实战四:参数掉电保存与多核运行——Flash 分区、NVS 存储与双核 SMP
在最后一节综合实战中,我们将解决两个高阶工程问题:
参数持久化:在
qemu_cortex_a53上通过app.overlay挂载zephyr,sim-flash模拟 Flash 控制器与storage_partition分区,使用 NVS 非易失键值存储 保存启动次数与报警阈值。多核对称多处理(SMP):利用
qemu_cortex_a53内置的双核 Cortex-A53 拓扑,切换目标为qemu_cortex_a53/qemu_cortex_a53/smp启动从核CPU #1。
跨版本兼容提示(Zephyr 3.7 LTS vs 4.x latest)
正如 从旧版教程迁移 所记录,NVS 头文件路径与 Flash Map 分区宏在 3.7 LTS 与 4.1+ 之间进行了规范演进。通过 <zephyr/version.h> 的 ZEPHYR_VERSION_CODE 宏,可以写出同时兼容两个版本的工程代码:
功能项 |
Zephyr 3.7 LTS |
Zephyr 4.1+ / |
|---|---|---|
NVS 头文件 |
|
|
获取分区设备 |
|
|
获取分区偏移 |
|
|
Step 1:编写 app.overlay 与 prj.conf
创建 smart_node/app.overlay,在 /soc 下挂载模拟 Flash 控制器并划分 16 KiB 的 storage_partition:
/ {
soc {
sim_flash_controller: sim_flash_controller@50000000 {
compatible = "zephyr,sim-flash";
reg = <0x0 0x50000000 0x0 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
erase-value = <0xff>;
flash_sim0: flash_sim@0 {
compatible = "soc-nv-flash";
reg = <0x00000000 0x10000>;
erase-block-size = <4096>;
write-block-size = <4>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
storage_partition: partition@0 {
label = "storage";
reg = <0x00000000 0x00004000>;
};
};
};
};
};
};
创建 smart_node/prj.conf:
CONFIG_LOG=y
CONFIG_SHELL=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_SIMULATOR=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
Step 2:编写 src/main.c 实现 NVS 挂载与读写验证
#include <zephyr/kernel.h>
#include <zephyr/version.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/storage/flash_map.h>
#if ZEPHYR_VERSION_CODE >= ZEPHYR_VERSION(4, 1, 0)
#include <zephyr/kvss/nvs.h>
#define STORAGE_DEV PARTITION_DEVICE(storage_partition)
#define STORAGE_OFF PARTITION_OFFSET(storage_partition)
#else
#include <zephyr/fs/nvs.h>
#define STORAGE_DEV FIXED_PARTITION_DEVICE(storage_partition)
#define STORAGE_OFF FIXED_PARTITION_OFFSET(storage_partition)
#endif
#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include <stdlib.h>
LOG_MODULE_REGISTER(smart_node_capstone, LOG_LEVEL_INF);
#define NVS_KEY_BOOT_COUNT 1
#define NVS_KEY_TEMP_THRESHOLD 2
static struct nvs_fs g_nvs;
static int32_t g_temp_threshold = 32;
static int storage_init(void)
{
struct flash_pages_info info;
int rc;
g_nvs.flash_device = STORAGE_DEV;
if (!device_is_ready(g_nvs.flash_device)) {
LOG_ERR("Simulated Flash device not ready");
return -ENODEV;
}
g_nvs.offset = STORAGE_OFF;
rc = flash_get_page_info_by_offs(g_nvs.flash_device, g_nvs.offset, &info);
if (rc != 0) {
return rc;
}
g_nvs.sector_size = info.size;
g_nvs.sector_count = 3U;
rc = nvs_mount(&g_nvs);
if (rc != 0) {
LOG_ERR("nvs_mount failed: %d", rc);
return rc;
}
uint32_t boot_cnt = 0;
if (nvs_read(&g_nvs, NVS_KEY_BOOT_COUNT, &boot_cnt, sizeof(boot_cnt)) > 0) {
boot_cnt++;
} else {
boot_cnt = 1;
}
(void)nvs_write(&g_nvs, NVS_KEY_BOOT_COUNT, &boot_cnt, sizeof(boot_cnt));
if (nvs_read(&g_nvs, NVS_KEY_TEMP_THRESHOLD, &g_temp_threshold,
sizeof(g_temp_threshold)) <= 0) {
(void)nvs_write(&g_nvs, NVS_KEY_TEMP_THRESHOLD, &g_temp_threshold,
sizeof(g_temp_threshold));
}
LOG_INF("NVS mounted (sector=%u B): boot_cnt=%u, saved_threshold=%d C",
g_nvs.sector_size, boot_cnt, g_temp_threshold);
return 0;
}
static int cmd_node_save(const struct shell *sh, size_t argc, char **argv)
{
ARG_UNUSED(argc);
int32_t new_th = (int32_t)strtol(argv[1], NULL, 10);
g_temp_threshold = new_th;
ssize_t written = nvs_write(&g_nvs, NVS_KEY_TEMP_THRESHOLD,
&g_temp_threshold, sizeof(g_temp_threshold));
int32_t verify_th = -1;
(void)nvs_read(&g_nvs, NVS_KEY_TEMP_THRESHOLD, &verify_th, sizeof(verify_th));
shell_print(sh, "Saved threshold=%d C to NVS (written=%zd B, readback=%d C)",
new_th, written, verify_th);
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(sub_node,
SHELL_CMD_ARG(save, NULL, "Save threshold to NVS: node save <C>", cmd_node_save, 2, 0),
SHELL_SUBCMD_SET_END
);
SHELL_CMD_REGISTER(node, &sub_node, "Smart Node NVS & Capstone commands", NULL);
int main(void)
{
LOG_INF("=== Smart Node Capstone on %s (CPU #%u, MP_MAX_NUM_CPUS=%d) ===",
CONFIG_BOARD, arch_curr_cpu()->id, CONFIG_MP_MAX_NUM_CPUS);
storage_init();
return 0;
}
Step 3:以双核 SMP 目标构建并验证
使用 qemu_cortex_a53/qemu_cortex_a53/smp 目标构建并启动:
west build -p always -b qemu_cortex_a53/qemu_cortex_a53/smp -d build_step4 smart_node
west build -d build_step4 -t run
实测终端输出如下(Secondary CPU core 1 (MPID:0x1) is up 表明双核已激活,node save 42 成功写入 NVS 并读回验证):
*** Booting Zephyr OS build 321e39a45766 ***
Secondary CPU core 1 (MPID:0x1) is up
[00:00:00.000,000] <inf> smart_node_capstone: === Smart Node Capstone on qemu_cortex_a53 (CPU #0, MP_MAX_NUM_CPUS=2) ===
[00:00:00.000,000] <inf> fs_nvs: 3 Sectors of 4096 bytes
[00:00:00.000,000] <inf> fs_nvs: alloc wra: 0, fe8
[00:00:00.000,000] <inf> fs_nvs: data wra: 0, 0
[00:00:00.000,000] <inf> smart_node_capstone: NVS mounted (sector=%u B): boot_cnt=1, saved_threshold=32 C
uart:~$ node save 42
Saved threshold=42 C to NVS (written=4 B, readback=42 C)