1. 前言
本文介紹如何編寫符合AliOS Things標準的SPI master設備驅動程序。
2. 頭文件
在程序中使用本文提及的功能應包含頭文件aos/spi_core.h。
3. 數據結構
AliOS Things提供SPI master設備的抽象基礎結構:
typedef struct aos_spi aos_spi_t;
采用結構體嵌套的方式從基礎結構派生出具體的硬件結構。派生類型應包含具體硬件操作所需的各種變量。例如:
typedef struct {
aos_spi_t spi;
/* private data */
void *reg_base;
int irq_num;
} spi_abc_t;
使用宏aos_container_of
實現從基礎結構指針到派生結構指針的轉換。例如:
aos_spi_t *spi_dev = foo();
spi_abc_t *spi_abc = aos_container_of(spi_dev, spi_abc_t, spi);
4. 注冊及注銷
4.1. 注冊
AliOS Things提供如下函數用于注冊SPI master設備:
aos_status_t aos_spi_register(aos_spi_t *spi);
調用注冊函數之前,BSP開發者應自行分配一個aos_spi_t
類型或其派生類型的變量并對包含的如下變量賦值:
dev.id
:uint32_t
類型,表示該設備的ID。不同SPI master設備不能擁有相同的ID。ops
:const aos_spi_ops_t *
類型,指向一組面向硬件的回調函數:
typedef struct {
void (*unregister)(aos_spi_t *spi);
aos_status_t (*startup)(aos_spi_t *spi);
void (*shutdown)(aos_spi_t *spi);
aos_status_t (*start_xfer)(aos_spi_t *spi);
void (*finish_xfer)(aos_spi_t *spi);
void (*abort_xfer)(aos_spi_t *spi);
} aos_spi_ops_t;
flags
:uint32_t
類型,可包含如下字段,各字段使用按位或運算連接:AOS_SPI_F_MODE_0
:表示支持SPI mode0。AOS_SPI_F_MODE_1
:表示支持SPI mode1。AOS_SPI_F_MODE_2
:表示支持SPI mode2。AOS_SPI_F_MODE_3
:表示支持SPI mode3。AOS_SPI_F_RX_1
:表示支持單線接收。AOS_SPI_F_RX_2
:表示支持雙線接收。AOS_SPI_F_RX_4
:表示支持四線接收。AOS_SPI_F_RX_8
:表示支持八線接收。AOS_SPI_F_TX_1
:表示支持單線發送。AOS_SPI_F_TX_2
:表示支持雙線發送。AOS_SPI_F_TX_4
:表示支持四線發送。AOS_SPI_F_TX_8
:表示支持八線發送。AOS_SPI_F_FULL_DUPLEX_1
:表示支持單線接收和單線發送同時進行。AOS_SPI_F_FULL_DUPLEX_2
:表示支持雙線接收和雙線發送同時進行。AOS_SPI_F_FULL_DUPLEX_4
:表示支持四線接收和四線發送同時進行。AOS_SPI_F_FULL_DUPLEX_8
:表示支持八線接收和八線發送同時進行。AOS_SPI_F_MSB_FIRST
:表示支持每字節MSB在前傳輸。AOS_SPI_F_LSB_FIRST
:表示支持每字節LSB在前傳輸。AOS_SPI_F_NO_CS
:表示該設備只與一個SPI slave設備連接,無需操作CS。此標志有效時,注冊函數會把CS數目設置為1。num_cs
:uint32_t
類型,表示該裝置支持的CS數目。min_hz
:uint32_t
類型,表示該設備傳輸數據時的最小時鐘頻率。max_hz
:uint32_t
類型,表示該設備傳輸數據時的最大時鐘頻率。
調用注冊函數之前,BSP開發者應初始化派生類型中的私有變量,并執行具體硬件相關的注冊時初始化工作(例如映射寄存器地址等)。
4.2. 注銷
AliOS Things提供如下函數用于注銷SPI master設備:
aos_status_t aos_spi_unregister(uint32_t id);
調用此函數之后,BSP開發者可回收相關聯的aos_spi_t
類型或其派生類型的變量。
5. 工作模式
一次完整的SPI數據傳輸在AliOS Things中稱為一個message。
相鄰的相同CS且相同mode的一個或多個message在AliOS Things中稱為一個sequence。同一個sequence中CS信號會自始至終保持有效。同一個sequence中的每一message的傳輸方向、數據線數、時鐘頻率可以不同。
每個message根據AliOS Things SPI設備驅動數據緩沖區的尺寸拆分為transfer。每個transfer的最大數據長度為AOS_SPI_BUF_SIZE
。AliOS Things SPI設備驅動以transfer為單位傳輸數據。
6. 回調函數
驅動程序應實現aos_spi_ops_t
定義的一組面向硬件的回調函數。
6.1. unregister
void (*unregister)(aos_spi_t *spi);
unregister
回調函數在設備注銷時被調用,可在該函數中執行具體硬件相關的注銷時反初始化工作(例如解除寄存器地址映射等)。
6.2. startup
aos_status_t (*startup)(aos_spi_t *spi);
startup
回調函數在設備引用計數從0增加到1時被調用,可在該函數中執行具體硬件相關的運行時初始化工作。初始化成功時應返回0;失敗時應返回errno(負值)。
6.3. shutdown
void (*shutdown)(aos_spi_t *spi);
shutdown
回調函數在設備引用計數從1減小到0時被調用,可在該函數中執行具體硬件相關的運行時反初始化工作。
6.4. start_xfer
aos_status_t (*start_xfer)(aos_spi_t *spi);
start_xfer
回調函數在設備開始發起一次transfer傳輸時被調用,可在該函數中控制硬件發起傳輸(例如操作FIFO或DMA)。發起成功后返回0;失敗時應返回errno(負值)。該函數不需等待數據全部傳輸完成后再返回,可在硬件中斷處理程序中處理后續工作。
start_xfer
回調函數或中斷處理程序可訪問spi
指向的如下成員變量獲取或修改傳輸信息:
x.flags
:uint32_t
類型,可包含如下字段,各字段使用按位或運算連接:AOS_SPI_XF_MSG_HEAD
:表示當前transfer是message中的第一個transfer。AOS_SPI_XF_MSG_TAIL
:表示當前transfer是message中的最后一個transfer。AOS_SPI_XF_SEQ_HEAD
:表示當前transfer是sequence中的第一個transfer。此標志有效時應將CS信號設置為有效。AOS_SPI_XF_SEQ_TAIL
:表示當前transfer是sequence中的最后一個transfer。如果此硬件可指定傳輸結束后取消CS信號,應在此標志有效時指定傳輸結束后將CS信號設置為無效。AOS_SPI_XF_RX
:表示正在接收數據。AOS_SPI_XF_TX
:表示正在發送數據。AOS_SPI_XF_RX
標志和AOS_SPI_XF_TX
標志可能同時有效。x.cfg
:uint32_t
類型,可包含如下字段,各字段使用按位或運算連接:Mode,掩碼為
AOS_SPI_MCFG_MODE_MASK
,取值必須為以下當中的一個:AOS_SPI_MCFG_MODE_0
:表示SPI mode0。AOS_SPI_MCFG_MODE_1
:表示SPI mode1。AOS_SPI_MCFG_MODE_2
:表示SPI mode2。AOS_SPI_MCFG_MODE_3
:表示SPI mode3。傳輸線數,掩碼為
AOS_SPI_MCFG_WIDTH_MASK
,取值必須為以下當中的一個:AOS_SPI_MCFG_WIDTH_1
:表示單線傳輸。AOS_SPI_MCFG_WIDTH_2
:表示雙線傳輸。AOS_SPI_MCFG_WIDTH_4
:表示四線傳輸。AOS_SPI_MCFG_WIDTH_8
:表示八線傳輸。AOS_SPI_MCFG_LSB_FIRST
:該標志有效時表示傳輸時LSB在前,否則為MSB在前。x.cs
:uint32_t
類型,表示當前CS序號。x.hz
:uint32_t
類型,表示當前傳輸時鐘頻率。x.pre_cs
:uint32_t
類型,表示從設置mode到CS信號有效之間的時間,單位為納秒。x.post_cs
:uint32_t
類型,表示設置CS信號無效之后的保持時間,單位為納秒。x.pre_clk
:uint32_t
類型,表示從設置CS信號有效到時鐘信號開始產生之間的時間,單位為納秒。x.post_clk
:uint32_t
類型,表示時鐘信號結束到設置CS信號無效之間的時間,單位為納秒。x.timeout
:uint32_t
類型,表示此次傳輸的超時時間,單位為毫秒。start_xfer
回調函數可修改該變量來指定超時時間,默認值為1000。中斷處理程序不應修改該變量。
硬件基于FIFO或DMA的一次傳輸稱為硬件傳輸。一次硬件傳輸的最大長度等于FIFO深度或DMA數據最大長度,考慮到硬件傳輸最大長度有限,一個transfer可能包含一次或多次硬件傳輸。
AliOS Things提供如下函數操作硬件傳輸。這些函數可以在start_xfer
回調函數或中斷處理程序中調用,支持在關中斷或者自旋鎖加鎖環境下調用。
size_t aos_spi_hard_push(aos_spi_t *spi, void *tx_buf, size_t count);
使用aos_spi_hard_push
獲取下一次硬件傳輸的實際長度。參數count
為硬件傳輸最大長度;返回值為下一次硬件傳輸的實際長度。如果當前正在發送數據,下一次硬件傳輸的數據將被復制到tx_buf
指向的空間,驅動程序可將這些數據送給FIFO或DMA并發起下一次硬件傳輸。
bool aos_spi_hard_pull(aos_spi_t *spi, const void *rx_buf, size_t count);
一次硬件傳輸成功后(例如FIFO或DMA操作完成并產生中斷),使用aos_spi_hard_pull
通知設備已完成本次硬件傳輸。參數count
為本次硬件傳輸的實際長度。如果當前正在接收數據,驅動程序應在調用該函數之前從FIFO或DMA獲取數據并存放到rx_buf
指向的空間。如果當前transfer已全部完成,該函數返回true
;否則返回false
,驅動程序應再次調用aos_spi_hard_push
并發起下一次硬件傳輸。
void aos_spi_hard_fail(aos_spi_t *spi);
如果硬件傳輸過程中出現異常,使用aos_spi_hard_fail
通知設備傳輸失敗。
6.5. finish_xfer
void (*finish_xfer)(aos_spi_t *spi);
finish_xfer
回調函數在設備完成一次transfer傳輸時被調用。如果AOS_SPI_XF_SEQ_TAIL
標志有效且此硬件需手動取消CS信號,應在此時將CS信號設置為無效。
6.6. abort_xfer
void (*abort_xfer)(aos_spi_t *spi);
abort_xfer
回調函數在設備傳輸超時或失敗時被調用,應在該函數中取消FIFO或DMA操作,禁用相關中斷,并將CS信號設置為無效。