
🎬 渡水无言 :个人主页渡水无言
❄专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》
⭐️流水不争先,争的是滔滔不绝
📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生
| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生
在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录
[一、DTS 文件的整体结构](#一、DTS 文件的整体结构)
[二、.dtsi 头文件](#二、.dtsi 头文件)
[4.1. compatible 属性](#4.1. compatible 属性)
[4.2、model 属性](#4.2、model 属性)
[4.3、status 属性](#4.3、status 属性)
[4.4、#address-cells 和#size-cells 属性](#address-cells 和#size-cells 属性)
[4.5、reg 属性](#4.5、reg 属性)
[4.6、ranges 属性](#4.6、ranges 属性)
前言
上一期博客我们初步介绍了一下设备树的概念,这一期博客我们来介绍一下DTS语法。
一、DTS 文件的整体结构
设备树源文件(.dts)采用一种类 C 语言的语法格式,以树形结构组织硬件信息。下面我们逐步解析其核心语法元素。
一个完整的 DTS 文件遵循树形层级结构,核心分为三部分,具体结构如下图所示:

代码如下:
// 1. 头文件/include引用(可选)
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
// 2. /dts-v1/ 版本声明(必须)
/dts-v1/;
// 设备树根节点,所有硬件描述都在这个节点内
/ {
// --------------------------
// 子节点:aliases(别名节点)
// 作用:为其他节点提供简短别名,方便引用
// --------------------------
aliases {
// 定义 can0 为 flexcan1 节点的别名,后续可通过 &can0 引用该CAN控制器
can0 = &flexcan1;
};
// --------------------------
// 子节点:cpus(CPU集合节点)
// 作用:管理系统中所有CPU核心的描述
// --------------------------
cpus {
// 子节点reg属性中,地址部分占用1个32位整数
#address-cells = <1>;
// 子节点reg属性中,大小部分占用0个32位整数(CPU节点不需要描述地址范围)
#size-cells = <0>;
// --------------------------
// 孙节点:cpu@0(CPU0核心节点)
// 标签为 cpu0,方便后续引用
// --------------------------
cpu0: cpu@0 {
// 兼容属性,用于内核匹配对应的CPU驱动
compatible = "arm,cortex-a7";
// 设备类型,明确该节点代表CPU设备
device_type = "cpu";
// 寄存器地址,对应CPU的ID(这里为0号CPU)
reg = <0>;
};
};
// --------------------------
// 子节点:interrupt-controller@00a01000(中断控制器节点)
// 标签为 intc,方便后续引用
// 物理基地址为 0x00a01000
// --------------------------
intc: interrupt-controller@00a01000 {
// 兼容属性,用于内核匹配对应的GIC中断控制器驱动
compatible = "arm,cortex-a7-gic";
// 引用该中断控制器时,每个中断描述占用3个32位整数
#interrupt-cells = <3>;
// 标识该节点是一个中断控制器(布尔属性,存在即为真)
interrupt-controller;
// 寄存器地址范围,描述GIC的两个寄存器块:
// 第一个块:基地址 0x00a01000,长度 0x1000
// 第二个块:基地址 0x00a02000,长度 0x100
reg = <0x00a01000 0x1000>,
<0x00a02000 0x100>;
};
};
二、.dtsi****头文件
和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为 .dtsi 。在 imx6ull-alientek-
emmc.dts 中有如下所示内容:
include <dt-bindings/input/input.h>
include "imx6ull.dtsi"
可以看到:
使用"#include"来引用"input.h"这个.h 头文件。
使用"#include"来引用"imx6ull.dtsi"这个.dtsi 头文件。
在.dts 设备树文件中,可以通过 "#include"来引用.h、.dtsi 和.dts 文件。只是,我们在编写设备树头文件的时候最好选择.dtsi 后缀。
一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、IIC 等等。比如 imx6ull.dtsi 就是描述 I.MX6ULL 这颗 SOC 内部外设情况信息的。
三、设备节点
3.1、设备节点整体结构
设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键---值对。以下是从 imx6ull.dtsi 文件中缩减出来的设备树文件内容:
// 设备树根节点,所有硬件描述都在这个节点内
/ {
// --------------------------
// 子节点:aliases(别名节点)
// 作用:为其他节点提供简短别名,方便引用
// --------------------------
aliases {
// 定义 can0 为 flexcan1 节点的别名,后续可通过 &can0 引用该CAN控制器
can0 = &flexcan1;
};
// --------------------------
// 子节点:cpus(CPU集合节点)
// 作用:管理系统中所有CPU核心的描述
// --------------------------
cpus {
// 子节点reg属性中,地址部分占用1个32位整数
#address-cells = <1>;
// 子节点reg属性中,大小部分占用0个32位整数(CPU节点不需要描述地址范围)
#size-cells = <0>;
// --------------------------
// 孙节点:cpu@0(CPU0核心节点)
// 标签为 cpu0,方便后续引用
// --------------------------
cpu0: cpu@0 {
// 兼容属性,用于内核匹配对应的CPU驱动
compatible = "arm,cortex-a7";
// 设备类型,明确该节点代表CPU设备
device_type = "cpu";
// 寄存器地址,对应CPU的ID(这里为0号CPU)
reg = <0>;
};
};
// --------------------------
// 子节点:interrupt-controller@00a01000(中断控制器节点)
// 标签为 intc,方便后续引用
// 物理基地址为 0x00a01000
// --------------------------
intc: interrupt-controller@00a01000 {
// 兼容属性,用于内核匹配对应的GIC中断控制器驱动
compatible = "arm,cortex-a7-gic";
// 引用该中断控制器时,每个中断描述占用3个32位整数
#interrupt-cells = <3>;
// 标识该节点是一个中断控制器(布尔属性,存在即为真)
interrupt-controller;
// 寄存器地址范围,描述GIC的两个寄存器块:
// 第一个块:基地址 0x00a01000,长度 0x1000
// 第二个块:基地址 0x00a02000,长度 0x100
reg = <0x00a01000 0x1000>,
<0x00a02000 0x100>;
};
};
"/"是根节点,每个设备树文件只有一个根节点。
注意:imx6ull.dtsi 和 imx6ull-alientek-emmc.dts 这两个文件都有一个"/"根节点,这样不会出错吗?不会的,因为这两个"/"根节点的内容会合并成一个根节点。
节点层级说明
根节点:/ ------ 整个设备树的顶层容器。
一级子节点:
aliases:别名定义节点
cpus:CPU 集合节点
intc: interrupt-controller@00a01000:中断控制器节点
二级子节点(孙节点):
cpu0: cpu@0:CPU0 核心节点,是cpus的子节点。
3.2、节点命名规则
aliases 、 cpus 和 intc 是三个子节点,在设备树中节点命名格式如下:
node-name@unit-address
其中"node-name"是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能。
"unit-address"一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话。"unit-address"可以不要。
但是我们在3.1中的代码 我们看到的节点命名却如下所示:
cpu0:cpu@0
上述命令并不是" node-name@unit-address "这样的格式,而是用":"隔开成了两部分。
":" 前面的是节点标签(label)。
":"后面的才是节点名字。
格式如下所示:
label: node-name@unit-address
引入 label 的目的就是为了方便访问节点,可以直接通过 &label 来访问这个节点,比如通过
&cpu0 就可以访问" cpu@0 "这个节点,而不需要输入完整的节点名字。
3.3节点属性
每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式如下所示:
①、字符串
compatible = "arm,cortex-a7";
上述代码设置 compatible 属性的值为字符串"arm,cortex-a7"。
②、32 位无符号整数
reg = <0>;
上述代码设置 reg 属性的值为 0,reg 的值也可以设置为一组值,比如:
reg = <0 0x123456 100>;
③、字符串列表
属性值也可以为字符串列表,字符串和字符串之间采用","隔开,如下所示:
compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
上述代码设置属性 compatible 的值为"fsl,imx6ull-gpmi-nand"和"fsl, imx6ul-gpmi-nand"。
四、标准属性
节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性,Linux 下的很多外设驱动都会使用这些标准属性,本节我们就来学习一下几个常用的标准属性。
4.1. compatible 属性
compatible 属性是设备树中最核心、最重要的标准属性,没有之一!它也被称为 "兼容性属性",核心作用是将硬件设备节点与 Linux 内核中的驱动程序完成绑定匹配。
4.1.1、基本格式
compatible 属性的值是一个字符串列表,每个字符串都遵循统一的命名规范:
compatible = "manufacturer,model";
manufacturer:设备厂商标识(如 fsl 代表飞思卡尔、rockchip 代表瑞芯微);
model:设备型号 / 对应的驱动模块名称,内核会通过该字段匹配具体驱动。
例子:
compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
该属性包含两个兼容值,内核会按顺序匹配:
优先使用第一个值 fsl,imx6ul-evk-wm8960 在内核中查找匹配的驱动;
若未找到,则使用第二个值 fsl,imx-audio-wm8960 继续查找。
这种 "多值列表" 的设计,既可以适配专属驱动,也能兼容通用驱动,是设备树兼容性设计的核心思路。
一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设
备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个
驱动。
4.2**、model属性**
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比如:
model = "wm8960-audio";
4.3**、status属性**
status 属性看名字就知道是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息,可选的状态如表所示:
| 值 | 描述 |
|---|---|
| "okay" | 表明设备是可操作的。 |
| "disabled" | 表明设备当前是不可操作的,但在未来可以变为可操作的(如热插拔设备插入后)。具体含义还要看设备的绑定文档。 |
| "fail" | 表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。 |
| "fail-sss" | 含义和 "fail" 相同,后面的 sss 部分是检测到的错误内容。 |
4.4、#address-cells和#size-cells****属性
#address-cells 和 #size-cells 是设备树中用于描述子节点地址信息的关键属性,它们的值均为无符号 32 位整数。这两个属性可以作用于任何拥有子节点的设备节点,用于规范其子节点 reg 属性的格式。
核心作用
#address-cells:决定子节点 reg 属性中,起始地址(address) 部分所占用的 32 位字长。
#size-cells:决定子节点 reg 属性中,地址长度(length) 部分所占用的 32 位字长。
#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性
都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:
reg = <address1 length1 address2 length2 address3 length3......>
每个"address length"组合表示一个地址范围,其中 address 是起始地址,length 是地址长
度,#address-cells 表明 address 这个数据所占用的字长,#size-cells 表明 length 这个数据所占用
的字长,比如:
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>; // 子节点地址占1个32位字
#size-cells = <1>; // 子节点长度占1个32位字
dcp: dcp@02280000 {
compatible = "fsl,imx6sl-dcp";
reg = <0x02280000 0x4000>; // 地址: 0x02280000, 长度: 0x4000
};
};
子节点 dcp@02280000 的 reg = <0x02280000 0x4000> 严格遵循了这一规则,清晰地定义了该设备的寄存器基地址为 0x02280000,地址空间大小为 0x4000 字节。
4.5、reg****属性
reg 属性前面已经提到过了, reg 属性的值一般是 (address , length) 对。 reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。
4.6、ranges****属性
ranges 是设备树中用于描述子总线与父总线地址映射关系的标准属性,核心作用是实现地址空间的转换或映射。该属性的值有两种形式:
空值(ranges;):表示子总线地址空间与父总线地址空间完全一致,无需做地址转换;
数字矩阵:按 (child-bus-address, parent-bus-address, length) 格式编写,每一组数据对应一段地址映射关系。
ranges 属性的每一组映射关系包含三个核心参数,其字长由父节点 的 #address-cells 和 #size-cells 决定:
| 参数 | 含义 | 字长规则 |
|---|---|---|
| child-bus-address | 子总线地址空间的起始物理地址 | 由父节点 #address-cells 定义 |
| parent-bus-address | 父总线地址空间的起始物理地址(子地址要映射到的目标地址) | 由父节点 #address-cells 定义 |
| length | 本次映射的地址空间长度(子地址空间的有效范围) | 由父节点 #size-cells 定义 |
总结
本期博客主要介绍了DTS的语法情况