1. 从零开始理解UART通信
作为一名单片机开发者,UART(通用异步收发器)是最基础也最重要的通信接口之一。STC89C52RC这款经典51单片机内置的UART模块,虽然原理简单,但实际配置时却有不少需要注意的细节。我第一次接触UART时,就被各种寄存器配置搞得晕头转向,后来通过反复实践才真正掌握了它的使用要领。
UART最大的特点就是异步全双工通信。所谓异步,指的是通信双方不需要共享时钟信号,而是通过预先约定好的波特率来同步数据。全双工则意味着可以同时进行数据的发送和接收。这种特性使得UART非常适合用于单片机与电脑、传感器模块等外设之间的通信。
2. 关键寄存器详解
2.1 串行控制寄存器SCON
SCON寄存器是UART配置的核心,它决定了UART的工作方式和基本功能。让我们逐个分析它的各个位:
- SM0和SM1:这两位组合决定了UART的工作方式。常见的有:
- 方式0:同步移位寄存器模式,波特率固定为fosc/12
- 方式1:8位UART,波特率可变(最常用)
- 方式2:9位UART,波特率固定为fosc/32或fosc/64
- 方式3:9位UART,波特率可变
对于初学者,我强烈建议从方式1开始,因为它最灵活也最常用。方式3虽然支持多机通信,但对新手来说配置更复杂。
- SM2:在多机通信中用于地址帧过滤。方式1下通常设为0。
- REN:接收使能位。设为1才能接收数据,这个我刚开始经常忘记设置,导致只能发送不能接收。
- TB8/RB8:方式2和方式3中的第9位数据。方式1中RB8存放的是停止位。
- TI/RI:中断标志位。发送或接收完成后会置1,需要手动清零。这里有个坑:如果不及时清零,会导致重复进入中断。
2.2 电源控制寄存器PCON
PCON中与UART相关的位不多,但很重要:
- SMOD:波特率加倍位。设为1时波特率翻倍,可以用于需要较高波特率的场合。但要注意,这会增加通信误差的风险。
- SMOD0:帧错误检测使能位。一般应用中可以设为0。
2.3 数据缓冲寄存器SBUF
SBUF是个很特殊的寄存器,它实际上是两个物理寄存器合用一个地址:
- 发送SBUF:写入数据时会存入发送寄存器
- 接收SBUF:读取时是从接收寄存器获取数据
这种设计巧妙地解决了全双工通信的需求。需要注意的是,SBUF只是数据的中转站,发送和接收都有独立的缓冲区。
3. 波特率配置实战
3.1 定时器1的配置
由于UART是异步通信,需要定时器来产生波特率时钟。STC89C52RC使用定时器1作为波特率发生器,配置步骤如下:
- 设置定时器1为模式2(8位自动重装)
- 计算并设置TH1的值
- 启动定时器1
波特率计算公式为:
波特率 = (2^SMOD × fosc) / (32 × 12 × (256 - TH1))
以常用的11.0592MHz晶振、9600波特率为例:
TH1 = 256 - (11059200 / (384 × 9600)) = 253 (0xFD)
3.2 完整初始化代码
c复制void UART_Init() {
SCON = 0x50; // 方式1,允许接收
PCON &= 0x7F; // SMOD=0
TMOD &= 0x0F; // 清除定时器1模式位
TMOD |= 0x20; // 定时器1,模式2
TH1 = 0xFD; // 9600波特率@11.0592MHz
TL1 = TH1;
ET1 = 0; // 禁止定时器1中断
TR1 = 1; // 启动定时器1
}
4. 数据收发实现
4.1 发送数据
发送数据很简单,只需写入SBUF即可:
c复制void UART_SendByte(unsigned char dat) {
SBUF = dat;
while(!TI); // 等待发送完成
TI = 0; // 清除标志位
}
注意:一定要等待TI置位并手动清零,否则会导致数据发送不完整或丢失。
4.2 接收数据
接收数据有两种方式:查询式和中断式。
查询式接收:
c复制unsigned char UART_ReceiveByte() {
while(!RI); // 等待接收完成
RI = 0;
return SBUF;
}
中断式接收:
c复制void UART_ISR() interrupt 4 {
if(RI) {
RI = 0;
unsigned char dat = SBUF;
// 处理接收到的数据
}
if(TI) {
TI = 0;
}
}
中断方式更适合实际应用,可以避免CPU空转等待数据。
5. 常见问题与调试技巧
5.1 通信不成功排查步骤
- 检查硬件连接:TX接RX,RX接TX,GND共地
- 确认波特率设置:双方必须完全一致
- 验证寄存器配置:特别是SCON和定时器1
- 检查中断配置:如果使用中断,确保ES和EA已使能
5.2 提高通信可靠性的技巧
- 在长距离通信时,适当降低波特率
- 添加简单的校验机制,如奇偶校验或累加和
- 为关键数据添加重发机制
- 在接收端实现超时检测,避免死等
5.3 实际项目中的经验
在做一个温湿度监测项目时,我发现UART通信偶尔会出错。经过排查,发现是电源不稳定导致的。后来在UART线上添加了上拉电阻,并在两端都加了0.1uF的滤波电容,问题就解决了。这个经验告诉我,通信问题不一定是软件原因,硬件环境也很重要。
另一个教训是关于数据接收的。有次我忘记及时读取SBUF,导致后续数据被覆盖。现在我会在中断服务程序中立即将数据存入缓冲区,然后再慢慢处理。
6. 进阶应用:多机通信
虽然初学者可能用不到,但了解多机通信原理很有必要。方式3配合SM2位可以实现硬件地址过滤:
- 主机发送地址帧(第9位为1)
- 从机SM2=1时,只有地址匹配的从机会响应
- 主机发送数据帧(第9位为0),目标从机SM2=0接收数据
这种机制大大简化了多机通信的软件设计。
7. 性能优化建议
- 对于高速通信,可以考虑使用SMOD=1加倍波特率
- 如果CPU负担重,尽量使用中断方式
- 发送大量数据时,可以先用数组缓存,再批量发送
- 关键数据可以添加重发机制
通过这篇文章,我希望能够帮助初学者避开我当年踩过的那些坑。UART看似简单,但真正掌握需要实践和经验的积累。建议大家在理解基本原理后,多动手实验,从简单的收发开始,逐步尝试更复杂的应用场景。
