590 likes | 787 Views
4.1 程序编制的方法和技巧. 第 4 章 80C51 的程序设计. 本章内容:. 4.2 源程序的编辑和汇编. 4.3 程序设计基础与举例. 4.1 程序编制的方法和技巧. 4.1.1 程序编制的步骤. 一、预完成任务的分析. 功能 要求 技术 指标 硬件资源和工作环境. 二、进行算法的优化. 速度 内存 使用. 三、程序总体设计及流程图绘制. 清晰的流程图是正确编制应用程序的基础和条件,是一个良好的编程习惯. 总流程图 : 程序的 逻辑结构 , 各程序 模块间 的相互关系。 局部流程图 : 程序模块的具体实施 细节 。.
E N D
4.1 程序编制的方法和技巧 第4章 80C51的程序设计 本章内容: 4.2 源程序的编辑和汇编 4.3 程序设计基础与举例
4.1程序编制的方法和技巧 4.1.1程序编制的步骤 一、预完成任务的分析 • 功能要求 • 技术指标 • 硬件资源和工作环境 二、进行算法的优化 • 速度 • 内存使用
三、程序总体设计及流程图绘制 清晰的流程图是正确编制应用程序的基础和条件,是一个良好的编程习惯 • 总流程图:程序的逻辑结构,各程序模块间的相互关系。 • 局部流程图:程序模块的具体实施细节。 常用的流程图符号 程序流向 开始、结束 判断分支 工作任务 程序连接
一、采用模块化程序设计方法 4.1.2 编制程序的方法和技巧 • 主程序、子程序,如:发送、接收、延时、打印和显示等。 • 模块化的优点 • 便于程序设计和调试 • 便于程序优化和分工 • 提高程序阅读性和可靠性
长度减少、内存空间减少。 二、尽量采用循环结构和子程序 • 避免 “死循环,注意循环初值和结束条件 • 子程序用到的寄存器内容应压栈保护,并注意堆栈操作的压入和弹出的平衡; • 中断处理子程序除了要保护程序中用到的寄存器外,还应保护标志寄存器。
语句行组成 : [标号:]操作码[操作数] [;注释] 4.1.3 汇编语言的语句格式 字段间用分隔符分隔: 空格、冒号、逗号、分号等。 • 标号:语句地址的标志符号。 几项规定: • 由1~8个ASCII字符 • 第1个字符必须是字母,其余为字母、数字或其他特定字符 • 不使用已定义的符号 • 后边必须跟冒号
操作码 规定语句执行的操作。它是汇编语句中唯一不能空缺的部分。
操作码 规定语句执行的操作。它是汇编语句中唯一不能空缺的部分。 • 操作数 给指令的操作提供数据或地址。汇编语句中操作数可能是空缺的,也可能包括一项,还可能包括两项或三项。操作数字段可包括以下几种情况: • 工作寄存器名 • 特殊功能寄存器名 • 标号名 • 常数 • 符号“$”,表示PC的当前值 • 表达式
注释 增加程序可读性,该字段要以分号“;”开头,长度不限。 • 数据的表示形式 • 二进制数,末尾以字母 B 标识 • 十进制数,末尾以字母 D 标识或将字母D省略。 • 十六进制数,末尾以字母 H 标识。但应注意以字母A~F开头时应在其前面加上数字“0”。 • ASCII码,以单引号标识。如:‘AB’,‘1245’
4.2 源程序的编辑和汇编 4.2.1 源程序的编辑与汇编 • 源程序的编辑 据汇编语言规则,正确使用汇编命令(即伪指令): ORG 0040H MOV A,#7FH MOV R1,#44H END • ORG和END是两条伪指令 • 以“ . ASM”扩展名存盘。
汇编语言源程序转换为单片机能执行的机器码形式的目标程序叫汇编。常用方法有两种:汇编语言源程序转换为单片机能执行的机器码形式的目标程序叫汇编。常用方法有两种: • 源程序的汇编 • 手工汇编,通过手工方式查指令编码表,逐个把助记符指令翻译成机器码,然后把得到的机器码程序(以十六进制形式)键入到单片机开发机中,并进行调试。 • 机器汇编,在PC上,用交叉汇编程序将汇编语言源程序转换为机器码形式的目标程序。
汇编形成两个主要文件为:列表文件和目标码文件。例:汇编形成两个主要文件为:列表文件和目标码文件。例: 列表文件: 地 址 目标码 汇编程序 ORG 0040H 0040H 747F MOV A,#7FH 0042H 7944 MOV R1,#44H END 目标码文件: 首地址 末地址 目标码 0040H 0044H 747F7944
4.2.2 伪指令 汇编程序能识别并对汇编过程进行某种控制的汇编命令。无对应的可执行目标码。 • 起始地址设定 ORG表达式 • 向汇编程序说明下面紧接的程序段或数据段存放的起始地址。 • 表达式通常为16进制地址,也可以是已定义的标号地址。
源程序的开始,要设置一条ORG伪指令来指定该程序在存储器中存放的起始位置。源程序的开始,要设置一条ORG伪指令来指定该程序在存储器中存放的起始位置。 • 省略ORG,则该程序段从0000H单元开始存放。 • 可以多次使用ORG伪指令,但要求地址值由小到大依序排列,不允许空间重叠。 • 汇编结束 END 汇编程序遇到END伪指令后即结束汇编。
字节数据定义 [标号:] DB字节数据表 将字节数据表中的数据根据从左到右的顺序依次存放在指定的存储单元中。一个数据占一个存储单元。例: DB “how are you?” 把字符串中的字符以ASCII码的形式存放在连续的ROM单元中。又如: DB -2,-4,-6,8,10,18 把6个数转换为十六进制表示(FEH,FCH,FAH,08H,0AH,12H),并连续地存放在6个ROM。
常用于存放数据表格。如存放显示用的十六进制的字形码:常用于存放数据表格。如存放显示用的十六进制的字形码: DB 0C0H,0F9H,0A4H,0B0H DB 99H,92H,82H,0F8H DB 80H,90H,88H,83H DB 0C6H,0A1H,86H,84H
字数据定义 [标号:] DW字数据表 从标号指定的地址单元开始,在程序存储器中定义字数据。应注意:高8位存放在低地址单元,低8位存放在高地址单元。例: ORG 1400H DATA:DW 324AH,3CH … … 汇编后,(1400H)=32H,(1401H)= 4AH,(1402H)=00H,(1403H)=3CH。
空间定义 [标号:] DS表达式 从标号指定的地址单元开始,在程序存储器中保留由表达式所指定的个数的存储单元,并都填以零值。例如: ORG 3000H BUF:DS 50 … … 汇编后,从地址3000H开始保留50个存储单元作为备用单元。
赋值 符号名 EQU表达式 将表达式的值或特定的某个汇编符号定义为一个指定的符号名。
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个无符号数进行求和,并将结果存入SUM单元中。
位地址符号定义 符号名 BIT位地址表达式 将位地址赋给指定的符号名。位地址表达式可以是绝对地址,也可以是符号地址。 例如: ST BIT P1.0 将P1.0的位地址赋给符号名ST,在其后的编程中就可以用ST来代替P1.0。
4.3 基本程序结构 4.3.1 顺序程序 指无分支、无循环结构的程序。 一、数据传送 例4-3-1 有一变量在片内RAM的20H单元,取值范围:00H~05H。编制一段程序,根据变量值求其平方值,并存入片内RAM的21H单元。
ORG 1000H START:MOV DPTR,#2000H MOV A,20H MOVC A,@A+DPTR MOV 21H,A SJMP $ ORG 2000H TABLE:DB 00,01,04,09,16,25 END
MOVC A,@A+ DPTR,用DPTR指向表首址,则变量与DPTR之和的地址单元中的内容就是变量的平方值。 • MOVC A,@A+PC • 不破坏DPTR的内容,从而可以减少保护DPTR的内容所需的开销。 • 表格存放在该指令后的256字节内。
例4-3-2 三字节无符号数加法。 设被加数存放在内部RAM的50H、 51H、52单元,加数存放在内部RAM的53H、54H、55H单元,相加的结果存放在内部RAM的50H、51H、52H单元,进位存放在位寻址区的00H位中。 MOV R0,#52H MOV R1,#55H MOV A,@R0 ADD A,@R1 MOV @R0,A DEC R0 DEC R1 MOV A,@R0 ADDC A,@R1 MOV @R0,A DEC R0 DEC R1 MOV A ,@R0 ADDC A,@R1 MOV @R0,A MOV 00H,C
4.3.2 分支程序 可分成单分支、双分支和多分支几种 : Y K=0? 分支0 N 条件成立? 条件成立? Y K=1? 程序段A 程序段A 程序段B 分支1 N Y K=2? 下条指令 分支2 N
START: MOV R0,#addr1 MOV R1,#addr2 MOV A,@R0 CPL A INC A MOV @R1,A INC R0 INC R1 JZ ZERO MOV A,@R0 CPL A MOV @R1,A SJMP LOOP1 ZERO:MOV A,@R0 CPL A INC A MOV @R1,A LOOP1:END 一、单分支程序 例 求双字节有符号数的二进制补码。
二、双分支程序 例 设变量 x 以补码的形式存放在片内RAM的30H单元,变量 y 与 x 的关系是:当 x 大于0时,y =x;当 x =0时,y =20H;当 x 小于0时,y =x+5。编制程序,根据 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 $
三、多分支程序 例 根据R7的内容转向相应的处理程序。 设R7的内容为0~N,对应的处理程序的入口地址分别为PP0~PPN。 START:MOV DPTR,#TAB ;置分支入口地址表首址 MOV A,R7 ;分支转移序号送A ADD A,R7 ;分支转移序号乘以2 MOV R3,A ;暂存于R3 MOVC A,@A+DPTR ;取高位地址 XCH A,R3 INC A MOVC A,@A+DPTR ;取低位地址 MOV DPL,A ;处理程序入口地址低8位送DPL MOV DPH,R3 ;处理程序入口地址高8位送DPH CLR A JMP @A+DPTR TAB:DW PP0 DW PP1 ……… DW PPN
有先执行后判断和先判断后执行两种结构 : 4.3.3 循环程序
一、先执行后判断 例 50ms延时程序。 若晶振频率为12MHz,则一个机器周期为1μs。执行一条DJNZ指令需要2个机器周期,即 2μs。采用循环计数法实现延时,循环次数可以通过计算获得,并选择先执行后判断的循环结构。 DEL:MOV R7,#200 ;1 μs DEL1:MOV R6,#123 ;1 μs NOP ;1 μs DEL2:DJNZ R6,DEL2 ;2μs,计(2×123)μs DJNZ R7,DEL1 ;2μs, RET 共计 [(2×123+2+ 2)×200+1] μs,即50.001ms
二、先判断后执行 例 将内部RAM中起始地址为data的数据串传送到外部RAM中起始地址为buffer的存储区域内,直到发现‘$ ’字符停止传送。由于循环次数事先不知道,但循环条件可以测试到。所以,采用先判断后执行的结构比较适宜。 MOV R0,#data MOV DPTR,#buffer LOOP0:MOV A,@R0 CJNE A,#24H,LOOP1 ;判是否为‘$ ’字符 SJMP LOOP2 ;是‘$ ’字符,转结束 LOOP1:MOVX @DPTR,A ;不是‘$ ’字符,执行传送 INC R0 INC DPTR SJMP LOOP0 ;传送下一数据 LOOP2:……
子程序的调用 对于通用性的问题,例如:数值转换、数值计算等,往往要进行多次。宜将其设计成子程序。 4.3.4 子程序及其调用 • 子程序在执行时需要由其它程序来调用 • 执行完后又需要把执行流程返回到调用的主程序。 调用时注意:一是现场的保护和恢复;二是主程序与子程序的参数传递。
现场保护与恢复 子程序经常用到R0~R7、A、DPTR,以及PSW等。而这些单元中的内容在调用结束后的主程序中仍有用,所以需要进行保护(即现场保护)。 在执行完子程序,返回继续执行主程序前恢复其原内容,称为现场恢复。保护与恢复的方法有以下两种: • 在主程序中实现; • 在子程序中实现。
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 程序规范、清晰。要注意保护与恢复的顺序。
三、参数传递 传递参数的方法有三种: • 利用累加器或寄存器 • 先把子程序需要的数据送入A或指定的工作寄存器中 • 当子程序执行时,可从指定的单元中取得数据,执行运算 子程序也可以用同样的方法把结果传送给主程序。
例 编写程序,实现c=a2+b2 。设a,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
利用存储器 数据量比较大时,利用存储器实现参数的传递,先建立一个参数表,用指针指示参数表所在位置。 • 参数表建立在内部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
利用堆栈 堆栈传递参数是在子程序嵌套中常采用的一种方法。 例 把内部RAM中20H单元中的1个字节十六进制数转换为2位ASCII码,存放在R0指示的两个单元中。
MAIN:MOV A,20H ; SWAP A PUSH ACC ;参数入栈 ACALL HEASC POP ACC MOV @R0,A ;存高位十六进制数转换结果 INC R0 ;修改指针 PUSH 20H ;参数入栈 ACALL HEASC POP ACC MOV @R0,A ;存低位十六进制数转换结果 SJMP $
HEASC:MOV R1,SP ;借用R1为堆栈指针 DEC R1 DEC R1 ;R1指向被转换数据 XCH A,@R1 ;取被转换数据 ANL A,#0FH ;取一位十六进制数 ADD A,#2 ; 所加值为MOVC与DB间字节数 MOVC A,@A+PC ;查表 XCH A,@R1 ;1字节指令,存结果于堆栈 RET ;1字节指令 ASCTAB:DB 30H,31H,32H,33H,34H,35H,36H,37H DB 38H,39H,41H,42H,43H,44H,45H,46H
一般说来: • 数据较少时,采用寄存器传递方式可以获得较快的传递速度; • 数据较多时,宜采用存储器或堆栈方式传递; • 若是子程序嵌套,宜采用堆栈方式。
4.4 常用程序举例 4.4.1 算术运算程序 • 多字节数的加、减运算 合理地运用进位(借位)标志 例 多字节无符号数的加法。 设两个N字节的无符号数分别存放在内部RAM中以DATA1和DATA2开始的单元中。相加后的结果要求存放在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 ;
多字节数乘法运算 例 设双字节无符号被乘数存放在R3、R2中,乘数存放在R5、R4中,R0指向积的高位。
例 多字节无符号数的减法。 设两N字节数分别放在内部RAM中以DATA1和DATA2开始的单元中。相减后的结果要求存放在DATA2数据区。 MOV R0,#DATA1 ; MOV R1,#DATA2 ; MOV R7,#N ;置字节数 CLR C ; LOOP:MOV A,@R0 ; SUBB A,@R1 ;求差 MOV @R1,A ;存结果 INC R0 ;修改指针 INC R1 ; DJNZ R7,LOOP ;
主程序: 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 ; INC R0 ; ADDC A,@R0 ; MOV @R0,A ; INC R0 ; MOV A,@R0 ; ADDC A,#00H ;加进位 MOV @R0,A ; RET