說來慚愧,玩了一年多的單片機,但是卻一直沒有一個非常好的串口接收策略,之前同學推薦了idle閑時中斷接收,當時也是因為時間原因沒有自己去嘗試,寒假准備完善自己的基礎代碼庫的時候才想起這回事。其實發的這篇文章並沒有什么技術含量,只是因為我的實現方案相較於網上的都比較簡單,和HAL庫結合比較緊密。同時也感嘆一下HAL庫和CUBEMX的組合實在是方便。
介紹一下串口的Idle中斷,參考手冊是這么說的:
When an idle frame is detected, there is the same procedure as a data received character plus an interrupt if the IDLEIE bit is set.
意思是,如果IDLEIE被設置后,那么當接收數據后的空閑幀被檢測到之后才會觸發一個中斷
關於網上也有不少配置DMA+串口idle的教程,我看了不少,感覺都挺麻煩,要自己配置不少東西。不過出於習慣,我經常直接去HAL庫的.h文件里翻函數寫,於是我發現了下面這套函數:
這不就是HAL庫封裝的關於閑時中斷的三個接收函數嗎,而且還把阻塞中斷DMA三種接收方式都封裝了。
這三個函數網上找了找幾乎就沒找到資料,不過好在HAL庫的注釋夠全,光看注釋也看的通。
那么我們核心的函數就是這個了:
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
下面是它的注釋
/**
* @brief Receive an amount of data in DMA mode till either the expected number of data is received or an IDLE event occurs.
* @note Reception is initiated by this function call. Further progress of reception is achieved thanks
* to DMA services, transferring automatically received data elements in user reception buffer and
* calling registered callbacks at half/end of reception. UART IDLE events are also used to consider
* reception phase as ended. In all cases, callback execution will indicate number of received data elements.
* @note When the UART parity is enabled (PCE = 1), the received data contain
* the parity bit (MSB position).
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M = 01),
* the received data is handled as a set of uint16_t. In this case, Size must indicate the number
* of uint16_t available through pData.
* @param huart UART handle.
* @param pData Pointer to data buffer (uint8_t or uint16_t data elements).
* @param Size Amount of data elements (uint8_t or uint16_t) to be received.
* @retval HAL status
*/
從注釋也是可以看出來他會通過DMA接收數據,超出我們指定的長度或者發生了串口閑時中斷,可以說是一步到位了。
我們要做的只是調用這個函數,然后寫個接收事件回調函數即可。
這個事件回調函數也是HAL庫預先幫我們做好的weak函數,要用的時候自己找個地方定義即可。
下面是函數調用棧,可以看到HAL庫寫的還是很復雜的
配置
挺簡單的,自己搗鼓去,我不想詳細寫。
主要就幾點:
- CUBEMX里面配置好串口和DMA,然后記得開啟串口的中斷
- 調用函數
HAL_UARTEx_ReceiveToIdle_DMA
- 在回調函數
HAL_UARTEx_RxEventCallback
中記錄下此次接收到的數據長度 - 要想再接收數據就必須再次調用接收函數