Linux驱动开发——按键为例介绍Linux内核中断
Linux内核中断1. 简介2. linux内核中中断的注册与注销3. 代码4. 测试1. 简介 linux的中断处理过程和ARM裸板中的中断处理过程是一致的。不同点在于裸板开始时所有的软件都是自行编程完成的,在linux中很多中断相关的代码内核已经实现完毕了,某个中断产生后应该做什么样的具体工作没有完成,这就需要我们自己编程实现。 &nb
·
Linux内核中断
所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!
关于 LINUX驱动 的其它文章请点击这里: LINUX驱动
1. 简介
linux的中断处理过程和ARM裸板中的中断处理过程是一致的。不同点在于裸板开始时所有的软件都是自行编程完成的,在linux中很多中断相关的代码内核已经实现完毕了,某个中断产生后应该做什么样的具体工作没有完成,这就需要我们自己编程实现。
● 中断服务程序有如下特点:
(不属于) 1)中断处理程序不属于进程,它运行于中断上下文
(不交换) 2)在中断上下文中不能做用户空间和内核空间的数据交互(linux不允许,STM32中裸板中可以)
copy_to_user/copy_from_user /kmalloc
(不阻塞) 3)在中断上下文中不允许执行引起阻塞或者睡眠的函数,如:
sleep //睡眠函数
recv //阻塞函数
(栈) 4)中断使用的栈为独立的栈空间,栈空间为一个4KB的内存页
(快) 5)要求对应的处理过程,执行速度越快越好
2. linux内核中中断的注册与注销
● 注册
/*
@function:注册中断服务程序
@para:
【irq】: 中断号,内核中将所有的中断源做了统一的编号, 获取linux内核中断号的两种方式:
1)int gpio_to_irq(unsigned gpio)----将管脚编号gpio转换为对应的中断编号
2) xxx_irq.h----IRQ_GPIO_A_START + index
【handler】: 要注册的irq号中断源对应的中断服务程序(用户端)
btn_isr 也被称作回调函数/钩子函数 (strcmp/open调用别人写的代码,而我们写的函数,被系统调用,就叫做钩子函数)
【flags】: 设置哪种情况下触发irq号中断
IRQF_TRIGGER_RISING, 上升沿触发中断
IRQF_TRIGGER_FALLING
IRQF_TRIGGER_HIGH,高电平触发中断
IRQF_TRIGGER_LOW
IRQF_SHARED: 共享中断
【name】: 名称
【dev】: 调用handler 函数时传递的参数(用户调用使用的)
@return:注册成功返回0 失败返回非0
*/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
● 注销
/*
@function:注销中断服务程序
@note:当调用request_irq(....,dev) 当调用free_iqr(.......,dev) 最后一参数要保持一致 不然注销失败
*/
void free_irq(unsigned int irq, void *dev)
3. 代码
硬件上使用了四个按键,都是下降沿触发。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/interrupt/h>
MODELE_LICENSE("GPL");
struct btn_desc
{
int irq; //中断号
char *name;
}
struct btn_desc btns[]=
{
{IRQ_GPIO_A_START+28, "K1"},
{IRQ_GPIO_B_START+30, "K2"},
{IRQ_GPIO_B_START+31, "K3"}
{IRQ_GPIO_B_START+9, "K4"},
}
/* 回调/钩子函数, 用户自定义
typedef irqreturn_t (*irq_handler_t)(int, void *);
第一个参数:中断号
第二个参数:用户参数
当按键触发的时候的时候,会打印 K1~K4 */
irqreturn_t btn_isr(int irq, void *dev)
{
//int test = *((int *)dev);
sturct btn_sesc *pdata = (sturct btn_desc *)dev;
printk("%s press!\n",pdate->name);
return IRQ_HANDLED;
}
//初始化:注册中断服务函数
int __init btn_drv_init(void)
{
//int irq = gqio_to_irq(PAD_GPIO_A + 28); //获取终端号
for(i=0; i<ARRAY_SIZE(btns); i++))
{
//注册中断服务函数 约定一下,按键的时候才使用,具体见上文的函数解释
int ret = request_irq(btns[i].irq, btn_isr, IRQF_RTIGGER_FALLING, btns[i].name, (void *)&(btns[i]));
if(ret)
{
printk("request_irq failed!\n");
return -EAGAIN;
}
}
return 0;
}
//注销中断服务程序
void __exit btn_drv_exit(void)
{
int i = 0;
for(i=0; i<ARRAY_SIZE(btns); i++)
{
//int irq = gqio_to_irq(PAD_GPIO_A + 28);
free_irq(btns[i].irq, (void *)&(btns[i]));
}
}
...
module_init(btn_drv_init);
module_exit(btn_drv_exit);
4. 测试
向内核加载模块:
insmod btn_drv.ko
insmod: can't insert 'btn_drv.ko': Resource temporarily unavailable
#原因: 内核中自带了按键的驱动程序,在其中通过request_irq的方式注册了K1对应的中断服务程序,导致再次调用request_irq时注册失败
解决方法:
make menuconfig
Device Drivers --->
Input device support --->
[*] Keyboards --->
< > SLsiAP push Keypad support
make uImage #让开发板使用新内核
cp arch/arm/boot/uImage /tftpboot/
tftp 48000000 uImage
mmc write 48000000 800 3000
重启后,再向内核加载模块:
insmod btn_drv.ko
# 按下K1观察实验效果
查看中断信息:
cat /proc/interrupts
#中断号 中断产生的次数
134: 0 GPIO K1
关于 LINUX驱动 的其它文章请点击这里: LINUX驱动
更多推荐
已为社区贡献2条内容
所有评论(0)