STM32F1的SPI模块协议介绍

电子说

1.2w人已加入

描述

SPI是是一种高速的,全双工,同步的总线通信方式。STM32F1低中容量设备的SPI模块支持主从两种模式。

一、SPI协议介绍

1.硬件连接

SPI使用三条数据总线和一条片选线: MOSI、MISO、SCK、NSS(CS)

MOSI(SDO):主设备输出/从设备输入。用于将数据从主机输出到从机。

MISO(SDI):主设备输入/从设备输出。数据经此由从机至主机,主机接收数据。

SCK:时钟信号线,用于通讯同步。时钟信号由主机提供

NSS:片选信号线。由主机通过此线使能从机。在一主多从的通讯模式下,只能同时有一个从机被使能。

SPI器件间的连接很简单,如图,只要名字相同线相连即可,主也可以比较方便地反转。

stm32f1

2.通信时序

SPI传输模式的精髓在时钟极性(CPOL)和时钟相位(CPHA)

CPOL控制空闲状态下时钟总线SCK的电平:

CPOL=0(LOW);时钟线空闲为低电平

CPOL=1(HIGH);时钟线空闲为高

CPHA控制采样位置和信号跳变位置:

stm32f1

※SPI传输从高位(MSB)开始还是低位(LSB)开始可以由用户设置

二、STM32F1的SPI模块

1.在CubeMX中进行配置

数据位可选择8或16

CPHA可选择从第一个数据沿开始(CPHA=0)或从第二个数据沿开始(CPHA=1)

可选择高位先发送或是低位先发送

可选择是否使用NSS以及NSS功能(输入、输出用)。用户也可以用普通IO的中断输入/输出功能模拟NSS,这种方法相对更加灵活。

按照个人开发经验,SPI一般配置为双线双向全双工情况比较多。

※STM32的 NSS引脚说明和工作极其复杂,建议开发者禁用硬件NSS,自行定义普通GPIO实现片选功能。(NSS脚禁用后可以进行GPIO配置当作普通IO控制实现CS功能)

2.相关寄存器

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

stm32f1

API:

1.初始化结构体LL_SPI_InitTypeDef

typedef struct
{
  uint32_t TransferDirection;/*
  数据线配置;通过调用LL_SPI_SetTransferDirection()实现;
  @ref:       LL_SPI_FULL_DUPLEX //全双工,双线双向
              LL_SPI_SIMPLEX_RX  //双线双向模式下禁止输出,仅能输入
              LL_SPI_HALF_DUPLEX_RX //单线,仅能接收
              LL_SPI_HALF_DUPLEX_TX //单线,仅能发送
              ※单线模式下,工作于Master时使用MOSI脚;Slave时为MISO脚
  */
  uint32_t Mode;/*
  设置主从模式,通过LL_SPI_SetMode()实现;
  @ref:       LL_SPI_MODE_MASTER //主模式,配置时若NSS由软件管理会将电平置高
              LL_SPI_MODE_SLAVE
  */
  uint32_t DataWidth;/*
  设置数据长度;通过LL_SPI_SetDataWidth()实现;
  @ref:       LL_SPI_DATAWIDTH_8BIT  //8位
              LL_SPI_DATAWIDTH_16BIT  //16位
  */
  uint32_t ClockPolarity;/*
  设置时钟极性(CPOL),通过LL_SPI_SetClockPolarity()实现
  @ref:       LL_SPI_POLARITY_LOW //低电平(CPOL=0)
              LL_SPI_POLARITY_HIGH  //高电平(CPOL=1)
  */
  uint32_t ClockPhase;/*
  设置时钟相位,通过LL_SPI_SetClockPhase()实现
  @ref:       LL_SPI_PHASE_1EDGE //CPHA =0
              LL_SPI_PHASE_2EDGE //CPHA=1
  */
  uint32_t NSS;/*
  配置NSS(CS),通过LL_SPI_SetNSSMode()实现;
  @ref:       LL_SPI_NSS_SOFT //通过软件管理NSS;※此时NSS引脚无法进行I/O操作控制
                 //CubeMx配置为Disable时配置为此模式(相当于禁用了NSS)
                 //此时可以通过操作SPI_CR1- >SSI位控制该位电平;LL库未提供函数;
              LL_SPI_NSS_HARD_INPUT //说不清除,手册和库函数说明冲突,建议不用
              LL_SPI_NSS_HARD_OUTPUT//同样,不建议配置
              //鉴于片选复杂性,推荐开发者直接通过GPIO直接模拟NSS(CS)功能,可用原NSS
  */
  uint32_t BaudRate;/*
  配置波特率分频,通过LL_SPI_SetBaudRatePrescaler()实现;
  @ref:        LL_SPI_BAUDRATEPRESCALER_DIVx //x2^n,max=128
*/
  uint32_t BitOrder;/*
  配置发送位顺序,通过LL_SPI_SetTransferBitOrder()实现;
  @ref:      LL_SPI_LSB_FIRST  //低位先
             LL_SPI_MSB_FIRST  //高位先
  */
  uint32_t CRCCalculation;/*!< Specifies if the CRC calculation is enabled or not.
  This parameter can be a value of @ref SPI_LL_EC_CRC_CALCULATION.
This feature can be modified afterwards using unitary functions @ref LL_SPI_EnableCRC() and @ref LL_SPI_DisableCRC().*/

  uint32_t CRCPoly;/*!< Specifies the polynomial used for the CRC calculation.
This parameter must be a number between Min_Data = 0x00 and Max_Data = 0xFFFF.
 This feature can be modified afterwards using unitary function @ref LL_SPI_SetCRCPolynomial().*/
} LL_SPI_InitTypeDef;

2.初始化函数

ErrorStatus LL_SPI_Init(SPI_TypeDef *SPIx, LL_SPI_InitTypeDef *SPI_InitStruct);/*
初始化SPI;
*/
void LL_SPI_StructInit(LL_SPI_InitTypeDef *SPI_InitStruct)/*
初始化SPI配置结构体
*/
ErrorStatus LL_SPI_DeInit(SPI_TypeDef *SPIx)/*
初始化SPI模块
*/

3.开启/关闭模块

__STATIC_INLINE void LL_SPI_Enable(SPI_TypeDef *SPIx);/*
开启SPI模块
*/
__STATIC_INLINE void LL_SPI_Disable(SPI_TypeDef *SPIx);/*
关闭SPI模块
*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabled(SPI_TypeDef *SPIx);/*
检测开启状态
*/

※与UART不同,目前版本CubeMX自动生成代码不会开启SPI,需用户手动开启

关闭SPI需要在传输完成后

4.标志位/状态位

stm32f1

MODF:主模式失效错误标志。在NSS引脚硬件模式管理下,主设备的NSS脚被拉低时;或者在NSS引脚软件模式管理下,SSI位被置0时被置位。同时SPI模块被关闭。 在使用LL库时若不使用NSS功能,则不会出现置位情况

※RXNE:接收缓冲非空。与USART类似,当※接收数据寄存器完全完成一次数据接收时,该位被置位。※对读取数据寄存器RDR的读取操作可以硬件清零该位。

※TXE:发送缓冲空。当发送数据寄存器数据被送出时,该位被置位。对发送数据寄存器TDR的写入操作可以硬件清零该位。

BSY:忙标志。SPI在通讯时该位为1。该位完全由硬件控制。在主模式的双向接收模式下 (MSTR=1、BDM=1并且BDOE=0),在接收期间BSY标志保持为低。不要使用BSY标志处理每一个数据项的发送和接收,最好使用TXE和RXNE标志。

OVR:溢出错误。接收数据时,当发送端设备已经发送了数据字节,而STM32还没有清除前一个数据字节产生的RXNE时,即为溢出错误。

当溢出时,读SPI_DR寄存器返回的是之前未读的数据,所有随后传送的数据都被丢弃。

※与USART不同,SPI模块TXE与RXNE位是只读的,其值由硬件管理。

__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_BSY(SPI_TypeDef *SPIx);/*
检测BSY是否置位,该位无法软件控制
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_OVR(SPI_TypeDef *SPIx);/*
检测OVR是否置位(发生过载错误)
*/
__STATIC_INLINE void LL_SPI_ClearFlag_OVR(SPI_TypeDef *SPIx);/*
置位OVR
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_TXE(SPI_TypeDef *SPIx);/*
检测TXE是否置位
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_RXNE(SPI_TypeDef *SPIx);/*
检测RXNE是否置位
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_MODF(SPI_TypeDef *SPIx);
__STATIC_INLINE void LL_SPI_ClearFlag_MODF(SPI_TypeDef *SPIx);

5.中断控制

__STATIC_INLINE void LL_SPI_EnableIT_ERR(SPI_TypeDef *SPIx);/*
使能ERR错误中断*/
__STATIC_INLINE void LL_SPI_DisableIT_ERR(SPI_TypeDef *SPIx);/*
禁用ERR错误中断*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabledIT_ERR(SPI_TypeDef *SPIx);/*
检测是否开启ERR中断*/

__STATIC_INLINE void LL_SPI_EnableIT_RXNE(SPI_TypeDef *SPIx);/*
使能RXNE接收缓冲非空中断*/
__STATIC_INLINE void LL_SPI_DisableIT_RXNE(SPI_TypeDef *SPIx);/*
禁用RXNE接收缓冲非空中断*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabledIT_RXNE(SPI_TypeDef *SPIx)/*
检测是否开启RXNE接收缓冲非空中断*/

__STATIC_INLINE void LL_SPI_EnableIT_TXE(SPI_TypeDef *SPIx);/*
使能TXE发送缓冲空中断*/
__STATIC_INLINE void LL_SPI_DisableIT_TXE(SPI_TypeDef *SPIx);/*
禁用TXE发送缓冲空中断*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabledIT_TXE(SPI_TypeDef *SPIx)/*
检测是否开启TXE发送缓冲空中断*/

6.SPI 收/发函数

__STATIC_INLINE uint8_t LL_SPI_ReceiveData8(SPI_TypeDef *SPIx);/*
从接收寄存器(缓冲区)DR中读取8位数据;
*/
__STATIC_INLINE uint16_t LL_SPI_ReceiveData16(SPI_TypeDef *SPIx);/*
从接收寄存器(缓冲区)DR中读取16位数据;
*/

__STATIC_INLINE void LL_SPI_TransmitData8(SPI_TypeDef *SPIx, uint8_t TxData);/*
向发送寄存器(缓冲区)DR中写入8位数据
*/
__STATIC_INLINE void LL_SPI_TransmitData16(SPI_TypeDef *SPIx, uint16_t TxData);/*
向发送寄存器(缓冲区)DR中写入16位数据
*/

SPI模块DMA的使用

相关函数:

待实验

__STATIC_INLINE void LL_USART_EnableDMAReq_RX(USART_TypeDef *SPIx);/*
使能接收DMA,启用后DR有数据时将允许发送DMA请求;具体见示例用法*/
__STATIC_INLINE void LL_USART_DisableDMAReq_RX(USART_TypeDef *SPIx);/*
禁用接收DMA*/
__STATIC_INLINE uint32_t LL_USART_IsEnabledDMAReq_RX(USART_TypeDef *SPIx);/*
检测是否使能接收DMA*/

__STATIC_INLINE void LL_USART_EnableDMAReq_TX(USART_TypeDef *SPIx);/*
使能发送DMA*/
__STATIC_INLINE void LL_USART_DisableDMAReq_TX(USART_TypeDef *SPIx);/*
禁用发送DMA*/
__STATIC_INLINE uint32_t LL_USART_IsEnabledDMAReq_TX(USART_TypeDef *SPIx);/*
检测是否使能发送DMA*/
/**************************************************/
__STATIC_INLINE uint32_t LL_USART_DMA_GetRegAddr(USART_TypeDef *SPIx);/*
返回SPI模块数据寄存器DR地址;无论是否启用DMA均可用
*/

发送时,在每次TXE被设置为’1’时发出DMA请求,此时软件控制DMA写数据至SPI_DR寄存器,TXE标志因此而被清除。

接收时,在每次RXNE被设置为’1’时发出DMA请求,在开启情况下DMA控制器从SPI_DR寄存器读出数据,RXNE标志因此而被清除。

LL的DMA使用与UART相似,可以参考之前的文章。

SPI在双向全双工传输数据的时候,每发出一字节数据的同时也会接收一字节数据,因此在作为主机接收的时候,应当考虑 如何处理接收到的无用数据。否则会出现OVR。

另外,由于在双向模式下配置为主机时,只有当SPI在写数据时时钟信号才能产生。处于master工作模式下,SPI的时钟只有在往DR寄存器里面写数据的时候才会产生,读是不会产生的。所以要读取slave shift out的数据,master必须先发一个“DUMMY”数据以产生时钟。

建议配置STM32为双向主机、从机; 配置为主机接收前读取一次DR,再发送DUMMY(建议发0x00或0xFF,不要增加没必要的干扰)

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分