在树莓派上新增一个按键并通过内核模块(ko 驱动)来处理,可以通过编写一个 Linux 内核模块来实现。以下是一个简单的示例,展示如何编写和加载一个内核模块来处理 GPIO 按键输入。
硬件连接
假设你已经将按键连接到树莓派的 GPIO 17 引脚,并且另一端连接到 GND。
编写内核模块
- 创建内核模块源文件:创建一个名为
gpio_button.c
的文件。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#define GPIO_BUTTON 17 // GPIO 引脚号
static unsigned int irqNumber;
static bool buttonPressed = false;
// 中断处理函数
static irqreturn_t gpio_irq_handler(int irq, void *dev_id) {
buttonPressed = !buttonPressed;
printk(KERN_INFO "GPIO Button: Interrupt! Button state is %d\n", buttonPressed);
return IRQ_HANDLED;
}
// 模块初始化函数
static int __init gpio_button_init(void) {
int result = 0;
// 请求 GPIO
if (!gpio_is_valid(GPIO_BUTTON)) {
printk(KERN_INFO "GPIO Button: Invalid GPIO\n");
return -ENODEV;
}
gpio_request(GPIO_BUTTON, "sysfs");
gpio_direction_input(GPIO_BUTTON);
gpio_set_debounce(GPIO_BUTTON, 200);
gpio_export(GPIO_BUTTON, false);
// 获取 IRQ 编号
irqNumber = gpio_to_irq(GPIO_BUTTON);
printk(KERN_INFO "GPIO Button: The button is mapped to IRQ: %d\n", irqNumber);
// 请求 IRQ
result = request_irq(irqNumber, (irq_handler_t) gpio_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpio_button_handler", NULL);
if (result) {
printk(KERN_INFO "GPIO Button: Unable to request IRQ: %d\n", result);
return result;
}
printk(KERN_INFO "GPIO Button: Module loaded\n");
return result;
}
// 模块退出函数
static void __exit gpio_button_exit(void) {
free_irq(irqNumber, NULL);
gpio_unexport(GPIO_BUTTON);
gpio_free(GPIO_BUTTON);
printk(KERN_INFO "GPIO Button: Module unloaded\n");
}
module_init(gpio_button_init);
module_exit(gpio_button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple GPIO Button Driver");
MODULE_VERSION("0.1");
- 编译内核模块:创建一个名为
Makefile
的文件,用于编译内核模块。
obj-m += gpio_button.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
- 编译和加载内核模块:
# 编译内核模块
make
# 加载内核模块
sudo insmod gpio_button.ko
# 检查内核日志
dmesg | tail
- 卸载内核模块:
# 卸载内核模块
sudo rmmod gpio_button
# 检查内核日志
dmesg | tail
代码解释
- 中断处理函数:
gpio_irq_handler
是中断处理函数,当按键状态发生变化时会被调用。它会切换buttonPressed
变量的状态,并打印按键状态。- 模块初始化函数:
gpio_button_init
是模块的初始化函数。它请求 GPIO 引脚,设置为输入模式,并请求中断。- 模块退出函数:
gpio_button_exit
是模块的退出函数。它释放中断和 GPIO 引脚。- 模块信息:
MODULE_LICENSE
、MODULE_AUTHOR
、MODULE_DESCRIPTION
和MODULE_VERSION
用于描述模块信息。总结
通过编写和加载一个内核模块,可以在树莓派上处理 GPIO 按键输入。这种方法适用于需要在内核空间处理硬件事件的场景。上述示例展示了如何编写一个简单的内核模块来处理 GPIO 按键的中断,并在内核日志中打印按键状态。