下面将程序分为头文件和源文件,并添加详细的文件注释和函数注释。
文件结构
stm32f103_uart_dma/
├── inc/
│ ├── uart_dma.h // 头文件
├── src/
│ ├── uart_dma.c // 源文件
│ ├── main.c // 主程序
1. 头文件 (uart_dma.h)
/**
******************************************************************************
* @file uart_dma.h
* @brief Header for UART DMA communication module
* @author Embedded Expert
* @version V1.0
* @date 2023-06-01
******************************************************************************
* @attention
*
* This module provides UART communication using DMA for STM32F103.
* Features:
* - DMA-based UART transmission and reception
* - Circular buffer for reception
* - Interrupt handling for transmission completion
*
******************************************************************************
*/
#ifndef __UART_DMA_H
#define __UART_DMA_H
#include "stm32f10x.h"
/* Exported defines ----------------------------------------------------------*/
#define TX_BUFFER_SIZE 32 // 发送缓冲区大小
#define RX_BUFFER_SIZE 256 // 接收缓冲区大小
/* Exported types ------------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
extern uint8_t TxBuffer[TX_BUFFER_SIZE]; // 发送缓冲区
extern uint8_t RxBuffer[RX_BUFFER_SIZE]; // 接收缓冲区
/* Exported functions --------------------------------------------------------*/
/**
* @brief 初始化UART DMA通信模块
* @param None
* @retval None
*/
void UART_DMA_Init(void);
/**
* @brief 通过DMA发送数据
* @param buffer: 要发送的数据缓冲区
* @param length: 要发送的数据长度
* @retval None
*/
void UART_DMA_Send(uint8_t *buffer, uint16_t length);
/**
* @brief 获取接收到的数据长度
* @param None
* @retval 已接收的数据长度
*/
uint16_t UART_DMA_GetReceivedLength(void);
/**
* @brief 处理接收到的数据
* @param length: 接收到的数据长度
* @retval None
*/
void UART_DMA_ProcessReceivedData(uint16_t length);
#endif /* __UART_DMA_H */
2. 源文件 (uart_dma.c)
/**
******************************************************************************
* @file uart_dma.c
* @brief Implementation of UART DMA communication module
* @author Embedded Expert
* @version V1.0
* @date 2023-06-01
******************************************************************************
* @attention
*
* This module implements UART communication using DMA for STM32F103.
* USART1 is used with DMA1 Channel4 (TX) and Channel5 (RX).
*
******************************************************************************
*/
#include "uart_dma.h"
#include <stdio.h>
/* Private defines -----------------------------------------------------------*/
#define USART1_DR_Base 0x40013804 // USART1数据寄存器基地址
/* Private variables ---------------------------------------------------------*/
uint8_t TxBuffer[TX_BUFFER_SIZE] = "STM32 DMA UART Test\r\n"; // 发送缓冲区
uint8_t RxBuffer[RX_BUFFER_SIZE]; // 接收缓冲区
/* Private function prototypes -----------------------------------------------*/
static void RCC_Configuration(void);
static void GPIO_Configuration(void);
static void USART_Configuration(void);
static void DMA_Configuration(void);
static void NVIC_Configuration(void);
/**
* @brief 初始化UART DMA通信模块
* @param None
* @retval None
*/
void UART_DMA_Init(void)
{
// 系统时钟配置
RCC_Configuration();
// GPIO配置
GPIO_Configuration();
// USART配置
USART_Configuration();
// DMA配置
DMA_Configuration();
// NVIC配置
NVIC_Configuration();
}
/**
* @brief 系统时钟配置
* @param None
* @retval None
*/
static void RCC_Configuration(void)
{
// 复位时钟配置
RCC_DeInit();
// 使能外部高速晶振
RCC_HSEConfig(RCC_HSE_ON);
// 等待HSE就绪
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
// 设置PLL时钟源及倍频系数
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
// 使能PLL
RCC_PLLCmd(ENABLE);
// 等待PLL就绪
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// 设置系统时钟为PLL输出
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 等待系统时钟设置完成
while(RCC_GetSYSCLKSource() != 0x08);
// 使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 使能USART1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
}
/**
* @brief GPIO配置
* @param None
* @retval None
*/
static void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 配置USART1 Tx (PA9)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1 Rx (PA10)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**
* @brief USART配置
* @param None
* @retval None
*/
static void USART_Configuration(void)
{
USART_InitTypeDef USART_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_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART
USART_Cmd(USART1, ENABLE);
}
/**
* @brief DMA配置
* @param None
* @retval None
*/
static void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
// DMA发送配置 (USART1_TX使用DMA1通道4)
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 内存到外设
DMA_InitStructure.DMA_BufferSize = TX_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_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
// DMA接收配置 (USART1_RX使用DMA1通道5)
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设到内存
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_Circular; // 循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
// 使能USART DMA发送和接收请求
USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
// 使能DMA接收通道
DMA_Cmd(DMA1_Channel5, ENABLE);
}
/**
* @brief NVIC配置
* @param None
* @retval None
*/
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 配置DMA发送完成中断
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能DMA发送完成中断
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
}
/**
* @brief 通过DMA发送数据
* @param buffer: 要发送的数据缓冲区
* @param length: 要发送的数据长度
* @retval None
*/
void UART_DMA_Send(uint8_t *buffer, uint16_t length)
{
// 等待上一次传输完成
while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET);
// 清除传输完成标志
DMA_ClearFlag(DMA1_FLAG_TC4);
// 配置DMA传输数据长度
DMA_SetCurrDataCounter(DMA1_Channel4, length);
// 重新设置内存地址
DMA1_Channel4->CMAR = (uint32_t)buffer;
// 使能DMA通道
DMA_Cmd(DMA1_Channel4, ENABLE);
}
/**
* @brief 获取接收到的数据长度
* @param None
* @retval 已接收的数据长度
*/
uint16_t UART_DMA_GetReceivedLength(void)
{
// 计算已接收数据长度 (循环缓冲区)
return RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);
}
/**
* @brief 处理接收到的数据
* @param length: 接收到的数据长度
* @retval None
*/
void UART_DMA_ProcessReceivedData(uint16_t length)
{
// 示例: 回显接收到的数据
UART_DMA_Send(RxBuffer, length);
// 这里可以添加更复杂的数据处理逻辑
// 例如协议解析、数据存储等
}
/**
* @brief DMA1通道4中断服务函数 (发送完成中断)
* @param None
* @retval None
*/
void DMA1_Channel4_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC4) != RESET)
{
// 清除中断标志
DMA_ClearITPendingBit(DMA1_IT_TC4);
// 可以在这里添加发送完成后的处理代码
// 例如: 点亮LED表示发送完成
}
}
3. 主程序 (main.c)
/**
******************************************************************************
* @file main.c
* @brief Main program body
* @author Embedded Expert
* @version V1.0
* @date 2023-06-01
******************************************************************************
* @attention
*
* Main application for UART DMA communication demo.
*
******************************************************************************
*/
#include "uart_dma.h"
#include "stm32f10x.h"
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
// 初始化UART DMA模块
UART_DMA_Init();
// 发送初始测试数据
UART_DMA_Send(TxBuffer, sizeof(TxBuffer));
while(1)
{
// 获取接收数据长度
uint16_t receivedLength = UART_DMA_GetReceivedLength();
// 如果有数据接收
if(receivedLength > 0)
{
// 处理接收到的数据
UART_DMA_ProcessReceivedData(receivedLength);
}
// 可以添加其他应用代码
}
}
/************************ (C) COPYRIGHT Embedded Expert *****END OF FILE****/
程序说明
-
模块化设计:
- 将UART DMA功能封装为独立模块
- 清晰的接口设计,便于其他模块调用
- 内部实现细节对外隐藏
-
完整注释:
- 每个文件头部有详细说明
- 每个函数有功能描述和参数说明
- 关键代码行有注释说明
-
功能特点:
- 使用DMA实现高效UART通信
- 发送使用普通模式,接收使用循环缓冲区
- 提供发送完成中断通知
- 示例实现了简单的数据回显功能
-
使用方法:
- 调用
UART_DMA_Init()
初始化模块 - 使用
UART_DMA_Send()
发送数据 - 在主循环中检查接收数据长度并处理
- 调用
这个分文件实现的版本更加规范,适合实际项目开发,便于维护和扩展。