1. 程式人生 > >對STM32 HAL庫的一些思考(一)SPI通訊的資料格式問題

對STM32 HAL庫的一些思考(一)SPI通訊的資料格式問題

眾所周知,STM32是一款價效比比較高的ARM晶片,並且它擁有極為豐富的外設,方便實現大部分的功能。2014年,意法半導體公司推出HAL(Hardware Abstracted Library)和配套的STM32CubeMX,更是讓STM32的開發變得易如反掌,使得繁複的初始化程式碼僅需簡單配置即可完成,這是一次重大的變革。然而,HAL庫中的某些功能的確讓人摸不著頭腦,比如我們今天的主角SPI。

關於SPI

SPI是Motorola推出的一種通訊介面,它具有很多優點,這裡不贅述,但是在HAL中,使用這個模組對應的庫函式並非易事,那麼問題出在哪裡呢?

SPI傳送16位資料的問題

根據參考手冊,STM32的SPI擁有一個16位的資料暫存器,記為DR,但是我們看HAL庫的函式,以輪詢傳送為例

/**
  * @brief  Transmit an amount of data in blocking mode.
  * @param  hspi pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @param  pData pointer to data buffer
  * @param
Size amount of data to be sent * @param Timeout Timeout duration * @retval HAL status */
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

可以看到,傳入該函式的資料指標是八位的,但是在C語言中,指標的型別是不可以混用的,於是問題來了,如何充分利用這個16位的DR暫存器呢?

在解決這個問題之前,先看一小段C語言程式碼

#include "stdio.h"
#include "stdint.h"

uint16_t re(uint8_t* p)
{
    uint16_t i =  *(uint16_t*)p;
    return (i);
}

int main(void)
{
    uint16_t i = 0x1234;
    uint8_t* p = &i;

    printf_s("0x%x \r\n", re(p));

    return (0);
}

這段程式碼展示瞭如何處理不同型別的指標,如果將uint16_t的指標強行解釋為uint8_t,則新指標的資料為原指標的低八位,不過此時原有資料在記憶體中儲存沒有發生變化,因此我們仍可以用原有指標取出資料;同時,這種轉換在C語言中是不安全的行為,常常產生警告,然而這種轉換如果作為表示式引數傳入函式中,則是合法的,所以以上程式碼的輸出為0x1234。

理解了這一點後,就能很方便的理解SPI的函數了,在該函式原始碼中,關於16bit傳送的部分如下:

  /* Transmit data in 16 Bit mode */
  if(hspi->Init.DataSize == SPI_DATASIZE_16BIT)
  {
    if((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01))
    {
      hspi->Instance->DR = *((uint16_t *)pData);
      pData += sizeof(uint16_t);
      hspi->TxXferCount--;
    }
    /* Transmit data in 16 Bit mode */
    while (hspi->TxXferCount > 0U)
    {
      /* Wait until TXE flag is set to send data */
      if(__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
      {
          hspi->Instance->DR = *((uint16_t *)pData);
          pData += sizeof(uint16_t);
          hspi->TxXferCount--;
      }
      else
      {
        /* Timeout management */
        if((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout)))
        {
          errorcode = HAL_TIMEOUT;
          goto error;
        }
      }
    }
  }

可以看到對DR暫存器賦值時,採用了型別轉換的方式,保證從記憶體中取出的資料為16位。
如果需要使用SPI傳送16位資料,可以按照下例:

int main(void)
{
    uint16_t arr[]={...};
    HAL_SPI_Transmit(&hspi1, (uint8_t* )arr, sizeof(arr), HAL_MAX_DELAY);
}

通過雙機通訊或者將資料寫入flash,可以看到資料是正確的,不過依然建議對Flash或者E2PROM,採取單位元組讀寫方式,安全可控。

全文完