经过上一节的介绍,我们已经清晰了任务堆栈初始化后任务堆栈的内容,及启动任务时如何将堆栈内容恢复并跳转到任务函数。那么本节接着介绍任务切换,任务切换决定了任务的执行顺序,包括如优先级高的任务打断优先级低的任务和同等优先级任务的时间片调度等。
任务切换可以在两种情况下进行,一种是执行一个系统调用,如直接调用引起任务切换的API函数,比如任务切换函数taskYIELD()。另一种是在滴答定时器Timer1中断中执行任务切换。无论哪种方法,最终实际都是调用portYIELD()函数来实现的。为了深刻理解这个切换过程,我们有必要再一次回顾涉及到的基本汇编语法。
我们首先看下portmacro.h中portYIELD()的宏定义,可以看出这里有个CALL语句,所以当前任务下一PC 执行地址将自动压栈。
接着我们看一下CALL调用的子函数vPortYield的内容,可以很容易的看出其首先按照堆栈初始化的方式进行压栈,直到压完uxCriticalNesting后将栈顶赋值给当前任务的TCB第一个成员变量pxTopOfStack;然受调用vTaskSwitchContext()来获取下一个要运行的任务(如存在高优先级的任务),明确该任务堆栈的栈顶指针,先出栈uxCriticalNesting,然后接着顺序出栈直到出栈SR,最后借助return汇编语句跳转到下一个要运行任务的任务函数。
最后在介绍一下时间片调度。FreeRTOS支持多个任务同时拥有一个优先级,同时允许一个任务运行一个时间片(一个时钟节拍的长度)后让出CPU的使用权,让拥有同一个优先级的下一个任务运行。任务是否切换这是通过滴答定时器中断中的xTaskIncrementTick()来判断的,并且只有当configUSE_PREEMPTION和configUSE_TIME_SLICING都为1的时候才允许进行时间片调度。在xTaskIncrementTick()中会判断当前任务的优先级下是否还有其他任务,如果有则返回pdTRUE,执行一次任务切换。
至此已经讲完了FreeRTOS在dsPIC33C系列芯片移植的所有内容,已经可以跑起来FreeRTOS的开发环境了,剩下的就是找一本讲解FreeRTOS的书,逐步了解FreeRTOS自身的知识点,边动手边思考,相信大家一定可以将FreeRTOS熟练应用到后续的项目中去。
额外的,还需要注意的是在例程中我定义了三个宏,通过这些宏可以区分port.c、portasm_dsPIC.S是针对dsPIC33C还是针对PIC24等。换句话说这个移植不仅针对于dsPIC33CK系列芯片,它更适用于Microchip全系16位MCU芯片。