SPI通讯硬件接口、原理、时序、程序详解

通讯协议3-SPI通讯 

 

注意:本文章需要配合无际单片机编程-硬件基础2.0教程视频学习。

注意:本文章需要配合无际单片机编程-硬件基础2.0教程视频学习。

 

一、SPI的介绍:

SPI中文名称是:串行外设接口(Serial Peripheral Interface)。

SPI一般用于芯片之间的数据交互,在单片机开发中比较常见。

是一种高速、全双工、同步通信总线。SPI有主、从两种模式,通常由一个主模块和一个或多个从模块组成(SPI不支持多主机)之间的通讯。

 

二、SPI硬件连接接口

SPI硬件连接比较简单,需要4根线(通讯设备需要共地):

  • MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入;
  • MISO( Master Input Slave Output):主设备数据输入,从设备数据输出;
  • SCLK(Serial Clock):时钟信号,由主设备产生;
  • CS/SS(Chip Select/Slave Select)(片选):从设备使能信号,由主设备控制,一主多从时,CS/SS是从芯片是否被主芯片选中的控制信号,只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。

 

问题:MOSI 和MOSI连接?还是MOSI和MISO连接?

在产品开发中,我们经常会碰到这个疑问,MOSI和MISO连接,还是和MOSI连接呢?和串口工作的原理类似,理论上是MOSI 和MISO 连接,MISO和MOSI连接的。 

如果是两个单片机通过SPI通讯,需要按照:MOSI-MISO、MISO-MOSI连接。  但如果是单片机和只能做从机的设备连接,是MOSI-MOSI、MISO-MOSI 连接。因为从机的丝印标志的是需要连接主机的端口。 其从机内部MISO脚的功能是MOSI,或MOSI的功能是MISO;

 

三、通讯协议:

SPI是一种全双工、高速的、同步的通信总线. SPI的通讯相比UART、IIC更加简单,没有起始位、结束位;数据按照时钟直接发送和接收;

1.  SPI通讯时序图举例:

①举例1: 

主机发送数据(0x65)给从机:   

从机发送数据(0xA9)给主机: 

时序图说明: SPI和IIC、Uart 相比,通讯更简单,没有起始位,停止位。

关于的时序图,有几个问题:

  1. 一个时钟周期怎么确定?
  2. 发送/接收数据 固定高字节在前面?
  3. 数据在哪里更改?哪里采集?

 

2.数据通讯原理

①时钟速率

SPI总线上的主设备必须在通信开始时候配置并生成相应的时钟信号。从理论上讲,只要实际可行,时钟速率就可以是你想要的任何速率,当然这个速率受限于每个系统能提供多大的系统时钟频率,以及最大的SPI传输速率。

②数据格式

SPI发送数据在对应的规格书中都有规定的,如下图,支持的是MSB(Most Significant Bit) 最高有效位在前,当然也可以配置成LSB(Least Significant Bit)最低有效位在前;

SPI程序开发,这个参数也是可以配置的:

③时钟极性(CPOL或CKP)

时钟极性,一般用CPOLCKP 来表示,其作用是用来规定,SPI空闲状态下CLK的电平的:

  • CPOL = 0:  CLK空闲状态是 低电平;
  • CPOL = 1:  CLK空闲状态是 高电平

④时钟相位

时钟相位,一般用CKECPHA来表示;其作用是用来规定,采集数据是在那个时钟信号的位置;

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

 

⑤SPI的四种通讯模式:

按照时钟极性和时钟相位不同的组合,SPI共有4种通讯模式,具体如下:

  • 模式0: CPOL = 0    CPHA = 0

  • 模式1: CPOL = 0    CPHA =1

  • 模式2: CPOL = 1    CPHA = 0

  • 模式3: CPOL = 1     CPHA =1

⑥SPI的通讯内部工作结构(了解)

这部分内容,建议大家有个了解即可,不需要花太多时间。

SPI通讯,主设备和从设备接口内部,都有一个串行移位寄存器,主设备通过向它的SPI串行寄存器写入一个字节来发起一次传输。 所以在主机发送数据的同时,也接收到了新的数据。

注意:SPI 有主模式和从模式之分,但没有读和写的区别,写操作和读操作是同步完成的。

如果SPI写操作时,主机需要忽略接收到的数据;同理,SPI主机读取从机数据的时候,就必须发送一个空字节来引发从机数据的传输。即:发一个数据必然会收到一个数据;要收一个数据必须也要先发一个数据。

 

四、  单片机SPI参数程序代码配置:

1.单片机内部硬件SPI接口参数配置

这里以STM32单片机为例:

①硬件连接图

②SPI参数配置-代码  

从机25Q64连接的是STM32单片机的硬件SPI2 接口。

单片机内部集成的SPI,可以自动的发送和接收数据,在程序开发的时候,我们只需要按照单片机官方的要求配置相关参数即可,如下:

/* SPI2 configuration */ 
// #define SPI_Direction_2Lines_FullDuplex ((uint16_t)0x0000)
// #define SPI_Direction_2Lines_RxOnly     ((uint16_t)0x0400)
// #define SPI_Direction_1Line_Rx          ((uint16_t)0x8000)
// #define SPI_Direction_1Line_Tx          ((uint16_t)0xC000)
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI1设置为两线全双工
// #define SPI_Mode_Master                 ((uint16_t)0x0104)   //主机
//  #define SPI_Mode_Slave                  ((uint16_t)0x0000)   //从机
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                      //设置SPI1为主模式
// #define SPI_DataSize_16b                ((uint16_t)0x0800)
// #define SPI_DataSize_8b                 ((uint16_t)0x0000)
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  //SPI发送接收8位帧结构
// #define SPI_CPOL_Low                    ((uint16_t)0x0000)
//  #define SPI_CPOL_High                   ((uint16_t)0x0002)
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                      //串行时钟在不操作时,时钟为高电平
// #define SPI_CPHA_1Edge                  ((uint16_t)0x0000)
// #define SPI_CPHA_2Edge                  ((uint16_t)0x0001)
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                    //第二个时钟沿开始采样数据
// #define SPI_NSS_Soft                    ((uint16_t)0x0200)
//  #define SPI_NSS_Hard                    ((uint16_t)0x0000)
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;              //NSS信号由软件(使用SSI位)管理

// #define SPI_BaudRatePrescaler_2         ((uint16_t)0x0000)
// #define SPI_BaudRatePrescaler_4         ((uint16_t)0x0008)
// #define SPI_BaudRatePrescaler_8         ((uint16_t)0x0010)
// #define SPI_BaudRatePrescaler_16        ((uint16_t)0x0018)
// #define SPI_BaudRatePrescaler_32        ((uint16_t)0x0020)
// #define SPI_BaudRatePrescaler_64        ((uint16_t)0x0028)
// #define SPI_BaudRatePrescaler_128       ((uint16_t)0x0030)
// #define SPI_BaudRatePrescaler_256       ((uint16_t)0x0038)
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8
//#define SPI_FirstBit_MSB                ((uint16_t)0x0000)
//#define SPI_FirstBit_LSB                ((uint16_t)0x0080)
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;          //数据传输从MSB位开始

这里只给大家提供了SPI参数的配置,关于相关GPIO口的配置和读写在这里就给大家不讲解了,大家后续做实际项目的时候再进一步去学习和理解。本节课的重点是要理解SPI通讯协议即可。

 

2.  模拟SPI通讯参数配置

 

① 硬件连接

主机: STM8L103

从机: SX1278

通过规格书知悉:数据高位在前  CPOL = 0  CPHA = 0; 

 

②代码

//输入  MISO
#define MISO_PORT GPIOB
#define MISO_PIN    GPIO_Pin_7
#define MISO_DATABIT GPIO_ReadInputDataBit(MISO_PORT,MISO_PIN)

//输出   MOSI
#define MOSI_PORT GPIOB
#define MOSI_PIN GPIO_Pin_6
#define MOSI_HIGH GPIO_SetBits(MOSI_PORT,MOSI_PIN)
#define MOSI_LOW GPIO_ResetBits(MOSI_PORT,MOSI_PIN)

#define SCK_PORT GPIOB
#define SCK_PIN GPIO_Pin_5
#define SCK_HIGH GPIO_SetBits(SCK_PORT,SCK_PIN)
#define SCK_LOW GPIO_ResetBits(SCK_PORT,SCK_PIN)

//CS
#define CS_PORT GPIOB
#define CS_PIN GPIO_Pin_4
#define CS_HIGH GPIO_SetBits(CS_PORT,CS_PIN)
#define CS_LOW GPIO_ResetBits(CS_PORT,CS_PIN)

//输入
#define RF_IRQ_DIO0_PORT GPIOB
#define RF_IRQ_DIO0_PIN GPIO_Pin_3
#define RF_IRQ_DIO0_DATABIT GPIO_ReadInputDataBit(RF_IRQ_DIO0_PORT,RF_IRQ_DIO0_PIN)

///通讯GPIO口初始化
static void Sx1278_GpioInt()
{
        //***** SPI_SCK        
         GPIO_Init(SCK_PORT,SCK_PIN , GPIO_Mode_Out_PP_Low_Slow); 
        //*****SPI_MISO       
        GPIO_Init(MISO_PORT,MISO_PIN , GPIO_Mode_In_PU_No_IT);
       //*****SPI_MOSI     
        GPIO_Init(MOSI_PORT,MOSI_PIN , GPIO_Mode_Out_PP_Low_Slow);

         CS_HIGH;
        RF_RST_HIGH;
}

/**********************************************************
**Name:     SPI_SentDate
**Function: SPI Write one byte
**Input:    WrPara
**Output:   none
**note:     use for burst mode
**********************************************************/
void SPI_SentDate(u8 WrPara)
{//CPOL = 0  CPHA = 0;
       u8 bitcnt;  
       CS_LOW;
       SCK_LOW;
        for(bitcnt=8; bitcnt!=0; bitcnt–)
       {
              SCK_LOW;
              if(WrPara&0x80)
                {///高位在前   1
                       MOSI_HIGH;
                }
                else
                {
                    MOSI_LOW;
                }
                asm(“nop”);
                asm(“nop”);
                SCK_HIGH;
                 asm(“nop”);
                WrPara <<= 1;
        }
       SCK_LOW;
       MOSI_HIGH;
}

/**********************************************************
**Name:     SPI_ReadByte
**Function: SPI Read one byte
**Input:    None
**Output:   result byte
**Note:     use for burst mode
**********************************************************/
u8 SPI_ReadByte(void)
{
    u8 RdPara = 0;
    u8 bitcnt;
    CS_LOW;                  
    MOSI_HIGH;
    for(bitcnt=8; bitcnt!=0; bitcnt–)
    {///高位在前
           SCK_LOW;
           asm(“nop”);
           asm(“nop”);
            RdPara <<= 1;
           SCK_HIGH;
           if(MISO_DATABIT)
           {
                   RdPara |= 0x01;
            }
           else
           {
              RdPara |= 0x00;
           }
             asm(“nop”);
           asm(“nop”);
    }
    SCK_LOW;
    return(RdPara);
}

 

五、SPI优缺点

1.优点

  • 无起始位和停止位、校验位,数据可以连续高效传输;
  • 数据传输速率比串口、IIC 速率更高  
  • 全双工,支持同时发送和接收数据;
  • 数据位长度没有限制,比较常用的是8bit、16bit

 

2.缺点

  • 通讯线比串口、IIC更多,需要四根信号线   CS   CLK  MOSI  MISO
  • 没有校验位/应答位,无法确认是否已成功接收数据 

<结束>

关于SPI 相关的知识点,大家可以看以下两篇文章。

单片机spi通信接口什么意思,spi接口干什么用的? (qq.com)

spi协议时序图和四种模式实际应用详解 (qq.com)

给TA买糖
共{{data.count}}人
人已赞赏
单片机百科单片机自学指南

IIC通讯接口、协议格式、时序、软件模拟详解

2023-5-24 9:22:34

单片机百科单片机自学指南

STM8学习路径、学习资料、工具、教程说明

2023-5-24 10:00:16

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧