3.按键驱动硬件操作
按键驱动的操作
通过上两节课的了解,接下来,我们通过例子来实现按键驱动的功能。
首先是按键的初始化,按键的初始化可以选择在open函数,和模块的初始化函数当中完成硬件的初始化。下面我们是选择在模块的初始化函数进行按键的初始化。按键的初始化,主要涉及对GPIO的引脚的功能进行相应的设置。在OK6410中有6个按键,今天我们选择第一个进行操作。
下面是buttons.c的代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#define GPNCON 0x7f008830
irqreturn_t key_int(int irq, void *dev_id)
{
//1.检测是否发生了按键中断
/*在这里可以不做,因为中断只有一个,不是共享中断*/
//2.清除已经发生的按键中断
/*这里的清除是硬件内部,而我们的中断是处理器级别的,可以不清除。比如用DM9000的网卡芯片需要清除,因为里面本身就有状态的寄存器,需要清除。*/
//3.打印按键值
printk("<0>key down!\n");
return 0;
}
void key_hw_init()
{
unsigned int *gpio_config;
unsigned short data;
gpio_config = ioremap(GPNCON,4);//物理地址转为虚拟地址,//用gpio变量来接受。
data = readw(gpio_config);//因为是16位的,所以用readw这个//函数来读出里面的值。
data &= ~0b11;//首先把最低两位设置为00
data |= 0b10;//最低的两位设置成10
writew(data,gpio_config);//把data的值写到寄存器里。这样,硬件的初始化就完成了。接下来就是按键中断的处理。首先是实现中断处理函数:irqreturn_t.然后注册中断处理程序。
}
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);
//硬件初始化
key_hw_init();
//注册中断处理程序
request_irq(IRQ_EINT(0),key_int, IRQF_TRIGGER_FALLING,"OK6410key",0);
return 0;
}
/*按键中断的处理,对于按键而言,可以在按下的时候产生中断,也可以在弹起的时候产生中断。需要通过一个标志来指定:IRQF_TRIGGER_FALLING,这个是从高电平到低电平产生中断。下表是其他产生中断的方式:*/
接下来是中断号的确定,就是request_irq函数的第一个参数。我们在内核代码中搜索irqs.h,找对应的板子的。我的是6410的。
从上面的代码看到,IRQ_EINT0_3的中断号是32.系统留出了S3C_IRQ_OFFSET=32个中断号,这是给软中断的。所以中断号就是等于硬件编号加上偏移量。可以查看内核代码的entry-macro.S
static void key_exit()
{
misc_deregister(&key_miscdevice);
}
module_init(key_init);
module_exit(key_exit);
Makefile:
obj-m := key.o
KDIR := /home/samba/linux-ok6410
all :
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
进行make编译完成,生成key.ko,拷贝到板子运行。在OK6410的终端看不到输出的效果。一个同伴说,这很可能是按键被屏幕占用的原因,所以,他把我的驱动程序拷贝到他的板子,装的是NFS的,屏幕是黑的,安装驱动,按下按键,真的在终端输出了key down。所以我就假定了真的是屏幕的原因。
下面是他改进的,按键驱动控制灯的程序key.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#define GPNCON 0x7F008830
#define VIC0INTENABLE 0x71200010
#define LEDCON 0x7F008820
#define LEDDAT 0x7F008824
unsigned int *ledcon;
unsigned int *leddata;
unsigned int *vic0intenable;
unsigned int *gpncon;
unsigned int data;
int key_int(int irq, void *dev_id)
{
data = readl(leddata);
data +=1;
writel(data,leddata);
return 0;
}
int key_open (struct inode *node, struct file *filp)
{
return 0;
}
struct file_operations key_ops =
{
.open = key_open,
};
struct miscdevice key =
{
.minor = 200,
.name = "key",
.fops = &key_ops
};
void key_hw_init()
{
gpncon = ioremap(GPNCON,4);
vic0intenable = ioremap(VIC0INTENABLE,4);
data = readl(gpncon);
data &= ~0b11;
data |= 0b10;
writel(data,gpncon);
data = readl(vic0intenable);
data |= 0b1;
writel(data,vic0intenable);
}
static int key_init()
{
misc_register(&key);
request_irq(IRQ_EINT(0),key_int,IRQF_TRIGGER_FALLING,"key",0);
key_hw_init();
ledcon = ioremap(LEDCON,4);
writel(0x1111,ledcon);
leddata = ioremap(LEDDAT,4);
writel(0,leddata);
return 0;
}
static void key_exit()
{
misc_deregister(&key);
free_irq(IRQ_EINT(0),0);
}
MODULE_LICENSE("GPL");
module_init(key_init);
module_exit(key_exit);
下面的这个例子是通过应用程序来测试按键驱动程序的:
Buttons.c:
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/fs.h> /* For file operations */
#include <linux/ioport.h> /* For io-port access */
#include<linux/platform_device.h>/*Forplatform_driverframework */
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include <linux/io.h> /* For inb/outb/... */
#define GPNCON_PA 0x7F008830
static int major = 0;
static struct class *buttons_class;
static volatile unsigned long *gpncon;
static volatile unsigned long *gpndat;
int buttons_open(struct inode *inode, struct file *file)
{
/* configure gpio as input */
printk("%s %d\n", __FUNCTION__, __LINE__);
*gpncon &= ~(0xfff);
return 0;
}
ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
char ker_buf[6];
int i;
if (size != 6)
{
printk("%s %d\n", __FUNCTION__, __LINE__);
return -EINVAL;
}
for (i = 0; i < 6; i++)
{
ker_buf[i] = ((*gpndat) >> i) & 1;
}
//printk("%s %d\n", __FUNCTION__, __LINE__);
copy_to_user(buf, ker_buf, 6);
return 6;
}
static struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = buttons_open,
.read = buttons_read,
};
int buttons_init(void)
{
major = register_chrdev(0, "buttons", &buttons_fops);
// mdev
buttons_class = class_create(THIS_MODULE, "buttons"); /* sysfs */
device_create(buttons_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpncon = ioremap(GPNCON_PA, 8);
gpndat = gpncon + 1;
return 0;
}
void buttons_exit(void)
{
unregister_chrdev(major, "buttons");
device_destroy(buttons_class, MKDEV(major, 0));
class_destroy(buttons_class);
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
下面是测试应用程序:buttons_test.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char **argv)
{
int fd;
char buf[6];
char pre_buf[6];
int len;
int i;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open /dev/xyz\n");
return -1;
}
while (1)
{
len = read(fd, buf, 6);
for (i = 0; i < 6; i++)
{
if (pre_buf[i] != buf[i])
{
printf("K%d %s\n", i+1, buf[i] ? "released" : "pressed");
}
}
memcpy(pre_buf, buf, 6);
}
return 0;
}