【Linux驱动】九、使用中断实现按键读取
【Linux驱动】九、使用中断实现按键读取
本文最后更新于329 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com
Avatar
对于 GPIO 按键,我们并不需要去写驱动程序,使用内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以,如果自己要从头写,然后你需要做的只是修改设备树指定引脚及键值。然后编写驱动。😊

原理图


修改设备树

对于一个引脚要用作中断时:

  1. 要通过 PinCtrl 把它设置为 GPIO 功能;
  2. 表明自身:是哪一个 GPIO 模块里的哪一个引脚

运行 NXP 提供的图形化设备树配置工具“i.MX Pins Tool v6”,点击左侧选中 GPIO5_IO01、GPIO4_IO14,如下图所示:

&iomuxc {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_hog_1>;
    imx6ull-board {
        key2_100ask: key2_100ask{                /*!< Function assigned for the core: Cortex-A7[ca7] */
            fsl,pins = <
                MX6UL_PAD_NAND_CE1_B__GPIO4_IO14           0x000010B0
            >;
        };
    };
};

&iomuxc_snvs {
    pinctrl-names = "default_snvs";
    imx6ull-board {
    pinctrl-0 = <&pinctrl_hog_2>;
        key1_100ask: key1_100ask{        /*!< Function assigned for the core: Cortex-A7[ca7] */
            fsl,pins = <
                MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01        0x000110A0
            >;
        };
    };
};

打开内核目录下的设备树文件:

vi /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14×14.dts

搜索: /&iomuxc_snvs 和 &iomuxc添加上面的内容

根节点下修改:

编译设备树,拷贝到网络文件系统


程序

test_drv.c

#include <linux/module.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>


/**
 * @brief GPIO按键结构体定义
 * 
 * 该结构体用于描述基于GPIO的按键相关属性,包括:
 * - GPIO引脚号:按键所连接的GPIO引脚。
 * - IRQ号:按键对应的中断请求号,用于处理按键事件。
 * - GPIO标志:描述GPIO引脚的配置标志,如输入输出方向、上下拉等。
 */
struct gpio_key {
    int gpio;            /**< GPIO引脚号,用于连接按键。 */
    int irq;             /**< 按键对应的中断请求号。 */
    enum of_gpio_flags flag;  /**< GPIO引脚的配置标志,描述GPIO的特性。 */
};

static struct gpio_key *gpio_keys;


/* 设备树匹配表 */
static const struct of_device_id ask100_gpios[] = {
    { .compatible = "100ask,gpio_key" },
    /* 空项作为数组的结束标志 */
    { },
};

/**
 * gpio_key_irq_100ask - 处理GPIO按键中断
 * @irq: 中断号
 * @dev_id: 设备标识,这里指代结构体gpio_key
 *
 * 该函数用于处理特定GPIO引脚上的按键中断事件。它通过读取GPIO值来确定按键状态,
 * 并将中断号和按键值打印到系统日志中。返回IRQ_HANDLED表示中断已成功处理。
 *
 */
static irqreturn_t gpio_key_irq_100ask(int irq, void *dev_id)
{
    /* 将void *类型转换为struct gpio_key *类型 */
    struct gpio_key *gpio_key = dev_id;

    /* 打印中断号和按键值到系统日志 */
    printk("key %d val %d\n", irq, gpio_get_value(gpio_key->gpio));

    /* 返回中断处理结果,表示中断已处理 */
    return IRQ_HANDLED;
}


/**
 * chip_demo_gpio_probe - 初始化GPIO按键设备
 * @pdev: platform_device结构体指针,代表设备节点
 * 
 * 该函数用于在设备初始化时,配置和请求GPIO及对应的中断资源。
 * 它通过解析设备树节点获取GPIO数量和具体 GPIO 及其属性,然后为每个GPIO按键配置中断处理函数。
 * 返回0表示初始化成功。
 */
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
    /* 获取设备树节点指针 */
    struct device_node *node = pdev->dev.of_node;
    int count;
    int i;
    enum of_gpio_flags flags;     /* GPIO标志 */
    int gpio;
    int irq;
    int err;

    /* 从设备树中获取GPIO数量 */
    count = of_gpio_count(node);

    /* 动态分配内存用于存储gpio_keys结构体数组 */
    gpio_keys = kzalloc(count * sizeof(struct gpio_key), GFP_KERNEL);

    for (i = 0; i < count; i++) {
        /* 从设备树中获取GPIO号和标志 */
        gpio = of_get_gpio_flags(node, i, &flags);
        /* 将GPIO号转换为中断号 */
        irq = gpio_to_irq(gpio);

        /* 初始化gpio_keys数组元素 */
        gpio_keys[i].gpio = gpio;
        gpio_keys[i].irq  = irq;
        gpio_keys[i].flag = flags;

        /* 请求中断资源,并配置中断处理函数 */
        err = request_irq(irq, gpio_key_irq_100ask, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys[i]);
    }

    /* 初始化成功返回0 */
    return 0;
}

static int chip_demo_gpio_remove(struct platform_device *pdev)
{
    struct device_node *node = pdev->dev.of_node;
    int count;
    int i;

    count = of_gpio_count(node);
    for (i = 0; i < count; i++)
    {
        free_irq(gpio_keys[i].irq, &gpio_keys[i]);
    }
    
    return 0;
}


/* 1. 定义platform_driver */
static struct platform_driver test_gpio_drv = {
    .probe      = chip_demo_gpio_probe,
    .remove     = chip_demo_gpio_remove,
    .driver     = {
        .name   = "100ask_led",
        .of_match_table = ask100_gpios,
    },
};


static int test_gpio_drv_init(void)
{
    platform_driver_register(&test_gpio_drv);
    return 0;
}

static void test_gpio_drv_exit(void)
{
    platform_driver_unregister(&test_gpio_drv);
}

module_init(test_gpio_drv_init);
module_exit(test_gpio_drv_exit);

MODULE_LICENSE("GPL");


Makefile


# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88# 板子所用内核源码的目录

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o



obj-m += test_drv.o


上机

板子挂载网络文件系统,安装驱动,按下按键自动执行程序

💡商业转载请联系作者获得授权,非商业转载请注明出处。
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)。
使用这些免费资源的时候应遵守版权意识,切勿非法利用,售卖,尊重原创内容知识产权。未经允许严禁转载。

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇