雷猴啊,今天聊另一个高频的面试题:SPI。
这个考的概率也非常非常大,因为SPI用的实在是太多太多了,可以说80%的项目都会用到。
以前我刚做开发时,也很怕spi,同样是spi时序,这个器件可以,换一个器件就不行了。
也被这玩意儿折腾得想砸键盘。以为不就是几根线嘛,接上去时序按照spi的不就行了?
这种痛,只有搞过单片机通信的人才懂,其实就是spi四种模式搞的鬼。如果SPI协议的传输流程和这四种模式你没搞透,将来遇到的问题只会更多。
连个最基本的通信都搞不定,还怎么去驱动复杂的器件?怎么去实现更牛逼的功能?
读完这篇文章,你将彻底告别SPI模式选择困难症,拿到一个新的SPI器件,你就能快速找到它的模式信息,自信地在代码里配置正确,让你的设备顺利"对上话"。
好,不卖关子了,直接开整。
介于很多人对spi可能不太熟悉,先介绍一下,SPI(Serial Peripheral Interface)协议,中文叫串行外围接口。它是一种高速、全双工、同步通信总线。
注意关键词:同步、串行、全双工。
同步,意味着通信双方要靠同一根时钟线(SCK)来协调数据发送和接收的节奏。就像两个跳舞的人,得跟着同一个节拍器才能跳齐。
串行,意味着数据是一位一位地传输,而不是像并行通信那样同时能传几个数据。虽然速度可能不如并行,但引脚少啊,省资源。
全双工,理论上是可以在同一时刻既发送数据又接收数据。SPI有两根独立的数据线:MOSI(Master Out Slave In,主机输出,从机输入)和MISO(Master In Slave Out,主机输入,从机输出)。
主机通过MOSI发送数据给从机,从机通过MISO发送数据给主机。因为这两根线是分开的,所以理论上可以同时进行,实现全双工。但实际应用中,很多场景是主机发完命令从机再发数据,或者反之,表现得更像半双工。
除了SCK、MOSI、MISO,SPI通常还需要一根片选线(SS,Slave Select,或者叫CS,Chip Select)。
主机通过SS线来告诉某个特定的从机:“喂,说你呢,准备好,我要跟你通信了!”
当主机需要跟多个从机通信时,每路从机都需要一个独立的SS线,主机通过拉低(通常是低电平有效)对应从机的SS线来选中它。未被选中的从机在高阻态,不参与通信。
理解SPI的传输流程,关键在于理解数据是怎么随着时钟脉冲一位一位地在MOSI和MISO线上“跑”的。
主机和从机都有一个移位寄存器(Shift Register)。发送方要发送一个字节(8个bit),它先把这8个bit装进自己的移位寄存器里。
接收方也准备一个空的移位寄存器等着。
通信开始时,主机拉低目标从机的SS线。然后,主机开始在SCK线上发送时钟脉冲。
每一个时钟脉冲到来时(具体是上升沿还是下降沿,取决于模式),发送方会把移位寄存器里的最高位(或者最低位,这取决于SPI的bit序设置,通常是MSB First,即最高位先行)“推”到数据线(MOSI或MISO)上。
同时,接收方会在此时刻从数据线(MISO或MOSI)上“读”入对方发送过来的一个bit,并把它“移”进自己的移位寄存器。
这个过程是同步进行的:发送方推一个bit出去的同时,接收方拉一个bit进来。就像两个人面对面,一个人把手里的球递出去,同时接过对方递过来的球。
这样重复8次,一个字节就传输完成了。发送方的移位寄存器清空了,接收方的移位寄存器里装满了对方发来的一个字节数据。
在同一个8个时钟周期内,主机通过MOSI发送了一个字节给从机,同时,从机也通过MISO发送了一个字节给主机(这个字节的内容可能是从机状态、之前操作的结果,或者仅仅是预设的填充值)。这就是为什么说是全双工,数据在两根线上是同时流动的。
当然,很多时候我们只关心其中一个方向的数据。比如主机发送一个命令,从机返回一个寄存器值。这时主机发送命令时,从机在MISO上发的是无效数据;
从机返回寄存器值时,主机在MOSI上发的可能是无效数据或者下一个命令的一部分。但物理上,数据流是同时进行的。
传输完一个字节或一组字节后,主机可以拉高SS线,结束本次通信。
下面聊下SPI的四种模式:CPOL与CPHA的组合
SPI之所以让很多人头疼,很大一部分原因就在于它的四种模式。这四种模式由两个参数决定:CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)。
•CPOL:时钟极性
CPOL决定了时钟线SCK在空闲状态(SS线为高电平,没有通信时)时的电平。
CPOL = 0:SCK空闲时为低电平。
CPOL = 1:SCK空闲时为高电平。
•CPHA:时钟相位
CPHA决定了数据是在时钟的哪个边沿被采样(读取)。
一个时钟周期,会有两个跳变沿,上升沿和下降沿。
CPHA = 0:数据在时钟的第一个跳边沿(上升沿)到来时被采样。
CPHA = 1:数据在时钟的第二个跳变沿(下降沿)到来时被采样。
将CPOL和CPHA进行组合,就产生了SPI的四种工作模式(Mode):
1.模式 0 (Mode 0): CPOL=0, CPHA=0
○SCK空闲时为低电平。
○数据在SCK的上升沿(从低到高)时被采样。
○SCK的下降沿(从高到低)用于发送方改变数据线上的数据。
举个例子:时钟线平时趴在地上(低),通信开始,它第一次站起来(上升沿),接收方赶紧看数据线!然后它又趴下(下降沿),发送方趁机把下一个数据放数据线上准备好。
2.模式 1 (Mode 1): CPOL=0, CPHA=1
○SCK空闲时为低电平。
○SCK的上升沿(从低到高,这是时钟的第一个跳变沿)用于发送方改变数据线上的数据。
○数据在SCK的下降沿(从高到低,这是时钟从活动状态回到空闲状态的第二个跳变沿)被采样。
举个例子:时钟线平时趴在地上(低),通信开始,它站起来(上升沿),发送方把数据放好。然后它又趴下(下降沿),接收方赶紧看数据线!
3.模式 2 (Mode 2): CPOL=1, CPHA=0
○SCK空闲时为高电平。
○数据在SCK的下降沿(从高到低,这是时钟从空闲状态进入活动状态的第一个跳变)被采样。
○SCK的上升沿(从低到高,这是时钟的第二个跳变)用于发送方改变数据线上的数据。
举个例子:时钟线平时站着(高),通信开始,它第一次趴下(下降沿),接收方赶紧看数据线!然后它又站起来(上升沿),发送方趁机把下一个数据放数据线上准备好。
4.模式 3 (Mode 3): CPOL=1, CPHA=1
•SCK空闲时为高电平。
•SCK的下降沿(从高到低,这是时钟的第一个跳变)用于发送方改变数据线上的数据。
•数据在SCK的上升沿(从低到高,这是时钟从活动状态回到空闲状态的第二个跳变)被采样。
举个例子:时钟线平时站着(高),通信开始,它趴下(下降沿),发送方把数据放好。然后它又站起来(上升沿),接收方赶紧看数据线!
理解上面这些图,面试再也不用慌啦,如果心理素质强的,直接现场边画边讲解效果更好。
为什么模式必须匹配?
因为SPI是同步通信,主从双方必须对数据的“节拍”达成一致。
主机的时钟波形、发送数据改变的时机、接收数据采样的时机,必须和从机期望的时钟波形、发送数据改变的时机、接收数据采样的时机完全对得上。
如果主机配置的是模式0(CPOL=0, CPHA=0),它会在时钟上升沿发出下一个脉冲并准备好数据,在下降沿改变数据线电平,并在上升沿采样数据。
如果从机配置的是模式3(CPOL=1, CPHA=1),它期望空闲时时钟是高电平(跟主机的低电平就不符),期望在时钟上升沿采样数据(跟主机的采样时机可能不符,取决于时钟波形)。
这种不匹配会导致:
•时钟信号的空闲电平就不对,从机可能根本不认为通信开始了。
•即使时钟勉强看起来像样,主机发送方在某个边沿改变数据电平,但从机接收方却在错误的边沿去采样,这时候数据线上的电平可能还在变化中,或者已经是下一个bit的电平了。结果自然是采样到错误的数据。
所以,当你的SPI通信出现问题时,第一件事就是去查从设备(传感器、屏幕、存储器等)的datasheet,找到它要求的SPI模式(CPOL和CPHA)。然后在你的单片机SPI配置寄存器里,把CPOL和CPHA位设置成对应的值。
对于常见的STM32、GD32、或其他厂家的单片机,SPI外设的控制寄存器里都会有专门的位来控制CPOL和CPHA。比如在某个单片机的SPI控制寄存器1 (SPI_CR1) 里,可能就有CPOL和CPHA这两个位。
比如要配置成模式0,你就把CPOL和CPHA都写0。
要配置成模式3,就把CPOL和CPHA都写1。
那对于一些用SPI的外围从机芯片呢?怎么看是用的SPI的哪种模式?
我们拿W25Q64这颗Flash芯片举例,我们在芯片数据手册上找到关于SPI的介绍。上面有写支持的模式,比如这颗芯片支持模式0(Mode 0)和模式3(Mode 3)。
就是这么简单,但却是确保通信正常的前提。
当然,SPI还有其他一些需要注意的配置,比如数据长度(8位还是16位)、数据位序(MSB First还是LSB First)、时钟分频等。但就模式而言,CPOL和CPHA是核心中的核心。
把SPI的这套逻辑摸透了,你会发现,其实它没那么玄乎,无非就是信号的配合和时序的同步。搞懂了模式的区别,datasheet上的时序图看起来也不会那么陌生和可怕了。
暂无评论