核心内容摘要
NVIDIA团队打造“罗马速建师“:一分钟重建千张照片的3D世界
基本概念与问题在嵌入式系统中串口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;
四种基础实现方法
空闲中断检测法原理利用串口硬件的空闲检测功能当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); } }
协议解析法原理在通信协议中定义固定的帧结构通过识别帧头、帧长等信息来确定帧边界。
协议示例字节位置内容说明00x5A帧头110xA5帧头22N数据长度3~N2数据有效载荷实现代码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 0xA
{ 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
) { uart1_rx.complete_flag 1; uart1_rx.length uart1_rx.count; state WAIT_HEADER1; } break; } }
超时判断法原理基于数据连续性假设如果在一定时间内没有收到新数据则认为当前帧已结束。
时间计算以9600波特率为例1个字节传输时间 ≈
04ms (10位/字节 ÷ 9600位/秒)超时时间建议
1.
倍字节时间 ≈ 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; // 准备接收下一帧 } }
环形缓冲区法原理中断只负责将数据存入缓冲区主程序从缓冲区读取并解析数据实现接收与处理的解耦。
数据结构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