SFUD 源码分析
1. 仓库概览
SFUD(Serial Flash Universal Driver)是一个通用的串行闪存驱动库,专为嵌入式系统设计。它支持多种串行闪存芯片,提供统一的操作接口,简化了闪存的读写、擦除等操作。
主要功能/亮点:
- 支持多种串行闪存芯片,包括 Winbond、GigaDevice、 Macronix、Cypress 等主流厂商的产品
- 支持 SPI 和 QSPI 接口模式
- 支持 SFDP(Serial Flash Discoverable Parameters)标准,自动识别闪存参数
- 提供统一的 API 接口,简化闪存操作
- 支持 4 字节地址模式,可处理大于 16MB 的闪存
- 支持多种擦除模式和写入模式
典型应用场景:
- 嵌入式系统中的固件存储
- 配置数据的持久化存储
- 日志数据的存储
- 物联网设备中的数据存储
2. 目录结构
SFUD 项目采用清晰的分层架构,将核心功能与平台适配代码分离。核心源码位于 sfud 目录,包含了驱动的主要实现。demo 目录提供了不同平台的示例代码,方便用户快速上手。
t:\MCU\SFUD-master\
├── demo/ # 示例代码
│ ├── esp32_ext_spi_flash/ # ESP32 平台示例
│ ├── stm32f10x_non_os/ # STM32F10x 无操作系统示例
│ ├── stm32f2xx_rtt/ # STM32F2xx 带 RT-Thread 示例
│ └── readme.md # 示例说明
├── docs/ # 文档
│ ├── zh/ # 中文文档
│ └── readme.md # 文档说明
├── sfud/ # 核心源码
│ ├── inc/ # 头文件
│ │ ├── sfud.h # 主头文件
│ │ ├── sfud_cfg.h # 配置头文件
│ │ ├── sfud_def.h # 定义头文件
│ │ └── sfud_flash_def.h # 闪存芯片定义头文件
│ ├── port/ # 平台适配层
│ │ └── sfud_port.c # 端口实现
│ └── src/ # 源码实现
│ ├── sfud.c # 主要实现
│ └── sfud_sfdp.c # SFDP 实现
├── .gitattributes
├── LICENSE
├── README.md
├── library.json
└── note.txt
- sfud/inc/:包含库的所有头文件,定义了 API 接口、数据结构和常量。
- sfud/src/:包含库的核心实现,包括闪存操作的主要逻辑。
- sfud/port/:包含平台适配代码,需要用户根据具体硬件平台实现。
- demo/:包含不同平台的示例代码,展示如何使用 SFUD 库。
3. 系统架构与主流程
SFUD 采用分层架构设计,将硬件操作与业务逻辑分离,提高了代码的可移植性和可维护性。
系统架构
主流程
-
初始化流程:
- 调用
sfud_init()初始化所有闪存设备 - 对每个闪存设备调用
sfud_device_init() - 执行硬件初始化(
hardware_init()):初始化 SPI 接口,读取 JEDEC ID,识别闪存芯片 - 执行软件初始化(
software_init()):设置软件参数
- 调用
-
读写流程:
- 读取操作:调用
sfud_read(),发送读取命令,等待数据返回 - 写入操作:调用
sfud_write(),发送写使能命令,执行页编程,等待操作完成 - 擦除操作:调用
sfud_erase(),发送擦除命令,等待操作完成
- 读取操作:调用
-
闪存识别流程:
- 读取 JEDEC ID(厂商ID、类型ID、容量ID)
- 尝试读取 SFDP 参数(如果支持)
- 如果 SFDP 读取失败,从内置的闪存信息表中查找
4. 核心功能模块
4.1 闪存识别模块
该模块负责识别闪存芯片的型号和参数,是 SFUD 能够支持多种闪存的基础。
- JEDEC ID 读取:通过
read_jedec_id()函数读取闪存的 JEDEC ID,包含厂商ID、类型ID和容量ID - SFDP 参数读取:通过
sfud_read_sfdp()函数读取闪存的 SFDP 参数,获取更详细的闪存信息 - 闪存信息表:当 SFDP 读取失败时,从内置的
flash_chip_table中查找匹配的闪存信息
4.2 读写操作模块
该模块提供了闪存的读写操作,支持不同的写入模式。
- 读取操作:
sfud_read()函数支持普通读取和快速读取模式 - 写入操作:
page256_or_1_byte_write():支持页编程(256字节)和字节写入模式aai_write():支持自动地址递增写入模式
- 擦除操作:
sfud_erase():支持扇区擦除sfud_chip_erase():支持整片擦除
4.3 QSPI 支持模块
该模块提供了对 QSPI 接口的支持,提高了数据传输速度。
- 快速读取:
sfud_qspi_fast_read_enable()函数启用 QSPI 快速读取模式 - 命令格式设置:
qspi_set_read_cmd_format()函数设置 QSPI 读取命令格式
4.4 SFDP 解析模块
该模块负责解析闪存的 SFDP 参数,获取闪存的详细信息。
- SFDP 头部读取:
read_sfdp_header()函数读取 SFDP 头部信息 - 基本参数表读取:
read_basic_table()函数读取 JEDEC 基本参数表 - 擦除参数获取:
sfud_sfdp_get_suitable_eraser()函数获取合适的擦除参数
5. 核心 API/类/函数
5.1 初始化相关函数
| 函数名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
sfud_init() | 初始化 SFUD 库 | 无 | sfud_err:操作结果 |
sfud_device_init() | 初始化指定的闪存设备 | sfud_flash *flash:闪存设备指针 | sfud_err:操作结果 |
sfud_get_device() | 获取指定索引的闪存设备 | size_t index:设备索引 | sfud_flash *:闪存设备指针 |
sfud_get_device_num() | 获取闪存设备总数 | 无 | size_t:设备数量 |
5.2 读写操作函数
| 函数名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
sfud_read() | 读取闪存数据 | const sfud_flash *flash:闪存设备uint32_t addr:起始地址size_t size:读取大小uint8_t *data:数据缓冲区 | sfud_err:操作结果 |
sfud_write() | 写入闪存数据(不擦除) | const sfud_flash *flash:闪存设备uint32_t addr:起始地址size_t size:写入大小const uint8_t *data:数据缓冲区 | sfud_err:操作结果 |
sfud_erase() | 擦除闪存数据 | const sfud_flash *flash:闪存设备uint32_t addr:起始地址size_t size:擦除大小 | sfud_err:操作结果 |
sfud_erase_write() | 先擦除后写入闪存数据 | const sfud_flash *flash:闪存设备uint32_t addr:起始地址size_t size:操作大小const uint8_t *data:数据缓冲区 | sfud_err:操作结果 |
sfud_chip_erase() | 擦除整个闪存 | const sfud_flash *flash:闪存设备 | sfud_err:操作结果 |
5.3 状态操作函数
| 函数名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
sfud_read_status() | 读取闪存状态寄存器 | const sfud_flash *flash:闪存设备uint8_t *status:状态缓冲区 | sfud_err:操作结果 |
sfud_write_status() | 写入闪存状态寄存器 | const sfud_flash *flash:闪存设备bool is_volatile:是否为易失性写入uint8_t status:状态值 | sfud_err:操作结果 |
5.4 QSPI 相关函数
| 函数名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
sfud_qspi_fast_read_enable() | 启用 QSPI 快速读取模式 | sfud_flash *flash:闪存设备uint8_t data_line_width:数据总线宽度 | sfud_err:操作结果 |
5.5 核心数据结构
sfud_flash 结构
typedef struct {
char *name; /**< 串行闪存名称 */
size_t index; /**< 闪存设备信息表中的索引 */
sfud_flash_chip chip; /**< 闪存芯片信息 */
sfud_spi spi; /**< SPI 设备 */
bool init_ok; /**< 初始化成功标志 */
bool addr_in_4_byte; /**< 闪存是否处于 4 字节寻址模式 */
struct {
void (*delay)(void); /**< 每次重试的延迟 */
size_t times; /**< 默认错误重试次数 */
} retry;
void *user_data; /**< 用户数据 */
#ifdef SFUD_USING_QSPI
sfud_qspi_read_cmd_format read_cmd_format; /**< 快速读取命令格式 */
#endif
#ifdef SFUD_USING_SFDP
sfud_sfdp sfdp; /**< 串行闪存可发现参数 */
#endif
} sfud_flash, *sfud_flash_t;
sfud_spi 结构
typedef struct __sfud_spi {
/* SPI 设备名称 */
char *name;
/* SPI 总线读写数据函数 */
sfud_err (*wr)(const struct __sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size);
#ifdef SFUD_USING_QSPI
/* QSPI 快速读取函数 */
sfud_err (*qspi_read)(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
uint8_t *read_buf, size_t read_size);
#endif
/* 锁定 SPI 总线 */
void (*lock)(const struct __sfud_spi *spi);
/* 解锁 SPI 总线 */
void (*unlock)(const struct __sfud_spi *spi);
/* 用户数据 */
void *user_data;
} sfud_spi, *sfud_spi_t;
6. 技术栈与依赖
| 技术/依赖 | 用途 | 来源 |
|---|---|---|
| C 语言 | 主要开发语言 | 标准库 |
| SPI 接口 | 与闪存通信 | 硬件平台 |
| QSPI 接口 | 高速数据传输 | 硬件平台(可选) |
| SFDP 标准 | 闪存参数自动识别 | JEDEC 标准 |
| JEDEC ID | 闪存芯片识别 | 闪存芯片 |
7. 关键模块与典型用例
7.1 基本读写操作
功能说明:实现闪存的基本读写操作,包括读取、写入和擦除。
配置与依赖:
- 需要在
sfud_cfg.h中配置闪存设备信息表SFUD_FLASH_DEVICE_TABLE - 需要实现
sfud_spi_port_init()函数初始化 SPI 接口
使用示例:
#include "sfud.h"
int main(void) {
sfud_err result;
uint8_t read_buf[256];
uint8_t write_buf[256] = "Hello, SFUD!";
// 初始化 SFUD 库
result = sfud_init();
if (result != SFUD_SUCCESS) {
printf("SFUD init failed: %d\n", result);
return -1;
}
// 获取第一个闪存设备
sfud_flash *flash = sfud_get_device(0);
if (!flash) {
printf("Get flash device failed\n");
return -1;
}
// 擦除指定区域
result = sfud_erase(flash, 0x0, 256);
if (result != SFUD_SUCCESS) {
printf("Erase failed: %d\n", result);
return -1;
}
// 写入数据
result = sfud_write(flash, 0x0, 256, write_buf);
if (result != SFUD_SUCCESS) {
printf("Write failed: %d\n", result);
return -1;
}
// 读取数据
result = sfud_read(flash, 0x0, 256, read_buf);
if (result != SFUD_SUCCESS) {
printf("Read failed: %d\n", result);
return -1;
}
printf("Read data: %s\n", read_buf);
return 0;
}
7.2 QSPI 快速读取
功能说明:启用 QSPI 快速读取模式,提高数据读取速度。
配置与依赖:
- 需要在
sfud_cfg.h中定义SFUD_USING_QSPI - 需要实现
qspi_read函数
使用示例:
#include "sfud.h"
int main(void) {
sfud_err result;
// 初始化 SFUD 库
result = sfud_init();
if (result != SFUD_SUCCESS) {
printf("SFUD init failed: %d\n", result);
return -1;
}
// 获取第一个闪存设备
sfud_flash *flash = sfud_get_device(0);
if (!flash) {
printf("Get flash device failed\n");
return -1;
}
// 启用 QSPI 快速读取模式,数据总线宽度为 4
result = sfud_qspi_fast_read_enable(flash, 4);
if (result != SFUD_SUCCESS) {
printf("QSPI fast read enable failed: %d\n", result);
return -1;
}
// 后续的读取操作将使用 QSPI 快速读取模式
return 0;
}
8. 配置、部署与开发
8.1 配置选项
SFUD 提供了多个配置选项,可在 sfud_cfg.h 文件中设置:
| 配置项 | 描述 | 默认值 |
|---|---|---|
SFUD_DEBUG_MODE | 启用调试模式 | 未定义 |
SFUD_USING_SFDP | 启用 SFDP 支持 | 未定义 |
SFUD_USING_QSPI | 启用 QSPI 支持 | 未定义 |
SFUD_USING_FLASH_INFO_TABLE | 启用闪存信息表 | 未定义 |
SFUD_USING_FAST_READ | 启用快速读取 | 未定义 |
SFUD_FLASH_DEVICE_TABLE | 闪存设备信息表 | 必须定义 |
8.2 平台适配
使用 SFUD 时,需要实现以下平台适配函数:
sfud_spi_port_init():初始化 SPI 接口spi.wr():SPI 读写函数spi.lock()和spi.unlock():SPI 总线锁定/解锁函数()delay延时函数()
8.3 开发流程
- 配置 SFUD:在
sfud_cfg.h中设置配置选项和闪存设备信息表 - 实现平台适配:实现 SPI 接口和必要的回调函数
- 初始化 SFUD:调用
sfud_init()初始化库 - 使用 API:调用 SFUD 提供的 API 进行闪存操作
9. 监控与维护
9.1 错误处理
SFUD 提供了错误码机制,通过 sfud_err 枚举定义了各种错误类型:
typedef enum {
SFUD_SUCCESS = 0, /**< 成功 */
SFUD_ERR_NOT_FOUND = 1, /**< 未找到或不支持 */
SFUD_ERR_WRITE = 2, /**< 写入错误 */
SFUD_ERR_READ = 3, /**< 读取错误 */
SFUD_ERR_TIMEOUT = 4, /**< 超时错误 */
SFUD_ERR_ADDR_OUT_OF_BOUND = 5, /**< 地址超出闪存范围 */
} sfud_err;
9.2 日志系统
SFUD 提供了日志系统,可通过以下函数输出日志:
sfud_log_debug():调试日志sfud_log_info():信息日志
用户需要在平台适配层实现这些函数。
9.3 常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 初始化失败 | SPI 接口未正确初始化 | 检查 SPI 初始化代码 |
| 读写失败 | 闪存保护位已设置 | 调用 sfud_write_status() 清除保护位 |
| 地址错误 | 地址超出闪存范围 | 检查地址和大小是否正确 |
| QSPI 模式失败 | QSPI 接口未正确实现 | 检查 QSPI 相关代码 |
10. 总结与亮点回顾
SFUD 是一个设计精良的串行闪存驱动库,具有以下亮点:
- 通用性:支持多种串行闪存芯片,提供统一的操作接口
- 灵活性:支持 SPI 和 QSPI 接口,可根据硬件平台选择
- 智能识别:支持 SFDP 标准,可自动识别闪存参数
- 可移植性:平台适配层与核心逻辑分离,易于移植到不同平台
- 可靠性:完善的错误处理和重试机制
- 高性能:支持 QSPI 快速读取模式,提高数据传输速度
SFUD 的设计思路值得学习:
- 分层架构:将硬件操作与业务逻辑分离,提高代码可维护性
- 模块化设计:核心功能模块化,便于扩展和测试
- 标准支持:遵循 JEDEC 标准,提高兼容性
- 配置灵活:通过配置选项可裁剪功能,适应不同场景
SFUD 不仅是一个实用的闪存驱动库,也是嵌入式系统中设备驱动设计的优秀范例。它的设计思想和实现技术对于理解嵌入式系统中的设备驱动开发具有参考价值。

473

被折叠的 条评论
为什么被折叠?



