PWM 与三相逆变桥原理:从硬件到寄存器

系列:电机控制系列 - 第 3 篇 目标平台:STM32F407ZGT6 阅读时间:30 分钟 前置知识:电路基础、电机基础

前言

你可能在想:"PWM 不就是调节占空比吗?有什么难的?"

但在电机控制中,PWM 没那么简单:

  • ❌ 死区时间不够 → MOSFET 烧毁
  • ❌ PWM 频率太低 → 电机噪声大、转矩脉动
  • ❌ 死区补偿不足 → 电流畸变、低速性能差

本篇目标:彻底理解 PWM 与三相逆变桥,掌握 STM32 寄存器级配置。


一、PWM 基础原理

1.1 什么是 PWM?

PWM(Pulse Width Modulation):脉冲宽度调制

 占空比 D = 高电平时间 / 周期 × 100%
 
      ┌───┐   ┌───┐   ┌───┐
      │   │   │   │   │   │
 ─────┘   └───┘   └───┘   └───
      └─Ton─┘
      └───── T ─────┘
 
 平均电压 U_avg = D × U_dc

例子

  • 母线电压 24V
  • 占空比 50%
  • 平均电压 = 0.5 × 24V = 12V

1.2 PWM 参数

频率(Frequency)

 f = 1 / T
 
 电机控制常用频率:
 - 低速应用:10-20 kHz
 - 高速应用:30-50 kHz
 - 超高频率(SiC/GaN):100-500 kHz

频率选择权衡

  • 频率高 → 纹波小、噪声低
  • 频率高 → 开关损耗大、发热

分辨率(Resolution)

 分辨率 = 母线电压 / PWM 级数
 
 STM32F407:
 - 定时器时钟 84 MHz
 - PWM 频率 20 kHz
 - 分辨率 = 84MHz / 20kHz = 4200 级
 
 精度 = 24V / 4200 ≈ 5.7 mV

1.3 单极性 vs 双极性 PWM

单极性 PWM

 相电压在 0 ~ U_dc 之间切换
 
 优点:
 ✅ 开关损耗小(只开关一半)
 ✅ 纹波小
 
 缺点:
 ❌ 需要复杂的调制策略

双极性 PWM

 相电压在 +U_dc/2 ~ -U_dc/2 之间切换
 
 优点:
 ✅ 控制简单
 ✅ 动态响应快
 
 缺点:
 ❌ 开关损耗大(翻倍)
 ❌ 纹波大

电机控制常用:单极性 SVPWM


二、三相全桥逆变器

2.1 拓扑结构

       U_dc (+)
         │
    ┌────┴────┬────┴────┬────┴────┐
    │         │         │         │
   Q1        Q3        Q5        │
    │         │         │         │
    ├────A────┼────B────┼────C────┤
    │         │         │         │
   Q4        Q6        Q2        │
    │         │         │         │
    └────┬────┴────┬────┴────┬────┘
         │         │         │
       U_dc (-)
 
 6 个 MOSFET(或 IGBT):
 - 上桥臂:Q1, Q3, Q5
 - 下桥臂:Q4, Q6, Q2
 - 同一相上下桥臂互补(不能同时导通)

2.2 八种开关状态

 开关状态(Q1Q3Q5):
 ┌─────┬───────┬──────────┬────────┐
 │状态 │ Q1 Q3 Q5 │ 输出电压矢量      │ 备注   │
 ├─────┼───────┼──────────┼────────┤
 │ V0  │ 0 0 0  │ (0, 0)          │ 零矢量 │
 │ V1  │ 1 0 0  │ (2/3·Udc, 0)    │ 有效   │
 │ V2  │ 1 1 0  │ (1/3·Udc, √3/3·Udc) │ 有效 │
 │ V3  │ 0 1 0  │ (-1/3·Udc, √3/3·Udc)│ 有效 │
 │ V4  │ 0 1 1  │ (-2/3·Udc, 0)   │ 有效   │
 │ V5  │ 0 0 1  │ (-1/3·Udc, -√3/3·Udc)│ 有效│
 │ V6  │ 1 0 1  │ (1/3·Udc, -√3/3·Udc) │ 有效│
 │ V7  │ 1 1 1  │ (0, 0)          │ 零矢量 │
 └─────┴───────┴──────────┴────────┘
 
 注:有效矢量 = 6 个,零矢量 = 2 个

2.3 六边形电压轨迹

             V3
             /\
            /  \
           /    \
      V4  /______\  V2
         /      / \
        /      /   \
       /______/_____\
      V5     V0/V7  V1
             V6
 
 六个有效矢量构成正六边形
 SVPWM 目标:用相邻矢量合成任意方向电压

三、死区效应

3.1 为什么需要死区?

问题:上下桥臂不能同时导通(直通)

如果 Q1 和 Q4 同时导通:

U_dc (+)
   │
  Q1 ─┬─ Q4
   │  │  │
   └──┴──┘
      │
U_dc (-)

→ 直通短路!
→ 电流爆炸!
→ MOSFET 烧毁!

解决方法:插入死区时间

理想 PWM:
Q1: ────┐     ┌────────
        └─────┘
Q4: ──────────┐     ┌──
              └─────┘

实际 PWM(带死区):
Q1: ────┐       ┌──────
        └───────┘
       └死区┘
Q4: ──────────┐       ┌
              └───────┘
              └死区┘

死区时间:上下桥臂都关断

3.2 死区时间设置

死区时间 T_dt 取决于:
1. MOSFET 开关时间(ton, toff)
2. 驱动电路延迟
3. 安全裕量

典型值:
- 小功率 MOSFET:100-500 ns
- 大功率 IGBT:1-3 μs

STM32F407 死区计算

定时器时钟:84 MHz
DTG[7:0] 寄存器:

DTG[7:5] = 0xx: T_dt = DTG[6:0] × T_dts
DTG[7:5] = 10x: T_dt = (64 + DTG[5:0]) × 2 × T_dts
DTG[7:5] = 110: T_dt = (32 + DTG[4:0]) × 8 × T_dts
DTG[7:5] = 111: T_dt = (32 + DTG[4:0]) × 16 × T_dts

其中 T_dts = 1 / 84MHz ≈ 11.9 ns

例子

// 死区时间 500ns
// 500ns / 11.9ns ≈ 42
// DTG = 42 = 0x2A
TIM1->BDTR = (0x2A << TIM_BDTR_DTG_Pos);

3.3 死区对电压的影响

死区导致电压畸变:

理想输出电压:U_ref
实际输出电压:U_ref - ΔU

ΔU = ±(T_dt / T_pwm) × U_dc

当电流 > 0 时:ΔU < 0(电压减小)
当电流 < 0 时:ΔU > 0(电压增加)

影响

  • ❌ 低速时电流畸变(死区占比大)
  • ❌ 转矩脉动
  • ❌ 噪声增大

解决:死区补偿(第 18 篇详细讲解)


四、STM32 PWM 配置(寄存器级)

4.1 TIM1 高级定时器

为什么用 TIM1?

  • ✅ 支持互补输出(CH1/CH1N, CH2/CH2N, CH3/CH3N)
  • ✅ 支持死区插入
  • ✅ 支持刹车功能
  • ✅ 适合电机控制

引脚分配

STM32F407ZGT6:
TIM1_CH1  → PA8  (A 相上桥臂)
TIM1_CH1N → PA7  (A 相下桥臂)
TIM1_CH2  → PA9  (B 相上桥臂)
TIM1_CH2N → PB0  (B 相下桥臂)
TIM1_CH3  → PA10 (C 相上桥臂)
TIM1_CH3N → PB1  (C 相下桥臂)
TIM1_BKIN → PA6  (刹车输入)

4.2 完整配置代码

/**
 * @file pwm.c
 * @brief TIM1 三相 PWM 配置(寄存器级)
 * @author 牛马工程师
 */

#include "stm32f4xx.h"

#define PWM_FREQUENCY    20000   // 20kHz
#define TIMER_CLOCK      84000000 // 84MHz
#define DEAD_TIME_NS     500     // 500ns

/**
 * @brief GPIO 配置
 */
void PWM_GPIO_Init(void) {
    // 使能 GPIO 时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;

    // 配置 PA8, PA9, PA10 (TIM1 CH1/CH2/CH3)
    GPIOA->MODER |= (2 << GPIO_MODER_MODE8_Pos) |   // 复用功能
                    (2 << GPIO_MODER_MODE9_Pos) |
                    (2 << GPIO_MODER_MODE10_Pos);
    GPIOA->AFR[1] |= (1 << GPIO_AFRH_AFSEL8_Pos) |  // AF1
                     (1 << GPIO_AFRH_AFSEL9_Pos) |
                     (1 << GPIO_AFRH_AFSEL10_Pos);
    GPIOA->OTYPER &= ~(GPIO_OTYPER_OT8 | GPIO_OTYPER_OT9 | GPIO_OTYPER_OT10); // 推挽
    GPIOA->OSPEEDR |= (3 << GPIO_OSPEEDR_OSPEED8_Pos) | // 高速
                      (3 << GPIO_OSPEEDR_OSPEED9_Pos) |
                      (3 << GPIO_OSPEEDR_OSPEED10_Pos);

    // 配置 PA7, PB0, PB1 (TIM1 CH1N/CH2N/CH3N)
    GPIOA->MODER |= (2 << GPIO_MODER_MODE7_Pos);
    GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL7_Pos);
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT7;
    GPIOA->OSPEEDR |= (3 << GPIO_OSPEEDR_OSPEED7_Pos);

    GPIOB->MODER |= (2 << GPIO_MODER_MODE0_Pos) | (2 << GPIO_MODER_MODE1_Pos);
    GPIOB->AFR[0] |= (1 << GPIO_AFRL_AFSEL0_Pos) | (1 << GPIO_AFRL_AFSEL1_Pos);
    GPIOB->OTYPER &= ~(GPIO_OTYPER_OT0 | GPIO_OTYPER_OT1);
    GPIOB->OSPEEDR |= (3 << GPIO_OSPEEDR_OSPEED0_Pos) | (3 << GPIO_OSPEEDR_OSPEED1_Pos);
}

/**
 * @brief TIM1 PWM 配置
 */
void TIM1_PWM_Init(void) {
    // 1. 使能 TIM1 时钟
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;

    // 2. 时基配置
    uint32_t psc = TIMER_CLOCK / 1000000 - 1;  // 1MHz 计数频率
    uint32_t arr = 1000000 / PWM_FREQUENCY - 1; // 20kHz → ARR = 49

    TIM1->PSC = psc;   // 预分频
    TIM1->ARR = arr;   // 自动重装载值
    TIM1->CR1 |= TIM_CR1_ARPE;  // ARR 预装载使能

    // 3. PWM 模式配置(模式 1:向上计数时 CNT < CCR 输出有效)
    TIM1->CCMR1 |= (6 << TIM_CCMR1_OC1M_Pos) | TIM_CCMR1_OC1PE;  // CH1 PWM 模式 1
    TIM1->CCMR1 |= (6 << TIM_CCMR1_OC2M_Pos) | TIM_CCMR1_OC2PE;  // CH2
    TIM1->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos) | TIM_CCMR2_OC3PE;  // CH3

    // 4. 使能输出和互补输出
    TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC1NE |  // CH1 + CH1N
                  TIM_CCER_CC2E | TIM_CCER_CC2NE |  // CH2 + CH2N
                  TIM_CCER_CC3E | TIM_CCER_CC3NE;   // CH3 + CH3N

    // 5. 死区配置(500ns)
    // T_dts = 1/84MHz = 11.9ns
    // DTG = 500ns / 11.9ns = 42
    uint8_t dtg = (uint8_t)(DEAD_TIME_NS * 84 / 1000);
    TIM1->BDTR |= (dtg << TIM_BDTR_DTG_Pos);

    // 6. 刹车配置(可选)
    TIM1->BDTR |= TIM_BDTR_BKE |   // 刹车使能
                  TIM_BDTR_AOE |   // 自动输出使能
                  (1 << TIM_BDTR_BKP_Pos);  // 刹车极性(低电平有效)

    // 7. 主输出使能(MOE)
    TIM1->BDTR |= TIM_BDTR_MOE;

    // 8. 使能计数器
    TIM1->CR1 |= TIM_CR1_CEN;

    // 9. 初始化占空比(50%)
    TIM1->CCR1 = arr / 2;
    TIM1->CCR2 = arr / 2;
    TIM1->CCR3 = arr / 2;
}

/**
 * @brief 更新 PWM 占空比
 * @param channel 通道(1/2/3)
 * @param duty 占空比(0.0 - 1.0)
 */
void PWM_Set_Duty(uint8_t channel, float duty) {
    uint16_t ccr = (uint16_t)(duty * TIM1->ARR);

    switch (channel) {
        case 1:
            TIM1->CCR1 = ccr;
            break;
        case 2:
            TIM1->CCR2 = ccr;
            break;
        case 3:
            TIM1->CCR3 = ccr;
            break;
    }
}

/**
 * @brief 刹车中断(可选)
 */
void TIM1_BRK_TIM9_IRQHandler(void) {
    if (TIM1->SR & TIM_SR_BIF) {
        // 刹车触发
        TIM1->SR &= ~TIM_SR_BIF;  // 清除标志

        // 关闭所有输出
        TIM1->BDTR &= ~TIM_BDTR_MOE;

        // 用户处理(报警、停机等)
        // ...
    }
}

4.3 关键寄存器说明

寄存器功能关键位
CR1控制寄存器 1CEN(计数使能)、ARPE(ARR 预装载)
PSC预分频计数频率 = f\_clk / (PSC + 1)
ARR自动重装载PWM 周期 = ARR × (1/f\_cnt)
CCMR1/2捕获/比较模式OCxM(PWM 模式)、OCxPE(预装载)
CCER捕获/比较使能CCxE(输出使能)、CCxNE(互补输出使能)
CCR1/2/3捕获/比较值占空比 = CCR / ARR
BDTR刹车和死区DTG(死区)、MOE(主输出使能)、BKE(刹车使能)
SR状态寄存器BIF(刹车中断标志)

五、PWM 测试与调试

5.1 示波器测试

测试点

  1. 上桥臂栅极(Q1 Gate)
  2. 下桥臂栅极(Q4 Gate)
  3. 相电压(A 相输出)

预期波形

上桥臂:
┌───┐   ┌───┐
│   │   │   │
┘   └───┘   └───

下桥臂:
─────┐     ┌────
      └─────┘
      └死区┘

死区时间测量:
光标 1:上桥臂下降沿
光标 2:下桥臂上升沿
Δt ≈ 500ns

5.2 常见问题

问题 1:PWM 无输出

可能原因

  • ❌ MOE 位未置 1
  • ❌ GPIO 复用配置错误
  • ❌ 时钟未使能

解决方法

// 检查 MOE 位
if (!(TIM1->BDTR & TIM_BDTR_MOE)) {
    TIM1->BDTR |= TIM_BDTR_MOE;
}

// 检查 GPIO
if ((GPIOA->AFR[1] & GPIO_AFRH_AFSEL8) == 0) {
    // 重新配置
}

问题 2:死区时间不对

可能原因

  • ❌ DTG 计算错误
  • ❌ 定时器时钟频率不对

解决方法

// 重新计算
// 假设定时器时钟 84MHz,死区 1us
// DTG = 1us / 11.9ns = 84
// DTG[7:0] = 84 = 0x54
// 但 DTG[7:5] = 0xx 时,最大 127 × 11.9ns = 1.51us
// 所以直接用 84
TIM1->BDTR = (TIM1->BDTR & ~TIM_BDTR_DTG) | (84 << TIM_BDTR_DTG_Pos);

问题 3:电机抖动

可能原因

  • ❌ 死区时间过大
  • ❌ PWM 频率太低
  • ❌ 电源纹波大

解决方法

// 减小死区
TIM1->BDTR = (TIM1->BDTR & ~TIM_BDTR_DTG) | (20 << TIM_BDTR_DTG_Pos); // 238ns

// 提高 PWM 频率
TIM1->ARR = 1000000 / 30000 - 1;  // 30kHz

六、总结

6.1 核心要点

  1. PWM 基础

    • 占空比控制平均电压
    • 频率选择:权衡纹波 vs 损耗
    • 分辨率:影响控制精度
  2. 三相逆变桥

    • 6 个 MOSFET,8 种开关状态
    • 上下桥臂互补,需死区
    • 六边形电压轨迹
  3. 死区效应

    • 防止直通,保护 MOSFET
    • 导致电压畸变(低速影响大)
    • 需要补偿
  4. STM32 配置

    • TIM1 高级定时器
    • 寄存器:PSC、ARR、CCRx、BDTR
    • 死区:DTG 寄存器

6.2 下一步

下一篇电机数学模型:从物理到方程

我们将学习:

  • ✅ abc 坐标系下的电压/磁链/转矩方程
  • ✅ 离散化与数值实现
  • ✅ 参数测量方法

附录:PWM 参数计算工具

Excel 计算器

输入:
- 母线电压(V)
- PWM 频率(Hz)
- 死区时间(ns)

输出:
- PSC 值
- ARR 值
- DTG 值
- 分辨率(mV)

在线工具


参考资料

  1. STM32F407 Reference Manual (RM0090)
  2. STM32F4 HAL Driver Description
  3. 《电力电子技术》- 王兆安
  4. TI Application Note: "Understanding and Applying Dead Time"

版权声明:本文采用 CC BY-NC-SA 4.0 协议,欢迎转载,但请注明出处。

更新日志

  • 2026-03-13:发布第 1 版
最后修改:2026 年 03 月 14 日
如果觉得我的文章对你有用,请随意赞赏