驱动开发(四):Linux内核中断

发布于:2024-06-16 ⋅ 阅读:(14) ⋅ 点赞:(0)
 驱动开发系列文章:
                驱动开发(一):驱动代码的基本框架    
                驱动开发(二):创建字符设备驱动
                驱动开发(三):内核层控制硬件层  

                驱动开发(四):Linux内核中断

Linux内核中断是一种控制传递机制,用于处理硬件设备的事件和异常。当硬件设备发生特定事件时,例如数据传输完成、错误发生或设备准备就绪,它会向CPU发送中断信号,以引起CPU的注意。

Linux内核中断分为两种类型:外部中断和内部中断。外部中断由外部硬件设备触发,如键盘按键、鼠标点击或网络数据包到达。内部中断由CPU内部产生,如除法错误、缺页异常或系统调用。

中断处理程序是用来响应和处理中断的函数。当中断发生时,CPU会暂时停止当前正在执行的任务,并跳转到相应的中断处理程序执行。中断处理程序根据中断的类型和优先级,执行相应的操作以处理中断事件。

Linux内核提供了一个中断向量表(Interrupt Vector Table),其中包含了所有中断的处理程序的入口地址。当中断发生时,CPU会根据中断号在中断向量表中查找相应的处理程序的地址,并将控制权转移给这个地址。

在Linux内核中,中断有优先级,较高优先级的中断可以打断正在运行的较低优先级中断,这被称为中断抢占。Linux内核通过中断处理程序的优先级和抢占策略来管理中断的响应和处理。

中断是Linux内核实现设备驱动和处理硬件事件的重要机制。它能够提高系统的并发性和响应性,有效地处理多种硬件设备的事件和异常。

中断原理

Linux内核中断的原理主要涉及硬件、中断控制器和中断处理程序的协作工作。

  1. 硬件触发中断:当硬件设备发生特定事件时,例如数据传输完成、错误发生或设备准备就绪,它会向CPU发送中断信号。这个中断信号会被送到中断控制器。

  2. 中断控制器:中断控制器是硬件设备负责管理和传递中断信号的组件。它负责接收中断信号,确定中断的类型和优先级,并通过中断向量表将控制权传递给相应的中断处理程序。

  3. 中断处理程序:中断处理程序是用来响应和处理中断的函数。当中断发生时,CPU会跳转到相应的中断处理程序的入口地址执行。中断处理程序根据中断的类型和优先级,执行相应的操作以处理中断事件。

  4. 中断处理过程:当中断发生时,CPU会先保存当前的执行环境(包括寄存器状态、程序计数器等),然后根据中断号在中断向量表中查找相应的中断处理程序的入口地址。中断处理程序会执行特定的操作,处理中断事件。处理完成后,CPU会恢复之前保存的执行环境,并继续执行被中断的任务。

  5. 中断抢占:在Linux内核中,中断有优先级,较高优先级的中断可以打断正在运行的较低优先级中断,这被称为中断抢占。中断抢占能够保证紧急事件的优先处理,并提高系统的并发性和响应性。

总结来说,Linux内核中断的原理是通过硬件设备触发中断信号,中断控制器将中断信号传递给CPU,CPU根据中断号在中断向量表中查找对应的中断处理程序,并执行特定的操作来处理中断事件。中断抢占能够保证紧急事件的优先处理,并提高系统的并发性和响应性。

先找到IRQ的标签,然后跳转,跳转时这个名字是写死的(handle_irq),在handle_irq里定义一个数组,irq_desc[],数组的每一个成员变量里存了结构体,irq_xxx。在结构体里面有个函数指针,这个指针指向了我们函数的名字。数组的下标和中断号有关系,这里是中断号,但是是软中断号。

软中断号是linux内核给分配的中断号,是内核为了兼容各种芯片,而设计的。这里兼容是通过映射实现的,我们不同的板子根据映射关系,使用中断号得到软中断号。

函数实现

注册中断

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
功能:注册中断
参数:
	@irq : 软中断号  gpio的软中断号   软中断号 = gpio_to_irq(gpio号);
	    gpio号 = m*32+n         
        m:哪一组  A B C D E      0 1 2 3 4	  
	    n:组内的序号
			例如:GPIOA28的gpio号 = 0*32+28 = 40
	@handler: 中断的处理函数
			irqreturn_t (*irq_handler_t)(int irqno, void *dev);
			IRQ_NONE        //中断没有处理完成
			IRQ_HANDLED     //中断正常处理完成
	@flags :中断的触发方式
			#define IRQF_DISABLED		0x00000020 //快速中断
			#define IRQF_SHARED		0x00000080     //共享中断
			#define IRQF_TRIGGER_RISING	0x00000001//上升沿
			#define IRQF_TRIGGER_FALLING	0x00000002//下降沿
			#define IRQF_TRIGGER_HIGH	0x00000004//高电平
			#define IRQF_TRIGGER_LOW	0x00000008//低电平
	@name :名字   cat /proc/interrupts
	@dev  :向中断处理函数中传递参数 ,如果不想传写个NULL就行

返回值:成功0,失败返回错误码
快速中断(在处理函数里面写了他,就先处理这个中断)

共享中断(中断的接口较少,但是器件都想要中断,那管脚需要外接两个,寄存器里面有中断状态标志位,看中断状态标志位有没有置位。一个口不可以链接两个按键,按键没办法区分)

注销中断 

oid free_irq(unsigned int irq, void *dev_id)
功能:注销中断
参数:
	@irq :软中断号
	@dev_id:向中断处理函数中传递的参数 ,如果上面写的NULL,这里就写NULL

按键中断实验

1、  查看实物,找到按键丝印)

2、  去地板原理图网络标号----->芯片引脚)

GPIOB8--------->gpio号=40

GPIOB16--------->gpio号=48

驱动代码 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>

#define GPIONO(m, n) m * 32 + n  //计算gpio号
#define GPIO_B8 (GPIONO(1, 8))   
#define GPIO_B16 (GPIONO(1, 16))
int gpiono[] = {GPIO_B8, GPIO_B16};    //数组内存入两个按键的软中断号
char *name[]={"gpio_it_8","gpio_it_16"};     //随便定义,用于区分

int ret,i;

//中断处理函数
irqreturn_t handler(int irqno, void *dev)
{
    if(irqno == gpio_to_irq(GPIO_B8))
    {
        printk(KERN_ERR "+++++++++++++\n");
    }
    if(irqno == gpio_to_irq(GPIO_B16))
    {
        printk(KERN_ERR "-------------\n");
    }
    return IRQ_HANDLED;
}

static int __init hello_init(void)
{
//循环注册中断  gpio_to_irq()
    for (i = 0; i < sizeof(gpiono) / sizeof(int); i++)
    {
        ret = request_irq(gpio_to_irq(gpiono[i]), handler, IRQF_TRIGGER_FALLING,name[i], NULL);
        if (ret != 0)
        {
            printk(KERN_ERR "%s request irq err\n",name[i]);
            return ret;
        }
    }

    return 0;
}
static void __exit hello_exit(void)
{
//循环注销中断
    for (i = 0; i < sizeof(gpiono) / sizeof(int); i++)
    {
        free_irq(gpio_to_irq(gpiono[i]), NULL);
    }
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

常见问题

驱动安装时报错:

insmod: can't insert 'farsight_irq.ko': Device or resource busy

解决方法

1、在开发板执行
cat /proc/interrupts

146: GPIO nxp-keypad

154: GPIO nxp-keypad

说明中断号已经被占用了,要在内核中将占用中断号的驱动删掉

2、在虚拟机中,内核顶层目录上执行 grep "nxp-keypad" * -nR

显示

arch/arm/mach-s5p6818/include/mach/devices.h:48:#define DEV_NAME_KEYPAD "nxp-keypad"

3、执行 grep "DEV_NAME_KEYPAD" * -nR

drivers/input/keyboard/nxp_io_key.c:324: .name = DEV_NAME_KEYPAD,

找到驱动文件的名字是nxp_io_key.c

4、cd drivers/input/keyboard/

5、vi Makefile 

6、找到由nxp_io_key.o生成的目标文件KEYBOARD_NXP_KEY

7、make menuconfig

8、make uImage 重新编译内核

9、cp arch/arm/boot/uImage ~/tftpboot 将内核放到TFTP下

关于按键的抖动

明明只按了一次按键,却打印了多条信息

所有的机械元件都会有抖动问题

主要原因不是手抖,而是硬件自身的抖动(输入的机械元件才会有抖动)

最好是使用定时器的方式消抖

定时器消抖的原理:

主程序触发中断-》在按键中断服务程序里开启定时器-》在定时器中断里实现按键中断操作

好处:避免在中断里面使用延时函数,增加CPU的执行效率