3. 设备树基础:设备树(DTS)语法入门
设备树(Device Tree)是一种描述硬件资源的数据结构,它使用树形节点来表述CPU、内存、外设、中断控制器、GPIO控制器等硬件信息。在嵌入式Linux系统中,设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),由Bootloader传递给内核,内核解析后自动创建平台设备,驱动通过匹配compatible属性与设备绑定。
compatible、reg、interrupts等核心概念,为后续编写GPIO按键设备树节点打下基础。
3.1 设备树文件结构
一个典型的DTS文件由根节点(/)和若干子节点组成,每个节点可以包含属性(键值对)和子节点。以下是一个简化的设备树结构示例:
/dts-v1/;
/ {
model = "Embedded Board v1.0";
compatible = "vendor,board-v1", "vendor,board";
chosen {
stdout-path = &uart0;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x0>;
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1GB RAM */
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@10000000 {
compatible = "ns16550a";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
clock-frequency = <24000000>;
};
gpio0: gpio@20000000 {
compatible = "vendor,gpio-controller";
reg = <0x20000000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupts = <0 40 4>;
};
};
};
/dts-v1/;声明版本;根节点用/表示;每个节点用节点名@地址格式命名;属性值支持字符串、32位整数数组、二进制数据等类型。
3.2 节点(Node)
节点是设备树的基本组成单元,每个节点代表一个硬件设备或总线。节点名称通常采用名称@单元地址的格式,其中单元地址用于区分同一父节点下相同名称的多个设备。
| 节点类型 | 命名示例 | 说明 |
|---|---|---|
| 根节点 | / |
设备树的顶层节点,包含所有硬件描述 |
| CPU节点 | cpu@0 |
描述一个CPU核心,@0表示CPU编号 |
| 内存节点 | memory@80000000 |
描述内存区域,@80000000表示起始地址 |
| 外设节点 | serial@10000000 |
描述UART外设,@10000000表示基地址 |
| GPIO控制器 | gpio@20000000 |
描述GPIO控制器,包含中断和GPIO相关属性 |
节点还可以通过标签(Label)进行引用,例如uart0: serial@10000000中的uart0就是标签,可以在其他位置通过&uart0引用该节点。
3.3 属性(Property)
属性是节点的键值对,用于描述设备的特性。常见的属性类型包括:
| 属性名 | 值类型 | 示例 | 说明 |
|---|---|---|---|
compatible |
字符串列表 | "vendor,device" |
驱动匹配的关键标识,格式为"厂商,设备型号" |
reg |
32位整数数组 | <0x10000000 0x1000> |
描述设备地址空间(地址+长度) |
interrupts |
32位整数数组 | <0 33 4> |
描述中断号、触发类型等信息 |
#address-cells |
32位整数 | <1> |
子节点reg属性中地址字段占用的单元数 |
#size-cells |
32位整数 | <1> |
子节点reg属性中长度字段占用的单元数 |
gpio-controller |
空属性 | gpio-controller; |
标记该节点是一个GPIO控制器 |
#gpio-cells |
32位整数 | <2> |
GPIO引用中需要的参数个数 |
3.4 compatible 属性:驱动匹配的关键
compatible属性是设备树中最核心的属性之一,它建立了设备节点与内核驱动之间的绑定关系。内核通过遍历设备树,将每个节点的compatible值与驱动注册的of_match_table进行匹配。
// 设备树节点
gpio-keys {
compatible = "gpio-keys";
...
};
// 内核驱动中的匹配表
static const struct of_device_id gpio_keys_of_match[] = {
{ .compatible = "gpio-keys", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
static struct platform_driver gpio_keys_driver = {
.driver = {
.name = "gpio-keys",
.of_match_table = gpio_keys_of_match,
},
.probe = gpio_keys_probe,
};
compatible属性中字符串的顺序进行匹配,优先匹配第一个。建议使用"厂商,设备型号"的命名规范,例如"ti,am335x-gpio",避免与其他厂商的设备冲突。
3.5 reg 属性:描述设备地址空间
reg属性用于描述设备在总线上的地址范围,其格式由父节点的#address-cells和#size-cells决定。每个reg条目包含地址和长度两部分,分别占用#address-cells和#size-cells个32位单元。
| #address-cells | #size-cells | reg 示例 | 含义 |
|---|---|---|---|
| 1 | 1 | <0x10000000 0x1000> |
基地址0x10000000,长度0x1000(4KB) |
| 2 | 1 | <0x0 0x80000000 0x40000000> |
64位地址0x80000000,长度0x40000000(1GB) |
| 1 | 0 | <0x0> |
仅地址,无长度(如CPU核心编号) |
// 示例:GPIO控制器reg属性
gpio0: gpio@20000000 {
compatible = "vendor,gpio-controller";
reg = <0x20000000 0x1000>; // 基地址0x20000000,映射大小4KB
#address-cells = <1>;
#size-cells = <1>;
ranges;
};
3.6 interrupts 属性:描述中断连接
interrupts属性描述设备的中断请求线,其格式由中断控制器节点的#interrupt-cells决定。常见的中断描述包含中断控制器编号、中断号、触发类型等字段。
| 中断控制器类型 | #interrupt-cells | interrupts 示例 | 含义 |
|---|---|---|---|
| GIC(通用中断控制器) | 3 | <0 33 4> |
SPI中断,中断号33,高电平触发 |
| 简单中断控制器 | 2 | <10 2> |
中断号10,下降沿触发 |
| GPIO中断 | 2 | <&gpio0 15 2> |
GPIO控制器gpio0的引脚15,下降沿触发 |
中断触发类型常用宏定义:
IRQ_TYPE_EDGE_RISING= 1:上升沿触发IRQ_TYPE_EDGE_FALLING= 2:下降沿触发IRQ_TYPE_EDGE_BOTH= 3:双边沿触发IRQ_TYPE_LEVEL_HIGH= 4:高电平触发IRQ_TYPE_LEVEL_LOW= 8:低电平触发
3.7 设备树包含与引用
实际项目中,设备树通常由多个文件组成,通过#include指令引入公共定义:
// board.dts
#include "soc.dtsi" // 包含SoC公共设备树
#include "pinmux.dtsi" // 包含引脚复用配置
#include "gpio-keys.dtsi" // 包含GPIO按键定义
/ {
model = "My Board";
compatible = "vendor,my-board";
// 通过 & 引用并覆盖 soc.dtsi 中定义的节点
&uart0 {
status = "okay";
pinctrl-0 = &uart0_pins;
pinctrl-names = "default";
};
&gpio0 {
status = "okay";
};
};
.dtsi文件是设备树包含文件,类似于C语言的头文件,通常存放SoC公共定义- 通过
&标签可以引用并扩展或覆盖其他文件中定义的节点 status属性用于控制设备是否启用,常用值为"okay"(启用)和"disabled"(禁用)pinctrl-*属性用于配置引脚复用功能,将在后续章节详细讲解
3.8 本章小结
设备树是嵌入式Linux驱动开发的基础,掌握DTS语法是编写GPIO按键驱动的第一步。本章重点内容:
- 节点结构:树形层级结构,根节点包含所有硬件描述,子节点代表具体设备
- compatible属性:驱动匹配的核心标识,遵循"厂商,设备型号"命名规范
- reg属性:描述设备地址空间,格式由父节点的
#address-cells和#size-cells决定 - interrupts属性:描述中断连接,包含中断域、中断号、触发类型等信息
- 文件组织:使用
.dtsi包含公共定义,通过标签引用实现节点扩展和覆盖