FreeRTOS处理器利用率
处理器利用率
1. 处理器利用率统计的作用
处理器利用率其实就是系统运行的程序占用的 CPU 资源,表示机器在某段时间程序运行的情况,如果这段时间中,程序一直在占用 CPU 的使用权,那么可以认为 CPU 的利用率是 100%;CPU 的利用率越高,说明机器在这个时间上运行了很多程序,反之较少。利用率的高低与 CPU 强弱有直接关系。比如同一段程序,如果在运算速度很慢的 CPU 上运行,它可能要 1000ms,而在运算速度很快的 CPU 上运行可能只需要 10ms,那么在 1000ms 这段时间中,前者的处理器利用率就是 100%,而后者只有 1%,因为 1000ms 内前者都在使用 CPU 做运算,而后者只使用 10ms 的时间做运算,剩下的时间 CPU 可以做其他事情。
调试的时候很有必要得到当前系统的 CPU 利用率相关信息,但是在产品发布时,就可以去掉 CPU 利用率统计功能,以避免消耗系统资源。FreeRTOS 是使用一个外部的变量进行时间统计,并且消耗一个高精度的定时器,其用于定时的精度是系统时钟节拍的 10-20 倍。比如当前系统时钟节拍是 1000Hz,那么定时器的计数节拍就要是 10000-20000Hz。
但是 FreeRTOS 进行 CPU 利用率统计时,也有一定缺陷,即没有对进行 CPU 利用率统计时间的变量做溢出保护,一般使用的是 32 位变量来记录系统运行时间计数值,若按 20000Hz 的中断频率计算,每 50us 进入一次中断,变量加一,则最大支持计数时间为:232*50us / 3600s = 59.6 分钟,即运行时间超过了 59.6 分钟后变量将溢出,统计结果将不准确,此外系统一直响应定时器 50us 一次的中断会比较影响系统的性能
2. 处理器利用率统计 API 函数
FreeRTOS 可以通过相关的配置来统计任务的运行时间信息,任务的运行时间信息提供了每个任务获取到 CPU 使用权总的时间。函数 vTaskGetRunTimeStats()
会将统计到的信息填充到一个表里,表里面提供了每个任务的运行时间和其所占总时间的百分比,如下图示:
1
2
3
4
5
6
7
8
/*****************************相关宏的配置*****************************/
#define configGENERATE_RUN_TIME_STATS 必须置为1
#define configUSE_STATS_FORMATTING_FUNCTIONS 必须置为1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS 初始化高精度定时器
#define portGET_RUN_TIME_COUNTER_VALUE 获取高精度定时器计数值
/***************************任务运行时间函数***************************/
函数原型:void vTaskGetRunTimeStats(char *pcWriteBuffer)
传 入 值:pcWriteBuffer 保存任务时间信息的存储区
3. 处理器利用率统计实例
使用 STM32CubeMX 配置 FreeRTOS,打开相关配置,创建如下三个任务:
Led_Task:D2 指示灯闪烁
Usart_Task:每隔 1s 向串口输出字符串
Key_Task:按下 K_UP,打印任务的运行时间信息
3.1 STM32CubeMX 设置
RCC 设置外接 HSE,时钟设置为 72M
PC1 设置为 GPIO 推挽输出模式、上拉、高速、默认输出电平为高电平
PA0 设置为 GPIO 输入模式、下拉模式;PE2/PE3/PE4 设置为 GPIO 输入模式、上拉模式
USART1 选择为异步通讯方式,波特率设置为 115200Bits/s,传输数据长度为 8Bit,无奇偶校验,1 位停止位
激活 FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数
配置宏
configGENERATE_RUN_TIME_STATS
和configUSE_STATS_FORMATTING_FUNCTIONS
为 1激活 TIM6 作为时间统计功能的高精度时钟,分频值为 72-1,自动重装载值为 50-1,所以定时器 6 周期是 50us;打开 TIM6 中断
使用 FreeRTOS 操作系统,一定要将 HAL 库的 Timebase Source 从 SysTick 改为其他定时器,选好定时器后,系统会自动配置 TIM
输入工程名,选择路径(不要有中文),选择 MDK-ARM V5;勾选 Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击 GENERATE CODE,生成工程代码
3.2 MDK-ARM 软件编程
- 创建按键驱动文件 key.c 和 key.h,参考按键输入例程
- 初始化高精度计时器,并获取高精度定时器计数值
1
2
3
4
5
6
7
8
9
//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS configureTimerForRunTimeStats
__weak void configureTimerForRunTimeStats(void){
HAL_TIM_Base_Start_IT(&htim6); //开启TIM6中断并启动定时器
u32TimeCount = 0ul; //定时器统计值初始化为0
}
//#define portGET_RUN_TIME_COUNTER_VALUE getRunTimeCounterValue
__weak unsigned long getRunTimeCounterValue(void){
return u32TimeCount;
}
- 在 main.c 文件中添加 TIM6 定时器中断回调函数,中断一次定时器计数器加一
1
2
3
4
5
6
7
8
9
10
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim->Instance == TIM1){
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if(htim->Instance == TIM6){
u32TimeCount++;
}
/* USER CODE END Callback 1 */
}
- 添加 Led_Task、Usart_Task 和 Key_Task 任务函数代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void Led_Task(void const * argument){
for(;;){
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);
osDelay(500); //1ms时基
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);
osDelay(500); //1ms时基
}
}
/*******************************************************/
void Usart_Task(void const * argument){
for(;;){
printf("UsartTask is Runing!\r\n");
osDelay(1000);
}
}
/*******************************************************/
uint8_t u8TaskListBuff[400];
void KeyTask(void const * argument){
uint8_t key = 0;
for(;;){
key = KEY_Scan(0);
switch(key){
case KEY_UP_PRES:
memset(u8TaskListBuff, 0, 400);
vTaskGetRunTimeStats((char*)u8TaskListBuff);
printf("Name Abs Time Time\r\n");
printf("******************************************************\r\n");
printf("%s",u8TaskListBuff);
printf("******************************************************\r\n");
key = 0;
break;
case KEY_DOWN_PRES:
//....
key = 0;
break;
}
osDelay(10);
}
}
3.3 下载验证
编译无误下载到开发板后,D2 指示灯闪烁表示程序正常运行。打开串口调试助手,可以看到串口每隔 1s 输出相应字符;按下 K_UP 按键,串口打印出每个任务的运行时间信息
以上转载自博主“安迪西嵌入式”的FreeRTOS专栏,仅作学习记录,如有侵权,请联系删除。