在单片机P2口外接8个发光二极管(低电平驱动)。试编写一个汇编程序,实现LED循环点亮功能:P2.0-P2.1-P2.2-P2.3-…-P2.7-P2.6-P25-…-P2.0的顺序,无限循环。要求采用软件延时方式控制闪烁时间间隔(约50ms)。

首先进行电路设计

电路原理图设计

利用 Proteus 软件的ISIS 模块绘制原理图。虑到LED低电平驱动要求,硬件电路设计时需使LED的阴极应接P2口,阳极通过限流电阻与+5V 电源相接。电路原理图如下:

在这里插入图片描述

XTAL1和XTAL2是晶振的输入和输出端口。C1和C2电容被连接到这些端口,以提供晶振所需的稳定电压和电流。这两个电容组成了一个串联谐振电路,可以帮助晶振产生稳定的振荡信号。这样,微控制器或其他设备就可以使用这个稳定的时钟信号来进行精确的计时和同步操作。

RST引脚连接C3电容器和电阻的目的是为了实现复位电路。当微控制器系统中存在异常或故障时,可以通过将RST引脚拉低来进行强制复位操作,将系统恢复到初始状态。C3和1K欧姆电阻组成了一个简单的延迟元件,在复位期间提供了一个短暂的时间延迟,并消除了可能存在的任何噪声或干扰信号,从而确保复位信号的稳定性和可靠性。

程序设计

编程思路:P2口的亮灯编码初值应能保证 P2.0 位输出低电平,其余位均为高电平。根据电路要求,这一编码初值应为 OFEH(二进制1111 1110),即D1 为亮,D2~D8 皆为暗。此后,不断将亮灯编码值进行循环左移输出,亮灯位将随之由上向下变化;循环左移7次后改为循环右移,则亮灯位将随之由下向上变化。如此反复进行便可实现题意要求的流水灯功能。

图 所示为编程思路的程序流程图。

在这里插入图片描述

汇编代码如下:

ORG     30H
CYC1    EQU  200
CYC2    EQU  125

MOV     A,#0FEH ;LED亮灯编码初值
MOV     P2,A
MOV     R2,#7 ;计数器 7次换方向

DOWN:   RL      A       ;下行方向
  ACALL   DEL50 ;延时50ms
  MOV     P2,A
  DJNZ    R2,DOWN
  MOV     R2,#7
 
UP:     RR      A       ;上行方向
  ACALL   DEL50
  MOV     P2,A
  DJNZ    R2,UP
  MOV     R2,#7
  SJMP    DOWN

DEL50:  MOV     R7,#CYC1;延时50ms
DEL1:   MOV     R6,#CYC2
  DJNZ    R6,$
  DJNZ    R7,DEL1
  RET
  END

下面进行一些解释:

  • ORG 30H:设置代码的起始地址为 30H。

  • CYC1 EQU 200:定义常数 CYC1 的值为 200,用于延时操作。

  • CYC2 EQU 125:定义常数 CYC2 的值为 125,用于延时操作。

  • MOV A,#0FEH:将二进制数 0xFE(1111 1110)存储到 A 寄存器中,这是一个控制 LED 灯亮灭的编码初值。

  • MOV P2,A:将 A 寄存器的值传送到 P2 引脚上,控制 LED 灯亮灭。

  • MOV R2,#7:初始化计数器 R2 的值为 7,用于实现循环功能。

  • DOWN: RL A;ACALL DEL50;MOV P2,A;DJNZ R2,DOWN;MOV R2,#7:这段代码实现了一个循环结构,在每次循环中逐行读取 P2 引脚的输入信号并将其输出给其他设备。具体来说,它执行以下步骤:

将 A 寄存器中的值向左循环移位一位,并将最高位的值保存在 Carry 标志位中;

将 A 寄存器的值传送到 P2 引脚上,输出给其他设备;

延时一定时间(由 DEL50 子程序控制);

将 R2 寄存器的值减去 1,并检查是否等于 0。如果不为 0,则跳转到标记为 DOWN 的代码行,继续执行下一次循环;否则,将 R2 的值重新设置为 7,结束循环。

  • UP: RR A;ACALL DEL50;MOV P2,A;DJNZ R2,UP;MOV R2,#7:这段代码与上面的 DOWN 循环结构类似,但是它是用来逆向读取 P2 引脚的输入信号并输出给其他设备。

  • SJMP DOWN:无条件跳转到标记为 DOWN 的代码行,实现无限循环。

  • DEL50: MOV R7,#CYC1;延时 50ms

  • DEL1: MOV R6,#CYC2;将常数 CYC2 存储到寄存器 R6 中,用于延时操作。

  • DJNZ R6,:将 R6 寄存器的值减去 1,并检查是否等于 0。如果 R6 不为 0,则跳转到当前代码行(:将R6寄存器的值减去1,并检查是否等于0。如果R6不为0,则跳转到当前代码行(),实现一个短暂的延时操作。

  • DJNZ R7,DEL1:将 R7 寄存器的值减去 1,并检查是否等于 0。如果 R7 不为 0,则跳转到标记为 DEL1 的代码行,继续执行延时操作。

  • RET:从子程序中返回主程序。

  • END:表示程序结束。


延时子程序

如何实现延时的:

CYC1    EQU  200    
CYC2    EQU  125
DEL50:  MOV     R7,#CYC1;延时50ms

DEL1:   MOV     R6,#CYC2
  DJNZ    R6,$
  DJNZ    R7,DEL1

首先设置CYC1 和 CYC2C 的值是 200 和125 ,类似常量

然后设置DEL50子程序: R7寄存器赋值,将CYC1的值200存储到R7寄存器中。
DEL1子程序标签也是这样,将CYC2的值125存储到R6寄存器中。

下面 DJNZ R6,$ :

将R6寄存器的值减1,如果结果不为零,则跳转到当前标签(即DEL1)所在的位置,继续执行循环。这样就可以实现循环CYC2(125)次的功能。

同样,DJNZ R7,DEL1的意思是将R7寄存器的值减一,如果结果不为零,则跳转到DEL1标签所在的位置,继续执行循环。这样就可以实现循环CYC1次的功能,从而实现50ms的延时。

有点像循环里面再嵌套一个

for
if 
 if

这样

最后计算时间延时:

在这里插入图片描述

以后汇编计算延时可以类似这样

 


仿真结果

汇编实现LED循环点亮(延时子程序模板)

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐