4. 启动与停止PWM:HAL_TIM_PWM_Start、HAL_TIM_PWM_Stop、多通道控制

配置好定时器、设置好占空比,PWM 就能自己跑了?没那么简单。你想想看,初始化只是把硬件状态准备好,真正让引脚输出波形,还得靠一个「启动」动作。反过来,想关掉输出,也得有个干净的停止流程。

这一节,我们就专门聊 HAL_TIM_PWM_Start 和 HAL_TIM_PWM_Stop 这两个函数,以及怎么同时控制多个通道。我敢说,很多新手甚至老手,都在多通道启动的顺序上踩过坑。

4.1 启动单通道PWM输出

启动一个通道的 PWM,代码极其简单:

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

第一个参数是定时器句柄指针,第二个参数是通道宏定义。调用之后,对应引脚就会按照你设定的频率和占空比开始输出波形。

但这里有个细节——启动的时机。我个人习惯是在所有外设初始化完成之后,再统一启动 PWM。为什么?因为如果初始化一半就启动,此时 GPIO 可能还没配好,或者 DMA 还没准备好,波形会有一段不确定状态。

小技巧: 如果你用逻辑分析仪抓波形,会发现从调用 Start 到第一个有效边沿,通常会有 1~2 个 PWM 周期的延迟。这是硬件同步机制导致的,不是 bug。

4.2 停止PWM输出

停止函数是对称的:

HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);

调用之后,引脚会变成什么状态?这取决于定时器的配置。默认情况下,停止后引脚会保持当前电平——可能是高、可能是低,取决于停止瞬间的计数值。

我曾经在一个电机驱动项目里,就因为停止后引脚电平不确定,导致 MOS 管短暂导通,差点烧了驱动板。后来我学乖了:停止之前,先把占空比设为 0,等一个周期再调用 Stop。

避坑指南: 停止 PWM 后,如果立即重新启动,建议先调用 HAL_TIM_PWM_Stop 再调用 HAL_TIM_PWM_Start,不要连续两次 Start。否则定时器状态机可能混乱。

4.3 多通道控制:同时启动与独立控制

一个定时器有多个通道,比如 TIM2 有 CH1~CH4。你可以单独启动每个通道:

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);

但这里有个性能问题:每个 Start 调用都会操作一次定时器的寄存器,如果四个通道依次启动,它们之间会有微小的时序差。对于要求严格同步的应用(比如三相电机控制),这个差异不可接受。

怎么办?HAL 库提供了一个批量启动的宏:

__HAL_TIM_ENABLE(&htim2);

这个宏直接操作定时器的 CR1 寄存器,一次性使能整个定时器。所有已配置的通道会同时开始输出。我个人在同步要求高的场合,都是先配置好所有通道,最后统一用这个宏启动。

启动方式 特点 适用场景
HAL_TIM_PWM_Start 逐个启动 每个通道独立使能,有微小延迟 普通 LED 调光、蜂鸣器控制
__HAL_TIM_ENABLE 统一启动 所有通道同时使能,同步性好 电机控制、多相电源

4.4 停止多通道的注意事项

停止也有类似的问题。逐个调用 HAL_TIM_PWM_Stop 会依次关闭每个通道,最后一个通道关闭后,定时器主计数器可能还在跑。如果你希望彻底停止所有输出,可以调用:

__HAL_TIM_DISABLE(&htim2);

这个宏会关闭定时器的主计数器,所有通道同时停止。但要注意:关闭后,定时器的配置会被保留,下次启动不需要重新初始化。

核心原则: 启动用 __HAL_TIM_ENABLE 保证同步,停止用 __HAL_TIM_DISABLE 保证干净。逐个 Start/Stop 留给需要独立控制的通道。

4.5 知识结构图:PWM启动与停止的完整流程

下面这张图,把整个启动/停止的路径画清楚了。你可以对照着理解每个步骤的作用。

PWM 启动与停止流程 定时器初始化完成 选择启动方式 逐个启动 vs 统一启动 逐个 HAL_TIM_PWM_Start 每个通道独立使能 统一 __HAL_TIM_ENABLE 所有通道同时使能 PWM 正常运行 停止方式 逐个 Stop vs 统一 DISABLE PWM 停止完成

4.6 实际项目中的经验总结

最后,分享几个我在项目中积累的实用经验:

  • 启动前检查状态: 调用 Start 之前,最好用 HAL_TIM_PWM_GetState 确认定时器处于就绪状态,避免重复启动导致死锁。
  • 停止后清空比较寄存器: 如果下次启动要用不同的占空比,建议在 Stop 之后把 CCR 寄存器清零,防止残留值影响下次输出。
  • 多通道的优先级: 同一个定时器的多个通道,输出优先级由硬件决定(CH1 > CH2 > CH3 > CH4)。如果两个通道配置了互补输出,启动顺序会影响死区插入的初始状态。
  • 调试时用示波器: 我每次调试 PWM 启动/停止逻辑,都会在引脚上挂示波器。看一眼波形,比读一百遍代码都管用。
重要提醒: 在 RTOS 环境下,HAL_TIM_PWM_Start 和 HAL_TIM_PWM_Stop 不是线程安全的。如果多个任务同时操作同一个定时器,必须加互斥锁保护。

嗯,启动与停止这部分,说白了就是「怎么让波形出来」和「怎么让波形消失」。掌握了这两个动作,再加上多通道的同步控制,你就能灵活地管理任意数量的 PWM 输出了。