> 技术文档 > 【Zephyr开发实践系列】06_存储块设备驱动开发(Nand Flash)_zephyr驱动开发

【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与闪存数据一致性

网站制作