STM32 定时器输出比较与 PWM 死区设置(HAL库)深度解析
👁 2 阅读 · 2026-08-19 · STM32 PWM
在电机控制、电源变换等应用中,PWM 死区是防止桥臂直通的关键。本文基于 STM32 HAL 库,深入讲解定时器输出比较模式与 PWM 死区设置的原理,通过 CubeMX 配置和代码示例,手把手教你生成带死区的互补 PWM 信号,并剖析常见陷阱与调试技巧,助你快速掌握高级定时器的核心用法。
## 一、为什么需要死区?
在 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` 中,添加以下代码:
```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 输出示例,包含初始化、启动和动态调整占空比:
```c
/* 定时器句柄 */
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_Start` 和 `HAL_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 信号,为电机控制等应用打下基础。实际项目中,还需根据功率器件特性调整死区时间,并考虑温度漂移等因素。希望本文能助你在嵌入式开发路上更进一步。