通信的基本概念
通信的发展历史 最早通信:烽火台、狼烟;信件;电子通信(电报、电话、网络信号)
通信中最重要的两个方面:(信息表达、分析)和(信息传输) 通信双方需要事先就信息的表达和分析达成一致,否则信息无法有效传递
信号传输方式是指编码后如何在传输介质上传输通信信息的过程。
通信过程分为三个步骤:
1.首先,发送方应根据信息编码编码有效信息(编程成可在通信线路上传输的信号形式)
2.编码后的信息在传输介质上传输给接收方;
3.最后,接收方在收到编码信息后解码,解码后获得可理解的有效信息。
同步通信和异步通信: 同步和异步之间的区别:首先,许多地方都有同步和异步的概念。简单地说,发送人和接收人根据同一时钟节拍工作,称为同步。发送人和接收人没有统一的时钟节拍,每个人根据自己的节拍工作称为异步。 在同步通信中,通信双方按照统一的节奏工作,合作良好;一般来说,发送方需要向接收方发送信息和时钟信号,接收方根据发送方发送的时钟信号安排自己的节奏。同步通信用于固定信息交换频率或定期通信。 异步通信又称异步通知。当双方通信频率不固定时(有时3ms收发一次,有时3天才收发一次)不适合同步通信,适合异步通信。接收方不必总是关心发送方,
电平信号和差分信号: 用于描述通信线路传输的电平信号和差分信号。也就是说,如何在通信线路上表达1和0 电平信号传输线中有一条参考电平线(通常是GND),信号线上的信号值由信号线电平和参考电平线的电压差决定。 差分信号的传输线没有参考电平,都是信号线。然后1和0的表达取决于信号线之间的电压差。 总结:
1.电平信号两条通信线之间的电平差容易受到干扰,传输容易失败;差分信号不易受到干扰,传输质量相对稳定。差分信号通常用于现代通信,几乎没有电平信号。 2.在看似相同根数的通信线下,电平信号比差分信号快;但事实上,差分信号仍然很快。由于差分信号具有较强的抗干扰能力,发送周期较短。
并行接口和串行接口 串行和并行主要考虑通信线的根数,即发送人和接收人可以同时传递多少信息 例如,在电平信号下,一条参考电平线 如果我们有3条线(2条信号线),一条信号线可以传递一位二进制; 1条参考线)可同时发送2位二进制;要同时发送8位二进制,需要9条线。 在差分信号下,两条线(差分)可以同时发送一位二进制;如果需要同时发送8位二进制,则需要16条线。 结论:听起来并行接口比串行接口快(串行接口只能发送一位二进制,并行接口可以发送多位二进制);但事实上,串行接口是用广泛。因为它节省了更多的信号线,而且对传输线的要求更低,成本更低;此外,通过提高通信速度,可以提高整体通信性能,不必并行。
结论:事实上,经过这么多年的发展,最终的胜利是:异步、串行、差异,如USB和网络通信。(目前使用最广泛的两种通信方式)
单工通信和双工通信
全双工:通信双方可以在同一时刻互相传输数据;
半双工:通信双方可以相互传输数据,但必须分时重用一条数据线,即同时只能向一个方向传输;
单工:通信只能由一方发送到另一方,不能反向传输。
常见通信接口:
串口通信
串口是一种应用广泛的通信接口,成本低,使用方便,通信线路简单,可实现两种设备的相互通信。 单片机的串口可以使单片机与单片机、单片机与计算机、单片机与各种模块相互通信,大大扩大了单片机的应用范围,增强了单片机系统的硬件实力。
串行通信功能是SoC提供的(内部)外设CPU无关紧要。各种不同SoC串行通信也差不多。串行通信通常用作主控SoC与其他外部芯片的通信接口。
串口通信的特点:异步、电平信号、串行 异步:串口通信的发送方和接收方之间是没有统一的时钟信号的。 电平信号:串口通信出现的时间较早,速率较低,传输的距离较近,所以干扰还不太明显,因此当时使用了电平信号传输。后期出现的传输协议都改成差分信号传输了。 串行通信:串口通信每次只能同时传输一个二进制位。
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电平电压范围小,适用于距离近、干扰小的情况。 我们台式电脑后面的串口插座是RS工业上使用串口时使用232接口,传输距离小于15米;TTL电平通常用于电路板内的两个芯片之间。 对于编程,RS232电平传输还是TTL电平没有区别。所以电平标准对硬件工程师更有意义,软件工程师只需要稍微了解一下。TTL电平和RS由于电压标准不同,232电平混电压标准不同,会导致数据混乱)
波特率 波特率(bandrate),指串口通信的速率,即串口通信时每秒可以传输多少个二进制位。例如,每秒可以传输9600个二进制位(传输一个二进制位需要1/9600秒,即104秒us),波特率为9600; 串口通信的波特率不能随意设置,而应在某些值中选择。一般最常见的波特率是9600或115200(低端单片机如51常用9600、高端单片机和嵌入式单片机SoC为什么波特率不能随便指定,一般用115200)?主要原因是:首先,通信双方必须提前设置相同的波特率,以便成功通信。如果发送人和接收人根据不同的波特率进行通信,则根本无法接收。因此,波特率最好是众所周知的,而不是随机指定的。二是经过长期发展,常用的波特率形成共识,常用的波特率为9600或115200。
起始位、数据位、奇偶校准位、停止位 串口通信时,收发是一个周期,一个周期,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电平串口,插座用插针插座,每个串口有三条线(Tx、Rx、GND),这些插座可以直接连接到外部TTL电平串口设备。
双方应提前规定通信参数(波特率、数据位、奇偶校准位、停止位等) 串口通信属于基层的基本通信规定。它本身不会协商通信参数。双方需要在通信前事先约定通信参数(通常是四个最重要的) 串口通信的任何关键参数设置错误都会导致通信失败。例如,如果波特率错误,发送方发送没有问题,接收方也可以接收,但收到的都是无序的代码···
信息通过二进制流在信道上传输 串口通信的发送人每隔一段时间(时间固定为1/波特率,单位为秒)将有效信息(1或0)放在通信线上,并逐一发送二进制位。 接收方通过读取通信线上的电平来区分发送给我的是1还是0(从读取到起始位标志开始,间隔由波特率决定)。依次读取数据位、奇偶验证位、停止位,停止位意味着通信单元(帧)结束,然后中间是不确定长度的非通信时间(发送人可送第二帧,也可以长时间不发送第二帧,称为异步通信),第二帧·····
结论:首先,波特率非常重要。如果波特率错了,整个通信就会陷入混乱;数据位置、奇偶验证位置和停止位置也非常重要,否则您可能无法识别数据。第三,无论发送数字、文本、命令或什么,首先编码发送内容,编码成二进制,然后逐个发送。 串口通常发送字符,通常是ASCII编码后的字符,所以数据位一般是8,方便一帧发送一个字符
DB9接口介绍 DB9接口是串口通信早期比较常用的一种规范化接口。 串行通信在早期是计算机与外界通信的主要手段,那时候的计算机都有标准配置的串口以实现和外部通信。那时候就定义了一套标准的串口规约,DB9接口就是标准接口。 DB9接口中有9根通信线,其中3根很重要,为GND、Tx、Rx,必不可少;剩余6根都是和流控有关的,现代我们使用串口都是用来做调试一般都禁用流控,所以这6根没用。 现在一般使用串口时要记得把流控禁止掉,不然可能发生意想不到的问题。
串口及引脚定义:
串口的发送方为主动方,可以直接发送;
接收方可以采用轮询方式接收,也可以采用中断方式接收。
51单片机串口通信
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
简单双向串口通信有两根通信线(发送端TXD和接收端RXD),TXD与RXD要交叉连接,当只需单向的数据传输时,可以直接一根通信线。
另外,当电平标准不一致时,需要加电平转换芯片:
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。
发送数据
/************************************************************ 日期:2022年7月24日 作者:星辰 文件内容:串口通信 **************************************************************/ #include<reg51.h> void SendData(unsigned char dataToSend); void Delay(void); /************************************************************* 函数入口 **************************************************************/ void main(void) { SCON = 0x50; //设置使用模式1,波特率可变的8位UART,接收模式可用 TMOD = 0x20; //配置定时器1处于模式3,8位自动重载,用作波特率发生器 PCON = 0x80; //使用波特率加倍 TH1 = TL1 = 243; //设置波特率为4800Hz TR1 = 1; //打开定时器1 SendData('B'); //发送一个字符 } /************************************************************* 先将数据写入缓冲区 **************************************************************/ void SendData(unsigned char dataToSend) { while(1) { SBUF = dataToSend; //直接把数据扔给硬件即可,之后的由硬件完成 while(!TI); //等待上一个数据发完再发下一轮 TI = 0; //软件复位标志位 Delay(); //必须要延时,速度太快,会出错 } } /************************************************************* 延时 **************************************************************/ void Delay(void) { int i = 0, j = 0; for(i; i < 1000; i++) { for(j; j < 300; j++); } }
要注意几个问题:
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码字符的传输。
波特率计算时,不加倍和加倍时,算出来的重装值不一样。以4800Hz为例,直接不加倍算出来的是249,通过加倍得到的重装值是243。通过实际测试,通过加倍方式得到的重装值更加准确。
/************************************************************ 日期:2022年7月24日 作者:星辰 文件内容:串口通信 **************************************************************/ #include<reg51.h> void SendData(char dataArr[], int len); void Delay(void); /************************************************************* 函数入口 **************************************************************/ void main(void) { char dataArr[] = "天将降大任于斯人也!\n\r"; int len = sizeof(dataArr) / sizeof(dataArr[0]); SCON = 0x50; //设置使用模式1,波特率可变的8位UART,接收模式可用 TMOD = 0x20; //配置定时器1处于模式3,8位自动重载,用作波特率发生器 PCON = 0x80; //使用波特率加倍 TH1 = TL1 = 243; //设置波特率为4800Hz TR1 = 1; //打开定时器1 SendData(dataArr, len); //发送一个字符 } /************************************************************* 先将数据写入缓冲区 **************************************************************/ void SendData(char dataArr[], int len) { while(1) { int i = 0; for(i; i < len; i++) { SBUF = dataArr[i]; //直接把数据扔给硬件即可,之后的由硬件完成 while(!TI); //等待上一个数据发完再发下一轮 TI = 0; //软件复位 Delay(); //必须要延时,速度太快,会出错 } Delay(); } } /************************************************************* 延时 **************************************************************/ void Delay(void) { int i = 0, j = 0; for(i; i < 1000; i++) { for(j; j < 300; j++); } }
要注意,window下的换行并回车的是\n\r。如果只是\n,那么只有换行,不会回车(回到一行的开头)。
在使用串口中断发送字符串的时候,发现一个比较尴尬的问题,就是我发送一个字符串,但是首先得发个任意字符过去触发串口中断或者把字符串的第一个字符先发过去作为触发条件,这样做显得很别扭。
串口工作的两种方式:
查询方式。
硬盘在发送完一帧数据后会将一个标志位置位(标志位本来是0),软件需要不断读取这个标志位的值来判断硬件是否完成了发送(如果读出来是0就表示硬件还在发还没完还在忙,所以我们就不能认为硬件发完了,就不能给硬件安排下一帧数据的发送;如果读出来的是1则说明硬件已经发完了上一帧数据,这时候软件就应该给硬件再给一帧数据去发送)。因为串口发送完这个事件对CPU来说是个异步事件,所以这里查询方式来处理和之前讲过的查询方式处理按键是非常类似的。 中断方式。
查询方式处理的劣势是CPU必须一直守着串口发送,在串口发送完所有字节之前CPU不能离开去做别的事情,这对CPU来说是极大的浪费(因此CPU的速度比串口发送的速度快多了)。因此用中断方式来处理串口发送是非常合适的,可以提升CPU使用率。
其实,在平时的工作中,串口发送会使用查询方式,而串口接收会使用中断方式。
接收数据
接收数据,使用中断方式,代码实现如下:
/************************************************************ 日期:2022年7月24日 作者:星辰 文件内容:串口通信 **************************************************************/ #include<reg51.h> void SendData(unsigned char dataToSend); void Delay(void); /************************************************************* 函数入口 **************************************************************/ void main(void) { SCON = 0x50; //设置使用模式1,波特率可变的8位UART,接收模式可用 TMOD = 0x20; //配置定时器1处于模式3,8位自动重载,用作波特率发生器 PCON = 0x80; //使用波特率加倍 TH1 = TL1 = 243; //设置波特率为4800Hz TR1 = 1; //打开定时器1 ES = 1; //串口中断 EA = 1; //中断总开关 while(1); } void UartIntr() interrupt 4 using 1 { if(RI) { unsigned char dataToSend= SBUF; SBUF = dataToSend; RI = 0; } }
测试是否接收到时,使用了回环测试,即将接收到的数据,又发送到电脑端显示。
注意,要打开串口,别没打开在那发送,肯定接收不到。
补充
在串口工具显示中,可以选择是以字符形式显示,还是十六进制的形式来显示,比如字符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来说是透明的。
更多详细内容,可以自行查阅资料。