当前位置: 首页 > news >正文

Day44 51单片机UART串行通信 软件模拟UART + 硬件UART回显

day44 51单片机UART串行通信 软件模拟UART + 硬件UART回显


📌 项目目标

通过两个完整工程,掌握 51单片机串行通信的两种实现方式

  1. 软件模拟UART —— 利用定时器+GPIO精确控制时序,模拟串口协议发送数据;
  2. 硬件UART回显系统 —— 使用51内置串口控制器,实现接收-发送闭环验证。

两者均通过 STC-ISP串口助手 验证通信结果,是嵌入式开发中串口调试的核心技能。


🧩 工程一:软件模拟UART发送字符串 “hello”

核心思想:用定时器中断实现精确位延时,通过P3.1引脚逐位模拟UART协议发送ASCII字符。

#include <reg51.h>  // 引入标准51单片机寄存器定义文件sbit UART_TXD = P3^1;  // 定义P3.1引脚为UART数据发送端口,用于输出串行数据(硬件TXD引脚,兼容外部串口设备)// 延时函数:通过双重循环结构实现基础延时,参数i控制延时长度(非精确,用于主循环节奏控制)
void delay(unsigned int i)
{unsigned int num = i;   // 将输入参数i暂存至局部变量numint j = 0;              // 外层循环计数器for(j = 0; j < 10; j++) // 外层循环执行10次{num = i;            // 每轮内层循环前重载初始值while(num--);       // 内层空循环,通过递减计数实现延时(粗略毫秒级)}
}unsigned char delay_flag = 0;  // 全局延时完成标志位,由定时器中断置位(用于精确微秒级等待)// 定时器0初始化函数:配置为模式1(16位定时器),并开启中断(用于生成精确位时间)
void timer0_init(void)
{unsigned char mod = 0;           // 临时变量,用于修改TMOD寄存器mod = TMOD;                      // 读取当前TMOD值mod &= ~(0xf << 0);              // 清除定时器0的模式控制位(低4位)mod |= (0x1 << 0);               // 设置定时器0为模式1(16位定时器)TMOD = mod;                      // 写回配置值ET0 = 1;                         // 使能定时器0中断EA = 1;                          // 开启总中断允许(全局中断开关)
}// UART位延时函数:通过定时器0中断实现固定时间间隔(约50μs,对应600bps波特率)
void uart_delay(void)
{unsigned char mod = 0;           // 保留局部变量定义(未使用,兼容性保留)delay_flag = 0;                  // 清除延时标志,准备等待中断触发TH0 = (65536 - 760) / 256 ;      // 装载定时器0高8位初值(机器周期=1μs,760周期≈760μs,实际用于600bps≈1667μs/位,此处为简化演示)TL0 = (65536 - 760) % 256 ;      // 装载定时器0低8位初值TR0 = 1;                         // 启动定时器0while(!delay_flag);              // 等待中断服务程序将标志位置1(阻塞等待,实现精确延时)
}// UART数据发送函数:支持无校验、偶校验、奇校验三种模式
// 参数说明:dat = 待发送字节,check = 0(无校验)、1(偶校验)、2(奇校验)
void uart_send(unsigned char dat, unsigned char check)
{unsigned char i = 0;             // 位计数器,用于遍历8位数据unsigned char num = 0;           // 统计数据中“1”的位数,用于校验计算UART_TXD = 1;                    // 空闲状态,输出高电平(线路空闲)UART_TXD = 0;                    // 发送起始位(低电平,通知接收方开始接收)uart_delay();                    // 延时,保持起始位宽度(约1位时间)uart_delay();                    // 二次延时,确保位宽稳定(增强抗干扰)// 逐位发送数据(从bit0到bit7,低位在前,符合UART标准)for(i = 0; i < 8; i++){if(dat & (0x1 << i))         // 判断当前位是否为1(位测试){UART_TXD = 1;            // 发送高电平num++;                   // 记录“1”的个数(用于后续校验位计算)}else{UART_TXD = 0;            // 发送低电平}uart_delay();                // 保持当前位电平宽度(1位时间)uart_delay();                // 二次延时,确保稳定(增强容错)}// 校验位发送(根据check参数选择校验方式,本工程实际使用无校验)if(check == 1)                   // 偶校验模式{if(0 == (num % 2))           // 若“1”的个数为偶数UART_TXD = 1;            // 发送高电平作为校验位elseUART_TXD = 0;            // 发送低电平作为校验位uart_delay();                // 校验位延时uart_delay();                // 二次延时}else if(check == 2)              // 奇校验模式{if(num % 2)                  // 若“1”的个数为奇数UART_TXD = 1;            // 发送高电平作为校验位elseUART_TXD = 0;            // 发送低电平作为校验位uart_delay();                // 校验位延时uart_delay();                // 二次延时}UART_TXD = 1;                    // 发送停止位(高电平,标志帧结束)uart_delay();                    // 停止位延时uart_delay();                    // 二次延时,确保帧结束稳定(接收方采样窗口)
}// 主程序入口
void main(void)
{unsigned char dat = 0;           // 保留变量,未使用(兼容性保留)char buf[10] = {"hello"};        // 定义待发送字符串缓冲区(含终止符\0,共6字节)unsigned char i = 0;             // 循环索引变量P2 = 0x55;                       // 初始化P2口输出模式(01010101,用于LED状态指示)timer0_init();                   // 初始化定时器0,用于位延时(波特率时基)while(1)                         // 主循环:持续发送数据(无限循环){for(i = 0; i < 5; i++)       // 依次发送"hello"中的5个字符(跳过\0){uart_send(buf[i], 0);    // 调用发送函数,无校验模式(check=0)}delay(1000);                 // 主循环延时,控制发送节奏(约几毫秒,防止发送过快)P2 = ~P2;                    // 翻转P2口电平,指示程序运行状态(LED闪烁)}
}// 定时器0中断服务函数(中断号1)
void timer0_handler(void) interrupt 1
{delay_flag = 1;                  // 中断触发,置位延时完成标志(唤醒uart_delay中的while循环)
}

工程一运行理想结果

单片机通过 P3.1 引脚持续发送字符串 “hello”,串口助手接收到重复的 “hello” 数据流,并在接收缓冲区中显示为连续的 “hellohellohello…” 字符串。

📊 具体表现:
  • 波特率:约600bps(由定时器初值760推算,实际位宽≈1667μs);
  • 帧结构:1起始位 + 8数据位 + 0校验位 + 1停止位 = 10位/字符;
  • 发送内容:每轮循环发送5字节:'h' 'e' 'l' 'l' 'o'
  • 接收显示:串口助手显示连续 hellohellohello...(无换行符);
  • 状态指示:P2口LED以固定频率闪烁(每发送完一次"hello"翻转一次);
  • 调试验证:STC-ISP串口助手“操作成功”,接收字节数持续增长(如563字节)。

🧩 工程二:硬件UART串口回显系统(9600bps)

核心思想:使用51内置串口控制器,配置定时器1为波特率发生器,实现接收即回显功能,验证串口通路。

/** 功能    : STC89C5x 最小串口回显工程* 硬件    : 11.0592 MHz 晶振 → 9600 bps(工业标准晶振,精确匹配常用波特率)*/#include <reg51.h>/*------- 引脚定义 -------*/
sbit UART_TXD = P3^1;          // 模拟 UART 发送脚(本工程未使用模拟发送,保留兼容性定义)/*------- 软件延时函数 -------*/
// 粗略延时,主频 11.0592 MHz 下约 1 ms@i≈120(用于超时等待)
void delay(unsigned int i)
{unsigned int num = i;int j = 0;for(j = 0; j < 10; j++){num = i;while(num--);}
}/*------- 定时器 0 相关 -------*/
unsigned char delay_flag = 0;  // 1:表示 50 µs 到点(uart_delay 用,为软件UART预留时基)/* 定时器 0 初始化:模式 1(16 位定时),用于产生 50 µs 基准(本工程未用于串口,为扩展预留) */
void timer0_init(void)
{unsigned char mod = 0;mod = TMOD;         // 读原寄存器mod &= ~(0xf << 0); // 清 T0 位(定时器0控制位)mod |= (0x1 << 0);  // 设 T0 为模式 1(16位定时器)TMOD = mod;ET0 = 1;            // 允许 T0 中断(使能定时器0中断)EA  = 1;            // 开总中断(全局中断允许)
}/* 阻塞 50 µs(9600 bps 一位时间≈104μs,此处760周期≈760μs,仅为示例占位) */
void uart_delay(void)
{delay_flag = 0;TH0 = (65536 - 760) / 256;   // 760 个机器周期≈760 µs(12MHz晶振下)TL0 = (65536 - 760) % 256;TR0 = 1;                     // 启动定时器0while(!delay_flag);          // 等待中断置位(阻塞等待)
}/*------- UART 底层收发 -------*/
// 发送 1 字节(查询 TI)—— 使用硬件串口发送寄存器SBUF
void uart_send_byte(unsigned char dat)
{TI = 0;         // 清发送标志(必须手动清除,硬件不自动清零)SBUF = dat;     // 写入发送缓冲寄存器,硬件自动添加起始位、停止位while(!TI);     // 等待发送完成标志置位(查询方式,阻塞等待)
}// 接收 1 字节(带 2 ms 超时)—— 使用硬件串口接收寄存器SBUF
// 返回 0 成功,-1 超时(防止程序卡死在等待接收)
char uart_recv_byte(unsigned char * dat)
{unsigned char num = 2;          // 约 2 ms 超时(调用delay()约2次)while((!RI) && num--)           // RI=0表示未收到,等待或超时{delay();    // 1 ms 级延时(粗略延时函数)}if(num < 0)                     // 超时判断(num递减至负数)return -1;  // 超时返回错误码*dat = SBUF;    // 从接收缓冲寄存器读取数据RI = 0;         // 清接收标志(必须手动清除)return 0;       // 成功返回0
}/*------- UART 初始化 -------*/
// 9600 bps @11.0592 MHz,8-N-1,允许接收(标准配置)
void uart_init(void)
{unsigned char mod = 0;mod = TMOD;SM0 = 0;        // 串口模式0:8位UART(方式1)SM1 = 1;        // 串口模式1:8位UART(方式1)REN = 1;        // 允许接收(接收使能)PCON &= ~(1 << 7); // SMOD=0,波特率不倍速(SMOD位清零)mod &= ~(0xf << 4); // 清 T1 位(定时器1控制位)mod |= (0x2 << 4);  // T1 模式 2(8 位自动重装,用作波特率发生器)TMOD = mod;TH1 = 0xfd;     // 9600 bps 初值(11.0592MHz晶振下标准值)TL1 = 0xfd;     // 自动重装值 = TH1TR1 = 1;        // 启动 T1(启动波特率发生器)
}/*------- 主函数 -------*/
void main(void)
{unsigned char dat = 0;          // 接收数据暂存变量char ret = 0;                   // 接收函数返回值(0成功,-1超时)char buf[10] = {"hello\n\r"};   // 仅占位,未使用(兼容性保留)unsigned char i = 0;            // 仅占位,未使用P2 = 0x55;                      // 初始 LED 状态(01010101)timer0_init();                  // 初始化 50 µs 时基(为软件UART预留)uart_init();                    // 初始化硬件串口(核心配置)while(1){ret = uart_recv_byte(&dat); // 尝试接收1字节(带超时)if(ret != -1)               // 若接收成功(未超时)uart_send_byte(dat);    // 立即回显该字节(原样发送回去)P2 = ~P2;                   // 指示系统运行(每轮循环翻转P2,LED闪烁)}
}/*------- 中断服务 -------*/
// 定时器 0 溢出:50 µs 到点(本工程未用于串口,为软件UART扩展预留)
void timer0_handler(void) interrupt 1
{delay_flag = 1;     // 唤醒 uart_delay(置位标志,解除阻塞)
}

工程二运行理想结果

单片机进入“回显模式”:通过串口助手发送任意字符(如 ‘A’),单片机立即原样返回该字符,实现“你说什么,我回什么”的闭环通信验证。

📊 具体表现:
  • 波特率:9600bps(精确匹配11.0592MHz晶振);
  • 帧格式:8数据位、无校验、1停止位(8-N-1);
  • 发送方式:查询TI标志,阻塞等待发送完成;
  • 接收方式:查询RI标志,带2ms超时防卡死;
  • 功能表现
    • 串口助手发送 “Hello!” → 单片机回显 “Hello!”;
    • 发送 “123” → 回显 “123”;
    • 无输入时,程序不阻塞,P2口持续闪烁;
  • 状态指示:P2口LED持续闪烁,证明主循环正常运行;
  • 调试意义:最小闭环系统,验证串口收发硬件功能正常。

📚 知识体系归纳(双工程对比 + 核心概念)

知识模块软件模拟UART(工程一)硬件UART(工程二)通用知识点说明
波特率产生定时器0中断 + uart_delay() 模拟位时间定时器1模式2自动重装,TH1=0xFD → 9600bps波特率 = 1 / 位时间;低速容忍误差大,高速需精确晶振
帧结构1起始 + 8数据 + [校验] + 1停止(可配奇偶校验)1起始 + 8数据 + 0校验 + 1停止(8-N-1)标准异步串行帧,接收方靠起始位同步
发送机制GPIO逐位翻转 + 精确延时写SBUF → 硬件自动加起止位 → 等待TI置位硬件发送更稳定高效;软件发送灵活但占资源
接收机制本工程未实现接收查询RI → 读SBUF → 清RI,带超时机制接收通常用中断更高效,本工程用查询防卡死
校验支持支持无/奇/偶校验(通过check参数)无校验(SM0=0, SM1=1固定8-N-1)工业环境常用奇偶校验增强可靠性
延时实现定时器0中断置标志 + while等待(精确微秒级)主循环用粗略delay();串口位时由硬件自动控制精确延时必须用定时器;粗略延时可用循环
运行指示P2 = ~P2 每发送完"hello"翻转一次P2 = ~P2 每轮主循环翻转一次(持续闪烁)LED状态指示是嵌入式调试基础手段
调试工具STC-ISP串口助手(支持低速600bps)通用串口助手(9600bps通用波特率)STC-ISP对低速支持更好,通用助手需标准波特率
适用场景无硬件串口、低速通信、教学演示标准产品开发、高速稳定通信软件UART是应急/学习方案,硬件UART是生产方案

🎯 核心知识点一句话总结

通过定时器中断精确控制GPIO电平翻转可软件模拟UART协议实现低速串行通信,而利用51单片机内置串口控制器配合定时器1波特率发生器可实现高效稳定的硬件UART收发,两者结合掌握串行通信底层原理与工程实践,是嵌入式开发的必备技能。


🧭 串行口通信四要素(通用)

  1. 波特率(Baud Rate) —— 每秒传输的位数(如9600bps),决定通信速度;
  2. 数据位(Data Bits) —— 每帧有效数据长度(通常8位);
  3. 校验位(Parity Bit) —— 奇校验/偶校验/无校验,用于简单错误检测;
  4. 停止位(Stop Bit) —— 帧结束标志(通常1位);
  5. 收发数据操作方法 —— 初始化 → 发送(uart_send)→ 接收(uart_recv)→ 处理。

✅ 最终运行效果总结

  • 工程一(软件UART):持续发送“hello”,串口助手显示“hellohellohello…”,P2口周期闪烁;
  • 工程二(硬件UART回显):发送任意字符立即原样返回,实现“你说我复读”,P2口持续闪烁;
  • 共同点:均通过STC-ISP或通用串口助手验证,P2口LED证明程序存活,定时器中断机制确保时序精确;
  • 教学价值:从零构建通信协议(软件)→ 使用硬件外设(硬件),完整覆盖串口通信知识体系。

文章转载自:

http://mpJUO9yw.yrxcn.cn
http://iLUrOOUM.yrxcn.cn
http://bhJA5eJU.yrxcn.cn
http://D4ThsKwY.yrxcn.cn
http://JlILUalD.yrxcn.cn
http://Q5sligRR.yrxcn.cn
http://ZyoMIQEm.yrxcn.cn
http://igPtUpEm.yrxcn.cn
http://UdJ72Lm9.yrxcn.cn
http://I0ZqY5Qu.yrxcn.cn
http://XIYmEo5X.yrxcn.cn
http://o4b4NK9Z.yrxcn.cn
http://Om52Jjbe.yrxcn.cn
http://bFn5D6cN.yrxcn.cn
http://GfAuysmE.yrxcn.cn
http://BOZOrNTS.yrxcn.cn
http://8NBhBnKs.yrxcn.cn
http://mOoMpy13.yrxcn.cn
http://4JMKzzJY.yrxcn.cn
http://4ch6G0KK.yrxcn.cn
http://nuLwDobw.yrxcn.cn
http://WrMf48lD.yrxcn.cn
http://6vfBCj0P.yrxcn.cn
http://AY6V1OFp.yrxcn.cn
http://4ICRNG5x.yrxcn.cn
http://5KrF4AIs.yrxcn.cn
http://bo6jJza3.yrxcn.cn
http://wAEoCNS3.yrxcn.cn
http://fNrbyvnF.yrxcn.cn
http://PfGvuvPO.yrxcn.cn
http://www.dtcms.com/a/388109.html

相关文章:

  • Freertos系列(调度机制与创建任务)
  • 深度学习(二)
  • 搭建node脚手架(六) ESLint 功能模块
  • mysql面试(2)
  • Linux系统DNS服务
  • 如何通过跳板机访问内网 Mysql 服务器
  • SSH 远程连接内网 Linux 服务器
  • Spring Cloud - 微服务监控
  • Flutter-[1]入门指导
  • Linux服务器运维自动化巡检工具
  • Java 大视界 -- Java 大数据在智能家居设备联动与场景化节能中的应用拓展(413)
  • Node.js 部署:PM2 的 Fork 与集群模式
  • 【C++上岸】C++常见面试题目--网络篇(第二十五期)
  • LangChain使用方法以OpenAI 的聊天模型GPT-4o为例
  • CephFS存储文件系统介绍
  • Java Swagger2 能显示页面但看不到一个接口
  • SSL证书有效期缩短:自动化解决方案
  • C# 多线程编程 (.NET Framework 4.0)
  • 一个手艺活 - 跨语言编程
  • docker安装ollama、下载模型详细步骤
  • 微服务和分布式的基础学识
  • 自动化测试框架pytest---Json Schema
  • 阿里云PolarDB MySQL版与MCP集成方案:数据处理分析全流程的效能革命
  • Python实现霸王龙优化算法(Tyrannosaurus Optimization Algorithm, TROA)(附完整代码)
  • 弥合安全分析与故障仿真之间差距的方法
  • JavaEE---9.网络原理TCP/IP
  • @Value
  • 安装es、kibana、logstash
  • Leetcode-148.排序链表
  • 基于ETF底仓的网格交易系统实现动态参数优化与动量因子融合