一、前 言 单片机RTOS进行任务调度的时候首先要保存当前任务的现场,然后恢复新任务的现场,最后将CPU的执行权交给新任务,完成一次任务调度。 但是任务现场包括哪些,不同的CPU架构是不同的,即使对于同一个CPU架构,不同的RTOS设计师的考虑也会不同。8051指令集中的8个通用寄存器R0-R7,称为一个当前寄存器页。8051CPU里包含4个寄存器页,当前寄存器页到底映射到其中哪一个,由程序状态字PSW中的RS1和RS0位决定。 8051CPU的这种寄存器页结构设计初衷就是为了方便任务快速切换,但是如何保存和恢复这些寄存器页,却是有多种选择。本文介绍STC8H单片机上的微山x51-uCOSII单片机RTOS的任务现场保存与恢复方法。
二、8051单片机中断的寄存器现场的保护与恢复 (1)除了RTOS有任务调度需要保存和恢复寄存器现场之外,普通裸机中断程序也有这个问题,从“main()”。函数开始的程序是“后台任务”,中断发生后CPU进入的中断服务程序ISR后就进入了“前台任务”。 (2)虽然8051CPU有4个寄存器页,但是发生中断的时刻寄存器组R0-R7只属于由PSW决定的特定的寄存器页(下面称“当前寄存器页”),中断时如何处理当前寄存器页,下面的ISR使用哪个寄存器页的问题在操作系统理论中属于多台串行加工设备服务优先级更高的新加工订单的调度策略问题。 (3)这种插队加工调度通常有两种策略可以使用: 策略1:当前哪台设备(当前寄存器页)正在工作,就保存该设备(当前寄存器页)的现场,然后继续使用这台设备进行新加工,加工完后恢复该设备的现场(当前寄存器页),继续进行当前的加工。 策略2:不管当前是哪台设备在工作,只认准0#设备(寄存器页0)处理新加工,这种策略很简单,保存0#设备(寄存器页0)的现场,然后使用0#设备(寄存器页0)进行新加工,新加工完成后恢复0#设备(寄存器页0)的现场,继续进行原来的加工。 (4)策略1的一个例子是某个uC/OS-II移植到STC8单片机上寄存器现场保存与恢复方法: 由于8051指令集中并没有直接将通用寄存器R0-R7推入和弹出堆栈的指令,只有将绝对的DATA地址字节推入和弹出堆栈的指令,因此上面的程序中只有“PUSH ACC”和“POP ACC”的指令,其他R0-R7寄存器需要通过“MOV”指令迁移到“ACC”中去。 不过从绝对可靠性的角度看,采用策略1的上面的程序存在很大瑕疵:如果在用“POPALL”恢复任务时PSW的RS0和RS1与保存任务时不一样的话,寄存器就恢复到其他寄存器页中了,会产生极大的隐患。
所以如果要采用策略1,应该最后推PSW入栈,最先恢复PSW,保证恢复到正确的寄存器页: (5)策略2的典型例子是Keil的C51编译器中断时的寄存器现场保存与恢复方法,下图是某个程序定时器0的ISR程序: 其中在第679行保存当前的PSW后,不管当前寄存器页是多少,第680行PSW=0(RS1=0,RS0=0)强制以下使用0#寄存器页,然后第681行到685行0#寄存器页的寄存器保存起来,最后在第701到705行把0#寄存器页的寄存器恢复出来。 显然如果当前寄存器页不是0#,那么既没有保存当前的寄存器页,也没有恢复当前的寄存器页。 Keil的C51采用策略2有三个原因:一是策略2非常明确指定了ISR使用0#寄存器页,二是C51编译器将C语言编译为汇编语言时缺省选0#寄存器页,三是如果用户使用“USING”指令或者对PSW的RS0和RS1赋值改变了寄存器页,那么谁改变,谁负责保存和恢复寄存器页。 三、 微山x51-uCOSII单片机RTOS的最佳策略 (6)RTOS只是一个平台程序,不是最终的用户程序,因此对于8051单片机上的RTOS,设计的第一决策就是允不允许用户自由地使用除0#外的其他寄存器页。微山x51-uCOSII决定允许。 (7)但是如果允许用户自由地使用全部寄存器页,那么一个任务的现场就不仅仅是进行任务切换时的当前寄存器页,用户可能之前已经使用了其他寄存器页来保留数据了,这时当前任务的寄存器现场就是全部寄存器页,具体地说就是DATA地址从00H到1FH的32个字节数据。 (8)考虑到Keil的C51编译器缺省是0#寄存器页,并且C51绝不自己改变寄存器页的特点,微山x51-uCOSII决定将任务的寄存器现场分为两个部分,0#寄存器页(DATA地址从00H到07H的8个字节数据)和DATA地址从08H到1FH的24个字节的寄存器扩展部分。 这样设计的优点是0#寄存器页是常规的,也是必须得,但是寄存器扩展部分却是可以灵活处理的。 (9)对于STC8H系列单片机,有很多XSFR的地址位于XDATA空间,为了避免在存取XDATA区域数据时产生误操作,专门使用了“P_SW2”来控制,使用XSFR时先打开,然后对XSFR进行系列操作,操作完成后再将其关闭。在这个过程中,如果任务被切换了,那么“P_SW2”的状态应该被保存起来,等待任务切换回来时,“P_SW2”的状态也要被恢复,也就是说对于STC8H单片机“P_SW2”也是一个像“DPH”和“DPL”要保存和恢复的寄存器现场。同样STC8H的操作双指针的“DPS”、“DPH1”和“DPL1”SFR寄存器在进行任务切换时也应该被保存和恢复。
(10)根据上面的考虑,微山x51-uCOSII的用于保存和恢复任务现场的任务堆栈的定义如下(任务堆栈初始化函数): 其中第34行程序从堆栈的06字节开始为1#、2#和3#寄存器页数据保存区域。 从上面可以看出除了一般8051的寄存器外,第93行到第98行还有4个STC8系列单片机的特有寄存器。
(11)在微山x51-uCOSII中,对0#寄存器页的保存和恢复程序如下: 其中第108行强制选择0#寄存器页,“Data_00H-Data_07H”直接指定DATA空间的绝对地址: (12)在微山x51-uCOSII中,保存和恢复任务扩展的寄存器页的程序如下:
其中对0A6H=P26的操作是在逻辑分析仪上测量复制扩展寄存器页的时间,当主频为33.1776MHz的时候,复制24字节的时间约6微秒,切换一次任务要12微秒。 (13)这个复制全部32字节寄存器页的方案虽然最可靠,但是每次任务切换要多花12微秒的时间,对于那些对实时性要求高的场合就不适合了。就像在一般的51单片机程序中也很少需要用到0#以外的寄存器页一样,绝大多数情况RTOS用户都很少使用0#以外的寄存器页,为了避免在每次任务切换都要多花12微秒,在微山x51-uCOSII中特别设置了一个公共变量“UCX51_BIOS_REGBANK_SAVE_FLAG”,然后根据这个变量的值来决定是否保存和恢复扩展寄存器页,比如: MOV A,UCX51_BIOS_REGBANK_SAVE_FLAG; JZ uCx51_Restore_TCBHighRdy_39; (14)用户在编写自己的RTOS程序时,随时都可以设置这个变量的值为0或者非0来控制。因此微山x51-uCOSII的这种在任务切换时保存和恢复任务寄存器页的策略既可靠又提供了灵活性,并且还加入了对STC8H单片机的特殊工作寄存器保护和恢复,是一种最佳策略。
|