拓冰建站拓冰建站
首页 / 资讯中心 / 正文

PWM脉宽调制从入门到实战:频率、占空比与STM32定时器详解

PWM这个东西我在嵌入式这行做了快十年到现在面试新人还是喜欢问。原因很简单它太基础了但又太容易出问题了。控制LED亮度、舵机角度、电机转速、加热器功率甚至通信背后都离不开PWM。我见过不少工作两三年的工程师会调用HAL库接口但一问到占空比怎么算、频率怎么选、为什么电机要用20kHz而不是1kHz就开始犯迷糊。这篇文章我就把这几年用PWM的经验揉碎了讲一讲从波形本质到工程应用从STM32到51单片机从呼吸灯到无刷电机把频率、占空比、死区、故障保护这些问题一次讲清楚。不管你是在校学生准备蓝桥杯还是刚转行做嵌入式开发都能从中找到直接用得上的东西。1. 从最直观的现象说起PWM到底是个什么东西1.1 不就是方波吗为什么它能控制那么多东西PWM全称Pulse Width Modulation脉宽调制。看着名字挺唬人但说白了就是输出一串高低电平交替的方波通过改变高电平持续的时间也就是脉宽来控制一个周期内的平均能量。我经常用开关灯来类比。假设你用一盏灯照一张纸如果开关切换的速度足够快人眼根本看不出灯在闪烁只知道纸的亮度变了。高电平时间占比越大纸就越亮。这个“高电平时间占整个周期时间的比例”就是占空比。这个原理落到实际器件上就是控制电气设备接受能量的时间比例。LED上电流流过的时间长了它就亮一些电机上两端电压等效值高了它就转得快舵机吃到的脉宽变宽了它就转到对应角度。数字IO只能输出0或1但PWM让单片机有了“模拟输出”的能力而且效率极高因为管子要么完全导通、要么完全截止损耗比线性稳压那种方案低得多。1.2 三个核心参数频率、占空比、分辨率工程上说到PWM无非就关心三件事频率、占空比、分辨率。频率就是PWM波形每秒钟重复多少个周期单位Hz。它决定了PWM信号“动作”的快慢。对LED来说频率高于人眼临界闪烁频率大约50Hz到60Hz以上人就感觉不到闪了实际一般用0.5kHz到20kHz。对电机来说PWM频率一般在20kHz左右目的是避开人耳能听到的频段1kHz到20kHz不然电机线圈会噪声吵得人难受。占空比就是在一个周期里高电平持续的时间占整个周期时间的百分比。这是PWM控制能量的关键参数。占空比0%就是始终输出低电平100%就是始终输出高电平。控制LED亮度、电机转速本质都是在调占空比。分辨率就是占空比能细分到什么程度。如果你的定时器是8位的那占空比最多分成256个等级如果是16位的就能分成65536个等级。像BLDC无刷电机控制、需要微调的精密电源这类场景对分辨率要求很高8位很可能不够用。这三个参数不是孤立存在的它们互相牵扯。我后面会详细说它们之间的关系。1.3 占空比怎么算一个公式解决90%的问题占空比的计算公式其实极其简单占空比% 高电平时间 ÷ 整个周期时间 × 100%举个例子一个PWM信号频率是1kHz也就是说周期是1ms毫秒。如果高电平持续了0.25ms那占空比就是25%。如果频率提升到10kHz周期变成0.1ms高电平持续0.05ms占空比就是50%。但实际在单片机里配置寄存器的时候我们通常不会直接填时间而是填计数值。这就涉及到定时器的计数频率和自动重装值怎么配合了。以STM32为例它的定时器是16位的计数范围从0到65535。PWM频率的公式是PWM频率 定时器时钟频率 ÷ PSC预分频值 1÷ ARR自动重装值 1也就是说你先把72MHz的时钟用预分频器降下来再用自动重装值控制计数周期最终决定PWM频率。占空比则由CCR比较寄存器值和ARR值的比例决定占空比 CCR ÷ ARR 1 × 100%之所以每个值都要加1是因为这些寄存器从0开始计数实际范围是0到寄存器值一共是寄存器值加1个计数单位。这个细节很多新手会搞错配出来的波形频率和预期差了一倍十有八九就是忘了加1。2. 硬件是怎么产生PWM的定时器的工作机制2.1 定时器、计数器、比较寄存器这三者是什么关系理解了PWM是什么接下来要理解单片机内部是怎么“凭空”变出这个波形来的。如果不搞清楚寄存器的关系出了问题基本只能靠瞎猜。单片机的定时器本质上就是一个计数器在时钟脉冲驱动下不断往上数数。计数器每增加1就和一个叫“比较寄存器”的数值比对一次。如果计数器的值小于比较寄存器的值输出高电平如果大于等于比较寄存器的值输出低电平。等计数器的值增长到自动重装寄存器ARR设定的上限计数器归零并重新开始计数输出又重新变高。可以把这个过程想象成一个循环赛跑。计数器是跑道上的运动员一圈一圈地跑ARR是赛道长度决定了跑一圈要多久也就是PWM周期CCR是终点线前的某个检查点运动员在这个检查点之前是高电平状态过了检查点就是低电平状态。想调频率就改赛道长度想调占空比就移动检查点位置两者互不干扰这就是配置PWM最核心的逻辑。STM32的定时器输出PWM底层就是这套机制在工作。无论你用标准库、HAL库还是寄存器操作本质上都是往这三个寄存器里填数值。2.2 边沿对齐、中心对齐到底有什么差别ST官方把PWM模式分成PWM模式1、模式2配置时还有个“边沿对齐”和“中心对齐”的选项。很多人在这一步直接默认选择边沿对齐但做电机控制时两者差别非常大下半年就靠这个坑了我一把。边沿对齐模式就是计数从0增到ARR然后立刻跳回0波形的高电平和低电平是在周期的边界处切换的波形上升沿和下降沿不对称——在一个周期内要么先高后低要么先低后高。这种模式配置简单适合LED调光、舵机控制这类对波形对称性没要求的场景。中心对齐模式计数器先从0增到ARR再从ARR递减回0然后把计数器的值和CCR相比输出高电平。在这种模式下高电平时脉是“居中”的一个周期内波形上下对称。这种对称波形对电机控制非常重要因为电流纹波更小电机的电磁噪声更小控制更平顺。还有一个好处是方便ADC采样。电机控制的经典做法是在PWM的中心点也就是计数器的值等于0的时刻启动ADC采样电流。因为这时PWM波形的开关噪声刚好处于波谷采样值最干净。如果你用边沿对齐想找这个“中心时刻”得自己算但用中心对齐模式PWM中心天然就是一个固定的基准点配置简单得多。2.3 高级定时器带来的死区、故障保护功能普通定时器如STM32的TIM3、TIM4输出PWM没问题但如果你做电机控制就得用高级控制定时器TIM1、TIM8。它们的区别在于高级定时器内置了互补输出、死区插入和刹车输入故障保护功能。互补输出就是同时输出两路电平相反的PWM信号专门用来驱动H桥或三相全桥电路的上桥和下桥管子。死区是在上桥关断到下桥导通之间插入的一小段安全间隔时间防止两管同时导通导致电源直通短路瞬间烧一片驱动管。这个时间通常设在几百纳秒到几微秒范围具体值根据你用的功率MOS管或IGBT的关断延迟时间决定。刹车输入就是故障保护引脚硬件检测到过流、过压后能直接把PWM输出强制拉低不需要软件干预。真实项目中电机堵转、驱动管击穿都可能引发短路如果没有这层硬件保护等CPU跑中断去处理可能MOS管已经烧了。这套机制用好了真的能救命我后面专门讲。3. 手把手实操基于STM32输出一路PWM3.1 这套方案怎么选型我用STM32F103C8T6为例STM32F103C8T6这块板子可以说是嵌入式入门的经典款了。它的优势在于硬件资源够用三个通用定时器加一个高级定时器、文档资料最多、引脚兼容性强后续换用F4、F7系列迁移成本低。选择用STM32而不选51是因为51单片机的定时器结构简单但功能有限很难产生多路、可独立调整频率和占空比的PWM更别说死区和故障保护了。STM32的定时器资源完全可以支撑从呼吸灯到BLDC电机的全套练习路线。3.2 先看时序图输出比较通道的工作原理STM32的每个定时器有4个输出比较通道每个通道对应一个独立的比较寄存器CCR可以配置成独立的PWM输出。这也解释了为什么一个定时器能同时输出多路不同占空比的PWM大家共享同一个计数器和自动重装值只是CCR各自独立。共享计数器意味着同一个定时器输出多路PWM时它们的频率必须一致占空比可以各自不同。如果需要两路频率完全不同的PWM要么用两个定时器要么用软件模拟。以TIM2做PWM输出为例配置过程大致如下先给TIM2挂上时钟配置GPIO复用模式让PA0映射到TIM2_CH1设置预分频值PSC和自动重装值ARR确定PWM频率设置输出比较模式比如PWM模式1计数值小于CCR时输出高电平设置初始占空比CCR使能输出比较通道使能定时器PWM波形就自动出来了配置完成之后CPU就完全不用管它了波形由定时器硬件自己跑。这是PWM的一大优势它不占用CPU时间你可以一边输出稳定的控制波形一边跑显示刷屏、通信协议等逻辑。3.3 呼吸灯实验从固定占空比到动态调整呼吸灯是PWM入门最经典的实验逻辑很简单每隔一小段时间调整一次CCR值让占空比从0%逐步变到100%再反向变回0%。LED的亮度视觉上会像呼吸一样逐渐变亮再逐渐变暗。用函数关系整理一下假如PWM周期固定为10kHzARR设置为7199PSC设置为0假设72MHz主频那CCR从0到7200之间变化时LED亮度就在0%到100%之间扫动。每10ms增加或减少一步看起来就非常平滑。呼吸灯看似简单但其实把PWM的“动态调节”精髓摸到了。后面做电机调速、舵机控制无外乎就是把“每一小步”改成PID算法计算出来的目标值而已。所以说呼吸灯是嵌入式PWM学习的第一关绝不是没有道理的。3.4 舵机控制精确固定脉宽才是关键舵机控制不像LED那样靠平均亮度它靠的是高电平脉冲宽度。标准舵机的控制信号是50Hz的PWM也就是周期20ms高电平脉冲宽度在0.5ms到2.5ms之间变化对应舵机从0度到180度。拿常用的SG90舵机举例0.5ms脉宽对应0度1.0ms对应45度1.5ms对应90度也就是中位2.0ms对应135度2.5ms对应180度对应到配置里ARR设为1999920ms周期那0.5ms脉宽对应的CCR就是4990.5ms除以20ms再乘以20000也就是2.5%的占空比。舵机对脉冲宽度的精度非常敏感建议CCR值至少精确到1到2微秒的层次也就是CCR每次变化量控制在10以内不然舵机会抖动。3.5 电机调速PWM频率的选择有讲究直流电机调速是PWM应用的另一个大头。电机转速和两端平均电压近似成正比所以调速的本质就是调占空比。看起来同样简单但要真正调好电机频率选择就得讲究。PWM频率太高驱动管的开关损耗会增大驱动芯片发热严重PWM频率太低电机转速会出现明显脉动而且会发出人耳能听到的啸叫声。常见的做法是选在20kHz左右因为20kHz以上人耳基本听不见同时开关损耗还不至于特别夸张。还有一个坑是电机是感性负载PWM驱动时电流不能突变所以一定要在电机两端并联续流二极管实际电路里由驱动芯片内置或外部添加否则关断瞬间会产生很高的反电动势尖峰直接把驱动管打穿。这个我在入行第二年就烧过一整个驱动板后来再也不敢省续流器件了。4. 进阶控制拆解几个必须掌握的PWM工程细节4.1 死区时间为什么H桥要防直通H桥电路是直流电机正反转的基本结构四个开关管两两组合通过交替导通不同的对角线实现电机换向。这里有个很致命的问题如果同一侧的上管和下管同时导通电源正负极就相当于直接短路了。这就是所谓的直通。避免直通的办法是在上管关断和下管导通之间留出足够的时间空隙让两个管子不会同时处于导通状态。这个空隙就是死区时间。简单说PWM信号在经过死区电路后上升沿会被强制延迟一段时间。死区时间的设置原则是“够用就好”。死区太短管子没有完全关断就开始了下一管导通依然有直通风险死区太长控制波形失真严重电机运行不平顺输出能力下降。具体取多少要看驱动管的关断延迟时间tdoff和下降时间一般在这个基础上留出一定裕量比如1到2倍。TC3xx系列的CCU6模块在生成三相六路PWM时死区配置一般设置在0.5到3微秒之间具体值由MOS管的开关时间决定。4.2 PWM频率如何选LED、舵机、电机、加热器差异巨大搞清各种负载的最佳PWM频率范围能帮你少走很多弯路。我整理了一个对照表负载类型推荐PWM频率原因LED调光500Hz以上常用1kHz到10kHz低于500Hz会有明显闪烁感过高频率会增加开关损耗LED本身响应很快舵机50Hz少数支持333Hz舵机内部电路按特定周期解析脉宽不能随意更改直流电机16kHz到25kHz高频避免人耳噪声过低会震颤过高开关损耗大加热器/热阻丝0.5Hz到10Hz负载惯性大低频即可但注意不要低于某个值导致寿命缩短BLDC无刷电机15kHz到30kHz常用20kHz兼顾噪音、损耗与控制精度特别注意舵机很多人习惯了用1kHz的PWM直接驱动舵机结果舵机要么不动要么抖动因为舵机电路只认50Hz的脉冲周期。这个不一样真是要先看数据手册。4.3 DMAPWM不占用CPU的高效调光方案当你想让多路LED独立呼吸或者做RGB混色、音乐律动灯效时CPU要频繁更新CCR寄存器。如果LED多路且更新频繁CPU的负担就会很大。这个问题的标准解法是DMA搬运数据。DMA可以在定时器更新事件发生时自动从内存把一段时间内各时刻的占空比数据搬运到CCR寄存器里CPU只需要设置好DMA描述符和缓冲区剩下全是硬件自己跑。你可以把一串预先算好的“呼吸曲线”数据放在数组里让DMA按节奏逐个输出CPU该干嘛干嘛去。一个典型的做法是定时器ARR固定准备一个环形数组存放若干帧的CCR值配置DMA为循环模式每次更新事件触发DMA把下一个CCR值送入比较寄存器。这样就能生成非常平滑的动态波形而且CPU占用率几乎为零。4.4 故障保护硬件层面的刹车机制高级定时器的刹车输入Break功能平时容易被忽略但真正做产品时是保命的。它允许外部引脚比如过流比较器的输出直接控制PWM输出的使能状态。当刹车信号有效时PWM输出被硬件立即强制到安全状态可以配置成低电平、高电平或高阻态。我调试电机驱动板时驱动芯片的温度保护脚TEMP_ALERT就接到了STM32的刹车输入引脚同时配合过流比较器把电流检测电阻上的电压与参考值比较超出阈值就触发刹车。这个设计让系统在极端情况下把自己的关断延迟控制在纳秒级根本不需要软件参与大大降低烧板子概率。5. 我踩过的坑和排查经验实录5.1 波形没输出第一步查什么新手做PWM第一反应就是怀疑定时器配置错了但实际上波形没输出的原因非常多。按照优先级给一个排查顺序确认GPIO复用到的是不是正确的引脚。有时候正确引脚是PA0却配置成了PA1确认引脚时钟和定时器时钟都已经使能。漏使能时钟是最常见的低级错误确认CCR值不等于0。如果CCR为0占空比就是0%逻辑分析仪上看就是恒定低电平确认CCR不等于ARR。如果CCR等于ARR占空比100%恒定高电平确认主输出使能位MOE已经置1。这个容易被忽略高级定时器不开MOEPWM是不会有输出的最后再考虑用示波器同时观测脉冲引脚和定时器更新事件引脚判断内部到底有没有波形5.2 输出占空比和配置值差了一倍占空比差一倍的经典原因是忘了ARR加1。ARR寄存器的物理值是N但实际计数范围是0到N总共N1个计数周期。如果拿物理值N来算占空比永远会差一点点当ARR比较小时误差尤其明显比如ARR为499时CCR设置为499就是100%但按照ARR直接算则是约99.8%而按照ARR1算就是刚好100%。另一种情况更常见配置PWM模式时选错了极性。在STM32里如果配置为高电平有效且处于PWM模式2逻辑就会完全反转占空比从25%变为75%。看起来像是波形有问题其实只是模式配错了。5.3 电机啸叫、LED闪烁多半是频率问题电机在马达运转时发出尖锐的电流声基本可以断定PWM频率落在人耳听觉范围内了一般在1kHz到20kHz之间。解决办法很简单把PWM频率调到20kHz以上。但如果频率增大后驱动芯片发热明显那就要检查开关损耗是不是太大了或者把关断速度调慢一些用加栅极电阻的方式来平衡噪声与损耗。LED在大电流下如果出现能感知的闪烁先看电源。纹波过大也会让LED亮度出现低频波动这种情况不一定意味着PWM频率低而可能是PWM信号经过驱动电源后纹波叠加。此时加大输出电容或者提高PWM频率到20kHz以上一般就能解决。5.4 常见问题速查表问题现象可能原因解决方案无输出波形GPIO配置错、时钟未使能、MOE未置位按5.1顺序排查占空比偏差极性配置错、ARR加1遗漏检查PWM模式和极性确认计算公式电机啸叫PWM频率在人耳可听范围提升到20kHz驱动芯片发烫开关频率过高、死区过短降低频率或优化死区加栅极电阻上电瞬间短促冲击死区设置不当或PWM默认状态不对先配置好死区再使能输出默认输出设成安全电平舵机抖动脉宽精度不够、电源功率不足提高分辨率换大功率电源电机和舵机供电分离多路LED亮度不均各路PWM共用一个定时器但频率不同使用不同定时器或软件PWMRGB灯闪烁三路占空比更新不同步使用DMA或中断一次性更新三路CCR5.5 调试工具与手段PWM调试我强烈建议备一个逻辑分析仪几十块钱的玩意就够用了。把三个通道同时夹在舵机信号、电机PWM和电流采样引脚上观察相位关系很多问题一眼就能看出来。如果只有万用表可以量平均电压来粗略判断占空比但无法判断频率和对齐方式只能作为辅助手段。有示波器的话记得用手持式或确保隔离的探头查栅极驱动和电机驱动电路时探头地线夹错位置容易引入噪声甚至造成测量错误。我见过有人把探头地夹在电机相线上结果直接烧了一路示波器探头这笔学费可不值。6. 给嵌入式学习者的趁手建议6.1 学习路线参考从LED到BLDCPWM这条学习线我建议按这么一个路线走先做呼吸灯理解定时器配置和CCR、ARR的意义。然后做舵机控制学会看数据手册找脉宽参数体会精确脉宽控制。再做直流电机调速加入频率选择、续流、H桥的知识点。最后如果有条件可以做BLDC无刷电机这时候你就必须掌握中心对齐PWM、ADC触发采样位置、死区配置和故障保护这些都是真正电机控制产品的核心技能。蓝桥杯嵌入式组每年的题目都少不了PWM基本就是按键、传感器加TFT屏显示的组合把PWM调速做好这道大题的分数就稳了。6.2 源码怎么读、怎么积累嵌入式学习离不开读源码。刚开始读源码不要一头扎进HAL库或驱动库里先把规则文件比如数据手册和参考手册里的定时器章节通读一遍再用最小配置验证每个寄存器的作用。比如先配置一个只有基本计时的定时器配置一个输出比较配置一个PWM每一步都用逻辑分析仪观察变化这个“最小实验法”学到的比看十篇文章都深刻。平时把常用的配置模板保存在自己仓库里比如STM32的PWM初始化函数、TC3xx的CCU6配置代码、ESP32的LEDC配置做一个“PWM工具箱”以后做项目直接复制改参数就行能省很多重复劳动。6.3 最后一个小技巧把PWM理解成“时间切片”我后来带新人时常用一个比喻PWM就是“时间切片”。系统通过控制切片中“开”和“关”的占比来实现对现实物理量的控制。你掌握了这一个概念LED、电机、舵机、加热器甚至数字电源、逆变器全部能串起来。我个人的体会是嵌入式开发里拨开所有复杂的框架和外设底层就是那么几个朴素的机制在反复组合。把PWM吃透等于打通了从数字世界到模拟世界的桥梁后面学ADC、PID、通信协议都会顺很多。这是嵌入式这条路上非常值得花时间打牢的基础。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门