单片机串口通信入门:手把手教你配置TMOD、SCON和SBUF寄存器(附代码)

张开发
2026/5/12 21:23:41 15 分钟阅读
单片机串口通信入门:手把手教你配置TMOD、SCON和SBUF寄存器(附代码)
单片机串口通信实战从寄存器配置到Hello World收发第一次接触单片机串口通信时看着那些晦涩的寄存器缩写——TMOD、SCON、SBUF是不是感觉头都大了别担心今天我们就用最直白的方式带你一步步实现单片机与电脑的Hello World通信。不同于枯燥的理论讲解我们将从一个实际项目出发让STC89C52单片机每秒向电脑串口助手发送一条问候信息。在这个过程中你会真正理解每个寄存器的配置意义避开那些新手常踩的坑。1. 硬件连接与开发环境准备在开始编程之前我们需要确保硬件连接正确。使用USB转TTL模块如CH340连接电脑和单片机时切记TX接RXRX接TX——这是新手最容易犯的错误之一。我的第一个串口项目就因为这个简单的接线问题调试了整整一个下午。开发环境方面Keil uVision是51单片机开发的主流选择。新建工程时需要注意选择正确的单片机型号如STC89C52设置晶振频率通常11.0592MHz这个特殊值后面会解释原因添加启动文件STARTUP.A51提示11.0592MHz的晶振不是随意选择的这个频率可以精确产生标准波特率避免通信误差。如果使用12MHz晶振常见的9600波特率会产生约8%的误差可能导致通信失败。串口助手软件推荐使用SSCOM或XCOM基本设置参数如下参数项推荐值波特率9600数据位8停止位1校验位无2. 定时器配置TMOD寄存器详解串口通信的波特率需要精确的时钟源我们使用定时器1作为波特率发生器。这就涉及到TMODTimer Mode寄存器的配置。这个8位寄存器被分为两部分高4位控制定时器1低4位控制定时器0。对于我们的串口通信项目需要将定时器1设置为模式28位自动重装。对应的TMOD配置值为TMOD 0x20; // 定时器1模式2定时器0不变这个赋值语句的二进制解析0010高4位定时器1设置GATE0仅由TR1控制C/T0定时器模式M11, M00模式20000低4位保持定时器0原有配置为什么选择模式2因为它的自动重装特性特别适合波特率生成TL1作为计数器TH1存储重装值计数溢出后自动从TH1重装无需软件干预3. 串口控制SCON寄存器逐位解析SCONSerial Control寄存器是串口通信的核心控制器它的每一位都有特定功能。让我们拆解这个8位寄存器SCON 0x50; // 常用配置值对应的位设置解析SM0和SM1位7和位6组合选择工作模式01模式18位UART波特率可变其他模式00同步移位寄存器109位UART固定波特率119位UART可变波特率SM2位5多机通信控制通常设为0单机通信设为1时需配合TB8/RB8使用REN位4接收使能1允许接收必须设置0禁止接收TB8位3发送的第9位模式2/3使用我们的模式1中不使用RB8位2接收的第9位模式2/3存储接收到的第9位模式1存储停止位TI位1发送中断标志发送完成后由硬件置1需要软件清零RI位0接收中断标志接收完成后由硬件置1需要软件清零注意TI和RI共用一个中断向量进入中断服务程序后需要通过判断这两个标志位来确定是发送还是接收中断。4. 波特率计算与TH1初始值设定波特率是串口通信的核心参数表示每秒传输的符号数。对于模式1波特率由定时器1的溢出率决定波特率 (2^SMOD / 32) × (定时器1溢出率)其中SMOD位于PCON寄存器通常默认为0。使用11.0592MHz晶振时9600波特率对应的TH1初始值计算如下定时器1模式2的溢出率 fosc / (12 × (256 - TH1))代入波特率公式9600 (1/32) × (11059200 / (12 × (256 - TH1)))解得TH1 2530xFD因此初始化代码为TH1 0xFD; // 9600波特率 11.0592MHz TL1 0xFD; // 初始值 TR1 1; // 启动定时器1常见波特率对应的TH1值波特率SMODTH1值十六进制24000F4h48000FAh96000FDh192001FDh576001FFh5. 数据收发实战SBUF寄存器的正确使用SBUFSerial Buffer是串口数据收发的核心寄存器虽然只有一个地址99H但物理上分为发送缓冲器和接收缓冲器。单片机通过不同的指令自动区分这两个缓冲区发送数据SBUF data;将数据写入发送缓冲器硬件自动开始串行发送接收数据data SBUF;从接收缓冲器读取数据实现Hello World发送的完整代码示例#include reg52.h #include string.h void UART_Init() { TMOD 0x20; // 定时器1模式2 TH1 0xFD; // 9600波特率 TL1 0xFD; SCON 0x50; // 模式1允许接收 TR1 1; // 启动定时器1 } void UART_SendByte(unsigned char dat) { SBUF dat; while(!TI); // 等待发送完成 TI 0; // 清除标志 } void UART_SendString(unsigned char *str) { while(*str) { UART_SendByte(*str); } } void main() { UART_Init(); while(1) { UART_SendString(Hello World\r\n); delay_ms(1000); // 简易延时 } }接收数据的处理通常通过中断实现void UART_ISR() interrupt 4 { if(RI) { // 接收中断 RI 0; // 清除标志 unsigned char rcv SBUF; // 处理接收到的数据 } if(TI) { // 发送中断 TI 0; // 清除标志 } }6. 常见问题与调试技巧在实际项目中串口通信可能会遇到各种问题。以下是几个典型故障及其解决方法接收乱码检查波特率是否匹配单片机与串口助手设置相同确认晶振频率是否为11.0592MHz验证TH1初始值计算是否正确无法接收数据检查REN位是否设置为1确认硬件连接TX-RX交叉测试USB转TTL模块是否正常发送不完整确保等待TI标志置位后再发送下一字节检查中断服务程序是否清除了TI标志调试时可以借助以下工具和技术逻辑分析仪观察实际波形串口助手发送测试数据分段测试先确保发送正常再调试接收7. 项目进阶自定义通信协议掌握了基础收发后可以设计简单的通信协议。例如实现一个控制LED的命令协议协议格式[头字节][命令][参数][校验和]示例代码框架#define HEADER 0xAA void ProcessCommand(unsigned char cmd, unsigned char param) { switch(cmd) { case 0x01: LED param; break; case 0x02: Buzzer param; break; // 其他命令... } } void UART_ProtocolHandler(unsigned char rcv) { static unsigned char state 0, cmd, param, checksum; switch(state) { case 0: if(rcv HEADER) state; break; case 1: cmd rcv; checksum rcv; state; break; case 2: param rcv; checksum rcv; state; break; case 3: if(rcv checksum) ProcessCommand(cmd, param); state 0; break; } }这种框架可以扩展为更复杂的工业通信协议如Modbus RTU等。关键在于明确的状态机设计完善的错误处理机制合理的超时管理

更多文章