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");
功能实现:按键控制蜂鸣器工作