Home

Awesome

SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库


0、SFUD 是什么

SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。

1、为什么选择 SFUD

2、SFUD 如何使用

2.1 已支持 Flash

下表为所有已在 Demo 平台上进行过真机测试过的 Flash。显示为 不支持 SFDP 标准的 Flash 已经在 Flash 参数信息表中定义,更多不支持 SFDP 标准的 Flash 需要大家以后 共同来完善和维护 (Github|OSChina|Coding)

如果觉得这个开源项目很赞,可以点击 项目主页 右上角的 Star ,同时把它推荐给更多有需要的朋友。

型号制造商容量最高速度SFDP 标准QSPI 模式备注
W25Q40BVWinbond4Mb50Mhz不支持双线已停产
W25Q80DVWinbond8Mb104Mhz支持双线
W25Q16BVWinbond16Mb104Mhz不支持双线by slipperstree
W25Q16CVWinbond16Mb104Mhz支持未测试
W25Q16DVWinbond16Mb104Mhz支持未测试by slipperstree
W25Q32BVWinbond32Mb104Mhz支持双线
W25Q64CVWinbond64Mb80Mhz支持四线
W25Q128BVWinbond128Mb104Mhz支持四线
W25Q256FVWinbond256Mb104Mhz支持四线
MX25L3206EMacronix32Mb86MHz支持双线
MX25L3233FMacronix32Mb133MHz支持未测试by JiapengLi
KH25L4006EMacronix4Mb86Mhz支持未测试by JiapengLi
KH25L3206EMacronix32Mb86Mhz支持双线
SST25VF016BMicrochip16Mb50MHz不支持不支持SST 已被 Microchip 收购
M25P40Micron4Mb75Mhz不支持未测试by redocCheng
M25P80Micron8Mb75Mhz不支持未测试by redocCheng
M25P32Micron32Mb75Mhz不支持不支持
EN25Q32BEON32Mb104MHz不支持未测试
GD25Q16BGigaDevice16Mb120Mhz不支持未测试by TanekLiang
GD25Q32CGigaDevice32Mb120Mhz不支持未测试by gaupen1186
GD25Q64BGigaDevice64Mb120Mhz不支持双线
S25FL216KCypress16Mb65Mhz不支持双线
S25FL032PCypress32Mb104Mhz不支持未测试by yc_911
S25FL164KCypress64Mb108Mhz支持未测试
A25L080AMIC8Mb100Mhz不支持双线
A25LQ64AMIC64Mb104Mhz支持支持
F25L004ESMT4Mb100Mhz不支持不支持
PCT25VF016BPCT16Mb80Mhz不支持不支持SST 授权许可,会被识别为 SST25VF016B
AT45DB161EADESTO16Mb85MHz不支持不支持ADESTO 收购 Atmel 串行闪存产品线
NM25Q128EVNor_Mem128Mb未测试不支持未测试SFDP可能会读取到信息后识别为超过32Gb
P25D40HPUYA4Mb未测试支持未测试by Shan
P25Q80HPUYA8Mb未测试支持未测试by Shan

注:QSPI 模式中,双线表示支持双线快读,四线表示支持四线快读。

一般情况下,支持四线快读的 FLASH 也支持双线快读。

2.2 API 说明

先说明下本库主要使用的一个结构体 sfud_flash 。其定义位于 /sfud/inc/sfud_def.h。每个 SPI Flash 会对应一个该结构体,该结构体指针下面统称为 Flash 设备对象。初始化成功后在 sfud_flash->chip 结构体中会存放 SPI Flash 的常见参数。如果 SPI Flash 还支持 SFDP ,还可以通过 sfud_flash->sfdp 看到更加全面的参数信息。以下很多函数都将使用 Flash 设备对象作为第一个入参,实现对指定 SPI Flash 的操作。

2.2.1 初始化 SFUD 库

将会调用 sfud_device_init ,初始化 Flash 设备表中的全部设备。如果只有一个 Flash 也可以只使用 sfud_device_init 进行单一初始化。

注意:初始化完的 SPI Flash 默认都 已取消写保护 状态,如需开启写保护,请使用 sfud_write_status 函数修改 SPI Flash 状态。

sfud_err sfud_init(void)

2.2.2 初始化指定的 Flash 设备

sfud_err sfud_device_init(sfud_flash *flash)
参数描述
flash待初始化的 Flash 设备

2.2.3 使能快速读模式(仅当 SFUD 开启 QSPI 模式后可用)

当 SFUD 开启 QSPI 模式后,SFUD 中的 Flash 驱动支持使用 QSPI 总线进行通信。相比传统的 SPI 模式,使用 QSPI 能够加速 Flash 数据的读取,但当数据需要写入时,由于 Flash 本身的数据写入速度慢于 SPI 传输速度,所以 QSPI 模式下的数据写入速度提升并不明显。

所以 SFUD 对于 QSPI 模式的支持仅限于快速读命令。通过该函数可以配置 Flash 所使用的 QSPI 总线的实际支持的数据线最大宽度,例如:1 线(默认值,即传统的 SPI 模式)、2 线、4 线。

设置后,SFUD 会去结合当前设定的 QSPI 总线数据线宽度,去 QSPI Flash 扩展信息表 中匹配最合适的、速度最快的快速读命令,之后用户在调用 sfud_read() 时,会使用 QSPI 模式的传输函数发送该命令。

sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width)
参数描述
flashFlash 设备
data_line_widthQSPI 总线支持的数据线最大宽度,例如:1、2、4

2.2.4 获取 Flash 设备对象

在 SFUD 配置文件中会定义 Flash 设备表,负责存放所有将要使用的 Flash 设备对象,所以 SFUD 支持多个 Flash 设备同时驱动。设备表的配置在 /sfud/inc/sfud_cfg.hSFUD_FLASH_DEVICE_TABLE 宏定义,详细配置方法参照 2.3 配置方法 Flash)。本方法通过 Flash 设备位于设备表中索引值来返回 Flash 设备对象,超出设备表范围返回 NULL

sfud_flash *sfud_get_device(size_t index)
参数描述
indexFlash 设备位于 FLash 设备表中的索引值

2.2.5 读取 Flash 数据

sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data)
参数描述
flashFlash 设备对象
addr起始地址
size从起始地址开始读取数据的总大小
data读取到的数据

2.2.6 擦除 Flash 数据

注意:擦除操作将会按照 Flash 芯片的擦除粒度(详见 Flash 数据手册,一般为 block 大小。初始化完成后,可以通过 sfud_flash->chip.erase_gran 查看)对齐,请注意保证起始地址和擦除数据大小按照 Flash 芯片的擦除粒度对齐,否则执行擦除操作后,将会导致其他数据丢失。

sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size)
参数描述
flashFlash 设备对象
addr起始地址
size从起始地址开始擦除数据的总大小

2.2.7 擦除 Flash 全部数据

sfud_err sfud_chip_erase(const sfud_flash *flash)
参数描述
flashFlash 设备对象

2.2.8 往 Flash 写数据

sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
参数描述
flashFlash 设备对象
addr起始地址
size从起始地址开始写入数据的总大小
data待写入的数据

2.2.9 先擦除再往 Flash 写数据

注意:擦除操作将会按照 Flash 芯片的擦除粒度(详见 Flash 数据手册,一般为 block 大小。初始化完成后,可以通过 sfud_flash->chip.erase_gran 查看)对齐,请注意保证起始地址和擦除数据大小按照 Flash 芯片的擦除粒度对齐,否则执行擦除操作后,将会导致其他数据丢失。

sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
参数描述
flashFlash 设备对象
addr起始地址
size从起始地址开始写入数据的总大小
data待写入的数据

2.2.10 读取 Flash 状态

sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status)
参数描述
flashFlash 设备对象
status当前状态寄存器值

2.2.11 写(修改) Flash 状态

sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status)
参数描述
flashFlash 设备对象
is_volatile是否为易闪失的,true: 易闪失的,及断电后会丢失
status当前状态寄存器值

2.3 配置方法

所有配置位于 /sfud/inc/sfud_cfg.h ,请参考下面的配置介绍,选择适合自己项目的配置。

2.3.1 调试模式

打开/关闭 SFUD_DEBUG_MODE 宏定义

2.3.2 是否使用 SFDP 参数功能

打开/关闭 SFUD_USING_SFDP 宏定义

注意:关闭后只会查询该库在 /sfud/inc/sfud_flash_def.h 中提供的 Flash 信息表。这样虽然会降低软件的适配性,但减少代码量。

2.3.3 是否使用快速读模式(SPI模式)

打开/关闭 SFUD_USING_FAST_READ 宏定义。许多Flash的读模式可能并不能满足较高的SPI频率(具体可查阅各Flash的数据手册交流特性部分),此时需要使能快速读模式。

注意:由于快速读模式在读时插入了默认一个空字节,在SPI速率较慢时可能相较于读模式速度更慢。

2.3.4 是否使用该库自带的 Flash 参数信息表

打开/关闭 SFUD_USING_FLASH_INFO_TABLE 宏定义

注意:关闭后该库只驱动支持 SFDP 规范的 Flash,也会适当的降低部分代码量。另外 2.3.2 及 2.3.3 这两个宏定义至少定义一种,也可以两种方式都选择。

2.3.5 既不使用 SFDP ,也不使用 Flash 参数信息表

为了进一步降低代码量,SFUD_USING_SFDPSFUD_USING_FLASH_INFO_TABLE 也可以 都不定义

此时,只要在定义 Flash 设备时,指定好 Flash 参数,之后再调用 sfud_device_init 对该设备进行初始化。参考如下代码:

sfud_flash sfud_norflash0 = {
        .name = "norflash0",
        .spi.name = "SPI1",
        .chip = { "W25Q64FV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L * 1024L * 1024L, SFUD_WM_PAGE_256B, 4096, 0x20 } };
......
sfud_device_init(&sfud_norflash0);
......

2.3.6 Flash 设备表

如果产品中存在多个 Flash ,可以添加 Flash 设备表。修改 SFUD_FLASH_DEVICE_TABLE 这个宏定义,示例如下:

enum {
    SFUD_W25Q64CV_DEVICE_INDEX = 0,
    SFUD_GD25Q64B_DEVICE_INDEX = 1,
};

#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_W25Q64CV_DEVICE_INDEX] = {.name = "W25Q64CV", .spi.name = "SPI1"},   \
    [SFUD_GD25Q64B_DEVICE_INDEX] = {.name = "GD25Q64B", .spi.name = "SPI3"},   \
}

上面定义了两个 Flash 设备(大部分产品一个足以),两个设备的名称为 "W25Q64CV""GD25Q64B" ,分别对应 "SPI1""SPI3" 这两个 SPI 设备名称(在移植 SPI 接口时会用到,位于 /sfud/port/sfud_port.c ), SFUD_W25Q16CV_DEVICE_INDEXSFUD_GD25Q64B_DEVICE_INDEX 这两个枚举定义了两个设备位于设备表中的索引,可以通过 sfud_get_device_table() 方法获取到设备表,再配合这个索引值来访问指定的设备。

2.3.7 QSPI 模式

打开/关闭 SFUD_USING_QSPI 宏定义

开启后,SFUD 也将支持使用 QSPI 总线连接的 Flash。

2.4 移植说明

移植文件位于 /sfud/port/sfud_port.c ,文件中的 sfud_err sfud_spi_port_init(sfud_flash *flash) 方法是库提供的移植方法,在里面完成各个设备 SPI 读写驱动(必选)、重试次数(必选)、重试接口(可选)及 SPI 锁(可选)的配置。更加详细的移植内容,可以参考 demo 中的各个平台的移植文件。

2.5 添加库目前不支持的 Flash

这里需要修改 /sfud/inc/sfdu_flash_def.h ,所有已经支持的 Flash 见 SFUD_FLASH_CHIP_TABLE 宏定义,需要提前准备的 Flash 参数内容分别为:| 名称 | 制造商 ID | 类型 ID | 容量 ID | 容量 | 写模式 | 擦除粒度(擦除的最小单位) | 擦除粒度对应的命令 | 。这里以添加 兆易创新 ( GigaDevice ) 的 GD25Q64B Flash 来举例。

此款 Flash 为兆易创新的早期生产的型号,所以不支持 SFDP 标准。首先需要下载其数据手册,找到 0x9F 命令返回的 3 种 ID, 这里需要最后面两字节 ID ,即 type idcapacity idGD25Q64B 对应这两个 ID 分别为 0x400x17 。上面要求的其他 Flash 参数都可以在数据手册中找到,这里要重点说明下 写模式 这个参数,库本身提供的写模式共计有 4 种,详见文件顶部的 sfud_write_mode 枚举类型,同一款 Flash 可以同时支持多种写模式,视情况而定。对于 GD25Q64B 而言,其支持的写模式应该为 SFUD_WM_PAGE_256B ,即写 1-256 字节每页。结合上述 GD25Q64B 的 Flash 参数应如下:

    {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},

再将其增加到 SFUD_FLASH_CHIP_TABLE 宏定义末尾,即可完成该库对 GD25Q64B 的支持。

2.6 Demo

目前已支持如下平台下的 Demo

路径平台描述
/demo/stm32f10x_non_osSTM32F10X 裸机平台
/demo/stm32f2xx_rttSTM32F2XX + RT-Thread 操作系统平台
/demo/stm32l475_non_os_qspiSTM32L475 + QSPI 模式 裸机平台
/demo/esp32_ext_spi_flashESP32C3 + SPI外部Flash ESP-IDF框架

2.7 许可

采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。