小灰的51单片机学习之交流吧单片机(8)uart通信传输实例

写在之前

在今天早上新来的时候,打开手机一看,握草,一堆人关注了我,然后兴奋了一整天哈哈哈,还有个人说我写得通俗易懂,这着实给了我动力,大家和我一起加油呐!废话不多说,我们进入正题吧!大家有没有发现我们的单片机到目前为止都是一个“孤儿”,哈哈哈这个词我自己都笑了很久,什么叫孤儿呢,到现在都是自娱自乐,自己玩一玩按键,自己显示,自己中断,自己快快乐乐的活着,然而突然有一天,有另外一个单片机出现在他的生活中,他们俩想要交流,那该怎么办呢?这就涉及到我们接下来一章和接下来几章的内容,通信,以及通信协议,悄咪咪说一句小灰就是学通信的hhh

通信协议

通信协议顾名思义,分为通信和协议,通信分为啥呢,我们人与人之间的交流叫通信,机器与机器之间的交流也叫通信,其实归根结底通信就是俩个人或者机器 人与机器之间的一种信息的交流,而这种交流我们则需要一定的方式,比如说我们不同的的国家有不同的语言,不同的语言则是我们一种约定俗成的交流方式,所以机器之间交流也有一定的方式,这就是协议,所以通信协议也就很好理解了。

如何才能通信

对于我们来说,通信是有三个要素的,分为信源,信道,信宿,woc,这些是啥?小伙伴们不要慌其实很简单。以我们打电话来说A打电话给B,大家都知道电话是通过基站传输的嘛,然后我们信源是什么呢,就是信息的源头,也就是A,信道呢,就是信息的通道,也就是通过电话以及基站传输过去的,信宿就是信息的宿主,其实换言之就是最后,信息到达的终点,也就是我们的B,这样我们才能进行一个简单的通信。

二进制在通信中的角色和地位

我们在前面说过,无论是计算机还是单片机都是用二进制,而二进制就是换言之也是一种高低电平的体现,高电平代表的是1,低电平代表的是0,而0101就是一个数据流,打个比方我用一根线连接俩个机器,我用A机器去拉高或者拉低这个线的电平信号,用B去检测,那么我1010,那么我就传输了一个10过去,再发一个0011,就发了一个3过去这样转换到十六进制就发送了一个A3,而A3又可以转换成ASCII嘛,以及转义成各种字符,那么A和B是不是就进行一个沟通和交流了,是不是觉得很神奇,其实这是最简单的一个通信协议,那么我们就开始今天的正题,uart通信。

uart通信

如果单纯的只说51单片机的通信,在代码里面怎么写,其实不太难,但是如果介绍一些底层的东西可能就比较复杂了,希望大家能看懂更好,看不懂也没有关系,我们首先是需要会写,其次再弄懂原理,这就叫逆向学习,我们不必要像书上那样,也就是前苏联式学习,先弄懂难度高深的公式,再会去用,这就容易让人失去兴趣。当然在讲之前大家还是得需要知道一些必要的知识的。

串行和并行

大家先看下面这幅图(图抄,侵删)
在这里插入图片描述
左边这幅图大家可以看到好多根线连接俩个设备,我们暂时称为设备,其实他也就是我们单片机,右边就俩根线,还有一根地线为了统一基准我们不算他。那么简单了,很多根线就叫并行,俩根线就叫串行了。按照上面的例子我们用一根线就能保证一个信息发送过去,当然接受的那个机器也能接受那根线就行发送和接受,接受就是判断线上的电压高低,接受就是拉高或者拉低他。但是我没办法同时俩个发呀,你会发现俩根线就能解决这个问题。一个用来A发,一个用来B发。这也就是串行通信。那么为什么我们还需要有并行呢?实际上我们在实际运用51单片机的时候你会发现几乎减不动并行通信,这是为啥呢?因为并行如图所示他有很多根线连着,以八根线为例,我一根线发送一个字符,是不是在相同时间内我以相同的速度发送的数据量是一根线的八倍,这也就是他的优点,数据量大的时候发送的比较快,但是同时这也是他的缺点,以51为例 一共只有32个IO口供你使用,结果你还要拿8个来通信,就很占io口的资源,所以说我们在使用 51的时候几乎用不到并行运算。

波特率

我们还需要知道的最后一个概念就是波特率,什么是波特率呢,百度百科给的解释是:波特率表示每秒钟传送的码元符号的个数,是衡量数据传送速率的指标,它用单位时间内载波调制状态改变的次数来表示。其实这么一看大家就又蒙了,其实我个人理解哈,波特率是什么呢。速度是路程除以时间,也就是1秒跑了多少路对吧,波特率就是1秒钟发送了多少个字符,也是变相的也就是反映出我们发送信息的快慢。

到这里为止这是我们所需要知道的一些必要的知识,代码示例如下

#include "reg52.h"

sbit LED= P2^0;
sbit LED1= P2^1;

void InitUART(void);
void SendByte(unsigned char dat);                 
void SendStr(unsigned char *s);
void Receive();
void delay(int x);
char temp;

void main(void)
{
	char a[]={"HELLO"};

	InitUART();
	while (1)
	{
		SendStr(a);
		Receive();
		if (temp == '1')
		{
			LED = 0;
		}
		if (temp == '2')
		{
			LED = 1;
		}
		if (temp == 3)
		{
			LED = 0;
		}
		if (temp == 4)
		{
			LED = 1;
		}
		
		delay(50);
		
	}
	
}

/*-----------------串口初始化-------------------*/
void InitUART(void)
{
    
    SCON = 0x50;		        			// SCON: 模式 1, 8-bit UART, 使能接收  
    TMOD = 0x20;               	// TMOD: timer 1, mode 2, 8-bit 重装
    TH1 = 0xFD;               	// TH1:  重装值 2400 波特率 晶振 11.0592MHz
	TL1 = TH1;							/* 0xE8=1200 0xF4=2400 0xFA=4800 0xFD=9600 11.0592M晶振*/
    TR1 = 1;                 		// TR1:  timer 1 打开                          									
}          

/*-----------------发送一个字节-----------------*/
void SendByte(unsigned char dat)
{
	 SBUF = dat;
	 while(!TI);
	 	TI = 0;
}
/*-----------------发送一个字符串---------------*/
void SendStr(unsigned char *s)
{
	 while(*s!='\0')// \0 表示字符串结束标志,通过检测是否字符串末尾
	 {
	  	SendByte(*s);
	  	s++;
	 }
}
/*-----------------接收到一个字符---------------*/
void Receive()
{
		if (RI == 1)
		{
			RI = 0;	
			temp = SBUF;			
		}
}

void delay(int x)
{
	int i,j;
	for (i = 0 ; i < x ; i++)
	{
		for (j= 0 ; j<122 ; j++);
	}
}

其实大家不要觉得代码很难,如果我们没办法看懂一些人家代码的一些配置和一些奇奇怪怪的东西,我们就努力先看懂人家的逻辑,比如说条件循环这之类的等等。
以这份代码为例,我们下面的这些函数可能看不太懂我们就去先看主函数,主函数有什么,我们一眼看过去就能看见四个if,四个条件判断,判断temp是否是1 2 3 4这四个数,如果是的话,就分别点亮或者关某个led灯,那我咋知道是led灯呢?这时候我们就要通过我们的变量名来判断,很明显,这个变量名是LED,其实由此可知,我们接触到一个新的代码的时候,可能并不是每个地方都有注释,我们可以尝试去看人家的代码逻辑,变量名函数名,来获得信息。我们接着往下看,那这个temp的变量定义在哪呢,一看主函数上面,嗷全局变量,我们接下来就去寻找全局变量在哪里改变了他的值,如果你仔细寻找就不难发现,这个变量的值是在下面的一个函数名为Receive的函数上面改变的有一句话
temp = SBUF,当然我们可能不知道SBUF是什么,我们不用管,结合函数名,我们就知道了这个receive函数的作用,是接受一个字符,好像这么写就能接受一个字符。

这上面是我们的一个逻辑分析过程,如果照这样分析,你会发现虽然你什么都不懂,但是你能写出一份类似的uart的代码,当我们自己写出来并成功了,大家就有信心和耐心去仔细理解底层一点的东西了,并且能结合自己的猜测能更好的理解。
那我们接着往下说,大家可能会发现一些新的东西子啊下面的函数会有一些新的东西比如说TI,RI,这俩个是俩个标志位,比如说我发送完一个字节之后,TI会从0变到1,如果TI没有变1就说明没有发送完,同理RI也是,他是负责接收的,如果RI变为1了就说明接收完了,RI没有变成1就没有接收完,但是这俩个标志位,没有办法有硬件置位为0,所以我们要手动清零,给这个赋值0。其实if 也好while也罢,我们都是要先知道TI或RI等于1之后才给它清零。可能我们在得知他们等于一之后进行某些操作比如说,我们知道RI等于1之后我们就能知道单片机收到一个字节了,这个时候我们就从SBUF中取出这个字节,然后再进行某些操作,就比如说我们这里的temp = SBUF。好的问题来了,SBUF是什么呢?其实SBUF是一个很好玩的东西,他是一堆孪生双胞胎,为什么这么说呢?SBUF是指串行口中的两个缓冲寄存器,一个是发送寄存器,一个是接收寄存器,在物理结构上是完全独立的,但地址是重叠的。它们都是字节寻址的寄存器,字节地址均为99H。实际上人家是当一个缓冲器的作用,打个比方,我单片机收到数据我会先放在SBUF里面,然后再从SBUF中取出,发送也是一样也是先放在SBUF里面,然后SBUF会把数据通过串口发送出去,很明显这是俩个东西对吧,对头,但是人家的名字是一样而且呢,地址也一样,就像孪生双胞胎一样,但是确实是俩个东西,很好玩。接下来我们只剩下一个函数了,也就是InitUART,其实大家看到INIT这种,大都代表着是初始化的意思,比如说这里也就是我们的uart的初始化。在uart传输中我们必须使用定时器1,模式为8位自动重装模式作为波特率发生器,至于啥意思大家就不管了,记住就好。在作为波特率发生器的时候高八位和第八位是相等的所以就会有TH1= TL1,那我们给定时的初值设定为多少呢?在这里小灰就不详细介绍了,大家可能都不太能看得懂,在代码的注释上面有几个常用的波特率的值,我们需要用的时候回来查询一下,然后选择就好了。你看说到这里大家是不是就会有对uart也就是我们说的串口通信有一定的了解了。
这里贴一份串口中断的代码,就不详细介绍了,我相信大家也一定能自己琢磨透

#include "reg52.h"

sbit LED0 = P2^0;
sbit LED1 = P2^1;
sbit LED2 = P2^2;
sbit LED3 = P2^3;

void InitUART(void);
void delay(int x);

char temp = -1;

void main()
{
	InitUART();
	
	while(1)
	{
		if (temp == '0')
		{
			LED0 = ~LED0;
			temp = -1;
		}
	}
}

void delay(int x)
{
	int i,j;
	for (i = 0 ; i < x ; i++)
	{
		for (j= 0 ; j < 122 ; j++);
	}
}

/*-----------------串口初始化-------------------*/
void InitUART(void)
{
    
    SCON = 0x50;		        			// SCON: 模式 1, 8-bit UART, 使能接收  
    TMOD = 0x20;               	// TMOD: timer 1, mode 2, 8-bit 重装
    TH1 = 0xFD;               	// TH1:  重装值 2400 波特率 晶振 11.0592MHz
																	/* 0xE8=1200 0xF4=2400 0xFA=4800 0xFD=9600 11.0592M晶振*/
    TR1 = 1;                 		// TR1:  timer 1 打开                         
    EA = 1;                  	//打开总中断
		ES = 1;											//串口中断
													
}          

/*-----------------串口中断--------------------*/
void UART_SER (void) interrupt 4
{
	if ( RI == 1)                          //检测接收数据
	{				
		RI = 0;															//清零
		temp = SBUF; 												//接收数据	
		SBUF = temp; 												//将接收的数据发送回	
	}
	if (TI == 1)
	{
		delay(1000);
		LED1 = ~LED1;
		TI = 0;
	}
}

写在最后

其实小灰的51单片机学习写到这里,第一阶段就已经结束了,我们已经能基本上使用51单片机的一些基础功能了,想想几篇博客之前大家还在连单片机都不会的,现在都已经能通信了,值得为大家骄傲,我们一定要理论和实际相结合,对于博客不能只看不自己操作,要动手。其实最后一篇博客咕咕咕了好久,其实不是我懒而是我真的不知道怎么将一个很复杂的东西说的比较清楚,不过到最后差强人意,还是写出来了。我们到后面都是使用一些芯片外设比如说pcf8591adc芯片,ds18b20温度,ds1302时钟芯片,还有eeprom这种,为了使用这些芯片我们会用到一些通信协议比如说iic,onewires,spi等等,这些会更难,希望大家继续加油,也非常感谢大家能看我的博客,虽然应该没几个人看哈哈哈。
我是小灰,一个努力用平实的语言写出困难内容的探索者!^ _ ^!

发布了15 篇原创文章 · 获赞 35 · 访问量 3397

猜你喜欢

转载自blog.csdn.net/weixin_44065323/article/details/103057999
今日推荐