fbb_ws63 项目 NOR Flash 驱动架构分析

6 阅读8分钟

fbb_ws63 项目 NOR Flash 驱动架构分析

📋 项目现状总结

好消息:✅ 项目已经完整支持外挂 NOR Flash!

项目使用 SFC(Serial Flash Controller)驱动框架,可直接接入外挂 NOR Flash,无需 MCU 片上 Flash。


🏗️ 整体架构

应用层 (littlefs 文件系统)
        ↓
fs_adapt_* API (littlefs_adapt.c/h)
        ↓
SFC 驱动框架 (sfc.h/sfc.c)
        ↓
HAL 层 (hal_sfc_v150.c)
        ↓
硬件接口
        ↓
外挂 NOR Flash (SPI 接口)

🔧 驱动框架结构

1. 核心文件清单

src/drivers/chips/ws63/porting/sfc/
├── driver/
│   └── sfc.c                    # SFC 驱动核心实现
├── flash_config/
│   └── flash_config_info.c      # Flash 芯片配置表
├── porting/
│   ├── sfc_porting.c            # 平台移植层
│   └── sfc_protect.c            # Flash 保护功能
├── sfc_porting.h                # SFC 接口头文件
├── flash_config_info.h          # Flash 配置信息头文件
└── CMakeLists.txt

2. 驱动层级

层级文件功能
应用层littlefs_adapt.c文件系统适配层,调用 SFC API
驱动层sfc.c高层驱动,处理 Flash 读写擦
HAL 层hal_sfc_v150.c硬件抽象层,SFC 控制器寄存器操作
移植层sfc_porting.c平台相关配置(地址、寄存器等)
配置层flash_config_info.cFlash 芯片命令配置表

💾 支持的 NOR Flash 芯片

已配置的 Flash 型号表

/* flash_config_info.c */

#define FLASH_W25Q16         0x1560EF   // Winbond 采样代码
#define FLASH_W25Q32         0x1660EF
#define FLASH_W25Q64         0x1760EF
#define FLASH_W25Q80         0x1460EF
#define FLASH_W25Q40         0x1360EF
#define FLASH_P25Q80         0x146085   // 文艺 (Paragon)
#define FLASH_GD25WD40       0x1364C8   // 兆易创新 GigaDevice
#define FLASH_G25LE80        0x1460C8
#define FLASH_GD25LQ64       0x1760C8
#define FLASH_GD25LQ32       0x1660C8
#define FLASH_GD25LQ16       0x1560C8
#define FLASH_GD25Q32        0x1640C8
#define FLASH_EN25S80        0x14381C   // EON

Flash 规格型号对应关系

芯片ID制造商型号容量说明
0x1560EFWinbondW25Q162 MB-
0x1660EFWinbondW25Q324 MB✅ 当前配置使用
0x1760EFWinbondW25Q648 MB-
0x1460EFWinbondW25Q801 MB-
0x1640C8GigaDeviceGD25Q324 MB✅ 生产配置
0x1760C8GigaDeviceGD25LQ648 MB-

📝 核心 SFC API

初始化接口

/* sfc.c */

// 初始化 SFC 控制器
errcode_t uapi_sfc_init(sfc_flash_config_t *config);

typedef struct sfc_flash_config {
    uint32_t mapping_addr;          // Flash 地址空间映射地址
    uint32_t mapping_size;          // 映射大小
    sfc_read_if_t read_type;        // 读接口类型 (Standard/Dual/Quad)
    sfc_write_if_t write_type;      // 写接口类型
} sfc_flash_config_t;

读写擦接口

/* sfc.h */

// 读取数据
errcode_t uapi_sfc_reg_read(uint32_t addr, uint8_t *buf, uint32_t len);

// 写入数据  
errcode_t uapi_sfc_reg_write(uint32_t addr, const uint8_t *buf, uint32_t len);

// 擦除数据
errcode_t uapi_sfc_reg_erase(uint32_t addr, uint32_t size);

🔄 littlefs 如何调用 SFC

littlefs_adapt.c 中的使用

/* littlefs_adapt.c */

// 1. 从分区表获取地址和大小
partition_information_t info;
ret = uapi_partition_get_info(CONFIG_LFS_PARTITION_ID, &info);
uint32_t start_addr = info.part_info.addr_info.addr;  // Flash 起始地址
uint32_t total_size = info.part_info.addr_info.size;  // 分区大小

// 2. 初始化 SFC
sfc_flash_config_t cfg = {
    .mapping_addr = start_addr,
    .mapping_size = total_size,
    .read_type = STANDARD_READ,      // 或 DUAL_READ / QUAD_READ
    .write_type = PAGE_PROGRAM        // 或 QUAD_PROGRAM
};
uapi_sfc_init(&cfg);

// 3. littlefs 的读写擦操作
struct lfs_config {
    .read  = littlefs_adapt_read;    // → uapi_sfc_reg_read()
    .prog  = littlefs_adapt_write;   // → uapi_sfc_reg_write()
    .erase = littlefs_adapt_erase;   // → uapi_sfc_reg_erase()
};

// littlefs_adapt_read 实现
static int littlefs_adapt_read(const struct lfs_config *c, lfs_block_t block, 
                               lfs_off_t off, void *buffer, lfs_size_t size)
{
    // 计算实际地址
    uint32_t addr = g_lfs_block_info.start_block * 4096 + block * 4096 + off;
    
    // 调用 SFC 读取
    return uapi_sfc_reg_read(addr, buffer, size);
}

🎯 Flash 命令配置机制

Flash 操作类型

/* flash_config_info.h */

typedef struct spi_opreation {
    uint32_t cmd_support : 3;    // 是否支持(1=支持)
    uint32_t cmd : 8;            // SPI 指令码
    uint32_t iftype : 3;          // 接口类型(SPI/Dual/Quad)
    uint32_t size : 18;           // 大小(擦除大小或 dummy 字节)
} spi_opreation_t;

Flash 操作命令表

/* flash_config_info.c - 以 GD25Q32 为例 */

// 读命令配置
static const spi_opreation_t g_flash_common_read_cmds[] = {
    {SPI_CMD_SUPPORT, 0x03, 0x0, 0x0},   // 标准读(33MHz)
    {SPI_CMD_SUPPORT, 0x0B, 0x0, 0x1},   // 快速读(FAST READ,94MHz)
    {SPI_CMD_SUPPORT, 0x3B, 0x1, 0x1},   // 双线读
    {SPI_CMD_SUPPORT, 0xBB, 0x2, 0x1},   // 双线 I/O 读
    {SPI_CMD_SUPPORT, 0x6B, 0x5, 0x1},   // 四线读(QUAD READ)
    {SPI_CMD_SUPPORT, 0xEB, 0x6, 0x3}    // 四线 I/O 读(最快)
};

// 写命令配置
static const spi_opreation_t g_flash_common_write_cmds[] = {
    {SPI_CMD_SUPPORT, 0x0, 0x0, 0},      // 无操作
    {SPI_CMD_SUPPORT, 0x02, 0x0, 0},     // 页编程(PAGE PROGRAM,256 字节)
    SPI_CMD_UNSUPPORT,                    // 预留
    SPI_CMD_UNSUPPORT,                    // 预留
    {SPI_CMD_SUPPORT, 0x32, 0x5, 0},     // 四线编程
};

// 擦除命令配置
static const spi_opreation_t g_flash_common_erase_cmds[] = {
    {SPI_CMD_SUPPORT, 0xC7, 0x0, 0x3ffff},  // 芯片擦除
    {SPI_CMD_SUPPORT, 0xD8, 0x0, 0x10000},  // 64K 扇区擦除
    {SPI_CMD_SUPPORT, 0x52, 0x0, 0x8000},   // 32K 块擦除
    {SPI_CMD_SUPPORT, 0x20, 0x0, 0x1000}    // 4K 扇区擦除
};

// Quad 模式使能配置
static const flash_cmd_execute_t g_flash_gd_bus_enable[] = {
    { FLASH_CMD_TYPE_PROCESSING, 3, { 0x05, 0, 0x0 } },   // 检查状态
    { FLASH_CMD_TYPE_PROCESSING, 3, { 0x35, 1, 0x1 } },   // 设置四线模式
    { FLASH_CMD_TYPE_END, 0, { 0x0 } }                     // 结束
};

🚀 如何扩展支持新 Flash 芯片

步骤1:获取 Flash 信息

  • Flash ID(例如:0x1640C8)
  • 数据手册,确认支持的操作类型

步骤2:添加到配置表

// flash_config_info.c

// 步骤 1:定义 Flash ID
#define FLASH_NEW_MODEL       0xXXXXXX

// 步骤 2:定义命令操作(如需与现有不同)
static const spi_opreation_t g_flash_new_read_cmds[] = {
    // 复制或修改现有配置
};

// 步骤 3:添加到 Flash 信息列表
static const flash_spi_info_t g_flash_spi_info_list[] = {
    {
        FLASH_GD25Q32,
        FLASH_SIZE_4MB,
        FLASH_ERASE_CMD_NUM_4,
        (spi_opreation_t *)g_flash_common_read_cmds,
        (spi_opreation_t *)g_flash_common_write_cmds,
        (spi_opreation_t *)g_flash_common_erase_cmds,
        (flash_cmd_execute_t *)g_flash_gd_bus_enable
    },
    // <<<< 添加新芯片
    {
        FLASH_NEW_MODEL,
        FLASH_SIZE_4MB,
        FLASH_ERASE_CMD_NUM_4,
        (spi_opreation_t *)g_flash_new_read_cmds,
        // ...
    }
};

步骤3:编译并测试


📊 性能指标

SFC 驱动性能

操作接口类型速率说明
读取标准 SPI~33 MHz单线
快速读~94 MHz预留 dummy 周期
双线读~120 MHz2 线
四线读~150+ MHz4 线(最快)
写入页编程~1 MB/s256 字节/页
四线编程~2 MB/s支持 QuadSPI
擦除4KB~50 ms最小粒度
32KB~200 ms-
64KB~300 ms-
芯片几秒全芯片擦除

🔌 硬件接口

SPI 引脚连接示意

MCU (WS63)          NOR Flash (例如 GD25Q32)
─────────────────────────────────────
CS     ──────────► CS (Chip Select)
CLK    ──────────► CLK (Clock)
MOSI   ──────────► DI (Data Input)
MISO   ◄────────── DO (Data Output)
GND    ──────────► GND

可选四线模式:
WP     ◄────────── WP (Write Protect) - 用于四线模式
HOLD   ◄────────── HOLD (Hold) - 用于暂停传输

时序要求

  • 时钟频率:支持 33MHz - 150MHz(取决于 Flash 和接口类型)
  • 传输模式:SPI 模式 0(CPOL=0, CPHA=0)

⚙️ 配置参数

littlefs 分区配置

/* src/include/common_def.h 或 Kconfig */

#define CONFIG_LFS_PARTITION_ID    X    // littlefs 所在的分区 ID
#define LFS_FLASH_START            0x200000    // Flash 起始地址
#define LFS_FLASH_4K               0x1000      // 块大小

SFC 初始化参数

/* littlefs_adapt.c */

sfc_flash_config_t cfg = {
    .mapping_addr = 0x200000,              // Flash 分区开始地址
    .mapping_size = 0x400000,              // littlefs 分区大小(4MB)
    .read_type = STANDARD_READ,            // 读接口(QUAD_READ 最快)
    .write_type = PAGE_PROGRAM             // 写接口
};

🔍 调试和验证

检查 Flash 是否正确识别

// 获取 Flash ID
uint32_t flash_id;
ret = hal_sfc_get_flash_id(&flash_id);
printf("Flash ID: 0x%X\n", flash_id);

// 验证匹配的配置
// 应输出:GD25Q32 (0x1640C8) 或其他配置的芯片

测试读写功能

// 测试写入
uint8_t test_data[] = "Hello Flash!";
uapi_sfc_reg_write(0x200000, test_data, sizeof(test_data));

// 测试读回
uint8_t read_buf[32];
uapi_sfc_reg_read(0x200000, read_buf, sizeof(test_data));
printf("%s\n", read_buf);  // 应输出:Hello Flash!

// 测试擦除
uapi_sfc_reg_erase(0x200000, 0x1000);  // 擦除 4K

// 再次读取(应全为 0xFF)
uapi_sfc_reg_read(0x200000, read_buf, 32);

📦 集成 littlefs + NOR Flash

完整初始化流程

void init_littlefs_with_nor_flash(void)
{
    // 1. 初始化 SFC 驱动
    sfc_flash_config_t sfc_cfg = {
        .mapping_addr = 0x200000,
        .mapping_size = 0x400000,
        .read_type = QUAD_READ,      // 四线读(最快)
        .write_type = QUAD_PROGRAM
    };
    
    if (uapi_sfc_init(&sfc_cfg) != ERRCODE_SUCC) {
        printf("SFC init failed\n");
        return;
    }
    
    // 2. 挂载 littlefs
    fs_adapt_mount();  // 用我们设计的 API
    
    // 3. 初始化日志管理系统
    log_manager_init();
    
    // 4. 可以开始使用文件系统存储日志
    log_manager_write("littlefs with NOR Flash initialized!");
}

✅ 现有实现清单

功能实现情况说明
SFC 驱动框架✅ 完整hal_sfc_v150.c
Flash 配置表✅ 完整支持 10+ 常见型号
littlefs 适配✅ 完整littlefs_adapt.c
分区管理✅ 完整partition API
四线 Quad 模式✅ 支持高速接口
写保护(WP)✅ 支持sfc_protect.c
扇区擦除优化✅ 支持4K/32K/64K

🎯 总结

你的需求 vs 项目现状

需求实现说明
外挂 NOR Flash✅ 支持SFC 驱动完全实现
多芯片支持✅ 支持10+ 常见制造商
littlefs 集成✅ 支持fs_adapt_* API
日志存储✅ 支持我们的 log_manager
高速接口✅ 支持Quad SPI 最快 150MHz
扩展性✅ 高易于添加新 Flash 型号