第36節:帶數碼管顯示的加法簡易計算器

2021-01-08 電子產品世界

開場白:

這一節要做一個簡單的計算器。這個計算器不帶小數點,只能進行不超過8位數據的加法運算,它麻雀雖小但是五臟俱全,它能清晰地勾勒出商業計算器的程序框架和思路。讀者只要看懂本節程序框架的規律,以後自己想做一個複雜一點的計算器應該是沒問題的。複雜的計算器在算法上要用數組進行特殊處理,不能簡單地直接用C語言的+,-,*,/運算符,這方面的內容我會在以後的章節中跟大家分享。

這一節要教會大家兩個知識點:

第一個:數字按鍵的輸入和十進位數值的移位方法。

第二個:繼續加深理解按鍵與數碼管的關聯程序框架。

具體內容,請看原始碼講解。

(1)硬體平臺:

基於朱兆祺51單片機學習板。數字1鍵對應S1鍵,數字2鍵對應S2鍵,數字3鍵對應S3鍵…. 數字9鍵對應S9鍵, 數字0鍵對應S10鍵。加號鍵對應S13,等於號鍵對應S14,清除復位按鍵對應S16。其它按鍵不用。

(2)實現功能:

常用的加法計算器功能。有連加功能。
本程序有2個窗口。

第1個窗口:原始數據和運算結果窗口。比如加法運算中的被加數

第2個窗口:第二個參與運行的數據窗口。比如加法運算中的加數

(3)原始碼講解如下:

#include "REG52.H"#define const_voice_short40 //蜂鳴器短叫的持續時間#define const_voice_long 900 //蜂鳴器長叫的持續時間#define const_key_time10 //按鍵去抖動延時的時間#define const_1s 422 //產生一秒鐘的時間基準void initial_myself(); void initial_peripheral();void delay_short(unsigned int uiDelayShort);void delay_long(unsigned int uiDelaylong);void T0_time();//定時中斷函數void key_service();void key_scan(); //按鍵掃描函數 放在定時中斷裡void number_key_input(unsigned long ucWhichKey);//由於數字按鍵的代碼相似度高,因此封裝在這個函數裡void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);void display_drive();//放在定時中斷裡的數碼管驅動函數void display_service();sbit key_sr1=P0^0; //第一行輸入sbit key_sr2=P0^1; //第二行輸入sbit key_sr3=P0^2; //第三行輸入sbit key_sr4=P0^3; //第四行輸入sbit key_dr1=P0^4; //第一列輸出sbit key_dr2=P0^5; //第二列輸出sbit key_dr3=P0^6; //第三列輸出sbit key_dr4=P0^7; //第四列輸出sbit beep_dr=P2^7; //蜂鳴器的驅動IO口sbit led_dr=P3^5; //LED指示燈sbit dig_hc595_sh_dr=P2^0; //數碼管 的74HC595程序sbit dig_hc595_st_dr=P2^1;sbit dig_hc595_ds_dr=P2^2;sbit hc595_sh_dr=P2^3; //LED燈的74HC595程序sbit hc595_st_dr=P2^4;sbit hc595_ds_dr=P2^5;unsigned char ucKeyStep=1;//按鍵掃描步驟變量unsigned char ucKeySec=0; //被觸發的按鍵編號unsigned intuiKeyTimeCnt=0; //按鍵去抖動延時計數器unsigned char ucKeyLock=0; //按鍵觸發後自鎖的變量標誌unsigned char ucRowRecord=1; //記錄當前掃描到第幾列了unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續時間計數器unsigned char ucDigShow8=0;//第8位數碼管要顯示的內容unsigned char ucDigShow7=0;//第7位數碼管要顯示的內容unsigned char ucDigShow6=0;//第6位數碼管要顯示的內容unsigned char ucDigShow5=0;//第5位數碼管要顯示的內容unsigned char ucDigShow4=0;//第4位數碼管要顯示的內容unsigned char ucDigShow3=0;//第3位數碼管要顯示的內容unsigned char ucDigShow2=0;//第2位數碼管要顯示的內容unsigned char ucDigShow1=0;//第1位數碼管要顯示的內容unsigned char ucDigShowTemp=0; //臨時中間變量unsigned char ucDisplayDriveStep=1;//動態掃描數碼管的步驟變量unsigned char ucDisplayUpdate=1; //更新顯示標誌unsigned long ulSource=0;//原始數據 比如在加法運算中的被加數unsigned long ulOther=0; //另外一個參與運算的數據比如在加法運算中的加數unsigned long ulResult=0; //運算結果unsigned char ucOperator=0; //運行符號。0代表當前沒有選擇運行符號。1代表當前的運算符是加法。/* 注釋一:*ucWd變量是本程序最核心的變量,代表數碼管顯示哪一個窗口*本程序只有兩個窗口,他們分別是:*第一個窗口:原始數據和運算結果窗口。比如加法運算中的被加數*第二個窗口:第二個參與運行的數據窗口。比如加法運算中的加數*/unsigned char ucWd=1;code unsigned char dig_table[]={0x3f,//0 序號00x06,//1 序號10x5b,//2 序號20x4f,//3 序號30x66,//4 序號40x6d,//5 序號50x7d,//6 序號60x07,//7 序號70x7f,//8 序號80x6f,//9 序號90x00,//不顯示序號10};void main(){ initial_myself(); delay_long(100); initial_peripheral(); while(1) { key_service(); display_service(); }}void display_service()//放在定時中斷裡的顯示應用程式{if(ucDisplayUpdate==1)//有數據更新顯示{ ucDisplayUpdate=0; switch(ucWd) //本程序最核心的變量ucWd { case 1://窗口1原始數據和運算結果窗口 if(ulSource>=10000000) { ucDigShow8=ulSource/10000000; } else { ucDigShow8=10;//數據顯示空 } if(ulSource>=1000000) { ucDigShow7=ulSource%10000000/1000000; } else { ucDigShow7=10;//數據顯示空 } if(ulSource>=100000) { ucDigShow6=ulSource%1000000/100000; } else { ucDigShow6=10;//數據顯示空 } if(ulSource>=10000) { ucDigShow5=ulSource%100000/10000; } else { ucDigShow5=10;//數據顯示空 } if(ulSource>=1000) { ucDigShow4=ulSource%10000/1000; } else { ucDigShow4=10;//數據顯示空 } if(ulSource>=100) { ucDigShow3=ulSource%1000/100; } else { ucDigShow3=10;//數據顯示空 } if(ulSource>=10) { ucDigShow2=ulSource%100/10; } else { ucDigShow2=10;//數據顯示空 } ucDigShow1=ulSource%10; break; case 2://窗口2第二個參與運算數據的窗口比如加法運算中的加數 if(ulOther>=10000000) { ucDigShow8=ulOther/10000000; } else { ucDigShow8=10;//數據顯示空 } if(ulOther>=1000000) { ucDigShow7=ulOther%10000000/1000000; } else { ucDigShow7=10;//數據顯示空 } if(ulOther>=100000) { ucDigShow6=ulOther%1000000/100000; } else { ucDigShow6=10;//數據顯示空 } if(ulOther>=10000) { ucDigShow5=ulOther%100000/10000; } else { ucDigShow5=10;//數據顯示空 } if(ulOther>=1000) { ucDigShow4=ulOther%10000/1000; } else { ucDigShow4=10;//數據顯示空 } if(ulOther>=100) { ucDigShow3=ulOther%1000/100; } else { ucDigShow3=10;//數據顯示空 } if(ulOther>=10) { ucDigShow2=ulOther%100/10; } else { ucDigShow2=10;//數據顯示空 } ucDigShow1=ulOther%10; break; }}}void display_drive()//放在定時中斷裡的數碼管驅動函數{ //以下程序,如果加一些數組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路 switch(ucDisplayDriveStep) { case 1://顯示第1位 ucDigShowTemp=dig_table[ucDigShow1]; dig_hc595_drive(ucDigShowTemp,0xfe); break; case 2://顯示第2位 ucDigShowTemp=dig_table[ucDigShow2]; dig_hc595_drive(ucDigShowTemp,0xfd); break; case 3://顯示第3位 ucDigShowTemp=dig_table[ucDigShow3]; dig_hc595_drive(ucDigShowTemp,0xfb); break; case 4://顯示第4位 ucDigShowTemp=dig_table[ucDigShow4]; dig_hc595_drive(ucDigShowTemp,0xf7); break; case 5://顯示第5位 ucDigShowTemp=dig_table[ucDigShow5]; dig_hc595_drive(ucDigShowTemp,0xef); break; case 6://顯示第6位 ucDigShowTemp=dig_table[ucDigShow6]; dig_hc595_drive(ucDigShowTemp,0xdf); break; case 7://顯示第7位 ucDigShowTemp=dig_table[ucDigShow7]; dig_hc595_drive(ucDigShowTemp,0xbf); break; case 8://顯示第8位 ucDigShowTemp=dig_table[ucDigShow8]; dig_hc595_drive(ucDigShowTemp,0x7f); break; } ucDisplayDriveStep++; if(ucDisplayDriveStep>8)//掃描完8個數碼管後,重新從第一個開始掃描 { ucDisplayDriveStep=1; }}//數碼管的74HC595驅動函數void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01){ unsigned char i; unsigned char ucTempData; dig_hc595_sh_dr=0; dig_hc595_st_dr=0; ucTempData=ucDigStatusTemp16_09;//先送高8位 for(i=0;i<8;i++) { if(ucTempData>=0x80)dig_hc595_ds_dr=1; else dig_hc595_ds_dr=0;/* 注釋二:*注意,此處的延時delay_short必須儘可能小,否則動態掃描數碼管的速度就不夠。*/ dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器 delay_short(1); dig_hc595_sh_dr=1; delay_short(1); ucTempData=ucTempData<<1; } ucTempData=ucDigStatusTemp08_01;//再先送低8位 for(i=0;i<8;i++) { if(ucTempData>=0x80)dig_hc595_ds_dr=1; else dig_hc595_ds_dr=0; dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器 delay_short(1); dig_hc595_sh_dr=1; delay_short(1); ucTempData=ucTempData<<1; } dig_hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上並且鎖存起來 delay_short(1); dig_hc595_st_dr=1; delay_short(1); dig_hc595_sh_dr=0; //拉低,抗幹擾就增強 dig_hc595_st_dr=0; dig_hc595_ds_dr=0;}//LED燈的74HC595驅動函數void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01){ unsigned char i; unsigned char ucTempData; hc595_sh_dr=0; hc595_st_dr=0; ucTempData=ucLedStatusTemp16_09;//先送高8位 for(i=0;i<8;i++) { if(ucTempData>=0x80)hc595_ds_dr=1; else hc595_ds_dr=0; hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器 delay_short(1); hc595_sh_dr=1; delay_short(1); ucTempData=ucTempData<<1; } ucTempData=ucLedStatusTemp08_01;//再先送低8位 for(i=0;i<8;i++) { if(ucTempData>=0x80)hc595_ds_dr=1; else hc595_ds_dr=0; hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器 delay_short(1); hc595_sh_dr=1; delay_short(1); ucTempData=ucTempData<<1; } hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上並且鎖存起來 delay_short(1); hc595_st_dr=1; delay_short(1); hc595_sh_dr=0; //拉低,抗幹擾就增強 hc595_st_dr=0; hc595_ds_dr=0;}void key_scan()//按鍵掃描函數 放在定時中斷裡{switch(ucKeyStep){ case 1: //按鍵掃描輸出第ucRowRecord列低電平 if(ucRowRecord==1)//第一列輸出低電平 { key_dr1=0; key_dr2=1; key_dr3=1; key_dr4=1; } else if(ucRowRecord==2)//第二列輸出低電平 { key_dr1=1; key_dr2=0; key_dr3=1; key_dr4=1; } else if(ucRowRecord==3)//第三列輸出低電平 { key_dr1=1; key_dr2=1; key_dr3=0; key_dr4=1; } else //第四列輸出低電平 { key_dr1=1; key_dr2=1; key_dr3=1; key_dr4=0; } uiKeyTimeCnt=0;//延時計數器清零 ucKeyStep++; //切換到下一個運行步驟 break; case 2: //此處的小延時用來等待剛才列輸出信號穩定,再判斷輸入信號。不是去抖動延時。 uiKeyTimeCnt++; if(uiKeyTimeCnt>1) { uiKeyTimeCnt=0; ucKeyStep++; //切換到下一個運行步驟 } break; case 3: if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==1) { ucKeyStep=1;//如果沒有按鍵按下,返回到第一個運行步驟重新開始掃描 ucKeyLock=0;//按鍵自鎖標誌清零 uiKeyTimeCnt=0; //按鍵去抖動延時計數器清零,此行非常巧妙 ucRowRecord++;//輸出下一列 if(ucRowRecord>4) { ucRowRecord=1; //依次輸出完四列之後,繼續從第一列開始輸出低電平 } } else if(ucKeyLock==0)//有按鍵按下,且是第一次觸發 { if(key_sr1==0&&key_sr2==1&&key_sr3==1&&key_sr4==1) { uiKeyTimeCnt++;//去抖動延時計數器 if(uiKeyTimeCnt>const_key_time) { uiKeyTimeCnt=0; ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零 if(ucRowRecord==1)//第一列輸出低電平 { ucKeySec=1;//觸發1號鍵 對應朱兆祺學習板的S1鍵 } else if(ucRowRecord==2)//第二列輸出低電平 { ucKeySec=2;//觸發2號鍵 對應朱兆祺學習板的S2鍵 } else if(ucRowRecord==3)//第三列輸出低電平 { ucKeySec=3;//觸發3號鍵 對應朱兆祺學習板的S3鍵 } else //第四列輸出低電平 { ucKeySec=4;//觸發4號鍵 對應朱兆祺學習板的S4鍵 } } } else if(key_sr1==1&&key_sr2==0&&key_sr3==1&&key_sr4==1) { uiKeyTimeCnt++;//去抖動延時計數器 if(uiKeyTimeCnt>const_key_time) { uiKeyTimeCnt=0; ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零 if(ucRowRecord==1)//第一列輸出低電平 { ucKeySec=5;//觸發5號鍵 對應朱兆祺學習板的S5鍵 } else if(ucRowRecord==2)//第二列輸出低電平 { ucKeySec=6;//觸發6號鍵 對應朱兆祺學習板的S6鍵 } else if(ucRowRecord==3)//第三列輸出低電平 { ucKeySec=7;//觸發7號鍵 對應朱兆祺學習板的S7鍵 } else //第四列輸出低電平 { ucKeySec=8;//觸發8號鍵 對應朱兆祺學習板的S8鍵 } } } else if(key_sr1==1&&key_sr2==1&&key_sr3==0&&key_sr4==1) { uiKeyTimeCnt++;//去抖動延時計數器 if(uiKeyTimeCnt>const_key_time) { uiKeyTimeCnt=0; ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零 if(ucRowRecord==1)//第一列輸出低電平 { ucKeySec=9;//觸發9號鍵 對應朱兆祺學習板的S9鍵 } else if(ucRowRecord==2)//第二列輸出低電平 { ucKeySec=10;//觸發10號鍵 對應朱兆祺學習板的S10鍵 } else if(ucRowRecord==3)//第三列輸出低電平 { ucKeySec=11;//觸發11號鍵 對應朱兆祺學習板的S11鍵 } else //第四列輸出低電平 { ucKeySec=12;//觸發12號鍵 對應朱兆祺學習板的S12鍵 } } } else if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==0) { uiKeyTimeCnt++;//去抖動延時計數器 if(uiKeyTimeCnt>const_key_time) { uiKeyTimeCnt=0; ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零 if(ucRowRecord==1)//第一列輸出低電平 { ucKeySec=13;//觸發13號鍵 對應朱兆祺學習板的S13鍵 } else if(ucRowRecord==2)//第二列輸出低電平 { ucKeySec=14;//觸發14號鍵 對應朱兆祺學習板的S14鍵 } else if(ucRowRecord==3)//第三列輸出低電平 { ucKeySec=15;//觸發15號鍵 對應朱兆祺學習板的S15鍵 } else //第四列輸出低電平 { ucKeySec=16;//觸發16號鍵 對應朱兆祺學習板的S16鍵 } } } } break;}}/* 注釋三:*按鍵服務程序操作的精髓在於根據當前系統處於什麼窗口下,在此窗口下的運算符處於*什麼狀態,然後緊緊圍繞著不同的窗口ucWd,不同的ucOperator來執行不同的操作。*/void key_service() //第三區 按鍵服務的應用程式{switch(ucKeySec) //按鍵服務狀態切換{ case 1:// 1號鍵 對應朱兆祺學習板的S1鍵 number_key_input(1);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 2:// 2號鍵 對應朱兆祺學習板的S2鍵 number_key_input(2);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 3:// 3號鍵 對應朱兆祺學習板的S3鍵 number_key_input(3);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 4:// 4號鍵 對應朱兆祺學習板的S4鍵 number_key_input(4);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 5:// 5號鍵 對應朱兆祺學習板的S5鍵 number_key_input(5);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 6:// 6號鍵 對應朱兆祺學習板的S6鍵 number_key_input(6);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 7:// 7號鍵 對應朱兆祺學習板的S7鍵 number_key_input(7);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 8:// 8號鍵 對應朱兆祺學習板的S8鍵 number_key_input(8);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 9:// 9號鍵 對應朱兆祺學習板的S9鍵 number_key_input(9);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 10:// 把這個按鍵專門用來輸入數字0 對應朱兆祺學習板的S10鍵 number_key_input(0);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 11:// 11號鍵 對應朱兆祺學習板的S11鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 12:// 12號鍵 對應朱兆祺學習板的S12鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 13:// 13號鍵 加號按鍵對應朱兆祺學習板的S13鍵 switch(ucWd) { case 1: //在原始數據和運算結果的窗口下 ucOperator=1; //加法 ulOther=ulSource;//第二個運算數默認等於原始數 ucDisplayUpdate=1;//刷新顯示窗口 break; case 2: //在第二個參與運算數據的窗口下 ulResult=ulSource+ulOther;//連加 ulSource=ulResult; //下一次運算的原始數據默認為當前運算結果,方便連加功能 ucWd=1; //切換到第一個窗口 ucDisplayUpdate=1;//刷新顯示窗口 break; } uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 14:// 14號鍵 等於號按鍵對應朱兆祺學習板的S14鍵 switch(ucWd) { case 1: //在原始數據和運算結果的窗口下 switch(ucOperator)//根據不同的運算符號進行不同的操作 { case 0://無運算符號 break; case 1://加法 ulResult=ulSource+ulOther;//連加 ulSource=ulResult; //下一次運算的原始數據默認為當前運算結果,方便連加功能 ucDisplayUpdate=1;//刷新顯示窗口 break; case 2://減法本程序沒有減法功能,如果讀者想增加減法程序,可以按鍵這個框架添加下去 break; } break; case 2: //在第二個參與運算數據的窗口下 switch(ucOperator)//根據不同的運算符號進行不同的操作 { case 1://加法 ulResult=ulSource+ulOther;//連加 ulSource=ulResult; //下一次運算的原始數據默認為當前運算結果,方便連加功能 ucWd=1; //切換到第一個窗口 ucDisplayUpdate=1;//刷新顯示窗口 break; case 2://減法本程序沒有減法功能,如果讀者想增加減法程序,可以按鍵這個框架添加下去 break; } break; } uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 15:// 15號鍵 對應朱兆祺學習板的S15鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; case 16:// 16號鍵 清除按鍵 相當於復位的功能。重新輸入數據對應朱兆祺學習板的S16鍵 ulSource=0; ulOther=0; ulResult=0; ucOperator=0; ucWd=1; //切換到第一個窗口 ucDisplayUpdate=1;//刷新顯示窗口 uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。 ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發 break; } }/* 注釋四:* 此處參與運算的輸入數字ucWhichKey記得用最大變量類型unsigned long,可以避免數據溢出等錯誤*/void number_key_input(unsigned long ucWhichKey)//由於數字按鍵的代碼相似度高,因此封裝在這個函數裡{ switch(ucWd) { case 1: //在原始數據和運算結果的窗口下 switch(ucOperator)//根據不同的運算符號進行不同的操作 { case 0://無運算符號按鍵輸入原始數據,比如被加輸 if(ulSource<=9999999) //最大只能輸入8位數 { ulSource=ulSource*10+ucWhichKey;//十進位的數值移位方法。 } break; default://在已經按下了運算符號的情況下 ulOther=0;//第二個運算數先清零,再輸入新的數據,然後馬上切換到第2個窗口下 ulOther=ucWhichKey; ucWd=2; //馬上切換到第二個窗口下 break; } ucDisplayUpdate=1;//刷新顯示窗口 break; case 2: //在第二個參與運算數據的窗口下 按鍵輸入第二個參與運算的數據 if(ulOther<=9999999) //最大只能輸入8位數 { ulOther=ulOther*10+ucWhichKey;//十進位的數值移位方法。 } ucDisplayUpdate=1;//刷新顯示窗口 break; }}void T0_time() interrupt 1{TF0=0;//清除中斷標誌TR0=0; //關中斷key_scan(); //放在定時中斷裡的按鍵掃描函數if(uiVoiceCnt!=0){ uiVoiceCnt--; //每次進入定時中斷都自減1,直到等於零為止。才停止鳴叫 beep_dr=0;//蜂鳴器是PNP三極體控制,低電平就開始鳴叫。}else{ ; //此處多加一個空指令,想維持跟if括號語句的數量對稱,都是兩條指令。不加也可以。 beep_dr=1;//蜂鳴器是PNP三極體控制,高電平就停止鳴叫。}display_drive();//放在定時中斷裡的數碼管驅動函數/* 注釋五:*注意,此處的重裝初始值不能太大,否則動態掃描數碼管的速度就不夠。我把原來常用的2000改成了500。*/TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0bTL0=0x0b;TR0=1;//開中斷}void delay_short(unsigned int uiDelayShort){ unsigned int i; for(i=0;i { ; //一個分號相當於執行一條空語句 }}void delay_long(unsigned int uiDelayLong){ unsigned int i; unsigned int j; for(i=0;i { for(j=0;j<500;j++)//內嵌循環的空指令數量 { ; //一個分號相當於執行一條空語句 } }}void initial_myself()//第一區 初始化單片機{led_dr=0;beep_dr=1; //用PNP三極體控制蜂鳴器,輸出高電平時不叫。hc595_drive(0x00,0x00);TMOD=0x01;//設置定時器0為工作方式1TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0bTL0=0x0b;}void initial_peripheral() //第二區 初始化外圍{EA=1; //開總中斷ET0=1; //允許定時中斷TR0=1; //啟動定時中斷}


總結陳詞:

這節講了加法簡易計算器的程序項目。為了讓讀者理解運動,按鍵,顯示是如何有規律關聯起來的,下節會繼續講一個相關的小項目程序。欲知詳情,請聽下回分解數碼管作為儀錶盤顯示跑馬燈的方向,速度和運行狀態。


相關焦點

  • 51單片機之4位數加法計算器的設計報告
    通過對4位數加法計算器題目的分析,採用4*4行列式鍵盤。2.2.5 顯示模塊的選擇方案論證方案一:採用帶字庫LCD模塊顯示。能顯示複雜的信息,具有質量輕,體積小,功耗低,指令功能強,接口簡單,可靠性強等優點,顯示內容豐富,圖形美觀,易於人機交流。但是價格昂貴。
  • 基於AT89C51單片機的十進位計算器系統設計
    顯示採用4 位7 段共陽極LED 動態顯示。軟體方面從分析計算器功能、流程圖設計,再到程序的編寫進行系統設計。  引言  本系統採用AT89C51 單片機作為控制器,用來實現實現四位數的「+」,「-」,「*」,「/」運算,運算結果通過數碼管顯示,並具有有清零功能。
  • 拆解前蘇聯產螢光數碼管計算器,內部電路結構彪悍!
    它的數碼字形由若千段分立的筆畫構成。螢光數碼管是一種電真空器件,其工作原理與普通的電子管相似,它的燈絲就是陰極(為直熱式陰極),當燈絲通電後燈絲就會發熱(即陰極發熱),由於在陰極表面塗有一層逸出功很小的氧化物,於是在陰極發熱後會發射大量的電子。  計算器的外觀:
  • 0.36寸3位7段數碼管
    0.36寸3位7段數碼管   數碼管動態顯示接口是單片機中應用為廣泛的一種顯示方式之一,動態驅動是將所有數碼管的8個顯示筆劃「a,b,c,d,e,f,g,dp」的同名端連在一起,另外為每個數碼管的公共極COM增加位選通控制電路,位選通由各自獨立的I/O線控制,當單片機輸出字形碼時,所有數碼管都接收到相同的字形碼,但究竟是哪個數碼管會顯示出字形,取決於單片機對位選通COM端電路的控制,
  • 正點原子開拓者FPGA開發板資料連載第十二章 動態數碼管顯示實驗
    第十二章 動態數碼管顯示實驗經過上一章的學習,我們已經知道如何使用數碼管靜態驅動的方式使數碼管顯示數字,但在很多情況下,我們需要讓數碼管各個位顯示不同的數字,這就需要以動態驅動的方式驅動數碼管。由此圖可知,兩位8段數碼管共10個引腳,每位數碼管的陽極連接在一起,為共陽極數碼管,每位數碼管相同段的led的陰極連接在一起,這樣當給第10和第5腳高電平,給第3腳低電平時,兩個數碼管的發光二極體A都點亮,對於此種數碼管以靜態方式驅動顯然不可能顯示像「18」這種個位與十位不同的數字
  • 如何使用MSP430單片機實現簡易計算器的設計
    本文介紹了一種基於MSP430單片機的簡易電子計算器設計。基於MSP430單片機的簡易電子計算器設計具有體積小、便於攜帶、應用方便、組成元器件少、製造成本低等優點。MSP430單片機具有的超低功耗、硬體浮點數運算等功能使得簡易電子計算器設計簡單、計算能力強,有著廣闊的發展前景。
  • 「每周FPGA案例」 至簡設計系列_簡易計算器設計(二)
    3、 由於該計算器支持連續輸入,如果當前計算的結果為負數,接著輸入的運算符為「加」,下一次進行加法運算,並且運算數1(此時比較不考慮符號位)小於或等於運算數2,則表示運算結果為正數,此時將result_neg信號拉低。4、 在進行連續計算的時候,如果得到的結果超過顯示上限,要進入錯誤狀態,這個時候符號位指示信號應該為低電平。
  • 正點原子開拓者FPGA開發板資料連載第十一章 靜態數碼管顯示實驗
    以共陽極數碼管為例,當我們想讓數碼管顯示數字「8」,可以給a、b、c…g七個引腳送低電平,數碼管就顯示「8」,顯示數字「1」,就給b、c引腳低電平,其餘引腳(除公共端)給高電平,數碼管就顯示「1」。因而對於多位數碼管的使用,一般都採用前一種方式進行電路設計,這種電路設計更為方便的是以動態方式驅動數碼管。動態顯示與靜態顯示的區別關鍵在於位選的控制。由於靜態顯示容易實現,本章我們以6位共陽數碼管的靜態方式為例,帶大家初步了解一下數碼管的工作原理。
  • 動態顯示LED數碼管
    通過分時輪流控制各個LED數碼管的COM(公共端),就使各個數碼管輪流受控顯示,這就是動態驅動。
  • python:簡易的計算器
    來咯來咯土土上場[比心]今天呢土土帶來了一個超級簡單的計算器代碼主要是學了def定義函數>def jia(x, y): return x + ydef jian(x, y): return x - ydef cheng(x, y): return x * ydef chu(x, y): return x / y設置一個選擇菜單print("|---歡迎使用簡易計算器
  • 基於51單片機12864簡易計算器
    終於寫計算器了,其實計算器老早就寫好了的,只是那會一直在忙考試還有實驗室項目,所以沒能空出時間來寫博客。現在寒假在家,終於可以靜心的學點東西了。 下面就先寫點自己寫這個程序的過程。其實這是個單片機課的期末課題,但是沒有幾個能寫的好的。計算器其實要考慮周全也是很複雜的,。
  • 動手做一個最簡單的加法計算器
    應該可以做一個最簡單的加法計算器了。 比如,我們想實現這樣一個效果,點擊一個按鈕,將顯示輸入框信息「請輸入一個數」,你輸入了一個數字之後,再顯示第二個輸入框信息「請輸入第二個數」,你再輸入第二個數。 然後電腦顯示兩個數計算的結果。
  • 八位數碼管動態顯示程序
    boardid=10id=4140/*動態數顯的設計思路,首先是我們要先知道怎樣才能使哪個位亮,顯示的段是什麼數值,接下來就是中斷時間的問題了,設計步驟如下,如果要讓八個數碼管靜態顯示1到8,我們可以修改中斷時間,如把50000改成100或更低*/#includereg52.h> #includeintrins.h>#define uint
  • 51單片機 8255 18b20 數碼管顯示溫度C程序
    ;uchar tplsb,tpmsb; // 溫度值低位、高位字節sbit date=P1^2; //數據通信線uchar code tableshi[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//數碼管
  • 8段數碼管引腳圖,8段數碼管動態顯示詳解
    打開APP 8段數碼管引腳圖,8段數碼管動態顯示詳解 發表於 2016-09-13 18:12:25   數碼管也稱LED數碼管
  • 單個數碼管顯示0-9
    一、實驗要求使用51單片機控制單個數碼管,使其循環顯示0-9.二、實驗原理數碼管(Segment Displays)由多個發光二極體封裝在一起組成「8」字型的器件,引線已在內部連接完成,只需引出它們的各個筆劃,公共電極。數碼管實際上是由七個發光管組成8字形構成的,加上小數點就是8個。這些段分別由字母a,b,c,d,e,f,g,dp來表示。
  • 單片機驅動數碼管設計詳解(74HC595實現)
    數碼管顯示設計本設計使用了一個4位的數碼管,為共陽型,為了節省單片機的IO口,使用了兩片74HC595作為數碼管的驅動晶片,共佔用3個IO口。兩片595採用級聯方式,即U2的第9腳接到U3的第14引腳。2. 74HC595簡介74HC595是8位的移位寄存器,串入並出,並具有鎖存功能,被廣泛的用於數碼管、點陣的驅動電路中。
  • 數碼管的靜態與動態顯示技術分析
    數碼管是單片機系統中經常用到的顯示器件, 從內部結構上可以分為共陰極和共陽極數碼管。對不同的數碼管,電路的接法也不一樣。圖1A為數碼管的結構圖。以共陽極數碼管為例, 要想點亮某段, 只需要在相應的段上給低電平即可。圖1B為共陽極數碼管段碼分布, 以及一個顯示的實例。
  • 【分享】cd4511功能表數碼管顯示
    cd4511數碼管驅動原理圖,是CD4511實現LED與單片機的並行接口方法如下圖:
  • LabVIEW+Arduino之四位數碼管顯示
    微信搜索【沛華測控】訂閱我們Shania本來想更一期「一位數碼管倒計時」的課程,後面覺得不是很有意思,於是換成四位數碼管,想做一個「數碼管顯示電壓值」的效果。經過幾天的摸索,分別使用LabVIEW和Arduino C/C++實現了同樣的效果,今天跟大家簡單分享一下。