通信协议-04-SPI通信
本文主要是通信协议——SPI通信相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
参考方向 | 参考原文 |
驱动开发指南 | i.MX6ULL Linux阿尔法开发板资料 |
STM32开发指南 | STM32F1开发指南-库函数版本_V3.3 |
野火STM32开发指南 | STM32库开发实战指南——基于野火霸道开发板 文档 (embedfire.com) |
一、 SPI协议简介
1. 什么是SPI?
SPI 协议,Serial Peripheral Interface ,即串行外围设备接口, 是由摩托罗拉公司提出的通讯协议( Motorola) 在1980前后提出的一种全双工同步串行通信接口,它用于MCU与各种外围设备以串行方式进行通信以交换信息,通信速度最高可达25MHz以上。SPI接口主要应用在EEPROM、 FLASH、实时时钟、网络控制器、 OLED显示驱动器、 AD转换器,数字信号处理器、数字信号解码器等与MCU间要求通讯速率较高的场合 。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
SPI 时钟频率相比 I2C 要高很多,最高可以工作在上百 MHz。 SPI 以主从方式工作,通常是有一个主设备和一个或多个从设备,一般 SPI 需要 4 根线,但是也可以使用三根线(单向传输) 。
2. 与I2C的对比
功能说明 | SPI总线 | I2C总线 |
---|---|---|
通信方式 | 同步 串行 全双工 | 同步 串行 半双工 |
通信速度 | 一般50MHz以下 | 100KHz、 400KHz、 3.4MHz |
从设备选择 | 引脚片选 | 设备地址片选 |
总线接口 | MOSI、 MISO、 SCK、 CS | SDA、 SCL |
SPI通常由四条线组成,一条主设备输出与从设备输入( Master Output Slave Input, MOSI) , 一条主设备输入与从设备输出( Master Input Slave Output, MISO) , 一条时钟信号( Serial Clock, SCLK) ,一条从设备使能选择( Chip Select, CS) 。 与I2C类似,协议都比较简单,也可以使用GPIO模拟SPI时序。
SPI可以同时发出和接收数据,因此SPI的理论传输速度比I2C更快。 SPI通过片选引脚选择从机,一个片选一个从机,因此在多从机结构中,需要占用较多引脚,而I2C通过设备地址选择从机,只要设备地址不冲突,始终只需要两个引脚。
二、基本原理简介
- (1)SPI接口一般使用 4 条线通信:
接口名称 | 说明 |
---|---|
MISO | 主设备数据输入,从设备数据输出 |
MOSI | 主设备数据输出,从设备数据输入 |
SCLK | 时钟信号,由主设备产生 |
CS | 从设备片选信号,由主设备控制 |
- (2)主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
- (3)串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
- (4)外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
三、设备连接
1. 物理拓扑结构
SPI通信设备之间常见的连接方式如下图:
- CS( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用CS表示。
当有多个SPI从设备与SPI主机相连时, 设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同只使用这3条总线; 而每个从设备都有独立的这一条CS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。
I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI协议中没有设备地址,它使用CS信号线来寻址, 当主机要选择从设备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效, 接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以CS线置低电平为开始信号,以CS线被拉高作为结束信号。
- SCK (Serial Clock):时钟信号线,用于通讯数据同步
它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样, 如STM32的SPI时钟频率最大为 fpclk/2 ,两个设备之间通讯时,通讯速率受限于低速设备。
- MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。
主机的数据从这条信号线输出, 从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
- MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。
主机从这条信号线读入数据, 从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
2. 数据交换
在SCK时钟周期的驱动下, MOSI和MISO同时进行,如下图所示,可以看作一个虚拟的环形拓扑结构。
主机和从机都有一个移位寄存器,主机移位寄存器数据经过MOSI将数据写入从机的移位寄存器,此时从机移位寄存器的数据也通过MISO传给了主机,实现了两个移位寄存器的数据交换。无论主机还是从机,发送和接收都是同时进行的,如同一个“环”。如果主机只对从机进行写操作,主机只需忽略接收的从机数据即可。如果主机要读取从机数据,需要主机发送一个空数据来引发从机发送数据。
四、SPI通信过程
1. 通信时序
这里的片选引脚用 NSS 表示了,它与CS是一个意思,前边有说明。
这是一个主机的通讯时序。NSS、SCK、MOSI信号都由主机控制产生,而MISO的信号由从机产生,主机通过该信号线读取从机的数据。 MOSI与MISO的信号只在NSS为低电平的时候才有效,在SCK的每个时钟周期MOSI和MISO传输一位数据。SPI通信发送是先发高字节再发低字节,接收是先收高字节再收低字节。
2. 起始与停止信号
SPI通信协议图中标号为 ① 和 ⑥ 的地方为起始和停止信号:
NSS信号线由高变低,是SPI通讯的起始信号。NSS是每个从机各自独占的信号线, 当从机在自己的NSS线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。
NSS信号由低变高, 是SPI通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。
3. 数据传输
SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据, 且数据输入输出是同时进行的。数据传输时,MSB先行或LSB先行并没有作硬性规定,但要保证两个SPI通讯设备之间使用同样的协定, 一般都会采用图 SPI通讯时序中的MSB先行模式。SPI每次数据传输可以8位或16位为单位,每次传输的单位数不受限制。
图中的 ②、③、④、⑤标号处,MOSI及MISO的数据在SCK的上升沿期间变化输出,在SCK的下降沿时被采样。即在SCK的下降沿时刻, MOSI及MISO的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI和MISO为下一次传输数据做准备。
4.四种通信模式
4.1 CPOL与CPHA
SPI 有四种工作模式,通过串行时钟极性(CPOL)和相位(CPHA)的搭配来得到四种工作模式。
CPOL( Clock Polarity,时钟极性) 表示SCK在空闲时(即SPI通讯开始前、 NSS线为高电平时SCK的状态)为高电平还是低电平。 当CPOL=0, SCK空闲时为低电平, 当CPOL=1, SCK空闲时为高电平。
CPHA( Clock Phase,时钟相位) 表示SCK在第几个时钟边缘采样数据。 当CPHA=0, 在SCK第一个边沿采样数据,当CPHA=1, 在SCK第二个边沿采样数据。 也可以这样理解,时钟相位CPHA是指数据的采样的时刻,当CPHA=0时, MOSI或MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样。当CPHA=1时,数据线在SCK的“偶数边沿”采样。 这样搭配下来就会有四种模式:
- CPOL=0,串行时钟空闲状态为低电平。
- CPOL=1,串行时钟空闲状态为高电平,此时可以通过配置时钟相位(CPHA)来选择具体的传输协议。
- CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
- CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。
SPI模式 | CPOL | CPHA | 空闲时SCK时钟 | 采样时刻 | 说明 |
---|---|---|---|---|---|
0 | 0 | 0 | 低电平 | 奇数边沿 | 时钟空闲状态为低电平; 在时钟第一个边沿(上升沿) 采样数据 |
1 | 0 | 1 | 低电平 | 偶数边沿 | 时钟空闲状态为低电平; 在时钟第二个边沿(下降沿) 采样数据 |
2 | 1 | 0 | 高电平 | 奇数边沿 | 时钟空闲状态为高电平; 在时钟第一个边沿(下降沿) 采样数据 |
3 | 1 | 1 | 高电平 | 偶数边沿 | 时钟空闲状态为高电平; 在时钟第二个边沿(上升沿) 采样数据 |
实际使用较多的是模式0和模式3。
4.2 CPHA = 0
这是 CPHA=0 的时序图:
(1)首先,根据SCK在空闲状态时的电平,分为两种情况。 SCK信号线在空闲状态为低电平时,CPOL=0;空闲状态为高电平时,CPOL=1。
(2)无论CPOL=0还是1,由于我们配置的时钟相位CPHA=0,采样时刻都是在SCK的奇数边沿。 注意当CPOL=0的时候,时钟的奇数边沿是上升沿,而CPOL=1的时候,时钟的奇数边沿是下降沿,所以SPI的采样时刻不是由上升/下降沿决定的。 MOSI和MISO数据线的有效信号在SCK的奇数边沿保持不变,数据信号将在SCK奇数边沿时被采样,在非采样时刻,MOSI和MISO的有效信号才发生切换。
4.3 CPHA = 1
(1)SCK信号线在空闲状态为低电平时, CPOL=0;空闲状态为高电平时, CPOL=1。
(2)与CPHA=0类似,当CPHA=1时,不受CPOL的影响,数据信号在SCK的偶数边沿被采样。MOSI和MISO数据线的有效信号在SCK的偶数边沿保持不变,数据信
号将在SCK偶数边沿时被采样,在非采样时刻, MOSI和MISO的有效信号才发生切换。
五、模拟SPI
我们已经了解了SPI的通信协议,那么我们接下来来了解一下如何用软件模拟SPI协议,以加深对SPI协议的理解。这里通过STM32驱动W25QXX为例,做基础的初始化和定义(基于HAL库),不过这里只实现SPI并不做对W25QXX读写的说明。
1. GPIO选择与引脚定义
1.1 宏定义
首先定义SPI传输涉及的四个引脚PA4、 PA5、 PA6、 PA7, 其中MISO( PA6)为输入引脚,其它全为输出引脚
1 |
1.2 GPIO初始化
随后将四个GPIO引脚初始化,使能引脚时钟,设置输入/输出模式。 SCK、 MOSI、 CS引脚,始终为输出模式, MISO引脚为数据输入引脚, 始终为输入模式
1 | void SPI_Init(void) |
2. 延时函数定义
我们既然是模拟SPI,那么就需要手动产生时钟,所以这里当然就需要延时函数啦:
1 |
定时器实现的延时函数,延时时间为 t us,为了缩短时间,这里这是演示,所以就不写具体实现了,具体的我们可以使用定时器来实现us级延时。
3. SPI读写函数
这里假设SPI主机工作在模式0,参考前面的SPI时序。
3.1 写一个字节
1 | /* |
第 13 - 23 行:SPI写1 Byte,循环8次,每次发送1 Bit。
3.2 读一个字节
1 | /* |
第 13 - 25 行:SPI读1 Byte,循环8次,每次接收1 Bit。
3.3 读写一个字节
SPI传输可以看作一个虚拟的环形拓扑结构,即输入和输出同时进行。在前面“ SPI_WriteByte()”函数里,发送了1 Byte,也应该接收1 Byte,只是代码中忽略了接收引脚MISO的状态; 在前面“ SPI_ReadByte()”函数里,接收了1 Byte,也应该发送1 Byte,只是代码中忽略了发送引脚MOSI的内容。有些场景, SPI需要同时读写,因此还需要编写SPI同时读写函数,
1 | /* |
第14 - 31行: SPI读和写1 Byte,循环8次,每次发送和接收1 Bit 。