为什么需要测量ISR耗时

在嵌入式实时系统中,中断服务函数(ISR)的执行时间是一个关键指标。过长的ISR会延迟低优先级中断响应、增加任务调度抖动,甚至导致数据丢失。但很多开发者仅凭“感觉”或GPIO翻转+示波器粗略评估,精度有限且需要额外引脚。

Cortex-M内核自带**DWT(Data Watchpoint and Trace)**单元,其中的CYCCNT寄存器是一个32位自由运行的周期计数器,可直接读取CPU时钟周期数。用它测量ISR耗时,无需任何外设,分辨率可达纳秒级(例如72MHz下约13.9ns),且代码侵入性极小。

DWT周期计数器原理

  • CYCCNT在DWT->CYCCNT寄存器中,每个内核时钟周期递增1。
  • 使能步骤:先使能DEMCR的TRCENA位,再置位DWT_CTRL的CYCCNTENA位。
  • 计数器为32位,在72MHz下约59.6秒溢出一次,对ISR测量完全够用。
  • 读取CYCCNT是单周期指令(MRS),对被测代码影响极小。

配置与测量代码

1. DWT初始化

#include "core_cm4.h"  // 根据内核型号调整

void DWT_Init(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 使能跟踪
    DWT->CYCCNT = 0;                                // 清零计数器
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;            // 使能周期计数
}

2. 测量宏与ISR示例

#define DWT_CYCCNT  (*(volatile uint32_t *)0xE0001004)

volatile uint32_t isr_cycles = 0;  // 记录ISR耗时(周期数)

void TIM2_IRQHandler(void)
{
    uint32_t start = DWT_CYCCNT;   // 进入ISR立即读取

    // ---- 被测ISR主体 ----
    if (TIM2->SR & TIM_SR_UIF) {
        TIM2->SR &= ~TIM_SR_UIF;
        // 模拟业务:翻转LED、处理数据等
        GPIOA->ODR ^= GPIO_ODR_OD5;
    }
    // ---------------------

    isr_cycles = DWT_CYCCNT - start; // 计算耗时(自动处理溢出)
}

3. 转换为微秒

float cycles_to_us(uint32_t cycles, uint32_t cpu_freq_hz)
{
    return (float)cycles * 1000000.0f / (float)cpu_freq_hz;
}
// 示例:72MHz下,isr_cycles=720 → 10us

测量注意事项

  • 读取位置:start应尽量靠近ISR入口,end尽量靠近出口,但需避开编译器优化导致的指令重排。建议用__attribute__((noinline))或volatile防止优化。
  • 中断嵌套:若ISR中允许更高优先级中断抢占,测量值会包含嵌套时间,需根据场景判断是否关闭嵌套。
  • 编译器优化:-O2下可能将DWT_CYCCNT读取优化掉,务必使用volatile指针。
  • 计数器溢出:32位减法天然处理单次溢出,但若ISR超过59秒(不可能),需额外处理。
  • 调试影响:调试器暂停时CYCCNT可能停止或继续,测量时避免断点。

优化取舍:何时该优化ISR

测得耗时后,不要盲目优化。建议按以下策略决策:

  • 对比中断周期:若ISR耗时超过中断周期的10%~20%,应优先优化。例如1kHz中断(1ms周期),ISR超过100us即需关注。
  • 区分硬实时与软实时:硬实时ISR必须满足最坏执行时间(WCET),软实时可适当放宽。
  • 优化手段优先级:
    • 减少ISR内浮点运算(改用定点或移出ISR)。
    • 将非关键处理移到主循环或RTOS任务(如置标志位)。
    • 避免在ISR中调用printf、malloc等耗时函数。
    • 使用DMA减轻CPU负担。
  • 不要过度优化:若ISR耗时远小于周期且系统稳定,保留可读性比压榨几微秒更有价值。

总结

DWT周期计数器为Cortex-M开发者提供了一种零成本、高精度的ISR耗时测量手段。通过本文的初始化与测量代码,你可以快速评估中断性能,并结合实际周期与实时性要求做出合理的优化取舍。记住:测量先行,优化有据,避免凭感觉调优。