FAL API分析
fal_flash_init
函数详解
1. 功能与作用
fal_flash_init
是 FAL(Flash 抽象层)中用于 初始化 Flash 设备 的核心函数,负责完成以下任务:
- 硬件初始化:调用底层 Flash 设备的
init()
操作函数(如片内 Flash 的时钟配置或 SPI Flash 的 SFUD 驱动初始化)。 - 参数校验:检查 Flash 设备的
name
、addr
、len
、blk_size
等参数是否合法,避免后续操作异常。 - 设备注册:将 Flash 设备信息注册到 FAL 全局设备表中,为分区管理和上层应用提供统一接口。
2. 调用流程
该函数通常在系统启动时由 **fal_init()
** 自动触发,具体流程如下:
// FAL 全局初始化函数(用户显式调用)
void fal_init(void) {fal_flash_init(); // 初始化所有 Flash 设备fal_partition_init(); // 初始化分区表
}
- 关联关系:
fal_flash_init
遍历 Flash 设备表(FAL_FLASH_DEV_TABLE
),依次初始化每个设备。
3. 关键实现细节
- 底层驱动适配:
用户需在fal_cfg.h
中定义 Flash 设备表,并实现设备操作函数(如read
、write
、erase
)。例如:// 片内 Flash 设备定义(参考网页6) const struct fal_flash_dev stm32_onchip_flash = {.name = "stm32_onchip",.addr = 0x08000000,.len = 1024 * 1024,.blk_size = 128 * 1024,.ops = {NULL, flash_read, flash_write, flash_erase}, // init 可为空.write_gran = 8 // STM32F4 按字节写入 };
- 自动参数更新:
对于 SFUD 驱动的 SPI Flash(如 W25Q128),fal_flash_init
会从 Flash 芯片读取实际参数(容量、块大小)并更新设备表。
4. 典型应用场景
- Bootloader 开发:初始化片内 Flash 和外部 Flash,为固件升级提供双区备份支持。
- 文件系统挂载:在 LittleFS 或 FATFS 中,通过 FAL 初始化 Flash 设备后挂载文件系统。
5. 常见问题与调试
- 设备未识别:检查
fal_cfg.h
中设备名称是否与驱动定义一致,例如"norflash0"
是否与 SFUD 设备名匹配。 - 初始化失败:确认 Flash 设备的
init()
函数是否正确实现(如 SPI 总线未初始化会导致 SFUD 驱动失败)。
fal_flash_device_find
函数详解
1. 功能与作用
fal_flash_device_find
是 FAL(Flash 抽象层)中用于 根据设备名称查找 Flash 设备对象 的关键函数。其核心作用包括:
- 设备检索:在全局 Flash 设备表中搜索指定名称的 Flash 设备(如片内 Flash、SPI Nor Flash 等)。
- 设备绑定:为后续操作(如读写擦除)提供设备句柄,确保操作正确的底层硬件。
- 参数:
name
:Flash 设备名称(字符串),需与fal_cfg.h
中定义的设备表一致(例如"stm32_onchip"
或"norflash0"
)。
- 返回值:
- 成功:返回指向
fal_flash_dev
结构体的指针(包含设备地址、容量、块大小等信息)。 - 失败:返回
NULL
(常见于名称拼写错误或设备未注册)。
- 成功:返回指向
-
场景 1:初始化阶段验证设备
在系统启动时,通过该函数检查 Flash 设备是否正常注册:const struct fal_flash_dev *flash_dev = fal_flash_device_find("stm32_onchip"); if (flash_dev == NULL) {rt_kprintf("Error: Flash device not found!\n"); }
-
场景 2:分区操作中的设备绑定
在擦除或读写分区时,需先获取分区对应的 Flash 设备句柄:// 查找分区 const struct fal_partition *part = fal_partition_find("app"); // 获取关联的 Flash 设备 const struct fal_flash_dev *flash_dev = fal_flash_device_find(part->flash_name); // 执行擦除操作 flash_dev->ops.erase(part->offset + addr, size);
- 设备名称一致性:确保
fal_cfg.h
中的设备表(FAL_FLASH_DEV_TABLE
)与代码中调用时使用的名称完全一致,包括大小写。 - 设备注册顺序:Flash 设备需在
fal_init()
中完成注册,否则函数无法检索到设备。 - 多设备管理:若项目涉及多个 Flash 设备(如片内 Flash + 外部 SPI Flash),需在设备表中分别定义并正确命名。
- 设备未找到(返回 NULL):
- 检查设备名称是否拼写错误(如
"stm32_onchip"
误写为"stm32_onchp"
)。 - 确认
fal_init()
已正确执行,确保设备表已加载。
- 检查设备名称是否拼写错误(如
- 操作权限问题:若设备未初始化(如 SPI Flash 未调用
rt_sfud_flash_probe
),需先完成驱动初始化。
fal_partition_read
函数详解
1. 功能与作用
fal_partition_read
是 FAL(Flash Abstraction Layer)中用于 从指定逻辑分区读取数据 的核心函数
。其核心作用包括:
- 数据读取:根据分区的相对地址和指定长度,从 Flash 设备中读取原始数据。
- 硬件抽象:通过 FAL 的分区管理机制,自动关联底层 Flash 设备的物理地址,开发者无需直接操作硬件地址。
2. 函数原型与参数
int fal_partition_read( const struct fal_partition *part, // 目标分区指针 uint32_t addr, // 分区内相对地址(字节) uint8_t *buf, // 数据缓冲区指针 size_t size // 需读取的字节数
);
参数说明:
-
part
:指向fal_partition
结构体的指针,通过fal_partition_find()
获取。 -
addr
:相对于分区起始地址的偏移量(单位:字节),需确保不超过分区范围。 -
buf
:用户提供的缓冲区,用于存储读取的数据。 -
size
:请求读取的字节数,实际读取量受剩余空间限制(如超出分区末尾则自动截断)。
返回值:
- 成功:返回实际读取的字节数(
≥0
)。 - 失败:返回
-1
(常见错误包括分区未注册、地址越界或底层驱动异常)。
3. 使用场景与示例
场景 1:验证 Flash 擦除状态
擦除分区后,通过循环读取验证所有数据是否为 0xFF
(擦除后的默认值):
const struct fal_partition *part = fal_partition_find("filesystem");
uint8_t buf[4096];
for (uint32_t offset = 0; offset < part->len; offset += sizeof(buf)) { int ret = fal_partition_read(part, offset, buf, sizeof(buf)); if (ret < 0) { rt_kprintf("Read failed at offset %lu\n", offset); break; } for (int i = 0; i < ret; i++) { if (buf[i] != 0xFF) { rt_kprintf("Erase verification failed!\n"); break; } }
}
场景 2:文件系统数据加载
结合文件系统(如 LittleFS),从分区读取配置文件:
int fd = open("/filesystem/config.ini", O_RDONLY);
if (fd >= 0) { uint8_t config_data[1024]; read(fd, config_data, sizeof(config_data)); close(fd);
}
(底层文件系统操作会隐式调用 fal_partition_read
)。
4. 配置注意事项
-
分区表对齐:
- 分区的
offset
和len
需与底层 Flash 设备的blk_size
(块大小)对齐。 - 例如,若 Flash 块大小为 4KB,则分区起始地址应为
4096
的整数倍。
- 分区的
-
写入粒度控制:
- 不同 Flash 设备的
write_gran
(写入粒度)可能不同(如 1bit、8bit),需在fal_flash_dev
中正确定义。 - STM32F1 需按 32bit 对齐写入,否则会导致硬件错误。
- 不同 Flash 设备的
-
驱动初始化:
- 外部 Flash(如 SPI Nor Flash)需通过
rt_sfud_flash_probe
初始化,否则fal_partition_read
无法访问设备。
- 外部 Flash(如 SPI Nor Flash)需通过
5. 常见问题与调试
-
返回
-1
错误:- 分区未找到:检查
fal_partition_find()
参数与分区表名称是否一致。 - 地址越界:确保
addr + size
不超过分区长度(part->len
)。
- 分区未找到:检查
-
数据校验失败:
- 硬件驱动异常:通过
fal_show_part_table()
命令验证分区信息,或使用fal probe
直接读写 Flash 测试底层驱动。
- 硬件驱动异常:通过
6. 扩展应用
-
与 EasyFlash 集成:通过
fal_partition_read
读取存储在 Flash 中的键值数据:-
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) { const struct fal_partition *part = fal_partition_find("easyflash"); return fal_partition_read(part, addr, (uint8_t *)buf, size) == size ? EF_NO_ERR : EF_READ_ERR; }
-
-
多设备协同:同时操作片内 Flash 和外部 SPI Flash 的分区,例如从片内读取引导程序,从外部 Flash 加载用户数据。
fal_partition_write
函数详解
fal_partition_write
是 RT-Thread FAL(Flash 抽象层)框架中用于向 逻辑分区写入数据 的核心函数,其功能覆盖硬件地址映射、数据校验与底层驱动适配。以下从功能、参数、使用场景及注意事项展开分析:
一、功能与作用
-
数据写入
根据分区的相对地址(addr
)和指定长度(size
),将用户缓冲区(buf
)中的数据写入 Flash 设备的物理地址。- 地址映射:自动将分区的逻辑地址转换为底层 Flash 的物理地址,开发者无需手动计算偏移。
- 硬件驱动适配:通过
fal_flash_dev
结构体调用底层驱动(如 SPI Flash 的sfud_write
函数)。
-
错误校验
- 地址越界检测:若
addr + size
超出分区长度(part->len
),返回-1
并输出错误日志。 - 设备状态检查:验证底层 Flash 设备是否已注册并初始化(如未调用
fal_init()
可能导致失败)。
- 地址越界检测:若
二、函数原型与参数
int fal_partition_write( const struct fal_partition *part, // 目标分区指针(通过 fal_partition_find 获取) uint32_t addr, // 分区内相对地址(字节偏移) const uint8_t *buf, // 待写入的数据缓冲区 size_t size // 需写入的字节数
);
返回值:
- 成功:返回实际写入的字节数(
≥0
)。 - 失败:返回
-1
(常见于地址越界、设备未注册或底层驱动异常)。
三、使用场景与示例
1. 初始化阶段写入默认配置
const struct fal_partition *part = fal_partition_find("config");
uint8_t default_config[128] = {0x00};
int ret = fal_partition_write(part, 0, default_config, sizeof(default_config));
if (ret < 0) { rt_kprintf("Failed to write config partition!\n");
}
2. OTA 固件更新
从网络接收固件数据后写入 Flash 的 app
分区:
uint8_t firmware_buf[4096];
// 假设 firmware_size 为实际接收的固件大小
int ret = fal_partition_write(part, 0, firmware_buf, firmware_size);
if (ret != firmware_size) { rt_kprintf("Firmware update failed!\n");
}
3. 与文件系统集成
结合 LittleFS 或 FATFS,通过文件操作隐式调用写入函数:
FIL file;
f_open(&file, "/flash/data.txt", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&file, buffer, sizeof(buffer), &bytes_written);
f_close(&file);
(底层文件系统通过 FAL 的 fal_partition_write
实现数据持久化)。
四、配置与调试注意事项
-
对齐要求
- 地址对齐:确保
addr
和size
满足底层 Flash 设备的 写入粒度(write_gran
,如 STM32F1 需按 32bit 对齐)。 - 块大小对齐:分区的
offset
和len
需与 Flash 设备的blk_size
对齐,否则可能导致擦除异常。
- 地址对齐:确保
-
驱动适配
- 外部 Flash:需通过
rt_sfud_flash_probe
初始化 SPI Flash 驱动,否则写入操作无效。 - 多设备管理:若项目涉及多 Flash 设备(如片内 Flash + 外部 SPI Flash),需在
fal_cfg.h
中正确定义分区表。
- 外部 Flash:需通过
-
性能优化
- 批量写入:优先按块大小(
blk_size
)分块写入,减少擦写次数(Flash 擦除耗时较长)。 - 缓冲区复用:避免频繁申请/释放内存,可使用静态缓冲区或内存池。
- 批量写入:优先按块大小(
五、常见问题与解决
-
写入失败(返回
-1
)- 地址越界:检查
part->len
是否足够容纳addr + size
。 - 驱动未初始化:通过
fal_show_part_table()
验证分区信息,或使用fal probe
直接测试底层驱动。
- 地址越界:检查
-
数据校验错误
- 写入未对齐:使用
fal_partition_write_nbyte
自定义函数处理非对齐写入(需先读取旧数据、修改后重写)。 - 硬件干扰:增加 CRC 校验或重试机制,确保数据完整性。
- 写入未对齐:使用
扩展应用
- 键值存储(EasyFlash):通过
fal_partition_write
实现参数持久化,支持掉电保存与快速检索。 - 日志系统:将日志数据写入指定分区,结合文件系统实现循环覆盖存储。
总结
fal_partition_write
是 FAL 框架中实现 透明化 Flash 写入 的核心接口。开发者需重点关注 对齐要求 和 驱动适配,结合 Shell 命令(如 fal probe
和 fal show
)快速验证功能。通过灵活应用该函数,可构建高效可靠的嵌入式存储方案(如固件升级、日志记录等)。
fal_partition_erase
函数详解
1. 功能与作用
fal_partition_erase
是 FAL(Flash 抽象层)框架中用于 擦除指定逻辑分区的 Flash 存储区域 的核心函数,其主要作用包括:
- 数据擦除:根据分区的逻辑地址和指定长度,擦除 Flash 的物理存储单元(通常将数据置为
0xFF
)。 - 硬件抽象:通过 FAL 的分区管理机制,自动关联底层 Flash 设备的物理地址,开发者无需直接操作硬件地址。
2. 函数原型与参数
int fal_partition_erase( const struct fal_partition *part, // 目标分区指针 uint32_t addr, // 分区内相对地址(字节偏移) size_t size // 需擦除的字节数
);
参数说明:
-
part
:指向fal_partition
结构体的指针,通过fal_partition_find()
获取。 -
addr
:相对于分区起始地址的偏移量,需确保不超过分区范围(0 ≤ addr + size ≤ part->len
)。 -
size
:请求擦除的字节数,实际擦除量受剩余空间限制。
返回值:
- 成功:返回
0
。 - 失败:返回
-1
(常见错误包括地址越界、设备未注册或底层驱动异常)。
3. 使用场景与示例
场景 1:固件升级前的分区擦除
在 OTA(空中升级)流程中,擦除目标分区以写入新固件:
const struct fal_partition *dl_part = fal_partition_find("download");
// 擦除下载分区的全部内容
int ret = fal_partition_erase(dl_part, 0, dl_part->len);
if (ret != 0) { rt_kprintf("Erase download partition failed!\n");
}
(参考网页 1 的 OTA 擦除逻辑)
场景 2:文件系统初始化
在挂载 LittleFS 或 FATFS 前,擦除文件系统分区以恢复初始状态:
const struct fal_partition *fs_part = fal_partition_find("filesystem");
// 擦除分区的前 4KB 区域(用于文件系统元数据)
ret = fal_partition_erase(fs_part, 0, 4096);
场景 3:错误恢复机制
检测到存储数据异常时,执行局部擦除并重新写入数据:
uint32_t corrupted_addr = 0x1000; // 检测到异常的起始地址
size_t erase_size = 1024; // 擦除 1KB 区域
ret = fal_partition_erase(part, corrupted_addr, erase_size);
if (ret == 0) { fal_partition_write(part, corrupted_addr, new_data, erase_size);
}
4. 配置与调试注意事项
-
对齐要求
- 块大小对齐:擦除操作需与 Flash 设备的
blk_size
(块大小)对齐。例如,STM32F4 的块大小为 128KB,若擦除 130KB 会导致硬件错误。 - 地址偏移验证:函数内部会校验
addr + size ≤ part->len
,若越界则返回-1
并输出错误日志。
- 块大小对齐:擦除操作需与 Flash 设备的
-
驱动适配
- 外部 Flash 初始化:使用 SPI Nor Flash 时需通过
rt_sfud_flash_probe
初始化驱动,否则擦除无效。 - 多设备管理:若分区涉及多个 Flash 设备(如片内 Flash + 外部 SPI Flash),需在
fal_cfg.h
中正确定义分区表。
- 外部 Flash 初始化:使用 SPI Nor Flash 时需通过
-
性能优化
- 批量擦除:优先按块大小(
blk_size
)分块擦除,减少操作次数(Flash 擦除耗时较长)。 - 擦除验证:擦除后调用
fal_partition_read
检查数据是否全为0xFF
,确保操作成功。
- 批量擦除:优先按块大小(
5. 常见问题与解决
-
擦除失败(返回
-1
)- 地址越界:检查
addr + size
是否超过分区长度(part->len
)。 - 驱动未初始化:通过
fal_show_part_table()
验证分区信息,或使用fal probe
直接测试底层驱动。
- 地址越界:检查
-
擦除后数据未清零
- 硬件特性限制:某些 Flash 芯片擦除后为
0xFF
而非0x00
,需在写入前显式填充数据。
- 硬件特性限制:某些 Flash 芯片擦除后为
-
硬件锁定异常
- Flash 写保护:部分 Flash 设备默认启用写保护,需通过
flash_dev->ops.write_protect()
解除锁定。
- Flash 写保护:部分 Flash 设备默认启用写保护,需通过
扩展应用
- 安全擦除:结合加密算法,在擦除前覆写敏感数据(如密钥),防止数据残留。
- 动态分区调整:根据运行时需求擦除并重新划分分区(需自定义分区表管理逻辑)