【无标题】

第二讲 高精度 PWM 发波

引言

上一讲我们完成了单相逆变器的硬件原理分析与参数设计。从本讲开始,我们正式进入软件控制部分。

逆变器要输出正弦波,核心动作是:用单片机产生高频 PWM,驱动全桥开关管按照 SPWM 调制通断。

本讲围绕 STM32G474RBTx 主控芯片,带你完成以下内容:

  • 理解高分辨率定时器(HRTIM)的架构与工作原理
  • 利用 HRTIM 产生两组带死区的互补 PWM,分别驱动全桥两个桥臂
  • 掌握 PWM 频率计算与死区配置方法

前置知识:了解 C 语言基础、STM32 开发环境(Keil / CubeMX),以及第一讲中全桥拓扑与 SPWM 的基本概念。


一、为什么选择高分辨率定时器(HRTIM)?

在第一讲中我们知道,全桥逆变器的同一桥臂有上管和下管,它们必须互补导通 ,且切换时必须插入死区时间防止直通短路。STM32 的高级定时器(TIM1 等)虽然也支持互补输出和硬件死区,但分辨率受限于 170 MHz 时钟(约 5.9 ns)。

STM32G474 提供了一个更强大的外设------HRTIM(高分辨率定时器) 。它通过内部 DLL(延迟锁定环)倍频技术,将时间分辨率提升至约 184 ps ,相当于等效时钟频率 5.44 GHz。这意味着:

  • 占空比调节更精细:50 kHz 下可实现超过 10 万级细分,输出波形质量更高
  • 死区控制更精确:纳秒级精度,避免过大死区引起的波形畸变
  • 多路独立控制:6 个从定时器可独立配置,天然适合全桥拓扑

因此,本课程选用 HRTIM 作为 PWM 发波的核心外设,这也是开关电源工程实践中的主流方案。


二、HRTIM 整体架构

STM32G474 的 HRTIM1 由 1 个主定时器(Master Timer)6 个从定时器(Timer A ~ Timer F) 组成,总共可输出 12 路高分辨率 PWM

对于单相全桥逆变器,我们只需要 Timer ATimer B,分别控制两个桥臂:

HRTIM 定时器 输出通道 GPIO 引脚 驱动对象
Timer A TA1(主)/ TA2(互补) PA8 / PA9 桥臂 A 上管 / 下管
Timer B TB1(主)/ TB2(互补) PA10 / PA11 桥臂 B 上管 / 下管

每个从定时器包含以下核心模块:

① 时钟源与 DLL 校准

HRTIM 要实现高分辨率,必须由 PLL 的高频输出 直接供给时钟。具体来说,初始化时需要两步:一是在 RCC_CFGR3 中为 HRTIM 选择 PLL 输出作为时钟源,二是使能 APB2 总线上的 HRTIM 寄存器时钟。也就是说,APB2 只是 HRTIM 寄存器的访问通路,真正驱动高分辨率计数的是 PLL 输出(170 MHz)。

上电后还必须进行 DLL 校准,DLL 在 PLL 时钟基础上进行延迟细分,校准完成后才能启用高分辨率倍频功能。

DLL 校准是 HRTIM 正常工作的前提,跳过则高分辨率功能不可用。DLL 仅在 100~170 MHz 范围内工作,分辨率随输入频率从 312 ps 到 184 ps 缩放。

② 时基单元

每个从定时器有独立的时基,核心寄存器包括:

  • 周期寄存器(PERxR):决定计数器溢出值,即 PWM 频率
  • 计数器(CNTxR):按设定模式计数,溢出后归零
  • 比较寄存器(CMP1xR ~ CMP4xR):产生 PWM 边沿、触发 ADC 等事件

STM32G474 搭载的是 HRTIMv2 ,从定时器支持递增计数递增-递减计数(Up-Down Mode)两种模式。递增-递减模式通过 HRTIM_TIMxCR2 寄存器的 UDM 位使能,可以产生中央对齐的对称 PWM 波形。本课程的配置中使用递增计数模式UpDownMode = HRTIM_TIMERUPDOWNMODE_UP)。

③ 互补输出与死区插入

每个从定时器有两路输出。开启死区插入(Dead-Time Insertion) 后,第二路(如 TA2)自动成为第一路(TA1)的互补输出,HRTIM 硬件自动生成互补波形并插入死区,无需额外配置。


三、PWM 产生原理

3.1 基本原理

PWM 的核心思想:在计数器频率固定的前提下,频率 由周期寄存器决定,占空比由比较寄存器决定。

以 HRTIM 递增计数模式为例(如下图所示):

  • 计数器从 0 开始递增
  • 到达周期值(PERxR)时,输出置位(拉高),计数器归零
  • 到达比较值(CMP1xR)时,输出复位(拉低)

改变 CMP1xR 的值,就能实时调节占空比------这正是 SPWM 的实现基础。

3.2 PWM 频率计算

HRTIM 的 PWM 频率公式:

fPWM=fHRTIMPERxRf_{PWM} = \frac{f_{HRTIM}}{PERxR}fPWM=PERxRfHRTIM

在我们的配置中,选用 HRTIM_PRESCALERRATIO_MUL16,等效计数频率为 170 MHz × 16 = 2.72 GHz

实例计算:开关频率 50 kHz:

PERxR=2.72 GHz50 kHz=54400PERxR = \frac{2.72\text{ GHz}}{50\text{ kHz}} = 54400PERxR=50 kHz2.72 GHz=54400

这就是代码中 HRTIM_PWM_PERIOD_TICKS = 54400 的由来。

3.3 占空比计算

在我们的配置中(PER 事件置位、CMP1 事件复位):

D=CMP1xRPERxRD = \frac{CMP1xR}{PERxR}D=PERxRCMP1xR

CMP1xR 越大,高电平持续越长,占空比越大。运行时,控制算法每个开关周期动态更新这个值。


四、互补输出与死区控制

4.1 什么是互补输出?

互补输出是指主通道(见黄色波形)和互补通道(见绿色波形)输出逻辑相反的 PWM 信号------一个为高时另一个为低。这正是同一桥臂上下管所需的驱动信号形式。

4.2 为什么需要死区时间?

现实中 MOSFET 存在开关延迟 ------关断需要时间。如果下管在上管还没完全关断时就开通,两管短暂同时导通,形成直通短路,瞬间过流可能烧毁器件。

死区时间就是在上管关断到下管开通之间强制插入的空白延迟,确保一管彻底截止后另一管才开通。

从波形图可以看到,主通道和互补通道切换时,中间有一小段两者都为低电平的区间------这就是死区时间。

4.3 死区时间计算

HRTIM 的死区发生器支持上升沿和下降沿分别配置,分辨率同样为亚纳秒级。

tDT=DeadTimeValuefDTt_{DT} = \frac{DeadTimeValue}{f_{DT}}tDT=fDTDeadTimeValue

在我们的配置中,死区预分频为 MUL8(死区时钟 = 170 MHz × 8 = 1.36 GHz,步进约 0.735 ns),死区值设为 200:

tDT=200×0.735 ns≈147 nst_{DT} = 200 \times 0.735\text{ ns} \approx 147\text{ ns}tDT=200×0.735 ns≈147 ns

50 kHz 开关频率对应 20 μs 周期,百纳秒级死区既能保证安全切换,又不会引入明显的波形畸变。


五、代码实战:HRTIM PWM 配置

5.1 关键宏定义

c 复制代码
#define HRTIM_PWM_PERIOD_TICKS    (54400U)  /* PWM 周期 → 50kHz */
#define HRTIM_CMP1_INIT_TICKS     (0U)      /* CMP1 初值 = 0(上电不输出有效 PWM) */
#define HRTIM_DEADTIME_TICKS      (200U)    /* 死区计数值 → 约 147ns */

5.2 初始化代码解读

整个 MX_HRTIM1_Init() 函数的流程如下:

① DLL 校准(必须最先完成)

c 复制代码
hhrtim1.Instance = HRTIM1;
hhrtim1.Init.HRTIMInterruptResquests = HRTIM_IT_NONE;
hhrtim1.Init.SyncOptions             = HRTIM_SYNCOPTION_NONE;
HAL_HRTIM_Init(&hhrtim1);

/* DLL 校准------高分辨率功能的前提 */
HAL_HRTIM_DLLCalibrationStart(&hhrtim1, HRTIM_CALIBRATIONRATE_3);
HAL_HRTIM_PollForDLLCalibration(&hhrtim1, 10);

DLL 校准完成后会周期性自动重校准,补偿温度漂移。

② 时基配置(Timer A / Timer B 共用)

c 复制代码
pTimeBaseCfg.Period            = HRTIM_PWM_PERIOD_TICKS;     /* 54400 → 50kHz */
pTimeBaseCfg.RepetitionCounter = 0x00;                       /* 每周期产生更新事件 */
pTimeBaseCfg.PrescalerRatio    = HRTIM_PRESCALERRATIO_MUL16; /* ×16 倍频 */
pTimeBaseCfg.Mode              = HRTIM_MODE_CONTINUOUS;      /* 连续计数 */

HAL_HRTIM_TimeBaseConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, &pTimeBaseCfg);
HAL_HRTIM_TimeBaseConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_B, &pTimeBaseCfg);

③ 比较值配置

c 复制代码
/* Timer A:CMP1 控制占空比(上电为 0) */
pCompareCfg.CompareValue = HRTIM_CMP1_INIT_TICKS;
HAL_HRTIM_WaveformCompareConfig(&hhrtim1,
    HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_1, &pCompareCfg);

/* Timer B:CMP1 控制占空比(上电为 0) */
HAL_HRTIM_WaveformCompareConfig(&hhrtim1,
    HRTIM_TIMERINDEX_TIMER_B, HRTIM_COMPAREUNIT_1, &pCompareCfg);

上电时 CMP1 = 0,确保控制算法启动前不会输出有效 PWM,避免误触发功率管。

④ 死区配置

c 复制代码
pDeadTimeCfg.Prescaler    = HRTIM_TIMDEADTIME_PRESCALERRATIO_MUL8;
pDeadTimeCfg.RisingValue  = HRTIM_DEADTIME_TICKS;   /* 上升沿死区 = 200 */
pDeadTimeCfg.RisingSign   = HRTIM_TIMDEADTIME_RISINGSIGN_POSITIVE;
pDeadTimeCfg.FallingValue = HRTIM_DEADTIME_TICKS;   /* 下降沿死区 = 200 */
pDeadTimeCfg.FallingSign  = HRTIM_TIMDEADTIME_FALLINGSIGN_POSITIVE;

HAL_HRTIM_DeadTimeConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_A, &pDeadTimeCfg);
HAL_HRTIM_DeadTimeConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_B, &pDeadTimeCfg);

上升沿和下降沿死区设为相同值(对称死区),也可根据实际 MOSFET 的开通/关断速度差异分别调整。

⑤ 输出通道配置

c 复制代码
/* 主输出 TA1 / TB1:PER 置位,CMP1 复位 */
pOutputCfg.SetSource   = HRTIM_OUTPUTSET_TIMPER;
pOutputCfg.ResetSource = HRTIM_OUTPUTRESET_TIMCMP1;
HAL_HRTIM_WaveformOutputConfig(&hhrtim1,
    HRTIM_TIMERINDEX_TIMER_A, HRTIM_OUTPUT_TA1, &pOutputCfg);
HAL_HRTIM_WaveformOutputConfig(&hhrtim1,
    HRTIM_TIMERINDEX_TIMER_B, HRTIM_OUTPUT_TB1, &pOutputCfg);

/* 互补输出 TA2 / TB2:无需配置事件,死区插入自动生成 */
pOutputCfg.SetSource   = HRTIM_OUTPUTSET_NONE;
pOutputCfg.ResetSource = HRTIM_OUTPUTRESET_NONE;
HAL_HRTIM_WaveformOutputConfig(&hhrtim1,
    HRTIM_TIMERINDEX_TIMER_A, HRTIM_OUTPUT_TA2, &pOutputCfg);
HAL_HRTIM_WaveformOutputConfig(&hhrtim1,
    HRTIM_TIMERINDEX_TIMER_B, HRTIM_OUTPUT_TB2, &pOutputCfg);

这是整个配置中最关键的部分。TA1/TB1 的 PWM 逻辑是:计数器溢出时拉高、到达 CMP1 时拉低。TA2/TB2 则完全交给硬件------因为定时器配置中已开启 DeadTimeInsertion = ENABLED,HRTIM 会自动生成带死区的互补波形。

⑥ GPIO 引脚配置

c 复制代码
/* PA8 → TA1, PA9 → TA2, PA10 → TB1, PA11 → TB2 */
GPIO_InitStruct.Pin       = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull      = GPIO_NOPULL;
GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF13_HRTIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

5.3 启动与运行时更新

初始化完成后,还需要显式启动计数器和输出:

c 复制代码
/* 启动计数器 */
HAL_HRTIM_WaveformCounterStart(&hhrtim1,
    HRTIM_TIMERID_TIMER_A | HRTIM_TIMERID_TIMER_B);

/* 启动 PWM 输出 */
HAL_HRTIM_WaveformOutputStart(&hhrtim1,
    HRTIM_OUTPUT_TA1 | HRTIM_OUTPUT_TA2 |
    HRTIM_OUTPUT_TB1 | HRTIM_OUTPUT_TB2);

运行过程中,控制算法在每个开关周期计算出两个桥臂的占空比值 DaDb,直接写入比较寄存器:

c 复制代码
/* 动态更新占空比(Da、Db 范围 0 ~ 54400) */
hhrtim1.Instance->sTimerxRegs[HRTIM_TIMERINDEX_TIMER_A].CMP1xR = Da;
hhrtim1.Instance->sTimerxRegs[HRTIM_TIMERINDEX_TIMER_B].CMP1xR = Db;

对于不同的调制策略,DaDb 的赋值方式不同:

  • 双极性调制 :两个桥臂的对角开关管交替导通,桥臂输出在 +Udc+U_{dc}+Udc 和 −Udc-U_{dc}−Udc 之间切换,DaDb 取相同的调制量
  • 单极性调制DaDb 取相反的调制量(vd-vd),正半周桥臂输出在 +Udc+U_{dc}+Udc 和 000 之间切换,负半周在 000 和 −Udc-U_{dc}−Udc 之间切换

调制策略的原理与波形对比将在后续课程详细展开。

5.4 实际运行效果

下面是配置完成后,用示波器观测 PA8(TA1)和 PA9(TA2)的实测波形:


  1. TA1 和 TA2 的互补 PWM 波形(50kHz,占空比约 50%)
  2. 放大后可见死区区间(两通道同时为低的间隔约 147ns)

从波形中可以验证:

  • PWM 频率:周期约 20 μs,对应 50 kHz,与计算一致
  • 互补关系:TA1 为高时 TA2 为低,反之亦然
  • 死区时间:切换瞬间两通道同时为低,持续约 147 ns,与配置吻合

小结

本讲以 HRTIM 高分辨率定时器为核心,完成了逆变器 PWM 发波的配置:

  1. HRTIM 架构:Timer A 和 Timer B 分别驱动全桥两个桥臂,各输出一组互补 PWM(PA8/PA9、PA10/PA11)

  2. 频率与占空比:PERxR = 54400 对应 50 kHz,运行时动态写 CMP1xR 更新占空比

  3. 死区保护:开启 DeadTimeInsertion,硬件自动生成约 147 ns 的互补死区,防止桥臂直通

  4. 代码流程:DLL 校准 → 时基配置 → 比较值初始化 → 死区配置 → 输出通道配置 → 启动发波

下一讲预告: 聚焦调制方式,对比双极性与单极性 SPWM 的原理与波形差异,敬请期待!

相关推荐
修己xj10 小时前
开源 Wiki 神器 Docmost:团队协作知识库的终极解决方案
开源
笨笨饿11 小时前
#58_万能函数的构造方法:ReLU函数
数据结构·人工智能·stm32·单片机·硬件工程·学习方法
冬奇Lab11 小时前
一天一个开源项目(第78篇):MiroFish - 用群体智能引擎预测未来
人工智能·开源·资讯
FreakStudio12 小时前
无硬件学LVGL:基于Web模拟器+MiroPython速通GUI开发—布局与空间管理篇
python·单片机·嵌入式·面向对象·并行计算·电子diy
三品吉他手会点灯15 小时前
STM32F103 学习笔记-21-串口通信(第4节)—串口发送和接收代码讲解(中)
笔记·stm32·单片机·嵌入式硬件·学习
darkb1rd16 小时前
lingbot-map:流式 3D 重建实战指南与解析
开源·github·好物分享
程序员吕洞宾17 小时前
开源多维表格SmartTable v1.1版本更新啦
开源·多维表格
项目題供诗17 小时前
STM32-LED闪烁&LED流水灯&蜂鸣器(四)
stm32·单片机·嵌入式硬件
OpenTiny社区17 小时前
WebSkill —— 运行在浏览器的 Agent 技能
前端·开源·ai编程