设计说明
源码说明
相关模块 | 源码路径 |
---|---|
SPI Driver framework | kernel/rt-thread/components/drivers/spi |
Driver | bsp/artinchip/drv/qspi |
HAL | bsp/artinchip/hal/qspi |
模块架构
RT-Thread 透过其定义的 QSPI/SPI 驱动框架,向上提供了 QSPI/SPI 接口,并且通过这一层接口支持各种应用,包括 SPI NAND, SPI NOR。
ArtInChip 提供了 QSPI HAL 层,并且实现了对接 RT-Thread 的驱动层。由于 QSPI 传输需要使用 DMA,因此 DMA HAL 是一个相关模块。
HAL 与 DRV
ArtInChip 的 QSPI 驱动按照 HAL 层 + Driver 层的结构进行设计,其中 HAL 层为硬件抽象层,提供系统无关的硬件驱动实现; 在 HAL 层之上,可根据不同 RTOS 的驱动框架,实现对应的 QSPI DRV 层进行对接。
QSPI HAL 的特点:
-
无状态
-
支持 sync、async(irq) 模式
-
支持 DMA、非 DMA 模式
QSPI HAL 相关的设备操作都需要通过 Handle 的方式进行。 由于 HAL 其内部无状态,不会进行空间分配,因此 Handle 的空间需要外部申请并且传入, 由 HAL 层进行使用。
关键流程设计
-
添加 QSPI 总线在 RT-Thread 板级初始化过程中,会调用 rt_hw_qspi_bus_init() 函数,将板子支持的 QSPI 控制器(总线) 添加到系统中。
INIT_BOARD_EXPORT(rt_hw_qspi_bus_init); // bsp/artinchip/drv/qspi/drv_qspi.c |-> rt_qspi_bus_register(); // kernel/rt-thread/components/drivers/spi/qspi_core.c |-> rt_spi_bus_register(); kernel/rt-thread/components/drivers/spi/spi_core.c |-> rt_spi_bus_device_init(); // kernel/rt-thread/components/drivers/spi/spi_dev.c |-> rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
-
添加 QSPI 设备将 QSPI 设备挂载到总线上。
aic_qspi_bus_attach_device("qspi0", "GD25B127D", 0, 4, RT_NULL, RT_NULL); // bsp/artinchip/drv/qspi/drv_qspi.c |-> rt_spi_bus_attach_device(); // kernel/rt-thread/components/drivers/spi/spi_core.c |-> rt_spidev_device_init(device, name); // kernel/rt-thread/components/drivers/spi/spi_dev.c |-> rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
-
初始化流程当 QSPI 设备需要使用之前,必须进行初始化配置。
rt_sfud_flash_probe_ex(); |-> rt_qspi_configure(qspi_dev, qspi_cfg); // kernel/rt-thread/components/drivers/spi/qspi_core.c |-> rt_spi_configure(&device->parent, &cfg->parent); // kernel/rt-thread/components/drivers/spi/spi_core.c |-> device->bus->ops->configure(device, &device->config); qspi_configure(device, &device->config); // bsp/artinchip/drv/qspi/drv_qspi.c |-> hal_qspi_master_init(&qspi->handle, &cfg);
-
中断处理流程
QSPI HAL 层提供了中断处理函数,但是是否使用中断模式由 DRV 层决定。 Luban-Lite SDK 中对接 RT-Thread 时,使能了中断处理模式。
首先在初始化时,注册了对应的中断处理函数。qspi_configure(); // bsp/artinchip/drv/qspi/drv_qspi.c |-> aicos_request_irq(qspi->irq_num, qspi_irq_handler, 0, NULL, (void *)&qspi->handle);
中断发生时,QSPI DRV 层的中断处理函数,调用 HAL 层的中断处理函数进行处理。
qspi_irq_handler(); p/artinchip/drv/qspi/drv_qspi.c |-> hal_qspi_master_irq_handler(h);
数据结构
struct qspi_master_config {
uint32_t idx;
uint32_t clk_in_hz;
uint32_t clk_id;
bool bit_mode;
bool wire3_en;
bool lsb_en;
bool cs_auto;
uint8_t cs_polarity;
uint8_t cpol;
uint8_t cpha;
};
struct qspi_master_dma_config {
uint32_t port_id;
uint32_t tx_bus_width;
uint32_t tx_max_burst;
uint32_t rx_bus_width;
uint32_t rx_max_burst;
};
struct qspi_transfer {
uint8_t *tx_data;
uint8_t *rx_data;
uint32_t data_len;
};
struct qspi_master_state {
uint32_t idx;
qspi_master_async_cb cb;
void *cb_priv;
uint32_t status;
uint32_t clk_id;
uint32_t bus_hz;
uint32_t bus_width;
struct qspi_master_dma_config dma_cfg;
void *dma_tx;
void *dma_rx;
uint8_t *async_tx; /* Used in Async Non-DMA mode */
uint8_t *async_rx; /* Used in Async Non-DMA mode */
uint32_t async_tx_remain; /* Used in Async Non-DMA mode */
uint32_t async_rx_remain; /* Used in Async Non-DMA mode */
uint32_t work_mode;
uint32_t done_mask;
};
接口设计
RT-Thread 中的 QSPI 接口设计,在 RT-Thread 的文档中已经有详细说明。 此处介绍 QSPI HAL 层的接口设计。
函数原型 | int hal_qspi_master_init(qspi_master_handle *h, struct qspi_master_config *cfg) |
功能说明 | QSPI 控制器的初始化函数 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
struct qspi_master_config *cfg
QSPI 控制器的初始化配置参数
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | 初始化时,Handle 的空间由使用者负责分配和释放 |
函数原型 | int hal_qspi_master_deinit(qspi_master_handle *h) |
功能说明 | QSPI 控制器的反初始化函数,在 QSPI 控制器关闭时使用 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | - |
函数原型 | int hal_qspi_master_set_cs(qspi_master_handle *h, uint32_t cs_num, bool enable) |
功能说明 | 设置 SPI 设备的片选信号 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
bool enable
CS 信号是否有效
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | - |
函数原型 | int hal_qspi_master_set_bus_freq(qspi_master_handle *h, uint32_t bus_hz) |
功能说明 | 设置 QSPI 控制器的接口总线工作时钟 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
uint32_t bus_hz
接口总线的工作时钟
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | QSPI 控制器的模块输入时钟,在 hal_qspi_master_init() 调用时配置 |
函数原型 | int hal_qspi_master_set_bus_width(qspi_master_handle *h, uint32_t bus_width) |
功能说明 | 设置传输所使用的总线位宽 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
uint32_t bus_width
总线位宽,取值可以为 1、2、4
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | - |
函数原型 | int hal_qspi_master_dma_config(qspi_master_handle *h, struct qspi_master_dma_config *cfg) |
功能说明 | 配置和使能 DMA |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
struct qspi_master_dma_config *cfg
QSPI 控制器使用的 TX、RX DMA 通道配置
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | 需要使能系统 DMA 进行数据传输,则需要在初始化时调用本 API,否则不使用系统 DMA。 |
函数原型 | int hal_qspi_master_register_cb(qspi_master_handle *h, qspi_master_async_cb cb, void *priv) |
功能说明 | 注册异步传输的回调函数 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
qspi_master_async_cb cb
回调函数指针
void *priv
回调函数的私有数据
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | - |
函数原型 | int hal_qspi_master_transfer_async(qspi_master_handle *h, struct qspi_transfer *t); |
功能说明 | 异步数据传输接口 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
struct qspi_transfer *t
传输的数据信息
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | 该函数启动硬件传输即返回,具体是否完成,需要检查硬件状态,或者等待回调函数 |
函数原型 | int hal_qspi_master_transfer_sync(qspi_master_handle *h, struct qspi_transfer *t) |
功能说明 | 同步数据传输接口 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
struct qspi_transfer *t
传输的数据信息
|
返回值 |
0: 成功
其他: 失败
|
注意事项 | 该函数为同步函数,阻塞式调用,数据传输完成或者出错才返回。 |
函数原型 | int hal_qspi_master_get_status(qspi_master_handle *h) |
功能说明 | 传输状态读取接口 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
|
返回值 |
0: 传输成功完成
其他: 传输失败,具体错误信息可参考 hal_qspi.h 中的定义
|
注意事项 | - |
函数原型 | void hal_qspi_master_irq_handler(qspi_master_handle *h) |
功能说明 | QSPI 中断处理接口 |
参数定义 |
qspi_master_handle *h
QSPI 控制器 Handle
|
返回值 |
无
|
注意事项 | 使用者需要向系统注册中断,并且在中断回调函数中调用本 API 进行中断处理 |