1. 电机控制方案概述
在工业控制和消费电子领域,BLDC(无刷直流电机)和PMSM(永磁同步电机)因其高效率、高可靠性和长寿命等优势,已成为主流选择。基于STM32F1系列MCU的电机驱动方案,凭借其出色的实时性能和丰富的外设资源,为电机控制提供了理想的硬件平台。
1.1 核心控制需求解析
电机控制的核心在于精确控制转子的位置和速度。根据应用场景的不同,我们可以选择有传感器或无传感器方案:
- 有传感器方案:通过霍尔元件或编码器直接检测转子位置,控制精度高但成本较高
- 无传感器方案:通过反电动势或滑模观测器等算法估算转子位置,降低成本但算法复杂度高
提示:选择方案时需要权衡成本、精度和开发难度。对于初学者,建议从BLDC的有传感器方案入手,再逐步过渡到无传感器和PMSM控制。
2. BLDC有传感器驱动实现
2.1 硬件设计与接口配置
霍尔传感器通常采用三路输出,对应电机的三个相位。在STM32F1上的典型配置如下:
c复制void BLDC_Hall_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA0-PA2为霍尔输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置外部中断用于霍尔信号变化检测
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 连接EXTI线到PA0-PA2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2);
// 配置EXTI触发方式
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn | EXTI1_IRQn | EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
2.2 换相逻辑与PWM控制
霍尔信号与电机换相的关系遵循六步换相法,每个霍尔状态对应特定的PWM输出组合:
| 霍尔状态 | 导通相位 | PWM高电平 | PWM低电平 | 浮空相位 |
|---|---|---|---|---|
| 001 | A+B- | A | B | C |
| 101 | A+C- | A | C | B |
| 100 | B+C- | B | C | A |
| 110 | B+A- | B | A | C |
| 010 | C+A- | C | A | B |
| 011 | C+B- | C | B | A |
对应的换相函数实现:
c复制void Commutation(uint8_t hall_state) {
switch(hall_state) {
case 0b001: // 状态1
TIM_SetCompare1(TIM1, PWM_Duty); // A相高
TIM_SetCompare2(TIM1, 0); // B相低
TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Disable); // C相浮空
break;
case 0b101: // 状态2
TIM_SetCompare1(TIM1, PWM_Duty); // A相高
TIM_SetCompare3(TIM1, 0); // C相低
TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Disable); // B相浮空
break;
// 其他状态类似实现
default:
// 错误处理
break;
}
}
2.3 速度闭环控制实现
速度控制通常采用PID算法,通过霍尔信号计算实际转速:
c复制void TIM_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 计算转速(RPM)
static uint32_t last_hall_time = 0;
uint32_t current_time = TIM_GetCounter(TIM2);
uint32_t delta_time = current_time - last_hall_time;
if (delta_time > 0) {
// 每转产生6个霍尔边沿(3个霍尔*2边沿)
float rpm = 60.0f * (SystemCoreClock / 6) / delta_time;
last_hall_time = current_time;
// PID计算
float error = target_rpm - rpm;
integral += error * dt;
derivative = (error - last_error) / dt;
output = Kp*error + Ki*integral + Kd*derivative;
last_error = error;
// 限制PWM输出范围
PWM_Duty = constrain(output, 0, MAX_PWM);
}
}
}
注意:霍尔信号需要添加硬件滤波(通常RC滤波)和软件消抖处理,避免误触发。建议在GPIO输入引脚添加0.1uF电容,并在软件中采用多次采样确认的方式。
3. BLDC无传感器驱动实现
3.1 反电动势过零点检测原理
无传感器方案的核心是检测未通电相的反电动势(BEMF)过零点。当电机旋转时,未通电的绕组会产生正弦波电压,其过零点对应换相时刻。
实现要点:
- 采用电阻分压网络将电机相电压衰减到MCU可接受的范围内
- 通过比较器或ADC检测电压变化
- 软件算法识别过零点
3.2 硬件电路设计
典型的三相电压检测电路:
code复制电机相电压 → 10kΩ电阻 → 1kΩ电阻 → GND
↓
ADC输入
1nF电容 → GND
在STM32中的ADC配置:
c复制void ADC_Config(void) {
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置ADC1的通道0-2(对应PA0-PA2)
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// ADC校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
3.3 软件算法实现
反电动势检测的关键代码:
c复制#define VIRTUAL_NEUTRAL_POINT (ADC_MAX/2) // 虚拟中性点电压
void BEMF_Detection(void) {
uint16_t adc_value = ADC_GetConversionValue(ADC1);
static uint8_t last_state = 0;
// 检测过零点(与虚拟中性点比较)
if ((last_state == 0 && adc_value > VIRTUAL_NEUTRAL_POINT) ||
(last_state == 1 && adc_value < VIRTUAL_NEUTRAL_POINT)) {
// 检测到过零点,延时30度电角度后换相
uint32_t delay = calculate_electrical_angle_delay();
delay_us(delay);
next_commutation();
}
last_state = (adc_value > VIRTUAL_NEUTRAL_POINT) ? 1 : 0;
}
3.4 启动策略
无传感器方案的最大挑战是启动阶段,此时反电动势为零。常用启动方法:
- 对齐启动:强制将转子定位到已知位置
- 三段式启动:
- 阶段1:强制换相低速运行
- 阶段2:开环加速到足够转速
- 阶段3:切换到闭环BEMF检测
启动流程代码框架:
c复制void Motor_Start(void) {
// 1. 转子对齐
align_rotor();
// 2. 开环加速
for (int i = 0; i < OPEN_LOOP_STEPS; i++) {
open_loop_commutation();
delay_us(OPEN_LOOP_DELAY);
OPEN_LOOP_DELAY -= ACCELERATION_RATE;
}
// 3. 切换到闭环运行
enable_bemf_detection();
}
实操技巧:开环到闭环的切换时机很关键,通常当检测到的反电动势幅度达到电源电压的25%时切换最可靠。切换过早会导致失步,过晚则可能引起过流。
4. PMSM有传感器FOC控制
4.1 磁场定向控制(FOC)原理
FOC通过坐标变换将三相电流分解为转矩分量(Iq)和励磁分量(Id),实现类似直流电机的控制特性。关键步骤:
- Clarke变换:三相静止坐标系→两相静止坐标系
- Park变换:两相静止坐标系→旋转坐标系
- PI调节器控制Id、Iq
- 逆Park变换
- SVPWM调制
4.2 霍尔FOC实现
基于霍尔传感器的简化FOC方案:
c复制void FOC_Hall_Update(void) {
// 1. 读取霍尔状态和电流
uint8_t hall = read_hall_sensors();
float ia = read_phase_current(A_PHASE);
float ib = read_phase_current(B_PHASE);
float ic = -ia - ib; // 假设三相平衡
// 2. Clarke变换
float i_alpha = ia;
float i_beta = (ia + 2*ib) * ONE_BY_SQRT3;
// 3. 估算电角度(基于霍尔)
float theta = estimate_angle_from_hall(hall);
// 4. Park变换
float i_d = i_alpha * cos(theta) + i_beta * sin(theta);
float i_q = -i_alpha * sin(theta) + i_beta * cos(theta);
// 5. PI控制
float v_d = pid_regulator_id(i_d_ref - i_d);
float v_q = pid_regulator_iq(i_q_ref - i_q);
// 6. 逆Park变换
float v_alpha = v_d * cos(theta) - v_q * sin(theta);
float v_beta = v_d * sin(theta) + v_q * cos(theta);
// 7. SVPWM生成
generate_svpwm(v_alpha, v_beta);
}
4.3 编码器高精度FOC
使用增量式编码器可提供更高精度的位置反馈:
c复制void TIM_Encoder_Config(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
// 编码器接口配置(TIM3)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_Period = ENCODER_MAX_COUNT;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 0x0F; // 滤波器设置
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_Cmd(TIM3, ENABLE);
}
float get_motor_angle(void) {
uint16_t encoder_cnt = TIM_GetCounter(TIM3);
return (2.0f * PI * encoder_cnt) / ENCODER_RESOLUTION;
}
5. PMSM无传感器滑模观测器
5.1 滑模观测器原理
滑模观测器通过构建电机数学模型,利用误差反馈来估算反电动势,进而得到转子位置。其核心特点是鲁棒性强,对参数变化不敏感。
5.2 算法实现
滑模观测器的基本实现:
c复制typedef struct {
float alpha; // 观测器增益
float k_slide; // 滑模增益
float theta; // 估算角度
float speed; // 估算速度
} SMO_TypeDef;
void SMO_Update(SMO_TypeDef* smo, float i_alpha, float i_beta, float v_alpha, float v_beta, float dt) {
// 电机参数
const float R = 1.0f; // 电阻
const float L = 0.001f; // 电感
const float Ke = 0.01f; // 反电动势常数
// 电流观测
float di_alpha_hat = (v_alpha - R*i_alpha)/L - smo->alpha*(i_alpha - smo->i_alpha_hat);
float di_beta_hat = (v_beta - R*i_beta)/L - smo->alpha*(i_beta - smo->i_beta_hat);
smo->i_alpha_hat += di_alpha_hat * dt;
smo->i_beta_hat += di_beta_hat * dt;
// 滑模控制项
float e_alpha = i_alpha - smo->i_alpha_hat;
float e_beta = i_beta - smo->i_beta_hat;
float z_alpha = (e_alpha > 0) ? smo->k_slide : -smo->k_slide;
float z_beta = (e_beta > 0) ? smo->k_slide : -smo->k_slide;
// 反电动势估算
float e_est_alpha = z_alpha * L;
float e_est_beta = z_beta * L;
// 角度估算
smo->theta = atan2(-e_est_alpha, e_est_beta);
// 速度估算(通过角度差分)
static float last_theta = 0;
smo->speed = (smo->theta - last_theta) / dt;
last_theta = smo->theta;
}
5.3 参数整定技巧
滑模观测器的性能取决于三个关键参数:
- α(观测器增益):影响电流跟踪速度,通常设置为R/L的5-10倍
- k_slide(滑模增益):需大于反电动势幅值/L,通常取1.2-1.5倍估算值
- 低通滤波器截止频率:用于平滑估算的反电动势,通常设置为电机电气频率的2-3倍
调试心得:实际应用中,可以先通过有传感器运行获取反电动势波形,据此设置k_slide。α值过大会引入噪声,过小则响应慢。建议先用仿真工具验证参数,再实际调试。
6. 系统集成与调试技巧
6.1 硬件设计注意事项
-
功率电路布局:
- 使用星型接地,避免功率地和信号地形成环路
- 栅极驱动电阻尽量靠近MOSFET放置
- 电流检测走线要对称,避免引入干扰
-
电流检测方案选择:
- 低端采样:简单但无法检测PWM关闭时的电流
- 高端采样:需要专用放大器如INA240
- 霍尔传感器:隔离好但成本高、带宽有限
-
保护电路:
- 过流保护:硬件比较器+软件双重保护
- 欠压保护:防止电源波动导致MCU异常
- 温度监测:功率器件和电机的温度监控
6.2 软件调试方法
-
分段调试策略:
- 先验证PWM生成和死区时间
- 再测试换相逻辑(可手动强制换相)
- 最后闭环调试
-
关键信号监测:
- 利用DAC输出内部变量到示波器
- 通过SWD实时查看变量变化
- 使用串口打印调试信息(注意实时性影响)
-
典型问题排查:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机抖动不转 | 霍尔相位错误 | 检查霍尔安装角度和接线顺序 |
| 高速失步 | 换相延时不当 | 调整过零点检测后的延时时间 |
| 电流波动大 | PID参数不合适 | 先调P,再调I,最后调D |
| 启动失败 | 初始位置错误 | 加强对齐启动过程 |
6.3 性能优化技巧
-
计算效率优化:
- 使用查表法替代实时三角函数计算
- 将Park变换等算法转换为定点数运算
- 合理利用STM32的硬件数学加速
-
实时性保证:
- 关键中断(如ADC、霍尔)设为最高优先级
- 耗时计算(如SMO)放在主循环
- 使用DMA传输减轻CPU负担
-
抗干扰措施:
- ADC采样多次取平均
- 对关键变量进行软件滤波
- 增加看门狗防止程序跑飞
在实际项目中,我通常会先搭建一个简单的测试框架,逐步验证每个功能模块。例如对于无传感器方案,可以先用函数发生器模拟反电动势信号,验证过零点检测算法,再实际连接电机调试。这种分阶段的方法能有效降低调试难度。
