06. FreeRTOS的任务管理

一、FreeRTOS任务相关API函数

1.1、获取指定任务的任务优先级

  uxTaskPriorityGet() 函数用于 获取指定任务的任务优先级,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_uxTaskPriorityGet 为 1,此函数的函数原型如下所示:

// 返回值:指定任务的优先级
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );      // 待获取优先级的任务

1.2、设置指定任务的优先级

  vTaskPrioritySet() 此函数用于 设置指定任务的优先级,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_vTaskPrioritySet 为 1,此函数的函数原型如下所示:

void vTaskPrioritySet( TaskHandle_t xTask,                      // 待设置优先级的任务
                       UBaseType_t uxNewPriority );             // 任务优先级

1.3、获取所有任务的状态信息

  uxTaskGetSystemState() 函数用于 获取所有任务的状态信息,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 configUSE_TRACE_FACILITY 为 1,此函数的函数原型如下所示:

// 返回值:获取信息的任务数量
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,                   // 接收信息变量数组的首地址
                                  const UBaseType_t uxArraySize,                            // 接收信息变量数组的大小
                                  configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime );    // 系统总运行时间

  函数 uxTaskGetSystemState() 的形参 pxTaskStatusArray 指向变量类型为 TaskStatus_t 的变量的首地址,可以是一个数组,用来存放多个 TaskStatus_t 类型的变量,函数 uxTaskGetSystemState() 使用将任务的状态信息,写入到该数组中,形参 uxArraySize 指示该数组的大小,其中变量类型 TaskStatus_t 的定义如下所示:

typedef struct xTASK_STATUS
{
    TaskHandle_t xHandle;                           // 任务句柄
    const char * pcTaskName;                        // 任务名
    UBaseType_t xTaskNumber;                        // 任务编号
    eTaskState eCurrentState;                       // 任务状态
    UBaseType_t uxCurrentPriority;                  // 任务优先级
    UBaseType_t uxBasePriority;                     // 任务原始优先级
    configRUN_TIME_COUNTER_TYPE ulRunTimeCounter;   // 任务被分配的运行时间
    StackType_t * pxStackBase;                      // 任务栈的基地址
    #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
        StackType_t * pxTopOfStack;                 // 任务栈顶
        StackType_t * pxEndOfStack;                 // 任务栈底
    #endif
    configSTACK_DEPTH_TYPE usStackHighWaterMark;    // 任务栈历史剩余最小值
    #if ( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
        UBaseType_t uxCoreAffinityMask;
    #endif
} TaskStatus_t;

  该结构体变量就包含了任务的一些状态信息,获取到的每个任务都有与之对应的 TaskStatus_t 结构体来保存该任务的状态信息。

1.4、获取指定任务的任务信息

  vTaskGetInfo()函数用于 获取指定任务的任务信息,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 configUSE_TRACE_FACILITY 为 1,此函数的函数原型如下所示:

void vTaskGetInfo( TaskHandle_t xTask,                      // 指定获取信息的任务
                    TaskStatus_t * pxTaskStatus,            // 接收任务信息的变量
                    BaseType_t xGetFreeStackSpace,          // 任务栈历史剩余最小值
                    eTaskState eState );                    // 任务状态

  形参 xGetFreeStackSpace 用来检查栈历史剩余最小值。当值为 pdFALSE 则跳过这个步骤。当值为 pdTRUE 则检查历史剩余最小堆栈。

  形参 eState 用来表示任务的状态,其变量类型为 eTaskState,变量类型 eTaskState 的定义如下所示:

typedef enum
{
    eRunning = 0,           // 运行态
    eReady,                 // 就绪态
    eBlocked,               // 阻塞态
    eSuspended,             // 挂起态
    eDeleted,               // 任务被删除
    eInvalid                // 非法值
} eTaskState;

  形参 eState 用于决定形参 pxTaskStatus 结构体中成员变量 eCurrentState 的值,表示任务的状态,如果传入的 eState 为 eInvalid,那么 eCurrentState 为任务当前的状态,否则 eCurrentState 为 eState。

1.5、获取指定任务的Tag

  xTaskGetApplicationTaskTag() 函数用于 获取指定任务的 Tag,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 configUSE_APPLICATION_TASK_TAG 为 1,此函数的函数原型如下所示:

// 返回指向任务的 Tag 的指针
TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask );        // xTask为待获取 Tag 的任务

1.6、获取正在运行的任务的任务句柄

  xTaskGetCurrentTaskHandle() 函数用于 获取当前系统正在运行的任务的任务句柄, 若使用此函数,需 在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_xTaskGetCurrentTaskHandle 为 1,此函数的函数原型如下所示:

// 返回值:当前系统正在运行的任务的任务句柄
TaskHandle_t xTaskGetCurrentTaskHandle( void );

1.7、通过任务名获取任务句柄

  xTaskGetHandle() 此函数用于 通过任务名获取任务句柄,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_xTaskGetHandle 为 1,此函数的函数原型如下所示:

// 返回值:任务句柄
TaskHandle_t xTaskGetHandle( const char * pcNameToQuery );      // pcNameToQuery为任务名

1.8、获取空闲任务的任务句

  xTaskGetIdleTaskHandle() 此函数用于 获取空闲任务的任务句柄,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_xTaskGetIdleTaskHandle 为 1,此函数的函数原型如下所示:

// 返回值:空闲任务的任务句柄
TaskHandle_t xTaskGetIdleTaskHandle( void );

1.9、获取指定任务的任务栈的历史剩余最小值

  uxTaskGetStackHighWaterMark() 此函数用于 获取指定任务的任务栈的历史剩余最小值, 若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_uxTaskGetStackHighWaterMark 为 1,此函数的函数原型如下所示:

// 返回值:任务栈的历史剩余最小值
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );      // xTask为待获取任务栈历史剩余最小值的任务

1.10、获取指定任务的状态

  eTaskGetState() 函数用于 获取指定任务的状态,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 INCLUDE_eTaskGetState 为 1,此函数的函数原型如下所示:

// 返回值:任务状态
eTaskState eTaskGetState( TaskHandle_t xTask );             // xTask为待获取状态的任务

1.11、获取指定任务的任务名

  pcTaskGetName() 函数用于 获取指定任务的任务名,此函数的函数原型如下所示:

// 返回值:任务名
char * pcTaskGetName( TaskHandle_t xTaskToQuery );      // xTaskToQuery为任务句柄

1.12、获取系统时钟节拍计数器的值

  xTaskGetTickCount() 函数用于 获取系统时钟节拍计数器的值,此函数的函数原型如下所示:

// 返回值:系统时钟节拍计数器的值
TickType_t xTaskGetTickCount( void );

1.13、在中断中获取系统时钟节拍计数器的值

  xTaskGetTickCountFromISR() 函数用于 在中断中获取系统时钟节拍计数器的值,此函数的函数原型如下所示:

// 返回值:系统时钟节拍计数器的值
TickType_t xTaskGetTickCountFromISR( void );

1.14、获取任务调度器的运行状态

  xTaskGetSchedulerState() 函数用于 获取任务调度器的运行状态,此函数的函数原型如下所示:

// 返回值:任务调度器的运行状态
BaseType_t xTaskGetSchedulerState( void );

1.15、获取系统中任务的数量

  uxTaskGetNumberOfTasks() 函数用于 获取系统中任务的数量,此函数的函数原型如下所示:

// 返回值:系统中任务的数量
UBaseType_t uxTaskGetNumberOfTasks( void );

1.16、获取系统中任务的信息

  vTaskList() 函数用于以 “表格” 的形式 获取系统中任务的信息,若使用此函数,需在 FreeRTOSConfig.h 文件中同时设置配置项 configUSE_TRACE_FACILITY 和配置项 configUSE_STATS_FORMATTING_FUNCTIONS 为 1,此函数的函数原型如下所示:

// pcWriteBuffer为接收任务信息的缓存指针
#define vTaskList( pcWriteBuffer )    vTaskListTasks( ( pcWriteBuffer ), configSTATS_BUFFER_MAX_LENGTH )

1.17、获取指定任务的运行时间、运行状态

  vTaskGetRunTimeStats() 此函数用于 获取指定任务的运行时间、运行状态等信息,若使用此函数,需在 FreeRTOSConfig.h 文件中同时设置配置项 configGENERATE_RUN_TIME_STATSconfigUSE_STATS_FORMATTING_FUNCTIONSconfigSUPPORT_DYNAMIC_ALLOCATION为 1,此函数的函数原型如下所示:

// pcWriteBuffer为接收任务运行时间和状态等信息的缓存指针
#define vTaskGetRunTimeStats( pcWriteBuffer )    vTaskGetRunTimeStatistics( ( pcWriteBuffer ), configSTATS_BUFFER_MAX_LENGTH )

1.18、用于设置指定任务的Tag

  vTaskSetApplicationTaskTag() 函数用于 设置指定任务的 Tag,若使用此函数,需在 FreeRTOSConfig.h 文件中设置配置项 configUSE_APPLICATION_TASK_TAG 为 1,此函数的函数原型如下所示:

void vTaskSetApplicationTaskTag( TaskHandle_t xTask,                            // 待插入 Tag 的任务
                                    TaskHookFunction_t pxHookFunction );        // Tag 指针

1.19、设置指定任务的独有数据数组指针

  vTaskSetThreadLocalStoragePointer() 函数用于 设置指定任务的独有数据数组指针,此函数的函数原型如下所示:

void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,        // 待设置的任务
                                        BaseType_t xIndex,              // 设置的指针
                                        void * pvValue );               // 值

1.20、获取指定任务的独有数据数组指针

  vTaskSetThreadLocalStoragePointer() 函数用于 获取指定任务的独有数据数组指针,此函数的函数原型如下所示:

void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,        // 待获取的任务
                                        BaseType_t xIndex,              // 接收的指针
                                        void * pvValue );               // 保存指针指向的值

二、实验例程

2.1、任务状态查询

  main() 函数:

int main(void)
{
    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);

    UART_Init(&g_usart1_handle, USART1, 115200);

    freertos_demo();

    return 0;
}

  FreeRTOS 的入口函数:

/**
 * @brief FreeRTOS的入口函数
 * 
 */
void freertos_demo(void)
{
    xTaskCreate((TaskFunction_t        ) start_task,                            // 任务函数
                (char *                ) "start_task",                          // 任务名
                (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,                 // 任务栈大小
                (void *                ) NULL,                                  // 入口参数
                (UBaseType_t           ) START_TASK_PRIORITY,                   // 任务优先级
                (TaskHandle_t *        ) start_task_handle);                    // 任务句柄

    vTaskStartScheduler();                                                      // 开启任务调度器
}

  start_task 任务配置:

/**
 * START_TASK 任务配置
 * 包括: 任务优先级 任务栈大小 任务句柄 任务函数
 */
#define START_TASK_PRIORITY     1
#define START_TASK_STACK_SIZE   128

TaskHandle_t start_task_handle;

void start_task(void *pvParameters );

/**
 * @brief 开始任务的任务函数
 * 
 * @param pvParameters 任务函数的入口参数
 */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();                                                       // 进入临界区,关闭中断

    xTaskCreate((TaskFunction_t        ) task1,                                 // 任务函数
                (char *                ) "task1",                               // 任务名
                (configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,                      // 任务栈大小
                (void *                ) NULL,                                  // 入口参数
                (UBaseType_t           ) TASK1_PRIORITY,                        // 任务优先级
                (TaskHandle_t *        ) &task1_handle);                        // 任务句柄

    vTaskDelete(NULL);                                                          // 删除任务自身

    taskEXIT_CRITICAL();                                                        // 退出临界区,重新开启中断
}

  task1 任务配置:

/**
 * TASK1 任务配置
 * 包括: 任务优先级 任务栈大小 任务句柄 任务函数
 */
#define TASK1_PRIORITY     2
#define TASK1_STACK_SIZE   128

TaskHandle_t task1_handle;

void task1(void *pvParameters);

/**
 * @brief 任务1的任务函数
 * 
 * @param pvParameters 任务函数的入口参数
 */
void task1(void *pvParameters)
{
    TaskStatus_t *status_array = 0;
    TaskStatus_t status;

    UBaseType_t priority = uxTaskPriorityGet(task1_handle);                     // 获取指定任务的优先级
    printf("task1 优先级:%ld\r\n\r\n", priority);

    vTaskPrioritySet(NULL, 3);                                                  // 设置自身任务的优先级

    priority = uxTaskPriorityGet(NULL);                                         // 获取自身任务的优先级
    printf("task1 优先级:%ld\r\n\r\n", priority);

    UBaseType_t count = uxTaskGetNumberOfTasks();                               // 获取任务数量
    printf("任务数量:%ld\r\n\r\n", count);

    status_array = pvPortMalloc(count * sizeof(TaskStatus_t));                  // 动态申请内存
    count = uxTaskGetSystemState(status_array, count, NULL);                    // 获取所有任务的状态信息

    printf("任务状态信息:\r\n");
    for (uint32_t i = 0; i < count; i++)
    {
        printf("任务名:%s, 任务优先级: %ld, 任务编号: %ld\r\n", status_array[i].pcTaskName, status_array[i].uxCurrentPriority, status_array[i].xTaskNumber);
    }

    printf("\r\n获取指定任务的状态信息: \r\n");
    vTaskGetInfo(task1_handle, &status, pdTRUE, eInvalid);                      // 获取指定任务的状态信息
    printf("任务名:%s, 任务优先级: %ld, 任务编号: %ld, 任务状态: %d\r\n\r\n", status.pcTaskName, status.uxCurrentPriority, status.xTaskNumber, status.eCurrentState);

    UBaseType_t task_min_stack = uxTaskGetStackHighWaterMark(task1_handle);     // 获取指定任务的历史剩余堆栈最小值
    printf("task1 历史剩余堆栈最小值:%ld\r\n\r\n", task_min_stack);

    while (1)
    {
  
    }
}

2.2、运行时间统计

  我们使用 vTaskGetRunTimeStats() 函数统计任务的运行时间信息,使用此函数需将宏 configGENERATE_RUN_TIME_STATconfigUSE_STATS_FORMATTING_FUNCTIONS 置 1。当将此宏 configGENERATE_RUN_TIME_STAT 置 1 之后,还需要实现 2 个宏定义:

// 用于初始化用于配置任务运行时间统计的时基定时器,这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍!
portCONFIGURE_TIMER_FOR_RUNTIME_STATE();  

// 用于获取该功能时基硬件定时器计数的计数值 
portGET_RUN_TIME_COUNTER_VALUE();

  FreeRTOSConfig.h 文件中有关函数运行时间的宏:

/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS                   1                       // 1: 使能任务运行时间统计功能,默认: 0 
#if configGENERATE_RUN_TIME_STATS
extern void ConfigureTimeForRunTimeStats(void);
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks
#endif /* configGENERATE_RUN_TIME_STATS */
#define configUSE_TRACE_FACILITY                        1                       // 1: 使能可视化跟踪调试,默认: 0 
#define configUSE_STATS_FORMATTING_FUNCTIONS            1                       // 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数,默认: 0 

  时基定时器初始化函数:

uint32_t FreeRTOSRunTimeTicks = 0;                                              // FreeRTOS运行时间

/**
 * @brief 时基定时器初始化函数
 * 
 */
void ConfigureTimeForRunTimeStats(void)
{
    TIM_Base_Init(&g_tim6_handle, TIM6, 83, 9);                                 // 定时10us
    FreeRTOSRunTimeTicks = 0;                                                   // 初始化FreeRTOS运行时间
    HAL_TIM_Base_Start_IT(&g_tim6_handle);                                      // 启动定时器
}

  修改定时器更新中断回调函数:

/**
 * @brief 定时器更新中断回调函数
 * 
 * @param htim 定时器句柄
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM6)
    {
        FreeRTOSRunTimeTicks++;
    }
}

  修改 main() 函数:

int main(void)
{
    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);

    UART_Init(&g_usart1_handle, USART1, 115200);
    Key_Init();

    freertos_demo();
  
    return 0;
}

  修改 task1 函数:

/**
 * @brief 任务1的任务函数
 * 
 * @param pvParameters 任务函数的入口参数
 */
void task1(void *pvParameters)
{
    while (1)
    {
        switch (Key_Scan(0))
        {
        case KEY1_PRESS:
            vTaskGetRunTimeStats(buffer);
            printf("%s\r\n", buffer);
            break;
    
        default:
            break;
        }
        vTaskDelay(10);
    }
}
posted @   星光映梦  阅读(170)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2023-03-09 13. 结构体
点击右上角即可分享
微信分享提示