跳转至

串行口(UART)

STC89C52 内置一个 全双工通用异步收发器(UART),是最常用的通信外设之一。

相关 SFR

寄存器 地址 功能
SCON 98H 串行口控制寄存器(可位寻址)
SBUF 99H 串行口数据缓冲器(读=接收,写=发送)
PCON 87H 电源控制(SMOD 位控制波特率加倍)

工作模式

模式 SM0 SM1 说明 波特率
0 0 0 移位寄存器模式(同步) 固定 = fosc/12
1 0 1 8 位 UART(最常用) 可变(定时器 1/2 提供)
2 1 0 9 位 UART 固定 = fosc/32 或 fosc/64
3 1 1 9 位 UART 可变

模式 1 — 8 位 UART(最常用)

数据帧格式:起始位(0) + 8位数据(LSB first) + 停止位(1)

波特率由定时器 1 或定时器 2 产生。

波特率设置

使用定时器 1 产生波特率(最常用)

定时器 1 工作于模式 2(8 位自动重载),波特率计算公式:

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

常用配置表(12T 模式):

晶振频率 目标波特率 SMOD TH1 初值 实际误差
11.0592MHz 9600 0 0xFD 0% ✅
11.0592MHz 19200 1 0xFD 0% ✅
11.0592MHz 4800 0 0xFA 0% ✅
12MHz 9600 1 0xF9 0.16%
12MHz 4800 1 0xF3 0.16%

💡 为什么推荐 11.0592MHz 晶振? 因为它能精确产生串口常用的波特率(9600、19200 等),无误差。

使用定时器 2 产生波特率

波特率 = fosc / (32 × (65536 - RCAP2))

串口初始化(9600bps,11.0592MHz)

void UART_Init() {
    // 配置定时器 1 为波特率发生器
    TMOD &= 0x0F;    // 清除 T1 设置
    TMOD |= 0x20;    // T1: 模式 2(8 位自动重载)
    TH1 = 0xFD;      // 9600bps(11.0592MHz)
    TL1 = 0xFD;
    TR1 = 1;         // 启动 T1

    // 配置串口
    SCON = 0x50;     // 模式 1(8 位 UART),REN=1(允许接收)

    // 使能中断
    ES = 1;          // 使能串口中断
    EA = 1;          // 开总中断
}

// 发送一个字节
void UART_SendByte(unsigned char byte) {
    SBUF = byte;
    while (!TI);     // 等待发送完成
    TI = 0;          // 清除发送标志
}

// 发送字符串
void UART_SendString(unsigned char *str) {
    while (*str) {
        UART_SendByte(*str++);
    }
}

// 串口中断服务函数
void UART_ISR() interrupt 4 {
    if (RI) {        // 接收中断
        RI = 0;
        unsigned char received = SBUF;
        // 处理接收到的数据
    }
    if (TI) {        // 发送中断
        TI = 0;
        // 发送完成处理
    }
}

常用串口通信协议

查询方式接收(非中断)

unsigned char UART_Receive() {
    while (!RI);     // 等待接收完成
    RI = 0;
    return SBUF;
}

带超时的接收

unsigned char UART_Receive_Timeout(unsigned int timeout_ms) {
    unsigned int i;
    while (!RI) {
        // 简单延时实现超时
        for (i = 0; i < 1000; i++);
        if (--timeout_ms == 0) return 0xFF;  // 超时返回
    }
    RI = 0;
    return SBUF;
}

模式 0 — 移位寄存器模式

  • TxD(P3.1) 提供移位时钟
  • RxD(P3.0) 发送/接收数据
  • 常用于扩展 I/O 口(配合 74HC164/74HC165)

实用技巧

  1. 发送字符串建议用查询 TI 方式(简单可靠)
  2. 接收数据建议用中断方式(不阻塞 CPU)
  3. 发送完要清 TI,接收完要清 RI
  4. SBUF 是双缓冲的:读和写操作的是不同的物理位置