首页 / 资讯中心 / 文章详情

STM32G431无感BLDC驱动开发:六步换相与PI速度环落地

STM32G431无感BLDC驱动开发:六步换相与PI速度环落地 ★ FEATURED ARTICLE
简介面向嵌入式电机控制开发者与STM32学习者这份基于STM32G431的无刷直流电机驱动及PI速度控制工程完整打通了从底层硬件到闭环调速的实践链路。方案利用STM32G431的HRTIM生成三路互补PWM并配置可编程死区配合霍尔传感器位置反馈和低边电流采样构成稳定的六步换相驱动软件侧通过定时中断读取霍尔边沿估算转速再由PI控制器动态调整PWM占空比以实现目标速度稳定输出。资源共1523个文件压缩包大小43.69MB主体为C源码、头文件与汇编文件同时包含工程配置文件、链接脚本、库文件及hex、map等编译产物适合直接阅读结构、烧录验证和二次修改。目前已有265人参与学习下载对正在设计无刷电机驱动板或调试PI参数的项目开发者具有直接参考价值。1. 无刷电机不是「给电就转」STM32G431、六步换相与PI速度环的落地路径第一次把三相线接到自制的STM32G431驱动板上时我以为只要给PWM无刷电机就会转。结果电机只嗡了一声、抖了一下母线电流飙到2A。折腾到后半夜才明白BLDC是「功率级、换相时序、位置检测」缺一不可的系统要先知道转子在哪再决定哪两相通电转起来之后还要一个PI速度环把转速绷在目标值上。这里把这条路径拆开讲三相全桥与反电动势检测、方波六步换相、三段式启动、PI速度闭环以及几个能省两天时间的坑。适合用G431做风机、电动工具或小功率无刷驱动想从「能转」进阶到「稳转」的工程师。2. 驱动级与采样电路把三相全桥、反电动势检测和PI的「眼睛」先搭稳2.1 三相全桥与栅极驱动选型分立MOS、预驱芯片与集成MOS的边界功率级是电机驱动里最不能省的部分。BLDC需要三相全桥六个N沟道MOS组成三个半桥每个桥的上管接母线正、下管接母线负电机三相线分别接在三个桥臂中点。所谓方波驱动就是让这些管子按120°导通规律两两组合轮流工作悬浮的那一相用来做反电动势检测。G431本身不带功率管所以功率级要么用「G431 栅极预驱 外置MOS」要么用「G431 集成三相预驱」要么换更集成的「带MOS的电机控制MCU」方案。我一般倾向G431加栅极预驱加外置MOS的组合原因有三母线电压范围宽从12V到48V都能覆盖功率管的散热和电流余量可以按实际负载自己挑G431内置的比较器和运放正好复用给反电动势检测和过流保护。如果做的是5V输入、一两安培电流的小电机直接选集成MOS驱动的电机控制MCU画板面积能小很多原理图也接近一个黑匣子不用自己算栅极驱动电流。几个参照方向列在下面方案母线电压面积调试难度适用场景分立MOS 栅极预驱如IR2103类12V~48V中中通用可换大功率MOS三相栅极驱动如DRV8301类6V~60V小低带电流放大、保护齐全集成MOS的电机控制MCU3V~12V极小低5V小功率、散热要求低选功率MOS看两个数漏源耐压按母线电压留两倍裕量比如24V系统至少用40V以上漏极电流按堵转峰值的两倍选。我做过一版24V风机三相全桥用60V/30A的N-MOS栅极驱动用自举方案自举电容取100nF到220nF开关频率20kHz跑一小时后MOS温度只比环境高二十来度。自举电容是常见翻车点太小会导致上管驱动电压不足管子半开半关发热选型时留足裕量。2.2 反电动势过零检测与虚拟中性点无感方波的换相信号怎么来无感方案不用霍尔换相时刻靠反电动势。原理是任意时刻有两相通电悬浮的第三相绕组在旋转磁场里切割磁感线会产生一个与转子位置相关的反电动势。这个反电动势会穿越星形连接的中点电位穿越点就是换相参考点。实际电路上三相各用一只大电阻接到同一节点形成虚拟中性点再把悬浮相电压和虚拟中性点电压一起送到比较器。过零发生时比较器翻转触发MCU记时间戳。分压参数上我常用的做法是每相用10kΩ电阻接到虚拟中点虚拟中点再经过10kΩ/10kΩ分压后进比较器分压中点加一个10nF到100nF的电容滤除开关噪声。电阻太大比较器输入偏置电流会在上面产生误差电阻太小反电动势波形会被拉变形。100nF电容对低频反电动势影响不大但会让过零检测有一二十微秒的延迟高速时要把这延迟算进30°换相补偿里。G431内部就有比较器可以把悬浮相分压信号接进COMP输入过零信号通过EXTI进入MCU省一片外部比较器芯片。这里还牵扯一个方向问题反电动势的极性取决于转子转向和绕组绕线方式。同一块板子换上正绕、反绕绕组不同的电机过零比较器的输出极性可能会反对应换相表也得跟着翻转。我建议在原理图上就把三相电流方向、反电动势采样极性标注清楚组装时按照同一个定义接线这个坑在后面单列一节。2.3 母线电流采样与G431的ADC触发采样点定在PWM中点PI速度环虽然直接出占空比但电流保护和调试数据都离不开母线电流采样。母线电流最省钱的做法是低边采样在三相桥的共源极和地之间串一只10mΩ左右的分流电阻用G431内部运放放大后进ADC。采样电阻选10mΩ、1%精度、足够功率的合金电阻不要用普通贴片电阻堵转时几安培电流在上面产生的热量会让阻值漂移电流数据全偏。采样时机比运放增益更影响结果。PWM开关瞬间母线上有高频震荡如果ADC在开关沿附近触发采到的电流波形全是毛刺。正确做法是把采样点定在PWM计数周期的中点让ADC由定时器触发避开开通和关断沿。下面是一段比较典型的ADC通道配置ADC_ChannelConfTypeDef sConfig {0}; sConfig.Channel ADC_CHANNEL_6; sConfig.SamplingTime ADC_SAMPLINGTIME_10CYCLES; HAL_ADC_ConfigChannel(hadc1, sConfig); // 由定时器触发触发时刻放在PWM中心 hadc1.Init.TriggerMode ADC_EXTERNALTRIGCONV_T6_TRGO; hadc1.Init.TriggerPolarity ADC_TRIGGERPOLARITY_RISING;这段代码把ADC采集交给定时器触发而不是CPU轮询触发沿对齐到PWM中心。SAMPLINGTIME_10CYCLES是采样保持时间一般够用如果母线噪声大可以改到更多周期但采样时间太长会把高频电流细节滤掉。G431的ADC还支持硬件过采样把单次转换换成16次平均效果比软件滤波好基本不占CPU。采样电阻两端还可以加一对100pF左右的小电容做差模滤波这属于电路板级的防抖。这套采样方案容易被别人说成玄学其实多半是采样时刻没对齐。3. 六步换相与无感启动换相状态机、强制换相和切闭环逻辑3.1 六步换相状态表与PWM输出两两导通120°是怎么写出来的方波驱动的核心是一张六步换相表。电机星形接法下任意时刻只有一个上管和一个下管导通导通角度120°电角度六种状态正好覆盖一个电周期。换相表按固定顺序轮换电机就往一个方向转反着轮换就反转。前面说的正绕反绕问题最终也体现在这张表上绕线方向变了反电动势过零的极性翻过来换相表就要用另一套顺序。#define PH_A 0 #define PH_B 1 #define PH_C 2 // 六步换相表{上管相号, 下管相号} const uint8_t comm_step[6][2] { {PH_A, PH_B}, // 状态0A上 B下C悬空 {PH_A, PH_C}, // 状态1A上 C下B悬空 {PH_B, PH_C}, // 状态2B上 C下A悬空 {PH_B, PH_A}, // 状态3B上 A下C悬空 {PH_C, PH_A}, // 状态4C上 A下B悬空 {PH_C, PH_B} // 状态5C上 B下A悬空 }; static uint8_t comm_idx 0; void comm_next(void) { comm_idx (comm_idx 1) % 6; set_phase(comm_step[comm_idx][0], comm_step[comm_idx][1]); }comm_next每换相一次被调用一次comm_idx加一后从状态0到状态1再到状态2依此转一圈。悬浮相自然就是上下管都关闭的那一相它的电压信号接入比较器做反电动势检测。set_phase里要做的是先把上一相的两路PWM关掉再开下一相顺序上先关后开避免瞬间直通。状态0到状态5的顺序千万别自己乱改我见过有人为了修正转向直接把表倒过来结果反电动势过零检测的极性没跟着改电机不仅反转还一顿一顿的。TIM1负责输出六路PWM配置要点是预分频、周期和死区。下面是一个中心对齐、约20kHz的配置周期值按170MHz定时器时钟反推htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_CENTERALIGNED1; htim1.Init.Period 4249; // 170MHz / ((42491)*2) ≈ 20kHz htim1.Init.DeadTime 30; // 死区按TIM1的DTG编码换算先保守取值 HAL_TIM_PWM_Init(htim1);中心对齐的好处是PWM的中间时刻正好是电流最平稳的时刻ADC采样点就选在这里。DeadTime寄存器值的实际宽度要查手册里的DTG编码表不是一个线性数值。先给一个保守的死区能转起来之后再用示波器看上下管栅极波形确认没有直通再逐步减小。3.2 预定位与强制换相静止的电机不可能直接闭环反电动势检测有个硬前提电机在转。静止时反电动势为零比较器输出没有任何有效过零信号所以无感启动必须走三段式预定位、强制换相、切闭环。预定位是给任意一相状态比如状态0通一个固定占空比持续三百毫秒左右让转子停在一个已知位置。然后强制换相按换相表每过一段时间切一次状态换相频率从慢到快、占空比从小到大地往上顶转子被旋转磁场拖着转起来。#define BOOT_ALIGN_MS 300 // 预定位时间视负载惯量加大 #define BOOT_ACCEL 5 // 每个换相周期缩短的微秒数 static uint16_t comm_period_us 2000; // 初始换相周期 static uint16_t boot_duty 850; // 开环占空比初值 void bldc_boot_task(void) { switch (boot_state) { case BOOT_ALIGN: set_phase(PH_A, PH_B); set_pwm_duty(boot_duty); if (sys_tick_ms() BOOT_ALIGN_MS) { boot_state BOOT_RAMP; boot_next_tick sys_tick_us(); } break; case BOOT_RAMP: if (sys_tick_us() boot_next_tick) { comm_next(); // 切换一次换相状态 if (comm_period_us 500) comm_period_us - BOOT_ACCEL; // 换相周期递减转速上升 boot_duty 8; // 同时抬占空比 boot_next_tick comm_period_us; } if (comm_period_us 500) { boot_state BOOT_CLOSE_LOOP; // 转速够了准备切闭环 speed_pi_reset(); } break; default: break; } }强制换相相当于开环的V/F加速换相周期决定电频率占空比决定绕组电压。BOOT_ACCEL是5µs意味着每个换相周期缩短5微秒加速斜率比较平缓带大惯量负载时可以把步长再调小。boot_duty初始850在满量程里大约两成占空比空载能带起来带负载起不来就提高初值。这里最怕的是换相周期减得太快转子跟不上磁场速度电机会原地抖导致启动失败——启动失败的表现不是不转而是电流很大、电机嗡嗡响这时候把BOOT_ACCEL改小基本能解决。3.3 过零窗口与30°换相延迟从检测点换算相时刻闭环阶段不能继续盲换相要等反电动势过零后再过30°电角度执行换相。为什么是30°因为过零点出现在悬浮相反电动势穿越中点的地方此时换相点还在30°之外提前换相会让转矩脉动变大、效率下降。G431的比较器在过零瞬间通过EXTI进入中断中断里记录时间戳再用一个单次定时器在comm_period_us/2之后触发换相——60°换相周期的一半正好对应30°电角度。void comp_zero_cross_irq(void) { uint32_t now sys_tick_us(); uint32_t half_comm comm_period_us / 2; // 30°电角度的延迟时间 trigger_one_shot_timer(now half_comm, comm_next); }comm_period_us来自换相中断里相邻两次换相的时间差它是滚动更新的速度升高时自动变小延迟时间也跟着缩短。这个关联让30°延迟在宽转速范围内都近似成立。如果电机高速时仍有明显脉动通常不是延迟公式错而是反电动势检测路径里的RC滤波产生了额外相位滞后这时要把滤波电容减小或者在高转速段对half_comm做一个负补偿。低速段则反过来反电动势太弱、过零检测抖我一般要求转速达到额定值20%以上才启用过零换相低于这个值继续用强制换相。注意comm_period_us是滚动更新的低速时别直接拿开环表里的初始值去算半周期否则换相点会差出一大截。4. PI速度环离散化从时间常数到限幅再把启动和闭环无缝接上4.1 单环电压模式为什么够用速度环直接出占空比无感方波方案里常见的是「速度环直接出占空比」的单环结构。想加电流内环也不是不行但方波驱动下你很难在每个换相周期拿到可靠的三相电流做内环的意义不大反而增加采样和计算开销。电机对速度的响应是一阶惯性加延迟单环PI把误差经过比例和积分后变成占空比指令已经能把转速稳定在目标值附近负载突变时靠PI自身的调节能力抗。真到了要求更高的场景比如高速低噪声、大负载突变就得放弃方波改FOC用滑模观测器去估算转子位置再上电流环和速度环的双闭环结构——那是另一套工程量这里先把方波单环的路走顺。单环方案的风险在于它没有电流上限约束负载突然变大时PI会拼命加占空比绕组电流就可能冲到很大。所以这个方案必须配两个措施一是PI输出限幅二是堵转检测。堵转检测逻辑放在避坑章专门讲这里先明确一个原则单环不是缺了电流保护是把保护责任从「电流环」移交给了「软件保护」。4.2 位置式PI离散实现与抗饱和Kp/Ki初值给的思路单片机里跑PI周期一般取1ms到5ms太短了换相和ADC任务会把CPU挤爆太长了转速响应迟钝。我用1ms控制周期放到定时器中断里。位置式PI的好处是结构直观积分项含义清楚配积分限幅和输出限幅后就是一个工程上够用的控制器。typedef struct { float kp; float ki; float integral; float integ_limit; // 积分限幅取输出限幅的 1/3 ~ 1/2 float out_limit; // 输出限幅最大占空比 } PI_Ctrl; float pi_update(PI_Ctrl *pid, float ref, float fb) { float err ref - fb; pid-integral err; if (pid-integral pid-integ_limit) pid-integral pid-integ_limit; if (pid-integral -pid-integ_limit) pid-integral -pid-integ_limit; float out pid-kp * err pid-ki * pid-integral; if (out pid-out_limit) out pid-out_limit; if (out -pid-out_limit) out -pid-out_limit; return out; } void speed_pi_reset(PI_Ctrl *pid) { pid-integral 0.0f; }integ_limit是这颗PI的后悔药堵转时误差长期存在没有积分限幅的PI会把积分推到天上输出顶满占空比100%电机堵着不动反而持续加热几分钟就能烧MOS。out_limit对应硬件能承受的最大占空比比如满量程8500里限到7500给死区和开关损耗留余地。Kp的初值可以从「额定转速对应多少占空比」粗算24V电机3000rpm满量程8500线性折算系数约2.8但实际先给0.5再翻倍往上加。Ki从0.01起步加到出现低频振荡就退一点。判断振荡的标准是转速反馈在目标值附近来回摆摆动周期一两秒多半是Ki大了。4.3 从开环强制换相切换到闭环PI复位时机与测速衔接强制换相切到闭环最忌讳的是直接让PI接管占空比开环运行时占空比可能已经加到2000多PI输出重置后从零开始占空比瞬间掉到接近零电机立刻掉速然后PI再追上去追的过程就是一次电流冲击和转速跌落。工程上叫「无扰切换」做法是让PI的输出初始值等于开环占空比或者让PI输出只叠加在开环占空比之上等速度稳定后再慢慢把开环分量让位给PI。速度反馈怎么来无感方案里没有霍尔可以直接用换相周期算转速。相邻两次换相间隔60°电角度乘以6就是一个电周期再除以极对数就是机械周期换算成每分钟转数就是下面这个公式。void comm_isr_measure_speed(void) { static uint32_t last_comm_tick; uint32_t now sys_tick_us(); uint32_t t_comm now - last_comm_tick; // 相邻换相间隔单位us last_comm_tick now; // rpm 60s内换相次数 / 6 / 极对数 // 60000000 / (6 * t_comm * pp) // 10000000 / (t_comm * pp) speed_rpm 10000000.0f / ((float)t_comm * motor_pp); }t_comm单位是微秒motor_pp是电机极对数航模无刷常是7极对或14极别拿「极数」直接代入要除以2。低速时t_comm很大转速分辨率偏粗可以在速度环里做一阶低通滤波时间常数给20ms到50ms高速时t_comm只有几百微秒中断抖动会影响测速精度我一般对连续几次t_comm做中值滤波去掉一个最大一个最小再平均反馈速度就稳了。提示极对数motor_pp是磁极对数电机铭牌标「14极」时这里要填7填错所有转速相关参数全偏。5. 无感BLDC最容易踩的五个坑换相错相、采样噪声与PI饱和排查记录5.1 换相错相与正绕反绕为什么电机反转还哗啦响现象上电强制换相能转但声音发闷、电流偏大或者按给定方向给定电机却往反方向转。原因多数是换相顺序和反电动势过零极性没对应上。无刷电机绕线有正绕、反绕之分绕法不同反电动势极性和中性点的相对关系就不同。同一套换相表在正绕电机上是对的换到反绕电机上过零检测的翻转方向反了相当于换相时刻整体偏移60°电机就拖着电流硬转。解决先断开PWM用示波器同时看两相线端反电动势波形手动盘转子确认三相波形相位差是不是120°以及极性方向。然后在换相表上动刀把comm_step数组倒序走一遍或者把数组里的上下管对调一版逐版试。不要同时改换相表和比较器极性一次只动一个变量不然越改越乱。我现在的习惯是驱动板丝印上标清楚A、B、C相序和反电动势采样极性每换一台电机先对相序再上强电。5.2 低速过零误判与滤波延迟启动瞬间打齿的元凶现象开环强制换相阶段一切正常一切入闭环就咔咔响转速提不上去甚至掉零。原因低速是过零检测最脆弱的区间。反电动势幅值跟转速成正比低速时信号只有几十毫伏叠加PWM开关噪声后噪声比信号还大比较器乱翻转。再加上RC滤波电容带来的相位延迟过零信号晚了那么几十微秒电机实际在错误时刻换相。解决低速段不要依赖过零检测强制换相阶段把切入闭环的门槛抬高比如转速达到额定值25%以上再启用过零换相。滤波电容从100nF收到10nF能明显减小相位滞后还可以用G431内部DAC给比较器设一个正负小门限让比较器输出不是裸过零而是过门限翻转噪声免疫会好很多。判断滤波延迟是否过大的办法是录一段高速波形对比过零信号和换相中断的时间差差值超过理论值的10%就处理。5.3 母线采样噪声与ADC过采样PI输出被毛刺带偏现象速度反馈看着稳但电流波形全是尖刺PI输出在某个值附近抖电机会有周期性轻微嗡响。原因采样时刻没落在PWM中心或者单次采样撞上了开关振铃。我见过最隐蔽的版本是ADC初始化里开了过采样但采样时间没有增加平均出来的数值仍然一个点里混着毛刺。地线上的噪声也会叠加进采样值G431的模拟地和功率地如果在板子上直接贯通反馈信号更难看。解决把ADC触发源改成定时器TRGO触发点卡在PWM中心开G431硬件过采样设成16次平均运放输出和ADC引脚之间加RC低通R给1kΩ、C给1nF左右截止频率大概160kHz既能滤振铃又不拖慢电流响应。采样值还是跳就用软件再做一次一阶滑动平均但别套太多层——软件滤波层数多了电流反馈的延迟会把PI相位裕度吃光稳不住。5.4 PI积分饱和与堵转电流失控堵转到底谁在放热现象把电机堵住转速跌落给定转速没有降到零母线电流在几秒内持续上升随后MOS管烫得不能碰。原因PI输出顶到占空比上限后积分还在涨这个状态叫积分饱和。恢复时积分要反向消掉这一大段累积值电机表现为堵转解除后不恢复、反而先往反方向冲一下。堵转期间占空比100%绕组电流巨大发热的是MOS管和电机线圈不是哪一个单独的「故障点」。解决第一道防线是积分限幅和输出限幅integ_limit设成out_limit的一半让积分最多把输出推到一半饱和区。第二道防线是堵转保护换相周期超过正常值一倍以上同时速度反馈低于给定值30%持续500ms就认定堵转关断PWM并报告故障。注意堵转恢复不要直接重开PWM要先重新走一遍预定位和启动流程否则转子还在锁死状态一上电又满电流。5.5 死区没配上TIM1互补输出上电直通烧管现象程序烧进去一使能PWM输出母线电流瞬间爆表或者某一对MOS在开关瞬间发热半小时后烧管。原因TIM1配置成互补输出但死区没生效。MCU复位后BDTR寄存器有默认值但整套定时器初始化经常会覆盖它尤其是先初始化了PWM功能、再单独补死区配置的时候。上下管栅极信号有一段重叠母线直接通过两个管子短路发热和烧管都在换相沿附近最严重。解决先把DeadTime写进初始化结构体再调HAL_TIM_PWM_Init不要在初始化之后再拿寄存器去补。调试初期改用非互补的单极性调制——上管PWM、下管直接导通输出低电平没有死区问题也能转。等电机跑顺了再用示波器看上下管栅极波形逐步加死区。G431的TIM1死区时间由DTG编码决定不是线性值调试时从大往小调先保证不直通再优化死区时间带来的效率损失。6. 拿什么证明闭环真的闭合斜坡带载测试和三个必查波形6.1 斜坡给定、带载突变和波形检查闭环调试的顺序闭环跑起来不等于闭环调好了。我验证一套无感方波PI方案的固定顺序是三步先查波形再跑斜坡最后带载突变。查波形看三点悬浮相电压过零波形是否干净、换相时刻是否等距、上下管死区时间里有没有直通。这三项过了再进入动态调参。斜坡给定用串口发命令目标转速从0到额定转速按每100rpm步进每步停留100ms左右同时回传PI的给定、反馈和输出。观察点在两个位置低速段有没有跟丢高速段有没有持续超调。低速跟丢说明过零切入门槛太低或启动占空比不足高速超调则减Kp、加Ki超调伴随长周期摆动时优先减Ki。斜坡跑稳之后做一次带载突变正常运行中把负载从空载压到额定负载看转速跌落多少、恢复时间多长。转速跌超过20%先提高Kp恢复时间超过1秒则加大Ki。这套调参顺序不要反着来先动Kp后动Ki出现振荡就先退Ki这是PI调试的老规矩。调完参数后有一个被我反复用来「验尸」的技巧把测速公式里的t_comm和PI输出一起通过串口发出来在电脑上画两条曲线。转速曲线如果是一条贴着给定的直线说明PI闭环是真的闭合的如果曲线有周期性锯齿八成是换相周期测速里混入了过零误判这时候看t_comm的散点图异常毛刺一目了然。任何速度波动问题先在反馈通路上找原因再回头怀疑PI参数——这个顺序帮我省掉了大量玄学调参时间。有一台24V的风机电机我调了一整天转速都稳不下来最后发现是换相中断里多写了一句无关的处理函数把测速时间戳拉偏了几微秒反馈速度里多了一个固定偏差PI自然永远追不平。从那以后我每次测速中断都强制走一遍「进中断先取时间戳、再处理其他逻辑」不让任何任务插在时间戳和换相动作之间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站