SPI通信协议

SPI通信

SPI协议和I2C协议的目的都是为了实现主控芯片和各种外挂芯片之间的数据交流

I2C可以在消耗最低硬件功能的情况下,实现最多的功能,在硬件上无论挂在多少个设备,都只需要挂载两根通信线,在软件上,数据双向通信、应答位,都可以实现,如果把通信协议比作一个人,I2C就属于精打细算、思维灵活类型的人,既要实现硬件上最少得通信线又要实现软件上最多的功能,但是由于I2C开漏外加上拉电阻的电路结构,使得通信线高电平的驱动能力比较弱,这就会导致,通信线由低电平变到高电平的时候,上升沿耗时比较长,这会限制I2C的最大通信速度,导致I2C的标准模式,只有100KHz的时钟频率,I2C的快速模式也只有400KHz,即使I2C协议之后又通过改进电路的方式设计出了高速模式,可以达到3.4MHz,但是高速模式目前普及程度不是很高,所以一般情况下,I2C的最高速度就是400KHz,相比较SPI还是慢了很多。

SPI传输更快,SPI协议并没有严格规定最大传输速度,最大传输速度取决于芯片厂商的设计需求,SPI设计比较简单粗暴实现的功能也没有I2C那么多,SPI的硬件开销比较大,通信线。的个数比较多,通信过程中还经常有资源浪费的现象,SPI不在乎硬件开销大大小只在乎任务有没有完成

  • SPI(Serial Peripheral Interface)中文:串行外设接口是由Motorola公司开发的一种通用数据总线
  • 四根通信线:SCK(Serial Clock串行时钟线)、MOSI(Master Output Slave Input主机输出从机输入)、MISO(Master Input Slave Output主机输入从机输出)、SS(Slave Select从机选择),SCK引脚是用来提供时钟信号的,数据位的输出和输入,都是在SCK的上升沿或下降沿进行,这样来确定数据位的收发时刻,同步时序,时钟快点慢点,中途暂停一会,都没有任何影响,类似于I2C的SCL两者作用相同。SS是从机选择线,有几个从机就开几条SS,给低电平就说明要找这个从机,给高电平就代表不这个从机通信了,
  • 同步,全双工,发送和接收各用一根线,发送用发送的线路,接收用接收的线路,两者互不影响,
  • 支持总线挂载多设备(一主多从),SPI没有应答机制,发送就发送接收就接收,至于从机是否存在SPI是不管的
SPI的硬件电路

所有SPI设备的SCK、MOSI、MISO分别连在一起

主机另外引出多条SS控制线,分别接到各从机的SS引脚

输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入

image-20230319214524373

左边是SPI主机,主导整个SPI总线,主机一般是像STM32这样的控制器来做,右下角的SPI从机1,2,3,是挂在在主机上的从设备,SPI主机实际引出了六根线,因为有3个从机,所以SS线需要三根,SPI所有通信线都是单端信号,它们的高低电平都是相对GND的电压差,单端信号所有的设备都需要共地,如果从机没有独立供电,主机还需要额外引出电源正极VCC,给从机供电,首先来看SCK时钟线,SCK完全由主机掌控,对于主机来说时钟线位输出,对于所有从机来说时钟线都为输入,这样主机的同步时钟就能送到各个从机了,主机的SS线都是输出,从机的SS线都是输入,SS线是低电平有效,主机想指定谁,就把对应的SS输出线置低电平,例如主机初始化后,所有的SS都输出高电平,这样就是谁也不指定,当主机需要和从机通信,例如从机1,主机就把SS1线输出低电平,这样从机1就知道主机在找它,这样的话主机在数据引脚进行的传输,就只有从机1会响应,其他从机的SS是高电平,所以它们都会保持沉默,当主机和从机1通信完成之后,就会把SS1置回高电平,这样从机1就知道主机结束了和从机1的通信。同一时间主机只能置一个SS为低电平,只能选中一个从机,如果主机同时选中多个从机,就会导致数据冲突。SPI选择从机的方式不需要像I2C一样进行寻址,对于输出配置推挽输出,推挽输出均有很强的驱动能力,这使得SPI引脚的下降沿,非常迅速,上升沿也非常迅速,不想I2C那样,下降沿非常迅速,但是上升沿,就比较缓慢了

image-20230319235307862

SPI信号变化的快,那自然它就能达到更高的传输速度,一般SPI信号都能轻松达到MHz的速度级别,I2C并不是不想使用更快的推挽输出,而是I2C要实现半双工,经常要切换输入输出,而且I2C又要实现多主机的时钟同步和总线仲裁,这些功能都不允许I2C使用推挽输出,要不然很容易发生电源短路,I2C选择更多的功能,自然就放弃更强的性能了,对于SPI,SPI不支持多主机,SPI还是全双工,SPI的输出引脚始终是输出,输入引脚始终是输入,基本不会出现冲突,所以SPI可以大胆地使用推挽输出,但是MISO引脚也可能发生冲突,在这个引脚上,主机一个是输入,但是三个从机全都是输出,如果三个从机都始终是推挽输出,势必会导致冲突,所以在SPI协议里有一条规定,当从机的SS引脚为高电平,也就是从机未被选中时,它的MISO引脚必须切换为高阻态,高阻态就相当于引脚断开,不输出任何电平,这样就可以防止,一条线有多个输出,而导致的电平冲突问题了,在SS为低电平时,MISO才允许变为推挽输出,这是SPI对这个可能得冲突做出的规定,但是这个切换过程都是在从机里,我们一般都是写主机的程序,所以主机的程序中并不需要关注这个问题。

移位示意图

image-20230320192336713

这个移位示意图是SPI硬件电路设计的核心,SPI的基本收发电路使用了一个移位的模型,左边是SPI主机,里面有一个8位的移位寄存器,右边是SPI的从机,里面也有一个8位的移位寄存器,移位寄存器有一个始终输入端,因为SPI一般都是高位先行的,所以每来一个始终,移位寄存器都会向左进行移位,移位寄存器中的时钟源是由主机提供的,叫做波特率发生器,它产生的时钟驱动主机的移位寄存器进行移位,同时这个时钟也通过SCK引脚进行输出,接到从机的移位寄存器里,主机移位寄存器左边移出去的数据,通过MOSI引脚,输入到从机移位寄存器的右边,从机移位寄存器左边移出去的数据,通过MISO引脚,输入到主机移位寄存器的右边,组成一个圈,规定波特率发生器时钟的上升沿所有移位寄存器向左移位一位,移出去的位放到引脚上,波特率发生器的下降沿引脚上的位,采样输入到移位寄存器的最低位,假设主机有个数据,10101010要发送给从机,同时从机有个数据01010101要发送到主机,那就可以驱动时钟,先产生一个上升沿,这时,所有的位,会往左移动一次,从最高位移出去的数据,就会放到通信线上,数据放到通信线上实际上是放到了输出数据寄存器,此时MOSI数据是1,所以MOSI的电平就是高电平,MISO的数据是0,所以MISO的电平就是低电平,这就是第一个时钟上升沿执行的结果,就是把主机和从机中,移位寄存器的最高位分别放到MOSI和MISO的通信线上,这就是数据的输出,之后时钟继续运行,上升沿之后,下一个边沿就是下降沿,在下降沿时主机和从机内,都会进行数据采样输入,也就是,MOSI的1,会采样输入到从机这里的最低位,MISO的0,会采样输入到主机这里的最低位,这就是第一个时钟结束后的现象,时钟继续运行,下一个上升沿,同样操作,移位输出,主机现在的最高位,也就是原始数据的最高位,输出到MOSI,从机现在的最高位,输出到MISO,变成主机的最低位。八个时钟后,原来主机的10101010,跑到从机里,原来从机的01010101跑到主机里了,这就实现了,主机和从机一个字节的数据交换,SPI的数据收发都是基于字节交换这个基本单元来实现的,当主机需要发送一个字节同时需要接受一个字节时,就可以执行一下字节交换的时序,这样主机要发送的数据跑到从机,主机要从从机接收的部分,跑到主机,这就完成发送同时接收的目的,如果只想发送不想接收,和原来一样,只是这次接收到的数据不看它就行了,只想接受不想发送就随便发一个数据只要能把从机的数据置换过来就行了,读取置换过来的数据,这里随便发过去的数据从机不会去看它,一般在接受的时候,统一发0x00或0xFF,去跟从机交换数据,这就是SPI的基本原理。

  • SPI通信的基础就是交换一个字节,有了交换一个字节,就可以实现发送一个字节、接收一个字节和发送同时接收一个字节,SPI在只执行发送或只执行接收的时候会存在一些资源浪费现象。

SPI时序

  • 起始条件:SS从高电平切换到低电平

image-20230403204519138

SS从高变到低,就是代表刚选中了某个从机了,这就是通信的开始

  • 终止条件:SS从低电平切换到高电平

image-20230403204556044

结束了从机的选中状态,结束了通信

模式1
  • 交换一个字节(模式1)
  • CPOL=0:空闲状态时,SCK为低电平
  • CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

image-20230403204912773

SS高电平期间,从机的MISO必须置回高阻态

因为CPHA=1,SCK第一个边沿移出数据SCK第一个边沿(上升沿),主机和从机同时移出数据,主机通过MOSI移出最高位,此时MOSI的电平就代表了主机要发送的数据B7,从机通过MISO移出最高位,此时MISO表示从机要发送的数据B7,然后时钟运行,产生下降沿,此时主机和从机同时移入数据,也就是进行数据采样,这里主机移出的B7,进入从机移位寄存器的最低位,从机移出的B7,进入主机移位寄存器的最低位,这样一个时钟脉冲产生完毕,一个数据位传输完毕,接下来就是同样的过程,上升沿,主机和从机同属输出当前移位寄存器的最高位,第二次的最高位,就是原始数据的B6,然后下降沿,主机和从机移入数据,B6传输完成,之后时钟继续运行,数据依次移出、移入、移出、移入,最后一个下降沿,数据B0传输完成。至此主机和从机就完成了一个字节的数据交换,如果主机只想交换一个字节,那这时就可以置SS为高电平,结束通信了,在SS的上升沿,MOSI还可以再变化一次,将MOSI置到一个默认的高电平或低电平也可以不管,SPI没有硬性规定MOSI的默认电平,MISO从机必须置回高阻态,此时如果主机的MISO位上拉输入的话,那MISO引脚的电平就是默认的高电平,如果主机MISO为浮空输入,那MISO引脚的电平不确定,这是交换一个字节就结束的流程。如果主机还想交换字节,就不需要把SS置回高电平了,直接重复一下交换一个字节的时序,这就是SPI传输数据的流程。

模式0

image-20230403210759317

模式0:的CPHA = 0表示SCK第一个边沿移入数据,第二个边沿移出数据,但是数据要先移出再移入,所以在模式0的配置下,SCK在第一个边沿开始之前,就要提前开始移出数据了,或者称为是在第0个边沿移出,第一个边沿移入。时序:首先,在SS下降沿开始通信,趁SCK还没有变化,SS下降沿时,就要立刻出发移位输出,所以这里MOSI和MISO的输出是对齐到SS的下降沿的,或者说把SS的下降沿,也当作时钟的一部分了,那SS下降沿触发了输出,SCL上升沿,就可以采样输入数据了,这样B7就传输完毕,之后SCK下降沿,移出B6,SCK上升沿,移入B6,继续下降沿移出,上升沿移入数据,最终在第8个上升沿时B0位移入完成,整个字节交换完成,之后SCK还有一个下降沿,如果主机只需要交换一个字节就结束,那在这个下降沿时,MOSI可以置回默认电平,或者不去管他,MISO也会变化一次,这一位实际上是下一个字节的B7.因为相位提前了,所以下一个字节的B7回露个头,如果不需要的话,SS上升沿之后,从机MISO置回高阻态。这是交换一个字节就结束,如果主机想交换多个字节的话,那就继续调用这个时序,在最后一个下降沿主机放下一个字节的B7,从机也放下一个字节的B7,SCK上升沿正好接着采样第二个字节的B7,这就SPI交换一个字节模式0,模式0和模式1的区别就在于模式0把数据变化的时机提前了,在时机应用中,模式0的应用是最多的的,所以重点掌握模式0。

  • CPHA是决定是第几个边沿采样,并不能单独决定是上升沿还是下降沿采样,在这4种模式里,模式0和模式3,都是SCK上升沿采样,模式1和模式2,都是SCK下降沿采样
模式2

image-20230403212404041

模式3

image-20230403212423899

时序波形图

SPI对字节流,不像I2C那样,I2C的规定一般是,有效数据流第一个字节是寄存器地址,之后依次是读写的数据使用的是读写寄存器的模型,而在SPI中,通常采用的是指令码加读写数据的模型。

这个过程是,SPI起始后,第一个交换发送给从机的数据一般叫做指令码,在从机中,对应的会定义一个指令集,当我们需要发送什么指令的时候,就可以在起始后第一个字节,发送指令集里面的数据这样就能指导从机完成相应的功能了,不同的指令可以有不同的数据个数,有的指令,只需要用一个字节的指令码就可以完成,比如W25Q64的写使能、写失能等指令,而有的指令,后面就需要再跟读写的数据比如W25Q64的写数据、读数据等,写数据指令后面就需要跟上,在哪里写,写什么,读数据指令后面就要跟上,在哪里读,读到的是什么,这就是指令码加读写数据的模型,在SPI从机的芯片手册中都会定义好指令集,什么指令对应什么功能,什么指令,后面得跟上什么数据。

发送指令
  • 向SS指定的设备,发送指令(0x06)

image-20230403213709284

  • 指令0x06的功能是由芯片厂商决定的,在W25Q64芯片里这个0x06代表的是写使能

在这里使用的是SPI模式0,在空闲状态时,SS为高电平,SCK为低电平,MOSI和MISO的默认电平没有严格规定,然后SS产生下降沿,时序开始,在这个下降沿时刻,MOSI和MISO就要开始交换数据了,这里是软件模拟的时序,所以MOSI的数据变化有延迟,没有紧贴SCK的下降沿,但是无所谓,时钟是主机控制的,只要在下一个SCK上升沿之前完成变化就行了,然后SCK上升沿,数据采样输入,在最后一位,下降沿,数据变化,MOSI变为0,上升沿,数据采样,从机接收数据0,SCK低电平是变化的时期,高电平是读取的时期,时序SCK最后一个上升沿结束。一个字节就交换完毕了。因为写使能是单独的指令,不需要跟随数据,SPI只需要交换一个字节就完成了,最后再SCK下降沿之后,SS置回高电平,结束通信。

主机用0x06换来了从机的0xFF,实际上从机并没有输出,0xFF是默认的高电平。整个时序的功能,就是发送指令,指令码是0x06,从机一比对事先定义好的指令集,发现0x06是写使能的指令,那从机就会控制硬件,进行写使能。这样一个指令从发送到执行就完成了,这就是发送单字节指令的时序

指定地址写
  • 向SS指定的设备,发送写指令(0x02), 随后在指定地址(Address[23:0])下,写入指定数据(Data)

image-20230403215026813

W25Q64芯片有8M字节的存储空间,一个字节的8为地址不够用,这里地址是24位的,分3个字节传输

时序:首先SS下降沿,开始时序,这里MOSI空闲时是高电平,所以在下降沿之后,SCK第一个时钟之前,可以看到,MOSI变换数据,由高电平变为低电平,然后SCK上升沿,数据采样输入,后面还是一样,下降沿变换数据,上升沿采样数据,8个时钟之后,一个字节变换完成,用0x02,换来了0xFF,其中发送的0x02是一条指令,代表这是一个写数据的时序,接收的0xFF,不需要看,写数据的时序后面必然需要跟着写的地址和数据了,所以在最后一个下降沿时刻,因为后续还需要继续交换字节,所以在这个下降沿要把下一个字节的最高位放到MOSI上,下一个字节的最高位仍然是0,所以这里数据没有变化,之后还是同样的流程,交换一个字节,第二个字节用0x12,换来了0xFF,根据W25Q64芯片的规定,写指令之后的字节,定义为地址高位,所以这个0x12,就代表发送地址的23-16位,之后继续交换一个字节发送的是0x34,就表示发送地址的15-8位,之后还是交换一个字节,发送的是0x56,这个表示发送地址的7~0位,通过3个字节的交换,24位的地址就发送完毕了,从机收到的24位地址是0x123456,3位地址结束后,就要发送写入指定地址的内容了,继续调用交换一个字节,发送数据,这里的波形是0x55,这就是表示,要在0x123456的地址下,写入0x55这个数据,如果只想写一个数据的话就可以SS置高电平,结束通信了,当然也可以继续发送数据,SPI也有和I2C类似的地址指针,每读取一个字节,地址指针自动加1,如果发送一个字节之后,不终止,继续发送的字节就会依次写入到后续的存储空间里,这样就可以实现从指定地址开始,写入多个字节了,这就是SPI写入的机制。没有用到接收MISO始终属于挂机的状态。

指定地址读
  • 向SS指定的设备,发送读指令(0x03), 随后在指定地址(Address[23:0])下,读取从机数据(Data)

image-20230403230756320

先发送0x03代表要读取数据,然后主机再依次交换3个字节,分别是0x12、0x34、0x56组合到一起就是0x123456,代表24位地址,制定地址后,交换数据,进行“抛砖引玉”,随便给从机一个数据,一般给0xFF,从机把0x123456地址下的数据通过MISO发送给主机,继续“抛砖引玉”,从机内部的地址指针会自动加1,从机就会把指定地址下一个位置的数据发过来,这样依次进行,就可以实现指定地址接收多个字节的目的了。最后数据传输完毕SS置回高电平,时序结束。由于MISO是硬件控制的波形所以它的数据变化,都可以紧贴时钟下降沿。

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计算机小混子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值