ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32G474 HRTIM实战:500kHz全桥PWM配置全攻略

STM32G474 HRTIM实战:500kHz全桥PWM配置全攻略 简介面向使用STM32F334内置高分辨率定时器(HRTIM)进行PWM输出的开发者这份仅含两个源文件的精简工程完整演示了HRTIM的实战配置与用法。该定时器最大计数频率高达4.608GHz时间控制精度可达217ps(0.217ns)特别适合数字电源、高频逆变器、变频器等高精度中高频控制场景。压缩包仅2KB结构清晰包含1个.h和1个.c两个文件即HRTIM_app.h与HRTIM_app.c代码集中在基本定时器初始化、PWM输出通道配置及中断处理环节便于直接移植复用。资源已有4411人学习下载适合具备一定STM32基础、希望跳过冗长参考手册快速掌握高级定时器特性的软硬件工程师。通过这个浓缩工程读者能直观理解HRTIM的寄存器操作流程、输出比较模式设置和死区控制思路进而快速验证PWM生成效果有效缩短项目预研时间整体结构很适合学习参考。 上周帮朋友调一块500kHz的全桥数字电源板板子主控是STM32G474一开始PWM方案用的是普通高级定时器TIM1后来被占空比分辨率卡死换成HRTIM高分辨率定时器才真正跑通。这个过程我后来总结了一下HRTIM用于PWM输出实际上要跨过时钟配置、死区计算、故障保护、引脚映射四个大坑每一个坑踩进去都够折腾一晚上。这篇文章就把这些经验和盘托出给正在被高频、高分辨率PWM折磨的人一个可以直接参照的路线。1. 为什么普通定时器在500kHz全桥上不够用1.1 频率每翻一倍分辨率就跟着掉一半普通的PWM像TIM1/TIM3本质上就是个计数器计数到周期值然后翻转输出。占空比分辨率完全取决于计数时钟和PWM频率的比值。比如F103上TIM3做主频72MHz产生20kHz的呼吸灯或者舵机控制信号一个周期有3600个计数值占空比步进大约0.028%完全够用但同一个定时器如果输出500kHz方波一个周期只剩144个计数值分辨率掉到0.7%反馈环路稍微精调一点输出就会有肉眼可见的跳变。我在STM32G474上用TIM1做500kHz全桥时就是这个问题。G474主频170MHzTIM1计数时钟最高170MHz周期寄存器ARR最多只能写到340占空比一步就是0.294%。但当时反馈环路的要求是0.1%以内的步进理论上至少需要850个计数值普通定时器怎么凑都凑不出来。这还没算死区调整的精细度——想把死区调到100ns附近TIM1的步进也非常尴尬。1.2 HRTIM不是更快的TIM而是另一套架构HRTIM和普通定时器最大的区别不是单纯把时钟频率提上去而是整个架构都不一样。以STM32G474上的HRTIM为例内部有6个定时器单元1个主定时器M和5个从定时器A到E。每个从定时器又带4组比较单元、两路输出并且这两路输出可以配置成互补PWM内部自带死区发生器。这意味着一个HRTIM外设只要用Timer A和Timer B两个单元就能同时产生两组互补PWM也就是四路信号正好驱动一个全桥。更关键的是它的计数器是32位的比较值更新支持预装载输出还带故障保护FLT、突发模式、斩波调制这些电源控制里才会用到的高级功能。相比之下普通高级定时器的刹车输入和互补输出只能说能用在精细度和响应速度上都不是一个量级。1.3 TIM1与HRTIM的横向对比项目TIM1高级定时器HRTIMSTM32G474计数时钟最高约170 MHz内部可推到GHz量级500kHz下的占空比分辨率约0.29%约0.012%按4.25GHz计数时钟估算计数器位数16位32位比较单元1组PWM比较每从定时器4组比较互补输出与死区支持步进较粗独立死区发生器分辨率可到百皮秒级故障保护刹车输入FLT外部输入/内部事件硬件级快速关断典型用途呼吸灯、舵机、普通电机调速数字电源、DCDC、逆变器、高频电机控制这里要提醒一下HRTIM虽好但不是所有项目都需要。网络热词里总有人拿F103的TIM3做呼吸灯那种场景用普通定时器就是最优解。HRTIM配置复杂、寄存器多、调试成本高只有当你真正被分辨率、死区精度或故障保护响应速度卡住的时候才值得动用它。2. 先把时钟和死区算明白再碰寄存器2.1 HRTIM的时钟链路外面进170MHz内部推到GHz级HRTIM用的时钟不是直接来自APB总线而是从PLL2或PLL1引出的一路独立时钟进入HRTIM模块再由HRTIM内部的可编程倍频路径推高。以STM32G474在170MHz主频条件下为例计数时钟可以推到GHz量级ST官方给出的分辨率在0.2ns附近。这个倍频关系在做系统时钟配置时就要算好CubeMX的时钟树界面里HRTIM1的时钟源要单独确认不能默认挂在APB外设时钟上。如果这里没配好后面HRTIM的周期、占空比、死区全部会偏。顺便说一个经验网上很多人跑不通HRTIM十有八九是CubeMX时钟树里HRTIM1的时钟显示为0或者低得离谱。进时钟树配置页面把HRTIM1的时钟源明确指向PLL2并确认最终频率是第一步也是最容易忽略的一步。2.2 频率、占空比、死区三组公式与实例换算HRTIM的PWM参数计算思路和普通定时器一致只是时钟基数变了。以最常用的连续上数模式为例PWM频率 fHRTIM /PER 1其中PER是周期寄存器值等于普通定时器的ARR占空比 CMP /PER 1× 100%死区时间 死区寄存器值 × 死区时基死区时基默认等于 1 / fHRTIM如果配置了预分频还要再乘预分频倍数假设一块板子配出的fHRTIM为4.25GHz目标频率500kHzPER 4.25GHz / 500kHz - 1 8499占空比50%时CMP 4250想实现100ns死区死区计数值 100ns × 4.25GHz 425这样占空比每一步变化就是1/8500约0.012%比普通TIM1好了25倍左右完全能满足0.1%步进的需求。如果你要的是2MHz开关频率PER 4.25GHz / 2MHz - 1 2124占空比分辨率仍有约0.047%优势依然明显。注意如果板子上实际配出的HRTIM计数时钟不是4.25GHz把上面公式里的fHRTIM替换成真实值重算即可。关键是理解公式本身而不是死记某一个数字。2.3 一套可以直接套用的计算流程我现在的习惯是拿到一个HRTIM PWM需求后按以下顺序算先确认CubeMX时钟树里HRTIM1的实际计数时钟fHRTIM由目标开关频率算PER注意是频率倒数减1由占空比要求算CMP确保CMP永远在0到PER之间由死区时间反推死区寄存器值并在示波器上实测微调。这套流程看着简单但能避免大部分寄存器填了但波形不对的问题。死区寄存器算出来的只是理论值实际还要加上驱动芯片的传播延迟和MOS管的开关延迟。我一般先按计算值设置再用示波器量实际死区差几十ns就直接在寄存器值上加加减减不用重新算整个周期。3. 手把手用CubeMX生成HRTIM互补PWM工程3.1 工程准备与时钟源设置我用的芯片是STM32G474RET6开发环境是STM32CubeMX 6.x加MDK-ARM。G474系列带HRTIM做电源类项目很划算。打开CubeMX后第一步不是配置HRTIM而是先建好整个工程选择芯片、配置调试口、确认时钟来源。把主频和PLL2都配到额定值之后再去时钟树里确认HRTIM1的时钟源指向PLL2并且时钟不是0。3.2 Timer A/B、死区、输出极性的配置顺序在CubeMX左侧选择HRTIM1需要设置的几个关键点Master Timer建议开启并配置为Continuous模式。虽然直接输出用Timer A和Timer B但主定时器可以提供统一的时间基准后面做多路同步或触发ADC采样都更灵活。Timer A和Timer B分别使能模式都选Continuous。进入Timer A配置界面Period填8499。比较单元CMP1填4250这是初始占空比50%的比较值运行中可以用代码动态修改。输出配置里把TIMA1和TIMA2配置为互补输出使能死区插入Dead-Time InsertionRising和Falling的值都填425对应约100ns。Timer B同理会把TIMB1/TIMB2配成互补同样填死区。这样四路输出正好是两组互补PWM可以接全桥的AH/AL、BH/BL四路驱动信号。CubeMX生成的HAL初始化代码会把这套配置全部写进MX_HRTIM1_Init不需要再手写繁琐的寄存器赋值。3.3 引脚分配千万别凭记忆写死HRTIM的输出引脚映射在不同型号上是真不一样同一颗芯片不同封装也可能有差异。我一开始偷懒参考旧项目把引脚写死结果在G474上压根没有对应功能白白查了半天。正确的做法是在CubeMX的Pinout视图中直接勾选HRTIM1的TIMA1、TIMA2、TIMB1、TIMB2四个输出让IDE自动分配引脚。如果自动分配的结果不是你想要的位置可以手动点选其他引脚看复用关系但无论怎么改都要核对最终生成的GPIO Alternate Function号。这一步错了最典型的现象就是程序跑起来后示波器一量引脚上什么都没有。3.4 启动PWM的代码和动态改占空比CubeMX生成工程后HRTIM1的初始化已经完成但真正要让引脚上有波形还需要在main函数里启动PWM这一步很容易漏int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_HRTIM1_Init(); // 启动HRTIM主定时器先运行再启动Timer A和Timer B的波形计数器 HAL_HRTIM_PWMStart(hhrtim1, HRTIM_TIMERID_MASTER); HAL_HRTIM_WaveformCounterStart(hhrtim1, HRTIM_TIMERID_TIMER_A); HAL_HRTIM_WaveformCounterStart(hhrtim1, HRTIM_TIMERID_TIMER_B); while (1) { // 动态修改占空比每次加85对应1%8500 * 1% 85 for (uint32_t cmp 0; cmp 8499U; cmp 85U) { __HAL_HRTIM_SET_CMP1(hhrtim1, HRTIM_TIMERID_TIMER_A, cmp); HAL_Delay(10); } } }__HAL_HRTIM_SET_CMP1只是修改比较值寄存器HRTIM会在下一个PWM周期自动装载生效。这比普通定时器在中断里改CCR的方式省心不需要担心写入时机。3.5 上电没波形时按这三步排查我第一次调HRTIM时板子一上电示波器什么都没量到当时差点怀疑芯片坏了。后来按顺序排查才发现问题。现在只要HRTIM不出波我固定检查三处电源和时钟HRTIM1的时钟源是不是真的指向PLL2CubeMX时钟树里HRTIM1的时钟有没有变成0输出使能HAL_HRTIM_PWMStart和HAL_HRTIM_WaveformCounterStart有没有都调用只启动其中一个输出可能停在复位状态。引脚复用GPIO是不是被初始化成了AF功能检查GPIO_InitStruct里的Alternate参数是否填对。这三处没问题波形基本就该出来了。如果还是没有拿示波器量一下引脚电平再查一下死区配置里是不是把输出极性配反了。4. 故障保护FLT输入让PWM在过流时直接硬件刹车4.1 为什么过流保护不能只靠中断普通定时器的PWM刹车也能做保护但很多人的第一反应是过流了进中断然后在中断里把PWM关掉。这个思路在低速场合勉强能用到了高频电源里就是隐患中断进入延迟、代码执行时间、芯片在异常状态下的不确定行为哪个环节都可能让MOS管多导通几个周期。500kHz的周期是2微秒如果中断慢了几微秒该关的管子还在导通轻则波形畸变重则炸管。HRTIM的FLT输入不是靠中断实现的它是外部事件直接作用到输出单元由硬件把PWM输出拉到一个安全电平。整个过程不需要CPU参与保护路径很短和软件中断完全不在一个量级。这是电源类应用选择HRTIM的一个很重要的原因甚至比高分辨率本身更值钱。4.2 CubeMX里配置FLT输入的四个要点在CubeMX的HRTIM1配置里找到Fault Routing页面添加一个FLT输入比如FLT_IN1。需要设置的几个要点有效电平根据过流保护电路是高电平触发还是低电平触发来选择。我的板子用的是比较器输出低电平表示过流所以选了Active Low。作用范围把FLT_IN1同时绑到Timer A和Timer B上这样任何一路异常四路输出会一起关断。锁定方式可以选永久锁定或者可重触发。永久锁定意味着保护发生后输出一直保持安全状态必须软件复位才能恢复可重触发则是故障信号消失后自动恢复。调试阶段建议用可重触发等整机测试稳定后再改成永久锁定。FLT输入对应的GPIO要确认被初始化成正确的功能模式否则保护信号接入后HRTIM根本感知不到。这些配置在CubeMX里勾选后生成的HAL初始化代码会自动完成不需要手写复杂寄存器。程序里需要额外处理的是记录故障标志、切断驱动电源这类应用层动作。4.3 实测快速关断是HRTIM最值钱的功能我做过一个简单测试用信号发生器给FLT_IN1引脚一个低电平脉冲同时用示波器抓栅极驱动信号。脉冲一到四路PWM输出几乎同时变到安全电平从输入脉冲下降沿到输出翻转的延迟在百纳秒量级肉眼在示波器上根本看不到普通中断方案那种拖泥带水的半个波形。如果只靠中断再快的中断服务函数也很难做到这个响应速度。所以我的建议是只要是做过流、过压保护的电源方案哪怕是比较简单的Buck电路也优先把保护挂在HRTIM的FLT输入上而不是依赖应用层代码。这个习惯能帮你省掉很多炸管烧板的维修费用。5. 三个容易翻车的坑100%占空比、引脚重映射、同步时序5.1 100%占空比的正确写法CMP1PER不是PER1有一次我图省事想快速让PWM输出100%占空比也就是引脚一直输出高电平直接把CMP1写成了PER1也就是8500。结果示波器看到的不是恒高而是一堆乱七八糟的窄脉冲输出波形完全不对。查了半天原因在于HRTIM比较值超过周期值后比较事件的产生时机和正常PWM模式下的预期不一致尤其在死区插入开启时Rising/Falling逻辑里会出现额外的边沿示波器上看起来就像毛刺。正确的做法是把CMP1设为PER也就是8499。这时候输出在计数周期结束前一直保持有效电平占空比严格等于100%。反过来0%占空比直接设CMP10不要设置成0xFFFFFFFF之类的大值。动态调占空比时我习惯加一层边界判断任何情况下CMP值都落在0到PER之间宁可牺牲一点点满占空比也不要让寄存器进入预期之外的状态。5.2 引脚重映射后必须核对AF号HRTIM输出引脚的重映射选项比普通定时器多这在PCB布局上是优势但也带来一个麻烦如果只靠图形化界面点选最后生成的GPIO配置可能不是你心里想的那组引脚。我遇到过一次情况CubeMX自动分配的TIMA1引脚和板上已有的SPI引脚冲突我在Pinout里手动选择了另一组引脚但忘了核对AF号结果下载程序后TIMA1上没有输出折腾了两个小时才发现是GPIO_InitStruct里的Alternate参数还是之前那个值。处理办法很简单在CubeMX里重新分配引脚后切到GPIO配置页面逐个确认每个输出引脚的Alternate Function Label都是HRTIM相关如果手动改过引脚一定要检查这个字段是否同步更新。5.3 Master、Timer A、Timer B的启动顺序不能乱HRTIM的Master Timer在这个例子里不参与直接输出但它负责统一调度和同步。主定时器不启动的时候Timer A和Timer B虽然也能计数但你在运行中更新比较值、做多路同步切换时就可能出现通道间相位不一致的情况。我自己的经验是程序里启动HRTIM时永远把Master放在第一个Start然后才是Timer A、Timer B。反过来会导致一些随机性的波形毛刺尤其在运行中动态调整死区或者切换PWM模式的时候。这个问题不是每次都复现但一旦复现非常难查最好提前养成固定顺序的习惯。5.4 我调HRTIM攒下的几个习惯最后分享几个踩过不少坑之后养成的习惯。第一初次点亮HRTIM建议先用低压逻辑测试不要直接带功率级否则波形没调对炸管就是一瞬间的事。第二调死区精度需要带宽足够的示波器普通示波器量高速PWM本身就有失真会误导你反复调参数。第三HRTIM寄存器实在太多不用指望全部记住我到现在配置还是靠CubeMX起手再针对性地看参考手册里对应章节。既然芯片厂商给了配置工具就用起来把精力放在波形和算法上比手搓寄存器有意义得多。本文还有配套的精品资源点击获取
返回列表