对于经常接触单片机控制步进电机 伺服电机的工程师来说, 步进电机加减速可以有各种实现方法, 本来有可以用的驱动, 可是我总感觉有什么不完善的地方, 抽时间写了个感觉功能足够完善的, 共享一下, 也希望有大神指点指点, 给点意见,
实现的功能有 查表法加减速控制, 可以运动过程中限制最大速度, 运功过程中重新设置目标位置, 如果设置的目标位置在另一方向 或者 在减速范围之内, 会自动减速停止后反向运行到目标位置。 Offset可以对应当前速度, 两个Pos是以脉冲数定义的电机绝对位置, 方向使用了 -1,0 +1, Pos直接 + 方向 来累积位置。
调用方法: 直接设置目标位置, 根据是否停止 决定是否在此设置方向, 其实不设置也没问题, 如果方向不对,中断函数内 也会走错误的方向一步后自动调转方向, 感觉不严谨, so do it here。。。
{
motTurn.aimPos = xxxxxxxx;
if(0 == motTurn.Offset) // 检查停止, 停止可以设置方向, 否则直接en tim
{
if(motTurn.aimPos > motTurn.curPos)
TurnDir(Right);
else TurnDir(Left);
} delay_us(5);
Turn_Enable(ENABLE);}
typedef enum // 方向 枚举
{
Left = (char)-1, Stop = (char)0, Right = (char)1
}Dir;typedef struct // 电机主要数据结构体
{
s32 aimPos;
s32 curPos; //根据 发送的 pluse 确定的位置, 右转为正, 左转为负, 校准时置零
u16 Offset; // 查表偏移 Dir dir; // -1 Left, 1 Right, 0, Stop
u8 err;
};MOT_DAT motTurn;
void TIM2_IRQHandler(void) //TIM2中断, 全部的 电机驱动都在这个中断中
{
static s32 lastAimPos, newPwmNum; //保存的上次目标 位置, 新的需要移动脉冲量
static u32 pwm_num=0; //剩余需要发送 脉冲数
static u8 last_dir=0, stopCal = 0; //
if(TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET) //检查TIM2 ccr2比较中断 发生与否
{
.curPos += motTurn.dir; // 不管还有没有脉冲数, 进此中断, 一个脉冲就发完了, 需要位置 计数
if(lastAimPos != motTurn.aimPos) // 允许 运动过程中 调整目标位置
{
lastAimPos = motTurn.aimPos;
newPwmNum = motTurn.aimPos - motTurn.curPos;
// u32 calPlusNum(u8* stopCalFlag) // use motTurn 这里这个函数未提取出去, //为了可以随意设置目标位置,而不需 检查停止
//{
if(0 == pwm_num)
{
if(newPwmNum > 0)
{
TurnDir(Right);
pwm_num = newPwmNum;
}
else
{
TurnDir(Left);
pwm_num = -newPwmNum;
}
}else
{
if(newPwmNum > 0)
{
if(motTurn.dir == Right)
{
if(pwm_num > newPwmNum)
{
if(newPwmNum >= SP_OFF_MAX)
pwm_num = newPwmNum;
else
stopCal = 1; // 需要停止后 计算, 反向移动
}else
pwm_num = newPwmNum;
}else
stopCal = 1;
}else
{
newPwmNum = -newPwmNum;
if(motTurn.dir == Left)
{
if(pwm_num > newPwmNum)
{
if(newPwmNum >= SP_OFF_MAX)
pwm_num = newPwmNum;
else
stopCal = 1; // 需要停止后 反向移动
}else
pwm_num = newPwmNum;
}else
stopCal = 1;
}
}
}
//}
if(pwm_num > motTurn.Offset) // 确定处于加速还是减速过程
{
if(motTurn.Offset < SP_OFF_MAX) //need 加速 每个电机的加速表最大值
motTurn.Offset++;
else if(motTurn.Offset > SP_OFF_MAX) //need 减速
motTurn.Offset--;
}
else if(motTurn.Offset) //need 减速
motTurn.Offset--; if(pwm_num) //未走完, 计位置,重装值
{
pwm_num--;
TIM_SetAutoreload(TIM2, pwm_value2[motTurn.Offset]);
TIM_SetCompare2( TIM2, pwm_value2[motTurn.Offset]/4);
}else // //走完了 停止 或者 需要反向 走
{
}else //if(pwm_nu == 0) //走完了, 或限位了, 停止...
{
if(stopCal) //需要停止后 再计算反向步数, 反向运行 --为了可以随意设置目标位置,而不需 先停止
{
newPwmNum = motTurn.aimPos - motTurn.curPos;
if(newPwmNum > 0)
{
TurnDir(Right);
pwm_num = newPwmNum;
}
else
{
TurnDir(Left);
pwm_num = -newPwmNum;
}
stopCal = 0;
}else // 目标到达, 停止
{
Turn_Enable(DISABLE);//关闭pwm TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
motTurn.Offset=0; // return;
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2); //清除TIMx更新中断标志
}
}