ITPub博客

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

《DMG474电机控制专题教程V1.1 》第18章 步进电机直线插补

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

第18章 步进电机直线插补


       前面我们所学习的单一步进电机只能完成和结构相关的简单直线运动(例如丝杆),也称为“一维运动”,但在实际应用中往往需要工件做斜线甚至圆弧运动,这时就需要通过两个步进电机连轴做同步运动才能实现,这种控制方式称为“多轴联动”。所以本章我们将学习控制两个直线位移的步进电机实现双轴联动,即:步进电机的直线插补运动。

       本章分为如下几个小节:

       18.1 插补的简介

       18.2 插补算法

       18.3 逐点比较法直线插补原理

       18.4 任意象限直线插补原理

       18.5 任意象限直线插补实验


        18.1 插补的简介

       插补:机床数控系统依照一定方法确定刀具运动轨迹的过程。也可以说是,已知曲线上的某些数据,数控装置根据输入的零件程序的信息,将程序段所描述的曲线的起点、终点之间的空间进行数据密化,从而形成要求的轮廓轨迹,这种“数据密化”机能就称为“插补”。

       在数控机床中,刀具不能严格地按照要求加工,做曲线运动,只能用折线轨迹逼近所要加工的曲线。而在已知曲线上的某些数据,按照某种算法计算已知点之间的中间点的方法,也称为“数据点的密化”。

       常见的插补方式有:直线插补、圆弧插补、抛物线插补、样条线插补等。而我们本章所要学习的就是直线插补,我们下面了解下什么是直线插补。

       直线插补(Llne Interpolation):是车床上常用的一种插补方式,通常用于走斜线或直线轨迹,在此方式中,两点间的插补沿着直线的点群来逼近。在实际应用中一个零件的轮廓可以是多样的,有直线、圆弧,也有可能是任意曲线、样条线等,而数控机床的刀具往往是不能以曲线的实际轮廓去走刀的,而是近似地以若干条很小的直线去走刀逼近,走刀的方向一般是x和y方向。


图18.1.1 直线插补图示


        18.2 插补算法

       插补算法的好坏,直接影响系统的控制速度和零件加工精度,那常见的插补算法有哪些?常见的插补算法有:脉冲增量插补法、数字增量插补法这两大类,下面我们来介绍下这两种算法。

       l 脉冲增量插补法:该插补为行程标量插补,常用于开环系统,每次插补结束产生一个行程增量,以脉冲的方式输出,一个脉冲所产生的坐标轴移动量叫做脉冲当量,也可以说,通过向各个运动轴分配驱动脉冲来控制机床坐标轴协调运动,从而加工出一定轮廓形状的算法

       l 数字增量插补法:该插补为时间标量插补,分两步进行。首先计算出插补周期内各坐标轴的增量值,称为粗插补;然后在根据采样得到的实际位置增量计算跟随误差,得到速度指令输出给伺服驱动系统,称为精插补。使用于闭环或者半闭环系统。

       由上述两种插补算法,其中脉冲增量算法非常适用于以步进电机为驱动装置的开环系统。脉冲增量算法也分为多种:逐点比较法、数字积分法、最小偏差法、目标点跟踪法、单步追踪法等,我们这里只介绍我们最为常用的逐点比较法。

       逐点比较法:顾名思义,就是每走一步都要将加工点的瞬时坐标同规定的图形轨迹相比较,判断其偏差,然后决定下一步的走向,如果加工点走到图形外面去了,那么下一步就要向图形里边走,如果加工点在图形里边,那下一步就往图形外面走,以缩小偏差,这样就能得到一个非常接近规定图形的轨迹。     


        18.3 逐点比较法直线插补原理


       18.3.1 逐点比较法整体流程图

       逐点比较法不管是直线插补还是圆弧插补步骤都分为以下四步:

       (1)偏差判别:根据偏差函数值判别加工点的相对直线位置

       (2)坐标进给:沿减小误差的方向进给一步

       (3)偏差计算:进给后,计算新加工点相对直线的位置

       (4)终点判别:判别是否到达终点,未到达则继续第一步步骤,继续插补,到终点则停止

       整体流程图如下:


图18.3.1.1 逐点比较法流程图


       18.3.2 偏差判别

       设有斜线OA,A点坐标为(Xe,Ye),有一动点P(Xi.Yi),他可能在OA线上,也可能在线的上方或下方,所以就有以下几种可能:


图18.3.2.1 第一象限直线插补示意图

 

       l 当P点在斜线上时,由于斜率相等就有:

       Yi / Xi = Ye / Xe

       取偏差判别式:F= Xe*Yi – Xi*Ye = 0

       l 当P点在直线上方,此时OP直线的斜率比OA大,那么有下述关系:

       Yi / Xi > Ye / Xe

       偏差判别式: F = Xe*Yi – Xi*Ye > 0

       l 当P点在直线下方,此时OP直线的斜率比OA小,那么有下述关系:

       Yi / Xi < Ye / Xe

       偏差判别式: F= Xe*Yi – Xi*Ye <0

       综上所述,可知F的大小反映了动点P与斜线OA的相对位置

       当F = 0,表示动点P在直线OA上;

       当F > 0,表示动点P在直线OA上方;

       当F < 0,表示动点P在直线OA下方。


       18.3.3 坐标进给

       当我们通过偏差判别知道动点与直线的相对位置后,接着就是坐标进给,需往误差减小的方向进给:

       当F = 0时,在直线上,需判断终点坐标x、y的值,当x >= y时,动点P往+X方向进给一步,否则往+Y方向进给一步;

       当F > 0时,在直线上方,应该向+X方向进给一步;

       当F < 0时,在直线下方,应该向+Y方向进给一步;

       按照上述方法进行步进,就可以走出逼近目标直线的轮廓出来。


       18.3.4 偏差计算

       接着就是偏差判别式的化简:

       由上面可知,偏差判别式为:F(Xi , Yi) = Xe*Yi – Xi*Ye;

       假设下一步向+X步进,那么此时:

       F(Xi+1 , Yi) = Xe*Yi –(Xi+1)Ye = Xe*Yi - Xi*Ye - Ye = F(Xi,Yi) –Ye;

       假设下一步向+Y步进,那么此时:

       F(Xi , Yi+1) = Xe(Yi+1) – Xi*Ye = Xe*Yi -Xi*Ye +Xe = F(Xi,Yi) +Xe;

       经过上述化简后,判别式简单很多,只需要知道终点坐标及上一步的偏差值,便可求出下一步的偏差值。


       18.3.5 终点判别 

       最后就是终点判别,插补到何时停止呢?常用的终点判别法有三种,分别是:总步长法、终点坐标法、投影法。

       l 总步长法:插补前,将终点位置的x,y坐标绝对值相加求和,得出总步数,开始插补时,无论是往哪个轴给进一步,总步数都减一,直到为0时,代表已到终点,就停止插补。

       l 终点坐标法:插补前,先定义x、y轴两个方向的步长计数器,分别存放两个坐标轴方向上应该走的总步数。开始插补后当 x、y方向每进给一步,就在相应的计数器中减1,直到两个计数器的值都减为 0 时,刀具抵达终点,停止插补。

       l 投影法:在插补处理开始之前,先确定所走步数较大的那根轴,并求出该轴运动的总步数,然后存放在总步长计数器中,当对应的轴有进给时,计数器减 1,直到计数器为 0。相当于终点坐标向值较大的轴做投影,所以叫投影法。

       由于总步长法实现简单且易于理解,所以在我们本实验中,终点判别使用的就是该方法。


        18.4 任意象限直线插补的原理

       前面讲解的都是以第一象限为例,实际上为了完成任意直线插补还需要考虑运动方向在4个象限内的情况,原理与第一象限同理,所以理解起来较为简单。


图18.4.1 四个象限直线插补图示


       这里以第二象限为例,此时的坐标点X轴方向都是小于0,与原本第一象限+X相反,所以此时步进电机运动方向需要反向,Y轴都为正,电机运动方向无需改变,这就可以插补出第二象限的直线了,第三、四象限同理,只要与第一象限坐标符号相反,所对应的轴的步进电机就往反方向运动,否则同向。总结下表:


表18.4.1 四个象限的直线插补计算


       这样我们就已将直线插补的原理部分讲解完毕,接下来让我们来到实战编程看看如何实现直线插补运动。


        18.5 任意象限直线插补实验


       18.5.1 硬件设计


       1、例程功能

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


       2、硬件资源

       1)LED灯:LED0 – PE0

       2)独立按键:

              KEY0 – PE12

              KEY1 – PE13

              KEY2 – PE14

       3)定时器8: TIM8_CH1:PC6,对应步进电机接口1

              TIM8_CH2:PC7,对应步进电机接口2

              方向引脚: DIR1: PF2

              DIR2: PE5

              脱机引脚: EN1: PF9

              EN2: PE6

       4)丝杆滑台步进电机2个

       5)步进电机驱动器2个

       6)12-50V的DC电源


       3、原理图


图18.5.1.1 步进电机接口1、2原理图


       本章将用到两个步进电机接口,分别是接口1以及接口2,如上图所示,接口1对应控制X轴的步进电机,接口2对应控制Y轴的步进电机。


       18.5.2 程序设计


       18.5.2.1 任意象限直线插补的配置步骤 

       1)初始化定时器

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

       2)电机旋转方向判定

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

       3)编写直线插补算法

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

       4)编写中断服务函数

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


       18.5.2.2 程序流程图


图18.5.2.2.1 直线插补流程图


       18.5.2.3 程序解析

       本实验在步进电机的基础驱动下添加代码,这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。源码我们主要分成定时器初始化、直线插补算法函数以及中断逻辑处理三大部分讲解,首先看下定时器,本次使用的PWM模式驱动,详解看stepper_tim.c:

/**
 * @brief       高级定时器TIMX PWM 初始化函数
 * @note
 *              高级定时器的时钟来自APB2,而PCLK2 = 170Mhz,我们设置PPRE2不分频,因此
 *              高级定时器时钟 = 170Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void atim_timx_oc_chy_init(uint16_t arr, uint16_t psc)
{
    ATIM_TIMX_PWM_CHY_CLK_ENABLE();                       /* TIMX 时钟使能 */
 
    g_atimx_handle.Instance = ATIM_TIMX_PWM;              /* 定时器x */
    g_atimx_handle.Init.Prescaler = psc;                  /* 定时器分频 */
    g_atimx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  /* 向上计数模式 */
    g_atimx_handle.Init.Period = arr;                    /* 自动重装载值 */
    g_atimx_handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; /* 分频因子 */
    g_atimx_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; 
    g_atimx_handle.Init.RepetitionCounter = 0;           /* 开始时不计数*/
    HAL_TIM_PWM_Init(&g_atimx_handle);                    /* 初始化PWM */
    
    g_atimx_oc_chy_handle.OCMode = TIM_OCMODE_PWM2;       /* 模式选择PWM2 */
    g_atimx_oc_chy_handle.Pulse = arr/2;
    g_atimx_oc_chy_handle.OCPolarity = TIM_OCPOLARITY_HIGH;/* 输出比较极性为高 */
    g_atimx_oc_chy_handle.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    g_atimx_oc_chy_handle.OCFastMode = TIM_OCFAST_DISABLE;
    g_atimx_oc_chy_handle.OCIdleState = TIM_OCIDLESTATE_RESET;
    g_atimx_oc_chy_handle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&g_atimx_handle, &g_atimx_oc_chy_handle, 
ATIM_TIMX_PWM_CH1); /* 配置TIMx通道y */
HAL_TIM_PWM_ConfigChannel(&g_atimx_handle, &g_atimx_oc_chy_handle, 
ATIM_TIMX_PWM_CH2); /* 配置TIMx通道y */      
}

       HAL_TIM_PWM_Init初始化TIM8并设置TIM8的ARR和PSC等参数,然后通过调用函数HAL_TIM_PWM_ConfigChannel设置TIM8_CH3、4的PWM模式以及比较值等参数,最后通过调用函数HAL_TIM_PWM_Start来使能TIM8以及使能PWM通道TIM8_CH3、4输出。通道的GPIO口初始化和定时器使能等程序放到回调函数HAL_TIM_PWM_MspInit中这里就不贴代码了,感兴趣打开源码看看。接着我们来看下stepper_motor.h核心部分:

/* 直线插补定义 */
 
#define AXIS_X              0   /* X轴标号 */
#define AXIS_Y              1   /* Y轴标号 */
#define LINE                0
 
enum dir
{
    CCW = 0,       /* 逆时针旋转 */
    CW ,           /* 顺时针旋转 */
};  
 
typedef enum           /* 电机状态 */
{
    STATE_STOP = 0,
    STATE_RUN = 1,
} st_motor_status_def;
 
typedef struct {
    uint16_t        pulse_pin;       /* 定时器脉冲输出引脚 */
    uint32_t        pulse_channel;   /* 定时器脉冲输出通道 */
    uint16_t        en_pin;          /* 电机使能引脚编号 */
    uint16_t        dir_pin;         /* 电机方向引脚编号 */
    GPIO_TypeDef    *dir_port;       /* 电机方向引脚端口 */
    GPIO_TypeDef    *en_port;        /* 电机使能引脚端口 */
} st_motor_ctr_def;
 
/*  插补算法类型定义 */
typedef struct {
    __IO uint8_t    moving_mode;     /* 运动模式 */
    __IO uint8_t    inter_dir;       /* 插补方向 */
    __IO uint8_t    qua_points;      /* 象限点 */
    __IO uint8_t    x_dir;           /* X轴方向 */
    __IO uint8_t    y_dir;           /* Y轴方向 */
    __IO int32_t    end_x;           /* 终点坐标X */
    __IO int32_t    end_y;           /* 终点坐标Y */
    __IO uint32_t   end_pulse;       /* 终点位置总的脉冲数 */
    __IO uint32_t   act_axis;        /* 活动轴 */
    __IO int32_t    f_e;             /* 函数方程 */
} inter_pol_def;
 
/* X轴电机相关引脚定义 */
#define STEPMOTOR_TIM_CHANNEL1      TIM_CHANNEL_1   /* 定时器8通道3 X轴 */
#define STEPMOTOR_TIM_PULSE_PIN_X   GPIO_PIN_6      /* 输出脉冲给X轴步进驱动器 */
 
#define STEPMOTOR_X_DIR_PORT        GPIOF           /* X轴方向脚 */
#define STEPMOTOR_X_DIR_PIN         GPIO_PIN_2      /* X轴方向脚 */
 
#define STEPMOTOR_X_ENA_PORT        GPIOF           /* X轴使能脚 */
#define STEPMOTOR_X_ENA_PIN         GPIO_PIN_9      /* X轴使能脚 */
 
/* Y轴电机相关引脚定义 */
#define STEPMOTOR_TIM_CHANNEL2      TIM_CHANNEL_2   /* 定时器8通道4 Y轴 */
#define STEPMOTOR_TIM_PULSE_PIN_Y   GPIO_PIN_7      /* 输出脉冲给Y轴步进驱动器 */
 
#define STEPMOTOR_Y_DIR_PORT        GPIOE           /* Y轴方向脚 */
#define STEPMOTOR_Y_DIR_PIN         GPIO_PIN_5      /* Y轴方向脚 */
 
#define STEPMOTOR_Y_ENA_PORT        GPIOE           /* Y轴使能脚 */
#define STEPMOTOR_Y_ENA_PIN         GPIO_PIN_6      /* Y轴使能脚 */
这里可以分为三部分,第一部分是步进电机运动相关的一些宏定义比如方向,运动状态等;第二部分inter_pol_def该结构体主要与直线插补算法相关;第三部分是关于步进电机X和Y轴上步进电机相关的引脚宏定义以及结构体定义。接着看下stepper_motor.c关于直线插补的部分源码:
/**
 * @brief       直线增量插补函数实现直线插补功能,
两个步进电机分别向X轴和Y轴步进IncX,IncY步
 * @param       IncX    :终点X轴坐标
 * @param       IncY    :终点Y轴坐标
 * @param       Speed   :进给速度
 * @retval      无
 */
void line_incmove(uint32_t IncX,uint32_t IncY,uint32_t Speed)
{
    /* 偏差方程置零 */
    g_pol_par.f_e = 0;
 
    /* 计算起点到终点坐标对应的脉冲数位置*/
    g_pol_par.end_x = IncX;
    g_pol_par.end_y = IncY;
    g_pol_par.end_pulse = g_pol_par.end_y + g_pol_par.end_x;
 
    /* 根据终点判断在直线上的进给方向,减少偏差 */
    if(g_pol_par.end_y > g_pol_par.end_x)
    {
        g_pol_par.act_axis = AXIS_Y;               /* 第一步进给Y轴 */
        g_pol_par.f_e = g_pol_par.f_e + g_pol_par.end_x;
    }
    else
    {
        g_pol_par.act_axis = AXIS_X;               /* 第一步进给X轴 */
        g_pol_par.f_e = g_pol_par.f_e - g_pol_par.end_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);
    /* ARR设置为比较值2倍,这样输出的波形就是50%的占空比 */    
    __HAL_TIM_SET_AUTORELOAD(&g_atimx_handle,Speed*2);  
 
TIM_CCxChannelCmd(TIM8, 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;              /* 标记电机正在运动 */
}
/**
 * @brief       实现任意象限直线插补
 * @param       coordsX    :终点X轴坐标
 * @param       coordsY    :终点Y轴坐标
 * @param       Speed      :进给速度
 * @retval      无
*/
void line_inpolation( int32_t coordsX,int32_t coordsY, int32_t Speed)
{
    if(g_motor_sta != STATE_STOP)    /* 当前电机正在运转 */
       return ;
    /* 其他象限的直线跟第一象限是一样,只是电机运动方向不一样 */
    g_pol_par.moving_mode = LINE;
    if(coordsX < 0)          /* 当x轴小于0时,电机方向设为反向*/
    {
        g_pol_par.x_dir = CCW;
        coordsX = -coordsX;      /* 取绝对值 */
        ST_LINE_DIR(CCW,AXIS_X);
    }
    else
    {
        g_pol_par.x_dir = CW;
        ST_LINE_DIR(CW,AXIS_X);
    }
    if(coordsY < 0)             /*当y轴小于0时,电机方向设为反向*/
    {
        g_pol_par.y_dir = CCW;
        coordsY = -coordsY;     /* 取绝对值 */
        ST_LINE_DIR(CCW,AXIS_Y);
    }
    else
    {
        g_pol_par.y_dir = CW;
        ST_LINE_DIR(CW,AXIS_Y);
    }
    line_incmove(coordsX,coordsY,Speed);
}

这里主要有两个函数,其中函数Line_InPolation用来实现任意象限直线插补的功能,有三个入口参数分别是:直线终点X坐标和Y坐标以及步进电机运行速度。首先该函数会判断终点坐标正负符号,设置对应的步进电机旋转方向,然后调用函数Llne_IncMove,该函数是我们实现插补算法的核心代码,首先会通过终点坐标计算出总步数,然后进行偏差判别、第一步的进给方向以及计算下一步的偏差值,然后设置比较值以及重装载值进而控制电机速度,最后使能定时器输出。到这里只是实现第一步的进给,后面的步数都是在中断处理逻辑实现,接着我们看下中断回调函数:

/**
 * @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.moving_mode == LINE)
{
    /* 偏差方程 > 0 ,位置位于直线上方,应向X轴进给 */
        if(g_pol_par.f_e > 0)    
        {
            g_pol_par.act_axis = AXIS_X; /* 第一象限的X轴进给时,偏差计算 */
            g_pol_par.f_e = g_pol_par.f_e - g_pol_par.end_y; 
        }
        else if(g_pol_par.f_e < 0) /* 偏差方程 < 0 ,位置位于直线下方,应向Y轴进给 */
        {
            g_pol_par.act_axis = AXIS_Y; /* 第一象限的Y轴进给时,偏差计算 */
            g_pol_par.f_e = g_pol_par.f_e+g_pol_par.end_x; 
        }
        /* 偏差为0的时候,判断x,y轴终点的大小决定进给方向 */
        else if(g_pol_par.f_e == 0) /* 偏差方程 = 0 ,直线上,应判断终点坐标再进给 */
        {
            if(g_pol_par.end_y > g_pol_par.end_x) /*当Y轴更长的话,应向Y轴进给*/
            {
                g_pol_par.act_axis = AXIS_Y;
                g_pol_par.f_e = g_pol_par.f_e+g_pol_par.end_x;
            }
            else
            {
                g_pol_par.act_axis = AXIS_X;
                g_pol_par.f_e = g_pol_par.f_e - g_pol_par.end_y;
            }
        }
    }
    /* 判断是否需要跟换进给轴 */
    if(axis != g_pol_par.act_axis)
    {
        TIM_CCxChannelCmd(TIM8,st_motor[axis].pulse_channel, TIM_CCx_DISABLE);
        TIM_CCxChannelCmd(TIM8,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(TIM8, st_motor[g_pol_par.act_axis].pulse_channel, 
TIM_CCx_DISABLE);  /* 关闭当前轴输出 */
        HAL_TIM_Base_Stop_IT(&g_atimx_handle);  /* 停止定时器 */
    }
}

       在中断回调函数中,首先保存上一次的活动轴是x还是y的步进电机,然后继续进行除第一步以外,其他步数的偏差判别、坐标进给、新的偏差计算,然后就是终点判别,并比较上一步进给轴与当前进给轴是不是一致,如果不是就切换当前轴。终点判别只要总步数值不等于0就会一直重复进行以上步骤,当等于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 */
/* 24.2Mhz的计数频率,太快容易堵转可适当调整 */
    stepper_init(0xFFFF - 1, 7 - 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开始插补 */
        {
/* 第一象限直线类似图形'/'  */
            line_inpolation(5 * 1600, 5 * 1600, 2000);  
            while (g_motor_sta);
/* 第四象限直线类似图形'\'  */
            line_inpolation(5 * 1600, -5 * 1600, 2000); 
            while (g_motor_sta);
/* 第三象限直线类似图形'/'  */
            line_inpolation(-5 * 1600, -5 * 1600, 2000);    
            while (g_motor_sta);
/* 第二象限直线类似图形'\'  */
            line_inpolation(-5 * 1600, 5 * 1600, 2000);     
            while (g_motor_sta);        
        }
        t++;
        if (t % 20 == 0)
        {
            LED0_TOGGLE();                          /* LED0(红灯) 翻转 */        
        }
        delay_ms(10);
    }
}

       mian函数主要就是初始化一些外设包括串口、定时器初、LCD等等,串口和LCD都会显示提示信息,当按下KEY0之后就会分别开启四个象限的直线插补运动。最后可以看出走的轨迹是一个正方形。大家如果要增加走的长度,可以自己调整Line_InPolation入口参数的x与y的坐标。


       18.5.3 下载验证

       需使用两个丝杆滑台组成一个标准的两轴X-Y平台,硬件连接好之后下载程序到开发板,液晶屏和串口都会显示提示信息,当我们按下按键KEY0,将会开启直线插补运动,最后可以看出我们的源码实现一个走正方形的轨迹,大家可自行下载程序,查看现象。


图18.5.3.1 串口提示信息


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

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

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    763867