免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1910 | 回复: 0
打印 上一主题 下一主题

mini2440按键驱动及详细解释 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-25 21:59 |只看该作者 |倒序浏览
经过为期一周左右的时间,参考《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;
指向缓冲区里面数据的头尾,比如:


当Read函数来读数据的时候就判断Head和Tail是不是指向同一个地方,如果指向同一个地方就表示无数据可读,反之,则把数据读出。
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、  程序框架图,左边是框图,右边相应的部分实现过程。
本程序所有按键都是采用中断低电平触发方式。



第二部分:按键事件处理
主要负责按键记录以及与应用层沟通,当应用层调用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>
//*******************************
//some macro define
//*******************************
#define KEY_NUM                    6
#define MAX_KEY_BUF                16    //for circle link
#define MINI2440_KEY_MAJOR     250
#define KEY_BUF_CLR                0x01
#define KEY_DELAY_20MS            (HZ/50) //delay 20ms
#define KEY_DELAY_100MS            (HZ/10) //delay 100ms
//buf循环链表:让x在mod进制下循环
//要求(mod-1)转换为二进制后必须是所有的"1"是连续在低位放置
//比如:mod=8; mod-1=7; 7 = 0x0000 0111(B)
//而不能是比如mod=9; mod-1=8; 8 = 0x0000 1000(B)
#define INC_BUF_POINTOR(x,mod)    ((++(x))&((mod)-1))
//判断Key所在端口的状态
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(g_tkey_info[key].gpio_port) == 0)
//*******************************
//key status define
//*******************************
#define KEYSTATUS_UP            0 //none action
#define KEYSTATUS_DOWN            1 //press key
#define KEYSTATUS_X                2 //unsure state
static int g_key_major = MINI2440_KEY_MAJOR;
struct KEY_DEV
{
    unsigned int tkeystatus[KEY_NUM];     //6个按键的状态
    unsigned char tbuf[MAX_KEY_BUF];     //按键缓冲区
    unsigned int head,tail;                //按键缓冲区头和尾
    wait_queue_head_t wq;        //等待队列
    struct cdev cdev;
};
struct KEY_DEV *g_pkey_dev;
static struct timer_list g_tkey_timer[KEY_NUM]; //6个按键去抖计时器
struct KEY_INFO
{
    int irq_no;        //中断号
    unsigned int gpio_port;        //GPOI端口
    int key_no;
};
struct KEY_INFO g_tkey_info[KEY_NUM] =
{
    //定义按键所使用的资源
    { IRQ_EINT8, S3C2410_GPG0, 0 }, //NO. 1 2 3 ... 与电路图匹配
    { IRQ_EINT11, S3C2410_GPG3, 1 },
   
    { IRQ_EINT13, S3C2410_GPG5, 2 },
    { IRQ_EINT15, S3C2410_GPG7, 3 },
    { IRQ_EINT14, S3C2410_GPG6, 4 },
    { IRQ_EINT19, S3C2410_GPG11, 5 },
};
static irqreturn_t key_eint_handler(int irq, void *dev_id)
{
    int cnt,key_index;
    key_index = 0;
    for(cnt=0; cntKEY_NUM; cnt++)
    {
        if(g_tkey_info[cnt].irq_no == irq)
        {
            key_index = g_tkey_info[cnt].key_no;
            break;
        }
    }
//    printk(KERN_NOTICE "Eint %d\n",key_index);   
    disable_irq(g_tkey_info[key_index].irq_no); //disable irq
    g_pkey_dev->tkeystatus[key_index] = KEYSTATUS_X; //set key in unsure state
    g_tkey_timer[key_index].expires = jiffies + KEY_DELAY_20MS; //set timer value
    add_timer(&g_tkey_timer[key_index]); //start timer
    return IRQ_HANDLED;
}
static int request_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;
//        set_external_irq(key_info->irq_no, EXT_LOWLEVEL, GPIO_PULLUP_DIS); //set INT low voltage level target
//        if(request_irq(key_info->irq_no, , SA_INTERRUPT, "Mini2440_Key", i))
//        /include/linux/irq.h        
//        #define    RQ_TYPE_NONE         0x00000000    /* Default, unspecified type */
//        #define IRQ_TYPE_EDGE_RISING 0x00000001 /* Edge rising type */
//        #define IRQ_TYPE_EDGE_FALLING 0x00000002 /* Edge falling type */
//        #define IRQ_TYPE_EDGE_BOTH         (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
//        #define IRQ_TYPE_LEVEL_HIGH     0x00000004 /* Level high type */
//        #define IRQ_TYPE_LEVEL_LOW     0x00000008 /* Level low type */
//        #define IRQ_TYPE_SENSE_MASK     0x0000000f /* Mask of the above */
//        #define IRQ_TYPE_PROBE     0x00000010 /* Probing in progress */
//        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.irq_no, IRQ_TYPE_LEVEL_LOW);
/* /include/linux/interrupt.h
* flags used only by the kernel as part of the irq handling routines.
* IRQF_DISABLED - keep irqs disabled when calling the action handler
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for
* performance reasons)
*
* #define IRQF_DISABLED 0x00000020
* #define IRQF_SAMPLE_RANDOM 0x00000040
* #define IRQF_SHARED          0x00000080
* #define IRQF_PROBE_SHARED 0x00000100
* #define IRQF_TIMER      0x00000200
* #define IRQF_PERCPU 0x00000400
* #define IRQF_NOBALANCING 0x00000800
* #define IRQF_IRQPOLL 0x00001000
*/
        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; iKEY_NUM; i++)
    {
        g_pkey_dev->tkeystatus = KEYSTATUS_UP;
    }
    init_waitqueue_head(&(g_pkey_dev->wq)); //initial wait queue
    for(i=0; iKEY_NUM; i++)
    {
    //    setup_timer(&g_tkey_timer, key_timer_handler,i);
        g_tkey_timer.function = key_timer_handler;
        g_tkey_timer.data = i;
        init_timer(&g_tkey_timer);
    }
    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;
}


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/102839/showart_2060243.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP