第一次接触单片机串口通信时,看着那些陌生的寄存器缩写——SCON、SBUF、PCON,是不是感觉头大?别担心,今天我们就用最直白的方式,把这些抽象的概念变成可操作的步骤。我依然记得自己第一次成功让单片机通过串口和电脑对话时的兴奋,那种"通了!"的成就感,现在想来依然记忆犹新。
串口通信就像两个人在用对讲机通话,需要约定好相同的语速(波特率)和对话规则(数据格式)。在51单片机中,这套规则主要由三个关键寄存器控制:
实际项目中,我遇到过不少初学者容易忽略的一个细节:TI和RI标志位的及时清除。这两个标志就像通信状态的指示灯,如果不及时手动清零,就像对讲机一直按着通话键不放,整个通信就会卡住。
提示:在Keil调试时,可以通过View->Serial Window->UART #1打开串口调试窗口,实时观察通信数据。
SCON的每一位都像是一个功能开关,我们需要重点关注这几个位:
| 位 | 名称 | 功能说明 | 典型设置 |
|---|---|---|---|
| SM0-SM1 | 工作模式 | 00=模式0, 01=模式1, 10=模式2, 11=模式3 | 通常设为01(模式1) |
| REN | 接收使能 | 1=允许接收, 0=禁止接收 | 必须设为1 |
| TI | 发送中断 | 发送完成自动置1,需手动清零 | 初始为0 |
| RI | 接收中断 | 接收完成自动置1,需手动清零 | 初始为0 |
c复制// 典型SCON配置示例
SCON = 0x50; // 01010000 -> 模式1,允许接收
记得去年指导一个学生做毕业设计,他死活调不通串口,最后发现是漏了REN=1这一行。没有打开接收使能,就像对讲机没开接收键,自然收不到任何消息。
SBUF的特殊之处在于它实际上是两个物理寄存器共用一个地址:
SBUF = data;时,数据进入发送队列data = SBUF;时,读取接收到的数据c复制// 发送数据示例
void UART_SendByte(unsigned char dat) {
SBUF = dat;
while(!TI); // 等待发送完成
TI = 0; // 必须手动清零!
}
PCON中只有SMOD位与串口相关:
c复制// 设置波特率加倍
PCON |= 0x80; // SMOD=1
需要准备:
连接方式:
注意:千万不要接反TXD和RXD,这是新手最容易犯的错误之一。我有次熬夜调试两小时,最后发现就是线接反了。
常用波特率对应的定时器初值:
| 波特率 | SMOD | TH1初值 | 实际误差 |
|---|---|---|---|
| 9600 | 0 | 0xFD | 0.16% |
| 9600 | 1 | 0xFA | 0.16% |
| 19200 | 1 | 0xFD | 0.16% |
c复制// 定时器1模式设置
TMOD &= 0x0F; // 清除高4位
TMOD |= 0x20; // 定时器1,模式2(8位自动重装)
TH1 = 0xFD; // 9600@11.0592MHz
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
c复制#include <reg52.h>
void UART_Init() {
PCON &= 0x7F; // SMOD=0
SCON = 0x50; // 模式1,允许接收
TMOD &= 0x0F; // 清除定时器1模式位
TMOD |= 0x20; // 定时器1,模式2
TH1 = 0xFD; // 9600波特率
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
EA = 1; // 开总中断
ES = 1; // 开串口中断
}
void UART_SendByte(unsigned char dat) {
SBUF = dat;
while(!TI);
TI = 0;
}
void UART_SendString(unsigned char *str) {
while(*str != '\0') {
UART_SendByte(*str++);
}
}
void UART_Interrupt() interrupt 4 {
if(RI) {
RI = 0; // 清除接收中断标志
// 处理接收到的数据(SBUF)
}
if(TI) {
TI = 0; // 清除发送中断标志
}
}
void main() {
UART_Init();
while(1) {
UART_SendString("Hello World!\r\n");
delay_ms(1000);
}
}
STC-ISP软件中的串口助手功能非常实用:
收不到数据:
数据乱码:
通信不稳定:
有一次帮同事排查问题,发现他的开发板晶振实际是12MHz,但代码按11.0592MHz配置,导致波特率偏差达到7%,这就是典型的数据乱码原因。