Flash 驱动
基于 lisa_device 框架的 Flash 存储设备驱动,为 ARCS 平台提供统一的 Flash 读写擦除接口。
功能特性
设备支持: SPI Flash 存储设备(自动检测容量)
存储操作: Flash 读取、写入、擦除操作
参数查询: 获取写块大小、擦除值、设备能力等参数
布局查询: 获取 Flash 页面布局信息(页大小、页数量、总容量)
线程安全: 内部使用互斥锁保护并发访问
智能写入: 自动检测数据位置,避免 Flash 读写冲突
动态容量: 通过读取 JEDEC ID 自动识别 Flash 容量
配置选项
在 Kconfig 中启用驱动:
CONFIG_LISA_FLASH_ARCS=y
常用配置参数
# Flash 扇区大小(擦除单位)
CONFIG_LISA_FLASH_ARCS_ERASE_SECTOR_SIZE=4096
# Flash 最小写入块大小
CONFIG_LISA_FLASH_ARCS_WRITE_BLOCK_SIZE=4
# Flash 数据宽度(1=标准SPI, 2=双SPI, 4=四SPI)
CONFIG_LISA_FLASH_ARCS_DATA_WIDTH=1
# Flash 地址字节数(3=<=16MB, 4=>16MB)
CONFIG_LISA_FLASH_ARCS_ADDR_BYTES=3
# 启用写保护
CONFIG_LISA_FLASH_ARCS_WRITE_PROTECT_ENABLE=y
# Flash 操作时禁用中断
CONFIG_LISA_FLASH_ARCS_DISABLE_INTERRUPTS=y
API 接口
读取 Flash 数据
int lisa_flash_read(lisa_device_t *dev, size_t offset, void *data, size_t len);
从 Flash 的指定偏移地址读取数据到缓冲区。
参数:
dev: Flash 设备指针offset: 读取起始偏移地址(字节)data: 数据缓冲区指针len: 读取长度(字节)
返回值:
LISA_DEVICE_OK (0): 成功LISA_DEVICE_ERR_INVALID: 参数无效LISA_DEVICE_ERR_NOT_SUPPORT: 不支持该操作LISA_DEVICE_ERR_IO: IO 错误
写入 Flash 数据
int lisa_flash_write(lisa_device_t *dev, size_t offset, const void *data, size_t len);
向 Flash 的指定偏移地址写入数据。
参数:
dev: Flash 设备指针offset: 写入起始偏移地址(字节)data: 数据缓冲区指针len: 写入长度(字节)
返回值:
LISA_DEVICE_OK (0): 成功LISA_DEVICE_ERR_INVALID: 参数无效LISA_DEVICE_ERR_NOT_SUPPORT: 不支持该操作LISA_DEVICE_ERR_IO: IO 错误LISA_DEVICE_ERR_NO_MEM: 内存不足(当数据位于 Flash 区域且需要临时缓冲区时)
擦除 Flash 区域
int lisa_flash_erase(lisa_device_t *dev, size_t offset, size_t size);
擦除 Flash 的指定区域。擦除后该区域的所有字节值为 0xFF。
参数:
dev: Flash 设备指针offset: 擦除起始偏移地址(字节)size: 擦除大小(字节)
返回值:
LISA_DEVICE_OK (0): 成功LISA_DEVICE_ERR_INVALID: 参数无效LISA_DEVICE_ERR_NOT_SUPPORT: 不支持该操作LISA_DEVICE_ERR_IO: IO 错误
获取 Flash 参数
const lisa_flash_parameters_t *lisa_flash_get_parameters(lisa_device_t *dev);
获取 Flash 设备的运行时参数信息,包括写块大小、能力标志、擦除值等。
参数:
dev: Flash 设备指针
返回值:
指向
lisa_flash_parameters_t结构体的指针:成功NULL: 参数无效或不支持该操作
参数结构体包含:
write_block_size: 最小写对齐和大小(字节)caps.no_explicit_erase: 是否需要显式擦除(SPI Flash 为 false)erase_value: 擦除后的值(通常为 0xFF)
获取 Flash 页面布局
const lisa_flash_pages_layout_t *lisa_flash_page_layout(lisa_device_t *dev, size_t *layout_size);
获取 Flash 设备的页面布局数组,描述 Flash 的完整布局信息。
参数:
dev: Flash 设备指针layout_size: 输出参数,返回布局数组的元素数量
返回值:
指向
lisa_flash_pages_layout_t数组的指针:成功NULL: 参数无效或不支持该操作
布局结构体包含:
pages_count: 此布局段中的页面数量pages_size: 每个页面的大小(字节)
辅助函数
// 从页面布局计算 Flash 总大小
size_t lisa_flash_get_size_from_layout(const lisa_flash_pages_layout_t *layout, size_t layout_size);
// 获取写块大小
size_t lisa_flash_params_get_write_block_size(const lisa_flash_parameters_t *params);
// 获取擦除值
uint8_t lisa_flash_params_get_erase_value(const lisa_flash_parameters_t *params);
// 检查是否需要显式擦除
bool lisa_flash_params_get_no_explicit_erase(const lisa_flash_parameters_t *params);
使用方法
基本步骤
获取设备: 通过
lisa_device_get()获取 Flash 设备查询参数: 调用
lisa_flash_get_parameters()和lisa_flash_page_layout()获取设备信息擦除区域: 调用
lisa_flash_erase()擦除目标区域(写入前必须先擦除)写入数据: 调用
lisa_flash_write()写入数据读取数据: 调用
lisa_flash_read()读取数据
基础使用示例
#include "lisa_flash.h"
// 1. 获取 Flash 设备
lisa_device_t *flash = lisa_device_get("flash0");
if (!lisa_device_ready(flash)) {
return -1;
}
// 2. 查询 Flash 参数
const lisa_flash_parameters_t *params = lisa_flash_get_parameters(flash);
if (!params) {
return -1;
}
// 3. 查询页面布局
size_t layout_count = 0;
const lisa_flash_pages_layout_t *layout = lisa_flash_page_layout(flash, &layout_count);
if (!layout) {
return -1;
}
// 获取页大小和总容量
size_t page_size = layout[0].pages_size;
size_t total_size = lisa_flash_get_size_from_layout(layout, layout_count);
printf("Flash: %zu bytes, page size: %zu bytes\n", total_size, page_size);
// 4. 准备测试数据
uint8_t write_buf[256];
uint8_t read_buf[256];
for (size_t i = 0; i < 256; i++) {
write_buf[i] = (uint8_t)(i & 0xFF);
}
// 5. 擦除 Flash 区域(必须先擦除才能写入)
size_t offset = 0x100000; // 1MB 偏移
int ret = lisa_flash_erase(flash, offset, page_size);
if (ret != LISA_DEVICE_OK) {
printf("Erase failed: %d\n", ret);
return ret;
}
// 6. 写入数据
ret = lisa_flash_write(flash, offset, write_buf, 256);
if (ret != LISA_DEVICE_OK) {
printf("Write failed: %d\n", ret);
return ret;
}
// 7. 读取数据
ret = lisa_flash_read(flash, offset, read_buf, 256);
if (ret != LISA_DEVICE_OK) {
printf("Read failed: %d\n", ret);
return ret;
}
// 8. 验证数据
if (memcmp(write_buf, read_buf, 256) == 0) {
printf("Data verification successful!\n");
} else {
printf("Data verification failed!\n");
}
查询 Flash 设备信息
#include "lisa_flash.h"
lisa_device_t *flash = lisa_device_get("flash0");
// 获取 Flash 参数
const lisa_flash_parameters_t *params = lisa_flash_get_parameters(flash);
if (params) {
size_t write_block = lisa_flash_params_get_write_block_size(params);
uint8_t erase_value = lisa_flash_params_get_erase_value(params);
bool no_erase = lisa_flash_params_get_no_explicit_erase(params);
printf("Write block size: %zu bytes\n", write_block);
printf("Erase value: 0x%02X\n", erase_value);
printf("Requires explicit erase: %s\n", no_erase ? "No" : "Yes");
}
// 获取页面布局
size_t layout_count = 0;
const lisa_flash_pages_layout_t *layout = lisa_flash_page_layout(flash, &layout_count);
if (layout) {
printf("Layout segments: %zu\n", layout_count);
size_t total_offset = 0;
for (size_t i = 0; i < layout_count; i++) {
printf("Segment %zu: %zu pages × %zu bytes (offset: 0x%zx)\n",
i, layout[i].pages_count, layout[i].pages_size, total_offset);
total_offset += layout[i].pages_count * layout[i].pages_size;
}
size_t total_size = lisa_flash_get_size_from_layout(layout, layout_count);
printf("Total flash size: %zu bytes (%zu KB)\n", total_size, total_size / 1024);
}
处理大数据写入
#include "lisa_flash.h"
lisa_device_t *flash = lisa_device_get("flash0");
// 获取页面布局
size_t layout_count = 0;
const lisa_flash_pages_layout_t *layout = lisa_flash_page_layout(flash, &layout_count);
size_t page_size = layout[0].pages_size;
// 准备大数据缓冲区(例如 8KB)
#define DATA_SIZE (8 * 1024)
uint8_t large_data[DATA_SIZE];
for (size_t i = 0; i < DATA_SIZE; i++) {
large_data[i] = (uint8_t)(i & 0xFF);
}
// 计算需要擦除的页数
size_t offset = 0x200000; // 2MB 偏移
size_t pages_needed = (DATA_SIZE + page_size - 1) / page_size;
size_t erase_size = pages_needed * page_size;
// 擦除足够的空间
printf("Erasing %zu pages (%zu bytes) at offset 0x%zx\n",
pages_needed, erase_size, offset);
int ret = lisa_flash_erase(flash, offset, erase_size);
if (ret != LISA_DEVICE_OK) {
printf("Erase failed: %d\n", ret);
return ret;
}
// 写入大数据
printf("Writing %zu bytes...\n", DATA_SIZE);
ret = lisa_flash_write(flash, offset, large_data, DATA_SIZE);
if (ret != LISA_DEVICE_OK) {
printf("Write failed: %d\n", ret);
return ret;
}
printf("Write completed successfully\n");
检查 Flash 是否需要擦除
#include "lisa_flash.h"
lisa_device_t *flash = lisa_device_get("flash0");
const lisa_flash_parameters_t *params = lisa_flash_get_parameters(flash);
// 准备数据
uint8_t data[256];
memset(data, 0x55, sizeof(data));
size_t offset = 0x100000;
// 检查是否需要显式擦除
if (!lisa_flash_params_get_no_explicit_erase(params)) {
// 需要显式擦除(SPI Flash)
printf("Device requires explicit erase\n");
// 获取页大小
size_t layout_count = 0;
const lisa_flash_pages_layout_t *layout = lisa_flash_page_layout(flash, &layout_count);
size_t page_size = layout[0].pages_size;
// 擦除
lisa_flash_erase(flash, offset, page_size);
} else {
// 无需显式擦除(设备自动擦除或不需要擦除)
printf("Device does not require explicit erase\n");
}
// 写入数据
lisa_flash_write(flash, offset, data, sizeof(data));
硬件配置
Flash 设备规格
参数 |
说明 |
|---|---|
设备名称 |
flash0 |
接口类型 |
SPI(支持标准/双/四路模式) |
扇区大小 |
4096 字节(默认) |
写块大小 |
4 字节(最小写入单位) |
擦除值 |
0xFF |
容量检测 |
自动通过 JEDEC ID 识别 |
地址模式
驱动支持 3 字节和 4 字节地址模式:
地址字节数 |
支持容量 |
配置 |
|---|---|---|
3 字节 |
≤ 16MB |
|
4 字节 |
> 16MB |
|
SPI 数据宽度
数据宽度 |
模式 |
配置 |
|---|---|---|
1 |
标准 SPI |
|
2 |
双 SPI |
|
4 |
四 SPI |
|
注意事项
写入前必须擦除: SPI Flash 必须先擦除目标区域才能写入,写入前必须调用
lisa_flash_erase()擦除对齐: 擦除操作的
offset和size必须按照页(扇区)大小对齐,通常为 4096 字节擦除后值: Flash 擦除后所有字节值为 0xFF
写入对齐: 写入操作建议按照
write_block_size对齐,通常为 4 字节地址范围: 读写擦除操作的
offset和size必须在有效的 Flash 容量范围内数据位置: 如果写入的数据位于 Flash 物理地址区域,驱动会自动将数据复制到 RAM 缓冲区以避免冲突
线程安全: 驱动内部已实现线程保护,可在多线程环境中使用
写保护: 驱动会在写入和擦除操作时自动控制写保护(根据配置)
中断控制: 可配置在 Flash 操作期间禁用中断,避免中断访问 Flash 导致冲突
设备初始化: Flash 设备在系统启动时自动初始化,无需手动初始化
容量检测: 驱动通过读取 JEDEC ID 自动检测 Flash 容量,无需手动配置
页面布局: SPI Flash 通常为均匀扇区布局,
layout_count为 1