UART串口是嵌入式開發常見的一種通信方式,但還是有不少人不知道怎么使用串口。
今天就來圍繞串口,簡單分享幾點內容:
串口接收方式
處理接收數據
通信協議解析
串口接收方式
串口接收(通信另一端)的數據,常見的方式:
輪詢(查詢)接收寄存器
中斷接收數據
輪詢,就是間隔一定時間(一般ms,甚至us)去查詢一下接收寄存器是否有數據,如果有數據,就處理接收到的數據。
中斷,平時沒有數據接收時,CPU干自己的事。當有接收數據時,UART串口控制器會響應中斷,通知CPU有事干了。
輪詢方式,大家想過有哪些弊端嗎?
效率低:CPU大部分時間都是去做查詢的工作;
響應不實時:如果短時間內有多個接收數據,CPU正在處理一件相對耗時的事情(比如:發送一個數據包),沒來得及查詢接收到的數據,此時,數據就可能丟失。(特別是早些年串口沒有FIFO功能的時候)
所以,不管是UART串口,還是I2C、 SPI、 CAN等串行通信,用的最多,最常見的還是中斷接收,很少有用輪詢的方式。
我之前維護一個老代碼(坑),CLI串口用輪詢方式,出現丟數據、溢出錯誤等眾多問題,讓我還加了好幾個班。。。
處理接收數據
中斷有數據來了,大家怎么處理接收到的數據?
我見過有些小項目,直接在中斷函數里面做一些應用的情況。比如:串口中斷接收一個傳感器發過來的數據,顯示數據并做一些響應的動作。
中斷函數,代碼能少盡少,耗時能少盡少,不能處理太多耗時的復雜的邏輯、應用等。
中斷有數據來了,一般是通過FIFO方式處理。
1.簡單的數組接收、應用解析并處理
比如:
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) { //拷貝接收到的數據 ????gRxCnt?=?0; ????//解析接收數據并處理 } }
2.中斷函數接收一幀完整數據再處理
比如:
void USART1_IRQHandler(void) { static uint8_t RxCnt = 0; //計數值 static uint8_t RxNum = 0; //數量 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; } /* 確定一幀數據長度 */ if(RxCnt == 3) { RxNum = gDgus_RxBuf[2] + 3; } /* 接收完一幀數據 */ if((6 <= RxCnt) && (RxNum <= RxCnt)) { RxCnt = 0; OSMboxPost(EventMBox_Touch, gDgus_RxBuf); //發送消息郵箱(執行觸控操作) } } }
中斷函數解析完一幀數據,可以通過標志位通知應用(裸機時),也可以通過消息隊列、郵箱等方式發送到應用(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);//下面把這個函數分離出來了 } } void CLI_RcvDateFromISR(uint8_t RcvData) { static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; if(xCLIRcvQueue != NULL) { xQueueSendFromISR(xCLIRcvQueue, &RcvData, &xHigherPriorityTaskWoken); } }
中斷來一字節數據,就通過消息隊列發送一個字節數據,如果沒有及時出來這個數據,也是存儲在隊列中。
通信協議解析
像上面第2種,簡單通信協議,項目相對較小的情況下,可以直接在中斷函數里面處理。
但是,如果項目相對較大、復雜一點,協議也先對復雜一點,上面第2種在函數內部出來方式就不可取。
1.裸機環境
裸機的情況下,建議用第一種:中斷數組緩存數據(FIFO),應用解析通信協議。
2.RTOS環境
RTOS情況下,建議用第三種方式:消息隊列、郵箱等方式接收數據,然后發送(通知)應用解析協議。
當然,以上說的都只是常見的方式,具體還需要結合你項目實際情況。
同時,其它類似I2C、CAN等通信,如有協議解析,也是類似。
比如之前給大家分享的MavLink,我就用CAN實現過:
void CAN_RX_IRQHandler(void) { static CanRxMsg RxMessage; static MAVRCV_QUEUE_TypeDef MAVRcvQueue_Union; CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //拷貝長度、 數據 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]); }
最后,以上內容,僅提供思路,代碼不一定適合項目。
審核編輯 :李倩
-
寄存器
+關注
關注
31文章
5325瀏覽量
120053 -
uart
+關注
關注
22文章
1228瀏覽量
101200 -
嵌入式開發
+關注
關注
18文章
1022瀏覽量
47518
原文標題:UART接收數據和解析的常見方式
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論