【Zephyr开发实践系列】06_存储块设备驱动开发(Nand Flash)_zephyr驱动开发
文章目录
- 前言
- 一、Flash驱动模型介绍
-
- 1.1 核心基础应用API(必须)
- 1.2 高级功能应用API(可选)
- 1.3 设置数据结构
- 1.4 硬件初始化
- 1.5 设备实例化
- 二、数据结构定义
-
- 2.1 获取Flash块与页大小
- 三、核心API函数实现
-
- 3.1 擦除函数
- 3.2 读取函数
- 3.3 写入函数
- 4.4 layout函数
- 4.5 坏区检测函数
- 总结
前言
在嵌入式系统中,常见的 Flash 存储模块根据接口类型和用途可分为NOR、NAND、EMMC、SD卡等。本文基于NAND-Flash介绍Zephyr下的驱动模型,SPI与HAL层寄存器操作暂不做解析,仅基于API实现最基本接口。
一、Flash驱动模型介绍
最基本的Flash模块驱动模型包括以下几个要点:
1.1 核心基础应用API(必须)
- read:从 Flash 读取数据
- write:写入数据到 Flash
- erase:擦除指定区域(扇区/块)
- get_parameters:获取 Flash 物理参数(页大小、块大小、总容量等)
1.2 高级功能应用API(可选)
- page_layout:提供 Flash 物理布局(如分页/分块信息)
- sfdp_read:通过 SFDP自动识别 Flash 参数
- read_jedec_id:读取JEDEC ID
- ex_op:扩展操作(如 QSPI 四线模式使能、DTR 模式配置等)
1.3 设置数据结构
- 配置结构: 只保留时钟频率/设备、块大小,页大小
1.4 硬件初始化
- 时钟使能: 确保flash模块时钟开启与在要求频率范围
- 引脚配置:若无pin-ctrl系统,需要直接配置引脚
- 取消保护操作: 取消Flash模块块保护
- 根据需要配置模式(四线,两线)
1.5 设备实例化
- 配置结构初始化: 从设备树提取基本配置
- 设备注册宏: 使用DEVICE_DT_INST_DEFINE注册设备
- 初始化优先级: 设置为POST_KERNEL
- 根据需求注册电源管理PM
二、数据结构定义
2.1 获取Flash块与页大小
static const struct flash_parameters shenju_flash_parameters = {#if DT_NODE_HAS_PROP(SOC_FLASH_NODE, write_block_size).write_block_size = DT_PROP(SOC_FLASH_NODE, write_block_size),#else.write_block_size = DEFAULT_WRITE_BLOCK_SIZE,#endif.erase_value = 0xff,};
说明:
- DT_PROP根据设备树获取信息
- 参数write_block_size:128*1024Byte
- 擦除后值为0xFF
三、核心API函数实现
3.1 擦除函数
static int flash_erase(const struct device *dev, off_t offset, size_t len){ if (FLASH_TYPE == NAND) int block_size = spinand_get_block_size(); int block_number = offset / block_size; int block_count = len / block_size; if(len % block_size) block_count++; spinand_erase_length(block_number, block_count, 1); return 0;}
说明:
- 一块block大小为128k
- 根据地址判断待擦除的block号
- 根据擦除的数据长度,若存在少量剩余,则按一块计算
3.2 读取函数
static int flash_read(const struct device *dev, off_t offset, void *data, size_t len){ sys_cache_data_flush_and_invd_range(data, len);if (FLASH_TYPE == NAND) // spinand_read((uint32_t)data, offset, len); spinand_read_quad((uint32_t)data, offset, len); return 0;}
说明:
- 读取数据之前需要确保缓冲区数据与物理内存一致(关键)
- 使用标准SPI或四线Quad SPI进行接收数据
- 标准SPI可不使用cache,Quad SPI必须使用cache方式操作
3.3 写入函数
static int flash_write(const struct device *dev, off_t offset, const void *data, size_t len){ uint32_t write_flash_addr = offset; uint16_t write_len = len; uint8_t *write_data = (uint8_t *)data; if ((ret = spinand_write_enable()) != 0) { return ret;} sys_cache_data_flush_range(write_data, write_len);if (FLASH_TYPE == NAND) // spinand_write((uint32_t)write_data, write_flash_addr, write_len); spinand_write_quad((uint32_t)write_data, write_flash_addr, write_len); return 0;}
说明:
- 发送Write Enable
- 数据维护
- Quad模式页或普通模式写入
4.4 layout函数
void flash_page_layout(const struct device *dev, const struct flash_pages_layout **layout, size_t *layout_size){static struct flash_pages_layout flash_layout = {.pages_count = 0,.pages_size = 0,};ARG_UNUSED(dev);if (flash_layout.pages_count == 0) { flash_layout.pages_size = DT_PROP(DT_CHOSEN(zephyr_flash), block_size);flash_layout.pages_count = DT_REG_SIZE(DT_CHOSEN(zephyr_flash))/flash_layout.pages_size;}*layout = &flash_layout;*layout_size = 1;}
说明:
- 作用于文件分区系统
- 可用于数据更新与板卡升级
4.5 坏区检测函数
void spinand_detect_bad_block_4kpage(void){ uint8_t buf[4]; for (uint32_t i = 0; i < 64; i++) { spinand_read_to_cache(64 * i, 1); spinand_read_from_cache((uint32_t)buf, 1024 * 2, 4); if (buf[0] != 0xff) { LOG_WRN(\"block:%u bad, value:%x\\n\", i, buf[0]); } }}
说明:
-
遍历所有块(假设共64个块)
-
读取每个块的第一页 的OOB(Out-of-Band)区域前4字节
-
检查OOB数据:若首字节非 0xFF,则标记为坏块
总结
这段代码展示了SPI NAND Flash(如W25N04KV)驱动的核心逻辑:通过坏块检测和Quad SPI加速实现高效存储管理。包括:揪出故障区块、四线Quad SPI拉满传输速度、sys_cache系列操作确保CPU与闪存数据一致性