实验过程
实验目的: 在龙芯开发板上面验证GPIO按键的输入过程
① 根据原理图连接按键板
② 将4个i2c引脚的功能复用为GPIO
③ 注册input设备驱动,绑定中断处理函数,使用定时器消抖
原理图
4个按键引脚:CPU_I2C0_SCL -> GPIO48, CPU_I2C0_SDA -> GPIO49, CPU_I2C1_SCL -> GPIO50, CPU_I2C1_SDA -> GPIO51
实物连接图:按顺序连接好按键板上面的K1、K2、K3、K4、GND
设备树
还是把i2c1和i2c2部分代码注释掉,需要把它们当作4个GPIO来使用
驱动程序
GPIO、中断和KEY的对应关系表
GPIO | 中断 | KEY | 引脚 |
---|---|---|---|
48 | 26 | ① | 16 |
49 | 27 | ② | 18 |
50 | 28 | ③ | 15 |
51 | 29 | ④ | 17 |
定义相关按键设备结构体
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/input.h>#define KEY_INPUT "key_input"
#define KEY_NUM 4struct my_key_dev{struct input_dev *idev;struct timer_list timer;int key[KEY_NUM];int irq[KEY_NUM]; int index;
};struct my_key_dev key_dev;
GPIO按键初始化
static int __init gpio_key_init(void)
{int ret = 0;int i = 0;// timertimer_setup(&key_dev.timer, key_timer_function, 0);for(i = 0; i < KEY_NUM; i++) {// gpio 48 49 50 51key_dev.key[i] = 48 + i;// requestret = gpio_request(key_dev.key[i], "LED-GPIO");if (ret) {printk(KERN_ERR "key_dev: Failed to request led-gpio\n");return ret;}// inputret = gpio_direction_input(key_dev.key[i]);if(ret < 0) {printk("can't set gpio!\r\n");}// irqkey_dev.irq[i] = gpio_to_irq(key_dev.key[i]); printk("key%d -> irq : %d\n", key_dev.key[i], key_dev.irq[i]);if(i == 0) {ret = request_irq(key_dev.irq[i], key_interrupt0, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key0_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}else if(i == 1) {ret = request_irq(key_dev.irq[i], key_interrupt1, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key1_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}else if(i == 2) {ret = request_irq(key_dev.irq[i], key_interrupt2, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key2_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}else if(i == 3) {ret = request_irq(key_dev.irq[i], key_interrupt3, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key3_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}}// input devkey_dev.idev = input_allocate_device();key_dev.idev->name = KEY_INPUT;key_dev.idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);input_set_capability(key_dev.idev, EV_KEY, KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN);ret = input_register_device(key_dev.idev);if (ret) {printk("register input device failed!\r\n");goto free_gpio;}return ret;free_gpio:for(i = 0; i < KEY_NUM; i++) {free_irq(key_dev.irq[i],NULL);gpio_free(key_dev.key[i]);}del_timer_sync(&key_dev.timer);return -EIO;
}
中断处理
static irqreturn_t key_interrupt0(int irq, void *dev_id)
{// printk("key_interrupt0 irq %d \n", irq);if(irq == key_dev.irq[0]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 0;}return IRQ_HANDLED;
}static irqreturn_t key_interrupt1(int irq, void *dev_id)
{// printk("key_interrupt1 irq %d \n", irq);if(irq == key_dev.irq[1]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 1;}return IRQ_HANDLED;
}static irqreturn_t key_interrupt2(int irq, void *dev_id)
{// printk("key_interrupt2 irq %d \n", irq);if(irq == key_dev.irq[2]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 2;}return IRQ_HANDLED;
}static irqreturn_t key_interrupt3(int irq, void *dev_id)
{// printk("key_interrupt3 irq %d \n", irq);if(irq == key_dev.irq[3]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 3;}return IRQ_HANDLED;
}
定时器消抖
int key_array[] = {KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN};static void key_timer_function(struct timer_list *arg)
{int val = gpio_get_value(key_dev.key[key_dev.index]);printk("key_timer_function %d -> %d\n", key_dev.key[key_dev.index], val);// if(val == 0) {input_report_key(key_dev.idev, key_array[key_dev.index], !val);input_sync(key_dev.idev);// input_report_key(key_dev.idev, key_array[key_dev.index], 0);// input_sync(key_dev.idev);// printk("key %d press %d\n", key_dev.key[key_dev.index], key_array[key_dev.index]);// }
}
驱动模块卸载函数:回收相关的设备资源,如GPIO、中断、定时器等
static void __exit gpio_key_exit(void)
{int i;for(i = 0; i < KEY_NUM; i++) {free_irq(key_dev.irq[i],NULL);gpio_free(key_dev.key[i]);}del_timer_sync(&key_dev.timer);input_unregister_device(key_dev.idev);
}
整合代码
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/input.h>#define KEY_INPUT "key_input"
#define KEY_NUM 4struct my_key_dev{struct input_dev *idev;struct timer_list timer;int key[KEY_NUM];int irq[KEY_NUM]; int index;
};struct my_key_dev key_dev; int key_array[] = {KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN};static void key_timer_function(struct timer_list *arg)
{int val = gpio_get_value(key_dev.key[key_dev.index]);printk("key_timer_function %d -> %d\n", key_dev.key[key_dev.index], val);// if(val == 0) {input_report_key(key_dev.idev, key_array[key_dev.index], !val);input_sync(key_dev.idev);// input_report_key(key_dev.idev, key_array[key_dev.index], 0);// input_sync(key_dev.idev);// printk("key %d press %d\n", key_dev.key[key_dev.index], key_array[key_dev.index]);// }
}static irqreturn_t key_interrupt0(int irq, void *dev_id)
{// printk("key_interrupt0 irq %d \n", irq);if(irq == key_dev.irq[0]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 0;}return IRQ_HANDLED;
}static irqreturn_t key_interrupt1(int irq, void *dev_id)
{// printk("key_interrupt1 irq %d \n", irq);if(irq == key_dev.irq[1]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 1;}return IRQ_HANDLED;
}static irqreturn_t key_interrupt2(int irq, void *dev_id)
{// printk("key_interrupt2 irq %d \n", irq);if(irq == key_dev.irq[2]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 2;}return IRQ_HANDLED;
}static irqreturn_t key_interrupt3(int irq, void *dev_id)
{// printk("key_interrupt3 irq %d \n", irq);if(irq == key_dev.irq[3]) {mod_timer(&key_dev.timer, jiffies + msecs_to_jiffies(15));key_dev.index = 3;}return IRQ_HANDLED;
}static int __init gpio_key_init(void)
{int ret = 0;int i = 0;// timertimer_setup(&key_dev.timer, key_timer_function, 0);for(i = 0; i < KEY_NUM; i++) {// gpio 48 49 50 51key_dev.key[i] = 48 + i;// requestret = gpio_request(key_dev.key[i], "LED-GPIO");if (ret) {printk(KERN_ERR "key_dev: Failed to request led-gpio\n");return ret;}// inputret = gpio_direction_input(key_dev.key[i]);if(ret < 0) {printk("can't set gpio!\r\n");}// irqkey_dev.irq[i] = gpio_to_irq(key_dev.key[i]); printk("key%d -> irq : %d\n", key_dev.key[i], key_dev.irq[i]);if(i == 0) {ret = request_irq(key_dev.irq[i], key_interrupt0, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key0_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}else if(i == 1) {ret = request_irq(key_dev.irq[i], key_interrupt1, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key1_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}else if(i == 2) {ret = request_irq(key_dev.irq[i], key_interrupt2, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key2_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}else if(i == 3) {ret = request_irq(key_dev.irq[i], key_interrupt3, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "Key3_IRQ", NULL);if (ret) {gpio_free(key_dev.key[i]);return ret;}}}// input devkey_dev.idev = input_allocate_device();key_dev.idev->name = KEY_INPUT;key_dev.idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);input_set_capability(key_dev.idev, EV_KEY, KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN);ret = input_register_device(key_dev.idev);if (ret) {printk("register input device failed!\r\n");goto free_gpio;}return ret;free_gpio:for(i = 0; i < KEY_NUM; i++) {free_irq(key_dev.irq[i],NULL);gpio_free(key_dev.key[i]);}del_timer_sync(&key_dev.timer);return -EIO;
}static void __exit gpio_key_exit(void)
{int i;for(i = 0; i < KEY_NUM; i++) {free_irq(key_dev.irq[i],NULL);gpio_free(key_dev.key[i]);}del_timer_sync(&key_dev.timer);input_unregister_device(key_dev.idev);
}module_init(gpio_key_init);
module_exit(gpio_key_exit);
MODULE_LICENSE("GPL");
Makefile文件
obj-m += key.o
KDIR:=/home/asensing/loongson/linux-4.19
ARCH=loongarch
CROSS_COMPILE=loongarch64-linux-gnu-
PWD?=$(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules
构建脚本
export PATH=$PATH:/home/asensing/loongson/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.3-1/bin
make -j8
# loongarch64-linux-gnu-gcc test.c -o test
FILE=$PWD/$(basename $PWD).ko
scp $FILE root@192.168.137.11:/home/root
实验效果
插入驱动后,按下按键立即打印4个按键值