ethercatbase.c

Go to the documentation of this file.
00001 /*
00002  * Simple Open EtherCAT Master Library 
00003  *
00004  * File    : ethercatbase.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  *
00011  * SOEM is free software; you can redistribute it and/or modify it under
00012  * the terms of the GNU General Public License version 2 as published by the Free
00013  * Software Foundation.
00014  *
00015  * SOEM is distributed in the hope that it will be useful, but WITHOUT ANY
00016  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00017  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00018  * for more details.
00019  *
00020  * As a special exception, if other files instantiate templates or use macros
00021  * or inline functions from this file, or you compile this file and link it
00022  * with other works to produce a work based on this file, this file does not
00023  * by itself cause the resulting work to be covered by the GNU General Public
00024  * License. However the source code for this file must still be made available
00025  * in accordance with section (3) of the GNU General Public License.
00026  *
00027  * This exception does not invalidate any other reasons why a work based on
00028  * this file might be covered by the GNU General Public License.
00029  *
00030  * The EtherCAT Technology, the trade name and logo “EtherCAT” are the intellectual
00031  * property of, and protected by Beckhoff Automation GmbH. You can use SOEM for
00032  * the sole purpose of creating, using and/or selling or otherwise distributing
00033  * an EtherCAT network master provided that an EtherCAT Master License is obtained
00034  * from Beckhoff Automation GmbH.
00035  *
00036  * In case you did not receive a copy of the EtherCAT Master License along with
00037  * SOEM write to Beckhoff Automation GmbH, Eiserstraße 5, D-33415 Verl, Germany
00038  * (www.beckhoff.com).
00039  */
00040 
00041 /** \file
00042  * \brief
00043  * Base EtherCAT functions. 
00044  *
00045  * Setting up a datagram in an ethernet frame.
00046  * EtherCAT datagram primitives, broadcast, auto increment, configured and
00047  * logical addressed data transfers. All base transfers are blocking, so
00048  * wait for the frame to be returned to the master or timeout. If this is
00049  * not acceptable build your own datagrams and use the functions from nicdrv.c.
00050  */
00051 
00052 #include <stdio.h>
00053 #include <string.h>
00054 #include "ethercattype.h"
00055 #include "nicdrv.h"
00056 #include "ethercatbase.h"
00057 
00058 /** Generate and set EtherCAT datagram in a standard ethernet frame.
00059  *
00060  * @param[out] frame      = framebuffer
00061  * @param[in] com     = command
00062  * @param[in] idx     = index used for TX and RX buffers
00063  * @param[in] ADP     = Address Position
00064  * @param[in] ADO     = Address Offset
00065  * @param[in] length    = length of datagram excluding EtherCAT header
00066  * @param[in] data      = databuffer to be copied in datagram
00067  * @return always 0
00068  */
00069 int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
00070 {
00071   ec_comt *datagramP;
00072   uint8 *frameP;
00073 
00074   frameP = frame;
00075   /* Ethernet header is preset and fixed in frame buffers
00076      EtherCAT header needs to be added after that */
00077   datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
00078   datagramP->elength = htoes(EC_ECATTYPE + EC_HEADERSIZE + length);
00079   datagramP->command = com;
00080   datagramP->index = idx; 
00081   datagramP->ADP = htoes(ADP);
00082   datagramP->ADO = htoes(ADO);
00083   datagramP->dlength = htoes(length); 
00084   if (length > 0)
00085   {
00086     memcpy(&frameP[ETH_HEADERSIZE + EC_HEADERSIZE], data, length);
00087   }
00088   /* set WKC to zero */
00089   frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length] = 0x00;
00090   frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length + 1] = 0x00;
00091   /* set size of frame in buffer array */
00092   ec_txbuflength[idx] = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + length;
00093 
00094   return 0;
00095 }
00096 
00097 /** Add EtherCAT datagram to a standard ethernet frame with existing datagram(s).
00098  *
00099  * @param[out] frame      = framebuffer
00100  * @param[in] com     = command
00101  * @param[in] idx     = index used for TX and RX buffers
00102  * @param[in] more      = TRUE if still more datagrams to follow
00103  * @param[in] ADP     = Address Position
00104  * @param[in] ADO     = Address Offset
00105  * @param[in] length    = length of datagram excluding EtherCAT header
00106  * @param[in] data      = databuffer to be copied in datagram
00107  * @return Offset to data in rx frame, usefull to retrieve data after RX.
00108  */
00109 int ec_adddatagram(void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
00110 {
00111   ec_comt *datagramP;
00112   uint8 *frameP;
00113   uint16 prevlength;
00114 
00115   frameP = frame;
00116   /* copy previous frame size */
00117   prevlength = ec_txbuflength[idx]; 
00118   datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
00119   /* add new datagram to ethernet frame size */
00120   datagramP->elength = htoes( etohs(datagramP->elength) + EC_HEADERSIZE + length );
00121   /* add "datagram follows" flag to previous subframe dlength */
00122   datagramP->dlength = htoes( etohs(datagramP->dlength) | EC_DATAGRAMFOLLOWS );
00123   /* set new EtherCAT header position */
00124   datagramP = (ec_comt*)&frameP[prevlength - EC_ELENGTHSIZE];
00125   datagramP->command = com;
00126   datagramP->index = idx; 
00127   datagramP->ADP = htoes(ADP);
00128   datagramP->ADO = htoes(ADO);
00129   if (more)
00130   {
00131     /* this is not the last datagram to add */
00132     datagramP->dlength = htoes(length | EC_DATAGRAMFOLLOWS); 
00133   }
00134   else
00135   {
00136     /* this is the last datagram in the frame */
00137     datagramP->dlength = htoes(length); 
00138   }
00139   if (length > 0)
00140   {
00141     memcpy(&frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE], data, length);
00142   } 
00143   /* set WKC to zero */
00144   frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length] = 0x00;
00145   frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length + 1] = 0x00;
00146   /* set size of frame in buffer array */
00147   ec_txbuflength[idx] = prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + EC_WKCSIZE + length;
00148 
00149   /* return offset to data in rx frame
00150      14 bytes smaller than tx frame due to stripping of ethernet header */
00151   return prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE - ETH_HEADERSIZE;  
00152 }
00153 
00154 /** BRW "broadcast write" primitive. Blocking.
00155  *
00156  * @param[in] ADP     = Address Position, normally 0
00157  * @param[in] ADO     = Address Offset, slave memory address
00158  * @param[in] length    = length of databuffer
00159  * @param[in] data      = databuffer to be written to slaves
00160  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00161  * @return Workcounter or EC_NOFRAME
00162  */ 
00163 int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00164 {
00165   uint8 idx;
00166   int wkc;
00167 
00168   /* get fresh index */
00169   idx = ec_getindex();
00170   /* setup datagram */
00171   ec_setupdatagram(&ec_txbuf[idx], EC_CMD_BWR, idx, ADP, ADO, length, data);
00172   /* send data and wait for answer */
00173   wkc = ec_srconfirm (idx, timeout);
00174   /* clear buffer status */
00175     ec_setbufstat(idx, EC_BUF_EMPTY);
00176 
00177   return wkc;
00178 } 
00179 
00180 /** BRD "broadcast read" primitive. Blocking.
00181  *
00182  * @param[in] ADP     = Address Position, normally 0
00183  * @param[in] ADO     = Address Offset, slave memory address
00184  * @param[in] length    = length of databuffer
00185  * @param[out] data     = databuffer to put slave data in
00186  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00187  * @return Workcounter or EC_NOFRAME
00188  */ 
00189 int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00190 {
00191   uint8 idx;
00192   int wkc;
00193 
00194   /* get fresh index */
00195   idx=ec_getindex();
00196   /* setup datagram */
00197   ec_setupdatagram(&ec_txbuf[idx], EC_CMD_BRD, idx, ADP, ADO, length, data);
00198   /* send data and wait for answer */
00199   wkc = ec_srconfirm (idx, timeout);
00200   if (wkc > 0)
00201   {
00202     /* copy datagram to data buffer */
00203     memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00204   } 
00205   /* clear buffer status */
00206     ec_setbufstat(idx, EC_BUF_EMPTY);
00207 
00208   return wkc;
00209 } 
00210 
00211 /** APRD "auto increment address read" primitive. Blocking.
00212  *
00213  * @param[in] ADP     = Address Position, each slave ++, slave that has 0 excecutes
00214  * @param[in] ADO     = Address Offset, slave memory address
00215  * @param[in] length    = length of databuffer
00216  * @param[out] data     = databuffer to put slave data in
00217  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00218  * @return Workcounter or EC_NOFRAME
00219  */ 
00220 int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00221 {
00222     int wkc;
00223     uint8 idx;
00224 
00225     idx = ec_getindex();
00226     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_APRD, idx, ADP, ADO, length, data);
00227     wkc = ec_srconfirm(idx, timeout);
00228     if (wkc > 0)
00229     {
00230         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00231     }
00232     ec_setbufstat(idx, EC_BUF_EMPTY);
00233 
00234     return wkc;
00235 }
00236 
00237 /** APRMW "auto increment address read, multiple write" primitive. Blocking.
00238  *
00239  * @param[in] ADP     = Address Position, each slave ++, slave that has 0 reads,
00240  *              following slaves write.
00241  * @param[in] ADO     = Address Offset, slave memory address
00242  * @param[in] length    = length of databuffer
00243  * @param[out] data     = databuffer to put slave data in
00244  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00245  * @return Workcounter or EC_NOFRAME
00246  */ 
00247 int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00248 {
00249     int wkc;
00250     uint8 idx;
00251 
00252     idx = ec_getindex();
00253     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_ARMW, idx, ADP, ADO, length, data);
00254     wkc = ec_srconfirm(idx, timeout);
00255     if (wkc > 0)
00256     {
00257         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00258     }
00259     ec_setbufstat(idx, EC_BUF_EMPTY);
00260 
00261     return wkc;
00262 }
00263 
00264 /** FPRMW "configured address read, multiple write" primitive. Blocking.
00265  *
00266  * @param[in] ADP     = Address Position, slave that has address reads,
00267  *              following slaves write.
00268  * @param[in] ADO     = Address Offset, slave memory address
00269  * @param[in] length    = length of databuffer
00270  * @param[out] data     = databuffer to put slave data in
00271  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00272  * @return Workcounter or EC_NOFRAME
00273  */ 
00274 int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00275 {
00276     int wkc;
00277     uint8 idx;
00278 
00279     idx = ec_getindex();
00280     ec_setupdatagram(&ec_txbuf[idx],EC_CMD_FRMW, idx, ADP, ADO, length, data);
00281     wkc = ec_srconfirm(idx, timeout);
00282     if (wkc > 0)
00283     {
00284         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00285     }
00286     ec_setbufstat(idx, EC_BUF_EMPTY);
00287 
00288     return wkc;
00289 }
00290 
00291 /** APRDw "auto increment address read" word return primitive. Blocking.
00292  *
00293  * @param[in] ADP     = Address Position, each slave ++, slave that has 0 reads.
00294  * @param[in] ADO     = Address Offset, slave memory address
00295  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00296  * @return word data from slave
00297  */ 
00298 uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout)
00299 {
00300     uint16 w;
00301 
00302   w = 0;
00303     ec_APRD(ADP,ADO, sizeof(w), &w, timeout);
00304 
00305     return w;
00306 }
00307 
00308 /** FPRD "configured address read" primitive. Blocking.
00309  *
00310  * @param[in] ADP     = Address Position, slave that has address reads.
00311  * @param[in] ADO     = Address Offset, slave memory address
00312  * @param[in] length    = length of databuffer
00313  * @param[out] data     = databuffer to put slave data in
00314  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00315  * @return Workcounter or EC_NOFRAME
00316  */ 
00317 int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00318 {
00319     int wkc;
00320     uint8 idx;
00321 
00322     idx = ec_getindex();
00323     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_FPRD, idx, ADP, ADO, length, data);
00324     wkc = ec_srconfirm(idx, timeout);
00325     if (wkc > 0)
00326     {
00327         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00328     }
00329     ec_setbufstat(idx, EC_BUF_EMPTY);
00330 
00331     return wkc;
00332 }
00333 
00334 /** FPRDw "configured address read" word return primitive. Blocking.
00335  *
00336  * @param[in] ADP     = Address Position, slave that has address reads.
00337  * @param[in] ADO     = Address Offset, slave memory address
00338  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00339  * @return word data from slave
00340  */ 
00341 uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int16 timeout)
00342 {
00343     uint16 w;
00344 
00345   w = 0;
00346     ec_FPRD(ADP, ADO, sizeof(w), &w, timeout);
00347     return w;
00348 }
00349 
00350 /** APWRw "auto increment address write" word primitive. Blocking.
00351  *
00352  * @param[in] ADP     = Address Position, each slave ++, slave that has 0 writes.
00353  * @param[in] ADO     = Address Offset, slave memory address
00354  * @param[in] data      = word data to write to slave.
00355  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00356  * @return Workcounter or EC_NOFRAME
00357  */ 
00358 int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
00359 {
00360     return ec_APWR(ADP,ADO, sizeof(data), &data, timeout);
00361 }
00362 
00363 /** APWR "auto increment address write" primitive. Blocking.
00364  *
00365  * @param[in] ADP     = Address Position, each slave ++, slave that has 0 writes.
00366  * @param[in] ADO     = Address Offset, slave memory address
00367  * @param[in] length    = length of databuffer
00368  * @param[in] data      = databuffer to write to slave.
00369  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00370  * @return Workcounter or EC_NOFRAME
00371  */ 
00372 int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00373 {
00374     uint8 idx;
00375   int wkc;
00376 
00377     idx = ec_getindex();
00378     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_APWR, idx, ADP, ADO, length, data);
00379     wkc=ec_srconfirm(idx, timeout);
00380     ec_setbufstat(idx, EC_BUF_EMPTY);
00381   
00382   return wkc;
00383 }
00384 
00385 /** FPWR "configured address write" primitive. Blocking.
00386  *
00387  * @param[in] ADP     = Address Position, slave that has address writes.
00388  * @param[in] ADO     = Address Offset, slave memory address
00389  * @param[in] data      = word to write to slave.
00390  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00391  * @return Workcounter or EC_NOFRAME
00392  */ 
00393 int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
00394 {
00395     return ec_FPWR(ADP,ADO, sizeof(data), &data, timeout);
00396 }
00397 
00398 /** FPWR "configured address write" primitive. Blocking.
00399  *
00400  * @param[in] ADP     = Address Position, slave that has address writes.
00401  * @param[in] ADO     = Address Offset, slave memory address
00402  * @param[in] length    = length of databuffer
00403  * @param[in] data      = databuffer to write to slave.
00404  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00405  * @return Workcounter or EC_NOFRAME
00406  */ 
00407 int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
00408 {
00409     int wkc;
00410     uint8 idx;
00411 
00412     idx = ec_getindex();
00413     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_FPWR, idx, ADP, ADO, length, data);
00414     wkc = ec_srconfirm(idx, timeout);
00415     ec_setbufstat(idx, EC_BUF_EMPTY);
00416 
00417     return wkc;
00418 }
00419 
00420 /** LRW "logical memory read / write" primitive. Blocking.
00421  *
00422  * @param[in] LogAdr    = Logical memory address
00423  * @param[in] length    = length of databuffer
00424  * @param[in,out] data    = databuffer to write to and read from slave.
00425  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00426  * @return Workcounter or EC_NOFRAME
00427  */ 
00428 int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout)
00429 {
00430     uint8 idx;
00431   int wkc;
00432 
00433     idx = ec_getindex();
00434     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
00435     wkc = ec_srconfirm(idx, timeout);
00436     if ((wkc > 0) && (ec_rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
00437     {
00438         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00439     }
00440     ec_setbufstat(idx, EC_BUF_EMPTY);
00441 
00442     return wkc;
00443 }
00444 
00445 /** LRD "logical memory read" primitive. Blocking.
00446  *
00447  * @param[in] LogAdr    = Logical memory address
00448  * @param[in] length    = length of bytes to read from slave.
00449  * @param[out] data     = databuffer to read from slave.
00450  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00451  * @return Workcounter or EC_NOFRAME
00452  */ 
00453 int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout)
00454 {
00455     uint8 idx;
00456   int wkc;
00457 
00458     idx = ec_getindex();
00459     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
00460     wkc = ec_srconfirm(idx, timeout);
00461     if ((wkc > 0) && (ec_rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD))
00462     {
00463         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00464     }
00465     ec_setbufstat(idx, EC_BUF_EMPTY);
00466 
00467     return wkc;
00468 }
00469 
00470 /** LWR "logical memory write" primitive. Blocking.
00471  *
00472  * @param[in] LogAdr    = Logical memory address
00473  * @param[in] length    = length of databuffer
00474  * @param[in] data      = databuffer to write to slave.
00475  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00476  * @return Workcounter or EC_NOFRAME
00477  */ 
00478 int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout)
00479 {
00480     uint8 idx;
00481   int wkc;
00482 
00483     idx = ec_getindex();
00484     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
00485     wkc = ec_srconfirm(idx, timeout);
00486     ec_setbufstat(idx, EC_BUF_EMPTY);
00487 
00488     return wkc;
00489 }
00490 
00491 /** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
00492  * Frame consists of two datagrams, one LRW and one FPRMW.
00493  *
00494  * @param[in] LogAdr    = Logical memory address
00495  * @param[in] length    = length of databuffer
00496  * @param[in,out] data    = databuffer to write to and read from slave.
00497  * @param[in] DCrs      = Distributed Clock reference slave address.
00498  * @param[out] DCtime   = DC time read from reference slave.
00499  * @param[in] timeout   = timeout in us, standard is EC_TIMEOUTRET
00500  * @return Workcounter or EC_NOFRAME
00501  */ 
00502 int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
00503 {
00504     uint16 DCtO;
00505     uint8 idx;
00506   int wkc;
00507   uint64 DCtE;
00508 
00509     idx = ec_getindex();
00510   /* LRW in first datagram */
00511     ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
00512   /* FPRMW in second datagram */
00513   DCtE = htoell(*DCtime);
00514     DCtO = ec_adddatagram(&ec_txbuf[idx], EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
00515     wkc = ec_srconfirm(idx, timeout);
00516     if ((wkc > 0) && (ec_rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
00517     {
00518         memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
00519     memcpy(&wkc, &ec_rxbuf[idx][EC_HEADERSIZE + length], EC_WKCSIZE);
00520     memcpy(&DCtE, &ec_rxbuf[idx][DCtO], sizeof(*DCtime));
00521     *DCtime = etohll(DCtE);
00522     }
00523     ec_setbufstat(idx, EC_BUF_EMPTY);
00524 
00525     return wkc;
00526 }
Generated by  doxygen 1.6.3