STM32F401平台闭环步进驱动方案,支持开环模式兼容42,57,60 86两相开环闭环步进电机
STM32F401平台闭环步进驱动方案,支持开环模式兼容42,57,60 86两相开环闭环步进电机,提供原理图+PCB+源代码
最近在搞步进电机驱动的兄弟看过来!今天分享个基于STM32F401的骚操作——同时支持开环和闭环控制的步进驱动方案。这玩意儿实测能扛42/57/60/86两相步进电机,闭环模式下堵转直接硬刚,开环模式还能向下兼容老设备,实测效果比某些商业驱动器还要顶。
硬件设计上核心是电流采样电路,这里用了双运放差分方案。直接上代码看ADC配置:
void ADC_Config(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
HAL_ADC_Start(&hadc1); // 关键在这启动连续转换
}
这个配置实现的是连续扫描模式,实测采样率能跑到500ksps。注意这里没开外部触发,直接靠定时器中断触发采样可能会更精准,不过对于步进电机控制来说这个采样率已经够用了。
驱动部分用了全桥MOSFET,PWM生成是关键。看TIM1的初始化:
void MX_TIM1_Init(void) {
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1680-1; // 主频168MHz时对应100kHz PWM
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 两路互补输出自动开启
}
这里有个坑要注意:ARR寄存器设置的是PWM周期,CCR才是占空比。调试时发现如果占空比超过95%会导致MOS管发热异常,后来在死区时间配置里加了62ns的延迟才解决。
闭环控制的核心算法是改进型PID,直接上硬核代码:
float step_pid_update(PID_HandleTypeDef *hpid, float error) {
float p_term = hpid->Kp * error;
hpid->integral += error * hpid->Ki;
// 抗积分饱和处理
if(hpid->integral > hpid->max_output) hpid->integral = hpid->max_output;
else if(hpid->integral < -hpid->max_output) hpid->integral = -hpid->max_output;
float d_term = hpid->Kd * (error - hpid->last_error);
hpid->last_error = error;
return p_term + hpid->integral + d_term;
}
这里把积分项单独做了限幅处理,实测比传统PID的抗饱和方式更有效。调参时建议先用开环模式让电机转起来,然后慢慢加Kp值直到出现震荡,再回调20%作为初始值。

STM32F401平台闭环步进驱动方案,支持开环模式兼容42,57,60 86两相开环闭环步进电机,提供原理图+PCB+源代码
原理图里有个骚操作:在MOS管驱动部分用了双光耦隔离。虽然成本高了点,但实测在86电机堵转时,普通单光耦方案会出现驱动信号畸变,这个设计直接解决问题。PCB布局时注意把功率地和信号地用0欧电阻单点连接,大电流路径尽量短粗。
现在说下怎么切换开环/闭环模式。代码里用了个状态机:
typedef enum {
MOTOR_OPENLOOP,
MOTOR_CLOSEDLOOP,
MOTOR_HOMING
} Motor_ModeTypeDef;
void motor_mode_switch(Motor_ModeTypeDef new_mode) {
if(current_mode == new_mode) return;
// 切换时先软停止
__disable_irq();
TIM1->CCR1 = 0;
TIM1->CCR2 = 0;
if(new_mode == MOTOR_CLOSEDLOOP) {
current_sensor_calibrate(); // 闭环模式需要重新校准电流零点
}
current_mode = new_mode;
__enable_irq();
}
注意模式切换时要先停PWM输出,特别是开环切闭环的时候,否则可能因为积分项累积导致电机突然猛转。实测在2000rpm切换时依然稳定,但建议在低速时操作更安全。
这个方案最爽的是兼容性——通过修改microstep_table数组就能适配不同电机:
const uint16_t microstep_table[] = {
// 全步进模式
[MICROSTEP_FULL] = {0xFFFF, 0x0000, 0xFFFF, 0x0000},
// 1/8微步
[MICROSTEP_8] = {2500,4800,6800,...}, // 实测波形数据
};
不同电机需要不同的微步细分参数,建议先用示波器抓取理想波形,再通过插值算法生成这个表。调试时可以用开环模式先验证波形是否正确,再切闭环加负载测试。
需要源码和设计文件的朋友可以到GitHub搜"STM32F401ClosedLoopStepper",原理图用KiCad打开,源码里有个隐藏技能:长按按键5秒进入参数自整定模式,适合懒人调参。最后提醒下,闭环模式下电机发热会比开环大,建议加散热片或者限制最大电流值。

更多推荐
所有评论(0)