目录
- 设备树树形结构概述
- 节点(Node)全解:命名规范+标准节点+常用设备节点
- 属性(Property)全解:类型+核心属性+总线专用属性
- 标签与节点引用:设备树复用的核心
- 常见错误与注意事项
- 总结:驱动开发必背节点与属性
1. 设备树树形结构概述
设备树采用分层树形结构 描述硬件,和计算机的文件系统目录结构几乎完全一致。它以根节点为起点,向下逐层展开,真实反映了计算机系统中硬件的物理连接关系。
1.1 树形结构的三大核心元素
设备树的所有内容都由这三个基本元素组成:
根节点(/)
├── 子节点1(代表一个硬件设备)
│ ├── 属性1 = 值1(描述设备特性)
│ ├── 属性2 = 值2
│ └── 孙子节点1(代表子设备)
│ └── 属性3 = 值3
└── 子节点2
└── 属性4 = 值4
- 根节点 :所有节点的父节点,用
/表示,每个设备树有且只有一个 - 节点:每个硬件设备对应一个节点,节点可以包含子节点,形成分层结构
- 属性:每个节点包含多个键值对形式的属性,用于描述设备的具体硬件信息
1.2 最简完整设备树示例
dts
/dts-v1/; // 设备树版本号,必须是第一行
/ { // 根节点
compatible = "myvendor,myboard";
model = "My Custom Development Board";
// 片上系统节点,包含所有内部外设
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
// UART串口节点
uart@12340000 {
compatible = "myvendor,my-uart";
reg = <0x12340000 0x1000>;
interrupts = <5>;
status = "okay";
};
// GPIO控制器节点
gpio@12341000 {
compatible = "myvendor,my-gpio";
reg = <0x12341000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
status = "okay";
};
};
// 外接LED节点
leds {
compatible = "gpio-leds";
blue-led {
label = "blue:user";
gpios = <&gpio 13 GPIO_ACTIVE_HIGH>;
};
};
};
2. 节点(Node)全解:命名规范+标准节点+常用设备节点
节点是设备树的基本单元,每个硬件设备对应一个节点。
2.1 节点命名规范(必须严格遵守)
不符合规范的节点会导致内核解析失败,驱动无法匹配。
标准命名格式
节点名@单元地址
各部分含义
-
节点名 :描述设备的通用类型,如
cpu、memory、uart、i2c- 只能使用小写字母、数字和连字符
- - 不能使用下划线
_、空格或其他特殊字符 - 尽量使用内核通用的设备类型名,不要使用厂商自定义名称
- 只能使用小写字母、数字和连字符
-
@单元地址:设备的唯一地址标识
- 内存映射设备:寄存器基地址(十六进制)
- I2C/SPI设备:从机地址/片选号
- CPU:CPU编号
- 作用:区分同类型的不同设备
正确与错误命名对比
| 正确命名 | 错误命名 | 错误原因 |
|---|---|---|
uart@12340000 |
UART@12340000 |
使用了大写字母 |
i2c@12341000 |
i2c_controller@12341000 |
节点名过长,应使用通用类型名 |
led@1 |
led_1 |
使用了下划线,缺少@和单元地址 |
cpu@0 |
cpu0 |
缺少@和单元地址 |
2.2 标准节点(每个设备树都必须包含)
这些节点不对应具体的外设,而是提供系统级的全局信息。
1. 根节点 /
-
作用:整个设备树的起点,包含系统全局信息
-
必须属性 :
dts/ { compatible = "厂商,板级型号"; // 板级兼容属性,匹配内核板级支持包 model = "开发板全称"; // 人类可读的开发板名称 #address-cells = <1>; // 子节点reg属性中地址部分占几个32位整数 #size-cells = <1>; // 子节点reg属性中长度部分占几个32位整数 };
2. /aliases 别名节点
-
作用:给节点起简短别名,方便内核和驱动引用
-
示例 :
dtsaliases { serial0 = &uart4; // 给uart4节点起别名serial0 i2c1 = &i2c1; // 给i2c1节点起别名i2c1 led0 = &blue_led; // 给LED节点起别名led0 }; -
用途 :内核通过别名快速找到特定设备,如
console=ttySTM0对应serial0别名
3. /chosen 选择节点
-
作用:传递内核启动参数和运行时配置
-
最重要属性 :
bootargs(内核命令行参数) -
示例 :
dtschosen { bootargs = "console=ttySTM0,115200 root=/dev/mmcblk0p2 rootwait rw"; stdout-path = &uart4; // 指定标准输出设备 };
4. /memory 内存节点
-
作用:描述系统物理内存信息
-
必须属性 :
dtsmemory@c0000000 { device_type = "memory"; // 固定值,标识这是内存节点 reg = <0xc0000000 0x20000000>; // 基地址0xc0000000,长度0x20000000(512MB) };
5. /cpus CPU节点
-
作用:描述系统中的CPU核心信息
-
结构 :包含一个或多个
cpu@x子节点 -
示例 :
dtscpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0>; // CPU编号 clock-frequency = <800000000>; // CPU主频800MHz }; cpu@1 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <1>; clock-frequency = <800000000>; }; };
6. /soc 片上系统节点
-
作用:包含所有片上外设(UART、I2C、SPI、GPIO等)
-
必须属性 :
dtssoc { compatible = "simple-bus"; // 告诉内核这是一个简单总线 #address-cells = <1>; #size-cells = <1>; ranges; // 地址映射,空表示子节点地址与父节点地址相同 // 所有片上外设节点都放在这里 uart@40010000 { ... }; i2c@40012000 { ... }; gpio@50002000 { ... }; };
2.3 驱动开发最常用的通用设备节点
1. 总线类节点
(1)UART串口节点
dts
uart4: serial@40010000 {
compatible = "st,stm32mp157-uart";
reg = <0x40010000 0x400>; // 寄存器基地址和长度
interrupts = <52>; // 中断号
clocks = <&rcc UART4_CLK>; // 时钟
status = "okay"; // 启用设备
};
(2)I2C总线节点
dts
i2c1: i2c@40012000 {
compatible = "st,stm32mp157-i2c";
reg = <0x40012000 0x400>;
interrupts = <31>;
#address-cells = <1>; // 子节点reg占1个cell(I2C从地址)
#size-cells = <0>; // 子节点reg没有长度部分
clock-frequency = <100000>; // I2C总线频率100kHz
status = "okay";
// I2C从设备节点
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>; // I2C从机地址0x50
};
};
(3)SPI总线节点
dts
spi1: spi@40013000 {
compatible = "st,stm32mp157-spi";
reg = <0x40013000 0x400>;
interrupts = <32>;
#address-cells = <1>; // 子节点reg占1个cell(片选号)
#size-cells = <0>;
status = "okay";
// SPI从设备节点
flash@0 {
compatible = "winbond,w25q64";
reg = <0>; // 片选号0
spi-max-frequency = <10000000>; // 最大SPI频率10MHz
};
};
(4)GPIO控制器节点
dts
gpioa: gpio@50002000 {
compatible = "st,stm32mp157-gpio";
reg = <0x50002000 0x400>;
gpio-controller; // 标识这是一个GPIO控制器
#gpio-cells = <2>; // GPIO引用需要2个参数:引脚号+极性
status = "okay";
};
2. 外设类节点
(1)LED节点
dts
leds {
compatible = "gpio-leds";
blue_led: led-blue {
label = "blue:user"; // LED名称
gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>; // 使用GPIOA13,高电平点亮
linux,default-trigger = "heartbeat"; // 默认心跳模式
};
};
(2)按键节点
dts
keys {
compatible = "gpio-keys";
key-user {
label = "user-key";
gpios = <&gpioc 13 GPIO_ACTIVE_LOW>; // GPIOC13,低电平有效
linux,code = <KEY_ENTER>; // 按键码
};
};
3. 属性(Property)全解:类型+核心属性+总线专用属性
属性是节点的核心,用于描述设备的具体硬件特性,采用键值对形式。
3.1 属性的5种基本类型
| 类型 | 语法 | 示例 | 用途 |
|---|---|---|---|
| 字符串类型 | 双引号""包裹 |
compatible = "st,stm32-uart"; |
描述名称、兼容属性、状态 |
| 32位整数类型 | 尖括号<>包裹 |
reg = <0x12340000 0x1000>; |
描述地址、长度、中断号 |
| 字节数组类型 | 方括号[]包裹 |
mac-address = [00 11 22 33 44 55]; |
描述MAC地址、二进制数据 |
| 布尔类型 | 只有属性名,没有值 | gpio-controller; |
标识设备具有某种特性 |
| 字符串数组类型 | 多个字符串用逗号分隔 | compatible = "st,stm32-uart", "arm,pl011-uart"; |
多个兼容属性 |
3.2 核心属性(驱动开发每天都会用到)
这些属性是所有设备节点通用的,也是最重要的属性。
1. compatible(最重要,驱动匹配唯一依据)
-
作用:设备和驱动之间的"配对暗号"
-
格式 :
"厂商,设备型号" -
匹配规则 :内核从左到右依次匹配驱动的
of_device_id表,匹配到第一个就停止 -
示例 :
dtscompatible = "st,stm32mp157-uart", "arm,pl011-uart"; -
注意 :必须和驱动代码中的字符串完全一致,一个字符都不能错,否则驱动永远不会执行probe函数
2. reg(地址资源描述)
-
作用:描述设备的寄存器地址范围或总线地址
-
格式 :由父节点的
#address-cells和#size-cells决定- 父节点
#address-cells = <1>,#size-cells = <1>:格式为<基地址 长度> - 父节点
#address-cells = <2>,#size-cells = <2>:格式为<addr_high addr_low len_high len_low>
- 父节点
-
示例 :
dts// 内存映射设备 reg = <0x40010000 0x400>; // 基地址0x40010000,长度0x400 // I2C设备(父节点#size-cells=0) reg = <0x50>; // I2C从机地址0x50 // SPI设备(父节点#size-cells=0) reg = <0>; // SPI片选号0
3. status(设备状态控制)
-
作用:控制设备是否启用
-
有效值 :
值 含义 okay设备正常启用 disabled设备禁用,内核会忽略该节点 fail设备存在但工作异常 -
注意 :
- 父节点
status = "disabled"时,所有子节点都会被递归忽略 .dtsi文件中节点默认状态通常是disabled,在.dts文件中显式改为okay启用
- 父节点
4. interrupts(中断资源描述)
-
作用:描述设备使用的中断号和触发方式
-
格式 :
<中断号 触发方式> -
常用触发方式 :
IRQ_TYPE_EDGE_RISING:上升沿触发IRQ_TYPE_EDGE_FALLING:下降沿触发IRQ_TYPE_LEVEL_HIGH:高电平触发IRQ_TYPE_LEVEL_LOW:低电平触发
-
示例 :
dtsinterrupts = <52 IRQ_TYPE_LEVEL_HIGH>; // 中断号52,高电平触发
5. #address-cells 和 #size-cells
-
作用 :定义子节点
reg属性的格式 -
#address-cells:用来描述子节点reg属性的地址表中用来描述首地址的元素所用字(cell)的数量,而每个字(cell)是一个无符号32位整形。 -
#size-cells:用来描述子节点reg属性的地址表中用来描述地址范围大小的元素所用字(cell)的数量,每个字(cell)是一个无符号32位整形。 -
示例 :
dtssoc { #address-cells = <1>; // 子节点地址占1个cell #size-cells = <1>; // 子节点长度占1个cell // 子节点reg格式:<addr len> uart@40010000 { reg = <0x40010000 0x400>; }; };
6. ranges(地址映射)
-
作用:定义子总线地址到父总线地址的映射关系
-
格式 :
<子地址 父地址 长度> -
空值
ranges;:表示子节点地址与父节点地址完全相同 -
示例 :
dtssoc { ranges = <0 0x40000000 0x10000000>; // 子地址0映射到父地址0x40000000,长度0x10000000 };
3.3 常用总线专用属性
1. I2C总线专用
clock-frequency:I2C总线频率,单位Hzreg:I2C从机地址
2. SPI总线专用
spi-max-frequency:SPI设备最大工作频率,单位Hzreg:SPI片选号spi-cpol:时钟极性,1表示空闲时时钟为高电平spi-cpha:时钟相位,1表示在第二个时钟沿采样
3. GPIO专用
gpio-controller:标识这是一个GPIO控制器#gpio-cells:GPIO引用需要的参数个数,通常是2(引脚号+极性)gpios:引用GPIO引脚,格式为<&gpio_controller 引脚号 极性>GPIO_ACTIVE_HIGH:高电平有效GPIO_ACTIVE_LOW:低电平有效
4. 中断控制器专用
interrupt-controller:标识这是一个中断控制器#interrupt-cells:中断引用需要的参数个数,通常是2(中断号+触发方式)interrupt-parent:指定该设备的中断控制器
4. 标签与节点引用:设备树复用的核心
设备树通过标签 和引用 实现代码复用,这是.dtsi头文件机制的基础。
4.1 标签(Label)
-
在节点名前加
标签名:,给节点起一个唯一的标识 -
作用:方便在其他地方引用该节点
-
示例 :
dtsuart4: serial@40010000 { ... };
4.2 节点引用
-
使用
&标签名引用已经定义的节点 -
主要用途:在
.dts文件中修改.dtsi文件中定义的节点属性 -
示例 :
dts// 在stm32mp157.dtsi中定义了uart4节点,默认状态是disabled uart4: serial@40010000 { compatible = "st,stm32mp157-uart"; reg = <0x40010000 0x400>; interrupts = <52>; status = "disabled"; }; // 在myboard.dts中引用uart4节点,修改为启用状态 &uart4 { status = "okay"; };
5. 常见错误与注意事项
- 节点名不规范:使用大写字母、下划线或特殊字符,导致内核解析失败
- 缺少@和单元地址:同类型的多个设备无法区分,驱动匹配混乱
- 属性值类型错误:应该用尖括号的用了双引号,应该用双引号的用了尖括号
- compatible属性不匹配:设备树和驱动的compatible字符串不一致,导致probe不执行
- 忘记以空结尾 :
of_device_id表和数组类型的属性必须以空结尾 - 父节点禁用导致子节点失效 :父节点
status = "disabled"时,所有子节点都会被忽略
6. 总结:驱动开发必背节点与属性
必须掌握的节点
- 标准节点:
/、/aliases、/chosen、/memory、/cpus、/soc - 常用设备节点:
uart、i2c、spi、gpio、leds、keys
必须掌握的属性
- 核心属性:
compatible、reg、status、interrupts、#address-cells、#size-cells - 总线属性:
ranges、clock-frequency、spi-max-frequency - GPIO属性:
gpios、gpio-controller、#gpio-cells