sanmaoljh的笔记 https://bbs.21ic.com/?560548 [收藏] [复制] [RSS]

日志

按键控制led驱动

已有 647 次阅读2012-6-30 07:05 |系统分类:ARM| 按键控制led驱动

/*******************************************************************************
硬件环境:  友善之臂mini2440开发板


软件环境:  操作系统ubuntu8.04,内核版本linux2.6.29



按键驱动说明:
    手动创建设备文件与自动创建设备文件
    自动创建设备文件,用设备名称DEVICE_NAME buttons 自动生成设备节点
    本程序按键按下消抖20ms,很稳定。但是按键抬起释放消抖不好,以后想办法
    要处理好。改成下降沿和上升沿触发中断,也不理想。
    
作者:   ljh
    
时间:   2012.06.20
    
********************************************************************************/


//#define DEBUG


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


#define DEVICE_NAME         "buttons-ctl-leds"  //设备名称
#define DEVICE_MAJOR        232                 //主设备号               
#define KEY_TIMER_DELAY1    (HZ/50)             //按键按下去抖延时20毫秒       
#define KEY_TIMER_DELAY2    (HZ/10)             //按键抬起去抖延时100毫秒
#define KEY_DOWN            0                   //按键按下                   
#define KEY_UP              1                   //按键抬起               
#define KEY_UNCERTAIN       2                   //按键不确定                   
#define KEY_COUNT           6                   //6个按键                   


static volatile int ev_press = 0;                //按键按下产生标志
static volatile int key_status[KEY_COUNT];       //记录6个按键的状态   
static struct timer_list key_timers[KEY_COUNT];  //定义6个按键去抖动定时器
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);    //定义并初始化等待队列


 


 


//组织硬件资源结构体
struct button_irq_desc   
{
    int irq;            //中断号
    int pin;            //引脚
    int pin_setting;    //引脚配置
    int number;   //编号
    char *name;         //按键名称,注意这个名称,在后面的一个现象中会出现
};


//定义6个按键资源结构体数组
static struct button_irq_desc button_irqs[]=
{
    {IRQ_EINT8 , S3C2410_GPG0, S3C2410_GPG0_EINT8 ,  0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG3, S3C2410_GPG3_EINT11 , 1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG5, S3C2410_GPG5_EINT13 , 2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG6, S3C2410_GPG6_EINT14 , 3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG7, S3C2410_GPG7_EINT15 , 4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG11,S3C2410_GPG11_EINT19, 5, "KEY5"},
};


 



/**************************************************************************/
static unsigned long led_table [] =
{
 S3C2410_GPB5,     //led1
 S3C2410_GPB6,     //led2
 S3C2410_GPB7,     //led3
 S3C2410_GPB8,     //led4
};


static unsigned int led_cfg_table [] =
{
 S3C2410_GPB5_OUTP,
 S3C2410_GPB6_OUTP,
 S3C2410_GPB7_OUTP,
 S3C2410_GPB8_OUTP,
};


 


 



static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
    struct button_irq_desc * button = (struct button_irq_desc *)dev_id;
    
    //获取当前按键资源的索引
    //int key =(int)dev_id;


    if(key_status[button->number]== KEY_UP)  
    {
        //设置当前按键的状态为不确定
        key_status[button->number]= KEY_UNCERTAIN;


        //设置当前按键按下去抖定时器的延时并启动定时器
        key_timers[button->number].expires= jiffies + KEY_TIMER_DELAY1; //按键按下开始延时20ms消抖  
        add_timer(&key_timers[button->number]);
       
        #ifdef DEBUG
         printk("test come interrupt\n"); 
         printk("number = %d\n",button->number + 1); 
     #endif
    }
   



    return IRQ_RETVAL(IRQ_HANDLED);
}


static void buttons_timer(unsigned long arg)
{
    //获取当前按键资源的索引
    int key =arg;


    //获取当前按键引脚上的电平值来判断按键是按下还是抬起
    int up = s3c2410_gpio_getpin(button_irqs[key].pin);


    if(!up)//低电平,按键按下
    {
        if(key_status[key]== KEY_UNCERTAIN)      //消抖20ms,检测仍是低电平,按键判定为按下状态
        {
            //标识当前按键状态为按下
            key_status[key]= KEY_DOWN;
           


            //标识当前按键已按下并唤醒等待队列
            ev_press = 1;
            wake_up_interruptible(&button_waitq);
           
            #ifdef DEBUG
            printk(" %d test falling\n",key+1);     //内核打印,哪个按键按下
         #endif
         
        }


        //设置当前按键抬起去抖定时器的延时并启动定时器
        key_timers[key].expires= jiffies + KEY_TIMER_DELAY2; //按键抬起释放消抖20ms 
        add_timer(&key_timers[key]);
    }
    else//高电平,按键抬起
    {
         //按键抬起释放消抖20ms 
   //if(key_status[key]== KEY_UNCERTAIN)   
         //{
           //标识当前按键状态为抬起
          key_status[key]= KEY_UP;
             
             #ifdef DEBUG
             printk(" %d test raising\n",key+1);     //内核打印,哪个按键释放        
           #endif
         //}  
       


   
    }
}


static int buttons_open(struct inode*inode, struct file *file)
{
    int i;
    int ret;


    for(i = 0; i < KEY_COUNT; i++)
    {
        //设置6个IO口为中断触发方式
        s3c2410_gpio_cfgpin(button_irqs.pin, button_irqs.pin_setting);


        //设置中断下降沿为有效触发
        set_irq_type(button_irqs.irq, IRQ_TYPE_EDGE_FALLING);
       
        //申请中断(类型为快速中断,中断服务时屏蔽所有外部中断?)
     //   ret = request_irq(button_irqs.irq, buttons_interrupt, IRQF_DISABLED, button_irqs.name,(void *)i);
        ret = request_irq(button_irqs.irq, buttons_interrupt, IRQF_DISABLED, button_irqs.name,(void *)&button_irqs);
       
        if(ret)
        {
            break;
        }


        //初始化6个按键的状态为抬起
        key_status= KEY_UP;


        //初始化并设置6个去抖定时器
        setup_timer(&key_timers, buttons_timer, i);
    }


    if(ret)
    {
        //中断申请失败处理
        i--;


        for(; i>= 0; i--)
        {
            //释放已注册成功的中断
            disable_irq(button_irqs.irq);
            free_irq(button_irqs.irq,(void *)&button_irqs);
        }


        return -EBUSY;
    }


    return 0;
}


static int buttons_close(struct inode*inode, struct file *file)
{
    int i;


    //释放6个定时器和中断
    for(i = 0; i < KEY_COUNT; i++)
    {
        del_timer(&key_timers);


        disable_irq(button_irqs.irq);
        free_irq(button_irqs.irq,(void *)&button_irqs);
    }


    return 0;
}


static int buttons_read(struct file *file,char __user *buf, size_t count, loff_t *offp)
{
    unsigned long ret;


    if(!ev_press)//判断按键按下产生标识,0没有产生
 {
        if(file->f_flags& O_NONBLOCK)
        {
            //应用程序若采用非阻塞方式读取则返回错误


            return -EAGAIN;
        }
        else
        {
            //以阻塞方式读取且按键按下没有产生,让等待队列进入睡眠
            wait_event_interruptible(button_waitq, ev_press);
        }
    }


    //1为按键按下产生,并清除标识为0,准备给下一次判断用
    ev_press = 0;


    //将内核中的按键状态数据拷贝到用户空间给应用程序使用
    //返回值大于0表示没有拷贝的字节数,返回0表示成功
    ret = copy_to_user(buf,(void *)key_status,min(sizeof(key_status),count));
 
    return ret ?-EFAULT : min(sizeof(key_status),count);
}


 



//驱动程序中的轮询,用于应用程序中的轮询查询是否可对设备进行访问
static int buttons_poll(struct file *file,struct poll_table_struct *wait)
{
    unsigned int mask= 0;


    //添加等待队列到等待队列表中(poll_table)
    poll_wait(file,&button_waitq, wait);


    if(ev_press)
    {
        //标识数据可以获得
        mask |= POLLIN| POLLRDNORM;
    }


    return mask;
}


 


 


//ioctl方法,通过相同名字的系统调用,达到控制硬件的能力
static int mini2440_leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
 int i=0;
 
 switch(cmd)
 {
  case 0:
  case 1:
   if (arg > 5)
   {
    return -EINVAL;
   }
  
   //s3c2410_gpio_setpin(led_table[arg], !cmd);
   int up = s3c2410_gpio_getpin(led_table[arg]); //获取相应管脚电平 
   
   if(up)
   {
    s3c2410_gpio_setpin(led_table[arg], 0);  //低电平led亮
   }
   else
   {
    s3c2410_gpio_setpin(led_table[arg], 1);  //高电平led灭  
   } 
   
   
   return 0;
 
  //自己添加四个led闪烁20s
  case 55:
   //延时20ms led闪烁
   while(i < 20)
   {
    s3c2410_gpio_setpin(led_table[0], 1); //高电平led灭 
    s3c2410_gpio_setpin(led_table[1], 1); //高电平led灭
    s3c2410_gpio_setpin(led_table[2], 1); //高电平led灭
    s3c2410_gpio_setpin(led_table[3], 1); //高电平led灭
    mdelay(500);       //延时500ms
    
    s3c2410_gpio_setpin(led_table[0], 0); //低电平led亮
    s3c2410_gpio_setpin(led_table[1], 0); //低电平led亮
    s3c2410_gpio_setpin(led_table[2], 0); //低电平led亮
    s3c2410_gpio_setpin(led_table[3], 0); //低电平led亮
    mdelay(500);       //延时500ms  
    
    i++;
   }
   return 0; 
   
  default:
   return -EINVAL;
 }
}


//设备操作列表
static struct file_operations buttons_fops=
{
    .owner        = THIS_MODULE,
    .open         = buttons_open,
    .release      = buttons_close,
    .read         = buttons_read,
    .poll         = buttons_poll,
 
 .ioctl    = mini2440_leds_ioctl,   //控制led灯光
 
};


 


static struct miscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &buttons_fops, 
};



static int __init button_init(void)
{
    int ret, i;


    //注册字符设备
   // ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME,&buttons_fops); //手动创建设备文件 
 ret = misc_register(&misc);           //动态创建设备文件
 
  
  //初始化leds关闭
  for (i = 0; i < 4; i++)
 {
  s3c2410_gpio_cfgpin(led_table, led_cfg_table);  //IO配置输出
  s3c2410_gpio_setpin(led_table, 1);      //输出高电平Leds灭
 }  
   
    if(ret < 0)
    {
        printk(DEVICE_NAME " register faild!\n");
        return ret;
    }
    else
    {
     printk (DEVICE_NAME"\tinitialized\n");
    } 


    return 0;
}


static void __exit button_exit(void)
{
    //注销字符设备
    //unregister_chrdev(DEVICE_MAJOR, DEVICE_NAME);
 misc_deregister(&misc);
}


module_init(button_init);
module_exit(button_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("ljh");
MODULE_DEION("buttons-ctl-leds driver"); 


 


 


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)