当前位置: 首页 > news >正文

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

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与启动顺序

http://www.jsqmd.com/news/1283693/

相关文章:

  • 计算机毕业设计之Bbs网站管理平台
  • AI赋能价值投资:NLP与知识图谱在量化分析中的应用
  • 小白程序员必看:通用大模型 VS 行业AI,如何为企业创造真正价值?
  • Jellium Desktop启动基础:启动入门
  • 上海奉贤全街镇空压机维修|24 小时上门、无隐形消费、合规维 - 起跑123
  • 如何快速掌握GBFR-Logs:面向《碧蓝幻想:Relink》玩家的完整数据指南
  • 从“被动养生”到“主动健康”:树鹊磁电王用户真实分享 - 优企甄选
  • TSharding注解详解:ShardingOrderPara如何优雅实现参数路由
  • Bioinformatics Data Skills 配套资源大揭秘:如何高效利用gh_mirrors/bd/bds-files提升数据分析能力
  • 丙午年六月十五观云月
  • NSGAII算法在无人机3D路径规划中的应用与实践
  • iOS侧边栏交互设计最佳实践:基于Interactive Side Menu的实现案例
  • TPIC7710 EVM评估板实战指南:从硬件解析到GUI软件调试
  • 2026年激光打码机厂家:紫光/光纤/视觉/飞行/非标定制/全自动高精度品牌与选购参考 - 优企名品
  • 本地离线 AI 智能体搭建方案,Hermes 整合包 3 步落地,规避路径、端口各类报错
  • 深度学习入门指南:CNN、RNN、GAN等七大神经网络核心应用与实战路径
  • 2026 年 7 月新发布:阎良正规的6寸潜水抽沙泵订制厂家深度解析,为啥河道清淤的效率能翻3倍?全靠这台大家伙。 - 企业推荐官【认证官方】
  • RAG智能体技术解析与金融行业实践指南
  • Lawnchair核心功能解析:手势导航、主题定制与智能小部件全揭秘
  • 一句话搭出九人旅行网站!奥特曼:ChatGPT Work名字起小了
  • SCRCPY+ vs 原生SCRCPY:图形界面如何提升投屏效率?对比测评
  • 2026年Q3曝气器制造企业实力解析与选型参考 - 优企名品
  • xmr-btc-swap高级功能探索:并发交换与自动价格调节的使用技巧
  • eBPFSnitch UI使用教程:可视化管理Linux应用网络访问
  • MATLAB手写签名识别系统开发与优化实践
  • 2026年跨境魔方海外采购商挖掘工具选型指南:AI获客全场景适配分析
  • 5分钟掌握MouseClick:免费开源鼠标连点器让重复点击自动化
  • AU-60全功能AI语音模块:内置Codec替代分立音频链路的设计架构分析
  • 2026 年现阶段襄樊可靠的UV光氧催化废气净化器生产厂家找哪家,车间刺鼻异味3天消?这款净化设备凭什么让老厂长连说3个“靠谱”? - 行业推荐官【认证】
  • Unity游戏开发中实现SpringBoot风格IoC容器的核心原理与实践