汇编程序与周期性任务设计
一、51单片机延时汇编语言
(一)指令周期,机器周期与时钟周期
1、指令周期
**指令周期:**执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期也不同。指令周期常常用若干个CPU周期数来表示,CPU周期也称机器周期。通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。如:MOV为单周期,DJNZ为双周期
2、机器周期
机器周期:计算机中,常把一条指令的执行过程划分为若干个阶段,每一个阶段完成一项工作(例如,取指令、存储器读、存储器写等)。每一项工作称为一个基本操作,完成一个基本操作所需要的时间称为机器周期。它一般由12个时钟周期(振荡周期)组成,即由6个状态周期组成,而振荡周期=1/晶振频率,因此单片机的机器周期=12/晶振频率。如:12M晶振频率,机器周期为1微秒。
3、时钟周期
时钟周期也称为振荡周期,定义为时钟脉冲的倒数,时钟周期就是单片机外接晶振的倒数,是计算机中的最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。时钟脉冲是计算机的基本工作脉冲,控制着计算机的工作节奏。时钟频率越高,工作速度就越快。如:12M的晶振,它的时钟周期就是1/12微秒。
(二)延时函数
1、单重循环短暂延时
短暂延时可以通过单重循环来实现。例如,下面为延时200μs子程序。
DELAY:MOV R7,#100 ; 寄存器R7赋值100
D1:DJNZ R7,D1 ;--R7,结果不为0跳转到D1继续执行
RET ;返回函数
DJNZ指令周期为两个机器周期,即执行DJNZ指令需要2μs
,每执行一次寄存器R7减一,不断重复,直到R7为0后,指令结束。即D1语句执行次数为100
次,延时100*2=200μs
。
2、多重循环较长延时
周期性点亮LED灯的延时函数:
下面给出的是周期性点亮一个LED灯的延时函数,采用双重循环的方式来实现。
DELAY: MOV R7,#250
D1: MOV R6,#250
D2: DJNZ R6,D2 ;250*2=500μs
DJNZ R7,D1 ;250*500μs=0.125S
RET
- 第一句:DELAY: MOV R7,#250 ;执行了一次,没有任何语句跳转给它,单周期。
- 第二句:D1: MOV R6,#250 ;执行了250次,全部是第四句跳过来的,单周期。250
- 第三句:D2: DJNZ R6,D2 ;原地执行了250次,从第二句顺延(第二句执行完,没有其它跳转的话肯定要执行第三句)过来250次,也就是250x250=62500次,双周期*2。125000
- 第四句: DJNZ R7,D1 ; 从第三句顺延过来250次,双周期*2。500
总计:1+250+125000+500=125751次,乘以1微秒,换算约为0.125秒。
Q:若要使LED每隔1S亮灭进行周期性变化,上述双重循环还能否实现?程序如何修改?
A:51单片机每个寄存器最大存储256字节,因此两个寄存器循环并不能达到延时1S的效果。为此我们为其增加一层循环,以三层循环来实现更长时间的延时。
1|DELAY:MOV R5,#10
2|D1: MOV R6,#200
3|D2: MOV R7,#250
4| DJNZ R7,$ ;250*2=500μs
5| DJNZ R6,D2 ;500*200=100000μs
6| DJNZ R5,D1 ;100000*10=1000000μs
7| RET
对每条指令进行计算得出延时时间约为1.1秒,近似等于1秒
NOP指令
NOP指令在汇编中的作用是空指令,意味着什么都不做,一般用来控制CPU时间,达到时钟延时的效果。
NOP指令为单周期指令,可由晶振频率计算出延时时间,对于12M晶振,延时1μs。
仍以上述延时1S为例,可在程序式中加入NOP指令:
1|DELAY:MOV R5,#10
2|D1: MOV R6,#200
3|D2: MOV R7,#250
4| NOP
5| DJNZ R7,$ ;250*2=500μs
6| DJNZ R6,D2 ;500*200=100000μs
7| DJNZ R5,D1 ;100000*10=1000000μs
8| RET
二、汇编语言查表法求平方数
(一)汇编程序
要求:通过查表法,求出1~9的平方数
代码如下:
1|ORG 0000H ;定义下一条的指令放在地址为0000H存储器
2|LJMP A1 ;长跳转指令至A1
3|ORG 0080H
4|A1: NOP
5| NOP
6| MOV SP,#60H
7| MOV DPTR,#2000H
8| MOV A,#03H
9| MOVC A,@A+DPTR
10|A2: SJMP A2
11| ORG 2000H
12| DB 01h,04h,09h,10h,19h,24h,31h,40h,51h
13| END
- 在程序存储器中0003H~0023H是中断入口,在编写程序时此空间需要预留出来。同时,LJMP是一个3字节指令,从复位入口0000H到第一个中断入口地址0003H刚好为3字节,刚好可以存放一个长跳转指令,且LJMP恰好可以跳转到64K程序空间的任一位置,因此主程序可以放到程序空间的任一位置。上述LJMP程序跳转到0080H开始执行其余有效代码。
- **NOP:**在51单片机上电复位后,在刚开始执行代码含有多个空操作,目的使单片机外围扩展芯片从复位状态回到正常状态中
- MOV SP,#60H :SP是栈指令,单片机上电后默认SP赋值为07H,意味着08H及往上均为堆栈区,而这在实际中是不合理的,因为08H占用了第一组工作寄存区以及往上的寄存区。因此在实际中,我们在RAM中的高端作为堆栈区,一般以60H~7FH作为堆栈区。
- DB指令:声明并初始化数据。声明数据的本质就是:在内存中占用一块空间 ;初始化数据的本质就是:给这个空间赋予一个值 。举例:
db 0,0,0,0,0
,在内存中占用了5个字节的空间,这5个字节的值都是0 。ORG 2000H ;对表格头的 DB 01h,04h,09h,10h,19h,24h,31h,40h,51h
将表格中第一个数据填充到地址2000H中,以此类推,表格数据被存放到2000H~2008H中
- MOV DPTR,#2000H ,MOVC A,@A+DPTR:
- 变址寻址——指令中使用DPTR的内容作为基地址,再与累加器A的内容相加,其合作为操作数地址,常用于查表操作。
- 特点——DPTR应预先存放有操作数的基地址;累加器A中预告放有被寻址操作数地址与基地址之间的偏移量,该地址偏移量应该是00H~FFH范围内的无符号数;
- 寻址空间——ROM,这是访问程序存储器ROM中数据的唯一方式。
代码优化:
上述采用固定表格位置的方法进行查表,若有效程序代码空间大于2000H,则会进入或覆盖表格空间,表格将会失效。为了解决该问题,我们设计浮动表格,使表格始终处于程序段有效代码的最后。
实现方法:先知道标号隐含着地址信息,因此可以将表格头所在的标号地址作为DPTR基地址。
7| MOV DPTR,#tab ;将标号的位置取出来赋值给DPTR
11| ;ORG 2000H ;不用ORG对表格位置进行声明
12|tab:DB 01h,04h,09h,10h,19h,24h,31h,40h,51h ;浮动表格,自动存放在程序段有效代码最后
(二)在Edsim中验证程序
将上述代码利用Edsim软件进行仿真测试,结果如下:
(三)在Proteus中验证程序
1、在keil软件中生成在Proteus中可调试的.omf文件
2、将.omf文件加载到AT89C52芯片中进行调试
以上,实验得以验证。
三、普中开发板应用实例
在利用普中开发板做实例之前,我们首先需要了解关于普中开发板的相关知识,详细可以参考以下链接:https://blog.csdn.net/yu57955/article/details/120928956
STC-ISP软件是专门给STC系列单片机下载烧录程序的,并不能适用于ARM系列单片机,可以在http://www.stcmcu.com/官网下载最新的版本。详细使用指南参考:https://blog.csdn.net/qq_28576837/article/details/125712180
(一)周期性点亮LED灯
1、LED模块原理图
上图左边8个口连接到单片机的P2.0口~P2.7口, 右边VCC是指电源;
VCC左边的蓝色方框是指电阻,右上角1K指1000Ω,用于保护电路,防止超载;
中间绿色的为LED二极管,当P2.0口为低电平时,则对应电路上的LED灯D1亮;若为高电平,则对应电路上的LED灯D1灭,其他灯同理。
2、代码实现
(1)C程序周期性点亮LED
-
其中头文件
#include <REGX52.H>
与#include <REG52.H>
区别在于第一个头文件中声明了I/O口的每个引脚如P2_0
,而第二个头文件只声明了P2
. -
#include <REGX52.H>
- #include <REG52.H>
- 其中C程序的延时函数可以直接使用STC-ISP生成代码
(2)汇编代码周期性点亮LED
3、将Keil程序生成的.hex文件通过STC-ISP写入单片机中
注意:烧录过程中,单片机需要在上电状态下点击下载/编程,再将单片机重启显示下载完成