☰
51单片机电子秤系列UART
2026/10/15 3:58:46 网站建设 项目流程
#include<REGX52.H>voidURAT_Init(baud){PCON&=0x7F;//波特率不倍速SCON=0x50;//8位数据,可变波特率TMOD&=0x0F;//清除定时器1模式位TMOD|=0x20;//设定定时器1为8位自动重装方式TL1=0xFD;//设定定时初值TH1=0xFD;//设定定时器重装值ET1=0;//禁止定时器1中断TR1=1;//启动定时器1ES=1;}//串口发送1个字节voidUART_SendByte(unsignedcharByte)//发送字节{SBUF=Byte;//发送字节while(TI==0);//如果TI=1就让它变回0,发送后电脑自动TI=1会向主机请求中断,手动软件关闭中断复位TI=0;//发送中断请求关闭}voidSend_Word(unsignedchar*p)//发送字符串{while(*p){SBUF=*(p++);while(TI==0){;;;};TI=0;}}//void UART_Init()//串口发送+T1定时器//{ // 0 1 0 0 0 0 0 0// SCON=0x40; //0100 0000串口工作模式1,T1与SMOD决定SMOD SM0/FE SM1 SM2 REN TB8 RB8 TI RI 不接收发送// PCON |= 0x80; //使能波特率倍速位SMOD//最高位置1//4800波速率// //串口// TMOD &= 0x0F; //清除定时器1模式位//定时器T1设置//高位置0//8位自动重组初始化// TMOD |= 0x20; //设定定时器1为8位自动重装方式//开始设置// TL1 = 0xF4; //设定定时初值//范围// TH1 = 0xF4; //设定定时器重装值// ET1 = 0; //禁止定时器1中断,分开关静止中断// TR1 = 1; //启动定时器1//}//TH1=256-11059200/32/12/9600//void URAT_Init(baud)//{// PCON &= 0x7F; //波特率不倍速// SCON = 0x50; //8位数据,可变波特率// TMOD &= 0x0F; //清除定时器1模式位// TMOD |= 0x20; //设定定时器1为8位自动重装方式// TL1 = 0xFD; //设定定时初值// TH1 = 0xFD; //设定定时器重装值// ET1 = 0; //禁止定时器1中断// TR1 = 1; //启动定时器1// ES=1;//}////串口发送1个字节//void senddata(char dat)//{// SBUF = dat;// while(TI==0);// TI = 0;//}//void sendstring(unsigned char *str)//{// while(*str!='\0')// {// senddata(*str++);// }//}//char putchar(char c)// {// //输出重定向到串口// senddata(c);// return c;//返回给函数调用者printf//}//void UART_SendByte(unsigned char Byte)//发送字节//{// SBUF=Byte;//发送字节// while (TI==0);//如果TI=1就让它变回0,发送后电脑自动TI=1会向主机请求中断,手动软件关闭中断复位// TI=0;//发送中断请求关闭//}//void UART_Init()//串口发送+T1定时器//{ // 0 1 0 1 0 0 0 0// SCON=0x50; //0100 0000串口工作模式1,T1与SMOD决定SMOD SM0/FE SM1 SM2 REN TB8 RB8 TI RI (REN=1允许接收)// PCON |= 0x80; //使能波特率倍速位SMOD//最高位置1//4800波速率 //sm1 sm2决定工作模式// //串口// TMOD &= 0x0F; //清除定时器1模式位//定时器T1设置// TMOD |= 0x20; //设定定时器1为8位自动重装方式// TL1 = 0xF4; //设定定时初值//范围// TH1 = 0xF4; //设定定时器重装值// ET1 = 0; //禁止定时器1中断,分开关静止中断// TR1 = 1; //启动定时器1// EA=1;//总开关启动中断// ES=1 ; //启动串口中断//}/************************* 串口中断函数模版 ***************************///void UART_Routine() interrupt 4//串口中断//{// //P1=0x00;//要在缓冲区发送f0发送数据进入中断// if(RI==1)//接收中断请求标志RI会自动置1请求中断然后需软件复位// {// RI=0;// }/*********************** ***********************///串口初始化//void UART_Init(unsigned int BAUD)//BAUD是波特率//{ //0 0 1 0 0 0 0 0// TMOD=(TMOD&0x0f)|0x20;//TMOD定时器/计数器模式控制寄存器 GATE c/t m1 m0 GATE c/t m1 m0 低位T0计数器设置,高位T1计数器设置 //看定时器图具体// //GATE门控位,GATE=0时,TCON中的TRO或TR1为1,启动定时器/计数器工作// // GATE=1时,使TCON中的TRO或TR1为1,且需要外部引脚也为高电平,才能工作// //C/T:定时/计数模式切换,C/T=0时为定时模式,C/T=1时为计数模式// //M1,M0:用来选择定时计/计数器的工作方式,一般使用都是采用16位的计时计数器// //有4种模式,00//13位计时器 01//16位计时器 10//8位重组计时器 自动重新载入计数值 11//两组独立计时器,T1 T0不能同时用// //模式0-3 8192 65536 256 256// //波特率9600晶振11.0592 SMOD=0// TH1=256-11059200/32/12/BAUD//=253=0xfd范围高值// TL1=TH1;// TR1=1;//定时器1开始计时// ET1=0;//定时器只做波特率发生器,不中断// SCON=0x50;//模式1 01010000 可变波特率 REN=1允许中断 串口模式// ES=1;//串口中断打开// PS=0;//设串口为低级中断//}////PCON功率控制寄存器 SMOD SMOD0 ... SMOD=0 /32 不加倍 SMOD=1 /16 加倍// // 1 0 000//void main()//{// UART_Init(9600);//初始化串口// EA=1;//打开总开关// while(1);//}////发送函数//void SendData(unsigned char dat)//{// SBUF=dat;//写数据到UART数据寄存器// while(TI==0);//等待前面数据发送完成TI自动=1发生中断// TI=0;//手动关闭中断//}//文本模式ASCALL码值对应发送,HEX 16进制////UART中断服务程序// void Uart() interrupt 4// {// if(RI)// {// RI=0;// temp=SBUF;// SBUF=temp;// }// }//void helloword()//{// senddata('L');// senddata('a');// senddata('s');// senddata('l');// senddata('o');// senddata('w');// senddata('o');// senddata('r');// senddata('d');//}
#ifndef__UART_H__#define__UART_H__voidURAT_Init(baud);voidUART_SendByte(unsignedcharByte);voidSend_Word(unsignedchar*p);//void UART_Init();//void UART_SendByte(unsigned char Byte);//void URAT_Init(baud);//void senddata(char dat);#endif

3. 代码讲解

下面逐函数讲解这段 UART 串口通信代码的实现原理。

3.1 串口初始化函数URAT_Init

voidURAT_Init(baud){PCON&=0x7F;//波特率不倍速SCON=0x50;//8位数据,可变波特率TMOD&=0x0F;//清除定时器1模式位TMOD|=0x20;//设定定时器1为8位自动重装方式TL1=0xFD;//设定定时初值TH1=0xFD;//设定定时器重装值ET1=0;//禁止定时器1中断TR1=1;//启动定时器1ES=1;}

这个函数完成串口通信的初始化配置,逐行分析如下:

代码作用说明
PCON &= 0x7F波特率不倍速将 SMOD 位清零,波特率按 1/32 分频
SCON = 0x50配置串口模式8 位数据、可变波特率、允许接收(REN=1)
TMOD &= 0x0F清除定时器 1 模式位只保留定时器 0 的配置
TMOD |= 0x20定时器 1 设为 8 位自动重装模式 2,自动重载计数值
TL1 = 0xFD设定定时初值低 8 位初值 253
TH1 = 0xFD设定重装值溢出后自动重装 253
ET1 = 0禁止定时器 1 中断定时器 1 只作波特率发生器
TR1 = 1启动定时器 1开始计数
ES = 1使能串口中断允许串口产生中断

关键点:TH1 = 0xFD对应波特率 9600。初值计算公式为:

TH1 = 256 - 晶振频率 / (32 × 12 × 波特率) = 256 - 11059200 / (32 × 12 × 9600) = 256 - 3 = 253 = 0xFD

3.2 发送单字节函数UART_SendByte

voidUART_SendByte(unsignedcharByte)//发送字节{SBUF=Byte;//发送字节while(TI==0);//如果TI=1就让它变回0,发送后电脑自动TI=1会向主机请求中断,手动软件关闭中断复位TI=0;//发送中断请求关闭}
代码作用
SBUF = Byte把要发送的数据写入串口发送缓冲区,硬件自动开始发送
while (TI == 0)等待发送完成。TI 是发送中断标志,发送完成后硬件自动置 1
TI = 0软件清除发送中断标志,为下一次发送做准备

执行流程:

否,继续等待

是,发送完成

调用 UART_SendByte(Byte)

SBUF = Byte 写入缓冲区

TI == 1 ?

TI = 0 清除标志

函数返回

3.3 发送字符串函数Send_Word

voidSend_Word(unsignedchar*p)//发送字符串{while(*p){SBUF=*(p++);while(TI==0){;;;};TI=0;}}
代码作用
while (*p)遍历字符串,直到遇到字符串结束符\0(值为 0)
SBUF = *(p++)取出当前字符写入缓冲区,指针后移指向下一个字符
while (TI == 0)等待当前字符发送完成
TI = 0清除发送标志,继续发送下一个字符

执行流程:

是

否,等待

是

否,字符串结束

调用 Send_Word(p)

*p != '\\0' ?

SBUF = *p,p++

TI == 1 ?

TI = 0

函数返回

3.4 头文件uart.h

#ifndef__UART_H__#define__UART_H__voidURAT_Init(baud);voidUART_SendByte(unsignedcharByte);voidSend_Word(unsignedchar*p);#endif

头文件使用#ifndef防止重复包含,声明了三个对外接口函数,供主程序调用。

4. 波特率计算详解

4.1 计算公式

波特率计算公式如下:

波特率 = 晶振频率 / (32 × 12 × (256 - TH1))

其中:

  • 晶振频率:51 单片机常用 11.0592MHz
  • 32:SMOD=0 时的分频系数(SMOD=1 时为 16)
  • 12:机器周期 = 12 个时钟周期
  • 256 - TH1:定时器1的重装计数值

4.2 常见波特率对照表

目标波特率晶振频率SMODTH1 初值实际波特率误差
960011.0592MHz00xFD (253)96000%
480011.0592MHz00xFA (250)48000%
1920011.0592MHz00xFD (253)192000%
240011.0592MHz00xF4 (244)24000%

为什么选择 11.0592MHz 晶振?因为 11059200 可以被 9600、4800、19200 等常用波特率整除,误差为 0%,保证通信稳定。

5. 寄存器配置详解

5.1 SCON 寄存器(串口控制寄存器)

位名称值说明
7SM00与 SM1 组合选择工作模式
6SM11SM0=0, SM1=1 → 模式1(8位UART,可变波特率)
5SM20多机通信控制位,单机通信置0
4REN1允许接收
3TB80发送第9位数据(模式2/3用)
2RB80接收第9位数据
1TI0发送中断标志
0RI0接收中断标志

5.2 TMOD 寄存器(定时器模式寄存器)

位名称值说明
7GATE0门控位,0=仅TR1控制启停
6C/T0定时模式
5-4M1M010模式2:8位自动重装
3GATE0定时器0门控
2C/T0定时模式
1-0M1M000定时器0模式0

6. 案例:串口控制 LED

// 通过串口接收指令控制 LEDvoidUART_Interrupt()interrupt4{unsignedcharcmd;if(RI)// 收到数据{RI=0;// 清除接收标志cmd=SBUF;// 读取数据if(cmd=='1')P1=0x00;// 点亮所有 LEDelseif(cmd=='0')P1=0xFF;// 熄灭所有 LED}}
代码作用
interrupt 4声明这是串口中断服务函数(中断号 4)
if (RI)判断是否收到数据,RI 为接收中断标志
RI = 0清除接收标志
cmd = SBUF读取接收缓冲区中的数据
P1 = 0x00收到字符 ‘1’ 时点亮 LED(低电平点亮)
P1 = 0xFF收到字符 ‘0’ 时熄灭 LED

7. 调试建议

7.1 调试工具配置

工具配置项推荐值
串口调试助手波特率9600(与代码一致)
数据位88
停止位11
校验位None无

7.2 常见问题排查

现象可能原因解决方法
乱码波特率不匹配检查晶振频率和 TH1 初值计算
无数据未打开总中断确认EA=1已执行
发送卡死TI 标志未清除确认发送循环中TI=0
接收不到REN 未置1确认SCON=0x50中 REN=1

8. 总结

本文逐函数讲解了 51 单片机 UART 串口通信的代码实现,包括初始化配置、单字节发送、字符串发送和中断接收。核心要点:

  1. 初始化:通过 SCON、TMOD、TH1/TL1 配置串口模式和波特率
  2. 发送:写入 SBUF 后轮询 TI 标志确认发送完成
  3. 接收:通过中断服务函数读取 SBUF 数据
  4. 波特率:由晶振频率和定时器初值共同决定,11.0592MHz 晶振可精确匹配常用波特率

4. 寄存器配置详解

4.1 SCON 寄存器(串口控制寄存器)

位名称值说明
7SM00与 SM1 组合选择工作模式
6SM11SM0=0, SM1=1 → 模式1(8位UART,可变波特率)
5SM20多机通信控制位,单机通信置0
4REN1允许接收
3TB80发送第9位数据(模式2/3用)
2RB80接收第9位数据
1TI0发送中断标志
0RI0接收中断标志

4.2 TMOD 寄存器(定时器模式寄存器)

位名称值说明
7GATE0门控位,0=仅TR1控制启停
6C/T0定时模式
5-4M1M010模式2:8位自动重装
3GATE0定时器0门控
2C/T0定时模式
1-0M1M000定时器0模式0

5. 案例补充建议

5.1 案例一:串口控制 LED

// 通过串口接收指令控制 LEDvoidUART_Interrupt()interrupt4{unsignedcharcmd;if(RI)// 收到数据{RI=0;// 清除接收标志cmd=SBUF;// 读取数据if(cmd=='1')P1=0x00;// 点亮所有 LEDelseif(cmd=='0')P1=0xFF;// 熄灭所有 LED}}

5.2 案例二:串口发送传感器数据

// 定时采集温度并发送voidmain(){unsignedinttemp;UART_Init(9600);EA=1;while(1){temp=Read_Temperature();// 读取温度传感器UART_SendString("Temp: ");UART_SendByte(temp/10+'0');// 发送十位UART_SendByte(temp%10+'0');// 发送个位UART_SendString(" C\r\n");Delay(1000);// 延时1秒}}

6. 调试建议

6.1 调试工具配置

工具配置项推荐值
串口调试助手波特率9600(与代码一致)
数据位88
停止位11
校验位None无

6.2 常见问题排查

现象可能原因解决方法
乱码波特率不匹配检查晶振频率和 TH1 初值计算
无数据未打开总中断确认EA=1已执行
发送卡死TI 标志未清除确认发送循环中TI=0
接收不到REN 未置1确认SCON=0x50中 REN=1

7. 总结

本文通过系统讲解 UART 串口通信的寄存器配置、波特率计算和代码实现,帮助读者掌握 51 单片机串口通信的核心要点。建议读者:

  1. 动手实践:在开发板上运行改进后的代码,观察串口输出
  2. 逐步调试:使用串口调试助手验证波特率配置
  3. 扩展学习:尝试中断接收、多机通信、与 PC 上位机交互等进阶功能

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询