示例工程代码库地址如下:
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 打印如下:
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 使用率统计异常了,先看结果:
在 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 */
}
}
注释中谈到统计使用率相关的变量没有溢出处理,计数值仅在溢出前有效
所以统计使用率只能在计数值溢出前是准确的。