3. 硬件接口与协议
设备树,说白了就是一块「硬件描述画板」。你告诉内核:这颗触摸芯片接在哪个I2C总线上、用哪根中断脚、复位和供电怎么控制。内核拿到这些信息,才能正确驱动硬件。
我刚开始接触设备树时,总觉得它就是一堆杂乱的属性。后来踩了几个坑才明白——设备树写对了,驱动就成功了一半。尤其是触摸屏这种对时序和中断敏感的器件,一个GPIO号配错,可能整块屏都点不亮。
3.1 触摸屏节点的基本骨架
先看一个完整的触摸屏设备树节点。以我常用的Goodix GT9271为例,它挂在I2C-0总线上:
&i2c0 {
status = "okay";
clock-frequency = <400000>;
gt9271: touchscreen@5d {
compatible = "goodix,gt9271";
reg = <0x5d>;
interrupt-parent = <&gpio1>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
irq-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>;
touchscreen-size-x = <1024>;
touchscreen-size-y = <600>;
AVDD28-supply = <&vcc_3v3>;
pinctrl-names = "default";
pinctrl-0 = <&ctp_int_pins &ctp_rst_pins>;
};
};
这个节点里,每一行都有讲究。我挑几个关键点说说。
核心属性速查表
| 属性 | 含义 | 常见坑点 |
|---|---|---|
| compatible | 驱动匹配字符串 | 必须与驱动中of_match_table完全一致 |
| reg | I2C设备地址 | 7位地址左移1位?不,直接写原始地址 |
| interrupts | 中断号+触发类型 | GPIO号与中断号不要搞混 |
| irq-gpios | 中断GPIO描述 | 用于驱动内部请求GPIO,非必须但建议加 |
| reset-gpios | 复位GPIO | 注意有效电平,有些芯片低电平复位 |
3.2 中断配置——最容易翻车的地方
中断配置,我见过太多人在这里栽跟头。尤其是interrupt-parent和interrupts这两个属性的关系。
interrupt-parent指定中断控制器,通常是某个GPIO控制器或GIC。而interrupts里的第一个参数是中断号——注意,这个中断号是硬件中断号,不是GPIO号。
举个例子:GPIO1_13作为中断脚。在设备树里,interrupts = <13 IRQ_TYPE_EDGE_FALLING>中的13是GPIO控制器内部的偏移量,不是芯片引脚号。我曾经接手一个项目,同事直接把GPIO bank的基地址加上偏移写进去,结果中断根本触发不了。查了两天才发现,他写的是物理引脚号,而内核期望的是bank内的偏移。
⚠️ 中断触发类型的选择
触摸屏通常使用IRQ_TYPE_EDGE_FALLING(下降沿触发)或IRQ_TYPE_LEVEL_LOW(低电平触发)。我个人习惯用边沿触发,因为电平触发容易在中断处理慢时产生重复中断。但有些芯片的datasheet明确要求电平触发,那就得按手册来。
3.3 GPIO配置——复位和中断的「手脚」
除了中断,触摸屏还需要复位GPIO。复位时序很关键:上电后先拉低复位脚一段时间,再释放,芯片才能进入正常工作状态。
设备树里用reset-gpios描述复位引脚。注意属性名末尾的s,这是GPIO binding的规范写法。我曾经漏掉这个s,编译不报错,但驱动就是拿不到GPIO——这种小错误最折磨人。
另外,irq-gpios和interrupts可以同时存在。前者用于驱动内部调用gpiod_get获取GPIO描述符,后者用于注册中断。有些驱动只认interrupts,有些两者都要。我的建议是:两个都写上,兼容性最好。
3.4 pinctrl设置——管脚复用别搞错
pinctrl负责配置管脚的复用功能。比如I2C的SCL/SDA要设为I2C功能,中断脚要设为GPIO输入。
设备树里通过pinctrl-names和pinctrl-0引用pin controller中定义的节点:
&pinctrl {
ctp_int_pins: ctp_int_pins {
pins = "GPIO1_13";
function = "gpio_in";
bias-pull-up;
};
ctp_rst_pins: ctp_rst_pins {
pins = "GPIO1_12";
function = "gpio_out";
drive-strength = <2>;
};
};
这里要注意:pinctrl的配置必须和芯片的datasheet一致。有些SoC的GPIO1_13默认是PWM功能,如果不配成GPIO,中断脚永远收不到信号。
我记得有次调试,触摸屏中断一直不触发。用示波器量中断脚,波形正常。后来查pinctrl配置,发现中断脚被配成了SPI的片选——硬件工程师改版后忘了通知我。从那以后,我每次拿到新板子,第一件事就是核对pinctrl配置和原理图。
3.5 电源与 regulators
触摸屏通常需要1.8V或3.3V供电。设备树里用AVDD28-supply或vdd-supply指定电源:
AVDD28-supply = <&vcc_3v3>;
vdd-supply = <&vcc_1v8>;
这些supply在驱动初始化时会被regulator_get获取,然后使能。如果电源没配好,驱动probe时就会报-EPROBE_DEFER,反复重试直到超时。
💡 调试小技巧
如果触摸屏驱动probe失败,先检查dmesg里有没有EPROBE_DEFER。有的话,八成是电源或pinctrl依赖没准备好。用deferred_probe_timeout内核参数可以延长等待时间,方便调试。
3.6 设备树配置的完整流程
我把整个配置流程画了张图,方便你对照检查:
3.7 调试手段——设备树写没写对,一测便知
设备树编译成DTB后,内核启动时会解析。如果语法错误,启动日志里会有OF: fdt: Error之类的信息。但更多时候,语法没错,逻辑错了。
我常用的调试方法:
- 查看设备树是否被正确解析:
ls /proc/device-tree/,看看你的节点在不在。 - 检查GPIO是否被申请:
cat /sys/kernel/debug/gpio,看中断脚和复位脚的状态。 - 验证中断是否注册:
cat /proc/interrupts,看触摸屏的中断号有没有出现。 - 用of_graph工具:内核提供了
scripts/dtc/dtx_diff等工具,可以对比设备树差异。
🔑 核心原则
设备树配置的本质是「对号入座」——把硬件手册上的信息,用设备树语法准确表达出来。多核对原理图,多查datasheet,少凭经验猜测。我见过太多「我觉得应该是这样」导致的返工。