Linux设备树节点语法与编写要点

浏览量:13
时间: 2026-08-19 09:56:40

PCB热仿真3D温度分布云图

本文概述

设备树是嵌入式Linux开发的基础技能,也是面试中的高频考点。设备树源文件DTS通过节点和属性的树状结构描述硬件资源,编译后生成DTB二进制文件供内核解析。节点命名遵循"设备类型@单元地址"格式,属性支持字符串、32位整数、phandle引用等多种类型。编写设备树的核心要点包括正确设置compatible属性、合理使用reg描述地址资源、正确配置中断属性和引脚复用,以及利用status属性控制设备启用禁用。

核心要点速览

  • 设备树三要素:DTS源文件、DTC编译器、DTB二进制文件

  • 节点格式:节点名@地址 { 属性 = 值; 子节点; };

  • compatible属性决定驱动匹配,格式为"厂商,型号"

  • reg属性描述地址资源:<起始地址 长度>

  • 常用特殊节点:根节点、chosen、aliases、memory、soc

面试题:你写过设备树吗?基本语法有哪些?添加一个新设备节点要注意什么?

设备树入门手册

Device Tree Syntax & Writing Guide

一、面试官考察点

基础概念
是否理解设备树的作用和DTS/DTB/DTC三者关系
语法掌握
节点和属性能不能写对,常见属性能不能说清楚
驱动关联
是否理解设备树和驱动的匹配机制(compatible、match表)
调试能力
遇到设备树相关问题时怎么排查

二、回答思路

1. 设备树是什么

设备树是一种描述硬件资源的数据结构,全称是Device Tree。它的作用是把硬件信息从内核的C代码里抽出来,用一种独立的数据格式描述。这样同一个内核镜像可以用在不同的硬件平台上,只要换不同的设备树文件就行,不用每个板子都重新编译内核。

设备树有三个核心概念:DTS是设备树源文件,是人可以读写的文本格式;DTC是设备树编译器,把DTS编译成二进制;DTB是编译后的二进制文件,内核启动的时候bootloader把DTB的地址传给内核,内核解析DTB来识别硬件。

为什么要引入设备树?

在设备树出现之前,ARM Linux的每个开发板都要在内核里写一套板级描述代码,导致内核里板级文件越来越多,维护起来很乱。引入设备树后,硬件描述和内核代码分离,内核更通用,维护成本大大降低。

2. 基本语法

设备树的语法很像C语言的结构体,但有自己的规则。最外层是根节点,用斜杠表示,里面嵌套各种子节点,每个节点可以有属性和子节点。

/* 根节点 */ / { model = "My Custom Board"; compatible = "myvendor,myboard", "fsl,imx6q"; /* 内存节点 */ memory@10000000 { device_type = "memory"; reg = <0x10000000 0x20000000>; /* 起始地址 长度 */ }; /* 外设节点示例:UART */ uart@40010000 { compatible = "myvendor,my-uart", "fsl,imx6q-uart"; reg = <0x40010000 0x1000>; interrupts = <0 26 4>; status = "okay"; }; };

节点的命名规则一般是"设备类型@单元地址"。设备类型描述设备是什么,比如uart、i2c、gpio;@后面的单元地址一般是设备的基地址。如果设备没有地址,可以不写@和地址。同类型的多个设备用不同的地址来区分。

3. 属性类型

设备树的属性值有几种常见类型:

字符串
用双引号括起来,比如 compatible = "vendor,device"
32位整数
用尖括号括起来,叫cell,比如 reg = <0x1000 0x200>
字符串列表
多个字符串用逗号分隔,比如 compatible = "a", "b"
二进制数据
用方括号括起来的十六进制,比如 mac-address = [00 11 22 33 44 55]
phandle引用
引用其他节点,比如 gpios = <&gpio1 5 0>
空属性
只有属性名没有值,表示布尔型真,比如 gpio-controller

4. 常用重要属性

compatible
最重要的属性,用来和驱动匹配。值是字符串列表,按优先级从高到低排列。格式一般是"厂商,型号"。驱动的of_match_table里的字符串和这里的compatible对应上了,就会调用probe函数。
reg
描述设备的地址资源,格式是<起始地址 长度>。如果有多个地址范围,就有多组。这个属性要和父节点的#address-cells和#size-cells配合使用,这两个属性决定了地址和长度各占几个cell。
interrupts / interrupt-parent
描述设备的中断信息。interrupt-parent指定中断控制器,interrupts是中断号和触发方式。具体格式由中断控制器的#interrupt-cells决定,一般GIC是3个cell:中断类型、中断号、触发方式。
status
设备的状态,常见值有"okay"(启用)、"disabled"(禁用)、"fail"(失败)。通常在dtsi里定义了节点但状态是disabled,板级dts里根据实际硬件改设为okay。
pinctrl
引脚配置,指定设备使用的引脚复用功能和电气特性。格式是pinctrl-names和pinctrl-0,分别是状态名和对应的引脚配置节点引用。

5. 添加一个新设备节点的步骤

实际项目中添加一个新设备的设备树节点,我一般按这个流程来:

第一步:找对位置

先确定这个设备挂在哪个总线上,就加到对应的总线节点下面。比如I2C设备加到i2c节点下面,SPI设备加到spi节点下面,内存映射的外设加到soc节点下面。

第二步:基本属性

写compatible属性,确保和驱动的match表对应;写reg属性,根据设备的地址或片选号设置;如果有中断,配置interrupts和interrupt-parent。

第三步:引脚配置

如果设备有专用引脚,在iomux或者pinctrl节点里加引脚配置,然后在设备节点里用pinctrl-0引用。

注意事项

添加节点时要注意:第一,节点名按规范来,用设备类型@地址的格式;第二,reg的格式要看父节点的#address-cells和#size-cells,位数不对编译会报错;第三,时钟、电源域这些容易漏掉的依赖也要配置上,否则驱动probe可能失败;第四,状态要设对,不使用的设备设为disabled。

三、追问环节

追问1:dts和dtsi是什么关系?怎么包含?

dtsi是设备树的头文件,include的意思。一般芯片厂商会把芯片通用的硬件描述放在dtsi文件里,比如CPU、内存控制器、外设控制器这些所有开发板都一样的部分。然后每个具体的开发板有自己的dts文件,include对应的dtsi,再根据板子的具体情况增加或修改节点。

包含的方式是用/include/ "xxx.dtsi" 语法,跟C语言的include类似。但设备树的include还有一个特性:后定义的同名节点会和前面的合并,属性如果重名会覆盖前面的值。这个特性很重要,板级dts就是通过覆盖dtsi里的属性来定制的,比如把某个外设的status从disabled改成okay。

追问2:设备树编译错误怎么排查?

编译错误一般分两类。一类是语法错误,DTC编译器会直接报哪一行哪个字符有问题,比如少了分号、括号不匹配、属性值格式不对等,根据错误信息找到对应位置改就行。

另一类是逻辑错误,编译能过但运行不正常。常见的有几种:地址或中断号写错了,驱动probe的时候资源不对;compatible不匹配,驱动根本没被调用;pinctrl没配置,引脚功能不对;时钟没配置,外设拿不到时钟就工作不起来。排查的时候,先看内核启动日志里有没有相关的probe信息,有没有报错。如果驱动根本没加载,先检查compatible对不对;如果probe失败了,看失败的错误码是什么,根据错误码找原因。

追问3:设备树里的label和&引用怎么用?

label是给节点起一个别名,方便在其他地方引用。在节点名前面加个标签名加冒号,比如uart1: uart@40010000 { ... };,这样在其他地方就可以用&uart1来引用这个节点,往里面追加或修改属性。

这个语法在板级dts里非常常用。比如dtsi里定义了uart1节点,板级dts想启用它,不用重写整个节点,只要写&uart1 { status = "okay"; }; 就行,简洁很多。也可以用这个语法往已有节点里加新属性或覆盖旧属性。

追问4:aliases节点有什么用?

aliases节点是定义别名的地方,主要用来给设备编号。比如aliases里写serial0 = &uart1; serial1 = &uart2;,这样内核就知道哪个串口是ttyS0、哪个是ttyS1,设备号就固定了,不会因为驱动加载顺序变化而变。

aliases的另一个作用是给某些需要编号的设备排序,比如I2C总线、SPI总线、以太网接口等。没有aliases的话,设备编号可能按驱动probe顺序来,每次启动可能不一样。

四、失分表达

"设备树就是个配置文件,照着抄就行"

显得很不专业。设备树虽然不难,但也是有规范和机制的,要能讲清楚语法、属性格式、驱动匹配这些。

"compatible就是个名字,随便写没事"

compatible必须和驱动里的of_match_table严格对应,写错了驱动根本匹配不上,这是非常基础的错误。

五、准备动作

手写一个节点
面试前能默写一个完整的设备节点,包含compatible、reg、interrupts、status等
驱动匹配机制
搞清楚compatible怎么和驱动的of_match_table对应上的,probe怎么被调用
项目案例
准备一个自己加过设备树节点的案例,讲清楚加了什么、遇到什么问题、怎么解决的
调试方法
知道怎么看设备树相关的内核日志,怎么确认设备有没有被正确识别

面试官你好,关于Linux设备树的语法和编写要点,以上是我的理解和经验

要不要我具体举一个添加I2C设备节点的完整例子?


声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。仅供学习交流使用,不构成商业目的。版权归原作者所有,如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时删除。侵权投诉
相关推荐HOT
开班信息