1.SPI通讯协议
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线
1.1 SPI引脚与工作过程
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。它们分别是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。
(1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
(2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
(3)SCLK – Serial Clock,时钟信号,由主设备产生;
(4)CS – Chip Select,从设备使能信号,由主设备控制。
其他制造商可能会遵循其他命名规则,但是最终他们指的相同的含义:
- MISO也可以是
SIMO
,DOUT
,DO
,SDO
或SO
(在主机端); - MOSI也可以是
SOMI
,DIN
,DI
,SDI
或SI
(在主机端); - NSS也可以是
CE
,CS
或SSEL
; - SCLK也可以是
SCK
;
主机在MOSI
线上发送一位数据,从机读取它,而从机在MISO
线上发送一位数据,主机读取它。
数据的采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),具体要看对SPI的配置。
SPI协议的整个工作过程如下:
当设备片选信号拉低之后,输入到从器件的CLK开始有效,全双工的传输过程开始。主器件在信号线MOSI上输入数据并被从器件读取,从器件输出数据到MISO信号线并被主器件读取。当片选信号拉高之后,从器件不再被选中,结束整个传输过程。
在传输的时候,不管是输入输出,通常都是以一个byte的最高有效位(MSB)开始传输:
比如主器件输入bit7->6->5->4->3->2->1>0,从器件接受每个bit之后再向右shift,最终一个byte传完之后,从器件看到的顺序是bit[0:7]。从器件的输出方式类似。
1.2 SPI内部工作原理
SPI由以下模块组成:
(1)SSPSR
移位寄存器(Shift Register),根据通信时序从SSPBUF中移出或移入数据
(2)SSPBUF
数据缓冲区。通过读写SPI的发送和接收寄存器,可以间接控制SSPBUF,从而实现发送/接收数据。
(3)Controller
主设备的Controller通过时钟信号(CLK)和片选信号(CS)控制从设备。
1.2.1 SSPSR
SSPSR 是 SPI 设备内部的移位寄存器(Shift Register). 它的主要作用是根据 SPI 时钟信号状态, 往 SSPBUF 里移入或者移出数据, 每次移动的数据大小由 Bus-Width 以及 Channel-Width 所决定。
Bus-Width 的作用是指定地址总线到 Master 设备之间数据传输的单位.
例如, 我们想要往 Master 设备里面的 SSPBUF 写入 16 Byte 大小的数据: 首先, 给 Master 设备的配置寄存器设置 Bus-Width 为 Byte; 然后往 Master 设备的 Tx-Data 移位寄存器在地址总线的入口写入数据, 每次写入 1 Byte 大小的数据(使用 writeb 函数); 写完 1 Byte 数据之后, Master 设备里面的 Tx-Data 移位寄存器会自动把从地址总线传来的1 Byte 数据移入 SSPBUF 里; 上述动作一共需要重复执行 16 次.
Channel-Width 的作用是指定 Master 设备与 Slave 设备之间数据传输的单位. 与 Bus-Width 相似, Master 设备内部的移位寄存器会依据 Channel-Width 自动地把数据从 Master-SSPBUF 里通过 Master-SDO 管脚搬运到 Slave 设备里的 Slave-SDI 引脚, Slave-SSPSR 再把每次接收的数据移入 Slave-SSPBUF里.通常情况下, Bus-Width 总是会大于或等于 Channel-Width, 这样能保证不会出现因 Master 与 Slave 之间数据交换的频率比地址总线与 Master 之间的数据交换频率要快, 导致 SSPBUF 里面存放的数据为无效数据这样的情况.
1.2.2 SSPBUF
我们知道, 在每个时钟周期内, Master 与 Slave 之间交换的数据其实都是 SPI 内部移位寄存器从 SSPBUF 里面拷贝的. 我们可以通过往 SSPBUF 对应的寄存器 (Tx-Data / Rx-Data register) 里读写数据, 间接地操控 SPI 设备内部的 SSPBUF.。
例如, 在发送数据之前, 我们应该先往 Master 的 Tx-Data 寄存器写入将要发送出去的数据, 这些数据会被 Master-SSPSR 移位寄存器根据 Bus-Width 自动移入 Master-SSPBUF 里, 然后这些数据又会被 Master-SSPSR 根据 Channel-Width 从 Master-SSPBUF 中移出, 通过 Master-SDO 管脚传给 Slave-SDI 管脚, Slave-SSPSR 则把从 Slave-SDI 接收到的数据移入 Slave-SSPBUF 里. 与此同时, Slave-SSPBUF 里面的数据根据每次接收数据的大小(Channel-Width), 通过 Slave-SDO 发往 Master-SDI, Master-SSPSR 再把从 Master-SDI 接收的数据移入 Master-SSPBUF.在单次数据传输完成之后, 用户程序可以通过从 Master 设备的 Rx-Data 寄存器读取 Master 设备数据交换得到的数据.。
1.2.3 Controller
Master 设备里面的 Controller 主要通过时钟信号(Clock Signal)以及片选信号(Slave Select Signal)来控制 Slave 设备. Slave 设备会一直等待, 直到接收到 Master 设备发过来的片选信号, 然后根据时钟信号来工作.。
Master 设备的片选操作必须由程序所实现. 例如: 由程序把 SS/CS 管脚的时钟信号拉低电平, 完成 SPI 设备数据通信的前期工作; 当程序想让 SPI 设备结束数据通信时, 再把 SS/CS 管脚上的时钟信号拉高电平.。
1.3 SPI通信配置(SPI模式)
在SPI设备的datasheet可能会有这样一句话:
表示SPI总线支持mode 0和3。这里的mode 0和3是指时钟模式,SPI协议通常有4种模式,可以通过CPOL(时钟极性 Clock Polarity)和CPHA(时钟相位 Clock Phase)来定义
1.3.1 时钟极性 CKP/Clock Polarity
除了配置串行时钟速率(频率)外,SPI主设备还需要配置时钟极性。
根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据;
CKP可以配置为1或0。这意味着您可以根据需要将时钟的默认状态(IDLE)设置为高或低。极性反转可以通过简单的逻辑逆变器实现。您必须参考设备的数据手册才能正确设置CKP和CKE。
CKP = 0
:时钟空闲IDLE
为低电平0
;CKP = 1
:时钟空闲IDLE
为高电平1
;
1.3.2 时钟相位 CKE /Clock Phase (Edge)
除配置串行时钟速率和极性外,SPI主设备还应配置时钟相位(或边沿),根据硬件制造商的不同,时钟相位通常写为CKE或CPHA;
顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;
CKE = 0
:在时钟信号SCK
的第一个跳变沿采样;CKE = 1
:在时钟信号SCK
的第二个跳变沿采样;
1.3.3 时钟配置总结
综上几种情况,下图总结了所有时钟配置组合,并突出显示了实际采样数据的时刻;
其中黑色线为采样数据的时刻;
蓝色线为SCK时钟信号;
具体如下图所示;
1.3.4 模式编号
SPI的时钟极性和相位的配置通常称为 SPI模式,所有可能的模式都遵循以下约定;具体如下表所示;
SPI Mode | CPOL | CPHA |
---|---|---|
0 [00] | 0 | 0 |
1 [01] | 0 | 1 |
2 [10] | 1 | 0 |
3 [11] | 1 | 1 |
除此之外,我们还应该仔细检查微控制器数据手册中包含的模式表,以确保一切正常。
1.5 SPI多从机模式
1.5.1 多NSS
- 通常,每个从机都需要一条单独的SS线。
- 如果要和特定的从机进行通讯,可以将相应的
NSS
信号线拉低,并保持其他NSS
信号线的状态为高电平;如果同时将两个NSS
信号线拉低,则可能会出现乱码,因为从机可能都试图在同一条MISO
线上传输数据,最终导致接收数据乱码。
SPI协议可以操作在主器件对单个或者多个从器件的条件下:
1.5.2 菊花链
在数字通信世界中,在设备信号(总线信号或中断信号)以串行的方式从一 个设备依次传到下一个设备,不断循环直到数据到达目标设备的方式被称为菊花链。
- 菊花链的最大缺点是因为是信号串行传输,所以一旦数据链路中的某设备发生故障的时候,它下面优先级较低的设备就不可能得到服务了;
- 另一方面,距离主机越远的从机,获得服务的优先级越低,所以需要安排好从机的优先级,并且设置总线检测器,如果某个从机超时,则对该从机进行短路,防止单个从机损坏造成整个链路崩溃的情况;
具体的连接如下图所示;
其中红线加粗为数据的流向;
所以最终的数据流向图可以表示为:
SCK为时钟信号,8clks表示8个边沿信号;
其中D为数据,X为无效数据;
所以不难发现,菊花链模式充分使用了SPI其移位寄存器的功能,整个链充当通信移位寄存器,每个从机在下一个时钟周期将输入数据复制到输出。
使用该方法时,由于数据是从一个从机传播到下一个从机,所以传输数据所需的时钟周期数与菊花链中的从机位置成比例。例如在图所示的8位系统中,为使第3个从机能够获得数据,需要24个时钟脉冲,而常规SPI模式下只需8个时钟脉冲。
1.6 SPI优缺点
1.6.1 SPI通讯的优势
使SPI作为串行通信接口脱颖而出的原因很多;
- 全双工串行通信;
- 高速数据传输速率。
- 简单的软件配置;
- 非常简单的硬件结构。从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。
- SPI的一个独特优势是可以无中断传输数据。可以在连续流中发送或接收任意数量的位。使用I2C和UART,数据以数据包的形式发送,限制为特定位数。开始和停止条件定义了每个数据包的开始和结束,因此数据在传输过程中被中断。
1.6.2 SPI的缺点
- 没有硬件从机应答信号(主机可能在不知情的情况下无处发送);
- 通常仅支持一个主设备;
- 需要更多的引脚(与I2C不同);
- 没有定义硬件级别的错误检查协议;
- 与RS-232和CAN总线相比,只能支持非常短的距离;
1.7 SPI使用心得
SPI不存在单独的读或者写数据。就算只进行单向的数据传输,也要保持这样的顺序。这就意味着无论接收任何数据,必须实际发送一些东西!在这种情况下,我们称其为虚拟数据;每一次通讯写操作必然伴随着读操作。若只进行写操作,主机只需忽略接收到的字节(虚拟数据);反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。
对于SPI通信并没有规范最高的通信速率,在我的开发经验中有见过达到50Mbit/s的应用场景,但通常比较常见的还是10Mbit/s左右,具体选用多快的通信速度,还得在实际项目中根据情况具体设计。
在主设备这边配置SPI接口时钟的时候一定要弄清楚从设备的时钟要求,因为主设备这边的时钟极性和相位都是以从设备为基准的。
因此在时钟极性的配置上一定要搞清楚从设备是在时钟的上升沿还是下降沿接收数据,是在时钟的下降沿还是上升沿输出数据。但要注意的是,由于主设备的SDO连接从设备的SDI,从设备的SDO连接主设备的SDI,从设备SDI接收的数据是主设备的SDO发送过来的,主设备SDI接收的数据是从设备SDO发送过来的,所以主设备这边SPI时钟极性的配置(即SDO的配置)跟从设备的SDI接收数据的极性是相反的,跟从设备SDO发送数据的极性是相同的。下
面这段话是Sychip Wlan8100 Module Spec上说的,充分说明了时钟极性是如何配置的:
The 81xx module will always input data bits at the rising edge of the clock, and the host will always output data bits on the falling edge of the clock.
意思是:主设备在时钟的下降沿发送数据,从设备在时钟的上升沿接收数据。因此主设备这边SPI时钟极性应该配置为下降沿有效。
又如,下面这段话是摘自LCD Driver IC SSD1289:
SDI is shifted into 8-bit shift register on every rising edge of SCK in the order of data bit 7, data bit 6 …… data bit 0.
意思是:从设备SSD1289在时钟的上升沿接收数据,而且是按照从高位到地位的顺序接收数据的。因此主设备的SPI时钟极性同样应该配置为下降沿有效。
时钟极性和相位配置正确后,数据才能够被准确的发送和接收, 因此应该对照从设备的SPI接口时序或者Spec文档说明来正确配置主设备的时钟。