51单片机串口通信

前言


        串口是一种应用十分广泛的通讯接口,是串行通信最纯粹的表现形式,即数据结构是由一串一串的数据流构成的,通常是8位一串地进行传递,每一位都有先后顺序,并不是同时到达目的地址的,因此串口的成本低,容易使用,线路简单,但速率较慢,可以实现两个设备的简单通信。与之对应的是并口,以8位为例,并行通信是8位数据同时发出,同步达到,理论上速率是穿行的8倍,但成本较高,应用场景较少。

        单片机的可以时单片机与单片机,单片机与电脑,单片机与各种各样的设备进行互相通信,极大地提升了单片机裸机的扩展性,增强了其硬件实力。        

一、UART


        51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。

        STC89C52系列单片机内部集成有一个功能很强的全双工串行通信口,与传统8051单片机的串口完全兼容。设有2个互相独立的接收、发送缓冲器,可以同时发送和接收数据。发送缓冲器只能写入而不能读出,接收缓冲器只能读出而不能写入,,因而两个缓冲器可以共用一个地址码(99H)。两个缓冲器统称串行通信特殊功能寄存器 SBUF 。

        电平协议:

        值得一提的是,串行通信分为很多种电平协议,例如TTL,RS232,RS485等,不做任何处理的话它们相互之间是无法正常通信的。电平标准就是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系。单片机一般都是TTL协议,电脑端大多都使用的是USB接口,TTL转USB协议就需要一颗ch340芯片,不过51的开发板已经装好了,所以我们只需要插上USB线就可以实现单片机和电脑端的串口通信了。

        串口常用的电平标准有如下三种:

        TTL电平:+5V表示1,0V表示0

        RS232电平:-3~-15V表示1,+3~+15V表示0

        RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)

        引脚定义 :

  • TXD :(Transmit Exchange Data) 数据发送
  • RXD:(Receive Exchange Data)  数据接收

        严格意义上来讲串口通信只有这两根线组成,通信原理也非常简单,就是一发一收,发送和接收分别独占一根数据线,可以兼顾发送和接收的工作,所以串口为全双工。由于没有第三根时钟线来统一传输的时间基准,所以串口为异步通信方式,双方必须约定好波特率,否则数据就会出现不可预知的错误。

        工作模式:

        STC89C52只有一个UART,总共有四种工作模式:

        模式0:同步移位寄存器     

        模式1:8位UART,波特率可变(常用)   

        模式2:9位UART,波特率固定     

        模式3:9位UART,波特率可变

        常用的只有模式1,其他3种模式小编到现在还没遇到过。

        

二、UART参数、时序、及其工作原理


        STC89C52系列单片机串行口对应的硬件部分对应的管脚是P3.0/RxD和P3.1/TxD。      

        参数:

        波特率:串口通信的速率(发送和接收各数据位的间隔时间)

        校验位:用于数据验证(奇校验/偶校验)

        停止位:用于数据帧间隔

        时序:

        一般情况我们都会选则模式1,此模式为8位UART格式,一帧数据为10位,1位起始位,8位数据位,1位停止位,波特率可变,即可根据需要进行设置。

        

8位数据格式

        模式2和模式3为9位数据格式,一帧数据总共11位,在停止位后紧接一位校验位,用以检验这帧数据的可靠性。

        

9位数据格式

        这两种方法均采用低位先行的原则,即从一帧数据的右端开始逐一发送,比如1010 1010,就是从最低位 “0” 开始发,接着发第二位的 “1” 接着 “0” 、“1”、“0”......经过一些硬件电路的处理后,接收端收到的数据仍然是“1010 1010”。

        下面是手册里给的时序图:

         工作原理:

 

         我们可以看到,串口是挂载在总线上的,除此之外总线上还挂载了许多设备交互工作。那么UART内部到底是如何完成数据的发送和接收的呢?将其放大后就是下面这张图:

          

         这就是UART串口模式1的功能结构示意图,上下两处标记的INTERNAL BUS就是内部总线,与上面单片机内部结构图相关联。接下来详细些说说我对这张图的理解,可能有不对的地方也请大家指正哈!

波特率发生器:

         首先就是控制波特率这边,stc89c52是利用了定时器1的溢出率,通过16分频或2分频后十六分频来控制收发器的采样时间,这里需要配置SMOD 这个寄存器来控制它的收发速率,来形成一个类似的波特率发生器,图中可以看到发送和接受是公用波特率电路的,所以发送和接收的波特率也是相同的,且与之进行通信的设备波特率也必须是相同的,否则传输的数据就会出错。

发送数据:

        写入的数据会存放在SBUF(Serial Buffer)这个缓存区中,同时触发start这个标志位,使发送控制器开始工作,数据就会一位一位的发送到TX引脚端。这里出现了一个触发器,我忘了这是个什么触发器了QAQ(原谅小编数电没学好),不过不影响对这部分框图的理解。

接收数据:

         数据从RX引脚端进来,接收控制器控制移位寄存器一位一位地放入到SBUF缓存区,再通过总线发送给CPU,我们想要数据的时候把这个缓存读出来就好了。注意,这个SBUF和发送端的SBUF在物理上是两个独立的寄存器,但占用了相同的地址(0x99),写操作时写入的是发送寄存器,读操作时读出的是接收寄存器。那么在程序中是如何区分的呢?当SBUF作为左值时(在“=”左边)就是写入缓存,当SBUF作为右值时(在“=”右边)就是读出缓存。

串口中断:

       

        发送控制器和接收控制器在发送和接收时分别会置"TI"和"RI"标志位,这两个标志位经过一个或门可以申请串口中断,需要使用串口中断时需先配置好中断参数,包括使能,选用通道,和优先级等等,TX和RX占用同一个中断通道,需要对其进行判断。在中断服务程序中可以进行相应的处理,比如当有数据传入时触发接收中断,这时就可以及时地对接收到的数据进行处理。

        OK那,这就是UART原理图部分的简单介绍了。

三、寄存器配置


        书写程序需要对照上面的框图和寄存器表格来配置参数:

         STC89C52系列单片机的穿行口有两个控制寄存器:串行控制寄存器SCON和波特率选择特殊功能寄存器PCON。

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

        SCON: 串行控制寄存器 (可位寻址)

         其中SMO、SM1按下列组合确定串行口的工作方式:

         我们这里选择方式1,即SMO、SM1分别给0、1。

        SM2:该位是控制是否允许方式2、方式3进行多机通信,我们使用的是方式1,这一位可以随便给,就置0了。

        RXE:该位为是否允许接收控制位,置1时为使能接收,置0时为失能接收通道,我们需要串口接收功能,这位给1。

        TB8和RB8两位分别是方式2、方式3第九位的发送校验位和接收校验位,不用管它。不过在方式1时,如果SM2为0,则RB8接收到的是停止位,也就是说在当前模式,这位数会在接收到一帧数据时变为停止位,我们不对停止位进行处理,还是不用管。

        TI:发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。

        RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI=1,向主机请求中断,响应中断后必须用软件复位,即RI=0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1(例外情况见SM2说明) ,必须由软件复位,即RI=0。

        后面这几位都是标志位所以都不需要配,它们会在特定情况改变,我们只需要读出它即可,比如串口中断时,初始化配置都给0就可以。那么这个8位寄存器配置的值就是 “0101 0000”

    

PCON: 电源控制寄存器(不可位寻址)

        SMOD:这个SMOD就是上面的波特率图中的那个开关控制,波特率选择位,当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍:SMOD=0,则波特率不加倍。复位时SMOD=0。

        SMOD0:帧错误检测有效控制位。当SMODO=1,SCON寄存器中的SMO/FE位用于FE(帧错误检测)功能:当SMOD0=0,SCON寄存器中的SMO/FE位用于SMO功能,和SM1一起指定串行口的工作方式。复位时SMODO=0

        剩余的其他位都是标志位不需要处理,配置为波特率加倍即可,即“1000 0000”,“0x80”。

        不是很明白为什么要把波特率控制放在电源控制寄存器中,这个寄存器暂时就配这两位就可以,接下来看一看是如何用定时器来产生波特率的:

        波特率是定时器产生的,前面博文介绍过关于定时器的工作原理和配置,这里就不做过多介绍了,需要注意的是串口波特率用的是定时器1,而且是不能更改的,要配置为双8位自动重装载模式,并且是根据溢出率来计算的,计数值每到2^8=255就会自动重装。至于这个波特率的计算方法还是比较麻烦的,涉及到晶振和时钟等复杂的计算,建议直接用ISP下载助手这个软件的波特率计算功能直接生成C代码。

四、 代码


        终于结束了枯燥的讲解部分,来看看代码怎么写,这段代码主要实现一收一发的简单功能,详细解释都写在了注释里面:

#include <REGX52.H>
void Delay(unsigned int xms)                //延时函数
{    
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

void Uart_Init(void)		                //4800bps@11.0592MHz
{
	SCON  = 0x50;			                //8位数据,可变波特率,接收使能
	PCON |= 0x80;			                //波特率加倍
	TMOD &= 0x0F;			                //设定定时器1为双8位自动重装方式
	TMOD |= 0x20;			                //设定定时器1为双8位自动重装方式
	TL1 = 0xF3;				                //设定定时初值
	TH1 = 0xF3;				                //设定定时初值
	ET1 = 0;				                //禁止定时器1中断
	TR1 = 1;				                //启动定时器1
	ES=1;					                //开串口中断
	EA=1;					                //开总中断
}

void UART_SendByte(unsigned char Byte)      //串口发送一字节函数
{
	SBUF=Byte;							    //这里SBUF做为左值,所以是写操作,也就是发送
	while(TI==0);                           //判断发送标志位是否置位,置1发送完成,否则等待
	TI=0;                                   //软件复位
}

void main()
{
	Uart_Init();						    //串口初始化
}

void UART_Interrupt() interrupt 4
{
	static unsigned char Temp;			    //创建一个8位变量传递数据
	if(RI==1)
	{
		RI=0;							    //标志位置位
		Temp=SBUF;  					    //这里SBUF做为右值,所以是读操作,也就是接收
		UART_SendByte(Temp);			    //将接收到的数据原封不动地发回去
	}
}	

总结


        感谢观看!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

且看林地几华里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值