跳转到内容
新建笔记

STM32H7 定时器进阶:预装载、重复计数、捕获与互补 PWM

预装载、重复计数器和捕获滤波都在改变定时器事件发生的条件。调试时要把计数时钟、PWM 周期、更新事件和CPU中断分开看,才能解释“波形正确但回调频率不同”或“写了参数却还没生效”。

本页以 STM32H743、RM0433 的 TIM1/8 高级定时器及 TIM2/5 通用定时器为范围。H743 的 TIM2/5 是32位,TIM1/8等为16位;这张能力表不能套到F103。具体实例是否有RCR、互补输出或Break还需查该实例章节。

先假定已经确认定时器内核时钟为240 MHz。设置PSC=239后:

fCNT=240 MHz239+1=1 MHz.f_{\mathrm{CNT}}=\frac{240\,\mathrm{MHz}}{239+1}=1\,\mathrm{MHz}.

向上、边沿对齐计数,ARR=999,CNT每1000个tick回到0,周期为1 ms。PWM1、CCR=500、高有效时得到50%的有效占空比。GPIO复用、CCxE、高级定时器MOE以及极性/空闲状态配置仍决定引脚能否输出预期波形。

240 MHz是这个例子的输入条件;H743的TIMPRE、APB分频、器件修订和电压档必须单独确认。CPU运行480 MHz不能直接推出TIM内核480 MHz,见 STM32时钟树。

PSC写入的是缓冲的分频重装值。ARR是否缓冲由ARPE控制,CCR输出比较值是否缓冲由OCxPE控制。

设置写入后何时影响正在运行的计数/比较
PSC经更新装载进入实际预分频器
ARPE=0ARR直接作用于有效值;运行中修改可能影响当前周期
ARPE=1写入ARR预装载,在更新事件转入有效值
OCxPE=0写CCR直接影响有效比较值
OCxPE=1写CCR预装载,在更新事件转入有效值

运行中把未预装载的ARR降到当前CNT以下,可能使当前周期超出预期,不能假设写入就自动把CNT复位。周期和占空比需要一起改变时,应考虑它们在哪一次更新一起生效,避免出现过渡脉冲。RM0433:定时器时基与预装载

EGR.UG可以产生更新装载,同时还涉及计数器/预分频器重新初始化和标志行为。UDIS抑制更新事件,URS选择哪些来源形成更新请求;它们不能笼统解释成“是否允许计数”。以初始化为目的使用UG后,应按当前设置清理不需要的UIF,再启用中断和输出。

RCR保存重复计数器的重装值N。硬件内部维护递减计数,软件读RCR并不会看到一个每次溢出都减一的“剩余次数”。在正常周期边界触发的情形下,经过N+1次对应的边界事件才形成一次更新事件。

沿用向上计数、1 MHz、ARR=999的例子:

RCR计数器溢出频率正常周期边界形成的UEV频率
01000 Hz1000 Hz
11000 Hz500 Hz
21000 Hz约333.333 Hz

PWM仍每1 ms完成一个周期;RCR=2不把PWM频率改成333 Hz。它改变更新/预装载传送及相关请求的节奏。软件UG或从模式控制器产生更新时还有另外的行为,不应把所有UEV都算成“数够三次溢出”。RM0433 第38.3.3节

H7的TIM15/16/17也有重复计数器,不能写成“只有TIM1和TIM8有RCR”。不同实例、不同系列的RCR位宽也可能不同。

中心对齐计数从0上数到ARR附近再下数到0。完整周期包含 2ARR2\mathrm{ARR} 个计数tick(ARR>0),溢出和下溢都能递减内部重复计数器。

因此1 MHz、ARR=999的中心对齐PWM完整周期为1998 µs,约500.5005 Hz;RCR=2对应每三个边界才更新,并不是每三个完整PWM周期才更新。RCR初值何时写入、计数器何时启动,还会影响首次更新落在上边界还是下边界。CMS同时影响输出比较标志在哪个方向产生,应与UIF更新事件分别检查。

捕获滤波器不使用 PSC 后的时钟

跳转到“捕获滤波器不使用 PSC 后的时钟”

输入捕获滤波器对输入进行采样,要求连续若干次采到一致状态才确认跳变。滤波器采样时钟由ICxF编码以及内部时钟/CKD产生的 fDTSf_{\mathrm{DTS}} 决定,不能直接拿1 MHz的CNT时钟计算。

RM0433中两个常用编码是:

ICxF采样时钟连续样本数N
0不使用该数字滤波流程,按手册规定的输入同步路径处理—
3fCK_INTf_{\mathrm{CK\_INT}}8
8fDTS/8f_{\mathrm{DTS}}/86

若CK_INT为240 MHz且CKD不分频,编码3的8样本尺度约33.3 ns;编码8的6样本尺度约200 ns。这里描述的是采样窗口尺度,实际确认延迟还包含输入到采样时刻的相位和同步路径。把CKD改为分频后,使用DTS的条目也相应变化。

滤波越强,能够通过的最短高/低电平宽度和最高边沿速率就越受限制;两个通道若延迟不同还会影响相位测量。应先测信号和毛刺,再选择编码,不能无条件推荐“设置为8”。

编码器与输入捕获的不同用途

跳转到“编码器与输入捕获的不同用途”

编码器模式让计数器随A/B相位关系递增或递减,适合相对位置及速度;输入捕获则在边沿到来时保存时间戳,适合周期、脉宽和相位。

“A相超前就正向”取决于A/B接线、通道映射和输入极性;接反两相会改变方向。计数器也不会自动提供绝对机械位置,通常还需要索引Z、回零或其他参考。计算速度时明确每转是单相周期数还是四倍频后的计数,并限定快照间位移不越过有符号模差的半范围。具体换算见 转速与编码器测量。

用两个捕获通道测相位

跳转到“用两个捕获通道测相位”

设参考A的两个连续同类边沿为 tA0t_{A0}、tA1t_{A1},同一周期内B的对应边沿为 tBt_B。定义B在A之后的偏移:

T=tA1−tA0,Δt=tB−tA0,ϕ=360∘ΔtT.T=t_{A1}-t_{A0},\qquad \Delta t=t_B-t_{A0},\qquad \phi=360^\circ\frac{\Delta t}{T}.

例如1 MHz时基下,A起点1000、B边沿4500、下一A边沿21000,周期为20000tick,B相对A延后3500tick,得到63°。如果要表达“B领先A”,应另行声明带符号角度或归一化约定,不能在不同图中随意互换。

以下纯计算函数检查B确实位于所给A周期内。无符号差值支持32位计数器跨回绕;真实时间跨度须小于半范围,且三条时间戳已经正确配对。

#include <stdbool.h>
#include <stdint.h>
bool phase_lag_degrees(uint32_t a0, uint32_t a1, uint32_t b,
double *degrees)
{
if (degrees == 0) {
return false;
}
uint32_t period = a1 - a0;
uint32_t offset = b - a0;
if (period == 0u || period >= UINT32_C(0x80000000) ||
offset >= period) {
return false;
}
*degrees = 360.0 * (double)offset / (double)period;
return true;
}

读取两个“最新CCR”不保证它们属于同一周期。如果同一次IRQ里两个捕获标志都挂起,HAL按通道检查的顺序也不等于边沿真实到达的顺序。可靠的数据收集过程应:

  1. 读取各通道捕获值与过捕获状态,记录所用边沿类型。
  2. 在约定的短时间范围内按时间戳顺序处理事件,或保留足够的捕获缓冲。
  3. 用两次连续A边沿建立周期,再寻找该区间内唯一符合条件的B边沿。
  4. 没有B、出现多个B或CCxOF时输出无效状态并重新同步。

H743 TIM2 的捕获时基配置片段

跳转到“H743 TIM2 的捕获时基配置片段”

下面只初始化TIM2内核;GPIO的正确复用、封装引脚、输入电平和之后的IRQ/DMA收集由应用负责。函数会复位TIM2,要求该实例没有其他使用者。给定240 MHz输入时,PSC为239、CNT为1 MHz。

#include <stdbool.h>
#include <stdint.h>
#include "stm32h743xx.h"
bool tim2_capture_timebase_init(uint32_t timer_hz)
{
if (timer_hz < 1000000u || timer_hz % 1000000u != 0u) {
return false;
}
uint32_t divider = timer_hz / 1000000u;
if (divider > 65536u) {
return false;
}
RCC->APB1LENR |= RCC_APB1LENR_TIM2EN;
(void)RCC->APB1LENR;
RCC->APB1LRSTR |= RCC_APB1LRSTR_TIM2RST;
RCC->APB1LRSTR &= ~RCC_APB1LRSTR_TIM2RST;
TIM2->CR1 = TIM_CR1_URS;
TIM2->PSC = divider - 1u;
TIM2->ARR = UINT32_MAX;
TIM2->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0 |
(3u << TIM_CCMR1_IC1F_Pos) |
(3u << TIM_CCMR1_IC2F_Pos);
TIM2->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E;
TIM2->EGR = TIM_EGR_UG;
TIM2->SR = 0u; /* stopped initialization; no events to retain yet */
TIM2->CR1 |= TIM_CR1_CEN;
return true;
}

两个通道都使用Direct TI、上升沿、捕获预分频DIV1和滤波编码3。这里没有开启中断,也没有完成三时间戳的配对状态机;不能把初始化成功当作“相位已经测得”。1 MHz的32位计数器约71.58分钟回绕,仍需使用模差并满足实际跨度条件。

高级定时器的Break可按配置使MOE关闭,从硬件路径改变输出。它解决的是输出级控制,不能仅据此说“电机会立即停止”:机械惯性、续流路径、驱动器使能和输出空闲状态都影响实际结果。

应一起检查:

  • Break输入的有效极性、滤波和允许来源。
  • MOE、AOE及故障消失后的自动/软件重新使能策略。
  • OSSI、OSSR、CR2中的OISx/OISxN、CCxP/CCxNP以及CHx/CHxN实际映射。
  • 外部栅极驱动的输入定义、使能脚和故障反馈。

不能只写一行“MOE=0”就宣称某个半桥输出已经满足所有停止条件。互补输出也不是把两个普通GPIO设成相反电平;它依赖高级定时器的互补和死区单元。

死区基于 tDTSt_{\mathrm{DTS}},不是PSC之后1 MHz计数器的1 µs。对RM0433所述DTG编码,用无符号整数 d=DTGd=\mathrm{DTG} 表示:

tdeadtDTS={d,0≤d≤127,2(64+(d&63)),128≤d≤191,8(32+(d&31)),192≤d≤223,16(32+(d&31)),224≤d≤255.\frac{t_{\mathrm{dead}}}{t_{\mathrm{DTS}}}= \begin{cases} d, & 0\le d\le127,\\ 2(64+(d\mathbin{\&}63)), & 128\le d\le191,\\ 8(32+(d\mathbin{\&}31)), & 192\le d\le223,\\ 16(32+(d\mathbin{\&}31)), & 224\le d\le255. \end{cases}

这里的 & 表示整数按位与。若 fDTS=240f_{\mathrm{DTS}}=240 MHz,DTG=120对应约500 ns;DTG=128对应约533.3 ns;DTG=192对应约1066.7 ns。高编码区间的步长会变大,不能用一个线性倍数贯穿0~255。

选择死区要结合器件开关时间、驱动传播差、负载和实际波形。配置值还可能受LOCK保护限制,修改后应按实际输出测量;计算出寄存器值不等于已经验证功率级行为。

调试时按事件顺序观察

跳转到“调试时按事件顺序观察”

先确认计数时钟和CNT变化,再看ARR/CCR何时装载、UIF与CCxIF何时出现,最后检查NVIC和回调。观察捕获必须同时记录CCxOF;观察互补PWM应同时测CHx、CHxN及Break动作。把软件中断频率当成PWM频率,或只读单个CCR判断整条测量链,都会漏掉关键条件。

需要完整寄存器和HAL字段对照时,参见 F1定时器PWM与寄存器;其中的跨系列说明同样适用,但具体能力仍以H743手册为准。