新聞中心

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

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

作者: 時間:2016-11-22 來源:網(wǎng)絡 收藏
開場白:
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ù)需要重新設置過,或者說明電池沒電了。
在移植DS1302驅動程序中,有一個地方最容易出錯,就是DS1302芯片的數(shù)據(jù)線DIO。我們編程時要特別留意這個IO口什么時候作為數(shù)據(jù)輸入,什么時候作為數(shù)據(jù)輸出,以便及時更改方向寄存器。對于51單片機,IO口在讀取數(shù)據(jù)之前,要先置1。
這一節(jié)要教會大家六個知識點:
第一個:DS1302做實時時鐘時,修改時間和讀取時間的常見程序框架。
第二個:如何編寫監(jiān)控備用電池電量耗盡的監(jiān)控程序。
第三個:在往DS1302寫入數(shù)據(jù)修改時間之前,必須注意調整一下“日”的變量,因為每個月的最大天數(shù)是不一樣的,有的一個月28天,有的一個月29天,有的一個月30天,有的一個月31天。
第四個:本程序第一次出現(xiàn)了電平按鍵,跟之前講的下降沿按鍵不一樣,請留意我是何如用軟件濾波的,以及它具體的實現(xiàn)代碼。
第五個:本程序第一次出現(xiàn)了一個按鍵按下去后,如果不松手就會觸發(fā)兩次事件,第一次是短按,第二次是長按3秒。請留意我是如何在之前的按鍵上略做修改就實現(xiàn)此功能的具體代碼。
第六個:繼續(xù)加深了解按鍵與顯示是如何緊密關聯(lián)起來的程序框架。

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

(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)會自動把我們修改設置的日期時間一次性寫入DS1302芯片內部,達到修改日期時間的目的。
S13是電平變化按鍵,用來切換窗口的,專門用來查看當前日期。按下S13按鍵時顯示日期窗口,松手后返回到顯示實時時間的窗口。

本程序在使用過程中的注意事項:
(a)第一次上電時,蜂鳴器會報警,只要DS1302芯片的備用電池電量充足,這個時候斷電再重啟一次,就不會報警了。
(b)第一次上電時,時間沒有走動,需要重新設置一下日期時間才可以。長按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內部的相關地址
  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. //驅動數(shù)碼管的74HC595
  32. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  33. void display_drive(void); //顯示數(shù)碼管字模的驅動函數(shù)
  34. void display_service(void); //顯示的窗口菜單服務程序
  35. //驅動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); //按鍵服務的應用程序
  39. void key_scan(void);//按鍵掃描函數(shù) 放在定時中斷里
  40. void ds1302_alarm_service(void); //ds1302出錯報警
  41. void ds1302_sampling(void); //ds1302采樣程序,內部每秒鐘采集更新一次
  42. void Write1302 ( unsigned char addr, unsigned char dat );//修改時間的驅動
  43. unsigned char Read1302 ( unsigned char addr );//讀取時間的驅動
  44. unsigned char bcd_to_number(unsigned char ucBcdTemp);//BCD轉原始數(shù)值
  45. unsigned char number_to_bcd(unsigned char ucNumberTemp); //原始數(shù)值轉BCD
  46. //日調整 每個月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
  47. unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp); //日調整
  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; //蜂鳴器的驅動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ù)碼管要顯示的內容
  80. unsigned char ucDigShow7;//第7位數(shù)碼管要顯示的內容
  81. unsigned char ucDigShow6;//第6位數(shù)碼管要顯示的內容
  82. unsigned char ucDigShow5;//第5位數(shù)碼管要顯示的內容
  83. unsigned char ucDigShow4;//第4位數(shù)碼管要顯示的內容
  84. unsigned char ucDigShow3;//第3位數(shù)碼管要顯示的內容
  85. unsigned char ucDigShow2;//第2位數(shù)碼管要顯示的內容
  86. unsigned char ucDigShow1;//第1位數(shù)碼管要顯示的內容
  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(); //按鍵服務的應用程序
  158. ds1302_sampling(); //ds1302采樣程序,內部每秒鐘采集更新一次
  159. display_service(); //顯示的窗口菜單服務程序
  160. ds1302_alarm_service(); //ds1302出錯報警
  161. }
  162. }
  163. /* 注釋一:
  164. * 系統(tǒng)不用時時刻刻采集ds1302的內部數(shù)據(jù),每隔一段時間更新采集一次就可以了。
  165. * 這個間隔時間應該小于或者等于1秒鐘的時間,否則在數(shù)碼管上看不到每秒鐘的時間變化。
  166. */
  167. void ds1302_sampling(void) //ds1302采樣程序,內部每秒鐘采集更新一次
  168. {
  169. if(ucPart==0)//當系統(tǒng)不是處于設置日期和時間的情況下
  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轉原始數(shù)值
  182. ucMonth=bcd_to_number(ucMonthBCD);//BCD轉原始數(shù)值
  183. ucDate=bcd_to_number(ucDateBCD);//BCD轉原始數(shù)值
  184. ucHour=bcd_to_number(ucHourBCD);//BCD轉原始數(shù)值
  185. ucMinute=bcd_to_number(ucMinuteBCD);//BCD轉原始數(shù)值
  186. ucSecond=bcd_to_number(ucSecondBCD);//BCD轉原始數(shù)值
  187. ucWd2Update=1; //窗口2更新顯示時間
  188. }
  189. }
  190. }
  191. //修改ds1302時間的驅動 ,注意,此處寫入的是BCD碼,
  192. void Write1302 ( unsigned char addr, unsigned char dat )
  193. {
  194. unsigned char i,temp; //單片機驅動DS1302屬于SPI通訊方式,根據(jù)我的經(jīng)驗,不用關閉中斷
  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時間的驅動 ,注意,此處讀取的是BCD碼,
  245. unsigned char Read1302 ( unsigned char addr )
  246. {
  247. unsigned char i,temp,dat1;
  248. DS1302_CE_dr=0; //單片機驅動DS1302屬于SPI通訊方式,根據(jù)我的經(jīng)驗,不用關閉中斷
  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方向寄存器設置為輸入
  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轉原始數(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ù)值轉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. //日調整 每個月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
  326. unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp) //日調整
  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) //顯示的窗口菜單服務程序
  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ù)碼管顯示實際內容
  399. ucDigShow7=ucTemp7;
  400. }
  401. if(ucWd1Part2Update==1)//局部月更新顯示
  402. {
  403. ucWd1Part2Update=0;
  404. ucTemp5=ucMonth/10;//月
  405. ucTemp4=ucMonth%10;
  406. ucDigShow5=ucTemp5; //數(shù)碼管顯示實際內容
  407. ucDigShow4=ucTemp4;
  408. }
  409. if(ucWd1Part3Update==1) //局部日更新顯示
  410. {
  411. ucWd1Part3Update=0;
  412. ucTemp2=ucDate/10;//日
  413. ucTemp1=ucDate%10;
  414. ucDigShow2=ucTemp2; //數(shù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  485. ucDigShow7=ucTemp7;
  486. }
  487. if(ucWd2Part2Update==1)//局部分更新顯示
  488. {
  489. ucWd2Part2Update=0;
  490. ucTemp5=ucMinute/10;//分
  491. ucTemp4=ucMinute%10;
  492. ucDigShow5=ucTemp5; //數(shù)碼管顯示實際內容
  493. ucDigShow4=ucTemp4;
  494. }
  495. if(ucWd2Part3Update==1) //局部秒更新顯示
  496. {
  497. ucWd2Part3Update=0;
  498. ucTemp2=ucSecond/10;//秒
  499. ucTemp1=ucSecond%10;
  500. ucDigShow2=ucTemp2; //數(shù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  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ù)碼管顯示實際內容
  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; //在軟件濾波中,非常關鍵的語句?。?!類似按鍵去抖動程序的及時清零
  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; //在軟件濾波中,非常關鍵的語句?。?!類似按鍵去抖動程序的及時清零
  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) //按鍵服務的應用程序
  640. {
  641. switch(ucKeySec) //按鍵服務狀態(tài)切換
  642. {
  643. case 1:// 加按鍵 對應朱兆祺學習板的S1鍵
  644. switch(ucWd)//在不同的窗口下,設置不同的參數(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;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(fā)
  709. break;
  710. case 2:// 減按鍵 對應朱兆祺學習板的S5鍵
  711. switch(ucWd)//在不同的窗口下,設置不同的參數(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;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(fā)
  776. break;
  777. case 3://短按設置按鍵 對應朱兆祺學習板的S9鍵
  778. switch(ucWd)//在不同的窗口下,設置不同的參數(shù)
  779. {
  780. case 1:
  781. ucPart++;
  782. if(ucPart>3)
  783. {
  784. ucPart=1;
  785. ucWd=2; //切換到第二個窗口,設置時分秒
  786. ucWd2Update=1;//窗口2更新顯示
  787. }
  788. ucWd1Update=1;//窗口1更新顯示
  789. break;
  790. case 2:
  791. if(ucPart>0) //在窗口2的時候,要第一次激活設置時間,必須是長按3秒才可以,這里短按激活不了第一次
  792. {
  793. ucPart++;
  794. if(ucPart>3)//設置時間結束
  795. {
  796. ucPart=0;
  797. /* 注釋五:
  798. * 每個月份的天數(shù)最大值是不一樣的,在寫入ds1302時鐘芯片內部數(shù)據(jù)前,應該做一次調整。
  799. * 有的月份最大28天,有的月份最大29天,有的月份最大30天,有的月份最大31天,
  800. */
  801. ucDate=date_adjust(ucYear,ucMonth,ucDate); //日調整 避免日的數(shù)值在某個月份超范圍
  802. ucYearBCD=number_to_bcd(ucYear);//原始數(shù)值轉BCD
  803. ucMonthBCD=number_to_bcd(ucMonth); //原始數(shù)值轉BCD
  804. ucDateBCD=number_to_bcd(ucDate);//原始數(shù)值轉BCD
  805. ucHourBCD=number_to_bcd(ucHour);//原始數(shù)值轉BCD
  806. ucMinuteBCD=number_to_bcd(ucMinute);//原始數(shù)值轉BCD
  807. ucSecondBCD=number_to_bcd(ucSecond);//原始數(shù)值轉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;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(fā)
  825. break;
  826. case 17://長按3秒設置按鍵 對應朱兆祺學習板的S9鍵
  827. switch(ucWd)//在不同的窗口下,設置不同的參數(shù)
  828. {
  829. case 2:
  830. if(ucPart==0) //處于非設置時間的狀態(tài)下,要第一次激活設置時間,必須是長按3秒才可以
  831. {
  832. ucWd=1;
  833. ucPart=1;//進入到設置日期的狀態(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;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(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;//進入到非設置時間的狀態(tài)下
  857. ucWd2Update=1;//窗口2更新顯示
  858. }
  859. else//按下去切換到顯示日期的窗口
  860. {
  861. ucWd=1; //顯示年月日的窗口
  862. ucPart=0;//進入到非設置時間的狀態(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驅動函數(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驅動函數(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; //關中斷
  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ù)碼管字模的驅動函數(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++)//內嵌循環(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);//關閉所有經(jīng)過另外兩個74HC595驅動的LED燈
  1076. TMOD=0x01;//設置定時器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內部RAM數(shù)據(jù)區(qū)的數(shù)據(jù)被更改,
  1097. * 與此同時,應該重新寫入一次0x5a,為下一次通電判斷做準備
  1098. */
  1099. ucCheckDs1302=Read1302(READ_CHECK); //判斷ds1302內部的數(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é)開始講單片機驅動溫度傳感器芯片的內容,欲知詳情,請聽下回分解-----利用DS18B20做一個溫控器。


關鍵詞: DS1302實時時

評論


技術專區(qū)

關閉