该按键驱动原理虽简单,但是在处理中却运用到了Linux驱动中中断的一些关键技术,比如“顶半部”和“底半部”使用,等待队列的设置。
这里“顶半部”即中断处理函数运行时间很短,基本就做了两件事:1、关中断;2、调用定时器。具体代码如下:
<span style="font-size:13px;">static irqreturn_t key_eint_handler(int irq, void *dev_id) { int cnt,key_index; key_index = 0; //下面的for循环确定是那个按键产生了中断,并把按键编码存储在key_index中 for(cnt=0; cnt<KEY_NUM; cnt++) { if(g_key_info[cnt].irq_no == irq) { key_index = g_key_info[cnt].key_no; break; } } // printk(KERN_NOTICE "Eint %d\n",key_index); disable_irq(g_key_info[key_index].irq_no); //disable irq 关对应中断 g_key_dev->keystatus[key_index] = KEYSTATUS_X; //set key in unsure state 状态设置成不确定型 g_key_timer[key_index].expires = jiffies + KEY_DELAY_20MS; //set timer value timer_list函数设置 add_timer(&g_key_timer[key_index]); //start timer 激活定时器 20ms后执行g_key_timer 函数 return IRQ_HANDLED; //中断服务程序返回值 }</span>
这里“底半部”通过设置内核定时器实现,“顶半部”调用定时器后便马上退出了,而定时器等待定时时间到达时调用设定的函数完成中断处理函数应该完成的事,即去抖动情况,确定按键被按下后,保存该按键序号,并唤醒被睡眠的进程,读取按键序号值。具体代码:
static void keyEvent(int key_index) { g_key_dev->buf[g_key_dev->head] = key_index; g_key_dev->head = INC_BUF_POINTOR(g_key_dev->head,MAX_KEY_BUF); wake_up_interruptible(&g_key_dev->wq); } static void key_timer_handler(unsigned long data) //data是key_eint_handler中传来的key_index值 { int key_index = data; //printk("B:get key %d\n",s3c2410_gpio_getpin(g_tkey_info[key_index].gpio_port)); if (ISKEY_DOWN(key_index)) //按键被按下 { printk(KERN_NOTICE "B\n"); if(g_key_dev->keystatus[key_index] == KEYSTATUS_X) //情况一、按键状态不明确 { g_key_dev->keystatus[key_index] = KEYSTATUS_DOWN; //change key state // 按键设置为低 g_key_timer[key_index].expires = jiffies + KEY_DELAY_100MS; //re_initial timer //设定延时时间 keyEvent(key_index); //保存按键序号,并唤醒等待队列中的读进程 add_timer(&g_key_timer[key_index]); //restart timer //激活定时器 } else //wait for user release the key //情况二、按键按下 进入等待释放程序 { g_key_timer[key_index].expires = jiffies + KEY_DELAY_100MS; //设定延时时间 add_timer(&g_key_timer[key_index]); //激活定时间,时间到后,再次进入g_key_timer函数 ,检测按键状态 } } else //user have released the key { g_key_dev->keystatus[key_index] = KEYSTATUS_UP; //del_timer(&g_key_timer[key_index]); enable_irq(g_key_info[key_index].irq_no); } }