嵌入式Linux学习: interrupt实验

Linux中的Interrupt(中断)系统是一个至关重要的组成部分,它负责管理和处理系统中发生的各种硬件和软件中断,确保系统能够正确响应外部设备的请求,保持系统的稳定性和可靠性。

1.中断的作用

  • 允许设备在没有CPU干预的情况下发送信号并请求处理。
  • 提高系统的并发处理能力,使CPU能够同时处理多个任务。
  • 确保系统能够及时响应外部设备的请求,保证系统的实时性和稳定性。

2. Linux中断子系统的组成与工作流程

  • 中断控制器:负责收集所有中断源发起的中断,并对中断进行初步处理,如优先级排序、中断分发等。
  • 中断处理函数:每个中断都会对应一个或多个中断处理函数,当中断发生时,CPU会暂停当前任务,跳转到相应的中断处理函数执行。
  • 中断描述符(irq_desc):用于描述IRQ(Interrupt Request,中断请求)线的属性与状态,是中断子系统中的重要数据结构。
  • 中断芯片(irq_chip):描述不同类型的中断控制器,提供与硬件中断控制器交互的接口。

3.特殊中断类型

  • SGI(Software Generated Interrupt):软件触发的中断,一般用于核间通信。
  • PPI(Private Peripheral Interrupt):私有外设中断,每个核心私有的中断。
  • SPI(Shared Peripheral Interrupt):共享外设中断,可以分发到某一个CPU上。
  • LPI(Locality-specific Peripheral Interrupt):GICv3中的新特性,基于消息的中断。

4. Linux 中断 API 函数

request_irq 函数

在 Linux 内核中要想使用某个中断是需要申请的,request_irq 函数用于申请中断,request_irq
函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函
数。 request_irq 函数会激活(使能)中断,所以不需要我们手动去使能中断, request_irq 函数原型
如下:

int request_irq(unsigned int irq,
				irq_handler_t handler,
				unsigned long flags,
				const char *name,
				void *dev)
/* 
	irq:要申请中断的中断号。
	handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
	flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志
	name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
	dev: 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
 返回值: 0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经
 被申请了。
 */

flags中断标志介绍如下:

| 标志               | 描述                                                       			  |  
|--------------------|--------------------------------------------------------------|  
| IRQF_SHARED        | 允许多个设备共享同一个中断线,所有共享中断都需指定此标志,dev 参数为唯一区分标志         |  
| IRQF_PROBE_SHARED  | 类似IRQF_SHARED,但用于自动探测共享中断。不推荐使用              |  
| IRQF_TIMER         | 这是一个定时器中断                                              |  
| IRQF_IRQPOLL       | 驱动程序应使用慢速中断轮询                                      |  
| IRQF_ONESHOT       | 如果是共享中断,request_irq的dev参数应唯一,用于区分中断请求     |  
| IRQF_NO_SUSPEND    | 禁止在挂起期间禁用此中断                                       |  
| IRQF_FORCE_RESUME  | 强制在设备唤醒时重新启用中断,即使它被标记为IRQF_NO_SUSPEND      |  
| IRQF_NOBALANCING   | 不允许在CPU之间平衡此中断                                     |  
| IRQF_IRQ_DISABLED  | 中断被禁用(用于特殊目的的中断初始化)                         |  
| IRQF_PERCPU        | 中断是每CPU的(不支持共享)                                   |  
| IRQF_NO_THREAD     | 不要为中断创建线程(也称为“bottom half”)                     |  
| IRQF_EARLY_RESUME  | 允许在suspend-to-idle期间尽早恢复中断(与IRQF_NO_SUSPEND相关)   |  
| IRQF_TRIGGER_NONE  | 没有触发事件                                                 |  
| IRQF_TRIGGER_RISING | 上升沿触发                                                   |  
| IRQF_TRIGGER_FALLING| 下降沿触发                                                   |  
| IRQF_TRIGGER_HIGH  | 高电平触发                                                   |  
| IRQF_TRIGGER_LOW   | 低电平触发                                                   |  
| IRQF_TRIGGER_MASK  | 触发类型掩码(用于清除触发类型标志)                           |

free_irq 函数

使用中断的时候需要通过 request_irq 函数申请,使用完成以后就要通过 free_irq 函数释放
掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。

void free_irq(unsigned int irq,
			  void *dev)
/*
	irq: 要释放的中断。
	dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断
	只有在释放最后中断处理函数的时候才会被禁止掉。
	返回值:无。
*/

5.interrupt子系统实验

注:本实验使用的是韦东山I.MX6U开发板

dts文件

/dts-v1/;
/{
    gpio_keys_100ask_imx6ull{
        compatible = "100ask,gpio_keys";
        gpios = <&gpio5 1 GPIO_ACTIVE_LOW
                &gpio4 14 GPIO_ACTIVE_LOW>;
        pinctrl-names = "default";
        pinctrl-0 = <&key1_pinctrl
                    &key2_pinctrl>;
        status = "okay";
    };
};
&iomuxc_snvs {
	key1_pinctrl: key1_pinctrl {        /*!< Function assigned for the core: Cortex-A7[ca7] */
            fsl,pins = <
                MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01        0x000110A0
            >;
        };
};
        
&iomuxc {
	key2_pinctrl: key2_pinctrl {       /*!< Function assigned for the core: Cortex-A7[ca7] */
            fsl,pins = <
                MX6UL_PAD_NAND_CE1_B__GPIO4_IO14           0x000010B0
            >;
        };
}

100ask_imx6ull-14x14.dts 完成编写后,在(内核目录)编译dts文件

book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make dtbs

将编译好的dtb文件拷贝到网络文件系统中

cp /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/

#include "asm-generic/errno-base.h"
#include "asm-generic/int-ll64.h"
#include "asm/gpio.h"
#include "linux/compiler.h"
#include "linux/err.h"
#include "linux/export.h"
#include "linux/gpio/consumer.h"
#include "linux/interrupt.h"
#include "linux/ioport.h"
#include "linux/kdev_t.h"
#include "linux/leds.h"
#include "linux/of.h"
#include "linux/printk.h"
#include "linux/slab.h"
#include "linux/stddef.h"
#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_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

struct gpio_key{
    int gpio;
    enum of_gpio_flags flags;
    int irq;
};

static struct gpio_key *gpio_keys;


static irqreturn_t gpio_keys_100ask_irq(int irq, void *dev_id)
{
    struct gpio_key *gpio_key = dev_id;

	printk("key %d val %d\n", irq,gpio_get_value(gpio_key->gpio));
	return IRQ_HANDLED;
}


/*   当驱动与设备连匹配成功时执行此函数   */
static int keys_probe (struct platform_device *pdv){
    int count,i,err;
    int gpio,irq;
    enum of_gpio_flags flags;
    struct device_node *node = pdv->dev.of_node;
    count = of_gpio_count(node); // 得到设备所使用的GPIO个数

    gpio_keys = kzalloc(count * sizeof(struct gpio_key), GFP_KERNEL);
    for(i = 0; i < count; i++)
    {
        gpio = of_get_gpio_flags(node,i,&flags);
        irq = gpio_to_irq(gpio);

        gpio_keys[i].gpio = gpio;
        gpio_keys[i].irq = irq;
        gpio_keys[i].flags = flags;

       err = request_irq(irq, gpio_keys_100ask_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys[i]);
       if(err != 0)
       {
        	dev_err(&pdv->dev, "Could not get irq %d.\n", irq);
       }
    }

    return 0;
}


static int keys_remove (struct platform_device *pdv)
{
    int i;

    for(i = 0; i < of_gpio_count(pdv->dev.of_node); i++)
    {
        free_irq(gpio_keys[i].irq, &gpio_keys[i]);
    }

    kfree(gpio_keys);

    return 0;
}


static struct of_device_id keys_match[] = {
	{
		.compatible = "100ask,gpio_keys"
	},
	{},
};



static struct platform_driver keys_driver = {
    .probe = keys_probe,
    .remove = keys_remove,
    .driver = {
        .name = "key_driver",
        .of_match_table = of_match_ptr(keys_match),
     },
};

static int __init keys_init(void)
{
    return platform_driver_register(&keys_driver);
}

static void __exit keys_exit(void)
{
    platform_driver_unregister(&keys_driver);
}


module_init(keys_init);
module_exit(keys_exit);

MODULE_AUTHOR("Pumpk1n");
MODULE_LICENSE("GPL");
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88

obj-m += keys_int_driver.o

all: 
	$(MAKE) -C $(KERN_DIR) M=`pwd` modules

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

执行命令: make

编译 keys_int_driver.c文件为 keys_int_driver.ko内核模块文件

拷贝文件到网络文件系统中

cp keys_int_driver.ko ~/nfs_rootfs/

串口连接开发板

挂载到网络文件系统中

[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

将/mnt/100ask_imx6ull-14x14.dtb文件复制到/boot/下面

[root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot/

重启开发板,等待重启完成后重新挂载网络文件系统中

[root@100ask:~]# reboot
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

进入到/mnt/目录中,并向内核加载模块文件keys_int_driver.ko

[root@100ask:~]# cd /mnt
[root@100ask:mnt]# insmod keys_int_driver.ko

设置内核消息的打印

echo "7 4 1 7" > /proc/sys/kernel/printk

按下按键key1,key2可分别在控制台打印消息

遇到的错误:

genirq: Flags mismatch irq 208. 00000003 (100ask_gpio_key) vs. 00000083 (User1 Button)

解决办法:

If you want to use the same irq number to register different interrupt handler, please use IRQ_SHARED flags.

 err = request_irq(irq, gpio_keys_100ask_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, "100ask_gpio_key", &gpio_keys[i]);
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南瓜籽_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值