news 2026/7/1 20:23:57

UART串口不定长数据接收方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UART串口不定长数据接收方法

一、基本概念与问题

在嵌入式系统中,串口(UART)通信时,数据通常以不定长的“帧”为单位发送。串口硬件本身只能识别单个字节的接收完成,无法自动判断一帧数据何时开始和结束。因此,需要通过软件方法来解决帧边界识别问题。

所有方法都基于一个基本的数据管理结构:

#define MAX_BUF_SIZE 200 typedef struct { uint8_t buffer[MAX_BUF_SIZE]; // 数据存储区 uint16_t count; // 已接收字节数 uint16_t length; // 帧长度 uint8_t complete_flag; // 帧完成标志 } UartRxManager; UartRxManager uart1_rx;

二、四种基础实现方法

1. 空闲中断检测法

原理

利用串口硬件的空闲检测功能:当RX引脚在一个字节传输时间后保持高电平,硬件会触发空闲中断,标志着一帧数据传输结束。

实现要点

初始化配置

// 开启空闲中断 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 启动DMA接收 HAL_UART_Receive_DMA(&huart1, uart1_rx.buffer, MAX_BUF_SIZE);

中断处理

void handle_idle_interrupt(UART_HandleTypeDef *huart) { if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(huart); // 停止DMA并计算接收长度 HAL_UART_DMAStop(huart); uart1_rx.length = MAX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx); uart1_rx.complete_flag = 1; // 重新启动接收 HAL_UART_Receive_DMA(huart, uart1_rx.buffer, MAX_BUF_SIZE); } }

2. 协议解析法

原理

在通信协议中定义固定的帧结构,通过识别帧头、帧长等信息来确定帧边界。

协议示例
字节位置内容说明
00x5A帧头1
10xA5帧头2
2N数据长度
3~N+2数据有效载荷
实现代码
typedef enum { WAIT_HEADER1, WAIT_HEADER2, WAIT_LENGTH, RECEIVING_DATA } RxState; void process_received_byte(uint8_t byte) { static RxState state = WAIT_HEADER1; static uint8_t expected_len = 0; switch(state) { case WAIT_HEADER1: if(byte == 0x5A) { uart1_rx.count = 0; uart1_rx.buffer[uart1_rx.count++] = byte; state = WAIT_HEADER2; } break; case WAIT_HEADER2: if(byte == 0xA5) { uart1_rx.buffer[uart1_rx.count++] = byte; state = WAIT_LENGTH; } else { state = WAIT_HEADER1; // 重新同步 } break; case WAIT_LENGTH: expected_len = byte; uart1_rx.buffer[uart1_rx.count++] = byte; state = RECEIVING_DATA; break; case RECEIVING_DATA: uart1_rx.buffer[uart1_rx.count++] = byte; if(uart1_rx.count >= (expected_len + 3)) { uart1_rx.complete_flag = 1; uart1_rx.length = uart1_rx.count; state = WAIT_HEADER1; } break; } }

3. 超时判断法

原理

基于数据连续性假设:如果在一定时间内没有收到新数据,则认为当前帧已结束。

时间计算

以9600波特率为例:

  • 1个字节传输时间 ≈ 1.04ms (10位/字节 ÷ 9600位/秒)

  • 超时时间建议:1.5-2倍字节时间 ≈ 2ms

实现代码
volatile uint32_t last_receive_time = 0; #define TIMEOUT_MS 2 // 接收中断中调用 void on_byte_received(uint8_t byte) { uart1_rx.buffer[uart1_rx.count++] = byte; last_receive_time = get_current_time(); // 更新时间戳 } // 主循环中检查 void check_timeout(void) { uint32_t current_time = get_current_time(); if(uart1_rx.count > 0 && (current_time - last_receive_time > TIMEOUT_MS)) { uart1_rx.complete_flag = 1; uart1_rx.length = uart1_rx.count; uart1_rx.count = 0; // 准备接收下一帧 } }

4. 环形缓冲区法

原理

中断只负责将数据存入缓冲区,主程序从缓冲区读取并解析数据,实现接收与处理的解耦。

数据结构
typedef struct { uint8_t *data; uint16_t size; uint16_t head; // 写入位置 uint16_t tail; // 读取位置 uint16_t count; // 数据数量 } RingBuffer; void rb_init(RingBuffer *rb, uint8_t *buf, uint16_t size) { rb->data = buf; rb->size = size; rb->head = rb->tail = rb->count = 0; } uint8_t rb_write(RingBuffer *rb, uint8_t byte) { if(rb->count >= rb->size) return 0; rb->data[rb->head] = byte; rb->head = (rb->head + 1) % rb->size; rb->count++; return 1; } uint8_t rb_read(RingBuffer *rb, uint8_t *byte) { if(rb->count == 0) return 0; *byte = rb->data[rb->tail]; rb->tail = (rb->tail + 1) % rb->size; rb->count--; return 1; }
使用方式
// 中断服务程序(极简) void USART1_IRQHandler(void) { uint8_t byte = USART1->DR; rb_write(&rx_buffer, byte); } // 主程序处理 int main(void) { uint8_t byte; while(1) { if(rb_read(&rx_buffer, &byte)) { // 解析协议或处理数据 process_received_byte(byte); } } }

三、方法对比与选择

方法优点缺点适用场景
空闲中断硬件支持、效率高需要硬件支持高速连续数据
协议解析可靠性高、有校验实现较复杂有固定协议
超时判断实现简单精度依赖定时低速应用
环形缓冲区解耦接收与处理内存占用较大多任务系统

四、选择建议

  1. 简单应用:从超时判断法开始,最容易理解和实现

  2. 可靠通信:选择协议解析法,具备错误检测能力

  3. 高效接收:使用空闲中断+DMA,减少CPU干预

  4. 复杂系统:采用环形缓冲区,便于扩展和维护

实际应用中常组合使用,如:空闲中断+协议解析,或环形缓冲区+超时判断。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/30 5:21:10

【2026官方最新】贵州工商职业学院专业设置一览表(含39个专业方向)

一、 学校概况:中国一流高职院校 贵州工商职业学院是经贵州省人民政府批准、教育部备案的全日制普通高等职业院校。学校办学实力雄厚,荣获“校友会全国高职院校前30强”、“中国一流高职院校”及“贵州省级高水平专业群院校”等殊荣。学校始终坚持“以学…

作者头像 李华
网站建设 2026/6/24 18:23:31

ShardingSphere 分库分表技术详解与 Spring Boot 实践

文章目录 前言理论基础1. 分库分表概念2. ShardingSphere 架构组成3. 核心组件4. 原理分析 Spring Boot 集成方案1. Maven 依赖配置2. 配置文件设置3. 测试用例4. 测试效果 实际应用场景1. 电商订单系统2. 日志分表策略 性能优化建议1. 连接池配置2. 查询优化 总结 前言 随着业…

作者头像 李华
网站建设 2026/6/24 11:53:36

PCBT600化学镀锡添加剂工程应用:基于工程与材料科学原理

摘要化学镀锡技术在PCB表面处理行业中占据重要地位。T600化学镀锡添加剂作为行业内的有效解决方案,主要解决了传统工艺中锡须生长、高纵横比孔径镀层不均、复杂布线无法覆盖等问题。其通过优化甲基磺酸/硫酸体系中锡离子反应状态,控制化学镀锡层稳定生长…

作者头像 李华
网站建设 2026/6/21 17:35:09

算清每一分钱:2026年AI开发平台选型与落地的精细化ROI测算模型

企业决策最终要回归财务逻辑。在AI热潮中保持清醒,必须回答:这个AI开发平台的投入,究竟能带来多少回报?本文将提供一个超越概念、可操作的精细化ROI测算框架,帮助您在思考2026年AI开发平台怎么选和规划AI应用如何落地时…

作者头像 李华
网站建设 2026/7/1 20:03:30

Android扩展系统框架新增API

新增API后需要执行下面两个命令: 1. m system-api-stubs-docs-non-updatable-update-current-api 2.m api-stubs-docs-non-updatable-update-current-api 单编framework: make -j16 framework-minus-apex

作者头像 李华