8051模拟器汇编入门(6)闪烁LED

在EdSim51模拟器的左边我们可以看到LED的对应端口为P1,8个LED分别对应P1.0-P1.7。

图片

这8个LED位于下面外设区以下位置:

图片

在这篇文章中我们来看看如何点亮和闪烁这些LED。

下面是模拟器的电路图:

图片

放大与LED相关的电路,可以看到LED的左边接的是高电平,所以如果要点亮LED,右边的端口必须设为电平(0)。

图片

让我们进一步简化为下面的布局:

图片

每个LED对应P1中的一位,假设我们要点亮最右边的LED,只需要将P1的值设为1111 1110即可,对应的十六位进制数为FE。

图片

让我们写个简单的程序点亮最右边的LED:

MOV P1,#0FEH

可以看到最右边的LED被成功点亮:

图片

然后从0我们再切换回1,这将关闭LED。如果我们不断重复这个过程,LED将会不断地开关,并且会闪烁。意味着我们只需要将P1的值在FE和FF之间来回切换即可。

图片

编写以下程序:

ORG 00H
BACK: MOV A,#0FEH
      MOV P1,A
    
      MOV A,#0FFH
      MOV P1,A
      
      SJMP BACK
      END

现在让我们看看编码,这相当简单,所以我们从0H开始,标签是BACK,稍后我会解释为什么我使用了这个标签。FE表示打开最低有效位的LED。我们把FE值记住,任何以字母开头的值都需要在前面加上一个前导零,所以这是0FE十六进制,将其移动到A寄存器,然后从A寄存器移动到P1端口,这将关闭LED。然后用SJMP,代表短跳转,跳回BACK,程序进入一个循环。

运行模拟器,期望中的闪烁并没有发生。

原因是微控制器以12兆赫兹运行,大约是每秒1200万次循环。现在LED实际上正在闪烁,但它以每秒1200万次的速度闪烁,这太快了,人眼无法检测到这种速度的变化。那么我们如何解决这个问题呢?

通常,我们可以在这里插入一个延迟,这里的延迟被称为子程序,这个子程序的功能是将LED保持在其状态一段时间,然后当它完成后,它将关闭LED。为了保持LED关闭状态,你插入另一个延迟函数或子程序,保持它关闭一段时间,然后它将跳转回并重复整个过程。

ORG 00H
BACK: MOV A,#0FEH
      MOV P1,A
      ACall Delay
          
      MOV A,#0FFH
      MOV P1,A
      ACall Delay
      
      SJMP BACK
Delay:Mov R0, #0FFH
Again:Mov R1,#0FFH
Here: Djnz R1, Here
      Djnz R0,Again
      Ret
      END

延迟函数的工作原理:

外循环 (R0):

MOV R0,#0FFH 将 R0 加载 255

这是外循环计数器。

内循环 (R1):

MOV R1,#0FFH 将 R1 加载 255

这是内循环计数器。

内循环操作:

DJNZ R1,Here

表示:将 R1 减 1,如果 R1 ≠ 0,则跳转回“Here”,此循环运行 255 次

外循环操作:

DJNZ R0,Again

表示:R1 达到 0 后,将 R0 减 1,如果 R0 ≠ 0,则跳转回“Again”并重新加载 R1,此过程重复 255 次。

总延迟计算:

内循环 (R1) 运行 255 次,外循环 (R0) 运行255 次,每次内循环迭代需要 2 个机器周期,总周期 ≈ 255 × 255 × 2 = 130,050 个周期。

这会产生延迟,因为每条 DJNZ 指令需要 2 个机器周期,每条 MOV 指令需要 1 个机器周期。使用典型的 12MHz 晶振:

1 个机器周期 = 1μs

总延迟 ≈ 130ms

运行程序后LED灯可以正常闪烁。

另一个控制LED的方法是单独设置LED灯的值,使用CLR和SETB命令,CLR将值清为0,SETB将值设为1。

ORG 00H
BACK: CLR P1.0      
      ACall Delay
          
      SETB P1.0
      ACall Delay
      
      SJMP BACK
Delay:Mov R0, #0FFH
Again:Mov R1,#0FFH
Here: Djnz R1, Here
      Djnz R0,Again
      Ret
      END 

以上代码可以同样实现LED闪烁。