詳細講解在STM32的UART通信中使用DMA機制
目錄
- 詳細講解在STM32的UART通信中使用DMA機制
- 一、DMA機制概述
- 二、DMA在UART中的作用
- 三、DMA的配置步驟
- 四、UART初始化與DMA結合
- 五、DMA傳輸的中斷處理
- 六、DMA與中斷的結合使用
- 七、注意事項與常見問題
- 八、代碼示例
- 九、總結
一、DMA機制概述
DMA(Direct Memory Access) 是一種硬件機制,允許外設不需要CPU干預的情況下直接訪問內存。它能夠顯著提升數據傳輸的效率,尤其是在需要頻繁數據傳輸的場景中。
二、DMA在UART中的作用
在STM32微控制器中, UART模塊支持DMA傳輸,能夠實現在CPU空閑的情況下,快速傳輸大量數據。具體來說,DMA可以將UART接收的數據直接傳輸到內存中的緩沖區,或者將內存中的數據緩沖區直接傳輸到UART發送緩沖區,從而降低CPU負載,提高系統效率。
三、DMA的配置步驟
- 使能DMA時鐘
在使用DMA之前,需要先使能DMA外設的時鐘。不同的 DMA 通道對應不同的時鐘配置。
// 使能DMA1時鐘(例如,對于DMA1的信道)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- 設置DMA Channel和DMA Mode 配置DMA通道和傳輸模式(比如正常模式、循環模式等)。
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0; // 選擇DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; // UART數據寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rxBuffer; // 接收緩沖區地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 設置傳輸方向,外設到內存
DMA_InitStructure.DMA_BufferSize = RX_BUFFER_SIZE; // 接收緩沖區大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外設地址不變
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 內存地址遞增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 數據寬度為字節
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 設置DMA優先級為高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO模式關閉
DMA_Init(DMA1_Channel0, &DMA_InitStructure); // 初始化DMA通道
- 使能DMA傳輸 完成配置后,使能DMA通道。
DMA_Cmd(DMA1_Channel0, ENABLE);
四、UART初始化與DMA結合
- UART初始化配置 配置UART的基本參數,如波特率、數據格式、校驗位等。
// 配置USART1
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
- 配置UART的DMA傳輸 使能 UART的DMA傳輸功能,通常在UART的中斷配置中完成。
// 使能USART1的DMA接收功能
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
五、DMA傳輸的中斷處理
- 配置DMA傳輸完成中斷 配置DMA傳輸完成后觸發中斷,以便進行后續處理。
// 配置DMA中斷
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 2; // 中斷優先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); - 編寫DMA中斷服務函數 在中斷服務函數中進行數據處理和DMA重裝載操作。
void DMA1_Channel0_IRQHandler(void) {
if (DMA_GetFlagStatus(DMA1_Channel0, DMA_FLAG_TCIF0)) {
// 清除傳輸完成標志
DMA_ClearFlag(DMA1_Channel0, DMA_FLAG_TCIF0);
// 處理接收到的數據
processData(rxBuffer, RX_BUFFER_SIZE);
// 重新裝載DMA緩沖區
DMA_SetCurrDataCounter(DMA1_Channel0, RX_BUFFER_SIZE);
}
}
六、DMA與中斷的結合使用
1.DMA和中斷的區別
? DMA :在不占用CPU的情況下,直接完成內存和外設之間的數據傳輸,適合大數據量的傳輸。
? 中斷 :當外設需要CPU處理特定事件時,通過中斷機制通知CPU。
2.在UART通信中DMA的優勢
? 提高了數據傳輸的效率,減少了CPU的負載。
? 適用于需要大量數據傳輸的應用場景,如長時間的串口數據采集。
3.什么時候最適合使用DMA
? 當需要實現高效的、大量數據的UART傳輸時。
? 當希望降低CPU使用率,讓CPU專注于其他任務處理時。
七、注意事項與常見問題
1.DMA傳輸中的內存配置 確保DMA傳輸的內存區域是連續的,避免因內存碎片導致傳輸錯誤。
2.緩沖區的大小和管理 合理設置DMA傳輸的緩沖區大小,避免因為緩沖區過小而導致頻繁的DMA中斷,或因為緩沖區過大而導致內存浪費。
3.DMA傳輸方向的設置 根據實際需求設置正確的傳輸方向(外設到內存或內存到外設),否則會導致數據傳輸錯誤。
4.DMA優先級的設置 根據系統的實際工作情況,合理設置DMA通道的優先級,以確保關鍵任務的優先執行。
5.傳輸完成后DMA的重裝載 在DMA傳輸完成后,配置DMA為循環模式或手動重新裝載模式,以保證持續的數據傳輸需求。
八、代碼示例
以下是一個完整的示例代碼,展示了如何在STM32中配置和使用DMA進行UART數據接收:
#include "stm32f10x.h"#define RX_BUFFER_SIZE 256uint8_t rxBuffer[RX_BUFFER_SIZE];void DMA_Configuration(void) {DMA_InitTypeDef DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;// 使能DMA1時鐘RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 配置DMA通道0DMA_InitStructure.DMA_Channel = DMA_Channel_0;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rxBuffer;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;DMA_InitStructure.DMA_BufferSize = RX_BUFFER_SIZE;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Channel0, &DMA_InitStructure);// 使能DMA通道0DMA_Cmd(DMA1_Channel0, ENABLE);// 配置DMA中斷優先級NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel0_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 啟用DMA傳輸完成中斷DMA_ITConfig(DMA1_Channel0, DMA_IT_TC, ENABLE);
}
void USART_Configuration(void) {
USART_InitTypeDef USART_InitStructure;
// 使能USART1外設時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // TX和RX引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
// 使能USART接收DMA
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
// 啟動USART
USART_Cmd(USART1, ENABLE);
}
void USART_NVIC_Configuration(void) {
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void) {
// 處理 USART 中斷,如有需要
//,...
}
void DMA1_Channel0_IRQHandler(void) {
if (DMA_GetFlagStatus(DMA1_Channel0, DMA_FLAG_TCIF0)) {
// 清除 DMA 傳輸完成標志
DMA_ClearFlag(DMA1_Channel0, DMA_FLAG_TCIF0);
// 數據處理操作
processData(rxBuffer, RX_BUFFER_SIZE);
// 重新裝載 DMA 緩沖區
DMA_SetCurrDataCounter(DMA1_Channel0, RX_BUFFER_SIZE);
}
}
int main(void) {
// 系統時鐘配置(假設已經完成)
// 配置DMA
DMA_Configuration();
// 配置USART1
USART_Configuration();
// 配置USART中斷
USART_NVIC_Configuration();
// 進入主循環
while (1) {
// 主循環可以執行其他任務
}
}
九、總結
通過在STM32的UART通信中使用DMA機制,可以在不占用CPU的情況下實現高效的數據傳輸,顯著提升系統的性能和響應速度。DMA的配置和使用需要仔細理解其工作原理和具體的配置步驟,確保在實際應用中的正確性和穩定性。
在實際開發中,還應結合具體的應用場景和硬件配置,進行合理的參數設置和優化,以達到最佳的傳輸效率和穩定