把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生成第一个半桥的互补PWM,Timer B生成第二个半桥的互补PWM,两路PWM之间的相位关系通过比较值来调整。这种方式代码量不大,逻辑也清晰,适合大多数人上手。
3.2 四路PWM的配置方案
单看PWM生成,移相全桥需要的是两组互补输出。Timer A的OUTA1和OUTA2是一对互补引脚,可以配置成互补且带死区的波形,用来驱动Q1和Q3;Timer 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,周期10us,HRTIM内部时钟按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 A:CMP1上升、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,值得折腾。