ethercatsoe.c

Go to the documentation of this file.
00001 /*
00002  * Simple Open EtherCAT Master Library 
00003  *
00004  * File    : ethercatsoe.c
00005  * Version : 1.2.5
00006  * Date    : 09-04-2011
00007  * Copyright (C) 2005-2011 Speciaal Machinefabriek Ketels v.o.f.
00008  * Copyright (C) 2005-2011 Arthur Ketels
00009  * Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven 
00010  * Thanks to Hidde Verhoef for testing and improving the SoE module
00011  *
00012  * SOEM is free software; you can redistribute it and/or modify it under
00013  * the terms of the GNU General Public License version 2 as published by the Free
00014  * Software Foundation.
00015  *
00016  * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY
00017  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019  * for more details.
00020  *
00021  * As a special exception, if other files instantiate templates or use macros
00022  * or inline functions from this file, or you compile this file and link it
00023  * with other works to produce a work based on this file, this file does not
00024  * by itself cause the resulting work to be covered by the GNU General Public
00025  * License. However the source code for this file must still be made available
00026  * in accordance with section (3) of the GNU General Public License.
00027  *
00028  * This exception does not invalidate any other reasons why a work based on
00029  * this file might be covered by the GNU General Public License.
00030  *
00031  * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual
00032  * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for
00033  * the sole purpose of creating, using and/or selling or otherwise distributing
00034  * an EtherCAT network master provided that an EtherCAT Master License is obtained
00035  * from Beckhoff Automation GmbH.
00036  *
00037  * In case you did not receive a copy of the EtherCAT Master License along with
00038  * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany
00039  * (www.beckhoff.com).
00040  */
00041 
00042 /** \file
00043  * \brief
00044  * Servo over EtherCAT (SoE) Module.
00045  */
00046 
00047 #include <stdio.h>
00048 #include <string.h>
00049 #include <sys/time.h>
00050 #include <unistd.h>
00051 #include "ethercattype.h"
00052 #include "nicdrv.h"
00053 #include "ethercatbase.h"
00054 #include "ethercatmain.h"
00055 #include "ethercatsoe.h"
00056 
00057 /** SoE (Servo over EtherCAT) mailbox structure */
00058 typedef struct PACKED
00059 {
00060     ec_mbxheadert   MbxHeader;
00061     uint8           opCode    :3;
00062   uint8     incomplete  :1;
00063   uint8     error   :1;
00064   uint8     driveNo   :3;
00065   uint8     elementflags;
00066   union
00067   {
00068     uint16    idn;
00069     uint16    fragmentsleft;
00070   };  
00071 } ec_SoEt;
00072 
00073 static ec_SoEmappingt     SoEmapping;
00074 static ec_SoEattributet   SoEattribute;
00075 
00076 /** Report SoE error.
00077  *
00078  * @param[in]  Slave    = Slave number
00079  * @param[in]  idn      = IDN that generated error
00080  * @param[in]  Error      = Error code, see EtherCAT documentation for list
00081  */
00082 void ec_SoEerror(uint16 Slave, uint16 idn, uint16 Error)
00083 {
00084     ec_errort Ec;
00085 
00086   gettimeofday(&Ec.Time, 0);
00087     Ec.Slave = Slave;
00088     Ec.Index = idn;
00089     Ec.SubIdx = 0;
00090     EcatError = TRUE;
00091     Ec.Etype = EC_ERR_TYPE_SOE_ERROR;
00092     Ec.ErrorCode = Error;
00093     ec_pusherror(&Ec);
00094 }
00095 
00096 /** SoE read, blocking.
00097  * 
00098  * The IDN object of the selected slave and DriveNo is read. If a response
00099  * is larger than the mailbox size then the response is segmented. The function
00100  * will combine all segments and copy them to the parameter buffer.
00101  *
00102  * @param[in]  slave    = Slave number
00103  * @param[in]  driveNo    = Drive number in slave
00104  * @param[in]  elementflags = Flags to select what properties of IDN are to be transfered.
00105  * @param[in]  idn      = IDN.
00106  * @param[in,out] psize   = Size in bytes of parameter buffer, returns bytes read from SoE.
00107  * @param[out] p      = Pointer to parameter buffer
00108  * @param[in]  timeout    = Timeout in us, standard is EC_TIMEOUTRXM
00109  * @return Workcounter from last slave response
00110  */
00111 int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
00112 {
00113     ec_SoEt *SoEp, *aSoEp;
00114     uint16 totalsize, framedatasize;
00115   int wkc;
00116     uint8 *bp;
00117   uint8 *mp;
00118   uint16 *errorcode;
00119     ec_mbxbuft MbxIn, MbxOut;
00120     uint8 cnt;
00121   boolean NotLast;
00122 
00123     ec_clearmbx(&MbxIn);
00124   /* Empty slave out mailbox if something is in. Timeout set to 0 */
00125     wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, 0);
00126     ec_clearmbx(&MbxOut);
00127     aSoEp = (ec_SoEt *)&MbxIn;
00128     SoEp = (ec_SoEt *)&MbxOut;
00129     SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert));
00130     SoEp->MbxHeader.address = htoes(0x0000);
00131     SoEp->MbxHeader.priority = 0x00;
00132   /* get new mailbox count value, used as session handle */
00133     cnt = ec_nextmbxcnt(ec_slave[slave].mbx_cnt);
00134     ec_slave[slave].mbx_cnt = cnt;
00135     SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
00136   SoEp->opCode = ECT_SOE_READREQ;
00137   SoEp->incomplete = 0;
00138   SoEp->error = 0;
00139   SoEp->driveNo = driveNo;
00140   SoEp->elementflags = elementflags;
00141   SoEp->idn = htoes(idn);
00142   totalsize = 0;
00143   bp = p;
00144   mp = (uint8 *)&MbxIn + sizeof(ec_SoEt);
00145   NotLast = TRUE;
00146   /* send SoE request to slave */
00147     wkc = ec_mbxsend(slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
00148     if (wkc > 0) /* succeeded to place mailbox in slave ? */
00149   {
00150     while (NotLast)
00151     { 
00152       /* clean mailboxbuffer */
00153       ec_clearmbx(&MbxIn);
00154       /* read slave response */
00155       wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, timeout);
00156       if (wkc > 0) /* succeeded to read slave response ? */
00157       {
00158         /* slave response should be SoE, ReadRes */
00159         if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
00160             (aSoEp->opCode == ECT_SOE_READRES) &&
00161             (aSoEp->error == 0) &&
00162             (aSoEp->driveNo == driveNo) &&
00163             (aSoEp->elementflags == elementflags))
00164         {
00165           framedatasize = etohs(aSoEp->MbxHeader.length) - sizeof(ec_SoEt)  + sizeof(ec_mbxheadert);
00166           totalsize += framedatasize;
00167           /* Does parameter fit in parameter buffer ? */
00168           if (totalsize <= *psize)
00169           {
00170             /* copy parameter data in parameter buffer */
00171             memcpy(bp, mp, framedatasize);
00172             /* increment buffer pointer */
00173             bp += framedatasize;
00174           }
00175           else
00176           {
00177             framedatasize -= totalsize - *psize;
00178             totalsize = *psize;
00179             /* copy parameter data in parameter buffer */
00180             if (framedatasize > 0) memcpy(bp, mp, framedatasize);
00181           } 
00182 
00183           if (!aSoEp->incomplete) 
00184           {
00185             NotLast = FALSE;
00186             *psize = totalsize;
00187           } 
00188         } 
00189         /* other slave response */
00190         else
00191         {
00192           NotLast = FALSE;
00193           if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
00194               (aSoEp->opCode == ECT_SOE_READRES) &&
00195               (aSoEp->error == 1))
00196           {
00197             mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
00198             errorcode = (uint16 *)mp;
00199             ec_SoEerror(slave, idn, *errorcode);
00200           }
00201           else
00202           {
00203             ec_packeterror(slave, idn, 0, 1); /* Unexpected frame returned */
00204           }
00205           wkc = 0;
00206         }
00207       }
00208       else
00209       {
00210         NotLast = FALSE;
00211         ec_packeterror(slave, idn, 0, 4); /* no response */
00212       } 
00213     } 
00214   }
00215     return wkc;
00216 }
00217 
00218 /** SoE write, blocking.
00219  * 
00220  * The IDN object of the selected slave and DriveNo is written. If a response
00221  * is larger than the mailbox size then the response is segmented.
00222  *
00223  * @param[in]  slave    = Slave number
00224  * @param[in]  driveNo    = Drive number in slave
00225  * @param[in]  elementflags = Flags to select what properties of IDN are to be transfered.
00226  * @param[in]  idn      = IDN.
00227  * @param[in]  psize    = Size in bytes of parameter buffer.
00228  * @param[out] p      = Pointer to parameter buffer
00229  * @param[in]  timeout    = Timeout in us, standard is EC_TIMEOUTRXM
00230  * @return Workcounter from last slave response
00231  */
00232 int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
00233 {
00234     ec_SoEt *SoEp, *aSoEp;
00235     uint16 framedatasize, maxdata;
00236   int wkc;
00237     uint8 *mp;
00238   uint8 *hp;
00239   uint16 *errorcode;
00240     ec_mbxbuft MbxIn, MbxOut;
00241     uint8 cnt;
00242   boolean NotLast;
00243 
00244     ec_clearmbx(&MbxIn);
00245   /* Empty slave out mailbox if something is in. Timeout set to 0 */
00246     wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, 0);
00247     ec_clearmbx(&MbxOut);
00248     aSoEp = (ec_SoEt *)&MbxIn;
00249     SoEp = (ec_SoEt *)&MbxOut;
00250     SoEp->MbxHeader.address = htoes(0x0000);
00251     SoEp->MbxHeader.priority = 0x00;
00252   SoEp->opCode = ECT_SOE_WRITEREQ;
00253   SoEp->error = 0;
00254   SoEp->driveNo = driveNo;
00255   SoEp->elementflags = elementflags;
00256   hp = p;
00257   mp = (uint8 *)&MbxOut + sizeof(ec_SoEt);
00258     maxdata = ec_slave[slave].mbx_l - sizeof(ec_SoEt);
00259   NotLast = TRUE;
00260   while (NotLast)
00261   { 
00262     framedatasize = psize;
00263     NotLast = FALSE;
00264     SoEp->idn = htoes(idn);
00265     SoEp->incomplete = 0;
00266     if (framedatasize > maxdata)
00267     {
00268       framedatasize = maxdata;  /*  segmented transfer needed  */
00269       NotLast = TRUE;
00270       SoEp->incomplete = 1;
00271       SoEp->fragmentsleft = psize / maxdata;
00272     }
00273     SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert) + framedatasize);
00274     /* get new mailbox counter, used for session handle */
00275     cnt = ec_nextmbxcnt(ec_slave[slave].mbx_cnt);
00276     ec_slave[slave].mbx_cnt = cnt;
00277     SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
00278     /* copy parameter data to mailbox */
00279     memcpy(mp, hp, framedatasize);
00280     hp += framedatasize;
00281     psize -= framedatasize;
00282     /* send SoE request to slave */
00283     wkc = ec_mbxsend(slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
00284     if (wkc > 0) /* succeeded to place mailbox in slave ? */
00285     {
00286       if (!NotLast || !ec_mbxempty(slave, timeout))
00287       { 
00288         /* clean mailboxbuffer */
00289         ec_clearmbx(&MbxIn);
00290         /* read slave response */
00291         wkc = ec_mbxreceive(slave, (ec_mbxbuft *)&MbxIn, timeout);
00292         if (wkc > 0) /* succeeded to read slave response ? */
00293         {
00294           NotLast = FALSE;
00295           /* slave response should be SoE, WriteRes */
00296           if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
00297               (aSoEp->opCode == ECT_SOE_WRITERES) &&
00298               (aSoEp->error == 0) &&
00299               (aSoEp->driveNo == driveNo) &&
00300               (aSoEp->elementflags == elementflags))
00301           {
00302             /* SoE write succeeded */
00303           } 
00304           /* other slave response */
00305           else
00306           {
00307             if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
00308                 (aSoEp->opCode == ECT_SOE_READRES) &&
00309                 (aSoEp->error == 1))
00310             {
00311               mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
00312               errorcode = (uint16 *)mp;
00313               ec_SoEerror(slave, idn, *errorcode);
00314             }
00315             else
00316             {
00317               ec_packeterror(slave, idn, 0, 1); /* Unexpected frame returned */
00318             }
00319             wkc = 0;
00320           }
00321         }
00322         else
00323         {
00324           ec_packeterror(slave, idn, 0, 4); /* no response */
00325         } 
00326       } 
00327     } 
00328   }
00329     return wkc;
00330 }
00331 
00332 /** SoE read AT and MTD mapping.
00333  *
00334  * SoE has standard indexes defined for mapping. This function
00335  * tries to read them and collect a full input and output mapping size
00336  * of designated slave.
00337  *
00338  * @param[in] Slave   = Slave number
00339  * @param[out] Osize  = Size in bits of output mapping (MTD) found
00340  * @param[out] Isize  = Size in bits of input mapping (AT) found
00341  * @return >0 if mapping succesful.
00342  */
00343 int ec_readIDNmap(uint16 slave, int *Osize, int *Isize)
00344 {
00345     int retVal = 0;
00346   int wkc;
00347   int psize;
00348   uint16 entries, itemcount;
00349 
00350   *Isize = 0;
00351   *Osize = 0;
00352   psize = sizeof(SoEmapping);
00353   /* read output mapping via SoE */
00354   wkc = ec_SoEread(slave, 0, EC_SOE_VALUE_B, EC_IDN_MDTCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
00355   if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
00356   {
00357     /* command word (uint16) is always mapped but not in list */
00358     *Osize = 16; 
00359     for (itemcount = 0 ; itemcount < entries ; itemcount++)
00360     {
00361       psize = sizeof(SoEattribute);
00362       /* read attribute of each IDN in mapping list */
00363       wkc = ec_SoEread(slave, 0, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
00364       if ((wkc > 0) && (!SoEattribute.list))
00365       {
00366         /* length : 0 = 8bit, 1 = 16bit .... */
00367         *Osize += (int)8 << SoEattribute.length;
00368       } 
00369     } 
00370   } 
00371   psize = sizeof(SoEmapping);
00372   /* read input mapping via SoE */
00373   wkc = ec_SoEread(slave, 0, EC_SOE_VALUE_B, EC_IDN_ATCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
00374   if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
00375   {
00376     /* status word (uint16) is always mapped but not in list */
00377     *Isize = 16; 
00378     for (itemcount = 0 ; itemcount < entries ; itemcount++)
00379     {
00380       psize = sizeof(SoEattribute);
00381       /* read attribute of each IDN in mapping list */
00382       wkc = ec_SoEread(slave, 0, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
00383       if ((wkc > 0) && (!SoEattribute.list))
00384       {
00385         /* length : 0 = 8bit, 1 = 16bit .... */
00386         *Isize += (int)8 << SoEattribute.length;
00387       } 
00388     } 
00389   } 
00390 
00391   /* found some I/O bits ? */
00392     if ((*Isize > 0) || (*Osize > 0))
00393     {
00394         retVal = 1;
00395     }
00396     return retVal;
00397 }
Generated by  doxygen 1.6.3