一、開啟任務調度器
函數原型:
void vTaskStartScheduler( void )
作用:用于啟動任務調度器,任務調度器啟動后, FreeRTOS 便會開始進行任務調度
內部實現機制(以動態創建為例):
(1)首先,判斷動態創建任務 or 靜態創建任務
(2)創建空閑任務
(3)如果使能軟件定時器,則創建軟件定時器任務
(4)關閉中斷,防止調度器開啟之前或過程中,受中斷干擾,會在運行第一個任務時打開中斷?
(5)初始化全局變量,并將任務調度器的運行標志設置為已運行?
(6)初始化任務運行時間統計功能的時基定時器
(7)調用函數 xPortStartScheduler()
xPortStartScheduler() :
(1)檢測用戶在 FreeRTOSConfig.h 文件中對中斷的相關配置是否有誤
(2)配置 PendSV 和 SysTick 的中斷優先級為最低優先級
(3)調用函數 vPortSetupTimerInterrupt()配置 SysTick(主要配置定時器的中斷周期)
(4)初始化臨界區嵌套計數器為 0
(5)調用函數 prvEnableVFP()使能 FPU(M4與M7內核才有FPU)
(6)調用函數 prvStartFirstTask()啟動第一個任務
二、啟動第一個任務
????????要運行任務,必須把任務的寄存器的值加載到CPU的寄存器中,任務A的寄存器值,在一開始創建任務時就保存在任務堆棧里邊
注:
1、中斷產生時,硬件自動將xPSR,PC(R15),LR(R14),R12,R3-R0出/入棧(保存和恢復);
而R4~R11需要手動出/入棧(保存和恢復)
2、進入中斷后硬件會強制使用MSP指針 ,此時LR(R14)的值將會被自動被更新為特殊的EXC_RETURN
2.1 開啟第一個任務函數:prvStartFirstTask?()
__asm void prvStartFirstTask( void )
{/* 八字節對齊 */PRESERVE8/* 將向量表偏移量寄存器地址存儲到R0 */ldr r0, =0xE000ED08/* 向量表偏移量寄存器存儲著向量表的起始地址 *//* 將向量表起始地址存儲到R0 */ldr r0, [ r0 ]/* 將MSP的初始值存儲到R0 */ldr r0, [ r0 ]/* 使MSP回到最初值 */msr msp, r0/* 使能中斷 */cpsie icpsie fdsbisb/* 觸發SVC中斷,開啟第一個任務 */svc 0nopnop
/* *INDENT-ON* */
}
功能:用于初始化啟動第一個任務前的環境,主要是獲取MSP 指針的初始值,并使能全局中斷,觸發SVC,開啟一個任務
Q:向量表偏移量寄存器為什么會出現在這?
?答:
?????????經過前面一系列的操作,MSP的值已經改變,為了獲取MSP的初始值,需要通過VTOR寄存器,找到向量表的起始地址,再通過此地址才可以找到MSP的初始值
程序在運行過程中需要一定的棧空間來保存局部變量等一些信息。當有信息保存到棧中時,
MCU 會自動更新 SP 指針,ARM Cortex-M 內核提供了兩個棧空間:
(1)MSP(主堆棧指針):它由 OS 內核、異常服務例程以及所有需要特權訪問的應用程序代碼來使用
(2)PSP(進程堆棧指針):用于常規的應用程序代碼(不處于異常服務例程中時)
在FreeRTOS中,中斷使用MSP(主堆棧),中斷以外使用PSP(進程堆棧)
?
2.2 SVC中斷函數:void vPortSVCHandler( )
函數功能:恢復現場、開啟中斷,并跳轉到PC所指向的函數中(第一個任務函數中)
//進行8字節對齊 PRESERVE8//將當前任務TCB結構體的指針的地址存儲在R3ldr r3, = pxCurrentTCB /* Restore the context. *///通過地址找到當前任務結構的地址ldr r1, [ r3 ] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. *///通過地址找到第一個成員變量,棧頂指針(保存著更新后的地址,已經開辟了寄存器組的空間)ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. *///出棧ldmia r0 !, { r4 - r11 } /* 將出棧后的棧頂指針的值賦值給PSP,用于后面的現場保存 */msr psp, r0 /* Restore the task stack pointer. */isb//R0清0mov r0, # 0//使能所有中斷msr basepri, r0//R14與0xd進行或運算,表示退出中斷后,進入線程模式,并使用PSPorr r14, # 0xd//跳轉到PC所指向地址的函數中bx r14
?
?
R14:鏈接寄存器,在中斷中記錄了異常返回值 EXC_RETURN
????????當從 SVC 中斷服務退出前,通過向 r14 寄存器最后 4 位按位或上 0x0D,使得硬件在退出時使用進程堆棧指針 PSP 完成出棧操作并返回后進入任務模式、返 回 Thumb 狀態。在 SVC 中斷服務里面,使用的是 MSP 堆棧指針,是處在 ARM 狀態。
????????當 r14 為 0xFFFFFFFX,執行是中斷返回指令,cortext-m3 的做法,X 的 bit0 為 1 表示 返回 thumb 狀態,bit1 和 bit2 分別表示返回后 sp 用 msp 還是 psp、以及返回到特權模式還 是用戶模式
????????異常返回,這個時候出棧使用的是 PSP 指針,自動將棧中的剩下 內容加載到 CPU 寄存器: xPSR,PC(任務入口地址),R14,R12,R3,R2,R1,R0 (任務的形參)同時 PSP 的值也將更新,即指向任務棧的棧頂
?
注意:SVC中斷只在啟動第一次任務時會調用一次,以后均不調用 ?
三、任務切換
本質:CPU寄存器值的切換
?假設當由任務A切換到任務B時,主要分為兩步:
????????第一步:需暫停任務A的執行,并將此時任務A的寄存器保存到任務堆棧,這個過程叫做保存現場;
????????第二步:將任務B的各個寄存器值(被存于任務堆棧中)恢復到CPU寄存器中,這個過程叫做恢復現場;
對任務A保存現場,對任務B恢復現場,這個整體的過程稱之為:上下文切換
注意:任務切換的過程在PendSV中斷服務函數里邊完成?