ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《DMG474电机控制专题教程V1.1 》第19章 步进电机圆弧插补

《DMG474电机控制专题教程V1.1 》第19章 步进电机圆弧插补

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

第19章 步进电机圆弧插补


       上一章我们学习了直线插补,但直线插补针对的是走直线或者斜线轮廓形状轨迹,而在实际的数控机床上,不单单只有走斜线或直线,如果需要走的目标轮廓是弧线呢?很显然,直线插补满足不了,所以这时候就需要用到今天所学习的内容——圆弧插补。

       本章分为如下几个小节:

       19.1 圆弧插补的简介

       19.2 逐点比较法圆弧插补原理

       19.3 任意象限圆弧插补原理

       19.4 任意象限圆弧插补实验


        19.1 圆弧插补的简介

       在圆弧起点与终点间,计算逼近实际圆弧的点群,控制刀具沿点运动,加工出圆弧曲线。它的思想与直线插补的类似,并且我们都是使用的逐点比较法来实现,所以它们的插补步骤也一样都分为:偏差判别、坐标进给、偏差计算、终点判别。我们这里为了降低难度,先从第一象限逆圆弧一步一步来进行分析。


        19.2 逐点比较法圆弧插补原理


       19.2.1 偏差判别

       设要加工图形为第一象限逆时针走向的圆弧AE,以原点为圆心,半径为R,动点P(Xi,Yi),动点P可能在圆弧外也可能在圆弧内,还有可能在圆弧上,所以就有三种可能,下面我们逐一来分析下:


图19.2.1.1 第一象限逆圆弧插补示意图


       l 当P点在圆弧上时,那么由勾股定理可得:

       Xi²+Yi² = R²

       即偏差判别式:F = Xi²+Yi²-R² = 0

       l 当P点在圆弧外的话,就有以下关系:

       Xi²+Yi² > R²

       偏差判别式: F = Xi²+Yi²-R² > 0

       l 当P点在圆弧内的话,就有以下关系:

       Xi²+Yi² < R²

       偏差判别式:F = Xi²+Yi²-R² < 0

       综上所述,可知F的大小反映了动点P与圆弧AE的相对位置

       当F = 0,表示动点P在圆弧AE上;

       当F > 0,表示动点P在圆弧AE外部;

       当F < 0,表示动点P在圆弧AE内部。


       19.2.2 坐标进给

       当我们知道动点与圆弧的相对位置后,接着就是坐标进给,需往误差减小的方向进给。 当F = 0时,在圆弧上,此时不管是往x轴步进还是往y轴步进它们之间的误差是差不多的,所以这里我们为了方便计算统一将其运动方向与F>0一样,也就是-X的方向步进。 但是这里要注意F=0表示动点P是在圆弧上的任意一点,所以也可能在x轴上,此时注意需要往+Y方向步进才可减少误差,其他的位置均与F>0的步进方向一致即可。

       当F > 0时,在圆弧外部,应该向圆内-X方向进给一步;

       当F < 0时,在圆弧内部,应该向圆外+Y方向进给一步;

       按照上述方法步进,就可以走出类似目标圆弧轮廓出来。整个过程如下图所示:


图19.2.2.1 第一象限逆圆弧插补的过程图


       图中所走的一步看着是比较夸张的,实际步进电机走的一步单位向量是非常小的,比头发丝的直径都还要小,所以实际运动中看着是非常顺滑的,并不会像图中这样棱角分明。


       19.2.3 偏差计算

       坐标进给之后得到新的动点坐标,接着就是计算新的偏差值,这里我们会对偏差判别式进行化简,由已知偏差判别式为: F = Xi²+Yi²-R² ,假设:

       l 当动点P(Xi,Yi)在圆弧上或圆弧外,此时F ≥ 0,需向X轴负方向进给一步(-X),

       此时新加工点P的位置:X = Xi-1,Y = Yi,此时加工偏差为:

       Fi+1 = (Xi-1)²+Yi²-R² à Fi+1 = Xi²+Yi²-R² -2Xi+1à Fi+1 = F-2Xi+1

       l 当动点P在圆弧内侧的话,此时F < 0,需向y轴正向进给一步(+Y),

       此时新加工点P的位置:X = Xi,Y = Yi+1,此时加工偏差为:

       Fi+1 = Xi²+(Yi+1)²-R² à Fi+1 = Xi²+Yi²-R² +2Yi+1 à Fi+1 = F-2Yi+1

       经过上述化简后的判别式比原式更简单,运算速度也更快;从式中可以看出,偏差 Fi+1 的值只跟上一步进给的偏差 Fi 和上一步的动点坐标值 Pi有关。注意:直线插补的偏差计算是不需要动点坐标的,这是圆弧插补与直线插补的区别。 


       19.2.4 终点判别

       最后就是终点判别,插补到何时停止呢?常用的终点判别有三种,分别是:总步长法、终点坐标发。投影法,与直线插补章节介绍一致,这里就不再赘述。

       我们这里使用的是总步长法。所以在插补处理开始之前,先设置一个总步长计数器n :

即:      n =|Xe - Xa|+ |Ye - Ya|

       其中,圆弧终点坐标E(Xe,Ye),起点坐标A(Xa,Ya),每当步进一步计数器n就减一,直至n = 0时,插补结束。


        19.3 任意象限圆弧插补原理

       前面讲解的都是以第一象限逆圆弧为例,推导的偏差计算公式也只适合第一象限逆圆弧,实际应用中还需要考虑运动方向在4个象限内的顺逆圆弧情况,这个就比较复杂,所以接着我们来讲解下任意象限顺逆圆弧的情况。


图19.3.1 任意象限顺逆圆弧的情况


       根据前面的逆圆弧差插补思路,我们可以推导第一象限顺圆弧的偏差判别,坐标进给以及偏差计算公式,如下表:


表19.3.1 第一象限顺圆弧推导


       由表19.3.1可知,第一象限的顺圆弧进给方向与第一象限逆圆弧的相反,两个方向的偏差计算公式的形式是不变的,只是符号和对应的X,Y轴发生变化。

       其他的象限推导方式也是很类似的,我可以依据上面的推导方法,推导出所有象限的顺逆圆弧的情况,我们将顺圆弧的1、2、3、4象限定义为:SR1、SR2、SR3、SR4,将逆圆弧的第1、2、3、4象限定义为:NR1、NR2、NR3、NR4,一共就有八种情况,我们这里直接进行归纳,如下表:


表19.3.2 任意象限顺逆圆弧情况汇总表


       可以将表中数据主要分为两组:第一组(SR1、NR2、SR3、NR4)和第二组(NR1、SR2、NR3、SR4),各组之间有共同点如下:

       第一组的共同点:

       当 Fi ≥ 0 时, Y 轴进给, SR1、NR2 走-Y 方向, SR3、NR4 走 +Y 方向;

       当 Fi < 0 时, X 轴进给, SR1、NR4 走 +X 方向, NR2、SR3 走-X 方向。

       第二组的共同点:

       当 Fi ≥ 0 时, X 轴进给, NR1、SR4 走-X 方向, SR2、NR3 走 +X 方向;

       当 Fi < 0 时, Y 轴进给, NR1、SR2 走 +Y 方向, NR3、SR4 走-Y 方向。

       偏差的计算公式格式是不变的,只是符号的正负以及X,Y的区别,所以在代码实现部分需要去判断当前所要走的圆弧属于第几象限以及顺逆方向,在根据表19.3.2代入各自所对应的偏差判别式进行偏差计算即可。任意象限的原理部分就讲解完了,大家可以自行去推导看看!


        19.4 任意象限圆弧插补实验


       19.4.1 硬件设计


       1、例程功能

       本实验使用步进电机接口1控制X轴步进电机和接口2控制Y轴步进电机,当按下KEY0控制步进电机开始圆弧插补运动。注意驱动器设置:1.0A,8细分,也就是旋转一圈所需要1600个脉冲数; LED0作为程序运行状态指示灯,闪烁周期200ms。


       2、硬件资源

       硬件连接部分与直线插补实验完全相同,这里就不再赘述,如不清楚可查看直线插补实验章节。


       19.4.2 程序设计


       19.4.2.1 任意象限圆弧插补的配置步骤 

       1)初始化定时器

       初始化定时器通道IO,设置ARR、PSC,计数方式以及脉冲输出模式等。

       2)设置圆弧方向

       需提前设置好是顺圆弧还是逆圆弧。

       3)电机旋转方向判定

       实现任意象限圆弧插补,需提前判断所属象限,设置电机旋转方向。

       4)编写圆弧插补算法

       实现插补第一步,偏差判别,坐标进给,偏差计算并开启脉冲输出与中断使能。

       5)编写中断服务函数

       计算动点新坐标,实现除第一步以外的其他步数的偏差判别、坐标进给、偏差计算、终点判别。


       19.4.2.2 程序流程图


图19.4.2.2.1 圆弧插补流程图


       19.4.2.3 程序解析

       本实验在步进电机直线插补实验的基础上添加代码,这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。与直线插补相同部分不再赘述,主要讲解圆弧插补相关部分,首先看stepper_motor.h中添加了哪部分:

#define AXIS_X               0   /* X轴标号 */
#define AXIS_Y               1   /* Y轴标号 */
 
#define EN                   0   /* 失能脱机信号 */
#define OFF                  1   /* 使能脱机信号 */
 
#define FIRST_QUADRANT       1   /* 第一象限 */
#define SECOND_QUADRANT      2   /* 第二象限 */
#define THIRD_QUADRANT       3   /* 第三象限 */
#define FOURTH_QUADRANT      4   /* 第四象限 */
 
#define ARC                  1   /* 圆弧插补 */

       主要就是添加了第1、2、3、4象限的宏定义,用于判断当前圆弧属于哪个象限。接着看下stepper_motor.c中的方向设置函数:

/**
 * @brief       设置圆弧插补方向
 * @param       coordsX,coordsY:x,y坐标,dir: 圆弧的插补方向
 * @retval      无
 */
void setarcdir(int32_t coords_x,int32_t coords_y,int8_t dir)
 {
    g_pol_par.inter_dir = dir;
    if(g_pol_par.inter_dir == CCW)
    {
        if(coords_x > 0)      /*x > 0 起点在X轴的正半轴,顺时针Y轴进给方向是负的 */
        {
            if(coords_y>=0)  /*  如果y > 0,则是第一象限 */
            {
                g_pol_par.qua_points = FIRST_QUADRANT;
                g_pol_par.x_dir = CCW;
                g_pol_par.y_dir = CW;
                sign_dir[AXIS_X] = -1;
                sign_dir[AXIS_Y] = 1;
                ST_LINE_DIR(CCW,AXIS_X);
                ST_LINE_DIR(CW,AXIS_Y);
            }
            else
            {
                g_pol_par.qua_points = FOURTH_QUADRANT;
                g_pol_par.x_dir = CW;      /*  y <= 0, 在第四象限 */
                g_pol_par.y_dir = CW;
                sign_dir[AXIS_X] = 1;
                sign_dir[AXIS_Y] = 1;
                ST_LINE_DIR(CW,AXIS_X);
                ST_LINE_DIR(CW,AXIS_Y);
            }
        }
        else if(coords_x < 0)      /*  X轴的负半轴 */
        {
            if(coords_y <= 0)      /*  y < 0,则是第三象限 */
            {
                g_pol_par.qua_points = THIRD_QUADRANT;
                g_pol_par.x_dir = CW;
                g_pol_par.y_dir = CCW;
                sign_dir[AXIS_X] = 1;
                sign_dir[AXIS_Y] = -1;
                ST_LINE_DIR(CW,AXIS_X);
                ST_LINE_DIR(CCW,AXIS_Y);
            }
            else          /*  y >= 0,第二象限 */
            {
                g_pol_par.qua_points = SECOND_QUADRANT;
                g_pol_par.x_dir = CCW;
                g_pol_par.y_dir = CCW;
                sign_dir[AXIS_X] = -1;
                sign_dir[AXIS_Y] = -1;
                ST_LINE_DIR(CCW,AXIS_X);
                ST_LINE_DIR(CCW,AXIS_Y);
            }
        }
        else if(coords_x == 0)   /* x = 0,在Y轴上的特殊起点 */
        {
            if(coords_y>0)
            {
                g_pol_par.qua_points = SECOND_QUADRANT;
                g_pol_par.x_dir = CCW;
                g_pol_par.y_dir = CCW;
                sign_dir[AXIS_X] = -1;
                sign_dir[AXIS_Y] = -1;
                ST_LINE_DIR(CCW,AXIS_X);
                ST_LINE_DIR(CCW,AXIS_Y);
            }
            else
            {
                g_pol_par.qua_points = FOURTH_QUADRANT;
                g_pol_par.x_dir = CW;
                g_pol_par.y_dir = CW;
                sign_dir[AXIS_X] = 1;
                sign_dir[AXIS_Y] = 1;
                ST_LINE_DIR(CW,AXIS_X);
                ST_LINE_DIR(CW,AXIS_Y);
            }
        }
    }
    else /* CW方向 */
    {
        if(coords_x > 0)    /* 坐标x>0起点在X轴的正半轴,顺时针Y轴进给方向是负的 */
        {
            if(coords_y >0) /*  如果y > 0,则是第一象限 */
            {
                g_pol_par.qua_points = FIRST_QUADRANT;
                g_pol_par.x_dir = CW;
                g_pol_par.y_dir = CCW;
                sign_dir[AXIS_X] = 1;
                sign_dir[AXIS_Y] = -1;
                ST_LINE_DIR(CW,AXIS_X);
                ST_LINE_DIR(CCW,AXIS_Y);
            }
            else
            {
                g_pol_par.qua_points = FOURTH_QUADRANT;
                g_pol_par.x_dir = CCW;     /*  y <= 0, 在第四象限 */
                g_pol_par.y_dir = CCW;
                sign_dir[AXIS_X] = -1;
                sign_dir[AXIS_Y] = -1;
                ST_LINE_DIR(CCW,AXIS_X);
                ST_LINE_DIR(CCW,AXIS_Y);
            }
        }
        else if(coords_x < 0)      /*  X轴的负半轴 */
        {
            if(coords_y < 0)       /*  y < 0,则是第三象限 */
            {
                g_pol_par.qua_points = THIRD_QUADRANT;
                g_pol_par.x_dir = CCW;
                g_pol_par.y_dir = CW;
                sign_dir[AXIS_X] = -1;
                sign_dir[AXIS_Y] = 1;
                ST_LINE_DIR(CCW,AXIS_X);
                ST_LINE_DIR(CW,AXIS_Y);
            }
            else          /*  y >= 0,第二象限 */
            {
                g_pol_par.qua_points = SECOND_QUADRANT;
                g_pol_par.x_dir = CW;
                g_pol_par.y_dir = CW;
                sign_dir[AXIS_X] = 1;
                sign_dir[AXIS_Y] = 1;
                ST_LINE_DIR(CW,AXIS_X);
                ST_LINE_DIR(CW,AXIS_Y);
            }
        }
        else if(coords_x == 0)     /* x = 0,在Y轴上的特殊起点 */
        {
            if(coords_y>0)
            {
                g_pol_par.qua_points = FIRST_QUADRANT;
                g_pol_par.x_dir = CW;
                g_pol_par.y_dir = CCW;
                sign_dir[AXIS_X] = 1;
                sign_dir[AXIS_Y] = -1;
                ST_LINE_DIR(CW,AXIS_X);
                ST_LINE_DIR(CCW,AXIS_Y);
            }
            else
            {
                g_pol_par.qua_points = THIRD_QUADRANT;
                g_pol_par.x_dir = CCW;
                g_pol_par.y_dir = CW;
                sign_dir[AXIS_X] = -1;
                sign_dir[AXIS_Y] = 1;
                ST_LINE_DIR(CCW,AXIS_X);
                ST_LINE_DIR(CW,AXIS_Y);
            }
        }
    }
}

       该函数有三个入口参数由用户设置,其中coords_x和coords_y为圆弧的起点坐标,通过该坐标判断当前圆弧属于哪一象限,dir设置的是圆弧的顺逆方向,这样就可确定该圆弧属于哪个象限以及它的顺逆方向情况了,之后设置XY轴的步进电机方向,并且设置对应的偏差计算公式的符号位。所以该函数非常关键!接着看实现任意象限画圆弧函数:

/**
 * @brief       在XOY平面内画任意圆弧
 * @param       start_x_point,start_y_point:  分别是起点坐标X,Y
 * @param       stop_x_point,stop_y_point:  分别是终点坐标X,Y
 * @param       speed:  速度值
 * @param       dir:    圆弧的方向
 * @retval      无
 */
void arc_incmove(int32_t start_x_point,int32_t start_y_point,
int32_t stop_x_point,int32_t stop_y_point,uint32_t speed,int8_t dir)
{
    if(g_motor_sta == STATE_RUN)            /*  当前电机正在运动 */
        return ;
 
    /* 不符合圆的坐标方程 */
if( (pow(start_x_point,2)+pow(start_y_point,2)) != 
(pow(stop_x_point,2)+pow(stop_y_point,2))) /* 需要满足半径一致才是一个圆 */
        return ;
    g_pol_par.moving_mode = ARC;       /* 圆弧标志 */
    
    g_pol_par.f_e = 0;                       /* 偏差方程置零 */
    g_pol_par.start_point[AXIS_X] = start_x_point;
    g_pol_par.start_point[AXIS_Y] = start_y_point;
    g_pol_par.end_x = stop_x_point;
    g_pol_par.end_y = stop_y_point;
    /* 设置电机做逆时针圆弧轨迹的运动方向 */
    setarcdir(g_pol_par.start_point[AXIS_X],g_pol_par.start_point[AXIS_Y],dir);
    /* 计算总的步数 */  
g_pol_par.end_pulse = abs((stop_y_point-start_y_point))+ abs((stop_x_point-
start_x_point));  /*  从起点到终点的脉冲数 */
    /* 起点坐标x = 0,说明起点位于y轴上,此时往X轴进给误差会减少 */
    if(g_pol_par.start_point[AXIS_X] == 0)
    {
        g_pol_par.act_axis = AXIS_X;                      /* 第一步给X轴 */
        /* 根据圆弧方向决定向X轴进给的偏差方程 */
        g_pol_par.f_e = g_pol_par.f_e + 
   sign_dir[AXIS_X]*g_pol_par.start_point[AXIS_X]*2 + 1;/* 偏差方程的计算*/
    }
    else
    {
        g_pol_par.act_axis = AXIS_Y;                         /* 第一步给Y轴 */
        g_pol_par.f_e = g_pol_par.f_e + 
sign_dir[AXIS_Y]*g_pol_par.start_point[AXIS_Y]*2 + 1;/* 偏差方程的计算 */
    }
    
    ST_LINE_EN(EN,AXIS_X);
    ST_LINE_EN(EN,AXIS_Y);
    __HAL_TIM_SET_COMPARE(&g_atimx_handle,st_motor[AXIS_X].pulse_channel,
speed);      /* 设置通道的比较值 */
    __HAL_TIM_SET_COMPARE(&g_atimx_handle,st_motor[AXIS_Y].pulse_channel,
speed);
    __HAL_TIM_SET_AUTORELOAD(&g_atimx_handle,speed*2);
TIM_CCxChannelCmd(ATIM_TIMX_PWM, 
st_motor[g_pol_par.act_axis].pulse_channel,TIM_CCx_ENABLE);
    HAL_TIM_Base_Start_IT(&g_atimx_handle);     /* 使能定时器以及开启更新中断 */
    g_motor_sta = STATE_RUN;                     /*  标记电机正在运动 */
}

       该函数实现计算总步长和判断入口参数起点与终点是否构成同一个圆的圆弧,为同一个圆才可继续否则直接退出;然后开始第一步的偏差判别,坐标进给,并会调用SetArcDir函数确定符号正负,进行偏差计算,最后就是开启定时器脉冲输出以及中断使能。然后其余步数的计算在中断回调函数里边实现,接着看下中断回调函数的逻辑:

/**
 * @brief       定时器中断回调函数
 * @param       htim : 定时器句柄
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    __IO uint32_t axis = 0;      /*  进给轴 */
    axis = g_pol_par.act_axis;     /*  当前进给轴 */
 
    /* 判断是否到达终点或者还没开始运动 */
    if(g_pol_par.end_pulse == 0)
        return;
    /* 计算新的动点坐标 */
    if(g_pol_par.act_axis == AXIS_X)
    {
        if(g_pol_par.x_dir == CCW)
        {
            g_pol_par.start_point[AXIS_X]--;
        }
        else
        {
            g_pol_par.start_point[AXIS_X]++;
        }
    }
    if(g_pol_par.act_axis == AXIS_Y)
    {
        if(g_pol_par.y_dir == CCW)
        {
            g_pol_par.start_point[AXIS_Y]--;
        }
        else
        {
            g_pol_par.start_point[AXIS_Y]++;
        }
    }    
    
    /* 根据进给方向 更新坐标值 */
    if(g_pol_par.moving_mode == ARC)
    {
        /* 根据上一次的偏差判断下一步进给方向,同时计算下一次的偏差 */
        if(g_pol_par.inter_dir == CCW)      /* 插补方向:逆时针圆弧 */
        {
            if(g_pol_par.f_e < 0)/* 偏差方程 < 0 ,当前位置位于圆弧内侧,应向圆外进给*/
            {
   /* 第二和第四象限,当偏差<0时都是向X轴进给 */
                if( (g_pol_par.qua_points == SECOND_QUADRANT) || 
(g_pol_par.qua_points == FOURTH_QUADRANT) )
                {
                    g_pol_par.act_axis = AXIS_X;
                }
                else        /*  在第一和第三象限,偏差<0,都是向Y轴进给 */
                {
                    g_pol_par.act_axis = AXIS_Y;
                }
            }
   /* 偏差方程 >= 0 ,当前位置位于圆弧外侧,应向圆内进给 */
            else if(g_pol_par.f_e >= 0) 
            {
                if( (g_pol_par.qua_points == SECOND_QUADRANT) || 
(g_pol_par.qua_points == FOURTH_QUADRANT) )
                {
                    g_pol_par.act_axis = AXIS_Y;
                }
                else
                {
                    g_pol_par.act_axis = AXIS_X;
                }
            }
        }
        else
        {
            if(g_pol_par.f_e < 0)/* 偏差方程 < 0 ,当前位置位于圆弧内侧,应向圆外进给*/
            {
   /* 第一和第三象限,当偏差<0时都是向X轴进给 */
                if( (g_pol_par.qua_points == FIRST_QUADRANT) || 
(g_pol_par.qua_points == THIRD_QUADRANT) )
                {
                    g_pol_par.act_axis = AXIS_X;
                }
                else        /* 在第二和第四象限,偏差<0,都是向Y轴进给 */
                {
                    g_pol_par.act_axis = AXIS_Y;
                }
            }
   /* 偏差方程 >= 0 ,说明当前位置位于圆弧外侧,应向圆内进给 */
            else if(g_pol_par.f_e >= 0)       
            {
                if( (g_pol_par.qua_points == FIRST_QUADRANT) || 
(g_pol_par.qua_points == THIRD_QUADRANT) )
                {
                    g_pol_par.act_axis = AXIS_Y;
                }
                else
                {
                    g_pol_par.act_axis = AXIS_X;
                }
            }
        }
        /* 计算当前坐标与目标曲线的偏差 */
        g_pol_par.f_e = g_pol_par.f_e + 2*sign_dir[g_pol_par.act_axis]*
g_pol_par.start_point[g_pol_par.act_axis] + 1;
    }   
    /* 判断是否需要跟换进给轴 */
    if(axis != g_pol_par.act_axis)
    {
        TIM_CCxChannelCmd(ATIM_TIMX_PWM, st_motor[axis].pulse_channel, 
 TIM_CCx_DISABLE);
        TIM_CCxChannelCmd(ATIM_TIMX_PWM, 
st_motor[g_pol_par.act_axis].pulse_channel, TIM_CCx_ENABLE);
    }
    /* 终点判别:总步长 */
    g_pol_par.end_pulse--;
    if(g_pol_par.end_pulse == 0)
    {
        g_motor_sta = STATE_STOP;            /*  到达终点 */
    /* 关闭当前轴输出 */
        TIM_CCxChannelCmd(ATIM_TIMX_PWM, 
st_motor[g_pol_par.act_axis].pulse_channel, TIM_CCx_DISABLE);
        HAL_TIM_Base_Stop_IT(&g_atimx_handle);    /*  停止定时器 */
    }
}

       中断回调函数里边首先记录上一步的进给活动轴,接着计算新的动点坐标用于下一步的偏差计算,接着进行除了第一步以外的其他步数的偏差判别、坐标进给、偏差计算,比较当前活动轴与上一步活动轴是否一致,若不一致则更换输出通道,然后总步数减一,最后判断总步长是否为0,是的话则插补停止,否则重复以上步骤。

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

int main(void)
{
    uint8_t key,t;
    
    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 */
/* 18.9Mhz的计数频率,太快容易堵转可适当调整 */
    stepper_init(0xFFFF - 1, 9 - 1);            
   
    /* LCD显示提示信息 */
    lcd_show_string(10, 10, 200, 32, 32, "Stepper Test", RED);  
    lcd_show_string(10, 45, 200, 24, 24, "KEY0:Run Once", RED);
    lcd_show_string(10, 75, 200, 24, 24, "Stepper ID:1--X", BLUE);
    lcd_show_string(10, 105, 200, 24, 24, "Stepper ID:2--Y", BLUE);
    printf("圆弧插补实验,按KEY0开始插补\r\n");
    
    while (1)
    {     
        key = key_scan(0);
        if (key == KEY0_PRES) /* 按下KEY0开始插补 */
        {
            arc_incmove(800 * 5, 0, 0, -800 * 5, 2000, CW); /* 第四象限圆弧 */
            while (g_motor_sta);
            arc_incmove(0, -800 * 5, -800 * 5, 0, 2000, CW); /* 第三象限圆弧 */   
            while (g_motor_sta);
            arc_incmove(-800 * 5, 0, 0, 800 * 5, 2000, CW);   /* 第二象限圆弧 */
            while (g_motor_sta);
            arc_incmove(0, 800 * 5, 800 * 5, 0, 2000, CW);   /* 第一象限圆弧 */
            while (g_motor_sta);    
        }
        t++;
        if (t % 20 == 0)
        {
            LED0_TOGGLE();     /* LED0(红灯) 翻转 */        
        }
        delay_ms(10);
    }
}

       mian函数主要就是初始化一些外设包括串口、定时器初、LCD等等,串口和LCD都会显示提示信息,当按下KEY0之后就会分别开启四个象限的圆弧插补运动。最后可以看出走的轨迹是一个顺时针画的圆。大家如果要改变圆弧半径,可以自己调整Arc_IncMove入口参数的起点以及终点坐标,注意坐标点必须满足在同一个圆上。


       19.4.3 下载验证

       同直线插补的硬件连接完全一致,需使用两个丝杆滑台组成一个标准的两轴X-Y平台,硬件连接好之后下载程序到开发板,液晶屏和串口都会显示提示信息,当我们按下按键KEY0,将会开启圆弧插补运动,最后可以看到我们的源码实现了一个顺时针画的圆。


图19.4.3.1 串口提示信息

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

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

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764014