☰
PWM调速原理与STM32实战:从LED调光到电机驱动
2026/10/27 9:12:22 网站建设 项目流程

1. 从一个让人困惑的现象说起

1.1 为什么LED半亮不亮,电机却能慢速转动

刚接触嵌入式那会儿,我盯着示波器上那串方波发呆:高电平3.3V,低电平0V,占空比50%,频率1kHz。按道理说,LED要么亮要么灭,怎么眼睛看到的是"半亮"?更离谱的是,同一个方波信号接到直流电机驱动板上,电机居然能稳稳地以半速转动,而不是一抖一抖地转停转停。

这个疑问困扰了我很久,直到我把PWM的底层逻辑彻底捋清楚。其实答案就藏在两个词里:惯性和积分效应。LED的发光材料有余辉,人眼有视觉暂留,电机转子有机械惯性,电感有电流续流——所有这些"慢半拍"的物理特性,让高频切换的方波在宏观上表现为一个连续的中间值。

这篇文章我打算把PWM从"为什么能调速"这个最朴素的问题切入,一路讲到STM32上怎么配置定时器输出PWM、GPIO的8种工作模式怎么选、LED驱动芯片的FB和CS脚怎么调、以及实际调试中那些让人抓狂的坑。不管你是刚点亮第一颗LED的新手,还是已经在调电机驱动的老手,应该都能从里面找到点有用的东西。

1.2 本文适合谁看,能解决什么问题

如果你正在做下面这些事情,这篇文章大概率能帮到你:

  • 用STM32或者51单片机点LED,想搞明白为什么调占空比就能调亮度
  • 想驱动直流电机调速,但不确定PWM频率该设多少、GPIO该配什么模式
  • 在用LED驱动芯片,看到FB、CS这些引脚不知道干嘛的
  • 调PWM背光时遇到显示干扰,或者PWM信号被干扰导致闪烁
  • 想搞清楚PWM模式1和模式2到底差在哪,什么时候用哪个

我会尽量用生活化的类比把原理讲透,然后给出可以直接抄的配置代码和参数计算过程。所有代码基于STM32 HAL库,但思路对GD32、HC32F460这些国产替代品同样适用。

2. PWM能调速的底层逻辑拆解

2.1 方波为什么能等效成模拟量

先建立一个最核心的认知:PWM不是在输出模拟电压,而是在输出一个时间维度上的能量比例。

想象你有一个水龙头,只能全开或者全关,不能半开。但你想让水流变成一半。怎么办?快速开关——开1秒关1秒,循环往复。如果下面接一个水桶,水桶里的水位会稳定在某个中间值,因为进水和出水达到了动态平衡。这个"开1秒关1秒"就是50%占空比,水桶水位就是等效的模拟量。

电路里是一样的道理。LED接在PWM输出上,高电平时电流流过LED发光,低电平时不发光。如果频率足够高(比如1kHz以上),人眼根本分辨不出闪烁,看到的就是平均亮度。这个平均亮度正比于占空比:

平均亮度 = 最大亮度 × 占空比

电机也是同理。高电平时电机绕组通电产生转矩,低电平时靠惯性继续转。平均转矩正比于占空比,所以占空比50%时转速大约是满速的一半(注意不是严格线性,后面会讲)。

这里有个关键点很多人会忽略:PWM等效的是平均功率,不是平均电压。对于纯阻性负载比如LED串联限流电阻,平均电压确实等于Vcc乘以占空比。但对于电机这种感性负载,电流的续流效应会让实际波形变得复杂,平均电流和占空比的关系需要看具体的驱动拓扑。

2.2 频率选多少才合适:LED和电机的不同诉求

PWM频率的选择是个权衡题,选低了会闪,选高了会有各种副作用。

对于LED,人眼的闪烁融合频率大约是60-90Hz,所以理论上超过100Hz就不会感觉闪。但实际用的时候我一般会选1kHz以上,原因有两个:一是摄像头拍摄时低频PWM会出现滚动条纹,二是有些LED驱动芯片的响应速度有限,频率太低会导致亮度非线性。不过频率也不是越高越好,太高了MOS管的开关损耗会上升,EMI也会变差。

对于直流电机,情况更复杂。有刷直流电机的PWM频率通常选在1kHz到20kHz之间。低于1kHz时电机会听到明显的啸叫声(因为绕组在音频范围内振动),高于20kHz就超出人耳听觉范围了,但开关损耗会增加。我实测下来,8kHz到16kHz是个比较舒服的区间,既听不到噪音,损耗也能接受。

有刷电机还有个特殊问题:换向火花。电刷换向时会产生高频干扰,如果PWM频率和换向频率产生谐振,可能会导致驱动MOS管误触发。所以实际调试时,我会用示波器看电机两端的波形,如果看到尖峰毛刺,就在电机两端并一个104陶瓷电容加一个RC吸收电路。

2.3 占空比和转速的非线性关系

很多人以为占空比50%电机就转50%速度,实际测下来往往不是。原因有几个:

第一,电机有启动死区。占空比太低时,平均电压不足以克服静摩擦和反电动势,电机根本不转。这个死区通常在5%-15%之间,具体看电机型号。

第二,电机内阻和反电动势的影响。电机的稳态转速近似正比于(占空比×Vcc - 反电动势常数×转速)/内阻,解出来转速和占空比的关系是一条带截距的曲线,不是过原点的直线。

第三,驱动电路的压降。MOS管有导通电阻,续流二极管有正向压降,这些都会吃掉一部分电压。

所以如果你要做精确调速,比如用PID闭环控制,就不能假设占空比和转速是线性的。我的做法是先测一条"占空比-转速"曲线,然后在软件里做分段线性化或者查表补偿。

3. STM32上配置PWM的完整实操

3.1 定时器选型和GPIO模式选择

STM32的PWM输出靠定时器实现。以最常见的STM32F103为例,有TIM1到TIM4,其中TIM1是高级定时器,TIM2/3/4是通用定时器。每个定时器有4个独立通道,可以输出4路PWM。

选哪个定时器?我的经验是:

  • 如果只是点LED或者调背光,随便哪个通用定时器都行,TIM2/3/4足够
  • 如果要驱动电机做互补输出加死区,必须用TIM1这种高级定时器
  • 如果PWM频率要求很高(比如几百kHz),注意看定时器的时钟频率和计数位数

GPIO模式这块是新手最容易踩坑的地方。STM32的GPIO有8种工作模式:

模式说明PWM输出该选哪个
输入浮空默认输入,电平不确定否
输入上拉输入带内部上拉否
输入下拉输入带内部下拉否
模拟输入用于ADC采集否
开漏输出只能拉低,需外部上拉特殊场景
推挽输出可拉高拉低,驱动能力强普通GPIO输出
推挽复用输出外设控制引脚PWM输出选这个
开漏复用输出外设控制,开漏特殊场景

关键点:PWM输出必须配置成复用推挽输出(GPIO_Mode_AF_PP)。因为此时引脚由定时器外设控制,不是普通GPIO。如果你配成普通推挽输出,定时器根本控制不了这个引脚,自然也不会有PWM波形。

以PA8输出PWM为例(对应TIM1_CH1),配置代码大概长这样:

GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_TIM1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽 GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速,保证边沿陡峭 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

注意:Speed一定要设成HIGH或者VERY_HIGH。我见过有人设成LOW,结果PWM上升沿变得很缓,驱动MOS管时进入线性区发热严重。

3.2 定时器参数计算:频率和占空比怎么定

配置定时器输出PWM,核心是算三个参数:预分频器(PSC)、自动重装载值(ARR)、比较值(CCR)。

公式是这样的:

PWM频率 = 定时器时钟 / ((PSC+1) × (ARR+1)) 占空比 = CCR / (ARR+1)

假设STM32F103的主频72MHz,TIM1挂载在APB2上,时钟也是72MHz。我想输出1kHz的PWM,分辨率希望有1000级(也就是占空比可以调0.1%的精度)。

先定ARR=999,这样计数从0到999共1000个周期,分辨率就是1/1000。然后算PSC:

1000 = 72000000 / ((PSC+1) × 1000) (PSC+1) = 72000000 / (1000 × 1000) = 72 PSC = 71

所以PSC=71,ARR=999,CCR设成500就是50%占空比。

这里有个取舍:ARR越大,分辨率越高,但频率上限越低。因为PSC+1最小是1,所以最高频率就是时钟除以(ARR+1)。如果你要100kHz的PWM,ARR最多只能设成719,分辨率就只有720级了。

我的经验是,调光应用ARR设1000左右够用,电机调速ARR设500到2000都行。如果要做音频DAC那种高分辨率应用,可以用定时器的中心对齐模式,等效分辨率能翻倍。

3.3 代码实现:从寄存器到HAL库

用HAL库配置PWM,流程大概是:初始化定时器时基→初始化PWM通道→启动PWM→动态改CCR调占空比。

TIM_HandleTypeDef htim1; TIM_OC_InitTypeDef sConfigOC = {0}; // 时基配置 htim1.Instance = TIM1; htim1.Init.Prescaler = 71; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 999; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(&htim1); // PWM通道配置 sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 500; // 初始50% sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1); // 启动 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 动态调占空比 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 800); // 改成80%

如果你用寄存器直接写,核心就是配置TIMx_ARR、TIMx_PSC、TIMx_CCR1,然后使能TIMx_CR1的CEN位和TIMx_CCER的CC1E位。HAL库的好处是帮你处理了高级定时器的刹车、死区这些复杂配置,坏处是代码体积大、执行效率略低。我一般项目里用HAL,对时序要求极严的地方才切寄存器。

3.4 PWM模式1和模式2的区别与选择

STM32的PWM输出有两种模式,很多人配的时候随手选一个,其实它们的行为正好相反:

  • PWM模式1:计数值小于CCR时输出有效电平,大于等于CCR时输出无效电平
  • PWM模式2:计数值小于CCR时输出无效电平,大于等于CCR时输出有效电平

"有效电平"是高还是低,由OCPolarity决定。如果OCPolarity设HIGH,那有效电平就是高电平。

所以PWM模式1加高极性,就是"CNT<CCR时输出高",这是最常见的配置。PWM模式2加高极性,就是"CNT<CCR时输出低",相当于把波形反相了。

什么时候用模式2?我遇到过的场景是驱动共阳极LED。共阳LED是低电平点亮,如果我用模式1高极性,占空比越大反而越暗,逻辑很别扭。这时候切成模式2,占空比越大越亮,符合直觉。

还有一种情况是互补输出。高级定时器可以同时输出CH1和CH1N,两个波形互补。这时候CH1用模式1,CH1N自动就是反相的,不需要手动配模式2。

4. LED驱动和电机驱动的实战差异

4.1 LED驱动芯片的FB和CS脚到底干嘛的

直接用GPIO驱动LED,简单但效率低。LED是电流型器件,用电压驱动会导致亮度随温度漂移,而且限流电阻上会白白消耗功率。所以稍微正规一点的设计都会用恒流驱动芯片。

这类芯片上经常看到FB和CS两个引脚,新手容易搞混:

  • FB(Feedback):反馈脚。芯片内部有一个基准电压(通常是0.1V到0.2V),FB脚电压会被稳压到这个值。你在FB脚和地之间接一个采样电阻R,流过LED的电流就是I = Vfb / R。比如Vfb=0.1V,R=0.1Ω,电流就是1A。
  • CS(Current Sense):电流采样脚。有些芯片把采样和反馈分开,CS脚接采样电阻,FB脚接反馈网络。功能上类似,具体看芯片手册。

用PWM调光的时候,有两种做法:

第一种是模拟调光,在FB脚上加一个可调的直流电压,改变恒流值。这种调光没有频闪,但调光范围窄,低亮度时线性度差。

第二种是PWM调光,在使能脚(EN)上加PWM信号,快速开关芯片。这种调光范围宽,可以做到0.1%以下,但要注意PWM频率不能太低,否则会看到闪烁。

我一般推荐PWM调光,因为色温漂移小。但有个坑:PWM调光时,如果频率落在音频范围内,电感会啸叫。解决办法是把频率提到20kHz以上,或者用展频技术打散频谱。

4.2 为什么51单片机驱动LED不能输出高电平

这是个经典问题,热词里也出现了。51单片机的IO口是准双向口,内部结构决定了它拉低能力强(可以吸收20mA),拉高能力弱(只有几十微安到几百微安,靠内部弱上拉)。

如果你用高电平驱动LED,比如P1.0接LED正极,LED负极接地,那LED的电流全部由IO口提供。这个电流太小,LED要么不亮,要么很暗。而且拉高时IO口内阻大,LED亮度会随温度变化。

正确做法是低电平驱动:LED正极接VCC,负极接IO口。IO口输出低电平时,LED电流从VCC经LED流入IO口到地。这时候IO口工作在灌电流模式,能吸收20mA,足够点亮普通LED。

STM32的推挽输出拉高拉低能力都强(20mA左右),所以高电平驱动也行。但为了统一习惯,我还是推荐低电平驱动,尤其是多个LED共用限流电阻的时候。

4.3 电机驱动:PWM频率和死区时间的考量

直流电机驱动通常用H桥。H桥有四个开关管,对角导通时电机正转,另一对角导通时反转。如果同侧上下管同时导通,就会短路炸管,所以必须留死区时间。

死区时间怎么算?主要看开关管的关断时间。MOSFET的关断时间通常在几十到几百纳秒,所以死区设个500ns到1μs比较保险。具体值要查MOS管手册的td(off)和tf参数,然后留2倍余量。

STM32的高级定时器TIM1和TIM8支持硬件死区插入,配置起来很方便:

TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime = 72; // 72个时钟周期,72MHz下就是1us sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);

PWM频率方面,有刷电机我一般用10kHz到16kHz。无刷电机(BLDC)的PWM频率通常更高,20kHz到40kHz,因为要配合换相逻辑。步进电机的PWM频率取决于细分设置和转速,这个要单独算。

实操心得:调电机时一定要先用示波器看H桥输出波形,确认死区正常、没有直通。我见过有人死区设太小,MOS管发热严重,跑几分钟就烫手,最后炸管。

5. 常见问题排查与避坑指南

5.1 PWM背光干扰显示的典型原因

用PWM调LCD背光时,经常遇到屏幕出现横条纹或者闪烁。这个问题我踩过好几次,总结下来原因有几个:

第一,PWM频率和LCD刷新率产生拍频。比如PWM是1kHz,LCD刷新率是60Hz,两者最小公倍数导致亮度周期性波动,人眼就能看到条纹。解决办法是把PWM频率设成LCD刷新率的整数倍,或者干脆提到20kHz以上。

第二,PWM走线离LCD数据线太近,开关噪声耦合到数据线上。这个要在PCB布局时注意,PWM走线尽量短,远离模拟信号和高速数据线,必要时包地处理。

第三,背光升压电路的输入电容不够。PWM开关瞬间电流突变,如果输入电容太小,电源电压会波动,影响整个系统。一般建议在升压芯片输入端并一个10μF以上的陶瓷电容加一个100μF的电解电容。

第四,地线回流路径不合理。大电流的背光回路和小信号的LCD回路共用一段地线,地线上的压降会串扰。正确做法是单点接地,功率地和信号地分开走,最后在电源入口处汇合。

5.2 PWM信号被干扰导致闪烁的排查思路

如果PWM信号本身被干扰,LED或者电机会出现不规则闪烁。排查步骤我一般这样走:

  1. 用示波器看PWM输出引脚波形,确认占空比和频率是否稳定
  2. 如果波形有毛刺,检查是否有长线缆、是否靠近干扰源
  3. 在PWM输出和地之间并一个100pF到1nF的电容,滤掉高频毛刺
  4. 如果是长线传输,考虑加一个缓冲器或者用差分传输
  5. 检查电源是否干净,必要时给MCU的电源加LC滤波

有个容易被忽略的点:如果PWM控制的是大功率负载,负载电流突变会通过地线耦合回MCU。这时候光在PWM信号上加滤波没用,要从电源和地线入手。我通常会在MOS管驱动电路和MCU之间加光耦或者数字隔离器,彻底切断干扰路径。

5.3 常见问题速查表

现象可能原因排查方法解决措施
LED完全不亮GPIO模式配错量引脚电压改成复用推挽输出
LED亮度不随占空比变化定时器没启动查TIMx_CR1的CEN位调用HAL_TIM_PWM_Start
电机低速抖动PWM频率太低听是否有啸叫提到8kHz以上
电机高速无力占空比上限不够量H桥输入电压检查驱动芯片使能脚
背光干扰显示拍频或耦合改变PWM频率试试提到20kHz或同步刷新率
PWM波形上升沿缓GPIO速度设太低看示波器边沿改成HIGH或VERY_HIGH
电机驱动管发热死区时间不够看上下管波形增大死区到1μs
调光时电感啸叫PWM频率在音频范围听声音频率提到20kHz以上

5.4 几个我踩过的坑和独家技巧

坑一:ARR设成0。有次我手滑把ARR设成0,结果定时器根本不计数,PWM没输出。ARR最小值是1,这个要记住。

坑二:忘了开定时器时钟。HAL库的HAL_TIM_PWM_Init不会自动开时钟,必须手动调__HAL_RCC_TIMx_CLK_ENABLE()。我见过有人调了半天没波形,最后发现是时钟没开。

坑三:CCR值超过ARR。如果CCR大于ARR,输出会一直是有效电平(或者无效,看模式)。调占空比时要注意边界,我一般会在软件里做限幅。

技巧一:用DMA更新CCR做呼吸灯。如果要做一个平滑的呼吸灯,用软件循环改CCR会有卡顿。可以用DMA把预先算好的正弦表搬到CCR寄存器,定时器每个周期自动更新,效果非常丝滑。

技巧二:用定时器主从模式同步多路PWM。如果要多路PWM严格同步,比如驱动三相无刷电机,可以把一个定时器设成主模式,其他定时器设成从模式,用主定时器的更新事件触发从定时器。这样所有PWM的相位关系是确定的。

技巧三:测量PWM频率用输入捕获。如果外部输入一个PWM信号,想知道它的频率和占空比,可以用定时器的输入捕获功能。配置成PWM输入模式,一个通道测周期,一个通道测高电平时间,硬件自动算好,软件直接读寄存器就行。

6. 从PWM到更复杂的控制场景

6.1 PWM控制DC-DC输出电压的思路

热词里有个问题:怎么用MCU控制DC-DC的输出电压。这个场景在可调电源、LED恒流驱动里很常见。思路有几种:

第一种,PWM加RC滤波生成模拟电压,接到DC-DC的FB脚。DC-DC的FB脚本来接分压电阻,你把PWM滤波后的电压叠加到FB节点,就能改变输出电压。优点是简单,缺点是响应慢,PWM纹波会串到输出。

第二种,用DAC直接改FB脚电压。STM32有些型号带DAC,输出0到3.3V,通过电阻分压后接到FB脚。这个比PWM加RC干净,但DAC分辨率有限,一般是12位。

第三种,用数字电位器替代分压电阻。I2C或者SPI接口的数字电位器,直接替换FB脚的上分压或下分压电阻。优点是精度高、无纹波,缺点是数字电位器有寿命限制,而且价格比RC贵。

第四种,PWM直接控制DC-DC的使能或者模式脚。有些DC-DC支持PWM调压模式,直接给使能脚PWM信号,芯片内部自己处理。这个要看具体芯片手册。

我一般首选DAC方案,如果MCU没有DAC就用PWM加二阶RC滤波。二阶RC的截止频率要设得比PWM频率低至少两个数量级,比如PWM是100kHz,截止频率设1kHz,这样纹波能压到1mV以下。

6.2 PWM故障保护:刹车输入和软件保护

做电机驱动或者大功率LED驱动时,故障保护是必须的。STM32的高级定时器有个刹车输入(Break Input)功能,检测到故障信号时硬件自动把PWM输出拉到安全状态,不需要软件干预。

刹车输入可以配置成高电平触发或者低电平触发,触发后PWM输出可以设成高阻、低电平或者高电平。我一般设成低电平,因为故障信号通常是低有效。

除了硬件刹车,软件上也要做保护:

  • 过流保护:采样电阻上的电压超过阈值时,软件立即关PWM
  • 过温保护:NTC测温度,超过阈值降功率或者停机
  • 堵转保护:电机堵转时电流会飙升,检测到持续大电流就停机
  • 欠压保护:电源电压太低时,MOS管可能进入线性区发热,要及时关断

这些保护逻辑我一般放在定时器中断里,每个PWM周期检查一次。响应速度要求高的用硬件比较器加刹车输入,要求不高的用软件轮询。

6.3 用PWM监控温度和其他物理量

热词里有个挺有意思的问题:PWM怎么监控硬盘温度。其实原理很简单:温度传感器输出一个电压,你用ADC采集,然后MCU根据温度值调整风扇的PWM占空比。温度高就转快,温度低就转慢。

这个逻辑可以推广到很多场景:

  • 用PWM控制风扇转速,根据温度闭环调节
  • 用PWM控制加热丝功率,根据温度传感器反馈做PID
  • 用PWM控制半导体制冷片,根据温度做双向调节

核心都是传感器采集→MCU计算→PWM输出→执行器动作这个闭环。PWM在这里扮演的是"功率调节器"的角色,把数字计算的结果转换成物理世界的能量。

我做过一个鱼缸温控项目,就是用STM32的ADC采集NTC温度,然后PWM控制加热棒。加热棒是大功率阻性负载,PWM频率不能太高,否则继电器或者MOS管扛不住。我最后用的是0.5Hz的超低频PWM,配合固态继电器,效果很稳。

6.4 从51到STM32再到HC32F460的PWM配置差异

不同MCU的PWM配置思路大同小异,但细节有差异。我简单对比一下:

MCU型号定时器特点PWM配置要点
51单片机定时器资源少,无硬件PWM软件模拟,中断里翻转IO
STM32F103定时器丰富,高级定时器带死区HAL库配置,注意复用推挽
GD32F407兼容STM32,主频更高库函数类似,注意时钟树差异
HC32F460国产,定时器功能强寄存器手册要仔细看,和STM32不完全一样

HC32F460的PWM配置我踩过坑:它的定时器计数模式和STM32不太一样,ARR寄存器的加载时机有区别。如果动态改频率,需要先停止定时器再改,否则会出现异常波形。这个在STM32上一般不用停,直接改ARR就行。

所以换MCU平台时,不要假设"配置逻辑一样",一定要把定时器章节的手册仔细读一遍。尤其是时钟树、预分频器、自动重装载这些关键寄存器的行为,各家实现都有微妙差异。

7. 一些零散但有用的经验

7.1 示波器是调PWM最好的朋友

调PWM相关的东西,示波器比万用表有用一百倍。万用表只能测平均电压,看不到波形细节。示波器能看到频率、占空比、上升沿、过冲、振铃、毛刺,这些都是排查问题的关键信息。

我建议至少用一个双通道示波器,一个通道看PWM输入,一个通道看输出响应。比如调电机时,CH1看MCU的PWM输出,CH2看电机两端的电压,这样能直观看到驱动电路的延迟和失真。

如果没有示波器,至少用一个逻辑分析仪。几十块钱的USB逻辑分析仪能看数字波形,虽然看不到模拟细节,但测频率和占空比够用了。

7.2 代码里加个PWM自检功能

产品出厂前一般要做自检。PWM通道也可以加自检:让PWM输出一个已知占空比,然后用ADC回采(如果硬件支持),或者用外部电路把PWM整流成直流再采集。如果采集值和预期不符,就报故障。

这个功能在批量生产时特别有用,能筛出焊接不良或者元件贴错的板子。我做过一个项目,PWM输出脚虚焊,出厂没检出来,到了客户手里才发现LED不亮,返修成本很高。后来加了自检,这类问题就堵住了。

7.3 低功耗场景下PWM的注意事项

如果做电池供电的产品,PWM会影响功耗。几个点要注意:

  • PWM频率越高,MOS管开关损耗越大,整体功耗越高
  • 如果负载不需要一直工作,可以用间歇PWM,工作一段时间停一段时间
  • 睡眠前记得把PWM输出设成安全状态,避免睡眠时负载意外导通
  • 有些MCU的低功耗定时器可以在睡眠模式下继续输出PWM,但频率和分辨率会受限

我做过一个便携LED灯项目,用STM32L系列的低功耗定时器,睡眠模式下PWM继续跑,整机功耗只有几十微安,电池能用好几个月。

7.4 最后分享一个调光曲线的小技巧

人眼对亮度的感知不是线性的,而是近似对数关系。如果PWM占空比从1%线性增加到100%,人眼感觉亮度变化是先快后慢。所以做调光时,最好做伽马校正,让占空比按指数曲线变化。

具体做法是建一个查找表,把0到100的亮度等级映射到0到ARR的占空比值。比如100级亮度,第1级对应占空比1,第50级对应占空比200,第100级对应占空比1000。这样人眼感觉亮度变化才是均匀的。

这个技巧在LED调光、背光调节、甚至电机调速里都适用。电机虽然不涉及人眼感知,但负载的机械特性也可能需要非线性补偿。核心思想就是:执行器的响应往往不是线性的,控制信号要做相应的校正。

调PWM这件事,说简单也简单,配好定时器就能出波形。但要说深,里面涉及的东西真不少:从物理层的开关损耗,到电路层的驱动拓扑,再到应用层的控制算法。我这些年调过的PWM项目,从简单的LED呼吸灯到复杂的电机伺服控制,每次都能学到新东西。希望这篇总结能帮你少走点弯路,把PWM这个工具用得更加得心应手。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询