• 【STM32F429开发板用户手册】第31章 STM32F429的SPI总线基础知识和HAL库API


    最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255

    第31章       STM32F429的SPI总线基础知识和HAL库API

    本章节为大家讲解SPI(Serial peripheral interface)总线的基础知识和对应的HAL库API。

    31.1 初学者重要提示

    31.2 SPI总线基础知识

    31.3 SPI总线的HAL库用法

    31.4 源文件stm32f4xx_hal_spi.c

    31.5 总结

    31.1 初学者重要提示

    1.   STM32H7的SPI支持4到32bit数据传输,而STM32F1和F4系列仅支持8bit或者16bit。
    2.   STM3F429的主频168MHz时,SPI1、4、5、6最高通信时钟是42MHz,而SPI2和SPI3是21MHz。
    3.   SPI总线的片选引脚SS在单一的主从器件配置下是可选的,一般情况下可以不使用。
    4.   搜集了几篇质量比较高的SPI总线介绍帖:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96788

    31.2 SPI总线基础知识

    31.2.1 SPI总线的硬件框图

    认识一个外设,最好的方式就是看它的框图,方便我们快速的了解SPI的基本功能,然后再看手册了解细节。

     

    通过这个框图,我们可以得到如下信息:

    •   SCK(CK),Serial Clock

    此引脚在主机模式下用于时钟输出,从机模式下用于时钟输入。

    •   MISO(SDI),Master In / Slave Out data

    此引脚在从机模式下用于发送数据,主机模式下接收数据。

    •   MOSI(SDO), Master Out / Slave In data

    此引脚在从机模式下用于数据接收,主机模式下发送数据。

    •   SS(WS), Slave select pin

    根据SPI和SS设置,此引脚可用于:

    (1)  选择从器件进行通信。

    (2)  允许多主模式(可以禁止NSS引脚输出)。

    31.2.2 SPI接口的区别和时钟源(SPI1到SPI6)

    这个知识点在初学的时候容易忽视,所以我们这里整理下。

    •   SPI1到SPI6的所在的总线

    SPI1,SPI4,SPI5,SPI6在APB2总线,SPI2,SPI3在APB1总线。SPI的最高时钟由这些总线决定的。

    •   SPI1到SPI6的支持的最高时钟

    STM32F429主频在168MHz下,SPI1,SPI4,SPI5,SPI6的最高时钟是84MHz,而SPI2和SPI3是42MHz。这里特别注意一点,SPI工作时最少选择二分频,也就是说SPI1,4,5,6实际通信时钟是42MHz,而SPI2,3是21MHz。

    31.2.3 SPI总线全双工,单工和半双工通信

    片选信号SS在单一的主从器件配置下是可选的,一般情况下可以不使用。

      全双工通信(F4只有一个移位寄存器)

    全双工就是主从器件之间同时互传数据,SPI总线的全双工模式接线方式如下:

     

    关于这个接线图要认识到以下几点:

    •   注意接线方式,对于主器件来说MISO引脚就是输入端,从器件的MISO是输出端,即Master In / Slave Out data。MOSI也是同样道理。
    •   每个时钟信号SCK的作用了,主器件的MISO引脚接收1个bit数据,MOSI引脚输出1个bit数据。
    •   这种单一的主从接线模式下,SS引脚可以不使用。

      半双工通信

    半双工就是同一个时刻只能为一个方向传输数据,SPI总线的半工模式接线方式如下:

     

    关于这个接线图要认识到以下几点:

    •   更改通信方式时,要先禁止SPI。
    •   主器件的MISO和从器件的MISO不使用,可以继续用作标准GPIO。
    •   1KΩ的接线电阻很有必要,因为当主器件和从器件的通信方向不是同步变化时,容易出现其中一个输出低电平,另一个输出高电平,造成短路。
    •   这种单一的主从接线模式下,SS引脚可以不使用

      单工模式

    单工就是只有一种通信方向,即发送或者接收,SPI总线的单工模式接线方式如下:

     

    关于这个接线图要认识到以下几点:

    •   未用到的MOSI或者MISO可以用作标准GPIO。
    •   这种单一的主从接线模式下,SS引脚可以不使用。

    31.2.4 SPI总线星型拓扑

    SPI总线星型拓扑用到的地方比较多,V6开发板就是用的星型拓扑外接多种SPI器件:

     

    关于这个接线图,有以下几点需要大家了解:

    •   主器件的SS引脚不使用,使用通用GPIO控制。为每个器件配一个SS引脚,方便单独片选控制。
    •   从器件的MISO引脚要配置为复用开漏输出(很多外部芯片在未片选时,数据引脚是呈现高阻态)。

    31.2.5 SPI总线通信格式

    SPI总线主要有四种通信格式,由CPOL时钟极性和CPHA时钟相位控制:

     

    四种通信格式如下:

    •   当CPOL = 1, CPHA = 1时

    SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。

    •   当CPOL = 0, CPHA = 1时

    SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。

    •   当CPOL = 1, CPHA = 0时

    SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。

    •   当CPOL = 0, CPHA = 0时

    SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。

    31.3 SPI总线的HAL库用法

    31.3.1 SPI总线结构体SPI_TypeDef

    SPI总线相关的寄存器是通过HAL库中的结构体SPI_TypeDef定义的,在stm32f429xx.h中可以找到这个类型定义:

    typedef struct
    {
      __IO uint32_t CR1;        /*!< SPI control register 1 (not used in I2S mode),      Address offset: 0x00 */
      __IO uint32_t CR2;        /*!< SPI control register 2,                             Address offset: 0x04 */
      __IO uint32_t SR;         /*!< SPI status register,                                Address offset: 0x08 */
      __IO uint32_t DR;         /*!< SPI data register,                                  Address offset: 0x0C */
      __IO uint32_t CRCPR;      /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */
      __IO uint32_t RXCRCR;     /*!< SPI RX CRC register (not used in I2S mode),         Address offset: 0x14 */
      __IO uint32_t TXCRCR;     /*!< SPI TX CRC register (not used in I2S mode),         Address offset: 0x18 */
      __IO uint32_t I2SCFGR;    /*!< SPI_I2S configuration register,                     Address offset: 0x1C */
      __IO uint32_t I2SPR;      /*!< SPI_I2S prescaler register,                         Address offset: 0x20 */
    } SPI_TypeDef;

    这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。

    __IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m4.h 文件定义了这个宏:

    #define     __O     volatile             /*!< Defines 'write only' permissions */
    #define     __IO    volatile             /*!< Defines 'read / write' permissions */

    下面我们看下SPI的定义,在stm32f429xx.h文件。

    #define PERIPH_BASE           0x40000000UL
    #define APB1PERIPH_BASE       PERIPH_BASE
    #define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000UL)
    
    #define SPI1_BASE             (APB2PERIPH_BASE + 0x3000UL)
    #define SPI2_BASE             (APB1PERIPH_BASE + 0x3800UL)
    #define SPI3_BASE             (APB1PERIPH_BASE + 0x3C00UL)
    #define SPI4_BASE             (APB2PERIPH_BASE + 0x3400UL)
    #define SPI5_BASE             (APB2PERIPH_BASE + 0x5000UL)
    #define SPI6_BASE             (APB2PERIPH_BASE + 0x5400UL)
    
    #define SPI1                ((SPI_TypeDef *) SPI1_BASE)
    #define SPI2                ((SPI_TypeDef *) SPI2_BASE)
    #define SPI3                ((SPI_TypeDef *) SPI3_BASE)
    #define SPI4                ((SPI_TypeDef *) SPI4_BASE)
    #define SPI5                ((SPI_TypeDef *) SPI5_BASE)
    #define SPI6                ((SPI_TypeDef *) SPI6_BASE) <----- 展开这个宏,(FLASH_TypeDef *)0x40015400

    我们访问SPI的CR1寄存器可以采用这种形式:SPI->CR1 = 0。

    31.3.2 SPI总线初始化结构体SPI_InitTypeDef

    下面是SPI总线的初始化结构体,用到的地方比较多:

    typedef struct
    {
      uint32_t Mode;               
      uint32_t Direction;         
      uint32_t DataSize;           
      uint32_t CLKPolarity;        
      uint32_t CLKPhase;           
      uint32_t NSS;                
      uint32_t BaudRatePrescaler;   
      uint32_t FirstBit;           
      uint32_t TIMode;              
      uint32_t CRCCalculation;      
      uint32_t CRCPolynomial;       
    } SPI_InitTypeDef;

    下面将结构体成员逐一做个说明:

    •   Mode

    用于设置工作在主机模式还是从机模式。

    #define SPI_MODE_SLAVE                  (0x00000000U)
    #define SPI_MODE_MASTER                 (SPI_CR1_MSTR | SPI_CR1_SSI)
    •   Direction

    用于设置SPI工作在全双工,单工,还是半双工模式。

    #define SPI_DIRECTION_2LINES            (0x00000000U)
    #define SPI_DIRECTION_2LINES_RXONLY     SPI_CR1_RXONLY
    #define SPI_DIRECTION_1LINE             SPI_CR1_BIDIMODE
    •   DataSize

    用于设置SPI总线数据收发的位宽,支持8bit或者16bit。

    #define SPI_DATASIZE_8BIT               (0x00000000U)
    #define SPI_DATASIZE_16BIT              SPI_CR1_DFF
    •   CLKPolarity

    用于设置空闲状态时,CLK是高电平还是低电平。

    #define SPI_POLARITY_LOW                (0x00000000U)
    #define SPI_POLARITY_HIGH               SPI_CR1_CPOL
    •   NSS

    用于设置NSS信号由硬件NSS引脚管理或者软件SSI位管理。

    #define SPI_NSS_SOFT                    SPI_CR1_SSM
    #define SPI_NSS_HARD_INPUT              (0x00000000U)
    #define SPI_NSS_HARD_OUTPUT             (SPI_CR2_SSOE << 16U)
    •   BaudRatePrescaler

    用于设置SPI时钟分频,仅SPI工作在主控模式下起作用,对SPI从机模式不起作用。

    #define SPI_BAUDRATEPRESCALER_2         (0x00000000U)
    #define SPI_BAUDRATEPRESCALER_4         (SPI_CR1_BR_0)
    #define SPI_BAUDRATEPRESCALER_8         (SPI_CR1_BR_1)
    #define SPI_BAUDRATEPRESCALER_16        (SPI_CR1_BR_1 | SPI_CR1_BR_0)
    #define SPI_BAUDRATEPRESCALER_32        (SPI_CR1_BR_2)
    #define SPI_BAUDRATEPRESCALER_64        (SPI_CR1_BR_2 | SPI_CR1_BR_0)
    #define SPI_BAUDRATEPRESCALER_128       (SPI_CR1_BR_2 | SPI_CR1_BR_1)
    #define SPI_BAUDRATEPRESCALER_256       (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
    •   FirstBit

    用于设置数据传输从最高bit开始还是从最低bit开始。

    #define SPI_FIRSTBIT_MSB                (0x00000000U)
    #define SPI_FIRSTBIT_LSB                SPI_CR1_LSBFIRST
    •   TIMode

    用于设置是否使能SPI总线的TI模式。

    #define SPI_TIMODE_DISABLE              (0x00000000U)
    #define SPI_TIMODE_ENABLE               SPI_CR2_FRF
    •   CRCCalculation

    用于设置是否使能CRC计算。

    #define SPI_CRCCALCULATION_DISABLE      (0x00000000U)
    #define SPI_CRCCALCULATION_ENABLE       SPI_CR1_CRCEN
    •   CRCPolynomial

    用于设置CRC计算使用的多项式,必须是奇数,范围0到65535。

    31.3.3 SPI总线句柄结构体SPI_HandleTypeDef

    下面是SPI总线的初始化结构体,用到的地方比较多:

    typedef struct __SPI_HandleTypeDef
    {
      SPI_TypeDef                *Instance;     
      SPI_InitTypeDef            Init;           
      uint8_t                    *pTxBuffPtr;   
      uint16_t                   TxXferSize;     
      __IO uint16_t              TxXferCount;    
      uint8_t                    *pRxBuffPtr;   
      uint16_t                   RxXferSize;     
      __IO uint16_t              RxXferCount;    
      void (*RxISR)(struct __SPI_HandleTypeDef *hspi);  
      void (*TxISR)(struct __SPI_HandleTypeDef *hspi);  
      DMA_HandleTypeDef          *hdmatx;       
      DMA_HandleTypeDef          *hdmarx;       
      HAL_LockTypeDef            Lock;          
      __IO HAL_SPI_StateTypeDef  State;         
      __IO uint32_t              ErrorCode;     
    #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
      void (* TxCpltCallback)(struct __SPI_HandleTypeDef *hspi);            
      void (* RxCpltCallback)(struct __SPI_HandleTypeDef *hspi);            
      void (* TxRxCpltCallback)(struct __SPI_HandleTypeDef *hspi);          
      void (* TxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);         
      void (* RxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);        
      void (* TxRxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi);      
      void (* ErrorCallback)(struct __SPI_HandleTypeDef *hspi);             
      void (* AbortCpltCallback)(struct __SPI_HandleTypeDef *hspi);         
      void (* MspInitCallback)(struct __SPI_HandleTypeDef *hspi);           
      void (* MspDeInitCallback)(struct __SPI_HandleTypeDef *hspi);          
    #endif 
    } SPI_HandleTypeDef;

    注意事项:

    条件编译USE_HAL_SPI_REGISTER_CALLBACKS用来设置使用自定义回调还是使用默认回调,此定义一般放在stm32f4xx_hal_conf.h文件里面设置:

      #define   USE_HAL_SPI_REGISTER_CALLBACKS   1

    通过函数HAL_SPI_RegisterCallback注册回调,取消注册使用函数HAL_SPI_UnRegisterCallback。

    这里重点介绍下面几个参数,其它参数主要是HAL库内部使用和自定义回调函数。

    •   SPI_TypeDef   *Instance

    这个参数是寄存器的例化,方便操作寄存器,比如使能SPI1。

    SET_BIT(SPI1 ->CR1,  SPI_CR1_SPE)。

    •   SPI_InitTypeDef  Init

    这个参数是用户接触最多的,在本章节3.2小节已经进行了详细说明。

    •   DMA_HandleTypeDef          *hdmatx               
    •   DMA_HandleTypeDef          *hdmarx

    用于SPI句柄关联DMA句柄,方便操作调用。

    31.4 SPI总线源文件stm32f4xx_hal_spi.c

    此文件涉及到的函数较多,这里把几个常用的函数做个说明:

    •   HAL_SPI_Init
    •   HAL_SPI_DeInit
    •   HAL_SPI_TransmitReceive
    •   HAL_SPI_TransmitReceive_IT
    •   HAL_SPI_TransmitReceive_DMA

    31.4.1 函数HAL_SPI_Init

    函数原型:

    HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
    {
      /* 检测句柄是否有效 */
      if (hspi == NULL)
      {
        return HAL_ERROR;
      }
    
      /* 检查参数 */
      assert_param(IS_SPI_ALL_INSTANCE(hspi->Instance));
      assert_param(IS_SPI_MODE(hspi->Init.Mode));
      assert_param(IS_SPI_DIRECTION(hspi->Init.Direction));
      assert_param(IS_SPI_DATASIZE(hspi->Init.DataSize));
      assert_param(IS_SPI_NSS(hspi->Init.NSS));
      assert_param(IS_SPI_BAUDRATE_PRESCALER(hspi->Init.BaudRatePrescaler));
      assert_param(IS_SPI_FIRST_BIT(hspi->Init.FirstBit));
      assert_param(IS_SPI_TIMODE(hspi->Init.TIMode));
      if (hspi->Init.TIMode == SPI_TIMODE_DISABLE)
      {
        assert_param(IS_SPI_CPOL(hspi->Init.CLKPolarity));
        assert_param(IS_SPI_CPHA(hspi->Init.CLKPhase));
      }
    #if (USE_SPI_CRC != 0U)
      assert_param(IS_SPI_CRC_CALCULATION(hspi->Init.CRCCalculation));
      if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
      {
        assert_param(IS_SPI_CRC_POLYNOMIAL(hspi->Init.CRCPolynomial));
      }
    #else
      hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    #endif 
    
      if (hspi->State == HAL_SPI_STATE_RESET)
      {
        /* 解锁 */
        hspi->Lock = HAL_UNLOCKED;
    
    #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
        /* 自定义回调函数 */
        hspi->TxCpltCallback       = HAL_SPI_TxCpltCallback;       /* Legacy weak TxCpltCallback       */
        hspi->RxCpltCallback       = HAL_SPI_RxCpltCallback;       /* Legacy weak RxCpltCallback       */
        hspi->TxRxCpltCallback     = HAL_SPI_TxRxCpltCallback;     /* Legacy weak TxRxCpltCallback     */
        hspi->TxHalfCpltCallback   = HAL_SPI_TxHalfCpltCallback;   /* Legacy weak TxHalfCpltCallback   */
        hspi->RxHalfCpltCallback   = HAL_SPI_RxHalfCpltCallback;   /* Legacy weak RxHalfCpltCallback   */
        hspi->TxRxHalfCpltCallback = HAL_SPI_TxRxHalfCpltCallback; /* Legacy weak TxRxHalfCpltCallback */
        hspi->ErrorCallback        = HAL_SPI_ErrorCallback;        /* Legacy weak ErrorCallback        */
        hspi->AbortCpltCallback    = HAL_SPI_AbortCpltCallback;    /* Legacy weak AbortCpltCallback    */
    
        if (hspi->MspInitCallback == NULL)
        {
          hspi->MspInitCallback = HAL_SPI_MspInit; /* Legacy weak MspInit  */
        }
    
        /* 初始化底层硬件: GPIO, CLOCK, NVIC... */
        hspi->MspInitCallback(hspi);
    #else
        /* 初始化底层硬件: GPIO, CLOCK, NVIC... */
        HAL_SPI_MspInit(hspi);
    #endif 
      }
    
      hspi->State = HAL_SPI_STATE_BUSY;
    
      /* 关闭SPI外设 */
      __HAL_SPI_DISABLE(hspi);
    
      /*----------------------- SPIx CR1 & CR2 配置 ---------------------*/
      /* 配置的各种SPI参数 */
      WRITE_REG(hspi->Instance->CR1, (hspi->Init.Mode | hspi->Init.Direction | hspi->Init.DataSize |
                                      hspi->Init.CLKPolarity | hspi->Init.CLKPhase | (hspi->Init.NSS & SPI_CR1_SSM) |hspi->Init.BaudRatePrescaler | hspi->Init.FirstBit  | hspi->Init.CRCCalculation));
    
      /* 配置NSS和TI模式位 */
      WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode));
    
    #if (USE_SPI_CRC != 0U)
      /*---------------------------- SPIx CRCPOLY 配置  ------------------*/
      /* 配置 : CRC 多项式 */
      if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
      {
        WRITE_REG(hspi->Instance->CRCPR, hspi->Init.CRCPolynomial);
      }
    #endif 
    
    #if defined(SPI_I2SCFGR_I2SMOD)
        CLEAR_BIT(hspi->Instance->I2SCFGR, SPI_I2SCFGR_I2SMOD);
    #endif 
    
      hspi->ErrorCode = HAL_SPI_ERROR_NONE;
      hspi->State     = HAL_SPI_STATE_READY;
    
      return HAL_OK;
    }

    函数描述:

    此函数用于初始化SPI。

    函数参数:

    •   第1个参数是SPI_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    注意事项:

    1. 函数HAL_SPI_MspInit用于初始化SPI的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
    2. 如果形参hspi的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量SPI_HandleTypeDef SpiHandle。

    对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_SPI_STATE_RESET  = 0x00U。

    解决办法有三

    方法1:用户自己初始串口和涉及到的GPIO等。

    方法2:定义SPI_HandleTypeDef SpiHandle为全局变量。

    方法3:下面的方法

    if(HAL_SPI_DeInit(&SpiHandle) != HAL_OK)
    {
        Error_Handler();
    }  
    if(HAL_SPI_Init(&SpiHandle) != HAL_OK)
    {
        Error_Handler();
    }

    使用举例:

    SPI_HandleTypeDef hspi = {0};
    
    
    /* 设置SPI参数 */
    hspi.Instance               = SPIx;                   /* 例化SPI */
    hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 设置波特率 */
    hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全双工 */
    hspi.Init.CLKPhase          = _CLKPhase;              /* 配置时钟相位 */
    hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置时钟极性 */
    hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 设置数据宽度 */
    hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 数据传输先传高位 */
    hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */
    hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC后,此位无效 */
    hspi.Init.NSS               = SPI_NSS_SOFT;            /* 使用软件方式管理片选引脚 */
    hspi.Init.Mode             = SPI_MODE_MASTER;         /* SPI工作在主控模式 */
    
    
    /* 初始化SPI */
    if (HAL_SPI_Init(&hspi) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }        

    31.4.2 函数HAL_SPI_DeInit

    函数原型:

    HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi)
    {
      /* 判断SPI句柄 */
      if (hspi == NULL)
      {
        return HAL_ERROR;
      }
    
      /* 检查参数 */
      assert_param(IS_SPI_ALL_INSTANCE(hspi->Instance));
    
      hspi->State = HAL_SPI_STATE_BUSY;
    
      /* 关闭SPI外设时钟 */
      __HAL_SPI_DISABLE(hspi);
    
    #if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
      if (hspi->MspDeInitCallback == NULL)
      {
        hspi->MspDeInitCallback = HAL_SPI_MspDeInit; /* Legacy weak MspDeInit  */
      }
    
      /* 复位底层硬件: GPIO, CLOCK, NVIC... */
      hspi->MspDeInitCallback(hspi);
    #else
      /* 复位底层硬件: GPIO, CLOCK, NVIC... */
      HAL_SPI_MspDeInit(hspi);
    #endif 
    
      hspi->ErrorCode = HAL_SPI_ERROR_NONE;
      hspi->State = HAL_SPI_STATE_RESET;
    
      /* 解锁 */
      __HAL_UNLOCK(hspi);
    
      return HAL_OK;
    }

    函数描述:

    用于复位SPI总线初始化。

    函数参数:

    •   第1个参数是SPI_HandleTypeDef类型结构体指针变量。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中

    31.4.3 函数HAL_SPI_TransmitReceive

    函数原型:

    HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
    uint16_t Size,uint32_t Timeout)
    {
       /* 省略未写 */
    
      /* 16bit数据传输 */
      if (hspi->Init.DataSize == SPI_DATASIZE_16BIT)
      {
          /* 省略未写 */
      }
      /* 8bit数据传输  */
      else
      {
         /* 省略未写 */
      }
    
    /* 省略未写 */
    }

    函数描述:

    此函数主要用于SPI数据收发,全双工查询方式。

    函数参数:

    •   第1个参数是SPI_HandleTypeDef类型结构体指针变量。
    •   第2个参数是发送数据缓冲地址。
    •   第3个参数是接收数据缓冲地址。
    •   第4个参数是传输的数据大小,单位字节个数。
    •   第5个参数是传输过程的溢出时间,单位ms。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    SPI_HandleTypeDef hspi = {0};
    
    if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }    

    31.4.4 函数HAL_SPI_TransmitReceive_IT

    函数原型:

    HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)
    {
      /* 省略未写 */
    
      /* 设置传输参数 */
      hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
      hspi->pTxBuffPtr  = (uint8_t *)pTxData;
      hspi->TxXferSize  = Size;
      hspi->TxXferCount = Size;
      hspi->pRxBuffPtr  = (uint8_t *)pRxData;
      hspi->RxXferSize  = Size;
      hspi->RxXferCount = Size;
    
      /* 设置中断处理 */
      if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
      {
        hspi->RxISR     = SPI_2linesRxISR_16BIT;
        hspi->TxISR     = SPI_2linesTxISR_16BIT;
      }
      else
      {
        hspi->RxISR     = SPI_2linesRxISR_8BIT;
        hspi->TxISR     = SPI_2linesTxISR_8BIT;
      }
    
    #if (USE_SPI_CRC != 0U)
      /* 复位CRC计算 */
      if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
      {
        SPI_RESET_CRC(hspi);
      }
    #endif 
    
      /* 使能TXE, RXNE 和  ERR 中断 */
      __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_TXE | SPI_IT_RXNE | SPI_IT_ERR));
    
      /* 检测SPI是否已经使能 */
      if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
      {
        /* 使能SPI外设 */
        __HAL_SPI_ENABLE(hspi);
      }
    
    error :
      /* 解锁 */
      __HAL_UNLOCK(hspi);
      return errorcode;
    }

    函数描述:

    此函数主要用于SPI数据收发,全双工中断方式。

    函数参数:

    •   第1个参数是SPI_HandleTypeDef类型结构体指针变量。
    •   第2个参数是发送数据缓冲地址。
    •   第3个参数是接收数据缓冲地址。
    •   第4个参数是传输的数据大小,单位字节个数。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    SPI_HandleTypeDef hspi = {0};
    
    if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }

    31.4.5 函数HAL_SPI_TransmitReceive_DMA

    函数原型:

    HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                                                                               uint16_t Size)
    {
       /* 省略未写 */
    
      /* 使能RX DMA */
      if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->DR, (uint32_t)hspi->pRxBuffPtr,
     hspi->RxXferCount))
      {
       
    
      }
    
      /* 使能RX DMA */
      if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR,
     hspi->TxXferCount))
      {
       
      }
    
      /* 省略未写 */
    
    }

    函数描述:

    此函数主要用于SPI数据收发,全双工DMA方式。

    函数参数:

    •   第1个参数是SPI_HandleTypeDef类型结构体指针变量。
    •   第2个参数是发送数据缓冲地址。
    •   第3个参数是接收数据缓冲地址。
    •   第4个参数是传输的数据大小,单位字节个数。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    SPI_HandleTypeDef hspi = {0};
    
    if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }

    31.5 总结

    本章节就为大家讲解这么多,要熟练掌握SPI总线的查询,中断和DMA方式的实现,因为基于SPI接口的外设芯片很多,熟练后,可以方便的驱动各种SPI接口芯片,以便选择合适的驱动方式。

  • 相关阅读:
    自定义udf添加一列
    spark执行命令 监控执行命令
    R链接hive/oracle/mysql
    [Hive_6] Hive 的内置函数应用
    [Hive_add_6] Hive 实现 Word Count
    [Hive_add_5] Hive 的 join 操作
    【爬坑】远程连接 MySQL 失败
    [Hive_add_4] Hive 命令行客户端 Beeline 的使用
    [Hive_5] Hive 的 JDBC 编程
    [Hive_add_3] Hive 进行简单数据处理
  • 原文地址:https://www.cnblogs.com/armfly/p/13376167.html
Copyright © 2020-2023  润新知