文章目錄
- 前言
- 一、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。
具體問題路徑: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 物理特性
- 內存對齊要求
通過系統性的問題分析和逐步調試,最終成功解決了所有編譯和運行時錯誤。