搜索
bottom↓
回复: 17

关于UCOS2 在定时器中断内 是否要保存SP的疑惑

[复制链接]

出0入0汤圆

发表于 2009-10-15 14:04:39 | 显示全部楼层 |阅读模式
ucos运行于atmega8l,时钟用内部1MHz晶振,系统时钟采用time2比较器,10ms一次中断。
以下是我自己的理解,如果有误,请指出。
ucos的任务调度分为OSCtxSw和OSIntCtxSw

1.正常情况下常情况下 OSTimeDlay调用OS_Sched调用 OSCtxSw  
OSCtxSw的过程{PUSHRS, PUSHSREG ,SaveSPToTcb, LoadSPFromTcb,POPSREG,POPSP}

2.如果发生定时比较器中断,则调用OSTickISR{}
OSTickISR{}的过程{PUSHRS,PUSHSREG,OSIntNesting++,OSTimeTick,OSIntExit,POPSREG,POPSP}
OSIntCtxSw的过程{LoadSPFromTcb,POPSREG,POPSP}

我不解的地方就在2处。OSTickISR中在PUSHSREG后是否需要SaveSPToTcb。
按照流程,任务1在OSTickISR中,PUSHRS,PUSHSREG后,堆栈指针的位置为SP1,而此时TCB里的位置为上次任务保存的SP0,然后所有delaytime都减1,然后,OSIntExit   第一种情况:如果最高优先级还是当前任务的话,则直接返回SP1处,寄存器出栈。这个应该没有问题。
               第二种情况:如果最高优先级不是当前任务的话,则调用OSIntCtxSw,此时SP的值为任务2的SP3,当新任务运行完后,再次调用任务1时,从TCB里面读取的值应该是SP0,而不是SP1,应该发生错误。
但是试验下来不管时候保存SP1,系统都能正常运行

请各位老师,高手赐教 O(∩_∩)O


<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


我的相关代码
********************************************************************************************************
OSCtxSw:
PUSHRS                              ; Save current tasks context
         PUSHSREG

LDS     R30,OSTCBCur                ; Z = OSTCBCur->OSTCBStkPtr
LDS     R31,OSTCBCur+1 ;

IN      r28,_SFR_IO_ADDR(SPL)
ST      Z+,R28                      ; Save Y (R29:R28) pointer
IN      r29,_SFR_IO_ADDR(SPH)
ST      Z+,R29                      ;

LDS     R16,OSPrioHighRdy           ; OSPrioCur = OSPrioHighRdy
STS     OSPrioCur,R16

LDS     R30,OSTCBHighRdy            ; Let Z point to TCB of highest priority task
LDS     R31,OSTCBHighRdy+1          ; ready to run
STS     OSTCBCur,R30                ; OSTCBCur = OSTCBHighRdy
STS     OSTCBCur+1,R31              ;

LD      R28,Z+                      ; Restore Y pointer
OUT     _SFR_IO_ADDR(SPL),R28
LD      R29,Z+                      ;
OUT     _SFR_IO_ADDR(SPH),R29

           POPSREG
POPRS                               ; Restore all registers and the status register
RET


;*********************************************************************************************************
;                                INTERRUPT LEVEL CONTEXT SWITCH
;*********************************************************************************************************

OSIntCtxSw:

                LDS     R16,OSPrioHighRdy           ; OSPrioCur = OSPrioHighRdy
                STS     OSPrioCur,R16               ;

                LDS     R30,OSTCBHighRdy            ; Z = OSTCBHighRdy->OSTCBStkPtr
                LDS     R31,OSTCBHighRdy+1          ;
                STS     OSTCBCur,R30                ; OSTCBCur = OSTCBHighRdy
                STS     OSTCBCur+1,R31              ;

                LD      R28,Z+                      ; Restore Y pointer
                OUT     _SFR_IO_ADDR(SPL),R28
                LD      R29,Z+                      ;
                OUT     _SFR_IO_ADDR(SPH),R29

                POPSREG
                POPRS                               ; Restore all registers and status register
                RET

;********************************************************************************************************
;                                           SYSTEM TICK ISR
;
; Description : This function is the ISR used to notify uC/OS-II that a system tick has occurred.

;********************************************************************************************************
OSTickISR:
SEI
PUSHRS ; Save all registers and status register
          PUSHSREG
;-------------------------------------------------------------------------------------------------
;试验中, 这一保存SP的指令加不加都一样
; LDS     R30,OSTCBCur                ; Z = OSTCBCur->OSTCBStkPtr
; LDS     R31,OSTCBCur+1 ;

; IN      r28,_SFR_IO_ADDR(SPL)
; ST      Z+,R28                      ; Save Y (R29:R28) pointer
; IN      r29,_SFR_IO_ADDR(SPH)
; ST      Z+,R29      
;-------------------------------------------------------------------------------------------------
LDS R16,OSIntNesting ; Notify uC/OS-II of ISR
INC R16 ;
STS OSIntNesting,R16 ;

RCALL   OSTimeTick                  ; Call uC/OS-IIs tick updating function
RCALL   OSIntExit                   ; Notify uC/OS-II about end of ISR

           POPSREG
POPRS                               ; Restore all registers and status register
RET


;*********************************************************************************************
void  OSTimeTick (void)
{
    OS_TCB    *ptcb;
    if (OSRunning == OS_TRUE)  
{
         ptcb = OSTCBList;                       
         while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO)  
{      
             OS_ENTER_CRITICAL();
             if (ptcb->OSTCBDly != 0)  
          {                     
                           if (--ptcb->OSTCBDly == 0)  
           {                             
                               if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY)  
                                                      {
                          OSRdyGrp|= ptcb->OSTCBBitY;                                                                                OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                     }
else
{
ptcb->OSTCBDly=1;
}
                 }
             }
             ptcb = ptcb->OSTCBNext;                        /* Point at next TCB in TCB list                */
             OS_EXIT_CRITICAL();
         }
           }

}

********************************************************************************************************************
void  OSIntExit (void)
{

OS_ENTER_CRITICAL();
if (OSIntNesting > 0) {                            /* Prevent OSIntNesting from wrapping       */
OSIntNesting--;
}
if (OSIntNesting == 0) {                           /* Reschedule only if all ISRs complete ... */
if (OSLockNesting == 0) {                      /* ... and not locked.                      */
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

OSCtxSwCtr++;                          /* Keep track of the number of ctx switches */
OSIntCtxSw();   /* Perform interrupt level ctx switch       */
}
}
}
OS_EXIT_CRITICAL();
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2009-10-15 15:02:22 | 显示全部楼层
这是OSTickISR程序,它主要就是调用了OSTimeTick()程序
void OSTickISR(void)
{
    OSIntEnter();        //进中断
    UserTickTimer();                                          
    OSTimeTick();        //系统节拍处理函数
    OSIntExit();        //实际上的任务切换是在这个函数里面进行的                               
}

这是
void  OSTimeTick (void)
{
    #if OS_CRITICAL_METHOD == 3                                /*同上  */
        OS_CPU_SR  cpu_sr;
    #endif   
    OS_TCB    *ptcb;


    OSTimeTickHook();                                      /* 调用用户自定义的挂钩程序*/
#if OS_TIME_GET_SET_EN > 0   
    OS_ENTER_CRITICAL();                                   
    OSTime++;
    OS_EXIT_CRITICAL();
#endif   
    ptcb = OSTCBList;                                      /* 指向TCB列表的第一个任务控制块,这里顺着一个链表操作下去,如果有的任务是在延时的话则把延时的值减一      */
    while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {              /* 利用WHILE循环处理所有的任务块列表       */
        OS_ENTER_CRITICAL();
        if (ptcb->OSTCBDly != 0) {                         /* 看看延时值是否为0,为零的话是带表不延时的   */
            if (--ptcb->OSTCBDly == 0) {                   /* 减一以后看看是否为零  */
                if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == 0x00) {   /* 如果任务不是在挂起状态的话则把这个任务对应的就绪表相应位置1         */
                    OSRdyGrp               |= ptcb->OSTCBBitY;
                    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                } else {                                       /* 如服任务是在挂起状态的话则把延时值改为1,让它继续处于挂起状态   */
                    ptcb->OSTCBDly = 1;              
                }                                             
            }
        }
        ptcb = ptcb->OSTCBNext;                                /* 指向任务控制块链表的下一个元素   */
        OS_EXIT_CRITICAL();
    }
}

void  OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3                            /*同上    */
    OS_CPU_SR  cpu_sr;
#endif
   
   
    OS_ENTER_CRITICAL();
    if (OSIntNesting > 0) {                            /* 如果中断嵌套层数还大于0的话则减一    */
        OSIntNesting--;
    }
    if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* 当条件为真的时候则开始重新调度任务,因为中断程序里面一般改变了某些任务的优先级,当退出中断的时候就立刻进行任务切换,来处理一些紧急的事件    */
        OSIntExitY    = OSUnMapTbl[OSRdyGrp];          /*取出就绪优先级最高的组                         */
        OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);/*    得到当前处于就绪状态的优先级最高的任务    */
        if (OSPrioHighRdy != OSPrioCur) {              /* 如果还是当前任务优先级最高的话则不进行任务切换     */
            OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];/*   通过任务优先级列表得到优先级最高的任务的任务控制块的地址,以进行任务切换     */
            OSCtxSwCtr++;                              /*任务切换计数器加一 */
            OSIntCtxSw();                              /* 进行中断状态下的任务切换,因为在中断情况下任务切换和非中断状态下的切换是不同的,需要进行SP指针的调整      */
        }
    }
    OS_EXIT_CRITICAL(); /*  开中断      */
}

中断里面进行任务切换使用OSIntCtxSw()函数
而调用OS_Sched这个函数进行任务切换的时候调用的是OS_TASK_SW()这个函数,其实就是OSCtxSw()这个函数
看看这两个函数的不同吧
其实就是调整SP指针,去掉在中断状态下,系统多调用了两个函数,OSIntExit()和OSIntCtxSw(),因为每一次调用函数,
系统都要对SP进行压栈,保存函数返回时的PC指针,但是中断切换任务后,并不需要返回到这两个函数出,故需要对SP进行调整,
我在51下面进行移植,也就是把SP指针的值减4
而不管是正常任务切换或者中断任务切换,SP的指针都不需要你保存,sp的起始地址是固定的,每次任务切换,整个SP的内容都会被当前TCB里面的内容所覆盖,我不知道我说明白了没,还有问题,欢迎继续交流

出0入0汤圆

 楼主| 发表于 2009-10-15 15:27:46 | 显示全部楼层
sp的起始地址是固定的
SP的不是在堆栈空间浮动的吗?

出0入0汤圆

发表于 2009-10-15 16:13:57 | 显示全部楼层
好,那我反问一个问题,ucos是如何把SP的内容保存到每个任务的TCB里面的?我要详细步骤

出0入0汤圆

 楼主| 发表于 2009-10-15 16:23:12 | 显示全部楼层
任务切换,先PUSH所有的寄存器,然后OSTCBCur->OSTCBStkPtr=SP
AVR上用汇编描述如下
        LDS     R30,OSTCBCur                ; Z = OSTCBCur->OSTCBStkPtr
        LDS     R31,OSTCBCur+1                 ;

        IN      r28,_SFR_IO_ADDR(SPL)
        ST      Z+,R28                      ; Save Y (R29:R28) pointer
        IN      r29,_SFR_IO_ADDR(SPH)
        ST      Z+,R29      
  。。。。

出0入0汤圆

 楼主| 发表于 2009-10-15 16:25:49 | 显示全部楼层
第一次初始化任务
在OSTaskStkInit最后的返回值即任务初始化后的堆栈顶地址
在OS_TCBinit中ptcb->OSTCBStkPtr= ptos; 对此地址进行第一次保存

出0入0汤圆

发表于 2009-10-15 16:30:12 | 显示全部楼层
ucos将堆栈分成两类,一个是系统栈(我暂且这么称呼),一个是任务栈(也就是TCB),系统栈是实际的硬件堆栈,起始地址是固定的,在保存任务栈的时候,UCOS首先根据当前SP指针和SP的起始指针算出堆栈的长度,然后将这个长度以及从SP起始地址开始的整体堆栈保存到TCB里面,然后用另外一个任务的tcb覆盖当前的系统栈

出0入0汤圆

发表于 2009-10-15 17:16:32 | 显示全部楼层
SP0的值是多少没关系,SP1才是有意义的值。
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3639472&bbs_page_no=1&bbs_id=1000

出0入0汤圆

 楼主| 发表于 2009-10-15 18:41:00 | 显示全部楼层
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3639472&bbs_id=1000

出0入0汤圆

发表于 2009-10-15 23:00:07 | 显示全部楼层
不是SP0,是SP1,SP0是个没意义的值。

OSTCBStkPtr的值SP0是任务上一次的入口,一旦任务运从入口OSTCBStkPtr恢复上下文后,OSTCBStkPtr的值就失去意义了,
任务不可能再次由OSTCBStkPtr恢复上下文,而只能是把出口写入的OSTCBStkPtr中,即把SP1写入到OSTCBStkPtr,当任务再次切换回来,
由OSTCBStkPtr的值SP1恢复到切换之前的状态。

当任务正在运行时,OSTCBStkPtr的值是多少,没有任何关系。
只有当任务没有运行,OSTCBStkPtr的值才有意义,此时OSTCBStkPtr正是记录任务上一次切换时的断点上下文指针,通过这个指针任务可以恢复到被切换之前的状态。

楼主的例子,发生了中断,只要中断还没有切换任务,任务的OSTCBStkPtr的值仍然是没有意义的。
(有意义的是CPU硬件的真实SP值,而不是保存在OSTCBStkPtr的SP值)
只有任务切换出去了OSTCBStkPtr的值才有意义。


任务控制块对战指针OSTCBStkPtr  

从其他任务切换到任务:读取OSTCBStkPtr的值SP0,恢复上下文(恢复上下文后,OSTCBStkPtr的值是多少就没有意义了,有意义的是CPU硬件真实SP值)

发生中断:中断压栈,此时SP的值是SP1

中断其他处理

中断切换到其他任务:把SP1写到OSTCBStkPtr里(OSTCBStkPtr之前的值没意义)

运行其他任务

其他任务切换回来:读取OSTCBStkPtr的值SP1,恢复上下文(上下文后回复后,恢复到任务被中断前的状态,注意这里不是恢复到中断中切换任务前的状态,而是恢复到中段前的状态)。



上面的过程可看做:
不管中间过程如何,对任务来说,就好像只是发生了一次中断,再从中断返回一样。

出0入0汤圆

 楼主| 发表于 2009-10-15 23:47:19 | 显示全部楼层
上官说的分析的很正确
我讨论的就是是否要 在中断程序中切换任务时保存SP1到OSTCBStkPtr
现在分析下来是,要保存。
没有保存从表象上看和保存效果一样是我程序出了问题。任务占用时间太短,大多数timetick发生咋idle里面。

出0入0汤圆

 楼主| 发表于 2009-10-16 00:04:59 | 显示全部楼层
还有个问题:

根据上官的分析:

发生中断:中断压栈,此时SP的值是SP1  

中断其他处理  

中断切换到其他任务:把SP1写到OSTCBStkPtr里(OSTCBStkPtr之前的值没意义)  
------------------------------------------------------------
中断发生:中断压栈,这时中断子程序使用的栈当前任务的任务栈空间,如果不在压栈后立即保存SP1的值,SP1的值会改变。等到任务切换时再保存已经晚了

出0入0汤圆

发表于 2009-10-16 00:45:45 | 显示全部楼层
如果不在压栈后立即保存SP1的值,SP1的值会改变。等到任务切换时再保存已经晚了

---------------------------------------------------------------------------

的确是。在中断压栈时,SP1已经保存到OSTCBStkPtr里了。

如果不进行任务切换,OSTCBStkPtr值是多少没有关系,

如果进行任务切换,任务切换并不保存当前的SP,而是直接使用了先前已经保存的SP1。

出0入0汤圆

 楼主| 发表于 2009-10-16 08:35:00 | 显示全部楼层
上官曰:的确是。在中断压栈时,SP1已经保存到OSTCBStkPtr里了。
------------------------------------------------------------------------
我的代码里就是没有保存SP1的值的代码,所以产生疑问,上来问问具体要不要保存

出0入0汤圆

发表于 2009-10-16 09:14:55 | 显示全部楼层
不保存SP1,在逻辑上完全错误。

只不过楼主的SP1正好等于SP0,导致楼主不保存SP1也可以正确执行。

原因是 任务运行时,发生中断切换任务,而此中断发生时的SP值是相同,中断压栈后,SP1的值就与原先的SP0值相同。

估计楼主的 任务 没有调用其他函数,或者一直调用某个函数,导致任务运行时SP的值相对稳定,没有浮动。

楼主不妨在任务里面加上一些汇编,人为入栈出栈,或者进行深层的函数嵌套调用,使SP浮动起来,应该很快就能观测出结果来。

出0入0汤圆

 楼主| 发表于 2009-10-16 09:47:26 | 显示全部楼层
我已经得出结论了,要保存SP的值

ATmega32 上官金虹(胡文涛)
ShangGuan 上官金虹(胡文涛)
这两个哪个是哪个啊


还有一个问题请教上官兄:
问题描述:我用IO口直接驱动段式LCD
我现在的做法
建立2个函数,命名为FlashLcd和FlashLcdData
功能描述:
FlashLcd()     负责产生驱动LCD的交流电平,LCD有2个COM口,设20ms刷新一次,则每个com口正电平和取反各5ms,一共20ms
FlashLcdData() 负责刷性LCD显示的数据

现在把FlashLcdData() 这个函数放到任务里去做,这个好办。

FlashLcd应该放到那里去处理比较合适,我现在是放到了系统的定时器中断里面。TimeTick里面,但是这样就限制我的系统时钟中断最长只能做到5ms,不能更长,导致功耗一直下不来,不能产时间sleep。


如果是上官兄,如何处理这个FlashLcd()函数,放到那个模块里去做?

出0入0汤圆

发表于 2010-7-8 09:34:12 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 14:24

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表