尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Zephyr学习 第四章 - 1:从 DEVICE_DT_DEFINE 到 struct device

Zephyr学习 第四章 - 1:从 DEVICE_DT_DEFINE 到 struct device
📅 发布时间:2026/7/28 23:54:12

04-1:从 DEVICE_DT_DEFINE 到 struct device

验证方式:Zephyr 源码、ELF/map、native_sim/native/64
验证结论:源码确认 + 编译确认 + 模拟确认

1. 本节目标

本节建立 Zephyr Device Model 的第一条完整链路:

Devicetree node -> DT_INST_FOREACH_STATUS_OKAY -> DEVICE_DT_INST_DEFINE -> struct device + device_state + init entry -> kernel 在 main 前调用 init -> DEVICE_DT_GET 取得 device pointer -> device_is_ready 检查初始化状态

本节重点理解设备对象的组成和生命周期,不深入初始化优先级、依赖排序和 API table 调度。

第四章计划分为五节:

04-1 DEVICE_DT_DEFINE 与 struct device 04-2 初始化 level、priority 与启动顺序 04-3 初始化失败、依赖与 device_is_ready 04-4 driver API table 与子系统调用 04-5 多实例驱动、常见问题与第四章总结

2. Device Model 解决什么问题

不同驱动的硬件和业务不同,但 Zephyr 希望用统一对象表达:

这个设备叫什么 它的只读硬件配置在哪里 它的可变运行状态在哪里 它提供什么 API 初始化是否已经执行并成功 它依赖哪些其他设备

这就是struct device的职责。

应用通常拿到:

conststructdevice*dev;

然后:

  • 检查设备是否 ready;
  • 通过子系统 API 使用设备;
  • 不直接了解驱动私有 data/config 的结构。

3. 本地 Zephyr 的 struct device

源码位置:

zephyr/include/zephyr/device.h:453

核心字段可简化为:

structdevice{constchar*name;constvoid*config;constvoid*api;structdevice_state*state;void*data;/* 根据 Kconfig 还可能有 deps、PM、DT metadata 等字段 */};

需要先记住五个核心指针:

字段含义常见存储性质
name设备实例名称只读字符串
config设备实例的固定配置static const,通常 ROM/rodata
api驱动对外函数表static const,通常 ROM/rodata
stateZephyr 维护的公共初始化状态可变状态
data驱动实例私有运行数据可变 RAM/BSS

源码确认

4. config 与 data 必须分开

实验驱动定义:

structlearning_device_config{intinitial_value;};structlearning_device_data{intcurrent_value;bool init_was_called;};

区别:

config 构建时由 Devicetree 决定 初始化后通常不改变 多数驱动声明为 static const data 运行过程中会改变 保存状态、buffer、callback、锁、计数等 不能声明为 const

本节的具体值:

config.initial_value = 42 data.current_value = 0(BSS 初值)

init 执行后:

data.current_value = 42 data.init_was_called = true

5. Devicetree 实例

overlay:

/ { learning_device0: learning-device-0 { compatible = "zephyr,learning-device"; status = "okay"; initial-value = <42>; }; };

binding:

compatible:"zephyr,learning-device"include:base.yamlproperties:initial-value:type:intrequired:true

生成结果:

#defineDT_N_INST_0_zephyr_learning_deviceDT_N_S_learning_device_0#defineDT_N_S_learning_device_0_P_initial_value42

编译确认

6. 每实例 config 和 data

驱动使用:

#defineDT_DRV_COMPATzephyr_learning_device

每实例展开宏:

#defineLEARNING_DEVICE_DEFINE(inst)\staticstructlearning_device_data\learning_device_data_##inst;\staticconststructlearning_device_config\learning_device_config_##inst={\.initial_value=\DT_INST_PROP(inst,initial_value),\};\DEVICE_DT_INST_DEFINE(inst,learning_device_init,NULL,\&learning_device_data_##inst,\&learning_device_config_##inst,\POST_KERNEL,50,NULL);DT_INST_FOREACH_STATUS_OKAY(LEARNING_DEVICE_DEFINE)

因为只有一个 status-okay 实例,简化展开后会产生:

learning_device_data_0 learning_device_config_0 一个 struct device 一个 device_state 一个 init entry

7. DEVICE_DT_INST_DEFINE 做了什么

本地定义:

zephyr/include/zephyr/device.h:246
#defineDEVICE_DT_INST_DEFINE(inst,...)\DEVICE_DT_DEFINE(DT_DRV_INST(inst),__VA_ARGS__)

它只是先把inst转成 node identifier,再调用:

DEVICE_DT_DEFINE(node_id,...)

所以:

DEVICE_DT_DEFINE 接收明确 node identifier DEVICE_DT_INST_DEFINE 接收当前 DT_DRV_COMPAT 的 instance number

驱动多实例模式通常使用后者。

8. DEVICE_DT_DEFINE 做了什么

本地定义:

zephyr/include/zephyr/device.h:229

核心工作可以概括为:

1. 为 node 创建 device_state 2. 创建全局 struct device 3. 把 name/config/api/state/data 写入 device object 4. 注册 init function、level 和 priority 5. 把对象放进 Zephyr 特定 iterable/linker section

它不是普通的局部变量声明,也不是运行时malloc()。

设备对象及初始化记录在链接阶段就已经存在于镜像中。

9. 本节 DEVICE_DT_INST_DEFINE 参数

DEVICE_DT_INST_DEFINE(inst,learning_device_init,NULL,&learning_device_data_##inst,&learning_device_config_##inst,POST_KERNEL,50,NULL);

参数含义:

参数本实验值含义
inst0当前 compatible instance
init_fnlearning_device_init启动初始化函数
pmNULL暂不使用设备电源管理
data&learning_device_data_0私有可变数据
config&learning_device_config_0私有只读配置
levelPOST_KERNELkernel 可用后初始化
prio50同 level 内的初始化优先级
apiNULL本节暂不建立 API table

level 和 priority 将在 04-2 单独验证。

10. init function 的输入

staticintlearning_device_init(conststructdevice*dev){conststructlearning_device_config*config=dev->config;structlearning_device_data*data=dev->data;data->current_value=config->initial_value;data->init_was_called=true;return0;}

初始化函数已经拿到了完整struct device,因此能从中取得对应实例的 config 和 data。

返回值:

0 初始化成功 非 0 初始化失败,device 不应被认为 ready

失败路径将在 04-3 用故障注入验证。

11. init 为什么在 main 之前运行

DEVICE_DT_DEFINE()不只是生成 device object,还创建 init entry。

Zephyr 启动代码按初始化 level 和 priority 遍历这些 entry,在进入应用main()前调用相应 init function。

本节选择:

POST_KERNEL, priority 50

实际输出顺序:

[init] device=learning-device-0 initial=42 current_before=0 *** Booting Zephyr OS build v4.1.0-rc1 *** [main] device=learning-device-0 ready=1 init_called=1 initial=42 current=42

结论:

learning_device_init() 先执行 main() 后执行,并观察到 init 已修改 data

模拟确认

12. DEVICE_DT_GET 不是运行时查找

main 中:

#defineLEARNING_DEVICE_NODEDT_NODELABEL(learning_device0)staticconststructdevice*constlearning_dev=DEVICE_DT_GET(LEARNING_DEVICE_NODE);

本地定义:

#defineDEVICE_DT_GET(node_id)(&DEVICE_DT_NAME_GET(node_id))

这表示:

DEVICE_DT_GET 在编译/链接期引用 node 对应的全局 device object 不是按字符串遍历设备表 不会自动检查 init 是否成功

如果 node 存在,但没有驱动为它执行DEVICE_DT_DEFINE(),通常会在链接时报错:

undefined reference to __device_dts_ord_<N>

这是很重要的排查信号。

13. node identifier 与 device pointer

不要混淆:

DT_NODELABEL(learning_device0)

这是预处理阶段使用的 node identifier。

DEVICE_DT_GET(DT_NODELABEL(learning_device0))

这是指向运行时struct device对象的 C pointer。

转换关系:

node identifier -> DEVICE_DT_GET -> const struct device *

node identifier 自己不是指针,也不能传给运行时 device API。

14. device_is_ready 的真实判断

本地实现:

zephyr/kernel/device.c:132
boolz_impl_device_is_ready(conststructdevice*dev){if(dev==NULL){returnfalse;}returndev->state->initialized&&(dev->state->init_res==0U);}

所以 ready 至少要求:

init function 已经被调用 && init result 表示成功

device_is_ready()不是:

  • Devicetree status 的别名;
  • 真实总线通信测试;
  • 传感器数据有效性测试;
  • 永久保证设备以后不会出错。

本节结果:

ready=1

因为 init 已执行并返回 0。模拟确认

15. device name 从哪里来

DEVICE_DT_DEFINE()使用:

DEVICE_DT_NAME(node_id)

本地规则:

如果 node 有 label property -> 使用 label property 字符串 否则 -> 使用 node full name

实验节点没有labelproperty,因此:

dev->name = "learning-device-0"

不要把这里的dev->name再与 node labellearning_device0混淆。

16. ELF 与 map 证据

检查:

nm-n/mnt/c/study/1-zephyr/work/ch04_device_object/zephyr/zephyr.elf\|rg'learning_device|__device_dts_ord|__init_'

实际关键 symbol:

learning_device_config_0 learning_device_data_0 learning_device_init __device_dts_ord_12 __init___device_dts_ord_12

生成头文件说明:

#defineDT_N_S_learning_device_0_ORD12

因此 ordinal 12 对应/learning-device-0。

map 中还能看到:

.rodata.learning_device_config_0 .bss.learning_device_data_0 __device_dts_ord_12

这与设计相符:

config -> rodata data -> BSS/RAM device + init entry -> 静态链接对象

编译确认

17. 本节最小构建

cd~/project/exportZEPHYR_SDK_INSTALL_DIR=/home/yff/zephyr-sdk/zephyr-sdk-0.17.1sourcezephyr/zephyr-env.sh west build\-bnative_sim/native/64\/mnt/c/study/1-zephyr/labs/ch04_device_model\--build-dir /mnt/c/study/1-zephyr/work/ch04_device_object\-palways /mnt/c/study/1-zephyr/work/ch04_device_object/zephyr/zephyr.exe

实验不需要 GPIO、SPI、interrupt 或真实传感器,验证的是 Zephyr Device Model 本身。

19. 常见错误

19.1 config 没有 const

固定硬件配置通常应放在只读区。误放入可变 data 会增加 RAM 使用并模糊职责。

19.2 在 config 中保存运行状态

config 可能位于只读存储,不能用于计数、callback 状态或实时数据。

19.3 DEVICE_DT_GET 后不检查 ready

取得 pointer 不表示 init 成功。

conststructdevice*dev=DEVICE_DT_GET(node_id);if(!device_is_ready(dev)){/* handle error */}

19.4 把 status okay 当 ready

status okay 只让实例参与构建;ready 是 init 后的运行时状态。

19.5 看到 __device_dts_ord_N undefined 就怀疑编译器

通常应检查:

  1. 对应 driver Kconfig 是否为 y;
  2. driver.c是否进入编译;
  3. node compatible/status 是否正确;
  4. driver 是否执行DEVICE_DT_DEFINE/INST_DEFINE。

19.6 在应用中直接访问 dev->data

本实验为了教学展示内部结构。正常应用应通过驱动/子系统 API 使用设备,避免依赖私有 data/config 类型。

20. 可迁移到其他驱动的结论

  1. struct device是配置、数据、API 和公共状态的统一连接点;
  2. config 通常static const,data 通常位于可变 RAM;
  3. DEVICE_DT_INST_DEFINE()为 Devicetree instance 创建静态 device object 和 init entry;
  4. DEVICE_DT_GET()取得全局对象地址,不执行字符串查找,也不检查 ready;
  5. device_is_ready()检查 init 已执行且返回成功;
  6. status okay、device object 存在和 runtime ready 是不同阶段。

下一节:04-2-初始化level、priority与启动顺序。

相关新闻

  • 如何快速掌握GBFR-Logs:面向《碧蓝幻想:Relink》玩家的完整数据指南
  • NSGAII算法在无人机3D路径规划中的应用与实践
  • Bioinformatics Data Skills 配套资源大揭秘:如何高效利用gh_mirrors/bd/bds-files提升数据分析能力

最新新闻

  • Android内核级Root隐藏终极指南:SUSFS4KSU-Module完全解析与实战配置
  • 主机平台的 CPU 特性利用:从 SIMD 到 Job 系统的平台差异
  • 如何在Windows电脑上直接安装APK文件:终极指南
  • KEYSIGHT是德科技 E8362C PNA系列20 GHz高性能微波矢量网络分析仪
  • 基于ESP32-C6的Matter智能灯泡开发全流程详解
  • 如何用Python工具3分钟找回QQ空间全部历史说说

日新闻

  • 金融舆情监测系统:多语言情感分析与实时可视化技术解析
  • QT C++调用Python异常处理:PyBind11实战与跨语言编程指南
  • A-47双麦回音消除模块:主次麦空间分布与差分连接对ENC性能的影响

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号