基于裸机的cpu loading监控方案

基于裸机的cpu loading监控方案

工程代码:https://github.com/i-jaffer/cpu_monitor_no_thread

1. What's this​!​ ​​

cpu_monitor_no_thread 是一个针对裸机应用编写的 cpu loading 监控方案。使用 FreeRTOS、RT-Thread 这类操作系统时,往往 RTOS 内都自带了 cpu monitor 的组件,但是针对裸机应用场景,却缺乏此类组件,因此在进行逻辑开发时,对于 CPU loading 统计便成了问题,因此开发了 cpu_monitor_no_thread 此组件。

使用 cpu_monitor_no_thread 时,建议对应的裸机开发也需遵循一定的任务思想进行开发,这样统计 cpu loading 才具有一定的意义,如果将事情全部放在while(1) 循环内,不关心执行频率,cpu 能跑多少次就跑多少次,对于这类应用场景,相信你也无须考虑 cpu loading 的问题,因为你的 cpu 一直都在忙着干活,一直都是100%

2. Other

为避免使用此组件的开发者未能理解裸机任务开发思想,特作此补充说明,若已理解可直接跳过

任务开发思想:即将 cpu 需要做的事情拆分为多个任务,如 A、B、C,并根据需求确定相关任务的执行频率,如:

  • 任务A:10ms 执行一次
  • 任务B:20ms 执行一次
  • 任务C:30ms 执行一次

由系统定时器(Cortex M处理器通常使用 system tick)产生基础时间节拍 tick,如 1ms 产生一个 tick

下方展示一个简单的由 system tick 产生时间节拍的处理方式:

c 复制代码
uint32_t clock_array[5]={0};		/* 用于拆分任务处理 */
uint32_t system_tick = 0;

void SysTick_Handler(void)
{
	uint8_t i = 0;
	system_tick ++;
	for(i = 0; i < 5; i++)
	{
		if(clock_array[i] != 0)
			clock_array[i] --;
	}
}

有了 system tick 提供的时间片之后,之后在主循环内即可设计对应任务处理了,伪代码如下:

c 复制代码
int main(void)
{
    /* xxxx init */
    while (1) {
        if (clock_array[0] == 0) {
            clock_array[0] = 10;
            A();	/* 执行A任务*/
        }
        if (clock_array[1] == 0) {
            clock_array[1] = 20;
            B();	/* 执行A任务*/
        }
        if (clock_array[2] == 0) {
            clock_array[2] = 30;
            C();	/* 执行A任务*/
        }
    }
}

3. Usage

3.1 Steps

step1:

添加 cpu_monitor_no_thread 组件至对应工程

step2:

根据对应平台,适配系统时间戳接口,uint32_t cpu_monitor_get_systick(void),接口声明位于cpu_monitor_interface.h 文件内

step3:

系统时钟初始化之后,且其他外设/中断未启动前,调用 cpu_monitor_init(); ,务必注意在系统时钟初始化之后再调用,否则初始化将失败;

此函数运行固定时间用来统计全cpu计算的基(分母),因此要求在其他外设/中断未启用前调用的,这样更加精准

step4:

主循环内调用 cpu_monitor_run_hook() 统计cpu空闲时占比

step5:

系统时钟中断内调用 cpu_monitor_calculate() 定周期计算 cpu loading

可通过修改 #define CPU_MONITOR_PERIOD_TICK 100 ,调整 cpu loading 计算周期

step6:

可调用 cpu_monitor_get_loading() 获取当前 cpu 使用率

3.2 Example

系统滴答处理:

复制代码
uint32_t clock_array[5]={0};		/* 用于拆分任务处理 */
uint32_t system_tick = 0;

void SysTick_Handler(void)
{
	uint8_t i = 0;
	system_tick ++;
	for(i = 0; i < 5; i++)
	{
		if(clock_array[i] != 0)
			clock_array[i] --;
	}
	cpu_monitor_calculate();
}

主函数处理:

c 复制代码
#include "cpu_monitor.h"
#include "cpu_monitor_interface.h"

uint32_t cpu_monitor_get_systick(void)
{
    return get_systick();
}

int main(void)
{
	uint8_t major = 0, minor = 0;
	Systick_Init();
	cpu_monitor_init();
    
    /* xxxx init */
    while (1) {
        cpu_monitor_run_hook();
        
        if (clock_array[0] == 0) {
            clock_array[0] = 10;
            A();	/* 执行A任务*/
        }
        if (clock_array[1] == 0) {
            clock_array[1] = 20;
            B();	/* 执行A任务*/
        }
        if (clock_array[2] == 0) {
            clock_array[2] = 30;
            C();	/* 执行A任务*/
        }
        if (clock_array[3] == 0) {
            clock_array[3] = 1000;
            cpu_monitor_get_loading(&major, &minor);
            printf("cpu loading:%d.%02d\r\n", major, minor);
        }
    }
}

4. Record

实现 cpu monitor 没什么太大困难,但是有一个坑点需要特别注意:

4.1 注意init和hook实现count计数逻辑一致性

实现 cpu_monitor_init() 的时候,主要思想是让cpu不干别的,只做 count 计算,统计cpu全力计数时能计到的最大值,作为后续cpu loading计算时的分母。那么这里务必注意代码编写的处理逻辑一定要和 cpu_monitor_run_hook() 里面空闲cpu计数的处理方式一致!

比如, cpu_monitor_run_hook() 运用的是 if 处理加 自加 运算,对应 cpu_monitor_init()里面也应如此!

c 复制代码
void cpu_monitor_init(void)
{
    uint32_t tick = cpu_monitor_get_systick();
    while (cpu_monitor_get_systick() - tick < CPU_MONITOR_PERIOD_TICK) {
        cpu_monitor.data.total_loop++;
        if (cpu_monitor.data.total_loop >= CPU_MONITOR_MAX_LOOP) {
            cpu_monitor.data.total_loop = 0;
            cpu_monitor.data.total_count ++;
        }
    }
    cpu_monitor.status.init_flag = 1;
}

void cpu_monitor_run_hook(void)
{
    cpu_monitor.data.run_loop++;
    if (cpu_monitor.data.run_loop >= CPU_MONITOR_MAX_LOOP) {
        cpu_monitor.data.run_loop = 0;
        cpu_monitor.data.run_count ++;
    }
}

如果 cpu_monitor_init() 里面换另外一种方式,如 while

c 复制代码
void cpu_monitor_init(void)
{
    uint32_t tick = cpu_monitor_get_systick();
    while (cpu_monitor_get_systick() - tick < CPU_MONITOR_PERIOD_TICK) {
        cpu_monitor.data.total_count++;
        cpu_monitor.data.total_loop = 0;
        while (cpu_monitor.data.total_loop < CPU_MONITOR_MAX_LOOP)
           cpu_monitor.data.total_loop++;
    }
    cpu_monitor.status.init_flag = 1;
}

那么你会发现,代码里面不放什么一开始的 loading 就是40%了!这是因为两种写法不一致,导致 cpu 执行周期不一致,进而导致的统计误差!

4.2 注意while循环内任务思想开发

这个不多说,章节2有详细描述!

5. Conclusion

🆗,以上就是一种裸机的 cpu loading 监控组件分享了,有问题欢迎大家提 pr !

相关推荐
嵌入式小企鹅5 小时前
嵌入式面试宝典
学习·面试·嵌入式·嵌入式工程师·高薪offer
星瞳科技OpenMV5 小时前
国家级高新技术企业星瞳科技,定义嵌入式机器视觉行业新标杆
人工智能·嵌入式·图像识别·机器视觉·openmv·星瞳科技·星瞳科技openmv
Hello_Embed6 小时前
【无标题】
网络·笔记·网络协议·tcp/ip·嵌入式
senijusene6 小时前
基于 MX6UL 的 DHT11 温湿度传感器 驱动开发
驱动开发
XD7429716367 小时前
001. MSP430G2553 入门总述:从零开始学习这颗单片机
单片机·嵌入式硬件·学习·嵌入式·msp430g2553
CinzWS7 小时前
A53 FPGA原型验证:从RTL到可运行系统的挑战
arm开发·嵌入式·芯片验证·原型验证·a53
charlie1145141918 小时前
嵌入式Linux驱动开发(8)——内存映射 I/O - 别拿物理地址当指针用
linux·开发语言·驱动开发·c·imx6ull
Wallace Zhang8 小时前
SimpleFOC源码学习09(v2.3.2) - 磁编码器MagneticSensorSPI.cpp与MagneticSensorSPI.h
驱动开发·stm32·simplefoc·foc电机控制
Freak嵌入式8 小时前
亲测可用!可本地部署的 MicroPython 开源仿真器
ide·驱动开发·嵌入式·仿真·micropython·upypi
嵌入式小企鹅12 小时前
CPU供需趋紧、DeepSeek V4全链适配、小米开源万亿模型
人工智能·学习·开源·嵌入式·小米·算力·昇腾