STM32 PWM从入门到实战:LED调光与直流电机调速原理详解

发布时间:2026/10/27 18:07:33
STM32 PWM从入门到实战:LED调光与直流电机调速原理详解 1. 从一颗LED的呼吸感说起PWM到底在干什么很多人第一次接触PWM是在调LED亮度的时候。代码里写一个TIM_SetCompare1(TIM3, 500)灯就暗了一半改成900灯又亮起来。看起来像是调电压但如果你拿万用表去量LED两端的电压会发现它几乎没变——还是3.3V或者5V。那亮度为什么变了答案藏在时间维度里。PWM的全称是Pulse Width Modulation脉冲宽度调制。它不去改变电压的高低而是改变高电平持续的时间占总周期的比例。这个比例叫占空比Duty Cycle。占空比50%意思是一个周期里有一半时间输出高电平、一半时间输出低电平。对于LED这种响应速度极快的器件它根本感觉不到每一次开关人眼更感觉不到——人眼的视觉暂留大约是几十毫秒级别只要PWM频率高于100Hz左右看到的就是一个稳定的平均亮度。所以PWM调速、调光的本质是用数字信号模拟出模拟量的效果。单片机最擅长的是输出0和1最不擅长的就是输出1.65V这种中间值。PWM绕开了这个短板我只会输出0和1但我可以控制0和1的比例后端的执行器件LED、电机、加热丝自己会把这份比例积分成平均效果。这里有个容易被忽略的点PWM不是真的在调压而是后端器件在帮你调压。LED靠人眼积分直流电机靠转子的机械惯性和电感积分加热丝靠热惯性积分。如果后端器件的响应速度比PWM周期还快那PWM就失效了——比如你拿PWM去驱动一个高速响应的电磁阀它就会跟着一起哒哒哒地抖。理解这一点后面调电机、调背光、做DAC滤波的时候很多现象就顺了。关键词里出现了STM32、GPIO、直流电机、LED这几个词基本勾勒出了PWM最典型的学习路径先用GPIO点灯再用定时器输出PWM调光最后拿PWM去驱动电机。这条路我走过不止一遍也踩过不少坑下面按这个顺序把原理和实操都摊开讲。2. 定时器是怎么把占空比变成一根引脚上的方波的2.1 从GPIO翻转说起最笨但最直观的PWM在讲定时器硬件PWM之前先说清楚软件PWM因为它是理解一切的起点。假设你要在PA8上输出一个1kHz、占空比50%的方波用最原始的办法while(1) { GPIO_SetBits(GPIOA, GPIO_Pin_8); // 拉高 delay_us(500); // 高电平持续500us GPIO_ResetBits(GPIOA, GPIO_Pin_8); // 拉低 delay_us(500); // 低电平持续500us }1kHz的周期是1000us高电平500us、低电平500us占空比就是50%。想调亮度把两个delay改成300和700占空比就变成30%。这就是软件PWM逻辑上完全正确。但它的致命问题是CPU被绑死了。这个while循环里干不了别的事一旦你去做别的任务delay就不准波形就抖。而且delay_us本身受中断影响稍微有个定时器中断进来占空比就飘了。所以软件PWM只适合演示原理或者对波形要求极低的场合比如某些对时序不敏感的指示灯。2.2 硬件PWM让定时器自己数数CPU该干嘛干嘛STM32这类MCU内部有专门的定时器外设它能自己计数、自己比较、自己翻转引脚全程不需要CPU插手。核心是三个寄存器在配合ARRAuto-Reload Register自动重装载寄存器决定PWM的周期。计数器CNT从0数到ARR然后归零重新开始。CCRCapture/Compare Register捕获比较寄存器决定占空比。CNT小于CCR时输出一种电平大于等于CCR时输出另一种电平。PSCPrescaler预分频器把定时器的时钟源分频用来调频率。PWM频率的公式是Fpwm Fclk / ((PSC 1) × (ARR 1))假设STM32的定时器时钟是72MHz你想要1kHz的PWM先定PSC。如果PSC71那么分频后时钟是72MHz/721MHz也就是计数一次1us。再定ARR。要1kHz周期即1000usARR11000所以ARR999。占空比由CCR决定。CCR500占空比就是500/100050%CCR300就是30%。这套参数我建议你亲手算一遍别直接抄。因为很多人抄了别人的代码ARR999、PSC71结果发现频率不对——原因往往是那颗芯片的定时器时钟不是72MHz可能是84MHz、168MHz或者挂在APB1上被二分频了。定时器时钟源到底是多少一定要去查时钟树这是新手最容易翻车的地方。2.3 PWM模式1和模式2一个反相开关的区别STM32的定时器有个PWM模式1和PWM模式2的选项很多人配的时候随手选一个能用就不管了。其实区别很简单模式CNT CCR 时CNT ≥ CCR 时典型用途PWM模式1有效电平通常高无效电平通常低常规调光、调速PWM模式2无效电平通常低有效电平通常高需要反相的场合所谓有效电平还要看CCER寄存器里的极性设置。极性为高、模式1就是CNTCCR时输出高极性为低、模式1就是CNTCCR时输出低。模式和极性是两个独立的开关组合起来有四种情况配错一个波形就跟你想象的反过来。我踩过的坑有一次调电机代码里CCR从0加到999电机却从满速降到停转跟预期完全相反。查了半天才发现是极性配反了。所以调PWM的第一步永远是拿示波器或者逻辑分析仪看一眼实际波形别靠脑补。2.4 为什么PA8能输出PWM而PA9不一定行关键词里有个pa8输出pwm这其实点到了一个关键概念引脚复用Alternate Function。STM32的每个GPIO引脚背后都连着一个复用功能选择器PA8默认是普通IO但你可以把它切到TIM1_CH1这样定时器1的通道1就能直接驱动这根引脚。但不是每个引脚都能复用成任意定时器通道。PA8能接TIM1_CH1是因为芯片内部就是这么连的你换成PA9它可能只能接TIM1_CH2或者根本接不了TIM1。具体哪个引脚对应哪个定时器通道必须查芯片的复用功能映射表Alternate Function Mapping这份表在数据手册里不在参考手册里很多人找错地方。配置的时候GPIO要设成复用推挽输出AF_PP而不是普通的推挽输出。设成普通推挽定时器再怎么数数引脚也不会动因为它根本没接到定时器上。这个坑我在带新人的时候见过太多次了。3. 拿PWM调LED亮度从能亮到亮得舒服3.1 频率选多少100Hz还是20kHz调LED亮度PWM频率不是随便定的。太低比如50Hz人眼能看出闪烁尤其是眼睛快速扫过的时候会有明显的频闪感。太高比如100kHz又没必要还会增加开关损耗和EMI。我的经验值普通指示灯1kHz左右足够看不出闪实现简单。摄像头会拍到的LED至少4kHz以上否则录像里会出现滚动黑条。人眼长时间注视的背光建议20kHz以上彻底避开人眼敏感频段减少视觉疲劳。关键词里有个pwm背光 干扰 显示这是个很典型的实际问题。LCD背光用PWM调光时如果频率落在音频范围内比如2kHz或者走线离显示信号线太近就可能干扰显示出现横纹、闪烁。解决办法通常是把背光PWM频率提到20kHz以上超出音频范围同时背光走线远离数据线必要时加RC滤波。3.2 占空比和亮度的关系不是线性的很多人以为占空比50%就是一半亮度实际上人眼对亮度的感知是近似对数的。占空比从0%到10%亮度变化非常明显从90%到100%几乎看不出差别。所以如果你要做呼吸灯或者平滑调光直接线性改CCR效果会很别扭——前段变化太快后段几乎没感觉。解决办法是做伽马校正把线性占空比映射到感知亮度。简单做法是查表// 8位伽马校正表简化示意 const uint16_t gamma_table[16] { 0, 1, 2, 4, 8, 16, 28, 44, 64, 90, 120, 158, 200, 248, 300, 360 };实际项目中我一般用2.2或2.4的伽马值生成一张256项的查找表CCR直接查表取值。这样呼吸灯看起来才顺滑。3.3 LED驱动芯片的FB和CS脚PWM调光的另一条路关键词里提到led驱动器芯片fb cs脚调整这说的是另一类方案不用MCU直接输出PWM驱动LED而是用专门的LED驱动芯片MCU通过PWM去调芯片的反馈FB或电流采样CS脚。这种方案的好处是LED的电流由驱动芯片闭环控制MCU只负责给一个目标值。比如驱动芯片的FB脚基准是0.2V你用PWM加RC滤波出一个0~0.2V的模拟电压接到FB脚就能控制输出电流。或者更直接用PWM去调CS脚的采样阈值。这条路适合大功率LED、需要恒流的场合。缺点是响应速度受RC滤波限制调光频率做不高。我做过一个项目用PWMRC调FB脚RC时间常数取10ms结果调光频率只能做到几十Hz勉强够用。如果要做高速调光还是得用驱动芯片自带的PWM调光脚通常是EN脚或者专门的DIM脚。3.4 一个实测细节PWM调光时LED的余辉有个现象很多人没注意PWM调光时如果频率不够高LED在低电平期间其实还有微弱余辉尤其是荧光粉LED。这在暗环境下用高速相机拍能看到明显的拖尾。对普通应用无所谓但如果是做光学测量、或者配合高速相机就得考虑用恒流线性调光替代PWM调光或者把PWM频率提到很高。4. 让直流电机转起来PWM调速和LED调光的本质区别4.1 电机是感性负载不能直接拿GPIO怼LED是二极管正向导通、反向截止相对听话。直流电机是感性负载断电瞬间电感会产生反向电动势电压可能高到把GPIO打穿。所以电机绝对不能直接接GPIO必须经过驱动电路。常见方案H桥驱动芯片如DRV8833、L298N能正反转适合需要换向的场合。MOS管续流二极管适合单向调速成本低。专用电机驱动模块集成度高适合快速验证。关键词里有stm32 drv8323这是TI的一款三相栅极驱动器用于BLDC电机。虽然和普通直流电机不是一回事但PWM调速的底层逻辑是相通的都是通过控制功率管的开关时间比例来调平均电压。4.2 电机调速的PWM频率为什么不能太低也不能太高电机调速的PWM频率选择和LED完全不同因为电机的积分靠的是机械惯性和电感不是人眼。频率太低1kHz电机会听到明显的啸叫因为绕组在音频范围内振动。低速时还可能感觉到一顿一顿的。频率太高20kHz开关损耗增加MOS管发热驱动芯片可能扛不住。而且高频下电感的续流效果变差电流纹波反而可能变大。我的经验值有刷直流电机用8kHz~16kHz这个区间超出人耳敏感范围开关损耗也可接受。无刷电机BLDC通常用20kHz以上因为要配合换相逻辑。4.3 占空比和转速的关系不是线性的但比LED好电机转速和占空比的关系比LED亮度线性得多因为电机的反电动势和转速成正比而平均电压和占空比成正比。理想情况下转速≈K×占空比。但实际上死区占空比低于某个值比如5%电机根本转不起来因为力矩不够克服静摩擦。饱和占空比接近100%时转速受限于电源电压和电机特性不再线性增加。负载影响同样的占空比空载和带载转速差很多。所以做闭环调速时光靠PWM开环是不够的得加编码器或者霍尔传感器做反馈。关键词里的stm32定时器捕获测频率就是用来读编码器脉冲的。4.4 一个真实的坑PWM调速时的启动电流电机启动瞬间反电动势为0相当于短路电流非常大。如果PWM占空比一上来就给很高启动电流可能烧驱动芯片。正确做法是软启动占空比从0慢慢加到目标值比如每10ms加1%给电机一个加速过程。我在一个项目里偷懒直接给80%占空比启动结果DRV8833的过流保护反复触发电机抖两下就停。后来加了软启动问题解决。这个细节在数据手册里不会写但实际做产品必须考虑。5. 从寄存器到CubeMX配置PWM的几条路径和各自的坑5.1 直接写寄存器最透明但最容易配错时钟直接操作寄存器配置PWM步骤大致是使能定时器时钟和GPIO时钟RCC寄存器。配置GPIO为复用推挽输出。配置定时器的PSC、ARR。配置PWM模式、极性、使能通道。使能定时器。// 以TIM3_CH1PA6为例的简化示意 RCC-APB1ENR | RCC_APB1ENR_TIM3EN; RCC-AHB1ENR | RCC_AHB1ENR_GPIOAEN; GPIOA-MODER ~(3 12); GPIOA-MODER | (2 12); // PA6复用模式 GPIOA-AFR[0] | (2 24); // AF2 TIM3 TIM3-PSC 71; // 72MHz/72 1MHz TIM3-ARR 999; // 1kHz TIM3-CCR1 500; // 50% TIM3-CCMR1 | (6 4); // PWM模式1 TIM3-CCMR1 | (1 3); // 预装载使能 TIM3-CCER | (1 0); // 通道1使能 TIM3-CR1 | (1 0); // 定时器使能这条路的好处是你清楚每一个bit在干什么坏处是时钟树、复用映射、寄存器位定义全靠自己查错一个就出不来波形。我建议新手至少手写一遍之后再上CubeMX。5.2 CubeMX配置快但要理解它生成了什么CubeMX配置PWM点几下鼠标就出来了。但很多人配完不知道它到底改了什么出了问题无从下手。我的习惯是CubeMX生成代码后一定去翻一遍它写的初始化函数对照上面那套寄存器逻辑看一遍。这样既快又不至于变成只会点鼠标。CubeMX里几个容易配错的点Clock Configuration定时器时钟到底是多少一定要在这里确认。APB1的定时器时钟可能是APB1频率的2倍别想当然。Prescaler和Counter PeriodCubeMX里填的是PSC和ARR的实际值不是分频系数。填PSC71才是72分频。Pulse就是CCR初值。GPIO Settings确认引脚被自动配成了复用模式有时候需要手动改。5.3 用HAL库动态改占空比别在中断里频繁写CCRHAL库改占空比用__HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, value)。这个操作本身很快但如果你在中断里高频调用可能和定时器的预装载机制打架。正确做法是使能CCR预装载CCMR的OCxPE位这样你写CCR时新值会在下一个更新事件才生效不会出现半个周期的毛刺。CubeMX默认会开这个但手写寄存器时容易忘。5.4 一个排查思路PWM没输出按这个顺序查PWM配好了但引脚没波形我一般按这个顺序排查时钟使能了吗定时器时钟、GPIO时钟两个都要。引脚复用对吗查数据手册的AF映射表别猜。定时器使能了吗CR1的CEN位。通道使能了吗CCER的CCxE位。CCR是不是0CCR0时PWM模式1下输出恒低看起来就像没输出。ARR是不是0ARR0定时器不计数。有没有被别的外设占用比如调试口、JTAG引脚冲突。这个顺序能解决90%的PWM没输出问题。6. 那些关键词背后的真实问题从GPIO模式到PWM故障保护6.1 GPIO的8种工作模式PWM该选哪种关键词里gpio的8种工作模式是个高频问题。STM32的GPIO有8种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用。PWM输出该选推挽复用AF_PP。为什么不是普通推挽因为普通推挽是由GPIO数据寄存器控制的定时器管不着只有复用模式引脚才交给定时器。为什么不是开漏开漏需要外部上拉上升沿慢PWM波形会变形除非你有特殊需求比如电平转换。6.2 PWM故障保护刹车输入和死区关键词里pwm故障保护指的是定时器的刹车功能Break。在电机控制里如果出现过流、过温需要立刻关断PWM输出保护功率管。STM32的高级定时器TIM1、TIM8有专门的刹车输入引脚触发后硬件自动把PWM输出拉到安全电平不需要CPU干预。配置刹车功能要注意刹车极性高电平触发还是低电平触发。刹车后的输出状态是强制拉低、拉高还是高阻。自动恢复还是需要软件清除。这个功能在工业电机控制里是标配做产品必须配。我见过因为没配刹车过流时功率管炸掉的案例损失不小。6.3 PWM监控温度一种取巧的测温法关键词里pwm怎么监控硬盘的温度听起来奇怪其实是一种利用PWM占空比传递信息的思路。比如某些温度传感器输出的是PWM信号占空比和温度成正比MCU用输入捕获测占空比反推温度。或者反过来MCU用PWM驱动一个加热元件通过调节占空比维持恒温同时读温度传感器做闭环。这类应用的核心是PWM既能当输出控制也能当输入测量。输入捕获模式下定时器记录上升沿和下降沿的时间戳算出高电平时间和周期占空比就出来了。6.4 从51到STM32为什么51驱动LED不能输出高电平关键词里51单片机驱动led时为什么不能采用输出高电平的驱动方式这是个经典问题。51的IO口是准双向口输出高电平时驱动能力很弱拉电流只有几十微安而输出低电平时灌电流能力较强可以到十几毫安。所以51驱动LED通常采用灌电流接法LED阳极接VCC阴极接IO口IO输出低电平点亮。STM32的GPIO是真正的推挽输出拉电流和灌电流都能到20mA左右所以高低电平都能驱动。但即便如此驱动大功率LED还是得加三极管或MOS管别指望GPIO直接扛。7. 把PWM用对几条我反复验证过的经验PWM这东西原理半小时就能讲完但真正用顺手得靠项目里一次次踩坑。分享几条我自己的经验。第一永远先用示波器看波形。不管你是调LED、调电机还是调背光配完PWM第一件事就是量波形。频率对不对、占空比对不对、极性对不对一眼就看出来。靠脑补和靠灯亮了就行迟早出问题。第二频率选择要结合负载特性。LED看人眼电机看听觉和开关损耗背光看显示干扰加热丝看热惯性。没有万能频率只有适合当前负载的频率。第三占空比变化要平滑。不管是呼吸灯还是电机软启动突变占空比都会带来问题LED闪一下、电机抖一下、电源电压跌一下。加个斜坡几行代码的事能省很多麻烦。第四注意PWM对电源的干扰。大功率PWM负载会在电源上产生纹波可能干扰MCU本身或者其他模拟电路。必要时加LC滤波、加大电容、把PWM负载和敏感电路分开供电。第五预装载和更新事件要理解清楚。改CCR、改ARR的时机不对会出现波形毛刺或者周期跳变。使能预装载让新值在更新事件生效是最稳妥的做法。第六别忽视死区。做H桥或者三相驱动时上下管同时导通就是短路。死区时间必须加具体多少看功率管的开关速度一般几百纳秒到几微秒。PWM从LED亮度到电机转速表面看是两个应用底层是同一套用时间换幅度的逻辑。把这套逻辑吃透再去看DAC、开关电源、 class D功放会发现它们都是同一个思路的变体。这大概就是PWM最迷人的地方——简单但无处不在。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询