ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《DMG474电机控制专题教程V1.1 》第17章 步进电机S形加减速

《DMG474电机控制专题教程V1.1 》第17章 步进电机S形加减速

原创 嵌入式/内核开发 作者:正点原子 时间:2026-04-22 10:09:23 0 删除 编辑

第17章 步进电机S形加减速


       上一章学习的梯形加减速算法其特点是:算法简便,占用时少、响应快、效率高,实现方便。但匀加速和匀减速阶段不符合步进电机速度变化规律,在变速和匀速转折点不能平滑过渡。启动、停止、高速运动段会产生较大的冲击和振动及噪音所以这种算法主要应用在对升降速过程要求不高的场合,如简单的定长送料。也就是说在一些精密的场合,梯形加减速算法并不适用。而是用到我们本章所学习的内容:S形加减速算法! 

       本章分为如下几个小节:

       17.1 S形曲线加减速的简介

       17.2 S形曲线加减速的的原理分析

       17.3 S型曲线加减速的控制实现


        17.1 S形曲线加减速的简介

       还是以梯形加减速章节提到的模型为例,如果滑块从启动速度到目标速度的加减速不是以固定的比例进行加速/减速,而在加减速的变化过程中速度曲线呈现一个英文字母“S”形的,我们称之为S形加减速算法。则上述将这个过程描述为如下图所示,


17.1.1 S曲线加减速模型


       可以获知OA段其实就是滑块的加速部分、AB则是匀速部分,BC则是减速部分。

       l 在OA加速过程中,速度刚开始是缓慢增加,后来增加得越来越快,而在中点时刻,增加又有所放慢,但依然继续增加逼近设定的速度。实际这一阶段又分成了三个阶段

       l 在AB匀速过程中,加速到设定速度之后,以设定速度匀速步进;

       l 在BC减速部分中,以设定的速度开始按照加速度段的变化规律做减速变化,直到速度降至0后停止。

       前面我们有提到梯形加减速的缺点,梯形加减速在启动、停止和高速运动的过程中会产生很大的冲击力振动和噪声,所以多数会应用于简单的定长送料的应用场合中,例如常见的3D打印机使用的就是梯形加减速算法;但是相比较S形加减速在启动停止以及高速运动时的速度变化的比较慢,导致冲击力噪音就很小,但这也决定了他在启动停止时需要较长的时间,所以多数适用于精密的工件搬运与建造。


        17.2 S形曲线加减速的的原理分析

       实际上要实现S型可以采用的方法有很多,在传统的S形曲线加减速算法中,它包括七个运动阶段:加加速阶段,恒加速阶段,减加速阶段,恒速阶段,加减速阶段,恒定减速阶段和减减速阶段。


图17.2.1 S曲线加减速七段式模型


       虽然该算法具有平稳、精度高的特点,但是也可以感受到该算法的参数复杂,大大降低到了工作效率,而且对硬件的要求也比较高。因此,本文介绍的是一种五段S曲线加减速算法并通过查表的方式进行实现,这是一种简单但具有实时性和高精度的加减速控制算法,非常适合资源紧凑的小型嵌入式系统。


图17.2.2  S曲线加减速五段式模型


       在图17.2.1中T1、T2、T3、T4、T5分别代表加加速阶段、减加速阶段、恒速阶段、加减速阶段和减减速阶段的时间。为了使该开始点和结束点的加加速度在减加速阶段为零,加加速阶段的时间必须等于减加速阶段的时间,即T1=T2,T4=T5=T。

       以加速段为例,第一分为加加速段,这部分是加速度a从0增大到最大值Amax,第二部分是减加速度段,Amax从最大值减少至0(注意是加速度的变化!在加速度段,速度是一直在增加的!)也就是说两部的划分是看加速度a的最大值,减速度段也同理,只不过加速度是相反变化的。


       17.2.1 确定已知量和未知量

       S形加减速我们需要求解的未知量如下:

       (1)加速阶段时的加加速度段需要多少脉冲数?使用n1进行表示

       (2)加速阶段时的减加速度段需要多少脉冲数?使用n2进行表示

       (3)下一个变化的脉冲的周期是多少?使用Cn进行表示

       同梯形算法一样,无论速度怎么变化,给定的距离是不变的,只要知道加速度段的距离和减速段的距离,确定加速阶段和减速阶段需要提供多少个脉冲,然后让控制器一个一个把脉冲送出去即可。但是这里需要说明的是,因为本文采用五段S曲线加减速算法模型使用对称结构,所以当我们知道了加速度段距离求解算法,同理就知道了减速度距离的求解算法,它俩算法一样,只是速度排序为倒序关系。所以这里不用另外求解减速度段的距离,这也是五段式的优点,如下图所示:


图17.2.1.1 S曲线加减速各阶段和脉冲的关系图解


       在进行S形加减速前我们必须要确定的目标量有如下:

       (1)电机需要步进的距离,即用户必须要明确需要电机步进的步数,使用step表示

       (2)加速度所需时间,使用ts1表示

       (3)减速度所需时间,使用ts2表示

       (4)最大速度大小,决定电机匀速时的速度值,用v表示

       这几个参数一起决定电机将以多快的速度接进目标值,需要注意的是,因为s型加减速的加速度和减速度是随时间变化的,这一点和梯形的加速度是固定的有所不同,所以不能确定,但是只要我们给出确定的时间和末速度就可以算出要步进的步数。确定了目标量,下面将对未知量进行具体求解。


       17.2.2 获取加加速度段的步数S1

       因为加速度段被分成了加加速度段和减加速度段,并且对应着两个不同的加速度,一个以加加速的方式逼近目标值,一个以减加速的方式逼近目标值,所以两部分所需要的步数也是不相同的,因为求解加速度段所需要的步数,可以通过求解加加速度段与减加速度段的和进行得出:

       要获取位移(步数),可知先获取速度,对速度v的积分就可以得到位移,如下所示:


[式2-1]


       要先求取速度,可对加速度a积分则得速度的变化量,如下:


[式2-2]


       实际的速度V1为初始速度加速度的变化量,如下:

       a为加速度,加速度a从0变化到最大值,有如下:(其中J是加加速度(jerk),即速度的变化率,)因为这里为加加速度段,对应的时间t也是在一定范围内(t=[0,t1])


[式2-3]


       结合两式可得位移公式:


[式2-4]


       可知,如果我们知道加加速度,并带入我们之前设置好的时间,就可以知道加加速度段所需要的距离,遗憾的是我们还不知道加加速度,所以需要先求解加加速度,根据图形模型可知,速度和时间的比值就是加加速度(斜率),速度是等于加速度的积分,则有如下:


[式2-4]


       而加速度在加速度段呈现两种变化,也就是在加速段有两种加速度斜率,我们知道在减加速度部段的加速斜率是以加加速度部段为基础反向变化的,所以只要知道加加速度段斜率就可知减加速度斜率,那么如何求解第一部分的加速度斜率?


图17.2.2.1 S曲线加减速各阶段速度解析


       如上图所示:第一部分和第二部分的加速是以加速度最大值作为划分的,而加速度最大值下对应着从初速度速度到末速度的中点速度,当加速度a随着时间变化到最大值时速度V= Vm,由于初速度为0,中点速度即为末速度的一半,这里的时间为t=T1,可得加加速度:


[式2-6]


       由于Vt是用户设置的已知的,所以就可以求出J1:


[式2-7]


       将就得的值J1代入[式2-4],可得加加速度段的距离为:


[式2-8]


       需要注意的是:为了使S1的数值直接对应步数,即S1的单位为Step,(如S1=2,则为2步,即需要两个脉冲),在设定速度时,Vm的单位应该设置为Step/s,表示1秒内可步进多少步数,这样设置还有一个好处是,速度v的倒数1/V(每一步对应的时间)直接对应要输出脉冲的周期,于是乎我们的问题3(下一个变化的脉冲的周期Cn是多少?)就迎刃而解,不用和梯形那样进行繁琐的推导,只要知道速度即可,在后面论述中我们会重点介绍。

       那么知道了加加速度段的距离,再求解减加速度段的距离就能得到加速段的距离。


       17.2.3 获取减加速度段的步数S2

       要获取减加速度段的位移,同样要知道速度和加速度,而减加速度段的加加速度J是和加加速度段是相反的,即为-J,所以有加速度为a2,其中时间t在初始时t=t1,t1为加加速度段的所用时间,t2-t1为减加速段所用时间,则有(t=[t1,t2]):



       其中,J· t 1为Vm处的加速度。

       但我们不采用这种常规分析的方法,这里我们基于末速度进行逆推,在末速度Vt的基础上逆向分析运动过程,如下图所示:


图17.2.3.2 逆向分析减加速段


       此时曲线所示运动过程可以看作是初速度为Vt,末速度为Vm,加速度不断增加的变减速运动,因为加速度方向和运动方向相反,有(t∈[t1,t2]):


[式2-9]


       对加速度进行积分则可得到对应的速度为(t=[t1,t2]):


[式2-10]


       其中Vt为这个阶段的末速度:(t=[t1,t2])


[式2-11]


       则对速度进行积分求得位移:


[式2-12]


       已知加加速阶段时间与减加速阶段时间相等,即t2-t1 = t1,所以减加速度段的位移段S2:


[式2-13]


        因最终加速度段的位移等于:


[式2-14]


       17.2.4 确定下一个脉冲的周期

       下一个脉冲的周期决定了电机速度的快慢,根据我们前面的论述,速度的倒数对应每一个脉冲的周期Tn(Tn=Cn*1/Ft)。



       所以对应的定时器计数值为:



       因此要想求出每一步对应的速度,以加加速度段为例,加加速度J已经确定,根据位移公式 ,必须要知道每一步速度下对应的时间t,而这个t可以通过位移 求得,如当S1=1,即第一步的对应时间为t1,同理第二步对应的时间为t2,而t2等于第一步时间和第二步脉冲周期的和(t2=t1+T2),因此t2-t1的时间就等于第二步脉冲的周期T2,但是我们不采用这种方法计算。

       我们只需要知道第一步所需的时间t1,通过t1计算出对应的速度V,速度的倒数1/V,就是第二步脉冲的周期,然后将第一步时间t1加上第二步周期T2就是位移公式中步进到第二步的时间总和,以此循环即可得出每一步的脉冲周期,当然了,如果是减加速度段,就使用减加速度段的公式即可。


        17.3 S型曲线加减速的控制实现


       17.3.1 硬件设计


       1、例程功能

       本实验我们将使用步进电机接口1控制电机实现S形加减速运动,驱动器设置的1.0A,8细分,也就是旋转一圈所需要1600个脉冲数,按键KEY0控制开启步进电机S形加减速运动,KEY1增加总步数,KEY2减少总步数,LED0作为程序运行状态指示灯,闪烁周期200ms。


       2、硬件资源

       本实验的硬件设计与前面的步进电机梯形加减速控制章节中的硬件设计完全相同,可查看前面的连接,在此不再赘述。


       17.3.2 程序设计


       17.3.2.1 S形加减速控制的配置步骤 

       1)开启TIMx和通道输出的GPIO时钟,配置该IO口的复用功能输出。

       首先开启TIMx的时钟,然后配置GPIO为复用功能输出。本实验我们默认用到定时器8通道1对应IO为PC6它的时钟开启方法如下:

__HAL_RCC_TIM8_CLK_ENABLE();            /* 使能定时器8 */
__HAL_RCC_GPIOC_CLK_ENABLE();           /* 开启GPIOC时钟 */

       IO口复用功能是通过函数HAL_GPIO_Init来配置的。

       2)初始化TIMx,设置TIMx的ARR和PSC等参数。

       使用定时器的比较输出翻转模式功能时,我们调用的是HAL_TIM_OC_Init函数来初始化定时器ARR和PSC等参数。

       注意:该函数会调用:HAL_TIM_OC_MspInit函数来完成对定时器底层以及其输出通道IO的初始化,包括:定时器及GPIO时钟使能、GPIO模式设置、中断设置等。

       3)设置定时器为比较输出翻转模式,输出比较极性,比较值等参数。

       在HAL库中,通过HAL_TIM_OC_ConfigChannel函数来设置定时器为翻转模式,根据需求设置输出比较的极性,设置比较值(控制占空比)等。

       本实验我们设置TIM8的通道1为翻转模式。 

       4)初始化方向DIR引脚,脱机使能EN引脚

       初始化电机接口1的的方向引脚以及使能引脚。

       5)编写S加减速的速度表函数

       计算 S 加减速的速度表。

       6)编写步进电机运动状态判断函数

       通过对步进电机的步数、加减速度和最大速度的设置来决定步进电机的运动。

       7)编写中断服务函数

       在中断里边对步进电机状态进行决策。


       17.3.2.2 程序流程图


图17.3.2.2.1 S形加减速流程图


       17.3.2.3 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。首先就是表格查找法,同梯形加减速不同的是,这里的S曲线加减速使用的查表法直接读取速度值。 

       (1)加减速度表的生成,在stepper_motor.c文件中:

/**
 * @brief       速度表计算函数
 * @param       vo,初速度;vt,末速度;time,加速时间
 * @retval      TRUE:成功;FALSE:失败
 */
uint8_t calc_speed(int32_t vo, int32_t vt, float time)
{
    uint8_t is_dec = FALSE;
    int32_t i = 0;
    int32_t vm =0;                /*  中间点速度 */
    int32_t inc_acc_stp;          /*  加加速所需的步数 */
    int32_t dec_acc_stp;          /*  减加速所需的步数 */
    int32_t accel_step;           /*  加速或减速需要的步数 */
    float jerk = 0;               /*  加加速度 */
    float ti = 0;                 /*  时间间隔 dt */
    float sum_t = 0;              /*  时间累加量 */
    float delta_v = 0;            /*  速度的增量dv */
    float ti_cube = 0;            /*  时间间隔的立方 */
    float *velocity_tab = NULL;  /*  速度表格指针 */
 
    if(vo > vt )             /* 初速度比末速度大,做减速运动,数值变化跟加速运动相同*/
    {         /*  只是建表的时候注意将速度倒序 */
        is_dec = TRUE;
        g_calc_t.vo = ROUNDPS_2_STEPPS(vt);    /*  起速:step/s */
        g_calc_t.vt = ROUNDPS_2_STEPPS(vo);    /*  末速:step/s */
    }
    else
    {
        is_dec = FALSE;
        g_calc_t.vo = ROUNDPS_2_STEPPS(vo);
        g_calc_t.vt = ROUNDPS_2_STEPPS(vt);
    }
 
    time = ACCEL_TIME(time);                  /* 得到加加速段的时间 */
    printf("time=%f\r\n",time);
    vm =  (g_calc_t.vo + g_calc_t.vt) / 2 ;   /* 计算中点速度 */
    
    jerk = fabs(2.0f * (vm - g_calc_t.vo) /  (time * time));/* 计算加加速度 */
/*  加加速需要的步数 */
    inc_acc_stp = (int32_t)(g_calc_t.vo * time + INCACCELSTEP(jerk,time));      
/*  减加速需要的步数 S = vt * time - S1 */
    dec_acc_stp = (int32_t)((g_calc_t.vt + g_calc_t.vo) * time - inc_acc_stp);  
 
    /* 申请内存空间存放速度表 */
    accel_step = dec_acc_stp + inc_acc_stp;    /* 加速需要的步数 */
    if( accel_step  % 2 != 0)  /* 由于浮点型数据转换成整形数据带来了误差,所以这里加1*/
        accel_step  += 1;
    /* mallo申请内存空间,记得释放 */
velocity_tab = (float*)(mymalloc(SRAMIN,((accel_step + 1) * 
sizeof(float))));
    if(velocity_tab == NULL)
    {
        printf("内存不足!请修改参数\r\n");
        return FALSE;
    }
/*
 * 目标的S型速度曲线是对时间的方程,但是在控制电机的时候则是以步进的方式控制,所以这里对V-t
 * 曲
 * 线做转换
 * 得到V-S曲线,计算得到的速度表是关于步数的速度值.使得步进电机每一步都在控制当中
 */
/*  计算第一步速度,根据第一步的速度值达到下一步的时间 */
    ti_cube  = 6.0f * 1.0f / jerk;   /*  第1步的时间:ti^3 = 6 * 1 / jerk */
    ti = pow(ti_cube,(1 / 3.0f));     /* ti */
    sum_t += ti;
    delta_v = 0.5f * jerk * pow(sum_t,2);  /* 第一步的速度 */
    velocity_tab[0] = g_calc_t.vo + delta_v;
 
/*****************************************************/
    if( velocity_tab[0] <= SPEED_MIN )  /* 以当前定时器频率所能达到的最低速度 */
        velocity_tab[0] = SPEED_MIN;
/*****************************************************/
    
    for(i = 1; i < accel_step; i++)
    {
        /* 步进电机的速度就是定时器脉冲输出频率,可以计算出每一步的时间 */
        /* 得到第i-1步的时间 */
        ti = 1.0f / velocity_tab[i-1];  /*  电机每走一步的时间 ti = 1 / Vn-1*/
        /* 加加速段速度计算 */
        if( i < inc_acc_stp)
        {
            sum_t += ti;       /* 从0开始到i的时间累积 */
            delta_v = 0.5f * jerk * pow(sum_t,2);  /* V = 1/2 * jerk * ti^2 */
/* 加加速段每一步对应的速度 */
            velocity_tab[i] = g_calc_t.vo + delta_v;
        /* 当最后一步的时候,时间并不严格等于time,所以这里要稍作处理,作为减加速段的时间 */
            if(i == inc_acc_stp - 1) sum_t  = fabs(sum_t - time );
        }
        /* 减加速段速度计算 */
        else
        {
            sum_t += ti;                          /*  时间累计 */
    /*  dV = 1/2 * jerk *(T-t)^2 */
            delta_v = 0.5f * jerk * pow(fabs( time - sum_t),2); 
            velocity_tab[i] = g_calc_t.vt - delta_v;  /*  V = vt - delta_v */
            if(velocity_tab[i] >= g_calc_t.vt)
            {
                accel_step = i;
                break;
            }
        }
    }
    if(is_dec == TRUE)                /* 减速 */
    {
        float tmp_Speed = 0;
        g_calc_t.decel_tab = velocity_tab;
        g_calc_t.decel_step = accel_step;
        /* 倒序排序 */
        for(i = 0; i< (accel_step / 2); i++)
        {
            tmp_Speed = velocity_tab[i];
            velocity_tab[i] = velocity_tab[accel_step-1 - i];
            velocity_tab[accel_step-1 - i] = tmp_Speed;
        }
    }
    else                              /* 加速 */
    {
        g_calc_t.accel_tab = velocity_tab;
        g_calc_t.accel_step = accel_step;
    }
    return TRUE;
}

       初始化时需先生成S曲线加减速段的速度表,根据前面思路的推导将每一个速度段的速度计算出来后进行存储,读取速度表部分则放置在定时器中断服务函数里面即可,接着看下S形加减速控制代码:

/**
 * @brief       S型加减速运动
 * @param       vo:初速度;vt:末速度;AcTime:加速时间;DeTime:减速时间;step:步数;
 * @retval      无
 */
void stepmotor_move_rel(int32_t vo, int32_t vt, float AcTime,float DeTime,int32_t step)
{
    if(calc_speed(vo,vt,AcTime)==FALSE) /*  计算出加速段的速度 */
        return;
    if(calc_speed(vt,vo,DeTime)==FALSE) /*  计算出减速段的速度 */
        return;
 
    if(step < 0)
    {
        step = -step;
        ST1_DIR(CCW);
    }
    else
    {
        ST1_DIR(CW);
    }
    
    if(step >= (g_calc_t.decel_step+g_calc_t.accel_step) )
    {
        g_calc_t.step = step;
        g_calc_t.dec_point = g_calc_t.step - g_calc_t.decel_step;
    }
    else              /* 步数不足以进行足够的加减速 */
    {
        printf("步数不足,参数设置错误!\r\n");
        return;
    }
    g_calc_t.step_pos = 0;
    g_motor_sta = STATE_ACCEL;   /*  电机为运动状态 */
 
    g_calc_t.ptr = g_calc_t.accel_tab; /* 把加速段的速度表存储到ptr里边 */
    Toggle_Pulse = (uint32_t)(T1_FREQ/(*g_calc_t.ptr));
    g_calc_t.ptr++;
    __HAL_TIM_SET_COUNTER(&g_atimx_handle,0);
    __HAL_TIM_SET_COMPARE(&g_atimx_handle,TIM_CHANNEL_1,
(uint16_t)(Toggle_Pulse));  /* 设置定时器比较值 */
    HAL_TIM_OC_Start_IT(&g_atimx_handle,ATIM_TIMX_PWM_CH1); /* 使能定时器通道 */
    ST3_EN(EN_ON);
}

       根据传入的参数判断是加速段还是减速段;如果初始速度小于末速度那么就是加速段,如果初始速度大于末速度那么就是减速段,然后此函数会调用到前面的速度表函数,将用户设定值计算出相应速度参数,并判断符合哪种运动状态,之后判断设定的步数参数是否合理,如果步数不足以实现S形加减速运动则直接返回并打印提示信息。当步数合理时,开启定时器工作,比较值设置为速度表第一个参数,即第一步,然后在定时器调用到速度表参数。接着看下中断处理函数:

/**
  * @brief  定时器比较中断
  * @param  htim:定时器句柄指针
  * @note   无
  * @retval 无
*/
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    volatile uint32_t Tim_Count = 0;
    volatile uint32_t tmp = 0;
    volatile float Tim_Pulse = 0;
    volatile static uint8_t i = 0;
    if(htim->Instance==TIM8)
    {
        i++;                     /*  定时器中断次数计数值 */
        if(i == 2)               /*  2次,说明已经输出一个完整脉冲 */
        {
            i = 0;               /*  清零定时器中断次数计数值 */
            g_step_pos ++;     /* 当前位置 */
            if((g_motor_sta!=STATE_IDLE)&&(g_motor_sta != STATE_STOP))
            {
                g_calc_t.step_pos ++;
            }
            switch(g_motor_sta)
            {
                case STATE_ACCEL:
                    g_add_pulse_count++;
                    /*  由速度表得到每一步的定时器计数值 */ 
    Tim_Pulse = T1_FREQ / (*g_calc_t.ptr);      
                    g_calc_t.ptr++;         /* 取速度表的下一位 */
/* 翻转模式C除以2 */
                    Toggle_Pulse = (uint16_t) (Tim_Pulse / 2);
    /* 大于加速段步数就进入匀速 */
                    if(g_calc_t.step_pos >= g_calc_t.accel_step) 
                    {
                        myfree(SRAMIN,g_calc_t.accel_tab);/* 运动完要释放内存 */
                        g_motor_sta = STATE_AVESPEED;
                    }
                    break;
                case STATE_DECEL:
                    g_add_pulse_count++;
    /* 由速度表得到每一步的定时器计数值 */
                    Tim_Pulse = T1_FREQ / (*g_calc_t.ptr);    
                    g_calc_t.ptr++;
                    Toggle_Pulse = (uint16_t) (Tim_Pulse / 2);
                    if(g_calc_t.step_pos >= g_calc_t.step )
                    {
                        myfree(SRAMIN,g_calc_t.decel_tab); /* 运动完要释放内存 */
                        g_motor_sta = STATE_STOP;
                    }
                    break;
                case STATE_AVESPEED:
                    g_add_pulse_count++;
                    Tim_Pulse  = T1_FREQ /g_calc_t.vt;
                    Toggle_Pulse = (uint16_t) (Tim_Pulse / 2);
                    if(g_calc_t.step_pos >= g_calc_t.dec_point )
                    {
     /* 将减速段的速度表赋值给ptr */
                        g_calc_t.ptr = g_calc_t.decel_tab;      
                        g_motor_sta = STATE_DECEL;
                    }
                    break;
                case STATE_STOP:
                    HAL_TIM_OC_Stop_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     
                    g_motor_sta = STATE_IDLE;
                    break;
                case STATE_IDLE:
                    break;
            }
        }
        /*  设置比较值 */
        Tim_Count=__HAL_TIM_GET_COUNTER(&g_atimx_handle);
        tmp = 0xFFFF & (Tim_Count + Toggle_Pulse);
        __HAL_TIM_SET_COMPARE(&g_atimx_handle,TIM_CHANNEL_1,tmp);
    }
}

       同梯形定时器输出设置相同,将定时器(TIM8)输出方式设置为输出比较模式,在中断服务函数里使用状态机进行不同速度段的切换,然后根据速度表查找每一步对应的速度值使用 公式即可计算出每一步对应的定时器计数值,该值就是每一步的脉冲周期。需要注意的是,因为使用的是查表方式,所以会消耗一定的存储空间,所以在加减速完成后要释放内存。

       在main函数里面编写如下代码:

/* 加速度和减速度选取一般根据实际需要,值越大速度变化越快,加减速阶段比较抖动
 * 所以加速度和减速度值一般是在实际应用中多尝试出来的结果
 */
#define V_END           300                     /* 末速度 */
#define V_START         0                       /* 初速度 */
#define ACCELTIME       3.5f                    /* 加速时间 (s) */
#define DECEELTIME      1.5f                    /* 减速时间 (s) */
 
__IO uint16_t g_step_angle = 15;                /* 设置的步进步数*/
extern __IO  uint32_t g_add_pulse_count;        /* 脉冲个数累计*/
extern motor_state_typedef g_motor_sta;
 
int main(void)
{
    uint8_t key, t;
    char buf[32];
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(85, 2, 2, 4, 8);       /* 设置时钟,170Mhz */
    delay_init(170);                            /* 延时初始化 */
    usart_init(115200);                         /* 初始化串口 */
    led_init();                                 /* 初始化LED */
    key_init();                                 /* 初始化按键 */
    lcd_init();                                 /* 初始化LCD */
    stepper_init(0xFFFF - 1, 170 - 1);          /* 1Mhz的计数频率 */
    my_mem_init(SRAMIN);                        /* 初始化内部SRAM内存池 */
    my_mem_init(SRAMCCM);                       /* 初始化内部SRAMCCM内存池 */
    
    /* LCD显示提示信息 */
    lcd_show_string(10, 10, 200, 32, 32, "Stepper Motor Test", RED);   
    lcd_show_string(10, 45, 200, 24, 24, "KEY0:Run Once", RED);
    lcd_show_string(10, 75, 200, 24, 24, "KEY1:STEP++", RED);
    lcd_show_string(10, 105, 200, 24, 24, "KEY2:STEP--", RED);
    lcd_show_string(10, 135, 200, 24, 24, "Stepper ID: 1", RED);
    printf("KEY0开启S形加减速\r\n");
    printf("KEY1增加步数\r\n");
    printf("KEY2减少步数\r\n");
    
    while (1)
    {     
        t++;
        if(t % 20 == 0)
        {            
            sprintf(buf,"Set_Aangle:%d ",g_step_angle);/*设置的旋转位置(角度)*/
            lcd_show_string(10, 165, 200, 24, 24, buf, BLUE);
            /*累计旋转的角度*/
            sprintf(buf,"Add_Aangle:%.2f ",g_add_pulse_count*0.225);    
            lcd_show_string(10, 195, 239, 24, 24, buf, BLUE);
            LED0_TOGGLE();                         /*LED0(红灯) 翻转*/                                
        }
        delay_ms(10);        
        key = key_scan(0);
        if (key == KEY0_PRES)           /* 开启电机S型加减速 */
        {
            if (g_motor_sta == STATE_IDLE)
            {
                g_add_pulse_count = 0;
                stepmotor_move_rel(V_START, V_END, ACCELTIME, DECEELTIME, 
g_step_angle * SPR );     /* 一次加减速运动 */
            }
        }
        else if (key == KEY1_PRES)               /* 步数加 */
        {
            g_step_angle = g_step_angle + 1;
            if(g_step_angle >= 50)  g_step_angle = 1;
        }
        else if (key == KEY2_PRES)                  /* 步数减 */
        {
            g_step_angle = g_step_angle - 1;
            if(g_step_angle <= 1)  g_step_angle = 50;
        }
 
    }
}

       首先第一部分是关于S形加减速的一些初始参数设置,如初速度、末速度、加速时间、减速时间等等,这些参数的设置是会影响程序的正常执行的,比如加速时长过久,会导致需要更多的内存来存储每一步的速度数据,当内存不足时,串口会打印提示信息。然后主函数的内容比较简单,通过按键判断逻辑实现,当按下KEY0控制开启步进电机S形加减速运动,KEY1增加总步数,KEY2减少总步数。LED0周期闪烁提示程序正在运行。


       17.3.3 下载验证

       下载代码后,可以看到LED0每隔200ms左右闪烁一次,LCD和串口都会显示提示信息, 根据屏幕提示,点击按键0即可使步进电机做S形加减速运动,如果现象不明显,请通过按键KEY1增加总步数,当步数足够多,可以明显感觉到步进电机由缓慢加速,再到最大速度,最后缓慢减速直到停止。


图17.3.3.1 串口打印提示信息


       注意:驱动器设置8细分,使用到的是步进电机接口1。实验现象大家可自行测试看下效果。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3116513/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    763797