51单片机串口通信完全指南:从原理到实战(发送、接收、回环与控制)

串口通信是单片机与外部设备交互最常用的方式之一。无论是调试打印、固件烧录,还是控制继电器、连接蓝牙模块,都离不开它。本文基于STC89C52单片机,结合多个实战代码和硬件图解,带你彻底搞懂51单片机的串口通信------包括发送、接收、数据回环以及通过串口命令控制外设。


一、串口通信基础:UART是什么?

UART (Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种双向、串行、异步的通信总线,仅用一根数据接收线(RX)和一根数据发送线(TX)就能实现全双工通信------即同时收发数据。

如图"串口介绍"所示,一帧数据由起始位(低电平0)数据位(5~8位)奇偶校验位(可选)停止位(高电平1) 组成,之后跟随空闲位(高电平1)。收发双方必须约定相同的波特率、数据位、停止位和校验位才能正确通信。

1.1 常见电平标准

  • TTL电平:单片机直接输出。逻辑0为0V,逻辑1为5V(或3.3V)。通信距离短(几十米内),但无需转换芯片,如蓝牙模块、GPS模块。

  • RS-232电平:逻辑1为-5V~-15V,逻辑0为+5V~+15V。抗干扰强,传输距离可达30米。电脑的DB9串口就是RS-232,需要MAX232芯片转换。

  • RS-485:差分信号,传输距离上千米,适合工业现场。

如图"TTL电平"和"RS-232"所示,两者不能直连,必须通过电平转换。


二、51单片机串口硬件及引脚

STC89C52单片机有一个全双工串口(UART),通过P3.0(RXD)和P3.1(TXD)引脚收发数据。内部结构如"数据是怎么发送的?"图片所示:

  • 发送缓冲器SBUF(写操作) → 移位寄存器 → TXD引脚发送。

  • 接收移位寄存器 → 接收缓冲器SBUF(读操作) ← RXD引脚接收。

  • 两个SBUF地址相同(0x99),但物理上是独立的。


三、核心寄存器详解(代码中的配置)

初始化串口必须配置以下寄存器,对照图片"串行控制寄存器SCON"、"PCON"、"TMOD"。

3.1 SCON ------ 串行控制寄存器(可位寻址,地址0x98)

B7(SM0/FE) B6(SM1) B5(SM2) B4(REN) B3(TB8) B2(RB8) B1(TI) B0(RI)
0 1 0 1 0 0 0 0
  • SM0=0,SM1=1 :选择工作方式1(8位UART,波特率可变)。这也是最常用的方式。

  • REN=1:允许接收数据。

  • TI:发送中断标志。发送完一帧数据后硬件自动置1,必须软件清零。

  • RI:接收中断标志。接收完一帧数据后硬件自动置1,必须软件清零。

代码中的初始化:SCON = 0x50; → 二进制0101 0000,正是方式1 + 允许接收。

3.2 PCON ------ 电源控制寄存器(不可位寻址,地址0x87)

B7(SMOD) B6(SMOD0) ...
0 0
  • SMOD :波特率加倍控制位。PCON &= 0x7F; 将SMOD清零,使用不加倍模式。

3.3 TMOD ------ 定时器工作模式寄存器(地址0x89)

我们使用定时器1(T1)产生波特率时钟。

7(GATE) 6(C/T) 5(M1) 4(M0) 3~0(定时器0)
0 0 1 0 无关
  • M1=1,M0=0 :定时器1工作在8位自动重装模式(模式2)。

  • C/T=0:定时模式(计数内部时钟)。

代码:

cpp 复制代码
TMOD &= 0x0F;   // 清空高4位(定时器1部分)
TMOD |= 0x20;   // 设置M1=1, M0=0

3.4 TH1 / TL1 ------ 波特率初值

波特率计算公式(方式1,不加倍,12T模式):

cpp 复制代码
波特率 = (2^SMOD / 32) × (定时器1溢出率)
定时器1溢出率 = 系统时钟 / 12 / (256 - TH1)

对于11.0592MHz晶振,要得到9600bps,计算:

cpp 复制代码
9600 = (1/32) × (11.0592e6 / 12 / (256 - TH1))
=> 256 - TH1 = 11.0592e6 / 12 / 32 / 9600 ≈ 3
=> TH1 = 253 = 0xFD

代码:TH1 = 0xFD; TL1 = 0xFD; TL1初值任意,因为自动重装时TH1会重新加载到TL1。

对照图片"常用波特率与定时器/计数器1各参数关系",11.0592MHz、9600bps、SMOD=0时重装值0xFD。


四、串口初始化完整步骤(代码逐行解析)

cpp 复制代码
void main() {
    SCON = 0x50;    // 方式1,允许接收
    PCON &= 0x7F;   // 波特率不加倍
    TMOD &= 0x0F;   // 清空定时器1部分
    TMOD |= 0x20;   // 定时器1工作模式2(8位自动重装)
    TH1 = 0xFD;     // 初值高位
    TL1 = 0xFD;     // 初值低位(自动重装,写入任意值)
    TR1 = 1;        // 启动定时器1
    ES = 1;         // 使能串口中断
    EA = 1;         // 总中断使能
    // 然后进入主循环...
}

补充知识:如果不使用中断而是查询方式,可以不开ES和EA,但在接收数据时需不断检测RI标志。


五、发送数据:字节与字符串

5.1 发送一个字节

cpp 复制代码
void UART_Send_Byte(uchar send_byte) {
    SBUF = send_byte;   // 数据写入发送缓冲器,立即启动发送
    while(!TI);         // 等待TI变1(发送完成)
    TI = 0;             // 软件清标志
}

原理:写SBUF后,硬件自动将数据移位到TXD引脚,按波特率逐位发送。发送完一帧(包括停止位)后,硬件置位TI。

5.2 发送字符串

cpp 复制代码
void UART_Send_Str(uchar *send_str) {
    while(*send_str != '\0') {
        UART_Send_Byte(*send_str++);
    }
}

补充知识 :字符串以空字符\0结尾,例如"I am god!\r\n"中的\r\n是回车换行,使串口助手自动换行。

5.3 主循环中循环发送

cpp 复制代码
while(1) {
    UART_Send_Str("I am god!\r\n");
    Delay_xms(1000);
}

效果:串口助手每秒收到一次"I am god!"


六、接收数据:中断方式详解

串口接收使用中断4(interrupt 4)。当RXD引脚检测到完整的一帧后,RI被置1,触发中断。

cpp 复制代码
void UART_Routine(void) interrupt 4 {
    if(RI) {                // 判断是否为接收中断
        RI = 0;             // 软件清零
        recv = SBUF;        // 读取接收缓冲器中的字节
        // 根据 recv 执行相应操作(LED、蜂鸣器等)
    }
}

关键点

  • 必须软件清零RI,否则会不断进入中断。

  • 读取SBUF的动作会清除接收缓冲器,但不会影响后续接收(硬件有移位寄存器并行装入)。

  • 中断内处理要尽量简短,否则可能丢失后续数据。

6.1 命令控制外设(代码中的switch-case)

接收到的字节(十六进制0x01~0x09)控制不同外设:

命令 功能 代码
0x01 LED1点亮 LED1 = 0;
0x02 LED2点亮 LED2 = 0;
0x03 LED3点亮 LED3 = 0;
0x04 LED4点亮 LED4 = 0;
0x05 熄灭所有LED P1 |= 0x0F;
0x06 蜂鸣器响(低电平触发) BEEP = 0;
0x07 蜂鸣器停 BEEP = 1;
0x08 继电器吸合(低电平触发) JDQ1 = 0;
0x09 继电器断开 JDQ1 = 1;

补充知识 :为什么用P1 |= 0x0F熄灭LED?因为LED共阳极(低电平亮),所以置高电平熄灭。P1 |= 0x0F将P1的低4位置1,不影响高4位(蜂鸣器在P1.6)。


七、数据回环实验:接收即发送

最简单的回环代码:

cpp 复制代码
void UART_Routine(void) interrupt 4 {
    if(RI) {
        RI = 0;
        recv = SBUF;
        SBUF = recv;        // 立即发回
        while(!TI);
        TI = 0;
    }
}

用途:测试串口线路是否正常,或者作为简单的串口调试助手。实际项目中可用来验证数据是否被正确接收。


八、硬件接线与实验平台

8.1 单片机与电脑通信(USB转TTL)

需要一根USB转TTL模块(如CH340芯片),接线如下:

USB转TTL模块 单片机
TXD P3.0 (RXD)
RXD P3.1 (TXD)
GND GND
5V VCC (可选,若模块供电)

注意 :TX接RX,RX接TX------这就是交叉连接。如果模块上有黄色跳帽(用于切换电平),直接拔掉即可(如"实验三接线方式"图片所示)。

8.2 继电器模块控制220V设备

继电器模块(低电平触发)接线:

  • VCC → 单片机5V

  • GND → 单片机GND

  • IN → 单片机P2.0(JDQ1)

当P2.0输出低电平时,继电器吸合,常开端(NO)与公共端(COM)导通,可控制大电流设备。状态指示灯也会亮起(见"继电器"图片)。

8.3 蓝牙模块(JDY-31-SPP)

用于手机无线控制,接线如下:

  • VCC → 5V(必须5V供电)

  • GND → GND

  • TXD → P3.0(RXD)

  • RXD → P3.1(TXD)

同样交叉连接。手机需先配对(密码1234),然后打开蓝牙SPP助手,选择HEX发送命令(如01),单片机响应。注意:蓝牙模块连接时会占用串口,烧录程序时需拔掉蓝牙。

如图"蓝牙模块接线方式"所示。


九、常见问题与调试技巧

9.1 串口打开失败(设备打开失败)

  • 检查USB转TTL模块是否插好,驱动是否安装(设备管理器查看COM口)。

  • 关闭所有占用该COM口的软件(如多个串口助手、烧录软件)。

  • 检查接线是否正确,特别是GND必须共地。

9.2 收发乱码

  • 波特率不一致:确认晶振是11.0592MHz,代码中TH1=0xFD对应9600。若用12MHz晶振,9600bps误差较大,建议改用2400bps(TH1=0xF3)。

  • 电平不匹配:TTL与RS-232直连会烧芯片,必须转换。

  • 停止位/数据位设置错误:一般为1停止位、8数据位、无校验。

9.3 接收不到数据

  • 检查REN是否置1。

  • 中断是否开启(ES、EA)。

  • 发送方是否正确发送(比如蓝牙模块需配对成功)。

9.4 发送字符串时主循环卡死

如果发送函数中没有等待TI清零而连续发送,会丢失数据。必须等待while(!TI);


十、进阶知识补充(代码中没有但很重要)

10.1 ASCII与HEX的本质

  • ASCII :每个字符对应一个字节值,例如'a'=0x61。发送字符串时逐个发送字符的ASCII码。

  • HEX (十六进制):直接发送字节的原始值,例如发送0x01,对方收到的是二进制00000001,而不是字符'1'(0x31)。

在串口助手中,如果发送"01"(ASCII),实际发送0x30和0x31;如果选择HEX发送"01",则发送0x01。我们的命令代码使用HEX模式,所以串口助手必须选HEX发送,而接收区选ASCII才能看到"I am god!"字符串。

10.2 多机通信与SM2位

SCON的SM2位用于多机通信。当SM2=1时,只有接收到第9位数据为1(地址帧)时才置RI中断。这样一主多从系统中,从机可以只响应地址呼叫,节省CPU资源。我们的实验未使用此功能,SM2保持0。

10.3 中断优先级

串口中断的优先级默认较低(由IP寄存器设定)。如果在串口中断中处理复杂任务(如长延时),可能错过后续数据。一般做法:中断只读取数据存入缓冲区,主循环中处理。

10.4 数据发送的物理过程

如"数据是怎么发送的?"图片所示:

  • 写SBUF后,数据进入发送移位寄存器。

  • TXD引脚按波特率时钟逐位输出:起始位(0) → 数据位D0~D7(LSB先发) → 可选校验位 → 停止位(1)。

  • 接收方在波特率时钟的特定时刻采样RXD引脚,重建数据。


十一、总结与实验建议

通过本文,你应掌握了:

  • 51单片机串口初始化的完整配置(SCON、PCON、TMOD、TH1/TL1)。

  • 字节发送与字符串发送函数实现。

  • 中断接收及命令解析控制LED、蜂鸣器、继电器。

  • 数据回环测试方法。

  • 与USB转TTL、蓝牙模块、继电器模块的硬件接线。

  • 常见问题的排查思路。

实验建议

  1. 先使用回环程序验证串口硬件和驱动程序正常。

  2. 再尝试发送字符串"Hello 51"。

  3. 最后用手机蓝牙APP发送HEX命令控制单片机,实现无线遥控。

串口通信就像单片机的"嘴巴"和"耳朵",掌握了它,你的单片机就能与外部世界无障碍对话!


所有图片素材均来自实验过程截图,包括寄存器说明、接线图、串口助手界面、蓝牙模块、继电器等。实际操作时请对照图片反复检查接线,避免接反烧毁设备。

参考资料:STC89C52数据手册,实验过程等图片。

相关推荐
一路往蓝-Anbo9 小时前
第三篇:ADC 与模拟前端
stm32·嵌入式硬件·嵌入式·硬件设计
noipp10 小时前
推荐题目:洛谷 P10907 [蓝桥杯 2024 国 B] 蚂蚁开会
c语言·c++·算法·编程·洛谷
Net_Walke11 小时前
【Linux系统】静态链接库与动态链接库
linux·嵌入式硬件
努力小周13 小时前
STM32智能安防系统
c语言·stm32·单片机·嵌入式硬件·物联网·计算机网络·pcb工艺
华科大胡子14 小时前
在STM32上跑通TinyML
stm32·单片机·嵌入式硬件
x1387028595715 小时前
c语言中srtlen(指针使用计算字符长度)、传值和传址调用
c语言·开发语言·算法·visual studio
iCxhust16 小时前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
智者知已应修善业17 小时前
【51单片机2个外部中断显示中断历时,初始化8左移3位共阳数码管】2024-6-6
c++·经验分享·笔记·算法·51单片机
zhaoshuzhaoshu18 小时前
嵌入式开发之IIC接口详解-STM32
嵌入式硬件·软件工程