按键中断实现流程:
1.设备树中添加中断信息:f9--key1,f7---key2,f8---key3
/*myirq{
interrupt-parent = <&gpiof>;
interrupts = <7 0>, <8 0>, <9 0>;//第一个成员指的是gpio编号,第二个成员表示触发方式,0表示默认属性
};*/
内核顶层目录make dtbs 编译,拷贝到tftpboot目录下,重新启动开发板加载设备树文件
2.编写驱动代码:
定义3个中断号,在入口函数中根据节点信息申请中断号
node=of_find_node_by_name(NULL,"myirq");
irqno[0]=irq_of_parse_and_map(node,2);
将中断号注册进内核(此步骤会跳转到回调函数,执行自定义的中断操作)
ret=request_irq(irqno[0],irq_handler1,IRQF_TRIGGER_FALLING,"key1_inte",NULL);
参数1:软中断号,参数2:中断处理函数(函数指针),参数3:中断触发方式(下降沿触发,上升沿触发,高电平,低电平触发)参数4:中断的名字,自己指定,参数5:在这个函数里传给中断处理函数的参数,根据实际情况使用
在出口注销中断号
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
struct gpio_desc *gpinum[3];//存放老版本
struct device_node *nod;
//定义指针指向获取的设备树节点信息空间
struct device_node *node;
int ret;
unsigned int irqno[3];//用于接收软中断号
unsigned int count=0;//用于计数,中断发生一次+1
//中断处理函数顶部
irqreturn_t irq_handler1(int irqno,void *dev)
{
count++;
printk("key1 interrupt....%d...\n",count);
gpiod_set_value(gpinum[0],!gpiod_get_value(gpinum[0]));
return IRQ_HANDLED;
}
irqreturn_t irq_handler2(int irqno,void *dev)
{
count++;
printk("key2 interrupt....%d...\n",count);
gpiod_set_value(gpinum[1],!gpiod_get_value(gpinum[1]));
return IRQ_HANDLED;
}
irqreturn_t irq_handler3(int irqno,void *dev)
{
count++;
printk("key3 interrupt....%d...\n",count);
gpiod_set_value(gpinum[2],!gpiod_get_value(gpinum[2]));
return IRQ_HANDLED;
}
static int __init mycdev_init(void)
{
//通过名字获取设备树节点信息
node=of_find_node_by_name(NULL,"myirq");
if(node==NULL)
{
printk("通过名字解析设备树节点失败\n");
return -EFAULT;
}
printk("成功解析到设备树节点\n");
//根据设备树节点信息获取软中断号key1
irqno[0]=irq_of_parse_and_map(node,2);
if(irqno[0]==0)
{
printk("获取软中断号失败\n");
return EINVAL;
}
printk("获取软中断号成功\n");
//将中断注册进内核
ret=request_irq(irqno[0],irq_handler1,IRQF_TRIGGER_FALLING,"key1_inte",NULL);
if(ret)
{
printk("注册中断失败\n");
return ret;
}
printk("注册中断1成功\n");
//根据设备树节点信息获取软中断号key2
irqno[1]=irq_of_parse_and_map(node,0);
if(irqno[1]==0)
{
printk("获取软中断号失败\n");
return EINVAL;
}
printk("获取软中断号成功\n");
//将中断注册进内核
ret=request_irq(irqno[1],irq_handler2,IRQF_TRIGGER_FALLING,"key2_inte",NULL);
if(ret)
{
printk("注册中断失败\n");
return ret;
}
printk("注册中断2成功\n");
//根据设备树节点信息获取软中断号key3
irqno[2]=irq_of_parse_and_map(node,1);
if(irqno[2]==0)
{
printk("获取软中断号失败\n");
return EINVAL;
}
printk("获取软中断号成功\n");
//将中断注册进内核
ret=request_irq(irqno[2],irq_handler3,IRQF_TRIGGER_FALLING,"key3_inte",NULL);
if(ret)
{
printk("注册中断失败\n");
return ret;
}
printk("注册中断3成功\n");
//灯的初始化
//1.通过名字获取设备节点信息
nod=of_find_node_by_name(NULL,"myleds");
//获取并申请gpio编号
gpinum[0]=gpiod_get_from_of_node(nod,"myled1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpinum[0]))
{
printk("获取gpioe10编号失败\n");
//将错误码指针转换成错误码
return PTR_ERR(gpinum[0]);
}
printk("获取gpio编号成功\n");
gpinum[1]=gpiod_get_from_of_node(nod,"myled2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpinum[1]))
{
printk("获取gpio编号失败\n");
//将错误码指针转换成错误码
return PTR_ERR(gpinum[1]);
}
printk("获取gpiof10编号成功\n");
gpinum[2]=gpiod_get_from_of_node(nod,"myled3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpinum[2]))
{
printk("获取gpio编号失败\n");
//将错误码指针转换成错误码
return PTR_ERR(gpinum[2]);
}
printk("获取gpioe8编号成功\n");
//设置管脚输出模式
//gpiod_direction_output(gpinum,0);
return 0;
}
static void __exit mycdev_exit(void)
{
//熄灭led1
gpiod_set_value(gpinum[0],0);
//卸载驱动
gpiod_put(gpinum[0]);
//熄灭led2
gpiod_set_value(gpinum[1],0);
//卸载驱动
gpiod_put(gpinum[1]);
//熄灭led3
gpiod_set_value(gpinum[2],0);
//卸载驱动
gpiod_put(gpinum[2]);
//中断的注销
free_irq(irqno[0],NULL);
free_irq(irqno[1],NULL);
free_irq(irqno[2],NULL);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
实验现象