DEV Community

Cover image for EtherCAT Motion Control Card -- Custom Curve
ZMotion Controller
ZMotion Controller

Posted on

EtherCAT Motion Control Card -- Custom Curve

Today, ZMotion shares customized motion curve application of EtherCAT motion control card, exactly, "how to encapsulate Basic instructions that you want to use into upper computer interface through online command" will be introduced.

01 Hardware Motion Control Card Introduction

ECI2828 motion control card is one bus type and modular network motion control card. As for programming method, it is developed through VC, VB, VS, C++ and several kinds of advanced languages. And there needs the dynamic library "zmotion.dll" for program running.

Image description

02 Qt development of motion control card

(1) Build new Qt project

Image description

Image description

Image description

Image description

--Copy files that relate to function library to new built project.

Image description

--Add static library of function library into new built project. (zmotion.lib)

Image description

Image description

Image description

--Add head files that relate to function library to project (zmcaux.cpp 、 zmcaux.h 、 Zmotion.h)

Image description

--Declare relevant head files, and define link handle.

Image description

(2) PC function introduction

--Obtain PC function library manual from "Zmotion/Download" or "Contact us".

--For PC programming, there needs to build the connection between upper computer and controller. Like motion control card connection, use net port, details as follow:

Image description

--If you want to encapsulate Basic instruction as upper computer, the interface that can be directly called must use "online command" interface to do function encapsulation. Below is online command interface description.

Image description

Image description

--Encapsulation example: SPEED of Basic instruction.

Image description

(3) Qt does encapsulation of Move_Pt instruction to achieve motion of custom curve

--Qt interface of customized curve as follow:

Image description

--Bind one channel function with Click Event of [connect] connect button through Qt to do controller connection.

//define one timer
QTimer *UpData = new QTimer(this);
connect(UpData,&QTimer::timeout,this,[=](){
   if(g_handle!=0)
   {
      //obtain axis position information
      ZAux_Direct_GetAllAxisPara(g_handle,"DPOS",AxisNum,Dpos);
      ui->DposX->setText(QString("%1").arg(Dpos[0]));
      ui->DposY->setText(QString("%1").arg(Dpos[1]));
      ui->DposZ->setText(QString("%1").arg(Dpos[2]));
      ui->DposU->setText(QString("%1").arg(Dpos[3]));
      //obtain axis speed information
      ZAux_Direct_GetAllAxisPara(g_handle,"MSPEED",AxisNum,Mspeed);
      ui->MspeedX->setText(QString("%1").arg(Mspeed[0]));
      ui->MspeedY->setText(QString("%1").arg(Mspeed[1]));
      ui->MspeedZ->setText(QString("%1").arg(Mspeed[2]));
      ui->MspeedU->setText(QString("%1").arg(Mspeed[3]));
      //obtain motion situation of each axis
      float idle[4]={0};
      ZAux_Direct_GetAllAxisPara(g_handle,"idle",AxisNum,idle);
      idle[0]=idle[0]+idle[1]+idle[2]+idle[3];
      if(idle[0]>(-4))
      {
         MotionStatus=1;//while moving
      }
      else
      {
         MotionStatus=0;
      }
   }
 }
Enter fullscreen mode Exit fullscreen mode

-- Update position and speed message of each axis through timer.

//define one timer
QTimer*UpData =newQTimer(this);
connect(UpData,&QTimer::timeout,this,[=](){
    if(g_handle!=0)
    {
        //obtain axis position information
       ZAux_Direct_GetAllAxisPara(g_handle,"DPOS",AxisNum,Dpos);
       ui->DposX->setText(QString("%1").arg(Dpos[0]));
       ui->DposY->setText(QString("%1").arg(Dpos[1]));
       ui->DposZ->setText(QString("%1").arg(Dpos[2]));
       ui->DposU->setText(QString("%1").arg(Dpos[3]));
        //obtain axis speed information
       ZAux_Direct_GetAllAxisPara(g_handle,"MSPEED",AxisNum,Mspeed);
       ui->MspeedX->setText(QString("%1").arg(Mspeed[0]));
       ui->MspeedY->setText(QString("%1").arg(Mspeed[1]));
       ui->MspeedZ->setText(QString("%1").arg(Mspeed[2]));
       ui->MspeedU->setText(QString("%1").arg(Mspeed[3]));
        //obtain motion situation of each axis
        floatidle[4]={0};
       ZAux_Direct_GetAllAxisPara(g_handle,"idle",AxisNum,idle);
       idle[0]=idle[0]+idle[1]+idle[2]+idle[3];
        if(idle[0]>(-4))
        {
            MotionStatus=1;//while moving
        }
        else
        {
            MotionStatus=0;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

-- Introduction: Basic -- Move_Pt

Image description

-- Move_Pt interface encapsulation

A.Encapsulate interface through "ZAux_DirectCommand()"

/*************************************************************
Description:    unit time distance
Input:          card link handle
                motion axes, axis list
                motion time, ticks unit, 1ticks≈1ms
                motion distance, units unit
Output:         No
Return:         error code
*************************************************************/
int32  MyApi::ZAux_Direct_MovePt(ZMC_HANDLE handle, int iAxisNum, int *piAxisList, int iTime, float *pfDisList)
{
    char  cmdbuff[2048],tempbuff[2048];
    char  cmdbuffAck[2048];
    //judge input parameters
    if((0 > iAxisNum || iAxisNum > MAX_AXIS_AUX)) return  ERR_AUX_PARAERR;
    if(NULL == piAxisList)      return  ERR_AUX_PARAERR;
    if(iTime<=0)                return  ERR_AUX_PARAERR;
    if(NULL == pfDisList)       return  ERR_AUX_PARAERR;
    //generate the command to select which axis to move......for example, Basic command, (0,1,2,3) means axis 0, axis 1, axis 2 and axis 3 are selected
    //encapsulate Basic axis-selection command through character string splicing command.
    BASE(piAxisList[0],piAxisList[1],.....piAxisList[i])
    strcpy(cmdbuff, "BASE(");
    for(int i = 0; i< iAxisNum-1; i++)
    {
        sprintf(tempbuff, "%d,",piAxisList[i]);
        strcat(cmdbuff, tempbuff);
    }
    sprintf(tempbuff, "%d)",piAxisList[iAxisNum-1]);
    strcat(cmdbuff, tempbuff);
    //change the line, keep encapsulating Basic command
    strcat(cmdbuff, "\n");
    //generate unit time motion distance command,......Basic command is achieved through Move_PT(ticks, dis1,dis2…)
    sprintf(tempbuff, "Move_PT(%d,",iTime);
    strcat(cmdbuff, tempbuff);
    //encapsulate each axis' motion distance
    for(int i = 0; i< iAxisNum-1; i++)
    {
        sprintf(tempbuff, "%f,",pfDisList[i]);
        strcat(cmdbuff, tempbuff);
    }
    sprintf(tempbuff, "%f)",pfDisList[iAxisNum-1]);
    strcat(cmdbuff, tempbuff);
    //call the command to execute the function
    //printf("%s",cmdbuff);
    return ZAux_DirectCommand(handle, cmdbuff, cmdbuffAck, 2048);
}
Enter fullscreen mode Exit fullscreen mode

B.Qt routine calls the interface encapsulated just now. -- MyApi::ZAux_Direct_MovePt()

Image description

//open Move_Pt motion
voidWidget::on_PtStartButton_clicked()
{
   if(0== MotionStatus)
   {
      intbuffNum=0;
      //obtain axis 0 remaining buffers, command only can be sent when the number of axes in buffer is enough.
      ZAux_Direct_GetRemain_LineBuffer(g_handle,0,&buffNum);
      if(buffNum>3)
      {
         intAxisList[4]={0,1,2,3};
         floatDisList[3][5];
         for(inti=0;i<3;i++)
         {
            for(intj=0;j<5;j++)
            {
               DisList[i][j]=LineData[i][j]->text().toFloat();
            }
            //call the function interface encapsulated by onw to move MOVE_PT.
            myapi->ZAux_Direct_MovePt(g_handle,AxisNum,AxisList,(int)(DisList[i][0]),&DisList[i][1]);
         }
      }
      else
      {
         QMessageBox::warning(this,"warning","insufficient axis in buffer");
      }
    }
    else
    {
       QMessageBox::warning(this,"warning","system is operting......");
   }
}
Enter fullscreen mode Exit fullscreen mode

C. Oscilloscope capture

Image description

(6)One API is encapsulated through sending several move_ptabs commands to process.

A.Send encapsulations of several move_ptabs commands.

/*************************************************************
Description:    send multiple unit time distance instructions in one time
Input:          card link handle
                the number of motion axes, axis list
                motion time, the unit is ticks, 1ticks≈1ms
                motion distance, the unit is units
Output:         No
Return:         error codes
*************************************************************/
int32  MyApi::ZAux_Direct_MovePtAbsS(ZMC_HANDLE handle, int iAxisNum, int *piAxisList, int ApiNum,int *iTime, float *pfDisList)
{
   char  cmdbuff[2048*128],tempbuff[2048];
   char  cmdbuffAck[2048];
   //input parameters to judge
   if((0 > iAxisNum || iAxisNum > MAX_AXIS_AUX)) return  ERR_AUX_PARAERR;//the number of axes is incorrect
   if(NULL == piAxisList)      return  ERR_AUX_PARAERR;//axis list is blank
   if(iTime== 0)               return  ERR_AUX_PARAERR;//time list is blank
   if(NULL == pfDisList)       return  ERR_AUX_PARAERR;//motion distance list is blank
   if((ApiNum<0)||(ApiNum>50))       return  ERR_AUX_PARAERR;//Api numbers is incorrect
   //generate the command to select which axes to be moved......for example, Basic command, BASE(0,1,2,3) means axis 0, axis 1, axis 2 and axis 3 are selected
   //encapsulate Basic axis selection instruction through character string splicing command
   BASE(piAxisList[0],piAxisList[1],.....piAxisList[i])
   strcpy(cmdbuff, "BASE(");
   for(int i = 0; i< iAxisNum-1; i++)
   {
      sprintf(tempbuff, "%d,",piAxisList[i]);
      strcat(cmdbuff, tempbuff);
   }
   sprintf(tempbuff, "%d)",piAxisList[iAxisNum-1]);
   strcat(cmdbuff, tempbuff);
   //keep encapsulating Basic command in new line
   strcat(cmdbuff, "\n");
   for(int j=0;j<ApiNum;j++)
   {
      if(iTime[j]>0)
      {
         //generate unit time motion distance command, ......Basic command is achieved through Move_PT(ticks, dis1,dis2…)
         sprintf(tempbuff, "Move_PtAbs(%d,",iTime[j]);
         strcat(cmdbuff, tempbuff);
         //encapsulate motion distance of each axis
         for(int i = 0; i< iAxisNum-1; i++)
         {
            sprintf(tempbuff, "%.5f,",pfDisList[i+j*iAxisNum]);
            strcat(cmdbuff, tempbuff);
         }
         sprintf(tempbuff, "%.5f)",pfDisList[iAxisNum-1+j*iAxisNum]);
         strcat(cmdbuff, tempbuff);
         strcat(cmdbuff, "\n");
      }
   }
   //call the command to execute the function
   return ZAux_DirectCommand(handle, cmdbuff, cmdbuffAck, 2048);
}
Enter fullscreen mode Exit fullscreen mode

B.Qt routine calls the interface encapsulated just now -- MyApi::ZAux_Direct_MovePtAbsS()

//obtain axis motion remaining buffers
ZAux_Direct_GetRemain_LineBuffer(g_handle,0,&buffNum);       
if((buffNum>ApiNum*2)&&(SendNum*1<(4*ui->HorizoScale->text().toFloat())))
{
                Num = (float) 4*ui->HorizoScale->text().toFloat();
                switch (RunType) {
                //((-sin(PI*2*i/T)/(PI*2))+i/T)*500
                case 0:
                    for(int i=0;i<ApiNum;i++)
                    {                        
DisList[i]=(float) (((-sin(M_PI*2*SendNum/(Num-1))/(M_PI*2))+SendNum/ (Num-1))*500);
                        SendNum=SendNum+1;                    }
                    break;
                default:
                    break;
                }
                if(SendNum<ApiNum+10)
                {
                    //set current point
                    ui->Canvas->StartPoint.setX(0);
                    ui->Canvas->StartPoint.setY(-DisList[0]*EquivalentY);
                    ui->Canvas->StopPoint.setX(0);
                    ui->Canvas->StopPoint.setY(-DisList[0]*EquivalentY);
                    TimerWaveform->start(10);
                    QString Str;
                    char buff[1204];
                    //send the online command to open oscilloscope
                    Str=QString("%1%2%3%4%5").arg("SCOPE(ON,1,").arg(0).arg(",").arg(3500).arg(",DPOS(0))");
                    ZAux_Execute(g_handle,Str.toLatin1().data(),buff,1024);
                    ZAux_Trigger(g_handle);
                    QThread::msleep(1);
                }
                myapi->ZAux_Direct_MovePtAbsS(g_handle,1, &AxisList,ApiNum, TimeList, DisList);
}
Enter fullscreen mode Exit fullscreen mode

C.Qt collects position data of axis motion, and generate position wavaform.

oidDraw::paintEvent(QPaintEvent*event)
{
    if(DrawFlag!=0)
    {
        //example, one painter draws, this means equipment to draw
        QPainterPainter(WaveformFigure);
        Painter.translate(0,(int)(ImgH/2));
        //set the brush
        QPenPen(QColor(255,0,0));
        Pen.setWidth(4);
        Pen.setStyle(Qt::SolidLine);
        Painter.setPen(Pen);
        //send the command about oscilloscope to be triggered to capture data
        if((CurTriggerNum>=SingTriggerNum))
        {
           qDebug()<<"trigger the oscilloscope"<<SingTriggerNum;
           QStringStr;
           charbuff[1204];
           Str=QString("%1%2%3%4%5").arg("SCOPE(ON,1,").arg(StartTableId).arg(",").arg(StartTableId+SingTriggerNum).arg(",DPOS(0))");
           ZAux_Execute(g_handle,Str.toLatin1().data(),buff,1024);
           ZAux_Trigger(g_handle);
        }
        //return the number of points of current SCOPE captured data
        intoldTriggerNum=CurTriggerNum;
        ZAux_Direct_GetUserVar(g_handle,"SCOPE_POS",&CurTriggerNum);
        //obtain current SCOPE captured data
        ZAux_Direct_GetTable(g_handle,StartTableId+oldTriggerNum,CurTriggerNum-oldTriggerNum,Data);
        //Qlist data processing
        QList<float>::iteratoriet;
        for(inti=0;i<CurTriggerNum-oldTriggerNum;i++,PointNum++)
        {
           iet= EquivalentDataY.begin()+PointNum;
           //set starting point
           if(0==i)
           {
              StartPoint.setY(-EquivalentY*(*(iet-1)));
              StartPoint.setX((PointNum-1)*EquivalentX);
           }
           else
           {
               StartPoint=StopPoint;
           }
           //obtain the position of new point
           if(iet<EquivalentDataY.end())
           {
              *iet=Data[i];
           }
           else
           {
               EquivalentDataY.append(Data[i]);
           }
          StopPoint.setY(-EquivalentY*(Data[i]));
          StopPoint.setX((PointNum)*EquivalentX);
           //draw the line
           if((StartPoint!= StopPoint)&& (PointNum>0))
           {
              Painter.drawLine(StartPoint,StopPoint);
           }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

D.Capture waveform to check effects.

a.Y=((-sin(PI*2*i/T)/(PI*2))+i/T)*500 speed and position curve

Image description

b.Qt captures Y=((-sin(PI*2*i/T)/(PI*2))+i/T)*position curve of 500

Image description

That's all, thank you for your reading -- EtherCAT motion control card -- Custom Curve.

This article is edited by ZMOTION, here, share with you, let's learn together.

ZMOTION: DO THE BEST TO USE MOTION CONTROL.

Note: Copyright belongs to ZMotion Technology, if there is reproduction, please indicate article source. Thank you.

Top comments (0)