news 2026/4/16 13:05:50

51单片机串口通信实验:零基础实现数据收发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
51单片机串口通信实验:零基础实现数据收发

51单片机串口通信实战:从点亮“Hello World”到全双工收发

你有没有过这样的经历?写好一段代码,烧录进单片机,然后……盯着几个LED灯猜:“它到底运行到哪一步了?”
没有反馈的开发,就像在黑暗中走路。而串口通信,就是嵌入式世界里那束最亮的手电筒光。

今天,我们就用最经典的51单片机,亲手实现一个完整的串口收发实验——让你的单片机学会“说话”,也能“听话”。零基础起步,全程不跳步,带你打通从寄存器配置到PC端双向通信的完整链路。


为什么是UART?为什么是51?

在SPI、I2C、USB满天飞的今天,我们为何还要学UART?尤其是早已“年过四十”的51单片机?

答案很简单:因为它足够简单,也足够真实

  • UART不需要时钟线,两根线(TXD、RXD)就能传数据;
  • 它不依赖复杂的协议栈,一帧数据清清楚楚:起始位 + 数据位 + 停止位;
  • 51单片机结构清晰,寄存器直观,适合理解“CPU如何控制外设”。

更重要的是——几乎所有现代MCU都保留了UART接口,STM32有,ESP32有,甚至树莓派Pico也有。你现在学的底层逻辑,未来全都能用上。

而且,当你调试一个跑飞的RTOS系统时,最终救命的,往往还是那一行通过串口打印出来的printf("Task Running...\n")

所以,别小看这个“老古董”,它是通往嵌入式核心世界的第一把钥匙


UART是怎么把字节变成高低电平的?

我们先来拆解一个问题:你在电脑上敲下字母'A',它是怎么一步步变成单片机IO口上的0和1的?

一帧数据长什么样?

UART采用异步串行通信,意思是发送和接收双方没有共用的时钟线,全靠事先约定好的速度(波特率)来同步。

以最常见的8-N-1 模式为例(8位数据、无校验、1位停止),传输一个字节'A'(ASCII码 0x41)的过程如下:

低 高高高低低低低高 高 ↓ ↓↓↓↓↓↓↓↓ ↓ [起始位] [D0 D1 D2 D3 D4 D5 D6 D7] [停止位] (1 0 0 0 0 0 1 0) ← 注意:低位先行!

也就是说,0x41的二进制是01000001,但发送时要从最低位开始,所以实际波形是:

起始(0) → 1 → 0 → 0 → 0 → 0 → 0 → 1 → 0 → 停止(1)

总共10个比特,耗时 = 10 / 波特率。比如9600bps,每帧约1.04ms。

为什么选 11.0592MHz 晶振?

你可能注意到很多51开发板都用11.0592MHz而不是更常见的12MHz。这是为啥?

因为——要精准生成标准波特率

51的UART依赖定时器1产生波特率,计算公式为:

波特率 = (2^SMOD / 32) × (fosc / (12 × (256 - TH1)))

如果我们用12MHz晶振算9600bps,会发现根本得不到整数初值,误差高达8.5%,极易丢包。

而换成11.0592MHz,配合 SMOD=1(波特率加倍),TH1=0xFD(即-3),刚好得到精确的9600bps,误差接近0。

这就是工程中的取舍:为了通信稳定,宁愿牺牲一点点运行速度。


让51单片机“开口说话”:寄存器级配置详解

现在,轮到我们的主角登场了——AT89C51内部的UART模块。

它有四个关键角色:

寄存器地址功能
SBUF0x99串行数据缓冲器,读写都走它
SCON0x98控制工作模式、启动接收、查看状态
TMOD/TH1/TL10x89, 0xF0定时器1设置,决定波特率
IE0xA8中断使能开关

第一步:选工作模式 —— 我们要用 Mode 1

SCON 寄存器决定了UART的工作方式。其中 SM0 和 SM1 组合如下:

SM1SM0模式说明
000同步移位模式(少见)
011✅ 8位UART,可变波特率(常用)
1029位UART,固定波特率
1139位UART,可变波特率

我们要的就是Mode 1,所以设置 SCON = 0x50:

SM0=0, SM1=1 → Mode 1 REN=1 → 允许接收(第4位)

即:0101 0000=0x50

第二步:让定时器1当“节拍器”

UART需要一个稳定的“心跳”来逐位采样,这个任务交给定时器1,工作在8位自动重载模式(TMOD=0x20)

配置步骤:

TMOD |= 0x20; // 定时器1,模式2(自动重载) TH1 = 0xFD; // 初值FDH → 对应9600bps TL1 = 0xFD; TR1 = 1; // 启动定时器

别忘了开启波特率加倍:

PCON |= 0x80; // SMOD = 1,提升精度

第三步:打开中断,让CPU“听得见”

如果不使用中断,主程序就得一直轮询 TI/RI 标志,效率极低。

我们可以开启串口中断,一旦收到数据或发送完成,自动跳转处理:

ES = 1; // 使能串口中断 EA = 1; // 开总中断

这样,每当 RI 或 TI 置位,就会触发中断服务函数。


实战代码:实现“你说啥我回啥”的回环测试

下面这段代码,是你能跑通的第一个真正意义上的嵌入式通信程序。

#include <reg51.h> void UART_Init(void); void UART_SendByte(unsigned char byte); void UART_SendString(char *str); /** * 串口初始化:波特率9600, 8-N-1, 开中断 */ void UART_Init() { TMOD |= 0x20; // 定时器1,模式2 PCON |= 0x80; // SMOD = 1 TH1 = 0xFD; // 9600bps @ 11.0592MHz TL1 = 0xFD; TR1 = 1; // 启动定时器 SCON = 0x50; // Mode 1, REN=1 ES = 1; // 使能串口中断 EA = 1; // 开全局中断 } /** * 发送一个字节 */ void UART_SendByte(unsigned char byte) { SBUF = byte; // 写SBUF启动发送 while (!TI); // 等待发送完成 TI = 0; // 手动清TI标志 } /** * 发送字符串 */ void UART_SendString(char *str) { while (*str) { UART_SendByte(*str++); } } /** * 串口中断服务程序 */ void UART_ISR() interrupt 4 { if (RI) { // 是否接收到数据? unsigned char dat = SBUF; UART_SendByte(dat); // 回传原数据(回显) RI = 0; // 清接收标志 } if (TI) { TI = 0; // 清发送标志(若用中断发) } } /** * 主函数 */ void main() { UART_Init(); UART_SendString("Serial Test Ready!\r\n"); while (1) { // 主循环可做其他事 } }

关键点解析:

  • SBUF是个“神奇”的寄存器:写它=发数据,读它=收数据
  • TI标志表示“上一字节已发送完毕”,必须手动清零。
  • RI表示“已收到一帧数据”,同样需软件清除。
  • 中断号4是51单片机为串口分配的固定中断向量。

烧录后,打开串口助手(如XCOM、SSCOM),输入任意字符,你会看到单片机立刻回传相同内容——恭喜,你们“通上话了”!


怎么连上电脑?TTL、RS232、USB 转换全解析

你以为TXD-RXD连上线就能通信?错!还有一个致命问题:电平不兼容

设备电平标准高电平低电平
51单片机TTL~5V~0V
PC传统串口RS232-12V左右+12V左右

直接连接轻则通信失败,重则烧毁芯片。

解法一:用 MAX232 做电平转换

MAX232 是经典方案,内部有电荷泵电路,能把+5V升压成±12V,完美适配RS232。

典型接法:

单片机 TXD → MAX232 T1IN MAX232 T1OUT → PC RXD 单片机 RXD ← MAX232 R1OUT MAX232 R1IN ← PC TXD GND ↔ GND (必须共地!)

但注意:现在大多数PC都没有DB9串口了。

解法二:USB转TTL模块(推荐新手)

直接使用CH340、CP2102 或 FT232RL模块,它们将USB信号转为TTL电平,插上电脑就识别为虚拟COM口。

接线极其简单:

[USB-TTL模块] ↔ [51单片机] TXD → RXD RXD ← TXD GND ↔ GND

无需额外电源,即插即用,成本不到10元,强烈推荐初学者使用。


常见坑点与调试秘籍

别以为代码一烧就通,以下是新手最容易踩的五个坑:

❌ 坑1:线接反了!

记住口诀:“收对发,发对收”

单片机 TXD → 接 USB模块 RXD
单片机 RXD ← 接 USB模块 TXD

交叉接!千万别直连。

❌ 坑2:忘了共地

两个设备必须共享同一个GND参考点,否则电压基准不同,信号全乱套。

❌ 坑3:波特率不一致

PC端串口工具设置的波特率必须和单片机完全一样(9600、19200等)。建议首次测试用9600,容错性最好。

❌ 坑4:晶振不对导致波特率偏差

如果你用了12MHz晶振却按11.0592MHz算TH1值,实际波特率偏差太大,必然出错。检查你的开发板晶振型号!

❌ 坑5:中断未开启或标志未清

如果用了中断但没开ESEA,程序不会进入中断函数;如果不清RI,下次中断不会再触发。


还能怎么玩?扩展思路给你灵感

搞定基础通信后,你可以尝试这些升级玩法:

🔹 加个LED,用串口命令控制开关

if (dat == '1') P1_0 = 0; // 开灯 if (dat == '0') P1_0 = 1; // 关灯

从此你的单片机有了“远程遥控”。

🔹 上报传感器数据

接个DS18B20温度传感器,每隔5秒主动上报一次:

while (1) { float temp = ReadTemp(); printf("Temperature: %.2f°C\r\n", temp); DelayMs(5000); }

数据传到PC,可以用Python绘制成曲线图。

🔹 实现简易Modbus通信

定义自己的命令协议,比如:

  • 0x01→ 读IO状态
  • 0x02→ 设置继电器
  • 0x03→ 获取版本号

为将来学习工业通信打基础。

🔹 结合LCD屏做本地显示

一边串口上传数据给PC,一边在本地OLED/LCD上显示,构建完整的人机交互系统。


写在最后:这不是终点,而是起点

也许你觉得51单片机太老,不够“酷”。但请相信我,每一个优秀的嵌入式工程师,都曾在一个晚上,盯着串口助手等待第一个字符返回时,心跳加速过

这个实验教会你的不只是UART通信,更是:

  • 如何阅读数据手册
  • 如何配置寄存器
  • 如何处理中断
  • 如何排查硬件连接问题
  • 如何建立“软硬协同”的系统思维

这些能力,不会因为你换了STM32或RISC-V而失效。

当你有一天面对一块全新的MCU,即使没有HAL库,你也能从头写出串口驱动——因为你已经知道,那个叫SBUF的寄存器背后,藏着怎样的故事。

所以,别急着追求“高级”,先把这第一课练熟。

拿起你的开发板,焊好线路,烧录代码,然后深呼吸,按下复位键……

等那一句"Serial Test Ready!"出现在屏幕上时,你就正式踏入嵌入式的大门了。

欢迎加入这个用代码操控物理世界的游戏。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 10:13:24

从零实现Keil正确配置toolkit路径

如何一劳永逸解决 Keil 的c9511e编译器路径错误&#xff1f;——深入剖析 ARM 工具链配置的本质你有没有在打开一个旧项目、换了一台新电脑&#xff0c;或者刚装完 Keil 后&#xff0c;点击“编译”按钮却只看到这样一行红字&#xff1a;error: c9511e: unable to determine th…

作者头像 李华
网站建设 2026/4/16 10:17:03

基于STM32CubeMX的PLC开发完整指南

从零构建软PLC&#xff1a;基于STM32CubeMX的工业控制开发实战 你有没有遇到过这样的场景&#xff1f;客户要一个小型自动化控制器&#xff0c;功能不复杂&#xff0c;但商用PLC太贵、体积太大、还不能定制逻辑。这时候&#xff0c;如果能用一颗STM32芯片自己“造”一个PLC&…

作者头像 李华
网站建设 2026/4/16 10:21:44

嵌入式环境下堆溢出导致crash的系统学习

堆溢出为何让嵌入式系统“猝死”&#xff1f;一次 HardFault 背后的真相你有没有遇到过这样的场景&#xff1a;设备在实验室跑得好好的&#xff0c;一到现场却隔三差五重启&#xff1b;调试器抓到的调用栈停在free()里&#xff0c;但代码里明明没写错&#xff1b;翻遍逻辑也找不…

作者头像 李华
网站建设 2026/4/9 23:35:26

强化学习算法

摘要&#xff1a;强化学习算法是一类通过环境交互优化决策的机器学习方法&#xff0c;分为基于模型和无模型两种类型。基于模型算法&#xff08;如动态规划、蒙特卡洛树搜索&#xff09;先构建环境模型进行预测&#xff0c;具有较高样本效率但计算复杂&#xff1b;无模型算法&a…

作者头像 李华
网站建设 2026/4/16 10:22:13

STM32CubeMX打不开:端口或服务占用的深度讲解

STM32CubeMX打不开&#xff1f;别急&#xff0c;可能是这个端口被“劫持”了&#xff01;你有没有遇到过这样的场景&#xff1a;刚打开电脑&#xff0c;兴致勃勃准备配置一个STM32项目&#xff0c;双击STM32CubeMX图标——结果……没反应&#xff1f;或者闪一下就没了&#xff…

作者头像 李华
网站建设 2026/4/15 10:26:02

51单片机点亮一个led灯的抗干扰操作指南

从点亮一颗LED开始&#xff1a;51单片机抗干扰设计的工程实战课你有没有遇到过这种情况——代码写得没错&#xff0c;电路也照着原理图连了&#xff0c;可LED就是不听话&#xff1a;时亮时不亮、微亮、闪烁频率乱跳&#xff0c;甚至单片机莫名其妙复位&#xff1f;别急&#xf…

作者头像 李华