说明:有代码和原理图 项目代码很全(是两个大项目,两个项目的区别是一个有传感器一个没有,其余实现功能都相同) 无感方波有 有传感器(霍尔元件)的编程也有 1: 基于STM32的无刷直流电机无传感器调速系统代码和原理图 2: 基于STM32的无刷直流电机有传感器调速系统代码和原理图 3: PI控制算法、速度电流双闭环控制 4:所用单片机为stm32f103C8t6 5:三步法起动 6:反电动势过零点检测

最近在折腾STM32F103的无刷电机控制,发现这玩意儿真是又虐又爽。手头刚好有两个项目,一个带霍尔传感器,另一个纯靠算法硬刚的无感方案,核心都是速度电流双闭环控制。今天咱们直接开扒代码,看看这俩方案怎么玩转电机。

先说硬件配置,STM32F103C8T6这货虽然便宜,但定时器资源够用。TIM1的三相PWM输出直接驱动半桥电路,TIM2做速度环计算,TIM4捕获霍尔信号或反电动势过零点。原理图上六个MOS管组成的逆变桥,注意死区时间必须配,不然分分钟烧管:

// PWM初始化关键代码
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_Pulse = CCR_Value;
TIM_OC1Init(TIM1, &TIM_OCInitStructure); 
TIM_BDTRInitStructure.TIM_DeadTime = 0x5F; //死区时间设置
TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

有传感器的方案里,霍尔信号处理是重点。三个GPIO设置成外部中断,每次换相时触发中断。这里有个坑爹问题——霍尔信号抖动,得在中断里加20us的延时去抖:

//霍尔中断处理
void EXTI9_5_IRQHandler(void){
    if(EXTI_GetITStatus(EXTI_Line5) != RESET){
        delay_us(20); //硬件消抖
        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5)){
            current_sector = (HALL3<<2)|(HALL2<<1)|HALL1; //计算当前扇区
            BLDC_UpdatePhase(current_sector); //换相函数
        }
        EXTI_ClearITPendingBit(EXTI_Line5);
    }
    //...类似处理其他两个霍尔信号
}

无感方案就刺激多了,完全靠反电动势过零检测。这里用电阻分压网络把电机相电压衰减,接入运放做比较。ADC检测悬空相的电压,当反电动势过零时触发换相。注意要避开PWM开关噪声,得在PWM关断期间采样:

//反电动势采样时机控制
void TIM3_IRQHandler(void){ //PWM周期中断
    if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){
        if(pwm_state == HIGH){
            //关闭PWM后开启采样窗口
            ADC_SoftwareStartConvCmd(ADC1, ENABLE);
            pwm_state = LOW;
        }else{
            //重新输出PWM
            PWM_Output();
            pwm_state = HIGH;
        }
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    }
}

启动阶段最头疼,无感方案用三步法强行让转子动起来。先给AB相通电1秒,接着AC相,再BC相,靠这种粗暴方式让电机转起来。这里有个参数得微调——每步持续时间要和电机惯性匹配:

void Three_Step_Start(void){
    for(int i=0; i<3; i++){
        switch(i){
            case 0: Phase_A_High(); Phase_B_Low(); break; //AB相通电
            case 1: Phase_A_High(); Phase_C_Low(); break; //AC相通电
            case 2: Phase_B_High(); Phase_C_Low(); break; //BC相通电
        }
        delay_ms(800); //根据负载调整时长
    }
}

双闭环控制才是核心,速度环套电流环的结构。这里用增量式PI防止积分饱和,注意两个环的周期要分开,速度环20ms一次,电流环1ms更新:

//双闭环PI计算
float PI_Calculate(PI_TypeDef *pi, float ref, float fdb){
    float err = ref - fdb;
    float p_out = err * pi->Kp;
    pi->integral += err * pi->Ki;
    
    //抗积分饱和
    if(pi->integral > pi->limit) pi->integral = pi->limit;
    else if(pi->integral < -pi->limit) pi->integral = -pi->limit;
    
    return p_out + pi->integral;
}

//在定时中断中调用
void Current_Loop(){
    current_output = PI_Calculate(&current_pi, target_current, actual_current);
}

void Speed_Loop(){
    speed_output = PI_Calculate(&speed_pi, target_speed, actual_speed);
}

调参时发现个玄学现象:速度环的Ki值太大反而会引发震荡。后来用Ziegler-Nichols法整定,先给Ki=0,逐渐加大Kp直到出现等幅振荡,此时临界增益Ku=0.8,振荡周期Tu=120ms,最后取Kp=0.48,Ki=0.4(1/(0.6Tu))=0.00556。这比瞎调靠谱多了。

说明:有代码和原理图 项目代码很全(是两个大项目,两个项目的区别是一个有传感器一个没有,其余实现功能都相同) 无感方波有 有传感器(霍尔元件)的编程也有 1: 基于STM32的无刷直流电机无传感器调速系统代码和原理图 2: 基于STM32的无刷直流电机有传感器调速系统代码和原理图 3: PI控制算法、速度电流双闭环控制 4:所用单片机为stm32f103C8t6 5:三步法起动 6:反电动势过零点检测

项目源码里还有个骚操作——用定时器的PWM突发模式产生驱动序列。比如换相时用DMA预装载六步换向表,直接硬件自动切换相位,比软件翻转快了至少5us:

//DMA配置换相序列
uint16_t commutation_table[6] = {0x0009,0x0012,0x0024,0x0021,0x0018,0x000C}; 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1->CCR1;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)commutation_table;
DMA_InitStructure.DMA_BufferSize = 6;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);

最后实测效果,带霍尔的方案在低速时稳如老狗,但超过2000转后霍尔信号就跟不上了。无感方案高速性能更好,但低速会抽搐。所以后来搞了个混合方案——低速用霍尔,高速切到无感模式,效果拔群。

完整工程里还有上位机调试界面,用匿名科创的协议,可以实时看转速波形。调PID参数时这个功能简直救命,不然真得凭感觉玄学调参了。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐