基于树莓派4B的Linux驱动------按键中断

基于树莓派4B的Linux驱动------按键中断

本人也是接触Linux不久,可能有些问题也没考虑到,以下仅是个人观点,欢迎留言,共同进步,话不多说,直接步入正题。

一、实验说明

本次实验采用设备树pinctrl方式编程
开发板基于树莓派4B
linux内核版本:linux-rpi-5.15.y
开发平台:ubuntu交叉编译
按键按下为高电平

二、修改设备树文件

为了方便,我这里是直接在根节点上添加了以下内容,我这里修改的是arch/arm/boot/bts/bcm2711-rpi-4-b.dts文件,在该文件下面添加以下内容,具体的一些pinctrl的设备树语法可以参考内核说明文档,在Documentation/devicetree/bindings/pinctrl目录下面的文档,如brcm,bcm2835-gpio.txt文件

bcm2711-rpi-4-b.dts根节点添加以下内容

keytest{
	#address-cells = <1>;
    #size-cells = <1>;
	compatible = "keytest";
	gpios = <&gpio 19 GPIO_ACTIVE_HIGH>;
	pinctrl-names = "default";
	pinctrl-0 = <&key1_test>;
	status = "okay";
};

bcm2711-rpi-4-b.dts添加以下追加内容

&gpio {
	key1_test: key1_test {
		brcm,pins = <19>;
		brcm,function = <0>;
		brcm,pull = <2>;
	};
};

在属性brcm,pins中,0表示GPIO0,1表示GPIO1,2表示GPIO2 ···
在属性brcm,function中,0表示in,1表示out,2表示ALT0,3表示ALT1 ···
在属性brcm,pull 中,0表示none, 1表示up, 2表示down
可以参考内核文档Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
ALT0,ALT1,ALT3,ALT4,ALT5可以去看bcm2711的芯片手册,在GPIO章节,手册可以到树莓派官网去下载

三、编写驱动程序

驱动程序可以参考内核的驱动程序,如drivers/input/keyboard/gpio_keys.c文件
以下驱动程序可以兼容使用多个按键,但是我在设备树里面只设置了一个按键,所以如果想用多个按键的时候,可以只修改设备树就能实现了
of_gpio_count 函数获取设备树描述的该节点的GPIO个数
kzalloc 函数用于申请内存
of_get_gpio_flags 函数用于获取GPIO编号
gpio_to_irq 函数用于获取中断编号
request_irq 函数用于申请中断,第一个参数是中断编号,第二个参数是中断服务函数的指针,第三个参数是触发方式,第四个参数是中断名字,第五个参数是传进去一个地址,这个地址后面会传到中断服务函数里面,然后中断服务函数就可以使用某些变量的值,当然如果不用到也可以不传,写个NULL就可以了

testkey.c

#include <linux/module.h>
#include <linux/hrtimer.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_gpio.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>


struct gpio_key{
	int gpio;
	int irq;
	enum of_gpio_flags flag;
};

static struct gpio_key *gpio_keys;


static irqreturn_t gpio_key_irq_handle(int irq, void *dev_id)
{
	struct gpio_key *gpio_key = dev_id;

	printk("key %d val %d\n", irq, gpio_get_value(gpio_key->gpio));

	return IRQ_HANDLED;
}

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

	count = of_gpio_count(node);
	if(count <= 0)
	{
		ret = -EINVAL;
		goto fail_count;
	}

	gpio_keys = kzalloc(count * sizeof(struct gpio_key), GFP_KERNEL);
	if(!gpio_keys)
	{
		printk("内存分配失败\n");
		ret = -ENOMEM;
		goto fail_kzalloc;
	}
	for(i = 0; i < count; i++)
	{
		gpio_keys[i].gpio = of_get_gpio_flags(node, i, &gpio_keys[i].flag);
		if (!gpio_is_valid(gpio_keys[i].gpio))
		{
			printk("设备树获取失败 key: %d\n", i);
			ret =  -EINVAL;
			goto fail_flags;
		}
		gpio_keys[i].irq = gpio_to_irq(gpio_keys[i].gpio);
		if(gpio_keys[i].irq < 0)
		{
			printk("中断号获取失败 key: %d\n", i);
			ret = gpio_keys[i].irq;
			goto fail_irq;
		}
		ret = request_irq(gpio_keys[i].irq, gpio_key_irq_handle, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "test_gpio_key", &gpio_keys[i]);
		if (ret != 0)
		{
			ret = -1;
			printk("无法请求 gpio_keys irq\n");
			free_irq(gpio_keys[i].irq, &gpio_keys[i]);
			goto fail_request;
		}
	}

	return 0;

fail_request:
fail_irq:
fail_flags:
fail_kzalloc:
	kfree(gpio_keys);
fail_count:
	return ret;
}

static const struct of_device_id key_gpios[] = {
	{.compatible = "keytest"},
	{},
};

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

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

	return 0;
}

static struct platform_driver test_gpio_drv = {
	.probe = chip_demo_gpio_probe,
	.remove = chip_demo_gpio_remove,
	.driver = {
		.name = "keytest",
		.of_match_table = key_gpios,
	},
};

static __init int test_gpio_init(void)
{
	printk("test_gpio_init\n");
	platform_driver_register(&test_gpio_drv);
	return 0;
}

static __exit void test_gpio_exit(void)
{
	printk("test_gpio_exit\n");
	platform_driver_unregister(&test_gpio_drv);
}

module_init(test_gpio_init);
module_exit(test_gpio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");

四、编写MakeFile程序

KERNELDIR := /home/pi/linux/pi4_kernel/linux-rpi-5.15.y

CURRENT_PATH := $(shell pwd)

obj-m := testkey.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

五、编译测试

make 编译驱动程序,并把编译好的.ko文件拷贝到树莓派
到内核目录 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs 编译设备树,把编译好的设备树文件拷贝到树莓派boot目录下,重启树莓派,执行
sudo insmod testkey.ko 加载驱动程序
可以使用dmesg命令查看内核日志
按下按键,查看内核日志,打印如下
key 80 val 1
key 80 val 0
好的,实验完成

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值