1. 程式人生 > >STM32—cubeMX+HAL庫的SPI介面使用

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):主裝置輸入 / 從裝置輸出引腳。主機從這條訊號線讀入資料,從機的資料則由這條訊號線輸出,即在這條線上資料的方向為從機到主機。

下圖是主器件與多個從器件通訊圖。其中SCKMOSI

MISO是接在一起的,NSS分別接到不同的IO管腳控制。主器件要和從器件通訊就先拉低對應從器件的NSS管腳使能。預設狀態IO1,IO2,IO3全為高電平,當主器件和從器件1通訊時,拉低IO1管腳使能從器件1。而從器件2,3不使能,不作響應。

(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);//接收資料