單片機(jī)串口通信的發(fā)送與接收_第1頁(yè)
單片機(jī)串口通信的發(fā)送與接收_第2頁(yè)
單片機(jī)串口通信的發(fā)送與接收_第3頁(yè)
單片機(jī)串口通信的發(fā)送與接收_第4頁(yè)
單片機(jī)串口通信的發(fā)送與接收_第5頁(yè)
已閱讀5頁(yè),還剩5頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論