新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應用 > 第48節(jié):利用DS1302做一個實時時鐘

        第48節(jié):利用DS1302做一個實時時鐘

        作者: 時間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
        開場白:
        DS1302有兩路獨立電源輸入,我們只要在其中一路電源上掛一個紐扣電池就可以實現(xiàn)掉電時鐘繼續(xù)跑的功能,紐扣電池作為備用電源必須比主電源的電壓低一點。DS1302還給我們預留了一片RAM區(qū),我們可以把一些數(shù)據(jù)存入到DS1302,只要DS1302的電池有電,那么它就相當于一個EEPROM。這個RAM區(qū)有什么用呢?因為RAM區(qū)的數(shù)據(jù)只要一掉電,所有的數(shù)據(jù)都會變成0x00或者0xff,也就是數(shù)據(jù)掉電會丟失,我們可以利用這個特點,可以在里面存入標志位數(shù)據(jù),一旦發(fā)現(xiàn)這個數(shù)據(jù)改變了,就知道時鐘的數(shù)據(jù)需要重新設(shè)置過,或者說明電池沒電了。
        在移植DS1302驅(qū)動程序中,有一個地方最容易出錯,就是DS1302芯片的數(shù)據(jù)線DIO。我們編程時要特別留意這個IO口什么時候作為數(shù)據(jù)輸入,什么時候作為數(shù)據(jù)輸出,以便及時更改方向寄存器。對于51單片機,IO口在讀取數(shù)據(jù)之前,要先置1。
        這一節(jié)要教會大家六個知識點:
        第一個:DS1302做實時時鐘時,修改時間和讀取時間的常見程序框架。
        第二個:如何編寫監(jiān)控備用電池電量耗盡的監(jiān)控程序。
        第三個:在往DS1302寫入數(shù)據(jù)修改時間之前,必須注意調(diào)整一下“日”的變量,因為每個月的最大天數(shù)是不一樣的,有的一個月28天,有的一個月29天,有的一個月30天,有的一個月31天。
        第四個:本程序第一次出現(xiàn)了電平按鍵,跟之前講的下降沿按鍵不一樣,請留意我是何如用軟件濾波的,以及它具體的實現(xiàn)代碼。
        第五個:本程序第一次出現(xiàn)了一個按鍵按下去后,如果不松手就會觸發(fā)兩次事件,第一次是短按,第二次是長按3秒。請留意我是如何在之前的按鍵上略做修改就實現(xiàn)此功能的具體代碼。
        第六個:繼續(xù)加深了解按鍵與顯示是如何緊密關(guān)聯(lián)起來的程序框架。

        具體內(nèi)容,請看源代碼講解。

        (1) 硬件平臺.
        基于朱兆祺51單片機學習板
        舊版的朱兆祺51學習板在硬件上有一個bug,DS1302芯片附近的R43,R42兩個電阻應該去掉,并且把R41的電阻換成0歐姆的電阻,或者直接短接起來。新版的朱兆祺51學習板已經(jīng)改過來了。

        (2)實現(xiàn)功能:
        本程序有2兩個窗口。
        第1個窗口顯示日期。顯示格式“年-月-日”。注意中間有“-”分開。
        第2個窗口顯示時間。顯示格式“時 分 秒”。注意中間沒“-”,只有空格分開。
        系統(tǒng)上電后,默認顯示第2個窗口,實時顯示動態(tài)的“時 分 秒”時間。此時按下S13按鍵不松手就會切換到顯示日期的第1個窗口。松手后自動切換回第2個顯示動態(tài)時間的窗口。
        需要更改時間的時候,長按S9按鍵不松手超過3秒后,系統(tǒng)將進入修改時間的狀態(tài),切換到第1個日期窗口,并且顯示“年”的兩位數(shù)碼管會閃爍,此時可以按S1或者S5加減按鍵修改年的參數(shù),修改完年后,繼續(xù)短按S9按鍵,會切換到“月”的參數(shù)閃爍狀態(tài),只要依次不斷按下S9按鍵,就會依次切換年,月,日,時,分,秒的參數(shù)閃爍狀態(tài),最后修改完秒的參數(shù)后,系統(tǒng)會自動把我們修改設(shè)置的日期時間一次性寫入DS1302芯片內(nèi)部,達到修改日期時間的目的。
        S13是電平變化按鍵,用來切換窗口的,專門用來查看當前日期。按下S13按鍵時顯示日期窗口,松手后返回到顯示實時時間的窗口。

        本程序在使用過程中的注意事項:
        (a)第一次上電時,蜂鳴器會報警,只要DS1302芯片的備用電池電量充足,這個時候斷電再重啟一次,就不會報警了。
        (b)第一次上電時,時間沒有走動,需要重新設(shè)置一下日期時間才可以。長按S9按鍵可以進入修改日期時間的狀態(tài)。

        (3)源代碼講解如下:
        1. #include "REG52.H"
        2. #define const_dpy_time_half200//數(shù)碼管閃爍時間的半值
        3. #define const_dpy_time_all 400//數(shù)碼管閃爍時間的全值 一定要比const_dpy_time_half 大
        4. #define const_voice_short40 //蜂鳴器短叫的持續(xù)時間
        5. #define const_key_time120 //按鍵去抖動延時的時間
        6. #define const_key_time220 //按鍵去抖動延時的時間
        7. #define const_key_time320 //按鍵去抖動延時的時間
        8. #define const_key_time420 //按鍵去抖動延時的時間
        9. #define const_key_time171200//長按超過3秒的時間
        10. #define const_ds1302_0_5s200 //大概0.5秒的時間
        11. #define const_ds1302_sampling_time 360 //累計主循環(huán)次數(shù)的時間,每次刷新采樣時鐘芯片的時間
        12. #define WRITE_SECOND 0x80 //DS1302內(nèi)部的相關(guān)地址
        13. #define WRITE_MINUTE 0x82
        14. #define WRITE_HOUR 0x84
        15. #define WRITE_DATE 0x86
        16. #define WRITE_MONTH 0x88
        17. #define WRITE_YEAR 0x8C
        18. #define WRITE_CHECK 0xC2//用來檢查芯片的備用電池是否用完了的地址
        19. #define READ_CHECK 0xC3//用來檢查芯片的備用電池是否用完了的地址
        20. #define READ_SECOND 0x81
        21. #define READ_MINUTE 0x83
        22. #define READ_HOUR 0x85
        23. #define READ_DATE 0x87
        24. #define READ_MONTH 0x89
        25. #define READ_YEAR 0x8D
        26. #define WRITE_PROTECT 0x8E
        27. void initial_myself(void);
        28. void initial_peripheral(void);
        29. void delay_short(unsigned int uiDelayShort);
        30. void delay_long(unsigned int uiDelaylong);
        31. //驅(qū)動數(shù)碼管的74HC595
        32. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
        33. void display_drive(void); //顯示數(shù)碼管字模的驅(qū)動函數(shù)
        34. void display_service(void); //顯示的窗口菜單服務(wù)程序
        35. //驅(qū)動LED的74HC595
        36. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
        37. void T0_time(void);//定時中斷函數(shù)
        38. void key_service(void); //按鍵服務(wù)的應用程序
        39. void key_scan(void);//按鍵掃描函數(shù) 放在定時中斷里
        40. void ds1302_alarm_service(void); //ds1302出錯報警
        41. void ds1302_sampling(void); //ds1302采樣程序,內(nèi)部每秒鐘采集更新一次
        42. void Write1302 ( unsigned char addr, unsigned char dat );//修改時間的驅(qū)動
        43. unsigned char Read1302 ( unsigned char addr );//讀取時間的驅(qū)動
        44. unsigned char bcd_to_number(unsigned char ucBcdTemp);//BCD轉(zhuǎn)原始數(shù)值
        45. unsigned char number_to_bcd(unsigned char ucNumberTemp); //原始數(shù)值轉(zhuǎn)BCD
        46. //日調(diào)整 每個月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
        47. unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp); //日調(diào)整
        48. sbit SCLK_dr =P1^3;
        49. sbit DIO_dr_sr =P1^4;
        50. sbit DS1302_CE_dr =P1^5;
        51. sbit key_sr1=P0^0; //對應朱兆祺學習板的S1鍵
        52. sbit key_sr2=P0^1; //對應朱兆祺學習板的S5鍵
        53. sbit key_sr3=P0^2; //對應朱兆祺學習板的S9鍵
        54. sbit key_sr4=P0^3; //對應朱兆祺學習板的S13鍵
        55. sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平
        56. sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口
        57. sbit eeprom_scl_dr=P3^7; //時鐘線
        58. sbit eeprom_sda_dr_sr=P3^6; //數(shù)據(jù)的輸出線和輸入線
        59. sbit dig_hc595_sh_dr=P2^0; //數(shù)碼管的74HC595程序
        60. sbit dig_hc595_st_dr=P2^1;
        61. sbit dig_hc595_ds_dr=P2^2;
        62. sbit hc595_sh_dr=P2^3; //LED燈的74HC595程序
        63. sbit hc595_st_dr=P2^4;
        64. sbit hc595_ds_dr=P2^5;
        65. unsigned int uiSampingCnt=0; //采集Ds1302的計時器,每秒鐘更新采集一次
        66. unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號
        67. unsigned intuiKeyTimeCnt1=0; //按鍵去抖動延時計數(shù)器
        68. unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標志
        69. unsigned intuiKeyTimeCnt2=0; //按鍵去抖動延時計數(shù)器
        70. unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標志
        71. unsigned intuiKeyTimeCnt3=0; //按鍵去抖動延時計數(shù)器
        72. unsigned char ucKeyLock3=0; //按鍵觸發(fā)后自鎖的變量標志
        73. unsigned int uiKey4Cnt1=0;//在軟件濾波中,用到的變量
        74. unsigned int uiKey4Cnt2=0;
        75. unsigned char ucKey4Sr=1;//實時反映按鍵的電平狀態(tài)
        76. unsigned char ucKey4SrRecord=0; //記錄上一次按鍵的電平狀態(tài)
        77. unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續(xù)時間計數(shù)器
        78. unsigned charucVoiceLock=0;//蜂鳴器鳴叫的原子鎖
        79. unsigned char ucDigShow8;//第8位數(shù)碼管要顯示的內(nèi)容
        80. unsigned char ucDigShow7;//第7位數(shù)碼管要顯示的內(nèi)容
        81. unsigned char ucDigShow6;//第6位數(shù)碼管要顯示的內(nèi)容
        82. unsigned char ucDigShow5;//第5位數(shù)碼管要顯示的內(nèi)容
        83. unsigned char ucDigShow4;//第4位數(shù)碼管要顯示的內(nèi)容
        84. unsigned char ucDigShow3;//第3位數(shù)碼管要顯示的內(nèi)容
        85. unsigned char ucDigShow2;//第2位數(shù)碼管要顯示的內(nèi)容
        86. unsigned char ucDigShow1;//第1位數(shù)碼管要顯示的內(nèi)容
        87. unsigned char ucDigDot8;//數(shù)碼管8的小數(shù)點是否顯示的標志
        88. unsigned char ucDigDot7;//數(shù)碼管7的小數(shù)點是否顯示的標志
        89. unsigned char ucDigDot6;//數(shù)碼管6的小數(shù)點是否顯示的標志
        90. unsigned char ucDigDot5;//數(shù)碼管5的小數(shù)點是否顯示的標志
        91. unsigned char ucDigDot4;//數(shù)碼管4的小數(shù)點是否顯示的標志
        92. unsigned char ucDigDot3;//數(shù)碼管3的小數(shù)點是否顯示的標志
        93. unsigned char ucDigDot2;//數(shù)碼管2的小數(shù)點是否顯示的標志
        94. unsigned char ucDigDot1;//數(shù)碼管1的小數(shù)點是否顯示的標志
        95. unsigned char ucDigShowTemp=0; //臨時中間變量
        96. unsigned char ucDisplayDriveStep=1;//動態(tài)掃描數(shù)碼管的步驟變量
        97. unsigned char ucWd=2;//本程序的核心變量,窗口顯示變量。類似于一級菜單的變量。代表顯示不同的窗口。
        98. unsigned char ucPart=0;//本程序的核心變量,局部顯示變量。類似于二級菜單的變量。代表顯示不同的局部。
        99. unsigned char ucWd1Update=0; //窗口1更新顯示標志
        100. unsigned char ucWd2Update=1; //窗口2更新顯示標志
        101. unsigned char ucWd1Part1Update=0;//在窗口1中,局部1的更新顯示標志
        102. unsigned char ucWd1Part2Update=0; //在窗口1中,局部2的更新顯示標志
        103. unsigned char ucWd1Part3Update=0; //在窗口1中,局部3的更新顯示標志
        104. unsigned char ucWd2Part1Update=0;//在窗口2中,局部1的更新顯示標志
        105. unsigned char ucWd2Part2Update=0; //在窗口2中,局部2的更新顯示標志
        106. unsigned char ucWd2Part3Update=0; //在窗口2中,局部3的更新顯示標志
        107. unsigned charucYear=0; //原始數(shù)據(jù)
        108. unsigned charucMonth=0;
        109. unsigned charucDate=0;
        110. unsigned charucHour=0;
        111. unsigned charucMinute=0;
        112. unsigned charucSecond=0;
        113. unsigned charucYearBCD=0; //BCD碼的數(shù)據(jù)
        114. unsigned charucMonthBCD=0;
        115. unsigned charucDateBCD=0;
        116. unsigned charucHourBCD=0;
        117. unsigned charucMinuteBCD=0;
        118. unsigned charucSecondBCD=0;
        119. unsigned char ucTemp1=0;//中間過渡變量
        120. unsigned char ucTemp2=0;//中間過渡變量
        121. unsigned char ucTemp4=0;//中間過渡變量
        122. unsigned char ucTemp5=0;//中間過渡變量
        123. unsigned char ucTemp7=0;//中間過渡變量
        124. unsigned char ucTemp8=0;//中間過渡變量
        125. unsigned char ucDelayTimerLock=0; //原子鎖
        126. unsigned intuiDelayTimer=0;
        127. unsigned char ucCheckDs1302=0;//檢查Ds1302芯片是否正常
        128. unsigned char ucDs1302Error=0; //Ds1302芯片的備用電池是否用完了的報警標志
        129. unsigned char ucDs1302Lock=0;//原子鎖
        130. unsigned intuiDs1302Cnt=0; //間歇性蜂鳴器報警的計時器
        131. unsigned char ucDpyTimeLock=0; //原子鎖
        132. unsigned intuiDpyTimeCnt=0;//數(shù)碼管的閃爍計時器,放在定時中斷里不斷累加
        133. //根據(jù)原理圖得出的共陰數(shù)碼管字模表
        134. code unsigned char dig_table[]=
        135. {
        136. 0x3f,//0 序號0
        137. 0x06,//1 序號1
        138. 0x5b,//2 序號2
        139. 0x4f,//3 序號3
        140. 0x66,//4 序號4
        141. 0x6d,//5 序號5
        142. 0x7d,//6 序號6
        143. 0x07,//7 序號7
        144. 0x7f,//8 序號8
        145. 0x6f,//9 序號9
        146. 0x00,//無 序號10
        147. 0x40,//- 序號11
        148. 0x73,//P 序號12
        149. };
        150. void main()
        151. {
        152. initial_myself();
        153. delay_long(100);
        154. initial_peripheral();
        155. while(1)
        156. {
        157. key_service(); //按鍵服務(wù)的應用程序
        158. ds1302_sampling(); //ds1302采樣程序,內(nèi)部每秒鐘采集更新一次
        159. display_service(); //顯示的窗口菜單服務(wù)程序
        160. ds1302_alarm_service(); //ds1302出錯報警
        161. }
        162. }
        163. /* 注釋一:
        164. * 系統(tǒng)不用時時刻刻采集ds1302的內(nèi)部數(shù)據(jù),每隔一段時間更新采集一次就可以了。
        165. * 這個間隔時間應該小于或者等于1秒鐘的時間,否則在數(shù)碼管上看不到每秒鐘的時間變化。
        166. */
        167. void ds1302_sampling(void) //ds1302采樣程序,內(nèi)部每秒鐘采集更新一次
        168. {
        169. if(ucPart==0)//當系統(tǒng)不是處于設(shè)置日期和時間的情況下
        170. {
        171. ++uiSampingCnt;//累計主循環(huán)次數(shù)的時間
        172. if(uiSampingCnt>const_ds1302_sampling_time)//每隔一段時間就更新采集一次Ds1302數(shù)據(jù)
        173. {
        174. uiSampingCnt=0;
        175. ucYearBCD=Read1302(READ_YEAR); //讀取年
        176. ucMonthBCD=Read1302(READ_MONTH); //讀取月
        177. ucDateBCD=Read1302(READ_DATE); //讀取日
        178. ucHourBCD=Read1302(READ_HOUR); //讀取時
        179. ucMinuteBCD=Read1302(READ_MINUTE); //讀取分
        180. ucSecondBCD=Read1302(READ_SECOND); //讀取秒
        181. ucYear=bcd_to_number(ucYearBCD);//BCD轉(zhuǎn)原始數(shù)值
        182. ucMonth=bcd_to_number(ucMonthBCD);//BCD轉(zhuǎn)原始數(shù)值
        183. ucDate=bcd_to_number(ucDateBCD);//BCD轉(zhuǎn)原始數(shù)值
        184. ucHour=bcd_to_number(ucHourBCD);//BCD轉(zhuǎn)原始數(shù)值
        185. ucMinute=bcd_to_number(ucMinuteBCD);//BCD轉(zhuǎn)原始數(shù)值
        186. ucSecond=bcd_to_number(ucSecondBCD);//BCD轉(zhuǎn)原始數(shù)值
        187. ucWd2Update=1; //窗口2更新顯示時間
        188. }
        189. }
        190. }
        191. //修改ds1302時間的驅(qū)動 ,注意,此處寫入的是BCD碼,
        192. void Write1302 ( unsigned char addr, unsigned char dat )
        193. {
        194. unsigned char i,temp; //單片機驅(qū)動DS1302屬于SPI通訊方式,根據(jù)我的經(jīng)驗,不用關(guān)閉中斷
        195. DS1302_CE_dr=0; //CE引腳為低,數(shù)據(jù)傳送中止
        196. delay_short(1);
        197. SCLK_dr=0; //清零時鐘總線
        198. delay_short(1);
        199. DS1302_CE_dr = 1; //CE引腳為高,邏輯控制有效
        200. delay_short(1);
        201. //發(fā)送地址
        202. for ( i=0; i<8; i++ ) //循環(huán)8次移位
        203. {
        204. DIO_dr_sr = 0;
        205. temp = addr;
        206. if(temp&0x01)
        207. {
        208. DIO_dr_sr =1;
        209. }
        210. else
        211. {
        212. DIO_dr_sr =0;
        213. }
        214. delay_short(1);
        215. addr >>= 1; //右移一位
        216. SCLK_dr = 1;
        217. delay_short(1);
        218. SCLK_dr = 0;
        219. delay_short(1);
        220. }
        221. //發(fā)送數(shù)據(jù)
        222. for ( i=0; i<8; i++ ) //循環(huán)8次移位
        223. {
        224. DIO_dr_sr = 0;
        225. temp = dat;
        226. if(temp&0x01)
        227. {
        228. DIO_dr_sr =1;
        229. }
        230. else
        231. {
        232. DIO_dr_sr =0;
        233. }
        234. delay_short(1);
        235. dat >>= 1; //右移一位
        236. SCLK_dr = 1;
        237. delay_short(1);
        238. SCLK_dr = 0;
        239. delay_short(1);
        240. }
        241. DS1302_CE_dr = 0;
        242. delay_short(1);
        243. }
        244. //讀取Ds1302時間的驅(qū)動 ,注意,此處讀取的是BCD碼,
        245. unsigned char Read1302 ( unsigned char addr )
        246. {
        247. unsigned char i,temp,dat1;
        248. DS1302_CE_dr=0; //單片機驅(qū)動DS1302屬于SPI通訊方式,根據(jù)我的經(jīng)驗,不用關(guān)閉中斷
        249. delay_short(1);
        250. SCLK_dr=0;
        251. delay_short(1);
        252. DS1302_CE_dr = 1;
        253. delay_short(1);
        254. //發(fā)送地址
        255. for ( i=0; i<8; i++ ) //循環(huán)8次移位
        256. {
        257. DIO_dr_sr = 0;
        258. temp = addr;
        259. if(temp&0x01)
        260. {
        261. DIO_dr_sr =1;
        262. }
        263. else
        264. {
        265. DIO_dr_sr =0;
        266. }
        267. delay_short(1);
        268. addr >>= 1; //右移一位
        269. SCLK_dr = 1;
        270. delay_short(1);
        271. SCLK_dr = 0;
        272. delay_short(1);
        273. }
        274. /* 注釋二:
        275. * 51單片機IO口的特點,在讀取數(shù)據(jù)之前必須先輸出高電平,
        276. * 如果是PIC,AVR等單片機,這里應該把IO方向寄存器設(shè)置為輸入
        277. */
        278. DIO_dr_sr =1; //51單片機IO口的特點,在讀取數(shù)據(jù)之前必須先輸出高電平,
        279. temp=0;
        280. for ( i=0; i<8; i++ )
        281. {
        282. temp>>=1;
        283. if(DIO_dr_sr==1)
        284. {
        285. temp=temp+0x80;
        286. }
        287. DIO_dr_sr =1;//51單片機IO口的特點,在讀取數(shù)據(jù)之前必須先輸出高電平
        288. delay_short(1);
        289. SCLK_dr = 1;
        290. delay_short(1);
        291. SCLK_dr = 0;
        292. delay_short(1);
        293. }
        294. DS1302_CE_dr=0;
        295. delay_short(1);
        296. dat1=temp;
        297. return (dat1);
        298. }
        299. unsigned char bcd_to_number(unsigned char ucBcdTemp)//BCD轉(zhuǎn)原始數(shù)值
        300. {
        301. unsigned char ucNumberResult=0;
        302. unsigned char ucBcdTemp10;
        303. unsigned char ucBcdTemp1;
        304. ucBcdTemp10=ucBcdTemp;
        305. ucBcdTemp10=ucBcdTemp10>>4;
        306. ucBcdTemp1=ucBcdTemp;
        307. ucBcdTemp1=ucBcdTemp1&0x0f;
        308. ucNumberResult=ucBcdTemp10*10+ucBcdTemp1;
        309. return ucNumberResult;
        310. }
        311. unsigned char number_to_bcd(unsigned char ucNumberTemp) //原始數(shù)值轉(zhuǎn)BCD
        312. {
        313. unsigned char ucBcdResult=0;
        314. unsigned char ucNumberTemp10;
        315. unsigned char ucNumberTemp1;
        316. ucNumberTemp10=ucNumberTemp;
        317. ucNumberTemp10=ucNumberTemp10/10;
        318. ucNumberTemp10=ucNumberTemp10<<4;
        319. ucNumberTemp10=ucNumberTemp10&0xf0;
        320. ucNumberTemp1=ucNumberTemp;
        321. ucNumberTemp1=ucNumberTemp1%10;
        322. ucBcdResult=ucNumberTemp10|ucNumberTemp1;
        323. return ucBcdResult;
        324. }
        325. //日調(diào)整 每個月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
        326. unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp) //日調(diào)整
        327. {
        328. unsigned char ucDayResult;
        329. unsigned int uiYearTemp;
        330. unsigned int uiYearYu;
        331. ucDayResult=ucDateTemp;
        332. switch(ucMonthTemp)//根據(jù)不同的月份來修正不同的日最大值
        333. {
        334. case 2://二月份要計算是否是閏年
        335. uiYearTemp=2000+ucYearTemp;
        336. uiYearYu=uiYearTemp%4;
        337. if(uiYearYu==0) //閏年
        338. {
        339. if(ucDayResult>29)
        340. {
        341. ucDayResult=29;
        342. }
        343. }
        344. else
        345. {
        346. if(ucDayResult>28)
        347. {
        348. ucDayResult=28;
        349. }
        350. }
        351. break;
        352. case 4:
        353. case 6:
        354. case 9:
        355. case 11:
        356. if(ucDayResult>30)
        357. {
        358. ucDayResult=30;
        359. }
        360. break;
        361. }
        362. return ucDayResult;
        363. }
        364. void ds1302_alarm_service(void) //ds1302出錯報警
        365. {
        366. if(ucDs1302Error==1)//備用電池的電量用完了報警提示
        367. {
        368. if(uiDs1302Cnt>const_ds1302_0_5s)//大概0.5秒鐘蜂鳴器響一次
        369. {
        370. ucDs1302Lock=1;//原子鎖加鎖
        371. uiDs1302Cnt=0; //計時器清零
        372. ucDs1302Lock=0;//原子鎖解鎖
        373. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        374. uiVoiceCnt=const_voice_short; //蜂鳴器聲音觸發(fā),滴一聲就停。
        375. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        376. }
        377. }
        378. }
        379. void display_service(void) //顯示的窗口菜單服務(wù)程序
        380. {
        381. switch(ucWd)//本程序的核心變量,窗口顯示變量。類似于一級菜單的變量。代表顯示不同的窗口。
        382. {
        383. case 1: //顯示日期窗口的數(shù)據(jù)數(shù)據(jù)格式 NN-YY-RR 年-月-日
        384. if(ucWd1Update==1)//窗口1要全部更新顯示
        385. {
        386. ucWd1Update=0;//及時清零標志,避免一直進來掃描
        387. ucDigShow6=11;//顯示一杠"-"
        388. ucDigShow3=11;//顯示一杠"-"
        389. ucWd1Part1Update=1;//局部年更新顯示
        390. ucWd1Part2Update=1;//局部月更新顯示
        391. ucWd1Part3Update=1;//局部日更新顯示
        392. }
        393. if(ucWd1Part1Update==1)//局部年更新顯示
        394. {
        395. ucWd1Part1Update=0;
        396. ucTemp8=ucYear/10;//年
        397. ucTemp7=ucYear%10;
        398. ucDigShow8=ucTemp8; //數(shù)碼管顯示實際內(nèi)容
        399. ucDigShow7=ucTemp7;
        400. }
        401. if(ucWd1Part2Update==1)//局部月更新顯示
        402. {
        403. ucWd1Part2Update=0;
        404. ucTemp5=ucMonth/10;//月
        405. ucTemp4=ucMonth%10;
        406. ucDigShow5=ucTemp5; //數(shù)碼管顯示實際內(nèi)容
        407. ucDigShow4=ucTemp4;
        408. }
        409. if(ucWd1Part3Update==1) //局部日更新顯示
        410. {
        411. ucWd1Part3Update=0;
        412. ucTemp2=ucDate/10;//日
        413. ucTemp1=ucDate%10;
        414. ucDigShow2=ucTemp2; //數(shù)碼管顯示實際內(nèi)容
        415. ucDigShow1=ucTemp1;
        416. }
        417. //數(shù)碼管閃爍
        418. switch(ucPart)//相當于二級菜單,根據(jù)局部變量的值,使對應的參數(shù)產(chǎn)生閃爍的動態(tài)效果。
        419. {
        420. case 0://都不閃爍
        421. break;
        422. case 1://年參數(shù)閃爍
        423. if(uiDpyTimeCnt==const_dpy_time_half)
        424. {
        425. ucDigShow8=ucTemp8; //數(shù)碼管顯示實際內(nèi)容
        426. ucDigShow7=ucTemp7;
        427. }
        428. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
        429. {
        430. ucDpyTimeLock=1; //原子鎖加鎖
        431. uiDpyTimeCnt=0; //及時把閃爍記時器清零
        432. ucDpyTimeLock=0;//原子鎖解鎖
        433. ucDigShow8=10; //數(shù)碼管顯示空,什么都不顯示
        434. ucDigShow7=10;
        435. }
        436. break;
        437. case 2: //月參數(shù)閃爍
        438. if(uiDpyTimeCnt==const_dpy_time_half)
        439. {
        440. ucDigShow5=ucTemp5; //數(shù)碼管顯示實際內(nèi)容
        441. ucDigShow4=ucTemp4;
        442. }
        443. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
        444. {
        445. ucDpyTimeLock=1; //原子鎖加鎖
        446. uiDpyTimeCnt=0; //及時把閃爍記時器清零
        447. ucDpyTimeLock=0;//原子鎖解鎖
        448. ucDigShow5=10; //數(shù)碼管顯示空,什么都不顯示
        449. ucDigShow4=10;
        450. }
        451. break;
        452. case 3: //日參數(shù)閃爍
        453. if(uiDpyTimeCnt==const_dpy_time_half)
        454. {
        455. ucDigShow2=ucTemp2; //數(shù)碼管顯示實際內(nèi)容
        456. ucDigShow1=ucTemp1;
        457. }
        458. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
        459. {
        460. ucDpyTimeLock=1; //原子鎖加鎖
        461. uiDpyTimeCnt=0; //及時把閃爍記時器清零
        462. ucDpyTimeLock=0;//原子鎖解鎖
        463. ucDigShow2=10; //數(shù)碼管顯示空,什么都不顯示
        464. ucDigShow1=10;
        465. }
        466. break;
        467. }
        468. break;
        469. case 2: //顯示時間窗口的數(shù)據(jù)數(shù)據(jù)格式 SS FF MM 時 分 秒
        470. if(ucWd2Update==1)//窗口2要全部更新顯示
        471. {
        472. ucWd2Update=0;//及時清零標志,避免一直進來掃描
        473. ucDigShow6=10;//顯示空
        474. ucDigShow3=10;//顯示空
        475. ucWd2Part3Update=1;//局部時更新顯示
        476. ucWd2Part2Update=1;//局部分更新顯示
        477. ucWd2Part1Update=1;//局部秒更新顯示
        478. }
        479. if(ucWd2Part1Update==1)//局部時更新顯示
        480. {
        481. ucWd2Part1Update=0;
        482. ucTemp8=ucHour/10;//時
        483. ucTemp7=ucHour%10;
        484. ucDigShow8=ucTemp8; //數(shù)碼管顯示實際內(nèi)容
        485. ucDigShow7=ucTemp7;
        486. }
        487. if(ucWd2Part2Update==1)//局部分更新顯示
        488. {
        489. ucWd2Part2Update=0;
        490. ucTemp5=ucMinute/10;//分
        491. ucTemp4=ucMinute%10;
        492. ucDigShow5=ucTemp5; //數(shù)碼管顯示實際內(nèi)容
        493. ucDigShow4=ucTemp4;
        494. }
        495. if(ucWd2Part3Update==1) //局部秒更新顯示
        496. {
        497. ucWd2Part3Update=0;
        498. ucTemp2=ucSecond/10;//秒
        499. ucTemp1=ucSecond%10;
        500. ucDigShow2=ucTemp2; //數(shù)碼管顯示實際內(nèi)容
        501. ucDigShow1=ucTemp1;
        502. }
        503. //數(shù)碼管閃爍
        504. switch(ucPart)//相當于二級菜單,根據(jù)局部變量的值,使對應的參數(shù)產(chǎn)生閃爍的動態(tài)效果。
        505. {
        506. case 0://都不閃爍
        507. break;
        508. case 1://時參數(shù)閃爍
        509. if(uiDpyTimeCnt==const_dpy_time_half)
        510. {
        511. ucDigShow8=ucTemp8; //數(shù)碼管顯示實際內(nèi)容
        512. ucDigShow7=ucTemp7;
        513. }
        514. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
        515. {
        516. ucDpyTimeLock=1; //原子鎖加鎖
        517. uiDpyTimeCnt=0; //及時把閃爍記時器清零
        518. ucDpyTimeLock=0;//原子鎖解鎖
        519. ucDigShow8=10; //數(shù)碼管顯示空,什么都不顯示
        520. ucDigShow7=10;
        521. }
        522. break;
        523. case 2: //分參數(shù)閃爍
        524. if(uiDpyTimeCnt==const_dpy_time_half)
        525. {
        526. ucDigShow5=ucTemp5; //數(shù)碼管顯示實際內(nèi)容
        527. ucDigShow4=ucTemp4;
        528. }
        529. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
        530. {
        531. ucDpyTimeLock=1; //原子鎖加鎖
        532. uiDpyTimeCnt=0; //及時把閃爍記時器清零
        533. ucDpyTimeLock=0;//原子鎖解鎖
        534. ucDigShow5=10; //數(shù)碼管顯示空,什么都不顯示
        535. ucDigShow4=10;
        536. }
        537. break;
        538. case 3: //秒?yún)?shù)閃爍
        539. if(uiDpyTimeCnt==const_dpy_time_half)
        540. {
        541. ucDigShow2=ucTemp2; //數(shù)碼管顯示實際內(nèi)容
        542. ucDigShow1=ucTemp1;
        543. }
        544. else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all一定要比const_dpy_time_half 大
        545. {
        546. ucDpyTimeLock=1; //原子鎖加鎖
        547. uiDpyTimeCnt=0; //及時把閃爍記時器清零
        548. ucDpyTimeLock=0;//原子鎖解鎖
        549. ucDigShow2=10; //數(shù)碼管顯示空,什么都不顯示
        550. ucDigShow1=10;
        551. }
        552. break;
        553. }
        554. break;
        555. }
        556. }
        557. void key_scan(void)//按鍵掃描函數(shù) 放在定時中斷里
        558. {
        559. if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        560. {
        561. ucKeyLock1=0; //按鍵自鎖標志清零
        562. uiKeyTimeCnt1=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
        563. }
        564. else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
        565. {
        566. uiKeyTimeCnt1++; //累加定時中斷次數(shù)
        567. if(uiKeyTimeCnt1>const_key_time1)
        568. {
        569. uiKeyTimeCnt1=0;
        570. ucKeyLock1=1;//自鎖按鍵置位,避免一直觸發(fā)
        571. ucKeySec=1; //觸發(fā)1號鍵
        572. }
        573. }
        574. if(key_sr2==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        575. {
        576. ucKeyLock2=0; //按鍵自鎖標志清零
        577. uiKeyTimeCnt2=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
        578. }
        579. else if(ucKeyLock2==0)//有按鍵按下,且是第一次被按下
        580. {
        581. uiKeyTimeCnt2++; //累加定時中斷次數(shù)
        582. if(uiKeyTimeCnt2>const_key_time2)
        583. {
        584. uiKeyTimeCnt2=0;
        585. ucKeyLock2=1;//自鎖按鍵置位,避免一直觸發(fā)
        586. ucKeySec=2; //觸發(fā)2號鍵
        587. }
        588. }
        589. /* 注釋三:
        590. * 注意,此處把一個按鍵的短按和長按的功能都實現(xiàn)了。
        591. */
        592. if(key_sr3==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        593. {
        594. ucKeyLock3=0; //按鍵自鎖標志清零
        595. uiKeyTimeCnt3=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
        596. }
        597. else if(ucKeyLock3==0)//有按鍵按下,且是第一次被按下
        598. {
        599. uiKeyTimeCnt3++; //累加定時中斷次數(shù)
        600. if(uiKeyTimeCnt3>const_key_time3)
        601. {
        602. uiKeyTimeCnt3=0;
        603. ucKeyLock3=1;//自鎖按鍵置位,避免一直觸發(fā)
        604. ucKeySec=3; //短按觸發(fā)3號鍵
        605. }
        606. }
        607. else if(uiKeyTimeCnt3
        608. {
        609. uiKeyTimeCnt3++; //累加定時中斷次數(shù)
        610. if(uiKeyTimeCnt3==const_key_time17)//等于3秒鐘,觸發(fā)17號長按按鍵
        611. {
        612. ucKeySec=17; //長按3秒觸發(fā)17號鍵
        613. }
        614. }
        615. /* 注釋四:
        616. * 注意,此處是電平按鍵的濾波抗干擾處理
        617. */
        618. if(key_sr4==1)//對應朱兆祺學習板的S13鍵
        619. {
        620. uiKey4Cnt1=0; //在軟件濾波中,非常關(guān)鍵的語句!!!類似按鍵去抖動程序的及時清零
        621. uiKey4Cnt2++; //類似獨立按鍵去抖動的軟件抗干擾處理
        622. if(uiKey4Cnt2>const_key_time4)
        623. {
        624. uiKey4Cnt2=0;
        625. ucKey4Sr=1;//實時反映按鍵松手時的電平狀態(tài)
        626. }
        627. }
        628. else
        629. {
        630. uiKey4Cnt2=0; //在軟件濾波中,非常關(guān)鍵的語句!!!類似按鍵去抖動程序的及時清零
        631. uiKey4Cnt1++;
        632. if(uiKey4Cnt1>const_key_time4)
        633. {
        634. uiKey4Cnt1=0;
        635. ucKey4Sr=0;//實時反映按鍵按下時的電平狀態(tài)
        636. }
        637. }
        638. }
        639. void key_service(void) //按鍵服務(wù)的應用程序
        640. {
        641. switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
        642. {
        643. case 1:// 加按鍵 對應朱兆祺學習板的S1鍵
        644. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
        645. {
        646. case 1:
        647. switch(ucPart) //在不同的局部變量下,相當于二級菜單
        648. {
        649. case 1://年
        650. ucYear++;
        651. if(ucYear>99)
        652. {
        653. ucYear=99;
        654. }
        655. ucWd1Part1Update=1;//更新顯示
        656. break;
        657. case 2: //月
        658. ucMonth++;
        659. if(ucMonth>12)
        660. {
        661. ucMonth=12;
        662. }
        663. ucWd1Part2Update=1;//更新顯示
        664. break;
        665. case 3: //日
        666. ucDate++;
        667. if(ucDate>31)
        668. {
        669. ucDate=31;
        670. }
        671. ucWd1Part3Update=1;//更新顯示
        672. break;
        673. }
        674. break;
        675. case 2:
        676. switch(ucPart) //在不同的局部變量下,相當于二級菜單
        677. {
        678. case 1://時
        679. ucHour++;
        680. if(ucHour>23)
        681. {
        682. ucHour=23;
        683. }
        684. ucWd2Part1Update=1;//更新顯示
        685. break;
        686. case 2: //分
        687. ucMinute++;
        688. if(ucMinute>59)
        689. {
        690. ucMinute=59;
        691. }
        692. ucWd2Part2Update=1;//更新顯示
        693. break;
        694. case 3: //秒
        695. ucSecond++;
        696. if(ucSecond>59)
        697. {
        698. ucSecond=59;
        699. }
        700. ucWd2Part3Update=1;//更新顯示
        701. break;
        702. }
        703. break;
        704. }
        705. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        706. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
        707. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        708. ucKeySec=0;//響應按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
        709. break;
        710. case 2:// 減按鍵 對應朱兆祺學習板的S5鍵
        711. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
        712. {
        713. case 1:
        714. switch(ucPart) //在不同的局部變量下,相當于二級菜單
        715. {
        716. case 1://年
        717. ucYear--;
        718. if(ucYear>99)
        719. {
        720. ucYear=0;
        721. }
        722. ucWd1Part1Update=1;//更新顯示
        723. break;
        724. case 2: //月
        725. ucMonth--;
        726. if(ucMonth<1)
        727. {
        728. ucMonth=1;
        729. }
        730. ucWd1Part2Update=1;//更新顯示
        731. break;
        732. case 3: //日
        733. ucDate--;
        734. if(ucDate<1)
        735. {
        736. ucDate=1;
        737. }
        738. ucWd1Part3Update=1;//更新顯示
        739. break;
        740. }
        741. break;
        742. case 2:
        743. switch(ucPart) //在不同的局部變量下,相當于二級菜單
        744. {
        745. case 1://時
        746. ucHour--;
        747. if(ucHour>23)
        748. {
        749. ucHour=0;
        750. }
        751. ucWd2Part1Update=1;//更新顯示
        752. break;
        753. case 2: //分
        754. ucMinute--;
        755. if(ucMinute>59)
        756. {
        757. ucMinute=0;
        758. }
        759. ucWd2Part2Update=1;//更新顯示
        760. break;
        761. case 3: //秒
        762. ucSecond--;
        763. if(ucSecond>59)
        764. {
        765. ucSecond=0;
        766. }
        767. ucWd2Part3Update=1;//更新顯示
        768. break;
        769. }
        770. break;
        771. }
        772. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        773. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
        774. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        775. ucKeySec=0;//響應按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
        776. break;
        777. case 3://短按設(shè)置按鍵 對應朱兆祺學習板的S9鍵
        778. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
        779. {
        780. case 1:
        781. ucPart++;
        782. if(ucPart>3)
        783. {
        784. ucPart=1;
        785. ucWd=2; //切換到第二個窗口,設(shè)置時分秒
        786. ucWd2Update=1;//窗口2更新顯示
        787. }
        788. ucWd1Update=1;//窗口1更新顯示
        789. break;
        790. case 2:
        791. if(ucPart>0) //在窗口2的時候,要第一次激活設(shè)置時間,必須是長按3秒才可以,這里短按激活不了第一次
        792. {
        793. ucPart++;
        794. if(ucPart>3)//設(shè)置時間結(jié)束
        795. {
        796. ucPart=0;
        797. /* 注釋五:
        798. * 每個月份的天數(shù)最大值是不一樣的,在寫入ds1302時鐘芯片內(nèi)部數(shù)據(jù)前,應該做一次調(diào)整。
        799. * 有的月份最大28天,有的月份最大29天,有的月份最大30天,有的月份最大31天,
        800. */
        801. ucDate=date_adjust(ucYear,ucMonth,ucDate); //日調(diào)整 避免日的數(shù)值在某個月份超范圍
        802. ucYearBCD=number_to_bcd(ucYear);//原始數(shù)值轉(zhuǎn)BCD
        803. ucMonthBCD=number_to_bcd(ucMonth); //原始數(shù)值轉(zhuǎn)BCD
        804. ucDateBCD=number_to_bcd(ucDate);//原始數(shù)值轉(zhuǎn)BCD
        805. ucHourBCD=number_to_bcd(ucHour);//原始數(shù)值轉(zhuǎn)BCD
        806. ucMinuteBCD=number_to_bcd(ucMinute);//原始數(shù)值轉(zhuǎn)BCD
        807. ucSecondBCD=number_to_bcd(ucSecond);//原始數(shù)值轉(zhuǎn)BCD
        808. Write1302 (WRITE_PROTECT,0X00); //禁止寫保護
        809. Write1302 (WRITE_YEAR,ucYearBCD); //年修改
        810. Write1302 (WRITE_MONTH,ucMonthBCD); //月修改
        811. Write1302 (WRITE_DATE,ucDateBCD); //日修改
        812. Write1302 (WRITE_HOUR,ucHourBCD); //小時修改
        813. Write1302 (WRITE_MINUTE,ucMinuteBCD); //分鐘修改
        814. Write1302 (WRITE_SECOND,ucSecondBCD); //秒位修改
        815. Write1302 (WRITE_PROTECT,0x80); //允許寫保護
        816. }
        817. ucWd2Update=1;//窗口2更新顯示
        818. }
        819. break;
        820. }
        821. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        822. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
        823. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        824. ucKeySec=0;//響應按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
        825. break;
        826. case 17://長按3秒設(shè)置按鍵 對應朱兆祺學習板的S9鍵
        827. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
        828. {
        829. case 2:
        830. if(ucPart==0) //處于非設(shè)置時間的狀態(tài)下,要第一次激活設(shè)置時間,必須是長按3秒才可以
        831. {
        832. ucWd=1;
        833. ucPart=1;//進入到設(shè)置日期的狀態(tài)下
        834. ucWd1Update=1;//窗口1更新顯示
        835. }
        836. break;
        837. }
        838. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        839. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
        840. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
        841. ucKeySec=0;//響應按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
        842. break;
        843. }
        844. /* 注釋六:
        845. * 注意,此處就是第一次出現(xiàn)的電平按鍵程序,跟以往的下降沿按鍵不一樣。
        846. * ucKey4Sr是經(jīng)過軟件濾波處理后,直接反應IO口電平狀態(tài)的變量.當電平發(fā)生
        847. * 變化時,就會切換到不同的顯示界面,這里多用了一個ucKey4SrRecord變量
        848. * 記錄上一次的電平狀態(tài),是為了避免一直刷新顯示。
        849. */
        850. if(ucKey4Sr!=ucKey4SrRecord)//說明S13的切換按鍵電平狀態(tài)發(fā)生變化
        851. {
        852. ucKey4SrRecord=ucKey4Sr;//及時記錄當前最新的按鍵電平狀態(tài)避免一直進來觸發(fā)
        853. if(ucKey4Sr==1) //松手后切換到顯示時間的窗口
        854. {
        855. ucWd=2; //顯示時分秒的窗口
        856. ucPart=0;//進入到非設(shè)置時間的狀態(tài)下
        857. ucWd2Update=1;//窗口2更新顯示
        858. }
        859. else//按下去切換到顯示日期的窗口
        860. {
        861. ucWd=1; //顯示年月日的窗口
        862. ucPart=0;//進入到非設(shè)置時間的狀態(tài)下
        863. ucWd1Update=1;//窗口1更新顯示
        864. }
        865. }
        866. }
        867. void display_drive(void)
        868. {
        869. //以下程序,如果加一些數(shù)組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路
        870. switch(ucDisplayDriveStep)
        871. {
        872. case 1://顯示第1位
        873. ucDigShowTemp=dig_table[ucDigShow1];
        874. if(ucDigDot1==1)
        875. {
        876. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        877. }
        878. dig_hc595_drive(ucDigShowTemp,0xfe);
        879. break;
        880. case 2://顯示第2位
        881. ucDigShowTemp=dig_table[ucDigShow2];
        882. if(ucDigDot2==1)
        883. {
        884. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        885. }
        886. dig_hc595_drive(ucDigShowTemp,0xfd);
        887. break;
        888. case 3://顯示第3位
        889. ucDigShowTemp=dig_table[ucDigShow3];
        890. if(ucDigDot3==1)
        891. {
        892. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        893. }
        894. dig_hc595_drive(ucDigShowTemp,0xfb);
        895. break;
        896. case 4://顯示第4位
        897. ucDigShowTemp=dig_table[ucDigShow4];
        898. if(ucDigDot4==1)
        899. {
        900. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        901. }
        902. dig_hc595_drive(ucDigShowTemp,0xf7);
        903. break;
        904. case 5://顯示第5位
        905. ucDigShowTemp=dig_table[ucDigShow5];
        906. if(ucDigDot5==1)
        907. {
        908. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        909. }
        910. dig_hc595_drive(ucDigShowTemp,0xef);
        911. break;
        912. case 6://顯示第6位
        913. ucDigShowTemp=dig_table[ucDigShow6];
        914. if(ucDigDot6==1)
        915. {
        916. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        917. }
        918. dig_hc595_drive(ucDigShowTemp,0xdf);
        919. break;
        920. case 7://顯示第7位
        921. ucDigShowTemp=dig_table[ucDigShow7];
        922. if(ucDigDot7==1)
        923. {
        924. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        925. }
        926. dig_hc595_drive(ucDigShowTemp,0xbf);
        927. break;
        928. case 8://顯示第8位
        929. ucDigShowTemp=dig_table[ucDigShow8];
        930. if(ucDigDot8==1)
        931. {
        932. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點
        933. }
        934. dig_hc595_drive(ucDigShowTemp,0x7f);
        935. break;
        936. }
        937. ucDisplayDriveStep++;
        938. if(ucDisplayDriveStep>8)//掃描完8個數(shù)碼管后,重新從第一個開始掃描
        939. {
        940. ucDisplayDriveStep=1;
        941. }
        942. }
        943. //數(shù)碼管的74HC595驅(qū)動函數(shù)
        944. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
        945. {
        946. unsigned char i;
        947. unsigned char ucTempData;
        948. dig_hc595_sh_dr=0;
        949. dig_hc595_st_dr=0;
        950. ucTempData=ucDigStatusTemp16_09;//先送高8位
        951. for(i=0;i<8;i++)
        952. {
        953. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
        954. else dig_hc595_ds_dr=0;
        955. dig_hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
        956. delay_short(1);
        957. dig_hc595_sh_dr=1;
        958. delay_short(1);
        959. ucTempData=ucTempData<<1;
        960. }
        961. ucTempData=ucDigStatusTemp08_01;//再先送低8位
        962. for(i=0;i<8;i++)
        963. {
        964. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
        965. else dig_hc595_ds_dr=0;
        966. dig_hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
        967. delay_short(1);
        968. dig_hc595_sh_dr=1;
        969. delay_short(1);
        970. ucTempData=ucTempData<<1;
        971. }
        972. dig_hc595_st_dr=0;//ST引腳把兩個寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來
        973. delay_short(1);
        974. dig_hc595_st_dr=1;
        975. delay_short(1);
        976. dig_hc595_sh_dr=0; //拉低,抗干擾就增強
        977. dig_hc595_st_dr=0;
        978. dig_hc595_ds_dr=0;
        979. }
        980. //LED燈的74HC595驅(qū)動函數(shù)
        981. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
        982. {
        983. unsigned char i;
        984. unsigned char ucTempData;
        985. hc595_sh_dr=0;
        986. hc595_st_dr=0;
        987. ucTempData=ucLedStatusTemp16_09;//先送高8位
        988. for(i=0;i<8;i++)
        989. {
        990. if(ucTempData>=0x80)hc595_ds_dr=1;
        991. else hc595_ds_dr=0;
        992. hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
        993. delay_short(1);
        994. hc595_sh_dr=1;
        995. delay_short(1);
        996. ucTempData=ucTempData<<1;
        997. }
        998. ucTempData=ucLedStatusTemp08_01;//再先送低8位
        999. for(i=0;i<8;i++)
        1000. {
        1001. if(ucTempData>=0x80)hc595_ds_dr=1;
        1002. else hc595_ds_dr=0;
        1003. hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
        1004. delay_short(1);
        1005. hc595_sh_dr=1;
        1006. delay_short(1);
        1007. ucTempData=ucTempData<<1;
        1008. }
        1009. hc595_st_dr=0;//ST引腳把兩個寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來
        1010. delay_short(1);
        1011. hc595_st_dr=1;
        1012. delay_short(1);
        1013. hc595_sh_dr=0; //拉低,抗干擾就增強
        1014. hc595_st_dr=0;
        1015. hc595_ds_dr=0;
        1016. }
        1017. void T0_time(void) interrupt 1 //定時中斷
        1018. {
        1019. TF0=0;//清除中斷標志
        1020. TR0=0; //關(guān)中斷
        1021. if(ucVoiceLock==0) //原子鎖判斷
        1022. {
        1023. if(uiVoiceCnt!=0)
        1024. {
        1025. uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
        1026. beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
        1027. }
        1028. else
        1029. {
        1030. ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
        1031. beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
        1032. }
        1033. }
        1034. if(ucDs1302Error>0) //EEPROM出錯
        1035. {
        1036. if(ucDs1302Lock==0)//原子鎖判斷
        1037. {
        1038. uiDs1302Cnt++;//間歇性蜂鳴器報警的計時器
        1039. }
        1040. }
        1041. if(ucDpyTimeLock==0) //原子鎖判斷
        1042. {
        1043. uiDpyTimeCnt++;//數(shù)碼管的閃爍計時器
        1044. }
        1045. key_scan(); //按鍵掃描函數(shù)
        1046. display_drive();//數(shù)碼管字模的驅(qū)動函數(shù)
        1047. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
        1048. TL0=0x0b;
        1049. TR0=1;//開中斷
        1050. }
        1051. void delay_short(unsigned int uiDelayShort)
        1052. {
        1053. unsigned int i;
        1054. for(i=0;i
        1055. {
        1056. ; //一個分號相當于執(zhí)行一條空語句
        1057. }
        1058. }
        1059. void delay_long(unsigned int uiDelayLong)
        1060. {
        1061. unsigned int i;
        1062. unsigned int j;
        1063. for(i=0;i
        1064. {
        1065. for(j=0;j<500;j++)//內(nèi)嵌循環(huán)的空指令數(shù)量
        1066. {
        1067. ; //一個分號相當于執(zhí)行一條空語句
        1068. }
        1069. }
        1070. }
        1071. void initial_myself(void)//第一區(qū) 初始化單片機
        1072. {
        1073. key_gnd_dr=0; //模擬獨立按鍵的地GND,因此必須一直輸出低電平
        1074. beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時不叫。
        1075. hc595_drive(0x00,0x00);//關(guān)閉所有經(jīng)過另外兩個74HC595驅(qū)動的LED燈
        1076. TMOD=0x01;//設(shè)置定時器0為工作方式1
        1077. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
        1078. TL0=0x0b;
        1079. }
        1080. void initial_peripheral(void) //第二區(qū) 初始化外圍
        1081. {
        1082. ucDigDot8=0; //小數(shù)點全部不顯示
        1083. ucDigDot7=0;
        1084. ucDigDot6=0;
        1085. ucDigDot5=0;
        1086. ucDigDot4=0;
        1087. ucDigDot3=0;
        1088. ucDigDot2=0;
        1089. ucDigDot1=0;
        1090. EA=1; //開總中斷
        1091. ET0=1; //允許定時中斷
        1092. TR0=1; //啟動定時中斷
        1093. /* 注釋七:
        1094. * 檢查ds1302芯片的備用電池電量是否用完了。
        1095. * 在上電的時候,在一個特定的地址里把數(shù)據(jù)讀出來,如果發(fā)現(xiàn)不等于0x5a,
        1096. * 則是因為備用電池電量用完了,導致保存在ds1302內(nèi)部RAM數(shù)據(jù)區(qū)的數(shù)據(jù)被更改,
        1097. * 與此同時,應該重新寫入一次0x5a,為下一次通電判斷做準備
        1098. */
        1099. ucCheckDs1302=Read1302(READ_CHECK); //判斷ds1302內(nèi)部的數(shù)據(jù)是否被更改
        1100. if(ucCheckDs1302!=0x5a)
        1101. {
        1102. Write1302 (WRITE_PROTECT,0X00); //禁止寫保護
        1103. Write1302 (WRITE_CHECK,0x5a); //重新寫入標志數(shù)據(jù),方便下一次更換新電池后的判斷
        1104. Write1302 (WRITE_PROTECT,0x80); //允許寫保護
        1105. ucDs1302Error=1;//表示ds1302備用電池沒電了,報警提示更換新電池
        1106. }
        1107. }

        總結(jié)陳詞:
        下一節(jié)開始講單片機驅(qū)動溫度傳感器芯片的內(nèi)容,欲知詳情,請聽下回分解-----利用DS18B20做一個溫控器。


        關(guān)鍵詞: DS1302實時時

        評論


        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 阳谷县| 兰州市| 丰镇市| 荆州市| 齐齐哈尔市| 铁岭县| 平昌县| 平遥县| 托克托县| 黎川县| 郧西县| 昌乐县| 安阳市| 景宁| 芒康县| 南充市| 凤山县| 虹口区| 大名县| 梓潼县| 平度市| 克什克腾旗| 体育| 吉首市| 济南市| 德钦县| 蓝山县| 五家渠市| 南岸区| 清徐县| 当涂县| 交城县| 翼城县| 兰考县| 堆龙德庆县| 固始县| 绵竹市| 江安县| 黄梅县| 上饶市| 安溪县|