目录
USART简介
USART的框图
串口的引脚
USART的基本结构
数据帧
起始位侦测
数据采样
波特率发生器
USD转串口模块的原理图
声明:本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记,我之所以记录下来是为了方便自己日后复习。如果你也是跟着江科大的视频学习的,可以配套本专栏食用,如有问题可以QQ交流群:963138186
USART简介
USART(UniversAl Synchronous/Asynchronous Receiver/TrAnsmitter)通用同步/异步收发器。
注:这个S就是Synchronous同步,如果少了这个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)
可选校验位(无校验/奇校验/偶校验)
我们最常用的是无校验
以上这些所有的参数都是可以通过配置寄存器来完成的,使用库函数配置的话就更简单了,直接给结构体赋值就行。
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
这个同步模式就是多了个时钟CLK的输出。
硬件流控制,比如A设备有个TX向B设备的RX发送数据。A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制,B就只能抛弃新数据或者覆盖原数据了。如果有硬件流控制,在硬件电路上会多出一根线,如果B没准备好接收就置高电平,如果准备好了,就置低电平。A接收到了B反馈的准备信号,就只会在B设备好的时候才发数据。如果B没准备好,数据就不会发送数据。这就是硬件流控制。它可以防止因为B处理慢而导致数据丢失的问题。STM32也有硬件流控制,不过我们一般不用。
这个串口支持DMA进行数据转运,如果有大量的数据进行收发,可以使用DMA转运数据,减轻CPU的负担。
最后智能卡(比如饭卡、公交卡)、IrDA(用于红外通信)、LIN这些是其它的一些协议,因为这些协议和串口是非常像,所以STM32就对USART加了一些小改动,就能兼容这么多协议了,不过我们一般不用。IrDA用于红外通信,这个红外通信就是一个红外发光管,另一边是红外接收管,然后靠闪烁红外光通信。LIN是局域网的通信协议,这个感兴趣的话,自己研究一下。
最后看一下我们这个芯片的USART资源,
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
其中这里USART1是APB2总线上的设备,剩下的都是APB1总线的设备,这个在开启时钟的时候注意一下,在使用的时候,挂载哪个总线影响并不是很大。
USART的框图
接下来我们来看一下USART的框图,
先看左上脚这里的引脚部分,
这里有TX和RX这两个就是发送和接收引脚,下面的那三个引脚是智能卡和IrDA通信的引脚,我们不用这些协议,所以这些引脚就不用管了。右边这个框框IrDASIR编码模块也不用管它。
TX发送脚就是从这里接出去的,
RX接收脚就是通向这里,
然后看一下右边这一块,这部分就是串口的数据寄存器,发送或接收的字节数据就存在这里
上面只有两个数据寄存器,一个是发送数据寄存器TDR(Transmit DR),另一个是接收数据寄存器RDR(Receive DR),这两个寄存器占用同一个地址,就跟51单片机串口的SBUF寄存器一样。在程序上只表现为一个寄存器,就是数据寄存器DR。但实际硬件中是分成了两个寄存器,一个用于发送,TDR,一个用于接收RDR。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电平,先放在最高位,然后往右移,移位八次之后就能接受一个字节。
同样,因为串口协议规定是低位先行,所以接收移位寄存器是从高位往低位这个方向移动的,之后,当一个字节移位完成之后,这一个字节的数据就会整体的,一下子转移到接收数据寄存器RDR。在转移的过程中也会置一个标志位,叫RXNE(RX Not Empty),接收数据寄存器非空。当我们检测到RXNE置1之后,就可以把数据读走了。
同样这里也是两个寄存器进行缓存,当数据从移位寄存器转移到RDR时,就可以直接一位接收下一帧数据了。
这就是USART外设整个的工作流程。
当然发送还需要加上帧头和帧尾,接收还需要剔除帧头和帧尾。这些操作它内部有电路会自动执行,我们知道有硬件帮我们做了这些工作就行了。
接着我们继续看一下这个框图中下面的控制部分和一些其它的增强功能。
下面这里是发送器控制,它就是用来控制发送移位寄存器的工作的。接收器控制,用来控制接收移位寄存器的工作。
然后左边这里有一个硬件数据流控,也就是硬件流控制,简称流控。前面我们也大概介绍过,如果发送设备发的太快,接收设备来不及处理,就会出现丢弃或覆盖数据的现象。有了流控就可以避免这个问题了。这里流控有两个引脚,一个是nRTS,一个是nCTS。
nRTS(Request To Send)是请求发送,是输出脚,也就是告诉别人我当前能不能接收。nCTS(Clear To Send)是清除发送,是输入脚,也就是用于接收别人nRTS的信号的。这里前面加个n意思是低电平有效。
这两个引脚怎么玩的?
首先得找另一个支持流控的串口,它的TX接到我们的RX,然后我们的RTS要输出一个能不能接收的反馈信号,接到对方的CTS。
当我们能接收的时候,RTS就置低电平,请求对方发送。对方的CTS接收到之后就可以一直发。当我们处理不过来时,比如接收数据寄存器一直没有读,又有新的数据过来了,现在就代表我们没有及时处理,RTS就会置高电平。对方CTS接收到之后就会暂停发送,直到这里接收数据寄存器被读走,RTS才又置低电平,新的数据才会继续发送。
反过来,当我们的TX给别人发送数据时,我们CTS就要接到对方的RTS用于判断对方能不能接收。
TX和CTS是一对的,RX和RTS是一对儿的,CTS和RTS也要交叉连接。这就是流控的工作模式。
当然我们一般不使用流控,所以流控就了解一下就行。
接着继续看右边这个模块,这部分电路用于产生同步的时钟信号,它是配合发送移位寄存器输出的。发送寄存器每移位一次,同步时钟电平就跳变一个周期,时钟告诉对方我移出去一位了,你看要不要让我这个时钟信号来指导你接收一下?
当然这个时钟只支持输出,不支持输入。所以两个USART之间不能实现同步的串口通信。
这个时钟信号有什么用?
第一个用途就是兼容别的协议,比如串口加上时钟之后就跟SPI协议特别像,所以有了时钟输出的串口就可以兼容SPI。另外这个时钟也可以做自适应波特率,比如接收设备不确定发送设备给的是什么波特率,就可以测量一下这个时钟的周期,然后再计算得到波色率。不过这就需要另外写程序来实现这个功能了。
这个时钟功能我们一般不用,所以也是了解一下就行。
然后继续看中间这个唤醒单元。
这部分的作用是实现串口挂载多设备。
我们之前说,串口一般是点对点的通信。点对点只支持两个设备互相通信,想发数据直接发就行。而多设备在一条总线上可以接多个从设备,每个设备分配一个地址。我想跟某个设备通信就先进行寻址,确定通信对象,再进行数据收发。
这个唤醒单元就可以用来实现多设备的功能。在这里可以给串口分配个地址,当你发送指定地址时,此设备唤醒开始工作。当你发送别的设备地址时,别的设备就唤醒工作。设备没收到地址就会保持沉默,这样就可以实现多设备的串口通信了。
这部分功能我们一般不用,大家也了解一下就行。
接着看下面,这部分是中断输出控制,中断申请位,就是状态寄存器这里的各种标志位。
状态寄存器这里有两个标志位比较重要,一个是TXE发送寄存器空,另一个是RXNE接收寄存器非空。这两个是判断发送状态和接收状态的必要标志位。刚才也都讲过,剩下的标志位了解一下就行。
中断输出控制这里就是配置中断是不是能通向NVIC。
然后最下面这里是波特率发生器部分。
上面说过波特率发生器其实就是分频器,APB时钟进分频,得到发送和接收移位的时钟。
这里时钟输入是fpCLKx(x=1或2),USART1挂载在APB2,所以就是PCLK2的时钟,一般是72M。其它的USART都挂载在ABB1,所以是PCLK1的时钟,一般是36M。
之后这个时钟进行一个分频,除一个USARTDIV的分频系数。
USARTDIV里面就是右边这样,是一个数值,并且分为了整数部分和小数部分。因为有些波特率用72M除一个整数的话,可能除不尽会有误差,所以这里分频系数是支持小数点后四位的,分频就更加精准。
之后分配完之后,还要再除个16,得到发送器时钟和接收器时钟通向控制部分。
然后右边这里如果TE为1就是发送器使能了,发送部分的波特率就有效。如果RE为1就是接收器使能,接收部分的波特率就有效。
到这里,我们这个串口的主体结构就看完了。
然后剩下还有一些寄存器的指示,比如各个CR控制寄存器的哪一位控制哪一部分电路,SR状态寄存器都有哪些标志位,这些可以自己看看手册里的寄存器描述。
串口的引脚
最后我们再看看串口的引脚。
复用功能这一栏就给出了每个USART的各个引脚都是复用在了哪个GPIO上的。
这些引脚都必须按照引脚定义里的规定,比如你要使用USART1,那TX必须是PA9,RX,必须是PA10。或者看一下重映射那栏有没有重映射,这里有USART1的重映射,所以有机会换一次口,剩下的引脚就没有机会做USART1的接口了。
所以这个表在设计电路的时候很重要,要提前规划好引脚,别让引脚复用功能冲突了。有关外设的复用引脚是哪个的问题,都是看这个表。
接下来看一下这个简化结构图
USART的基本结构
最左边这里是波特率发生器用于产生约定的通信速率。时钟来源是PCLK2或1。经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,发送控制器和接收控制器用来控制发送移位和接收移位。
之后由发送数据寄存器和发送移位寄存器这两个寄存器的配合,将数据一位一位地移出去,通过GPIO口的复用输出,输出到TX引脚,产生串口协议规定的波形。
图中画了几个右移的符号,就是代表这个移位计准器是往右移的,是低位先行。
当数据由数据寄存器转移到移位寄存器时,会置一个TXE的标志位,我们判断这个标志位就可以知道是不是可以写下一个数据了。
然后接收部分也是类似的,RX引脚的波形通过GPIO口输入,在接收控制器的控制下,一位一位地移入接收移位寄存器,这里画了右移的符号,也是右移的。因为是低位先行,所以要从左边开始移进来,移完一帧数据后,数据就会统一转运到接收寄存器去,在转移的同时是一个RXNE标志位,我们检查这个标志位就可以知道是不是收到数据了。
同时这个标志位也可以去申请中断,这样就可以在收到数据时直接进入中断函数,然后快速的读取和保存数据。
这里实际上有四个寄存器,但是在软件层面只有一个DR寄存器可以供我们读写。
写入DR时数据走上面这条路进行发送。
读取DR时,数据走下面这条路进行接收。
这就是USART进行串口数据收发的过程。
最后右下脚是一个开关控制,就是配置完成之后用cmd开启一下外设,这个也是常规操作了。
到这里USART的基本结构就讲完了,接下来我们来看几个细节的问题。
数据帧
这个图是在程序中配置八位字长和九位字长的波形对比。这里的字长就是我们前面说的数据位长度,它这里的字长是包含校验位的。
看一下上面这个九位字长的波形,第一条时序很明显,就是TX发送或者RX接收的数据帧格式。空闲高电平,然后起始为零,然后根据写入的数据置1或置0,依次发送位0到位8,加起来就是九位,最后停止位1,数据帧结束。在这里位八,也就是第九个位置,是一个可能的奇偶校验位,通过配置寄存器就可以配置成奇校验、偶校验或者无校验。这里可以选择配置成八位有效载荷加一位校验位,也可以选择九位,全都是有效载荷。不过既然你选择了九位字长,一般都是要加上校验位的,因为8位有效载荷正好对应一个字节。
然后这个时钟就是我们之前说的同步时钟输出的功能。可以看到这里在每个数据位的中间都有一个时钟上升沿,时钟的频率和数据速率也是一样的。接收端可以在时钟上升沿进行采样,这样就可以精准定位每一位数据。这个时钟的最后一位可以通过这个LBCL位来控制要不要输出。
另外这个时钟的极性,相位什么的,也可以通过配置寄存器配置,需要的话可以了解一下。
然后下面这两个波形,一个是空闲帧,就是从头到尾都是1,还有一个是断开帧,从头到尾都是零,这两个数据帧是局域网协议用的,我们串口用不着不用管的。
接着下面是八位字长的波形,可以看到这里的数据位是从位零一直到位七总共是八位,比上面这个少了一个位八。同样这个最后一位位七也是一个可能的奇偶校验位。同样,既然你选择了八位字长,这里就最好选择无校验,要不然你校验位占一位,有效载荷就只剩七位了,一个字节都发不了。
总的来说,这里有四种选择,九位字长有校验或无校验,八位字长有校验或无校验,但我们最好选择九位字长有校验,或八位字长无校验这两种,这样每帧的有效载荷都是一字节。
接下来我们继续来看这个数据帧,看一下不同停止位的波形变化。
STM32的串口可以配置停止位长度为0.5、1、1.5、2这四种。这四种参数的区别就是停止位的时长不一样。
这四种停止位长度的波形对应上图中四个波形。
1个停止位,这时停止位的时长就和数据位的1位时长一样;
1.5个停止位,这时的停止位就是数据位1位时长的1.5倍;
2个停止位的,停止位时长就是两倍;
0.5个停止位,时长就是0.5倍。
一般选择1位停止位就行了。
之后我们继续来看一些细节问题,这两个图展示的是USART电路输入数据的一些策略。对于串口来说,串口的输出TX应该是比输入RX简单很多。
输出就定时翻转TX引脚高低电平就行了。
但是输入就复杂一些,不仅要保证输入的采样频率和波特率一致,还要保证每次输入采样的位置,要正好处于每一位的正中间。只有在每一位的正中间采样,这样高低电平读进来才是最可靠的。如果你采样点过于靠前或靠后,有可能高低电平还正在翻转,电平还不稳定,或者稍有误差,数据就采样错了。
另外输入最好还要对噪声有一定的判断能力。如果是噪声最好能置个标志位提醒我一下,这些就是输入数据所面临的问题。
那我们来看一下STM32是如何来设计输入电路的。
起始位侦测
首先第一个图,这里展示的是USART的起始位侦测。
当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率连续采样一帧数据。同时从起始位开始采样位置就要对齐到位的正中间,只要第一位对齐了,后面就肯定都是对齐的。为了实现这些功能,首先输入的这部分电路对采样时钟进行了细分,它会以波特率的16倍频率进行采样,也就是在一位的时间里可以进行16次采样。
然后它的策略是最开始空闲状态高电平,这时采样就一直是1,在某个位置,突然采到一个0,那么就说明在这两次采样之间出现了下降沿,如果没有任何噪声,之后就应该是起始位了。
在起始位会进行连续16次采样。没有噪声的话,这16次采样肯定就都是0。但是实际电路还是会存在一些噪声的。所以这里即使出现下降沿了,后续也要再采样几次,以防万一。
根据手册的描述,这个接收电路还会在下降沿之后的第三次、五次、七次进行一批采样。在第八次、九次、十次再进行一批采样,且这两批采样都要要求每三位里面至少应该有两个零。
如果没有噪声,肯定全是零,则满足情况。
如果有一些轻微的噪声,导致这里三位里面只有两个0,另一个是1,也还算是检测到了起始位了,但是在状态寄存器里会置一个NE(Noise Error)噪声标志位,就是提醒你一下数据是收到了,但是有噪声。
如果这里三位里面只有一个零,就不算监测到了起始位。可能前面个下降沿是噪声导致的,这时电路就忽略前面的数据重新开始捕捉下降沿,这就是STM32的串口在接收过程中对噪声的处理。
如果通过了这个起始位侦测,接收状态就由空闲变为接收起始位。同时,第八、九、十次采样的位置,就正好是起始位的正中间,之后接收数据位时,就都在第八、九、十、次进采样。这样就能保证采样位置在位的正中间了,这就是起始位侦测和采样位置对齐的策略。
紧跟着我们来看一下这个数据采样的流程了。
数据采样
这里从1到16是一个数据位的时间长度。在一个数据位有十六个采样时钟,由于起始位侦测已经对齐了采样时钟,所以这里就直接在第八、九、十、次采样数据位,为了保证数据的可靠性,这里是连续采样三次。没有噪声的理想情况下,这三次肯定全为1或者全为0。全为1就认为收到了1,全为0就认为收到了0。
如果有噪声导致三次采样不是全为1或者全为0,它就按照2:1的规则来。也就是两次为1就认为收到了1,两次为0就认为收到了0。在这种情况下,噪声标志位NE也会置1,告诉你,我收到数据了,但是有噪声你悠着点用。这就是检测噪声的数据采样。
最后我们再来看一下波特率发生器。
波特率发生器
波特率发生器就是分频器
发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
下面这个图就是BRR寄存器。里面就是分频系数DIV,分为整数部分DIV_Mantissa和小数部分DIV_Fraction,可以实现更细腻的分频
波特率和分频系数的关系可以由这个计算公式进行计算
fPCLK2/1这个f是时钟频率,哟啊除以16倍的DIV,(频率=次数/时间)。
为什么这里多个16?看之前这个图就明白了,因为它内部还有一个十六倍波特率的采样时钟。所以这里输入时钟除以DIV要等于十六倍的波特率,最终计算波特率自然要多除一个十六了。
举个例子,比如要配置USART1为9600的波特率。如何配置这个BRR寄存器?
我们代入公式,就是9600=72M(USART1的时钟)/16*DIV ,则DIV=72M/9600/16,结果是468.75。这是一个带小数的分频系数,最终写到寄存器还需要转换成二进制。
转换结果是111010100.11
随便在网上找个转换器转换就行了,没必要手算:在线进制转换器 | 菜鸟工具 (jyshare.com)
所以最终写到这个寄存器就是整数部分为111010100,前面多出来的补零。小数部分为11,后面多出来的补零。
这就是根据波特率写BRR寄存器的方法,了解一下。不过我们用库函数配置的话就非常方便,需要多少波特率直接写就行了,库函数会自动帮我们算。
USD转串口模块的原理图
最后我们再看一下这个USD转串口模块的原理图。
这个图展示的就是我们套件里USB转串口模块的内部电路图。
最左边这里是USB的端口,USB有四根线。GND、D+、D-、VCC。
USB标准供电是5V,然后中间D+和D-是通信线,走的也是USB协议,所以这里需要加一个CH340芯片转换一下,转换之后输出的就是TXD和RXD,是串口协议。最后通过这里的排针引出来。
需要注意的就是这边的供电策略。首先所有的电都是从这个VCC+5V来的。然后VCC+5V通过这个稳压管电路进行稳压,得到VCC+3.3V。之后,VCC+5V和VCC+3.3V都通过排针引出来了。所以这个第六脚和第四脚是分别由5V和3.3V输出的。
很多人迷惑的是,这个第五脚,板子上标的是VCC,这个硬件通过原理图可以看到,它是通向了CH340芯片的VCC上。
所以这个第五脚实际上是CH340的电源输入脚。一般我们这个模块的排针会有个跳线帽,这个跳线帽需要插在四五脚或者五六脚上。右边这里文文字说,短路五5V到VCC,CH340供电为5V,TTL电电平为5V。短路3V3到VCC,CH340的供电为3.3V,TTL电平为3.3V。所以这个跳键帽是用来选择通信电平的,也是给CH340芯片供电的,所以最好不要拿掉。如果你拿掉了,就相当于这整个芯片没有供电。
不过实际上试了一下,即使把降线帽拔掉,不给芯片供电,这个串口还是能正常工作。可能是从别的地方集取的一些电流。当然还有可能是别的原因,这个模块的电路是有些变动的。如果不插电线帽,通信电平为3.3V,不过为了稳定,最好还是要插上电线帽。我们STM32通信需要3.3V,所以把跳线帽插在这里的四五脚上就行了,供电就只剩一个5V脚了。所以这个供电有点折磨人。要么选择5V电平,剩下供电脚就只有3.3V。要么选择3.3V电平,剩下供电脚就是5V。所以这个供电点的设计有点不太方便。
不过考虑到电平的5V和3.3V可以互相兼容。所以如果你既需要通信,又需要供电,就保证供电是正确的就行了。通信电平没法一致,这应该也没问题。
然后右边还有引脚说明看一下,5V脚是从USB取出来的5V电源输出。VCC是模块供电点,3V3是模块稳压出来的3.3V电源输出。TXD串口数据输出,RXD串口数据输入,GND模块负极,这就是这些引脚说明。
最后右下脚,这些是指示灯和电源滤波,这里有PWR电源指示灯和TXD、RXD的指示灯,如果引脚上有数据传输,这两个指示灯会对应闪烁,方便我们观察。
到这里有关USART的理论部分就结束了,下节写串口的代码。
QQ交流群:963138186
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓