测量中断程序的 CPU 负载

电器工程 测量 中断 占空比
2022-01-30 22:02:09

我有一个以固定频率更新显示的 isr。我想调整我的例程以最小化开销并保持尽可能多的 cpu 时间以供其他处理使用,但我没有任何好的方法来收集指标以确定我的 cpu 负载。

我可以查看程序集并分析例程,但我没有耐心或能力准确地做到这一点。我也不觉得我需要非常细粒度的结果,只需要 isr 占用的 cpu 时间的简单百分比。

只有当 isr 处于活动状态并在外部测量时,我才能将引脚设置为高电平。这在代码中的开销最小,但我不知道用什么来衡量它。我没有示波器或类似的东西。有没有简单的 ic 或简单的方法来使用另一个微测量占空比?我听说过专用频率计数器芯片,但是有什么占空比的吗?

4个回答

进入 ISR 时设置一个输出引脚,并在从它返回之前清除它。使用 RC 滤波器过滤输出。电容器两端的电压应为您提供 ISR 占空比。
例如,如果您的电源为 3.3V,而您测量的电压为 33mV,那么您将 1% 的时间用于 ISR。

只是一个半生不熟的想法,您也许可以像这样使用计时器(伪代码):

int main(void)
{

    /* ... init timers and uart here, enable your interrupts ... */

    start_timer0();
    while (!timer1Started()){}
    stop_timer1();

    uart_puts("Idle ticks: %d, ISR ticks: %d", timer0_value, timer1_value);

}

并在您的显示 ISR 中...

ISR_display()
{
    stop_timer0();
    start_timer1();

    /* ... your ISR routine ... */
}

我在这里做了一些假设。1 - 您没有将计时器用于其他任何事情,并且 2 - 启动和停止计时器的开销很小(通常通过单个寄存器写入完成)。编辑:第三个假设,您可以在发生计时器溢出之前捕获所有这些,但也许您也可以考虑这一点。

会有一些你无法捕捉到的上下文切换开销,这也会在你的 ISR 中添加两个额外的操作(确保为你的 start_timer/stop_timer 使用宏来消除函数调用开销)。如果您可以获得用于启动+停止计时器宏的总周期数,那么您可以从 timer1_value 中减去这些滴答声,以更准确地获得 ISR 滴答声值。您对所用 CPU 时间百分比的最终计算将是:

Usagecpu=(TicksisrTicksisr+Ticksidle)100

最简单的方法是在模拟器中运行代码并测量中断例程所占用的周期。例如,Microchip MPLAB 模拟器有一个方便的秒表功能,对此非常有用。

如果做不到这一点,在中断开始时升高一个引脚并在结束时降低它会有所帮助。最简单的查看方法是使用示波器。如果你正在做微控制器和电子项目,你应该得到一个。

如果没有示波器,您可以简单地对引脚电压进行低通滤波,然后用电压表测量它。该电压除以处理器电源电压将为您提供引脚高电平时间的一小部分。例如,如果处理器在 3.3V 上运行,并且低通滤波引脚电压为 800mV,则引脚为高电平的时间为 800mV / 3.3V = 24%。

由于您显然正在使用编译器,因此您需要稍微降低这个答案。编译器可能会在您的代码运行之前添加一些中断入口代码,并在您的代码运行之后添加中断退出代码。真正的中断时间将在脉冲的任一侧延长几个周期。当然,如果您关心中断时序,则首先不应该使用编译器。

我找到了一种有趣的方法来测量 Arduino 上的中断负载。似乎 ISR 会减慢delayMicroseconds()功能并使其不准确,这可用于检测 ISR 中使用了多少个周期。完整代码在这里(当 ISR 被禁用时,延迟微秒似乎被限制为 1000 的错误):https ://github.com/Palatis/arduino-softpwm/blob/9567314ef35e31d8483e6d0c5dda6f33e67d4be0/src/SoftPWM.h#L214

unsigned long time1, time2;

interrupts(); // enable interrupt
time1 = micros();
delayMicroseconds(1000);
time1 = micros() - time1;

noInterrupts(); // disable interrupt
time2 = micros();
delayMicroseconds(1000);  // MAX while interrupts are disabled is 1000
time2 = micros() - time2;
interrupts(); // enable interrupt

float cpuFrequency = F_CPU;
float load = float(time1 - time2) / time1;
float cycles_per_interrupt = load * cpuFrequency / interrupt_frequency;

对于其他 MCU,需要分析该 delayMicroseconds()功能在内部是如何工作的。在此示例中,还应该可以将 time2 硬编码为 1000。