基于 STM32 HAL 库实现 **串口 DMA + 状态机接收** 的代码框架(2)

以下是基于 STM32 HAL 库实现 串口 DMA + 状态机接收 的代码框架,支持帧头 (0xA5 0x5A) 和帧尾 (0x0D 0x0A) 的数据解析:


1. 硬件配置(STM32CubeMX)

  1. 启用 USART(如 USART1),配置波特率(如 115200)。
  2. 启用 DMA:
    • 添加 DMA 通道(如 USART1_RX),模式为 Circular(循环模式)。
    • 内存地址自增,数据宽度为 Byte
  3. 生成代码。

2. 代码实现

2.1 定义数据结构和缓冲区
// 定义状态机状态
typedef enum {
    RX_STATE_WAIT_HEADER1,   // 等待帧头第一个字节 0xA5
    RX_STATE_WAIT_HEADER2,   // 等待帧头第二个字节 0x5A
    RX_STATE_RECEIVE_DATA,   // 接收数据直到帧尾
} UART_RxState;

// 定义接收控制块
typedef struct {
    UART_HandleTypeDef *huart;   // 串口句柄
    UART_RxState state;          // 当前状态
    uint8_t *rx_buffer;          // DMA 接收缓冲区(需足够大)
    uint16_t data_index;         // 数据索引
    uint16_t frame_length;       // 完整帧长度(含头尾)
    uint8_t frame_ready;         // 帧接收完成标志
} UART_DMA_RxCB;

// 全局变量
UART_DMA_RxCB uart_rx;
uint8_t dma_rx_buffer[256];      // DMA 接收缓冲区
2.2 初始化接收控制块
void UART_DMA_Init(UART_HandleTypeDef *huart) {
    uart_rx.huart = huart;
    uart_rx.rx_buffer = dma_rx_buffer;
    uart_rx.state = RX_STATE_WAIT_HEADER1;
    uart_rx.data_index = 0;
    uart_rx.frame_ready = 0;

    // 启动 DMA 接收(循环模式)
    HAL_UARTEx_ReceiveToIdle_DMA(huart, uart_rx.rx_buffer, sizeof(dma_rx_buffer));
}
2.3 状态机解析数据
void UART_ProcessData(void) {
    for (uint16_t i = 0; i < sizeof(dma_rx_buffer); i++) {
        uint8_t byte = uart_rx.rx_buffer[i];

        switch (uart_rx.state) {
            case RX_STATE_WAIT_HEADER1:
                if (byte == 0xA5) {
                    uart_rx.state = RX_STATE_WAIT_HEADER2;
                    uart_rx.data_index = 0;
                }
                break;

            case RX_STATE_WAIT_HEADER2:
                if (byte == 0x5A) {
                    uart_rx.state = RX_STATE_RECEIVE_DATA;
                } else {
                    uart_rx.state = RX_STATE_WAIT_HEADER1;  // 帧头错误,重置
                }
                break;

            case RX_STATE_RECEIVE_DATA:
                // 检查帧尾:0x0D 0x0A
                if (uart_rx.data_index > 0 && 
                    uart_rx.rx_buffer[uart_rx.data_index - 1] == 0x0D && 
                    byte == 0x0A) 
                {
                    uart_rx.frame_length = uart_rx.data_index + 1;
                    uart_rx.frame_ready = 1;    // 标记帧接收完成
                    uart_rx.state = RX_STATE_WAIT_HEADER1;
                } else {
                    uart_rx.data_index++;
                    if (uart_rx.data_index >= sizeof(dma_rx_buffer)) {
                        uart_rx.state = RX_STATE_WAIT_HEADER1;  // 缓冲区溢出,重置
                    }
                }
                break;
        }
    }
}
2.4 中断回调处理
// 在 stm32xxxx_it.c 中实现 DMA 或 UART 空闲中断回调
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
    if (huart == uart_rx.huart) {
        UART_ProcessData();  // 处理接收到的数据
    }
}
2.5 主循环处理完整帧
// 在 main.c 的 while(1) 循环中检查帧完成标志
while (1) {
    if (uart_rx.frame_ready) {
        // 处理完整帧数据(帧内容在 dma_rx_buffer[0] 到 dma_rx_buffer[frame_length-1])
        // 例如:解析数据、校验等
        Process_Frame(dma_rx_buffer, uart_rx.frame_length);

        // 重置标志
        uart_rx.frame_ready = 0;
    }
}

3. 关键点说明

  1. DMA 模式

    • 使用 HAL_UARTEx_ReceiveToIdle_DMA 实现 循环 DMA 接收,配合空闲中断 (IDLE) 触发数据处理。
    • 缓冲区需足够大(如 256 字节),避免数据覆盖。
  2. 状态机设计

    • 状态1:等待帧头第一个字节 0xA5
    • 状态2:验证帧头第二个字节 0x5A
    • 状态3:持续接收数据,直到检测到帧尾 0x0D 0x0A
  3. 帧处理

    • 检测到帧尾后,标记 frame_ready,主循环处理数据。
    • 需实现 Process_Frame() 函数解析有效数据(如校验、数据提取等)。

4. 优化建议

  • 数据校验:在帧尾前添加 CRC 校验字节,并在 Process_Frame() 中验证。
  • 双缓冲:使用双 DMA 缓冲区切换,避免处理数据时覆盖。
  • 超时机制:若长时间未收到帧尾,强制重置状态机。

通过此方案,可高效实现串口数据的 DMA 接收 + 状态机解析,确保数据完整性和低 CPU 占用率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值