ucos移植过程中的用户堆栈地址问题

时间:2021-03-08 16:36:57
我建立一个任务MyTask,代码如下:



void MyTask(void *data)
{
    data = data;



    psem = OSSemCreate(0); // 创建一个信号量



    while(1)
{
     transData(0x55);  //通过串口发送数据55
     OSSemPend(psem,0,error);  //等待信号量
     transData(0x33); //通过串口发送数据33

}
}



程序发送数据55后,执行等待信号量函数,



但是一旦进入OSSemPend,R0的地址就立即减少4,例如任务堆栈地址是51EA(采用16位寻址方式,数据页指针是DPP1),R0的正确地址是51EA,一旦进入OSSemPend,R0的地址就减去4,变成了51E6。再往下执行一步,R0又减去8,最终是51DE,以后再执行程序保持该值不变。



这样就导致了进行任务调度时,保存context时出现错误,因此切换到其他任务后,就再不能正确切换到MyTask了,



请问高手,这是什么原因。



我的qq是57109527,希望能和大家探讨一下

8 个解决方案

#1


R0中保存的是任务堆栈的地址?还是任务堆栈中保存了R0? C调用任务时一般会用R0-R4传递参数。 问题再详细点,不是很明白你的意思

#2


我用的Cl67CR的单片机,编译器是tasking 7.5 v2。 
现在做的工作是将ucosII v200 移植到此单片机上。
每当运行一个任务,R0就指向该任务堆栈,
在进行任务切换时,通过R0来保存和恢复CPU的相关寄存器。
现在不清楚的问题有两个,一是通过什么机制,让R0指向了当前运行任务的堆栈?
                        二是上面的问题,是什么原因导致R0的值发生了改变,以致不能正常的进行任务切换?
呵呵,希望能和你探讨一下。

#3


今天发现真正的问题不在指向任务堆栈的指针,而是进行任务切换时保存的返回地址有问题。

函数的调用关系如下:MyTask() -> OSSemPend() -> OSSched(),OSSched() 代码如下:

void OSSched (void)
{
    INT8U y;


    OS_ENTER_CRITICAL();
    if ((OSLockNesting | OSIntNesting) == 0) {   /* Task scheduling must be enabled and not ISR level  */
        y             = OSUnMapTbl[OSRdyGrp];    /* Get pointer to highest priority task ready to run  */
        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
  transData(OSPrioHighRdy);
  transData(OSPrioCur);
        if (OSPrioHighRdy != OSPrioCur) {         /* No context switch if current task is highest ready */
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            OSCtxSwCtr++;                        /* Increment context switch counter                   */
   transData(0x99);
            OS_TASK_SW();                        /* Perform a context switch                           */
        }
    }
    OS_EXIT_CRITICAL();
}

调试的时候发现,当运行到OS_TASK_SW()时,IP的值是0CEA。

然后进入到 OS_TASK_SW(),此函数是用汇编写的,做任务切换。

在 OS_TASK_SW()中,要保存程序的返回地址,此时保存的0CEC,也就是OSSched()中OS_EXIT_CRITICAL();的地址。

在OS_TASK_SW()中成功跳转到另一个任务MyTask1(),该任务中函数调用关系是:MyTask1() -> OSSemPost() -> OSSched(),

在OSSched()中调用OS_TASK_SW(),返回到地址0CEC处,这时候不是返回到MyTask()中,而是在MyTask1() 中顺序执行下面的语句。

 

唉,不知道有什么好的解决方法。

希望大家探讨一下。

#4


1.在移植代码中,将任务堆栈地址保存在了R0寄存器中。
2.我想根本原因是任务切换时候的返回地址出错。

#5


"返回到地址0CEC处,这时候不是返回到MyTask()中,而是在MyTask1() 中顺序执行下面的语句。"这句话什么意思?
MyTask1() -> OSSemPost() -> OSSched()后返回的地方应该是上一次OSSemPend()中调用的OSSched()中  的OS_EXIT_CRITICAL()处,过程如下:
OSSched()中  的OS_EXIT_CRITICAL()->OSSemPend()中的 OS_Sched()的下一句,执行完OSSemPend()中的剩余代码后就回到了你的MyTask()中。

#6


因为0CEC是OSSched()中OS_EXIT_CRITICAL();语句的存放的地址,
返回时有什么机制确保是在上次调用OSSemPend()中调用的OSSched()中  的OS_EXIT_CRITICAL()处,
而不是本次调用OSSemPost() 中调用的OSSched()中  的OS_EXIT_CRITICAL()处呢?
从实际的运行结果来看,是返回到了本次调用OSSemPost() 中调用的OSSched()中  的OS_EXIT_CRITICAL()处。
这样就导致了任务不能正确的切换。

多谢Cifeng_Liang!

#7


当在Mytask中调用Pend函数而发生任务切换时,进入任务切换函数,在此函数中保存Mytask的上下文到与Mytask相应的堆栈中,那么当Mytask1调用Post函数而使Mytask就绪,且Mytask的优先级比Mytask1的优先级高,则再次进行一次调度,这时就把上一次保存在Mytask堆栈中的内容恢复过来就好了。

#8


该回复于2008-05-04 09:38:44被版主删除

#1


R0中保存的是任务堆栈的地址?还是任务堆栈中保存了R0? C调用任务时一般会用R0-R4传递参数。 问题再详细点,不是很明白你的意思

#2


我用的Cl67CR的单片机,编译器是tasking 7.5 v2。 
现在做的工作是将ucosII v200 移植到此单片机上。
每当运行一个任务,R0就指向该任务堆栈,
在进行任务切换时,通过R0来保存和恢复CPU的相关寄存器。
现在不清楚的问题有两个,一是通过什么机制,让R0指向了当前运行任务的堆栈?
                        二是上面的问题,是什么原因导致R0的值发生了改变,以致不能正常的进行任务切换?
呵呵,希望能和你探讨一下。

#3


今天发现真正的问题不在指向任务堆栈的指针,而是进行任务切换时保存的返回地址有问题。

函数的调用关系如下:MyTask() -> OSSemPend() -> OSSched(),OSSched() 代码如下:

void OSSched (void)
{
    INT8U y;


    OS_ENTER_CRITICAL();
    if ((OSLockNesting | OSIntNesting) == 0) {   /* Task scheduling must be enabled and not ISR level  */
        y             = OSUnMapTbl[OSRdyGrp];    /* Get pointer to highest priority task ready to run  */
        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
  transData(OSPrioHighRdy);
  transData(OSPrioCur);
        if (OSPrioHighRdy != OSPrioCur) {         /* No context switch if current task is highest ready */
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            OSCtxSwCtr++;                        /* Increment context switch counter                   */
   transData(0x99);
            OS_TASK_SW();                        /* Perform a context switch                           */
        }
    }
    OS_EXIT_CRITICAL();
}

调试的时候发现,当运行到OS_TASK_SW()时,IP的值是0CEA。

然后进入到 OS_TASK_SW(),此函数是用汇编写的,做任务切换。

在 OS_TASK_SW()中,要保存程序的返回地址,此时保存的0CEC,也就是OSSched()中OS_EXIT_CRITICAL();的地址。

在OS_TASK_SW()中成功跳转到另一个任务MyTask1(),该任务中函数调用关系是:MyTask1() -> OSSemPost() -> OSSched(),

在OSSched()中调用OS_TASK_SW(),返回到地址0CEC处,这时候不是返回到MyTask()中,而是在MyTask1() 中顺序执行下面的语句。

 

唉,不知道有什么好的解决方法。

希望大家探讨一下。

#4


1.在移植代码中,将任务堆栈地址保存在了R0寄存器中。
2.我想根本原因是任务切换时候的返回地址出错。

#5


"返回到地址0CEC处,这时候不是返回到MyTask()中,而是在MyTask1() 中顺序执行下面的语句。"这句话什么意思?
MyTask1() -> OSSemPost() -> OSSched()后返回的地方应该是上一次OSSemPend()中调用的OSSched()中  的OS_EXIT_CRITICAL()处,过程如下:
OSSched()中  的OS_EXIT_CRITICAL()->OSSemPend()中的 OS_Sched()的下一句,执行完OSSemPend()中的剩余代码后就回到了你的MyTask()中。

#6


因为0CEC是OSSched()中OS_EXIT_CRITICAL();语句的存放的地址,
返回时有什么机制确保是在上次调用OSSemPend()中调用的OSSched()中  的OS_EXIT_CRITICAL()处,
而不是本次调用OSSemPost() 中调用的OSSched()中  的OS_EXIT_CRITICAL()处呢?
从实际的运行结果来看,是返回到了本次调用OSSemPost() 中调用的OSSched()中  的OS_EXIT_CRITICAL()处。
这样就导致了任务不能正确的切换。

多谢Cifeng_Liang!

#7


当在Mytask中调用Pend函数而发生任务切换时,进入任务切换函数,在此函数中保存Mytask的上下文到与Mytask相应的堆栈中,那么当Mytask1调用Post函数而使Mytask就绪,且Mytask的优先级比Mytask1的优先级高,则再次进行一次调度,这时就把上一次保存在Mytask堆栈中的内容恢复过来就好了。

#8


该回复于2008-05-04 09:38:44被版主删除