| 
 
| 一、功能概述 本教程基于STM32F103芯片,通过DMA(直接内存访问)实现高效串口通信,并结合**环形缓冲区(Circular Buffer)**解决数据接收时的溢出问题。代码核心功能如下:
 USART1的DMA收发配置:自动传输数据,不占用CPU资源。
 循环缓冲区管理:确保接收数据的连续性,避免丢失。
 中断触发机制:通过空闲中断检测一帧数据结束,通过DMA中断管理缓冲区状态。
 
 二、代码结构解析
 1. 宏定义与全局变量
 #define USART1_RX_DMA_CHANNEL DMA1_Channel5  // 接收DMA通道
 #define USART1_TX_DMA_CHANNEL DMA1_Channel4  // 发送DMA通道
 #define INTERCOM_RX_BUFF_SIZE 512            // 接收缓冲区大小
 #define BUFFER_SIZE INTERCOM_RX_BUFF_SIZE    // 环形缓冲区大小
 
 // 环形缓冲区结构体
 typedef struct {
 volatile uint8_t buffer[BUFFER_SIZE];
 volatile uint16_t head;   // 数据起始位置
 volatile uint16_t tail;   // 数据结束位置
 volatile uint16_t count;  // 当前数据量
 } CircularBuffer_T;
 
 // 全局DMA通信管理结构体
 TRANSFER_UART_DMA_T Transfer_UART_DMA;
 
 
 关键点:
 
 接收使用循环模式DMA,发送使用单次模式DMA。
 环形缓冲区通过head和tail指针实现数据的循环覆盖,count记录有效数据量。
 2. 环形缓冲区操作函数
 初始化缓冲区
 
 void CB_InitBuffer(CircularBuffer_T *cb) {
 cb->head = 0;
 cb->tail = 0;
 cb->count = 0;
 memset(cb->buffer, 0, BUFFER_SIZE);
 }
 
 
 数据出队(从缓冲区读取)
 
 uint8_t CB_Dequeue(CircularBuffer_T *cb, uint8_t *data) {
 if (cb->count == 0) return 1; // 缓冲区为空
 *data = cb->buffer[cb->head];
 cb->head = (cb->head + 1) % BUFFER_SIZE;
 cb->count--;
 return 0;
 }
 
 
 3. USART1与DMA初始化
 USART1配置(Transfer_USART1_Init_Config)
 
 GPIO配置:
 PB6(TX):复用推挽输出。
 PB7(RX):浮空输入。
 波特率设置:根据SystemParam.Uart_Baud_Rate_Num选择(2400~115200)。
 中断配置:使能空闲中断(USART_IT_IDLE),用于检测一帧数据接收完成。
 关键代码:
 
 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 空闲中断
 USART_Cmd(USART1, ENABLE);                      // 启动USART1
 
 
 4. 数据收发流程
 发送数据(Transfer_USART1_DMA_Send)
 检查发送缓冲区是否空闲。
 将数据拷贝到发送缓冲区tx_buff。
 设置DMA传输长度并启动发送。
 代码逻辑:
 
 memcpy(Transfer_UART_DMA.tx_buff, data_p, len); // 数据拷贝
 DMA_SetCurrDataCounter(USART1_TX_DMA_CHANNEL, len); // 设置长度
 DMA_Cmd(USART1_TX_DMA_CHANNEL, ENABLE);        // 启动发送
 
 
 接收数据(中断处理)
 空闲中断(USART1_IRQHandler):
 触发条件:串口总线空闲(一帧数据接收完毕)。
 计算接收到的数据长度,更新环形缓冲区指针。
 DMA完成中断(DMA1_Channel5_IRQHandler):
 标记缓冲区循环状态,处理数据覆盖逻辑。
 // 空闲中断中更新缓冲区
 temp = DMA_GetCurrDataCounter(USART1_RX_DMA_CHANNEL);
 Transfer_UART_DMA.rx_buff.tail = BUFFER_SIZE - temp;
 
 
 二、完整代码
 #define USART1_RX_DMA_CHANNEL DMA1_Channel5
 #define USART1_TX_DMA_CHANNEL DMA1_Channel4
 
 
 #define INTERCOM_RX_BUFF_SIZE 512
 #define INTERCOM_TX_BUFF_SIZE 512
 
 // 定义环形缓冲区的大小
 #define BUFFER_SIZE INTERCOM_RX_BUFF_SIZE
 
 typedef struct
 {
 volatile uint8_t buffer[BUFFER_SIZE]; // 存储数据的数组
 volatile uint16_t head;               // 指向第一个有效数据的索引
 volatile uint16_t tail;               // 指向下一个空闲位置的索引
 volatile uint16_t count;              // 当前缓冲区中有效数据的数量
 } CircularBuffer_T;
 
 typedef struct
 {
 // volatile uint8_t rx_flag;
 // uint8_t rx_buff[INTERCOM_RX_BUFF_SIZE];
 // volatile uint16_t rx_len;
 
 CircularBuffer_T rx_buff;
 volatile uint8_t rx_buff_loopback;
 uint8_t tx_buff[INTERCOM_TX_BUFF_SIZE];
 
 } TRANSFER_UART_DMA_T;
 
 
 TRANSFER_UART_DMA_T Transfer_UART_DMA;
 
 CircularBuffer_T Rx_CircularBuffer;
 
 // 初始化环形缓冲区
 void CB_InitBuffer(CircularBuffer_T *cb)
 {
 cb->head = 0;
 cb->tail = 0;
 cb->count = 0;
 // 初始化数组为0
 for (int i = 0; i < BUFFER_SIZE; i++)
 cb->buffer = 0;
 }
 
 // 检查环形缓冲区是否为空
 uint8_t CB_IsEmpty(CircularBuffer_T *cb)
 {
 return cb->count == 0;
 }
 
 // 检查环形缓冲区是否已满
 uint8_t CB_IsFull(CircularBuffer_T *cb)
 {
 return cb->count == BUFFER_SIZE;
 }
 
 /**
 * @brief  从环形缓冲区取出数据
 * @param  无
 * @retval 0 有数据,1 无数据
 */
 uint8_t CB_Dequeue(CircularBuffer_T *cb, uint8_t *data)
 {
 if (CB_IsEmpty(cb))
 return 1; // 缓冲区为空
 
 if (data == NULL)
 return 1;
 
 *data = cb->buffer[cb->head];
 cb->head = (cb->head + 1) % BUFFER_SIZE;
 cb->count--;
 return 0;
 }
 
 
 /**
 * @brief  USART1配置
 * @param  无
 * @retval 无
 */
 void Transfer_USART1_Init_Config(void)
 {
 uint8_t clear = clear;
 GPIO_InitTypeDef GPIO_InitStructure;
 USART_InitTypeDef USART_InitStructure;
 NVIC_InitTypeDef NVIC_InitStruct;
 
 // 打开时钟
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
 
 // 将USART Tx的GPIO配置为推挽复用模式
 GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 // 将USART Rx的GPIO配置为浮空输入
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 // 配置串口的工作参数
 // 配置波特率
 switch (SystemParam.Uart_Baud_Rate_Num)
 {
 case 0:
 USART_InitStructure.USART_BaudRate = 2400;
 break;
 case 1:
 USART_InitStructure.USART_BaudRate = 4800;
 break;
 case 2:
 USART_InitStructure.USART_BaudRate = 9600;
 break;
 case 3:
 USART_InitStructure.USART_BaudRate = 19200;
 break;
 case 4:
 USART_InitStructure.USART_BaudRate = 38400;
 break;
 case 5:
 USART_InitStructure.USART_BaudRate = 115200;
 break;
 default:
 USART_InitStructure.USART_BaudRate = 115200;
 break;
 }
 // USART_InitStructure.USART_BaudRate = UART_TRANSFER_BAUD_RATE;
 // 配置帧数据字长
 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
 // 配置停止位
 USART_InitStructure.USART_StopBits = USART_StopBits_1;
 // 配置校验位
 USART_InitStructure.USART_Parity = USART_Parity_No;
 // 配置硬件流控制
 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
 // 配置工作模式,收发一起
 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 // 完成串口的初始化配置
 USART_Init(USART1, &USART_InitStructure);
 
 NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
 NVIC_InitStruct.NVIC_IRQChannelSubPriority = UART1_PRIORITY;
 NVIC_Init(&NVIC_InitStruct);
 
 // //使能发送完成中断
 // USART_ITConfig(USART1,USART_IT_TC,ENABLE);
 // 使能空闲中断
 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
 USART_ClearFlag(USART1, USART_IT_IDLE);
 clear = USART1->SR;
 clear = USART1->DR;
 // 使能串口
 USART_Cmd(USART1, ENABLE);
 }
 
 /**
 * @brief  USART1 DMA配置
 * @param  无
 * @retval 无
 */
 void Transfer_USART1_DMA_Init_Config(void)
 {
 DMA_InitTypeDef DMA_InitStructure;
 NVIC_InitTypeDef NVIC_InitStruct;
 
 // 开启DMA时钟
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 
 DMA_DeInit(USART1_TX_DMA_CHANNEL); // TX
 DMA_DeInit(USART1_RX_DMA_CHANNEL); // RX
 
 // Rx到接收缓冲区
 DMA_StructInit(&DMA_InitStructure);
 //  设置DMA源地址:串口数据寄存器地址*/
 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART1->DR));
 // 内存地址(要传输的变量的指针)
 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Transfer_UART_DMA.rx_buff.buffer;
 // 方向:从外设到内存
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
 // 传输大小
 DMA_InitStructure.DMA_BufferSize = INTERCOM_RX_BUFF_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模式,循环
 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA_Mode_Circular DMA_Mode_Normal
 // 优先级:中
 DMA_InitStructure.DMA_Priority = DMA_Priority_High;
 // 禁止内存到内存的传输
 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
 // 配置DMA通道
 DMA_Init(USART1_RX_DMA_CHANNEL, &DMA_InitStructure);
 
 // 开启传输完成中断
 DMA_ITConfig(USART1_RX_DMA_CHANNEL, DMA_IT_TC, ENABLE);
 DMA_ClearFlag(DMA1_FLAG_TC5);
 
 NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;
 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
 NVIC_InitStruct.NVIC_IRQChannelSubPriority = DMA_PRIORITY;
 NVIC_Init(&NVIC_InitStruct);
 
 // 发送缓冲区到TX
 DMA_StructInit(&DMA_InitStructure);
 
 //  设置DMA源地址:串口数据寄存器地址*/
 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART1->DR));
 // 内存地址(要传输的变量的指针)
 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Transfer_UART_DMA.tx_buff;
 // 方向:从外设到内存
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
 // 传输大小
 DMA_InitStructure.DMA_BufferSize = 0;
 // 外设地址不增
 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模式,循环
 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA_Mode_Circular DMA_Mode_Normal
 // 优先级:中
 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
 // 禁止内存到内存的传输
 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
 
 DMA_Init(USART1_TX_DMA_CHANNEL, &DMA_InitStructure);
 
 // 使能DMA
 DMA_Cmd(USART1_RX_DMA_CHANNEL, ENABLE);
 DMA_Cmd(USART1_TX_DMA_CHANNEL, ENABLE);
 
 USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE); // 使能DMA串口发送和接受请求
 }
 
 void Transfer_USART1_DMA_Start(void)
 {
 DMA_Cmd(USART1_RX_DMA_CHANNEL, DISABLE);
 // 重新写入要传输的数据数量
 DMA_SetCurrDataCounter(USART1_RX_DMA_CHANNEL, INTERCOM_RX_BUFF_SIZE);
 // 启动DMA
 DMA_Cmd(USART1_RX_DMA_CHANNEL, ENABLE);
 }
 
 /**
 * @brief  USART1 DMA 发送数据
 * @param  无
 * @retval 0 正常,1 异常
 */
 uint8_t Transfer_USART1_DMA_Send(uint8_t *data_p, uint16_t len)
 {
 // /* 发送一个字节数据到USART */
 // USART_SendData(USART1,*data_p);
 
 // /* 等待发送数据寄存器为空 */
 // while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
 
 if (len == 0 || len > INTERCOM_TX_BUFF_SIZE)
 return 1;
 
 // if (DMA_GetCurrDataCounter(USART1_TX_DMA_CHANNEL))
 //     return 1;
 while (DMA_GetCurrDataCounter(USART1_TX_DMA_CHANNEL))
 ;
 
 if (data_p)
 memcpy(Transfer_UART_DMA.tx_buff, data_p, len);
 
 DMA_Cmd(USART1_TX_DMA_CHANNEL, DISABLE);
 DMA_SetCurrDataCounter(USART1_TX_DMA_CHANNEL, len); // 重新写入要传输的数据数量
 DMA_Cmd(USART1_TX_DMA_CHANNEL, ENABLE);             // 启动DMA发送
 
 return 0;
 }
 
 void Transfer_USART1_DMA_Send_Test(void)
 {
 // if (Transfer_USART1_DMA_Send(Transfer_UART_DMA.rx_buff, Transfer_UART_DMA.rx_len) == 0)
 //     ;
 // Transfer_UART_DMA.rx_len = 0;
 
 uint8_t rx_data = 0;
 if (CB_Dequeue(&(Transfer_UART_DMA.rx_buff), &rx_data) == 0)
 Transfer_USART1_DMA_Send(&rx_data, 1);
 }
 
 /**
 * @brief  USART1 中断
 * @param  无
 * @retval 无
 */
 void USART1_IRQHandler(void)
 {
 uint16_t temp;
 
 // 空闲中断
 if (USART_GetITStatus(USART1, USART_IT_IDLE))
 {
 USART_ClearITPendingBit(USART1, USART_IT_IDLE);
 temp = USART1->SR;
 temp = USART1->DR;
 
 // 接收完成
 temp = DMA_GetCurrDataCounter(USART1_RX_DMA_CHANNEL);
 
 // 计算新增数据
 if (Transfer_UART_DMA.rx_buff_loopback == 0)
 Transfer_UART_DMA.rx_buff.count += BUFFER_SIZE - temp - Transfer_UART_DMA.rx_buff.tail;
 else
 {
 Transfer_UART_DMA.rx_buff.count += BUFFER_SIZE - Transfer_UART_DMA.rx_buff.tail + BUFFER_SIZE - temp;
 Transfer_UART_DMA.rx_buff_loopback = 0;
 }
 
 Transfer_UART_DMA.rx_buff.tail = BUFFER_SIZE - temp;
 
 // 数据溢出,覆盖
 if (Transfer_UART_DMA.rx_buff.count > BUFFER_SIZE)
 {
 // 更新头部位置
 Transfer_UART_DMA.rx_buff.count = BUFFER_SIZE;
 Transfer_UART_DMA.rx_buff.head = Transfer_UART_DMA.rx_buff.tail + 1 % BUFFER_SIZE;
 }
 }
 }
 
 void DMA1_Channel5_IRQHandler(void)
 {
 if (DMA_GetITStatus(DMA1_IT_TC5))
 {
 DMA_ClearITPendingBit(DMA1_IT_TC5);
 Transfer_UART_DMA.rx_buff_loopback = 1;
 }
 }
 
 
 
 
 ————————————————
 
 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
 
 原文链接:https://blog.csdn.net/hallo8888/article/details/146337620
 
 
 | 
 |