8051汇编程序与LED灯周期性闪烁设计

一、深入了解汇编指令和常用程序结构,以周期性点亮LED灯的延时函数为例

下面是一个让LED灯周期性闪烁的例子

LOOP: SETB 90H  ;LED0置1
    LCALL DELAY  ;调用延时函数
    CLR 90H  ;LED0置0
    LCALL DELAY  ;
    AJMP LOOP  ; 

DELAY: MOV R7,#250   ;将立即数 250 赋值给寄存器 R7
D1: MOV R6,#250   ;将立即数 250 赋值给寄存器 R1
D2: DJNZ R6,D2
    DJNZ R7,D1
    RET  ;表示函数结束并返回。在延时函数中,这会使程序返回到调用该延时函数的位置。
END

 1.通过软件多重循环软件计数的定时方法

(1)查阅汇编指令 “MOV R6,#250”和“DJNZ R6,D2”的指令周期数,计算其对应的时钟周期 us值;然后计算 Delay函数的总的循环次数和对应的时钟周期总数us, 说明这个LED灯大约每隔多少毫秒(ms)或秒(s)才变化一次亮灭状态

 指令周期(Instruction Cycle)

CPU从存储器中取出并执行一条指令所需的全部时间称之为指令周期。

时钟周期(cycle,clock cycle)

主频:计算机内部主时钟的频率,通常以MHz或者GHz为单位,是生产设计CPU时就已经确定下来的。主频越高,CPU的运算速度越快,时钟周期越短,硬件技术水平的提高可以提升主频。主频可能会发生变化(例如Intel的Turbo Boost技术),也可能保持不变(例如ARM、RISC-V架构的处理器)时钟周期(cycle):定义为主频的倒数,有时也称之为节拍(pulse),时钟周期是计算机中最基本的、最小的时间单位,在一个时钟周期内计算机仅能完成一个“微操作”或若干相容的(数据通路上不发生冲突、互不干扰的)“微操作”。

机器周期(Machine Cycle)

或者称为CPU周期(CPU Cycle),机器周期是人为规定的,实际上是对一条指令执行过程阶段的划分。具体的划分方法随计算机的不同而不同。

指令"MOV"周期数为1,指令"DJNZ"周期数为2。

根据上图延时50ms的例子可得出:D2: DJNZ R6,D2   ;   250*2=500us
                                                              DJNZ R7,D1   ;   0.5ms*250=1.25s

因此,Delay 函数的总的时钟周期数约为1.25s。则LED灯每隔约1.25秒 (s) 变化一次亮灭状态。

在edsim51中仿真结果如下:

(2)实现准确的LED 每隔1s亮灭的周期性变化

51单片机的每个寄存器存储的最大值为256个字节,因此二重循环无法达到延时1秒的效果,为了实现延时1秒的效果采用三重循环。

DELAY:MOV R5,#25
D1: MOV R6,#100
D2: MOV R7,#200
	DJNZ R7,$		;200*2=400μs
	DJNZ R6,D2		;400*100=40000μs
	DJNZ R5,D1		;40000*25=1000000μs
RET

代码解析:

DELAY: MOV R5, #25: 定义一个名为DELAY的子程序,将循环次数25存储到寄存器R5中,用于控制总的延时时长。

D1: MOV R6, #100: 在标签D1处,将循环次数100存储到寄存器R6中,用于控制内层延时时长。

D2: MOV R7, #200: 在标签D2处,将循环次数200存储到寄存器R7中,用于控制内层延时时长。

DJNZ R7, $: 使用DJNZ指令(Decrement and Jump if Not Zero)对寄存器R7进行减1操作,如果结果不为零,则跳转到当前指令地址,实现了一个内层延时循环。

DJNZ R6, D2: 使用DJNZ指令对寄存器R6进行减1操作,如果结果不为零,则跳转到标签D2处,实现了一个较长的内层延时循环。

DJNZ R5, D1: 使用DJNZ指令对寄存器R5进行减1操作,如果结果不为零,则跳转到标签D1处,实现了一个外层循环,循环次数为25次。

RET: 返回指令,结束延时程序。

在edsim51中仿真结果如下:

2.通过循环+nop指令的方法

NOP(No Operation)指令是一种空操作指令,执行时不进行任何实际操作,只是简单地占用一个CPU周期。因此,通过在程序中循环执行NOP指令,可以实现一定的延时效果。具体地说,通过在程序中嵌套循环,并在内部循环中执行一定数量的NOP指令,可以控制程序执行的时间,从而实现需要的延时效果。在嵌套循环中,外部循环控制总体延时的次数,而内部循环中的NOP指令则控制每次循环的延时时间。

依旧以LED灯间隔1s周期性闪烁为例:

DELAY:  MOV R2, #20     
OUTER:  MOV R1, #250    
MIDDLE: MOV R0, #184    
     NOP
INNER:  DJNZ R0, INNER  
        DJNZ R1, MIDDLE 
        DJNZ R2, OUTER  
        RET             

代码解析:

DELAY: MOV R2, #20: 定义了一个名为DELAY的子程序,将循环次数20存储到寄存器R2中,用于控制外层循环的次数。

OUTER: MOV R1, #250: 在标签OUTER处,将循环次数250存储到寄存器R1中,用于控制中间层循环的次数。

MIDDLE: MOV R0, #184: 在标签MIDDLE处,将循环次数184存储到寄存器R0中,用于控制内层循环的次数。

NOP: 插入一个空指令,用于程序控制。

INNER: DJNZ R0, INNER: 使用DJNZ指令对寄存器R0进行减1操作,如果结果不为零,则跳转到INNER标签处,实现了内层循环,约为184个指令周期的延时。

DJNZ R1, MIDDLE: 当内层循环执行完毕后,使用DJNZ指令对寄存器R1进行减1操作,如果结果不为零,则跳转到MIDDLE标签处,实现了中间层循环,约为184*250个指令周期的延时。

DJNZ R2, OUTER: 当中间层循环执行完毕后,使用DJNZ指令对寄存器R2进行减1操作,如果结果不为零,则跳转到OUTER标签处,实现了外层循环,约为18425020个指令周期的延时。

RET: 返回指令,结束延时程序。

二、通过汇编语言用查表法完成求平方数的程序

1.代码

ORG 0000H
LJMP A1
ORG 0080H
A1: NOP
    NOP
    MOV SP,#60H
	MOV DPTR,#tab
	MOV A,#05H
	MOVC A,@A+DPTR
A2: SJMP A2
	tab:DB 01h,04h,09h,10h,19h,24h,31h,40h,51h
END
 

代码解析:

ORG 0000H: 定义程序的起始地址为0x0000,即程序入口点。

LJMP A1: 无条件跳转到标签A1处执行代码。

ORG 0080H: 定义代码段的起始地址为0x0080,这个地址用于存放程序的实际代码。

A1: NOP: 在标签A1处插入一个空指令(No Operation),用于程序控制。

NOP: 再次插入一个空指令,用于程序控制。

MOV SP, #60H: 设置堆栈指针寄存器SP的初始值为0x60,为栈分配空间。

MOV DPTR, #tab: 将查找表tab的地址加载到数据指针DPTR中。

MOV A, #05H: 将要计算平方的数5加载到累加器A中。

MOVC A, @A+DPTR: 使用MOVC指令从查找表tab中查找索引为5的平方数,并将结果存储到累加器A中。

A2: SJMP A2: 无限循环,程序将在此处无限循环执行。

tab: DB 01h, 04h, 09h, 10h, 19h, 24h, 31h, 40h, 51h: 定义了一个表tab,其中包含了0到9的平方数。

在edsim51中仿真结果如下:

三、掌握普中单片机实验开发板的开发使用方法,在板子上完成LED周期性点灯的C程序实验

1.用C语言使LED灯周期性闪烁

(1)c语言代码

#include <REGX52.H>
#include <INTRINS.H>
void Delay1000ms(void)	//@12.000MHz
{
	unsigned char data i, j, k;
 
	_nop_();
	i = 8;
	j = 154;
	k = 122;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
 
 
void main()
{
	while(1)
	{
		P2=0xFE;
		Delay1000ms();
		P2=0xFF;
		Delay1000ms();
	}
}

代码解析:

#include <REGX52.H> 和 #include <INTRINS.H>:这两行代码包含了 8051 单片机的头文件,用于定义单片机的寄存器和一些内置函数。

void Delay1000ms(void) //@12.000MHz:定义了一个延时函数 Delay1000ms(),用于产生 1000 毫秒的延时。@12.000MHz 注释表示这个延时函数是在 12MHz 的时钟频率下编写的。

unsigned char data i, j, k;:定义了三个无符号字符型的变量 i、j、k,用于控制延时的循环次数。

do { do { while (--k); } while (--j); } while (--i);:这是一个三重嵌套的 do-while 循环,用于产生延时。通过控制循环次数来实现延时。

void main():主函数。

while(1):无限循环。

P2=0xFE;:将 P2 端口的值设置为 0xFE,即最低位为 0,其余位为 1。这通常用于控制 LED 灯的亮灭。

Delay1000ms();:调用延时函数产生 1000 毫秒的延时。

P2=0xFF;:将 P2 端口的值设置为 0xFF,即全部位都为 1,关闭 LED。

Delay1000ms();:再次调用延时函数产生 1000 毫秒的延时。

这样,循环中的LED灯就会每隔2秒闪烁一次。

 四、总结

        本次实验让我跟进一步了解了汇编语言和LED定时闪烁的方法以及如何对lED闪烁间隔时间进行更加精确的设计。还学习了部分江协科技对于8051单片机的应用。

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值