跳转到内容
新建笔记

STM32H743 中断预算:周期估算、丢事件与实测方法

STM32H743能够承受多高的中断频率,取决于每次服务实际消耗多少时间,以及系统是否允许其他工作被推迟。CPU主频只能提供计算预算,不能单独给出“推荐中断频率”。判断是否可用,还要检查响应延迟、事件是否丢失和其他任务是否满足时限。

本页保留480 MHz条件下的预算算例,并给出实际测量方法。以下频率表不是某块开发板的实测结果,也不代表1~5 MHz中断在任意应用中都合理。

先确定硬件和运行条件

跳转到“先确定硬件和运行条件”

H743采用Cortex-M7,配有16 KiB指令缓存和16 KiB数据缓存。允许的最高CPU频率还取决于硅片修订、电压档、Flash等待状态和时钟配置;不能只看料号就假设当前是480 MHz。普通TIM的时钟也与CPU不同,需核对APB分频、TIMPRE及其规格上限。STM32H743规格、AN5312修订迁移

记录测试条件时至少包括:实际CPU与定时器时钟、代码和栈所在存储区、缓存状态、编译器/优化选项、HAL版本、FPU使用情况、其他中断和RTOS任务。H743的Chrom-ART图形加速器不是对任意ISR从Flash执行时间的保证,不能把不同系列的ART描述混用。

时间量起点与终点用来回答什么
响应延迟外部事件或硬件请求出现,到ISR开始执行事件是否能及时处理
ISR内测量区间代码中两个计时点之间某段软件处理消耗多少周期
一次完整服务成本因该中断发生而增加的全部CPU工作平均负载预算和其他工作的可用时间

异常进入/返回、编译器保存寄存器、FPU现场、内存访问、缓存失效、总线竞争、优先级屏蔽和嵌套都会影响结果。Cortex-M7支持尾链和迟到中断等机制,但不能把“入口12周期+出口12周期”当作全部工程的固定ISR总成本。Arm Cortex-M7异常进入与返回

在C函数开头读取DWT时,部分入口和函数序言已经执行;最后读取之后还存在统计写入、函数尾声和异常返回。因此代码测得100周期,不等于整次服务只占100周期。

设CPU频率为 fCPUf_{\mathrm{CPU}},实际完成的每次服务平均消耗 CserviceC_{\mathrm{service}} 个CPU周期,中断服务频率为 fIRQf_{\mathrm{IRQ}}。在统计口径一致、没有重复计算嵌套成本时,平均占用近似为:

U≈fIRQCservicefCPU.U\approx\frac{f_{\mathrm{IRQ}}C_{\mathrm{service}}}{f_{\mathrm{CPU}}}.

若要求这类中断占用不超过预算 Umax⁡U_{\max},算术上有:

fIRQ≲Umax⁡fCPUCservice.f_{\mathrm{IRQ}}\lesssim \frac{U_{\max}f_{\mathrm{CPU}}}{C_{\mathrm{service}}}.

下面假设CPU为480 MHz,并假设完整服务成本已经是表中的数值:

假设完整服务成本算术上占满CPU时的服务频率分配50%平均CPU预算时
30周期16 MHz8 MHz
50周期9.6 MHz4.8 MHz
100周期4.8 MHz2.4 MHz
200周期2.4 MHz1.2 MHz
500周期960 kHz480 kHz
1000周期480 kHz240 kHz

这些是除法结果,不能把“30周期”自动指定给空ISR,也不能把“100~200周期”自动指定给任意HAL回调。占满CPU的列更不是可用于产品的目标,它没有给主循环、通信、操作系统或突发事件留下时间。

例如100 kHz、每次500周期,在480 MHz下约占10.42%的平均周期预算;1 MHz则约104.17%,已经超出这个假设下的总预算。可见仅说“100 kHz一定有问题”或“1 MHz一定很轻松”都缺少服务成本条件。即使平均预算未超,最坏响应延迟仍可能不满足某个控制周期。

使用CYCCNT前,需要启用相应调试/跟踪功能,确认实现了周期计数、软件锁状态允许配置,再使能CYCCNT并验证它确实在递增。不要因为调试器连接时它恰好可用,就假设脱离调试器启动也已经配置完成。运行中不要由不同模块重复清零同一个计数器。

以下函数只测量一次函数调用区间,要求DWT已经正确启用,测量期间CPU频率不变、计数器不被复位,区间短于一次32位回绕:

#include <stdbool.h>
#include <stdint.h>
#include "stm32h743xx.h"
bool measure_cycle_interval(void (*work)(void), uint32_t *cycles)
{
if (work == 0 || cycles == 0 ||
(DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk) != 0u ||
(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) == 0u) {
return false;
}
__DSB();
__ISB();
uint32_t start = DWT->CYCCNT;
work();
__DSB();
__ISB();
*cycles = DWT->CYCCNT - start;
return true;
}

这里的屏障、间接调用和结束读取也有开销;结束DSB还会等待先前显式访问完成。可以用空工作函数测一个参考基线,但不能认为所有流水线、缓存和总线状态下都能精确减去同一个常数。若允许中断抢占,区间可能包含其他ISR时间;若为测量而屏蔽中断,又改变了实际运行条件,应分别标注。

480 MHz下,32位CYCCNT约8.948秒回绕。无符号减法能处理数值跨零,但不能区分已经经过多圈。时间换算使用实测/已核实的CPU时钟:

tμs=106ΔCfCPU.t_{\mu s}=10^6\frac{\Delta C}{f_{\mathrm{CPU}}}.

低功耗、调试暂停或动态变频可能改变该解释。不要把这些条件下的CYCCNT差值直接当作连续墙上时间。

下面展示TIM2更新事件的一个精简测量入口。应用必须先完成TIM2/NVIC配置、开启DWT,并提供 service_tick()。它只处理独占TIM2的更新来源,不是任意共享TIM中断的通用处理器。

#include <stdint.h>
#include "stm32h743xx.h"
extern void service_tick(void);
volatile uint32_t irq_samples;
volatile uint32_t irq_last_cycles;
volatile uint32_t irq_max_cycles;
void TIM2_IRQHandler(void)
{
if ((TIM2->SR & TIM_SR_UIF) != 0u &&
(TIM2->DIER & TIM_DIER_UIE) != 0u) {
uint32_t start = DWT->CYCCNT;
TIM2->SR = ~TIM_SR_UIF;
service_tick();
uint32_t elapsed = DWT->CYCCNT - start;
irq_last_cycles = elapsed;
if (elapsed > irq_max_cycles) {
irq_max_cycles = elapsed;
}
irq_samples++;
}
__DSB();
}

所测区间包含清标志和 service_tick(),不包含前面的标志判断、部分入口开销、后面的统计和异常返回。它适合比较工作函数改动,不应直接拿这个数值填入“完整服务成本”表。前台读取多个统计字段时使用一致快照;打印和格式化放到前台,不要放进被测高频ISR。

TIM2->SR=0会同时清掉其他已置位标志。对写0清除的状态位,读改写也可能误清读取后才到达的另一事件;应按手册语义只清本次处理的目标。上例采用对应目标位写0、其余位写1的形式,与HAL定时器清标志宏的思路一致。

软件次数不等于硬件事件数

跳转到“软件次数不等于硬件事件数”

外设状态位和NVIC挂起位通常不能无限保存同一来源的多个事件。若UIF已经是1,随后又发生更新,软件可能仍只看到一个1。ISR每次递增的 irq_samples 统计的是被处理的服务次数,不能自行证明每个硬件周期都已服务。

检验高频中断需要一个独立参照,例如:

  • 让硬件定时器输出一个可观察的比较/PWM信号,同时用逻辑分析仪对照ISR GPIO脉冲。
  • 使用另一个硬件计数/捕获通道记录源事件,比较源事件数和软件服务数。
  • 使用DMA收集捕获或采样数据,检查缓冲区序列、溢出和处理进度;DMA同样有带宽和缓冲容量限制。

输入捕获还要记录CCxOF过捕获。只看平均ISR间隔、只看LED仍在闪烁,或只看串口还有输出,都不能排除已经丢事件。

GPIO与主循环观察的作用

跳转到“GPIO与主循环观察的作用”

在ISR入口置GPIO、出口清GPIO,可以观察被包围代码的脉宽及抢占造成的延长。要测完整响应延迟,还需要在另一通道同时观察真正的源事件;仅有ISR自己的GPIO无法给出事件发生到入口之间的时间。GPIO写入也经过总线,并不与C语句零延迟等同。

原稿用中断开启前后主循环 main_count 的下降比例估算负载,这可以作为粗略比较,但会受编译优化、访存、缓存、计数溢出、测试窗口和打印影响。循环减少50%不应直接标成精确50%CPU占用。固定构建与测试条件后,它能帮助发现明显退化,精确预算仍需合适的计时和事件完整性证据。

优先减少需要CPU逐次处理的事件:用硬件PWM/比较产生边沿,用定时器触发ADC,用DMA按块搬运,再让任务批量处理。需要高频事件并不意味着必须同频进入CPU中断。

之后再测量是否值得精简HAL分派、减少回调层级、避免浮点/复杂计算,或将时序关键代码及数据放入合适的ITCM/DTCM。放入TCM前要核对链接布局;DMA可达性和D-Cache一致性也要另行检查,见 H7 USB DMA与缓存。

优化应同时报告平均/最大区间、完整响应延迟、源事件与服务次数、其他任务的最坏延迟,以及冷/热缓存等测试条件。只有这些条件一起满足,才能判断某个频率适合当前系统。