【驱动篇】龙芯LS2K0300之按键驱动

实验过程

实验目的: 在龙芯开发板上面验证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

在这里插入图片描述

设备树

还是把i2c0和i2c1部分代码注释掉,需要把它们当作4个GPIO来使用

在这里插入图片描述

驱动程序

GPIO、中断和KEY的对应关系表

GPIO中断KEY引脚
482516
492618
502715
512817

定义相关按键设备结构体

#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	4

struct 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;
	
	// timer
	timer_setup(&key_dev.timer, key_timer_function, 0);

	for(i = 0; i < KEY_NUM; i++) {
		// gpio 48 49 50 51
		key_dev.key[i] = 48 + i;

		// request
		ret = gpio_request(key_dev.key[i], "LED-GPIO");
		if (ret) {
			printk(KERN_ERR "key_dev: Failed to request led-gpio\n");
			return ret;
		}

		// input
		ret = gpio_direction_input(key_dev.key[i]);
		if(ret < 0) {
			printk("can't set gpio!\r\n");
		}

		// irq
		key_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 dev
	key_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	4

struct 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;
    
    // timer
	timer_setup(&key_dev.timer, key_timer_function, 0);

	for(i = 0; i < KEY_NUM; i++) {
		// gpio 48 49 50 51
		key_dev.key[i] = 48 + i;

		// request
		ret = gpio_request(key_dev.key[i], "LED-GPIO");
		if (ret) {
			printk(KERN_ERR "key_dev: Failed to request led-gpio\n");
			return ret;
		}

		// input
		ret = gpio_direction_input(key_dev.key[i]);
		if(ret < 0) {
			printk("can't set gpio!\r\n");
		}

		// irq
		key_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 dev
	key_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个按键对应的log

在这里插入图片描述

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是一个简单的按键中断流水灯系统代码,使用龙芯系统的C语言编写: ``` #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #define GPIO_DATA_OFFSET 0x00 #define GPIO_TRI_OFFSET 0x04 #define GPIO_DATAIN_OFFSET 0x10 #define GPIO_INTEN_OFFSET 0x30 #define GPIO_INTMASK_OFFSET 0x34 #define GPIO_INTTYPE_LEVEL_OFFSET 0x38 #define GPIO_INT_POLARITY_OFFSET 0x3C #define GPIO_INT_STATUS_OFFSET 0x40 #define LED_COUNT 8 #define LED_DELAY 200000 volatile unsigned int *gpio; void setup_gpio() { int fd; fd = open("/dev/mem", O_RDWR); if (fd < 0) { perror("open /dev/mem failed"); exit(1); } gpio = (unsigned int *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x18040000); if (gpio == MAP_FAILED) { perror("mmap failed"); exit(1); } close(fd); // Configure LED pins as outputs *gpio |= (0xff << 16); // Configure button pin as input *gpio &= ~(1 << 4); // Enable interrupt for button pin *(gpio + GPIO_INTMASK_OFFSET/4) &= ~(1 << 4); *(gpio + GPIO_INTTYPE_LEVEL_OFFSET/4) &= ~(1 << 4); *(gpio + GPIO_INT_POLARITY_OFFSET/4) &= ~(1 << 4); *(gpio + GPIO_INTEN_OFFSET/4) |= (1 << 4); } void handle_interrupt(int sig) { static int led_index = 0; if (*(gpio + GPIO_INT_STATUS_OFFSET/4) & (1 << 4)) { // Button interrupt for (int i = 0; i < LED_COUNT; i++) { *gpio |= (1 << (16 + i)); usleep(LED_DELAY); *gpio &= ~(1 << (16 + i)); } led_index = (led_index + 1) % LED_COUNT; *gpio |= (1 << (16 + led_index)); *(gpio + GPIO_INT_STATUS_OFFSET/4) |= (1 << 4); // clear interrupt status } } int main() { signal(SIGIO, handle_interrupt); setup_gpio(); while (1) { pause(); } return 0; } ``` 这个程序使用了龙芯系统的GPIO模块来控制8个LED灯,同时监听一个按键的中断信号。每次按下按键,流水灯会依次亮起,然后再从头开始循环。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下里巴人hywing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值