SPI通信结介绍
W25Q64是一个Flash存储器芯片,内部可以存储8M字节的数据,并且是掉电不丢失的。
四根通信线:SCK(Serial Clock)串行时钟线、MOSI(Master Output Slave Input)主机输出从机输入、MISO(Master Input Slave Output)主机输入从机输出、SS(Slave Select)从机选择。
SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO,片选线为SS,它们的作用介绍如下:
- (1) SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32的SPI时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
- (2) MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
- (3) MISO (Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
- (4) SS ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS表示。当有多个SPI从设备与SPI主机相连时,设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同只使用这3条总线; 而每个从设备都有独立的这一条NSS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。
I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI协议中没有设备地址,它使用NSS信号线来寻址,当主机要选择从设备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以NSS线置低电平为开始信号,以NSS线被拉高作为结束信号。
硬件电路
所有SPI设备的SCK、MOSI、MISO分别连在一起:
SCK时钟线,对于主机来说,时钟线为输出,对于所有从机来说,时钟线为输入。
MOSI主机输出从机输入,主机对应MO,输出,从机对应SI,输入。数据传送方向:主机通过MOSI输出,所有从机通过MOSI输入。
MIOS主机输入从机输出,主机对应MI,输入,从机对应SO。数据传送方向:三个从机对通过MIOS输入,主机通过MIOSI输出。
主机另外引出多条SS控制线,分别接到各从机的SS引脚
主机的SS线是输出,从机的SS线是输入,SS线是低电平有效,主机想指定谁,就把对应的SS线置低电平就行。如主机初始化化后,SS全部置高电平,这就是谁也不指定。
当主机需要和从机进行通信,主机就把SS1线置低电平,进行数据传输时,只有从机1响应,其余SS线高电平,保持沉默。
当主机和从机1通信完成后,就会把SS置回高电平,这样主机就结束和从机1的通信。
在同一时间,主机只能置一个SS为低电平,只能选择一个从机,否则,如果主机同时选中多个从机,就会导致数据冲突,这就是SPI选择从机的方式。
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入:
SPI有一个冲突点的:主机MISO引脚是一个输入,而从机MISO引脚三个从机都是输出,如三个从机都是推挽输出,会导致冲突。所以SPI协议规定,当从机的SS引脚为高电平时,也就是从机未被选中,它的MISO引脚,必须是高阻态,高阻态相当于引脚断卡,不输出任何电平。在SS低电平时,MISO才允许变为推挽输出。
移位示意图
总结;SPI通信是交换一个字节,有了交换一个字节,就可以发送一个字节,接收一个字节和发送同时接收一个字节。
这里移位寄存器有一个时钟输入端,因SPI一般是高位先行,所以每来一个时钟,移位寄存器都会向左移位,从机同理。
移位寄存器的时钟源由主机提供的,叫做波特率发生器,它产生的时钟,驱动主机的移位寄存器进行移位,同时这个引脚也通过SCK引脚进行输出,接到从机的移位寄存器里。
工作:
首先,规定波特率发生器的时钟上升沿,所以移位寄存器向左移动一位,移出去的一位放到引脚上,MOSI是1,MISO是0 ,之后时钟继续运行,时钟下降沿时,MOSI的1,会采样输入到从机的最低位,MISO的0,会采样输入到主机的最低位,这是第一个时钟结束后现象。
8个时钟后,主机的10101010跑到从机里面了,从机的01010101跑到主句里面了,实现了主机和从机的一个字节的数据交接。
如只想发送,不想接收呢:扔然调用交换字节的时序,发送,同时接收,只是接收到的数据,我们不看它就行了。
SPI时序基本单元
模式1
CPO时钟极性,CPHA时钟相位。
在SS未被选中时,SCK默认电平为低电平。
第一个SS从机选择,在通信开始前,SS位为高电平,在通信过程中,SS始终保持低电平,通信结束,SS恢复高电平。
最下面MISO主机输入从机输出,SS高电平时,MISO用一条中间的线,表示高组态,SS下降沿之后,从机MISO被允许开启输出,SS上升沿之后,从机的MISO必须置回高组态。
移位传输:因为CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据。
SCK上上升沿,主机和从机同时移出数据,主机通过MOSI移出最高位,此时MOSI的电平就表示了主机要发送的数据B7,从机通过MISO移出最高位,此时MISO表示从机要发送的数据B7,然后时钟运行,产生下降沿,此时从机和主机同时移入数据,也就是进行数据采样,这里主机移出的B7,进入从机移位寄存器的最低位,从机移出的B7,进入从主移位寄存器的最低位,这样一个脉冲产生完毕,一个字节传输完毕。
如主机只想交换一个字节,可以置SS为高电平,通信结束,在SS上升沿,MOSI还可以再变化一次,将MOSI置到一个默认的高电平或低电平,也可不管,因为SPI没有硬性规定MOSI的默认电平。
然后,MISO从机必须置回高阻态,此时如果主机MISO,为上拉输入,那MISO引脚电平默认是高电平;如果主机MISO,为浮空输入,那MISO引脚电不确定。这是交换一个字节结束的流程。
如果主机还想继续交换字节,此时,主机就不必把SS置回高电平,直接重复一下,从这里到这里,这样可以交换多个字节。
模式0
模式0和模式1时序上区别:就是模式0数据移入移出,相位提前了。模式0应用更多。
SCK第一个边沿移入数据,第二个边沿移出数据:
在模式0下,SCK第一个边沿之前,就要提前开始移出数据,或称作第0个边沿移出,第一个边沿移入。
首先,SS下降沿开始通信,SCK还没有变化时,SS下降沿,就要立刻触发移位输出。这里MOSI和MISO的输出是对齐到SS的下降沿,那SS下降沿触位输出,SCK上升沿,就可以采样输入数据了,这样B7传输完毕。最终在第8个上升沿时,B0移入完成。整个字节交换完成。
之后,SCK还有一个下降沿,如主机只想交换一个字节结束,那在这个下降沿时,MOSII置到一个默认的高电平或低电平,也可不管;MISO也会变化一次,这一位实际上是下一字节的B7。因为相位提前了,如不需要,SS上升沿后,从机MISO置回高阻态。
模式3
模式0和模式2上区别:就是模式0的CPOL=0,模式2的CPOL=1。两者的波形,就是SCK的极性取反。
模式1和模式3上区别:就是模式1的CPOL=0,模式3的CPOL=1。两者的波形,也是SCK的极性取反。
模式0和模式3,都是SCK上升沿采样。
模式1和模式2,都是SCK下降沿采样。
SPI时序波形
发送指令
每个芯片对于SPI时序,字节流功能的定义不一样,这里采用的是W25Q64。
SPI对字节流功能的规定不像I2C那样,I2C的规定一般是,有效数据流第一个字节是寄存器地址,之后依次是读写的数据,使用的是读写寄存器器的模型。
而在SPI中,通常采用的是指令码加读写数据的模型。这个过程就是SPI起始后,第一个交换发送给从机的数据一般叫做指令码,在从机中,对应的会定义一个指令集,当我们需要发送什么指令时,就可以在起始后第一个字节发送指令集里面的数据,这样就能指导从机完成相应的功能了,不同的指令可以有不同的数据个数,有的指令只需要一个字节的指令码就可以完成。
发送指令,向SS指定的设备,发送指令(0x06):
指令0x06到是,可以由芯片厂商自己规定,在W25Q64 型面里,这个0x06 代表的是写使能。
在这里我们使用的是SPI模式0,在空闲状态是SS为高电平,SCK为低电平,MOSI和MISO的默认电平没有严格规定,然后SS产生下降沿时序开始,在这个下降沿时刻,MOSI和MISO就要开始变换数据了,MOSI由于指令码最高位仍然是0,所以这里保持低电平不变,MISO从机现在没有数据发给主机,引脚电平没有变化,实际上W25Q64不需要回弹数据时,手册里规定的是MISO仍然是高阻态,从机并没有开启输出,不过这也没问题,反正这个数据我们也不要看,那这里因STM32的MISO是上拉输入,所以这里MISO呈现高电平,之后SCK第一个上升沿进行数据采样,我这里画了一条绿线,从机采样输入得到零,主机采样输入得到一,之后继续第二个时钟,主机数据仍然是零,所以波形仍然没有变化,然后这样一位一位的发送接收发送接收。
到这一位数据才开始变化,主机要发送数据一,下降沿数据移出,主机将一移出到MOSI,MOSI变为高电平,这里因为是软件模拟的时序,所以MOSI的数据变化有些延迟,没有紧贴SCK的下降沿。
不过这也没关系,时钟是主机控制的,我们只要在下一个SCK上升沿之前完成变化就行了,然后SCK上升沿数据采样输入,在最后一位呢下降沿数据变化MOSI变为0,上升沿数据采样,从机接收数据0,SCK低电平是变化的时期,高电平是读取的时期,那时序SCK最后一个上升沿结束,一个字节就交换完毕了,因为写使能是单独的指令,不需要跟随数据,SPI只需要交换一个字节就完事了,所以最后在SCK下降沿之后,SS置回高电平结束通信。
总结一下就是,主机用0x06换来了从机的0xff,但实际上从机并没有输出,这个0xf f是默认的高电平,不过这个0xf f没有意义,我们不用管,那整个时序的功能就是发送指令,指令码是0x06 ,从机一比对事先定义好的指令集,发现0x06是写使能的指令,那从机就会控制硬件进行写使能,这样一个指令从发送到执行就完成了,就是发送单字节指令的时序。
指定地址写
W25Q64 芯片有8M字节的存储空间,一个字节的八位地址肯定不够,所以这里地址是24位的分三个字节传输。
首先ss下降沿开始时序,mosi空闲时是高电平,所以在下降沿之后,sck第一个时钟之前可以看到mosi变换数据由高电平变为低电平,然后sck上升沿数据采样输入,后面还是一样下降沿变换数据上升沿采样数据,八个时钟之后一个字节交换完成,我们用0x02换来了0xff,其中发送的0x02是一条指令,代表这是一个写数据的时序,接收到0x ff不需要看。
那既然是写数据的时序,后面必然还要跟着写的地址和数据,所以在最后一个下降沿时刻,因为我们后续还需要继续交换字节,所以在这个下降沿,我们要把下一个字节的最高位放到MOSI上,当然下一个字节的最高位仍然是0,所以这里数据没有变化,最后还是同样的流程交换一个字节,第二个字节我们用0x12换来了0xff,根据w25q64 芯片的规定,写指令之后的字节定义为地址高位,所以这个0x12 就表示发送地址的23~16位。
继续看一下交换一个字节,发送的是0x34 这个就表示发送地址的15~8位,最后还是交换一个字节发送的是0x56 ,这个表示发送地址的7~0位,通过三个字节的交换,24位的地址就发送完毕了,从机收到的24位地址是0x123456 。
那三位地址结束后,就要发送写入指定地址的内容了,我们继续调用交换一个字节,发送数据这里的波形是0x55 ,这个表示我要在0x123456 地址下,写入0x55 这个数据,最后如果只想写出一个数据的话,就可以SS置高电平结束通信了,当然这里也可以继续发送数据,spi里也会有和I2C一样的地址指针,每读写一个字节地址指针自动加一,如果发送一个字节之后不终止继续发送的字节就会依次写入到后续的存储空间里,这样就可以实现从指定地址开始写入多个字节了,这就是spi写入的时序。
指定地址读
我们看一下时序,起始之后第一个字节主机发送指令0x03 ,表示我要读取数据了,最后还是一样,主机在依次交换三个字节,分别是0x12 0x34 0x56 ,组合到一起就是0x123456代表24位地址。
0X03
0x12
0x34
0x56
0x55
最后这个地方就是关键点,因为我们是读取数据,指定地址之后,显然我们就要开始接收数据,所以这里三个字节的地址交换完之后,我们要把从机的数据搞过来,我们还是交换一个数据来个抛砖引玉,我们随便给从机一个数据,一般给ff就行了,从机就会乖乖的把0x123456地址下的数据通过MISO发给主机,可以看到这样的波形就表示指定地址下的数据是0x55 ,这样主机就实现了指定地址读一个字节的目的。然后如果我们继续抛砖引玉,那么从机内部的地址指针自动加一,从机就会继续把指定地址下一个位置的数据发过来,这样依次进行,就可以实现指定地址接收多个字节的目的了,最后数据传输完毕,SS置回高电平,时序结束。
当然时序这里也会有些细节,比如由于MISO是硬件控制的波形,所以它的数据变化都可以紧贴时钟的下降沿,另外我们可以看到miso数据的最高位实际上是在上一个字节,最后一个下降沿提前发生的,因为这是SPI模式0,所以数据变化都要提前半个周期。