我之前也没有用过操作系统,刚刚接触uC,想请教诸位一个问题。
以下是我写的一段代码,核心思想是在一个任务运行过程中检查变量,如果发现超出阈值则挂起此任务,
唤醒另外一个任务。但是事实上每次都无法切换任务,仍然在执行原任务。可能是什么原因呢?
while(1)
{
if(auto_speed <= 5)
{
Uart_SendString("Swtich WorkMode\n"); (1)
err = OSTaskSuspend(OS_PRIO_SELF);//挂起当前任务--自动巡航
Uart_ShowInt(err); (2)
Uart_SendByte('\t'); (3)
err = OSTaskResume(StopModeTaskPrio);//手动驾驶任务解除挂起
Uart_ShowInt(err);
Uart_SendByte('\n');
break;
}
OSTimeDly(OS_TICKS_PER_SEC/50);//频率为50Hz
}
我总共有三个任务,StopModeTask、ManualModeTask、AutoModeTask,刚开始时都被挂起,先运行AutoModeTask,按键中断
可以改变速度,如果速度小到一定程度,就挂起当前的AutoModeTask任务,转而执行StopModeTask任务。
实际的运行结果是输出来了(1)处,但是(2)(3)处都没有输出,并且一直在原任务——AutoModeTask运行,根本没有切换。
请问这是什么原因呢?
回复 楼主 black0936 的帖子
err = OSTaskSuspend(OS_PRIO_SELF);//挂起当前任务--自动巡航
当这条语句被执行结束之后,任务AutoModeTask即被挂起,后续的代码不被执行(也就是说StopModeTask这个任务是无法被激活的,而2、3也不会输出),由于此时你的其他两个任务StopModeTask、ManualModeTask也都处于被挂起的状态。因此,此时系统实际上是在运行空闲任务或者统计任务。
这是根据你给出的代码所做的推测,因为不清楚你其他部分的代码是怎么写的,所以仅供参考。
非常感谢juntianya的回复。本来已经隔了几天没看这个论坛了,没想到您还是热心的帮我解答问题,非常感谢。事实上也的确是你说的那个问题,我颠倒了一下顺序就好了。非常感谢。
现在还是有一个问题希望请教。我是三个任务来回切换,例如说A、B、C,切换有两种可能,中断或者是任务中设置条件满足。现在问题来了,有这种情况,在任务B运行过程中(此时A、C都没有运行)
if(满足条件)
{
OSTaskResume(A);//解除任务A的挂起状态
OSTaskSuspend(B);//挂起当前任务B
}
这种情况下,解除A的挂起状态时运行过OSSched(),因为A的优先级高,所以直接切换过去运行A,等到A中运行到OSTimeDly()这一块,又转回来运行B,然后发现B需要挂起,有点绕远路的感觉。我觉得这个过程有点麻烦,有没有什么方法能先解除A的挂起,再挂起B,然后才运行调度函数,进行任务切换。我的想法是
if(满足条件)
{
OSLockNesting++;
OSTaskResume(A);//解除任务A的挂起状态
OSLockNesting++;
OSTaskSuspend(B);//挂起当前任务B
}
这样子就能保证先解除A的挂起状态同时不进行任务切换,留到挂起B的时候再进行任务切换,你觉得这样合适吗?谢谢!
回复 板凳 black0936 的帖子
额……第二句 OSLockNesting++;应该是OSLockNesting--;吧?
这样做可以。
不过直接对OSLockNesting自增和自减而不做任何的保护是有一定风险的,会影响系统的稳定性。你可以用OSSchedLock ()和OSSchedUnlock()来代替,考虑到OSSchedUnlock()中会进行任务调度的问题,可以把这句放到 OSTaskSuspend(B)之后。
又或者至少使用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来对OSLockNesting自增和自减做保护。
当然以上的两种做法主要是考虑到系统的稳定性问题。如果你的程序本身比较简单的话,就无关紧要了,直接按你自己说的来就行了。恕我啰嗦了,呵呵……
其实想问的正是这一句,如果将OSSchedUnlock()放到OSTaskSuspend(B)之后,那这一句还会不会执行呢?挂起任务后是不是之后的语句都不会执行了呢?也就是说,实际上OSTaskSuspend(B)挂起自身后又没有来得及执行OSSchedUnlock(),所以程序无事可做,可能会死掉。这样理解对吗?谢谢指点
回复 5楼 black0936 的帖子
OSTaskSuspend(B),这句话的意思是将B任务挂起。但是,这个函数时如何完成对于任务的挂起的呢?说白了,就是两步。第一步:将这个任务置为OS_STAT_SUSPEND(挂起状态),这样做可以保证任务在未被恢复之前将不会被涉及到OS的任务调度中去。第二步:调用OS_Sched()函数,进行任务切换。
而在我们现在讨论的场景之下,第一步被执行了,但是第二步却被阻止了。于是,由于没有执行任务调度,占用CPU的还是任务B,OSTaskSuspend(B)函数执行完成以后又返回到了任务B当中执行下一条代码,也就是OSSchedUnlock()函数,完成了调度开锁和任务调度操作。
说白了,就是将挂起函数的第二步拖延到调度开锁以后再执行。