3. 设备树基础:设备树(DTS)语法入门

设备树(Device Tree)是一种描述硬件资源的数据结构,它使用树形节点来表述CPU、内存、外设、中断控制器、GPIO控制器等硬件信息。在嵌入式Linux系统中,设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),由Bootloader传递给内核,内核解析后自动创建平台设备,驱动通过匹配compatible属性与设备绑定。

学习目标:掌握DTS文件的基本语法结构,理解节点、属性、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引用该节点。

设备树节点层级结构 根节点 / cpus memory@80000000 soc cpu@0 uart0: serial@10000000 gpio0: gpio@20000000 节点属性说明 每个节点包含描述硬件资源的属性:compatible、reg、interrupts、#address-cells、#size-cells 等 标签(如 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:低电平触发
设备树核心属性关系 设备树节点 compatible reg interrupts 驱动匹配标识 格式:"厂商,设备型号" 如 "gpio-keys" 地址空间描述 格式:<基地址 长度> 如 <0x20000000 0x1000> 中断连接描述 格式:<中断域 中断号 触发类型> 如 <0 33 4> 三者共同构成设备节点的核心描述:compatible 标识设备类型,reg 定位地址空间,interrupts 连接中断系统

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包含公共定义,通过标签引用实现节点扩展和覆盖