如何写便于移植的驱动,以软件模拟 QSPI 为例

SAL_QSPI ( Sofetware Abstraction Layer - QSPI )

  • 这是一个极为通用的软件抽象 4 线 spi 模拟驱动,移植到不同平台只需要实现两个函数

  • 仓库链接:https://gitee.com/wattercutter/sal_qspi

在这里插入图片描述

用法

在工程中加入 ./include/_sal_qspi_mod.h./src/_sal_qspi_mod.c, 调用方法见 ./demo/test_main.c

#include "_sal_qspi_mod.h"

void delay_us(unsigned int us_){
    // porting
};
void set_pin(unsigned int pin_, unsigned int val_){
    // porting
};

int main()
{
    sal_qspi_info_t devx = {    
        .cs_num_ = 1,
        .clk_pin_ = 2,
        .cs_pins_ = {3,4,5,6,7,8,9,10,},
        .dat_pins_ = {11,12,13,14}
    };

    // registering delay & set_pin function, reset 1 cs pin, reset dat pins
    sal_qspi_init(
        (sal_qspi_info_t*)&devx,
        (unsigned long long)1,
        // _STAUS_HIGH_LEVEL,
        // _STAUS_HIGH_LEVEL, 
        delay_us,
        set_pin
    );

    while(1){
        // write [64]-bits data [0x12345678abcdef] to selected chip [(unsigned long long)0], info contained in [devx]
        sal_qspi_wdat_64bit(
            (sal_qspi_info_t*)&devx,
            (unsigned long long)0,
            (unsigned long long)0x12345678abcdef,
            (unsigned long long)64
        );
    }
}

逐块说明一下:

1

#include "_sal_qspi_mod.h"

这是驱动的头文件,调用驱动的程序需要 #include 这个文件

2

void delay_us(unsigned int us_){
    // porting
};
void set_pin(unsigned int pin_, unsigned int val_){
    // porting
};

这是要在初始化时传入的两个函数,

  • delay_us:以 1us 为基数进行延时,传参 us_ 表示延时 us_ 个微妙
  • set_pin:把指定引脚 pin_ 的输出设置为 val_
    这两个函数用于适配不同的硬件平台,比如使用不同的处理器就需要用对应的库/驱动实现这两个接口,供给 sal_qspi 的驱动去调用,这样就不需要修改驱动本身了。

这实际上是 sal_qspi 通过函数指针实现了方法的注册,减少了使用者阅读驱动源码的负担。

3

sal_qspi_info_t devx = {    
	.cs_num_ = 1,
	.clk_pin_ = 2,
	.cs_pins_ = {3,4,5,6,7,8,9,10,},
	.dat_pins_ = {11,12,13,14}
};

这是准备 sal_sqpi 的基本信息,用到的结构体的原型如下:

typedef struct sal_qspi_info_{
    unsigned int cs_num_;
    unsigned int cs_pins_[SAL_QSPI_CS_MAX_NUM];
    unsigned int clk_pin_;
    unsigned int dat_pins_[4];
}sal_qspi_info_t;
  • cs_num_ 指的是片选信号的数量
  • cs_pins_[SAL_QSPI_CS_MAX_NUM] 指的是各个片选信号对应的 gpio 引脚号
  • clk_pin_ 指的是时钟信号对应的 gpio 引脚号
  • dat_pins[4] 指的是 4 个数据信号线对应的 gpio 引脚号
    这样我们就知道上面的代码描述的 qspi 基本信息就是:有 1 个 片选,对应的引脚号是 3,时钟信号对应的引脚号是 2,4 个数据线对应的引脚号分别是 11、12、13、14

4

// registering delay & set_pin function, reset 1 cs pin, reset dat pins
    sal_qspi_init(
        (sal_qspi_info_t*)&devx,
        (unsigned long long)1,
        delay_us,
        set_pin
    );

这里是把前面实现的延时函数、gpio 操作函数两个接口函数注册到我们要初始化的 sal_qspi 设备信息中。用到的函数原型如下:

void 
sal_qspi_init(
    sal_qspi_info_t* sal_qspi_dev_,
    unsigned long long cs_num,
    void(*delay_us_)(unsigned int us_),
    void(*set_pin_val_)(unsigned int pin_, unsigned int val_)
); // register delay call back function, reset gpio pin output 

注意后两个参数是函数指针,一个是 void(*)(unsigned int) 类型的,另一个是 void(*)(unsigned int, unsigned int) 类型的。

5

发送数据的函数原型如下:

void 
sal_qspi_wdat_64bit(
    sal_qspi_info_t* sal_qspi_dev_,
    unsigned long long csn_,
    unsigned long long dat_,
    unsigned long long bits_num_
);

发送数据的位宽 bits_num_ 范围为 1-64 bits,需要是 4 的整数倍,不同位宽的发送时序如下:

bits_num_==24

在这里插入图片描述

bits_num_==8

在这里插入图片描述

例程

  • 路径:./demo/test_main.c
  • 包含路径:./include/
  • 源文件:./src/*.c

移植说明

参考 …/demo/test_main.c,仅需实现以下两个函数(函数名任意)并作为参数在初始化时传入

void delay_us(unsigned int us_){
    // porting
};
void set_pin(unsigned int pin_, unsigned int val_){
    // porting
};

函数说明如下:

  • delay_us:以 1us 为基数进行延时,传参 us_ 表示延时 us_ 个微妙
  • set_pin:把指定引脚(pin_)的输出设置为 _val

约束

  • 初始化不包含对应引脚的复用配置,请在 sal_qspi 初始化前完成相应配置
GD32是一款由STMicroelectronics生产的ARM Cortex-M微控制器系列,它支持软件模拟外设,包括QSPI(Quad-SPI闪存)。ESP-PSRAM64H是一种外部存储器,通常用于扩大单片机的内存容量。 在GD32上模拟QSPIESP-PSRAM64H的一个基本步骤可能会涉及以下几个部分: 1. **初始化QSPI模块**: 首先,你需要配置QSPI的工作模式、时钟源、数据线以及选通信号。如,在GD32F4xx的HAL库中,你可以使用`stm32f4xx_hal_qspi_init()`函数来完成这个步骤。 ```c void qspi_init(void) { HAL_QSPI_Init(&hqspi); HAL_QSPI_MspInit(&hqspi); } ``` 2. **配置SPI Flash地址**: 对于ESP-PSRAM64H,你需要设置正确的起始地址和数据传输长度。 ```c uint32_t address = 0x00; // 开始地址 uint32_t length = 1024; // 数据块大小 ``` 3. **软件模拟读取**: 使用软件模拟,你需要手动控制QSPI的寄存器操作来发送读命令,并处理返回的数据。如,读取操作可能涉及到发送读取命令和等待响应。 ```c void read_data(void) { hqspi.Instance->DR = QSPI_READ_CMD; // 发送读取命令 while (!(hqspi.Instance->SR & QSPI_FLAG_RXNE)); // 等待接收缓冲区非空 memcpy(data_buffer, hqspi.Instance->DR, sizeof(data_buffer)); // 处理接收到的数据 } ``` 4. **模拟入**: 入时则相反,需要发送入命令和数据,然后确认操作完成。 ```c void write_data(uint8_t* data) { hqspi.Instance->DR = *data++; // 发送数据 if (length > 1) { hqspi.Instance->DR = *data++; // ...继续发送剩余数据 } hqspi.Instance->DR = QSPI_WRITE_CMD; while (!(hqspi.Instance->SR & QSPI_FLAG_BSY)); // 等待操作完成 } ``` 5. **清理资源**: 在操作完成后别忘了关闭QSPI和相关的中断。 ```c void qspi_cleanup() { HAL_QSPI_DeInit(&hqspi); HAL_QSPI_MspDeInit(&hqspi); } ``` 请注意,以上示简化了实际过程,真正的实现可能需要处理更多的错误检查、同步和异步通信细节。此外,ESP-PSRAM64H的具体交互协议可能会有所不同,需查阅其文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值