在我们之前学习单片机的是按键一般有轮询和中断两种方式,轮询就不用说了,需要CPU不断的去扫描,一般是不会采用这种方式的。而中断方式在单片机的一般的操作方法如下: 1, 配置中断引脚; 2, 设置中断模式(如,上升沿,下降沿,低电平,高电平。。。。。。。); 3, 清除所有中断 4, 开中断电 在中断函数里面: 1, 读中断状态 2, 中断操作任务 3, 任务结束,清中断 在我们的pcDuino里面操作方法是一样的,只不过添加了一些linux驱动框架。篇文章 一,按键驱动 其实相比LED驱动,中断按键驱动,只是多添加了三个函数。 1, [mw_shl_code=c,true]int request_irq(unsignedint irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)[/mw_shl_code]
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,中断处理函数,本例中 [mw_shl_code=c,true]static irqreturn_t key_irq(int irq, void*dev_id)[/mw_shl_code] 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 字节 )。 废话用多说,下面是代码。
[mw_shl_code=c,true]/*
*实验目的:演示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+ 0x104)
#define PIO_INT_CFG2 (GPIO_TEST_BASE + 0x208)
#define PIO_INT_CTL (GPIO_TEST_BASE+ 0x210)
#define PH18_SELECT (6 << 8)
static struct class *keydrv_class;
static irqreturn_t key_irq(int irq, void *dev_id)
{
unsignedint status;
status= readl(GPIO_TEST_BASE + 0x214 ) ;
printk("irq= %d \n",irq);
writel(status,GPIO_TEST_BASE + 0x214);
returnIRQ_HANDLED;
}
static int key_drv_open(struct inode *inode, structfile *file)
{
interror;
intirq_ctl;
/*1,设置中断位*/
irq_ctl= readl(PH_CFG2);
writel(PH18_SELECT| irq_ctl ,PH_CFG2);
/*2,设置中断方式*/
irq_ctl= readl(PIO_INT_CFG2);
writel((0x1<<(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"keyopeni\n");
return0;
}
static ssize_t key_drv_write(struct file *file,const char __user *buf, size_t count, loff_t * ppos)
{
printk(KERN_ALERT"keywrite\n");
return0;
}
int key_drv_close(struct inode *inode, struct file*file)
{
free_irq(GPIO_IRQ,1);
return0;
}
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");
return0;
}
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");[/mw_shl_code]
二,测试由于pcDuino要做arduino平台,所以pcDuino将外部中断做到了udev里面,这里为了避和arduino的外部中断有冲突,需要先删掉原来arduino的驱动。 sudo rmmod sw_interrupt
另外我们的中断函数需要printk打印信息,为了让信息及时显示出来,我们需要对系统进行的printk的打印等级进行配置。 echo "8 4 1 7">/proc/sys/kernel/printk
通过上面准备就可以测试按键驱动了。
[mw_shl_code=c,true]sudo insmod key.ko
exec 5</dev/key #打开设备开始中断[/mw_shl_code]#需要在串口调试中执行,打印也是在调试串口中出来的
现在按几下板子上面的MENU建,终端上个就会不断有下面信息出现。 root@ubuntu:~# exec 5</dev/key [ 6693.380000] key openi root@ubuntu:~# [ 6701.890000] irq = 28 [ 6702.650000] irq = 28 [ 6703.280000] irq = 28 [ 6703.870000] irq = 28 [ 6704.340000] irq = 28 [ 6704.820000] irq = 28 [ 6705.270000] irq = 28 [ 6706.220000] irq = 28 [ 6707.080000] irq = 28 [ 6707.850000] irq = 28 [ 6708.330000] irq = 28 [ 6708.750000] irq = 28 [ 6713.920000] irq = 28 [ 6714.400000] irq = 28 [ 6714.830000] irq = 28
测试结束 exec 5<&- #关闭设备
三,说明这个例子只是对外部中断进行说明,并没有对按键操作进行完善,后面的几章教程将对按键进行一步完善,请关注后面内容。
|