4. 设备树基础

设备树(Device Tree)是嵌入式Linux驱动开发中描述硬件资源的标准化机制。它替代了传统板级文件(board-*.c)中硬编码的硬件信息,将CPU、内存、外设、中断控制器等硬件拓扑以树形结构传递给内核。对于触摸屏驱动而言,设备树是驱动与input子系统之间硬件描述的关键桥梁。

4.1 设备树核心概念与文件格式

设备树使用三种文件格式协同工作:

文件类型 扩展名 说明
DTS(Device Tree Source) .dts 人类可读的文本源文件,描述单板硬件
DTSI(Device Tree Source Include) .dtsi 公共头文件,被.dts包含,用于SoC级描述
DTB(Device Tree Blob) .dtb 编译后的二进制文件,由Bootloader加载给内核
实践提示:在触摸屏驱动开发中,通常只需修改单板的.dts文件,添加或修改触摸控制器节点。SoC级的中断控制器、I2C控制器等已在.dtsi中定义。

4.2 DTS语法基础

DTS文件采用树形层次结构,每个硬件设备对应一个节点(node),节点内包含属性(property)描述设备特征。基本语法规则如下:

  • 节点定义: 节点名@地址 { ... };,地址通常为寄存器基址或设备地址
  • 属性赋值: 属性名 = <值>; 或 属性名 = "字符串";
  • 标签引用: &标签名 { ... }; 用于在别处扩展已有节点
  • 包含指令: #include "xxx.dtsi" 引入公共定义
// 一个典型的触摸屏设备节点示例
/dts-v1/;

#include "imx6ull.dtsi"

/ {
    model = "Embedded Touch Board";
    compatible = "vendor,touch-board";

    &i2c2 {
        clock-frequency = <100000>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_i2c2>;
        status = "okay";

        touchscreen@38 {
            compatible = "goodix,gt911";
            reg = <0x38>;
            interrupt-parent = <&gpio1>;
            interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
            irq-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
            reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
            touchscreen-size-x = <1024>;
            touchscreen-size-y = <600>;
        };
    };
};

4.3 节点与属性详解

设备树节点由节点名、单元地址和属性列表组成。属性是驱动获取硬件参数的核心途径。

4.3.1 标准属性

属性名 类型 用途
compatible 字符串列表 驱动匹配的关键标识,格式"厂商,设备名"
reg u32数组 描述设备地址空间(基址+长度)
interrupts u32数组 描述中断号、触发类型
interrupt-parent phandle 指向中断控制器节点
status 字符串 "okay"启用,"disabled"禁用
pinctrl-* phandle列表 引脚复用配置

4.3.2 自定义属性

驱动开发者可以定义私有属性,例如触摸屏的坐标范围、旋转角度、最大触点数等。这些属性通过内核OF(Open Firmware)API读取:

// 驱动中读取自定义属性示例
struct device_node *np = dev->of_node;
u32 max_x, max_y;

of_property_read_u32(np, "touchscreen-size-x", &max_x);
of_property_read_u32(np, "touchscreen-size-y", &max_y);

4.4 reg属性详解

reg属性描述设备在总线上的地址范围,其格式由#address-cells和#size-cells决定。对于I2C/SPI触摸屏,reg通常只包含设备地址:

总线类型 address-cells size-cells reg示例 含义
I2C 1 0 reg = <0x38>; I2C从设备地址0x38
SPI 1 0 reg = <0>; SPI片选号0
内存映射 1 1 reg = <0x2000000 0x1000>; 基址0x2000000,长度0x1000

4.5 interrupts属性详解

中断描述是触摸屏驱动的关键。interrupts属性通常包含中断号和触发类型两个单元,具体格式由中断控制器的#interrupt-cells决定。

4.5.1 中断触发类型

宏定义 值 触发方式
IRQ_TYPE_NONE 0 未指定
IRQ_TYPE_EDGE_RISING 1 上升沿触发
IRQ_TYPE_EDGE_FALLING 2 下降沿触发
IRQ_TYPE_LEVEL_HIGH 4 高电平触发
IRQ_TYPE_LEVEL_LOW 8 低电平触发
// 常见触摸屏中断配置
interrupt-parent = <&gpio1>;       // 中断控制器为GPIO1
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;  // GPIO1_IO5,下降沿触发

4.6 设备树编译与反编译

设备树源文件需要通过编译器生成二进制DTB,内核启动时解析DTB构建设备树。开发调试时也常需要反编译DTB查看实际内容。

4.6.1 编译流程

# 编译DTS为DTB
dtc -I dts -O dtb -o myboard.dtb myboard.dts

# 编译时输出警告信息
dtc -I dts -O dtb -o myboard.dtb myboard.dts -W all

# 编译并保留符号信息
dtc -I dts -O dtb -o myboard.dtb myboard.dts -@

4.6.2 反编译流程

# 反编译DTB为DTS
dtc -I dtb -O dts -o myboard.dts myboard.dtb

# 查看DTB中的节点树结构
fdtdump myboard.dtb

# 仅查看特定节点
dtc -I dtb -O dts myboard.dtb | grep -A 20 "touchscreen"
常见错误:编译时若出现"FATAL: Input tree has errors",通常是因为节点缺少必要属性(如compatible)、地址格式不匹配或引用了不存在的标签。使用dtc -W all可显示详细警告。

4.7 设备树与触摸屏驱动的数据流

下图展示了设备树从源文件到驱动使用的完整链路:

设备树数据流:从DTS到触摸屏驱动 DTS/DTSI源文件 文本描述硬件拓扑 DTC编译器 dtc -I dts -O dtb DTB二进制 Bootloader加载 内核OF核心(Open Firmware) 解析DTB,构建device_node树 触摸屏驱动匹配 of_match_table → compatible匹配 驱动读取属性 of_property_read_*()

4.8 触摸屏设备树实战要点

关键实践:在编写触摸屏设备树节点时,务必确认以下三点:
  1. compatible属性必须与驱动中of_device_id表完全一致,包括大小写和厂商前缀
  2. 中断引脚的GPIO号与中断号对应关系,需查阅SoC数据手册确认GPIO bank编号
  3. reg地址必须与触摸控制器硬件地址匹配,I2C设备通常为7位地址左移1位后的值

4.8.1 调试设备树的方法

# 查看内核中实际解析的设备树
ls /proc/device-tree/

# 查看触摸屏节点属性
cat /proc/device-tree/soc/i2c@021a0000/touchscreen@38/compatible
cat /proc/device-tree/soc/i2c@021a0000/touchscreen@38/reg

# 使用dtc反编译当前内核DTB
dtc -I fs -O dts /proc/device-tree/

4.8.2 常见错误排查

错误现象 可能原因 解决方案
驱动probe未调用 compatible不匹配 检查驱动of_device_id与节点compatible字符串
中断不触发 interrupts配置错误 确认中断号、触发类型、interrupt-parent
I2C通信失败 reg地址错误 核对硬件地址,注意7位/8位地址差异
编译报错 语法或引用错误 使用dtc -W all查看详细警告

掌握设备树基础是嵌入式Linux驱动开发的必备技能。对于触摸屏驱动而言,正确配置compatible、reg、interrupts三个核心属性,就能让内核正确识别硬件并调用驱动probe函数,为后续input子系统的联动打下坚实基础。