天天看點

AliOS Things 硬體抽象層(HAL)對接系列2 — SPI driver porting

HAL層(Hardware abstraction layer) 的目的是為了屏蔽底層不同晶片平台的差異,進而使驅動層上面的軟體不會随晶片平台而改變。AliOS Things定義了全面的HAL抽象層,這個系列主要介紹AliOS ThingsHAL層與不同晶片平台對接的poring要點,并舉例說明。

Hal porting系列2 —— SPI driver porting

一. 接口定義說明

SPI 對外接口定義在 include/hal/soc下面,接口函數主要有以下幾個:
int32_t hal_spi_init(spi_dev_t *spi);
int32_t hal_spi_send(spi_dev_t *spi, const uint8_t *data, uint16_t size, uint32_t timeout);
int32_t hal_spi_recv(spi_dev_t *spi, uint8_t *data, uint16_t size, uint32_t timeout);
int32_t hal_spi_send_recv(spi_dev_t *spi, uint8_t *tx_data, uint8_t *rx_data, uint16_t size, uint32_t timeout);
int32_t hal_spi_finalize(spi_dev_t *spi);

其中,結構體 spi_dev_t 定義為:
typedef struct {
   uint8_t      port;          /* spi port */
    spi_config_t config;  /* spi config */
    void        *priv;          /* priv data */
} spi_dev_t;

結構體 spi_config_t 定義為:
typedef struct {
    uint32_t mode;        /* spi communication mode */
    uint32_t freq;          /* communication frequency Hz */
} spi_config_t;
           

port 指spi的端口号,在一個系統中,可能會有不止一對的spi主從裝置,此時可以通過port值來區分是哪個spi裝置,如spi0、spi1等等;

config是使用者需要指定的配置,這裡給出了2個較為常見的配置資料。分别是:

mode --- 模式 master or slave

freq --- 傳輸頻率,不用硬體支援的頻率不同,一般可選從125K到8M。

若使用者還有其他需要指定的資料,可以通過priv來傳入。

二. 接口使用說明

初始化 spi 裝置:

需要定義spi_dev_t 的變量,舉例說明:

spi_dev_t spi_0 = {.port   = 0,
                   .config = {SPI_MODE, SPI_FREQ_8M},
                   .priv   = 0};

初始化:
hal_spi_init(spi_0);

發送資料: 
ret = hal_spi_send(spi_0 , buf, nbytes, timeout); 

接收資料:
ret = hal_spi_recv(spi_0, buf, nbytes, timeout)

master發送資料同時接收slvae發來的資料:
ret = hal_spi_send_recv( spi_0, buf_tx,  buf_rx, nbytes , timeout)
           

三. hal層對接要點

以 STM32L4 系列為例介紹hal層具體porting步驟:

HAL層接口函數位于/include/hal/soc目錄下,SPI 的HAL層接口函數定義在對應的spi.h中

hal層定義的接口為:

int32_t hal_spi_init(spi_dev_t *spi)
           

STM32L4的初始化接口為:

HAL_StatusTypeDef  HAL_SPI_Init(SPI_HandleTypeDef *hspi)
           

其中 SPI_HandleTypeDef 是ST系列自定義的結構體定義,可參考ST驅動源碼。

由于STM32L4的驅動函數和hal層定義的接口并非完全一緻,我們需要在STM32L4驅動上封裝一層,以對接hal層。

我們需要建立兩個檔案hal_spi_stm32l4.c和hal_spi_stm32l4.h,将封裝層代碼放到這兩個檔案中。

在hal_spi_stm32l4.c中,首先定義相應的STM32L4的spi句柄:

/ handle for spi /

SPI_HandleTypeDef spi1_handle;

然後自定義如下函數,将使用者指定的mode和freq傳入 spi1_handle

int32_t spi_mode_transform(uint32_t mode_hal, uint32_t *mode_stm32l4);
int32_t spi_freq_transform(uint32_t freq_hal, uint32_t *BaudRatePrescaler_stm32l4_stm32l4);
           

代碼示例如下:

int32_t spi1_init(spi_dev_t *spi)
{
    int32_t ret = 0;
    spi1_handle.Instance = SPI1;
    ret = spi_mode_transform(spi->config.mode, &spi1_handle.Init.Mode);
    ret = spi_freq_transform(spi->config.freq, &spi1_handle.Init.BaudRatePrescaler);
    if (ret != 0) {
        return -1;
    }
    /* init spi */
    ret = HAL_SPI_Init(&spi1_handle);
    return ret;
}

int32_t hal_spi_init(spi_dev_t *spi)
{
    int32_t ret = -1;

    if (spi == NULL) {
        return -1;
    }

    /*init spi handle*/
    memset(&spi1_handle, 0, sizeof(spi1_handle));

    switch (spi->port) {
        case PORT_SPI1:
            spi->priv = &spi1_handle;
            ret = spi1_init(spi);
            break;

    /* if ohter spi exist add init code here */
        default:
            break;
    }
    return ret;
}
           

以 Nordic NRF52xxx系列為例:

NRF的spi init驅動定義如下:

ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance,
                            nrf_drv_spi_config_t const * p_config,
                            nrf_drv_spi_evt_handler_t handler,
                            void * p_context)
           

是以,要對接NRF系列的HAL層,需要仔細研究驅動的定義,下面給出示例:

我們在建立的hal_spi_nrf52xxx.h中可以将 上述接口中使用的函數入參統一到一個新的結構體中,并命名為SPI_HandleTypeDef:

typedef struct __SPI_HandleTypeDef
{
    nrf_drv_spi_t spi_dev;
    nrf_drv_spi_config_t spi_config;
    nrf_drv_spi_evt_handler_t spi_handler;
    void * p_context;
} SPI_HandleTypeDef;

在 hal_spi_nrf52xxx.c中 定義相應的NRF52的spi句柄:
/* handle for spi */

SPI_HandleTypeDef  spi0_handle   = {NRF_DRV_SPI_INSTANCE(AOS_PORT_SPI0), NRF_DRV_SPI_DEFAULT_CONFIG, 0, NULL};

(其中的 NRF_DRV_SPI_INSTANCE 和 NRF_DRV_SPI_DEFAULT_CONFIG定義參考NRF驅動源碼)

static int32_t spi0_init(spi_dev_t *spi)
{
    int32_t ret1 = 0, ret2 = 0;

    spi0_handle.spi_config.ss_pin   = SPIM0_SS_PIN;
    spi0_handle.spi_config.miso_pin = SPIM0_MISO_PIN;
    spi0_handle.spi_config.mosi_pin = SPIM0_MOSI_PIN;
    spi0_handle.spi_config.sck_pin  = SPIM0_SCK_PIN;

    ret1 = spi_mode_transform(spi->config.mode, &spi0_handle.spi_config.mode);
    ret2 = spi_freq_transform(spi->config.freq, &spi0_handle.spi_config.frequency);

    if ((ret1 != 0) || (ret2 != 0))
        return -1;

    return nrf_drv_spi_init(&spi0_handle.spi_dev, &spi0_handle.spi_config, NULL, NULL);
}

static int32_t spi_mode_transform(uint32_t mode_hal, uint32_t *mode_nrf52xxx)
{
    nrf_drv_spi_mode_t mode = 0;
    int32_t             ret = 0;

    switch (mode_hal)
    {
 case SPI_MODE_0:
            mode = NRF_DRV_SPI_MODE_0;
     break;
 case SPI_MODE_1:
            mode = NRF_DRV_SPI_MODE_1;
     break;
 case SPI_MODE_2:
            mode = NRF_DRV_SPI_MODE_2;
     break;
 case SPI_MODE_3:
            mode = NRF_DRV_SPI_MODE_3;
     break;

 default:
     ret = -1;
    }

    if(ret == 0)
        *mode_nrf52xxx = (uint32_t)mode;

    return ret;
}

static int32_t spi_freq_transform(uint32_t freq_hal, uint32_t *freq_nrf52xxx)
{
    nrf_drv_spi_frequency_t freq = 0;
    int32_t                  ret = 0;

    switch (freq_hal)
    {
 case SPI_FREQ_125K:
            freq = NRF_SPI_FREQ_125K;
     break;
 case SPI_FREQ_250K:
            freq = NRF_SPI_FREQ_250K;
     break;
 case SPI_FREQ_500K:
            freq = NRF_SPI_FREQ_500K;
     break;
 case SPI_FREQ_1M:
            freq = NRF_SPI_FREQ_1M;
     break;
 case SPI_FREQ_2M:
            freq = NRF_SPI_FREQ_2M;
     break;
 case SPI_FREQ_4M:
            freq = NRF_SPI_FREQ_4M;
     break;
 case SPI_FREQ_8M:
            freq = NRF_SPI_FREQ_8M;
     break;

 default:
     ret = -1;
    }

    if(ret == 0)
        *freq_nrf52xxx = (uint32_t)freq;

    return ret;
}
           

發送資料:

ret = hal_spi_send(spi_0 , buf, nbytes, timeout);
           

表示在timeout時間範圍内,将buf開始的大小為nbytes位元組的資料通過spi_0 裝置發送。

調用這個接口時需要注意兩點:

  1. 需要對傳回值ret進行判斷,不用晶片平台驅動的傳回值不同:

ST系列:

typedef enum
{
  HAL_OK       = 0x00,
  HAL_ERROR    = 0x01,
  HAL_BUSY     = 0x02,
  HAL_TIMEOUT  = 0x03
} HAL_StatusTypeDef;
           

NRF系列:

AliOS Things 硬體抽象層(HAL)對接系列2 — SPI driver porting

要根據不同傳回值的定義判斷驅動此時的狀态。

2.不同晶片平台驅動中,對timeout的了解不同:

ST系列,底層發送驅動中會對 timeout進行判斷,若timeout時間到仍未發送完成,則傳回 HAL_TIMEOUT ;

NRF系列,若不使用中斷,則發送驅動中采用的是while死等的操作方式,此時參數 timeout将不起作用。

資料接收 hal_spi_recv 接口的對接與發送類似。

繼續閱讀