关键词:串口通信的基本原理;波特率;URAT;串口接收、发送数据;串口与中断
一、知识点
1.单工、半双工、全双工;同步、异步;并行通信、串行通信
2.波特率:含义;9600BPS对应参数
3.UART的数据发送和接收:2个SBUF;无论数据发送还是接收,都会产生中断,若用中断查询,需要分清是发送还是接收
4.异步8位UART并且允许接收:SCON=0x50
5.查询方式和中断方式
6.MAX232芯片——计算机和单片机之间的电平转换
详细参考:
【蓝桥杯单片机09】串行接口的基本原理与应用
https://bbs.21ic.com/icview-2478488-1-1.html?fromuser=
(出处: 21ic电子技术开发论坛)
二、实验
实验1:
实验目的:
所需部件:
1.串口通信初始化函数Init_Uart
2.串口的中断服务函数——用中断来查询单片机是否读成功
3.串口向上机位发送数据函数
4.main函数
实验步骤:
S1:辅助寄存器自定义(看开发板是否需要)
原因:
因为头文件中没有辅助寄存器AUXR的地址,而在串口通信的初始化中需要使AURX=0x00(只有AUXR第0位为0,串口才能正常收发数据),所以需要在前面进行自定义
sfr AUXR=0x8e;
S2:UART初始化函数
思路流程:
1.串口通信中需要利用定时器1产生波特率,采用8位自动重装10,而TMOD后四位管定时器0,所以不必管,可全部置为0——TMOD=0x20;
2.查数据手册知,采用8位自动重装(且产生9600BPS波特率)的重装值(TH0)为0xFD,自动重装中TL0=TH0,也为0xFD——TH0=0xfd; TL0=0xfd;
3.打开定时器1——TR1=1;
4.串口控制寄存器SCON一般固定——SCON=0x50
5.AUXR置0——AUXR=0x00;
6.打开串口中断开关——ES=1;EA=1;
代码:
void Init_Uart()
{
TMOD=0x20;
TH0=0xfd;
TL0=0xfd;
TR1=1;
SCON=0x50;//串口控制寄存器,一般这个值是固定的
AUXR=0x00;//上电后第0位可能不是0
ES=1;
EA=1;//打开串口的中断
}
S3: 用中断方式让串口接收上机位输来的数据(读)
思路流程:
1.判断是否读成功——if(RI==1)
2.如果读成功,就手动将RI清零,并设一个变量接收缓存寄存器SBUF中的数据——Urdat=SBUF;
3.单是执行上面2步,上机位看不到单片机是否接收到数据了,所以需要再将读到的字节发送到上机位,在串口助手中显示出来
4.用到串口向上机位发送数据函数,但定义在下面,会报错,有2种处理方法:①前面声明②将整个函数体挪到前面
代码:
unsigned char Urdat=0;
void ServiceUart() interrupt 4
{
if(RI==1)
{
RI=0;
Urdat=SBUF;
SendByte(Urdat+1);
}
}
S4:用查询方式让串口向上机位发送数据
思路:
1.发送的数据存在发送寄存器SBUF中——SBUF=dat;
2.检测是否发送成功数据,用空循环语句——while(TI==0);
3.手工清零发送成功标志位——TI=0;
代码:
void SendByte(unsigned char dat)
{
SBUF=dat;
while(TI==0);//直到发送完成后,TI=1,跳出空循环
TI=0;//手工清零
}
S5:main函数完善
代码:
void main()
{
Init_Uart();
SendByte(0x5a);
SendByte(0xa5);
while(1)
{
}
}
完整代码:
#include <REGX52.H>
sfr AUXR=0x8e;//新增寄存器需要自定义,辅助寄存器
void SendByte(unsigned char dat);
void Init_Uart()
{
TMOD=0x20;
TH0=0xfd;
TL0=0xfd;
TR1=1;
SCON=0x50;//串口控制寄存器,一般这个值是固定的
AUXR=0x00;//上电后第0位可能不是0
ES=1;
EA=1;//打开串口的中断
}
//串口通信中需要利用定时器1产生波特率,采用8位自动重装10
unsigned char Urdat=0;
void ServiceUart() interrupt 4
{
if(RI==1)
{
RI=0;
Urdat=SBUF;
SendByte(Urdat+1);
}
}
void SendByte(unsigned char dat)
{
SBUF=dat;
while(TI==0);//直到发送完成后,TI=1,跳出空循环
TI=0;//手工清零
}
void main()
{
Init_Uart();
SendByte(0x5a);
SendByte(0xa5);
while(1)
{
}
}
实验2:
实验目的:
实验2.1 通过串口向上位机发送字符串
大体和实验1相同,亮点在:
1.关闭蜂鸣器和继电器等设备,需要将HC573锁存器丰富完整(case0的增加)以及系统初始化函数,在main最开始调用
void Init_System()
{
SelectHC573(5);//Ñ¡Ôñ·äÃùÆ÷ºÍ¼ÌµçÆ÷¶ÔÓ¦µÄY5ͨµÀ
P0=0x00;//¹Ø±ÕʹÄܶË
SelectHC573(4);//Ñ¡ÔñLED¶ÔÓ¦Y4ͨµÀ
P0=0x00;
}
2. 发送字符串函数
思路:
①字符串用数组盛放,参数可用指针等价,表示数组的首地址。
②逐一发送数组中的字节(指针++,指针往下移,实现发送下一个字节)
③直到(while)碰到字符串的结束标志‘\0’
代码:
void SendString(unsigned char *str)
{
while(*str!='\0')
{
SendByte(*str++);
}
}
完整代码:
#include <REGX52.H>
sfr AUXR=0x8e;
void SendByte(unsigned char dat);
void SelectHC573(unsigned char channel)
{
switch(channel)
{
case 4:P2=(P2&0x1f)|0x80;break;
case 5:P2=(P2&0x1f)|0xA0;break;
case 6:P2=(P2&0x1f)|0xC0;break;
case 7:P2=(P2&0x1f)|0xE0;break;
case 0:P2=(P2&0x1f)|0x00;break;//关闭锁存器所有开关
}
}
void Init_System()
{
SelectHC573(5);//选择蜂鸣器和继电器对应的Y5通道
P0=0x00;//关闭使能端
SelectHC573(4);//选择LED对应Y4通道
P0=0x00;
}
//*************************串口通信相关
void Init_Uart()
{
TMOD=0x20;
TH0=0xfd;
TL0=0xfd;
SCON=0x50;
AUXR=0x00;
ES=1;
EA=1;
TR1=1;
}
unsigned char URdat=0;
void ServiceUart() interrupt 4
{
if(RI==1)
{
}
}
void SendByte(unsigned char dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
void SendString(unsigned char *str)
{
while(*str!='\0')
{
SendByte(*str++);
}
}
//***********************************
void main()
{
Init_System();
Init_Uart();
SendString("Welcome to DPJ World!\r\n");//\r是回车,\n是换行
while(1)
{
}
}
实验现象:
上电后,蜂鸣器、继电器关闭,串口助手中的接收框显示发送的字符串
实验2.2 上位机通过串口控制LED&上位机读取下位机运行状态
沿用实验2.1,亮点是:
1.单片机需要从上位机处接收到指令command,读判断
unsigned char command=0x00;
unsigned char URdat=0;
void ServiceUart() interrupt 4
{
if(RI==1)
{
command=SBUF;
RI=0;
}
}
2.对指令进行分析,并控制LED(0表示开灯,1表示关灯)
思路:
①command初始化为0x00,如果接收到,则高四位一定不为0——以if(command!=0x00)为界
②case 0xc0:发送字符串+清零command(否则在while循环中不断扫描,会不断输出字符串)+break(其余几个case沿用大格式)
③case 0xa0:让P0保持高四位不变,低四位变成~command
(P0|0x0f)——>P0的高四位不变,低四位全部置1,L1-L4全灭
(~command|0xf0)——>0xf0使得低四位为~command的低四位,高四位全为1,这样之后&后,P0的高四位还是保持不变。~command是因为0表示开灯,1表示关灯,要进行取反才能让command生效
④case 0xb0:总体思路与0xa0相同,而0xf0等要变化,~command要左移四位,P0的高四位控制L5-L8;
代码:
void working()
{
if(command!=0x00)
{ //command高四位如果接收到,不是A,就是B,C,只有判断接受到command才对其分析
switch(command&0xf0)
{
case 0xa0:P0=(P0|0x0f)&(~command|0xf0);command=0x00;break;
case 0xb0:P0=(P0|0xf0)&((~command<<4)|0x0f);command=0x00;break;
case 0xc0:SendString("The system is running!\r\n");command=0x00;break;
}
}
}
三、总结收获
1.串口通信的基本原理与相关知识
2.串口与上机位之间的收、发数据函数
3.串口与中断函数
4.学会关闭蜂鸣器和继电器,上电后进行初始化
5.通过串口控制LED、输出字符串、让上机位检测下机位(eg.单片机)的运行状态
感谢B站小蜜蜂老师的教程,本笔记资料及代码均来自教程,仅作为个人复习、整理和学习交流用。