uCOS-II中的任务切换机制

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

uCOS-II中的任务切换机制

【@.1 函数周期与死循环】

一般函数的生命周期很简单,从开始调用函数起,直到函数返回,即结束。这样一来就完成了这个函数的使命,它也就不再需要了。对于一般的函数就是这样,但是回过头想想,对于一个系统、OS、或者工业控制中的一个控制器重的系统个,函数返回是很轻易很随便的就能返回吗?返回就意味着函数结束,死亡,若是想系统这样一个很大的函数,它的返回就意味着系统结束。因此,对于系统的函数返回有些时候我们不希望它返回,返回时是需要好好设计的,像嵌入式中的控制程序我们也并不需要它返回,直接关机就好了。因此,一个系统往往就是一个很大的循环,不停的扫描,而我们编程的时候对于这个死循环是需要好好设计的。考虑以下一个控制要求,

@.按键控制电机启、停、正转反转,并每秒发送CAN报文报告当前情况。

我们可以有多种方法实现这一要求:

方法一:每次在循环体重扫描当前按键的电平,从而进入对应的控制电机函数,如果所有电平都没有信号则直接进入下一个循环。发送CAN报文就直接用一个定时中断。这样的好处就是编程简单直白,每次循环进入不同的电机控制函数,坏处很明显,一定要等待到下

一个循环才能进入其他的电机控制函数,每次循环的时间不好控制,不管你用函数指针还是if/else来判断,每次循环一定要等待电机动作结束才能进入下一个循环。

方法二:改用外部中断来处理按键。仅当按键按下时触发外部中断,从而控制响应的电机进行操作。这样的好处就是循环体简单,可以仅仅就是一个计数器加一,所有控制都等中断来实现。但这样带来的问题也很明显,就是中断嵌套问题。比如当电机正转时按下停止按钮,这时由于是在中断中,停止按钮是否真的能够得到响应?这就涉及到中断嵌套问题,并不见得所有CPU都能支持中断嵌套,我的这一篇文章对中断嵌套问题进行了一个讨论。

方法三:采用RTOS的思想,加入任务调度系统。每次任务调度系统就是一个小小的循环,对于各个任务进行轮询,当这次轮询发现某任务是已经就绪的优先级最高的任务,则交给CPU处理,所有中断与任务,任务与任务之间有通讯机制可以交换信息。当然实际上并不仅仅只在任务调度器轮询时才进行任务的切换,实际上的操作比这个复杂一些,我的这篇文章就想以uCOS-II为例讨论RTOS的任务调度系统是怎样执行的。

【@.2 uCOS-II中的任务调度】

回到前面的方法一,二,我们将这种任务称作前后台任务。

后台就是指程序的大循环,程序一定要等到前台任务(可以说中断或某个功能函数)返回才能继续运行下去。而在RTOS中每个任务都有自己的控制块指向改任务,由任务调度器来决定这个时候该运行哪个任务。

所有这些任务控制快(TCB)构成一个双向链表,每个TCB中都有一些控制字,比如一个指向堆栈的指针(*sp),一个表明当前任务状态的位(State),指明任务被挂起等待的超时时间(dly),任务的优先级(Prio),指向事件控制块的指针(*Event,事件机制后面会讨论)。一个全局的任务就续表记录了当前任务是否就绪,任务调度器就靠查询任务就绪表寻找到就绪任务中优先级最高的一个任务。

每一个任务都是一个死循环,并且必须在循环内调用系统函数来释放CPU控制权,比如调用系统的延时函数OSTimeDly()延时一段时间,这个时候系统就会知道这个任务被延时了,延时时间记录在TCB中的超时时间dly中,任务调度器将其他任务予以运行。

实际的任务调度有两种机制

1.中断级任务调度:任何中断返回时必须调用一个系统函数OSIntExit(void),进行一次任务调度。比如一般任务调度器通常是一个定时中断,比如1000次每秒,成为OS时钟。每次OS时钟要返回时都会调用OSInitExit()进行任务切换,运行当前就绪的优先级最高任务。一般这个OS时钟的优先级很低,为整个系统优先级倒数第二低(倒数第一低的是Idle Task,空闲任务),因此实际上这个OS时钟最终并不会返回。

2.任务级任务调度:在任务运行中执行一次OS的特定函数,比如前面提到的

OSTimeDly(),此函数在返回之前会执行一次任务调度。

因此总的任务调度次数会远远高于OS时钟的轮询频率的。

我们下面举例来说明这两种调度机制:

【@.3 中断级任务调度】

当OS时钟的定时中断来临时(比如1000次每秒),会进入OS时钟的服务函数,它会做大致一下工作,递增一个全局的计数器OSTIme,遍历所有OSTCB链表,将其中的dly超时时间递减,若等于0,则在就绪列表中置位。最后这个中断要返回时会调用OSInitExit(),它会找到在任务就绪表中处于就绪状态的最高优先级的任务,并且调用OSInitCtxSw(),即上下文切换,将之前找到的任务放在任务堆栈中的寄存器推入到CPU的R0~R15,CPSR 中,最后结束。这里的这个OSInitCtxSw(),上下文切换函数是实际上跟CPU打交道的函数,也是移植uCOS-II需要编写的函数。

任何中断返回时都必须调用这个OSInitExit(),不管是外部中断,定时器中断,串口中断。当然,我们可以在实际编写中将这一中断过程编写一个统一模板,调用中断服务实际函数。这是后话了。

【@.4 任务级任务调度】

这种调度方式实际上丰富了任务之间的通讯机制以及任务的控制,也是最需要理解的一种方式。主要的流程跟中断级方式类似,不过是需要在任务中调用一些特定的系统函数。实际的任务切换函数是OSSched(),会调用实际的上下文切换函数OS_TASK_SW()。这个函数往往跟中断级的上下文切换函数是一样的,我们移植时只需要编写一个函数即可。下面以几个具体的函数为例进行讲解:

1.OSTaskResume/ OSTaskSuspend

首先是一个比较好理解的OSTaskSuspend()和OSTaskResume(),直接控制任务的挂起和恢复。对于图中的TaskA,在任务运行到某处调用OSTaskSuspend()之后,会挂起就绪表中优先级为prio的任务x(优先级也就是这个函数的传入参数)。之后调用OSSched,进行任务切换。若挂起的是自己,即调用OSTaskSuspend时传入的优先级是自己的,则自身被挂起,函数不返回;若挂起的是别的任务,那么别的任务就直接被挂起,当前函数返回。

相关文档
最新文档