1. 从一次电机抖动排查说起:PWM到底在解决什么问题
前阵子帮一个做小型云台的朋友排查问题,他反馈电机在低速转动时抖得厉害,换了驱动芯片、改了电源滤波都没用。我让他把示波器接到驱动板的输入脚上,波形一出来问题就很明显了——他用的是一路普通GPIO在做软件延时翻转,频率只有几百赫兹,占空比还忽大忽小。这种“手动开关”的方式,本质上就是在用CPU的时间片去硬凑一个方波,稍微有点中断打断,波形就歪了。后来换成定时器硬件输出的PWM,频率拉到20kHz,抖动立刻消失。
这个案例其实点出了PWM的核心价值:用数字手段去等效地控制模拟量。单片机、DSP、FPGA这些数字器件只能输出高电平或低电平,但现实世界里我们要控制的东西——电机转速、LED亮度、加热丝功率、舵机角度、甚至音频音量——大多是连续变化的。PWM就是在这两者之间架的一座桥:通过快速切换开关状态,并调节一个周期内高电平所占的比例,让负载“感受到”的等效电压或功率连续可调。
如果你正在做电机驱动、灯光调光、电源管理、音频功放,或者只是想让一个LED呼吸起来,PWM几乎是你绕不开的基础技能。这篇文章我会从PWM的本质特点讲起,把频率、占空比、分辨率、死区、模式1和模式2这些容易混淆的概念掰开揉碎,再结合STM32、HC32F460、CCU6这些常见平台的配置思路,把实际应用里踩过的坑和排查方法一并交代清楚。不管你是刚接触单片机的新手,还是已经用过PWM但总在细节上翻车的老手,应该都能从里面找到点有用的东西。
2. PWM的核心特点与关键参数拆解
2.1 占空比、频率、分辨率:三个必须分清的概念
很多人一开始会把这三个词混着用,结果配置的时候参数对不上。我用一个生活化的类比来说明:假设你在用燃气灶烧水,PWM就像是你快速地点火、熄火、点火、熄火。
占空比(Duty Cycle)指的是一个周期内“点火”时间占总时间的比例。占空比50%就是一半时间在烧、一半时间在停。它直接决定了输出到负载的等效能量大小。占空比越大,等效电压越高,电机转得越快,LED越亮。
频率(Frequency)指的是你每秒完成多少次“点火-熄火”的循环。频率太低,负载会明显地一顿一顿;频率太高,开关损耗又会上升。频率的选择跟负载特性强相关,后面我会给一张常用场景的频率参考表。
分辨率(Resolution)指的是你能把占空比调得多细。比如一个8位分辨率的PWM,占空比只能取0/256、1/256……255/256这256个档位;而16位分辨率就有65536个档位。分辨率越高,调节越平滑,但对定时器位宽和主频的要求也越高。
这三个参数的关系可以用一个公式串起来:
分辨率位数 = log2(定时器计数周期) PWM频率 = 定时器时钟 / (预分频系数 × 计数周期)
举个例子,STM32F103主频72MHz,定时器时钟也是72MHz,预分频设为0,计数周期设为999,那么PWM频率就是72MHz / 1000 = 72kHz,分辨率是log2(1000)≈9.97位,约等于10位。如果你想要更高的分辨率,就得加大计数周期,但频率会相应降低。这是一个典型的“鱼与熊掌”权衡。
2.2 边沿对齐与中心对齐:波形模式的选择逻辑
PWM波形有两种基本的对齐方式,这个在STM32的定时器里体现得特别明显。
边沿对齐模式下,计数器从0一直加到自动重装载值(ARR),然后归零重新开始。输出在计数器小于比较值(CCR)时是一种电平,大于时是另一种。这种模式实现简单,适合大多数电机驱动和调光场景。
中心对齐模式下,计数器先从0加到ARR,再从ARR减到0,形成一个三角波。比较匹配发生在上升沿和下降沿两个时刻,输出的PWM脉冲关于周期中心对称。这种模式最大的好处是谐波分量更分散,在电机控制里能有效降低电磁噪声和转矩脉动。代价是同样的定时器时钟下,中心对齐模式的PWM频率只有边沿对齐的一半。
我个人的经验是:普通调光、加热控制用边沿对齐就够了;做BLDC电机FOC控制、或者对EMI敏感的场合,优先考虑中心对齐。STM32的CubeMX里在定时器配置页可以直接勾选“PWM Mode 1”或“PWM Mode 2”,再配合“Counter Mode”选择Up或Center Aligned,配置起来很直观。
2.3 PWM模式1和模式2的区别:一个容易搞反的细节
这是热词里被问得最多的问题之一。在STM32的参考手册里,PWM模式1和模式2的定义是这样的:
| 模式 | 向上计数时的行为 | 向下计数时的行为 |
|---|---|---|
| PWM模式1 | CNT < CCR时输出有效电平 | CNT > CCR时输出有效电平 |
| PWM模式2 | CNT < CCR时输出无效电平 | CNT > CCR时输出无效电平 |
“有效电平”是什么,取决于你设置的输出极性(高有效还是低有效)。所以模式1和模式2本质上是把输出波形反相了。听起来简单,但实际配置时特别容易搞混,尤其是当你换了驱动芯片、换了MOS管的高端/低端接法之后。
我踩过的一个坑:用STM32驱动一个低端NMOS控制电机,一开始用PWM模式1、高极性,结果上电电机就全速转,占空比设0也停不下来。后来发现是模式选反了,改成PWM模式2之后才正常。所以我的建议是:先确定你的驱动电路在输入高电平时负载是通还是断,再决定用哪个模式。如果不确定,就先用示波器看波形,别急着接负载。
2.4 死区时间:半桥驱动里不能省的一环
只要你的应用涉及半桥或全桥拓扑(比如电机正反转、逆变器),死区时间就是必须认真对待的参数。死区时间是在上下桥臂切换时,故意插入的一小段“两个管子都关断”的时间,防止因为开关延迟导致上下管同时导通,也就是常说的“直通短路”。
STM32的高级定时器(TIM1、TIM8)硬件支持死区插入,配置起来很方便。死区时间的大小要根据你的MOS管或IGBT的开关特性来定,一般从几百纳秒到几微秒不等。太小了起不到保护作用,太大了会损失有效占空比、增加谐波失真。
计算死区时间的公式是:
DT = DTG[7:0] × T_dts
其中T_dts是定时器时钟周期,DTG是死区发生器寄存器的值。具体对应关系在参考手册里有表格,我一般会先在CubeMX里选一个近似值,再用示波器实测上下管的栅极波形,确认没有交叠才放心。
3. 主流平台PWM配置实操:从STM32到HC32F460
3.1 STM32CubeMX配置PWM的完整流程
STM32是很多人入门PWM的第一站,CubeMX把配置流程简化了很多。我以最常见的“PA8输出PWM”为例,把步骤走一遍。
第一步,在CubeMX里选好芯片型号,找到PA8引脚,它对应的是TIM1_CH1。把引脚功能设为“TIM1_CH1”的复用功能。
第二步,进入Timers配置页,选中TIM1,把Clock Source设为Internal Clock,Channel1设为“PWM Generation CH1”。
第三步,配置参数。这里有几个关键项:
- Prescaler(预分频):决定定时器时钟。如果主频72MHz,预分频设为71,定时器时钟就是1MHz。
- Counter Period(ARR):计数周期。设为999,配合1MHz时钟,PWM频率就是1kHz。
- Pulse(CCR):初始占空比。设为500,就是50%占空比。
- PWM Mode:选PWM Mode 1或2,根据你的驱动电路决定。
- CH Polarity:输出极性,一般选High。
第四步,生成代码。在main函数里调用HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);启动PWM。之后可以用__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, value);动态改占空比。
这里有个新手常犯的错误:忘了调用Start函数,或者只调了HAL_TIM_Base_Start没调HAL_TIM_PWM_Start,结果引脚一直没输出。还有人改了CCR值但没生效,检查一下是不是改错了通道,或者定时器根本没在运行。
3.2 HC32F460的PWM配置历程:国产MCU的另一种思路
HC32F460是华大半导体的一款M4内核MCU,在工业控制和电机驱动里用得不少。它的PWM模块叫“Timer6”,配置逻辑和STM32有相似之处,但寄存器命名和库函数风格差别挺大。
我拿它做过一个三相无刷电机的六路PWM输出,配置过程大致是这样的:
首先使能Timer6的时钟,然后配置时基单元。HC32F460的Timer6支持三角波和锯齿波两种计数模式,做电机FOC的话选三角波(中心对齐)。时基配置里要设周期寄存器和比较寄存器,周期值决定了PWM频率。
接着配置输出通道。Timer6有多个通道,每个通道可以独立设置比较值和输出极性。做三相驱动需要三对互补输出,所以要开启互补通道并使能死区功能。死区时间通过DTCR寄存器配置,单位是定时器时钟周期。
最后是启动。HC32F460的库函数里,启动PWM需要调用TIM6_PWM_Start之类的接口,并且要确保输出使能位已经置位。我第一次用的时候因为漏了互补通道的使能,只有上桥臂有波形,下桥臂一直是低,查了半天才发现是配置顺序的问题。
对比下来,STM32的CubeMX生态确实更成熟,图形化配置省心;HC32F460的库函数更接近寄存器操作,灵活度高但上手门槛稍高。如果你是从STM32转过来的,建议先把HC32F460的Timer6章节通读一遍,别想当然地套用STM32的经验。
3.3 CCU6在电机控制中的PWM应用
CCU6是英飞凌(Infineon)XMC系列和一些电机专用芯片里的捕获比较单元,专门为电机控制设计。它最大的特点是硬件死区生成和多通道同步做得非常扎实,适合做三相逆变器的六路PWM。
CCU6通常包含一个T12定时器用于PWM生成,一个T13定时器用于额外的时间基准。T12有三个通道,每个通道可以输出一对互补PWM,正好对应三相桥的六个开关管。死区时间通过死区控制寄存器配置,可以分别设置上升沿和下降沿的死区。
用CCU6做电机控制时,一个很实用的功能是紧急停止输入。当检测到过流或故障信号时,硬件会立刻把六路输出拉到安全状态,不依赖软件响应。这个在工业场景里非常重要,软件中断再快也有延迟,硬件保护才是最后一道防线。
3.4 常用PWM频率参考与选型建议
频率选多少,是配置PWM时第一个要回答的问题。选低了负载会抖、会叫;选高了开关损耗大、驱动芯片可能跟不上。我整理了一张常用场景的参考表:
| 应用场景 | 推荐频率范围 | 选择理由 |
|---|---|---|
| LED调光 | 1kHz - 5kHz | 高于人眼闪烁感知,低于此范围会看到闪烁 |
| 有刷电机调速 | 5kHz - 20kHz | 避开人耳听觉敏感区,减少啸叫 |
| 无刷电机FOC | 10kHz - 20kHz | 兼顾电流环带宽和开关损耗 |
| 舵机控制 | 50Hz | 标准舵机的周期要求 |
| 开关电源 | 100kHz - 500kHz | 减小电感和电容体积 |
| 加热丝控制 | 1Hz - 100Hz | 热惯性大,低频即可,减少开关损耗 |
| 音频D类功放 | 200kHz - 1MHz | 远高于音频带宽,便于滤波 |
这张表是经验值,实际还要结合你的驱动芯片手册、MOS管开关特性、以及EMC测试结果来微调。我一般会先按表选一个初值,然后用示波器看波形质量,再决定要不要调整。
4. PWM实际应用中的典型场景与故障保护
4.1 PWM控制电机:从调速到正反转
用PWM控制电机是最经典的应用。直流有刷电机的调速很简单,一路PWM加一个MOS管就能实现。占空比从0到100%变化,电机从停转到全速。但要注意,电机是感性负载,关断时会产生反向电动势,所以MOS管旁边必须加续流二极管,否则很容易击穿。
如果要正反转,就得用H桥拓扑。H桥有四个开关管,对角导通实现正转或反转。这时候PWM的加法和有刷调速不一样:通常是对角的一对管子中,一个常通、一个用PWM调制,或者两个都调制但互补。这里死区时间就必不可少了,否则同侧上下管直通,瞬间大电流会把管子烧掉。
我做过一个带电流检测的电机驱动板,PWM频率20kHz,死区设了500ns。实测下来,电机运行平稳,MOS管温升也在可接受范围。后来把频率降到5kHz试了试,电机在低速时能听到明显的啸叫,所以频率还是不能省。
4.2 PWM故障保护:硬件刹车与软件监控的配合
PWM系统出故障,最常见的就是过流、过压、过热。保护机制要分两层来做。
硬件层:利用MCU的刹车输入(Break Input)功能。STM32的高级定时器有BKIN引脚,一旦检测到低电平(或高电平,可配置),硬件会立刻把PWM输出强制到预设的安全状态,并可以触发中断。这个过程不经过CPU,响应时间在纳秒级。我在电机驱动板上会把过流比较器的输出直接接到BKIN引脚,一旦电流超过阈值,PWM立刻关断。
软件层:在中断或主循环里定期采样电流、电压、温度,做二级保护。软件保护响应慢,但可以做更复杂的判断,比如积分过流、温度变化率异常等。两层配合,才能既快又稳。
有个细节容易被忽略:刹车触发后,要清除刹车标志才能恢复输出。STM32里需要调用__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_BREAK);,否则PWM一直出不来。我第一次遇到这个问题时,以为是硬件坏了,查了半天手册才发现是标志没清。
4.3 PWM波形监控与调试手段
调PWM离不开示波器。但光看波形还不够,我一般会关注这几个点:
- 频率是否准确:用示波器的频率计功能测一下,和理论值对比。偏差大说明时钟配置有问题。
- 占空比是否线性:从0到100%扫一遍,看实际占空比和设定值是否成线性关系。非线性往往是因为比较值超出了计数范围。
- 上升沿和下降沿是否干净:如果有明显的振铃或过冲,说明驱动电路阻抗不匹配,可能要加栅极电阻。
- 死区是否足够:同时看上下管的栅极波形,确认没有交叠区。
如果没有示波器,也可以用MCU的输入捕获功能来测自己的PWM输出,虽然精度差一些,但验证基本功能足够了。
4.4 PWM与温度监控的关联应用
热词里有个“pwm怎么监控硬盘的温度”,这个问题的本质其实是:用PWM控制风扇转速,再根据温度传感器读数动态调整占空比。硬盘温度升高,就提高风扇PWM占空比;温度降下来,就降低转速减少噪音。
实现逻辑很直接:温度传感器(比如DS18B20或NTC热敏电阻)采集温度,MCU根据温度值查表或做PID运算,输出对应的PWM占空比给风扇。风扇一般是4线制,其中一根是PWM调速线,标准频率是25kHz。
这里有个经验:风扇的PWM调速线通常是开漏输出,需要上拉电阻。而且不是所有风扇都支持0%占空比停转,有些风扇在低占空比下会停转或者抖动,选型时要看手册。
5. 常见问题与排查技巧实录
5.1 PWM无输出或输出异常的排查清单
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 引脚一直低电平 | 定时器未启动 | 检查是否调用了PWM Start函数 |
| 引脚一直高电平 | 模式或极性配置反了 | 切换PWM模式1/2或改极性 |
| 频率不对 | 预分频或ARR算错 | 重新计算时钟链路 |
| 占空比不变化 | 改错了CCR寄存器或通道 | 确认通道号和寄存器地址 |
| 波形有毛刺 | 驱动能力不足或干扰 | 加缓冲器或缩短走线 |
| 电机低速抖动 | PWM频率太低 | 提高到5kHz以上 |
| 上下管发热严重 | 死区不足或直通 | 加大死区,查栅极波形 |
这张表是我自己排查问题时总结的,大部分PWM异常都能在里面找到对应项。
5.2 占空比调不到100%或0%的坑
很多人发现占空比设到最大时输出不是全高,设到最小时还有窄脉冲。这通常是因为比较值的边界处理不对。
在边沿对齐模式下,如果ARR=999,CCR设为999时,计数器从0到998时CNT<CCR,输出有效;CNT=999时CNT=CCR,根据模式定义可能输出无效。所以严格来说占空比是999/1000而不是100%。要输出真正的100%,需要把CCR设得比ARR大,或者用强制输出功能。
STM32里可以用__HAL_TIM_SET_COMPARE设一个大于ARR的值,或者直接操作输出使能位强制通道输出高。我在做LED调光时,如果要求全亮,就直接把引脚切成普通GPIO输出高,比调PWM更干净。
5.3 多路PWM同步与相位控制
做三相电机控制或者多路LED调光时,经常要求多路PWM同步或者有固定相位差。STM32的高级定时器支持主从模式,可以用一个定时器作为主,触发其他定时器同步启动。这样多路PWM的相位关系就是确定的。
如果只是同一个定时器的不同通道,它们天然就是同步的,因为共用一个计数器。但要注意,不同通道的比较值独立,所以占空比可以不同,但频率和相位是对齐的。
我做过一个RGB LED调光项目,三路PWM用同一个定时器的三个通道,频率一致,占空比独立可调。这样颜色混合很均匀,不会因为频率差异产生拍频闪烁。
5.4 低功耗场景下的PWM注意事项
电池供电的设备里,PWM的频率和占空比会影响功耗。频率越高,MOS管的开关损耗越大;占空比越大,负载消耗的功率越大。所以在低功耗设计里,要在性能和功耗之间找平衡。
另外,有些MCU在低功耗模式下会关闭定时器时钟,PWM就停了。如果需要在睡眠期间维持PWM输出,要选择支持低功耗运行的定时器,或者在唤醒后快速恢复PWM。
6. 几个容易被忽略的实战心得
先说一个关于PWM分辨率与频率权衡的体会。很多人一开始总想把频率拉得很高,觉得越高越好。但实际上,如果你的应用对调节平滑度要求高(比如精密调光),反而应该适当降低频率来换取更高的分辨率。我做过一个医疗设备的背光调光,要求256级以上的平滑度,最后把频率定在2kHz,用16位分辨率,效果比20kHz、8位分辨率好得多。
再说一个关于软件PWM和硬件PWM的选择。有些引脚没有硬件PWM功能,只能用软件模拟。软件PWM的优点是灵活,任何GPIO都能用;缺点是占用CPU、频率上不去、抖动大。我的原则是:频率低于1kHz、路数少于3路的场合可以用软件PWM凑合;一旦超过这个范围,果断换有硬件PWM的引脚或芯片。
最后提一下PWM与EMC的关系。PWM本质上是一个快速开关的方波,谐波分量很丰富,很容易通过线缆辐射出去。如果产品要过EMC认证,PWM频率的选择、边沿速率的控制、以及输出滤波都很关键。我一般会在PWM输出端加一个RC低通或者磁珠,把高频谐波压下去。边沿速率也不是越快越好,适当减慢上升沿能显著改善辐射,代价是开关损耗增加,需要权衡。
这些经验都是我在实际项目里一点点试出来的,书本上不一定写,但用起来确实管用。PWM看着简单,真要做好,细节还是挺多的。