全部博文(321)
分类: 嵌入式
2013-03-07 22:42:12
中断指CPU在执行程序的过程中,出现了某些突发事件时CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回原程序被中断的位置并继续执行。
二、中断分类:
a) 外部中断(手册E开头的,或引出一个引脚) VS 内部中断(CPU内部的一个控制模块)
b) 可屏蔽中断 (一般都有mask位)VS 不可屏蔽中断
c) 向量中断(中断向量表) VS 非向量中断 =P190 向量:不同的中断号,非向量:同一个入口地址,再由软件判断是具体哪个中断。
三、中断上半部和下半部
上半部:紧急操作。
A.实际响应中断例行
B.用request_irq注册的中断例程
C.它在很短的时间内完成
下半部:非紧急
被顶半部调用,并在稍后更安全的时间内执行。
四、为设备实现一个中断包含两个步骤:
1.向内核注册中断
2.实现中断处理函数
五、
Q: 什么是中断处理程序,有何特别之处?
A: 中断处理程序就是普通的C代码。唯一特别的地方是中断处理程序是在中断时间内 运行的,它的行为要受某些限制:
1、处理程序不能向用户空间发送或接受数据,因为它不属于任何进程的上下文。
2、不能使用可能引起阻塞的函数。
六、软中断和硬中断:
硬中断:外部设备对CPU的中断;
软中断:通常是硬中断服务程序对内核的中断,而信号则是由内核(或其他进程)对某 个进程的中断。
中断底半部机制:
一、Tasklet
二、工作队列
三、软中断
中断共享:
在中断到来时,所有共享此中断的处理函数都会被执行,在中断处理程序顶半部中,应迅速得根据硬件寄存器中的信息比照传入的dev_id参数判断是否是本设备的中断,若不是,应迅速返回。
代码:
1、定义:struct work_struct my_key;
2、申请中断:
if((result=request_irq(keyIrqArray[0], (void *)key1_interrupt,IRQ_TYPE_EDGE_FALLING, "KEY1", NULL)) !=0)
工作队列:INIT_WORK(&(globalmem_devp->my_key),(void *)key2_do_tasklet);//初 始化等待队列
3中断处理函数
A.无上下半部机制
irqreturn_t key1_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
printk("The KEY1 is pressed...!\n");
//tasklet_schedule(&key1_tasklet);
keyvalue = 1;
keyflag = 1;
globalmem_devp->current_len = 4;
return 0;
}
B.工作队列schedule_work(&globalmem_devp->my_key);
C.tasklet==tasklet_schedule(&key3_tasklet);
4声明绑定==》对tasklet
DECLARE_TASKLET(key3_tasklet,key3_do_tasklet,0);
5执行函数: void key2_do_tasklet(unsigned long data)
6释放中断free_irq(keyIrqArray[0], NULL);
软中断和tasklet仍然运行于中断上下文,而工作队列则运行于进程上下文。因此,软中断和tasklet处理函数中不能睡眠(中断函数里面不能阻塞,或者睡眠),但是工作队列函数可以允许睡眠。
总结:顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操作。Tasklet和工作队列都是调度中断底半部的良好机制,tasklet基于软中断实现。内核定时器也是依靠软中断来实现的。
定时器:通过修改expires的值来改变定时器到来的时间值globalmem_devp->s_timer,mod_timer(&globalmem_devp->s_timer,jiffies+HZ);
一、linux通过时钟中断来确定时间间隔。时钟中断的发生频率为HZ=100,不同的体系
jiffies值就是自linux启动后的时钟滴答的次数
二、S3C2410的RTC可产生两种中断:周期节拍(tick)中断和报警(alarm)中断。前者相当于一个周期性的定时器,后者相当于一个闹钟,它在预先设定的时间到来时产生中断。
三、定时器用于控制某函数(定时器处理函数)在未来某个特定时间执行。内核定时器注册的处理函数只执行一次--不是循环执行的。
四、由于我们在第一次中断处理函数中,修改了(不断的设置)定时器到来的时间的值,即mod_timer(&globalmem_devp->s_timer(定时器),jiffies+HZ(expires));,即通过新的被-传入的expires到来后,才会执行定时器函数。
五、为了防止因jiffies溢出导致的问题,最好使用宏比较
#include
a) void udelay(unsigned long usecs);
b) void ndelay(unsigned long nsecs);
c) void mdelay(unsigned long msecs);
d) time_before(jiffies,delay)
在定时器处理函数中,在做完响应的工作后,往往会延后expires并将定时器再次添加到内核定时器链表,仪表定时器能再次触发。
五、代码:
1、定义: struct timer_list s_timer;//设备要使用的定时器
atomic_t counter;//一共经历了多少秒
2、初始化定时器:
a) init_timer(&globalmem_devp->s_timer);
b) globalmem_devp->s_timer.function = &second_timer_handle;
c) globalmem_devp->s_timer.expires = jiffies+HZ;
3、添加到内核:
a) add_timer(&globalmem_devp->s_timer);
b) atomic_set(&globalmem_devp->counter,0);//计数器清零
4、实现中断处理函数:
a) mod_timer(&globalmem_devp->s_timer,jiffies+HZ);
b) atomic_inc(&globalmem_devp->counter);
5、释放函数
del_timer(&globalmem_devp->s_timer);
总结:内核中的延时是忙等待或者睡眠等待,为了充分利用CPU资源,使系统有更好的吞吐性能,在对延迟时间的要求并不是很精确的情况下,睡眠等待同城是值得推荐的。。
忙等待延时:长延时---time_before和time_after()====类似于while(n--);
睡着延时:睡着延时在等待的时间到来之间出去睡眠状态,CPU资源被其他进程使用。
内核定时器与tasklet比较:
相同点:
1、在中断期间运行;
2、始终会在调度的同一CPU上运行;
3、软件中断上下文,院子模式运行。
不同点:
1、不能要求tasklet在给定时间执行;
2、软件中断是打开硬件中断的同时执行某些异步任务的一种内核机制。