第四章 指令系统及汇编语言程序设计
STC15系列单片机器件手册----1473页 任课教师:刘忠国 山东大学课程中心网站: http://course.sdu.edu.cn/G2S/stcmcu.cc 宏晶官方网站:http://www.stcmcu.com/ stc15系列单片机器件手册等 keil μvision软件下载及指导手册(Help→μvision Help) http://www.keil.com/ STC15系列单片机器件手册----1473页 (2015/6/29更新)
第四章 指令系统及汇编语言程序设计 本章学习目标 了解助记符、指令格式 掌握单片机寻址方式 掌握单片机指令系统 掌握单片机汇编语言程序设计及开发环境 参考资料: keil μVision软件的帮助文件 18:13:32
第四章 指令系统及汇编语言程序设计语言 4.6 汇编语言程序设计 4.6.1 汇编语言程序设计的一般步骤和基本框架 4.1 编程语言简介 4.2 指令和伪指令 4.3 汇编语言程序调试 4.4 利用STC-ISP工具将程序下载到单片机中验证程序 4.5各类指令详解 4.5.1 数据传送类指令 4.5.2 逻辑操作类指令 4.5.3 算术运算类指令 4.5.4 位操作指令 4.5.5 控制类转移指令 4.6 汇编语言程序设计 4.6.1 汇编语言程序设计的一般步骤和基本框架 4.6.2 汇编语言程序设计举例 18:13:32
根据算法要求分配资源,包括内部RAM、定时器、 中断等资源的分配。 根据流程图编写程序。 上机调试源程序,进而确定源程序。 4.6 汇编语言程序设计 4.6.1 汇编语言程序设计的一般步骤和基本框架 汇编语言程序设计的一般步骤是: 分析课题,确定算法或解题思路。 根据算法或思路画出流程图。 根据算法要求分配资源,包括内部RAM、定时器、 中断等资源的分配。 根据流程图编写程序。 上机调试源程序,进而确定源程序。 对复杂的程序可按功能分为不同的模块, 按模块功能 确定结构, 编写程序时应采用模块化的程序设计方法。 18:13:32
4.6.1 汇编语言程序设计的一般步骤和基本框架 COLUMN EQU 32H BUFFER DATA 40H 4.6.1 汇编语言程序设计的一般步骤和基本框架 $INCLUDE (STC15.INC) ;下面是汇编语言程序的框架 ;本语句包含IAP15W4K58S4单片机寄存器定义头文件 ;---------------------这里可以编写程序中用到的一些符号定义(使用 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中断入口 COLUMN EQU 32H BUFFER DATA 40H 18:13:32
4.6.1 汇编语言程序设计的一般步骤和基本框架 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中断服务程序入口 18:13:32
IAP15W4K58S4的以上中断与STC15F2K60S2的相同 4.6.1 汇编语言程序设计的一般步骤和基本框架 ORG 0053H LJMP INT2_ISR ;INT2中断服务程序入口 ORG 005BH LJMP INT3_ISR ;INT3中断服务程序入口 ORG 0063H LJMP T2_ISR ;定时器2中断服务程序入口 ORG 0083H LJMP INT4_ISR ;INT4中断服务程序入口 IAP15W4K58S4的以上中断与STC15F2K60S2的相同 18:13:32
IAP15W4K58S4的以下中断对STC15F2K60S2不存在 4.6.1 汇编语言程序设计的一般步骤和基本框架 4.6.1 汇编语言程序设计的一般步骤和基本框架 ORG 008BH LJMP UART3_ISR ; UART3中断服务程序入口 ORG 0093H LJMP UART4_ISR ;UART4中断服务程序入口 ORG 009BH LJMP T3_ISR ;T3中断服务程序入口 ORG 00A3H LJMP T4_ISR ;T4中断服务程序入口 ORG 00ABH LJMP COMP_ISR ;比较器中断服务程序入口 ORG 00B3H LJMP PWM_ISR ;PWM中断服务程序入口 ORG 00BBH LJMP PWMFD_ISR ;PWM异常检测(PWM Fault Detection)
;设置堆栈指针(可根据实际情况进行修改) ……;初始化内存区域内容 ……;设置有关特殊功能寄存器(SFR)的控制字 4.6.1 汇编语言程序设计的一般步骤和基本框架 ORG 0100H MAIN: MOV SP, #70H ;设置堆栈指针(可根据实际情况进行修改) ……;初始化内存区域内容 ……;设置有关特殊功能寄存器(SFR)的控制字 ……;根据需要开放相应的中断控制 MAINLOOP: ;主程序循环 LJMP MAINLOOP 18:13:32
4.6.1 汇编语言程序设计的一般步骤和基本框架 ;下面是各个中断服务子程序的入口 INT0_ISR: ;外部中断0服务子程序 …… ;根据需要填入适当的内容 RETI INT1_ISR: ;外部中断1服务子程序 T0_ISR: ;定时器0中断服务子程序 T1_ISR: ;定时器1中断服务子程序 UART1_ISR: ;串口1中断服务子程序 …… ;根据需要填入适当内容(注意中断请求标志位清零) 18:13:32
4.6.1 汇编语言程序设计的一般步骤和基本框架 UART2_ISR: ;串口2中断服务子程序 …… ;根据需要填入适当内容(注意中断请求标志位清零) RETI ADC_ISR: ;ADC中断服务子程序 SPI_ISR: ;SPI通信中断服务子程序 LVD_ISR: ;低电压检测服务子程序 PCA_ISR: ;PCA和PWM中断服务子程序 18:13:32
4.6.1 汇编语言程序设计的一般步骤和基本框架 INT2_ISR: ;INT2中断服务子程序 …… ;根据需要填入适当的内容 RETI INT3_ISR: ;INT3中断服务子程序 T2_ISR: ;定时器2中断服务子程序 INT4_ISR: ;INT4中断服务子程序 ; IAP15W4K58S4的以上中断服务程序同STC15F2K60S2 18:13:32
4.6.1 汇编语言程序设计的一般步骤和基本框架 IAP15W4K58S4的以下中断中断服务程序对STC15F2K60S2不存在 T3_ISR: ;定时器3中断服务子程序 … ;根据需要填入适当的内容 RETI T4_ISR: ;定时器4中断服务子程序 UART3_ISR: ;串口3中断服务子程序 … ;根据需要填入适当的内容(注意中断请求标志位的清0) UART4_ISR: ;串口4中断服务子程序 … ;根据需要填入适当的内容(注意中断请求标志位的清0) COMP_ISR: ;比较器中断服务子程序 PWM_ISR: ;PWM中断服务子程序 … ;根据需要填入适当的内容(注意中断请求标志位的清0) PWMFD_ISR: ;PWM异常检测中断服务子程序 … ;根据需要填入适当的内容(注意中断请求标志位的清0) ;下面可以编写其他子程序或者定义程序中所用的常数 END IAP15W4K58S4的以下中断中断服务程序对STC15F2K60S2不存在 4.6.1 汇编语言程序设计的一般步骤和基本框架
所以第一条指令是一条长跳转指令,跳到避开上述中 断处理子程序入口地址的0100H的地址,主程序MAIN 从这个地址开始存放; 4.6.1 汇编语言程序设计的一般步骤和基本框架 注意: 由于地址0003H、000BH、0013H、001BH、0023H 、002BH、 0033H、003BH、0043H、004BH、0053H、005BH、0063H、 0083H、0033H、008BH、0093H、009BH、00A3H、00ABH、 00B3H和00BBH是专门为中断处理子程序分别预留的入 口地址, 所以第一条指令是一条长跳转指令,跳到避开上述中 断处理子程序入口地址的0100H的地址,主程序MAIN 从这个地址开始存放; MAIN 语句前面的伪指令“ORG 0100H”表示,以标 号MAIN表示的主程序放在0100H开始的区域,当然也 可以是跳到能够避开上述入口地址的其他地址。 18:13:32
如果用户系统根本没有任何中断源,或者没有使用 全部中断源,就可以不用或者少用中断的功能; 4.6.1 汇编语言程序设计的一般步骤和基本框架 如果用户系统根本没有任何中断源,或者没有使用 全部中断源,就可以不用或者少用中断的功能; 0003H到00BBH的区域也就无须全部或部分用于中 断处理。没有任何中断的情况下,主程序甚至可以 从0000H开始连续存放下去。 主程序的末尾是一条长跳转指令,跳转到某个合适 的地方反复执行主程序。 一般的子程序不可形成死循环,但是作为整个主程 序却应该是一个最大的死循环。无论执行哪个子程 序,之后都要回到主程序,反复循环运行。 18:13:32
程序流程图 4.6.1 汇编语言程序设计的一般步骤和基本框架 在程序编制以前,先根据系统方案绘制程序流程图 是一个很好的方法。 4.6.1 汇编语言程序设计的一般步骤和基本框架 程序流程图 在程序编制以前,先根据系统方案绘制程序流程图 是一个很好的方法。 程序流程图可以简洁清晰地将程序的分支走向标示 清楚,尤其是在程序复杂,编写人员较多相互衔接 容易出错的的情况下,利用流程图理顺各部分关系 显得尤为重要。 画流程图有两个常用的结构:顺序执行的矩形框和 条件分支的菱形框。 18:13:32
画流程图两结构:顺序执行矩形框和条件分支菱形框 顺序执行:某个局部功能或者顺序执行的语句使用矩形 方框表示,矩形方框内注明程序的功能,各方框之间用 箭头表示执行顺序,一目了然; 条件分支: 遇到需要根据条件判断是否转移时,使用菱形 方框表示, 菱形框内注明分支条件, 不同出口表明分支的 去向: 可以向后跳转, 也可向前跳转。 计数单元-1=0? 退出循环 Y N 循环体 Y N 循环体 R0-1→R0=0? 存储单元清零 堆栈指针赋初值 调用延时子程序 分支结构 顺序结构 分支结构另种画法 例: DJNZ R0, L1 18:13:32
程序流程图 循环程序设计 当程序处理的对象 具有重复性规律时, 可以使用循环程序 设计。一个循环表 示重复执行一组指 令(程序段)。 图4-47 典型循环程序结构的流程图 18:13:32
画流程图两结构:顺序执行矩形框和条件分支菱形框 条件分支: 菱形框内注明分支条件, 不同出口表明分支 的去向: 可以向后跳转, 也可向前跳转。一般框图如下 所示: 二分支结构 用: CJNZ, JZ, J(N)C, J(N)B等 多分支结构 18:13:32
1、分支程序设计 2、查表程序设计 3、循环程序设计 4、定点数运算子程序设计 5、数据排序程序设计 6、代码转换程序设计 4.6.2 典型汇编语言程序设计举例 1、分支程序设计 2、查表程序设计 3、循环程序设计 4、定点数运算子程序设计 5、数据排序程序设计 6、代码转换程序设计 18:13:32
1、分支程序设计 程序分支是通过条件转移指令实现的,即根据条件进 行判断后决定程序的走向。条件满足则进行程序转移, 不满足就顺序执行程序。 程序分支是通过条件转移指令实现的,即根据条件进 行判断后决定程序的走向。条件满足则进行程序转移, 不满足就顺序执行程序。 通过条件判断实现单分支程序转移的指令有JZ、JNZ、 CJNE (4条)和DJNZ(2条)等。 以位状态为条件,进行程序分支的指令JC、JNC、 JB、JNB和JBC等。 18:13:32
1、分支程序设计 【例4-10】编程实现下面的比较函数。设变量x存放 在R0,求得的y 值存入SIGN单元。 解:可以利用比较转移CJNE指令和进位位C状态控制 转移(JC指令)来实现三分支转移。 18:13:32
【例4-10】程序 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 18:13:32
2、查表程序设计 查表法产生的背景 参数的计算非常复杂; 公式计算法计算程序长,难于计算; 需要耗费大量时间; 非线性参数,无法用一般算术运算就可以计算出来, 如指数、对数、三角函数以及积分、微分等运算; 数学计算无法建立相应的数学模型。 查表法定义 就是把事先计算或测得的数据按一定顺序编制成表格, 查表程序根据被测参数的值或中间结果, 查出最终所需 的结果。它具有程序简单, 执行速度快等优点。 18:13:32
2、查表程序设计 应用: 在键盘处理程序中,查找按键相应的命令处理子程 序的入口地址; 在键盘处理程序中,查找按键相应的命令处理子程 序的入口地址; 在一些快速计算的场合,根据自变量的值,从函数 表上查找出相应的函数值以及实现非线性修正、代 码转换等等。 常用MOVC A,@A+DPTR查找程序存储器空间 的代码或常数,每次传送一个字节。 举例: 在LED显示程序中, 获得LED数码管显示字模; 18:13:32
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 18:13:32
3、循环程序设计 DJNZ R7, LOOP 延时程序是典型的循环程序。 下面就以延时程序为例, 说明 循环程序的设计方法。 流程图如图所示。 DJNZ R7, LOOP 图4-48 延时程序流程图 18:13:32
3、循环程序设计 简单延时子程序如下(注释部分为指令的时钟周期数): DELAY100US : ;@11.0592MHz PUSH 30H ;指令的时钟周期数3T MOV 30H, #218 ;3T DLY_LOOP: DJNZ 30H, DLY_LOOP ; 5T ;30H←(30H)-1, 若(30H)≠0, 则转到DLY_LOOP执行 POP 30H ;2T RET ;4T IAP15W4K58S4为1T的8051单片机, 当系统时钟为 11.0592MHz时, 上述程序可延时约0.1ms。若需加长延 时时间, 可采用多重循环延时程序方法。 18:13:32
3、循环程序设计 创建延时程序最简单的方法是利用宏晶公司的下载工具 STC-ISP的“软件延时计算器”获得延时程序代码, 如图4- 49所示。上述延时0.1ms的程序即可用此法得到。 在工具中选择“软件延时计算器”标签页, 设置系统频率, 定时长度和8051指令集, 最后单击“生成ASM代码”按钮 即可生成延时子程序汇编代码。也可生成C程序代码。 18:13:32
4、定点数运算子程序设计 多字节无符号加法子程序和减法子程序设计较简单, 在此介绍有代表性的多字节BCD码减法程序和多字 节乘法程序的设计。 (1)多字节十进制BCD码减法 因指令系统中只有十进制加法调整指令DA A, 也即 该指令只有在加法指令(ADD、ADDC)后, 才能得到 正确的结果。 为了用十进制加法调整指令对十进制减法进行调整, 必须采用补码相加的办法,用9AH (100)减去减数即 得以10(100)为模的减数的补码。 GO 参考例4-9 18:13:32
求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, 且无借位, 计算正确。 18:13:32
(1)多字节十进制BCD码减法 BCD码减法程序举例:采用补码相加的办法,用 9AH(100)减去减数即得以10(100)为模的减数的补码。 程序说明(减法运算化成100的补码加法运算) 程序中,减数求补后与被减数相加,方可利用DA A 指令进行调整; 若二者相加调整(DA A)后结果无进位(C=0),实际 上表示二者相减有借位; 若二者相加调整(DA A)后有进位(C=1),实际上表 示二者相减没有借位(教材加文字); 参考例4-9 因此, 都需对进位位C进行求反操作。 18:13:32
(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 ;返回 18:13:32
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位, 其余几项的含义类似。 程序如下: 18:13:32
(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 18:13:32
(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: 用户标志位 18:13:32
(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: 用户标志位 18:13:32
5、数据排序程序设计 数据排序是将数据块中的数据按升序或降序排列。下 面以数据升序排序为例, 说明数据排序程序设计方法。 数据升序排列常采用冒泡法。冒泡法是一种相邻数据 互换的排列方法,同查找极大值方法一样,一次冒泡 即找到数据块的极大值放到数据块最后, 再一次冒泡,次大数排在倒数第二位置,多次冒泡实现升序排列。 例, 将片内RAM 30H~37H中的数据从小到大升序排列。 设R6为循环次数计数器, R7为比较次数计数器。 F0为冒泡过程中是否有数据交换的状态标志,F0=0表 示无交换发生,F0=1表示有互换发生,须继续循环。 R0为指向RAM单元的地址指针初值为30H。 18:13:32
冒泡法数据排序程序流程图 数据在30H~37H中; R6为循环次数计数器 R7为比较次数计数器; F0为数据交换状态标志; R0地址指针. 18:13:32
冒泡法数据排序程序 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 18:13:32
冒泡法数据排序程序 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 ;返回 18:13:32
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 18:13:32
(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 ;返回 18:13:32
(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存高字节。 18:13:32
(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 18:13:32
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 18:13:32
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) 18:13:32
两个16位的数据指针:DPTR0和DPTR1。它们的逻 辑地址相同,但是物理上是独立的。 7、IAP15W4K58S4单片机双数据指针的使用 两个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 18:13:32
[例4-11]编程实现将单片机内部扩展RAM中0000H~ 000FH单元中内容传送到0040H~004FH单元中。 7、IAP15W4K58S4单片机双数据指针的使用 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-11]编程实现将单片机内部扩展RAM中0000H~ 000FH单元中内容传送到0040H~004FH单元中。 思路:可以分别由DPTR0和DPTR1分别指向源数据地 址和目的数据地址。 18:13:32
AUXR1 DATA 0A2H ;定义辅助寄存器AUXR1直接地址 ORG 0000H LJMP MAIN ORG 0100H 7、IAP15W4K58S4单片机双数据指针的使用 编程如下: 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寄存器定义头文件 18:13:32
LOOP: ANL AUXR1, #0FEH ;该句可用INC DEC AUXR1代替 MOVX A, @DPTR ; A← (DPTR0) 7、IAP15W4K58S4单片机双数据指针的使用 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 18:13:32
8、IAP15W4K58S4单片机数据Flash(EEPROM)的使用 IAP15W4K58S4单片机片内集成1KB的数据Flash存储 器, 可作为EEPROM使用, 用来保存程序的设置参数。 GO 【例4-12】一个完整的数据Flash操作实例。 $INCLUDE (STC15.INC) ;包含IAP15W4K58S4寄存器定义文件 ;定义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测试起始地址 18:13:33
[例4-12] 一个完整的数据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在此无限循环执行此句 18:13:33
[例4-12] 一个完整的数据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在此无限循环执行此句 18:13:33
[例4-12] 一个完整的数据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。 18:13:33
[例4-12] 一个完整的数据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 18:13:33
[例4-12] 一个完整的数据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 18:13:33
[例4-12] 一个完整的数据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) *时钟周期 18:13:33
第4章 作业 基本: 4-1; 4-2; 4-3; 4-4; 4-5; 4-6; 4-12; 综合: 4-11; 4-14; 18:13