搜索
bottom↓
回复: 0

《DMF407电机控制专题教程_V1.0》第18章 步进电机直线插补

[复制链接]

出0入234汤圆

发表于 2022-8-18 10:20:00 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-8-18 10:19 编辑

1)实验平台:正点原子DMF407电机开发板
2)平台购买地址: https://detail.tmall.com/item.htm?&id=677230699323
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/stm32dj/ATK-DMF407.html
4)对正点原子电机开发板感兴趣的同学可以加群讨论: 5929291 lQLPJxaFi2zaB4UWWrDAMgIsFEW2pwLb3abnwDMA_90_22.png
lQDPJxaFi2nfFizMjM0CbLCPlxn_FVheIQLb3aGrwFQA_620_140.jpg

lQLPJxaFi2nfFhLMkM0BXrDNvOUyeU_FPgLb3aGvQNIA_350_144.png


第18章 步进电机直线插补


前面我们所学习的单一步进电机只能完成和结构相关的简单直线运动(例如丝杆),也称为“一维运动”,但在实际应用中往往需要工件做斜线甚至圆弧运动,这时就需要通过两个步进电机连轴做同步运动才能实现,这种控制方式称为“多轴联动”。所以本章我们将学习控制两个直线位移的步进电机实现双轴联动,即:步进电机的直线插补运动。
本章分为如下几个小节:
18.1 插补的简介
18.2 插补算法
18.3 逐点比较法直线插补原理
18.4 任意象限直线插补原理
18.5 任意象限直线插补实验


18.1 插补的简介

        插补:机床数控系统依照一定方法确定刀具运动轨迹的过程。也可以说,已知曲线上的某些数据,数控装置根据输入的零件程序的信息,将程序段所描述的曲线的起点、终点之间的空间进行数据密化,从而形成要求的轮廓轨迹,这种“数据密化”机能就称为“插补”。
        在数控机床中,刀具不能严格地按照要求加工的曲线运动,只能用折线轨迹逼近所要加工的曲线。 机床数控系统依照一定方法确定刀具运动轨迹的过程。 也可以说,已知曲线上的某些数据,按照某种算法计算已知点之间的中间点的方法,也称为“数据点的密化”。
常见的插补方式有:直线插补、圆弧插补、抛物线插补、样条线插补等。而我们本章所要学习的就是直线插补,我们下面了解下什么是直线插补。
        直线插补(Llne Interpolation):是车床上常用的一种插补方式,通常用于走斜线或直线轨迹,在此方式中,两点间的插补沿着直线的点群来逼近。在实际应用中一个零件的轮廓可以是多样的,有直线、圆弧,也有可能是任意曲线、样条线等,而数控机床的刀具往往是不能以曲线的实际轮廓去走刀的,而是近似地以若干条很小的直线去走刀逼近,走刀的方向一般是x和y方向。
image002.jpg
图18.1.1 直线插补图示

18.2 插补算法

插补算法的好坏,直接影响系统的控制速度和零件加工精度,那常见的插补算法有哪些?常见的插补算法有:脉冲增量插补法、数字增量插补法这两大类,下面我们来介绍下这两种算法。
        脉冲增量插补法:该插补为行程标量插补,常用于开环系统,每次插补结束产生一个行程增量,以脉冲的方式输出,一个脉冲所产生的坐标轴移动量叫做脉冲当量,也可以说,通过向各个运动轴分配驱动脉冲来控制机床坐标轴协调运动,从而加工出一定轮廓形状的算法
        数字增量插补法:该插补为时间标量插补,分两步进行。首先计算出插补周期内各坐标轴的增量值,称为粗插补;然后在根据采样得到的实际位置增量计算跟随误差,得到速度指令输出给伺服驱动系统,称为精插补。使用于闭环或者半闭环系统。
由上述两种插补算法,其中脉冲增量算法非常适用于以步进电机为驱动装置的开环系统。脉冲增量算法也分为多种:逐点比较法、数字积分法、最小偏差法、目标点跟踪法、单步追踪法等,我们这里只介绍我们最为常用的逐点比较法。
逐点比较法:顾名思义,就是每走一步都要将加工点的瞬时坐标同规定的图形轨迹相比较,判断其偏差,然后决定下一步的走向,如果加工点走到图形外面去了,那么下一步就要向图形里边走,如果加工点在图形里边,那下一步就往图形外面走,以缩小偏差,这样就能得到一个非常接近规定图形的轨迹。     
18.3 逐点比较法直线插补原理
18.3.1 逐点比较法整体流程图

逐点比较法不管是直线插补还是圆弧插补步骤都分为以下四步:
(1)偏差判别:根据偏差函数值判别加工点的相对直线位置
(2)坐标进给:沿减小误差的方向进给一步
(3)偏差计算:进给后,计算新加工点相对直线的位置
(4)终点判别:判别是否到达终点,未到达则继续第一步步骤,继续插补,到终点则停止
整体流程图如下:
image004.png
图18.3.1.1 逐点比较法流程图

18.3.2 偏差判别
        设有斜线OA,A点坐标为(Xe,Ye),有一动点P(Xi.Yi),他可能在OA线上,也可能在线的上方或下方,所以就有以下几种可能:
image006.jpg
图18.3.2.1 第一象限直线插补示意图


        当P点在斜线上时,由于斜率相等就有:
        Yi / Xi = Ye / Xe
        取偏差判别式:F= Xe*Yi – Xi*Ye = 0
        当P点在直线上方,此时OP直线的斜率比OA大,那么有下述关系:
         Yi / Xi > Ye / Xe
        偏差判别式: F = Xe*Yi – Xi*Ye > 0
        当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 终点判别
        最后就是终点判别,插补到何时停止呢?常用的终点判别法有三种,分别是:总步长法、终点坐标法、投影法。
        总步长法:插补前,将终点位置的x,y坐标绝对值相加求和,得出总步数,开始插补时,无论是往哪个轴给进一步,总步数都减一,直到为0时,代表已到终点,就停止插补。
        终点坐标法:插补前,先定义x、y轴两个方向的步长计数器,分别存放两个坐标轴方向上应该走的总步数。开始插补后当 x、y方向每进给一步,就在相应的计数器中减1,直到两个计数器的值都减为 0 时,刀具抵达终点,停止插补。
        投影法:在插补处理开始之前,先确定所走步数较大的那根轴,并求出该轴运动的总步数,然后存放在总步长计数器中,当对应的轴有进给时,计数器减 1,直到计数器为 0。相当于终点坐标向值较大的轴做投影,所以叫投影法。
        由于总步长法实现简单且易于理解,所以在我们本实验中,终点判别使用的就是该方法。
18.4 任意象限直线插补的原理
前面讲解的都是以第一象限为例,实际上为了完成任意直线插补还需要考虑运动方向在4个象限内的情况,原理与第一象限同理,所以理解起来较为简单。
image008.jpg
图18.4.1 四个象限直线插补图示

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

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

        这样我们就已将直线插补的原理部分讲解完毕,接下来让我们来到实战编程看看如何实现直线插补运动。
18.5 任意象限直线插补实验
18.5.1 硬件设计

1、例程功能
本实验使用步进电机接口三控制X轴步进电机和接口四控制Y轴步进电机,当按下KEY0控制步进电机开始直线插补运动。注意驱动器设置为1.0A,8细分,也就是旋转一圈所需要1600个脉冲数;LED0作为程序运行状态指示灯,闪烁周期200ms。
2、硬件资源
1)LED灯:LED0 – PE0
2)独立按键:
    KEY0 – PE2
        KEY1 – PE3
        KEY2 – PE4
3)定时器8: TIM8_CH3:PI7,对应步进电机接口三
                          TIM8_CH4:PC9,对应步进电机接口四
   方向引脚: DIR3: PB2
                          DIR4: PH2
   脱机引脚: EN3: PF11
                          EN4: PH3
4)丝杆滑台步进电机2个
5)步进电机驱动器2个
6)12-50V的DC电源
3、原理图
image010.jpg
图18.5.1.1 步进电机接口3、4原理图

        本章将用到两个步进电机接口,分别是接口三以及接口四,如上图所示,接口三对应控制X轴的步进电机,接口四对应控制Y轴的步进电机。
18.5.2 程序设计
18.5.2.1 任意象限直线插补的配置步骤

1)初始化定时器
        初始化定时器通道IO,设置ARR、PSC,计数方式以及脉冲输出模式等
2)电机旋转方向判定
        实现任意象限直线插补,需提前判断所属象限,设置电机旋转方向
3)编写直线插补算法
        实现插补第一步,偏差判别,坐标进给,偏差计算并开启脉冲输出与中断使能
4)编写中断服务函数
        实现除第一步以外的其他步数的偏差判别、坐标进给、偏差计算、终点判别
18.5.2.2 程序流程图
image012.png
图18.5.2.2.1 直线插补流程图

18.5.2.3 程序解析
        本实验在步进电机的基础驱动下添加代码,这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。源码我们主要分成定时器初始化、直线插补算法函数以及中断逻辑处理三大部分讲解,首先看下定时器,本次使用的PWM模式驱动,详解看stepper_tim.c
/**
* @brief               高级定时器PWM 初始化函数
* @note
*                      高级定时器的时钟来自APB2, 而PCLK2 = 168Mhz, 我们设置PPRE2不分频, 因此
*                      高级定时器时钟 = 168Mhz
*                      定时器溢出时间计算方法: 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;                    /* 模式选择PWM1*/
    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_ConfigChannel(&g_atimx_handle, &g_atimx_oc_chy_handle,
                                                                ATIM_TIMX_PWM_CH3);                 /* 配置TIMx通道y */
HAL_TIM_PWM_ConfigChannel(&g_atimx_handle, &g_atimx_oc_chy_handle,
                                                                ATIM_TIMX_PWM_CH4);                 /* 配置TIMx通道y */
    HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 2, 2);                         /* 设置中断优先级分组*/
    HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
    /* 直线插补提前使能主输出以及清除更新中断标志位 */
    __HAL_TIM_MOE_ENABLE(&g_atimx_handle);                              /* 主输出使能 */
    __HAL_TIM_CLEAR_IT(&g_atimx_handle,TIM_IT_UPDATE);          /* 清除更新中断标志位*/
   
}
        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_3           /* 定时器8通道3 X轴 */
#define STEPMOTOR_TIM_PULSE_PIN_X   GPIO_PIN_7              /* 输出脉冲给X轴步进驱动器 */

#define STEPMOTOR_X_DIR_PORT        GPIOB                   /* X轴方向脚 */
#define STEPMOTOR_X_DIR_PIN         GPIO_PIN_2              /* X轴方向脚 */

#define STEPMOTOR_X_ENA_PORT        GPIOF                   /* X轴使能脚 */
#define STEPMOTOR_X_ENA_PIN         GPIO_PIN_11             /* X轴使能脚 */

/* Y轴电机相关引脚定义 */
#define STEPMOTOR_TIM_CHANNEL2      TIM_CHANNEL_4           /* 定时器8通道4 Y轴 */
#define STEPMOTOR_TIM_PULSE_PIN_Y   GPIO_PIN_9              /* 输出脉冲给Y轴步进驱动器 */

#define STEPMOTOR_Y_DIR_PORT        GPIOH                   /* Y轴方向脚 */
#define STEPMOTOR_Y_DIR_PIN         GPIO_PIN_2              /* Y轴方向脚 */

#define STEPMOTOR_Y_ENA_PORT        GPIOH                   /* Y轴使能脚 */
#define STEPMOTOR_Y_ENA_PIN         GPIO_PIN_3              /* 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;                          /* 取绝对值 */
        ST3_LINE_DIR(CCW,AXIS_X);
    }
    else
    {
        g_pol_par.x_dir = CW;
        ST3_LINE_DIR(CW,AXIS_X);
    }
    if(coordsY < 0)                                         /*当y轴小于0时,电机方向设为反向*/
    {
        g_pol_par.y_dir = CCW;
        coordsY = -coordsY;                                 /* 取绝对值 */
        ST3_LINE_DIR(CCW,AXIS_Y);
    }
    else
    {
        g_pol_par.y_dir = CW;
        ST3_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(336, 8, 2, 7);           /* 设置时钟,168Mhz */
    delay_init(168);                                 /* 延时初始化 */
    usart_init(115200);                              /* 串口初始化为115200 */
    led_init();                                      /* 初始化LED */
    key_init();                                      /* 初始化按键 */
    lcd_init();                                      /* 初始化LCD */
    stepper_init(0xFFFF, 8 - 1);                     /* 定时器初始化 */
   
    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10,10,200,16,16,"Stepper Motor Test",   g_point_color);
    lcd_show_string(10,30,200,16,16,"KEY0:Run Once",        g_point_color);
    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,将会开启直线插补运动,最后可以看出我们的源码实现一个走正方形的轨迹,大家可自行下载程序,查看现象。
image014.jpg
图18.5.3.1 串口提示信息 



阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 13:26

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表