4. 传热学基础
PWM驱动,说白了就是让微控制器输出一串宽度可调的脉冲。压缩机调速、电子风扇无级变速,全靠它。我刚开始做这个的时候,以为配个定时器、设个占空比就完事了。结果电机嗡嗡叫,管子烫得能煎鸡蛋……嗯,后来才明白,频率选不对、死区没设好,系统根本跑不稳。
4.1 定时器配置:PWM的“心脏”
绝大多数MCU都用高级定时器(如STM32的TIM1/TIM8)来生成PWM。为什么?因为这类定时器有互补输出、刹车功能和死区插入硬件,专门为电机控制设计的。
配置步骤其实不复杂,我一般按这个顺序来:
- 时钟使能:打开定时器时钟和GPIO时钟。
- GPIO复用:把引脚设为复用推挽输出,注意查数据手册里的AF号。
- 时基结构体:设置预分频器(PSC)、自动重装载值(ARR)、计数模式。
- 输出比较结构体:选择PWM模式1或2,设置极性、使能输出。
- 死区配置:在刹车和死区结构体里填死区时间。
- 启动:使能预装载、生成更新事件、使能定时器。
核心公式:PWM频率 = 定时器时钟 / (PSC + 1) / (ARR + 1)
占空比 = 比较值(CCR) / (ARR + 1) × 100%
举个例子,我用STM32G4做压缩机驱动,主频170MHz,想要25kHz的PWM:
// 时基配置
TIM_TimeBaseInitTypeDef tim_base = {0};
tim_base.Prescaler = 0; // 预分频1分频
tim_base.Period = 6800 - 1; // ARR = 6799
tim_base.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; // 中央对齐模式
// 频率 = 170MHz / 1 / 6800 = 25kHz
// 输出比较配置
TIM_OCInitTypeDef tim_oc = {0};
tim_oc.OCMode = TIM_OCMODE_PWM1;
tim_oc.Pulse = 3400; // 50%占空比
tim_oc.OCPolarity = TIM_OCPOLARITY_HIGH;
tim_oc.OCNPolarity = TIM_OCNPOLARITY_HIGH;
tim_oc.OCIdleState = TIM_OCIDLESTATE_RESET;
tim_oc.OCNIdleState = TIM_OCNIDLESTATE_RESET;
我的习惯:ARR值尽量取整,方便后续占空比计算。比如25kHz对应6800,那1%占空比就是68个计数单位,脑子转得快。
4.2 频率选择:为什么是25kHz?
很多人问我:“为什么压缩机PWM频率选25kHz,而不是10kHz或50kHz?”
我做过对比测试,给你列个表:
| 频率范围 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 1kHz ~ 5kHz | 开关损耗低 | 人耳可听噪声大,电机振动明显 | 老式交流压缩机 |
| 10kHz ~ 20kHz | 噪声开始减小 | 铁损增加,驱动IC发热 | 部分直流无刷风扇 |
| 25kHz | 人耳听不到,损耗适中 | 对MCU定时器精度要求略高 | 压缩机、电子风扇主流选择 |
| 40kHz ~ 100kHz | 电流纹波极小 | 开关损耗大,驱动芯片贵 | 高精度伺服、医疗泵 |
25kHz这个值,其实是行业多年摸索出来的平衡点。人耳听觉上限约20kHz,25kHz完全听不到“滋滋”声。同时,IGBT和MOSFET在这个频率下开关损耗可控,不用加太大的散热器。
我曾经踩过的坑:有一款电子风扇项目,我选了20kHz,想着刚好超过人耳范围。结果量产后有客户投诉“高频啸叫”。查了半天,发现是电机定子谐波共振,20kHz的2次谐波40kHz和机械结构产生了差拍。换成25kHz后问题消失。所以,别卡着边界值选频率,留点余量。
4.3 死区时间设置:防止“直通”的保命符
死区时间,就是上下桥臂同时关断的那段空白期。没有它,一个管子还没关断,另一个就导通了——瞬间短路,电流飙升,管子直接炸掉。
我见过最惨的一次,实习生把死区设成0,上电瞬间IGBT炸了三个,PCB铜箔都烧断了。嗯,那场面,终身难忘。
死区时间怎么算?主要看三个因素:
- 开关器件关断延迟:IGBT一般几百ns,MOSFET几十ns。
- 驱动芯片传输延迟:光耦隔离驱动通常100~500ns。
- 温度漂移:高温下关断时间会变长,留20%~30%余量。
我常用的经验公式:
死区时间 ≥ (关断延迟 + 驱动延迟) × 1.5
例如:IGBT关断延迟400ns,驱动延迟200ns,则死区 ≥ (400+200)×1.5 = 900ns
实际配置时,STM32的死区时间由DTG寄存器控制:
// 刹车和死区配置
TIM_BDTRInitTypeDef tim_bdtr = {0};
tim_bdtr.OSSR = TIM_OSSR_ENABLE; // 运行模式关断状态
tim_bdtr.OSSI = TIM_OSSI_ENABLE; // 空闲模式关断状态
tim_bdtr.LOCK = TIM_LOCKLEVEL_1; // 保护等级
tim_bdtr.DTG = 90; // 死区时间 = 90个时钟周期
// 时钟周期 = 1/170MHz ≈ 5.88ns
// 死区时间 = 90 × 5.88ns ≈ 530ns
tim_bdtr.BKP = TIM_BKP_ENABLE; // 刹车输入使能
tim_bdtr.BKE = TIM_BKE_ENABLE;
tim_bdtr.AOE = TIM_AOE_ENABLE;
我建议:调试阶段把死区设大一点(比如1~2μs),确认系统能正常工作后,再逐步减小到目标值。别一上来就追求极致效率,安全第一。
4.4 知识体系总览
下面这张图,把PWM驱动开发的三个核心模块串起来了:
4.5 调试实战:示波器上看什么?
配置写完了,怎么验证对不对?我一般用双通道示波器看三样东西:
- 频率是否准确:测PWM输出引脚,看周期是不是40μs(25kHz)。
- 死区时间:同时看上下桥臂的驱动波形,放大到ns级,测量两路都低电平的时间。
- 占空比线性度:从0%到100%扫一遍,看实际占空比和设定值是否一致。
注意:千万别用万用表测PWM频率!万用表只能测平均电压,频率一高读数全是错的。我见过有人拿万用表调PWM,调了半天电机不转,还以为是代码写错了……