串口协议和USART串口外设

447 阅读19分钟

1.基本知识

  • 通信的目的:将一个设备的信息传送到另外一个设备;拓展硬件系统

  • 通信协议:指定通信规则,通信双方按照规则进行信息收发

  • 双工

    • 全双工:全双工通信允许数据在两个方向上同时传输
    • 半双工:半双工通信允许数据在两个方向上交替传输但不能同时进行。
  • 时钟

    • 同步:在同步系统中,所有的操作和数据传输都依赖于一个共同的时钟信号。所有的设备在同一时刻接收和发送数据。 (适用于需要高精度和高一致性的应用,如计算机内部的通信。)

    • 异步:在异步系统中,数据传输不依赖于共同的时钟信号,而是通过起始位和停止位来标识数据的开始和结束。 (数据可以在任意时间发送,接收方通过起始位识别数据的到来。适用于不需要严格时序的应用,如串口通信(UART)。   可能会导致数据传输的延迟和不一致性,但实现简单。)

  • 电平

    • 差分:差分信号是通过两根信号线传输的信号的电压是通过两根线之间的电压差来表示的
    • 单端:单端信号是相对于地(或参考电平)进行测量的信号。信号的电压值是相对于地电位的。

2.串口通信

3.硬件电路

  • VCC(电源) 如果两个设备都有独立供电,那VCC可以不接。 如果其中一个设备没有供电,比如这里设备1是STM32,设备2是蓝牙串口模块,STM32有独立供电,蓝牙串口没有独立供电,需要把蓝牙串口的VCC和STM32的VCC接在一起,STM32通过这根线,向右边的子模块供电,当然,供电的电压也需要注意一下,要按照子模块的要求来,这就是供电要求。
  • TX,RX TX是发送,RX是接收,要交叉连接,是一个设备的发送接另一个设备的接收 。

如只需要设备1向设备2单向通信,那就可以只接一根TX到RX的线,另一根不接,这就是单工的通信方式。

4.串口参数与时序

  • 介绍:串口中,每一个字节都装载在一个数据帧里面,每个数据帧由起始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。 左边的是数据位有8位,代表一个字节。右边的数据帧里面,还可以再数据位的最后面加一个奇偶校验位,校验位可以加可不加

  • 校验位:用于数据验证,是根据数据位计算得来的。这里串口,使用的是一种叫奇偶校验的数据验证方法,奇偶校验可以判断数据传输是不是出错了。如果数据出错了,可以选择丢弃或者要求重传,校验可以选择3种方式,无校验,奇校验,偶校验。

    • 1.无校验:就是不需要校验位,波形不发生改变,起始位、数据位、停止位,总共3个部分
    • 2.奇校验:是起始位、数据位、校验位、停止位。要求有效数据和校验位中“1”的个数为奇数,比如一个8位长的有效数据为:01 10 10 01,此时总共有4个“1”, 为达到奇校验效果,校验位为“1”,最后传输的数据将是8位的有效数据加上1位的校验位总共9位
    • 3.偶校验:与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数, 比如数据帧:11001010,此时数据帧“1”的个数为4个,所以偶校验位为“0”。
  • 当然奇偶校验的检出率并不是很高,比如如果有两位数据同时出错。奇偶特性不变,那就校验不出来了,所以奇偶校验只能保证一定程度上的数据校验。 如果想要更高的检出率,可以了解一下CRC校验,这个校验会更加好用,当然也会更复杂。我们这个STM32内部也有CRC的外设,可以了解一下,那到这里,串口的时序我们就了解了。

5.串口实际波形

在stm32中如图上的根据字节反转高低电平时USART自己完成。也可以自己软件模拟产生,首先,定时器顶一个104us的时间,时间到后,按照数据帧的要求,调用GPIO——WriteBit置高低电平,定时调用GPIO——ReadInputDataBit来读取

总结
  • TX引脚输出定时翻转的高低电平,RX引脚定时读取引脚的高低电平。每个字节的数据加上起始位、停止位、可选的校验位,打包为数据帧,依次输出在TX引脚,另一端RX引脚依次接收,这样就完成了字节数据的传递,这就是串口通信


6.USART简介

  • USART(通用同步/异步收发器):我们经常还会遇到串口,叫UART,少了个S,就是通用异步收发器,一般我们串口很少使用这个同步功能,所以USART和UART使用起来,也没有什么区别。
  • 其实这个STM32的USART同步模式,只是多了个时钟输出而已(它只支持时钟输出,不支持时钟输入)所以这个同步模式更多的是为了,兼容别的协议或者特殊用途而设计的,并不支持两个USART之间进行同步通信。所以我们学习串口,主要还是异步通信。
  • 波特率发生器:就是用来配置波特率的,其实是一个分频器,比如我们APB2总线给个72MHZ的频率,然后波特器进行一个分频,得到我们想要的波特率时钟,最后在这个时钟下,进行收发,就是我们指定的通信波特率。
  • 硬件流控制:比如A设备的TX脚向B设备的RX脚发送数据,A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制,那B就只能抛弃新数据或者覆盖原数据了。如果有硬件流控制,在硬件电路上,会多出一根线,如果B没准备好接收,就置高电平,如果准备好了,就置低电平。A接收到了B反馈的准备信号,就只会在B准备好的时候,才发数据,如果B没准备好,那数据就不会发送出去,这就是硬件流控制,可以防止因为B处理慢而导致数据丢失的问题。
  • USART1是APB2总线上的设备,USART2、 USART3是APB1总线上的设备。

7.USART基本电路


1.引脚部分

  • TX发送数据输出引脚。
  • RX接收数据输入引脚。
  • SCLK: 发送器时钟输出引脚。这个引脚仅适用于同步模式。
  • SW_RX,IRDA_OUT,IRDA_IN:是智能卡和IRDA的通讯的引脚

2.寄存器

  • USART_DR包含了已发送的数据或者接收到的数据。USART_DR实际是包含了两个寄存器,一个专门用于发送的可写TDR, 一个专门用于接收的可读RDR。 这两个寄存器占用同一个地址,在程序上,只表现为一个寄存器-数据寄存器DR
  • 当进行发送操作时,往USART_DR写入数据会自动存储在TDR内;当进行读取操作时,向USART_DR读取数据会自动提取RDR数据。TDR和RDR都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把TDR内容转移到发送移位寄存器, 然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到RDR。
  • 需要注意每次数据从数据寄存器移动到移位寄存器后,数据寄存器都会置一个标志位,例如:TXE,当要写入下一个数据时,便需要检查TXE==1,然后才可写入。相反接收数据寄存器则是置标志位 RXNE,即接受寄存器非空,但检测到RXNE,便可以读取数据。提高了工作效率

3.流控

image.png

  • 流件:硬件数据流控制

有两个引脚,一个是nRTS,一个是nCTS。nRTS(Request To Send)是请求发送,是输出脚,也就是告诉别人,我当前能不能接收;nCTS (Clear To Send)是清除发送,是输入脚,也就是用于接收别人nRTS的信号的。这里前面加个n意思是低电平有效。不常用。

4.SCLK控制

image.png

  • 简介:这部分电路用于产生同步的时钟信号,它是配合发送移位寄存器输出的,发送寄存器每移位一次,同步时钟电平就跳变一个周期。时钟告诉对方,我移出去一位数据,你看要不要让我这个时钟信号来指导你接收一下?当然这个时钟只支持输出,不支持输入,所以两个USART之间,不能实现同步的串口通信。
  • 那这个时钟信号的作用:
    • 兼容别的协议。比如串口加上时钟之后,就跟SPI协议特别像,所以有了时钟输出的串口,就可以兼容SPI。
    • 另外这个时钟也可以做自适应波特率,比如接收设备不确定发送设备给的什么波特率,然后再计算得到波特率,不过这就需要另外写程序来实现这个功能了。这个时钟功能,我们一般不用,所以也是了解一下就行。

5.唤醒单元

image.png

  • 作用:实现串口挂载多设备。串口一般是点对点的通信(只支持两个设备互相通信)。而多设备,在一条总线上,可以接多个从设备,每个设备分配一个地址,我想跟某个设备通信,就先进行寻址,确定通信对象,再进行数据收发。了解即可。

6.波特率发生器

  • 波特率发生器其实就是分频器,APB时钟进行分频,得到发送和接收移位的时钟。这里时钟输入是fPCLKx(x=1或2)(USART1挂载在APB2,所以就是PCLK2的时钟,一般是72M;其他的USART都挂载在APB1,所以是PCLK1的时钟,一般是36M)。

  • 之后这个时钟进行一个分频,除一个USARTDIV的分频系数,并且分为了整数部分和小数部分,因为有些波特率,用72M除一个整数的话,可能除不尽,会有误差。 所以这里分频系数是支持小数点后4位的,分频就更加精准,之后分频完之后,还要再除个16,得到发送器时钟和接收器时钟,通向控制部分。

  • 然后右边这里,如果TE (TX Enable)为1,就是发送器使能了,发送部分的波特率就有效;如果RE(RX Enable)为1,就是接收器使能了,接收部分的波特率就有效。 image.png

  • 波特率=FCLK/(16*DIV) 16是因为在数据采样部分进行了标准的 UART/USART 采用 16 倍过采样,这意味着每一个数据位都会被采样 16 次。

8.USART基本结构

image.png

  • 最左边这里是波特率发生器,用于产生约定的通信速率,时钟来源是PCLK2/1(USART1使用PCLK2=72M,USART2,USART3使用PCLK1=36M),经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器。发送控制器和接收控制器,用来控制发送移位和接收移位。

  • 之后,由发送教据寄存器和发送移位寄存器这两个寄存器的配合,将数据一位一位地移出去,通过GPIO的复用输出,输出到TX引脚,产生串口协议规定的波形,这里画了几个右移的符号,就是代表这个移位寄存器是往右格的,是低位先行。

  • 当数据由数据寄存器转到移位寄存器时,会置一个TXE的标志位,我们判断这个标志位,就可以知道是不是可以写下一个数据。然后接收部分也是类似的,RX引脚的波形,通过GPIO输入,在接收控制器的控制,下,一位一位地移入接收移位寄存器,这里画了右移的符号,也是右移的,因为是低位先行,所以要从左边开始移进来,移完一帧数据后, 数据就会统一转运到接收数据寄存器,在转移的同时,会置一个RXNE标志位,我们检查这个标志位,就可以知道是不是收到数据了。同时这个标志位也可以去申请中断,这样就可以在收到数据时,直接进入中断函数,然后快速地读取和保存数据。

  • 那右边这实际上有四个寄存器,但在软件层面,只有一个DR寄存器可以供我们读写,写入DR时,数据走上面这条路,进行发送,读取DR时,数据走下面这条路,进行接收,这就是USART进行串口数据收发的过程。

9.数据帧

  • M 位 表示 数据帧的长度模式,也就是数据帧中数据位的长度。

  • 那看一下上面这个9位字长的波形,第一条时序很明显就是TX发送或者RX接收的数据帧格式。 空闲高电平,然后起始位0,然后根据写入的数据,置1或0,依次发送位0到位8,加起来就是9位,最后停止位1,数据帧结束。在这里位8,也就是第9个位置,是一个可能的奇偶校验位,通过配置寄存器就可以配置成奇校验、偶校验或者无校验,这里可以选择配置成8位有效载荷+1位校验位,也可以选择9位全都是有效载荷。不过既然你选择了9位字长,那一般都是要加上校验位的,因为8位有效载荷,正好对应一个字节。

  • 然后下面这个时钟,就是我们之前说的同步时钟输出的功能,可以看到,这里在每个数据位的中 间,都有一个时钟上升沿,时钟的频率,和数据速率也是一样的,接收端可以在时钟上升沿进行采样,这样就可以精准定位每一位数据。这个时钟的最后位,可以通过这个LBCL位控制,要不要揄出。另外这个时钟的极性、相位什么的,也可以通过配置寄存器配置。 然后下面这两个波形,一个是空闲帧,就是从头到尾都是1;还有一个是断开帧,从头到尾都是0.这两个数据帧,是局域网协议用的,我们串口用不著,不用管的。

  • 接下来是8位字长的波形,可以看到,这里的数据位是从位0一直到位7,总共是8位,比上面这个少了一个位8,同样这个最后一位位7,也是一个可能的奇偶校验位。还是同样,既然你选择了8位字长,那这里就最好选择无校验,要不然你校验位占1位,有效载荷就只剩7位了,一个字节都发不了。

10.输入电路

那我们来看一下STM32是如何来设计输入电路的呢

1.起始位侦测



  • 第一个图展示了USART的起始位侦测。当输入电路侦测到数据帧的起始位后,将以波特率的频率连续采样一帧数据。同时,从起始位开始,采样位置要对齐到位的正中间。只要第一位对齐了,后面就都是对齐的。

  • 为了实现这些功能,输入电路对采样时钟进行了细分,以波特率的16倍频率进行采样。 在一位的时间里,可以进行16次采样。比如最开始时,空闲状态为高电平,采样一直是1。在某个位置突然采到0,说明两次采样之间出现了下降沿,如果没有噪声,那之后就应该是起始位了在起始位,会进行连续16次采样,没有噪声的话,这16次采样肯定都是0。但是实际电路还是会存在一些噪声,所以这里即使出现下降沿了,后续也要再采样几次以防万一。

  • 根据手册描述,接收电路在下降沿之后的第3次、5次、7次进行一批采样在第8次、9次、10次再进行一批采样。这两批采样都要求每3位里面至少应有2个0。如果没有噪声,那肯定全是0,满足情况;如果有一些轻微的噪声导致3位里面只有两个0,另一个是1,那也算是检测到了起始位(但是在状态寄存器里会置一个NE(Noise Error),提醒你数据收到了但是有噪声,你悠着点用);如果3位里面只有1个0,那就不算检测到了起始位,可能前面那个下降沿是噪声导致的,这时电路就忽略前面的数据重新开始捕捉下降沿。

2.数据采样

  • 数据采样的流程:这里,从1到16,是一个数据位的时间长度,在一个数据位,有16个采样时钟,由于起始位侦测已经对齐了采样时钟,所以,这里就直接在第8、9、10次采样数据位。

  • 为了保证数据的可靠性,这里是连续采样3次,没有噪声的理想情况下,这3次肯定全为1或者全为0,全为1,就认为收到了1,全为0,就认为收到了0;如果有噪声,导致3次采样不是全为1或者全为0,那它就按照2:1的规则来,2次为1,就认为收到了1,2次为0,就认为收到了0,在这种情况下,噪声标志位NE也会置1,告诉你,我收到数据了,但是有噪声,你悠着点用,这就是检测噪声的数据采样,





11.USART串口数据包

1.数据流分割方法:
  • 这种方法是把每个数据的最高位当做标志位来进行分割的。如UTF8的编码方法。
  • 额外添加包头包尾,数据包有2种格式:如图所示,固定包长,就是每个数据包长度都固定不变,每个数据包前面是包头,后面是包尾。第2种是,可变包长,就是每个数据包长度可以是不一样,我们可以根据用户需求自行规定数据包格式。
2.如何避免传输的数据和包头包尾重复
  1. 限制载荷数据的范围
  2. 数据包有2种格式:固定包长,就是每个数据包长度都固定不变,每个数据包前面是包头,后面是包尾。第2种是,可变包长,就是每个数据包长度可以是不一样,我们可以根据用户需求自行规定数据包格式。**
  3. 增加包头包尾的数量,并且尽量让它呈现出载荷数据出现不了的状态。比如我们使用FF、FE作为包头,FD、FC作为包尾,这样也可以避免载荷数据和包头包尾重复的情况发生
3.数据包收发流程

  发送数据包的过程是可控的,我们可以根据需要发送任何类型的数据包。   接收一个数据包,比较复杂了,这里是固定包长HEX数据包的接收方法,和可变包长文本数据包的接收方法,其他的数据包也都可以套用这个形式   我们先看一下如何来接收这个固定包长的HEX数据包。要接收固定包长的HEX数据包,我们需要设计一个状态机来处理。根据之前的代码,我们知道每当收到一个字节,程序会进入中断。在中断函数里,我们可以获取这个字节,但获取后需要退出中断。因此,每个收到的数据都是独立的过程,而数据包则具有前后关联性,包括包头、数据和包尾。为了处理这三种状态,我们需要设计一个能够记住不同状态的机制,并在不同状态下执行不同的操作,同时进行状态合理转移。这种程序设计思维就是“状态机”。   对于—个固定包长HEX数据包来说,我们可以定义3个状态,第一个状态是等待包头,第二个状态是收数据,第三个状志是等待包尾,每个状态需要用一个变量来标志一下,比如我这里用变量S来标志,三个状态依次为S=0 S=1 S=2,这一点类似于置标志位,只不过标志位只有0和1,而状态机是多标志位状态的一种方式。

蓝牙串口通信

  • JDY31 只能当从机 HC05 是主从机