嵌入式Linux驱动开发指南 —— 设备树语法与编译工具 —— 读懂这张“藏宝图“(3)

发布时间:2026/6/14 16:14:20
嵌入式Linux驱动开发指南 —— 设备树语法与编译工具 —— 读懂这张“藏宝图“(3) 接前一篇文章嵌入式Linux驱动开发指南 —— 设备树语法与编译工具 —— 读懂这张“藏宝图“2标准属性那些你一定会用到的Linux内核定义了一些标准属性驱动程序会对它们有特定的预期处理。compatible驱动匹配的灵魂这是最重要的属性没有之一。格式为manufacturer,model它是驱动和硬件绑定的“红娘”compatible fsl,imx6ul-evk-wm8960, fsl,imx-audio-wm8960;内核会拿着这个字符串去驱动的of_match_table里匹配static const struct of_device_id imx_wm8960_dt_ids[] { { .compatible fsl,imx-audio-wm8960, }, { /* sentinel */ } };只要有一个能对上驱动就会被触发。status设备的生死开关okay设备可操作内核加载驱动。disabled设备禁用内核忽略它。fail/fail-sss设备有严重错误。移植时最常改的属性。很多外设在.dtsi里默认disabled我们在.dts里改成okay来激活。reg地址与长度描述设备寄存器地址范围格式由父节点的#address-cells和#size-cells决定uart1: serial02020000 { compatible fsl,imx6ul-uart, fsl,imx6q-uart, fsl,imx21-uart; reg 0x02020000 0x4000; };#address-cells和#size-cells地址的度量衡这两个属性出现在父节点里指导子节点如何写reg#address-cells地址字段占多少个 32 位整数。#size-cells长度字段占多少个 32 位整数。spi4 { #address-cells 1; #size-cells 0; // 没有长度字段 gpio_spi0 { reg 0; // 只有一个地址 }; };再看aips-bus02200000 { #address-cells 1; #size-cells 1; dcp02280000 { reg 0x02280000 0x4000; // 地址 长度 }; };ranges地址翻译ranges是一个翻译函数ranges child-bus-address parent-bus-address length。空ranges意味着子地址和父地址一一对应不需要翻译这在SoC内部总线中很常见soc { ranges; };如果不为空比如ranges 0x0 0xe0000000 0x00100000则子总线地址0x4600会被翻译为0xe0004600。大部分普通开发板移植不需要关心这个。语法糖让代码更简洁include机制#include dt-bindings/input/input.h #include imx6ull.dtsiDTC不仅能include.dtsi连.h文件也支持。约定上.h放宏定义把“魔术数字”变成可读常量.dtsi放节点定义。/delete-node/删除节点/delete-node/ sim2;把不需要的节点彻底删除而不是让它以disabled状态存在。节点追加与覆盖i2c1 { status okay; // 覆盖原来的 disabled clock-frequency 100000; // 新增属性 };如果原节点已有该属性则覆盖没有则新增。不用修改原始文件就能完全定制设备树行为。实战示例一个完整的设备树文件下面是一个用于Alpha开发板的LED驱动设备树文件/dts-v1/; #include imx6ull.dtsi #include imx6ull-aes.dtsi / { model Awesome Embedded Studio IMX6ULL Example Driver; compatible fsl,imx6ull-14x14-evk, fsl,imx6ull; imx_aes_led { #address-cells 1; #size-cells 1; compatible atkalpha-led; status okay; reg 0X020C406C 0X04 /* CCM_CCGR1_BASE */ 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */ 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */ 0X0209C000 0X04 /* GPIO1_DR_BASE */ 0X0209C004 0X04 ; /* GPIO1_GDIR_BASE */ }; };逐行解析/dts-v1/;声明设备树版本必须写。#include引入芯片级头文件和板级配置。根节点的compatible必须和内核里某个DT_MACHINE_START的.dt_compat匹配否则内核启动失败。imx_aes_led自定义节点没有address后缀因为它只是描述寄存器的“容器”。compatible atkalpha-led驱动匹配的关键字符串。reg列出了驱动需要的五个寄存器地址每个由物理地址和长度组成。踩坑预警根节点compatible拼写错误手滑多打一个字母比如fsl,imx6ullll内核启动后只显示Starting kernel ...然后就没了。内核启动不了第一件事检查根节点compatible拼写忘记写ranges定义总线节点时忘了ranges内核映射外设寄存器时可能拿到错误地址。SoC内部总线通常用空ranges表示直接映射。#address-cells和#size-cells 不匹配父节点定义了#address-cells 1和#size-cells 1子节点reg却只写了一个数字。DTC 可能不报错但内核解析时会出问题。直接修改.dtsi文件新手最容易犯的错误。.dtsi是公用的改了会影响所有引用它的项目。正确做法是在.dts里通过label引用修改。小结这一章我们先把 DTC 编译器的原理过了一遍 —— 词法分析、语法分析、二进制生成三阶段以及DTB的文件头、结构块、字符串块布局。然后重点讲解了设备树的语法节点命名、属性类型、标准属性、语法糖最后通过一个完整的 LED 驱动示例串联了所有知识点。掌握这些你就能看懂任何.dts文件也能写出规范的设备树代码了。