串行口(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 位自动重载),波特率计算公式:
常用配置表(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 产生波特率
串口初始化(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_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)
实用技巧
- 发送字符串建议用查询 TI 方式(简单可靠)
- 接收数据建议用中断方式(不阻塞 CPU)
- 发送完要清 TI,接收完要清 RI
- SBUF 是双缓冲的:读和写操作的是不同的物理位置