串行之路:UART通信原理、参数体系与51单片机实战精要

# 串行之路:UART通信原理、参数体系与51单片机实战精要


串口是嵌入式系统中最基础、最顽强的通信接口。其硬件简单,两根信号线即可实现双向通信;其协议透明,从51单片机到ARM、PC串口助手均可直接互联。然而许多开发者调通串口后便不再深究:起始位为何是低电平?波特率误差容忍范围多少?奇偶校验如何在中断中处理?本文从异步串行通信的帧结构出发,逐层解析波特率发生器、寄存器配置、中断架构,最终落地为可复用的工程代码。


---


## 一、异步串行通信的帧解剖


UART通信以**字符帧**为基本单元。帧结构由起始位、数据位、可选校验位、停止位拼接而成:


**起始位**:1位,低电平。总线空闲时为高电平,下降沿标志接收开始。


**数据位**:5~8位,低位先行。51单片机模式1固定为8位数据。


**校验位**:奇/偶/无。奇校验使“数据位+校验位”中1的个数为奇数。


**停止位**:1或2位,高电平。为下一帧起始位的下降沿提供准备时间。


**时序关系**:每帧时长 = (1起始 + 数据位 + 校验位 + 停止位) × 位时间。9600bps下8N1格式每帧约1.04ms。


**理解帧结构是诊断串口问题的前提**。乱码常因波特率对位误差累计;帧错位常因停止位丢失或干扰被误认为起始位。


---


## 二、波特率发生器:从公式到初值


51单片机UART的波特率源自**定时器1的溢出率**。模式1下:


```

波特率 = (2^SMOD / 32) × 定时器1溢出率

```


定时器1方式2(8位自动重装)时:


```

溢出率 = 晶振频率 / (12 × (256 - TH1))

```


**工程公式**:


```

TH1 = 256 - 晶振频率 / (波特率 × 12 × (32 / 2^SMOD))

```


常用配置(11.0592MHz):


| 波特率 | SMOD | TH1初值 | 误差  |

|--------|------|---------|-------|

| 4800   | 0    | 0xFA    | 0.00% |

| 9600   | 0    | 0xFD    | 0.00% |

| 19200  | 1    | 0xFD    | 0.00% |

| 115200 | 1    | 0xFF    | 8.5%  |


**误差容忍**:串口异步通信要求波特率误差 ≤ ±2.5%。115200在11.0592MHz下误差8.5%已不可靠,需换用22.1184MHz晶振或硬件串口独立波特率发生器。


---


## 三、寄存器配置:SCON与PCON


**SCON**:串口控制寄存器,地址0x98,可位寻址。


```c

SCON = 0x50;  // 0101 0000

// SM0 SM1 SM2 REN TB8 RB8 TI RI

// 0   1   0   1   0   0   0   0

```


- SM0/SM1 = 01:模式1,8位UART,波特率可变

- REN = 1:允许接收

- TI/RI:发送/接收中断标志,硬件置位,**必须软件清零**

<"y3.a8k1.org.cn"><"e7.a8k1.org.cn"><"u9.a8k1.org.cn">

**PCON**:电源控制寄存器,SMOD位(PCON.7)控制波特率倍频。


```c

PCON &= 0x7F;  // SMOD = 0,波特率正常

PCON |= 0x80;  // SMOD = 1,波特率加倍

```


**SBUF**:串口缓冲寄存器。物理上为**两个独立寄存器**——写SBUF启动发送,读SBUF获取接收数据。


---


## 四、收发架构:查询发送 + 中断接收


**查询发送**:TI标志轮询,实现简单。


```c

void UART_SendByte(unsigned char dat) {

    SBUF = dat;

    while (!TI);   // 等待发送完成

    TI = 0;        // 软件清零

}

```


**中断接收**:响应及时,不阻塞主循环。


```c

void UART_ISR(void) interrupt 4 {

    if (RI) {

        RI = 0;

        unsigned char rx = SBUF;

        process_rx_byte(rx);  // 仅状态机流转,绝不阻塞

    }

    // TI中断通常不在ISR中处理,或仅置标志位

}

```


**收发互锁**:发送操作在中断开启时也会触发TI中断。若中断函数内未判断TI/RI且无TI处理,将产生**冗余中断**。规范做法是ISR中仅处理RI,TI由查询方式管理。


---


## 五、协议层:帧边界与状态机


字节收发是物理层能力,**协议层需解决帧同步问题**。两种工程模式:


**模式A:固定帧长 + 定时超时**


接收固定字节数后认定一帧完整。适用于指令长度恒定的场景。


```c

#define FRAME_LEN 8

unsigned char rx_buffer[FRAME_LEN];

unsigned char rx_index = 0;


void process_rx_byte(unsigned char dat) {

    rx_buffer[rx_index++] = dat;

    if (rx_index >= FRAME_LEN) {

        rx_index = 0;

        frame_ready = 1;

    }

}

```


**模式B:特殊字符定界 + 状态机**


以帧头帧尾标识边界,兼容变长帧。常用格式:`AA CMD LEN DATA ... SUM 55`


```c

typedef enum { IDLE, HEAD1, HEAD2, CMD, LEN, DATA, CH K } RxState;

RxState rx_state = IDLE;

unsigned char rx_sum, rx_len, rx_cnt, rx_cmd;

unsigned char rx_payload[16];


void process_rx_byte(unsigned char dat) {

    switch (rx_state) {

        case IDLE:    if (dat == 0xAA) rx_state = HEAD1; break;

        case HEAD1:   rx_state = (dat == 0x55) ? HEAD2 : IDLE; break;

        case HEAD2:   rx_cmd = dat; rx_sum = dat; rx_state = CMD; break;

        case CMD:     rx_len = dat; rx_sum += dat; rx_cnt = 0; 

                      rx_state = (rx_len > 0) ? DATA : CH K; break;

        case DATA:    rx_payload[rx_cnt++] = dat; rx_sum += dat;

                      if (rx_cnt >= rx_len) rx_state = CH K; break;

        case CH K:     if (dat == rx_sum) { /* 帧有效,执行命令 */ }

                      rx_state = IDLE; break;

        default:      rx_state = IDLE; break;

    }

}

```


**状态机优势**:无需帧级缓冲区,每字节处理时间O(1),**与帧长度无关**。


---


## 六、工程优化与常见陷阱


**晶振选择**:11.0592MHz是串口通信的黄金频率——其分频可精确产生多种标准波特率。12MHz虽为整数,但9600bps误差达8.5%,高波特率下不可用。


**波特率自适应**:某些系统需检测主机波特率。原理:主机发送0x55(二进制01010101),从机测量相邻下降沿间隔计算位时间。


**发送阻塞优化**:大批量数据发送时`while(!TI)`占用CPU。可改为**发送中断驱动环形队列**:主循环或定时器填充缓冲区,ISR取下一字节发送。


```c

// 简化示意:TI中断中发送缓冲区下一字节

if (TI) {

    TI = 0;

    if (tx_head != tx_tail) {

        SBUF = tx_buffer[tx_tail++];

    }

}

```

<"b2.a8k1.org.cn"><"x5.a8k1.org.cn"><"r1.a8k1.org.cn">

**电气噪声**:长线通信需加0.1μF电容对地,或使用MAX232转换RS-232电平。TTL电平直连超过50cm易产生误码。


---


## 七、从配置到框架


掌握SCON、TMOD、TH1是串口入门的起点,但**工程级串口驱动应是可配置、可移植、低耦合的框架**。将波特率计算、中断处理、环形缓冲区、协议解析封装为独立模块,上层业务无需关注SBUF何时写入、TI何时清零。


串口是嵌入式的“标准外设”,其原理三十余年未变。理解帧结构才能诊断乱码,掌握波特率生成才能自由移植,封装协议层才能使串口真正服务于业务。**从调通到精通,正是将寄存器操作抽象为通信服务的过程**。


请使用浏览器的分享功能分享到微信等