第四节:GPIO控制器实战——从设备树绑定到按键驱动
好,咱们今天聊点实在的。GPIO控制器,说白了就是芯片上那些能输出高低电平、能检测外部信号的引脚。在设备树里怎么描述它?怎么让一个按键被内核正确识别?这些坑,我当年可没少踩。
GPIO控制器的设备树绑定
先看一个典型的GPIO控制器节点长什么样。以我常用的i.MX6ULL为例:
gpio1: gpio@0209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
这里有两个属性,是GPIO控制器的灵魂:
- gpio-controller:告诉内核“我是GPIO控制器”。没有这个,内核不认识你。
- #gpio-cells:描述每个GPIO引用需要几个参数。常见的是2个:引脚号和标志位。
核心要点:gpio-controller是声明,#gpio-cells是协议。缺一不可。
#gpio-cells的玄机
为什么是2个cell?我解释一下:
- 第一个cell:GPIO引脚编号。比如GPIO1_IO03,编号就是3。
- 第二个cell:标志位。控制高低电平有效、上拉下拉等。
标志位的定义,内核里有个标准:
| 位 | 含义 | 值 |
|---|---|---|
| bit 0 | 极性 | 0=高电平有效,1=低电平有效 |
| bit 1 | 上拉/下拉 | 0=无,1=上拉,2=下拉 |
| bit 2 | 驱动能力 | 0=默认,1=强驱动 |
举个例子:<&gpio1 3 GPIO_ACTIVE_LOW>,表示GPIO1的第3脚,低电平有效。这个GPIO_ACTIVE_LOW在内核里定义成0x1。
个人习惯:我一般把标志位写成宏定义,比如GPIO_ACTIVE_HIGH,这样代码可读性高。别直接写数字,三个月后你自己都看不懂。
实战:描述一个GPIO按键
好,现在我们要在设备树里加一个按键。假设按键接在GPIO1_IO03上,按下为低电平。
首先,在根节点下添加按键节点:
/ {
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
power-key {
label = "Power Key";
linux,code = <KEY_POWER>;
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
debounce-interval = <10>;
wakeup-source;
};
};
};
这里有几个关键点:
- compatible = "gpio-keys":内核里对应的驱动是
gpio-keys.c,它会解析这个节点。 - gpios:引用GPIO控制器,格式就是
<&控制器 引脚号 标志位>。 - linux,code:按键上报的键值。KEY_POWER是116,定义在
input-event-codes.h里。 - debounce-interval:消抖时间,单位毫秒。我一般设10ms,够用。
- wakeup-source:允许按键唤醒系统。这个属性不加,休眠后按了没反应。
我曾经踩过的坑:有一次忘记加pinctrl配置,结果GPIO引脚默认是浮空状态,按键怎么按都没反应。查了两天才发现是引脚复用没配好。所以,GPIO的pinctrl一定要配,不然内核不知道这个引脚要当GPIO用。
引脚复用配置(pinctrl)
在i.MX平台上,GPIO引脚需要先配置复用功能。一般在iomuxc节点里:
&iomuxc {
pinctrl_key: keygrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x17059
>;
};
};
这个0x17059是配置值,控制上下拉、驱动能力、速度等。每个芯片的配置值含义不同,得看数据手册。
我个人习惯把pinctrl和按键节点分开写,这样结构清晰。别图省事写在一起,后期维护会想骂人。
知识体系图
下面这张图,是我自己总结的GPIO设备树配置流程:
调试技巧
配好了设备树,怎么验证?我一般这么做:
- 查看设备树是否被正确解析:
ls /proc/device-tree/gpio-keys/,能看到节点说明解析成功。 - 检查GPIO是否被申请:
cat /sys/kernel/debug/gpio,看对应引脚状态。 - 测试按键事件:
evtest /dev/input/eventX,按一下看有没有事件上报。
避坑指南:如果按键没反应,先查dmesg。我曾经遇到过gpio-keys驱动没编译进内核的情况,dmesg里直接报No driver found。所以,先确认内核配置里有没有选上CONFIG_KEYBOARD_GPIO。
嗯,GPIO控制器的设备树绑定,说白了就是三件事:声明控制器、配置引脚复用、描述外设。把这三步走通,按键就能跑起来。下次遇到LED、蜂鸣器这些,套路是一样的,换汤不换药。