4. 传热学基础

PWM驱动,说白了就是让微控制器输出一串宽度可调的脉冲。压缩机调速、电子风扇无级变速,全靠它。我刚开始做这个的时候,以为配个定时器、设个占空比就完事了。结果电机嗡嗡叫,管子烫得能煎鸡蛋……嗯,后来才明白,频率选不对、死区没设好,系统根本跑不稳。

4.1 定时器配置:PWM的“心脏”

绝大多数MCU都用高级定时器(如STM32的TIM1/TIM8)来生成PWM。为什么?因为这类定时器有互补输出、刹车功能和死区插入硬件,专门为电机控制设计的。

配置步骤其实不复杂,我一般按这个顺序来:

  1. 时钟使能:打开定时器时钟和GPIO时钟。
  2. GPIO复用:把引脚设为复用推挽输出,注意查数据手册里的AF号。
  3. 时基结构体:设置预分频器(PSC)、自动重装载值(ARR)、计数模式。
  4. 输出比较结构体:选择PWM模式1或2,设置极性、使能输出。
  5. 死区配置:在刹车和死区结构体里填死区时间。
  6. 启动:使能预装载、生成更新事件、使能定时器。

核心公式: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驱动开发的三个核心模块串起来了:

PWM驱动开发核心知识体系 定时器配置 • 时钟使能 → GPIO复用 → 时基结构体 • 输出比较结构体 → 死区配置 → 启动定时器 频率选择(25kHz典型值) • 人耳听觉上限20kHz → 25kHz完全静音 • 开关损耗与电流纹波的平衡点 死区时间设置 • 防止上下桥臂直通短路 • 死区 ≥ (关断延迟 + 驱动延迟) × 1.5 避坑要点 1. 频率别卡边界 2. 死区宁大勿小 3. 调试阶段先确认 4. 高温下关断变慢 5. 留20%余量 6. 用示波器看波形 7. 别信仿真数据 8. 实测才是王道

4.5 调试实战:示波器上看什么?

配置写完了,怎么验证对不对?我一般用双通道示波器看三样东西:

  1. 频率是否准确:测PWM输出引脚,看周期是不是40μs(25kHz)。
  2. 死区时间:同时看上下桥臂的驱动波形,放大到ns级,测量两路都低电平的时间。
  3. 占空比线性度:从0%到100%扫一遍,看实际占空比和设定值是否一致。

注意:千万别用万用表测PWM频率!万用表只能测平均电压,频率一高读数全是错的。我见过有人拿万用表调PWM,调了半天电机不转,还以为是代码写错了……