跳转至

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_sample、AT_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 处理函数只负责调用公开运行接口并输出 PASS 或 FAIL。测试没有调用 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.c的g_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模式