基于STM32 HAL库的串口DMA接收不定长数据的解决方案,包含空闲中断检测、环形缓冲区管理和数据处理。
一、系统架构设计
1.1 头文件定义
c
// uart_dma.h
#ifndef __UART_DMA_H
#define __UART_DMA_H
#include "main.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdbool.h>
// 定义使用的串口和DMA
#define UART_DMA_HUART huart1 // 串口句柄
#define UART_DMA_HDMA hdma_usart1_rx // DMA句柄
#define UART_DMA_RX_BUFFER_SIZE 1024 // 接收缓冲区大小
#define UART_DMA_TX_BUFFER_SIZE 256 // 发送缓冲区大小
#define UART_DMA_MAX_PACKET_SIZE 512 // 最大数据包长度
#define UART_DMA_TIMEOUT_MS 1000 // 超时时间(毫秒)
// 串口状态枚举
typedef enum {
UART_DMA_STATE_READY = 0,
UART_DMA_STATE_BUSY,
UART_DMA_STATE_ERROR,
UART_DMA_STATE_TIMEOUT
} UART_DMA_StateTypeDef;
// 串口数据包结构
typedef struct {
uint8_t data[UART_DMA_MAX_PACKET_SIZE];
uint16_t length;
uint32_t timestamp;
} UART_DMA_PacketTypeDef;
// 串口DMA管理结构
typedef struct {
UART_HandleTypeDef *huart;
DMA_HandleTypeDef *hdma;
// 接收缓冲区(双缓冲机制)
uint8_t rx_buffer[2][UART_DMA_RX_BUFFER_SIZE];
volatile uint8_t active_buffer; // 当前活动的接收缓冲区索引(0或1)
// 环形缓冲区用于存储接收到的完整数据包
UART_DMA_PacketTypeDef rx_packets[16]; // 最多存储16个数据包
volatile uint16_t write_index; // 写索引
volatile uint16_t read_index; // 读索引
volatile uint16_t packet_count; // 当前数据包数量
// DMA接收相关变量
volatile uint16_t dma_write_pos; // DMA当前写入位置
volatile uint16_t dma_last_pos; // DMA上一次位置
volatile bool dma_receiving; // DMA接收状态
// 空闲中断相关
volatile bool idle_flag; // 空闲中断标志
volatile uint32_t last_rx_time; // 最后接收时间
// 状态和错误
UART_DMA_StateTypeDef state;
uint32_t error_flags;
// 回调函数
void (*on_receive_complete)(UART_DMA_PacketTypeDef *packet);
void (*on_error)(uint32_t error_code);
// 统计信息
uint32_t rx_byte_count;
uint32_t rx_packet_count;
uint32_t error_count;
} UART_DMA_HandleTypeDef;
// 函数声明
void UART_DMA_Init(UART_DMA_HandleTypeDef *huart_dma, UART_HandleTypeDef *huart, DMA_HandleTypeDef *hdma);
void UART_DMA_StartReceive(UART_DMA_HandleTypeDef *huart_dma);
void UART_DMA_StopReceive(UART_DMA_HandleTypeDef *huart_dma);
void UART_DMA_IdleCallback(UART_DMA_HandleTypeDef *huart_dma);
void UART_DMA_ProcessData(UART_DMA_HandleTypeDef *huart_dma);
bool UART_DMA_GetPacket(UART_DMA_HandleTypeDef *huart_dma, UART_DMA_PacketTypeDef *packet);
uint16_t UART_DMA_SendData(UART_DMA_HandleTypeDef *huart_dma, uint8_t *data, uint16_t length);
uint16_t UART_DMA_SendString(UART_DMA_HandleTypeDef *huart_dma, char *str);
void UART_DMA_Reset(UART_DMA_HandleTypeDef *huart_dma);
uint32_t UART_DMA_GetRxByteCount(UART_DMA_HandleTypeDef *huart_dma);
uint32_t UART_DMA_GetRxPacketCount(UART_DMA_HandleTypeDef *huart_dma);
uint32_t UART_DMA_GetErrorCount(UART_DMA_HandleTypeDef *huart_dma);
// 中断服务函数原型
void UART_DMA_RX_IRQHandler(UART_DMA_HandleTypeDef *huart_dma);
void UART_DMA_DMA_IRQHandler(UART_DMA_HandleTypeDef *huart_dma);
#endif /* __UART_DMA_H */
二、核心实现文件
2.1 串口DMA初始化和管理
c
// uart_dma.c
#include "uart_dma.h"
// 私有函数声明
static void UART_DMA_ResetRxBuffer(UART_DMA_HandleTypeDef *huart_dma);
static void UART_DMA_SwitchBuffer(UART_DMA_HandleTypeDef *huart_dma);
static uint16_t UART_DMA_GetDMARemainingData(UART_DMA_HandleTypeDef *huart_dma);
static uint16_t UART_DMA_GetDMAWritePosition(UART_DMA_HandleTypeDef *huart_dma);
static void UART_DMA_StorePacket(UART_DMA_HandleTypeDef *huart_dma, uint8_t *data, uint16_t length);
// 初始化串口DMA接收
void UART_DMA_Init(UART_DMA_HandleTypeDef *huart_dma, UART_HandleTypeDef *huart, DMA_HandleTypeDef *hdma)
{
// 参数检查
if (huart_dma == NULL || huart == NULL || hdma == NULL) {
return;
}
// 初始化结构体
memset(huart_dma, 0, sizeof(UART_DMA_HandleTypeDef));
// 保存句柄
huart_dma->huart = huart;
huart_dma->hdma = hdma;
// 初始化状态
huart_dma->state = UART_DMA_STATE_READY;
huart_dma->active_buffer = 0;
huart_dma->dma_receiving = false;
huart_dma->idle_flag = false;
// 初始化环形缓冲区
huart_dma->write_index = 0;
huart_dma->read_index = 0;
huart_dma->packet_count = 0;
// 清空缓冲区
memset(huart_dma->rx_buffer, 0, sizeof(huart_dma->rx_buffer));
for (int i = 0; i < 16; i++) {
huart_dma->rx_packets[i].length = 0;
}
// 初始化统计
huart_dma->rx_byte_count = 0;
huart_dma->rx_packet_count = 0;
huart_dma->error_count = 0;
// 配置串口
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE); // 禁用RXNE中断
__HAL_UART_DISABLE_IT(huart, UART_IT_PE); // 禁用奇偶错误中断
__HAL_UART_DISABLE_IT(huart, UART_IT_ERR); // 禁用错误中断
// 使能空闲中断
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
// 配置DMA为循环模式
hdma->Init.Mode = DMA_CIRCULAR;
hdma->Init.PeriphInc = DMA_PINC_DISABLE;
hdma->Init.MemInc = DMA_MINC_ENABLE;
hdma->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma->Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(hdma);
// 关联DMA到串口
__HAL_LINKDMA(huart, hdmarx, *hdma);
// 启动DMA接收
UART_DMA_StartReceive(huart_dma);
}
// 启动DMA接收
void UART_DMA_StartReceive(UART_DMA_HandleTypeDef *huart_dma)
{
if (huart_dma->state != UART_DMA_STATE_READY) {
return;
}
// 重置接收缓冲区
UART_DMA_ResetRxBuffer(huart_dma);
// 启动DMA接收
HAL_UART_Receive_DMA(huart_dma->huart,
huart_dma->rx_buffer[huart_dma->active_buffer],
UART_DMA_RX_BUFFER_SIZE);
// 更新状态
huart_dma->dma_receiving = true;
huart_dma->dma_last_pos = 0;
huart_dma->state = UART_DMA_STATE_BUSY;
huart_dma->last_rx_time = HAL_GetTick();
}
// 停止DMA接收
void UART_DMA_StopReceive(UART_DMA_HandleTypeDef *huart_dma)
{
if (!huart_dma->dma_receiving) {
return;
}
// 停止DMA传输
HAL_UART_DMAStop(huart_dma->huart);
// 更新状态
huart_dma->dma_receiving = false;
huart_dma->state = UART_DMA_STATE_READY;
}
// 重置接收缓冲区
static void UART_DMA_ResetRxBuffer(UART_DMA_HandleTypeDef *huart_dma)
{
memset(huart_dma->rx_buffer[0], 0, UART_DMA_RX_BUFFER_SIZE);
memset(huart_dma->rx_buffer[1], 0, UART_DMA_RX_BUFFER_SIZE);
huart_dma->active_buffer = 0;
huart_dma->dma_write_pos = 0;
huart_dma->dma_last_pos = 0;
}
// 获取DMA剩余数据量
static uint16_t UART_DMA_GetDMARemainingData(UART_DMA_HandleTypeDef *huart_dma)
{
if (huart_dma->hdma == NULL) {
return 0;
}
return __HAL_DMA_GET_COUNTER(huart_dma->hdma);
}
// 获取DMA当前写入位置
static uint16_t UART_DMA_GetDMAWritePosition(UART_DMA_HandleTypeDef *huart_dma)
{
uint16_t remaining = UART_DMA_GetDMARemainingData(huart_dma);
uint16_t write_pos = UART_DMA_RX_BUFFER_SIZE - remaining;
return write_pos;
}
// 空闲中断回调函数
void UART_DMA_IdleCallback(UART_DMA_HandleTypeDef *huart_dma)
{
if (!huart_dma->dma_receiving) {
return;
}
// 清除空闲中断标志
__HAL_UART_CLEAR_IDLEFLAG(huart_dma->huart);
// 获取DMA当前写入位置
huart_dma->dma_write_pos = UART_DMA_GetDMAWritePosition(huart_dma);
// 计算接收到的数据长度
uint16_t received_len = 0;
if (huart_dma->dma_write_pos >= huart_dma->dma_last_pos) {
// 正常情况,没有发生缓冲区回绕
received_len = huart_dma->dma_write_pos - huart_dma->dma_last_pos;
} else {
// 发生缓冲区回绕
received_len = (UART_DMA_RX_BUFFER_SIZE - huart_dma->dma_last_pos) + huart_dma->dma_write_pos;
// 切换到另一个缓冲区
UART_DMA_SwitchBuffer(huart_dma);
}
if (received_len > 0) {
// 处理接收到的数据
uint8_t *data_ptr = &huart_dma->rx_buffer[huart_dma->active_buffer][huart_dma->dma_last_pos];
// 更新最后接收时间
huart_dma->last_rx_time = HAL_GetTick();
// 统计接收字节数
huart_dma->rx_byte_count += received_len;
// 存储数据包
UART_DMA_StorePacket(huart_dma, data_ptr, received_len);
// 调用接收完成回调
if (huart_dma->on_receive_complete != NULL) {
UART_DMA_PacketTypeDef temp_packet;
temp_packet.length = received_len;
temp_packet.timestamp = huart_dma->last_rx_time;
memcpy(temp_packet.data, data_ptr, received_len);
huart_dma->on_receive_complete(&temp_packet);
}
}
// 更新DMA最后位置
huart_dma->dma_last_pos = huart_dma->dma_write_pos;
// 如果发生了缓冲区回绕,重置最后位置
if (huart_dma->dma_last_pos >= UART_DMA_RX_BUFFER_SIZE) {
huart_dma->dma_last_pos = 0;
}
}
// 切换到另一个缓冲区
static void UART_DMA_SwitchBuffer(UART_DMA_HandleTypeDef *huart_dma)
{
// 切换活动缓冲区索引
huart_dma->active_buffer = (huart_dma->active_buffer + 1) % 2;
// 重新配置DMA目标地址
HAL_UART_DMAStop(huart_dma->huart);
// 重新启动DMA,使用新的缓冲区
HAL_UART_Receive_DMA(huart_dma->huart,
huart_dma->rx_buffer[huart_dma->active_buffer],
UART_DMA_RX_BUFFER_SIZE);
// 重置写入位置
huart_dma->dma_write_pos = 0;
huart_dma->dma_last_pos = 0;
}
// 存储数据包到环形缓冲区
static void UART_DMA_StorePacket(UART_DMA_HandleTypeDef *huart_dma, uint8_t *data, uint16_t length)
{
if (length == 0 || length > UART_DMA_MAX_PACKET_SIZE) {
return;
}
// 检查环形缓冲区是否已满
if (huart_dma->packet_count >= 16) {
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x01); // 缓冲区溢出错误
}
return;
}
// 获取当前写入位置
uint16_t write_idx = huart_dma->write_index;
// 存储数据包
memcpy(huart_dma->rx_packets[write_idx].data, data, length);
huart_dma->rx_packets[write_idx].length = length;
huart_dma->rx_packets[write_idx].timestamp = HAL_GetTick();
// 更新写索引和数据包计数
huart_dma->write_index = (write_idx + 1) % 16;
huart_dma->packet_count++;
huart_dma->rx_packet_count++;
}
// 处理接收到的数据
void UART_DMA_ProcessData(UART_DMA_HandleTypeDef *huart_dma)
{
// 检查是否有数据包需要处理
if (huart_dma->packet_count == 0) {
return;
}
// 获取当前读索引
uint16_t read_idx = huart_dma->read_index;
// 处理数据包
UART_DMA_PacketTypeDef *packet = &huart_dma->rx_packets[read_idx];
// 这里可以添加自定义的数据处理逻辑
// 例如:解析协议、校验数据等
// 示例:回显接收到的数据
UART_DMA_SendData(huart_dma, packet->data, packet->length);
// 更新读索引和数据包计数
huart_dma->read_index = (read_idx + 1) % 16;
huart_dma->packet_count--;
}
// 获取数据包
bool UART_DMA_GetPacket(UART_DMA_HandleTypeDef *huart_dma, UART_DMA_PacketTypeDef *packet)
{
if (huart_dma->packet_count == 0 || packet == NULL) {
return false;
}
// 获取当前读索引
uint16_t read_idx = huart_dma->read_index;
// 复制数据包
memcpy(packet->data, huart_dma->rx_packets[read_idx].data,
huart_dma->rx_packets[read_idx].length);
packet->length = huart_dma->rx_packets[read_idx].length;
packet->timestamp = huart_dma->rx_packets[read_idx].timestamp;
// 更新读索引和数据包计数
huart_dma->read_index = (read_idx + 1) % 16;
huart_dma->packet_count--;
return true;
}
// 发送数据
uint16_t UART_DMA_SendData(UART_DMA_HandleTypeDef *huart_dma, uint8_t *data, uint16_t length)
{
if (huart_dma->state != UART_DMA_STATE_READY || data == NULL || length == 0) {
return 0;
}
HAL_StatusTypeDef status = HAL_UART_Transmit_DMA(huart_dma->huart, data, length);
if (status != HAL_OK) {
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x02); // 发送错误
}
return 0;
}
return length;
}
// 发送字符串
uint16_t UART_DMA_SendString(UART_DMA_HandleTypeDef *huart_dma, char *str)
{
if (str == NULL) {
return 0;
}
return UART_DMA_SendData(huart_dma, (uint8_t*)str, strlen(str));
}
// 重置串口DMA
void UART_DMA_Reset(UART_DMA_HandleTypeDef *huart_dma)
{
// 停止接收
UART_DMA_StopReceive(huart_dma);
// 重置结构体
UART_DMA_Init(huart_dma, huart_dma->huart, huart_dma->hdma);
}
// 获取统计信息
uint32_t UART_DMA_GetRxByteCount(UART_DMA_HandleTypeDef *huart_dma)
{
return huart_dma->rx_byte_count;
}
uint32_t UART_DMA_GetRxPacketCount(UART_DMA_HandleTypeDef *huart_dma)
{
return huart_dma->rx_packet_count;
}
uint32_t UART_DMA_GetErrorCount(UART_DMA_HandleTypeDef *huart_dma)
{
return huart_dma->error_count;
}
三、中断处理模块
3.1 中断服务函数
c
// uart_dma_it.c
#include "uart_dma.h"
// 外部变量声明
extern UART_DMA_HandleTypeDef huart1_dma;
// 串口接收中断服务函数
void UART_DMA_RX_IRQHandler(UART_DMA_HandleTypeDef *huart_dma)
{
if (huart_dma == NULL) {
return;
}
// 检查空闲中断
if (__HAL_UART_GET_FLAG(huart_dma->huart, UART_FLAG_IDLE) != RESET) {
// 处理空闲中断
UART_DMA_IdleCallback(huart_dma);
// 清除空闲中断标志
__HAL_UART_CLEAR_IDLEFLAG(huart_dma->huart);
}
// 检查其他错误中断
if (__HAL_UART_GET_FLAG(huart_dma->huart, UART_FLAG_PE) != RESET) {
// 奇偶校验错误
__HAL_UART_CLEAR_PEFLAG(huart_dma->huart);
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x04);
}
}
if (__HAL_UART_GET_FLAG(huart_dma->huart, UART_FLAG_FE) != RESET) {
// 帧错误
__HAL_UART_CLEAR_FEFLAG(huart_dma->huart);
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x08);
}
}
if (__HAL_UART_GET_FLAG(huart_dma->huart, UART_FLAG_NE) != RESET) {
// 噪声错误
__HAL_UART_CLEAR_NEFLAG(huart_dma->huart);
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x10);
}
}
if (__HAL_UART_GET_FLAG(huart_dma->huart, UART_FLAG_ORE) != RESET) {
// 溢出错误
__HAL_UART_CLEAR_OREFLAG(huart_dma->huart);
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x20);
}
}
}
// DMA中断服务函数
void UART_DMA_DMA_IRQHandler(UART_DMA_HandleTypeDef *huart_dma)
{
if (huart_dma == NULL || huart_dma->hdma == NULL) {
return;
}
// 检查DMA传输完成中断
if (__HAL_DMA_GET_FLAG(huart_dma->hdma, __HAL_DMA_GET_TC_FLAG_INDEX(huart_dma->hdma))) {
// 传输完成
__HAL_DMA_CLEAR_FLAG(huart_dma->hdma, __HAL_DMA_GET_TC_FLAG_INDEX(huart_dma->hdma));
// 处理DMA传输完成
// 这里可以添加DMA传输完成的处理逻辑
}
// 检查DMA半传输完成中断
if (__HAL_DMA_GET_FLAG(huart_dma->hdma, __HAL_DMA_GET_HT_FLAG_INDEX(huart_dma->hdma))) {
// 半传输完成
__HAL_DMA_CLEAR_FLAG(huart_dma->hdma, __HAL_DMA_GET_HT_FLAG_INDEX(huart_dma->hdma));
// 处理DMA半传输完成
// 可以在这里处理前半部分数据
}
// 检查DMA传输错误中断
if (__HAL_DMA_GET_FLAG(huart_dma->hdma, __HAL_DMA_GET_TE_FLAG_INDEX(huart_dma->hdma))) {
// 传输错误
__HAL_DMA_CLEAR_FLAG(huart_dma->hdma, __HAL_DMA_GET_TE_FLAG_INDEX(huart_dma->hdma));
huart_dma->error_count++;
if (huart_dma->on_error != NULL) {
huart_dma->on_error(0x40); // DMA传输错误
}
}
}
// 超时检测函数(需要在主循环中定期调用)
void UART_DMA_TimeoutCheck(UART_DMA_HandleTypeDef *huart_dma)
{
if (huart_dma == NULL || !huart_dma->dma_receiving) {
return;
}
uint32_t current_time = HAL_GetTick();
// 检查是否超时(超过1秒没有收到数据)
if ((current_time - huart_dma->last_rx_time) > UART_DMA_TIMEOUT_MS) {
// 超时处理
if (huart_dma->dma_write_pos != huart_dma->dma_last_pos) {
// 有未处理的数据,强制处理
UART_DMA_IdleCallback(huart_dma);
}
// 重置最后接收时间
huart_dma->last_rx_time = current_time;
}
}
四、CubeMX配置和主程序
4.1 CubeMX配置步骤
c
// 在CubeMX中配置以下内容:
// 1. 串口配置 (USART1为例)
// - Mode: Asynchronous
// - Baud Rate: 115200
// - Word Length: 8 Bits
// - Parity: None
// - Stop Bits: 1
// - Oversampling: 16
// - Advanced Features: 启用DMA接收
// 2. DMA配置
// - Stream: DMA2 Stream2 (USART1_RX)
// - Direction: Peripheral To Memory
// - Priority: High
// - Mode: Circular
// - Increment Address: Memory
// - Data Width: Byte
// 3. NVIC配置
// - 启用USART1全局中断
// - 启用DMA2 Stream2全局中断
// - 设置合适的优先级
4.2 主程序
c
// main.c
#include "main.h"
#include "uart_dma.h"
// 全局变量
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
UART_DMA_HandleTypeDef huart1_dma;
// 自定义回调函数
void OnReceiveComplete(UART_DMA_PacketTypeDef *packet)
{
// 接收到数据的处理函数
// 可以在这里处理协议解析、数据存储等
// 示例:通过串口回显接收到的数据
UART_DMA_SendData(&huart1_dma, packet->data, packet->length);
// 可以添加其他处理逻辑
// 比如:数据校验、命令解析等
}
void OnError(uint32_t error_code)
{
// 错误处理函数
switch (error_code) {
case 0x01:
// 缓冲区溢出
break;
case 0x02:
// 发送错误
break;
case 0x04:
// 奇偶校验错误
break;
case 0x08:
// 帧错误
break;
case 0x10:
// 噪声错误
break;
case 0x20:
// 溢出错误
break;
case 0x40:
// DMA传输错误
break;
default:
break;
}
}
// 系统时钟配置
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSE、PLL等
// ... CubeMX生成的代码
}
// 外设初始化
static void MX_GPIO_Init(void)
{
// GPIO初始化
// ... CubeMX生成的代码
}
static 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;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
static void MX_DMA_Init(void)
{
// DMA控制器时钟使能
__HAL_RCC_DMA2_CLK_ENABLE();
// 配置DMA2 Stream2
hdma_usart1_rx.Instance = DMA2_Stream2;
hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;
hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_usart1_rx);
// 关联DMA到串口
__HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);
}
// 初始化所有外设
void MX_Init(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
}
int main(void)
{
// 外设初始化
MX_Init();
// 初始化串口DMA接收
UART_DMA_Init(&huart1_dma, &huart1, &hdma_usart1_rx);
// 设置回调函数
huart1_dma.on_receive_complete = OnReceiveComplete;
huart1_dma.on_error = OnError;
// 启动接收
UART_DMA_StartReceive(&huart1_dma);
// 发送欢迎信息
UART_DMA_SendString(&huart1_dma, "UART DMA Receiver Ready!\r\n");
while (1) {
// 主循环
// 1. 超时检测
UART_DMA_TimeoutCheck(&huart1_dma);
// 2. 处理接收到的数据
UART_DMA_ProcessData(&huart1_dma);
// 3. 其他任务
// ...
// 4. 延时
HAL_Delay(1);
}
}
// 串口1中断服务函数
void USART1_IRQHandler(void)
{
UART_DMA_RX_IRQHandler(&huart1_dma);
}
// DMA2 Stream2中断服务函数
void DMA2_Stream2_IRQHandler(void)
{
UART_DMA_DMA_IRQHandler(&huart1_dma);
}
五、高级功能和协议解析
5.1 协议解析模块
c
// protocol_parser.c
#include "protocol_parser.h"
#include "uart_dma.h"
// 私有变量
static ProtocolParser_HandleTypeDef hparser;
// 初始化协议解析器
void ProtocolParser_Init(void)
{
memset(&hparser, 0, sizeof(ProtocolParser_HandleTypeDef));
hparser.state = PROTOCOL_STATE_IDLE;
hparser.checksum = 0;
}
// 解析接收到的数据
ProtocolParseResult ProtocolParser_Parse(uint8_t *data, uint16_t length)
{
ProtocolParseResult result = {0};
for (uint16_t i = 0; i < length; i++) {
uint8_t byte = data[i];
switch (hparser.state) {
case PROTOCOL_STATE_IDLE:
if (byte == PROTOCOL_HEADER) {
hparser.state = PROTOCOL_STATE_HEADER;
hparser.buffer[hparser.index++] = byte;
hparser.checksum = 0;
}
break;
case PROTOCOL_STATE_HEADER:
if (byte == PROTOCOL_SECOND_HEADER) {
hparser.buffer[hparser.index++] = byte;
hparser.state = PROTOCOL_STATE_LENGTH;
} else {
ProtocolParser_Reset();
}
break;
case PROTOCOL_STATE_LENGTH:
hparser.expected_length = byte;
hparser.buffer[hparser.index++] = byte;
if (hparser.expected_length > PROTOCOL_MAX_LENGTH) {
ProtocolParser_Reset();
result.status = PROTOCOL_STATUS_ERROR_LENGTH;
} else {
hparser.state = PROTOCOL_STATE_DATA;
}
break;
case PROTOCOL_STATE_DATA:
hparser.buffer[hparser.index++] = byte;
hparser.checksum ^= byte; // 简单的异或校验
if (hparser.index >= (hparser.expected_length + PROTOCOL_HEADER_SIZE)) {
hparser.state = PROTOCOL_STATE_CHECKSUM;
}
break;
case PROTOCOL_STATE_CHECKSUM:
if (byte == hparser.checksum) {
// 校验成功
hparser.buffer[hparser.index++] = byte;
// 完整的协议帧
result.status = PROTOCOL_STATUS_OK;
result.length = hparser.index;
memcpy(result.data, hparser.buffer, hparser.index);
// 触发回调
if (hparser.on_frame_received != NULL) {
hparser.on_frame_received(result.data, result.length);
}
} else {
result.status = PROTOCOL_STATUS_ERROR_CHECKSUM;
}
ProtocolParser_Reset();
break;
default:
ProtocolParser_Reset();
break;
}
// 防止缓冲区溢出
if (hparser.index >= PROTOCOL_BUFFER_SIZE) {
ProtocolParser_Reset();
result.status = PROTOCOL_STATUS_ERROR_OVERFLOW;
}
}
return result;
}
// 构建协议帧
uint16_t ProtocolParser_BuildFrame(uint8_t *data, uint8_t length, uint8_t *frame)
{
if (length > PROTOCOL_MAX_DATA_LENGTH) {
return 0;
}
uint8_t index = 0;
uint8_t checksum = 0;
// 帧头
frame[index++] = PROTOCOL_HEADER;
frame[index++] = PROTOCOL_SECOND_HEADER;
// 数据长度
frame[index++] = length;
// 数据
for (uint8_t i = 0; i < length; i++) {
frame[index++] = data[i];
checksum ^= data[i];
}
// 校验和
frame[index++] = checksum;
return index;
}
// 重置解析器
void ProtocolParser_Reset(void)
{
hparser.state = PROTOCOL_STATE_IDLE;
hparser.index = 0;
hparser.checksum = 0;
memset(hparser.buffer, 0, PROTOCOL_BUFFER_SIZE);
}
// 设置接收回调
void ProtocolParser_SetCallback(void (*callback)(uint8_t *, uint16_t))
{
hparser.on_frame_received = callback;
}
5.2 协议解析头文件
c
// protocol_parser.h
#ifndef __PROTOCOL_PARSER_H
#define __PROTOCOL_PARSER_H
#include <stdint.h>
#include <string.h>
// 协议定义
#define PROTOCOL_HEADER 0xAA
#define PROTOCOL_SECOND_HEADER 0x55
#define PROTOCOL_BUFFER_SIZE 256
#define PROTOCOL_MAX_LENGTH 128
#define PROTOCOL_MAX_DATA_LENGTH 64
#define PROTOCOL_HEADER_SIZE 3 // 帧头1 + 帧头2 + 长度
// 协议状态
typedef enum {
PROTOCOL_STATE_IDLE = 0,
PROTOCOL_STATE_HEADER,
PROTOCOL_STATE_LENGTH,
PROTOCOL_STATE_DATA,
PROTOCOL_STATE_CHECKSUM
} ProtocolState;
// 解析结果状态
typedef enum {
PROTOCOL_STATUS_OK = 0,
PROTOCOL_STATUS_ERROR_HEADER,
PROTOCOL_STATUS_ERROR_LENGTH,
PROTOCOL_STATUS_ERROR_CHECKSUM,
PROTOCOL_STATUS_ERROR_OVERFLOW,
PROTOCOL_STATUS_ERROR_TIMEOUT
} ProtocolStatus;
// 解析结果
typedef struct {
uint8_t data[PROTOCOL_BUFFER_SIZE];
uint16_t length;
ProtocolStatus status;
} ProtocolParseResult;
// 协议解析器句柄
typedef struct {
ProtocolState state;
uint8_t buffer[PROTOCOL_BUFFER_SIZE];
uint16_t index;
uint8_t checksum;
uint8_t expected_length;
void (*on_frame_received)(uint8_t *data, uint16_t length);
} ProtocolParser_HandleTypeDef;
// 函数声明
void ProtocolParser_Init(void);
ProtocolParseResult ProtocolParser_Parse(uint8_t *data, uint16_t length);
uint16_t ProtocolParser_BuildFrame(uint8_t *data, uint8_t length, uint8_t *frame);
void ProtocolParser_Reset(void);
void ProtocolParser_SetCallback(void (*callback)(uint8_t *, uint16_t));
#endif /* __PROTOCOL_PARSER_H */
六、示例应用
6.1 数据转发应用
c
// app_forward.c
#include "app_forward.h"
#include "uart_dma.h"
#include "protocol_parser.h"
// 私有变量
static ForwardApp_HandleTypeDef hforward;
// 初始化转发应用
void ForwardApp_Init(UART_DMA_HandleTypeDef *huart1, UART_DMA_HandleTypeDef *huart2)
{
memset(&hforward, 0, sizeof(ForwardApp_HandleTypeDef));
hforward.huart1 = huart1;
hforward.huart2 = huart2;
hforward.enabled = true;
// 初始化协议解析器
ProtocolParser_Init();
ProtocolParser_SetCallback(ForwardApp_OnFrameReceived);
}
// 处理接收到的数据
void ForwardApp_Process(void)
{
if (!hforward.enabled) {
return;
}
// 从UART1获取数据包
UART_DMA_PacketTypeDef packet;
if (UART_DMA_GetPacket(hforward.huart1, &packet)) {
// 解析协议
ProtocolParseResult result = ProtocolParser_Parse(packet.data, packet.length);
if (result.status == PROTOCOL_STATUS_OK) {
// 协议解析成功,转发到UART2
UART_DMA_SendData(hforward.huart2, result.data, result.length);
// 统计
hforward.forward_count++;
} else {
// 协议解析失败,直接转发原始数据
UART_DMA_SendData(hforward.huart2, packet.data, packet.length);
// 统计错误
hforward.error_count++;
}
}
}
// 帧接收回调
void ForwardApp_OnFrameReceived(uint8_t *data, uint16_t length)
{
// 可以在这里添加特殊的帧处理逻辑
// 例如:心跳包响应、命令执行等
// 检查是否是心跳包
if (length == 4 && data[0] == 0xAA && data[1] == 0x55 && data[2] == 0x01 && data[3] == 0x01) {
// 心跳包,发送响应
uint8_t response[] = {0xAA, 0x55, 0x01, 0x02, 0x00}; // 校验和需要计算
response[4] = response[2] ^ response[3]; // 计算校验和
UART_DMA_SendData(hforward.huart1, response, sizeof(response));
}
}
// 获取统计信息
ForwardApp_Stats ForwardApp_GetStats(void)
{
ForwardApp_Stats stats;
stats.forward_count = hforward.forward_count;
stats.error_count = hforward.error_count;
stats.rx_bytes = UART_DMA_GetRxByteCount(hforward.huart1);
stats.tx_bytes = hforward.tx_bytes; // 需要自己统计发送字节数
return stats;
}
// 启用/禁用转发
void ForwardApp_SetEnabled(bool enabled)
{
hforward.enabled = enabled;
}
七、错误处理和调试
7.1 调试信息输出
c
// debug_utils.c
#include "debug_utils.h"
#include "uart_dma.h"
// 发送调试信息
void Debug_SendInfo(const char *format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
int len = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (len > 0) {
UART_DMA_SendData(&huart1_dma, (uint8_t*)buffer, len);
}
}
// 十六进制转储
void Debug_HexDump(const char *label, uint8_t *data, uint16_t length)
{
char buffer[64];
char hex[16];
Debug_SendInfo("\r\n=== %s (%d bytes) ===\r\n", label, length);
for (uint16_t i = 0; i < length; i += 16) {
// 地址
snprintf(buffer, sizeof(buffer), "%04X: ", i);
// 十六进制
for (uint8_t j = 0; j < 16; j++) {
if (i + j < length) {
snprintf(hex, sizeof(hex), "%02X ", data[i + j]);
strcat(buffer, hex);
} else {
strcat(buffer, " ");
}
if (j == 7) {
strcat(buffer, " ");
}
}
strcat(buffer, " ");
// ASCII
for (uint8_t j = 0; j < 16; j++) {
if (i + j < length) {
uint8_t c = data[i + j];
if (c >= 32 && c <= 126) {
snprintf(hex, sizeof(hex), "%c", c);
} else {
snprintf(hex, sizeof(hex), ".");
}
strcat(buffer, hex);
} else {
strcat(buffer, " ");
}
}
Debug_SendInfo("%s\r\n", buffer);
}
Debug_SendInfo("===================\r\n");
}
// 发送状态信息
void Debug_SendStatus(UART_DMA_HandleTypeDef *huart_dma)
{
Debug_SendInfo("\r\n=== UART DMA Status ===\r\n");
Debug_SendInfo("State: %s\r\n",
huart_dma->state == UART_DMA_STATE_READY ? "Ready" :
huart_dma->state == UART_DMA_STATE_BUSY ? "Busy" :
huart_dma->state == UART_DMA_STATE_ERROR ? "Error" : "Unknown");
Debug_SendInfo("Active Buffer: %d\r\n", huart_dma->active_buffer);
Debug_SendInfo("DMA Receiving: %s\r\n", huart_dma->dma_receiving ? "Yes" : "No");
Debug_SendInfo("Packet Count: %d\r\n", huart_dma->packet_count);
Debug_SendInfo("RX Bytes: %lu\r\n", huart_dma->rx_byte_count);
Debug_SendInfo("RX Packets: %lu\r\n", huart_dma->rx_packet_count);
Debug_SendInfo("Error Count: %lu\r\n", huart_dma->error_count);
Debug_SendInfo("=======================\r\n");
}
参考代码 串口通信+DMA接收不定长数据 www.youwenfan.com/contentcsv/71862.html
八、使用示例
8.1 完整应用示例
c
// main_example.c
#include "main.h"
#include "uart_dma.h"
#include "protocol_parser.h"
#include "debug_utils.h"
// 全局句柄
UART_DMA_HandleTypeDef huart1_dma;
UART_DMA_HandleTypeDef huart2_dma;
// 自定义接收回调
void OnDataReceived(UART_DMA_PacketTypeDef *packet)
{
// 发送接收到的数据到调试串口
Debug_HexDump("Received Data", packet->data, packet->length);
// 解析协议
ProtocolParseResult result = ProtocolParser_Parse(packet->data, packet->length);
if (result.status == PROTOCOL_STATUS_OK) {
Debug_SendInfo("Valid protocol frame received (%d bytes)\r\n", result.length);
// 处理协议帧
ProcessProtocolFrame(result.data, result.length);
} else {
Debug_SendInfo("Invalid frame (status: %d)\r\n", result.status);
}
}
// 处理协议帧
void ProcessProtocolFrame(uint8_t *data, uint16_t length)
{
if (length < 4) {
return;
}
uint8_t command = data[3]; // 假设第4个字节是命令
switch (command) {
case 0x01: // 读取状态
SendStatusResponse();
break;
case 0x02: // 设置参数
ProcessSetCommand(data, length);
break;
case 0x03: // 读取数据
SendDataResponse();
break;
default:
SendErrorResponse(0x01); // 无效命令
break;
}
}
// 发送状态响应
void SendStatusResponse(void)
{
uint8_t response[32];
uint16_t length = 0;
// 构建响应帧
response[length++] = 0xAA; // 帧头1
response[length++] = 0x55; // 帧头2
response[length++] = 0x10; // 数据长度
response[length++] = 0x81; // 命令响应
// 状态数据
response[length++] = 0x01; // 设备状态
response[length++] = 0x00; // 错误代码
response[length++] = 0x00; // 保留
response[length++] = 0x00; // 保留
// 统计信息
uint32_t rx_count = UART_DMA_GetRxByteCount(&huart1_dma);
response[length++] = (rx_count >> 24) & 0xFF;
response[length++] = (rx_count >> 16) & 0xFF;
response[length++] = (rx_count >> 8) & 0xFF;
response[length++] = rx_count & 0xFF;
uint32_t packet_count = UART_DMA_GetRxPacketCount(&huart1_dma);
response[length++] = (packet_count >> 24) & 0xFF;
response[length++] = (packet_count >> 16) & 0xFF;
response[length++] = (packet_count >> 8) & 0xFF;
response[length++] = packet_count & 0xFF;
uint32_t error_count = UART_DMA_GetErrorCount(&huart1_dma);
response[length++] = (error_count >> 24) & 0xFF;
response[length++] = (error_count >> 16) & 0xFF;
response[length++] = (error_count >> 8) & 0xFF;
response[length++] = error_count & 0xFF;
// 计算校验和
uint8_t checksum = 0;
for (uint16_t i = 2; i < length; i++) { // 从长度字节开始
checksum ^= response[i];
}
response[length++] = checksum;
// 发送响应
UART_DMA_SendData(&huart1_dma, response, length);
}
int main(void)
{
// 初始化
HAL_Init();
SystemClock_Config();
// 初始化外设
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
// 初始化串口DMA
UART_DMA_Init(&huart1_dma, &huart1, &hdma_usart1_rx);
UART_DMA_Init(&huart2_dma, &huart2, &hdma_usart2_rx);
// 设置回调
huart1_dma.on_receive_complete = OnDataReceived;
// 启动接收
UART_DMA_StartReceive(&huart1_dma);
UART_DMA_StartReceive(&huart2_dma);
// 发送启动信息
Debug_SendInfo("System Started\r\n");
Debug_SendStatus(&huart1_dma);
while (1) {
// 主循环任务
// 1. 超时检测
UART_DMA_TimeoutCheck(&huart1_dma);
UART_DMA_TimeoutCheck(&huart2_dma);
// 2. 处理接收到的数据
UART_DMA_ProcessData(&huart1_dma);
UART_DMA_ProcessData(&huart2_dma);
// 3. 定期发送状态
static uint32_t last_status_time = 0;
if (HAL_GetTick() - last_status_time > 5000) { // 每5秒
SendStatusResponse();
last_status_time = HAL_GetTick();
}
// 4. 其他任务
// ...
HAL_Delay(1);
}
}
九、注意事项
- DMA缓冲区大小:根据实际数据量和系统内存调整缓冲区大小
- 中断优先级:合理配置串口中断和DMA中断的优先级
- 数据竞争:在多任务环境下注意数据访问的互斥保护
- 错误处理:完善各种错误情况的处理机制
- 性能优化:根据实际需求调整DMA传输模式
这个实现方案提供了完整的串口DMA接收不定长数据的功能,包括:
- 双缓冲机制防止数据丢失
- 空闲中断检测帧结束
- 环形缓冲区管理
- 协议解析支持
- 错误处理和调试功能
- 统计信息记录