简介:这份资源面向嵌入式电机控制开发者与学习者,聚焦意法半导体MCU平台下的无刷直流电机磁场定向控制(FOC)实现,并针对双电机协同场景给出霍尔传感器驱动的完整方案。包内整合了ST MCSDK V5.4.4相关组件,涵盖FOC算法、霍尔位置采样与接口、基于RTOS的多任务调度与双电机同步控制,适合希望从底层驱动到上层算法系统掌握双电机FOC的工程师参考。资源共2000个文件,以1273个C源文件与672个头文件为主体,辅以少量txt说明、cpp代码及工程配置文件,压缩包约26.76MB,目录结构便于按模块检索。目前已有307人学习下载。通过研读可获取霍尔位置计算、电流电压调制、电机状态估计及双电机协调控制等核心实现思路,对理解ST MCU配合霍尔传感器完成高精度双电机FOC控制具有较高参考价值。
1. 双电机霍尔FOC控制:一份能直接跑在板子上的工程包
如果你手头正好有一块 STM32 板子、两个带霍尔传感器的无刷电机,还想用 FOC 把转速环和电流环都跑起来,那这份「ST FOC-双电机控制-霍尔驱动」工程包大概率能省掉你不少从零搭框架的时间。它不是一个只讲原理的 Demo,而是一套基于 ST MCSDK 生成、针对双电机霍尔场景做过裁剪和适配的完整工程,里面包含了电机参数配置、霍尔换相逻辑、双路 PWM 输出以及串口调试接口。我第一次拿到它的时候,最直观的感受是:不用再对着 MCSDK 那一堆自动生成的中间件发懵,工程里已经把双电机需要的资源分配、中断优先级、DMA 通道都理清楚了。适合谁?适合已经跑通过单电机 FOC、现在要上双电机,或者被霍尔换相时序折腾过的嵌入式开发者。下面我就按「这包能干什么 → 怎么把它跑起来 → 怎么改成你自己的板子」的顺序,把里面几个关键模块拆开讲。
2. 工程结构与双电机资源分配:先看清谁占了什么外设
2.1 目录层级与 MCSDK 生成代码的边界
这份工程包解压后,根目录下通常能看到Core、Drivers、Middlewares、MCSDK_v5.x.x这类文件夹,外加一个.ioc文件。Core里放的是main.c、stm32g4xx_it.c、stm32g4xx_hal_msp.c这些用户代码和中断入口;Middlewares下是 ST 的电机控制库,包括MotorControl、MCSDK两个子目录。很多人第一次打开会直接去改main.c,结果发现电机根本不转——因为 MCSDK 的工作流是「先配置再生成」,你改main.c里的初始化顺序,很容易被下一次重新生成覆盖掉。
我一般会先确认.ioc文件里两个电机实例的配置是否完整。用 STM32CubeMX 打开它,在Motor Control选项卡下应该能看到Motor 1和Motor 2两个配置页,每个页里都勾选了Hall Sensor作为位置反馈。如果这里只配了一个电机,那工程里双电机的代码就是手工补的,改起来要格外小心。
/* main.c 中典型的双电机初始化调用顺序 */ MX_MotorControl_Init(); /* MCSDK 生成的电机控制初始化 */ MX_USART2_UART_Init(); /* 调试串口,用于输出转速和状态 */ /* 注意:两个电机的 Init 都在 MX_MotorControl_Init 内部按实例依次调用 */这段代码看起来简单,但MX_MotorControl_Init()内部会依次调用MC_Init()和MCT_Init(),分别对应电机控制底层和电机控制任务层。参数说明上,MC_Init()负责 ADC、PWM、霍尔接口的硬件抽象层初始化;MCT_Init()负责把两个电机实例注册到任务调度器里。如果你在main.c里手动插了别的外设初始化,一定要放在MX_MotorControl_Init()之前,否则可能因为时钟或 GPIO 冲突导致霍尔采样异常。
2.2 双电机 PWM 与 ADC 的通道划分
双电机 FOC 最吃紧的资源就是高级定时器和 ADC 注入通道。以常见的 STM32G4 为例,一份合格的工程会这样分配:电机 1 用TIM1的 CH1/CH2/CH3 输出三路互补 PWM,电机 2 用TIM8的 CH1/CH2/CH3。霍尔接口方面,电机 1 的 H1/H2/H3 接到TIM2的 CH1/CH2/CH3,电机 2 接到TIM3。ADC 则用ADC1和ADC2分别采样两路母线和相电流,注入组触发源分别来自TIM1和TIM8的更新事件。
| 资源 | 电机 1 | 电机 2 | 备注 |
|---|---|---|---|
| 高级定时器 | TIM1 | TIM8 | 互补 PWM + 死区 |
| 霍尔接口 | TIM2 | TIM3 | 捕获模式,6 步换相 |
| ADC 注入组 | ADC1 | ADC2 | 相电流 + 母线电压 |
| 调试串口 | USART2 | 共用 | 分时打印两路转速 |
这张表是我从工程里MC_Config结构体和.ioc配置反推出来的,实际工程里可能用ADC1的多个通道轮流采两个电机,但那样会引入采样延迟,双电机同时跑高速时容易翻车。常见做法是每个电机独占一个 ADC,注入组由各自定时器触发,这样电流环的时序最干净。
2.3 中断优先级与任务调度
MCSDK 默认把电流环放在 ADC 注入完成中断里,速度环放在 SysTick 或一个低速定时器里。双电机场景下,两个 ADC 中断的优先级必须错开,否则会出现一个电机抢中断、另一个电机电流环丢拍的情况。工程里通常把电机 1 的 ADC 中断设为抢占优先级 1,电机 2 设为 2,速度环任务统一放在SysTick里以 1ms 周期轮询。
/* stm32g4xx_it.c 中 ADC 中断优先级配置片段 */ HAL_NVIC_SetPriority(ADC1_2_IRQn, 1, 0); /* 电机1电流环,高优先级 */ HAL_NVIC_SetPriority(ADC3_IRQn, 2, 0); /* 电机2电流环,低优先级 */ HAL_NVIC_EnableIRQ(ADC1_2_IRQn); HAL_NVIC_EnableIRQ(ADC3_IRQn);这里ADC1_2_IRQn是共享中断向量,两个 ADC 共用一个入口,需要在中断服务函数里读ISR寄存器判断是哪个 ADC 触发。参数上,抢占优先级数字越小优先级越高,子优先级在双电机场景下建议都设为 0,避免嵌套过深导致栈溢出。如果你发现电机 2 在高速时电流波形畸变,先查这里的中断优先级是不是被别的外设打断了。
3. 霍尔换相与 FOC 启动:从静止到闭环的完整链路
3.1 霍尔接口的捕获配置与换相表
霍尔传感器输出的是三路方波,每 60 度电角度跳变一次。MCSDK 里用定时器的输入捕获模式来读这三路信号,然后查表得到当前扇区。工程里hall_sensor.c或mc_hall.c中会有一个HALL_Table数组,把三路霍尔状态映射成 1 到 6 的扇区号。
/* 霍尔状态到扇区的映射表,顺序对应 H3 H2 H1 */ const uint8_t HALL_Table[8] = {0, 1, 3, 2, 5, 4, 6, 0}; /* 索引 0 和 7 是无效状态,用于检测霍尔断线 */这个表的索引是(H3<<2) | (H2<<1) | H1,值就是扇区号。参数说明:如果电机转向和表顺序反了,电机会抖动但不转,这时候把表倒序或者把电机三相线任意对调两根即可。我踩过的坑是霍尔安装角度不是标准的 120 度,导致换相点偏移,这时候需要在 MCSDK 的Hall Sensor配置里填Electrical Angle偏移量,一般靠示波器抓反电动势过零点来校准。
3.2 启动阶段的强拖与切换条件
霍尔 FOC 的启动比无感 FOC 简单,因为一开始就知道转子位置。但双电机同时启动时,如果两个电机都从零速强拖,母线电压会被拉低,导致第二个电机启动失败。工程里常见的做法是:电机 1 先启动,等转速超过设定阈值(比如 100 RPM)后,再启动电机 2。这个逻辑在MCT_StartMotor()的调用顺序里体现。
/* 双电机启动顺序控制,避免母线跌落 */ MCT_StartMotor(0); /* 先启动电机1 */ while(MCT_GetSpeed(0) < 100); /* 等待转速建立,实际工程用状态机非阻塞 */ MCT_StartMotor(1); /* 再启动电机2 */这段代码是简化示意,实际工程里不会用while死等,而是放在 1ms 任务里做状态判断。参数上,MCT_GetSpeed()返回的是机械转速,单位 RPM,阈值 100 是我根据小功率云台电机惯量估的,你的电机如果惯量大,这个值要往上调。切换条件除了转速,还要看电流是否稳定,如果启动瞬间电流冲到限幅值,说明强拖时间不够,需要加长Startup_Duration。
3.3 电流环与速度环的参数整定
MCSDK 生成的工程里,电流环和速度环的 PI 参数都在pmsm_motor_parameters.h或drive_parameters.h里。双电机场景下,两个电机的参数是独立的结构体实例,可以分别整定。电流环带宽一般设 1kHz 到 2kHz,速度环带宽设 50Hz 到 100Hz。
/* 电机1电流环PI参数 */ #define MOTOR1_CURR_KP 1200 #define MOTOR1_CURR_KI 300 /* 电机2电流环PI参数,因电感不同略有差异 */ #define MOTOR2_CURR_KP 1100 #define MOTOR2_CURR_KI 280这些参数不是拍脑袋写的,KP和KI的比值决定了零点位置,通常让零点抵消电机电气时间常数。如果你发现电流波形有高频振荡,先把KP减半;如果响应太慢,先加KI。速度环整定时,先把电流环调好,再合速度环,否则两个环耦合在一起,调起来就是玄学。
4. 避坑与常见问题:双电机霍尔 FOC 的五个血泪教训
4.1 电机 2 不转,但电机 1 正常
现象:上电后电机 1 能闭环,电机 2 要么不动,要么抖动几下就停。原因通常是电机 2 的霍尔接口定时器没有使能捕获中断,或者 ADC 注入通道没配好。解决:检查MX_TIM3_Init()里是否调用了HAL_TIM_IC_Start_IT(),以及ADC3的注入组是否在MX_ADC3_Init()里使能了ADC_INJECTED_GROUP。我遇到过因为TIM3的复用引脚被别的外设占用,导致霍尔信号进不来,查引脚分配表才发现。
4.2 高速时电流波形畸变,转速上不去
现象:电机 1 单独跑能到 3000 RPM,双电机一起跑,两个都卡在 1500 RPM 左右,电流波形削顶。原因:母线电压被两个电机的 PWM 占空比拉低,或者 ADC 采样时刻被另一个电机的中断打断。解决:先确认电源功率够不够,再检查两个 ADC 中断优先级是否错开。如果用的是共享母线,在 MCSDK 里把VBUS滤波系数调大,让电压前馈更平滑。
4.3 霍尔换相表顺序错误导致反转
现象:电机启动时轻微抖动,然后朝反方向缓慢旋转。原因:霍尔表索引顺序和实际接线不匹配。解决:把电机三相线任意两根对调,或者把HALL_Table里的值按 6→1 倒序。注意不要同时改表和接线,否则会转成另一种错误。
4.4 调试串口打印导致电流环丢拍
现象:一开串口打印,电机就发出异响,转速波动。原因:printf是阻塞式输出,在电流环中断里调用会拖长中断时间。解决:把打印放到 1ms 速度环任务里,或者用 DMA 发送。工程里如果用了MC_Printf之类的宏,确认它不在 ADC 中断里被调用。
4.5 重新生成代码后配置丢失
现象:用 CubeMX 改了个引脚,重新生成后双电机配置只剩一个。原因:.ioc里电机 2 的配置没有保存,或者 MCSDK 版本不匹配。解决:改配置前先备份.ioc和MCSDK_v5.x.x目录,重新生成后对比Core/Src/main.c的MX_MotorControl_Init()调用次数。如果少了,手动把电机 2 的初始化加回去,但要注意下次生成还会丢,最好在 CubeMX 里把两个电机都配好再生成。
5. 二次开发与移植:把工程改成你自己的板子
5.1 修改电机参数与引脚映射
拿到一份别人的工程,第一步永远是改电机参数。打开pmsm_motor_parameters.h,把MOTOR1_POLES、MOTOR1_RS、MOTOR1_LS、MOTOR1_KE这些改成你电机的实际值。极对数看电机铭牌,相电阻和电感用万用表和 LCR 表测,反电动势常数如果没数据,可以先填个大概,跑起来后再根据转速和电压反推。引脚映射在.ioc里改,改完重新生成,但记得把MCSDK_v5.x.x里你手动改过的文件先备份出来,生成后再覆盖回去。
/* pmsm_motor_parameters.h 中需要按实际电机修改的宏 */ #define MOTOR1_POLES 7 /* 极对数,不是极数 */ #define MOTOR1_RS 2.5f /* 相电阻,单位欧姆 */ #define MOTOR1_LS 0.003f /* 相电感,单位亨利 */ #define MOTOR1_KE 0.05f /* 反电动势常数,V/(rad/s) */这些参数直接影响电流环和速度环的整定。RS和LS填错,电流环会振荡或者响应迟钝;KE填错,速度环在高速时会出现稳态误差。我一般会先用 MCSDK 的电机参数识别功能跑一遍,拿到实测值再填进去,比手算靠谱。
5.2 增加自定义控制逻辑
如果你想在双电机基础上加自己的逻辑,比如根据外部传感器切换转速,或者做双电机同步,建议在MCT_Task()里加一个用户任务钩子。MCSDK 的任务调度器每个周期会调用一次MCT_Task(),你可以在里面读传感器、算目标转速,然后调用MCT_SetSpeed()。
/* 在 1ms 任务里增加自定义同步逻辑 */ void MCT_Task(void) { MCT_SetSpeed(0, target_speed_1); MCT_SetSpeed(1, target_speed_2); /* 双电机同步:让电机2跟随电机1的转速 */ if (sync_enable) { target_speed_2 = MCT_GetSpeed(0); } }注意MCT_SetSpeed()的参数是机械转速,单位 RPM,内部会换算成电角度。如果你要做位置控制,得用MCT_SetPosition()或者直接改电流环的Iq参考值。改完这些,编译下载,先用低转速试,确认两个电机转向一致、电流正常,再往上加转速。
5.3 验证方法与调试习惯
移植完一定要做三步验证:第一步,不接电机,用示波器看六路 PWM 和霍尔信号,确认死区和换相顺序;第二步,接一个电机,开环强拖,看电流波形是否正弦;第三步,双电机闭环,从 500 RPM 开始,每 500 RPM 加一档,记录电流和转速波动。我习惯在MCT_Task()里加一个软件示波器变量,把关键数据通过 DAC 输出,比串口打印直观得多。
从那以后我每次改完电机参数,都强制走一遍「示波器看 PWM → 单电机开环 → 双电机闭环」的流程,不再直接上闭环。希望帮到你。
本文还有配套的精品资源,点击获取