编写基于输入子系统的按键驱动程序

s3c2440芯片

功能

开发板上有4个按键,让这4个按键有了电脑键盘的功能
按键S2对应键盘的 l
按键S3对应键盘的s
按键S4对应键盘的 enter
按键S5对应键盘的shift;
还加入了定时器防抖动,代码如下

头文件

#include <linux/module.h>
#include <linux/version.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/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>

#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

全局变量和全局结构体定义

static struct timer_list key_timer;	//使用init_timer()来初始化这个定时器,注意init_timer()函数不复制分配内存,所以这里创建的是结构体变量,而不是结构体指针
static struct input_dev *my_key_input_dev;	//使用input_allocate_device()函数能为这个结构体指针分配内存和初始化
static struct my_key_desc *now_irq_id;	// 这个全局结构体指针在中断处理函数中用到,将中断处理函数传进来的dev_id赋给它,用来在定时器中断中分辨具体是哪个按键中断产生了

struct my_key_desc{			// 结构体定义不能设置为static,但是结构体实化的时候可以
	unsigned int irq; 			// 中断号
	unsigned long irq_flags;	// 中断类型
	const char *name;			// 名字 
	unsigned int pin;			// 引脚号 -- 使用s3c2410_gpio_getpin()函数取引脚值的时候用到 
	unsigned int key_val;		// 事件段代码 -- 使用input_event()函数发送事件的时候用到	
};

static struct my_key_desc key_irq_desc[4]={
	{IRQ_EINT0,  IRQT_BOTHEDGE,  "EINT0_s2", S3C2410_GPF0,  KEY_L}, 
	{IRQ_EINT2,  IRQT_BOTHEDGE,  "EINT0_s3", S3C2410_GPF2,  KEY_S},
	{IRQ_EINT11, IRQT_BOTHEDGE,  "EINT0_s4", S3C2410_GPG3,  KEY_ENTER},
	{IRQ_EINT19, IRQT_BOTHEDGE,  "EINT0_s5", S3C2410_GPG11, KEY_LEFTSHIFT},
};

module_init(init_buttons);

经过module_init修饰后init_buttons()函数就变成不是普通的函数了

/* 安装该驱动设备时执行的初始化操作: */
static int init_buttons(void)		// init函数需要加int类型返回值
{
	int i;
	int error;
	
	my_key_input_dev = input_allocate_device();	//为这个结构体分配内存和初始化
	
	//初始化input_dev结构体--使用set_bit函数对成员进行设置
	set_bit(EV_KEY, my_key_input_dev->evbit);		// 可以发生按键这一“类”事件
	set_bit(EV_REP, my_key_input_dev->evbit);		// 可以发送重复类事件 -- 设置了这位后,执行input_event()函数时会判断是否设置了重复类事件
	// 添加为重复类事件,可以长按按键然后一直输出对应的模拟代码 L或S												 	

	//可以发生这一类事件里的具体哪一个事
	set_bit(KEY_L, 			my_key_input_dev->keybit );	// 按下按键 L 
	set_bit(KEY_S, 			my_key_input_dev->keybit );	// 按下按键 S
	set_bit(KEY_ENTER, 		my_key_input_dev->keybit ); 	// 按下按键 ENTER
	set_bit(KEY_LEFTSHIFT, my_key_input_dev->keybit ); 	// 按下按键 LEFTSHIFT	
	
	input_register_device(my_key_input_dev);		//向input层注册这个input_dev结构体
	
	//注册中断处理函数  中断号;中断处理函数;中断类型;设备名字;设备id号
	for(i=0;i<4;i++){
		error = request_irq(key_irq_desc[i].irq, key_INT_handle, key_irq_desc[i].irq_flags, key_irq_desc[i].name, &key_irq_desc[i]);
		if(error)	{	//中断注册成功则返回0 -- 如果注册不成功则执行该if语句
			printk("gpio-keys: unable to claim irq \n\r");		
		}
	}
	
	init_timer(&key_timer);						//初始化这个结构体 -- 不包括分配内存
	key_timer.function = key_timer_handle; 	//分配处理函数
	add_timer(&key_timer);						//将该定时器链入相应的链表

	return 0;
}

key_INT_handle()按键中断处理函数

/* 按键中断处理函数 -- 注册好后发生中断时会进入该函数 */
static irqreturn_t key_INT_handle(int irq, void *dev_id)
{	
	now_irq_id = ( struct my_key_desc *)dev_id;
	
	//更新一个定时器的超时时间, 使用一个超时定时器的一个普通的任务 -- 只执行一次超时处理
	mod_timer(&key_timer, jiffies+HZ/100); 
	return IRQ_RETVAL(IRQ_HANDLED);
}

key_timer_handle()定时器中断处理函数

/* 定时器中断处理函数用来解决按键抖动问题 */
static void key_timer_handle(unsigned long param)
{
	unsigned int logical_level;	//获取引脚值,这里具体用来获得逻辑电平
	struct my_key_desc *key_desc;
	
	key_desc = now_irq_id;		//将当前按键中断的dev_id赋给key_desc
	
	if( !key_desc )
		return;
	
	logical_level = s3c2410_gpio_getpin(now_irq_id->pin);	//获取当前时刻的引脚电平
	if( logical_level == 0 ){  	//最后一个参数 0表示松开,1表示按下,logical_level==0时按键为按下状态,所以最后一位设置为1
		input_event(my_key_input_dev, EV_KEY, key_desc->key_val, 1);  
		input_sync(my_key_input_dev);	//发送同步信号,表示数据发送完毕
	}	
	else{	//松开:最后一个参数应该填0 【按下并松开才能形成一个完整的字符输入】
		input_event(my_key_input_dev, EV_KEY, key_desc->key_val, 0); 
		input_sync(my_key_input_dev);
	}
}

module_exit(exit_buttons);

/* 卸载该驱动设备时执行的操作 */
static void exit_buttons(void)
{
	int i;
	for(i=0;i<4;i++){
		free_irq(key_irq_desc[i].irq, &key_irq_desc[i]);
	}
	del_timer(&key_timer);		// 删除这个定时器,从链表中移除等操作
	input_unregister_device(my_key_input_dev);	// 从链表中去除该结构体的节点成员
	input_free_device(my_key_input_dev);			// 释放结构体占用的内存
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值