【详细讲解在STM32的UART通信中使用DMA机制】
详细讲解在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 256
uint8_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通道0
DMA_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通道0
DMA_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的配置和使用需要仔细理解其工作原理和具体的配置步骤,确保在实际应用中的正确性和稳定性。
在实际开发中,还应结合具体的应用场景和硬件配置,进行合理的参数设置和优化,以达到最佳的传输效率和稳定