3. GPIO中断触发按键事件

任务目标

在瑞芯微RK3588平台上,将一个GPIO引脚配置为中断输入模式,连接按键。当按键按下时,GPIO电平变化触发中断,在中断处理函数中记录按键事件(打印日志或更新计数器)。本案例使用Linux内核GPIO子系统标准API实现,不依赖具体板级私有驱动。

环境与依赖

  • 内核版本:Linux 5.10+(RK3588 SDK默认内核)
  • 硬件连接:按键一端接GPIO(本例以GPIO0_A5为例,对应bank 0,pin 5),另一端接GND,GPIO内部使能上拉
  • 内核配置:需开启CONFIG_GPIOLIB(默认开启)

关键完整代码

以下为完整的按键中断驱动模块,采用platform driver模型,通过设备树获取GPIO编号并注册中断:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/of.h>

static struct gpio_desc *key_gpio;
static int irq_num;
static unsigned long press_count;

static irqreturn_t key_irq_handler(int irq, void *dev_id)
{
    press_count++;
    pr_info("Key pressed! Total: %lu\n", press_count);
    return IRQ_HANDLED;
}

static int key_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    int ret;

    key_gpio = devm_gpiod_get(dev, "key", GPIOD_IN);
    if (IS_ERR(key_gpio)) {
        dev_err(dev, "Failed to get key gpio\n");
        return PTR_ERR(key_gpio);
    }

    gpiod_direction_input(key_gpio);

    irq_num = gpiod_to_irq(key_gpio);
    if (irq_num < 0) {
        dev_err(dev, "Failed to get irq number\n");
        return irq_num;
    }

    ret = devm_request_irq(dev, irq_num, key_irq_handler,
                           IRQF_TRIGGER_FALLING | IRQF_SHARED,
                           "rk3588-key", &press_count);
    if (ret) {
        dev_err(dev, "Failed to request irq %d\n", irq_num);
        return ret;
    }

    dev_info(dev, "Key driver probed, irq=%d\n", irq_num);
    return 0;
}

static const struct of_device_id key_of_match[] = {
    { .compatible = "rk3588,gpio-key-irq" },
    { }
};
MODULE_DEVICE_TABLE(of, key_of_match);

static struct platform_driver key_driver = {
    .probe = key_probe,
    .driver = {
        .name = "rk3588_gpio_key",
        .of_match_table = key_of_match,
    },
};
module_platform_driver(key_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RK3588 GPIO interrupt key driver");

设备树配置

在板级设备树(如rk3588-evb.dtsi)中添加节点:

&pinctrl {
    key_int: key-int {
        rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>;
    };
};

key_demo {
    compatible = "rk3588,gpio-key-irq";
    key-gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
    pinctrl-names = "default";
    pinctrl-0 = <&key_int>;
    status = "okay";
};

编译与加载

  1. 将驱动源码放入内核drivers/input/misc/目录,并添加Makefile条目:obj-m += rk3588_key_irq.o
  2. 编译模块:make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- M=drivers/input/misc/
  3. 将生成的.ko文件拷贝到目标板,加载:insmod rk3588_key_irq.ko
  4. 确认设备树节点匹配后,probe成功会打印:Key driver probed, irq=xxx

验证方法

  • 加载模块后执行 cat /proc/interrupts | grep rk3588-key,应能看到对应中断号及触发次数统计
  • 按下按键,执行 dmesg | tail,每次按下应输出一条 Key pressed! Total: N 日志,N逐次递增
  • 判定通过条件:每次按键均产生中断日志,且计数连续递增无丢失;释放按键不产生中断(因配置为下降沿触发)

常见问题

  • 若probe失败提示gpio获取失败,检查设备树中key-gpios属性名与驱动中devm_gpiod_get第二个参数是否一致(此处为"key")
  • 若中断不触发,检查pinctrl配置中GPIO功能是否被复用为其他功能,以及按键接线是否与设备树pin一致
  • IRQF_SHARED标志用于兼容同GPIO上多个中断源,若确认独占可去掉