文章目錄
- 一.概要
- 二.什么是實時操作系統
- 三.FreeRTOS的特性
- 四.FreeRTOS的任務詳解
- 1.任務函數定義
- 2.任務的創建
- 3.任務的調度原理
- 五.CubeMX配置一個FreeRTOS例程
- 1.硬件準備
- 2.創建工程
- 3.調試FreeRTOS任務調度
- 六.CubeMX工程源代碼下載
- 七.講解視頻鏈接地址
- 八.小結
一.概要
FreeRTOS是一個迷你的實時操作系統內核。作為一個輕量級的操作系統,功能包括:任務管理、時間管理、信號量、消息隊列、內存管理、記錄功能、軟件定時器、協程等,可基本滿足較小系統的需要。
由于RTOS需占用一定的系統資源(尤其是RAM資源),只有μC/OS-II、embOS、salvo、FreeRTOS等少數實時操作系統能在小RAM單片機上運行。相對μC/OS-II、embOS等商業操作系統,FreeRTOS操作系統是完全免費的操作系統,具有源碼公開、可移植、可裁減、調度策略靈活的特點,可以方便地移植到各種單片機上運行。
二.什么是實時操作系統
操作系統是一個控制程序,作為硬件和應用程序之間的橋梁,主要是和硬件打交道,負責協調分配計算資源和內存資源給不同的應用程序使用,并防止系統出現故障。面對來自不同應用程序的大量且互相競爭的資源請求,操作系統通過一個調度算法和內存管理算法盡可能把資源公平且有效率地分配給不同的程序。應用程序則通過調用操作系統提供的API接口獲得相應資源完成指定的任務。
實時操作系統(RTOS-Real Time Operating System)中實時(Real Time)指的是任務(Task)或者說實現一個功能的線程(Thread)必須在給定的時間(Deadline)內完成。
三.FreeRTOS的特性
具有搶占式或者合作式的實時操作系統內核
功能可裁剪,最小占用10kB左右rom空間,0.5kB ram空間
靈活的任務優先級分配
具有低功耗模式
有互斥鎖、信號量、消息隊列等功能
運行過程可追蹤
支持中斷嵌套
四.FreeRTOS的任務詳解
FreeRTOS的核心是任務調度器(Task Scheduler),它負責按照一定的調度策略將任務分配給處理器執行。每個任務都是一個獨立的函數,可以有不同的優先級和堆棧大小。任務調度器根據任務的優先級和調度策略決定哪個任務被執行。
下圖就是任務調度簡單介紹
下面代碼就是個簡單的示例代碼,通過調用osThreadCreate函數創建了兩個任務LED_Thread1(驅動LED燈滅)和LED_Thread2(驅動LED燈亮)。在main函數中,通過調用osKernelStart函數啟動了實時系統,使得任務可以被調度執行,由于兩個任務都能不斷運行,所以能驅動LED燈閃爍。
//定義任務函數1
void LED_Thread1(void const * argument)
{(void) argument;for (;;){HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);//PC13輸出高,LED滅osDelay(100);//等待100ms}
}
//定義任務函數2
void LED_Thread2(void const * argument)
{(void) argument;for (;;){HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);//PC13輸出低,LED亮osDelay(250);//等待250ms}
}int main(void)
{HAL_Init();SystemClock_Config();//外部8M晶振,系統72M主頻MX_GPIO_Init();//PC13配置成推挽輸出MX_FREERTOS_Init();//實時系統初始化/* Start scheduler */osThreadDef(THREAD1, LED_Thread1, osPriorityNormal, 0, 128);THREAD1Handle = osThreadCreate(osThread(THREAD1), NULL);//建立任務1/* definition and creation of THREAD2 */osThreadDef(THREAD2, LED_Thread2, osPriorityNormal, 0, 128);THREAD2Handle = osThreadCreate(osThread(THREAD2), NULL);//建立任務2osKernelStart();//實時系統啟動while (1){}}
1.任務函數定義
無論采用何種方法創建任務,均需要用到任務函數。FreeRTOS 規定任務函數的返回值必須為void,而且帶有一個void型指針參數。
void LED_Thread1(void const * argument)
{(void) argument;for (;;){HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);//PC13輸出高,LED滅osDelay(100);//等待100ms}
}
STM32F103C8T6單片機只有一個內核,那怎么讓多個人同時干活呢?其實每個子任務雖然都是死循環,但并不是每個子任務一直都在執行,每個子任務在執行期間,可能需要延時,這邊就通過 osDelay(100),等待100ms,單片機就可以停止此任務,然后切換到其它任務執行,這樣看起來就是多個人在同時干活了。
2.任務的創建
osThreadDef(THREAD1, LED_Thread1, osPriorityNormal, 0, 128);//定義一個任務,任務名,任務函數,優先級,堆棧大小
THREAD1Handle = osThreadCreate(osThread(THREAD1), NULL); //根據上面定義的任務,創建一個任務
osThreadDef函數中的128是任務棧大小定義:
任務棧大小指定了任務可以使用多少RAM來存儲局部變量和其他臨時數據。這個大小應該足夠大,能夠容納任務在執行期間可能出現的最大需求。
osThreadDef函數中的LED_Thread1是任務函數:
任務函數是任務執行的代碼,它應該是一個無返回值的函數,其參數是一個可選的指針,可以用來傳遞任何需要的數據,我們代碼中的任務就是控制GPIO輸出高或者低電平。
osThreadDef函數中的osPriorityNormal任務優先級:
任務優先級決定了任務在操作系統調度下的執行順序。數值越大,優先級越高。
osThreadCreate函數的返回值THREAD1Handle是任務句柄:
任務句柄是一個指針,可以用來引用已經創建的任務,以便可以在任務創建后對其進行操作,例如刪除、掛起、恢復等。
3.任務的調度原理
FreeRTOS默認使用搶占式調度策略,對同等優先級的任務使用時間片輪詢調度,時間片輪詢就是可輪流享有相同的單片機時間(可設置),一個時間片等于SysTick中斷周期。
搶占式調度是指調度器始終運行優先級最高且處于可運行狀態的任務,無論任務何時可以運行。如在中斷服務函數中更改了優先級最高且可運行的任務,調度器會停止當前執行的低優先級任務,并啟動高優先級任務。
剛才程序中的osDelay 是通過將當前任務加入到延時列表中,并設置一個定時器來在指定的延時時間,延時時間過去之后將任務從延時列表中移除并將其設置為就緒狀態。這樣當定時器觸發時,任務重新被加入到就緒列表中,等待被調度器再次調度執行。
上面代碼中的LED_Thread1(驅動LED燈滅)和LED_Thread2(驅動LED燈亮)兩個任務的調度順序以及時間,如下圖所示,橫坐標是時間,箭頭代表運行順序
下面代碼截圖就是操作系統底層尋找就緒任務的最高優先級,獲取優先級最高的就緒任務的 TCB(任務控制塊),然后更新到 pxCurrentTCB ,當前運行的任務只可能有一個,因此pxCurrentTCB只是單個TCB_t指針,它始終指向當前運行的任務。通過xPortPendSVHandler(PendSV_Handler)函數實現調度
PendSV_Handler 是 ARM Cortex-M 處理器中的一個特殊的中斷處理函數,用于處理掛起 PendSV(Pending Supervisor Call)中斷。PendSV 中斷是 Cortex-M 架構中的一種特殊的軟件中斷,它可以用來實現任務切換或者其他與系統調度相關的操作。
PendSV_Handler 在中斷向量表的位置
__asm void xPortPendSVHandler( void ){extern uxCriticalNesting;extern pxCurrentTCB;extern vTaskSwitchContext;PRESERVE8mrs r0, psp //(1) 讀取進程棧指針,保存在寄存器 R0 里面。isbldr r3, =pxCurrentTCB//(2) 獲取當前任務的任務控制塊,并將任務控制塊的地址保存在寄存器 R2 里面。ldr r2, [r3] //(3)tst r14, #0x10 //(4) 判斷任務是否使用了 FPUit eq //(5)vstmdbeq r0!, {s16-s31}// (6)stmdb r0!, {r4-r11, r14} //(7)str r0, [r2] //(8)stmdb sp!, {r3} //(9)mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY //(10) msr basepri, r0 //(11) 關閉中斷,進入臨界區dsbisbbl vTaskSwitchContext //(12) 調用函數 vTaskSwitchContext(),此函數用來獲取下一個要運行的任務,并將pxCurrentTCB 更新為這個要運行的任務。mov r0, #0 //(13)msr basepri, r0 //(14) 打開中斷,退出臨界區。ldmia sp!, {r3} //(15)ldr r1, [r3] //(16)ldr r0, [r1] //(17)ldmia r0!, {r4-r11, r14} //(18)tst r14, #0x10 //(19)it eq //(20)vldmiaeq r0!, {s16-s31} //(21)msr psp, r0 //(22) 更新進程棧指針 PSP 的值isbbx r14 //(23) 執行此行代碼以后硬件自動恢復寄存器 R0~R3、 R12、 LR、 PC 和 xPSR 的值,確定異常返回以后應該進入處理器模式還是進程模式,使用主棧指針(MSP)還是進程棧指針(PSP)。很明顯這里會進入進程模式,并且使用進程棧指針(PSP),寄存器 PC 值會被恢復為即將運行的任務的任務函數,新的任務開始運行!至此,任務切換成功。}
五.CubeMX配置一個FreeRTOS例程
1.硬件準備
STLINK接STM32F103C8T6小系統板,STLINK接電腦USB口。
2.創建工程
打開STM32CubeMX軟件,新建工程
Part Number處輸入STM32F103C8,再雙擊就創建新的工程
配置下載口引腳
配置外部晶振引腳
可以查看STM32F103C8T6小系統板原理圖,PC13連接LED燈,所以配置PC13為GPIO輸出
配置FreeRTOS
配置系統主頻
配置工程文件名,保存路徑,KEIL5工程輸出方式
生成工程
用Keil5打開工程
添加代碼
3.調試FreeRTOS任務調度
調試代碼,能更好理解任務調度。
下載調試,在LED_Thread1打斷點,程序能運行到斷點處停止
在LED_Thread2打斷點,程序也能運行到斷點處停止
在xPortPendSVHandler調度函數中打斷點,可以看到操作系統將要跳轉到任務1運行
單步執行,運行指針就跳轉到任務1開始運行
xPortPendSVHandler調度函數中打斷點,可以看到操作系統將要跳轉到任務2運行
單步執行,運行指針就跳轉到任務2開始運行
六.CubeMX工程源代碼下載
鏈接:https://pan.baidu.com/s/1f50X8w2UXnDtbM6U6zwYqw
提取碼:0sys
如果鏈接失效,可以聯系博主給最新鏈接
程序下載下來之后解壓就行
七.講解視頻鏈接地址
FreeRTOS實驗
八.小結
FreeRTOS實時操作系統能使在STM32軟件開發中,程序結構清晰,單片機執行效率提升許多,在多個任務模塊的代碼中,可以考慮使用FreeRTOS。