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);

使用方法

基本步骤

  1. 获取设备: 通过 lisa_device_get() 获取 Flash 设备

  2. 查询参数: 调用 lisa_flash_get_parameters()lisa_flash_page_layout() 获取设备信息

  3. 擦除区域: 调用 lisa_flash_erase() 擦除目标区域(写入前必须先擦除)

  4. 写入数据: 调用 lisa_flash_write() 写入数据

  5. 读取数据: 调用 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

CONFIG_LISA_FLASH_ARCS_ADDR_BYTES=3

4 字节

> 16MB

CONFIG_LISA_FLASH_ARCS_ADDR_BYTES=4

SPI 数据宽度

数据宽度

模式

配置

1

标准 SPI

CONFIG_LISA_FLASH_ARCS_DATA_WIDTH=1

2

双 SPI

CONFIG_LISA_FLASH_ARCS_DATA_WIDTH=2

4

四 SPI

CONFIG_LISA_FLASH_ARCS_DATA_WIDTH=4

注意事项

  1. 写入前必须擦除: SPI Flash 必须先擦除目标区域才能写入,写入前必须调用 lisa_flash_erase()

  2. 擦除对齐: 擦除操作的 offsetsize 必须按照页(扇区)大小对齐,通常为 4096 字节

  3. 擦除后值: Flash 擦除后所有字节值为 0xFF

  4. 写入对齐: 写入操作建议按照 write_block_size 对齐,通常为 4 字节

  5. 地址范围: 读写擦除操作的 offsetsize 必须在有效的 Flash 容量范围内

  6. 数据位置: 如果写入的数据位于 Flash 物理地址区域,驱动会自动将数据复制到 RAM 缓冲区以避免冲突

  7. 线程安全: 驱动内部已实现线程保护,可在多线程环境中使用

  8. 写保护: 驱动会在写入和擦除操作时自动控制写保护(根据配置)

  9. 中断控制: 可配置在 Flash 操作期间禁用中断,避免中断访问 Flash 导致冲突

  10. 设备初始化: Flash 设备在系统启动时自动初始化,无需手动初始化

  11. 容量检测: 驱动通过读取 JEDEC ID 自动检测 Flash 容量,无需手动配置

  12. 页面布局: SPI Flash 通常为均匀扇区布局,layout_count 为 1