- 打卡等级:以坛为家II
- 打卡总天数:435
- 最近打卡:2025-05-04 20:54:20
荣誉版主
- 积分
- 4275
|
发表于 2024-6-1 17:27:29
|
显示全部楼层
本帖最后由 CosyOS 于 2024-6-1 20:40 编辑
CosyOS-II “中断挂起服务FIFO队列” @ “中断挂起服务FLAG队列”
为了实现“非最低优先级中断的零中断延迟”,CosyOS 采用了“中断挂起服务”的方案。
所谓“中断挂起服务”,是指用户在中断中调用的服务,不在本地直接执行,
而是挂起到“服务层临界区”(SysTick 或 PendSV)中 间接执行,
从而实现不用关闭总中断的临界区保护方案,实现零中断延迟。
这种间接执行的中断挂起服务,在具体实施上,可以有两种方案。
方案一:“中断挂起服务FIFO队列”
把服务的相关内容保存在局部的结构体中,再把结构体指针 入 FIFO队列,而后触发PendSV。
在PendSV中,从 FIFO队列 依次取出各指针,而后执行服务。
方案一的优势:
1、FIFO队列 可保证同一中断中或不同中断中,各中断挂起服务之间可能存在的时序逻辑关系。
2、FIFO队列 可保证同一服务自身的时序逻辑关系。
什么是同一服务自身的时序逻辑关系呢?
就是某一项服务,参数是可变的,每次调用该服务时,参数可能会不同。
当存在瞬时的并发调用时,由于是 先入先出,可确保最终执行结果的正确。
3、FIFO队列 可保证同一服务,当存在瞬时的并发调用时,不会丢失执行次数。
如发送一个计数信号量,如果存在瞬时的并发调用,如果丢失了执行次数,将导致计数出错。
方案一的劣势:
入队列、触发PendSV、出队列 等操作是比较耗时的,如果大量的中断挂起服务都经常性的频繁调用,
会对系统实时性造成不利影响,甚至导致任务没有机会运行。
尤其是对于 51 来说,由于先天的缺陷,主频又低,相关操作将更为耗时。
方案二:“中断挂起服务FLAG队列”
1、在中断中,当需要调用一项服务时,仅设置一个标志位即可,再触发PendSV;
而后在 pendsv_hook 中,依次查询各标志位,为真就清零并执行相应服务。
2、在中断中,当需要定时操作时,仅设置一个标志位即可;
而后在 tick_hook 中,依次查询各标志位,为真就清零并执行定时操作。
方案二的特点:
即使出现瞬时的并发调用,在执行时,也只能执行一次服务。
方案二的优势:
1、与方案一相比,调用和执行服务的时间都将明显缩短。
2、当出现瞬时的并发调用时,由于仅执行一次服务,执行效率会更高。
3、对高速中断中每次都需要定时是非常有利的(如示例串口中断中,每接收一帧都要定时中断),
采用该方案后,即使串口波特率为1M、2M等,都不会有问题,是极为高效的。
它的高效不仅体现在,中断的执行时间仅是增加了一个 设置标志位 的时间;
而是对于高速的频繁定时来说,不管定时了多少次,都是等待进入 系统滴答中断 时,定时一次而已,
这极大的促进了整个系统的全面的高效运行。
总结:
CosyOS-II 自 V2.3.0 版本开始,将同时支持上述两种方案。
当在中断中调用一项挂起服务时,
我们可以优先考虑采用“中断挂起服务标志队列”,
如无法满足要求再考虑“中断挂起服务FIFO队列”。
采用“中断挂起服务标志队列”的条件:
1、CosyOS已提供相应的服务支持(API前缀为:p);
2、该服务与其它挂起服务之间不存在时序逻辑关系;
3、即使存在瞬时的并发调用,也仅需执行一次即可;
4、该服务的参数为常量。
以前版本的 sv_int_pend.c, sv_int_pend.h,
自 V2.3.0 版本开始,将替换为:
sv_int_pend_fifo.c, sv_int_pend_fifo.h:“中断挂起服务FIFO队列”,
sv_int_pend_flag.c, sv_int_pend_flag.h:“中断挂起服务标志队列”。
以前版本的API,前缀分为 u、d、t、i、x,
自 V2.3.0 版本开始,新增前缀 p,意为在 pendsv_hook 中调用并执行的中断挂起服务。
CosyOS-II V2.3.0 版本 新特性
1、支持的前缀为 p 的挂起服务:
pResumeTask(task)
pSuspendTask(task)
pDeleteTask(task)
pSetTaskPri(task, pri)
pSetBlock_tc(task, tc)
pSetBlock_ms(task, ms)
pSetBlock_s(task, s)
pSetBlock_m(task, m)
pSetBlock_h(task, h)
pClearBlock(task)
pLockBin(bin)
pGiveBin(bin)
未来可能还会增加其它的挂起服务支持。
2、新增服务:
/* 终止定时中断 */
uTimInt_Cancel(tmid)
tTimInt_Cancel(tmid)
iTimInt_Cancel(tmid)
/* 终止定时查询 */
uTimQry_Cancel(tmid)
tTimQry_Cancel(tmid)
iTimQry_Cancel(tmid)
3、系统配置头文件,进行了适当的结构调整,
新增 定时中断/查询设置,
用户定时查询初始化状态。
CosyOS-II V2.3.0 版本 已发布!
再补充一个示例,大家一看便能懂 两种 挂起服务方案 该如何应用。
如在 TIM2中断 中要恢复一个任务:task_3,
采用 中断挂起服务FIFO队列:
void TIM2_IRQHandler(void)
{
iResumeTask(task_3);
}
采用 中断挂起服务标志队列:
bool i_resume_task3 = false;
void TIM2_IRQHandler(void)
{
i_resume_task3 = true;
mPendSV_Set;
}
void pendsv_hook(void) MCUCFG_USING
{
if(i_resume_task3){
i_resume_task3 = false;
pResumeTask(task_3);
}
}
我们再来比较一下二者的性能差距(51为例):
采用 中断挂起服务FIFO队列:
iResumeTask(task_3); 的反汇编:(仅是入FIFO 并触发PendSV),
从 0xB593 - 0xB5A9,共15句汇编;
还要再加上 void mPendSV_FIFOLoader(s_u16_t sv) 的执行,
是连续的 JBC指令,来抢占 队列位,再 sv 入 队列,
即使能入队列首,至少也需10句汇编。
一共至少25句汇编。
采用 中断挂起服务标志队列:
i_resume_task3 = true;
mPendSV_Set;
这两句的反汇编:
仅两句 SETB。
两种方法,在性能上可谓是 天壤之别。
另外,采用 中断挂起服务标志队列,即使出现 永不停息的高速并发调用,任务仍然有执行的机会。
即使是 ARM,两种方案在性能上也会有明显的差距,只不过 可通过 高主频 来弥补。
但 中断挂起服务FIFO队列 的优势在于,只要能处理过来,在各种情况下应用都确保不会出问题。
而 中断挂起服务标志队列 的应用,则必须满足如下条件:
1、CosyOS已提供相应的服务支持(API前缀为:p);
2、该服务与其它挂起服务之间不存在时序逻辑关系;
3、即使存在瞬时的并发调用,也仅需执行一次即可;
4、该服务的参数为常量。
所以,还是要权衡利弊,针对不同的服务和应用场景,选择适合的方法。
|
|