4. UART帧格式详解:起始位、数据位(5-8位)、奇偶校验位、停止位

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)作为最经典的串行通信协议之一,其帧格式是理解整个通信过程的基石。与I2C、SPI等同步协议不同,UART在传输数据时并不依赖时钟线,而是依靠收发双方预先约定好的波特率(Baud Rate)以及帧结构来完成数据的同步与解析。一个标准的UART帧由空闲位(Idle)、起始位(Start Bit)、数据位(Data Bits)、可选的奇偶校验位(Parity Bit)以及停止位(Stop Bit)构成。本章将深入拆解帧格式的每一个细节,结合时序图、配置参数与实战案例,帮助您彻底掌握UART数据在物理线路上的真实形态。

UART 标准帧结构时序图(8N1 格式示例) 起始位 (低电平) D0 D1 D2 D3 D4 D5 D6 数据位(8位,LSB先行) 停止 空闲 高(1) 低(0) t 1 bit 1 bit 1 bit 1 bit 1 bit 1 bit 1 bit 1 bit 完整帧 = 1起始 + 8数据 + 1停止 = 10 bits TX 线电平

4.1 帧格式核心原理:异步通信的“握手”机制

UART通信的本质是异步串行传输,这意味着发送端和接收端之间没有独立的时钟信号线。为了正确解析数据,收发双方必须约定完全一致的帧格式和波特率。帧格式定义了数据在传输线上的“包装”方式,而波特率则定义了每一位(bit)在时间轴上的宽度。当线路处于空闲状态时,TX线保持在高电平(逻辑1)。当发送端准备发送数据时,首先拉低电平,产生一个下降沿,这个下降沿就是接收端开始采样的“启动信号”,即起始位。

起始位之后,紧接着是数据位。数据位的数量可以是5、6、7或8位,具体取决于系统设计需求。在传输时,数据通常按照最低有效位(LSB)先行的顺序发送。例如,要发送十六进制数据0x53(二进制0101 0011),则首先发送最低位1,然后是1、0、0、1、0、1、0,最后是最高位0。数据位之后是可选奇偶校验位,用于简单的错误检测。最后,发送端必须将线路拉高至少1位时间,即停止位,以表示当前帧传输结束,并为下一帧的起始位下降沿做准备。

关键理解:UART帧的每一位宽度由波特率决定。例如,在9600bps下,每一位持续约104.17微秒(1/9600)。接收端会在每一位的中间时刻进行采样,以避开信号跳变沿,提高采样可靠性。起始位的下降沿是接收端重新同步本地采样时钟的基准点。

4.2 帧结构逐位拆解与配置参数

一个完整的UART帧由以下五个部分顺序组成,任何一部分的配置不一致都会导致通信失败或数据乱码。

帧组成 电平状态 位宽(典型值) 功能说明
空闲位(Idle) 高电平(1) 任意长度 表示线路空闲,等待起始位下降沿
起始位(Start Bit) 低电平(0) 1 bit 标志一帧数据的开始,用于同步接收时钟
数据位(Data Bits) 高/低电平 5~8 bits(常用8位) 实际载荷数据,LSB先行发送
奇偶校验位(Parity) 高/低电平 0 或 1 bit 用于检测数据位在传输过程中是否发生单比特翻转
停止位(Stop Bit) 高电平(1) 1 或 2 bits 标志帧结束,保证下一帧起始位有明确的下降沿

4.2.1 起始位与停止位:帧同步的边界

起始位永远是低电平,且宽度严格等于1个位时间。接收端通过检测高电平到低电平的跳变沿来启动内部采样定时器。停止位则必须保持高电平,其宽度可以是1位、1.5位或2位。常见的配置为1位停止位。如果接收端在停止位期间检测到低电平,则会报出“帧错误”(Frame Error),这通常意味着收发双方的波特率不匹配或线路受到干扰。

4.2.2 数据位宽度选择:5-8位的权衡

数据位宽度的选择取决于需要传输的信息量。早期用于传输ASCII码时,7位数据位足够(0x00-0x7F)。8位数据位是最常用的配置,可以表示一个完整的字节(0x00-0xFF),适用于二进制协议或扩展ASCII字符。5位和6位数据位较少见,通常用于一些特殊的工业总线或老式电传打字机设备。在嵌入式开发中,几乎99%的场景都采用8位数据位。

4.2.3 奇偶校验位:最简单的错误检测机制

奇偶校验位分为三种模式:无校验(None)、奇校验(Odd)和偶校验(Even)。其原理是统计数据位中“1”的个数,然后根据校验规则填充校验位,使得整个数据位+校验位中“1”的总数为奇数(奇校验)或偶数(偶校验)。例如,发送数据0x53(0101 0011),其中包含4个“1”。若采用偶校验,校验位应为0(保持总数为4,偶数);若采用奇校验,校验位应为1(总数为5,奇数)。接收端会重新计算并比对,若不一致则报出“校验错误”(Parity Error)。需要注意的是,奇偶校验只能检测出奇数个比特错误,无法检测偶数个错误,也无法纠错。

工程经验:在噪声环境较强的工业现场,如果数据量不大,建议开启奇偶校验以增加一层保护。但在高速、大数据量传输时,奇偶校验会额外占用约10%的带宽,且检测能力有限,此时更推荐使用CRC校验(在协议层实现)。

4.3 帧时序图深度解读与采样点分析

观察上方的时序图,我们可以清晰地看到发送一个字节0x53(假设D0为最低位)的完整波形。线路初始为高电平空闲状态。在第一个位时间,线路被拉低,形成起始位。随后,D0(1)保持高电平,D1(0)拉低,D2(1)拉高,D3(0)拉低,D4(1)拉高,D5(0)拉低,D6(1)拉高,D7(0)拉低。最后,线路恢复高电平并保持至少1个位时间,作为停止位。

接收端的采样策略至关重要。由于没有时钟线,接收端通常使用波特率发生器产生一个比波特率高16倍或64倍的采样时钟。在检测到起始位的下降沿后,接收端会等待半个位时间(即8个过采样时钟周期),然后开始在每个位时间的中心点进行采样。这种“中心采样”方法可以有效避免信号在跳变沿附近的不稳定区域,提高数据解析的准确性。

4.4 实战案例:配置STM32串口发送与接收数据

下面我们通过一个基于STM32F103微控制器的实战案例,演示如何配置UART帧格式并完成数据收发。该案例的目标是通过串口1以115200bps、8位数据位、无校验、1位停止位(8N1)的格式,将字符串“Hello UART”发送出去,并将接收到的数据原样返回。

初始现象与目标:开发板通过USB转串口模块连接PC,PC端串口助手显示乱码或无数据。目标是通过正确配置USART寄存器,实现稳定的数据回环。

关键配置参数:

  • 波特率:115200
  • 数据位:8位
  • 停止位:1位
  • 校验位:无
  • 硬件流控:无

操作步骤(基于标准外设库):

// 1. 初始化GPIO引脚(PA9-TX, PA10-RX)
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

// TX引脚配置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// RX引脚配置为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 2. 配置USART参数
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);

// 3. 使能串口
USART_Cmd(USART1, ENABLE);

// 4. 发送字符串
char msg[] = "Hello UART\r\n";
for (int i = 0; i < strlen(msg); i++) {
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, (uint8_t)msg[i]);
}

// 5. 中断接收回环(需配置NVIC)
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        uint8_t rx_data = USART_ReceiveData(USART1);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
        USART_SendData(USART1, rx_data);
    }
}

结果对比与复盘:配置完成后,PC端串口助手正确显示“Hello UART”,且发送任意字符均能收到相同回显。若将波特率改为9600,而PC端仍为115200,则接收端会因采样点偏移而收到乱码。这验证了帧格式中波特率一致性的重要性。此外,若将停止位配置为2位,通信依然正常,但传输效率会降低约10%。

常见误区警示:在配置UART时,务必检查GPIO的复用功能是否正确(例如STM32的AFIO映射)。此外,当使用奇偶校验时,数据位长度必须包含校验位本身。例如,若配置为“8位数据+偶校验”,实际上物理帧中数据区为8位,但有效载荷只有7位,这在某些协议栈中容易引发数据错位。

4.5 常见误区与排障复盘

在实际调试UART通信时,工程师常遇到以下问题,通过帧格式知识可以快速定位。

故障现象 可能原因 排查手段
完全无数据 TX/RX接线错误、共地缺失 用示波器测量TX引脚是否有波形翻转
乱码(字符错乱) 波特率不匹配、时钟源频率偏差过大 核对双方波特率,检查MCU系统时钟配置
偶发丢字节 接收缓冲区溢出、停止位过短 启用流控或降低连续发送速率,检查停止位配置
帧错误(Frame Error) 停止位采样为低电平,多为波特率微小偏差或线路干扰 调整波特率误差,检查线路阻抗匹配

总结复盘:UART帧格式虽然简单,但每一个位都承载着特定的同步与校验职责。起始位提供了时间基准,数据位承载信息,校验位提供基本完整性检查,停止位则保证了帧间间隔。在工程实践中,建议优先采用8N1(8数据位、无校验、1停止位)作为默认配置,这是最通用且兼容性最好的格式。当遇到通信异常时,使用逻辑分析仪抓取波形,对照本章的时序图逐位分析,是最高效的排障手段。