3. 设备树(DTS)基础:DTS语法、节点、属性、reg、interrupts、compatible

设备树(Device Tree)是嵌入式Linux系统中描述硬件拓扑结构的核心机制。对于蓝牙驱动开发而言,DTS是连接蓝牙芯片与内核驱动的“硬件描述语言”。本章将系统讲解DTS的语法结构、关键属性及其在蓝牙HID场景中的实际应用。

学习目标:掌握DTS基本语法与节点结构;理解compatible、reg、interrupts属性的含义与用法;能够为蓝牙HID设备编写正确的设备树节点。

3.1 设备树概述与作用

设备树是一种树形数据结构,用于向操作系统描述不可枚举的硬件设备信息。在嵌入式Linux中,DTS文件被编译为DTB(Device Tree Blob),由Bootloader传递给内核。

概念 说明 文件扩展名
DTS 设备树源文件,人类可读的文本格式 .dts / .dtsi
DTC 设备树编译器,将DTS编译为DTB 工具
DTB 设备树二进制文件,内核解析的最终格式 .dtb
DTBO 设备树覆盖层,用于动态添加/修改节点 .dtbo
蓝牙驱动视角:蓝牙控制器通常连接在UART、USB、SDIO或SPI总线上。DTS节点告诉内核:“这里有一个蓝牙芯片,它挂在UART3上,使用中断线GPIO_17,兼容Broadcom BCM43438协议。”
设备树在蓝牙驱动中的角色 设备树根节点 soc 节点 chosen 节点 memory 节点 uart3 节点 spi2 节点 i2c1 节点 蓝牙控制器 (BCM43438) 蓝牙HID设备 (鼠标/键盘) 关键属性 • compatible: 驱动匹配 • reg: 地址/片选 • interrupts: 中断映射

3.2 DTS语法基础

DTS采用类似C语言的语法风格,包含节点、属性和值。每个设备树文件以/dts-v1/;开头,表示使用设备树v1规范。

3.2.1 节点定义

节点是设备树的基本组成单元,每个节点代表一个设备或总线。节点名称格式为node-name@unit-address,其中unit-address是设备在总线上的地址。

/dts-v1/;

/ {
    model = "Embedded Bluetooth Board";
    compatible = "vendor,board-name";

    // 蓝牙UART控制器节点
    uart3: serial@1000b000 {
        compatible = "vendor,bluetooth-uart";
        reg = <0x1000b000 0x1000>;
        interrupts = <0 45 4>;
        clock-frequency = <48000000>;

        // 子节点:蓝牙芯片
        bluetooth {
            compatible = "brcm,bcm43438-bt";
            reg = <0>;
            interrupts-extended = <&gpio 17 IRQ_TYPE_EDGE_FALLING>;
            vbat-supply = <&vcc_3v3>;
        };
    };
};
常见错误:节点名称中的@unit-address必须与reg属性的第一个地址一致。例如节点serial@1000b000的reg首地址应为0x1000b000,否则DTC编译会报地址不匹配警告。

3.2.2 标签与引用

标签(label)是节点的别名,用于在树中其他位置引用该节点。标签以&符号引用。

语法 含义 示例
label: node-name 定义标签 uart3: serial@1000b000
&label 引用标签 &uart3 { ... }
<&label> 在属性值中引用 interrupts-extended = <&gpio 17 2>;
phandle = <num> 显式句柄(通常自动生成) phandle = <0x1>;

3.3 核心属性详解

3.3.1 compatible 属性

compatible是设备树中最重要的属性,用于将设备节点与内核驱动进行匹配。其值为字符串列表,按优先级从高到低排列。

// 蓝牙控制器compatible示例
compatible = "brcm,bcm43438-bt", "brcm,bcm4330-bt", "simple-bluetooth";

// 匹配规则:内核驱动按顺序尝试匹配
// 1. 优先匹配 "brcm,bcm43438-bt"
// 2. 其次匹配 "brcm,bcm4330-bt"
// 3. 最后匹配 "simple-bluetooth"
驱动匹配流程:内核的of_match_table中列出驱动支持的compatible列表。当设备树节点被扫描时,内核遍历驱动的匹配表,找到第一个匹配的compatible字符串即绑定成功。蓝牙驱动通常使用MODULE_DEVICE_TABLE(of, ...)导出匹配表。

3.3.2 reg 属性

reg描述设备在总线上的地址空间。格式为reg = <address1 length1 address2 length2 ...>,每个地址-长度对描述一个连续的地址区域。

总线类型 reg含义 示例
内存映射(MMIO) 基地址 + 寄存器范围长度 reg = <0x1000b000 0x1000>;
SPI 片选号 + 无长度(0) reg = <0>; // CS0
I2C 7位从机地址 reg = <0x29>;
USB USB端口号 reg = <1>; // 端口1
// SPI蓝牙控制器reg示例
spi_bt: bluetooth@0 {
    compatible = "infineon,cyw43439-bt";
    reg = <0>;              // SPI片选CS0
    spi-max-frequency = <48000000>;
    interrupt-parent = <&gpio>;
    interrupts = <19 IRQ_TYPE_LEVEL_LOW>;
};

3.3.3 interrupts 属性

interrupts描述设备的中断请求信息。其格式依赖于中断控制器类型,常见格式为interrupts = <中断号 触发类型>。

中断类型 宏定义 数值 说明
上升沿触发 IRQ_TYPE_EDGE_RISING 1 信号从低到高跳变时触发
下降沿触发 IRQ_TYPE_EDGE_FALLING 2 信号从高到低跳变时触发
高电平触发 IRQ_TYPE_LEVEL_HIGH 4 信号为高电平时触发
低电平触发 IRQ_TYPE_LEVEL_LOW 8 信号为低电平时触发
// 蓝牙HID中断配置示例
// 方案1:使用interrupt-parent引用
bluetooth {
    compatible = "brcm,bcm43438-bt";
    interrupt-parent = <&gpio>;
    interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
    // GPIO17下降沿触发:蓝牙芯片拉低表示有数据
};

// 方案2:使用interrupts-extended(推荐)
bluetooth {
    compatible = "brcm,bcm43438-bt";
    interrupts-extended = <&gpio 17 IRQ_TYPE_EDGE_FALLING>;
    // 直接指定中断控制器和参数
};
蓝牙HID中断处理流程 蓝牙控制器 BCM43438 GPIO控制器 中断控制器 GIC 通用中断控制器 GPIO17 下降沿 SPI 45 中断处理函数 bt_interrupt_handler HID协议解析 HID报告解码 用户空间HID事件 DTS中断配置要点 1. interrupt-parent指向GPIO 2. interrupts指定引脚+触发类型 3. 蓝牙HID常用下降沿触发 4. 中断号需与硬件连接一致 5. 电平触发需考虑中断锁

3.4 蓝牙HID设备树实战

以下是一个完整的蓝牙HID设备树配置示例,包含UART蓝牙控制器和中断映射。

// 完整蓝牙HID设备树配置
/dts-v1/;
#include "dt-bindings/interrupt-controller/irq.h"
#include "dt-bindings/gpio/gpio.h"

/ {
    model = "Bluetooth HID Development Board";
    compatible = "vendor,bt-hid-board";

    // 电源管理
    vcc_3v3: regulator-3v3 {
        compatible = "regulator-fixed";
        regulator-name = "3.3V";
        regulator-min-microvolt = <3300000>;
        regulator-max-microvolt = <3300000>;
    };

    // UART控制器
    uart0: serial@fe201000 {
        compatible = "arm,pl011", "arm,primecell";
        reg = <0xfe201000 0x1000>;
        interrupts = <0 33 4>;
        clock-frequency = <48000000>;
        status = "okay";

        // 蓝牙子节点
        bluetooth {
            compatible = "brcm,bcm43438-bt";
            reg = <0>;
            max-speed = <3000000>;
            shutdown-gpios = <&gpio 16 GPIO_ACTIVE_HIGH>;
            device-wakeup-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
            host-wakeup-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
            interrupts-extended = <&gpio 18 IRQ_TYPE_EDGE_FALLING>;
            vbat-supply = <&vcc_3v3>;
            clocks = <&clk_32k>;
            clock-names = "lpo";
        };
    };

    // 32KHz时钟
    clk_32k: clk-32k {
        compatible = "fixed-clock";
        clock-frequency = <32768>;
    };
};
蓝牙HID中断设计建议:
  • host-wakeup:蓝牙芯片通知主机有数据,建议使用下降沿触发,避免电平触发导致中断锁死
  • device-wakeup:主机通知蓝牙芯片唤醒,通常使用GPIO输出控制,不涉及中断
  • shutdown:蓝牙芯片复位/使能控制,使用GPIO输出,高电平有效
  • 中断优先级:蓝牙HID对实时性要求较高,建议配置为中等优先级(如Linux中的IRQ_DEFAULT_PRIORITY)

3.5 设备树编译与调试

编写完DTS文件后,需要使用DTC工具编译为DTB。以下是常用命令和调试技巧。

命令 说明 示例
dtc -I dts -O dtb DTS编译为DTB dtc -I dts -O dtb -o board.dtb board.dts
dtc -I dtb -O dts DTB反编译为DTS dtc -I dtb -O dts -o board.dts board.dtb
fdtdump 查看DTB内容 fdtdump board.dtb
dt-validate 验证DTS语法 dt-validate board.dts
// 编译与调试流程
# 1. 编译DTS为DTB
dtc -I dts -O dtb -o bcm2711-rpi-4-b.dtb bcm2711-rpi-4-b.dts

# 2. 检查编译错误
dtc -I dts -O dtb -o test.dtb test.dts 2>&1 | grep -i error

# 3. 反编译查看实际内容
dtc -I dtb -O dts -o decompiled.dts test.dtb

# 4. 在内核中查看设备树
# 挂载debugfs后
cat /sys/kernel/debug/device-tree/uart0/bluetooth/compatible

# 5. 查看中断映射
cat /proc/interrupts | grep bluetooth
调试陷阱:
  • DTC编译时-@选项用于生成符号表,支持设备树覆盖层,蓝牙模块动态加载时需要
  • 内核启动参数dtb=可指定使用外部DTB,方便调试
  • 使用of_*API在内核驱动中打印设备树信息:dev_info(dev, "compatible: %s\n", of_get_property(np, "compatible", NULL));
  • 中断号在GIC中可能被重新映射,需通过irq_of_parse_and_map()获取实际中断号

3.6 本章小结

设备树是嵌入式Linux蓝牙驱动开发的基石。本章重点内容包括:

  • DTS语法:节点定义、标签引用、属性赋值的基本规则
  • compatible属性:驱动匹配的核心机制,字符串列表按优先级匹配
  • reg属性:描述设备地址空间,不同总线有不同的编码方式
  • interrupts属性:中断映射配置,包括中断号、触发类型、中断控制器
  • 蓝牙HID实战:host-wakeup/device-wakeup/shutdown的GPIO与中断配置
  • 编译调试:DTC工具链使用、内核设备树查看方法