单片机 :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;
}
}
}
测试结果:
