
我一向认为搞单片机的人迟早都会感受到 PWM 的魅力。我最早接触它是发现一个很朴素的现象同一颗 LED用 Arduino 的 analogWrite(128) 点着肉眼看上去比直接接 3.3V 暗了一半可你要真拿电流表去测它并不是一个稳定的半电流而是一串密集的脉冲。就是这串脉冲把“亮度”这种看似连续的模拟量变成了几乎不需要消耗额外能量的数字控制。再后来我把同一套思路搬到直流电机上发现电机居然也吃这一套——给它一个占空比转速就变了而且比串联电阻调速舒服太多。这篇文章我想把这层窗户纸彻底捅破PWM 到底是什么为什么同一套东西既能调 LED 亮度又能让直流电机转起来以及你在实际项目里配置 PWM 时那些没人明说但极容易踩的坑。从原理到 STM32 这类常见单片机的参数计算再到 LED 驱动器、电机啸叫、背光干扰这些现实问题都会聊到。无论你是刚点亮第一颗 LED 的新手还是已经写了不少固化代码的老油条这都值得花十分钟捋一遍。1. 从一盏灯的“渐暗”说起PWM 到底在调什么1.1 占空比LED 的欺骗术LED 本质是一个电流型器件亮度正比于流过它的平均电流。你当然可以用一个电位器串联在回路里调小电流让灯变暗但这样做有几个非常别扭的问题一是电阻上白白分压发热能量全浪费了二是 LED 的亮度变化并不平滑尤其是低压段稍动一下电阻亮度就跳一大截三是如果这颗 LED 是背光、指示灯这类有光学要求的场景线性调光会导致色温漂移白灯会明显偏黄。PWM 换了一种思路不改变 LED 的驱动电流大小而是在高电平和低电平之间快速切换。当切换频率足够高远超肉眼能分辨的闪烁阈值时人眼看到的就是“一个以比例时间发光的灯”。这个比例就是占空比——一个周期内高电平时间占整个周期的百分比。50% 占空比不是一个“电压除以二”的概念而是一个“一半时间全功率发光一半时间完全不发光”的概念。因为视觉暂留效应我们的大脑替它做了平均观感就是半亮。这里有一个很反直觉的点也是很多入门者会困惑的地方用 PWM 调光时LED 峰值电流并没变小所以发光效率、色温这些指标都保持不变。你只改了一个参数——时间占比。这带来的直接好处是LED 可以在 0% 到 100% 之间实现真正线性的亮度调节而且几乎没有额外功耗。PWM 的本质不是“调电”而是“调度能量”。1.2 为什么不用模拟调光色温、效率和驱动三座大山有人会问既然最终都是让 LED 暗下来为什么业内几乎清一色用 PWM而不直接用可调恒流源把电流调小除了前面说的效率问题还有更现实的驱动结构原因。高性能 LED 驱动器通常是恒流 IC而不是通用稳压器它的反馈环路是为“维持设定电流”设计的。如果你从外部把设定电压往下拉环路响应慢、精度差还容易振荡尤其在低电流时线性度很差。相比之下PWM 调光只需要控制 IC 的 EN 引脚或给调光脚一个方波数字端干数字活模拟环路完全不参与稳定可靠。色温和光效问题就更直观了。LED 的发光波长与电流密度相关当电流下降时白光 LED 的色温会变化红光会减弱混合色不如预期。但在 PWM 模式下每次导通都是设计额定电流色温光效全程不变。这也是手机屏幕、电视背光、摄影灯这些对色彩要求高的场景宁可忍受 PWM 频闪也要用它的核心原因。所以你可以记住这个结论凡是要求亮度连续可调又不想引入额外功耗、色偏PWM 是比线性调节更优的方案。我们平时做的呼吸灯、夜灯、屏幕背光背后基本都是这个逻辑。2. 直流电机转起来PWM 凭什么能控制转速2.1 电机的“惯性平均”与平均电压把 PWM 从 LED 搬到直流电机核心思路完全一致但物理机制不同。LED 靠“人眼积分”来平滑而电机靠“机械惯性 电感”来平滑。电机线圈本身有电感阻碍电流突变转子又带负载有惯性阻碍转速突变。当 PWM 高电平把 12V 加到电机两端时电流上升转矩增大转子的角加速度变大低电平断电时电流和转矩下降但转子靠动能继续转。如此反复电机的转速就稳定在一个由占空比决定的平均值上。从能量角度看电机的等效模型是一个电阻串一个电感再串一个反电动势源。PWM 给到电机上的平均电压大约等于 Vcc × 占空比。比如 12V 的电机60% 占空比宏观等效于 7.2V 电源转速大致下降 40% 左右。这个“等效”之所以成立前提是 PWM 频率远高于电机机电时间常数的带宽电机响应不过来只感受到平均值。实际上电机是天然的低通滤波器它替你把 PWM 波形的交流成分滤掉了。这里要注意一个工程细节平均电压公式是近似值实际还要扣除 MOSFET 导通压降和续流二极管压降。小电压系统里这些压降不可忽略比如 3.3V 电机 50% 占空比实测平均电压可能只有 1.3V 而不是 1.65V。调试时如果你发现转速和理论值差了一截首先怀疑的应该是管压降而不是占空比公式不对。2.2 为什么电阻调速又慢又烫PWM 又快又好我见过很多入门者一开始用滑动变阻器给电机调速结果就是变阻器发烫、电机低速时扭矩软绵绵、调节范围窄。电阻调速的本质是“用电阻吃掉多余的电压”能量全变成热量而且这种串阻降压方式没办法给电机启动时提供足够大的电流低速扭矩极差。PWM 调速就不一样。功率管在开关过程中只有两种状态完全导通和完全关断。完全导通时管压降接近零损耗是 I²R导通电阻很小一般毫欧级完全关断时没有电流损耗也是零。真正的损耗只发生在开关切换的瞬间而切换时间只要设计得当远短于导通周期所以整体效率可以做到 90% 以上。这就是电力电子里常说的“开关电源比线性电源高效”的根本原因。还有一个容易忽略的细节是电机启动。直流电机堵转时启动电流非常大如果直接 PWM 给一个固定小占空比可能会出现“占空比太小转子根本不转”的情况因为平均电压不足以克服静摩擦和轴承阻力。解决方式是软启动从 0% 开始逐步增加占空比或者先给一个较大的启动占空比再回落。这一步写进代码里非常简单但对电机寿命和启动手感影响极大。调速方式效率低速扭矩发热控制精度串联电阻低差严重低线性稳压器低一般大中等PWM 开关调速高好极小高3. PWM 波形背后的硬件逻辑占空比、频率与配置计算3.1 频率怎么选LED 看闪不闪电机听声不准PWM 有两个基本参数频率和占空比。占空比决定输出平均量的大小频率决定这个调节的“粒度”和是否被人眼、人耳、传感器感知。对 LED 来说频率太低会看到频闪人眼能感知的闪烁阈值因人而异通常低于 100Hz 就会有明显不适但凭感觉行事不靠谱摄像头的采样会造成更明显的滚动静止条也就是摩尔纹。安全起见LED 调光频率至少要 1kHz 以上能上 20kHz 更好很多高端背光甚至做到几十千赫兹。对电机来说情况就微妙了。电机对大几百赫兹的 PWM 响应迟钝所以频率太低了会表现为明显的转速脉动和振动频率太高比如超过 20kHz虽然听不见噪声了但开关损耗增加MOSFET 的栅极驱动电路负担也重还可能因为电机线缆和寄生电感导致过冲。很多舵机和直流电机的驱动板默认用 1kHz 到 20kHz 之间具体数值要看电机的电感参数。一个实用的调试技巧先把频率调到能听到明显啸叫的区间然后逐步提高直到啸叫消失这个位置通常就是适合你系统的甜点区。那到底怎么从芯片手册或原理上精确选定可以参考一个经验规则PWM 频率应远高于负载的截止频率。电机的电气时间常数 L/R 通常在 1ms 量级对应截止频率约 1kHz 左右所以 PWM 至少要用几十 kHz 才能保证电流连续LED 虽然本身没有惯性但人眼和摄影机的积分时间决定了调光频率的下限。总之没有一个万能频率你需要根据负载性质和你的应用场景做权衡。3.2 STM32 定时器配置计算PSC、ARR、CCR 与 PA8 输出这是很多人最晕的部分拿 STM32F103 举例PA8 这个引脚有点特殊它默认是普通 IO但你把它配置成复用推挽输出后它其实映射到 TIM1_CH1——定时器 1 的第一通道。也就是说你想在 PA8 上输出 PWM不能只是 GPIO 翻转而是必须启动定时器 1把定时器配置到 PWM 模式同时还要打开主输出使能 MOE否则 PA8 上什么都出不来。这个坑我至少见过五个新手踩进去现象就是寄存器全配了、GPIO 也配置了示波器一测还是低电平。定时器产生 PWM 的核心逻辑是这样的内部计数器 CNT 从 0 开始向上计数数到 ARR 时归零并重新开始每个周期里软件会把 CCR 寄存器和 CNT 比较根据当前工作在 PWM 模式 1 还是模式 2决定输出引脚是高电平还是低电平。PWM 频率 定时器时钟频率 / ((PSC1) × (ARR1))占空比 CCR / (ARR1)。这里的 1 是因为这两个寄存器都是从 0 开始计数的实际分频值是寄存器值加一。举个例子72MHz 主频下要产生 1kHz 的 PWM最简单的组合是预分频 PSC71自动重装 ARR999。这样定时器时钟变成 72MHz/721MHz即每个 CNT 计数步进为 1μs要数 1000 个步进才溢出一次也就是 1ms 一个周期正好 1kHz。占空比 50% 就让 CCR500CNT 小于 500 时输出高CNT 到 500 后输出低。这套参数是教材级的经典配置。在 STM32CubeMX 里操作更直观选择 TIM1Channel1 设为 PWM Generation CH1给 PSC 填 71、ARR 填 999然后去 Parameter Settings 里核对 PSC、ARR、CCR 的值。CubeMX 会实时显示 Current Period(s)你马上能看到频率结果。注意它默认 CCR 是 0PWM 输出刚开始可能是全低。生成代码后要记得调用 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1)这个函数在 main 里漏掉的话现象又是先亮后灭或者干脆不亮。3.3 PWM 模式 1 和模式 2 的区别谁正谁反很有讲究很多人在 CubeMX 里看到 PWM mode 的选项就随便选一个结果发现高电平的方向反了然后到处找问题。其实 PWM 模式 1 和模式 2 的区别一句话就能说清向上计数时PWM 模式 1 是 CNT 小于 CCR 时输出有效电平CNT 达到 CCR 后输出无效电平PWM 模式 2 刚好相反CNT 小于 CCR 时输出无效电平CNT 达到 CCR 后才输出有效电平。所以如果把模式 1 的波形反相就等价于模式 2。那到底什么时候用哪种这跟你的外部电路有关系。如果你的驱动电路是低电平有效比如 PWM 直接接到一个 PNP 管基极那肯定要选模式 2避免上电瞬间输出高电平导致电机误转。反过来当你想让控制器默认不输出功率时选模式 1 通常更安全因为高电平默认输出低。还有一个更隐蔽的场景做调速时如果想让“占空比增大代表输出变大”你务必确认 CCR 增加时有效电平占比确实增加了。否则就会出现“代码里调大了占空比电机反而减速”的怪问题根源就是模式选反了。华大 HC32F460 等其他国产 MCU 的用法大同小异只是寄存器名字可能叫 CNT、CMP、PER再看一下参考手册的引脚复用配置表把 AF 功能选对就行。核心思维是一样的先搞清楚计数时钟来源和分频再定周期和比较值最后启动主输出。4. 实操中的坑调光闪烁、电机啸叫与驱动器调试4.1 PWM 调光闪烁真凶频率、电源纹波与摄像头当 LED 用 PWM 调光时如果肉眼看上去明明有闪烁先别急着怀疑代码。我对出的第一个案例是定时器频率算错了实际只跑了几十赫兹肉眼直接可见第二个案例是电源纹波PWM 导通瞬间 LED 抽取高峰值电流如果电源内阻大电压就被拉出一个不小的下陷而这个下陷会通过共地干扰到单片机模拟部分甚至让驱动 IC 误判。排除顺序很重要。第一步用示波器看 PWM 引脚本身的波形有没有毛刺和缺失第二步看 LED 两端的电压波形确认在 0 和峰值之间跳变第三步换个低内阻电源或加大输入电容。百试百灵的方法在 LED 驱动板电源入口并联一个 100μF 电解电容再加一个 100nF 陶瓷电容很多“频闪”立刻就消失了因为它们确实不是 PWM 问题而是电源动态响应问题。摄像头下的频闪有一个更隐蔽的原因——采样混叠。摄像机的快门不是连续曝光而是一帧一帧采样当 PWM 频率和帧率或快门速度之间存在非整数倍关系时画面上就会看到缓慢移动的亮暗横条纹。这个在手机拍屏幕时最常见。要彻底解决要么把 PWM 频率提高到快门几乎无法采到完整周期的级别比如 20kHz 以上要么干脆用 DC 调光彻底告别频闪。很多手机 OLED 在低亮度下采用高频 PWM就是这个原因所谓“护眼”很大程度也是冲这个指标来的。4.2 电机啸叫与续流二极管不要忽略的感性负载细节我拿 PWM 驱动直流电机时最常见的问题是电机发出尖锐的“吱吱”声频率还不低听着很心烦。这个声音的本质是 PWM 电流在电机线圈上产生交变磁场“电生磁磁生力”线圈在开关频率下以微米级幅度振动振动通过外壳传到空气中就成了人耳能听到的声音。一般来说啸叫频率等于 PWM 频率或其谐波所以最简单的办法就是把 PWM 频率移出人耳敏感区往 20kHz 以上调或者往 20Hz 以下调但后者会带来肉眼可见的速度脉动不推荐。来自工程串励电机或带减速齿轮箱的电机齿轮啮合也会产生次谐波噪声这种就属于机械问题PWM 再高也救不了。比啸叫更关键的是续流二极管。电机是感性负载当 PWM 关断的瞬间线圈电流不能突变如果电流没有泄放回路就会在 MOS 管漏极上冲出一个电压尖峰轻则击穿栅极重则把单片机顺带带走。所以任何用 PWM 驱动电机的电路电感两端必须并联一个续流二极管方向必须是反向并联——正常工作时不导通关断瞬间把电感电流短路抽走。注意这个二极管要快恢复型或肖特基管普通 1N4007 开关速度太慢放这里会导致关断瞬间有一小段“导通延迟”尖峰照样能把管子打穿。这一点写在你布局和选型清单上比纠结 PID 参数更值钱。4.3 LED 驱动芯片的 FB 和 CS 脚PWM 调光的“后台操控”如果你用的不是单片机的 GPIO 直驱 LED而是专门的 LED 驱动器芯片那 PWM 玩法就升级了。这类芯片上常见两个脚FB 和 CS。CS 是电流采样脚芯片内部通过检测 CS 和地之间的采样电阻压降闭环控制输出电流恒流FB 通常是反馈脚通过电阻分压设定输出电压或接受外部调光信号。PWM 调光一般有两种连法要么把 PWM 直接接到芯片 EN/调光引脚要么把 PWM 经过 RC 滤波变成平滑直流后接到 FB 脚用电压微调输出电流。直接用 EN 脚调光最简单芯片在 PWM 高电平时工作、低电平时关闭相当于占空比控制的是芯片的开与关。但有些芯片 EN 脚响应带宽有限频率太高反应不过来会出现亮度曲线非线性所以要查手册确认“PWM dimming frequency”参数。用 FB 脚调光则更精细常见做法是在 FB 脚和地之间接 RC 低通滤波PWM 经过滤波后变成直流电压电压越高输出电流越大。这里注意 RC 时间常数不能太大否则调光响应迟钝也不能太小否则 FB 脚的纹波又会让输出电流抖动。我通常会让 RC 截止频率设置在 PWM 频率的 1/10 以下先试没问题再微调。调试这种电路最省事的方法是测 CS 脚电阻两端的电压波形。如果 CS 电压是一个干净的方波说明芯片在按你的 PWM 节奏通断如果 CS 电压带尖峰或振荡那基本可以断定环路不稳定或地线处理差。凡是这种电源类芯片调试示波器探头要直接采用弹簧地尽量别用长接地夹不然夹子本身的电感就会给你造出几伏的假振铃信号。4.4 PWM 背光干扰显示屏幕水波纹的排查思路最后聊一个我后来才彻底想明白的谜案。我在做一块小屏幕背光调节时只要把 PWM 调光频率放到比较低的范围屏幕上就会出现明显的水波纹和暗横带干扰而且这个干扰还跟着 PWM 频率上下游走。当时我以为屏幕驱动时序坏了甚至怀疑屏线接触不良折腾到最后才发现背光的 PWM 频率和屏幕的数据时钟/刷新频率产生了交叉干扰。具体原因有两层一层是传导——PWM 背光的高频脉冲电流在地回路上产生噪声耦合进屏幕 FPC 排线或 LCD 驱动电源影响了显示刷新时序另一层是采样——我的摄像头预览里看到的水波纹是 PWM 和帧率混叠出来的伪影跟屏本身无关。排查这类问题需要分步走先用示波器看背光驱动板的供电纹波再看屏幕逻辑电源的纹波最后把 PWM 频率拉高十倍干扰是否移动或消失。最终我的解决方法是给背光驱动单独加了 π 型滤波并把这个频率改到 25kHz显示立刻干净。这里也能看出PWM 并不总是“优解”它只是给了你一个能量开关的控制权怎么开、在哪开、开多快需要结合负载和应用环境来设计。好在这些坑只要踩过一次你以后选型、布板、配参时就会形成条件反射。我个人在实际项目中的体会是PWM 的精髓不在波形本身而在“平均”二字。你调 LED 亮度时做平均的是人眼调电机转速时做平均的是机械惯性给屏背光做恒流时做平均的是反馈环路。无论负载是灯泡、电机还是驱动器你只要想清楚三个问题——开关频率多少合适、占空比怎么计算、以及关断瞬间的能量去哪里——整个系统就不会跑偏。最后再分享一个使我受益很久的小习惯手上没有示波器时我一般留一个测试周期把 CCR 设定成阶梯波每 200ms 加一次占了耳朵听电机声音也能大概判断占空比与转速的线性关系。等上了示波器再细化波形参数效率会高很多。