首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Zephyr BSP: 16-Zephyr Devicetree to C 代码生成全过程
📅 2026/9/29 10:53:21
✍️ 爱科研究院
👁 阅读 3,247
摘要:本文深入剖析 Zephyr RTOS 中 Devicetree 从.dts文本到 C 代码中DT_*宏的完整生成链路。文章从整体流水线出发,依次讲解.dts/.dtsi/.overlay的合并与预处理、zephyr.dts与devicetree_generated.h的区别、EDT(Enhanced Devicetree)对象模型、Binding 的作用,以及DT_NODELABEL()、DT_PATH()、DT_ALIAS()、DT_CHOSEN()、DT_INST()、DT_REG_ADDR()、DT_IRQN()、DT_PROP()等核心宏的由来与用法。最终串联 Init Priority、Driver Dependency、Dependency Graph,揭示DEVICE_DT_DEFINE()如何将 Devicetree 节点转化为struct device,并给出 BSP 开发调试的实用检查清单。Zephyr Devicetree → C 代码生成全过程你前面已经把:13 Init Priority→14 Driver Dependency→15 Devicetree Dependency Graph串起来了。下一步非常关键:Devicetree 文件到底是怎么一步一步变成 C 代码里的 DT_NODELABEL()、DT_INST()、DT_PROP()、DEVICE_DT_DEFINE() 的?这篇建议重点解决一个问题:.dts 是文本,最终却能让 C 编译器看到各种 DT_* 宏。中间到底发生了什么?1. 先看完整流水线先建立一张总图:Devicetree Source │ │ ▼ board.dts / .dtsi │ │ │ C preprocessor │ ▼ merged devicetree │ │ ▼ dtc / edtlib │ ┌─────────────┴─────────────┐ │ │ ▼ ▼ zephyr.dts edt.pickle │ │ │ │ ▼ ▼ devicetree_generated.h Python EDT │ │ │ │ └─────────────┬─────────────┘ │ ▼ Zephyr build system │ ▼ C/C++ compiler │ ▼ Driversource│ ▼ DEVICE_DT_DEFINE(...)│ ▼ struct device │ ▼ final firmware这里最容易产生一个误解:Zephyr 不是简单地把 .dts 转换成一个 .h。实际上存在两条重要路线:DTS │ ┌────────┴────────┐ │ │ ▼ ▼ devicetree.h Python EDT │ │ │ └── binding / dependency / │ instance / property analysis │ ▼ C preprocessor │ ▼ DT_* macros │ ▼ Driver C code而EDT(Enhanced Devicetree)是理解现代 Zephyr Devicetree 的关键。2. 第一层:.dts 到底是什么?例如我们有:/{soc{uart0:serial@40004000{compatible="company,my-uart";reg=0x400040000x1000;interrupts=5;clock-frequency=48000000;status="okay";};};};这里描述的是:Node ├── name │ └── serial@40004000 │ ├── label │ └── uart0 │ ├── compatible │ └── company,my-uart │ ├── reg │ └── 0x40004000 0x1000 │ ├── interrupts │ └──5│ ├── clock-frequency │ └──48000000│ └── status └── okay注意:Devicetree 本身不是 C。它只是一个硬件描述语言。所以:DT_NODELABEL(uart0)并不是 .dts 里面天然存在的东西。它是 Zephyr 后续生成出来的C 宏接口。3. 第二层:.dtsi 和 .dts 先合并真实 Zephyr 项目中,通常不是一个 DTS 文件。例如:boards/ company/ my_board/ my_board.dts里面可能:#includelt;company/soc.dtsigt;uart0{status="okay";};而:soc.dtsi里面可能已经定义:uart0: serial@40004000{compatible="company,my-uart";reg=...;};所以最终并不是分别处理:soc.dtsi my_board.dts而是先形成:soc.dtsi │ │ board.dts │ ▼ C preprocessor │ ▼ merged DTSsource可以理解为:SoC 描述 + Board 描述 + Shield + overlay + chosen + aliases + 其他 .dtsi │ ▼ 最终 Devicetree4. Overlay 在什么时候进入?比如:boards/company/my_board/my_board.dts定义:uart0{status="okay";};你的应用又有:app.overlay里面:uart0{current-speed=lt;115200gt;;};最终得到:uart0 ├── compatible="company,my-uart"├── reg=...├── status="okay"└── current-speed=115200所以 overlay 并不是运行时配置。它是在build time修改 Devicetree。这一点非常重要:overlay │ ▼ buildtime│ ▼ Devicetree 被修改 │ ▼ C code 使用最终结果而不是:firmware running │ ▼ 读取 overlay完全不是这样。5. 第三层:C preprocessor 处理 DTS这是理解 Zephyr BSP 的一个关键点。Devicetree 的预处理阶段会处理:# include# define# if# ifdef之类的东西。因此:board.dts │ ├── include soc.dtsi ├── include bindings └── overlay │ ▼ preprocessed DTS之后才交给 Devicetree compiler / Zephyr Devicetree tooling。6. 第四层:生成 zephyr.dts构建过程中你通常可以在:build/zephyr/附近看到:zephyr.dts这个文件非常值得你直接打开看。它代表:最终合并之后的 Devicetree。例如:soc{uart0: serial@40004000{compatible="company,my-uart";reg=0x40004000 0x1000;interrupts=5;status="okay";};};此时:.dts .dtsi .overlay │ ▼ 最终 Devicetree │ ▼ build/zephyr/zephyr.dts7. zephyr.dts 和 devicetree_generated.h 不一样这是很多刚开始研究 Zephyr 的人最容易混淆的地方。你可以把它们理解成:zephyr.dts给人和 Devicetree 工具看的:Node ├── compatible ├── reg ├── interrupts ├── status └── propertiesdevicetree_generated.h给 C 编译器看的:# define DT_N_S_soc_S_serial_40004000 ...# define DT_N_S_soc_S_serial_40004000_REG_ADDR ...# define DT_N_S_soc_S_serial_40004000_IRQ ...也就是说:zephyr.dts │ │ Devicetree representation ▼ devicetree_generated.h │ │ C macro representation ▼ Csource8. 最关键的文件:devicetree_generated.h假设:uart0: serial@40004000{compatible="company,my-uart";reg=0x40004000 0x1000;interrupts=5;status="okay";};Zephyr 会生成大量宏。概念上类似:# define DT_N_S_soc_S_serial_40004000 ...然后:DT_NODELABEL(uart0)最终会通过宏展开找到对应 node identifier。9. 为什么 Devicetree Node 会变成奇怪的宏名字?这是 Zephyr Devicetree 最值得理解的机制之一。假设:soc{uart0: serial@40004000{...};};它有一个完整 path:/soc/serial@40004000Zephyr 需要把这个路径编码成 C identifier。于是概念上变成:/soc/serial@40004000 ↓ DT_N_S_soc_S_serial_40004000这里:/ → S @ → 处理成合法 identifier\- → 处理成合法 identifier具体编码规则比较复杂,但核心思想非常简单:把 Devicetree path 编码成合法的 C identifier。所以:Devicetreenode↓ canonicalnodeidentifier ↓ C preprocessor macro10. DT_NODELABEL(uart0) 是怎么来的?你写:# define UART_NODE DT_NODELABEL(uart0)看起来像:uart0直接变成 node。其实不是。可以把它理解成:DT_NODELABEL(uart0)│ ▼ DT_N_NODELABEL_uart0 │ ▼ DT_N_S_soc_S_serial_40004000也就是说:
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/29 10:53:21
AI眼镜PCBA代工怎么选?天地通 vs 通用方案深度对比
2026/9/29 10:53:21
仿muduo高并发网络库——Poller和EpollPoller
2026/9/29 10:48:20
机房类运维服务方案(Word)
2026/9/29 11:23:23
机器视觉打光实战:光源选型、角度控制与案例排坑
2026/9/29 11:23:23
Springboot+MySQL校园在线拍卖系统实战:从环境搭建到并发出价避坑
2026/9/29 11:23:23
Cloude Code 安装一图流:TaoToken 统一 Key 配置 settings.json 骨架
2026/9/29 11:23:23
第26篇_源码加更 08|真机测试、编译入口和 PLC 主程序
2026/9/29 11:23:23
第二课:SystemMessage系统提示词和UserMessage用户提示词
2026/9/29 11:18:23
【2019-07-08】How To Fix Disabled Edit Option In Shutter in Ubuntu 18.04
2026/9/29 0:02:32
开源模型端侧落地实战:量化、推理加速与Agent上下文管理
2026/9/29 0:02:32
AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成
2026/9/29 0:02:32
Java采购管理系统实战:从数据库设计到事务一致性
2026/9/28 2:37:38
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
2026/9/28 5:00:42
新手入门看这篇:建设网站加盟避坑指南与SEO实操
2026/9/28 8:17:28
论文AIGC疑似度是什么意思?想查论文AI率有哪些免费工具?