1. 项目概述
在工业控制和消费电子领域,BLDC(无刷直流电机)和PMSM(永磁同步电机)因其高效率、低噪音和长寿命等优势,正逐步取代传统有刷电机。作为嵌入式开发者,掌握这两种电机的驱动技术已成为必备技能。本文将基于STM32F1平台,详细解析BLDC和PMSM的有感/无感驱动实现方案,所有代码均经过实际验证。
特别说明:本文提供的方案适用于24V/48V中小功率电机场景,对于高压大功率电机需注意功率器件选型和隔离设计。
2. BLDC电机驱动实现
2.1 硬件架构设计
典型的三相BLDC驱动电路包含以下核心模块:
- STM32F103C8T6最小系统(72MHz主频)
- 三相全桥驱动电路(如IR2104+MOSFET方案)
- 电流采样电阻(50mΩ/2W规格)
- 霍尔传感器接口或反电动势分压网络
电路设计要点:
- 栅极驱动电阻建议选择10-100Ω,过大导致开关损耗增加,过小可能引起振荡
- 母线电容按每安培电流1000μF配置,ESR要尽可能低
- 电流采样运放带宽需大于20kHz(如INA240)
2.2 有传感器驱动(霍尔实现)
霍尔传感器安装通常有120°和60°两种机械角度布置方式,对应不同的换相逻辑。以120°布置为例,其真值表如下:
| Hall A | Hall B | Hall C | 导通相 |
|---|---|---|---|
| 1 | 0 | 1 | A+B- |
| 1 | 0 | 0 | A+C- |
| 1 | 1 | 0 | B+C- |
| 0 | 1 | 0 | B+A- |
| 0 | 1 | 1 | C+A- |
| 0 | 0 | 1 | C+B- |
关键代码实现:
c复制// 霍尔换相表
const uint8_t HallCommutationTable[8] = {
0xFF, // 无效状态
0b001001, // 状态1: A+B-
0b001100, // 状态2: A+C-
0xFF,
0b011000, // 状态4: B+C-
0xFF,
0b010010, // 状态6: B+A-
0b100010 // 状态7: C+A-
};
void TIM1_UP_IRQHandler(void) {
if(TIM_GetITStatus(TIM1, TIM_IT_Update)) {
uint8_t hall_state = Read_Hall_State();
uint8_t pwm_pattern = HallCommutationTable[hall_state];
// 更新PWM输出
TIM1->CCR1 = (pwm_pattern & 0b000001) ? PWM_Value : 0;
TIM1->CCR2 = (pwm_pattern & 0b000010) ? PWM_Value : 0;
// ... 其他通道配置
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}
}
调试技巧:用逻辑分析仪同时捕获霍尔信号和PWM输出,确保换相时序准确。常见问题是霍尔安装角度偏移导致的转矩脉动。
2.3 无传感器驱动(反电动势过零检测)
反电动势检测的关键在于:
- 虚拟中性点构建:通过三个等值电阻(10kΩ)组成Y型网络
- 过零点检测窗口:必须在PWM关断期间采样(通常开启ADC的注入通道)
- 30°相位补偿:过零点到实际换相点的延迟
改进型过零检测代码:
c复制void ADC_IRQHandler(void) {
if(ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {
uint16_t bemf_a = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
uint16_t bemf_b = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
uint16_t bemf_c = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_3);
// 计算虚拟中性点电压
uint16_t v_neutral = (bemf_a + bemf_b + bemf_c) / 3;
// 检测过零点
if((bemf_a - v_neutral) * last_sign_a <= 0) {
Handle_Zero_Crossing(PHASE_A);
last_sign_a = -last_sign_a;
}
// 其他相检测同理...
ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);
}
}
启动策略采用三段式:
- 预定位:强制导通特定相使转子对齐(持续200ms)
- 开环加速:固定换相频率斜坡上升(1-5Hz/ms)
- 切换闭环:当反电动势幅值达到阈值(>5%额定电压)
3. PMSM电机驱动实现
3.1 磁场定向控制(FOC)原理
FOC通过以下变换实现解耦控制:
- Clarke变换:三相静止→两相静止(α-β坐标系)
$$
\begin{cases}
I_\alpha = I_a \
I_\beta = \frac{1}{\sqrt{3}}(I_a + 2I_b)
\end{cases}
$$ - Park变换:两相静止→两相旋转(d-q坐标系)
$$
\begin{cases}
I_d = I_\alpha \cos\theta + I_\beta \sin\theta \
I_q = -I_\alpha \sin\theta + I_\beta \cos\theta
\end{cases}
$$
3.2 有感驱动实现
3.2.1 霍尔FOC方案
霍尔传感器提供低分辨率位置信息(通常7.5°精度),需要配合观测器提升精度:
c复制typedef struct {
float angle; // 估计角度
float speed; // 估计转速
int16_t hall_state; // 当前霍尔状态
} HallFOC_Observer;
void Update_Hall_Observer(HallFOC_Observer* obs, uint8_t new_hall) {
const float hall_angles[6] = {0, 60, 120, 180, 240, 300};
static float last_angle = 0;
if(new_hall != obs->hall_state) {
float target = DEG2RAD(hall_angles[new_hall]);
// 角度插值
if(fabs(target - last_angle) > M_PI) {
target += (target < last_angle) ? 2*M_PI : -2*M_PI;
}
obs->angle = target;
obs->hall_state = new_hall;
}
// 速度估算
float delta = obs->angle - last_angle;
if(delta > M_PI) delta -= 2*M_PI;
else if(delta < -M_PI) delta += 2*M_PI;
obs->speed = delta / CONTROL_PERIOD;
last_angle = obs->angle;
}
3.2.2 编码器方案
增量式编码器接口配置要点:
c复制void TIM2_Encoder_Init(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
// 4倍频计数模式
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising,
TIM_ICPolarity_Rising);
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
}
float Get_Elapsed_Angle(void) {
static int16_t last_cnt = 0;
int16_t curr_cnt = TIM2->CNT;
int16_t delta = curr_cnt - last_cnt;
// 处理计数器溢出
if(delta > 32768) delta -= 65536;
else if(delta < -32768) delta += 65536;
last_cnt = curr_cnt;
return delta * (2*M_PI / ENCODER_RESOLUTION);
}
3.3 无感驱动(滑模观测器)
改进型滑模观测器实现:
c复制typedef struct {
float alpha; // α轴观测电流
float beta; // β轴观测电流
float z_alpha; // α轴滑模变量
float z_beta; // β轴滑模变量
float angle; // 估计角度
float speed; // 估计转速
} SMO_Observer;
void Update_SMO(SMO_Observer* obs, float u_alpha, float u_beta,
float i_alpha, float i_beta, float R, float L) {
const float k_slide = 50.0f; // 滑模增益
const float Ts = 0.0001f; // 100us控制周期
// 电流误差
float e_alpha = obs->alpha - i_alpha;
float e_beta = obs->beta - i_beta;
// 滑模控制量
obs->z_alpha = (e_alpha > 0) ? k_slide : -k_slide;
obs->z_beta = (e_beta > 0) ? k_slide : -k_slide;
// 状态更新
obs->alpha += Ts * ((u_alpha - R*i_alpha + obs->z_alpha)/L);
obs->beta += Ts * ((u_beta - R*i_beta + obs->z_beta)/L);
// 角度估算
obs->angle = atan2f(-obs->z_beta, obs->z_alpha);
// 速度估算(采用锁相环结构)
static float last_angle = 0;
float delta = obs->angle - last_angle;
if(delta > M_PI) delta -= 2*M_PI;
else if(delta < -M_PI) delta += 2*M_PI;
obs->speed = delta / Ts;
last_angle = obs->angle;
}
4. 关键问题与解决方案
4.1 启动失败问题排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机抖动不转 | 霍尔相位接反 | 调整相序或霍尔接线 |
| 反转后卡死 | 换相逻辑错误 | 检查换相表顺序 |
| 高速时失步 | 反电动势采样延迟 | 增加相位补偿角 |
4.2 电流采样优化
推荐采用双电阻采样方案:
- 在下桥臂电阻采样(需PWM同步触发ADC)
- 采样窗口应在PWM开通后期(避免开关噪声)
- 采用硬件过流比较器作为保护(响应时间<1μs)
c复制void ADC_Sampling_Config(void) {
ADC_InitTypeDef ADC_InitStructure;
// 注入通道配置
ADC_InjectedSequencerLengthConfig(ADC1, 2);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_7Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_9, 2, ADC_SampleTime_7Cycles5);
// 外部触发源配置
ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_CC4);
// 自动注入模式
ADC_InjectedDiscModeCmd(ADC1, ENABLE);
}
4.3 死区时间设置
死区时间计算公式:
$$
T_{dead} = \frac{Q_g \times R_g}{V_{drive}} + 50ns
$$
其中:
- Qg:MOSFET栅极电荷(可从datasheet获取)
- Rg:栅极电阻
- Vdrive:驱动芯片输出电压
STM32定时器死区配置示例:
c复制TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
TIM_BDTRInitStructure.TIM_DeadTime = 0x18; // 约1us死区
TIM_BDTRInitStructure.TIM_Break = DISABLE;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
5. 性能优化技巧
-
PWM频率选择:
- 中小功率电机:16-20kHz(超过人耳听觉范围)
- 大功率电机:8-12kHz(降低开关损耗)
-
电流环参数整定:
c复制// PI参数经验公式 void Calculate_PI_Params(float R, float L, float* Kp, float* Ki) { float bandwidth = 1000.0f; // 1kHz带宽 *Kp = L * bandwidth; *Ki = R * bandwidth; } -
速度观测器滤波:
c复制// 低通滤波器实现 float LowPass_Filter(float input, float* state, float alpha) { *state = alpha * input + (1-alpha) * (*state); return *state; } -
Flash存储参数:
c复制typedef struct { float Kp; float Ki; uint16_t max_rpm; } Motor_Params; void Save_Params_To_Flash(void) { FLASH_Unlock(); FLASH_ErasePage(0x0801F000); FLASH_ProgramHalfWord(0x0801F000, *(uint16_t*)¶ms); FLASH_Lock(); }
在实际项目中,电机参数的自动识别(如电阻、电感测量)可以显著提升系统适应性。这里分享一个简单的离线参数辨识方法:
c复制void Motor_Parameter_Identification(void) {
// 1. 施加直流电压测量相电阻
Set_PWM_Duty(0.1, 0, 0);
Delay_ms(500);
float R = (3.3/4096)*ADC_Value / 0.1;
// 2. 脉冲测试测量电感
Set_PWM_Duty(0.3, 0, 0);
Delay_us(100);
float di = (3.3/4096)*ADC_Value / R;
float L = (0.3*VBUS * 100e-6) / di;
}
通过示波器捕获电流上升沿,可以准确计算电机参数。这个技巧在替换不同型号电机时特别有用,避免了重新调试控制参数的麻烦。