单片机BLDC PID控制实验

单片机 :STM32F407

开发板:DMF407电机开发板

平台:keil V5.31

HSE 为8MHZ

HSI为16MHZ


第一步:当只有一对级时,转子旋转一圈,霍尔输出一个完整脉冲(其中高电平与低电
平持续时间均为 180°电角度);
第二步:计算其中高电平的持续时间,即:t = C / Ft (其中 t 为 180°电角度所代表的时
间,Ft 是霍尔脉冲的频率,C 为计数次数)
第三步:所以旋转一圈,需要的总时间为 T = 2*C/Ft;
第四步:所得出的结果单位为:s/圈 ,倒数即为:圈/s ,需将其单位转化为 RPM 即:Ft/
(2*C) *60 。
第五步:当转子为 2 对级时,霍尔输出的高低电平时间均为 360°电角度,所以速度公式
为:Ft/(4*C)*60。
主函数:

复制代码
int main(void)
{
    uint8_t debug_cmd = 0;                   /* 存放上位机指令 */
    float current_lpf[4] = {0.0f};           /* 存放三相电流以及母线电流 */
    uint8_t key,t;
    char buf[32];
    float current[3] = {0.0f};

    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);      /* 设置时钟,168Mhz */
    delay_init(168);                         /* 延时初始化 */
    usart_init(115200);                      /* 串口初始化为115200 */
    led_init();                              /* 初始化LED */
    key_init();                              /* 初始化按键 */
    lcd_init();                              /* 初始化LCD */
    bldc_init(168000/18-1,0);
    bldc_ctrl(MOTOR_1,CCW,0);                /* 初始无刷电机接口1速度 */
    pid_init();
    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10,10,240,24,24,"BLDC Motor Test",g_point_color);
    lcd_show_string(10,40,200,16,16,"KEY0:Step++",g_point_color);
    lcd_show_string(10,60,200,16,16,"KEY1:Step--",g_point_color);
    lcd_show_string(10,80,200,16,16,"KEY2:Stop",g_point_color);
    adc_nch_dma_init();
    
#if DEBUG_ENABLE                                                /* 开启调试*/
    debug_init();                                               /* PID调试初始化*/
    debug_send_motorcode(BLDC_MOTOR );                          /* 直流无刷电机 */
    debug_send_motorstate(IDLE_STATE);                          /* 电机空闲 */
    /* 初始化同步数据(选择第x组PIDX,目标速度地址,P,I,D参数)到上位机 */
    debug_send_initdata(TYPE_PID1,(float*)(&g_speed_pid.SetPoint),S_KP,S_KI,S_KD);/* 速度环PID参数(PID1)*/
#endif

    while (1)
    {
        t++;
        if(t % 20 == 0)
        {
            sprintf(buf,"PWM_Duty:%.1f%%",(float)((g_bldc_motor1.pwm_duty/MAX_PWM_DUTY)*100));/* 显示控制PWM占空比 */
            lcd_show_string(10,100,200,16,16,buf,g_point_color);
            sprintf(buf,"SetSpeed:%4d   ",(int16_t)*user_setpoint);     /* 显示设置速度 */
            lcd_show_string(10,120,200,16,16,buf,g_point_color);
            sprintf(buf,"M1 Speed:%4d   ",(int16_t)g_bldc_motor1.speed);
            lcd_show_string(10,140,200,16,16,buf,g_point_color);        /* 显示测量速度 */
            sprintf(buf,"M1 pos:%4d",g_bldc_motor1.pos);                
            lcd_show_string(10,160,200,16,16,buf,g_point_color);        /* 显示位置变化 */
            sprintf(buf,"Power:%.3fV ",g_adc_value[0]*ADC2VBUS);
            lcd_show_string(10,180,200,16,16,buf,g_point_color);        /* 显示电源电压 */
            sprintf(buf,"Temp:%.1fC ",get_temp(g_adc_value[1]));
            lcd_show_string(10,200,200,16,16,buf,g_point_color);        /* 显示温度 */   
            LED0_TOGGLE();                                              /* LED0(红灯) 翻转 */

            current[0] = adc_amp_un[0] * ADC2CURT; /* U */
            current[1] = adc_amp_un[1] * ADC2CURT; /* V */
            current[2] = adc_amp_un[2] * ADC2CURT; /* W */

            /* 一阶数字滤波 滤波系数0.1 用于显示 */
            FirstOrderRC_LPF(current_lpf[0],current[0],0.1f);
            FirstOrderRC_LPF(current_lpf[1],current[1],0.1f);
            FirstOrderRC_LPF(current_lpf[2],current[2],0.1f);
            FirstOrderRC_LPF(current_lpf[3],adc_amp_bus,0.1f);
            if(g_bldc_motor1.run_flag == STOP)                          /* 停机的电流显示 */
            {
                current_lpf[0] = 0;
                current_lpf[1] = 0;
                current_lpf[2] = 0;
            }
            sprintf(buf,"Amp U:%.3fmA ",(float)current_lpf[0]);
            lcd_show_string(10,230,200,16,16,buf,g_point_color);
            sprintf(buf,"Amp V:%.3fmA ",(float)current_lpf[1]);
            lcd_show_string(10,250,200,16,16,buf,g_point_color);
            sprintf(buf,"Amp W:%.3fmA ",(float)current_lpf[2]);
            lcd_show_string(10,270,200,16,16,buf,g_point_color);
            sprintf(buf,"Amp Bus:%.3fmA ",(float)adc_amp_bus);
            lcd_show_string(10,290,200,16,16,buf,g_point_color);
        }

        key = key_scan(0);
        if(key == KEY0_PRES)                /* 按下KEY1速度目标值++ */
        {
            g_bldc_motor1.run_flag = RUN;   /* 开启运行 */
            start_motor1();                 /* 开启运行 */
            if(*user_setpoint == 0 && g_bldc_motor1.dir == CCW)
            {
                pid_init();                 /* 换向时刻,重新初始化PID,防止速度突变 */
                g_bldc_motor1.dir = CW;
            }

            *user_setpoint += 400;          /* 顺时针旋转下递增 */
            if(*user_setpoint >= 4000)
                *user_setpoint = 4000;
            if(*user_setpoint == 0)
            {
                g_bldc_motor1.run_flag = STOP; 
                stop_motor1();              /* 停机 */
                g_bldc_motor1.speed = 0;
                motor_pwm_s = 0;
                g_bldc_motor1.pwm_duty = 0;
            }
        }
        else if(key == KEY1_PRES)           /* 按下KEY1速度目标值-- */
        {
            g_bldc_motor1.run_flag = RUN;   /* 开启运行 */
            start_motor1();                 /* 开启运行 */
            if(*user_setpoint == 0 && g_bldc_motor1.dir == CW)
            {     
                pid_init();
                g_bldc_motor1.dir = CCW;
            }
            *user_setpoint -= 400;          /* 逆时针旋转下递增 */
            if(*user_setpoint <= -4000)
                *user_setpoint = -4000;
            if(*user_setpoint == 0)
            {
                g_bldc_motor1.run_flag = STOP;  
                stop_motor1();              /* 停机 */
                g_bldc_motor1.speed = 0;
                motor_pwm_s = 0;
                g_bldc_motor1.pwm_duty = 0;
            }
        }
        else if(key == KEY2_PRES)           /* 按下KEY2关闭电机 */
        {
            bldc_speed_stop();              /* 清除电机状态并关闭电机 */
        }
        
#if DEBUG_ENABLE
        /* Debug发送部分 */
        /* 主要显示参数 */
        debug_send_valtage(g_adc_value[0] * ADC2VBUS);              /* 发送电压 */
        debug_send_speed(g_bldc_motor1.speed);                      /* 发送速度 */
        debug_send_temp(50,get_temp(g_adc_value[1]));               /* 发送电机温度、驱动板温度 */
        debug_send_current((float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000)); /* 发送电流 */
        /* 电流波形和速度波形 */
        debug_send_wave_data(1,(int16_t)g_bldc_motor1.speed);       /* 选择通道1 发送实际速度 */
        debug_send_wave_data(2,(int16_t)*user_setpoint);            /* 选择通道2 发送实际速度 */
        debug_send_wave_data(3,current_lpf[0]);                     /* 选择通道3 发送实际电流U */
        debug_send_wave_data(4,current_lpf[1]);                     /* 选择通道4 发送实际电流V */
        debug_send_wave_data(5,current_lpf[2]);                     /* 选择通道5 发送实际电流W */
        debug_send_wave_data(6,current_lpf[3]);                     /* 选择通道6 发送实际母线电流 */
        /* Debug接收部分 */
        debug_receive_pid(TYPE_PID1,(float*)&g_speed_pid.Proportion,/* 查询接收PID助手的PID1参数 */
                          (float*)&g_speed_pid.Integral,(float*)&g_speed_pid.Derivative);
        debug_cmd = debug_receive_ctrl_code();                      /* 读取命令 */
        if(debug_cmd == HALT_CODE)                                  /* 停机 */
        {
            pid_init();                                             /* 重新初始化PID,防止积分过大失控 */
            g_bldc_motor1.run_flag = STOP;                          /* 标记停机 */
            stop_motor1();                                          /* 停机 */
            g_bldc_motor1.speed = 0;
            motor_pwm_s = 0;
            g_bldc_motor1.pwm_duty = 0;
        }
        else if(debug_cmd == RUN_CODE)                              /* 运行 */
        {
            g_bldc_motor1.dir = CW;
            g_bldc_motor1.run_flag = RUN;                           /* 运行标记 */
            *user_setpoint = 400;                                   /* 自动设置目标 */
            start_motor1();                                         /* 启动电机 */
            debug_send_motorstate(RUN_STATE);                       /* 电机运行 */
        }
        else if (debug_cmd == BREAKED)                              /* 刹车(电机停止 点击电机运行才可解除) */
        {
            *user_setpoint = 0;                                     /* 减速直至0 */
            debug_send_motorstate(BREAKED_STATE);                   /* 电机刹车 */
        }
#endif
        
        delay_ms(10);
    }
}
/**
 * @brief       清除电机状态并关闭电机
 * @param       无
 * @retval      无
 */
void bldc_speed_stop(void)
{
    pid_init();                     /* 重新初始化PID,防止积分过大失控 */
    g_bldc_motor1.run_flag = STOP;  /* 标记停机 */
    stop_motor1();                  /* 停机 */
    g_bldc_motor1.speed = 0;
    motor_pwm_s = 0;
    g_bldc_motor1.pwm_duty = 0;
}

PID:

复制代码
void pid_init(void)
{
    g_speed_pid.SetPoint = 0;       /* 设定目标值 */
    g_speed_pid.ActualValue = 0.0;  /* 期望值输出 */
    g_speed_pid.SumError = 0.0;     /* 积分值 */
    g_speed_pid.Error = 0.0;        /* Error[1] */
    g_speed_pid.LastError = 0.0;    /* Error[-1] */
    g_speed_pid.PrevError = 0.0;    /* Error[-2] */
    g_speed_pid.Proportion = S_KP;  /* 比例常数 Proportional Const */
    g_speed_pid.Integral = S_KI;    /* 积分常数 Integral Const */
    g_speed_pid.Derivative = S_KD;  /* 微分常数 Derivative Const */ 
    g_speed_pid.IngMax = 20;
    g_speed_pid.IngMin = -20;
    g_speed_pid.OutMax = 150;       /* 输出限制 */
    g_speed_pid.OutMin = -150;    
}

/**
 * @brief       闭环PID控制算法设计
 * @note        通过宏 INCR_LOCT_SELECT 选择使用位置式算法/增量式算法
 * @param       *PID:PID结构体句柄所对应的目标值
 * @param       Feedback_value : 实际值
 * @retval      目标控制量
 */
int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value)
{
    PID->Error = (float)(PID->SetPoint - Feedback_value);                   /* 速度档位偏差*/
#if  INCR_LOCT_SELECT
    PID->ActualValue += (PID->Proportion * (PID->Error - PID->LastError))   /* E[k]项*/
                        + (PID->Integral * PID->Error)                      /* E[k-1]项*/
                        + (PID->Derivative * (PID->Error - 2 * PID->LastError + PID->PrevError)); /*E[k-2]项*/
    PID->PrevError = PID->LastError;                                        /* 存储误差,用于下次计算*/
    PID->LastError = PID->Error;
#else
    PID->SumError += PID->Error;
    PID->ActualValue = (PID->Proportion * PID->Error)                       /* E[k]项*/
                       + (PID->Integral * PID->SumError)                    /* E[k-1]项*/
                       + (PID->Derivative * (PID->Error - PID->LastError)); /* E[k-2]项*/
    PID->LastError = PID->Error;
#endif
    return ((int32_t)(PID->ActualValue));                                   /* 返回实际控制数值*/
}

定时器:

复制代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint8_t i;
    uint8_t bldc_dir=0;
    static uint8_t times_count=0;           /* 定时器时间记录 */
    int16_t temp_speed=0;                   /* 临时速度存储 */
    if(htim->Instance == ATIM_TIMX_PWM)     /* 55us */
    {
        /******************************* 六步换向 *******************************/
        if(g_bldc_motor1.run_flag == RUN)
        {
            if(g_bldc_motor1.dir == CW)
            {
                g_bldc_motor1.step_sta = hallsensor_get_state(MOTOR_1);
            }
            else
            {
                g_bldc_motor1.step_sta = 7 - hallsensor_get_state(MOTOR_1);
            }
            if((g_bldc_motor1.step_sta <= 6)&&(g_bldc_motor1.step_sta >= 1))
            {
                pfunclist_m1[g_bldc_motor1.step_sta-1]();
            }
            else    /* 编码器错误、接触不良、断开等情况 */
            {
                stop_motor1();
                g_bldc_motor1.run_flag = STOP;
            }
            
            /******************************* 速度计算 *******************************/
            g_bldc_motor1.count_j++;                /* 计算速度专用计数值 */
            g_bldc_motor1.hall_sta_edge = uemf_edge(g_bldc_motor1.hall_single_sta);/* 检测单个霍尔信号的变化 */
            if(g_bldc_motor1.hall_sta_edge == 0)    /* 统计单个霍尔信号的高电平时间,当只有一对级的时候,旋转一圈为一个完整脉冲。一高一低相加即旋转一圈所花的时间*/
            {
                /*计算速度*/
                if(g_bldc_motor1.dir == CW)
                    temp_speed = (SPEED_COEFF/g_bldc_motor1.count_j);
                else
                    temp_speed = -(SPEED_COEFF/g_bldc_motor1.count_j);
                FirstOrderRC_LPF(g_bldc_motor1.speed,temp_speed,0.2379f);   /* 一阶滤波 */
                g_bldc_motor1.no_single = 0;
                g_bldc_motor1.count_j = 0;
            }
            if(g_bldc_motor1.hall_sta_edge == 1)    /* 当采集到下降沿时数据清0 */
            {
                g_bldc_motor1.no_single = 0;
                g_bldc_motor1.count_j = 0;
            }
            if(g_bldc_motor1.hall_sta_edge == 2)    /* 霍尔值一直不变代表未换向 */
            {
                g_bldc_motor1.no_single++;          /* 不换相时间累计 超时则判定速度为0 */
                
                if(g_bldc_motor1.no_single > 15000)
                {
                    
                    g_bldc_motor1.no_single = 0;
                    g_bldc_motor1.speed = 0;        /* 超时换向 判定为停止 速度为0 */
                }
            }
            /******************************* 位置记录 *******************************/
            if(g_bldc_motor1.step_last != g_bldc_motor1.step_sta)
            {
                bldc_dir = check_hall_dir(&g_bldc_motor1);
                if(bldc_dir == CCW)
                {
                    g_bldc_motor1.pos -= 1;
                }
                else if(bldc_dir == CW)
                {
                    g_bldc_motor1.pos += 1;
                }
                g_bldc_motor1.step_last = g_bldc_motor1.step_sta;
            }
            /******************************* PID控制 *******************************/
                temp_pwm1 = increment_pid_ctrl(&g_speed_pid,g_bldc_motor1.speed);   /* PID控制算法,输出期望值 */
                FirstOrderRC_LPF(motor_pwm_s,temp_pwm1,0.085);                      /* 一阶滤波 */
                if(motor_pwm_s < 0)                                                 /* 判断正负值 */
                {
                    g_bldc_motor1.pwm_duty = -motor_pwm_s;
                }
                else
                {
                   g_bldc_motor1.pwm_duty = motor_pwm_s;
                }
            /******************************* 三相电流计算 *******************************/
            for(i=0; i<3; i++)
            {
                adc_val_m1[i] = g_adc_val[i+2];
                adc_amp[i] = adc_val_m1[i] - adc_amp_offset[i][ADC_AMP_OFFSET_TIMES];  /* 运动状态ADC值 - 停机状态ADC值 = 实际作用ADC值 */
                
                if(adc_amp[i]>=0)                                                      /* 去除反电动势引起的负电流数据 */
                 adc_amp_un[i] = adc_amp[i];
            }
            /* 运算母线电流(母线电流为任意两个有开关动作的相电流之和)*/
            if(g_bldc_motor1.step_sta == 0x05)
            {
                adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[1])*ADC2CURT;/* UV */
            }
            else if(g_bldc_motor1.step_sta== 0x01)
            {
                adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[2])*ADC2CURT;/* UW */
            }
            else if(g_bldc_motor1.step_sta== 0x03)
            {
                adc_amp_bus= (adc_amp_un[1]+ adc_amp_un[2])*ADC2CURT;/* VW */
            }
            else if(g_bldc_motor1.step_sta== 0x02)
            {
                adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[1])*ADC2CURT;/* UV */
            }
            else if(g_bldc_motor1.step_sta == 0x06)
            {
                adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[2])*ADC2CURT;/* WU */
            }
            else if(g_bldc_motor1.step_sta == 0x04)
            {
                adc_amp_bus= (adc_amp_un[2]+ adc_amp_un[1])*ADC2CURT;/* WV */
            }
        }
    }
    else if(htim->Instance == TIM6)
    {
        /******************************* 采集电机停机状态下的偏置电压 *******************************/
        times_count++;
        if(g_bldc_motor1.run_flag == STOP)
        {
            uint8_t i;
            uint32_t avg[3] = {0,0,0};
            adc_amp_offset[0][adc_amp_offset_p] = g_adc_val[2];     /* 获取电机停机状态下的三相电流 */
            adc_amp_offset[1][adc_amp_offset_p] = g_adc_val[3];
            adc_amp_offset[2][adc_amp_offset_p] = g_adc_val[4];

            adc_amp_offset_p ++;
            NUM_CLEAR(adc_amp_offset_p,ADC_AMP_OFFSET_TIMES);
            for(i=0; i<ADC_AMP_OFFSET_TIMES; i++)
            {
                avg[0] += adc_amp_offset[0][i];                     /* 各相数值累加 */
                avg[1] += adc_amp_offset[1][i];
                avg[2] += adc_amp_offset[2][i];
            }
            for(i=0; i<3; i++)
            {
                avg[i] /= ADC_AMP_OFFSET_TIMES;                     /* 取平均 */
                adc_amp_offset[i][ADC_AMP_OFFSET_TIMES] = avg[i];   /* 赋值 */
            }
        }
    }
}

测试结果:

速度环+电流环控制

主函数

复制代码
int main(void)
{
    uint8_t debug_cmd = 0;                   /* 存放上位机指令 */
    float current_lpf[4] = {0.0f};           /* 存放三相电流以及母线电流 */
    uint8_t key,t;
    char buf[32];
    float current[3] = {0.0f};
    int16_t speed_diplay = 0;
    float user_setpoint_temp  = 0.0;

    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);      /* 设置时钟,168Mhz */
    delay_init(168);                         /* 延时初始化 */
    usart_init(115200);                      /* 串口初始化为115200 */
    led_init();                              /* 初始化LED */
    key_init();                              /* 初始化按键 */
    lcd_init();                              /* 初始化LCD */
    bldc_init(168000/18-1,0);                /* 18KHz */
    bldc_ctrl(MOTOR_1,CCW,0);                /* 初始无刷电机接口1速度 */
    pid_init();
    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10,10,200,16,16,"BLDC Motor Test",g_point_color);
    lcd_show_string(10,30,200,16,16,"KEY0:Step++",g_point_color);
    lcd_show_string(10,50,200,16,16,"KEY1:Step--",g_point_color);
    lcd_show_string(10,70,200,16,16,"KEY2:Stop",g_point_color);
    adc_nch_dma_init();

#if DEBUG_ENABLE                            /* 开启调试 */
    debug_init();                           /* PID调试初始化 */
    debug_send_motorcode(BLDC_MOTOR);      /* 直流电机 */
    debug_send_motorstate(IDLE_STATE);      /* 电机空闲 */
    /* 初始化同步数据(选择第x组PIDX,目标速度地址,P,I,D参数)到上位机 */
    debug_send_initdata(TYPE_PID1,(float*)(&g_speed_pid.SetPoint),C_KP,C_KI,C_KD);    /* 电流环PID参数(PID1)*/
    debug_send_initdata(TYPE_PID2,(float*)(&g_speed_pid.SetPoint),S_KP,S_KI,S_KD);    /* 速度环PID参数(PID2)*/
#endif
    while (1)
    {
        t++;
        if(t % 20 == 0)
        {
            sprintf(buf,"PWM_Duty:%.1f%%  ",(float)((g_bldc_motor1.pwm_duty/MAX_PWM_DUTY)*100));        /* 显示控制PWM占空比 */
            lcd_show_string(10,110,200,16,16,buf,g_point_color);

            user_setpoint_temp = (*user_setpoint);
            speed_diplay = g_bldc_motor1.speed;
            sprintf(buf,"SetSpeed:%4d   ",(int16_t)user_setpoint_temp);                                 /* 显示设置速度 */
            lcd_show_string(10,110,200,16,16,buf,g_point_color);
            sprintf(buf,"M1 speed:%4d   ",speed_diplay);                                                /* 显示转速 */
            lcd_show_string(10,130,200,16,16,buf,g_point_color);
            sprintf(buf,"M1 pos:%4d",g_bldc_motor1.pos);                                                /* 显示位置变化 */
            lcd_show_string(10,150,200,16,16,buf,g_point_color);
            sprintf(buf,"PWM_Duty:%.1f%%  ",(float)((g_bldc_motor1.pwm_duty/(float)MAX_PWM_DUTY)*100)); /* 显示控制PWM占空比 */
            lcd_show_string(10,170,200,16,16,buf,g_point_color);


            sprintf(buf,"Power:%.3fV ",g_adc_value[0]*ADC2VBUS);
            lcd_show_string(10,190,200,16,16,buf,g_point_color);
            sprintf(buf,"Temp:%.1fC ",get_temp(g_adc_value[1]));
            lcd_show_string(10,210,200,16,16,buf,g_point_color);
            LED0_TOGGLE();                                                                              /* LED0(红灯) 翻转 */
            /* 三相电流计算 */
            current[0] = adc_amp_un[0]* ADC2CURT;/*U*/
            current[1] = adc_amp_un[1]* ADC2CURT;/*V*/
            current[2] = adc_amp_un[2]* ADC2CURT;/*W*/

            /*一阶数字滤波 滤波系数0.1 用于显示*/
            FirstOrderRC_LPF(current_lpf[0],current[0],0.1f);
            FirstOrderRC_LPF(current_lpf[1],current[1],0.1f);
            FirstOrderRC_LPF(current_lpf[2],current[2],0.1f);
            FirstOrderRC_LPF(current_lpf[3],adc_amp_bus,0.1f);
            if(g_bldc_motor1.run_flag == STOP)                  /* 停机的电流显示 */
            {
                current_lpf[0]=0;
                current_lpf[1]=0;
                current_lpf[2]=0;
                current_lpf[3]=0;
            }
            
            sprintf(buf,"Amp U:%.3fmA ",(float)current_lpf[0]);
            lcd_show_string(10,230,200,16,16,buf,g_point_color);
            sprintf(buf,"Amp V:%.3fmA ",(float)current_lpf[1]);
            lcd_show_string(10,250,200,16,16,buf,g_point_color);
            sprintf(buf,"Amp W:%.3fmA ",(float)current_lpf[2]);
            lcd_show_string(10,270,200,16,16,buf,g_point_color);
            sprintf(buf,"Amp Bus:%.3fmA ",(float)adc_amp_bus);
            lcd_show_string(10,290,200,16,16,buf,g_point_color);
            
        }

        key = key_scan(0);
        if(key == KEY0_PRES)                                    /* 按下KEY0目标速度值++ */
        {
            g_bldc_motor1.run_flag = RUN;                       /* 开启运行 */
            start_motor1();                                     /* 开启运行 */

            if(g_bldc_motor1.dir == CCW && *user_setpoint == 0) /* 切换方向条件*/
            {
                g_bldc_motor1.dir = CW; 
            }
            *user_setpoint += 400;                              /* 逆时针旋转下递增 */
            if(*user_setpoint >= 3000)                          /* 最高不超过3000PRM */
                *user_setpoint = 3000;
            if(*user_setpoint == 0)
            {
                pid_init();                                     /* 初始化PID */
                g_bldc_motor1.run_flag = STOP;                  /* 标记停机 */
                stop_motor1();                                  /* 停机 */
                g_bldc_motor1.speed = 0;
                motor_pwm_s = 0;
                g_bldc_motor1.pwm_duty = 0;
            }
            debug_data_temp = *user_setpoint;
        }
        else if(key == KEY1_PRES)                               /* 按下KEY1目标速度值-- */
        {
            g_bldc_motor1.run_flag = RUN;                       /* 开启运行 */
            start_motor1();                                     /* 运行电机 */
            /* 切换方向条件 */
            if(g_bldc_motor1.dir == CW && *user_setpoint == 0)
            {
                g_bldc_motor1.dir = CCW;
            }
            *user_setpoint -= 400;                              /* 逆时针旋转下递增 */
            if(*user_setpoint <= -3000)                         /* 最高不超过300PRM */
                *user_setpoint = -3000;
            if(*user_setpoint == 0)
            {
                pid_init();                                     /* 初始化PID */
                g_bldc_motor1.run_flag = STOP;                  /* 标记停机 */
                stop_motor1();                                  /* 停机 */
                g_bldc_motor1.speed = 0;
                motor_pwm_s = 0;
                g_bldc_motor1.pwm_duty = 0;
            }

            debug_data_temp = *user_setpoint;
        }
        else if(key == KEY2_PRES)                               /* 按下KEY2关闭电机 */
        {
            bldc_speed_stop();
        }

#if DEBUG_ENABLE
        /* Debug发送部分 */
        /* 主要显示参数 */
        debug_send_valtage(g_adc_value[0] * ADC2VBUS);                  /* 发送电压*/
        debug_send_speed(g_bldc_motor1.speed);                          /* 发送速度*/
        debug_send_temp(50,get_temp(g_adc_value[1]));                   /* 发送电机温度、驱动板温度*/
        debug_send_current((float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000),(float)(current_lpf[0]/1000));/*发送电流*/
        /* 电流波形和速度波形 */
        debug_send_wave_data(1,(int16_t)g_bldc_motor1.speed);           /* 选择通道1 发送实际速度 */
        debug_send_wave_data(2,(int16_t)*user_setpoint);                /* 选择通道2 发送目标速度 */ 
        debug_send_wave_data(3,current_lpf[0]);                         /* 选择通道3 发送实际电流U */
        debug_send_wave_data(4,current_lpf[1]);                         /* 选择通道4 发送实际电流V */
        debug_send_wave_data(5,current_lpf[2]);                         /* 选择通道5 发送实际电流W */
        debug_send_wave_data(7,current_lpf[3]);                         /* 选择通道7 发送实际电流 */
        debug_send_wave_data(8,g_current_pid.SetPoint);                 /* 选择通道8 发送目标电流 */
        /* Debug接收部分 */
        debug_receive_pid(TYPE_PID1,(float*)&g_current_pid.Proportion,  /* 查询接收PID助手的PID1参数*/
                          (float*)&g_current_pid.Integral,(float*)&g_current_pid.Derivative);
        debug_receive_pid(TYPE_PID2,(float*)&g_speed_pid.Proportion,    /* 查询接收PID助手的PID2参数*/
                          (float*)&g_speed_pid.Integral,(float*)&g_speed_pid.Derivative);
        debug_cmd = debug_receive_ctrl_code();                          /* 读取命令 */
        if(debug_cmd == HALT_CODE)                                      /* 停机 */
        {
            pid_init();
            g_bldc_motor1.run_flag = STOP;  /* 标记停机 */
            stop_motor1();                  /* 停机 */
            g_bldc_motor1.speed = 0;
            motor_pwm_s = 0;
            g_bldc_motor1.pwm_duty = 0;
        }
        else if(debug_cmd == RUN_CODE)                                  /* 运行 */
        {
            g_bldc_motor1.run_flag = RUN;                               /* 运行标记 */
            *user_setpoint = 400;                                       /* 自动设置目标 */
            debug_data_temp = *user_setpoint;
            start_motor1();                                             /* 启动电机 */
            debug_send_motorstate(RUN_STATE);                           /* 电机运行 */
        }
        else if (debug_cmd == BREAKED)                                  /* 刹车(电机停止 点击电机运行才可解除) */
        {
            *user_setpoint = 0;                                         /* 减速直至0 */
            debug_send_motorstate(BREAKED_STATE);                       /* 电机刹车 */
        }
#endif

        delay_ms(10);
    }
}
/**
 * @brief       关闭电机并清除状态
 * @param       无
 * @retval      无
 */
void bldc_speed_stop(void)
{
    pid_init();
    g_bldc_motor1.run_flag = STOP;  /* 标记停机 */
    stop_motor1();                  /* 停机 */
    g_bldc_motor1.speed = 0;
    motor_pwm_s = 0;
    g_bldc_motor1.pwm_duty = 0;
}

PID

复制代码
void pid_init(void)
{
    /* 设定电流目标1500mA(空载最小电流400mA左右)较高的转速对应的电流较大,力矩较大,可适应较高的转速调节*/
    /* 【注意】如设置的转速对应的电流超过了电流设定值,将导致PID转至电流环调节,转速将无法继续提升 */
    g_current_pid.SetPoint = 1500.0;
    g_current_pid.ActualValue = 0.0;    /* 设定目标Desired Value */
    g_current_pid.LastError = 0.0;      /* Error[1] */
    g_current_pid.LastError = 0.0;      /* Error[-1] */
    g_current_pid.PrevError = 0.0;      /* Error[-2] */
    g_current_pid.Proportion = C_KP;    /* 比例常数 Proportional Const */
    g_current_pid.Integral = C_KI;      /* 积分常数 Integral Const */
    g_current_pid.Derivative = C_KD;    /* 微分常数 Derivative Const */
    g_current_pid.IngMax = 9000;
    g_current_pid.IngMin = 600;
    g_current_pid.OutMin = 600;
    g_current_pid.OutMax = 9000;
    
    g_speed_pid.SetPoint = 0;           /* 设定目标Desired Value */
    g_speed_pid.ActualValue = 0.0;      /* 设定目标Desired Value */
    g_speed_pid.Ui = 0.0;
    g_speed_pid.Up = 0.0;
    g_speed_pid.Ud = 0.0;
    g_speed_pid.Error = 0.0;            /* Error[1] */
    g_speed_pid.LastError = 0.0;        /* Error[-1] */
    g_speed_pid.PrevError = 0.0;        /* Error[-2] */
    g_speed_pid.Proportion = S_KP;      /* 比例常数 Proportional Const */
    g_speed_pid.Integral = S_KI;        /* 积分常数 Integral Const */
    g_speed_pid.Derivative = S_KD;      /* 微分常数 Derivative Const */ 
    g_speed_pid.IngMax = 9000;
    g_speed_pid.IngMin = -9000;
    g_speed_pid.OutMax = 9000;          /* 输出限制 */
    g_speed_pid.OutMin = -9000;
}

/**
 * @brief       位置式PID算法
 * @param       *PID:PID结构体句柄所对应的目标值
 * @param       Feedback_value : 实际值
 * @retval      目标控制量
 */
int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value)
{
    PID->Error = (float)(PID->SetPoint - Feedback_value);   /* 目标值与实际值的偏差值 */
    PID->Up = PID->Proportion * PID->Error;
    PID->Ui += (PID->Error * PID->Integral);
    LIMIT_OUT(PID->Ui,PID->IngMax,PID->IngMin);             /* 积分限制 */
    PID->Ud = PID->Derivative * (PID->Error - PID->LastError);
    PID->ActualValue = PID->Up + PID->Ui + PID->Ud;
    LIMIT_OUT(PID->ActualValue,PID->OutMax,PID->OutMin);    /* 输出限制 */
    PID->LastError = PID->Error;                            /* 存储上次误差,以便下次计算使用 */
    return ((int32_t)(PID->ActualValue));                   /* 返回实际控制数值 */
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint8_t i;
    uint8_t bldc_dir=0;
    static uint8_t times_count=0;                       /* 定时器时间记录 */
    int16_t temp_speed=0;                               /* 临时速度存储 */
    if(htim->Instance == ATIM_TIMX_PWM)                 /* 55us */
    {
        if(g_bldc_motor1.run_flag == RUN)
        {
            /******************************* 三相电流计算 *******************************/
            for(i=0; i<3; i++)
            {
                adc_val_m1[i] = g_adc_val[i+2];         /* UVW三相ADC通道 */
                adc_amp[i] = adc_val_m1[i] - adc_amp_offset[i][ADC_AMP_OFFSET_TIMES];

               if(adc_amp[i] >= 0)                      /* 去除反电动势引起的负电流数据 */
               adc_amp_un[i] = adc_amp[i];
            }
            /* 运算母线电流(母线电流为任意两个有开关动作的相电流之和)*/
            if(g_bldc_motor1.step_sta == 0x05)
            {
                adc_amp_bus = (adc_amp_un[0] + adc_amp_un[1])*ADC2CURT; /* UV */
            }
            else if(g_bldc_motor1.step_sta == 0x01)
            {
                adc_amp_bus = (adc_amp_un[0] + adc_amp_un[2])*ADC2CURT; /* UW */
            }
            else if(g_bldc_motor1.step_sta == 0x03)
            {
                adc_amp_bus = (adc_amp_un[1]+ adc_amp_un[2])*ADC2CURT;  /* VW */
            }
            else if(g_bldc_motor1.step_sta == 0x02)
            {
                adc_amp_bus = (adc_amp_un[0]+ adc_amp_un[1])*ADC2CURT;  /* UV */
            }
            else if(g_bldc_motor1.step_sta == 0x06)
            {
                adc_amp_bus= (adc_amp_un[0]+ adc_amp_un[2])*ADC2CURT;   /* WU */
            }
            else if(g_bldc_motor1.step_sta == 0x04)
            {
                adc_amp_bus= (adc_amp_un[2]+ adc_amp_un[1])*ADC2CURT;   /* WV */
            }
            
            /******************************* 六步换向 *******************************/
            if(g_bldc_motor1.dir == CW)
            {
                g_bldc_motor1.step_sta = hallsensor_get_state(MOTOR_1);
            }
            else
            {
                g_bldc_motor1.step_sta = 7 - hallsensor_get_state(MOTOR_1);
            }
            if((g_bldc_motor1.step_sta <= 6)&&(g_bldc_motor1.step_sta >= 1))     
            {
                pfunclist_m1[g_bldc_motor1.step_sta-1]();
            }
            else    /* 编码器错误、接触不良、断开等情况 */
            {
                stop_motor1();
                g_bldc_motor1.run_flag = STOP;
            }
            /******************************* 速度计算 *******************************/
            g_bldc_motor1.count_j++;                /* 计算速度专用计数值 */
            g_bldc_motor1.hall_sta_edge = uemf_edge(g_bldc_motor1.hall_single_sta);
            if(g_bldc_motor1.hall_sta_edge == 0)    /* 统计单个霍尔信号的高电平时间 */
            {
                /* 计算速度 */
                if(g_bldc_motor1.dir == CW)
                    temp_speed = (SPEED_COEFF/g_bldc_motor1.count_j);
                else
                    temp_speed = -(SPEED_COEFF/g_bldc_motor1.count_j);
                FirstOrderRC_LPF(g_bldc_motor1.speed,temp_speed,0.2379);
                g_bldc_motor1.no_single = 0;
                g_bldc_motor1.count_j = 0;
            }
            if(g_bldc_motor1.hall_sta_edge == 1)
            {
                g_bldc_motor1.no_single = 0;
                g_bldc_motor1.count_j = 0;
            }
            if(g_bldc_motor1.hall_sta_edge == 2)            /* 霍尔值一直不变代表未换向 */
            {
                g_bldc_motor1.no_single++;                  /* 不换相时间累计 超时则判定速度为0 */
                if(g_bldc_motor1.no_single > 15000)
                {
                    g_bldc_motor1.no_single = 0;
                    g_bldc_motor1.speed = 0;                /* 超时换向 判定为停止 速度为0 */
                }
            }
            /******************************* 位置记录以及堵转标记 *******************************/
            if(g_bldc_motor1.step_last != g_bldc_motor1.step_sta)
            {
                g_bldc_motor1.hall_keep_t = 0;
                bldc_dir = check_hall_dir(&g_bldc_motor1);
                if(bldc_dir == CCW)
                {
                    g_bldc_motor1.pos -= 1;
                }
                else if(bldc_dir == CW)
                {
                    g_bldc_motor1.pos += 1;
                }
                g_bldc_motor1.step_last = g_bldc_motor1.step_sta;
            }
            else if(g_bldc_motor1.run_flag == RUN)          /* 运行且霍尔保持时 */
            {
                g_bldc_motor1.hall_keep_t++;                /* 换向一次所需计数值(时间) 单位1/18k */
                if(g_bldc_motor1.hall_keep_t > 15000)       /* 堵转 */
                {
                    g_bldc_motor1.hall_keep_t = 0;
#if LOCK_TAC
                    stop_motor1();
                    g_bldc_motor1.run_flag = STOP;;         /* 标记停机 */
                    g_bldc_motor1.pwm_duty = 0;
#endif
                    g_bldc_motor1.locked_rotor = 1;         /* 标记堵转 */
                }
            }
        /******************************* PID控制 *******************************/
        if(g_bldc_motor1.run_flag == RUN)                   /* 进入PID闭环控制 */
        {
            pid_c_count++;
            pid_s_count++;

            if(pid_s_count > 2)
            {
/* 开启上位机调试 在PID执行之前对调节数值进行预先判断 */
#if DEBUG_ENABLE 
                debug_set_point_range(3000,-3000,6000);     /* 控制目标调节范围(3000~-3000)并且最大步进值不超过6000 RPM */
                debug_data_temp = *user_setpoint;
                if(*user_setpoint < 0)                      /* 上位机指令欲切换旋转方向 */
                {
                    /* 为简化控制逻辑 只以电机状态作为判依据(同步)*/
                    if(g_bldc_motor1.speed == 0)
                    {
                        g_bldc_motor1.dir = CCW;
                    }
                    else if(g_bldc_motor1.dir == CW && g_bldc_motor1.speed != 0)/* 当前状态不能立刻切换 */
                    {
                        *user_setpoint = 0;
                    }
                }
                else if(*user_setpoint > 0)
                {
                    if(g_bldc_motor1.speed == 0)
                    {
                        g_bldc_motor1.dir = CW;
                    }
                    else if(g_bldc_motor1.dir == CCW && g_bldc_motor1.speed != 0)/* 当前状态不能立刻切换 */
                    {
                        *user_setpoint = 0;
                    }
                }
#endif
         /******************************* PID计算 *******************************/
                temp_pwm1 = increment_pid_ctrl(&g_speed_pid,g_bldc_motor1.speed);
                FirstOrderRC_LPF(motor_pwm_s,temp_pwm1,0.085);
                if(motor_pwm_s < 0)
                {
                    motor_pwm_sl = -motor_pwm_s;
                }
                else
                {
                    motor_pwm_sl = motor_pwm_s;
                }

                *user_setpoint = debug_data_temp;   /* 重新保持上位机指令要求 */
                pid_s_count = 0;
            }

            if(pid_c_count > 1)                     /* 电流环 */
            {
                /* 换向尖峰电流大于设定的电流值将导致PID调节转至电流环调节 速度环无法起作用,转速无法调节 */
                if(adc_amp_bus > (g_current_pid.SetPoint - 20))
                {
                    cf_count++;                     /* 滤除换向尖峰电流的影响 */
                    if(cf_count > 4)
                    {
                        cf_count = 0;
                        temp_pwm2 = increment_pid_ctrl(&g_current_pid,adc_amp_bus);
                        FirstOrderRC_LPF(motor_pwm_c,temp_pwm2,0.085);/* 一阶数字滤波 滤波系数0.08 */
                    }
                }
                else
                {
                    cf_count = 0;
                    temp_pwm2 = increment_pid_ctrl(&g_current_pid,adc_amp_bus);
                    FirstOrderRC_LPF(motor_pwm_c,temp_pwm2,0.085);
                }
                pid_c_count = 0;
            }
            /* 电流环输出值大于速度环输出则使用速度环调节 */
            if(motor_pwm_c > motor_pwm_sl)
            {
                g_bldc_motor1.pwm_duty = motor_pwm_sl;
                if(motor_pwm_s < 0)                     /* 正反转积分控制 */
                    g_current_pid.Ui = -g_speed_pid.Ui;
                else
                    g_current_pid.Ui = g_speed_pid.Ui;
            }
            else  /* 速度环输出值大于电流环输出则使用电流环调节 */
            {
                g_bldc_motor1.pwm_duty = motor_pwm_c;
                if(motor_pwm_s < 0)
                    g_speed_pid.Ui = -g_current_pid.Ui;
                else
                    g_speed_pid.Ui = g_current_pid.Ui;
            }
        }
        }
    }
    if(htim->Instance == TIM6)
    {
        /******************************* 采集电机停机状态下的偏置电压 *******************************/
        times_count++;
        if(g_bldc_motor1.run_flag == STOP)
        {
            uint8_t i;
            uint32_t avg[3] = {0,0,0};
            adc_amp_offset[0][adc_amp_offset_p] = g_adc_val[2];     /* 得到还未开始运动时三相的基准电压 */
            adc_amp_offset[1][adc_amp_offset_p] = g_adc_val[3];
            adc_amp_offset[2][adc_amp_offset_p] = g_adc_val[4];

            adc_amp_offset_p++;
            NUM_CLEAR(adc_amp_offset_p,ADC_AMP_OFFSET_TIMES);       /* 最大采集ADC_AMP_OFFSET_TIMES次,超过即从0开始继续采集 */
            for(i = 0; i < ADC_AMP_OFFSET_TIMES; i++)               /* 将采集的每个通道值累加 */
            {
                avg[0] += adc_amp_offset[0][i];
                avg[1] += adc_amp_offset[1][i];
                avg[2] += adc_amp_offset[2][i];
            }
            for(i = 0; i < 3; i++)                                  /* 取平均即软件滤波 */
            {
                avg[i] /= ADC_AMP_OFFSET_TIMES;
                adc_amp_offset[i][ADC_AMP_OFFSET_TIMES] = avg[i];   /* 得到还未开始运动时的基准电压 */
            }
        }
        /******************************* 定时判断电机是否发生堵塞 *******************************/
        if(times_count == SMAPLSE_PID_SPEED)
        {
#if (LOCK_TAC == 2)
            if(g_bldc_motor1.locked_rotor == 1)         /* 堵转处理,当到达一定速度后可进入闭环控制 */
            {
                clc++;
                if(clc > 50)                            /* 延迟2s后重新启动 */
                {
#if DEBUG_ENABLE /*开启调试*/
                    debug_send_motorstate(RUN_STATE);   /* 电机运行*/
#endif
                    clc = 0;
                    pid_init();
                    stop_motor1();
                    g_speed_pid.SetPoint = 400.0;       /* 400PRM */
                    g_bldc_motor1.pwm_duty = 500;       /* 加速启动速度 */
                    g_bldc_motor1.run_flag = RUN;       /* 开启运行 */
                    start_motor1();                     /* 运行电机 */
                    g_bldc_motor1.locked_rotor = 0;
                }
            }
#endif
            times_count = 0;
        }

    }
}

测试结果:

相关推荐
DLGXY1 小时前
STM32 项目实战:温湿度 / 光敏 / 蓝牙 + 风扇 / LED 双闭环控制(一)
stm32·单片机·嵌入式硬件
崇山峻岭之间15 小时前
单片机步进电机实验
单片机·嵌入式硬件
xiangw@GZ17 小时前
802.11全系列标准调制编码与速率档对应关系
网络·单片机·嵌入式硬件·架构
希希之光17 小时前
Aurix Tc3xx Port&Dio模块总结
单片机·嵌入式硬件
日拱一卒的小田17 小时前
ZYNQ学习笔记2-ZYNQ的UART控制器1
单片机·嵌入式硬件
我想走路带风18 小时前
OPENWRT-Day01
stm32·单片机·嵌入式硬件
ACP广源盛1392462567318 小时前
GSV2221@ACP#DP 1.4 MST 多屏转换芯片,物理 AI 多模态交互的视觉中枢
大数据·人工智能·嵌入式硬件·gpt·spark
云栖梦泽19 小时前
Linux内核与驱动:pinctl子系统和GPIO子系统
linux·单片机·嵌入式硬件
电气_空空19 小时前
基于 LabVIEW 的单片机串口通信设计
单片机·嵌入式硬件·毕业设计·labview