SPI协议学习笔记

目录

一、SPI简介

二、常见SPI标准

常见4线制 / 3线制标准

3线制&4线 SPI区别

接口介绍

 SPI的四种工作模式

三、工作机制

四、SPI通信时序

SPI时序

模拟SPI

五、注意事项


一、SPI简介

SPI(Serial Peripheral Interface)即串行外设接口,是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线。

SPI优点

(1)支持全双工通信

(2)通信简单

(3)数据传输速率块

缺点

(1)没有指定的流控制

(2)没有应答机制确认是否接收到数据

(3)抗干扰能力差

特点

(1)高速、同步、全双工、非差分、总线式

(2)主从机通信模式


二、常见SPI标准

初识SPI感觉比较混乱,主要是因为其没有标准的协议,只有moto的事实标准,没有被任何的国际委员会承认。所以衍生出多个版本。

常见4线制 / 3线制标准

4线SPI包括SCLK、SDO、SDI、CS,用在多机模式中,而3线SPI仅包括SCLK、SDO、SDI,没有CS;

另一种说法则认为4线SPI包括SCLK、SDO、SDI、CS,而3线SPI包括SCLK、DATA、CS,其中DATA负责数据的发送和接收。SPI没有标准协议,所以应用时需要仔细核对主机、从机器件规格。

3线制&4线 SPI区别

三线与四线的区别在于DATA线.  mosi 和miso 合并为一根data线 就是标准的三线

省略cs的虽然也是三根线. 但是不是表完全意义上是三线

接口介绍

SPI接口是以主从方式工作的,这种模式通常有一个主器件和一个或多个从器件,其接口包括以下四种信号:

(1)SDO/MOSI    ----    主器件数据输出,从器件数据输入

(2)SDI/MISO    ----    主器件数据输入,从器件数据输出

(3)SCLK    ----    时钟信号,由主器件产生

(4)NSS/CS/SS    ----    从器件使能信号,由主器件控制,当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高

 

SPI协议学习笔记

                                                                                           SPI接线示例

 SPI的四种工作模式

四种工作模式是按照 SPI时钟极性CPOL 和 SPI时钟相位CPHA 来划分的。

不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式。

模式 CPOL CPHA
Mode0 0 0
Mode1 0 1
Mode2 1 0
Mode3 1 1

                                                                                           SPI工作模式

时钟极性CPOL是用来配置SCLK的电平出于哪种状态时是空闲态或者有效态,时钟相位CPHA是用来配置数据采样是在第几个边沿:
CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时
CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿
CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿

接下来展示一下四种情况下的SPI工作模式图,可以方便我们的理解,如下:

SPI协议学习笔记

 

三、工作机制

SPI协议学习笔记

                                                                   SPI 设备间通信简单的描述

 SPI 设备间的数据传输之所以又被称为数据交换,是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 "发送者(Transmitter)" 或者 "接收者(Receiver)"。在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据,相当于该设备有一个 bit 大小的数据被交换了。

  一个 Slave 设备要想能够接收到 Master 发过来的控制信号, 必须在此之前能够被 Master 设备进行访问 (Access)。所以, Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选,把想要访问的 Slave 设备选上。

下面就来解释一下图中所示的几个组件(Module):

       SSPBUF( Synchronous Serial Port Buffer)泛指 SPI 设备里面的内部缓冲区, 一般在物理上是以 FIFO 的形式, 保存传输过程中的临时数据;

       SSPSR(Synchronous Serial Port Register)泛指 SPI 设备里面的移位寄存器(Shift Regitser),它的作用是根据设置好的数据位宽(bit-width) 把数据移入或者移出 SSPBUF;

       Controller,泛指 SPI 设备里面的控制寄存器,可以通过配置它们来设置 SPI 总线的传输模式。

        通常情况下,我们只需要对上图所描述的四个管脚(pin) 进行编程即可控制整个 SPI 设备之间的数据通信;

        SCK(Serial Clock)主要的作用是 Master 设备往 Slave 设备传输时钟信号, 控制数据交换的时机以及速率;

        SS/CS(Slave Select/Chip Select)用于 Master 设备片选 Slave 设备,使被选中的 Slave 设备能够被 Master 设备所访问;

        SDO/MOSI(Serial Data Output/Master Out Slave In)在 Master 上面也被称为 Tx-Channel,作为数据的出口,主要用于 SPI 设备发送数据;

        SDI/MISO(Serial Data Input/Master In Slave Out)在 Master 上面也被称为 Rx-Channel,作为数据的入口,主要用于SPI 设备接收数据;

        SPI 设备在进行通信的过程中, Master 设备和 Slave 设备之间会产生一个数据链路回环(Data Loop),就像上图所画的那样,通过 SDO 和 SDI 管脚,SSPSR 控制数据移入移出 SSPBUF,Controller 确定 SPI 总线的通信模式,SCK 传输时钟信号。

 

四、SPI通信时序

SPI时序

SPI并不像UART或者IIC通信那样有专门的通信周期,有专门的通信起始信号,有专门的通信结束信号;它由主设备时钟信号的出现与否来确定主/从设备间的通信。一旦检测到主设备的时钟信号,数据开始传输。当没有数据交流的时候我们的时钟线要么是保持高电平要么是保持低电平,由CPOL决定。

SPI协议学习笔记

SPI协议学习笔记

 

模拟SPI

/*
表示相关引脚高低电平,要根据实际引脚修改。
SSEL_D(0) SSEL_D(1)  //片选
SCK_D(0)  SCK_D(1)     //时钟信号
MOSI_D(0) MOSI_D(1)   //SDO
MISO_I(0) MISO_I(1)       //SDI
*/

#define _CPOL     1  //时钟极性
#define _CPHA     0  //时钟相位

//延时子程序
void delay()
{
 unsigned char m,n;
     for(n=0;n<5;n++);
    for(m=0;m<100;m++);
}


/**********************************************
模式零           写数据
***********************************************/
#if _CPOL==0&&_CPHA==0          //MODE   0  0   
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 for(n=0;n<8;n++)
 {
  SCK_D(0);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(1);
 }
  SCK_D(0);
}


/*********************************************
模式零         读数据
*********************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 for(n=0;n<8;n++)
 {
  SCK_D(0);
  dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(1);
 }
  SCK_D(0);
  return dat;
}
#endif


/*********************************************
模式一        写数据
*********************************************/
#if _CPOL==0&&_CPHA==1           //MODE  0  1
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 SCK_D(0);
 for(n=0;n<8;n++)
 {
  SCK_D(1);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(0);
 }
}
/*********************************************
模式一       读数据
*********************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 for(n=0;n<8;n++)
 {
  SCK_D(1);
   dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(0);
 }
  SCK_D(0);
  return dat;
}
#endif


/**********************************************
模式二           写数据
***********************************************/
#if _CPOL==1&&_CPHA==0           //MODE   1  0
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 for(n=0;n<8;n++)
 {
  SCK_D(1);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(0);
 }
  SCK_D(1);
}
/*********************************************
模式二          读数据
*********************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 for(n=0;n<8;n++)
 {
  SCK_D(1);
  dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(0);
 }
  SCK_D(1);
  return dat;
}


#endif



/**********************************************
模式三          写数据
***********************************************/
#if _CPOL==1&&_CPHA==1            //MODE  1  1
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 SCK_D(1);
 for(n=0;n<8;n++)
 {
  SCK_D(0);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(1);
 }
}

/************************************
模式三          读数据
************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 SCK_D(0);
 for(n=0;n<8;n++)
 { SCK_D(0);
  dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(1);
 }
  SCK_D(1);
  return dat;
}
#endif

 

五、注意事项

在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样。如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致 SPI 物理模块最终失效。因此, 在程序中一般都会在 SPI 传输完数据后,去读取 SPI 设备里的数据,即使这些数据(Dummy Data)在我们的程序里是无用的。

如果只发送数据,要读取的数据的时候再读取,这样子会造成溢出标志OVR被置1。

因为平时的数据没有被读取,又来一个,真正要读取的时候,就会发现都是FF。发送OVR之后,还是DR的内容不会被修改,所以都是FF。

SPI协议学习笔记

翻译如下:

溢出标志(overrun flag,OVR):当接收到数据且尚未从SPI DR中读取以前的数据时,将设置此标志。结果,传入数据丢失。如果在SPI CR2中设置了erriebit,则可能会产生中断。在这种情况下,接收缓冲区内容不会用从发送器设备接收到的新数据进行更新。SPI DR寄存器的读取操作返回先前正确接收的数据。所有其它随后传输的半字都会丢失。通过在SPI DR寄存器上执行读操作,然后对SPI SR寄存器进行读访问来清除OVR位。