.. _develop_application: 应用构建、Kconfig 与 Devicetree ############################################################ 本页依据 :doc:`/maintenance` 中记录的 Zephyr ``main`` 快照更新。 Zephyr 的应用配置由源码、CMake、Kconfig、Devicetree 和板级定义共同组成。 先把命令行构建跑通,再配置 IDE,能更容易定位构建或编辑器各自的问题。 独立应用与工作区布局 ==================== 实际项目通常将应用放在 Zephyr 主仓库之外(Out-of-Tree Application),避免直接修改 ``zephyr/samples/``,以便主仓库与模块升级时保持应用目录独立: .. code-block:: text zephyrproject/ <-- West 工作区顶层(含 .west/) ├── zephyr/ <-- Zephyr 官方主仓库(只读) ├── modules/ <-- 第三方 HAL 与组件模块 └── app/ <-- 独立应用工程目录 ├── CMakeLists.txt <-- 构建入口 ├── Kconfig <-- (可选)应用自定义配置菜单 ├── prj.conf <-- 应用 Kconfig 配置文件 ├── app.overlay <-- (可选)设备树叠加层 ├── include/ <-- (可选)应用私有头文件 └── src/ └── main.c <-- 应用主程序入口 最小应用 ======== 在工作区顶层创建 ``app``,最小结构仅需 ``CMakeLists.txt``、``prj.conf`` 和 ``src/main.c``: ``CMakeLists.txt``: .. code-block:: cmake cmake_minimum_required(VERSION 3.20.5) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(cn_demo) target_sources(app PRIVATE src/main.c) ``prj.conf``: .. code-block:: ini CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 ``src/main.c``: .. code-block:: c #include #include LOG_MODULE_REGISTER(cn_demo, LOG_LEVEL_INF); int main(void) { LOG_INF("Zephyr CN application started on %s", CONFIG_BOARD); return 0; } 在工作区顶层构建并运行(可使用 ``qemu_cortex_m3`` 或 64 位 ``qemu_cortex_a53``): .. code-block:: bash west build -p always -b qemu_cortex_a53 -d build app west build -d build -t run ``find_package(Zephyr ...)`` 必须位于 ``project()`` 之前,由 Zephyr 接管交叉编译工具链初始化; ``app`` 是 Zephyr 提供的应用构建目标。当工程包含多个源文件与头文件目录时,可在 ``CMakeLists.txt`` 中使用 ``target_include_directories(app PRIVATE include)`` 和 ``target_sources(app PRIVATE src/main.c src/other.c)``。 Kconfig 选择软件能力 ==================== ``prj.conf`` 配置软件功能、资源数量和参数。构建期间 Kconfig 的合并与生成链路如下: .. code-block:: text [配置定义树: zephyr/Kconfig + app/Kconfig] │ ▼ [按优先级合并输入源] ① Kconfig 默认值 (default) ② 板级默认配置 (_defconfig) ③ 应用配置 (prj.conf / EXTRA_CONF_FILE) │ ▼ [构建目录输出: build/zephyr/] • .config (最终生效的完整配置快照) • include/generated/zephyr/autoconf.h (自动转换为 #define CONFIG_xxx 宏) Kconfig 符号存在依赖与选择关系,强行写 ``CONFIG_...=y`` 不能越过缺失的依赖。可通过交互界面检查依赖: .. code-block:: bash west build -d build -t menuconfig 最终生效值保存在 ``build/zephyr/.config`` 中,生成的 ``autoconf.h`` 会自动注入所有编译单元。 **不要直接手工编辑** ``.config`` 或生成的头文件;``menuconfig`` 中的临时修改需整理回 ``prj.conf``,才能在重新配置或 ``-p always`` 干净构建后复现。 需要定义应用自己的配置项时,在应用目录添加 ``Kconfig``,并包含 ``Kconfig.zephyr``: .. code-block:: kconfig mainmenu "Custom Application Configuration" source "Kconfig.zephyr" config APP_SAMPLE_INTERVAL_MS int "Sensor sampling interval in milliseconds" default 1000 range 100 60000 在 ``Kconfig`` 中定义符号时不写 ``CONFIG_`` 前缀;在 ``prj.conf``、``CMakeLists.txt`` 和 C 源码中引用时统一加上 ``CONFIG_`` 前缀(如 ``CONFIG_APP_SAMPLE_INTERVAL_MS``)。 Devicetree 描述硬件实例 ======================= 设备树描述寄存器地址、引脚、中断、总线、时钟、设备状态和板级连接;``dts/bindings/*.yaml`` 定义节点的属性规则。应用可通过 ``app.overlay``(或 ``-DDTC_OVERLAY_FILE=...``)在不改动官方板级源码的前提下修改或扩展目标硬件描述: .. code-block:: text [SoC .dtsi] + [板级 .dts] + [应用 app.overlay] + [dts/bindings/*.yaml] │ ▼ [构建目录输出: build/zephyr/] • zephyr.dts (最终合并展开的完整设备树) • include/generated/zephyr/devicetree_generated.h (供 DT_* 与 DEVICE_DT_GET 宏使用) 编译生成的 ``devicetree_generated.h`` 供设备树宏使用,不能通过直接修改它替代 overlay。 ``DT_NODELABEL()`` 使用 DTS 节点标签,``DT_ALIAS()`` 使用 ``/aliases`` 中的别名(DTS 中的连字符 ``-`` 在 C 宏中需替换为下划线 ``_``),``DT_CHOSEN()`` 用于获取 ``/chosen`` 约定的系统角色设备。 应用取得设备的常用模式: .. code-block:: c #include #include const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(my_device)); ``my_device`` 是示意标签,必须替换为目标设备树中实际存在且可用的节点。 调用驱动接口前必须检查 ``device_is_ready(dev)``。 **Kconfig 与 Devicetree 的配合与排错**: * 只有同时满足 **① 设备树中存在该节点且** ``status = "okay"``、**② Kconfig 中启用了对应驱动程序**、**③ 运行时驱动初始化成功**,才构成可访问的设备。 * 链接时若出现 ``undefined reference to `__device_dts_ord_XX'`` 错误,打开 ``build/zephyr/zephyr.dts`` 搜索该数字序号 ``XX`` 定位对应节点,通常是节点已在设备树中启用但 ``prj.conf`` 未开启对应的驱动 ``CONFIG_*`` 选项,或节点 ``status`` 未设为 ``"okay"``。 开发板目标与硬件模型 ==================== Zephyr 3.7 引入的新硬件模型改变了许多板名与板级组织。 当前目标可包含板名、SoC、CPU cluster 和 variant,例如 ``nrf52840dk/nrf52840``、``nrf5340dk/nrf5340/cpuapp`` 或双核 SMP 模拟目标 ``qemu_cortex_a53/qemu_cortex_a53/smp``。 旧 ``nrf52840dk_nrf52840`` 名称不能直接假定仍适用。 ``west boards`` 帮助查找板卡,完整目标与 qualifiers 应看对应板卡页面。 开发自定义板卡时按当前 Board Porting Guide 配置 ``board.yml``、SoC 关系、DTS、 Kconfig 与 runner,避免复制旧硬件模型的目录后只改名称。 多镜像与 sysbuild ================= 需要协调引导程序或多个镜像时,可采用 sysbuild: .. code-block:: bash west build --sysbuild -p always -b -d build-sysbuild app 替换目标后再执行。sysbuild 自身配置使用 ``SB_CONFIG_*``,与每个镜像的 ``CONFIG_*`` 属于不同配置域。根据镜像名和域定位最终 .config、DTS 与 ELF, 不要把单镜像的输出路径套用到所有 sysbuild 产物。 可重复构建 ========== 记录源码与模块提交、应用配置、overlay、板卡完整目标、SDK 和构建命令。 切换 Zephyr 版本、板卡或工具链后进行干净构建。 确认命令行构建输出后,再用 ``compile_commands.json`` 配置编辑器,见 :doc:`vscode`。 若希望通过一个完整工程演练 Kconfig、Devicetree Overlay、日志、Shell、GPIO、多线程与 NVS 的综合协同,请继续阅读 :doc:`/tutorial/index`。 上游依据 ======== * `官方文档:develop/application/index `_ * `官方文档:build/kconfig/setting `_ * `官方文档:build/dts/howtos `_ * `官方文档:hardware/porting/board_porting `_ * `官方文档:build/sysbuild/index `_