前面介绍过轮询的方法控制LED灯,这里将介绍如何使用按键产生中断的方法控制LED灯。
linux内核的中断需要使用request_irq函数来申请,并用free_irq来释放它,在此就不介绍它的原理,下面将直接讲如何使用它们来完成中断操作,首先查看request_irq的原型,如下:
1、static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
其中__must_check是提醒工程师在使用这个函数后最好检查其返回值,以确保申请中断成功,以下对各参数作介绍:
参数1:irq表示中断号,这个与芯片直接相关,不能随便定义,应该与硬件的中断号相对应;
参数2:代表一个函数指针,其实就是一个中断回调函数,当发生中断时会调用这个函数进行相应操作,它的原型为static irqreturn_t (*handler)(int irq, void *dev),其中irq对应到request_irq的参数1,代表执行的哪个中断号的中断,因为有些中断会共享同一个中断,如IO口的中断,*dev对应于request_irq的最后一个参数,用于传递 参数给 回调函数。
参数3:代表这个中断发生的标志,如IO口的中断有上升沿触发或下降沿触发等等
参数4:中断的名字,通过cat /proc/interrupts 查找
参数5:用于传递给中断回调函数的参数
2、void free_irq(unsigned int irq, void *dev_id)
释放中断,一般在结束时释放它irq对应于request_irq第一个参数,第二个对应于request_irq的最后一个参数。
首先让我们查看原理图按键部分
由原理图可知KEY1对应于XEINT0,则通过查芯片手册可知其对应的中断号为XIENT[0],其它按键同理可查,在以前的基础上改写驱动可得
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct {
struct cdev cdev;
dev_t devno;
struct class *leddrv_class;
struct device *leddrv_class_dev;
int irq;
int major;
unsigned long virt;
}CharLedDrive;
static CharLedDrive led_info;
static volatile int press = 0;
struct pin_desc{
unsigned int pin;
unsigned char key_val;
};
static unsigned char witch_key = 0;
static struct pin_desc key_descs[8] ={
[0] = {
.pin = S5PV210_GPH0(0),
.key_val = 0x01,
},
[1] = {
.pin = S5PV210_GPH0(1),
.key_val = 0x02,
},
[2] = {
.pin = S5PV210_GPH0(2),
.key_val = 0x03,
},
[3] = {
.pin = S5PV210_GPH0(3),
.key_val = 0x04,
},
[4] = {
.pin = S5PV210_GPH0(4),
.key_val = 0x05,
},
[5] = {
.pin = S5PV210_GPH0(5),
.key_val = 0x06,
},
[6] = {
.pin = S5PV210_GPH2(6),
.key_val = 0x07,
},
[7] = {
.pin = S5PV210_GPH2(7),
.key_val = 0x08,
},
};
volatile unsigned long *GPC0CON, *GPC0DAT;//用与存放两个个寄存器的地址
volatile unsigned long *GPH0CON, *GPH0DAT;//按键
static unsigned char KeyFlag;
static void all_leds_off(void);
static void led_config(void)
{
volatile unsigned long phys;//用于存放虚拟地址和物理地址
phys = 0xE0200060;
led_info.virt =(unsigned long)ioremap(phys, 0xf00);
GPC0CON = (unsigned long *)(led_info.virt + 0x00);//指定需要操作的三个寄存器的地址
GPC0DAT = (unsigned long *)(led_info.virt + 0x04);
GPH0CON = (unsigned long *)(led_info.virt + 0xc00-0x60); //keY1配置为输入
GPH0DAT = (unsigned long *)(led_info.virt + 0xc00-0x60+0x04);
*GPC0CON &= ~(0xFF << 12);
*GPC0CON |= 0x11 << 12; // 配置GPC0_3和GPC0_4为输出
*GPH0CON &= ~0x0F; //配置为输入
all_leds_off();
}
static void led1_on(void)
{
*GPC0DAT |= 1 << 3;
*GPC0DAT &= ~(0x01 << 4);
// printk("led1 light\n");
}
static void led2_on(void)
{
*GPC0DAT |= 1 << 4;
*GPC0DAT &= ~(0x01 << 3);
// printk("led2 light\n");
}
static void all_leds_on(void)
{
*GPC0DAT |= 1 << 3; // 点亮LED1
*GPC0DAT |= 1 << 4; // 点亮LED2
printk("all leds light\n");
}
static void all_leds_off(void)
{
*GPC0DAT &= ~(0x3 << 3); // 熄灭LED1和LED2
printk("all leds off\n");
}
static int led_drv_open(struct inode *inode, struct file *file)
{
printk("led_drv_open\n");
led_config();
all_leds_off();
return 0;
}
static irqreturn_t led_handler(int irq, void *devid)
{
struct pin_desc * pindesc = (struct pin_desc *)devid;
witch_key = pindesc->key_val;
KeyFlag = !KeyFlag;
printk("KERNEL:irq == %d witch_key= %d\n",irq,witch_key);
return IRQ_RETVAL(IRQ_HANDLED);
}
static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
char val = 0;
int ret = -1;
ret = copy_from_user(&val, buf, count);
if(ret)
{
printk("write ret= %x\n",ret);
}
return count;
}
static ssize_t led_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int ret = -1;
unsigned char from[1]={0};
if(KeyFlag)
{
led1_on();
}
else
{
led2_on();
}
from[0] = witch_key;
if(witch_key>0)
{
ret = copy_to_user(buf,from,sizeof(from));
if(ret)
{
printk("copy to user failed\n");
}
witch_key = 0;
}
return 0;
}
static struct file_operations led_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_drv_open,
.write = led_drv_write,
.read = led_drv_read,
};
//static irqreturn_t led_handler(int irq, void *dev_id)
static int led_drv_init(void)
{
int ret = -1;
int i;
cdev_init(&led_info.cdev, &led_drv_fops);
//led_info.major = register_chrdev(0, "led_drv", &led_drv_fops); // 注册, 告诉内核
led_info.cdev.owner = THIS_MODULE;
ret = alloc_chrdev_region(&led_info.devno, 0, 1, "led_drv");
if(ret){
printk(KERN_ERR "alloc char device region faild!\n");
return ret;
}
ret = cdev_add(&led_info.cdev, led_info.devno, 1);
if(ret){
printk(KERN_ERR "add char device faild!\n");
goto cdev_add_error;
}
led_info.leddrv_class = class_create(THIS_MODULE, "led_class_drv");
if(IS_ERR(led_info.leddrv_class)){
printk(KERN_ERR "function class_create excute error!\n");
goto led_class_error;
}
led_info.leddrv_class_dev = device_create(led_info.leddrv_class, NULL, led_info.devno, NULL, "led_drv"); /* /dev/xyz */
if(IS_ERR(led_info.leddrv_class_dev)){
printk(KERN_ERR "function device_create excute error!\n");
goto led_device_error;
}
for(i=0;i<5;i++)
{
ret = request_irq(IRQ_EINT(i), &led_handler, IRQ_TYPE_EDGE_FALLING,"led_irq", &key_descs[i]);
if(ret)
{
printk(KERN_ERR"function request_irq(%d) excute error\n",i);
goto led_request_irq_error;
}
}
return 0;
led_request_irq_error:
device_unregister(led_info.leddrv_class_dev);
led_device_error:
class_destroy(led_info.leddrv_class);
led_class_error:
cdev_del(&led_info.cdev);
cdev_add_error:
unregister_chrdev_region(led_info.devno,1);
return -ENODEV;
}
static void led_drv_exit(void)
{
free_irq(IRQ_EINT(0), &key_descs[0]);
free_irq(IRQ_EINT(1), &key_descs[1]);
free_irq(IRQ_EINT(2), &key_descs[2]);
free_irq(IRQ_EINT(3), &key_descs[3]);
free_irq(IRQ_EINT(4), &key_descs[4]);
printk("led_drv_exit ....excute ok\n");
//all_leds_off();
printk("led_drv_exit ....excute ok0\n");
device_unregister(led_info.leddrv_class_dev);
printk("led_drv_exit ....excute ok1\n");
class_destroy(led_info.leddrv_class);
printk("led_drv_exit ....excute ok2\n");
cdev_del(&led_info.cdev);
printk("led_drv_exit ....excute ok3\n");
unregister_chrdev_region(led_info.devno, 1);
printk("led_drv_exit ....excute ok4\n");
iounmap((void *)led_info.virt); //撤销映射关系
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
#include
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
int fd = -1;
char val = 0;
char buf[10]={0};
char temp= 0;
char *s = malloc(100);
unsigned char key[1];
fd = open("/dev/led_drv",O_RDWR);
if(fd<0)
printf("can't open \n");
while(1)
{
val++;
if(read(fd,key,sizeof(key))>0)
{
temp = key[0];
if(temp)
printf("pressed key is key[%x]\n",temp);
temp = 0;
}
}
free(s);
return 0;
}