STM32 串口

串口

1.如果想发送字符,那我们就需要一个数据到字符的映射表
最常用的映射表,就是ASCII码表。 ASCII表中,0x41就映射为字符A
所以发送0x41时,如果以HEX模式显示,就是数据本身。如果以文本显示,就会先去找一下映射表,最终发现,0x41对应就是字符A,所以就显示A。
这就是HEX模式和文本模式的区别。

在这里插入图片描述

这些电路的配置寄存器,数据寄存器都在芯片里面,操作这些寄存器非常简单,直接读写就行了。
但是也有一些功能就是STM32内部没有的,比如我们想要蓝牙无线遥控功能,想要陀螺仪加速度计测量姿态的功能,STM32没有,所以就只能外挂芯片来完成。外挂的芯片,它的数据都在STM32外面,STM32如何获取这些数据,就需要我们两个设备之间,连上一根或多跟通信线,通过线路发送或接收数据,完成数据交换,从而实现控制外挂模块和读取外挂模块数据的目的。

TX(Transmit Exchange) 数据发送脚
RX(Receive Exchange)数据接收脚

SDA(Serial Data) 数据线
SCL(Serial Clock)时钟

MOSI(Master Output Slave Input)主机输出数据脚
MISO(Master Input Slave Output)主机输入数据脚
CS(Chip Select) 片选 用于指定通信的对象
CAN_H和CAN_L 两个差分数据脚,用两个引脚表示一个差分数据

USB通信 引脚DP(Data Positive)和DM(Data Minus)或者叫D+和D-,也是一对差分数据脚,这里I2C和SPI有单独的时钟线,所以它们是同步的,接收方可以在时钟信号的指引下进行采样,剩下的串口、CAN和USB没有时钟线,所以需要双方约定一个采样频率,这就是异步通信,并且还需要加一些帧头帧尾,进行采样位置的对齐,之后是电平特性,上面3个都是单端信号,也就是它们引脚的高低电平都是对GND的电压差,所以单端信号通信双方必须要共地,就是把GND接在一起,,所以说这里通信的引脚,前三个还应该加一个GND引脚,不接GND是没法通信的,

后面的CAN和USB是差分信号,它是靠两个差分引脚的电压差来传输信号的,在通信的时候可以不需要GND,不过USB协议里面也有一些地方需要单端信号,所以USB还是需要共地的。使用差分信号能极大地提高抗干扰特性,所以差分信号一般传递速度和距离都会非常高,性能也不错。

在这里插入图片描述

单片机里面都会有串口,
一般串口是点对点通信,是两个设备之间的互相通信,
IIC和SPI这些,一般是芯片之间的通信,不会接在电脑上,

在这里插入图片描述
TX和RX都是单端信号,它们的高低电平都是相对于GND的,所以严格意义来说,GND也算是通信线。所以串口通信的TX、RX和GND必须要接。如果两个设备有独立供电,那VCC可以不接,

复杂一点的串口通信还有其他引脚:比如时钟引脚,硬件流控制引脚。

在这里插入图片描述
相同的电平才能互相通信,不同的电平信号,需要加一个电平转换芯片,
RS232一般在大型机器上使用,由于环境恶劣,静电干扰比较大,,所以这里的电平电压都比较大,而且允许波动的范围也很大,
RS485电平是差分信号,差分线抗干扰能力非常强,使用485电平标准,通信距离可以达到上千米。
TTL和RS232最远只能达到几十米,再远就传不了了。

单片机小型设备属于TTL,在软件层面,都属于串口,所以程序并不会有什么变化。

因为STM32是3.3V的期间,所以如果线路对地是3.3V,就代表发送了逻辑1,线路对地是0V,就代表发送了逻辑0,

在这里插入图片描述
这两个时序图,就是串口发送一个字节的格式,这个格式是串口协议规定的,串口中,每一个字节都装载在一个数据帧里面,每个数据帧都由起始位,数据位,和停止位组成。数据位有8个,代表一个字节的8位,数据位的后面可以加一个奇偶校验位,这样数据帧总共就是9位,,其中有效负载是前8位,代表一个字节,检验位跟在有效载荷后面,占1位。

异步通信要约定好速率,即波特率.如果接收快了,就会重复接收某些位,接收慢了就会漏掉某些位。
波特率本来的意思是每秒传输码元的个数,单位是码元/s,或者直接叫波特Baud;另外还有个速率表示,叫比特率,意思是每秒钟传输的比特数,单位是bit/s,或者叫bps,在二进制调制情况下,一个码元就是一个bit,此时波特率等于比特率。

前半部分我们主要讲的是車口的通信协议

如果是多进制调制。那波特率和能特率就不一样了

那反应到波形上

此如我们双方规定波特率为1000bps

那就表示。1s要发1000位,每一位的时间就是1ms

发送方每附1ms发送 位。接收方每临lmns接收-位

这就是波特率,它决定厅每隔多次发送位

首先。申口的空闲状态是高电平。也就是没有数据輸的时候

脚必须要置高电平。作为空闲状态

然后需要传输的时候。必须要先发送1个起始位

来打破空闲狀态的高电平。产生 一个下降沿,告诉接收设备这个数据要开始了

如果没有起始位,那些我发送8个1的时候

是不是效据线就一直都是高电平。没有任何波动。对吧

这样。较收方怎么知道我发送数据厅呢

所以这里必须要有一个固定为低电平的起始位

同理,在一个字节数据发送完成后。必级要有一 个停止位

这个停止位的作用是。用于数据帧间隔,固定为高电平

同时这个停止位,也是为下个起始位做准备的

如果没有停业位。那当我数据最局 位是0的时候

下次需发送新的- 帆。是不是就设法产坐下降滑子

超始位固定为0。产生下降滑。表示传諭开始,停止位国定为1。把引(脚恢复成高电平

方便下-次的下降溜。如果没有数据子

正好引脚也为高电平。代表空阅状态

这里数据位表示数据帧的有效载荷

也就是11110000。像这样。依次放在发送引脚上

所以最终。引脚波形就是这样

如果数据出错了,可以选择丢弃或者要求重传

校給可以选择种方式, 无校验、奇校验和偶校验

无校验。就是不需要校验位,波形就是左边这个

超始位、数据位停止位,总其3个部分

奇校验和偶校验的波形是右边这介

起始位、数据位、校验位、停止位。总典4个部分

如果使用了奇校验。那么包括校验位街肉的9位敪据会出现奇数 个1

接收方。在接收数据后。会验证数据位和梭验位,当然奇偶校验的检出率笄不是很高

奇偶特性不变。那就校验不出来了

比如如果有两位数据
同时出错

服以奇偶校验冥能保证 定程度上数据核验

如果想要更高的检出率,可以了解—下CRC校验

这个校验会更加好用。当然也会更复杂

最后严说明一下。我他这里的數据位。疱两科表示方法

-种
是把校验位作为数据位的- 部分

分为8位数据和9位数据

其中9位数据。就是8位有效载荷和1位校验位

昺-种就是把数据位和校验位独立牙

数据位就是有效载荷。校验位
是独立的1位

像我这上面的描述。就是把数据位和
河校验位分开描述了

在这里插入图片描述
波特率9600,所以每一位的时间就是1/9600s,大概是104us
在STM32中,根据字节数据翻转高低电平,是由USART外设自动完成的,不用我们操心,当然软件也可以产生这样的波形,那就是定时器定一个104us的时间,时间到之后,按照数据帧的要求,调用GPIO WriteBit置高低电平,产生一个和这个一模一样的波形,这样也是可以完成串口通信的,TX引脚发送,就是置高低电平,在RX引脚接收,显然就是读取高低电平了,也由USART外设自动完成,不用操心,如果软件模拟的话,就是定时调用GPIO ReadInputDataBit来读取每一位,最终拼成一个字节,当然接收的时候,应该还需要一个外部中断,在起始位的下降沿触发,进入接收状态,并且对齐采样时钟,然后依次采样8次,这就是接收的逻辑

每个字节的数据加上起始位,停止位,可选的校验位,打包为数据帧,依次输出在TX引脚,另一端RX引脚依次接收,这样就完成了字节数据的传递。

前面部分讲:
串口的通信时序:软硬件的规则:这部分与某个具体的硬件无关,

后面部分讲:STM32内部的UART外设:

外设的作用:按照串口协议来产生和接收高低电平信号,实现串口通信

在这里插入图片描述
一般串口很少用这个同步功能,所以USART和UART使用起来,没有什么区别
STM32的USART模式只是多了个时钟输出而已,它只支持时钟的输出,不支持时钟的输入,所以这个同步模式更多的是为了兼容别的协议或者特殊用途而设计的,并不支持两个USART之间的同步通信,所以学习串口,主要还是异步通信。

波特率发生器实质就是一个分频器,比如APB2总线给个72MHz的频率,然后波特率发生器进行一个分频,得到我们想要的波特率时钟。最后在这个时钟下,进行首发,就是我们指定的通信波特率

停止位长度决定了帧的间隔,最常用的就是1位停止位,其他的很少用。
最常用的是无校验,
以上的所有参数,都是可以通过配置寄存器来完成。如果使用库函数,更加简单。直接给结构体赋值就可以了。

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

硬件流控制,STM32也有,但是一般不用。

串口支持DMA进行数据转运,如果有大量的数据进行收发,可以使用DMA转运数据,减轻CPU的负担
智能卡,IrDA,LIN是其他的一些协议,所以STM32对USART加了一些小改动,就能兼容这么多协议了
不过我们一般不用,
智能卡应该是跟我们刷的饭卡,公交卡有关的,IrDA是用于红外通信的,这个红外通信就是一个红外发光管,另一边是红外接收管,然后靠闪烁红外光通信,LIN是局域网的通信协议,

在这里插入图片描述

在这里插入图片描述
这里把各个寄存器和寄存器的每一位控制的地方都画出来了,所以显得比较乱,我们看的时候可以先忽略这些寄存器,先看主体结构,左上角的引脚部分,TX和RX就是两个发送和接收引脚,SW_RX、IRDA_OUT/IN这些都是智能卡和IrDA通信的引脚,我们不用这些协议,所以这些引脚可以先不用管,右边框框里的IrDA,SIR这些东西也都不用管,

发送和接收的字节放在右边那一块,串口的数据寄存器,一个是发送数据寄存器 TDR Transmit DR,另一个是接收数据寄存器RDR(Receive DR),这两个寄存器占用同一个地址,就跟51单片机串口的SBUF寄存器一样,在程序上,只表现为一个寄存器,就是数据寄存器DR Data Register,一个用于发送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电平,先放在最高位,然后向右移,一位一位地读取RX电平,先放在最高位,然后向右移,移位8次之后,就能接收一个字节了,同样因为串口协议规定的低位先行,所以接收移位寄存器是从高位往低位这个方向移动的,之后,当一个字节移动完成之后,一下子转移到这个RDR接收数据寄存器里,叫RXNE RX Not Empty,接收数据寄存器非空,当我们检测到RXNE置1之后,就可以把数据读走了,同样这里也是两个寄存器进行缓存,当数据从移位寄存器转移到RDR时,就饿可以直接移位接收下一帧数据了。这就是USART外设整个的工作流程。

接着继续看一下控制部分和一些其他的增强功能

下面是发送器控制,就是用来控制发送移位寄存器的工作的,接收器控制是用来控制接收移位寄存器的工作,左边有一个硬件数据流控,也就是硬件流控制,简称流控,有两个引脚,一个是nRTS(Request To  Send)请求发送,是输出脚,也就是告诉别人,我当前能不能接收,一个是nCTS(Clear To Send)是清除发送,是输入脚,用于接收别人nRTS的信号的,这里前面加一个n的意思是低电平有效。
那这两个脚上是怎么玩的呢,首先,得找到另一个支持流控的串口,他的TX到我的RX,然后我的RTS要输出一个能不能接收的反馈信号接到对方的CTS,当我能接收的时候,RTS就置低电平,请求对方发送,对方的CTS接收到之后,就可以一直发,当我处理不过来的时候,比如接收数据寄存器我一直没有读,又有新的数据过来了,现在就代表我没有及时处理,那RTS就会置高电平,对方CTS接收到之后,就会暂停发送,直到这里接收数据寄存器被读走,RTS置低电平,新的数据才会继续发送。那反过来,当我的TX给对方发送数据时,我们的CTS就要接到对方的RTX,用于判断对方能不能接收,TX和CTS是一对,RX和RTS是一对的,CTS和RTS也要交叉连接,当然我们一般不使用流控,所以了解一下就可以了。

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

唤醒单元的作用是实现串口挂载多设备,我们之前说过串口一般是点对点的通信(只支持两个设备互相通信,),
而多设备,在一条总线上,可以接多个从设备,每个设备分配一个地址,想要跟某个设备进行通信,就先进行寻址,确定通信对象,
唤醒单元就可以用来实现多设备的功能,,在这里给串口分配一个地址,当你发送指定地址时,此设备唤醒开始工作,当你发送别的设备地址时,别的设备就唤醒工作,这个设备没收到地址,就会保持沉默,这样就可以实现多设备的串口通信了

接着看下面,这部分时中断输出控制,中断申请位,就是状态寄存器这里的各种标志位,状态寄存器里面有两个标志位比较重要,一个是TXE发送寄存器空,另一个时RXNE接收寄存器非空,这两个是判断发送状态和接收状态的必要标志位,,剩下的标志位,了解一下就行。

就是配置中断是不是能通向NVIC,

最下面是波特率发生器部分,APB时钟进行分频,得到发送和接收移位的时钟,这里时钟输入时fPCLKx(x=1或2)
UASART挂载在APB2,所以就是PCLK2的时钟,一般是72M,其他的USART都挂载在APB1,所以PCLK1的时钟,一般是36M,之后这个时钟进行一个分频,除一个USARTDIV的分频系数,USARTDIV里面就是右边这样,是一个数值,并且分为了整数部分和小数部分,因为有些波特率,用72M除一个整数的话,可能除不尽,会有误差,所以这里分频系数是支持小数点后4位的,分频就更加精准,之后分频完之后,还有除16,得到发送器时钟和接收器时钟,通向控制部分,就是发送器使能了,发送部分的波特率就有效,如果RE(RX Enable)为1,就是接收器使能了,接收部分的波特率就有效。
到这里串口的主体结构就看完了,然后剩下一些寄存器的指示,比如各个CR控制寄存器的哪一位控制哪一部分电路,SR状态寄存器都有哪些标志位,这些可以自己看手册里的寄存器描述。

在这里插入图片描述
这就是USART最主要,最基本的结构,最左边是波特率发生器,用于产生约定的通信速率,时钟来源是PCLK1或2,经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,用来控制发送移位和接收移位,之后由发送数据寄存器和发送移位寄存器这两个寄存器的配合,将数据一位一位的移出去,通过GPIO的复用输出,输出到TX引脚,产生串口协议规定的波形,>>代表移位寄存器是往右移的,是低位先行,当数据由数据寄存器转到移位寄存器时,会置一个TXE的标志位,我们判断这个标志位,就可以知道是不是可以写下一个数据了,然后接收部分也是类似的,RX引脚的波形,通过GPIO输入,在接收控制器的控制下,一位一位地移入接收移位寄存器,因为是低位先行,所以要从左边开始移进来,移完一帧数据后,数据会统一转运到接收数据寄存器,在转运的同时,置一个RXNE标志位,我们检查这个标志位,就可以知道是不是接收到数据了,同时这个标志位也可以去申请中断,这样就可以在收到数据时,直接进入中断函数,那右边实际上有4个寄存器,但在软件层面,只有一个DR寄存器可以供我们读写,写入DR时,数据走上面这条路,进行发送,读取DR时,数据走下面这条路,进行接收,这就是USART进行串口数据收发地过程,最后右下角是一个开关控制,配置完成之后,用Cmd开启外设,这也是常规操作了。
到这里,USART的基本结构就讲完了,接下来看几个细节的问题:

在这里插入图片描述
这个图是在程序中配置8位字长和9位字长的波形对比,这里的字长就是前面说的数据位长度(包含校验位的,是这种描述,)
第一条时序,很明显就是TX发送或者RX接收的数据帧格式,在这里的位8,也就是第9个位置,是一个可能的奇偶校验位,这里可以选择配置成8位有效载荷+1位校验位,也可以选择9位都是有效载荷,不过一般选择了9位字长,那一般都是要加上校验位的,因为8位有效载荷,刚好对应一个字节,
然后下面的时钟就是我们之前说的同步时钟输出的功能,,可以看到每个数据位的中间,都有一个时钟上升沿,时钟的频率和数据速率也是一样的,接收端可以在时钟上升沿进行采样,这样就可以精准定位每一位数据,这个时钟的最后一位,可以通过这个LBCL位控制,要不要输出,另外时钟的极性、相位也可以通过配置寄存器配置,需要的话可以了解一下。

一个是空闲帧,就是从头到尾都是1,还有一个是断开帧,从头到尾都是0,这两个数据帧,是局域网协议用的,串口用不着不用管。

接着是8位字长的波形,可以看到,这里的数据位是从位0一直到位7,总共是8位,比上面这个少了一个位8,同样这个最后一位位7,也是可能的奇偶校验位,还是同样,既然选择了8位字长,那这里最好选择无校验,。

总的来说,这里有4种选择,8位和9位字长有校验和无校验,最好选9位有校验
8位字长无校验,这样每一帧的有效载荷都是1字节,这样才舒服。

在这里插入图片描述
看一下不同停止位的波形变化,STM32的串口,可以配置停止位长度为0.5、1、1.5、2这四种,这四种参数的区别,就是停止位的时常不一样,对应波形就是这样,第一个是1个停止位,这时停止位的时长和数据位的一样,时长一样,然后是1.5个停止位,这里的停止位就是数据位一位,时长的1.5倍
这就是控制停止位时长的,一般选择一个停止位就行了,其他的参数不太常用。

在这里插入图片描述
继续来看一些细节问题,这两个图展示的是USART电路输入数据的一些策略,根据我们前面的介绍,可以想到,串口的输出比输入简单很多,输出你就定时器翻转TX引脚高低电平就行了,但是输入就复杂一些,不仅要保证,输入的采样频率和波特率一致,还要保证每次输入采样的位置,要正好处于每一位的正中间,只有在每一位的正中间采样,这样高低电平读进来,才是最可靠的。如果采样点过于靠前或靠后,那有可能高低电平还正在翻转,电平还不稳定,或者稍有误差,数据就采样错了,另外,输入最好还要对噪声有一定的判断能力,如果是噪声,最好能置个标志位提醒我一下,这些就是输入数据所面临的问题。

那接下来我们看STM32是如何设计输入电路的呢,这里展示的是USART的起始位侦测,当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率,连续采样一帧数据,同时从起始位开始,采样位置就要对齐到位的正中间,只要第一位对其了,后面就肯定都是对齐的,那为了实现这些功能,首先输入的这部分电路对采样时钟进行了细分,它会以波特率的16倍频率进行采样,也就是在一位时间里,可以进行16次采样,然后他的策略是,最开始,空闲状态高电平,那采样就一直是1,在某个位置,突然采到一个0,那么就说明,在这两次采样之间,出现了下降沿,如果没有任何噪声,那之后就应该是起始位了,在起始位,会进行连续16次采样,没有噪声的话,这16次采样,肯定都是0,这没问题,但是实际电路还是会存在一些噪声的,所以这里即使出现了下降沿,后续也要多采样几次,以防万一,。根据手册描述,这个接收电路,还会在下降沿之后的第三次,5次,7次进行一批采样,在第8,9,10次,再进行一批采样,且这两批采样,都要要求每3位里面至少要有2个0,没有噪声,那肯定全是0,满足情况,
导致这3位里面,只有两个0,另一个是1,那也算是检测到了起始位,但是在状态寄存器里会置一个NE(Noisc Error),噪声标志位,就是提醒一下,数据我收到了,但是有噪声,悠着点用。
如果这里3位里面,只有1个0,那就不算检测到了起始位,可能前面那个下降沿是噪声导致的,这时电路就忽略前面的数据,重新开始捕捉下降沿,这就是STM32的串口,在接收过程中,对噪声的处理。
如果通过了起始位侦测,那状态就由空闲,变为接收起始位,同时第8,9,10次采样的位置,就正好是起始位的正中间,之后接收数据时,就都在第8,9,10次,进行采样,这样就能保证采样位置在位的正中间了,这就是起始位侦测和采样位置对齐的策略

在这里插入图片描述
那紧跟着,我们就可以看这个数据采样的流程了这里从1-16,是一个数据为的时间长度,在一个数据位,有16个采样时钟,由于起始位侦测已经对齐了采样时钟,多以这里就直接在第8,9,10次采样数据位,为了保证数据的可靠性,这里是连续采样3次,没有噪声的理想情况下,这3次肯定全为1或者全为0,全为1就认为收到了1,全为0就认为收到了0,如果有噪声导致3次采样不是全为0或者全为1,那就按照2:1的规则来,2次为什么就认为收到了什么,同时噪声标志位NE也会置1,,这就是检测噪声的数据采样,可见STM32对这个电路的设计考虑还是非常充分的。

在这里插入图片描述
最后看一下波形发生器,发送器和接收器的波特率寄存器BRR里面的DIV确定,下面这个图就是BRR寄存器,里面就是分频系数DIV,DIV分为整数部分和小数部分,可以实现更细腻的分频,

为什么这里多个16,看上面数据采样的图就明白了,因为它内部还有一个16倍波特率的采样时钟,所以这里输入时钟/DIV要等于16倍的波特率,
举个例子,比如要配置USART1为9600的波特率,那如何配置这个BRR寄存器呢,代入公式就是9600等于 USART的时钟72M,除16倍的DIV,解得,DIV=72M/9600/16,最终等于468.75,这是一个带小数的分频系数,最终写到寄存器还需要转换成二进制,这就是根据波特率写BRR寄存器的方法,了解一下,不过我们用库函数配置的话,就非常方便,,需要多少波特率,直接写就可以了,库函数会帮我们自动计算。

USB转串口的电路:

参考手册里的介绍:USART 里面介绍的知识很多,因为要讲全面,但是实际用到的就是普通串口那一块

寄存器有几个经典的分类:
状态寄存器 SR Status Register 存放各种标志位
数据寄存器 DR Data Register 存放最关键的数据
配置寄存器 CR Config Register 存放各种配置参数
这三类寄存器基本每个外设都有,然后剩下的就是一些零碎的寄存器了,

串口之间要共地,才能比较谁高谁低,当两个都有独立供电时(地平线必须一致),通信的电源正极就可以不用接了,只接3根线就好了

代码的初始化流程:

1.开启时钟,把需要用的USART和GPIO的时钟打开
2.GPIO初始化,把TX配置成复用输出,RX配置成输入
3.配置USART,直接使用一个结构体,把所有的参数都配置好
4.如果只需要发送功能,就直接开启USART,初始化就结束了,如果需要接收的功能,可能还需要配置中断,那就在开启USART之前,再加上ITConfig和NVIC的代码就行了
整个初始化流程中规中矩,初始化完成之后,如果需要发送数据,调用一个发送函数就行了,如果需要接收数据,就调用接收的函数,如果要获取发送和接收的状态,就调用获取标志位的函数,这就是USART外设的使用思路

在这里插入图片描述
这里面有很多都是那些增强型功能和兼容其他协议的函数,,我们都不会用到,真正常用的很少
Deint,Init,StructInit,这些不用多说
ClockInit,和Cloct StructInit这两个函数是用来配置同步时钟输出的,包括时钟是不是要输出,时钟的极性相位等参数,因为参数比较多,所以也是用结构体这种方式来配置的,需要时钟输出的话,可以了解一下这两个函数,然后Cmd,ITConfig,这些不用多说
USART_DMACmd,这个可以开启USART到DMA的触发通道,需要用DMA的话,可以了解一下,
下面一段,设置地址、唤醒、LIN、这些函数,我们都不用,
SendData发送数据和ReceiveData接收数据这两个函数比较重要,在我们发送和接收的时候会用到,SendData就是写DR寄存器,ReceiveData就是读DR寄存器,DR寄存器内部有4个寄存器,控制发送与接收,至于怎样产生波形,这里程序就非常简单了,写DR就是发送,读DR就是接收,怎么判断输入,软件一概不管

下面一大段函数,什么智能卡,IrDa,我们也都用不到,不用看的,
最后4个标志位相关的函数,也不用多说
总结:用的函数非常少,而且都是常见函数

Ctrl+Alt+空格,, 联想一下代码
单片机是一种软件和硬件高度配合的产品,要想学好单片机,程序思维和电路分析都是必不可少的
这里的串口助手软件都由UP设计,其实可以用上位机来写就可以了。我的建议使用C(熟练),C++(专精),Python(普用)这条路径

在这里插入图片描述
文本模式:通过查找字符集,编码成一个字符,这里的表展示的就是ASCII码字符集,可以看到0x41对应就是大写字母A
大家在做数据和字符串的相互转换时,需要查一下这个表,ASCII码是一个最简单,最常用的字符集,如果想显示和存储汉字的话,也得制定汉字的字符集,由于汉字比较多,所以就需要多个字节才能编码一个汉字,常用的汉字字符集有GB2312,GBK,GB18030等等,,对应的国外也有相应的字符集,比如欧洲的ASCII扩展码,日本的编码,韩国的编码等等,随着计算机的发展,全球互相通信,为了防止不同国家编码的不兼容现象,我们可以把所有国家的字符全部收录到一个统一的字符集,这就是Unicode字符集,Unicode最常用的传输形式就是UTF-8,如果编码不匹配,就会出现非常烦人的乱码,
右边的模式图描述的就是字符和数据,在发送和接收的转换关系,比如最上面,发送0x41数据,发送到线路传输的就是0x41,接收方如果以原始数据形式显示,就是0x41,如果以字符显示,就是走下面这一条路,通过字符集(ASCII码,GBK,UTF-8等等)译码,找到字符,显示A,在发送方,也可以直接发送字符A,这是会先从字符集找到A的数据,进行编码,发现A对应的数据是0x41,最终在线路中传输的必须是十六进制数,0x41,然后接收方,可以选择查看原始数据0x41,也可以进行译码,得到字符A,这就是字符和数据子发送接收过程中经历的变化,

封装函数,传一个字节,转到传一串字符,对Printf进行重定向,将printf函数打印的东西输出到串口,因为printf函数默认是输出到屏幕,我们单片机没有屏幕,所以要进行重定向,步骤就是,在串口模式里,最开始加上,#include <stdio.h>,之后,在这重写fputc函数
int fputc(int ch,FILE *f)(具体函数看代码)
那重定向fputc跟printf有什么关系呢,这是因为fputc是printf函数的底层,printf函数在打印的时候,就是不断调用fputc函数一个个打印的,我们把fputc函数重定向到串口,那printf自然就输出到串口了,串口头文件包含stdio,stdio里面有printf的声明,main.c又包含串口头文件

如果多个串口都想用printf怎么办呢,
方法一:这时就可以用sprintf,sprintf可以把格式化字符输出到一个字符串里,所以可以先定义一个字符串,
。。。。。。 sprintf可以指定打印位置,不涉及重定向的东西,所以每个串口都可以用sprintf进行格式化打印,
方法二:sprintf每次都得先定义字符串,再发送字符串,太麻烦了,我们要是能封装一下这个过程,就再好不过了,
方法三:封装sprintf,由于printf这类函数比较特殊,它支持可变的参数,像我们之前写的函数,参数的个数都是固定的,可变参数这个执行起来比较复杂,所以想要深入学习的话,搜索C语言可变参数,
常见的和一般使用最多的是第一种,代码的编码和串口助手的编码得一致才不会乱码
UTF-8还乱码的话,需要打开工程选项,C/C ++,这里杂项控制栏,写上–no-multibyte-chars,需要给编译器输入这样一个参数,注意别写错了,这样汉字就没问题了。或者GBK编码

在这里插入图片描述
串口数据包收发的思路和流程,
首先,数据包的作用是方便我们进行多字节的数据通信,
之前串口的代码,发送一个字节,接收一个字节,都没问题,但在实际应用中,我们可能需要把多个字节打包为一个整体进行发送,比如说,我们有个陀螺仪传感器,需要用串口发送数据到STM32,比如X轴一个字节,Y轴一个字节,Z轴一个字节,总共3个数据,需要连续不断地发送,当你像这样XYZXYZXYZ连续发送的时候,就会出现一个问题,接收方不知道这数据哪个对应X,哪个对应Y,哪个对应Z,因为接收方可能会从任意位置开始接收,所以会出现数据错位的现象,这时候我们需要研究一种方式,把这个数据进行分割,把XYZ这一批数据分割开,分成一个个数据包,这样再接收的时候,就知道了数据包的第一个数据就是X,第二个就是Y,第三个就是Z,这就是数据包的任务,把属于同一批的数据进行打包和分割,方便接收方进行识别,有关分割打包的方法,可以发挥想象力来设计。只要逻辑行得通就行,感兴趣可以了解一下。

串口数据包,通常使用的是额外添加包头包尾的方式,比如这里列举了两种数据包格式,第一种是固定包长,含包头包尾,也就是每个数据包的长度都固定不变,数据包前面是包头,后面是包尾,
第二种是可变包长,含包头包尾,也就是每个数据包的长度可以是不一样的,前面是包头,后面是包尾,
它们的数据包格式,可以是用户根据需求,自己规定的,
也可以是你买一个模块,别的开发者规定的,那我这里的规定,比如固定包长这里,一批数据,规定有4个字节,在这4个字节之前,加一个包头,比如定义0xFF为包头,在4个字节之后,加一个包尾,比如定义0xFE为包尾,那当接收到0xFF 之后,我就知道一个数据包来了,接着我再接收到4个字节,就当作数据包的第1,2,3,4个数据,存在一个数组里,最后跟一个包尾,当我收到0xFE之后,就可以置一个标志位,告诉程序,我收到了一个数据包,然后新的数据包过来,再重复之前的过程,这样就可以在一个连续不断的数据流中,分割出我们想要的数据包了,这就是添加包头包尾实现数据打包的思路,
接着我们来研究几个问题,
第一个问题就是:包头包尾和数据载荷重复的问题,这里定义FF为包头,FE为包尾,如果我传输的数据本身就是FF和FE怎么办,有几种解决方法:第一种,限制载荷数据的范围,如果可以的话,我们在发送的时候,对数据进行限幅,比如XYZ,3个数据,变化范围都可以是0-100,那就可以在载荷中只发送0-100的数据,这样就不会和包头包尾重复了,
第二种:如果无法避免载荷数据和包头包尾重复,那我们就尽量使用固定长度的数据包,这样由于载荷数据是固定的,只要我们通过包头包尾对齐的数据,哪个数据应该是包头包尾,哪个数据应该是载荷数据,在接收载荷数据的时候,我们并不会判断它是否是包头包尾,
第三种:增加包头包尾的数量,并且让它尽量呈现出载荷数据出现不了的状态,比如我们使用FF,FE作为包头,FD,FC作为包尾,这样也可以避免载荷数据和包头包尾重复的情况发生,

第二个问题:这个包头包尾并不是全部需要的,比如我们只需要一个包头,把包尾删掉,这样数据包的格式就是,一个包头FF,加4个数据,这样也是可以的,检测到FF,开始接收,收够4个字节之后,置标志位,一个数据包接收完成,不过这样的话,载荷和包头重复的问题会更严重一些,比如最严重的情况是载荷全是FF,包头也是FF,那肯定不知道哪个是包头了,而加上FE作为包尾,无论数据怎么变化,都可以分辨出包头包尾的,

第三个问题:就是固定包长和可变包长的选择问题,对于HEX数据包来说,如果你的载荷会出现和包头包尾重复的情况,那最好选择固定包长,这样可以避免接收错误,如果载荷不会和包头包尾重复,那可以选择可变包长,像这样4,3位等等,1位,10位,来回任意变,都没有问题,因为包头包尾是唯一的,只要出现包头,就开始数据包,这样就非常灵活了,这就是固定包长和可变包长选择的问题。

最后一个问题:就是各种数据转换为字节流的问题,这里数据包都是一个字节一个字节组成的,如果你想发送16位的整形数据,32位的整形数据,float,double,甚至是结构体,其实都没有问题,因为他们内部其实都是由一个字节一个字节组成的,只需要用一个uint8_t的指针指向它,把它们当作一个字节数组发送就行了,这个操作方法,指针里有。

在这里插入图片描述
这里文本数据包 分别对应了文本模式和HEX这两种模式,在HEX数据包里面,数据都是以原始的字节数据本身呈现的,而在文本数据包里面,每个字节就经过一层编码和译码,最终表现出来,就是文本格式。但实际上,每个文本字符背后,其实都还是一个个字节的HEX数据,就会存在大量的字符可以作为包头包尾了,可以有效避免载荷和包头包尾重复的问题,以\r\n也就是换行,这两个字符作为包尾,在载荷数据中间可以出现除了包头包尾的任意字符,所以文本数据包基本不用担心载荷和包头包尾重复的问题,使用非常灵活。可变包长、各种字母、符号、数字都可以随意使用,我们接收到载荷数据之后,得到的就是一个字符串,在软件中再对字符串进行操作和判断,而且字符串数据包表达的意义很明显,可以把字符串数据包直接打印到串口助手上,所以这个文本数据包,通常以换行作为包尾,

HEX数据包,比较适合一些模块发送原始的数据,比如一些使用串口通信的陀螺仪,温湿度传感器,缺点是灵活性不足,载荷容易和包头包尾重复,优点是数据直观易理解,非常灵活,比较适合一些输入指令进行人机交互的场合,比如蓝牙模块的AT指令,CNC和3D打印机常用的G代码,缺点就是解析效率低,

HEX数据包就是一个字节100,完事,文本数据包就得是3个字节的字符,‘1’,‘0’,‘0’,收到之后还要把字符转换成数据,才能得到100,其实数据包的发送非常简单,

在这里插入图片描述
演示固定包长HEX数据包的接收方法,和可变包长文本数据包的接收方法,其他的数据包也可以套用这个形式,

每收到一个字节,程序都会进一遍中断,但拿到之后,我们就要退出中断了,而对于数据包来说,很明显它具有前后关联性,包头之后是数据,数据之后是包尾,对于包头,数据,包尾这3中状态,我们都需要有不同的处理逻辑,,在不同状态执行不同的操作,同时还要执行状态的合理转移,这种程序设计思维,叫做状态机,在这里我们使用一个状态机的方法来接收一个数据包,要想设计要给好的状态机程序,画一个这样的状态转移图是必要的,

每个状态都需要用变量来标志一下。

在这里插入图片描述

串口收发数据包 ,程序还隐藏着一个问题,就是这个RxPacket数组,它是一个同时被写入又同时被读出的数组,在中断函数里,我们会依次写入它,在主函数里,我们又会依次读出它,就是数据之间可能会混在一起,比如你读出的过程太慢了,前面两个数据刚读出来,等了一会,才继续往后读,那这时后面的数据就可能会刷新为下一个数据包的数据,也就是你读取的数据可能一部分属于上一个数据包,另一部分属于下一个数据包,
解决办法:可以是在接收部分加入判断,就是在每个数据包读取处理完毕后,再接收下一个数据包,当然很多情况下其实还可以不进行处理,像这种HEX数据包,多是用来传输各种传感器的每个独立数据,他们相邻数据包的数据,具有连续性,这样即使相邻数据包混在一起了,也没关系,

FlyMCU 程序烧录软件:、STLink Utility
可以通过串口给STM32下载程序,FlyMcu是串口下载,STLINK Utility 是STLINK下载,

FlyMcu烧录程序要生成一个HEX文件,配置好串口和波特率下载后,进入BootLoader,STM32执行的程序就是,不断接收USART1的数据,刷新到主闪存。
STM32接收数据,然后刷新到0800这一块的位置就行了,接收并转存数据,这个本身也是程序,如何利用程序自我更新呢,
就像是一个机器人一样,给自己换电池,需要先拆掉旧电池,再装上新电池,但是一旦把旧电池拆掉,机器人就无法工作了,所以我们还需要做以个小机器人完成换电池的工作后再回到大机器人运行,。
同理,STM32通过串口进行程序的自我更新,这个小机器人就是BootLoader,BootLoader是ST公司写好的一段程序代码,这段程序的存储位置就是ROM区的最后,1FFF F000,这段区域叫做系统存储器,存储的是BootLoader程序,或者叫自举程序,用途是程序自我更新,串口下载,BootLoader接收USART1数据,刷新到程序存储器,这时主程序处于瘫痪状态,更新好之后,再启动主程序,执行新程序,这就是串口下载的流程。
给手机或者电脑重装系统,就可以类比BootLoader就相当于手机的刷机模式,电脑的PE系统,是辅助主程序进行自我更新的。

BOOT的启动配置,也就是1FFF F000开始运行,启动小机器人,所以说BOOT引脚切换后,都要按一下复位,
FlyMcu成功从0800开始运行,这就是在软件上,人工加入了一条跳转指令,这样就能执行主程序了,
FlyMcu的读Flash,可以把芯片里的程序读出来,这样就可以大批量山寨别人的产品了,觉得不错可以抄PCB板,程序文件可以通过这个读出来。不过STM32也可以配置读保护,反过来说,如果开发产品不注意这个问题,程序就容易泄露,为了保护程序的安全,可以设置成阻止读出,这个具体防止被抄的细节需要自己注意,里面内容应该挺多的。

STLINK固件更新功能,Fireware update,给我们的STLINK烧录器进行更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

硬匠的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值