本节课我们将继续学习下一个通信协议,SPI。SPI通信和我们刚刚学习过的I2C通信差不多。两个协议的设计目的都一样都是实现主控芯片和各种外挂芯片之间的数据交流,有了数据交流的能力,我们的主控芯片就可以挂载并操纵各式各样的外部芯片,来实现一个功能更加强大的控制系统。
SPI(Serial Peripheral Interface),翻译而来就是串行外设接口。是由Motorola公司开发的一种通用数据总线。我们知道,I2C可以在消耗最低硬件资源的情况下,实现最多的功能,但其存在的缺点就是由于I2C开漏外加上拉电阻的电路结构,使得通信线高电平的驱动能力比较弱,这就会导致通信线由低电平转变到高电平的时候,上升沿耗时会比较长,限制了I2C的最大通信速度。所以I2C的标准模式只有100KHz的时钟频率,I2C的快速模式也只有400KHz的时钟频率。这个速度,相比于SPI而言还是慢很多的。了解完I2C的优势和缺点,我们就来看一下SPI,在学习之前,简单概括一下SPI相对于I2C的优缺点。
首先,SPI传输更快,SPI协议并没有严格规定最大传输速度,这个最大传输速度取决于芯片厂商的设计需求;其次,SPI的设计比较简单粗暴,实现的功能没有I2C那么多,所以学习起来,SPI还是比I2C简单很多的;最后,SPI的硬件开销比较大,通信线的个数比较多,并且通信过程种,经常会有资源浪费的现象。
SPI有四根通信线:SCK(Serial Clock),意思是串行时钟线;MOSI(Master Output Slave Input),意思是主机输出从机输入;MISO(Master Input Slave Output),意思是主机输入从机输出;SS(Slave Select),意思是从机选择。
SPI的基本特征是同步全双工,首先既然是同步时序,肯定就得有时钟线了,所以这个SCK引脚就是用来提供时钟信号的。数据位的输出和输入都是在SCK的上升沿或下降沿进行的,这样,数据位的收发时刻就可以明确的确定,并且同步时序,时钟快点慢点或者中途暂停一会都是没问题的,这就是同步时序的好处,那对照I2C总线,这个SCK就相当于I2C的SCL,两者作用相同。之后,SPI是全双工的协议,就是数据发送和数据接收单独各占一条线,发送用发送的线路,接收用接收的线路,两者互不影响,所以这里MOSI和MISO就是分别用于主机发送和接收的两条线路。
MOSI线是主机输出从机输入,如果是主机接在这条线上,那就是MO,主机输出;如果是从机接在这条线上,那就是SI,从机输入。意思就是一条通信线,如果主机接在上面配置为输出,那从机肯定得配置为输入,才能接收主机的数据,主机和从机不能同时配置为输出或输入,不然就没办法通信了。那同理,下面这条MISO,就是主机从从机接收数据的线路,这就是全双工通信的两根通信线,这两根通信线加在一起,就相当于I2C总线的SDA。
SPI支持总线挂载多设备,使用的是一主多从的模型,SPI仅支持一主多从不支持多主机,这一点,SPI从功能上,没有I2C强大。那I2C实现一主多从的方式是在起始条件之后,主机必须先发送一个字节进行寻址,用来指定我要跟哪个从机进行通信,所以I2C这里,要涉及分配地址和寻址的问题,但是SPI表示,你这太麻烦了,它直接大手一挥,再开辟一条通信线,专门用来指定我要跟哪个从机进行通信,所以这条专门用来指定从机的通信线就是这里的SS,从机选择线。并且这个SS可能不止一条,SPI的主机表示,我有几个从机,我就开几条SS,所有从机一人一根,都别抢,我需要找你的时候,我就控制接到你那一根的SS线,给你低电平,就说明我要找你了,给你高电平,就说明我不跟你玩了。这就是SPI实现一主多从,指定从机的方式。
SPI没有应答机制的设计,发送数据就发送,接收数据就接收,至于对面是不是存在,SPI是不管的。
接下来我们来看一下SPI的硬件和软件规定,首先是硬件电路,下图就是SPI一个典型的应用电路。我们看一下,左边这里,是SPI主机,主导整个SPI总线,主机一般都是控制器来做,比如STM32,下面这里SPI从机1、2、3,就是挂载在主机上的从设备了。因为有三个从机,所以SS线需要3根,再加SCK、MOSI、MISO,就是6根通信线。当然SPI的所有通信线都是单端信号,它们的高低电平都是相对GND的电压差,所以单端信号,所有的设备还需要共地,这里GND的线没画出来,但是是必须要接的。然后如果从机没有独立供电的话,主机还需要再额外引出电源正极VCC给从机供电。所有SPI设备的SCK、MOSI、MISO分别连在一起;主机另外引出多条SS控制线,分别接到各从机的SS引脚;输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入,推挽输出有很强的驱动能力,这将使得SPI引脚信号的下降沿非常迅速,上升沿也非常迅速,不像I2C那样下降沿非常迅速但是上升沿比较缓慢了。SPI信号变化的快,那自然它就能达到更高的传输速度。
不过SPI还是有一个冲突点的,就是图中的MISO,在这个引脚上,主机一个是输入,但是三个从机全都是输出,如果三个从机都始终是推挽输出,势必会导致冲突。所以在SPI协议里,有一条规定,就是当从机的SS引脚为高电平,也就是从机未被选中时,它的MISO引脚必须切换为高阻态,高阻态就相当于引脚断开,不输出任何电平,这样就可以防止一条线有多个输出,而导致的电平冲突的问题了,在SS为低电平时,MISO才允许变为推挽输出。
接下来我们来看一下下面的移位示意图,这个移位示意图是SPI硬件电路设计的核心。SPI的基本收发电路,就是使用了这样一个移位的模型。左边是SPI主机,里面有一个8位的移位寄存器;右边是SPI从机,里面也有一个8位的移位寄存器。这里移位寄存器有一个时钟输入端,因为SPI一般都是高位先行的,所以每来一个时钟,移位寄存器都会向左进行移位,从机中的移位寄存器也是同理。然后移位寄存器的时钟源是由主机提供的,这里叫做波特率发生器,它产生的时钟驱动主机的移位寄存器进行移位,同时这个时钟也通过SCK引脚进行输出,接到从机的移位寄存器里。之后,上面移位寄存器的接法是:主机移位寄存器左边移出去的数据通过MOSI引脚输入到从机移位寄存器的右边;从机移位寄存器左边移出去的数据通过MISO引脚输入到主机移位寄存器的右边,形成一个圈。接下来演示一下这个电路如何工作,首先我们规定,波特率发生器时钟的上升沿,所有移位寄存器向左移动一位,移出去的位放到引脚上,波特率发生器时钟的下降沿,引脚上的位,采样输入到移位寄存器的最低位。接下来,假设主机有个数据10101010要发送到从机,同时,从机有个数据01010101要发送到主机,那我们就可以驱动时钟,先产生一个上升沿,这时,所有的位都会往左移动一次,从最高位移出去的二进制数就会放到通信线上,实际上是放到了输出数据寄存器,那么此时MOSI数据是1,所以MOSI的电平就是高电平;MISO的数据是0,所以MISO的电平就是低电平,这就是第一个时钟上升沿执行的结果,就是把主机和从机中,移位寄存器的最高位,分别放到MOSI和MISO的通信线上,这就是数据的输出。之后,时钟继续运行,上升沿之后,下一个边沿就是下降沿,在下降沿时,主机和从机内,都会进行数据采样输入,也就是MOSI的1,会采样输入到从机的最低位,MISO的0,会采样输入到主机的最低位,这就是第一个时钟结束后的现象。那时钟继续运行,下一个上升沿,同样的操作,移位输出,随后下降沿,数据采样输入,如此循环。8个时钟后,原来主机里的10101010跑到从机里了,原来从机里的01010101,跑到主机里了,这就实现了主机和从机一个字节的数据交换,实际上SPI的运行过程就是这样,SPI的数据收发,都是基于字节交换,这个基本单元来进行的。当主机需要发送一个字节并且同时需要接收一个字节时,就可以执行一下字节交换的时序,这样主机要发送的数据,跑到从机,主机要从从机接收的数据跑到主机,这就完成了发送同时接收的目的。
那你肯定会问,如果我主机只想发送不想接收怎么办呢?其实很简单,我们仍然调用交换字节的时序,只是主机接收到的数据我们不看它就行了;如果我主机只想接收不想发送怎么办呢?同理,我们还是调用交换字节的时序,只是我们会随便发送一个数据,只要能把从机的数据置换过来就行了,我们读取置换过来的数据,不就是接收了吗。这里我们随便发过去的数据,从机也不会去看它当然这个随便的数据我们不会真的随便发,一般在接收的时候,我们会统一发送0x00或0xFF去跟从机换数据。以上就是SPI的基本原理。
总结一下就是,SPI通信的基础是交换一个字节,有了交换一个字节,就可以实现发送一个字节、接收一个字节和发送同时接收一个字节,这三种功能。可以看出,SPI在只执行发送或者只执行接收的时候会存在一些资源浪费的现象,不过全双工的通信,本来就会有浪费的情况发生。
接下来我们来看一下SPI的时序,首先是SPI的起始和终止。其中,起始条件是SS从高电平切换到低电平,也就是左边的图。SS是低电平有效的,那SS从高变到低,是不是就代表刚选中了某个从机,这就是通信的开始。然后终止条件是SS从低电平切换到高电平,也就是右边的图。SS从低电平变到高电平,就是结束了从机的选中状态,就是通信的结束。在从机的整个选中状态中,SS要始终保持位低电平。
接下来就是数据传输的基本单元了,这个基本单元,就是建立在我们刚才说的移位模型上的,并且这个基本单元什么时候开始移位,是上升沿移位还是下降沿移位,SPI并没有限定死,给了我们可以配置的选择,这样的话,SPI就可以兼容更多的芯片。那在这里,SPI有两个可以配置的位,分别叫做CPOL(Clock Polarity)时钟极性和CPHA(Clock Phase)时钟相位,每一位可以配置为1或0,总共组合起来就有模式0、1、2、3这四种模式。
先看模式1,这个时序的基本功能是交换一个字节,也就是我们刚开始介绍的那个循环。这里CPOL=0,表示空闲状态时,SCK为低电平,下图可以看到,在SS未被选中时,SCK默认是低电平的,然后CPHA=1,表示SCK第一个边沿移出数据,第二个边沿移入数据。图中MISO中间的线表示高阻态,SS下降沿之后,从机的MISO被允许开启输出,SS上升沿之后,从机的MISO必须置回高阻态。由于CPHA=1,SCK第一个边沿也就是上升沿时,主机和从机同时移出数据,主机通过MOSI移出最高位,此时MOSI的电平就表示了主机要发送数据的B7;从机通过MISO移出最高位,此时MISO表示从机要发松数据的B7,然后时钟运行,产生下降沿,此时主机和从机同时移入数据,也就是进行数据采样。这里主机移出的B7,进入从机移位寄存器的最低位,从机移出的B7,进入主机移位寄存器的最低位,这样一个时钟脉冲产生完毕,一个数据为传输完毕。接下来就是同样的过程,上升沿,主机和从机同时输出当前移位寄存器的最高位,第二次最高位,就是原始数据的B6,然后下降沿,主机和从机移入数据,B6传输完成,之后是时钟继续运行,数据依次移出、移入、移出、移入,最后一个下降沿,数据B0传输完成。至此,主机和从机就完成了一个字节的数据交换。如果主机只想交换一个字节,那这时就可以置SS为高电平,结束通信了。在SS的上升沿,MOSI还可以再变化一次,将MOSI置到一个默认的高电平或低电平,MISO从机必须置回高阻态。那如果主机还想继续交换字节呢?在此时,主机就不必把SS置回高电平了,直接重复一下,从SCK的第一个上升沿到最后一个下降沿的时序,这样就可以交换多个字节了。
接下来我们继续看一下模式0,模式0和模式1的区别就是模式0的CPHA=0,模式1的CPHA=1。两者在时序上的区别就是模式0的数据移出移入的时机会提前半个时钟,也就是相位提前了。模式0,CPHA=0,表示SCK第一个边沿移入数据,第二个边沿移出数据,模式0在SCK第一个边沿就要移入数据,但是数据总得先移出,才能移入。所以在模式0的配置下,SCK第一个边沿之前,就要提前开始移出数据了。或者把它称作是在第0个边沿移出,第一个边沿移入。
看一下时序,首先,SS下降沿开始通信,现在SCK还没有变化,但是SCK一旦开始变化就要移入数据了,所以趁SCK还没有变化,SS下降沿时,就要立刻触发移位输出,所以这里MOSI和MISO的输出是对齐到SS的下降沿的。或者说,这里把SS的下降沿也当作时钟的一部分了。
了解完模式0和模式1,下面的模式2和模式3就非常简单了其中模式0和模式2的区别,就是模式0的CPOL=0,模式2的CPOL=1,两者的波形就是SCK的极性取反一下,剩下流程上的东西完全一致
模式1和模式3的区别,也是模式1的CPOL=0,模式3的CPOL=1,两者的波形,也是SCK的极性取反一下,其他地方没有变化。这就是这四种模式。
最后提醒一下,这个CPHA表示的是时钟相位,决定是第一个时钟采样移入还是第二个时钟采样移入,并不是规定上升沿采样还是下降沿采样的。当然,在CPOL确定的情况下,CPHA确实会改变采样时刻的上升沿和下降沿。比如模式0的时候,是SCK上升沿采样移入;模式1的时候,是SCK下降沿采样移入。这个了解一下,CPHA决定是第几个边沿采样,并不能单独决定是上升沿还是下降沿。然后在这4种模式里,模式0和模式3,都是SCK上升沿采样;模式1和模式2,都是SCK下降沿采样。这就是时序基本单元,我们就讲完了。
最后就是看几个完整的SPI时序波形了,以W25Q64的时序为例进行时序波形讲解。SPI对字节流功能的规定,不像I2C那样,I2C的规定一般是有效数据流第一个字节是寄存器地址,之后依次是读写的数据,使用的是读写寄存器的模型;而在SPI中,通常采用的是指令码加读写数据的模型,这个过程就是SPI起始后,第一个交换发送给从机的数据一般叫做指令码,在从机中,对应的会定义一个指令集,当我们需要发送什么指令时,就可以在起始后第一个字节发送指令集里面的数据,这样就能指导从机完成相应的功能了。不同的指令可以有不同的数据个数,有的指令,只需要一个字节的指令码就可以完成,比如W25Q64的写使能、写失能等指令,而有的指令,后面就需要再跟读写的数据,比如W25Q64的写数据、读数据等。写数据,指令后面就得跟上我要在哪里写,我要写什么;读数据,指令后面就得跟上我要在哪里读,我读到的是什么。这就是指令码加读写数据的模型,在SPI从机的芯片手册里,都会定义好指令集,什么指令对应什么功能,什么指令后面得跟上什么数据。
首先是SPI发送指令的波形,这个指令的功能是向SS指定的设备,发送指令(0x06),在W25Q64芯片中,0x06代表的是写使能。在这里,我们使用的是SPI模式0。在空闲状态时,SS为高电平,SCK为低电平,MOSI和MISO的默认电平没有严格规定。然后SS产生下降沿,时序开始,在SS的下降沿时刻,MOSI和MISO就要开始变换数据了。MOSI由于指令码最高位仍然是0,所以这里保持低电平不变;MISO,从机现在没有数据发送给主机,引脚电平没有变换。实际上W25Q64不需要回传数据时手册里规定的是MISO仍然是高阻态,从机并没有开启输出,不过这也没问题,反正这个数据我们也不要看。这里因为STM32的MISO是上拉输入,所以这里MISO呈现高电平。之后SCK第一个上升沿进行数据采样,从机采样输入得到0,主机采样输入,得到1。之后继续第二个时钟,主机数据仍然是0,所以波形仍然没有变化。然后这样一位一位地发送、接收、发送、接收,到第6位数据才开始变化,主机要发送数据1,下降沿数据移出,主机将1移出到MOSI,MOSI变为高电平,然后SCK上升沿,数据采样输入。在最后一位下降沿,数据变化,MOSI变为0,上升沿,数据采样,从机接收数据0。SCK低电平是变化的时期,高电平是读取的时期。时序SCK最后一个上升沿结束,一个字节就交换完毕了。因为写使能是单独的指令,不需要跟随数据,SPI只需要交换一个字节就完事了。所以最后,在SCK下降沿之后,SS置回高电平,结束通信。总结一下就是主机用0x06换来了从机的0xFF,当然实际上从机并没有输出,整个时序的功能就是发送指令,指令码是0x06,从机一比对事先定义好的指令集,发现0x06是写使能的指令,那从机就会控制硬件,进行写使能,这样一个指令从发送到执行就完成了,这就是发送单字节指令的时序。
之后看下一个,我们再看一条指令,下面这条指令是指定地址写,功能是向SS指定的设备,先发送写指令,写指令在指令集中规定是0x02,随后在指定地址下,写入指定数据。因为W25Q64芯片有8M字节的存储空间,一个字节的8位地址肯定不够,所以这里地址是24位的,分3个字节传输。我们来看一下时序,首先SS下降沿,开始时序,这里,MOSI空闲时是高电平,所以在下降沿之后,SCK第一个时钟之前,可以看到MOSI变换数据,由高电平变为低电平,然后SCK上升沿数据采样输入。后面还是一样,下降沿变换数据,上升沿采样数据。8个时钟之后,一个字节交换完成,我们用0x02换来了0xFF,其中发送的0x02是一条指令,代表这是一个写数据的时序。既然是写数据的时序,后面必然还要跟着写的地址和数据了,所以在最后一个下降沿时刻,因为我们后续还要继续交换字节,所以在数据交换完成后的下一个SCK下降沿,我们要把下一个字节的最高位放到MOSI上,当然下一个字节的最高位仍然是0,所以这里数据没有变化,之后还是同样的流程,交换一个字节,第二个字节,我们用0x12,换来了0xFF,根据W25Q64芯片的规定,写指令之后的字节,定义为地址高位,所以这个0x12就表示发送地址的23-16位,之后发送的是0x34,这个就表示发送地址的15-8位,之后还是交换一个字节,发送的是0x56,这个表示发送地址的7-0位,通过3个字节的交换,24位的地址就发送完毕了。从机收到的24位地址是0x123456。那三位地址结束后,就要发送写入指定地址的内容了,我们继续调用交换一个字节,发送数据,这里的波形是0x55,这就表示我要在0x123456地址下,写入0x55这个数据。最后,如果只想写入一个数据的话,就可以SS置高电平,结束通信了。当然这里也可以继续发送数据,SPI里也会有和I2C一样的地址指针,每读写一个字节,地址指针自动加1。如果发送一个字节之后不终止,继续发送的字节就会依次写入到后续的存储空间里,这样就可以实现从指定地址开始,写入多个字节了,这就是SPI写入的时序。由于SPI没有应答机制,所以交换一个字节后,就立刻交换下一个字节就行了。由于整个流程,我们只需要发送的功能,并没有接收的需求,所以MISO这条接收的线路就始终处于挂机的状态,我们并没有用到。
接着下一个时序,下面这幅图是指定地址读的时序,功能是向SS指定的设备,先发送读指令,这里芯片定义0x03为读指令,随后在指定地址下读取从机数据。我们看一下时序,起始之后第一个字节,主机发送指令0x03,代表我要读数据了,之后还是一样,主机再依次交换三个字节,分别是0x12、0x34、0x56,组合到一起就是0x123456,代表24位地址。最后这个地方就是关键点了,因为我们是读取数据,指定地址之后,显然我们要开始接收数据,所以这里3个字节地址交换完后,我们要把从机的数据搞过来。我们随便给主机一个数据,当然一般给FF就行了,从机就会把0x123456地址下的数据通过MISO发给主机,可以看到,这样的波形就表示指定地址下的数据是0x55,这样主机就实现了指定地址读一个字节的目的,如果继续进行数据交换,那么从机内部的地址指针自动加1,从机就会继续把指定地址下一个位置的数据发过来,这样依次进行,就可以实现指定地址接收多个字节的目的了。然后最后,数据传输完毕,SS置回高电平,时序结束。