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 环境配置后执行:
构建成功后,使用一站式 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+DRIVERSELFTEST;如果只需要读取现有外设信息,也可执行 AT+PERIPHERALSID=2。
预期结果
Flash ID 和容量取决于板载器件。当前开发板的典型输出如下:
文件结构与代码走读
文件职责
| 文件 | 职责 | 关键内容 |
|---|---|---|
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 开发自己的应用
- 复制 samples/native_samples/flash,修改组件名和构建开关,保留
diting_flash_demo_run()这类可直接调用的运行函数。 - 从应用任务、系统服务或
app_run启动入口调用运行函数,并根据业务处理成功、失败和恢复路径。 - 查询其他 SFC 实例时修改
SFC_ID_0,并核对对应端口配置。 - 如需增加读写验证,必须使用明确预留的测试分区,先读出备份,验证后恢复;禁止在应用镜像、NV、文件系统或升级分区上直接试写。
- 在上层 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定义:
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相关参数配置
在sfc_config_info.c的g_flash_spi_info_list表中添加新Flash的信息,需要添加Flash ID、ID、大小、擦除命令数量、读命令、写命令、擦除命令、连续读命令参数:
- 根据手册填写Flash ID
- 根据手册填写Flash Size
- 根据手册填写支持的擦除命令个数
- 配置读命令(定义支持的读类型、读命令等)
- 配置写命令(定义支持的写类型、写命令等)
- 配置擦除命令(包含命令字、擦除范围)
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读
};
手册:

配置写命令,定义支持的写类型,写命令等,参考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,
};

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扇区擦除
};

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列表

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
32M NOR Flash额外配置
对于超过16M的NOR Flash,还需进行以下额外配置:
-
修改
FLASH_LENGTH为Flash实际容量大小,例如,32M需配置为0x2000000。修改FLASH_LENGTH示意图

-
修改分区表信息(
build/config/target_config/3322/param_sector/${type}.json)分区表配置示意图

-
修改 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器件支持内部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进行下电操作;唤醒后,会对寄存器进行恢复
注意事项
- 在枚举前,请确保eMMC器件的供电正常,对应的RESET管脚配置正常
- 本驱动代码上层基于LiteOS中的开源mmc代码实现,底层适配芯片的配置完成
- 如需进行二次开发,请在开源代码中进行处理
常见问题
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模式 |