如何计算中断函数的执行时间

乐活   2025-01-02 07:15   内蒙古  

上面理论一通,再来点调试技巧。我们需要知道这个函数到底耗时不?

最简单可以使用使用GPIO来计算,将MCU的功耗和IO引脚关联起来分析 不仅可以计算时间还可以计算功耗。

使用一个 GPIO 引脚来记录中断函数的开始和结束时间。
  1. 在中断函数的开头将一个 GPIO 引脚置高。
  2. 在中断函数的结尾将这个 GPIO 引脚置低。
  3. 用示波器或逻辑分析仪测量 GPIO 的高电平持续时间,即为中断函数的执行时间。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {    if (htim == &htim2) {        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // GPIO 引脚置高
// 中断任务 AD7682_Read_4_ADC_Value(ADC_Value_u16_inter); IIR_50HZ_Norch_Filter(...); applyIIRFilter(...);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // GPIO 引脚置低 }}


也可以利用 MCU 自带的定时器硬件,在中断函数中记录时间戳。
  1. 在中断开始时读取定时器的计数值( TIMx->CNT)。
  2. 在中断结束时再次读取计数值。
  3. 两次计数值的差值乘以定时器时钟周期,即为中断函数的执行时间。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {    if (htim == &htim2) {        uint32_t start_time = __HAL_TIM_GET_COUNTER(&htim3); // 获取定时器开始计数值
// 中断任务 AD7682_Read_4_ADC_Value(ADC_Value_u16_inter); IIR_50HZ_Norch_Filter(...); applyIIRFilter(...);
uint32_t end_time = __HAL_TIM_GET_COUNTER(&htim3); // 获取定时器结束计数值
uint32_t elapsed_ticks = (end_time >= start_time) ? (end_time - start_time) : ((htim3.Init.Period - start_time) + end_time + 1);
float elapsed_time = elapsed_ticks * (1.0f / HAL_RCC_GetPCLK1Freq()); // 转换为秒 }}

一作差就可以了

不需要外部硬件,完全依赖 MCU 内部资源。可以精确计算运行时间。
需要占用一个定时器。这是什么狗屁话,我直接使用。定时器频率和计数溢出可能需要额外的处理,再说吧。
也可以使用 SysTick 定时器(系统滴答定时器)来记录时间。这个MCU都有,不会浪费捏。
  1. 在中断开始时读取 SysTick 的计数值(SysTick->VAL)。
  2. 在中断结束时再次读取 SysTick 的计数值。
  3. 两次计数值的差值乘以 SysTick 的时钟周期,即为中断函数的执行时间。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {    if (htim == &htim2) {        uint32_t start_time = SysTick->VAL; // 记录开始时间
// 中断任务 AD7682_Read_4_ADC_Value(ADC_Value_u16_inter); IIR_50HZ_Norch_Filter(...); applyIIRFilter(...);
uint32_t end_time = SysTick->VAL; // 记录结束时间
uint32_t elapsed_ticks = (start_time >= end_time) ? (start_time - end_time) : (SysTick->LOAD - end_time + start_time);
float elapsed_time = elapsed_ticks * (1.0f / HAL_RCC_GetSysClockFreq()); // 转换为秒 }}
SysTick 分辨率受限于其时钟频率。如果中断执行时间较长,可能会导致 SysTick 溢出,需要额外处理。这个目前还不知道咋办

还有自带的调试内核,这个时比较高级的技巧,值得重点学习。

  1. 启用 ARM Cortex-M 的 DWT(数据观察和跟踪单元)。
  2. 在中断开始和结束时记录 DWT 的计数值。
  3. 通过计数差值和时钟频率计算执行时间。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {    if (htim == &htim2) {        DWT->CYCCNT = 0; // 重置 DWT 计数器        uint32_t start_cycles = DWT->CYCCNT; // 开始时计数值
// 中断任务 AD7682_Read_4_ADC_Value(ADC_Value_u16_inter); IIR_50HZ_Norch_Filter(...); applyIIRFilter(...);
uint32_t end_cycles = DWT->CYCCNT; // 结束时计数值
uint32_t elapsed_cycles = end_cycles - start_cycles; float elapsed_time = elapsed_cycles * (1.0f / SystemCoreClock); // 转换为秒 }}

其实Keli有个调试组件,Event Recorder

有这个功能,更简单,加个代码就行

在CMSIS里面打开

然后重定向

其实也是使用的DWT

再说吧

安富莱有着详细的教程。代码,三分写,气氛调,不是啥好活。

云深之无迹
纵是相见,亦如不见,潇湘泪雨,执念何苦。
 最新文章