作了一單片機設計,要用C語言與彙編語言同時實現,現將此次設計的感覺和收穫,還有遇到的問題寫下,歡迎感興趣的朋友交流想法,提出建議。git
單片機設計:基於51單片機的99碼錶設計github
軟件環境:Proteus8.0 + Keil4函數
要求:1,開關按一下,數碼管開始計時。2,按兩下,數碼管顯示靜止。3,按三下,數碼管數值清零。ui
代碼片斷地址spa
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語言——>彙編語言之旅設計
(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。code
(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語言轉匯編,是讓大家體會數據在底層的存儲過程,這樣在之後大家用高級語言寫程序時,不會犯看似低級但又沒法避免的錯誤(大概意思就是這樣)。
感謝個人老師,若是沒有他的指引,我估計就沒法體會計算機底層蘊含的神奇之處。