FreeRTOS-04-核心控制函式+時間管理函式

zhengcixi發表於2021-08-08

說明

本文僅作為學習FreeRTOS的記錄文件,作為初學者肯定很多理解不對甚至錯誤的地方,望網友指正。
FreeRTOS是一個RTOS(實時作業系統)系統,支援搶佔式、合作式和時間片排程。適用於微處理器或小型微處理器的實時應用。
本文件使用的FreeRTOS版本:FreeRTOS Kernel V10.4.1
參考文件:《FreeRTOS_Reference_Manual_V10.0.0.pdf》《FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf》《STM32F4 FreeRTOS開發手冊_V1.1.pdf》
參考視訊:正點原子FreeRTOS手把手教學-基於STM32_嗶哩嗶哩_bilibili

6 核心控制函式

核心控制函式就是FreeRTOS核心所使用的函式,一般情況下應用程式不使用這些函式。

官網API說明:FreeRTOS - Task Control Functions and Macros for the Free Open Source RTOS FreeRTOS

6.1 任務切換

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void taskYIELD( void );

函式描述:任務切換函式。如果沒有與當前任務同等優先順序或高優先順序的任務,則任務排程器會選擇當前任務繼續執行。必須在排程器初始化之後使用。

函式引數:

返回值:

測試程式碼:建立兩個任務,任務task0優先順序為2,任務函式中每次從0計數到2進行一次任務切換;任務task1優先順序也為2,任務函式中每次列印之後就掛起自己。

configSTACK_DEPTH_TYPE Task0_STACK_SIZE = 5;
UBaseType_t  Task0_Priority = 2;
TaskHandle_t Task0_xhandle;

configSTACK_DEPTH_TYPE Task1_STACK_SIZE = 5;
UBaseType_t  Task1_Priority = 2;
TaskHandle_t Task1_xhandle;

void task0_code(void *para)
{
    unsigned int i = 0;

    for (;;)
    {
        for (i = 0; i < 4; i++) {
            PRINT(" task0 cnt %u...", i);
            if (i == 2) {
                vTaskResume(Task1_xhandle);
                taskYIELD();
            }
        }
        vTaskDelay(2000);
    }
}

void task1_code(void *para)
{
    static unsigned int cnt = 0;

    for (;;)
    {
        PRINT(" task1 cnt %u...", cnt);
        cnt++;
        vTaskSuspend(Task1_xhandle);
    }
}

void creat_task(void)
{
    taskENTER_CRITICAL();
    
    if (xTaskCreate(task0_code, "task0 task", 
        Task0_STACK_SIZE, NULL, Task0_Priority,
        &Task0_xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    }

    if (xTaskCreate(task1_code, "task1 task", 
        Task1_STACK_SIZE, NULL, Task1_Priority,
        &Task1_xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    }
    
    taskEXIT_CRITICAL();
    vTaskStartScheduler();
}

編譯、執行,結果符合預期,每次排程taskYIELD()之後執行一次任務切換,結果如下:

$ ./build/freertos-simulator 
 task0 cnt 0...
 task0 cnt 1...
 task0 cnt 2...
 task1 cnt 0...
 task0 cnt 3...
 task0 cnt 0...
 task0 cnt 1...
 task0 cnt 2...
 task1 cnt 1...
 task0 cnt 3...

現在將任務task0的優先順序改為3,大於任務task1的優先順序:

UBaseType_t  Task0_Priority = 3;

編譯、執行,結果符合預期,每次排程taskYIELD()之後不會任務切換,結果如下:

$ ./build/freertos-simulator 
 task0 cnt 0...
 task0 cnt 1...
 task0 cnt 2...
 task0 cnt 3...
 task1 cnt 0...
 task0 cnt 0...
 task0 cnt 1...
 task0 cnt 2...
 task0 cnt 3...
 task1 cnt 1...
 task0 cnt 0...

6.2 進入臨界區

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void taskENTER_CRITICAL( void );

函式描述:進入臨界區,不能在中斷服務函式中呼叫。中斷服務函式中呼叫taskENTER_CRITICAL_FROM_ISR()進入臨界區。

函式引數:

返回值:

6.3 退出臨界區

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void taskEXIT_CRITICAL( void );

函式描述:退出臨界區,不能在中斷服務函式中呼叫。中斷服務函式中呼叫taskEXIT_CRITICAL_FROM_ISR()退出臨界區。

函式引數:

返回值:

taskENTER_CRITICAL()和taskEXIT_CRITICAL()函式用於臨界段程式碼保護(任務級)。

taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()函式用於中斷級臨界段程式碼保護。

臨界段程式碼:也叫做臨界區,指那些必須完整執行,不能被打斷的程式碼。比如某些外設的初始化。FreeRTOS在進入臨界段程式碼的時候需要關閉中斷,當處理完臨界段程式碼以後再開啟中斷。

6.4 關閉可遮蔽中斷

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void taskDISABLE_INTERRUPTS( void );

函式描述:關閉可遮蔽中斷。不可巢狀使用。

函式引數:

返回值:

6.4 開啟可遮蔽中斷

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void taskENABLE_INTERRUPTS( void );

函式描述:關閉可遮蔽中斷。不可巢狀使用。

函式引數:

返回值:

6.5 啟動任務排程器

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void vTaskStartScheduler( void );

函式描述:啟動任務排程器。典型應用為:main()函式先於排程器使用,排程器啟動之後,執行任務及中斷函式。排程器啟動之後將選擇優先順序最高的任務進行執行。排程器啟動之後,空閒任務(Idle task)將自動被建立。

函式引數:

返回值:

6.5 關閉任務排程器

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void vTaskEndScheduler( void );

函式描述:關閉任務排程器。僅支援x86架構處理器。關閉任務排程器之後,核心時鐘將停止計數,所有建立的任務都會自動刪除。

函式引數:

返回值:

7 時間管理

FreeRTOS延時函式分為相對模式和絕對模式。vTaskDelay()是相對延時函式。vTaskDelayUntil()是絕對延時函式。

官網API說明:Links to the FreeRTOS task control API function vTaskDelay, vTaskDelayUntil, uxTaskPriorityGet, vTaskPrioritySet, vTaskSuspend, vTaskResume, xTaskResumeFromISR, vTaskSetApplicationTaskTag, xTaskCallApplicationTaskHook

7.1 相對延時

函式原型:

#include "FreeRTOS.h"
#include "task.h"

void vTaskDelay( const TickType_t xTicksToDelay );

函式描述:呼叫該函式的任務將進入阻塞態,中斷一段固定的時鐘週期。使用這個函式必須將巨集INCLUDE_vTaskDelay置1。

函式引數:xTicksToDelay表示呼叫函式的任務的阻塞態保持時間,單位為時鐘節拍數。真正的延時時間取決於時鐘節拍頻率。巨集 portTICK_PERIOD_MS被用來根據時鐘節拍數來計算一個時鐘節拍的延時週期。

#define portTICK_PERIOD_MS			( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define configTICK_RATE_HZ						( 1000 )

可以看出,單個時鐘節拍計數時間為1ms,比如引數xTicksToDelay設為100,就表示延時100ms。

延時達到之後將進入就緒態。例如:當時鍾計數到10000時,函式呼叫了vTaskDelay(100),然後任務進入阻塞態,並且保持阻塞態直到時鐘計數到10100。

巨集pdMS_TO_TICKS()可以被使用來延時毫秒。例如:呼叫vTaskDelay( pdMS_TO_TICKS(100) ),任務將進入阻塞態100毫秒。

如果引數xTicksToDelay為0,則等同於呼叫了一次taskYIELD()函式進行了一次任務切換。

返回值:

7.2 絕對延時

函式原型:

#include “FreeRTOS.h”
#include “task.h”
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement );

函式描述:呼叫該函式的任務將進入阻塞態直到一個絕對的時間到來。週期任務可以呼叫這個函式來實現一個固定的執行頻率。使用這個函式必須將巨集INCLUDE_vTaskDelayUntil置1。

函式引數:pxPreviousWakeTime:上一次任務延時結束被喚醒的時間點,任務中第一次呼叫函式vTaskDelayUntil()需要將pxPreviousWakeTime初始化為進入任務的while()迴圈體的時間點值。在以後的執行中函式vTaskDelayUntil()會自動更新pxPreviousWakeTime。

xTimeIncrement:任務需要延時的節拍數(相對於pxPreviousWakeTime本次延時的節拍數),也就是任務在pxPreviousWakeTime+xTimeIncrementpd時鐘計數時從阻塞態恢復。MS_TO_TICKS()巨集可用於延時毫秒。

image-20210808134418550

(1)為任務主體,也就是任務執行的工作;(2)為任務呼叫vTaskDelayUntil()函式;(3)為其它任務執行。任務延時時間為xTimeIncrement,可看出任務總的執行時間一定小於任務的延時時間,也就是說使用vTaskDelayUntil()函式任務的執行週期永遠是xTimeIncrement,而任務一定要在這個時間內完成,這個延時值就是絕對延時時間。

上面圖中,xConstTickCount和xTimeToWake可能溢位,這些情況暫不討論,這裡僅說明函式的用法。

測試程式碼:建立一個任務,使用絕對延時函式延時50ms。

configSTACK_DEPTH_TYPE Task1_STACK_SIZE = 5;
UBaseType_t  Task1_Priority = 2;
TaskHandle_t Task1_xhandle;

void task1_code(void *para)
{
    unsigned int cnt = 0;
    TickType_t xLastWakeTime;
    const TickType_t xPeriod = pdMS_TO_TICKS(500);

    xLastWakeTime = xTaskGetTickCount();
    for (;;)
    {
        vTaskDelayUntil(&xLastWakeTime, xPeriod);
        PRINT(" task1 cnt %u...", cnt);
        cnt++;
    }

}

void creat_task(void)
{
    
    if (xTaskCreate(task1_code, "task1 task", 
        Task1_STACK_SIZE, NULL, Task1_Priority,
        &Task1_xhandle) != pdPASS)
    {
        PRINT("creat task failed!\n");
    }

}

7.3 系統時鐘節拍

xTickCount是FreeRTOS系統時鐘節拍計數器,每個滴答定時器中斷中xTickCount就會加1。xTickCount具體操作過程在xTaskIncrementTick()函式中進行,這個函式在時鐘計數器中斷函式中呼叫。

相關文章