6.驱动支持多按键操作

多按键驱动程序优化

优化的代码,支持两个按键的支持的代码:duokey.c:

#include <linux/module.h>        /* For module specific items */

#include <linux/fs.h>            /* For file operations */

#include <linux/ioport.h>        /* For io-port access */

#include <linux/io.h>            /* For inb/outb/... */

#include <linux/init.h>

#include <linux/miscdevice.h>

#include <linux/interrupt.h>

#include <linux/slab.h>

#define GPNCON 0x7f008830

#define GPNDAT 0x7f008834

 

 

struct work_struct *work1;

struct timer_list key_timer;//定义定时器

unsigned int *gpio_data;

 

void work1_func(struct work_struct *work)

{

    //启动定时器 100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间

    mod_timer(&key_timer,jiffies+HZ/10);

}

void key_timer_func(unsigned long data)

{    //定时器超时的函数需要修改,需要判断是哪个按键超时

    

    unsigned int key_val;

    //超时的时候,就要读取data

    key_val=readw(gpio_data)&0x01;//读出一个按键EINT0的值。

    //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

    if(0==key_val)//真正按下

        printk("<0> key1 down!\n");

 

    key_val=readw(gpio_data)&0x02;//读出一个按键EINT1的值。

    //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

    if(0==key_val)//真正按下

        printk("<0> key2 down!\n");

}

 

irqreturn_t key_int(int irq, void *dev_id)

{

    //1.检测是否发生了按键中断

    //2.清除已经发生的按键中断

//前面的都是硬件相关的工作,必须在中断里面执行

//下面是硬件无关的工作,我们把它提到中断以外的work1_func函数去处理。

    //3.打印按键值

    schedule_work(work1);

    

    return 0;

}

 

void key_hw_init()

{

    unsigned int *gpio_config;

    unsigned short data;

    gpio_config = ioremap(GPNCON,4);

    data = readw(gpio_config);

    data &= ~0b1111;//增加一个按键

    data |= 0b1010;

    writew(data,gpio_config);

 

    gpio_data = ioremap(GPNDAT,4);

}

int key_open(struct inode *node, struct file *filp)

{

    return 0;

}

struct file_operations key_fops =

{

    .open = key_open,

};

struct miscdevice key_miscdevice =

{

    .minor = 200,

    .name = "OK6410key",

    .fops = &key_fops,

};

 

static int key_init()

{

    misc_register(&key_miscdevice);

    //注册中断处理程序

    request_irq(IRQ_EINT(0),key_int, IRQF_TRIGGER_FALLING,"OK6410key",0);

    //增加一个按键的支持

    request_irq(IRQ_EINT(1),key_int, IRQF_TRIGGER_FALLING,"OK6410key",0);

    

    //硬件初始化

    key_hw_init();//相应的位进行设置

 

    //2. 创建工作

    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

    INIT_WORK(work1, work1_func);

    //定时器初始化

    init_timer(&key_timer);

    key_timer.function=key_timer_func;

    

    //注册定时器

    add_timer(&key_timer);

    return 0;

}

 

static void key_exit()

{

    misc_deregister(&key_miscdevice);

    

}

 

module_init(key_init);

module_exit(key_exit);

 

/*优化:多一个中断,gpio也进行多按键初始化,中断产生的时候要判断是哪个按键产生的中断。*/

Make的结果:

生成.ko驱动文件,拷贝到板子运行。按下1,2号键会输出相应的信息。

首先是创建相应的字符设备文件。

mknod /dev/6410key c 10 200

 

 

 

 

 

 

 

 

 

按键应用程序设计

应用程序的代码key_app.c:主要的功能是读出按下键的值:

#include <stdio.h>

#include <stdlib.h>

int main()

{

int fd;

int key_num;

//1.打开设备

fd=open("/dev/6410key",0);

if(fd<0)

printf("open device fail!\n");

//2.读取设备

read(fd,&key_num,4);

printf("key is %d\n",key_num);

//3.关闭设备

close(fd);

return 0;

}

编译:arm-linux-gcc -static key_app.c -o key_app

这就是我们的按键测试应用程序。

其实现在还不能进行测试我们之前的驱动程序:因为我们之前的file_operations 结构里只有open函数。并不支持read。

struct file_operations key_ops =

{

    .open = key_open,

    

};

这些函数都不用记学会去内核代码找原型。

编写完,make,出错:implicit declaration of function 'copy_to_user'没有他的头文件,头文件是linux/eaccess.h.最后make成功:

改进后的duokey.c:

#include <linux/module.h>        /* For module specific items */

#include <linux/fs.h>            /* For file operations */

#include <linux/ioport.h>        /* For io-port access */

#include <linux/io.h>            /* For inb/outb/... */

#include <linux/init.h>

#include <linux/miscdevice.h>

#include <linux/interrupt.h>

#include <linux/slab.h>

#include <linux/uaccess.h>

 

#define GPNCON 0x7f008830

#define GPNDAT 0x7f008834

 

 

struct work_struct *work1;

struct timer_list key_timer;//定义定时器

unsigned int *gpio_data;

 

//全局变量

unsigned int key_num;

//read按键

ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)

{

    //返回内核的给用户

    copy_to_user(buf,&key_num,4);

    return 4;

}

 

void work1_func(struct work_struct *work)

{

    //启动定时器 100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间

    mod_timer(&key_timer,jiffies+HZ/10);

}

void key_timer_func(unsigned long data)

{    //定时器超时的函数需要修改,需要判断是哪个按键超时

    

    unsigned int key_val;

    //超时的时候,就要读取data

    key_val=readw(gpio_data)&0x01;//读出一个按键EINT0的值。

    //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

    if(0==key_val)//真正按下

        key_num=1;//读取按键编号

 

    key_val=readw(gpio_data)&0x02;//读出一个按键EINT1的值。

    //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

    if(0==key_val)//真正按下

        key_num=2;

}

 

irqreturn_t key_int(int irq, void *dev_id)

{

    //1.检测是否发生了按键中断

    //2.清除已经发生的按键中断

//前面的都是硬件相关的工作,必须在中断里面执行

//下面是硬件无关的工作,我们把它提到中断以外的work1_func函数去处理。

    //3.打印按键值

    schedule_work(work1);

    

    return 0;

}

 

void key_hw_init()

{

    unsigned int *gpio_config;

    unsigned short data;

    gpio_config = ioremap(GPNCON,4);

    data = readw(gpio_config);

    data &= ~0b1111;//增加一个按键

    data |= 0b1010;

    writew(data,gpio_config);

 

    gpio_data = ioremap(GPNDAT,4);

}

int key_open(struct inode *node, struct file *filp)

{

    return 0;

}

struct file_operations key_fops =

{

    .open = key_open,

    .read = key_read,//增加了读取操作

};

struct miscdevice key_miscdevice =

{

    .minor = 200,

    .name = "6410key",

    .fops = &key_fops,

};

 

static int key_init()

{

    misc_register(&key_miscdevice);

    //注册中断处理程序

    request_irq(IRQ_EINT(0),key_int, IRQF_TRIGGER_FALLING,"6410key",0);

    //增加一个按键的支持

    request_irq(IRQ_EINT(1),key_int, IRQF_TRIGGER_FALLING,"6410key",0);

    

    //硬件初始化

    key_hw_init();//相应的位进行设置

 

    //2. 创建工作

    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

    INIT_WORK(work1, work1_func);

    //定时器初始化

    init_timer(&key_timer);

    key_timer.function=key_timer_func;

    

    //注册定时器

    add_timer(&key_timer);

    return 0;

}

 

static void key_exit()

{

    misc_deregister(&key_miscdevice);

    

}

 

module_init(key_init);

module_exit(key_exit);

 

/*优化:多一个中断,gpio也进行多按键初始化,中断产生的时候要判断是哪个按键产生的中断。*/

接下来就是开发板的测试:首先还是把生成的按键驱动文件拷贝到开发板:安装。

接着是创建字符设备文件:

mknod /dev/6410key c 10 200

6410key是我们用到的设备文件,c是字符设备文件的标志,10是混杂设备的固定的主设备号,200是我们在字符驱动里定义的次设备号。

创建好了之后,运行应用程序key_app:

[root@FORLINX6410]# ./key_app

key is 0

posted @ 2016-02-14 07:19  cestlavie  阅读(284)  评论(0编辑  收藏  举报