目录
引言
一、通讯相关基础
1.1 并行通讯与串行通讯
1.2 单工、半双工、全双工通讯
1.3 同步与异步通讯
1.4 串口介绍
1.5 串口通讯协议
二、USART外设
三、USART_功能框图原理介绍
四、波特率的设置
五、总结
引言
本次介绍STM32中片上外设中的模块之一——串口通讯模块,也是最为简单基础的一种串口通讯,其名称也叫USART。当然,串口、USART这俩概念到底是什么、他们俩之间的关系是什么、哪些模块叫串口、我们可能见过的RS232等就叫串口吗?等等概念和意义我们目前仍比较陌生,为了逐步理解这些概念和含义,本次我们遵循从易到难的思路,从基础知识和基本概念逐步梳理陌生点、疑难点。
一、通讯相关基础
首先,我们要先了解一些通讯相关的基础知识
1.1 并行通讯与串行通讯
第一个就是了解一下什么是串行通信和并行通信。
主要区别就是在数据传输的方式上做了一个划分,根据数据传送的方式将通讯分成了串行通讯与并行通讯。
串行通讯:按位传输,也就是每一次传输只传一位的数据,理论上含多位数据时的传输先后实际上没有要求的,一般来说都是从低位开始传输的;
并行传输:多位同时传输,也就是一次传输多个位的数据,一般8位、16位、32位同时传输等。因此,可想而知,在硬件上当然就需要多根导线连接才能实现多位传输的过程。
现在,对比一下这两种通讯在不同角度的区别:
速度:并行通信快于串行通信。但由于并行通信时多条线同时传输,其多线间会产生干扰,且多线传输对传输速率和时序的控制要求会更高,因此当传输距离变长以后,这些误差可能会越来越大,所以并行通信受距离限制会更大。换句话说,一般做长距离传输时我们更多的还是选择串行通信;
复杂性:并行通信由于多位并行,因此需要更多的数据线,那么布线上就会更加复杂;而串口是单位传输,理论上只需要一根线就可以了,要传输数据的时候挨个发就行,同一时刻要发送数据时我就传一位,这样的话串行的布线就会非常的简单,同时接口也简单。
当然,串行通信也会存在某些复杂性的问题。就计算机内部处理的时候,我们多数看见的一般都是多位并行的,比如我们STM32中数据传输时都是32位数据并行的,那个时候我们要和别的外设作数据交互走串行通讯的话,那是不是还涉及到我要把并行的32位数据按照优先顺序依次拿出来然后一位一位地发,同时接收方也是一位一位地收到以后,又转换成32位并行的方式去做后续的处理,这个时候就会涉及到一个串并转换的一个接口电路。所以呢由于我们计算机内部一般都是并行传输的,在需要串行通信时就会需要一个串并转换的电路设计了。
距离:串行通信就一根线一位一位的传输,接口也简单,传输过程中数据线产生的干扰相对就非常非常小了,所以显然串行通信会更适合远距离传输,甚至我们还可以增加一些别的电平规则、电气化规则的设置,比如用两根导线实现传说中的差分传输,因为我们一根线传输时受到环境的干扰后,那个电平就可能会受到影响,导致混乱,而两根线差分传输时看的是两根线直接的电平差,那么此时环境的影响对俩线都是一样的,那么传输时看的电平差就不会变化,这就是那种共模干扰,这时候就相当于把干扰给屏蔽掉了。因此这个差分信号传输的方式抗干扰的程度就更强了,当然前提还是线比较少,而我们串行线就一根,满足了条件,所以串行通信时就更有利于做抗干扰处理,因此串行通信也就更适合远距离传输了。相反,并行通信多线同时传输,那么线与线之间就会产生干扰,如果距离又很长的话,多条线的数据传输的要求相应就更高了,否则就可能导致多线传输时速度不匹配或者数据的有效性出现差错而导致错误,同时因为线多了也没那么好做抗干扰处理了,所以并行通信更适合短距离传输。
换句话说,理论上我们并行的速度确实比串行快,但其因对传输的要求比较高,因此受环境、距离等的影响限制就会导致其速度没办法达到一定的高度。特别是我们外部两个不同设备直接进行并行的数据传输的话,当时钟频率高到一定程度以后就不能在往上提高频率了,换句话说就是多线同时传输的时候速度高了以后受到环境以及线之间的干扰就会更大而难以避免,进而就导致咱数据的正确性下降,那就只好去使用串行通信了。我们使用串行通讯时,就一条线嘛,那我极端情况,时钟频率一直往上升,是不是影响都不大啊,这个时候对串口通讯速度的影响就小,就更容易提升我们数据的传输速度了。
所以大家就会发现,我们数据传输的这个速度并不是绝对的,咱现在所谓的这个模块之间的通讯实际上都是串口通讯,因为这样的话能够使用的时钟频率更高。当然这都是后面会讲到的,这里作为一个扩展知道就行。
成本:串行接口通常成本较低,因为布线上本来就简单,当然硬件上需要的成本就少了。
抗干扰:通过前面的一些对比,大家也能理解到,串行通讯的抗干扰显然要优于并行的。
好,这里再借助表格总结一下串行通讯与并行通讯的比较:
随着技术的发展,串行通信越来越受欢迎。原因在于其较低的复杂性和成本,以及适应长距离传输的能力。
由于串行通讯可以不断提速,所以可以说,我们现在进入了高速串行通讯的时代。这里可以扩展一个例子:
早些时候的显卡其实是有各种不同的接口标准的,有一种标准叫做PCI,即PCI接口标准。早期他是一种并行的通讯接口,然后解出来的那个总线叫做PCI总线,我们一般就这个总线去接显卡,大家都知道,我们显卡有大量的图形数据处理,并且要和CPU内存间进行数据传递,那他这个接口即PCI就是并行传输的,同时早期后来还有个AGP接口(早期NVIDIA的显卡接口标准)也是使用并行通讯的。但是后来发现,他们这个频率提升到一定程度以后就提升不上去了,所以他们就被另一种叫PCIe接口的显卡替代了,这个“e”就是express扩展的意思,很明显他是一种PCI接口基础上的扩展,当然,PCIe不只是一种接口标准,他也连接的是一个总线标准。之前的PCI是一种并行的传输,而扩展的PCIe其实是一种串行传输,这个时候可能就会疑问了:扩展那不得是性能有所提升,那怎么又会变成单线传输的串口呢?实际上,经扩展后的PCIe接口的性能确实提升了,主要就是提升了他的时钟频率,所以它的数据传输的速度就更快了。当然了,理论上讲,他是一种串行传输技术,理论上一根线就行,不过他也可以扩展它的位宽,也就是说他也可以从一根线扩展为8根线、16根线、32根线等等,只不过它还是一根一根的线单独去传,本质上都还是串行通讯的,因此他现在的传输速度就比之前并行的要快很多了。同时现在大家基本也见不到传统的老式的那种PCI接口的显卡了,甚至后来产生的AGP接口的显卡也被淘汰不用了,如果咱见到的是独立显卡的话,基本上都是PCIe接口。
还有一个例子就是我们这个使用的硬盘的接口标准。常见的有哪些呢?早期的时候有一种接口叫做ATA,当然他也是一种并行的传输方式,然后后来呢发现这个速度已经上不去了,那怎么办呢,结果就又换成了串行的一种接口标准:那个对应的接口标准就是在原来的ATA前面加了一个“S”,变成了SATA接口标准,这个‘S’其实就是表示英文单词“serial”串行的意思。也就是说,现在的SATA接口就是把以前ATA并行的接口变成了串行的接口标准,然后时钟频率得到了提升,进而传输数据的速率反而更高了。当然有人为了作对比,也把之前的ATA接口叫PATA,前面的这个‘P’就是英文单词Parallel的首字母。这就是和串行并行相关的一些概念,咱可以把他们联系起来进行理解。
说这么多,那并行传输难道就会被淘汰吗?当然不是,虽然他在长距离传输或者有时候频率达不到一定的程度,但是其在距离短,他的时钟频率能够提升上去的话,那从原理上看,多线传输的并行还是会应该比串行更快。所以大家会发现,我们CPU芯片内部的总线,就比如我们这个STM32,他不还是32位并行嘛,本身内部还是做得并行的传输,而我们模块间都是用串行传输主要是因为模块之间的距离和CPU内部总线间距离比起来已经算是长距离传输了,所以我们后面介绍的STM32里面的模块基本上都是用的串口工具。所以,我们的并行通讯仍是有必要的,尤其是在内部总线中高速数据传输的场合。
1.2 单工、半双工、全双工通讯
按照数据传输的方向,我们可以分为三种不同的通讯方式:单工、半双工以及全双工。
单工:同一时间,只能由特定的一方发出数据,然后另一方接收。不能两边都发出和接收数据,即同一时间,数据只能A -> B;
半双工:同一时间,只能由一方发送数据给另一方。两方都可以进行发送和接收的功能,但同一时间只能一方发送一方接收,即同一时间,数据只能A -> B或者B -> A;
全双工:同一时间,一方既能发送数据给另一方,也可以接收另一方发过来的数据,即数据可以同时在A和B间进行发送和接收,A <->B,同一时间可以两个方向传输。
1.3 同步与异步通讯
按照数据的同步方式,我们可以把通讯分为两种不同的方式,即同步和异步。这俩方式的区别主要在于有没有一个基准来进行通讯,就像我们平时跑步有没有人喊口号去步调一致地跑一样。
同步:有时钟,就相当于有一个统一的步调去进行通讯;
异步:没有时钟,没有统一的步调,随时进行。
这里,我们再看看在原理图上的连线会是什么情况:
再看看异步:
由此可见,同步和异步在原理图上的区别主要就在于是否多一根时钟线。
1.4 串口介绍
前面我们已经介绍了通讯领域的一些基本概念,那么接下来我们就来重点讨论一下:到底什么是串口?什么是串口通讯?值得注意的是:我们平常所说的串口通讯并不等同于前面说的串行通讯,当然了,串口通讯本身确实是串行通讯的其中一种通讯方式,因为串行通讯是一种通讯方式的大类,我们前面说过只要是一根线一位一位的传输的通讯方式他都叫串行通讯,因此串口通讯是不能与串行通讯画等号的。
我们这里所说的串口,他其实是一种特殊的通讯协议,他是串行的,但是到底需要怎么样去传输、怎么规定数据格式、怎么去开始、怎么去结束、按照什么速率去进行数据传输等等问题我们都得去自己定义清楚,而其中一种定义好后产生的一套通讯标准就叫做我们这个串口通讯,因此可以说:他是一套专门定义出来的标准或者说协议。
因此,总结一段话:即串口通讯(Serial Communication)是一种设备间常用的串行通讯方式,由于其简单便捷,因此大部分的电子设备以及我们当前的外设模块都支持这种方式。同时,当前的一些电子工程师、嵌入式工程师在调试设备模块时也经常利用串口做一个连接,然后来输出调试信息进行调试的。
关于利用串口调试方面,通过观察我们下载器部分原理能发现:在我们单片机的下载器上,常常就会借助其集成的虚拟串口功能去连接单片机做一些调试,这也是串口在调试方面的一个基本应用。
下面,我们看如下一张串口通讯原理图的简单表达:
我们能够发现,在串口通讯的基本定义下是不带时钟的,实际上你带时钟也行,只是最初基本的定义上他不带时钟,只需要有数据就行了。这时候我们按照前面讲述的一些通讯方式的划分能够发现,串口通讯的基本定义是一种什么样的通讯呢?不带时钟——异步;两者可以同时互相发送和接收——全双工,因此这是一种异步全双工的串行通讯的协议,最简单的一种方式,也就是我们现在所说的串口通讯方式。
我们看他原理是怎么实现的呢?
即两个串口设备之间,只要连两根线就够用了,且他们都是数据线,因为没有时钟嘛;因为他是全双工,所以我就用一根线来发、一根线用来接收;然后串口的这个定义标准简单来说就是:如果是设备的话就是有两个端口,或者说如果时芯片的话就是两个引脚:一个叫TX,另一个叫RX,其中TX就是发送端、RX就是接收端。因此两个设备要做连接就是点对点的连接。换句话说,串口设备1要发送数据给2的话,就是串口设备1上的TX要连到串口2的RX上面,也就是我的发要连接你的收,同时我的收就是要连接你的发呗。如果两个串口设备的TX和RX都是一样的排列顺序的话,那么这两根线连上就是一种交叉连接的样式。
这样看来,大家能够发出疑问:我们了解串口通讯连线的基本方式以后,那能不能多个设备一起连接呢?就像系统内部的总线一样,把多个设备挂在一根线上,然后去进行串口通讯一样,这能行不?
答案当然是否定的,因为按照上面的理解,我们串口通讯都是两个设备之间你的发要连接我的收,同时我的发要连接你的收,这是一定得是一种点对点的连接。如果是多个设备的话,那他就相当于多个设备集成在一块然后都各自要连出两根线然后去与另外的设备做链接啊,然后这边的收和发就要和别人的收和发连在一起,别人的收和发也要和我们这边对应的设备的收和发连在一起。也就是说,我们这个串口通讯协议本质上就是一种点对点的通讯协议,故必须要做这样一个连接。
这就是对串口的一个基本介绍,下面我们就来继续深入,看看他到底是怎么样来发送数据的。
1.5 串口通讯协议
根据前面对串口的介绍,我们能发现这个串口通讯只有俩数据线,没有时钟线,那么我们怎么知道什么时候应该发数据或者接收数据呢?显然,这个时候就涉及到了下面要叙述的串口通讯协议了。
串口通讯协议就相当于是在我们不知道什么时候应该发数据的时候,给我们作的一个“出现什么样的信号的时候就应该发数据或者接收数据”的约定。换句话说,就是他给一个特定的信号,就代表你可以开始发送数据了,大概就是这样的意思。
好,现在我们来看一个串口通讯协议的最简单的一个标准:
那么这是什么来发数据的呢?一般我们都是一个字节一个字节得来发送数据的,然后一个字节要发送的时候他是有8位(bit)的,所以我们当然就是一位一位的发,毕竟他是串行的。
看上图容易发现,这个串行通讯协议规定的是低位开始发,LSB(List Significant Bit),最低有效位,也就是最低的有效的那一位,即我们是从低位开始发;相应的有MSB最高有效位,所以我们这里是低位先发,高位后发。
我们要发这8位数据,首先需要知道什么时候开始发,因此这之前会有一个“起始位”,他是固定的,必须是一个低电平。相应的,我们停止发送数据的标志就是这个“停止位”,为了和起始位出现明显的区分,故一定是给一个高电平,这样才能避免与起始位重合而混淆嘛。
然后我们不发送数据的时候就会给一个默认的状态,这个状态也是一个高电平,也就是我们上图中后面的“空闲位”。这就意味着我们如果一直处于高电平时,突然来了一个低电平的话,就说明我们现在要开始发送数据了,因为空闲是高电平,起始位即要开始发数据的时候才会变成低电平一下。
这个时候,知道什么时候发送和接收或者不发的状态了,那么在发送数据的时候,我们为了保证数据的有效性和正确性,串口通讯协议里面还会在停止接收之前加一个“奇偶校验位”:简单来说就是看 连同这个校验位一起,加上我的数据位里面“1”的总个数到底是奇数还是偶数,同时还得看我们校验位是奇校验还是偶校验。如果是偶校验的时候,我数据中有2个1,那么这个时候偶校验位就是0,这个时候连同偶校验位和数据位一起所有的1的个数就是个偶数嘛;同理,如果数据中有奇数个1的话,那么这个时候我们偶校验位就会给1,这样就会保证我们1的个数是个偶数了,这样就能大概保证一下数据的准确性了。当然,我们这样一描述,就会感觉吧,这奇偶校验简单是简单,但是到底准不准呢?似乎看着就不怎么准了哈哈。因此呢,咱这个串口通讯协议的好处主要就体现在这呗——简单,因为简单,所以其底层的电路实现也会比较容易,成本也比较低,所以他就比较通用了。如果说我们想用更复杂的方式去校验的话,其实也有,那就是CRC,即循环冗余校验,他就会更复杂也更精确,当然我们这里没有用到。
我们这里使用的还是奇偶校验,所以大家可以发现,其实我们这里的奇偶校验位应该是可有可无的,因此我们发送数据的时候本来一般都是一个字节,也就是8位,但是我们其实也可以选择发送6位、7位等,或者要不要再加上一位奇偶校验位,如果是已经发8位了,那加上校验位就是可以发9位了。所以呢,这些情况都会体现在不同的场景之中。然后如果我们把所有数据都发完之后(包括含我们这个校验位时),发出的这一段数据有时候我们也把它称作“数据帧”,如图
这一段数据可以是<8位、也可以是8位、甚至可以是包含奇偶校验位以后的9位数据,大概就是1个字节的数据的样子。然后发完就会接着停止位,这里是固定的高电平,接着就进入空闲位,一个持续的高电平,直到我们再次开始发送数据就会出现一个低电平。
那么,这个时候就有一个问题:如果我们此时要发送一个字符串呢,大家都知道一个字符串会包含多个字节,那我们这个时候肯定就是一个字节一个字节去发呗,但是这样的话我们不就得分几次这个发送的过程了,然后我们这个时候怎么样才能判断他把字符串都发完了呢?如果单纯靠到停止位空闲位来判断的话很明显会出现问题,因为万一他发完一个字节再去发下一个字节时稍微停顿,然后在停止后多过了一会与第二次继续发送出现了时间差,那么这个时候就可能会直接停止发送导致不能发送一个完整的字符串了。
所以啊,实际上在STM32中,为了保证能够发送一个完整字符串即判断发送完多个字节后再结束,是额外增加了一个操作的:就是咱空闲位以后不是就一直会处于高电平嘛,那么在STM32中就规定:如果在从起始位开始到停止位结束这一整个过程长度全部都是持续的高电平的话,那就说明他已经把数据全部发送完了。同时,我们把这一段一直是高电平的整个发送数据的过程称之为“空闲帧”,就是我们这一段全部处于空闲状态,那么这个时候咱就可以判断一个字符串发送结束,可以把他当做我们真正发送结束的一个标志了。
所以,我们经常会用他来检测我们发送的变长数据,也就是我们如果用来发送,尤其是接受别人发来的数据的时候,我们不知道别人会发来多少数据,这时候我们利用这种空闲帧的检测方式,就能比较合理地判断接受完毕或者发送完毕了。以上就是我们这个串口通讯协议的一些基本概念
这里,我们还有一个非常重要的概念——波特率,为什么会有这个概念呢?根据前面介绍我们知道,这个串口通讯是没有时钟的,也就意味着我们面对一堆发送的数据可能会不知道什么时候结束,比如我要是发送的全是高电平1的数据,那么这个时候我们后面判断停止也是高电平1,这个时候我们就没法根据通讯协议来进行判断我们到底是发多少数据了,对于一恶搞莫得感情的机器其本身是不知道有多长的,都得咱自己去设置,所以这个情况下,我们只能知道我是啥时候发的数据,但是具体是什么时候结束我们没法进行判断。因此,为了完善这个缺陷,我们必须额外再去定义一个参数,什么参数呢?简单来讲就是我们一收一发的两个设备,事先商量好,我们在收发数据的时候大概以一个同样大小速率去进行,双方都不要超过这个大致的数据范围。也就相当于我们跑步没有人给我们喊121,那我们就事先说好我们跑的时候大概都去以一个怎样的步频去跑、然后谁都不要超过或者慢于这个速率就可以,稍微差一点点多一点点倒是问题不大,这样我们还是可以知道要发送多少数据。像这样,我们当前协定好的这样的一个数据的传输速率,在串口通讯协议里面就称之为波特率。现在我们给出波特率的相关概念:
“波特率”(Baudrate),它表示每秒钟传输了多少个码元。在二进制的世界码元和位是等价的。用每秒传输的比特数表示波特率。
值得注意的是,我们这里涉及到了一个新的概念:即波特:它的概念是表示每秒传输了多少个码元,那么这个码元又是个什么玩意呢?大家可能会想,这个码元是不是就和我们平时写代码有关呢?那是不是就相当于咱所说的比特也就是位呢?其实呢,我们平常用到的这个场景里边,即在我们计算机系统里面,大家可以粗略认为波特率就是比特率,也就是说你设置4800的波特率,大家可以暂且认为他就是1秒钟传输4800个比特,即传输4800个位。但是实际上在真正的通讯领域里面,“波特”和“比特”这两个概念是不一样的,波特说的是每秒传输了多少个码元,这个码元实际上的意思就是说:我现在物理上要传递一个电平信号,这个信号就是一个码元,但这个电平信号不一定就只是一位,虽然咱数字信号传递的时候只有0和1两种情况,但是在物理上可就不一定了,比如以下这种情况:
我现在有四种电平信号,分别是10V、5V、0V以及-5V,然后现在呈现出的波形如下图所示
你需要表示一下对应的电平信号,也就是要我们表示其中一个码元。这个时候看起来和我们数字信号中的单个波形差不多,实际上我们已经没办法直接变成只有0或者1表示的数字信号进行处理了,也就是说我们不能单纯说这一个电平信号就是一位,他甚至可能是多个位,即如果我们要把某个码元翻译成数字信号表示的话,就得使用多个位来进行编码才能表示出这四种不同的电平信号,然后才能表示出某一个码元。如上图比如说我们把10V编码成11、5V编码成10、0V编码成01、-5V编码成00,那么这个时候,我们一个码元就需要用两位来表示一个码元,显然码元不一定就表示我们的一个比特位了。
因此,我们波特率实际表示的是一秒钟传递多少个电平,即多少个码元;而我们比特率表示的是1秒钟传递多少比特,按上述例子的话,我们波特率不就是一个比特率的两倍了,因为上述例子中一个电平需要两位来表示嘛。
之所以我们现在可以粗略认为波特率传递的一个码元就相当于一个比特,主要是因为我们当前传递的电平信号就是只用0或者1来表示,也就是像数字信号中一样只有0和1两种情况。所以我们现在波特率中多少个波特就是有多少个比特了,因此就显得完全一样了,所以大家注意一下这个点就行了。
当然了,STM32提供了串口异步通讯,异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,这样我们就能知道一段数据到底有多长,以便对信号进行解码,最后把数据全都传递完成。常见的波特率为 4800、9600、115200等。
OK,到目前为止,我们关于串口通讯协议的内容就基本叙述完了,下面我再放上关于串口通讯协议中一些概念的直接解释:
(1)空闲位
串口协议规定,当总线处于空闲状态时信号线的状态为‘1’即高电平,表示当前线路上没有数据。
(2)通讯的起始位
每开始一次通信时发送方先发出一个逻辑”0”的信号(低电平),表示传输字符的开始。因为总线空闲时为高电平所以开始一次通信时先发送一个明显区别于空闲状态的信号即低电平。
(3)通讯的停止位
停止信号可以自己设置, 可由 0.5、1、1.5 或 2个逻辑1的数据位表示,只要双方约定一致即可。
(4)有效数据位
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度在协议中并没有规定死,常被约定为 5、6、7 或8位长。构成一个字符(一般都是8位)。先发送最低位,最后发送最高位,使用低电平表示‘0’、高电平表示‘1’完成数据位的传输。
(5)校验位
数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。串口校验分几种方式:
1、无校验(no parity);
2、奇校验(odd parity):如果数据位中“1”的数目是偶数,则校验位为“1”,如果“1”的数目是奇数,校验位为“0”;
3、偶校验(even parity):如果数据为中“1”的数目是偶数,则校验位为“0”,如果为奇数,校验位为“1”。
二、USART外设
前面介绍了通讯相关的基础知识以及串口通讯协议的相关概念与原理后,我们思考:如果要在芯片或者一些设备上实现串口的通讯也并不是那么的简单,首先我们与芯片交互,我们前面说过芯片内部还是并行传输,因此如果要利用串口与芯片交互的话我们还需要串并转换的电路;其次我们串口通讯没有时钟,需要给双方都设置相应的波特率,那么我们底层还需要设计一个能够设置波特率的地方,再者,我们两边设备的一收一发也需要相应电路的支持。所以呢,我们要实现设备间串口的通讯,并不仅仅是一个通讯协议就能搞定的,还需要底层也有相应的电路才合适。
那么在我们STM32中具体的硬件电路一般情况就是叫做UART(Universal Asynchronous Receiver Transmitter),全称叫通用异步收发器,而我们本节标题所说的USART说的是在通用异步收发器的基础上,增加了一个S(Synchronous)同步的功能,也就相当于USART是在UART的基础上增加了时钟信号的功能。即在STM32中片内外设模块上提供的是USART,也就是说它本身一个通用的同步异步收发器,可以工作在同步条件下,也可以工作在异步条件下。看名字我们可以知道其本质上就是一个具有发送和接受功能的一个硬件装置。
即 STM32给我们提供的USART实际上就是一个进行串口通讯需要的硬件装置,是一个串行通信设备,可以灵活的与外部设备进行全双工的数据交互。在外面一些资料中也会看见串口称为UART的,实际上就相当于是去掉了我们这里USART中的同步通讯功能,形成的原本最传统、更简单的一种异步传输方式。
在STM32中,一共提供了5个串口功能供开发者选择使用:
STM32中对串口的设置就是扩展出来以上图中的这些功能,即异步模式(串口通讯最基本、最经典的一种传输模式,5个串口都支持)、硬件流控制(后面讲解时再结合原理图去了解,只有USART1-3支持)、多缓存通讯(走DMA直接内存访问通道)(USART1-3以及UART4支持,只有5不支持)、多处理器通讯(5个串口均支持)、同步(显然只有USART会支持)、智能卡、半双工(单线模式)(就是只有其中一根数据线工作,就能变成一个半双工的模式,5个串口均支持)、IrDA(是一种红外收发上会使用的)、LIN(局域网上,通常被用在汽车方面工业应用上,专门为汽车的应用提出来的一种链接传输数据的标准)
值得注意的是:USART1串口时这五个串口中速度最快的一个,大概可以达到4.5MB/s的出具传输速度,其他串口的速度理论上只能达到USART1的一半,那么这样的话这些串口对应的外设总线以及对应的时钟频率都会有所不同。之前我们描述STM32的总线体系架构时就发现其外设实际上被利用两条不同的外设总线连接,即APB1(低速外设总线,最高36MHz)和APB2(高速外设总线,最高72MHz),显然我们的USART1就是连在了APB2上面,不妨我们打开手册看看其总线体系架构图便知:
很明显,既然USART1之外的四个串口都连在最高速度只有1的一半的总线上的话,那么理论上其他四种串口的数据传输速度当然只能有USART1的一半了。所以呢,我们使用STM32的串口时最常用的就是这个USART1了,因为其传输速度快。
这里扩展一下相关概念:
一、串口通讯协议与UART协议
大家在一些资料里面可能会看到:本身UART底层的概念说的是通用异步收发器,是一个物理上的硬件设备,而就因为我们底层上需要用到这样的硬件设备,才能实现我们前面说的这样一个串口通讯的协议或者支持他,因此有些地方也会直接把这个串口通讯协议称为UART协议,他其实代表的就是用UART这种底层硬件设备去实现的串口通讯协议,即我们这种经典的串口通讯协议
当然,在这里面有一些细节的点我们可以自行去做一些配置,前面也说过,如有效数据位的长度、奇偶校验位是否使用、停止位给的电平多少以及后面波特率的大小等等都可以去自己配置,但因为我们整体的协议都是一样的,所以有时候就直接把这个协议标准叫做UART了。
二、RS232、UART、RS485、RS422以及电脑上的COM口有啥关系
在电脑上,我们一般不会叫这个接口为UART或者是USART,我们从来也没有听说过这样的一个说法。我们都知道,过去在电脑上使用串口都是那些工程师们要对设备进行一些调试或者长距离的设备间的数据传输才会使用,早期我们的打印机就是用串口直接去做的一个连接,所以他是可以进行长距离传输的。
由于UART只是提供我们完成这样一个协议的一些基本的电路,所以如果我们要用起来进行长距离的数据传输的话,那对其要求的电气化标准就会更高,所以我们的电脑就基于原来UART的通讯协议,在能够完成原来的协议的基础上继续增加了一系列能够进行长距离传输的电气化标准。然后基于这些增加的电气化标准,就就产生了我们现在的RS-232、RS-485、RS422等等,我们可以认为他们底层都是用的串口通讯协议,由于我们这个串口通讯协议和UART那个底层硬件设备就是紧密结合在一起的,所以我们就把他们混在一起,直接就叫做UART协议了;而这个RS232、RS485这些呢,我们一般不把他们叫做什么什么协议,因为协议主要描述的是数据怎么传输,而RS232他们主要规定的是传输时候的一些电气化标准,就比方说RS232就规定的是我用所谓的负逻辑来进行电平零一的传输,其中负逻辑指的是我传输一个1的时候给的是负电压(一般是-15V ~ -5V),传输一个0的时候给的是正电压(5V ~ +15V)。设置这样的标准主要就是为了区分我们这种板子上的串口,避免传输的0和1被判断错了,因此RS232呢就把传输1需要的电压设置的很高、传输0需要的电压设置的很低,还多加了一个负逻辑进行传输,这样更容易区分0和1的信号了。关于RS232这个接口最经典的就是一种9针的接口,如下图所示就是最经典的一种RS232了
然后在电脑上,我们也会看见另外一个说法:就是通常电脑上的串口会被叫做COM口,它应该是微软用他这个操作系统管理的时候定义出来的,他把我们电脑上看到的这个串行通讯设备的名字统一叫做COM,这其实也是个缩写:Component Object Model,这里大概知道是怎么个事就行了。所以我们在电脑上看见的com1、com2等等的标志,其实表示的就是我们连在电脑上的串口了。
所以有时候为了以示区分,我们在电脑上把这个串口就叫做com口,然后在单片机上因为他没有加一些电气化标准、而是直接就在这板子上,不涉及远距离的传输,所以我们直接把单片机上的串口就叫做UART或者USART了。
RS232本身他是一个负逻辑的双线全双工的传输,然后RS485还是一个双线的,但是他是做了两根线共同传递一个信号,也就是我们之前所说的差分信号传输,这时候就能提高抗干扰性,缺点就是此时只能一次传递一路信号,就相当于变成了双线半双工的传输了。
如果在此基础上继续扩展成两组差分信号的线的话,那就相当于变成了四线全双工差分信号的传输了吧,这时候的串口就叫做RS422,就相当于是两组RS485结合在一起进行传输了。
总的来说,我们可以发现这些差分信号的传输还是负逻辑处理啊电压的高低啥的实际上都和我们串口通讯协议一点关系都没有,只是我们底层遵循的是他规定固定的一个协议标准。而我们为了进行长距离传输,要使用一个什么样的电平、什么样的电气化标准,这才是所谓RS232、285这个标准决定的。
而我们单片机上就是使用的简单的TTL电平、同时也没有作一些电气化标准的扩展,所以其底层就是直接使用的它硬件收发的外设,因此它的接口我们就直接管他叫UART了,如果再加个同步功能,那就是叫USART了。因为这个串口通讯协议很简单,硬件上实现也简单,因此这种通讯协议在单片机上使用还是挺多的,在当前的嵌入式领域使用还相对比较广泛。
三、USART_功能框图原理介绍
前面介绍了USART相关外设,接下来我们继续深入USART底层,来看看他底层的功能结构。如下图是STM32的USART结构框图:
初次看这张图,只会觉得是如此的复杂,没关系。按照以往看结构图的做法,我们先从边缘看,由引脚处往里面看就一定能看明白了。显然,我们发现,左边缘有一根线挂着很多个引脚,同时右边也有一个引脚,也就是说这之间应该就是USART的内部结构了:
大概的部分知道了,我们就来先看看引脚情况:首先左边:我们当前主要用到的、最核心的引脚就是TX和RX,即TX(Transmit)发送端,也就是USART内部往外发送的引脚,所以看图引脚连接内部的箭头是往外指的;RX(Receive)接收端,也就是外部往USART内部发、USART内部接收经过的引脚,所以看图引脚连接内部的线的箭头是往内指的。
然后,我们再来稍微看看其他引脚的作用:
SW-RX引脚:看名字也能发现这是一个接收引脚,不够前面多了个SW,实际上这是在半双工(单线模式)以及智能卡模式下才会用到的引脚,而且该引脚是存在于内部,我们外面看不见;
IRDA_OUT和IRD_IN引脚:“IRDA”这表示的是红外模块相关的,也就是这两个引脚是红外模块进行串行传输的输入输出引脚,进入USART后可以看见里面就是lrDASIR编解码模块,这就意味着我们是可以使用红外远距离模块去进行串行传输的;
nRTS与nCTS引脚:n表示的是该引脚低电平有效,RTS表示的是request to send,意思是请求发送,就是说USART内部向外发一个低电平0,请求外面往内发送数据,相当于告诉外面:“我已经准备好了,你可以给我发数据了!”,然后外面这时候就知道要给我发数据;使用另一个nCTS就是表示低电平有效,然后clear to send表达的就是外界给内部一个低电平0,然后表示外部已经清除了之前的数据,已经准备好接收下一次内部发送的数据了,相当于外部说:“我已经准备好了!你可以给我发送数据了啊!”。所以我们可以发现,这两个引脚也是一种点对点的连接方式,一边是控制外部往内部发送;一边是控制内部往外发送数据。实际上这里所说的“硬件数据流控”表达的就是硬件数据流的控制作用,通过硬件电路来直接控制是否发送和接收数据。
还有一个就是右边的引脚SCLK:这个CLK显然表示的是时钟,然后前面的S表示的是同步的意思,就是我们使用串口的同步功能以后就会多一个时钟信号,即这里就往外引出了一个同步时钟线。为什么是把时钟信号往外引?主要是为了内部和外部做一个对表的操作,相当于这时候内部给出时钟,然后外部根据我给的时钟来进行同步的操作的意思,这样就能实现同步的效果了嘛。
当然,我们现在学习主要使用的还是USART1,然后这些功能目前都不会使用到,主要还是使用的就是TX和RX两个收发引脚。
然后我们就来看看USART内部的内容:我明显可以发现,内部最主要的就是他框出的灰色部分的两组寄存器了,这里我们用数据传输的过程来讲述一下:
上图就是数据从外界发送到内部的一个过程。首先数据经过RX接收到内部,然后由于我们此时不用红外编解码模块,使用直接掠过然后经过一根线进入【接收移位寄存器】,为什么要先给这个寄存器呢?因为我们数据进入是串行传输的,使用一位一位的传进来,但是我们知道的系统内部是并行传递的,使用这里我们需要做一个串并转换,因此这里就多加了一个移位的寄存器,此时又会遇到一个问题,我怎么保证放进接收移位寄存器的数据位是对应高低的呢?其实首先会看我们串行传输的方式:如果是低位开始传,那么进入移位寄存器的数据就先放在高位,然后后来的数据就依次右移,这样就能完美的对应上了,反之也是一样。而我们这里按照前面串口通讯协议规定的是低位先传,所以这里先来的一位数据放高位即最右边,然后依次移位就行。所以这个过程就是让每一位数据都放进来直到装满【接收移位寄存器】,然后再把整个移位寄存器的数据直接并着给后面的【接收数据寄存器】,这样就能实现数据传输的串并转换了,接着在读取寄存器中的数据数据经过上面的总线给到CPU内核或者其他部分进行相关的处理即可。
同理,数据从内部发送出去也是差不多的过程:
即:总线上无论是CPU内核还是内存等其他地方计算来的数据经过写操作进入【发送数据寄存器TDR】,接着把整个数据全部交给【发送移位寄存器】进行并串转换,由于是低位开始发,所以我们线从低位连接到引脚TX,然后从低位一位一位的发送到外界就完事了。
值得注意的是:由于这里发送数据寄存器只是写入不会读取、接收数据寄存器只是读取不会写入,因此在我们进行寄存器编程时这俩寄存器是合并在了一起的,称为数据寄存器DR。
然后,下面还有一些其他东西,这里我们先做一个简单了解。下面的一些东西主要就是对我们上面的做一些控制,这里有发送器控制、接收器控制、时钟啊中断等等的控制,同时我也相应也能看见就有很多类似的寄存器了,CR1、CR2等等,CR就是表示Control register控制寄存器的意思。
这些控制寄存器可以控制串口中各种模式的选择以及我们前面所说的串口通讯协议中一些细节的相关配置等,在控制寄存器这里,可以进行一切的控制、所有的都会被这里管控。当然这些连线比较复杂,所以我们简单知道干什么的就行。
然后我们看看这样一个寄存器:图中的SR,即状态寄存器Status Register,这里就是用来表达我们发送接收以及其他的一些状态地寄存器。
如果我们要发送数据,那麽首先是不是应该确认发送数据的地方是否为空啊,所以SR中有一个【TXE】就是表示TX发送端Empty空,就是用来判断我们发送端数据是不是空,是空的那就说明我们可以继续发数据了;同理,我们接收数据的话,由于内部并行地,所以只要我们判断接收不为空就能说明他数据接收到了,因此我们就可以后面在进行下一次的接收了,所以呢SR中有一个【RXNE】表示RX接收端not empty非空,意味着这就能判断我们接收数据是否已经满了,满了就说明我们可以再去进行下一次接收了;然后我们发送数据完毕后,显然根据前面对协议的叙述,我们总线上就会变成空闲状态,那么当一整个发送数据部分全部处于空闲即出现一个空闲帧的时候,就说明我们此时是数据发送完毕了,此时SR中的【IDLE】位就会置1,表示已经空闲了,我数据发送完毕了,可以接收了的意思。这三个位就是我们后面进行编程时经常会用到的位了,这里先简单提一下。
然后我们再往下会发现还有一个寄存器,即波特率寄存器
主要就是来控制我们接收器和发送器的波特率的一个寄存器。上图可以看出,我们把波特率设置好了以后,因为串口所连的外设总线分了高速外设总线APB2和低速外设总线APB1,所以这里还会根据总线不同使用不同的时钟频率,然后除以一个【/USARTDIV】,然后再/16,最后才会作为时钟给到我们发送器和接收器。也就是说,理论上我们把他们看成时钟信号是没有问题的,因为我们没有同步功能时不就是使用波特率来控制他们发送接收的速率嘛,功能上和时钟是差不多的。
好,这就是关于USART结构框图的一个基本介绍了。
四、波特率的设置
然后我们再来仔细看看这个波特率寄存器部分的原理。 首先这里有发送器波特率控制以及接收器波特率的控制,通过使能【TE】TX Enable发送端使能和【RE】RX Enable接收端使能来控制这俩的开启,当开启发送和接收的波特率控制后,就会用中间这个16位的值进行波特率寄存器配置,其中这16位被分成了两部分,低4位是表示小数部分、高12位是表示整数部分,这样有零有整可能更精确,当然在二进制中小数部分没那么方便计算,使用呢我们可以发现上图左下角有一个计算公式,也就是我们把这16位值给到【/USARTDIV】时经过的计算:他是把小数部分先直接当整数拿出来,然后/16,由于4位小数位一个就是16种,那么/16以后这个小数位不就是处于[0,1]了,就可以看作小数,然后再加上整数部分的12位,这俩的和作为【/USARTDIV】即可。接着就是再根据不同的外设总线上的串口给不同的时钟频率【fPCLKx】,接着【/16】就可以分别给到发送和接收器去了。
所以,整个过程中计算以后,我们最终使用的波特率就是【fPCLK】 / (【USARTDIV】* 16)得到的结果了。我们打开手册也能看见其波特率计算公式相类似的,如下图:
如上图,我们波特率的计算方式就是等于时钟频率除以(USARTDIV(也就是我们16位值分开经过计算以后得到的值)×16)。
就比如我们现在要计算的波特率为115200,那么我们直接套公式打开计算机计算就行,就是右边分母的USARTDIV移到左边去,再把波特率移到右边作为分母就能计算了。假如我们时钟频率是72MHz此时我们通过计算可以算出USARTDIV的值为 39.0625,如下图
显然,此时我们得到的USARTDIV就是39.0625,然后我们就只需要经过下图中的公式计算
就可以得到我们要配置的值了。当然我们需要的值是一个16进制数,然后根据这个公式,我们很容易中的其中的12位整数就是对应十进制的39,我们通过计算机可以中的39的16进制是27,12位对应二进制是 0000 0010 0111
然后,我们再看小数部分的东西,除去整数,按照公式就是小数位/16就是我们当前十进制的0.0625了,所以小数位对应十进制就是0.0625*16=1了,即小数位十六进制也就是1。
综上可以中的我们要配置的16位值对应16进制就是0x271(或者说0x0271),换成二进制就是【0000 0010 0111 0001】
然后,手册中也给我们提供了常见波特率的寄存器配置的值,如下图
由图中就能发现,我们刚才计算的115200,也就是图中表示的115.2Kbps的波特率,对应72MHz的计算的置于寄存器中的值就是39.0625,和我们计算的一样的。同时我们会发现,超过该值以后,误差就开始增大了,这是为什么呢?主要原因就是我们计算出来的值小数部分有很多位,然后由于只保留了一位小数,所以就与实际值产生了误差,我们以230.4Kpbs为例,亲自计算一下就能发现的确是这样如下图,然后上图中取得是19.5,但实际计算的是19.53125,省掉的0.03125就产生了误差,导致实际的波特率偏大了。
好了,上面就是对波特率和对应寄存器值的计算和配置。
五、总结
本次关于串口的理论知识的讲解就到此为止了。
本次主要讲解了串口的相关前置知识,包括通讯方式的分类以及串口和串口通讯协议的介绍,然后深入STM32中,对底层的硬件结构进行了相关介绍,包括了USART(通用同步异步收发器)以及其底层功能框图的介绍,最后再深入讲解了波特率寄存器的配置以及相关的计算。本次内容篇幅较长,笔者编写不易,也希望读者能够耐心阅读,相应你我都能有所收获!
以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!
鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!