ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PWM电机调速全解析:从占空比原理到51与STM32实战代码

PWM电机调速全解析:从占空比原理到51与STM32实战代码 在电机控制这个圈子里摸爬滚打这些年PWM 电机调速算是我接触最多、也最常被身边的人问起的一个话题。不管是搞智能车、做机器人小车还是调一个工业风扇只要涉及直流电机最终几乎都会落到PWM脉冲宽度调制这玩意上。很多刚入门的同学总觉得 PWM 很玄乎动不动就“占空比”“频率”“死区”一堆名词糊上来其实它本身的思想极其简单就是一扇不停开关的门通过控制门开着的时间比例来控制流过电机的能量。正因为简单、高效、容易用代码实现PWM 才成了电机调速事实上的标准方案。这篇文章我想从原理到代码完完整整地把 PWM 电机调速这件事讲透。整篇文章会基于我实际调试过的 51 单片机和 STM32 两套方案展开把占空比、频率、死区、故障保护这些关键点全部落到实处给出可以直接抄的代码也把我在调试中踩过的坑、排查过的“飞车”和抖动问题一并写出来。无论你是刚接触单片机的学生还是正在做产品样机的工程师这篇文章都能让你少走不少弯路。1. 先搞清楚底层逻辑PWM 调速到底在调什么1.1 直流电机的转速本质上是“电压”决定的直流电机比如最常见的空心杯电机、带减速箱的 N20 电机、大一点的 370 电机转动靠的是电枢电流在磁场中受力而电流的大小由外加电压和回路阻抗决定。你可以做个最简单的手动实验拿一节干电池直接接在电机两端电机转得飞快把两节电池串联转速明显上升用一个可变电阻串进回路电阻调大、转速下降。这些操作本质上都是在改电机两端的“平均电压”。所以调速的核心问题就是怎么方便、高效、可控地给电机提供一个可变的平均电压传统方案的毛病非常明显用滑动变阻器调速能量全烧在电阻上了而且电阻发热严重效率极低用线性稳压器调速同样是发热大户大电流下根本没法用用 DCDC 降压动态响应和电路复杂度又有点高对小项目来说过于重。而 PWM 调速就聪明多了我不需要把电压真正“变成”某个中间值而是用高速开关把电源电压整段整段地“切”给电机只要开关速度足够快电机的转动惯量和线圈电感就会自然地把你给的方波“滤波”成一个等效的平均电压转速也就跟着稳定下来。1.2 占空比、频率、等效电压这三者的关系必须滚瓜烂熟PWM 信号本身的参数就三个但每个都很关键。占空比Duty CycleD一个周期内高电平时间与整个周期的比值。60% 占空比意味着在一个周期里有 60% 的时间电机两端是满电压40% 的时间是断电状态。等效电压的计算公式特别简单[ V_{avg} D \times V_{source} ]比如 12V 电源、D0.45那电机感受到的平均电压就是 5.4V。这个公式看似简单但实际调试时很多人会忽略一个细节这只是理想情况电机内部有电感、有反电动势实际电流波形并不是跟随占空比线性变化的所以转速也并不会严格和占空比成正比。不过这并不影响我们把它当作工程上的近似依据绝大多数项目按这个思路设计完全够用。频率Frequencyf每秒钟开关多少次。低频时电机会一顿一顿地转甚至能听到明显的“嗡嗡”声高频时电机运转平滑噪声也更小。但频率不能无限高太高了开关管的开关损耗、MCU 频繁中断的负担都会上来大功率 MOS 管在高频下发热甚至会非常明显。关于频率选择我一般遵循这几个经验值普通直流减速电机 5kHz~20kHz 都比较合适大功率电机或对噪声敏感的场景选 20kHz 以上超过人耳可听范围舵机的控制信号固定是 50Hz这个不能乱来如果是通过 MOSFET 驱动还要考虑管子的开关特性动作慢的大功率管不适合高频。后面我会再单独说频率选型的细节。1.3 为什么 PWM 调速效率高而电阻调速不行从小项目到大功率驱动PWM 方案几乎通吃这背后是能量转化效率的差异。电阻调速时多余的功率全部变成热量消耗在电阻上电机实际能得到的功率很低。极限情况下电阻调到一半位置电机和电阻各分一半功率效率只有 50%。PWM 调速时MOS 管只有两种状态完全导通和完全截止。完全导通时管子的导通电阻只有几毫欧到几十毫欧压降极小完全截止时没有电流流过也不消耗功率。所以开关状态下的损耗非常小理论上效率可以达到 90% 以上。用一句通俗的话总结电阻调速是“把多余的电变成热量扔掉”PWM 调速是“把电不必要的时间不给它”前者是浪费后者是节制效率当然不可同日而语。2. 动手前必须想清楚的三个硬件问题光会写代码不行硬件上有些问题不提前考虑调试时会让你怀疑人生。下面这三个问题是我自己在做项目时反复踩过坑之后总结出来的强烈建议你动手之前先过一遍。2.1 开关频率到底选多少这个参数不值得拍脑袋关于频率我见过太多人随手填个数值导致电机异响或驱动器发烫。选频率时脑子里要有这样一张表场景推荐频率原因小型直流减速电机/玩具电机5kHz~10kHz兼顾噪声和开关损耗大功率直流电机如电动推杆、电钻类10kHz~20kHz频率太低电流纹波大电机会发抖驱动感性负载需要降低噪声20kHz 以上超出人耳听觉范围避免刺耳的啸叫舵机信号50Hz舵机协议固定占空比范围 0.5ms~2.5ms步进电机细分驱动参考20kHz~40kHz需匹配驱动芯片的斩波频率我之前做一个 24V 直流电机项目最初用 1kHz 的频率结果电机低速时一顿一顿的跟打太极似的而且开关噪声非常明显整个实验室都能听到。后来改成 16kHz问题立刻消失了。但要注意频率调高以后如果 MOS 管的栅极驱动能力不够开关波形会变得很烂上升沿和下降沿变缓功耗反而增加。所以频率选型要综合 MCU 能力、MOS 管特性和电机噪声综合考虑不能孤立地看某一个指标。2.2 死区不是 H 桥专属电机调速同样要重视如果你只用 PWM 控制电机的一端电机另一端接地那确实不需要考虑死区问题。但大多数应用里我们用的是 H 桥电路也就是四个管子组成的全桥结构用来实现正反转和调速。H 桥在切换方向的瞬间如果上桥和下桥同时导通就等于把电源短路了电流会瞬间飙升轻则管子发热重则直接炸管。解决这个问题的手段就是死区Dead Zone也就是在上下桥切换时留一段“两边都关断”的时间。在代码上的实现思路是当你需要改变 PWM 输出方向或者占空比时先强制让所有管子关闭一小段时间比如 1μs~5μs再打开目标管子。STM32 的高级定时器TIM1/TIM8一般内置了死区发生器配置好寄存器就能自动插入如果是用 GPIO 软件模拟 PWM就得自己在切换时加延时。还有一点经验PWM 频率越高死区时间相对占比就越大对输出能力的影响也越明显所以低频应用下死区可以设置得保守一点高频应用下要在安全的前提下尽量缩短死区。2.3 故障保护与“飞车”代码要能兜住硬件的底“飞车”这个词是我在调试现场跟做动力系统的同事学来的指的是 PWM 占空比异常升高或处于不受控状态时电机转速瞬间飙升到不可控的危险状态。造成飞车的常见原因有几个MCU 跑飞或死机导致 PWM 输出端保持恒高电平程序里除零或数据溢出把占空比算成 100% 以上外部干扰导致 IO 口电平被拉高。处理飞车不能只靠代码但代码里确实可以做很多事。我的习惯做法是三层保护第一层程序逻辑层。所有占空比变量统一用 unsigned char 或 uint16_t并且在赋值前做饱和处理超过上限就截断防止脏数据进入比较寄存器。第二层硬件使能层。在 MOS 管驱动芯片的 EN 引脚上接一个下拉电阻平时默认失能只有当程序初始化完成、确认所有占空比参数正常后才将 EN 拉高。一旦程序检测到异常看门狗超时、传感器不合理等立刻把 EN 拉低让电机自然滑行停止。第三层速度反馈层。如果项目预算允许加装编码器或霍尔测速在程序里做转速闭环当检测到转速超出设定值的 1.5 倍时直接切入刹车逻辑或者封锁 PWM。这一点在做大功率电机时几乎是必须的不要指望靠人眼发现飞车再手动断电。3. 代码实现从 51 单片机到 STM32 的两套完整方案代码这部分是很多人最关心的我会给出两套方案一套是 51 单片机用定时器模拟 PWM非常适合理解原理而且可以无缝移植到任何没有硬件 PWM 外设的平台上另一套是 STM32 用高级定时器输出硬件 PWM效率高、精度高适用于正式的工程产品。3.1 51 单片机模拟 PWM理解原理的第一课51 单片机很多型号没有专用的 PWM 外设STC 的一些增强型型号有硬件 PWM但基础 8051 内核没有所以最通用的做法是用定时器中断来控制 IO 口的翻转。思路非常简单设置一个定时器中断周期为 PWM 的一个最小时间片中断里维护一个计数器 cnt每次中断加一当 cnt 小于占空比阈值时输出高电平否则输出低电平。// 基于 8051 的软件 PWM 实现 // 适用平台STC89C52 / AT89S52 等基础 51 内核 // 频率说明定时器每 50us 中断一次PWM 周期 50us * 100 5ms即 200Hz // 增大 PWM_PERIOD 可降低频率反之提高频率 #include reg52.h #define PWM_PERIOD 100 // PWM 周期单位中断次数 #define PWM_MAX 100 // 占空比最大值对应 100% sbit PWM_PIN P1^0; // PWM 输出引脚接驱动电路输入端 sbit EN_PIN P1^1; // 驱动使能引脚低电平禁用 static unsigned int pwm_cnt 0; static unsigned char pwm_duty 50; // 初始占空比 50% // 定时器 0 初始化50us 中断一次 // 使用 12MHz 晶振定时器工作在模式 116 位 void timer0_init(void) { TMOD 0xF0; TMOD | 0x01; // 定时器 0模式 116 位 TH0 0xFF; // 50us 定时初值计算(65536 - 50*12/12) / 256 TL0 0xCE; ET0 1; // 开定时器 0 中断 EA 1; // 开总中断 TR0 1; // 启动定时器 } // 设置占空比输入范围 0~100 void pwm_set_duty(unsigned char duty) { if (duty PWM_MAX) { duty PWM_MAX; // 饱和处理防止非法值导致飞车 } pwm_duty duty; } // 使能或禁用 PWM 输出 void pwm_set_enable(unsigned char en) { EN_PIN en ? 1 : 0; } // 定时器 0 中断服务函数 void timer0_isr(void) __interrupt 1 { TH0 0xFF; // 重新装载初值 TL0 0xCE; pwm_cnt; if (pwm_cnt PWM_PERIOD) { pwm_cnt 0; } if (pwm_cnt pwm_duty) { PWM_PIN 1; // 高电平区间 } else { PWM_PIN 0; // 低电平区间 } } void main(void) { unsigned char speed 0; timer0_init(); pwm_set_enable(1); while (1) { // 简单演示循环改变占空比从 0 到 100 再到 0 for (speed 0; speed 100; speed) { pwm_set_duty(speed); delay_ms(50); // 需要自行实现延时函数 } for (speed 100; speed 0; speed--) { pwm_set_duty(speed); delay_ms(50); } } }这段代码的关键细节有三个。第一是定时器初值的计算12MHz 晶振、模式 116 位下50us 对应计数次数是50 * 12 / 12 50所以初值是65536 - 50 65486十六进制就是0xFFCE这对应代码里的TH00xFF, TL00xCE。如果你用的是 11.0592MHz 晶振需要重新计算这个坑我当年踩过直接复制代码后定时时间完全不对。第二是占空比饱和处理调用pwm_set_duty传 200 这种非法值时程序不会把比较寄存器设成 200因为 cnt 永远不会大于 100最后实际输出 100% 占空比电机就满速转了这其实也是一种隐藏的“飞车”隐患。第三是 PWM_PIN 输出的信号不能直接驱动电机必须经驱动电路三极管、MOS 管或者驱动芯片放大电流。3.2 STM32 硬件 PWM精度和效率同时在线STM32 的定时器天生就是为 PWM 而生只要配置好时基和比较值硬件会自动完成计数、比较、翻转输出整个过程不占用 CPU。我用得比较多的是 STM32F103 系列下面这段代码基于标准外设库实现TIM2 的通道 1 输出 PWM频率 16kHz占空比可调。// 基于 STM32F103 标准外设库的 PWM 电机调速实现 // 频率计算72MHz / (711) / (491) 20kHz // 占空比范围0~999对应 0.0%~99.9% #include stm32f10x.h // 初始化 TIM2 CH1 输出 PWM引脚 PA0 void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 1. 开启 TIM2 和 GPIOA 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置 PA0 为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 3. 配置定时器时基20kHz PWM TIM_TimeBaseStructure.TIM_Period 999; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler 71; // 预分频系数 72-1 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // 4. 配置 PWM 模式 1占空比初始为 0 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); // 5. 使能 TIM2 的预装载寄存器 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM2, ENABLE); // 6. 启动定时器 TIM_Cmd(TIM2, ENABLE); } // 设置占空比范围 0~1000对应 0~100.0% void PWM_SetDuty(uint16_t duty) { if (duty 1000) { duty 1000; } TIM_SetCompare1(TIM2, duty); }这段代码的频率计算是我经常要跟人解释的点STM32F103 的内部时钟是 72MHz预分频器设置为 71也就是对 72MHz 进行 72 分频得到 1MHz 的计数时钟自动重装载值设为 999计数器从 0 加到 999 需要 1000 个计数周期所以 PWM 频率就是1MHz / 1000 1kHz不对等等这里要仔细算一下。1MHz 计数时钟下 1000 个周期就是 1ms对应 1kHz那如果我要 20kHz计数时钟应该是 20MHz预分频应该是 72MHz / 20MHz 3.6没法整除。所以上面的代码配置实际算下来是72MHz / (711) / (9991) 72MHz / 72 / 1000 1kHz注释里的 20kHz 是我故意写错的吗不是请仔细看TIM_Prescaler 71意味着分频系数是 72TIM_Period 999意味着计数值是 100072MHz / 72 1MHz, 1MHz / 1000 1kHz。如果需求是 20kHz那预分频应该是72MHz / 20kHz / 1000 3.6取整后可以设置预分频为 3即分频系数 4此时计数时钟 18MHz又和 1000 个计数值组合得到 18kHz。所以正确的 20kHz 配置可以这样预分频 3、重装载值 89972MHz / 4 / 900 20kHz。我在这里把实际计算过程写出来就是提醒大家不要盲信网上的代码注释一定要自己按公式算一遍。这一点非常重要因为频率差一点点在示波器上能看出来但算错了 PWM 波的周期可能导致电机控制出现难以排查的怪问题。3.3 封装一个可复用的调速模块让代码在不同项目之间无缝迁移不管是 51 还是 STM32最终我建议你把手动改占空比的逻辑收拢到一个独立的模块里对外只暴露“设置速度”这一个接口。这样做的好处很明显调速策略比如加减速曲线、PID 闭环改起来不动底层驱动换硬件平台时只需要重写底层业务层代码一行都不用改。下面是我在实际项目中常用的一个接口设计用 C 语言伪代码表示// motor.h #ifndef __MOTOR_H #define __MOTOR_H #include stdint.h #define MOTOR_PWM_MAX 1000 // 占空比千分比0~1000 // 初始化电机驱动 void motor_init(void); // 设置电机目标速度speed 范围 0~1000 void motor_set_speed(uint16_t speed); // 紧急停止封锁 PWM电机滑行或刹车 void motor_emergency_stop(void); // 查询当前 PWM 占空比 uint16_t motor_get_duty(void); #endif实现时需要注意的隐藏细节motor_set_speed内部要同时做上限判断和变化率限制。变化率限制就是让占空比不能突变太大比如每次调用最多只能变化 20这样电机会软启动避免启动瞬间电流过大。我调试过一个项目直接把占空比从 0 拉到 1000电源 12V 直接被拉垮了板子上的其他传感器全部复位。加了 50ms 的斜坡加速之后启动电流降了一半还多。4. 从代码到转起来完整调试过程记录4.1 示波器测量确认你的 PWM 信号真的“对”写好了代码先不要急着接电机第一步应该用示波器看 PWM 波形。这是整个调试过程中最关键的一步因为如果没有示波器验证你根本无法判断是软件配置错了还是硬件没接对。把示波器探头接到 PWM 输出引脚注意把探头的地夹子和板子共地调节触发电平到波形中间位置你应该看到频率和占空比都符合预期的方波。具体检查三步第一步检查频率是否对。读数如果和计算值偏差超过 5%优先检查晶振频率和分频配置。第二步检查占空比是否可调。调用motor_set_speed改变目标速度看波形高电平宽度是否随速度改变而变化。第三步检查上升沿和下降沿是否陡峭。如果边沿出现明显的斜坡而不是垂直上升说明驱动能力不足或负载电容太大这会带来开关损耗和发热问题。没有示波器的同学也别慌有两条路可以走一是在 PWM 输出脚接一个 LED 和限流电阻占空比越大 LED 越亮用肉眼看亮度变化也能大致判断占空比在变二是用一个万用表直流电压挡测 PWM 引脚的电压根据等效电压公式V_avg D * V_source可以反推出当前占空比。比如 3.3V 系统、测到 1.65V那占空比就是 50%。这个方法精度一般但用来做基本验证完全够用。4.2 接上电机后的测试清单从空载到带载逐步加码PWM 波形验证通过后才可以接电机。我建议按照下面的清单逐步测试每一步都确认没问题了再进下一步空载测试电机不接负载输出轴空转的情况下从 0% 占空比慢慢加到 10%、20%、30%观察电机是不是平稳起步、有没有抖动、有没有异响。如果刚加电时电机纹丝不动占空比加到 15% 左右才突然跳起来说明启动电压偏高需要在代码里做软启动补偿。方向测试如果电路支持正反转测试换向是否顺畅。重点听换向瞬间有没有金属碰撞声其实是电流冲击声如果声音刺耳或者电源指示灯闪烁说明死区时间不够需要增加死区。带载测试给电机加上正常负载测试中高转速下的稳定性。这阶段最容易发现的问题有两个一个是带载能力不足电机转速被拉得很低这是电源功率不够的表现另一个是明显的高频啸叫可以尝试调整 PWM 频率来规避。长时间运行测试满载运行 30 分钟以上重点摸三个位置的温度MOS 管表面、电机外壳、电源模块。如果 MOS 管温度超过 80 度就要认真思考散热问题了常见的改进方向是降低 PWM 频率减少开关损耗或者换用导通电阻更小的管子。4.3 用数据说话记录转速和占空比的对应关系调试了一圈之后建议你花点时间把占空比和实测转速记录下来做成一个表这张表对你后面的控制策略大有帮助。下面是我调试一款 12V 减速电机减速比 1:30空载额定转速约 300RPM时记录的几组数据占空比 (D)等效电压 (V)实测转速 (RPM)备注10%1.20未达到启动电压电机不动20%2.448刚起步转速不稳定30%3.692正常转动50%6.0158线性度较好70%8.4209线性度较好90%10.8243接近额定转速100%12.0265空载转速减速后这张表透露了几个重要信息死区区间10%~20% 之间有一段不响应区、近似线性区30%~70%、饱和区90% 以上。如果要做精密的闭环控制你必须对死区区间进行补偿也就是在程序里把速度指令映射到实际转速时跳过那一段不响应区域否则低速控制会忽快忽停非常难用。5. 高频问题排查实录从“飞车”到抖动的完整定位思路5.1 电机突然满速“飞车”先查这几个点“飞车”是我接到过最多的求助类型现象非常吓人电机上电后突然以最高速狂转关也关不掉只能拔电。根据我的排查经验优先级如下第一占空比变量是否被意外赋值成超过最大值了。C 语言里数组越界、指针偏移错误都可能导致某个寄存器被改掉最好在关键变量上打印日志或者用仿真器实时观察。第二检查定时器/比较寄存器是否配置正确。STM32 的 ARR 和 CCRx 关系错了比如 CCRx 大于 ARR硬件 PWM 会一直输出高电平电机当然满速转。第三检查使能引脚和 MOS 驱动逻辑。有些驱动 IC 的 EN 是高电平有效有些是低电平有效接反了会导致上电就导通。排查建议在程序里加一个启动延时上电后先等待 500ms如果期间检测到任何异常标志直接禁止 PWM 输出。这个延时虽然简单但能挡掉不少上电瞬间电平不稳导致的误动作。另外看门狗一定要加程序跑飞后至少能把系统拉回到复位状态。5.2 电机低速抖动、震动明显找频率和占空比的问题低速抖动在 PWM 调速里极其常见尤其在小占空比下。抖动的原因往往是电流断续低速时线圈电感储能不足电流断续导致转矩脉动电机的响应又跟不上。解决思路有三个第一个是适当提高 PWM 频率让每个周期内电流更连续第二个是如果驱动 IC 支持开启纹波抑制模式第三个是在低速区改用电流闭环或者加一点死区补偿。如果你用的是 51 模拟 PWM还要注意中断里取余和比较操作是否耗时太多导致中断周期不稳定时间抖动会造成额外的速度波动实测中这个原因很容易被忽视。5.3 电机运行有“吱吱”声多半是频率调到了听觉灵敏区电机啸叫的声源其实不是机械摩擦而是电机绕组在 PWM 开关频率下的微小振动发出的声音。人耳对 2kHz~5kHz 的声音特别敏感所以如果你的 PWM 频率恰好落在这个区间电机就会发出刺耳的啸叫。解决非常简单把频率挪到 16kHz 以上多数人听不到或者降到 1kHz 以下变成低频嗡嗡声没那么刺耳。我做 12V 直流电机时把频率从 4kHz 调到 20kHz啸叫立刻消失但代价是 MOS 管温度比原来高了大约 5 度左右所以在对噪声敏感的场景里需要权衡散热方案。5.4 速度虽然能调但线性度特别差怎么处理线性度差的典型表现是占空比 20% 和 50% 时转速差别不大但 60% 以后突然加速很猛。这主要是电机电枢电阻和反电动势共同导致的非线性占空比小时电流小、转矩小占空比大时电流和转矩关系又进入另一个区间。解决办法分应用场景如果只是做风扇这类开环控制那用户体验优先可以在软件里加一条映射表把占空比-转速曲线人为“扳直”如果是做小车或者云台这类需要精确速度控制的场景必须上闭环用编码器反馈转速给 PID 调节器让 PID 去动态修占空比。我个人的经验是纯开环的线性度优化做到一定程度就会遇到天花板追求高精度控制最终都要走闭环路线这是一个无法绕开的选择。5.5 常见问题速查表现象可能原因排查顺序与解决方案电机完全不动占空比为 0、驱动未使能、MOS 管损坏示波器查 PWM 波形量驱动芯片输出替换 MOS 管电机只有高速档没有低速档占空比下限太高、PWM 频率与电机不匹配检查代码占空比下限检查启动电压曲线电机运行时尖叫声明显PWM 频率落在 2kHz~5kHz 敏感区调整频率到 16kHz 以上或降到 1kHz 以下电机飞车、无法控制CCRx 配置超限、变量被改写、上电默认占空比过高检查寄存器配置做占空比饱和限制增加使能控制电机发热严重电流过大、频率过高导致开关损耗大测量电机电流换低导通电阻 MOS适当降频电源电压被拉垮启动电流过大、电源功率不足加软启动斜坡更换更大功率电源加母线电容正反转切换时板子复位死区不足导致短路电流冲击增加死区时间检查上下桥驱动逻辑互锁6. 从直流电机到舵机与高级定时器PWM 的边界延展搞明白了直流电机的 PWM 调速其实 PWM 的很多衍生应用你也能很快上手。这里我只挑两个最常见的扩展方向说因为它们用的底层技术和电机调速完全同源只是参数和协议不一样。第一个是舵机控制。舵机跟普通直流电机的区别在于它内部的直流电机只是动力源真正的控制目标是一个角度位置。舵机的控制信号固定为 50Hz周期 20ms通过脉冲宽度来指定角度0.5ms 对应 0 度、1.5ms 对应 90 度、2.5ms 对应 180 度。如果用 STM32 的定时器输出这对 PWM频率那栏直接填 50Hz占空比计算用脉冲宽度折算就行。这里最常见的坑是有人把控制舵机的 50Hz PWM 和调速电机的 20kHz PWM 混用一个定时器结果两边参数互相干扰舵机乱转、电机乱抖。第二个是 STM32 高级定时器的中心对齐模式和死区设置。当你把 H 桥做到一定程度会希望 PWM 的对称性更好、控制更精细这时候就该接触中心对齐模式了。普通 PWM边沿对齐是计数器从 0 加到 ARR然后立刻跳回 0中心对齐是计数器先加到 ARR 再减回 0产生的波形以中间时刻为对称轴。中心对齐模式下的 ADC 采样时刻设置、死区插入这些细节在官方参考手册里写得很分散实际调试时建议你把测得的波形截图保存下来标注好死区位置方便以后对照。TIM1/TIM8 的高级定时器里有一个TIM_BDTR寄存器专门控制死区时间我最近一次调试时设了 1μs 左右在 16kHz 频率下实测效果良好。关于 555 PWM 电路这类纯硬件方案在不需要 MCU 的场景里依然有价值。用 555 搭建的多谐振荡器配上电位器调整占空比可以做出一个非常便宜的模拟调速器输出频率由 R1、R2 和 C 决定。网上相关的电路图和计算工具很齐全适合不想写代码的极简场景。不过它的缺点是占空比调整范围和线性度都不好做得很精确一旦项目需要闭环555 的方案就得让位给单片机了。我在实际项目里的体会是PWM 电机调速真正难的不是代码而是对“开关状态”这个底层逻辑的理解以及调试时把原理、电路、代码三者对应起来的能力。很多时候你盯着代码看半天找不到问题拿示波器量一下波形答案马上自己就跳出来了。所以最后再分享一个我自己坚持了很多年的习惯凡是涉及 PWM 输出无论多急的调试任务先把波形测清楚再接电机。这个习惯帮我省下的时间和换掉的管子真的数都数不清。
返回列表