<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

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

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

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

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

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

          (1)硬件平臺:
          基于朱兆祺51單片機學習板。

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

          第二:通過串口把更改的參數(shù)發(fā)送給從機。
          波特率是: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ù)長度。高位在左,低位在右。
          其中從第7,8位XXXX是被更改的參數(shù)。高位在左,低位在右。
          第9位CY是累加和,前面所有字節(jié)的累加。
          一個完整的通訊必須發(fā)送完4串數(shù)據(jù),每串數(shù)據(jù)之間的間隔時間不能超過10秒鐘,否則認為通訊超時主機會重發(fā)數(shù)據(jù),如果連續(xù)三次都沒有返回,則引發(fā)蜂鳴器報警。如果接收到得數(shù)據(jù)校驗正確,主機繼續(xù)發(fā)送新的一串數(shù)據(jù),直到把4串數(shù)據(jù)發(fā)送完畢為止。

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

          通過電腦的串口助手來模擬從機,返回不同的應答
          從機返回校驗正確應答:eb 00 55 f5 00 00 35
          從機返回校驗出錯應答:eb00 55 fa 00 00 3a

          (3)源代碼講解如下:
          1. #include "REG52.H"
          2. #define const_voice_short40 //蜂鳴器短叫的持續(xù)時間
          3. #define const_key_time120 //按鍵去抖動延時的時間
          4. #define const_key_time220 //按鍵去抖動延時的時間
          5. #define const_key_time320 //按鍵去抖動延時的時間
          6. #define const_key_time420 //按鍵去抖動延時的時間
          7. #define const_led_0_5s200 //大概0.5秒的時間
          8. #define const_led_1s 400 //大概1秒的時間
          9. #define const_send_time_out 4000//通訊超時出錯的時間 大概10秒
          10. #define const_rc_size20//接收串口中斷數(shù)據(jù)的緩沖區(qū)數(shù)組大小
          11. #define const_receive_time5//如果超過這個時間沒有串口數(shù)據(jù)過來,就認為一串數(shù)據(jù)已經(jīng)全部接收完,這個時間根據(jù)實際情況來調(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ū)動數(shù)碼管的74HC595
          18. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
          19. void display_drive(void); //顯示數(shù)碼管字模的驅(qū)動函數(shù)
          20. void display_service(void); //顯示的窗口菜單服務程序
          21. //驅(qū)動LED的74HC595
          22. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
          23. void T0_time(void);//定時中斷函數(shù)
          24. void usart_receive(void); //串口接收中斷函數(shù)
          25. void usart_service(void);//串口接收服務程序,在main函數(shù)里
          26. void communication_service(void); //一發(fā)一收的通訊服務程序
          27. void eusart_send(unsigned char ucSendData); //發(fā)送一個字節(jié),內(nèi)部自帶每個字節(jié)之間的delay延時
          28. void key_service(void); //按鍵服務的應用程序
          29. void key_scan(void);//按鍵掃描函數(shù) 放在定時中斷里
          30. void status_service(void);//狀態(tài)顯示的應用程序
          31. sbit key_sr1=P0^0; //對應朱兆祺學習板的S1鍵
          32. sbit key_sr2=P0^1; //對應朱兆祺學習板的S5鍵
          33. sbit key_sr3=P0^2; //對應朱兆祺學習板的S9鍵
          34. sbit key_sr4=P0^3; //對應朱兆祺學習板的S13鍵
          35. sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平
          36. sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口
          37. sbit led_dr=P3^5;//作為狀態(tài)指示燈 亮的時候表示待機狀態(tài).閃爍表示非待機狀態(tài),處于正在發(fā)送數(shù)據(jù)或者出錯的狀態(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; //用來識別串口是否接收完一串數(shù)據(jù)的計時器
          46. unsigned char ucSendLock=1; //串口服務程序的自鎖變量,每次接收完一串數(shù)據(jù)只處理一次
          47. unsigned intuiRcregTotal=0;//代表當前緩沖區(qū)已經(jīng)接收了多少個數(shù)據(jù)
          48. unsigned char ucRcregBuf[const_rc_size]; //接收串口中斷數(shù)據(jù)的緩沖區(qū)數(shù)組
          49. unsigned intuiRcMoveIndex=0;//用來解析數(shù)據(jù)協(xié)議的中間變量
          50. unsigned charucSendCntLock=0; //串口計時器的原子鎖
          51. unsigned char ucRcType=0;//數(shù)據(jù)類型
          52. unsigned intuiRcSize=0;//數(shù)據(jù)長度
          53. unsigned char ucRcCy=0;//校驗累加和
          54. unsigned char ucLedLock=0; //原子鎖
          55. unsigned intuiLedCnt=0;//控制Led閃爍的延時計時器
          56. unsigned intuiSendTimeOutCnt=0; //用來識別接收數(shù)據(jù)超時的計時器
          57. unsigned char ucSendTimeOutLock=0; //原子鎖
          58. unsigned char ucStatus=0; //當前狀態(tài)變量 0代表待機 1代表正在通訊過程 2代表發(fā)送出錯
          59. unsigned char ucSendStep=0; //發(fā)送數(shù)據(jù)的過程步驟
          60. unsigned char ucErrorCnt=0; //累計錯誤總數(shù)
          61. unsigned char ucSendTotal=0; //記錄當前已經(jīng)發(fā)送了多少串數(shù)據(jù)
          62. unsigned char ucReceiveStatus=0; //返回的數(shù)據(jù)狀態(tài) 0代表待機 1代表校驗正確 2代表校驗出錯
          63. unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號
          64. unsigned intuiKeyTimeCnt1=0; //按鍵去抖動延時計數(shù)器
          65. unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標志
          66. unsigned intuiKeyTimeCnt2=0; //按鍵去抖動延時計數(shù)器
          67. unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標志
          68. unsigned intuiKeyTimeCnt3=0; //按鍵去抖動延時計數(shù)器
          69. unsigned char ucKeyLock3=0; //按鍵觸發(fā)后自鎖的變量標志
          70. unsigned intuiKeyTimeCnt4=0; //按鍵去抖動延時計數(shù)器
          71. unsigned char ucKeyLock4=0; //按鍵觸發(fā)后自鎖的變量標志
          72. unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續(xù)時間計數(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ù)點是否顯示的標志
          83. unsigned char ucDigDot7;//數(shù)碼管7的小數(shù)點是否顯示的標志
          84. unsigned char ucDigDot6;//數(shù)碼管6的小數(shù)點是否顯示的標志
          85. unsigned char ucDigDot5;//數(shù)碼管5的小數(shù)點是否顯示的標志
          86. unsigned char ucDigDot4;//數(shù)碼管4的小數(shù)點是否顯示的標志
          87. unsigned char ucDigDot3;//數(shù)碼管3的小數(shù)點是否顯示的標志
          88. unsigned char ucDigDot2;//數(shù)碼管2的小數(shù)點是否顯示的標志
          89. unsigned char ucDigDot1;//數(shù)碼管1的小數(shù)點是否顯示的標志
          90. unsigned char ucDigShowTemp=0; //臨時中間變量
          91. unsigned char ucDisplayDriveStep=1;//動態(tài)掃描數(shù)碼管的步驟變量
          92. unsigned char ucWd1Update=1; //窗口1更新顯示標志
          93. unsigned char ucWd2Update=0; //窗口2更新顯示標志
          94. unsigned char ucWd3Update=0; //窗口3更新顯示標志
          95. unsigned char ucWd4Update=0; //窗口4更新顯示標志
          96. unsigned char ucWd=1;//本程序的核心變量,窗口顯示變量。類似于一級菜單的變量。代表顯示不同的窗口。
          97. unsigned intuiSetData1=0;//本程序中需要被設置的參數(shù)1
          98. unsigned intuiSetData2=0;//本程序中需要被設置的參數(shù)2
          99. unsigned intuiSetData3=0;//本程序中需要被設置的參數(shù)3
          100. unsigned intuiSetData4=0;//本程序中需要被設置的參數(shù)4
          101. unsigned char ucTemp1=0;//中間過渡變量
          102. unsigned char ucTemp2=0;//中間過渡變量
          103. unsigned char ucTemp3=0;//中間過渡變量
          104. unsigned char ucTemp4=0;//中間過渡變量
          105. //根據(jù)原理圖得出的共陰數(shù)碼管字模表
          106. code unsigned char dig_table[]=
          107. {
          108. 0x3f,//0 序號0
          109. 0x06,//1 序號1
          110. 0x5b,//2 序號2
          111. 0x4f,//3 序號3
          112. 0x66,//4 序號4
          113. 0x6d,//5 序號5
          114. 0x7d,//6 序號6
          115. 0x07,//7 序號7
          116. 0x7f,//8 序號8
          117. 0x6f,//9 序號9
          118. 0x00,//無 序號10
          119. 0x40,//- 序號11
          120. 0x73,//P 序號12
          121. };
          122. void main()
          123. {
          124. initial_myself();
          125. delay_long(100);
          126. initial_peripheral();
          127. while(1)
          128. {
          129. key_service(); //按鍵服務的應用程序
          130. usart_service();//串口接收服務程序
          131. communication_service(); //一發(fā)一收的通訊服務程序
          132. display_service(); //顯示的窗口菜單服務程序
          133. status_service();//狀態(tài)顯示的應用程序
          134. }
          135. }
          136. void communication_service(void) //一發(fā)一收的通訊服務程序
          137. {
          138. unsigned int i;
          139. if(ucStatus==1)//處于正在通訊的過程中
          140. {
          141. switch(ucSendStep)
          142. {
          143. case 0: //通訊過程0發(fā)送一串數(shù)據(jù)
          144. switch(ucSendTotal)//根據(jù)當前已經(jīng)發(fā)送到第幾條數(shù)據(jù)來決定發(fā)送哪些參數(shù)
          145. {
          146. case 0: //發(fā)送參數(shù)1
          147. ucSendregBuf[0]=0xeb; //把準備發(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個字節(jié)的有效數(shù)據(jù)
          153. ucSendregBuf[6]=uiSetData1>>8;//把int類型的參數(shù)分解成兩個字節(jié)的數(shù)據(jù)
          154. ucSendregBuf[7]=uiSetData1;
          155. break;
          156. case 1://發(fā)送參數(shù)2
          157. ucSendregBuf[0]=0xeb; //把準備發(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個字節(jié)的有效數(shù)據(jù)
          163. ucSendregBuf[6]=uiSetData2>>8;//把int類型的參數(shù)分解成兩個字節(jié)的數(shù)據(jù)
          164. ucSendregBuf[7]=uiSetData2;
          165. break;
          166. case 2://發(fā)送參數(shù)3
          167. ucSendregBuf[0]=0xeb; //把準備發(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個字節(jié)的有效數(shù)據(jù)
          173. ucSendregBuf[6]=uiSetData3>>8;//把int類型的參數(shù)分解成兩個字節(jié)的數(shù)據(jù)
          174. ucSendregBuf[7]=uiSetData3;
          175. break;
          176. case 3://發(fā)送參數(shù)4
          177. ucSendregBuf[0]=0xeb; //把準備發(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個字節(jié)的有效數(shù)據(jù)
          183. ucSendregBuf[6]=uiSetData4>>8;//把int類型的參數(shù)分解成兩個字節(jié)的數(shù)據(jù)
          184. ucSendregBuf[7]=uiSetData4;
          185. break;
          186. }
          187. ucSendregBuf[8]=0x00;
          188. for(i=0;i<8;i++)//最后一個字節(jié)是校驗和,是前面所有字節(jié)累加,溢出部分不用我們管,系統(tǒng)會有規(guī)律的自動處理
          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ā)送給下位機
          195. }
          196. ucSendTimeOutLock=1; //原子鎖加鎖
          197. uiSendTimeOutCnt=0;//超時計時器計時清零
          198. ucSendTimeOutLock=0; //原子鎖解鎖
          199. ucReceiveStatus=0;//返回的數(shù)據(jù)狀態(tài)清零
          200. ucSendStep=1;//切換到下一個步驟,等待返回的數(shù)據(jù)
          201. break;
          202. case 1: //通訊過程1判斷返回的指令
          203. if(ucReceiveStatus==1)//校驗正確
          204. {
          205. ucErrorCnt=0; //累計校驗錯誤總數(shù)清零
          206. ucSendTotal++;//累加當前發(fā)送了多少串數(shù)據(jù)
          207. if(ucSendTotal>=4) //已經(jīng)發(fā)送完全部4串數(shù)據(jù),結束
          208. {
          209. ucStatus=0;//切換到結束時的待機狀態(tài)
          210. }
          211. else//還沒發(fā)送完4串數(shù)據(jù),則繼續(xù)發(fā)送下一串新數(shù)據(jù)
          212. {
          213. ucSendStep=0;//返回上一個步驟,繼續(xù)發(fā)送新數(shù)據(jù)
          214. }
          215. }
          216. else if(ucReceiveStatus==2||uiSendTimeOutCnt>const_send_time_out)//校驗出錯或者超時出錯
          217. {
          218. ucErrorCnt++; //累計錯誤總數(shù)
          219. if(ucErrorCnt>=3)//累加重發(fā)次數(shù)3次以上,則報錯
          220. {
          221. ucStatus=2;//切換到出錯報警狀態(tài)
          222. }
          223. else//重發(fā)還沒超過3次,繼續(xù)返回重發(fā)
          224. {
          225. ucSendStep=0;//返回上一個步驟,重發(fā)一次數(shù)據(jù)
          226. }
          227. }
          228. break;
          229. }
          230. }
          231. }
          232. void status_service(void)//狀態(tài)顯示的應用程序
          233. {
          234. if(ucStatus!=0) //處于非待機的狀態(tài),Led閃爍
          235. {
          236. if(uiLedCnt
          237. {
          238. led_dr=1;//前半秒亮
          239. if(ucStatus==2)//處于發(fā)送數(shù)據(jù)出錯的狀態(tài),則蜂鳴器間歇鳴叫報警
          240. {
          241. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          242. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
          243. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          244. }
          245. }
          246. else if(uiLedCnt
          247. {
          248. led_dr=0; //前半秒滅
          249. }
          250. else
          251. {
          252. ucLedLock=1; //原子鎖加鎖
          253. uiLedCnt=0; //延時計時器清零,讓Led燈處于閃爍的反復循環(huán)中
          254. ucLedLock=0; //原子鎖解鎖
          255. }
          256. }
          257. else//處于待機狀態(tài),Led一直亮
          258. {
          259. led_dr=1;
          260. }
          261. }
          262. void usart_service(void)//串口接收服務程序,在main函數(shù)里
          263. {
          264. unsigned int i;
          265. if(uiSendCnt>=const_receive_time&&ucSendLock==1) //說明超過了一定的時間內(nèi),再也沒有新數(shù)據(jù)從串口來
          266. {
          267. ucSendLock=0; //處理一次就鎖起來,不用每次都進來,除非有新接收的數(shù)據(jù)
          268. //下面的代碼進入數(shù)據(jù)協(xié)議解析和數(shù)據(jù)處理的階段
          269. uiRcMoveIndex=0; //由于是判斷數(shù)據(jù)頭,所以下標移動變量從數(shù)組的0開始向最尾端移動
          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ù)類型一個字節(jié)
          275. uiRcSize=ucRcregBuf[uiRcMoveIndex+4]; //數(shù)據(jù)長度兩個字節(jié)
          276. uiRcSize=uiRcSize<<8;
          277. uiRcSize=uiRcSize+ucRcregBuf[uiRcMoveIndex+5];
          278. ucRcCy=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]; //記錄最后一個字節(jié)的校驗
          279. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=0;//清零最后一個字節(jié)的累加和變量
          280. for(i=0;i<(3+1+2+uiRcSize);i++) //計算校驗累加和
          281. {
          282. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]+ucRcregBuf[uiRcMoveIndex+i];
          283. }
          284. if(ucRcCy==ucRcregBuf[uiRcMoveIndex+6+uiRcSize])//如果一串數(shù)據(jù)校驗正確,則進入以下數(shù)據(jù)指令的判斷
          285. {
          286. switch(ucRcType) //根據(jù)不同的數(shù)據(jù)類型來做不同的數(shù)據(jù)處理
          287. {
          288. case 0xf5: //返回的是正確的校驗指令
          289. ucReceiveStatus=1;//代表校驗正確
          290. break;
          291. case 0xfa: //返回的是錯誤的校驗指令
          292. ucReceiveStatus=2;//代表校驗錯誤
          293. break;
          294. }
          295. }
          296. break; //退出循環(huán)
          297. }
          298. uiRcMoveIndex++; //因為是判斷數(shù)據(jù)頭,游標向著數(shù)組最尾端的方向移動
          299. }
          300. uiRcregTotal=0;//清空緩沖的下標,方便下次重新從0下標開始接受新數(shù)據(jù)
          301. }
          302. }
          303. void eusart_send(unsigned char ucSendData) //發(fā)送一個字節(jié),內(nèi)部自帶每個字節(jié)之間的delay延時
          304. {
          305. ES = 0; //關串口中斷
          306. TI = 0; //清零串口發(fā)送完成中斷請求標志
          307. SBUF =ucSendData; //發(fā)送一個字節(jié)
          308. delay_short(400);//每個字節(jié)之間的延時,這里非常關鍵,也是最容易出錯的地方。延時的大小請根據(jù)實際項目來調(diào)整
          309. TI = 0; //清零串口發(fā)送完成中斷請求標志
          310. ES = 1; //允許串口中斷
          311. }
          312. void display_service(void) //顯示的窗口菜單服務程序
          313. {
          314. switch(ucWd)//本程序的核心變量,窗口顯示變量。類似于一級菜單的變量。代表顯示不同的窗口。
          315. {
          316. case 1: //顯示P--1窗口的數(shù)據(jù)
          317. if(ucWd1Update==1)//窗口1要全部更新顯示
          318. {
          319. ucWd1Update=0;//及時清零標志,避免一直進來掃描
          320. ucDigShow8=12;//第8位數(shù)碼管顯示P
          321. ucDigShow7=11;//第7位數(shù)碼管顯示-
          322. ucDigShow6=1; //第6位數(shù)碼管顯示1
          323. ucDigShow5=10;//第5位數(shù)碼管顯示無
          324. //先分解數(shù)據(jù)
          325. ucTemp4=uiSetData1/1000;
          326. ucTemp3=uiSetData1%1000/100;
          327. ucTemp2=uiSetData1%100/10;
          328. ucTemp1=uiSetData1%10;
          329. //再過渡需要顯示的數(shù)據(jù)到緩沖變量里,讓過渡的時間越短越好
          330. if(uiSetData1<1000)
          331. {
          332. ucDigShow4=10;//如果小于1000,千位顯示無
          333. }
          334. else
          335. {
          336. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
          337. }
          338. if(uiSetData1<100)
          339. {
          340. ucDigShow3=10;//如果小于100,百位顯示無
          341. }
          342. else
          343. {
          344. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
          345. }
          346. if(uiSetData1<10)
          347. {
          348. ucDigShow2=10;//如果小于10,十位顯示無
          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;//及時清零標志,避免一直進來掃描
          361. ucDigShow8=12;//第8位數(shù)碼管顯示P
          362. ucDigShow7=11;//第7位數(shù)碼管顯示-
          363. ucDigShow6=2;//第6位數(shù)碼管顯示2
          364. ucDigShow5=10; //第5位數(shù)碼管顯示無
          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,千位顯示無
          372. }
          373. else
          374. {
          375. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
          376. }
          377. if(uiSetData2<100)
          378. {
          379. ucDigShow3=10;//如果小于100,百位顯示無
          380. }
          381. else
          382. {
          383. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
          384. }
          385. if(uiSetData2<10)
          386. {
          387. ucDigShow2=10;//如果小于10,十位顯示無
          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;//及時清零標志,避免一直進來掃描
          400. ucDigShow8=12;//第8位數(shù)碼管顯示P
          401. ucDigShow7=11;//第7位數(shù)碼管顯示-
          402. ucDigShow6=3;//第6位數(shù)碼管顯示3
          403. ucDigShow5=10; //第5位數(shù)碼管顯示無
          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,千位顯示無
          411. }
          412. else
          413. {
          414. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
          415. }
          416. if(uiSetData3<100)
          417. {
          418. ucDigShow3=10;//如果小于100,百位顯示無
          419. }
          420. else
          421. {
          422. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
          423. }
          424. if(uiSetData3<10)
          425. {
          426. ucDigShow2=10;//如果小于10,十位顯示無
          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;//及時清零標志,避免一直進來掃描
          439. ucDigShow8=12;//第8位數(shù)碼管顯示P
          440. ucDigShow7=11;//第7位數(shù)碼管顯示-
          441. ucDigShow6=4;//第6位數(shù)碼管顯示4
          442. ucDigShow5=10; //第5位數(shù)碼管顯示無
          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,千位顯示無
          450. }
          451. else
          452. {
          453. ucDigShow4=ucTemp4;//第4位數(shù)碼管要顯示的內(nèi)容
          454. }
          455. if(uiSetData4<100)
          456. {
          457. ucDigShow3=10;//如果小于100,百位顯示無
          458. }
          459. else
          460. {
          461. ucDigShow3=ucTemp3;//第3位數(shù)碼管要顯示的內(nèi)容
          462. }
          463. if(uiSetData4<10)
          464. {
          465. ucDigShow2=10;//如果小于10,十位顯示無
          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ù) 放在定時中斷里
          477. {
          478. if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
          479. {
          480. ucKeyLock1=0; //按鍵自鎖標志清零
          481. uiKeyTimeCnt1=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
          482. }
          483. else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
          484. {
          485. uiKeyTimeCnt1++; //累加定時中斷次數(shù)
          486. if(uiKeyTimeCnt1>const_key_time1)
          487. {
          488. uiKeyTimeCnt1=0;
          489. ucKeyLock1=1;//自鎖按鍵置位,避免一直觸發(fā)
          490. ucKeySec=1; //觸發(fā)1號鍵
          491. }
          492. }
          493. if(key_sr2==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
          494. {
          495. ucKeyLock2=0; //按鍵自鎖標志清零
          496. uiKeyTimeCnt2=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
          497. }
          498. else if(ucKeyLock2==0)//有按鍵按下,且是第一次被按下
          499. {
          500. uiKeyTimeCnt2++; //累加定時中斷次數(shù)
          501. if(uiKeyTimeCnt2>const_key_time2)
          502. {
          503. uiKeyTimeCnt2=0;
          504. ucKeyLock2=1;//自鎖按鍵置位,避免一直觸發(fā)
          505. ucKeySec=2; //觸發(fā)2號鍵
          506. }
          507. }
          508. if(key_sr3==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
          509. {
          510. ucKeyLock3=0; //按鍵自鎖標志清零
          511. uiKeyTimeCnt3=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
          512. }
          513. else if(ucKeyLock3==0)//有按鍵按下,且是第一次被按下
          514. {
          515. uiKeyTimeCnt3++; //累加定時中斷次數(shù)
          516. if(uiKeyTimeCnt3>const_key_time3)
          517. {
          518. uiKeyTimeCnt3=0;
          519. ucKeyLock3=1;//自鎖按鍵置位,避免一直觸發(fā)
          520. ucKeySec=3; //觸發(fā)3號鍵
          521. }
          522. }
          523. if(key_sr4==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
          524. {
          525. ucKeyLock4=0; //按鍵自鎖標志清零
          526. uiKeyTimeCnt4=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。
          527. }
          528. else if(ucKeyLock4==0)//有按鍵按下,且是第一次被按下
          529. {
          530. uiKeyTimeCnt4++; //累加定時中斷次數(shù)
          531. if(uiKeyTimeCnt4>const_key_time4)
          532. {
          533. uiKeyTimeCnt4=0;
          534. ucKeyLock4=1;//自鎖按鍵置位,避免一直觸發(fā)
          535. ucKeySec=4; //觸發(fā)4號鍵
          536. }
          537. }
          538. }
          539. void key_service(void) //按鍵服務的應用程序
          540. {
          541. switch(ucKeySec) //按鍵服務狀態(tài)切換
          542. {
          543. case 1:// 加按鍵 對應朱兆祺學習板的S1鍵
          544. switch(ucWd)//在不同的窗口下,設置不同的參數(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;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          580. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
          581. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          582. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(fā)
          583. break;
          584. case 2:// 減按鍵 對應朱兆祺學習板的S5鍵
          585. switch(ucWd)//在不同的窗口下,設置不同的參數(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;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          621. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
          622. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          623. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(fā)
          624. break;
          625. case 3:// 切換窗口按鍵 對應朱兆祺學習板的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;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          647. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
          648. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          649. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(fā)
          650. break;
          651. case 4:// 啟動發(fā)送數(shù)據(jù)和復位按鍵 對應朱兆祺學習板的S13鍵
          652. switch(ucStatus)//在不同的狀態(tài)下,進行不同的操作
          653. {
          654. case 0://處于待機狀態(tài),則啟動發(fā)送數(shù)據(jù)
          655. ucErrorCnt=0; //累計錯誤總數(shù)清零
          656. ucSendTotal=0; //已經(jīng)發(fā)送串數(shù)據(jù)總數(shù)清零
          657. ucSendStep=0; //發(fā)送數(shù)據(jù)的過程步驟清零,返回開始的步驟待命
          658. ucStatus=1; //啟動發(fā)送數(shù)據(jù),1代表正在通訊過程
          659. break;
          660. case 1://處于正在通訊的過程
          661. break;
          662. case 2: //發(fā)送數(shù)據(jù)出錯,比如中間超時沒有接收到數(shù)據(jù)
          663. ucStatus=0; //切換回待機的狀態(tài)
          664. break;
          665. }
          666. ucVoiceLock=1;//原子鎖加鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          667. uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
          668. ucVoiceLock=0;//原子鎖解鎖,保護主函數(shù)與中斷函數(shù)的共享變量uiVoiceCnt
          669. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發(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ù)點
          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ù)點
          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ù)點
          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ù)點
          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ù)點
          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ù)點
          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ù)點
          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ù)點
          739. }
          740. dig_hc595_drive(ucDigShowTemp,0x7f);
          741. break;
          742. }
          743. ucDisplayDriveStep++;
          744. if(ucDisplayDriveStep>8)//掃描完8個數(shù)碼管后,重新從第一個開始掃描
          745. {
          746. ucDisplayDriveStep=1;
          747. }
          748. }
          749. //數(shù)碼管的74HC595驅(qū)動函數(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引腳把兩個寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來
          779. delay_short(1);
          780. dig_hc595_st_dr=1;
          781. delay_short(1);
          782. dig_hc595_sh_dr=0; //拉低,抗干擾就增強
          783. dig_hc595_st_dr=0;
          784. dig_hc595_ds_dr=0;
          785. }
          786. //LED燈的74HC595驅(qū)動函數(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引腳把兩個寄存器的數(shù)據(jù)更新輸出到74HC595的輸出引腳上并且鎖存起來
          816. delay_short(1);
          817. hc595_st_dr=1;
          818. delay_short(1);
          819. hc595_sh_dr=0; //拉低,抗干擾就增強
          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)//超過緩沖區(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ù)據(jù)還沒發(fā)送完畢,那么它永遠也長不大,因為每個串口接收中斷它都被清零。
          838. ucSendCntLock=0; //解鎖
          839. }
          840. }
          841. else//我在其它單片機上都不用else這段代碼的,可能在51單片機上多增加" TI = 0;"穩(wěn)定性會更好吧。
          842. {
          843. TI = 0;//如果不是串口接收中斷,那么必然是串口發(fā)送中斷,及時清除發(fā)送中斷的標志,否則一直發(fā)送中斷
          844. }
          845. }
          846. void T0_time(void) interrupt 1 //定時中斷
          847. {
          848. TF0=0;//清除中斷標志
          849. TR0=0; //關中斷
          850. /* 注釋一:
          851. * 此處多增加一個原子鎖,作為中斷與主函數(shù)共享數(shù)據(jù)的保護,實際上是借鑒了"紅金龍吸味"關于原子鎖的建議.
          852. */
          853. if(ucSendCntLock==0)//原子鎖判斷
          854. {
          855. ucSendCntLock=1; //加鎖
          856. if(uiSendCnt
          857. {
          858. uiSendCnt++; //表面上這個數(shù)據(jù)不斷累加,但是在串口中斷里,每接收一個字節(jié)它都會被清零,除非這個中間沒有串口數(shù)據(jù)過來
          859. ucSendLock=1; //開自鎖標志
          860. }
          861. ucSendCntLock=0; //解鎖
          862. }
          863. if(ucVoiceLock==0) //原子鎖判斷
          864. {
          865. if(uiVoiceCnt!=0)
          866. {
          867. uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
          868. beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
          869. }
          870. else
          871. {
          872. ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
          873. beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
          874. }
          875. }
          876. if(ucStatus!=0) //處于非待機的狀態(tài),Led閃爍
          877. {
          878. if(ucLedLock==0)//原子鎖判斷
          879. {
          880. uiLedCnt++; //Led閃爍計時器不斷累加
          881. }
          882. }
          883. if(ucStatus==1) //處于正在通訊的狀態(tài),
          884. {
          885. if(ucSendTimeOutLock==0)//原子鎖判斷
          886. {
          887. uiSendTimeOutCnt++; //超時計時器累加
          888. }
          889. }
          890. key_scan(); //按鍵掃描函數(shù)
          891. display_drive();//數(shù)碼管字模的驅(qū)動函數(shù)
          892. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
          893. TL0=0x0b;
          894. TR0=1;//開中斷
          895. }
          896. void delay_short(unsigned int uiDelayShort)
          897. {
          898. unsigned int i;
          899. for(i=0;i
          900. {
          901. ; //一個分號相當于執(zhí)行一條空語句
          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. ; //一個分號相當于執(zhí)行一條空語句
          913. }
          914. }
          915. }
          916. void initial_myself(void)//第一區(qū) 初始化單片機
          917. {
          918. /* 注釋二:
          919. * 矩陣鍵盤也可以做獨立按鍵,前提是把某一根公共輸出線輸出低電平,
          920. * 模擬獨立按鍵的觸發(fā)地,本程序中,把key_gnd_dr輸出低電平。
          921. * 朱兆祺51學習板的S1就是本程序中用到的一個獨立按鍵。
          922. */
          923. key_gnd_dr=0; //模擬獨立按鍵的地GND,因此必須一直輸出低電平
          924. led_dr=1;//點亮獨立LED燈
          925. beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時不叫。
          926. hc595_drive(0x00,0x00);//關閉所有經(jīng)過另外兩個74HC595驅(qū)動的LED燈
          927. TMOD=0x01;//設置定時器0為工作方式1
          928. TH0=0xfe; //重裝初始值(65535-500)=65035=0xfe0b
          929. TL0=0x0b;
          930. //配置串口
          931. SCON=0x50;
          932. TMOD=0X21;
          933. /* 注釋三:
          934. * 為了保證串口中斷接收的數(shù)據(jù)不丟失,必須設置IP = 0x10,相當于把串口中斷設置為最高優(yōu)先級,
          935. * 這個時候,串口中斷可以打斷任何其他的中斷服務函數(shù)實現(xiàn)嵌套,
          936. */
          937. IP =0x10;//把串口中斷設置為最高優(yōu)先級,必須的。
          938. TH1=TL1=-(11059200L/12/32/9600);//串口波特率為9600。
          939. TR1=1;
          940. }
          941. void initial_peripheral(void) //第二區(qū) 初始化外圍
          942. {
          943. ucDigDot8=0; //小數(shù)點全部不顯示
          944. ucDigDot7=0;
          945. ucDigDot6=0;
          946. ucDigDot5=0;
          947. ucDigDot4=0;
          948. ucDigDot3=0;
          949. ucDigDot2=0;
          950. ucDigDot1=0;
          951. EA=1; //開總中斷
          952. ES=1; //允許串口中斷
          953. ET0=1; //允許定時中斷
          954. TR0=1; //啟動定時中斷
          955. }


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


          評論


          技術專區(qū)

          關閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();