51程序设计.docx
- 文档编号:3741450
- 上传时间:2022-11-25
- 格式:DOCX
- 页数:48
- 大小:171.78KB
51程序设计.docx
《51程序设计.docx》由会员分享,可在线阅读,更多相关《51程序设计.docx(48页珍藏版)》请在冰豆网上搜索。
51程序设计
第4章单片机程序设计
用汇编语言编写的程序占内存少,运行速度快,效率高,而且汇编语言能直接控制存储器及接口电路,能准确地掌握指令的执行时间,因此汇编语言特别适用于实时控制系统程序设计。
单片机是面向工业控制的,因此,必须掌握其汇编语言程序设计方法。
另外,在实际应用中,尽可能选用现成的程序模块(或作少量修改),以提高编程效率。
本章根据MCS-51的特点,除介绍一些常用的汇编语言程序设计方法和工程实用子程序外。
还介绍如何用C语言设计MCS-51单片机应用程序。
4.1汇编语言源程序的格式及伪指令
完整的汇编语言源程序是由助记符和标号地址编写的指令性语句和控制机器汇编的伪指令语句组成。
每条助记符指令都可以作为汇编语言源程序中的一条语句单独存在,且有与之相对应的机器码。
在机器执行源程序之前,必须经过汇编,逐条生成目标代码。
而伪指令无对应的机器码。
.
4.1.1汇编语言源程序的格式
汇编语言源程序是由若干语句组成的,每一语句可由4个部分组成:
标号、操作码、操作数及注释。
每一部分间以不同的分隔符分隔,语句格式如下:
[标号]:
操作码[操作数1,][操作数2,][操作数3];[注释]
其中[]项为可选项,视具体的指令选用.
标号是表示该语句所在地址的标志符号,使用标号可方便程序中的其他语句访问该语句。
标号由字母打头的1-8个字母数字串组成,但指令保留符、寄存器名、位址记忆符、伪指令符等都不能作标号使用.以下字符是不能用来作标号的。
一条语句可以有标号,也可以没有标号,标号的有无取决于程序中的其他语句是否需要访问这条语句,标号后面必须跟以冒号。
例如:
START:
LOOP:
TAB-1:
SUB-ADD均为正确的标号。
3B:
B+C:
(不能用“+”)END:
均为不正确的标号等.
操作码表示操作的性质,它是汇编指令中唯一不能缺省的部分。
操作数表示操作的对象,在一条语句中,操作数可能是空白或以逗号分开的几个。
它可由立即数,寄存器,直接地址,寄存器间址等方式实现.。
立即数即可是十六进制数,后缀用”H”表示;可以是二进制数,后缀用”B”表示;十进制数,没有后缀。
在进行汇编时,立即数均应汇编成十六进制数,第一位如果是字母A-F,则字母前加”0”。
注释是对语句或程序段功能的解释说明有助于的阅读和维护。
4.1.2汇编语言源程序的汇编
将汇编语言源程序“翻译”成机器语言目标程序的过程称为汇编,对单片机助记符的汇编有两种方法:
人工汇编和机器汇编。
人工汇编是用人工查表将源程序译成机器码。
一般分为两步进行。
第一步将源程序中的指令逐条译成目标码,指令中的标号地址待求。
第二步由伪指令求出标号所代表的具体地址,进行有关程序存储区的数据操作并进行偏移量的计算。
机器汇编是将源程序输入计算机后,由汇编程序实现翻译工作,产生相应的机器码。
这是一种非常高效和方便的方法。
4.2.3MCS-51伪指令
在机器汇编时,对汇编过程进行控制和指导的指令称为伪指令。
在汇编过程中,伪指令供汇编程序识别和执行但不产生可执行的目标代码。
如规定汇编生成的目标代码在ROM中的存放区域,给源程序符号、标号赋值,指示汇编结束等。
每种汇编程序都有自己的伪指令,标准的MCS-51定义的伪指令常用的有以下7条。
1.汇编其始地址伪指令——ORG(Origin)
格式:
:
ORG16位地址
功能:
规定该指令后的下一段源程序经汇编后生成的目标代码存放的起始地址。
例如:
ORG0500H
START:
MOVA,R0
………
END
ORG0500H伪指令既规定了标号START的地址是0500H,又指定了汇编后第一条指令及后续指令的机器码从0500H单元开始依次存放。
ORG伪指令总是出现在每段源程序或数据块的开始,汇编语言源程序中多处使用ORG指令,可使程序员把子程序、数据块存放在ROM的任何位置。
每当ORG出现时,下条指令的存放地址由此重新定位,所以ORG定义地址的顺序应有小到大,且不能重叠。
2.汇编结束伪指令-END(EndofAssembly)
格式:
[标号]:
END
功能:
结束汇编语言源程序的操作。
在源程序中只能有一条END,END后所写的指令,汇编程序不予处理。
3.符号赋值伪指令——EQU(EQUate)
指令格式:
字符名称EQU数或汇编符号
功能:
将一个数或特定的汇编符号赋给指定的字符名称。
字符名称为一自定的符号,而不是标号,字符名称后无“:
“。
字符名称可用来作数据地址,立即数,位地址或者是一代码地址,其值可以是一个8位数,也可以是16位数。
例如:
TESTEQU20H
MOVA,TEST
这里字符名称TEST就代表了内部RAM20H地址单元。
又例如:
DELAYEQU1234H
A1EQU10H
MOVA,A1
LCALLDELAY
这里A1代表片内RAM的直接地址单元10H,DELAY则定义了一个16位子程序的入口地址。
使用EQU伪指令可以把抽象的数字地址表示成有一定意义的符号,增强程序的可读性。
4.定义数据字节伪指令——DB(DefineByte)
格式:
<标号:
>DB<项或项表>
项或项表是指一个字节,逗号隔开的8位二进制的数或字符串,或撇号’括起来的ASCII字符串。
功能:
从标号指定的地址单元开始,在程序存储器中存入一组8位二进制数,或者将一个数据表格存入程序存储器。
这条伪指令汇编后影响程序存储器的内容。
例如:
ORG2000H
TABL:
DB01H,04H,09H,10H
DB00001111B,‘1’,‘A’,‘BC’
以上伪指令经汇编后,将对程序存储器从2000H开始的以下9个单元赋值为:
(2000H)=01H(2001)=04H
(2002H)=09H(2003)=31H
(2004H)=0FH(2005)=04H
(2006H)=41H(2001)=42H
(2007H)=43H
常用DB命令在程序存储器中存放数据表格,例如存放数码管显示的十六进制数的字形码,可使用多条DB命令定义:
DB0C0H,0F9H,0A4H,0B0H
DB99H,,92H,82H,0F8H
DB80H,90H,88H,83H
DB0C6H,0A1H,86H,84H
查表时,为确定数据区的起始地址,可采用两种方法:
(1)根据DB命令前一条指令的地址确定。
把该地址加上它的字节数,就是DB所定义的数据字节的起始地址。
例如:
8100H:
MOVA,#49H
TAB:
DB0C0H,0F9H,0A4H,0B0H
………
定义的数码管字形码从8002地址开始存放。
(2)使用ORG命令专门规定。
ORG4000H
TAB:
DB0C0H,0F9H,0A4H,0B0H
………
定义的数码管字形码从4000地址开始存放。
5.定义数据字命令——DW(DefineWord)
格式:
<标号:
>DW<项或项表>
功能:
DW的功能和DB类似,DW是从标号指定的地址开始存放16位而非8位二进制数,存放时,数据字的高8位在前(低地址),低8位在后(高地址)。
例如:
ORG5000H
MOVA,#30H
………
ORG5020H
ADDTAB:
DW1234H,100H,10
………;
END
以上伪指令经汇编后,程序存储器从5020H单元开始的内容为:
(5020H)=12H(5021H)=34H
(5022H)=01H(5023H)=00H
(5024H)=00H(5025H)=0AH
一条DB和DW语句定义的数表其数的个数不得超过80个。
当数据的数目较多时,可使用多个定义命令。
在MCS-51程序设计应用中,常以DW来定义地址。
6.预留存储区伪指令——DS(DefineStorage)
格式:
<标号:
>DS<表达式>
功能:
本命令用于从指定地址开始,保留DS之后表达式的值所需数目的字节单元作为存储区以备后用。
汇编时,对这些单元不赋值。
例如:
ORG0100H
MOVA,#50H
………
ADDRTABL:
DS05H
DB20H
END
从标号ADDRTABL代表的地址开始,保留5个连续的ROM地址单元,第6个单元存放20H。
注意:
对MCS-51单片机来说,DB,DW,DS伪指令只能对程序存储器使用,而不能对数据存储器进行初始化。
7.位地址赋值伪指令——BIT
格式:
<字符名称>BIT<位地址>
功能:
本命令用于给字符名称赋以位地址。
其中<位地址>可以是绝对地址,也可以是符号地址(即位符号名称)。
例如:
AQBITP1.0
A2BIT07H
这两条指令分别把P1.0的位地址赋给变量AQ,位地址07H赋给符号名A2,在其后的编程中AQ和A2就可以作为位地址使用。
4.2分支与查表程序设计
4.2.1分支程序设计
程序分支是通过条件转移指令实现的,即根据条件进行判断后决定程序的走向。
条件满足则进行程序转移,不满足就顺序执行程序。
在MCS-51指令系统中,通过条件判断实现单分支程序转移的指令有JZ、JNZ、CJNE和DJNZ等。
此外,还有以位状态为条件,进行程序分支的指令JC、JNC、JB、JNB和JBC等。
使用这些指令,可以完成或为0、1,或为正、为负,以及相等、不相等各种条件判断,以实现程序有条件地转移。
单分支结构的程序很多,此处就不专门举例了。
在实际应用中常需要判断两个或两个以上的复合条件,实现多分支转移。
下面重点介绍三分支和多分支程序设计
1.三分支程序
例1:
x、y均为二进制数,设变量x存放在R0,根据如下条件编程求y值并存入FUNC
单元。
+1x>0
y=-1x<0
0x=0
方法1:
将变量X送累加器A,利用累加器判0转移和直接位状态控制转移来实现三分支转移。
程序框图如图4.1(a)所示。
源程序为:
ORG0100H
FUNCEQU50H
MOVA,R0
JZDONE;若(A)=X=0,则转DONE
JNBACC.7,POSI;若(A)=X>0,则转POSI
MOVA,#0FFH;若(A)=X<0,则转A←-1
SJMPDONE
POSI:
MOVA,#01H;A+1
DONE:
MOVFUNC,A;存Y值
SJMP$
END
方法2:
利用CJNE指令和进位位C状态控制转移(JC指令)来实现三分支转移。
程序框图如图4.1(b)所示。
源程序:
ORQ0100H
FUNCEQU50H
CLRC
CJNER0,#00H,CMP1;R0中数与00H数比较,不相等则转CMP1
MOVFUNC,#00H;若比较相等,则0→FUN
SJMPEN
CMP1:
JCNEG;两数不相等,若R0<0则转NEG
POSI:
MOVFUNC,#01H,;R0>0,FUNC←+1
SJMPEN
NEG:
MOVFUNC,#0FFH;R0<0,R1←-1(以补码形式给出)
EN:
NOP
END
上面是两个包含了ORG和END伪指令的完整汇编语言源程序,机器汇编时,汇编程序经过两次扫描完成对源程序的汇编,生成目标程序。
再由程序写入器将其加载到单片机的内存中。
2.多分支程序
MCS-51指令系统中没有多分支转移指令,无法使用一条指令完成多分支转移。
要实现多分支转移,可采用多种方法。
例2:
用多条CJNE指令通过逐次比较实现N<8分支程序转移
下列程序用于测试由P1口读入100个0~9的数的概率分布统计。
ORG0100H
COUNTEREQUR7
MOVR7,#100;立即数100送R7单元
READ:
MOVA,P1;P1口输入送A
CHK0:
CJNEA,#0,CHK1;P1口输入内容与0比较
INC30H;30H单元内容加1,统计输入0的次数
DJNZR7,READ;当(R7)-1≠0,返回READ继续输入
CHK1:
CJNEA,#1,CHK2;(A)≠1,转CHK2
INC31H;当(A)=1,则(31H)+1,统计输入1的次数
DJNZR7,READ;当(R7)-1≠0,转READ继续输入
CHK2:
CJNEA,#2,CHK3;(A)≠2,转CHK3
INC32H;当(A)=2,则(32H)+1,统计输入2的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK3:
:
CJNEA,#3,CHK4;(A)≠3,转CHK4
INC33H;当(A)=3,则(33H)+1,统计输入3的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK4:
CJNEA,#4,CHK5;(A)≠4,转CHK5
INC34H;当(A)=4,则(34H)+1,统计输入4的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK5:
CJNEA,#5,CHK6;(A)≠5,转CHK6
INC35H;当(A)=5,则(35H)+1,统计输入5
的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK6:
CJNE
A,#6,CHK7;(A)≠6,转CHK7
INC36H;当(A)=6,则(36H)+1,统计输入6的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK7:
CJNEA#7,CHK8;(A)≠7,转CHK8
INC37H;当(A)=7,则(37H)+1,统计输入7的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK8:
CJNEA,#8,CHK9;(A)≠8,转CHK9
INC38H;当(A)=8,则(38H)+1,统计输入8的次数
DJNZR7,READ;当(R7)-1≠0,转READ
CHK9:
INC39H;当(A)=9,则(39H)+1,统计输入9的次数
DJNZR7,READ;当(R7)-1≠0,转READ
HERE:
SJMP$;统计结束
以上程序是对从P1口输入100个0~9的数的概率统计,统计的次数分别存储在30H~39H单元中,其中DJNE为循环转移指令。
每循环一次,计数器R7单元内容(100)减1,并判等于0否,若不为0,则转移到READ,继续P1口的输入和统计;若为0,则结束循环,表示从P1口已输入完100个数。
用CJNE指令实现多分支方法的优点是层次清晰,程序简单易懂。
但这种方法各分支需逐次判断,特别是分支较多时,速度很慢,程序效率低。
此外,分支入口地址应在8位偏移量的有效范围之内。
该方法适用于N<8的分支程序设计。
例3:
用间接转移指令(JMP@A+DPTR)实现多分支程序转移,这条指令只需通过一次转移即可转入相应的分支处理程序,效率较高。
实现的方法是:
在程序中建立一个地址差值表,并将各分支入口地址与该表首址的差值按序排列其中;差值表首址送DPTR,分支序号值送A中,查表后就可通过转移指令JMP@A+DPTR进行分支。
假定有4个分支程序段,各分支段的功能依次为:
从内部RAM取数,从外部RAM低256B范围取数,从外部RAM地段4KB范围取数和从外部RAM64KB范围取数。
假定R0中存放RAM低8位地址,R1中存放RAM高8位地址,R3中存放分支序号值(0,1,2,3),SUBTAB为差值表首地址,RAM0-SUBTAB~RAM3-SUBTAB为8位的地址差值。
以下程序段实现4路分支转.。
……
MOVA,R3;分支号送A
MOVDPTR,#SUBTAB;地址差值表首址送DPTR
MOVCA,@A+DPTR;查表
JMP@A+DPTR;转移
SUBTAB:
DBRAM0-SUBTAB;建立地址差值表
DBRAM1-SUBTAB
DBRAM2-SUBTAB
DBRAM3-SUBTAB
RAM0:
MOVA,@R0;从内部RAM中取数
SJMPDONE
RAM1:
MOVXA,@R0;从外部RAM256B取数
SJMPDONE
RAM2:
MOVA,R1;从外部RAM低段4KB取数,只需送12位地址
`ANLA,#00001111B
ANLP2,#11110000B
ORLP2,A
MOVXA,@R0
SJMPDONE
RAM3:
MOVDPL,R0;从外部RAM64KB取数
MOVDPH,R1
MOVXA,@DPTR
DONE:
SJMP$
其中RAM2分支是从外部RAM低段的4KB范围取数,因此需要12位地址,程序中对所需高4位地址的处理,是为了留出P2的高4位口线以作它用。
查表方法的技巧性较强,由于地址差值表的差值A只限于8位,地址表的长度加上第N路分支处理程序的入口地址的长度<256B,使分支程序入口地址的分布范围受到限制。
当分支较多或分支程序较长时,应采用其它方法。
例4:
使用查表方法把转移指令直接放入表中,实现N<256分支程序转移
方法是用AJMP指令直接建立分支程序转移地址入口表,使这些分支程序都通过绝对转移指令AJMP或LJMP进行转移。
设分支序号值存放在R3中,则可用如下程序段实现多分支转移:
……
MOVA,R3;送分支号
MOVDPTR,#SUBTAB
CLRC
RLCA;(A)×2
JNCA-JMP;前128分支程序,直接转移
INCDPH;后128分支程序,则基址高8位加1再转移
A-JMP:
JMP@A+DPTR
SUBTAB:
AJMPPROGM0;转移分支程序入口地址表
AJMPPROGM1
AJMPPROGM2
…………
AJMPPROGMn
使用指令把分支序号值乘以2是因为AJMP指令为2字节指令。
分支实现过程是根据分支序号值(R3),通过JMP指令转向SUBTAB表中的某一条AJMP指令,然后再执行该AJMP指令,而把程序转移到指定分支的入口。
这种分支方法实际上是通过两次转移而实现的。
由于AJMP指令的转移范围是2KB,所以散转表首地址SUBTAB和处理程序入口地址PROGM0、PROGM1、,须在同一个2KB范围内安排,如在此区域放不下所有的处理程序时,可有以下两种方法解决:
(1)在较长处理程序的入口地址单元内存放LJMP指令,将该处理程序跳转到其它存储区(第三次跳转),例如处理程序PROGM0、PROGM1较长,则在PROGM0、PROGM1的入口处安排下列指令:
PROGM0:
LJMPPPROGM0
PROGM1:
LJMPPPROGM1
(2)把表中的转移指令改为长转移LJMP,则分支程序可在64KB范围内分布,但这时要对分支序号值以乘以3处理。
另外,通过堆栈操作也可实现多分支序号程序转移,参考其他资料。
4.2.2查表程序设计
1.查表技术
在微型机控制系统中,有些参数的计算是非常复杂的,用计算法计算不仅程序长,难于计算,而且需要耗费大量时间。
还有一些非线性参数,它们不是用一般算术运算就可以计算出来,而是要涉及到指数,对数,三角函数,以及积分,微分等运算。
所有这些运算用汇编语言编程计算都比较复杂,有些甚至无法建立相应的数学模型。
为了解决这些问题,可以采用查表法。
所谓查表法,就是把事先计算或测得的数据按一定顺序编制成表格,查表程序的任务就是根据被测参数的值或者中间结果,查出最终所需要的结果。
它具有程序简单,执行速度快等优点。
查表程序在微型机控制系统中应用非常广泛,例如,在键盘处理程序中,查找按键相应的命令处理子程序的入口地址;在LED显示程序中,获得LED数码管的显示代码;在一些快速计算的场合,根据自变量的值,从函数表上查找出相应的函数值以及实现非线性修正、代码转换等等。
所有这些应用,都需采用查表技术。
在MCS-51系列单片机中,设有专门的查表指令,因此,查表程序的设计比较简单。
具体来讲,该系列单片机的查表指令有两种:
MOVCA,@A+PC和MOVCA,@A+DPTR。
这是两条从程序存储器到累加器的传送指令,用于查找程序存储器空间的代码或常数,每次传送一个字节。
前者是以PC为基地址,以A累加器的内容为变址,从(PC+A)存储单元中读出待查找的内容;后者则是以DPTR的内容为基地址,以A累加器的内容为变址,从(DPTR+A)存储单元中读出待查数。
值得注意的是,前者只能访问该指令以后的256个单元,因此称为短查表指令;后者则可以访问64KB程序存储单元,故称其为长查表指令。
此外,在查表程序中,为了把要查找的关键字和表中的数进行比较,还经常用到CJNE指令。
:
查表程序的繁简程度及查询时间的长短,除与表格的长短有关外,很重要的因素在于表格的排列方法。
一般来讲,表格有两种排列方法:
(1)无序表格,即表中的数是任意排列的;
(2)有序表格,即表中的数是按一定的顺序,如表中各项均按大小顺序排列等。
表的排列不同,查表的方法也不同。
2.查表程序设计
例5:
用查表方法求函数Y=
(0≥x≤9),设自变量x存放在VAR单元中,Y值存放在FUNC单元中。
源程序如下:
ORG0500H
VAREQU20H
FUNCEQU30H
MOVA,VAR;送x→A
ADDA,#04H;跳过查表指令的后两条指令(共占4字节)
MOVCA,@A+PC
MOVFUNC,A;将平方值Y送FUNC单元保存
SJMP$
SQUTAB:
DB00H,01H,04H,09H,16H;平方表
DB25H,36H,49H,64H,81H
END
指令ADDA,#04H的目的要绕过查表指令的后两条指令,指向表头SQUTAB。
如果查表指令和表的首址间还有其它指令,这些指令所占字节数也应计算在内,以保证正确的查表。
只要给一个(0~9)的数,则执行上面的程序,就能在获得该数的平方值。
如果使用MOVCA,@A+DPTR指令,不需计算累加器A中需加的偏移量。
………
PUSHDPH;入栈保护DPTR
PUSHDPL;
MOVDPTR,#TAB
MOVA,VAR
MOVCA,@A+DPTR
MOVFUNC,A
POPDPL;恢复DPTR
POPDPH;
TAB:
DB00H,01H,04H,09H,16H,25H;平方表
DB36H,49H,64H,81H
………
例6:
根据功能键输入的一个ASCII命令字符,CPU转去执行相对应的处理程序。
设功能键的七个命令字符为:
A,D,E,L,M,X和R,其对应的处理程序入口地址标号分别为XA,XD,XE,XL,XM,XX和XR。
命令信息由P1口输入。
根据题意,用查表方法实现的键盘处理程序如下:
ORG4000H
MOVA,P1
START:
MOVDPTR,#TAB;将表首地址送到DPTR
MOVB,A;送命令字符到B中保存
LOOP:
CLRA
M
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 51 程序设计