本人在 stm32 应用程序中, 想通过软件检测出 CPU 的占用率. 就象 Windows 系统里的任务管理器能够告知 CPU 的使用率一样. 当然在 UCOS 里也具有这个功能.
但是, 我现在是在裸奔系统里. 这个系统有多个不同优先级且发生频率很高的中断服务, 大部分的程序功能都是在中断服务里完成的. 现要求尽量不使用复杂的计算和软件变换. 也不希望因为要检测 CPU 的占用率而在代码中增加 "cli" 这样的中断禁止功能. 并尽可能少用硬件(比如定时器)资源.
总结起来一句话, 要尽可能简单地完成检测 CPU 占用率的功能, 并要求计算结果能够相当地准确反映实践情况(当然也并非要求到几个周期都不能误差)
顺便提一下, 以前在 luminary, 倒是有个功能, 设定某个定时器在 CPU 关闭时钟的 Idle 模式, 也同时关闭时钟, 这样通过这个定时器的计数值, 就可以算出 CPU 占用率. 但 STM32 没有提供这个功能.
下面我做了个程序模块, 使用的是 Systick 定时器里的计数器值来检测.
思路是: 在 main() 中执行 Idle (其实是__wfi 函数) 之前. 先调用 Idle_SaveCount() 记录下当前的 Systick 计数值. 并保存在全局变量 g_ulCPUUsageIdle 中. 然后就进入 Idle 模式.
当发生中断唤醒时, CPU 首先会执行该中断服务程序, 于是我在所有中断服务开始处首先调用 CPUUsageINTsvr() 函数.
本函数读取 Systick 计数值, 取 g_ulCPUUsageIdle 变量值并清零(清零的目的是避免迟来的中断重复计算).
另外, 有个特别的中断服务 --Systick 中断. 当这个中断发生时, Systick 当前计数值已经是最大值了, 所以不能调用 CPUUsageINTsvr(), 好在 Systick 中断发生时计数值肯定为零. 于是专门写了个 Systick 中断里记录用的函数 CPUUsageSystick().
最后, 在 main() 里或者 Systick 里, 定期(比如1S) 调用 CPUIdleClocks() 取得 IDLE 所用的时钟数, 并清除累计值. 用 (1-累计值/时钟频率) 来计算 CPU 占用率.
#define GetSTCurrent() SysTick->VAL
u32 g_ulCPUUsageIdle; // 记录进入 Idle 的时刻, 0 代表非唤醒中断
u32 g_ulCPUIdleCount; // 累计到的 Idle 的时间
__inline void Idle_SaveCount(void) {
g_ulCPUUsageIdle = GetSTCurrent(); // 保存进入 Idle 的时刻
}
// 判断是否为中断唤醒, 保存唤醒时刻的 SYSTICK 当前计数值.
void CPUUsageINTsvr(void) {
unsigned int stcount = GetSTCurrent(), idly = g_ulCPUUsageIdle;
g_ulCPUUsageIdle = 0; // 置0, 禁止迟来中断进行重复计算
if (idly > stcount) { // 保证有效的休眠时间
g_ulCPUIdleCount += idly - stcount; // 累计 IDLE 的时间
}
}
__inline void CPUUsageSystick(void) {
unsigned int inc = g_ulCPUUsageIdle;
g_ulCPUUsageIdle = 0;
g_ulCPUIdleCount += inc;
}
unsigned long CPUIdleClocks(void) {
unsigned long stclks;
stclks = g_ulCPUIdleCount;
g_ulCPUIdleCount = 0;
return (stclks);
}
从原理上讲, 忽略的中断响应时间, 应该不会导致太大的误差. 但调试后发现的结果是. 每次 CPUIdleClocks 返回的时钟数居然超过时钟频率, 而且超过了将近一半! 问题在哪里, 是否有更好的办法来计算呢? |