410 likes | 567 Views
2-4 汇编语言程序设计方法(二). 单片机常见汇编语言程序设计举例: 数制转换程序 算术和逻辑运算类程序设计 查表程序设计 子程序设计 定时程序. ·. ·. ·. ·. ·. ·. ·. ·. ·. B. A. 22H. 21H. 20H. 1 、数制转换. 例 1 : 将 20H 单元的两个压缩 BCD 码拆开变成 ASCII 码,存入 21H 、 22H 单元。 ( 假设 20H 中的 BCD 码为 00110100). 什么是 BCD 码? 什么是 ASII 码?. 高四位 ASII 码. 0011. 低四位 ASII 码.
E N D
2-4 汇编语言程序设计方法(二) • 单片机常见汇编语言程序设计举例: • 数制转换程序 • 算术和逻辑运算类程序设计 • 查表程序设计 • 子程序设计 • 定时程序
· · · · · · · · · B A 22H 21H 20H 1、数制转换 • 例1:将20H单元的两个压缩BCD码拆开变成ASCII码,存入21H、22H单元。(假设20H中的BCD码为00110100) 什么是BCD码? 什么是ASII码? 高四位ASII码 0011 低四位ASII码 0011 压缩BCD码 0011 0100
方法1:将BCD码除以10H,恰好是将BCD码分别移到了A、B的低4位。然后再各自与30H相或,即成为ASCII码。方法1:将BCD码除以10H,恰好是将BCD码分别移到了A、B的低4位。然后再各自与30H相或,即成为ASCII码。 方法2:利用半字节交换指令来实现。
开始 · · · (20H)A · · · · · · 10HB A/B (A中为高4位BCD码,B中为低4位BCD码) PC PC PC PC PC PC PC PC B B+30HB A B(22H) 22H A+30HA 21H A(21H) 20H 结束 数制转换程序1---方法1 ORG 0000H MOV A,20H MOV B,#10H DIV AB ORL B,#30H MOV 21H,B ORL A,#30H MOV 22H,A SJMP $ END 源程序如下: 0001 0000 0000 0100 0011 0100 0011 0000 0011 0011 0011 0100 0100
开始 #20HR0 · · · · · · #30HA · · · A的低4位(20H)的低4位 A PC PC PC PC PC PC PC PC PC PC A(21H) (20H)A 22H 21H A的低4位A的高4位 20H A+30HA R0 A(22H) 结束 简单程序例1---方法2 ORG 0000H MOV R0,#20H MOV A,#30H XCHD A,@R0 MOV 21H,A MOV A,@R0 SWAP A ANL A, #0FH ORL A , #30H MOV 22H, A SJMP $ END 源程序如下: 0011 0011 0011 0011 0000 0100 0100 0000 0011 0011 0011 0100 0000 0010 0000
解题思路: (40H) 16 商为二进制的高四位 余为二进制的低四位 余数 将高四位乘以0AH,再加上低四位即可。 • BCD码十进制转换成二进制 • 例:假如在内部RAM40H单元中存储有一个压缩BCD编码的两位十进制数,设计一段程序把这个数转换成二进制数并存入41H单元中。
程序清单 • ORG 0000H • MOV A,40H • MOV B,#16 • DIV AB • MOV 20H,B • MOV B,#0AH • MUL AB • ADD A,20H • MOV 41H,A • SJMP $ • END 取出30H数据除以16,商A,余B 将十位数乘以0AH,其结果不会超过一个字节,在A中 将十位数的二进制数加上个位数,结果41H
算术和逻辑运算类程序设计 • 多字节BCD码十进制数相加例:假如在MCS-51单片机内部RAM中30H~37H单元、 38H~3FH单元分别存放有两个8字节BCD码十进制数,设计一段程序将这两个数相加,并把结果存于2FH~37H单元中,小地址存数据的高字节。 • 解题思路:先清Cy位,把(37H)和(3FH)进行带Cy相加,在进行十进制调整,结果存于37H单元中;把(36H)和(3EH)进行带Cy相加,在进行十进制调整……循环至结束,结果把最高字节的Cy存入2FH单元。
开始 R037H(被加数首地址) R13FH(加数首地址) R208H(循环次数) 清Cy位 加数和被加数 相加,十进制 调整,并回存 调整数据指针 R0(R0)-1 R1(R1)-1 N (R2)-1=0? Y Cy存入2FH单元 结束 流程与程序清单 设定循环次数,R0和R1分别指向加数和被加数的首地址 • ORG 0000H • MOV R2,#08H • MOV R0,#37H • MOV R1,#3FH • CLR C • LOP:MOV A,@R0 • ADDC A,@R1 • DA A • MOV @R0,A • DEC R0 • DEC R1 • DJNZ R2,LOP • CLR A • RLC A • MOV 2FH,A • SJMP $ • END 相加,并回存结果 R0和R1分别指向加数和被加数的下一地址,判断循环次数 存进位标志
极值查找程序 • 例:在MCS-51单片机内部RAM的40H-47H单元中存有8个无符号数,设计一段程序找出其中的最大值,并存放到48H单元中。 • 这是一个求最大值的问题。 • 解题思路:先把(A)(40H),然后将(41H)和(A)进行比较,如果(41H)>(A),则(A)(41H),然后和下一个字节进行比较,依次类推,最后(A)必定是最大值。
开始 R040H(数据区首地址) R207H(循环次数) A((R0))(取第一个数) R0(R0)+1(指向下一个数) B ((R0)) Y A≥B? N A (B) N (R2)-1=0? Y 48H(A) 结束 • ORG 0000H • MOV R2,#07H • MOV R0,#40H • MOV A,@R0 • LOOP:INC R0 • MOV B,@R0 • CJNE A,B,NEX • NEX: JNC NET • MOV A,B • NET: DJNZ R2,LOOP • MOV 48H,A • SJMP $ • END 设定循环次数R1,R0作为数据指针,指向数据区第一个字节 取出下一个字节,存入B 如果(A)<(B), 则(A)(B) 次数到否,到则取出(A)48H
排序程序 程序名 :BUBBLE 功能 :将片内RAM数据块由大到小排序 入口参数:R0指向数据块首地址,R2存放数据块长度 出口参数:仍存放原来位置 占用资源:R0,R1,R2,R3,R5,A,PSW;00H位
BUB2:DEC R1 MOV @R1,A INC R1 MOV A,@R1 DJNZ R5,BUB1 MOV A,R0 MOV R1,A MOV A,R2 MOV R5,A JB 00H,BUBB1 RET • BUBBLLE:MOV A,R0 • MOV R1,A • MOV A,R2 • MOV R5,A • BUBB1: CLR 00H • DEC R5 • MOV A,@R1 • BUB1: INC R1 • MOV B,@R1 • CJNE A,B,BUB0 • BUB0: JNC BUB2 • SETB 00H • XCH A,@R1
键控移位例:有一电路如图所示,设计一个程序实现以下功能:SW按下第1次,VD1发光;SW按下第2次,VD1、VD2发光; SW按下第3次,VD1、VD2、VD3发光…… SW按下第7次,VD1—VD7发光; SW按下第8次,VD1发光; SW按下第9次,VD1、VD2发光……依次轮回。 • 解题思路:本例涉及到查询按键按下的过程,SW没有按下时P2.0为高电平,按下为低电平。实际应该查询的是P2.0的下降沿, P2.0有一个下降沿,表明有一个按键按下,LED的状态应该改变一次。
初态 次态 程序清单 • SW BIT P2.0 • ORG 0000H • LOOP0: SETB SW • MOV A,#01H • MOV R7,#07H • LOOP1: MOV C,SW • MOV R6,#255 • DELAY: NOP • NOP • DJNZ R6,DELAY • ANL C,/SW • JNC LOOP1 • RLC A • ORL A,#01H • MOV P2,A • DJNZ R7,LOOP1 • SJMP LOOP0 • END 置P2.0为输入方式,确定循环次数为7 延时消抖 判断是否有下降沿,有则移位,否则继续查询 显示输出,如果循环次数到则重新开始
数据排序程序 • 例:将片内RAM中20H-27H中的数据按照从小到大的顺序重新排列。 • 解题思路:见下图
R1←20H , R0 ←(R1)+1 R3 ←08H B←((R1)) ,R2 ←(R3)-1 查找最小值并和(R1)单元互换 R1 ←(R1)+1 N R3-1=0? Y END 程序流程:
程序清单:ORG 0000H • MOV R1, #20H • MOV R3, #08H LOOP1: MOV A, R1 ;R0←(R1)+1 INC A MOV R0, A MOV A, R3 ; R2←(R3)-1 DEC A MOV R2, A MOV B, @R1 LOOP: MOV A, @R0 CJNE A, B, MM MM: JNC SS ;如果A≥B跳转 MOV B, A MOV 28H, R0 ;28h纪录最小值所在单元的编号 SS: INC R0 DJNZ R2, LOOP MOV R0, 28H ;最小值单元和20H单元内容互换 MOV A, @R1 ;数据 MOV @R0, A MOV @R1, B INC R1 DJNZ R3, LOOP1 END
查表程序设计 • 程序使用的专用指令:MOVC A, @A+PC MOVC A, @A+DPTR • 一般情况下, 常使用后者 • 程序设计一般规则: • 先确定表格存放的位置 • 在表格中填入相应的数据 • 查表时先将表格的首地址给DPTR,再将要查表的数据送给A,最后用MOVC A, @A+DPTR
例1:假如在MCS-51单片机内部RAM中40H单元内存放的是一个角度(范围0- 90),设计一段程序,计算出200sin(),把结果存入41H单元中(结果只取整数)。 • 解题思路:先建立一个相应的数值表格,定义在程序存储器中,利用输入的值进行查表。 • 例程:ORG 0000HMOV DPTR,#TABLE MOV A,40H MOVC A,@A+DPTR MOV 41H,A SJMP $TABLE:DB 0,3,7,10,14,17,21,24,28,31,35,38 DB 41,45,48…199,200,200END 表格的数据是怎么计算出来的? 查表 表格内容
例2:假如在MCS-51单片机内部RAM中40H单元内存放的是一个参数x(范围0-10),设计一段程序,计算出5x3+4x2+3x+1,把结果存入41H和42H单元中(高位在前)。例2:假如在MCS-51单片机内部RAM中40H单元内存放的是一个参数x(范围0-10),设计一段程序,计算出5x3+4x2+3x+1,把结果存入41H和42H单元中(高位在前)。 • 解题思路:先建立一个相应的数值表格(每个为2个字节),定义在程序存储器中,利用输入的值进行查表。 • 例程: ORG 0000H MOV DPTR,#TABLE MOV A,40H CLR C • RLC A;A*2-->A • PUSH ACC MOVC A,@A+DPTR MOV 41H,A POP ACC INC A MOVC A,@A+DPTR MOV 42H,A SJMP $TABLE: DW 0001H,000DH,003FH,00B5H,018DH DW 02D1H,04DBH,078DH,0B19H,0F9DH,1537H END 查表,得到高两位数 查表,得到低两位数 表格内容
4.子程序设计 • 在实际问题中,常常会遇到在一个程序中多次用到相同的运算或操作,若每遇到这些运算或操作,都从头编起,将使程序繁琐、浪费内存。因此在实际中,经常把这种多次使用的程序段,按一定结构编好,存放在存储器中,当需要时,可以调用这些独立的程序段。通常将这种可以被调用的程序段称为子程序。 主要内容: 1. 主程序与子程序的关系 2. 子程序嵌套 3. 子程序的调用与返回
子程序SUB 主程序与子程序的关系 子程序入口地址 主程序MAIN 调用子程序 LCALL SUB RET 返回
开始 送显示初值 设闪烁次数 点亮相应的LED 延时 熄灭相应的LED 延时 N 次数-1=0 Y 初值左移1位 指向下一个LED 实例:LED灯的闪烁点亮(一) ORG 0000H MAIN: MOV A,#0FEH ;送显示初值 LP: MOV R0,#10 ;送闪烁次数 LP0: MOV P1,A ;点亮LED LCALL DELAY ;延时 MOV P1,#0FFH;熄灭灯 LCALL DELAY ;延时 DJNZ R0,LP0 RL A SJMP LP END
子程序嵌套 子程序嵌套(或称多重转子)是指在子程序执行过程中,还可以调用另一个子程序。 主程序MAIN 子程序SUB1 子程序SUB2 LCALL SUB1 LCALL SUB2 RET RET
ORG 0000H MAIN: MOV A,#0FEH ;送显示初值 COUN: ACALL FLASH ;调闪烁子程序 RL A ;A左移,下一个灯闪烁 SJMP COUN ;循环不止 FLASH: MOV R0,#10 ;送闪烁次数 FLASH1: MOV P1,A ;点亮LED LCALL DELAY ;延时 MOV P1,#0FFH ;熄灭灯 LCALL DELAY ;延时 DJNZ R0,FLASH1 ;闪烁次数不够10次,继续 RET DELAY: MOV R3,#0FFH ;延时子程序 DEL2: MOV R4,#0FFH DEL1: NOP DJNZ R4,DEL1 DJNZ R3,DEL2 RET END 子程序嵌套范例:LED灯闪烁(二)
子程序的调用与返回 问题:子程序调用、返回到主程序中的正确位置,并接著执行主程序中的后续指令呢? 为了解决这个问题,我们采用了堆栈技术。 PC 20 13 21 13 20 13 21 13 主程序MAIN 子程序SUB1 子程序SUB2 2100 2200 21 2010 2110 LCALL SUB1 13 LCALL SUB2 2013 2113 20 13 RET RET 堆栈指针SP 堆栈
子程序设计注意事项 (1)要给每个子程序起一个名字,也就是入口地址的代号。 (2)要能正确地传递参数。即首先要有入口条件,说明进入子程序时,它所要处理的数据放在何处(如:是放在A中还是放在某个工作寄存器中等)。另外,要有出口条件,即处理的结果存放在何处。 (3)注意保护现场和恢复现场。在子程序使用累加器、工作寄存器等资源时,要先将其原来的内容保存起来,即保护现场。当子程序执行完毕,在返回主程序之前,要将这些内容再取出,送还到累加器、工作寄存器等原单元中,这一过程称为恢复现场。 例4.11 查表子程序。 注意:1.入口参数和出口参数的位置 2.现场的保护与恢复。
子程序的参数传递 范例:计算平方和c=a2+b2 ORG 0000H ;主程序 MOV SP,#3FH ;设置栈底 MOV A,31H ;取数a存放到累加器A中作为入口参数 LCALL SQR ;计算a2 MOV R1,A ;出口参数——平方值存放在A中 MOV A,32H ;取数b存放到累加器A中作为出口参数 LCALL SQR ;计算b2 ADD A,R1 ;求和 MOV 33H,A ;存放结果 SJMP $
;子程序:SQR ;功能:通过查表求出平方值y=x2 ;入口参数:x存放在累加器A中 ;出口参数:求得的平方值y存放在A中 ;占用资源:累加器A,数据指针DPTR SQR:USH DPH ;保护现场,将主程序中DPTR的高八位放入堆栈 PUSH DPL ;保护现场,将主程序中DPTR的低八位放入堆栈 MOV DPTR,#TABLE ;在子程序中重新使用DPTR,表首地址→DPTR MOVC A, @A+DPTR ;查表 POP DPL ;恢复现场,将主程序中DPTR的低八位从堆栈中弹出 POP DPH ;恢复现场,将主程序中DPTR的高八位从堆栈中弹出 RET TABLE: DB 0,1,4,9,16,25,36,49,64,81
例:假如在MCS-51单片机外部RAM中00H—07H单元中依次存放有8个无符号数Xi(i为0-7),设计一段程序计算出Yi=Xi2,并把结果存于外部RAM 10H开始的16个单元中( Yi占用两个字节,高位在前);再计算Zi=Xi÷2,并把Zi依次存放在Xi所在的单元。 • 解题思路:在本例中,Yi=Xi2和Zi=Xi÷2都比较复杂,我们可以分别使用子程序。 • 程序清单: ORG 0000H • MOV R0,#00H • MOV R1,#10H • MOV R2,#08H • LOOP: MOVX A,@R0 • LCALL DIVIDE • LCALL SQUARE • MOVX @R1,B • INC R1 • MOVX @R1,A • DJNZ R2,LOOP • SJMP $ • END R0和R1作为数据指针,R2为循环次数。 传递参数,调用运算子程序 存放结果
Zi=Xi÷2运算子程序 • DIVIDE:PUSH ACC • CLR C • RRC A • MOVX @R0,A • POP ACC • RET • SQUARE:MOV B,A • MUL AB • RET • END Yi=Xi2运算子程序
定时程序 • 单重循环延时 • 例1:假设单片机的fosc=12MHz,计算单片机执行下面程序消耗的时间。 • DELAY: MOV R5,#TIME ;1机器周期 • MM: NOP ;1机器周期 • DJNZ R5,MM ;2机器周期 • RET ;2机器周期 • 执行完以上4条语句,所花时间: • T=(1+(1+2)×TIME)+2 ×1s • 推广计算式: • T(机器周期数)=(循环体机器周期数) ×循环次数+初始化机器周期数
多重循环延时 • 例1:假设单片机的fosc=12MHz,计算单片机执行下面程序消耗的时间。 • DELAY2: MOV R3,#TIME1 ;1机器周期 • LOOP1: MOV R2,#TIME2 ;1机器周期 • LOOP2: NOP ;1机器周期 • DJNZ R2,LOOP2 ;2机器周期 • DJNZ R3,LOOP1 ;2机器周期 • RET ;2机器周期 • 执行完以上6条语句,所花时间: • T=(1+(1+(1+2)×TIME2+2)×TIME1 +2) ×1s
ORG 000H LJMP MAIN ORG 100H MAIN: ;;给片内RAM30H单元开始的16个单元赋值。 MOV R0,#30H MOV R1,#(table1-table) MOV DPTR,#TABLE LOP: CLR A MOVC A,@A+DPTR MOV @R0,A INC R0 INC DPTR DJNZ R1,LOP ;;查找片内RAM30H单元开始的16个单元的最小值,并存于B寄存器。 MOV R0,#30H MOV R1,#(table1-table) MOV B,#0FFH LOP0: MOV A,@R0 CJNE A,B,$+3 JNC LOP1 MOV B,@R0 LOP1: INC R0 DJNZ R1,LOP0 ;;读出片内RAM30H单元开始的16个单元的内容,并和最小值比较,相等则把该单元的值替换成0ffh. MOV R0,#30H MOV R1,#(table1-table) LOP2: MOV A,@R0 CJNE A,B,LOP3 MOV @R0,#0FFH LOP3: INC R0 DJNZ R1,LOP2 SJMP $ TABLE: DB 10H,25H,2H,00H,12H,33H,45H,00H,78H DB 80H,0AAH,0BBH,12H,23H,34H,00H TABLE1: END 习题选讲 例1. 设计一段程序实现如下功能:找出从片内RAM30h开始的16个单元中最小值所在的单元,并将该单元的值替换成0ffh。(最小单元可能不止一个)
习题选讲 例2. 设计一段程序实现逻辑运算:P1.7=P1.6(P1.5⊙P1.4) 分析:在位逻辑运算指令中只有 ANL 、ORL、CPL 指令,没有XRL(异或)和同或指令,所以对上述表达式进行等效转换后为: P1.7=P1.6*(P1.5*P1.4+/P1.5*/P1.4) ORG 0000h LJMP START ORG 50h START: MOV C,P1.4 CPL C ANL C,/P1.5 ;C=/P1.4*/P1.5 MOV F0,C ;F0=C=/P1.4*/P1.5 MOV C,P1.4 ANL C,P1.5 ;C=P1.4*P1.5 ORL C,F0 ;C=C+F0=P1.4*P1.5+/P1.4*/P1.5= P1.5⊙P1.4 ANL C,P1.6 ;C=P1.6*(P1.5⊙P1.4) MOV P1.7,C ;P1.7=C=P1.6*(P1.5⊙P1.4) SJMP $ END
习题选讲 例3. 参照下图设计一个彩灯控制程序,让8个led按照自己的设想发光,显示的样式尽可能的丰富。
工作原理 当P1。0~P1。7中的某端口为高电平时,对应的发光二极管亮,为低电平灭。我们只要控制P1口各位的电平状态,就可以控制8只LED的亮与灭。 例如使(P1)=01010101B=55H,D7、D5、D3、D14只LED灭,D6、D4、D2、D04只LED亮,从效果上看亮与灭是相间隔的;反之,使(P1)=10101010B=AAH,则另外4只LED亮,若反复以一定时间间隔不断从P1口轮流输出55H和AAH,则P1口上8只LED会呈现流水彩灯的效果。
开始 55H送P1口 延时0.3秒 AAH送P1口 流水式彩灯程序流程图 延时0.3秒 ORG 0000H LOOP: MOV P1,#55H ;将立即数55H送P1端口 LCALL TIME ;调延时子程序TIME MOV P1,#0AAH ;将立即数AAH送P1端口 LCALL TIME ;调延时子程序TIME SJMP LOOP ;转移到LOOP TIME: MOV R6,#200 ;延时子程序TIME TIME1:MOV R7,#200 TIME2:NOP NOP NOP DJNZ R7,TIME2 DJNZ R6,TIME1 RET END
4)开幕式与闭幕式彩灯程序 ORG 0000H LJMP MAIN ;跳转到主程序 ORG 0030H MAIN:MOV R0,#8 ;R0送循环次数8 MOV DPTR,#TAB ;给数据指针赋值 LOOP:MOV A,#00H ; MOVC A,@ A + DPTR MOV P1,A LCALL TIME INC DPL DJNZ R0,LOOP SJMP MAIN TIME:MOV R6,#200 TIME1:MOV R7,#200 TIME2:NOP NOP NOP DJNZ R7,TIME2 DJNZ R6,TIME1 RET TAB:DB 81H,0C3H,0E7H,0FFH,18H,3CH,7EH,0FFH END 你发现数据的特点了吗?
本章小结 修改上例中Tab中的数据,使显示方式尽可能的丰富。 • 本章要求: • 了解MCS-51单片机的7种寻址方式; • 掌握数据传送与交换、算术运算、逻辑运算、控制转移、位操作5类共111条指令的功能,特别是每一类指令中比较常用的指令; • 掌握MCS-51单片机汇编语言程序的基本格式及伪指令; • 了解程序的基本结构(顺序结构、分支结构、循环结构)和程序设计的基本方法; • 掌握数制转换、算术运算、极值查找等典型程序的设计; • 掌握子程序的设计方法及其参数传递方法。