0%

51单片机——UART通信简单记录

简单整理一下之前用51单片机玩UART通信的笔记(๑¯∀¯๑)。

单片机:STC89C52RC

UART相关

概括

UART(Universal Asynchronous Receiver/Transmitter的缩写,中文名称:通用异步收发传输器)包括了RS232、RS449、RS423、RS422和RS485等接口标准规范和总线标准规范(UART不是通信协议,而是MCU中相对独立的电路模块),它将需要传输的数据在串行通信与并行通信之间加以转换:发送端的UART将来自控制设备(例如CPU)的并行数据转换为串行数据,以串行方式将其发送到接收端的UART,随后接收端的UART将串行数据转换为并行数据传递给接收端的控制设备。

协议层

UART将需要传输的数据封装为数据包,每个数据包包含1个起始位,5~9个数据位(取决于UART的具体设置),一个可选的校验位,1个到2个停止位(1位、1.5位、2位)。

起始位

发送端与接收端之间的UART传输总线通常在不传输数据时保持在高电平;在开始传输数据前,发送端的UART在一个时钟周期内将传输总线从高电平拉低到低电平;当接收端的UART检测到传输总线由高电平转换到低电平时,将以波特率的频率读取数据位中的每一位数据。

数据位

数据位包含接收端的控制设备实际需要的数据,数据位的长度可以是5、6、7、8,9位(如果使用校验位,则可以是5位,最多8位;如果不使用校验位,数据帧的长度可以是9位(此时可以把校验位当作数据位来使用)),一般为8位,因为一个ASCII码的长度为8位。

数据位从低有效位到高有效位依次发送。

校验位

校验位可以是奇校验、偶校验、1校验、0校验,也可以没有校验位。

  • 奇校验:校验位为1,数据位与校验位的高逻辑位的位数之和为奇数;
  • 偶校验:校验位为0,数据位与校验位的高逻辑位的位数之和位偶数;
  • 1校验:强行将校验位设置为高逻辑位,仅通过检查校验位的值是否被修改为依据来判断是否有噪声干扰了通信或者数据的发送和接收是否同步;
  • 0校验:强行将校验位设置为低逻辑位,效果同上。

停止位

发送端的UART将传输总线拉高到高电平,持续1到2个传输位的时间,以此来通知接收端的UART停止接收;同时给发送端和接收端提供了同步时钟的机会,停止位的位数越多,时钟同步的容错性越好,但数据传输越慢。

优、缺点

优点

  • 占用的引脚少,除电源部分外,只需要两条数据传输线即可进行通信;
  • 无需时钟信号;
  • 有校验位,有利于检查数据是否完整无修改;
  • 只需要在发送端和接收端设置好数据包结构,即可稳定通信。

缺点

  • 数据帧最大只支持9位数据;
  • 不支持多主机或多从机的主从系统。

51单片机UART的使用

与51单片机UART通信相关的特殊功能寄存器有串行控制寄存器SCON、电源控制寄存器PCON、定时器模式寄存器TMOD

串行控制寄存器SCON

串行控制寄存器SCON用于选择串行通信的工作方式和某些控制功能。

各位地址为0x98~0x9f,可位寻址。

SM0&SM1

通过SM0SM1的组合可选择串行通信的工作方式:

  • 方式0:在方式0下,数据从RXD端串行输出或输入,以8位数据为一帧,没有起始位和停止位,同步信号从TXD端输出,波特率固定不变,为晶振频率的1/12;常用于串口外接移位寄存器以拓展并行I/O,不适用于两个51单片机之间的串行通信。
  • 方式1:真正用于数据的串行发送和接收,TXD端和RXD端分别用于发送和接收数据;一帧数据为10位,包括1个起始位、8个数据位、1个停止位。
  • 方式2:当串口工作于方式2时,被定义为9位异步通信接口,每帧数据均为11位,包括1个起始位、8个数据位、1个校验位、1个停止位;第9位数据可通过软件控制,与SCON中的SM2位配合可使串口用于多机通信;波特率固定不变,为晶振频率的1/64或1/32,由PCON的最高位控制。
  • 方式3:与方式2类似,但方式3的波特率可变,也适用于多机通信。

SM2

多机通信控制位;主要用于方式2和方式3;若SM2=1,则允许多机通信;若SM2=0,则不允许多机通信,在接收完一帧数据后,置RI为1,同时把接收到的数据写入SBUF,在方式0中,SM2必须置0,在方式1中,若SM2=1,只有接收到有效停止位时,RI才置1。

REN

允许接收控制位;由软件置1或清零;当REN=1时,允许接收;当REN=0时,禁止接收。

TB8

将待发送数据的第9位写入TB8,可以在多机通信中作为发送地址帧或数据帧的标志位。

RB8

将接收到的数据的第9位写入RB8,类似于TB8

TI

发送中断标志位;一帧数据准备发送时,该帧数据会被写入一个用作发送缓冲的SBUF等待马上被读取,当该帧数据发送完后,TI被置1,申请中断,表示该用作发送缓冲的SBUF已空,可以准备发送下一帧数据;TI不会自动清零,必须软件清零。

RI

接收中断标志位;当接收完一帧数据并且该帧数据被写入一个用作接收缓冲的SBUF后,RI被置1,申请中断,表示数据必须要马上被读取;RI也必须软件清零。RITI是同一中断源,CPU一开始并不知道是发送还是接收产生的中断请求,必须在中断服务程序中通过是TI==1还是RI==1来判别。

电源控制寄存器PCON

电源控制寄存器PCON中只有SMOD位与串口运行有关,不可位寻址。

  • SMOD:波特率倍增位;在方式1、2、3中,当SMOD=1时,波特率增加一倍。

SBUF

UART支持全双工的串口通信,可以同时接收和发送,待发送和已接收的数据分别写入两个不同的SBUF中暂存,这两个寄存器在物理上是相对独立的,但共用同一地址0x99。两个SBUF每次都只能暂存一个数据帧,只有暂存在SBUF中的数据帧被读取后,才能暂存下一数据帧;数据帧被写入SBUF后,必须要被马上读取,否则将被下一数据帧覆写。

举个栗子

//51单片机与PC进行串口通信:PC向51单片机发送字符,51单片机将接收到的字符返回给PC


#include<reg51.h>

void UART_init() //初始化串口,波特率9600,晶振频率11.0592MHz
{
PCON=0x80; //使能波特率倍增位SMOD
SCON=0x50; //设置串口工作于方式1,不允许多机通信
TMOD=0x20; //设置定时器1为8位自动重载模式,STC89C52RC的定时器0不能用作波特率发生器
TL1=0xfa; //设置定时器1的初值
TH1=0xfa; //设置定时器1的重载值
TR1=1; //开启定时器1
ES=1; //开启串口中断
EA=1; //开启总中断
}

void UART() interrupt 4
{
char temp;
if(RI==1) //当软件判别是串口接收产生的中断请求的时候
{
temp=SBUF; //将接收到的数据赋值给变量temp
RI=0; //软件清零
SBUF=temp;//将变量的值写入SBUF,准备发送
}
while(TI==0); //等待数据发送完毕
TI=0;
}

void main()
{
UART_init();
while(1);
}

双机通信

补充一个在两片51单片机之间进行双机通信的例子。

若主机的按键从未被按下,则从机的数码管显示数字“0“;当主机的按键K1被按下后,从机的数码管刷新显示数字“1”;当主机的按键K2被按下后,从机的数码管刷新显示数字“2”;当主机的按键K3被按下后,从机的数码管刷新显示数字“3”。

//主机程序


#include<reg51.h>

sbit Key1=P1^0;
sbit Key2=P1^1;
sbit Key3=P1^2;

void Delaytime(unsigned char i)
{
while(i--);
}

void UART_init() //串口初始化,串口工作方式1,定时器工作方式2,波特率9600
{
SCON=0x50;
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
ES=0;
EA=1;
}

void UART_TX(unsigned char p) //串口输出数据
{
if(p!=0)
{
SBUF=p;
while(TI==0);
TI=0;
}
}

unsigned char KEY() //按键扫描;无按键被按下,则返回数值0;按下按键K1,则返回数值1;按下按键K2,则返回数值2;按下按键K3,则返回数值3
{
int key=0;
if(key==0&&(Key1==0||Key2==0||Key3==0))
{
Delaytime(100);
key=1;
if(Key1==0)
{
return 1;
}
else if(Key2==0)
{
return 2;
}
else if(Key3==0)
{
return 3;
}
}
else if(Key1==1&&Key2==1&&Key3==1)
{
key=0;
}
return 0;
}

void main()
{
unsigned char num;
UART_init();
while(1)
{
num=KEY();
if(num) //当有按键被按下时,则将相应的数值发送给从机
{
UART_TX(num);
}
Delaytime(1000);
}
}
//从机程序


#include<reg51.h>

unsigned char LEDS[]={0xc0,0xf9,0xa4,0xb0}; //数码管段码,分别显示数值0、1、2、3
unsigned char num=0;

void Delaytime(unsigned char i)
{
while(i--);
}

void UART_init() //串口初始化
{
SCON=0x50;
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
ES=1;
EA=1;
}

void UART() interrupt 4
{
num=SBUF; //获取主机按键值
RI=0;
}

void main()
{
UART_init();
while(1)
{
P0=LEDS[num];
Delaytime(1000);
}
}


如有错误,请在评论区多多指教,非常感谢!

参考资料:

UART串口通信的基本应用

我打赌!你还不会UART!不信,你看看~

STC89C52RC官方技术文档

【51单片机】(手把手教你)串口通信-基础篇

51单片机串口通讯UART