PWM 是怎么"调"出来的
单片机的输出引脚只有高和低两个电平,非 0 即 1,中间没有档位。可你偏偏要一盏"半亮的灯"、一个"半速的风扇"、一个"转到 60 度的舵机"。这些都是中间值,引脚给不了。PWM 就是绕过这个限制的招法——它不去憋出一个中间电压,而是用极快的开关比例,让负载"感觉"到中间值。搞懂它,调光、调速、控舵机、甚至凑合当个 DAC,都是同一套原理的变体。
核心:不给中间电压,给中间"比例"
PWM = 脉宽调制(Pulse Width Modulation)。它的做法是把引脚极快地开、关、开、关……你眼睛看不出它在闪,但真正决定效果的,是一个周期里"开"占了多大比例——这个比例叫占空比(Duty Cycle)。
- 占空比 100% = 全程开 = 灯全亮、电机全速
- 占空比 50% = 一半时间开 = 灯约半亮、电机约半速
- 占空比 10% = 大部分时间关 = 灯很暗、电机慢慢转
为什么骗得过去?因为开关频率足够高(几百到几万次每秒),LED 的余辉、电机转子的惯性、你的视觉暂留,都来不及跟上单次的开与关,只能对一整段时间"求平均"。于是灯的实际亮度、电机的实际转速,就正比于占空比。引脚始终在满幅跳变,中间值是"平均"出来的,不是真的输出了 1.65V。 这一点想清楚,后面很多坑就自然通了。
三要素:频率、占空比、分辨率
一路 PWM 由三个量框定,缺一个都调不准。
- 频率:每秒开关多少个完整周期,单位 Hz。它决定"闪得多快",间接决定会不会被看穿、电机会不会啸叫。
- 占空比:上面说的开占的比例,决定输出的"强度"。这是你运行时反复调的那个量。
- 分辨率:占空比能分成多少档,由位数决定。8 位 = 256 档(占空比可取 0~255),10 位 = 1024 档,13 位 = 8192 档。档越多,亮度/转速就能调得越细腻,不会一跳一大格。
前两个决定"闪多快、开多久",第三个决定"能调多细"。三者的关系不是各管各的——在 ESP-IDF 里它们会互相牵制,这是下一节的重点。
频率怎么选:看你在驱动什么
频率不是随便填的,选错了要么被看穿、要么根本不工作。
- LED 调光:给我大于 1kHz,几 kHz 更稳。低于这个数,眼睛直接就看出闪烁;更麻烦的是手机摄像头对低频 PWM 极其敏感,几百 Hz 的调光在录像里会出现一条条滚动的暗纹(频闪),做产品这会被投诉。
- 直流电机调速:常用几 kHz 到 20kHz。太低电机会发出可闻的"吱吱"啸叫(因为开关频率落在人耳听力范围内),把频率抬到 20kHz 以上超声区就安静了,代价是开关损耗略增。
- 舵机(SG90 那类):死死定在 50Hz(周期 20ms)。舵机不看占空比,看的是每个周期里高电平的绝对宽度——通常 0.5ms 对应 0 度、1.5ms 对应 90 度、2.5ms 对应 180 度。频率填错,舵机要么抖要么不动。
记住这条分界:LED 和电机看的是占空比(平均值),舵机看的是脉冲的绝对宽度。 前者可以随便调频率只要够高,后者频率是硬约束。
分辨率与频率的权衡:LEDC 的时钟account
这是新手最容易踩、又最少人讲清的一点。在 ESP-IDF 里,PWM 由 LEDC(LED Control)外设产生。它内部有一个时钟源(比如 80MHz 的 APB 时钟),你要的频率和分辨率都是从这一个时钟分出来的,于是二者是一对矛盾:
时钟频率 ≈ PWM 频率 × 2^分辨率
同一个时钟,PWM 频率越高,一个周期里能塞进去的时钟节拍就越少,能切分出的占空比档位(分辨率)就越少。反过来,你想要很高的分辨率,就得压低频率。举例:80MHz 时钟下,想要 13 位分辨率(8192 档),频率上限大约是 80M / 8192 ≈ 9.8kHz;你要是硬把频率提到 40kHz,13 位就分不出来了,LEDC 会自动把分辨率降到能满足的位数。
所以配置 LEDC 时先想清楚这一对:调光要细腻(高分辨率)就别把频率定太高,几 kHz + 13 位是常见甜点;电机要 20kHz 静音,那分辨率就得让一让,10 位左右够用。填了一组频率+分辨率初始化却报错或档位对不上,八成就是超了这个时钟上限。
它到底能控制什么
同一套开关比例,套到不同负载上就是不同的用途:
- LED 调光:直接改占空比,最经典的入门应用。
- 直流电机 / 风扇调速:占空比越大平均电压越高,转得越快。注意电机是感性负载,PWM 驱动一定要配续流二极管和合适的驱动管,不能拿引脚硬灌。
- 舵机角度:50Hz 下靠脉冲宽度定角度,如上一节。
- 伪 DAC(用 PWM 假装模拟输出):PWM 输出后面接一个 RC 低通滤波,把高频开关"抹平",就得到一个正比于占空比的近似直流电压。这就是没有真 DAC 时凑合出模拟量的常用招。它和真正的 DAC 有本质区别——真 DAC 直接输出稳定电压、纹波小;PWM 伪 DAC 靠滤波,带载能力弱、有残余纹波,只适合驱动高阻、慢变的场合(比如给个偏置电压)。要干净的模拟输出,还是用真 DAC。
呼吸灯别用 delay 硬扫
新手做呼吸灯,第一反应是写个循环:占空比从 0 加到满、再减回来,中间夹一堆 delay。能亮,但 CPU 全程被占着数数,干不了别的,还容易和其它任务打架。
ESP-IDF 的 LEDC 外设自带硬件渐变(fade):你只要告诉它"从当前占空比、用多少毫秒、渐变到目标占空比",硬件自己一档一档平滑推过去,CPU 设定完就撒手去干别的,时间到了还能回调通知你。这样呼吸灯、按键松开后灯慢慢熄灭这类效果,既平滑又不占 CPU。凡是"占空比要平滑过渡"的场景,先想有没有硬件 fade,别拿软件 delay 硬扫。
一句话口诀
PWM 不给你中间电压,给你中间比例——靠极快开关的占空比,让负载对时间求平均。记牢三要素:频率(闪多快,LED>1kHz 防频闪、电机上 20kHz 防啸叫、舵机死守 50Hz)、占空比(调强度)、分辨率(调多细)。在 ESP-IDF 的 LEDC 里,频率和分辨率共用一个时钟、此消彼长,频率越高分辨率越少,配置前先算好这一对。想要模拟电压?PWM 加 RC 滤波能凑个伪 DAC,但要干净输出还得靠真家伙。
下一步
PWM 管的是"往外输出中间值",反过来"把外面的电压读进来"是 ADC 采样原理 的活。这一切的地基是引脚本身怎么被配置成输出、能灌多大电流,去看 GPIO 原理。想要更干净的模拟输出、不靠滤波凑合,看 DAC。原理懂了就上手把一盏 LED 调到呼吸,去 L2 PWM 调光实战。