本帖最后由 杨为民 于 2024-3-31 10:57 编辑
一、uC/OS-II中断外任务切换过程 (1)在单片机RTOS中进行任务调度的函数所在的位置分两类,一类是在中断ISR里,另外一类是在中断ISR外。在uC/OS-II系统中,中断里的任务调度函数是“OSIntExit()”,中断外的任务调度函数是“OS_Sched()”。 (2)在uC/OS-II系统中,中断里任务调度函数“OSIntExit()”仅在中断ISR中使用,但是中断外任务调度函数是很多任务管理函数的基础,这些函数在完成任务管理的功能后,都统一通过“OS_Sched()”函数来进行任务调度,控制RTOS系统进入新的运行状态。 比如uC/OS-II系统中的任务休眠函数: 其中在完成所有的设置后,第71行调用“OS_Sched()”进行任务调度。
(3)uC/OS-II系统建立之初,为了未来移植到其他各种CPU架构上的方便,中断外的任务调度函数“OS_Sched()”中的任务切换函数“OS_TASK_SW()”是一个宏定义。下图是“OS_Sched()”函数的程序: (4)任务调度是RTOS的核心功能和核心过程,“OS_Sched()”是uC/OS-II最重要的基础函数。 中断外任务调度函数从第1641行开始进入临界区保护开始,到第1655行退出临界区保护结束,整个uC/OS-II的中断外任务调度函数都在临界区保护之下。 (5)在uC/OS-II中,具体实现中断外任务切换的函数是上面第1651行的“OS_TASK_SW()”宏函数。 在首次引入中国的uC/OS-II的原始版本中,这个宏函数是利用80x86 CPU的软中断指令“INT”实现的。由于80x86软中断指令“INT”不受第1641行uC/OS-II进入临界区保护时关闭总中断的影响,在第1651行处产生一个真正的“软中断”,实现任务切换。在完成任务切换退出“软中断”之后,单片机继续执行第1655行的退出临界区保护程序,打开总中断,至此uC/OS-II系统才脱离临界区。 因此,从一开始起,uC/OS-II系统的中断外任务切换整个过程(包括将当前任务的寄存器现场推入堆栈、设置新任务为当前任务和恢复新任务的寄存器现场三大部分)就是在关闭总中断的临界区保护之中进行的。 二、挑战者x51的中断外任务切换方法 (6)挑战者x51的原型中的中断外任务切换方法是利用STC32G的定时器4硬中断来模拟“软中断”的替代法:
#define OS_TASK_SW() PendSv_GenerateSWInterrupt() (7)在原型中第1651行程序仅仅只是执行了上面的第164行的语句设置起定时器4的中断标志,由于这时程序仍然处于临界区保护之下,总中断是关闭的,定时器4的中断这时并不会发生,任务切换也不会开始。 (8)在原型中承担中断外任务切换功能的定时器4中断要等待执行完第1655行打开总中断退出临界区保护的程序之后才能发生,这就带来两个巨大的风险:一是如果发生任务切换中断,这时任务切换过程离开了临界区的保护,违背了uC/OS-II系统设计的原理,二是在第1655行程序打开总中断的时刻,发生中断的未必是定时器4,其他中断也有可能与其发生竞争,最终导致任务切换被延迟或者被阻塞,产生不可预料的结果。导致这种风险的根源是用硬中断模拟软中断造成的,真正的“软中断”不存在这种风险。 (9)RTOS移植玩家版与产品版的最大区别在于核心标准。 对于玩家版,“新颖性”是最重要的核心指标,它直接反映了移植者本人的技术水平,比如在目前还没有开放软中断TRAP指令的STC32G单片机上,采用硬中断模拟软中断的方法实现任务切换技术,为将来STC32G/F系列单片机RTOS探索新路径。 而对于产品版,RTOS系统的可靠性是最重要的核心指标,为此在有可能选择的情况,应该选择最可靠的方法。 (10)在一个函数中实现任务切换功能(“函数法”),然后作为宏函数对应的实体函数在上面第1651行中代替“OS_TASK_SW()”实现任务切换过程,既符合了uC/OS-II的设计初衷将整个任务切换过程置于临界区保护之下,又不会有任何中断来打断任务切换过程,保证了任务切换过程的连续性。 因此对于目前还没有开放软中断TRAP指令的STC32G单片机而言,采用“函数法”作为中断外任务切换方法对于uC/OS-II产品级移植版是一正确的选择 (11)挑战者x51移植版(V1.10)的中断外任务切换函数是从定时器4的ISR修改而来的。定时器4的ISR程序见下图: (12)在80251指令集中,中断时推4个字节的现场进系统堆栈,而“ECALL”和“LCALL”指令分别推3个和2个字节的现场进系统堆栈。
挑战者x51采用4字节的任务切换堆栈,但是CODE大模式下中断外任务切换函数的访问只推入2个字节,因此在函数法的任务切换函数开始的部分要加一段转换程序: 其中第167行和第168行弹出“LCALL”压入堆栈的16位返回地址,第172行到第175行按照硬件中断的格式在系统堆栈中建立一个供80251中断返回指令“RETI”使用的4字节堆栈内容。 经过这段程序,CODE大模式的函数访问指令堆栈就转换为硬件中断的堆栈了,这样后面的程序在将当前任务的寄存器现场进行保存的时候就会一起保存到任务堆栈中了。
(13)同样,大模式的函数返回指令“RET”只需要16位2字节的返回地址即可,因此在中断外任务切换函数的后面部分同样也要加一段转换程序: 其中第218行到第221行从新任务的任务堆栈弹出新任务的恢复点,然后将新任务的恢复点压入堆栈,在第230行打开总中断之后,完全恢复了新任务现场,实现了中断外任务切换。
(14)为了对比用中断外任务切换的“函数法”和“替代法”,本文范例只是将函数法的程序附加在挑战者x51的V1.01版本上形成V1.10版本,两种方法都提供给用户选择和测试比较。下图是用户选择界面: (15)下图是本文范例采用替代法运行效果的逻辑分析仪截图: 其中通道1是替代法的中断外任务切换信号,任务切换时间是1.125微秒。
(16)下图是本文范例采用函数法运行效果的逻辑分析仪截图: 其中通道3是函数法的中断外任务切换信号,任务切换时间是1.5微秒。 (17)函数法时间较替代法长的原因是增加了开通和结尾的转换部分,其差别是1.5/1.125=134%。 对于玩家版,“任务切换速度”是一个核心指标,它直接反映了移植者本人的编程技巧水平。 而对于产品版,RTOS系统的可靠性是最重要的核心指标,因此在任务切换速度满足要求的情况,应该选择最可靠的方法。 如果真的有实际应用需要那么高的任务切换速度,只要采用更高的工作主频即可,同时又不必牺牲RTOS系统的可靠性。
附件:本文范例源程序
|