基于platform驱动模型-mini2440按键驱动(完美支持防抖动)

时间:2022-04-20 23:31:13

一、开发环境

  • 主  机:ubuntu10.04
  • 开发板:Mini2440--256MB Nand, Kernel:2.6.32.2
  • 编译器:arm-linux-gcc-4.3.2

二、实现步骤

1. 硬件原理图分析。由原理图可知每个按键所用到的外部中断分别是EINT8、EINT11、EINT13、EINT14、EINT15、EINT19,所对应的IO口分别是GPG0、GPG3、GPG5、GPG6、GPG7、GPG11。再由按键的接口电路可知,当按键按下时按键接通,中断线上原有的VDD3.3V高电平被拉低,从而触发中断的产生。

基于platform驱动模型-mini2440按键驱动(完美支持防抖动)
 
2. 开始编写合适mini2440的按键驱动(含去抖动功能),文件名:platButtons.c

首先进行平台设备的定义和注册
//平台资源的定义
static struct resource gitS3cButtonsResource[] = {
    [0]={
            .start = BUZZER_GPG_ADDR,    //GPG管教的数据寄存器,具体值定义在头文件中
            .end   = BUZZER_GPG_ADDR + 4,
            .flags = IORESOURCE_MEM,
    },
    [1]={
            .start = IRQ_EINT8,
            .end   = IRQ_EINT8,
            .flags = IORESOURCE_IRQ,
    },
    [2]={
            .start = IRQ_EINT11,
            .end   = IRQ_EINT11,
            .flags = IORESOURCE_IRQ,
    },
    [3]={
            .start = IRQ_EINT13,
            .end   = IRQ_EINT13,
            .flags = IORESOURCE_IRQ,
     },
    [4]={
            .start = IRQ_EINT14,
            .end   = IRQ_EINT14,
            .flags = IORESOURCE_IRQ,
    },
    [5]={
            .start = IRQ_EINT15,
            .end   = IRQ_EINT15,
            .flags = IORESOURCE_IRQ,
    },
    [6]={
            .start = IRQ_EINT19,
            .end   = IRQ_EINT19,
            .flags = IORESOURCE_IRQ,
    }
};

//平台设备定义
static struct platform_device gitButtonDev = {
    .name = PLAT_DEV_NAME,
    .id = -1,
    .num_resources = ARRAY_SIZE(gitS3cButtonsResource),
    .resource = gitS3cButtonsResource,
    .dev = {
        .release = ButtonRelease,
    },
};

//平台驱动定义
static struct platform_driver gitButtonDriver = {
    .probe = ButtonsProbe,
    .remove = ButtonsRemove,
    .driver = {
        .name = PLAT_DEV_NAME,
        .owner = THIS_MODULE,
    },
};

static int __init ButtonsInit(void)
{
    DPRINTK(banner);
    platform_device_register(&gitButtonDev);
    platform_driver_register(&gitButtonDriver);
    return 0;
}

static void __exit ButtonsExit(void)
{
    platform_driver_unregister(&gitButtonDriver);
    platform_device_unregister(&gitButtonDev);
}

module_init(ButtonsInit);
module_exit(ButtonsExit);

MODULE_AUTHOR("apple_guet");
MODULE_DESCRIPTION("Mini2440 Buttons Driver");
MODULE_LICENSE("GPL");

static int ButtonsRemove(struct platform_device *dev)
{
    iounmap(gipGpgIoDateAdd);
    misc_deregister(&gitButtonMiscDev);
    return 0;
}

//  设备探测函数
static int ButtonsProbe(struct platform_device *pdev)
{
    struct resource *res;
    int i = 0, ret = 0;
    DPRINTK("probe:%s\n", __func__);
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if(res == NULL)
    {
        return -ENOENT;
    }
    gipGpgIoDateAdd = ioremap(res->start, (res->end - res->start));
    if(gipGpgIoDateAdd == NULL)
    {
        return -EINVAL;
    }
    DPRINTK(KERN_DEBUG"probe: mapped gipGpgIoDateAdd = %p\n", gipGpgIoDateAdd);
    //get irq number
    for(i = 0; i < 6; i++)
    {
        gitpButtonsIrq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        if(gitpButtonsIrq == NULL)
        {
            ret = -ENOENT;
            goto err_map;
        }
        giaButtonIrqs[i] = gitpButtonsIrq->start;
        DPRINTK("giaButtonIrqs[%d]=%d\n", i, giaButtonIrqs[i]);  
    }
    ret = misc_register(&gitButtonMiscDev);
    return 0;
err_map:
    iounmap(gipGpgIoDateAdd);
    return ret;
}

static void ButtonRelease(struct device * dev)
{
    /* do nothing */
}

static int ButtonsOpen(struct inode *inode, struct file *file)
{
    int err = 0;
    uint8_t i = 0;
    //注册中断
    for(i = 0; i < KEY_COUNT; i++)
    {
        if(giaButtonIrqs[i] < 0)
        {
            continue;
        }
        //中断触发方式:上升沿触发
        err = request_irq(giaButtonIrqs[i], PlatButtonsIrq, IRQ_TYPE_EDGE_RISING, NULL, NULL);
        if(err != 0)
        {
            break;
        }
        //初始化并设置6个去抖定时器
        setup_timer(&gitKeyTimers[i], ButtonsTimer, i);
    }
    if(err != 0)
    {
        i--;
        for(; i >= 0; i--) 
        {
            if(giaButtonIrqs[i] < 0) 
            {
                continue;
            }
            disable_irq(giaButtonIrqs[i]);
            free_irq(giaButtonIrqs[i], NULL);
        }
        return -EBUSY;
    }
    giButtonStat = 0;   
    return 0;
}

static int ButtonsClose(struct inode *inode, struct file *file)
{
    uint8_t i = 0;    
    for(i = 0; i < KEY_COUNT; i++)
    {
        del_timer(&gitKeyTimers[i]);
        if(giaButtonIrqs[i] < 0)
        {
            continue;
        }
        free_irq(giaButtonIrqs[i], NULL);
    }
    return 0;
}

static int ButtonsRead(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
    unsigned long ret = 0;
    if(giButtonStat == 0) 
    {
        if((filp->f_flags & O_NONBLOCK) != 0)
        {
            return -EAGAIN;
        }
        else
        {
            wait_event_interruptible(gipButtonWaitQue, giButtonStat);
        }
    }    
    giButtonStat = 0;
    ret = copy_to_user(buff, &giKeyValue, min(sizeof(giKeyValue), count));
    return (ret ? -EFAULT : min(sizeof(giKeyValue), count));
}

static unsigned int ButtonsPoll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &gipButtonWaitQue, wait);
    if(giButtonStat == 1)
    {
        mask |= POLLIN | POLLRDNORM;
    }
    return mask;
}

static struct file_operations gitButtonsFops = {
    .owner   = THIS_MODULE,
    .open    = ButtonsOpen,
    .release = ButtonsClose,
    .read    = ButtonsRead,
    .poll    = ButtonsPoll,
};
static struct miscdevice gitButtonMiscDev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &gitButtonsFops,
};

接下来是比较重要的两个函数:

1、按键触发函数

    在此函数中,增加了一个 giInIrqFlg 变量,在调试中 发现,如果没有这个变量,在我们按键过程中,会有抖动,即有可能多次进入此中断函数,那么每进一次此函数,都会设置一次定时器中断,造成程序执行时,报错定时器timer.c kernel panic,加入此变量后,问题解决。

static irqreturn_t PlatButtonsIrq(int irq, void *dev_id)
{
    uint8_t i = 0;
    if(giInIrqFlg == 0)
    {
        for(i = 0; i < KEY_COUNT; i++)
        {
            if(irq == (int)giaButtonIrqs[i])
            {
                giInIrqFlg = 1;
                //设置当前按键按下去抖定时器的延时并启动定时器
                gitKeyTimers[i].expires = jiffies + KEY_TIMER_DELAY;
                DPRINTK("in platButtonsIrq i = %d\n", i);  
                add_timer(&gitKeyTimers[i]);
            }
        }
    }/* end if(giInIrqFlg == 0) */
    return IRQ_RETVAL(IRQ_HANDLED);
}

static void ButtonsTimer(unsigned long arg)
{
     int key = arg, iDate = *gipGpgIoDateAdd;
     giInIrqFlg = 0;
     DPRINTK(KERN_INFO "this is in ButtonsTimer, key = %d\n", key);
    if((iDate & KEY_PRESS_FLG) != 0)
    {
        //低电平,按键按下
        giKeyValue = key;
        giButtonStat = 1;    
        wake_up_interruptible(&gipButtonWaitQue);
    }/* end if(iIoStat == 0) */
}

全局变量定义如下:

static volatile int giKeyValue = 0;
static struct resource *gitpButtonsIrq;
static volatile uint8_t giInIrqFlg = 0;
static volatile uint8_t giButtonStat = 0;
static struct timer_list gitKeyTimers[KEY_COUNT];   //定义6个按键去抖动定时器
static DECLARE_WAIT_QUEUE_HEAD(gipButtonWaitQue);
static volatile unsigned long *gipGpgIoDateAdd = NULL;
static volatile uint8_t giaButtonIrqs[KEY_COUNT] = {0};
const static char banner[] __initdata = "Mini2440 Buttons Driver\n";


platButtons.h头文件内容为:

#ifndef _PLAT_BUTTOBS_H
#define _PLAT_BUTTOBS_H


#define KEY_COUNT 0x06
#define KEY_TIMER_DELAY (HZ/50) //按键按下去抖延时20毫秒 
#define DEVICE_NAME "HDW_BUTTONS"
#define PLAT_DEV_NAME "hdw-buttons"

#define BUZZER_GPG_ADDR 0x56000064
#define KEY1_POS (0x0001 << 0)
#define KEY3_POS (0x0001 << 3)
#define KEY5_POS (0x0001 << 5)
#define KEY6_POS (0x0001 << 6)
#define KEY7_POS (0x0001 << 7)
#define KEY11_POS (0x0001 << 11)
#define KEY_PRESS_FLG (KEY1_POS | KEY3_POS | KEY5_POS| KEY6_POS | KEY7_POS | KEY11_POS)

#undef DEBUG
#define _DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK(x...) (void)(0)
#endif

#endif


测试程序:

int main(void)
{
    fd_set rds;
    unsigned int idKeyValue = 0;
    int idButtonsFd = 0, ret = 0;
    idButtonsFd = open("/dev/HDW_BUTTONS", 0);
    if(idButtonsFd < 0) 
    {
        perror("open device buttons");
        exit(1);
    }
    while(1) 
    {
        FD_ZERO(&rds);
        FD_SET(idButtonsFd, &rds);
        ret = select(idButtonsFd + 1, &rds, NULL, NULL, NULL);
        if(ret < 0)
        {
            perror("select");
            exit(1);
        }
        else if(ret == 0)
        {
            printf("Timeout.\n");
        } 
        if(FD_ISSET(idButtonsFd, &rds) != 0) 
        {
            ret = read(idButtonsFd, &idKeyValue, sizeof(idKeyValue));
            if(ret != sizeof(idKeyValue))
            {
                if(errno != EAGAIN)
                {
                    perror("errno != EAGAIN, read buttons\n");
                }
                continue;
            }
            else
            {
                printf("buttons_value: %d\n", idKeyValue + 1);
            }
        }/* end if(FD_ISSET(idButtonsFd, &rds) != 0) */
    }/* end while(1) */
    close(idButtonsFd);
    return 0;
}
经过测试,发现完全可以去除抖动,按键过程中稳定。