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 设备树与触摸屏驱动的数据流
下图展示了设备树从源文件到驱动使用的完整链路:
4.8 触摸屏设备树实战要点
关键实践:在编写触摸屏设备树节点时,务必确认以下三点:
- compatible属性必须与驱动中of_device_id表完全一致,包括大小写和厂商前缀
- 中断引脚的GPIO号与中断号对应关系,需查阅SoC数据手册确认GPIO bank编号
- 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子系统的联动打下坚实基础。