一、SPI通訊協議簡介
SPI 是 Serial Peripheral interface 縮寫,顧名思義就是序列外圍裝置介面。SPI 通訊協議是 Motorola 公司首先在其 MC68HCXX 系列處理器上定義的。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 種模式。
/**
* @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 序列暫存器寫入一個位元組來發起一次傳輸。序列移位暫存器透過 MOSI 訊號線將位元組傳送給從機,從機也將自己的序列移位暫存器中的內容透過 MISO 訊號線返回給主機。這樣,兩個移位暫存器中的內容就被交換。外設的寫操作和讀操作是同步完成的。如果只是進行寫操作,主機只需忽略接收到的位元組。反之,若主機要讀取從機的一個位元組,就必須傳送一個空位元組引發從機傳輸。
四、SPI常用暫存器
4.1、SPI控制暫存器
該暫存器控制著 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,採用單工模式。
4.2、SPI狀態暫存器
該暫存器是查詢當前 SPI 的狀態的,常用位 0 RXNE 檢測接收緩衝區是否為空來判斷是否完成接收和 位 1 TXE 檢測傳送緩衝區是否為空來判斷髮送是否完成。
4.3、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;
Init:SPI 初始化結構體,用於配置通訊引數。
#define SPI1 ((SPI_TypeDef *) SPI1_BASE)
#define SPI2 ((SPI_TypeDef *) SPI2_BASE)
#define SPI3 ((SPI_TypeDef *) SPI3_BASE)
hdmatx,hdmarx:配置 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);
形參 hspi 是 SPI_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);
形參 hspi 是 SPI_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);
形參 hspi 是 SPI_HandleTypeDef 結構體指標型別的 SPI 控制代碼。形參 pTxData 是 要傳送資料的緩衝區指標。形參 pRxData 是 要接收資料的緩衝區指標。形參 Size 是 要傳送的資料大小,以位元組為單位。形參 Timeout 設定 超時時間,以毫秒為單位。
該函式的返回值是 HAL_StatusTypeDef 列舉型別的值,有 4 個,分別是 HAL_OK 表示 成功,HAL_ERROR 表示 錯誤,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超時。