1、SPI协议
简介
SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface),即串行外围设备接口,是
一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率
较高的场合。
SPI 物理层
SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为 SS。
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 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。
时序图
NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。
通讯的起始和停止信号
在图中的标号处**,NSS 信号线由高变低,是 SPI 通讯的起始信号**。
NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选了,开始准备与主机通讯。在图中的标号处,NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。
数据有效性
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 SPI系统架构
STM32 的 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一样。
QSPI通常使用6个信号线连接Flash,包括四个数据线(BK1_IO0~BK1_IO3)、一个时钟输出(CLK)和一个片选输出(低电平有效,BK1_nCS)。这些信号线的作用是实现与SPI Flash存储介质的通信。
采用双闪存模式时,将同时访问两个 Quad-SPI Flash,吞吐量和容量均可提高二倍。
QSPI功能框图
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命令序列时序
四线模式读命令时序
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;