当前位置:首页 >> 信息与通信 >>

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; 指向缓冲区里面数据的头尾,比如:

当 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 #define MAX_KEY_BUF

6 16 //for circle link

#define MINI2440_KEY_MAJOR #define KEY_BUF_CLR #define KEY_DELAY_20MS

250 0x01 (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; cnt<KEY_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

// type */

#define IRQ_TYPE_EDGE_FALLING 0x00000002 /* Edge falling

//

#define IRQ_TYPE_EDGE_BOTH

(IRQ_TYPE_EDGE_FALLING

| IRQ_TYPE_EDGE_RISING)

// type */

#define IRQ_TYPE_LEVEL_HIGH

0x00000004 /* Level high

// */

#define IRQ_TYPE_LEVEL_LOW

0x00000008 /* Level low type

// above */

#define IRQ_TYPE_SENSE_MASK

0x0000000f /* Mask of the

// */

#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[i].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].g pio_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; }


相关文章:
嵌入式Linux之我行 mini2440按键驱动
2. 开始编写合适 mini2440 的按键驱动(含去抖动功能),文件名:my2440_buttons.c 1)按键驱动基本框架。这里我就指定主设备号为 232,简单的注册为字符设备,另定义...
按键驱动程序解析
linux 驱动程序 15:46) 先说明一下按键与 S3C2440芯片的连接方式: KEY1 <-...驱动程序源码如下: (drivers/char/mini2440_buttons.c) 1 2 3 4 5 6 7 ...
2440键盘驱动分析笔记
笔记本触摸板和键盘驱动j解... 1页 免费 mini2440按键驱动及详细解... 21页 免费 mini2440按键驱动实例详解 11页 5财富值 基于mini2440的linux驱动程... 6页...
ARM开发板mini2440的按键控制LED小程序
mini2440, s3c2440, 嵌入式, 开发板, 按键, 驱动 mini2440 是当前最经济的 ...我写的测试的程序可在这里下载,很简单,有很清楚的注释说明,就不在这里解释了...
mini2440button驱动程序分析
mini2440 Linux 按键驱动... 11页 2财富值如要投诉违规内容,请到百度文库投诉中心;如要提出功能问题或意见建议,请点击此处进行反馈。 ...
实验六、键盘驱动实践
Mini2440驱动第三站:ADC 12页 免费 单片机C语言键盘驱动 6页 免费如要投诉违规...将中断引脚 KB_INT1 配置为输入,并读取该引脚的值,如果为低 电平,说明有键...
按键控制LED驱动
/*mini2440_buttons_my.c*/ /*后面加了_my*/ /*按键驱动程序*/ /*mini2440 所用到的按键资源*/ /***/ /* 按键 对应的 IO 寄存器 对应的中断引脚*/...
mini2440X35驱动
mini2440X35驱动_计算机软件及应用_IT/计算机_专业资料。#include <linux/module...(gpcup); /* 设置假的调色板 , iounmap(gpdcon); iounmap(gpdup); } ...
在mini2440上面搞定CC2500驱动
mini2440按键驱动及详细... 21页 免费 mini2440触摸屏驱动分析 10页 免费 Mini...但是,文章中不可能就每一个涉及到的概念都大加讲解。 因此,这篇文章还是需要...
实验一 mini2440裸板驱动开发环境搭建
实验一 mini2440裸板驱动开发环境搭建_计算机软件及应用_IT/计算机_专业资料。...当 minicom 窗口出现后,将 mini2440 开发板的启动方式选择按键拨到 nor flash ...
更多相关标签: