无刷直流电机与基于Hall传感器的BLDCM双闭环控制
无刷直流电机双闭环控制,基于hall的BLDCM双闭环控制
最近在调无刷电机的时候被同事吐槽:"你这电机转起来怎么跟抽筋似的?"行吧,咱今天就聊聊怎么让BLDCM(无刷直流电机)转得顺溜点。双闭环控制算是基本功,不过用Hall信号搞这事儿,还真有几个坑得注意。
先说硬件配置:STM32F4的定时器生成PWM,三个Hall传感器接在TIM4的捕获通道,ADC用来采母线电流。重点在于怎么把Hall信号玩出花来,咱们先看段换相代码:
// Hall状态到换相表的映射
const uint8_t phase_table[8] = {
0xFF, // 非法状态
PWM_AH_BL, // 001
PWM_AH_CL, // 010
PWM_BH_CL, // 011
PWM_BH_AL, // 100
PWM_CH_AL, // 101
PWM_CH_BL, // 110
0xFF // 非法
};
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if(GPIO_Pin == HALL_U_Pin|HALL_V_Pin|HALL_W_Pin){
uint8_t hall_state = (HALL_U_Read()<<2) | (HALL_V_Read()<<1) | HALL_W_Read();
TIM1->CCMR1 = phase_table[hall_state] & 0x0F; // 更新PWM输出
TIM1->CCMR2 = (phase_table[hall_state] & 0xF0) >> 4;
}
}
这段中断处理代码有个小机关——phasetable里的宏定义其实是PWM通道的组合掩码。比如PWMAH_BL表示A相上桥臂高边导通,B相下桥臂低边导通。注意这里没直接操作CCRx寄存器,而是通过修改CCMR寄存器来批量切换PWM输出,比单独操作通道更利索。

测速这块儿容易翻车,特别是低速时Hall信号跳变间隔太长。我的做法是用定时器捕获两次跳变的时间差,再结合机械极对数算转速:
uint32_t last_cap = 0;
float rpm = 0.0f;
void Hall_Capture_Callback(TIM_HandleTypeDef *htim) {
uint32_t now = TIM4->CCR1;
uint32_t delta = now - last_cap;
if(delta > 100) { // 防止除零
rpm = (60.0f * 1e6) / (delta * TIM4->Instance->PSC * 7); // 7是极对数x6步换相
last_cap = now;
}
}
这里有个隐藏知识点:1e6是因为定时器时钟是1MHz,分母里的7需要根据实际电机极对数调整。调试时发现,低速时用M法测速误差太大,这种周期测量法更靠谱。
电流环采样得讲究时机,别在PWM切换的瞬间采样,否则ADC值能飘到外太空。我通常在PWM中点触发采样:
void PWM_Midpoint_Trigger() {
if(htim1.Instance->CNT > (htim1.Instance->ARR / 2)) {
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_buf, 2);
}
}
配合DMA搬运数据,这样处理电流反馈不卡主程序。最后上PID控制器,注意电流环和速度环的响应速度要拉开差距:
typedef struct {
float Kp, Ki, Kd;
float integral;
float last_err;
} PID;
float PID_Update(PID* pid, float err, float dt) {
float diff = (err - pid->last_err) / dt;
pid->integral += err * dt;
float output = pid->Kp * err
+ pid->Ki * pid->integral
+ pid->Kd * diff;
pid->last_err = err;
return output;
}
调试时先调电流环:把目标转速设成0,给个阶跃电流指令,看响应曲线。调差不多了再开速度环,这时候如果出现振荡,别急着调Kp,先检查Hall信号有没有毛刺——别问我怎么知道的,说多了都是泪。
最后给个忠告:用Hall做位置传感器,低速时别指望能有多精准。真要玩高精度控制,还是得上编码器。不过对于大多数风机、水泵应用来说,这套方案够用还便宜,毕竟老板们的钱包比电机的转速曲线更重要不是?

更多推荐
所有评论(0)