1、SPI协议

简介

SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface),即串行外围设备接口,是
一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率
较高的场合。

SPI 物理层

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】-LMLPHP
1、SS*(* Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS。
2、SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
3、MOSI (Master Output,Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
4、MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信线读入数据,
从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

协议层

与 I2C 的类似,SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。

时序图

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】-LMLPHP NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。。

通讯的起始和停止信号

在图中的标号处**,**。
NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选了,开始准备与主机通讯。在图中的标号处,,表示本次通讯结束,从机的选中状态被取消。

数据有效性

1、SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。
2、MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图 中的 MSB 先行模式。
3、观察图中的标号处,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI 及 MISO 为下一次表示数据做准备。
4、SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

CPOL/CPHA 及通讯模式

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】-LMLPHP

STM32 SPI系统架构

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】-LMLPHPSTM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 fpclk/2 (STM32F103 型
号的芯片默认 f:sub:pclk1 为 42MHz(407)45Mhz(429),fpclk2 为 84MHz(407) 90Mhz(429)),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工 (前面小节说明的都是这种模式)、双线单向以及单线模式。其中双线单向模式可以同时使用 MOSI 及 MISO 数据线向一个方向传输数据,可以加快一倍的传输速度。而单线模式则可以减少硬件接线,当然这样速率会受到影响。我们只讲解双线全双工模式。

2、SPI使用

HAL库SPI结构体成员

/**
  * @brief  SPI handle Structure definition
  */
typedef struct __SPI_HandleTypeDef
{
  SPI_TypeDef                *Instance;      /*!< SPI registers base address               */
  SPI_InitTypeDef            Init;           /*!< SPI communication parameters             */
  uint8_t                    *pTxBuffPtr;    /*!< Pointer to SPI Tx transfer Buffer        */
  uint16_t                   TxXferSize;     /*!< SPI Tx Transfer size                     */
  __IO uint16_t              TxXferCount;    /*!< SPI Tx Transfer Counter                  */
  uint8_t                    *pRxBuffPtr;    /*!< Pointer to SPI Rx transfer Buffer        */
  uint16_t                   RxXferSize;     /*!< SPI Rx Transfer size                     */
  __IO uint16_t              RxXferCount;    /*!< SPI Rx Transfer Counter                  */
  void (*RxISR)(struct __SPI_HandleTypeDef *hspi);   /*!< function pointer on Rx ISR       */
  void (*TxISR)(struct __SPI_HandleTypeDef *hspi);   /*!< function pointer on Tx ISR       */
  DMA_HandleTypeDef          *hdmatx;        /*!< SPI Tx DMA Handle parameters             */
  DMA_HandleTypeDef          *hdmarx;        /*!< SPI Rx DMA Handle parameters             */
  HAL_LockTypeDef            Lock;           /*!< Locking object                           */
  __IO HAL_SPI_StateTypeDef  State;          /*!< SPI communication state                  */
  __IO uint32_t              ErrorCode;      /*!< SPI Error code                           */

#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
  void (* TxCpltCallback)(struct __SPI_HandleTypeDef *hspi);             /*!< SPI Tx Completed callback          */
  void (* RxCpltCallback)(struct __SPI_HandleTypeDef *hspi);             /*!< SPI Rx Completed callback          */
  void (* TxRxCpltCallback)(struct __SPI_HandleTypeDef *hspi);           /*!< SPI TxRx Completed callback        */
  void (* TxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);         /*!< SPI Tx Half Completed callback     */
  void (* RxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);         /*!< SPI Rx Half Completed callback     */
  void (* TxRxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);       /*!< SPI TxRx Half Completed callback   */
  void (* ErrorCallback)(struct __SPI_HandleTypeDef *hspi);              /*!< SPI Error callback                 */
  void (* AbortCpltCallback)(struct __SPI_HandleTypeDef *hspi);          /*!< SPI Abort callback                 */
  void (* MspInitCallback)(struct __SPI_HandleTypeDef *hspi);            /*!< SPI Msp Init callback              */
  void (* MspDeInitCallback)(struct __SPI_HandleTypeDef *hspi);          /*!< SPI Msp DeInit callback            */

#endif  /* USE_HAL_SPI_REGISTER_CALLBACKS */
} SPI_HandleTypeDef;

HAL库函数

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi); //初始化函数
HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi); //默认初始化函数
__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi); //初始化回调函数
__weak void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi); //默认初始化回调函数
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout); //发送函数
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout); //接收函数
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,
                                          uint32_t Timeout); //接收,发送
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size); //中断发送
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size) //中断接收
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,
                                          uint32_t Timeout); //中断接收发送
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size); //DMA发送
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size) //DMA接收
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                              uint16_t Size); //DMA发送接收
HAL_StatusTypeDef HAL_SPI_Abort(SPI_HandleTypeDef *hspi); //我不知道干啥的,知道的给我说,字面意思说是中止SPI。
HAL_StatusTypeDef HAL_SPI_Abort_IT(SPI_HandleTypeDef *hspi); // 我不知道干啥的
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi); //暂停DMA
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi); //停止DMA
void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi); //中断服务函数
HAL_SPI_StateTypeDef HAL_SPI_GetState(SPI_HandleTypeDef *hspi); //获取SPI状态函数
uint32_t HAL_SPI_GetError(SPI_HandleTypeDef *hspi); //获取SPI错误代码函数
__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi); //接收完成回调
__weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi); //发送完成回调
__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi); //错误回调

3、QSPI协议

QSPI是Queued SPI的简写,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即QSPI协议)。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。
STM32上加 QUADSPI。
1、间接模式:在这个模式下,所有的操作都是通过QSPI寄存器来执行的。这意味着数据的传输和接收都需要通过寄存器来进行中转。
2、状态轮询模式:在这种模式下,外部Flash的状态寄存器会被周期性地读取,当某些标志位(如擦除或烧写完成的标志位)置为1时,会产生中断,从而通知控制器进行相应的处理。
3、内存映射模式:在内存映射模式下,外部Flash被映射到微控制器的地址空间,系统将其视为内部存储器的一部分。这种方式可以让处理器直接访问Flash存储空间,就像访问内部RAM一样。

采用双闪存模式时,将同时访问两个 Quad-SPI Flash,吞吐量和容量均可提高二倍。

QSPI功能框图

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】-LMLPHP
1、BK1_nCS:片选输出(低电平有效),适用于 FLASH 1。如果 QSPI 始终在双闪存模式下工
作,则其也可用于 FLASH 2 从设备选择信号线。QSPI 通讯以 BK1_nCS 线置低电平为开始信号,以 BK1_nCS 线被拉高作为结束信号。
2、CLK:时钟输出,适用于两个存储器,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 QSPI 时钟频率最大为 fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
3、BK1_IO0:在双线 / 四线模式中为双向 IO,单线模式中为串行输出,适用于 FLASH 1。
4、BK1_IO1:在双线 / 四线模式中为双向 IO,单线模式中为串行输入
5、BK1_IO2:在四线模式中为双向 IO
6、BK1_IO3:在四线模式中为双向 IO

QSPI命令序列

QUADSPI 通过命令与 Flash 通信每条命令包括指令、地址、交替字节、空指令和数据这五个阶段任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。nCS 在每条指令开始前下降,在每条指令完成后再次上升。

QSPI命令序列时序

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】-LMLPHP四线模式读命令时序

QUADSPI 信号接口协议模式

1、单线SPI模式
2、双线SPI模式
3、四线SPI模式
以上模式在这有介绍,慢慢看。
4、SDR模式
5、DDR模式
6、双闪存模式

QSPI使用

QSPI HAL库 结构体

/**
  * @brief  QSPI Handle Structure definition
  */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
typedef struct __QSPI_HandleTypeDef
#else
typedef struct
#endif
{
  QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
  QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
  uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
  __IO uint32_t              TxXferSize;       /* QSPI Tx Transfer size              */
  __IO uint32_t              TxXferCount;      /* QSPI Tx Transfer Counter           */
  uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
  __IO uint32_t              RxXferSize;       /* QSPI Rx Transfer size              */
  __IO uint32_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
  MDMA_HandleTypeDef          *hmdma;            /* QSPI Rx/Tx MDMA Handle parameters   */
  __IO HAL_LockTypeDef       Lock;             /* Locking object                     */
  __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
  __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
  uint32_t                   Timeout;          /* Timeout for the QSPI memory access */
#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)
  void (* ErrorCallback)        (struct __QSPI_HandleTypeDef *hqspi);
  void (* AbortCpltCallback)    (struct __QSPI_HandleTypeDef *hqspi);
  void (* FifoThresholdCallback)(struct __QSPI_HandleTypeDef *hqspi);
  void (* CmdCpltCallback)      (struct __QSPI_HandleTypeDef *hqspi);
  void (* RxCpltCallback)       (struct __QSPI_HandleTypeDef *hqspi);
  void (* TxCpltCallback)       (struct __QSPI_HandleTypeDef *hqspi);
  void (* StatusMatchCallback)  (struct __QSPI_HandleTypeDef *hqspi);
  void (* TimeOutCallback)      (struct __QSPI_HandleTypeDef *hqspi);

  void (* MspInitCallback)      (struct __QSPI_HandleTypeDef *hqspi);
  void (* MspDeInitCallback)    (struct __QSPI_HandleTypeDef *hqspi);
#endif
}QSPI_HandleTypeDef;

其它功能结构体

/**
  * @brief  QSPI Command structure definition
  */
typedef struct
{
  uint32_t Instruction;        /* Specifies the Instruction to be sent
                                  This parameter can be a value (8-bit) between 0x00 and 0xFF */
  uint32_t Address;            /* Specifies the Address to be sent (Size from 1 to 4 bytes according AddressSize)
                                  This parameter can be a value (32-bits) between 0x0 and 0xFFFFFFFF */
  uint32_t AlternateBytes;     /* Specifies the Alternate Bytes to be sent (Size from 1 to 4 bytes according AlternateBytesSize)
                                  This parameter can be a value (32-bits) between 0x0 and 0xFFFFFFFF */
  uint32_t AddressSize;        /* Specifies the Address Size
                                  This parameter can be a value of @ref QSPI_AddressSize */
  uint32_t AlternateBytesSize; /* Specifies the Alternate Bytes Size
                                  This parameter can be a value of @ref QSPI_AlternateBytesSize */
  uint32_t DummyCycles;        /* Specifies the Number of Dummy Cycles.
                                  This parameter can be a number between 0 and 31 */
  uint32_t InstructionMode;    /* Specifies the Instruction Mode
                                  This parameter can be a value of @ref QSPI_InstructionMode */
  uint32_t AddressMode;        /* Specifies the Address Mode
                                  This parameter can be a value of @ref QSPI_AddressMode */
  uint32_t AlternateByteMode;  /* Specifies the Alternate Bytes Mode
                                  This parameter can be a value of @ref QSPI_AlternateBytesMode */
  uint32_t DataMode;           /* Specifies the Data Mode (used for dummy cycles and data phases)
                                  This parameter can be a value of @ref QSPI_DataMode */
  uint32_t NbData;             /* Specifies the number of data to transfer. (This is the number of bytes)
                                  This parameter can be any value between 0 and 0xFFFFFFFF (0 means undefined length
                                  until end of memory)*/
  uint32_t DdrMode;            /* Specifies the double data rate mode for address, alternate byte and data phase
                                  This parameter can be a value of @ref QSPI_DdrMode */
  uint32_t DdrHoldHalfCycle;   /* Specifies if the DDR hold is enabled. When enabled it delays the data
                                  output by one half of system clock in DDR mode.
                                  This parameter can be a value of @ref QSPI_DdrHoldHalfCycle */
  uint32_t SIOOMode;           /* Specifies the send instruction only once mode
                                  This parameter can be a value of @ref QSPI_SIOOMode */
}QSPI_CommandTypeDef;

/**
  * @brief  QSPI Auto Polling mode configuration structure definition
  */
typedef struct
{
  uint32_t Match;              /* Specifies the value to be compared with the masked status register to get a match.
                                  This parameter can be any value between 0 and 0xFFFFFFFF */
  uint32_t Mask;               /* Specifies the mask to be applied to the status bytes received.
                                  This parameter can be any value between 0 and 0xFFFFFFFF */
  uint32_t Interval;           /* Specifies the number of clock cycles between two read during automatic polling phases.
                                  This parameter can be any value between 0 and 0xFFFF */
  uint32_t StatusBytesSize;    /* Specifies the size of the status bytes received.
                                  This parameter can be any value between 1 and 4 */
  uint32_t MatchMode;          /* Specifies the method used for determining a match.
                                  This parameter can be a value of @ref QSPI_MatchMode */
  uint32_t AutomaticStop;      /* Specifies if automatic polling is stopped after a match.
                                  This parameter can be a value of @ref QSPI_AutomaticStop */
}QSPI_AutoPollingTypeDef;

/**
  * @brief  QSPI Memory Mapped mode configuration structure definition
  */
typedef struct
{
  uint32_t TimeOutPeriod;      /* Specifies the number of clock to wait when the FIFO is full before to release the chip select.
                                  This parameter can be any value between 0 and 0xFFFF */
  uint32_t TimeOutActivation;  /* Specifies if the timeout counter is enabled to release the chip select.
                                  This parameter can be a value of @ref QSPI_TimeOutActivation */
}QSPI_MemoryMappedTypeDef;
03-31 07:04