經(jīng)過上一節(jié)的介紹,我們已經(jīng)清晰了任務(wù)堆棧初始化后任務(wù)堆棧的內(nèi)容,及啟動任務(wù)時如何將堆棧內(nèi)容恢復(fù)并跳轉(zhuǎn)到任務(wù)函數(shù)。那么本節(jié)接著介紹任務(wù)切換,任務(wù)切換決定了任務(wù)的執(zhí)行順序,包括如優(yōu)先級高的任務(wù)打斷優(yōu)先級低的任務(wù)和同等優(yōu)先級任務(wù)的時間片調(diào)度等。
任務(wù)切換可以在兩種情況下進行,一種是執(zhí)行一個系統(tǒng)調(diào)用,如直接調(diào)用引起任務(wù)切換的API函數(shù),比如任務(wù)切換函數(shù)taskYIELD()。另一種是在滴答定時器Timer1中斷中執(zhí)行任務(wù)切換。無論哪種方法,最終實際都是調(diào)用portYIELD()函數(shù)來實現(xiàn)的。為了深刻理解這個切換過程,我們有必要再一次回顧涉及到的基本匯編語法。
我們首先看下portmacro.h中portYIELD()的宏定義,可以看出這里有個CALL語句,所以當前任務(wù)下一PC 執(zhí)行地址將自動壓棧。
接著我們看一下CALL調(diào)用的子函數(shù)vPortYield的內(nèi)容,可以很容易的看出其首先按照堆棧初始化的方式進行壓棧,直到壓完uxCriticalNesting后將棧頂賦值給當前任務(wù)的TCB第一個成員變量pxTopOfStack;然受調(diào)用vTaskSwitchContext()來獲取下一個要運行的任務(wù)(如存在高優(yōu)先級的任務(wù)),明確該任務(wù)堆棧的棧頂指針,先出棧uxCriticalNesting,然后接著順序出棧直到出棧SR,最后借助return匯編語句跳轉(zhuǎn)到下一個要運行任務(wù)的任務(wù)函數(shù)。
最后在介紹一下時間片調(diào)度。FreeRTOS支持多個任務(wù)同時擁有一個優(yōu)先級,同時允許一個任務(wù)運行一個時間片(一個時鐘節(jié)拍的長度)后讓出CPU的使用權(quán),讓擁有同一個優(yōu)先級的下一個任務(wù)運行。任務(wù)是否切換這是通過滴答定時器中斷中的xTaskIncrementTick()來判斷的,并且只有當configUSE_PREEMPTION和configUSE_TIME_SLICING都為1的時候才允許進行時間片調(diào)度。在xTaskIncrementTick()中會判斷當前任務(wù)的優(yōu)先級下是否還有其他任務(wù),如果有則返回pdTRUE,執(zhí)行一次任務(wù)切換。
至此已經(jīng)講完了FreeRTOS在dsPIC33C系列芯片移植的所有內(nèi)容,已經(jīng)可以跑起來FreeRTOS的開發(fā)環(huán)境了,剩下的就是找一本講解FreeRTOS的書,逐步了解FreeRTOS自身的知識點,邊動手邊思考,相信大家一定可以將FreeRTOS熟練應(yīng)用到后續(xù)的項目中去。
額外的,還需要注意的是在例程中我定義了三個宏,通過這些宏可以區(qū)分port.c、portasm_dsPIC.S是針對dsPIC33C還是針對PIC24等。換句話說這個移植不僅針對于dsPIC33CK系列芯片,它更適用于Microchip全系16位MCU芯片。