STM32 串口 (DMA + 空閑中斷 + 環形緩沖區)
1. 基本概念
-
UART 空閑中斷(IDLE):
- 當串口 RX 線上 連續一段時間沒有數據接收,USART 外設觸發 空閑中斷。
- 空閑中斷的主要作用是通知數據傳輸完成或當前幀結束。
-
DMA 接收模式:
- DMA(Direct Memory Access) 自動將串口接收到的數據存儲到指定緩沖區。
- CPU 不再需要逐字節處理接收數據,提高效率。
HAL_UARTEx_ReceiveToIdle_DMA
:啟動 DMA 接收,支持接收數據直到觸發 空閑中斷。
-
環形緩沖區:
- 通過固定大小的緩沖區 + 讀寫指針 實現數據的循環存儲。
- 用于連續接收數據,解決 DMA 數據處理問題。
- 讀寫指針邏輯:
- 寫指針:指向新接收數據的位置。
- 讀指針:指向待處理數據的位置。
2. 流程概述
-
初始化 UART 和 DMA:
- 配置 UART 和 DMA。
- 啟用 DMA 接收并啟動
HAL_UARTEx_ReceiveToIdle_DMA
。
-
串口接收數據:
- 數據通過 DMA 存儲到 DMA 緩沖區
uart_rx_dma_buffer
。 - 串口數據未停止時,DMA 自動接收,CPU 不參與。
- 數據通過 DMA 存儲到 DMA 緩沖區
-
觸發空閑中斷:
- 當 RX 線上 超過一個字節時間沒有數據接收,觸發 空閑中斷(IDLE)。
- 調用
USARTx_IRQHandler
。
-
中斷處理:
- 在
USARTx_IRQHandler
中調用HAL_UART_IRQHandler
。 - HAL 庫檢測到 IDLE 中斷,觸發回調函數
HAL_UARTEx_RxEventCallback
。
- 在
-
回調函數處理接收數據:
- 在
HAL_UARTEx_RxEventCallback
中:- 計算接收到的數據長度。
- 將 DMA 緩沖區的數據拷貝到 環形緩沖區。
- 清空 DMA 緩沖區,準備下一次接收。
- 重新啟動 DMA 接收
HAL_UARTEx_ReceiveToIdle_DMA
。
- 在
-
主循環讀取數據:
- 通過環形緩沖區的 讀寫指針 提取接收到的數據,進行處理。
3. 代碼展示
初始化 UART 和 DMA
void MX_USART1_UART_Init(void) {huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;HAL_UART_Init(&huart1);// 啟動 DMA 接收HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uart_rx_dma_buffer, sizeof(uart_rx_dma_buffer));__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // 禁用半滿中斷
}
中斷服務函數
void USART1_IRQHandler(void) {HAL_UART_IRQHandler(&huart1); // 處理 UART 中斷
/*不同的觸發,跳轉不同的回調函數,若是空閑中斷觸發,跳轉的是 *`HAL_UARTEx_RxEventCallback`*//* 調用HAL庫的串口中斷處理函數 */HAL_UARTEx_ReceiveToIdle_DMA(&huart1, uart_rx_dma_buffer, sizeof(uart_rx_dma_buffer));/* 重新啟動DMA接收,確保下一次接收正常進行 */ __HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);
}
回調函數:處理空閑中斷
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {if (huart->Instance == USART1) {if (!ringbuffer_is_full(&usart_rb)) {ringbuffer_write(&usart_rb, uart_rx_dma_buffer, Size); // 將數據寫入環形緩沖區}memset(uart_rx_dma_buffer, 0, sizeof(uart_rx_dma_buffer)); // 清空 DMA 緩沖區}
}
4. 環形緩沖區實現
環形緩沖區結構
-
ringbuffer_t
結構體用于管理緩沖區:
buffer
:實際存儲數據的數組。r
:讀指針。w
:寫指針。itemCount
:當前緩沖區內的數據量。
typedef struct {uint32_t w; // 寫指針uint32_t r; // 讀指針uint8_t buffer[RINGBUFFER_SIZE]; // 數據存儲緩沖區uint32_t itemCount; // 當前緩沖區數據量
} ringbuffer_t;
函數功能
-
初始化環形緩沖區:
- 清零緩沖區、讀寫指針和數據量。
void ringbuffer_init(ringbuffer_t *rb);
-
寫入數據:
- 檢查緩沖區是否已滿,未滿時將數據寫入。
int8_t ringbuffer_write(ringbuffer_t *rb, uint8_t *data, uint32_t num);
-
讀取數據:
- 檢查緩沖區是否為空,非空時讀取數據。
int8_t ringbuffer_read(ringbuffer_t *rb, uint8_t *data, uint32_t num);
-
緩沖區狀態檢查:
ringbuffer_is_full
:檢查緩沖區是否已滿。ringbuffer_is_empty
:檢查緩沖區是否為空。
5. 工作順序總結
HAL_UARTEx_ReceiveToIdle_DMA
啟動 DMA 接收。- UART 接收到數據,DMA 將數據存入緩沖區。
- 空閑中斷(IDLE)觸發,調用
USART1_IRQHandler
。 HAL_UART_IRQHandler
檢測到空閑中斷,自動調用HAL_UARTEx_RxEventCallback
。- 在回調函數中:
- 處理接收到的數據(寫入環形緩沖區)。
- 重新啟動 DMA 接收(調用
HAL_UARTEx_ReceiveToIdle_DMA
)。
- 主循環處理數據:調用
uart_proc
,從環形緩沖區中提取數據并處理
6. 圖文總結
UART RX --> DMA 接收數據 --> RX 空閑中斷 --> USART1_IRQHandler --> HAL_UARTEx_RxEventCallback| ||---- 重新啟動 DMA 接收 ----------------|
數據 --> 寫入環形緩沖區 --> 主循環讀取數據 --> 用戶處理
本系統實現了 STM32 串口 DMA 接收 + 空閑中斷 + 環形緩沖區,旨在高效接收和處理串口不定長數據,保證數據的完整性與實時性。其核心工作流程如下:
- DMA 接收:通過調用
HAL_UARTEx_ReceiveToIdle_DMA
函數啟動 DMA 模式接收,將接收到的數據自動存儲到 DMA 緩沖區,減少 CPU 干預,提高效率。 - 空閑中斷觸發:當串口 RX 線超過一個字節時間沒有新數據輸入時,觸發 空閑中斷(IDLE),并在中斷中調用
HAL_UART_IRQHandler
,進一步觸發HAL_UARTEx_RxEventCallback
回調函數。 - 數據存儲:
在回調函數HAL_UARTEx_RxEventCallback
中,將 DMA 緩沖區接收到的數據寫入 環形緩沖區,通過ringbuffer_write
函數實現數據的安全存儲,防止數據丟失。隨后清空 DMA 緩沖區,重新啟動 DMA 接收,確保數據連續接收。 - 數據處理:
在主循環中,調用uart_proc
函數,通過ringbuffer_read
從環形緩沖區讀取數據進行處理。環形緩沖區通過讀寫指針和數據計數機制實現數據的循環存儲與讀取,適用于不定長、連續數據接收的場景。 - 環形緩沖區管理:
ringbuffer_is_full
和ringbuffer_is_empty
用于判斷緩沖區狀態。ringbuffer_write
和ringbuffer_read
分別實現數據的寫入與讀取,確保緩沖區數據有序管理,防止數據丟失。
系統特點
- 高效性:DMA 自動接收數據,減少 CPU 開銷。
- 實時性:通過 UART 空閑中斷實時捕獲數據接收完成狀態。
- 可靠性:使用環形緩沖區管理數據,確保數據存儲穩定,避免數據丟失。
- 適用性:適合高頻率、不定長數據的串口通信場景。