.. _tutorial_step4: 实战四:参数掉电保存与多核运行——Flash 分区、NVS 存储与双核 SMP ############################################################################ 在最后一节综合实战中,我们将解决两个高阶工程问题: #. **参数持久化**:在 ``qemu_cortex_a53`` 上通过 ``app.overlay`` 挂载 ``zephyr,sim-flash`` 模拟 Flash 控制器与 ``storage_partition`` 分区,使用 :doc:`/develop/subsys/storage/nvs` 保存启动次数与报警阈值。 #. **多核对称多处理(SMP)**:利用 ``qemu_cortex_a53`` 内置的双核 Cortex-A53 拓扑,切换目标为 ``qemu_cortex_a53/qemu_cortex_a53/smp`` 启动从核 ``CPU #1``。 跨版本兼容提示(Zephyr 3.7 LTS vs 4.x ``latest``) ================================================== 正如 :doc:`/migration` 所记录,NVS 头文件路径与 Flash Map 分区宏在 3.7 LTS 与 4.1+ 之间进行了规范演进。通过 ```` 的 ``ZEPHYR_VERSION_CODE`` 宏,可以写出同时兼容两个版本的工程代码: .. list-table:: :header-rows: 1 :widths: 25 37 38 * - 功能项 - Zephyr 3.7 LTS - Zephyr 4.1+ / ``latest`` * - NVS 头文件 - ``#include `` - ``#include `` * - 获取分区设备 - ``FIXED_PARTITION_DEVICE(storage_partition)`` - ``PARTITION_DEVICE(storage_partition)`` * - 获取分区偏移 - ``FIXED_PARTITION_OFFSET(storage_partition)`` - ``PARTITION_OFFSET(storage_partition)`` Step 1:编写 ``app.overlay`` 与 ``prj.conf`` ============================================ 创建 ``smart_node/app.overlay``,在 ``/soc`` 下挂载模拟 Flash 控制器并划分 16 KiB 的 ``storage_partition``: .. code-block:: devicetree / { 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``: .. code-block:: ini 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 挂载与读写验证 =================================================== .. code-block:: c #include #include #include #include #include #if ZEPHYR_VERSION_CODE >= ZEPHYR_VERSION(4, 1, 0) #include #define STORAGE_DEV PARTITION_DEVICE(storage_partition) #define STORAGE_OFF PARTITION_OFFSET(storage_partition) #else #include #define STORAGE_DEV FIXED_PARTITION_DEVICE(storage_partition) #define STORAGE_OFF FIXED_PARTITION_OFFSET(storage_partition) #endif #include #include #include 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 ", 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`` 目标构建并启动: .. code-block:: bash 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 并读回验证): .. code-block:: console *** Booting Zephyr OS build 321e39a45766 *** Secondary CPU core 1 (MPID:0x1) is up [00:00:00.000,000] smart_node_capstone: === Smart Node Capstone on qemu_cortex_a53 (CPU #0, MP_MAX_NUM_CPUS=2) === [00:00:00.000,000] fs_nvs: 3 Sectors of 4096 bytes [00:00:00.000,000] fs_nvs: alloc wra: 0, fe8 [00:00:00.000,000] fs_nvs: data wra: 0, 0 [00:00:00.000,000] 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) 相关参考 ======== * :doc:`/develop/driver/flash_introduction` * :doc:`/develop/subsys/storage/flash_map` * :doc:`/develop/subsys/storage/nvs` * :doc:`/develop/subsys/storage/nvs_analyze` * :doc:`/migration`