一、CPU利用率简介
1 基本概念
CPU 使用率其实就是系统运行的程序占用的 CPU 资源,表示机器在某段时间程序运行的情况,如果这段时间中,程序一直在占用 CPU 的使用权,那么可以人为 CPU 的利用率是 100%。CPU 的利用率越高,说明机器在这个时间上运行了很多程序,反之较少。利用率的高低与 CPU 强弱有直接关系,就像一段一模一样的程序,如果使用运算速度很慢的 CPU,它可能要运行 1000ms,而使用很运算速度很快的 CPU 可能只需要 10ms,那么在 1000ms 这段时间中,前者的 CPU 利用率就是 100%,而后者的 CPU 利用率只有 1%,因为 1000ms 内前者都在使用 CPU 做运算,而后者只使用 10ms 的时间做运算,剩下的时间 CPU 可以做其他事情。
FreeRTOS 是多任务操作系统,对 CPU 都是分时使用的:比如 A 任务占用 10ms,然后 B 任务占用 30ms,然后空闲 60ms,再又是 A 任务占 10ms,B 任务占 30ms,空闲 60ms;
2 FreeRTOS进行CPU利用率统计
在调试的时候很有必要得到当前系统的 CPU 利用率相关信息,但是在产品发布的时候,就可以把 CPU 利用率统计这个功能去掉,因为使用任何功能的时候,都是需要消耗系统资源的,FreeRTOS 是使用一个外部的变量进行统计时间的,并且消耗一个高精度的定时器,其用于定时的精度是系统时钟节拍的 10-20 倍,比如当前系统时钟节拍是 1000HZ,那么定时器的计数节拍就要是 10000-20000HZ。而且 FreeRTOS 进行 CPU 利用率统计的时候,也有一定缺陷,因为它没有对进行 CPU 利用率统计时间的变量做溢出保护,我们使用的是 32 位变量来系统运行的时间计数值,而按 20000HZ 的中断频率计算,每进入一中断就是 50us,变量加一,最大支持计数时间:2^32 * 50us / 3600s = 59.6 分钟,运行时间超过了 59.6 分钟后统计的结果将不准确,除此之外整个系统一直响应定时器 50us 一次的中断会比较影响系统的性能。
二、STM32CubeMX设置
1、配置RCC、USART1、时钟72M
2、配置SYS,将Timebase Source修改为除滴答定时器外的其他定时器。
3、初始化LED的两个引脚
4、开启FreeRTOS,v1与v2版本不同,一般选用v1即可
5、创建三个线程LED1,LED2,task3。
以上步骤可参考:STM32CubeMX学习笔记22---FreeRTOS(任务创建和删除)-CSDN博客
6、打开相关配置
要想获得CPU使用率必须在 Config parameters
中把 GENERATE_RUN_TIME_STATS
、USE_TRACE_FACILITY
、USE_STATS_FORMATTING_FUNCTIONS
选择 Enabled
来使能。
7、创建一个定时器,分频72,定时50us ,优先级为6
- Prescaler(时钟预分频数):72-1 则驱动计数器的时钟 CK_CNT = CK_INT(即72MHz)/(71+1) = 1MHz
- Counter Mode(计数模式):Up(向上计数模式) 基本定时器只能是向上计数
- Counter Period(自动重装载值):50-1 则定时时间 1/CK_CLK*(49+1) = 50us
- auto-reload-preload(自动重装载):Enable(使能)
- TRGO Parameters(触发输出):不使能 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)
配置nvic
8、生成代码
三、程序编程
要计算cup的利用率还需要实现一个中断频率为 20000HZ 定时器,用于系统运行时间统计,可以通过自定义 g_osRuntimeCounter 变量自加即可,这个变量是用于记录系统运行时间。其自加程序可以在定时器回调函数(main.c)中实现:
uint32_t g_osRuntimeCounter;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if(htim->Instance == TIM6)
{
g_osRuntimeCounter++; //添加g_osRuntimeCounter自加
}
/* USER CODE END Callback 1 */
}
/*
注:使用前需在main函数中先开启定时器中断
HAL_TIM_Base_Start_IT(&htim6); // 开启定时器中断
*/
打开configGENERATE_RUN_TIME_STATS时所需的函数,freertos.c文件中带有configGENERATE_RUN_TIME_STATS的函数,需要将我们定义的变量放置在其中。
extern uint32_t g_osRuntimeCounter;
__weak void configureTimerForRunTimeStats(void)
{
g_osRuntimeCounter = 0;
}
__weak unsigned long getRunTimeCounterValue(void)
{
//return 0;
return g_osRuntimeCounter;
}
编写LED1正常运行的程序
void LED1_Task1(void const * argument)
{
/* USER CODE BEGIN LED1_Task1 */
/* Infinite loop */
int i=0;
for(;;)
{
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5); //LED1状态每500s翻转一次
printf("led1 run ..%d\n",i++);
osDelay(500);
}
/* USER CODE END LED1_Task1 */
}
编写 LED2正常运行程序
void LED2_Task03(void const * argument)
{
/* USER CODE BEGIN LED2_Task03 */
/* Infinite loop */
int i=0;
for(;;)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5); //LED1状态每500s翻转一次
printf("led run..%d\n",i++);
osDelay(1000);
}
/* USER CODE END LED2_Task03 */
}
编写任务3程序,通过 osThreadList()
和 vTaskGetRunTimeStats()
获取 CPU 利用率与任务相关信息并通过串口打印出来
void StartTask3(void const * argument)
{
/* USER CODE BEGIN StartTask3 */
/* Infinite loop */
uint8_t CPU_RunInfo[250]="123"; //保存任务运行时间信息
for(;;)
{
memset(CPU_RunInfo,0,strlen((const char *)CPU_RunInfo)); //信息缓冲区清零
osThreadList(CPU_RunInfo); //获取任务运行时间信息
printf("---------------------------------------------\r\n");
printf("Task Task_Status Priority Remaining_Stack Task_No\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
memset(CPU_RunInfo,0,strlen((const char *)CPU_RunInfo)); //信息缓冲区清零
vTaskGetRunTimeStats((char *)&CPU_RunInfo);
printf("Task Running_Count Utilization\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n\n");
osDelay(1000);
}
/* USER CODE END StartTask3 */
}
四、下载验证
程序编译无误后下载到板子上
五、参考文献
STM32CubeMX学习笔记(37)——FreeRTOS实时操作系统使用(CPU使用率统计)_单片机freertos操作系统cpu占用率测试-CSDN博客