般PLC 运动控制卡都有实现电机的T型 S型加减速算法,我们只需要设置起始最小速度(Pulse/S),最终最大速度(Pulse/s),加速时间(ms)即可,这样使用起来很方便和直观。但有些控制场合不是使用PLC或运动控制卡,而是我们自己做的STM32控制板时,这些加速算法就需我们自己来实现了。
步进电机加速一般有S型加速和T型加速,T型由于电机的运动状态包含匀加速 匀速 匀减速三种状态,运动轨迹看起来像个等腰梯形,所以叫T型加速.
加速数据生成
加减速部分速度与时间的关系为:
V
=
a
∗
t
+
V
0
V=a*t+V_0
V=a∗t+V0
V
0
V_0
V0就是起始速度SpeedMin
以下生成的数据,是以定时器频率为12M为基准:
/***************************************************************************************************************
* 函数名称: GetTData
* 函数描述: 根据y=a*x+b线性加速,斜率a=(SpeedMax-SpeedMin)/nAccTime,b=SpeedMin,nAccTime单位为ms
* 参数 pTData 生成的加速数据
* 返回值:数组的长度
* 其它说明:
*
****************************************************************************************************************/
short GetTData(unsigned short* pTData,short SpeedMin,short SpeedMax,short nAccTime)
{
short TabLen= 0;
double tSum=0;
short RealSpeed=SpeedMin;//实时速度初始值
unsigned int nAccTimer = nAccTime*1000*12; //加速时间对应的定时器的数值:1S定时器数值变化12M次,那么1ms就变化12000次
short deltaSpeed = (SpeedMax-SpeedMin); //为了加快计算速度,所以预先计算斜率数值的一部分
while(RealSpeed<SpeedMax)
{
RealSpeed = deltaSpeed*tSum/nAccTimer+SpeedMin;//deltaSpeed/nAccTimer是斜率a,SpeedMin是b
pTData[Len] = 12000000/2*RealSpeed;//由于发一个高电平然后一个低电平电机才走一步,所以单个电平的速度需要乘2
tSum += 2*pTData[Len]; //走一步需要一个高电平和低电平所以*2
TabLen++;
}
return TabLen;
}
定时器设置
/***************************************************************************************************************
* 函数名称: 定时器初始化
* 函数描述:
* 其它说明:
*
****************************************************************************************************************/
void MX_TIM_Init(TIM_TypeDef* TIMX,TIM_HandleTypeDef* htim,uint32_t nPresc)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim->Instance = TIMX;
htim->Init.Prescaler = nPresc-1;
htim->Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数模式
htim->Init.Period = 0;
htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;
htim->Init.RepetitionCounter = 0;
if(HAL_TIM_Base_Init(htim) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; //内部时钟
if (HAL_TIM_ConfigClockSource(htim, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(htim, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
void InitTimer()
{
SystemClock_Config();
MX_TIM_Init(TIM1,&htim1,14); //时钟频率为168/14=12Mhz
MX_TIM_Init(TIM2,&htim2,7);
MX_TIM_Init(TIM3,&htim3,7);
MX_TIM_Init(TIM4,&htim4,7);
MX_TIM_Init(TIM5,&htim5,7);
MX_TIM_Init(TIM8,&htim8,14);
MX_TIM_Init(TIM9,&htim9,14);
MX_TIM_Init(TIM10,&htim10,14);
MX_TIM_Init(TIM11,&htim11,14);
MX_TIM_Init(TIM12,&htim12,7);
MX_TIM_Init(TIM13,&htim13,7);
MX_TIM_Init(TIM14,&htim14,7);
}
路径规划
在电机运动前需要先计算好加速的步数,匀速的步数,减速的步数.
/***************************************************************************************************************
* 函数名称: 路径规划
* 函数描述: speed-运行速度,step-需要走的步数
* 其它说明:
*
****************************************************************************************************************/
void PlanMovePath(unsigned short speed,unsigned int step)
{
unsigned short nTimerSpeed = 12000000/2*speed; //speed 1S脉冲个数 12M
for(int i=0;i<TabLen;i++)
{
if(pTData[i]<=nTimerSpeed)
{
TabIndex = i;
break;
}
}
if(speed>SpeedMax)
{
TabIndex = TabLen;
}
if((TabIndex) <= (step>>1)) //梯形走法
{
AccStep = TabIndex;
ConstStep = step - (TabIndex)*2;
DecStep = AccStep;
}
else//三角形
{
pSmCtrl->AccStep = (step>>1);
pSmCtrl->ConstStep = 0;
pSmCtrl->DecStep = step-AccStep;
}
if(step==1)
{
AccStep = 1;
ConstStep = 0;
DecStep = 0;
}
}
中断响应
void StepMotorTimerISR()
{
if (CurrentPulseLevel==HIGH_LEVEL)
{
CurrentPulseLevel = LOW_LEVEL;
ResetPin(Pulse);
return ;
}
if (AccStep!=0) //加速阶段
{
AccStep--;
ReloadTimer(pTimer,pTData[CurrentSpeed]);
CurrentPulseLevel = HIGH_LEVEL;
SetPin(Pulse);
}
else if (ConstStep!=0)//匀速阶段
{
ConstStep--;
CurrentPulseLevel = HIGH_LEVEL;
SetPin(Pulse);
}
else if (DecStep!=0) //减速阶段
{
DecStep--;
if (CurrentSpeed>0)
{
pCurrentMotor->CurrentSpeed--;
}
ReloadTimer(pTimer,pTData[CurrentSpeed]);
CurrentPulseLevel = HIGH_LEVEL;
SetPin(Pulse);
}
else
{
StopTimer(pTimer);
SetPin(Enable);
ResetPin(Pulse);
}
}