嵌入式經常用到串口,如何判斷串口數據接收完成?

說起通信,首先想到的肯定是串口,日常中232和485的使用比比皆是,數據的發送、接收是串口通信最基礎的內容。這篇文章主要討論串口接收數據的斷幀操作。
空閑中斷斷幀
一些mcu(如:stm32f103)在出廠時就已經在串口中封裝好了一種中斷——空閑幀中斷,用戶可以通過獲取該中斷標志位來判斷數據是否接收完成,中斷標志在中斷服務函數中獲取,使用起來相對簡單。
在這里插入圖片描述
例程中,當接收完成標志 Lora_RecvData.Rx_over 為1時,就可以獲取 uart4 接收到的一幀數據,該數據存放在 Lora_RecvData.RxBuf 中。
超時斷幀
空閑幀中斷的使用固然方便,但是并不是每個mcu都有這種中斷存在(只有個別高端mcu才有),那么這個時候就可以考慮使用超時斷幀了。

Modbus協議中規定一幀數據的結束標志為3.5個字符時長,那么同樣的可以把這種斷幀方式類比到串口的接收上,這種方法需要搭配定時器使用。

其作用原理就是:串口進一次接收中斷,就打開定時器超時中斷,同時裝載值清零(具體的裝載值可以自行定義),只要觸發了定時器的超時中斷,說明在用戶規定的時間間隔內串口接收中斷里沒有新的數據進來,可以認為數據接收完成。
uint16_t Time3_CntValue = 0;//計數器初值

/*******************************************************************************

  • TIM3中斷服務函數
    ******************************************************************************/
    void Tim3_IRQHandler(void)
    {
    if(TRUE == Tim3_GetIntFlag(Tim3UevIrq))
    {
    Tim3_M0_Stop(); //關閉定時器3
    Uart0_Rec_Count = 0;//接收計數清零
    Uart0_Rec_Flag = 1; //接收完成標志
    Tim3_ClearIntFlag(Tim3UevIrq); //清除定時器中斷
    }
    }

void Time3_Init(uint16_t Frame_Spacing)
{
uint16_t u16ArrValue;//自動重載值
uint32_t u32PclkValue;//PCLK頻率

stc_tim3_mode0_cfg_t     stcTim3BaseCfg;//結構體初始化清零
DDL_ZERO_STRUCT(stcTim3BaseCfg);Sysctrl_SetPeripheralGate(SysctrlPeripheralTim3, TRUE); //Base Timer外設時鐘使能stcTim3BaseCfg.enWorkMode = Tim3WorkMode0;              //定時器模式
stcTim3BaseCfg.enCT       = Tim3Timer;                  //定時器功能,計數時鐘為內部PCLK
stcTim3BaseCfg.enPRS      = Tim3PCLKDiv1;               //不分頻
stcTim3BaseCfg.enCntMode  = Tim316bitArrMode;           //自動重載16位計數器/定時器
stcTim3BaseCfg.bEnTog     = FALSE;
stcTim3BaseCfg.bEnGate    = FALSE;
stcTim3BaseCfg.enGateP    = Tim3GatePositive;Tim3_Mode0_Init(&stcTim3BaseCfg);             //TIM3 的模式0功能初始化u32PclkValue = Sysctrl_GetPClkFreq();          //獲取Pclk的值

//u16ArrValue = 65535-(u32PclkValue/1000); //1ms測試
u16ArrValue = 65536 - (uint16_t)((float)(Frame_Spacing10)/RS485_BAUDRATEu32PclkValue);//根據幀間隔計算超時時間
Time3_CntValue = u16ArrValue; //計數初值
Tim3_M0_ARRSet(u16ArrValue); //設置重載值
Tim3_M0_Cnt16Set(u16ArrValue); //設置計數初值

Tim3_ClearIntFlag(Tim3UevIrq);            //清中斷標志
Tim3_Mode0_EnableIrq();                   //使能TIM3中斷(模式0時只有一個中斷)
EnableNvic(TIM3_IRQn, IrqLevel3, TRUE);   //TIM3 開中斷  

}

/**此處省略串口初始化部分/
//串口0中斷服務函數
void Uart0_IRQHandler(void)
{
uint8_t rec_data=0;

if(Uart_GetStatus(M0P_UART0, UartRC))         
{Uart_ClrStatus(M0P_UART0, UartRC);        rec_data = Uart_ReceiveData(M0P_UART0);     if(Uart0_Rec_Count<UART0_BUFF_LENGTH)//幀長度{Uart0_Rec_Buffer[Uart0_Rec_Count++] = rec_data;        }Tim3_M0_Cnt16Set(Time3_CntValue);//設置計數初值 Tim3_M0_Run();   //開啟定時器3 超時即認為一幀接收完成
}

}

例程所用的是華大的hc32l130系列mcu,其它類型的mcu也可以參考這種寫法。其中超時時間的計算尤其要注意數據類型的問題,u16ArrValue = 65536 - (uint16_t)((float)(Frame_Spacing * 10)/RS485_BAUDRATE * u32PclkValue);其中Frame_Spacing為用戶設置的字符個數,uart模式為一個“1+8+1”共10bits。

狀態機斷幀
狀態機,狀態機,又是狀態機,沒辦法!誰讓它使用起來方便呢?其實這種方法我用的也不多,但是狀態機的思想還是要有的,很多邏輯用狀態機梳理起來會更加的清晰。

相對于超時斷幀,狀態機斷幀的方法節約了一個定時器資源,一般的mcu外設資源是足夠的,但是做一些資源冗余也未嘗不是一件好事,萬一呢?對吧。
//狀態機斷幀
void UART_IRQHandler(void) //作為485的接收中斷
{
uint8_t count = 0;
unsigned char lRecDat = 0;

if(/*觸發接收中斷標志*/)  
{//清中斷狀態位rec_timeout = 5;if((count == 0)) //接收數據頭,長度可以自定義{RUart0485_DataC[count++] = /*串口接收到的數據*/;gRecStartFlag = 1;return;}if(gRecStartFlag == 1){RUart0485_DataC[count++] = /*串口接收到的數據*/;if(count > MAXLEN) //一幀數據接收完成{count=0;gRecStartFlag = 0;if(RUart0485_DataC[MAXLEN]==CRC16(RUart0485_DataC,MAXLEN)){memcpy(&gRecFinshData,RUart0485_DataC,13);gRcvFlag = 1; //接收完成標志位                    }}   }return; 
}
return ;

}

這種做法適合用在一直有數據接收的場合,每次接收完一幀有效數據后就把數據放到緩沖區中去解析,同時還不影響下一幀數據的接收。

整個接收狀態分為兩個狀態——接收數據頭和接收數據塊,如果一幀數據存在多個部分的話還可以在此基礎上再增加幾種狀態,這樣不僅可以提高數據接收的實時性,還能夠隨時看到數據接收到哪一部分,還是比較實用的。
"狀態機+FIFO"斷幀
如果串口有大量數據要接收,同時又沒有空閑幀中斷你會怎么做?

沒錯,就是FIFO(當時并沒有回答上來,因為沒用過),說白了就是開辟一個緩沖區,每次接收到的數據都放到這個緩沖區里,同時記錄數據在緩沖區中的位置,當數據到達要求的長度的時候再把數據取出來,然后放到狀態機中去解析。
當然FIFO的使用場合有很多,很多數據處理都可以用FIFO去做,有興趣的可以多去了解一下。
/**串口初始化省略,華大mcu hc32l130/
void Uart1_IRQHandler(void)
{
uint8_t data;
if(Uart_GetStatus(M0P_UART1, UartRC)) //UART0數據接收
{
Uart_ClrStatus(M0P_UART1, UartRC); //清中斷狀態位
data = Uart_ReceiveData(M0P_UART1); //接收數據字節
comFIFO(&data,1);
}
}

/FIFO*/
volatile uint8_t fifodata[FIFOLEN],fifoempty,fifofull;
volatile uint8_t uart_datatemp=0;

uint8_t comFIFO(uint8_t *data,uint8_t cmd)
{
static uint8_t rpos=0; //當前寫的位置 position 0–99
static uint8_t wpos=0; //當前讀的位置

if(cmd==0) //寫數據
{if(fifoempty!=0)       //1 表示有數據 不為空,0表示空{*data=fifodata[rpos];fifofull=0;rpos++;if(rpos==FIFOLEN) rpos=0;if(rpos==wpos) fifoempty=0;return 0x01;} elsereturn 0x00;} 
else if(cmd==1) //讀數據
{if(fifofull==0){fifodata[wpos]=*data;fifoempty=1;wpos++;if(wpos==FIFOLEN) wpos=0;if(wpos==rpos) fifofull=1;return 0x01;} elsereturn 0x00;
}
return 0x02;

}

/*狀態機處理/
void LoopFor485ReadCom(void)
{
uint8_t data;

while(comFIFO(&data,0)==0x01)
{if(rEadFlag==SAVE_HEADER_STATUS) //讀取頭{if(data==Header_H){buffread[0]=data;continue;}if(data==Header_L){buffread[1]=data;if(buffread[0]==Header_H){rEadFlag=SAVE_DATA_STATUS;}} else{memset(buffread,0,Length_Data);}} else if(rEadFlag==SAVE_DATA_STATUS)  //讀取數據{buffread[i485+2]=data;i485++;if(i485==(Length_Data-2)) //數據幀除去頭{unsigned short crc16=CRC16_MODBUS(buffread,Length_Data-2);if((buffread[Length_Data-2]==(crc16>>8))&&(buffread[Length_Data-1]==(crc16&0xff))){rEadFlag=SAVE_OVER_STATUS;memcpy(&cmddata,buffread,Length_Data);  //拷貝Length_Struct個字節,完整的結構體} else{rEadFlag=SAVE_HEADER_STATUS;}memset(buffread,0,Length_Data);i485=0;break;}}
}

}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895885.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895885.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895885.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML/CSS中并集選擇器

1.作用:選中多個選擇器對應的元素,又稱:分組選擇器 所謂并集就是或者的含義. 2.語法:選擇器1,選擇器2,選擇器3,......選擇器n 多個選擇器通過,連接,此處,的含義就是:或. .rich,.beauty{color: blue;} 3.注意事項 1.并集選擇器,我們一般豎著寫 2.任何形式的選擇器,都可以作為并…

解鎖機器學習核心算法 | 隨機森林算法:機器學習的超強武器

一、引言 在機器學習的廣闊領域中&#xff0c;算法的選擇猶如為一場冒險挑選趁手的武器&#xff0c;至關重要。面對海量的數據和復雜的任務&#xff0c;合適的算法能夠化繁為簡&#xff0c;精準地挖掘出數據背后隱藏的模式與價值。機器學習領域有十大核心算法&#xff0c;而隨…

Shapr3D在ipad上無法識別鼠標點擊問題

此問題我去過長沙Apple官方直營店咨詢過此問題&#xff0c;官方直營店也不知道解決方案&#xff0c;遂在此提醒長沙Apple官方線下直營店的所有店員。 問題描述 1.不知道為什么在買了Magic Keyboard后還是無法識別單擊觸控板&#xff0c;遂為了解決這個問題我特意從江西跑到長沙…

【架構】微內核架構(Microkernel Architecture)

微內核架構(Microkernel Architecture) 核心思想 微內核架構(又稱插件式架構)通過最小化核心系統,將可擴展功能以插件模塊形式動態加載,實現高內聚低耦合。其核心設計原則: 核心最小化:僅封裝基礎通用能力(如插件管理、通信機制、安全校驗)功能插件化:所有業務功能…

AI 編程助手 cursor的系統提示詞 prompt

# Role 你是一名極其優秀具有10年經驗的產品經理和精通java編程語言的架構師。與你交流的用戶是不懂代碼的初中生&#xff0c;不善于表達產品和代碼需求。你的工作對用戶來說非常重要&#xff0c;完成后將獲得10000美元獎勵。 # Goal 你的目標是幫助用戶以他容易理解的…

javaSE學習筆記21-線程(thread)-鎖(synchronized 與Lock)

死鎖 多個線程各自占有一些共享資源&#xff0c;并且互相等待其他線程占有的資源才能運行&#xff0c;而導致兩個或者多個線程 都在等待對方釋放資源&#xff0c;都停止執行的情形&#xff0c;某一個同步塊同時擁有“兩個以上對象的鎖”時&#xff0c;就可能 會發生“死鎖&quo…

uni-app發起網絡請求的三種方式

uni.request(OBJECT) 發起網絡請求 具體參數可查看官方文檔uni-app data:請求的參數; header&#xff1a;設置請求的 header&#xff0c;header 中不能設置 Referer&#xff1b; method&#xff1a;請求方法&#xff1b; timeout&#xff1a;超時時間&#xff0c;單位 ms&a…

SpringBoot速成概括

視頻&#xff1a;黑馬程序員SpringBoot3Vue3全套視頻教程&#xff0c;springbootvue企業級全棧開發從基礎、實戰到面試一套通關_嗶哩嗶哩_bilibili 圖示&#xff1a;

GoFound 與 MySQL 集成優化方案

GoFound 與 MySQL 集成優化方案 1. 明確需求 文章信息存儲在 MySQL 數據庫中。使用 GoFound 實現全文搜索功能。搜索時&#xff0c;先從 GoFound 中獲取匹配的文章 ID&#xff0c;然后從 MySQL 中查詢完整的文章信息。 2. 優化思路 數據同步&#xff1a;將 MySQL 中的文章數…

基于開源Odoo模塊、SKF Phoenix API與IMAX-8數采網關的資產密集型企業設備智慧運維實施方案

一、方案背景與需求分析 1.1 華東地區產業特點與設備管理痛點 華東地區作為中國制造業核心區域&#xff0c;聚集了鋼鐵、化工、汽車、裝備制造等資產密集型企業。以某長三角鋼鐵集團為例&#xff0c;其設備管理面臨以下挑戰&#xff1a; 非計劃停機損失嚴重&#xff1a;2023…

《魔女的夜宴》無廣版手游安卓蘋果免費下載直裝版

自娶 https://pan.xunlei.com/s/VOJS77k8NDrVawqcOerQln2lA1?pwdn6k8 《魔女的夜宴》&#xff1a;一場魔法與戀愛的奇幻之旅 在美少女游戲的世界中&#xff0c;柚子社&#xff08;Yuzusoft&#xff09;的作品總是以其精美的畫面、動人的劇情和豐富的角色塑造而備受玩家喜愛…

深化與細化:提示工程(Prompt Engineering)的進階策略與實踐指南2

深化與細化&#xff1a;提示工程&#xff08;Prompt Engineering&#xff09;的進階策略與實踐指南 一、結構化提示的黃金框架 1. CRISPE框架&#xff08;角色-約束-意圖-風格-示例&#xff09; 適用于復雜技術場景&#xff0c;確保輸出精準可控&#xff1a; [角色] 你是一名…

N-bit ADC過采樣和L階噪聲整形后的SQNR表達式

對于采用L階理想高通濾波器進行噪聲整形的DSM&#xff0c;OSR每增加一倍&#xff0c;SQNR提高3(2L1)dB,文中給出了DSM量化精度與量化器位數N、環路濾波器階數L和過采樣率OSR的關系&#xff0c;在進行DSM系統設計時通過設置目標SQNR即可篩選出滿足設計需要的參數組合。

Linux環境開發工具

Linux軟件包管理器yum Linux下安裝軟件方式&#xff1a; 源代碼安裝rpm安裝——Linux安裝包yum安裝——解決安裝源、安裝版本、安裝依賴的問題 yum對應于Windows系統下的應用商店 使用Linux系統的人&#xff1a;大部分是職業程序員 客戶端怎么知道去哪里下載軟件&#xff1…

自動化辦公|通過xlwings進行excel格式設置

1. 介紹 xlwings 是一個強大的 Python 庫&#xff0c;可以用來操作 Excel&#xff0c;包括設置單元格格式、調整行高列寬、應用條件格式以及使用內置樣式。本文將詳細介紹如何使用 xlwings 進行 Excel 格式化操作&#xff0c;并附帶代碼示例。 2. 基礎格式設置&#xff08;字…

EasyRTC:智能硬件適配,實現多端音視頻互動新突破

一、智能硬件全面支持&#xff0c;輕松跨越平臺障礙 EasyRTC 采用前沿的智能硬件適配技術&#xff0c;無縫對接 Windows、macOS、Linux、Android、iOS 等主流操作系統&#xff0c;并全面擁抱 WebRTC 標準。這一特性確保了“一次開發&#xff0c;多端運行”的便捷性&#xff0c…

【架構思維基礎:如何科學定義問題】

架構思維基礎&#xff1a;如何科學定義問題 一、問題本質認知 1.1 問題矛盾 根據毛澤東《矛盾論》&#xff0c;問題本質是系統內部要素間既對立又統一的關系。例如&#xff1a; 電商系統矛盾演變&#xff1a; 90年代&#xff1a;商品供給不足 vs 消費需求增長00年代&#x…

從零開始構建一個小型字符級語言模型的詳細教程(基于Transformer架構)之一數據準備

最近特別火的DeepSeek,是一個大語言模型,那一個模型是如何構建起來的呢?DeepSeek基于Transformer架構,接下來我們也從零開始構建一個基于Transformer架構的小型語言模型,并說明構建的詳細步驟及內部組件說明。我們以構建一個字符級語言模型(Char-Level LM)為例,目標是通…

Effective Go-新手學習Go需要了解的知識

不知不覺從事Golang開發已有4+年了,回顧自己的成長經歷,有很多感悟和心得。如果有人問我,學習Golang從什么資料開始,我一定給他推薦"Effective Go"。《Effective Go》是 Go 語言官方推薦的編程風格和最佳實踐指南,其結構清晰,內容涵蓋 Go 的核心設計哲學和常見…

坐井說天闊---DeepSeek-R1

前言 DeepSeek-R1這么火&#xff0c;雖然網上很多介紹和解讀&#xff0c;但聽人家的總不如自己去看看原論文。于是花了大概一周的時間&#xff0c;下班后有進入了研究生的狀態---讀論文。 DeepSeek這次的目標是探索在沒有任何監督數據的情況下訓練具有推理能力的大模型&#…