51单片机之串口通信

目录

1.串口简介

1.1TXD和RXD

1.2通讯接口

1.3通信方式

1.4 51单片机的UART模式

2.串口配置

2.1寄存器简介

        SCON寄存器配置

        PCON配置

2.2代码配置串口

2.2.1  配置串口发送数据

2.2.2配置电脑向单片机发送数据点亮LED


1.串口简介

        串口是一个应用十分广泛的通讯接口,它可以实现两个设备之间的相互通信,大大提高了单片机的硬件实力。前面讲述中断的时候顺便提过51单片机的串口中断,我们使用的51单片机使用的是UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可以实现单片机的串口通信。

1.1TXD和RXD

        简单的两个设备之间的通信可以简化成下面的图:

        VCC与GND的连接就不多说, 主要是为了供电,TXD和RXD需要解释一下:TXD可以展开成Transmit Exchange Data,即传输数据端;RXD可以展开成Receive Exchange Data,即接收数据端。也就是我们使用TXD输出数据,RXD接收数据,实现两个设备之间的相互通信。

1.2通讯接口

        就像学校机房老式台式机就有接口这样一个东西:这就是我们所说的接口

        我们设备之间的通信也是通过接口进行的,像这里列举出的常见的接口:

        UART就是我们单片机上的通讯接口,I2C也是一个常见的接口,未来会接触很多。

        此外,设备之间通信还有CANUSB.USB就是我们常用的一个简单易用的通讯工具,就像我们使用的U盘,USB数据线,很多都是USB通讯使用的。

1.3通信方式

        上面表格里写了通信接口的通信方式,这里介绍一下这个通信方式的意思:

  • 全双工:通信双方可以在同一时刻互相传输数据
  • 半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
  • 单工:通信只能有一方发送到另一方,不能反向传输

1.全双工就类似有两根线 ,像这样:

        因为有两个可以相互通信的通道,所以就可以进行两个设备之间的通信,并且可以在同一时刻进行相互通信

2.半双工就是类似只有一根线,但是却又可以进行两端的通信

        因为只有一个通信的通道,所以两设备之间的通信不能在同一时间完成,需要一次进行一个单方向的通信,完成后才可以进行下一个通信。

3.单工就比较简单了,就是只能进行单方向的通信

        就好比我们的电视机遥控器向电视发送信息,这就是单工。


  • 异步:通信双方各自约定通信速率
  • 同步:通信双方靠一根时钟线来约定通信速率

1.异步就是通信双方互相约定一个速率,让两边设备虽然运作方式不同,但是可以通过约定的规则进行采样获得数据

        比如这里就是对同一个数据不同的采样方式,前一个得到的是1100,后一个得到的是10,再把这个数据变得更加复杂一点,我们使用不同的采样方式,得到的数据会完全不同,所以我们使用异步约定速率还是很重要的,并且一般异步的两个设备是可以不使用线连接的。  

2.同步是双方使用一根时钟线直接约定速率,所以一般同步都是使用线连接的。这里就不多讲


  • 总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)

总线就是一种运输方式,这里只是做一个简单介绍,后面会详细介绍 。

就像是一个大马路连接了路边所有住户,所有设备可以通过这条总线进行相互通信。

1.4 51单片机的UART模式

STC89C52有1个UART

STC89C52的UART有四种工作模式:

  •   模式0:同步移位寄存器
  •   模式18UART,波特率可变(常用)
  •   模式29UART,波特率固定
  •   模式39UART,波特率可变
  •  
    •         波特率又是一个我们经常听到的东西,那么它是什么呢?其实就是一个约定的速率,就是我们串口通信的速率(发送和接收各数据位的间隔时间)

      •         还有检验位是用于数据验证的。打开这个软件

      • 打开

      • 这里有几个校验位:就是用来检测数据的正确性的。

      • 这里主要介绍最常用的两个:奇校验和偶校验

      • 奇校验:

        •         给定一串二进制数据如1001 1100,设置该数据的1的个数为奇数个,如果原本的数据中1的个数就是奇数,那么设置校验位为0,否则设置为1,这里的1001 1100就可以把校验位设置为1,然后再把传输后的数据进行同样的操作,比较校验位是否相同,如果不相同则说传输数据出错了。

      • 偶校验:

        •         和奇校验类似,控制二进制数据中1的个数为偶数个,设置校验位。然后再在传输数据之后设置校验位。

          •         很显而易见的是,奇校验和偶校验是可以一定程度上检验数据的准确性的,但是还是有很多情况是无法检测错误的,比如1111 0011变成1100 0011,这样我们的奇校验和偶校验都无法检验出来。

停止位用于数据帧的间隔,即数据之间有间隔,我们使用停止位来占用这部分位。

8位数据和9位数据格式的区别就是9位数据可以用来设置一位为校验位。

2.串口配置

2.1寄存器简介

        我们这里配置最常见的串口模式——模式1

        这是串口模式配置图,这里有一个很重要的寄存器,就是SBUF寄存器,也叫串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器

        最左边的就是总线,两个不同意义上的SBUF寄存器使用一个总线连接,从输入端进入就会从输出端出。

        其中的这部分是使用定时器1实现的,主要作用就是控制波特率,也就是我们的数据收发速率。

       有了前面配置定时器0的经验,现在我们来配置这个串口的中断也是比较简单的,还是配置一下寄存器和使能还有中断优先级。所以我们来直接看寄存器表格:

        首先来介绍一下SCON寄存器:

        SCON 还是可位寻址,所以我们还是可以直接一位一位的配置这个寄存器。但是我们一般都是直接配置SCON的,如果想要一个一个配也是可以的。

        再来看看PCON寄存器:

        这个寄存器本来是用来控制电源的,但是这里多了两个控制模式的位SMOD和SMOD0,原因就是SCON寄存器的位满了,而中国PCON还有几个空闲的位,所以干脆就直接把这两个模式控制的就放在PCON里了。


        SCON寄存器配置

        我们一般使用的是方式1,所以这里我们要把SM0设为0,SM1设为1。并且,我们需要SM0为方式判断的位,我们还要把PCON中的第六位置为0。

        REN即允许/禁止串口接收数据,我们需要使用软件置位,我们需要使用接收数据就置REN为1,否则我们就置REN为0.

 

TI和RI我们在前面原理图的地方有见到过:

        TI和RI任意一个为1,就会导致跳转到中断函数,其中TI为发送中断请求标志位,RI为接收中断请求标志位。我们需要把这两个在初始时都标为0,然后一般由硬件置为1,当中断启用的时候,我们就必须把它们再次在软件中置为0.

 

        这三个我们是不需要管的,这三个是和方式2和方式3相关联,我们使用的是方式1,所以我们不配置,直接置为0都可以。

        总的来说,如果我们只需要串口发送数据,我们只要把SCON中的SM1置为1,其他的置为0就可以了,这样我们就可以直接配置SCON = 0x40;或者是分开来。如果我们需要串口接收数据,就还要加一个REN = 1,即SCON = 0x50.


        PCON配置

        上面讲到说我们的SCON中需要SM0和SM1共同配置我们的串口运作的方式,而不是用来检测错误,我们就需要把PCON中的第六位置为0.

        所以我们就要把SMOD0 = 0先设好。

        而PCON的第七位SMOD是用来设置方式123的波特率是否加倍:

        这一点我们在原理图上也可以看到: 

        我们让SMOD = 0,就说明我们的波特率需要除以2,也就是我们说的“不加倍”,然后SMOD=1就说明我们的波特率不用除以2,也就是要加倍。


         最后还有几个寄存器就简要介绍一下:

SADEN (Slave Address Enable):SADEN是一个特殊功能寄存器,用于启用或禁用I2C总线中从设备的地址匹配。当SADEN为1时,从设备的地址将参与地址匹配过程;当SADEN为0时,该从设备的地址将被忽略。

SADDR (Slave Address):SADDR是用于设置51单片机作为I2C从设备时的从设备地址。通过配置SADDR,可以将从设备的地址与主设备进行匹配,以便进行通信。SADDR的值可以是7位或10位,取决于所使用的I2C工作模式。

SADEN和SADDR都是用于配置51单片机作为I2C从设备时的地址匹配功能。SADEN用于启用或禁用地址匹配,而SADDR用于设置从设备的地址。这些功能使得51单片机能够与其他I2C设备进行通信。

然后就是老生常谈的IE使能位

        这里我们只要把EA和ES单独拿出来配置就好了,两个为0就禁止中断,两个为1就开放中断,EA主要是针对CPU的中断允许控制,ES则是针对串口的中断允许控制只要我们需要开放中断系统,我们就要把这两个都设置为1.

最后就是IP/IPH配置中断优先级,我们只有一个中断的时候就不用管,不用配置都可以,需要的时候按照下面配置一下就好了:

2.2代码配置串口

2.2.1  配置串口发送数据

        我们先根据上面的介绍,配置一个简单的单片机串口向电脑端发送数据的代码。

        首先,我们讲过,我们如果串口只要发送数据,我们就只要配置SCON为0x40,然后我们的PCON中波特率要加倍就把SMOD配置为1,这里我们就不加倍配置为0,然后SMOD0是控制SCON的SM0和SM1对应模式还是检验的前提,我们需要使用SM0和SM1把串口配置为模式1,所以我们的SMOD0必须为0,至于PCON里的其他配置暂时我们先不考虑,我们一会会通过软件生成

就有:        

SCON = 0x40;

        这里我们只要单片机向电脑发送数据,所以我们不需要使用中断,但是我们发送数据需要一定的波特率即发送的速率,这样的速率需要很精确的时钟计时,否则会发生数据传输出错的严重失误。

        我们在这里就使用定时器实现严格的时序控制,从而确保串口通信的稳定性和可靠性。并且我们需要更加精确的定时器,所以我们这里选择定时器1并且使用8位自动重载实现更加精确的计时。

        但是波特率的配置时比较复杂的,所以我们使用软件工具:

        像这样实现了我们的配置之后,我们就可以把代码复制出来使用了。一般来说,波特率设置越高误差越大,当然这个针对其他频率的51单片机而言的,我们的这个11.0592MHz是非常精确的一个频率,所以我们使用9600就可以了,其他频率的单片机比如12MHz的就需要使用4800在点击波特率加倍来使得这个误差减小,我们的11.0592就直接选9600就好了。

生成代码:

void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR &= 0xBF;		//定时器1时钟为Fosc/12,即12T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

同样的,这里有的东西是需要更改的比如这个AUXR是更高级的单片机寄存器,我们就可以删掉,因为我们单片机没有这个东西,SCON可以配置为0x40,取决于自己的需求。

更改之后代码为:

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

        这里我们设置ET1 = 0是因为我们实现单片机发送数据只需要定时器1作为一个时钟计算波特率,而不需要定时器1实现中断,所以就把它置为0禁止定时器1中断。


        配置好初始化函数,我们就再来配置一个发送数据的子函数,使用这个函数就可以在TI发送标志位被置为1的时候发送数据,并且在函数内把它重新置为0。发送数据要我们把数据发送到SBUF寄存器中。这样一个简单的发送数据的函数就写好了:

void SendByte(unsigned char buffer)
{
    SBUF = buffer;
    while(TI== 0);//标志位为0时停留在函数内部
    TI = 0;//在此处TI变为1,我们需要在函数中将其置为0
}

        注意,我们这里需要发送的数据应该是个8位的二进制,又因为我们在单片机中二级制无法直接表示,所以我们转化成十六进制再发送到SBUF中。

#include <REGX52.H>
void UartInit(void)	
{
	PCON &= 0x7F;	
	SCON = 0x50;	
	TMOD &= 0x0F;	
	TMOD |= 0x20;	
	TL1 = 0xFD;	
	TH1 = 0xFD;	
	ET1 = 0;		
	TR1 = 1;		
}

void SendByte(unsigned char buffer)
{
    SBUF = buffer;
    while(TI== 0);
    TI = 0;
}

void main()
{
	UartInit();
	SendByte(0x12);
	while(1)
	{
	}
}

        这里就是一个发送0x12的程序,我们这里没有把发送数据的子函数放在while循环中是因为我们把它放在while循环中就会导致一次发太多数据,不好观察,而且会出错,所以我们把它放在while循环前面,并且使用按下单片机上的复位按键来重置发送数据。

然后打开串口调试小助手,看一下发送的是不是对的。

STC-ISP在这里,要注意这些需要注意的东西,而且使用前串口是关闭的,需要我们手动打开串口,这个串口就是我们用来下载程序的CH340驱动的接口。

PZ-ISP就注意这里:

        大部分的都是一样的,只是两个软件不能同时使用,所以需要注意一下

注意:到这里看一下是不是发送的数据都是想要的值,如果按下复位键还是没有显示,检查一下是不是开启的是字符发送,需要改成HEX才可以看的到,或者是串口没有打开。这里如果显示的和自己的不太一样,检查一下波特率设置是否正确,如果还是不一样,建议重新生成代码,并且检查是不是选择的是定时器1的8位自动重载模式。

如果想要把发送的函数放在while循环中,可能会出现发送的数据不对的情况,这个可能是因为发送的速度太快,可以在函数后面加个Delay函数试试。

2.2.2配置电脑向单片机发送数据点亮LED

        还是按照上面的流程,我们直接在工具里生成代码:这里把多余的寄存器AUXR删除,其他的就不要变了,SCON保持0x50

        或者是直接把前面的代码复制下来,直接把SCON改成0x50也可以

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

        然后这里我们还需要配置串口中断,注意:是串口的中断而不是定时器的中断,即我们的电脑向单片机发送数据之后,单片机接收到这个串口数据的那一时刻,就处理这个信息让这个LED点亮,使用串口的中断会更好实现一点。

        主要是这里几个寄存器,IE使能中,我们只要设置EA和ES为1允许中断即可。

        于是就写好了初始化函数:

void UartInit(void)	
{
	PCON &= 0x7F;	
	SCON = 0x50;	
	TMOD &= 0x0F;	
	TMOD |= 0x20;	
	TL1 = 0xFD;	
	TH1 = 0xFD;	
	ET1 = 0;		
	TR1 = 1;	
	EA = 1;
	ES = 1;
}

然后是中断函数:

        查询中断号,我们可以知道我们要使用的是4号中断。

开始配置函数:

void UART_Routine() interrupt 4
{
	if(RI == 1)
	{
		P2 = SBUF;
		RI = 0;
	}
}

然后我们使用电脑端发送数据就可以控制我们的单片机LED了。这里注意我们要控制这个RI的取值在为1的时候才算接收到数据,之后就要把它重新置为0

  • 26
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值