跳转至

flash开发指南

概述

Flash存储器是一种非易失性存储技术,支持电擦除和编程。根据接口和架构的不同,常见类型包括NOR Flash、NAND Flash和eMMC。本指南涵盖三种Flash存储类型的适配方法。

架构示意图

**架构示意图**

快速跑通 Flash Demo

功能说明

Flash 信息读取示例 调用 uapi_sfc_get_flash_info(SFC_ID_0, &info) 读取板载 NOR Flash 的 ID 和容量,并检查结果非零。该测试不执行擦除或写入,不会破坏固件和用户数据。

Demo 代码归档路径

samples/native_samples/flash/
├── CMakeLists.txt      # 独立组件构建配置
├── README.md           # 项目级运行说明
├── flash_demo.c        # Flash 信息读取、结果判断和 AT 指令
└── flash_demo.h        # 对外运行接口和 AT 注册函数声明

编译与烧录

完成一站式 CLI 环境配置后执行:

fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

烧录生成的 diting-community.fwpkg,重新上电后以 750000 波特率打开 UART2 串口。

使用方式

AT+FLASHTEST

需要一次性检查多类板载驱动时,也可以执行 AT+DRIVERSELFTEST;如果只需要读取现有外设信息,也可执行 AT+PERIPHERALSID=2

预期结果

Flash ID 和容量取决于板载器件。当前开发板的典型输出如下:

[DRV][FLASH] id=0x001860c8 size=0x01000000
[DRV][FLASH] PASS
OK

文件结构与代码走读

文件职责

文件 职责 关键内容
flash_demo.c 读取并检查 Flash 属性,注册单项 AT 指令 diting_flash_demo_run()
flash_demo.h 公开运行和 AT 注册接口 at_diting_flash_example_cmd_register()
CMakeLists.txt 定义独立组件和注册宏 diting_flash_sampleAT_DITING_EXAMPLE_FLASH_TEST
README.md 说明无损验证边界和预期输出 AT+FLASHTEST

核心流程

运行函数读取 SFC_ID_0 的属性并检查 ID、容量均非零:

bool diting_flash_demo_run(void)
{
    sfc_info_t info = {0};
    errcode_t ret = uapi_sfc_get_flash_info(SFC_ID_0, &info);
    if (ret != ERRCODE_SUCC) {
        return false;
    }
    printf("[DRV][FLASH] id=0x%08x size=0x%08x\n",
        (unsigned int)info.flash_id, (unsigned int)info.flash_size);
    return (info.flash_id != 0U) && (info.flash_size != 0U);
}

AT 处理函数只负责调用公开运行接口并输出 PASSFAIL。测试没有调用 uapi_sfc_erase()uapi_sfc_write(),因此可以作为固件烧录后的无损冒烟测试反复执行。

基于 Flash Demo 开发自己的应用

  1. 复制 samples/native_samples/flash,修改组件名和构建开关,保留 diting_flash_demo_run() 这类可直接调用的运行函数。
  2. 从应用任务、系统服务或 app_run 启动入口调用运行函数,并根据业务处理成功、失败和恢复路径。
  3. 查询其他 SFC 实例时修改 SFC_ID_0,并核对对应端口配置。
  4. 如需增加读写验证,必须使用明确预留的测试分区,先读出备份,验证后恢复;禁止在应用镜像、NV、文件系统或升级分区上直接试写。
  5. 在上层 CMake 和目标配置中增加新组件入口;先保留无损信息读取,再单独实现需要明确测试地址的读写验证。

只有需要串口反复触发板端回归时,才增加 AT 命令名、命令 ID 和 AT 适配层注册。产品业务不应依赖 AT 命令访问 Flash。

Flash存储类型对比

特性 NOR Flash NAND Flash eMMC
接口类型 SPI/并行 SPI/QSPI MMC
读取方式 随机读取,支持XIP 顺序读取 顺序读取
写入方式 字节写入 页写入 块写入
擦除方式 扇区擦除 块擦除 块擦除
读写速度 较慢 较快
成本
寿命 10万次擦写 10万次擦写 有限擦写次数
典型应用 代码存储启动 数据存储 大容量存储

NOR Flash适配

NOR Flash是一种并行或串行接口的闪存,支持代码直接执行(XIP)。芯片通过SFC(Serial Flash Controller)控制NOR Flash。

功能描述

SFC模块提供的接口:

接口名称 功能简述 API文档
uapi_sfc_init 初始化并配置SFC sfc_v2.md
uapi_sfc_erase 擦除Flash指定区域 sfc_v2.md
uapi_sfc_erase_chip 擦除整片Flash sfc_v2.md
uapi_sfc_write 写入Flash数据 sfc_v2.md
uapi_sfc_dma_write 以DMA模式写入Flash sfc_v2.md
uapi_sfc_read 读取Flash数据 sfc_v2.md
uapi_sfc_dma_read 以DMA模式读取Flash sfc_v2.md
uapi_sfc_get_flash_info 获取Flash属性信息 sfc_v2.md
uapi_sfc_enable_continue_read 使能连续读模式 sfc_v2.md
uapi_sfc_disable_continue_read 去使能连续读模式 sfc_v2.md
uapi_sfc_read_unique_id 读取Flash唯一ID sfc_v2.md

配置文件说明

Flash 相关配置参数在 sfc_config_info.c 及相关头文件中,含 Flash ID、Flash 枚举值、Flash 读写相关命令、Flash 参数列表、进入 QSPI 模式命令、配置 dummy cycle、Flash QE 命令列表等参数。

Flash配置参数说明:

static const flash_spi_info_t g_flash_spi_info_list[] = {
    {
        FLASH_W25Q128,              // Flash枚举值
        FLASH_SIZE_16MB,            // Flash大小
        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_winbond_bus_enable, // 总线使能命令
    },
};

Flash ID定义:

#define FLASH_W25Q128           0x1860EF
#define FLASH_W25Q128_IM        0x1880EF

Flash大小定义:

#define FLASH_SIZE_4MB  0x400000
#define FLASH_SIZE_8MB  0x800000
#define FLASH_SIZE_16MB 0x1000000
#define FLASH_SIZE_32MB 0x2000000

适配步骤

Flash ID及枚举值配置

手册

**手册**

Flash ID宏定义

sfc_config_info.h中添加该Flash的ID。

Flash-ID宏定义

Flash相关参数配置

sfc_config_info.cg_flash_spi_info_list表中添加新Flash的信息,需要添加Flash ID、ID、大小、擦除命令数量、读命令、写命令、擦除命令、连续读命令参数:

  1. 根据手册填写Flash ID
  2. 根据手册填写Flash Size
  3. 根据手册填写支持的擦除命令个数
  4. 配置读命令(定义支持的读类型、读命令等)
  5. 配置写命令(定义支持的写类型、写命令等)
  6. 配置擦除命令(包含命令字、擦除范围)

Flash参数列表示意图

Flash参数列表

配置读命令,定义支持的读类型,读命令等,参考Flash datasheeet

static const spi_opreation_t g_flash_common_read_cmds[] = {
    {SPI_CMD_SUPPORT, 0x03, STANDARD_SPI, 0x0},    // 标准SPI读
    {SPI_CMD_SUPPORT, 0x0B, STANDARD_SPI, 0x1},    // 快速读
    {SPI_CMD_SUPPORT, 0x3B, DUAL_INPUT_DUAL_OUTPUT_SPI, 0x1}, // 双线读
    {SPI_CMD_SUPPORT, 0xBB, DUAL_IO_SPI, 0x1},     // 双线I/O读
    {SPI_CMD_SUPPORT, 0x6B, QUAL_INPUT_QUAL_OUTPUT_SPI, 0x1}, // 四线读
    {SPI_CMD_SUPPORT, 0xEB, QUAL_IO_SPI, 0x3}      // 四线I/O读
};
以0xEB命令为例,命令类型为Quad I/O ,dummy cycle为3字节长度,包含M7-0。

手册:

**手册:**

配置写命令,定义支持的写类型,写命令等,参考Flash datasheeet

static const spi_opreation_t g_flash_common_write_cmds[] = {
    {SPI_CMD_SUPPORT, SFC_INVALID_CMD, 0x0, 0},
    {SPI_CMD_SUPPORT, 0x02, STANDARD_SPI, 0},
    SPI_CMD_UNSUPPORT,
    SPI_CMD_UNSUPPORT,
    {SPI_CMD_SUPPORT, 0x32, QUAL_INPUT_QUAL_OUTPUT_SPI, 0},
    SPI_CMD_UNSUPPORT,
};
以0x32命令为例,类型为Quad input。

以0x32命令为例,类型为Quad input

Flash擦除命令:包含命令字、擦除范围,通用型号支持四个擦除命令,参考Flash datasheeet

static const spi_opreation_t g_flash_common_erase_cmds[] = {
    {SPI_CMD_SUPPORT, 0xC7, 0x0, CHIP_SIZE},   // 整片擦除
    {SPI_CMD_SUPPORT, 0xD8, 0x0, _64K},        // 64KB块擦除
    {SPI_CMD_SUPPORT, 0x52, 0x0, _32K},        // 32KB块擦除
    {SPI_CMD_SUPPORT, 0x20, 0x0, _4K}          // 4KB扇区擦除
};

**Flash擦除命令:包含命令字、擦除范围,通用型号支持四个擦除命令,参考Flash datasheeet**

bus enable(含Flash ID、命令长度、读命令字、写命令、写mask、读mask。):

static flash_cmd_qe_enable_t g_flash_cmd_qe_enable[] = {
    {FLASH_W25Q128, 1, { SPI_CMD_RDSR_2, 0}, SPI_CMD_WDSR_2, {0x2, 0}, {0x2, 0}},
    {FLASH_W25Q128_IM, 1, { SPI_CMD_RDSR_2, 0}, SPI_CMD_WDSR_2, {0x2, 0}, {0x2, 0}},
    {FLASH_GD25LQ64, 2, { SPI_CMD_RDSR, SPI_CMD_RDSR_2}, SPI_CMD_WDSR, {0x0, 0x2}, {0x0, 0x2}},
    {FLASH_GD25LE128E, 2, { SPI_CMD_RDSR, SPI_CMD_RDSR_2}, SPI_CMD_WDSR, {0x0, 0x2}, {0x0, 0x2}},
    {FLASH_FM25M4AA,   2, { SPI_CMD_RDSR, SPI_CMD_RDSR_2}, SPI_CMD_WDSR, {0x0, 0x2}, {0x0, 0x2}},
    {FLASH_EN25SX128A, 1, { SPI_CMD_RDSR_2, 0}, SPI_CMD_WDSR_2, {0x2, 0}, {0x2, 0}},
    {FLASH_XT25Q128, 2, { SPI_CMD_RDSR, SPI_CMD_RDSR_2}, SPI_CMD_WDSR, {0x0, 0x2}, {0x0, 0x2}},
    {FLASH_DS25M4AE, 2, { SPI_CMD_RDSR, SPI_CMD_RDSR_2}, SPI_CMD_WDSR, {0x0, 0x2}, {0x0, 0x2}},
};

QE模式配置

部分Flash在切换四线模式前,需要配置QE(Quad Enable)模式:

static flash_cmd_qe_enable_t g_flash_cmd_qe_enable[] = {
    {FLASH_W25Q128, 1, { SPI_CMD_RDSR_2, 0}, SPI_CMD_WDSR_2, {0x2, 0}, {0x2, 0}},
    // 添加新Flash的QE配置
};

需要配置QE bit的Flash列表

需要配置QE-bit的Flash列表

Flash状态寄存器说明

Flash状态寄存器1

Flash状态寄存器2

QE位说明

**Flash状态寄存器说明**

开发指引

SFC初始化

static int test_sfc_init(void)
{
    sfc_flash_config_t sfc_cfg = {0};
    sfc_cfg.mapping_addr = FLASH_START;
    sfc_cfg.mapping_size = FLASH_LENGTH;
    sfc_cfg.read_type = FAST_READ_QUAD_OUTPUT;
    sfc_cfg.write_type = PAGE_PROGRAM;

    errcode_t ret = uapi_sfc_init(SFC_ID_0, &sfc_cfg);
    if (ret != ERRCODE_SUCC) {
        PRINT("flash err = 0x%x \r\n", ret);
        return ERRCODE_FAIL;
    }

    PRINT("sfc init success\r\n");
    return ERRCODE_SUCC;
}

擦除数据

// 擦除指定区域
uapi_sfc_erase(SFC_ID_0, flash_addr, erase_size);

// 擦除整个flash
uapi_sfc_erase_chip(SFC_ID_0);

写入数据

static uint8_t *g_test_sfc_write_buff;
static uint32_t g_test_sfc_buff_len = TEST_SFC_DATA_BUFF_LEN;

// 总线方式写
memcpy_s((uint8_t *)(uintptr_t)(flash_addr + FLASH_START),
         g_test_sfc_buff_len,
         g_test_sfc_write_buff,
         g_test_sfc_buff_len);

// 使用接口写
uapi_sfc_write(SFC_ID_0, flash_addr, g_test_sfc_write_buff, g_test_sfc_buff_len);

// 通过DMA写
uapi_sfc_dma_write(SFC_ID_0, flash_addr, g_test_sfc_write_buff, g_test_sfc_buff_len);

读取数据

static uint8_t *g_test_sfc_read_buff;
static uint32_t g_test_sfc_buff_len = TEST_SFC_DATA_BUFF_LEN;

// 总线方式读
memcpy_s(g_test_sfc_read_buff,
         g_test_sfc_buff_len,
         (uint8_t *)(uintptr_t)(flash_addr + FLASH_START),
         g_test_sfc_buff_len);

// 使用接口读
uapi_sfc_read(SFC_ID_0, flash_addr, g_test_sfc_read_buff, g_test_sfc_buff_len);

// 通过DMA读
uapi_sfc_dma_read(SFC_ID_0, flash_addr, g_test_sfc_read_buff, g_test_sfc_buff_len);

获取Flash信息

sfc_flash_info_t flash_info;
uapi_sfc_get_flash_info(SFC_ID_0, &flash_info);
PRINT("Flash ID: 0x%x, Size: %u MB\r\n",
      flash_info.flash_id,
      flash_info.flash_size / (1024 * 1024));

连续读模式

// 使能连续读模式(使能后只能总线读,不允许其他Flash操作)
uapi_sfc_enable_continue_read(SFC_ID_0);

// 去使能连续读模式
uapi_sfc_disable_continue_read(SFC_ID_0);

读取Unique ID

uint8_t unique_id[8];
uapi_sfc_read_unique_id(SFC_ID_0, unique_id, sizeof(unique_id));

32M NOR Flash额外配置

对于超过16M的NOR Flash,还需进行以下额外配置:

  1. 修改FLASH_LENGTH为Flash实际容量大小,例如,32M需配置为0x2000000。

    修改FLASH_LENGTH示意图

    修改FLASH_LENGTH

  2. 修改分区表信息(build/config/target_config/3322/param_sector/${type}.json

    分区表配置示意图

    分区表配置

  3. 修改 product_evb_standard.h 中的页大小配置

    页大小配置示意图

    页大小配置

注意事项: 由于代码中存储分区size空间为24bit,因此每个分区大小不允许大于等于16M,且需按page对齐。

NAND Flash适配

NAND Flash是一种高密度串行接口闪存,适用于大容量数据存储。

功能描述

接口说明: NAND Flash适配主要涉及配置文件修改,详细驱动接口请参考对应驱动代码。

适配步骤

当前 SDK 已完成对大部分厂商 NAND Flash 的适配,查看 nand_ids.c 中的 nand_flash_info_t 数组:

static struct nand_flash_info nand_flash_info_t[] = {
    {       /* SLC 4bit/512 1.8V */
        .name      = "DS35M1GA",
        .id        = {0xE5, 0x21},
        .id_len    = 2,
        .chipsize  = _128M,     // 颗粒总大小
        .pagesize  = _2K,       // 页大小
        .blocksize = _128K,     // block size大小
        .oobsize   = 64,        // oob大小
        .ecctype   = NAND_ECC_0BIT,
        .badblock_pos = BBP_FIRST_PAGE,
        .driver    = &spi_driver_general,
    },
    // 添加新器件配置
};

NAND Flash存储组织结构

NAND-Flash存储组织图

常见问题

须知: 由于NAND Flash器件支持内部ECC(Error Correcting Code)特性,该特性在版本上需要使用宏ENABLE_ECC进行开启或关闭。当版本上该特性发生切换(开启 -> 关闭或关闭 -> 开启)时,由于NAND Flash内部存储结构发生变化,需要对文件系统进行格式化后再使用。

镜像烧录注意: 如果需要使用镜像烧录功能,请将修改同步到 bootloader 中的 nand_ids.c

eMMC适配

eMMC(Embedded Multi-Media Card)是一种内置控制器的多媒体卡存储接口。

功能描述

eMMC初始化流程按照JESD84-B51标准完成,只要对端器件支持该标准协议,都无需进行特殊适配。

特性说明:

  • 芯片eMMC host支持的最高速率模式为HS200,时钟频率最大支持100MHz
  • eMMC管脚默认空闲态为高,除CLOCK管脚外的CMD和DATA管脚都需要默认为高,建议增加上拉电阻或配置管脚PAD为上拉状态
  • 芯片进入深睡后,会对eMMC的host进行下电操作;唤醒后,会对寄存器进行恢复

注意事项

  1. 在枚举前,请确保eMMC器件的供电正常,对应的RESET管脚配置正常
  2. 本驱动代码上层基于LiteOS中的开源mmc代码实现,底层适配芯片的配置完成
  3. 如需进行二次开发,请在开源代码中进行处理

常见问题

NOR Flash常见问题

问题 解决方案
Flash ID读取为0 检查焊接是否正常
四线模式数据始终为0或0xCC 检查Dummy cycle配置
高频时异常 检查信号完整性
QSPI/QPI模式配置错误 检查模式配置和时序
切四线前需要配置QE模式 确认Flash是否需要QE使能
四线模式数据错位 检查Dummy cycle配置是否正确

NAND Flash常见问题

问题 解决方案
ECC特性切换 需要格式化文件系统
镜像烧录失败 同步修改 bootloader 中的 nand_ids.c
OOB长度不匹配 根据器件手册调整oobsize配置
BLOCK数量差异 根据器件手册调整chipsize配置

eMMC常见问题

问题 解决方案
枚举失败 检查供电和RESET管脚配置
深睡唤醒后异常 确保eMMC器件在深睡时不会下电
速率不匹配 检查时钟配置是否支持HS200模式