全开源代码 BLDC PMSM FOC 有感 无感 滑膜 霍尔 编码器 基于STM32F1的有传感器和无传感驱动 直流无刷电机有传感器和无传感驱动程序, 无传感的实现是基于反电动势过零点实现的,有传感的霍尔实现。 永磁同步电机有感无感程序,有感为霍尔FOC和编码器方式, 无感为换滑模观测器方式。 有原理图和文档
最近在捣鼓一个全开源的电机驱动项目,用STM32F1实现了有感和无感的BLDC/PMSM驱动。这玩意儿挺有意思,尤其是无感方案在资源紧张的F1上跑起来还有点挑战性,今天就跟大伙儿唠唠实现细节。
先看硬件架构,核心是用三路PWM配合六步换相。原理图上MOS管驱动用的经典IR2101方案,电流采样直接用ADC怼相电压。这里有个坑:F1的ADC采样率得控制在1MHz以内,不然数据会飘。我们的ADC配置代码长这样:
void ADC_Config(void) { ADC_InitTypeDef adc; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ADC_DeInit(ADC1); adc.ADC_Mode = ADC_Mode_Independent; adc.ADC_ScanConvMode = DISABLE; adc.ADC_ContinuousConvMode = ENABLE; adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; adc.ADC_DataAlign = ADC_DataAlign_Right; adc.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &adc); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); }有传感器方案相对简单,霍尔信号处理用外部中断触发换相。这里注意要消抖处理,我们的骚操作是在中断里启动10μs的定时器延时:
void EXTI9_5_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line8) != RESET) { TIM_OC1PreloadConfig(TIM2, DISABLE); //关闭预装载 Hall_Update(); //换相逻辑 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty); //更新占空比 EXTI_ClearITPendingBit(EXTI_Line8); } }无感方案才是重头戏,反电动势过零点检测用硬件比较器+Vbus分压。关键点在滤波处理——我们用了移动平均滤波配合滞后比较,实测在20000转时还能稳如老狗:
#define FILTER_LEN 8 uint16_t bemf_buffer[FILTER_LEN] = {0}; uint8_t Detect_ZeroCross(void) { static uint8_t index = 0; uint32_t sum = 0; bemf_buffer[index] = ADC_GetValue(); index = (index + 1) % FILTER_LEN; for(int i=0; i<FILTER_LEN; i++) sum += bemf_buffer[i]; uint16_t avg = sum / FILTER_LEN; return (avg > threshold_high) || (avg < threshold_low); }PMSM的滑模观测器实现更刺激,核心算法在定时器中断里跑。注意F1没有FPU,得用Q格式定点数运算。关键代码段:
int32_t SMO_Estimate(int32_t ia, int32_t ib) { static int32_t z_alpha=0, z_beta=0; int32_t e_alpha = ia - (Lq*ia + z_alpha); int32_t e_beta = ib - (Lq*ib + z_beta); //滑模切换函数 z_alpha += (e_alpha > 0) ? K_SMO : -K_SMO; z_beta += (e_beta > 0) ? K_SMO : -K_SMO; //反正切求角度 return _atan2(z_beta, z_alpha); }实测发现无感启动需要两步走:先对齐转子位置,再用三段式启动。这里有个骚操作是用PWM占空比渐变代替传统开环加速:
void Sensorless_Startup(void) { //强制对齐 PWM_Output(ALIGN_PHASE, 30); Delay_ms(300); //斜坡加速 for(int i=30; i<80; i+=2){ PWM_Output(next_phase, i); Delay_us(200); //换相间隔随转速增加 } //切入闭环 Enable_SMO_Observer(); }项目里还藏了个彩蛋:通过宏定义切换霍尔FOC和编码器FOC。编码器方案用定时器的编码器接口模式,配合SVPWM实现真闭环。不过要注意F1的定时器只有16位,高转速时记得处理计数器溢出:
void Encoder_Config(void) { TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_SetCounter(TIM3, 32768); //初始值设为中间值防溢出 }最后给想复现的兄弟提个醒:无感方案对PCB布局敏感,采样电阻到MCU的走线要尽量短。代码仓库里有个motor_debug.c文件,里面集成了在线观测功能,可以通过串口实时调整PID参数,这玩意儿调参时能救命。