## 一、为什么需要死区? 在 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 信号,为电机控制等应用打下基础。实际项目中,还需根据功率器件特性调整死区时间,并考虑温度漂移等因素。希望本文能助你在嵌入式开发路上更进一步。