SPI通信协议详解

一、SPI简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据。

SPI是一种事实标准,并没有一个官方标准,已知已有的器件SPI 速率可达到50Mbps,具体到产品中SPI的速率主要看主从器件SPI控制器的性能限制。此外, SPI没有相应的流控和应答机制,这样跟IC协议相比在数据可靠性上有一定的缺陷。

SPI还有升级的类型 DSPI(Dual SPI)和 QSPI(Quad SPI),这两种接口拥有更快的通讯速度,代价分别是失去全双工特性和增加额外通讯线。本篇文章暂不对其进行介绍。

二、SPI物理协议层

1、SPI信号线详解

SPI协议是主从通信,在物理接线上很简单,在双向传输时,至少需要4根线,单向传输时3根线,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选):

  • MISO( Master Input Slave Output):主设备数据输入,从设备数据输出;

  • MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入;

  • SCLK(Serial Clock):时钟信号,由主设备产生;

  • CS/SS(Chip Select/Slave Select):从设备使能信号,低电平使能。
    在通信时,主机要拉低要通信的从设备的CS/SS引脚,只有这样,主芯片对此从芯片的操作才有效。

2、SPI通信主从器件连接方式

2.1 单从机连接

在这里插入图片描述

2.2 多从机连接

通过主机的GPIO口来控制主机与哪一个从机通信。
在这里插入图片描述

三、SPI通信时序

SPI的通讯时序中NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。 MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO各传输一位数据(SPI协议中数据读写是同步进行的)。

1、SPI的四种工作模式

在了解SPI的四种工作模式之前,我们需要先了解两个概念,这个在SPI通信协议中是非常重要的,因为时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据。

时钟极性:
根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。CKP或CPOL指的是SCLK总线在不进行通讯时的电平。CKP或CPOL可以配置为1或0。这意味着你可以根据需要将时钟的默认状态(IDLE)设置为高或低。

  • CKP = 0:时钟空闲IDLE为低电平 0;
  • CKP = 1:时钟空闲IDLE为高电平1;

时钟相位
根据硬件制造商的不同,时钟相位通常写为CKE或CPHA。CPHA指在一个时钟周期中采样是发生在第一个边缘还是第二个边缘;

  • CKE = 0:在时钟信号SCK的第一个跳变沿采样;
  • CKE = 1:在时钟信号SCK的第二个跳变沿采样;

起始和终止信号—NSS
NSS 信号线由高变低,是 SPI 通讯的起始信号。 NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。 NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

1.1 模式0(CPOL=0, CPHA=0)

MODE0:CPOL=0, CPHA=0:当空闲态时,SCK处于低电平,数据采样是在第1个边沿,也就是SCK由低电平到高电平的跳变, MOSI 及 MISO 的数据在 SCK 的下降沿期间变化输出,在 SCK 的上升沿时被采样。即在 SCK 的上升沿时刻, MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效, MOSI 及 MISO为下一次表示数据做准备。
在这里插入图片描述

1.2 模式1 (CPOL=0, CPHA=1)

MODE1:CPOL=0, CPHA=1:当空闲态时,SCK处于低电平,数据发送是在第2个边沿,也就是SCK由低电平到高电平的跳变, MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK 的下降沿时被采样。即在 SCK 的下降沿时刻, MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效, MOSI 及 MISO为下一次表示数据做准备。
在这里插入图片描述

1.3 模式2(CPOP=1, CPHA=0)

MODE2:CPOP=1, CPHA=0:当空闲态时,SCK处于高电平,数据采集是在第1个边沿,也就是SCK由高电平到低电平的跳变,MOSI 及MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK 的下降沿时被采样。即在 SCK 的下降沿时刻, MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效, MOSI 及 MISO为下一次表示数据做准备。
在这里插入图片描述

1.4 模式3(CPOP=1, CPHA=1)

MODE3:CPOP=1, CPHA=1:当空闲态时,SCK处于高电平,数据发送是在第2个边沿,也就是SCK由高电平到低电平的跳变,MOSI 及MISO 的数据在 SCK 的下降沿期间变化输出,在 SCK 的上升沿时被采样。即在 SCK 的上升沿时刻, MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效, MOSI 及 MISO为下一次表示数据做准备。
在这里插入图片描述

四、SPI软件模拟驱动(C语言版)

  • 源文件代码
#include "spi.h"
/*
*********************************************************************************************************
*	SPI四种工作模式介绍:
*   Mode0:CPOL = 0, CPHA = 0 , SCLK 空闲是为低电平,数据在上升沿有效
*   Mode1: CPOL = 0,CPHA = 1 , SCLK 空闲是为低电平,数据在下降沿有效
*   Mode2: CPOL = 1,CPHA = 0 , SCLK 空闲是为高电平,数据在下降沿有效
*   Mode3: CPOL = 1, CPHA = 1 , SCLK 空闲是为高电平,数据在上升沿有效
*********************************************************************************************************
*/

static void spi_delay_us(char tim)
{
   smr_delay_us(tim); 
}


/* CPOL = 0, CPHA = 0, MSB first, SCLK 空闲是为低电平,数据在上升沿有效 */
uint8_t smr_spr_rw_mode0( uint8_t write_dat )
{
    uint8_t i, read_dat=0;
    SCK_L;
    for( i = 0; i < 8; i++ )
    {
        if( write_dat & 0x80 )
            MOSI_H;  
        else                    
            MOSI_L;  
        write_dat <<= 1;
        spi_delay_us(1);  
        SCK_H; 
        read_dat <<= 1;  
        if( MISO ) 
            read_dat++; 
        spi_delay_us(1);
        SCK_L; 

    }
  
    return read_dat;
}
 
 
/* CPOL=0,CPHA=1, MSB first, SCLK 空闲是为低电平,数据在下降沿有效 */
uint8_t smr_spr_rw_mode1(uint8_t byte) 
{
    uint8_t i,Temp=0;
  SCK_L;
  for(i=0;i<8;i++)     // 循环8次
  {
    SCK_H;     //拉高时钟
    if(byte&0x80)
    {
      MOSI_H;  //若最到位为高,则输出高
    }
    else      
    {
      MOSI_L;   //若最到位为低,则输出低
    }
    byte <<= 1;     // 低一位移位到最高位
    spi_delay_us(1);
    SCK_L;     //拉低时钟
    Temp <<= 1;     //数据左移
 
    if(MISO)
      Temp++;     //若从从机接收到高电平,数据自加一
    spi_delay_us(1);
 
  }
  return (Temp);     //返回数据
}
 
/* CPOL=1,CPHA=0, MSB first, SCLK 空闲是为高电平,数据在下降沿有效 */
uint8_t smr_spr_rw_mode2(uint8_t byte) 
{
  uint8_t i,Temp=0;
  SCK_H;
  for(i=0;i<8;i++)     // 循环8次
  {
    if(byte&0x80)
        {
      MOSI_H;  //若最到位为高,则输出高
        }
    else      
    {
      MOSI_L;   //若最到位为低,则输出低
    }
    byte <<= 1;     // 低一位移位到最高位
    spi_delay_us(1);
    SCK_L;     //拉低时钟
    Temp <<= 1;     //数据左移
 
    if(MISO)
      Temp++;     //若从从机接收到高电平,数据自加一
    spi_delay_us(1);
    SCK_H;     //拉高时钟
    
  }
  return (Temp);     //返回数据
}
 
 
/* CPOL = 1, CPHA = 1, MSB first, SCLK 空闲是为高电平,数据在上升沿有效 */
uint8_t smr_spr_rw_mode3( uint8_t write_dat )
{
    uint8_t i, read_dat=0;
    SCK_H;
    for( i = 0; i < 8; i++ )
    {
        SCK_L; 
        if( write_dat & 0x80 )
            MOSI_H;  
        else                    
            MOSI_L;  
        write_dat <<= 1;
        spi_delay_us(1);  
        SCK_H; 
        read_dat <<= 1;  
        if( MISO ) 
            read_dat++; 
        spi_delay_us(1);

    }
    return read_dat;
}

  • 头文件代码
#ifndef _SPI_H_
#define _SPI_H_

//引脚配置  根据使用的平台自行配置
#define MOSI_H 1
#define MOSI_L 0

#define MISO   0

#define SCK_H  1
#define SCK_L  0


uint8_t smr_spr_rw_mode0( uint8_t write_dat );
uint8_t smr_spr_rw_mode1( uint8_t byte );
uint8_t smr_spr_rw_mode2( uint8_t byte );
uint8_t smr_spr_rw_mode3( uint8_t write_dat );

#endif

文章编写参考以下几篇文章:

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值