一、为什么需要死区?

在 H 桥或三相逆变器中,上下桥臂的开关管不能同时导通,否则会造成电源短路。由于开关管存在关断延迟,若互补 PWM 信号无间隔切换,极易发生直通。死区(Dead Time)就是在互补信号之间插入的一段延时,确保一个管子完全关断后另一个才导通。STM32 高级定时器(TIM1/TIM8)内置死区发生器,可自动插入延时,简化设计。

二、输出比较与 PWM 原理

定时器输出比较(Output Compare)是指定时器计数值 CNT 与预设的比较值 CCR 匹配时,触发输出引脚电平翻转或产生事件。PWM 模式是输出比较的一种特殊应用:计数器从 0 向上计数到 ARR(自动重载值),当 CNT < CCR 时输出有效电平,否则输出无效电平,从而生成占空比可调的方波。

  • 频率 = 定时器时钟 / (ARR + 1) / (PSC + 1)
  • 占空比 = CCR / (ARR + 1)(向上计数模式)

高级定时器支持互补输出(CHx 和 CHxN),并可通过死区寄存器(BDTR)设置插入延时。

三、HAL 库配置步骤(以 STM32F103 为例)

1. CubeMX 配置

  • 选择定时器 TIM1,时钟源设为内部时钟(Internal Clock)。
  • 通道 1 模式设为 PWM Generation CH1,并勾选 Complementary Output(互补输出)。
  • 在 Parameter Settings 中设置:
    • Prescaler(PSC):例如 72-1,得到 1MHz 计数频率。
    • Counter Period(ARR):例如 1000-1,则 PWM 频率 = 1MHz / 1000 = 1kHz。
    • Pulse(CCR):例如 500,占空比 50%。
    • Dead Time:直接填写数值,单位是定时器时钟周期。若时钟为 1MHz,则 1 代表 1us。
  • 使能输出通道和互补通道,并设置 GPIO 为复用功能(AF)。

2. 代码生成与初始化

CubeMX 自动生成 MX_TIM1_Init()HAL_TIM_PWM_Init(),但死区寄存器需要手动配置。在 main.c 中,添加以下代码:

/* 开启 PWM 输出 */
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);

/* 设置死区时间(单位:定时器时钟周期) */
htim1.Instance->BDTR &= ~TIM_BDTR_DTG_Mask;
htim1.Instance->BDTR |= TIM_BDTR_DTG_0; // 例如设置 DTG=1,即 1us(若时钟1MHz)

注意:死区寄存器 BDTR 的 DTG 位段支持多种编码格式,详见参考手册。简单模式下,DTG[7:0] 直接表示死区周期数。

四、完整代码示例

以下是一个完整的 PWM 输出示例,包含初始化、启动和动态调整占空比:

/* 定时器句柄 */
TIM_HandleTypeDef htim1;

void MX_TIM1_Init(void)
{
    TIM_OC_InitTypeDef sConfigOC = {0};
    TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 71;          // 72MHz/72 = 1MHz
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim1.Init.Period = 999;            // 1kHz PWM
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim1.Init.RepetitionCounter = 0;
    htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    HAL_TIM_PWM_Init(&htim1);

    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 500;              // 占空比 50%
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
    sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
    HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);

    sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
    sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
    sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
    sBreakDeadTimeConfig.DeadTime = 10;  // 10us 死区(1MHz 时钟)
    sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
    sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
    sBreakDeadTimeConfig.BreakFilter = 0;
    sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
    HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM1_Init();

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);

    while (1)
    {
        // 动态调整占空比
        for (int duty = 0; duty <= 999; duty += 10)
        {
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty);
            HAL_Delay(10);
        }
        for (int duty = 999; duty >= 0; duty -= 10)
        {
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty);
            HAL_Delay(10);
        }
    }
}

五、注意事项与调试技巧

  • 死区单位:DeadTime 的值是定时器时钟周期数,务必根据 PSC 计算实际时间。例如 PSC=71(72MHz/72=1MHz),DeadTime=10 即 10us。
  • 互补输出使能:必须同时调用 HAL_TIM_PWM_StartHAL_TIMEx_PWMN_Start,否则互补通道无输出。
  • 主输出使能:高级定时器需要设置 MOE 位(Main Output Enable),HAL 库在启动时自动处理,但若手动操作寄存器需注意。
  • 死区寄存器格式:DTG 位段支持 7 位或 8 位编码,当 DTG[7:5] 非零时,死区时间计算方式不同,建议查阅参考手册。
  • 刹车功能:若开启 Break 功能,需配置刹车输入极性,否则 PWM 可能无法输出。
  • 调试建议:使用示波器同时观察 CH1 和 CH1N 引脚,验证死区时间是否符合预期。若波形异常,检查 GPIO 复用配置和时钟使能。
  • 动态调整:修改 CCR 时使用 __HAL_TIM_SET_COMPARE,注意在 PWM 周期边界更新,避免毛刺。

六、总结

本文详细介绍了 STM32 高级定时器输出比较与 PWM 死区设置的原理和 HAL 库实现。通过 CubeMX 配置和代码示例,你可以快速生成带死区的互补 PWM 信号,为电机控制等应用打下基础。实际项目中,还需根据功率器件特性调整死区时间,并考虑温度漂移等因素。希望本文能助你在嵌入式开发路上更进一步。