新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 第35節:帶數碼管顯示的象棋比賽專用計時器

        第35節:帶數碼管顯示的象棋比賽專用計時器

        作者: 時間:2016-11-22 來源:網絡 收藏
        開場白:
        2014年春節的時候,一幫朋友舉行小規模的象棋比賽,有一些朋友下棋的速度實在是太慢了,為了限制比賽時間,我專門用朱兆祺的51學習板做了一個棋類比賽專用計時器給他們用。這一節要教會大家兩個知識點:
        第一個:按鍵服務程序操作的精髓在于根據當前系統處于什么窗口狀態下就執行什么操作。緊緊圍繞著不同的窗口ucWd來執行不同的操作。
        第二個:繼續加深熟悉鴻哥首次提出的“一二級菜單顯示理論”:凡是人機界面顯示,不管是數碼管還是液晶屏,都可以把顯示的內容分成不同的窗口來顯示,每個顯示的窗口中又可以分成不同的局部顯示。其中窗口就是一級菜單,用ucWd變量表示。局部就是二級菜單,用ucPart來表示。不同的窗口,會有不同的更新顯示變量ucWdXUpdate來對應,表示整屏全部更新顯示。不同的局部,也會有不同的更新顯示變量ucWdXPartYUpdate來對應,表示局部更新顯示。

        具體內容,請看源代碼講解。

        (1)硬件平臺:基于朱兆祺51單片機學習板
        剛上電開機時,紅棋加時鍵對應S1鍵,紅棋減時鍵對應S2鍵.。
        剛上電開機時,黑棋加時鍵對應S3鍵,黑棋減時鍵對應S4鍵.。
        比賽中途暫停雙方計時的暫停按鍵對應S6鍵。剛上電時,復位雙方默認20分時間的復位按鍵對應S7按鍵。
        紅棋的搶時按鍵對應S13鍵,黑棋的搶時按鍵對應S16按鍵。

        (2)實現功能:
        棋類計時器有點像搶答器,本質上有兩個計時器。比賽的時候對弈的兩個棋友各用一個不同的按鍵搶時間,紅棋走一步棋后,就按一下自己的搶時按鍵,這個時候紅棋的計時器停止計時,而黑棋的計時器開始計時,黑棋走了一步棋后,按一下自己的計時器,黑棋停止計時,紅棋繼續計時,依次循環,誰的時間最先用完誰就輸,蜂鳴器也會發出長鳴的聲音提示時間到。
        上電開機默認雙方各有20分鐘的時間,左邊顯示的是紅棋的時間,右邊顯示的是黑棋的時間。此時可以通過S1,S2.,S3,S4的加減按鍵來設置各自的最大倒計時時間。此時如果按下復位按鍵S7,會自動把雙方的時間設置為默認的20分鐘。
        設置好最大倒計時的時間后,此時任意一方按下各自的搶時按鍵(S13或者S16),則自己的計時器停止計時,而對方開始倒計時。此時數碼管顯示的是對方的時間,而自己的時間屏蔽不顯示。
        在開始倒計時的時候,如果中途有棋友要接聽電話或者忙別的事情,需要暫時暫停一下雙方的時間,這個時候可以按S6暫停按鍵來暫停雙方的計時,忙完后再次按下暫停按鍵會繼續倒計時。任何一方的時間走完,都會蜂鳴器長鳴提示。
        (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 dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
        14. void display_drive();//放在定時中斷里的數碼管驅動函數
        15. void time_service();//放在定時中斷里的時間應用程序
        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;//作為中途暫停指示燈 亮的時候表示中途暫停
        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 ucDigDot3=1;//數碼管3的小數點是否顯示的標志
        48. unsigned char ucDigDot7=1;//數碼管7的小數點是否顯示的標志
        49. unsigned char ucDigShowTemp=0; //臨時中間變量
        50. unsigned char ucDisplayDriveStep=1;//動態掃描數碼管的步驟變量
        51. unsigned int uiRedTimeCnt=0; //紅棋產生秒基準的時間計時器
        52. unsigned int uiBlackTimeCnt=0;//黑棋產生秒基準的時間計時器
        53. unsigned int uiRedTotal=1200; //紅棋的總時間
        54. unsigned int uiBlackTotal=1200;//黑棋的總時間
        55. unsigned char ucRedFlag=0;//紅棋是否開始計時的標志
        56. unsigned char ucBlackFlag=0;//黑棋是否開始計時的標志
        57. unsigned char ucDisplayUpdate=1; //更新顯示標志
        58. /* 注釋一:
        59. *ucWd變量是本程序最核心的變量,代表顯示哪一個窗口和系統處于當前哪種狀態
        60. */
        61. unsigned char ucWd=1;
        62. code unsigned char dig_table[]=
        63. {
        64. 0x3f,//0 序號0
        65. 0x06,//1 序號1
        66. 0x5b,//2 序號2
        67. 0x4f,//3 序號3
        68. 0x66,//4 序號4
        69. 0x6d,//5 序號5
        70. 0x7d,//6 序號6
        71. 0x07,//7 序號7
        72. 0x7f,//8 序號8
        73. 0x6f,//9 序號9
        74. 0x00,//不顯示序號10
        75. };
        76. void main()
        77. {
        78. initial_myself();
        79. delay_long(100);
        80. initial_peripheral();
        81. while(1)
        82. {
        83. key_service();
        84. display_service();
        85. }
        86. }
        87. void time_service()//放在定時中斷里的時間應用程序
        88. {
        89. if(ucRedFlag==1)//1代表紅棋在運行中
        90. {
        91. uiRedTimeCnt++;
        92. if(uiRedTimeCnt>const_1s)
        93. {
        94. uiRedTimeCnt=0;
        95. if(uiRedTotal>0)
        96. {
        97. uiRedTotal--;
        98. }
        99. else//時間到
        100. {
        101. ucRedFlag=0; //紅棋和黑棋同時停止計時
        102. ucBlackFlag=0;
        103. ucWd=1;//切換到第一個窗口的狀態
        104. uiVoiceCnt=const_voice_long; //報警聲音觸發
        105. }
        106. ucDisplayUpdate=1;//更新顯示
        107. }
        108. }
        109. if(ucBlackFlag==1)//1代表黑棋在運行中
        110. {
        111. uiBlackTimeCnt++;
        112. if(uiBlackTimeCnt>const_1s)
        113. {
        114. uiBlackTimeCnt=0;
        115. if(uiBlackTotal>0)
        116. {
        117. uiBlackTotal--;
        118. }
        119. else//時間到
        120. {
        121. ucRedFlag=0;//紅棋和黑棋同時停止計時
        122. ucBlackFlag=0;
        123. ucWd=1;//切換到第一個窗口的狀態
        124. uiVoiceCnt=const_voice_long; //報警聲音觸發
        125. }
        126. ucDisplayUpdate=1;//更新顯示
        127. }
        128. }
        129. }
        130. void display_service()//放在定時中斷里的顯示應用程序
        131. {
        132. if(ucDisplayUpdate==1)//有數據更新顯示
        133. {
        134. ucDisplayUpdate=0;
        135. switch(ucWd) //本程序最核心的變量ucWd
        136. {
        137. case 1://窗口1,代表剛上電或者復位后的狀態
        138. //紅棋分解出分
        139. ucDigShowTemp=uiRedTotal/60;
        140. ucDigShow8=ucDigShowTemp/10;
        141. ucDigShow7=ucDigShowTemp%10;
        142. //紅棋分解出秒
        143. ucDigShowTemp=uiRedTotal%60;
        144. ucDigShow6=ucDigShowTemp/10;
        145. ucDigShow5=ucDigShowTemp%10;
        146. ucDigDot7=1;//數碼管7的小數點顯示
        147. //黑棋分解出分
        148. ucDigShowTemp=uiBlackTotal/60;
        149. ucDigShow4=ucDigShowTemp/10;
        150. ucDigShow3=ucDigShowTemp%10;
        151. //黑棋分解出秒
        152. ucDigShowTemp=uiBlackTotal%60;
        153. ucDigShow2=ucDigShowTemp/10;
        154. ucDigShow1=ucDigShowTemp%10;
        155. ucDigDot3=1;//數碼管3的小數點顯示
        156. led_dr=1;//計時器處于停止狀態,LED亮
        157. break;
        158. case 2://窗口2,代表黑棋正在運行中的狀態
        159. //紅棋全部不顯示
        160. ucDigShow8=10;
        161. ucDigShow7=10;
        162. ucDigShow6=10;
        163. ucDigShow5=10;
        164. ucDigDot7=0;//數碼管7的小數點不顯示
        165. //黑棋分解出分
        166. ucDigShowTemp=uiBlackTotal/60;
        167. ucDigShow4=ucDigShowTemp/10;
        168. ucDigShow3=ucDigShowTemp%10;
        169. //黑棋分解出秒
        170. ucDigShowTemp=uiBlackTotal%60;
        171. ucDigShow2=ucDigShowTemp/10;
        172. ucDigShow1=ucDigShowTemp%10;
        173. ucDigDot3=1;//數碼管3的小數點顯示
        174. led_dr=0;//計時器處于計時狀態,LED滅
        175. break;
        176. case 3://窗口3,代表黑棋在中途暫停的狀態
        177. //紅棋全部不顯示
        178. ucDigShow8=10;
        179. ucDigShow7=10;
        180. ucDigShow6=10;
        181. ucDigShow5=10;
        182. ucDigDot7=0;//數碼管7的小數點不顯示
        183. //黑棋分解出分
        184. ucDigShowTemp=uiBlackTotal/60;
        185. ucDigShow4=ucDigShowTemp/10;
        186. ucDigShow3=ucDigShowTemp%10;
        187. //黑棋分解出秒
        188. ucDigShowTemp=uiBlackTotal%60;
        189. ucDigShow2=ucDigShowTemp/10;
        190. ucDigShow1=ucDigShowTemp%10;
        191. ucDigDot3=1;//數碼管3的小數點顯示
        192. led_dr=1;//計時器處于暫停狀態,LED亮
        193. break;
        194. case 4://窗口4,代表紅棋正在運行中的狀態
        195. //紅棋分解出分
        196. ucDigShowTemp=uiRedTotal/60;
        197. ucDigShow8=ucDigShowTemp/10;
        198. ucDigShow7=ucDigShowTemp%10;
        199. //紅棋分解出秒
        200. ucDigShowTemp=uiRedTotal%60;
        201. ucDigShow6=ucDigShowTemp/10;
        202. ucDigShow5=ucDigShowTemp%10;
        203. ucDigDot7=1;//數碼管7的小數點顯示
        204. //黑棋全部不顯示
        205. ucDigShow4=10;
        206. ucDigShow3=10;
        207. ucDigShow2=10;
        208. ucDigShow1=10;
        209. ucDigDot3=0;//數碼管3的小數點不顯示
        210. led_dr=0;//計時器處于倒計時狀態,LED滅
        211. break;
        212. case 5://窗口5,代表紅棋在中途暫停的狀態
        213. //紅棋分解出分
        214. ucDigShowTemp=uiRedTotal/60;
        215. ucDigShow8=ucDigShowTemp/10;
        216. ucDigShow7=ucDigShowTemp%10;
        217. //紅棋分解出秒
        218. ucDigShowTemp=uiRedTotal%60;
        219. ucDigShow6=ucDigShowTemp/10;
        220. ucDigShow5=ucDigShowTemp%10;
        221. ucDigDot7=1;//數碼管7的小數點顯示
        222. //黑棋全部不顯示
        223. ucDigShow4=10;
        224. ucDigShow3=10;
        225. ucDigShow2=10;
        226. ucDigShow1=10;
        227. ucDigDot3=0;//數碼管3的小數點不顯示
        228. led_dr=1;//計時器處于暫停狀態,LED亮
        229. break;
        230. }
        231. }
        232. }
        233. void display_drive()//放在定時中斷里的數碼管驅動函數
        234. {
        235. //以下程序,如果加一些數組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路
        236. switch(ucDisplayDriveStep)
        237. {
        238. case 1://顯示第1位
        239. ucDigShowTemp=dig_table[ucDigShow1];
        240. dig_hc595_drive(ucDigShowTemp,0xfe);
        241. break;
        242. case 2://顯示第2位
        243. ucDigShowTemp=dig_table[ucDigShow2];
        244. dig_hc595_drive(ucDigShowTemp,0xfd);
        245. break;
        246. case 3://顯示第3位
        247. ucDigShowTemp=dig_table[ucDigShow3];
        248. if(ucDigDot3==1)
        249. {
        250. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數點
        251. }
        252. dig_hc595_drive(ucDigShowTemp,0xfb);
        253. break;
        254. case 4://顯示第4位
        255. ucDigShowTemp=dig_table[ucDigShow4];
        256. dig_hc595_drive(ucDigShowTemp,0xf7);
        257. break;
        258. case 5://顯示第5位
        259. ucDigShowTemp=dig_table[ucDigShow5];
        260. dig_hc595_drive(ucDigShowTemp,0xef);
        261. break;
        262. case 6://顯示第6位
        263. ucDigShowTemp=dig_table[ucDigShow6];
        264. dig_hc595_drive(ucDigShowTemp,0xdf);
        265. break;
        266. case 7://顯示第7位
        267. ucDigShowTemp=dig_table[ucDigShow7];
        268. if(ucDigDot7==1)
        269. {
        270. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數點
        271. }
        272. dig_hc595_drive(ucDigShowTemp,0xbf);
        273. break;
        274. case 8://顯示第8位
        275. ucDigShowTemp=dig_table[ucDigShow8];
        276. dig_hc595_drive(ucDigShowTemp,0x7f);
        277. break;
        278. }
        279. ucDisplayDriveStep++;
        280. if(ucDisplayDriveStep>8)//掃描完8個數碼管后,重新從第一個開始掃描
        281. {
        282. ucDisplayDriveStep=1;
        283. }
        284. }
        285. //數碼管的74HC595驅動函數
        286. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
        287. {
        288. unsigned char i;
        289. unsigned char ucTempData;
        290. dig_hc595_sh_dr=0;
        291. dig_hc595_st_dr=0;
        292. ucTempData=ucDigStatusTemp16_09;//先送高8位
        293. for(i=0;i<8;i++)
        294. {
        295. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
        296. else dig_hc595_ds_dr=0;
        297. /* 注釋二:
        298. *注意,此處的延時delay_short必須盡可能小,否則動態掃描數碼管的速度就不夠。
        299. */
        300. dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
        301. delay_short(1);
        302. dig_hc595_sh_dr=1;
        303. delay_short(1);
        304. ucTempData=ucTempData<<1;
        305. }
        306. ucTempData=ucDigStatusTemp08_01;//再先送低8位
        307. for(i=0;i<8;i++)
        308. {
        309. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
        310. else dig_hc595_ds_dr=0;
        311. dig_hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
        312. delay_short(1);
        313. dig_hc595_sh_dr=1;
        314. delay_short(1);
        315. ucTempData=ucTempData<<1;
        316. }
        317. dig_hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上并且鎖存起來
        318. delay_short(1);
        319. dig_hc595_st_dr=1;
        320. delay_short(1);
        321. dig_hc595_sh_dr=0; //拉低,抗干擾就增強
        322. dig_hc595_st_dr=0;
        323. dig_hc595_ds_dr=0;
        324. }
        325. //LED燈的74HC595驅動函數
        326. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
        327. {
        328. unsigned char i;
        329. unsigned char ucTempData;
        330. hc595_sh_dr=0;
        331. hc595_st_dr=0;
        332. ucTempData=ucLedStatusTemp16_09;//先送高8位
        333. for(i=0;i<8;i++)
        334. {
        335. if(ucTempData>=0x80)hc595_ds_dr=1;
        336. else hc595_ds_dr=0;
        337. hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
        338. delay_short(1);
        339. hc595_sh_dr=1;
        340. delay_short(1);
        341. ucTempData=ucTempData<<1;
        342. }
        343. ucTempData=ucLedStatusTemp08_01;//再先送低8位
        344. for(i=0;i<8;i++)
        345. {
        346. if(ucTempData>=0x80)hc595_ds_dr=1;
        347. else hc595_ds_dr=0;
        348. hc595_sh_dr=0; //SH引腳的上升沿把數據送入寄存器
        349. delay_short(1);
        350. hc595_sh_dr=1;
        351. delay_short(1);
        352. ucTempData=ucTempData<<1;
        353. }
        354. hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到74HC595的輸出引腳上并且鎖存起來
        355. delay_short(1);
        356. hc595_st_dr=1;
        357. delay_short(1);
        358. hc595_sh_dr=0; //拉低,抗干擾就增強
        359. hc595_st_dr=0;
        360. hc595_ds_dr=0;
        361. }
        362. void key_scan()//按鍵掃描函數 放在定時中斷里
        363. {
        364. switch(ucKeyStep)
        365. {
        366. case 1: //按鍵掃描輸出第ucRowRecord列低電平
        367. if(ucRowRecord==1)//第一列輸出低電平
        368. {
        369. key_dr1=0;
        370. key_dr2=1;
        371. key_dr3=1;
        372. key_dr4=1;
        373. }
        374. else if(ucRowRecord==2)//第二列輸出低電平
        375. {
        376. key_dr1=1;
        377. key_dr2=0;
        378. key_dr3=1;
        379. key_dr4=1;
        380. }
        381. else if(ucRowRecord==3)//第三列輸出低電平
        382. {
        383. key_dr1=1;
        384. key_dr2=1;
        385. key_dr3=0;
        386. key_dr4=1;
        387. }
        388. else //第四列輸出低電平
        389. {
        390. key_dr1=1;
        391. key_dr2=1;
        392. key_dr3=1;
        393. key_dr4=0;
        394. }
        395. uiKeyTimeCnt=0;//延時計數器清零
        396. ucKeyStep++; //切換到下一個運行步驟
        397. break;
        398. case 2: //此處的小延時用來等待剛才列輸出信號穩定,再判斷輸入信號。不是去抖動延時。
        399. uiKeyTimeCnt++;
        400. if(uiKeyTimeCnt>1)
        401. {
        402. uiKeyTimeCnt=0;
        403. ucKeyStep++; //切換到下一個運行步驟
        404. }
        405. break;
        406. case 3:
        407. if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==1)
        408. {
        409. ucKeyStep=1;//如果沒有按鍵按下,返回到第一個運行步驟重新開始掃描
        410. ucKeyLock=0;//按鍵自鎖標志清零
        411. uiKeyTimeCnt=0; //按鍵去抖動延時計數器清零,此行非常巧妙
        412. ucRowRecord++;//輸出下一列
        413. if(ucRowRecord>4)
        414. {
        415. ucRowRecord=1; //依次輸出完四列之后,繼續從第一列開始輸出低電平
        416. }
        417. }
        418. else if(ucKeyLock==0)//有按鍵按下,且是第一次觸發
        419. {
        420. if(key_sr1==0&&key_sr2==1&&key_sr3==1&&key_sr4==1)
        421. {
        422. uiKeyTimeCnt++;//去抖動延時計數器
        423. if(uiKeyTimeCnt>const_key_time)
        424. {
        425. uiKeyTimeCnt=0;
        426. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
        427. if(ucRowRecord==1)//第一列輸出低電平
        428. {
        429. ucKeySec=1;//觸發1號鍵 對應朱兆祺學習板的S1鍵
        430. }
        431. else if(ucRowRecord==2)//第二列輸出低電平
        432. {
        433. ucKeySec=2;//觸發2號鍵 對應朱兆祺學習板的S2鍵
        434. }
        435. else if(ucRowRecord==3)//第三列輸出低電平
        436. {
        437. ucKeySec=3;//觸發3號鍵 對應朱兆祺學習板的S3鍵
        438. }
        439. else //第四列輸出低電平
        440. {
        441. ucKeySec=4;//觸發4號鍵 對應朱兆祺學習板的S4鍵
        442. }
        443. }
        444. }
        445. else if(key_sr1==1&&key_sr2==0&&key_sr3==1&&key_sr4==1)
        446. {
        447. uiKeyTimeCnt++;//去抖動延時計數器
        448. if(uiKeyTimeCnt>const_key_time)
        449. {
        450. uiKeyTimeCnt=0;
        451. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
        452. if(ucRowRecord==1)//第一列輸出低電平
        453. {
        454. ucKeySec=5;//觸發5號鍵 對應朱兆祺學習板的S5鍵
        455. }
        456. else if(ucRowRecord==2)//第二列輸出低電平
        457. {
        458. ucKeySec=6;//觸發6號鍵 對應朱兆祺學習板的S6鍵
        459. }
        460. else if(ucRowRecord==3)//第三列輸出低電平
        461. {
        462. ucKeySec=7;//觸發7號鍵 對應朱兆祺學習板的S7鍵
        463. }
        464. else //第四列輸出低電平
        465. {
        466. ucKeySec=8;//觸發8號鍵 對應朱兆祺學習板的S8鍵
        467. }
        468. }
        469. }
        470. else if(key_sr1==1&&key_sr2==1&&key_sr3==0&&key_sr4==1)
        471. {
        472. uiKeyTimeCnt++;//去抖動延時計數器
        473. if(uiKeyTimeCnt>const_key_time)
        474. {
        475. uiKeyTimeCnt=0;
        476. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
        477. if(ucRowRecord==1)//第一列輸出低電平
        478. {
        479. ucKeySec=9;//觸發9號鍵 對應朱兆祺學習板的S9鍵
        480. }
        481. else if(ucRowRecord==2)//第二列輸出低電平
        482. {
        483. ucKeySec=10;//觸發10號鍵 對應朱兆祺學習板的S10鍵
        484. }
        485. else if(ucRowRecord==3)//第三列輸出低電平
        486. {
        487. ucKeySec=11;//觸發11號鍵 對應朱兆祺學習板的S11鍵
        488. }
        489. else //第四列輸出低電平
        490. {
        491. ucKeySec=12;//觸發12號鍵 對應朱兆祺學習板的S12鍵
        492. }
        493. }
        494. }
        495. else if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==0)
        496. {
        497. uiKeyTimeCnt++;//去抖動延時計數器
        498. if(uiKeyTimeCnt>const_key_time)
        499. {
        500. uiKeyTimeCnt=0;
        501. ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
        502. if(ucRowRecord==1)//第一列輸出低電平
        503. {
        504. ucKeySec=13;//觸發13號鍵 對應朱兆祺學習板的S13鍵
        505. }
        506. else if(ucRowRecord==2)//第二列輸出低電平
        507. {
        508. ucKeySec=14;//觸發14號鍵 對應朱兆祺學習板的S14鍵
        509. }
        510. else if(ucRowRecord==3)//第三列輸出低電平
        511. {
        512. ucKeySec=15;//觸發15號鍵 對應朱兆祺學習板的S15鍵
        513. }
        514. else //第四列輸出低電平
        515. {
        516. ucKeySec=16;//觸發16號鍵 對應朱兆祺學習板的S16鍵
        517. }
        518. }
        519. }
        520. }
        521. break;
        522. }
        523. }
        524. /* 注釋三:
        525. *按鍵服務程序操作的精髓在于根據當前系統處于什么窗口下就執行什么操作。
        526. *緊緊圍繞著不同的窗口ucWd來執行不同的操作。
        527. */
        528. void key_service() //第三區 放在定時中斷里的按鍵服務應用程序
        529. {
        530. switch(ucKeySec) //按鍵服務狀態切換
        531. {
        532. case 1:// 1號鍵 對應朱兆祺學習板的S1鍵紅棋加分 按鍵
        533. switch(ucWd)//本程序最核心的變量ucWd
        534. {
        535. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        536. uiRedTotal=uiRedTotal+60;//加紅棋分的時間,此處60秒代表一分
        537. if(uiRedTotal>5940)
        538. {
        539. uiRedTotal=5940;
        540. }
        541. uiRedTotal=uiRedTotal-(uiRedTotal%60);//去秒取整分
        542. ucDisplayUpdate=1;//更新顯示
        543. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        544. break;
        545. case 2://窗口2,代表黑棋正在運行中的狀態
        546. break;
        547. case 3://窗口3,代表黑棋在中途暫停的狀態
        548. break;
        549. case 4://窗口4,代表紅棋正在運行中的狀態
        550. break;
        551. case 5://窗口5,代表紅棋在中途暫停的狀態
        552. break;
        553. }
        554. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        555. break;
        556. case 2:// 2號鍵 對應朱兆祺學習板的S2鍵紅棋減分 按鍵
        557. switch(ucWd)//本程序最核心的變量ucWd
        558. {
        559. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        560. if(uiRedTotal>=60)
        561. {
        562. uiRedTotal=uiRedTotal-60;//減紅棋分的時間,此處60秒代表一分
        563. }
        564. uiRedTotal=uiRedTotal-(uiRedTotal%60);//去秒取整分
        565. ucDisplayUpdate=1;//更新顯示
        566. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        567. break;
        568. case 2://窗口2,代表黑棋正在運行中的狀態
        569. break;
        570. case 3://窗口3,代表黑棋在中途暫停的狀態
        571. break;
        572. case 4://窗口4,代表紅棋正在運行中的狀態
        573. break;
        574. case 5://窗口5,代表紅棋在中途暫停的狀態
        575. break;
        576. }
        577. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        578. break;
        579. case 3:// 3號鍵 對應朱兆祺學習板的S3鍵黑棋加分 按鍵
        580. switch(ucWd)//本程序最核心的變量ucWd
        581. {
        582. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        583. uiBlackTotal=uiBlackTotal+60;//加黑棋分的時間,此處60秒代表一分
        584. if(uiBlackTotal>5940)
        585. {
        586. uiBlackTotal=5940;
        587. }
        588. uiBlackTotal=uiBlackTotal-(uiBlackTotal%60);//去秒取整分
        589. ucDisplayUpdate=1;//更新顯示
        590. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        591. break;
        592. case 2://窗口2,代表黑棋正在運行中的狀態
        593. break;
        594. case 3://窗口3,代表黑棋在中途暫停的狀態
        595. break;
        596. case 4://窗口4,代表紅棋正在運行中的狀態
        597. break;
        598. case 5://窗口5,代表紅棋在中途暫停的狀態
        599. break;
        600. }
        601. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        602. break;
        603. case 4:// 4號鍵 對應朱兆祺學習板的S4鍵黑棋減分 按鍵
        604. switch(ucWd)//本程序最核心的變量ucWd
        605. {
        606. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        607. if(uiBlackTotal>=60)
        608. {
        609. uiBlackTotal=uiBlackTotal-60;//減黑棋分的時間,此處60秒代表一分
        610. }
        611. uiBlackTotal=uiBlackTotal-(uiBlackTotal%60);//去秒取整分
        612. ucDisplayUpdate=1;//更新顯示
        613. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        614. break;
        615. case 2://窗口2,代表黑棋正在運行中的狀態
        616. break;
        617. case 3://窗口3,代表黑棋在中途暫停的狀態
        618. break;
        619. case 4://窗口4,代表紅棋正在運行中的狀態
        620. break;
        621. case 5://窗口5,代表紅棋在中途暫停的狀態
        622. break;
        623. }
        624. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        625. break;
        626. case 5:// 5號鍵 對應朱兆祺學習板的S5鍵
        627. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        628. break;
        629. case 6:// 6號鍵 對應朱兆祺學習板的S6鍵中途暫停和啟動按鍵
        630. switch(ucWd)//本程序最核心的變量ucWd
        631. {
        632. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        633. break;
        634. case 2://窗口2,代表黑棋正在運行中的狀態
        635. ucRedFlag=0; //暫停計時
        636. ucBlackFlag=0;//暫停計時
        637. ucWd=3; //切換到黑棋中途暫停的狀態
        638. ucDisplayUpdate=1;//更新顯示
        639. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        640. break;
        641. case 3://窗口3,代表黑棋在中途暫停的狀態
        642. ucRedFlag=0; //紅棋暫停計時
        643. ucBlackFlag=1; //黑棋繼續計時
        644. ucWd=2; //切換到黑棋正在運行中的狀態
        645. ucDisplayUpdate=1;//更新顯示
        646. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        647. break;
        648. case 4://窗口4,代表紅棋正在運行中的狀態
        649. ucRedFlag=0; //暫停計時
        650. ucBlackFlag=0;//暫停計時
        651. ucWd=5; //切換到紅棋中途暫停的狀態
        652. ucDisplayUpdate=1;//更新顯示
        653. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        654. break;
        655. case 5://窗口5,代表紅棋在中途暫停的狀態
        656. ucRedFlag=1; //紅棋繼續計時
        657. ucBlackFlag=0; //黑棋暫停計時
        658. ucWd=4; //切換到紅棋正在運行中的狀態
        659. ucDisplayUpdate=1;//更新顯示
        660. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        661. break;
        662. }
        663. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        664. break;
        665. case 7:// 7號鍵 對應朱兆祺學習板的S7鍵在第一個窗口下,把計時器的值恢復為開機時的默認值20分鐘
        666. switch(ucWd)//本程序最核心的變量ucWd
        667. {
        668. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        669. uiRedTotal=1200; //紅棋的總時間
        670. uiBlackTotal=1200;//黑棋的總時間
        671. ucDisplayUpdate=1;//更新顯示
        672. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        673. break;
        674. case 2://窗口2,代表黑棋正在運行中的狀態
        675. break;
        676. case 3://窗口3,代表黑棋在中途暫停的狀態
        677. break;
        678. case 4://窗口4,代表紅棋正在運行中的狀態
        679. break;
        680. case 5://窗口5,代表紅棋在中途暫停的狀態
        681. break;
        682. }
        683. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        684. break;
        685. case 8:// 8號鍵 對應朱兆祺學習板的S8鍵
        686. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        687. break;
        688. case 9:// 9號鍵 對應朱兆祺學習板的S9鍵
        689. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        690. break;
        691. case 10:// 10號鍵 對應朱兆祺學習板的S10鍵
        692. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        693. break;
        694. case 11:// 11號鍵 對應朱兆祺學習板的S11鍵
        695. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        696. break;
        697. case 12:// 12號鍵 對應朱兆祺學習板的S12鍵
        698. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        699. break;
        700. case 13:// 13號鍵 對應朱兆祺學習板的S13鍵紅棋按下
        701. switch(ucWd)//本程序最核心的變量ucWd
        702. {
        703. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        704. ucRedFlag=0; //紅棋暫停計時
        705. ucBlackFlag=1;//黑棋繼續計時
        706. ucWd=2; //切換到黑棋正在運行中的狀態
        707. ucDisplayUpdate=1;//更新顯示
        708. break;
        709. case 2://窗口2,代表黑棋正在運行中的狀態
        710. break;
        711. case 3://窗口3,代表黑棋在中途暫停的狀態
        712. break;
        713. case 4://窗口4,代表紅棋正在運行中的狀態
        714. ucRedFlag=0; //紅棋暫停計時
        715. ucBlackFlag=1;//黑棋繼續計時
        716. ucWd=2; //切換到黑棋正在運行中的狀態
        717. ucDisplayUpdate=1;//更新顯示
        718. break;
        719. case 5://窗口5,代表紅棋在中途暫停的狀態
        720. break;
        721. }
        722. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        723. break;
        724. case 14:// 14號鍵 對應朱兆祺學習板的S14鍵
        725. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        726. break;
        727. case 15:// 15號鍵 對應朱兆祺學習板的S15鍵
        728. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        729. break;
        730. case 16:// 16號鍵 對應朱兆祺學習板的S16鍵 黑棋按下
        731. switch(ucWd)//本程序最核心的變量ucWd
        732. {
        733. case 1://窗口1,代表剛上電,完成或者復位后的狀態
        734. ucRedFlag=1; //紅棋繼續計時
        735. ucBlackFlag=0;//黑棋暫停計時
        736. ucWd=4; //切換到紅棋正在運行中的狀態
        737. ucDisplayUpdate=1;//更新顯示
        738. break;
        739. case 2://窗口2,代表黑棋正在運行中的狀態
        740. ucRedFlag=1; //紅棋繼續計時
        741. ucBlackFlag=0;//黑棋暫停計時
        742. ucWd=4; //切換到紅棋正在運行中的狀態
        743. ucDisplayUpdate=1;//更新顯示
        744. break;
        745. case 3://窗口3,代表黑棋在中途暫停的狀態
        746. break;
        747. case 4://窗口4,代表紅棋正在運行中的狀態
        748. break;
        749. case 5://窗口5,代表紅棋在中途暫停的狀態
        750. break;
        751. }
        752. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        753. break;
        754. }
        755. }
        756. void T0_time() interrupt 1
        757. {
        758. TF0=0;//清除中斷標志
        759. TR0=0; //關中斷
        760. key_scan(); //放在定時中斷里的按鍵掃描函數
        761. time_service();//放在定時中斷里的時間應用程序
        762. if(uiVoiceCnt!=0)
        763. {
        764. uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
        765. beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
        766. }
        767. else
        768. {
        769. ; //此處多加一個空指令,想維持跟if括號語句的數量對稱,都是兩條指令。不加也可以。
        770. beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
        771. }
        772. display_drive();//放在定時中斷里的數碼管驅動函數
        773. /* 注釋四:
        774. *注意,此處的重裝初始值不能太大,否則動態掃描數碼管的速度就不夠。我把原來常用的2000改成了500。
        775. */
        776. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
        777. TL0=0x0b;
        778. TR0=1;//開中斷
        779. }
        780. void delay_short(unsigned int uiDelayShort)
        781. {
        782. unsigned int i;
        783. for(i=0;i
        784. {
        785. ; //一個分號相當于執行一條空語句
        786. }
        787. }
        788. void delay_long(unsigned int uiDelayLong)
        789. {
        790. unsigned int i;
        791. unsigned int j;
        792. for(i=0;i
        793. {
        794. for(j=0;j<500;j++)//內嵌循環的空指令數量
        795. {
        796. ; //一個分號相當于執行一條空語句
        797. }
        798. }
        799. }
        800. void initial_myself()//第一區 初始化單片機
        801. {
        802. led_dr=1;
        803. beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時不叫。
        804. hc595_drive(0x00,0x00);
        805. TMOD=0x01;//設置定時器0為工作方式1
        806. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
        807. TL0=0x0b;
        808. }
        809. void initial_peripheral() //第二區 初始化外圍
        810. {
        811. EA=1; //開總中斷
        812. ET0=1; //允許定時中斷
        813. TR0=1; //啟動定時中斷
        814. }


        總結陳詞:
        這節講了象棋比賽專用計時器的項目程序。為了繼續加深讀者理解按鍵和顯示是如何有規律關聯起來的,下節會繼續講一個相關的小項目程序。欲知詳情,請聽下回分解-----帶數碼管顯示的加法簡易計算器。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 贵州省| 定安县| 阿巴嘎旗| 昭觉县| 龙门县| 德化县| 新邵县| 五台县| 邵阳市| 巴青县| 南丰县| 辉县市| 华容县| 龙州县| 屏山县| 黔西县| 获嘉县| 平度市| 林甸县| 本溪市| 尚义县| 翁牛特旗| 伊金霍洛旗| 内江市| 永州市| 舟山市| 昌图县| 潼南县| 怀安县| 汽车| 屏南县| 盐池县| 新干县| 安宁市| 灵寿县| 德安县| 安化县| 新邵县| 遂宁市| 广宁县| 安乡县|