freertos移植教程
準備
在移植之前,我們首先要獲取到FreeRTOS的官方的源碼包。這里我們提供兩個下載鏈接:
一個是官網:http://www.freertos.org/
另外一個是代碼托管網站:https://sourceforge.net/projects/freertos/files/FreeRTOS/
這里我們演示如何在代碼托管網站里面下載。打開網站鏈接之后,我們選擇FreeRTOS的最新版本V9.0.0(2016年),盡管現在FreeRTOS的版本已經更新到V10.0.1了,但是我們還是選擇V9.0.0,因為內核很穩定,并且網上資料很多,因為V10.0.0版本之后是亞馬遜收購了FreeRTOS之后才出來的版本,主要添加了一些云端組件,我們本書所講的FreeRTOS是實時內核,采用V9.0.0版本足以。
簡單介紹FreeRTOS
FreeRTOS包含Demo例程和內核源碼(比較重要,我們就需要提取該目錄下的大部分文件)。
Source文件夾里面包含的是FreeRTOS內核的源代碼,我們移植FreeRTOS的時候就需要這部分源代碼;
Demo 文件夾里面包含了FreeRTOS官方為各個單片機移植好的工程代碼,FreeRTOS為了推廣自己,會給各種半導體廠商的評估板寫好完整的工程程序,這些程序就放在Demo這個目錄下,這部分Demo非常有參考價值。
Source文件夾
這里我們再重點分析下FreeRTOS/ Source文件夾下的文件,①和③包含的是FreeRTOS的通用的頭文件和C文件,這兩部分的文件試用于各種編譯器和處理器,是通用的。需要移植的頭文件和C文件放在②portblle這個文件夾。
portblle文件夾,是與編譯器相關的文件夾,在不同的編譯器中使用不同的支持文件。①中的KEIL就是我們就是我們使用的編譯器,其實KEIL里面的內容跟RVDS里面的內容一樣,所以我們只需要③RVDS文件夾里面的內容即可,里面包含了各種處理器相關的文件夾,從文件夾的名字我們就非常熟悉了,我們學習的STM32有M0、M3、M4等各種系列,FreeRTOS是一個軟件,單片機是一個硬件,FreeRTOS要想運行在一個單片機上面,它們就必須關聯在一起。MemMang文件夾下存放的是跟內存管理相關的源文件。
移植過程
提取源碼
1.首先在我們的STM32裸機工程模板根目錄下新建一個文件夾,命名為“FreeRTOS”,并且在FreeRTOS文件夾下新建兩個空文件夾,分別命名為“src”與“port”,src文件夾用于保存FreeRTOS中的核心源文件,也就是我們常說的‘.c文件’,port文件夾用于保存內存管理以及處理器架構相關代碼,這些代碼FreeRTOS官方已經提供給我們的,直接使用即可,在前面已經說了,FreeRTOS是軟件,我們的開發版是硬件,軟硬件必須有橋梁來連接,這些與處理器架構相關的代碼,可以稱之為RTOS硬件接口層,它們位于FreeRTOS/Source/Portable文件夾下。
2.打開FreeRTOS V9.0.0源碼,在“FreeRTOSv9.0.0\FreeRTOS\Source”目錄下找到所有的‘.c文件’,將它們拷貝到我們新建的src文件夾中,
3.打開FreeRTOS V9.0.0源碼,在“FreeRTOSv9.0.0\FreeRTOS\Source\portable”目錄下找到“MemMang”文件夾與“RVDS”文件夾,將它們拷貝到我們新建的port文件夾中
4.打開FreeRTOS V9.0.0源碼,在“FreeRTOSv9.0.0\ FreeRTOS\Source”目錄下找到“include”文件夾,它是我們需要用到FreeRTOS的一些頭文件,將它直接拷貝到我們新建的FreeRTOS文件夾中,完成這一步之后就可以看到我們新建的FreeRTOS文件夾已經有3個文件夾,這3個文件夾就包含FreeRTOS的核心文件,至此,FreeRTOS的源碼就提取完成。
添加到工程
添加FreeRTOSConfig.h文件
FreeRTOSConfig.h文件是FreeRTOS的工程配置文件,因為FreeRTOS是可以裁剪的實時操作內核,應用于不同的處理器平臺,用戶可以通過修改這個FreeRTOS內核的配置頭文件來裁剪FreeRTOS的功能,所以我們把它拷貝一份放在user這個文件夾下面。
打開FreeRTOSv9.0.0源碼,在“FreeRTOSv9.0.0\FreeRTOS\Demo”文件夾下面找到“CORTEX_STM32F103_Keil”這個文件夾,雙擊打開,在其根目錄下找到這個“FreeRTOSConfig.h”文件,然后拷貝到我們工程的user文件夾下即可,等下我們需要對這個文件進行修改。
創建工程分組
接下來我們在mdk里面新建FreeRTOS/src和FreeRTOS/port兩個組文件夾,其中FreeRTOS/src用于存放src文件夾的內容,FreeRTOS/port用于存放port\MemMang文件夾 與port\RVDS\ARM_CM3文件夾的內容。
然后我們將工程文件中FreeRTOS的內容添加到工程中去,按照已經新建的分組添加我們的FreeRTOS工程源碼。
在FreeRTOS/port分組中添加MemMang文件夾中的文件只需選擇其中一個即可,我們選擇“heap_4.c”,這是FreeRTOS的一個內存管理源碼文件。
添加完成后:
** 添加頭文件路徑**
FreeRTOS的源碼已經添加到開發環境的組文件夾下面,編譯的時候需要為這些源文件指定頭文件的路徑,不然編譯會報錯。FreeRTOS的源碼里面只有FreeRTOS\include和FreeRTOS\port\RVDS\ARM_CM3這兩個文件夾下面有頭文件,只需要將這兩個頭文件的路徑在開發環境里面指定即可。同時我們還將FreeRTOSConfig.h這個頭文件拷貝到了工程根目錄下的user文件夾下,所以user的路徑也要加到開發環境里面。
修改FreeRTOSConfig.h
FreeRTOSConfig.h是直接從demo文件夾下面拷貝過來的,該頭文件對裁剪整個FreeRTOS所需的功能的宏均做了定義,有些宏定義被使能,有些宏定義被失能,一開始我們只需要配置最簡單的功能即可。要想隨心所欲的配置FreeRTOS的功能,我們必須對這些宏定義的功能有所掌握,下面我們先簡單的介紹下這些宏定義的含義,然后再對這些宏定義進行修改。
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include “stm32f10x.h”
#include “bsp_usart.h”
//針對不同的編譯器調用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include 《stdint.h》
extern uint32_t SystemCoreClock;
#endif
//斷言
#define vAssertCalled(char,int) printf(“Error:%s,%d\r\n”,char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
/************************************************************************
* FreeRTOS基礎配置配置選項
*********************************************************************/
/* 置1:RTOS使用搶占式調度器;置0:RTOS使用協作式調度器(時間片)
*
* 注:在多任務管理機制上,操作系統可以分為搶占式和協作式兩種。
* 協作式操作系統是任務主動釋放CPU后,切換到下一個任務。
* 任務切換的時機完全取決于正在運行的任務。
*/
#define configUSE_PREEMPTION 1
//1使能時間片調度(默認式使能的)
#define configUSE_TIME_SLICING 1
/* 某些運行FreeRTOS的硬件有兩種方法選擇下一個要執行的任務:
* 通用方法和特定于硬件的方法(以下簡稱“特殊方法”)。
*
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION 為 0 或者硬件不支持這種特殊方法。
* 2.可以用于所有FreeRTOS支持的硬件
* 3.完全用C實現,效率略低于特殊方法。
* 4.不強制要求限制最大可用優先級數目
* 特殊方法:
* 1.必須將configUSE_PORT_OPTIMISED_TASK_SELECTION設置為1。
* 2.依賴一個或多個特定架構的匯編指令(一般是類似計算前導零[CLZ]指令)。
* 3.比通用方法更高效
* 4.一般強制限定最大可用優先級數目為32
* 一般是硬件計算前導零指令,如果所使用的,MCU沒有這些硬件指令的話此宏應該設置為0!
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 置1:使能低功耗tickless模式;置0:保持系統節拍(tick)中斷一直運行
* 假設開啟低功耗的話可能會導致下載出現問題,因為程序在睡眠中,可用以下辦法解決
*
* 下載方法:
* 1.將開發版正常連接好
* 2.按住復位按鍵,點擊下載瞬間松開復位按鍵
*
* 1.通過跳線帽將 BOOT 0 接高電平(3.3V)
* 2.重新上電,下載
*
* 1.使用FlyMcu擦除一下芯片,然后進行下載
* STMISP -》 清除芯片(z)
*/
#define configUSE_TICKLESS_IDLE 0
/*
* 寫入實際的CPU內核時鐘頻率,也就是CPU指令執行頻率,通常稱為Fclk
* Fclk為供給CPU內核的時鐘信號,我們所說的cpu主頻為 XX MHz,
* 就是指的這個時鐘信號,相應的,1/Fclk即為cpu時鐘周期;
*/
#define configCPU_CLOCK_HZ (SystemCoreClock)
//RTOS系統節拍中斷的頻率。即一秒中斷的次數,每次中斷RTOS都會進行任務調度
#define configTICK_RATE_HZ (( TickType_t )1000)
//可使用的最大優先級
#define configMAX_PRIORITIES (32)
//空閑任務使用的堆棧大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
//任務名字字符串長度
#define configMAX_TASK_NAME_LEN (16)
//系統節拍計數器變量數據類型,1表示為16位無符號整形,0表示為32位無符號整形
#define configUSE_16_BIT_TICKS 0
//空閑任務放棄CPU使用權給其他同優先級的用戶任務
#define configIDLE_SHOULD_YIELD 1
//啟用隊列
#define configUSE_QUEUE_SETS 1
//開啟任務通知功能,默認開啟
#define configUSE_TASK_NOTIFICATIONS 1
//使用互斥信號量
#define configUSE_MUTEXES 1
//使用遞歸互斥信號量
#define configUSE_RECURSIVE_MUTEXES 1
//為1時使用計數信號量
#define configUSE_COUNTING_SEMAPHORES 1
/* 設置可以注冊的信號量和消息隊列個數 */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_APPLICATION_TASK_TAG 0
/*****************************************************************
FreeRTOS與內存申請有關配置選項
*****************************************************************/
//支持動態內存申請
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持靜態內存
#define configSUPPORT_STATIC_ALLOCATION 0
//系統所有總的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))
/***************************************************************
FreeRTOS與鉤子函數有關的配置選項
**************************************************************/
/* 置1:使用空閑鉤子(Idle Hook類似于回調函數);置0:忽略空閑鉤子
*
* 空閑任務鉤子是一個函數,這個函數由用戶來實現,
* FreeRTOS規定了函數的名字和參數:void vApplicationIdleHook(void ),
* 這個函數在每個空閑任務周期都會被調用
* 對于已經刪除的RTOS任務,空閑任務可以釋放分配給它們的堆棧內存。
* 因此必須保證空閑任務可以被CPU執行
* 使用空閑鉤子函數設置CPU進入省電模式是很常見的
* 不可以調用會引起空閑任務阻塞的API函數
*/
#define configUSE_IDLE_HOOK 0
/* 置1:使用時間片鉤子(Tick Hook);置0:忽略時間片鉤子
*
*
* 時間片鉤子是一個函數,這個函數由用戶來實現,
* FreeRTOS規定了函數的名字和參數:void vApplicationTickHook(void )
* 時間片中斷可以周期性的調用
* 函數必須非常短小,不能大量使用堆棧,
* 不能調用以”FromISR“ 或 ”FROM_ISR”結尾的API函數
*/
/*xTaskIncrementTick函數是在xPortSysTickHandler中斷函數中被調用的。因此,vApplicationTickHook()函數執行的時間必須很短才行*/
#define configUSE_TICK_HOOK 0
//使用內存申請失敗鉤子函數
#define configUSE_MALLOC_FAILED_HOOK 0
/*
* 大于0時啟用堆棧溢出檢測功能,如果使用此功能
* 用戶必須提供一個棧溢出鉤子函數,如果使用的話
* 此值可以為1或者2,因為有兩種棧溢出檢測方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0
/********************************************************************
FreeRTOS與運行時間和任務狀態收集有關的配置選項
**********************************************************************/
//啟用運行時間統計功能
#define configGENERATE_RUN_TIME_STATS 0
//啟用可視化跟蹤調試
#define configUSE_TRACE_FACILITY 0
/* 與宏configUSE_TRACE_FACILITY同時為1時會編譯下面3個函數
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
/********************************************************************
FreeRTOS與協程有關的配置選項
*********************************************************************/
//啟用協程,啟用協程以后必須添加文件croutine.c
#define configUSE_CO_ROUTINES 0
//協程的有效優先級數目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/***********************************************************************
FreeRTOS與軟件定時器有關的配置選項
**********************************************************************/
//啟用軟件定時器
#define configUSE_TIMERS 1
//軟件定時器優先級
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//軟件定時器隊列長度
#define configTIMER_QUEUE_LENGTH 10
//軟件定時器任務堆棧大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
/************************************************************
FreeRTOS可選函數配置選項
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0
/******************************************************************
FreeRTOS與中斷有關的配置選項
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中斷最低優先級
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
//系統可管理的最高中斷優先級
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY 《《 (8 - configPRIO_BITS) ) /* 240 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 《《 (8 - configPRIO_BITS) )
/****************************************************************
FreeRTOS與中斷服務函數有關的配置選項
****************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
/* 以下為使用Percepio Tracealyzer需要的東西,不需要時將 configUSE_TRACE_FACILITY 定義為 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include “trcRecorder.h”
#define INCLUDE_xTaskGetCurrentTaskHandle 1 // 啟用一個可選函數(該函數被 Trace源碼使用,默認該值為0 表示不用)
#endif
#endif /* FREERTOS_CONFIG_H */
修改stm32f10x_it.c
SysTick中斷服務函數是一個非常重要的函數,FreeRTOS所有跟時間相關的事情都在里面處理,SysTick就是FreeRTOS的一個心跳時鐘,驅動著FreeRTOS的運行,就像人的心跳一樣,假如沒有心跳,我們就相當于“死了”,同樣的,FreeRTOS沒有了心跳,那么它就會卡死在某個地方,不能進行任務調度,不能運行任何的東西,因此我們需要實現一個FreeRTOS的心跳時鐘,FreeRTOS幫我們實現了SysTick的啟動的配置:在port.c文件中已經實現vPortSetupTimerInterrupt()函數,并且FreeRTOS通用的SysTick中斷服務函數也實現了:在port.c文件中已經實現xPortSysTickHandler()函數,所以移植的時候只需要我們在stm32f10x_it.c文件中實現我們對應(STM32)平臺上的SysTick_Handler()函數即可。FreeRTOS為開發者考慮得特別多,PendSV_Handler()與SVC_Handler()這兩個很重要的函數都幫我們實現了,在在port.c文件中已經實現xPortPendSVHandler()與vPortSVCHandler()函數,防止我們自己實現不了,那么在stm32f10x_it.c中就需要我們注釋掉PendSV_Handler()與SVC_Handler()這兩個函數了。
//void SVC_Handler(void)
//{
//}
//void PendSV_Handler(void)
//{
//}
extern void xPortSysTickHandler(void);
//systick中斷服務函數
void SysTick_Handler(void)
{
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
}
創建任務
這里,我們創建一個單任務,任務使用的棧和任務控制塊是在創建任務的時候FreeRTOS動態分配的。
任務必須是一個死循環,否則任務將通過LR返回,如果LR指向了非法的內存就會產生HardFault_Handler,而FreeRTOS指向一個死循環,那么任務返回之后就在死循環中執行,這樣子的任務是不安全的,所以避免這種情況,任務一般都是死循環并且無返回值的。
并且每個任務循環主體中應該有阻塞任務的函數,否則就會餓死比它優先級更低的任務!!!
/* FreeRTOS頭文件 */
#include “FreeRTOS.h”
#include “task.h”
/* 開發板硬件bsp頭文件 */
#include “bsp_led.h”
static void AppTaskCreate(void);/* AppTask任務 */
/* 創建任務句柄 */
static TaskHandle_t AppTask_Handle = NULL;
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認為pdPASS */
/* 開發板硬件初始化 */
BSP_Init();
/* 創建AppTaskCreate任務 */
xReturn = xTaskCreate((TaskFunction_t )AppTask, /* 任務入口函數 */
(const char* )“AppTask”,/* 任務名字 */
(uint16_t )512, /* 任務棧大小 */
(void* )NULL,/* 任務入口函數參數 */
(UBaseType_t )1, /* 任務的優先級 */
(TaskHandle_t* )&AppTask_Handle);/* 任務控制塊指針 */
/* 啟動任務調度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 啟動任務,開啟調度 */
else
return -1;
while(1); /* 正常不會執行到這里 */
}
static void AppTask(void* parameter)
{
while (1)
{
LED1_ON;
vTaskDelay(500); /* 延時500個tick */
LED1_OFF;
vTaskDelay(500); /* 延時500個tick */
}
}
freertos任務調度原理
本章教程為大家將介紹 FreeRTOS 操作系統支持的任務調度方式:搶占式,時間片和合作式,這部分算是 FreeRTOS 操作系統的核心了。 對于初學者來說,要一下子就能夠理解這些比較困難,需要多花些時間把這些基本概念搞清楚,然后閱讀下源碼,深入理解實現方法。
關于合作式調度器的特別說明
FreeRTOS 支持的調度方式
FreeRTOS 操作系統支持三種調度方式:搶占式調度,時間片調度和合作式調度。 實際應用主要是搶占式調度和時間片調度,合作式調度用到的很少。搶占式調度每個任務都有不同的優先級,任務會一直運行直到被高優先級任務搶占或者遇到阻塞式的 API 函數,比如 vTaskDelay。時間片調度每個任務都有相同的優先級,任務會運行固定的時間片個數或者遇到阻塞式的 API 函數,比如vTaskDelay,才會執行同優先級任務之間的任務切換。
什么是調度器
簡單的說,調度器就是使用相關的調度算法來決定當前需要執行的任務。所有的調度器有一個共同的特性:
調度器可以區分就緒態任務和掛起任務(由于延遲,信號量等待,郵箱等待,事件組等待等原因而使得任務被掛起)。
調度器可以選擇就緒態中的一個任務,然后激活它(通過執行這個任務)。 當前正在執行的任務是運行態的任務。
不同調度器之間最大的區別就是如何分配就緒態任務間的完成時間。
嵌入式實時操作系統的核心就是調度器和任務切換,調度器的核心就是調度算法。任務切換的實現在不同的嵌入式實時操作系統中區別不大,基本相同的硬件內核架構,任務切換也是相似的。調度算法就有些區別了。下面我們主要了解一下搶占式調度器和時間片調度器。
搶占式調度器基本概念
在實際的應用中,不同的任務需要不同的響應時間。例如,我們在一個應用中需要使用電機,鍵盤和LCD 顯示。電機比鍵盤和 LCD 需要更快速的響應,如果我們使用合作式調度器或者時間片調度,那么電機將無法得到及時的響應,這時搶占式調度是必須的。
如果使用了搶占式調度,最高優先級的任務一旦就緒,總能得到 CPU 的控制權。 比如,當一個運行著的任務被其它高優先級的任務搶占,當前任務的 CPU 使用權就被剝奪了,或者說被掛起了,那個高優先級的任務立刻得到了 CPU 的控制權并運行。 又比如,如果中斷服務程序使一個高優先級的任務進入就緒態,中斷完成時,被中斷的低優先級任務被掛起,優先級高的那個任務開始運行。
使用搶占式調度器,使得最高優先級的任務什么時候可以得到 CPU 的控制權并運行是可知的,同時使得任務級響應時間得以最優化。
總的來說,學習搶占式調度要掌握的最關鍵一點是:每個任務都被分配了不同的優先級,搶占式調度器會獲得就緒列表中優先級最高的任務,并運行這個任務。
FreeRTOS 搶占式調度器的實現
如果用戶在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中禁止使用時間片調度, 那么每個任務必須配置不同的優先級。當 FreeRTOS 多任務啟動執行后,基本會按照如下的方式去執行:
首先執行的最高優先級的任務 Task1, Task1 會一直運行直到遇到系統阻塞式的 API 函數,比如延遲,事件標志等待,信號量等待,Task1 任務會被掛起,也就是釋放 CPU 的執行權,讓低優先級的任務得到執行。
FreeRTOS 操作系統繼續執行任務就緒列表中下一個最高優先級的任務 Task2,Task2 執行過程中有兩種情況:
Task1由于延遲時間到, 接收到信號量消息等方面的原因, 使得 Task1從掛起狀態恢復到就緒態,在搶占式調度器的作用下,Task2 的執行會被 Task1 搶占。
Task2 會一直運行直到遇到系統阻塞式的 API 函數,比如延遲,事件標志等待,信號量等待, Task2任務會被掛起,繼而執行就緒列表中下一個最高優先級的任務。
如果用戶創建了多個任務并且采用搶占式調度器的話,基本都是按照上面兩條來執行。 根據搶占式調度器,當前的任務要么被高優先級任務搶占,要么通過調用阻塞式 API 來釋放 CPU 使用權讓低優先級任務執行,沒有用戶任務執行時就執行空閑任務。
運行條件:
這里僅對搶占式調度進行說明。
創建 3 個任務 Task1,Task2 和 Task3。
Task1 的優先級為 1,Task2 的優先級為 2,Task3 的優先級為 3。 FreeRTOS 操作系統是設置的數值越小任務優先級越低,故 Task3 的優先級最高,Task1 的優先級最低。
此框圖是 FreeRTOS 操作系統運行過程中的一部分。
運行過程描述如下:
此時任務 Task1 在運行中,運行過程中由于 Task2 就緒,在搶占式調度器的作用下任務 Task2 搶占Task1 的執行。 Task2 進入到運行態,Task1 由運行態進入到就緒態。
任務 Task2 在運行中,運行過程中由于 Task3 就緒,在搶占式調度器的作用下任務 Task3 搶占 Task2的執行。 Task3 進入到運行態,Task2 由運行態進入到就緒態。
任務 Task3 運行過程中調用了阻塞式 API 函數,比如 vTaskDelay,任務 Task3 被掛起,在搶占式調度器的作用下查找到下一個要執行的最高優先級任務是 Task2,任務 Task2 由就緒態進入到運行態。
任務 Task2 在運行中,運行過程中由于 Task3 再次就緒,在搶占式調度器的作用下任務 Task3 搶占Task2 的執行。 Task3 進入到運行態,Task2 由運行態進入到就緒態。
上面就是一個簡單的不同優先級任務通過搶占式調度進行任務調度和任務切換的過程。
時間片調度器基本概念
在小型的嵌入式 RTOS 中,最常用的的時間片調度算法就是 Round-robin 調度算法。這種調度算法可以用于搶占式或者合作式的多任務中。另外,時間片調度適合用于不要求任務實時響應的情況。
實現 Round-robin 調度算法需要給同優先級的任務分配一個專門的列表,用于記錄當前就緒的任務,并為每個任務分配一個時間片(也就是需要運行的時間長度,時間片用完了就進行任務切換)。
FreeRTOS 時間片調度器的實現在 FreeRTOS 操作系統中只有同優先級任務才會使用時間片調度,另外還需要用戶在FreeRTOSConfig.h 文件中使能宏定義:
#define configUSE_TIME_SLICING 1默認情況下,此宏定義已經在 FreeRTOS.h 文件里面使能了,用戶可以不用在 FreeRTOSConfig.h 文件中再單獨使能。
下面我們通過如下的框圖來說明一下時間片調度在 FreeRTOS 中的運行過程,讓大家有一個形象的認識。
運行條件:
這里僅對時間片調度進行說明。
創建 4 個同優先級任務 Task1,Task2,Task3 和 Task4。
每個任務分配的時間片大小是 5 個系統時鐘節拍。
運行過程描述如下:
先運行任務 Task1,運行夠 5 個系統時鐘節拍后,通過時間片調度切換到任務 Task2。
任務 Task2 運行夠 5 個系統時鐘節拍后,通過時間片調度切換到任務 Task3。
任務 Task3 在運行期間調用了阻塞式 API 函數,調用函數時,雖然 5 個系統時鐘節拍的時間片大小還沒有用完,此時依然會通過時間片調度切換到下一個任務 Task4。 (注意,沒有用完的時間片不會再使用,下次任務 Task3 得到執行還是按照 5 個系統時鐘節拍運行)
任務 Task4 運行夠 5 個系統時鐘節拍后,通過時間片調度切換到任務 Task1。
上面就是一個簡單的同優先級任務通過時間片調度進行任務調度和任務切換的過程。
Summary:
時間片調度和搶占式調度我們一般都是開啟了的,這樣優先級相同時,使用時間片調度,優先級不同時,使用搶占式調度。默認情況下,在freertos.h中使能了時間片調度:
而搶占式調度需要我們用戶自己開啟,一般在freertosconfig.h中使能:
小編推薦閱讀:
評論
查看更多