一、单片机课设题目要求与软件环境介绍
做了一单片机设计,要用C语言与汇编语言同时实现,现将这次设计的感受和收获,还有遇到的问题写下,欢迎感兴趣的朋友交流想法,提出建议。
单片机设计:基于51单片机的99码表设计
软件环境:Proteus8.0 + Keil4
要求:1,开关按一下,数码管开始计时。2,按两下,数码管显示静止。3,按三下,数码管数值清零。
二、C语言程序
1 #include<reg51.h> 2 #define uint unsigned int 3 #define uchar unsigned char 4 uchar shi,ge,aa,keycount=0,temp; 5 sbit anjian=P1^7; 6 uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; 7 void display(shi,ge); 8 void key (); 9 void init(); 10 void delay(uint z); 11 /*-----主程序-----*/ 12 void main() 13 { 14 init(); //初始化 15 while(1) 16 { 17 key (); 18 if(keycount==1) 19 TR0=1; //开中断 20 if(keycount==2) 21 TR0=0; 22 if(keycount==3) 23 { 24 temp=0; 25 keycount=0; 26 } 27 if(aa==10){aa=0; 28 if(temp<=99) 29 { 30 temp++;display(shi,ge); 31 } 32 else 33 temp=0;} 34 } 35 } 36 37 38 /*------初始化程序-------*/ 39 void init() 40 { 41 keycount=0; 42 temp=0; 43 TMOD=0x01; 44 TH0=(65536-50000)/256; 45 TL0=(65536-50000)%256; 46 EA=1; 47 ET0=1; 48 //TR0=0; 49 } 50 /*-----定时器中断-----*/ 51 void timer0() interrupt 1 52 { 53 TH0=(65536-50000)/256; 54 TL0=(65536-50000)%256; 55 aa++; 56 } 57 /*-----显示子程序-----*/ 58 void display(shi,ge) 59 { 60 shi=temp/10; 61 ge=temp%10; 62 P0=table[shi];;delay(70); 63 P2=table[ge]; ;delay(70); 64 } 65 /*-----按键检测子程序-----*/ 66 void key () 67 { 68 if(anjian==0) 69 { 70 delay(5); //消抖 71 if(anjian==0) 72 keycount++; 73 } 74 //while(anjian==0); 75 //display(shi,ge); //等待按键弹起 76 } 77 /*-----延时子程序-----*/ 78 void delay(uint z) //延时约1ms 79 { 80 uint x,y; 81 for(x=z;x>0;x--) 82 for(y=100;y>0;y--); 83 }
电路仿真结果如下:
三、C语言转汇编语言步骤
好了,那么接下来我们就开始C语言——>汇编语言之旅
(1)C语言1-10行改为
1 ORG 0000H //汇编起始伪指令,功能是规定程序存储器中源程序或数据块存放的起始地址 2 ajmp STAR //ajmp无条件跳转指令 3 ORG 000bh 4 ajmp timer0 5 anjian equ P1.7 //位定义 6 keycount equ 40h 7 shi equ 41h 8 gewei equ 42h 9 aa equ 43h 10 temp equ 44h 11 tab: db 3fh,6h,5bh,4fh,66h //建表 12 db 6dh,7dh,7h,7fh,6fh
(2)C语言中的初始化函数 12-14行和39-49行改为
1 STAR: 2 acall init //子程序近程调用指令,功能是主程序调用子程序,调用子程序的范围为2kb
1 init: 2 mov keycount,#0 //keycount=0 3 mov temp,#0 //temp=1 4 mov tmod,#01h //TMOD=0x01 5 mov TH0,#60 6 mov TL0,#176 7 setb EA //位置位指令,对操作数所指出的位进行置1操作 8 setb ET0 9 setb TR0 10 ret
acall为子程序近程调用指令,返回用ret。
(3)C语言中15-35行是个while循环,逻辑比较繁琐,注意了!
1 START: 2 acall display 3 inc temp //加1指令,将操作数所指定的单元或寄存器中的内容加1 4 acall delay70 //近程调用delay70 5 x8: mov r0,keycount 6 cjne r0,#2,F1 //cjne比较跳转指令,若r0=2则跳转到x8,否则跳转到F1。 7 ajmp x8 8 F1: mov r0,temp 9 cjne r0,#99,START //若r0<99时,重复循环,否则temp=0 10 mov temp,#0 11 ajmp START 12 F9: 13 acall key 14 mov r0,keycount 15 cjne r0,#0,F2 //keycount=0顺序执行,否则跳转到F1 16 CLR P1.3 //清0 17 SETB TR0 18 19 F2: mov r0,keycount //第二次按键 20 cjne r0,#2,F2 21 clr TR0 22 reti 23 mov r0,keycount //第三次按键 24 cjne r0,#3,F3 25 mov temp,#0 26 mov keycount,#0
inc 增量指令,功能是将操作数所指定的单元或寄存器中的内容加1,其结果返还回原操作数单元中。
clr 位复位,功能是对操作数所指出的位进行清“0”操作。
或者在中断函数中
1 timer0: 2 w1: 3 acall key 4 mov TH0,#60 5 mov TL0,#176 6 cpl p1.0 7 JB keycount,x2 8 ajmp x3 9 x2: 10 ajmp START 11 clr p1.0 12 ajmp w1 13 ajmp w1 14 15 x3: mov r0,keycount 16 cjne r0,#3,w1 //若r0=3则顺序执行,否则跳转到w1 17 mov temp,#0 18 mov keycount,#0 19 ret
(4)C语言58-64行display函数改为
1 display: 2 mov a,temp 3 mov b,#10 4 div ab //除法指令,实现两个八位无符号数的除法操作。 5 mov r2,A 6 mov r3,B 7 mov dptr,#tab //16位数据传送使用方式 8 mov a,r2 9 movc a,@a+dptr //查表,先将累加器A的内容与数据指针寄存器DPTR的内容相加,再以其结果为地址,将该地址的结果送入A中 10 mov P0,a 11 acall delay70 12 nop //空指令 13 mov a,r3 14 movc a,@a+dptr 15 mov P2,a 16 nop 17 acall delay70 18 ret
div为除法指令,功能是实现两个8位无符号数的除法操作,一般被除数放在累加器A中,除数放在寄存器B中。指令执行后,商放在A中,余数放在B中。
movc为查表指令,先将累加器A的内容与数据指针寄存器DPTR的内容相加,再以其结果为地址,将该地址的内容送入A中。
nop为空操作指令,它不作任何操作,但要占用一个机器周期(即12个振荡周期)的时间,常用于延时或等待。(有些程序执行的效果由于延时时间太短,在人眼视觉暂时作用下无法辨认清楚)
此段程序的作用在于将一个两位数分别分在一个十位上的数码管和一个个位上的数码管显示。
(5)C语言66-76行key函数改为
1 key: 2 jb anjian,F6 //若anjian=0则顺序执行,否则跳转到F6 3 ACALL delay5 4 inc keycount //keycount++ 5 F6: 6 ret
jb为位条件转移指令,功能是若直接寻址的位bit=1,则程序转移到指定的目标地址去执行,若bit=0,则程序顺序执行。
(6)C语言78-83行delay函数改为
1 delay70: 2 mov r6,#70 3 D2: mov R7,#248 4 d1: djnz R7,d1 //248*70次 5 djnz R6,D2 6 ret 7 8 delay5: 9 mov r6,#5 //消抖。 10 F7: mov R7,#248 11 F8: djnz r7,F8 //248*5次 12 djnz r6,F7 13 ret
注意:248=28 ,约等于1ms。delay为延时程序。
温馨提示:在汇编中程序代码的大小写不受影响,但在C语言中就有影响了。
四、思考
思考1:ret 和 reti都是程序返回指令,有什么区别?
我的回答:ret是子程序返回指令,reti是中断子程序返回指令。区别在于如果是acall 、lcall指令调用的子程序,返回指令就用ret;如果地址是0003,0013,000B,001B,0023调用的子程序,返回指令就用reti。
思考2:mov 20h,#0h 和 setb 20h 都是加1,用什么区别?
我的回答:mov指令中的20h指字节,setb中的20h是位。
五、感受
还记得前段时间我一直纠结于汇编语言中的各种指令的语法和功能,直到一个阳光明媚的中午,我一手拿着已经写好的两页半的C语言代码,一手拿着一本单片机的汇编指令查询手册,开始一行一行的翻译,可能汇编代码会在调试中有所错误,但基本逻辑是对的。而且这次C——>汇编,使我更加深入地理解了数据在计算机中的存储与调用。在此期间班主任和同学也给我答疑解惑,相信在以后的道路上,我会更加更深入地理解计算机。
至今记得班主任对我说的一段话,在此转述如下:这辈子你可能都不会用汇编语言写代码,但我要求你们用C语言转汇编,是让你们体会数据在底层的存储过程,这样在以后你们用高级语言写程序时,不会犯看似低级但又无法避免的错误(大概意思就是这样)。
感谢我的老师,如果没有他的指引,我估计就无法体会计算机底层蕴含的神奇之处。