ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32 PWM从原理到实战:LED调光与电机调速的完整指南

2026/10/8 11:23:29 拓冰建站 浏览量
STM32 PWM从原理到实战:LED调光与电机调速的完整指南 1. 从一个现象说起为什么调亮度不用调电压很多人第一次接触PWM脑子里冒出来的问题都差不多既然要让LED变暗直接把电压调低不就行了要让电机转慢把供电电压降下来不就行了这个思路在逻辑上没毛病但实际工程里几乎没人这么干。原因很简单——调电压这件事在数字电路里既麻烦又不划算。拿LED来说它的亮度确实和电流相关而电流又和电压挂钩。但LED的伏安特性曲线非常陡电压稍微动一点点电流可能就翻好几倍。你用可调电阻去分压理论上能调亮度实际上稍微转一下旋钮亮度就从刺眼跳到几乎熄灭中间那段线性区间窄得可怜。更别说电阻上白白消耗的功率发热、效率低纯属给自己找麻烦。电机那边更典型。直流电机的转速和电枢电压近似成正比降压确实能降速。但问题在于电机在低速时扭矩会明显下降因为电压低了能提供的电磁转矩也跟着小。你想要的可能是“慢而有力”结果得到的是“慢而无力”带不动负载甚至堵转。而且用线性稳压去降压大电流下发热量惊人一个几安培的电机稳压芯片能烫到没法摸。PWM换了个思路我不调电压幅值我调电压“出现的时间比例”。供电始终是满幅的要么全开要么全关中间靠快速切换来等效出一个平均电压。这个平均电压才是真正作用在负载上的“有效值”。因为开关管要么导通压降极小要么完全截止不耗电所以效率极高发热很少。这就是PWM能调速、调光的根本原因——它用时间维度上的占空比替代了电压维度上的幅值调节。理解这一点后面所有的配置、计算、踩坑才有落脚点。不然你对着STM32的寄存器一顿操作波形是出来了但为什么这样配、参数为什么这么算还是糊的。2. PWM的核心原理占空比、频率与等效电压2.1 占空比到底在表达什么占空比Duty Cycle就是一个周期内高电平时间占总时间的比例。假设周期是T高电平持续时间是Ton那占空比D Ton / T。如果供电是5V占空比50%那负载感受到的平均电压就是2.5V占空比20%平均电压就是1V。但这里有个关键点容易被忽略平均电压是“等效”出来的不是真实存在的。负载上实际看到的是一个方波高的时候是5V低的时候是0V。之所以LED看起来是变暗而不是闪烁是因为人眼的视觉暂留效应把快速切换的光通量做了积分。电机之所以转得平滑是因为转子的机械惯性把脉冲扭矩做了平滑。所以PWM的频率必须足够高高到负载“反应不过来”才能得到连续的等效效果。这就引出了频率选择的问题。频率太低LED会闪电机会有啸叫甚至抖动频率太高开关损耗增加驱动电路可能跟不上EMI也变差。没有万能频率只有适合场景的频率。2.2 频率选多少才合适不同负载对PWM频率的敏感度差别很大。我整理了一个常见场景的参考区间负载类型推荐频率范围原因说明LED调光1kHz ~ 20kHz低于1kHz人眼可能察觉闪烁高于20kHz开关损耗上升直流有刷电机5kHz ~ 20kHz太低有啸叫和抖动太高驱动MOS开关损耗大舵机控制50Hz标准舵机协议固定50Hz周期20ms步进电机细分20kHz ~ 40kHz需要高于音频范围避免啸叫加热丝调功1Hz ~ 100Hz热惯性大低频即可减少开关次数这张表不是死规定但能帮你快速定位一个合理起点。实际调试时再根据噪声、发热、响应速度微调。2.3 等效电压的计算不是简单乘法很多人以为占空比50%就是一半电压这在纯阻性负载下近似成立。但LED和电机都不是纯阻性。LED有正向压降电机有反电动势。所以实际电流和转速还要结合负载特性来看。以电机为例平均电压Vavg D × Vsupply这个没错。但电机转速还受负载转矩影响。空载时转速近似和Vavg成正比带载时转速会下降因为电流增大内阻压降增加。所以PWM调速时如果你想要闭环稳速光调占空比不够还得加转速反馈。这也是为什么很多电机控制方案里PWM只是执行环节前面还有PID调节。3. STM32上配置PWM的完整实操3.1 定时器选型与通道映射STM32的PWM输出靠定时器实现。以常见的STM32F103为例TIM1、TIM2、TIM3、TIM4都能输出PWM。每个定时器有4个独立通道对应不同的GPIO引脚。比如TIM1_CH1默认在PA8TIM2_CH1在PA0TIM3_CH1在PA6。这里有个坑不是所有引脚都能随便映射。STM32有复用功能Alternate Function你需要查数据手册的“引脚复用表”确认目标引脚支持哪个定时器通道。PA8对应TIM1_CH1这是固定的不用重映射。但如果你想用PB4输出TIM3_CH1就得开重映射还得注意PB4默认是JTAG引脚得先禁用JTAG才能当普通GPIO用。我个人的习惯是优先选默认映射的引脚省去重映射的麻烦。PA8就是个好选择TIM1_CH1高级定时器功能全引脚也好焊。3.2 时基配置频率和分辨率的权衡配置PWM核心就两个参数预分频器PSC和自动重装载值ARR。它们决定了PWM频率和占空比分辨率。公式是这样的PWM频率 定时器时钟 / ((PSC 1) × (ARR 1))占空比分辨率 1 / (ARR 1)假设STM32F103的主频72MHz定时器时钟也是72MHz。我想要一个10kHz的PWMARR设成999那PSC就是PSC 72,000,000 / (10,000 × 1000) - 1 6.2取整PSC 6实际频率 72,000,000 / (7 × 1000) 10.28kHz够用了。这里有个权衡ARR越大占空比调节越细腻但频率会降低ARR越小频率越高但调节步进变粗。比如ARR99占空比只能按1%步进调ARR999能按0.1%步进调。如果你要做平滑调光ARR别太小。3.3 代码实现HAL库和寄存器两种写法用HAL库配置TIM1_CH1输出PWM大致流程是这样// 1. 使能时钟 __HAL_RCC_TIM1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // 2. 配置GPIO为复用推挽输出 GPIO_InitTypeDef gpio {0}; gpio.Pin GPIO_PIN_8; gpio.Mode GPIO_MODE_AF_PP; gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, gpio); // 3. 配置定时器时基 TIM_HandleTypeDef htim1 {0}; htim1.Instance TIM1; htim1.Init.Prescaler 6; htim1.Init.Period 999; htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim1); // 4. 配置PWM通道 TIM_OC_InitTypeDef oc {0}; oc.OCMode TIM_OCMODE_PWM1; oc.Pulse 500; // 初始占空比50% oc.OCPolarity TIM_OCPOLARITY_HIGH; oc.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim1, oc, TIM_CHANNEL_1); // 5. 启动PWM HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1);这段代码跑起来PA8上就有10kHz、50%占空比的方波了。想调亮度或转速改oc.Pulse的值就行范围是0到999。运行时可以用__HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, value)动态修改。如果你用寄存器写法核心就是配CCMR1的OC1M位为110PWM模式1使能CCER的CC1E然后设CCR1的值。寄存器写法更直接但可读性差项目里我一般用HAL调试时偶尔看寄存器确认状态。3.4 PWM模式1和模式2的区别这是很多人配的时候会懵的地方。PWM模式1和模式2的区别本质是输出极性的问题。PWM模式1计数器小于CCR时输出有效电平大于等于CCR时输出无效电平。PWM模式2反过来计数器小于CCR时输出无效电平大于等于CCR时输出有效电平。如果你把有效电平设成高那模式1就是“先高后低”模式2是“先低后高”。对于LED和电机这两种模式都能用只是占空比的计算方向反了。比如模式1下CCR300是30%高电平模式2下CCR300就是70%高电平。我一般用模式1因为直觉上CCR越大占空比越大调起来不容易搞反。但如果你用的是互补输出或者死区控制模式选择会影响互补通道的相位那就得仔细看参考手册了。4. 从LED到电机负载差异带来的电路考量4.1 LED驱动限流电阻和驱动芯片STM32的GPIO输出电流有限通常单个引脚最大20mA左右整个芯片也有总电流限制。直接驱动一个小功率LED还行串个限流电阻就行。电阻怎么算假设LED正向压降2V目标电流10mAGPIO高电平3.3VR (3.3 - 2) / 0.01 130Ω取标准值130Ω或150Ω都行。但如果你要驱动大功率LED比如1W的电流300mAGPIO根本带不动必须加驱动电路。简单点用三极管讲究点用专门的LED驱动芯片。LED驱动芯片里经常看到FB和CS引脚。FB是反馈脚CS是电流采样脚。芯片通过CS上的采样电阻来设定恒流值FB则用来监测输出电压或电流是否达标。比如某款驱动芯片CS脚接一个0.1Ω电阻内部基准100mV那恒流值就是100mV / 0.1Ω 1A。这种芯片适合做恒流驱动PWM调光时在EN脚给PWM信号响应快线性度好。4.2 直流电机驱动H桥和续流回路电机不能直接接GPIO原因有三电流大、有反电动势、需要正反转。所以必须用驱动电路最常见的是H桥。H桥有四个开关管对角导通时电机正转另一对角导通时反转。PWM调速时通常固定一个方向在其中一个开关管上叠加PWM信号。比如上桥臂常通下桥臂用PWM斩波这样电机两端电压就在0和Vsupply之间切换。这里有个关键点电机是感性负载电流不能突变。当开关管关断时电机线圈会产生反向电动势如果不给电流提供续流通路电压会飙得很高击穿开关管。所以H桥里必须有续流二极管或者用MOS的体二极管来续流。自己搭H桥的话二极管别省。另外PWM频率对电机噪声影响很大。我试过用1kHz驱动电机发出明显的“滋滋”声调到16kHz以上基本听不见了。但频率高了MOS的开关损耗也上去了驱动芯片会发热。折中下来8kHz到16kHz是个甜点区。4.3 隔离与保护别让电机干扰单片机电机启停时电流突变会产生很大的电磁干扰可能通过电源和地线串到单片机导致复位或死机。我踩过这个坑电机一转STM32就重启查了半天发现是电源被拉低了。解决办法有几个一是电机和单片机分开供电用光耦或磁隔离做信号隔离二是在电机两端加TVS管和滤波电容三是PCB布局时电机驱动部分和MCU部分的地分开走最后单点接地。这些措施看着麻烦但能省下大量调试时间。5. 常见问题与排查技巧实录5.1 波形出不来先查这三处PWM配好了但引脚没输出我一般按这个顺序查时钟使能了吗定时器时钟和GPIO时钟都要开少一个都不行。引脚模式对吗必须是复用推挽输出AF_PP配成普通输出或输入都没波形。定时器启动了吗HAL_TIM_PWM_Start调了吗有时候配置都对忘了启动干等半天。如果这三步都没问题拿示波器或逻辑分析仪看引脚确认有没有信号。没有的话查复用映射对不对特别是重映射的引脚。5.2 占空比调不了或者调反了这种情况通常是CCR寄存器没更新或者PWM模式选反了。用HAL库的话确认你调的是__HAL_TIM_SET_COMPARE而不是直接改htim.Init.Period。改Period会改频率不是占空比。如果占空比方向和预期相反检查PWM模式1还是模式2以及输出极性设置。极性设成LOW的话高电平有效变成低电平有效占空比含义就反了。5.3 电机低速抖动、发热低速抖动多半是PWM频率太低或者占空比分辨率不够。比如ARR只有99占空比1%步进低速时每调一档转速跳变很大看起来就是抖。把ARR加大到999或1999调节细腻了抖动会明显改善。发热则可能是开关损耗大或者电机本身在低速时效率低。如果驱动MOS发热严重考虑降低PWM频率或者换导通电阻更小的MOS。5.4 常见问题速查表现象可能原因排查方向引脚无输出时钟未使能、引脚模式错、定时器未启动查RCC寄存器、GPIO配置、Start函数占空比不可调CCR未更新、模式选错确认SET_COMPARE调用、检查OCMode电机抖动频率太低、分辨率不足提高频率、增大ARR单片机复位电机干扰、电源跌落隔离供电、加滤波、分开接地LED闪烁频率低于视觉暂留阈值提高到1kHz以上驱动芯片发热开关损耗大、频率过高降频、换低Rds(on)的MOS6. 几个容易被忽略的细节6.1 GPIO的8种工作模式怎么选STM32的GPIO有8种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、推挽复用、开漏复用。PWM输出必须选复用模式因为控制权在定时器手里不是普通GPIO控制器。推挽和开漏的区别在于推挽能主动输出高和低驱动能力强开漏只能拉低高电平靠外部上拉适合总线或多电压域场景。PWM驱动MOS时如果MOS是低电平导通开漏也能用但如果是高电平导通必须用推挽否则高电平靠上拉电阻上升沿会变缓开关损耗增加。6.2 高级定时器要额外使能PWM输出STM32的TIM1和TIM8是高级定时器除了常规配置还需要调用HAL_TIM_PWM_Start之前确保刹车和死区寄存器配置正确。更关键的是高级定时器的PWM输出默认是关闭的需要设置TIM_BDTR寄存器的MOE位主输出使能。HAL库的HAL_TIM_PWM_Start内部会处理这个但如果你用寄存器写法忘了设MOE波形就是出不来。这个坑我踩过查了一晚上才发现。6.3 动态调占空比时的缓冲问题运行时频繁改CCR值一般没问题。但如果你在定时器更新事件之外改可能会出现一个周期的毛刺。讲究的做法是开启CCR的预装载功能OCPreload这样新值在下一个更新事件时才生效波形更干净。HAL库默认开启预装载寄存器写法要手动设CCMR1的OC1PE位。7. 从PWM延伸到更广的控制思路PWM本质上是一种“用数字手段实现模拟效果”的技术。它的应用远不止调光和调速。比如舵机控制50Hz的PWM脉宽0.5ms到2.5ms对应角度0到180度。开关电源通过PWM调节占空比来稳定输出电压反馈环路控制占空比。加热控制用PWM调节加热丝的平均功率配合温度传感器做闭环。音频D类功放把音频信号调制成PWM再滤波还原效率极高。这些场景的共同点是需要连续调节但用数字方式实现更高效、更可靠。理解了PWM的占空比、频率、等效值这三个核心概念换到哪个场景都能快速上手。我在实际项目里最深的体会是PWM配置本身不难难的是根据负载特性选对频率和驱动电路。LED和电机看着都是“通电就亮/转”但电气特性天差地别。LED怕过流电机怕反电动势LED调光看频率电机调速看扭矩。把这些差异想清楚了PWM才真正算会用而不是只会抄配置代码。最后分享一个小技巧调试PWM时先把频率设低一点比如100Hz用示波器看波形确认占空比变化正常再逐步提高到目标频率。这样万一有问题低频下更容易观察和定位。直接上高频波形还没看清问题已经藏进去了。