您的位置:首页 > 其它

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

2013-12-19 20:34 288 查看



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][/b]:[/b]

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

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

unsigned char
tbuf[MAX_KEY_BUF][/b]:[/b]

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

unsigned int head,tail;

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



[align=center] [/align]

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

Wq[/b]:[/b]

等待队列,当应用程序的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>

/

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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: