第四章 指令系统及汇编语言程序设计
任课教师:刘忠国 山东大学课程中心网站: http://course.sdu.edu.cn/G2S/stcmcu.cc 宏晶官方网站:http://www.stcmcu.com/ stc15系列单片机器件手册等 keil μvision软件下载及指导手册(Help→μvision Help) http://www.keil.com/ 何宾STC单片机原理及应用 > http://www.gpnewtech.com/study/stc/ Keil Software –Cx51 编译器用户手册: Cx51编译器--对传统和扩展的8051微处理器的优化的C编译器和库参考
第四章 指令系统及汇编语言程序设计 本章学习目标 了解助记符、指令格式 掌握单片机寻址方式 掌握单片机指令系统 掌握单片机汇编语言程序设计及开发环境 参考资料: keil μVision软件的帮助文件 06:02:17
第四章 指令系统及汇编语言程序设计语言 4.1 编程语言 4.2 指令格式及其分类 4.3 寻址方式 4.4 数据传送类指令 4.5 逻辑操作类指令 4.6 算术运算类指令 4.7 位操作指令 4.8 控制类转移指令 4.9 汇编语言程序设计 4.10 汇编语言程序调试 4.11利用ISP工具将程序下载到单片机中验证程序 06:02:17
§4.9 汇编语言程序设计 4.9.1 伪指令(Pseudo- Instruction) 用户将编辑好的汇编语言源程序通过专门的软件(称为 汇编程序)汇编成相应的机器语言程序时,需要有一些 专门的说明性语句。 例如,指定目标程序或数据存放的起始地址、给一些 指定的标号赋值、在内存中予留工作单元、表示源程 序结束等指令。 指令并不产生对应CPU操作的机器码, 故称伪指令, 也 叫指示性语句(Directives); 相对应的,可产生实质性操作 的指令叫指令性语句(Instructions), 指令性语句表示 CPU要进行的某种操作。 例, MOV A, #30H, 表示将立即数30H送到寄存器A中。 06:02:17
1、起始地址设置伪指令ORG(ORIGIN) 格式为: ORG xxxxH 4.9.1 伪指令 1、起始地址设置伪指令ORG(ORIGIN) 格式为: ORG xxxxH ORG是起始地址设置伪指令的操作码,xxxx是四位 十六进制地址。该指令表明其后紧跟的指令性语句 的机器码放在以xxxxH为起始地址的单元中。 例: ORG 0100H DELAY: MOV R0,#30H 06:02:17
1、起始地址设置伪指令ORG 一般,在整个程序的起始处放置一条“ORG 0000H”伪 指令,表明下面开始的指令性语句从0000H开始存放。 后面有一定独立性的程序段也可以用这条指令指定程 序段存放的起始地址。 例如: ORG 0000H START: LJMP MAIN MOV A,#00H …… ORG 0100H DELAY: MOV R0,#30H 0000H 0000 0010 0001H addr15~8 0002H addr7~0 0003H外部中断0中服入口地址 …… 0100H 0111 1000 0101H 0011 0000 …… 06:02:17
4.9.1 伪指令 2、数据定义伪指令 功能:定义一个数据存储区,其类型由数据定义伪指 令指定,可以给存储区赋初值,也可以仅仅给变量分 配存储单元,而不赋予特定的值。 一般格式: [标号:] 数据定义伪指令 操作数 [,操作数…] [;注释] 方括号中的内容为可选项。伪指令后面的操作数可以 不止一个。如有多个操作数,互相之间用逗号隔开。 例如: SEG: DB 23H ;定义一个字节 标号SEG 可以如下方式引用: MOV R0, #SEG MOV A, @R0 标号SEG 不能以变量方式被引用: MOV A, SEG 06:02:17
2、数据定义伪指令 常用的数据定义伪指令有DB和DW: (1)定义字节DB(Define Byte) [标号:] DB 〈项或项表〉 其中项或项表:指一个字节、数或数字串,或以引号 括起来的ASCII码字符串(一个字符用ASCII码表示, 相当于一个字节)。 MSG: DB 'Press A Key To Continue', 0 TAB: DB 2, 3, 5, 7, 11, 13, 17, 19, ';' 06:02:17
2、数据定义伪指令 (2)定义字DW(Define Word) DW伪指令格式:[标号:] DW <项或项表> 功能: 用于定义字变量。项或项表指所定义的一个字 (两个字节)或用逗号分开的字串。可用来定义地址。 每个字变量占2字节存储单元,两个字节存储单元相邻, 低(高)位字节在低地址中, 高(低)位字节在高地址中。 TABLE: 多个操作数时,按排列顺序从 低地址开始存放。 0100H 01H 0101H 00H 0102H 0103H 1AH 0104H 0105H 06H 0106H 0107H 0108H 0109H 08H TABLE: DW TABLE, TABLE+10, HERE HERE: DW 0 DW $ HERE: 06:02:17
(DS在“§5.3 单片机C语言程序调试”用到) 2、数据定义伪指令 (DS在“§5.3 单片机C语言程序调试”用到) (3)预留存储空间 DS (补充) 格式:[标号:] DS 表达式 功能:从标号地址开始, 保留若干个字节的内存空间 以备存放数据。保留字节单元数由表达式值决定。 例如: DSEG AT 0x30 ; 数据段起始地址30H VARIBLE: DS 5H ;该指令不能出现在代码段 汇编后从30H开始,预留5个字节的内存单元。 仅DS伪指令需在数据段中定义 数据段中的标号SEG 可以变量方式被引用: MOV VARIBLE, A; MOV R1, VARIBLE; CSEG AT 0080h; 代码段可用此指令定义起始地址, 或省略则默认为都是代码段。(仅作了解) 06:02:17
2、数据定义伪指令 例如: ORG 1000H SEG: DB 23H ;定义一个字节 DW 1000H ;定义一个字 DB ‘MCS-51’ ;定义一个字符串 END 则: (1000H)=23H SEG的地址为1000H (1001H)=00H 10H (1002H)=10H 00H (1003H)=4DH ‘M‘的ASCII码 (1004H)=43H ‘C’ 的ASCII码 (1005H)=53H ‘S’ 的ASCII码 (1006H)=2DH ‘-’的ASCII码 (1007H)=35H 数字5的ASCII码 (1008H)=31H 数字1的ASCII码 06:02:17
2、数据定义伪指令 由数据定义伪指令DB或者DW后面的操作数确定变量 的初值,常用的有以下几种形式: 常数或数值表达式。 ASCII码字符串。 ? 表示定义的变量无确定值。可用DS预留存储空间(见后) 或用EQU, DATA定义一个存储单元地址(见后) 汇编程序中的常数,可采用不同的数制和不同表示方法: 十进制数:数字的后面加一个字母“D”(Decimal),表 示是十进制数;或者什么也不加,默认为是十进制数。 二进制数:数字的后面加一个字母“B”(Binary),如 10101001B。 06:02:17
2、数据定义伪指令 DB, DW 十六进制数: 数字后面加一个字母“H” (Hexadecimal), 而且,当十六进制数字不是以数字0~9开始,而是以 字母(A~F)开头时,前面要再加一个前导数字0,这是 为了在进行汇编时,以区别是数字,不是符号名。 ASCII常数: 应将字符放在单引号中, 例如‘A’,‘8’等。 汇编语言语句中表达式为数值表达式, 由数值和运算 符组成, 产生一个数值结果。运算符为算术运算符, 常用算术运算符有: 十(加), –(减), *(乘), /(除)。 注意, 作为操作数部分的项或项表, 若为数值, 其取值 范围为00~0FFH(对DB)或0~0FFFFH (对DW) 。 06:02:17
4.9.1 伪指令 3、等值伪指令EQU(Equate) 标号 EQU 表达式 功能: 将语句操作数值赋于本语句标号。格式中表达式 可以是一常数、符号、数值表达式或地址表达式等。 若源程序中需多次引用某一表达式, 则可用EQU伪指令给 其赋一个标号(名字), 在以后的代码中, 可用该标号来代 替上述表达式,从而使程序更加简洁,便于阅读。 如欲改变表达式的值,也只需在EQU指令处修改一次, 而不必修改多处,使程序易于调试、修改和维护。 注意:此伪指令中的标号后面不能加冒号 “:” 06:02:17
3、等值伪指令EQU(Equate) 【例】 COLUMN EQU 32H ROW EQU 68H BUFFER DB ? BUFFER EQU 40H MOV A, #COLUMN MOV B, #ROW MUL AB MOV BUFFER, A MOV BUFFER+1, B ┊ (该程序段稍改一下才适于 51单片机) 执行后, 就把COLUMN 和 ROW 的乘积放在了单元 BUFFER和BUFFER+1中。 只要改变COLUMN 和 ROW的值就可以计算不同 数据的乘积。 或用DATA伪指令 可用直接寻址40H, 41H 注意:在同一程序中,用EQU伪指令对标号赋值后, 该标号的值在整个程序中不能再改变。 06:02:17
3、等值伪指令EQU(Equate) 【例】 COLUMN EQU 32H ROW EQU 68H DSEG AT 0x40 ;数据段始于40H BUFFER: DBS ? 2 CSEG AT 0100h ;代码段起始地址 MOV A, #COLUMN MOV B, #ROW MUL AB MOV BUFFER, A MOV BUFFER+1, B ┊ 在这里BUFFER地址只能用 BUFFER: DS 2在数据段(存 储器)里定义,数据段里不允许 用BUFFER: DB 01H定义, 若在代码段里定义BUFFER (无论是用BUFFER: DB 01H 还是用BUFFER: DS 2定义), 在这里BUFFER地址都不能 被识别, BUFFER不能用作目 的操作数地址, 因不能向程序 存储器里写数据, 只能从程序 存储器读数据。 注意:在同一程序中,用EQU伪指令对标号赋值后, 该标号的值在整个程序中不能再改变。 06:02:17
4.9.1 伪指令 4、DATA指令 符号名 DATA 表达式 该指令将一个内部RAM的地址赋给指定的符号名。表 达式必须是一个简单表达式, 其值在00H~0FFH之间。 例如: BUFFER DATA 40H 5、XDATA指令(Extenal Data) 符号名 XDATA 表达式 该指令将一个外部RAM的地址赋给指定的符号名。表 达式必须是简单表达式, 其值在0000H~0FFFFH之间。 例如: MYDATA XDATA 0100H 06:02:17
4.9.1 伪指令 6、定义位命令BIT 字符名称 BIT 位地址 用于给字符名称定义位地址。 例如: DOGOUT BIT P3.4; 或写成P3^4, 0B0H.4, 0B0H^4 经定义后, 允许在后续指令代码中用DOGOUT代替P3.4。 P3 DATA 0B0H P3,P0等已在头文件STC15.INC中定义, .是位操作符 7、文件包含命令INCLUDE 文件包含命令INCLUDE用于将寄存器定义文件(一般 的后缀名为.INC)包含于当前程序中,与C语言中的 #include语句的作用类似。 使用格式为: $INCLUDE (文件名) 06:02:17
7、文件包含命令INCLUDE 例如,为使用方便,把STC15F2K60S2单片机的寄存 器定义保存在文件STC15.INC中(见附录B)。 使用时,可在程序的开始处使用下面的命令将其包 含到用户程序中: $INCLUDE (STC15.INC) 使用上述命令后,在用户程序中就可以直接使用 STC15F2K60S2单片机的特殊寄存器名称了。 例, MOV CMOD, #10000000B ;设置PCA工作模式 寄存器定义的指令如: P3 DATA 0B0H 因文件中有寄存器定义: CMOD DATA 0D9H 06:02:17
4.9.1 伪指令 8、条件汇编控制指令 条件汇编控制指令的作用是使源程序中的一部分程 序行根据需要决定是否进行汇编。 4.9.1 伪指令 8、条件汇编控制指令 条件汇编控制指令的作用是使源程序中的一部分程 序行根据需要决定是否进行汇编。 条件汇编控制伪指令的一般格式: IF 表达式 [ 程序块1 ] [ ELSE ] [ 程序块2 ] ENDIF 06:02:17
8、条件汇编控制指令 当IF指令中的表达式为真时,汇编程序汇编程序块1; IF块可嵌套使用, 最大嵌套深度为63层(Keil μVision 集成开发环境只能达10层)。 如果IF ELSEIF和ELSE块不汇编,则其嵌套的条件 块也不汇编。 06:02:17
4.9.1 伪指令 9、汇编结束伪指令 格式: END [标号] 汇编程序结束时,最后一条伪指令为END。 4.9.1 伪指令 9、汇编结束伪指令 格式: END [标号] 汇编程序结束时,最后一条伪指令为END。 指令格式中的标号是整个汇编语言源程序第一条可 执行语句的标号。该伪指令告诉汇编程序:整个源 程序结束,汇编程序停止汇编,并指出第一条指令 性语句的标号。 在一般的程序中,END后的标号都省略不写。 06:02:17
根据算法要求分配资源,包括内部RAM、定时器、 中断等资源的分配。 根据流程图编写程序。 上机调试源程序,进而确定源程序。 4.9.2 汇编语言程序设计的一般步骤和基本框架 汇编语言程序设计的一般步骤是: 分析课题,确定算法或解题思路。 根据算法或思路画出流程图。 根据算法要求分配资源,包括内部RAM、定时器、 中断等资源的分配。 根据流程图编写程序。 上机调试源程序,进而确定源程序。 对于复杂的程序可以按功能分为不同的模块,按模 块功能确定结构。编写程序时采用模块化的程序设 计方法。 06:02:17
4.9.2 汇编语言程序设计的一般步骤和基本框架 $INCLUDE (STC15.INC) ;下面是汇编语言程序的框架 ;本语句包含STC15F2K60S2单片机寄存器定义头文件 ;---------------------这里可以编写程序中用到的一些符号定义(使用 EQU, DATA, BIT等伪指令) ORG 0000H START: LJMP MAIN ;跳转到主程序 ORG 0003H LJMP INT0_ISR ;外部中断0入口 ORG 000BH LJMP T0_ISR ;定时器0中断入口 ORG 0013H LJMP INT1_ISR ;外部中断1入口 ORG 001BH LJMP T1_ISR ;定时器1中断入口 ORG 0023H LJMP UART1_ISR ;串口1中断入口 06:02:17
4.9.2 汇编语言程序设计的一般步骤和基本框架 ORG 002BH LJMP ADC_ISR ;ADC中断服务程序入口 ORG 0033H LJMP LVD_ISR ;低电压检测中断服务程序入口 ORG 003BH LJMP PCA_ISR ;PCA中断服务程序入口 ORG 0043H LJMP UART2_ISR ;串口2中断服务程序入口 ORG 004BH LJMP SPI_ISR ;SPI中断服务程序入口 06:02:17
4.9.2 汇编语言程序设计的一般步骤和基本框架 ORG 0053H LJMP INT2_ISR ;INT2中断服务程序入口 ORG 005BH LJMP INT3_ISR ;INT3中断服务程序入口 ORG 0063H LJMP T2_ISR ;定时器2中断服务程序入口 ORG 0083H LJMP INT4_ISR ;INT4中断服务程序入口 06:02:17
;设置堆栈指针(可根据实际情况进行修改) ……;初始化内存区域内容 ……;设置有关特殊功能寄存器(SFR)的控制字 4.9.2 汇编语言程序设计的一般步骤和基本框架 ORG 0100H MAIN: MOV SP, #70H ;设置堆栈指针(可根据实际情况进行修改) ……;初始化内存区域内容 ……;设置有关特殊功能寄存器(SFR)的控制字 ……;根据需要开放相应的中断控制 MAINLOOP: ;主程序循环 LJMP MAINLOOP 06:02:17
4.9.2 汇编语言程序设计的一般步骤和基本框架 ;下面是各个中断服务子程序的入口 INT0_ISR: ;外部中断0服务子程序 …… ;根据需要填入适当的内容 RETI INT1_ISR: ;外部中断1服务子程序 T0_ISR: ;定时器0中断服务子程序 T1_ISR: ;定时器1中断服务子程序 UART1_ISR: ;串口1中断服务子程序 …… ;根据需要填入适当内容(注意中断请求标志位清零) 06:02:17
4.9.2 汇编语言程序设计的一般步骤和基本框架 UART2_ISR: ;串口2中断服务子程序 …… ;根据需要填入适当内容(注意中断请求标志位清零) RETI ADC_ISR: ;ADC中断服务子程序 SPI_ISR: ;SPI通信中断服务子程序 LVD_ISR: ;低电压检测服务子程序 PCA_ISR: ;PCA和PWM中断服务子程序 06:02:17
4.9.2 汇编语言程序设计的一般步骤和基本框架 INT2_ISR: ;INT2中断服务子程序 …… ;根据需要填入适当的内容 RETI INT3_ISR: ;INT3中断服务子程序 T2_ISR: ;定时器2中断服务子程序 INT4_ISR: ;INT4中断服务子程序 ;下面可以编写其他子程序或者定义程序中所用的常数 END 06:02:17
所以第一条指令是一条长跳转指令,跳到避开上述 中断处理子程序入口地址的0100H的地址,主程序 MAIN从这个地址开始存放; 4.9.2 汇编语言程序设计的一般步骤和基本框架 注意 由于地址0003H、000BH、0013H、001BH、0023H 、 002BH、0033H、003BH、0043H、004BH、0053H、 005BH、0063H和0083H是专门为中断处理子程序分 别预留的入口地址, 所以第一条指令是一条长跳转指令,跳到避开上述 中断处理子程序入口地址的0100H的地址,主程序 MAIN从这个地址开始存放; MAIN 语句前面的伪指令“ORG 0100H”表示,以 标号MAIN表示的主程序放在0100H开始的区域,当 然也可以是跳到能够避开上述入口地址的其他地址。 06:02:17
如果用户系统根本没有任何中断源,或者没有使用 全部中断源,就可以不用或者少用中断的功能; 4.9.2 汇编语言程序设计的一般步骤和基本框架 如果用户系统根本没有任何中断源,或者没有使用 全部中断源,就可以不用或者少用中断的功能; 0003H到0083H的区域也就无须全部或部分用于中断 处理。没有任何中断的情况下,主程序甚至可以从 0000H开始连续存放下去。 主程序的末尾是一条长跳转指令,跳转到某个合适 的地方反复执行主程序。 一般的子程序不可形成死循环,但是作为整个主程 序却应该是一个最大的死循环。无论执行哪个子程 序,之后都要回到主程序,反复循环运行。 06:02:17
程序流程图 4.9.2 汇编语言程序设计的一般步骤和基本框架 在程序编制以前,先根据系统方案绘制程序流程图 是一个很好的方法。 4.9.2 汇编语言程序设计的一般步骤和基本框架 程序流程图 在程序编制以前,先根据系统方案绘制程序流程图 是一个很好的方法。 程序流程图可以简洁清晰地将程序的分支走向标示 清楚,尤其是在程序复杂,编写人员较多相互衔接 容易出错的的情况下,利用流程图理顺各部分关系 显得尤为重要。 画流程图有两个常用的结构:顺序执行的矩形框和 条件分支的菱形框。 06:02:17
画流程图两结构:顺序执行矩形框和条件分支菱形框 顺序执行:某个局部功能或者顺序执行的语句使用矩形 方框表示,矩形方框内注明程序的功能,各方框之间用 箭头表示执行顺序,一目了然; 条件分支: 遇到需要根据条件判断是否转移时,使用菱形 方框表示, 菱形框内注明分支条件, 不同出口表明分支的 去向: 可以向后跳转, 也可向前跳转。 计数单元-1=0? 退出循环 Y N 循环体 Y N 循环体 R0-1→R0=0? 存储单元清零 堆栈指针赋初值 调用延时子程序 分支结构 顺序结构 分支结构另种画法 例: DJNZ R0, L1 06:02:17
程序流程图 循环程序设计 当程序处理的对象 具有重复性规律时, 可以使用循环程序 设计。一个循环表 示重复执行一组指 令(程序段)。 图4-17 典型循环程序结构的流程图 06:02:17
画流程图两结构:顺序执行矩形框和条件分支菱形框 条件分支: 菱形框内注明分支条件, 不同出口表明分支 的去向: 可以向后跳转, 也可向前跳转。一般框图如下 所示: 二分支结构 用: CJNZ, JZ, J(N)C, J(N)B等 多分支结构 06:02:17
1、分支程序设计 2、查表程序设计 3、循环程序设计 4、定点数运算子程序设计 5、数据排序程序设计 6、代码转换程序设计 4.9.3 典型汇编语言程序设计举例 1、分支程序设计 2、查表程序设计 3、循环程序设计 4、定点数运算子程序设计 5、数据排序程序设计 6、代码转换程序设计 06:02:17
1、分支程序设计 程序分支是通过条件转移指令实现的,即根据条件进 行判断后决定程序的走向。条件满足则进行程序转移, 不满足就顺序执行程序。 程序分支是通过条件转移指令实现的,即根据条件进 行判断后决定程序的走向。条件满足则进行程序转移, 不满足就顺序执行程序。 通过条件判断实现单分支程序转移的指令有JZ、JNZ、 CJNE (4条)和DJNZ(2条)等。 以位状态为条件,进行程序分支的指令JC、JNC、 JB、JNB和JBC等。 06:02:17
1、分支程序设计 【例4-9】编程实现下面的比较函数。设变量x存放 在R0,求得的y 值存入SIGN单元。 解:可以利用比较转移CJNE指令和进位位C状态控制 转移(JC指令)来实现三分支转移。 06:02:17
【例4-9】程序 SIGN EQU 50H ;求得的y 值存入SIGN单元 ORG 0000H LJMP MAIN ORG 0100H MAIN: CJNE R0, #37, NOTEQ ;R0与37比较, 不相等则转NOTEQ MOV SIGN, #00H ;若比较相等,则SIGN←0 LJMP ENDM ;转到程序结束 NOTEQ:JC NEG ;两数不相等, 若R0<37则C=1, 转NEG处理 MOV SIGN,#01H ;R0>37时,SIGN←+1 LJMP ENDM ;转到程序结束 NEG:MOV SIGN, #0FFH ;R0<37时, SIGN←-1 ENDM:NOP END 设变量x值存放在R0 06:02:17
2、查表程序设计 查表法产生的背景 参数的计算非常复杂; 公式计算法计算程序长,难于计算; 需要耗费大量时间; 非线性参数,无法用一般算术运算就可以计算出来, 如指数、对数、三角函数以及积分、微分等运算; 数学计算无法建立相应的数学模型。 查表法定义 就是把事先计算或测得的数据按一定顺序编制成表格, 查表程序根据被测参数的值或中间结果, 查出最终所需 的结果。它具有程序简单, 执行速度快等优点。 06:02:17
2、查表程序设计 应用: 在键盘处理程序中,查找按键相应的命令处理子程 序的入口地址; 在键盘处理程序中,查找按键相应的命令处理子程 序的入口地址; 在一些快速计算的场合,根据自变量的值,从函数 表上查找出相应的函数值以及实现非线性修正、代 码转换等等。 常用MOVC A,@A+DPTR查找程序存储器空间 的代码或常数,每次传送一个字节。 举例: 在LED显示程序中, 获得LED数码管显示字模; 06:02:17
2、查表程序设计 1 1 h g f e d c b a h g f e d c b a A中存要显示的0~9的数 例如, 假如要显示的数据需放到累加器A中, 采用共阳极LED显 示, 则可采用下面查表法程序获得LED显示字模: MOV DPTR, #SEGTAB ;获得字模表的首地址 MOVC A, @A+DPTR ;查表获得字模 …… ;下面可以送出字模进行显示 SEGTAB: DB 0C0H ;0的字模 DB 0F9H ;1的字模 DB 0A4H ;2的字模 DB 0B0H ;3的字模 DB 99H ;4的字模 DB 92H ;5的字模 DB 82H ;6的字模 DB 0F8H ;7的字模 DB 80H ;8的字模 DB 90H ;9的字模 如何转换 1 1 D7 D6 D5 D4 D3 D2 D1 D0 h g f e d c b a 数字的字模 h g f e d c b a 06:02:17
3、循环程序设计 DJNZ R7, LOOP 延时程序是典型的循环程序。 下面就以延时程序为例, 说明 循环程序的设计方法。 流程图如图所示。 DJNZ R7, LOOP 图4-18 延时程序流程图 06:02:17
3、循环程序设计 简单延时子程序如下(注释部分为指令的时钟周期数): MOV R7,#150 ;2T LOOP: DJNZ R7, LOOP ;4T, ;R7←R7-1, 若R7≠0, 则转到LOOP执行 RET STC15F2K60S2为1T的8051单片机, 当系统时钟为 6MHz时, 每时钟周期为1/6us, 上述程序可延时约0.1ms。 为达到准确延时的目的, 可在适当地方加入NOP指令。 延时时间: 150×4×1/6μs=100μs 06:02:17
3、循环程序设计 若需加长延时时间, 可采用多重循环延时程序方法。 若需加长延时时间, 可采用多重循环延时程序方法。 以下程序, 内循环延时0.1ms, 外循环次数可在调用前 (入口)设定, 根据设定值不同, 可在0.1~25.5ms间延时。 入口: 设置延时时间N (以0.1ms为单位) 送入R0。 出口: 若延时到(R0减到0), 则退出程序, 无参数传递。 MOV R0, #100 ;2T, R0控制外循环次数 DELAY: MOV R7, #150 ;2T, R7控制内循环次数 LOOP: DJNZ R7, LOOP ;4T,当6MHz时钟时,延时约0.1ms DJNZ R0, DELAY ;4T,外循环次数×0.1ms≈延时时间 RET 延时时间: 100×[2+(150×4)+4] ×1/6 μ s =10100μ s=10.1ms≈ 100×0.1 ms 06:02:17
4、定点数运算子程序设计 多字节无符号加法子程序和减法子程序设计较简单, 在此介绍有代表性的多字节BCD码减法程序和多字 节乘法程序的设计。 (1)多字节十进制BCD码减法 因指令系统中只有十进制加法调整指令DA A, 也即 该指令只有在加法指令(ADD、ADDC)后, 才能得到 正确的结果。 为了用十进制加法调整指令对十进制减法进行调整, 必须采用补码相加的办法,用9AH (100)减去减数即 得以10(100)为模的减数的补码。 GO 参考例4-8 06:02:17
求BCD码8943H - 7649H=? 编程前由实例测算计算过程。 以十六进制形式表示 求BCD码8943H - 7649H=? 编程前由实例测算计算过程。 先对低位字节运算43H - 49H: 1001 1010 模9A -) 0100 1001 减数49 0101 0001 得49对100补码51 +)0100 0011 加被减数43 0 1001 0100 差94 再对高字节运算89H -76H - C: 1001 1010 9A -) 0111 0110 76 0010 0100 得76对100补码为24 -)0000 0001 减去借位位C=1 0010 0011 减借位1后的值为23 +)1000 1001 加被减数89 1010 1100 结果0AC +) 0110 0110 对结果加66修正 10001 0010 差为12 应理解为 43向高位借位与49相减的结果 DA A 调整 后C=0 C=0无进位, 表示二者相减有 借位。应对借位C求反使C=1。 高字节减数变补与被减数相加调整后有进位1, 表示两者相减无借位, 为正确反映借位情况应对进位C求反使C=0(减法时C=1,表示有借位; C=0, 表示无借位)。最后结果为1294H, 且无借位, 计算正确。 06:02:17
(1)多字节十进制BCD码减法 BCD码减法程序举例:采用补码相加的办法,用 9AH(100)减去减数即得以10(100)为模的减数的补码。 程序说明(减法运算化成100的补码加法运算) 程序中,减数求补后与被减数相加,方可利用DA A 指令进行调整; 若二者相加调整(DA A)后结果无进位(C=0),实际 上表示二者相减有借位; 若二者相加调整(DA A)后有进位(C=1),实际上表 示二者相减没有借位(教材加文字); 参考例4-8 因此, 都需对进位位C进行求反操作。 06:02:17
(1)多字节十进制BCD码减法 编程代码: R1: 被减数低字节地址; R2: 字节数; R3: 差的字节数。 R0: 减数低字节地址;也是最后结果差(BCD码)的低字节地址 07H位地址存最终结果符号位。0表示结果为正,1表示结果为负。 SUBCD: MOV R3, #00H ;差的字节数置0 CLR 07H ;符号位单元清0 CLR C ;下面用带进位减法指令SUBB, 借位位C清0 SUBCD1: MOV A, #9AH SUBB A, @R0 ;求减数的100的补码 ADD A, @R1 ;补码与被减数相加 DA A ;十进制加法调整指令 MOV @R0, A ;结果差送到R0间接寻址单元 INC R0 ;减数地址值增1, 指向高字节 INC R1 ;被减数地址值增1, 指向高字节 INC R3 ;差的字节数增1 CPL C ;进位求反,以形成正确借位 DJNZ R2, SUBCD1 ;每字节减法算法相同,未完循环, 减完 顺序执行, JNC SUBCD2 ;无借位去SUBCD2返主, 否则继续 SETB 07H ;差为负置符号位07H为“1” SUBCD2: RET ;返回 06:02:17
GO (2)多字节乘法运算子程序 单片机指令系统中只有单字节乘法指令MUL AB, 而工 程应用中常需8位乘16位、两个16位数相乘的运算。 以两个16位无符号数相乘为例说明多字节乘法程序设计。 设被乘数放在R2、R3两单元(高字节在前), 乘数放在R6、 R7两单元, 两个双字节无符号数相乘, 结果送33H、32H、 31H、30H。 算法示意图如图所示。 (R3R7)L表示R3×R7的低8位, (R3R7)H表示R3×R7的高8位, 其余几项的含义类似。 程序如下: 06:02:17
(2)多字节乘法运算子程序 MOV B, R7 MUL AB ;R3×R7 MOV 30H, A ;(30H) ← (R3×R7)L DMUL: MOV A, R3 MOV B, R7 MUL AB ;R3×R7 MOV 30H, A ;(30H) ← (R3×R7)L MOV 31H, B ;(31H) ← (R3×R7)H MOV A, R2 MUL AB ;R2×R7 ADD A, 31H ;(R3×R7)H+(R2×R7)L MOV 31H, A CLR A ADDC A, B ;进位位C与(R2R7)H加 MOV 32H, A ;(32H) ← (R2R7)H 06:02:17
(2)多字节乘法运算子程序 MOV A, R3 MOV B, R6 MUL AB ;R3×R6 ADD A, 31H MOV 31H, A MOV A, B ADDC A, 32H ;(R2×R7)H+(R3×R6)H MOV 32H, A MOV F0, C ;暂存Cy ;因下面乘法使C清0 ;也可CLR A, ADDC A, #0, MOV 33H, A;下面程序相应修改 程序状态标志寄存器PSW 位号 D7 D6 D5 D4 D3 D2 D1 D0 符号 CY AC F0 RS1 RS0 OV F1 P F0: 用户标志位 06:02:17
(2)多字节乘法运算子程序 MOV F0, C ;暂存Cy MOV A, R2 MOV B, R6 MUL AB ;R2×R6 ADD A, 32H MOV 32H, A CLR A MOV ACC.0, C ;或 ADDC A, #0 MOV C, F0 ;前次加法进位送C, 为ADDC加(R2R6)H准备 ADDC A, B MOV 33H, A RET 程序状态标志寄存器PSW 位号 D7 D6 D5 D4 D3 D2 D1 D0 符号 CY AC F0 RS1 RS0 OV F1 P F0: 用户标志位 06:02:17
5、数据排序程序设计 数据排序是将数据块中的数据按升序或降序排列。下 面以数据升序排序为例, 说明数据排序程序设计方法。 数据升序排列常采用冒泡法。冒泡法是一种相邻数据 互换的排列方法,同查找极大值方法一样,一次冒泡 即找到数据块的极大值放到数据块最后, 再一次冒泡,次大数排在倒数第二位置,多次冒泡实现升序排列。 例, 将片内RAM 30H~37H中的数据从小到大升序排列。 设R6为循环次数计数器, R7为比较次数计数器。 F0为冒泡过程中是否有数据交换的状态标志,F0=0表 示无交换发生,F0=1表示有互换发生,须继续循环。 R0为指向RAM单元的地址指针初值为30H。 06:02:17
冒泡法数据排序程序流程图 数据在30H~37H中; R6为循环次数计数器 R7为比较次数计数器; F0为数据交换状态标志; R0地址指针. 06:02:18
冒泡法数据排序程序 SORT: MOV R6,#07H ;循环次数送到R6 GOON: CLR F0 ;交换标志清0 MOV R0, #30H ;数据首址送R0 MOV A, R6 MOV R7, A ;各次冒泡比较次数送R7 LOOP: MOV A, @R0 ;取前数 MOV 3BH, A ; 3BH单元存前数 INC R0 MOV 3AH, @R0 ;取后数送3AH单元 CLR C CJNE A, 3AH, EXCH LJMP NEXT A≥3AH时清C, A<3AH 时置C 06:02:18
冒泡法数据排序程序 CJNE A, 3AH, EXCH LJMP NEXT A≥3AH清C, A<3AH置C EXCH: JC NEXT ;前数(3BH)小于后数(3AH)不交换 MOV @R0, 3BH ;3BH单元内前数存后数地址 DEC R0 ; R0指向前数 MOV @R0, 3AH ; 3AH单元后数存前数地址 INC R0 ; R0指向后数地址 SETB F0 ;置交换标志位 NEXT: DJNZ R7, LOOP ;未比较完, 进行下一次比较 JNB F0, DONE ;一次也没交换, 说明已按顺序排列 DJNZ R6, GOON ; 循环次数减1, 不为0进下一轮循环 DONE: RET ;返回 06:02:18
6、代码转换程序设计 在汇编语言程序设计中,数据输入/输出、A/D、D/A 转换等常采用BCD码,字符的存储用ASCII码,算术 逻辑运算又采用二进制数。 除了用硬件逻辑实现转换外,可采用算法处理和查 表方法软件实现。 (1)4位二进制数转换为ASCII代码 从ASCII编码表可知,若4位二进制数小于10,则此 二进制数加上30H即变为相应的ASCII码,若大于10 (包括等于10, 是字符ABCDEF),则应加37H。 入口: 转换前4位二进制数存R2。 出口: 转换后的ASCII码存R2。 41H A 42H B 43H C 44H D 45H E 46H F 37H+0AH=41H 06:02:18
(1)4位二进制数转换为ASCII代码 ASCB1: MOV A, R2 ANL A,#0FH ;取出4位二进制数 CJNE A, #0AH, NOTA ;影响CY标志, 但是不改变A中的值 NOTA: JC LOOP ;该数<10去LOOP ADD A, #07H ;否则加37H(下面还加30H) LOOP: ADD A, #30H ;加30H MOV R2, A ;转换之ASCII码送R2中 RET ;返回 06:02:18
(3)BCD码转换为二进制码子程序 例: 设有用BCD码表示的4位十进制数分别存于R1, R2 中, 其中R2存千位和百位数, R1存拾位和个位数, 要把 其转换成二进制码。 解决思路:可用由高位到低位逐位检查BCD码的数值, 然后累加各十进制位(乘权值)对应的二进制数来实现。 其中, 1000=03E8H, 100=0064H, 10=000AH (个位数的 BCD码与二进制码相同)。 R2 千位数 百位数 R1 十位数 个位数 入口: 待转换的BCD码存于R1, R2中, 分配如下: 低位字节 : R1 ; 高位字节 : R2 出口: 结果存在20H, 21H单元中, 其中20H存低字节, 21H存高字节。 06:02:18
(3)BCD码转换为二进制码子程序 BCDB11:MOV 20H, #00H MOV 21H, #00H ;存结果单元清0 MOV R3, #0E8H MOV R4, #03H ;1千的二进制数03E8H送R3, R4 MOV A, R2 ANL A, #0F0H ;取千位数 SWAP A ;将千位数移至低四位 JZ BRAN1 ;千位数为0则转BRAN1, 去处理百位数 LOOP1: DEC A LCALL ADDT ;千位数不为0, 加千位数二进制权码 ;千位数是n, 就加n次千位数二进制码03E8H JNZ LOOP1 ;本循环即实现千位数n乘权值03E8H 06:02:18
BRAN1:MOV R3, #64H ;下面实现百位数转二进制码 MOV R4, #00H ;百位数的二进制码64H送R3, R4 MOV A, R2 ANL A, #0FH ;取百位数 JZ BRAN2 ;百位数是0转BRAN2, 去处理十位数 LOOP2: DEC A LCALL ADDT ;加百位数二进制权码 JNZ LOOP2 ;百位数是n, 就加n次64H BRAN2: MOV R3, #0AH ;十位数权值0AH送R3, R4=00H MOV A, R1 ;下面实现十位数转二进制码 ANL A, #0F0H ;取十位数 SWAP A JZ BRAN3 ;十位数为0转BRAN3, 去处理个位数 LOOP3: DEC A LCALL ADDT ;十位数不为0, 加十位数二进制权码 JNZ LOOP3 ;十位数是n, 就加n次0AH 06:02:18
MOV R3, A ;个位数(权值是自身)送R3, R4=00H LCALL ADDT ;加个位数二进制码 RET BRAN3: MOV A, R1 ANL A, #0FH ;取个位数 MOV R3, A ;个位数(权值是自身)送R3, R4=00H LCALL ADDT ;加个位数二进制码 RET ADDT: PUSH PSW PUSH ACC CLR C MOV A, 20H ;20H(低), 21H单元存累加的转换结果 ADD A, R3 ;累加转换结果 MOV 20H, A MOV A, 21H ADDC A, R4 MOV 21H, A POP ACC POP PSW RET ; R3, R4存1千(百, 十)的二进制数权值03E8H(0064H, 000AH) 06:02:18
两个16位的数据指针:DPTR0和DPTR1。它们的逻 辑地址相同,但是物理上是独立的。 7、STC15F2K60S2单片机双数据指针的使用 两个16位的数据指针:DPTR0和DPTR1。它们的逻 辑地址相同,但是物理上是独立的。 功能:利用这两个数据指针,可以方便地进行数据的 迁移和拷贝。 使用方法:这两个数据指针在指令中只能以DPTR的 形式出现,因此,在使用中,需进行切换。这种切换 是通过设置辅助寄存器AUXR1中的DPS位实现的。 当DPS选择位为0时, 选择DPTR0; 当DPS选择位为1时, 选择DPTR1。 AUXR1各位定义: 位号 D7 D6 D5 D4 D3 D2 D1 D0 位名称 S1_S1 S1_S0 CCP_S0 CCP_S1 SPI_S1 SPI_S0 DPS 06:02:18
[例4-10]编程实现将单片机内部扩展RAM中0000H~ 000FH单元中内容传送到0040H~004FH单元中。 7、STC15F2K60S2单片机双数据指针的使用 AUXR1各位定义 位号 D7 D6 D5 D4 D3 D2 D1 D0 位名称 S1_S1 S1_S0 CCP_S0 CCP_S1 SPI_S1 SPI_S0 DPS DPS:DPTR寄存器选择位。 0:选择DPTR0; 1:选择DPTR1 [例4-10]编程实现将单片机内部扩展RAM中0000H~ 000FH单元中内容传送到0040H~004FH单元中。 思路:可以分别由DPTR0和DPTR1分别指向源数据地 址和目的数据地址。 06:02:18
AUXR1 DATA 0A2H ;定义辅助寄存器AUXR1直接地址 ORG 0000H LJMP MAIN ORG 0100H 7、STC15F2K60S2单片机双数据指针的使用 编程如下: AUXR1 DATA 0A2H ;定义辅助寄存器AUXR1直接地址 ORG 0000H LJMP MAIN ORG 0100H MAIN: MOV SP, #30H ;设置堆栈指针 MOV R2, #10H ;设置计数值(传送的字节数16) ANL AUXR1, #0FEH ;令DPS.0=0,选择DPTR0 MOV DPTR, #0000H ;置源数据地址指针DPTR0=0H ORL AUXR1, #01H ;令DPS.0=1,选择DPTR1 ;该句可用INC AUXR1代替(思考: 为何?) MOV DPTR, #0040H ;置目的数据地址指针DPTR1=40H 或$INCLUDE (STC15.INC); 包含STC15寄存器定义头文件 06:02:18
LOOP: ANL AUXR1, #0FEH ;该句可用INC DEC AUXR1代替 MOVX A, @DPTR ; A← (DPTR0) 7、STC15F2K60S2单片机双数据指针的使用 LOOP: ANL AUXR1, #0FEH ;该句可用INC DEC AUXR1代替 MOVX A, @DPTR ; A← (DPTR0) INC DPTR ;修正源数据地址指针DPTR0+1 ORL AUXR1, #01H ;该句可用INC AUXR1代替, MOVX @DPTR, A ; (DPTR1) ← A INC DPTR ;修正目的数据地址指针DPTR1+1 DJNZ R2, LOOP ;传送字节数16没完, 继续循环传送 SJMP $ ;$表示本条语句地址, 本指令是死循环等待 ;SJMP $ 相当于HERE: SJMP HERE END 选DPTR0 选DPTR1 06:02:18
8、STC15F2K60S2单片机数据Flash(EEPROM)的使用 STC15F2K60S2单片机片内集成1KB的数据Flash存储器, 可作为EEPROM使用, 用来保存程序的设置参数。 GO 【例4-11】一个完整的数据Flash操作实例。 $INCLUDE (STC15.INC) ;包含STC15F2K60S2寄存器定义文件 ;定义ISP/IAP命令 ISP_IAP_BYTE_READ EQU 1H ;字节读 ISP_IAP_BYTE_PROGRAM EQU 2H ;字节编程 ISP_IAP_SECTOR_ERASE EQU 3H ;扇区擦除 ;定义Flash操作等待时间及允许IAP/ISP操作的常数(设置IAP_CONTR) ENABLE_IAP EQU 82H ;系统工作时钟<20MHz时 DEBUG_DATA EQU 5AH ;EEPROM单元的测试值,如正确应等于该值 START_ADDRESS EQU 0000H ;EEPROM测试起始地址 06:02:18
[例4-11] 一个完整的数据Flash操作实例(续) ORG 0000H LJMP MAIN ORG 0100H MAIN: MOV SP, #70H ;堆栈指针指向 70H单元 LCALL Delay ;延时 ;下面程序读出EEPROM测试起始地址单元的内容 MAIN1: MOV DPTR, #START_ADDRESS ;将EEPROM测试起始地址0H送DPTR数据指针 LCALL Byte_Read ;调子程读数据经IAP_DATA送入累加器A MOV 40H, A ;将EEPROM 的值送40H 单元保存 CJNE A, #DEBUG_DATA, NOT_EQU_DEBUG_DATA ;若数据不正确(非5AH),就跳转;数据正确(5AH)时, 顺序执行 LCALL Delay ;延时 SJMP $ ;数据正确, CPU在此无限循环执行此句 06:02:18
[例4-11] 一个完整的数据Flash操作实例(续) NOT_EQU_DEBUG_DATA: ;下面代码是当EEPROM里的数据错误时, 需进行的处理程序 ;即将该EEPROM所在的扇区整个擦除,将正确的数据写入 LCALL Delay ;延时 MOV DPTR, #START_ADDRESS ;将EEPROM测试起始地址0H送DPTR数据指针 LCALL Sector_Erase ;调擦除整个扇区子程序 ;将EEPROM测试起始地址送DPTR数据指针 MOV A, #DEBUG_DATA ;写入 EEPROM 数据 #DEBUG_DATA(5A) LCALL Byte_Program ;字节编程 SJMP $ ;字节编程后,CPU在此无限循环执行此句 06:02:18
[例4-11] 一个完整的数据Flash操作实例(续) ;下面程序是读一字节, 调用前需打开IAP功能, Byte_Read: ;入口: DPTR=字节地址, 返回: A=读出字节 MOV IAP_CONTR, #ENABLE_IAP ; IAP_CONTR←82H, 打开IAP功能,设置Flash操作等待时间 MOV IAP_CMD, #ISP_IAP_BYTE_READ ; IAP_CMD←01H,设置为IAP/ISP字节读模式命令 MOV IAP_ADDRH, DPH ;设置目标单元地址的高8位地址0H MOV IAP_ADDRL, DPL ;设置目标单元地址的低8位地址0H MOV IAP_TRIG, #5AH ;先送5AH,再送A5H到ISP/IAP触发寄存器 MOV IAP_TRIG, #0A5H ;送A5H后,ISP/IAP命令即被触发启动 NOP MOV A, IAP_DATA ;读出数据在IAP_DATA单元,送累加器A LCALL IAP_Disable ;关闭IAP功能, 清相关特殊功能寄存器 RET 字节读操作也可用MOVC指令, 用MOVC访问数据Flash存储器时, 其地址范围为F000H~F3FFH。 06:02:18
[例4-11] 一个完整的数据Flash操作实例(续) ;下面程序是字节编程, 调用前需打开IAP功能, Byte_Program: ;入口:DPTR=字节地址, A=需写入的数据 MOV IAP_CONTR, #ENABLE_IAP ; IAP_CONTR←82H, 打开IAP功能,设置Flash操作等待时间 MOV IAP_CMD, #ISP_IAP_BYTE_PROGRAM ; IAP_CMD←02H, 设置为IAP/ISP字节读模式命令 MOV IAP_ADDRH, DPH ;设置目标单元地址的高8位地址0H MOV IAP_ADDRL, DPL ;设置目标单元地址的低8位地址0H MOV IAP_DATA, A ;要编程的数据先送进ISP_DATA寄存器 MOV IAP_TRIG, #5AH ;先送5AH,再送A5H到ISP/IAP触发寄存器 MOV IAP_TRIG, #0A5H ;送完A5H后,ISP/IAP命令即被触发启动 NOP LCALL IAP_Disable ;关闭 IAP功能, 清相关特殊功能寄存器 RET 06:02:18
[例4-11] 一个完整的数据Flash操作实例(续) Sector_Erase: ;下面程序擦除扇区, 入口: DPTR =扇区地址 MOV IAP_CONTR, #ENABLE_IAP ;打开IAP功能,设置 等待时间 MOV IAP_CMD, #03H ;设置为IAP/ISP扇区擦除模式命令 MOV IAP_ADDRH, DPH ;设置目标单元地址的高8位地址 MOV IAP_ADDRL, DPL ;设置目标单元地址的低8位地址 MOV IAP_TRIG, #5AH ;先送5AH,再送A5H到ISP/IAP触发寄存器 MOV IAP_TRIG, #0A5H ;送A5H 后,ISP/IAP命令即被触发启动 NOP LCALL IAP_Disable ;关闭IAP功能, 清相关特殊功能寄存器 RET IAP_Disable: ;下面程序关闭IAP功能, 清相关特殊功能寄存器 ;一次连续的IAP操作完成之后建议关闭IAP功能,不需每次都关 MOV IAP_CONTR, #0 ;关闭 IAP 功能 MOV IAP_ADDRH, #0FFH ;送地址高字节单元,指向非EEPROM区 MOV IAP_ADDRL, #0FFH ;送地址低字节单元为FFH,防止误操作 RET 06:02:18
[例4-11] 一个完整的数据Flash操作实例 (续) ;------- ---------------- 延时子程序------------------------- Delay: CLR A MOV R0, A MOV R1, A MOV R2, #20H Delay_Loop: DJNZ R0, Delay_Loop DJNZ R1, Delay_Loop DJNZ R2, Delay_Loop RET END 延时时间: 256*256*32(20H) *时钟周期 06:02:18
§4.10汇编语言程序调试 程序编写完成后, 并不一定能够保证实现所期望的功 能,一般都需要使用集成开发环境对程序进行调试和 验证。常见的集成开发环境是Keil μVision。 4.10.1 Keil μVision集成开发环境简介 Keil μVision 集成开发环境(IDE, 简称Keil)是一个基于 Windows的开发平台, 包含高效的编辑器、项目管理 器和MAKE工具。 Keil支持所有的Keil 8051工具, 包括C编译器、宏汇编 器连接/定位器、目标代码、到HEX的转换器。 06:02:18
4.10.1 Keil μVision集成开发环境简介 Keil通过以下特性加速单片机(嵌入)应用系统开发过程: 全功能的源代码编辑器; 器件库用来配置开发工具设置; 项目管理器用来创建和维护项目; 集成MAKE工具可汇编编译和连接用户的嵌入式应用; 所有开发工具的设置都是对话框形式的; 真正的源代码级的对CPU和外围器件的调试器; 高级GDI(Graphic Debug Interface)接口用来在目标硬 件上进行软件调试以及和Monitor-51进行通信; 与开发工具手册和器件数据手册和用户指南有直接的 链接。 06:02:18
4.10.1 Keil μVision集成开发环境简介 Keil通过以下特性加速单片机(嵌入)应用系统开发过程: 全功能的源代码编辑器; 器件库用来配置开发工具设置; 项目管理器用来创建和维护项目; 集成MAKE工具可汇编编译和连接用户的嵌入式应用; 所有开发工具的设置都是对话框形式的; 真正的源代码级的对CPU和外围器件的调试器; 高级GDI(Graphic Debug Interface)接口用来在目标硬 件上进行软件调试以及和Monitor-51进行通信; 与开发工具手册和器件数据手册和用户指南有直接的 链接。 06:02:18
4.10.2 Keil μVision集成开发环境调试汇编语言程序的方法 §4.10汇编语言程序调试 4.10.2 Keil μVision集成开发环境调试汇编语言程序的方法 Keil集成开发环境中包括一个项目管理器,要创建一 个应用,需要按下列步骤进行操作: 启动Keil, 新建一个项目文件并从器件库中选择一个器件。 新建一个源文件并把它加入到项目中。 针对目标硬件设置工具选项。 编译项目并生成可以编程到程序存储器的HEX文件。 下载到单片机中进行仿真调试。 06:02:18
4.10.2 Keil μVision集成开发环境调试汇编语言程序方法 书中介绍以Keil μVision 3为例, μVision 5的安装步骤: 下载keil uvision5安装文件-官网下载-C51V954a.exe, 双 击安装完成后, 双击生成的Keil uVision5桌面图标即可 使用。只不过是没有授权, 与授权版的唯一区别是编译 代码有2KB字节的限制。 注册方法: 点击菜单栏’File’→’License Management’, 从打开的窗口中可通过网络向官方获取注册号LIC,然 后输入注册号LIC, 添加完成后即可。 或从该窗口复制CID(Computer ID)号: 如C23M5- KSTLM。 06:02:18
4.10.2 Keil μVision集成开发环境调试汇编语言程序方法 注册方法: 或从该窗口复制CID(Computer ID)号: 如C23M5- KSTLM。 然后用Keil-μVision4的注册机(网上下载Keil_lic.exe), 可 生成uVision5的注册号LIC, 具体步骤: 启动注册机, 输入 CID (Cmoputer ID), 点击Generate即可生成LIC, 可用。 然后复制LIC号,粘贴到通过菜单栏’File’→’License Management’打开的窗口中(注册号LIC输入栏), 添加完 成后即可。 或者参考网络课堂: 何宾《STC单片机原理及应用》 第17讲:keil μVision下载和安装 http://www.gpnewtech.com/study/stc/101.html 06:02:18
4.10.2 Keil μVision集成开发环境调试汇编语言程序方法 注册方法: 或从该窗口复制CID(Computer ID)号: 如C23M5- KSTLM。 然后用Keil-μVision4的注册机(网上下载Keil_lic.exe), 可 生成uVision5的注册号LIC, 具体步骤: 启动注册机, 输入 CID (Cmoputer ID), 点击Generate即可生成LIC, 可用。 然后复制LIC号,粘贴到通过菜单栏’File’→’License Management’打开的窗口中(注册号LIC输入栏), 添加完 成后即可。 或者参考网络课堂: 何宾《STC单片机原理及应用》 第17讲:keil μVision下载和安装 http://www.gpnewtech.com/study/stc/101.html 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 在Keil开发环境下编写STC系列芯片的程序时,创建 工程时都需要把该芯片的启动代码添加到工程中。可 是Keil里面是没有STC的CPU选项。 在Keil开发环境下STC单片机可选择Intel的8052芯片 型号进行编译,新增的资源自行在程序中定义即可(例 头文件STC15F2K60S2.H, STC12C5A60S2.H等的定义, 头文件等可从官网下载 ) 。 但这样做毕竟不方便和容易出错。 其实有非常简便的 办法,不用下载头文件,可自动添加STC单片机各系 列头文件!即用STC-ISP下载编程烧录软件往Keil添加 STC的芯片型号和头文件。 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 首先在STC官网(http://www.stcmcu.com/) 下载stc-isp 下载编程烧录软件 (最新STC-ISP软件V6.85I.zip), 解 压后运行stc-isp-15xx-v6.85I.exe, 出现界面如图: 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 第二步, 在STC-ISP V6.85I 的 “Keil仿真设置” 标签下, 点击按钮“添加型号和头文件到Keil中 添加STC仿真 器驱动到Keil中”。如下图所示: 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 第三步,点按钮“添加型号和头文件到Keil中 添加STC仿 真器驱动到Keil中” 后。在弹出的“浏览文件夹”对话框 中,找到Keil软件安装目录下的“C51” 文件夹后, 选中它, 然后点击“确定” 。此时弹出“添加成功” 对话框。 Keil软件安装后, 以上3步运行添加一次即可。 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 第四步, 打开“Keil”软件。单击Project菜单,在弹出下拉 菜单中选New (μVision) Project选项, 打开对话框如图: 在弹出 “Create New Project”对话框中, 填工程名和选 择工程保存路径, 例“ ex4-12” , 然后“保存” 。在弹出的 “Select CPU Date Base Flie” 对话框中, 下拉选择“STC MCU Database” 。如下图所示: 如何使用STC-ISP向KEIL添加STC芯片型号和头文件 对μVision5版, 对话框 名称是 “Select Device for Target ‘Target 1’ ” μVision5 μVision3 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 第五步, 选择“STC MCU Database”后, 点击 “OK” , 在 弹出的“Select Device for Target ‘Target 1’ ” 窗口中选 择自己使用的芯片型号后按 “确定”。如下图所示: 对μVision5版, 在同一个对话框中设置 “Select Device for Target ‘Target 1’ ” μVision3 μVision5 μVision3 06:02:18
如何使用STC-ISP向KEIL添加STC芯片型号和头文件 在弹出的“Select Device for Target ‘Target 1’ ” 窗口中 选择自己使用的芯片型号后按 “确定”。 第六步, 按 “确定” 后,在弹出对话框中点击“是” 。 注: 在汇编语言程序设计中,不需添加该文件。在C语言 程序设计中,也不需添加该文件。也可点“否” 按钮。 每建立一个新工程Project文件, 以上四、五、六步都要执行设置、选择单片机芯片型号。 06:02:18
4.10.2 Keil μVision集成开发环境调试汇编语言程序方法 [例4-12]假设晶振频率为6MHz。将STC15F2K60S2单片 机片内RAM 40H~4FH单元的内容清零, 然后循环从P1.0 输出10ms方波。 单片机编程实现仿真调试步骤: (如前第4步到第6步, 选单片机型号) 1.启动Keil并创建一个项目Project(以μVision5版本为例); 2.新建并添加汇编语言文件ex4-12. A51到项目, 并编写程序; μVision3版后缀.asm 3. 针对目标硬件设置工具选项; 用Options for Target ‘Target 1’ 对话窗。设置晶振频率6MHz; 选 Create HEX File框产生烧写用HEX格式文件, Use Simulator框(不) 选软件模拟调试。 4. 编译项目并生成可以编程到程序存储器的HEX文件; 5. 对程序进行软件模拟调试或下载到单片机在线仿真调试。 单步、连续运行; 设置断点、存储器查看、查看变量、查看外围等 操作。 06:02:18
4.10.2 Keil μVision集成开发环境调试汇编语言程序方法 【例4-12】 假设晶振频率为6MHz。将STC15F2K60S2 单片机片内RAM 40H~4FH单元的内容清零,然后循 环从P1.0输出10ms方波。 解:1.启动Keil并创建一个项目(下面以μVision5版本为例) 新建一个项目Project文件: 单击Project菜单,在弹出下拉菜单中选中New (μVision) Project选项, 打开对话框如图: 然后选择项目保存路径, 输入项目文件名, 注意一定要保存在新建立的文件夹中。然后点保存。 然后自动弹出选择单片机型号对话窗, 选STC15F2K60S2: (如前面第4步到第6步, 这里略) 06:02:18
[例4-12] 将RAM 40H~4FH清0, 从P1.0输出10ms方波。 2.新建并添加汇编语言文件到项目 在主界面左侧窗口中,选择 Project标签栏。在该标签窗 口下, 给出了Project工程信 息, 如右图所示。 其中, 顶层文件夹名为Target1。 在该文件夹下, 存在一个 Source Group 1子目录。 选择Project 标签栏 06:02:18
[例4-12] 将RAM 40H~4FH清0, 从P1.0输出10ms方波。 2.新建并添加汇编语言文件到项目 添加汇编语言文件的步骤主要包括: 在Project窗口中, 选择 Source Group 1, 单击 右键, 出现浮动菜单。 在浮动菜单内, 选择 “Add New Item to Group ‘Source Group 1’ ” 选项。 出现Add New Item to Group ‘Source Group 1’ 对话窗界面, 如图所示; 06:02:18
[例4-12] 将RAM 40H~4FH清0, 从P1.0输出10ms方波。 2.新建并添加汇编语言文件到项目 选择汇编文件类型 Asm File(.s) 添加汇编语言文件的步骤主要包括: 在Project窗口中, 选择 Source Group 1, 单击 右键, 出现浮动菜单。 在浮动菜单内, 选择 “Add New Item to Group ‘Source Group 1’ ” 选项。 出现Add New Item to Group ‘Source Group 1’ 对话窗界面, 如图所示; 选择前空白 06:02:18
[例4-12] 将RAM 40H~4FH清0, 从P1.0输出10ms方波。 2.新建并添加汇编语言文件到项目 选择后出现文件类型.a51 ex4-12 按下面设置参数: 在该界面左侧窗口中, 选中Asm File(.s)。 在Name右侧的文本框中输入ex4-12。 点击Add按钮。 注:该汇编语言文件名为ex4-12.a51(书中ex4-12.asm)。 μVision3 06:02:18
[例4-12] 将RAM 40H~4FH清0, 从P1.0输出10ms方波。 2.新建并添加汇编语言文件到项目 点击Add按钮后 此处输入代码, 如下页所示 在上图所示Project窗口中, 在Source Group 1子目录下 添加了名为ex4-12.a51的汇编语言文件。 在右侧窗口中, 自动打开了ex4-12.a51文件编辑窗。 输入代码, 如下页所示。 保存设计代码。 μVision3无Add New Item to Group, 用菜单栏’File’→’New’ μVision3对保存的ex4-12.asm还需添加到项目 06:02:18
[例4-12] 将片内RAM 40H~4FH单元清零, 循环从P1.0输出10ms方波。 例4-12程序代码 [例4-12] 将片内RAM 40H~4FH单元清零, 循环从P1.0输出10ms方波。 ORG 0000H LJMP MAIN ORG 0100H MAIN: MOV SP, #70H ;设置堆栈指针 MOV R0, #40H ; 40H~4FH单元的起始地址40H送R0 MOV R2, #10H ;计数器R2控制循环(LOOP1)次数(16=10H) CLR A LOOP1: MOV @R0, A ;将(R0)=40H (开始的16个)单元内容清零 INC R0 DJNZ R2, LOOP1 ;将40H~4FH的16个单元的内容清零 SETB P1.0 ;P1.0输出高电平,(置初值, 这里也可去掉) LOOP2: CPL P1.0 ; P1.0输出方波高低电平变换 LCALL DELAY LJMP LOOP2 ;循环输出方波 DELAY: MOV R2, #50 ;10ms延时子程序;该指令 2T DLY_LOOP: MOV A, #240 ;该指令2T, 2/6 us LOOP: DEC A ;该指令1T JNZ LOOP ;该指令4T NOP ;1T DJNZ R2, DLY_LOOP ;4T RET ; 4T END 设晶振频率为6MHz LOOP循环延时: 240*5* 1/(6MHz)=200us DLY_LOOP循环延时: 50*(240*5+7)* 1/6≈10ms 将此代码复制到 ex4-12.a51文件编辑 窗 06:02:18
3. 针对目标硬件设置工具选项 Project窗口,选中Target 1, 并单击右键, 出现浮动菜单。 浮动菜单中选中 Options for Target ‘Target 1’...选项。 出现Options for Target ‘Target 1’ 对话框界面。在该界面中,点 击Target标签。在该标签界面中,按下面设置参数: 在Xtal(MHz)右侧文本框中, 输入6,如图所示。 其余按默认设置。 在此设置使用晶振的频率 06:02:18
3. 针对目标硬件设置工具选项 Project窗口,选中Target 1, 并单击右键, 出现浮动菜单。浮动菜 单中选中 Options for Target ‘Target 1’...选项。 在该对话框界面下, 再次选中Output标签。在该标签界面下, 选中Create HEX File前面的复选框, 如下图所示。 自动填入与工程名相同的HEX文件名 选中Create HEX File复选框 06:02:18
3. 针对目标硬件设置工具选项 参看5. 软件模拟调试 在该对话框界面下, 再次选中Debug标签。在该标签界面下, 选中Use Simulator前面的单选框, 如下图所示。 选择软件模拟调试功能 点击OK按钮, 退出目标硬件设置选项对话框界 面。 06:02:18
4. 编译项目并生成可以编程到程序存储器的HEX文件 在主界面主菜单下, 选择Project->Build target。开始 对设计进行建立过程。或点击工具条Build按钮 。 注:该过程对汇编源文件, 进行汇编和链接, 最后生成 可执行二进制文件和HEX文件。 06:02:18
4. 编译项目并生成可以编程到程序存储器的HEX文件 编译失败时的提示。 中文逗号, 出错 双击错误信息行,可进行错误定位 06:02:18
4. 编译项目并生成可以编程到程序存储器的HEX文件 编译成功时的提示。 06:02:18
5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 编译成功后即可进行程序的仿真调试。对程序的调试, 有两种方式: 一是,下载到仿真器或单片机中进行在线仿真调试; 二是,进行软件模拟调试(不连接单片机)。 其中,利用软件模拟调试方式可以对程序的运算及逻 辑功能进行调试,软件模拟调试成功后,基本上不需 做多大修改即可应用到真正的系统中。 并且,软件模拟调试方式与在线仿真调试的方式基本 相同。 06:02:18
5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 编译成功后, 从“Debug” 菜单中选择“Start/Stop debug session ” 菜单项 (快捷键是Ctrl+F5), 或者从工具条中 单击 “Start/Stop debug session ”按钮 。 出现调试器界面 06:02:18
5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 在调试器左边出现Registers标签窗口。在该界面的上 方出现Disassembly窗口, 该窗口是程序代码的反汇编 程序。在该窗口下方是汇编语言源程序界面。 。 Disassembly窗口 Registers窗口 源程序窗口 06:02:18
5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 掌握常见调试操作: 连续运行Run (F5) ; 单步运行Step (into) (F11) , 可单步进入到函数内; 单步跳过运行Step Over (F10) , 跳过单步运行调用 函数(也执行函数, 但非单步执行), 其他都单步运行。 运行到光标所在行Run to Cursor Line(Ctrl+F10) ; 设置断点 、存储器查看、查看变量、查看外围等操作 (见下面)。 选择Debug(低版本Peripherals)->Reset CPU, 准备重新 运行程序。 06:02:18
5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 设置断点: 在需设置断点的代码行行号前空白顶格处单 击(低版本双击), 或者在右键菜单栏中, 点击“Insert/ Remove Breakpoint” 或右键 06:02:18
5. 软件模拟调试或下载到单片机中进行在线仿真调试 存储器查看: 在调试模式主菜单中, 选View-> Memory Windows->Memory1;或在工具栏内单击按钮 , 出 现存储器查看窗Memory1。 在Address: 右侧框输入c:0x0119 或d:0x0039或x:0x1039 (c,d,x分别指程序存储器、片内和扩展数据存储器)。 06:02:18
5. 对程序进行软件模拟调试或下载到单片机中 进行在线仿真调试 查看变量: 调试模式菜单中, 选View->Watch Windows; 或单击按钮 , 看查看程序中用到的变量值。 输入变量名 06:02:18
5. 软件模拟调试或下载到单片机中进行在线仿真调试 查看外围: 在调试模式主菜单下, 选择Peripherals -> Interrupt, 打开中断向量表窗口, 在窗口里显示了所有 的中断向量。对选定的中断向量, 可用窗口下的复选框 进行设置。 方式位:0下降沿触发 单片机型号STC15F2K60S2 型号不同中断源数目及方式mode, 优先级pri值不尽相同。 中断优先级 具体功能见第6章 中断 中断使能位 中断总使能位 中断请求标志位 06:02:18
5. 软件模拟调试或下载到单片机中进行在线仿真调试 查看外围: 选Peripherals -> I/O-Ports ->Port 0(或1~3)。 弹出Parallel Port 0(或1~3)端口界面, 如图所示。 在该界面中, 显示了端口0的状态, 可随时修改端口的 状态, 从而可模拟外部的输入。 其中, 有“√” 标记的位值为1, 没有的值为0。 上一行状态对应锁存器的值,下一行状态对应引脚值。 06:02:18
§4.10汇编语言程序调试 出错的行 双击错误信息行,可进行错误定位 利用教材中的例子进行演示! 06:02:18
4.11利用ISP工具将程序下载到单片机中验证程序 用串口或USB给单片机下载程序 用stc-isp-15xx-v6.85I.exe下载程序时, 必须先发下载命令流(即点击“下载/编程”按钮)!再给单片机上电! 用stc-isp软件 先发下载命令流 再给 否则会出现 ‘正在检测目标单片机’提示,一直等待。表示需冷启动目标单片机系统。 因下载/仿真接口仅可用[P3.0, P3.1], 故推荐将串口1放在P3.6/P3.7 或 P1.6/P1.7 , 若用户不想切换, 坚持用P3.0/P3.1 或作为串口1进行通信, 则务必在下载程序时, 在软件上勾选“下次冷启动时需P3.2/P3.3=0/0才可下载程序”。 图4-41 STC15F2K60S2单片机ISP编程流程 06:02:18
4.11利用ISP工具将程序下载到单片机中验证程序 用串口或USB给单片机下载程序 用stc-isp-15xx-v6.85I.exe下载程序时, 必须先发下载命令流(即点击“下载/编程”按钮)!再给单片机上电! 用stc-isp软件 先发下载命令流 再给 否则会出现 ‘正在检测目标单片机’提示,一直等待。表示需冷启动目标单片机系统。 因下载/仿真接口仅可用[P3.0, P3.1], 故推荐将串口1放在P3.6/P3.7 或 P1.6/P1.7 , 若用户不想切换, 坚持用P3.0/P3.1 或作为串口1进行通信, 则务必在下载程序时, 在软件上勾选“下次冷启动时需P3.2/P3.3=0/0才可下载程序”。 图4-41 STC15F2K60S2单片机ISP编程流程 06:02:18
4.11利用ISP工具将程序下载到单片机中验证程序 如果用户系统的P3.0和P3.1连接到RS-485电路, 下载 程序时, 需将其断开。并且, 建议在下载选项中选择 “下次冷启动时需P3.2/P3.3=0/0才可以下载程序”。 要使用ISP功能,必须让单片机掉电后重新上电,外 部手动复位或者看门狗复位都不能使单片机运行ISP 程序。 单片机运行ISP程序时,检测有无合法下载命令流, 大约需要时间几十ms~几百ms,如果没有合法的下 载命令流,则立即运行用户程序。 06:02:18
4.11.1 ISP下载程序的运行过程 使用ISP功能时,需要注意以下几点: 若已设置只当P3.2和P3.3同时为0时, 才判断是否下载 用户程序, 则冷启动后, 若P3.2和P3.3不同时为0, 则直 接运行用户程序, 只会占时50uS,可忽略不计。 程序调试完成后, 建议用户选择P3.2和P3.3不同时为0, 立即运行用户程序, 跨过系统ISP监控程序。这样可以 加快用户系统的启动速度。 使用stc-isp-15xx-v6.85I.exe下载程序时, 计算机端的控 制软件必须先发下载命令流(即点击“下载/编程”按钮), 再给单片机上电复位。 06:02:18
4.11.2 使用ISP工具下载程序的步骤 利用教材中的例子进行演示! 06:02:18
第4章 作业 4-1; 4-4; 4-5; 4-6; 4-8; 4-11; 4-14; 4-13 题(x86汇编,去掉) 或修改及说明: STR1: DB 0, 1 ;包括下面的标号后面都需加冒号(:) STR2: DB ‘45’ NUMB: DB ‘2DUP(0)’ ; 改成字符串, 放到单引号‘’内 NUMW: DB 10H, -60H 06:02