UART串口是嵌入式開發(fā)常見的一種通信方式,但還是有不少人不知道怎么使用串口。
今天就來圍繞串口,簡單分享幾點內(nèi)容:
串口接收方式
處理接收數(shù)據(jù)
通信協(xié)議解析
串口接收方式
串口接收(通信另一端)的數(shù)據(jù),常見的方式:
輪詢(查詢)接收寄存器
中斷接收數(shù)據(jù)
輪詢,就是間隔一定時間(一般ms,甚至us)去查詢一下接收寄存器是否有數(shù)據(jù),如果有數(shù)據(jù),就處理接收到的數(shù)據(jù)。
中斷,平時沒有數(shù)據(jù)接收時,CPU干自己的事。當有接收數(shù)據(jù)時,UART串口控制器會響應(yīng)中斷,通知CPU有事干了。
輪詢方式,大家想過有哪些弊端嗎?
效率低:CPU大部分時間都是去做查詢的工作;
響應(yīng)不實時:如果短時間內(nèi)有多個接收數(shù)據(jù),CPU正在處理一件相對耗時的事情(比如:發(fā)送一個數(shù)據(jù)包),沒來得及查詢接收到的數(shù)據(jù),此時,數(shù)據(jù)就可能丟失。(特別是早些年串口沒有FIFO功能的時候)
所以,不管是UART串口,還是I2C、 SPI、 CAN等串行通信,用的最多,最常見的還是中斷接收,很少有用輪詢的方式。
我之前維護一個老代碼(坑),CLI串口用輪詢方式,出現(xiàn)丟數(shù)據(jù)、溢出錯誤等眾多問題,讓我還加了好幾個班。。。
處理接收數(shù)據(jù)
中斷有數(shù)據(jù)來了,大家怎么處理接收到的數(shù)據(jù)?
我見過有些小項目,直接在中斷函數(shù)里面做一些應(yīng)用的情況。比如:串口中斷接收一個傳感器發(fā)過來的數(shù)據(jù),顯示數(shù)據(jù)并做一些響應(yīng)的動作。
中斷函數(shù),代碼能少盡少,耗時能少盡少,不能處理太多耗時的復雜的邏輯、應(yīng)用等。
中斷有數(shù)據(jù)來了,一般是通過FIFO方式處理。
1.簡單的數(shù)組接收、應(yīng)用解析并處理
比如:
static uint8_t gRxCnt = 0;
static uint8_t gRxBuf[10];
void USART1_IRQHandler(void)
{
//...
gDgus_RxBuf[gRxCnt] = (uint8_t)USART_ReceiveData(USART1);
gRxCnt++;
//...
}
void App(void)
{
//...
if(0 < gRxCnt)
{
//拷貝接收到的數(shù)據(jù)
????gRxCnt?=?0;
????//解析接收數(shù)據(jù)并處理
}
}
2.中斷函數(shù)接收一幀完整數(shù)據(jù)再處理
比如:
void USART1_IRQHandler(void)
{
static uint8_t RxCnt = 0; //計數(shù)值
static uint8_t RxNum = 0; //數(shù)量
if((USART1->SR & USART_FLAG_RXNE) == USART_FLAG_RXNE)
{
gDgus_RxBuf[RxCnt] = (uint8_t)USART_ReceiveData(USART1);
RxCnt++;
/* 判斷幀頭 */
if(gDgus_RxBuf[0] != DGUS_FRAME_HEAD1) //接收到幀頭1
{
RxCnt = 0;
return;
}
if((2 == RxCnt) && (gDgus_RxBuf[1] != DGUS_FRAME_HEAD2))
{
RxCnt = 0;
return;
}
/* 確定一幀數(shù)據(jù)長度 */
if(RxCnt == 3)
{
RxNum = gDgus_RxBuf[2] + 3;
}
/* 接收完一幀數(shù)據(jù) */
if((6 <= RxCnt) && (RxNum <= RxCnt))
{
RxCnt = 0;
OSMboxPost(EventMBox_Touch, gDgus_RxBuf); //發(fā)送消息郵箱(執(zhí)行觸控操作)
}
}
}
中斷函數(shù)解析完一幀數(shù)據(jù),可以通過標志位通知應(yīng)用(裸機時),也可以通過消息隊列、郵箱等方式發(fā)送到應(yīng)用(RTOS時)。
3.RTOS隊列、郵箱接收
比如:
void DEBUG_COM_IRQHandler(void)
{
static uint8_t Data;
if(USART_GetITStatus(DEBUG_COM, USART_IT_RXNE) != RESET)
{
Data = USART_ReceiveData(DEBUG_COM);
CLI_RcvDateFromISR(Data);//下面把這個函數(shù)分離出來了
}
}
void CLI_RcvDateFromISR(uint8_t RcvData)
{
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if(xCLIRcvQueue != NULL)
{
xQueueSendFromISR(xCLIRcvQueue, &RcvData, &xHigherPriorityTaskWoken);
}
}
中斷來一字節(jié)數(shù)據(jù),就通過消息隊列發(fā)送一個字節(jié)數(shù)據(jù),如果沒有及時出來這個數(shù)據(jù),也是存儲在隊列中。
通信協(xié)議解析
像上面第2種,簡單通信協(xié)議,項目相對較小的情況下,可以直接在中斷函數(shù)里面處理。
但是,如果項目相對較大、復雜一點,協(xié)議也先對復雜一點,上面第2種在函數(shù)內(nèi)部出來方式就不可取。
1.裸機環(huán)境
裸機的情況下,建議用第一種:中斷數(shù)組緩存數(shù)據(jù)(FIFO),應(yīng)用解析通信協(xié)議。
2.RTOS環(huán)境
RTOS情況下,建議用第三種方式:消息隊列、郵箱等方式接收數(shù)據(jù),然后發(fā)送(通知)應(yīng)用解析協(xié)議。
當然,以上說的都只是常見的方式,具體還需要結(jié)合你項目實際情況。
同時,其它類似I2C、CAN等通信,如有協(xié)議解析,也是類似。
比如之前給大家分享的MavLink,我就用CAN實現(xiàn)過:
void CAN_RX_IRQHandler(void)
{
static CanRxMsg RxMessage;
static MAVRCV_QUEUE_TypeDef MAVRcvQueue_Union;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
//拷貝長度、 數(shù)據(jù)
MAVRcvQueue_Union.MAVRcvStruct.MAVLink_Len = RxMessage.DLC;
memcpy(&MAVRcvQueue_Union.MAVRcvStruct.MAVLink_Buf[0], &RxMessage.Data[0], RxMessage.DLC);
MAVLink_RcvDateFromISR(&MAVRcvQueue_Union.MAVLinkRcv_Queue[0]);
}
最后,以上內(nèi)容,僅提供思路,代碼不一定適合項目。
審核編輯 :李倩
-
寄存器
+關(guān)注
關(guān)注
31文章
5609瀏覽量
130016 -
uart
+關(guān)注
關(guān)注
22文章
1314瀏覽量
106680 -
嵌入式開發(fā)
+關(guān)注
關(guān)注
18文章
1146瀏覽量
50135
原文標題:UART接收數(shù)據(jù)和解析的常見方式
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
介紹內(nèi)部EEPROM數(shù)據(jù)讀取和解析
stm32 uart1如何通過DMA方式發(fā)送和接收數(shù)據(jù)?
如何去實現(xiàn)Stm32 Uart用DMA的方式接收數(shù)據(jù)呢
如何使用查詢方式通過UART接收數(shù)據(jù)
常見的數(shù)據(jù)發(fā)送接收處理方式
uart pdma方式接收不定長數(shù)據(jù)如何解決?
STC單片機串口接收一幀數(shù)據(jù)全為0的原因和解決辦法
K210應(yīng)用5-使用查詢方式通過UART接收數(shù)據(jù)
無功補償?shù)脑怼⒆饔眉?b class='flag-5'>常見方式
常見的UART收發(fā)方式
如何采用“狀態(tài)機”解析UART數(shù)據(jù)幀
UART接收數(shù)據(jù)和解析的常見方式
評論