STM32—cubeMX+HAL庫的SPI介面使用
摘要:
本文主要介紹STM32的SPI介面、cubeMX軟體配置SPI介面和分析SPI相關程式碼。
STM32之SPI簡介:
(1)SPI協議【Serial Peripheral Interface】
序列外圍裝置介面,是一種高速全雙工的通訊匯流排。主要用在MCU與FLASH\ADC\LCD等模組之間的通訊。
(2)SPI訊號線
SPI 共包含 4 條匯流排。
SS(Slave Select):片選訊號線,當有多個SPI 裝置與 MCU 相連時,每個裝置的這個片選訊號線是與 MCU 單獨的引腳相連的,而其他的 SCK、MOSI、MISO 線則為多個裝置並聯到相同的 SPI 總線上,低電平有效。
SCK (Serial Clock):時鐘訊號線,由主通訊裝置產生,不同的裝置支援的時鐘頻率不一樣,如 STM32 的 SPI 時鐘頻率最大為 f PCLK /2。
MOSI (Master Output Slave Input):主裝置輸出 / 從裝置輸入引腳。主機的資料從這條訊號線輸出,從機由這條訊號線讀入資料,即這條線上資料的方向為主機到從機。
MISO(Master Input Slave Output):主裝置輸入 / 從裝置輸出引腳。主機從這條訊號線讀入資料,從機的資料則由這條訊號線輸出,即在這條線上資料的方向為從機到主機。
下圖是主器件與多個從器件通訊圖。其中SCK,MOSI
(3)SPI特性
單次傳輸可選擇為 8 或 16 位。
波特率預分頻係數(最大為 fPCLK/2) 。
時鐘極性(CPOL)和相位(CPHA)可程式設計設定。
資料順序的傳輸順序可進行程式設計選擇,MSB 在前或 LSB 在前。
注:MSB(Most Significant Bit)是“最高有效位”,LSB(Least Significant Bit)
可觸發中斷的專用傳送和接收標誌。
可以使用 DMA 進行資料傳輸操作。
下圖是STM32的SPI框架圖。
如上圖,MISO資料線接收到的訊號經移位暫存器處理後把資料轉移到接收緩衝區,然後這個資料就可以由我們的軟體從接收緩衝區讀出了。
當要傳送資料時,我們把資料寫入傳送緩衝區,硬體將會把它用移位暫存器處理後輸出到 MOSI資料線。
SCK 的時鐘訊號則由波特率發生器產生,我們可以通過波特率控制位(BR)來控制它輸出的波特率。
控制暫存器 CR1掌管著主控制電路,STM32的 SPI模組的協議設定(時鐘極性、相位等)就是由它來制定的。而控制暫存器 CR2則用於設定各種中斷使能。
最後為 NSS引腳,這個引腳扮演著 SPI協議中的SS片選訊號線的角色,如果我們把 NSS引腳配置為硬體自動控制,SPI模組能夠自動判別它能否成為 SPI的主機,或自動進入 SPI從機模式。但實際上我們用得更多的是由軟體控制某些 GPIO引腳單獨作為SS訊號,這個 GPIO引腳可以隨便選擇。
(4)SPI時鐘時序
根據時鐘極性(CPOL)及相位(CPHA)不同,SPI有四種工作模式。
時鐘極性(CPOL)定義了時鐘空閒狀態電平:
CPOL=0為時鐘空閒時為低電平
CPOL=1為時鐘空閒時為高電平
時鐘相位(CPHA)定義資料的採集時間。
CPHA=0:在時鐘的第一個跳變沿(上升沿或下降沿)進行資料取樣。
CPHA=1:在時鐘的第二個跳變沿(上升沿或下降沿)進行資料取樣。
cubeMX軟體配置SPI:
下面繼續介紹cubeMX軟體配置STM32L152的SPI介面方法。
(1)開啟軟體,選擇對應晶片後,配置好時鐘源;
(2)勾選SPI1為全雙工,硬體NSS關閉,如下圖:
(3)勾選好後,PA5、PA6、PA7如下圖,在配置PA4為普通io口,gpio_output
(4)SPI1的引數配置選擇預設,如下圖所示
(5)生成程式碼,儲存即可。
HAL庫的SPI函式分析:
下面具體分析下生成的SPI函式和函式呼叫。
SPI_HandleTypeDef hspi1; //SPI結構體類定義,下面看其結構體內部的宣告。
下面分析SPI的初始化函式:
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();//使能SPI1時鐘
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);//配置SPI的資料線和時鐘線
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;//主模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES;//全雙工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;//資料位為8位
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;//CPOL=0,low
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;//CPHA為資料線的第一個變化沿
hspi1.Init.NSS = SPI_NSS_SOFT;//軟體控制NSS
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分頻,32M/2=16MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;//最高位先發送
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;//TIMODE模式關閉
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//CRC關閉
hspi1.Init.CRCPolynomial = 10;//預設值,無效
if (HAL_SPI_Init(&hspi1) != HAL_OK)//初始化
{
_Error_Handler(__FILE__, __LINE__);
}
}
利用SPI介面傳送和接收資料主要呼叫以下兩個函式:
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);//傳送資料
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);//接收資料