Linux内核中断

2023-12-27 21:32:58

Linux内核中断

ARM里当按下按键的时候,他首先会执行汇编文件start.s里面的异常向量表里面的irq,在irq里面进行一些操作

再跳转到C的do_irq();

进行操作:1)判断中断的序号;2)处理中断;3)清除中断;

Linux内核实现和ARM裸机实现中断的原理是一样的。

内核:当按键按下后依然到异常向量表,再到handler_irq函数(写死的),在handler_irq里面定义了一个数组,数组中每个成员里面存放的是结构体,在结构体里面有个函数指针,这个函数指针就指向了咱们自己提交函数的名字;(数组的下标是Linux内核的软中断号,它和硬件中断号之间有个映射关系)。内核实现中断时,在handler_irq函数里面把中断的寄存器都初始化好了,咱们只需要拿到软中断号,绑定我的中断处理函数就可以

1、注册中断

int?request_irq(unsigned?int?irq,?irq_handler_t?handler,?unsigned?long?flags,const?char?*name,?void?*dev)
功能:注册中断??
参数:
	@irq :?软中断号?gpio的软中断号
    @handler:?中断的处理函数
    @flags?:中断的触发方式
    @name :名字???cat?/proc/interrupts
	@dev  :向中断处理函数中传递参数?,不想传就写为NULL
返回值:成功0,失败返回错误码

参数1:@irq :?软中断号?gpio的软中断号

//0-159 -> 160????GPIOB15?- >1*32+15???GPIOC7?-2*32+7

???软中断号?=?gpio_to_irq(gpino号);//160--》?0-159

gpiono?=?m*32+n(n:组内的序号)

m:那一组??A?B?C?D?E(5组)

??0 1 2 3 4

例:?gpioa28?= 0*32+28???gpiob8?=1*32+8???gpiob16?= 1*32+16

控制器中断号(ADC):

find?-name?irqs.h(在内核源码中找)

find?-name?irqs.h (位置路径./arch/arm/mach-s5p6818/include/mach/irqs.h)

find?-name?s5p6818_irq.h?(位置路径./arch/arm/mach-s5p6818/include/mach/s5p6818_irq.h)

#define?IRQ_PHY_ADC???(41?+?32)??//IRQ_PHY_ADC软中断号

参数2:@handler:?中断的处理函数

irqreturn_t?(*irq_handler_t)(int?irqno,?void?*dev);

IRQ_NONE????????//中断没有处理完成

IRQ_HANDLED?????//中断正常处理完成

参数3:@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?(低电平触发)

参数4:@name :名字???cat?/proc/interrupts

参数5:@dev :向中断处理函数中传递参数?,不想传就写为NULL

2、注销中断

void?free_irq(unsigned?int?irq,?void?*dev_id)
	功能:注销中断
	参数:
		@irq :软中断号
		@dev_id:向中断处理函数中传递的参数,不想传就写为NULL????

Eg:按键所对应的中断号是多少?及找所对应的GPIO

第一步:找底板原理图,找到按键

第二步:拷贝网络标号,到核心板

及对应的软中断号为:gpio_to_irq gpiob8?=?1*32+8);

gpio_to_irq gpiob16?=?1*32+16

ARRAY_SIZE计算数组里面元素的个数;

中断号占用问题

[root@farsight]#insmod?farsight_irq.ko?

[?21.262000]?request?irq146?error

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

通过?cat?/proc/interrupts

146:????????GPIO??nxp-keypad

154:????????GPIO??nxp-keypad

说明中断号已经被占用了

解决办法:在内核中将这个驱动删掉

1、如何确定驱动文件的名字是谁?

1)grep?"nxp-keypad"?*?-nR

arch/arm/mach-s5p6818/include/mach/devices.h:48:

#define?DEV_NAME_KEYPAD??"nxp-keypad"

2)grep?"DEV_NAME_KEYPAD"?*?-nR

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

3)驱动文件的名字是nxp_io_key.c

4)找宏的名字,在Makefine里面知道;

2、如何从内核中将他去掉?

选项菜单的名字?Kconfig

config?KEYBOARD_NXP_KEY

tristate?"SLsiAP?push?Keypad?support"

5)make?menuconfig

<>SLsiAP?push?Keypad?support

去掉图形化界面里面的*号后,可以把nxp_io_key.o删除掉,这样再次编译内核的时候就可以看出来nxp_io_key.c是否备编译,如果被编译就有对应的.o生成,如果不被编译,就不会生成nxp_io_key.o文件。

6)rm?nxp_io_key.o

3、make?uImage??重新编译内核

7)make?uImage

8)cp??arch/arm/boot/uImage?~/tftpboot

4、重新启动板子;
5、安装驱动:

6、然后按键,进行测试;

按键蜂鸣器驱动函数练习

驱动:

#include?<linux/init.h>
#include?<linux/module.h>
#include?<linux/printk.h>
#include?<linux/fs.h>
#include?<linux/uaccess.h>
#include?<asm/io.h>
#include?<linux/device.h>
#include?<linux/interrupt.h>
#include?<linux/gpio.h>

#define?GPIONO(m,n)?(m*32+n)
#define?GPIO_NOB8?GPIONO(1,8)
#define?GPIO_NOB16?GPIONO(1,16)
#define?NAME?"chrdev_dev"

//定义宏保存物理地址基地址
#define?BUZZER_BASE?0xc001c000
int?gpiono[]={GPIO_NOB8,GPIO_NOB16};
char *name[]={"interrupt_b8","interrupt_b16"};
int?i;


//定义指针保存映射后的虚拟地址首地址
unsigned int *buz_addr?= NULL;
//open??read??write??close
//中断处理函数
irqreturn_t?irq_handler(int?irq,void *arg)
{
    if(irq?== gpio_to_irq(GPIO_NOB8))
    {
        *buz_addr?|= (1 << 14);       //喇叭关闭
        printk(KERN_ALERT"+++++++++++++++++++++++++++++++++++\n");//设置为大于终端打印权限,不然只能在demsg中查看
    }
    if(irq?== gpio_to_irq(GPIO_NOB16))
    {
        *buz_addr?&= (~(1 << 14));       //喇叭关闭
        printk(KERN_ALERT"-----------------------------------\n");
    }
    return?IRQ_HANDLED;
}
static int?__init?interrupt_init(void)
{
    //建立虚拟地址和物理地址之间的映射关系-控制喇叭
????buz_addr?= (unsigned int *)ioremap(BUZZER_BASE, 40);
    if (buz_addr?== NULL)
    {
        printk("ioremap?red?err.\n");
        return -EINVAL;
    }
    //初始化喇叭
    *(buz_addr?+ 8) &= (~(3 << 28)); //选择GPIOc14功能
    *(buz_addr?+ 8) |= (1 << 28);    //选择GPIOc14功能
    *(buz_addr?+ 1) |= (1 << 14);    //选择输出使能
    *buz_addr?&= (~(1 << 14));       //喇叭关闭
    //注册中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        if(request_irq(gpio_to_irq(gpiono[i]),irq_handler,IRQF_TRIGGER_FALLING,name[i],NULL)!=0)
        {
            printk("%s?request_ire?err.\n",name[i]);
            return -EINVAL;
        }
    }
    return 0;
}

static void?__exit?interrupt_exit(void)
{
    //注销中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        free_irq(gpio_to_irq(gpiono[i]),NULL);
    }
    //取消映射
    iounmap(buz_addr);
    //?//注销字符设备驱动
    //?unregister_chrdev(major,?NAME);
}
module_init(interrupt_init);
module_exit(interrupt_exit);
MODULE_LICENSE("GPL");

功能实现:按键控制蜂鸣器工作

文章来源:https://blog.csdn.net/m0_74937538/article/details/135254723
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。