本文最后更新于329 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

对于 GPIO 按键,我们并不需要去写驱动程序,使用内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以,如果自己要从头写,然后你需要做的只是修改设备树指定引脚及键值。然后编写驱动。😊
原理图
修改设备树
对于一个引脚要用作中断时:
- 要通过 PinCtrl 把它设置为 GPIO 功能;
- 表明自身:是哪一个 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
上机
板子挂载网络文件系统,安装驱动,按下按键自动执行程序
评论