mini2440驱动学习第三课————BUTTON

本文介绍了一种基于Linux内核的按键驱动实现方法,重点讲解了IRQ中断的注册及使用,以及工作队列的应用。通过具体代码展示了如何为每个按键注册中断处理函数,并在按键按下时更新状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一课是按键驱动,其中重点是linux内irq中断的注册以及使用方法和工作列表的用法。

 

mini2440所用到的按键资源    

按键          对应的IO寄存器     对应的中断引脚  

K1             GPG0                 EINT8       

K2             GPG3                 EINT11      

K3             GPG5                 EINT13       

K4             GPG6                 EINT14     

K5             GPG7                 EINT15       

K6             GPG11               EINT19       

 

贴出代码如下:

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>

 

#define DEVICE_NAME     "buttons"

 

struct button_irq_desc

{
     int irq;                          //中断号
     int pin;                         //对应寄存器
     int pin_setting;              //对应引脚
     int number;                   //编号
     char *name;                  //名称
};

 

static struct button_irq_desc button_irqs [] =

{
    {IRQ_EINT8 ,  S3C2410_GPG(0) ,   S3C2410_GPG0_EINT8  ,   0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG(3) ,   S3C2410_GPG3_EINT11 ,  1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG(5) ,   S3C2410_GPG5_EINT13 ,  2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG(6) ,   S3C2410_GPG6_EINT14 ,  3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG(7) ,   S3C2410_GPG7_EINT15 ,  4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},
};


static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'}; //存放按键操作结果

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);          //定义一个工作队列button_waitq

static volatile int ev_press = 0;                                      //按键标识位


static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{                             //按键中断处理函数
      struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

                               //把触发中断的中断设备信息拷贝到指针button_irqs上,方便接下来的调用
      int down;

      // udelay(0);
      down = !s3c2410_gpio_getpin(button_irqs->pin);

      if (down != (key_values[button_irqs->number] & 1))

      {                                        // 按键状态有改变

          key_values[button_irqs->number] = '0' + down;
 
          ev_press = 1;
          wake_up_interruptible(&button_waitq);       //唤醒工作队列中的任务
      }
   
       return IRQ_RETVAL(IRQ_HANDLED);
}


static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{                                //按键设备打开函数
      int i;
      int err = 0;
   
      for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)

      {
          if (button_irqs[i].irq < 0)

          {
              continue;
          }
          err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
                                   button_irqs[i].name, (void *)&button_irqs[i]);

                                                        //中断注册函数,若成功返回0
          if (err)
              break;
      }

 

     if (err)                   //若有其中一个中断申请失败,则连之前申请成功的都一并卸载掉

     {
          i--;
          for (; i >= 0; i--)

          {
               if (button_irqs[i].irq < 0)

               {
                    continue;
               }
               disable_irq(button_irqs[i].irq);
               free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
           }
           return -EBUSY;
      }

      ev_press = 1;         
   
      return 0;
}


static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{                                 //关闭按键设备
      int i;
   
      for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)

      {                           //释放中断资源
            if (button_irqs[i].irq < 0)

            {
                 continue;
            }
            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
      }

      return 0;
}


static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{                                  //读取按键状态
      unsigned long err;

      if (!ev_press)           //若无按键按下

      {
            if (filp->f_flags & O_NONBLOCK)    //无阻塞,不等待,直接返回-EAGAIN
                  return -EAGAIN;
            else                                            //阻塞,当ev_press为真时跳出
                  wait_event_interruptible(button_waitq, ev_press);
      }
   
      ev_press = 0;

      err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));

                                                             //把按键状态数组key_values传到用户空间

      return err ? -EFAULT : min(sizeof(key_values), count);
}

 

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{                                           //此poll函数有待学习,在此做个记号**********
      unsigned int mask = 0;
      poll_wait(file, &button_waitq, wait);
      if (ev_press)
          mask |= POLLIN | POLLRDNORM;
      return mask;
}


static struct file_operations dev_fops =

{
      .owner   =   THIS_MODULE,
      .open     =   s3c24xx_buttons_open,
      .release =   s3c24xx_buttons_close,
      .read      =   s3c24xx_buttons_read,
      .poll        =   s3c24xx_buttons_poll,
};

 

static struct miscdevice misc =

{
      .minor = MISC_DYNAMIC_MINOR,
      .name = DEVICE_NAME,
      .fops   = &dev_fops,
};

 

static int __init dev_init(void)
{                         //模块初始化
      int ret;

      ret = misc_register(&misc);

      printk (DEVICE_NAME"/tinitialized/n");

      return ret;
}

 

static void __exit dev_exit(void)
{                          //退出模块
      misc_deregister(&misc);
}

 

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

 

注意:

1.中断处理相关函数

注册irq

int request_irq(unsigned int irq,

                      void (*handler)(int, void *, struct pt_regs *),

                      unsigned long irqflags,

                      const char *devname,

                      void *dev_id);

    irq是要申请的硬件中断号。在Intel平台,范围0--15。

      handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device id,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。

    irqflags是中断处理的一些属性。比较重要的有SA_INTERRUPT, 标明中断处理程序是快速处理程序(设置SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有一个SA_SHIRQ属性,设置了以后运行多个设备共享中断。

    devname是中断设备名。

    dev_id在中断共享时会用到。一般设置为这个设备的 device结构本身或者NULL。

 

释放irq

void free_irq(unsigned int irq,void *dev_id);

    需要释放irq资源时,只需填入相应的irq中断号和dev_id即可。

 

中断处理函数

static irqreturn_t buttons_interrupt(int irq, void *dev_id);

    每次按键触发中断,进入中断处理函数,进行相关操作。

 

    中断资源弥足珍贵,最好选择在打开设备即需要使用时注册,而不是装载模块时,若退出模块时请切记释放中断资源。

 

2.工作队列

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    创建了一个等待队列,每当有一按键按下,激活队列中一个等待的任务。

poll_wait(file, &button_waitq, wait);
    监测进程队列button_waitq里的进程,如果ev_press置1,就跳出等待。 

wait_event_interruptible(button_waitq, ev_press);
    等待,当ev_press为1时,跳出。  

wake_up_interruptible(&button_waitq);   

    数组可读,唤醒休眠的进程。   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值