当前位置:   article > 正文

51单片机内部外设:串口通信_单片机无线传输模块串口通信。加键盘,显示等

单片机无线传输模块串口通信。加键盘,显示等

通信基础概念

通信中最重要的两个方面:

  • 信息表示和解析方法;
  • 信息的传输方法。

通信双方事先需要约定好信息的表示方法和解析方法,做到一致,否则信息不能有效传递。

信号的传输方法是指经过编码后的通信信息如何在传输介质上传输的过程。


通信过程分为3个步骤:

1、首先发送方先按照信息编码方式对有效信息进行编码(编程成可以在通信线路上传输的信号形态)

2、然后编码后的信息在传输介质上进行传输,输送给接收方;

3、最后接收方接收到编码信息后进行解码,解码后得到可以理解的有效信息。

同步通信和异步通信

这两者最明显的区别就是在通信时,是否有统一的时钟节拍。

同步通信如IIC以及SPI等,除了传输数据,还需要同时传输时钟信号,然后通信双方在统一的时钟下进行数据传输,其中严格定义了时钟的电平和边沿触发等,什么时候开始传输,什么时候结束,都需要在特定的时钟周期内完成。

而异步传输时,不需要统一的时钟信号的参与,发送方想什么时候发,就什么时候发,双方的工作可以不同步。发送方需要发送信息时会首先给接收方一个信息开始的起始信号,接收方接收到起始信号后就认为后面紧跟着的就是有效信息,才会开始注意接收信息,直到收到发送方发过来的结束标志。有可能发送方一个周期发送一位数据,但是到了接收方那边,因为频率不同,可能5个周期才能接收到一位数据。相对来说,异步通信更容易出错。

电平信号和差分信号

电平信号和差分信号是用来描述通信线路传输方式的。也就是说如何在通信线路上表达1和0
电平信号的传输线中有一个参考电平线(一般是GND),然后信号线上的信号值是由信号线电平和参考电平线的电压差决定。
差分信号的传输线中没有参考电平,所有都是信号线。然后1和0的表达靠信号线之间的电压差。
总结:

1、电平信号的2根通信线之间的电平差异容易受到干扰,传输容易失败;差分信号不容易受到干扰因此传输质量比较稳定,现代通信一般都使用差分信号,电平信号几乎没有了。
2、看起来似乎相同根数的通信线下,电平信号要比差分信号要快;但是实际还是差分信号快,因为差分信号抗干扰能力强,因此1个发送周期更短。

并行接口和串行接口

串行、并行主要是考虑通信线的根数,就是发送方和接收方同时可以传递的信息量的多少
譬如在电平信号下,1根参考电平线+1根信号线可以传递1位二进制;如果我们有3根线(2根信号线+1根参考线)就可以同时发送2位二进制;如果想同时发送8位二进制就需要9根线。
在差分信号下,2根线(彼此差分)可以同时发送1位二进制;如果需要同时发送8位二进制,需要16根线。
总结:听起来似乎并行接口比串行接口要快(串行接口一次只能发送1位二进制,而并行接口一次可以发送多位二进制)要更优秀;但是实际上串行接口才是王道,用的比较广。因为更省信号线,而且对传输线的要求更低、成本更低;而且串行时可以通过提高通信速度来提高总体通信性能,不一定非得要并行。

总结:其实这么多年发展,最终胜出的是:异步、串行、差分,譬如USB和网络通信。(当前使用最广泛的两种通信方式)

单工通信和双工通信

全双工:有2根数据线,通信双方可以在同一时刻互相传输数据;

半双工(准双工):通信双方可以互相传输数据,但必须分时复用一根数据线,即同一时刻只能一个方向传输;

单工:通信只能有一方发送到另一方,不能反向传输。

常见通信接口:

串口通信

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。

串行通信功能是SoC的一个(内部)外设提供的,与CPU本身无关。各种不同SoC的串行通信大同小异。串行通信经常作为主控SoC与其他外部芯片之间的通信接口。

串口通信的特点:异步、电平信号、串行
异步:串口通信的发送方和接收方之间是没有统一的时钟信号的。
电平信号:串口通信出现的时间较早,速率较低,传输的距离较近,所以干扰还不太明显,因此当时使用了电平信号传输。后期出现的传输协议都改成差分信号传输了。
串行通信:串口通信每次同时只能传输1个二进制位。

RS232电平和TTL电平
电平信号是用信号线电平减去参考线电平得到电压差,这个电压差决定了传输值是1还是0
电平信号是多少V时代表1,多少V时代表0不是固定的,取决于电平标准。

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下两种:

1、TTL电平:+5V表示1,0V表示0(电平信号)

2、RS232电平:-3~-15V表示1,+3~+15V表示0(电平信号)


不管哪种电平都是为了在传输线上表示1和0。区别在于适用的环境和条件不同。RS232的电平定义比较大,适合干扰大、距离远的情况;TTL电平电压范围小,适合距离近且干扰小的情况。
我们台式电脑后面的串口插座就是RS232接口的,在工业上用串口时都用这个,传输距离小于15米;TTL电平一般用在电路板内部两个芯片之间。
对编程来说,RS232电平传输还是TTL电平是没有差异的。所以电平标准对硬件工程师更有意义,而软件工程师只要略懂即可。(把TTL电平和RS232电平混接是不可以的,因为电压标准不一样,所以会导致数据混乱)

波特率
波特率(baudrate),指的是串口通信的速率,也就是串口通信时每秒钟可以传输多少个二进制位。譬如每秒种可以传输9600个二进制位(传输一个二进制位需要的时间是1/9600秒,也就是104us),波特率就是9600;
串口通信的波特率不能随意设定,而应该在一些值中去选择。一般最常见的波特率是9600或者115200(低端单片机如51常用9600,高端单片机和嵌入式SoC一般用115200),为什么波特率不可以随便指定?主要是因为:第一,通信双方必须事先设定相同的波特率这样才能成功通信,如果发送方和接收方按照不同的波特率通信则根本收不到,因此波特率最好是大家熟知的而不是随意指定的。第二,常用的波特率经过长久发展,就形成了共识,大家常用就是9600或者115200。

串口的波特率越高,速度越快,但是相应干扰越多,越不稳定,能传输的距离就越短;波特率低,干扰相对较少,但是速度慢。所以,要根据实际情况选择合适的波特率,常用的有115200;9600;4800。

起始位、数据位、奇偶校验位、停止位
串口通信时,收发是一个周期一个周期进行的,每周期传输n个二进制位。这一个周期就叫做一个通信单元,一个通信单元是由:起始位+数据位+奇偶校验位+停止位组成的。
起始位表示发送方要开始发送一个通信单元;数据位是一个通信单元中发送的有效信息位;奇偶校验位是用来校验数据位,以防止数据位出错的;停止位是发送方用来表示本通信单元结束标志的。


起始位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。
数据位是本次通信真正要发送的有效数据,串口通信一次发送多少位有效数据是可以设定的(一般可选的有6、7、8、9,99%情况下我们都是选择8位数据位。因为我们一般通过串口发送的文字信息都是ASCII码编码的,而ASCII码中一个字符刚好编码为8位。)
奇偶校验位是用来给数据位进行奇偶校验(把待校验的有效数据逐个位的加起来,总和为奇数,奇偶校验位就为1,总和为偶数,奇偶校验位就为0)的,可以在一定程度上防止位反转。
停止位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。常见的有1位停止位,1.5位停止位,2位停止位等。99%情况下都是用1位停止位。

总结:串口通信时因为是异步通信,所以通信双方必须事先约定好通信参数,这些通信参数包括:波特率、数据位、奇偶校验位、停止位(串口通信中起始位定义是唯一的,所以一般不用选择)


三根通信线:Rx Tx GND
任何通信都要有信息传输载体,或者是有线的或者是无线的。
串口通信是有线通信,是通过串口线来通信的。
串口通信线最少需要2根(GND和信号线),可以实现半双工通信,也可以使用3根通信线(Tx、Rx、GND)来实现全双工。
一般开发板都会引出SoC上串口引脚直接输出的TTL电平的串口,插座用插针式插座,每个串口引出的都有3个线(Tx、Rx、GND),可以用这些插座直接连接外部的TTL电平的串口设备。

收发双方事先规定好通信参数(波特率、数据位、奇偶校验位、停止位等)
串口通信属于基层基本性的通信规约,它自己本身不会去协商通信参数,需要通信前通信双方事先约定好通信参数(一般4个最重要的)
串口通信的任何一个关键参数设置错误,都会导致通信失败。譬如波特率调错了,发送方发送没问题,接收方也能接收,但是接收到全是乱码···

信息以二进制流的方式在信道上传输
串口通信的发送方每隔一定时间(时间固定为1/波特率,单位是秒)将有效信息(1或者0)放到通信线上去,逐个二进制位的进行发送。
接收方通过定时(起始时间由读到起始位标志开始,间隔时间由波特率决定)读取通信线上的电平高低来区分发送给我的是1还是0。依次读取数据位、奇偶校验位、停止位,停止位就表示这一个通信单元(帧)结束,然后中间是不定长短的非通信时间(发送方有可能紧接着就发送第二帧,也可能半天都不发第二帧,这就叫异步通信),下来就是第二帧·····


总结:第一,波特率非常重要,波特率错了整个通信就乱套了;数据位、奇偶校验位、停止位也很重要,否则可能认不清数据。第三,通过串口不管发数字、还是文本还是命令还是什么,都要先对发送内容进行编码,编码成二进制再进行逐个位的发送。
串口发送的一般都是字符,一般都是ASCII码编码后的字符,所以一般设置数据位都是8,方便刚好一帧发送1个字符。

DB9接口介绍
DB9接口是串口通信早期比较常用的一种规范化接口。
串行通信在早期是计算机与外界通信的主要手段,那时候的计算机都有标准配置的串口以实现和外部通信。那时候就定义了一套标准的串口规约,DB9接口就是标准接口。
DB9接口中有9根通信线,其中3根很重要,为GND、Tx、Rx,必不可少;剩余6根都是和流控有关的,现代我们使用串口都是用来做调试一般都禁用流控,所以这6根没用。
现在一般使用串口时要记得把流控禁止掉,不然可能发生意想不到的问题。

串口及引脚定义:

注意:

串口的发送方为主动方,可以直接发送;

接收方可以采用轮询方式接收,也可以采用中断方式接收。

51单片机串口通信

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

简单双向串口通信有两根通信线(发送端TXD和接收端RXD),TXD与RXD要交叉连接,当只需单向的数据传输时,可以直接一根通信线。

另外,当电平标准不一致时,需要加电平转换芯片:

STC89C52有1个UART

STC89C52的UART有四种工作模式:

模式0:同步移位寄存器

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

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

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

更多具体内容参考收据手册。

串口接线

第一种:

直接使用USB转TTL串口线连接到单片机的的UART引脚上。

第二种:

通过DB9标准的USB转串口线。

DB9由转换芯片如MAX232等芯片电路实现。

MAX232

当用单片机和PC机通过串口进行通信,尽管单片机有串行通信的功能,但单片机提供的信号电平和RS232的标准不一样,因此要通过max232这种类似的芯片进行电平转换。

MAX232芯片的作用:是将单片机输出的TTL电平转换成PC机能接收的232电平或将PC机输出的232电平转换成单片机能接收的TTL电平。

第三种:

使用USB转串口芯片,如CH340

CH340是一个USB总线的转接芯片,实现USB转串口或者USB转打印口。

本文使用第三种方式。

串口监视工具

usb既不是串口也不是并口,是一种独立的接口技术,usb基于串口通信,但不是串口,usb是通用串行总线的意思,一个是狭义的,一个是广义的,电脑的串口是9针的,USB是平口的。

串口是一种硬件通信口,很多年前的时候串口是CPU之间进行通信的主要接口。但是现在因为串口通信的速度很低,所以现在串口主要是用来做程序输出监控、调试。


桌面电脑可以打开一个虚拟控制台,嵌入式系统一般是用串口来做控制台的。一般是用一根串口线连接开发板的串口和我们笔记本电脑的串口,然后在电脑上打开一个串口监视,这样开发板上的串口输出内容就可以在电脑上看到。还可以通过监视终端向开发板输入一些控制命令由开发板执行。常用的串口监视软件有:超级终端、SecureCRT、minicom。

本来电脑都是有串口的(DM9接口),但是现在大家都用笔记本没有串口了,所以这种串口连接线用不了。办法是使用USB转串口线,这种线传入电脑后需要安装驱动,安装驱动后在电脑上会形成一个串口(叫usb转串口),这样就相当于你电脑有了一个串口,可以通过这个串口来监视开发板的串口输出。


注意

1:windows对USB设备的管理是和USB口有关的,你每次把usb转串口线插到1个口中,这样得到的COM口号码是不变的,方便我们后期使用。如果每次胡乱更换插口,可能得到的COM口会变。
2:COM口号码是可以改的,还可以强制占用显示“已使用”的COM号。

如果显示乱码,可以尝试更改编码方式(默认是UTF-8):

点击Options --> 选择Session Options --> 点击Appearance。
请添加图片描述

接着设置编码方式为GB2312。

发送数据

单片机发送1个字符数据

  1. /************************************************************
  2. 日期:2022724
  3. 作者:星辰
  4. 文件内容:串口通信
  5. **************************************************************/
  6. #include<reg51.h>
  7. void SendData(unsigned char dataToSend);
  8. void Delay(void);
  9. /*************************************************************
  10. 函数入口
  11. **************************************************************/
  12. void main(void)
  13. {
  14. SCON = 0x50; //设置使用模式1,波特率可变的8位UART,接收模式可用
  15. TMOD = 0x20; //配置定时器1处于模式38位自动重载,用作波特率发生器
  16. PCON = 0x80; //使用波特率加倍
  17. TH1 = TL1 = 243; //设置波特率为4800Hz
  18. TR1 = 1; //打开定时器1
  19. SendData('B'); //发送一个字符
  20. }
  21. /*************************************************************
  22. 先将数据写入缓冲区
  23. **************************************************************/
  24. void SendData(unsigned char dataToSend)
  25. {
  26. while(1)
  27. {
  28. SBUF = dataToSend; //直接把数据扔给硬件即可,之后的由硬件完成
  29. while(!TI); //等待上一个数据发完再发下一轮
  30. TI = 0; //软件复位标志位
  31. Delay(); //必须要延时,速度太快,会出错
  32. }
  33. }
  34. /*************************************************************
  35. 延时
  36. **************************************************************/
  37. void Delay(void)
  38. {
  39. int i = 0, j = 0;
  40. for(i; i < 1000; i++)
  41. {
  42. for(j; j < 300; j++);
  43. }
  44. }

要注意几个问题:

1、

每帧数据发完之后必须要有延时,要不然很容易出错;

2、

上面说了,主从机要协商好波特率、数据位、奇偶校验位和停止位。波特率软件中有体现,数据位也有体现,奇偶校验位不需要,可是,没看到哪里体现了停止位。

关于串口通信的停止位,有1、1.5、2个停止位三种,是说一帧数据传送完,持续1个、1.5个、2个高电平时,视为停止位。

所以,是不是数据位传送完,下一个高电平就默认为停止位呢?这样,两帧数据之间的延时要大于一个传送周期???????????才能保证不出错。

比如,9600的波特率,那么每1/9600秒传送一个二进制数,大约是105us传送一个二进制数。那么,1个停止位的时间就是105us。那么,当延时150us之后,会不会出错呢?试一试:经测试,好像没有必然联系。

由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。

这里的具体细节,暂时不纠结了。

3、

电脑上显示的都是字符串,如果传送的是一个数据呢?我试了下,传了1个int类型的100,结果在电脑上显示的是小写字母d,即ASCLL码中100的对应字符。

典型地,串口用于ASCLL码字符的传输。

4

注意注意注意,波特率计算时,不加倍和加倍时,算出来的重装值不一样。以4800Hz为例,直接不加倍算出来的是249,通过加倍得到的重装值是243。通过实际测试,通过加倍方式得到的重装值更加准确。

发送字符串

  1. /************************************************************
  2. 日期:2022724
  3. 作者:星辰
  4. 文件内容:串口通信
  5. **************************************************************/
  6. #include<reg51.h>
  7. void SendData(char dataArr[], int len);
  8. void Delay(void);
  9. /*************************************************************
  10. 函数入口
  11. **************************************************************/
  12. void main(void)
  13. {
  14. char dataArr[] = "天将降大任于斯人也!\n\r";
  15. int len = sizeof(dataArr) / sizeof(dataArr[0]);
  16. SCON = 0x50; //设置使用模式1,波特率可变的8位UART,接收模式可用
  17. TMOD = 0x20; //配置定时器1处于模式38位自动重载,用作波特率发生器
  18. PCON = 0x80; //使用波特率加倍
  19. TH1 = TL1 = 243; //设置波特率为4800Hz
  20. TR1 = 1; //打开定时器1
  21. SendData(dataArr, len); //发送一个字符
  22. }
  23. /*************************************************************
  24. 先将数据写入缓冲区
  25. **************************************************************/
  26. void SendData(char dataArr[], int len)
  27. {
  28. while(1)
  29. {
  30. int i = 0;
  31. for(i; i < len; i++)
  32. {
  33. SBUF = dataArr[i]; //直接把数据扔给硬件即可,之后的由硬件完成
  34. while(!TI); //等待上一个数据发完再发下一轮
  35. TI = 0; //软件复位
  36. Delay(); //必须要延时,速度太快,会出错
  37. }
  38. Delay();
  39. }
  40. }
  41. /*************************************************************
  42. 延时
  43. **************************************************************/
  44. void Delay(void)
  45. {
  46. int i = 0, j = 0;
  47. for(i; i < 1000; i++)
  48. {
  49. for(j; j < 300; j++);
  50. }
  51. }

要注意,window下的换行并回车的是\n\r。如果只是\n,那么只有换行,不会回车(回到一行的开头)。

使用串口中断发送字符串

在使用串口中断发送字符串的时候,发现一个比较尴尬的问题,就是我发送一个字符串,但是首先得发个任意字符过去触发串口中断或者把字符串的第一个字符先发过去作为触发条件,这样做显得很别扭。

串口工作的两种方式:

查询方式。

硬盘在发送完一帧数据后会将一个标志位置位(标志位本来是0),软件需要不断读取这个标志位的值来判断硬件是否完成了发送(如果读出来是0就表示硬件还在发还没完还在忙,所以我们就不能认为硬件发完了,就不能给硬件安排下一帧数据的发送;如果读出来的是1则说明硬件已经发完了上一帧数据,这时候软件就应该给硬件再给一帧数据去发送)。因为串口发送完这个事件对CPU来说是个异步事件,所以这里查询方式来处理和之前讲过的查询方式处理按键是非常类似的。
中断方式。

查询方式处理的劣势是CPU必须一直守着串口发送,在串口发送完所有字节之前CPU不能离开去做别的事情,这对CPU来说是极大的浪费(因此CPU的速度比串口发送的速度快多了)。因此用中断方式来处理串口发送是非常合适的,可以提升CPU使用率。

其实,在平时的工作中,串口发送会使用查询方式,而串口接收会使用中断方式。

接收数据

接收数据,使用中断方式,代码实现如下:

  1. /************************************************************
  2. 日期:2022724
  3. 作者:星辰
  4. 文件内容:串口通信
  5. **************************************************************/
  6. #include<reg51.h>
  7. void SendData(unsigned char dataToSend);
  8. void Delay(void);
  9. /*************************************************************
  10. 函数入口
  11. **************************************************************/
  12. void main(void)
  13. {
  14. SCON = 0x50; //设置使用模式1,波特率可变的8位UART,接收模式可用
  15. TMOD = 0x20; //配置定时器1处于模式38位自动重载,用作波特率发生器
  16. PCON = 0x80; //使用波特率加倍
  17. TH1 = TL1 = 243; //设置波特率为4800Hz
  18. TR1 = 1; //打开定时器1
  19. ES = 1; //串口中断
  20. EA = 1; //中断总开关
  21. while(1);
  22. }
  23. void UartIntr() interrupt 4 using 1
  24. {
  25. if(RI)
  26. {
  27. unsigned char dataToSend= SBUF;
  28. SBUF = dataToSend;
  29. RI = 0;
  30. }
  31. }

测试是否接收到时,使用了回环测试,即将接收到的数据,又发送到电脑端显示。

注意,要打开串口,别没打开在那发送,肯定接收不到。

补充

在串口工具显示中,可以选择是以字符形式显示,还是十六进制的形式来显示,比如字符a,是就显示字符a,还是说显示对应的十六进制数61。

这里是工具帮我们实现的,但是,我们自己怎么实现呢?暂时不赘述。

RS485

UART的缺点:传输距离受限

  • 理论上RS232不超过15米
  • 理论上TTL电平通信距离更短
  • 实际上几百米也有人宣称做到了,但是稳定性不能保证
  • 波特率越高通信距离越近

远距离传输怎么办?

  • 提高电压标准
  • 提高通信线抗干扰能力、降低阻抗
  • 使用差分信号

RS485(RS422)

  • 最大通信距离1200多米,最快通信速率10Mbps,距离和速度成反比
  • 差分信号负逻辑
  • 更远距离可以加中继器
  • 半双工
  • RS485只提供物理层通信能力,不提供数据层协议,需要用户自定义,或者使用标准协议如MODBUS协议。

MAX485介绍
CPU本身只会提供UART接口,而不会提供RS485接口。CPU根本不认识RS485。

RS485使用时场景是:CPUA->UART转RS485------远距离通信-----RS485转UART->CPUB

对RS485的理解,应该是这样的:RS485是纯硬件实现的,使用硬件芯片如MAX485来管理的,根本不涉及软件编程。软件工程师只关注串口,只通过串口将数据发送出去或者接收回来即可。UART转485和485转UART对CPU来说是透明的。

更多详细内容,可以自行查阅资料。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/84221
推荐阅读
相关标签
  

闽ICP备14008679号