【STM32H7教程】第72章 STM32H7的SPI總線基礎知識和HAL庫API


完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第72章       STM32H7的SPI總線基礎知識和HAL庫API

本章節為大家講解SPI(Serial peripheral interface)總線的基礎知識和對應的HAL庫API。

72.1 初學者重要提示

72.2 SPI總線基礎知識

72.3 SPI總線的HAL庫用法

72.4 源文件stm32h7xx_hal_spi.c

72.5 總結

 

 

72.1 初學者重要提示

  1.   STM32H7的SPI支持4到32bit數據傳輸,而STM32F1和F4系列僅支持8bit或者16bit。
  2.   STM32H7的主頻400MHz時,SPI1, 2, 3最高通信時鍾是100MHz,而SPI4, 5, 6是50MHz。
  3.   STM32H7的MISO和MOSI引腳功能可以互換,使用比較靈活。
  4.   SPI總線的片選引腳SS在單一的主從器件配置下是可選的,一般情況下可以不使用。
  5.   搜集了幾篇質量比較高的SPI總線介紹帖:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96788

72.2 SPI總線基礎知識

72.2.1 SPI總線的硬件框圖

認識一個外設,最好的方式就是看它的框圖,方便我們快速的了解SPI的基本功能,然后再看手冊了解細節。

 

通過這個框圖,我們可以得到如下信息:

  •   spi_wkup輸出

低功耗喚醒信號。

  •   spi_it輸出

spi的中斷請求信號。

  •   spi_tx_dma
  • spi_rx_dma

spi的DMA發送和接收請求信號。

  •   spi_pclk

為寄存器提供時鍾。

  •   spi_ker_ck

為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設置,此引腳可用於:

a. 選擇三個從器件進行通信。

b. 同步數據幀。

c. 檢測多個主器件之間是否存在沖突。

 

通過這個框圖還要認識到一點,SPI有三個時鍾域,分別是寄存器所在的ABP總線時鍾域,內核時鍾發生器時鍾域以及內核時鍾發生器分頻后的串行時鍾域。

72.2.2 SPI接口的區別和時鍾源(SPI1到SPI6)

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

  •   SPI1到SPI6的區別
    •   SPI1,SPI2和SPI3支持4到32bit數據傳輸,SPI4,SPI5和SPI6是4到16bit數據傳輸。
    •   SPI1,SPI2和SPI3的FIFO大小是16*8bit,而SPI4,SPI5和SPI6的FIFO大小是8*8bit。

  •   SPI1到SPI6的所在的總線(對應SPI框圖的SPI_CLK時鍾域

SPI1,SPI4和SPI5在APB2總線,SPI2,SPI3在APB1總線,SPI6在APB4總線。注意,SPI的最高時鍾不是由這些總線決定的。

  •   SPI1到SPI6的支持的最高時鍾(對應SPI框圖的SPI_KER_CK

STM32H7主頻在400MHz下,SPI1,SPI2和SPI3的最高時鍾是200MHz,而SPI4,5,6是100MHz, 以SPI1為了,可以選擇的時鍾源如下:

 

這里特別注意一點,SPI工作時最少選擇二分頻,也就是說SPI1,2,3實際通信時鍾是100MHz,而SPI4,5,6是50MHz。

72.2.3 SPI總線全雙工,單工和半雙工通信

片選信號SS在單一的主從器件配置下是可選的,一般情況下可以不使用。但需要同步數據流,或者用於TI模式時需要此信號。

  •   全雙工通信

全雙工就是主從器件之間同時互傳數據,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引腳可以不使用。

72.2.4 SPI總線星型拓撲

SPI總線星型拓撲用到的地方比較多,V7開發板就是用的星型拓撲外接多種SPI器件:

 

關於這個接線圖,有以下幾點需要大家了解:

  •   主器件的SS引腳不使用,使用通用GPIO控制。為每個器件配一個SS引腳,方便單獨片選控制。
  •   從器件的MISO引腳要配置為復用開漏輸出(很多外部芯片在未片選時,數據引腳是呈現高阻態)。

72.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 = 1, CPHA = 0時

SCK引腳在空閑狀態處於高電平,SCK引腳的第1個邊沿捕獲傳輸的第1個數據。

72.3 SPI總線的HAL庫用法

72.3.1 SPI總線結構體SPI_TypeDef

SPI總線相關的寄存器是通過HAL庫中的結構體SPI_TypeDef定義的,在stm32h743xx.h中可以找到這個類型定義:

typedef struct
{
  __IO uint32_t CR1;           /*!< SPI/I2S Control register 1,                      Address offset: 0x00 */
  __IO uint32_t CR2;           /*!< SPI Control register 2,                          Address offset: 0x04 */
  __IO uint32_t CFG1;          /*!< SPI Configuration register 1,                    Address offset: 0x08 */
  __IO uint32_t CFG2;          /*!< SPI Configuration register 2,                    Address offset: 0x0C */
  __IO uint32_t IER;           /*!< SPI/I2S Interrupt Enable register,               Address offset: 0x10 */
  __IO uint32_t SR;            /*!< SPI/I2S Status register,                         Address offset: 0x14 */
  __IO uint32_t IFCR;          /*!< SPI/I2S Interrupt/Status flags clear register,   Address offset: 0x18 */
  uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                        */
  __IO uint32_t TXDR;          /*!< SPI/I2S Transmit data register,                  Address offset: 0x20 */
  uint32_t      RESERVED1[3];  /*!< Reserved, 0x24-0x2C                                                   */
  __IO uint32_t RXDR;          /*!< SPI/I2S Receive data register,                   Address offset: 0x30 */
  uint32_t      RESERVED2[3];  /*!< Reserved, 0x34-0x3C                                                   */
  __IO uint32_t CRCPOLY;       /*!< SPI CRC Polynomial register,                     Address offset: 0x40 */
  __IO uint32_t TXCRC;         /*!< SPI Transmitter CRC register,                    Address offset: 0x44 */
  __IO uint32_t RXCRC;         /*!< SPI Receiver CRC register,                       Address offset: 0x48 */
  __IO uint32_t UDRDR;         /*!< SPI Underrun data register,                      Address offset: 0x4C */
  __IO uint32_t I2SCFGR;       /*!< I2S Configuration register,                      Address offset: 0x50 */

} SPI_TypeDef;

這個結構體的成員名稱和排列次序和CPU的寄存器是一 一對應的。

__IO表示volatile, 這是標准C語言中的一個修飾字,表示這個變量是非易失性的,編譯器不要將其優化掉。core_m7.h 文件定義了這個宏:

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

 

下面我們看下SPI的定義,在stm32h743xx.h文件。

#define PERIPH_BASE           (0x40000000UL) 
#define D2_APB1PERIPH_BASE     PERIPH_BASE
#define D2_APB2PERIPH_BASE    (PERIPH_BASE + 0x00010000UL)
#define D3_APB1PERIPH_BASE    (PERIPH_BASE + 0x18000000UL)

#define SPI2_BASE             (D2_APB1PERIPH_BASE + 0x3800UL)
#define SPI3_BASE             (D2_APB1PERIPH_BASE + 0x3C00UL)
#define SPI1_BASE             (D2_APB2PERIPH_BASE + 0x3000UL)
#define SPI4_BASE             (D2_APB2PERIPH_BASE + 0x3400UL)
#define SPI5_BASE             (D2_APB2PERIPH_BASE + 0x5000UL)
#define SPI6_BASE             (D3_APB1PERIPH_BASE + 0x1400UL)

#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 *)0x58001400

 

我們訪問SPI的CR1寄存器可以采用這種形式:SPI->CR1 = 0。

72.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;                     
  uint32_t CRCLength;                        
  uint32_t NSSPMode;                         
  uint32_t NSSPolarity;                    
  uint32_t TxCRCInitializationPattern;       
  uint32_t RxCRCInitializationPattern;       
  uint32_t MasterSSIdleness;                 
  uint32_t MasterInterDataIdleness;           
  uint32_t MasterReceiverAutoSusp;          
  uint32_t MasterKeepIOState;               
  uint32_t IOSwap;                          
} SPI_InitTypeDef;

 

下面將結構體成員逐一做個說明:

  •   Mode

用於設置工作在主機模式還是從機模式。

#define SPI_MODE_SLAVE              (0x00000000UL)
#define SPI_MODE_MASTER             SPI_CFG2_MASTER

 

  •   Direction

用於設置SPI工作在全雙工,單工,還是半雙工模式。

#define SPI_DIRECTION_2LINES           (0x00000000UL)     /* 全雙工 */
#define SPI_DIRECTION_2LINES_TXONLY     SPI_CFG2_COMM_0   /* 單工,僅發送 */
#define SPI_DIRECTION_2LINES_RXONLY     SPI_CFG2_COMM_1   /* 單工,僅接收 */
#define SPI_DIRECTION_1LINE             SPI_CFG2_COMM     /* 半雙工 */

 

  •   DataSize

用於設置SPI總線數據收發的位寬,支持4-32bit。

#define SPI_DATASIZE_4BIT                             (0x00000003UL)
#define SPI_DATASIZE_5BIT                             (0x00000004UL)
#define SPI_DATASIZE_6BIT                             (0x00000005UL)
#define SPI_DATASIZE_7BIT                             (0x00000006UL)
#define SPI_DATASIZE_8BIT                             (0x00000007UL)
#define SPI_DATASIZE_9BIT                             (0x00000008UL)
#define SPI_DATASIZE_10BIT                            (0x00000009UL)
#define SPI_DATASIZE_11BIT                            (0x0000000AUL)
#define SPI_DATASIZE_12BIT                            (0x0000000BUL)
#define SPI_DATASIZE_13BIT                            (0x0000000CUL)
#define SPI_DATASIZE_14BIT                            (0x0000000DUL)
#define SPI_DATASIZE_15BIT                            (0x0000000EUL)
#define SPI_DATASIZE_16BIT                            (0x0000000FUL)
#define SPI_DATASIZE_17BIT                            (0x00000010UL)
#define SPI_DATASIZE_18BIT                            (0x00000011UL)
#define SPI_DATASIZE_19BIT                            (0x00000012UL)
#define SPI_DATASIZE_20BIT                            (0x00000013UL)
#define SPI_DATASIZE_21BIT                            (0x00000014UL)
#define SPI_DATASIZE_22BIT                            (0x00000015UL)
#define SPI_DATASIZE_23BIT                            (0x00000016UL)
#define SPI_DATASIZE_24BIT                            (0x00000017UL)
#define SPI_DATASIZE_25BIT                            (0x00000018UL)
#define SPI_DATASIZE_26BIT                            (0x00000019UL)
#define SPI_DATASIZE_27BIT                            (0x0000001AUL)
#define SPI_DATASIZE_28BIT                            (0x0000001BUL)
#define SPI_DATASIZE_29BIT                            (0x0000001CUL)
#define SPI_DATASIZE_30BIT                            (0x0000001DUL)
#define SPI_DATASIZE_31BIT                            (0x0000001EUL)
#define SPI_DATASIZE_32BIT                            (0x0000001FUL)

 

  •   CLKPolarity

用於設置空閑狀態時,CLK是高電平還是低電平。

#define SPI_POLARITY_LOW       (0x00000000UL)
#define SPI_POLARITY_HIGH      SPI_CFG2_CPOL

 

  •   NSS

用於設置NSS信號由硬件NSS引腳管理或者軟件SSI位管理。

#define SPI_NSS_SOFT                                  SPI_CFG2_SSM
#define SPI_NSS_HARD_INPUT                            (0x00000000UL)
#define SPI_NSS_HARD_OUTPUT                           SPI_CFG2_SSOE

 

  •   BaudRatePrescaler

用於設置SPI時鍾分頻,僅SPI工作在主控模式下起作用,對SPI從機模式不起作用。

#define SPI_BAUDRATEPRESCALER_2                       (0x00000000UL)
#define SPI_BAUDRATEPRESCALER_4                       (0x10000000UL)
#define SPI_BAUDRATEPRESCALER_8                       (0x20000000UL)
#define SPI_BAUDRATEPRESCALER_16                      (0x30000000UL)
#define SPI_BAUDRATEPRESCALER_32                      (0x40000000UL)
#define SPI_BAUDRATEPRESCALER_64                      (0x50000000UL)
#define SPI_BAUDRATEPRESCALER_128                     (0x60000000UL)
#define SPI_BAUDRATEPRESCALER_256                     (0x70000000UL)

 

  •   FirstBit

用於設置數據傳輸從最高bit開始還是從最低bit開始。

#define SPI_FIRSTBIT_MSB                              (0x00000000UL)
#define SPI_FIRSTBIT_LSB                              SPI_CFG2_LSBFRST

 

  •   TIMode

用於設置是否使能SPI總線的TI模式。

#define SPI_TIMODE_DISABLE               (0x00000000UL)
#define SPI_TIMODE_ENABLE                SPI_CFG2_SP_0

 

  •   CRCCalculation

用於設置是否使能CRC計算。

#define SPI_CRCCALCULATION_DISABLE                    (0x00000000UL)
#define SPI_CRCCALCULATION_ENABLE                     SPI_CFG1_CRCEN

 

  •   CRCPolynomial

用於設置CRC計算使用的多項式,必須是奇數,范圍0到65535。

  •   CRCLength

用於設置CRC計算時的CRC長度。大小要與同屬此結構體的DataSize一致。或是DataSize的整數倍。

#define SPI_CRC_LENGTH_DATASIZE                       (0x00000000UL)
#define SPI_CRC_LENGTH_4BIT                           (0x00030000UL)
#define SPI_CRC_LENGTH_5BIT                           (0x00040000UL)
#define SPI_CRC_LENGTH_6BIT                           (0x00050000UL)
#define SPI_CRC_LENGTH_7BIT                           (0x00060000UL)
#define SPI_CRC_LENGTH_8BIT                           (0x00070000UL)
#define SPI_CRC_LENGTH_9BIT                           (0x00080000UL)
#define SPI_CRC_LENGTH_10BIT                          (0x00090000UL)
#define SPI_CRC_LENGTH_11BIT                          (0x000A0000UL)
#define SPI_CRC_LENGTH_12BIT                          (0x000B0000UL)
#define SPI_CRC_LENGTH_13BIT                          (0x000C0000UL)
#define SPI_CRC_LENGTH_14BIT                          (0x000D0000UL)
#define SPI_CRC_LENGTH_15BIT                          (0x000E0000UL)
#define SPI_CRC_LENGTH_16BIT                          (0x000F0000UL)
#define SPI_CRC_LENGTH_17BIT                          (0x00100000UL)
#define SPI_CRC_LENGTH_18BIT                          (0x00110000UL)
#define SPI_CRC_LENGTH_19BIT                          (0x00120000UL)
#define SPI_CRC_LENGTH_20BIT                          (0x00130000UL)
#define SPI_CRC_LENGTH_21BIT                          (0x00140000UL)
#define SPI_CRC_LENGTH_22BIT                          (0x00150000UL)
#define SPI_CRC_LENGTH_23BIT                          (0x00160000UL)
#define SPI_CRC_LENGTH_24BIT                          (0x00170000UL)
#define SPI_CRC_LENGTH_25BIT                          (0x00180000UL)
#define SPI_CRC_LENGTH_26BIT                          (0x00190000UL)
#define SPI_CRC_LENGTH_27BIT                          (0x001A0000UL)
#define SPI_CRC_LENGTH_28BIT                          (0x001B0000UL)
#define SPI_CRC_LENGTH_29BIT                          (0x001C0000UL)
#define SPI_CRC_LENGTH_30BIT                          (0x001D0000UL)
#define SPI_CRC_LENGTH_31BIT                          (0x001E0000UL)
#define SPI_CRC_LENGTH_32BIT                          (0x001F0000UL)

 

  •   NSSPMode

用於設置是否使能NSSP信號,可以通過SPIx_CR2寄存器的SSOM位使能。注意,只有配置為摩托羅拉SPI主控模式時設置此成員才有用。

#define SPI_NSS_PULSE_DISABLE                         (0x00000000UL)
#define SPI_NSS_PULSE_ENABLE                          SPI_CFG2_SSOM

 

  •   NSSPolarity

用於設置NSS引腳上的高電平或者低電平作為激活電平。

#define SPI_NSS_POLARITY_LOW                          (0x00000000UL)
#define SPI_NSS_POLARITY_HIGH                          SPI_CFG2_SSIOP

 

  •   FifoThreshold

用於設置SPI的FIFO閥值。

#define SPI_FIFO_THRESHOLD_01DATA                     (0x00000000UL)
#define SPI_FIFO_THRESHOLD_02DATA                     (0x00000020UL)
#define SPI_FIFO_THRESHOLD_03DATA                     (0x00000040UL)
#define SPI_FIFO_THRESHOLD_04DATA                     (0x00000060UL)
#define SPI_FIFO_THRESHOLD_05DATA                     (0x00000080UL)
#define SPI_FIFO_THRESHOLD_06DATA                     (0x000000A0UL)
#define SPI_FIFO_THRESHOLD_07DATA                     (0x000000C0UL)
#define SPI_FIFO_THRESHOLD_08DATA                     (0x000000E0UL)
#define SPI_FIFO_THRESHOLD_09DATA                     (0x00000100UL)
#define SPI_FIFO_THRESHOLD_10DATA                     (0x00000120UL)
#define SPI_FIFO_THRESHOLD_11DATA                     (0x00000140UL)
#define SPI_FIFO_THRESHOLD_12DATA                     (0x00000160UL)
#define SPI_FIFO_THRESHOLD_13DATA                     (0x00000180UL)
#define SPI_FIFO_THRESHOLD_14DATA                     (0x000001A0UL)
#define SPI_FIFO_THRESHOLD_15DATA                     (0x000001C0UL)
#define SPI_FIFO_THRESHOLD_16DATA                     (0x000001E0UL)

 

  •   TxCRCInitializationPattern

發送CRC初始化模式。

#define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN       (0x00000000UL)
#define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN        (0x00000001UL)

 

  •   RxCRCInitializationPattern

接收CRC初始化模式

#define SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN       (0x00000000UL)
#define SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN        (0x00000001UL)

 

  •  MasterSSIdleness

在主模式下插入到SS有效邊沿和第一個數據開始之間的額外延遲,單位SPI時鍾周期個數。

#define SPI_MASTER_SS_IDLENESS_00CYCLE                (0x00000000UL)
#define SPI_MASTER_SS_IDLENESS_01CYCLE                (0x00000001UL)
#define SPI_MASTER_SS_IDLENESS_02CYCLE                (0x00000002UL)
#define SPI_MASTER_SS_IDLENESS_03CYCLE                (0x00000003UL)
#define SPI_MASTER_SS_IDLENESS_04CYCLE                (0x00000004UL)
#define SPI_MASTER_SS_IDLENESS_05CYCLE                (0x00000005UL)
#define SPI_MASTER_SS_IDLENESS_06CYCLE                (0x00000006UL)
#define SPI_MASTER_SS_IDLENESS_07CYCLE                (0x00000007UL)
#define SPI_MASTER_SS_IDLENESS_08CYCLE                (0x00000008UL)
#define SPI_MASTER_SS_IDLENESS_09CYCLE                (0x00000009UL)
#define SPI_MASTER_SS_IDLENESS_10CYCLE                (0x0000000AUL)
#define SPI_MASTER_SS_IDLENESS_11CYCLE                (0x0000000BUL)
#define SPI_MASTER_SS_IDLENESS_12CYCLE                (0x0000000CUL)
#define SPI_MASTER_SS_IDLENESS_13CYCLE                (0x0000000DUL)
#define SPI_MASTER_SS_IDLENESS_14CYCLE                (0x0000000EUL)
#define SPI_MASTER_SS_IDLENESS_15CYCLE                (0x0000000FUL)

 

  •   MasterInterDataIdleness

主模式下在兩個連續數據幀之間插入的最小時間延遲,單位SPI時鍾周期個數。

#define SPI_MASTER_RX_AUTOSUSP_DISABLE                (0x00000000UL)
#define SPI_MASTER_RX_AUTOSUSP_ENABLE                 SPI_CR1_MASRX

 

  •   MasterReceiverAutoSusp

用於控制主器件接收器模式下的連續 SPI 傳輸以及自動管理,以避免出現上溢情況。

#define SPI_MASTER_RX_AUTOSUSP_DISABLE                (0x00000000UL)
#define SPI_MASTER_RX_AUTOSUSP_ENABLE                 SPI_CR1_MASRX

 

  •   MasterKeepIOState

禁止SPI后,SPI相關引腳保持當前狀態,以防止出現毛刺。在從模式下,該位不應該使用。

#define SPI_MASTER_KEEP_IO_STATE_DISABLE              (0x00000000UL)
#define SPI_MASTER_KEEP_IO_STATE_ENABLE               SPI_CFG2_AFCNTR

 

  •   IOSwap

用於交換MISO和MOSI引腳。

#define SPI_IO_SWAP_DISABLE                           (0x00000000UL)
#define SPI_IO_SWAP_ENABLE                            SPI_CFG2_IOSWP

 

72.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;                 
  uint32_t                   CRCSize;                     
  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 defined(USE_SPI_RELOAD_TRANSFER)
  SPI_ReloadTypeDef          Reload;                      
#endif 

#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)
  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用來設置使用自定義回調還是使用默認回調,此定義一般放在stm32h7xx_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句柄,方便操作調用。

72.4 SPI總線源文件stm32h7xx_hal_spi.c

此文件涉及到的函數較多,這里把幾個常用的函數做個說明:

  •   HAL_SPI_Init
  •   HAL_SPI_DeInit
  •   HAL_SPI_TransmitReceive
  •   HAL_SPI_TransmitReceive_IT
  •   HAL_SPI_TransmitReceive_DMA

72.4.1 函數HAL_SPI_Init

函數原型:

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
{
  uint32_t crc_length = 0UL;
  uint32_t packet_length;

  /* 省略未寫 */

  /* 如果數據位寬大於16bit,必須是SPI1,SPI2或者SPI3,而SPI4,SPI5和SPI6不支持大於16bit */
  if ((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (hspi->Init.DataSize > SPI_DATASIZE_16BIT))
  {
    return HAL_ERROR;
  }

  /* SPI1,SPI2和SPI3的FIFO大小是16*8bit,而SPI4,SPI5和SPI6的FIFO大小是8*8bit
     這里是查看設置的緩沖大小是否超出了FIFO支持的大小。
*/
  packet_length = SPI_GetPacketSize(hspi);
  if (((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (packet_length > SPI_LOWEND_FIFO_SIZE)) ||
      ((IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (packet_length > SPI_HIGHEND_FIFO_SIZE)))
  {
    return HAL_ERROR;
  }

#if (USE_SPI_CRC != 0UL)
    /* 省略未寫 */
#endif 

  if (hspi->State == HAL_SPI_STATE_RESET)
  {
    /* 解鎖 */
    hspi->Lock = HAL_UNLOCKED;

    /* 使用自定義回調 */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)
    /* 設置默認回調函數 */
    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; 
    }

    /* 初始化地址硬件: 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 配置---------------------*/
  if ((hspi->Init.NSS == SPI_NSS_SOFT) && (hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.NSSPolarity ==
 SPI_NSS_POLARITY_LOW))
  {
      SET_BIT(hspi->Instance->CR1, SPI_CR1_SSI);
  }

  /* SPIx CFG1配置 */
  WRITE_REG(hspi->Instance->CFG1, (hspi->Init.BaudRatePrescaler | hspi->Init.CRCCalculation | crc_length |
                                   hspi->Init.FifoThreshold     | hspi->Init.DataSize));

  /* SPIx CFG2配置 */
  WRITE_REG(hspi->Instance->CFG2, (hspi->Init.NSSPMode     | hspi->Init.TIMode           | hspi->Init.NSSPolarity  |
                                   hspi->Init.NSS          | hspi->Init.CLKPolarity      | hspi->Init.CLKPhase     |
                                   hspi->Init.FirstBit     | hspi->Init.Mode             | hspi->Init.MasterInterDataIdleness |
                                   hspi->Init.Direction    | hspi->Init.MasterSSIdleness | hspi->Init.IOSwap));

#if (USE_SPI_CRC != 0UL)
  /*---------------------------- SPIx CRC配置 ------------------*/
  /* 配置SPI CRC */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    /* 初始化TX CRC初始值 */
    if (hspi->Init.TxCRCInitializationPattern == SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN)
    {
      SET_BIT(hspi->Instance->CR1, SPI_CR1_TCRCINI);
    }
    else
    {
      CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_TCRCINI);
    }

    /* 初始化RXCRC初始值 */
    if (hspi->Init.RxCRCInitializationPattern == SPI_CRC_INITIALIZATION_ALL_ONE_PATTERN)
    {
      SET_BIT(hspi->Instance->CR1, SPI_CR1_RCRCINI);
    }
    else
    {
      CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_RCRCINI);
    }

    /* 使能 33/17 bit CRC計算 */
    if (((!IS_SPI_HIGHEND_INSTANCE(hspi->Instance)) && (crc_length == SPI_CRC_LENGTH_16BIT)) ||
        ((IS_SPI_HIGHEND_INSTANCE(hspi->Instance))  && (crc_length == SPI_CRC_LENGTH_32BIT)))
    {
      SET_BIT(hspi->Instance->CR1, SPI_CR1_CRC33_17);
    }
    else
    {
      CLEAR_BIT(hspi->Instance->CR1, SPI_CR1_CRC33_17);
    }

    /* 寫CRC多項式到SPI寄存器 */
    WRITE_REG(hspi->Instance->CRCPOLY, hspi->Init.CRCPolynomial);
  }
#endif 

  /* SPI從模式,下溢配置 */
  if (hspi->Init.Mode == SPI_MODE_SLAVE)
  {
    /* 設置默認下溢配置 */
#if (USE_SPI_CRC != 0UL)
    if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_DISABLE)
#endif
    {
      MODIFY_REG(hspi->Instance->CFG1, SPI_CFG1_UDRDET, SPI_CFG1_UDRDET_0);
    }
    MODIFY_REG(hspi->Instance->CFG1, SPI_CFG1_UDRCFG, SPI_CFG1_UDRCFG_1);
  }

#if defined(SPI_I2SCFGR_I2SMOD)
  CLEAR_BIT(hspi->Instance->I2SCFGR, SPI_I2SCFGR_I2SMOD);
#endif 

  /* 確保AFCNTR bit由SPI主機模式管理 */
  if ((hspi->Init.Mode & SPI_MODE_MASTER) == SPI_MODE_MASTER)
  {
    /* Alternate function GPIOs control */
    MODIFY_REG(hspi->Instance->CFG2, SPI_CFG2_AFCNTR, (hspi->Init.MasterKeepIOState));
  }

  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.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC后,此位無效 */
hspi.Init.NSS               = SPI_NSS_SOFT;                    /* 使用軟件方式管理片選引腳 */
hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;       /* 設置FIFO大小是一個數據項 */
hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;           /* 禁止脈沖輸出 */
hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相關引腳保持當前狀態 */  
hspi.Init.Mode                  = SPI_MODE_MASTER;            /* SPI工作在主控模式 */

if (HAL_SPI_Init(&hspi) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

 

72.4.2 函數HAL_SPI_DeInit

函數原型:

HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi)
{
/* 檢測SPI句柄是否有效 */
  if (hspi == NULL)
  {
    return HAL_ERROR;
  }

/* 檢查SPI例化參數 */
  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 == 1UL)
  if (hspi->MspDeInitCallback == NULL)
  {
    hspi->MspDeInitCallback = HAL_SPI_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;

  /* 解鎖SPI */
  __HAL_UNLOCK(hspi);

  return HAL_OK;
}

 

函數描述:

用於復位SPI總線初始化。

函數參數:

  •   第1個參數是SPI_HandleTypeDef類型結構體指針變量。
  •   返回值,返回HAL_TIMEOUT表示超時,HAL_ERROR表示參數錯誤,HAL_OK表示發送成功,HAL_BUSY表示忙,正在使用中

72.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,小於16bi的數據收發 */
  else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
  {
      /* 省略未寫 */
  }
  /* 小於等於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__);
}

 

72.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_16BIT)
  {
    hspi->TxISR     = SPI_TxISR_32BIT;
    hspi->RxISR     = SPI_RxISR_32BIT;
  }
  else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
  {
    hspi->RxISR     = SPI_RxISR_16BIT;
    hspi->TxISR     = SPI_TxISR_16BIT;
  }
  else
  {
    hspi->RxISR     = SPI_RxISR_8BIT;
    hspi->TxISR     = SPI_TxISR_8BIT;
  }

  /* 設置當前傳輸數據大小 */
  MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, Size);

  /* 使能SPI外設 */
  __HAL_SPI_ENABLE(hspi);

  /* 使能各種中斷標志 */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_EOT | SPI_IT_RXP | SPI_IT_TXP | SPI_IT_DXP | SPI_IT_UDR | SPI_IT_OVR | 
SPI_IT_FRE | SPI_IT_MODF | SPI_IT_TSERF));

  if (hspi->Init.Mode == SPI_MODE_MASTER)
  {
    /* 啟動傳輸 */
    SET_BIT(hspi->Instance->CR1, SPI_CR1_CSTART);
  }

  /* 解鎖 */
  __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__);
}

 

72.4.5 函數HAL_SPI_TransmitReceive_DMA

函數原型:

HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                              uint16_t Size)
{
   /* 省略未寫 */

/* 注意DMA的位寬和對齊設置 */
  if (((hspi->Init.DataSize > SPI_DATASIZE_16BIT) && (hspi->hdmarx->Init.MemDataAlignment !=
 DMA_MDATAALIGN_WORD))  || 
      ((hspi->Init.DataSize > SPI_DATASIZE_8BIT) && ((hspi->hdmarx->Init.MemDataAlignment != DMA_MDATAALIGN_HALFWORD) && (hspi->hdmarx->Init.MemDataAlignment != DMA_MDATAALIGN_WORD))))
  {
  }

/* 調整DMA對齊和數據大小 */
  if (hspi->Init.DataSize <= SPI_DATASIZE_8BIT)
  {
     /* 省略未寫 */
  }
  else if (hspi->Init.DataSize <= SPI_DATASIZE_16BIT)
  {
     /* 省略未寫 */
  }
  else
  {
      /* 省略未寫 */
  }

/*  DMA接收配置 */
  if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmarx, (uint32_t)&hspi->Instance->RXDR, (uint32_t)hspi->pRxBuffPtr,
 hspi->RxXferCount))
  {
 
  }

/* DMA發送配置 */
  if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->TXDR,
 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__);
}

 

72.5 總結

本章節就為大家講解這么多,要熟練掌握SPI總線的查詢,中斷和DMA方式的實現,因為基於SPI接口的外設芯片很多,熟練后,可以方便的驅動各種SPI接口芯片,以便選擇合適的驅動方式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM