第一次接触51单片机串口通信时,我被那一堆专业术语搞得晕头转向。后来才发现,串口通信就像两个人打电话一样简单。想象一下,单片机(MCU)和电脑(PC)就像两个需要交流的朋友,串口就是他们之间的电话线。
硬件连接上,你需要准备一个USB转串口模块(比如CH340G),这是连接单片机和电脑的桥梁。我常用的接法是:模块的TXD接单片机的P3.0(RXD),模块的RXD接单片机的P3.1(TXD),GND对接GND。这里有个容易踩的坑:千万别把TXD对TXD直接相连,就像打电话时不能把两个话筒对在一起,必须交叉连接才能正常通信。
关于通信参数,波特率就像通话时的语速。常见的有9600、19200等,新手建议先用9600bps。记得用STC-ISP软件里的波特率计算器生成初始化代码,能避免手动计算的错误。我刚开始调试时,因为波特率不匹配,接收到的全是乱码,折腾了半天才发现问题。
SCON寄存器就像串口的"大脑",我用示波器实测过各个位的功能。最常用的配置是模式1(8位UART),设置SM0=0、SM1=1。REN位特别重要——它像接电话的开关,设为1才能接收数据。TI和RI这两个标志位需要特别注意:必须用软件清零,我有次忘了清TI导致数据只发送一次就卡死。
PCON的最高位SMOD是波特率的"加速开关"。当SMOD=1时,波特率会翻倍。通过STC-ISP工具可以看到,使用11.0592MHz晶振时,9600波特率对应的定时器初值是0xFD。这里有个实用技巧:如果通信不稳定,可以尝试降低波特率或启用SMOD。
SBUF这个寄存器很特别——它实际上是两个寄存器共用同一个地址。发送时写入SBUF,数据进入发送缓冲区;读取SBUF时,数据来自接收缓冲区。我做过实验:如果在接收中断里直接修改SBUF,不会影响发送过程,这验证了它们的物理独立性。
完整的初始化需要5个步骤:
c复制void UartInit() {
PCON &= 0x7F; // 波特率不倍速
SCON = 0x50; // 模式1+允许接收
TMOD &= 0x0F; // 清除定时器1模式位
TMOD |= 0x20; // 设置定时器1为模式2
TH1 = 0xFD; // 9600bps@11.0592MHz
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
ES = 1; // 允许串口中断
EA = 1; // 开总中断
}
发送单个字节的函数看似简单,但藏着几个关键点:
c复制void SendByte(unsigned char dat) {
SBUF = dat;
while(!TI); // 等待发送完成
TI = 0; // 必须软件清零
}
实测发现,在9600波特率下发送一个字节约需1ms。如果发送数组,建议每字节间隔1.2ms以上,我用延时函数实现了稳定传输。
原始的中断服务程序有个隐患:没有缓冲区会导致数据丢失。我改进后的版本加入了环形缓冲区:
c复制#define BUF_SIZE 32
unsigned char xdata rxBuf[BUF_SIZE];
unsigned char bufHead = 0, bufTail = 0;
void UART_ISR() interrupt 4 {
if(RI) {
RI = 0;
rxBuf[bufHead++] = SBUF;
if(bufHead >= BUF_SIZE) bufHead = 0;
}
}
在主程序中通过检查bufHead != bufTail来判断是否有新数据。这种方法即使处理复杂协议也不会丢包,我在Modbus通信中验证过其可靠性。
用STC-ISP软件发送数据时,要注意选择正确的端口和波特率。我习惯发送16进制数据,比如发送"55 AA"测试通信。单片机收到后可以通过LED或返回数据确认:
c复制if(bufHead != bufTail) {
P1 = rxBuf[bufTail++]; // 用LED显示数据
if(bufTail >= BUF_SIZE) bufTail = 0;
}
调试输出功能时,建议先发送固定数据测试。比如循环发送0-9的数字:
c复制unsigned char i = 0;
while(1) {
SendByte(i+'0');
DelayMs(500);
if(++i > 9) i = 0;
}
在串口助手中应该看到连续的数字输出。如果出现乱码,首先检查波特率,再用逻辑分析仪抓取TXD引脚波形确认实际速率。
根据我的调试经验,这些问题最常见:
有个特别隐蔽的坑:某些USB转串口模块需要安装特定驱动。我有次换了电脑后通信失败,最后发现是驱动问题。