




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、51單片機(jī)的串口,是個(gè)全雙工的串口,發(fā)送數(shù)據(jù)的同時(shí),還可以接收數(shù)據(jù)。當(dāng)串行發(fā)送完畢后,將在標(biāo)志位 TI 置 1,同樣,當(dāng)收到了數(shù)據(jù)后,也會(huì)在 RI 置 1。無(wú)論 RI 或 TI 出現(xiàn)了 1,只要串口中斷處于開(kāi)放狀態(tài),單片機(jī)都會(huì)進(jìn)入串口中斷處理程序。在中斷程序中,要區(qū)分出來(lái)究竟是發(fā)送引起的中斷,還是接收引起的中斷,然后分別進(jìn)行處理。看到過(guò)一些書(shū)籍和文章,在串口收、發(fā)數(shù)據(jù)的處理方法上,很多人都有不妥之處。接收數(shù)據(jù)時(shí),基本上都是使用“中斷方式”,這是正確合理的。即:每當(dāng)收到一個(gè)新數(shù)據(jù),就在中斷函數(shù)中,把 RI 清零,并用一個(gè)變量,通知主函數(shù),收到了新數(shù)據(jù)。發(fā)送數(shù)據(jù)時(shí),很多的程序都是使用的“查詢(xún)方式”
2、,就是執(zhí)行 while(TI =0); 這樣的語(yǔ)句來(lái)等待發(fā)送完畢。這時(shí),處理不好的話(huà),就可能帶來(lái)問(wèn)題??戳艘恍┚W(wǎng)友編寫(xiě)的程序,發(fā)現(xiàn)有如下幾條容易出錯(cuò):有人在發(fā)送數(shù)據(jù)之前,先關(guān)閉了串口中斷!等待發(fā)送完畢后,再打開(kāi)串口中斷。這樣,在發(fā)送數(shù)據(jù)的等待期間內(nèi),如果收到了數(shù)據(jù),將不能進(jìn)入中斷函數(shù),也就不會(huì)保存的這個(gè)新收到的數(shù)據(jù)。這種處理方法,就會(huì)遺漏收到的數(shù)據(jù)。有人在發(fā)送數(shù)據(jù)之前,并沒(méi)有關(guān)閉串口中斷,當(dāng) TI = 1 時(shí),是可以進(jìn)入中斷程序的。但是,卻在中斷函數(shù)中,將 TI 清零!這樣,在主函數(shù)中的while(TI =0);,將永遠(yuǎn)等不到發(fā)送結(jié)束的標(biāo)志。還有人在中斷程序中,并沒(méi)有區(qū)分中斷的來(lái)源,反而讓發(fā)送
3、引起的中斷,執(zhí)行了接收中斷的程序。對(duì)此,做而論道發(fā)表自己常用的方法:接收數(shù)據(jù)時(shí),使用“中斷方式”,清除 RI 后,用一個(gè)變量通知主函數(shù),收到新數(shù)據(jù)。發(fā)送數(shù)據(jù)時(shí),也用“中斷方式”,清除 TI 后,用另一個(gè)變量通知主函數(shù),數(shù)據(jù)發(fā)送完畢。這樣一來(lái),收、發(fā)兩者基本一致,編寫(xiě)程序也很規(guī)范、易懂。更重要的是,主函數(shù)中,不用在那兒死等發(fā)送完畢,可以有更多的時(shí)間查看其它的標(biāo)志。實(shí)例:求一個(gè)PC與單片機(jī)串口通信的程序,要求如下:1、如果在電腦上發(fā)送以$開(kāi)始的字符串,則將整個(gè)字符串原樣返回(字符串長(zhǎng)度不是固定的)。2、如果接收到1,則將P10置高電平,接收到0,P10置低電平。(用來(lái)控制一個(gè)LED)單片機(jī)是STC
4、89C52RC/晶振11.0592/波特率要求是9600或4800。謝謝!問(wèn)題補(bǔ)充:可能會(huì)將【$ABCD,123456,987654ccc,aasdasd,aaaa,sssd,4D】這樣的字符串(字符串長(zhǎng)度約為50-150個(gè)字符)傳送給單片機(jī),只能能原樣返回。cpp view plaincopy1. 最佳答案: 2. 下列程序,已經(jīng)調(diào)試成功。 3. #include <REG52.H> 4. sbit LED = P10; 5. unsigned
5、 char UART_buff; 6. bit New_rec = 0, Send_ed = 1, Money = 0; 7. /- 8. void main (void) 9. 10. SCON = 0x50; /串口方式1, 8-n-1,
6、160;允許接收. 11. TMOD = 0x20; /T1方式2 12. TH1 = 0xFD; url=/9600bps11.0592MHz/url 13. TL1 = 0xFD; 14.
7、; TR1 = 1; 15. ES = 1; /開(kāi)中斷. 16.
8、; EA = 1; 17. while(Money = 0); /等著交費(fèi),呵呵,等著接收$. 18. while(1) 19. if (New_rec = 1)
9、60;&& (Send_ed = 1) /如果收到新數(shù)據(jù)及發(fā)送完畢 20. SBUF = UART_buff; /那就發(fā)送. 21. New_rec = 0; 22.
10、0; Send_ed = 0; 23. 24. 25. /- 26. void ser_int (void) interrupt 4 27. 28. if(RI = 1)
11、;/如果收到. 29. RI = 0; /清除標(biāo)志. 30. New_rec = 1; 31. UART_buff = SBUF; /接收. 3
12、2. if(UART_buff = '1') LED = 1; 33. if(UART_buff = '0') LED = 0; 34. if(UART_buff =
13、0;'$') Money = 1; 35. 36. else /如果送畢. 37. TI = 0;
14、;/清除標(biāo)志. 38. Send_ed = 1; 39. 40. 41. /- 串口接收程序是基于串口中斷的,單片機(jī)的串口每次接收到一字節(jié)數(shù)據(jù)產(chǎn)生一次中斷,然后再讀取某個(gè)寄存器就可以得到串口接收的數(shù)據(jù)了。然而在實(shí)際應(yīng)用當(dāng)中,基本上不會(huì)有單字節(jié)接收的情況。一般都是基于一定串口通信協(xié)議的多字節(jié)通信。在422或者485通信中,還可能是一個(gè)
15、主機(jī)(一般是計(jì)算機(jī))帶多個(gè)從機(jī)(相應(yīng)的有單片機(jī)的板卡)。這就要求我們的單片機(jī)能夠在連續(xù)接收到的串口數(shù)據(jù)序列中識(shí)別出符合自己板卡對(duì)應(yīng)的通信協(xié)議,來(lái)進(jìn)行控制操作,不符合則不進(jìn)行任何操作。簡(jiǎn)而言之就是,單片機(jī)要在一串?dāng)?shù)據(jù)中找到符合一定規(guī)律的幾個(gè)字節(jié)的數(shù)據(jù)。 先來(lái)說(shuō)下怎樣定串口協(xié)議吧。這個(gè)協(xié)議指的不是串口底層的協(xié)議,而是前面提到的數(shù)據(jù)幀協(xié)議。一般都是有幀頭(23個(gè)字節(jié)吧),數(shù)據(jù)(長(zhǎng)度根據(jù)需要),結(jié)束位(1位,有時(shí)候設(shè)計(jì)成校驗(yàn)字節(jié),最簡(jiǎn)單的校驗(yàn)也就是前面所有數(shù)據(jù)求和)。 比如0xaa
16、0x55 +(數(shù)據(jù)部分省略)+校驗(yàn)和(除了aa 55 之外數(shù)據(jù)的和),如果要是多板卡的話(huà)有時(shí)候還要在幀頭后面加一個(gè)板選字節(jié)(相當(dāng)于3字節(jié)幀頭了)。 第一次寫(xiě)串口接收程序的時(shí)候,我首先想到的就是定義一個(gè)全局變量(實(shí)際上最好是定義局部靜態(tài)變量),初始值設(shè)置為0,然后每進(jìn)一次中斷+1,然后加到串口通信協(xié)議的長(zhǎng)度的時(shí)候再清零。然后判斷幀頭、校驗(yàn)。寫(xiě)完了之后我自己都覺(jué)得不對(duì),一旦數(shù)據(jù)錯(cuò)開(kāi)了一位,后面就永遠(yuǎn)都接收不到數(shù)了。無(wú)奈看了一下前輩們的代碼,跟我的思路差不多,只不過(guò)那個(gè)計(jì)數(shù)值跟接收到的數(shù)據(jù)時(shí)同時(shí)判斷的,而且每次中斷都
17、要判斷,一旦不對(duì)計(jì)數(shù)的那個(gè)變量就清零。 廢話(huà)少說(shuō),直接上一段代碼讓大家看看就明白了。(通信協(xié)議姑且按照簡(jiǎn)單的aa 55 一個(gè)字節(jié)數(shù)據(jù) 一個(gè)字節(jié)校驗(yàn),代碼是基于51單片機(jī)的)。接收成功則在中斷程序中把串口接收成功標(biāo)志位置1。cpp view plaincopy1. 然后串口中斷部分 2. void ser()interrupt 4 3. 4. static unsigned char c
18、ount;/串口接收計(jì)數(shù)的變量 5. RI=0;/手動(dòng)清某個(gè)寄存器,大家都懂的 6. receivecount=SBUF; 7. if(count=0&&receivecount=0xaa)/同時(shí)判斷count跟收到的數(shù)據(jù) 8. 9. count=1; 10.
19、0; 11. else if(count=1&&receivecount=0x55) 12. 13. count=2; 14. 15. else if(count=2) 16. 17. &
20、#160; count+; 18. 19. else if(count=3&&receivecount= receive 2)/判斷校驗(yàn)和,數(shù)據(jù)多的話(huà)是求/和,或者其他的校驗(yàn)方法,也可能是固定的幀尾 20. 21. count=0; 22.
21、0; uart_flag =1;/串口接收成功標(biāo)志,為1時(shí)在主程序中回復(fù),然后清零 23. ES=0; /關(guān)中斷,回復(fù)完了再ES=1; 24. 25. else 26. 27. count=0;/判斷不滿(mǎn)足條件就將計(jì)
22、數(shù)值清零 28. 29. 第一次做的串口大概就按照這個(gè)方法寫(xiě)完了(我后來(lái)看過(guò)其他的代碼,有人用switch語(yǔ)句寫(xiě)的,邏輯跟這個(gè)也差不多,不過(guò)我還是感覺(jué)用if else來(lái)寫(xiě)清晰一些), 不過(guò)在測(cè)試的時(shí)候發(fā)現(xiàn)了bug,如果數(shù)據(jù)幀發(fā)送一半,然后突然停止,再來(lái)重新發(fā),就會(huì)丟失一幀的數(shù)據(jù)。比如先接受到aa 55,然后斷了,再進(jìn)來(lái)aa 55 01 01,就不受控制了。后來(lái)我也想到一個(gè)bug,如果在多設(shè)備通信中,屬于其他設(shè)備的的幀數(shù)據(jù)最后一位是aa(或者最后兩位
23、為aa 55 ,或者最后3位為aa 55 板選),下一次通信的數(shù)據(jù)就接收不到了。 當(dāng)時(shí)對(duì)于數(shù)據(jù)突然中斷的bug,沒(méi)有想到很好的解決辦法,不過(guò)這種情況幾率極小,所以一直用這個(gè)方法寫(xiě)也沒(méi)有問(wèn)題。多設(shè)備通信最后一位恰好是aa的幾率也很小,出問(wèn)題的可能也很小。當(dāng)時(shí)項(xiàng)目里面的控制數(shù)據(jù)跟校驗(yàn)恰好不可能出現(xiàn)aa,于是我把if(count=0&&receivecount=0xaa)改成了if(receivecount=0xaa)其他都沒(méi)變,解決了,沒(méi)有bug了。
24、后來(lái)我又寫(xiě)了幾次單片機(jī)程序,才想到了一些解決問(wèn)題的方法不過(guò)改天再接著寫(xiě)吧,太累了,明天還要上班呢。 在后來(lái)的項(xiàng)目中,真的遇到了數(shù)據(jù)位跟校驗(yàn)位都可能出現(xiàn)aa的情況。我考慮到每次數(shù)據(jù)都是連續(xù)發(fā)送的(至少我們用labwindows做的上位機(jī)程序是這樣的),成功接收到了一幀數(shù)據(jù)是要有一定時(shí)間回復(fù)的,也就是說(shuō)如果接收到一半,但是很長(zhǎng)時(shí)間沒(méi)接收到數(shù)據(jù),把計(jì)數(shù)值count清零就ok啦。涉及時(shí)間的問(wèn)題自然要用定時(shí)器來(lái)實(shí)現(xiàn)啦。這次的通信協(xié)議如下,串口波特率19200,2個(gè)幀頭aa 55 ,一個(gè)板選,6字節(jié)數(shù)據(jù),一個(gè)校驗(yàn)字節(jié)(除幀頭外其他數(shù)據(jù)的和)
25、。cpp view plaincopy1. 全局變量定義 2. unsigned char boardAddr;/板選地址,通過(guò)檢測(cè)幾個(gè)io引腳,具體怎么得到的就不寫(xiě)了,很簡(jiǎn)單的 3. unsigned char g_DatRev 10=0;/接收緩存 4. bit retFlag=0;/為1代表串口接收到了一幀數(shù)據(jù) 5. 6. 7. 串口初始化函數(shù),晶振22.118
26、4 8. 9. void init_uart() 10. 11. SCON = 0x50; /串口方式1允許接收 12.
27、 TMOD = 0x21; /定時(shí)器1,方式2,8位自動(dòng)重載,同時(shí)配置定時(shí)器0,工作方式1 13. PCON = 0x80;
28、; / 波特率加倍 14. TH1 = 0xfa; 15. TL1 = 0xfa;
29、60; /寫(xiě)入串口定時(shí)器初值 16. TH0=(65536-2000)/256; /寫(xiě)入定時(shí)器0初值,串口傳輸一個(gè)字節(jié)時(shí)間為(1/19200)*10,計(jì)算得0.52ms 17. TL0=(65536-2000)%256; /定時(shí)器0定時(shí)大約1ms多 18.
30、60; EA=1; 19. ET0=1; /波特率:19200 22.1184M 初值:250(0xfa) 20. IE
31、 |= 0x90; 21. TR1 = 1; 22. 23.
32、; 24. 串口中斷函數(shù) 25. 26. void UART_INT(void) interrupt 4 27. 28. static unsigned char count;/串口接收計(jì)數(shù)的變量 29. 30.
33、; RI = 0; 31. g_DatRevcount = SBUF; 32.
34、60;if(g_DatRevcount=0xaa&&count=0) /幀頭 33. 34. &
35、#160; count=1;
36、; 35. 36. else if(count=1&&g_DatRevcount=0x55) 37
37、. 38. count=2;
38、0; 39. 40. 41. else if (count=2&&g_DatRev2 =
39、boardAddr) 42. 43. CK = g_DatRevcount; 44.
40、60; count=3; 45. 46.
41、; 47. 48. else if(count>=3&&count<9) 49.
42、160; 50. 51.
43、 CK += g_DatRevcount; 52. count +; 53. 54.
44、 55. else if(count = 9&&CK=g_DatRev9) 56.
45、160; 57. ES = 0; 58. &
46、#160; retFlag = 1; 59.
47、; count=0; 60.
48、; 61. else 62. 63. &
49、#160; count=0; 64. 65. resettimer();
50、160;66. 67. 68. 69. /判斷count不為0的話(huà)就啟動(dòng)定時(shí)器 70. void resettimer() 71. 72. TR0=0; 73. TH0=(65536-2000)/256; 74.
51、 TL0=(65536-2000)%256; 75. if(count!=0) 76. 77. TR0=1;
52、60; 78. 79. 80. 81. 定時(shí)器中斷函數(shù) 82. void T0_time()interrupt 1 83. 84. TR0=0; 85. &
53、#160; TH0=(65536-2000)/256; 86. TL0=(65536-2000)%256; 87. count=0; 88. 89. 這種方法的確是本人自己想出來(lái)的,別人可能也這樣做過(guò),但我這個(gè)絕對(duì)不是抄襲或者模仿來(lái)的。這樣寫(xiě)的確可以避免前面提到過(guò)的bug,不過(guò)代價(jià)是
54、多用了一個(gè)定時(shí)器的資源,而且中斷函數(shù)里的內(nèi)容更多了,占用了更多的時(shí)間。 要是能把第一種方法改進(jìn)一下就好了,主要是那個(gè)校驗(yàn)不能為aa的那個(gè)bug,因?yàn)楫吘箓鬏數(shù)揭话胪蝗粩嗔说目赡苄允欠浅P〉摹:髞?lái)我想第一個(gè)判斷if(count=0&&receivecount=0xaa)好像有點(diǎn)太嚴(yán)格了,考慮到第二字節(jié)的幀頭,跟板選地址不可能為aa,于是把這個(gè)改寫(xiě)為if(count>=0&&count<=2&& receivecount=0xaa),這樣就把bug出現(xiàn)的幾率降到了非常小,也
55、只是在前一幀結(jié)尾數(shù)據(jù)恰好為 aa 55 板選 的時(shí)候才出現(xiàn),幾率是多少大家自己算一下吧,呵呵。這樣我自己覺(jué)得,昨天寫(xiě)的那種方法改進(jìn)到這個(gè)程度,應(yīng)該算可以啦,反正我是很滿(mǎn)意了。 實(shí)際上我還想過(guò)其他的方法,比如緩存的數(shù)組采用移位寄存的方式。拿前面的4個(gè)字節(jié)的協(xié)議為例。cpp view plaincopy1. void ser()interrupt 4 2. 3. unsigned char i; 4. &
56、#160; RI=0; 5. 6. for(i=0;i<3;i+) 7. 8. receivei=receivei+1; 9. 10. receive3=SBUF; 11. if(reveive0=0xaa&&rec
57、eive1=0x55&&receive2=receive3) 12. 13. ret_flag=1; 14. ES = 0; 15. 16. 17. 這段代碼看上去可是簡(jiǎn)單明
58、了,這樣判斷可是不錯(cuò)啊,同時(shí)判斷幀頭跟校驗(yàn)不會(huì)產(chǎn)生前面提到的bug。說(shuō)實(shí)話(huà)當(dāng)時(shí)我剛想出這種方法并寫(xiě)出來(lái)的時(shí)候,馬上就被我給否了。那個(gè)for循環(huán)可真是很占時(shí)間的啊,延時(shí)函數(shù)都是這樣寫(xiě)的。每次都循環(huán)一下,這延時(shí)太長(zhǎng),通信速度太快的話(huà)就不能接收到下一字節(jié)數(shù)據(jù)了。最要命的是這個(gè)時(shí)間的長(zhǎng)度是隨著通信協(xié)議幀的字節(jié)數(shù)增加而增加的,如果一次要接收幾十個(gè)字節(jié),肯定就玩完了。這種方法我一次都沒(méi)用過(guò)。 不過(guò)我居然又想出來(lái)了這種方法的改良措施,是前兩天剛想出來(lái)的,呵呵,還沒(méi)有實(shí)踐過(guò)呢。下面代碼的協(xié)議就按第二段程序(定時(shí)器清零的那個(gè)協(xié)議,一共10字節(jié))全
59、局變量 cpp view plaincopy1. bit ret_flag; 2. unsigned char receive256=0; 3. unsigned char boardaddress; 4. 5. 中斷函數(shù) 6. 7. void ser()interrupt 4 8. 9.
60、 10. 11. static unsigned char i=0; 12. static unsigned char total=0; 13. RI=0; 14. receivei=SBUF; 15. total=total-receivei-7+receivei-1; 16. 17. if(receivei-9=0xaa&&receivei-8=0x55 18. &&receivei-7=boardaddress&&receivei=total 19. ) 20. 21. ret_flag=1;
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二年級(jí)上冊(cè)數(shù)學(xué)教案-練習(xí)七-北師大版
- 六年級(jí)上冊(cè)數(shù)學(xué)教案-3.1 倒-數(shù) |西師大版
- 六年級(jí)下冊(cè)數(shù)學(xué)教案-4.1扇形統(tǒng)計(jì)圖的認(rèn)識(shí) ︳西師大版
- 三年級(jí)上冊(cè)數(shù)學(xué)教案-解決問(wèn)題第一課時(shí)|蘇教版
- 六年級(jí)上冊(cè)數(shù)學(xué)教案 -2.1 分?jǐn)?shù)混合運(yùn)算|北師大版
- 融資擔(dān)保培訓(xùn)協(xié)議書(shū)(2篇)
- 北師大版數(shù)學(xué)三年級(jí)上冊(cè)單元測(cè)試卷-第三單元-加與減(含答案)
- 2024年血壓調(diào)節(jié)用品項(xiàng)目資金籌措計(jì)劃書(shū)代可行性研究報(bào)告
- 2025年度兩人共同投資可再生能源項(xiàng)目的股份合作合同
- 2025年度合伙人退出與合作伙伴關(guān)系維護(hù)協(xié)議
- 生涯規(guī)劃與就業(yè)創(chuàng)業(yè)全套課件電子教案板
- 湘少版六年級(jí)英語(yǔ)下冊(cè)《全冊(cè)課件》
- 2024-2030年中國(guó)護(hù)眼臺(tái)燈行業(yè)市場(chǎng)發(fā)展趨勢(shì)與前景展望戰(zhàn)略分析報(bào)告
- 《土壤肥料學(xué)通論》課程教學(xué)大綱
- 第十四屆全國(guó)交通運(yùn)輸行業(yè)職業(yè)技能競(jìng)賽(公路收費(fèi)及監(jiān)控員)賽項(xiàng)題庫(kù)-下(多選題-共3部分-2)
- 集合功能的測(cè)定(雙眼視檢查)
- 2024年農(nóng)村自建房裝修合同
- 2024年《高等教育心理學(xué)》考前輔導(dǎo)必背習(xí)題庫(kù)(300題)
- 2024年江蘇農(nóng)牧科技職業(yè)學(xué)院?jiǎn)握新殬I(yè)適應(yīng)性測(cè)試題庫(kù)完美版
- 2024年廣西職業(yè)院校技能大賽中職組《智慧物流作業(yè)》模塊MC競(jìng)賽樣題
- 人事專(zhuān)員簡(jiǎn)歷模板
評(píng)論
0/150
提交評(píng)論