新聞中心

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

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

作者: 時(shí)間:2016-11-22 來源:網(wǎng)絡(luò) 收藏
開場(chǎng)白:
根據(jù)上一節(jié)的預(yù)告,本來這一節(jié)內(nèi)容打算講“利用AT24C02進(jìn)行掉電后的數(shù)據(jù)保存”的,但是由于網(wǎng)友“261854681”強(qiáng)烈建議我講一個(gè)完整的串口收發(fā)程序?qū)嵗?,因此我決定再花兩節(jié)篇幅講講這方面的內(nèi)容。
實(shí)際上在大部分的項(xiàng)目中,串口都需要“一收一應(yīng)答”的握手協(xié)議,上位機(jī)作為主機(jī),單片機(jī)作為從機(jī),主機(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í)沒有接收到任何應(yīng)答指令,則繼續(xù)重發(fā),如果連續(xù)重發(fā)三次都是錯(cuò)誤應(yīng)答或者無應(yīng)答,主機(jī)就進(jìn)行報(bào)錯(cuò)處理。
這節(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ā)端程序框架
第三個(gè):從機(jī)的狀態(tài)指示程序框架。可以指示待機(jī),通訊中,超時(shí)出錯(cuò)三種狀態(tài)。

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

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

(2)實(shí)現(xiàn)功能:
顯示和獨(dú)立按鍵部分根據(jù)第29節(jié)的程序來改編,用朱兆祺51單片機(jī)學(xué)習(xí)板中的S1,S5,S9,S13作為獨(dú)立按鍵。
一共有4個(gè)窗口。每個(gè)窗口顯示一個(gè)參數(shù)。有兩種更改參數(shù)的方式:
第一種:按鍵更改參數(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是復(fù)位按鍵,當(dāng)通訊超時(shí)蜂鳴器報(bào)警時(shí),可以按下此鍵清除報(bào)警。

第二種:通過串口來更改參數(shù):
波特率是:9600.
通訊協(xié)議:EB 00 55GG 00 02 XX XXCY
其中第1,2,3位EB 00 55就是數(shù)據(jù)頭
其中第4位GG就是數(shù)據(jù)類型。01代表更改參數(shù)1,02代表更改參數(shù)2,03代表更改參數(shù)3,04代表更改參數(shù)4,
其中第5,6位00 02就是有效數(shù)據(jù)長度。高位在左,低位在右。
其中從第7,8位XX XX是被更改的參數(shù)。高位在左,低位在右。
第9位CY是累加和,前面所有字節(jié)的累加。
一個(gè)完整的通訊必須接收完4串?dāng)?shù)據(jù),每串?dāng)?shù)據(jù)之間的間隔時(shí)間不能超過10秒鐘,否則認(rèn)為通訊超時(shí)出錯(cuò)引發(fā)蜂鳴器報(bào)警。如果接收到得數(shù)據(jù)校驗(yàn)正確,
則返回校驗(yàn)正確應(yīng)答:eb 00 55 f5 00 00 35,
否則返回校驗(yàn)出錯(cuò)應(yīng)答::eb 00 55 fa 00 00 3a。
系統(tǒng)處于待機(jī)狀態(tài)時(shí),LED燈一直亮,
系統(tǒng)處于非待機(jī)狀態(tài)時(shí),LED燈閃爍,
系統(tǒng)處于通訊超時(shí)出錯(cuò)狀態(tài)時(shí),LED燈閃爍,并且蜂鳴器間歇鳴叫報(bào)警。


通過電腦的串口助手,依次發(fā)送以下測(cè)試數(shù)據(jù),將會(huì)分別更改參數(shù)1,參數(shù)2,參數(shù)3,參數(shù)4。注意,每串?dāng)?shù)據(jù)之間的時(shí)間最大不能超過10秒,否則系統(tǒng)認(rèn)為通訊超時(shí)報(bào)警。
把參數(shù)1更改為十進(jìn)制的1: eb 00 55 01 00 02 00 01 44
把參數(shù)2更改為十進(jìn)制的12:eb 00 55 02 00 02 00 0c 50
把參數(shù)3更改為十進(jìn)制的123: eb 00 55 03 00 02 00 7b c0
把參數(shù)4更改為十進(jìn)制的1234:eb 00 55 04 00 02 04 d2 1c

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


總結(jié)陳詞:
這節(jié)詳細(xì)講了從機(jī)收發(fā)端的程序框架,而主機(jī)端的程序則用電腦的串口助手來模擬。實(shí)際上,主機(jī)端的程序也有很多內(nèi)容,它包括依次發(fā)送每一串?dāng)?shù)據(jù),根據(jù)返回的應(yīng)答來決定是否需要重發(fā)數(shù)據(jù),重發(fā)三次如果沒反應(yīng)則進(jìn)行報(bào)錯(cuò),以及超時(shí)沒接收到數(shù)據(jù)等等內(nèi)容。主機(jī)收發(fā)端的程序框架是什么樣的?欲知詳情,請(qǐng)聽下回分解-----主機(jī)的串口收發(fā)綜合程序框架


關(guān)鍵詞: 從機(jī)串口收程序框

評(píng)論


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

關(guān)閉