ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【串口技术系列文档 04】UART协议深度解析

【串口技术系列文档 04】UART协议深度解析 本文目标深入剖析UART协议帧格式、时序特性、错误检测机制掌握协议层面的设计与优化目录[TOC](目录)UART协议基础UART vs USART vs SCI异步通信原理全双工通信帧格式详解标准帧结构常见配置命名数据位传输顺序起始位与停止位起始位Start Bit停止位Stop Bit数据位配置5/6/7/8/9数据位9位模式应用实例奇偶校验机制奇偶校验原理硬件实现软件计算奇偶校验的局限性时序图与波形分析标准8N1波形连续传输波形波特率不匹配波形协议变体与扩展LIN总线基于UARTIrDA红外通信ISO7816智能卡错误检测与处理帧错误Framing Error奇偶校验错误Parity Error过载错误Overrun Error噪声错误Noise Error协议层性能优化减少协议开销批量传输优化接收缓冲优化实战案例分析案例1串口数据乱码分析案例2间歇性丢字节案例3长帧传输失败关键问题解答Q1为什么UART采用LSB先行Q29位模式有什么实际用途Q3奇偶校验还有必要吗延伸阅读参考资料UART协议基础UART vs USART vs SCIUART (Universal Asynchronous Receiver/Transmitter)特点 ✓ 异步通信无时钟线 ✓ 全双工 ✓ 点对点 ✓ 简单可靠USART (Universal Synchronous/Asynchronous Receiver/Transmitter)UART的超集 ✓ 支持同步模式带时钟 ✓ 支持ISO7816智能卡协议 ✓ 更复杂的硬件SCI (Serial Communication Interface)Freescale/NXP的命名 实质上就是UART异步通信原理为什么不需要时钟线1. 收发双方预先约定波特率 2. 每个字节通过起始位同步 3. 数据位时间由波特率确定 4. 停止位提供帧间隔 优点 ✓ 只需2根线TX/RX ✓ 长距离传输 ✓ 电气简单 缺点 ✗ 波特率误差敏感 ✗ 速度受限 ✗ 每帧开销大全双工通信双向独立传输MCU A MCU B TX ------------------ RX RX ------------------ TX GND ---------------- GND 发送和接收可同时进行 各自独立的移位寄存器帧格式详解标准帧结构基本格式空闲 | 起始位 | 数据位 | 奇偶校验位 | 停止位 | 空闲 ─────┐ ┌───────────┐ ┌───────────── └───────┘ └─────────┘ IDLE (高) START (0) DATA (LSB first) PARITY (可选) STOP (1 or 1.5 or 2)常见配置命名8N1- 最常用8: 8个数据位 N: 无奇偶校验 (None) 1: 1个停止位 总共1 8 0 1 10位/字节 效率8/10 80%8E1- 带偶校验8: 8个数据位 E: 偶校验 (Even) 1: 1个停止位 总共1 8 1 1 11位/字节 效率8/11 72.7%8O1- 带奇校验8: 8个数据位 O: 奇校验 (Odd) 1: 1个停止位其他变体7N1: 用于ASCII7位足够 9N1: 多机通信地址位 8N2: 高速通信增加间隔数据位传输顺序LSB First低位先行发送 0xA5 (10100101b) 时间轴→ START D0 D1 D2 D3 D4 D5 D6 D7 STOP 线电平 0 1 0 1 0 0 1 0 1 1 先发送 LSB (D01) 最后发送 MSB (D71) 为什么 - 早期UART硬件简化移位寄存器自然顺序 - 成为事实标准起始位与停止位起始位Start Bit作用1. 通知接收方数据来了 2. 同步接收方的采样时钟 3. 从空闲状态(高)跳变到低电平检测原理// 伪代码起始位检测voiduart_rx_state_machine(void){staticenum{IDLE,START,DATA,STOP}stateIDLE;switch(state){caseIDLE:if(RX_PINLOW){// 检测到下降沿start_bit_timer();stateSTART;}break;caseSTART:if(timer_reached(0.5*bit_time)){// 在起始位中间采样确认是否为低if(RX_PINLOW){stateDATA;bit_counter0;}else{// 虚假起始位返回空闲stateIDLE;}}break;// ...}}虚假起始位原因 - 噪声干扰 - 电平抖动 - 线路瞬态 解决 - 在起始位中间再次采样确认 - 硬件滤波器 - 多数判决停止位Stop Bit作用1. 标记帧结束 2. 保证最小帧间间隔 3. 给接收方处理时间停止位配置1位停止位 - 标准配置 - 最高效率 1.5位停止位 - 5位数据位专用 - 历史遗留 2位停止位 - 增加接收方处理时间 - 降低对波特率误差的敏感性 - 降低吞吐量停止位错误检测// 检测停止位应该为高if(stop_bit_sampleLOW){// 帧错误 (Framing Error)UART_STATUS|FRAMING_ERROR_FLAG;// 可能原因// 1. 波特率不匹配// 2. 发送方过早发送下一字节// 3. 噪声干扰}数据位配置5/6/7/8/9数据位5位数据历史Baudot码电传打字机 现代几乎不用 范围0x00 - 0x1F32个字符7位数据应用ASCII码0x00-0x7F 优点节省带宽 用途纯文本传输8位数据应用最常用 范围0x00 - 0xFF256个值 用途二进制数据、扩展ASCII9位数据应用多机通信Multi-processor Communication 第9位地址/数据标识位 原理 - 第9位 1地址字节所有从机接收 - 第9位 0数据字节仅被寻址的从机接收 STM32实现 USART_CR1 | USART_CR1_M; // 9位模式9位模式应用实例RS485多机通信// 主机发送voidsend_to_slave(uint8_taddr,uint8_tdata){// 发送地址第9位1uart_send_9bit(addr,1);// 发送数据第9位0uart_send_9bit(data,0);}// 从机接收voiduart_rx_interrupt(void){uint16_treceivedUART_DR;bool is_address(received0x100);// 第9位uint8_tdatareceived0xFF;if(is_address){if(dataMY_ADDRESS){addressedtrue;}else{addressedfalse;}}else{if(addressed){process_data(data);}}}奇偶校验机制奇偶校验原理偶校验Even Parity规则数据位中1的个数 校验位 偶数 例子 数据 0xA5 10100101b 1的个数 4偶数 校验位 0 数据 0xA4 10100100b 1的个数 3奇数 校验位 1奇校验Odd Parity规则数据位中1的个数 校验位 奇数 例子 数据 0xA5 10100101b 1的个数 4偶数 校验位 1硬件实现奇偶校验生成电路module parity_generator ( input wire [7:0] data, input wire parity_mode, // 0偶校验, 1奇校验 output wire parity_bit ); // XOR树计算1的个数奇偶性 wire parity_even; assign parity_even ^data; // 所有位异或 // 根据模式选择 assign parity_bit parity_even ^ parity_mode; endmodule奇偶校验检测module parity_checker ( input wire [7:0] data, input wire received_parity, input wire parity_mode, output wire parity_error ); wire calculated_parity; assign calculated_parity (^data) ^ parity_mode; assign parity_error (calculated_parity ! received_parity); endmodule软件计算C语言实现// 计算偶校验位uint8_tcalc_even_parity(uint8_tdata){uint8_tparity0;for(inti0;i8;i){parity^(datai)1;}returnparity;}// 优化使用查表法constuint8_tparity_table[256]{0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,// ... 256个预计算值};uint8_tcalc_parity_fast(uint8_tdata){returnparity_table[data];}// 更优化使用位操作技巧uint8_tcalc_parity_bitwise(uint8_tdata){data^data4;data^data2;data^data1;returndata1;}奇偶校验的局限性只能检测奇数位错误原始数据10100101 1位错误10100100 → 检测到 ✓ 2位错误10100100 → 检测不到 ✗ 3位错误10100000 → 检测到 ✓检错不纠错知道有错误但不知道 - 哪一位错了 - 错了几位 - 无法自动纠正更强的校验方案CRC循环冗余校验 - 检测多位错误 - 应用层实现 海明码 - 可纠错 - 硬件复杂时序图与波形分析标准8N1波形发送0x55 (01010101b)时间0 1 2 3 4 5 6 7 8 9 10 ────┐ ┌────┐ ┌────┐ ┌────┐ ┌───────── └────┘ └────┘ └────┘ └────┘ START D0 D1 D2 D3 D4 D5 D6 D7 STOP 说明 - 空闲状态高电平 - 起始位0下降沿触发 - D0-D701010101LSB first - 停止位1回到高电平发送0x00 (全0)────┐ ┌───── └───────────────────────────────────┘ START 8个数据位(全0) STOP 注意连续9个低电平起始位8个0发送0xFF (全1)────┐ ┌──────────────────────────────────── └────┘ START 8个数据位(全1) STOP 注意数据位和停止位连在一起连续传输波形背靠背传输字节1 字节2 ────┐ ┌─┐ ┌─┐ ┌────┐ ┌─┐ ┌─┐ └────┘ └────┘ └──┘ └────┘ └────┘ └──... S D0-7 P ST S D0-7 P ST 最小间隔 1个停止位时间有间隔传输字节1 字节2 ────┐ ┌─┐ ┌─┐ ┌──────────┐ ┌─┐ └────┘ └────┘ └──┘ └────┘ └──... S D0-7 P ST IDLE S D0-7 间隔可任意长保持高电平波特率不匹配波形发送方115200接收方57600慢一倍发送 ────┐ ┐ ┐ ┐ ┐ ┐ └──┴──┴──┴──┴──┘ 接收方采样 ↓ ↓ ↓ ↓ 正确 错误 错误 错误 结果只能正确接收起始位数据全错协议变体与扩展LIN总线基于UARTLocal Interconnect Network在UART基础上增加 1. 同步间隔段Sync Break至少13位低电平 2. 同步字段0x55 3. 标识符字段6位ID 2位校验 4. 数据字段1-8字节 5. 校验和增强校验 应用汽车内部低速网络LIN帧格式// LIN Break字段唤醒总线voidlin_send_break(void){// 发送至少13位的低电平UART_CR1~UART_CR1_TE;// 禁用发送GPIO_ResetBits(TX_PIN);// 强制拉低delay_us(13*bit_time);// 13位时间GPIO_SetBits(TX_PIN);// 恢复高电平UART_CR1|UART_CR1_TE;// 重新使能发送// 发送同步字节uart_send(0x55);}IrDA红外通信红外数据协会标准基于UART调制为红外脉冲 - 逻辑03/16位时间的脉冲 - 逻辑1无脉冲 STM32支持 USART_CR3 | USART_CR3_IREN; // 使能IrDA模式ISO7816智能卡单线半双工UART特点 - 单根数据线双向 - 卡与读卡器使用相同线路 - 需要方向控制 - 特殊时序要求 STM32配置 USART_CR3 | USART_CR3_HDSEL; // 半双工模式错误检测与处理帧错误Framing Error定义在预期的停止位位置没有检测到高电平原因1. 波特率不匹配最常见 2. 发送方提前发送下一帧 3. 噪声导致停止位被拉低 4. 线路断开保持低电平检测代码voiduart_rx_interrupt(void){uint32_tstatusUART_SR;uint8_tdataUART_DR;// 读DR清除部分标志if(statusUART_SR_FE){// 帧错误frame_error_count;// 可能需要重新同步或通知上层}}奇偶校验错误Parity Error定义计算的奇偶校验位与接收的不匹配原因1. 单bit传输错误噪声 2. 校验配置不一致 3. 数据损坏处理策略if(statusUART_SR_PE){parity_error_count;// 策略1丢弃数据// (数据已读自动丢弃)// 策略2请求重传send_nak();// 策略3记录但继续log_error(data);}过载错误Overrun Error定义新数据到达但上一个数据还未被读取 数据寄存器被覆盖原因1. 中断响应太慢 2. 中断被长时间禁用 3. 接收处理速度不够预防措施// 使用DMA自动搬运voiduart_dma_init(void){DMA_InitTypeDef DMA_InitStruct;DMA_InitStruct.DMA_PeripheralBaseAddr(uint32_t)UART1-DR;DMA_InitStruct.DMA_MemoryBaseAddr(uint32_t)rx_buffer;DMA_InitStruct.DMA_BufferSizeRX_BUFFER_SIZE;DMA_InitStruct.DMA_ModeDMA_Mode_Circular;// ...DMA_Init(DMA1_Channel5,DMA_InitStruct);UART_DMACmd(UART1,UART_DMAReq_Rx,ENABLE);}噪声错误Noise Error定义在采样点附近检测到电平抖动硬件滤波多数判决采样3次取多数 例如采样点7, 8, 9 - 采样值1, 1, 0 → 判定为1 - 采样值0, 1, 0 → 判定为0协议层性能优化减少协议开销选择合适的帧格式8N1: 10位/字节 → 80%效率 8E1: 11位/字节 → 72.7%效率 8N2: 11位/字节 → 72.7%效率 推荐 - 可靠环境8N1 - 干扰环境8E1或增加应用层CRC批量传输优化背靠背发送// 低效每次发送等待完成for(inti0;i100;i){uart_send_byte(buffer[i]);while(!uart_tx_complete());// 浪费CPU}// 高效使用FIFO或DMAuart_send_dma(buffer,100);// CPU继续执行其他任务接收缓冲优化环形缓冲区#defineRX_BUFFER_SIZE256typedefstruct{uint8_tbuffer[RX_BUFFER_SIZE];volatileuint16_thead;volatileuint16_ttail;}ring_buffer_t;// 中断中写入voiduart_rx_irq(void){uint8_tdataUART_DR;uint16_tnext(rx_buf.head1)%RX_BUFFER_SIZE;if(next!rx_buf.tail){// 缓冲区未满rx_buf.buffer[rx_buf.head]data;rx_buf.headnext;}else{// 缓冲区满数据丢失overflow_count;}}// 主循环读取uint8_tuart_read(void){while(rx_buf.headrx_buf.tail);// 等待数据uint8_tdatarx_buf.buffer[rx_buf.tail];rx_buf.tail(rx_buf.tail1)%RX_BUFFER_SIZE;returndata;}实战案例分析案例1串口数据乱码分析问题描述现象接收的数据完全乱码 环境STM32与PC通信115200 bps排查步骤// 1. 检查波特率配置printf(USART BRR: 0x%04X\n,USART1-BRR);// 应该是72MHz / (16*115200) 39.0625 → 0x0271// 2. 检查帧格式printf(CR1: 0x%04X\n,USART1-CR1);// 检查M位数据位长度PCE位校验使能// 3. 使用示波器测量实际波特率// 发送0x55测量单个位的时间根因发现BRR 0x0139 (实际应该是0x0271) 计算实际波特率 72MHz / (16 * 0x139) 230400 结论配置成了2倍波特率解决// 正确配置USART1-BRR0x0271;// 115200 bps 72MHz案例2间歇性丢字节问题描述现象大部分时间正常偶尔丢失字节 特点高负载时更频繁分析// 检查中断优先级NVIC_GetPriority(USART1_IRQn);// 返回15最低// 检查中断嵌套// 发现SPI中断优先级更高执行时间长根因UART中断被SPI中断抢占 SPI ISR执行时间 1字节接收时间 导致overrun error解决方案// 方案1提高UART中断优先级NVIC_SetPriority(USART1_IRQn,2);// 高于SPI// 方案2使用FIFO如果硬件支持UART_CR1|UART_FIFO_ENABLE;// 方案3使用DMAuart_dma_init();案例3长帧传输失败问题描述短数据包10字节正常 长数据包100字节末尾数据错误波形分析示波器观察 - 前80字节波形正常 - 后20字节位宽逐渐变窄 - 最后几字节严重变形根因发送端使用软件延时控制位时间 长时间发送导致MCU温升 内部RC振荡器频率漂移 波特率逐渐升高解决1. 改用外部晶振精度高温度稳定 2. 改用硬件UART不依赖软件延时 3. 增加散热措施关键问题解答Q1为什么UART采用LSB先行答历史原因 1. 早期机械电传打字机采用这种顺序 2. 简化移位寄存器硬件设计 3. 成为事实标准 技术原因 - 移位寄存器自然从低位输出 - 无需额外逻辑反转位序Q29位模式有什么实际用途答1. 多机通信RS485网络 - 第9位标识地址/数据 2. 奇偶校验 标记位 - 同时支持校验和额外标志 3. 扩展字符集 - 支持512个不同符号Q3奇偶校验还有必要吗答现代系统通常不用 ✗ 只能检测奇数位错误 ✗ 不能纠错 ✗ 增加开销 更好的方案 ✓ 应用层CRC校验 ✓ 帧序号 重传机制 ✓ 加密认证如TLS 但在简单系统中仍有价值 ✓ 零成本硬件自动 ✓ 快速发现通信问题 ✓ 调试有帮助延伸阅读[[03-时钟与波特率]] - 波特率生成与误差分析[[05-过采样原理与实现]] - 接收端采样机制[[07-UART控制器寄存器详解]] - 硬件配置细节[[08-中断驱动vs轮询vs DMA]] - 数据收发方式参考资料芯片手册STM32F1xx Reference Manual - USART章节AVR USART Protocol Description标准文档RS-232-C StandardLIN Specification 2.2A应用笔记AN2606: UART通信协议详解AN1045: LIN协议实现上一篇[[03-时钟与波特率]]下一篇[[05-过采样原理与实现]]✨ 感谢你的阅读 ✨技术之路漫漫分享是为了更好地交流。如果本文的内容对你有启发希望能得到你的支持 点赞|⭐ 收藏| 关注如果你在调试过程中遇到了其他问题欢迎在评论区 留言我们一起探讨。关注我一起交流底层开发的那些事儿
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进