中断
1、先知道需要使用的中断对应的中断号
2、先申请request_irq,此函数会激活中断。
3、如果不用中断了,那就时放点,free_irq;
4、中断处理函数 irqreturn_t (*irq_handler_t) (int, void *)
5、5、中断使能与禁止函数
常用的中断使用和禁止函数如下所示:
void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)
2022.5.31
上半部和下半部
上半部分占用时间少,下半部分占用时间多。
中断处理一定要越快越好。
参考点:
①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。
1、软中断
要先注册软中断、要先注册。软中断要在编译的时候静态使用!!!
触发软中断:
*** 软中断不要去用!!!
也需要使用到上半部,只是在上半部的中断处理函数重点调用的是tasklet_schedule
。
1、定义一个tasklet。
2、初始化、重点是设置对应的处理函数。
对于 gpio 来说,gpio 节点也可以作为中断控制器,比如 imx6ull.dtsi 文件中的 gpio5 节点内容如下所示:
示例代码 51.1.3.2 gpio5 设备节点
gpio5: gpio@020ac000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x020ac000 0x4000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
如果需要写驱动的话 需要做的事情。
驱动代码编写
添加中断信息
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-key";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */
status = "okay";
};
加载驱动失败的原因是因为:
两个节点都使用了 同一个GPIO
原厂驱动代码解析 按键设备树。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/atomic.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#define IMX6ULIRQ_NAME "imx6ul"
#define IMX6ULIRQ_COUNT 1
#define IMX6ULIRQOFF 0 /* 关灯 */
#define IMX6ULIRQON 1 /* 开灯 */
#define KEY_NUM 1
#define KEY0_VALUE 0X01
#define INVAKEY 0XFF
/*按键结构体*/
struct irq_keydesc{
int gpio; /*io编号*/
int irqnum; /*中断号*/
unsigned char value; /*键值*/
char name[10]; /*名字*/
irqreturn_t (*handler)(int ,void *); /*中断处理函数*/
};
struct imx6uirq_dev {
struct cdev cdev;
struct class *class;/*类:为了自动创建节点*/
struct device *device;/*设备:为了自动创建节点*/
dev_t devid; //设备号
int major; //主设备号
int minor; //次设备号
struct device_node *nd; //设备节点
int led_gpio;
struct irq_keydesc irqkey[KEY_NUM];
struct timer_list timer;
atomic_t keyvalue;
atomic_t releasekey; //
struct mutex lock;
};
struct imx6uirq_dev imx6uirq; //imx6uirq设备
static int imx6uirq_open(struct inode *inode,struct file *filp)
{
filp->private_data = &imx6uirq;
return 0;
}
static ssize_t imx6uirq_read(struct file *filp,char __user *buf,size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char keyvalue;
unsigned char releasekey;
struct imx6uirq_dev *dev = filp->private_data;
keyvalue = atomic_read(&dev->keyvalue);
releasekey = atomic_read(&dev->releasekey);
if (releasekey)
{
if (keyvalue & 0x80)
{
keyvalue &= ~0x80;
ret = copy_to_user(buf,&keyvalue,sizeof(keyvalue));
}
else{
goto data_error;
}
atomic_set(&dev->releasekey,0);//按下标志清0
}else{
goto data_error;
}
return ret;
data_error:
return -EINVAL;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
//struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
return 0;
}
static int imx6uirq_release(struct inode *inode,struct file *filp)
{
//struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
return 0;
}
static irqreturn_t key0_handler(int irq,void *dev_id)
{
struct imx6uirq_dev *dev = dev_id;
#if 0
value = gpio_get_value(dev->irqkey[0].gpio);
if (value == 0)
{
printk("key 0 press\r\n");
}
else if (value == 1)
{
printk("key 0 release\r\n");
}
#endif
dev->timer.data = (volatile unsigned long)dev_id;
mod_timer(&dev->timer,jiffies + msecs_to_jiffies(10)); //10ms
return IRQ_HANDLED;
}
/*定时器处理函数*/
static void timer_func(unsigned long arg)
{
int value = 0;
struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;
value = gpio_get_value(dev->irqkey[0].gpio);
if (value == 0)
{
atomic_set(&dev->keyvalue,dev->irqkey[0].value);
printk("key 0 press\r\n");
}
else if (value == 1)
{
atomic_set(&dev->keyvalue,0x80|(dev->irqkey[0].value));
atomic_set(&dev->releasekey,1);//完整的释放 按键完成之后写入1
printk("key 0 release\r\n");
}
//重新开启定时器
//mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->timerperiod));
}
static int keyio_init(struct imx6uirq_dev *dev)
{
/*1、按键的初始化*/
int i = 0;
int ret = 0;
dev->nd = of_find_node_by_path("/key");
if (dev->nd == NULL)
{
printk("key node not find!\r\n");
return -EINVAL;
}
for(i = 0; i <KEY_NUM;i++){
dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpio",i);
if (dev->irqkey[i].gpio < 0) {
printk("can't get key%d\r\n", i);
}
}
for(i = 0; i <KEY_NUM;i++){
// /*获取按键的IO编号*/
// dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpios",i);
memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));
sprintf(dev->irqkey[i].name,"KEY%d",i);
gpio_request(dev->irqkey[i].gpio,dev->irqkey[i].name);
gpio_direction_input(dev->irqkey[i].gpio);
dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
printk("key%d:gpio=%d, irqnum=%d\r\n",i,dev->irqkey[i].gpio,dev->irqkey[i].irqnum);
//或者
//dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i);
}
dev->irqkey[0].handler = key0_handler;
dev->irqkey[0].value = KEY0_VALUE;
/*2、中断初始化*/
for(i = 0; i <KEY_NUM;i++){
ret = request_irq(dev->irqkey[i].irqnum,dev->irqkey[i].handler,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
dev->irqkey[i].name,&imx6uirq);
if (ret<0)
{
printk("irq request_irq err");
goto fail_irq;
}
}
/*3 初始化定时器*/
init_timer(&imx6uirq.timer);
//imx6uirq.timer.expires = jiffies + msecs_to_jiffies(imx6uirq.timerperiod);
imx6uirq.timer.function = timer_func;
imx6uirq.timer.data = (unsigned long)&imx6uirq;
return 0;
fail_irq:
for(i = 0; i <KEY_NUM;i++)
gpio_free(dev->irqkey[i].gpio);
return ret;
}
static struct file_operations imx6uirq_fops = {
.owner = THIS_MODULE,
.write = imx6uirq_write,
.open = imx6uirq_open,
.release = imx6uirq_release,
.read = imx6uirq_read,
};
static int __init imx6uirq_init(void)
{
int ret = 0;
/*互斥体初始化*/
mutex_init(&imx6uirq.lock);
/*注册字符设备*/
imx6uirq.major = 0;
if (imx6uirq.major){
imx6uirq.devid = MKDEV(imx6uirq.major,0);//设备号
ret = register_chrdev_region(imx6uirq.devid,IMX6ULIRQ_COUNT,IMX6ULIRQ_NAME);
} else{
ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6ULIRQ_COUNT,IMX6ULIRQ_NAME);
imx6uirq.major = MAJOR(imx6uirq.devid);
imx6uirq.minor = MINOR(imx6uirq.devid);
}printk("led major = %d led minor = %d \r\n",imx6uirq.major,imx6uirq.minor);
if (ret < 0){
goto faile_devid;
}
/*2、添加字符设备*/
imx6uirq.cdev.owner = THIS_MODULE;
cdev_init(&imx6uirq.cdev,&imx6uirq_fops);
ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6ULIRQ_COUNT);
if(ret<0){
goto fail_cdev;
}
/*3、创建设备类*/
imx6uirq.class = class_create(THIS_MODULE,IMX6ULIRQ_NAME);
if(IS_ERR(imx6uirq.class)){
ret = PTR_ERR(imx6uirq.class);
printk("can't class_create \r\n");
goto fail_class;
}
/*创建设备节点*/
imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6ULIRQ_NAME);
if(IS_ERR(imx6uirq.device)){
ret = PTR_ERR(imx6uirq.device);
printk("can't device_create \r\n");
goto fail_device;
}
ret = keyio_init(&imx6uirq);
if (ret<0)
{
goto fail_keyinit;
}
atomic_set(&imx6uirq.keyvalue,INVAKEY);
atomic_set(&imx6uirq.releasekey,0);
fail_keyinit:
device_destroy(imx6uirq.class,imx6uirq.devid);
fail_device:
class_destroy(imx6uirq.class);
fail_class:
cdev_del(&imx6uirq.cdev);
fail_cdev:
unregister_chrdev_region(imx6uirq.devid,IMX6ULIRQ_COUNT);
faile_devid:
return ret;
}
static void __exit imx6uirq_exit(void)
{
int i = 0;
//1、释放中断
for(i = 0; i <KEY_NUM;i++)
free_irq(imx6uirq.irqkey[i].irqnum,&imx6uirq);
//2、释放IO
for(i = 0; i <KEY_NUM;i++)
gpio_free(imx6uirq.irqkey[i].gpio);
/*删除定时器*/
del_timer_sync(&imx6uirq.timer);
/*删除设备*/
cdev_del(&imx6uirq.cdev);
/*释放设备号*/
unregister_chrdev_region(imx6uirq.devid,IMX6ULIRQ_COUNT);
/*摧毁设备*/
device_destroy(imx6uirq.class,imx6uirq.devid);
/*释放类*/
class_destroy(imx6uirq.class);
/*释放IO*/
//gpio_free(imx6uirq.led_gpio);
}
//模块加载函数
module_init(imx6uirq_init);
//模块卸载
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qhy");