搜索
bottom↓
回复: 3

[求助]2440按键去抖动问题????

[复制链接]

出0入0汤圆

发表于 2009-11-11 17:25:13 | 显示全部楼层 |阅读模式
各位前辈!!小弟刚学驱动,写了个按键驱动。当加上去抖动后运行测试程序,串口终端没有任何反应,而且按Ctrl+C也退不出测试程序;当去掉抖动部分后又正常。请求各位能帮我看看是什么地方有错误啊?感激不尽!!程序如下:

驱动代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>

#define DEVICE_NAME                        "my2440_buttons"        //设备名称
#define DEVICE_MAJOR                232                                        //主设备号                               
#define KEY_TIMER_DELAY1        (HZ/100)                        //去抖延时1               
#define KEY_TIMER_DELAY2        (HZ/10)                                //去抖延时2
#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个按键的状态
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;                        //按键名称
};

static struct button_irq_desc button_irqs[] = //定义6个按键资源结构体数组
{
    {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 irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
        //获取当前按键的资源数据
        struct button_irq_desc *button_irq = (struct button_irq_desc *)dev_id;

        //屏蔽当前按键的中断
        disable_irq(button_irq->irq);

        //设置当前按键按下去抖定时器的延时并启动定时器
        key_timers[button_irq->number].expires = jiffies + KEY_TIMER_DELAY1;
        add_timer(&key_timers[button_irq->number]);

        return IRQ_RETVAL(IRQ_HANDLED);
}

static void buttons_timer(unsigned long arg)
{
        //获取当前按键的资源数据
        int key = arg;
        struct button_irq_desc *button_irq = (struct button_irq_desc *)&button_irqs[key];

        //获取当前按键引脚上的电平值来判断按键是按下还是抬起
        int up = s3c2410_gpio_getpin(button_irq->pin);

        if(!up)//低电平,按键按下
        {
                if(key_status[key] == KEY_UNCERTAIN)
                {
                        //标识当前按键状态为按下
                        key_status[key] = KEY_DOWN;

                        //标识当前按键的中断已被触发并唤醒等待队列
                        ev_press = 1;
                        wake_up_interruptible(&button_waitq);
                }

                //设置当前按键抬起去抖定时器的延时并启动定时器
                key_timers[key].expires = jiffies + KEY_TIMER_DELAY2;
                add_timer(&key_timers[key]);
        }
        else//高电平,按键抬起
        {
                //标识当前按键状态为抬起
                key_status[key] = KEY_UP;

                //使能当前按键的中断
                enable_irq(button_irqs[key].irq);
        }
}

static int buttons_open(struct inode *inode, struct file *file)
{
        int i, 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 *)&button_irqs);

                if(ret)
                {
                        break;
                }
               
                //初始化6个按键的状态为不确定
                key_status = KEY_UNCERTAIN;

                //初始化并设置6个去抖定时器
                init_timer(&key_timers);
                key_timers.function = buttons_timer;
                key_timers.data = 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;

        //将内核中的按键状态数据拷贝到用户空间给应用程序使用
        ret = copy_to_user(buf, (void *)key_status, min(sizeof(key_status), count));

        //复位6个按键的状态为不确定
        memset((void *)key_status, KEY_UNCERTAIN , sizeof(key_status));

        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_wait(file, &button_waitq, wait);

        if(ev_press)
        {
                mask |= POLLIN | POLLRDNORM;
        }

        return mask;
}

static struct file_operations buttons_fops =
{
        .owner                = THIS_MODULE,
        .open                = buttons_open,
        .release        = buttons_close,
        .read                = buttons_read,
        .poll                = buttons_poll,
};

static int __init button_init(void)
{
        int ret;

        //注册字符设备
        ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &buttons_fops);

        if(ret < 0)
        {
                printk(DEVICE_NAME " register faild!\n");
                return ret;
        }

        return 0;
}

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

module_init(button_init);
module_exit(button_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("My2440 button driver");

测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char **argv)
{
        int fd;
        int key_status[6];

        memset(key_status, 2, sizeof(key_status));

        fd = open("/dev/my2440_buttons", 0);

        if(fd < 0)
        {
                printf("Open Buttons Device Faild!\n");
                exit(1);
        }

        while(1)
        {
                int ret, i;
                fd_set rds;
               
                FD_ZERO(&rds);
                FD_SET(fd, &rds);
               
                ret = select(fd + 1, &rds, NULL, NULL, NULL);
               
                if(ret < 0)
                {
                        printf("Read Buttons Device Faild!\n");
                        exit(1);
                }
               
                if(ret == 0)
                {
                        printf("Read Buttons Device Timeout!\n");
                }
                else if(FD_ISSET(fd, &rds))
                {
                        ret = read(fd, key_status, sizeof(key_status));

                        if(ret != sizeof(key_status))
                        {
                                if(errno != EAGAIN)
                                        printf("Read Button Device Faild!\n");
                                continue;
                        }
                        else
                        {
                                for(i = 0; i < 6; i++)
                                {
                                        if(key_status != 2)
                                        {
                                                printf("Key%d %s\n", i + 1, (key_status) ? "UP" : "DOWN");
                                        }
                                }
                        }
                }
        }

        close(fd);

        return 0;
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2009-11-14 18:44:16 | 显示全部楼层
经过调试发现程序到了中断服务程序里面的屏蔽中断的地方就卡在这里了,这是为什么啊?
//屏蔽当前按键的中断
disable_irq(button_irq->irq); //就是到了这里就卡住了

出0入0汤圆

 楼主| 发表于 2009-11-18 14:28:46 | 显示全部楼层
经过一个礼拜的摸索,问题终于解决啦!!
在申请中断的时候使用的是IRQF_DISABLED快速中断,在执行服务程序的时候已经屏蔽了所有的外部中断。故修改后的驱动和应用如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>

#define DEVICE_NAME                        "my2440_buttons"        //设备名称
#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;        //引脚配置
        char *name;                        //按键名称
};

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

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

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

                //设置当前按键按下去抖定时器的延时并启动定时器
                key_timers[key].expires = jiffies + KEY_TIMER_DELAY1;
                add_timer(&key_timers[key]);
        }

        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)
                {
                        //标识当前按键状态为按下
                        key_status[key] = KEY_DOWN;

                        //标识当前按键已按下并唤醒等待队列
                        ev_press = 1;
                        wake_up_interruptible(&button_waitq);
                }

                //设置当前按键抬起去抖定时器的延时并启动定时器
                key_timers[key].expires = jiffies + KEY_TIMER_DELAY2;
                add_timer(&key_timers[key]);
        }
        else//高电平,按键抬起
        {
                //标识当前按键状态为抬起
                key_status[key] = KEY_UP;
        }
}

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);

                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 *)i);
                }

                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 *)i);
        }

        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;

        //将内核中的按键状态数据拷贝到用户空间给应用程序使用
        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;
}

//设备操作列表
static struct file_operations buttons_fops =
{
        .owner                = THIS_MODULE,
        .open                = buttons_open,
        .release        = buttons_close,
        .read                = buttons_read,
        .poll                = buttons_poll,
};

static int __init button_init(void)
{
        int ret;

        //注册字符设备
        ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &buttons_fops);

        if(ret < 0)
        {
                printk(DEVICE_NAME " register faild!\n");
                return ret;
        }

        return 0;
}

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

module_init(button_init);
module_exit(button_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("My2440 button driver");


应用程序:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char **argv)
{
        int fd;
        int key_status[6];

        //以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK
        fd = open("/dev/my2440_buttons", 0);

        if(fd < 0)
        {
                printf("Open Buttons Device Faild!\n");
                exit(1);
        }

        while(1)
        {
                int i;
                int ret;
                fd_set rds;
               
                FD_ZERO(&rds);
                FD_SET(fd, &rds);
               
                //应用程序进行轮询,查询是否可对设备进行访问
                ret = select(fd + 1, &rds, NULL, NULL, NULL);
               
                if(ret < 0)
                {
                        printf("Read Buttons Device Faild!\n");
                        exit(1);
                }
               
                if(ret == 0)
                {
                        printf("Read Buttons Device Timeout!\n");
                }
                else if(FD_ISSET(fd, &rds))
                {
                        //读设备
                        ret = read(fd, key_status, sizeof(key_status));

                        if(ret != sizeof(key_status))
                        {
                                if(errno != EAGAIN)
                                {
                                        printf("Read Button Device Faild!\n");
                                }

                                continue;
                        }
                        else
                        {
                                for(i = 0; i < 6; i++)
                                {
                                        //对应驱动中按键的状态,为0即按键被按下
                                        if(key_status == 0)
                                        {
                                                printf("Key%d DOWN\n", i + 1);
                                        }
                                }
                        }
                }
        }

        close(fd);

        return 0;
}

出0入0汤圆

发表于 2009-11-20 08:52:43 | 显示全部楼层
学习一下。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 19:44

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表