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); // 释放结构体占用的内存
}