文章目录

  • 前言
  • 一、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.pswp.cn/pingmian/89385.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89385.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/89385.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全第二次作业

靶场闯关1~8 1. 在url后的name后输入payload ?name<script>alert(1)</script> 2. 尝试在框中输入上一关的payload,发现并没有通过&#xff0c;此时我们可以点开页面的源代码看看我们输入的值被送到什么地方去了 从图中可以看到&#xff0c;我们输入的值被送到i…

LangChain 源码剖析(七)RunnableBindingBase 深度剖析:给 Runnable“穿衣服“ 的装饰器架构

每一篇文章都短小精悍&#xff0c;不啰嗦。一、功能定位&#xff1a;Runnable 的 "增强包装器"RunnableBindingBase 是 LangChain 中实现装饰器模式的核心组件。它就像给原有 Runnable 套上一件 "功能外套"—— 不改变原有 Runnable 的核心逻辑&#xff0c…

为 Git branch 命令添加描述功能

写在最前面的使用方式 查看 所有分支的备注 git branch.notes创建分支并为分支添加备注 git co -b feat/oauth -m 第三方用户登录对分支描述的添加与清除 添加 git branch.note --add 清除 git branch.note --clear &#x1f4dd; 为 Git branch 命令添加描述功能 &#x…

LeetCode|Day18|20. 有效的括号|Python刷题笔记

LeetCode&#xff5c;Day18&#xff5c;20. 有效的括号&#xff5c;Python刷题笔记 &#x1f5d3;️ 本文属于【LeetCode 简单题百日计划】系列 &#x1f449; 点击查看系列总目录 >> &#x1f4cc; 题目简介 题号&#xff1a;20. 有效的括号 难度&#xff1a;简单 题目…

使⽤Pytorch构建⼀个神经⽹络

关于torch.nn:使⽤Pytorch来构建神经⽹络, 主要的⼯具都在torch.nn包中.nn依赖于autograd来定义模型, 并对其⾃动求导.构建神经⽹络的典型流程:定义⼀个拥有可学习参数的神经⽹络遍历训练数据集处理输⼊数据使其流经神经⽹络计算损失值将⽹络参数的梯度进⾏反向传播以⼀定的规则…

网络爬虫的详细知识点

基本介绍 什么是网络爬虫 网络爬虫&#xff08;Web Crawler&#xff09;是一种自动化程序&#xff0c;用于从互联网上抓取、解析和存储网页数据。其核心功能是模拟人类浏览行为&#xff0c;通过HTTP/HTTPS协议访问目标网站&#xff0c;提取文本、链接、图片或其他结构化信息&…

AndroidX中ComponentActivity与原生 Activity 的区别

一、AndroidX 与原生 Activity 的区别 1. 概念与背景 原生 Activity&#xff1a;指 Android 早期&#xff08;API 1 起&#xff09;就存在于 android.app 包下的 Activity 类&#xff08;如 android.app.Activity&#xff09;&#xff0c;是 Android 最初的 Activity 实现&…

Spring AI 使用 Elasticsearch 作为向量数据库

前言 嗨&#xff0c;大家好&#xff0c;我是雪荷&#xff0c;最近在公司开发 AI 知识库&#xff0c;同时学到了一些 AI 开发相关的技术&#xff0c;这期先与大家分享一下如何用 ES 当做向量数据库。 安装ES 第一步我们先安装 Elasticsearch&#xff0c;这里建议 Elasticsear…

TypeScript 配置全解析:tsconfig.json、tsconfig.app.json 与 tsconfig.node.json 的深度指南

前言在现代前端和后端开发中&#xff0c;TypeScript 已经成为许多开发者的首选语言。然而&#xff0c;TypeScript 的配置文件&#xff08;特别是多个配置文件协同工作时&#xff09;常常让开发者感到困惑。本文将深入探讨 tsconfig.json、tsconfig.app.json 和 tsconfig.node.j…

读书笔记(学会说话)

1、一个人只有会说话&#xff0c;才会有好人缘&#xff0c;做事才会顺利。会说话的人容易成功。善于说话的人易成功&#xff0c;而不善说话的人往往寸步难行。我们要把话说得好听&#xff0c;同时更要把事做得漂亮。或许一句话&#xff0c;一件事&#xff0c;就可能使人生的旅途…

私有服务器AI智能体搭建-大模型选择优缺点、扩展性、可开发

以下是主流 AI 框架与模型的对比分析&#xff0c;涵盖其优缺点、扩展性、可开发性等方面。 文章目录一、AI 框架对比二、主流大模型对比三、扩展性对比总结四、可开发性对比总结五、选择建议&#xff08;按场景&#xff09;六、未来趋势一、AI 框架对比 框架优点缺点扩展性可开…

OpenCV直线段检测算法类cv::line_descriptor::LSDDetector

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该类用于实现 LSD (Line Segment Detector) 直线段检测算法。LSD 是一种快速、准确的直线检测方法&#xff0c;能够在不依赖边缘检测的前提下直接从…

Go语言流程控制(if / for)

分支结构package mainimport ("fmt""strconv" )/* 1.顺序结构 2.分支结构 3.循环结构 *//* if 条件1 {// 条件1为真时执行的代码 } else if 条件2 {// 条件1为假但条件2为真时执行的代码 } else {// 所有条件均为假时执行的代码 }一种特殊的条件分支结构if…

wx小程序设置沉浸式导航文字高度问题

第一步&#xff1a;在app.json中设置"navigationStyle": "custom"第二步骤&#xff1a;文件的home.js中// pages/test/test.js Page({/*** 页面的初始数据*/data: {statusBarHeight: 0,navBarHeight: 44 // 自定义导航内容区高度(单位px)},/*** 生命周期函…

C++算法竞赛篇:DevC++ 如何进行debug调试

C算法竞赛篇&#xff1a;DevC 如何进行debug调试前言一、准备工作&#xff1a;编译生成可执行程序二、核心步骤&#xff1a;设置断点与启动调试1. 设置断点2. 启动调试模式三、调试操作&#xff1a;逐步执行与变量监控1. 逐步执行代码2. 监控变量值变化四、调试结束前言 在算法…

语音大模型速览(三)- cosyvoice2

CosyVoice 2: Scalable Streaming Speech Synthesis with Large Language Models 论文链接&#xff1a;https://arxiv.org/pdf/2412.10117代码链接&#xff1a;https://github.com/FunAudioLLM/CosyVoice 一句话总结 CosyVoice 2 是一款改进的流式语音合成模型&#xff0c;其…

-lstdc++与-static-libstdc++的用法和差异

CMakeLists.txt 里写了&#xff1a; target_link_libraries(${PROJECT_NAME} PRIVATEgccstdc ) target_link_options(${PROJECT_NAME} PRIVATE -static-libstdc)看起来像是“链接了两次 C 标准库”&#xff0c;其实它们的作用完全不同&#xff1a;1. target_link_libraries(...…

Redis学习其二(事务,SpringBoot整合,持久化RDB和AOF)

文章目录5,事务5.1Redis 事务不保证原子性的原因5.2事务操作过程5.3监控6,SpringBoot整合Redis6.1Redis客户端6.1.1Jedis简单使用6.1.2Lettuce&Jedis6.2配置相关6.3使用6.3.1使用RedisTemplate6.3.2Redis工具类7,持久化RDB7.1RDB持久化原理7.2触发机制save命令flushall命令…

springboot项目部署到K8S

java后台 创建harbor镜像拉取Secret&#xff1a;kubectl create secret docker-registry harbor-regcred \--docker-server \ #harbor仓库地址--docker-username \ #harbor 账号--docker-password \ #harbor密码-n productionDockerfile FROM *harbor地址*/library/custom-jdk…

【FPGA开发】一文轻松入门Modelsim的基本操作

Modelsim仿真的步骤 &#xff08;1&#xff09;创建新的工程。 &#xff08;2&#xff09;在弹出的窗口中&#xff0c;确定项目名和工作路径&#xff0c;库保持为work不变(如有需要可以根据需求进行更改)。 &#xff08;3&#xff09;添加已经存在的文件&#xff08;rtl代码和t…