FreeRTOS 之任务 CPU 使用率统计

示例工程代码库地址如下:

1. 先看结果

博主对 vTaskList 和 vTaskGetRunTimeStats 函数进行了封装,
名为 OS_SysInfo,其实现如下:

/**
 * @brief printf system info, include task list and run time
 */
void OS_SysInfo(void)
{
    char *pBuf = OS_MemAlloc(uxTaskGetNumberOfTasks() * (60 + configMAX_TASK_NAME_LEN));
    OLOGF("taskname     state     priority    freestack   number\n");
    if(pBuf){
        vTaskList(pBuf);
        OLOGF("%s", pBuf);

        OLOGF("\ntaskname     count     %%cpu\n");
        vTaskGetRunTimeStats(pBuf);
        OLOGF("%s", pBuf);
        OS_MemFree(pBuf);
    }else{
        OLOGF("Heap space is not enough\n");
    }
}

调用 OS_SysInfo 打印如下:
FreeRTOS 之任务 CPU 使用率统计

2. 配置过程

官网配置流程说明,请点击跳转
根据官网对 vTaskGetRunTimeStats 描述,点击跳转

configGENERATE_RUN_TIME_STATS, configUSE_STATS_FORMATTING_FUNCTIONS and configSUPPORT_DYNAMIC_ALLOCATION must all be defined as 1 for this function to be available. The application must also then provide definitions for portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE to configure a peripheral timer/counter and return the timer’s current count value respectively. The counter should be at least 10 times the frequency of the tick count.
NOTE: This function will disable interrupts for its duration. It is not intended for normal application runtime use but as a debug aid.

需要注意:

  • 使用率统计用到的时钟至少是系统时钟的 10 倍
  • 该函数执行期间会关闭中断,仅适合调试阶段使用,否则会影响系统运行效率

FreeRTOSConfig.h

extern volatile uint32_t g_timeticks;

#define configTICK_RATE_HZ				( ( TickType_t ) 1000 )

/* Run time and task stats gathering related definitions. */
#define configUSE_TRACE_FACILITY					1
#define configGENERATE_RUN_TIME_STATS				1
#define configUSE_STATS_FORMATTING_FUNCTIONS		1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 	(g_timeticks = 0ul)
#define portGET_RUN_TIME_COUNTER_VALUE()            g_timeticks

delay.h

/*
 * @Descripttion: delay function
 * @Author: Jerry
 * @Date: 2021-12-13 17:05:30
 * @LastEditTime: 2022-01-14 16:18:29
 * 
 * Copyright © 2021 Jerry, All Rights Reserved
 */
#ifndef _DELAY_H_
#define _DELAY_H_

#include "stdint.h"

void delay_init(void);

/**
 * @brief delay a time in microsecond
 * @param us [in] us
 */
void delay_us(uint32_t us);

/**
 * @brief delay a time in milliseconds
 * @param ms [in] ms
 */
void delay_ms(uint32_t ms);

#endif

delay.c

/*
 * @Descripttion: delay function
 * @Author: Jerry
 * @Date: 2021-12-13 17:05:25
 * @LastEditTime: 2022-01-19 10:45:08
 * 
 * Copyright © 2021 Jerry, All Rights Reserved
 */
#include "delay.h"
#include "gd32f30x_timer.h"

#define DELAY_TIMER_RCU_PERIPH  RCU_TIMER6
#define DELAY_TIMER_PERIPH      TIMER6
#define DELAY_TIMER_NVIC_IRQ    TIMER6_IRQn
#define DELAY_TIMER_IRQ_HANDLER TIMER6_IRQHandler

//int_time = CLK / ((prescaler + 1) * period)
//int_time = 1us
#define DELAY_TIMER_PRESCALER   1
#define DELAY_TIMER_PERIOD      60

volatile uint32_t g_timeticks = 0;

/*******************************************************************/
/***       			    Local Function                           ***/
/*******************************************************************/
void delay_timer_init(void)
{
    timer_parameter_struct timer_parameter;

    rcu_periph_clock_enable(DELAY_TIMER_RCU_PERIPH);
    //预分频
    timer_parameter.prescaler = DELAY_TIMER_PRESCALER;
    //对齐模式
    timer_parameter.alignedmode = TIMER_COUNTER_EDGE;
    //定时器增长方向
    timer_parameter.counterdirection = TIMER_COUNTER_UP;
    //定时器自动加载值
    timer_parameter.period = DELAY_TIMER_PERIOD;
    //时钟分频值
    timer_parameter.clockdivision = TIMER_CKDIV_DIV1;
    
    timer_init(DELAY_TIMER_PERIPH, &timer_parameter);
    timer_interrupt_enable(DELAY_TIMER_PERIPH, TIMER_INT_UP);
    nvic_irq_enable(DELAY_TIMER_NVIC_IRQ, 0, 0);
    timer_enable(DELAY_TIMER_PERIPH);
}

void DELAY_TIMER_IRQ_HANDLER(void)
{
    timer_interrupt_flag_clear(DELAY_TIMER_PERIPH, TIMER_INT_UP);

    g_timeticks++;
}

/******************************************************************/
/***                    Exported Functions                      ***/
/******************************************************************/
void delay_init(void)
{
    delay_timer_init();
}

/**
 * @brief delay a time in microsecond
 * @param us [in] us
 */
void delay_us(uint32_t us)
{
    uint32_t start_count = g_timeticks;
    while((g_timeticks - start_count) < us);
}

/**
 * @brief delay a time in milliseconds
 * @param ms [in] ms
 */
void delay_ms(uint32_t ms)
{
    delay_us(ms * 1000);
}

3. 统计异常

程序持续运行一段时间后,CPU 使用率统计异常了,先看结果:
FreeRTOS 之任务 CPU 使用率统计
在 tasks.c 中的 vTaskSwitchContext 的函数中:

void vTaskSwitchContext( void )
{
    if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    {
        /* The scheduler is currently suspended - do not allow a context
         * switch. */
        xYieldPending = pdTRUE;
    }
    else
    {
        xYieldPending = pdFALSE;
        traceTASK_SWITCHED_OUT();

        #if ( configGENERATE_RUN_TIME_STATS == 1 )
            {
                #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
                    portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
                #else
                    ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
                #endif
				
				//下面注释谈到统计使用率相关的变量没有溢出处理,计数值仅在溢出前有效
                /* Add the amount of time the task has been running to the
                 * accumulated time so far.  The time the task started running was
                 * stored in ulTaskSwitchedInTime.  Note that there is no overflow
                 * protection here so count values are only valid until the timer
                 * overflows.  The guard against negative values is to protect
                 * against suspect run time stat counter implementations - which
                 * are provided by the application, not the kernel. */
                if( ulTotalRunTime > ulTaskSwitchedInTime )
                {
                    pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                ulTaskSwitchedInTime = ulTotalRunTime;
            }
        #endif /* configGENERATE_RUN_TIME_STATS */

        /* Check for stack overflow, if configured. */
        taskCHECK_FOR_STACK_OVERFLOW();

        /* Before the currently running task is switched out, save its errno. */
        #if ( configUSE_POSIX_ERRNO == 1 )
            {
                pxCurrentTCB->iTaskErrno = FreeRTOS_errno;
            }
        #endif

        /* Select a new task to run using either the generic C or port
         * optimised asm code. */
        taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
        traceTASK_SWITCHED_IN();

        /* After the new task is switched in, update the global errno. */
        #if ( configUSE_POSIX_ERRNO == 1 )
            {
                FreeRTOS_errno = pxCurrentTCB->iTaskErrno;
            }
        #endif

        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            {
                /* Switch Newlib's _impure_ptr variable to point to the _reent
                 * structure specific to this task.
                 * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
                 * for additional information. */
                _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
            }
        #endif /* configUSE_NEWLIB_REENTRANT */
    }
}

注释中谈到统计使用率相关的变量没有溢出处理,计数值仅在溢出前有效

所以统计使用率只能在计数值溢出前是准确的。

上一篇:【蓝桥杯第五届省赛题-简易温度采集与控制装置】


下一篇:JS的节流、函数防抖 原理及使用场景