前言
UART、I2C和SPI是我们在嵌入式开发中比较常见的通信协议了,没有最好的通信协议,每个通信协议都有自己的优缺点。如果想要通信速度快,SPI 将是理想的选择;如果用户想要连接多个设备而不是过于复杂,I2C 将是理想的选择,因为它最多可以连接 127 个设备并且易于管理;UART的通信速度相对较慢,通信也比较简单,单对单。
uart接口比较常用于主设备与蓝牙、wifi模块的通信、打印调试等。
i2c接口多用于和传感器的通信,例如触摸屏、计步器、温度传感器、EEPROM等。
spi接口多用于对速率要求高一些的场景,如spi flash、spi lcd屏、SD卡等。
1. UART通信
1.1 UART基本概念
UART(Universal Asynchronous Receiver/Transmitter):解释为通用异步接收传输,是一种串行、异步、全双工的通信协议。
① UART 没有时钟线,使用两条数据线进行设备通信,每个设备上都有一个 RX 引脚和一个 TX 引脚(RX 用于接收,TX 用于传输), RX 引脚连接到另一个设备的 TX 引脚;
② 由于 UART 没有时钟线,因此添加了起始位和停止位以表示数据的开始和结束;
③ 两个通信设备间必须以大致相同的波特率(误差不超过10%)运行,否则就可能操作数据错乱;
④ 异步通信以一个字符为传输单位,通信中两个相邻字符之间的时间间隔是不固定,但同一字符中相邻两个位之间的时间间隔是固定的。
1.2 通信数据格式
空闲:空闲时总线为高电平,通常软件上把数据线配置为推挽、上拉;
起始位:主设备发送一个逻辑“0”信号表示传输字符的开始;
数据位:数据位可以是5~9位组成的一个字符(通常为8位),从最低位开始传输;
校验位:通常我们设置为无奇偶检验位;
奇校验:如果数据位中“1”的个数为偶数,则校验位为“1”,为奇数,则校验位为‘0’;
偶校验:如果数据位中“1”的个数为偶数,则校验位为“0”,为奇数,则校验位为“1”;
停止位:可以是 1 、1.5 位或 2 位的逻辑“1”电平,由于同一字符间相邻位的传输时间是固定的,每个设备uart都有自己的时钟,在通信中两个设备之间很可能会出现一点不同步,所以停止位不仅表示传输结束,还能提供纠正时钟的作用,停止位越多,数据传输越稳定,但数据传输速度越慢。
1.3 UART、USART区别
USART (通用同步/异步接收器/发送器)可以说和uart是包含关系,uart有的功能usart都有(异步功能模式),usart可以工作在异步和同步模式下。
在同步模式下,有自己的时钟线,此时接收端不需要知道发送端的波特率,数据是以帧的形式传输的,传输速率比uart高。
uart的协议结构很简单,而USART 更为复杂,可以生成与许多不同标准协议相对应的形式的数据,例如 IrDA、LIN、智能卡、RS-485 接口的驱动程序启用和 Modbus 等。
2. I2C通信
2.1. I2C基本概念
I2C(内部集成电路)接口有2条线,分别是SCL(串行时钟线)和SDA(串行数据线)。SCL用作数据的同步传输,一般由主设备提供给从设备;另外,由于数据线只有一条;因此,它是个串行、同步、半双工的通信接口。
① I2C支持多主、多从;任何设备都可以同时作为主从,但同时只能有一个主控,一个I2C控制器可以支持挂多个I2C从机设备,其中每个从机的设备地址都是不一样的,以便I2C主控制器访问到该设备;
② 硬件I2C的SCL和SDA接口电路必须外接上拉电阻,空闲时,两条线都为高电平;
③ 起始、停止信号都是由主机端发出的,在起始信号产生后,总线就处于被占用的状态;在停止信号产生后,总线就处于空闲状态;
④ I2C总线以一个字节(8bit)为单位传输数据,每传输一个字节时,必须得到数据接收方的应答信号,数据都是从最高有效位开始传输,
⑤ I2C的每次通信都是由主设备发起的,在这个过程中,发起端扮演了两个角色,当处于应答时段发起端扮演从端,其余时间都扮演主端;
⑥ 主设备每次发送数据和读写位时,都会等待从设备的响应信号ACK。
2.2 通信协议格式
空闲态:SCL和SDA线均为高电平,处于空闲状态;
起始信号:当SCL为高电平期间,SDA由高到低电平的跳变(启动信号是一种电平跳变时序信号,而不是一个电平信号);
地址位:由7位或者10位组成,主设备端发送从设备的设备地址;
读写位:逻辑“0”表示主向从写操作,逻辑“1”表示主向从读操作;
应答位:从设备通过将SDA线拉为低电平来表示ACK(有效应答)成功,SDA被拉高表示NACK(无效应答);
数据位:一个字节代表一个数据,SCL为高电平的时候读取SDA的有效数据,每字节数据后都需要接收从设备的应答,检测是否接收成功;
停止信号:当SCL为高电平期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
2.3 工作过程
在空闲状态的时候,主设备向从设备发送起始信号,告诉从设备将要开始i2c通信了;
发送完起始信号后,接着发送需要通信的从设备的设备地址,该i2c控制器下的每个从设备对比该设备地址,比对不正确的忽略此次通信,匹配成功的继续通信;
接着主设备发送读写位,主设备释放总线,并等待从设备的应答信号,发起端接收到应答信号后,从设备就会释放总线控制,总线控制权归还给发起端;
写操作:
发送需要操作的从设备寄存器地址 + 写入寄存器的值;
读操作:
相对写操作会复杂些,写入需要读的从设备寄存器地址,等待从设备的应答后,主设备重新向从设备发送起始信号 + 从设备地址 ,并将读写位设备为读,等待从设备应答,最后就会开始接收从设备发送过来的数据,直到停止信号发生。
2.4 传输模式
除超快速模式外,其它都是可以向后兼容的,也就是说快速模式设备向后兼容,可以与 0 至 100 kbit/s I2C 总线系统的标准模式设备通信,然而,由于标准模式设备不向上兼容,它们不能在快速 I2C 总线系统中运行。
网图2.5. I2C死锁问题
死锁是什么意思呢?就是你在等我做某件事,而我又在等你做某件事,然后双方就互相等待,啥也不去做了,这就是死锁。
发生死锁的常见情况有两种:
① 从设备回复ACK时主设备异常复位;
② 从设备回复数据位为0时,主设备异常复位。
两者的共同点就是SDA在主设备异常复位时,处于被从设备拉为低电平的状态,而SCL在主设备复位后处于空闲状态(高电平)。此时从设备会等待主设备拉低SCL取ACK或数据位,主设备会等待从设备释放SDA线。主设备和从设备互相等待,进入死锁状态。
解决死锁问题的可尝试方法:
主设备检测到SDA拉低超过一段时间后,主动复位从设备并释放SDA线。这种方法的前提是从设备有复位引脚,MCU可以控制从设备的复位引脚对其进行复位。主设备检测到SDA拉低超过一段时间后,将9个时钟信号推入时钟总线(即接收应答信号),然后取出从设备的ACK位,使从设备释放SDA到高电平。在主设备和从设备之间串联一个 I2C 缓冲器,可以自动检测死锁情况。当检测到死锁时,会主动断开与主设备的连接,并向从设备发送9个时钟信号,从设备释放SDA线后,会重新与主设备建立连接。I2C死锁问题无法从根本上避免。除了MCU异常复位导致I2C死锁外,正常通信过程中从设备也可能异常拉低SDA导致死锁。因此,软件应该设计成当死锁发生时能够从死锁中恢复,这样I2C通信才能继续进行。
3. SPI通信
3.1 SPI基本概念
SPI(Serial Peripheral Interface):串行外设接口,它是由摩托罗拉开发的一种串行、同步、全双工的通信协议,可以同时发送和接收数据,发送的数据先发到发送数据缓存区中,然后再到移位寄存器中按位传送,数据都是以字节为单位,高位优先发送。
① SPI 总线总共有 4 条线:
MOSI :主设备数据输出,从设备数据输入;MISO :主设备数据输入,从设备数据输出;SCLK: 主设备产生的时钟信号;CS/SS:(Chip select)由master设备控制,选择与哪个SPI从设备通信;通常设为低电平表示使能该从设备;② 支持一主多从,一个主设备控制多个从设备,通常配置为每个从设备对应一条CS线,通过拉低CS信号,选择需要通信的从设备,如果同时将两个SS信号线拉低,则可能会出现乱码,因为从机可能都试图在同一条MISO线上传输数据,最终导致接收数据乱码;
③ 在一个时钟周期内,SPI 设备会进行1 bit 数据的发送和接收,其实就是主从间数据交换的过程,在数据传输的过程中,接收到的数据必须在下一次数据传输之前被采样,如果之前接收到的数据没有被采样,那么之前的数据就会被覆盖掉,丢失。
④ 主、从设备必须工作在同一模式下。
3.2 四种工作模式
SPI通信时钟信号是由主设备产生的,其工作模式就涉及到时钟极性(CKP/CPOL)和时钟相位(CKE/CPHA)了,时钟极性和时钟相位共同决定SPI读取数据的模式;
时钟极性:设为“0”表示空闲状态时钟为低电平,
设为“1”表示空闲状态时钟为高电平;
时钟相位:设为"0"表示在时钟信号的第一个跳变沿采样,
设为"1"表示在时钟信号的第二个跳变沿采样;
要理解这四个工作模式,你必须要知道的一点就是,SPI通信传输过程其实就是主、从设备数据交换的一个过程,MOSI 和 MISO在一个时钟周期内都会进行数据位的发送和接收,如果不需要接收的数据,直接忽略就可以了,不做处理。
3.2.1 模式0 (CPOL = 0,CPHA = 0)
在空闲状态下,SCLK处于低电平,在第1个时钟边沿进行数据采样。SCLK由低电平到高电平的跳变进行数据采样(第一个跳变沿采样),采样完在下一个跳变沿发送数据。
3.2.2 模式1 (CPOL = 0,CPHA = 1)
在空闲状态下,SCLK处于低电平,在第2个时钟边沿进行数据采样,第一个时钟沿进行发送数据。
3.2.3 模式2 (CPOL = 1,CPHA = 0)
在空闲状态下,SCLK处于高电平,在第1个时钟边沿进行数据采样。SCLK由高电平到低电平的跳变进行数据采样(第一个跳变沿采样),采样完在下一个跳变沿发送数据。
3.2.4 模式3 (CPOL = 1,CPHA = 1)
在空闲状态下,SCLK处于高电平,在第2个时钟边沿进行数据采样,第一个时钟沿进行发送数据。