0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32F1的SPI模块协议介绍

冬至子 来源:ITRelief 作者:Sachefgh Xu 2023-07-24 15:32 次阅读

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

一、SPI协议介绍

1.硬件连接

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

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

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

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

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

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

图片

2.通信时序

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

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

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

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

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

图片

※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.相关寄存器

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

图片

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.标志位/状态位

图片

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,不要增加没必要的干扰)

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据寄存器
    +关注

    关注

    0

    文章

    31

    浏览量

    7555
  • SPI接口
    +关注

    关注

    0

    文章

    251

    浏览量

    33934
  • CPHA
    +关注

    关注

    0

    文章

    8

    浏览量

    9344
  • USART串口
    +关注

    关注

    0

    文章

    31

    浏览量

    6723
  • stm32f1
    +关注

    关注

    1

    文章

    53

    浏览量

    12036
收藏 人收藏

    评论

    相关推荐

    STM32F1的I2C模块协议简介

    I2C是一种多主从的串行通讯协议STM32F1的I2C模块支持标速(最高100kHz)和高速(最高400kHz)两种工作模式。
    发表于 07-25 14:49 2424次阅读
    <b class='flag-5'>STM32F1</b>的I2C<b class='flag-5'>模块</b><b class='flag-5'>协议</b>简介

    STM32F1单片机存储外扩选SPI SRAM

    通道、2条SPI通道、3条USART通道、8kB内部SRAM、128kB内部Flash和JTAG与SWD调试支持。然而 这款MCU的SRAM资源非常有限,从ISSI这款型号为STM32F1的SRAM容量
    发表于 12-21 11:35

    请问有STM32F1 SPI电容屏的接法和参考源码吗?

    请问谁有STM32F1 SPI电容屏的接法,以及参考源码?
    发表于 12-18 08:43

    STM32F1 PWM介绍

    文章目录实验要求一、用STM32F103输出一路PWM波形(1)PWM简介(2)STM32F1 PWM介绍(3)编程实现(4)计算拟合周期(5)最后运行结果二、用
    发表于 08-09 08:48

    基于STM32F1的G-code解释器移植

    、项目总览基本介绍:本项目基于STM32F1,移植了著名的GRBL——G-code解释器,做的一款写字机(或激光雕刻),使用LVGL的GUI库设计屏幕交互,扩展了SD卡脱机打印功能,板载ESP8266-E12SWIFI模块(由于
    发表于 08-11 07:43

    STM32F1系列的MCU

    的MCU,然后跟平台之间对接协议很多,代码量较大,所以换到了STM32F1系列的MCU。在STC15MCU上面通过串口接收数据只能老老实实用接收中断来做,每接收一个字节都需要判断帧头帧尾,一帧结束再处...
    发表于 08-13 07:50

    STM32F1的USART介绍

    声明:所有资源均来自于普中STM32F103开发板相关资料,这是自己购买的一款开发板,如果原作者认为侵权,请联系我以便及时处理。STM32F1的USART介绍USART即通用同步异步收发器,它能
    发表于 08-17 06:27

    介绍STM32F1工程建立的步骤

    怎样去开发一种STM32F1单片机呢?STM32F1工程建立都有哪些步骤?
    发表于 11-25 06:56

    STM32F1 DSP官方库的安装

    STM32F1 DSP官方库的安装1.下载完毕后进行安装,这里我甩出一个下载链接。2.接收协议。3.安装路径,我这里喜欢选择D盘。4.安装完毕后,有一些需要的库和参考Demo文件。5.
    发表于 11-30 07:57

    STM32F1外部中断简介

    开启了学习机器学习,本文就介绍了机器学习的基础内容。提示:以下是本篇文章正文内容,下面案例可供参考一、 STM32F1 外部中断简介我们首先讲解 STM32F1 IO 口中断的一些基础概念。S
    发表于 12-09 07:26

    如何去实现基于STM32F1开发板和NRF24L01模块SPI单通信呢

    如何去实现基于STM32F1开发板和NRF24L01模块SPI单通信呢?其相关代码该怎样去实现呢?
    发表于 12-16 06:14

    如何利用STM32F1SPI来实现对外部FLASH的读写呢

    如何利用STM32F1SPI来实现对外部FLASH的读写呢?并将结果显示在TFTLCD模块上?
    发表于 12-17 07:05

    STM32F1SPI功能有何作用

    SPI的主要特点有哪些?STM32F1SPI功能有何作用?
    发表于 12-17 06:43

    如何使用STM32F1SPI和FLASH进行通信来实现数据的读写操作呢

    如何使用STM32F1SPI和FLASH进行通信来实现数据的读写操作呢?
    发表于 12-17 06:26

    stm32f1的io口作为输出的使用方法

    前言stm32 io口简介硬件设计软件设计proteus仿真前言在流水灯试验中,介绍stm32f1的io口作为输出的使用方法。在本次试验中,我将介绍io口作为输出的使用方法。按键检测
    发表于 01-12 06:32