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

2020-12-05 電子產品世界

開場白:

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

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

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

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

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

(1)硬體平臺:

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

(2)實現功能:

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

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

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

(3)原始碼講解如下:

  1. #include "REG52.H"
  2. #define const_voice_short40 //蜂鳴器短叫的持續時間
  3. #define const_voice_long 900 //蜂鳴器長叫的持續時間
  4. #define const_key_time10 //按鍵去抖動延時的時間
  5. #define const_1s 422 //產生一秒鐘的時間基準
  6. void initial_myself();
  7. void initial_peripheral();
  8. void delay_short(unsigned int uiDelayShort);
  9. void delay_long(unsigned int uiDelaylong);
  10. void T0_time();//定時中斷函數
  11. void key_service();
  12. void key_scan(); //按鍵掃描函數 放在定時中斷裡
  13. void number_key_input(unsigned long ucWhichKey);//由於數字按鍵的代碼相似度高,因此封裝在這個函數裡
  14. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  15. void display_drive();//放在定時中斷裡的數碼管驅動函數
  16. void display_service();
  17. sbit key_sr1=P0^0; //第一行輸入
  18. sbit key_sr2=P0^1; //第二行輸入
  19. sbit key_sr3=P0^2; //第三行輸入
  20. sbit key_sr4=P0^3; //第四行輸入
  21. sbit key_dr1=P0^4; //第一列輸出
  22. sbit key_dr2=P0^5; //第二列輸出
  23. sbit key_dr3=P0^6; //第三列輸出
  24. sbit key_dr4=P0^7; //第四列輸出
  25. sbit beep_dr=P2^7; //蜂鳴器的驅動IO口
  26. sbit led_dr=P3^5; //LED指示燈
  27. sbit dig_hc595_sh_dr=P2^0; //數碼管 的74HC595程序
  28. sbit dig_hc595_st_dr=P2^1;
  29. sbit dig_hc595_ds_dr=P2^2;
  30. sbit hc595_sh_dr=P2^3; //LED燈的74HC595程序
  31. sbit hc595_st_dr=P2^4;
  32. sbit hc595_ds_dr=P2^5;
  33. unsigned char ucKeyStep=1;//按鍵掃描步驟變量
  34. unsigned char ucKeySec=0; //被觸發的按鍵編號
  35. unsigned intuiKeyTimeCnt=0; //按鍵去抖動延時計數器
  36. unsigned char ucKeyLock=0; //按鍵觸發後自鎖的變量標誌
  37. unsigned char ucRowRecord=1; //記錄當前掃描到第幾列了
  38. unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續時間計數器
  39. unsigned char ucDigShow8=0;//第8位數碼管要顯示的內容
  40. unsigned char ucDigShow7=0;//第7位數碼管要顯示的內容
  41. unsigned char ucDigShow6=0;//第6位數碼管要顯示的內容
  42. unsigned char ucDigShow5=0;//第5位數碼管要顯示的內容
  43. unsigned char ucDigShow4=0;//第4位數碼管要顯示的內容
  44. unsigned char ucDigShow3=0;//第3位數碼管要顯示的內容
  45. unsigned char ucDigShow2=0;//第2位數碼管要顯示的內容
  46. unsigned char ucDigShow1=0;//第1位數碼管要顯示的內容
  47. unsigned char ucDigShowTemp=0; //臨時中間變量
  48. unsigned char ucDisplayDriveStep=1;//動態掃描數碼管的步驟變量
  49. unsigned char ucDisplayUpdate=1; //更新顯示標誌
  50. unsigned long ulSource=0;//原始數據 比如在加法運算中的被加數
  51. unsigned long ulOther=0; //另外一個參與運算的數據比如在加法運算中的加數
  52. unsigned long ulResult=0; //運算結果
  53. unsigned char ucOperator=0; //運行符號。0代表當前沒有選擇運行符號。1代表當前的運算符是加法。
  54. /* 注釋一:
  55. *ucWd變量是本程序最核心的變量,代表數碼管顯示哪一個窗口
  56. *本程序只有兩個窗口,他們分別是:
  57. *第一個窗口:原始數據和運算結果窗口。比如加法運算中的被加數
  58. *第二個窗口:第二個參與運行的數據窗口。比如加法運算中的加數
  59. */
  60. unsigned char ucWd=1;
  61. code unsigned char dig_table[]=
  62. {
  63. 0x3f,//0 序號0
  64. 0x06,//1 序號1
  65. 0x5b,//2 序號2
  66. 0x4f,//3 序號3
  67. 0x66,//4 序號4
  68. 0x6d,//5 序號5
  69. 0x7d,//6 序號6
  70. 0x07,//7 序號7
  71. 0x7f,//8 序號8
  72. 0x6f,//9 序號9
  73. 0x00,//不顯示序號10
  74. };
  75. void main()
  76. {
  77. initial_myself();
  78. delay_long(100);
  79. initial_peripheral();
  80. while(1)
  81. {
  82. key_service();
  83. display_service();
  84. }
  85. }
  86. void display_service()//放在定時中斷裡的顯示應用程式
  87. {
  88. if(ucDisplayUpdate==1)//有數據更新顯示
  89. {
  90. ucDisplayUpdate=0;
  91. switch(ucWd) //本程序最核心的變量ucWd
  92. {
  93. case 1://窗口1原始數據和運算結果窗口
  94. if(ulSource>=10000000)
  95. {
  96. ucDigShow8=ulSource/10000000;
  97. }
  98. else
  99. {
  100. ucDigShow8=10;//數據顯示空
  101. }
  102. if(ulSource>=1000000)
  103. {
  104. ucDigShow7=ulSource%10000000/1000000;
  105. }
  106. else
  107. {
  108. ucDigShow7=10;//數據顯示空
  109. }
  110. if(ulSource>=100000)
  111. {
  112. ucDigShow6=ulSource%1000000/100000;
  113. }
  114. else
  115. {
  116. ucDigShow6=10;//數據顯示空
  117. }
  118. if(ulSource>=10000)
  119. {
  120. ucDigShow5=ulSource%100000/10000;
  121. }
  122. else
  123. {
  124. ucDigShow5=10;//數據顯示空
  125. }
  126. if(ulSource>=1000)
  127. {
  128. ucDigShow4=ulSource%10000/1000;
  129. }
  130. else
  131. {
  132. ucDigShow4=10;//數據顯示空
  133. }
  134. if(ulSource>=100)
  135. {
  136. ucDigShow3=ulSource%1000/100;
  137. }
  138. else
  139. {
  140. ucDigShow3=10;//數據顯示空
  141. }
  142. if(ulSource>=10)
  143. {
  144. ucDigShow2=ulSource%100/10;
  145. }
  146. else
  147. {
  148. ucDigShow2=10;//數據顯示空
  149. }
  150. ucDigShow1=ulSource%10;
  151. break;
  152. case 2://窗口2第二個參與運算數據的窗口比如加法運算中的加數
  153. if(ulOther>=10000000)
  154. {
  155. ucDigShow8=ulOther/10000000;
  156. }
  157. else
  158. {
  159. ucDigShow8=10;//數據顯示空
  160. }
  161. if(ulOther>=1000000)
  162. {
  163. ucDigShow7=ulOther%10000000/1000000;
  164. }
  165. else
  166. {
  167. ucDigShow7=10;//數據顯示空
  168. }
  169. if(ulOther>=100000)
  170. {
  171. ucDigShow6=ulOther%1000000/100000;
  172. }
  173. else
  174. {
  175. ucDigShow6=10;//數據顯示空
  176. }
  177. if(ulOther>=10000)
  178. {
  179. ucDigShow5=ulOther%100000/10000;
  180. }
  181. else
  182. {
  183. ucDigShow5=10;//數據顯示空
  184. }
  185. if(ulOther>=1000)
  186. {
  187. ucDigShow4=ulOther%10000/1000;
  188. }
  189. else
  190. {
  191. ucDigShow4=10;//數據顯示空
  192. }
  193. if(ulOther>=100)
  194. {
  195. ucDigShow3=ulOther%1000/100;
  196. }
  197. else
  198. {
  199. ucDigShow3=10;//數據顯示空
  200. }
  201. if(ulOther>=10)
  202. {
  203. ucDigShow2=ulOther%100/10;
  204. }
  205. else
  206. {
  207. ucDigShow2=10;//數據顯示空
  208. }
  209. ucDigShow1=ulOther%10;
  210. break;
  211. }
  212. }
  213. }
  214. void display_drive()//放在定時中斷裡的數碼管驅動函數
  215. {
  216. //以下程序,如果加一些數組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路
  217. switch(ucDisplayDriveStep)
  218. {
  219. case 1://顯示第1位
  220. ucDigShowTemp=dig_table[ucDigShow1];
  221. dig_hc595_drive(ucDigShowTemp,0xfe);
  222. break;
  223. case 2://顯示第2位
  224. ucDigShowTemp=dig_table[ucDigShow2];
  225. dig_hc595_drive(ucDigShowTemp,0xfd);
  226. break;
  227. case 3://顯示第3位
  228. ucDigShowTemp=dig_table[ucDigShow3];
  229. dig_hc595_drive(ucDigShowTemp,0xfb);
  230. break;
  231. case 4://顯示第4位
  232. ucDigShowTemp=dig_table[ucDigShow4];
  233. dig_hc595_drive(ucDigShowTemp,0xf7);
  234. break;
  235. case 5://顯示第5位
  236. ucDigShowTemp=dig_table[ucDigShow5];
  237. dig_hc595_drive(ucDigShowTemp,0xef);
  238. break;
  239. case 6://顯示第6位
  240. ucDigShowTemp=dig_table[ucDigShow6];
  241. dig_hc595_drive(ucDigShowTemp,0xdf);
  242. break;
  243. case 7://顯示第7位
  244. ucDigShowTemp=dig_table[ucDigShow7];
  245. dig_hc595_drive(ucDigShowTemp,0xbf);
  246. break;
  247. case 8://顯示第8位
  248. ucDigShowTemp=dig_table[ucDigShow8];
  249. dig_hc595_drive(ucDigShowTemp,0x7f);
  250. break;
  251. }
  252. ucDisplayDriveStep++;
  253. if(ucDisplayDriveStep>8)//掃描完8個數碼管後,重新從第一個開始掃描
  254. {
  255. ucDisplayDriveStep=1;
  256. }
  257. }
  258. //數碼管的74HC595驅動函數
  259. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  260. {
  261. unsigned char i;
  262. unsigned char ucTempData;
  263. dig_hc595_sh_dr=0;
  264. dig_hc595_st_dr=0;
  265. ucTempData=ucDigStatusTemp16_09;//先送高8位
  266. for(i=0;i<8;i++)
  267. {
  268. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  269. else dig_hc595_ds_dr=0;
  270. /* 注釋二:
  271. *注意,此處的延時delay_short必須儘可能小,否則動態掃描數碼管的速度就不夠。
  272. */
  273. dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
  274. delay_short(1);
  275. dig_hc595_sh_dr=1;
  276. delay_short(1);
  277. ucTempData=ucTempData<<1;
  278. }
  279. ucTempData=ucDigStatusTemp08_01;//再先送低8位
  280. for(i=0;i<8;i++)
  281. {
  282. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  283. else dig_hc595_ds_dr=0;
  284. dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
  285. delay_short(1);
  286. dig_hc595_sh_dr=1;
  287. delay_short(1);
  288. ucTempData=ucTempData<<1;
  289. }
  290. dig_hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上並且鎖存起來
  291. delay_short(1);
  292. dig_hc595_st_dr=1;
  293. delay_short(1);
  294. dig_hc595_sh_dr=0; //拉低,抗幹擾就增強
  295. dig_hc595_st_dr=0;
  296. dig_hc595_ds_dr=0;
  297. }
  298. //LED燈的74HC595驅動函數
  299. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  300. {
  301. unsigned char i;
  302. unsigned char ucTempData;
  303. hc595_sh_dr=0;
  304. hc595_st_dr=0;
  305. ucTempData=ucLedStatusTemp16_09;//先送高8位
  306. for(i=0;i<8;i++)
  307. {
  308. if(ucTempData>=0x80)hc595_ds_dr=1;
  309. else hc595_ds_dr=0;
  310. hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
  311. delay_short(1);
  312. hc595_sh_dr=1;
  313. delay_short(1);
  314. ucTempData=ucTempData<<1;
  315. }
  316. ucTempData=ucLedStatusTemp08_01;//再先送低8位
  317. for(i=0;i<8;i++)
  318. {
  319. if(ucTempData>=0x80)hc595_ds_dr=1;
  320. else hc595_ds_dr=0;
  321. hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
  322. delay_short(1);
  323. hc595_sh_dr=1;
  324. delay_short(1);
  325. ucTempData=ucTempData<<1;
  326. }
  327. hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上並且鎖存起來
  328. delay_short(1);
  329. hc595_st_dr=1;
  330. delay_short(1);
  331. hc595_sh_dr=0; //拉低,抗幹擾就增強
  332. hc595_st_dr=0;
  333. hc595_ds_dr=0;
  334. }
  335. void key_scan()//按鍵掃描函數 放在定時中斷裡
  336. {
  337. switch(ucKeyStep)
  338. {
  339. case 1: //按鍵掃描輸出第ucRowRecord列低電平
  340. if(ucRowRecord==1)//第一列輸出低電平
  341. {
  342. key_dr1=0;
  343. key_dr2=1;
  344. key_dr3=1;
  345. key_dr4=1;
  346. }
  347. else if(ucRowRecord==2)//第二列輸出低電平
  348. {
  349. key_dr1=1;
  350. key_dr2=0;
  351. key_dr3=1;
  352. key_dr4=1;
  353. }
  354. else if(ucRowRecord==3)//第三列輸出低電平
  355. {
  356. key_dr1=1;
  357. key_dr2=1;
  358. key_dr3=0;
  359. key_dr4=1;
  360. }
  361. else //第四列輸出低電平
  362. {
  363. key_dr1=1;
  364. key_dr2=1;
  365. key_dr3=1;
  366. key_dr4=0;
  367. }
  368. uiKeyTimeCnt=0;//延時計數器清零
  369. ucKeyStep++; //切換到下一個運行步驟
  370. break;
  371. case 2: //此處的小延時用來等待剛才列輸出信號穩定,再判斷輸入信號。不是去抖動延時。
  372. uiKeyTimeCnt++;
  373. if(uiKeyTimeCnt>1)
  374. {
  375. uiKeyTimeCnt=0;
  376. ucKeyStep++; //切換到下一個運行步驟
  377. }
  378. break;
  379. case 3:
  380. if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==1)
  381. {
  382. ucKeyStep=1;//如果沒有按鍵按下,返回到第一個運行步驟重新開始掃描
  383. ucKeyLock=0;//按鍵自鎖標誌清零
  384. uiKeyTimeCnt=0; //按鍵去抖動延時計數器清零,此行非常巧妙
  385. ucRowRecord++;//輸出下一列
  386. if(ucRowRecord>4)
  387. {
  388. ucRowRecord=1; //依次輸出完四列之後,繼續從第一列開始輸出低電平
  389. }
  390. }
  391. else if(ucKeyLock==0)//有按鍵按下,且是第一次觸發
  392. {
  393. if(key_sr1==0&&key_sr2==1&&key_sr3==1&&key_sr4==1)
  394. {
  395. uiKeyTimeCnt++;//去抖動延時計數器
  396. if(uiKeyTimeCnt>const_key_time)
  397. {
  398. uiKeyTimeCnt=0;
  399. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零
  400. if(ucRowRecord==1)//第一列輸出低電平
  401. {
  402. ucKeySec=1;//觸發1號鍵 對應朱兆祺學習板的S1鍵
  403. }
  404. else if(ucRowRecord==2)//第二列輸出低電平
  405. {
  406. ucKeySec=2;//觸發2號鍵 對應朱兆祺學習板的S2鍵
  407. }
  408. else if(ucRowRecord==3)//第三列輸出低電平
  409. {
  410. ucKeySec=3;//觸發3號鍵 對應朱兆祺學習板的S3鍵
  411. }
  412. else //第四列輸出低電平
  413. {
  414. ucKeySec=4;//觸發4號鍵 對應朱兆祺學習板的S4鍵
  415. }
  416. }
  417. }
  418. else if(key_sr1==1&&key_sr2==0&&key_sr3==1&&key_sr4==1)
  419. {
  420. uiKeyTimeCnt++;//去抖動延時計數器
  421. if(uiKeyTimeCnt>const_key_time)
  422. {
  423. uiKeyTimeCnt=0;
  424. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零
  425. if(ucRowRecord==1)//第一列輸出低電平
  426. {
  427. ucKeySec=5;//觸發5號鍵 對應朱兆祺學習板的S5鍵
  428. }
  429. else if(ucRowRecord==2)//第二列輸出低電平
  430. {
  431. ucKeySec=6;//觸發6號鍵 對應朱兆祺學習板的S6鍵
  432. }
  433. else if(ucRowRecord==3)//第三列輸出低電平
  434. {
  435. ucKeySec=7;//觸發7號鍵 對應朱兆祺學習板的S7鍵
  436. }
  437. else //第四列輸出低電平
  438. {
  439. ucKeySec=8;//觸發8號鍵 對應朱兆祺學習板的S8鍵
  440. }
  441. }
  442. }
  443. else if(key_sr1==1&&key_sr2==1&&key_sr3==0&&key_sr4==1)
  444. {
  445. uiKeyTimeCnt++;//去抖動延時計數器
  446. if(uiKeyTimeCnt>const_key_time)
  447. {
  448. uiKeyTimeCnt=0;
  449. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零
  450. if(ucRowRecord==1)//第一列輸出低電平
  451. {
  452. ucKeySec=9;//觸發9號鍵 對應朱兆祺學習板的S9鍵
  453. }
  454. else if(ucRowRecord==2)//第二列輸出低電平
  455. {
  456. ucKeySec=10;//觸發10號鍵 對應朱兆祺學習板的S10鍵
  457. }
  458. else if(ucRowRecord==3)//第三列輸出低電平
  459. {
  460. ucKeySec=11;//觸發11號鍵 對應朱兆祺學習板的S11鍵
  461. }
  462. else //第四列輸出低電平
  463. {
  464. ucKeySec=12;//觸發12號鍵 對應朱兆祺學習板的S12鍵
  465. }
  466. }
  467. }
  468. else if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==0)
  469. {
  470. uiKeyTimeCnt++;//去抖動延時計數器
  471. if(uiKeyTimeCnt>const_key_time)
  472. {
  473. uiKeyTimeCnt=0;
  474. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有鬆開按鍵,此標誌位才會被清零
  475. if(ucRowRecord==1)//第一列輸出低電平
  476. {
  477. ucKeySec=13;//觸發13號鍵 對應朱兆祺學習板的S13鍵
  478. }
  479. else if(ucRowRecord==2)//第二列輸出低電平
  480. {
  481. ucKeySec=14;//觸發14號鍵 對應朱兆祺學習板的S14鍵
  482. }
  483. else if(ucRowRecord==3)//第三列輸出低電平
  484. {
  485. ucKeySec=15;//觸發15號鍵 對應朱兆祺學習板的S15鍵
  486. }
  487. else //第四列輸出低電平
  488. {
  489. ucKeySec=16;//觸發16號鍵 對應朱兆祺學習板的S16鍵
  490. }
  491. }
  492. }
  493. }
  494. break;
  495. }
  496. }
  497. /* 注釋三:
  498. *按鍵服務程序操作的精髓在於根據當前系統處於什麼窗口下,在此窗口下的運算符處於
  499. *什麼狀態,然後緊緊圍繞著不同的窗口ucWd,不同的ucOperator來執行不同的操作。
  500. */
  501. void key_service() //第三區 按鍵服務的應用程式
  502. {
  503. switch(ucKeySec) //按鍵服務狀態切換
  504. {
  505. case 1:// 1號鍵 對應朱兆祺學習板的S1鍵
  506. number_key_input(1);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  507. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  508. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  509. break;
  510. case 2:// 2號鍵 對應朱兆祺學習板的S2鍵
  511. number_key_input(2);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  512. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  513. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  514. break;
  515. case 3:// 3號鍵 對應朱兆祺學習板的S3鍵
  516. number_key_input(3);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  517. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  518. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  519. break;
  520. case 4:// 4號鍵 對應朱兆祺學習板的S4鍵
  521. number_key_input(4);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  522. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  523. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  524. break;
  525. case 5:// 5號鍵 對應朱兆祺學習板的S5鍵
  526. number_key_input(5);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  527. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  528. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  529. break;
  530. case 6:// 6號鍵 對應朱兆祺學習板的S6鍵
  531. number_key_input(6);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  532. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  533. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  534. break;
  535. case 7:// 7號鍵 對應朱兆祺學習板的S7鍵
  536. number_key_input(7);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  537. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  538. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  539. break;
  540. case 8:// 8號鍵 對應朱兆祺學習板的S8鍵
  541. number_key_input(8);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  542. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  543. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  544. break;
  545. case 9:// 9號鍵 對應朱兆祺學習板的S9鍵
  546. number_key_input(9);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  547. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  548. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  549. break;
  550. case 10:// 把這個按鍵專門用來輸入數字0 對應朱兆祺學習板的S10鍵
  551. number_key_input(0);//由於數字按鍵的代碼相似度高,因此把具體代碼封裝在這個函數裡
  552. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  553. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  554. break;
  555. case 11:// 11號鍵 對應朱兆祺學習板的S11鍵
  556. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  557. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  558. break;
  559. case 12:// 12號鍵 對應朱兆祺學習板的S12鍵
  560. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  561. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  562. break;
  563. case 13:// 13號鍵 加號按鍵對應朱兆祺學習板的S13鍵
  564. switch(ucWd)
  565. {
  566. case 1: //在原始數據和運算結果的窗口下
  567. ucOperator=1; //加法
  568. ulOther=ulSource;//第二個運算數默認等於原始數
  569. ucDisplayUpdate=1;//刷新顯示窗口
  570. break;
  571. case 2: //在第二個參與運算數據的窗口下
  572. ulResult=ulSource+ulOther;//連加
  573. ulSource=ulResult; //下一次運算的原始數據默認為當前運算結果,方便連加功能
  574. ucWd=1; //切換到第一個窗口
  575. ucDisplayUpdate=1;//刷新顯示窗口
  576. break;
  577. }
  578. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  579. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  580. break;
  581. case 14:// 14號鍵 等於號按鍵對應朱兆祺學習板的S14鍵
  582. switch(ucWd)
  583. {
  584. case 1: //在原始數據和運算結果的窗口下
  585. switch(ucOperator)//根據不同的運算符號進行不同的操作
  586. {
  587. case 0://無運算符號
  588. break;
  589. case 1://加法
  590. ulResult=ulSource+ulOther;//連加
  591. ulSource=ulResult; //下一次運算的原始數據默認為當前運算結果,方便連加功能
  592. ucDisplayUpdate=1;//刷新顯示窗口
  593. break;
  594. case 2://減法本程序沒有減法功能,如果讀者想增加減法程序,可以按鍵這個框架添加下去
  595. break;
  596. }
  597. break;
  598. case 2: //在第二個參與運算數據的窗口下
  599. switch(ucOperator)//根據不同的運算符號進行不同的操作
  600. {
  601. case 1://加法
  602. ulResult=ulSource+ulOther;//連加
  603. ulSource=ulResult; //下一次運算的原始數據默認為當前運算結果,方便連加功能
  604. ucWd=1; //切換到第一個窗口
  605. ucDisplayUpdate=1;//刷新顯示窗口
  606. break;
  607. case 2://減法本程序沒有減法功能,如果讀者想增加減法程序,可以按鍵這個框架添加下去
  608. break;
  609. }
  610. break;
  611. }
  612. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  613. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  614. break;
  615. case 15:// 15號鍵 對應朱兆祺學習板的S15鍵
  616. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  617. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  618. break;
  619. case 16:// 16號鍵 清除按鍵 相當於復位的功能。重新輸入數據對應朱兆祺學習板的S16鍵
  620. ulSource=0;
  621. ulOther=0;
  622. ulResult=0;
  623. ucOperator=0;
  624. ucWd=1; //切換到第一個窗口
  625. ucDisplayUpdate=1;//刷新顯示窗口
  626. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
  627. ucKeySec=0;//響應按鍵服務處理程序後,按鍵編號清零,避免一致觸發
  628. break;
  629. }
  630. }
  631. /* 注釋四:
  632. * 此處參與運算的輸入數字ucWhichKey記得用最大變量類型unsigned long,可以避免數據溢出等錯誤
  633. */
  634. void number_key_input(unsigned long ucWhichKey)//由於數字按鍵的代碼相似度高,因此封裝在這個函數裡
  635. {
  636. switch(ucWd)
  637. {
  638. case 1: //在原始數據和運算結果的窗口下
  639. switch(ucOperator)//根據不同的運算符號進行不同的操作
  640. {
  641. case 0://無運算符號按鍵輸入原始數據,比如被加輸
  642. if(ulSource<=9999999) //最大只能輸入8位數
  643. {
  644. ulSource=ulSource*10+ucWhichKey;//十進位的數值移位方法。
  645. }
  646. break;
  647. default://在已經按下了運算符號的情況下
  648. ulOther=0;//第二個運算數先清零,再輸入新的數據,然後馬上切換到第2個窗口下
  649. ulOther=ucWhichKey;
  650. ucWd=2; //馬上切換到第二個窗口下
  651. break;
  652. }
  653. ucDisplayUpdate=1;//刷新顯示窗口
  654. break;
  655. case 2: //在第二個參與運算數據的窗口下 按鍵輸入第二個參與運算的數據
  656. if(ulOther<=9999999) //最大只能輸入8位數
  657. {
  658. ulOther=ulOther*10+ucWhichKey;//十進位的數值移位方法。
  659. }
  660. ucDisplayUpdate=1;//刷新顯示窗口
  661. break;
  662. }
  663. }
  664. void T0_time() interrupt 1
  665. {
  666. TF0=0;//清除中斷標誌
  667. TR0=0; //關中斷
  668. key_scan(); //放在定時中斷裡的按鍵掃描函數
  669. if(uiVoiceCnt!=0)
  670. {
  671. uiVoiceCnt--; //每次進入定時中斷都自減1,直到等於零為止。才停止鳴叫
  672. beep_dr=0;//蜂鳴器是PNP三極體控制,低電平就開始鳴叫。
  673. }
  674. else
  675. {
  676. ; //此處多加一個空指令,想維持跟if括號語句的數量對稱,都是兩條指令。不加也可以。
  677. beep_dr=1;//蜂鳴器是PNP三極體控制,高電平就停止鳴叫。
  678. }
  679. display_drive();//放在定時中斷裡的數碼管驅動函數
  680. /* 注釋五:
  681. *注意,此處的重裝初始值不能太大,否則動態掃描數碼管的速度就不夠。我把原來常用的2000改成了500。
  682. */
  683. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
  684. TL0=0x0b;
  685. TR0=1;//開中斷
  686. }
  687. void delay_short(unsigned int uiDelayShort)
  688. {
  689. unsigned int i;
  690. for(i=0;i
  691. {
  692. ; //一個分號相當於執行一條空語句
  693. }
  694. }
  695. void delay_long(unsigned int uiDelayLong)
  696. {
  697. unsigned int i;
  698. unsigned int j;
  699. for(i=0;i
  700. {
  701. for(j=0;j<500;j++)//內嵌循環的空指令數量
  702. {
  703. ; //一個分號相當於執行一條空語句
  704. }
  705. }
  706. }
  707. void initial_myself()//第一區 初始化單片機
  708. {
  709. led_dr=0;
  710. beep_dr=1; //用PNP三極體控制蜂鳴器,輸出高電平時不叫。
  711. hc595_drive(0x00,0x00);
  712. TMOD=0x01;//設置定時器0為工作方式1
  713. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
  714. TL0=0x0b;
  715. }
  716. void initial_peripheral() //第二區 初始化外圍
  717. {
  718. EA=1; //開總中斷
  719. ET0=1; //允許定時中斷
  720. TR0=1; //啟動定時中斷
  721. }


總結陳詞:

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


相關焦點

  • 一種基於AT89C51單片機的十進位計算器系統
    顯示採用4位7段共陽極LED動態顯示。軟體方面從分析計算器功能、流程圖設計,再到程序的編寫進行系統設計。本文引用地址:http://www.eepw.com.cn/article/262335.htm  引言  本系統採用AT89C51單片機作為控制器,用來實現實現四位數的「+」,「-」,「*」,「/」運算,運算結果通過數碼管顯示,並具有有清零功能。
  • 0.36寸3位7段數碼管
    0.36寸3位7段數碼管   數碼管動態顯示接口是單片機中應用為廣泛的一種顯示方式之一,動態驅動是將所有數碼管的8個顯示筆劃「a,b,c,d,e,f,g,dp」的同名端連在一起,另外為每個數碼管的公共極COM增加位選通控制電路,位選通由各自獨立的I/O線控制,當單片機輸出字形碼時,所有數碼管都接收到相同的字形碼,但究竟是哪個數碼管會顯示出字形,取決於單片機對位選通COM端電路的控制,
  • LED數碼管知識簡介
    LED數碼管是設備的簡易顯示常用電子元器件,下面將介紹它的性能特點,簡單檢測方法及應用注意事項等內容。 1.性能特點 led數碼管的主要特點如下: (1)能在低電壓、小電流條件下驅動發光,能與CMOS、ITL電路兼容。
  • 0.36寸6位led數碼管哪家口碑好
    0.36寸6位led數碼管哪家口碑好LED數碼管也稱半導體數碼管,它是將若干發光二極體按一定圖形排列並封裝在一起的常用的數碼顯示器件之一。LED數碼管種類很多,品種五花八門,這裡僅向初學者介紹常用的小型「8」字形LED數碼管的識別與使用方法。常用小型LED數碼管的封裝形式幾乎全部採用了雙列直插結構,並按照需要將1至多個「8」字形字符封裝在一起,以組成顯示位數不同的數碼管。
  • 自學單片機第四十一篇:什麼是數碼管
    在日常的顯示中,我看到的最多的是數碼管,也就是數字顯示的七段數碼管。萬年曆,溫度計,工業顯示屏等等。主要是原理簡單,顯示也比較豐富,可以完全滿足日常需求,節能。數碼管:數欄位碼顯示管。常見的是七段的,有的帶上小數點,時間顯示的會帶上秒表閃爍分隔。也有些複雜的顯示,會用米字型的,使得顯示內容更豐富。儘管顏色不一,外形各異,但原理是一樣的,內部都是一個一個的小LED並聯而成,通過控制單個小燈,實現總體中的部分亮滅。
  • 8段數碼管引腳圖,8段數碼管動態顯示詳解
    打開APP 8段數碼管引腳圖,8段數碼管動態顯示詳解 發表於 2016-09-13 18:12:25   數碼管也稱LED數碼管,不同行業人士對數碼管的稱呼不一樣,其實都是同樣的產品。
  • 數碼管顯示電壓電路圖大全(六款數碼管顯示電壓電路原理圖詳解)
    數碼管顯示電壓電路圖大全(六款數碼管顯示電壓電路原理圖詳解) 時間:2019-01-18 18:34 數碼管
  • 數碼管的工作原理與編碼顯示接口分析
    絕大多數熱水器用的都是數碼管,其他家電也用液晶屏與螢光屏。  數碼管也稱LED數碼管,不同行業人士對數碼管的稱呼不一樣,其實都是同樣的產品。數碼管按段數可分為七段數碼管和八段數碼管,八段數碼管比七段數碼管多一個發光二極體單元,也就是多一個小數點(DP)這個小數點可以更精確的表示數碼管想要顯示的內容;按能顯示多少個(8)可分為1位、2位、3位、4位、5位、6位、7位等數碼管。
  • 拆解前蘇聯產螢光數碼管計算器,內部電路結構彪悍!
    拆解前蘇聯產螢光數碼管計算器,內部電路結構彪悍!
  • ADC0832數字電壓表數碼管顯示(示例程序)
    ******************************************************************************描述:本文引用地址:http://www.eepw.com.cn/article/201611/317443.htmADC0832數字電壓表數碼管顯示
  • 動手做一個最簡單的加法計算器
    應該可以做一個最簡單的加法計算器了。 比如,我們想實現這樣一個效果,點擊一個按鈕,將顯示輸入框信息「請輸入一個數」,你輸入了一個數字之後,再顯示第二個輸入框信息「請輸入第二個數」,你再輸入第二個數。 然後電腦顯示兩個數計算的結果。
  • LabVIEW+Arduino之四位數碼管顯示
    微信搜索【沛華測控】訂閱我們Shania本來想更一期「一位數碼管倒計時」的課程,後面覺得不是很有意思,於是換成四位數碼管,想做一個「數碼管顯示電壓值」的效果。請看視頻實物接線圖這裡我們用到一個電位器跟一個12引腳的共陽極四位數碼管,數碼管的引腳圖如下:單個數碼管的每一段用a、b、c、d、d、e、f、dp標誌,對應Arduino的0-7號數字引腳;公共端1-4對應8-11號數字引腳
  • 單片機實現7段數碼管顯示
    本文引用地址:http://www.eepw.com.cn/article/201611/315974.htm單片機選擇常用的AT89C52,用與控制7段數碼管的顯示,共有4位7段共陰極數碼管,用於顯示數據.另外所需的器件如下:1、74ls273地址鎖存器,用於字位鎖存器和字型鎖存器,上升沿鎖存。
  • led數碼管怎麼接線_LED數碼管的測試
    第二步:將LED數碼管的信號線、電源線對接起來,信號線是兩芯的公母插頭(小頭);電源線也是兩芯的公母插(大頭);根據公進每出的通電方式,也就是公頭進電(帶帽),母頭出電,大頭為電源線(棕色正極,藍色負極)小頭為信號線(棕色正極,藍色負極)。
  • BCD七段數碼管顯示解碼器電路
    BCD七段數碼管顯示解碼器電路發光二極體(LED)由特殊的半導體材料砷化鎵、 磷砷化鎵等製成,可以單獨使用,也可以組裝成分段式或點陣式LED顯示器件(半導體顯示器)。分段式顯示器(LED數碼管)由7條線段圍成8型,每一段包含一個發光二極體。外加正向電壓時二極體導通,發出清晰的光,有紅、黃、綠等色。只要按規律控制各發光段的亮、滅,就可以顯示各種字形或符號。 圖4 - 17(a)是共陰式LED數碼管的原理圖,圖4-17(b)是其表示符號。
  • LPC1114的簡易數碼相框設計
    摘要:以NXP公司基於ARM Cortex—M0的LPC1100系列MCU LPC1114為控制核心,配合SD存儲卡、外圍電路和TFT液晶顯示屏,提出了一種簡易數碼相框的設計方案。
  • 簡易數字直流電壓表電路及程序
    設計一個簡易數字直流電壓表。(量程0V-2V、測量速度為大於等於2次/秒、測量誤差在±0.05V以內,有超限報警、數碼管顯示。)
  • 【放心託付】0.56寸3位7段數碼管
    0.56寸3位7段數碼管如果兩者相對比,線條燈成本比護欄管的要高,因為它是用鋁條,而護欄管是用PC罩,應該相差一半左右,但護欄管只能做簡單一些的圖形,因為它比較寬(一般為30、40、50mm),兩條之間的間隔較大,適合於較遠處觀看,而線條燈就比較窄了,只有十多毫米,安裝後可以做為顯示屏,顯示文字、圖片、甚至影像數碼管是用來顯示信息的,比如電梯上顯示樓層, x2電容器。
  • 萬能計算器
    萬能計算器 生活工具 大小: 9.36M
  • 孫德利 | 「菲利克斯」手搖計算機:「奧德涅爾輪」的應用讓計算器...
    在之後的4年中,他研製出了更完美的加法器樣機,這種加法器可以進行十進位加減法運算。帕斯卡大約製造了50臺樣機,並獲得了皇家授權生產。  這種加法器與算盤不同,它利用齒輪的轉動達到進位的目的,低位齒輪旋轉一周,高位齒輪旋轉一個數位,這樣就達到了自動進位的目的。雖然這臺計算器只能做加法運算,卻被公認為是世界上第一臺實用的計算器。