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; }
相关文章推荐
- mini2440按键驱动及详细解释(转)
- 转:一个相当详细的MINI2440按键驱动详解
- 一个相当详细的MINI2440按键驱动详解
- 一个相当详细的MINI2440按键驱动详解
- 一个相当详细的MINI2440按键驱动详解
- 转:一个相当详细的MINI2440按键驱动详解
- LINUX2.6设备驱动模型详细解释
- mini2440 KEY按键设备驱动(中断模式和查询模式)源代码--(宋宝华框架)
- 把驱动编译进内核的3种方法(mini2440 key按键为例)
- MINI2440 按键输入子系统 驱动及测试代码分析
- Linux-2.6.32.2内核在mini2440上的移植(十六)---基于中断的按键驱动移植
- mini2440按键驱动分析
- mini2440 按键控制LED 驱动
- mini2440 按键驱动异步信号通知模式 实验
- 中断驱动设计----mini2440 按键驱动设计之路
- Linux-2.6.32.2内核在mini2440上的移植(十六)---基于中断的按键驱动移植
- mini2440 按键驱动添加定时器消抖动
- MINI2440按键驱动详解
- mini2440按键驱动分析(转载)
- 关于mini2440触摸屏驱动中信号量作用的最新解释