目录
一、实现LED灯每隔1S亮灭的周期性变化
汇编指令是汇编语言中的基本命令,它们是计算机CPU指令集的直接表示。每一条汇编指令对应于机器语言中的一项操作,如数据传输、算术运算、逻辑运算、控制流等。汇编指令通常由操作码(OpCode)和操作数(Operand)组成,操作码指定要执行的操作,而操作数指定操作的作用对象和相关信息。在51单片机编程中,使用的是8051微控制器的汇编语言,51单片机是一种经典的8位微控制器。
1.1 通过软件多重循环软件计数的定时方法
通过软件多重循环进行计时的方法是一种在缺乏硬件定时器或需要在现有硬件定时器之外实现额外定时功能时使用的技巧。这种方法通常被称为“软件定时”或“软件延时”。其基本原理是让微控制器执行一系列空操作(如循环),直到达到预定的循环次数,从而实现特定的时间延迟。
MOV R7,#250;//R7赋值为250
D1:MOV R6,#250;
D2:DJNZ R6,D2;//若R6-1不等于0,则继续执行D2
DJNZ R7,D1;
上述代码使用两个寄存器R6和R7来进行计数。R7被初始化为250,而R6在每次R7循环时也被初始化为250。每次R6减到0时,它会被重新加载为250,然后R7减1。当R7也减到0时,循环结束。
计算总的循环次数时需要考虑两个循环的嵌套。R6每减少一次,都会执行一个内部的循环,该循环在R6再次减到0之前会执行250次。这个内部循环会重复R7次,即250次。
总的循环次数 = R6的初始值 × R7的初始值 = 250 × 250次
总的循环时间计算如下:
每次内层循环迭代消耗的时间 = 每个DJNZ指令的时钟周期数 + 每个MOV指令的时钟周期数
总的循环时间 = (每次内层循环迭代消耗的时间 × 内层循环的迭代次数) × 外层循环的迭代次数
0.001*(2*250)*250 = 125 ms
1.2 通过循环 +nop指令
如果想要通过添加NOP指令来调整软件延时循环的时间,需要知道每个NOP指令消耗的时钟周期数以及微控制器的时钟频率。在8051单片机上,一个NOP指令通常消耗1个时钟周期。
假设微控制器的时钟频率是1 MHz(即每个时钟周期1微秒),那么每个NOP指令将消耗1微秒的时间。可以根据需要添加NOP指令的数量来调整延时时间。
例如,如果您想要增加延时时间,可以在两个DJNZ指令之间添加NOP指令。
MOV R7, #250 ; R7赋值为250
D1: MOV R6, #250 ; R6赋值为250
D2: DJNZ R6, D2 ; 若R6-1不等于0,则继续执行D2
NOP ; 添加NOP指令
NOP ; 添加NOP指令
DJNZ R7, D1 ; 若R7-1不等于0,则继续执行D1
上述代码每次内层循环迭代后都会执行两个NOP指令,这将增加每次迭代的延时时间。
要计算总的循环时间和循环次数,我们使用之前的方法,只需将每次内层循环迭代的时间调整为包括NOP指令的时间。如果每个NOP指令消耗1个时钟周期,那么每次内层循环迭代将额外消耗2个时钟周期(2个NOP指令×1个时钟周期/指令)。总的循环时间为:0.001*(4*250)*250 = 250 ms
1.3 Edsim51 实现LED灯每隔1S的周期性闪烁
(一)机器周期与指令周期
1.机器周期是指单片机完成一个基本操作所花费的时间,一般使用微秒来计量单片机的运行速度,51 单片机的一个机器周期包括12 个时钟振荡周期,也就是说如果51 单片机采用12MHz 晶振,那么执行一个机器周期就只需要1μs;如果采用的是6MHz 的晶振,那么执行一个机器周期就需要2 μs。
2 .指令周期是指单片机执行一条指令所需要的时间,一般利用单片机的机器周期来计量指令周期。在51 单片机里有单周期指令(执行这条指令只需一个机器周期),双周期指令(执行这条指令只需要两个机器周期),四周期指令(执行这条指令需要四个机器周期)。除了乘、除两条指令是四周期指令,其余均为单周期或双周期指令。也就是说,如果51 单片机采用的是12MHz 晶振,那么它执行一条指令一般只需1~2 微秒的时间;如果采用的是6MH 晶振,执行一条指令一般就需2~4 微秒的时间。
(二)延时指令
在单片机编程里面并没有真正的延时指令,单片机每执行一条指令都需要一定的时间,所以要达到延时的效果,只须让单片机不断地执行没有具体实际意义的指令,从而达到了延时的效果。
1.数据传送指令 MOV
数据传送指令功能是将数据从一个地方复制、拷贝到另一个地方。如:MOV R7,#80H ;将数据80H 送到寄存器R7,这时寄存器R7 里面存放着80H,就单这条指令而言并没有任何实际意义,而执行该指令则需要一个机器周期。
2.空操作指令 NOP
空操作指令功能只是让单片机执行没有意义的操作,消耗一个机器周期。
3.循环转移指令 DJNZ
循环转移指令功能是将第一个数进行减1 并判断是否为0,不为0 则转移到指定地点;为0 则往下执行。如:DJNZ R7,KK ;将寄存器R7 的内容减1 并判断寄存器R7 里的内容减完1 后是否为0,如果不为0 则转移到地址标号为KK 的地方;如果为0 则执行下一条指令。这条指令需要2 个机器周期。
(三)以下代码是一个LED周期性闪烁的汇编程序:
(1)指令 “MOV R6,#250”的机器周期数为1,其对应的时钟周期值为1 us; “DJNZ R6,D2”的机器周期数为2,其对应的时钟周期值为:2 us。
Delay函数的总的循环次数:250*250 = 62500 次,即 125000 us, 说明这个LED灯大约每隔多少毫秒125 ms 才变化一次亮灭状态;
(2)将上述代码进行修改,使其实现准确的LED 每隔1s亮灭的周期性变化。
代码如下:
LOOP:SETB 90H ;led0置1
LCALL DELAY ;调用延时函数
CLR 90H ;led0置0
LCALL DELAY
AJMP LOOP ;跳转到LOOP
DELAY: MOV R7,#10 ;延时函数
D1: MOV R6,#200
D2: MOV R5,#248
D3: DJNZ R5,D3
DJNZ R6,D2
DJNZ R7,D1
RET ;返回主函数
END ;指令结束
对每条指令进行计算得出精确延时时间为:
1+(1*10)+(1*200*10)+(2*200*248*10)+(2*200*10)+(2*10)+2 = 998033 us ≈ 1s
仿真结果如下:
1.4 流水灯汇编程序的编写与仿真验证
(一)、keil 汇编代码
ORG 0000H //指定程序的起始地址
AJMP MAIN //无条件跳转到MAIN指令,程序的入口点
ORG 0030H //指定MAIN标签的地址
MAIN: MOV SP, #60H //程序的主体从这里开始,将堆栈指针(SP)设置为0x60,确保有足够的空间来存储局部变量和函数调用的返回地址
MOV A, #0FEH; //将累加器A的值设置为0xFE,这是一个二进制数,最高位为0,其余位为1
LOOP: //主循环开始
INC R0; //将寄存器R0的值增加1
RR A; //将累加器A的值右移一位,最低位进入进位标志C,最高位补0
MOV P0,A; //将累加器A的值输出到端口P0,该端口连接LED灯,每次A的值右移一位,LED灯的亮灭状态就会改变
ACALL DELAY; //调用子程序DELAY,实现延迟
AJMP LOOP; //无条件跳转到LOOP,继续主循环
AJMP $; //这是一个无限循环,$代表当前地址,该指令确保程序不会意外退出
DELAY: //延迟子程序的开始
MOV R4,#50; //将寄存器R4的值设为50
DEL1: //内部循环的开始
MOV R6,#100; //将寄存器R6的值设为100
DEL2: //更内部循环的开始
MOV R7,#100; //将寄存器R7的值设为100
DJNZ R7,$; //将寄存器R7的值减1,如果不为0,则跳转到当前指令位置($位置)
DJNZ R6,DEL2; //将寄存器R6的值减1,如果不为0,则跳转到DEL2
DJNZ R4,DEL1; //将寄存器R4的值减1,如果不为0,则跳转到DEL1
RETI ; //从子程序返回,并启用中断,该程序中没有中断处理
END //程序结束
以上代码实现的效果是:LED灯的亮灭会以大约每秒一次的频率变化。每次通过RR A指令,A的值右移一位,LED灯的亮灭状态就会改变一次。延时子程序DELAY确保了LED灯的状态变化有一个明显的间隔,从而实现闪烁效果。在普中开发板上验证时,要将P0改为P2。
(2)、Edsim仿真验证
(3)、Proteus仿真验证
二、 在普中单片机开发板上进行验证
2.1 普中 51-实验板
外观图如下:
功能模块介绍:
在使用普中开发板之前,我们需要安装CH340驱动,在安装过程中要注意:必须使用USB线将电脑USB口与开发板USB接口连接。如果驱动安装成功,会显示以下界面:
2.2 烧录程序
安装好 CH340 驱动后,我们就可以下载程序了,在下载程序前先确认下开发板的 USB 转 TTL 串口模块上的 P5 端子短接片是否短接好(即 P31T 与 URXD连接,P30R 与 UTXD 连接),出厂的时候该短接片默认已经短接好。
打开“PZ-ISP”下载软件,选择正确的芯片类型(根据板载芯片型号是否含有RC来选择芯片类型),本次验证所选芯片为“STC89C52xxx-RC”;之后设置合适的波特率,如果发现下载速度较慢,可以提高波特率;如果下载失败,可以降低波特率。其他选项保持默认设置,点击打开文件,选择汇编生成的 .hex 文件并打开,点击程序下载按钮即可完成程序下载,烧录成功,实验板会看到相应的实验效果。如下:
三、汇编语言用查表法完成求平方数
3.1 查表法求平方数
在51单片机中,查表法求平方数是一种编程技术,它利用了查表指令来快速获取数据的平方值,而不是通过执行乘法运算来计算平方。这种方法通常用于那些平方值预先已知并且数量有限的情况。
具体来说,查表法求平方数的工作原理如下:
1、建立平方数表:首先,你需要创建一个包含一系列平方数的表。例如,如果你想要计算1到10的每个整数的平方,你的表将包含1, 4, 9, 16, 25, 36, 49, 64, 81, 100这些数。
2、使用查表指令:在8051单片机中,可以使用MOVC指令来查表。这个指令允许你通过将一个寄存器的值(通常是累加器A)作为索引来从表中读取数据。
3、执行查表操作:当你想要计算一个数的平方时,你只需将该数作为索引,使用MOVC指令从平方数表中读取对应的平方值。
例如,如果你想要计算3的平方,你会将3放入累加器A,然后使用MOVC指令从表中读取索引为3的项(即9)。
查表法的优点是速度快,因为它避免了复杂的数学运算。然而,它的局限性在于表的大小和范围,因为表必须预先填充,且只能包含表内已有的平方数。如果需要计算表外数的平方,就需要使用其他方法,如乘法算法。
3.2 汇编程序
查找1-9的平方数,汇编代码如下:
ORG 0000H
LJMP A1
ORG 0080H
A1: NOP
NOP
MOV SP,#60H
MOV DPTR,#2000H
MOV A,#03H
MOVC A,@A+DPTR
A2: SJMP A2
ORG 2000H
DB 00h,01h,04h,09h,10h,19h,24h,31h,40h,51h
END
//ORG 0000H指令用于指定程序的起始地址,即地址0000H。LJMP A1是一个长跳转指令,无条件跳转到标签A1的位置。这是程序的主入口点。
//ORG 0080H指定了标签A1的地址。在8051单片机中,通常会将栈指针(SP)设置在内部RAM的高地址部分,以避免与程序使用的其他内存区域冲突。MOV SP,#60H将栈指针设置为60H,这是8051内部RAM的一个较高地址。
MOV DPTR,#2000H将数据指针DPTR设置为2000H,这是外部数据内存的起始地址,用于存储查表数据。
MOV A,#03H将累加器A的值设置为03H,这是我们要查找的表的索引。
MOVC A,@A+DPTR是一个查表指令,它将累加器A中的值(索引)与DPTR的内容(表地址)相加,然后从该地址读取数据到累加器A。这样,累加器A中就存储了索引为3的表项的值。
//A2是一个标签,SJMP A2是一个短跳转指令,它使得程序在这里无限循环。一旦执行到这个指令,程序将不断跳转到标签A2,形成一个死循环。
//DB 00h,01h,04h,09h,10h,19h,24h,31h,40h,51h
ORG 2000H指定了数据表的内存地址。DB指令用于定义字节(byte),这里定义了一系列的值,这些值构成了我们的查表数据。例如,索引0的值是00h,索引1的值是01h,以此类推。
四、总结
本次实验通过对汇编指令和常用程序结构的学习与实践,我们掌握了软件多重循环计数定时方法和循环+nop指令的定时方法,了解了查表法在求平方数程序中的应用,以及普中单片机实验开发板的使用方法。实验过程中,我们通过实际操作,锻炼了编程能力和动手能力,对单片机原理和编程有了更深入的理解。同时,我们也认识到了软件定时方法的重要性,以及在实际应用中根据需求选择合适方法的重要性。
文章多有不足,如果大家发现错误,还请多多批评指正!