新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 第45節:主機的串口收發綜合程序框架

        第45節:主機的串口收發綜合程序框架

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

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

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

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

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

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

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

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

        (3)源代碼講解如下:
        1. #include "REG52.H"
        2. #define const_voice_short40 //蜂鳴器短叫的持續時間
        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//接收串口中斷數據的緩沖區數組大小
        11. #define const_receive_time5//如果超過這個時間沒有串口數據過來,就認為一串數據已經全部接收完,這個時間根據實際情況來調整大小
        12. #define const_send_size10//串口發送數據的緩沖區數組大小
        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. //驅動數碼管的74HC595
        18. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
        19. void display_drive(void); //顯示數碼管字模的驅動函數
        20. void display_service(void); //顯示的窗口菜單服務程序
        21. //驅動LED的74HC595
        22. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
        23. void T0_time(void);//定時中斷函數
        24. void usart_receive(void); //串口接收中斷函數
        25. void usart_service(void);//串口接收服務程序,在main函數里
        26. void communication_service(void); //一發一收的通訊服務程序
        27. void eusart_send(unsigned char ucSendData); //發送一個字節,內部自帶每個字節之間的delay延時
        28. void key_service(void); //按鍵服務的應用程序
        29. void key_scan(void);//按鍵掃描函數 放在定時中斷里
        30. void status_service(void);//狀態顯示的應用程序
        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; //蜂鳴器的驅動IO口
        37. sbit led_dr=P3^5;//作為狀態指示燈 亮的時候表示待機狀態.閃爍表示非待機狀態,處于正在發送數據或者出錯的狀態
        38. sbit dig_hc595_sh_dr=P2^0; //數碼管的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]; //發送的緩沖區數組
        45. unsigned intuiSendCnt=0; //用來識別串口是否接收完一串數據的計時器
        46. unsigned char ucSendLock=1; //串口服務程序的自鎖變量,每次接收完一串數據只處理一次
        47. unsigned intuiRcregTotal=0;//代表當前緩沖區已經接收了多少個數據
        48. unsigned char ucRcregBuf[const_rc_size]; //接收串口中斷數據的緩沖區數組
        49. unsigned intuiRcMoveIndex=0;//用來解析數據協議的中間變量
        50. unsigned charucSendCntLock=0; //串口計時器的原子鎖
        51. unsigned char ucRcType=0;//數據類型
        52. unsigned intuiRcSize=0;//數據長度
        53. unsigned char ucRcCy=0;//校驗累加和
        54. unsigned char ucLedLock=0; //原子鎖
        55. unsigned intuiLedCnt=0;//控制Led閃爍的延時計時器
        56. unsigned intuiSendTimeOutCnt=0; //用來識別接收數據超時的計時器
        57. unsigned char ucSendTimeOutLock=0; //原子鎖
        58. unsigned char ucStatus=0; //當前狀態變量 0代表待機 1代表正在通訊過程 2代表發送出錯
        59. unsigned char ucSendStep=0; //發送數據的過程步驟
        60. unsigned char ucErrorCnt=0; //累計錯誤總數
        61. unsigned char ucSendTotal=0; //記錄當前已經發送了多少串數據
        62. unsigned char ucReceiveStatus=0; //返回的數據狀態 0代表待機 1代表校驗正確 2代表校驗出錯
        63. unsigned char ucKeySec=0; //被觸發的按鍵編號
        64. unsigned intuiKeyTimeCnt1=0; //按鍵去抖動延時計數器
        65. unsigned char ucKeyLock1=0; //按鍵觸發后自鎖的變量標志
        66. unsigned intuiKeyTimeCnt2=0; //按鍵去抖動延時計數器
        67. unsigned char ucKeyLock2=0; //按鍵觸發后自鎖的變量標志
        68. unsigned intuiKeyTimeCnt3=0; //按鍵去抖動延時計數器
        69. unsigned char ucKeyLock3=0; //按鍵觸發后自鎖的變量標志
        70. unsigned intuiKeyTimeCnt4=0; //按鍵去抖動延時計數器
        71. unsigned char ucKeyLock4=0; //按鍵觸發后自鎖的變量標志
        72. unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續時間計數器
        73. unsigned charucVoiceLock=0;//蜂鳴器鳴叫的原子鎖
        74. unsigned char ucDigShow8;//第8位數碼管要顯示的內容
        75. unsigned char ucDigShow7;//第7位數碼管要顯示的內容
        76. unsigned char ucDigShow6;//第6位數碼管要顯示的內容
        77. unsigned char ucDigShow5;//第5位數碼管要顯示的內容
        78. unsigned char ucDigShow4;//第4位數碼管要顯示的內容
        79. unsigned char ucDigShow3;//第3位數碼管要顯示的內容
        80. unsigned char ucDigShow2;//第2位數碼管要顯示的內容
        81. unsigned char ucDigShow1;//第1位數碼管要顯示的內容
        82. unsigned char ucDigDot8;//數碼管8的小數點是否顯示的標志
        83. unsigned char ucDigDot7;//數碼管7的小數點是否顯示的標志
        84. unsigned char ucDigDot6;//數碼管6的小數點是否顯示的標志
        85. unsigned char ucDigDot5;//數碼管5的小數點是否顯示的標志
        86. unsigned char ucDigDot4;//數碼管4的小數點是否顯示的標志
        87. unsigned char ucDigDot3;//數碼管3的小數點是否顯示的標志
        88. unsigned char ucDigDot2;//數碼管2的小數點是否顯示的標志
        89. unsigned char ucDigDot1;//數碼管1的小數點是否顯示的標志
        90. unsigned char ucDigShowTemp=0; //臨時中間變量
        91. unsigned char ucDisplayDriveStep=1;//動態掃描數碼管的步驟變量
        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;//本程序中需要被設置的參數1
        98. unsigned intuiSetData2=0;//本程序中需要被設置的參數2
        99. unsigned intuiSetData3=0;//本程序中需要被設置的參數3
        100. unsigned intuiSetData4=0;//本程序中需要被設置的參數4
        101. unsigned char ucTemp1=0;//中間過渡變量
        102. unsigned char ucTemp2=0;//中間過渡變量
        103. unsigned char ucTemp3=0;//中間過渡變量
        104. unsigned char ucTemp4=0;//中間過渡變量
        105. //根據原理圖得出的共陰數碼管字模表
        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(); //一發一收的通訊服務程序
        132. display_service(); //顯示的窗口菜單服務程序
        133. status_service();//狀態顯示的應用程序
        134. }
        135. }
        136. void communication_service(void) //一發一收的通訊服務程序
        137. {
        138. unsigned int i;
        139. if(ucStatus==1)//處于正在通訊的過程中
        140. {
        141. switch(ucSendStep)
        142. {
        143. case 0: //通訊過程0發送一串數據
        144. switch(ucSendTotal)//根據當前已經發送到第幾條數據來決定發送哪些參數
        145. {
        146. case 0: //發送參數1
        147. ucSendregBuf[0]=0xeb; //把準備發送的數據放入發送緩沖區
        148. ucSendregBuf[1]=0x00;
        149. ucSendregBuf[2]=0x55;
        150. ucSendregBuf[3]=0x01; //代表發送參數1
        151. ucSendregBuf[4]=0x00;
        152. ucSendregBuf[5]=0x02; //代表發送2個字節的有效數據
        153. ucSendregBuf[6]=uiSetData1>>8;//把int類型的參數分解成兩個字節的數據
        154. ucSendregBuf[7]=uiSetData1;
        155. break;
        156. case 1://發送參數2
        157. ucSendregBuf[0]=0xeb; //把準備發送的數據放入發送緩沖區
        158. ucSendregBuf[1]=0x00;
        159. ucSendregBuf[2]=0x55;
        160. ucSendregBuf[3]=0x02; //代表發送參數2
        161. ucSendregBuf[4]=0x00;
        162. ucSendregBuf[5]=0x02; //代表發送2個字節的有效數據
        163. ucSendregBuf[6]=uiSetData2>>8;//把int類型的參數分解成兩個字節的數據
        164. ucSendregBuf[7]=uiSetData2;
        165. break;
        166. case 2://發送參數3
        167. ucSendregBuf[0]=0xeb; //把準備發送的數據放入發送緩沖區
        168. ucSendregBuf[1]=0x00;
        169. ucSendregBuf[2]=0x55;
        170. ucSendregBuf[3]=0x03; //代表發送參數3
        171. ucSendregBuf[4]=0x00;
        172. ucSendregBuf[5]=0x02; //代表發送2個字節的有效數據
        173. ucSendregBuf[6]=uiSetData3>>8;//把int類型的參數分解成兩個字節的數據
        174. ucSendregBuf[7]=uiSetData3;
        175. break;
        176. case 3://發送參數4
        177. ucSendregBuf[0]=0xeb; //把準備發送的數據放入發送緩沖區
        178. ucSendregBuf[1]=0x00;
        179. ucSendregBuf[2]=0x55;
        180. ucSendregBuf[3]=0x04; //代表發送參數4
        181. ucSendregBuf[4]=0x00;
        182. ucSendregBuf[5]=0x02; //代表發送2個字節的有效數據
        183. ucSendregBuf[6]=uiSetData4>>8;//把int類型的參數分解成兩個字節的數據
        184. ucSendregBuf[7]=uiSetData4;
        185. break;
        186. }
        187. ucSendregBuf[8]=0x00;
        188. for(i=0;i<8;i++)//最后一個字節是校驗和,是前面所有字節累加,溢出部分不用我們管,系統會有規律的自動處理
        189. {
        190. ucSendregBuf[8]=ucSendregBuf[8]+ucSendregBuf[i];
        191. }
        192. for(i=0;i<9;i++)
        193. {
        194. eusart_send(ucSendregBuf[i]);//把一串完整的數據發送給下位機
        195. }
        196. ucSendTimeOutLock=1; //原子鎖加鎖
        197. uiSendTimeOutCnt=0;//超時計時器計時清零
        198. ucSendTimeOutLock=0; //原子鎖解鎖
        199. ucReceiveStatus=0;//返回的數據狀態清零
        200. ucSendStep=1;//切換到下一個步驟,等待返回的數據
        201. break;
        202. case 1: //通訊過程1判斷返回的指令
        203. if(ucReceiveStatus==1)//校驗正確
        204. {
        205. ucErrorCnt=0; //累計校驗錯誤總數清零
        206. ucSendTotal++;//累加當前發送了多少串數據
        207. if(ucSendTotal>=4) //已經發送完全部4串數據,結束
        208. {
        209. ucStatus=0;//切換到結束時的待機狀態
        210. }
        211. else//還沒發送完4串數據,則繼續發送下一串新數據
        212. {
        213. ucSendStep=0;//返回上一個步驟,繼續發送新數據
        214. }
        215. }
        216. else if(ucReceiveStatus==2||uiSendTimeOutCnt>const_send_time_out)//校驗出錯或者超時出錯
        217. {
        218. ucErrorCnt++; //累計錯誤總數
        219. if(ucErrorCnt>=3)//累加重發次數3次以上,則報錯
        220. {
        221. ucStatus=2;//切換到出錯報警狀態
        222. }
        223. else//重發還沒超過3次,繼續返回重發
        224. {
        225. ucSendStep=0;//返回上一個步驟,重發一次數據
        226. }
        227. }
        228. break;
        229. }
        230. }
        231. }
        232. void status_service(void)//狀態顯示的應用程序
        233. {
        234. if(ucStatus!=0) //處于非待機的狀態,Led閃爍
        235. {
        236. if(uiLedCnt
        237. {
        238. led_dr=1;//前半秒亮
        239. if(ucStatus==2)//處于發送數據出錯的狀態,則蜂鳴器間歇鳴叫報警
        240. {
        241. ucVoiceLock=1;//原子鎖加鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        242. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        243. ucVoiceLock=0;//原子鎖解鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        244. }
        245. }
        246. else if(uiLedCnt
        247. {
        248. led_dr=0; //前半秒滅
        249. }
        250. else
        251. {
        252. ucLedLock=1; //原子鎖加鎖
        253. uiLedCnt=0; //延時計時器清零,讓Led燈處于閃爍的反復循環中
        254. ucLedLock=0; //原子鎖解鎖
        255. }
        256. }
        257. else//處于待機狀態,Led一直亮
        258. {
        259. led_dr=1;
        260. }
        261. }
        262. void usart_service(void)//串口接收服務程序,在main函數里
        263. {
        264. unsigned int i;
        265. if(uiSendCnt>=const_receive_time&&ucSendLock==1) //說明超過了一定的時間內,再也沒有新數據從串口來
        266. {
        267. ucSendLock=0; //處理一次就鎖起來,不用每次都進來,除非有新接收的數據
        268. //下面的代碼進入數據協議解析和數據處理的階段
        269. uiRcMoveIndex=0; //由于是判斷數據頭,所以下標移動變量從數組的0開始向最尾端移動
        270. while(uiRcregTotal>=5&&uiRcMoveIndex<=(uiRcregTotal-5))
        271. {
        272. if(ucRcregBuf[uiRcMoveIndex+0]==0xeb&&ucRcregBuf[uiRcMoveIndex+1]==0x00&&ucRcregBuf[uiRcMoveIndex+2]==0x55)//數據頭eb 00 55的判斷
        273. {
        274. ucRcType=ucRcregBuf[uiRcMoveIndex+3]; //數據類型一個字節
        275. uiRcSize=ucRcregBuf[uiRcMoveIndex+4]; //數據長度兩個字節
        276. uiRcSize=uiRcSize<<8;
        277. uiRcSize=uiRcSize+ucRcregBuf[uiRcMoveIndex+5];
        278. ucRcCy=ucRcregBuf[uiRcMoveIndex+6+uiRcSize]; //記錄最后一個字節的校驗
        279. ucRcregBuf[uiRcMoveIndex+6+uiRcSize]=0;//清零最后一個字節的累加和變量
        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])//如果一串數據校驗正確,則進入以下數據指令的判斷
        285. {
        286. switch(ucRcType) //根據不同的數據類型來做不同的數據處理
        287. {
        288. case 0xf5: //返回的是正確的校驗指令
        289. ucReceiveStatus=1;//代表校驗正確
        290. break;
        291. case 0xfa: //返回的是錯誤的校驗指令
        292. ucReceiveStatus=2;//代表校驗錯誤
        293. break;
        294. }
        295. }
        296. break; //退出循環
        297. }
        298. uiRcMoveIndex++; //因為是判斷數據頭,游標向著數組最尾端的方向移動
        299. }
        300. uiRcregTotal=0;//清空緩沖的下標,方便下次重新從0下標開始接受新數據
        301. }
        302. }
        303. void eusart_send(unsigned char ucSendData) //發送一個字節,內部自帶每個字節之間的delay延時
        304. {
        305. ES = 0; //關串口中斷
        306. TI = 0; //清零串口發送完成中斷請求標志
        307. SBUF =ucSendData; //發送一個字節
        308. delay_short(400);//每個字節之間的延時,這里非常關鍵,也是最容易出錯的地方。延時的大小請根據實際項目來調整
        309. TI = 0; //清零串口發送完成中斷請求標志
        310. ES = 1; //允許串口中斷
        311. }
        312. void display_service(void) //顯示的窗口菜單服務程序
        313. {
        314. switch(ucWd)//本程序的核心變量,窗口顯示變量。類似于一級菜單的變量。代表顯示不同的窗口。
        315. {
        316. case 1: //顯示P--1窗口的數據
        317. if(ucWd1Update==1)//窗口1要全部更新顯示
        318. {
        319. ucWd1Update=0;//及時清零標志,避免一直進來掃描
        320. ucDigShow8=12;//第8位數碼管顯示P
        321. ucDigShow7=11;//第7位數碼管顯示-
        322. ucDigShow6=1; //第6位數碼管顯示1
        323. ucDigShow5=10;//第5位數碼管顯示無
        324. //先分解數據
        325. ucTemp4=uiSetData1/1000;
        326. ucTemp3=uiSetData1%1000/100;
        327. ucTemp2=uiSetData1%100/10;
        328. ucTemp1=uiSetData1%10;
        329. //再過渡需要顯示的數據到緩沖變量里,讓過渡的時間越短越好
        330. if(uiSetData1<1000)
        331. {
        332. ucDigShow4=10;//如果小于1000,千位顯示無
        333. }
        334. else
        335. {
        336. ucDigShow4=ucTemp4;//第4位數碼管要顯示的內容
        337. }
        338. if(uiSetData1<100)
        339. {
        340. ucDigShow3=10;//如果小于100,百位顯示無
        341. }
        342. else
        343. {
        344. ucDigShow3=ucTemp3;//第3位數碼管要顯示的內容
        345. }
        346. if(uiSetData1<10)
        347. {
        348. ucDigShow2=10;//如果小于10,十位顯示無
        349. }
        350. else
        351. {
        352. ucDigShow2=ucTemp2;//第2位數碼管要顯示的內容
        353. }
        354. ucDigShow1=ucTemp1;//第1位數碼管要顯示的內容
        355. }
        356. break;
        357. case 2://顯示P--2窗口的數據
        358. if(ucWd2Update==1)//窗口2要全部更新顯示
        359. {
        360. ucWd2Update=0;//及時清零標志,避免一直進來掃描
        361. ucDigShow8=12;//第8位數碼管顯示P
        362. ucDigShow7=11;//第7位數碼管顯示-
        363. ucDigShow6=2;//第6位數碼管顯示2
        364. ucDigShow5=10; //第5位數碼管顯示無
        365. ucTemp4=uiSetData2/1000; //分解數據
        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位數碼管要顯示的內容
        376. }
        377. if(uiSetData2<100)
        378. {
        379. ucDigShow3=10;//如果小于100,百位顯示無
        380. }
        381. else
        382. {
        383. ucDigShow3=ucTemp3;//第3位數碼管要顯示的內容
        384. }
        385. if(uiSetData2<10)
        386. {
        387. ucDigShow2=10;//如果小于10,十位顯示無
        388. }
        389. else
        390. {
        391. ucDigShow2=ucTemp2;//第2位數碼管要顯示的內容
        392. }
        393. ucDigShow1=ucTemp1;//第1位數碼管要顯示的內容
        394. }
        395. break;
        396. case 3://顯示P--3窗口的數據
        397. if(ucWd3Update==1)//窗口3要全部更新顯示
        398. {
        399. ucWd3Update=0;//及時清零標志,避免一直進來掃描
        400. ucDigShow8=12;//第8位數碼管顯示P
        401. ucDigShow7=11;//第7位數碼管顯示-
        402. ucDigShow6=3;//第6位數碼管顯示3
        403. ucDigShow5=10; //第5位數碼管顯示無
        404. ucTemp4=uiSetData3/1000; //分解數據
        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位數碼管要顯示的內容
        415. }
        416. if(uiSetData3<100)
        417. {
        418. ucDigShow3=10;//如果小于100,百位顯示無
        419. }
        420. else
        421. {
        422. ucDigShow3=ucTemp3;//第3位數碼管要顯示的內容
        423. }
        424. if(uiSetData3<10)
        425. {
        426. ucDigShow2=10;//如果小于10,十位顯示無
        427. }
        428. else
        429. {
        430. ucDigShow2=ucTemp2;//第2位數碼管要顯示的內容
        431. }
        432. ucDigShow1=ucTemp1;//第1位數碼管要顯示的內容
        433. }
        434. break;
        435. case 4://顯示P--4窗口的數據
        436. if(ucWd4Update==1)//窗口4要全部更新顯示
        437. {
        438. ucWd4Update=0;//及時清零標志,避免一直進來掃描
        439. ucDigShow8=12;//第8位數碼管顯示P
        440. ucDigShow7=11;//第7位數碼管顯示-
        441. ucDigShow6=4;//第6位數碼管顯示4
        442. ucDigShow5=10; //第5位數碼管顯示無
        443. ucTemp4=uiSetData4/1000; //分解數據
        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位數碼管要顯示的內容
        454. }
        455. if(uiSetData4<100)
        456. {
        457. ucDigShow3=10;//如果小于100,百位顯示無
        458. }
        459. else
        460. {
        461. ucDigShow3=ucTemp3;//第3位數碼管要顯示的內容
        462. }
        463. if(uiSetData4<10)
        464. {
        465. ucDigShow2=10;//如果小于10,十位顯示無
        466. }
        467. else
        468. {
        469. ucDigShow2=ucTemp2;//第2位數碼管要顯示的內容
        470. }
        471. ucDigShow1=ucTemp1;//第1位數碼管要顯示的內容
        472. }
        473. break;
        474. }
        475. }
        476. void key_scan(void)//按鍵掃描函數 放在定時中斷里
        477. {
        478. if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        479. {
        480. ucKeyLock1=0; //按鍵自鎖標志清零
        481. uiKeyTimeCnt1=0;//按鍵去抖動延時計數器清零,此行非常巧妙,是我實戰中摸索出來的。
        482. }
        483. else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
        484. {
        485. uiKeyTimeCnt1++; //累加定時中斷次數
        486. if(uiKeyTimeCnt1>const_key_time1)
        487. {
        488. uiKeyTimeCnt1=0;
        489. ucKeyLock1=1;//自鎖按鍵置位,避免一直觸發
        490. ucKeySec=1; //觸發1號鍵
        491. }
        492. }
        493. if(key_sr2==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        494. {
        495. ucKeyLock2=0; //按鍵自鎖標志清零
        496. uiKeyTimeCnt2=0;//按鍵去抖動延時計數器清零,此行非常巧妙,是我實戰中摸索出來的。
        497. }
        498. else if(ucKeyLock2==0)//有按鍵按下,且是第一次被按下
        499. {
        500. uiKeyTimeCnt2++; //累加定時中斷次數
        501. if(uiKeyTimeCnt2>const_key_time2)
        502. {
        503. uiKeyTimeCnt2=0;
        504. ucKeyLock2=1;//自鎖按鍵置位,避免一直觸發
        505. ucKeySec=2; //觸發2號鍵
        506. }
        507. }
        508. if(key_sr3==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        509. {
        510. ucKeyLock3=0; //按鍵自鎖標志清零
        511. uiKeyTimeCnt3=0;//按鍵去抖動延時計數器清零,此行非常巧妙,是我實戰中摸索出來的。
        512. }
        513. else if(ucKeyLock3==0)//有按鍵按下,且是第一次被按下
        514. {
        515. uiKeyTimeCnt3++; //累加定時中斷次數
        516. if(uiKeyTimeCnt3>const_key_time3)
        517. {
        518. uiKeyTimeCnt3=0;
        519. ucKeyLock3=1;//自鎖按鍵置位,避免一直觸發
        520. ucKeySec=3; //觸發3號鍵
        521. }
        522. }
        523. if(key_sr4==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
        524. {
        525. ucKeyLock4=0; //按鍵自鎖標志清零
        526. uiKeyTimeCnt4=0;//按鍵去抖動延時計數器清零,此行非常巧妙,是我實戰中摸索出來的。
        527. }
        528. else if(ucKeyLock4==0)//有按鍵按下,且是第一次被按下
        529. {
        530. uiKeyTimeCnt4++; //累加定時中斷次數
        531. if(uiKeyTimeCnt4>const_key_time4)
        532. {
        533. uiKeyTimeCnt4=0;
        534. ucKeyLock4=1;//自鎖按鍵置位,避免一直觸發
        535. ucKeySec=4; //觸發4號鍵
        536. }
        537. }
        538. }
        539. void key_service(void) //按鍵服務的應用程序
        540. {
        541. switch(ucKeySec) //按鍵服務狀態切換
        542. {
        543. case 1:// 加按鍵 對應朱兆祺學習板的S1鍵
        544. switch(ucWd)//在不同的窗口下,設置不同的參數
        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;//原子鎖加鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        580. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        581. ucVoiceLock=0;//原子鎖解鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        582. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        583. break;
        584. case 2:// 減按鍵 對應朱兆祺學習板的S5鍵
        585. switch(ucWd)//在不同的窗口下,設置不同的參數
        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;//原子鎖加鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        621. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        622. ucVoiceLock=0;//原子鎖解鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        623. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        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;//原子鎖加鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        647. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        648. ucVoiceLock=0;//原子鎖解鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        649. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        650. break;
        651. case 4:// 啟動發送數據和復位按鍵 對應朱兆祺學習板的S13鍵
        652. switch(ucStatus)//在不同的狀態下,進行不同的操作
        653. {
        654. case 0://處于待機狀態,則啟動發送數據
        655. ucErrorCnt=0; //累計錯誤總數清零
        656. ucSendTotal=0; //已經發送串數據總數清零
        657. ucSendStep=0; //發送數據的過程步驟清零,返回開始的步驟待命
        658. ucStatus=1; //啟動發送數據,1代表正在通訊過程
        659. break;
        660. case 1://處于正在通訊的過程
        661. break;
        662. case 2: //發送數據出錯,比如中間超時沒有接收到數據
        663. ucStatus=0; //切換回待機的狀態
        664. break;
        665. }
        666. ucVoiceLock=1;//原子鎖加鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        667. uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
        668. ucVoiceLock=0;//原子鎖解鎖,保護主函數與中斷函數的共享變量uiVoiceCnt
        669. ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
        670. break;
        671. }
        672. }
        673. void display_drive(void)
        674. {
        675. //以下程序,如果加一些數組和移位的元素,還可以壓縮容量。但是鴻哥追求的不是容量,而是清晰的講解思路
        676. switch(ucDisplayDriveStep)
        677. {
        678. case 1://顯示第1位
        679. ucDigShowTemp=dig_table[ucDigShow1];
        680. if(ucDigDot1==1)
        681. {
        682. ucDigShowTemp=ucDigShowTemp|0x80;//顯示小數點
        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;//顯示小數點
        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;//顯示小數點
        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;//顯示小數點
        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;//顯示小數點
        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;//顯示小數點
        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;//顯示小數點
        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;//顯示小數點
        739. }
        740. dig_hc595_drive(ucDigShowTemp,0x7f);
        741. break;
        742. }
        743. ucDisplayDriveStep++;
        744. if(ucDisplayDriveStep>8)//掃描完8個數碼管后,重新從第一個開始掃描
        745. {
        746. ucDisplayDriveStep=1;
        747. }
        748. }
        749. //數碼管的74HC595驅動函數
        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引腳的上升沿把數據送入寄存器
        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引腳的上升沿把數據送入寄存器
        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引腳把兩個寄存器的數據更新輸出到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驅動函數
        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引腳的上升沿把數據送入寄存器
        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引腳的上升沿把數據送入寄存器
        810. delay_short(1);
        811. hc595_sh_dr=1;
        812. delay_short(1);
        813. ucTempData=ucTempData<<1;
        814. }
        815. hc595_st_dr=0;//ST引腳把兩個寄存器的數據更新輸出到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 //串口接收數據中斷
        824. {
        825. if(RI==1)
        826. {
        827. RI = 0;
        828. ++uiRcregTotal;
        829. if(uiRcregTotal>const_rc_size)//超過緩沖區
        830. {
        831. uiRcregTotal=const_rc_size;
        832. }
        833. ucRcregBuf[uiRcregTotal-1]=SBUF; //將串口接收到的數據緩存到接收緩沖區里
        834. if(ucSendCntLock==0)//原子鎖判斷
        835. {
        836. ucSendCntLock=1; //加鎖
        837. uiSendCnt=0;//及時喂狗,雖然在定時中斷那邊此變量會不斷累加,但是只要串口的數據還沒發送完畢,那么它永遠也長不大,因為每個串口接收中斷它都被清零。
        838. ucSendCntLock=0; //解鎖
        839. }
        840. }
        841. else//我在其它單片機上都不用else這段代碼的,可能在51單片機上多增加" TI = 0;"穩定性會更好吧。
        842. {
        843. TI = 0;//如果不是串口接收中斷,那么必然是串口發送中斷,及時清除發送中斷的標志,否則一直發送中斷
        844. }
        845. }
        846. void T0_time(void) interrupt 1 //定時中斷
        847. {
        848. TF0=0;//清除中斷標志
        849. TR0=0; //關中斷
        850. /* 注釋一:
        851. * 此處多增加一個原子鎖,作為中斷與主函數共享數據的保護,實際上是借鑒了"紅金龍吸味"關于原子鎖的建議.
        852. */
        853. if(ucSendCntLock==0)//原子鎖判斷
        854. {
        855. ucSendCntLock=1; //加鎖
        856. if(uiSendCnt
        857. {
        858. uiSendCnt++; //表面上這個數據不斷累加,但是在串口中斷里,每接收一個字節它都會被清零,除非這個中間沒有串口數據過來
        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括號語句的數量對稱,都是兩條指令。不加也可以。
        873. beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
        874. }
        875. }
        876. if(ucStatus!=0) //處于非待機的狀態,Led閃爍
        877. {
        878. if(ucLedLock==0)//原子鎖判斷
        879. {
        880. uiLedCnt++; //Led閃爍計時器不斷累加
        881. }
        882. }
        883. if(ucStatus==1) //處于正在通訊的狀態,
        884. {
        885. if(ucSendTimeOutLock==0)//原子鎖判斷
        886. {
        887. uiSendTimeOutCnt++; //超時計時器累加
        888. }
        889. }
        890. key_scan(); //按鍵掃描函數
        891. display_drive();//數碼管字模的驅動函數
        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. ; //一個分號相當于執行一條空語句
        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++)//內嵌循環的空指令數量
        911. {
        912. ; //一個分號相當于執行一條空語句
        913. }
        914. }
        915. }
        916. void initial_myself(void)//第一區 初始化單片機
        917. {
        918. /* 注釋二:
        919. * 矩陣鍵盤也可以做獨立按鍵,前提是把某一根公共輸出線輸出低電平,
        920. * 模擬獨立按鍵的觸發地,本程序中,把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);//關閉所有經過另外兩個74HC595驅動的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. * 為了保證串口中斷接收的數據不丟失,必須設置IP = 0x10,相當于把串口中斷設置為最高優先級,
        935. * 這個時候,串口中斷可以打斷任何其他的中斷服務函數實現嵌套,
        936. */
        937. IP =0x10;//把串口中斷設置為最高優先級,必須的。
        938. TH1=TL1=-(11059200L/12/32/9600);//串口波特率為9600。
        939. TR1=1;
        940. }
        941. void initial_peripheral(void) //第二區 初始化外圍
        942. {
        943. ucDigDot8=0; //小數點全部不顯示
        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. }


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


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 湖北省| 宜丰县| 崇信县| 巢湖市| 慈溪市| 托克托县| 刚察县| 卢氏县| 祥云县| 达尔| 烟台市| 张北县| 郸城县| 金昌市| 务川| 抚远县| 盘山县| 如东县| 巴彦县| 青冈县| 城步| 两当县| 兴业县| 肇州县| 富川| 四川省| 东至县| 喀什市| 道孚县| 台州市| 白朗县| 西丰县| 双牌县| 道真| 昆山市| 蓝山县| 民乐县| 元氏县| 阜新市| 辽宁省| 阳信县|