你需要让 LED 平滑调光,或让直流电机从静止加速到稳定转速?PWM(脉冲宽度调制)用只有高、低电平两种状态的数字信号,就能同时完成这两件事。读完本文,你将理解占空比如何决定平均电压与功率,以及在实际项目中如何选择频率、排查问题。
一、PWM 的核心:用时间比例表达控制量
PWM 的本质非常简单:在一个固定周期内,高电平持续的时间越长,占空比就越大。
占空比 = 高电平时间 / 整个周期 × 100%
例如周期为 1ms(频率 1kHz),高电平持续 0.25ms,占空比就是 25%。关键在于,输出端从头到尾只有高电平和低电平两种状态,并没有出现一个真正的“中间电压”。但对很多负载来说,它们感知到的恰好就是这个时间比例所对应的平均效果。
来源:playembedded.org 解释了占空比定义为 ON 时间与总时间之比;cnblogs.com 的示例代码用 20 次循环、每次 50μs 的软件延时实现了 25%、50%、75% 三档亮度。
二、LED 亮度:人眼充当生物低通滤波器
LED 本身只有亮和灭两种状态。如果直接用 GPIO 控制,它要么全亮,要么全灭。但把开关速度提到几百赫兹以上,人眼的视觉暂留效应就会自动“积分”这些快速闪烁,感知到的不是闪烁,而是由占空比决定的平均亮度。
来源:h-cled.com 指出,只要开关速度足够快(通常在几百赫兹到几千赫兹),人眼就感觉不到闪烁,只会看到亮度的连续变化;wiki.lckfb.com 补充说,一般人眼对 80Hz 以上的刷新频率完全没有闪烁感。
具体来说:
- 占空比 25% → LED 看起来偏暗
- 占空比 50% → LED 看起来中等亮度
- 占空比 75% → LED 看起来偏亮
这个关系可以用一个更底层的公式描述:
平均电压 = 占空比 × 高电平电压
来源:arduino.nxez.com 直接给出了公式“输出电压 =(接通时间/脉冲时间)× 最大电压值”;ampheo.com 用 5V 信号、25% 占空比的举例验证了这一点(等效约 1.25V);wevolver.com 则确认了 12V 高电平、0V 低电平的 PWM 波形,其平均电压随占空比线性变化。
这意味着,即使 GPIO 只输出 3.3V 或 5V,通过调节占空比,你就能让 LED 呈现从接近全暗到全亮之间的任意亮度。
三、为什么 PWM 比电阻调压效率高得多
传统的做法是在 LED 或电机回路中串联一个可调电阻,通过分压来降低负载两端的电压。但电阻会把多余的电能直接变成热量浪费掉。
PWM 的做法完全不同:开关器件要么完全导通(压降极小,损耗极低),要么完全截止(电流为零,没有损耗)。它不在中间状态浪费能量,而是通过快速开关的时间比例来等效出一个较低的平均电压。
来源:blog.csdn.net 解释了 PWM 的开关元件工作在“全开或全关”状态,减少了线性工作的损耗;electrolik-eg.com 用产品页面说明了“电机在任何瞬间要么完全开启要么完全关闭,即使在低速设置下也很少有功率以热量形式浪费”;easyelecmodule.com 则直接对比了传统串联电阻调速“将多余电能通过电阻直接转化为废热”的缺点。
这就是为什么在电机驱动、LED 调光、开关电源等领域,PWM 几乎取代了线性调压方案。
四、直流电机:电感和惯性充当积分器
直流电机的调速机制与 LED 有相似之处,但依赖的是不同的物理特性。
电机的线圈本质上是一个电感。电感的一个关键特性是阻止电流突变——当 PWM 信号从高电平跳变为低电平时,线圈中的电流不会立刻归零,而是通过续流回路逐渐衰减。与此同时,转子的机械惯性也让它不会在每次断电时立刻停下来。
来源:playembedded.org 指出,对于有刷直流电机,电机绕组和机械惯性会平滑开关动作,转速几乎与供电电压成正比;cnblogs.com 用“连续很多个周期里的能量积累成平均转矩”来描述这一过程;21ic.com 的总结最为直白——电机处于一种“似停非停、似全速转动又非全速转动”的状态,周期内的平均电压决定了转速。
结果是:占空比越高,电机在一个周期内获得的平均能量越多,转速就越快;占空比越低,平均能量越少,转速就越慢。这个关系在一定范围内是近似线性的。
五、频率选择:6kHz 到 20kHz 的工程考量
PWM 的频率直接影响两个实际体验:噪音和开关损耗。
- 频率太低(比如几百赫兹):电机的电流脉动可能被人耳听到,表现为“嗡嗡”的啸叫声;LED 也可能在特定条件下出现可见闪烁。
- 频率太高(比如超过 100kHz):开关器件每次切换都会产生损耗,频率越高损耗越大,同时电磁干扰(EMI)也会加剧。
来源:developer.quectel.com 和 21ic.com 都给出了“正常的 PWM 频率在 6–16kHz 为好”的建议;ariat-tech.com 进一步解释了低频率会导致可闻噪音、高频率会增加开关损耗和发热的权衡关系。
在实际项目中,直流电机 PWM 调速通常选择 6kHz 到 20kHz 之间的频率,这样既能把噪音推出人耳听觉范围,又不会让开关损耗过大。
六、实战:从 LED 呼吸灯到电机调速
理解原理之后,下面是一个可以动手验证的完整流程。
6.1 用 LED 验证占空比
最简单的起点是用 LED 观察亮度变化。以 Arduino 为例,analogWrite() 函数接受 0 到 255 的值,对应 0% 到 100% 的占空比:
cpp const int ledPin = 9; // 支持 PWM 的引脚
void setup() { pinMode(ledPin, OUTPUT); }
void loop() { // 从熄灭逐渐变亮 for (int duty = 0; duty <= 255; duty++) { analogWrite(ledPin, duty); delay(5); } // 从最亮逐渐熄灭 for (int duty = 255; duty >= 0; duty--) { analogWrite(ledPin, duty); delay(5); } }
来源:ampheo.com 的 Arduino LED 渐变示例直接使用了这段代码,并说明 analogWrite(ledPin, 0) 对应 0% 占空比、analogWrite(ledPin, 255) 对应满占空比。
如果你使用 STM32,硬件 PWM 的配置方式不同:定时器的预分频器(PSC)决定计数频率,自动重装载值(ARR)决定周期,捕获/比较寄存器(CCR)决定占空比。改 CCR 就是在改占空比,不需要 CPU 每次翻转 GPIO。
来源:cnblogs.com 的 STM32 示例中,系统时钟 72MHz,PSC 设为 72-1,ARR 设为 50-1,得到 20kHz 的 PWM 频率;通过 TIM_SetCompare1() 改变 CCR1 的值即可调节占空比。
6.2 接入直流电机:驱动板是必需的
注意:PWM 输出引脚不能直接连接电机。 GPIO 引脚无法提供电机所需的启动电流,也不能承受电机断电时的反冲电压。
正确的做法是通过驱动板(如 TB6612FNG 或 L298N)连接:
STM32 的 PWM 引脚 → 驱动板的逻辑输入 驱动板的功率输出 → 电机 12V 电池 → 驱动板的电机电源 降压模块 → STM32 和驱动板的逻辑电源 所有模块共地
来源:cnblogs.com 详细描述了 TB6612FNG 和 L298N 的接线方式,强调了共地的重要性以及“电机启动电流不能沿着 STM32 的细电源线绕一圈”这一实际陷阱。
6.3 用百分比控制转速和方向
在代码层面,可以用一个带符号的百分比值统一表示转速和方向:
c #define MOTOR_PWM_PERIOD (50) // ARR + 1,即一个周期内的计数总数
void Motor_SetPercent(int16_t percent) { uint16_t compare;
// 限幅保护 if (percent > 100) percent = 100; if (percent < -100) percent = -100;
// 先清零再换方向,避免转子还在转动时突然反向 TIM_SetCompare1(TIM2, 0);
if (percent >= 0) { Motor_SetDirection(1); // IN1=高,IN2=低 compare = (uint16_t)percent; } else { Motor_SetDirection(0); // IN1=低,IN2=高 compare = (uint16_t)(-percent); }
compare = (uint16_t)((compare * MOTOR_PWM_PERIOD) / 100); TIM_SetCompare1(TIM2, compare); }
来源:cnblogs.com 提供了这段 Motor_SetPercent() 实现,并特别强调了方向切换前先清零的必要性——“转子还在向前转时,突然把方向脚翻成反向,相当于让驱动器立刻施加反向力矩,齿轮、驱动芯片和电源都会收到一记很硬的冲击”。
七、验证与排查
完成接线和代码后,按以下顺序验证:
- 不接电机,先接 LED:观察 0%、20%、50%、80% 占空比下的亮度变化,确认占空比与亮度正相关。
- 接驱动板逻辑侧:确认共地,TB6612 的 STBY 或 L298N 的 ENA 处于使能状态。
- 电机架空测试:从 10% 占空比开始逐步增加,记录最低启动占空比。注意,很多电机在 1%–15% 之间可能不会立刻转动——这是静摩擦力导致的正常现象,不是故障。
- 反转测试:先降到 0%,再切换方向,不要直接从正转跳到反转。
- 频率测试:保持 50% 占空比,改变 PWM 频率,区分可闻啸叫和低占空比引起的抖动。
常见问题排查:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| LED 亮度不随占空比变化 | TIM2 时钟未使能、PA0 未配置为复用推挽、PWM 通道未映射到正确引脚 | 检查 RCC 时钟配置和 GPIO 模式 |
| 电机嗡嗡响但不转 | 占空比低于启动门槛、PWM 频率在可闻范围 | 提高占空比或频率到 6kHz 以上 |
| 同样占空比下左右轮转速不同 | 电机个体差异、驱动板压降不同、机械负载不同 | 分别测量实际转速,不要假设占空比等于转速 |
| 程序下载后电机自己转起来 | Motor_Init() 或 main() 中没有正确停机 | 确保初始化后立即调用 Motor_Stop() |
| 电机启动时 STM32 复位 | 电机启动电流拉低电源电压、没有共地 | 检查电源线径、加电容、确保共地 |
来源:cnblogs.com 的验证顺序和排查表格综合了多个实际工程经验,包括启动门槛的非线性、左右轮差异、以及初始化停机的重要性。
八、需要注意的边界
- 占空比不等于转速:PWM 命令描述的是“给多久的驱动”,不保证电机转得多快。实际转速还受负载、电源电压、驱动板压降、齿轮摩擦等因素影响。
- 低占空比可能不转:这是静摩擦力导致的正常现象。换一个电机、换一块电池、换一个驱动板,启动门槛都可能改变。
- 频率不是越高越好:过高的频率会增加开关损耗和 EMI,6–20kHz 是平衡噪音和损耗的推荐范围。
- 电机必须通过驱动板:不要尝试用 GPIO 直接驱动电机,这会损坏微控制器。
总结:PWM 用“只输出高或低,却能控制平均效果”的方式,让数字系统能够精确控制模拟负载。它不制造一个不存在的中间电压,而是把高、低电平放进固定周期里,用时间比例表达控制量。掌握了占空比、频率和负载特性这三个变量,你就能同时应对 LED 调光和直流电机调速两类典型应用。