新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 第45節(jié):主機(jī)的串口收發(fā)綜合程序框架

第45節(jié):主機(jī)的串口收發(fā)綜合程序框架

作者: 時(shí)間:2016-11-22 來(lái)源:網(wǎng)絡(luò) 收藏
開(kāi)場(chǎng)白:
在大部分的項(xiàng)目中,串口都需要“一收一應(yīng)答”的握手協(xié)議,主機(jī)先發(fā)一串?dāng)?shù)據(jù),從機(jī)收到數(shù)據(jù)后進(jìn)行校驗(yàn)判斷,如果校驗(yàn)正確則返回正確應(yīng)答指令,如果校驗(yàn)錯(cuò)誤則返回錯(cuò)誤應(yīng)答指令,主機(jī)收到應(yīng)答指令后,如果發(fā)現(xiàn)是正確應(yīng)答指令則繼續(xù)發(fā)送其它的新數(shù)據(jù),如果發(fā)現(xiàn)是錯(cuò)誤應(yīng)答指令,或者超時(shí)沒(méi)有接收到任何應(yīng)答指令,則繼續(xù)重發(fā),如果連續(xù)重發(fā)三次都是錯(cuò)誤應(yīng)答或者無(wú)應(yīng)答,主機(jī)就進(jìn)行報(bào)錯(cuò)處理。
上一節(jié)已經(jīng)講了從機(jī),這節(jié)就講主機(jī)的收發(fā)端程序?qū)嵗?。要教?huì)大家四個(gè)知識(shí)點(diǎn):

第一個(gè):為了保證串口中斷接收的數(shù)據(jù)不丟失,在初始化時(shí)必須設(shè)置IP= 0x10,相當(dāng)于把串口中斷設(shè)置為最高優(yōu)先級(jí),這個(gè)時(shí)候,串口中斷可以打斷任何其他的中斷服務(wù)函數(shù),實(shí)現(xiàn)中斷嵌套。
第二個(gè):主機(jī)端的收發(fā)端程序框架。包括重發(fā),超時(shí)檢測(cè)等等。
第三個(gè):主機(jī)的狀態(tài)指示程序框架??梢灾甘敬龣C(jī),通訊中,超時(shí)出錯(cuò)三種狀態(tài)。
第四個(gè):其實(shí)上一節(jié)的LED燈閃爍的時(shí)間里,我忘了加原子鎖,不加原子鎖的后果是,閃爍的時(shí)間有時(shí)候會(huì)不一致,所以這節(jié)多增加一個(gè)原子鎖變量ucLedLock,再次感謝“紅金龍吸味”關(guān)于原子鎖的建議,真的很好用。

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

(1)硬件平臺(tái):
基于朱兆祺51單片機(jī)學(xué)習(xí)板

(2)實(shí)現(xiàn)功能:
顯示和獨(dú)立按鍵部分根據(jù)第29節(jié)的程序來(lái)改編,用朱兆祺51單片機(jī)學(xué)習(xí)板中的S1,S5,S9,S13作為獨(dú)立按鍵。
一共有4個(gè)窗口。每個(gè)窗口顯示一個(gè)參數(shù)。串口可以把當(dāng)前設(shè)置的4個(gè)數(shù)據(jù)發(fā)送給從機(jī)。從機(jī)端可以用電腦的串口助手來(lái)模擬。
第一:按鍵更改參數(shù):
第8,7,6,5位數(shù)碼管顯示當(dāng)前窗口,P-1代表第1個(gè)窗口,P-2代表第2個(gè)窗口,P-3代表第3個(gè)窗口,P-4代表第1個(gè)窗口。
第4,3,2,1位數(shù)碼管顯示當(dāng)前窗口被設(shè)置的參數(shù)。范圍是從0到9999。S1是加按鍵,按下此按鍵會(huì)依次增加當(dāng)前窗口的參數(shù)。S5是減按鍵,按下此按鍵會(huì)依次減少當(dāng)前窗口的參數(shù)。S9是切換窗口按鍵,按下此按鍵會(huì)依次循環(huán)切換不同的窗口。S13是啟動(dòng)發(fā)送數(shù)據(jù)和復(fù)位按鍵,當(dāng)系統(tǒng)處于待機(jī)狀態(tài)時(shí),按下此按鍵會(huì)啟動(dòng)發(fā)送數(shù)據(jù);當(dāng)通訊超時(shí)蜂鳴器報(bào)警時(shí),可以按下此鍵清除報(bào)警,返回到待機(jī)的狀態(tài)。

第二:通過(guò)串口把更改的參數(shù)發(fā)送給從機(jī)。
波特率是:9600.
通訊協(xié)議:EB00 55GG 00 02 XX XXCY
其中第1,2,3位EB00 55就是數(shù)據(jù)頭
其中第4位GG就是數(shù)據(jù)類型。01代表更改參數(shù)1,02代表更改參數(shù)2,03代表更改參數(shù)3,04代表更改參數(shù)4,
其中第5,6位0002就是有效數(shù)據(jù)長(zhǎng)度。高位在左,低位在右。
其中從第7,8位XXXX是被更改的參數(shù)。高位在左,低位在右。
第9位CY是累加和,前面所有字節(jié)的累加。
一個(gè)完整的通訊必須發(fā)送完4串?dāng)?shù)據(jù),每串?dāng)?shù)據(jù)之間的間隔時(shí)間不能超過(guò)10秒鐘,否則認(rèn)為通訊超時(shí)主機(jī)會(huì)重發(fā)數(shù)據(jù),如果連續(xù)三次都沒(méi)有返回,則引發(fā)蜂鳴器報(bào)警。如果接收到得數(shù)據(jù)校驗(yàn)正確,主機(jī)繼續(xù)發(fā)送新的一串?dāng)?shù)據(jù),直到把4串?dāng)?shù)據(jù)發(fā)送完畢為止。

系統(tǒng)處于待機(jī)狀態(tài)時(shí),LED燈一直亮,
系統(tǒng)處于非待機(jī)狀態(tài)時(shí),LED燈閃爍,
系統(tǒng)處于出錯(cuò)狀態(tài)時(shí),LED燈閃爍,并且蜂鳴器間歇鳴叫報(bào)警。

通過(guò)電腦的串口助手來(lái)模擬從機(jī),返回不同的應(yīng)答
從機(jī)返回校驗(yàn)正確應(yīng)答:eb 00 55 f5 00 00 35
從機(jī)返回校驗(yàn)出錯(cuò)應(yīng)答:eb00 55 fa 00 00 3a

(3)源代碼講解如下:
  1. #include "REG52.H"
  2. #define const_voice_short40 //蜂鳴器短叫的持續(xù)時(shí)間
  3. #define const_key_time120 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  4. #define const_key_time220 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  5. #define const_key_time320 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  6. #define const_key_time420 //按鍵去抖動(dòng)延時(shí)的時(shí)間
  7. #define const_led_0_5s200 //大概0.5秒的時(shí)間
  8. #define const_led_1s 400 //大概1秒的時(shí)間
  9. #define const_send_time_out 4000//通訊超時(shí)出錯(cuò)的時(shí)間 大概10秒
  10. #define const_rc_size20//接收串口中斷數(shù)據(jù)的緩沖區(qū)數(shù)組大小
  11. #define const_receive_time5//如果超過(guò)這個(gè)時(shí)間沒(méi)有串口數(shù)據(jù)過(guò)來(lái),就認(rèn)為一串?dāng)?shù)據(jù)已經(jīng)全部接收完,這個(gè)時(shí)間根據(jù)實(shí)際情況來(lái)調(diào)整大小
  12. #define const_send_size10//串口發(fā)送數(shù)據(jù)的緩沖區(qū)數(shù)組大小
  13. void initial_myself(void);
  14. void initial_peripheral(void);
  15. void delay_short(unsigned int uiDelayShort);
  16. void delay_long(unsigned int uiDelaylong);
  17. //驅(qū)動(dòng)數(shù)碼管的74HC595
  18. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
  19. void display_drive(void); //顯示數(shù)碼管字模的驅(qū)動(dòng)函數(shù)
  20. void display_service(void); //顯示的窗口菜單服務(wù)程序
  21. //驅(qū)動(dòng)LED的74HC595
  22. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  23. void T0_time(void);//定時(shí)中斷函數(shù)
  24. void usart_receive(void); //串口接收中斷函數(shù)
  25. void usart_service(void);//串口接收服務(wù)程序,在main函數(shù)里
  26. void communication_service(void); //一發(fā)一收的通訊服務(wù)程序
  27. void eusart_send(unsigned char ucSendData); //發(fā)送一個(gè)字節(jié),內(nèi)部自帶每個(gè)字節(jié)之間的delay延時(shí)
  28. void key_service(void); //按鍵服務(wù)的應(yīng)用程序
  29. void key_scan(void);//按鍵掃描函數(shù) 放在定時(shí)中斷里
  30. void status_service(void);//狀態(tài)顯示的應(yīng)用程序
  31. sbit key_sr1=P0^0; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
  32. sbit key_sr2=P0^1; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
  33. sbit key_sr3=P0^2; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S9鍵
  34. sbit key_sr4=P0^3; //對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S13鍵
  35. sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平
  36. sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動(dòng)IO口
  37. sbit led_dr=P3^5;//作為狀態(tài)指示燈 亮的時(shí)候表示待機(jī)狀態(tài).閃爍表示非待機(jī)狀態(tài),處于正在發(fā)送數(shù)據(jù)或者出錯(cuò)的狀態(tài)
  38. sbit dig_hc595_sh_dr=P2^0; //數(shù)碼管的74HC595程序
  39. sbit dig_hc595_st_dr=P2^1;
  40. sbit dig_hc595_ds_dr=P2^2;
  41. sbit hc595_sh_dr=P2^3; //LED燈的74HC595程序
  42. sbit hc595_st_dr=P2^4;
  43. sbit hc595_ds_dr=P2^5;
  44. unsigned char ucSendregBuf[const_send_size]; //發(fā)送的緩沖區(qū)數(shù)組
  45. unsigned intuiSendCnt=0; //用來(lái)識(shí)別串口是否接收完一串?dāng)?shù)據(jù)的計(jì)時(shí)器
  46. unsigned char ucSendLock=1; //串口服務(wù)程序的自鎖變量,每次接收完一串?dāng)?shù)據(jù)只處理一次
  47. unsigned intuiRcregTotal=0;//代表當(dāng)前緩沖區(qū)已經(jīng)接收了多少個(gè)數(shù)據(jù)
  48. unsigned char ucRcregBuf[const_rc_size]; //接收串口中斷數(shù)據(jù)的緩沖區(qū)數(shù)組
  49. unsigned intuiRcMoveIndex=0;//用來(lái)解析數(shù)據(jù)協(xié)議的中間變量
  50. unsigned charucSendCntLock=0; //串口計(jì)時(shí)器的原子鎖
  51. unsigned char ucRcType=0;//數(shù)據(jù)類型
  52. unsigned intuiRcSize=0;//數(shù)據(jù)長(zhǎng)度
  53. unsigned char ucRcCy=0;//校驗(yàn)累加和
  54. unsigned char ucLedLock=0; //原子鎖
  55. unsigned intuiLedCnt=0;//控制Led閃爍的延時(shí)計(jì)時(shí)器
  56. unsigned intuiSendTimeOutCnt=0; //用來(lái)識(shí)別接收數(shù)據(jù)超時(shí)的計(jì)時(shí)器
  57. unsigned char ucSendTimeOutLock=0; //原子鎖
  58. unsigned char ucStatus=0; //當(dāng)前狀態(tài)變量 0代表待機(jī) 1代表正在通訊過(guò)程 2代表發(fā)送出錯(cuò)
  59. unsigned char ucSendStep=0; //發(fā)送數(shù)據(jù)的過(guò)程步驟
  60. unsigned char ucErrorCnt=0; //累計(jì)錯(cuò)誤總數(shù)
  61. unsigned char ucSendTotal=0; //記錄當(dāng)前已經(jīng)發(fā)送了多少串?dāng)?shù)據(jù)
  62. unsigned char ucReceiveStatus=0; //返回的數(shù)據(jù)狀態(tài) 0代表待機(jī) 1代表校驗(yàn)正確 2代表校驗(yàn)出錯(cuò)
  63. unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號(hào)
  64. unsigned intuiKeyTimeCnt1=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  65. unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  66. unsigned intuiKeyTimeCnt2=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  67. unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  68. unsigned intuiKeyTimeCnt3=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  69. unsigned char ucKeyLock3=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  70. unsigned intuiKeyTimeCnt4=0; //按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器
  71. unsigned char ucKeyLock4=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
  72. unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器
  73. unsigned charucVoiceLock=0;//蜂鳴器鳴叫的原子鎖
  74. unsigned char ucDigShow8;//第8位數(shù)碼管要顯示的內(nèi)容
  75. unsigned char ucDigShow7;//第7位數(shù)碼管要顯示的內(nèi)容
  76. unsigned char ucDigShow6;//第6位數(shù)碼管要顯示的內(nèi)容
  77. unsigned char ucDigShow5;//第5位數(shù)碼管要顯示的內(nèi)容
  78. unsigned char ucDigShow4;//第4位數(shù)碼管要顯示的內(nèi)容
  79. unsigned char ucDigShow3;//第3位數(shù)碼管要顯示的內(nèi)容
  80. unsigned char ucDigShow2;//第2位數(shù)碼管要顯示的內(nèi)容
  81. unsigned char ucDigShow1;//第1位數(shù)碼管要顯示的內(nèi)容
  82. unsigned char ucDigDot8;//數(shù)碼管8的小數(shù)點(diǎn)是否顯示的標(biāo)志
  83. unsigned char ucDigDot7;//數(shù)碼管7的小數(shù)點(diǎn)是否顯示的標(biāo)志
  84. unsigned char ucDigDot6;//數(shù)碼管6的小數(shù)點(diǎn)是否顯示的標(biāo)志
  85. unsigned char ucDigDot5;//數(shù)碼管5的小數(shù)點(diǎn)是否顯示的標(biāo)志
  86. unsigned char ucDigDot4;//數(shù)碼管4的小數(shù)點(diǎn)是否顯示的標(biāo)志
  87. unsigned char ucDigDot3;//數(shù)碼管3的小數(shù)點(diǎn)是否顯示的標(biāo)志
  88. unsigned char ucDigDot2;//數(shù)碼管2的小數(shù)點(diǎn)是否顯示的標(biāo)志
  89. unsigned char ucDigDot1;//數(shù)碼管1的小數(shù)點(diǎn)是否顯示的標(biāo)志
  90. unsigned char ucDigShowTemp=0; //臨時(shí)中間變量
  91. unsigned char ucDisplayDriveStep=1;//動(dòng)態(tài)掃描數(shù)碼管的步驟變量
  92. unsigned char ucWd1Update=1; //窗口1更新顯示標(biāo)志
  93. unsigned char ucWd2Update=0; //窗口2更新顯示標(biāo)志
  94. unsigned char ucWd3Update=0; //窗口3更新顯示標(biāo)志
  95. unsigned char ucWd4Update=0; //窗口4更新顯示標(biāo)志
  96. unsigned char ucWd=1;//本程序的核心變量,窗口顯示變量。類似于一級(jí)菜單的變量。代表顯示不同的窗口。
  97. unsigned intuiSetData1=0;//本程序中需要被設(shè)置的參數(shù)1
  98. unsigned intuiSetData2=0;//本程序中需要被設(shè)置的參數(shù)2
  99. unsigned intuiSetData3=0;//本程序中需要被設(shè)置的參數(shù)3
  100. unsigned intuiSetData4=0;//本程序中需要被設(shè)置的參數(shù)4
  101. unsigned char ucTemp1=0;//中間過(guò)渡變量
  102. unsigned char ucTemp2=0;//中間過(guò)渡變量
  103. unsigned char ucTemp3=0;//中間過(guò)渡變量
  104. unsigned char ucTemp4=0;//中間過(guò)渡變量
  105. //根據(jù)原理圖得出的共陰數(shù)碼管字模表
  106. code unsigned char dig_table[]=
  107. {
  108. 0x3f,//0 序號(hào)0
  109. 0x06,//1 序號(hào)1
  110. 0x5b,//2 序號(hào)2
  111. 0x4f,//3 序號(hào)3
  112. 0x66,//4 序號(hào)4
  113. 0x6d,//5 序號(hào)5
  114. 0x7d,//6 序號(hào)6
  115. 0x07,//7 序號(hào)7
  116. 0x7f,//8 序號(hào)8
  117. 0x6f,//9 序號(hào)9
  118. 0x00,//無(wú) 序號(hào)10
  119. 0x40,//- 序號(hào)11
  120. 0x73,//P 序號(hào)12
  121. };
  122. void main()
  123. {
  124. initial_myself();
  125. delay_long(100);
  126. initial_peripheral();
  127. while(1)
  128. {
  129. key_service(); //按鍵服務(wù)的應(yīng)用程序
  130. usart_service();//串口接收服務(wù)程序
  131. communication_service(); //一發(fā)一收的通訊服務(wù)程序
  132. display_service(); //顯示的窗口菜單服務(wù)程序
  133. status_service();//狀態(tài)顯示的應(yīng)用程序
  134. }
  135. }
  136. void communication_service(void) //一發(fā)一收的通訊服務(wù)程序
  137. {
  138. unsigned int i;
  139. if(ucStatus==1)//處于正在通訊的過(guò)程中
  140. {
  141. switch(ucSendStep)
  142. {
  143. case 0: //通訊過(guò)程0發(fā)送一串?dāng)?shù)據(jù)
  144. switch(ucSendTotal)//根據(jù)當(dāng)前已經(jīng)發(fā)送到第幾條數(shù)據(jù)來(lái)決定發(fā)送哪些參數(shù)
  145. {
  146. case 0: //發(fā)送參數(shù)1
  147. ucSendregBuf[0]=0xeb; //把準(zhǔn)備發(fā)送的數(shù)據(jù)放入發(fā)送緩沖區(qū)
  148. ucSendregBuf[1]=0x00;
  149. ucSendregBuf[2]=0x55;
  150. ucSendregBuf[3]=0x01; //代表發(fā)送參數(shù)1
  151. ucSendregBuf[4]=0x00;
  152. ucSendregBuf[5]=0x02; //代表發(fā)送2個(gè)字節(jié)的有效數(shù)據(jù)
  153. ucSendregBuf[6]=uiSetData1>>8;//把int類型的參數(shù)分解成兩個(gè)字節(jié)的數(shù)據(jù)
  154. ucSendregBuf[7]=uiSetData1;
  155. break;
  156. case 1://發(fā)送參數(shù)2
  157. ucSendregBuf[0]=0xeb; //把準(zhǔn)備發(fā)送的數(shù)據(jù)放入發(fā)送緩沖區(qū)
  158. ucSendregBuf[1]=0x00;
  159. ucSendregBuf[2]=0x55;
  160. ucSendregBuf[3]=0x02; //代表發(fā)送參數(shù)2
  161. ucSendregBuf[4]=0x00;
  162. ucSendregBuf[5]=0x02; //代表發(fā)送2個(gè)字節(jié)的有效數(shù)據(jù)
  163. ucSendregBuf[6]=uiSetData2>>8;//把int類型的參數(shù)分解成兩個(gè)字節(jié)的數(shù)據(jù)
  164. ucSendregBuf[7]=uiSetData2;
  165. break;
  166. case 2://發(fā)送參數(shù)3
  167. ucSendregBuf[0]=0xeb; //把準(zhǔn)備發(fā)送的數(shù)據(jù)放入發(fā)送緩沖區(qū)
  168. ucSendregBuf[1]=0x00;
  169. ucSendregBuf[2]=0x55;
  170. ucSendregBuf[3]=0x03; //代表發(fā)送參數(shù)3
  171. ucSendregBuf[4]=0x00;
  172. ucSendregBuf[5]=0x02; //代表發(fā)送2個(gè)字節(jié)的有效數(shù)據(jù)
  173. ucSendregBuf[6]=uiSetData3>>8;//把int類型的參數(shù)分解成兩個(gè)字節(jié)的數(shù)據(jù)
  174. ucSendregBuf[7]=uiSetData3;
  175. break;
  176. case 3://發(fā)送參數(shù)4
  177. ucSendregBuf[0]=0xeb; //把準(zhǔn)備發(fā)送的數(shù)據(jù)放入發(fā)送緩沖區(qū)
  178. ucSendregBuf[1]=0x00;
  179. ucSendregBuf[2]=0x55;
  180. ucSendregBuf[3]=0x04; //代表發(fā)送參數(shù)4
  181. ucSendregBuf[4]=0x00;
  182. ucSendregBuf[5]=0x02; //代表發(fā)送2個(gè)字節(jié)的有效數(shù)據(jù)
  183. ucSendregBuf[6]=uiSetData4>>8;//把int類型的參數(shù)分解成兩個(gè)字節(jié)的數(shù)據(jù)
  184. ucSendregBuf[7]=uiSetData4;
  185. break;
  186. }
  187. ucSendregBuf[8]=0x00;
  188. for(i=0;i<8;i++)//最后一個(gè)字節(jié)是校驗(yàn)和,是前面所有字節(jié)累加,溢出部分不用我們管,系統(tǒng)會(huì)有規(guī)律的自動(dòng)處理
  189. {
  190. ucSendregBuf[8]=ucSendregBuf[8]+ucSendregBuf[i];
  191. }
  192. for(i=0;i<9;i++)
  193. {
  194. eusart_send(ucSendregBuf[i]);//把一串完整的數(shù)據(jù)發(fā)送給下位機(jī)
  195. }
  196. ucSendTimeOutLock=1; //原子鎖加鎖
  197. uiSendTimeOutCnt=0;//超時(shí)計(jì)時(shí)器計(jì)時(shí)清零
  198. ucSendTimeOutLock=0; //原子鎖解鎖
  199. ucReceiveStatus=0;//返回的數(shù)據(jù)狀態(tài)清零
  200. ucSendStep=1;//切換到下一個(gè)步驟,等待返回的數(shù)據(jù)
  201. break;
  202. case 1: //通訊過(guò)程1判斷返回的指令
  203. if(ucReceiveStatus==1)//校驗(yàn)正確
  204. {
  205. ucErrorCnt=0; //累計(jì)校驗(yàn)錯(cuò)誤總數(shù)清零
  206. ucSendTotal++;//累加當(dāng)前發(fā)送了多少串?dāng)?shù)據(jù)
  207. if(ucSendTotal>=4) //已經(jīng)發(fā)送完全部4串?dāng)?shù)據(jù),結(jié)束
  208. {
  209. ucStatus=0;//切換到結(jié)束時(shí)的待機(jī)狀態(tài)
  210. }
  211. else//還沒(méi)發(fā)送完4串?dāng)?shù)據(jù),則繼續(xù)發(fā)送下一串新數(shù)據(jù)
  212. {
  213. ucSendStep=0;//返回上一個(gè)步驟,繼續(xù)發(fā)送新數(shù)據(jù)
  214. }
  215. }
  216. else if(ucReceiveStatus==2||uiSendTimeOutCnt>const_send_time_out)//校驗(yàn)出錯(cuò)或者超時(shí)出錯(cuò)
  217. {
  218. ucErrorCnt++; //累計(jì)錯(cuò)誤總數(shù)
  219. if(ucErrorCnt>=3)//累加重發(fā)次數(shù)3次以上,則報(bào)錯(cuò)
  220. {
  221. ucStatus=2;//切換到出錯(cuò)報(bào)警狀態(tài)
  222. }
  223. else//重發(fā)還沒(méi)超過(guò)3次,繼續(xù)返回重發(fā)
  224. {
  225. ucSendStep=0;//返回上一個(gè)步驟,重發(fā)一次數(shù)據(jù)
  226. }
  227. }
  228. break;
  229. }
  230. }
  231. }
  232. void status_service(void)//狀態(tài)顯示的應(yīng)用程序
  233. {
  234. if(ucStatus!=0) //處于非待機(jī)的狀態(tài),Led閃爍
  235. {
  236. if(uiLedCnt
  237. {
  238. led_dr=1;//前半秒亮
  239. if(ucStatus==2)//處于發(fā)送數(shù)據(jù)出錯(cuò)的狀態(tài),則蜂鳴器間歇鳴叫報(bào)警
  240. {
  241. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  242. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  243. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  244. }
  245. }
  246. else if(uiLedCnt
  247. {
  248. led_dr=0; //前半秒滅
  249. }
  250. else
  251. {
  252. ucLedLock=1; //原子鎖加鎖
  253. uiLedCnt=0; //延時(shí)計(jì)時(shí)器清零,讓Led燈處于閃爍的反復(fù)循環(huán)中
  254. ucLedLock=0; //原子鎖解鎖
  255. }
  256. }
  257. else//處于待機(jī)狀態(tài),Led一直亮
  258. {
  259. led_dr=1;
  260. }
  261. }
  262. void usart_service(void)//串口接收服務(wù)程序,在main函數(shù)里
  263. {
  264. unsigned int i;
  265. if(uiSendCnt>=const_receive_time&&ucSendLock==1) //說(shuō)明超過(guò)了一定的時(shí)間內(nèi),再也沒(méi)有新數(shù)據(jù)從串口來(lái)
  266. {
  267. ucSendLock=0; //處理一次就鎖起來(lái),不用每次都進(jìn)來(lái),除非有新接收的數(shù)據(jù)
  268. //下面的代碼進(jìn)入數(shù)據(jù)協(xié)議解析和數(shù)據(jù)處理的階段
  269. uiRcMoveIndex=0; //由于是判斷數(shù)據(jù)頭,所以下標(biāo)移動(dòng)變量從數(shù)組的0開(kāi)始向最尾端移動(dòng)
  270. while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5))
  271. {
  272. if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55)//數(shù)據(jù)頭eb 00 55的判斷
  273. {
  274. ucRcType=ucRcregBuf[uiRcMoveIndex+3]; //數(shù)據(jù)類型一個(gè)字節(jié)
  275. uiRcSize=ucRcregBuf[uiRcMoveIndex+4]; //數(shù)據(jù)長(zhǎng)度兩個(gè)字節(jié)
  276. uiRcSize=uiRcSize<<8;
  277. uiRcSize=uiRcSize+ucRcregBuf[uiRcMoveIndex+5];
  278. ucRcCy=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]; //記錄最后一個(gè)字節(jié)的校驗(yàn)
  279. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=0;//清零最后一個(gè)字節(jié)的累加和變量
  280. for(i=0;i<(3+1+2+uiRcSize);i++) //計(jì)算校驗(yàn)累加和
  281. {
  282. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]+ucRcregBuf[uiRcMoveIndex+i];
  283. }
  284. if(ucRcCy==ucRcregBuf[uiRcMoveIndex+6+uiRcSize])//如果一串?dāng)?shù)據(jù)校驗(yàn)正確,則進(jìn)入以下數(shù)據(jù)指令的判斷
  285. {
  286. switch(ucRcType) //根據(jù)不同的數(shù)據(jù)類型來(lái)做不同的數(shù)據(jù)處理
  287. {
  288. case 0xf5: //返回的是正確的校驗(yàn)指令
  289. ucReceiveStatus=1;//代表校驗(yàn)正確
  290. break;
  291. case 0xfa: //返回的是錯(cuò)誤的校驗(yàn)指令
  292. ucReceiveStatus=2;//代表校驗(yàn)錯(cuò)誤
  293. break;
  294. }
  295. }
  296. break; //退出循環(huán)
  297. }
  298. uiRcMoveIndex++; //因?yàn)槭桥袛鄶?shù)據(jù)頭,游標(biāo)向著數(shù)組最尾端的方向移動(dòng)
  299. }
  300. uiRcregTotal=0;//清空緩沖的下標(biāo),方便下次重新從0下標(biāo)開(kāi)始接受新數(shù)據(jù)
  301. }
  302. }
  303. void eusart_send(unsigned char ucSendData) //發(fā)送一個(gè)字節(jié),內(nèi)部自帶每個(gè)字節(jié)之間的delay延時(shí)
  304. {
  305. ES = 0; //關(guān)串口中斷
  306. TI = 0; //清零串口發(fā)送完成中斷請(qǐng)求標(biāo)志
  307. SBUF =ucSendData; //發(fā)送一個(gè)字節(jié)
  308. delay_short(400);//每個(gè)字節(jié)之間的延時(shí),這里非常關(guān)鍵,也是最容易出錯(cuò)的地方。延時(shí)的大小請(qǐng)根據(jù)實(shí)際項(xiàng)目來(lái)調(diào)整
  309. TI = 0; //清零串口發(fā)送完成中斷請(qǐng)求標(biāo)志
  310. ES = 1; //允許串口中斷
  311. }
  312. void display_service(void) //顯示的窗口菜單服務(wù)程序
  313. {
  314. switch(ucWd)//本程序的核心變量,窗口顯示變量。類似于一級(jí)菜單的變量。代表顯示不同的窗口。
  315. {
  316. case 1: //顯示P--1窗口的數(shù)據(jù)
  317. if(ucWd1Update==1)//窗口1要全部更新顯示
  318. {
  319. ucWd1Update=0;//及時(shí)清零標(biāo)志,避免一直進(jìn)來(lái)掃描
  320. ucDigShow8=12;//第8位數(shù)碼管顯示P
  321. ucDigShow7=11;//第7位數(shù)碼管顯示-
  322. ucDigShow6=1; //第6位數(shù)碼管顯示1
  323. ucDigShow5=10;//第5位數(shù)碼管顯示無(wú)
  324. //先分解數(shù)據(jù)
  325. ucTemp4=uiSetData1/1000;
  326. ucTemp3=uiSetData1%1000/100;
  327. ucTemp2=uiSetData1%100/10;
  328. ucTemp1=uiSetData1%10;
  329. //再過(guò)渡需要顯示的數(shù)據(jù)到緩沖變量里,讓過(guò)渡的時(shí)間越短越好
  330. if(uiSetData1<1000)
  331. {
  332. ucDigShow4=10;//如果小于1000,千位顯示無(wú)
  333. }
  334. else
  335. {
  336. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
  337. }
  338. if(uiSetData1<100)
  339. {
  340. ucDigShow3=10;//如果小于100,百位顯示無(wú)
  341. }
  342. else
  343. {
  344. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
  345. }
  346. if(uiSetData1<10)
  347. {
  348. ucDigShow2=10;//如果小于10,十位顯示無(wú)
  349. }
  350. else
  351. {
  352. ucDigShow2=ucTemp2;//第2位數(shù)碼管要顯示的內(nèi)容
  353. }
  354. ucDigShow1=ucTemp1;//第1位數(shù)碼管要顯示的內(nèi)容
  355. }
  356. break;
  357. case 2://顯示P--2窗口的數(shù)據(jù)
  358. if(ucWd2Update==1)//窗口2要全部更新顯示
  359. {
  360. ucWd2Update=0;//及時(shí)清零標(biāo)志,避免一直進(jìn)來(lái)掃描
  361. ucDigShow8=12;//第8位數(shù)碼管顯示P
  362. ucDigShow7=11;//第7位數(shù)碼管顯示-
  363. ucDigShow6=2;//第6位數(shù)碼管顯示2
  364. ucDigShow5=10; //第5位數(shù)碼管顯示無(wú)
  365. ucTemp4=uiSetData2/1000; //分解數(shù)據(jù)
  366. ucTemp3=uiSetData2%1000/100;
  367. ucTemp2=uiSetData2%100/10;
  368. ucTemp1=uiSetData2%10;
  369. if(uiSetData2<1000)
  370. {
  371. ucDigShow4=10;//如果小于1000,千位顯示無(wú)
  372. }
  373. else
  374. {
  375. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
  376. }
  377. if(uiSetData2<100)
  378. {
  379. ucDigShow3=10;//如果小于100,百位顯示無(wú)
  380. }
  381. else
  382. {
  383. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
  384. }
  385. if(uiSetData2<10)
  386. {
  387. ucDigShow2=10;//如果小于10,十位顯示無(wú)
  388. }
  389. else
  390. {
  391. ucDigShow2=ucTemp2;//第2位數(shù)碼管要顯示的內(nèi)容
  392. }
  393. ucDigShow1=ucTemp1;//第1位數(shù)碼管要顯示的內(nèi)容
  394. }
  395. break;
  396. case 3://顯示P--3窗口的數(shù)據(jù)
  397. if(ucWd3Update==1)//窗口3要全部更新顯示
  398. {
  399. ucWd3Update=0;//及時(shí)清零標(biāo)志,避免一直進(jìn)來(lái)掃描
  400. ucDigShow8=12;//第8位數(shù)碼管顯示P
  401. ucDigShow7=11;//第7位數(shù)碼管顯示-
  402. ucDigShow6=3;//第6位數(shù)碼管顯示3
  403. ucDigShow5=10; //第5位數(shù)碼管顯示無(wú)
  404. ucTemp4=uiSetData3/1000; //分解數(shù)據(jù)
  405. ucTemp3=uiSetData3%1000/100;
  406. ucTemp2=uiSetData3%100/10;
  407. ucTemp1=uiSetData3%10;
  408. if(uiSetData3<1000)
  409. {
  410. ucDigShow4=10;//如果小于1000,千位顯示無(wú)
  411. }
  412. else
  413. {
  414. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
  415. }
  416. if(uiSetData3<100)
  417. {
  418. ucDigShow3=10;//如果小于100,百位顯示無(wú)
  419. }
  420. else
  421. {
  422. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
  423. }
  424. if(uiSetData3<10)
  425. {
  426. ucDigShow2=10;//如果小于10,十位顯示無(wú)
  427. }
  428. else
  429. {
  430. ucDigShow2=ucTemp2;//第2位數(shù)碼管要顯示的內(nèi)容
  431. }
  432. ucDigShow1=ucTemp1;//第1位數(shù)碼管要顯示的內(nèi)容
  433. }
  434. break;
  435. case 4://顯示P--4窗口的數(shù)據(jù)
  436. if(ucWd4Update==1)//窗口4要全部更新顯示
  437. {
  438. ucWd4Update=0;//及時(shí)清零標(biāo)志,避免一直進(jìn)來(lái)掃描
  439. ucDigShow8=12;//第8位數(shù)碼管顯示P
  440. ucDigShow7=11;//第7位數(shù)碼管顯示-
  441. ucDigShow6=4;//第6位數(shù)碼管顯示4
  442. ucDigShow5=10; //第5位數(shù)碼管顯示無(wú)
  443. ucTemp4=uiSetData4/1000; //分解數(shù)據(jù)
  444. ucTemp3=uiSetData4%1000/100;
  445. ucTemp2=uiSetData4%100/10;
  446. ucTemp1=uiSetData4%10;
  447. if(uiSetData4<1000)
  448. {
  449. ucDigShow4=10;//如果小于1000,千位顯示無(wú)
  450. }
  451. else
  452. {
  453. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
  454. }
  455. if(uiSetData4<100)
  456. {
  457. ucDigShow3=10;//如果小于100,百位顯示無(wú)
  458. }
  459. else
  460. {
  461. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
  462. }
  463. if(uiSetData4<10)
  464. {
  465. ucDigShow2=10;//如果小于10,十位顯示無(wú)
  466. }
  467. else
  468. {
  469. ucDigShow2=ucTemp2;//第2位數(shù)碼管要顯示的內(nèi)容
  470. }
  471. ucDigShow1=ucTemp1;//第1位數(shù)碼管要顯示的內(nèi)容
  472. }
  473. break;
  474. }
  475. }
  476. void key_scan(void)//按鍵掃描函數(shù) 放在定時(shí)中斷里
  477. {
  478. if(key_sr1==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  479. {
  480. ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
  481. uiKeyTimeCnt1=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  482. }
  483. else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  484. {
  485. uiKeyTimeCnt1++; //累加定時(shí)中斷次數(shù)
  486. if(uiKeyTimeCnt1>const_key_time1)
  487. {
  488. uiKeyTimeCnt1=0;
  489. ucKeyLock1=1;//自鎖按鍵置位,避免一直觸發(fā)
  490. ucKeySec=1; //觸發(fā)1號(hào)鍵
  491. }
  492. }
  493. if(key_sr2==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  494. {
  495. ucKeyLock2=0; //按鍵自鎖標(biāo)志清零
  496. uiKeyTimeCnt2=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  497. }
  498. else if(ucKeyLock2==0)//有按鍵按下,且是第一次被按下
  499. {
  500. uiKeyTimeCnt2++; //累加定時(shí)中斷次數(shù)
  501. if(uiKeyTimeCnt2>const_key_time2)
  502. {
  503. uiKeyTimeCnt2=0;
  504. ucKeyLock2=1;//自鎖按鍵置位,避免一直觸發(fā)
  505. ucKeySec=2; //觸發(fā)2號(hào)鍵
  506. }
  507. }
  508. if(key_sr3==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  509. {
  510. ucKeyLock3=0; //按鍵自鎖標(biāo)志清零
  511. uiKeyTimeCnt3=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  512. }
  513. else if(ucKeyLock3==0)//有按鍵按下,且是第一次被按下
  514. {
  515. uiKeyTimeCnt3++; //累加定時(shí)中斷次數(shù)
  516. if(uiKeyTimeCnt3>const_key_time3)
  517. {
  518. uiKeyTimeCnt3=0;
  519. ucKeyLock3=1;//自鎖按鍵置位,避免一直觸發(fā)
  520. ucKeySec=3; //觸發(fā)3號(hào)鍵
  521. }
  522. }
  523. if(key_sr4==1)//IO是高電平,說(shuō)明按鍵沒(méi)有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  524. {
  525. ucKeyLock4=0; //按鍵自鎖標(biāo)志清零
  526. uiKeyTimeCnt4=0;//按鍵去抖動(dòng)延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來(lái)的。
  527. }
  528. else if(ucKeyLock4==0)//有按鍵按下,且是第一次被按下
  529. {
  530. uiKeyTimeCnt4++; //累加定時(shí)中斷次數(shù)
  531. if(uiKeyTimeCnt4>const_key_time4)
  532. {
  533. uiKeyTimeCnt4=0;
  534. ucKeyLock4=1;//自鎖按鍵置位,避免一直觸發(fā)
  535. ucKeySec=4; //觸發(fā)4號(hào)鍵
  536. }
  537. }
  538. }
  539. void key_service(void) //按鍵服務(wù)的應(yīng)用程序
  540. {
  541. switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  542. {
  543. case 1:// 加按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
  544. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
  545. {
  546. case 1:
  547. uiSetData1++;
  548. if(uiSetData1>9999) //最大值是9999
  549. {
  550. uiSetData1=9999;
  551. }
  552. ucWd1Update=1;//窗口1更新顯示
  553. break;
  554. case 2:
  555. uiSetData2++;
  556. if(uiSetData2>9999) //最大值是9999
  557. {
  558. uiSetData2=9999;
  559. }
  560. ucWd2Update=1;//窗口2更新顯示
  561. break;
  562. case 3:
  563. uiSetData3++;
  564. if(uiSetData3>9999) //最大值是9999
  565. {
  566. uiSetData3=9999;
  567. }
  568. ucWd3Update=1;//窗口3更新顯示
  569. break;
  570. case 4:
  571. uiSetData4++;
  572. if(uiSetData4>9999) //最大值是9999
  573. {
  574. uiSetData4=9999;
  575. }
  576. ucWd4Update=1;//窗口4更新顯示
  577. break;
  578. }
  579. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  580. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  581. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  582. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  583. break;
  584. case 2:// 減按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
  585. switch(ucWd)//在不同的窗口下,設(shè)置不同的參數(shù)
  586. {
  587. case 1:
  588. uiSetData1--;
  589. if(uiSetData1>9999)
  590. {
  591. uiSetData1=0;//最小值是0
  592. }
  593. ucWd1Update=1;//窗口1更新顯示
  594. break;
  595. case 2:
  596. uiSetData2--;
  597. if(uiSetData2>9999)
  598. {
  599. uiSetData2=0;//最小值是0
  600. }
  601. ucWd2Update=1;//窗口2更新顯示
  602. break;
  603. case 3:
  604. uiSetData3--;
  605. if(uiSetData3>9999)
  606. {
  607. uiSetData3=0;//最小值是0
  608. }
  609. ucWd3Update=1;//窗口3更新顯示
  610. break;
  611. case 4:
  612. uiSetData4--;
  613. if(uiSetData4>9999)
  614. {
  615. uiSetData4=0;//最小值是0
  616. }
  617. ucWd4Update=1;//窗口4更新顯示
  618. break;
  619. }
  620. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  621. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  622. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  623. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  624. break;
  625. case 3:// 切換窗口按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S9鍵
  626. ucWd++;//切換窗口
  627. if(ucWd>4)
  628. {
  629. ucWd=1;
  630. }
  631. switch(ucWd)//在不同的窗口下,在不同的窗口下,更新顯示不同的窗口
  632. {
  633. case 1:
  634. ucWd1Update=1;//窗口1更新顯示
  635. break;
  636. case 2:
  637. ucWd2Update=1;//窗口2更新顯示
  638. break;
  639. case 3:
  640. ucWd3Update=1;//窗口3更新顯示
  641. break;
  642. case 4:
  643. ucWd4Update=1;//窗口4更新顯示
  644. break;
  645. }
  646. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  647. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  648. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  649. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  650. break;
  651. case 4:// 啟動(dòng)發(fā)送數(shù)據(jù)和復(fù)位按鍵 對(duì)應(yīng)朱兆祺學(xué)習(xí)板的S13鍵
  652. switch(ucStatus)//在不同的狀態(tài)下,進(jìn)行不同的操作
  653. {
  654. case 0://處于待機(jī)狀態(tài),則啟動(dòng)發(fā)送數(shù)據(jù)
  655. ucErrorCnt=0; //累計(jì)錯(cuò)誤總數(shù)清零
  656. ucSendTotal=0; //已經(jīng)發(fā)送串?dāng)?shù)據(jù)總數(shù)清零
  657. ucSendStep=0; //發(fā)送數(shù)據(jù)的過(guò)程步驟清零,返回開(kāi)始的步驟待命
  658. ucStatus=1; //啟動(dòng)發(fā)送數(shù)據(jù),1代表正在通訊過(guò)程
  659. break;
  660. case 1://處于正在通訊的過(guò)程
  661. break;
  662. case 2: //發(fā)送數(shù)據(jù)出錯(cuò),比如中間超時(shí)沒(méi)有接收到數(shù)據(jù)
  663. ucStatus=0; //切換回待機(jī)的狀態(tài)
  664. break;
  665. }
  666. ucVoiceLock=1;//原子鎖加鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  667. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
  668. ucVoiceLock=0;//原子鎖解鎖,保護(hù)主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
  669. ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號(hào)清零,避免一致觸發(fā)
  670. break;
  671. }
  672. }
  673. void display_drive(void)
  674. {
  675. //以下程序,如果加一些數(shù)組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路
  676. switch(ucDisplayDriveStep)
  677. {
  678. case 1://顯示第1位
  679. ucDigShowTemp=dig_table[ucDigShow1];
  680. if(ucDigDot1==1)
  681. {
  682. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  683. }
  684. dig_hc595_drive(ucDigShowTemp,0xfe);
  685. break;
  686. case 2://顯示第2位
  687. ucDigShowTemp=dig_table[ucDigShow2];
  688. if(ucDigDot2==1)
  689. {
  690. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  691. }
  692. dig_hc595_drive(ucDigShowTemp,0xfd);
  693. break;
  694. case 3://顯示第3位
  695. ucDigShowTemp=dig_table[ucDigShow3];
  696. if(ucDigDot3==1)
  697. {
  698. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  699. }
  700. dig_hc595_drive(ucDigShowTemp,0xfb);
  701. break;
  702. case 4://顯示第4位
  703. ucDigShowTemp=dig_table[ucDigShow4];
  704. if(ucDigDot4==1)
  705. {
  706. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  707. }
  708. dig_hc595_drive(ucDigShowTemp,0xf7);
  709. break;
  710. case 5://顯示第5位
  711. ucDigShowTemp=dig_table[ucDigShow5];
  712. if(ucDigDot5==1)
  713. {
  714. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  715. }
  716. dig_hc595_drive(ucDigShowTemp,0xef);
  717. break;
  718. case 6://顯示第6位
  719. ucDigShowTemp=dig_table[ucDigShow6];
  720. if(ucDigDot6==1)
  721. {
  722. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  723. }
  724. dig_hc595_drive(ucDigShowTemp,0xdf);
  725. break;
  726. case 7://顯示第7位
  727. ucDigShowTemp=dig_table[ucDigShow7];
  728. if(ucDigDot7==1)
  729. {
  730. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  731. }
  732. dig_hc595_drive(ucDigShowTemp,0xbf);
  733. break;
  734. case 8://顯示第8位
  735. ucDigShowTemp=dig_table[ucDigShow8];
  736. if(ucDigDot8==1)
  737. {
  738. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數(shù)點(diǎn)
  739. }
  740. dig_hc595_drive(ucDigShowTemp,0x7f);
  741. break;
  742. }
  743. ucDisplayDriveStep++;
  744. if(ucDisplayDriveStep>8)//掃描完8個(gè)數(shù)碼管后,重新從第一個(gè)開(kāi)始掃描
  745. {
  746. ucDisplayDriveStep=1;
  747. }
  748. }
  749. //數(shù)碼管的74HC595驅(qū)動(dòng)函數(shù)
  750. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  751. {
  752. unsigned char i;
  753. unsigned char ucTempData;
  754. dig_hc595_sh_dr=0;
  755. dig_hc595_st_dr=0;
  756. ucTempData=ucDigStatusTemp16_09;//先送高8位
  757. for(i=0;i<8;i++)
  758. {
  759. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  760. else dig_hc595_ds_dr=0;
  761. dig_hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
  762. delay_short(1);
  763. dig_hc595_sh_dr=1;
  764. delay_short(1);
  765. ucTempData=ucTempData<<1;
  766. }
  767. ucTempData=ucDigStatusTemp08_01;//再先送低8位
  768. for(i=0;i<8;i++)
  769. {
  770. if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  771. else dig_hc595_ds_dr=0;
  772. dig_hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
  773. delay_short(1);
  774. dig_hc595_sh_dr=1;
  775. delay_short(1);
  776. ucTempData=ucTempData<<1;
  777. }
  778. dig_hc595_st_dr=0;//ST引腳把兩個(gè)寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來(lái)
  779. delay_short(1);
  780. dig_hc595_st_dr=1;
  781. delay_short(1);
  782. dig_hc595_sh_dr=0; //拉低,抗干擾就增強(qiáng)
  783. dig_hc595_st_dr=0;
  784. dig_hc595_ds_dr=0;
  785. }
  786. //LED燈的74HC595驅(qū)動(dòng)函數(shù)
  787. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  788. {
  789. unsigned char i;
  790. unsigned char ucTempData;
  791. hc595_sh_dr=0;
  792. hc595_st_dr=0;
  793. ucTempData=ucLedStatusTemp16_09;//先送高8位
  794. for(i=0;i<8;i++)
  795. {
  796. if(ucTempData>=0x80)hc595_ds_dr=1;
  797. else hc595_ds_dr=0;
  798. hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
  799. delay_short(1);
  800. hc595_sh_dr=1;
  801. delay_short(1);
  802. ucTempData=ucTempData<<1;
  803. }
  804. ucTempData=ucLedStatusTemp08_01;//再先送低8位
  805. for(i=0;i<8;i++)
  806. {
  807. if(ucTempData>=0x80)hc595_ds_dr=1;
  808. else hc595_ds_dr=0;
  809. hc595_sh_dr=0; //SH引腳的上升沿把數(shù)據(jù)送入寄存器
  810. delay_short(1);
  811. hc595_sh_dr=1;
  812. delay_short(1);
  813. ucTempData=ucTempData<<1;
  814. }
  815. hc595_st_dr=0;//ST引腳把兩個(gè)寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來(lái)
  816. delay_short(1);
  817. hc595_st_dr=1;
  818. delay_short(1);
  819. hc595_sh_dr=0; //拉低,抗干擾就增強(qiáng)
  820. hc595_st_dr=0;
  821. hc595_ds_dr=0;
  822. }
  823. void usart_receive(void) interrupt 4 //串口接收數(shù)據(jù)中斷
  824. {
  825. if(RI==1)
  826. {
  827. RI = 0;
  828. ++uiRcregTotal;
  829. if(uiRcregTotal>const_rc_size)//超過(guò)緩沖區(qū)
  830. {
  831. uiRcregTotal=const_rc_size;
  832. }
  833. ucRcregBuf[uiRcregTotal-1]=SBUF; //將串口接收到的數(shù)據(jù)緩存到接收緩沖區(qū)里
  834. if(ucSendCntLock==0)//原子鎖判斷
  835. {
  836. ucSendCntLock=1; //加鎖
  837. uiSendCnt=0;//及時(shí)喂狗,雖然在定時(shí)中斷那邊此變量會(huì)不斷累加,但是只要串口的數(shù)據(jù)還沒(méi)發(fā)送完畢,那么它永遠(yuǎn)也長(zhǎng)不大,因?yàn)槊總€(gè)串口接收中斷它都被清零。
  838. ucSendCntLock=0; //解鎖
  839. }
  840. }
  841. else//我在其它單片機(jī)上都不用else這段代碼的,可能在51單片機(jī)上多增加" TI = 0;"穩(wěn)定性會(huì)更好吧。
  842. {
  843. TI = 0;//如果不是串口接收中斷,那么必然是串口發(fā)送中斷,及時(shí)清除發(fā)送中斷的標(biāo)志,否則一直發(fā)送中斷
  844. }
  845. }
  846. void T0_time(void) interrupt 1 //定時(shí)中斷
  847. {
  848. TF0=0;//清除中斷標(biāo)志
  849. TR0=0; //關(guān)中斷
  850. /* 注釋一:
  851. * 此處多增加一個(gè)原子鎖,作為中斷與主函數(shù)共享數(shù)據(jù)的保護(hù),實(shí)際上是借鑒了"紅金龍吸味"關(guān)于原子鎖的建議.
  852. */
  853. if(ucSendCntLock==0)//原子鎖判斷
  854. {
  855. ucSendCntLock=1; //加鎖
  856. if(uiSendCnt
  857. {
  858. uiSendCnt++; //表面上這個(gè)數(shù)據(jù)不斷累加,但是在串口中斷里,每接收一個(gè)字節(jié)它都會(huì)被清零,除非這個(gè)中間沒(méi)有串口數(shù)據(jù)過(guò)來(lái)
  859. ucSendLock=1; //開(kāi)自鎖標(biāo)志
  860. }
  861. ucSendCntLock=0; //解鎖
  862. }
  863. if(ucVoiceLock==0) //原子鎖判斷
  864. {
  865. if(uiVoiceCnt!=0)
  866. {
  867. uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
  868. beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開(kāi)始鳴叫。
  869. }
  870. else
  871. {
  872. ; //此處多加一個(gè)空指令,想維持跟if括號(hào)語(yǔ)句的數(shù)量對(duì)稱,都是兩條指令。不加也可以。
  873. beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  874. }
  875. }
  876. if(ucStatus!=0) //處于非待機(jī)的狀態(tài),Led閃爍
  877. {
  878. if(ucLedLock==0)//原子鎖判斷
  879. {
  880. uiLedCnt++; //Led閃爍計(jì)時(shí)器不斷累加
  881. }
  882. }
  883. if(ucStatus==1) //處于正在通訊的狀態(tài),
  884. {
  885. if(ucSendTimeOutLock==0)//原子鎖判斷
  886. {
  887. uiSendTimeOutCnt++; //超時(shí)計(jì)時(shí)器累加
  888. }
  889. }
  890. key_scan(); //按鍵掃描函數(shù)
  891. display_drive();//數(shù)碼管字模的驅(qū)動(dòng)函數(shù)
  892. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
  893. TL0=0x0b;
  894. TR0=1;//開(kāi)中斷
  895. }
  896. void delay_short(unsigned int uiDelayShort)
  897. {
  898. unsigned int i;
  899. for(i=0;i
  900. {
  901. ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句
  902. }
  903. }
  904. void delay_long(unsigned int uiDelayLong)
  905. {
  906. unsigned int i;
  907. unsigned int j;
  908. for(i=0;i
  909. {
  910. for(j=0;j<500;j++)//內(nèi)嵌循環(huán)的空指令數(shù)量
  911. {
  912. ; //一個(gè)分號(hào)相當(dāng)于執(zhí)行一條空語(yǔ)句
  913. }
  914. }
  915. }
  916. void initial_myself(void)//第一區(qū) 初始化單片機(jī)
  917. {
  918. /* 注釋二:
  919. * 矩陣鍵盤(pán)也可以做獨(dú)立按鍵,前提是把某一根公共輸出線輸出低電平,
  920. * 模擬獨(dú)立按鍵的觸發(fā)地,本程序中,把key_gnd_dr輸出低電平。
  921. * 朱兆祺51學(xué)習(xí)板的S1就是本程序中用到的一個(gè)獨(dú)立按鍵。
  922. */
  923. key_gnd_dr=0; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平
  924. led_dr=1;//點(diǎn)亮獨(dú)立LED燈
  925. beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時(shí)不叫。
  926. hc595_drive(0x00,0x00);//關(guān)閉所有經(jīng)過(guò)另外兩個(gè)74HC595驅(qū)動(dòng)的LED燈
  927. TMOD=0x01;//設(shè)置定時(shí)器0為工作方式1
  928. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
  929. TL0=0x0b;
  930. //配置串口
  931. SCON=0x50;
  932. TMOD=0X21;
  933. /* 注釋三:
  934. * 為了保證串口中斷接收的數(shù)據(jù)不丟失,必須設(shè)置IP = 0x10,相當(dāng)于把串口中斷設(shè)置為最高優(yōu)先級(jí),
  935. * 這個(gè)時(shí)候,串口中斷可以打斷任何其他的中斷服務(wù)函數(shù)實(shí)現(xiàn)嵌套,
  936. */
  937. IP =0x10;//把串口中斷設(shè)置為最高優(yōu)先級(jí),必須的。
  938. TH1=TL1=-(11059200L/12/32/9600);//串口波特率為9600。
  939. TR1=1;
  940. }
  941. void initial_peripheral(void) //第二區(qū) 初始化外圍
  942. {
  943. ucDigDot8=0; //小數(shù)點(diǎn)全部不顯示
  944. ucDigDot7=0;
  945. ucDigDot6=0;
  946. ucDigDot5=0;
  947. ucDigDot4=0;
  948. ucDigDot3=0;
  949. ucDigDot2=0;
  950. ucDigDot1=0;
  951. EA=1; //開(kāi)總中斷
  952. ES=1; //允許串口中斷
  953. ET0=1; //允許定時(shí)中斷
  954. TR0=1; //啟動(dòng)定時(shí)中斷
  955. }


總結(jié)陳詞:
前面花了大量篇幅詳細(xì)地講解了串口收發(fā)數(shù)據(jù)的程序框架,從下一節(jié)開(kāi)始我講解單片機(jī)掉電后數(shù)據(jù)保存的內(nèi)容,欲知詳情,請(qǐng)聽(tīng)下回分解-----利用AT24C02進(jìn)行掉電后的數(shù)據(jù)保存。


評(píng)論


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

關(guān)閉