|
本帖最后由 lulu爱 于 2013-11-17 11:19 编辑
中断上下半部原理以及实践
中断一般可以分成上半部以及下半部,所谓中断上半部cpu通常是关中断的,这个时候主要是从硬件那里接收一些数据,然后触发中断下半部。一出中断后,根据相应的中断下半部的优先级去执行相应的中断下半部。中断下半部主要用于数据的处理,数据的处理过程一般比较缓慢,所以这个时候中断往往是打开的。为什么要有中断上下半部的原因主要有如下:
如果中断函数内处理的事情非常多的话,意味着中断运行时间过长。这个时候cpu 通常是关中断的,这样会无法及时相应其它的中断,造成其他中断数据丢失。对于cortex-m系列的cpu进入中断后是开着中断的,但是中断运行时间太长的话,其它低优先级的中断也得不到运行,因为被高优先级的任务占着中断不放,通常也会引发低优先级中断的数据丢失。所以尽量,尽可能减低中断内处理的时间,是刻不容缓,非常重要的事情,直接决定了整个系统的实时性。
中断下半部是人为的划分,这个时候是处于任务环境下,所以可以响应中断。中断下半部的划分主要是首先判断某具体中断内的执行时间,如果时间很长的话,就需要把数据以及数据处理部分人为的划分开来,接收数据部分往往叫中断上半部,处理数据部分往往叫中断下半部。
中断的下半部处理是可以是有优先级的,有的硬件设备的中断数据处理可以延迟处理,但是有的必须马上要处理,事情处理有轻重缓急之分,这也是为什么中断下半部有task 0 和workqueue, idle event的原因。
下图是raw os中可能使用到的中断流程:
raw os支持的中断下半部方法有task 0 以及work queue, task 0 的优先级要高于work queue。work queue的优先级是高于idle event 的。task 0 和work queue的区别是task 0 运行的时候不能调用能引起睡眠的函数,但是work queue是能调用引起睡眠的函数的, idle event是基于事件驱动的,优先级是最低的,也可以做为中断下半部去使用,如果这个中断的处理优先级是很低的话, idle event也是不能允许睡眠的。
raw os的中断下半部最具特色的是支持上半部向下半部传递数据,这一点非常重要,把数据接收和数据处理彻底的分割开来。有些系统比如linux,上半部是无法向下半部传递数据的,因为内核的api不支持这个特性。
综上所述,raw os为软件化的区分中断优先级提供了有效地手段,即task 0,workqueue以及idle event。对于系统的快速中断响应有了根本的保障。
task 0 api 使用
1 RAW_U16 raw_task_0_post(EVENT_HANLDER *p, TASK_0_EVENT_TYPE ev, void *event_data)
函数功能:
往任务0发出一个事件消息,此函数必须在中断里使用,用来触发中断下半部task 0。使用此函数必须注意任务事件信息被耗尽的情况,一旦发生被耗尽的情况,函数会返回RAW_TASK_0_EVENT_EXHAUSTED,交给用户去处理。一般情况下,可以开大MAX_TASK_EVENT 的定义,如果还是不行的话,说明系统中断太快,task 0任务处理不了,只能丢弃信息。需要注意的是,此函数会发往task 0 事件队列的尾部。
此函数的参数有3个,分别含义如下:
p:为事件触发函数
ev:为事件触发类型
event_data:为事件的私有数据
函数的返回值:
RAW_NOT_CALLED_BY_TASK:此函数应该使用在中断里面。
RAW_TASK_0_EVENT_EXHAUSTED:task 0 任务的事件信息被耗尽。
RAW_SUCCESS:函数调用成功。
2 RAW_U16 raw_task_0_front_post(EVENT_HANLDER *p, TASK_0_EVENT_TYPE ev, void *event_data)
函数功能:
往任务0 发出一个事件消息,此函数必须在中断里使用,用来触发中断下半部task 0。使用此函数必须注意任务事件信息被耗尽的情况,一旦发生被耗尽的情况,函数会返回RAW_TASK_0_EVENT_EXHAUSTED,交给用户去处理。一般情况下,可以开大MAX_TASK_EVENT 的定义,如果还是不行的话,说明系统中断太快,task 0任务处理不了,只能丢弃信息。需要注意的是,此函数会发往task 0 事件队列的头部,做为紧急事件去处理。
此函数的参数有3个,分别含义如下:
p:为事件触发函数
ev:为事件触发类型
event_data:为事件的私有数据
函数的返回值:
RAW_NOT_CALLED_BY_TASK:此函数应该使用在中断里面。
RAW_TASK_0_EVENT_EXHAUSTED:task 0 任务的事件信息被耗尽。
RAW_SUCCESS:函数调用成功。
3 void task_0_tick_post()
函数功能:
此函数在系统定时器中使用.如果CONFIG_RAW_TASK_0 使能打开,系统时钟函数必须使用task_0_tick_post, 反之使用raw_time_tick。
函数的返回值:
RAW_NOT_CALLED_BY_TASK:此函数应该使用在中断里面。
task 0 系统内部api
1 void task_0_tick_handler(RAW_U8 ev, RAW_VOID *event_data)
函数功能:
task 0 内部的系统时钟函数。
此函数的参数有2个,分别含义如下:
ev:为事件触发类型
event_data:为事件的私有数据
函数的返回值:
无
2 static void task_0_switch()
函数功能:
task 0 内部调度切换
函数的返回值:
无
3 static void task_0_process(void *pa)
函数功能:
task 0 任务主体,主要来处理中断那里来的事件,没有事件处理的时候,就切换给其他任务继续运行。
此函数的参数有1个,含义如下:
pa:无效参数
函数的返回值:
无
4 void int_msg_handler(RAW_U8 ev, RAW_U8 *msg_data)
函数功能:
task 0 任务中处理中断发过来的事件,这些事件都是中断内部使用到的raw os api.
此函数的参数有2个,分别含义如下:
ev:为事件触发类型
msg_data:为事件的私有数据
函数的返回值:
无
5 void int_msg_init()
函数功能:
task 0 内部消息初始化。
函数的返回值:
无
6 RAW_U16 int_msg_post(RAW_U8 type, void *p_obj, void *p_void, RAW_U32 msg_size, RAW_U32 flags, RAW_U8 opt)
函数功能:
raw os 内部api 在中断里面的消息发送,消息发送给task 0, 出中断后系统如果没有关抢占的话,task 0 会去处理消息。
函数的返回值:
无
7 void raw_task_0_init()
函数功能:
task 0 使用前必须的初始化,有CONFIG_RAW_TASK_0 使能。
函数的返回值:
无
task 0 使用总结
raw os 中的task 0 相当于linux 中的software irq 和tasklet,属于高优先级的中断下半部,需要注意的是task 0 内部的事件处理函数是不能睡眠的,这一点和linux中的software irq 和tasklet是完全一致的。
以上代码在中断中触发一个work queue的下半部,出了中断后将会在未来一定时间运行此work queue的注册函数。
work queue 原理以及实践
task 0 是中端下半部中优先级最高的,而且有不能睡眠的特点。有的时候往往中断下半部的处理不需要很急,如果都往task 0 内部去做的话,会很大的增加系统任务最大延迟时间,对实时性损害很大。这个时候workqueue是最好的选择,可以创建一到几个系统的work queue, 专门用来处理系统中的下半部。由于work queue可以指定优先级,比task 0 要灵活的多, 更重要的是work queue内部允许睡眠。
以下是演示work queue代码使用:
以下代码初始化了整个work queue系统。
void task1(void * arg)
{
global_work_queue_init(); (1)
work_queue_create(); (2)
}
以下代码注册了用户的work queue回调函数,当这个回调函数执行时会动态传入两个参数一个指针以及一个RAW_U32 的数据。
static void user_handler(RAW_U32 arg, void *msg)
{
/*doing something*/ (3)
}
以下代码在中断中触发了事先注册的work queue。
isr()
{
sche_work_queue(arg1,arg2,arg3,arg4); (4)
}
以上(1)处代码是使用work queue 之前必须要初始化work queue 系统。(2)处代码的地方创建一个work queue供使用。(4)处代码在中断内会触发事先注册的work queue,(3)处代码会获得执行。
raw os 的work queue设计灵活之处是,调用sche_work_queue可以动态的传入参数,像linux这样的系统也不具备像work queue 动态传入参数的能力。
work queue api 使用
1 void global_work_queue_init(OBJECT_WORK_QUEUE_MSG *work_queue_msg, RAW_U32 size)
函数功能:
初始化整个workqueue的系统,用户使用workqueue之前必须先调用此函数。
此函数的参数有2个,分别含义如下:
work_queue_msg为类型为OBJECT_WORK_QUEUE_MSG的空间起始位置。
size为有多少个work_queue_msg。
函数的返回值:
无
2 RAW_U16 work_queue_create(WORK_QUEUE_STRUCT *wq, RAW_U8 work_task_priority, RAW_U32 work_queue_stack_size, PORT_STACK *work_queue_stack_base, RAW_VOID **msg_start, RAW_U32 work_msg_size)
函数功能:
创建一个workqueue供中断下半部来使用,workqueue 内部的函数可以睡眠。这个是和task 0所不同的。
此函数的参数有6个,分别含义如下:
wq为work queue的控制块地址。
work_task_priority为work queue内部任务的优先级。
work_queue_stack_size为work queue内部任务的栈大小。
work_queue_stack_base为work queue内部任务的栈起始地址。
msg_start为work queue内部queue的消息起始地址。
work_msg_size为work queue内部queue的消息长度。
函数的返回值:
RAW_SUCCESS:成功创建work queue。
3 RAW_U16 sche_work_queue(WORK_QUEUE_STRUCT *wq, RAW_U32 arg, void *msg, WORK_QUEUE_HANDLER handler)
函数功能:
触发调度一个work queue,此函数会唤醒外部的一个work queue任务,并传递消息给外部的handler去处理。
此函数的参数有4个,分别含义如下:
wq为work queue的控制块地址。
arg为传递给handler的参数。
Msg为传递给handler的参数。
handler为该work queue的处理函数。
函数的返回值:
RAW_WORK_QUEUE_MSG_MAX:work queue 内部消息耗完,用户可能要增加消息总空间。
RAW_SUCCESS:成功触发调度一个work queue。
work queue内部api
1 static void work_queue_task(void *pa)
函数功能:
work queue内部任务,用来实现work queue的功能。
此函数的参数有1个,含义如下:
pa为无效参数,可以忽略。
函数的返回值:
无。
raw os 官网地址为:www.raw-os.org |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|