当前位置: 首页 > news >正文

【Zephyr开发实践系列】08_NVS文件系统调试记录

文章目录

  • 前言
  • 一、NVS原理介绍:
  • 二、BUG-NO1:将NVS运用在NAND-Flash类大容量存储设备
    • 2.1 情况描述:
    • 2.2 BUG复现:
      • 文件系统设备树构建
      • 测试应用编写(导致错误部分):
      • 问题呈现:
    • 2.3 问题简述:
      • 问题定位:
      • 强制实现:
    • 2.3 最后解决:
  • 三、BUG-NO2:nvs_partition地址范围超出内存范围
    • 3.1 情况描述:
    • 3.2 BUG复现:
      • 文件系统设备树构建:
      • 测试代码编写(无错代码):
      • 问题定位:
    • 3.3 最后解决:
  • 总结


前言

在嵌入式系统开发中,稳定可靠的非易失性存储方案是系统健壮性的关键保障。近期在项目芯片驱动开发中,先后实现了内部Flash和外部NAND Flash的基础驱动验证。随即需要着手构建文件系统层->NVS与LittleFS对Flash进行统一文件管理。
选NVS,如果

  • 只需要存储少量配置参数(如Wi-Fi SSID、设备序列号)。
  • 数据需要频繁更新(如计数器、状态标志)。
  • 对RAM/Flash占用极其敏感。

选LittleFS,如果:

  • 需要存储日志、图片、固件等较大文件。
  • 需要目录管理(如/config/device.cfg)。
  • 设备可能频繁断电,需强数据一致性。

但一般可以互相搭配使用

  • NVS 存储关键配置(如蓝牙MAC、校准参数)。
  • LittleFS 管理日志、固件包等大文件。

本文记录使用内部Flash与外部NAND-Flash进行NVS文件系统操作时出现的BUG与调试情况。

一、NVS原理介绍:

请参考:NVS文件系统原理及应用

二、BUG-NO1:将NVS运用在NAND-Flash类大容量存储设备

2.1 情况描述:

想在外部NAND-Flash上构建类似EEPROM专门存储小数据的掉电记忆系统,于是碰见了NVS。

2.2 BUG复现:

文件系统设备树构建

boot_partition: partition@0 {label = "mcuboot";reg = <0x00000000 DT_SIZE_M(1)>;};lfs1_partition: partition@100000 {reg = <0x00100000 DT_SIZE_M(510)>;};slot0_partition: partition@1FF00000 {label = "image-0";reg = <0x1FF00000 DT_SIZE_K(128)>;};slot1_partition: partition@1FF20000 {label = "image-1";reg = <0x1FF20000 DT_SIZE_K(128)>;};nvs_partition: partition@1FF40000 {label = "nvs";reg = <0x1FF40000 DT_SIZE_K(256)>;};

说明将512M NAND-Flash划分为四个分区:

  • boot_partition:存放mcu-boot
  • lfs1_partition:LittleFS文件系统管理处
  • slot0_partition:迭代版本程序0
  • slot1_partition:迭代版本程序1
  • nvs_partition:NVS文件系统管理处
    1M+510M+128K+128K+256K<512M,使用该分配是合理的。

测试应用编写(导致错误部分):

/* 最小擦除单位是128KB*/
#define NVS_SECTOR_SIZE  128*1024
#define NVS_SECTOR_COUNT (DT_SIZE_K(256K) / NVS_SECTOR_SIZE)  // 256KB / 128KB = 2 static struct nvs_fs fs = {.sector_size = NVS_SECTOR_SIZE, //最小擦除单位*n.sector_count = NVS_SECTOR_COUNT, //大于等于2.offset = NVS_PARTITION_OFFSET, //起始地址,必须是可擦除区域的边界:1FF40000/128K无余数
};

说明:

  • sector_size:最小擦除单位,针对NOR-Flash叫扇区,一般是1-2K。而NAND-Flash,无扇区概念,额定最小擦除单位(块):128K
  • sector_count:扇区个数。sector_size*sector_count小于等于nvs_partition大小即可。
  • offset:可擦除边界地址

问题呈现:

编译后报错:NVS_SECTOR_SIZE Over sector_size(0~65536)

2.3 问题简述:

问题定位:

sector_size的数据格式是uint16(0~65535)=64K,若输入范围大于uint16将会返回错误。
定义文件路径:settings_env.c

	if (nvs_sector_size > UINT16_MAX) {return -EDOM;}

我们NAND-Flash最小擦除范围是128K,大于uint16。

强制实现:

尝试强制将NAND-Flash改为sector_size的范围(64K),同时增加sector_count数。
经尝试编译通过:但毫无疑问出现边界未对齐问题,芯片直接跑飞:

[19:57:48.802][00:00:01.387,000] <err> os: ***** USAGE FAULT *****
[19:57:48.804][00:00:01.387,000] <err> os:   Unaligned memory access
[19:57:48.805][00:00:01.387,000] <err> os: r0/a1:  0x00000073  r1/a2:  0x39c00000  r2/a3:  0x00000073
[19:57:48.806][00:00:01.387,000] <err> os: r3/a4:  0x39c00000 r12/ip:  0x0000019b r14/lr:  0x0c0cf2fb
[19:57:48.807][00:00:01.387,000] <err> os:  xpsr:  0x01100000
[19:57:48.808][00:00:01.387,000] <err> os: s[ 0]:  0x00000000  s[ 1]:  0x00000000  s[ 2]:  0x00000000  s[ 3]:  0x00000000
[19:57:48.810][00:00:01.387,000] <err> os: s[ 4]:  0x616c7f25  s[ 5]:  0x3fe55555  s[ 6]:  0xf8198216  s[ 7]:  0x3f326636
[19:57:48.811][00:00:01.387,000] <err> os: s[ 8]:  0x5f00cb5a  s[ 9]:  0xbf912862  s[10]:  0x35793c76  s[11]:  0x3dea39ef
[19:57:48.812][00:00:01.387,000] <err> os: s[12]:  0x40400000  s[13]:  0x40600000  s[14]:  0x3bb54000  s[15]:  0x44160000
[19:57:48.814][00:00:01.387,000] <err> os: fpscr:  0x20040010

2.3 最后解决:

经询问,原因是NVS不支持需要大块擦除的存储设备,特别是NAND-Flash还需要ECC和坏块检测的存储设备,NAND-Flash只能使用LittleFS。
github
具体问题路径:NVS service sector_size limited range is (0~65535)

三、BUG-NO2:nvs_partition地址范围超出内存范围

3.1 情况描述:

幸亏Zephyr频繁在支持与更新,问题一很快得到了官方项目贡献者的回答。
得知NVS无法运用在NAND-Flash于是将NVS运用在内部NOR-Flash上,但由于nvs_partition地址范围超出内存范围,出现读取错误。

3.2 BUG复现:

文件系统设备树构建:

&flash0 {partitions {compatible = "fixed-partitions";#address-cells = <1>;#size-cells = <1>;/* Bootloader保留区 (16KB) */boot_partition: partition@0 {label = "bootloader";reg = <0x00000000 DT_SIZE_K(16)>;read-only;};/* 主应用程序区 (432KB) */app_partition: partition@4000 {label = "app";reg = <0x00004000 DT_SIZE_K(432)>;};/* NVS存储区 (48KB) - 严格对齐2KB边界 */storage_partition: partition@70000 {label = "storage";reg = <0x0007C000 DT_SIZE_K(48)>; };};
};

内部Flash大小是512K,16K+532KB+48KB<512K

测试代码编写(无错代码):

/* 最小擦除单位是2KB*/
#define NVS_SECTOR_SIZE  2*1024
#define NVS_SECTOR_COUNT (DT_SIZE_K(256K) / NVS_SECTOR_SIZE)  // 64KB / 2KB = 32 static struct nvs_fs fs = {.sector_size = NVS_SECTOR_SIZE, //最小擦除单位*n.sector_count = NVS_SECTOR_COUNT, //大于等于2.offset = NVS_PARTITION_OFFSET, //起始地址,必须是可擦除区域的边界
};

编译通过,但读取过程报错:

*** Booting Zephyr OS build v4.1.0-5510-g8d2010e1e179 ***[2025-07-18 12:48:39.376]
[00:00:00.005,000] [1;31m<err> flash_stm32: Read range invalid. Offset: 526328, len: 8[0m[2025-07-18 12:48:39.385]
[00:00:00.013,000] [1;31m<err> flash_stm32: Read range invalid. Offset: 528376, len: 8[0m[2025-07-18 12:48:39.392]
[00:00:00.021,000] [1;31m<err> flash_stm32: Read range invalid. Offset: 528376, len: 8[0m[2025-07-18 12:48:39.405]
[00:00:00.029,000] [1;31m<err> flash_stm32: Read range invalid. Offset: 530424, len: 8[0m[2025-07-18 12:48:39.412]
[00:00:00.037,000] [1;31m<err> flash_stm32: Read range invalid. Offset: 530424, len: 8[0m[2025-07-18 12:48:39.420]
[00:00:00.045,000] [1;31m<err> flash_stm32: Read range invalid. Offset: 532472, len: 8[0m[2025-07-18 12:48:39.428]

问题定位:

看似合理,但是512KB = 0x00000000 ~ 0x0007FFFF
地址计算错误:
storage_partition分区的起始地址 0x7C000 + 大小 0xC000 = 0x88000>512K,但实际Flash结束于 0x7FFFF。

根据NVS算法原理:NVS在存储时会向ate(记录分配表)里写入一组数据,ate在扇区中从后向前增长。所以说明部分ID数据的ATE表会保存在最后一块扇区的最后几位即0x88000附近>512K。这就是导致问题的直接原因。

3.3 最后解决:

&flash0 {partitions {compatible = "fixed-partitions";#address-cells = <1>;#size-cells = <1>;/* Bootloader保留区 (16KB) */boot_partition: partition@0 {label = "bootloader";reg = <0x00000000 DT_SIZE_K(16)>;read-only;};/* 主应用程序区 (432KB) */app_partition: partition@4000 {label = "app";reg = <0x00004000 DT_SIZE_K(432)>;};/* NVS存储区 (48KB) - 严格对齐2KB边界 */storage_partition: partition@70000 {label = "storage";reg = <0x00070000 DT_SIZE_K(48)>; };};
};

0x00070000 +48K<512K,符合要求,测试顺利!!!!

总结

在 Zephyr 上配置 NVS 时,需要综合考虑:

  • 设备树分区布局
  • Flash 物理特性
  • 内存对齐要求

通过系统性的问题分析和逐步调试,最终成功解决了所有编译和运行时错误。

http://www.lryc.cn/news/592392.html

相关文章:

  • 基于单片机汽车驾驶防瞌睡防疲劳报警器自动熄火设计
  • 如何上传github(解决git的时候输入正确的账号密码,但提示认证失败)
  • PPIO × Lemon AI:一键解锁全流程自动化开发能力
  • 【PHP】Swoole:CentOS安装MySQL+Swoole
  • 易用性强短视频矩阵平台源头开发商推荐
  • 替代标准库:实用 C++ 开源组件推荐
  • 前端篇——番外篇 Bootstrap框架
  • 短视频矩阵的时代结束了吗?
  • linux 的list_for_each_entry
  • 五分钟掌握 TDengine 数据文件的工作原理
  • 【Unity】IEnumeratorCoroutine
  • 【面试题】大厂高压面经实录丨第二期
  • 解锁 Java 并发编程的奥秘:《Java 并发编程之美》中的技术亮点与难题攻克
  • 构建直播平台大体的流程
  • Netty封装Websocket并实现动态路由
  • 在git中同时配置gitcode和github访问权限
  • 微信小程序 wx.request() 的封装
  • 【图像处理基石】什么是CCM?
  • 解决 Selenium 页面跳转过快导致的内容获取问题:从原理到实践
  • 填坑 | React Context原理
  • 29、鸿蒙Harmony Next开发:深浅色适配和应用主题换肤
  • 私有服务器AI智能体搭建配置选择记录
  • 苍穹外卖项目日记(day12)
  • 再探Java多线程Ⅱ --- (创建方式+等待唤醒+Lock锁)
  • 【论文蒸馏】Recent Advances in Speech Language Models: A Survey
  • 《设计模式之禅》笔记摘录 - 8.命令模式
  • 企业如何让内部视频仅限公司官网或指定域名播放?
  • 2025年SEVC SCI2区,利用增强粒子群算法(MR-MPSO)优化MapReduce效率和降低复杂性,深度解析+性能实测
  • 某邮生活旋转验证码逆向
  • 5W8-3D牢游戏超级大集合[2012年6月] 地址 + 解压密码