通信模块
在STM32中,集成了很多用于通信的外设模块,比如USART、I2C、SPI、CAN和USB。
通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。比如我们的STM32芯片,它里面集成了很多功能模块,什么定时器计数、PWM输出、AD采集等等,这些都是芯片内部的电路,这些电路的配置寄存器、数据寄存器都在芯片里面,操作这些寄存器非常简单,直接读写就行了,但是也有一些功能是STM32内部没有的,比如我们想要蓝牙无线遥控的功能、想要陀螺仪加速度计测量姿态的功能,STM32没有,所以就只能外挂芯片来完成,那外挂的芯片,它的数据都在STM32外面,STM32如何才能获取到这些数据呢?这就需要我们在这两个设备之间,连接上一根或者多根通信线,通过通信线路发送或者接收数据,完成数据交换,从而实现外挂模块和读取外挂模块数据的目的。所以,通讯的目的是将一个设备的数据传送到另一个设备。
通信协议:制定通信的规则,通信双方按照协议规则进行数据收发。

USART串口:它的引脚是TX和RX,有的地方也叫TXD和RXD,这两种名称是一个意思,TX(Transmit Exchange)是数据发送脚,RX(Receive Exchange)是数据接收脚。
I2C通信:它的引脚是SCL和SDA,SCL(Serial Clock)是时钟,SDA(Serial Data)是数据。
SPI通信:它的引脚是SCLK、MOSI、MISO、CS,SCLK(Serial Clock)是时钟,MOSI(Master Output Slave Input)是主机输出数据脚,MISO(Master Input Slave Output)是主机输入数据脚,CS(Chip Select)是片选,用于指定通信的对象。
Can通信:它的引脚是CAN_H和CAN_L,这两个是差分数据脚,是用两个引脚表示一个差分数据。
USB通信:它的引脚是DP(Data Positive)和DM(Data Minus)或者叫D+和D-,也是一对差分数据脚。
差分数据传输使用两条信号线来传输同一个信号,但两条线上的信号是相反的(即,一条线上传输高电平时,另一条线上传输低电平,反之亦然)。这两条信号线通常被称为 D+ 和 D- 或 CAN_H 和 CAN_L。
例如,在 CAN 总线中,信号线为 CAN_H 和 CAN_L,而在 USB 中,信号线为 D+ 和 D-。
差分数据传输是一种通过两条信号线传输相反信号的技术,具有抗干扰能力强、信号完整性好和电磁辐射低等优点。在 STM32 微控制器中,CAN 通信和 USB 通信都使用差分数据传输,以确保稳定可靠的数据传输。了解差分传输的工作原理有助于更好地设计和调试相关的通信系统。
抗干扰能力强:
因为差分信号的两条线上的信号是相反的,所以电磁干扰会同时影响两条线,从而使干扰信号在接收端被抵消。
为什么电磁干扰会同时影响两条线?
电磁干扰(EMI)通常是由外部电磁场引起的,这些电磁场可以来自多种来源,如电机、电源线、无线设备等。差分信号传输线通常是成对的,并且距离非常接近,通常在同一根电缆或同一个走线区域内。
由于这两条信号线相互靠近并且平行布置,它们在暴露于相同的外部电磁场时,几乎同时受到相同的干扰。这种干扰信号在两条线上的幅度和相位基本相同,被称为共模干扰。
为什么干扰信号会被抵消?

数据按照协议的规定在这些引脚上进行输入和输出,从而实现通信。
接着我们看一下这些通信协议的特性:
1.双工模式:
全双工,就是指双方能够同时进行双向通信,一般来说,全双工的通信都有两根通信线,比如串口,一根TX发送,一根RX接收。SPI,一根MOSI发送,一根MISO接收。发送线路和接收线路互不影响,叫做全双工。
剩下的这些,I2C、CAN和USB,都只有一根数据线,CAN和USB两根差分线也是组合成为一根数据线的,所以都是半双工。
当然还有一种方式,就是单工,单工是指数据只能从一个设备到另一个设备,而不能反着来。比如把串口的RX引脚去掉,那串口就退化成单工了。
2.时钟特性:
比如你发送一个波形,高电平然后低电平(1、0);接收方是怎么知道你是1、0还是1、1、0、0呢?这就需要有个时钟信号来告诉接收方,你什么时候需要采集数据。
时钟特性分为同步和异步,这里I2C和SPI有单独的时钟线,所以他们是同步的,接收方可以在时钟信号的指引下进行采样。
剩下的串口、CAN和USB没有时钟线,所以需要双方约定一个采样频率,这就是异步通信,并且还需要加一些帧头和帧尾,进行采样位置的对齐。
3.电平特性:
USART、I2C和SPI都是单端信号,也就是它们引脚的高低电平都是对GND的电压差,所以单端信号通信的双方必须要共地,就是把GND接在一起,所以这里通信的引脚,还应该加一个GND引脚,不接GND是没办法通信的。
CAN和USB是差分信号,他是靠两个差分引脚的电压差来传输信号的,在通信的时候,可以不需要GND。不过USB协议里面也有一些地方需要单端信号,所以USB还是需要共地的。使用差分信号可以极大的提高抗干扰特性,所以差分信号一般传输速度和距离都会非常高,性能也很不错。
4.设备特性:
串口和USB属于是点对点的通信;I2C、SPI和CAN是可以在总线上挂在多个设备的。
点对点通信就相当于老师找你去办公室谈话,只有两个人,直接传输数据就行了。
多设备就相当于老师在教室里,面对所有同学讲话,需要有一个寻址的过程,以确定通信的对象。
串口通信
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。在我们这个单片机的领域,串口其实是一种最简单的通信接口,它的协议相比较I2C、SPI等,已经是非常简单的了,而且一般单片机,它里面都会有串口的硬件外设,使用也是非常方便的。一般串口都是点对点的通信,所以是两个设备之间的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,其中单片机和电脑通信,是串口的一大优势,可以接电脑屏幕,非常适合调试程序,打印信息。像I2C、SPI这些,一般都是芯片之间的通信,不会接在电脑上。极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。
串口硬件电路

一般串口通信模块都有4个引脚,VCC、TX、RX、GND。VCC和GND是供电,TX和RX是通信的引脚。TX和RX是单端信号,它们的高低电平都是相对于GND的,所以严格上来说,GND应该也算是通信线。所以串口通信的TX、RX、GND是必须要接的,上面的VCC如果两个设备都有独立的供电,那VCC可以不接,如果其中一个设备没有供电,比如这里的设备1是STM32,设备2是蓝牙串口模块,STM32有独立供电,蓝牙串口没有独立供电,那就需要把蓝牙串口的VCC和STM32的VCC接在一起,STM32通过VCC向蓝牙串口模块供电。
简单双向串口通信有两根通信线(发送端TX和接收端RX),复杂的串口通信还有其他引脚,比如时钟引脚、硬件流控制的引脚这些引脚STM32的串口也有,不过我们最常用还是简单的串口通信。
TX与RX要交叉连接,这个也好理解,TX是发送,RX是接收,那你肯定得是一个设备的发送接另一个设备的接收。
当只需单向的数据传输时,可以只接一根通信线。比如你只需要设备1向设备2的单向通信,那就可以只接一根TX到RX的线,另一根就可以不接,这就变成了单工的通信方式。
当电平标准不一致时,需要加电平转换芯片。串口也是有很多电平标准的,像我们这种直接从控制器里出来的信号,一般都是TTL电平,相同的电平才能互相通信,不同的电平信号,需要加一个电平转换芯片,转接一下。
说到电平标准,我们就来看一下串口常用的几种电平标准,电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:(我们单片机中最常见的是TTL电平,也就是5V或者3.3V表示逻辑1,0V表示逻辑0)
TTL电平:+3.3V或+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0 (一般在大型机器上使用,由于环境比较恶劣,静电干扰比较大,所以这里电平电压都比较大,而且允许波动的范围也很大)
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号),这里电平参考是两线的电压差,所以RS485的电平是差分信号,抗干扰能力非常强,通信距离可以达到上千米,而TTL和RS232最远只能几十米,再远就传不了了。
像单片机这种低压小型设备,使用的都是TTL电平,我们之后的内容也都是基于TTL电平来讲解的,如果你做设备需要其他电平,那就再加电平转换芯片就行了。在软件层面,他们都属于串口,所以程序并不会有什么变化。
串口的参数
如何用1和0,来组成我们想要发送的一个字节数据?
看一下下面的两个时序图,这就是串口发送一个字节的格式:

这个格式是串口协议规定的,串口中,每一个字节都装载在一个数据帧里,每个数据帧都由起始位、数据位和停止位组成,这里数据位有8个,代表一个字节的8位。而在右边这个数据帧中,还可以在数据位的最后加一个奇偶校验位,这样数据位总共就是9位,其中有效载荷是前8位,代表一个字节,校验位跟在有效荷载后面,占1位。
串口的参数:
波特率:串口通信的速率,它的用途是规定串口的通信速率,我们之前说了,串口一般使用异步通信,所以需要双方约定一个通信速率,比如我每隔1s发送一位,那你也得每隔1s接收一位,如果你接的快了,那就回重复的接收某些位,如果你接的慢了,那就会漏掉某些位。所以说发送和接收必须要约定好速率,这个速率参数,就是波特率。波特率本来的意思是每秒传输码元的个数,单位是码元/s,或者直接叫波特(Baud);另外还有个速率表示,叫比特率,比特率的意思是每秒传输的比特数,单位是bit/s,或者叫bps。在二进制调制的情况下,一个码元就是一个bit,此时,波特率就等于比特率。像我们单片机的串口通信,基本都是二进制调制,也就是高电平表示1,低电平表示0,一位就是1bit。所以说这个串口的波特率经常会和比特率混用。不过这也没关系的,因为这两个说法的数值相等,如果是多进制调制,那波特率和比特率就不一样了。
比如我们双方规定波特率位1000bps,那就表示1s要发送1000位,每一位的时间就是1ms,发送方每隔1ms发送一位,接收方每隔1ms接收一位,这就是波特率,他决定了每隔多久发送一位。
起始位和停止位:标志一个数据帧的开始,固定为低电平。他是标志一个数据帧的开始,固定为低电平。首先串口的空闲状态是高电平,也就是没有数据传输的时候引脚必须要置高电平,作为空闲状态。然后需要传输的时候,必须要先发送一个起始位,这个起始位必须是低电平,来打破空闲状态的高电平,产生一个下降沿,这个下降沿就告诉接收设备,这一帧数据要开始了。同理在字节数据发送完成后,必须要有一个停止位,这个停止位的作用是用于数据帧间隔,固定为高电平。同时这个停止位也是为下一个起始位做准备的,如果没有停止位,那当我们数据最后一位是0的时候,下次再发送新的一帧,是不是就没法产生下降沿了。
起始位固定为0,产生下降沿,表示传输开始;停止位固定为1,把引脚恢复成高电平,方便下一次的下降沿。如果没有数据了,正好引脚也为高电平,代表空闲状态
。
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行。比如我要发送一个字节0x0F,就是0000 1111,低位先行就是数据要从低位开始发送,就是 1111 0000,依次放在发送引脚上。
校验位:用于数据验证,根据数据位计算得来。这里串口是使用的一种叫奇偶校验的数据验证方法,奇偶校验可以判断数据传输是不是出错了,如果数据出错了,可以选择丢弃或者要求重传,校验可以选择3种方式,无校验、奇校验和偶校验。无校验就是不需要校验位,就是左边这个图;奇校验和偶校验的波形就是右边这个,如果使用了奇校验,那么包括校验位在内的9位数据会出现奇数个1,比如你传输0000 1111,目前有4个1,是偶数个,那么校验位就需要再补一个1,连同校验位就是0000 1111 1 ,总共5个1,保证1为奇数。如果数据是0000 1110,此时3个1,是奇数个,那么校验位就是0,总共3个1,1的个数为奇数。发送方在发送数据后,会补一个校验位,保证1的个数为奇数;接收方,在接收数据后,会验证数据位和校验位,如果1的个数还是奇数,就认为数据没有出错,如果传输中,因为干扰有一位由1变成0,或者由0变成1,那么整个数据的奇偶特性就会变化,接收方一验证,发现1的个数不是奇数,那就认为传输出错,就可以选择丢弃或者重传,偶校验同理。当然奇偶校验的检出率并不是很高,比如如果有两位同时出错,奇偶特性不变,那就校验不出来了。所以奇偶校验只能保证一定程度上的校验,如果想要更高的检出率,可以了解一下CRC校验,这个校验会更加好用,当然也会更复杂。我们这个STM32内部也由CRC外设,可以了解一下。
最后再说明一下,我们这里的数据位,有两种表示方法,一种是把校验位作为数据位的一部分,就像上面的图一样,分为8位数据和9位数据,其中9位数据就是8位有效载荷和1位校验位;另一种就是把数据位和校验位独立开,数据位就是有效载荷,校验位就是独立的1位。
串口的时序
这个波形是发送一个字节数据0x55时,在TX引脚输出的波形:

波特率是9600,所以每一位的时间就是1/9600,大概是104us。
可以看到,这里1位就是100us多一点,就是104us,没发送数据的时候,是空闲状态高电平,数据帧开始,先发送起始位,产生下降沿,代表数据帧的开始,0x55转为二进制0101 0101,低位先行就是依次发送1010 1010,然后这个没有校验位,只有一个停止位,拉到高电平,这样一个数据帧就完成了。
在STM32中,这个根据字节数据翻转高低电平,是由USART外设自动完成的,不用我们操心,当然你也可以软件模拟产生这样的波形,就是定时器定一个104us的时间,时间到之后,按照数据帧的要求,调用GPIO_WriteBit置高低电平。产生一个和这一模一样的波形,这样也是可以完成串口通信的。
另外注意一下,串口的停止位是可以进行配置的,可以选择1位、1.5位、2位等:

停止位变成2位,可以让数据分割的更宽一些。