2.25 USART串口外设

USART简介

USART(Universal Synchronous/Asynchronous Receiver/Transmitter),直译通用同步/异步收发器。USART中的S就是同步的意思。另外我们还会经常遇到串口叫UART,这里少了个S,就是异步收发器。一般串口我们很少使用同步功能。所以USART和UART使用起来也没什么区别。其实这个STM32的USART模式只是多了个时钟输出而已,他只支持时钟输出,不支持时钟输入,所以这个同步模式更多是为了兼容别的协议或者特殊用途而设计的,并不支持两个USART之间进行同步通信。所以我们学习串口,主要还是异步通信。

USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。USART外设,就是串口通信的硬件支持电路,USART大体可以分为发送和接收两部分,发送部分就是将数据寄存器的一个字节数据,自动转化为协议规定的波形,从TX引脚发送出去;接收部分就是自动接收RX引脚的波形,按照协议规定,解码为一个字节数据,存放在数据寄存器里。这就是USART电路的功能。

自带波特率发生器,最高达4.5Mbits/s,这个波特率发生器,就是用来配置波特率的,他其实就是一个分频器,比如我们APB2总线给个72MHz的频率,然后波特率发生器进行一个分频,得到我们想要的波特率时钟,最后在这个时钟下,进行收发,就是我们指定的通信波特率。最常用的波特率是9600或者115200。

可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2),这些就是STM32 USART支持配置的参数了,数据位长度就是带不带校验位,不带就是8位,带就是9位,停止位就是看你想要帧之间的间隔有多长,最常用的就是1位停止位,其他的很少用。

可选校验位(无校验/奇校验/偶校验),最常用的是无校验,毕竟有校验也没啥用……

支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN。

同步模式:就是多了个时钟CLK的输出。

硬件流控制:比如A设备有个TX向B设备的RX发送数据,A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制,那么B就只能抛弃新数据或者覆盖原数据了,如果有硬件流控制,在硬件电路上,会多出一根线,如果B没准备好接收,就置高电平,如果准备好了,就置低电平,A接收到了B反馈的准备信号,就只会在B准备好的时候,才发数据。可以防止因为B处理慢而导致数据丢失的问题。硬件流控制STM32也是有的,不过我们一般不用。

DMA:是这个串口支持DMA进行数据转运,如果有大量的数据进行收发,可以使用DMA转运数据,减轻CPU的负担。

智能卡、IrDA、LIN:这些是其他的一些协议,因为这些协议和串口非常像,所以STM32就对USART加了一些小改动,就能兼容这多么协议了,不过一般我们不用。

STM32F103C8T6 USART资源: USART1、 USART2、 USART3 ,总共3个USART外设,其中USART1是在APB2总线上的设备,剩下两个都是APB1总线的设备。这个开启时钟的时候注意一下。

USART框图

这个图就是把USART外设的各个寄存器和寄存器每一位控制的地方都画出来了。

先看左上角引脚部分,这里TX和RX就是发送和接收了。下面的SW_RX、IRDA_OUT/IN这些是智能卡和IrDA通信的引脚,我们不用这些协议,所以就不用管。

这部分是串口的数据寄存器了,发送或者接收的字节数据就存在这里。上面是有两个数据寄存器,一个是发送数据寄存器TDR,另一个接收数据寄存器RDR。这两个寄存器占用同一个地址,即在程序上,只表现为一个寄存器,就是数据寄存器DR,但实际硬件中,是分成了两个寄存器,一个用于发送TDR,一个用于接收RDR。TDR是只写的,RDR是只读的。下面两个是移位寄存器,一个用于发送,一个用于接收,发送移位寄存器的作用就是,把一个字节的数据一位一位的移出去,正好对应串口协议的波形的数据位。

,这两个寄存器是怎么工作的呢?

比如你在某时刻给TDR写入了0x55这个数据,在寄存器里就是二进制存储,0101 0101,那么此时,硬件检测到你写入数据了,发送移位寄存器就会检查当前是不是有数据正在移位,如果没有,0101 0101就会立刻全部移动到发送移位寄存器,准备发送。当数据从TDR移动到发送移位寄存器时,会置一个标志位,叫TXE(TX Empty),发送寄存器空的意思,我们检查这个标志位,如果置1了,那么代表数据全部移位完成了,就可以在TDR继续写入下一个数据了,注意一下,当TXE位置1的时候,数据其实还没有发送出去,只要数据从TDR转移到发送移位寄存器了,TXE就会置1,我们就可以写入新的数据了。

然后,发送移位寄存器就会在下面的发生器控制的驱动下,向右一位一位的,把数据输出到TX引脚,这里是向右移位,所以正好和串口协议规定的低位先行是一致的。当数据移位完成后,新的数据就会再次自动的从TDR转移到发送移位寄存器里来。如果当前移位寄存器移位还没有完成,TDR的数据就会进行等待,一旦移位完成后,就会立刻转移过来。有了TDR和移位寄存器的双重缓存,可以保证连续发送数据的时候,数据帧之间不会有空闲,提高了工作效率。

简单来说,就是数据一旦从TDR转移到移位寄存器,管你有没有完成,直接把新的数据塞进TDR里,一旦移完了,新的数据就会立刻跟上,这样做效率比较高。

接收端这里也是类似的,数据从RX引脚通向接收移位寄存器,在接收器控制的驱动下,一位一位的读取RX电平,然后先放在最高位,然后向右移,移位8次之后,就能接收一个字节了,同样,因为串口协议是低位先行,所以接收移位寄存器是从高位往低位这个方向移动的,当一个字节移位完成之后,这一个字节的数据就会整体的一下子转移到RDR里来,在转移的过程中,也会置一个标志位,叫RXNE,接受数据寄存器非空的意思,当我们检测到RXNE置1之后,就可以把数据读走了,同样这里也是两个寄存器进行缓存,当数据从移位寄存器转移到RDR时,移位寄存器就可以直接移位接收下一帧的数据了。

这些就是USART外设的主要功能。

举例理解USART的发送和接收

我们要使用串口协议发送0101 0101。

USART外设中,发送移位寄存器是低位发送的,比如TDR中是0101 0101,那么发送移位寄存器发送的数据就是1010 1010。接收移位寄存器通常会从最低位开始填充接收到的数据,因此第一个接收到的位(数据的最高位,这里是1)会被放在移位寄存器的最低位,接着是下一个位(0),依此类推,直到最后一个接收到的位(原始数据的最低位,这里是0)被放在移位寄存器的最高位。 简单来说,STM32中的接收移位寄存器的数据,会照搬在RDR中,因为是串口通信,发送端发送的是反序的数据,接收移位寄存器会将这些反序的数据再次反序,这样数据就正常了。

接着我们看一下下面的控制部分:


发生器控制,他就是用来控制发送移位寄存器工作的;接收器控制,就是用来控制接收移位寄存器工作的。


左边还有个硬件流控制,简称流控,可以防止因为设备处理慢而导致数据丢失的问题。nRTS是请求发送,是输出脚,告诉别人我当前能不能接收;nCTS是清楚发送,是输入脚,也就是用于接收别人nRTS的信号的,n的意思就是低电平有效的意思。这个怎么玩?首先得找另一个支持流控的串口,其他设备的TX接在本机的RX,然后我的RTS要输出一个能不能接收的反馈信号,接到对方的CTS上。我能接收的时候,RTS就置0,请求对方发送,对方的CTS接收到之后,就可以一直发,当我处理不过来时,比如接受数据寄存器我一直没有读,又有新的数据过来,就代表现在我没有及时处理,那么RTS就会置1,对方CTS接收到之后,就会暂停发送。


,右边这个模块,这部分电路用于产生同步的时钟信号,它是配合发送移位寄存器输出的,发送移位寄存器每移位一次,同步时钟电平就跳变一个周期,时钟告诉对方,我移出去了一位。当然这个时钟只支持输出,不支持输入。所以两个USART之间不能实现同步的串口通信。那这个时钟信号有什么用呢?一是兼容别的协议,比如加上时钟口之后,就和SPI协议很像,就可以兼容SPI。二是可以做自适应波特率,比如接收设备不确定发送设备给的什么波特率,那就可以测量一下这个时钟周期,再计算得到波特率。


,唤醒单元的作用是实现串口挂载多设备,我们之前说,串口一般是点对点的通信,点对点,只支持两个设备互相通信,想发数据直接发就行,而多设备,是在一条线上有很多设备,每个设备都会被分配一个地址,想对某个设备进行通信,就要先进行寻址,确定通信的对象,再进行数据的收发。 唤醒单元就可以实现多设备的功能,唤醒单元可以给串口分配一个地址,当你发送指定地址时,此设备唤醒开始工作,当你发送别的设备地址时,别的设备就唤醒工作,本设备没收到地址,就会保持沉默。这样就可以实现多设备的串口通信了。这部分我们一般不用,大家了解一下就行。


中断输出控制,中断申请位,就是状态寄存器这里的各种标志位,状态寄存器中有两个标志位比较重要,一个是TXE发送寄存器空,另一个是RXNE接受寄存器非空。这两个是判断发送状态和接受状态的必要标志位。


波特率发生器部分,之前也说过,波特率发生器其实就是分频器,APB时钟进行分频,得到发送和接收移位的时钟。如果TE位为1,就是发送器使能,发送波特率就有效。如果RE位为1,就是接收器使能,接收部分的波特率就有效。RE和TE是USART控制寄存器中的特定位。这些位用于控制串行端口的接收和发送功能。

到这里,USART串口的主体部分就讲完了。

USART简化流程图

我们可以看到,这里有4个寄存器,但是在软件层面上,只有一个DR寄存器可供我们读写,就是4合1,写入DR,数据就走上面的发送,读取DR,就从下面那一路进行接收。

细节相关问题

数据帧

这个图是在程序中配置8位字长和9位字长波形的对比:

我们可以看到,每个数据位中间,都有一个时钟上升沿,时钟的频率和数据速率也是一样的,接收端可以在时钟上升沿进行采样,这样就可以精准定位每一位数据。

起始位检测

根据我们前面的介绍,可以想到串口的输出TX应该是比输入RX简单很多,输出你就定时翻转TX引脚的高低电平就行了,但是输入就复杂一些,你不仅要保证,输入的采样频率和波特率一致,还要保证每次输入采集的位置,要正好处于每一位的正中间,只有在每一位的最中间采样,读出来的高低电平才是最可靠的,如果采样点过于靠前或者靠后,那有可能高低电平还正在翻转,电平还不稳定,或者少有误差,数据就采样错了。

另外,输入最好还要对噪声有一定的判断能力,如果是噪声,最好能置一个标志位提醒我一下,这些就是输入数据所面临的问题了。

上图就是STM32来设计的输入电路中的起始位侦测,当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率,连续采样一帧的数据,同时,从起始位开始,采样位置就要对其到位的正中间。只要第一位对齐了,后面就肯定都是对齐的,那为了实现这些功能,首先输入的这部分电路对采样时钟进行了细分,他会以波特率的16倍频率进行采样,也就是在1位的时间里,可以进行16次采样,它的策略是,最开始,空闲状态高电平,那采样就一直是1,在某个位置突然采到了0,在这两次采样之间,出现了下降沿,如果没有任何噪声,那之后就应该是起始位了。在起始位,会连续16次采样,没有噪声的话,这16次采样肯定都是0,但是实际电路会存在一些噪声,所以这里即使出现下降沿了,后面也要再采样几次,以防万一,根据手册说所,会在第3、5、7次进行一批采样,在第8、9、10次进行第二批采样,且这批采样都要求每3位至少有2个0。同时,第8、9、10次采样的位置,正好是起始位的正中间,之后接收数据位时,都在第8、9、10次进行采样,这样就能保证采样位置在位的正中间了。

波特率发生器

波特率发生器就是分频器。

发送器和接收器的波特率由波特率寄存器BRR里的DIV确定。上面的图就是BRR寄存器,里面的分频系数就是DIV。

DIV分为整数部分和小数部分,可以实现更细腻的分频。

计算公式:波特率 = fPCLK2/1 / (16 * DIV) 。 (因为上面说了,是16倍的波特率进行采样的数据)