找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 6762|回復(fù): 5
打印 上一主題 下一主題
收起左側(cè)

51單片機(jī)串口接收數(shù)據(jù)完畢檢測程序

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:675145 發(fā)表于 2020-4-29 15:03 | 只看該作者 回帖獎勵 |倒序?yàn)g覽 |閱讀模式
這兩天買了個藍(lán)牙模塊研究,發(fā)現(xiàn)串口接收到的數(shù)據(jù)沒有規(guī)律,不好檢測到底接收完了沒有。查閱了些資料發(fā)現(xiàn)了比較好的方法,為了幫助跟我一樣的萌新可以更好的學(xué)習(xí),我決定發(fā)個帖子,也算是給自己做個筆記。
1、IDLE中斷
IDLE就是串口收到一幀數(shù)據(jù)后,發(fā)生的中斷。什么是一幀數(shù)據(jù)呢?比如說給單片機(jī)一次發(fā)來1個字節(jié),或者一次發(fā)來8個字節(jié),這些一次發(fā)來的數(shù)據(jù),就稱為一幀數(shù)據(jù),也可以叫做一包數(shù)據(jù)。
如何判斷一幀數(shù)據(jù)結(jié)束,就是我們今天討論的問題。因?yàn)楹芏囗?xiàng)目中都要用到這個,因?yàn)橹挥薪邮盏揭粠瑪?shù)據(jù)以后,你才可以判斷這次收了幾個字節(jié)和每個字節(jié)的內(nèi)容是否符合協(xié)議要求。

看到這個第一反應(yīng)就是“好東西”,可惜51單片機(jī)好像沒有,所以只是做了粗略的了解,有興趣的各位可以去找些資料看看,隨便查一下資料還挺多的
2、自定義結(jié)束符
在一幀數(shù)據(jù)尾部添加一個字符,比如傳輸?shù)拿恳粠瑪?shù)據(jù)尾部都是“#”,當(dāng)單片機(jī)接收到“#”后,說明已經(jīng)接收完畢,然后立個Flag,就可以開始處理這些接收到的數(shù)據(jù)了,如下:
  1. u8 xdata RxLen=0;                                //接收計數(shù)
  2. u8 xdata RxFlag=0;                                //接收完畢標(biāo)志位
  3. u8 xdata Uart4_Rx_Buffer[33];        //接收到的數(shù)據(jù)

  4. void Uart4_Init()        //藍(lán)牙串口4,選擇定時器2為波特率發(fā)生器,波特率57600
  5. {
  6.         S4CON = 0x10;                //8位數(shù)據(jù),可變波特率
  7.         S4CON &= 0xBF;                //串口4選擇定時器2為波特率發(fā)生器
  8.         AUXR |= 0x04;                //定時器2時鐘為Fosc,即1T
  9.         T2L = 0x7E;                        //設(shè)定定時初值
  10.         T2H = 0xFF;                        //設(shè)定定時初值
  11.         AUXR |= 0x10;                //啟動定時器2
  12.         
  13.         IE2|=0X10;                        //打開串口中斷4
  14.         EA=1;                                //打開總中斷
  15. }
  16. void  S4_Routine() interrupt 18        //串口4中斷函數(shù)
  17. {               
  18.         if(S4CON&0x01)                //接收中斷
  19.         {
  20.                 S4CON&=0xFE;        //清除接收中斷
  21.                 Uart4_Rx_Buffer[RxLen]=S4BUF;
  22.                 if(Uart4_Rx_Buffer[RxLen] == '#')        //是結(jié)束符,說明接收完畢,就可以拿著RxFlag出去搞事情了
  23.                 {
  24.                         RxFlag=1;
  25.                 }
  26.                 else                                                                //不是結(jié)束符,老實(shí)接收下一個數(shù)據(jù)
  27.                 {
  28.                         RxLen++;
  29.                 }
  30.         }                        
  31. }
  32. void BLE_Allot()        //數(shù)據(jù)分配
  33. {
  34.         if(RxFlag)
  35.         {
  36.                 RxFlag=0;
  37.                 RxLen=0;
  38.                 /*干一些你想干的事情*/
  39.         }
  40. }
復(fù)制代碼
3、用另外一個定時器檢測

具體的思路是,因?yàn)槊恳晃粋鬏數(shù)臅r間間隔由波特率決定都是一樣,所以每一個字節(jié)的傳輸時間也是一樣的。那么我們每傳輸一個字節(jié)時,把定時器初值復(fù)位,以保證定時器不會溢出。定時器時間取傳輸一個字節(jié)的1.5倍,這樣一來,當(dāng)定時器超時時候,就意味著串口接收數(shù)據(jù)停止了。此時同樣立一個flag開始處理這些接收到的數(shù)據(jù),如下:
PS:大佬用的是滴答定時器,我發(fā)現(xiàn)51單片機(jī)還是沒有,欲哭無淚,所以只好自己做一個了
  1. u8 xdata RxLen=0;                                //接收計數(shù)
  2. u8 xdata RxFlag=0;                                //接收完畢標(biāo)志位
  3. u8 xdata Uart4_Rx_Buffer[33];        //接收到的數(shù)據(jù)
  4. void Timer1_Init()        //定時器1初始化
  5. {
  6.         AUXR |= 0x40;        //定時器時鐘1T模式
  7.         TMOD &= 0x0F;        //設(shè)置定時器模式
  8.         TL1 = 0x82;                //設(shè)置定時初值
  9.         TH1 = 0xE7;                //設(shè)置定時初值
  10.         TF1 = 0;                //清除TF1標(biāo)志
  11.         TR1 = 0;                //這里先不開始計時,等傳輸信號的時候開始計時
  12.         
  13.         ET1=1;
  14.         EA=1;
  15. }
  16. void Timer0_Rountine() interrupt 3
  17. {
  18.         TR1 = 0;        //定時器1停止計時
  19.         TF1 = 0;        //清除TF1標(biāo)志        
  20.         RxFlag=1;        //串口接收完畢
  21. }
  22. void Uart4_Init()        //藍(lán)牙串口4,選擇定時器2為波特率發(fā)生器,波特率57600
  23. {
  24.         S4CON = 0x10;                //8位數(shù)據(jù),可變波特率
  25.         S4CON &= 0xBF;                //串口4選擇定時器2為波特率發(fā)生器
  26.         AUXR |= 0x04;                //定時器2時鐘為Fosc,即1T
  27.         T2L = 0x7E;                        //設(shè)定定時初值
  28.         T2H = 0xFF;                        //設(shè)定定時初值
  29.         AUXR |= 0x10;                //啟動定時器2
  30.         
  31.         IE2|=0X10;                        //打開串口中斷4
  32.         EA=1;                                //打開總中斷
  33. }
  34. void  S4_Routine() interrupt 18        //串口4中斷函數(shù)
  35. {               
  36.         if(S4CON&0x01)                //接收中斷
  37.         {
  38.                 S4CON&=0xFE;        //清除接收中斷
  39.                 Uart4_Rx_Buffer[RxLen]=S4BUF;        //將數(shù)據(jù)放到數(shù)組中
  40.                 RxLen++;                //下次進(jìn)來數(shù)據(jù)存放地址+1
  41.                 TR1=0;                        //定時器1停止計時(這里搞了好久,一定要先停止,不然會出現(xiàn)錯誤)               
  42.                 TL1=0x82;                //設(shè)置定時初值
  43.                 TH1=0xE7;                //設(shè)置定時初值
  44.                 TR1=1;                        //定時器1開始計時               
  45.         }        
  46. }
  47. void BLE_Allot()        //數(shù)據(jù)分配
  48. {
  49.         if(RxFlag)
  50.         {
  51.                 RxFlag=0;        //標(biāo)志清零
  52.                 RxLen=0;        //計數(shù)清零
  53.                 /*干一些你想干的事情*/
  54.         }
  55. }
復(fù)制代碼
最后,感謝大佬在網(wǎng)上發(fā)布的好資料能給我們參考,也希望大佬能進(jìn)來多討論,多給點(diǎn)意見和建議



評分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報

沙發(fā)
ID:675145 發(fā)表于 2020-4-29 15:21 | 只看該作者
第三種方法定時器傳輸時間計算方法:比如波特率為57600,那么每秒鐘傳輸?shù)淖止?jié)數(shù)為:57600/8=7200B/S,那么一個字節(jié)需要的時間約為:1s/7200B=139uS,所以一個字節(jié)的1.5倍時間約為:209uS
回復(fù)

使用道具 舉報

板凳
ID:424977 發(fā)表于 2020-5-2 21:34 | 只看該作者
感謝大佬分享 學(xué)習(xí)到了
回復(fù)

使用道具 舉報

地板
ID:876099 發(fā)表于 2021-10-11 17:08 | 只看該作者
使用了你的程序,感謝分享
回復(fù)

使用道具 舉報

5#
ID:65956 發(fā)表于 2021-10-12 08:39 | 只看該作者
我說問問,如果用你這個,當(dāng)一幀來時存在著兩個23(#的十六進(jìn)制數(shù)是23),那怎么辦?所以說這個并不是最終的好辦法
void  S4_Routine() interrupt 18        //串口4中斷函數(shù)
{               
        if(S4CON&0x01)                //接收中斷
        {
                S4CON&=0xFE;        //清除接收中斷
                Uart4_Rx_Buffer[RxLen]=S4BUF;
                if(Uart4_Rx_Buffer[RxLen] == '#')        //是結(jié)束符,說明接收完畢,就可以拿著RxFlag出去搞事情了
                {
                        RxFlag=1;
                }
                else                                                                //不是結(jié)束符,老實(shí)接收下一個數(shù)據(jù)
                {
                        RxLen++;
                }
        }                        
}
第2種你用定時器0作接收超時的想法是對的,但應(yīng)是這樣的才不用這說的那么麻煩
/*******串口1接收中斷程序******************************************************
//接收時打開T0定時計數(shù)器(通過主函數(shù)再判斷是否接收超時)
//*****************************************************************************/
void Uart_Isr() interrupt 4 //using 1
{
        if(RI)
        {
                 RI = 0;
                //---------------------------------------
                TL0 = 0x00;                                        //設(shè)置定時初值
                TH0 = 0x4C;                                        //設(shè)置定時初值
                TF0 = 0;                                        //清除TF0標(biāo)志
                ET0 = 0;                                            //關(guān)閉T0的溢出中斷
                TR0 = 1;                                        //定時器0開始計時
                //---------------------------------------
                 S1_busy = 1;                                        //忙
                S1_Rec_data = SBUF;
                S1_Rectemp[S1_number] = S1_Rec_data;
                S1_number++;                                        //個數(shù)加1
                if(S1_number>=100);                                //判斷是否超出設(shè)置的最大緩存區(qū)
                {
                        S1_number = 0;
                }
                step1 = S1_number;                                 
        }
        S1_busy = 0;
}
這是我之前項(xiàng)目作的,當(dāng)然不只這種方法,還有很多,這個方法我現(xiàn)在都不用了,只是看你這么寫,給參考一下
回復(fù)

使用道具 舉報

6#
ID:624769 發(fā)表于 2021-10-12 23:33 | 只看該作者
張小不懂 發(fā)表于 2020-4-29 15:21
第三種方法定時器傳輸時間計算方法:比如波特率為57600,那么每秒鐘傳輸?shù)淖止?jié)數(shù)為:57600/8=7200B/S,那么 ...

更正一下, 串口傳輸時,無效驗(yàn)的話,10位為一個字節(jié),有效驗(yàn)的話,11位為一個字節(jié),因?yàn)橛衅鹗己屯V刮弧?br /> 另外,計算超時其實(shí)不用那么麻煩,按增強(qiáng)型51大多是4分波特率的,所以,你定時器2既然做了波特率發(fā)生器,那么,串口接受完成一個字節(jié)后,被RI觸發(fā)后,只要開T2中斷, 然后賦值超時計數(shù)(比如:Time_Out) = 105; 別問我這105怎么來的,反正有人算過加上N次試驗(yàn),105是最佳值,反正按4分波特率算就是2個半字節(jié)的超時吧,然后T2中斷,最低優(yōu)先級,每次--, 減到0了,置標(biāo)志位就可以了。這樣做的好處是,不用刻意用多一個定時器,而且,不管你波特率變成多少,超時判斷的字節(jié)數(shù)是不變的。
當(dāng)然,標(biāo)準(zhǔn)51會累點(diǎn),16分波特率,得減420次,有點(diǎn)累……
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表