1、4.1 程序编制的方法和技巧程序编制的方法和技巧 4.1.1 程序编制的步骤一、预完成任务的分析 首先,要对单片机应用系统预完成的任务进行深入的分析,明确系统的设计任务、功能要求和技术指标。其次,要对系统的硬件资源和工作环境进行分析。这是单片机应用系统程序设计的基础和条件。二、进行算法的优化 算法是解决具体问题的方法。同一个问题的算法可以有多种,结果也可能不尽相同,应对各种算法进行分析比较,并进行合理的优化。兼顾速度和内存使用三、程序总体设计及流程图绘制 经过任务分析、算法优化后,就可以进行程序的总体构思,确定程序的结构和数据形式,考虑资源的分配和参数的计算。画出程序流程图清晰正确的流程图是编
2、制正确无误的应用程序的基础和条件。所以,绘制一个好的流程图,是程序设计的一项重要内容。流程图可以分为总流程图和局部流程图。总流程图侧重反映程序的逻辑结构和各程序模块之间的相互关系。局部流程图反映程序模块的具体实施细节。对于简单的应用程序,可以不画流程图。但是当程序较为复杂时,绘制流程图是一个良好的编程习惯。常用的流程图符号有:开始和结束符号、工作任务符号、判断分支符号、程序连接符号、程序流向符号等 4.1.2 编制程序的方法和技巧一、采用模块化程序设计方法 应用系统的程序由包含多个模块的主程序和各种子程序组成。各程序模块都要完成一个明确的任务,实现某个具体的功能,如:发送、接收、延时、打印和显
3、示等。把一个多功能的复杂的程序划分为若干个简单的、功能单一的程序模块,有利于程序的设计和调试,有利于程序的优化和分工,提高了程序的阅读性和可靠性,使程序的结构层次一目了然。二、尽量采用循环结构和子程序 采用循环结构和子程序可以使程序的长度减少、占用内存空间减少。多重循环,注意各重循环的初值和循环结束条件,避免出现“死循环”现象;通用的子程序,除了用于存放子程序入口参数的寄存器外,子程序中用到的其它寄存器的内容应压入堆栈进行现场保护,并要特别注意堆栈操作的压入和弹出的平衡;中断处理子程序除了要保护程序中用到的寄存器外,还应保护标志寄存器。4.2 源程序的编辑和汇编 一、源程序的编辑 要依据89C
4、51汇编语言的基本规则,特别要用好常用的汇编命令(即伪指令):ORG 0040H MOV A,#7FH MOV R1,#44H END 编辑好的源程序应以“.ASM”扩展名存盘,以备汇编程序调用。4.2.1 源程序的编辑与汇编二、源程序的汇编 将汇编语言源程序转换为单片机能执行的机器码形式的目标程序的过程叫汇编。常用的方法有两种:手工汇编时,把程序用助记符指令写出后,通过手工方式查指令编码表,逐个把助记符指令翻译成机器码,然后把得到的机器码程序(以十六进制形式)键入到单片机开发机中,并进行调试。机器汇编是在常用的个人计算机PC上,使用交叉汇编程序将汇编语言源程序转换为机器码形式的目标程序。生成
5、的目标程序由PC机传送到开发机上,经调试无误后,再固化到单片机的程序存储器ROM中。源程序经过机器汇编后,形成的若干文件中含有两个主要文件,一是列表文件,另一个是目标码文件。因汇编软件的不同,文件的格式及信息会有一些不同。但主要信息如下:列表文件:地 址 目标码 汇编程序 ORG 0040H0040H 747F MOV A,#7FH0042H 7944 MOV R1,#44H END目标码文件:首地址 末地址 目标码0040H 0044H 747F7944 伪指令是汇编程序能够识别并对汇编过程进行某种控制的汇编命令。它不是单片机执行的指令,所以没有对应的可执行目标码,汇编后产生的目标程序中不会
6、再出现伪指令。4.2.2 伪指令一、起始地址设定伪指令 ORG格式为:ORG 表达式 该指令的功能是向汇编程序说明下面紧接的程序段或数据段存放的起始地址。表达式通常为16进制地址,也可以是已定义的标号地址。ORG 8000HSTART:MOV A,#30H 此时规定该段程序的机器码从地址8000H单元开始存放。在每一个汇编语言源程序的开始,都要设置一条ORG伪指令来指定该程序在存储器中存放的起始位置。若省略ORG伪指令,则该程序段从0000H单元开始存放。在一个源程序中,可以多次使用ORG伪指令规定不同程序段或数据段存放的起始地址,但要求地址值由小到大依序排列,不允许空间重叠。二、汇编结束伪指
7、令 END格式为:END 该指令的功能是结束汇编。汇编程序遇到END伪指令后即结束汇编。处于END之后的程序,汇编程序将不处理。三、字节数据定义伪指令 DB 标号:DB 字节数据表 功能是从标号指定的地址开始,在ROM中定义字节数据。该伪指令将字节数据表中的数据根据从左到右的顺序依次存放在指定的存储单元中。一个数据占一个存储单元。例如:DB “how are you?”把字符串中的字符以ASCII码的形式存放在连续的ROM单元中。又如:DB 2,4,6,8,10,18把6个数转换为十六进制表示(FEH,FCH,FAH,08H,0AH,12H),并连续地存放在6个ROM。该伪指令常用于存放数据表
8、格。如要存放显示用的十六进制的字形码,可以用多条DB指令完成:DB 0C0H,0F9H,0A4H,0B0H DB 99H,92H,82H,0F8H DB 80H,90H,88H,83H DB 0C6H,0A1H,86H,84H四、字数据定义伪指令 DW 标号:DW 字数据表 功能是从标号指定的地址单元开始,在程序存储器中定义字数据。该伪指令将字或字表中的数据根据从左到右的顺序依次存放在指定的存储单元中。应特别注意:16位的二进制数,高8位存放在低地址单元,低8位存放在高地址单元。五、空间定义伪指令 DS 标号:DS 表达式 功能是从标号指定的地址单元开始,在程序存储器中保留由表达式所指定的个数
9、的存储单元作为备用的空间,并都填以零值。例如:ORG 3000HBUF:DS 50 汇编后,从地址3000H开始保留50个存储单元作为备用单元。六、赋值伪指令 EQU符号名 EQU 表达式 功能:将表达式的值(一个8位或16位的数)或特定的某个汇编符号定义为一个指定的符号名。例如:A09 EQU R1 LEN EQU 10 SUM EQU 21H BLOCK EQU 22H CLR A MOV R7,LEN MOV R0,BLOCK LOOP:ADD A,R0 INC R0 DJNZ R7,LOOP MOV SUM,A END 该程序的功能是,把BLOCK单元开始存放的10个无符号数进行求和,
10、并将结果存入SUM单元中。七.数据地址赋值伪指令:字符名 DATA nn DATA 指令与EQU指令功能类似,它是将16位地址赋值所定义的字符名。DATA指令与EQU指令主要的区别是:EQU定义的名必须先定义后使用,而DATA定义的字符名没有这种限制,故DATA伪指令通常用在原程序的开头或末尾 。八、位地址符号定义伪指令 BIT格式为:符号名 BIT 位地址表达式 功能:将位地址赋给指定的符号名。其中,位地址表达式可以是绝对地址,也可以是符号地址。例如:ST BIT P1.0将P1.0的位地址赋给符号名ST,在其后的编程中就可以用ST来代替P1.0。4.3 基本程序结构程序的基本算法结构有 3
11、 种:顺序结构分支(选择)结构循环结构 4.3.1 顺序程序 顺序程序是指无分支、无循环结构的程序。其执行流程是依指令在存储器中的存放顺序进行的。例:有一变量存放在片内RAM的20H单元,其取值范围为:00H05H。要求编制一段程序,根据变量值求其平方值,并存入片内RAM的21H单元。ORG 0000H AJMP MAIN ORG 1000HMAIN:MOV DPTR,#2000H MOV A,20H MOVC A,A+DPTR MOV 21H,A SJMP$ORG 2000HTABLE:DB 00,01,04,09,16,25 END。#2000H DPTR(20H)A(A+DPTR)AA
12、21H开始结束 例 双字节无符号数加法。设被加数存放在内部RAM的51H、50H单元,加数存放在内部RAM的61H、60H单元,相加的结果存放在内部RAM的51H、50H单元,进位存放在位寻址区的00H位中。程序段如下:MOV R0,50H;被加数的低字节地址MOV R1,60H;加数的低字节地址MOV A,R0 ;取被加数低字节ADD A,R1 ;加上加数低字节MOV R0,A ;保存低字节相加结果INC R0 ;指向被加数高字节INC R1 ;指向加数高字节MOV A,R0 ;取被加数高字节ADDC A,R1 ;加上加数高字节(带进位加)MOV R0,A ;存高字节相加结果MOV 00H,
13、C ;保存进位。4.3.2 分支程序 分支结构可以分成单分支、双分支和多分支几种情况:双分支程序例 设变量 x 以补码的形式存放在片内RAM的30H单元,根据 x 的大小求y并送回原单元。程序段如下:START:MOV A,30H JZ NEXT ANL A,#80H ;判断符号位 JZ LP MOV A,#05H ADD A,30H MOV 30H,A SJMP LP NEXT:MOV 30H,#20H LP:SJMP$520yxHx多分支程序例 根据R7的内容转向相应的处理程序。设R7的内容为07F,对应的处理程序的入口地址分别为PP00PP7F。程序段如下:START:MOV A,R7
14、;分支转移序号送A RL A ;分支转移序号乘以2 MOV DPTR,#TAB ;置分支入口地址表首址 JMP A+DPTR TAB:AJMP PP00 AJMP PP01 AJMP PP7F4.3.3 4.3.3 循环程序循环程序按某种控制规律重复执行的程序称为循环程序。循环程序有先执行后判断和先判断后执行两种基本结构:一、先执行后判断例 50ms延时程序(晶振频率为12MHz)。程序段如下:DEL:MOV R7,#200 ;1 sDEL1:MOV R6,#123 ;1 s NOP ;1 sDEL2:DJNZ R6,DEL2 ;2s,计(2123)s DJNZ R7,DEL1;2s,RET
15、共计(21232 2)200+1 s,即50.001ms二、先判断后执行 例 将内部RAM中起始地址为data的数据串传送到外部RAM中起始地址为buffer的存储区域内,直到发现$字符停止传送。MOV R0,#data MOV DPTR,#bufferLOOP0:MOV A,R0 A,#24H,LOOP1;判断是否为$字符 SJMP LOOP2 ;是$字符,转结束LOOP1:MOVX DPTR,A ;不是$字符,执行传送 INC R0 INC DPTR SJMP LOOP0 ;传送下一数据 LOOP2:4.3.4 4.3.4 子程序及其调用子程序及其调用一、子程序的调用 在实际应用中,经常会
16、遇到一些带有通用性的问题,例如:数值转换、数值计算等,在一个程序中可能要使用多次。这时可以将其设计成通用的子程序供随时调用。子程序主要特点是,在执行过程中需要由其它程序来调用,执行完后又需要把执行流程返回到调用该子程序的主程序。子程序调用时要注意两点:一是现场的保护和恢复;二是主程序与子程序的参数传递。二、现场保护与恢复 在子程序执行过程中常常要用到单片机的一些通用单元,如工作寄存器R0R7、累加器A、数据指针DPTR,以及有关标志和状态等。而这些单元中的内容在调用结束后的主程序中仍有用,所以需要进行保护,称为现场保护。在执行完子程序,返回继续执行主程序前恢复其原内容,称为现场恢复。保护与恢复
17、的方法有以下两种:在主程序中实现;在子程序中实现。1、在主程序中实现 示例如下:PUSH PSW ;保护现场 PUSH ACC ;PUSH B ;MOV PSW,#10H;换当前工作寄存器组 LCALL addr16 ;子程序调用 POP B ;恢复现场 POP ACC ;POP PSW ;其特点是结构灵活。2、在子程序中实现示例如下:SUB1:PUSH PSW ;保护现场 PUSH ACC ;PUSH B ;MOV PSW,#10H;换当前工作寄存器组 POP B ;恢复现场 POP ACC ;POP PSW ;RET其特点是程序规范、清晰。注意,无论哪种方法保护与恢复的顺序要对应。三、参数
18、传递 由于子程序是主程序的一部分,所以,在程序的执行时必然要发生数据上的联系。在调用子程序时,主程序应通过某种方式把有关参数(即子程序的入口参数)传给子程序,当子程序执行完毕后,又需要通过某种方式把有关参数(即子程序的出口参数)传给主程序。在89C51单片机中,传递参数的方法有三种:1、利用累加器或寄存器 在这种方式中,要把预传递的参数存放在累加器A或工作寄存器R0R7中。即在主程序调用子程序时,应事先把子程序需要的数据送入累加器A或指定的工作寄存器中,当子程序执行时,可以从指定的单元中取得数据,执行运算。反之,子程序也可以用同样的方法把结果传送给主程序。例 编写程序,实现c=a2+b2。设a
19、,b,c分别存于内部RAM的30H,31H,32H三个单元中。程序段如下:START:MOV A,30H ;取a ACALL SQR ;调用查平方表 MOV R1,A ;a2 暂存于R1中 MOV A,31H ;取b ACALL SQR ;调用查平方表 ADD A,R1 ;a2+b2 存于A中 MOV 32H,A ;存结果 SJMP$SQR:MOV DPTR,#TAB ;子程序 MOVC A,A+DPTR;RET TAB:DB 0,1,4,9,16,25,36,49,64,81 2、利用存储器 当传送的数据量比较大时,可以利用存储器实现参数的传递。在这种方式中,事先要建立一个参数表,用指针指示
20、参数表所在的位置。当参数表建立在内部RAM时,用R0或R1作参数表的指针。当参数表建立在外部RAM时,用DPTR作参数表的指针。例 将R0和R1指向的内部RAM 中两个3字节无符号整数相加,结果送到由R0指向的内部RAM中。入口时,R0和R1分别指向加数和被加数的低位字节;出口时,R0指向结果的高位字节。低字节在高地址,高字节在低地址。实现程序:NADD:MOV R7,#3 ;三字节加法 CLR C ;NADD1:MOV A,R0 ;取加数低字节 ADDC A,R1 ;被加数低字节加A MOV R0,A ;DEC R0 DEC R1 DJNZ R7,NADD1 INC R0 RET 3、利用堆
21、栈 利用堆栈传递参数是在子程序嵌套中常采用的一种方法。在调用子程序前,用PUSH指令将子程序中所需数据压入堆栈,进入执行子程序时,再用POP指令从堆栈中弹出数据。一般说来:当相互传递的数据较少时,采用寄存器传递方式可以获得较快的传递速度;当相互传递的数据较多时,宜采用存储器或堆栈方式传递;如果是子程序嵌套时,最好是采用堆栈方式。4.4 常用程序举例4.4.1 算术运算程序 一、多字节数的加、减运算 89C51单片机的指令系统提供的是字节运算指令,所以在处理多字节数的加减运算时,要合理地运用进位(借位)标志。例 多字节无符号数的加法。设两个N字节的无符号数分别存放在内部RAM中以DATA1和DA
22、TA2开始的单元中。相加后的结果要求存放在DATA2数据区。MOV R0,#DATA1;MOV R1,#DATA2;MOV R7,#N ;置字节数置字节数 CLR C ;LOOP:MOV A,R0 ;ADDC A,R1 ;求和;求和 MOV R1,A ;存结果;存结果 INC R0 ;修改指针;修改指针 INC R1 ;DJNZ R7,LOOP ;例 多字节无符号数的减法。设两个N字节的无符号数分别存放在内部RAM中以DATA1和DATA2开始的单元中。相减后的结果要求存放在DATA2数据区。MOV R0,#DATA1 ;MOV R1,#DATA2 ;MOV R7,#N ;置字节数 CLR C
23、 ;LOOP:MOV A,R0 ;SUBB A,R1 ;求差 MOV R1,A ;存结果 INC R0 ;修改指针 INC R1 ;DJNZ R7,LOOP ;二、多字节数乘法运算例 双字节无符号数的乘法。设双字节的无符号被乘数存放在R3、R2中,乘数存放在R5、R4中,R0指向积的高位。主程序段如下:MULTB:MOV R7,#04 ;结果单元清0 LOOP:MOV R0,#00H ;DJNZ R7,LOOP ;DEC R0 ACALL BMUL ;SJMP$另有2段子程序:BMUL RADD(在BMUL中被调用)先看子程序段:RADD:ADD A,R0 ;MOV R0,A ;MOV A,B
24、 ;INC R0 ;ADDC A,R0 ;MOV R0,A ;INC R0 ;MOV A,R0 ;ADDC A,#00H ;加进位 MOV R0,A ;RETBMUL:MOV A,R2 ;MOV B,R4 ;MUL AB ;低位乘 ACALL RADD ;MOV A,R2 ;MOV B,R5 ;MUL AB ;交叉乘 DEC R0 ;ACALL RADD ;MOV A,R4 ;MOV B,R3 ;MUL AB ;交叉乘 DEC R0 ;DEC R0 ;ACALL RADD ;MOV A,R5 ;MOV B,R3 ;MUL AB ;高字节乘 DEC R0 ;ACALL RADD ;DEC R0
25、RET一、十六进制数与ASCII码间的转换 十六进制数与ASCII码的对应关系如表所示。当十六进制数在09之间时,其对应的ASCII码值为该十六进制数加30H;当十六进制数在AF之间时,其对应的ASCII码值为该十六进制数加37H。4.4.2 码型转换码型转换 例 将1位十六进制数转换成相应的ASCII码。设十六进制数存放在R0中,转换后的ASCII 码存放于R2中。实现程序如下:HASC:MOV A,R0 ;取4位二进制数 ANL A,#0FH ;屏蔽掉高4位 PUSH ACC ;4位二进制数入栈 CLR C ;清进(借)位位 SUBB A,#0AH;用借位位的状态判断该数在09还是AF之间
26、 POP ACC ;弹出原4位二进制数 JC LOOP ;借位位为1,跳转至LOOP ADD A,#07H ;借位位为0,该数在AF之间,加37HLOOP:ADD A,#30H ;该数在09之间,加30H MOV R2,A ;ASCII码存于R2 RET 例 将多位十六进制数转换成ASCII码。设地址指针R0指向十六进制数低位,R2中存放字节数,转换后地址指针R0指向十六进制数的高位。R1指向要存放的ASCII码的高位地址。实现程序如下:HTASC:MOV A,R0 ;取低4位二进制数 ANL A,#0FH ;ADD A,#15 ;偏移量修正 MOVC A,A+PC ;查表 MOV R1,A
27、;存ASCII码 INC R1 ;MOV A,R0 ;取十六进制高4位 SWAP A ANL A,#0FH ;ADD A,#06H ;偏移值修正 MOVC A,A+PC ;MOV R1,A INC R0 ;指向下一单元 INC R1 ;DJNZ R2,HTASC ;字节数存于R2 RETASCTAB:DB 30H,31H,32H,33H,34H,35H,36H,37H DB 38H,39H,41H,42H,43H,44H,45H,46H思考题及习题1、89C51单片机汇编语言有何特点?2、利用89C51单片机汇编语言进行程序设计的步骤如何?3、常用的程序结构有哪几种?特点如何?4、子程序调用时
28、,参数的传递方法有哪几种?5、什么是伪指令?常用的伪指令功能如何?6、设被加数存放在内部RAM的20H、21H单元,加数存放在22H、23H单元,若要求和存放在24H、25H中,试编写出16位数相加的程序。7、编写一段程序,把外部RAM中1000H1030H的内容传送到内部RAM的30H60H中。8、编写程序,实现双字节无符号数加法运算,要求(R1R0)+(R7R6)(61H60H)。9、若89C51的晶振频率为6MHz,试计算延时子程序的延时时间。DELAY:MOV R7,#0F6H LP:MOV R6,#0FAH DJNZ R6,$DJNZ R7,LP RET10、在内部RAM 的21H单元开始存有一组单字节不带符号数,数据长度为30H,要求找出最大数存入BIG单元。11、编写程序,把累加器A中的二进制数变换成3位BCD码,并将百、十、个位数分别存放在内部RAM的50H、51H、52H中。12、编写子程序,将R1中的2个十六进制数转换为ASCII码后存放在R3和R4中。13、编写程序,求内部RAM中50H59H十个单元内容的平均值,并存放在5AH单元。