OpenVINOTM,给你看得见的未来!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » ucos51临界区BUG的保守解决方案

共1条 1/1 1 跳转至

ucos51临界区BUG的保守解决方案

助工
2015-05-06 16:38:10    评分
方法一: 
    执行这两个宏的第一个也是最简单的方法是在OS_ENTER_CRITICAL()中调用处理器指令来禁止中断,以及在OS_EXIT_CRITICAL()中调用允许中断指令。 
    缺点:在这个过程中还存在着小小的问题。如果用户在禁止中断的情况下调用μC/OS-Ⅱ函数,在从μC/OS-Ⅱ返回的时候,中断可能会变成是允许的了!如果用户禁止中断就表明用户想在从μC/OS-Ⅱ函数返回的时候中断还是禁止的。在这种情况下,光靠这种执行方法可能是不够的。嵌套调用OS_ENTER_CRITICAL()/OS_EXIT_CRITICAL()对时,会引发错误。 
    优点:(1)速度快;(2)避免关闭中断后调用PEND阻塞类API函数引起死机。 

方法二: 
    执行OS_ENTER_CRITICAL()时先将中断禁止状态保存到堆栈中,然后禁止中断。而执行OS_EXIT_CRITICAL()时只是从堆栈中恢复中断状态。 
    缺点:(1)总指令周期长,速度慢; 
          (2)如果用户在中断禁止的时候调用μC/OS-Ⅱ服务,其实用户是在延长应用程序的中断响应时间。 
          (3)用户的应用程序还可以用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来保护代码的临界段。但是,用户在使用这种方法的时候还得十分小心,因为如果用户在调用象OSTimeDly()之类的服务之前就禁止中断,很有可能用户的应用程序会崩溃。发生这种情况的原因是任务被挂起直到时间期满,而中断是禁止的,因而用户不可能获得节拍中断!很明显,所有的PEND调用都会涉及到这个问题,用户得十分小心。一个通用的办法是用户应该在中断允许的情况下调用μC/OS-Ⅱ的系统服务! 
    优点:如果用这个方法的话,不管用户是在中断禁止还是允许的情况下调用μC/OS-Ⅱ服务,在整个调用过程中都不会改变中断状态。 

结论: 
    哪种方法更好一点?这就得看用户想牺牲些什么。如果用户并不关心在调用μC/OS-Ⅱ服务后用户的应用程序中中断是否是允许的,那么用户应该选择第一种方法执行。如果用户想在调用μC/OS-Ⅱ服务过程中保持中断禁止状态,那么很明显用户应该选择第二种方法。 

    ucos书上给出的例子是基于PC机的,它运行的环境是Windows下的DOS仿真。由于此时CPU工作在保护模式,程序运行在V86模式,中断被Windows操作系统接管了,ucos无法实际关闭中断。而且在ucos启动前(OSStart),就有时钟中断存在。作者给出的ucos在DOS环境下的演示方案是:所有例子只提供一个时钟中断,没有其他任何IO中断存在,开关中断只影响tickISR,用户根本不用关心中断是否是允许的,用方法一或方法二入出临界区都可以。“用户必须在开始多任务调度后(即调用OSStart()后)允许时钟节拍中断。换句话说,就是用户应该在OSStart()运行后,μC/OS-Ⅱ启动运行的第一个任务中初始化节拍中断。通常所犯的错误是在调用OSInit()和OSStart()之间允许时钟节拍中断。”对此,作者给出的方案是,创建第一个任务TaskStart ,在这个任务里把ucos的tickISR挂接到硬时钟中断上,然后创建需要的各种任务,接下来死循环周期采样判断是否有按键退出。这样满足了允许时钟节拍中断的时机要求。 
    对于51上的ucos,由于有多个IO中断,OS_ENTER_CRITICAL()/OS_EXIT_CRITICAL()对有时嵌套调用,所以,我保守地建议使用第二种方法入出临界区。虽然执行速度慢,但稳定可靠。只要用户遵循“在中断允许的情况下调用μC/OS-Ⅱ的系统服务!”原则,就不会死机。 
    1。由于#pragma关键字不能出现在H头文件里,所以必须手工修改所有的OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()为“4.入出临界区标准代码”所示的样子。用户程序使用入出临界区保护代码时也需手工粘贴此代码,然后按“3.KEILC51内嵌汇编的方法”进行编译。虽然不如宏定义方便,但也不是很麻烦。 

    2。使用第二种方法,需要在OSIntCtxSw中调整SP指针去掉在调用OSIntExit(),OS_ENTER_CRITICAL(),OSIntCtxSw()过程中压入堆栈的多余内容(SP=SP-5,2+1+2),如下所示: 
    OS_CORE.C文件:51堆栈由下至上 
      OSIntExit  //2字节返回地址 
        OS_ENTER_CRITICAL()  //PUSH IE; 1字节 
          OSIntCtxSw()  //2字节返回地址 

    3。上电后51自动关中断,在什么地方打开中断最合适呢?在OSStartHighRdy退出时SETB EA开中断最合适! 
    为了减少代码量,OSCtxSw、OSIntCtxSw和OSStartHighRdy合用退出代码段,会不会有问题呢?不会! 
    任务切换只会发生在1、复位;2、tick中断;3、中断;4、系统API调用四个阶段。复位后打开中断,允许系统正常运行是必然的,在OSStartHighRdy退出时必须打开中断。如果是2、3情况引发的调度,中断本来就是打开的,OSIntCtxSw退出时打开中断毫无影响。按照“在中断允许的情况下调用μC/OS-Ⅱ的系统服务!”原则,用户必须在打开中断的情况下调用系统API,所以OSCtxSw退出时打开中断也毫无影响。虽然此时打开中断多此一举,浪费了代码空间和执行时间,但强制退出时开中断,可以稍微起到保护系统不死机的作用(例如:调用系统API前关中断的错误情况)。 
     
    通过上面三步,入出临界区的BUG得以完整解决,我仔细审查了一下,觉得这个思路比较严密,应该是比较健壮了,希望网友们继续提出意见和建议,让我们一起把它做得更完善。 


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]