# include <REGX52.H> 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 UART_SendByte ( unsigned char Byte) //发送字节 { SBUF= Byte; //发送字节 while ( TI== 0 ) ; //如果TI=1就让它变回0,发送后电脑自动TI=1会向主机请求中断,手动软件关闭中断复位 TI= 0 ; //发送中断请求关闭 } void Send_Word ( unsigned char * 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__ void URAT_Init ( baud) ; void UART_SendByte ( unsigned char Byte) ; void Send_Word ( unsigned char * 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 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 ; } 这个函数完成串口通信的初始化配置,逐行分析如下:
代码 作用 说明 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 = 0xFD3.2 发送单字节函数UART_SendByte void UART_SendByte ( unsigned char Byte) //发送字节 { SBUF= Byte; //发送字节 while ( TI== 0 ) ; //如果TI=1就让它变回0,发送后电脑自动TI=1会向主机请求中断,手动软件关闭中断复位 TI= 0 ; //发送中断请求关闭 } 代码 作用 SBUF = Byte把要发送的数据写入串口发送缓冲区,硬件自动开始发送 while (TI == 0)等待发送完成。TI 是发送中断标志,发送完成后硬件自动置 1 TI = 0软件清除发送中断标志,为下一次发送做准备
执行流程 :
3.3 发送字符串函数Send_Word void Send_Word ( unsigned char * p) //发送字符串 { while ( * p) { SBUF= * ( p++ ) ; while ( TI== 0 ) { ; ; ; } ; TI= 0 ; } } 代码 作用 while (*p)遍历字符串,直到遇到字符串结束符\0(值为 0) SBUF = *(p++)取出当前字符写入缓冲区,指针后移指向下一个字符 while (TI == 0)等待当前字符发送完成 TI = 0清除发送标志,继续发送下一个字符
执行流程 :
3.4 头文件uart.h # ifndef __UART_H__ # define __UART_H__ void URAT_Init ( baud) ; void UART_SendByte ( unsigned char Byte) ; void Send_Word ( unsigned char * p) ; # endif 头文件使用#ifndef防止重复包含,声明了三个对外接口函数,供主程序调用。
4. 波特率计算详解 4.1 计算公式 波特率计算公式如下:
波特率 = 晶振频率 / (32 × 12 × (256 - TH1))其中:
晶振频率 :51 单片机常用 11.0592MHz32 :SMOD=0 时的分频系数(SMOD=1 时为 16)12 :机器周期 = 12 个时钟周期256 - TH1 :定时器1的重装计数值4.2 常见波特率对照表 目标波特率 晶振频率 SMOD TH1 初值 实际波特率 误差 9600 11.0592MHz 0 0xFD (253) 9600 0% 4800 11.0592MHz 0 0xFA (250) 4800 0% 19200 11.0592MHz 0 0xFD (253) 19200 0% 2400 11.0592MHz 0 0xF4 (244) 2400 0%
为什么选择 11.0592MHz 晶振? 因为 11059200 可以被 9600、4800、19200 等常用波特率整除,误差为 0%,保证通信稳定。
5. 寄存器配置详解 5.1 SCON 寄存器(串口控制寄存器) 位 名称 值 说明 7 SM0 0 与 SM1 组合选择工作模式 6 SM1 1 SM0=0, SM1=1 → 模式1(8位UART,可变波特率) 5 SM2 0 多机通信控制位,单机通信置0 4 REN 1 允许接收 3 TB8 0 发送第9位数据(模式2/3用) 2 RB8 0 接收第9位数据 1 TI 0 发送中断标志 0 RI 0 接收中断标志
5.2 TMOD 寄存器(定时器模式寄存器) 位 名称 值 说明 7 GATE 0 门控位,0=仅TR1控制启停 6 C/T 0 定时模式 5-4 M1M0 10 模式2:8位自动重装 3 GATE 0 定时器0门控 2 C/T 0 定时模式 1-0 M1M0 00 定时器0模式0
6. 案例:串口控制 LED // 通过串口接收指令控制 LED void UART_Interrupt ( ) interrupt4 { unsigned char cmd; if ( RI) // 收到数据 { RI= 0 ; // 清除接收标志 cmd= SBUF; // 读取数据 if ( cmd== '1' ) P1= 0x00 ; // 点亮所有 LED else if ( 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(与代码一致) 数据位 8 8 停止位 1 1 校验位 None 无
7.2 常见问题排查 现象 可能原因 解决方法 乱码 波特率不匹配 检查晶振频率和 TH1 初值计算 无数据 未打开总中断 确认EA=1已执行 发送卡死 TI 标志未清除 确认发送循环中TI=0 接收不到 REN 未置1 确认SCON=0x50中 REN=1
8. 总结 本文逐函数讲解了 51 单片机 UART 串口通信的代码实现,包括初始化配置、单字节发送、字符串发送和中断接收。核心要点:
初始化 :通过 SCON、TMOD、TH1/TL1 配置串口模式和波特率发送 :写入 SBUF 后轮询 TI 标志确认发送完成接收 :通过中断服务函数读取 SBUF 数据波特率 :由晶振频率和定时器初值共同决定,11.0592MHz 晶振可精确匹配常用波特率4. 寄存器配置详解 4.1 SCON 寄存器(串口控制寄存器) 位 名称 值 说明 7 SM0 0 与 SM1 组合选择工作模式 6 SM1 1 SM0=0, SM1=1 → 模式1(8位UART,可变波特率) 5 SM2 0 多机通信控制位,单机通信置0 4 REN 1 允许接收 3 TB8 0 发送第9位数据(模式2/3用) 2 RB8 0 接收第9位数据 1 TI 0 发送中断标志 0 RI 0 接收中断标志
4.2 TMOD 寄存器(定时器模式寄存器) 位 名称 值 说明 7 GATE 0 门控位,0=仅TR1控制启停 6 C/T 0 定时模式 5-4 M1M0 10 模式2:8位自动重装 3 GATE 0 定时器0门控 2 C/T 0 定时模式 1-0 M1M0 00 定时器0模式0
5. 案例补充建议 5.1 案例一:串口控制 LED // 通过串口接收指令控制 LED void UART_Interrupt ( ) interrupt4 { unsigned char cmd; if ( RI) // 收到数据 { RI= 0 ; // 清除接收标志 cmd= SBUF; // 读取数据 if ( cmd== '1' ) P1= 0x00 ; // 点亮所有 LED else if ( cmd== '0' ) P1= 0xFF ; // 熄灭所有 LED } } 5.2 案例二:串口发送传感器数据 // 定时采集温度并发送 void main ( ) { unsigned int temp; 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(与代码一致) 数据位 8 8 停止位 1 1 校验位 None 无
6.2 常见问题排查 现象 可能原因 解决方法 乱码 波特率不匹配 检查晶振频率和 TH1 初值计算 无数据 未打开总中断 确认EA=1已执行 发送卡死 TI 标志未清除 确认发送循环中TI=0 接收不到 REN 未置1 确认SCON=0x50中 REN=1
7. 总结 本文通过系统讲解 UART 串口通信的寄存器配置、波特率计算和代码实现,帮助读者掌握 51 单片机串口通信的核心要点。建议读者:
动手实践 :在开发板上运行改进后的代码,观察串口输出逐步调试 :使用串口调试助手验证波特率配置扩展学习 :尝试中断接收、多机通信、与 PC 上位机交互等进阶功能