21、TSP设备树配置:compatible属性、reg属性、interrupts属性、pinctrl配置
各位同学,今天我们来聊聊触摸屏驱动里最基础、也最关键的一环——设备树配置。说实话,我刚入行那会儿,觉得设备树就是一堆枯燥的键值对,后来踩了几个坑才明白,这玩意儿写不对,驱动根本跑不起来。
设备树说白了就是硬件的“身份证”。内核通过它来识别你的触摸屏是什么型号、接在哪个总线上、用哪个中断引脚、GPIO怎么配。今天我就带大家把这几个核心属性掰开揉碎了讲清楚。
核心要点:设备树配置是驱动与硬件之间的桥梁。compatible负责“认人”,reg负责“找地址”,interrupts负责“传信号”,pinctrl负责“管引脚”。四者缺一不可。
一、compatible属性:驱动与硬件的“接头暗号”
compatible属性,我习惯叫它“身份证号”。内核在加载驱动时,会拿这个字符串去匹配驱动列表。匹配上了,驱动就绑定到设备上。
格式很简单:"厂商,型号"。比如:
compatible = "goodix,gt911";
这里要注意,厂商名一般用小写,型号用数字或字母组合。我在项目中遇到过有人把厂商名写成了大写,结果驱动死活加载不上,查了半天才发现是大小写不匹配。嗯,这种低级错误,犯过一次就记住了。
有时候一个芯片可能兼容多个型号,你可以写多个compatible字符串:
compatible = "goodix,gt911", "goodix,gt9xx";
内核会按顺序匹配,匹配到第一个就停。我个人习惯把最精确的型号写在最前面,这样更保险。
二、reg属性:告诉驱动“你在哪”
reg属性描述的是设备在总线上的地址。对于I2C接口的触摸屏,reg就是I2C从设备地址。
&i2c3 {
status = "okay";
touchscreen@5d {
compatible = "goodix,gt911";
reg = <0x5d>;
};
};
你想想看,I2C总线上可能挂着好几个设备,每个设备都有一个唯一的地址。reg就是告诉驱动:“嘿,我在这儿呢!”
这里有个坑——I2C地址有7位和10位之分。大部分触摸屏用7位地址,但有些芯片支持地址浮动。我记得有一次调试,明明芯片手册上写的是0x5d,但实际读出来是0xba。后来发现是芯片的ADDR引脚电平没拉对,导致地址偏移了一位。所以,reg的值一定要和硬件实际配置一致,不能光看手册。
注意:I2C地址通常是7位,但设备树里写的是8位值(左移1位后的结果)。比如7位地址0x5d,在设备树里写0x5d还是0xba?答案是0x5d。内核会自动处理移位,你直接写芯片手册上的地址就行。
三、interrupts属性:中断信号的“快递单号”
触摸屏是典型的“中断驱动”设备。用户手指一碰,芯片就拉高或拉低中断引脚,通知CPU来读数据。interrupts属性就是描述这个中断信号的。
格式一般是:interrupts = <中断控制器编号 中断号 触发方式>。
touchscreen@5d {
compatible = "goodix,gt911";
reg = <0x5d>;
interrupt-parent = <&gpio1>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
};
这里我解释一下:
- interrupt-parent:指定中断由哪个GPIO控制器管理。不写的话,默认用父节点的中断控制器。
- 第一个参数:中断号,对应GPIO的引脚编号。比如GPIO1_13,就是13。
- 第二个参数:触发方式。常用的有:
| 宏定义 | 触发方式 | 适用场景 |
|---|---|---|
| IRQ_TYPE_EDGE_RISING | 上升沿触发 | 芯片在事件发生时拉高引脚 |
| IRQ_TYPE_EDGE_FALLING | 下降沿触发 | 芯片在事件发生时拉低引脚(最常见) |
| IRQ_TYPE_LEVEL_HIGH | 高电平触发 | 持续高电平表示有事件 |
| IRQ_TYPE_LEVEL_LOW | 低电平触发 | 持续低电平表示有事件 |
我曾经在一个项目里把下降沿写成了上升沿,结果手指按下去没反应,抬起来反而触发中断。你想想看,用户点一下屏幕,数据还没读呢,中断就结束了。这种问题排查起来特别头疼,最后用示波器一看,才发现是触发极性反了。
小技巧:如果不确定中断触发方式,可以先配成双边沿触发(IRQ_TYPE_EDGE_BOTH),然后在中断处理函数里打印引脚电平,用示波器或逻辑分析仪确认实际波形。确认后再改回单边沿,减少不必要的唤醒次数。
四、pinctrl配置:GPIO引脚的“上岗培训”
pinctrl,全称pin control,就是管脚控制。触摸屏的I2C引脚、中断引脚、复位引脚,都需要通过pinctrl配置成正确的功能模式。
设备树里一般这样写:
&i2c3 {
status = "okay";
touchscreen@5d {
compatible = "goodix,gt911";
reg = <0x5d>;
interrupt-parent = <&gpio1>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
pinctrl-names = "default";
pinctrl-0 = <&ts_int_default &ts_reset_default>;
};
};
然后在pinctrl节点里定义具体的引脚配置:
&pinctrl {
ts_int_default: ts_int_default {
pins = "GPIO1_13";
function = "gpio";
bias-pull-up;
input-enable;
};
ts_reset_default: ts_reset_default {
pins = "GPIO1_12";
function = "gpio";
output-high;
};
};
这里我解释一下pinctrl的套路:
- pinctrl-names:定义状态名称,一般至少有一个"default"状态。驱动可以在不同阶段切换不同状态,比如休眠时切到"sleep"状态来省电。
- pinctrl-0:引用具体的引脚配置节点。可以引用多个,用空格隔开。
- 引脚配置节点:里面定义引脚号、功能模式、上下拉、驱动能力等。
我个人习惯把中断引脚配上拉输入,复位引脚配成推挽输出。为什么?因为中断引脚默认高电平,手指按下时拉低,配上拉可以保证空闲时电平稳定。复位引脚需要主动拉低再拉高来复位芯片,所以必须配成输出。
踩坑提醒:我曾经遇到过pinctrl配置了,但引脚功能死活不对。后来发现是pinctrl节点和i2c节点不在同一个.dtsi文件里,编译时pinctrl节点被覆盖了。所以,一定要确认pinctrl节点被正确编译进去了,可以在生成的.dtb文件里反编译检查。
五、完整示例:一个典型的TSP设备树节点
说了这么多,我们来拼一个完整的例子。假设我们用的是Goodix GT911,接在I2C3总线上,中断引脚是GPIO1_13,复位引脚是GPIO1_12:
&i2c3 {
status = "okay";
clock-frequency = <400000>; // I2C时钟400kHz
touchscreen@5d {
compatible = "goodix,gt911";
reg = <0x5d>;
interrupt-parent = <&gpio1>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&ts_int_default &ts_reset_default>;
pinctrl-1 = <&ts_int_sleep &ts_reset_sleep>;
reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
irq-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>;
touchscreen-size-x = <1024>;
touchscreen-size-y = <600>;
};
};
注意这里我加了reset-gpios和irq-gpios,这是驱动自己定义的属性,用来获取GPIO句柄。设备树里除了标准属性,驱动开发者也可以自定义属性,只要驱动代码里能解析就行。
六、知识体系梳理
为了让大家更直观地理解这几个属性的关系,我画了一张图:
从这张图可以看出来,设备树节点就像一棵树的树干,四个属性是主要的枝干,各自负责不同的功能。驱动加载时,先通过compatible找到对应的驱动,然后通过reg访问硬件,通过interrupts注册中断,通过pinctrl配置引脚。四者配合,触摸屏才能正常工作。
七、写在最后
设备树配置这东西,说难不难,说简单也不简单。我见过太多人把compatible写错、把中断号搞混、把pinctrl漏掉。其实只要记住一句话:设备树是写给内核看的硬件说明书,每个属性都要和实际硬件一一对应。
嗯,今天的内容就到这里。配置设备树的时候,多对照芯片手册和原理图,多检查几遍,能省去后面很多调试时间。
公众号:蓝海资料掘金营,微信deep3321