15. SPI通訊協議

星光樱梦發表於2024-03-17

一、SPI通訊協議簡介

  SPI 是 Serial Peripheral interface 縮寫,顧名思義就是序列外圍裝置介面。SPI 通訊協議是 Motorola 公司首先在其 MC68HCXX 系列處理器上定義的。SPI 介面是一種高速的全雙工同步的通訊匯流排。

SPI匯流排掛在多個裝置

  • SCK(Serial Clock)時鐘訊號,由主裝置產生。
  • MOSI(Master Out / Slave In)主裝置資料輸出,從裝置資料輸入。
  • MISO(Master In / Slave Out)主裝置資料輸入,從裝置資料輸出。
  • CS(Chip Select)從裝置片選訊號,由主裝置產生。

SPI 匯流排具有三種傳輸方式:全雙工、單工以及半雙工傳輸方式。

二、SPI工作模式

  SPI 通訊協議就具備 4 種工作模式,在講這 4 種工作模式前,首先先知道兩個單詞 CPOL 和 CPHA。

  • CPOL,詳稱 Clock Polarity,就是 時鐘極性,當主從機沒有資料傳輸的時候 SCL 線的電平狀態(即空閒狀態)。假如空閒狀態是高電平,CPOL=1;若空閒狀態時低電平,那麼 CPOL=0。
  • CPHA,詳稱 Clock Phase,就是 時鐘相位。 同步通訊時,資料的變化和取樣都是在時鐘邊沿上進行的,每一個時鐘週期都會有上升沿和下降沿兩個邊沿,那麼資料的變化和取樣就分別安排在兩個不同的邊沿,由於資料在產生和到它穩定是需要一定的時間,那麼假如我們在第 1 個邊沿訊號把資料輸出了,從機只能從第 2 個邊沿訊號去取樣這個資料。

  CPHA 實質指的是資料的取樣時刻,CPHA=0 的情況就表示資料的取樣是從第 1 個邊沿訊號上即奇數邊沿,具體是上升沿還是下降沿的問題,是由 CPOL 決定的。這裡就存在一個問題:當開始傳輸第一個 bit 的時候,第 1 個時鐘邊沿就採集該資料了,那資料是什麼時候輸出來的呢?那麼就有兩種情況:一是 CS 使能的邊沿,二是上一幀資料的最後一個時鐘沿。

  CPHA=1 的情況就是表示資料取樣是從第 2 個邊沿即偶數邊沿,它的邊沿極性要注意一點,不是和上面 CPHA=0 一樣的邊沿情況。前面的是奇數邊沿取樣資料,從 SCL 空閒狀態的直接跳變,空閒狀態是高電平,那麼它就是下降沿,反之就是上升沿。由於 CPHA=1 是偶數邊沿取樣,所以需要根據偶數邊沿判斷,假如第一個邊沿即奇數邊沿是下降沿,那麼偶數邊沿的邊沿極性就是上升沿。

  由於 CPOL 和 CPHA 都有兩種不同狀態,所以 SPI 分成了 4 種模式。

SPI工作模式表

/**
 * @brief SPI初始化函式
 * 
 */
void SPI_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能SPI SCK MISO MOSI對應GPIO引腳的時鐘
    RCC_SPI_SCK_GPIO_CLK_ENABLE();
    RCC_SPI_MISO_GPIO_CLK_ENABLE();
    RCC_SPI_MOSI_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Pin = SPI_SCK_GPIO_PIN;                                     // SPI的SCL引腳
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                                 // 推輓輸出
    GPIO_InitStruct.Pull = GPIO_NOPULL;                                         // 不使用上下拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;                               // 輸出速度
    HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_MOSI_GPIO_PIN;                                    // SPI的MOSI引腳
    HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_MISO_GPIO_PIN;                                    // SPI的MISO引腳
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;                                     // 輸入模式
    HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct);


    SPI_SCK(0);                                                                 // SPI的SCK引腳預設為低電平,選擇工作模式0或1
    // SPI_SCK(1);                                                                 // SPI的SCK引腳預設為高電平,選擇工作模式2或3
}

【1】、工作模式 0:序列時鐘的奇數邊沿上升沿取樣

序列時鐘的奇數邊沿上升沿取樣時序圖

  CPOL= 0 && CPHA= 0 的情形,由於配置了 CPOL= 0,可以看到當資料未傳送或者傳送完畢,SCK 的狀態是 低電平,再者 CPHA = 0 即是 奇數邊沿採集。所以傳輸的資料會在 奇數邊沿上升沿 被採集,MOSI 和 MISO 資料的有效訊號需要在 SCK 奇數邊沿保持穩定且被取樣,在非取樣時刻,MOSI 和 MISO 的有效訊號才發生變化。

/**
 * @brief SPI交換一個位元組函式
 * 
 * @param data 待交換的資料
 * @return uint8_t 交換後的資料
 */
uint8_t SPI_SwapOneByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        // 移出資料
        SPI_MOSI(data & 0x80);
        data <<= 1;
        // SCK上升沿
        SPI_SCK(1);
        // 移入資料
        if (SPI_MISO())
        {
            data |= 0x01;
        }
        // SCK下降沿
        SPI_SCK(0);
    }

    return data;
}

【2】、工作模式 1:序列時鐘的偶數邊沿下降沿取樣

序列時鐘的偶數邊沿下降沿取樣圖

  CPOL= 0 && CPHA= 1 的情形,由於配置了 CPOL= 0,可以看到當資料未傳送或者傳送完畢,SCK 的狀態是 低電平,再者 CPHA = 1 即是 偶數邊沿採集。所以傳輸的資料會在 偶數邊沿下降沿 被採集,MOSI 和 MISO 資料的有效訊號需要在 SCK 偶數邊沿保持穩定且被取樣,在非取樣時刻,MOSI 和 MISO 的有效訊號才發生變化。

/**
 * @brief SPI交換一個位元組函式
 * 
 * @param data 待交換的資料
 * @return uint8_t 交換後的資料
 */
uint8_t SPI_SwapOneByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        // SCK上升沿
        SPI_SCK(1);
        // 移出資料
        SPI_MOSI(data & 0x80);
        data <<= 1;
        // SCK下降沿
        SPI_SCK(0);
        // 移入資料
        if (SPI_MISO())
        {
            data |= 0x01;
        }
    }

    return data;
}

【3】、工作模式 2:序列時鐘的奇數邊沿下降沿取樣

序列時鐘的奇數邊沿下降沿取樣圖

  CPOL= 1 && CPHA= 0 的情形,由於配置了 CPOL= 1,可以看到當資料未傳送或者傳送完畢,SCK 的狀態是 高電平,再者 CPHA = 0 即是 奇數邊沿採集。所以傳輸的資料會在 奇數邊沿下升沿 被採集,MOSI 和 MISO 資料的有效訊號需要在 SCK 奇數邊沿保持穩定且被取樣,在非取樣時刻,MOSI 和 MISO 的有效訊號才發生變化。

/**
 * @brief SPI交換一個位元組函式
 * 
 * @param data 待交換的資料
 * @return uint8_t 交換後的資料
 */
uint8_t SPI_SwapOneByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        // 移出資料
        SPI_MOSI(data & 0x80);
        data <<= 1;
        // SCK下降沿
        SPI_SCK(0);
        // 移入資料
        if (SPI_MISO())
        {
            data |= 0x01;
        }
        // SCK上升沿
        SPI_SCK(1);
    }

    return data;
}

【4】、工作模式 3:序列時鐘的偶數邊沿上升沿取樣

序列時鐘的偶數邊沿上升沿取樣圖

  CPOL= 1 && CPHA= 1 的情形,由於配置了 CPOL= 1,可以看到當資料未傳送或者傳送完畢,SCK 的狀態是 高電平,再者 CPHA = 1 即是 偶數邊沿採集。所以傳輸的資料會在 偶數邊沿上升沿 被採集,MOSI 和 MISO 資料的有效訊號需要在 SCK 偶數邊沿保持穩定且被取樣,在非取樣時刻,MOSI 和 MISO 的有效訊號才發生變化。

/**
 * @brief SPI交換一個位元組函式
 * 
 * @param data 待交換的資料
 * @return uint8_t 交換後的資料
 */
uint8_t SPI_SwapOneByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        // SCK下降沿
        SPI_SCK(0);
        // 移出資料
        SPI_MOSI(data & 0x80);
        data <<= 1;
        // SCK上升沿
        SPI_SCK(1);
        // 移入資料
        if (SPI_MISO())
        {
            data |= 0x01;
        }
    }

    return data;
}

三、硬體SPI框圖

  STM32 的 SPI 外設可用作通訊的主機及從機,支援最高的 SCK 時脈頻率為 fpclk/2 ,完全支援 SPI 協議的 4 種模式,資料幀長度可設定為 8 位或 16 位,可設定資料 MSB 先行或 LSB 先行。它還支援雙線全雙工、雙線單向以及單線模式。其中雙線單向模式可以同時使用 MOSI 及 MISO 資料線向一個方向傳輸資料,可以加快一倍的傳輸速度。而單線模式則可以減少硬體接線,當然這樣速率會受到影響。

SPI框圖

  在主機和從機都有一個序列移位暫存器,主機透過向它的 SPI 序列暫存器寫入一個位元組來發起一次傳輸。序列移位暫存器透過 MOSI 訊號線將位元組傳送給從機,從機也將自己的序列移位暫存器中的內容透過 MISO 訊號線返回給主機。這樣,兩個移位暫存器中的內容就被交換。外設的寫操作和讀操作是同步完成的。如果只是進行寫操作,主機只需忽略接收到的位元組。反之,若主機要讀取從機的一個位元組,就必須傳送一個空位元組引發從機傳輸。

四、SPI常用暫存器

4.1、SPI控制暫存器

SPI控制暫存器1

  該暫存器控制著 SPI 很多相關資訊,包括主裝置模式選擇,傳輸方向,資料格式,時鐘極性、時鐘相位和使能等。

  在位 0 CPHA 置 0,資料取樣從第一個時鐘邊沿開始;該位置 1,資料取樣從第二個時鐘邊沿開始;

  在位 1 CPOL 置 0,在空閒狀態時,SCK 保持低電平;該位置 1,空閒狀態時,SCK 保持高電平;

  在位 2 MSTR 置 0,配置為從裝置;該位置 1,配置為主裝置;

  在位 [5:3] BR[2:0] 置 7,使用 256 分頻,速度最低;

  在位 6 SPE 置 0,關閉 SPI 裝置,該位置 1,開啟 SPI 裝置;

  在位 7 LSBFIRST 置 0,MSB 先傳輸;該位置 1,LSB 先傳輸。

  在位 8 SSI 置 1,禁止軟體從裝置,即做主機;

  在位 9 SSM 置 0,使用硬體 SPI 的片選引腳,該位置 1,使用普通 GPIO 模擬片選引腳;

  在位 11 DFF 置 0,使用 8 位資料幀格式,該位置 1,使用 16 位資料幀格式。

  在位 15 RXONLY 置 0,傳輸方式採用的是全雙工模式,該位置 1,採用單工模式。

SPI控制暫存器2

4.2、SPI狀態暫存器

SPI狀態暫存器

  該暫存器是查詢當前 SPI 的狀態的,常用位 0 RXNE 檢測接收緩衝區是否為空來判斷是否完成接收和 位 1 TXE 檢測傳送緩衝區是否為空來判斷髮送是否完成。

4.3、SPI資料暫存器

SPI資料暫存器

  該暫存器是 SPI 資料暫存器,是一個雙暫存器,包括了傳送快取和接收快取。當向該暫存器寫資料的時候,SPI 就會自動傳送,當收到資料的時候,也是存在該暫存器內。

五、IO引腳複用功能

【1】、SPI1 引腳複用及其重對映功能

功能引腳 複用引腳 重對映引腳
SCK PA5 PB3
MOSI PA7 PB5
MISO PA6 PB4
CS PA4 PA15

【2】、SPI2 引腳複用及其重對映功能

功能引腳 複用引腳 重對映引腳
SCK PB10 PB13
MOSI PC3 PB15
MISO PC2 PB14
CS PB12 PB9

【3】、SPI3 引腳複用及其重對映功能

功能引腳 複用引腳 重對映引腳
SCK PC10 PB3
MOSI PC12 PB5
MISO PC11 PB4
CS PA4 PA15

六、SPI配置步驟

6.1、使能對應的時鐘

#define __HAL_RCC_SPI1_CLK_ENABLE()     do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SPI1EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SPI1EN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_SPI2_CLK_ENABLE()     do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_SPI2EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_SPI2EN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_SPI3_CLK_ENABLE()     do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_SPI3EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_SPI3EN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOB_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOC_CLK_ENABLE()  do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)

6.2、配置SPI工作引數

  要使用一個外設首先要對它進行初始化,SPI 的初始化函式,其宣告如下:

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);

  形參 hspi 是 SPI 的控制代碼,SPI_HandleTypeDef 結構體型別,其定義如下:

typedef struct __SPI_HandleTypeDef
{
    SPI_TypeDef *Instance;                              //  SPI暫存器基地址
    SPI_InitTypeDef Init;                               // SPI初始化結構體
    uint8_t *pTxBuffPtr;                                // SPI的傳送資料緩衝區
    uint16_t TxXferSize;                                // SPI傳送資料大小
    __IO uint16_t TxXferCount;                          // SPI傳送端計數器
    uint8_t *pRxBuffPtr;                                // SPI的接收資料緩衝區
    uint16_t RxXferSize;                                // SPI接收資料大小
    __IO uint16_t RxXferCount;                          // SPI接收端計數器

    void (*RxISR)(struct __SPI_HandleTypeDef *hspi);    // SPI的接收端中斷服務函式
    void (*TxISR)(struct __SPI_HandleTypeDef *hspi);    // SPI 的傳送端中斷服務函式

    DMA_HandleTypeDef *hdmatx;                          // SPI傳送引數設定(DMA)
    DMA_HandleTypeDef *hdmarx;                          // SPI接收引數設定(DMA)
    HAL_LockTypeDef Lock;                               // SPI鎖物件
    __IO HAL_SPI_StateTypeDef State;                    // SPI傳輸狀態結構體
    __IO uint32_t ErrorCode;                            // SPI操作錯誤資訊
} SPI_HandleTypeDef;

  InitSPI 初始化結構體,用於配置通訊引數。

#define SPI1                ((SPI_TypeDef *) SPI1_BASE)
#define SPI2                ((SPI_TypeDef *) SPI2_BASE)
#define SPI3                ((SPI_TypeDef *) SPI3_BASE)

  hdmatxhdmarx:配置 SPI 傳送接收資料的 DMA 具體引數

  Lock:對資源操作增加操作 鎖保護,可選 HAL_UNLOCKED 或者 HAL_LOCKED 兩個引數。

  ErrorCode串列埠錯誤操作資訊。主要用於存放 SPI 操作的錯誤資訊。

  SPI_InitTypeDef 這個結構體型別,該結構體用於配置 SPI 的各個通訊引數,具體說明如下:

typedef struct
{
    uint32_t Mode;                  // 設定SPI的主/從機端模式
    uint32_t Direction;             // 設定SPI的單雙向模式
    uint32_t DataSize;              // 資料幀格式
    uint32_t CLKPolarity;           // 時鐘極性CPOL
    uint32_t CLKPhase;              // 時鐘相位CPHA
    uint32_t NSS;                   // 設定NSS引腳由SPI硬體控制還是軟體控制
    uint32_t BaudRatePrescaler;     // 設定時鐘分頻因子
    uint32_t FirstBit;              // 起始位
    uint32_t TIMode;                // 指定是否啟用TI模式
    uint32_t CRCCalculation;        // 硬體CRC是否使能
    uint32_t CRCPolynomial;         // 設定 CRC 多項式
} SPI_InitTypeDef;

  Mode:設定 設定SPI的主/從機端模式。可選值如下:

#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 的資料幀長度,可選 8/16 位

#define SPI_DATASIZE_8BIT               (0x00000000U)
#define SPI_DATASIZE_16BIT              SPI_CR1_DFF

  CLKPolarity設定時鐘極性,可選值如下:

#define SPI_POLARITY_LOW                (0x00000000U)
#define SPI_POLARITY_HIGH               SPI_CR1_CPOL

  CLKPhase設定時鐘相位,可選值如下:

#define SPI_PHASE_1EDGE                 (0x00000000U)
#define SPI_PHASE_2EDGE                 SPI_CR1_CPHA

  NSS設定 NSS 引腳由 SPI 硬體控制還是軟體控制,可選值如下:

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

  BaudRatePrescaler設定時鐘分頻因子,可選值如下:

#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設定起始位是高位先行還是低位先行,可選值如下:

#define SPI_FIRSTBIT_MSB                (0x00000000U)        // 高位先行
#define SPI_FIRSTBIT_LSB                SPI_CR1_LSBFIRST     // 低位先行

  TIMode指定是否啟用 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

  該函式的返回值是 HAL_StatusTypeDef 列舉型別的值,有 4 個,分別是 HAL_OK 表示 成功HAL_ERROR 表示 錯誤HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超時

typedef enum 
{
    HAL_OK = 0x00U,             // 成功
    HAL_ERROR = 0x01U,          // 錯誤
    HAL_BUSY = 0x02U,           // 忙碌
    HAL_TIMEOUT = 0x03U         // 超時
} HAL_StatusTypeDef;

6.3、SPI底層初始化

  HAL 庫中,提供 HAL_GPIO_Init() 函式用於配置 GPIO 功能模式,初始化 GPIO。該函式的宣告如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);

  該函式的第一個形參 GPIOx 用來 指定埠號,可選值如下:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)

  第二個引數是 GPIO_InitTypeDef 型別的結構體變數,用來 設定 GPIO 的工作模式,其定義如下:

typedef struct
{
  uint32_t Pin;         // 引腳號
  uint32_t Mode;        // 模式設定
  uint32_t Pull;        // 上下拉設定
  uint32_t Speed;       // 速度設定
  uint32_t Alternate;   // 複用功能設定
}GPIO_InitTypeDef;

  成員 Pin 表示 引腳號,範圍:GPIO_PIN_0 到 GPIO_PIN_15。

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */

  成員 Mode 是 GPIO 的 模式選擇,有以下選擇項:

#define  GPIO_MODE_AF_PP                        0x00000002U     // 推輓式複用

  成員 Pull 用於 配置上下拉電阻,有以下選擇項:

#define  GPIO_NOPULL        0x00000000U     // 無上下拉
#define  GPIO_PULLUP        0x00000001U     // 上拉
#define  GPIO_PULLDOWN      0x00000002U     // 下拉

  成員 Speed 用於 配置 GPIO 的速度,有以下選擇項:

#define  GPIO_SPEED_FREQ_LOW         0x00000000U    // 低速
#define  GPIO_SPEED_FREQ_MEDIUM      0x00000001U    // 中速
#define  GPIO_SPEED_FREQ_HIGH        0x00000002U    // 高速
#define  GPIO_SPEED_FREQ_VERY_HIGH   0x00000003U    // 極速

  成員 Alternate 用於 配置具體的複用功能,不同的 GPIO 口可以複用的功能不同,具體可參考資料手冊。

#define GPIO_AF5_SPI1          ((uint8_t)0x05)  /* SPI1 Alternate Function mapping        */
#define GPIO_AF5_SPI2          ((uint8_t)0x05)  /* SPI2/I2S2 Alternate Function mapping   */

#define GPIO_AF6_SPI3          ((uint8_t)0x06)  /* SPI3/I2S3 Alternate Function mapping  */

6.4、SPI傳送資料

  HAL 庫提供了 HAL_SPI_Transmit() 函式傳送資料。該函式的宣告如下:

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

  形參 hspiSPI_HandleTypeDef 結構體指標型別的 SPI 控制代碼。形參 pData要傳送資料的緩衝區指標。形參 Size要傳送的資料大小,以位元組為單位。形參 Timeout 設定 超時時間,以毫秒為單位。

  該函式的返回值是 HAL_StatusTypeDef 列舉型別的值,有 4 個,分別是 HAL_OK 表示 成功HAL_ERROR 表示 錯誤HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超時

6.5、SPI讀取資料

  HAL 庫提供了 HAL_SPI_Receive() 函式接收資料。該函式的宣告如下:

HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

  形參 hspiSPI_HandleTypeDef 結構體指標型別的 SPI 控制代碼。形參 pData要接收資料的緩衝區指標。形參 Size要接受收的資料大小,以位元組為單位。形參 Timeout 設定 超時時間,以毫秒為單位。

  該函式的返回值是 HAL_StatusTypeDef 列舉型別的值,有 4 個,分別是 HAL_OK 表示 成功HAL_ERROR 表示 錯誤HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超時

6.6、SPI傳送讀取資料

  HAL 庫提供了 HAL_SPI_TransmitReceive() 函式傳送接收資料。該函式的宣告如下:

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);

  形參 hspiSPI_HandleTypeDef 結構體指標型別的 SPI 控制代碼。形參 pTxData要傳送資料的緩衝區指標。形參 pRxData要接收資料的緩衝區指標。形參 Size要傳送的資料大小,以位元組為單位。形參 Timeout 設定 超時時間,以毫秒為單位。

  該函式的返回值是 HAL_StatusTypeDef 列舉型別的值,有 4 個,分別是 HAL_OK 表示 成功HAL_ERROR 表示 錯誤HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超時

相關文章