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