最新教程下載: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 初學者重要提示
- STM32H7的SPI支持4到32bit數據傳輸,而STM32F1和F4系列僅支持8bit或者16bit。
- STM3F407的主頻168MHz時,SPI1最高通信時鍾是42MHz,而SPI2和SPI3是21MHz。
- SPI總線的片選引腳SS在單一的主從器件配置下是可選的,一般情況下可以不使用。
- 搜集了幾篇質量比較高的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表示忙,正在使用中。
注意事項:
- 函數HAL_SPI_MspInit用於初始化SPI的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能。由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
- 如果形參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接口芯片,以便選擇合適的驅動方式。