1. 程式人生 > 實用技巧 >【STM32F407開發板使用者手冊】第31章 STM32F407的SPI匯流排基礎知識和HAL庫API

【STM32F407開發板使用者手冊】第31章 STM32F407的SPI匯流排基礎知識和HAL庫API

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

第31章 STM32F407的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. STM3F407的主頻168MHz時,SPI1最高通訊時鐘是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到SPI3)

這個知識點在初學的時候容易忽視,所以我們這裡整理下。

  • SPI1到SPI3的所在的匯流排

SPI1在APB2匯流排,SPI2,SPI3在APB1匯流排。SPI的最高時鐘由這些匯流排決定的。

  • SPI1到SPI3的支援的最高時鐘

STM32F407主頻在168MHz下,SPI1的最高時鐘是84MHz,而SPI2和SPI3是42MHz。這裡特別注意一點,SPI工作時最少選擇二分頻,也就是說SPI1實際通訊時鐘是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匯流排星型拓撲用到的地方比較多,V5開發板就是用的星型拓撲外接多種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定義的,在stm32f407xx.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的定義,在stm32f407xx.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 SPI1                ((SPI_TypeDef *) SPI1_BASE)
#define SPI2                ((SPI_TypeDef *) SPI2_BASE)
#define SPI3                ((SPI_TypeDef *) SPI3_BASE)<----- 展開這個巨集,(FLASH_TypeDef *)0x40013C00

我們訪問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:使用者自己初始SPI和涉及到的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介面晶片,以便選擇合適的驅動方式。