一、按键驱动
其实相比LED驱动,中断按键驱动,只是多添加了三个函数。
1,intrequest_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char*devname, void *dev_id)
irq是要申请的硬件中断号。(A10芯片所有外部中断都用28号中断)
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags是中断处理的属性,若设置了IRQF_DISABLED,则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED,则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM,表示对系统熵有贡献,对系统获取随机数有好处。
devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
2,free_irq,对应request_irq,释放中断
3,中断处理函数,本例中
static irqreturn_t key_irq(int irq, void*dev_id)
irq即为中断号,(外部中断28)
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
大家可能看到三星等其他的一些学习开发板中只需要用到这两个函数,不需要之前说的单片机里面的一些配置,那是因为开发板厂商为了降低学习难度,去掉了中断的配置。
为了降低操作寄存器的复杂度,这里给大家介绍两个很好的函数。
头文件:#include <asm/io.h>
原型:void writel (unsigned char data , unsigned short addr )
说明:writel() 往内存映射的 I/O 空间上写数据,wirtel() I/O 上写入 32 位数据 (4字节)。
头文件:#include <asm/io.h>
原型:unsigned char readl (unsigned int addr )
说明:readl() 从内存映射的 I/O 空间读取数据,readl 从 I/O 读取 32 位数据 ( 4 字节 )。
废话用多说,下面是代码。
/*
*实验目的:演示linux系统中外部中断功能
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <asm/io.h>
#include <linux/platform_device.h>
//#include <mach/gpio.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/syscalls.h>
#include <linux/reboot.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/completion.h>
#include <asm/uaccess.h>
#include <mach/irqs.h>
#include <mach/system.h>
#include <mach/hardware.h>
#include <plat/sys_config.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#define GPIO_IRQ 28
#define GPIO_TEST_BASE 0xf1c20800
#define PH_CFG2 (GPIO_TEST_BASE + 0×104)
#define PIO_INT_CFG2 (GPIO_TEST_BASE + 0×208)
#define PIO_INT_CTL (GPIO_TEST_BASE + 0×210)
#define PH18_SELECT (6 << 8)
static struct class *keydrv_class;
static irqreturn_t key_irq(int irq, void*dev_id)
{
unsigned int status;
status = readl(GPIO_TEST_BASE + 0×214 ) ;
printk(“irq = %d \n”,irq);
writel(status, GPIO_TEST_BASE + 0×214);
return IRQ_HANDLED;
}
static int key_drv_open(struct inode*inode, struct file *file)
{
int error;
int irq_ctl;
/*1,设置中断位*/
irq_ctl = readl(PH_CFG2);
writel(PH18_SELECT | irq_ctl ,PH_CFG2);
/*2,设置中断方式*/
irq_ctl = readl(PIO_INT_CFG2);
writel((0×1 <<(2*4)) | irq_ctl,PIO_INT_CFG2);
/*3,开中断*/
irq_ctl = readl(PIO_INT_CTL);
writel((1 << 18)|irq_ctl,PIO_INT_CTL);
error = request_irq(GPIO_IRQ, key_irq,0, “button”, 1);
if (error) {
printk(“failed to register keypad interrupt\n”);
}
printk(KERN_ALERT”key openi\n”);
return 0;
}
static ssize_t key_drv_write(struct file*file, const char __user *buf, size_t count, loff_t * ppos)
{
printk(KERN_ALERT”key write\n”);
return 0;
}
int key_drv_close(struct inode *inode,struct file *file)
{
free_irq(GPIO_IRQ,1);
return 0;
}
static struct file_operations key_drv_fops= {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = key_drv_open,
.write = key_drv_write,
.release = key_drv_close,
};
int major;
static int key_drv_init(void)
{
major = register_chrdev(0, “key_drv”, &key_drv_fops);
keydrv_class = class_create(THIS_MODULE,”key_drv”);
device_create(keydrv_class,NULL,MKDEV(major,0),NULL,”key”);
printk(KERN_ALERT”register\n”);
return 0;
}
static void key_drv_exit(void)
{
unregister_chrdev(major, “key_drv”); // 卸载
device_destroy(keydrv_class,MKDEV(major,0));
class_destroy(keydrv_class);
printk(KERN_ALERT”unregister\n”);
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE(“GPL”);