
把STM32G474的HRTIM用到移相全桥控制上是我最近大半年一直在折腾的事儿。移相全桥这个拓扑在中大功率DC-DC变换器里非常常见而STM32G474这颗芯片恰好因为HRTIM高分辨率定时器在数字电源圈子里火起来。刚开始接触HRTIM时那堆寄存器和各种事件同步关系确实让人头大但把它摸透之后去做移相全桥你会发现这颗料几乎就是为这类拓扑准备的。这篇文章主要面向正在做全桥DC-DC、想用数字方案替代模拟移相芯片、或者被HRTIM的配置逻辑绕晕的工程师。我会把移相全桥的控制原理、HRTIM的关键配置思路、实际代码框架和调试中遇到的坑一次性说清楚。内容尽量偏实操但也把“为什么这么做”讲透这样你换一个功率等级、换一种频率也能自己推算出来。1. 为什么偏偏选STM32G474这颗料来做移相全桥1.1 模拟移相方案与数字方案的取舍移相全桥的传统做法是直接用模拟移相控制芯片比如UCC3895这类。模拟方案的好处是电路简单、响应快外围搭起来就能工作很多老工程师用得很顺手。但它的短板也很明显移相角靠RC充放电产生参数会随温度漂移环路补偿靠运放周边阻容网络实现调一次参数就要换电阻电容而且没有通信接口想上报电压电流状态、想在线更新控制策略基本无从下手。数字方案把这些痛点都解决了。环路参数可以在代码里改保护逻辑可以按需组合还能通过串口或者CAN实时监控运行状态。但数字方案对MCU提出了一个硬性要求PWM必须足够精确。移相全桥的本质是调节两个半桥之间的相位差相位的细微变化会直接反映在输出占空比上如果PWM分辨率不够环路调起来就是一个字“涩”输出纹波也压不下去。1.2 HRTIM高分辨率到底高在哪普通MCU的高级定时器比如STM32F4系列PWM分辨率受限于定时器时钟一般在6ns左右。这个精度做100kHz的移相全桥勉强可用但要做到500kHz甚至更高开关频率一个计数周期就是好几纳秒占空比调节步进太大环路输出稍微动一下就会在功率级产生明显扰动。STM32G474的HRTIM1最高能跑到4GHz时基分辨率在亚纳秒级别ST官方标称最小分辨率约184ps。以100kHz开关频率为例一个PWM周期10us普通定时器大概只能分1667份HRTIM可以分40000份左右移相角步进能到0.01度级别。这意味着环路可以输出非常细腻的移相调节量在小功率偏差时也能平缓修正不会出现“加一个计数偏大、减一个计数偏小”的尴尬局面。1.3 整个系统的硬件组成用STM32G474做移相全桥典型系统分两块控制板和功率板。控制板核心是G474负责产生四路PWM、采集电压电流、跑环路和保护逻辑。功率板包含全桥MOSFET、隔离驱动、高频变压器、谐振电感、整流二极管和LC输出滤波。电流采样可以用采样电阻加隔离运放也可以用霍尔传感器电压采样用电阻分压加运放调理后进ADC。这套组合里G474承担的不只是发PWM它内部的多个12.5Msps ADC可以同时采样原边电流和输出电压内置比较器还能直接把过流信号做成硬件保护不依赖软件响应。CORDIC和FMAC协处理器虽然在这个项目里用得不重但做闭环运算时也能腾出CPU资源。2. 移相全桥到底在“移”什么拓扑时序拆解2.1 四个功率管的动作顺序移相全桥的主电路有四个开关管Q1、Q3构成一个半桥Q2、Q4构成另一个半桥高频变压器接在两个桥臂中点之间。控制上Q1和Q3是互补导通Q2和Q4也是互补导通但两对桥臂之间有一个固定的相位差这个相位差就是“移相角”。一个完整的开关周期可以拆成几个阶段首先是Q1和Q4同时导通原边加正向电压能量从输入传到副边随后Q4关断Q2还没导通这时候原边电流通过Q2的体二极管续流原边电压被钳到接近零再隔一个死区时间Q2开通此时原边电流继续流过Q2的反并联二极管Q2是零电压开通。接下来Q1关断、Q3导通进入负半周期过程类似。从这里能看出每个管子导通的占空比其实接近50%真正决定原边电压脉宽的是两个半桥之间的移相角。移相角越大原边正向电压持续的时间越长副边得到的能量就越多输出电压越高。2.2 副边电压是怎么“变窄”的很多人第一次看移相全桥会困惑每路PWM都是50%占空比那输出电压怎么调关键就在于两个桥臂中点电压的“差值”波形。Q1、Q3桥臂中点输出一个50%方波Q2、Q4桥臂中点也输出一个50%方波但两个方波有相位差。变压器原边看到的是两点之间的电压差这个差值波形在每个周期里有正电压、负电压、还有两个零电压区间零电压区间的宽度正好等于移相角对应的死区比例。移相角为零时两个桥臂同步动作原边电压差一直是零副边无输出输出电压为零。移相角增大零电压区间变窄原边有效占空比变大副边整流后的平均电压升高。理解了这个就明白为什么移相控制本质上是在“调压”而不是“调频”。2.3 ZVS软开关和占空比丢失移相全桥最大的优势是能实现ZVS软开关减小开关损耗。但四个管子实现ZVS的难度并不一样。超前桥臂的管子关断时原边电流有谐振电感或变压器漏感参与换流电感电流会帮助抽取MOS结电容电荷所以比较容易实现零电压开通。滞后桥臂的管子关断时原边电流处于自由续流状态能量主要靠谐振电感提供如果电感储能不够开关管开通时VDS已经回升ZVS就丢失了。轻载时原边电流小滞后桥臂ZVS丢失几乎是必然的。这时候管子硬开关效率下降甚至可能因为di/dt过大产生振铃。工程上常用增大谐振电感的方式来补偿但电感太大又会带来另一个问题占空比丢失。因为电流换向需要时间在这段时间里原边虽然有电压副边却没有输出实际副边占空比小于原边占空比。谐振电感越大占空比丢失越严重输出能力反而下降。2.4 死区时间和移相角的边界死区时间在这个拓扑里不是可有可无的。同一个桥臂上下两个管子如果死区太小导通切换瞬间可能直通炸管风险极高死区太大又占用了有效占空比影响输出能力。具体死区设置要根据MOS管的结电容、驱动电阻和原边电流大小来算核心原则是让一个管子的体二极管先导通续流再打开另一个管子。移相角也有边界。理论上移相角从0到180度副边占空比从0到接近50%。但实际上移相角接近180度时原边电压脉冲宽度接近整个半周期留给死区和电流换向的空间几乎没有副边占空比反而上不去。我一般把最大移相角限制在170度以内留出安全余量。3. HRTIM配置实战从时基到四路PWM输出3.1 HRTIM内部定时器结构HRTIM1内部有一个Master定时器加五个定时器单元Timer A、B、C、D、E。每个定时器单元都有独立的周期寄存器、多组比较寄存器、死区发生器和输出引脚。这些定时器单元可以各自独立运行也可以通过事件互相同步灵活性非常强。做移相全桥最常用的组合是用Master定时器产生全局时间基准然后让Timer A和Timer B都同步到这个时间基准上。Timer A生成第一个半桥的互补PWMTimer B生成第二个半桥的互补PWM两路PWM之间的相位关系通过比较值来调整。这种方式代码量不大逻辑也清晰适合大多数人上手。3.2 四路PWM的配置方案单看PWM生成移相全桥需要的是两组互补输出。Timer A的OUTA1和OUTA2是一对互补引脚可以配置成互补且带死区的波形用来驱动Q1和Q3Timer B的OUTB1和OUTB2配置成另一对互补波形驱动Q2和Q4。重点在于Timer A和Timer B虽然频率一样但输出波形在时间轴上要错开一个相位。实现移相的方法很多最简单直观的做法是让Timer A和Timer B的计数器完全同相然后直接把Timer B的比较点加上移相计数值。比如Timer A在CMP1位置输出高电平、CMP2位置输出低电平Timer B则在高电平点和低电平点都叠加上一个delay计数值。这样两路PWM的频率和占空比完全一致只是整体在时间轴上平移了delay个计数就得到了想要的移相角。3.3 移相角的换算与代码实现假设开关频率100kHz周期10usHRTIM内部时钟按4GHz算一个计数周期0.25ns。一个完整周期需要的计数个数是40000。移相角30度对应的计数值就是40000乘以30除以360约等于3333个计数。把这个数加到Timer B的CMP1和CMP2上Timer B的输出就比Timer A晚了约833ns。关键代码如下uint32_t period_cnt 40000; uint32_t half_cnt 20000; uint32_t phase_delay (uint32_t)((deg / 360.0f) * period_cnt); /* Timer ACMP1上升、CMP2下降占空比50% */ sCompareCfg.CompareValue 0; HAL_HRTIM_WaveformCompareConfig(hrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_1, sCompareCfg); sCompareCfg.CompareValue half_cnt; HAL_HRTIM_WaveformCompareConfig(hrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_2, sCompareCfg); /* Timer B在A的基础上整体后移 */ sCompareCfg.CompareValue phase_delay; HAL_HRTIM_WaveformCompareConfig(hrtim1, HRTIM_TIMERINDEX_TIMER_B, HRTIM_COMPAREUNIT_1, sCompareCfg); sCompareCfg.CompareValue half_cnt phase_delay; HAL_HRTIM_WaveformCompareConfig(hrtim1, HRTIM_TIMERINDEX_TIMER_B, HRTIM_COMPAREUNIT_2, sCompareCfg);注意CMP2的取值要小于周期计数因为它是“CMP1之后半个周期低电平”这个逻辑里的下降点。当移相角接近180度时half_cnt加上phase_delay会逼近甚至超过40000比较值越过周期边界输出波形就会乱。所以我在代码里强制限制移相角不超过170度宁可牺牲一点点最大占空比也要保证边界的确定性。3.4 死区时间与故障保护配置HRTIM的每个定时器单元都自带死区生成器可以对上升沿和下降沿分别设置死区时间。在HAL库里死区值的单位同样是HRTIM的计数个数设置前要先把死区时间换算成计数。比如死区时间125ns在4GHz时基下对应500个计数。sDeadTimeCfg.RisingValue 500; sDeadTimeCfg.FallingValue 500; HAL_HRTIM_DeadTimeConfig(hrtim1, HRTIM_TIMERINDEX_TIMER_A, sDeadTimeCfg); HAL_HRTIM_DeadTimeConfig(hrtim1, HRTIM_TIMERINDEX_TIMER_B, sDeadTimeCfg);这里有个容易踩的坑HRTIM死区寄存器的位数有限不是无符号大整型。如果算出来的计数值超过寄存器上限写入后会按截断后的值处理实际死区时间跟你预期完全不一样。我建议配置完之后把寄存器读出来回验一下尤其是死区时间比较长或者改过分频系数的时候。故障保护是电源产品里不能省的环节。HRTIM的FLT输入可以配置成故障保护触发源一旦检测到过流信号拉低指定输出脚立刻回到设定的安全电平。这个保护动作是硬件级联的不依赖CPU跑中断速度比软件处理快得多。我在项目里把过流比较器的输出直接接到HRTIM的FLT输入同时让故障触发后的PWM输出强制为低电平确保炸管风险被压到最低。4. 工程落地的关键代码与闭环调参方法4.1 用CubeMX快速搭建HRTIM工程STM32CubeMX对HRTIM的支持已经比较完善虽然生成的初始化代码仍然只是半成品但至少把时钟树和外设时钟理清了。我的习惯是先在CubeMX里开好HRTIM1的四个输出通道配置定时器时钟为最大倍数然后关掉自动生成的初始化函数自己重写配置函数。一个容易被忽略的配置项是HRTIM的时钟来源。G474的HRTIM时钟来自PLL输出不是你系统的40MHz或其他低速时钟必须进CubeMX的Clock Configuration里确认HRTIM1的时钟源已经选到了最高频的PLL输出否则后面所有计数和换算全都会偏。4.2 开环测试先输出波形再谈环路上电后的第一件事不是接功率板而是让PWM在开环状态下输出固定移相角用示波器看四路波形是否正确。我会先把移相角设成一个中间值比如30度然后重点检查几件事同一桥臂两个管子之间是否有死区、两路半桥波形是否频率一致、移相角算出来的时间间隔对不对。对了观察波形最好同时看两个桥臂中点的电压而不是只看PWM输出。PWM输出能看出来移相但看不出死区对副边占空比的影响。只有看到功率级输出端的方波脉宽和毛刺才说明整个驱动链路已经就位。开环状态下如果波形正常再往前推进闭环调试。4.3 PWM移相动态调整与ADC触发采样闭环运行时移相角不是固定的要根据输出电压偏差实时调整。调整的核心就是修改Timer B的两个比较值。但要注意直接修改比较寄存器很可能在PWM周期中途生效导致当前周期的波形变窄或变宽产生畸变。解决办法是利用HRTIM的影子寄存器特性。比较值寄存器在使能了预装载机制后写入的新值不会立刻生效而是在下一个周期更新事件到来时才被装载。HAL库的WaveformCompareConfig函数本身就会走这套机制所以要做的就是保证更新事件与PWM周期同步。HRTIM周期更新事件默认在每个计数周期结束时产生这正好能满足需求。ADC采样时刻也需要讲究。移相全桥的原边电压只有在移相阶段才有脉冲如果随便采样很容易采到Mosfet开关振铃或者电感电流换向尖峰。我用Timer A的CMP2事件触发ADC注入组采样这个点在桥臂中点电压的中点附近理论上电流噪声最小。采样窗口放在这里采到的输出电压和原边电流相对稳定环路控制也更干净。4.4 电压环电流环的PI整定经验移相全桥的闭环控制一般用电压外环加电流内环也可以只做单电压环看需求。我在这套系统上跑了双环电流环响应快限制原边电流峰值电压环调输出稳定度抗负载突变。PI整定没有任何玄学就是一步步来。先挂纯电阻负载电流环断开只调电压环。比例系数从很小开始加观察到输出电压低频振荡就减小一点直到阶跃响应没有明显超调。比例确定了再加积分积分时间先给一个保守值慢慢缩短让稳态误差消失但又不产生低频纹波。需要特别提醒的是环路输出上下限必须做限幅。移相角的调节范围是0到170度如果PI输出超过这个范围比较值就会越界波形直接崩掉。我在代码里每次更新PI输出后都做一次饱和判断同时把积分器做抗积分饱和处理这个问题不处理轻载启动很容易出震荡。5. 实测问题排查实录与避坑速查表5.1 上电炸机前必查的三件事移相全桥最容易出事的就是电容充电瞬间和启动瞬间。第一件事是检查死区同一桥臂两个驱动信号之间没有死区一上电就是直通功率管瞬间冒烟。第二件事是最大移相角限制如果软件跑飞或者PI输出上限没限住移相角逼近180度功率管换流时间不足也很容易炸。第三件事是隔离驱动供电顺序驱动芯片上电时序和PWM输出时序不一致会在MCU复位期间出现不可控的栅极电平这一步比前两个更隐蔽。我一个朋友的项目就遇到过这种问题MCU复位瞬间GPIO处于高阻态驱动芯片输入悬空干扰拉高了栅极电平一送电就炸了一片管。后来在驱动前端加了下拉电阻并把驱动禁用引脚接到MCU的一个IO上MCU初始化完成后再使能驱动这才稳定下来。5.2 PWM抖动、相位不准、占空比漂移怎么查如果你在示波器上看到PWM沿有轻微抖动先别怀疑HRTIM大概率是采样和中断干扰。我在早期测试中就有过这样的现象ADC中断处理时间太长把PWM更新的优先级挤掉了导致比较值的装载时刻发生微小偏移。解决办法是把HRTIM更新相关的代码放到最高优先级中断里ADC数据处理放到主循环或者低优先级中断里做。相位不准还有一个常见原因就是两个定时器单元的计数器没有对齐。Timer A和Timer B如果都是在初始化后各自启动虽然频率一样但相位完全随机必须在初始化时配置成由同一个硬件事件同步启动。我用的是Master定时器的周期事件同时复位Timer A和Timer B这样每次周期开始两个定时器都从零计数相位对齐才有保证。5.3 采样噪声的来源与处理降压电流采样最常见的噪声源是Mosfet开关瞬间的di/dt耦合和地弹。即使采样点已经选在PWM中点驱动回路和采样回路如果共用一段地线依然会把开关毛刺带进ADC。我的做法是功率地、驱动地、采样地先分区布线最后单点连接ADC输入端加了RC低通滤波截止频率远低于环路带宽不影响控制速度。如果采样后依然有偶发尖峰可以在代码里做软件滤波比如连续采样四次取中值或者用一个简单的一阶低通滤波。但滤波深度要控制好加太多滤波对环路动态响应不利轻载到重载突变时容易反应不过来。5.4 问题排查速查表现象可能原因检查方法处理方法一上电就炸管死区不足或直通断电状态下测量驱动波形死区时间增大死区计数检查PWM极性配置输出电压不可调移相角没更新或PI饱和在线查看实际CMP值是否变化检查影子寄存器更新方式检查限幅PWM波形抖动中断优先级或采样干扰看示波器波形与中断执行时间关联提高HRTIM更新中断优先级移相角度偏差两个定时器未同相启动用示波器同时看两路桥臂中点电压配置主定时器同步复位事件轻载输出振荡环路积分饱和或采样噪声抓输出电压波形观察振荡周期加抗积分饱和压缩PI限幅死区实际值和设置值不符死区寄存器位数溢出读回死区寄存器实际值重新换算计数或降低时基5.5 一些关于调试思路的体会回看整个调试过程最大的心得是移相全桥的调试顺序不能跳。先把开环波形调干净再弄保护最后才碰闭环。开环波形没稳定之前就着急上环出了问题很难分清楚是控制逻辑的问题还是功率级的问题。另外HRTIM的寄存器逻辑虽然复杂但它与其他外设的联动能力很强特别是事件触发ADC、故障输入直接封锁PWM这些功能在数字电源里非常实用。只要把时基、同步、影子寄存器这几个概念吃透移相全桥的HRTIM配置并不难。如果你正准备做类似功率段的全桥DC-DC大胆用G474的HRTIM值得折腾。