新聞中心

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

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

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

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

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

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

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

(3)源代碼講解如下:
  1. #include "REG52.H"
  2. #define const_dpy_time_half200//數(shù)碼管閃爍時(shí)間的半值
  3. #define const_dpy_time_all 400//數(shù)碼管閃爍時(shí)間的全值 一定要比const_dpy_time_half 大
  4. #define const_voice_short40 //蜂鳴器短叫的持續(xù)時(shí)間
  5. #define const_key_time120 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  6. #define const_key_time220 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  7. #define const_key_time320 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  8. #define const_key_time420 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  9. #define const_key_time171200//長(zhǎng)按超過(guò)3秒的時(shí)間
  10. #define const_ds1302_0_5s200 //大概0.5秒的時(shí)間
  11. #define const_ds1302_sampling_time 360 //累計(jì)主循環(huán)次數(shù)的時(shí)間,每次刷新采樣時(shí)鐘芯片的時(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//用來(lái)檢查芯片的備用電池是否用完了的地址
  19. #define READ_CHECK 0xC3//用來(lái)檢查芯片的備用電池是否用完了的地址
  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ū)動(dòng)數(shù)碼管的74HC595
  32. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  33. void display_drive(void); //顯示數(shù)碼管字模的驅(qū)動(dòng)函數(shù)
  34. void display_service(void); //顯示的窗口菜單服務(wù)程序
  35. //驅(qū)動(dòng)LED的74HC595
  36. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  37. void T0_time(void);//定時(shí)中斷函數(shù)
  38. void key_service(void); //按鍵服務(wù)的應(yīng)用程序
  39. void key_scan(void);//按鍵掃描函數(shù) 放在定時(shí)中斷里
  40. void ds1302_alarm_service(void); //ds1302出錯(cuò)報(bào)警
  41. void ds1302_sampling(void); //ds1302采樣程序,內(nèi)部每秒鐘采集更新一次
  42. void Write1302 ( unsigned char addr, unsigned char dat );//修改時(shí)間的驅(qū)動(dòng)
  43. unsigned char Read1302 ( unsigned char addr );//讀取時(shí)間的驅(qū)動(dòng)
  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)整 每個(gè)月份的日最大取值不同,有的最大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; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
  52. sbit key_sr2=P0^1; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
  53. sbit key_sr3=P0^2; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S9鍵
  54. sbit key_sr4=P0^3; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S13鍵
  55. sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平
  56. sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動(dòng)IO口
  57. sbit eeprom_scl_dr=P3^7; //時(shí)鐘線
  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的計(jì)時(shí)器,每秒鐘更新采集一次
  66. unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號(hào)
  67. unsigned intuiKeyTimeCnt1=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  68. unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  69. unsigned intuiKeyTimeCnt2=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  70. unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  71. unsigned intuiKeyTimeCnt3=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  72. unsigned char ucKeyLock3=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  73. unsigned int uiKey4Cnt1=0;//在軟件濾波中,用到的變量
  74. unsigned int uiKey4Cnt2=0;
  75. unsigned char ucKey4Sr=1;//實(shí)時(shí)反映按鍵的電平狀態(tài)
  76. unsigned char ucKey4SrRecord=0; //記錄上一次按鍵的電平狀態(tài)
  77. unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(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ù)點(diǎn)是否顯示的標(biāo)志
  88. unsigned char ucDigDot7;//數(shù)碼管7的小數(shù)點(diǎn)是否顯示的標(biāo)志
  89. unsigned char ucDigDot6;//數(shù)碼管6的小數(shù)點(diǎn)是否顯示的標(biāo)志
  90. unsigned char ucDigDot5;//數(shù)碼管5的小數(shù)點(diǎn)是否顯示的標(biāo)志
  91. unsigned char ucDigDot4;//數(shù)碼管4的小數(shù)點(diǎn)是否顯示的標(biāo)志
  92. unsigned char ucDigDot3;//數(shù)碼管3的小數(shù)點(diǎn)是否顯示的標(biāo)志
  93. unsigned char ucDigDot2;//數(shù)碼管2的小數(shù)點(diǎn)是否顯示的標(biāo)志
  94. unsigned char ucDigDot1;//數(shù)碼管1的小數(shù)點(diǎn)是否顯示的標(biāo)志
  95. unsigned char ucDigShowTemp=0; //臨時(shí)中間變量
  96. unsigned char ucDisplayDriveStep=1;//動(dòng)態(tài)掃描數(shù)碼管的步驟變量
  97. unsigned char ucWd=2;//本程序的核心變量,窗口顯示變量。類似于一級(jí)菜單的變量。代表顯示不同的窗口。
  98. unsigned char ucPart=0;//本程序的核心變量,局部顯示變量。類似于二級(jí)菜單的變量。代表顯示不同的局部。
  99. unsigned char ucWd1Update=0; //窗口1更新顯示標(biāo)志
  100. unsigned char ucWd2Update=1; //窗口2更新顯示標(biāo)志
  101. unsigned char ucWd1Part1Update=0;//在窗口1中,局部1的更新顯示標(biāo)志
  102. unsigned char ucWd1Part2Update=0; //在窗口1中,局部2的更新顯示標(biāo)志
  103. unsigned char ucWd1Part3Update=0; //在窗口1中,局部3的更新顯示標(biāo)志
  104. unsigned char ucWd2Part1Update=0;//在窗口2中,局部1的更新顯示標(biāo)志
  105. unsigned char ucWd2Part2Update=0; //在窗口2中,局部2的更新顯示標(biāo)志
  106. unsigned char ucWd2Part3Update=0; //在窗口2中,局部3的更新顯示標(biāo)志
  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;//中間過(guò)渡變量
  120. unsigned char ucTemp2=0;//中間過(guò)渡變量
  121. unsigned char ucTemp4=0;//中間過(guò)渡變量
  122. unsigned char ucTemp5=0;//中間過(guò)渡變量
  123. unsigned char ucTemp7=0;//中間過(guò)渡變量
  124. unsigned char ucTemp8=0;//中間過(guò)渡變量
  125. unsigned char ucDelayTimerLock=0; //原子鎖
  126. unsigned intuiDelayTimer=0;
  127. unsigned char ucCheckDs1302=0;//檢查Ds1302芯片是否正常
  128. unsigned char ucDs1302Error=0; //Ds1302芯片的備用電池是否用完了的報(bào)警標(biāo)志
  129. unsigned char ucDs1302Lock=0;//原子鎖
  130. unsigned intuiDs1302Cnt=0; //間歇性蜂鳴器報(bào)警的計(jì)時(shí)器
  131. unsigned char ucDpyTimeLock=0; //原子鎖
  132. unsigned intuiDpyTimeCnt=0;//數(shù)碼管的閃爍計(jì)時(shí)器,放在定時(shí)中斷里不斷累加
  133. //根據(jù)原理圖得出的共陰數(shù)碼管字模表
  134. code unsigned char dig_table[]=
  135. {
  136. 0x3f,//0 序號(hào)0
  137. 0x06,//1 序號(hào)1
  138. 0x5b,//2 序號(hào)2
  139. 0x4f,//3 序號(hào)3
  140. 0x66,//4 序號(hào)4
  141. 0x6d,//5 序號(hào)5
  142. 0x7d,//6 序號(hào)6
  143. 0x07,//7 序號(hào)7
  144. 0x7f,//8 序號(hào)8
  145. 0x6f,//9 序號(hào)9
  146. 0x00,//無(wú) 序號(hào)10
  147. 0x40,//- 序號(hào)11
  148. 0x73,//P 序號(hào)12
  149. };
  150. void main()
  151. {
  152. initial_myself();
  153. delay_long(100);
  154. initial_peripheral();
  155. while(1)
  156. {
  157. key_service(); //按鍵服務(wù)的應(yīng)用程序
  158. ds1302_sampling(); //ds1302采樣程序,內(nèi)部每秒鐘采集更新一次
  159. display_service(); //顯示的窗口菜單服務(wù)程序
  160. ds1302_alarm_service(); //ds1302出錯(cuò)報(bào)警
  161. }
  162. }
  163. /* 注釋一:
  164. * 系統(tǒng)不用時(shí)時(shí)刻刻采集ds1302的內(nèi)部數(shù)據(jù),每隔一段時(shí)間更新采集一次就可以了。
  165. * 這個(gè)間隔時(shí)間應(yīng)該小于或者等于1秒鐘的時(shí)間,否則在數(shù)碼管上看不到每秒鐘的時(shí)間變化。
  166. */
  167. void ds1302_sampling(void) //ds1302采樣程序,內(nèi)部每秒鐘采集更新一次
  168. {
  169. if(ucPart==0)//當(dāng)系統(tǒng)不是處于設(shè)置日期和時(shí)間的情況下
  170. {
  171. ++uiSampingCnt;//累計(jì)主循環(huán)次數(shù)的時(shí)間
  172. if(uiSampingCnt>const_ds1302_sampling_time)//每隔一段時(shí)間就更新采集一次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); //讀取時(shí)
  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更新顯示時(shí)間
  188. }
  189. }
  190. }
  191. //修改ds1302時(shí)間的驅(qū)動(dòng) ,注意,此處寫入的是BCD碼,
  192. void Write1302 ( unsigned char addr, unsigned char dat )
  193. {
  194. unsigned char i,temp; //單片機(jī)驅(qū)動(dòng)DS1302屬于SPI通訊方式,根據(jù)我的經(jīng)驗(yàn),不用關(guān)閉中斷
  195. DS1302_CE_dr=0; //CE引腳為低,數(shù)據(jù)傳送中止
  196. delay_short(1);
  197. SCLK_dr=0; //清零時(shí)鐘總線
  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時(shí)間的驅(qū)動(dòng) ,注意,此處讀取的是BCD碼,
  245. unsigned char Read1302 ( unsigned char addr )
  246. {
  247. unsigned char i,temp,dat1;
  248. DS1302_CE_dr=0; //單片機(jī)驅(qū)動(dòng)DS1302屬于SPI通訊方式,根據(jù)我的經(jīng)驗(yàn),不用關(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單片機(jī)IO口的特點(diǎn),在讀取數(shù)據(jù)之前必須先輸出高電平,
  276. * 如果是PIC,AVR等單片機(jī),這里應(yīng)該把IO方向寄存器設(shè)置為輸入
  277. */
  278. DIO_dr_sr =1; //51單片機(jī)IO口的特點(diǎn),在讀取數(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單片機(jī)IO口的特點(diǎn),在讀取數(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)整 每個(gè)月份的日最大取值不同,有的最大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ù)不同的月份來(lái)修正不同的日最大值
  333. {
  334. case 2://二月份要計(jì)算是否是閏年
  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出錯(cuò)報(bào)警
  365. {
  366. if(ucDs1302Error==1)//備用電池的電量用完了報(bào)警提示
  367. {
  368. if(uiDs1302Cnt>const_ds1302_0_5s)//大概0.5秒鐘蜂鳴器響一次
  369. {
  370. ucDs1302Lock=1;//原子鎖加鎖
  371. uiDs1302Cnt=0; //計(jì)時(shí)器清零
  372. ucDs1302Lock=0;//原子鎖解鎖
  373. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  374. uiVoiceCnt=const_voice_short; //蜂鳴器聲音觸發(fā),滴一聲就停。
  375. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  376. }
  377. }
  378. }
  379. void display_service(void) //顯示的窗口菜單服務(wù)程序
  380. {
  381. switch(ucWd)//本程序的核心變量,窗口顯示變量。類似于一級(jí)菜單的變量。代表顯示不同的窗口。
  382. {
  383. case 1: //顯示日期窗口的數(shù)據(jù)數(shù)據(jù)格式 NN-YY-RR 年-月-日
  384. if(ucWd1Update==1)//窗口1要全部更新顯示
  385. {
  386. ucWd1Update=0;//及時(shí)清零標(biāo)志,避免一直進(jìn)來(lái)掃描
  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ù)碼管顯示實(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ù)碼管顯示實(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ù)碼管顯示實(shí)際內(nèi)容
  415. ucDigShow1=ucTemp1;
  416. }
  417. //數(shù)碼管閃爍
  418. switch(ucPart)//相當(dāng)于二級(jí)菜單,根據(jù)局部變量的值,使對(duì)應(yīng)的參數(shù)產(chǎn)生閃爍的動(dòng)態(tài)效果。
  419. {
  420. case 0://都不閃爍
  421. break;
  422. case 1://年參數(shù)閃爍
  423. if(uiDpyTimeCnt==const_dpy_time_half)
  424. {
  425. ucDigShow8=ucTemp8; //數(shù)碼管顯示實(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; //及時(shí)把閃爍記時(shí)器清零
  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ù)碼管顯示實(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; //及時(shí)把閃爍記時(shí)器清零
  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ù)碼管顯示實(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; //及時(shí)把閃爍記時(shí)器清零
  462. ucDpyTimeLock=0;//原子鎖解鎖
  463. ucDigShow2=10; //數(shù)碼管顯示空,什么都不顯示
  464. ucDigShow1=10;
  465. }
  466. break;
  467. }
  468. break;
  469. case 2: //顯示時(shí)間窗口的數(shù)據(jù)數(shù)據(jù)格式 SS FF MM 時(shí) 分 秒
  470. if(ucWd2Update==1)//窗口2要全部更新顯示
  471. {
  472. ucWd2Update=0;//及時(shí)清零標(biāo)志,避免一直進(jìn)來(lái)掃描
  473. ucDigShow6=10;//顯示空
  474. ucDigShow3=10;//顯示空
  475. ucWd2Part3Update=1;//局部時(shí)更新顯示
  476. ucWd2Part2Update=1;//局部分更新顯示
  477. ucWd2Part1Update=1;//局部秒更新顯示
  478. }
  479. if(ucWd2Part1Update==1)//局部時(shí)更新顯示
  480. {
  481. ucWd2Part1Update=0;
  482. ucTemp8=ucHour/10;//時(shí)
  483. ucTemp7=ucHour%10;
  484. ucDigShow8=ucTemp8; //數(shù)碼管顯示實(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ù)碼管顯示實(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ù)碼管顯示實(shí)際內(nèi)容
  501. ucDigShow1=ucTemp1;
  502. }
  503. //數(shù)碼管閃爍
  504. switch(ucPart)//相當(dāng)于二級(jí)菜單,根據(jù)局部變量的值,使對(duì)應(yīng)的參數(shù)產(chǎn)生閃爍的動(dòng)態(tài)效果。
  505. {
  506. case 0://都不閃爍
  507. break;
  508. case 1://時(shí)參數(shù)閃爍
  509. if(uiDpyTimeCnt==const_dpy_time_half)
  510. {
  511. ucDigShow8=ucTemp8; //數(shù)碼管顯示實(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; //及時(shí)把閃爍記時(shí)器清零
  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ù)碼管顯示實(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; //及時(shí)把閃爍記時(shí)器清零
  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ù)碼管顯示實(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; //及時(shí)把閃爍記時(shí)器清零
  548. ucDpyTimeLock=0;//原子鎖解鎖
  549. ucDigShow2=10; //數(shù)碼管顯示空,什么都不顯示
  550. ucDigShow1=10;
  551. }
  552. break;
  553. }
  554. break;
  555. }
  556. }
  557. void key_scan(void)//按鍵掃描函數(shù) 放在定時(shí)中斷里
  558. {
  559. if(key_sr1==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  560. {
  561. ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
  562. uiKeyTimeCnt1=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  563. }
  564. else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  565. {
  566. uiKeyTimeCnt1++; //累加定時(shí)中斷次數(shù)
  567. if(uiKeyTimeCnt1>const_key_time1)
  568. {
  569. uiKeyTimeCnt1=0;
  570. ucKeyLock1=1;//自鎖按鍵置位,避免一直觸發(fā)
  571. ucKeySec=1; //觸發(fā)1號(hào)鍵
  572. }
  573. }
  574. if(key_sr2==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  575. {
  576. ucKeyLock2=0; //按鍵自鎖標(biāo)志清零
  577. uiKeyTimeCnt2=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  578. }
  579. else if(ucKeyLock2==0)//有按鍵按下,且是第一次被按下
  580. {
  581. uiKeyTimeCnt2++; //累加定時(shí)中斷次數(shù)
  582. if(uiKeyTimeCnt2>const_key_time2)
  583. {
  584. uiKeyTimeCnt2=0;
  585. ucKeyLock2=1;//自鎖按鍵置位,避免一直觸發(fā)
  586. ucKeySec=2; //觸發(fā)2號(hào)鍵
  587. }
  588. }
  589. /* 注釋三:
  590. * 注意,此處把一個(gè)按鍵的短按和長(zhǎng)按的功能都實(shí)現(xiàn)了。
  591. */
  592. if(key_sr3==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  593. {
  594. ucKeyLock3=0; //按鍵自鎖標(biāo)志清零
  595. uiKeyTimeCnt3=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  596. }
  597. else if(ucKeyLock3==0)//有按鍵按下,且是第一次被按下
  598. {
  599. uiKeyTimeCnt3++; //累加定時(shí)中斷次數(shù)
  600. if(uiKeyTimeCnt3>const_key_time3)
  601. {
  602. uiKeyTimeCnt3=0;
  603. ucKeyLock3=1;//自鎖按鍵置位,避免一直觸發(fā)
  604. ucKeySec=3; //短按觸發(fā)3號(hào)鍵
  605. }
  606. }
  607. else if(uiKeyTimeCnt3
  608. {
  609. uiKeyTimeCnt3++; //累加定時(shí)中斷次數(shù)
  610. if(uiKeyTimeCnt3==const_key_time17)//等于3秒鐘,觸發(fā)17號(hào)長(zhǎng)按按鍵
  611. {
  612. ucKeySec=17; //長(zhǎng)按3秒觸發(fā)17號(hào)鍵
  613. }
  614. }
  615. /* 注釋四:
  616. * 注意,此處是電平按鍵的濾波抗干擾處理
  617. */
  618. if(key_sr4==1)//對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S13鍵
  619. {
  620. uiKey4Cnt1=0; //在軟件濾波中,非常關(guān)鍵的語(yǔ)句?。?!類似按鍵去抖動(dòng)程序的及時(shí)清零
  621. uiKey4Cnt2++; //類似獨(dú)立按鍵去抖動(dòng)的軟件抗干擾處理
  622. if(uiKey4Cnt2>const_key_time4)
  623. {
  624. uiKey4Cnt2=0;
  625. ucKey4Sr=1;//實(shí)時(shí)反映按鍵松手時(shí)的電平狀態(tài)
  626. }
  627. }
  628. else
  629. {
  630. uiKey4Cnt2=0; //在軟件濾波中,非常關(guān)鍵的語(yǔ)句?。?!類似按鍵去抖動(dòng)程序的及時(shí)清零
  631. uiKey4Cnt1++;
  632. if(uiKey4Cnt1>const_key_time4)
  633. {
  634. uiKey4Cnt1=0;
  635. ucKey4Sr=0;//實(shí)時(shí)反映按鍵按下時(shí)的電平狀態(tài)
  636. }
  637. }
  638. }
  639. void key_service(void) //按鍵服務(wù)的應(yīng)用程序
  640. {
  641. switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  642. {
  643. case 1:// 加按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
  644. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
  645. {
  646. case 1:
  647. switch(ucPart) //在不同的局部變量下,相當(dāng)于二級(jí)菜單
  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) //在不同的局部變量下,相當(dāng)于二級(jí)菜單
  677. {
  678. case 1://時(shí)
  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;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  706. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  707. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  708. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  709. break;
  710. case 2:// 減按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
  711. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
  712. {
  713. case 1:
  714. switch(ucPart) //在不同的局部變量下,相當(dāng)于二級(jí)菜單
  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) //在不同的局部變量下,相當(dāng)于二級(jí)菜單
  744. {
  745. case 1://時(shí)
  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;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  773. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  774. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  775. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  776. break;
  777. case 3://短按設(shè)置按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S9鍵
  778. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
  779. {
  780. case 1:
  781. ucPart++;
  782. if(ucPart>3)
  783. {
  784. ucPart=1;
  785. ucWd=2; //切換到第二個(gè)窗口,設(shè)置時(shí)分秒
  786. ucWd2Update=1;//窗口2更新顯示
  787. }
  788. ucWd1Update=1;//窗口1更新顯示
  789. break;
  790. case 2:
  791. if(ucPart>0) //在窗口2的時(shí)候,要第一次激活設(shè)置時(shí)間,必須是長(zhǎng)按3秒才可以,這里短按激活不了第一次
  792. {
  793. ucPart++;
  794. if(ucPart>3)//設(shè)置時(shí)間結(jié)束
  795. {
  796. ucPart=0;
  797. /* 注釋五:
  798. * 每個(gè)月份的天數(shù)最大值是不一樣的,在寫入ds1302時(shí)鐘芯片內(nèi)部數(shù)據(jù)前,應(yīng)該做一次調(diào)整。
  799. * 有的月份最大28天,有的月份最大29天,有的月份最大30天,有的月份最大31天,
  800. */
  801. ucDate=date_adjust(ucYear,ucMonth,ucDate); //日調(diào)整 避免日的數(shù)值在某個(gè)月份超范圍
  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); //禁止寫保護(hù)
  809. Write1302 (WRITE_YEAR,ucYearBCD); //年修改
  810. Write1302 (WRITE_MONTH,ucMonthBCD); //月修改
  811. Write1302 (WRITE_DATE,ucDateBCD); //日修改
  812. Write1302 (WRITE_HOUR,ucHourBCD); //小時(shí)修改
  813. Write1302 (WRITE_MINUTE,ucMinuteBCD); //分鐘修改
  814. Write1302 (WRITE_SECOND,ucSecondBCD); //秒位修改
  815. Write1302 (WRITE_PROTECT,0x80); //允許寫保護(hù)
  816. }
  817. ucWd2Update=1;//窗口2更新顯示
  818. }
  819. break;
  820. }
  821. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  822. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  823. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  824. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  825. break;
  826. case 17://長(zhǎng)按3秒設(shè)置按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S9鍵
  827. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
  828. {
  829. case 2:
  830. if(ucPart==0) //處于非設(shè)置時(shí)間的狀態(tài)下,要第一次激活設(shè)置時(shí)間,必須是長(zhǎng)按3秒才可以
  831. {
  832. ucWd=1;
  833. ucPart=1;//進(jìn)入到設(shè)置日期的狀態(tài)下
  834. ucWd1Update=1;//窗口1更新顯示
  835. }
  836. break;
  837. }
  838. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  839. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  840. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  841. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  842. break;
  843. }
  844. /* 注釋六:
  845. * 注意,此處就是第一次出現(xiàn)的電平按鍵程序,跟以往的下降沿按鍵不一樣。
  846. * ucKey4Sr是經(jīng)過(guò)軟件濾波處理后,直接反應(yīng)IO口電平狀態(tài)的變量.當(dāng)電平發(fā)生
  847. * 變化時(shí),就會(huì)切換到不同的顯示界面,這里多用了一個(gè)ucKey4SrRecord變量
  848. * 記錄上一次的電平狀態(tài),是為了避免一直刷新顯示。
  849. */
  850. if(ucKey4Sr!=ucKey4SrRecord)//說(shuō)明S13的切換按鍵電平狀態(tài)發(fā)生變化
  851. {
  852. ucKey4SrRecord=ucKey4Sr;//及時(shí)記錄當(dāng)前最新的按鍵電平狀態(tài)避免一直進(jìn)來(lái)觸發(fā)
  853. if(ucKey4Sr==1) //松手后切換到顯示時(shí)間的窗口
  854. {
  855. ucWd=2; //顯示時(shí)分秒的窗口
  856. ucPart=0;//進(jìn)入到非設(shè)置時(shí)間的狀態(tài)下
  857. ucWd2Update=1;//窗口2更新顯示
  858. }
  859. else//按下去切換到顯示日期的窗口
  860. {
  861. ucWd=1; //顯示年月日的窗口
  862. ucPart=0;//進(jìn)入到非設(shè)置時(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ù)點(diǎn)
  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ù)點(diǎn)
  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ù)點(diǎn)
  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ù)點(diǎn)
  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ù)點(diǎn)
  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ù)點(diǎn)
  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ù)點(diǎn)
  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ù)點(diǎn)
  933. }
  934. dig_hc595_drive(ucDigShowTemp,0x7f);
  935. break;
  936. }
  937. ucDisplayDriveStep++;
  938. if(ucDisplayDriveStep>8)//掃描完8個(gè)數(shù)碼管后,重新從第一個(gè)開(kāi)始掃描
  939. {
  940. ucDisplayDriveStep=1;
  941. }
  942. }
  943. //數(shù)碼管的74HC595驅(qū)動(dòng)函數(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引腳把兩個(gè)寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來(lái)
  973. delay_short(1);
  974. dig_hc595_st_dr=1;
  975. delay_short(1);
  976. dig_hc595_sh_dr=0; //拉低,抗干擾就增強(qiáng)
  977. dig_hc595_st_dr=0;
  978. dig_hc595_ds_dr=0;
  979. }
  980. //LED燈的74HC595驅(qū)動(dòng)函數(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引腳把兩個(gè)寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來(lái)
  1010. delay_short(1);
  1011. hc595_st_dr=1;
  1012. delay_short(1);
  1013. hc595_sh_dr=0; //拉低,抗干擾就增強(qiáng)
  1014. hc595_st_dr=0;
  1015. hc595_ds_dr=0;
  1016. }
  1017. void T0_time(void) interrupt 1 //定時(shí)中斷
  1018. {
  1019. TF0=0;//清除中斷標(biāo)志
  1020. TR0=0; //關(guān)中斷
  1021. if(ucVoiceLock==0) //原子鎖判斷
  1022. {
  1023. if(uiVoiceCnt!=0)
  1024. {
  1025. uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
  1026. beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開(kāi)始鳴叫。
  1027. }
  1028. else
  1029. {
  1030. ; //此處多加一個(gè)空指令,想維持跟if括號(hào)語(yǔ)句的數(shù)量對(duì)稱,都是兩條指令。不加也可以。
  1031. beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  1032. }
  1033. }
  1034. if(ucDs1302Error>0) //EEPROM出錯(cuò)
  1035. {
  1036. if(ucDs1302Lock==0)//原子鎖判斷
  1037. {
  1038. uiDs1302Cnt++;//間歇性蜂鳴器報(bào)警的計(jì)時(shí)器
  1039. }
  1040. }
  1041. if(ucDpyTimeLock==0) //原子鎖判斷
  1042. {
  1043. uiDpyTimeCnt++;//數(shù)碼管的閃爍計(jì)時(shí)器
  1044. }
  1045. key_scan(); //按鍵掃描函數(shù)
  1046. display_drive();//數(shù)碼管字模的驅(qū)動(dòng)函數(shù)
  1047. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
  1048. TL0=0x0b;
  1049. TR0=1;//開(kāi)中斷
  1050. }
  1051. void delay_short(unsigned int uiDelayShort)
  1052. {
  1053. unsigned int i;
  1054. for(i=0;i
  1055. {
  1056. ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句
  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. ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句
  1068. }
  1069. }
  1070. }
  1071. void initial_myself(void)//第一區(qū) 初始化單片機(jī)
  1072. {
  1073. key_gnd_dr=0; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平
  1074. beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時(shí)不叫。
  1075. hc595_drive(0x00,0x00);//關(guān)閉所有經(jīng)過(guò)另外兩個(gè)74HC595驅(qū)動(dòng)的LED燈
  1076. TMOD=0x01;//設(shè)置定時(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ù)點(diǎn)全部不顯示
  1083. ucDigDot7=0;
  1084. ucDigDot6=0;
  1085. ucDigDot5=0;
  1086. ucDigDot4=0;
  1087. ucDigDot3=0;
  1088. ucDigDot2=0;
  1089. ucDigDot1=0;
  1090. EA=1; //開(kāi)總中斷
  1091. ET0=1; //允許定時(shí)中斷
  1092. TR0=1; //啟動(dòng)定時(shí)中斷
  1093. /* 注釋七:
  1094. * 檢查ds1302芯片的備用電池電量是否用完了。
  1095. * 在上電的時(shí)候,在一個(gè)特定的地址里把數(shù)據(jù)讀出來(lái),如果發(fā)現(xiàn)不等于0x5a,
  1096. * 則是因?yàn)閭溆秒姵仉娏坑猛炅耍瑢?dǎo)致保存在ds1302內(nèi)部RAM數(shù)據(jù)區(qū)的數(shù)據(jù)被更改,
  1097. * 與此同時(shí),應(yīng)該重新寫入一次0x5a,為下一次通電判斷做準(zhǔn)備
  1098. */
  1099. ucCheckDs1302=Read1302(READ_CHECK); //判斷ds1302內(nèi)部的數(shù)據(jù)是否被更改
  1100. if(ucCheckDs1302!=0x5a)
  1101. {
  1102. Write1302 (WRITE_PROTECT,0X00); //禁止寫保護(hù)
  1103. Write1302 (WRITE_CHECK,0x5a); //重新寫入標(biāo)志數(shù)據(jù),方便下一次更換新電池后的判斷
  1104. Write1302 (WRITE_PROTECT,0x80); //允許寫保護(hù)
  1105. ucDs1302Error=1;//表示ds1302備用電池沒(méi)電了,報(bào)警提示更換新電池
  1106. }
  1107. }

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


評(píng)論


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

關(guān)閉