您的位置:首页 > 运维架构 > Linux

Linux驱动开发———Linux中断处理程序设计

2017-05-10 20:35 239 查看
1、裸机程序的中断流程:

        A、所有的中断都有统一的入口;



        B、事先要注册中断处理程序;



        C、根据中断源的编号调用中断处理程序。



2、Linux中断处理流程:
        A、irq_svc是linux中中断的统一入口;
        B、获取中断号;
        C、根据中断号找到相应的中断;
        D、从对应的中断中取出事先准备好的中断函数来运行。



3、从对Linux中断处理程序的流程分析出我们的驱动程序需要做什么?
        A、实现中断处理程序;
        B、注册中断处理程序,即将实现的中断处理程序的中断号注册到Linux系统中。
4、Linux中断处理程序设计:



5、Linux中断注册:
        request_irq函数用于注册中断:









6、中断处理程序需要注意事项:



7、中断处理程序的实现流程:



8、注销中断:



9、程序代码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <asm/uaccess.h>/*copy_to_user依赖的头文件*/
#include <linux/uaccess.h>/*copy_to_user依赖的头文件*/
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/slab.h>
//#include <linux/wait.h>
#include <linux/sched.h>//wait_event(queue,condition)中TASK_UNINTERRUPTIBLE依赖的头文件。
#include <linux/platform_device.h>

#define GPHCON 0xE0200C40	/*tiny210v2开发板:按键K1->引脚GPH2_0->寄存器0xE0200C40*/
#define GPHDAT 0xE0200C44

/*定义一个工作指针*/
struct work_struct *work1;

/*定义一个定时器变量*/
struct timer_list key_timer;

unsigned int *gpio_data;

unsigned int key_num = 0;/*用来存放按键值*/

//定义等待队列:
wait_queue_head_t key_q;

/*定义一个变量用来存放从平台驱动对应的平台设备那里取来的中断号*/
struct resource *res_irq,*res_mem;
unsigned int *key_base;

/*定义这项工作要执行的函数*/
void work1_fuc(struct work_struct *work)
{
//printk("this is work1->\n");

/*启动定时器;*/
/*jiffies:linux系统当时时间,滴答计时,1秒计1000个*/
/*HZ/10:一个HZ就是一秒,HZ/10=100ms*/
/*jiffies+HZ/10:计时时间*/
mod_timer(&key_timer,jiffies+HZ/10);

}

/*定时器的超时函数*/
void key_timer_fuc(unsigned long data)
{
unsigned int key_val;
//key_val = readl(gpio_data)&0x01;
key_val = readl(key_base+1)&0x01;
if(key_val == 0)
{
//printk("key1 down ---- timer\n");
key_num = 1;
}

//key_val = readl(gpio_data)&0x02;
key_val = readl(key_base+1)&0x02;
if(key_val == 0)
{
//printk("key2 down ---- timer\n");
key_num = 2;
}

//有数据的时候将阻塞唤醒:
wake_up(&key_q);
}

/*实现中断处理函数*/
irqreturn_t key_int(int irq, void *dev_id)
{
//1. 检测是否发生了按键中断

//2. 清除已经发生的按键中断

//3. 打印按键值
//printk("key down!\n");

/*提交下半部*/
/*将这些与硬件无关的代码程序提交给工作,有内核自动创建线程来执行,
这样就极大的减少了中断中执行程序的时间,中断中代码越少越好*/
//3.挂载(提交)工作:
/*这个挂载和提交只是将工作提交到工作队列中,工作并没有运行*/
/*这个被挂载进队列的工作的运行是由系统内核自动创建的线程来给你运行的*/
/*至于什么时候运行也是系统自己决定的,你无需管*/
/*入口参数:工作队列指针;工作指针。指将这个工作挂载到这个工作队列中*/
//queue_work(my_wq,work1);
schedule_work(work1);

//return 0;
return IRQ_HANDLED;
}

/*按键硬件初始化函数*/
void key_hw_init()
{
//unsigned int data;
//unsigned int *gpio_config;

/*将物理地址转化为虚拟地址,长度为4个字节*/
/*将一个IO地址空间映射到内核的虚拟地址空间*/
/*从起始地址GPHCON开始的4个字节的长度映射为虚拟地址,存放在指针变量gpio_cofig中*/
//gpio_config = ioremap(GPHCON,4);
//gpio_data = ioremap(GPHDAT,4);

/*将地址(虚拟地址)gpio_config中的值读出来,放在变量data中。*/
//data = readl(gpio_config);
//data &= ~0b11;
//data |= 0b1111;
//writel(data,gpio_config);

//writel(0x000000ff,gpio_config);

writel(0x000000ff,key_base);

//创建工作:
/*给指针分配空间*/
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
/*对相应的工作进行初始化:*/
/*INIT_WORK函数的入口参数:要初始化的工作的指针;工作要执行的函数。*/
INIT_WORK(work1,work1_fuc);

}

static int key_open(struct inode *node,struct file *filp)
{
/*程序调试使用*/
//printk("in kernel : at key_open fuc!\n");

return 0;
}

/*加上这个函数之后,应用程序才可以读取到驱动程序中的数据*/
static ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
/*程序调试使用*/
//printk("in kernel :key num is %d\n",key_num);

//如果没有数据可供读,进入等待队列休眠
//入口参数:等待队列   条件
wait_event(key_q,key_num);

/*将驱动程序空间的数据传递到应用程序空间*/
/*入口参数:应用程序接收数据的地址;驱动程序被传送的数据的地址;传送的数据量*/
copy_to_user(buf, &key_num, 4);

key_num = 0;

bc0f
return 4;
}

static const struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};

/*初始化混杂设备*/
static const struct miscdevice key_miscdev = {
.minor = 250,/*注意这个混杂设备驱动模型的次设备号,如果这个号被占用,那么驱动程序将不能和用户程序通信*/
.name = "s5pv210key",
.fops = &key_fops,
};

/*定义一个函数,这个函数要被用在平台驱动初始化中*/
/*当这个驱动找到与之对应的设备之后要调用的函数*/
/*平台设备驱动只是找到平台总线上与驱动对应的设备或者与设备对应的驱动*/
/*原则:驱动和设备是要独立开的,硬件信息都是保存在设备里面的*/
/*在驱动中不保存硬件信息,以增加驱动的可移植性*/
int __devinit key_probe(struct platform_device *pdev)
{
int size,ret;

/*对按键驱动程序混杂设备的注册*/
/*注册混杂设备*/
ret = misc_register(&key_miscdev);

/*从与该驱动对应的平台设备中拿到中断号*/
res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);

//注册中断处理程序
/*注意:一个设备可以注册多个中断*/
/*IRQF_TRIGGER_FALLING:下降沿产生中断*/
/*IRQ_EINT16_31(中断号)查找文件路径: /arch/arm/mach-s5pv210/include/mach/irqs.h 中*/
//request_irq(gpio_to_irq(S5PV210_GPH2(0)),key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);
//request_irq(gpio_to_irq(S5PV210_GPH2(1)),key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);

request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);
request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);

res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
size = res_mem->end - res_mem->start +1;
key_base = ioremap(res_mem->start,size);
//按键硬件初始化
key_hw_init();

/*初始化定时器:*/
init_timer(&key_timer);
/*设置超时函数:*/
key_timer.function = key_timer_fuc;

/*向内核注册定时器:*/
add_timer(&key_timer);

//初始化等待队列:
init_waitqueue_head(&key_q);

return ret;
}

/*定义一个函数,这个函数要被用在平台函数初始化中*/
/*当与该驱动对应的设备移除的时候要调用的函数*/
static int __devexit key_remove(struct platform_device *pdev)
{
misc_deregister(&key_miscdev);
}

/*定义一个平台变量*/
struct platform_driver key_driver ={
.probe = key_probe,
.remove = key_remove,
.driver = {
.name = "my-key",
},
};

/*模块初始化函数*/
static int button_init()
{
/*注册平台驱动*/
return platform_driver_register(&key_driver);
/*对于平台驱动程序的初始化,一定是要找到设备的时候再做初始化*/
}

static void button_exit()
{
/*卸载平台驱动*/
platform_driver_unregister(&key_driver);
}

MODULE_LICENSE("GPL");

module_init(button_init);
module_exit(button_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: