SPI设备驱动程序开发指南

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);
int32_t aos_status_t
Definition: kernel.h:185

调用注册函数之前,BSP开发者应自行分配一个aos_spi_t类型或其派生类型的变量并对包含的如下变量赋值:

  • dev.iduint32_t类型,表示该设备的ID。不同SPI master设备不能拥有相同的ID。
  • opsconst 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;
  • flagsuint32_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_hzuint32_t类型,表示该设备传输数据时的最小时钟频率。
  • max_hzuint32_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.flagsuint32_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.cfguint32_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.csuint32_t类型,表示当前CS序号。
  • x.hzuint32_t类型,表示当前传输时钟频率。
  • x.pre_csuint32_t类型,表示从设置mode到CS信号有效之间的时间,单位为纳秒。
  • x.post_csuint32_t类型,表示设置CS信号无效之后的保持时间,单位为纳秒。
  • x.pre_clkuint32_t类型,表示从设置CS信号有效到时钟信号开始产生之间的时间,单位为纳秒。
  • x.post_clkuint32_t类型,表示时钟信号结束到设置CS信号无效之间的时间,单位为纳秒。
  • x.timeoutuint32_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信号设置为无效。