资讯动态

STM32 PWM从LED调光到电机调速:原理、配置与避坑指南

发布时间:2026/10/27 1:03:12 来源:尧图企业网站定制
1. 从一个现象说起为什么调亮度、调转速都绕不开 PWM如果你玩过单片机大概率第一个实验就是点亮 LED第二个实验就是让 LED 闪起来第三个实验十有八九就是让 LED「呼吸」——亮度慢慢变亮再慢慢变暗。再往后你买了个直流电机想让它转慢一点结果发现直接给它降电压转速是下来了但扭矩也跟着塌了电机还发出「嗡嗡」的怪声。这时候老玩家通常会甩给你一句话用 PWM 啊。PWM 这三个字母全称是 Pulse Width Modulation中文叫脉冲宽度调制。听起来很唬人但它的本质朴素得不行就是让一个开关以极快的速度不停地开、关、开、关然后通过调整「开」的时间占整个周期的比例来等效地控制输出到负载上的平均能量。这个「开的时间占比」就是占空比Duty Cycle。为什么这招能调速、能调光核心在于负载的「惯性」。LED 的惯性是余辉和人眼的视觉暂留电机的惯性是转子的机械惯性和绕组的电感。只要开关频率足够高高到负载「反应不过来」它感受到的就是一个被平均过的效果。你给它 50% 的时间通电它就以为自己一直只收到一半的能量。这就是 PWM 能调速、能调光的物理底层逻辑。这篇文章我打算把这条线彻底讲透从最基础的 LED 亮度变化到直流电机真正转起来、转得稳中间涉及 STM32 的定时器配置、GPIO 复用、占空比计算、频率选择、驱动电路设计以及一堆我实际踩过的坑。适合刚上手 STM32 想搞明白 PWM 到底怎么回事的朋友也适合做过实验但没深究过「为什么这么配」的同行。看完你至少能做到拿到一个 STM32 板子知道 PA8 上怎么输出一路 PWM知道频率和占空比该怎么算知道为什么电机不能直接挂 IO 口上。2. PWM 的核心原理拆解平均能量与负载惯性2.1 占空比、频率、周期三个参数到底谁管什么先把三个最容易混淆的概念钉死。周期Period是一个完整开关循环的时间长度频率Frequency是周期的倒数占空比Duty Cycle是高电平时间占整个周期的百分比。假设定时器周期是 1000 个计数比较值是 300那占空比就是 30%。这三个参数里频率决定「负载能不能被忽悠住」占空比决定「平均能量给多少」。很多人第一次配 PWM 会犯一个错以为改频率就能改亮度。其实改频率只影响闪烁感和噪声真正决定亮度的是占空比。你把频率从 1kHz 调到 20kHz占空比不变LED 亮度基本不变只是从可能听得到啸叫变成听不到了。这个区分非常关键后面调电机的时候会反复用到。那频率到底选多少这取决于负载。LED 调光人眼视觉暂留大概在几十赫兹以上就感觉不到闪了但为了保险和避免摄像设备拍到条纹一般选 1kHz 以上常见 1k~5kHz。直流电机则要考虑两个矛盾频率太低电机会听到「哒哒」的脉动声电流纹波大频率太高MOS 管的开关损耗上去了驱动器发热。工程上直流有刷电机常用 10k~20kHz刚好在人耳听觉上限附近既听不到啸叫开关损耗也能接受。2.2 为什么是「平均」而不是「真的降压」这里必须讲清楚一个常见误解。有人会问我用 PWM 调光LED 两端的电压明明还是 3.3V 满幅凭什么说亮度降了答案在于能量是时间的积分。LED 在导通的那段时间里电流确实是满的但它只导通了一部分时间。人眼和光电传感器响应的是光通量对时间的积分也就是平均功率。50% 占空比下虽然瞬时功率是满的但平均功率只有一半所以看起来就是一半亮度。电机同理但更微妙。电机绕组是电感电感有个特性叫「阻碍电流突变」。当你用 PWM 快速开关时绕组里的电流不会跟着电压瞬间跳变而是被电感「抹平」成一个近似直流的平均值。这个平均电流产生的平均转矩就决定了电机的转速。所以 PWM 调速的本质是用开关的方式让电机绕组「以为」自己接到了一个可调的低压直流源。注意PWM 调速对直流有刷电机有效是因为电感和机械惯性起了滤波作用。如果你拿 PWM 去驱动一个纯电阻负载比如白炽灯它感受到的也是平均功率但如果你去驱动某些对瞬时电压敏感的器件就必须加滤波否则会出问题。2.3 从 LED 到电机同一个原理不同的边界条件LED 和电机虽然都用 PWM但边界条件差别很大这也是为什么很多人 LED 调光做得好好的一换电机就翻车。LED 是电流型器件它关心的是流过它的电流而不是电压。PWM 直接驱动小功率 LED 时串一个限流电阻就行因为 IO 口的驱动能力通常 20mA 以内刚好够。但电机是功率器件启动电流可能是额定电流的 5~10 倍STM32 的 IO 口最大也就几十毫安直接接电机轻则 IO 口烧毁重则芯片报废。所以从 LED 到电机中间必须加一级「功率放大」也就是驱动电路。常见方案是 MOS 管或者专用电机驱动芯片。PWM 信号只负责「指挥」真正的「力气活」交给 MOS 管去干。这个分工思想是理解整个 PWM 驱动体系的关键。3. STM32 上怎么把 PWM 输出出来从 PA8 到定时器3.1 为什么 PWM 一定要挂在定时器上新手最容易困惑的一点我能不能用 GPIO 加个延时函数手动翻转电平来产生 PWM理论上可以这叫「软件 PWM」。但实际项目里几乎没人这么干原因有三个。第一软件 PWM 占 CPU。你让 CPU 一直忙着翻转 IO它就没法干别的事了。第二精度差。延时函数受中断、其他任务影响抖动很大占空比不稳。第三频率上不去。想要 20kHz意味着每 25 微秒就要翻转一次软件根本忙不过来。硬件 PWM 就不一样了。STM32 内部有专门的定时器外设它自己有一个计数器在跑还有一个比较寄存器。计数器从 0 数到自动重装载值ARR数到和比较值CCR相等时硬件自动翻转输出电平。整个过程 CPU 完全不参与你只要把 ARR 和 CCR 配好波形就稳定地出来了。这就是为什么 PWM 必须用定时器。STM32 的定时器分好几类高级定时器TIM1、TIM8、通用定时器TIM2~TIM5、基本定时器TIM6、TIM7。基本定时器没有输出比较通道出不了 PWM。高级和通用定时器才有 PWM 输出能力。PA8 这个引脚在 STM32F103 上对应的是 TIM1_CH1也就是高级定时器的通道 1。3.2 PA8 输出 PWM 的完整配置思路为什么热词里「pa8输出pwm」被反复搜因为 PA8 是很多入门板子上默认引出的引脚而且它挂的是 TIM1功能全。下面我把配置逻辑讲清楚你照着这个思路换任何引脚都能推出来。第一步确定引脚和定时器的对应关系。这个必须查芯片的「引脚复用表」或者数据手册的「Alternate Function」章节。PA8 对应 TIM1_CH1这是固定的不能随便改。选错引脚PWM 永远出不来。第二步使能时钟。STM32 的外设默认是关着的省电。你要用 GPIOA 和 TIM1就得先把这两个时钟打开。这一步新手最常忘忘了就是「代码没错但没波形」。第三步配置 GPIO 为复用推挽输出。注意是「复用」不是普通输出。复用模式下引脚的控制权交给定时器外设而不是普通的输出数据寄存器。模式选推挽速度选高一点比如 50MHz保证边沿够陡。第四步配置定时器的时基。这里要算两个值预分频器PSC和自动重装载值ARR。它们共同决定 PWM 频率。公式是PWM 频率 定时器时钟 / ((PSC1) × (ARR1))。第五步配置输出比较通道。设置 PWM 模式模式 1 还是模式 2、占空比CCR、输出极性、使能输出。第六步使能定时器。到这一步波形才真正出来。3.3 频率和占空比的参数计算实例光说公式太干直接上例子。假设 STM32F103 主频 72MHzTIM1 挂在 APB2 上时钟也是 72MHz。我想输出 1kHz 的 PWM占空比可调。先定频率1kHz 意味着周期 1ms。定时器时钟 72MHz一个计数是 1/72 微秒。要凑够 1ms需要 72000 个计数。我可以让 PSC71这样计数频率变成 72MHz/721MHz一个计数 1 微秒。然后 ARR999总共 1000 个计数正好 1ms频率 1kHz。占空比怎么设如果我要 30%CCR (ARR1) × 30% 1000 × 0.3 300。要 50%CCR500。要 100%CCR1000注意有些配置下 100% 需要 CCRARR。如果我想改成 20kHz 驱动电机呢周期变成 50 微秒。保持 PSC711MHz 计数ARR 就要设成 49总共 50 个计数50 微秒20kHz。这时候占空比的分辨率就只有 50 级了50% 就是 CCR25。分辨率变粗了这是高频的代价。实操心得分辨率和频率是一对矛盾。ARR 越大占空比能调的档位越多但频率越低。做 LED 呼吸灯频率 1kHz 够用ARR 可以给到 1000 甚至更大呼吸才顺滑。做电机驱动20kHz 下 ARR 只有几十调速档位少但电机本来也不需要那么细。4. 从 LED 到直流电机驱动电路才是分水岭4.1 LED 直驱为什么可以电机直驱为什么不行先说 LED。小功率 LED比如 5mm 的指示灯正向压降大概 2V工作电流 5~20mA。STM32 的 IO 口在推挽输出下单个引脚能拉/灌 20mA 左右短时间峰值能到 25mA。所以串一个限流电阻直接挂在 PA8 上完全没问题。电阻怎么算假设 LED 压降 2VIO 输出 3.3V想要 10mA电阻 (3.3-2)/0.01 130 欧姆取标准值 150 欧姆就行。但这里有个热词提到的坑「51 单片机驱动 LED 时为什么不能采用输出高电平的驱动方式」。这个问题的本质是「灌电流」和「拉电流」的能力差异。很多老单片机包括 51 和部分 STM32 的某些模式灌电流引脚拉低电流从外部流入引脚能力比拉电流引脚拉高电流从引脚流出强。所以接 LED 时常见接法是 LED 正极接 VCC负极接 IO 口IO 输出低电平时 LED 亮。这样用的是灌电流驱动能力更足。STM32 的推挽输出拉灌能力对称两种接法都行但养成「低电平点亮」的习惯没坏处。电机就完全是另一回事了。一个普通的 130 小电机额定电流可能 200mA堵转电流能到 1A 以上。你把它直接挂 IO 口上等于让一个只能扛 20mA 的引脚去扛 1A结果就是引脚烧毁甚至芯片内部走线熔断。所以电机必须经过驱动级。4.2 MOS 管驱动方案与续流二极管最基础的电机驱动方案是「一个 N 沟道 MOS 管 一个续流二极管」。PWM 信号接到 MOS 管的栅极MOS 管的漏极接电机负极电机正极接电源正极源极接地。PWM 高电平时 MOS 管导通电机通电低电平时 MOS 管关断电机断电。但这里有个致命问题电机是感性负载断电瞬间绕组里的电流不能突变会产生一个反向的高压尖峰叫反电动势。这个尖峰电压可能高达电源电压的好几倍足以击穿 MOS 管。所以必须在电机两端反向并联一个续流二极管也叫飞轮二极管给这个反向电流提供一条泄放通路。MOS 管怎么选关键看三个参数耐压Vds、导通电阻Rds(on)、栅极阈值电压Vgs(th)。耐压要大于电源电压的 2 倍以上比如 12V 供电选 30V 以上的。导通电阻越小发热越少但一般也越贵。栅极阈值电压要低因为 STM32 的 IO 高电平只有 3.3V如果 MOS 管的阈值电压是 4V那 3.3V 根本打不开它电机就不转。所以要选「逻辑电平」MOS 管阈值电压在 2V 以下。注意如果你用 5V 单片机IO 高电平 5V选 MOS 管宽松一些。但 STM32 是 3.3V 系统选 MOS 管一定要看 Vgs(th) 和「3.3V 下的导通电阻」这两个参数很多 MOS 管标称的 Rds(on) 是在 10V 栅压下测的3.3V 下会大很多。4.3 专用驱动芯片省心但要看懂引脚如果不想自己搭 MOS 管电路可以用专用电机驱动芯片比如常见的 H 桥驱动芯片。这类芯片内部集成了 MOS 管、续流二极管、甚至保护电路你只要给它 PWM 信号和方向信号它就能驱动电机正反转和调速。用驱动芯片的好处是省心坏处是要看懂它的引脚定义和逻辑真值表。一般这类芯片有 VCC逻辑电源、VM电机电源、IN1/IN2方向控制、PWM调速输入、OUT1/OUT2接电机这些引脚。有些芯片的 PWM 是直接控制使能的有些是通过 IN1/IN2 的组合来控制。用之前一定要把真值表看明白否则会出现「给了 PWM 但电机不转」或者「电机只会全速转」的情况。热词里提到的「led驱动器芯片 fb cs 脚调整」其实和电机驱动是同一类思路。LED 驱动芯片的 FB反馈脚和 CS电流采样脚本质上也是通过反馈调节来实现恒流或调光。PWM 调光信号进去芯片内部根据占空比调整输出电流的平均值。原理相通只是应用场景不同。5. 实操过程一步步让电机转起来5.1 硬件连接清单与检查顺序动手之前先把硬件连接理清楚。我按信号流向列一遍STM32 的 PA8TIM1_CH1接驱动电路的 PWM 输入。驱动电路的电源输入接电机电源比如 12V逻辑电源接 3.3V 或 5V。驱动电路的输出接电机两端。共地。这一点极其重要STM32 的地、驱动芯片的地、电机电源的地必须连在一起否则 PWM 信号没有参考电平驱动芯片识别不了。检查顺序我建议从后往前先确认电机电源正常再确认驱动芯片逻辑电源正常再确认 PWM 信号有波形最后接电机。这样万一出问题能快速定位是哪一级。5.2 定时器配置代码逐段解析下面用 STM32 标准外设库的风格写一段 PA8 输出 PWM 的配置代码逐段解释。用 HAL 库的朋友逻辑一样只是函数名不同。// 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE); // 2. 配置 PA8 为复用推挽输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 3. 配置定时器时基 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period 999; // ARR TIM_TimeBaseStructure.TIM_Prescaler 71; // PSC TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // 4. 配置输出比较通道 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; // PWM 模式 1 TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 300; // CCR30% 占空比 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM1, TIM_OCInitStructure); // 5. 使能预装载改 CCR 时波形不抖 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); // 6. 高级定时器必须使能主输出否则没波形 TIM_CtrlPWMOutputs(TIM1, ENABLE); // 7. 启动定时器 TIM_Cmd(TIM1, ENABLE);这段代码里有两个点新手最容易漏。第一个是第 6 步TIM1 是高级定时器它有一个「刹车」机制默认主输出是关的你不调用TIM_CtrlPWMOutputs使能通道配得再对也没波形。通用定时器TIM2~TIM5没这个问题。第二个是第 5 步的预装载不使能的话你在运行中改 CCR可能会在某个周期出现一个异常宽的脉冲电机就会抖一下。5.3 占空比动态调整与实测波形配置好之后怎么动态调速最简单的办法就是直接改 CCR 寄存器TIM_SetCompare1(TIM1, new_value);。new_value 的范围是 0 到 ARR1。0 就是全关ARR1 就是全开。实测的时候如果你有示波器直接探头接 PA8 和地应该能看到一个 1kHz、30% 占空比的方波。没有示波器怎么办可以接一个 LED 串电阻到地看亮度。30% 占空比下LED 应该明显比全亮暗但不闪。如果你看到 LED 在闪说明频率太低了把 ARR 调小或者 PSC 调小把频率提上去。调电机的时候我建议先用一个固定的中等占空比比如 50%测试电机能不能转。能转之后再写个循环让占空比从 0 慢慢加到 100观察电机转速变化。正常情况应该是转速随占空比单调上升。如果出现「低占空比不转突然某个点猛转」说明驱动电路的启动死区比较大或者 MOS 管阈值电压偏高3.3V 驱动不充分。实操心得调电机时占空比不要从 0 开始一点点加因为电机有静摩擦太低占空比根本转不起来你会以为电路坏了。直接从 30% 或 40% 开始试转起来之后再往下调找到能稳定转动的最低占空比这个值对理解你的驱动电路很有帮助。6. 常见问题与排查技巧实录6.1 PWM 没波形、波形不对的排查表PWM 调试遇到的问题八成集中在下面这几类。我整理成表方便对照排查。现象可能原因排查方法完全没波形时钟没使能检查 RCC 使能代码完全没波形高级定时器主输出没开检查 TIM_CtrlPWMOutputs完全没波形引脚复用配错查数据手册确认 AF 映射有波形但频率不对PSC/ARR 算错重新按公式核算有波形但占空比不对CCR 超范围确认 CCR ≤ ARR1波形抖动预装载没开使能 OCxPreload电机不转但波形正常驱动电路问题查 MOS 管阈值、共地、电源电机转但发热严重频率太低或 MOS 导通电阻大提高频率、换低 Rds(on) 的管子6.2 电机干扰导致显示异常的实战处理热词里有个「pwm背光 干扰 显示」这个坑我踩过。现象是LCD 背光用 PWM 调光结果屏幕出现横向条纹或者闪烁。原因通常是 PWM 的频率和 LCD 的刷新率产生了拍频或者 PWM 的开关噪声通过电源耦合到了显示电路。解决办法有几个方向。第一把 PWM 频率调开避开和刷新率接近的频段比如从 1kHz 调到 2kHz 或者 5kHz。第二在背光 LED 的电源上加滤波电容把开关噪声滤掉。第三PWM 走线远离显示信号线减少耦合。第四如果背光驱动是恒流芯片检查它的调光频率是否和显示刷新有冲突。这个问题的本质是「开关噪声的传导和辐射」。PWM 本身就是一个噪声源频率越高、边沿越陡噪声频谱越宽。所以在敏感系统里PWM 的频率选择和布线布局和电路功能本身一样重要。6.3 那些文档里不会写的避坑经验第一条共地是玄学问题的头号元凶。我见过太多次「波形正常但电机不转」最后发现是 STM32 和驱动板没共地。PWM 信号是相对地的高电平没有共同的地参考驱动芯片根本不知道你的高电平是多少伏。第二条MOS 管的栅极电阻不能省。直接在 IO 和栅极之间连线开关瞬间的栅极电容充电电流可能很大会干扰 MCU。串一个 10~100 欧姆的电阻能明显改善。这个电阻还能抑制栅极振荡。第三条电机电源和逻辑电源要分开。电机启停时电流变化剧烈会在电源线上产生很大的纹波。如果逻辑电源和电机电源共用MCU 可能被拉复位。正确做法是两路电源或者至少在电机电源入口加大电容缓冲。第四条占空比 100% 不一定等于全速。有些驱动芯片在 100% 占空比时反而会进入异常状态因为自举电容没有充电时间。用 H 桥驱动时如果发现 100% 占空比电机反而变慢就是这个原因。解决办法是限制最大占空比在 95%~98%。第五条调试时先断开电机用示波器或 LED 确认 PWM 正常再接电机。这个顺序能帮你把「控制问题」和「功率问题」分开排查效率高很多。7. 关于 PWM 模式 1 和模式 2以及 GPIO 模式的补充7.1 PWM 模式 1 和模式 2 到底差在哪热词里有人搜「pwm模式1和模式2的区别」这个确实容易绕。简单说PWM 模式 1 是「计数器小于 CCR 时输出有效电平」模式 2 是「计数器小于 CCR 时输出无效电平」。所谓有效电平又取决于你设的极性。实际用起来模式 1 配高极性就是「计数值小于 CCR 时输出高」。模式 2 配高极性就是「计数值小于 CCR 时输出低」。两者波形正好反相。什么时候用哪个取决于你的驱动电路是「高电平有效」还是「低电平有效」。比如你的 MOS 管是低电平导通P 沟道那就用模式 2让低电平时间等于你想要的导通时间。很多人纠结这个其实记住一句话就行模式 1 和模式 2 只是把输出反了个相配合极性设置能凑出四种组合总有一种适合你的驱动电路。不用死记拿示波器看一眼不对就换。7.2 GPIO 八种工作模式和 PWM 的关系热词里「gpio的8种工作模式」也是高频问题。STM32 的 GPIO 有输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用这八种。PWM 输出用的是「复用推挽」或者「复用开漏」。为什么是复用因为普通输出模式下引脚电平由 GPIO 的输出数据寄存器控制定时器管不着。只有复用模式才把引脚的控制权交给定时器这个「复用功能」。推挽还是开漏推挽能主动输出高和低驱动能力强适合大多数场合。开漏只能主动拉低高电平靠外部上拉适合需要电平转换或者「线与」的场合。PWM 驱动 MOS 管一般用复用推挽。选错模式是 PWM 出不来的常见原因之一。如果你配成了普通推挽输出然后发现改 CCR 没反应八成就是这个。7.3 不同芯片平台的 PWM 配置差异热词里出现了 STM32、GD32、HC32F460、51 单片机说明大家用的平台很杂。这里说下差异。STM32 和 GD32 的定时器外设高度相似很多代码能直接移植但时钟树和库函数有差别移植时要注意时钟频率。HC32F460 是另一家的芯片定时器叫法不同但 PWM 的基本逻辑时基比较输出是一样的看懂一个平台换平台只是查手册的事。51 单片机没有硬件 PWM标准 8051只能软件模拟所以频率和精度都受限。这也是为什么 51 做电机调速效果往往不如 STM32。不是 51 不行是它没有专门的硬件外设。8. 写在最后的一点个人体会PWM 这个东西原理一句话就能说完但真正用好靠的是对「负载特性」和「边界条件」的理解。LED 和电机用的是同一个 PWM 原理但一个能直驱一个必须加驱动级差别不在 PWM 本身而在负载的功率等级和电气特性。我自己调电机的时候最开始也犯过「直接拿 IO 口接电机」的错结果烧了一个引脚心疼了好几天。后来才明白PWM 只是「指挥信号」真正干活的永远是驱动电路。把这两层分清楚很多问题就迎刃而解了。如果你现在正卡在某个 PWM 调试问题上我的建议是先别急着改代码拿示波器或者万用表从 MCU 引脚一路往后量看信号在哪一级丢的。九成的 PWM 问题都能靠这个笨办法定位出来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑