STM32控制编码器电机速度闭环实现



利用STM32高级定时器的互补输出模式驱动电机运行
/**
* 函数功能: BDCMOTOR定时器初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void BDCMOTOR_TIMx_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig; // 定时器时钟
TIM_OC_InitTypeDef sConfigOC; // 定时器通道比较输出
TIM_BreakDeadTimeConfigTypeDef sBDTConfig; // 定时器死区时间比较输出
/* 基本定时器外设时钟使能 */
BDCMOTOR_TIM_RCC_CLK_ENABLE();
/* 定时器基本环境配置 */
htimx_BDCMOTOR.Instance = BDCMOTOR_TIMx; // 定时器编号
htimx_BDCMOTOR.Init.Prescaler = BDCMOTOR_TIM_PRESCALER; // 定时器预分频器
htimx_BDCMOTOR.Init.CounterMode = TIM_COUNTERMODE_UP; // 计数方向:向上计数
htimx_BDCMOTOR.Init.Period = BDCMOTOR_TIM_PERIOD; // 定时器周期
htimx_BDCMOTOR.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; // 时钟分频
htimx_BDCMOTOR.Init.RepetitionCounter = BDCMOTOR_TIM_REPETITIONCOUNTER; // 重复计数器
/* 初始化定时器比较输出环境 */
HAL_TIM_PWM_Init(&htimx_BDCMOTOR);
/* 定时器时钟源配置 */
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; // 使用内部时钟源
HAL_TIM_ConfigClockSource(&htimx_BDCMOTOR, &sClockSourceConfig);
/* 死区刹车配置,配置有效电平是低电平 */
sBDTConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE ;
sBDTConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW ;
sBDTConfig.BreakState = TIM_BREAK_DISABLE ;
sBDTConfig.DeadTime = 0 ;
sBDTConfig.LockLevel = TIM_LOCKLEVEL_OFF ;
sBDTConfig.OffStateIDLEMode= TIM_OSSI_DISABLE ;
sBDTConfig.OffStateRunMode = TIM_OSSR_ENABLE ;
HAL_TIMEx_ConfigBreakDeadTime(&htimx_BDCMOTOR,&sBDTConfig);
/* 定时器比较输出配置 */
sConfigOC.OCMode = TIM_OCMODE_PWM1; // 比较输出模式:PWM1模式
sConfigOC.Pulse = PWM_Duty; // 占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; // 输出极性
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_LOW; // 互补通道输出极性
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 快速模式
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; // 空闲电平
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; // 互补通道空闲电平
HAL_TIM_PWM_ConfigChannel(&htimx_BDCMOTOR, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_Base_Start(&htimx_BDCMOTOR);
}

电机控制的几个基本操作又一下几个函数完成
/**
* 函数功能: 设置电机速度
* 输入函数: Duty,输出脉冲占空比
* 返 回 值: 无
* 说 明: 无
*/
void SetMotorSpeed(int16_t Duty)
{
__HAL_TIM_SetCompare(&htimx_BDCMOTOR,TIM_CHANNEL_1,Duty);
}
/**
* 函数功能: 设置电机转动方向
* 输入函数: Dir,电机转动方向
* 返 回 值: 无
* 说 明: 无
*/
void SetMotorDir(int16_t Dir)
{
if(Dir)
{
HAL_TIM_PWM_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出
}
else
{
HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出
}
}
/**
* 函数功能: 设置电机停止
* 输入函数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SetMotorStop()
{
HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出
}
/**
* 函数功能: 设置电机启动
* 输入函数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SetMotorStart()
{
HAL_TIM_PWM_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出
}

电机速度控制反馈信号是利用STM32定时器编码器模式读取电机编码器脉冲信号
/**
* 函数功能: 通用定时器初始化并配置编码器模式
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void ENCODER_TIMx_Init(void)
{
ENCODER_TIM_RCC_CLK_ENABLE();
htimx_Encoder.Instance = ENCODER_TIMx;
htimx_Encoder.Init.Prescaler = ENCODER_TIM_PRESCALER;
htimx_Encoder.Init.CounterMode = TIM_COUNTERMODE_UP;
htimx_Encoder.Init.Period = ENCODER_TIM_PERIOD;
htimx_Encoder.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TIx;
sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC1Filter = 0;
sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC2Filter = 0;
__HAL_TIM_SET_COUNTER(&htimx_Encoder,0);
HAL_TIM_Encoder_Init(&htimx_Encoder, &sEncoderConfig);
__HAL_TIM_CLEAR_IT(&htimx_Encoder, TIM_IT_UPDATE); // 清除更新中断标志位
__HAL_TIM_URS_ENABLE(&htimx_Encoder); // 仅允许计数器溢出才产生更新中断
__HAL_TIM_ENABLE_IT(&htimx_Encoder,TIM_IT_UPDATE); // 使能更新中断
HAL_NVIC_SetPriority(ENCODER_TIM_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ENCODER_TIM_IRQn);
HAL_TIM_Encoder_Start(&htimx_Encoder, TIM_CHANNEL_ALL);
}



编码器电机速度闭环控制由PID算法在调控周期里进行反馈调整
/**
* 函数名称:速度闭环PID控制设计
* 输入参数:当前控制量
* 返 回 值:目标控制量
* 说 明:无
*/
int32_t SpdPIDCalc(float NextPoint)
{
float iError,iIncpid;
iError = (float)sPID.SetPoint - NextPoint; //偏差
if((iError<0.2f )&& (iError>-0.2f))
iError = 0.0f;
iIncpid=(sPID.Proportion * iError) //E[k]项
-(sPID.Integral * sPID.LastError) //E[k-1]项
+(sPID.Derivative * sPID.PrevError); //E[k-2]项
sPID.PrevError = sPID.LastError; //存储误差,用于下次计算
sPID.LastError = iError;
return(iIncpid); //返回增量值
}
/* 速度环周期100ms */
if(uwTick % pid_n_s == 0) //到达一个调控周期
{
//Spd_Pulse 编码器捕获值 Pulse = 累计捕获中断次数*65535 + 通过编码器接口HAL接口读取的脉冲数
Spd_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);
//检测周期里累计的脉冲数=编码器捕获的脉冲数 - 上次捕获的脉冲值
Spd_PPS = Spd_Pulse - LastSpd_Pulse;
//记录为上次捕获的脉冲值(为下次计算使用
LastSpd_Pulse = Spd_Pulse ;
/*变量pid_1=(圈数/周期)*/
float pid_1 = (float)Spd_PPS/(float)PPR ;
//pid_1:pid_n_s周期内电机转动的圈数=周期内捕获的脉冲数 / 每圈的脉冲数
/*变量pid_2=(转/毫秒)*/
float pid_2 = pid_1 / (float)pid_n_s ;
//pid_2:计算实际测量速度(转/毫秒)=周期内转过的圈数 / 周期时间值 (因为周期单位为毫秒)
/*Spd_RPM=(转/秒)*/
Spd_RPM = pid_2 * (float)1000 ;//单位转换为(转/秒)
/* 计算PID结果 */
if(Start_flag == 1)
{
PWM_Duty += SpdPIDCalc(Spd_RPM);
/* 判断当前运动方向 */
if(PWM_Duty < 0)
{
Motor_Dir = CW;
BDDCMOTOR_DIR_CW();
SetMotorSpeed(-PWM_Duty);
}
else
{
Motor_Dir = CCW;
BDDCMOTOR_DIR_CCW();
SetMotorSpeed(PWM_Duty);
}
}
}
