1. 程式人生 > >51微控制器串列埠通訊的傳送與接收

51微控制器串列埠通訊的傳送與接收

51微控制器的串列埠,是個全雙工的串列埠,傳送資料的同時,還可以接收資料。
當序列傳送完畢後,將在標誌位 TI 置 1,同樣,當收到了資料後,也會在 RI 置 1。
無論 RI 或 TI 出現了 1,只要串列埠中斷處於開放狀態,微控制器都會進入串列埠中斷處理程式。
在中斷程式中,要區分出來究竟是傳送引起的中斷,還是接收引起的中斷,然後分別進行處理。
看到過一些書籍和文章,在串列埠收、發資料的處理方法上,很多人都有不妥之處。
接收資料時,基本上都是使用“中斷方式”,這是正確合理的。
即:每當收到一個新資料,就在中斷函式中,把 RI 清零,並用一個變數,通知主函式,收到了新資料。
傳送資料時,很多的程式都是使用的“查詢方式”,就是執行 while(TI ==0); 這樣的語句來等待發送完畢。
這時,處理不好的話,就可能帶來問題。
看了一些網友編寫的程式,發現有如下幾條容易出錯:
1.有人在傳送資料之前,先關閉了串列埠中斷!等待發送完畢後,再開啟串列埠中斷。
這樣,在傳送資料的等待期間內,如果收到了資料,將不能進入中斷函式,也就不會儲存的這個新收到的資料。
這種處理方法,就會遺漏收到的資料。
2.有人在傳送資料之前,並沒有關閉串列埠中斷,當 TI = 1 時,是可以進入中斷程式的。
但是,卻在中斷函式中,將 TI 清零!
這樣,在主函式中的while(TI ==0);,將永遠等不到傳送結束的標誌。
3.還有人在中斷程式中,並沒有區分中斷的來源,反而讓傳送引起的中斷,執行了接收中斷的程式。
對此,做而論道發表自己常用的方法:
接收資料時,使用“中斷方式”,清除 RI 後,用一個變數通知主函式,收到新資料。
傳送資料時,也用“中斷方式”,清除 TI 後,用另一個變數通知主函式,資料傳送完畢。
這樣一來,收、發兩者基本一致,編寫程式也很規範、易懂。
更重要的是,主函式中,不用在那兒死等傳送完畢,可以有更多的時間檢視其它的標誌。


例項:
求一個PC與微控制器串列埠通訊的程式,要求如下:
1、如果在電腦上傳送以$開始的字串,則將整個字串原樣返回(字串長度不是固定的)。
2、如果接收到1,則將P10置高電平,接收到0,P10置低電平。(用來控制一個LED)
微控制器是STC89C52RC/晶振11.0592/波特率要求是9600或4800。謝謝!
問題補充:可能會將【$ABCD,123456,987654ccc,aasdasd,aaaa,sssd,4D】這樣的字串(字串長度約為50-150個字元)傳送給微控制器,只能能原樣返回。

  1. 最佳答案:  
  2. 下列程式,已經除錯成功。  
  3. #include <REG52.H>
  4. sbit LED = P1^0;  
  5. unsigned 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, 允許接收.
  11.     TMOD = 0x20;   //T1方式2
  12.     TH1 = 0xFD;    [url=]//[email protected]
    [/url]
  13.     TL1 = 0xFD;  
  14.     TR1 = 1;                          
  15.     ES  = 1;       //開中斷.
  16.     EA  = 1;  
  17.     while(Money == 0);    //等著交費,呵呵,等著接收$.
  18.     while(1)  {   
  19.       if ((New_rec == 1) && (Send_ed == 1))  {  //如果收到新資料及傳送完畢
  20.         SBUF = UART_buff; //那就傳送.
  21.         New_rec = 0;  
  22.         Send_ed = 0;  
  23.     } }  
  24. }  
  25. //----------------------------------------------
  26. void ser_int (void) interrupt 4   
  27. {  
  28.     if(RI == 1) {  //如果收到.
  29.       RI = 0;      //清除標誌.
  30.       New_rec = 1;  
  31.       UART_buff = SBUF;  //接收.
  32.       if(UART_buff == '1')  LED = 1;  
  33.       if(UART_buff == '0')  LED = 0;  
  34.       if(UART_buff == '$')  Money = 1;  
  35.     }  
  36.     else  {        //如果送畢.
  37.       TI = 0;      //清除標誌.
  38.       Send_ed = 1;  
  39.     }  
  40. }   
  41. //----------------------------------------------

串列埠接收程式是基於串列埠中斷的,微控制器的串列埠每次接收到一位元組資料產生一次中斷,然後再讀取某個暫存器就可以得到串列埠接收的資料了。然而在實際應用當中,基本上不會有單位元組接收的情況。一般都是基於一定串列埠通訊協議的多位元組通訊。在422或者485通訊中,還可能是一個主機(一般是計算機)帶多個從機(相應的有微控制器的板卡)。這就要求我們的微控制器能夠在連續接收到的串列埠資料序列中識別出符合自己板卡對應的通訊協議,來進行控制操作,不符合則不進行任何操作。簡而言之就是,微控制器要在一串資料中找到符合一定規律的幾個位元組的資料。

        先來說下怎樣定串列埠協議吧。這個協議指的不是串列埠底層的協議,而是前面提到的資料幀協議。一般都是有幀頭(2~3個位元組吧),資料(長度根據需要),結束位(1位,有時候設計成校驗位元組,最簡單的校驗也就是前面所有資料求和)。

        比如0xaa 0x55 +(資料部分省略)+校驗和(除了aa 55 之外資料的和),如果要是多板卡的話有時候還要在幀頭後面加一個板選位元組(相當於3位元組幀頭了)。

       第一次寫串列埠接收程式的時候,我首先想到的就是定義一個全域性變數(實際上最好是定義區域性靜態變數),初始值設定為0,然後每進一次中斷+1,然後加到串列埠通訊協議的長度的時候再清零。然後判斷幀頭、校驗。寫完了之後我自己都覺得不對,一旦資料錯開了一位,後面就永遠都接收不到數了。無奈看了一下前輩們的程式碼,跟我的思路差不多,只不過那個計數值跟接收到的資料時同時判斷的,而且每次中斷都要判斷,一旦不對計數的那個變數就清零。

       廢話少說,直接上一段程式碼讓大家看看就明白了。(通訊協議姑且按照簡單的aa 55 一個位元組資料 一個位元組校驗,程式碼是基於51微控制器的)。接收成功則在中斷程式中把串列埠接收成功標誌位置1。

  1. 然後串列埠中斷部分  
  2. void ser()interrupt 4  
  3. {  
  4. static unsigned char count;//串列埠接收計數的變數
  5.   RI=0;//手動清某個暫存器,大家都懂的
  6.   receive[count]=SBUF;  
  7.   if(count==0&&receive[count]==0xaa)//同時判斷count跟收到的資料
  8.   {  
  9.        count=1;  
  10.   }  
  11.   elseif(count==1&&receive[count]==0x55)  
  12.   {  
  13.      count=2;  
  14.   }  
  15.   elseif(count==2)  
  16.   {  
  17.        count++;  
  18.   }  
  19.   elseif(count==3&&receive[count]== receive [2])//判斷校驗和,資料多的話是求//和,或者其他的校驗方法,也可能是固定的幀尾
  20.   {  
  21.     count=0;  
  22.      uart_flag =1;//串列埠接收成功標誌,為1時在主程式中回覆,然後清零
  23.    ES=0;      //關中斷,回覆完了再ES=1;
  24.   }  
  25.   else
  26.   {  
  27.      count=0;//判斷不滿足條件就將計數值清零
  28.   }  
  29. }  

第一次做的串列埠大概就按照這個方法寫完了(我後來看過其他的程式碼,有人用switch語句寫的,邏輯跟這個也差不多,不過我還是感覺用if else來寫清晰一些),

        不過在測試的時候發現了bug,如果資料幀傳送一半,然後突然停止,再來重新發,就會丟失一幀的資料。比如先接受到aa 55,然後斷了,再進來aa 55 01 01,就不受控制了。後來我也想到一個bug,如果在多裝置通訊中,屬於其他裝置的的幀資料最後一位是aa(或者最後兩位為aa 55 ,或者最後3位為aa 55 板選),下一次通訊的資料就接收不到了。

        當時對於資料突然中斷的bug,沒有想到很好的解決辦法,不過這種情況機率極小,所以一直用這個方法寫也沒有問題。多裝置通訊最後一位恰好是aa的機率也很小,出問題的可能也很小。當時專案裡面的控制資料跟校驗恰好不可能出現aa,於是我把if(count==0&&receive[count]==0xaa)改成了if(receive[count]==0xaa)其他都沒變,解決了,沒有bug了。

        後來我又寫了幾次微控制器程式,才想到了一些解決問題的方法——不過改天再接著寫吧,太累了,明天還要上班呢。

        在後來的專案中,真的遇到了資料位跟校驗位都可能出現aa的情況。我考慮到每次資料都是連續傳送的(至少我們用labwindows做的上位機程式是這樣的),成功接收到了一幀資料是要有一定時間回覆的,也就是說如果接收到一半,但是很長時間沒接收到資料,把計數值count清零就ok啦。涉及時間的問題自然要用定時器來實現啦。

這次的通訊協議如下,串列埠波特率19200,2個幀頭aa 55 ,一個板選,6位元組資料,一個校驗位元組(除幀頭外其他資料的和)。


  1. 全域性變數定義  
  2. unsigned char boardAddr;//板選地址,通過檢測幾個io引腳,具體怎麼得到的就不寫了,很簡單的
  3. unsigned char g_DatRev [10]={0};//接收快取
  4. bit retFlag=0;//為1代表串列埠接收到了一幀資料
  5. 串列埠初始化函式,晶振22.1184  
  6. void init_uart()  
  7. {  
  8.        SCON = 0x50;                 //串列埠方式1允許接收
  9.        TMOD = 0x21;                //定時器1,方式2,8位自動過載,同時配置定時器0,工作方式1
  10.        PCON = 0x80;                // 波特率加倍
  11.        TH1 = 0xfa;  
  12.        TL1 = 0xfa;               //寫入串列埠定時器初值
  13.        TH0=(65536-2000)/256;    //寫入定時器0初值,串列埠傳輸一個位元組時間為(1/19200)*10,計算得0.52ms
  14.        TL0=(65536-2000)%256;   //定時器0定時大約1ms多
  15.     EA=1;  
  16.     ET0=1;                  //波特率:19200    22.1184M  初值:250(0xfa)
  17.        IE |= 0x90;             
  18.     TR1 = 1;                     
  19. }  
  20. 串列埠中斷函式  
  21. void UART_INT(void) interrupt 4  
  22. {   
  23.        static unsigned char count;//串列埠接收計數的變數
  24.               RI = 0;  
  25.               g_DatRev[count] = SBUF;  
  26.               if(g_DatRev[count]==0xaa&&count==0)             //幀頭
  27.            {  
  28.                    count=1;                                                   
  29.             }  
  30.                elseif(count==1&&g_DatRev[count]==0x55)   
  31.             {    
  32.                          count=2;            
  33.             }  
  34.                 elseif (count==2&&g_DatRev[2] == boardAddr)  
  35.                {   
  36.                   CK = g_DatRev[count];  
  37.                    count=3;  
  38.                }  
  39.                elseif(count>=3&&count<9)  
  40.               {       
  41.                      CK += g_DatRev[count];  
  42.                     count ++;  
  43.             }  
  44.            elseif(count == 9&&CK==g_DatRev[9])  
  45.                      {       
  46.                          ES = 0;   
  47.                         retFlag = 1;  
  48.                          count=0;              
  49.                      }              
  50.               else
  51.                {  
  52.                     count=0;  
  53.                }   
  54.              resettimer();  
  55. }  
  56. //判斷count不為0的話就啟動定時器
  57. void resettimer()  
  58. {  
  59.        TR0=0;  
  60.        TH0=(65536-2000)/256;  
  61.        TL0=(65536-2000)%256;  
  62.        if(count!=0)  
  63.        {  
  64.               TR0=1;  
  65.        }  
  66. }  
  67. 定時器中斷函式  
  68. void T0_time()interrupt 1  
  69. {       
  70.     TR0=0;  
  71.        TH0=(65536-2000)/256;  
  72.        TL0=(65536-2000)%256;  
  73.        count=0;  
  74. }  

這種方法的確是本人自己想出來的,別人可能也這樣做過,但我這個絕對不是抄襲或者模仿來的。這樣寫的確可以避免前面提到過的bug,不過代價是多用了一個定時器的資源,而且中斷函式裡的內容更多了,佔用了更多的時間。

        要是能把第一種方法改進一下就好了,主要是那個校驗不能為aa的那個bug,因為畢竟傳輸到一半突然斷了的可能性是非常小的。後來我想第一個判斷if(count==0&&receive[count]==0xaa)好像有點太嚴格了,考慮到第二位元組的幀頭,跟板選地址不可能為aa,於是把這個改寫為if(count>=0&&count<=2&& receive[count]==0xaa),這樣就把bug出現的機率降到了非常小,也只是在前一幀結尾資料恰好為 aa 55 板選 的時候才出現,機率是多少大家自己算一下吧,呵呵。這樣我自己覺得,昨天寫的那種方法改進到這個程度,應該算可以啦,反正我是很滿意了。

        實際上我還想過其他的方法,比如快取的陣列採用移位寄存的方式。拿前面的4個位元組的協議為例。

  1. void ser()interrupt 4  
  2. {  
  3. 相關推薦

    51微控制器串列通訊傳送接收

    51微控制器的串列埠,是個全雙工的串列埠,傳送資料的同時,還可以接收資料。 當序列傳送完畢後,將在標誌位 TI 置 1,同樣,當收到了資料後,也會在 RI 置 1。 無論 RI 或 TI 出現了 1,只要串列埠中斷處於開放狀態,微控制器都會進入串列埠中斷處理程式。 在中斷

    QT筆記(8)——Qt51微控制器串列通訊

    工業控制中微控制器與pc機通訊,常常通過串列埠通訊來完成,本次基於RS232通訊來完成的,硬體是普中的STC51微控制器開發板;板子如下圖: 不需要液晶顯示器,自帶的例子這裡就不貼了,主要實驗是Qt進行串列埠連結,傳送資料,微控制器判斷髮送的內容並做出反饋; 微控制器

    51微控制器串列通訊接收一串字串

    在51微控制器中,我們使用上下位機時,我們通常會發送一串字串,將它作為訊號發給微控制器處理。 因為串列埠通訊時,傳送資訊是以一個個字元的形式傳送過來的,所以接收的就是一個個字元,通常我們是一個字元陣列儲存,在進行下一步處理,同時字元陣列長度固定有限,但是如果上位機發送的字元

    51微控制器串列通訊(電腦傳送數字到微控制器數碼管顯示)

    #include<reg51.h> #include<intrins.h> //--定義使用的IO--// #define GPIO_DIG P0 sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; typ

    51微控制器串列通訊的幀資料接收

    首先定義一個數據幀格式,Header :{ 0xAA 0x55} type:{ 0x01 | 0x02 | 0x03 } length:{  N } body :{____n個位元組的資料___ } 資料的格式就是上面的定義  Header 為幀頭 標記一個數據幀的開始,t

    深入理解51微控制器串列通訊

    串列埠通訊的基本認識 通訊分為並行通訊和序列通訊,並行通訊時的資料各個位同時傳送,可以實現位元組為單位通訊,但通訊線多佔用資源,成本高。以前用到的的P1=0x55,一次給P1口的8個管腳分別賦值,同時進行訊號輸出,類似於8個車道可以過去8輛車,這樣的形式是並行的,一般稱P0

    51微控制器串列通訊的實現.

    在串列埠通訊實際操作裡面往往串列埠還要和電腦上的上位機軟體進行互動,實現電腦軟件傳送不同的指令,微控制器對應執行不同操作的功能,這就要求我們組織一個比較合理的通訊機制和邏輯關係,用來實現我們想要的結果。我們發的資料往往是一組(一幀)資料,那麼我們是如何判斷一段資料有沒有接收完

    微控制器利用串列通訊傳送溫度

    /********該程式主要是利用DS18B20採集溫度,然後通過數碼管顯示溫度*************/ /*當程式收到上位機發送的命令之後,該程式會將當時的溫度值通過串列埠傳送給上位機*/ #i

    PIC微控制器精通_非同步串列通訊例項細節

    1.前言 PIC16F876a非同步串列埠通訊的定義以及暫存器控制,這裡不再多談,前面已經進行過詳細的分析。這裡注意集中在幾個關鍵點上。 串列埠中斷服務程式應該注意什麼? 利用串列埠助手進行除錯應

    51微控制器 (5)VSPD+Proteus+串列除錯工具+Keil C51實現微控制器串列通訊模擬

    【若有疑問錯誤或版權等問題請聯絡我】 1、效果圖 2、虛擬串列埠 通過VSPD虛擬串列埠工具虛擬出兩個串列埠用於通訊 3、串列埠除錯 開啟兩份串列埠除錯工具,第一個開啟com1,

    關於串列通訊查詢中斷兩種方式

    void init_ser()   //串列埠初始化 {   TMOD=0x20;     //定時器1工作於方式2  TH1=0xf3;     //娤初值波特率為2400  TL1=0xf3;  TR1=1;      //開定時器1  SCON=0x50;     //設定串列埠方式1 允許接收

    關於微控制器串列通訊的幾個問題

    串列埠通訊:(從微控制器的角度考慮) 過程:(無論中斷開關與否,資料都能進出SBUF,且RI和TI都能硬體置1,只是CPU未進行接收) 接收:PC發一個位元組-->RI硬體置1-->進入中斷,接收資料(RI手動置0)-->返回現場 傳送:單片機發一個位元

    微控制器串列通訊波特率計算

    在模式0和模式2下,串列埠波特率固定,模式0為Fosc/12。模式2為Fosc/32或Fosc/64,具體由PCON暫存器的SMOD位決定。 在模式1和模式3下,波特率是一個可變值,波特率可以由定時器1產生(8052可以由定時2產生),那麼波特率到底為多少呢?波特率由定

    51微控制器串列列印不管是中文還是英文都是亂碼的問題

    講道理都tm大三暑假了還搞51微控制器而且關鍵是還遇到了一個問題解決了一天才解決出來真的是很丟人了。不過我會珍惜這個機會的,畢竟這有可能是我輩子最後一次跟著老師搞這個破玩意了。好了不廢話了 收!昨天遇到的問題是微控制器用串列埠給電腦發資料,本人用串列埠助手檢視時亂碼了,傳送的

    第一次用verilog除錯串列傳送接收

    1、首先是傳送,程式如下 `timescale 1ns / 1ps module send(in_data,out_data,en,clk); input clk; input[7:0] in_data; input en; output reg out_da

    51微控制器串列通訊(一)

    串列埠通訊好東西,但我沒用過。 下面照著普中科技的ppt搬運下。        隨著多微機系統的廣泛應用和計算機網路技術的普及,計算機的通訊功能愈來愈顯得重要。計算機通訊是指計算機與外部裝置或計算機與計算機之間的資訊交換。 通訊方式 有並行

    教你如何在51微控制器上模擬串列通訊!!!

    我們可以不使用微控制器本身帶有的串列埠,而自己用程式去模擬一個串列埠並達到和本身的串列埠具有同樣的功能, 首先,我們需要用到CH340串列埠模組,大家可以上某寶自行購買。 正面: 反面:   然後我們需要了解一下這串列埠模組上的引腳: 5V  :與VCC短路為5V TL

    51微控制器學習——串列通訊

    51微控制器的UART串列埠的結構由序列口控制暫存器SCON、傳送和接收電路三部分構成。 SCON位分配 位 7 6 5 4 3 2 1 0 符號

    FPGA-串列通訊接收模組(傳送接收模組)

    接收模組和傳送模組類似:        在接收的過程中為了保證接收資料的準確性對單個時鐘波特率進行分頻,單個時鐘訊號下分頻16次,進行資料採集保證了資料的準確性,這裡的程式碼思想借鑑了小梅哥的程式碼的編寫思想。 傳送接收模組的驗證:  

    PC機串列除錯助手微控制器通訊字元問題

    在利用PC機的串列埠除錯助手與微控制器通訊時,微控制器與PC機是以ASCII碼的形式通訊的,比如傳送1,其實發送的是字元1,如果在接收區以十六進位制顯示的話,是31(1的ASCII碼)。具體如下: 1. 十六進位制傳送與十六進位制顯示都為選中,傳送與接收的都是字元的形式;