步进电机梯形加减速开环速度模式的实现

背景介绍

很多介绍步进电机梯形加减速的都源于参考书目《AVR446_Linear speed control of stepper motor.pdf》,正点原子,野火,硬石等都有相应的教程,都讲了固定脉冲数时的梯形加减速原理及对应程序。
在实际项目中,步进除了工作于精确的位置模式,有时还需要步进电机工作于速度模式,并可以方便的更改其旋转速度。本文主要解决步进工作在速度模式下如何通过梯形加减速来实现变速运行。

梯形加减速算法原理详解

梯形加减速详细的算法原理有很多资料参考,不再本文范围内,后续主要介绍梯形加减速如何工作在速度模式下。基本原理介绍可以参考此处步进电机梯形加减速实现

如何工作于速度模式下

只需要将工作模式设置于速度模式,加速完毕后,一直处于RUN即可

步进处于运行时,如何更改速度及加速度

根据新速度,及步进当前速度,判断步进应加速,减速或运行。
当处于RUN时,当前速度就是之前的设定速度。
当处于加减速时 当前速度可以由 下面公式获得。

cur_speed = (int32_t)(A_T_x10 /step_delay);

根据新旧加减速度,判断是否更新accel_count
在这里插入图片描述
通过公式8我们可以计算出新的加速时,对应的C0值,注意此处无需乘以0.676这个修正系数。
通过公式9,我们可以反算出脉冲周期数n=(Cn/C0-C0/Cn)^2/4
Cn是我们当前的定时器周期计数值,C0由新的加速度计算获得。我们就可以得到当前速度对应于新的加速度时所处的脉冲周期数n。用n更新accel_count即可改变加速度大小。
注意此方法忽略了rest的影响。

具体实现代码

#define T1_FREQ_148_NO_676 ((float)((T1_FREQ ) / 10)) 
/// @brief 计算当前速度在新的加速度下的位于第n次脉冲
/// @param cur_delay 当前速度对应的定时器计数值
/// @param accel  新的加速度
/// @return 返回当前速度在新的加速度下的脉冲数
uint32_t get_Accel_Count(uint32_t cur_delay, uint32_t accel)
{
    int32_t C0_delay;
    float ftemp;
    // 计算C0
    C0_delay = (int32_t)((T1_FREQ_148_NO_676 * sqrt(A_SQ / accel)) / 10);
    // 公式Cn/C0=(sqrt(n+1)-sqrt(n)) 逆运算 n=(Cn/C0-C0/Cn)^2/4
    // 计算出当前速度(定时器计数值cur_delay)对应的n
    ftemp = (float)cur_delay / C0_delay - (float)C0_delay / cur_delay;
    // 由新的accel更新accel_count
    return (ftemp * ftemp) / 4;
}
/// @brief 电机旋转函数
/// @param speed 速度 speed更改大于SPEED_THRESHOLD才有效
/// @param accel 加减速 accel更改大于ACCEL_THRESHOLD才有效 speed大于当前速度 则表示加速度 speed小于当前速度 则表示减速度
/// @param dir 方向 仅启动时有效,在更改速度时,无效。如需更改方向,需要先停止再换向。
/// @param channel 通道号
void STEPMOTOR_Rotate(uint32_t speed, uint32_t accel, uint8_t dir, uint8_t channel)
{
    stcPluseChannelState *pPlusChState;
    pPlusChState = &pPluseChannel[channel].State;
    // 达到最大速度时的步数
    // uint32_t max_s_lim;
    uint32_t cur_speed;
    uint32_t old_speed;
    uint32_t old_accel;

    if (pPluseChannel[channel].Para.movemode == FIXEDLENGTH_MODE && pPlusChState->run_state != STOP)
        return;
    if (pPlusChState->run_state == STOP)
    {
        if (speed == 0 || accel == 0)
            return;
        // 记录参数
        pPluseChannel[channel].Para.dir = dir;
        pPluseChannel[channel].Para.movemode = SPEED_MODE;
        pPluseChannel[channel].Para.set_speed = speed;
        pPluseChannel[channel].Para.step_accel = accel;
        pPluseChannel[channel].Para.step_decel = accel;
        pPlusChState->dir = dir;
        pPlusChState->steps = 0;
        pPlusChState->step_count = 0;
        // pPlusChState->step_position = 0;

        pPlusChState->min_delay = (int32_t)(A_T_x10 / speed);
        pPlusChState->step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel)) / 10);

        pPlusChState->run_state = ACCEL;        // 以加速开始,遇到极限之后就停下
        pPlusChState->decel_start = 0xFFFFFFFF; // 开始减速的位置设为最大,步数达不到最大值就不会减速
        // 复位加速度计数值
        // accel_count必须复位否则加速效果有误
        pPlusChState->accel_count = 0;
        STEPMOTOR_StartChannel(dir, channel, pPlusChState->step_delay);
    }
    else
    {
        if (accel == 0)
            return;
        old_speed = pPluseChannel[channel].Para.set_speed; //(int32_t)(A_T_x10 / pPlusChState->min_delay);
        if (abs(speed - old_speed) < SPEED_THRESHOLD)//
        { // 如果目标速度没有改变
            old_accel = pPluseChannel[channel].Para.step_accel;
            // 如更改加速度,就重新计算 accel_count
            if (abs(accel - old_accel) > ACCEL_THRESHOLD)
            {
                pPlusChState->accel_count = get_Accel_Count(pPlusChState->step_delay, accel);

                if (pPlusChState->run_state == DECEL)
                {
                    pPluseChannel[channel].Para.step_decel = accel;
                    if (pPlusChState->accel_count > 0)
                    {
                        pPlusChState->accel_count = -pPlusChState->accel_count;
                    }
                }
                else if (pPlusChState->run_state == ACCEL)
                {
                    pPluseChannel[channel].Para.step_accel = accel;
                }
                else if (pPlusChState->run_state == RUN)
                {
                }
            }
        }
        else
        {
            cur_speed = (int32_t)(A_T_x10 / pPlusChState->step_delay);
            if (abs(speed - cur_speed) < SPEED_THRESHOLD)
            { // 新速度刚好等于当前速度
                if (pPlusChState->accel_count < 0)
                    pPlusChState->accel_count = -pPlusChState->accel_count;
                pPlusChState->steps = 0;
                pPlusChState->step_count = 0;
                pPlusChState->run_state = RUN;
            }
            else if (speed > cur_speed)
            { // 新速度大于当前速度,继续加速
                old_accel = pPluseChannel[channel].Para.step_accel;
                // 如更改加速度,就重新计算 accel_count
                if (abs(accel - old_accel) > ACCEL_THRESHOLD)
                {
                    pPlusChState->accel_count = get_Accel_Count(pPlusChState->step_delay, accel);
                    pPluseChannel[channel].Para.step_accel = accel;
                }
                if (pPlusChState->run_state == DECEL)
                {
                    if (pPlusChState->accel_count < 0)
                    {
                        pPlusChState->accel_count = -pPlusChState->accel_count;
                    }
                }
                else if (pPlusChState->run_state == ACCEL)
                {
                }
                else if (pPlusChState->run_state == RUN)
                {
                }
                pPlusChState->min_delay = (int32_t)(A_T_x10 / speed);
                pPlusChState->run_state = ACCEL;
            }
            else
            { // 新速度小于当前速度,则减速
                old_accel = pPluseChannel[channel].Para.step_decel;
                // 如更改加速度,就重新计算 accel_count
                if (abs(accel - old_accel) > ACCEL_THRESHOLD)
                {
                    pPlusChState->accel_count = get_Accel_Count(pPlusChState->step_delay, accel);
                    pPluseChannel[channel].Para.step_decel = accel;
                }

                if (pPlusChState->accel_count > 0)
                {
                    pPlusChState->accel_count = -pPlusChState->accel_count;
                }
                if (speed > 0)
                {
                    pPlusChState->min_delay = (int32_t)(A_T_x10 / speed);
                }
                else
                {
                    pPlusChState->min_delay = INT32_MAX; // 最大的int32值。
                }
                pPlusChState->run_state = DECEL;
            }
        }
        pPluseChannel[channel].Para.set_speed = speed;
    }
}
//速度决策函数,中断内调用
__STATIC_INLINE void IsrProcess(uint8_t channel)
{
    // 保存新(下)一个延时周期
    uint32_t new_step_delay = 0;
    int32_t temp1 = 0;
    int32_t temp2 = 0;
    __IO static uint32_t last_accel_delay = 0;

    // 记录new_step_delay中的余数,提高下一步计算的精度
    __IO static int32_t rest = 0;
    //	  __IO static  uint8_t flg=0;
    stcPluseChannelState *pPlusChState;
    pPlusChState = &pPluseChannel[channel].State;

    switch (pPlusChState->run_state) // 加减速曲线阶段
    {
    case STOP:
        pPlusChState->step_delay = 0; // 清零步数计数器
        rest = 0;                     // 清零余值
                                      // 关闭通道
        STEPMOTOR_StopChannel(channel);
        //         	flg=0;
        break;
    case ACCEL:
        pPlusChState->step_count++; // 步数加1
        if (pPlusChState->dir == CW)
        {
            pPlusChState->step_position++; // 绝对位置加1
        }
        else
        {
            pPlusChState->step_position--; // 绝对位置减1
        }
        pPlusChState->accel_count++; // 加速计数值加1
        temp1 = ((2 * pPlusChState->step_delay) + rest);
        temp2 = (4 * pPlusChState->accel_count + 1);
        new_step_delay = pPlusChState->step_delay - temp1 / temp2;
        rest = temp1 % temp2;
        //        new_step_delay = pPlusChState->step_delay - (((2 *pPlusChState->step_delay) + rest)/(4 * pPlusChState->accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
        //        rest = ((2 * pPlusChState->step_delay)+rest)%(4 * pPlusChState->accel_count + 1);// 计算余数,下次计算补上余数,减少误差

        if (new_step_delay <= pPlusChState->min_delay) // 检查是否到达期望的最大速度
        {
            last_accel_delay = new_step_delay;        // 保存加速过程中最后一次延时(脉冲周期)
            new_step_delay = pPlusChState->min_delay; // 使用min_delay(对应最大速度speed)
            rest = 0;                                 // 清零余值
            pPlusChState->run_state = RUN;            // 设置为匀速运行状态
                                                      //            printf("srd.run_state =%d srd.accel_count=%d \r\n",pPlusChState->run_state,pPlusChState->accel_count);
        }
        if (pPluseChannel[channel].Para.movemode == FIXEDLENGTH_MODE)
        {
            if (pPlusChState->step_count >= pPlusChState->decel_start) // 检查是否应该开始减速
            {
                pPlusChState->accel_count = pPlusChState->decel_val; // 加速计数值为减速阶段计数值的初始值
                pPlusChState->run_state = DECEL;                     // 下个脉冲进入减速阶段
            }
            // 检查是否为最后一步
            if (pPlusChState->step_count >= pPlusChState->steps)
            {
                pPlusChState->run_state = STOP;
            }
        }
        break;
    case RUN:
        pPlusChState->step_count++; // 步数加1
        if (pPlusChState->dir == CW)
        {
            pPlusChState->step_position++; // 绝对位置加1
        }
        else
        {
            pPlusChState->step_position--; // 绝对位置减1
        }
        new_step_delay = pPlusChState->min_delay; // 使用min_delay(对应最大速度speed)
        //					flg=0;
        // 检查是否为最后一步//pPlusChState->steps=0表示一直运行
        if (pPluseChannel[channel].Para.movemode == FIXEDLENGTH_MODE)
        {
            if (pPlusChState->step_count >= pPlusChState->decel_start) // 需要开始减速
            {
                pPlusChState->accel_count = pPlusChState->decel_val; // 减速步数做为加速计数值
                new_step_delay = last_accel_delay;                   // 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
                pPlusChState->run_state = DECEL;                     // 状态改变为减速
            }
            if (pPlusChState->step_count >= pPlusChState->steps)
            {
                pPlusChState->run_state = STOP;
            }
        }
        break;

    case DECEL:
        pPlusChState->step_count++; // 步数加1

        if (pPlusChState->dir == CW)
        {
            pPlusChState->step_position++; // 绝对位置加1
        }
        else
        {
            pPlusChState->step_position--; // 绝对位置减1
        }
        pPlusChState->accel_count++;
        temp1 = ((2 * pPlusChState->step_delay) + rest);
        temp2 = (4 * pPlusChState->accel_count + 1);
        new_step_delay = pPlusChState->step_delay - temp1 / temp2;
        rest = temp1 % temp2;
        //        new_step_delay = pPlusChState->step_delay - (((2 * pPlusChState->step_delay) + rest)/(4 * pPlusChState->accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
        //        rest = ((2 * pPlusChState->step_delay)+rest)%(4 * pPlusChState->accel_count + 1);// 计算余数,下次计算补上余数,减少误差
        if (pPluseChannel[channel].Para.movemode == SPEED_MODE)
        {
            // 检查是否为最后一步
            if (pPlusChState->accel_count >= 0)
            {
                if (pPluseChannel[channel].Para.set_speed == 0)
                {
                    pPlusChState->run_state = STOP;
                    break;
                }
            }

            if (new_step_delay > pPlusChState->min_delay)
            {
                new_step_delay = pPlusChState->min_delay;
                pPlusChState->min_delay = pPlusChState->min_delay;
                if (pPlusChState->accel_count < 0)
                    pPlusChState->accel_count = -pPlusChState->accel_count;
                pPlusChState->run_state = RUN;
            }
        }
        else if (pPluseChannel[channel].Para.movemode == FIXEDLENGTH_MODE)
        {
            // 检查是否为最后一步
            if (pPlusChState->accel_count >= 0 || pPlusChState->step_count >= pPlusChState->steps)
            {
                pPlusChState->run_state = STOP;
            }
        }

        break;
    default:
        break;
    }
    pPlusChState->step_delay = new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
    // rt_kprintf("%d %d\r\n", pPlusChState->accel_count, pPlusChState->step_delay);
}

使用示例

    STEPMOTOR_Rotate(1200, 12000 ,0,0);
    rt_thread_mdelay(100);
    STEPMOTOR_Rotate(2400, 12000, 0,0);
    rt_thread_mdelay(200);
    STEPMOTOR_Rotate(2400, 120000, 0,0);
    rt_thread_mdelay(200);
    STEPMOTOR_Rotate(1200, 120000, 0,0);
    rt_thread_mdelay(200);
    STEPMOTOR_Rotate(1200, 12000, 0,0);
    rt_thread_mdelay(100);
    STEPMOTOR_Rotate(2400, 12000, 0,0);
    rt_thread_mdelay(200);
    STEPMOTOR_Rotate(0, 12000, 0,0);
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
步进电机是一种常用于控制机械运动的电机,其控制方式简单,可靠性高。丝杠滑台作为一种常见的机械结构,可以通过步进电机的驱动来实现运动控制。本文将介绍如何使用STM32驱动步进电机实现丝杠滑台的开环控制。 ### 步进电机驱动 步进电机是一种转换输入脉冲数为旋转角度的电机,其输出转速与输入脉冲频率成正比,转动角度与输入脉冲数成正比。步进电机有两种控制方式:全步进和半步进。 全步进控制方式是指每次输入一个脉冲,电机就会转动一个固定的角度,通常为1.8度或0.9度。全步进控制方式简单可靠,但转动精度较低。 半步进控制方式是指每次输入一个脉冲,电机会转动一个固定的角度的一半,即0.9度或0.45度。半步进控制方式可以提高转动精度,但控制复杂度略高。 在STM32驱动步进电机时,可以选择使用全步进或半步进控制方式。具体控制方式根据应用需求来选择。 ### 丝杠滑台控制 丝杠滑台是一种常见的机械结构,通过丝杠的旋转来实现滑台的线性运动。丝杠的旋转可以通过步进电机的驱动来实现。丝杠的螺距与步进电机的步距角之间存在比例关系,通过控制步进电机的步进数和方向,可以实现丝杠的旋转控制,从而实现滑台的线性运动控制。 在开环控制中,可以通过设置步进电机的旋转速度和步进数来控制滑台的运动速度和位置。具体控制方式需要根据应用需求来选择。 ### STM32驱动步进电机实现丝杠滑台开环控制 以下是使用STM32驱动步进电机实现丝杠滑台开环控制的基本步骤: 1. 初始化GPIO口和定时器:使用STM32的GPIO口和定时器来控制步进电机的旋转和控制丝杠滑台的运动。 2. 配置定时器的计数模式:定时器的计数模式需要根据步进电机的控制方式来选择。 3. 配置定时器的时钟源和分频系数:定时器的时钟源和分频系数需要根据具体的步进电机控制方式来选择。 4. 设置定时器的计数值:定时器的计数值需要根据步进电机的旋转速度来选择。 5. 设置步进电机的方向:根据丝杠滑台的运动方向来设置步进电机的方向控制引脚。 6. 发送脉冲信号:通过控制定时器的计数值来发送脉冲信号,从而驱动步进电机旋转。 以上是使用STM32驱动步进电机实现丝杠滑台开环控制的基本步骤。具体实现时需要根据具体的硬件和软件环境进行调整和优化。 总之,STM32驱动步进电机实现丝杠滑台开环控制是一种常见的机械运动控制方式,其控制方式简单可靠,适用于许多应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值