mini2440按键驱动及详细解释(转)

mini2440按键驱动及详细解释(转)

经过为期一周左右的时间,参考《linux设备驱动开发详解》把mini2440开发板的按键驱动完成了。
 

程序可以分成两部分来看:

第一部分:按键侦测,主要包括中断的初始化、中断处理、按键去抖、等待按键松开,侦测

完成返回按键键值。

第二部分:按键事件处理,主要包括,将案件事件保存至循环链表(其实就是一个数组,可

以循环保存数据)、将案件事件反馈给应用程序(Read函数)。

 

其中涉及到的数据结构有以下几个:

第一个数据结构:设备驱动结构

struct KEY_DEV

{

       unsigned int tkeystatus[KEY_NUM];    //6个按键的状态,每个key对应一个位置

       unsigned char tbuf[MAX_KEY_BUF]; //按键缓冲区,保存按键事件,Read函数从这读取

       unsigned int head,tail;                          //按键缓冲区头和尾,指向缓冲区数据的头和尾

       wait_queue_head_t wq;                 //等待队列,当无数据可读时read函数将挂在这

       struct cdev cdev;                               //没什么好解释啦,^_^

};

所以这个结构体里面各个变量分工是这样的:

unsigned int tkeystatus[KEY_NUM]

当有中断触发的时候,先在tkeystatus[KEY_NUM]这里的对应位置记为

“待定(KEYSTATUS_X)”,当延时20ms后如果通过读IO口的方式发现该端口仍然处于低电平,那么就认为确实有按下这个按键,那么在tkeystatus[KEY_NUM]这里的对应位置记为“按下(KEYSTATUS_DOWN)”

unsigned char tbuf[MAX_KEY_BUF]

按键缓冲区,当确认某一个按键被按下后,将按键编号(看你喜欢怎么标识某一个按键啦,随便定义,只要能区分就行了)记录在这里。

unsigned int head,tail;

指向缓冲区里面数据的头尾,比如:

mini2440按键驱动及详细解释(转) - 地狱伏龙 - 卧龙居

 

 

Read函数来读数据的时候就判断HeadTail是不是指向同一个地方,如果指向同一个地方就表示无数据可读,反之,则把数据读出。

Wq

等待队列,当应用程序的Read采用阻塞方式读取的时候,如果当前没按键按下,那么就不能让Read函数返回,所以就用一个等待队列把Read函数挂起来(Read函数),当有数据可读的时候再把队列上的Read函数唤醒(keyEvent函数)。

 

第二个数据结构:

static struct timer_list g_tkey_timer[KEY_NUM]; //6个按键去抖计时器

主要用于计时函数,比如去抖、等待按键松开等。

 

第三个数据结构:

struct KEY_INFO

{

       int  irq_no;                         //中断号

       unsigned int gpio_port;          //GPOI端口

       int  key_no;                        //自己安排的按键号

};

很简单明了的结构体,一看就知道啦,所以不多说咯!

 

下面开始讲程序架构

第一部分:按键侦测。

1、  程序框架图,左边是框图,右边相应的部分实现过程。

本程序所有按键都是采用中断低电平触发方式。

 

mini2440按键驱动及详细解释(转) - 地狱伏龙 - 卧龙居

 

 

第二部分:按键事件处理

主要负责按键记录以及与应用层沟通,当应用层调用Read函数的时候,如果在缓冲区有数据则马上反馈,如果无数据则判断应用层是否用阻塞方式读取,如果阻塞方式读取则将Read函数挂起,否则返回。

涉及函数一:keyEvent()记录按键事件到缓冲区。

涉及函数二:key_read()很明白啦…^_^

 

完整驱动代码:

 

//

//                     书写规范                 //

//结构体定义:一律大写字母,中间可用"_"区分     //

//全局变量 :全部用小写字母,加前缀"g_"         //

//局部变量 :全部用小写字母组合,无其他前后缀 //

//指针变量    :在变量前加"p",优先级比"g_"低     //

//数组        :在变量前加"t",优先级比"g_"低     //

//自制函数    :自制函数名字都以"key_"作为前缀     //

//


#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>

/

//        #define IRQ_TYPE_EDGE_RISING 0x00000001

//        #define IRQ_TYPE_EDGE_FALLING 0x00000002

//        #define IRQ_TYPE_EDGE_BOTH         (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)

//        #define IRQ_TYPE_LEVEL_HIGH     0x00000004

//        #define IRQ_TYPE_LEVEL_LOW     0x00000008

//        #define IRQ_TYPE_SENSE_MASK     0x0000000f

//        #define IRQ_TYPE_PROBE     0x00000010

//        set_external_irq(key_info->irq_no, IRQ_TYPE_LEVEL_LOW, GPIO_PULLUP_DIS); //set INT low voltage level target


//        int set_irq_type (unsigned int irq, unsigned int type);

        set_irq_type(g_tkey_info[i].irq_no, IRQ_TYPE_LEVEL_LOW);


        if(request_irq(key_info->irq_no, key_eint_handler, IRQF_DISABLED,"Mini2440_Key", &i))
        {
            return -1;
        }
    }
    return 0;
}

void free_irqs(void)
{
    struct KEY_INFO *key_info;
    int i;
    for(i=0; i<(sizeof(g_tkey_info)/sizeof(g_tkey_info[1])); i++)
    {
        key_info = g_tkey_info + i;
        free_irq(key_info->irq_no, &i);
    }
}

static void keyEvent(int key_index)
{
    g_pkey_dev->tbuf[g_pkey_dev->head] = key_index;
    g_pkey_dev->head = INC_BUF_POINTOR(g_pkey_dev->head,MAX_KEY_BUF);
    wake_up_interruptible(&g_pkey_dev->wq);
}

static void key_timer_handler(unsigned long data)
{
    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_pkey_dev->tkeystatus[key_index] == KEYSTATUS_X)
        {
            g_pkey_dev->tkeystatus[key_index] = KEYSTATUS_DOWN; //change key state

            g_tkey_timer[key_index].expires = jiffies + KEY_DELAY_100MS; //re_initial timer

            
            keyEvent(key_index);

            add_timer(&g_tkey_timer[key_index]); //restart timer

        }
        else //wait for user release the key

        {
            g_tkey_timer[key_index].expires = jiffies + KEY_DELAY_100MS;
            add_timer(&g_tkey_timer[key_index]);
        }
    }
    else //user have released the key

    {
        g_pkey_dev->tkeystatus[key_index] = KEYSTATUS_UP;
        //del_timer(&g_tkey_timer[key_index]);

        enable_irq(g_tkey_info[key_index].irq_no);
    }
}

static int key_open(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE "key opened\n");    
    g_pkey_dev->head = g_pkey_dev->tail = 0;
    return 0;
}

static int key_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t key_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
    unsigned int ret,temp;
    unsigned long flag;
    retry:
    if(g_pkey_dev->head != g_pkey_dev->tail)
    {
        local_irq_save(flag); //进入临界区,关闭中断

        ret = g_pkey_dev->tbuf[g_pkey_dev->tail]; //读取尾部指针所指内容

        g_pkey_dev->tail = INC_BUF_POINTOR(g_pkey_dev->tail, MAX_KEY_BUF);
        local_irq_restore(flag); //退出临界区

        //printk(KERN_NOTICE "driver key_read,key no:%d\n",ret);

        temp = copy_to_user(buf, &ret, sizeof(unsigned int));
        //printk(KERN_NOTICE "copy to user return %d\n", temp);

        return (sizeof(unsigned int));
    }
    else
    {
        //printk(KERN_NOTICE "A\n");

        if(filp->f_flags & O_NONBLOCK)
        {
            return -EAGAIN;
        }

        //printk("E:test %d\n",s3c2410_gpio_getpin(g_tkey_info[0].gpio_port));

        interruptible_sleep_on(&(g_pkey_dev->wq));
        
        goto retry;
    }
//    return 0;

}

static int key_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
{
    unsigned long flag;
    switch(cmd)
    {
        case KEY_BUF_CLR:
            local_irq_save(flag);
            g_pkey_dev->head = g_pkey_dev->tail = 0;
            local_irq_restore(flag);
            printk(KERN_NOTICE "key buf is clear\n");
            break;

        default:
            return - EINVAL;
    }
    return 0;
}

static struct file_operations g_tkey_fops = 
{
    .owner      = THIS_MODULE,
    .open = key_open,        //打开设备

    .release = key_release,        //关闭设备

    .read     = key_read,        //读取按键的键值

    .ioctl     = key_ioctl,        //清除缓冲区

};

static void key_setup_cdev(struct KEY_DEV *pdev, int index)
{
    //1. cdev init

    //2. cdev bind fops

    //3. cdev add

    int err, devno; 
    devno = MKDEV(g_key_major, index);

    cdev_init(&(g_pkey_dev->cdev), &g_tkey_fops);
    pdev->cdev.owner = THIS_MODULE;
    pdev->cdev.ops = &g_tkey_fops;

    err = cdev_add(&pdev->cdev, devno, 1);
    if(err)
    {
        printk(KERN_NOTICE "Error %d adding dev %d", err, index);
    }
}

static int mini2440_key_init(void)
{
    //**********************************

    //申请设备号,添加设备

    //**********************************

    int ret,i;
    dev_t devno = MKDEV(g_key_major, 0);

    if(g_key_major)
    {
        ret = register_chrdev_region(devno, 1, "Mini2440_Key");
    }
    else
    {
        ret = alloc_chrdev_region(&devno, 0, 1,"Mini2440_Key");
        g_key_major = MAJOR(devno);
    }

    if(ret < 0)
    {
        return ret;
    }

    g_pkey_dev = kmalloc(sizeof(struct KEY_DEV), GFP_KERNEL);
    if(!g_pkey_dev)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }
    memset(g_pkey_dev, 0, sizeof(struct KEY_DEV));

    key_setup_cdev(g_pkey_dev, 0);
    
    //**********************************

    //申请设备号,添加设备 完毕!

    //下面初始化其他内容

    //**********************************

    
    request_irqs(); //request all the key irq


    g_pkey_dev->head = g_pkey_dev->tail = 0; //initial key_dev

    
    for(i=0; i<KEY_NUM; i++)
    {
        g_pkey_dev->tkeystatus[i] = KEYSTATUS_UP;
    }

    init_waitqueue_head(&(g_pkey_dev->wq)); //initial wait queue


    for(i=0; i<KEY_NUM; i++)
    {
    //    setup_timer(&g_tkey_timer[i], key_timer_handler,i);

        g_tkey_timer[i].function = key_timer_handler;
        g_tkey_timer[i].data = i;
        init_timer(&g_tkey_timer[i]);
    }

    return 0;

    fail_malloc:unregister_chrdev_region(devno, 1);
    return ret;
}

static void key_exit(void)
{
    free_irqs(); //free irq

    cdev_del(&g_pkey_dev->cdev); //del cdev

    kfree(g_pkey_dev); //free memory

    g_pkey_dev = NULL;
    unregister_chrdev_region(MKDEV(g_key_major,0), 1);
}

MODULE_AUTHOR("Benson");
MODULE_LICENSE("Dual BSD/GPL");

module_param(g_key_major, int, S_IRUGO);
module_init(mini2440_key_init);
module_exit(key_exit);

完整测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int fd,ret,key_num;
    fd = open("/dev/Mini2440_Key", 0);
    if(fd < 0)
    {
        printf("open error!");
        return -1;
    }
    while(1)
    {
    //    printf("A\n");

        ret = read(fd, &key_num, sizeof(int));
        printf("you press the key %d\n", key_num);
    }
    close(fd);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值