
1. 从一个让人抓狂的调试现场说起如果你玩过单片机、调过电机、做过呼吸灯或者只是单纯想用一块开发板让LED“柔和地亮起来”那你一定绕不开PWM这三个字母。PWM的全称是脉冲宽度调制翻译成人话就是“通过快速开关来控制平均效果”。听起来简单得不行但我见过太多人——包括我自己早期——在调试PWM时陷入一种诡异的状态代码明明写了示波器上波形也对可电机就是嗡嗡响不转LED亮度就是不对舵机就是抖得像筛糠。问题的根源往往不在于“PWM怎么配置”而在于一个更底层的问题PWM到底控制了什么这个问题看起来像哲学拷问但它直接决定了你调参的方向。如果你以为PWM控制的是电压那你可能会在驱动电路上白费功夫如果你以为PWM控制的是频率那你可能会在占空比上反复折腾却毫无进展。实际上PWM控制的既不是单纯的电压也不是单纯的频率它控制的是一种时间维度上的能量分配比例。这篇文章适合所有正在和PWM打交道的人——不管你是刚接触嵌入式的新手还是已经能写PWM驱动但总觉得自己在“盲调”的老手。我会从底层原理讲起把PWM控制的本质、参数计算、实操配置、常见坑点全部拆开揉碎让你下次看到PWM波形时脑子里不再是一团浆糊而是一张清晰的能量流向图。2. PWM控制的本质时间切片里的能量分配2.1 为什么不是“控制电压”这么简单很多人第一次学PWM时教材会告诉你“PWM通过改变占空比来改变输出电压的平均值”。这句话本身没错但它容易让人产生一个误解以为PWM是在控制电压本身。实际上PWM输出的是方波高电平就是高电平比如3.3V或5V低电平就是低电平0V它从来没有输出过中间电压。所谓“平均电压”是负载或后续电路对时间积分后的结果。举个例子假设你用5V的电源给一个LED供电直接接上去LED会很亮。如果你用PWM占空比设为50%频率1kHz那么LED会在每1毫秒内亮0.5毫秒、灭0.5毫秒。因为人眼的视觉暂留效应你看到的是“半亮”。但如果你用示波器看LED两端的电压它仍然是0V和5V之间跳变的方波不存在2.5V这个电压值。所以PWM控制的第一个层面是控制负载在单位时间内接收到的高电平时间比例。这个比例就是占空比。占空比越大负载“感受到”的能量越多占空比越小能量越少。但这里有一个关键前提——负载必须具有惯性或低通特性否则你看到的就不是“平均效果”而是闪烁或抖动。2.2 频率决定了“控制”的细腻程度占空比决定了能量多少频率则决定了这种能量分配的“颗粒度”。还是用LED举例如果PWM频率只有10Hz占空比50%你会看到LED明显闪烁因为10Hz的周期是100毫秒人眼能轻松分辨。如果频率提高到1kHz周期变成1毫秒人眼就看不出来了。如果频率提高到20kHz以上连人耳都听不到电机或电感的啸叫声了。这里有一个常见的误区很多人以为频率越高越好。实际上频率越高MOS管或三极管的开关损耗就越大驱动电路的发热就越严重。而且频率太高时PCB走线的寄生电感和电容会导致波形畸变反而让控制效果变差。所以频率的选择是一个权衡既要高于负载的响应带宽又要低于驱动电路的开关能力上限。对于LED调光通常1kHz到5kHz就够了对于直流电机调速常用10kHz到20kHz避开人耳听觉范围对于舵机控制频率固定为50Hz因为舵机内部的控制芯片就是按这个标准设计的对于开关电源频率可能高达几百kHz甚至MHz因为要减小电感和电容的体积。2.3 占空比与频率的乘积效应把占空比和频率放在一起看你会发现PWM控制的真正对象是单位时间内的能量包数量和每个能量包的大小。频率决定了能量包的数量占空比决定了每个能量包中“高电平”所占的比例。两者共同决定了负载最终获得的平均功率。用公式表示就是平均电压 高电平电压 × 占空比。平均功率 高电平电压² / 负载电阻 × 占空比对于纯电阻负载。如果是电机这类感性负载还要考虑电感的续流效应实际电流波形会更平滑但平均电流仍然与占空比成正比。理解这一点之后你就能明白为什么调PWM时不能只盯着一个参数。如果你发现电机转速不够你可以提高占空比也可以检查频率是否太低导致电机发热严重。如果你发现LED有轻微闪烁你可以提高频率也可以检查占空比是否太低导致平均电流太小。PWM控制的是一个二维空间时间维度上的频率和幅度维度上的占空比。3. 核心参数计算从需求反推配置3.1 占空比的计算与分辨率选择占空比的计算看起来很简单占空比 高电平时间 / 周期时间 × 100%。但在实际配置时你需要考虑定时器的分辨率。假设你用一个16位定时器自动重装载值设为1000那么占空比的分辨率就是1/1000也就是0.1%。如果你需要更精细的调光比如0.01%的分辨率那自动重装载值至少要设为10000。但分辨率不是越高越好。分辨率越高意味着定时器的计数频率越高或者自动重装载值越大。如果自动重装载值太大而PWM频率又要求比较高那定时器的时钟频率就必须非常高这对晶振和定时器本身都是压力。所以选择分辨率时要问自己一个问题我的应用真的需要这么细的调节吗对于LED调光人眼对亮度的感知是对数性的0.1%的分辨率已经足够。对于电机调速1%的分辨率通常就够用了。对于舵机控制占空比的范围是2.5%到12.5%对应0度到180度如果分辨率是0.1%那角度分辨率就是1.8度对于大多数舵机应用来说已经足够。3.2 频率的计算与负载匹配频率的计算公式是PWM频率 定时器时钟频率 / (预分频系数 × 自动重装载值)。假设你的单片机主频是72MHz预分频系数设为72自动重装载值设为1000那么PWM频率就是72MHz / (72 × 1000) 1kHz。选择频率时要考虑三个因素负载的响应特性、驱动电路的开关能力、以及是否会产生可听噪声或可见闪烁。对于直流电机频率太低会导致电机抖动和噪声频率太高会导致驱动管发热。一个实用的经验法则是频率至少是电机机械时间常数的10倍以上。大多数小型直流电机的机械时间常数在10ms到100ms之间所以频率选在10kHz到20kHz是比较稳妥的。对于LED频率太低会闪烁频率太高会导致LED的寄生电容充放电不充分反而降低效率。通常1kHz到5kHz是甜点区。对于舵机频率必须是50Hz因为舵机内部的基准电路就是按20ms周期设计的你改频率舵机就会疯掉。3.3 死区时间的计算与设置如果你用PWM驱动的是半桥或全桥电路比如H桥电机驱动那死区时间是必须考虑的。死区时间是为了防止上下桥臂同时导通造成短路。计算死区时间时要考虑MOS管的开关时间和驱动电路的延迟。一般来说死区时间在几百纳秒到几微秒之间。死区时间太短上下桥臂可能直通烧毁MOS管死区时间太长输出波形失真电机效率下降。一个实用的方法是先查MOS管数据手册中的开通延迟和关断延迟取两者中的较大值再乘以1.5到2倍的安全系数。比如MOS管开通延迟100ns关断延迟80ns那死区时间至少设150ns稳妥一点设200ns。4. 实操配置从寄存器到代码4.1 定时器配置的完整流程以常见的STM32定时器为例配置PWM输出的步骤大致如下。首先使能定时器时钟和GPIO时钟这一步很多人会忘结果代码烧进去没反应查半天才发现是时钟没开。然后配置GPIO为复用推挽输出注意要选对复用功能编号不同定时器通道对应的复用功能可能不同。接下来配置定时器的时基单元预分频系数、自动重装载值、计数模式。预分频系数决定了计数频率自动重装载值决定了周期。然后配置PWM输出通道PWM模式选择、输出极性、使能输出。最后使能定时器PWM就开始输出了。这里有一个容易踩的坑自动重装载寄存器的值是在更新事件时才生效的。如果你在运行中修改自动重装载值新的值不会立即生效要等到下一个更新事件。如果你需要立即生效可以手动触发一次更新事件但要注意这会重置计数器可能导致波形出现一个异常周期。4.2 占空比动态调整的代码实现动态调整占空比是PWM应用中最常见的操作。在STM32中你只需要修改捕获比较寄存器的值即可。但要注意捕获比较寄存器的值不能大于自动重装载值否则输出会一直是高电平或低电平具体取决于PWM模式。如果你用的是HAL库可以直接调用__HAL_TIM_SET_COMPARE宏。如果你直接操作寄存器就是写TIMx-CCRy寄存器。修改占空比时最好在更新事件之后进行这样可以避免波形出现毛刺。如果对波形质量要求很高可以使用预装载功能让新的占空比值在下一个周期生效。对于需要平滑调节的应用比如呼吸灯你可以用一个循环逐步改变占空比每次改变后延时一段时间。延时的长短决定了呼吸的快慢。但要注意如果延时太短人眼会感觉亮度变化太快如果延时太长又会感觉卡顿。通常每次改变1%的占空比延时10ms到20ms效果比较自然。4.3 多通道PWM的同步与相位控制很多应用需要多路PWM同步输出比如三相电机驱动、RGB LED调色、多路舵机控制。这时候就需要考虑通道之间的同步和相位关系。对于同一个定时器的不同通道它们共享同一个计数器所以频率是相同的但占空比可以独立设置。如果你需要相位差可以通过设置不同的捕获比较值来实现但相位差是固定的不能动态调整。如果你需要更灵活的相位控制可以使用多个定时器并通过主从模式同步。一个定时器作为主定时器输出触发信号其他定时器作为从定时器接收触发信号并启动计数。这样你可以让多个定时器的PWM输出保持严格的相位关系。对于RGB LED调色通常三路PWM频率相同占空比分别控制红绿蓝的亮度。这里有一个细节三路PWM的占空比更新最好同时生效否则在颜色切换时会出现短暂的混色错误。你可以使用定时器的更新事件来同步更新所有通道的捕获比较寄存器。5. 常见问题与排查技巧实录5.1 PWM无输出或输出异常这是最常见的问题排查思路可以按以下顺序进行。首先检查时钟是否使能包括定时器时钟和GPIO时钟。很多人只记得开定时器时钟忘了开GPIO时钟结果引脚一直是高阻态。其次检查GPIO配置是否正确复用功能编号是否选对输出模式是否设为推挽。如果用的是开漏输出没有上拉电阻的话高电平根本出不来。然后检查定时器的配置预分频系数和自动重装载值是否合理计数模式是否正确PWM模式是否选对。如果PWM模式选成了“边沿对齐”但极性设反了输出波形就会反相。最后检查捕获比较寄存器的值是否在有效范围内如果大于自动重装载值输出会一直不变。还有一个隐蔽的坑有些定时器的通道需要显式使能输出。比如STM32的高级定时器需要设置TIMx-BDTR寄存器的MOE位否则PWM输出被强制关闭。这个位在标准库中由TIM_CtrlPWMOutputs函数设置在HAL库中由HAL_TIM_PWM_Start函数内部处理但如果你直接操作寄存器就很容易漏掉。5.2 电机发热严重或噪声大电机发热严重通常是因为PWM频率太低导致电机电流纹波太大铜损和铁损增加。你可以用示波器观察电机电流波形如果电流波动幅度超过平均电流的20%就说明频率太低了。提高频率可以减小电流纹波但也会增加驱动管的开关损耗所以要找一个平衡点。电机噪声大可能是频率落在了人耳听觉范围内通常是20Hz到20kHz。你可以把频率提高到20kHz以上噪声就会消失。但要注意频率提高后驱动管的开关损耗会增加如果驱动管没有散热片可能会过热。另外频率太高时电机绕组的寄生电容会导致漏电流增加反而降低效率。还有一个可能的原因是死区时间设置不当。死区时间太长会导致输出波形失真产生低次谐波引起电机振动和噪声。你可以用示波器观察电机两端的电压波形如果发现波形有明显的平顶或缺口就说明死区时间太长了。5.3 LED调光非线性或闪烁LED调光非线性通常是因为人眼对亮度的感知是对数性的而PWM占空比是线性的。如果你按线性方式调节占空比人眼会觉得低亮度区变化太快高亮度区变化太慢。解决方法是在占空比和亮度之间做一个对数映射或者使用伽马校正表。很多LED驱动芯片内部就集成了伽马校正你只需要输入亮度值芯片会自动转换成占空比。LED闪烁可能是频率太低也可能是占空比太低导致平均电流太小LED处于非线性工作区。你可以提高频率到1kHz以上同时保证最小占空比不低于1%。如果还有闪烁检查电源是否稳定PWM输出是否受到了其他信号的干扰。还有一个容易被忽略的原因PWM输出和ADC采样之间的干扰。如果你在用ADC采集其他信号而PWM频率和ADC采样频率有某种倍数关系可能会产生拍频干扰导致LED看起来在闪烁。解决方法是让PWM频率和ADC采样频率错开或者使用硬件触发ADC采样让采样时刻避开PWM开关瞬间。5.4 舵机抖动或角度不准舵机抖动通常是因为PWM信号不稳定或频率不对。舵机要求50Hz的频率周期20ms高电平时间在0.5ms到2.5ms之间对应0度到180度。如果你的频率偏离50Hz太多舵机内部的基准电路就无法正常工作会导致抖动或角度不准。另一个常见原因是电源功率不足。舵机在转动瞬间电流很大可能达到1A甚至更高。如果电源功率不够电压会被拉低导致舵机复位或抖动。你可以在舵机电源端并联一个大电容比如1000uF来提供瞬态电流。还有一个细节舵机的控制信号线要远离电机电源线否则电机的PWM噪声会耦合到信号线上导致舵机抖动。如果无法远离可以使用屏蔽线或者在信号线上加一个低通滤波器。6. 进阶话题PWM在非典型场景中的应用6.1 用PWM实现DAC输出PWM不仅可以控制功率还可以用来产生模拟电压。原理很简单PWM方波经过一个低通滤波器高频成分被滤除剩下的就是平均电压。这个平均电压与占空比成正比所以你可以通过改变占空比来产生不同的模拟电压。但这里有几个坑。首先低通滤波器的截止频率必须远低于PWM频率否则纹波会很大。通常截止频率取PWM频率的1/10到1/100。其次PWM的占空比分辨率决定了DAC的分辨率。如果你需要12位DAC那PWM的自动重装载值至少要设为4096。最后PWM输出的高电平电压必须稳定如果高电平电压本身有波动DAC输出也会波动。用PWM做DAC的优点是成本低几乎任何单片机都有PWM。缺点是响应速度慢因为低通滤波器需要时间建立。如果你需要快速变化的模拟电压还是用专用DAC芯片比较好。6.2 用PWM控制加热器温度PWM控制加热器时频率不能太高因为加热器的热时间常数很大通常几秒到几分钟。如果PWM频率太高加热器的温度波动很小但驱动电路的开关损耗会很大。通常PWM频率选在0.1Hz到10Hz之间也就是周期100ms到10s。控制加热器时占空比对应的是平均功率。但加热器的电阻会随温度变化所以同样的占空比在不同温度下产生的功率不同。如果你需要精确控温最好用PID算法根据温度反馈动态调整占空比。还有一个安全问题加热器断电后还有余温如果你用PWM控制加热器在占空比为0时加热器仍然可能很热。所以在做安全设计时要考虑余温的影响必要时增加强制散热或温度保险丝。6.3 用PWM驱动蜂鸣器产生音乐蜂鸣器分为有源和无源两种。有源蜂鸣器内部有振荡电路只要通电就响频率固定。无源蜂鸣器需要外部提供PWM信号频率决定音调。所以如果你想用蜂鸣器播放音乐必须用无源蜂鸣器通过改变PWM频率来改变音调。播放音乐时你需要知道每个音符对应的频率。比如中音C是262Hz中音D是294Hz等等。然后你用一个定时器产生对应频率的PWM占空比设为50%蜂鸣器就会发出那个音调。音符的持续时间由延时决定。这里有一个技巧占空比不一定是50%最好听。有些蜂鸣器在特定占空比下声音更响你可以通过实验找到最佳占空比。另外播放音乐时要注意节奏每个音符之间要有短暂的停顿否则听起来会连成一片。7. 我个人在实际操作中的体会调了这么多年PWM我最大的体会是不要一上来就写代码先想清楚你要控制什么。如果你要控制的是亮度那频率和占空比哪个更重要如果你要控制的是速度那电流和电压哪个是限制因素如果你要控制的是位置那反馈和开环哪个更可靠这些问题想清楚了PWM的配置就是水到渠成的事。另一个体会是示波器是最好的老师。很多问题你看代码看不出来但用示波器一看波形就明白了。比如你以为占空比是50%结果示波器显示是49.8%那可能是定时器的时钟源有偏差。比如你以为频率是1kHz结果示波器显示是999Hz那可能是预分频系数取整导致的。这些细节在代码里看不出来但在波形上一目了然。最后分享一个小技巧如果你需要精确的PWM频率不要用预分频系数和自动重装载值的组合去凑而是先用示波器测量实际频率然后反推预分频系数和自动重装载值。因为定时器的时钟源本身可能有误差晶振的精度、PLL的抖动都会影响最终频率。实测反推比理论计算更可靠。这个内容后续还可以这样扩展如果你对PWM的谐波分析感兴趣可以用FFT分析PWM波形的频谱看看开关频率和各次谐波的分布。如果你对PWM的电磁兼容性感兴趣可以研究如何通过随机化PWM频率来降低EMI。如果你对PWM在电力电子中的应用感兴趣可以研究空间矢量PWM和正弦PWM的区别。每一个方向都够写一篇长文但核心始终是那个问题PWM到底控制了什么想清楚这个问题剩下的都是技术细节。