通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统
STM32 芯片里面集成了很多功能模块,如定时器计数、PWM 输出、AD 采集等等,这些都是芯片内部的电路,这些电路的配置寄存器、数据寄存器都在芯片里面,操作这些寄存器非常简单,直接读写就行了。但是也有一些功能是 STM32 内部没有的,比如我们想要蓝牙无线遥控的功能,想要陀螺仪加速度计测量姿态的功能,STM32 没有,所以就只能外挂芯片来完成,那外挂的芯片,它的数据都在 STM32 外面,STM32 如何才能获取到这些数据呢?这就需要我们在这两个设备之间,连接上一根或者多根通信线,通过通信线路发送或者接受数据,完成数据交换,从而实现控制外挂模块和读取外挂模块数据的目的。所以通信的目的是将一个设备的数据传送到另一个设备。
单片机有了通信的功能,就能与众多别的模块互联,极大地扩展了硬件系统。
通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
通信的目的是进行信息传递,双方约定的规则就是通信协议。
在 STM32 中,集成了很多用于通信的外设模块,比如 USART、I2C、SPI、CAN 和 USB,这么多通信接口,我们这个 C8T6 芯片是全部都支持的。
在 STM32 里面有下表这么多的通信协议:
名称 | 引脚 | 双工 | 时钟 | 电平 | 设备 |
---|---|---|---|---|---|
USART | TX、RX(或称 TXD、RXD) | 全双工 | 异步 | 单端 | 点对点 |
I2C | SCL、SDA | 半双工 | 同步 | 单端 | 多设备 |
SPI | SCLK、MOSI、MISO、CS | 全双工 | 同步 | 单端 | 多设备 |
CAN | CAN_H、CAN_L | 半双工 | 异步 | 差分 | 多设备 |
USB | DP、DM | 半双工 | 异步 | 差分 | 点对点 |
表中只列了通信协议的一个最典型的参数,因为各种通信协议应用都很宽泛,参数也很多,所以这里列出的仅是它最常用、最简单的配置。
通信协议规定的引脚:数据按照协议的规定在这些引脚上进行输入和输出,从而实现通信。
- TX 与 RX:TX(Transmit Exchange) 是数据发送脚,RX(Receive Exchange) 是数据接收脚。
- SCL 与 SDA:SCL(Serial Clock)是时钟,SDA 是数据。
- SCLK、MOSI、MISO、CS:SCLK(Serial Clock)是时钟,MOSI(Master Output Slave Input)是主机输出数据脚,MISO(Master Input Slave Output)是主机输入数据脚,CS(Chip Select)是片选,用于指定通信的对象。
- CAN_H、CAN_L:这两个是差分数据脚,用两个引脚表示一个差分数据。
- DP、DM:或者叫 D+ 和 D-,也是一对差分数据脚
双工模式:
- 全双工:指通信双方能够同时进行双向通信。一般来说,全双工的通信都有两根通信线,比如串口,一根 TX 发送,一根 RX 接收;SPI,一根 MOSI 发送,一根 MISO 接收 。发送线路和接收线路互不影响,全双工。
- 半双工:剩下的这些,I2C、CAN 和 USB,都只有一根数据线,CAN 和 USB 两根差分线也是组合成为一根数据线的,所以都是半双工。当然还有一种方式,就是单工。
- 单工:是指数据只能从一个设备到另一个设备,而不能反着来,比如把 串口的 RX 引脚去掉,那串口就退化成单工了。
时钟特性:比如你发送一个波形,高电平然后低电平,接收方怎么知道你是 1、0 还是 1、1、0、0 呢?这就需要有一个时钟信号来告诉接收方,你什么时候需要采集数据。时钟特性分为同步和异步。
- 同步:这里 I2C 和 SPI 有单独的时钟线,所以它们是同步的,接收方可以在时钟信号的指引下进行采样。
- 异步:剩下的串口、CAN 和 USB 没有时钟线,所以需要双方约定一个采样频率,这就是异步通信。并且还需要加一些帧头帧尾等,进行采样位置的对齐。
电平特性:
- 上面三个都是单端信号,也就是它们引脚的高低电平都是对 GND 的电压差,所以单端信号通信的双方必须要共地,就是把 GND 接在一起,所以说这里通信的引脚,前三个还应该加一个 GND 引脚,不接 GND 是无法通信的。
- 之后 CAN 和 USB 是差分信号,它是靠两个差分引脚的电压差来传输信号的,是差分信号。在通信的时候可以不需要 GND,不过 USB 协议里面也有一些地方需要单端信号,所以 USB 还是需要共地的。使用差分信号可以极大地提高抗干扰特性,所以差分信号一般传输速度和距离都会非常高,性能也是很不错的。
设备特性:
- 串口 和 USB 属于点对点通信,点对点通信就相当于老师找你去办公室谈话,只有两个人,直接传输数据就可以了。
- 中间三个是可以在总线上挂载多个设备的,多设备就相当于老师在教室里,面对所有同学谈话,需要有一个寻址的过程,以确定通信的对象。
那本节。我们就来学习一下这里的第一个通信接口 — USART 串口。
就是软硬件的规则,与某个具体的硬件无关。
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
- 在单片机领域,串口其实是一种最简单的通信接口。它的协议相比较 I2C、SPI 等,已经是非常简单了。而且一般单片机,它里面都会有串口的硬件外设,使用也是非常方便的。
- 一般串口都是点对点通信,所以是两个设备之间的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。
其中单片机和电脑通信是串口的一大优势,可以接电脑屏幕,非常适合调试程序,打印信息,像 I2C 和 SPI 这些,一般都是芯片之间的通信,不会接在电脑上。
简单双向串口通信有两根通信线(发送端 TX 和接收端 RX),复杂的串口通信还有其他引脚,比如时钟引脚、硬件流控制的引脚,这些引脚 STM32 的串口也有,不过我们最常用的还是简单的串口通信。也就是 VCC、GND、TX 和 TX 这 4 个引脚。
TX与RX要交叉连接。TX 是发送,RX 是接收,那肯定是一个设备的发送接另一个设备的接收,一个设备的接收接另一个设备的发送 这样来接线。这个注意一下,别接错了。
当只需单向的数据传输时,可以只接一根通信线。比如你只需要设备 1 向设备 2 的单向通信,那就可以只接这一根 TX 到 RX 的线,另一根就可以不接,这就变成了单工的通信方式。
当电平标准不一致时,需要加电平转换芯片。串口也是有很多电平标准的,像我们这种,直接从控制器里出来的信号,一般都是 TTL 电平,相同的电平才能互相通信,不同的电平信号,需要加一个电平转换芯片,转接一下。
串口接线图:
一般串口通信的模块都有 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 通过这根线,向右边的子模块供电,当然供电的电压也需要注意一下,要按照子模块的要求来,这就是供电要求。
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
- 我们在单片机电路中最常见的是 TTL 电平,也就是 5V 或者 3.3V 表示逻辑 1,0V 表示逻辑 0,这是我们最多遇到的电平标准。但是串口还有一些其他的电平标准,这里了解一下。
- 这里 1 的电压,如果你是 5V 的器件,就是 +5V;如果是 3.3V 的器件,就是 +3.3V。逻辑 1,就是高电平的电压,就是 VCC 的电压。
RS232 电平一般在大型的机器上使用,由于环境可能比较恶劣,静电干扰比较大,所以这里电平的电压都比较大,而且允许波动的范围也很大。
这里电平参考是两线压差,所以 RS485 电平是差分信号,差分信号抗干扰能力非常强,使用 RS485 电平标准,通信距离可以达到上千米。而上面这两种电平,最远只能达到几十米,再远就传不了了。
像单片机这种低压小型设备,使用的都是 TTL 电平,我们之后的内容,也都是基于 TTL 电平来讲解的,如果你做设备需要其他的电平,那就再加电平转换芯片就行了,在软件层面,它们都属于串口,所以程序并不会有什么变化。
到这里,串口协议的硬件部分我们就清楚了。在硬件电路上,协议规定是,一个设备使用 TX 发送高低电平,另一个设备使用 RX 接收高低电平。在线路中,使用 TTL 电平,因为 STM32 是 3.3V 的器件,所以如果线路对地是 3.3V,就代表发送了逻辑 1;如果线路对地是 0V,就代表发送了逻辑 0,那现在如何接线,如何发送 1 和 0 我们就知道了。
接下来我们来看一下串口协议的软件部分。如何用 1 和 0,来组成我们想要发送的一个字节数据。
这两个时序图就是串口发送一个字节的格式。这个格式是串口协议规定的,串口中,每一个字节都装载在一个数据帧里面,每个数据帧都由起始位、数据位和停止位组成,这里数据位有 8 个,代表一个字节的 8 位;在下边这个数据帧里面,还可以在数据位的最后,加一个奇偶校验位,这样数据位总共就是九位,其中有效载荷是前 8 位,代表一个字节,校验位跟在有效载荷后面,占 1 位,这就是串口数据帧的整体结构。
那我们来看一下串口的参数:
串口一般是使用异步通信,所以需要双方约定一个通信速率,比如我每隔 1s 发送一位,那你就也得每隔 1s 接收一位,如果你接收快了,那就会重复接收某些位。如果你接收慢了,那就会漏掉某些位,所以说发送和接收,必须要约定好速率,这个速率参数,就是波特率。
波特率本来的意思是每秒传输码元的个数,单位是 码元/s,或者直接叫波特(Baud);另外还有个速率表示,叫比特率,比特率的意思是每秒传输的比特数,单位是 bit/s,或者叫 bps。在二进制调制的情况下,一个码元就是一个 bit,此时波特率就等于比特率,像我们单片机的串口通信,基本都是二进制调制,也就是高电平表示 1,低电平表示 0,一位就是 1bit,所以说,这个串口的波特率,经常会和比特率混用,不过这也是没关系的,因为这两个说法的数值相等。如果是多进制调制,那比特率和波特率就不一样了,这个了解一下。
那反应到波形上,比如我们双方规定波特率为 1000bps,那就表示,1s 要发 1000 位,每一位的时间就是 1ms,发送方每隔 1ms 发送一位,接收方每隔 1ms 接收一位,这就是波特率,它决定了每隔多久发送一位。
- 在时序图的波形中,首先,串口的空闲状态是高电平,也就是没有数据传输的时候,引脚必须要置高电平,作为空闲状态,然后需要传输的时候,必须要先发送一个起始位,这个起始位必须是低电平,来打破空闲状态的高电平,产生一个下降沿,这个下降沿,就告诉接收设备,这一帧数据要开始了。如果没有起始位,那当我发送 8 个 1 的时候,是不是数据线就一直都是高电平,没有任何波动,对吧,这样接收方怎么知道我发送数据了呢?所以这里必须要有一个固定为低电平的起始位,产生下降沿,来告诉接收设备,我要发送数据了。
- 同原理,在一个字节数据发送完成之后,必须要有一个停止位。停止位的作用是:用于数据帧间隔,固定为高电平。同时这个停止位,也是为下一个起始位做准备的,如果没有停止位,那当我数据最后一位是 0 的时候,下次再发送新的一帧,是不是就没法产生下降沿了,对吧,这就是起始位和停止位的作用。
起始位固定为 0,产生下降沿,表示传输开始,停止位固定为 1,把引脚恢复成高电平,方便下一次的下降沿,如果没有数据了,正好引脚也为高电平,代表空闲状态。
比如我要发送一个字节,是 0x0F,那就首先把 0F 转换为二进制,就是 0000 1111,然后低位先行,所以数据要从低位开始发送。也就是 1111 0000,像这样,依次放在发送引脚上,所以最终,引脚的波形就是 111(空闲状态) 0(起始位) 1111 0000 1(停止位) 111(空闲状态)。所以说如果你想发送 0x0F 这一个字节数据,那就按照波特率要求,定时翻转引脚电平,产生一个这样的波形就行了。
这里串口使用的是一种叫奇偶检验的数据验证方法,奇偶校验可以判断数据传输是不是出错了,如果数据出错了,可以选择丢弃或者要求重传,校验可以选择 3 种方式,无校验、奇校验和偶校验,无校验,就是不需要校验位,波形就是上边这个,起始位、数据位、停止位,总共 3 个部分;奇校验和偶校验的波形是下边这个,起始位、数据位、校验位、停止位,总共 4 个部分。如果使用了奇校验,那么包括校验位在内的 9 位数据会出现奇数个 1,比如如果你传输 0000 1111,目前总共 4 个 1,是偶数个,那么校验位就需要再补一个 1,连同检验位就是 0000 1111 1,总共 5 个 1,保证 1 为奇数;如果数据是 0000 1110,此时 3 个 1,是奇数个,那么检验位就补一个 0,连同校验位就是 0000 1110 0,总共还是 3 个 1,1 的个数为奇数。发送方在发送数据后,会补一个校验位,保证 1 的个数为 奇数,接收方在接收数据后,会验证数据位和检验位,如果 1 的个数还是奇数,就认为数据没有出错,如果在传输中,因为干扰,有 1 位由 1 变成 0,或者由 0 变成 1 了,那么整个数据的奇偶特性就会变化,接收方一验证,发现 1 的个数不是奇数,那就认为传输出错,就可以选择丢弃或者要求重传,这就是奇校验的差错控制方法。如果选择双方约定偶校验,那就是保证 1 的个数为偶数,校验方法也是一样的道理。当然奇偶校验的 检出率并不是很高,比如如果有两位数据同时出错,奇偶特性不变,那就校验不出来了,所以奇偶校验只能保证一定程度上的数据校验,如果想要更高的检出率,可以了解一下 CRC 校验,这个校验会更加好用,当然也会更复杂,我们这个 STM32 内部也有 CRC 的外设,可以了解一下。
我们这里的数据位,有两种表示方法,一种是把检验位作为数据位的一部分,就像上面那个时序一样,分为 8 位数据和 9 位数据,其中 9 位数据,就是 8 位有效载荷和 1 位校验位;另一种就是把数据位和校验位独立开,数据位就是有效载荷,校验位就是独立的 1 位,像上面的描述,就是把数据位和校验位分开描述了,在串口助手软件里,也是用的这种分开描述的方法,数据位 8 位,检验位 1 位。总之,无论是合在一起,还是分开描述,描述的都是同一个东西,这个应该也好理解。
通过波形理解串口是如何来传输数据的,这些波形是用示波器实测的。(操作方法是,把探头的 GND 接在负极,探头接在发送设备的 TX 引脚,然后发送数据,就能捕捉到这些波形了。
到这里,有关串口协议的硬件和软件就介绍完了,总结一下就是,TX 引脚输出定时翻转的高低电平,RX 引脚定时读取引脚的高低电平,每个字节的数据加上起始位、停止位、可选的校验位,打包成数据帧,依次输出在 TX 引脚,另一端 RX 引脚依次接收,这样就完成了字节数据的传递。这就是串口通信。
外设作用:按照串口协议来产生和接收高低电平信号,实现串口通信。
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
- 我们经常还会遇到串口,叫 UART,这里少了 S,就是异步收发器;
- 其实这个STM32 的 USART 同步模式,只是多了个时钟输出而已,它只支持时钟输出,不支持时钟输入,所以这个同步模式更多的是为了兼容别的协议或者特殊用途而设计的,并不支持两个 USART 之间进行同步通信。所以我们学习串口,主要还是异步通信。
- 一般我们串口很少使用同步功能。所以 USART 和 UART 使用起来,也没什么区别。
USART 是 STM32 内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从 TX 引脚发送出去,也可自动接收 RX 引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。
- 我们之前学习串口协议,串口主要就是靠收发约定好的波形来进行通信的,那这个 USART 外设,就是串口通信的硬件支持电路,USART 大体可分为发送和接收两部分,发送部分就是将数据寄存器的一个字节数据,自动转换为协议规定的波形,从 TX 引脚发送出去;接收部分就是自动接收 RX 引脚的波形,按照协议规定,解码为一个字节数据,存放在数据寄存器里。这就是 USART 电路的功能。
- 当我们配置好了 USART 电路,直接读写数据寄存器,就能自动发送和接收数据了。
自带波特率发生器,最高达4.5Mbits/s
这个波特率发生器就是用来配置波特率的,它其实就是一个分频器,比如我们 APB2 总线给个 72MHz 的频率,然后波特率发生器进行一个分频,得到我们想要的波特率时钟,最后在这个时钟下,进行收发,就是我们指定的通信波特率。
可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
- 这些就是 STM32 的 USART 支持配置的参数了,这个数据位长度就是我们 串口参数及时序 节的参数,有 8 位和 9 位,是包含奇偶校验位的长度,一般不需要校验就选 8 位,需要校验就选 9 位。
- 停止位长度支持三种停止位,也就是在进行连续发送时,停止位长度决定了帧的间隔,我们最常用的就是 1 位停止位,其他的很少用。
可选校验位(无校验/奇校验/偶校验)
我们最常用的是无校验。
以上这些所有的参数,都是可以通过配置寄存器来完成的。使用库函数配置的话,就更简单了,直接给结构体赋值就行了。
串口参数我们最常用的是波特率 9600 或者 115200,数据位 8 位,停止位 1 位,无校验,一般我们都选这种常用的参数,不用纠结,选它就是了。
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
- 同步模式,就是多了个时钟 CLK 的输出
- 硬件流控制,比如 A 设备有个 TX 向 B 设备的 RX 发送数据,A 设备一直在发,发的太快了,B 处理不过来,如果没有硬件流控制,那 B 就只能抛弃新数据或者覆盖原数据了,如果有硬件流控制,在硬件电路上,就会多出一根线,如果 B 没准备好接收,就置高电平,如果准备好了,就置低电平,A 接收到了 B 反馈的准备信号,就只会在 B 准备好的时候,才发数据,如果 B 没准备好,那数据就不会发送出去。这就是硬件流控制,可以防止因为 B 处理慢而导致的数据丢失的问题。硬件流控制,STM32 也是有的,不过我们一般不用,需要的话可以了解一下。
- DMA,是这个串口支持 DMA 进行数据转运,如果有大量的数据进行收发,可以使用 DMA 转运数据,减轻 CPU 的负担。
- 智能卡、IrDA、LIN,这些是其他的一些协议,因为这些协议和串口是非常像的,所以 STM32 就对 USART 加了一些小改动,就能兼容这么多协议了。不过我们一般不用。智能卡应该是跟我们刷的饭卡、公交卡这类有关的;IrDA 用于红外通信,这个红外通信就是一个红外发光管,另一边是红外接收管,然后靠闪烁红外光通信,并不是我们遥控器的那个红外通信,所以并不能模拟遥控器;LIN 是局域网的通信协议,这个感兴趣的话,可以自己研究一下。
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
总共 3 个独立的 USART 外设,可以挂载很多串口设备,其中这里 USART1 是 APB2 总线上的设备,剩下的都是 APB1 总线上的设备,这个就开启时钟的时候注意一下,在使用的时候,挂载在哪个总线,影响并不是很大。
这个框图一眼看上去还是非常复杂的,但是实际上主要部分也没有很多,它这里就是把各个寄存器和寄存器每一位控制的地方都画出来了,所以才显得比较乱,我们看的时候可以先忽略这些寄存器,先看主体结构。
到这里,这个外设的主要功能就差不多了。大体上,就是数据寄存器和移位寄存器,发送移位寄存器往 TX 引脚移位,接收移位寄存器从 RX 引脚移位,当然发送还需要加上帧头帧尾,接收还需要剔除帧头帧尾,这些操作,它内部有电路会自动执行,我们知道有硬件帮我们做了这些工作就行了。
我们一般不使用流控,所以流控就了解一下就行。
剩下还有一些寄存器的指示,比如各个 CR 控制寄存器的哪一位控制哪一部分电路,SR 状态寄存器都有哪些标志位,这些可以自己看看手册里的寄存器描述,那里的描述比这里清晰很多。
最后我们再看看串口的引脚。在引脚定义表的复用功能这一栏就给出了每个 USART,它的各个引脚都是复用在了哪个 GPIO 口上的,比如这里 USART2 的 TX 是 PA2 口,RX 是 PA3 口,USART3 的 TX 和 RX 分别是 PB10 和 PB11,然后,USART1 的 TX 和 RX 分别是 PA9 和 PA10,这些引脚都必须按照引脚定义里的规定来。
比如你要使用 USART1,那 TX 必须是 PA9,RX 必须是 PA10,或者看一下重映射这里,有没有重映射,这里有 USART1 的重映射,所有有机会换一次口。剩下的引脚,就没有机会作为 USART1 的接口了,所以这个表在设计电路的时候很重要,要提前规划好引脚,别让引脚复用功能冲突了,有关外设的复用引脚是哪个的问题,都是看引脚定义表,一看就知道。
这个图是在程序中配置 8 位字长和 9 位字长的波形对比。这里的字长就是我们前面说的数据位长度。它这里的字长,是包含校验位的描述方式。
总的来说,这里有 4 种选择,9 位字长,有校验或无校验;8 位字长,有校验或无校验;但我们最好选择 9 位字长,有校验 或 8 位字长,无校验这两种,这样每一帧的有效载荷都是 1 字节,这样才舒服。
STM32 的串口可以配置停止位长度为 0.5、1、1.5、2 这四种。这四种参数区别就是停止位的时长不一样。
就是控制停止位的时长的,一般选择 1 位停止位就行了。其他的参数不太常用。