Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SOEM SPI_STMPE610 SPI_TFT_ILI9341 TFT_fonts
Revision 0:7077d8f28b3e, committed 2019-06-11
- Comitter:
- sulymarco
- Date:
- Tue Jun 11 10:19:08 2019 +0000
- Child:
- 1:665b62862f15
- Commit message:
- Added SPI speed parameter
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CONTRIBUTING.md Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,5 @@ +# Contributing to Mbed OS + +Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. + +To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SOEM/LICENSE.txt Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,34 @@ +Simple Open EtherCAT Master Library + +Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. +Copyright (C) 2005-2017 Arthur Ketels +Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven +Copyright (C) 2009-2017 rt-labs AB, Sweden + +SOEM is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License version 2 as published by the Free Software +Foundation. + +SOEM is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +As a special exception, if other files instantiate templates or use macros or +inline functions from this file, or you compile this file and link it with other +works to produce a work based on this file, this file does not by itself cause +the resulting work to be covered by the GNU General Public License. However the +source code for this file must still be made available in accordance with +section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based on this +file might be covered by the GNU General Public License. + +The EtherCAT Technology, the trade name and logo "EtherCAT" are the intellectual +property of, and protected by Beckhoff Automation GmbH. You can use SOEM for the +sole purpose of creating, using and/or selling or otherwise distributing an +EtherCAT network master provided that an EtherCAT Master License is obtained +from Beckhoff Automation GmbH. + +In case you did not receive a copy of the EtherCAT Master License along with +SOEM write to Beckhoff Automation GmbH, Eiserstrasse 5, D-33415 Verl, Germany +(www.beckhoff.com).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SOEM/SOEM/ethercat.h Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,26 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +/** \file + * \brief + * Headerfile for all ethercat headers + */ + +#ifndef _EC_ETHERCAT_H +#define _EC_ETHERCAT_H + +#include "ethercattype.h" +#include "nicdrv.h" +#include "ethercatbase.h" +#include "ethercatmain.h" +#include "ethercatdc.h" +#include "ethercatcoe.h" +#include "ethercatfoe.h" +#include "ethercatsoe.h" +#include "ethercateoe.h" +#include "ethercatconfig.h" +#include "ethercatprint.h" + +#endif /* _EC_ETHERCAT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatbase.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,637 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Base EtherCAT functions.
+ *
+ * Setting up a datagram in an ethernet frame.
+ * EtherCAT datagram primitives, broadcast, auto increment, configured and
+ * logical addressed data transfers. All base transfers are blocking, so
+ * wait for the frame to be returned to the master or timeout. If this is
+ * not acceptable build your own datagrams and use the functions from nicdrv.c.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "oshw.h"
+#include "osal.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+
+/** Write data to EtherCAT datagram.
+ *
+ * @param[out] datagramdata = data part of datagram
+ * @param[in] com = command
+ * @param[in] length = length of databuffer
+ * @param[in] data = databuffer to be copied into datagram
+ */
+static void ecx_writedatagramdata(void *datagramdata, ec_cmdtype com, uint16 length, const void * data)
+{
+ if (length > 0)
+ {
+ switch (com)
+ {
+ case EC_CMD_NOP:
+ /* Fall-through */
+ case EC_CMD_APRD:
+ /* Fall-through */
+ case EC_CMD_FPRD:
+ /* Fall-through */
+ case EC_CMD_BRD:
+ /* Fall-through */
+ case EC_CMD_LRD:
+ /* no data to write. initialise data so frame is in a known state */
+ memset(datagramdata, 0, length);
+ break;
+ default:
+ memcpy(datagramdata, data, length);
+ break;
+ }
+ }
+}
+
+/** Generate and set EtherCAT datagram in a standard ethernet frame.
+ *
+ * @param[in] port = port context struct
+ * @param[out] frame = framebuffer
+ * @param[in] com = command
+ * @param[in] idx = index used for TX and RX buffers
+ * @param[in] ADP = Address Position
+ * @param[in] ADO = Address Offset
+ * @param[in] length = length of datagram excluding EtherCAT header
+ * @param[in] data = databuffer to be copied in datagram
+ * @return always 0
+ */
+int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+ ec_comt *datagramP;
+ uint8 *frameP;
+
+ frameP = frame;
+ /* Ethernet header is preset and fixed in frame buffers
+ EtherCAT header needs to be added after that */
+ datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
+ datagramP->elength = htoes(EC_ECATTYPE + EC_HEADERSIZE + length);
+ datagramP->command = com;
+ datagramP->index = idx;
+ datagramP->ADP = htoes(ADP);
+ datagramP->ADO = htoes(ADO);
+ datagramP->dlength = htoes(length);
+ ecx_writedatagramdata(&frameP[ETH_HEADERSIZE + EC_HEADERSIZE], com, length, data);
+ /* set WKC to zero */
+ frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length] = 0x00;
+ frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length + 1] = 0x00;
+ /* set size of frame in buffer array */
+ port->txbuflength[idx] = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + length;
+
+ return 0;
+}
+
+/** Add EtherCAT datagram to a standard ethernet frame with existing datagram(s).
+ *
+ * @param[in] port = port context struct
+ * @param[out] frame = framebuffer
+ * @param[in] com = command
+ * @param[in] idx = index used for TX and RX buffers
+ * @param[in] more = TRUE if still more datagrams to follow
+ * @param[in] ADP = Address Position
+ * @param[in] ADO = Address Offset
+ * @param[in] length = length of datagram excluding EtherCAT header
+ * @param[in] data = databuffer to be copied in datagram
+ * @return Offset to data in rx frame, usefull to retrieve data after RX.
+ */
+int ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+ ec_comt *datagramP;
+ uint8 *frameP;
+ uint16 prevlength;
+
+ frameP = frame;
+ /* copy previous frame size */
+ prevlength = port->txbuflength[idx];
+ datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];
+ /* add new datagram to ethernet frame size */
+ datagramP->elength = htoes( etohs(datagramP->elength) + EC_HEADERSIZE + length );
+ /* add "datagram follows" flag to previous subframe dlength */
+ datagramP->dlength = htoes( etohs(datagramP->dlength) | EC_DATAGRAMFOLLOWS );
+ /* set new EtherCAT header position */
+ datagramP = (ec_comt*)&frameP[prevlength - EC_ELENGTHSIZE];
+ datagramP->command = com;
+ datagramP->index = idx;
+ datagramP->ADP = htoes(ADP);
+ datagramP->ADO = htoes(ADO);
+ if (more)
+ {
+ /* this is not the last datagram to add */
+ datagramP->dlength = htoes(length | EC_DATAGRAMFOLLOWS);
+ }
+ else
+ {
+ /* this is the last datagram in the frame */
+ datagramP->dlength = htoes(length);
+ }
+ ecx_writedatagramdata(&frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE], com, length, data);
+ /* set WKC to zero */
+ frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length] = 0x00;
+ frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length + 1] = 0x00;
+ /* set size of frame in buffer array */
+ port->txbuflength[idx] = prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + EC_WKCSIZE + length;
+
+ /* return offset to data in rx frame
+ 14 bytes smaller than tx frame due to stripping of ethernet header */
+ return prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE - ETH_HEADERSIZE;
+}
+
+/** BRW "broadcast write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, normally 0
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[in] data = databuffer to be written to slaves
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_BWR (ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ uint8 idx;
+ int wkc;
+
+ /* get fresh index */
+ idx = ecx_getindex (port);
+ /* setup datagram */
+ ecx_setupdatagram (port, &(port->txbuf[idx]), EC_CMD_BWR, idx, ADP, ADO, length, data);
+ /* send data and wait for answer */
+ wkc = ecx_srconfirm (port, idx, timeout);
+ /* clear buffer status */
+ ecx_setbufstat (port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** BRD "broadcast read" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, normally 0
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[out] data = databuffer to put slave data in
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_BRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ uint8 idx;
+ int wkc;
+
+ /* get fresh index */
+ idx = ecx_getindex(port);
+ /* setup datagram */
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_BRD, idx, ADP, ADO, length, data);
+ /* send data and wait for answer */
+ wkc = ecx_srconfirm (port, idx, timeout);
+ if (wkc > 0)
+ {
+ /* copy datagram to data buffer */
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ /* clear buffer status */
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** APRD "auto increment address read" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, each slave ++, slave that has 0 executes
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[out] data = databuffer to put slave data in
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ int wkc;
+ uint8 idx;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APRD, idx, ADP, ADO, length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if (wkc > 0)
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** APRMW "auto increment address read, multiple write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, each slave ++, slave that has 0 reads,
+ * following slaves write.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[out] data = databuffer to put slave data in
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ int wkc;
+ uint8 idx;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_ARMW, idx, ADP, ADO, length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if (wkc > 0)
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** FPRMW "configured address read, multiple write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, slave that has address reads,
+ * following slaves write.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[out] data = databuffer to put slave data in
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ int wkc;
+ uint8 idx;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, ADP, ADO, length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if (wkc > 0)
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** APRDw "auto increment address read" word return primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, each slave ++, slave that has 0 reads.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return word data from slave
+ */
+uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
+{
+ uint16 w;
+
+ w = 0;
+ ecx_APRD(port, ADP, ADO, sizeof(w), &w, timeout);
+
+ return w;
+}
+
+/** FPRD "configured address read" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, slave that has address reads.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[out] data = databuffer to put slave data in
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ int wkc;
+ uint8 idx;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, ADP, ADO, length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if (wkc > 0)
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** FPRDw "configured address read" word return primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, slave that has address reads.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return word data from slave
+ */
+uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
+{
+ uint16 w;
+
+ w = 0;
+ ecx_FPRD(port, ADP, ADO, sizeof(w), &w, timeout);
+ return w;
+}
+
+/** APWR "auto increment address write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, each slave ++, slave that has 0 writes.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[in] data = databuffer to write to slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ uint8 idx;
+ int wkc;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APWR, idx, ADP, ADO, length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** APWRw "auto increment address write" word primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, each slave ++, slave that has 0 writes.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] data = word data to write to slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+ return ecx_APWR(port, ADP, ADO, sizeof(data), &data, timeout);
+}
+
+/** FPWR "configured address write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, slave that has address writes.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] length = length of databuffer
+ * @param[in] data = databuffer to write to slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ int wkc;
+ uint8 idx;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPWR, idx, ADP, ADO, length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** FPWR "configured address write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] ADP = Address Position, slave that has address writes.
+ * @param[in] ADO = Address Offset, slave memory address
+ * @param[in] data = word to write to slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+ return ecx_FPWR(port, ADP, ADO, sizeof(data), &data, timeout);
+}
+
+/** LRW "logical memory read / write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] LogAdr = Logical memory address
+ * @param[in] length = length of databuffer
+ * @param[in,out] data = databuffer to write to and read from slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+ uint8 idx;
+ int wkc;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** LRD "logical memory read" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] LogAdr = Logical memory address
+ * @param[in] length = length of bytes to read from slave.
+ * @param[out] data = databuffer to read from slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+ uint8 idx;
+ int wkc;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD))
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** LWR "logical memory write" primitive. Blocking.
+ *
+ * @param[in] port = port context struct
+ * @param[in] LogAdr = Logical memory address
+ * @param[in] length = length of databuffer
+ * @param[in] data = databuffer to write to slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+ uint8 idx;
+ int wkc;
+
+ idx = ecx_getindex(port);
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+/** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
+ * Frame consists of two datagrams, one LRW and one FPRMW.
+ *
+ * @param[in] port = port context struct
+ * @param[in] LogAdr = Logical memory address
+ * @param[in] length = length of databuffer
+ * @param[in,out] data = databuffer to write to and read from slave.
+ * @param[in] DCrs = Distributed Clock reference slave address.
+ * @param[out] DCtime = DC time read from reference slave.
+ * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
+{
+ uint16 DCtO;
+ uint8 idx;
+ int wkc;
+ uint64 DCtE;
+
+ idx = ecx_getindex(port);
+ /* LRW in first datagram */
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
+ /* FPRMW in second datagram */
+ DCtE = htoell(*DCtime);
+ DCtO = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
+ {
+ memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);
+ memcpy(&wkc, &(port->rxbuf[idx][EC_HEADERSIZE + length]), EC_WKCSIZE);
+ memcpy(&DCtE, &(port->rxbuf[idx][DCtO]), sizeof(*DCtime));
+ *DCtime = etohll(DCtE);
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+
+ return wkc;
+}
+
+#ifdef EC_VER1
+int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+ return ecx_setupdatagram (&ecx_port, frame, com, idx, ADP, ADO, length, data);
+}
+
+int ec_adddatagram (void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
+{
+ return ecx_adddatagram (&ecx_port, frame, com, idx, more, ADP, ADO, length, data);
+}
+
+int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_BWR (&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_BRD(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_APRD(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_ARMW(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_FRMW(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout)
+{
+ uint16 w;
+
+ w = 0;
+ ec_APRD(ADP, ADO, sizeof(w), &w, timeout);
+
+ return w;
+}
+
+int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_FPRD(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout)
+{
+ uint16 w;
+
+ w = 0;
+ ec_FPRD(ADP, ADO, sizeof(w), &w, timeout);
+ return w;
+}
+
+int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_APWR(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+ return ec_APWR(ADP, ADO, sizeof(data), &data, timeout);
+}
+
+int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
+{
+ return ecx_FPWR(&ecx_port, ADP, ADO, length, data, timeout);
+}
+
+int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
+{
+ return ec_FPWR(ADP, ADO, sizeof(data), &data, timeout);
+}
+
+int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+ return ecx_LRW(&ecx_port, LogAdr, length, data, timeout);
+}
+
+int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+ return ecx_LRD(&ecx_port, LogAdr, length, data, timeout);
+}
+
+int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout)
+{
+ return ecx_LWR(&ecx_port, LogAdr, length, data, timeout);
+}
+
+int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
+{
+ return ecx_LRWDC(&ecx_port, LogAdr, length, data, DCrs, DCtime, timeout);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatbase.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,63 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatbase.c
+ */
+
+#ifndef _ethercatbase_
+#define _ethercatbase_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ecx_BWR(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ecx_BRD(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout);
+int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout);
+int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
+int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
+int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout);
+int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout);
+
+#ifdef EC_VER1
+int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ec_adddatagram(void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data);
+int ec_BWR(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ec_BRD(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout);
+int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout);
+int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout);
+int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout);
+int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout);
+int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout);
+int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout);
+int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout);
+int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatcoe.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1505 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * CAN over EtherCAT (CoE) module.
+ *
+ * SDO read / write and SDO service functions
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatcoe.h"
+
+/** SDO structure, not to be confused with EcSDOserviceT */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert MbxHeader;
+ uint16 CANOpen;
+ uint8 Command;
+ uint16 Index;
+ uint8 SubIndex;
+ union
+ {
+ uint8 bdata[0x200]; /* variants for easy data access */
+ uint16 wdata[0x100];
+ uint32 ldata[0x80];
+ };
+} ec_SDOt;
+PACKED_END
+
+/** SDO service structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert MbxHeader;
+ uint16 CANOpen;
+ uint8 Opcode;
+ uint8 Reserved;
+ uint16 Fragments;
+ union
+ {
+ uint8 bdata[0x200]; /* variants for easy data access */
+ uint16 wdata[0x100];
+ uint32 ldata[0x80];
+ };
+} ec_SDOservicet;
+PACKED_END
+
+/** Report SDO error.
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Index = Index that generated error
+ * @param[in] SubIdx = Subindex that generated error
+ * @param[in] AbortCode = Abortcode, see EtherCAT documentation for list
+ */
+void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
+{
+ ec_errort Ec;
+
+ memset(&Ec, 0, sizeof(Ec));
+ Ec.Time = osal_current_time();
+ Ec.Slave = Slave;
+ Ec.Index = Index;
+ Ec.SubIdx = SubIdx;
+ *(context->ecaterror) = TRUE;
+ Ec.Etype = EC_ERR_TYPE_SDO_ERROR;
+ Ec.AbortCode = AbortCode;
+ ecx_pusherror(context, &Ec);
+}
+
+/** Report SDO info error
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Index = Index that generated error
+ * @param[in] SubIdx = Subindex that generated error
+ * @param[in] AbortCode = Abortcode, see EtherCAT documentation for list
+ */
+static void ecx_SDOinfoerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
+{
+ ec_errort Ec;
+
+ memset(&Ec, 0, sizeof(Ec));
+ Ec.Slave = Slave;
+ Ec.Index = Index;
+ Ec.SubIdx = SubIdx;
+ *(context->ecaterror) = TRUE;
+ Ec.Etype = EC_ERR_TYPE_SDOINFO_ERROR;
+ Ec.AbortCode = AbortCode;
+ ecx_pusherror(context, &Ec);
+}
+
+/** CoE SDO read, blocking. Single subindex or Complete Access.
+ *
+ * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes
+ * then a "expedited" response is returned, otherwise a "normal" response. If a "normal"
+ * response is larger than the mailbox size then the response is segmented. The function
+ * will combine all segments and copy them to the parameter buffer.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] index = Index to read
+ * @param[in] subindex = Subindex to read, must be 0 or 1 if CA is used.
+ * @param[in] CA = FALSE = single subindex. TRUE = Complete Access, all subindexes read.
+ * @param[in,out] psize = Size in bytes of parameter buffer, returns bytes read from SDO.
+ * @param[out] p = Pointer to parameter buffer
+ * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex,
+ boolean CA, int *psize, void *p, int timeout)
+{
+ ec_SDOt *SDOp, *aSDOp;
+ uint16 bytesize, Framedatasize;
+ int wkc;
+ int32 SDOlen;
+ uint8 *bp;
+ uint8 *hp;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt, toggle;
+ boolean NotLast;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSDOp = (ec_SDOt *)&MbxIn;
+ SDOp = (ec_SDOt *)&MbxOut;
+ SDOp->MbxHeader.length = htoes(0x000a);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
+ if (CA)
+ {
+ SDOp->Command = ECT_SDO_UP_REQ_CA; /* upload request complete access */
+ }
+ else
+ {
+ SDOp->Command = ECT_SDO_UP_REQ; /* upload request normal */
+ }
+ SDOp->Index = htoes(index);
+ if (CA && (subindex > 1))
+ {
+ subindex = 1;
+ }
+ SDOp->SubIndex = subindex;
+ SDOp->ldata[0] = 0;
+ /* send CoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be CoE, SDO response and the correct index */
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+ (aSDOp->Index == SDOp->Index))
+ {
+ if ((aSDOp->Command & 0x02) > 0)
+ {
+ /* expedited frame response */
+ bytesize = 4 - ((aSDOp->Command >> 2) & 0x03);
+ if (*psize >= bytesize) /* parameter buffer big enough ? */
+ {
+ /* copy parameter in parameter buffer */
+ memcpy(p, &aSDOp->ldata[0], bytesize);
+ /* return the real parameter size */
+ *psize = bytesize;
+ }
+ else
+ {
+ wkc = 0;
+ ecx_packeterror(context, slave, index, subindex, 3); /* data container too small for type */
+ }
+ }
+ else
+ { /* normal frame response */
+ SDOlen = etohl(aSDOp->ldata[0]);
+ /* Does parameter fit in parameter buffer ? */
+ if (SDOlen <= *psize)
+ {
+ bp = p;
+ hp = p;
+ /* calculate mailbox transfer size */
+ Framedatasize = (etohs(aSDOp->MbxHeader.length) - 10);
+ if (Framedatasize < SDOlen) /* transfer in segments? */
+ {
+ /* copy parameter data in parameter buffer */
+ memcpy(hp, &aSDOp->ldata[1], Framedatasize);
+ /* increment buffer pointer */
+ hp += Framedatasize;
+ *psize = Framedatasize;
+ NotLast = TRUE;
+ toggle= 0x00;
+ while (NotLast) /* segmented transfer */
+ {
+ SDOp = (ec_SDOt *)&MbxOut;
+ SDOp->MbxHeader.length = htoes(0x000a);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
+ SDOp->Command = ECT_SDO_SEG_UP_REQ + toggle; /* segment upload request */
+ SDOp->Index = htoes(index);
+ SDOp->SubIndex = subindex;
+ SDOp->ldata[0] = 0;
+ /* send segmented upload request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ /* is mailbox transferred to slave ? */
+ if (wkc > 0)
+ {
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ /* has slave responded ? */
+ if (wkc > 0)
+ {
+ /* slave response should be CoE, SDO response */
+ if ((((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+ ((aSDOp->Command & 0xe0) == 0x00)))
+ {
+ /* calculate mailbox transfer size */
+ Framedatasize = etohs(aSDOp->MbxHeader.length) - 3;
+ if ((aSDOp->Command & 0x01) > 0)
+ { /* last segment */
+ NotLast = FALSE;
+ if (Framedatasize == 7)
+ /* subtract unused bytes from frame */
+ Framedatasize = Framedatasize - ((aSDOp->Command & 0x0e) >> 1);
+ /* copy to parameter buffer */
+ memcpy(hp, &(aSDOp->Index), Framedatasize);
+ }
+ else /* segments follow */
+ {
+ /* copy to parameter buffer */
+ memcpy(hp, &(aSDOp->Index), Framedatasize);
+ /* increment buffer pointer */
+ hp += Framedatasize;
+ }
+ /* update parameter size */
+ *psize += Framedatasize;
+ }
+ /* unexpected frame returned from slave */
+ else
+ {
+ NotLast = FALSE;
+ if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
+ ecx_SDOerror(context, slave, index, subindex, etohl(aSDOp->ldata[0]));
+ else
+ ecx_packeterror(context, slave, index, subindex, 1); /* Unexpected frame returned */
+ wkc = 0;
+ }
+ }
+ }
+ toggle = toggle ^ 0x10; /* toggle bit for segment request */
+ }
+ }
+ /* non segmented transfer */
+ else
+ {
+ /* copy to parameter buffer */
+ memcpy(bp, &aSDOp->ldata[1], SDOlen);
+ *psize = SDOlen;
+ }
+ }
+ /* parameter buffer too small */
+ else
+ {
+ wkc = 0;
+ ecx_packeterror(context, slave, index, subindex, 3); /* data container too small for type */
+ }
+ }
+ }
+ /* other slave response */
+ else
+ {
+ if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
+ {
+ ecx_SDOerror(context, slave, index, subindex, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, slave, index, subindex, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ }
+ return wkc;
+}
+
+/** CoE SDO write, blocking. Single subindex or Complete Access.
+ *
+ * A "normal" download request is issued, unless we have
+ * small data, then a "expedited" transfer is used. If the parameter is larger than
+ * the mailbox size then the download is segmented. The function will split the
+ * parameter data in segments and send them to the slave one by one.
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Index = Index to write
+ * @param[in] SubIndex = Subindex to write, must be 0 or 1 if CA is used.
+ * @param[in] CA = FALSE = single subindex. TRUE = Complete Access, all subindexes written.
+ * @param[in] psize = Size in bytes of parameter buffer.
+ * @param[out] p = Pointer to parameter buffer
+ * @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex,
+ boolean CA, int psize, void *p, int Timeout)
+{
+ ec_SDOt *SDOp, *aSDOp;
+ int wkc, maxdata;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt, toggle;
+ uint16 framedatasize;
+ boolean NotLast;
+ uint8 *hp;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSDOp = (ec_SDOt *)&MbxIn;
+ SDOp = (ec_SDOt *)&MbxOut;
+ maxdata = context->slavelist[Slave].mbx_l - 0x10; /* data section=mailbox size - 6 mbx - 2 CoE - 8 sdo req */
+ /* if small data use expedited transfer */
+ if ((psize <= 4) && !CA)
+ {
+ SDOp->MbxHeader.length = htoes(0x000a);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* get new mailbox counter, used for session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits */
+ SDOp->Command = ECT_SDO_DOWN_EXP | (((4 - psize) << 2) & 0x0c); /* expedited SDO download transfer */
+ SDOp->Index = htoes(Index);
+ SDOp->SubIndex = SubIndex;
+ hp = p;
+ /* copy parameter data to mailbox */
+ memcpy(&SDOp->ldata[0], hp, psize);
+ /* send mailbox SDO download request to slave */
+ wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0)
+ {
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
+ if (wkc > 0)
+ {
+ /* response should be CoE, SDO response, correct index and subindex */
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+ (aSDOp->Index == SDOp->Index) &&
+ (aSDOp->SubIndex == SDOp->SubIndex))
+ {
+ /* all OK */
+ }
+ /* unexpected response from slave */
+ else
+ {
+ if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
+ {
+ ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ framedatasize = psize;
+ NotLast = FALSE;
+ if (framedatasize > maxdata)
+ {
+ framedatasize = maxdata; /* segmented transfer needed */
+ NotLast = TRUE;
+ }
+ SDOp->MbxHeader.length = htoes(0x0a + framedatasize);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* get new mailbox counter, used for session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits */
+ if (CA)
+ {
+ SDOp->Command = ECT_SDO_DOWN_INIT_CA; /* Complete Access, normal SDO init download transfer */
+ }
+ else
+ {
+ SDOp->Command = ECT_SDO_DOWN_INIT; /* normal SDO init download transfer */
+ }
+ SDOp->Index = htoes(Index);
+ SDOp->SubIndex = SubIndex;
+ if (CA && (SubIndex > 1))
+ {
+ SDOp->SubIndex = 1;
+ }
+ SDOp->ldata[0] = htoel(psize);
+ hp = p;
+ /* copy parameter data to mailbox */
+ memcpy(&SDOp->ldata[1], hp, framedatasize);
+ hp += framedatasize;
+ psize -= framedatasize;
+ /* send mailbox SDO download request to slave */
+ wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0)
+ {
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
+ if (wkc > 0)
+ {
+ /* response should be CoE, SDO response, correct index and subindex */
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+ (aSDOp->Index == SDOp->Index) &&
+ (aSDOp->SubIndex == SDOp->SubIndex))
+ {
+ /* all ok */
+ maxdata += 7;
+ toggle = 0;
+ /* repeat while segments left */
+ while (NotLast)
+ {
+ SDOp = (ec_SDOt *)&MbxOut;
+ framedatasize = psize;
+ NotLast = FALSE;
+ SDOp->Command = 0x01; /* last segment */
+ if (framedatasize > maxdata)
+ {
+ framedatasize = maxdata; /* more segments needed */
+ NotLast = TRUE;
+ SDOp->Command = 0x00; /* segments follow */
+ }
+ if (!NotLast && (framedatasize < 7))
+ {
+ SDOp->MbxHeader.length = htoes(0x0a); /* minimum size */
+ SDOp->Command = 0x01 + ((7 - framedatasize) << 1); /* last segment reduced octets */
+ }
+ else
+ {
+ SDOp->MbxHeader.length = htoes(framedatasize + 3); /* data + 2 CoE + 1 SDO */
+ }
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* get new mailbox counter value */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
+ SDOp->Command = SDOp->Command + toggle; /* add toggle bit to command byte */
+ /* copy parameter data to mailbox */
+ memcpy(&SDOp->Index, hp, framedatasize);
+ /* update parameter buffer pointer */
+ hp += framedatasize;
+ psize -= framedatasize;
+ /* send SDO download request */
+ wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0)
+ {
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
+ if (wkc > 0)
+ {
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
+ ((aSDOp->Command & 0xe0) == 0x20))
+ {
+ /* all OK, nothing to do */
+ }
+ else
+ {
+ if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
+ {
+ ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ NotLast = FALSE;
+ }
+ }
+ }
+ toggle = toggle ^ 0x10; /* toggle bit for segment request */
+ }
+ }
+ /* unexpected response from slave */
+ else
+ {
+ if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
+ {
+ ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ }
+ }
+
+ return wkc;
+}
+
+/** CoE RxPDO write, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] RxPDOnumber = Related RxPDO number
+ * @param[in] psize = Size in bytes of PDO buffer.
+ * @param[out] p = Pointer to PDO buffer
+ * @return Workcounter from last slave response
+ */
+int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber, int psize, void *p)
+{
+ ec_SDOt *SDOp;
+ int wkc, maxdata;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+ uint16 framedatasize;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ SDOp = (ec_SDOt *)&MbxOut;
+ maxdata = context->slavelist[Slave].mbx_l - 0x08; /* data section=mailbox size - 6 mbx - 2 CoE */
+ framedatasize = psize;
+ if (framedatasize > maxdata)
+ {
+ framedatasize = maxdata; /* limit transfer */
+ }
+ SDOp->MbxHeader.length = htoes(0x02 + framedatasize);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* get new mailbox counter, used for session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes((RxPDOnumber & 0x01ff) + (ECT_COES_RXPDO << 12)); /* number 9bits service upper 4 bits */
+ /* copy PDO data to mailbox */
+ memcpy(&SDOp->Command, p, framedatasize);
+ /* send mailbox RxPDO request to slave */
+ wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+
+ return wkc;
+}
+
+/** CoE TxPDO read remote request, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] TxPDOnumber = Related TxPDO number
+ * @param[in,out] psize = Size in bytes of PDO buffer, returns bytes read from PDO.
+ * @param[out] p = Pointer to PDO buffer
+ * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout)
+{
+ ec_SDOt *SDOp, *aSDOp;
+ int wkc;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+ uint16 framedatasize;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSDOp = (ec_SDOt *)&MbxIn;
+ SDOp = (ec_SDOt *)&MbxOut;
+ SDOp->MbxHeader.length = htoes(0x02);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* get new mailbox counter, used for session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes((TxPDOnumber & 0x01ff) + (ECT_COES_TXPDO_RR << 12)); /* number 9bits service upper 4 bits */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0)
+ {
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be CoE, TxPDO */
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_TXPDO))
+ {
+ /* TxPDO response */
+ framedatasize = (aSDOp->MbxHeader.length - 2);
+ if (*psize >= framedatasize) /* parameter buffer big enough ? */
+ {
+ /* copy parameter in parameter buffer */
+ memcpy(p, &aSDOp->Command, framedatasize);
+ /* return the real parameter size */
+ *psize = framedatasize;
+ }
+ /* parameter buffer too small */
+ else
+ {
+ wkc = 0;
+ ecx_packeterror(context, slave, 0, 0, 3); /* data container too small for type */
+ }
+ }
+ /* other slave response */
+ else
+ {
+ if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
+ {
+ ecx_SDOerror(context, slave, 0, 0, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, slave, 0, 0, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ }
+
+ return wkc;
+}
+
+/** Read PDO assign structure
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] PDOassign = PDO assign object
+ * @return total bitlength of PDO assign
+ */
+int ecx_readPDOassign(ecx_contextt *context, uint16 Slave, uint16 PDOassign)
+{
+ uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
+ uint8 subcnt;
+ int wkc, bsize = 0, rdl;
+ int32 rdat2;
+
+ rdl = sizeof(rdat); rdat = 0;
+ /* read PDO assign subindex 0 ( = number of PDO's) */
+ wkc = ecx_SDOread(context, Slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+ rdat = etohs(rdat);
+ /* positive result from slave ? */
+ if ((wkc > 0) && (rdat > 0))
+ {
+ /* number of available sub indexes */
+ nidx = rdat;
+ bsize = 0;
+ /* read all PDO's */
+ for (idxloop = 1; idxloop <= nidx; idxloop++)
+ {
+ rdl = sizeof(rdat); rdat = 0;
+ /* read PDO assign */
+ wkc = ecx_SDOread(context, Slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+ /* result is index of PDO */
+ idx = etohs(rdat);
+ if (idx > 0)
+ {
+ rdl = sizeof(subcnt); subcnt = 0;
+ /* read number of subindexes of PDO */
+ wkc = ecx_SDOread(context, Slave,idx, 0x00, FALSE, &rdl, &subcnt, EC_TIMEOUTRXM);
+ subidx = subcnt;
+ /* for each subindex */
+ for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
+ {
+ rdl = sizeof(rdat2); rdat2 = 0;
+ /* read SDO that is mapped in PDO */
+ wkc = ecx_SDOread(context, Slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
+ rdat2 = etohl(rdat2);
+ /* extract bitlength of SDO */
+ if (LO_BYTE(rdat2) < 0xff)
+ {
+ bsize += LO_BYTE(rdat2);
+ }
+ else
+ {
+ rdl = sizeof(rdat); rdat = htoes(0xff);
+ /* read Object Entry in Object database */
+// wkc = ec_readOEsingle(idx, (uint8)SubCount, pODlist, pOElist);
+ bsize += etohs(rdat);
+ }
+ }
+ }
+ }
+ }
+ /* return total found bitlength (PDO) */
+ return bsize;
+}
+
+/** Read PDO assign structure in Complete Access mode
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Thread_n = Calling thread index
+ * @param[in] PDOassign = PDO assign object
+ * @return total bitlength of PDO assign
+ */
+int ecx_readPDOassignCA(ecx_contextt *context, uint16 Slave, int Thread_n,
+ uint16 PDOassign)
+{
+ uint16 idxloop, nidx, subidxloop, idx, subidx;
+ int wkc, bsize = 0, rdl;
+
+ /* find maximum size of PDOassign buffer */
+ rdl = sizeof(ec_PDOassignt);
+ context->PDOassign[Thread_n].n=0;
+ /* read rxPDOassign in CA mode, all subindexes are read in one struct */
+ wkc = ecx_SDOread(context, Slave, PDOassign, 0x00, TRUE, &rdl,
+ &(context->PDOassign[Thread_n]), EC_TIMEOUTRXM);
+ /* positive result from slave ? */
+ if ((wkc > 0) && (context->PDOassign[Thread_n].n > 0))
+ {
+ nidx = context->PDOassign[Thread_n].n;
+ bsize = 0;
+ /* for each PDO do */
+ for (idxloop = 1; idxloop <= nidx; idxloop++)
+ {
+ /* get index from PDOassign struct */
+ idx = etohs(context->PDOassign[Thread_n].index[idxloop - 1]);
+ if (idx > 0)
+ {
+ rdl = sizeof(ec_PDOdesct); context->PDOdesc[Thread_n].n = 0;
+ /* read SDO's that are mapped in PDO, CA mode */
+ wkc = ecx_SDOread(context, Slave,idx, 0x00, TRUE, &rdl,
+ &(context->PDOdesc[Thread_n]), EC_TIMEOUTRXM);
+ subidx = context->PDOdesc[Thread_n].n;
+ /* extract all bitlengths of SDO's */
+ for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
+ {
+ bsize += LO_BYTE(etohl(context->PDOdesc[Thread_n].PDO[subidxloop -1]));
+ }
+ }
+ }
+ }
+
+ /* return total found bitlength (PDO) */
+ return bsize;
+}
+
+/** CoE read PDO mapping.
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave.
+ *
+ * Principal structure in slave:\n
+ * 1C00:00 is number of SM defined\n
+ * 1C00:01 SM0 type -> 1C10\n
+ * 1C00:02 SM1 type -> 1C11\n
+ * 1C00:03 SM2 type -> 1C12\n
+ * 1C00:04 SM3 type -> 1C13\n
+ * Type 0 = unused, 1 = mailbox in, 2 = mailbox out,
+ * 3 = outputs (RxPDO), 4 = inputs (TxPDO).
+ *
+ * 1C12:00 is number of PDO's defined for SM2\n
+ * 1C12:01 PDO assign SDO #1 -> f.e. 1A00\n
+ * 1C12:02 PDO assign SDO #2 -> f.e. 1A04\
+ *
+ * 1A00:00 is number of object defined for this PDO\n
+ * 1A00:01 object mapping #1, f.e. 60100710 (SDO 6010 SI 07 bitlength 0x10)
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[out] Osize = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping successful.
+ */
+int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, int *Osize, int *Isize)
+{
+ int wkc, rdl;
+ int retVal = 0;
+ uint8 nSM, iSM, tSM;
+ int Tsize;
+ uint8 SMt_bug_add;
+
+ *Isize = 0;
+ *Osize = 0;
+ SMt_bug_add = 0;
+ rdl = sizeof(nSM); nSM = 0;
+ /* read SyncManager Communication Type object count */
+ wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
+ /* positive result from slave ? */
+ if ((wkc > 0) && (nSM > 2))
+ {
+ /* limit to maximum number of SM defined, if true the slave can't be configured */
+ if (nSM > EC_MAXSM)
+ nSM = EC_MAXSM;
+ /* iterate for every SM type defined */
+ for (iSM = 2 ; iSM < nSM ; iSM++)
+ {
+ rdl = sizeof(tSM); tSM = 0;
+ /* read SyncManager Communication Type */
+ wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
+ if (wkc > 0)
+ {
+// start slave bug prevention code, remove if possible
+ if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
+ {
+ SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
+ }
+ if(tSM)
+ {
+ tSM += SMt_bug_add; // only add if SMt > 0
+ }
+ if((iSM == 2) && (tSM == 0)) // SM2 has type 0, this is a bug in the slave!
+ {
+ tSM = 3;
+ }
+ if((iSM == 3) && (tSM == 0)) // SM3 has type 0, this is a bug in the slave!
+ {
+ tSM = 4;
+ }
+// end slave bug prevention code
+
+ context->slavelist[Slave].SMtype[iSM] = tSM;
+ /* check if SM is unused -> clear enable flag */
+ if (tSM == 0)
+ {
+ context->slavelist[Slave].SM[iSM].SMflags =
+ htoel( etohl(context->slavelist[Slave].SM[iSM].SMflags) & EC_SMENABLEMASK);
+ }
+ if ((tSM == 3) || (tSM == 4))
+ {
+ /* read the assign PDO */
+ Tsize = ecx_readPDOassign(context, Slave, ECT_SDO_PDOASSIGN + iSM );
+ /* if a mapping is found */
+ if (Tsize)
+ {
+ context->slavelist[Slave].SM[iSM].SMlength = htoes((Tsize + 7) / 8);
+ if (tSM == 3)
+ {
+ /* we are doing outputs */
+ *Osize += Tsize;
+ }
+ else
+ {
+ /* we are doing inputs */
+ *Isize += Tsize;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* found some I/O bits ? */
+ if ((*Isize > 0) || (*Osize > 0))
+ {
+ retVal = 1;
+ }
+
+ return retVal;
+}
+
+/** CoE read PDO mapping in Complete Access mode (CA).
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave. Slave has to support CA, otherwise use ec_readPDOmap().
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Thread_n = Calling thread index
+ * @param[out] Osize = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping successful.
+ */
+int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, int *Osize, int *Isize)
+{
+ int wkc, rdl;
+ int retVal = 0;
+ uint8 nSM, iSM, tSM;
+ int Tsize;
+ uint8 SMt_bug_add;
+
+ *Isize = 0;
+ *Osize = 0;
+ SMt_bug_add = 0;
+ rdl = sizeof(ec_SMcommtypet);
+ context->SMcommtype[Thread_n].n = 0;
+ /* read SyncManager Communication Type object count Complete Access*/
+ wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, 0x00, TRUE, &rdl,
+ &(context->SMcommtype[Thread_n]), EC_TIMEOUTRXM);
+ /* positive result from slave ? */
+ if ((wkc > 0) && (context->SMcommtype[Thread_n].n > 2))
+ {
+ nSM = context->SMcommtype[Thread_n].n;
+ /* limit to maximum number of SM defined, if true the slave can't be configured */
+ if (nSM > EC_MAXSM)
+ {
+ nSM = EC_MAXSM;
+ ecx_packeterror(context, Slave, 0, 0, 10); /* #SM larger than EC_MAXSM */
+ }
+ /* iterate for every SM type defined */
+ for (iSM = 2 ; iSM < nSM ; iSM++)
+ {
+ tSM = context->SMcommtype[Thread_n].SMtype[iSM];
+
+// start slave bug prevention code, remove if possible
+ if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
+ {
+ SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
+ }
+ if(tSM)
+ {
+ tSM += SMt_bug_add; // only add if SMt > 0
+ }
+// end slave bug prevention code
+
+ context->slavelist[Slave].SMtype[iSM] = tSM;
+ /* check if SM is unused -> clear enable flag */
+ if (tSM == 0)
+ {
+ context->slavelist[Slave].SM[iSM].SMflags =
+ htoel( etohl(context->slavelist[Slave].SM[iSM].SMflags) & EC_SMENABLEMASK);
+ }
+ if ((tSM == 3) || (tSM == 4))
+ {
+ /* read the assign PDO */
+ Tsize = ecx_readPDOassignCA(context, Slave, Thread_n,
+ ECT_SDO_PDOASSIGN + iSM );
+ /* if a mapping is found */
+ if (Tsize)
+ {
+ context->slavelist[Slave].SM[iSM].SMlength = htoes((Tsize + 7) / 8);
+ if (tSM == 3)
+ {
+ /* we are doing outputs */
+ *Osize += Tsize;
+ }
+ else
+ {
+ /* we are doing inputs */
+ *Isize += Tsize;
+ }
+ }
+ }
+ }
+ }
+
+ /* found some I/O bits ? */
+ if ((*Isize > 0) || (*Osize > 0))
+ {
+ retVal = 1;
+ }
+ return retVal;
+}
+
+/** CoE read Object Description List.
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number.
+ * @param[out] pODlist = resulting Object Description list.
+ * @return Workcounter of slave response.
+ */
+int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist)
+{
+ ec_SDOservicet *SDOp, *aSDOp;
+ ec_mbxbuft MbxIn, MbxOut;
+ int wkc;
+ uint16 x, n, i, sp, offset;
+ boolean stop;
+ uint8 cnt;
+ boolean First;
+
+ pODlist->Slave = Slave;
+ pODlist->Entries = 0;
+ ec_clearmbx(&MbxIn);
+ /* clear pending out mailbox in slave if available. Timeout is set to 0 */
+ wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSDOp = (ec_SDOservicet*)&MbxIn;
+ SDOp = (ec_SDOservicet*)&MbxOut;
+ SDOp->MbxHeader.length = htoes(0x0008);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* Get new mailbox counter value */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
+ SDOp->Opcode = ECT_GET_ODLIST_REQ; /* get object description list request */
+ SDOp->Reserved = 0;
+ SDOp->Fragments = 0; /* fragments left */
+ SDOp->wdata[0] = htoes(0x01); /* all objects */
+ /* send get object description list request to slave */
+ wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
+ /* mailbox placed in slave ? */
+ if (wkc > 0)
+ {
+ x = 0;
+ sp = 0;
+ First = TRUE;
+ offset = 1; /* offset to skip info header in first frame, otherwise set to 0 */
+ do
+ {
+ stop = TRUE; /* assume this is last iteration */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
+ /* got response ? */
+ if (wkc > 0)
+ {
+ /* response should be CoE and "get object description list response" */
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((aSDOp->Opcode & 0x7f) == ECT_GET_ODLIST_RES))
+ {
+ if (First)
+ {
+ /* extract number of indexes from mailbox data size */
+ n = (etohs(aSDOp->MbxHeader.length) - (6 + 2)) / 2;
+ }
+ else
+ {
+ /* extract number of indexes from mailbox data size */
+ n = (etohs(aSDOp->MbxHeader.length) - 6) / 2;
+ }
+ /* check if indexes fit in buffer structure */
+ if ((sp + n) > EC_MAXODLIST)
+ {
+ n = EC_MAXODLIST + 1 - sp;
+ ecx_SDOinfoerror(context, Slave, 0, 0, 0xf000000); /* Too many entries for master buffer */
+ stop = TRUE;
+ }
+ /* trim to maximum number of ODlist entries defined */
+ if ((pODlist->Entries + n) > EC_MAXODLIST)
+ {
+ n = EC_MAXODLIST - pODlist->Entries;
+ }
+ pODlist->Entries += n;
+ /* extract indexes one by one */
+ for (i = 0; i < n; i++)
+ {
+ pODlist->Index[sp + i] = etohs(aSDOp->wdata[i + offset]);
+ }
+ sp += n;
+ /* check if more fragments will follow */
+ if (aSDOp->Fragments > 0)
+ {
+ stop = FALSE;
+ }
+ First = FALSE;
+ offset = 0;
+ }
+ /* got unexpected response from slave */
+ else
+ {
+ if ((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR) /* SDO info error received */
+ {
+ ecx_SDOinfoerror(context, Slave, 0, 0, etohl(aSDOp->ldata[0]));
+ stop = TRUE;
+ }
+ else
+ {
+ ecx_packeterror(context, Slave, 0, 0, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ x += 20;
+ }
+ }
+ x++;
+ }
+ while ((x <= 128) && !stop);
+ }
+ return wkc;
+}
+
+/** CoE read Object Description. Adds textual description to object indexes.
+ *
+ * @param[in] context = context struct
+ * @param[in] Item = Item number in ODlist.
+ * @param[in,out] pODlist = referencing Object Description list.
+ * @return Workcounter of slave response.
+ */
+int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist)
+{
+ ec_SDOservicet *SDOp, *aSDOp;
+ int wkc;
+ uint16 n, Slave;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+
+ Slave = pODlist->Slave;
+ pODlist->DataType[Item] = 0;
+ pODlist->ObjectCode[Item] = 0;
+ pODlist->MaxSub[Item] = 0;
+ pODlist->Name[Item][0] = 0;
+ ec_clearmbx(&MbxIn);
+ /* clear pending out mailbox in slave if available. Timeout is set to 0 */
+ wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSDOp = (ec_SDOservicet*)&MbxIn;
+ SDOp = (ec_SDOservicet*)&MbxOut;
+ SDOp->MbxHeader.length = htoes(0x0008);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* Get new mailbox counter value */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
+ SDOp->Opcode = ECT_GET_OD_REQ; /* get object description request */
+ SDOp->Reserved = 0;
+ SDOp->Fragments = 0; /* fragments left */
+ SDOp->wdata[0] = htoes(pODlist->Index[Item]); /* Data of Index */
+ /* send get object description request to slave */
+ wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
+ /* mailbox placed in slave ? */
+ if (wkc > 0)
+ {
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
+ /* got response ? */
+ if (wkc > 0)
+ {
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((aSDOp->Opcode & 0x7f) == ECT_GET_OD_RES))
+ {
+ n = (etohs(aSDOp->MbxHeader.length) - 12); /* length of string(name of object) */
+ if (n > EC_MAXNAME)
+ {
+ n = EC_MAXNAME; /* max chars */
+ }
+ pODlist->DataType[Item] = etohs(aSDOp->wdata[1]);
+ pODlist->ObjectCode[Item] = aSDOp->bdata[5];
+ pODlist->MaxSub[Item] = aSDOp->bdata[4];
+
+ strncpy(pODlist->Name[Item] , (char *)&aSDOp->bdata[6], n);
+ pODlist->Name[Item][n] = 0x00; /* String terminator */
+ }
+ /* got unexpected response from slave */
+ else
+ {
+ if (((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR)) /* SDO info error received */
+ {
+ ecx_SDOinfoerror(context, Slave,pODlist->Index[Item], 0, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, Slave,pODlist->Index[Item], 0, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ }
+
+ return wkc;
+}
+
+/** CoE read SDO service object entry, single subindex.
+ * Used in ec_readOE().
+ *
+ * @param[in] context = context struct
+ * @param[in] Item = Item in ODlist.
+ * @param[in] SubI = Subindex of item in ODlist.
+ * @param[in] pODlist = Object description list for reference.
+ * @param[out] pOElist = resulting object entry structure.
+ * @return Workcounter of slave response.
+ */
+int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+ ec_SDOservicet *SDOp, *aSDOp;
+ int wkc;
+ uint16 Index, Slave;
+ int16 n;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+
+ wkc = 0;
+ Slave = pODlist->Slave;
+ Index = pODlist->Index[Item];
+ ec_clearmbx(&MbxIn);
+ /* clear pending out mailbox in slave if available. Timeout is set to 0 */
+ wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSDOp = (ec_SDOservicet*)&MbxIn;
+ SDOp = (ec_SDOservicet*)&MbxOut;
+ SDOp->MbxHeader.length = htoes(0x000a);
+ SDOp->MbxHeader.address = htoes(0x0000);
+ SDOp->MbxHeader.priority = 0x00;
+ /* Get new mailbox counter value */
+ cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
+ context->slavelist[Slave].mbx_cnt = cnt;
+ SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + (cnt << 4); /* CoE */
+ SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
+ SDOp->Opcode = ECT_GET_OE_REQ; /* get object entry description request */
+ SDOp->Reserved = 0;
+ SDOp->Fragments = 0; /* fragments left */
+ SDOp->wdata[0] = htoes(Index); /* Index */
+ SDOp->bdata[2] = SubI; /* SubIndex */
+ SDOp->bdata[3] = 1 + 2 + 4; /* get access rights, object category, PDO */
+ /* send get object entry description request to slave */
+ wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
+ /* mailbox placed in slave ? */
+ if (wkc > 0)
+ {
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
+ /* got response ? */
+ if (wkc > 0)
+ {
+ if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
+ ((aSDOp->Opcode & 0x7f) == ECT_GET_OE_RES))
+ {
+ pOElist->Entries++;
+ n = (etohs(aSDOp->MbxHeader.length) - 16); /* length of string(name of object) */
+ if (n > EC_MAXNAME)
+ {
+ n = EC_MAXNAME; /* max string length */
+ }
+ if (n < 0 )
+ {
+ n = 0;
+ }
+ pOElist->ValueInfo[SubI] = aSDOp->bdata[3];
+ pOElist->DataType[SubI] = etohs(aSDOp->wdata[2]);
+ pOElist->BitLength[SubI] = etohs(aSDOp->wdata[3]);
+ pOElist->ObjAccess[SubI] = etohs(aSDOp->wdata[4]);
+
+ strncpy(pOElist->Name[SubI] , (char *)&aSDOp->wdata[5], n);
+ pOElist->Name[SubI][n] = 0x00; /* string terminator */
+ }
+ /* got unexpected response from slave */
+ else
+ {
+ if (((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR)) /* SDO info error received */
+ {
+ ecx_SDOinfoerror(context, Slave, Index, SubI, etohl(aSDOp->ldata[0]));
+ }
+ else
+ {
+ ecx_packeterror(context, Slave, Index, SubI, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ }
+
+ return wkc;
+}
+
+/** CoE read SDO service object entry.
+ *
+ * @param[in] context = context struct
+ * @param[in] Item = Item in ODlist.
+ * @param[in] pODlist = Object description list for reference.
+ * @param[out] pOElist = resulting object entry structure.
+ * @return Workcounter of slave response.
+ */
+int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+ uint16 SubCount;
+ int wkc;
+ uint8 SubI;
+
+ wkc = 0;
+ pOElist->Entries = 0;
+ SubI = pODlist->MaxSub[Item];
+ /* for each entry found in ODlist */
+ for (SubCount = 0; SubCount <= SubI; SubCount++)
+ {
+ /* read subindex of entry */
+ wkc = ecx_readOEsingle(context, Item, (uint8)SubCount, pODlist, pOElist);
+ }
+
+ return wkc;
+}
+
+#ifdef EC_VER1
+/** Report SDO error.
+ *
+ * @param[in] Slave = Slave number
+ * @param[in] Index = Index that generated error
+ * @param[in] SubIdx = Subindex that generated error
+ * @param[in] AbortCode = Abortcode, see EtherCAT documentation for list
+ * @see ecx_SDOerror
+ */
+void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
+{
+ ecx_SDOerror(&ecx_context, Slave, Index, SubIdx, AbortCode);
+}
+
+/** CoE SDO read, blocking. Single subindex or Complete Access.
+ *
+ * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes
+ * then a "expedited" response is returned, otherwise a "normal" response. If a "normal"
+ * response is larger than the mailbox size then the response is segmented. The function
+ * will combine all segments and copy them to the parameter buffer.
+ *
+ * @param[in] slave = Slave number
+ * @param[in] index = Index to read
+ * @param[in] subindex = Subindex to read, must be 0 or 1 if CA is used.
+ * @param[in] CA = FALSE = single subindex. TRUE = Complete Access, all subindexes read.
+ * @param[in,out] psize = Size in bytes of parameter buffer, returns bytes read from SDO.
+ * @param[out] p = Pointer to parameter buffer
+ * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ * @see ecx_SDOread
+ */
+int ec_SDOread(uint16 slave, uint16 index, uint8 subindex,
+ boolean CA, int *psize, void *p, int timeout)
+{
+ return ecx_SDOread(&ecx_context, slave, index, subindex, CA, psize, p, timeout);
+}
+
+/** CoE SDO write, blocking. Single subindex or Complete Access.
+ *
+ * A "normal" download request is issued, unless we have
+ * small data, then a "expedited" transfer is used. If the parameter is larger than
+ * the mailbox size then the download is segmented. The function will split the
+ * parameter data in segments and send them to the slave one by one.
+ *
+ * @param[in] Slave = Slave number
+ * @param[in] Index = Index to write
+ * @param[in] SubIndex = Subindex to write, must be 0 or 1 if CA is used.
+ * @param[in] CA = FALSE = single subindex. TRUE = Complete Access, all subindexes written.
+ * @param[in] psize = Size in bytes of parameter buffer.
+ * @param[out] p = Pointer to parameter buffer
+ * @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ * @see ecx_SDOwrite
+ */
+int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex,
+ boolean CA, int psize, void *p, int Timeout)
+{
+ return ecx_SDOwrite(&ecx_context, Slave, Index, SubIndex, CA, psize, p, Timeout);
+}
+
+/** CoE RxPDO write, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in] Slave = Slave number
+ * @param[in] RxPDOnumber = Related RxPDO number
+ * @param[in] psize = Size in bytes of PDO buffer.
+ * @param[out] p = Pointer to PDO buffer
+ * @return Workcounter from last slave response
+ * @see ecx_RxPDO
+ */
+int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber, int psize, void *p)
+{
+ return ecx_RxPDO(&ecx_context, Slave, RxPDOnumber, psize, p);
+}
+
+/** CoE TxPDO read remote request, blocking.
+ *
+ * A RxPDO download request is issued.
+ *
+ * @param[in] slave = Slave number
+ * @param[in] TxPDOnumber = Related TxPDO number
+ * @param[in,out] psize = Size in bytes of PDO buffer, returns bytes read from PDO.
+ * @param[out] p = Pointer to PDO buffer
+ * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ * @see ecx_TxPDO
+ */
+int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout)
+{
+ return ecx_TxPDO(&ecx_context, slave, TxPDOnumber, psize, p, timeout);
+}
+
+/** Read PDO assign structure
+ * @param[in] Slave = Slave number
+ * @param[in] PDOassign = PDO assign object
+ * @return total bitlength of PDO assign
+ */
+int ec_readPDOassign(uint16 Slave, uint16 PDOassign)
+{
+ return ecx_readPDOassign(&ecx_context, Slave, PDOassign);
+}
+
+/** Read PDO assign structure in Complete Access mode
+ * @param[in] Slave = Slave number
+ * @param[in] PDOassign = PDO assign object
+ * @param[in] Thread_n = Calling thread index
+ * @return total bitlength of PDO assign
+ * @see ecx_readPDOmap
+ */
+int ec_readPDOassignCA(uint16 Slave, uint16 PDOassign, int Thread_n)
+{
+ return ecx_readPDOassignCA(&ecx_context, Slave, Thread_n, PDOassign);
+}
+
+/** CoE read PDO mapping.
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave.
+ *
+ * For details, see #ecx_readPDOmap
+ *
+ * @param[in] Slave = Slave number
+ * @param[out] Osize = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping succesful.
+ */
+int ec_readPDOmap(uint16 Slave, int *Osize, int *Isize)
+{
+ return ecx_readPDOmap(&ecx_context, Slave, Osize, Isize);
+}
+
+/** CoE read PDO mapping in Complete Access mode (CA).
+ *
+ * CANopen has standard indexes defined for PDO mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave. Slave has to support CA, otherwise use ec_readPDOmap().
+ *
+ * @param[in] Slave = Slave number
+ * @param[in] Thread_n = Calling thread index
+ * @param[out] Osize = Size in bits of output mapping (rxPDO) found
+ * @param[out] Isize = Size in bits of input mapping (txPDO) found
+ * @return >0 if mapping succesful.
+ * @see ecx_readPDOmap ec_readPDOmapCA
+ */
+int ec_readPDOmapCA(uint16 Slave, int Thread_n, int *Osize, int *Isize)
+{
+ return ecx_readPDOmapCA(&ecx_context, Slave, Thread_n, Osize, Isize);
+}
+
+/** CoE read Object Description List.
+ *
+ * @param[in] Slave = Slave number.
+ * @param[out] pODlist = resulting Object Description list.
+ * @return Workcounter of slave response.
+ * @see ecx_readODlist
+ */
+int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist)
+{
+ return ecx_readODlist(&ecx_context, Slave, pODlist);
+}
+
+/** CoE read Object Description. Adds textual description to object indexes.
+ *
+ * @param[in] Item = Item number in ODlist.
+ * @param[in,out] pODlist = referencing Object Description list.
+ * @return Workcounter of slave response.
+ * @see ecx_readODdescription
+ */
+int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist)
+{
+ return ecx_readODdescription(&ecx_context, Item, pODlist);
+}
+
+int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+ return ecx_readOEsingle(&ecx_context, Item, SubI, pODlist, pOElist);
+}
+
+/** CoE read SDO service object entry.
+ *
+ * @param[in] Item = Item in ODlist.
+ * @param[in] pODlist = Object description list for reference.
+ * @param[out] pOElist = resulting object entry structure.
+ * @return Workcounter of slave response.
+ * @see ecx_readOE
+ */
+int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist)
+{
+ return ecx_readOE(&ecx_context, Item, pODlist, pOElist);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatcoe.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,95 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatcoe.c
+ */
+
+#ifndef _ethercatcoe_
+#define _ethercatcoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** max entries in Object Description list */
+#define EC_MAXODLIST 1024
+
+/** max entries in Object Entry list */
+#define EC_MAXOELIST 256
+
+/* Storage for object description list */
+typedef struct
+{
+ /** slave number */
+ uint16 Slave;
+ /** number of entries in list */
+ uint16 Entries;
+ /** array of indexes */
+ uint16 Index[EC_MAXODLIST];
+ /** array of datatypes, see EtherCAT specification */
+ uint16 DataType[EC_MAXODLIST];
+ /** array of object codes, see EtherCAT specification */
+ uint8 ObjectCode[EC_MAXODLIST];
+ /** number of subindexes for each index */
+ uint8 MaxSub[EC_MAXODLIST];
+ /** textual description of each index */
+ char Name[EC_MAXODLIST][EC_MAXNAME+1];
+} ec_ODlistt;
+
+/* storage for object list entry information */
+typedef struct
+{
+ /** number of entries in list */
+ uint16 Entries;
+ /** array of value infos, see EtherCAT specification */
+ uint8 ValueInfo[EC_MAXOELIST];
+ /** array of value infos, see EtherCAT specification */
+ uint16 DataType[EC_MAXOELIST];
+ /** array of bit lengths, see EtherCAT specification */
+ uint16 BitLength[EC_MAXOELIST];
+ /** array of object access bits, see EtherCAT specification */
+ uint16 ObjAccess[EC_MAXOELIST];
+ /** textual description of each index */
+ char Name[EC_MAXOELIST][EC_MAXNAME+1];
+} ec_OElistt;
+
+#ifdef EC_VER1
+void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode);
+int ec_SDOread(uint16 slave, uint16 index, uint8 subindex,
+ boolean CA, int *psize, void *p, int timeout);
+int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex,
+ boolean CA, int psize, void *p, int Timeout);
+int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber , int psize, void *p);
+int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
+int ec_readPDOmap(uint16 Slave, int *Osize, int *Isize);
+int ec_readPDOmapCA(uint16 Slave, int Thread_n, int *Osize, int *Isize);
+int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist);
+int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist);
+int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+#endif
+
+void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode);
+int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex,
+ boolean CA, int *psize, void *p, int timeout);
+int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex,
+ boolean CA, int psize, void *p, int Timeout);
+int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber , int psize, void *p);
+int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
+int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, int *Osize, int *Isize);
+int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, int *Osize, int *Isize);
+int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist);
+int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist);
+int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatconfig.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1688 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Configuration module for EtherCAT master.
+ *
+ * After successful initialisation with ec_init() or ec_init_redundant()
+ * the slaves can be auto configured with this module.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatcoe.h"
+#include "ethercatsoe.h"
+#include "ethercatconfig.h"
+
+
+typedef struct
+{
+ int thread_n;
+ int running;
+ ecx_contextt *context;
+ uint16 slave;
+} ecx_mapt_t;
+
+ecx_mapt_t ecx_mapt[EC_MAX_MAPT];
+#if EC_MAX_MAPT > 1
+OSAL_THREAD_HANDLE ecx_threadh[EC_MAX_MAPT];
+#endif
+
+#ifdef EC_VER1
+/** Slave configuration structure */
+typedef const struct
+{
+ /** Manufacturer code of slave */
+ uint32 man;
+ /** ID of slave */
+ uint32 id;
+ /** Readable name */
+ char name[EC_MAXNAME + 1];
+ /** Data type */
+ uint8 Dtype;
+ /** Input bits */
+ uint16 Ibits;
+ /** Output bits */
+ uint16 Obits;
+ /** SyncManager 2 address */
+ uint16 SM2a;
+ /** SyncManager 2 flags */
+ uint32 SM2f;
+ /** SyncManager 3 address */
+ uint16 SM3a;
+ /** SyncManager 3 flags */
+ uint32 SM3f;
+ /** FMMU 0 activation */
+ uint8 FM0ac;
+ /** FMMU 1 activation */
+ uint8 FM1ac;
+} ec_configlist_t;
+
+#include "ethercatconfiglist.h"
+#endif
+
+/** standard SM0 flags configuration for mailbox slaves */
+#define EC_DEFAULTMBXSM0 0x00010026
+/** standard SM1 flags configuration for mailbox slaves */
+#define EC_DEFAULTMBXSM1 0x00010022
+/** standard SM0 flags configuration for digital output slaves */
+#define EC_DEFAULTDOSM0 0x00010044
+
+#ifdef EC_VER1
+/** Find slave in standard configuration list ec_configlist[]
+ *
+ * @param[in] man = manufacturer
+ * @param[in] id = ID
+ * @return index in ec_configlist[] when found, otherwise 0
+ */
+int ec_findconfig( uint32 man, uint32 id)
+{
+ int i = 0;
+
+ do
+ {
+ i++;
+ } while ( (ec_configlist[i].man != EC_CONFIGEND) &&
+ ((ec_configlist[i].man != man) || (ec_configlist[i].id != id)) );
+ if (ec_configlist[i].man == EC_CONFIGEND)
+ {
+ i = 0;
+ }
+ return i;
+}
+#endif
+
+void ecx_init_context(ecx_contextt *context)
+{
+ int lp;
+ *(context->slavecount) = 0;
+ /* clean ec_slave array */
+ memset(context->slavelist, 0x00, sizeof(ec_slavet) * context->maxslave);
+ memset(context->grouplist, 0x00, sizeof(ec_groupt) * context->maxgroup);
+ /* clear slave eeprom cache, does not actually read any eeprom */
+ ecx_siigetbyte(context, 0, EC_MAXEEPBUF);
+ for(lp = 0; lp < context->maxgroup; lp++)
+ {
+ /* default start address per group entry */
+ context->grouplist[lp].logstartaddr = lp << EC_LOGGROUPOFFSET;
+ }
+}
+
+int ecx_detect_slaves(ecx_contextt *context)
+{
+ uint8 b;
+ uint16 w;
+ int wkc;
+
+ /* make special pre-init register writes to enable MAC[1] local administered bit *
+ * setting for old netX100 slaves */
+ b = 0x00;
+ ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS, sizeof(b), &b, EC_TIMEOUTRET3); /* Ignore Alias register */
+ b = EC_STATE_INIT | EC_STATE_ACK;
+ ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3); /* Reset all slaves to Init */
+ /* netX100 should now be happy */
+ ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3); /* Reset all slaves to Init */
+ wkc = ecx_BRD(context->port, 0x0000, ECT_REG_TYPE, sizeof(w), &w, EC_TIMEOUTSAFE); /* detect number of slaves */
+ if (wkc > 0)
+ {
+ /* this is strictly "less than" since the master is "slave 0" */
+ if (wkc < EC_MAXSLAVE)
+ {
+ *(context->slavecount) = wkc;
+ }
+ else
+ {
+ EC_PRINT("Error: too many slaves on network: num_slaves=%d, EC_MAXSLAVE=%d\n",
+ wkc, EC_MAXSLAVE);
+ return -2;
+ }
+ }
+ return wkc;
+}
+
+static void ecx_set_slaves_to_default(ecx_contextt *context)
+{
+ uint8 b;
+ uint16 w;
+ uint8 zbuf[64];
+ memset(&zbuf, 0x00, sizeof(zbuf));
+ b = 0x00;
+ ecx_BWR(context->port, 0x0000, ECT_REG_DLPORT , sizeof(b) , &b, EC_TIMEOUTRET3); /* deact loop manual */
+ w = htoes(0x0004);
+ ecx_BWR(context->port, 0x0000, ECT_REG_IRQMASK , sizeof(w) , &w, EC_TIMEOUTRET3); /* set IRQ mask */
+ ecx_BWR(context->port, 0x0000, ECT_REG_RXERR , 8 , &zbuf, EC_TIMEOUTRET3); /* reset CRC counters */
+ ecx_BWR(context->port, 0x0000, ECT_REG_FMMU0 , 16 * 3 , &zbuf, EC_TIMEOUTRET3); /* reset FMMU's */
+ ecx_BWR(context->port, 0x0000, ECT_REG_SM0 , 8 * 4 , &zbuf, EC_TIMEOUTRET3); /* reset SyncM */
+ b = 0x00;
+ ecx_BWR(context->port, 0x0000, ECT_REG_DCSYNCACT , sizeof(b) , &b, EC_TIMEOUTRET3); /* reset activation register */
+ ecx_BWR(context->port, 0x0000, ECT_REG_DCSYSTIME , 4 , &zbuf, EC_TIMEOUTRET3); /* reset system time+ofs */
+ w = htoes(0x1000);
+ ecx_BWR(context->port, 0x0000, ECT_REG_DCSPEEDCNT , sizeof(w) , &w, EC_TIMEOUTRET3); /* DC speedstart */
+ w = htoes(0x0c00);
+ ecx_BWR(context->port, 0x0000, ECT_REG_DCTIMEFILT , sizeof(w) , &w, EC_TIMEOUTRET3); /* DC filt expr */
+ b = 0x00;
+ ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS , sizeof(b) , &b, EC_TIMEOUTRET3); /* Ignore Alias register */
+ b = EC_STATE_INIT | EC_STATE_ACK;
+ ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL , sizeof(b) , &b, EC_TIMEOUTRET3); /* Reset all slaves to Init */
+ b = 2;
+ ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG , sizeof(b) , &b, EC_TIMEOUTRET3); /* force Eeprom from PDI */
+ b = 0;
+ ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG , sizeof(b) , &b, EC_TIMEOUTRET3); /* set Eeprom to master */
+}
+
+#ifdef EC_VER1
+static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
+{
+ int cindex;
+ ec_slavet *csl;
+
+ csl = &(context->slavelist[slave]);
+ cindex = ec_findconfig( csl->eep_man, csl->eep_id );
+ csl->configindex= cindex;
+ /* slave found in configuration table ? */
+ if (cindex)
+ {
+ csl->Dtype = ec_configlist[cindex].Dtype;
+ strcpy(csl->name ,ec_configlist[cindex].name);
+ csl->Ibits = ec_configlist[cindex].Ibits;
+ csl->Obits = ec_configlist[cindex].Obits;
+ if (csl->Obits)
+ {
+ csl->FMMU0func = 1;
+ }
+ if (csl->Ibits)
+ {
+ csl->FMMU1func = 2;
+ }
+ csl->FMMU[0].FMMUactive = ec_configlist[cindex].FM0ac;
+ csl->FMMU[1].FMMUactive = ec_configlist[cindex].FM1ac;
+ csl->SM[2].StartAddr = htoes(ec_configlist[cindex].SM2a);
+ csl->SM[2].SMflags = htoel(ec_configlist[cindex].SM2f);
+ /* simple (no mailbox) output slave found ? */
+ if (csl->Obits && !csl->SM[2].StartAddr)
+ {
+ csl->SM[0].StartAddr = htoes(0x0f00);
+ csl->SM[0].SMlength = htoes((csl->Obits + 7) / 8);
+ csl->SM[0].SMflags = htoel(EC_DEFAULTDOSM0);
+ csl->FMMU[0].FMMUactive = 1;
+ csl->FMMU[0].FMMUtype = 2;
+ csl->SMtype[0] = 3;
+ }
+ /* complex output slave */
+ else
+ {
+ csl->SM[2].SMlength = htoes((csl->Obits + 7) / 8);
+ csl->SMtype[2] = 3;
+ }
+ csl->SM[3].StartAddr = htoes(ec_configlist[cindex].SM3a);
+ csl->SM[3].SMflags = htoel(ec_configlist[cindex].SM3f);
+ /* simple (no mailbox) input slave found ? */
+ if (csl->Ibits && !csl->SM[3].StartAddr)
+ {
+ csl->SM[1].StartAddr = htoes(0x1000);
+ csl->SM[1].SMlength = htoes((csl->Ibits + 7) / 8);
+ csl->SM[1].SMflags = htoel(0x00000000);
+ csl->FMMU[1].FMMUactive = 1;
+ csl->FMMU[1].FMMUtype = 1;
+ csl->SMtype[1] = 4;
+ }
+ /* complex input slave */
+ else
+ {
+ csl->SM[3].SMlength = htoes((csl->Ibits + 7) / 8);
+ csl->SMtype[3] = 4;
+ }
+ }
+ return cindex;
+}
+#else
+static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
+{
+ (void)context;
+ (void)slave;
+ return 0;
+}
+#endif
+
+/* If slave has SII and same slave ID done before, use previous data.
+ * This is safe because SII is constant for same slave ID.
+ */
+static int ecx_lookup_prev_sii(ecx_contextt *context, uint16 slave)
+{
+ int i, nSM;
+ if ((slave > 1) && (*(context->slavecount) > 0))
+ {
+ i = 1;
+ while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||
+ (context->slavelist[i].eep_id != context->slavelist[slave].eep_id ) ||
+ (context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&
+ (i < slave))
+ {
+ i++;
+ }
+ if(i < slave)
+ {
+ context->slavelist[slave].CoEdetails = context->slavelist[i].CoEdetails;
+ context->slavelist[slave].FoEdetails = context->slavelist[i].FoEdetails;
+ context->slavelist[slave].EoEdetails = context->slavelist[i].EoEdetails;
+ context->slavelist[slave].SoEdetails = context->slavelist[i].SoEdetails;
+ if(context->slavelist[i].blockLRW > 0)
+ {
+ context->slavelist[slave].blockLRW = 1;
+ context->slavelist[0].blockLRW++;
+ }
+ context->slavelist[slave].Ebuscurrent = context->slavelist[i].Ebuscurrent;
+ context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+ memcpy(context->slavelist[slave].name, context->slavelist[i].name, EC_MAXNAME + 1);
+ for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+ {
+ context->slavelist[slave].SM[nSM].StartAddr = context->slavelist[i].SM[nSM].StartAddr;
+ context->slavelist[slave].SM[nSM].SMlength = context->slavelist[i].SM[nSM].SMlength;
+ context->slavelist[slave].SM[nSM].SMflags = context->slavelist[i].SM[nSM].SMflags;
+ }
+ context->slavelist[slave].FMMU0func = context->slavelist[i].FMMU0func;
+ context->slavelist[slave].FMMU1func = context->slavelist[i].FMMU1func;
+ context->slavelist[slave].FMMU2func = context->slavelist[i].FMMU2func;
+ context->slavelist[slave].FMMU3func = context->slavelist[i].FMMU3func;
+ EC_PRINT("Copy SII slave %d from %d.\n", slave, i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Enumerate and init all slaves.
+ *
+ * @param[in] context = context struct
+ * @param[in] usetable = TRUE when using configtable to init slaves, FALSE otherwise
+ * @return Workcounter of slave discover datagram = number of slaves found
+ */
+int ecx_config_init(ecx_contextt *context, uint8 usetable)
+{
+ uint16 slave, ADPh, configadr, ssigen;
+ uint16 topology, estat;
+ int16 topoc, slavec, aliasadr;
+ uint8 b,h;
+ uint8 SMc;
+ uint32 eedat;
+ int wkc, cindex, nSM;
+ uint16 val16;
+
+ EC_PRINT("ec_config_init %d\n",usetable);
+ ecx_init_context(context);
+ wkc = ecx_detect_slaves(context);
+ if (wkc > 0)
+ {
+ ecx_set_slaves_to_default(context);
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ ADPh = (uint16)(1 - slave);
+ val16 = ecx_APRDw(context->port, ADPh, ECT_REG_PDICTL, EC_TIMEOUTRET3); /* read interface type of slave */
+ context->slavelist[slave].Itype = etohs(val16);
+ /* a node offset is used to improve readability of network frames */
+ /* this has no impact on the number of addressable slaves (auto wrap around) */
+ ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(slave + EC_NODEOFFSET) , EC_TIMEOUTRET3); /* set node address of slave */
+ if (slave == 1)
+ {
+ b = 1; /* kill non ecat frames for first slave */
+ }
+ else
+ {
+ b = 0; /* pass all frames for following slaves */
+ }
+ ecx_APWRw(context->port, ADPh, ECT_REG_DLCTL, htoes(b), EC_TIMEOUTRET3); /* set non ecat frame behaviour */
+ configadr = ecx_APRDw(context->port, ADPh, ECT_REG_STADR, EC_TIMEOUTRET3);
+ configadr = etohs(configadr);
+ context->slavelist[slave].configadr = configadr;
+ ecx_FPRD(context->port, configadr, ECT_REG_ALIAS, sizeof(aliasadr), &aliasadr, EC_TIMEOUTRET3);
+ context->slavelist[slave].aliasadr = etohs(aliasadr);
+ ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET3);
+ estat = etohs(estat);
+ if (estat & EC_ESTAT_R64) /* check if slave can read 8 byte chunks */
+ {
+ context->slavelist[slave].eep_8byte = 1;
+ }
+ ecx_readeeprom1(context, slave, ECT_SII_MANUF); /* Manuf */
+ }
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* Manuf */
+ context->slavelist[slave].eep_man = etohl(eedat);
+ ecx_readeeprom1(context, slave, ECT_SII_ID); /* ID */
+ }
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* ID */
+ context->slavelist[slave].eep_id = etohl(eedat);
+ ecx_readeeprom1(context, slave, ECT_SII_REV); /* revision */
+ }
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* revision */
+ context->slavelist[slave].eep_rev = etohl(eedat);
+ ecx_readeeprom1(context, slave, ECT_SII_RXMBXADR); /* write mailbox address + mailboxsize */
+ }
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* write mailbox address and mailboxsize */
+ context->slavelist[slave].mbx_wo = (uint16)LO_WORD(etohl(eedat));
+ context->slavelist[slave].mbx_l = (uint16)HI_WORD(etohl(eedat));
+ if (context->slavelist[slave].mbx_l > 0)
+ {
+ ecx_readeeprom1(context, slave, ECT_SII_TXMBXADR); /* read mailbox offset */
+ }
+ }
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ if (context->slavelist[slave].mbx_l > 0)
+ {
+ eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* read mailbox offset */
+ context->slavelist[slave].mbx_ro = (uint16)LO_WORD(etohl(eedat)); /* read mailbox offset */
+ context->slavelist[slave].mbx_rl = (uint16)HI_WORD(etohl(eedat)); /*read mailbox length */
+ if (context->slavelist[slave].mbx_rl == 0)
+ {
+ context->slavelist[slave].mbx_rl = context->slavelist[slave].mbx_l;
+ }
+ ecx_readeeprom1(context, slave, ECT_SII_MBXPROTO);
+ }
+ configadr = context->slavelist[slave].configadr;
+ val16 = ecx_FPRDw(context->port, configadr, ECT_REG_ESCSUP, EC_TIMEOUTRET3);
+ if ((etohs(val16) & 0x04) > 0) /* Support DC? */
+ {
+ context->slavelist[slave].hasdc = TRUE;
+ }
+ else
+ {
+ context->slavelist[slave].hasdc = FALSE;
+ }
+ topology = ecx_FPRDw(context->port, configadr, ECT_REG_DLSTAT, EC_TIMEOUTRET3); /* extract topology from DL status */
+ topology = etohs(topology);
+ h = 0;
+ b = 0;
+ if ((topology & 0x0300) == 0x0200) /* port0 open and communication established */
+ {
+ h++;
+ b |= 0x01;
+ }
+ if ((topology & 0x0c00) == 0x0800) /* port1 open and communication established */
+ {
+ h++;
+ b |= 0x02;
+ }
+ if ((topology & 0x3000) == 0x2000) /* port2 open and communication established */
+ {
+ h++;
+ b |= 0x04;
+ }
+ if ((topology & 0xc000) == 0x8000) /* port3 open and communication established */
+ {
+ h++;
+ b |= 0x08;
+ }
+ /* ptype = Physical type*/
+ val16 = ecx_FPRDw(context->port, configadr, ECT_REG_PORTDES, EC_TIMEOUTRET3);
+ context->slavelist[slave].ptype = LO_BYTE(etohs(val16));
+ context->slavelist[slave].topology = h;
+ context->slavelist[slave].activeports = b;
+ /* 0=no links, not possible */
+ /* 1=1 link , end of line */
+ /* 2=2 links , one before and one after */
+ /* 3=3 links , split point */
+ /* 4=4 links , cross point */
+ /* search for parent */
+ context->slavelist[slave].parent = 0; /* parent is master */
+ if (slave > 1)
+ {
+ topoc = 0;
+ slavec = slave - 1;
+ do
+ {
+ topology = context->slavelist[slavec].topology;
+ if (topology == 1)
+ {
+ topoc--; /* endpoint found */
+ }
+ if (topology == 3)
+ {
+ topoc++; /* split found */
+ }
+ if (topology == 4)
+ {
+ topoc += 2; /* cross found */
+ }
+ if (((topoc >= 0) && (topology > 1)) ||
+ (slavec == 1)) /* parent found */
+ {
+ context->slavelist[slave].parent = slavec;
+ slavec = 1;
+ }
+ slavec--;
+ }
+ while (slavec > 0);
+ }
+ (void)ecx_statecheck(context, slave, EC_STATE_INIT, EC_TIMEOUTSTATE); //* check state change Init */
+
+ /* set default mailbox configuration if slave has mailbox */
+ if (context->slavelist[slave].mbx_l>0)
+ {
+ context->slavelist[slave].SMtype[0] = 1;
+ context->slavelist[slave].SMtype[1] = 2;
+ context->slavelist[slave].SMtype[2] = 3;
+ context->slavelist[slave].SMtype[3] = 4;
+ context->slavelist[slave].SM[0].StartAddr = htoes(context->slavelist[slave].mbx_wo);
+ context->slavelist[slave].SM[0].SMlength = htoes(context->slavelist[slave].mbx_l);
+ context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);
+ context->slavelist[slave].SM[1].StartAddr = htoes(context->slavelist[slave].mbx_ro);
+ context->slavelist[slave].SM[1].SMlength = htoes(context->slavelist[slave].mbx_rl);
+ context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);
+ eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP);
+ context->slavelist[slave].mbx_proto = etohl(eedat);
+ }
+ cindex = 0;
+ /* use configuration table ? */
+ if (usetable == 1)
+ {
+ cindex = ecx_config_from_table(context, slave);
+ }
+ /* slave not in configuration table, find out via SII */
+ if (!cindex && !ecx_lookup_prev_sii(context, slave))
+ {
+ ssigen = ecx_siifind(context, slave, ECT_SII_GENERAL);
+ /* SII general section */
+ if (ssigen)
+ {
+ context->slavelist[slave].CoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x07);
+ context->slavelist[slave].FoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x08);
+ context->slavelist[slave].EoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x09);
+ context->slavelist[slave].SoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x0a);
+ if((ecx_siigetbyte(context, slave, ssigen + 0x0d) & 0x02) > 0)
+ {
+ context->slavelist[slave].blockLRW = 1;
+ context->slavelist[0].blockLRW++;
+ }
+ context->slavelist[slave].Ebuscurrent = ecx_siigetbyte(context, slave, ssigen + 0x0e);
+ context->slavelist[slave].Ebuscurrent += ecx_siigetbyte(context, slave, ssigen + 0x0f) << 8;
+ context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+ }
+ /* SII strings section */
+ if (ecx_siifind(context, slave, ECT_SII_STRING) > 0)
+ {
+ ecx_siistring(context, context->slavelist[slave].name, slave, 1);
+ }
+ /* no name for slave found, use constructed name */
+ else
+ {
+ sprintf(context->slavelist[slave].name, "? M:%8.8x I:%8.8x",
+ (unsigned int)context->slavelist[slave].eep_man,
+ (unsigned int)context->slavelist[slave].eep_id);
+ }
+ /* SII SM section */
+ nSM = ecx_siiSM(context, slave, context->eepSM);
+ if (nSM>0)
+ {
+ context->slavelist[slave].SM[0].StartAddr = htoes(context->eepSM->PhStart);
+ context->slavelist[slave].SM[0].SMlength = htoes(context->eepSM->Plength);
+ context->slavelist[slave].SM[0].SMflags =
+ htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));
+ SMc = 1;
+ while ((SMc < EC_MAXSM) && ecx_siiSMnext(context, slave, context->eepSM, SMc))
+ {
+ context->slavelist[slave].SM[SMc].StartAddr = htoes(context->eepSM->PhStart);
+ context->slavelist[slave].SM[SMc].SMlength = htoes(context->eepSM->Plength);
+ context->slavelist[slave].SM[SMc].SMflags =
+ htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));
+ SMc++;
+ }
+ }
+ /* SII FMMU section */
+ if (ecx_siiFMMU(context, slave, context->eepFMMU))
+ {
+ if (context->eepFMMU->FMMU0 !=0xff)
+ {
+ context->slavelist[slave].FMMU0func = context->eepFMMU->FMMU0;
+ }
+ if (context->eepFMMU->FMMU1 !=0xff)
+ {
+ context->slavelist[slave].FMMU1func = context->eepFMMU->FMMU1;
+ }
+ if (context->eepFMMU->FMMU2 !=0xff)
+ {
+ context->slavelist[slave].FMMU2func = context->eepFMMU->FMMU2;
+ }
+ if (context->eepFMMU->FMMU3 !=0xff)
+ {
+ context->slavelist[slave].FMMU3func = context->eepFMMU->FMMU3;
+ }
+ }
+ }
+
+ if (context->slavelist[slave].mbx_l > 0)
+ {
+ if (context->slavelist[slave].SM[0].StartAddr == 0x0000) /* should never happen */
+ {
+ EC_PRINT("Slave %d has no proper mailbox in configuration, try default.\n", slave);
+ context->slavelist[slave].SM[0].StartAddr = htoes(0x1000);
+ context->slavelist[slave].SM[0].SMlength = htoes(0x0080);
+ context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);
+ context->slavelist[slave].SMtype[0] = 1;
+ }
+ if (context->slavelist[slave].SM[1].StartAddr == 0x0000) /* should never happen */
+ {
+ EC_PRINT("Slave %d has no proper mailbox out configuration, try default.\n", slave);
+ context->slavelist[slave].SM[1].StartAddr = htoes(0x1080);
+ context->slavelist[slave].SM[1].SMlength = htoes(0x0080);
+ context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);
+ context->slavelist[slave].SMtype[1] = 2;
+ }
+ /* program SM0 mailbox in and SM1 mailbox out for slave */
+ /* writing both SM in one datagram will solve timing issue in old NETX */
+ ecx_FPWR(context->port, configadr, ECT_REG_SM0, sizeof(ec_smt) * 2,
+ &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
+ }
+ /* some slaves need eeprom available to PDI in init->preop transition */
+ ecx_eeprom2pdi(context, slave);
+ /* request pre_op for slave */
+ ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP | EC_STATE_ACK) , EC_TIMEOUTRET3); /* set preop status */
+ }
+ }
+ return wkc;
+}
+
+/* If slave has SII mapping and same slave ID done before, use previous mapping.
+ * This is safe because SII mapping is constant for same slave ID.
+ */
+static int ecx_lookup_mapping(ecx_contextt *context, uint16 slave, int *Osize, int *Isize)
+{
+ int i, nSM;
+ if ((slave > 1) && (*(context->slavecount) > 0))
+ {
+ i = 1;
+ while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||
+ (context->slavelist[i].eep_id != context->slavelist[slave].eep_id ) ||
+ (context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&
+ (i < slave))
+ {
+ i++;
+ }
+ if(i < slave)
+ {
+ for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+ {
+ context->slavelist[slave].SM[nSM].SMlength = context->slavelist[i].SM[nSM].SMlength;
+ context->slavelist[slave].SMtype[nSM] = context->slavelist[i].SMtype[nSM];
+ }
+ *Osize = context->slavelist[i].Obits;
+ *Isize = context->slavelist[i].Ibits;
+ context->slavelist[slave].Obits = *Osize;
+ context->slavelist[slave].Ibits = *Isize;
+ EC_PRINT("Copy mapping slave %d from %d.\n", slave, i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, int thread_n)
+{
+ int Isize, Osize;
+ int rval;
+
+ ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* check state change pre-op */
+
+ EC_PRINT(" >Slave %d, configadr %x, state %2.2x\n",
+ slave, context->slavelist[slave].configadr, context->slavelist[slave].state);
+
+ /* execute special slave configuration hook Pre-Op to Safe-OP */
+ if(context->slavelist[slave].PO2SOconfig) /* only if registered */
+ {
+ context->slavelist[slave].PO2SOconfig(slave);
+ }
+ /* if slave not found in configlist find IO mapping in slave self */
+ if (!context->slavelist[slave].configindex)
+ {
+ Isize = 0;
+ Osize = 0;
+ if (context->slavelist[slave].mbx_proto & ECT_MBXPROT_COE) /* has CoE */
+ {
+ rval = 0;
+ if (context->slavelist[slave].CoEdetails & ECT_COEDET_SDOCA) /* has Complete Access */
+ {
+ /* read PDO mapping via CoE and use Complete Access */
+ rval = ecx_readPDOmapCA(context, slave, thread_n, &Osize, &Isize);
+ }
+ if (!rval) /* CA not available or not succeeded */
+ {
+ /* read PDO mapping via CoE */
+ rval = ecx_readPDOmap(context, slave, &Osize, &Isize);
+ }
+ EC_PRINT(" CoE Osize:%d Isize:%d\n", Osize, Isize);
+ }
+ if ((!Isize && !Osize) && (context->slavelist[slave].mbx_proto & ECT_MBXPROT_SOE)) /* has SoE */
+ {
+ /* read AT / MDT mapping via SoE */
+ rval = ecx_readIDNmap(context, slave, &Osize, &Isize);
+ context->slavelist[slave].SM[2].SMlength = htoes((Osize + 7) / 8);
+ context->slavelist[slave].SM[3].SMlength = htoes((Isize + 7) / 8);
+ EC_PRINT(" SoE Osize:%d Isize:%d\n", Osize, Isize);
+ }
+ context->slavelist[slave].Obits = Osize;
+ context->slavelist[slave].Ibits = Isize;
+ }
+
+ return 1;
+}
+
+static int ecx_map_sii(ecx_contextt *context, uint16 slave)
+{
+ int Isize, Osize;
+ int nSM;
+ ec_eepromPDOt eepPDO;
+
+ Osize = context->slavelist[slave].Obits;
+ Isize = context->slavelist[slave].Ibits;
+
+ if (!Isize && !Osize) /* find PDO in previous slave with same ID */
+ {
+ (void)ecx_lookup_mapping(context, slave, &Osize, &Isize);
+ }
+ if (!Isize && !Osize) /* find PDO mapping by SII */
+ {
+ memset(&eepPDO, 0, sizeof(eepPDO));
+ Isize = (int)ecx_siiPDO(context, slave, &eepPDO, 0);
+ EC_PRINT(" SII Isize:%d\n", Isize);
+ for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+ {
+ if (eepPDO.SMbitsize[nSM] > 0)
+ {
+ context->slavelist[slave].SM[nSM].SMlength = htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
+ context->slavelist[slave].SMtype[nSM] = 4;
+ EC_PRINT(" SM%d length %d\n", nSM, eepPDO.SMbitsize[nSM]);
+ }
+ }
+ Osize = (int)ecx_siiPDO(context, slave, &eepPDO, 1);
+ EC_PRINT(" SII Osize:%d\n", Osize);
+ for( nSM=0 ; nSM < EC_MAXSM ; nSM++ )
+ {
+ if (eepPDO.SMbitsize[nSM] > 0)
+ {
+ context->slavelist[slave].SM[nSM].SMlength = htoes((eepPDO.SMbitsize[nSM] + 7) / 8);
+ context->slavelist[slave].SMtype[nSM] = 3;
+ EC_PRINT(" SM%d length %d\n", nSM, eepPDO.SMbitsize[nSM]);
+ }
+ }
+ }
+ context->slavelist[slave].Obits = Osize;
+ context->slavelist[slave].Ibits = Isize;
+ EC_PRINT(" ISIZE:%d %d OSIZE:%d\n",
+ context->slavelist[slave].Ibits, Isize,context->slavelist[slave].Obits);
+
+ return 1;
+}
+
+static int ecx_map_sm(ecx_contextt *context, uint16 slave)
+{
+ uint16 configadr;
+ int nSM;
+
+ configadr = context->slavelist[slave].configadr;
+
+ EC_PRINT(" SM programming\n");
+ if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[0].StartAddr)
+ {
+ ecx_FPWR(context->port, configadr, ECT_REG_SM0,
+ sizeof(ec_smt), &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);
+ EC_PRINT(" SM0 Type:%d StartAddr:%4.4x Flags:%8.8x\n",
+ context->slavelist[slave].SMtype[0],
+ context->slavelist[slave].SM[0].StartAddr,
+ context->slavelist[slave].SM[0].SMflags);
+ }
+ if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[1].StartAddr)
+ {
+ ecx_FPWR(context->port, configadr, ECT_REG_SM1,
+ sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET3);
+ EC_PRINT(" SM1 Type:%d StartAddr:%4.4x Flags:%8.8x\n",
+ context->slavelist[slave].SMtype[1],
+ context->slavelist[slave].SM[1].StartAddr,
+ context->slavelist[slave].SM[1].SMflags);
+ }
+ /* program SM2 to SMx */
+ for( nSM = 2 ; nSM < EC_MAXSM ; nSM++ )
+ {
+ if (context->slavelist[slave].SM[nSM].StartAddr)
+ {
+ /* check if SM length is zero -> clear enable flag */
+ if( context->slavelist[slave].SM[nSM].SMlength == 0)
+ {
+ context->slavelist[slave].SM[nSM].SMflags =
+ htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) & EC_SMENABLEMASK);
+ }
+ /* if SM length is non zero always set enable flag */
+ else
+ {
+ context->slavelist[slave].SM[nSM].SMflags =
+ htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) | ~EC_SMENABLEMASK);
+ }
+ ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt))),
+ sizeof(ec_smt), &context->slavelist[slave].SM[nSM], EC_TIMEOUTRET3);
+ EC_PRINT(" SM%d Type:%d StartAddr:%4.4x Flags:%8.8x\n", nSM,
+ context->slavelist[slave].SMtype[nSM],
+ context->slavelist[slave].SM[nSM].StartAddr,
+ context->slavelist[slave].SM[nSM].SMflags);
+ }
+ }
+ if (context->slavelist[slave].Ibits > 7)
+ {
+ context->slavelist[slave].Ibytes = (context->slavelist[slave].Ibits + 7) / 8;
+ }
+ if (context->slavelist[slave].Obits > 7)
+ {
+ context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;
+ }
+
+ return 1;
+}
+
+#if EC_MAX_MAPT > 1
+OSAL_THREAD_FUNC ecx_mapper_thread(void *param)
+{
+ ecx_mapt_t *maptp;
+ maptp = param;
+ ecx_map_coe_soe(maptp->context, maptp->slave, maptp->thread_n);
+ maptp->running = 0;
+}
+
+static int ecx_find_mapt(void)
+{
+ int p;
+ p = 0;
+ while((p < EC_MAX_MAPT) && ecx_mapt[p].running)
+ {
+ p++;
+ }
+ if(p < EC_MAX_MAPT)
+ {
+ return p;
+ }
+ else
+ {
+ return -1;
+ }
+}
+#endif
+
+static int ecx_get_threadcount(void)
+{
+ int thrc, thrn;
+ thrc = 0;
+ for(thrn = 0 ; thrn < EC_MAX_MAPT ; thrn++)
+ {
+ thrc += ecx_mapt[thrn].running;
+ }
+ return thrc;
+}
+
+static void ecx_config_find_mappings(ecx_contextt *context, uint8 group)
+{
+ int thrn, thrc;
+ uint16 slave;
+
+ for (thrn = 0; thrn < EC_MAX_MAPT; thrn++)
+ {
+ ecx_mapt[thrn].running = 0;
+ }
+ /* find CoE and SoE mapping of slaves in multiple threads */
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ if (!group || (group == context->slavelist[slave].group))
+ {
+#if EC_MAX_MAPT > 1
+ /* multi-threaded version */
+ while ((thrn = ecx_find_mapt()) < 0)
+ {
+ osal_usleep(1000);
+ }
+ ecx_mapt[thrn].context = context;
+ ecx_mapt[thrn].slave = slave;
+ ecx_mapt[thrn].thread_n = thrn;
+ ecx_mapt[thrn].running = 1;
+ osal_thread_create(&(ecx_threadh[thrn]), 128000,
+ &ecx_mapper_thread, &(ecx_mapt[thrn]));
+#else
+ /* serialised version */
+ ecx_map_coe_soe(context, slave, 0);
+#endif
+ }
+ }
+ /* wait for all threads to finish */
+ do
+ {
+ thrc = ecx_get_threadcount();
+ if (thrc)
+ {
+ osal_usleep(1000);
+ }
+ } while (thrc);
+ /* find SII mapping of slave and program SM */
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ if (!group || (group == context->slavelist[slave].group))
+ {
+ ecx_map_sii(context, slave);
+ ecx_map_sm(context, slave);
+ }
+ }
+}
+
+static void ecx_config_create_input_mappings(ecx_contextt *context, void *pIOmap,
+ uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
+{
+ int BitCount = 0;
+ int ByteCount = 0;
+ int FMMUsize = 0;
+ int FMMUdone = 0;
+ uint8 SMc = 0;
+ uint16 EndAddr;
+ uint16 SMlength;
+ uint16 configadr;
+ uint8 FMMUc;
+
+ EC_PRINT(" =Slave %d, INPUT MAPPING\n", slave);
+
+ configadr = context->slavelist[slave].configadr;
+ FMMUc = context->slavelist[slave].FMMUunused;
+ if (context->slavelist[slave].Obits) /* find free FMMU */
+ {
+ while (context->slavelist[slave].FMMU[FMMUc].LogStart)
+ {
+ FMMUc++;
+ }
+ }
+ /* search for SM that contribute to the input mapping */
+ while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Ibits + 7) / 8)))
+ {
+ EC_PRINT(" FMMU %d\n", FMMUc);
+ while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4))
+ {
+ SMc++;
+ }
+ EC_PRINT(" SM%d\n", SMc);
+ context->slavelist[slave].FMMU[FMMUc].PhysStart =
+ context->slavelist[slave].SM[SMc].StartAddr;
+ SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+ ByteCount += SMlength;
+ BitCount += SMlength * 8;
+ EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+ while ((BitCount < context->slavelist[slave].Ibits) && (SMc < (EC_MAXSM - 1))) /* more SM for input */
+ {
+ SMc++;
+ while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4))
+ {
+ SMc++;
+ }
+ /* if addresses from more SM connect use one FMMU otherwise break up in multiple FMMU */
+ if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr)
+ {
+ break;
+ }
+ EC_PRINT(" SM%d\n", SMc);
+ SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+ ByteCount += SMlength;
+ BitCount += SMlength * 8;
+ EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+ }
+
+ /* bit oriented slave */
+ if (!context->slavelist[slave].Ibytes)
+ {
+ context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+ context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+ *BitPos += context->slavelist[slave].Ibits - 1;
+ if (*BitPos > 7)
+ {
+ *LogAddr += 1;
+ *BitPos -= 8;
+ }
+ FMMUsize = *LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1;
+ context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+ context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+ *BitPos += 1;
+ if (*BitPos > 7)
+ {
+ *LogAddr += 1;
+ *BitPos -= 8;
+ }
+ }
+ /* byte oriented slave */
+ else
+ {
+ if (*BitPos)
+ {
+ *LogAddr += 1;
+ *BitPos = 0;
+ }
+ context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+ context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+ *BitPos = 7;
+ FMMUsize = ByteCount;
+ if ((FMMUsize + FMMUdone)> (int)context->slavelist[slave].Ibytes)
+ {
+ FMMUsize = context->slavelist[slave].Ibytes - FMMUdone;
+ }
+ *LogAddr += FMMUsize;
+ context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+ context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+ *BitPos = 0;
+ }
+ FMMUdone += FMMUsize;
+ if (context->slavelist[slave].FMMU[FMMUc].LogLength)
+ {
+ context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
+ context->slavelist[slave].FMMU[FMMUc].FMMUtype = 1;
+ context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
+ /* program FMMU for input */
+ ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
+ sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
+ /* add one for an input FMMU */
+ context->grouplist[group].inputsWKC++;
+ }
+ if (!context->slavelist[slave].inputs)
+ {
+ if (group)
+ {
+ context->slavelist[slave].inputs =
+ (uint8 *)(pIOmap) +
+ etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) -
+ context->grouplist[group].logstartaddr;
+ }
+ else
+ {
+ context->slavelist[slave].inputs =
+ (uint8 *)(pIOmap) +
+ etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);
+ }
+ context->slavelist[slave].Istartbit =
+ context->slavelist[slave].FMMU[FMMUc].LogStartbit;
+ EC_PRINT(" Inputs %p startbit %d\n",
+ context->slavelist[slave].inputs,
+ context->slavelist[slave].Istartbit);
+ }
+ FMMUc++;
+ }
+ context->slavelist[slave].FMMUunused = FMMUc;
+}
+
+static void ecx_config_create_output_mappings(ecx_contextt *context, void *pIOmap,
+ uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
+{
+ int BitCount = 0;
+ int ByteCount = 0;
+ int FMMUsize = 0;
+ int FMMUdone = 0;
+ uint8 SMc = 0;
+ uint16 EndAddr;
+ uint16 SMlength;
+ uint16 configadr;
+ uint8 FMMUc;
+
+ EC_PRINT(" OUTPUT MAPPING\n");
+
+ FMMUc = context->slavelist[slave].FMMUunused;
+ configadr = context->slavelist[slave].configadr;
+
+ /* search for SM that contribute to the output mapping */
+ while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Obits + 7) / 8)))
+ {
+ EC_PRINT(" FMMU %d\n", FMMUc);
+ while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3))
+ {
+ SMc++;
+ }
+ EC_PRINT(" SM%d\n", SMc);
+ context->slavelist[slave].FMMU[FMMUc].PhysStart =
+ context->slavelist[slave].SM[SMc].StartAddr;
+ SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+ ByteCount += SMlength;
+ BitCount += SMlength * 8;
+ EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+ while ((BitCount < context->slavelist[slave].Obits) && (SMc < (EC_MAXSM - 1))) /* more SM for output */
+ {
+ SMc++;
+ while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3))
+ {
+ SMc++;
+ }
+ /* if addresses from more SM connect use one FMMU otherwise break up in multiple FMMU */
+ if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr)
+ {
+ break;
+ }
+ EC_PRINT(" SM%d\n", SMc);
+ SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);
+ ByteCount += SMlength;
+ BitCount += SMlength * 8;
+ EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;
+ }
+
+ /* bit oriented slave */
+ if (!context->slavelist[slave].Obytes)
+ {
+ context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+ context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+ *BitPos += context->slavelist[slave].Obits - 1;
+ if (*BitPos > 7)
+ {
+ *LogAddr += 1;
+ *BitPos -= 8;
+ }
+ FMMUsize = *LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1;
+ context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+ context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+ *BitPos += 1;
+ if (*BitPos > 7)
+ {
+ *LogAddr += 1;
+ *BitPos -= 8;
+ }
+ }
+ /* byte oriented slave */
+ else
+ {
+ if (*BitPos)
+ {
+ *LogAddr += 1;
+ *BitPos = 0;
+ }
+ context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);
+ context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;
+ *BitPos = 7;
+ FMMUsize = ByteCount;
+ if ((FMMUsize + FMMUdone)> (int)context->slavelist[slave].Obytes)
+ {
+ FMMUsize = context->slavelist[slave].Obytes - FMMUdone;
+ }
+ *LogAddr += FMMUsize;
+ context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);
+ context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;
+ *BitPos = 0;
+ }
+ FMMUdone += FMMUsize;
+ context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;
+ context->slavelist[slave].FMMU[FMMUc].FMMUtype = 2;
+ context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;
+ /* program FMMU for output */
+ ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),
+ sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);
+ context->grouplist[group].outputsWKC++;
+ if (!context->slavelist[slave].outputs)
+ {
+ if (group)
+ {
+ context->slavelist[slave].outputs =
+ (uint8 *)(pIOmap) +
+ etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) -
+ context->grouplist[group].logstartaddr;
+ }
+ else
+ {
+ context->slavelist[slave].outputs =
+ (uint8 *)(pIOmap) +
+ etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);
+ }
+ context->slavelist[slave].Ostartbit =
+ context->slavelist[slave].FMMU[FMMUc].LogStartbit;
+ EC_PRINT(" slave %d Outputs %p startbit %d\n",
+ slave,
+ context->slavelist[slave].outputs,
+ context->slavelist[slave].Ostartbit);
+ }
+ FMMUc++;
+ }
+ context->slavelist[slave].FMMUunused = FMMUc;
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+* in sequential order (legacy SOEM way).
+*
+ *
+ * @param[in] context = context struct
+ * @param[out] pIOmap = pointer to IOmap
+ * @param[in] group = group to map, 0 = all groups
+ * @return IOmap size
+ */
+int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
+{
+ uint16 slave, configadr;
+ uint8 BitPos;
+ uint32 LogAddr = 0;
+ uint32 oLogAddr = 0;
+ uint32 diff;
+ uint16 currentsegment = 0;
+ uint32 segmentsize = 0;
+
+ if ((*(context->slavecount) > 0) && (group < context->maxgroup))
+ {
+ EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);
+ LogAddr = context->grouplist[group].logstartaddr;
+ oLogAddr = LogAddr;
+ BitPos = 0;
+ context->grouplist[group].nsegments = 0;
+ context->grouplist[group].outputsWKC = 0;
+ context->grouplist[group].inputsWKC = 0;
+
+ /* Find mappings and program syncmanagers */
+ ecx_config_find_mappings(context, group);
+
+ /* do output mapping of slave and program FMMUs */
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ configadr = context->slavelist[slave].configadr;
+
+ if (!group || (group == context->slavelist[slave].group))
+ {
+ /* create output mapping */
+ if (context->slavelist[slave].Obits)
+ {
+ ecx_config_create_output_mappings (context, pIOmap, group, slave, &LogAddr, &BitPos);
+ diff = LogAddr - oLogAddr;
+ oLogAddr = LogAddr;
+ if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+ {
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+ {
+ currentsegment++;
+ segmentsize = diff;
+ }
+ }
+ else
+ {
+ segmentsize += diff;
+ }
+ }
+ }
+ }
+ if (BitPos)
+ {
+ LogAddr++;
+ oLogAddr = LogAddr;
+ BitPos = 0;
+ if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+ {
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+ {
+ currentsegment++;
+ segmentsize = 1;
+ }
+ }
+ else
+ {
+ segmentsize += 1;
+ }
+ }
+ context->grouplist[group].outputs = pIOmap;
+ context->grouplist[group].Obytes = LogAddr - context->grouplist[group].logstartaddr;
+ context->grouplist[group].nsegments = currentsegment + 1;
+ context->grouplist[group].Isegment = currentsegment;
+ context->grouplist[group].Ioffset = segmentsize;
+ if (!group)
+ {
+ context->slavelist[0].outputs = pIOmap;
+ context->slavelist[0].Obytes = LogAddr -
+ context->grouplist[group].logstartaddr; /* store output bytes in master record */
+ }
+
+ /* do input mapping of slave and program FMMUs */
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ configadr = context->slavelist[slave].configadr;
+ if (!group || (group == context->slavelist[slave].group))
+ {
+ /* create input mapping */
+ if (context->slavelist[slave].Ibits)
+ {
+
+ ecx_config_create_input_mappings(context, pIOmap, group, slave, &LogAddr, &BitPos);
+ diff = LogAddr - oLogAddr;
+ oLogAddr = LogAddr;
+ if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+ {
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+ {
+ currentsegment++;
+ segmentsize = diff;
+ }
+ }
+ else
+ {
+ segmentsize += diff;
+ }
+ }
+
+ ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */
+ ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , EC_TIMEOUTRET3); /* set safeop status */
+
+ if (context->slavelist[slave].blockLRW)
+ {
+ context->grouplist[group].blockLRW++;
+ }
+ context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+ }
+ }
+ if (BitPos)
+ {
+ LogAddr++;
+ oLogAddr = LogAddr;
+ BitPos = 0;
+ if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+ {
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+ {
+ currentsegment++;
+ segmentsize = 1;
+ }
+ }
+ else
+ {
+ segmentsize += 1;
+ }
+ }
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ context->grouplist[group].nsegments = currentsegment + 1;
+ context->grouplist[group].inputs = (uint8 *)(pIOmap) + context->grouplist[group].Obytes;
+ context->grouplist[group].Ibytes = LogAddr -
+ context->grouplist[group].logstartaddr -
+ context->grouplist[group].Obytes;
+ if (!group)
+ {
+ context->slavelist[0].inputs = (uint8 *)(pIOmap) + context->slavelist[0].Obytes;
+ context->slavelist[0].Ibytes = LogAddr -
+ context->grouplist[group].logstartaddr -
+ context->slavelist[0].Obytes; /* store input bytes in master record */
+ }
+
+ EC_PRINT("IOmapSize %d\n", LogAddr - context->grouplist[group].logstartaddr);
+
+ return (LogAddr - context->grouplist[group].logstartaddr);
+ }
+
+ return 0;
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+ * overlapping. NOTE: Must use this for TI ESC when using LRW.
+ *
+ * @param[in] context = context struct
+ * @param[out] pIOmap = pointer to IOmap
+ * @param[in] group = group to map, 0 = all groups
+ * @return IOmap size
+ */
+int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
+{
+ uint16 slave, configadr;
+ uint8 BitPos;
+ uint32 mLogAddr = 0;
+ uint32 siLogAddr = 0;
+ uint32 soLogAddr = 0;
+ uint32 tempLogAddr;
+ uint32 diff;
+ uint16 currentsegment = 0;
+ uint32 segmentsize = 0;
+
+ if ((*(context->slavecount) > 0) && (group < context->maxgroup))
+ {
+ EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);
+ mLogAddr = context->grouplist[group].logstartaddr;
+ siLogAddr = mLogAddr;
+ soLogAddr = mLogAddr;
+ BitPos = 0;
+ context->grouplist[group].nsegments = 0;
+ context->grouplist[group].outputsWKC = 0;
+ context->grouplist[group].inputsWKC = 0;
+
+ /* Find mappings and program syncmanagers */
+ ecx_config_find_mappings(context, group);
+
+ /* do IO mapping of slave and program FMMUs */
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ configadr = context->slavelist[slave].configadr;
+ siLogAddr = soLogAddr = mLogAddr;
+
+ if (!group || (group == context->slavelist[slave].group))
+ {
+ /* create output mapping */
+ if (context->slavelist[slave].Obits)
+ {
+
+ ecx_config_create_output_mappings(context, pIOmap, group,
+ slave, &soLogAddr, &BitPos);
+ if (BitPos)
+ {
+ soLogAddr++;
+ BitPos = 0;
+ }
+ }
+
+ /* create input mapping */
+ if (context->slavelist[slave].Ibits)
+ {
+ ecx_config_create_input_mappings(context, pIOmap, group,
+ slave, &siLogAddr, &BitPos);
+ if (BitPos)
+ {
+ siLogAddr++;
+ BitPos = 0;
+ }
+ }
+
+ tempLogAddr = (siLogAddr > soLogAddr) ? siLogAddr : soLogAddr;
+ diff = tempLogAddr - mLogAddr;
+ mLogAddr = tempLogAddr;
+
+ if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM))
+ {
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ if (currentsegment < (EC_MAXIOSEGMENTS - 1))
+ {
+ currentsegment++;
+ segmentsize = diff;
+ }
+ }
+ else
+ {
+ segmentsize += diff;
+ }
+
+ ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */
+ ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP), EC_TIMEOUTRET3); /* set safeop status */
+
+ if (context->slavelist[slave].blockLRW)
+ {
+ context->grouplist[group].blockLRW++;
+ }
+ context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;
+
+ }
+ }
+
+ context->grouplist[group].IOsegment[currentsegment] = segmentsize;
+ context->grouplist[group].nsegments = currentsegment + 1;
+ context->grouplist[group].Isegment = 0;
+ context->grouplist[group].Ioffset = 0;
+
+ context->grouplist[group].Obytes = soLogAddr - context->grouplist[group].logstartaddr;
+ context->grouplist[group].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;
+ context->grouplist[group].outputs = pIOmap;
+ context->grouplist[group].inputs = (uint8 *)pIOmap + context->grouplist[group].Obytes;
+
+ /* Move calculated inputs with OBytes offset*/
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ context->slavelist[slave].inputs += context->grouplist[group].Obytes;
+ }
+
+ if (!group)
+ {
+ /* store output bytes in master record */
+ context->slavelist[0].outputs = pIOmap;
+ context->slavelist[0].Obytes = soLogAddr - context->grouplist[group].logstartaddr;
+ context->slavelist[0].inputs = (uint8 *)pIOmap + context->slavelist[0].Obytes;
+ context->slavelist[0].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;
+ }
+
+ EC_PRINT("IOmapSize %d\n", context->grouplist[group].Obytes + context->grouplist[group].Ibytes);
+
+ return (context->grouplist[group].Obytes + context->grouplist[group].Ibytes);
+ }
+
+ return 0;
+}
+
+
+/** Recover slave.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = slave to recover
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return >0 if successful
+ */
+int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)
+{
+ int rval;
+ int wkc;
+ uint16 ADPh, configadr, readadr;
+
+ rval = 0;
+ configadr = context->slavelist[slave].configadr;
+ ADPh = (uint16)(1 - slave);
+ /* check if we found another slave than the requested */
+ readadr = 0xfffe;
+ wkc = ecx_APRD(context->port, ADPh, ECT_REG_STADR, sizeof(readadr), &readadr, timeout);
+ /* correct slave found, finished */
+ if(readadr == configadr)
+ {
+ return 1;
+ }
+ /* only try if no config address*/
+ if( (wkc > 0) && (readadr == 0))
+ {
+ /* clear possible slaves at EC_TEMPNODE */
+ ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);
+ /* set temporary node address of slave */
+ if(ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(EC_TEMPNODE) , timeout) <= 0)
+ {
+ ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);
+ return 0; /* slave fails to respond */
+ }
+
+ context->slavelist[slave].configadr = EC_TEMPNODE; /* temporary config address */
+ ecx_eeprom2master(context, slave); /* set Eeprom control to master */
+
+ /* check if slave is the same as configured before */
+ if ((ecx_FPRDw(context->port, EC_TEMPNODE, ECT_REG_ALIAS, timeout) ==
+ htoes(context->slavelist[slave].aliasadr)) &&
+ (ecx_readeeprom(context, slave, ECT_SII_ID, EC_TIMEOUTEEP) ==
+ htoel(context->slavelist[slave].eep_id)) &&
+ (ecx_readeeprom(context, slave, ECT_SII_MANUF, EC_TIMEOUTEEP) ==
+ htoel(context->slavelist[slave].eep_man)) &&
+ (ecx_readeeprom(context, slave, ECT_SII_REV, EC_TIMEOUTEEP) ==
+ htoel(context->slavelist[slave].eep_rev)))
+ {
+ rval = ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(configadr) , timeout);
+ context->slavelist[slave].configadr = configadr;
+ }
+ else
+ {
+ /* slave is not the expected one, remove config address*/
+ ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , timeout);
+ context->slavelist[slave].configadr = configadr;
+ }
+ }
+
+ return rval;
+}
+
+/** Reconfigure slave.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = slave to reconfigure
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return Slave state
+ */
+int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)
+{
+ int state, nSM, FMMUc;
+ uint16 configadr;
+
+ configadr = context->slavelist[slave].configadr;
+ if (ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_INIT) , timeout) <= 0)
+ {
+ return 0;
+ }
+ state = 0;
+ ecx_eeprom2pdi(context, slave); /* set Eeprom control to PDI */
+ /* check state change init */
+ state = ecx_statecheck(context, slave, EC_STATE_INIT, EC_TIMEOUTSTATE);
+ if(state == EC_STATE_INIT)
+ {
+ /* program all enabled SM */
+ for( nSM = 0 ; nSM < EC_MAXSM ; nSM++ )
+ {
+ if (context->slavelist[slave].SM[nSM].StartAddr)
+ {
+ ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt))),
+ sizeof(ec_smt), &context->slavelist[slave].SM[nSM], timeout);
+ }
+ }
+ ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP) , timeout);
+ state = ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* check state change pre-op */
+ if( state == EC_STATE_PRE_OP)
+ {
+ /* execute special slave configuration hook Pre-Op to Safe-OP */
+ if(context->slavelist[slave].PO2SOconfig) /* only if registered */
+ {
+ context->slavelist[slave].PO2SOconfig(slave);
+ }
+ ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , timeout); /* set safeop status */
+ state = ecx_statecheck(context, slave, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE); /* check state change safe-op */
+ /* program configured FMMU */
+ for( FMMUc = 0 ; FMMUc < context->slavelist[slave].FMMUunused ; FMMUc++ )
+ {
+ ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc)),
+ sizeof(ec_fmmut), &context->slavelist[slave].FMMU[FMMUc], timeout);
+ }
+ }
+ }
+
+ return state;
+}
+
+#ifdef EC_VER1
+/** Enumerate and init all slaves.
+ *
+ * @param[in] usetable = TRUE when using configtable to init slaves, FALSE otherwise
+ * @return Workcounter of slave discover datagram = number of slaves found
+ * @see ecx_config_init
+ */
+int ec_config_init(uint8 usetable)
+{
+ return ecx_config_init(&ecx_context, usetable);
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+ * in sequential order (legacy SOEM way).
+ *
+ * @param[out] pIOmap = pointer to IOmap
+ * @param[in] group = group to map, 0 = all groups
+ * @return IOmap size
+ * @see ecx_config_map_group
+ */
+int ec_config_map_group(void *pIOmap, uint8 group)
+{
+ return ecx_config_map_group(&ecx_context, pIOmap, group);
+}
+
+/** Map all PDOs in one group of slaves to IOmap with Outputs/Inputs
+* overlapping. NOTE: Must use this for TI ESC when using LRW.
+*
+* @param[out] pIOmap = pointer to IOmap
+* @param[in] group = group to map, 0 = all groups
+* @return IOmap size
+* @see ecx_config_overlap_map_group
+*/
+int ec_config_overlap_map_group(void *pIOmap, uint8 group)
+{
+ return ecx_config_overlap_map_group(&ecx_context, pIOmap, group);
+}
+
+/** Map all PDOs from slaves to IOmap with Outputs/Inputs
+ * in sequential order (legacy SOEM way).
+ *
+ * @param[out] pIOmap = pointer to IOmap
+ * @return IOmap size
+ */
+int ec_config_map(void *pIOmap)
+{
+ return ec_config_map_group(pIOmap, 0);
+}
+
+/** Map all PDOs from slaves to IOmap with Outputs/Inputs
+* overlapping. NOTE: Must use this for TI ESC when using LRW.
+*
+* @param[out] pIOmap = pointer to IOmap
+* @return IOmap size
+*/
+int ec_config_overlap_map(void *pIOmap)
+{
+ return ec_config_overlap_map_group(pIOmap, 0);
+}
+
+/** Enumerate / map and init all slaves.
+ *
+ * @param[in] usetable = TRUE when using configtable to init slaves, FALSE otherwise
+ * @param[out] pIOmap = pointer to IOmap
+ * @return Workcounter of slave discover datagram = number of slaves found
+ */
+int ec_config(uint8 usetable, void *pIOmap)
+{
+ int wkc;
+ wkc = ec_config_init(usetable);
+ if (wkc)
+ {
+ ec_config_map(pIOmap);
+ }
+ return wkc;
+}
+
+/** Enumerate / map and init all slaves.
+*
+* @param[in] usetable = TRUE when using configtable to init slaves, FALSE otherwise
+* @param[out] pIOmap = pointer to IOmap
+* @return Workcounter of slave discover datagram = number of slaves found
+*/
+int ec_config_overlap(uint8 usetable, void *pIOmap)
+{
+ int wkc;
+ wkc = ec_config_init(usetable);
+ if (wkc)
+ {
+ ec_config_overlap_map(pIOmap);
+ }
+ return wkc;
+}
+
+/** Recover slave.
+ *
+ * @param[in] slave = slave to recover
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return >0 if successful
+ * @see ecx_recover_slave
+ */
+int ec_recover_slave(uint16 slave, int timeout)
+{
+ return ecx_recover_slave(&ecx_context, slave, timeout);
+}
+
+/** Reconfigure slave.
+ *
+ * @param[in] slave = slave to reconfigure
+ * @param[in] timeout = local timeout f.e. EC_TIMEOUTRET3
+ * @return Slave state
+ * @see ecx_reconfig_slave
+ */
+int ec_reconfig_slave(uint16 slave, int timeout)
+{
+ return ecx_reconfig_slave(&ecx_context, slave, timeout);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatconfig.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,44 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatconfig.c
+ */
+
+#ifndef _ethercatconfig_
+#define _ethercatconfig_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define EC_NODEOFFSET 0x1000
+#define EC_TEMPNODE 0xffff
+
+#ifdef EC_VER1
+int ec_config_init(uint8 usetable);
+int ec_config_map(void *pIOmap);
+int ec_config_overlap_map(void *pIOmap);
+int ec_config_map_group(void *pIOmap, uint8 group);
+int ec_config_overlap_map_group(void *pIOmap, uint8 group);
+int ec_config(uint8 usetable, void *pIOmap);
+int ec_config_overlap(uint8 usetable, void *pIOmap);
+int ec_recover_slave(uint16 slave, int timeout);
+int ec_reconfig_slave(uint16 slave, int timeout);
+#endif
+
+int ecx_config_init(ecx_contextt *context, uint8 usetable);
+int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
+int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
+int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout);
+int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatconfiglist.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,66 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * DEPRECATED Configuration list of known EtherCAT slave devices.
+ *
+ * If a slave is found in this list it is configured according to the parameters
+ * in the list. Otherwise the configuration info is read directly from the slave
+ * EEPROM (SII or Slave Information Interface).
+ */
+
+#ifndef _ethercatconfiglist_
+#define _ethercatconfiglist_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*
+ explanation of dev:
+ 1: static device with no IO mapping ie EK1100
+ 2: input device no mailbox ie simple IO device
+ 3: output device no mailbox
+ 4: input device with mailbox configuration
+ 5: output device with mailbox configuration
+ 6: input/output device no mailbox
+ 7: input.output device with mailbox configuration
+*/
+#define EC_CONFIGEND 0xffffffff
+
+ec_configlist_t ec_configlist[] = {
+ {/*Man=*/0x00000000,/*ID=*/0x00000000,/*Name=*/"" ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x044c2c52,/*Name=*/"EK1100" ,/*dtype=*/1,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x03ea3052,/*Name=*/"EL1002" ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x03ec3052,/*Name=*/"EL1004" ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x03f43052,/*Name=*/"EL1012" ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x03f63052,/*Name=*/"EL1014" ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x03fa3052,/*Name=*/"EL1018" ,/*dtype=*/2,/*Ibits=*/ 8,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x07d23052,/*Name=*/"EL2002" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 2,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x07d43052,/*Name=*/"EL2004" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 4,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x07d83052,/*Name=*/"EL2008" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 8,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x07f03052,/*Name=*/"EL2032" ,/*dtype=*/6,/*Ibits=*/ 2,/*Obits=*/ 2,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x0c1e3052,/*Name=*/"EL3102" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+ {/*Man=*/0x00000002,/*ID=*/0x0c283052,/*Name=*/"EL3112" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+ {/*Man=*/0x00000002,/*ID=*/0x0c323052,/*Name=*/"EL3122" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+ {/*Man=*/0x00000002,/*ID=*/0x0c463052,/*Name=*/"EL3142" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+ {/*Man=*/0x00000002,/*ID=*/0x0c503052,/*Name=*/"EL3152" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+ {/*Man=*/0x00000002,/*ID=*/0x0c5a3052,/*Name=*/"EL3162" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1},
+ {/*Man=*/0x00000002,/*ID=*/0x0fc03052,/*Name=*/"EL4032" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1100,/*SM2f*/0x00010024,/*SM3a*/0x1180,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x10063052,/*Name=*/"EL4102" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x10103052,/*Name=*/"EL4112" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x101a3052,/*Name=*/"EL4122" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x10243052,/*Name=*/"EL4132" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0},
+ {/*Man=*/0x00000002,/*ID=*/0x13ed3052,/*Name=*/"EL5101" ,/*dtype=*/7,/*Ibits=*/40,/*Obits=*/24,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/1,/*FM1ac*/1},
+ {/*Man=*/EC_CONFIGEND,/*ID=*/0x00000000,/*Name=*/"" ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatdc.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,448 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Distributed Clock EtherCAT functions.
+ *
+ */
+#include "oshw.h"
+#include "osal.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatdc.h"
+
+#define PORTM0 0x01
+#define PORTM1 0x02
+#define PORTM2 0x04
+#define PORTM3 0x08
+
+/** 1st sync pulse delay in ns here 100ms */
+#define SyncDelay ((int32)100000000)
+
+/**
+ * Set DC of slave to fire sync0 at CyclTime interval with CyclShift offset.
+ *
+ * @param[in] context = context struct
+ * @param [in] slave Slave number.
+ * @param [in] act TRUE = active, FALSE = deactivated
+ * @param [in] CyclTime Cycltime in ns.
+ * @param [in] CyclShift CyclShift in ns.
+ */
+void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
+{
+ uint8 h, RA;
+ uint16 slaveh;
+ int64 t, t1;
+ int32 tc;
+
+ slaveh = context->slavelist[slave].configadr;
+ RA = 0;
+
+ /* stop cyclic operation, ready for next trigger */
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
+ if (act)
+ {
+ RA = 1 + 2; /* act cyclic operation and sync0, sync1 deactivated */
+ }
+ h = 0;
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
+ t1 = 0;
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
+ t1 = etohll(t1);
+
+ /* Calculate first trigger time, always a whole multiple of CyclTime rounded up
+ plus the shifttime (can be negative)
+ This insures best synchronization between slaves, slaves with the same CyclTime
+ will sync at the same moment (you can use CyclShift to shift the sync) */
+ if (CyclTime > 0)
+ {
+ t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift;
+ }
+ else
+ {
+ t = t1 + SyncDelay + CyclShift;
+ /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
+ }
+ t = htoell(t);
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
+ tc = htoel(CyclTime);
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
+
+ // update ec_slave state
+ context->slavelist[slave].DCactive = (uint8)act;
+ context->slavelist[slave].DCshift = CyclShift;
+ context->slavelist[slave].DCcycle = CyclTime;
+}
+
+/**
+ * Set DC of slave to fire sync0 and sync1 at CyclTime interval with CyclShift offset.
+ *
+ * @param[in] context = context struct
+ * @param [in] slave Slave number.
+ * @param [in] act TRUE = active, FALSE = deactivated
+ * @param [in] CyclTime0 Cycltime SYNC0 in ns.
+ * @param [in] CyclTime1 Cycltime SYNC1 in ns. This time is a delta time in relation to
+ the SYNC0 fire. If CylcTime1 = 0 then SYNC1 fires a the same time
+ as SYNC0.
+ * @param [in] CyclShift CyclShift in ns.
+ */
+void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
+{
+ uint8 h, RA;
+ uint16 slaveh;
+ int64 t, t1;
+ int32 tc;
+ uint32 TrueCyclTime;
+
+ /* Sync1 can be used as a multiple of Sync0, use true cycle time */
+ TrueCyclTime = ((CyclTime1 / CyclTime0) + 1) * CyclTime0;
+
+ slaveh = context->slavelist[slave].configadr;
+ RA = 0;
+
+ /* stop cyclic operation, ready for next trigger */
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
+ if (act)
+ {
+ RA = 1 + 2 + 4; /* act cyclic operation and sync0 + sync1 */
+ }
+ h = 0;
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
+ t1 = 0;
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
+ t1 = etohll(t1);
+
+ /* Calculate first trigger time, always a whole multiple of TrueCyclTime rounded up
+ plus the shifttime (can be negative)
+ This insures best synchronization between slaves, slaves with the same CyclTime
+ will sync at the same moment (you can use CyclShift to shift the sync) */
+ if (CyclTime0 > 0)
+ {
+ t = ((t1 + SyncDelay) / TrueCyclTime) * TrueCyclTime + TrueCyclTime + CyclShift;
+ }
+ else
+ {
+ t = t1 + SyncDelay + CyclShift;
+ /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
+ }
+ t = htoell(t);
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
+ tc = htoel(CyclTime0);
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
+ tc = htoel(CyclTime1);
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
+
+ // update ec_slave state
+ context->slavelist[slave].DCactive = (uint8)act;
+ context->slavelist[slave].DCshift = CyclShift;
+ context->slavelist[slave].DCcycle = CyclTime0;
+}
+
+/* latched port time of slave */
+static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port)
+{
+ int32 ts;
+ switch (port)
+ {
+ case 0:
+ ts = context->slavelist[slave].DCrtA;
+ break;
+ case 1:
+ ts = context->slavelist[slave].DCrtB;
+ break;
+ case 2:
+ ts = context->slavelist[slave].DCrtC;
+ break;
+ case 3:
+ ts = context->slavelist[slave].DCrtD;
+ break;
+ default:
+ ts = 0;
+ break;
+ }
+ return ts;
+}
+
+/* calculate previous active port of a slave */
+static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port)
+{
+ uint8 pport = port;
+ uint8 aport = context->slavelist[slave].activeports;
+ switch(port)
+ {
+ case 0:
+ if(aport & PORTM2)
+ pport = 2;
+ else if (aport & PORTM1)
+ pport = 1;
+ else if (aport & PORTM3)
+ pport = 3;
+ break;
+ case 1:
+ if(aport & PORTM3)
+ pport = 3;
+ else if (aport & PORTM0)
+ pport = 0;
+ else if (aport & PORTM2)
+ pport = 2;
+ break;
+ case 2:
+ if(aport & PORTM1)
+ pport = 1;
+ else if (aport & PORTM3)
+ pport = 3;
+ else if (aport & PORTM0)
+ pport = 0;
+ break;
+ case 3:
+ if(aport & PORTM0)
+ pport = 0;
+ else if (aport & PORTM2)
+ pport = 2;
+ else if (aport & PORTM1)
+ pport = 1;
+ break;
+ }
+ return pport;
+}
+
+/* search unconsumed ports in parent, consume and return first open port */
+static uint8 ecx_parentport(ecx_contextt *context, uint16 parent)
+{
+ uint8 parentport = 0;
+ uint8 b;
+ /* search order is important, here 3 - 1 - 2 - 0 */
+ b = context->slavelist[parent].consumedports;
+ if (b & PORTM3)
+ {
+ parentport = 3;
+ b &= (uint8)~PORTM3;
+ }
+ else if (b & PORTM1)
+ {
+ parentport = 1;
+ b &= (uint8)~PORTM1;
+ }
+ else if (b & PORTM2)
+ {
+ parentport = 2;
+ b &= (uint8)~PORTM2;
+ }
+ else if (b & PORTM0)
+ {
+ parentport = 0;
+ b &= (uint8)~PORTM0;
+ }
+ context->slavelist[parent].consumedports = b;
+ return parentport;
+}
+
+/**
+ * Locate DC slaves, measure propagation delays.
+ *
+ * @param[in] context = context struct
+ * @return boolean if slaves are found with DC
+ */
+boolean ecx_configdc(ecx_contextt *context)
+{
+ uint16 i, slaveh, parent, child;
+ uint16 parenthold = 0;
+ uint16 prevDCslave = 0;
+ int32 ht, dt1, dt2, dt3;
+ int64 hrt;
+ uint8 entryport;
+ int8 nlist;
+ int8 plist[4];
+ int32 tlist[4];
+ ec_timet mastertime;
+ uint64 mastertime64;
+
+ context->slavelist[0].hasdc = FALSE;
+ context->grouplist[0].hasdc = FALSE;
+ ht = 0;
+
+ ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET); /* latch DCrecvTimeA of all slaves */
+ mastertime = osal_current_time();
+ mastertime.sec -= 946684800UL; /* EtherCAT uses 2000-01-01 as epoch start instead of 1970-01-01 */
+ mastertime64 = (((uint64)mastertime.sec * 1000000) + (uint64)mastertime.usec) * 1000;
+ for (i = 1; i <= *(context->slavecount); i++)
+ {
+ context->slavelist[i].consumedports = context->slavelist[i].activeports;
+ if (context->slavelist[i].hasdc)
+ {
+ if (!context->slavelist[0].hasdc)
+ {
+ context->slavelist[0].hasdc = TRUE;
+ context->slavelist[0].DCnext = i;
+ context->slavelist[i].DCprevious = 0;
+ context->grouplist[context->slavelist[i].group].hasdc = TRUE;
+ context->grouplist[context->slavelist[i].group].DCnext = i;
+ }
+ else
+ {
+ context->slavelist[prevDCslave].DCnext = i;
+ context->slavelist[i].DCprevious = prevDCslave;
+ }
+ /* this branch has DC slave so remove parenthold */
+ parenthold = 0;
+ prevDCslave = i;
+ slaveh = context->slavelist[i].configadr;
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);
+ context->slavelist[i].DCrtA = etohl(ht);
+ /* 64bit latched DCrecvTimeA of each specific slave */
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);
+ /* use it as offset in order to set local time around 0 + mastertime */
+ hrt = htoell(-etohll(hrt) + mastertime64);
+ /* save it in the offset register */
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET);
+ context->slavelist[i].DCrtB = etohl(ht);
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET);
+ context->slavelist[i].DCrtC = etohl(ht);
+ (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET);
+ context->slavelist[i].DCrtD = etohl(ht);
+
+ /* make list of active ports and their time stamps */
+ nlist = 0;
+ if (context->slavelist[i].activeports & PORTM0)
+ {
+ plist[nlist] = 0;
+ tlist[nlist] = context->slavelist[i].DCrtA;
+ nlist++;
+ }
+ if (context->slavelist[i].activeports & PORTM3)
+ {
+ plist[nlist] = 3;
+ tlist[nlist] = context->slavelist[i].DCrtD;
+ nlist++;
+ }
+ if (context->slavelist[i].activeports & PORTM1)
+ {
+ plist[nlist] = 1;
+ tlist[nlist] = context->slavelist[i].DCrtB;
+ nlist++;
+ }
+ if (context->slavelist[i].activeports & PORTM2)
+ {
+ plist[nlist] = 2;
+ tlist[nlist] = context->slavelist[i].DCrtC;
+ nlist++;
+ }
+ /* entryport is port with the lowest timestamp */
+ entryport = 0;
+ if((nlist > 1) && (tlist[1] < tlist[entryport]))
+ {
+ entryport = 1;
+ }
+ if((nlist > 2) && (tlist[2] < tlist[entryport]))
+ {
+ entryport = 2;
+ }
+ if((nlist > 3) && (tlist[3] < tlist[entryport]))
+ {
+ entryport = 3;
+ }
+ entryport = plist[entryport];
+ context->slavelist[i].entryport = entryport;
+ /* consume entryport from activeports */
+ context->slavelist[i].consumedports &= (uint8)~(1 << entryport);
+
+ /* finding DC parent of current */
+ parent = i;
+ do
+ {
+ child = parent;
+ parent = context->slavelist[parent].parent;
+ }
+ while (!((parent == 0) || (context->slavelist[parent].hasdc)));
+ /* only calculate propagation delay if slave is not the first */
+ if (parent > 0)
+ {
+ /* find port on parent this slave is connected to */
+ context->slavelist[i].parentport = ecx_parentport(context, parent);
+ if (context->slavelist[parent].topology == 1)
+ {
+ context->slavelist[i].parentport = context->slavelist[parent].entryport;
+ }
+
+ dt1 = 0;
+ dt2 = 0;
+ /* delta time of (parentport - 1) - parentport */
+ /* note: order of ports is 0 - 3 - 1 -2 */
+ /* non active ports are skipped */
+ dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
+ ecx_porttime(context, parent,
+ ecx_prevport(context, parent, context->slavelist[i].parentport));
+ /* current slave has children */
+ /* those children's delays need to be subtracted */
+ if (context->slavelist[i].topology > 1)
+ {
+ dt1 = ecx_porttime(context, i,
+ ecx_prevport(context, i, context->slavelist[i].entryport)) -
+ ecx_porttime(context, i, context->slavelist[i].entryport);
+ }
+ /* we are only interested in positive difference */
+ if (dt1 > dt3) dt1 = -dt1;
+ /* current slave is not the first child of parent */
+ /* previous child's delays need to be added */
+ if ((child - parent) > 1)
+ {
+ dt2 = ecx_porttime(context, parent,
+ ecx_prevport(context, parent, context->slavelist[i].parentport)) -
+ ecx_porttime(context, parent, context->slavelist[parent].entryport);
+ }
+ if (dt2 < 0) dt2 = -dt2;
+
+ /* calculate current slave delay from delta times */
+ /* assumption : forward delay equals return delay */
+ context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
+ context->slavelist[parent].pdelay;
+ ht = htoel(context->slavelist[i].pdelay);
+ /* write propagation delay*/
+ (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);
+ }
+ }
+ else
+ {
+ context->slavelist[i].DCrtA = 0;
+ context->slavelist[i].DCrtB = 0;
+ context->slavelist[i].DCrtC = 0;
+ context->slavelist[i].DCrtD = 0;
+ parent = context->slavelist[i].parent;
+ /* if non DC slave found on first position on branch hold root parent */
+ if ( (parent > 0) && (context->slavelist[parent].topology > 2))
+ parenthold = parent;
+ /* if branch has no DC slaves consume port on root parent */
+ if ( parenthold && (context->slavelist[i].topology == 1))
+ {
+ ecx_parentport(context, parenthold);
+ parenthold = 0;
+ }
+ }
+ }
+
+ return context->slavelist[0].hasdc;
+}
+
+#ifdef EC_VER1
+void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)
+{
+ ecx_dcsync0(&ecx_context, slave, act, CyclTime, CyclShift);
+}
+
+void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
+{
+ ecx_dcsync01(&ecx_context, slave, act, CyclTime0, CyclTime1, CyclShift);
+}
+
+boolean ec_configdc(void)
+{
+ return ecx_configdc(&ecx_context);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatdc.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatdc.c
+ */
+
+#ifndef _EC_ECATDC_H
+#define _EC_ECATDC_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef EC_VER1
+boolean ec_configdc();
+void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift);
+void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift);
+#endif
+
+boolean ecx_configdc(ecx_contextt *context);
+void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift);
+void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EC_ECATDC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercateoe.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,640 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Ethernet over EtherCAT (EoE) module.
+ *
+ * Set / Get IP functions
+ * Blocking send/receive Ethernet Frame
+ * Read incoming EoE fragment to Ethernet Frame
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercat.h"
+
+ /** EoE utility function to convert uint32 to eoe ip bytes.
+ * @param[in] ip = ip in uint32
+ * @param[out] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet
+ */
+static void EOE_ip_uint32_to_byte(eoe_ip4_addr_t * ip, uint8_t * byte_ip)
+{
+ byte_ip[3] = eoe_ip4_addr1(ip); /* 1st octet */
+ byte_ip[2] = eoe_ip4_addr2(ip); /* 2nd octet */
+ byte_ip[1] = eoe_ip4_addr3(ip); /* 3ed octet */
+ byte_ip[0] = eoe_ip4_addr4(ip); /* 4th octet */
+}
+
+/** EoE utility function to convert eoe ip bytes to uint32.
+* @param[in] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet
+* @param[out] ip = ip in uint32
+*/
+static void EOE_ip_byte_to_uint32(uint8_t * byte_ip, eoe_ip4_addr_t * ip)
+{
+ EOE_IP4_ADDR_TO_U32(ip,
+ byte_ip[3], /* 1st octet */
+ byte_ip[2], /* 2nd octet */
+ byte_ip[1], /* 3ed octet */
+ byte_ip[0]); /* 4th octet */
+}
+
+/** EoE fragment data handler hook. Should not block.
+*
+* @param[in] context = context struct
+* @param[in] hook = Pointer to hook function.
+* @return 1
+*/
+int ecx_EOEdefinehook(ecx_contextt *context, void *hook)
+{
+ context->EOEhook = hook;
+ return 1;
+}
+
+/** EoE EOE set IP, blocking. Waits for response from the slave.
+*
+* @param[in] context = Context struct
+* @param[in] slave = Slave number
+* @param[in] port = Port number on slave if applicable
+* @param[in] ipparam = IP parameter data to be sent
+* @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave response or returned result code
+*/
+int ecx_EOEsetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout)
+{
+ ec_EOEt *EOEp, *aEOEp;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint16 frameinfo1, result;
+ uint8 cnt, data_offset;
+ uint8 flags = 0;
+ int wkc;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aEOEp = (ec_EOEt *)&MbxIn;
+ EOEp = (ec_EOEt *)&MbxOut;
+ EOEp->mbxheader.address = htoes(0x0000);
+ EOEp->mbxheader.priority = 0x00;
+ data_offset = EOE_PARAM_OFFSET;
+
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+
+ EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */
+
+ EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_INIT_REQ) |
+ EOE_HDR_FRAME_PORT_SET(port) |
+ EOE_HDR_LAST_FRAGMENT);
+ EOEp->frameinfo2 = 0;
+
+ /* The EoE frame will include "empty" IP/DNS entries, makes wireshark happy.
+ * Specification say they are optional, TwinCAT include empty entries.
+ */
+ if (ipparam->mac_set)
+ {
+ flags |= EOE_PARAM_MAC_INCLUDE;
+ memcpy(&EOEp->data[data_offset], ipparam->mac.addr, EOE_ETHADDR_LENGTH);
+ }
+ data_offset += EOE_ETHADDR_LENGTH;
+ if (ipparam->ip_set)
+ {
+ flags |= EOE_PARAM_IP_INCLUDE;
+ EOE_ip_uint32_to_byte(&ipparam->ip, &EOEp->data[data_offset]);
+ }
+ data_offset += 4;
+ if (ipparam->subnet_set)
+ {
+ flags |= EOE_PARAM_SUBNET_IP_INCLUDE;
+ EOE_ip_uint32_to_byte(&ipparam->subnet, &EOEp->data[data_offset]);
+ }
+ data_offset += 4;
+ if (ipparam->default_gateway_set)
+ {
+ flags |= EOE_PARAM_DEFAULT_GATEWAY_INCLUDE;
+ EOE_ip_uint32_to_byte(&ipparam->default_gateway, &EOEp->data[data_offset]);
+ }
+ data_offset += 4;
+ if (ipparam->dns_ip_set)
+ {
+ flags |= EOE_PARAM_DNS_IP_INCLUDE;
+ EOE_ip_uint32_to_byte(&ipparam->dns_ip, &EOEp->data[data_offset]);
+ }
+ data_offset += 4;
+ if (ipparam->dns_name_set)
+ {
+ flags |= EOE_PARAM_DNS_NAME_INCLUDE;
+ memcpy(&EOEp->data[data_offset], (void *)ipparam->dns_name, EOE_DNS_NAME_LENGTH);
+ }
+ data_offset += EOE_DNS_NAME_LENGTH;
+
+ EOEp->mbxheader.length = htoes(EOE_PARAM_OFFSET + data_offset);
+ EOEp->data[0] = flags;
+
+ /* send EoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be FoE */
+ if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+ {
+ frameinfo1 = etohs(aEOEp->frameinfo1);
+ result = etohs(aEOEp->result);
+ if ((EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_INIT_RESP) ||
+ (result != EOE_RESULT_SUCCESS))
+ {
+ wkc = -result;
+ }
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ }
+ }
+ return wkc;
+}
+
+/** EoE EOE get IP, blocking. Waits for response from the slave.
+*
+* @param[in] context = Context struct
+* @param[in] slave = Slave number
+* @param[in] port = Port number on slave if applicable
+* @param[out] ipparam = IP parameter data retrived from slave
+* @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave response or returned result code
+*/
+int ecx_EOEgetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout)
+{
+ ec_EOEt *EOEp, *aEOEp;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint16 frameinfo1, eoedatasize;
+ uint8 cnt, data_offset;
+ uint8 flags = 0;
+ int wkc;
+
+ /* Empty slave out mailbox if something is in. Timout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aEOEp = (ec_EOEt *)&MbxIn;
+ EOEp = (ec_EOEt *)&MbxOut;
+ EOEp->mbxheader.address = htoes(0x0000);
+ EOEp->mbxheader.priority = 0x00;
+ data_offset = EOE_PARAM_OFFSET;
+
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+
+ EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */
+
+ EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_GET_IP_PARAM_REQ) |
+ EOE_HDR_FRAME_PORT_SET(port) |
+ EOE_HDR_LAST_FRAGMENT);
+ EOEp->frameinfo2 = 0;
+
+ EOEp->mbxheader.length = htoes(0x0004);
+ EOEp->data[0] = flags;
+
+ /* send EoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be FoE */
+ if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+ {
+ frameinfo1 = etohs(aEOEp->frameinfo1);
+ eoedatasize = etohs(aEOEp->mbxheader.length) - 0x0004;
+ if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_GET_IP_PARAM_RESP)
+ {
+ wkc = -EOE_RESULT_UNSUPPORTED_FRAME_TYPE;
+ }
+ else
+ {
+ /* The EoE frame will include "empty" IP/DNS entries, makes
+ * wireshark happy. Specification say they are optional, TwinCAT
+ * include empty entries.
+ */
+ flags = aEOEp->data[0];
+ if (flags & EOE_PARAM_MAC_INCLUDE)
+ {
+ memcpy(ipparam->mac.addr,
+ &aEOEp->data[data_offset],
+ EOE_ETHADDR_LENGTH);
+ ipparam->mac_set = 1;
+ }
+ data_offset += EOE_ETHADDR_LENGTH;
+ if (flags & EOE_PARAM_IP_INCLUDE)
+ {
+ EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+ &ipparam->ip);
+ ipparam->ip_set = 1;
+ }
+ data_offset += 4;
+ if (flags & EOE_PARAM_SUBNET_IP_INCLUDE)
+ {
+ EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+ &ipparam->subnet);
+ ipparam->subnet_set = 1;
+ }
+ data_offset += 4;
+ if (flags & EOE_PARAM_DEFAULT_GATEWAY_INCLUDE)
+ {
+ EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+ &ipparam->default_gateway);
+ ipparam->default_gateway_set = 1;
+ }
+ data_offset += 4;
+ if (flags & EOE_PARAM_DNS_IP_INCLUDE)
+ {
+ EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
+ &ipparam->dns_ip);
+ ipparam->dns_ip_set = 1;
+ }
+ data_offset += 4;
+ if (flags & EOE_PARAM_DNS_NAME_INCLUDE)
+ {
+ uint16_t dns_len;
+ if ((eoedatasize - data_offset) < EOE_DNS_NAME_LENGTH)
+ {
+ dns_len = (eoedatasize - data_offset);
+ }
+ else
+ {
+ dns_len = EOE_DNS_NAME_LENGTH;
+ }
+ /* Assume ZERO terminated string */
+ memcpy(ipparam->dns_name, &aEOEp->data[data_offset], dns_len);
+ ipparam->dns_name_set = 1;
+ }
+ data_offset += EOE_DNS_NAME_LENGTH;
+ /* Something os not correct, flag the error */
+ if(data_offset > eoedatasize)
+ {
+ wkc = -EC_ERR_TYPE_MBX_ERROR;
+ }
+ }
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ }
+ }
+ return wkc;
+}
+
+/** EoE ethernet buffer write, blocking.
+*
+* If the buffer is larger than the mailbox size then the buffer is sent in
+* several fragments. The function will split the buf data in fragments and
+* send them to the slave one by one.
+*
+* @param[in] context = context struct
+* @param[in] slave = Slave number
+* @param[in] port = Port number on slave if applicable
+* @param[in] psize = Size in bytes of parameter buffer.
+* @param[in] p = Pointer to parameter buffer
+* @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave transmission
+*/
+int ecx_EOEsend(ecx_contextt *context, uint16 slave, uint8 port, int psize, void *p, int timeout)
+{
+ ec_EOEt *EOEp;
+ ec_mbxbuft MbxOut;
+ uint16 frameinfo1, frameinfo2;
+ uint16 txframesize, txframeoffset;
+ uint8 cnt, txfragmentno;
+ boolean NotLast;
+ int wkc, maxdata;
+ const uint8 * buf = p;
+ static uint8_t txframeno = 0;
+
+ ec_clearmbx(&MbxOut);
+ EOEp = (ec_EOEt *)&MbxOut;
+ EOEp->mbxheader.address = htoes(0x0000);
+ EOEp->mbxheader.priority = 0x00;
+ /* data section=mailbox size - 6 mbx - 4 EoEh */
+ maxdata = context->slavelist[slave].mbx_l - 0x0A;
+ txframesize = psize;
+ txfragmentno = 0;
+ txframeoffset = 0;
+ NotLast = TRUE;
+
+ do
+ {
+ txframesize = psize - txframeoffset;
+ if (txframesize > maxdata)
+ {
+ /* Adjust to even 32-octect blocks */
+ txframesize = ((maxdata >> 5) << 5);
+ }
+
+ if (txframesize == (psize - txframeoffset))
+ {
+ frameinfo1 = (EOE_HDR_LAST_FRAGMENT_SET(1) | EOE_HDR_FRAME_PORT_SET(port));
+ NotLast = FALSE;
+ }
+ else
+ {
+ frameinfo1 = EOE_HDR_FRAME_PORT_SET(port);
+ }
+
+ frameinfo2 = EOE_HDR_FRAG_NO_SET(txfragmentno);
+ if (txfragmentno > 0)
+ {
+ frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET((txframeoffset >> 5)));
+ }
+ else
+ {
+ frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET(((psize + 31) >> 5)));
+ txframeno++;
+ }
+ frameinfo2 = frameinfo2 | EOE_HDR_FRAME_NO_SET(txframeno);
+
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+
+ EOEp->mbxheader.length = htoes(4 + txframesize); /* no timestamp */
+ EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */
+
+ EOEp->frameinfo1 = htoes(frameinfo1);
+ EOEp->frameinfo2 = htoes(frameinfo2);
+
+ memcpy(EOEp->data, &buf[txframeoffset], txframesize);
+
+ /* send EoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, timeout);
+ if ((NotLast == TRUE) && (wkc > 0))
+ {
+ txframeoffset += txframesize;
+ txfragmentno++;
+ }
+ } while ((NotLast == TRUE) && (wkc > 0));
+
+ return wkc;
+}
+
+
+/** EoE ethernet buffer read, blocking.
+*
+* If the buffer is larger than the mailbox size then the buffer is received
+* by several fragments. The function will assamble the fragments into
+* a complete Ethernet buffer.
+*
+* @param[in] context = context struct
+* @param[in] slave = Slave number
+* @param[in] port = Port number on slave if applicable
+* @param[in/out] psize = Size in bytes of parameter buffer.
+* @param[in] p = Pointer to parameter buffer
+* @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+* @return Workcounter from last slave response or error code
+*/
+int ecx_EOErecv(ecx_contextt *context, uint16 slave, uint8 port, int * psize, void *p, int timeout)
+{
+ ec_EOEt *aEOEp;
+ ec_mbxbuft MbxIn;
+ uint16 frameinfo1, frameinfo2, rxframesize, rxframeoffset, eoedatasize;
+ uint8 rxfragmentno, rxframeno;
+ boolean NotLast;
+ int wkc, buffersize;
+ uint8 * buf = p;
+
+ ec_clearmbx(&MbxIn);
+ aEOEp = (ec_EOEt *)&MbxIn;
+ NotLast = TRUE;
+ buffersize = *psize;
+ rxfragmentno = 0;
+
+ /* Hang for a while if nothing is in */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+
+ while ((wkc > 0) && (NotLast == TRUE))
+ {
+ /* slave response should be FoE */
+ if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+ {
+
+ eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004;
+ frameinfo1 = etohs(aEOEp->frameinfo1);
+ frameinfo2 = etohs(aEOEp->frameinfo2);
+
+ if (rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2))
+ {
+ if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0)
+ {
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ /* Exit here*/
+ break;
+ }
+ }
+
+ if (rxfragmentno == 0)
+ {
+ rxframeoffset = 0;
+ rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2);
+ rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5);
+ if (rxframesize > buffersize)
+ {
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ /* Exit here*/
+ break;
+ }
+ if (port != EOE_HDR_FRAME_PORT_GET(frameinfo1))
+ {
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ /* Exit here*/
+ break;
+ }
+ }
+ else
+ {
+ if (rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2))
+ {
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ /* Exit here*/
+ break;
+ }
+ else if (rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5))
+ {
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ /* Exit here*/
+ break;
+ }
+ }
+
+ if ((rxframeoffset + eoedatasize) <= buffersize)
+ {
+ memcpy(&buf[rxframeoffset], aEOEp->data, eoedatasize);
+ rxframeoffset += eoedatasize;
+ rxfragmentno++;
+ }
+
+ if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1))
+ {
+ /* Remove timestamp */
+ if (EOE_HDR_TIME_APPEND_GET(frameinfo1))
+ {
+ rxframeoffset -= 4;
+ }
+ NotLast = FALSE;
+ *psize = rxframeoffset;
+ }
+ else
+ {
+ /* Hang for a while if nothing is in */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ }
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ }
+ return wkc;
+}
+
+/** EoE mailbox fragment read
+*
+* Will take the data in incoming mailbox buffer and copy to destination
+* Ethernet frame buffer at given offset and update current fragment variables
+*
+* @param[in] MbxIn = Received mailbox containing fragment data
+* @param[in/out] rxfragmentno = Fragment number
+* @param[in/out] rxframesize = Frame size
+* @param[in/out] rxframeoffset = Frame offset
+* @param[in/out] rxframeno = Frame number
+* @param[in/out] psize = Size in bytes of frame buffer.
+* @param[out] p = Pointer to frame buffer
+* @return 0= if fragment OK, >0 if last fragment, <0 on error
+*/
+int ecx_EOEreadfragment(
+ ec_mbxbuft * MbxIn,
+ uint8 * rxfragmentno,
+ uint16 * rxframesize,
+ uint16 * rxframeoffset,
+ uint16 * rxframeno,
+ int * psize,
+ void *p)
+{
+ uint16 frameinfo1, frameinfo2, eoedatasize;
+ int wkc;
+ ec_EOEt * aEOEp;
+ uint8 * buf;
+
+ aEOEp = (ec_EOEt *)MbxIn;
+ buf = p;
+ wkc = 0;
+
+ /* slave response should be EoE */
+ if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE)
+ {
+ eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004;
+ frameinfo1 = etohs(aEOEp->frameinfo1);
+ frameinfo2 = etohs(aEOEp->frameinfo2);
+
+ /* Retrive fragment number, is it what we expect? */
+ if (*rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2))
+ {
+ /* If expected fragment number is not 0, reset working variables */
+ if (*rxfragmentno != 0)
+ {
+ *rxfragmentno = 0;
+ *rxframesize = 0;
+ *rxframeoffset = 0;
+ *rxframeno = 0;
+ }
+
+ /* If incoming fragment number is not 0 we can't recover, exit */
+ if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0)
+ {
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ return wkc;
+ }
+ }
+
+ /* Is it a new frame?*/
+ if (*rxfragmentno == 0)
+ {
+ *rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5);
+ *rxframeoffset = 0;
+ *rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2);
+ }
+ else
+ {
+ /* If we're inside a frame, make sure it is the same */
+ if (*rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2))
+ {
+ *rxfragmentno = 0;
+ *rxframesize = 0;
+ *rxframeoffset = 0;
+ *rxframeno = 0;
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ return wkc;
+ }
+ else if (*rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5))
+ {
+ *rxfragmentno = 0;
+ *rxframesize = 0;
+ *rxframeoffset = 0;
+ *rxframeno = 0;
+ wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA;
+ return wkc;
+ }
+ }
+
+ /* Make sure we're inside expected frame size */
+ if (((*rxframeoffset + eoedatasize) <= *rxframesize) &&
+ ((*rxframeoffset + eoedatasize) <= *psize))
+ {
+ memcpy(&buf[*rxframeoffset], aEOEp->data, eoedatasize);
+ *rxframeoffset += eoedatasize;
+ *rxfragmentno += 1;
+ }
+
+ /* Is it the last fragment */
+ if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1))
+ {
+ /* Remove timestamp */
+ if (EOE_HDR_TIME_APPEND_GET(frameinfo1))
+ {
+ *rxframeoffset -= 4;
+ }
+ *psize = *rxframeoffset;
+ *rxfragmentno = 0;
+ *rxframesize = 0;
+ *rxframeoffset = 0;
+ *rxframeno = 0;
+ wkc = 1;
+ }
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ return wkc;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercateoe.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,206 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatfoe.c
+ */
+
+#ifndef _ethercateoe_
+#define _ethercateoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <ethercattype.h>
+
+/** DNS length according to ETG 1000.6 */
+#define EOE_DNS_NAME_LENGTH 32
+/** Ethernet address length not including VLAN */
+#define EOE_ETHADDR_LENGTH 6
+
+#define EOE_MAKEU32(a,b,c,d) (((uint32_t)((a) & 0xff) << 24) | \
+ ((uint32_t)((b) & 0xff) << 16) | \
+ ((uint32_t)((c) & 0xff) << 8) | \
+ (uint32_t)((d) & 0xff))
+
+#if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN)
+
+#define EOE_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
+#define EOE_NTOHS(x) EOE_HTONS(x)
+#define EOE_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \
+ (((x) & 0x0000ff00UL) << 8) | \
+ (((x) & 0x00ff0000UL) >> 8) | \
+ (((x) & 0xff000000UL) >> 24))
+#define EOE_NTOHL(x) EOE_HTONL(x)
+#else
+#define EOE_HTONS(x) (x)
+#define EOE_NTOHS(x) (x)
+#define EOE_HTONL(x) (x)
+#define EOE_NTOHL(x) (x)
+#endif /* !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN) */
+
+/** Get one byte from the 4-byte address */
+#define eoe_ip4_addr1(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[0])
+#define eoe_ip4_addr2(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[1])
+#define eoe_ip4_addr3(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[2])
+#define eoe_ip4_addr4(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[3])
+
+/** Set an IP address given by the four byte-parts */
+#define EOE_IP4_ADDR_TO_U32(ipaddr,a,b,c,d) \
+ (ipaddr)->addr = EOE_HTONL(EOE_MAKEU32(a,b,c,d))
+
+/** Header frame info 1 */
+#define EOE_HDR_FRAME_TYPE_OFFSET 0
+#define EOE_HDR_FRAME_TYPE (0xF << 0)
+#define EOE_HDR_FRAME_TYPE_SET(x) (((x) & 0xF) << 0)
+#define EOE_HDR_FRAME_TYPE_GET(x) (((x) >> 0) & 0xF)
+#define EOE_HDR_FRAME_PORT_OFFSET 4
+#define EOE_HDR_FRAME_PORT (0xF << 4)
+#define EOE_HDR_FRAME_PORT_SET(x) (((x) & 0xF) << 4)
+#define EOE_HDR_FRAME_PORT_GET(x) (((x) >> 4) & 0xF)
+#define EOE_HDR_LAST_FRAGMENT_OFFSET 8
+#define EOE_HDR_LAST_FRAGMENT (0x1 << 8)
+#define EOE_HDR_LAST_FRAGMENT_SET(x) (((x) & 0x1) << 8)
+#define EOE_HDR_LAST_FRAGMENT_GET(x) (((x) >> 8) & 0x1)
+#define EOE_HDR_TIME_APPEND_OFFSET 9
+#define EOE_HDR_TIME_APPEND (0x1 << 9)
+#define EOE_HDR_TIME_APPEND_SET(x) (((x) & 0x1) << 9)
+#define EOE_HDR_TIME_APPEND_GET(x) (((x) >> 9) & 0x1)
+#define EOE_HDR_TIME_REQUEST_OFFSET 10
+#define EOE_HDR_TIME_REQUEST (0x1 << 10)
+#define EOE_HDR_TIME_REQUEST_SET(x) (((x) & 0x1) << 10)
+#define EOE_HDR_TIME_REQUEST_GET(x) (((x) >> 10) & 0x1)
+
+/** Header frame info 2 */
+#define EOE_HDR_FRAG_NO_OFFSET 0
+#define EOE_HDR_FRAG_NO (0x3F << 0)
+#define EOE_HDR_FRAG_NO_SET(x) (((x) & 0x3F) << 0)
+#define EOE_HDR_FRAG_NO_GET(x) (((x) >> 0) & 0x3F)
+#define EOE_HDR_FRAME_OFFSET_OFFSET 6
+#define EOE_HDR_FRAME_OFFSET (0x3F << 6)
+#define EOE_HDR_FRAME_OFFSET_SET(x) (((x) & 0x3F) << 6)
+#define EOE_HDR_FRAME_OFFSET_GET(x) (((x) >> 6) & 0x3F)
+#define EOE_HDR_FRAME_NO_OFFSET 12
+#define EOE_HDR_FRAME_NO (0xF << 12)
+#define EOE_HDR_FRAME_NO_SET(x) (((x) & 0xF) << 12)
+#define EOE_HDR_FRAME_NO_GET(x) (((x) >> 12) & 0xF)
+
+/** EOE param */
+#define EOE_PARAM_OFFSET 4
+#define EOE_PARAM_MAC_INCLUDE (0x1 << 0)
+#define EOE_PARAM_IP_INCLUDE (0x1 << 1)
+#define EOE_PARAM_SUBNET_IP_INCLUDE (0x1 << 2)
+#define EOE_PARAM_DEFAULT_GATEWAY_INCLUDE (0x1 << 3)
+#define EOE_PARAM_DNS_IP_INCLUDE (0x1 << 4)
+#define EOE_PARAM_DNS_NAME_INCLUDE (0x1 << 5)
+
+/** EoE frame types */
+#define EOE_FRAG_DATA 0
+#define EOE_INIT_RESP_TIMESTAMP 1
+#define EOE_INIT_REQ 2 /* Spec SET IP REQ */
+#define EOE_INIT_RESP 3 /* Spec SET IP RESP */
+#define EOE_SET_ADDR_FILTER_REQ 4
+#define EOE_SET_ADDR_FILTER_RESP 5
+#define EOE_GET_IP_PARAM_REQ 6
+#define EOE_GET_IP_PARAM_RESP 7
+#define EOE_GET_ADDR_FILTER_REQ 8
+#define EOE_GET_ADDR_FILTER_RESP 9
+
+/** EoE parameter result codes */
+#define EOE_RESULT_SUCCESS 0x0000
+#define EOE_RESULT_UNSPECIFIED_ERROR 0x0001
+#define EOE_RESULT_UNSUPPORTED_FRAME_TYPE 0x0002
+#define EOE_RESULT_NO_IP_SUPPORT 0x0201
+#define EOE_RESULT_NO_DHCP_SUPPORT 0x0202
+#define EOE_RESULT_NO_FILTER_SUPPORT 0x0401
+
+
+/** EOE ip4 address in network order */
+typedef struct eoe_ip4_addr {
+ uint32_t addr;
+}eoe_ip4_addr_t;
+
+/** EOE ethernet address */
+PACKED_BEGIN
+typedef struct PACKED eoe_ethaddr
+{
+ uint8_t addr[EOE_ETHADDR_LENGTH];
+} eoe_ethaddr_t;
+PACKED_END
+
+/** EoE IP request structure, storage only, no need to pack */
+typedef struct eoe_param
+{
+ uint8_t mac_set : 1;
+ uint8_t ip_set : 1;
+ uint8_t subnet_set : 1;
+ uint8_t default_gateway_set : 1;
+ uint8_t dns_ip_set : 1;
+ uint8_t dns_name_set : 1;
+ eoe_ethaddr_t mac;
+ eoe_ip4_addr_t ip;
+ eoe_ip4_addr_t subnet;
+ eoe_ip4_addr_t default_gateway;
+ eoe_ip4_addr_t dns_ip;
+ char dns_name[EOE_DNS_NAME_LENGTH];
+} eoe_param_t;
+
+/** EOE structure.
+* Used to interpret EoE mailbox packets.
+*/
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert mbxheader;
+ uint16_t frameinfo1;
+ union
+ {
+ uint16_t frameinfo2;
+ uint16_t result;
+ };
+ uint8 data[0];
+} ec_EOEt;
+PACKED_END
+
+int ecx_EOEdefinehook(ecx_contextt *context, void *hook);
+int ecx_EOEsetIp(ecx_contextt *context,
+ uint16 slave,
+ uint8 port,
+ eoe_param_t * ipparam,
+ int timeout);
+int ecx_EOEgetIp(ecx_contextt *context,
+ uint16 slave,
+ uint8 port,
+ eoe_param_t * ipparam,
+ int timeout);
+int ecx_EOEsend(ecx_contextt *context,
+ uint16 slave,
+ uint8 port,
+ int psize,
+ void *p,
+ int timeout);
+int ecx_EOErecv(ecx_contextt *context,
+ uint16 slave,
+ uint8 port,
+ int * psize,
+ void *p,
+ int timeout);
+int ecx_EOEreadfragment(
+ ec_mbxbuft * MbxIn,
+ uint8 * rxfragmentno,
+ uint16 * rxframesize,
+ uint16 * rxframeoffset,
+ uint16 * rxframeno,
+ int * psize,
+ void *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatfoe.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,370 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * File over EtherCAT (FoE) module.
+ *
+ * SDO read / write and SDO service functions
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatfoe.h"
+
+#define EC_MAXFOEDATA 512
+
+/** FOE structure.
+ * Used for Read, Write, Data, Ack and Error mailbox packets.
+ */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert MbxHeader;
+ uint8 OpCode;
+ uint8 Reserved;
+ union
+ {
+ uint32 Password;
+ uint32 PacketNumber;
+ uint32 ErrorCode;
+ };
+ union
+ {
+ char FileName[EC_MAXFOEDATA];
+ uint8 Data[EC_MAXFOEDATA];
+ char ErrorText[EC_MAXFOEDATA];
+ };
+} ec_FOEt;
+PACKED_END
+
+/** FoE progress hook.
+ *
+ * @param[in] context = context struct
+ * @param[in] hook = Pointer to hook function.
+ * @return 1
+ */
+int ecx_FOEdefinehook(ecx_contextt *context, void *hook)
+{
+ context->FOEhook = hook;
+ return 1;
+}
+
+/** FoE read, blocking.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number.
+ * @param[in] filename = Filename of file to read.
+ * @param[in] password = password.
+ * @param[in,out] psize = Size in bytes of file buffer, returns bytes read from file.
+ * @param[out] p = Pointer to file buffer
+ * @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
+{
+ ec_FOEt *FOEp, *aFOEp;
+ int wkc;
+ int32 dataread = 0;
+ int32 buffersize, packetnumber, prevpacket = 0;
+ uint16 fnsize, maxdata, segmentdata;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+ boolean worktodo;
+
+ buffersize = *psize;
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aFOEp = (ec_FOEt *)&MbxIn;
+ FOEp = (ec_FOEt *)&MbxOut;
+ fnsize = (uint16)strlen(filename);
+ maxdata = context->slavelist[slave].mbx_l - 12;
+ if (fnsize > maxdata)
+ {
+ fnsize = maxdata;
+ }
+ FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
+ FOEp->MbxHeader.address = htoes(0x0000);
+ FOEp->MbxHeader.priority = 0x00;
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
+ FOEp->OpCode = ECT_FOE_READ;
+ FOEp->Password = htoel(password);
+ /* copy filename in mailbox */
+ memcpy(&FOEp->FileName[0], filename, fnsize);
+ /* send FoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ do
+ {
+ worktodo = FALSE;
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be FoE */
+ if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
+ {
+ if(aFOEp->OpCode == ECT_FOE_DATA)
+ {
+ segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006;
+ packetnumber = etohl(aFOEp->PacketNumber);
+ if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize))
+ {
+ memcpy(p, &aFOEp->Data[0], segmentdata);
+ dataread += segmentdata;
+ p = (uint8 *)p + segmentdata;
+ if (segmentdata == maxdata)
+ {
+ worktodo = TRUE;
+ }
+ FOEp->MbxHeader.length = htoes(0x0006);
+ FOEp->MbxHeader.address = htoes(0x0000);
+ FOEp->MbxHeader.priority = 0x00;
+ /* get new mailbox count value */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
+ FOEp->OpCode = ECT_FOE_ACK;
+ FOEp->PacketNumber = htoel(packetnumber);
+ /* send FoE ack to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc <= 0)
+ {
+ worktodo = FALSE;
+ }
+ if (context->FOEhook)
+ {
+ context->FOEhook(slave, packetnumber, dataread);
+ }
+ }
+ else
+ {
+ /* FoE error */
+ wkc = -EC_ERR_TYPE_FOE_BUF2SMALL;
+ }
+ }
+ else
+ {
+ if(aFOEp->OpCode == ECT_FOE_ERROR)
+ {
+ /* FoE error */
+ wkc = -EC_ERR_TYPE_FOE_ERROR;
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ }
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ *psize = dataread;
+ }
+ } while (worktodo);
+ }
+
+ return wkc;
+}
+
+/** FoE write, blocking.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number.
+ * @param[in] filename = Filename of file to write.
+ * @param[in] password = password.
+ * @param[in] psize = Size in bytes of file buffer.
+ * @param[out] p = Pointer to file buffer
+ * @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
+{
+ ec_FOEt *FOEp, *aFOEp;
+ int wkc;
+ int32 packetnumber, sendpacket = 0;
+ uint16 fnsize, maxdata;
+ int segmentdata;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+ boolean worktodo, dofinalzero;
+ int tsize;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aFOEp = (ec_FOEt *)&MbxIn;
+ FOEp = (ec_FOEt *)&MbxOut;
+ dofinalzero = FALSE;
+ fnsize = (uint16)strlen(filename);
+ maxdata = context->slavelist[slave].mbx_l - 12;
+ if (fnsize > maxdata)
+ {
+ fnsize = maxdata;
+ }
+ FOEp->MbxHeader.length = htoes(0x0006 + fnsize);
+ FOEp->MbxHeader.address = htoes(0x0000);
+ FOEp->MbxHeader.priority = 0x00;
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
+ FOEp->OpCode = ECT_FOE_WRITE;
+ FOEp->Password = htoel(password);
+ /* copy filename in mailbox */
+ memcpy(&FOEp->FileName[0], filename, fnsize);
+ /* send FoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ do
+ {
+ worktodo = FALSE;
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be FoE */
+ if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
+ {
+ switch (aFOEp->OpCode)
+ {
+ case ECT_FOE_ACK:
+ {
+ packetnumber = etohl(aFOEp->PacketNumber);
+ if (packetnumber == sendpacket)
+ {
+ if (context->FOEhook)
+ {
+ context->FOEhook(slave, packetnumber, psize);
+ }
+ tsize = psize;
+ if (tsize > maxdata)
+ {
+ tsize = maxdata;
+ }
+ if(tsize || dofinalzero)
+ {
+ worktodo = TRUE;
+ dofinalzero = FALSE;
+ segmentdata = tsize;
+ psize -= segmentdata;
+ /* if last packet was full size, add a zero size packet as final */
+ /* EOF is defined as packetsize < full packetsize */
+ if (!psize && (segmentdata == maxdata))
+ {
+ dofinalzero = TRUE;
+ }
+ FOEp->MbxHeader.length = htoes(0x0006 + segmentdata);
+ FOEp->MbxHeader.address = htoes(0x0000);
+ FOEp->MbxHeader.priority = 0x00;
+ /* get new mailbox count value */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */
+ FOEp->OpCode = ECT_FOE_DATA;
+ sendpacket++;
+ FOEp->PacketNumber = htoel(sendpacket);
+ memcpy(&FOEp->Data[0], p, segmentdata);
+ p = (uint8 *)p + segmentdata;
+ /* send FoE data to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc <= 0)
+ {
+ worktodo = FALSE;
+ }
+ }
+ }
+ else
+ {
+ /* FoE error */
+ wkc = -EC_ERR_TYPE_FOE_PACKETNUMBER;
+ }
+ break;
+ }
+ case ECT_FOE_BUSY:
+ {
+ /* resend if data has been send before */
+ /* otherwise ignore */
+ if (sendpacket)
+ {
+ if (!psize)
+ {
+ dofinalzero = TRUE;
+ }
+ psize += segmentdata;
+ p = (uint8 *)p - segmentdata;
+ --sendpacket;
+ }
+ break;
+ }
+ case ECT_FOE_ERROR:
+ {
+ /* FoE error */
+ if (aFOEp->ErrorCode == 0x8001)
+ {
+ wkc = -EC_ERR_TYPE_FOE_FILE_NOTFOUND;
+ }
+ else
+ {
+ wkc = -EC_ERR_TYPE_FOE_ERROR;
+ }
+ break;
+ }
+ default:
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* unexpected mailbox received */
+ wkc = -EC_ERR_TYPE_PACKET_ERROR;
+ }
+ }
+ } while (worktodo);
+ }
+
+ return wkc;
+}
+
+#ifdef EC_VER1
+int ec_FOEdefinehook(void *hook)
+{
+ return ecx_FOEdefinehook(&ecx_context, hook);
+}
+
+int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
+{
+ return ecx_FOEread(&ecx_context, slave, filename, password, psize, p, timeout);
+}
+
+int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
+{
+ return ecx_FOEwrite(&ecx_context, slave, filename, password, psize, p, timeout);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatfoe.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatfoe.c
+ */
+
+#ifndef _ethercatfoe_
+#define _ethercatfoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef EC_VER1
+int ec_FOEdefinehook(void *hook);
+int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout);
+int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout);
+#endif
+
+int ecx_FOEdefinehook(ecx_contextt *context, void *hook);
+int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout);
+int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatmain.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,2395 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/**
+ * \file
+ * \brief
+ * Main EtherCAT functions.
+ *
+ * Initialisation, state set and read, mailbox primitives, EEPROM primitives,
+ * SII reading and processdata exchange.
+ *
+ * Defines ec_slave[]. All slave information is put in this structure.
+ * Needed for most user interaction with slaves.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercat.h"
+
+
+/** delay in us for eeprom ready loop */
+#define EC_LOCALDELAY 200
+
+/** record for ethercat eeprom communications */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ uint16 comm;
+ uint16 addr;
+ uint16 d2;
+} ec_eepromt;
+PACKED_END
+
+/** mailbox error structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert MbxHeader;
+ uint16 Type;
+ uint16 Detail;
+} ec_mbxerrort;
+PACKED_END
+
+/** emergency request structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert MbxHeader;
+ uint16 CANOpen;
+ uint16 ErrorCode;
+ uint8 ErrorReg;
+ uint8 bData;
+ uint16 w1,w2;
+} ec_emcyt;
+PACKED_END
+
+#ifdef EC_VER1
+/** Main slave data array.
+ * Each slave found on the network gets its own record.
+ * ec_slave[0] is reserved for the master. Structure gets filled
+ * in by the configuration function ec_config().
+ */
+ec_slavet ec_slave[EC_MAXSLAVE];
+/** number of slaves found on the network */
+int ec_slavecount;
+/** slave group structure */
+ec_groupt ec_group[EC_MAXGROUP];
+
+/** cache for EEPROM read functions */
+static uint8 ec_esibuf[EC_MAXEEPBUF];
+/** bitmap for filled cache buffer bytes */
+static uint32 ec_esimap[EC_MAXEEPBITMAP];
+/** current slave for EEPROM cache buffer */
+static ec_eringt ec_elist;
+static ec_idxstackT ec_idxstack;
+
+/** SyncManager Communication Type struct to store data of one slave */
+static ec_SMcommtypet ec_SMcommtype[EC_MAX_MAPT];
+/** PDO assign struct to store data of one slave */
+static ec_PDOassignt ec_PDOassign[EC_MAX_MAPT];
+/** PDO description struct to store data of one slave */
+static ec_PDOdesct ec_PDOdesc[EC_MAX_MAPT];
+
+/** buffer for EEPROM SM data */
+static ec_eepromSMt ec_SM;
+/** buffer for EEPROM FMMU data */
+static ec_eepromFMMUt ec_FMMU;
+/** Global variable TRUE if error available in error stack */
+boolean EcatError = FALSE;
+
+int64 ec_DCtime;
+
+ecx_portt ecx_port;
+ecx_redportt ecx_redport;
+
+ecx_contextt ecx_context = {
+ &ecx_port, // .port =
+ &ec_slave[0], // .slavelist =
+ &ec_slavecount, // .slavecount =
+ EC_MAXSLAVE, // .maxslave =
+ &ec_group[0], // .grouplist =
+ EC_MAXGROUP, // .maxgroup =
+ &ec_esibuf[0], // .esibuf =
+ &ec_esimap[0], // .esimap =
+ 0, // .esislave =
+ &ec_elist, // .elist =
+ &ec_idxstack, // .idxstack =
+ &EcatError, // .ecaterror =
+ 0, // .DCtO =
+ 0, // .DCl =
+ &ec_DCtime, // .DCtime =
+ &ec_SMcommtype[0], // .SMcommtype =
+ &ec_PDOassign[0], // .PDOassign =
+ &ec_PDOdesc[0], // .PDOdesc =
+ &ec_SM, // .eepSM =
+ &ec_FMMU, // .eepFMMU =
+ NULL, // .FOEhook()
+ NULL // .EOEhook()
+};
+#endif
+
+/** Create list over available network adapters.
+ *
+ * @return First element in list over available network adapters.
+ */
+ec_adaptert * ec_find_adapters (void)
+{
+ ec_adaptert * ret_adapter;
+
+ ret_adapter = oshw_find_adapters ();
+
+ return ret_adapter;
+}
+
+/** Free dynamically allocated list over available network adapters.
+ *
+ * @param[in] adapter = Struct holding adapter name, description and pointer to next.
+ */
+void ec_free_adapters (ec_adaptert * adapter)
+{
+ oshw_free_adapters (adapter);
+}
+
+/** Pushes an error on the error list.
+ *
+ * @param[in] context = context struct
+ * @param[in] Ec pointer describing the error.
+ */
+void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec)
+{
+ context->elist->Error[context->elist->head] = *Ec;
+ context->elist->Error[context->elist->head].Signal = TRUE;
+ context->elist->head++;
+ if (context->elist->head > EC_MAXELIST)
+ {
+ context->elist->head = 0;
+ }
+ if (context->elist->head == context->elist->tail)
+ {
+ context->elist->tail++;
+ }
+ if (context->elist->tail > EC_MAXELIST)
+ {
+ context->elist->tail = 0;
+ }
+ *(context->ecaterror) = TRUE;
+}
+
+/** Pops an error from the list.
+ *
+ * @param[in] context = context struct
+ * @param[out] Ec = Struct describing the error.
+ * @return TRUE if an error was popped.
+ */
+boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec)
+{
+ boolean notEmpty = (context->elist->head != context->elist->tail);
+
+ *Ec = context->elist->Error[context->elist->tail];
+ context->elist->Error[context->elist->tail].Signal = FALSE;
+ if (notEmpty)
+ {
+ context->elist->tail++;
+ if (context->elist->tail > EC_MAXELIST)
+ {
+ context->elist->tail = 0;
+ }
+ }
+ else
+ {
+ *(context->ecaterror) = FALSE;
+ }
+ return notEmpty;
+}
+
+/** Check if error list has entries.
+ *
+ * @param[in] context = context struct
+ * @return TRUE if error list contains entries.
+ */
+boolean ecx_iserror(ecx_contextt *context)
+{
+ return (context->elist->head != context->elist->tail);
+}
+
+/** Report packet error
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Index = Index that generated error
+ * @param[in] SubIdx = Subindex that generated error
+ * @param[in] ErrorCode = Error code
+ */
+void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode)
+{
+ ec_errort Ec;
+
+ memset(&Ec, 0, sizeof(Ec));
+ Ec.Time = osal_current_time();
+ Ec.Slave = Slave;
+ Ec.Index = Index;
+ Ec.SubIdx = SubIdx;
+ *(context->ecaterror) = TRUE;
+ Ec.Etype = EC_ERR_TYPE_PACKET_ERROR;
+ Ec.ErrorCode = ErrorCode;
+ ecx_pusherror(context, &Ec);
+}
+
+/** Report Mailbox Error
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] Detail = Following EtherCAT specification
+ */
+static void ecx_mbxerror(ecx_contextt *context, uint16 Slave,uint16 Detail)
+{
+ ec_errort Ec;
+
+ memset(&Ec, 0, sizeof(Ec));
+ Ec.Time = osal_current_time();
+ Ec.Slave = Slave;
+ Ec.Index = 0;
+ Ec.SubIdx = 0;
+ Ec.Etype = EC_ERR_TYPE_MBX_ERROR;
+ Ec.ErrorCode = Detail;
+ ecx_pusherror(context, &Ec);
+}
+
+/** Report Mailbox Emergency Error
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] ErrorCode = Following EtherCAT specification
+ * @param[in] ErrorReg
+ * @param[in] b1
+ * @param[in] w1
+ * @param[in] w2
+ */
+static void ecx_mbxemergencyerror(ecx_contextt *context, uint16 Slave,uint16 ErrorCode,uint16 ErrorReg,
+ uint8 b1, uint16 w1, uint16 w2)
+{
+ ec_errort Ec;
+
+ memset(&Ec, 0, sizeof(Ec));
+ Ec.Time = osal_current_time();
+ Ec.Slave = Slave;
+ Ec.Index = 0;
+ Ec.SubIdx = 0;
+ Ec.Etype = EC_ERR_TYPE_EMERGENCY;
+ Ec.ErrorCode = ErrorCode;
+ Ec.ErrorReg = (uint8)ErrorReg;
+ Ec.b1 = b1;
+ Ec.w1 = w1;
+ Ec.w2 = w2;
+ ecx_pusherror(context, &Ec);
+}
+
+/** Initialise lib in single NIC mode
+ * @param[in] context = context struct
+ * @param[in] ifname = Dev name, f.e. "eth0"
+ * @return >0 if OK
+ */
+int ecx_init(ecx_contextt *context, const char * ifname)
+{
+ return ecx_setupnic(context->port, ifname, FALSE);
+}
+
+/** Initialise lib in redundant NIC mode
+ * @param[in] context = context struct
+ * @param[in] redport = pointer to redport, redundant port data
+ * @param[in] ifname = Primary Dev name, f.e. "eth0"
+ * @param[in] if2name = Secondary Dev name, f.e. "eth1"
+ * @return >0 if OK
+ */
+int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name)
+{
+ int rval, zbuf;
+ ec_etherheadert *ehp;
+
+ context->port->redport = redport;
+ ecx_setupnic(context->port, ifname, FALSE);
+ rval = ecx_setupnic(context->port, if2name, TRUE);
+ /* prepare "dummy" BRD tx frame for redundant operation */
+ ehp = (ec_etherheadert *)&(context->port->txbuf2);
+ ehp->sa1 = oshw_htons(secMAC[0]);
+ zbuf = 0;
+ ecx_setupdatagram(context->port, &(context->port->txbuf2), EC_CMD_BRD, 0, 0x0000, 0x0000, 2, &zbuf);
+ context->port->txbuflength2 = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + 2;
+
+ return rval;
+}
+
+/** Close lib.
+ * @param[in] context = context struct
+ */
+void ecx_close(ecx_contextt *context)
+{
+ ecx_closenic(context->port);
+};
+
+/** Read one byte from slave EEPROM via cache.
+ * If the cache location is empty then a read request is made to the slave.
+ * Depending on the slave capabilities the request is 4 or 8 bytes.
+ * @param[in] context = context struct
+ * @param[in] slave = slave number
+ * @param[in] address = eeprom address in bytes (slave uses words)
+ * @return requested byte, if not available then 0xff
+ */
+uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address)
+{
+ uint16 configadr, eadr;
+ uint64 edat64;
+ uint32 edat32;
+ uint16 mapw, mapb;
+ int lp,cnt;
+ uint8 retval;
+
+ retval = 0xff;
+ if (slave != context->esislave) /* not the same slave? */
+ {
+ memset(context->esimap, 0x00, EC_MAXEEPBITMAP * sizeof(uint32)); /* clear esibuf cache map */
+ context->esislave = slave;
+ }
+ if (address < EC_MAXEEPBUF)
+ {
+ mapw = address >> 5;
+ mapb = address - (mapw << 5);
+ if (context->esimap[mapw] & (uint32)(1 << mapb))
+ {
+ /* byte is already in buffer */
+ retval = context->esibuf[address];
+ }
+ else
+ {
+ /* byte is not in buffer, put it there */
+ configadr = context->slavelist[slave].configadr;
+ ecx_eeprom2master(context, slave); /* set eeprom control to master */
+ eadr = address >> 1;
+ edat64 = ecx_readeepromFP (context, configadr, eadr, EC_TIMEOUTEEP);
+ /* 8 byte response */
+ if (context->slavelist[slave].eep_8byte)
+ {
+ put_unaligned64(edat64, &(context->esibuf[eadr << 1]));
+ cnt = 8;
+ }
+ /* 4 byte response */
+ else
+ {
+ edat32 = (uint32)edat64;
+ put_unaligned32(edat32, &(context->esibuf[eadr << 1]));
+ cnt = 4;
+ }
+ /* find bitmap location */
+ mapw = eadr >> 4;
+ mapb = (eadr << 1) - (mapw << 5);
+ for(lp = 0 ; lp < cnt ; lp++)
+ {
+ /* set bitmap for each byte that is read */
+ context->esimap[mapw] |= (1 << mapb);
+ mapb++;
+ if (mapb > 31)
+ {
+ mapb = 0;
+ mapw++;
+ }
+ }
+ retval = context->esibuf[address];
+ }
+ }
+
+ return retval;
+}
+
+/** Find SII section header in slave EEPROM.
+ * @param[in] context = context struct
+ * @param[in] slave = slave number
+ * @param[in] cat = section category
+ * @return byte address of section at section length entry, if not available then 0
+ */
+int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat)
+{
+ int16 a;
+ uint16 p;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ a = ECT_SII_START << 1;
+ /* read first SII section category */
+ p = ecx_siigetbyte(context, slave, a++);
+ p += (ecx_siigetbyte(context, slave, a++) << 8);
+ /* traverse SII while category is not found and not EOF */
+ while ((p != cat) && (p != 0xffff))
+ {
+ /* read section length */
+ p = ecx_siigetbyte(context, slave, a++);
+ p += (ecx_siigetbyte(context, slave, a++) << 8);
+ /* locate next section category */
+ a += p << 1;
+ /* read section category */
+ p = ecx_siigetbyte(context, slave, a++);
+ p += (ecx_siigetbyte(context, slave, a++) << 8);
+ }
+ if (p != cat)
+ {
+ a = 0;
+ }
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+
+ return a;
+}
+
+/** Get string from SII string section in slave EEPROM.
+ * @param[in] context = context struct
+ * @param[out] str = requested string, 0x00 if not found
+ * @param[in] slave = slave number
+ * @param[in] Sn = string number
+ */
+void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn)
+{
+ uint16 a,i,j,l,n,ba;
+ char *ptr;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ ptr = str;
+ a = ecx_siifind (context, slave, ECT_SII_STRING); /* find string section */
+ if (a > 0)
+ {
+ ba = a + 2; /* skip SII section header */
+ n = ecx_siigetbyte(context, slave, ba++); /* read number of strings in section */
+ if (Sn <= n) /* is req string available? */
+ {
+ for (i = 1; i <= Sn; i++) /* walk through strings */
+ {
+ l = ecx_siigetbyte(context, slave, ba++); /* length of this string */
+ if (i < Sn)
+ {
+ ba += l;
+ }
+ else
+ {
+ ptr = str;
+ for (j = 1; j <= l; j++) /* copy one string */
+ {
+ if(j <= EC_MAXNAME)
+ {
+ *ptr = (char)ecx_siigetbyte(context, slave, ba++);
+ ptr++;
+ }
+ else
+ {
+ ba++;
+ }
+ }
+ }
+ }
+ *ptr = 0; /* add zero terminator */
+ }
+ else
+ {
+ ptr = str;
+ *ptr = 0; /* empty string */
+ }
+ }
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+}
+
+/** Get FMMU data from SII FMMU section in slave EEPROM.
+ * @param[in] context = context struct
+ * @param[in] slave = slave number
+ * @param[out] FMMU = FMMU struct from SII, max. 4 FMMU's
+ * @return number of FMMU's defined in section
+ */
+uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU)
+{
+ uint16 a;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ FMMU->nFMMU = 0;
+ FMMU->FMMU0 = 0;
+ FMMU->FMMU1 = 0;
+ FMMU->FMMU2 = 0;
+ FMMU->FMMU3 = 0;
+ FMMU->Startpos = ecx_siifind(context, slave, ECT_SII_FMMU);
+
+ if (FMMU->Startpos > 0)
+ {
+ a = FMMU->Startpos;
+ FMMU->nFMMU = ecx_siigetbyte(context, slave, a++);
+ FMMU->nFMMU += (ecx_siigetbyte(context, slave, a++) << 8);
+ FMMU->nFMMU *= 2;
+ FMMU->FMMU0 = ecx_siigetbyte(context, slave, a++);
+ FMMU->FMMU1 = ecx_siigetbyte(context, slave, a++);
+ if (FMMU->nFMMU > 2)
+ {
+ FMMU->FMMU2 = ecx_siigetbyte(context, slave, a++);
+ FMMU->FMMU3 = ecx_siigetbyte(context, slave, a++);
+ }
+ }
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+
+ return FMMU->nFMMU;
+}
+
+/** Get SM data from SII SM section in slave EEPROM.
+ * @param[in] context = context struct
+ * @param[in] slave = slave number
+ * @param[out] SM = first SM struct from SII
+ * @return number of SM's defined in section
+ */
+uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM)
+{
+ uint16 a,w;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ SM->nSM = 0;
+ SM->Startpos = ecx_siifind(context, slave, ECT_SII_SM);
+ if (SM->Startpos > 0)
+ {
+ a = SM->Startpos;
+ w = ecx_siigetbyte(context, slave, a++);
+ w += (ecx_siigetbyte(context, slave, a++) << 8);
+ SM->nSM = (w / 4);
+ SM->PhStart = ecx_siigetbyte(context, slave, a++);
+ SM->PhStart += (ecx_siigetbyte(context, slave, a++) << 8);
+ SM->Plength = ecx_siigetbyte(context, slave, a++);
+ SM->Plength += (ecx_siigetbyte(context, slave, a++) << 8);
+ SM->Creg = ecx_siigetbyte(context, slave, a++);
+ SM->Sreg = ecx_siigetbyte(context, slave, a++);
+ SM->Activate = ecx_siigetbyte(context, slave, a++);
+ SM->PDIctrl = ecx_siigetbyte(context, slave, a++);
+ }
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+
+ return SM->nSM;
+}
+
+/** Get next SM data from SII SM section in slave EEPROM.
+ * @param[in] context = context struct
+ * @param[in] slave = slave number
+ * @param[out] SM = first SM struct from SII
+ * @param[in] n = SM number
+ * @return >0 if OK
+ */
+uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n)
+{
+ uint16 a;
+ uint16 retVal = 0;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ if (n < SM->nSM)
+ {
+ a = SM->Startpos + 2 + (n * 8);
+ SM->PhStart = ecx_siigetbyte(context, slave, a++);
+ SM->PhStart += (ecx_siigetbyte(context, slave, a++) << 8);
+ SM->Plength = ecx_siigetbyte(context, slave, a++);
+ SM->Plength += (ecx_siigetbyte(context, slave, a++) << 8);
+ SM->Creg = ecx_siigetbyte(context, slave, a++);
+ SM->Sreg = ecx_siigetbyte(context, slave, a++);
+ SM->Activate = ecx_siigetbyte(context, slave, a++);
+ SM->PDIctrl = ecx_siigetbyte(context, slave, a++);
+ retVal = 1;
+ }
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+
+ return retVal;
+}
+
+/** Get PDO data from SII PDO section in slave EEPROM.
+ * @param[in] context = context struct
+ * @param[in] slave = slave number
+ * @param[out] PDO = PDO struct from SII
+ * @param[in] t = 0=RXPDO 1=TXPDO
+ * @return mapping size in bits of PDO
+ */
+int ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t)
+{
+ uint16 a , w, c, e, er, Size;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ Size = 0;
+ PDO->nPDO = 0;
+ PDO->Length = 0;
+ PDO->Index[1] = 0;
+ for (c = 0 ; c < EC_MAXSM ; c++) PDO->SMbitsize[c] = 0;
+ if (t > 1)
+ t = 1;
+ PDO->Startpos = ecx_siifind(context, slave, ECT_SII_PDO + t);
+ if (PDO->Startpos > 0)
+ {
+ a = PDO->Startpos;
+ w = ecx_siigetbyte(context, slave, a++);
+ w += (ecx_siigetbyte(context, slave, a++) << 8);
+ PDO->Length = w;
+ c = 1;
+ /* traverse through all PDOs */
+ do
+ {
+ PDO->nPDO++;
+ PDO->Index[PDO->nPDO] = ecx_siigetbyte(context, slave, a++);
+ PDO->Index[PDO->nPDO] += (ecx_siigetbyte(context, slave, a++) << 8);
+ PDO->BitSize[PDO->nPDO] = 0;
+ c++;
+ e = ecx_siigetbyte(context, slave, a++);
+ PDO->SyncM[PDO->nPDO] = ecx_siigetbyte(context, slave, a++);
+ a += 4;
+ c += 2;
+ if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) /* active and in range SM? */
+ {
+ /* read all entries defined in PDO */
+ for (er = 1; er <= e; er++)
+ {
+ c += 4;
+ a += 5;
+ PDO->BitSize[PDO->nPDO] += ecx_siigetbyte(context, slave, a++);
+ a += 2;
+ }
+ PDO->SMbitsize[ PDO->SyncM[PDO->nPDO] ] += PDO->BitSize[PDO->nPDO];
+ Size += PDO->BitSize[PDO->nPDO];
+ c++;
+ }
+ else /* PDO deactivated because SM is 0xff or > EC_MAXSM */
+ {
+ c += 4 * e;
+ a += 8 * e;
+ c++;
+ }
+ if (PDO->nPDO >= (EC_MAXEEPDO - 1))
+ {
+ c = PDO->Length; /* limit number of PDO entries in buffer */
+ }
+ }
+ while (c < PDO->Length);
+ }
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+
+ return (Size);
+}
+
+#define MAX_FPRD_MULTI 64
+
+int ecx_FPRD_multi(ecx_contextt *context, int n, uint16 *configlst, ec_alstatust *slstatlst, int timeout)
+{
+ int wkc;
+ uint8 idx;
+ ecx_portt *port;
+ int sldatapos[MAX_FPRD_MULTI];
+ int slcnt;
+
+ port = context->port;
+ idx = ecx_getindex(port);
+ slcnt = 0;
+ ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx,
+ *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
+ sldatapos[slcnt] = EC_HEADERSIZE;
+ while(++slcnt < (n - 1))
+ {
+ sldatapos[slcnt] = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, TRUE,
+ *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
+ }
+ if(slcnt < n)
+ {
+ sldatapos[slcnt] = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, FALSE,
+ *(configlst + slcnt), ECT_REG_ALSTAT, sizeof(ec_alstatust), slstatlst + slcnt);
+ }
+ wkc = ecx_srconfirm(port, idx, timeout);
+ if (wkc >= 0)
+ {
+ for(slcnt = 0 ; slcnt < n ; slcnt++)
+ {
+ memcpy(slstatlst + slcnt, &(port->rxbuf[idx][sldatapos[slcnt]]), sizeof(ec_alstatust));
+ }
+ }
+ ecx_setbufstat(port, idx, EC_BUF_EMPTY);
+ return wkc;
+}
+
+/** Read all slave states in ec_slave.
+ * @param[in] context = context struct
+ * @return lowest state found
+ */
+int ecx_readstate(ecx_contextt *context)
+{
+ uint16 slave, fslave, lslave, configadr, lowest, rval, bitwisestate;
+ ec_alstatust sl[MAX_FPRD_MULTI];
+ uint16 slca[MAX_FPRD_MULTI];
+ boolean noerrorflag, allslavessamestate;
+ boolean allslavespresent = FALSE;
+ int wkc;
+
+ /* Try to establish the state of all slaves sending only one broadcast datagram.
+ * This way a number of datagrams equal to the number of slaves will be sent only if needed.*/
+ rval = 0;
+ wkc = ecx_BRD(context->port, 0, ECT_REG_ALSTAT, sizeof(rval), &rval, EC_TIMEOUTRET);
+
+ if(wkc >= *(context->slavecount))
+ {
+ allslavespresent = TRUE;
+ }
+
+ rval = etohs(rval);
+ bitwisestate = (rval & 0x0f);
+
+ if ((rval & EC_STATE_ERROR) == 0)
+ {
+ noerrorflag = TRUE;
+ context->slavelist[0].ALstatuscode = 0;
+ }
+ else
+ {
+ noerrorflag = FALSE;
+ }
+
+ switch (bitwisestate)
+ {
+ case EC_STATE_INIT:
+ case EC_STATE_PRE_OP:
+ case EC_STATE_BOOT:
+ case EC_STATE_SAFE_OP:
+ case EC_STATE_OPERATIONAL:
+ allslavessamestate = TRUE;
+ context->slavelist[0].state = bitwisestate;
+ break;
+ default:
+ allslavessamestate = FALSE;
+ break;
+ }
+
+ if (noerrorflag && allslavessamestate && allslavespresent)
+ {
+ /* No slave has toggled the error flag so the alstatuscode
+ * (even if different from 0) should be ignored and
+ * the slaves have reached the same state so the internal state
+ * can be updated without sending any datagram. */
+ for (slave = 1; slave <= *(context->slavecount); slave++)
+ {
+ context->slavelist[slave].ALstatuscode = 0x0000;
+ context->slavelist[slave].state = bitwisestate;
+ }
+ lowest = bitwisestate;
+ }
+ else
+ {
+ /* Not all slaves have the same state or at least one is in error so one datagram per slave
+ * is needed. */
+ context->slavelist[0].ALstatuscode = 0;
+ lowest = 0xff;
+ fslave = 1;
+ do
+ {
+ lslave = *(context->slavecount);
+ if ((lslave - fslave) >= MAX_FPRD_MULTI)
+ {
+ lslave = fslave + MAX_FPRD_MULTI - 1;
+ }
+ for (slave = fslave; slave <= lslave; slave++)
+ {
+ const ec_alstatust zero = { 0, 0, 0 };
+
+ configadr = context->slavelist[slave].configadr;
+ slca[slave - fslave] = configadr;
+ sl[slave - fslave] = zero;
+ }
+ ecx_FPRD_multi(context, (lslave - fslave) + 1, &(slca[0]), &(sl[0]), EC_TIMEOUTRET3);
+ for (slave = fslave; slave <= lslave; slave++)
+ {
+ configadr = context->slavelist[slave].configadr;
+ rval = etohs(sl[slave - fslave].alstatus);
+ context->slavelist[slave].ALstatuscode = etohs(sl[slave - fslave].alstatuscode);
+ if ((rval & 0xf) < lowest)
+ {
+ lowest = (rval & 0xf);
+ }
+ context->slavelist[slave].state = rval;
+ context->slavelist[0].ALstatuscode |= context->slavelist[slave].ALstatuscode;
+ }
+ fslave = lslave + 1;
+ } while (lslave < *(context->slavecount));
+ context->slavelist[0].state = lowest;
+ }
+
+ return lowest;
+}
+
+/** Write slave state, if slave = 0 then write to all slaves.
+ * The function does not check if the actual state is changed.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number, 0 = master
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_writestate(ecx_contextt *context, uint16 slave)
+{
+ int ret;
+ uint16 configadr, slstate;
+
+ if (slave == 0)
+ {
+ slstate = htoes(context->slavelist[slave].state);
+ ret = ecx_BWR(context->port, 0, ECT_REG_ALCTL, sizeof(slstate),
+ &slstate, EC_TIMEOUTRET3);
+ }
+ else
+ {
+ configadr = context->slavelist[slave].configadr;
+
+ ret = ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL,
+ htoes(context->slavelist[slave].state), EC_TIMEOUTRET3);
+ }
+ return ret;
+}
+
+/** Check actual slave state.
+ * This is a blocking function.
+ * To refresh the state of all slaves ecx_readstate()should be called
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number, 0 = all slaves (only the "slavelist[0].state" is refreshed)
+ * @param[in] reqstate = Requested state
+ * @param[in] timeout = Timeout value in us
+ * @return Requested state, or found state after timeout.
+ */
+uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout)
+{
+ uint16 configadr, state, rval;
+ ec_alstatust slstat;
+ osal_timert timer;
+
+ if ( slave > *(context->slavecount) )
+ {
+ return 0;
+ }
+ osal_timer_start(&timer, timeout);
+ configadr = context->slavelist[slave].configadr;
+ do
+ {
+ if (slave < 1)
+ {
+ rval = 0;
+ ecx_BRD(context->port, 0, ECT_REG_ALSTAT, sizeof(rval), &rval , EC_TIMEOUTRET);
+ rval = etohs(rval);
+ }
+ else
+ {
+ slstat.alstatus = 0;
+ slstat.alstatuscode = 0;
+ ecx_FPRD(context->port, configadr, ECT_REG_ALSTAT, sizeof(slstat), &slstat, EC_TIMEOUTRET);
+ rval = etohs(slstat.alstatus);
+ context->slavelist[slave].ALstatuscode = etohs(slstat.alstatuscode);
+ }
+ state = rval & 0x000f; /* read slave status */
+ if (state != reqstate)
+ {
+ osal_usleep(1000);
+ }
+ }
+ while ((state != reqstate) && (osal_timer_is_expired(&timer) == FALSE));
+ context->slavelist[slave].state = rval;
+
+ return state;
+}
+
+/** Get index of next mailbox counter value.
+ * Used for Mailbox Link Layer.
+ * @param[in] cnt = Mailbox counter value [0..7]
+ * @return next mailbox counter value
+ */
+uint8 ec_nextmbxcnt(uint8 cnt)
+{
+ cnt++;
+ if (cnt > 7)
+ {
+ cnt = 1; /* wrap around to 1, not 0 */
+ }
+
+ return cnt;
+}
+
+/** Clear mailbox buffer.
+ * @param[out] Mbx = Mailbox buffer to clear
+ */
+void ec_clearmbx(ec_mbxbuft *Mbx)
+{
+ memset(Mbx, 0x00, EC_MAXMBX);
+}
+
+/** Check if IN mailbox of slave is empty.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] timeout = Timeout in us
+ * @return >0 is success
+ */
+int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout)
+{
+ uint16 configadr;
+ uint8 SMstat;
+ int wkc;
+ osal_timert timer;
+
+ osal_timer_start(&timer, timeout);
+ configadr = context->slavelist[slave].configadr;
+ do
+ {
+ SMstat = 0;
+ wkc = ecx_FPRD(context->port, configadr, ECT_REG_SM0STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+ SMstat = etohs(SMstat);
+ if (((SMstat & 0x08) != 0) && (timeout > EC_LOCALDELAY))
+ {
+ osal_usleep(EC_LOCALDELAY);
+ }
+ }
+ while (((wkc <= 0) || ((SMstat & 0x08) != 0)) && (osal_timer_is_expired(&timer) == FALSE));
+
+ if ((wkc > 0) && ((SMstat & 0x08) == 0))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Write IN mailbox to slave.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[out] mbx = Mailbox data
+ * @param[in] timeout = Timeout in us
+ * @return Work counter (>0 is success)
+ */
+int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout)
+{
+ uint16 mbxwo,mbxl,configadr;
+ int wkc;
+
+ wkc = 0;
+ configadr = context->slavelist[slave].configadr;
+ mbxl = context->slavelist[slave].mbx_l;
+ if ((mbxl > 0) && (mbxl <= EC_MAXMBX))
+ {
+ if (ecx_mbxempty(context, slave, timeout))
+ {
+ mbxwo = context->slavelist[slave].mbx_wo;
+ /* write slave in mailbox */
+ wkc = ecx_FPWR(context->port, configadr, mbxwo, mbxl, mbx, EC_TIMEOUTRET3);
+ }
+ else
+ {
+ wkc = 0;
+ }
+ }
+
+ return wkc;
+}
+
+/** Read OUT mailbox from slave.
+ * Supports Mailbox Link Layer with repeat requests.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[out] mbx = Mailbox data
+ * @param[in] timeout = Timeout in us
+ * @return Work counter (>0 is success)
+ */
+int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout)
+{
+ uint16 mbxro,mbxl,configadr;
+ int wkc=0;
+ int wkc2;
+ uint16 SMstat;
+ uint8 SMcontr;
+ ec_mbxheadert *mbxh;
+ ec_emcyt *EMp;
+ ec_mbxerrort *MBXEp;
+
+ configadr = context->slavelist[slave].configadr;
+ mbxl = context->slavelist[slave].mbx_rl;
+ if ((mbxl > 0) && (mbxl <= EC_MAXMBX))
+ {
+ osal_timert timer;
+
+ osal_timer_start(&timer, timeout);
+ wkc = 0;
+ do /* wait for read mailbox available */
+ {
+ SMstat = 0;
+ wkc = ecx_FPRD(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+ SMstat = etohs(SMstat);
+ if (((SMstat & 0x08) == 0) && (timeout > EC_LOCALDELAY))
+ {
+ osal_usleep(EC_LOCALDELAY);
+ }
+ }
+ while (((wkc <= 0) || ((SMstat & 0x08) == 0)) && (osal_timer_is_expired(&timer) == FALSE));
+
+ if ((wkc > 0) && ((SMstat & 0x08) > 0)) /* read mailbox available ? */
+ {
+ mbxro = context->slavelist[slave].mbx_ro;
+ mbxh = (ec_mbxheadert *)mbx;
+ do
+ {
+ wkc = ecx_FPRD(context->port, configadr, mbxro, mbxl, mbx, EC_TIMEOUTRET); /* get mailbox */
+ if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == 0x00)) /* Mailbox error response? */
+ {
+ MBXEp = (ec_mbxerrort *)mbx;
+ ecx_mbxerror(context, slave, etohs(MBXEp->Detail));
+ wkc = 0; /* prevent emergency to cascade up, it is already handled. */
+ }
+ else if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == ECT_MBXT_COE)) /* CoE response? */
+ {
+ EMp = (ec_emcyt *)mbx;
+ if ((etohs(EMp->CANOpen) >> 12) == 0x01) /* Emergency request? */
+ {
+ ecx_mbxemergencyerror(context, slave, etohs(EMp->ErrorCode), EMp->ErrorReg,
+ EMp->bData, etohs(EMp->w1), etohs(EMp->w2));
+ wkc = 0; /* prevent emergency to cascade up, it is already handled. */
+ }
+ }
+ else if ((wkc > 0) && ((mbxh->mbxtype & 0x0f) == ECT_MBXT_EOE)) /* EoE response? */
+ {
+ ec_EOEt * eoembx = (ec_EOEt *)mbx;
+ uint16 frameinfo1 = etohs(eoembx->frameinfo1);
+ /* All non fragment data frame types are expected to be handled by
+ * slave send/receive API if the EoE hook is set
+ */
+ if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) == EOE_FRAG_DATA)
+ {
+ if (context->EOEhook)
+ {
+ if (context->EOEhook(context, slave, eoembx) > 0)
+ {
+ /* Fragment handled by EoE hook */
+ wkc = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (wkc <= 0) /* read mailbox lost */
+ {
+ SMstat ^= 0x0200; /* toggle repeat request */
+ SMstat = htoes(SMstat);
+ wkc2 = ecx_FPWR(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+ SMstat = etohs(SMstat);
+ do /* wait for toggle ack */
+ {
+ wkc2 = ecx_FPRD(context->port, configadr, ECT_REG_SM1CONTR, sizeof(SMcontr), &SMcontr, EC_TIMEOUTRET);
+ } while (((wkc2 <= 0) || ((SMcontr & 0x02) != (HI_BYTE(SMstat) & 0x02))) && (osal_timer_is_expired(&timer) == FALSE));
+ do /* wait for read mailbox available */
+ {
+ wkc2 = ecx_FPRD(context->port, configadr, ECT_REG_SM1STAT, sizeof(SMstat), &SMstat, EC_TIMEOUTRET);
+ SMstat = etohs(SMstat);
+ if (((SMstat & 0x08) == 0) && (timeout > EC_LOCALDELAY))
+ {
+ osal_usleep(EC_LOCALDELAY);
+ }
+ } while (((wkc2 <= 0) || ((SMstat & 0x08) == 0)) && (osal_timer_is_expired(&timer) == FALSE));
+ }
+ }
+ } while ((wkc <= 0) && (osal_timer_is_expired(&timer) == FALSE)); /* if WKC<=0 repeat */
+ }
+ else /* no read mailbox available */
+ {
+ wkc = 0;
+ }
+ }
+
+ return wkc;
+}
+
+/** Dump complete EEPROM data from slave in buffer.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[out] esibuf = EEPROM data buffer, make sure it is big enough.
+ */
+void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf)
+{
+ int address, incr;
+ uint16 configadr;
+ uint64 *p64;
+ uint16 *p16;
+ uint64 edat;
+ uint8 eectl = context->slavelist[slave].eep_pdi;
+
+ ecx_eeprom2master(context, slave); /* set eeprom control to master */
+ configadr = context->slavelist[slave].configadr;
+ address = ECT_SII_START;
+ p16=(uint16*)esibuf;
+ if (context->slavelist[slave].eep_8byte)
+ {
+ incr = 4;
+ }
+ else
+ {
+ incr = 2;
+ }
+ do
+ {
+ edat = ecx_readeepromFP(context, configadr, address, EC_TIMEOUTEEP);
+ p64 = (uint64*)p16;
+ *p64 = edat;
+ p16 += incr;
+ address += incr;
+ } while ((address <= (EC_MAXEEPBUF >> 1)) && ((uint32)edat != 0xffffffff));
+
+ if (eectl)
+ {
+ ecx_eeprom2pdi(context, slave); /* if eeprom control was previously pdi then restore */
+ }
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 32bit
+ */
+uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout)
+{
+ uint16 configadr;
+
+ ecx_eeprom2master(context, slave); /* set eeprom control to master */
+ configadr = context->slavelist[slave].configadr;
+
+ return ((uint32)ecx_readeepromFP(context, configadr, eeproma, timeout));
+}
+
+/** Write EEPROM to slave bypassing cache.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] data = 16bit data
+ * @param[in] timeout = Timeout in us.
+ * @return >0 if OK
+ */
+int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout)
+{
+ uint16 configadr;
+
+ ecx_eeprom2master(context, slave); /* set eeprom control to master */
+ configadr = context->slavelist[slave].configadr;
+ return (ecx_writeeepromFP(context, configadr, eeproma, data, timeout));
+}
+
+/** Set eeprom control to master. Only if set to PDI.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @return >0 if OK
+ */
+int ecx_eeprom2master(ecx_contextt *context, uint16 slave)
+{
+ int wkc = 1, cnt = 0;
+ uint16 configadr;
+ uint8 eepctl;
+
+ if ( context->slavelist[slave].eep_pdi )
+ {
+ configadr = context->slavelist[slave].configadr;
+ eepctl = 2;
+ do
+ {
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* force Eeprom from PDI */
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ eepctl = 0;
+ cnt = 0;
+ do
+ {
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* set Eeprom to master */
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ context->slavelist[slave].eep_pdi = 0;
+ }
+
+ return wkc;
+}
+
+/** Set eeprom control to PDI. Only if set to master.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @return >0 if OK
+ */
+int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave)
+{
+ int wkc = 1, cnt = 0;
+ uint16 configadr;
+ uint8 eepctl;
+
+ if ( !context->slavelist[slave].eep_pdi )
+ {
+ configadr = context->slavelist[slave].configadr;
+ eepctl = 1;
+ do
+ {
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCFG, sizeof(eepctl), &eepctl , EC_TIMEOUTRET); /* set Eeprom to PDI */
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ context->slavelist[slave].eep_pdi = 1;
+ }
+
+ return wkc;
+}
+
+uint16 ecx_eeprom_waitnotbusyAP(ecx_contextt *context, uint16 aiadr,uint16 *estat, int timeout)
+{
+ int wkc, cnt = 0, retval = 0;
+ osal_timert timer;
+
+ osal_timer_start(&timer, timeout);
+ do
+ {
+ if (cnt++)
+ {
+ osal_usleep(EC_LOCALDELAY);
+ }
+ *estat = 0;
+ wkc=ecx_APRD(context->port, aiadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
+ *estat = etohs(*estat);
+ }
+ while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
+ if ((*estat & EC_ESTAT_BUSY) == 0)
+ {
+ retval = 1;
+ }
+
+ return retval;
+}
+
+/** Read EEPROM from slave bypassing cache. APRD method.
+ * @param[in] context = context struct
+ * @param[in] aiadr = auto increment address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ */
+uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout)
+{
+ uint16 estat;
+ uint32 edat32;
+ uint64 edat64;
+ ec_eepromt ed;
+ int wkc, cnt, nackcnt = 0;
+
+ edat64 = 0;
+ edat32 = 0;
+ if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_EMASK) /* error bits are set */
+ {
+ estat = htoes(EC_ECMD_NOP); /* clear error bits */
+ wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+ }
+
+ do
+ {
+ ed.comm = htoes(EC_ECMD_READ);
+ ed.addr = htoes(eeproma);
+ ed.d2 = 0x0000;
+ cnt = 0;
+ do
+ {
+ wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ if (wkc)
+ {
+ osal_usleep(EC_LOCALDELAY);
+ estat = 0x0000;
+ if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_NACK)
+ {
+ nackcnt++;
+ osal_usleep(EC_LOCALDELAY * 5);
+ }
+ else
+ {
+ nackcnt = 0;
+ if (estat & EC_ESTAT_R64)
+ {
+ cnt = 0;
+ do
+ {
+ wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ }
+ else
+ {
+ cnt = 0;
+ do
+ {
+ wkc = ecx_APRD(context->port, aiadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ edat64=(uint64)edat32;
+ }
+ }
+ }
+ }
+ }
+ while ((nackcnt > 0) && (nackcnt < 3));
+ }
+
+ return edat64;
+}
+
+/** Write EEPROM to slave bypassing cache. APWR method.
+ * @param[in] context = context struct
+ * @param[in] aiadr = configured address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] data = 16bit data
+ * @param[in] timeout = Timeout in us.
+ * @return >0 if OK
+ */
+int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout)
+{
+ uint16 estat;
+ ec_eepromt ed;
+ int wkc, rval = 0, cnt = 0, nackcnt = 0;
+
+ if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_EMASK) /* error bits are set */
+ {
+ estat = htoes(EC_ECMD_NOP); /* clear error bits */
+ wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+ }
+ do
+ {
+ cnt = 0;
+ do
+ {
+ wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+
+ ed.comm = EC_ECMD_WRITE;
+ ed.addr = eeproma;
+ ed.d2 = 0x0000;
+ cnt = 0;
+ do
+ {
+ wkc = ecx_APWR(context->port, aiadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ if (wkc)
+ {
+ osal_usleep(EC_LOCALDELAY * 2);
+ estat = 0x0000;
+ if (ecx_eeprom_waitnotbusyAP(context, aiadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_NACK)
+ {
+ nackcnt++;
+ osal_usleep(EC_LOCALDELAY * 5);
+ }
+ else
+ {
+ nackcnt = 0;
+ rval = 1;
+ }
+ }
+ }
+
+ }
+ while ((nackcnt > 0) && (nackcnt < 3));
+ }
+
+ return rval;
+}
+
+uint16 ecx_eeprom_waitnotbusyFP(ecx_contextt *context, uint16 configadr,uint16 *estat, int timeout)
+{
+ int wkc, cnt = 0, retval = 0;
+ osal_timert timer;
+
+ osal_timer_start(&timer, timeout);
+ do
+ {
+ if (cnt++)
+ {
+ osal_usleep(EC_LOCALDELAY);
+ }
+ *estat = 0;
+ wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(*estat), estat, EC_TIMEOUTRET);
+ *estat = etohs(*estat);
+ }
+ while (((wkc <= 0) || ((*estat & EC_ESTAT_BUSY) > 0)) && (osal_timer_is_expired(&timer) == FALSE)); /* wait for eeprom ready */
+ if ((*estat & EC_ESTAT_BUSY) == 0)
+ {
+ retval = 1;
+ }
+
+ return retval;
+}
+
+/** Read EEPROM from slave bypassing cache. FPRD method.
+ * @param[in] context = context struct
+ * @param[in] configadr = configured address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ */
+uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout)
+{
+ uint16 estat;
+ uint32 edat32;
+ uint64 edat64;
+ ec_eepromt ed;
+ int wkc, cnt, nackcnt = 0;
+
+ edat64 = 0;
+ edat32 = 0;
+ if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_EMASK) /* error bits are set */
+ {
+ estat = htoes(EC_ECMD_NOP); /* clear error bits */
+ wkc=ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+ }
+
+ do
+ {
+ ed.comm = htoes(EC_ECMD_READ);
+ ed.addr = htoes(eeproma);
+ ed.d2 = 0x0000;
+ cnt = 0;
+ do
+ {
+ wkc=ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ if (wkc)
+ {
+ osal_usleep(EC_LOCALDELAY);
+ estat = 0x0000;
+ if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_NACK)
+ {
+ nackcnt++;
+ osal_usleep(EC_LOCALDELAY * 5);
+ }
+ else
+ {
+ nackcnt = 0;
+ if (estat & EC_ESTAT_R64)
+ {
+ cnt = 0;
+ do
+ {
+ wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat64), &edat64, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ }
+ else
+ {
+ cnt = 0;
+ do
+ {
+ wkc=ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat32), &edat32, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ edat64=(uint64)edat32;
+ }
+ }
+ }
+ }
+ }
+ while ((nackcnt > 0) && (nackcnt < 3));
+ }
+
+ return edat64;
+}
+
+/** Write EEPROM to slave bypassing cache. FPWR method.
+ * @param[in] context = context struct
+ * @param[in] configadr = configured address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] data = 16bit data
+ * @param[in] timeout = Timeout in us.
+ * @return >0 if OK
+ */
+int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout)
+{
+ uint16 estat;
+ ec_eepromt ed;
+ int wkc, rval = 0, cnt = 0, nackcnt = 0;
+
+ if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_EMASK) /* error bits are set */
+ {
+ estat = htoes(EC_ECMD_NOP); /* clear error bits */
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+ }
+ do
+ {
+ cnt = 0;
+ do
+ {
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPDAT, sizeof(data), &data, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ ed.comm = EC_ECMD_WRITE;
+ ed.addr = eeproma;
+ ed.d2 = 0x0000;
+ cnt = 0;
+ do
+ {
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ if (wkc)
+ {
+ osal_usleep(EC_LOCALDELAY * 2);
+ estat = 0x0000;
+ if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+ {
+ if (estat & EC_ESTAT_NACK)
+ {
+ nackcnt++;
+ osal_usleep(EC_LOCALDELAY * 5);
+ }
+ else
+ {
+ nackcnt = 0;
+ rval = 1;
+ }
+ }
+ }
+ }
+ while ((nackcnt > 0) && (nackcnt < 3));
+ }
+
+ return rval;
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 1, make request to slave.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ */
+void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma)
+{
+ uint16 configadr, estat;
+ ec_eepromt ed;
+ int wkc, cnt = 0;
+
+ ecx_eeprom2master(context, slave); /* set eeprom control to master */
+ configadr = context->slavelist[slave].configadr;
+ if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, EC_TIMEOUTEEP))
+ {
+ if (estat & EC_ESTAT_EMASK) /* error bits are set */
+ {
+ estat = htoes(EC_ECMD_NOP); /* clear error bits */
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(estat), &estat, EC_TIMEOUTRET3);
+ }
+ ed.comm = htoes(EC_ECMD_READ);
+ ed.addr = htoes(eeproma);
+ ed.d2 = 0x0000;
+ do
+ {
+ wkc = ecx_FPWR(context->port, configadr, ECT_REG_EEPCTL, sizeof(ed), &ed, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ }
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 2, actual read from slave.
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 32bit
+ */
+uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout)
+{
+ uint16 estat, configadr;
+ uint32 edat;
+ int wkc, cnt = 0;
+
+ configadr = context->slavelist[slave].configadr;
+ edat = 0;
+ estat = 0x0000;
+ if (ecx_eeprom_waitnotbusyFP(context, configadr, &estat, timeout))
+ {
+ do
+ {
+ wkc = ecx_FPRD(context->port, configadr, ECT_REG_EEPDAT, sizeof(edat), &edat, EC_TIMEOUTRET);
+ }
+ while ((wkc <= 0) && (cnt++ < EC_DEFAULTRETRIES));
+ }
+
+ return edat;
+}
+
+/** Push index of segmented LRD/LWR/LRW combination.
+ * @param[in] context = context struct
+ * @param[in] idx = Used datagram index.
+ * @param[in] data = Pointer to process data segment.
+ * @param[in] length = Length of data segment in bytes.
+ */
+static void ecx_pushindex(ecx_contextt *context, uint8 idx, void *data, uint16 length)
+{
+ if(context->idxstack->pushed < EC_MAXBUF)
+ {
+ context->idxstack->idx[context->idxstack->pushed] = idx;
+ context->idxstack->data[context->idxstack->pushed] = data;
+ context->idxstack->length[context->idxstack->pushed] = length;
+ context->idxstack->pushed++;
+ }
+}
+
+/** Pull index of segmented LRD/LWR/LRW combination.
+ * @param[in] context = context struct
+ * @return Stack location, -1 if stack is empty.
+ */
+static int ecx_pullindex(ecx_contextt *context)
+{
+ int rval = -1;
+ if(context->idxstack->pulled < context->idxstack->pushed)
+ {
+ rval = context->idxstack->pulled;
+ context->idxstack->pulled++;
+ }
+
+ return rval;
+}
+
+/**
+ * Clear the idx stack.
+ *
+ * @param context = context struct
+ */
+static void ecx_clearindex(ecx_contextt *context) {
+
+ context->idxstack->pushed = 0;
+ context->idxstack->pulled = 0;
+
+}
+
+/** Transmit processdata to slaves.
+ * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+ * Both the input and output processdata are transmitted.
+ * The outputs with the actual data, the inputs have a placeholder.
+ * The inputs are gathered with the receive processdata function.
+ * In contrast to the base LRW function this function is non-blocking.
+ * If the processdata does not fit in one datagram, multiple are used.
+ * In order to recombine the slave response, a stack is used.
+ * @param[in] context = context struct
+ * @param[in] group = group number
+ * @return >0 if processdata is transmitted.
+ */
+static int ecx_main_send_processdata(ecx_contextt *context, uint8 group, boolean use_overlap_io)
+{
+ uint32 LogAdr;
+ uint16 w1, w2;
+ int length, sublength;
+ uint8 idx;
+ int wkc;
+ uint8* data;
+ boolean first=FALSE;
+ uint16 currentsegment = 0;
+ uint32 iomapinputoffset;
+
+ wkc = 0;
+ if(context->grouplist[group].hasdc)
+ {
+ first = TRUE;
+ }
+
+ /* For overlapping IO map use the biggest */
+ if(use_overlap_io == TRUE)
+ {
+ /* For overlap IOmap make the frame EQ big to biggest part */
+ length = (context->grouplist[group].Obytes > context->grouplist[group].Ibytes) ?
+ context->grouplist[group].Obytes : context->grouplist[group].Ibytes;
+ /* Save the offset used to compensate where to save inputs when frame returns */
+ iomapinputoffset = context->grouplist[group].Obytes;
+ }
+ else
+ {
+ length = context->grouplist[group].Obytes + context->grouplist[group].Ibytes;
+ iomapinputoffset = 0;
+ }
+
+ LogAdr = context->grouplist[group].logstartaddr;
+ if(length)
+ {
+
+ wkc = 1;
+ /* LRW blocked by one or more slaves ? */
+ if(context->grouplist[group].blockLRW)
+ {
+ /* if inputs available generate LRD */
+ if(context->grouplist[group].Ibytes)
+ {
+ currentsegment = context->grouplist[group].Isegment;
+ data = context->grouplist[group].inputs;
+ length = context->grouplist[group].Ibytes;
+ LogAdr += context->grouplist[group].Obytes;
+ /* segment transfer if needed */
+ do
+ {
+ if(currentsegment == context->grouplist[group].Isegment)
+ {
+ sublength = context->grouplist[group].IOsegment[currentsegment++] - context->grouplist[group].Ioffset;
+ }
+ else
+ {
+ sublength = context->grouplist[group].IOsegment[currentsegment++];
+ }
+ /* get new index */
+ idx = ecx_getindex(context->port);
+ w1 = LO_WORD(LogAdr);
+ w2 = HI_WORD(LogAdr);
+ ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LRD, idx, w1, w2, sublength, data);
+ if(first)
+ {
+ context->DCl = sublength;
+ /* FPRMW in second datagram */
+ context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
+ context->slavelist[context->grouplist[group].DCnext].configadr,
+ ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
+ first = FALSE;
+ }
+ /* send frame */
+ ecx_outframe_red(context->port, idx);
+ /* push index and data pointer on stack */
+ ecx_pushindex(context, idx, data, sublength);
+ length -= sublength;
+ LogAdr += sublength;
+ data += sublength;
+ } while (length && (currentsegment < context->grouplist[group].nsegments));
+ }
+ /* if outputs available generate LWR */
+ if(context->grouplist[group].Obytes)
+ {
+ data = context->grouplist[group].outputs;
+ length = context->grouplist[group].Obytes;
+ LogAdr = context->grouplist[group].logstartaddr;
+ currentsegment = 0;
+ /* segment transfer if needed */
+ do
+ {
+ sublength = context->grouplist[group].IOsegment[currentsegment++];
+ if((length - sublength) < 0)
+ {
+ sublength = length;
+ }
+ /* get new index */
+ idx = ecx_getindex(context->port);
+ w1 = LO_WORD(LogAdr);
+ w2 = HI_WORD(LogAdr);
+ ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LWR, idx, w1, w2, sublength, data);
+ if(first)
+ {
+ context->DCl = sublength;
+ /* FPRMW in second datagram */
+ context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
+ context->slavelist[context->grouplist[group].DCnext].configadr,
+ ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
+ first = FALSE;
+ }
+ /* send frame */
+ ecx_outframe_red(context->port, idx);
+ /* push index and data pointer on stack */
+ ecx_pushindex(context, idx, data, sublength);
+ length -= sublength;
+ LogAdr += sublength;
+ data += sublength;
+ } while (length && (currentsegment < context->grouplist[group].nsegments));
+ }
+ }
+ /* LRW can be used */
+ else
+ {
+ if (context->grouplist[group].Obytes)
+ {
+ data = context->grouplist[group].outputs;
+ }
+ else
+ {
+ data = context->grouplist[group].inputs;
+ /* Clear offset, don't compensate for overlapping IOmap if we only got inputs */
+ iomapinputoffset = 0;
+ }
+ /* segment transfer if needed */
+ do
+ {
+ sublength = context->grouplist[group].IOsegment[currentsegment++];
+ /* get new index */
+ idx = ecx_getindex(context->port);
+ w1 = LO_WORD(LogAdr);
+ w2 = HI_WORD(LogAdr);
+ ecx_setupdatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_LRW, idx, w1, w2, sublength, data);
+ if(first)
+ {
+ context->DCl = sublength;
+ /* FPRMW in second datagram */
+ context->DCtO = ecx_adddatagram(context->port, &(context->port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE,
+ context->slavelist[context->grouplist[group].DCnext].configadr,
+ ECT_REG_DCSYSTIME, sizeof(int64), context->DCtime);
+ first = FALSE;
+ }
+ /* send frame */
+ ecx_outframe_red(context->port, idx);
+ /* push index and data pointer on stack.
+ * the iomapinputoffset compensate for where the inputs are stored
+ * in the IOmap if we use an overlapping IOmap. If a regular IOmap
+ * is used it should always be 0.
+ */
+ ecx_pushindex(context, idx, (data + iomapinputoffset), sublength);
+ length -= sublength;
+ LogAdr += sublength;
+ data += sublength;
+ } while (length && (currentsegment < context->grouplist[group].nsegments));
+ }
+ }
+
+ return wkc;
+}
+
+/** Transmit processdata to slaves.
+* Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+* Both the input and output processdata are transmitted in the overlapped IOmap.
+* The outputs with the actual data, the inputs replace the output data in the
+* returning frame. The inputs are gathered with the receive processdata function.
+* In contrast to the base LRW function this function is non-blocking.
+* If the processdata does not fit in one datagram, multiple are used.
+* In order to recombine the slave response, a stack is used.
+* @param[in] context = context struct
+* @param[in] group = group number
+* @return >0 if processdata is transmitted.
+*/
+int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group)
+{
+ return ecx_main_send_processdata(context, group, TRUE);
+}
+
+/** Transmit processdata to slaves.
+* Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+* Both the input and output processdata are transmitted.
+* The outputs with the actual data, the inputs have a placeholder.
+* The inputs are gathered with the receive processdata function.
+* In contrast to the base LRW function this function is non-blocking.
+* If the processdata does not fit in one datagram, multiple are used.
+* In order to recombine the slave response, a stack is used.
+* @param[in] context = context struct
+* @param[in] group = group number
+* @return >0 if processdata is transmitted.
+*/
+int ecx_send_processdata_group(ecx_contextt *context, uint8 group)
+{
+ return ecx_main_send_processdata(context, group, FALSE);
+}
+
+/** Receive processdata from slaves.
+ * Second part from ec_send_processdata().
+ * Received datagrams are recombined with the processdata with help from the stack.
+ * If a datagram contains input processdata it copies it to the processdata structure.
+ * @param[in] context = context struct
+ * @param[in] group = group number
+ * @param[in] timeout = Timeout in us.
+ * @return Work counter.
+ */
+int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout)
+{
+ int pos, idx;
+ int wkc = 0, wkc2;
+ uint16 le_wkc = 0;
+ int valid_wkc = 0;
+ int64 le_DCtime;
+ boolean first = FALSE;
+
+ if(context->grouplist[group].hasdc)
+ {
+ first = TRUE;
+ }
+ /* get first index */
+ pos = ecx_pullindex(context);
+ /* read the same number of frames as send */
+ while (pos >= 0)
+ {
+ idx = context->idxstack->idx[pos];
+ wkc2 = ecx_waitinframe(context->port, context->idxstack->idx[pos], timeout);
+ /* check if there is input data in frame */
+ if (wkc2 > EC_NOFRAME)
+ {
+ if((context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD) || (context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRW))
+ {
+ if(first)
+ {
+ memcpy(context->idxstack->data[pos], &(context->port->rxbuf[idx][EC_HEADERSIZE]), context->DCl);
+ memcpy(&le_wkc, &(context->port->rxbuf[idx][EC_HEADERSIZE + context->DCl]), EC_WKCSIZE);
+ wkc = etohs(le_wkc);
+ memcpy(&le_DCtime, &(context->port->rxbuf[idx][context->DCtO]), sizeof(le_DCtime));
+ *(context->DCtime) = etohll(le_DCtime);
+ first = FALSE;
+ }
+ else
+ {
+ /* copy input data back to process data buffer */
+ memcpy(context->idxstack->data[pos], &(context->port->rxbuf[idx][EC_HEADERSIZE]), context->idxstack->length[pos]);
+ wkc += wkc2;
+ }
+ valid_wkc = 1;
+ }
+ else if(context->port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LWR)
+ {
+ if(first)
+ {
+ memcpy(&le_wkc, &(context->port->rxbuf[idx][EC_HEADERSIZE + context->DCl]), EC_WKCSIZE);
+ /* output WKC counts 2 times when using LRW, emulate the same for LWR */
+ wkc = etohs(le_wkc) * 2;
+ memcpy(&le_DCtime, &(context->port->rxbuf[idx][context->DCtO]), sizeof(le_DCtime));
+ *(context->DCtime) = etohll(le_DCtime);
+ first = FALSE;
+ }
+ else
+ {
+ /* output WKC counts 2 times when using LRW, emulate the same for LWR */
+ wkc += wkc2 * 2;
+ }
+ valid_wkc = 1;
+ }
+ }
+ /* release buffer */
+ ecx_setbufstat(context->port, idx, EC_BUF_EMPTY);
+ /* get next index */
+ pos = ecx_pullindex(context);
+ }
+
+ ecx_clearindex(context);
+
+ /* if no frames has arrived */
+ if (valid_wkc == 0)
+ {
+ return EC_NOFRAME;
+ }
+ return wkc;
+}
+
+
+int ecx_send_processdata(ecx_contextt *context)
+{
+ return ecx_send_processdata_group(context, 0);
+}
+
+int ecx_send_overlap_processdata(ecx_contextt *context)
+{
+ return ecx_send_overlap_processdata_group(context, 0);
+}
+
+int ecx_receive_processdata(ecx_contextt *context, int timeout)
+{
+ return ecx_receive_processdata_group(context, 0, timeout);
+}
+
+#ifdef EC_VER1
+void ec_pusherror(const ec_errort *Ec)
+{
+ ecx_pusherror(&ecx_context, Ec);
+}
+
+boolean ec_poperror(ec_errort *Ec)
+{
+ return ecx_poperror(&ecx_context, Ec);
+}
+
+boolean ec_iserror(void)
+{
+ return ecx_iserror(&ecx_context);
+}
+
+void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode)
+{
+ ecx_packeterror(&ecx_context, Slave, Index, SubIdx, ErrorCode);
+}
+
+/** Initialise lib in single NIC mode
+ * @param[in] ifname = Dev name, f.e. "eth0"
+ * @return >0 if OK
+ * @see ecx_init
+ */
+int ec_init(const char * ifname)
+{
+ return ecx_init(&ecx_context, ifname);
+}
+
+/** Initialise lib in redundant NIC mode
+ * @param[in] ifname = Primary Dev name, f.e. "eth0"
+ * @param[in] if2name = Secondary Dev name, f.e. "eth1"
+ * @return >0 if OK
+ * @see ecx_init_redundant
+ */
+int ec_init_redundant(const char *ifname, char *if2name)
+{
+ return ecx_init_redundant (&ecx_context, &ecx_redport, ifname, if2name);
+}
+
+/** Close lib.
+ * @see ecx_close
+ */
+void ec_close(void)
+{
+ ecx_close(&ecx_context);
+};
+
+/** Read one byte from slave EEPROM via cache.
+ * If the cache location is empty then a read request is made to the slave.
+ * Depending on the slave capabillities the request is 4 or 8 bytes.
+ * @param[in] slave = slave number
+ * @param[in] address = eeprom address in bytes (slave uses words)
+ * @return requested byte, if not available then 0xff
+ * @see ecx_siigetbyte
+ */
+uint8 ec_siigetbyte(uint16 slave, uint16 address)
+{
+ return ecx_siigetbyte (&ecx_context, slave, address);
+}
+
+/** Find SII section header in slave EEPROM.
+ * @param[in] slave = slave number
+ * @param[in] cat = section category
+ * @return byte address of section at section length entry, if not available then 0
+ * @see ecx_siifind
+ */
+int16 ec_siifind(uint16 slave, uint16 cat)
+{
+ return ecx_siifind (&ecx_context, slave, cat);
+}
+
+/** Get string from SII string section in slave EEPROM.
+ * @param[out] str = requested string, 0x00 if not found
+ * @param[in] slave = slave number
+ * @param[in] Sn = string number
+ * @see ecx_siistring
+ */
+void ec_siistring(char *str, uint16 slave, uint16 Sn)
+{
+ ecx_siistring(&ecx_context, str, slave, Sn);
+}
+
+/** Get FMMU data from SII FMMU section in slave EEPROM.
+ * @param[in] slave = slave number
+ * @param[out] FMMU = FMMU struct from SII, max. 4 FMMU's
+ * @return number of FMMU's defined in section
+ * @see ecx_siiFMMU
+ */
+uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU)
+{
+ return ecx_siiFMMU (&ecx_context, slave, FMMU);
+}
+
+/** Get SM data from SII SM section in slave EEPROM.
+ * @param[in] slave = slave number
+ * @param[out] SM = first SM struct from SII
+ * @return number of SM's defined in section
+ * @see ecx_siiSM
+ */
+uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM)
+{
+ return ecx_siiSM (&ecx_context, slave, SM);
+}
+
+/** Get next SM data from SII SM section in slave EEPROM.
+ * @param[in] slave = slave number
+ * @param[out] SM = first SM struct from SII
+ * @param[in] n = SM number
+ * @return >0 if OK
+ * @see ecx_siiSMnext
+ */
+uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n)
+{
+ return ecx_siiSMnext (&ecx_context, slave, SM, n);
+}
+
+/** Get PDO data from SII PDO section in slave EEPROM.
+ * @param[in] slave = slave number
+ * @param[out] PDO = PDO struct from SII
+ * @param[in] t = 0=RXPDO 1=TXPDO
+ * @return mapping size in bits of PDO
+ * @see ecx_siiPDO
+ */
+int ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t)
+{
+ return ecx_siiPDO (&ecx_context, slave, PDO, t);
+}
+
+/** Read all slave states in ec_slave.
+ * @return lowest state found
+ * @see ecx_readstate
+ */
+int ec_readstate(void)
+{
+ return ecx_readstate (&ecx_context);
+}
+
+/** Write slave state, if slave = 0 then write to all slaves.
+ * The function does not check if the actual state is changed.
+ * @param[in] slave = Slave number, 0 = master
+ * @return 0
+ * @see ecx_writestate
+ */
+int ec_writestate(uint16 slave)
+{
+ return ecx_writestate(&ecx_context, slave);
+}
+
+/** Check actual slave state.
+ * This is a blocking function.
+ * @param[in] slave = Slave number, 0 = all slaves
+ * @param[in] reqstate = Requested state
+ * @param[in] timeout = Timeout value in us
+ * @return Requested state, or found state after timeout.
+ * @see ecx_statecheck
+ */
+uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout)
+{
+ return ecx_statecheck (&ecx_context, slave, reqstate, timeout);
+}
+
+/** Check if IN mailbox of slave is empty.
+ * @param[in] slave = Slave number
+ * @param[in] timeout = Timeout in us
+ * @return >0 is success
+ * @see ecx_mbxempty
+ */
+int ec_mbxempty(uint16 slave, int timeout)
+{
+ return ecx_mbxempty (&ecx_context, slave, timeout);
+}
+
+/** Write IN mailbox to slave.
+ * @param[in] slave = Slave number
+ * @param[out] mbx = Mailbox data
+ * @param[in] timeout = Timeout in us
+ * @return Work counter (>0 is success)
+ * @see ecx_mbxsend
+ */
+int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout)
+{
+ return ecx_mbxsend (&ecx_context, slave, mbx, timeout);
+}
+
+/** Read OUT mailbox from slave.
+ * Supports Mailbox Link Layer with repeat requests.
+ * @param[in] slave = Slave number
+ * @param[out] mbx = Mailbox data
+ * @param[in] timeout = Timeout in us
+ * @return Work counter (>0 is success)
+ * @see ecx_mbxreceive
+ */
+int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout)
+{
+ return ecx_mbxreceive (&ecx_context, slave, mbx, timeout);
+}
+
+/** Dump complete EEPROM data from slave in buffer.
+ * @param[in] slave = Slave number
+ * @param[out] esibuf = EEPROM data buffer, make sure it is big enough.
+ * @see ecx_esidump
+ */
+void ec_esidump(uint16 slave, uint8 *esibuf)
+{
+ ecx_esidump (&ecx_context, slave, esibuf);
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * @param[in] slave = Slave number
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 32bit
+ * @see ecx_readeeprom
+ */
+uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout)
+{
+ return ecx_readeeprom (&ecx_context, slave, eeproma, timeout);
+}
+
+/** Write EEPROM to slave bypassing cache.
+ * @param[in] slave = Slave number
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] data = 16bit data
+ * @param[in] timeout = Timeout in us.
+ * @return >0 if OK
+ * @see ecx_writeeeprom
+ */
+int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout)
+{
+ return ecx_writeeeprom (&ecx_context, slave, eeproma, data, timeout);
+}
+
+/** Set eeprom control to master. Only if set to PDI.
+ * @param[in] slave = Slave number
+ * @return >0 if OK
+ * @see ecx_eeprom2master
+ */
+int ec_eeprom2master(uint16 slave)
+{
+ return ecx_eeprom2master(&ecx_context, slave);
+}
+
+int ec_eeprom2pdi(uint16 slave)
+{
+ return ecx_eeprom2pdi(&ecx_context, slave);
+}
+
+uint16 ec_eeprom_waitnotbusyAP(uint16 aiadr,uint16 *estat, int timeout)
+{
+ return ecx_eeprom_waitnotbusyAP (&ecx_context, aiadr, estat, timeout);
+}
+
+/** Read EEPROM from slave bypassing cache. APRD method.
+ * @param[in] aiadr = auto increment address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ */
+uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout)
+{
+ return ecx_readeepromAP (&ecx_context, aiadr, eeproma, timeout);
+}
+
+/** Write EEPROM to slave bypassing cache. APWR method.
+ * @param[in] aiadr = configured address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] data = 16bit data
+ * @param[in] timeout = Timeout in us.
+ * @return >0 if OK
+ * @see ecx_writeeepromAP
+ */
+int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout)
+{
+ return ecx_writeeepromAP (&ecx_context, aiadr, eeproma, data, timeout);
+}
+
+uint16 ec_eeprom_waitnotbusyFP(uint16 configadr,uint16 *estat, int timeout)
+{
+ return ecx_eeprom_waitnotbusyFP (&ecx_context, configadr, estat, timeout);
+}
+
+/** Read EEPROM from slave bypassing cache. FPRD method.
+ * @param[in] configadr = configured address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 64bit or 32bit
+ * @see ecx_readeepromFP
+ */
+uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout)
+{
+ return ecx_readeepromFP (&ecx_context, configadr, eeproma, timeout);
+}
+
+/** Write EEPROM to slave bypassing cache. FPWR method.
+ * @param[in] configadr = configured address of slave
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @param[in] data = 16bit data
+ * @param[in] timeout = Timeout in us.
+ * @return >0 if OK
+ * @see ecx_writeeepromFP
+ */
+int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout)
+{
+ return ecx_writeeepromFP (&ecx_context, configadr, eeproma, data, timeout);
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 1, make request to slave.
+ * @param[in] slave = Slave number
+ * @param[in] eeproma = (WORD) Address in the EEPROM
+ * @see ecx_readeeprom1
+ */
+void ec_readeeprom1(uint16 slave, uint16 eeproma)
+{
+ ecx_readeeprom1 (&ecx_context, slave, eeproma);
+}
+
+/** Read EEPROM from slave bypassing cache.
+ * Parallel read step 2, actual read from slave.
+ * @param[in] slave = Slave number
+ * @param[in] timeout = Timeout in us.
+ * @return EEPROM data 32bit
+ * @see ecx_readeeprom2
+ */
+uint32 ec_readeeprom2(uint16 slave, int timeout)
+{
+ return ecx_readeeprom2 (&ecx_context, slave, timeout);
+}
+
+/** Transmit processdata to slaves.
+ * Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+ * Both the input and output processdata are transmitted.
+ * The outputs with the actual data, the inputs have a placeholder.
+ * The inputs are gathered with the receive processdata function.
+ * In contrast to the base LRW function this function is non-blocking.
+ * If the processdata does not fit in one datagram, multiple are used.
+ * In order to recombine the slave response, a stack is used.
+ * @param[in] group = group number
+ * @return >0 if processdata is transmitted.
+ * @see ecx_send_processdata_group
+ */
+int ec_send_processdata_group(uint8 group)
+{
+ return ecx_send_processdata_group (&ecx_context, group);
+}
+
+/** Transmit processdata to slaves.
+* Uses LRW, or LRD/LWR if LRW is not allowed (blockLRW).
+* Both the input and output processdata are transmitted in the overlapped IOmap.
+* The outputs with the actual data, the inputs replace the output data in the
+* returning frame. The inputs are gathered with the receive processdata function.
+* In contrast to the base LRW function this function is non-blocking.
+* If the processdata does not fit in one datagram, multiple are used.
+* In order to recombine the slave response, a stack is used.
+* @param[in] context = context struct
+* @param[in] group = group number
+* @return >0 if processdata is transmitted.
+* @see ecx_send_overlap_processdata_group
+*/
+int ec_send_overlap_processdata_group(uint8 group)
+{
+ return ecx_send_overlap_processdata_group(&ecx_context, group);
+}
+
+/** Receive processdata from slaves.
+ * Second part from ec_send_processdata().
+ * Received datagrams are recombined with the processdata with help from the stack.
+ * If a datagram contains input processdata it copies it to the processdata structure.
+ * @param[in] group = group number
+ * @param[in] timeout = Timeout in us.
+ * @return Work counter.
+ * @see ecx_receive_processdata_group
+ */
+int ec_receive_processdata_group(uint8 group, int timeout)
+{
+ return ecx_receive_processdata_group (&ecx_context, group, timeout);
+}
+
+int ec_send_processdata(void)
+{
+ return ec_send_processdata_group(0);
+}
+
+int ec_send_overlap_processdata(void)
+{
+ return ec_send_overlap_processdata_group(0);
+}
+
+int ec_receive_processdata(int timeout)
+{
+ return ec_receive_processdata_group(0, timeout);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatmain.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,526 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatmain.c
+ */
+
+#ifndef _ethercatmain_
+#define _ethercatmain_
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** max. entries in EtherCAT error list */
+#define EC_MAXELIST 64
+/** max. length of readable name in slavelist and Object Description List */
+#define EC_MAXNAME 40
+/** max. number of slaves in array */
+#define EC_MAXSLAVE 200
+/** max. number of groups */
+#define EC_MAXGROUP 2
+/** max. number of IO segments per group */
+#define EC_MAXIOSEGMENTS 64
+/** max. mailbox size */
+#define EC_MAXMBX 1486
+/** max. eeprom PDO entries */
+#define EC_MAXEEPDO 0x200
+/** max. SM used */
+#define EC_MAXSM 8
+/** max. FMMU used */
+#define EC_MAXFMMU 4
+/** max. Adapter */
+#define EC_MAXLEN_ADAPTERNAME 128
+/** define maximum number of concurrent threads in mapping */
+#define EC_MAX_MAPT 1
+
+typedef struct ec_adapter ec_adaptert;
+struct ec_adapter
+{
+ char name[EC_MAXLEN_ADAPTERNAME];
+ char desc[EC_MAXLEN_ADAPTERNAME];
+ ec_adaptert *next;
+};
+
+/** record for FMMU */
+PACKED_BEGIN
+typedef struct PACKED ec_fmmu
+{
+ uint32 LogStart;
+ uint16 LogLength;
+ uint8 LogStartbit;
+ uint8 LogEndbit;
+ uint16 PhysStart;
+ uint8 PhysStartBit;
+ uint8 FMMUtype;
+ uint8 FMMUactive;
+ uint8 unused1;
+ uint16 unused2;
+} ec_fmmut;
+PACKED_END
+
+/** record for sync manager */
+PACKED_BEGIN
+typedef struct PACKED ec_sm
+{
+ uint16 StartAddr;
+ uint16 SMlength;
+ uint32 SMflags;
+} ec_smt;
+PACKED_END
+
+PACKED_BEGIN
+typedef struct PACKED ec_state_status
+{
+ uint16 State;
+ uint16 Unused;
+ uint16 ALstatuscode;
+} ec_state_status;
+PACKED_END
+
+#define ECT_MBXPROT_AOE 0x0001
+#define ECT_MBXPROT_EOE 0x0002
+#define ECT_MBXPROT_COE 0x0004
+#define ECT_MBXPROT_FOE 0x0008
+#define ECT_MBXPROT_SOE 0x0010
+#define ECT_MBXPROT_VOE 0x0020
+
+#define ECT_COEDET_SDO 0x01
+#define ECT_COEDET_SDOINFO 0x02
+#define ECT_COEDET_PDOASSIGN 0x04
+#define ECT_COEDET_PDOCONFIG 0x08
+#define ECT_COEDET_UPLOAD 0x10
+#define ECT_COEDET_SDOCA 0x20
+
+#define EC_SMENABLEMASK 0xfffeffff
+
+/** for list of ethercat slaves detected */
+typedef struct ec_slave
+{
+ /** state of slave */
+ uint16 state;
+ /** AL status code */
+ uint16 ALstatuscode;
+ /** Configured address */
+ uint16 configadr;
+ /** Alias address */
+ uint16 aliasadr;
+ /** Manufacturer from EEprom */
+ uint32 eep_man;
+ /** ID from EEprom */
+ uint32 eep_id;
+ /** revision from EEprom */
+ uint32 eep_rev;
+ /** Interface type */
+ uint16 Itype;
+ /** Device type */
+ uint16 Dtype;
+ /** output bits */
+ uint16 Obits;
+ /** output bytes, if Obits < 8 then Obytes = 0 */
+ uint32 Obytes;
+ /** output pointer in IOmap buffer */
+ uint8 *outputs;
+ /** startbit in first output byte */
+ uint8 Ostartbit;
+ /** input bits */
+ uint16 Ibits;
+ /** input bytes, if Ibits < 8 then Ibytes = 0 */
+ uint32 Ibytes;
+ /** input pointer in IOmap buffer */
+ uint8 *inputs;
+ /** startbit in first input byte */
+ uint8 Istartbit;
+ /** SM structure */
+ ec_smt SM[EC_MAXSM];
+ /** SM type 0=unused 1=MbxWr 2=MbxRd 3=Outputs 4=Inputs */
+ uint8 SMtype[EC_MAXSM];
+ /** FMMU structure */
+ ec_fmmut FMMU[EC_MAXFMMU];
+ /** FMMU0 function */
+ uint8 FMMU0func;
+ /** FMMU1 function */
+ uint8 FMMU1func;
+ /** FMMU2 function */
+ uint8 FMMU2func;
+ /** FMMU3 function */
+ uint8 FMMU3func;
+ /** length of write mailbox in bytes, if no mailbox then 0 */
+ uint16 mbx_l;
+ /** mailbox write offset */
+ uint16 mbx_wo;
+ /** length of read mailbox in bytes */
+ uint16 mbx_rl;
+ /** mailbox read offset */
+ uint16 mbx_ro;
+ /** mailbox supported protocols */
+ uint16 mbx_proto;
+ /** Counter value of mailbox link layer protocol 1..7 */
+ uint8 mbx_cnt;
+ /** has DC capability */
+ boolean hasdc;
+ /** Physical type; Ebus, EtherNet combinations */
+ uint8 ptype;
+ /** topology: 1 to 3 links */
+ uint8 topology;
+ /** active ports bitmap : ....3210 , set if respective port is active **/
+ uint8 activeports;
+ /** consumed ports bitmap : ....3210, used for internal delay measurement **/
+ uint8 consumedports;
+ /** slave number for parent, 0=master */
+ uint16 parent;
+ /** port number on parent this slave is connected to **/
+ uint8 parentport;
+ /** port number on this slave the parent is connected to **/
+ uint8 entryport;
+ /** DC receivetimes on port A */
+ int32 DCrtA;
+ /** DC receivetimes on port B */
+ int32 DCrtB;
+ /** DC receivetimes on port C */
+ int32 DCrtC;
+ /** DC receivetimes on port D */
+ int32 DCrtD;
+ /** propagation delay */
+ int32 pdelay;
+ /** next DC slave */
+ uint16 DCnext;
+ /** previous DC slave */
+ uint16 DCprevious;
+ /** DC cycle time in ns */
+ int32 DCcycle;
+ /** DC shift from clock modulus boundary */
+ int32 DCshift;
+ /** DC sync activation, 0=off, 1=on */
+ uint8 DCactive;
+ /** link to config table */
+ uint16 configindex;
+ /** link to SII config */
+ uint16 SIIindex;
+ /** 1 = 8 bytes per read, 0 = 4 bytes per read */
+ uint8 eep_8byte;
+ /** 0 = eeprom to master , 1 = eeprom to PDI */
+ uint8 eep_pdi;
+ /** CoE details */
+ uint8 CoEdetails;
+ /** FoE details */
+ uint8 FoEdetails;
+ /** EoE details */
+ uint8 EoEdetails;
+ /** SoE details */
+ uint8 SoEdetails;
+ /** E-bus current */
+ int16 Ebuscurrent;
+ /** if >0 block use of LRW in processdata */
+ uint8 blockLRW;
+ /** group */
+ uint8 group;
+ /** first unused FMMU */
+ uint8 FMMUunused;
+ /** Boolean for tracking whether the slave is (not) responding, not used/set by the SOEM library */
+ boolean islost;
+ /** registered configuration function PO->SO */
+ int (*PO2SOconfig)(uint16 slave);
+ /** readable name */
+ char name[EC_MAXNAME + 1];
+} ec_slavet;
+
+/** for list of ethercat slave groups */
+typedef struct ec_group
+{
+ /** logical start address for this group */
+ uint32 logstartaddr;
+ /** output bytes, if Obits < 8 then Obytes = 0 */
+ uint32 Obytes;
+ /** output pointer in IOmap buffer */
+ uint8 *outputs;
+ /** input bytes, if Ibits < 8 then Ibytes = 0 */
+ uint32 Ibytes;
+ /** input pointer in IOmap buffer */
+ uint8 *inputs;
+ /** has DC capabillity */
+ boolean hasdc;
+ /** next DC slave */
+ uint16 DCnext;
+ /** E-bus current */
+ int16 Ebuscurrent;
+ /** if >0 block use of LRW in processdata */
+ uint8 blockLRW;
+ /** IO segments used */
+ uint16 nsegments;
+ /** 1st input segment */
+ uint16 Isegment;
+ /** Offset in input segment */
+ uint16 Ioffset;
+ /** Expected workcounter outputs */
+ uint16 outputsWKC;
+ /** Expected workcounter inputs */
+ uint16 inputsWKC;
+ /** check slave states */
+ boolean docheckstate;
+ /** IO segmentation list. Datagrams must not break SM in two. */
+ uint32 IOsegment[EC_MAXIOSEGMENTS];
+} ec_groupt;
+
+/** SII FMMU structure */
+typedef struct ec_eepromFMMU
+{
+ uint16 Startpos;
+ uint8 nFMMU;
+ uint8 FMMU0;
+ uint8 FMMU1;
+ uint8 FMMU2;
+ uint8 FMMU3;
+} ec_eepromFMMUt;
+
+/** SII SM structure */
+typedef struct ec_eepromSM
+{
+ uint16 Startpos;
+ uint8 nSM;
+ uint16 PhStart;
+ uint16 Plength;
+ uint8 Creg;
+ uint8 Sreg; /* don't care */
+ uint8 Activate;
+ uint8 PDIctrl; /* don't care */
+} ec_eepromSMt;
+
+/** record to store rxPDO and txPDO table from eeprom */
+typedef struct ec_eepromPDO
+{
+ uint16 Startpos;
+ uint16 Length;
+ uint16 nPDO;
+ uint16 Index[EC_MAXEEPDO];
+ uint16 SyncM[EC_MAXEEPDO];
+ uint16 BitSize[EC_MAXEEPDO];
+ uint16 SMbitsize[EC_MAXSM];
+} ec_eepromPDOt;
+
+/** mailbox buffer array */
+typedef uint8 ec_mbxbuft[EC_MAXMBX + 1];
+
+/** standard ethercat mailbox header */
+PACKED_BEGIN
+typedef struct PACKED ec_mbxheader
+{
+ uint16 length;
+ uint16 address;
+ uint8 priority;
+ uint8 mbxtype;
+} ec_mbxheadert;
+PACKED_END
+
+/** ALstatus and ALstatus code */
+PACKED_BEGIN
+typedef struct PACKED ec_alstatus
+{
+ uint16 alstatus;
+ uint16 unused;
+ uint16 alstatuscode;
+} ec_alstatust;
+PACKED_END
+
+/** stack structure to store segmented LRD/LWR/LRW constructs */
+typedef struct ec_idxstack
+{
+ uint8 pushed;
+ uint8 pulled;
+ uint8 idx[EC_MAXBUF];
+ void *data[EC_MAXBUF];
+ uint16 length[EC_MAXBUF];
+} ec_idxstackT;
+
+/** ringbuf for error storage */
+typedef struct ec_ering
+{
+ int16 head;
+ int16 tail;
+ ec_errort Error[EC_MAXELIST + 1];
+} ec_eringt;
+
+/** SyncManager Communication Type structure for CA */
+PACKED_BEGIN
+typedef struct PACKED ec_SMcommtype
+{
+ uint8 n;
+ uint8 nu1;
+ uint8 SMtype[EC_MAXSM];
+} ec_SMcommtypet;
+PACKED_END
+
+/** SDO assign structure for CA */
+PACKED_BEGIN
+typedef struct PACKED ec_PDOassign
+{
+ uint8 n;
+ uint8 nu1;
+ uint16 index[256];
+} ec_PDOassignt;
+PACKED_END
+
+/** SDO description structure for CA */
+PACKED_BEGIN
+typedef struct PACKED ec_PDOdesc
+{
+ uint8 n;
+ uint8 nu1;
+ uint32 PDO[256];
+} ec_PDOdesct;
+PACKED_END
+
+/** Context structure , referenced by all ecx functions*/
+typedef struct ecx_context ecx_contextt;
+struct ecx_context
+{
+ /** port reference, may include red_port */
+ ecx_portt *port;
+ /** slavelist reference */
+ ec_slavet *slavelist;
+ /** number of slaves found in configuration */
+ int *slavecount;
+ /** maximum number of slaves allowed in slavelist */
+ int maxslave;
+ /** grouplist reference */
+ ec_groupt *grouplist;
+ /** maximum number of groups allowed in grouplist */
+ int maxgroup;
+ /** internal, reference to eeprom cache buffer */
+ uint8 *esibuf;
+ /** internal, reference to eeprom cache map */
+ uint32 *esimap;
+ /** internal, current slave for eeprom cache */
+ uint16 esislave;
+ /** internal, reference to error list */
+ ec_eringt *elist;
+ /** internal, reference to processdata stack buffer info */
+ ec_idxstackT *idxstack;
+ /** reference to ecaterror state */
+ boolean *ecaterror;
+ /** internal, position of DC datagram in process data packet */
+ uint16 DCtO;
+ /** internal, length of DC datagram */
+ uint16 DCl;
+ /** reference to last DC time from slaves */
+ int64 *DCtime;
+ /** internal, SM buffer */
+ ec_SMcommtypet *SMcommtype;
+ /** internal, PDO assign list */
+ ec_PDOassignt *PDOassign;
+ /** internal, PDO description list */
+ ec_PDOdesct *PDOdesc;
+ /** internal, SM list from eeprom */
+ ec_eepromSMt *eepSM;
+ /** internal, FMMU list from eeprom */
+ ec_eepromFMMUt *eepFMMU;
+ /** registered FoE hook */
+ int (*FOEhook)(uint16 slave, int packetnumber, int datasize);
+ /** registered EoE hook */
+ int (*EOEhook)(ecx_contextt * context, uint16 slave, void * eoembx);
+};
+
+#ifdef EC_VER1
+/** global struct to hold default master context */
+extern ecx_contextt ecx_context;
+/** main slave data structure array */
+extern ec_slavet ec_slave[EC_MAXSLAVE];
+/** number of slaves found by configuration function */
+extern int ec_slavecount;
+/** slave group structure */
+extern ec_groupt ec_group[EC_MAXGROUP];
+extern boolean EcatError;
+extern int64 ec_DCtime;
+
+void ec_pusherror(const ec_errort *Ec);
+boolean ec_poperror(ec_errort *Ec);
+boolean ec_iserror(void);
+void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode);
+int ec_init(const char * ifname);
+int ec_init_redundant(const char *ifname, char *if2name);
+void ec_close(void);
+uint8 ec_siigetbyte(uint16 slave, uint16 address);
+int16 ec_siifind(uint16 slave, uint16 cat);
+void ec_siistring(char *str, uint16 slave, uint16 Sn);
+uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU);
+uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM);
+uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n);
+int ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t);
+int ec_readstate(void);
+int ec_writestate(uint16 slave);
+uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout);
+int ec_mbxempty(uint16 slave, int timeout);
+int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout);
+int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout);
+void ec_esidump(uint16 slave, uint8 *esibuf);
+uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout);
+int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout);
+int ec_eeprom2master(uint16 slave);
+int ec_eeprom2pdi(uint16 slave);
+uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout);
+int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout);
+uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout);
+int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout);
+void ec_readeeprom1(uint16 slave, uint16 eeproma);
+uint32 ec_readeeprom2(uint16 slave, int timeout);
+int ec_send_processdata_group(uint8 group);
+int ec_send_overlap_processdata_group(uint8 group);
+int ec_receive_processdata_group(uint8 group, int timeout);
+int ec_send_processdata(void);
+int ec_send_overlap_processdata(void);
+int ec_receive_processdata(int timeout);
+#endif
+
+ec_adaptert * ec_find_adapters(void);
+void ec_free_adapters(ec_adaptert * adapter);
+uint8 ec_nextmbxcnt(uint8 cnt);
+void ec_clearmbx(ec_mbxbuft *Mbx);
+void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec);
+boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec);
+boolean ecx_iserror(ecx_contextt *context);
+void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode);
+int ecx_init(ecx_contextt *context, const char * ifname);
+int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name);
+void ecx_close(ecx_contextt *context);
+uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address);
+int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat);
+void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn);
+uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU);
+uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM);
+uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n);
+int ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t);
+int ecx_readstate(ecx_contextt *context);
+int ecx_writestate(ecx_contextt *context, uint16 slave);
+uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout);
+int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout);
+int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout);
+int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout);
+void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf);
+uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout);
+int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout);
+int ecx_eeprom2master(ecx_contextt *context, uint16 slave);
+int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave);
+uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout);
+int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout);
+uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout);
+int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout);
+void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma);
+uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout);
+int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group);
+int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout);
+int ecx_send_processdata(ecx_contextt *context);
+int ecx_send_overlap_processdata(ecx_contextt *context);
+int ecx_receive_processdata(ecx_contextt *context, int timeout);
+int ecx_send_processdata_group(ecx_contextt *context, uint8 group);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatprint.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,365 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Module to convert EtherCAT errors to readable messages.
+ *
+ * SDO abort messages and AL status codes are used to relay slave errors to
+ * the user application. This module converts the binary codes to readable text.
+ * For the defined error codes see the EtherCAT protocol documentation.
+ */
+
+#include <stdio.h>
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatmain.h"
+
+#define EC_MAXERRORNAME 127
+
+/** SDO error list type definition */
+typedef struct
+{
+ /** Error code returned from SDO */
+ uint32 errorcode;
+ /** Readable error description */
+ char errordescription[EC_MAXERRORNAME + 1];
+} ec_sdoerrorlist_t;
+
+/** AL status code list type definition */
+typedef struct
+{
+ /** AL status code */
+ uint16 ALstatuscode;
+ /** Readable description */
+ char ALstatuscodedescription[EC_MAXERRORNAME + 1];
+} ec_ALstatuscodelist_t;
+
+/** SoE error list type definition */
+typedef struct
+{
+ /** SoE error code */
+ uint16 errorcode;
+ /** Readable description */
+ char errordescription[EC_MAXERRORNAME + 1];
+} ec_soeerrorlist_t;
+
+/** MBX error list type definition */
+typedef struct
+{
+ /** MBX error code */
+ uint16 errorcode;
+ /** Readable description */
+ char errordescription[EC_MAXERRORNAME + 1];
+} ec_mbxerrorlist_t;
+
+char estring[EC_MAXERRORNAME];
+
+/** SDO error list definition */
+const ec_sdoerrorlist_t ec_sdoerrorlist[] = {
+ {0x00000000, "No error" },
+ {0x05030000, "Toggle bit not changed" },
+ {0x05040000, "SDO protocol timeout" },
+ {0x05040001, "Client/Server command specifier not valid or unknown" },
+ {0x05040005, "Out of memory" },
+ {0x06010000, "Unsupported access to an object" },
+ {0x06010001, "Attempt to read to a write only object" },
+ {0x06010002, "Attempt to write to a read only object" },
+ {0x06010003, "Subindex can not be written, SI0 must be 0 for write access" },
+ {0x06010004, "SDO Complete access not supported for variable length objects" },
+ {0x06010005, "Object length exceeds mailbox size" },
+ {0x06010006, "Object mapped to RxPDO, SDO download blocked" },
+ {0x06020000, "The object does not exist in the object directory" },
+ {0x06040041, "The object can not be mapped into the PDO" },
+ {0x06040042, "The number and length of the objects to be mapped would exceed the PDO length" },
+ {0x06040043, "General parameter incompatibility reason" },
+ {0x06040047, "General internal incompatibility in the device" },
+ {0x06060000, "Access failed due to a hardware error" },
+ {0x06070010, "Data type does not match, length of service parameter does not match" },
+ {0x06070012, "Data type does not match, length of service parameter too high" },
+ {0x06070013, "Data type does not match, length of service parameter too low" },
+ {0x06090011, "Subindex does not exist" },
+ {0x06090030, "Value range of parameter exceeded (only for write access)" },
+ {0x06090031, "Value of parameter written too high" },
+ {0x06090032, "Value of parameter written too low" },
+ {0x06090036, "Maximum value is less than minimum value" },
+ {0x08000000, "General error" },
+ {0x08000020, "Data cannot be transferred or stored to the application" },
+ {0x08000021, "Data cannot be transferred or stored to the application because of local control" },
+ {0x08000022, "Data cannot be transferred or stored to the application because of the present device state" },
+ {0x08000023, "Object dictionary dynamic generation fails or no object dictionary is present" },
+ {0xffffffff, "Unknown" }
+};
+
+/** AL status code list definition */
+const ec_ALstatuscodelist_t ec_ALstatuscodelist[] = {
+ {0x0000 , "No error" },
+ {0x0001 , "Unspecified error" },
+ {0x0002 , "No memory" },
+ {0x0011 , "Invalid requested state change" },
+ {0x0012 , "Unknown requested state" },
+ {0x0013 , "Bootstrap not supported" },
+ {0x0014 , "No valid firmware" },
+ {0x0015 , "Invalid mailbox configuration" },
+ {0x0016 , "Invalid mailbox configuration" },
+ {0x0017 , "Invalid sync manager configuration" },
+ {0x0018 , "No valid inputs available" },
+ {0x0019 , "No valid outputs" },
+ {0x001A , "Synchronization error" },
+ {0x001B , "Sync manager watchdog" },
+ {0x001C , "Invalid sync Manager types" },
+ {0x001D , "Invalid output configuration" },
+ {0x001E , "Invalid input configuration" },
+ {0x001F , "Invalid watchdog configuration" },
+ {0x0020 , "Slave needs cold start" },
+ {0x0021 , "Slave needs INIT" },
+ {0x0022 , "Slave needs PREOP" },
+ {0x0023 , "Slave needs SAFEOP" },
+ {0x0024 , "Invalid input mapping" },
+ {0x0025 , "Invalid output mapping" },
+ {0x0026 , "Inconsistent settings" },
+ {0x0027 , "Freerun not supported" },
+ {0x0028 , "Synchronisation not supported" },
+ {0x0029 , "Freerun needs 3buffer mode" },
+ {0x002A , "Background watchdog" },
+ {0x002B , "No valid Inputs and Outputs" },
+ {0x002C , "Fatal sync error" },
+ {0x002D , "No sync error" }, // was "Invalid Output FMMU Configuration"
+ {0x002E , "Invalid input FMMU configuration" },
+ {0x0030 , "Invalid DC SYNC configuration" },
+ {0x0031 , "Invalid DC latch configuration" },
+ {0x0032 , "PLL error" },
+ {0x0033 , "DC sync IO error" },
+ {0x0034 , "DC sync timeout error" },
+ {0x0035 , "DC invalid sync cycle time" },
+ {0x0036 , "DC invalid sync0 cycle time" },
+ {0x0037 , "DC invalid sync1 cycle time" },
+ {0x0041 , "MBX_AOE" },
+ {0x0042 , "MBX_EOE" },
+ {0x0043 , "MBX_COE" },
+ {0x0044 , "MBX_FOE" },
+ {0x0045 , "MBX_SOE" },
+ {0x004F , "MBX_VOE" },
+ {0x0050 , "EEPROM no access" },
+ {0x0051 , "EEPROM error" },
+ {0x0060 , "Slave restarted locally" },
+ {0x0061 , "Device identification value updated" },
+ {0x00f0 , "Application controller available" },
+ {0xffff , "Unknown" }
+};
+
+/** SoE error list definition */
+const ec_soeerrorlist_t ec_soeerrorlist[] = {
+ {0x0000, "No error" },
+ {0x1001, "No IDN" },
+ {0x1009, "Invalid access to element 1" },
+ {0x2001, "No Name" },
+ {0x2002, "Name transmission too short" },
+ {0x2003, "Name transmission too long" },
+ {0x2004, "Name cannot be changed (read only)" },
+ {0x2005, "Name is write-protected at this time" },
+ {0x3002, "Attribute transmission too short" },
+ {0x3003, "Attribute transmission too long" },
+ {0x3004, "Attribute cannot be changed (read only)" },
+ {0x3005, "Attribute is write-protected at this time" },
+ {0x4001, "No units" },
+ {0x4002, "Unit transmission too short" },
+ {0x4003, "Unit transmission too long" },
+ {0x4004, "Unit cannot be changed (read only)" },
+ {0x4005, "Unit is write-protected at this time" },
+ {0x5001, "No minimum input value" },
+ {0x5002, "Minimum input value transmission too short" },
+ {0x5003, "Minimum input value transmission too long" },
+ {0x5004, "Minimum input value cannot be changed (read only)" },
+ {0x5005, "Minimum input value is write-protected at this time" },
+ {0x6001, "No maximum input value" },
+ {0x6002, "Maximum input value transmission too short" },
+ {0x6003, "Maximum input value transmission too long" },
+ {0x6004, "Maximum input value cannot be changed (read only)" },
+ {0x6005, "Maximum input value is write-protected at this time" },
+ {0x7002, "Operation data transmission too short" },
+ {0x7003, "Operation data transmission too long" },
+ {0x7004, "Operation data cannot be changed (read only)" },
+ {0x7005, "Operation data is write-protected at this time (state)" },
+ {0x7006, "Operation data is smaller than the minimum input value" },
+ {0x7007, "Operation data is smaller than the maximum input value" },
+ {0x7008, "Invalid operation data:Configured IDN will not be supported" },
+ {0x7009, "Operation data write protected by a password" },
+ {0x700A, "Operation data is write protected, it is configured cyclically" },
+ {0x700B, "Invalid indirect addressing: (e.g., data container, list handling)" },
+ {0x700C, "Operation data is write protected, due to other settings" },
+ {0x700D, "Reserved" },
+ {0x7010, "Procedure command already active" },
+ {0x7011, "Procedure command not interruptible" },
+ {0x7012, "Procedure command at this time not executable (state)" },
+ {0x7013, "Procedure command not executable (invalid or false parameters)" },
+ {0x7014, "No data state" },
+ {0x8001, "No default value" },
+ {0x8002, "Default value transmission too long" },
+ {0x8004, "Default value cannot be changed, read only" },
+ {0x800A, "Invalid drive number" },
+ {0x800A, "General error" },
+ {0x800A, "No element addressed" },
+ {0xffff, "Unknown" }
+};
+
+/** MBX error list definition */
+const ec_mbxerrorlist_t ec_mbxerrorlist[] = {
+ {0x0000, "No error" },
+ {0x0001, "Syntax of 6 octet Mailbox Header is wrong" },
+ {0x0002, "The mailbox protocol is not supported" },
+ {0x0003, "Channel Field contains wrong value"},
+ {0x0004, "The service is no supported"},
+ {0x0005, "Invalid mailbox header"},
+ {0x0006, "Length of received mailbox data is too short"},
+ {0x0007, "No more memory in slave"},
+ {0x0008, "The length of data is inconsistent"},
+ {0xffff, "Unknown"}
+};
+
+/** Look up text string that belongs to SDO error code.
+ *
+ * @param[in] sdoerrorcode = SDO error code as defined in EtherCAT protocol
+ * @return readable string
+ */
+const char* ec_sdoerror2string( uint32 sdoerrorcode)
+{
+ int i = 0;
+
+ while ( (ec_sdoerrorlist[i].errorcode != 0xffffffffUL) &&
+ (ec_sdoerrorlist[i].errorcode != sdoerrorcode) )
+ {
+ i++;
+ }
+
+ return ec_sdoerrorlist[i].errordescription;
+}
+
+/** Look up text string that belongs to AL status code.
+ *
+ * @param[in] ALstatuscode = AL status code as defined in EtherCAT protocol
+ * @return readable string
+ */
+char* ec_ALstatuscode2string( uint16 ALstatuscode)
+{
+ int i = 0;
+
+ while ( (ec_ALstatuscodelist[i].ALstatuscode != 0xffff) &&
+ (ec_ALstatuscodelist[i].ALstatuscode != ALstatuscode) )
+ {
+ i++;
+ }
+
+ return (char *) ec_ALstatuscodelist[i].ALstatuscodedescription;
+}
+
+/** Look up text string that belongs to SoE error code.
+ *
+ * @param[in] errorcode = SoE error code as defined in EtherCAT protocol
+ * @return readable string
+ */
+char* ec_soeerror2string( uint16 errorcode)
+{
+ int i = 0;
+
+ while ( (ec_soeerrorlist[i].errorcode != 0xffff) &&
+ (ec_soeerrorlist[i].errorcode != errorcode) )
+ {
+ i++;
+ }
+
+ return (char *) ec_soeerrorlist[i].errordescription;
+}
+
+/** Look up text string that belongs to MBX error code.
+ *
+ * @param[in] errorcode = MBX error code as defined in EtherCAT protocol
+ * @return readable string
+ */
+char* ec_mbxerror2string( uint16 errorcode)
+{
+ int i = 0;
+
+ while ( (ec_mbxerrorlist[i].errorcode != 0xffff) &&
+ (ec_mbxerrorlist[i].errorcode != errorcode) )
+ {
+ i++;
+ }
+
+ return (char *) ec_mbxerrorlist[i].errordescription;
+}
+
+/** Look up error in ec_errorlist and convert to text string.
+ *
+ * @param[in] context = context struct
+ * @return readable string
+ */
+char* ecx_elist2string(ecx_contextt *context)
+{
+ ec_errort Ec;
+ char timestr[20];
+
+ if (ecx_poperror(context, &Ec))
+ {
+ sprintf(timestr, "Time:%12.3f", Ec.Time.sec + (Ec.Time.usec / 1000000.0) );
+ switch (Ec.Etype)
+ {
+ case EC_ERR_TYPE_SDO_ERROR:
+ {
+ sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n",
+ timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode));
+ break;
+ }
+ case EC_ERR_TYPE_EMERGENCY:
+ {
+ sprintf(estring, "%s EMERGENCY slave:%d error:%4.4x\n",
+ timestr, Ec.Slave, Ec.ErrorCode);
+ break;
+ }
+ case EC_ERR_TYPE_PACKET_ERROR:
+ {
+ sprintf(estring, "%s PACKET slave:%d index:%4.4x.%2.2x error:%d\n",
+ timestr, Ec.Slave, Ec.Index, Ec.SubIdx, Ec.ErrorCode);
+ break;
+ }
+ case EC_ERR_TYPE_SDOINFO_ERROR:
+ {
+ sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n",
+ timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode));
+ break;
+ }
+ case EC_ERR_TYPE_SOE_ERROR:
+ {
+ sprintf(estring, "%s SoE slave:%d IDN:%4.4x error:%4.4x %s\n",
+ timestr, Ec.Slave, Ec.Index, (unsigned)Ec.AbortCode, ec_soeerror2string(Ec.ErrorCode));
+ break;
+ }
+ case EC_ERR_TYPE_MBX_ERROR:
+ {
+ sprintf(estring, "%s MBX slave:%d error:%4.4x %s\n",
+ timestr, Ec.Slave, Ec.ErrorCode, ec_mbxerror2string(Ec.ErrorCode));
+ break;
+ }
+ default:
+ {
+ sprintf(estring, "%s error:%8.8x\n",
+ timestr, (unsigned)Ec.AbortCode);
+ break;
+ }
+ }
+ return (char*) estring;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+#ifdef EC_VER1
+char* ec_elist2string(void)
+{
+ return ecx_elist2string(&ecx_context);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatprint.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,32 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatprint.c
+ */
+
+#ifndef _ethercatprint_
+#define _ethercatprint_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+char* ec_sdoerror2string( uint32 sdoerrorcode);
+char* ec_ALstatuscode2string( uint16 ALstatuscode);
+char* ec_soeerror2string( uint16 errorcode);
+char* ecx_elist2string(ecx_contextt *context);
+
+#ifdef EC_VER1
+char* ec_elist2string(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatsoe.c Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,389 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Servo over EtherCAT (SoE) Module.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "osal.h"
+#include "oshw.h"
+#include "ethercattype.h"
+#include "ethercatbase.h"
+#include "ethercatmain.h"
+#include "ethercatsoe.h"
+
+#define EC_SOE_MAX_DRIVES 8
+
+/** SoE (Servo over EtherCAT) mailbox structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ ec_mbxheadert MbxHeader;
+ uint8 opCode :3;
+ uint8 incomplete :1;
+ uint8 error :1;
+ uint8 driveNo :3;
+ uint8 elementflags;
+ union
+ {
+ uint16 idn;
+ uint16 fragmentsleft;
+ };
+} ec_SoEt;
+PACKED_END
+
+/** Report SoE error.
+ *
+ * @param[in] context = context struct
+ * @param[in] Slave = Slave number
+ * @param[in] idn = IDN that generated error
+ * @param[in] Error = Error code, see EtherCAT documentation for list
+ */
+void ecx_SoEerror(ecx_contextt *context, uint16 Slave, uint16 idn, uint16 Error)
+{
+ ec_errort Ec;
+
+ memset(&Ec, 0, sizeof(Ec));
+ Ec.Time = osal_current_time();
+ Ec.Slave = Slave;
+ Ec.Index = idn;
+ Ec.SubIdx = 0;
+ *(context->ecaterror) = TRUE;
+ Ec.Etype = EC_ERR_TYPE_SOE_ERROR;
+ Ec.ErrorCode = Error;
+ ecx_pusherror(context, &Ec);
+}
+
+/** SoE read, blocking.
+ *
+ * The IDN object of the selected slave and DriveNo is read. If a response
+ * is larger than the mailbox size then the response is segmented. The function
+ * will combine all segments and copy them to the parameter buffer.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] driveNo = Drive number in slave
+ * @param[in] elementflags = Flags to select what properties of IDN are to be transferred.
+ * @param[in] idn = IDN.
+ * @param[in,out] psize = Size in bytes of parameter buffer, returns bytes read from SoE.
+ * @param[out] p = Pointer to parameter buffer
+ * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
+{
+ ec_SoEt *SoEp, *aSoEp;
+ uint16 totalsize, framedatasize;
+ int wkc;
+ uint8 *bp;
+ uint8 *mp;
+ uint16 *errorcode;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+ boolean NotLast;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSoEp = (ec_SoEt *)&MbxIn;
+ SoEp = (ec_SoEt *)&MbxOut;
+ SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert));
+ SoEp->MbxHeader.address = htoes(0x0000);
+ SoEp->MbxHeader.priority = 0x00;
+ /* get new mailbox count value, used as session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
+ SoEp->opCode = ECT_SOE_READREQ;
+ SoEp->incomplete = 0;
+ SoEp->error = 0;
+ SoEp->driveNo = driveNo;
+ SoEp->elementflags = elementflags;
+ SoEp->idn = htoes(idn);
+ totalsize = 0;
+ bp = p;
+ mp = (uint8 *)&MbxIn + sizeof(ec_SoEt);
+ NotLast = TRUE;
+ /* send SoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ while (NotLast)
+ {
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ /* slave response should be SoE, ReadRes */
+ if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+ (aSoEp->opCode == ECT_SOE_READRES) &&
+ (aSoEp->error == 0) &&
+ (aSoEp->driveNo == driveNo) &&
+ (aSoEp->elementflags == elementflags))
+ {
+ framedatasize = etohs(aSoEp->MbxHeader.length) - sizeof(ec_SoEt) + sizeof(ec_mbxheadert);
+ totalsize += framedatasize;
+ /* Does parameter fit in parameter buffer ? */
+ if (totalsize <= *psize)
+ {
+ /* copy parameter data in parameter buffer */
+ memcpy(bp, mp, framedatasize);
+ /* increment buffer pointer */
+ bp += framedatasize;
+ }
+ else
+ {
+ framedatasize -= totalsize - *psize;
+ totalsize = *psize;
+ /* copy parameter data in parameter buffer */
+ if (framedatasize > 0) memcpy(bp, mp, framedatasize);
+ }
+
+ if (!aSoEp->incomplete)
+ {
+ NotLast = FALSE;
+ *psize = totalsize;
+ }
+ }
+ /* other slave response */
+ else
+ {
+ NotLast = FALSE;
+ if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+ (aSoEp->opCode == ECT_SOE_READRES) &&
+ (aSoEp->error == 1))
+ {
+ mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
+ errorcode = (uint16 *)mp;
+ ecx_SoEerror(context, slave, idn, *errorcode);
+ }
+ else
+ {
+ ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ else
+ {
+ NotLast = FALSE;
+ ecx_packeterror(context, slave, idn, 0, 4); /* no response */
+ }
+ }
+ }
+ return wkc;
+}
+
+/** SoE write, blocking.
+ *
+ * The IDN object of the selected slave and DriveNo is written. If a response
+ * is larger than the mailbox size then the response is segmented.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[in] driveNo = Drive number in slave
+ * @param[in] elementflags = Flags to select what properties of IDN are to be transferred.
+ * @param[in] idn = IDN.
+ * @param[in] psize = Size in bytes of parameter buffer.
+ * @param[out] p = Pointer to parameter buffer
+ * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM
+ * @return Workcounter from last slave response
+ */
+int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
+{
+ ec_SoEt *SoEp, *aSoEp;
+ uint16 framedatasize, maxdata;
+ int wkc;
+ uint8 *mp;
+ uint8 *hp;
+ uint16 *errorcode;
+ ec_mbxbuft MbxIn, MbxOut;
+ uint8 cnt;
+ boolean NotLast;
+
+ ec_clearmbx(&MbxIn);
+ /* Empty slave out mailbox if something is in. Timeout set to 0 */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
+ ec_clearmbx(&MbxOut);
+ aSoEp = (ec_SoEt *)&MbxIn;
+ SoEp = (ec_SoEt *)&MbxOut;
+ SoEp->MbxHeader.address = htoes(0x0000);
+ SoEp->MbxHeader.priority = 0x00;
+ SoEp->opCode = ECT_SOE_WRITEREQ;
+ SoEp->error = 0;
+ SoEp->driveNo = driveNo;
+ SoEp->elementflags = elementflags;
+ hp = p;
+ mp = (uint8 *)&MbxOut + sizeof(ec_SoEt);
+ maxdata = context->slavelist[slave].mbx_l - sizeof(ec_SoEt);
+ NotLast = TRUE;
+ while (NotLast)
+ {
+ framedatasize = psize;
+ NotLast = FALSE;
+ SoEp->idn = htoes(idn);
+ SoEp->incomplete = 0;
+ if (framedatasize > maxdata)
+ {
+ framedatasize = maxdata; /* segmented transfer needed */
+ NotLast = TRUE;
+ SoEp->incomplete = 1;
+ SoEp->fragmentsleft = psize / maxdata;
+ }
+ SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert) + framedatasize);
+ /* get new mailbox counter, used for session handle */
+ cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
+ context->slavelist[slave].mbx_cnt = cnt;
+ SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */
+ /* copy parameter data to mailbox */
+ memcpy(mp, hp, framedatasize);
+ hp += framedatasize;
+ psize -= framedatasize;
+ /* send SoE request to slave */
+ wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
+ if (wkc > 0) /* succeeded to place mailbox in slave ? */
+ {
+ if (!NotLast || !ecx_mbxempty(context, slave, timeout))
+ {
+ /* clean mailboxbuffer */
+ ec_clearmbx(&MbxIn);
+ /* read slave response */
+ wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
+ if (wkc > 0) /* succeeded to read slave response ? */
+ {
+ NotLast = FALSE;
+ /* slave response should be SoE, WriteRes */
+ if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+ (aSoEp->opCode == ECT_SOE_WRITERES) &&
+ (aSoEp->error == 0) &&
+ (aSoEp->driveNo == driveNo) &&
+ (aSoEp->elementflags == elementflags))
+ {
+ /* SoE write succeeded */
+ }
+ /* other slave response */
+ else
+ {
+ if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) &&
+ (aSoEp->opCode == ECT_SOE_READRES) &&
+ (aSoEp->error == 1))
+ {
+ mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16));
+ errorcode = (uint16 *)mp;
+ ecx_SoEerror(context, slave, idn, *errorcode);
+ }
+ else
+ {
+ ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */
+ }
+ wkc = 0;
+ }
+ }
+ else
+ {
+ ecx_packeterror(context, slave, idn, 0, 4); /* no response */
+ }
+ }
+ }
+ }
+ return wkc;
+}
+
+/** SoE read AT and MTD mapping.
+ *
+ * SoE has standard indexes defined for mapping. This function
+ * tries to read them and collect a full input and output mapping size
+ * of designated slave.
+ *
+ * @param[in] context = context struct
+ * @param[in] slave = Slave number
+ * @param[out] Osize = Size in bits of output mapping (MTD) found
+ * @param[out] Isize = Size in bits of input mapping (AT) found
+ * @return >0 if mapping successful.
+ */
+int ecx_readIDNmap(ecx_contextt *context, uint16 slave, int *Osize, int *Isize)
+{
+ int retVal = 0;
+ int wkc;
+ int psize;
+ int driveNr;
+ uint16 entries, itemcount;
+ ec_SoEmappingt SoEmapping;
+ ec_SoEattributet SoEattribute;
+
+ *Isize = 0;
+ *Osize = 0;
+ for(driveNr = 0; driveNr < EC_SOE_MAX_DRIVES; driveNr++)
+ {
+ psize = sizeof(SoEmapping);
+ /* read output mapping via SoE */
+ wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_MDTCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
+ if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
+ {
+ /* command word (uint16) is always mapped but not in list */
+ *Osize = 16;
+ for (itemcount = 0 ; itemcount < entries ; itemcount++)
+ {
+ psize = sizeof(SoEattribute);
+ /* read attribute of each IDN in mapping list */
+ wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
+ if ((wkc > 0) && (!SoEattribute.list))
+ {
+ /* length : 0 = 8bit, 1 = 16bit .... */
+ *Osize += (int)8 << SoEattribute.length;
+ }
+ }
+ }
+ psize = sizeof(SoEmapping);
+ /* read input mapping via SoE */
+ wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_ATCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM);
+ if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING))
+ {
+ /* status word (uint16) is always mapped but not in list */
+ *Isize = 16;
+ for (itemcount = 0 ; itemcount < entries ; itemcount++)
+ {
+ psize = sizeof(SoEattribute);
+ /* read attribute of each IDN in mapping list */
+ wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM);
+ if ((wkc > 0) && (!SoEattribute.list))
+ {
+ /* length : 0 = 8bit, 1 = 16bit .... */
+ *Isize += (int)8 << SoEattribute.length;
+ }
+ }
+ }
+ }
+
+ /* found some I/O bits ? */
+ if ((*Isize > 0) || (*Osize > 0))
+ {
+ retVal = 1;
+ }
+ return retVal;
+}
+
+#ifdef EC_VER1
+int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout)
+{
+ return ecx_SoEread(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout);
+}
+
+int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout)
+{
+ return ecx_SoEwrite(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout);
+}
+
+int ec_readIDNmap(uint16 slave, int *Osize, int *Isize)
+{
+ return ecx_readIDNmap(&ecx_context, slave, Osize, Isize);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercatsoe.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,130 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for ethercatsoe.c
+ */
+
+#ifndef _ethercatsoe_
+#define _ethercatsoe_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define EC_SOE_DATASTATE_B 0x01
+#define EC_SOE_NAME_B 0x02
+#define EC_SOE_ATTRIBUTE_B 0x04
+#define EC_SOE_UNIT_B 0x08
+#define EC_SOE_MIN_B 0x10
+#define EC_SOE_MAX_B 0x20
+#define EC_SOE_VALUE_B 0x40
+#define EC_SOE_DEFAULT_B 0x80
+
+#define EC_SOE_MAXNAME 60
+#define EC_SOE_MAXMAPPING 64
+
+#define EC_IDN_MDTCONFIG 24
+#define EC_IDN_ATCONFIG 16
+
+/** SoE name structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ /** current length in bytes of list */
+ uint16 currentlength;
+ /** maximum length in bytes of list */
+ uint16 maxlength;
+ char name[EC_SOE_MAXNAME];
+} ec_SoEnamet;
+PACKED_END
+
+/** SoE list structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ /** current length in bytes of list */
+ uint16 currentlength;
+ /** maximum length in bytes of list */
+ uint16 maxlength;
+ union
+ {
+ uint8 byte[8];
+ uint16 word[4];
+ uint32 dword[2];
+ uint64 lword[1];
+ };
+} ec_SoElistt;
+PACKED_END
+
+/** SoE IDN mapping structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ /** current length in bytes of list */
+ uint16 currentlength;
+ /** maximum length in bytes of list */
+ uint16 maxlength;
+ uint16 idn[EC_SOE_MAXMAPPING];
+} ec_SoEmappingt;
+PACKED_END
+
+#define EC_SOE_LENGTH_1 0x00
+#define EC_SOE_LENGTH_2 0x01
+#define EC_SOE_LENGTH_4 0x02
+#define EC_SOE_LENGTH_8 0x03
+#define EC_SOE_TYPE_BINARY 0x00
+#define EC_SOE_TYPE_UINT 0x01
+#define EC_SOE_TYPE_INT 0x02
+#define EC_SOE_TYPE_HEX 0x03
+#define EC_SOE_TYPE_STRING 0x04
+#define EC_SOE_TYPE_IDN 0x05
+#define EC_SOE_TYPE_FLOAT 0x06
+#define EC_SOE_TYPE_PARAMETER 0x07
+
+/** SoE attribute structure */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ /** evaluation factor for display purposes */
+ uint32 evafactor :16;
+ /** length of IDN element(s) */
+ uint32 length :2;
+ /** IDN is list */
+ uint32 list :1;
+ /** IDN is command */
+ uint32 command :1;
+ /** datatype */
+ uint32 datatype :3;
+ uint32 reserved1 :1;
+ /** decimals to display if float datatype */
+ uint32 decimals :4;
+ /** write protected in pre-op */
+ uint32 wppreop :1;
+ /** write protected in safe-op */
+ uint32 wpsafeop :1;
+ /** write protected in op */
+ uint32 wpop :1;
+ uint32 reserved2 :1;
+} ec_SoEattributet;
+PACKED_END
+
+#ifdef EC_VER1
+int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout);
+int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout);
+int ec_readIDNmap(uint16 slave, int *Osize, int *Isize);
+#endif
+
+int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout);
+int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout);
+int ecx_readIDNmap(ecx_contextt *context, uint16 slave, int *Osize, int *Isize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/SOEM/ethercattype.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,565 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * General typedefs and defines for EtherCAT.
+ *
+ * Defines that could need optimisation for specific applications
+ * are the EC_TIMEOUTxxx. Assumptions for the standard settings are a
+ * standard linux PC or laptop and a wired connection to maximal 100 slaves.
+ * For use with wireless connections or lots of slaves the timeouts need
+ * increasing. For fast systems running Xenomai and RT-net or alike the
+ * timeouts need to be shorter.
+ */
+
+#ifndef _EC_TYPE_H
+#define _EC_TYPE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Define Little or Big endian target */
+#define EC_LITTLE_ENDIAN
+
+/** define EC_VER1 if version 1 default context and functions are needed
+ * comment if application uses only ecx_ functions and own context */
+#define EC_VER1
+
+#include "osal.h"
+
+/** return value general error */
+#define EC_ERROR -3
+/** return value no frame returned */
+#define EC_NOFRAME -1
+/** return value unknown frame received */
+#define EC_OTHERFRAME -2
+/** maximum EtherCAT frame length in bytes */
+#define EC_MAXECATFRAME 1518
+/** maximum EtherCAT LRW frame length in bytes */
+/* MTU - Ethernet header - length - datagram header - WCK - FCS */
+#define EC_MAXLRWDATA (EC_MAXECATFRAME - 14 - 2 - 10 - 2 - 4)
+/** size of DC datagram used in first LRW frame */
+#define EC_FIRSTDCDATAGRAM 20
+/** standard frame buffer size in bytes */
+#define EC_BUFSIZE EC_MAXECATFRAME
+/** datagram type EtherCAT */
+#define EC_ECATTYPE 0x1000
+/** number of frame buffers per channel (tx, rx1 rx2) */
+#define EC_MAXBUF 16
+/** timeout value in us for tx frame to return to rx */
+//#define EC_TIMEOUTRET 2000
+
+#define EC_TIMEOUTRET 500 //Changed: must be less than the CYCLE_TIME
+
+/** timeout value in us for safe data transfer, max. triple retry */
+#define EC_TIMEOUTRET3 (EC_TIMEOUTRET * 3)
+/** timeout value in us for return "safe" variant (f.e. wireless) */
+#define EC_TIMEOUTSAFE 20000
+/** timeout value in us for EEPROM access */
+#define EC_TIMEOUTEEP 20000
+/** timeout value in us for tx mailbox cycle */
+#define EC_TIMEOUTTXM 20000
+/** timeout value in us for rx mailbox cycle */
+#define EC_TIMEOUTRXM 700000
+/** timeout value in us for check statechange */
+#define EC_TIMEOUTSTATE 2000000
+/** size of EEPROM bitmap cache */
+#define EC_MAXEEPBITMAP 128
+/** size of EEPROM cache buffer */
+#define EC_MAXEEPBUF EC_MAXEEPBITMAP << 5
+/** default number of retries if wkc <= 0 */
+#define EC_DEFAULTRETRIES 3
+/** default group size in 2^x */
+#define EC_LOGGROUPOFFSET 16
+
+/** definition for frame buffers */
+typedef uint8 ec_bufT[EC_BUFSIZE];
+
+/** ethernet header definition */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ /** destination MAC */
+ uint16 da0,da1,da2;
+ /** source MAC */
+ uint16 sa0,sa1,sa2;
+ /** ethernet type */
+ uint16 etype;
+} ec_etherheadert;
+PACKED_END
+
+/** ethernet header size */
+#define ETH_HEADERSIZE sizeof(ec_etherheadert)
+
+/** EtherCAT datagram header definition */
+PACKED_BEGIN
+typedef struct PACKED
+{
+ /** length of EtherCAT datagram */
+ uint16 elength;
+ /** EtherCAT command, see ec_cmdtype */
+ uint8 command;
+ /** index, used in SOEM for Tx to Rx recombination */
+ uint8 index;
+ /** ADP */
+ uint16 ADP;
+ /** ADO */
+ uint16 ADO;
+ /** length of data portion in datagram */
+ uint16 dlength;
+ /** interrupt, currently unused */
+ uint16 irpt;
+} ec_comt;
+PACKED_END
+
+/** EtherCAT header size */
+#define EC_HEADERSIZE sizeof(ec_comt)
+/** size of ec_comt.elength item in EtherCAT header */
+#define EC_ELENGTHSIZE sizeof(uint16)
+/** offset position of command in EtherCAT header */
+#define EC_CMDOFFSET EC_ELENGTHSIZE
+/** size of workcounter item in EtherCAT datagram */
+#define EC_WKCSIZE sizeof(uint16)
+/** definition of datagram follows bit in ec_comt.dlength */
+#define EC_DATAGRAMFOLLOWS (1 << 15)
+
+/** Possible error codes returned. */
+typedef enum
+{
+ /** No error */
+ EC_ERR_OK = 0,
+ /** Library already initialized. */
+ EC_ERR_ALREADY_INITIALIZED,
+ /** Library not initialized. */
+ EC_ERR_NOT_INITIALIZED,
+ /** Timeout occurred during execution of the function. */
+ EC_ERR_TIMEOUT,
+ /** No slaves were found. */
+ EC_ERR_NO_SLAVES,
+ /** Function failed. */
+ EC_ERR_NOK
+} ec_err;
+
+/** Possible EtherCAT slave states */
+typedef enum
+{
+ /** No valid state. */
+ EC_STATE_NONE = 0x00,
+ /** Init state*/
+ EC_STATE_INIT = 0x01,
+ /** Pre-operational. */
+ EC_STATE_PRE_OP = 0x02,
+ /** Boot state*/
+ EC_STATE_BOOT = 0x03,
+ /** Safe-operational. */
+ EC_STATE_SAFE_OP = 0x04,
+ /** Operational */
+ EC_STATE_OPERATIONAL = 0x08,
+ /** Error or ACK error */
+ EC_STATE_ACK = 0x10,
+ EC_STATE_ERROR = 0x10
+} ec_state;
+
+/** Possible buffer states */
+typedef enum
+{
+ /** Empty */
+ EC_BUF_EMPTY = 0x00,
+ /** Allocated, but not filled */
+ EC_BUF_ALLOC = 0x01,
+ /** Transmitted */
+ EC_BUF_TX = 0x02,
+ /** Received, but not consumed */
+ EC_BUF_RCVD = 0x03,
+ /** Cycle completed */
+ EC_BUF_COMPLETE = 0x04
+} ec_bufstate;
+
+/** Ethercat data types */
+typedef enum
+{
+ ECT_BOOLEAN = 0x0001,
+ ECT_INTEGER8 = 0x0002,
+ ECT_INTEGER16 = 0x0003,
+ ECT_INTEGER32 = 0x0004,
+ ECT_UNSIGNED8 = 0x0005,
+ ECT_UNSIGNED16 = 0x0006,
+ ECT_UNSIGNED32 = 0x0007,
+ ECT_REAL32 = 0x0008,
+ ECT_VISIBLE_STRING = 0x0009,
+ ECT_OCTET_STRING = 0x000A,
+ ECT_UNICODE_STRING = 0x000B,
+ ECT_TIME_OF_DAY = 0x000C,
+ ECT_TIME_DIFFERENCE = 0x000D,
+ ECT_DOMAIN = 0x000F,
+ ECT_INTEGER24 = 0x0010,
+ ECT_REAL64 = 0x0011,
+ ECT_INTEGER64 = 0x0015,
+ ECT_UNSIGNED24 = 0x0016,
+ ECT_UNSIGNED64 = 0x001B,
+ ECT_BIT1 = 0x0030,
+ ECT_BIT2 = 0x0031,
+ ECT_BIT3 = 0x0032,
+ ECT_BIT4 = 0x0033,
+ ECT_BIT5 = 0x0034,
+ ECT_BIT6 = 0x0035,
+ ECT_BIT7 = 0x0036,
+ ECT_BIT8 = 0x0037
+} ec_datatype;
+
+/** Ethercat command types */
+typedef enum
+{
+ /** No operation */
+ EC_CMD_NOP = 0x00,
+ /** Auto Increment Read */
+ EC_CMD_APRD,
+ /** Auto Increment Write */
+ EC_CMD_APWR,
+ /** Auto Increment Read Write */
+ EC_CMD_APRW,
+ /** Configured Address Read */
+ EC_CMD_FPRD,
+ /** Configured Address Write */
+ EC_CMD_FPWR,
+ /** Configured Address Read Write */
+ EC_CMD_FPRW,
+ /** Broadcast Read */
+ EC_CMD_BRD,
+ /** Broadcast Write */
+ EC_CMD_BWR,
+ /** Broadcast Read Write */
+ EC_CMD_BRW,
+ /** Logical Memory Read */
+ EC_CMD_LRD,
+ /** Logical Memory Write */
+ EC_CMD_LWR,
+ /** Logical Memory Read Write */
+ EC_CMD_LRW,
+ /** Auto Increment Read Multiple Write */
+ EC_CMD_ARMW,
+ /** Configured Read Multiple Write */
+ EC_CMD_FRMW
+ /** Reserved */
+} ec_cmdtype;
+
+/** Ethercat EEprom command types */
+typedef enum
+{
+ /** No operation */
+ EC_ECMD_NOP = 0x0000,
+ /** Read */
+ EC_ECMD_READ = 0x0100,
+ /** Write */
+ EC_ECMD_WRITE = 0x0201,
+ /** Reload */
+ EC_ECMD_RELOAD = 0x0300
+} ec_ecmdtype;
+
+/** EEprom state machine read size */
+#define EC_ESTAT_R64 0x0040
+/** EEprom state machine busy flag */
+#define EC_ESTAT_BUSY 0x8000
+/** EEprom state machine error flag mask */
+#define EC_ESTAT_EMASK 0x7800
+/** EEprom state machine error acknowledge */
+#define EC_ESTAT_NACK 0x2000
+
+/* Ethercat SSI (Slave Information Interface) */
+
+/** Start address SII sections in Eeprom */
+#define ECT_SII_START 0x0040
+
+enum
+{
+ /** SII category strings */
+ ECT_SII_STRING = 10,
+ /** SII category general */
+ ECT_SII_GENERAL = 30,
+ /** SII category FMMU */
+ ECT_SII_FMMU = 40,
+ /** SII category SM */
+ ECT_SII_SM = 41,
+ /** SII category PDO */
+ ECT_SII_PDO = 50
+};
+
+/** Item offsets in SII general section */
+enum
+{
+ ECT_SII_MANUF = 0x0008,
+ ECT_SII_ID = 0x000a,
+ ECT_SII_REV = 0x000c,
+ ECT_SII_BOOTRXMBX = 0x0014,
+ ECT_SII_BOOTTXMBX = 0x0016,
+ ECT_SII_MBXSIZE = 0x0019,
+ ECT_SII_TXMBXADR = 0x001a,
+ ECT_SII_RXMBXADR = 0x0018,
+ ECT_SII_MBXPROTO = 0x001c
+};
+
+/** Mailbox types definitions */
+enum
+{
+ /** Error mailbox type */
+ ECT_MBXT_ERR = 0x00,
+ /** ADS over EtherCAT mailbox type */
+ ECT_MBXT_AOE,
+ /** Ethernet over EtherCAT mailbox type */
+ ECT_MBXT_EOE,
+ /** CANopen over EtherCAT mailbox type */
+ ECT_MBXT_COE,
+ /** File over EtherCAT mailbox type */
+ ECT_MBXT_FOE,
+ /** Servo over EtherCAT mailbox type */
+ ECT_MBXT_SOE,
+ /** Vendor over EtherCAT mailbox type */
+ ECT_MBXT_VOE = 0x0f
+};
+
+/** CoE mailbox types */
+enum
+{
+ ECT_COES_EMERGENCY = 0x01,
+ ECT_COES_SDOREQ,
+ ECT_COES_SDORES,
+ ECT_COES_TXPDO,
+ ECT_COES_RXPDO,
+ ECT_COES_TXPDO_RR,
+ ECT_COES_RXPDO_RR,
+ ECT_COES_SDOINFO
+};
+
+/** CoE SDO commands */
+enum
+{
+ ECT_SDO_DOWN_INIT = 0x21,
+ ECT_SDO_DOWN_EXP = 0x23,
+ ECT_SDO_DOWN_INIT_CA = 0x31,
+ ECT_SDO_UP_REQ = 0x40,
+ ECT_SDO_UP_REQ_CA = 0x50,
+ ECT_SDO_SEG_UP_REQ = 0x60,
+ ECT_SDO_ABORT = 0x80
+};
+
+/** CoE Object Description commands */
+enum
+{
+ ECT_GET_ODLIST_REQ = 0x01,
+ ECT_GET_ODLIST_RES = 0x02,
+ ECT_GET_OD_REQ = 0x03,
+ ECT_GET_OD_RES = 0x04,
+ ECT_GET_OE_REQ = 0x05,
+ ECT_GET_OE_RES = 0x06,
+ ECT_SDOINFO_ERROR = 0x07
+};
+
+/** FoE opcodes */
+enum
+{
+ ECT_FOE_READ = 0x01,
+ ECT_FOE_WRITE,
+ ECT_FOE_DATA,
+ ECT_FOE_ACK,
+ ECT_FOE_ERROR,
+ ECT_FOE_BUSY
+};
+
+/** SoE opcodes */
+enum
+{
+ ECT_SOE_READREQ = 0x01,
+ ECT_SOE_READRES,
+ ECT_SOE_WRITEREQ,
+ ECT_SOE_WRITERES,
+ ECT_SOE_NOTIFICATION,
+ ECT_SOE_EMERGENCY
+};
+
+/** Ethercat registers */
+enum
+{
+ ECT_REG_TYPE = 0x0000,
+ ECT_REG_PORTDES = 0x0007,
+ ECT_REG_ESCSUP = 0x0008,
+ ECT_REG_STADR = 0x0010,
+ ECT_REG_ALIAS = 0x0012,
+ ECT_REG_DLCTL = 0x0100,
+ ECT_REG_DLPORT = 0x0101,
+ ECT_REG_DLALIAS = 0x0103,
+ ECT_REG_DLSTAT = 0x0110,
+ ECT_REG_ALCTL = 0x0120,
+ ECT_REG_ALSTAT = 0x0130,
+ ECT_REG_ALSTATCODE = 0x0134,
+ ECT_REG_PDICTL = 0x0140,
+ ECT_REG_IRQMASK = 0x0200,
+ ECT_REG_RXERR = 0x0300,
+ ECT_REG_FRXERR = 0x0308,
+ ECT_REG_EPUECNT = 0x030C,
+ ECT_REG_PECNT = 0x030D,
+ ECT_REG_PECODE = 0x030E,
+ ECT_REG_LLCNT = 0x0310,
+ ECT_REG_WDCNT = 0x0442,
+ ECT_REG_EEPCFG = 0x0500,
+ ECT_REG_EEPCTL = 0x0502,
+ ECT_REG_EEPSTAT = 0x0502,
+ ECT_REG_EEPADR = 0x0504,
+ ECT_REG_EEPDAT = 0x0508,
+ ECT_REG_FMMU0 = 0x0600,
+ ECT_REG_FMMU1 = ECT_REG_FMMU0 + 0x10,
+ ECT_REG_FMMU2 = ECT_REG_FMMU1 + 0x10,
+ ECT_REG_FMMU3 = ECT_REG_FMMU2 + 0x10,
+ ECT_REG_SM0 = 0x0800,
+ ECT_REG_SM1 = ECT_REG_SM0 + 0x08,
+ ECT_REG_SM2 = ECT_REG_SM1 + 0x08,
+ ECT_REG_SM3 = ECT_REG_SM2 + 0x08,
+ ECT_REG_SM0STAT = ECT_REG_SM0 + 0x05,
+ ECT_REG_SM1STAT = ECT_REG_SM1 + 0x05,
+ ECT_REG_SM1ACT = ECT_REG_SM1 + 0x06,
+ ECT_REG_SM1CONTR = ECT_REG_SM1 + 0x07,
+ ECT_REG_DCTIME0 = 0x0900,
+ ECT_REG_DCTIME1 = 0x0904,
+ ECT_REG_DCTIME2 = 0x0908,
+ ECT_REG_DCTIME3 = 0x090C,
+ ECT_REG_DCSYSTIME = 0x0910,
+ ECT_REG_DCSOF = 0x0918,
+ ECT_REG_DCSYSOFFSET = 0x0920,
+ ECT_REG_DCSYSDELAY = 0x0928,
+ ECT_REG_DCSYSDIFF = 0x092C,
+ ECT_REG_DCSPEEDCNT = 0x0930,
+ ECT_REG_DCTIMEFILT = 0x0934,
+ ECT_REG_DCCUC = 0x0980,
+ ECT_REG_DCSYNCACT = 0x0981,
+ ECT_REG_DCSTART0 = 0x0990,
+ ECT_REG_DCCYCLE0 = 0x09A0,
+ ECT_REG_DCCYCLE1 = 0x09A4
+};
+
+/** standard SDO Sync Manager Communication Type */
+#define ECT_SDO_SMCOMMTYPE 0x1c00
+/** standard SDO PDO assignment */
+#define ECT_SDO_PDOASSIGN 0x1c10
+/** standard SDO RxPDO assignment */
+#define ECT_SDO_RXPDOASSIGN 0x1c12
+/** standard SDO TxPDO assignment */
+#define ECT_SDO_TXPDOASSIGN 0x1c13
+
+/** Ethercat packet type */
+#define ETH_P_ECAT 0x88A4
+
+/** Error types */
+typedef enum
+{
+ EC_ERR_TYPE_SDO_ERROR = 0,
+ EC_ERR_TYPE_EMERGENCY = 1,
+ EC_ERR_TYPE_PACKET_ERROR = 3,
+ EC_ERR_TYPE_SDOINFO_ERROR = 4,
+ EC_ERR_TYPE_FOE_ERROR = 5,
+ EC_ERR_TYPE_FOE_BUF2SMALL = 6,
+ EC_ERR_TYPE_FOE_PACKETNUMBER = 7,
+ EC_ERR_TYPE_SOE_ERROR = 8,
+ EC_ERR_TYPE_MBX_ERROR = 9,
+ EC_ERR_TYPE_FOE_FILE_NOTFOUND = 10,
+ EC_ERR_TYPE_EOE_INVALID_RX_DATA = 11
+} ec_err_type;
+
+/** Struct to retrieve errors. */
+typedef struct
+{
+ /** Time at which the error was generated. */
+ ec_timet Time;
+ /** Signal bit, error set but not read */
+ boolean Signal;
+ /** Slave number that generated the error */
+ uint16 Slave;
+ /** CoE SDO index that generated the error */
+ uint16 Index;
+ /** CoE SDO subindex that generated the error */
+ uint8 SubIdx;
+ /** Type of error */
+ ec_err_type Etype;
+ union
+ {
+ /** General abortcode */
+ int32 AbortCode;
+ /** Specific error for Emergency mailbox */
+ struct
+ {
+ uint16 ErrorCode;
+ uint8 ErrorReg;
+ uint8 b1;
+ uint16 w1;
+ uint16 w2;
+ };
+ };
+} ec_errort;
+
+/** Helper macros */
+/** Macro to make a word from 2 bytes */
+#define MK_WORD(msb, lsb) ((((uint16)(msb))<<8) | (lsb))
+/** Macro to get hi byte of a word */
+#define HI_BYTE(w) ((w) >> 8)
+/** Macro to get low byte of a word */
+#define LO_BYTE(w) ((w) & 0x00ff)
+/** Macro to swap hi and low byte of a word */
+#define SWAP(w) ((((w)& 0xff00) >> 8) | (((w) & 0x00ff) << 8))
+/** Macro to get hi word of a dword */
+#define LO_WORD(l) ((l) & 0xffff)
+/** Macro to get hi word of a dword */
+#define HI_WORD(l) ((l) >> 16)
+
+#define get_unaligned(ptr) \
+ ({ __typeof__(*(ptr)) __tmp; memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; })
+
+#define put_unaligned32(val, ptr) \
+ (memcpy((ptr), &(val), 4))
+
+#define put_unaligned64(val, ptr) \
+ (memcpy((ptr), &(val), 8))
+
+#if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN)
+
+ #define htoes(A) (A)
+ #define htoel(A) (A)
+ #define htoell(A) (A)
+ #define etohs(A) (A)
+ #define etohl(A) (A)
+ #define etohll(A) (A)
+
+#elif !defined(EC_LITTLE_ENDIAN) && defined(EC_BIG_ENDIAN)
+
+ #define htoes(A) ((((uint16)(A) & 0xff00) >> 8) | \
+ (((uint16)(A) & 0x00ff) << 8))
+ #define htoel(A) ((((uint32)(A) & 0xff000000) >> 24) | \
+ (((uint32)(A) & 0x00ff0000) >> 8) | \
+ (((uint32)(A) & 0x0000ff00) << 8) | \
+ (((uint32)(A) & 0x000000ff) << 24))
+ #define htoell(A) ((((uint64)(A) & (uint64)0xff00000000000000ULL) >> 56) | \
+ (((uint64)(A) & (uint64)0x00ff000000000000ULL) >> 40) | \
+ (((uint64)(A) & (uint64)0x0000ff0000000000ULL) >> 24) | \
+ (((uint64)(A) & (uint64)0x000000ff00000000ULL) >> 8) | \
+ (((uint64)(A) & (uint64)0x00000000ff000000ULL) << 8) | \
+ (((uint64)(A) & (uint64)0x0000000000ff0000ULL) << 24) | \
+ (((uint64)(A) & (uint64)0x000000000000ff00ULL) << 40) | \
+ (((uint64)(A) & (uint64)0x00000000000000ffULL) << 56))
+
+ #define etohs htoes
+ #define etohl htoel
+ #define etohll htoell
+
+#else
+
+ #error "Must define one of EC_BIG_ENDIAN or EC_LITTLE_ENDIAN"
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EC_TYPE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/osal/osal.cpp Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,117 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#include "mbed.h"
+
+#include <osal.h>
+#include <config.h>
+
+
+uint64_t tick = 0;
+uint64_t last_tick = 0;
+
+
+#define timercmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) ? \
+ ((a)->tv_usec CMP (b)->tv_usec) : \
+ ((a)->tv_sec CMP (b)->tv_sec))
+
+
+#define timeradd(a, b, result) \
+ do{ \
+ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
+ if ((result)->tv_usec >= 1000000) \
+ { \
+ ++(result)->tv_sec; \
+ (result)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+
+
+#define CFG_TICKS_PER_SECOND 1000000
+#define USECS_PER_SEC 1000000
+#define USECS_PER_TICK (USECS_PER_SEC / CFG_TICKS_PER_SECOND)
+
+
+
+int gettimeofday_(struct timeval *tp, void *tzp)
+{
+ //tick_t tick = tick_get();
+ uint32_t actual_tick = us_ticker_read();
+ tick += (actual_tick - last_tick);
+ last_tick = actual_tick;
+
+ //tick_t ticks_left;
+ uint64_t ticks_left;
+
+ tp->tv_sec = tick / CFG_TICKS_PER_SECOND;
+ ticks_left = tick % CFG_TICKS_PER_SECOND;
+ tp->tv_usec = ticks_left * USECS_PER_TICK;
+
+ return 0;
+}
+
+int osal_usleep (uint32 usec)
+{
+ wait_us(usec);
+ return 0;
+}
+
+
+int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ return gettimeofday_(tv, tz);
+}
+
+
+ec_timet osal_current_time (void)
+{
+ struct timeval current_time;
+ ec_timet return_value;
+
+ gettimeofday_ (¤t_time, 0);
+ return_value.sec = current_time.tv_sec;
+ return_value.usec = current_time.tv_usec;
+ return return_value;
+}
+
+void osal_timer_start (osal_timert * self, uint32 timeout_usec)
+{
+ struct timeval start_time;
+ struct timeval timeout;
+ struct timeval stop_time;
+
+ gettimeofday_ (&start_time, 0);
+ timeout.tv_sec = timeout_usec / USECS_PER_SEC;
+ timeout.tv_usec = timeout_usec % USECS_PER_SEC;
+ timeradd (&start_time, &timeout, &stop_time);
+
+ self->stop_time.sec = stop_time.tv_sec;
+ self->stop_time.usec = stop_time.tv_usec;
+}
+
+boolean osal_timer_is_expired (osal_timert * self)
+{
+ struct timeval current_time;
+ struct timeval stop_time;
+ int is_not_yet_expired;
+
+ gettimeofday_ (¤t_time, 0);
+ stop_time.tv_sec = self->stop_time.sec;
+ stop_time.tv_usec = self->stop_time.usec;
+ is_not_yet_expired = timercmp (¤t_time, &stop_time, <);
+
+ return is_not_yet_expired == false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/osal/osal.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,57 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#ifndef _osal_
+#define _osal_
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "osal_defs.h"
+#include <stdint.h>
+
+/* General types */
+#define TRUE 1
+#define FALSE 0
+
+#define boolean uint8_t
+
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+#define uint64 uint64_t
+#define int8 int8_t
+#define int16 int16_t
+#define int32 int32_t
+#define int64 int64_t
+
+
+
+typedef struct
+{
+ uint32 sec; //< Seconds elapsed since the Epoch (Jan 1, 1970)
+ uint32 usec; //< Microseconds elapsed since last second boundary
+} ec_timet;
+
+
+typedef struct osal_timer
+{
+ ec_timet stop_time;
+} osal_timert;
+
+void osal_timer_start(osal_timert * self, uint32 timeout_us);
+boolean osal_timer_is_expired(osal_timert * self);
+int osal_usleep(uint32 usec);
+ec_timet osal_current_time(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/osal/osal_defs.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,37 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#ifndef _osal_defs_
+#define _osal_defs_
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+// define if debug printf is needed
+//#define EC_DEBUG
+
+#ifdef EC_DEBUG
+#define EC_PRINT printf
+#else
+#define EC_PRINT(...) do {} while (0)
+#endif
+
+//#ifndef PACKED
+#define PACKED_BEGIN
+#define PACKED __attribute__((__packed__))
+#define PACKED_END
+//#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/nicdrv.cpp Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,782 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * EtherCAT RAW socket driver.
+ *
+ * Low level interface functions to send and receive EtherCAT packets.
+ * EtherCAT has the property that packets are only send by the master,
+ * and the send packets always return in the receive buffer.
+ * There can be multiple packets "on the wire" before they return.
+ * To combine the received packets with the original send packets a buffer
+ * system is installed. The identifier is put in the index item of the
+ * EtherCAT header. The index is stored and compared when a frame is received.
+ * If there is a match the packet can be combined with the transmit packet
+ * and returned to the higher level function.
+ *
+ * The socket layer can exhibit a reversal in the packet order (rare).
+ * If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
+ * will reorder the packets automatically.
+ *
+ * The "redundant" option will configure two sockets and two NIC interfaces.
+ * Slaves are connected to both interfaces, one on the IN port and one on the
+ * OUT port. Packets are send via both interfaces. Any one of the connections
+ * (also an interconnect) can be removed and the slaves are still serviced with
+ * packets. The software layer will detect the possible failure modes and
+ * compensate. If needed the packets from interface A are resent through interface B.
+ * This layer is fully transparent for the higher layers.
+ */
+
+
+#include "mbed.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "osal.h"
+#include "oshw.h"
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+
+
+/** Redundancy modes */
+enum
+{
+ /** No redundancy, single NIC mode */
+ ECT_RED_NONE,
+ /** Double redundant NIC connection */
+ ECT_RED_DOUBLE
+};
+
+/** Primary source MAC address used for EtherCAT.
+ * This address is not the MAC address used from the NIC.
+ * EtherCAT does not care about MAC addressing, but it is used here to
+ * differentiate the route the packet traverses through the EtherCAT
+ * segment. This is needed to find out the packet flow in redundant
+ * configurations. */
+const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
+/** Secondary source MAC address used for EtherCAT. */
+const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };
+
+/** second MAC word is used for identification */
+#define RX_PRIM priMAC[1]
+/** second MAC word is used for identification */
+#define RX_SEC secMAC[1]
+
+
+
+
+//******************************************************************************
+// driver used:
+// \mbed-os-5.5.7\targets\TARGET_STM\TARGET_STM32F7\device\stm32f7xx_hal_eth.c
+//******************************************************************************
+
+
+GPIO_InitTypeDef GPIO_InitStructure;
+
+
+
+ETH_HandleTypeDef hEth; // ethernet interface handle structure
+
+ETH_DMADescTypeDef DMATxDescTab[ETH_TXBUFNB]; // dma tx descriptors
+ETH_DMADescTypeDef DMARxDescTab[ETH_RXBUFNB]; // dma rx descriptors
+
+uint8_t DmaTxBuff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE]; // dma tx buffers
+uint8_t DmaRxBuff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE]; // dma rx buffers
+
+
+
+HAL_StatusTypeDef HalStatus; // response from Hal functions
+
+
+//------------------------------------------------------------------------------
+
+void EthInit()
+{
+ // HAL status codes
+ //
+ // HAL_OK = 0x00U,
+ // HAL_ERROR = 0x01U,
+ // HAL_BUSY = 0x02U,
+ // HAL_TIMEOUT = 0x03U
+
+
+ uint8_t MACAddr[6] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}; // mac address
+
+ hEth.State = HAL_ETH_STATE_RESET; // don't know if it is useful !!!
+
+ hEth.Instance = ETH;
+ hEth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_DISABLE;
+ hEth.Init.Speed = ETH_SPEED_100M;
+ hEth.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
+ hEth.Init.PhyAddress = 0x00;
+ hEth.Init.MACAddr = &MACAddr[0];
+ hEth.Init.RxMode = ETH_RXPOLLING_MODE;
+ hEth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
+ hEth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
+
+ HalStatus = HAL_ETH_Init(&hEth);
+ NVIC_DisableIRQ(ETH_IRQn); // we don't use interrupt
+ if (HalStatus != HAL_OK)
+ {
+ printf ("HAL_ETH_Init err %d\n", HalStatus);
+ }
+
+ HalStatus = HAL_ETH_DMATxDescListInit(&hEth, &DMATxDescTab[0], &DmaTxBuff[0][0], ETH_TXBUFNB);
+ if (HalStatus != HAL_OK)
+ {
+ printf ("HAL_ETH_DMATxDescListInit err %d\n", HalStatus);
+ }
+
+ HalStatus = HAL_ETH_DMARxDescListInit(&hEth, &DMARxDescTab[0], &DmaRxBuff[0][0], ETH_RXBUFNB);
+ if (HalStatus != HAL_OK)
+ {
+ printf ("HAL_ETH_DMARxDescListInit err %d\n", HalStatus);
+ }
+
+ HalStatus = HAL_ETH_Start(&hEth);
+ if (HalStatus != HAL_OK)
+ {
+ printf ("HAL_ETH_Start err %d\n", HalStatus);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+int EthWrPacket (uint8_t* pBuff, uint16_t Len)
+{
+ uint8_t* pDmaBuff;
+
+ if ((hEth.TxDesc->Status & ETH_DMATXDESC_OWN) == (uint32_t)RESET)
+
+ {
+ pDmaBuff = (uint8_t*)(hEth.TxDesc->Buffer1Addr);
+
+ memcpy (pDmaBuff, pBuff, Len);
+
+ HalStatus = HAL_ETH_TransmitFrame(&hEth, Len);
+ if (HalStatus != HAL_OK)
+ {
+ printf ("HAL_ETH_TransmitFrame err %d\n", HalStatus);
+ return NULL;
+ }
+
+ return Len;
+ }
+
+ else
+ {
+ return NULL;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+int EthRdPacket(uint8_t* pBuff)
+{
+ int Len;
+ uint8_t* pDmaBuff;
+
+ HalStatus = HAL_ETH_GetReceivedFrame(&hEth); // check if a packet has been received
+
+ if (HalStatus == HAL_OK) // packet received
+ {
+ Len = hEth.RxFrameInfos.length; // packet lenght
+ pDmaBuff = (uint8_t*)hEth.RxFrameInfos.buffer; // DMA buffer pointer
+
+ memcpy (pBuff, pDmaBuff, Len); // read the data
+
+ // release the descriptor
+ hEth.RxFrameInfos.FSRxDesc->Status |= ETH_DMARXDESC_OWN;
+ hEth.RxFrameInfos.SegCount = 0; // reset segment count
+
+ return Len; // return the number of bytes read
+ }
+
+ else
+ {
+ return NULL; // no packet received
+ }
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+
+
+
+static void ecx_clear_rxbufstat(int *rxbufstat)
+{
+ int i;
+ for(i = 0; i < EC_MAXBUF; i++)
+ {
+ rxbufstat[i] = EC_BUF_EMPTY;
+ }
+}
+
+/** Basic setup to connect NIC to socket.
+ * @param[in] port = port context struct
+ * @param[in] ifname = Name of NIC device, f.e. "eth0"
+ * @param[in] secondary = if >0 then use secondary stack instead of primary
+ * @return >0 if succeeded
+ */
+int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)
+{
+ int i;
+ int rVal;
+ int *psock;
+
+ EthInit();
+
+ if (rVal != 0)
+ return 0;
+
+ if (secondary)
+ {
+ /* secondary port struct available? */
+ if (port->redport)
+ {
+ /* when using secondary socket it is automatically a redundant setup */
+ psock = &(port->redport->sockhandle);
+ *psock = -1;
+ port->redstate = ECT_RED_DOUBLE;
+ port->redport->stack.sock = &(port->redport->sockhandle);
+ port->redport->stack.txbuf = &(port->txbuf);
+ port->redport->stack.txbuflength = &(port->txbuflength);
+ port->redport->stack.tempbuf = &(port->redport->tempinbuf);
+ port->redport->stack.rxbuf = &(port->redport->rxbuf);
+ port->redport->stack.rxbufstat = &(port->redport->rxbufstat);
+ port->redport->stack.rxsa = &(port->redport->rxsa);
+ ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
+ }
+ else
+ {
+ /* fail */
+ return 0;
+ }
+ }
+ else
+ {
+ port->sockhandle = -1;
+ port->lastidx = 0;
+ port->redstate = ECT_RED_NONE;
+ port->stack.sock = &(port->sockhandle);
+ port->stack.txbuf = &(port->txbuf);
+ port->stack.txbuflength = &(port->txbuflength);
+ port->stack.tempbuf = &(port->tempinbuf);
+ port->stack.rxbuf = &(port->rxbuf);
+ port->stack.rxbufstat = &(port->rxbufstat);
+ port->stack.rxsa = &(port->rxsa);
+ ecx_clear_rxbufstat(&(port->rxbufstat[0]));
+ psock = &(port->sockhandle);
+ }
+
+ /* setup ethernet headers in tx buffers so we don't have to repeat it */
+ for (i = 0; i < EC_MAXBUF; i++)
+ {
+ ec_setupheader(&(port->txbuf[i]));
+ port->rxbufstat[i] = EC_BUF_EMPTY;
+ }
+ ec_setupheader(&(port->txbuf2));
+
+ return 1;
+}
+
+/** Close sockets used
+ * @param[in] port = port context struct
+ * @return 0
+ */
+int ecx_closenic(ecx_portt *port)
+{
+ if (port->sockhandle >= 0)
+ {
+ close(port->sockhandle);
+ }
+ if ((port->redport) && (port->redport->sockhandle >= 0))
+ {
+ close(port->redport->sockhandle);
+ }
+ return 0;
+}
+
+/** Fill buffer with ethernet header structure.
+ * Destination MAC is always broadcast.
+ * Ethertype is always ETH_P_ECAT.
+ * @param[out] p = buffer
+ */
+
+void ec_setupheader(void *p)
+{
+ ec_etherheadert *bp;
+
+ bp = (ec_etherheadert*)p;
+
+
+ bp->da0 = oshw_htons(0xffff);
+ bp->da1 = oshw_htons(0xffff);
+ bp->da2 = oshw_htons(0xffff);
+ bp->sa0 = oshw_htons(priMAC[0]);
+ bp->sa1 = oshw_htons(priMAC[1]);
+ bp->sa2 = oshw_htons(priMAC[2]);
+ bp->etype = oshw_htons(ETH_P_ECAT);
+}
+
+/** Get new frame identifier index and allocate corresponding rx buffer.
+ * @param[in] port = port context struct
+ * @return new index.
+ */
+int ecx_getindex(ecx_portt *port)
+{
+ int idx;
+ int cnt;
+
+ // mtx_lock (port->getindex_mutex); //******//
+
+ idx = port->lastidx + 1;
+ /* index can't be larger than buffer array */
+ if (idx >= EC_MAXBUF)
+ {
+ idx = 0;
+ }
+ cnt = 0;
+ /* try to find unused index */
+ while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
+ {
+ idx++;
+ cnt++;
+ if (idx >= EC_MAXBUF)
+ {
+ idx = 0;
+ }
+ }
+ port->rxbufstat[idx] = EC_BUF_ALLOC;
+ if (port->redstate != ECT_RED_NONE)
+ {
+ port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
+ }
+ port->lastidx = idx;
+
+ // mtx_unlock (port->getindex_mutex); //******//
+
+ return idx;
+}
+
+/** Set rx buffer status.
+ * @param[in] port = port context struct
+ * @param[in] idx = index in buffer array
+ * @param[in] bufstat = status to set
+ */
+void ecx_setbufstat(ecx_portt *port, int idx, int bufstat)
+{
+ port->rxbufstat[idx] = bufstat;
+ if (port->redstate != ECT_RED_NONE)
+ {
+ port->redport->rxbufstat[idx] = bufstat;
+ }
+}
+
+/** Transmit buffer over socket (non blocking).
+ * @param[in] port = port context struct
+ * @param[in] idx = index in tx buffer array
+ * @param[in] stacknumber = 0=Primary 1=Secondary stack
+ * @return socket send result
+ */
+int ecx_outframe(ecx_portt *port, int idx, int stacknumber)
+{
+ int lp, rval;
+ ec_stackT *stack;
+
+ if (!stacknumber)
+ {
+ stack = &(port->stack);
+ }
+ else
+ {
+ stack = &(port->redport->stack);
+ }
+ lp = (*stack->txbuflength)[idx];
+ (*stack->rxbufstat)[idx] = EC_BUF_TX;
+
+ rval = EthWrPacket ((*stack->txbuf)[idx], lp);
+
+ return rval;
+}
+
+/** Transmit buffer over socket (non blocking).
+ * @param[in] port = port context struct
+ * @param[in] idx = index in tx buffer array
+ * @return socket send result
+ */
+int ecx_outframe_red(ecx_portt *port, int idx)
+{
+ ec_comt *datagramP;
+ ec_etherheadert *ehp;
+ int rval;
+
+ ehp = (ec_etherheadert *)&(port->txbuf[idx]);
+ /* rewrite MAC source address 1 to primary */
+ ehp->sa1 = oshw_htons(priMAC[1]);
+ /* transmit over primary socket*/
+ rval = ecx_outframe(port, idx, 0);
+ if (port->redstate != ECT_RED_NONE)
+ {
+ // mtx_lock (port->tx_mutex); //******//
+ ehp = (ec_etherheadert *)&(port->txbuf2);
+ /* use dummy frame for secondary socket transmit (BRD) */
+ datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
+ /* write index to frame */
+ datagramP->index = idx;
+ /* rewrite MAC source address 1 to secondary */
+ ehp->sa1 = oshw_htons(secMAC[1]);
+ /* transmit over secondary socket */
+ //send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
+ // OBS! redundant not ACTIVE for BFIN, just added to compile
+
+ port->redport->rxbufstat[idx] = EC_BUF_TX;
+
+ EthWrPacket((uint8_t*)&(port->txbuf2), port->txbuflength2);
+
+ // mtx_unlock (port->tx_mutex); //******//
+ }
+
+ return rval;
+}
+
+/** Non blocking read of socket. Put frame in temporary buffer.
+ * @param[in] port = port context struct
+ * @param[in] stacknumber = 0=primary 1=secondary stack
+ * @return >0 if frame is available and read
+ */
+static int ecx_recvpkt(ecx_portt *port, int stacknumber)
+{
+ int lp, bytesrx;
+ ec_stackT *stack;
+
+ if (!stacknumber)
+ {
+ stack = &(port->stack);
+ }
+ else
+ {
+ stack = &(port->redport->stack);
+ }
+ lp = sizeof(port->tempinbuf);
+
+ bytesrx = EthRdPacket(*stack->tempbuf);
+
+
+ port->tempinbufs = bytesrx;
+
+ return (bytesrx > 0);
+}
+
+/** Non blocking receive frame function. Uses RX buffer and index to combine
+ * read frame with transmitted frame. To compensate for received frames that
+ * are out-of-order all frames are stored in their respective indexed buffer.
+ * If a frame was placed in the buffer previously, the function retrieves it
+ * from that buffer index without calling ec_recvpkt. If the requested index
+ * is not already in the buffer it calls ec_recvpkt to fetch it. There are
+ * three options now, 1 no frame read, so exit. 2 frame read but other
+ * than requested index, store in buffer and exit. 3 frame read with matching
+ * index, store in buffer, set completed flag in buffer status and exit.
+ *
+ * @param[in] port = port context struct
+ * @param[in] idx = requested index of frame
+ * @param[in] stacknumber = 0=primary 1=secondary stack
+ * @return Workcounter if a frame is found with corresponding index, otherwise
+ * EC_NOFRAME or EC_OTHERFRAME.
+ */
+int ecx_inframe(ecx_portt *port, int idx, int stacknumber)
+{
+ uint16 l;
+ int rval;
+ uint8 idxf;
+ ec_etherheadert *ehp;
+ ec_comt *ecp;
+ ec_stackT *stack;
+ ec_bufT *rxbuf;
+
+ if (!stacknumber)
+ {
+ stack = &(port->stack);
+ }
+ else
+ {
+ stack = &(port->redport->stack);
+ }
+ rval = EC_NOFRAME;
+ rxbuf = &(*stack->rxbuf)[idx];
+ /* check if requested index is already in buffer ? */
+ if ((idx < EC_MAXBUF) && ( (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
+ {
+ l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
+ /* return WKC */
+ rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
+ /* mark as completed */
+ (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
+ }
+ else
+ {
+ // mtx_lock (port->rx_mutex); //******//
+ /* non blocking call to retrieve frame from socket */
+ if (ecx_recvpkt(port, stacknumber))
+ {
+ rval = EC_OTHERFRAME;
+ ehp =(ec_etherheadert*)(stack->tempbuf);
+ /* check if it is an EtherCAT frame */
+ if (ehp->etype == oshw_htons(ETH_P_ECAT))
+ {
+ ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
+ l = etohs(ecp->elength) & 0x0fff;
+ idxf = ecp->index;
+ /* found index equals requested index ? */
+ if (idxf == idx)
+ {
+ /* yes, put it in the buffer array (strip ethernet header) */
+ memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
+ /* return WKC */
+ rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
+ /* mark as completed */
+ (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
+ /* store MAC source word 1 for redundant routing info */
+ (*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
+ }
+ else
+ {
+ /* check if index exist and someone is waiting for it */
+ if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
+ {
+ rxbuf = &(*stack->rxbuf)[idxf];
+ /* put it in the buffer array (strip ethernet header) */
+ memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
+ /* mark as received */
+ (*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
+ (*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
+ }
+ else
+ {
+ /* strange things happened */
+ }
+ }
+ }
+ }
+ // mtx_unlock (port->rx_mutex); //******//
+
+ }
+
+ /* WKC if matching frame found */
+ return rval;
+}
+
+
+/** Blocking redundant receive frame function. If redundant mode is not active then
+ * it skips the secondary stack and redundancy functions. In redundant mode it waits
+ * for both (primary and secondary) frames to come in. The result goes in an decision
+ * tree that decides, depending on the route of the packet and its possible missing arrival,
+ * how to reroute the original packet to get the data in an other try.
+ *
+ * @param[in] port = port context struct
+ * @param[in] idx = requested index of frame
+ * @param[in] timer = absolute timeout time
+ * @return Workcounter if a frame is found with corresponding index, otherwise
+ * EC_NOFRAME.
+ */
+static int ecx_waitinframe_red(ecx_portt *port, int idx, osal_timert timer)
+{
+ int wkc = EC_NOFRAME;
+ int wkc2 = EC_NOFRAME;
+ int primrx, secrx;
+
+ /* if not in redundant mode then always assume secondary is OK */
+ if (port->redstate == ECT_RED_NONE)
+ {
+ wkc2 = 0;
+ }
+ do
+ {
+ /* only read frame if not already in */
+ if (wkc <= EC_NOFRAME)
+ {
+ wkc = ecx_inframe(port, idx, 0);
+ }
+ /* only try secondary if in redundant mode */
+ if (port->redstate != ECT_RED_NONE)
+ {
+ /* only read frame if not already in */
+ if (wkc2 <= EC_NOFRAME)
+ wkc2 = ecx_inframe(port, idx, 1);
+ }
+ /* wait for both frames to arrive or timeout */
+ } while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (osal_timer_is_expired(&timer) == FALSE));
+
+ /* only do redundant functions when in redundant mode */
+ if (port->redstate != ECT_RED_NONE)
+ {
+ /* primrx if the received MAC source on primary socket */
+ primrx = 0;
+ if (wkc > EC_NOFRAME)
+ {
+ primrx = port->rxsa[idx];
+ }
+ /* secrx if the received MAC source on psecondary socket */
+ secrx = 0;
+ if (wkc2 > EC_NOFRAME)
+ {
+ secrx = port->redport->rxsa[idx];
+ }
+ /* primary socket got secondary frame and secondary socket got primary frame */
+ /* normal situation in redundant mode */
+ if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
+ {
+ /* copy secondary buffer to primary */
+ memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
+ wkc = wkc2;
+ }
+ /* primary socket got nothing or primary frame, and secondary socket got secondary frame */
+ /* we need to resend TX packet */
+ if ( ((primrx == 0) && (secrx == RX_SEC)) ||
+ ((primrx == RX_PRIM) && (secrx == RX_SEC)) )
+ {
+ osal_timert read_timer;
+
+ /* If both primary and secondary have partial connection retransmit the primary received
+ * frame over the secondary socket. The result from the secondary received frame is a combined
+ * frame that traversed all slaves in standard order. */
+ if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
+ {
+ /* copy primary rx to tx buffer */
+ memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
+ }
+
+ //osal_timer_start(&read_timer, EC_TIMEOUTRET);
+
+ osal_timer_start(&read_timer, 500);
+
+ /* resend secondary tx */
+ ecx_outframe(port, idx, 1);
+ do
+ {
+ /* retrieve frame */
+ wkc2 = ecx_inframe(port, idx, 1);
+ } while ((wkc2 <= EC_NOFRAME) && (osal_timer_is_expired(&read_timer) == FALSE));
+ if (wkc2 > EC_NOFRAME)
+ {
+ /* copy secondary result to primary rx buffer */
+ memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
+ wkc = wkc2;
+ }
+ }
+ }
+
+ /* return WKC or EC_NOFRAME */
+ return wkc;
+}
+
+/** Blocking receive frame function. Calls ec_waitinframe_red().
+ * @param[in] port = port context struct
+ * @param[in] idx = requested index of frame
+ * @param[in] timeout = timeout in us
+ * @return Workcounter if a frame is found with corresponding index, otherwise
+ * EC_NOFRAME.
+ */
+int ecx_waitinframe(ecx_portt *port, int idx, int timeout)
+{
+ int wkc;
+
+ osal_timert timer;
+ osal_timer_start (&timer, timeout);
+ wkc = ecx_waitinframe_red(port, idx, timer);
+
+ return wkc;
+}
+
+/** Blocking send and receive frame function. Used for non processdata frames.
+ * A datagram is build into a frame and transmitted via this function. It waits
+ * for an answer and returns the workcounter. The function retries if time is
+ * left and the result is WKC=0 or no frame received.
+ *
+ * The function calls ec_outframe_red() and ec_waitinframe_red().
+ *
+ * @param[in] port = port context struct
+ * @param[in] idx = index of frame
+ * @param[in] timeout = timeout in us
+ * @return Workcounter or EC_NOFRAME
+ */
+int ecx_srconfirm(ecx_portt *port, int idx, int timeout)
+{
+ int wkc = EC_NOFRAME;
+
+ osal_timert timer;
+ osal_timer_start(&timer, timeout);
+
+ do
+ {
+ osal_timert read_timer;
+
+ /* tx frame on primary and if in redundant mode a dummy on secondary */
+ ecx_outframe_red(port, idx);
+ osal_timer_start(&read_timer, MIN(timeout, EC_TIMEOUTRET));
+
+ /* get frame from primary or if in redundant mode possibly from secondary */
+ wkc = ecx_waitinframe_red(port, idx, read_timer);
+ /* wait for answer with WKC>0 or otherwise retry until timeout */
+ } while ((wkc <= EC_NOFRAME) && (osal_timer_is_expired(&timer) == FALSE));
+
+ return wkc;
+}
+
+
+#ifdef EC_VER1
+int ec_setupnic(const char *ifname, int secondary)
+{
+ return ecx_setupnic(&ecx_port, ifname, secondary);
+}
+
+int ec_closenic(void)
+{
+ return ecx_closenic(&ecx_port);
+}
+
+int ec_getindex(void)
+{
+ return ecx_getindex(&ecx_port);
+}
+
+void ec_setbufstat(int idx, int bufstat)
+{
+ ecx_setbufstat(&ecx_port, idx, bufstat);
+}
+
+int ec_outframe(int idx, int stacknumber)
+{
+ return ecx_outframe(&ecx_port, idx, stacknumber);
+}
+
+int ec_outframe_red(int idx)
+{
+ return ecx_outframe_red(&ecx_port, idx);
+}
+
+int ec_inframe(int idx, int stacknumber)
+{
+ return ecx_inframe(&ecx_port, idx, stacknumber);
+}
+
+int ec_waitinframe(int idx, int timeout)
+{
+ return ecx_waitinframe(&ecx_port, idx, timeout);
+}
+
+int ec_srconfirm(int idx, int timeout)
+{
+ return ecx_srconfirm(&ecx_port, idx, timeout);
+}
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/nicdrv.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,110 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for nicdrv.c
+ */
+
+#ifndef _nicdrvh_
+#define _nicdrvh_
+
+/** pointer structure to Tx and Rx stacks */
+typedef struct
+{
+ /** socket connection used */
+ int *sock;
+ /** tx buffer */
+ ec_bufT (*txbuf)[EC_MAXBUF];
+ /** tx buffer lengths */
+ int (*txbuflength)[EC_MAXBUF];
+ /** temporary receive buffer */
+ ec_bufT *tempbuf;
+ /** rx buffers */
+ ec_bufT (*rxbuf)[EC_MAXBUF];
+ /** rx buffer status fields */
+ int (*rxbufstat)[EC_MAXBUF];
+ /** received MAC source address (middle word) */
+ int (*rxsa)[EC_MAXBUF];
+} ec_stackT;
+
+/** pointer structure to buffers for redundant port */
+typedef struct
+{
+ ec_stackT stack;
+ int sockhandle;
+ /** rx buffers */
+ ec_bufT rxbuf[EC_MAXBUF];
+ /** rx buffer status */
+ int rxbufstat[EC_MAXBUF];
+ /** rx MAC source address */
+ int rxsa[EC_MAXBUF];
+ /** temporary rx buffer */
+ ec_bufT tempinbuf;
+} ecx_redportt;
+
+/** pointer structure to buffers, vars and mutexes for port instantiation */
+typedef struct
+{
+ ec_stackT stack;
+ int sockhandle;
+ /** rx buffers */
+ ec_bufT rxbuf[EC_MAXBUF];
+ /** rx buffer status */
+ int rxbufstat[EC_MAXBUF];
+ /** rx MAC source address */
+ int rxsa[EC_MAXBUF];
+ /** temporary rx buffer */
+ ec_bufT tempinbuf;
+ /** temporary rx buffer status */
+ int tempinbufs;
+ /** transmit buffers */
+ ec_bufT txbuf[EC_MAXBUF];
+ /** transmit buffer lengths */
+ int txbuflength[EC_MAXBUF];
+ /** temporary tx buffer */
+ ec_bufT txbuf2;
+ /** temporary tx buffer length */
+ int txbuflength2;
+ /** last used frame index */
+ int lastidx;
+ /** current redundancy state */
+ int redstate;
+ /** pointer to redundancy port and buffers */
+ ecx_redportt *redport;
+ //mtx_t * getindex_mutex; //******//
+ //mtx_t * tx_mutex; //******//
+ //mtx_t * rx_mutex; //******//
+} ecx_portt;
+
+extern const uint16 priMAC[3];
+extern const uint16 secMAC[3];
+
+#ifdef EC_VER1
+extern ecx_portt ecx_port;
+extern ecx_redportt ecx_redport;
+
+int ec_setupnic(const char * ifname, int secondary);
+int ec_closenic(void);
+void ec_setbufstat(int idx, int bufstat);
+int ec_getindex(void);
+int ec_outframe(int idx, int stacknumber);
+int ec_outframe_red(int idx);
+int ec_waitinframe(int idx, int timeout);
+int ec_srconfirm(int idx,int timeout);
+#endif
+
+void ec_setupheader(void *p);
+
+int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
+int ecx_closenic(ecx_portt *port);
+void ecx_setbufstat(ecx_portt *port, int idx, int bufstat);
+int ecx_getindex(ecx_portt *port);
+int ecx_outframe(ecx_portt *port, int idx, int stacknumber);
+int ecx_outframe_red(ecx_portt *port, int idx);
+int ecx_waitinframe(ecx_portt *port, int idx, int timeout);
+int ecx_srconfirm(ecx_portt *port, int idx,int timeout);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/oshw.cpp Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,56 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+#include "oshw.h"
+#include <stdlib.h>
+#include "lwip/def.h"
+
+
+/**
+ * Host to Network byte order (i.e. to big endian).
+ *
+ * Note that Ethercat uses little endian byte order, except for the Ethernet
+ * header which is big endian as usual.
+ */
+
+
+
+uint16 oshw_htons(const uint16 host)
+{
+ return htons (host);
+}
+
+
+/**
+ * Network (i.e. big endian) to Host byte order.
+ *
+ * Note that Ethercat uses little endian byte order, except for the Ethernet
+ * header which is big endian as usual.
+ */
+uint16 oshw_ntohs(const uint16 network)
+{
+ return ntohs (network);
+}
+
+/* Create list over available network adapters.
+ * @return First element in linked list of adapters
+ */
+ec_adaptert * oshw_find_adapters(void)
+{
+ ec_adaptert * ret_adapter = NULL;
+
+ /* TODO if needed */
+
+ return ret_adapter;
+}
+
+/** Free memory allocated memory used by adapter collection.
+ * @param[in] adapter = First element in linked list of adapters
+ * EC_NOFRAME.
+ */
+void oshw_free_adapters(ec_adaptert * adapter)
+{
+ /* TODO if needed */
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SOEM/oshw/oshw.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,34 @@
+/*
+ * Licensed under the GNU General Public License version 2 with exceptions. See
+ * LICENSE file in the project root for full license information
+ */
+
+/** \file
+ * \brief
+ * Headerfile for oshw.c
+ */
+
+#ifndef _oshw_
+#define _oshw_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ethercattype.h"
+#include "nicdrv.h"
+#include "ethercatmain.h"
+
+
+uint16 oshw_htons(uint16 host);
+uint16 oshw_ntohs(uint16 network);
+
+ec_adaptert * oshw_find_adapters(void);
+void oshw_free_adapters(ec_adaptert * adapter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SPI_TFT_ILI9341.lib Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/teams/EasyCAT-master/code/SPI_TFT_ILI9341/#2e5c5943b3fd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TFT_fonts.lib Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/dreschpe/code/TFT_fonts/#76774250fcec
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/config.h Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,144 @@
+#ifndef config_H
+#define config_H
+
+#include "ethercat.h"
+
+
+//------------------------------------------------------------------------------
+
+#define LAB_1 1
+#define LAB_2 2
+
+/*
+#define EK1100 3
+#define EL2004 4
+#define EL1014 5
+*/
+
+#define SLAVE_NUM 2
+//#define SLAVE_NUM 5
+
+//------------------------------------------------------------------------------
+
+
+#define IO_MAP_SIZE 256
+
+
+
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+ uint8_t Alarm;
+}out_LAB_1t;
+
+typedef struct __attribute__((__packed__))
+{
+ float Temperature;
+}in_LAB_1t;
+
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+ uint8_t Segments;
+}out_LAB_2t;
+
+typedef struct __attribute__((__packed__))
+{
+ uint16_t Potentiometer;
+ uint8_t Buttons;
+}in_LAB_2t;
+
+/*
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+ uint8_t Out_0:1;
+ uint8_t Out_1:1;
+ uint8_t Out_2:1;
+ uint8_t Out_3:1;
+ uint8_t PAD:4;
+}out_EL2004t;
+
+//------------------------------------------------------------------------------
+
+typedef struct __attribute__((__packed__))
+{
+ uint8_t In_0:1;
+ uint8_t In_1:1;
+ uint8_t In_2:1;
+ uint8_t In_3:1;
+ uint8_t PAD:4;
+}in_EL1014t;
+
+
+//------------------------------------------------------------------------------
+*/
+
+out_LAB_1t *out_LAB_1;
+in_LAB_1t *in_LAB_1;
+
+out_LAB_2t *out_LAB_2;
+in_LAB_2t *in_LAB_2;
+
+/*
+out_EL2004t *out_EL2004;
+
+in_EL1014t *in_EL1014;
+*/
+
+//------------------------------------------------------------------------------
+
+char IOmap[IO_MAP_SIZE];
+char IOmapSafe[IO_MAP_SIZE];
+
+
+void MapLocalStructures (void)
+{
+ out_LAB_1 = (out_LAB_1t*)((char *)ec_slave[LAB_1].outputs - &IOmap[0] + &IOmapSafe[0]);
+ in_LAB_1 = (in_LAB_1t*)((char *)ec_slave[LAB_1].inputs - &IOmap[0] + &IOmapSafe[0]);
+
+ out_LAB_2 = (out_LAB_2t*)((char *)ec_slave[LAB_2].outputs - &IOmap[0] + &IOmapSafe[0]);
+ in_LAB_2 = (in_LAB_2t*)((char *)ec_slave[LAB_2].inputs - &IOmap[0] + &IOmapSafe[0]);
+
+/*
+ out_EL2004 = (out_EL2004t*)((char *)ec_slave[EL2004].outputs - &IOmap[0] + &IOmapSafe[0]);
+
+ in_EL1014 = (in_EL1014t*)((char *)ec_slave[EL1014].inputs - &IOmap[0] + &IOmapSafe[0]);
+*/
+}
+
+
+
+//------------------------------------------------------------------------------
+
+uint32_t network_configuration(void)
+{
+
+ if (ec_slavecount != SLAVE_NUM) // check if the number of slaves matches what we expect
+ return 0;
+
+ if (strcmp(ec_slave[LAB_1].name,"LAB_1")) // verify slave by slave that the slave names are correct
+ return 0;
+
+ else if (strcmp(ec_slave[LAB_2].name,"LAB_2"))
+ return 0;
+
+/*
+ else if (strcmp(ec_slave[EK1100].name,"EK1100"))
+ return 0;
+
+ else if (strcmp(ec_slave[EL2004].name,"EL2004"))
+ return 0;
+
+ else if (strcmp(ec_slave[EL1014].name,"EL1014"))
+ return 0;
+*/
+
+ return 1;
+}
+
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,1525 @@
+//********************************************************************************************
+// *
+// This software is distributed as an example, "AS IS", in the hope that it could *
+// be useful, WITHOUT ANY WARRANTY of any kind, express or implied, included, but *
+// not limited, to the warranties of merchantability, fitness for a particular *
+// purpose, and non infringiment. In no event shall the authors be liable for any *
+// claim, damages or other liability, arising from, or in connection with this software. *
+// *
+//********************************************************************************************
+
+
+// The AB&T EasyCAT LAB is a complete experimental EtherCAT® system, composed by
+// one master and two slaves.
+// The EasyCAT LAB software is provided free of charge and its pourpose is to allow
+// makers and educational institutes to experiment with the EtherCAT® protocol.
+//
+// The EasyCAT LAB is developed by "AB&T Tecnologie Informatiche" Via dell'About 2A Ivrea Italy.
+// www.bausano.net
+// www.easycatshield.com
+//
+// The EasyCAT LAB uses the SOEM library by rt:labs
+// https://rt-labs.com/products/soem-ethercat-master-stack/
+//
+// EtherCAT® is a registered trademark and patented technology, licensed by Beckhoff Automation GmbH.
+// www.beckhoff.com
+// www.ethercat.org
+
+
+#define ETH_TXBUFNB 16
+#define ETH_RXBUFNB 16
+
+#include "mbed.h"
+
+#ifndef __align
+#define __align MBED_ALIGN
+#endif
+
+#include "config.h"
+#include "SPI_TFT_ILI9341.h"
+#include "Arial12x12.h"
+#include "Arial24x23.h"
+#include "Arial28x28.h"
+#include "font_big.h"
+#include "soem_start.h"
+
+#define CYCLE_TIME 1000 // master cycle time in uS
+ // 1000 = 1mS
+
+
+#define SysMilliS() (uint32_t)Kernel::get_ms_count()
+
+Serial pc(USBTX,USBRX,115200); // set the debug serial line speed to 115200
+
+
+//---- TFT with resistive touchscreen pins -------------------------------------
+
+// the display used is the SeeedStudio 2.8 inch TFT v2.0
+// http://wiki.seeedstudio.com/2.8inch_TFT_Touch_Shield_v2.0/
+
+#define PIN_YP A3 // resistive touchscreen
+#define PIN_YM A1 //
+#define PIN_XM A2 //
+#define PIN_XP A0 //
+
+#define PIN_MOSI D11 // TFT display SPI
+#define PIN_MISO D12 //
+#define PIN_SCLK D13 //
+#define PIN_CS_TFT D5 //
+#define PIN_DC_TFT D6 //
+
+
+//---- touchscreen parameters --------------------------------------------------
+
+#define TOUCH_SAMPLES 8
+#define TOUCH_WINDOW 0.05
+#define TOUCH_THRESHOLD 0.2
+#define TOUCH_MAX_ROUNDS 16
+#define TIME_TOUCH_RELEASED 300
+
+#define TOUCH_X_OFFSET 0.118
+#define TOUCH_X_GAIN 402
+
+#define TOUCH_Y_OFFSET 0.090
+#define TOUCH_Y_GAIN 302
+
+
+//---- side menu parameters ----------------------------------------------------
+
+#define MENU_Y 0
+#define MENU_WIDTH 83
+#define MENU_HEIGHT 42
+#define MENU_X (319-MENU_WIDTH)
+
+
+//---- slave parameters - LAB_1 EasyCAT with multifunction shield --------------
+
+#define TERMO_X 55
+#define TERMO_Y 185
+
+#define ALARM_X 140
+#define ALARM_Y 0
+#define ALARM_WIDTH 83
+#define ALARM_HEIGHT 42
+
+#define TIME_BLINK 1000
+#define TIME_AUTO_REP_START 1000
+#define TIME_AUTO_REP_REPEAT 200
+
+
+//---- slave parameters - LAB_2 EasyCAT with multifunction shield --------------
+
+#define SEG_X 0
+#define SEG_Y 0
+#define SEG_WIDTH 42
+#define SEG_HEIGHT 16
+#define SEG_STEP 60
+
+#define BUTTONS_X 38
+#define BUTTONS_Y 80
+#define BUTTONS_WIDTH 26
+#define BUTTONS_R 3
+#define BUTTONS_STEP 60
+
+#define ANALOG_X 0
+#define ANALOG_Y 120
+#define ANALOG_WIDTH 222
+#define ANALOG_HEIGHT 80
+
+#define TIME_REP_SEG 300
+#define TIME_POTENTIOMETER 100
+
+/*
+//---- slave 3 parameters - EL2004 ---------------------------------------------
+
+#define OUT_X 0
+#define OUT_Y 65
+#define OUT_WIDTH 42
+#define OUT_HEIGHT 42
+#define OUT_STEP 60
+
+
+//---- slave 4 parameters - EL1014 ---------------------------------------------
+
+#define INPUTS_X 12
+#define INPUTS_Y 84
+#define INPUTS_WIDTH 26
+#define INPUTS_R 3
+#define INPUTS_STEP 60
+*/
+
+
+//---- local functions ---------------------------------------------------------
+
+void DrawBanner();
+void DrawSlaveFixedParts();
+void DrawSideMenu (uint8_t Slave);
+
+void DrawTemperatureValue(float fValue);
+void DrawAlarmSettings(float fThreshold, bool OnOff, bool MinMax);
+void DrawOnlyThreshold(float fThreshold, bool OnOff, bool MinMax);
+void DrawAlarmStatus(bool Alarm);
+void DisplayInRect(int X, int Y, int X_off, int Y_off, char* Value, int BackColor, unsigned char* Font);
+
+void DrawButtonsValue(uint8_t Value);
+void DrawSegmentsValue(uint8_t Value);
+void DrawPotentiometerValue(uint16_t PotValue);
+
+void DrawOutputsValue(uint8_t Value);
+
+void DrawInputsValue(uint8_t Value);
+
+
+
+float ReadAnalog(AnalogIn Ana);
+
+
+void Application();
+
+void TouchScreenManagement();
+bool TouchRead(uint16_t* X, uint16_t* Y);
+uint16_t TouchRead_X();
+uint16_t TouchRead_Y();
+bool TouchRead_Z();
+
+
+
+//---- global variables --------------------------------------------------------
+
+
+bool TouchWasReleased;
+
+bool FirstRound;
+
+//------------------------------------------------------------------------------
+
+SPI_TFT_ILI9341 TFT(PIN_MOSI, PIN_MISO, PIN_SCLK, PIN_CS_TFT, NC, PIN_DC_TFT);
+
+Ticker SampleTicker;
+Thread thread;
+
+DigitalOut Test_1(D1); // debug test points
+DigitalOut Test_2(D2); //
+DigitalOut Test_3(D3); //
+DigitalOut Test_4(D4); //
+
+
+//------------------------------------------------------------------------------
+
+float fAlarmThreshold;
+bool AlarmOnOff;
+bool AlarmMinMax;
+
+
+uint16_t PotValue;
+uint16_t PrevPotValue;
+uint8_t Buttons;
+uint8_t PrevButtons;
+uint8_t Segments;
+uint8_t PrevSegments;
+int16_t Graph_x;
+
+uint8_t Outputs;
+uint8_t PrevOutputs;
+
+uint8_t Inputs;
+uint8_t PrevInputs;
+
+
+int8_t VisuSlave;
+
+
+//------------------------------------------------------------------------------
+
+uint32_t Time;
+uint32_t TimeBlink;
+uint32_t TimeAutoRepeat;
+uint32_t TimeAutoRepRepeat;
+uint32_t TimePotentiometer;
+uint32_t TimeTouchReleased;
+
+bool Blink;
+
+uint8_t TouchAxes = 0;
+uint8_t Action = 0;
+
+uint16_t X;
+uint16_t Y;
+
+float fTemperature;
+float static fPrevTemperature;
+
+int ExpectWorkCounter;
+int WorkCounter;
+int WorkCounterSafe;
+bool NetworkError;
+bool NetworkErrorSafe;
+
+#define DATA_EXCHANGE_FLAG (1UL << 0)
+#define APPLICATION_FLAG (1UL << 1)
+
+EventFlags event_flags;
+
+Mutex IO_data;
+
+
+//---- data exchange thread ----------------------------------------------------
+
+void ExchangeMaster()
+{
+ while (true)
+ {
+
+
+ event_flags.wait_any(DATA_EXCHANGE_FLAG); // the thread waits for the synchronization flag
+
+ //Test_1 = 1;
+
+ IO_data.lock(); // Ethercat data exchange
+ ec_send_processdata(); //
+ WorkCounter = ec_receive_processdata(EC_TIMEOUTRET);
+
+ if (WorkCounter != ExpectWorkCounter)
+ NetworkError = true;
+ else
+ NetworkError = false;
+
+ IO_data.unlock(); //
+ event_flags.set(APPLICATION_FLAG); // synchronize the application
+
+ //Test_1 = 0;
+ }
+}
+
+
+//----- thicker generated sample time ------------------------------------------
+
+void SampleIsr() // set the event that starts
+{ // the data exchange
+ event_flags.set(DATA_EXCHANGE_FLAG); //
+} //
+
+
+//****** initialization ********************************************************
+
+int main()
+{
+ int i;
+
+ printf("Start \n");
+
+ Test_1 = 0;
+ Test_2 = 0;
+ Test_3 = 0;
+ Test_4 = 0;
+
+ TFT.background(Black); // init TFT
+ TFT.cls(); //
+ TFT.set_orientation(3); //
+
+ DrawBanner();
+
+ NetworkError = false;
+ VisuSlave = LAB_1;
+
+ AlarmOnOff = true;
+ AlarmMinMax = false;
+ fAlarmThreshold = 28.8;
+ fTemperature = 0;
+ TouchWasReleased = true;
+
+
+ if (ec_init(NULL)) // init SOEM
+ {
+ printf("ec_init succeeded.\n");
+ printf("Scanning the network\n");
+
+ TFT.cls();
+
+ TFT.set_font((unsigned char*) Arial12x12);
+ TFT.foreground(Green);
+ TFT.locate(0, 0);
+
+ TFT.printf("Scanning the network\n");
+
+ if (network_scanning())
+ {
+ if (network_configuration()) // check network configuration
+ {
+ ec_config_map(&IOmap); // map the I/O
+ MapLocalStructures();
+
+ printf("\nSlaves mapped, state to SAFE_OP.\n");
+ // wait for all slaves to reach SAFE_OP state
+ ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE);
+
+ printf("Request operational state for all slaves\n");
+ ec_slave[0].state = EC_STATE_OPERATIONAL;
+
+ ec_send_processdata(); // send one valid process data to make outputs in slaves happy
+ ExpectWorkCounter = ec_receive_processdata(EC_TIMEOUTRET);
+
+ ec_writestate(0); // request OP state for all slaves
+
+ // wait for all slaves to reach OP state
+ ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);
+ if (ec_slave[0].state == EC_STATE_OPERATIONAL )
+ {
+ printf("Operational state reached for all slaves.\n");
+ }
+ else
+ {
+ printf("Not all slaves reached operational state.\n");
+ ec_readstate();
+ for(i = 1; i<=ec_slavecount ; i++)
+ {
+ if(ec_slave[i].state != EC_STATE_OPERATIONAL)
+ {
+ printf("Slave %d State=0x%04x StatusCode=0x%04x\n",
+ i, ec_slave[i].state, ec_slave[i].ALstatuscode);
+ }
+ }
+
+ TFT.foreground(Red);
+ TFT.locate(0, 0);
+ TFT.printf("Not all slaves reached operational state!");
+ while(1){}
+ }
+
+ DrawSlaveFixedParts();
+
+ thread.start(ExchangeMaster);
+ thread.set_priority(osPriorityRealtime);
+
+ SampleTicker.attach_us(&SampleIsr, CYCLE_TIME);
+
+ Application();
+ }
+
+ else
+ {
+ printf("Mismatch of network units!\n");
+ TFT.foreground(Red);
+ TFT.locate(0, 0);
+ TFT.printf("Mismatch of network units!");
+
+ while(1){}
+ }
+ }
+
+ else
+ {
+ printf("No slaves found!\n");
+ TFT.foreground(Red);
+ TFT.printf("No slaves found!");
+
+ while(1){}
+ }
+ }
+ else
+ {
+ printf("Ethernet interface init failed!");
+ TFT.foreground(Red);
+ TFT.locate(0, 0);
+ TFT.printf("Ethernet interface init failed!");
+ while(1){}
+ }
+}
+
+
+//****** user master application **********************************************
+
+void Application()
+{
+
+ while(1)
+ {
+ event_flags.wait_any(APPLICATION_FLAG); // the application waits for the synchronization flag
+
+ //Test_2 = 1;
+
+ IO_data.lock(); // copy the Ethercat data to a safe buffer
+ memcpy(&IOmapSafe[0], &IOmap[0], IO_MAP_SIZE); //
+ //
+ if (NetworkError) //
+ { //
+ NetworkErrorSafe = NetworkError; //
+ WorkCounterSafe = WorkCounter; //
+ } //
+ IO_data.unlock(); //
+
+ if (NetworkErrorSafe)
+ {
+ TFT.rect(35,50, 285, 182, Red);
+ TFT.fillrect(36,51, 284, 181, Black);
+ TFT.foreground(Red);
+ TFT.set_font((unsigned char*) Arial28x28);
+ TFT.locate(58, 65);
+ TFT.printf("Network error!");
+ printf("Network error!\n");
+ TFT.foreground(Magenta);
+ TFT.set_font((unsigned char*) Arial12x12);
+ TFT.locate(58, 106);
+
+ if(WorkCounterSafe >= 0)
+ {
+ TFT.printf("Expected working counter %d", ExpectWorkCounter);
+ TFT.locate(58, 118);
+ TFT.printf("Actual working counter %d", WorkCounterSafe);
+ printf("Expected working counter %d\n", ExpectWorkCounter);
+ printf("Actual working counter %d\n", WorkCounterSafe);
+ }
+ else
+ {
+ TFT.printf("Timeout");
+ printf("Timeout\n");
+ }
+
+ TFT.foreground(Green);
+ TFT.locate(58, 142);
+ TFT.printf("Please fix the error and");
+ TFT.locate(58, 154);
+ TFT.printf("press the reset button");
+ printf("Please fix the error and press the reset button \n");
+
+ SampleTicker.detach(); // stop the sample interrupt
+ while(1){} // and loop for ever
+ }
+
+ //----- slave 1 data management ------------
+
+ fTemperature = in_LAB_1->Temperature; // read the temperature
+
+ if (fTemperature != fPrevTemperature) // check if the temperature has changed
+ {
+ fPrevTemperature = fTemperature; // remember the current temperature value
+
+ if (VisuSlave == LAB_1) // if the HMI is setted to slave 1
+ { // visualize it
+ DrawTemperatureValue (fTemperature); //
+ }
+ }
+
+ bool AlarmStatus;
+ if (AlarmOnOff) // check if we are in alarm
+ { //
+ if ((AlarmMinMax && (fTemperature < fAlarmThreshold)) || (!AlarmMinMax && (fTemperature > fAlarmThreshold)))
+ {
+ out_LAB_1->Alarm = 0x01; // signal the alarm condition to the slave
+ AlarmStatus = true; // and to remember it
+ }
+ else
+ {
+ out_LAB_1->Alarm = 0x00; // signal the no alarm condition to the slave
+ AlarmStatus = false; // and remember it
+ }
+ }
+ else
+ {
+ out_LAB_1->Alarm = 0x00; // signal the no alarm condition to the slave
+ AlarmStatus = false; // and remember it
+ }
+
+ if (VisuSlave == LAB_1) // if the HMI is set to slave 1
+ { //
+ DrawAlarmStatus(AlarmStatus); // update the alarm status on the TFT
+ }
+
+ if (VisuSlave == LAB_1 && FirstRound) // if the HMI is set to slave 1
+ { // and it is the first time
+ FirstRound = false; //
+ DrawTemperatureValue (fTemperature); // draw the current temperature value
+ DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax); // draw the alarm settings
+ }
+
+ //----- end LAB_1 ------------------------
+
+
+ //----- slave 2 data management ------------
+
+ PotValue = in_LAB_2->Potentiometer; // read the potentiometer value
+
+ if (VisuSlave == LAB_2) // if the HMI is setted to slave 2
+ { //
+ Time = SysMilliS(); // and the visualization timer is elapsed
+ if (Time-TimePotentiometer > TIME_POTENTIOMETER) //
+ { // draw the potentiometer value
+ TimePotentiometer = Time; //
+ DrawPotentiometerValue(PotValue); //
+ } //
+ }
+
+ Buttons = in_LAB_2->Buttons; // read the buttons status from the slave
+
+ if (Buttons != PrevButtons) // check if the buttons value has changed
+ {
+ PrevButtons = Buttons; // remember the current buttons value
+
+ if (VisuSlave == LAB_2) // if the HMI is setted to slave 2
+ { //
+ DrawButtonsValue(Buttons); // draw the current buttons value
+ } //
+ }
+
+ if (Segments != PrevSegments) // check if the segments value has changed
+ {
+ PrevSegments = Segments; // remember the current segments value
+
+ if (VisuSlave == LAB_2) // if the HMI is setted to slave 2
+ { //
+ DrawSegmentsValue(Segments); // draw the current segments value
+ } //
+ }
+
+ if (VisuSlave == LAB_2 && FirstRound) // if the HMI is set to slave 2
+ { // and it is the first time
+ FirstRound = false; //
+ //
+ DrawButtonsValue(Buttons); // draw the current buttons value
+ DrawSegmentsValue(Segments); // draw the current segments value
+ }
+
+ out_LAB_2->Segments = Segments; // send the segments status to the slave
+
+
+ //----- end LAB_2 ------------------------
+
+
+ /*
+ //----- slave 3 EL2004 data management -----
+
+ if (Outputs != PrevOutputs) // check if the outputs value has changed
+ {
+ PrevOutputs = Outputs; // remember the current outputs value
+
+ if (VisuSlave == EL2004) // if the HMI is setted to slave 3
+ { //
+ DrawOutputsValue(Outputs); // draw the current Outputs value
+ } //
+ }
+
+ if (VisuSlave == EL2004 && FirstRound) // if the HMI is set to slave 3
+ { // and it is the first time
+ FirstRound = false; //
+ //
+ DrawOutputsValue(Outputs); // draw the current Outputs value
+ }
+
+ if (Outputs & 0x01) // send the Outputs status to the slave
+ out_EL2004->Out_0 = 1; //
+ else //
+ out_EL2004->Out_0 = 0; //
+ if (Outputs & 0x02) //
+ out_EL2004->Out_1 = 1; //
+ else //
+ out_EL2004->Out_1 = 0; //
+ if (Outputs & 0x04) //
+ out_EL2004->Out_2 = 1; //
+ else //
+ out_EL2004->Out_2 = 0; //
+ if (Outputs & 0x08) //
+ out_EL2004->Out_3 = 1; //
+ else //
+ out_EL2004->Out_3 = 0; //
+
+ //----- end EL2004 -----------------------
+
+
+ //----- slave 4 EL1014 data management -----
+ Inputs = 0;
+ if (in_EL1014->In_0) // read the inputs status from the slave
+ Inputs |= 0x01; //
+ if (in_EL1014->In_1) //
+ Inputs |= 0x02; //
+ if (in_EL1014->In_2) //
+ Inputs |= 0x04; //
+ if (in_EL1014->In_3) //
+ Inputs |= 0x08; //
+
+ if (Inputs != PrevInputs) // check if the inputs value has changed
+ {
+ PrevInputs = Inputs; // remember the current inputs value
+
+ if (VisuSlave == EL1014) // if the HMI is setted to slave 4
+ { //
+ DrawInputsValue(Inputs); // draw the current inputs value
+ } //
+ }
+ //----- end EL1014 -----------------------
+ */
+
+ TouchScreenManagement(); // check if the touchscreen is tapped
+ // and handle it
+
+ Time = SysMilliS(); // toggle the variable Blink every
+ if ((Time-TimeBlink) > TIME_BLINK) // TIME_BLINK mS
+ { //
+ TimeBlink = Time; // we use it to blink a field on the TFT
+ Blink = !Blink; //
+ }
+
+ IO_data.lock(); // copy the IO data from the safe area
+ memcpy(&IOmap[0], &IOmapSafe[0], IO_MAP_SIZE); // to the EtherCAT buffer
+ IO_data.unlock(); //
+
+ //Test_2 = 0;
+ }
+}
+
+
+//******************************************************************************
+
+
+
+//******* general functions ****************************************************
+
+
+//------ touchscreen management ------------------------------------------------
+
+void TouchScreenManagement()
+{
+ uint16_t X;
+ uint16_t Y;
+
+ int i;
+
+
+ if (TouchRead(&X, &Y)) // the touchscreen has been tapped
+ {
+ TimeTouchReleased = SysMilliS();
+ // check if it is the side menu
+ // decrement slave button
+ if ((X>MENU_X) && (X>MENU_X+MENU_WIDTH/2) && (Y>MENU_HEIGHT) && (Y<MENU_HEIGHT*2))
+ { //
+ VisuSlave--;
+
+ if (VisuSlave == 0) //
+ VisuSlave = SLAVE_NUM; //
+ // the visualized slave has changed
+ DrawSlaveFixedParts(); // draw the new slave fixed parts
+ } //
+
+ // check if it is the side menu
+ // increment slave button
+ if ((X>MENU_X) && (X<MENU_X+MENU_WIDTH/2) && (Y>MENU_HEIGHT) && (Y<MENU_HEIGHT*2))
+ { //
+ VisuSlave++;
+
+ if (VisuSlave > SLAVE_NUM) //
+ VisuSlave = LAB_1; //
+ // the visualized slave has changed
+ DrawSlaveFixedParts(); // draw the new slave fixed parts
+ } //
+
+ switch (VisuSlave) // check which slave is visualized on the TFT
+ {
+ case (LAB_1): //-------------- slave 1 -----------------------
+
+
+ if(TouchWasReleased) // first check if the touch was
+ { // not tapped in the previous rounds
+ TouchWasReleased = false; // because for the following fields
+ TimeTouchReleased = SysMilliS(); // we don't want autorepeat
+
+ TimeAutoRepeat = SysMilliS(); // reload the autorepeat time
+
+ // handle taps on the ">" and "<" buttons
+ if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y+ALARM_HEIGHT) && (Y<ALARM_Y+ALARM_HEIGHT*2))
+ { //
+ AlarmMinMax = true; //
+ DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ } //
+ if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y+ALARM_HEIGHT) && (Y<ALARM_Y+ALARM_HEIGHT*2)) //
+ { //
+ AlarmMinMax = false; //
+ DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ }
+
+ // handle taps on the "ON" and "OFF" buttons
+ if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y) && (Y<ALARM_Y+ALARM_HEIGHT))
+ { //
+ AlarmOnOff = true; //
+ DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ } //
+ if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y) && (Y<ALARM_Y+ALARM_HEIGHT))
+ { //
+ AlarmOnOff = false; //
+ DrawAlarmSettings(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ }
+
+ // handle taps on the "+" and "-" buttons
+ // here we don't use the autorepeat to increment
+ // or decrement the threshold by 0.1
+ if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))
+ { //
+ fAlarmThreshold += 0.1; //
+ DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ } //
+ if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))
+ { //
+ fAlarmThreshold -= 0.1; //
+ DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ } //
+ }
+
+ else // autorepeat management
+ { //
+ Time = SysMilliS(); //
+ if (Time-TimeAutoRepeat > TIME_AUTO_REP_START) //
+ { //
+ if (Time-TimeAutoRepRepeat > TIME_AUTO_REP_REPEAT)
+ { //
+ TimeAutoRepRepeat = Time; //
+
+ // handle taps on the "+" and "-" buttons
+ // here we use the autorepeat to increment
+ // or decrement the threshold by 1
+ if ((X>ALARM_X) && (X<ALARM_X+(ALARM_WIDTH/2)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))
+ { //
+ fAlarmThreshold += 1; //
+ DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ } //
+ if ((X>ALARM_X+(ALARM_WIDTH/2)) && (X<ALARM_X+(ALARM_WIDTH)) && (Y>ALARM_Y+(ALARM_HEIGHT*3)) && (Y<ALARM_Y+(ALARM_HEIGHT*4)))
+ { //
+ fAlarmThreshold -= 1; //
+ DrawOnlyThreshold(fAlarmThreshold, AlarmOnOff, AlarmMinMax);
+ }
+ }
+ }
+ }
+
+ break; //-------------- end slave 1 -------------------
+
+
+
+ case (LAB_2): //-------------- slave 2 -----------------------
+
+ if(TouchWasReleased) // first check if the touch was
+ { // not tapped in the previous rounds
+ TouchWasReleased = false; // because for the following fields
+ TimeTouchReleased = SysMilliS(); // we don't want autorepeat
+
+ uint8_t Mask = 0x08; // check if one of the segment is tapped
+ //
+ for (i=0; i<4; i++) //
+ { //
+ if ((X>SEG_X+(i*SEG_STEP)) && (X<SEG_X+(i*SEG_STEP)+SEG_WIDTH) && (Y>SEG_Y) && (Y<SEG_Y+SEG_HEIGHT*3))
+ { //
+ Segments ^= Mask >> i; //
+ } //
+ } //
+ }
+ break; //----------------- end slave 2 ----------------
+
+
+ /*
+ case (EL2004): //-------------- slave 3 EL2004 ----------------
+
+
+ if(TouchWasReleased) // first check if the touch was
+ { // not tapped in the previous rounds
+ TouchWasReleased = false; // because for the following fields
+ TimeTouchReleased = SysMilliS(); // we don't want autorepeat
+
+ uint8_t Mask = 0x08; // check if one of the output is tapped
+ //
+ for (i=0; i<4; i++) //
+ { //
+ if ((X>OUT_X+(i*OUT_STEP)) && (X<OUT_X+(i*OUT_STEP)+OUT_WIDTH) && (Y>OUT_Y) && (Y<OUT_Y+OUT_HEIGHT))
+ { //
+ Outputs ^= Mask >> i; //
+ } //
+ } //
+ }
+*/
+ }
+ }
+
+ else
+ {
+ if ( SysMilliS()-TimeTouchReleased > TIME_TOUCH_RELEASED) // if the touchscreen was not
+ { // tapped for enought time
+ TouchWasReleased = true; // remember it
+
+ }
+ }
+}
+
+
+//----- draw the fixed part of the visualized slave ----------------------------
+
+void DrawSlaveFixedParts()
+{
+ int i;
+ int Offset;
+
+ TFT.cls(); // clear screen
+ DrawSideMenu(VisuSlave); // draw the side menu
+
+ switch (VisuSlave) // check which slave is visualized on the TFT
+ {
+ case (LAB_1): //-------------- slave 1 ---------------------------
+
+ TFT.foreground(Yellow); // draw the thermometer
+
+ TFT.circle(TERMO_X, TERMO_Y, 18, Yellow); // bowl
+ TFT.fillcircle(TERMO_X, TERMO_Y, 17, Red); //
+
+ // tube
+ TFT.rect(TERMO_X-8, TERMO_Y-185, TERMO_X+8, TERMO_Y-18, Yellow);
+ TFT.fillrect(TERMO_X-7, TERMO_Y-18, TERMO_X+7, TERMO_Y-16, Red);
+
+ for (i=0; i<8; i++) // scale
+ { //
+ if (i <1) //
+ Offset = 5; //
+ else //
+ Offset = 0; //
+ //
+ TFT.line(TERMO_X-8, (TERMO_Y-28)-(i *20), TERMO_X-18,(TERMO_Y-28)-(i*20), Yellow);
+ TFT.locate(TERMO_X-48+Offset, TERMO_Y-(i*20)-32); //
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.printf("%3d",(i*10)-10); //
+ }
+
+ TFT.foreground(Green); //
+ TFT.set_font((unsigned char*) Arial28x28); //
+ TFT.locate(TERMO_X+25, TERMO_Y+28); //
+ TFT.printf("C"); //
+
+ // draw the alarm control panel
+
+ // frame
+ TFT.rect(ALARM_X, ALARM_Y, ALARM_X+ALARM_WIDTH, ALARM_Y+(ALARM_HEIGHT*4), Magenta);
+ //
+ for (i=0; i<3; i++) //
+ { //
+ TFT.line(ALARM_X, ALARM_Y+((i+1)*ALARM_HEIGHT) , ALARM_X+ALARM_WIDTH, ALARM_Y+((i+1)*ALARM_HEIGHT), Magenta);
+ } //
+ //
+ TFT.line(ALARM_X+(ALARM_WIDTH/2), ALARM_Y, ALARM_X+(ALARM_WIDTH/2), ALARM_Y+(ALARM_HEIGHT*2), Magenta);
+ TFT.line(ALARM_X+(ALARM_WIDTH/2), ALARM_Y+(ALARM_HEIGHT*3), ALARM_X+(ALARM_WIDTH/2), ALARM_Y+(ALARM_HEIGHT*4), Magenta);
+
+ TFT.set_font((unsigned char*) Arial28x28); // "+" and "-"
+ TFT.foreground(Green); //
+ TFT.locate(ALARM_X+11, ALARM_Y+(ALARM_HEIGHT*3)+9); //
+ TFT.printf("+"); //
+ TFT.locate(ALARM_X+(ALARM_WIDTH/2)+14, ALARM_Y+(ALARM_HEIGHT*3)+9);
+ TFT.printf("-"); //
+
+ // alarm bar
+ TFT.rect(ALARM_X-30, ALARM_Y, ALARM_X-25, ALARM_Y+168, Yellow);
+
+ TFT.set_font((unsigned char*) Arial12x12); // draw "ALARM SETTINGS"
+ TFT.foreground(Yellow); //
+ TFT.locate(ALARM_X-35, ALARM_Y+14+(ALARM_HEIGHT*4)); //
+ TFT.printf("ALARM SETTINGS"); //
+
+ break;
+
+
+
+
+ case (LAB_2): //-------------- slave 2 ---------------------------
+
+ TFT.foreground(Yellow);
+
+ for (i=0; i<4; i++) // draw the segments fixed parts
+ { //
+ TFT.rect(SEG_X+(i*SEG_STEP), SEG_Y, SEG_X+SEG_WIDTH+(i*SEG_STEP), SEG_Y+SEG_HEIGHT, Yellow);
+ //
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.locate(SEG_X+44, SEG_Y+SEG_HEIGHT+12); //
+ TFT.printf("MIDDLE SEGMENTS"); //
+ } //
+
+ for (i=0; i<3; i++) // draw the buttons fixed parts
+ { //
+ TFT.circle(BUTTONS_X+(i*BUTTONS_STEP), BUTTONS_Y, BUTTONS_R, Red);
+ TFT.circle(BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP), BUTTONS_Y, BUTTONS_R, Red);
+ //
+ TFT.line(BUTTONS_X+(i*BUTTONS_STEP)-BUTTONS_R, BUTTONS_Y, BUTTONS_X+(i*BUTTONS_STEP)-BUTTONS_R-4, BUTTONS_Y, Red);
+ TFT.line(BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP)+BUTTONS_R, BUTTONS_Y, BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP)+BUTTONS_R+4 , BUTTONS_Y, Red);
+ } //
+ //
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.locate(BUTTONS_X+38, BUTTONS_Y+12); //
+ TFT.printf("BUTTONS"); //
+
+ // draw the potentiometer window fixed parts
+ TFT.rect(ANALOG_X, ANALOG_Y, ANALOG_X+ANALOG_WIDTH, ANALOG_Y+ANALOG_HEIGHT, Magenta);
+ //
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.locate(ANALOG_X+35, ANALOG_Y+ANALOG_HEIGHT+12); //
+ TFT.printf("POTENTIOMETER"); //
+
+ Graph_x = 0;
+
+ DrawSegmentsValue(Segments); // draw the segments status
+ DrawButtonsValue (Buttons); // draw the buttons status
+ DrawPotentiometerValue(PotValue); // draw the potentiometer value
+ break;
+
+/*
+ case (EK1100): //-------------- slave 3 EK1100 --------------------
+
+ TFT.foreground(Red);
+ TFT.set_font((unsigned char*) Arial12x12);
+ TFT.locate(0, MENU_Y+(MENU_HEIGHT*2)+12);
+
+ TFT.printf("This slave has no Input/Output:");
+
+ break;
+
+
+ case (EL2004): //-------------- slave 4 EL2004 --------------------
+
+ TFT.foreground(Yellow);
+
+ for (i=0; i<4; i++) // draw the outputs fixed parts
+ { //
+ TFT.rect(OUT_X+(i*OUT_STEP), OUT_Y, OUT_X+OUT_WIDTH+(i*OUT_STEP), OUT_Y+OUT_HEIGHT, Yellow);
+ //
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.locate(OUT_X+45, OUT_Y+OUT_HEIGHT+40); //
+ TFT.printf("DIGITAL OUTPUTS"); //
+ } //
+
+ DrawOutputsValue(Outputs); // draw the outputs status
+ break;
+
+ case (EL1014): //-------------- slave 5 EL1014 --------------------
+
+ TFT.foreground(Yellow);
+
+
+ for (i=0; i<4; i++) // draw the inputs fixed parts
+ { //
+ TFT.circle(INPUTS_X+(i*INPUTS_STEP), INPUTS_Y, INPUTS_R, Red);
+ TFT.circle(INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP), INPUTS_Y, INPUTS_R, Red);
+ //
+ TFT.line(INPUTS_X+(i*INPUTS_STEP)-INPUTS_R, INPUTS_Y, INPUTS_X+(i*INPUTS_STEP)-INPUTS_R-4, INPUTS_Y, Red);
+ TFT.line(INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP)+INPUTS_R, INPUTS_Y, INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP)+INPUTS_R+4 , INPUTS_Y, Red);
+ } //
+ //
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.locate(INPUTS_X+45, INPUTS_Y+40); //
+ TFT.printf("DIGITAL INPUTS"); //
+
+ DrawInputsValue (Inputs); // draw the inputs status
+ break;
+*/
+
+ }
+
+ FirstRound = true;
+}
+
+
+//---- draw the menu on the upper right part of the TFT ------------------------
+
+void DrawSideMenu(uint8_t Slave)
+{ // draw the side menu frame
+ TFT.rect(MENU_X, MENU_Y, MENU_X+MENU_WIDTH, MENU_Y+MENU_HEIGHT*2, Green);
+ TFT.line(MENU_X, MENU_HEIGHT, MENU_X+MENU_WIDTH, MENU_HEIGHT, Green);
+ TFT.line(MENU_X+(MENU_WIDTH/2), MENU_Y+MENU_HEIGHT, MENU_X+(MENU_WIDTH/2), MENU_Y+(MENU_HEIGHT*2), Green);
+
+ // draw the slave number
+ TFT.fillrect(MENU_X+1, MENU_Y+1, MENU_X+MENU_WIDTH-1, MENU_Y+MENU_HEIGHT-1, Red);
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.foreground(Yellow); //
+ TFT.locate(MENU_X+8 , MENU_Y+18); //
+ TFT.background(Red); //
+ TFT.printf("SLAVE %d", Slave); //
+ TFT.background(Black); //
+
+ TFT.set_font((unsigned char*) Arial28x28); // draw "+" and "-"
+ TFT.foreground(Green); //
+ TFT.locate (MENU_X+11, MENU_Y+MENU_HEIGHT+9); //
+ TFT.printf("+"); //
+ TFT.locate (MENU_X+(MENU_WIDTH/2)+14, MENU_Y+MENU_HEIGHT+9); //
+ TFT.printf("-"); //
+
+ TFT.set_font((unsigned char*) Arial12x12); // draw the slave name
+ TFT.foreground(Red); //
+ //
+ TFT.locate(MENU_X, MENU_Y+(MENU_HEIGHT*2)+12); //
+ TFT.printf("%.9s", ec_slave[Slave].name); //
+}
+
+
+//---- draw the starting banner ------------------------------------------------
+
+void DrawBanner()
+{
+ TFT.set_font((unsigned char*) Arial24x23);
+ TFT.foreground(Red);
+ TFT.locate(30, 50);
+ TFT.printf("EasyCAT");
+ TFT.locate(30, 80);
+ TFT.printf("SOEM MASTER");
+
+ TFT.set_font((unsigned char*) Arial12x12);
+ TFT.foreground(Green);
+ TFT.locate(30, 140);
+ TFT.printf("www.bausano.net");
+
+ TFT.foreground(Green);
+ TFT.locate(30, 160);
+ TFT.printf("www.easycatshield.com");
+
+ TFT.locate(30, 180);
+ TFT.printf("https://openethercatsociety.github.io/");
+}
+
+
+
+//****** slave 1 functions *****************************************************
+
+//---- draw the temperature value both in analog and in digital form ------------
+
+void DrawTemperatureValue(float fValue)
+{
+ TFT.set_font((unsigned char*) Arial28x28); // digital visualization
+ TFT.foreground(Green); //
+ TFT.locate(TERMO_X-45, TERMO_Y+28); //
+ TFT.printf("%4.1f", fValue); //
+
+ if (fValue > 68) // limit the value for the
+ fValue = 68; // analog visualization
+ //
+ if (fValue < -15) //
+ fValue = -15; //
+
+ int LenColonnina = fValue * 2; // analog visualization
+ TFT.fillrect(TERMO_X-7, TERMO_Y-184, TERMO_X+7, TERMO_Y-18-LenColonnina-30-1, Black);
+ TFT.fillrect(TERMO_X-7, TERMO_Y-18-LenColonnina-30, TERMO_X+7, TERMO_Y-18, Red);
+}
+
+
+//---- draw the current parameter of the temperature alarm ---------------------
+
+void DrawAlarmSettings(float fThreshold, bool OnOff, bool MinMax)
+{
+
+ if (OnOff) // alarm on
+ { // draw "ON" on red background
+ // and "OFF"" on black background
+ DisplayInRect (ALARM_X, ALARM_Y, 10, 14, "ON", Red, (unsigned char*)Arial12x12);
+ DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y, 10, 14, "OFF", Black, (unsigned char*)Arial12x12);
+ }
+ else // alarm off
+ { // and "ON"" on black background
+ // draw "OFF" on red background
+ DisplayInRect (ALARM_X, ALARM_Y, 10, 14, "ON", Black, (unsigned char*)Arial12x12);
+ DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y, 10, 14, "OFF", Red, (unsigned char*)Arial12x12);
+ }
+
+
+ if (MinMax) // alarm when temperature < threshold
+ { // draw ">" on red background
+ // and ">"" on black background
+ DisplayInRect (ALARM_X, ALARM_Y+ALARM_HEIGHT, 10, 9, ">", Red, (unsigned char*)Arial28x28);
+ DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y+ALARM_HEIGHT, 10, 9, "<", Black, (unsigned char*)Arial28x28);
+ }
+ else // alarm when temperature > threshold
+ { // draw ">" on black background
+ // and ">"" on red background
+ DisplayInRect (ALARM_X, ALARM_Y+ALARM_HEIGHT, 10, 9, ">", Black, (unsigned char*)Arial28x28);
+ DisplayInRect (ALARM_X+(ALARM_WIDTH/2), ALARM_Y+ALARM_HEIGHT, 10, 9, "<", Red, (unsigned char*)Arial28x28);
+ }
+
+ DrawOnlyThreshold(fThreshold, OnOff, MinMax);
+}
+
+
+//---- draw only the threshold setting -----------------------------------------
+
+void DrawOnlyThreshold(float fThreshold, bool OnOff, bool MinMax) // this function is used to update
+{ // only the threshold value not to
+ // flicker the TFT, when "+" or
+ // "-" are tapped
+
+ TFT.foreground(Yellow); // draw the alarm threshold
+ TFT.set_font((unsigned char*) Arial12x12); // in digital form
+ TFT.locate(ALARM_X+26, ALARM_Y+14+(ALARM_HEIGHT*2)); //
+ TFT.printf(" "); //
+ TFT.locate(ALARM_X+26, ALARM_Y+14+(ALARM_HEIGHT*2)); //
+ TFT.printf("%+3.1f", fThreshold); //
+
+ if (fThreshold > 68) // limit the value for the
+ fThreshold = 68; // analog visualization
+ //
+ if (fThreshold < -15) //
+ fThreshold = -15; //
+
+ int LenAlarmBar = fThreshold * 2;
+
+ if (OnOff) // alarm on
+ { //
+ if (MinMax) // fill the threshold bar
+ { // in accordance with the MinMax setting
+ TFT.fillrect(ALARM_X-29, ALARM_Y+1, ALARM_X-26, ALARM_Y+167-LenAlarmBar-30, Green);
+ TFT.fillrect(ALARM_X-29, ALARM_Y+167-LenAlarmBar-30, ALARM_X-26, ALARM_Y+167, Red);
+ } //
+ else //
+ { //
+ TFT.fillrect(ALARM_X-29, ALARM_Y+1, ALARM_X-26, ALARM_Y+167-LenAlarmBar-30, Red);
+ TFT.fillrect(ALARM_X-29, ALARM_Y+167-LenAlarmBar-30, ALARM_X-26, ALARM_Y+167, Green);
+ } //
+ }
+
+ else // alarm off
+ { //
+ // clear the threshold bar
+ TFT.fillrect(ALARM_X-29, ALARM_Y+1, ALARM_X-26, ALARM_Y+167, Black);
+ } //
+}
+
+
+//---- draw the blinking alarm signal ------------------------------------------
+
+void DrawAlarmStatus(bool Alarm)
+{
+ bool static PrevAlarmVisu;
+
+ TFT.set_font((unsigned char*) Arial28x28);
+ TFT.foreground(Red);
+ TFT.locate(TERMO_X+90, TERMO_Y+28);
+
+ if (Alarm && Blink && !PrevAlarmVisu)
+ {
+ TFT.printf("ALARM !");
+ PrevAlarmVisu = true;
+ }
+ else if ((!Alarm && PrevAlarmVisu) || (Alarm && !Blink && PrevAlarmVisu))
+ {
+ TFT.printf(" ");
+ PrevAlarmVisu = false;
+ }
+}
+
+
+//----- draw a rectangle with text and colored background ----------------------
+
+void DisplayInRect (int X, int Y, int X_off, int Y_off, char* Value, int BackColor, unsigned char* Font)
+{
+ TFT.set_font(Font);
+ TFT.foreground(Green);
+
+ TFT.fillrect(X+1, Y+1, X+(MENU_WIDTH/2)-1, Y+MENU_HEIGHT-1, BackColor);
+
+ TFT.locate(X+X_off , Y+Y_off);
+ TFT.background(BackColor);
+ TFT.printf("%s", Value );
+ TFT.background(Black);
+}
+
+
+
+
+//****** slave 2 functions *****************************************************
+
+//------------------------------------------------------------------------------
+
+void DrawButtonsValue (uint8_t Value)
+{
+ uint8_t Slope;
+ int i;
+
+ for (i=0; i<3; i++)
+ {
+ if ((Value & 0x04) == 0x04)
+ Slope = BUTTONS_R;
+ else
+ Slope = 16;
+
+ TFT.fillrect(BUTTONS_X+(i*BUTTONS_STEP), BUTTONS_Y-16-1, BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP), BUTTONS_Y-BUTTONS_R-1, Black);
+
+ TFT.line(BUTTONS_X+(i*BUTTONS_STEP), BUTTONS_Y-BUTTONS_R-1, BUTTONS_X+BUTTONS_WIDTH+(i*BUTTONS_STEP), BUTTONS_Y-Slope-1, Red);
+
+ Value = Value << 1;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+
+void DrawSegmentsValue(uint8_t Value)
+{
+ int i;
+ int Color;
+
+ for (i=0; i<4; i++)
+ {
+ if ((Value & 0x08) == 0x08)
+ Color = Red;
+ else
+ Color = Black;
+
+ TFT.fillrect(SEG_X+(i*SEG_STEP)+1, SEG_Y+1, SEG_X+SEG_WIDTH+(i*SEG_STEP)-1, SEG_Y+SEG_HEIGHT-1, Color);
+
+ Value = Value << 1;
+ }
+}
+
+
+//---- draw the potentiometer value --------------------------------------------
+
+void DrawPotentiometerValue (uint16_t PotValue)
+{
+
+ if (PotValue != PrevPotValue)
+ {
+ PrevPotValue = PotValue;
+
+ TFT.set_font((unsigned char*) Arial12x12); //
+ TFT.foreground(Green); //
+ TFT.locate(ANALOG_X+170, ANALOG_Y+ANALOG_HEIGHT+12); //
+ TFT.printf("%4d", (int)PotValue); //
+ }
+
+
+ if (++Graph_x > ANALOG_WIDTH-3)
+ {
+ Graph_x = 0;
+ }
+
+ PotValue = PotValue / (1023/(ANALOG_HEIGHT-2));
+
+ TFT.pixel(ANALOG_X+Graph_x+1, ANALOG_Y+ANALOG_HEIGHT-PotValue-1, Green);
+
+ int Overflow = (ANALOG_WIDTH-2) - (Graph_x+16);
+ if (Overflow < 0)
+ {
+ TFT.fillrect(ANALOG_X+Graph_x+2, ANALOG_Y+1, ANALOG_X+Graph_x+16+Overflow, ANALOG_Y+ANALOG_HEIGHT-1, Black);
+ TFT.fillrect(ANALOG_X+1, ANALOG_Y+1, ANALOG_X+1-Overflow, ANALOG_Y+ANALOG_HEIGHT-1, Black);
+ }
+ else
+ {
+ TFT.fillrect(ANALOG_X+Graph_x+2, ANALOG_Y+1, ANALOG_X+Graph_x+16, ANALOG_Y+ANALOG_HEIGHT-1, Black);
+ }
+}
+
+
+//****** slave 3 functions EL2004 **********************************************
+/*
+
+//------------------------------------------------------------------------------
+
+void DrawOutputsValue(uint8_t Value)
+{
+ int i;
+ int Color;
+
+ for (i=0; i<4; i++)
+ {
+ if ((Value & 0x08) == 0x08)
+ Color = Green;
+ else
+ Color = Black;
+
+ TFT.fillrect(OUT_X+(i*OUT_STEP)+1, OUT_Y+1, OUT_X+OUT_WIDTH+(i*OUT_STEP)-1, OUT_Y+OUT_HEIGHT-1, Color);
+
+ Value = Value << 1;
+ }
+}
+
+//****** slave 4 functions *****************************************************
+
+//------------------------------------------------------------------------------
+
+void DrawInputsValue (uint8_t Value)
+{
+ uint8_t Slope;
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ if ((Value & 0x08) == 0x08)
+ Slope = INPUTS_R;
+ else
+ Slope = 16;
+
+ TFT.fillrect(INPUTS_X+(i*INPUTS_STEP), INPUTS_Y-16-1, INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP), INPUTS_Y-INPUTS_R-1, Black);
+
+ TFT.line(INPUTS_X+(i*INPUTS_STEP), INPUTS_Y-INPUTS_R-1, INPUTS_X+INPUTS_WIDTH+(i*INPUTS_STEP), INPUTS_Y-Slope-1, Red);
+
+ Value = Value << 1;
+ }
+}
+*/
+
+
+//****** touchscreen functions *************************************************
+
+//----- read touchscreen status ------------------------------------------------
+
+bool TouchRead(uint16_t* X, uint16_t* Y)
+{
+ bool Result = false;
+
+ {
+ if (TouchRead_Z()) // if the touchscreen is tapped
+ { //
+ *X = TouchRead_X(); // read also the X and Y axis
+ *Y = TouchRead_Y(); //
+
+ wait_us (1000);
+
+ if (TouchRead_Z()) // if the touchscreen is still tapped
+ { // we assume that the result is good
+ Result = true; //
+
+ // TFT.pixel (*X, *Y, White); //debug - draw the touched point on the TFT
+ }
+ }
+ }
+
+ return Result;
+}
+
+
+//----- read touchscreen X axis ------------------------------------------------
+
+uint16_t TouchRead_X()
+{
+ float fValue;
+
+ DigitalIn Ym(PIN_YM); // set the I/O
+ Ym.mode(PullDown); //
+ //
+ DigitalOut Xp(PIN_XP); //
+ Xp = 1; //
+ //
+ DigitalOut Xm(PIN_XM); //
+ Xm = 0; //
+ //
+ AnalogIn Yp(PIN_YP); //
+
+ fValue = ReadAnalog(Yp); // read the axis
+
+ fValue -= TOUCH_X_OFFSET; // rectify offsett and gain
+ if (fValue <0 ) //
+ fValue = 0; //
+ //
+ fValue *= TOUCH_X_GAIN; //
+
+ return (uint16_t)fValue;
+}
+
+
+//----- read touchscreen Y axis ------------------------------------------------
+
+uint16_t TouchRead_Y()
+{
+ float fValue;
+
+ DigitalIn Xm(PIN_XM); // set the I/O
+ Xm.mode(PullDown); //
+ //
+ DigitalOut Yp(PIN_YP); //
+ Yp = 1; //
+ //
+ DigitalOut Ym(PIN_YM); //
+ Ym = 0; //
+ //
+ AnalogIn Xp(PIN_XP); //
+
+ fValue = ReadAnalog(Xp); // read the axis
+
+ fValue -= TOUCH_Y_OFFSET; // rectify offset and gain
+ if (fValue <0 ) //
+ fValue = 0; //
+ //
+ fValue *= TOUCH_Y_GAIN; //
+
+ return (uint16_t)fValue;
+}
+
+
+//----- read touchscreen Z axis ------------------------------------------------
+
+bool TouchRead_Z() // read the Z axis to see if the
+{ // touchscreen has been tapped
+ float fValue = 0;
+ bool Result;
+
+ DigitalIn Yp(PIN_YP); // set the I/O
+ Yp.mode(PullUp); //
+ //
+ AnalogIn Ym(PIN_YM); //
+ //
+ DigitalOut Xm(PIN_XM); //
+ Xm = 0; //
+ //
+ DigitalOut Xp(PIN_XP); //
+ Xp = 0; //
+
+ for (int i = 0; i<TOUCH_SAMPLES; i++) // read the axis several times
+ { // and average the result
+ wait_us(10); //
+ //
+ fValue += Ym.read(); //
+ } //
+ fValue /= TOUCH_SAMPLES; //
+
+ if (fValue < TOUCH_THRESHOLD) // compare the result with
+ Result = true; // the threshold
+ else //
+ Result = false; //
+
+ return Result; //
+}
+
+
+//----- read touchscreen X or Y axis with a window filter ----------------------
+
+float ReadAnalog (AnalogIn AnaCh) // check that consecutive readings
+{ // fall in the acceptance window
+ float fArray[TOUCH_SAMPLES]; //
+ float fResult;
+ float fDiff;
+
+ int Rounds = TOUCH_MAX_ROUNDS; // maximum number of attempts
+
+ for (int i=0; i<TOUCH_SAMPLES; i++)
+ {
+ wait_us(10);
+
+ fResult = AnaCh.read();
+
+ if (i>0)
+ {
+ fDiff = abs(fResult - fArray[i-1]);
+
+ if (fDiff > TOUCH_WINDOW)
+ i= -1;
+ else
+ fArray[i] = fResult;
+
+ if (Rounds-- < 0)
+ {
+ fResult = 0;
+ return fResult;
+ }
+ }
+
+ else
+ {
+ fArray[i] = fResult;
+ }
+ }
+
+ fResult =0;
+
+ for (int i=0; i<TOUCH_SAMPLES; i++)
+ {
+ fResult += fArray[i];
+ }
+
+ return fResult /= TOUCH_SAMPLES;
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#88fb9b162d93a10e0d97f151c91bf2faf69e1b9e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,9 @@
+{
+ "target_overrides":
+ {
+ "NUCLEO_F767ZI":
+ {
+ "target.d11_configuration": "PB_5"
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/note_marco.txt Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,26 @@ +Libreria usata per il display SeeedStudio + +https://os.mbed.com/users/Rhyme/code/SPI_TFT_ILI9341/file/61fa73a34834/SPI_TFT_ILI9341.cpp/ + +Motoo Tanaka / SPI_TFT_ILI9341 + +---------------------------------------------------------------------- + +The SPI_TFT_IL9341 lib is deprecated. +Please use the UniGraphic lib for new projects. +https://developer.mbed.org/teams/GraphicsDisplay/code/UniGraphic/ +This has fixed font declarations. You can also use them in your project. + +-------------------------------------------------------------------------------- + +network driver + +https://os.mbed.com/users/mbed_official/code/lwip-eth/file/9de8bd8ca1c8/arch/TARGET_STM/stm32f4_emac.c/ + +//------------------------------------------------------------------------------ + +wiki TFT SeeedStudio +http://wiki.seeedstudio.com/2.8inch_TFT_Touch_Shield_v2.0/ + +schema TFT SeeedStudio +https://github.com/SeeedDocument/TFT_Touch_Shield_V2/raw/master/resources/TFT%20Touch.pdf \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/soem_start.cpp Tue Jun 11 10:19:08 2019 +0000
@@ -0,0 +1,705 @@
+
+#include "soem_start.h"
+#include "ethercat.h"
+#include "string.h"
+#include "oshw.h"
+
+#include "mbed.h"
+
+#include <inttypes.h>
+
+#include "SPI_TFT_ILI9341.h"
+
+extern SPI_TFT_ILI9341 TFT;
+
+boolean printSDO = TRUE;
+boolean printMAP = TRUE;
+
+ec_ODlistt ODlist;
+ec_OElistt OElist;
+char usdo[128];
+char hstr[1024];
+
+char extern IOmap[];
+
+uint32_t error_counter = 0;
+
+
+char* dtype2string(uint16 dtype);
+char* SDO2string(uint16 slave, uint16 index, uint8 subidx, uint16 dtype);
+int si_PDOassign(uint16 slave, uint16 PDOassign, int mapoffset, int bitoffset);
+void si_sdo(int cnt);
+int si_map_sdo(int slave);
+int si_siiPDO(uint16 slave, uint8 t, int mapoffset, int bitoffset);
+bool network_scanning(void);
+
+
+
+//---- convert Ethercat types to string ----------------------------------------
+
+
+char* dtype2string(uint16 dtype)
+{
+ switch(dtype)
+ {
+ case ECT_BOOLEAN:
+ sprintf(hstr, "BOOLEAN");
+ break;
+ case ECT_INTEGER8:
+ sprintf(hstr, "INTEGER8");
+ break;
+ case ECT_INTEGER16:
+ sprintf(hstr, "INTEGER16");
+ break;
+ case ECT_INTEGER32:
+ sprintf(hstr, "INTEGER32");
+ break;
+ case ECT_INTEGER24:
+ sprintf(hstr, "INTEGER24");
+ break;
+ case ECT_INTEGER64:
+ sprintf(hstr, "INTEGER64");
+ break;
+ case ECT_UNSIGNED8:
+ sprintf(hstr, "UNSIGNED8");
+ break;
+ case ECT_UNSIGNED16:
+ sprintf(hstr, "UNSIGNED16");
+ break;
+ case ECT_UNSIGNED32:
+ sprintf(hstr, "UNSIGNED32");
+ break;
+ case ECT_UNSIGNED24:
+ sprintf(hstr, "UNSIGNED24");
+ break;
+ case ECT_UNSIGNED64:
+ sprintf(hstr, "UNSIGNED64");
+ break;
+ case ECT_REAL32:
+ sprintf(hstr, "REAL32");
+ break;
+ case ECT_REAL64:
+ sprintf(hstr, "REAL64");
+ break;
+ case ECT_BIT1:
+ sprintf(hstr, "BIT1");
+ break;
+ case ECT_BIT2:
+ sprintf(hstr, "BIT2");
+ break;
+ case ECT_BIT3:
+ sprintf(hstr, "BIT3");
+ break;
+ case ECT_BIT4:
+ sprintf(hstr, "BIT4");
+ break;
+ case ECT_BIT5:
+ sprintf(hstr, "BIT5");
+ break;
+ case ECT_BIT6:
+ sprintf(hstr, "BIT6");
+ break;
+ case ECT_BIT7:
+ sprintf(hstr, "BIT7");
+ break;
+ case ECT_BIT8:
+ sprintf(hstr, "BIT8");
+ break;
+ case ECT_VISIBLE_STRING:
+ sprintf(hstr, "VISIBLE_STRING");
+ break;
+ case ECT_OCTET_STRING:
+ sprintf(hstr, "OCTET_STRING");
+ break;
+ default:
+ sprintf(hstr, "Type 0x%4.4X", dtype);
+ }
+ return hstr;
+}
+
+
+//------------------------------------------------------------------------------
+
+char* SDO2string(uint16 slave, uint16 index, uint8 subidx, uint16 dtype)
+{
+ int l = sizeof(usdo) - 1, i;
+ uint8 *u8;
+ int8 *i8;
+ uint16 *u16;
+ int16 *i16;
+ uint32 *u32;
+ int32 *i32;
+ uint64 *u64;
+ int64 *i64;
+ float *sr;
+ double *dr;
+ char es[32];
+
+ memset(&usdo, 0, 128);
+ ec_SDOread(slave, index, subidx, FALSE, &l, &usdo, EC_TIMEOUTRXM);
+ if (EcatError)
+ {
+ return ec_elist2string();
+ }
+ else
+ {
+ switch(dtype)
+ {
+ case ECT_BOOLEAN:
+ u8 = (uint8*) &usdo[0];
+ if (*u8) sprintf(hstr, "TRUE");
+ else sprintf(hstr, "FALSE");
+ break;
+ case ECT_INTEGER8:
+ i8 = (int8*) &usdo[0];
+ sprintf(hstr, "0x%2.2x %d", *i8, *i8);
+ break;
+ case ECT_INTEGER16:
+ i16 = (int16*) &usdo[0];
+ sprintf(hstr, "0x%4.4x %d", *i16, *i16);
+ break;
+ case ECT_INTEGER32:
+ case ECT_INTEGER24:
+ i32 = (int32*) &usdo[0];
+ sprintf(hstr, "0x%8.8x %d", *i32, *i32);
+ break;
+ case ECT_INTEGER64:
+ i64 = (int64*) &usdo[0];
+ sprintf(hstr, "0x%16.16"PRIx64" %"PRId64, *i64, *i64);
+ break;
+ case ECT_UNSIGNED8:
+ u8 = (uint8*) &usdo[0];
+ sprintf(hstr, "0x%2.2x %u", *u8, *u8);
+ break;
+ case ECT_UNSIGNED16:
+ u16 = (uint16*) &usdo[0];
+ sprintf(hstr, "0x%4.4x %u", *u16, *u16);
+ break;
+ case ECT_UNSIGNED32:
+ case ECT_UNSIGNED24:
+ u32 = (uint32*) &usdo[0];
+ sprintf(hstr, "0x%8.8x %u", *u32, *u32);
+ break;
+ case ECT_UNSIGNED64:
+ u64 = (uint64*) &usdo[0];
+ sprintf(hstr, "0x%16.16"PRIx64" %"PRIu64, *u64, *u64);
+ break;
+ case ECT_REAL32:
+ sr = (float*) &usdo[0];
+ sprintf(hstr, "%f", *sr);
+ break;
+ case ECT_REAL64:
+ dr = (double*) &usdo[0];
+ sprintf(hstr, "%f", *dr);
+ break;
+ case ECT_BIT1:
+ case ECT_BIT2:
+ case ECT_BIT3:
+ case ECT_BIT4:
+ case ECT_BIT5:
+ case ECT_BIT6:
+ case ECT_BIT7:
+ case ECT_BIT8:
+ u8 = (uint8*) &usdo[0];
+ sprintf(hstr, "0x%x", *u8);
+ break;
+ case ECT_VISIBLE_STRING:
+ strcpy(hstr, usdo);
+ break;
+ case ECT_OCTET_STRING:
+ hstr[0] = 0x00;
+ for (i = 0 ; i < l ; i++)
+ {
+ sprintf(es, "0x%2.2x ", usdo[i]);
+ strcat( hstr, es);
+ }
+ break;
+ default:
+ sprintf(hstr, "Unknown type");
+ }
+ return hstr;
+ }
+}
+
+
+//----- read PDO assign structure ----------------------------------------------
+
+int si_PDOassign(uint16 slave, uint16 PDOassign, int mapoffset, int bitoffset)
+{
+ uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
+ uint8 subcnt;
+ int wkc, bsize = 0, rdl;
+ int32 rdat2;
+ uint8 bitlen, obj_subidx;
+ uint16 obj_idx;
+ int abs_offset, abs_bit;
+
+ rdl = sizeof(rdat); rdat = 0;
+ // read PDO assign subindex 0 (= number of PDO's)
+ wkc = ec_SDOread(slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+ rdat = etohs(rdat);
+
+ if ((wkc > 0) && (rdat > 0)) // positive result from slave?
+ {
+ nidx = rdat; // number of available sub indexes
+ bsize = 0;
+
+ for (idxloop = 1; idxloop <= nidx; idxloop++) // read all PDO's
+ {
+ rdl = sizeof(rdat); rdat = 0;
+ // read PDO assign
+ wkc = ec_SDOread(slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
+
+ idx = etohl(rdat); // result is index of PDO
+ if (idx > 0)
+ {
+ rdl = sizeof(subcnt); subcnt = 0;
+ // read number of subindexes of PDO
+ wkc = ec_SDOread(slave,idx, 0x00, FALSE, &rdl, &subcnt, EC_TIMEOUTRXM);
+ subidx = subcnt;
+ // for each subindex
+ for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
+ {
+ rdl = sizeof(rdat2); rdat2 = 0;
+ // read SDO that is mapped in PDO
+ wkc = ec_SDOread(slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
+ rdat2 = etohl(rdat2);
+
+ bitlen = LO_BYTE(rdat2); // extract bitlength of SDO
+ bsize += bitlen;
+ obj_idx = (uint16)(rdat2 >> 16);
+ obj_subidx = (uint8)((rdat2 >> 8) & 0x000000ff);
+ abs_offset = mapoffset + (bitoffset / 8);
+ abs_bit = bitoffset % 8;
+ ODlist.Slave = slave;
+ ODlist.Index[0] = obj_idx;
+ OElist.Entries = 0;
+ wkc = 0;
+
+ if(obj_idx || obj_subidx) // read object entry from dictionary if not a filler (0x0000:0x00)
+ wkc = ec_readOEsingle(0, obj_subidx, &ODlist, &OElist);
+ printf(" [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
+
+ if((wkc > 0) && OElist.Entries)
+ {
+ printf(" %-12s %s\n", dtype2string(OElist.DataType[obj_subidx]), OElist.Name[obj_subidx]);
+ }
+ else
+ printf("\n");
+ bitoffset += bitlen;
+ };
+ };
+ };
+ };
+
+ return bsize; // return total found bitlength (PDO)
+}
+
+
+//---- PDO mapping according to CoE --------------------------------------------
+
+int si_map_sdo(int slave)
+{
+ int wkc, rdl;
+ int retVal = 0;
+ uint8 nSM, iSM, tSM;
+ int Tsize, outputs_bo, inputs_bo;
+ uint8 SMt_bug_add;
+
+ printf("PDO mapping according to CoE :\n\n");
+ SMt_bug_add = 0;
+ outputs_bo = 0;
+ inputs_bo = 0;
+ rdl = sizeof(nSM); nSM = 0;
+ // read SyncManager Communication Type object count
+ wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
+
+ if ((wkc > 0) && (nSM > 2)) // positive result from slave ?
+ {
+ nSM--; // make nSM equal to number of defined SM
+
+ if (nSM > EC_MAXSM) // limit to maximum number of SM defined,
+ nSM = EC_MAXSM; // if true the slave can't be configured
+
+ for (iSM = 2 ; iSM <= nSM ; iSM++) // iterate for every SM type defined
+ {
+ rdl = sizeof(tSM); tSM = 0;
+ // read SyncManager Communication Type
+ wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
+ if (wkc > 0)
+ {
+ if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
+ {
+ SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
+ printf("Activated SM type workaround, possible incorrect mapping.\n");
+ }
+
+ if(tSM) // only add if SMt > 0
+ tSM += SMt_bug_add;
+
+ if (tSM == 3) // outputs
+ { // read the assign RXPDO
+
+ printf(" SM%1d outputs\n addr b index: sub bitl data_type name\n", iSM);
+ Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].outputs - (uint8 *)&IOmap[0]), outputs_bo );
+ outputs_bo += Tsize;
+ }
+
+ if (tSM == 4) // inputs
+ { // read the assign TXPDO
+
+ printf(" SM%1d inputs\n addr b index: sub bitl data_type name\n", iSM);
+ Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].inputs - (uint8 *)&IOmap[0]), inputs_bo );
+ inputs_bo += Tsize;
+ }
+ }
+ }
+ }
+
+ if ((outputs_bo > 0) || (inputs_bo > 0)) // found some I/O bits?
+ retVal = 1;
+ return retVal;
+}
+
+
+//---- CoE objects description -------------------------------------------------
+
+void si_sdo(int cnt)
+{
+ int i, j;
+
+ ODlist.Entries = 0;
+ memset(&ODlist, 0, sizeof(ODlist));
+ if( ec_readODlist(cnt, &ODlist))
+ {
+ printf(" CoE Object Description found, %d entries.\n",ODlist.Entries);
+ for( i = 0 ; i < ODlist.Entries ; i++)
+ {
+ ec_readODdescription(i, &ODlist);
+ while(EcatError) printf("%s", ec_elist2string());
+ printf(" Index: %4.4x Datatype: %4.4x Objectcode: %2.2x Name: %s\n",
+ ODlist.Index[i], ODlist.DataType[i], ODlist.ObjectCode[i], ODlist.Name[i]);
+ memset(&OElist, 0, sizeof(OElist));
+ ec_readOE(i, &ODlist, &OElist);
+ while(EcatError) printf("%s", ec_elist2string());
+ for( j = 0 ; j < ODlist.MaxSub[i]+1 ; j++)
+ {
+ if ((OElist.DataType[j] > 0) && (OElist.BitLength[j] > 0))
+ {
+ printf(" Sub: %2.2x Datatype: %4.4x Bitlength: %4.4x Obj.access: %4.4x Name: %s\n",
+ j, OElist.DataType[j], OElist.BitLength[j], OElist.ObjAccess[j], OElist.Name[j]);
+ if ((OElist.ObjAccess[j] & 0x0007))
+ {
+ printf(" Value :%s\n", SDO2string(cnt, ODlist.Index[i], j, OElist.DataType[j]));
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ while(EcatError) printf("%s", ec_elist2string());
+ }
+}
+
+
+//---- PDO mapping according to SII --------------------------------------------
+
+int si_map_sii(int slave)
+{
+ int retVal = 0;
+ int Tsize, outputs_bo, inputs_bo;
+
+ printf("\nPDO mapping according to SII :\n\n");
+
+ outputs_bo = 0;
+ inputs_bo = 0;
+ // read the assign RXPDOs
+ Tsize = si_siiPDO(slave, 1, (int)(ec_slave[slave].outputs - (uint8*)&IOmap), outputs_bo );
+ outputs_bo += Tsize;
+ // read the assign TXPDOs
+ Tsize = si_siiPDO(slave, 0, (int)(ec_slave[slave].inputs - (uint8*)&IOmap), inputs_bo );
+ inputs_bo += Tsize;
+
+ if ((outputs_bo > 0) || (inputs_bo > 0)) // found some I/O bits ?
+ retVal = 1;
+ return retVal;
+}
+
+
+//------------------------------------------------------------------------------
+
+int si_siiPDO(uint16 slave, uint8 t, int mapoffset, int bitoffset)
+{
+ uint16 a , w, c, e, er, Size;
+ uint8 eectl;
+ uint16 obj_idx;
+ uint8 obj_subidx;
+ uint8 obj_name;
+ uint8 obj_datatype;
+ uint8 bitlen;
+ int totalsize;
+ ec_eepromPDOt eepPDO;
+ ec_eepromPDOt *PDO;
+ int abs_offset, abs_bit;
+ char str_name[EC_MAXNAME + 1];
+
+ eectl = ec_slave[slave].eep_pdi;
+ Size = 0;
+ totalsize = 0;
+ PDO = &eepPDO;
+ PDO->nPDO = 0;
+ PDO->Length = 0;
+ PDO->Index[1] = 0;
+
+ for (c = 0 ; c < EC_MAXSM ; c++)
+ PDO->SMbitsize[c] = 0;
+
+ if (t > 1)
+ t = 1;
+
+ PDO->Startpos = ec_siifind(slave, ECT_SII_PDO + t);
+
+ if (PDO->Startpos > 0)
+ {
+ a = PDO->Startpos;
+ w = ec_siigetbyte(slave, a++);
+ w += (ec_siigetbyte(slave, a++) << 8);
+ PDO->Length = w;
+ c = 1;
+
+ do // traverse through all PDOs
+ {
+ PDO->nPDO++;
+ PDO->Index[PDO->nPDO] = ec_siigetbyte(slave, a++);
+ PDO->Index[PDO->nPDO] += (ec_siigetbyte(slave, a++) << 8);
+ PDO->BitSize[PDO->nPDO] = 0;
+ c++;
+
+ e = ec_siigetbyte(slave, a++); // number of entries in PDO
+ PDO->SyncM[PDO->nPDO] = ec_siigetbyte(slave, a++);
+ a++;
+ obj_name = ec_siigetbyte(slave, a++);
+ a += 2;
+ c += 2;
+
+ if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) // active and in range SM?
+ {
+ str_name[0] = 0;
+ if(obj_name)
+ ec_siistring(str_name, slave, obj_name);
+
+ if (t)
+ printf(" SM%1d RXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
+ else
+ printf(" SM%1d TXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
+
+ printf(" addr b index: sub bitl data_type name\n");
+
+ for (er = 1; er <= e; er++) // read all entries defined in PDO
+ {
+ c += 4;
+ obj_idx = ec_siigetbyte(slave, a++);
+ obj_idx += (ec_siigetbyte(slave, a++) << 8);
+ obj_subidx = ec_siigetbyte(slave, a++);
+ obj_name = ec_siigetbyte(slave, a++);
+ obj_datatype = ec_siigetbyte(slave, a++);
+ bitlen = ec_siigetbyte(slave, a++);
+ abs_offset = mapoffset + (bitoffset / 8);
+ abs_bit = bitoffset % 8;
+
+ PDO->BitSize[PDO->nPDO] += bitlen;
+ a += 2;
+
+ if(obj_idx || obj_subidx) // skip entry if filler (0x0000:0x00)
+ {
+ str_name[0] = 0;
+ if(obj_name)
+ ec_siistring(str_name, slave, obj_name);
+
+ printf(" [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
+ printf(" %-12s %s\n", dtype2string(obj_datatype), str_name);
+ }
+ bitoffset += bitlen;
+ totalsize += bitlen;
+ }
+ PDO->SMbitsize[ PDO->SyncM[PDO->nPDO] ] += PDO->BitSize[PDO->nPDO];
+ Size += PDO->BitSize[PDO->nPDO];
+ c++;
+ }
+
+ else // PDO deactivated because SM is 0xff or > EC_MAXSM
+ {
+ c += 4 * e;
+ a += 8 * e;
+ c++;
+ }
+ // limit number of PDO entries in buffer
+ if (PDO->nPDO >= (EC_MAXEEPDO - 1)) c = PDO->Length;
+ }
+ while (c < PDO->Length);
+ }
+
+ if (eectl)
+ ec_eeprom2pdi(slave); // if eeprom control was previously pdi then restore
+
+ return totalsize;
+}
+
+
+//------------------------------------------------------------------------------
+
+bool network_scanning(void)
+
+{
+ int expectedWKC;
+ int cnt, i, j, nSM;
+ uint16 ssigen;
+
+ if ( ec_config(FALSE, &IOmap) > 0 ) // find and configure the slaves
+ {
+ ec_configdc();
+ while(EcatError) printf("%s", ec_elist2string());
+
+ printf("%d slaves found and configured.\n",ec_slavecount);
+ TFT.printf("%d slaves found and configured.\n\n",ec_slavecount);
+
+ expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
+ printf("Calculated workcounter %d\n", expectedWKC);
+ // wait for all slaves to reach SAFE_OP state
+ ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 3);
+
+ if (ec_slave[0].state != EC_STATE_SAFE_OP )
+ {
+ printf("Not all slaves reached safe operational state.\n");
+ ec_readstate();
+ for(i = 1; i<=ec_slavecount ; i++)
+ {
+ if(ec_slave[i].state != EC_STATE_SAFE_OP)
+ {
+ printf("Slave %d State=%2x StatusCode=%4x : %s\n",
+ i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
+ }
+ }
+ }
+
+ ec_readstate();
+
+ for( cnt = 1 ; cnt <= ec_slavecount ; cnt++)
+ {
+ printf("\nSlave:%d -------------------------------------------------\nName:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d[ns]\n Has DC: %d\n",
+ cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
+ ec_slave[cnt].state, ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);
+
+ int Line = cnt-1;
+
+ TFT.foreground(Green); // print Name, Manufacturer, ID and Revision
+ TFT.locate(0, 12*(((Line % 6)*3)+3)); // of the slaves found on the TFT
+ TFT.printf("Slave %d ", cnt); //
+ TFT.foreground(Yellow); //
+ //
+ TFT.foreground(Magenta); //
+ TFT.locate(60, 12*(((Line % 6)*3)+3)); //
+ TFT.printf("Name"); //
+ TFT.foreground(Yellow); //
+ TFT.locate(104, 12*(((Line % 6)*3)+3)); //
+ TFT.printf("%.14s", ec_slave[cnt].name); //
+ //
+ TFT.foreground(Magenta); //
+ TFT.locate(220, 12*(((Line % 6)*3)+3)); //
+ TFT.printf("Man"); //
+ TFT.foreground(Yellow); //
+ TFT.locate(250, 12*(((Line % 6)*3)+3)); //
+ TFT.printf("%8.8X", (int)ec_slave[cnt].eep_man);//
+ //
+ TFT.foreground(Magenta); //
+ TFT.locate(60, 12*(((Line % 6)*3)+4)); //
+ TFT.printf("ID"); //
+ TFT.foreground(Yellow); //
+ TFT.locate(104, 12*(((Line % 6)*3)+4)); //
+ TFT.printf("%8.8X", (int)ec_slave[cnt].eep_id); //
+ //
+ TFT.foreground(Magenta); //
+ TFT.locate(220, 12*(((Line % 6)*3)+4)); //
+ TFT.printf("Rev "); //
+ TFT.foreground(Yellow); //
+ TFT.locate(250, 12*(((Line % 6)*3)+4)); //
+ TFT.printf("%8.8X", (int)ec_slave[cnt].eep_rev);//
+
+
+ if (ec_slave[cnt].hasdc) printf(" DCParentport:%d\n", ec_slave[cnt].parentport);
+ printf(" Activeports:%d.%d.%d.%d\n", (ec_slave[cnt].activeports & 0x01) > 0 ,
+ (ec_slave[cnt].activeports & 0x02) > 0 ,
+ (ec_slave[cnt].activeports & 0x04) > 0 ,
+ (ec_slave[cnt].activeports & 0x08) > 0 );
+
+ printf(" Configured address: %4.4X\n", ec_slave[cnt].configadr);
+ printf(" Outputs address: %X\n", ec_slave[cnt].outputs);
+ printf(" Inputs address: %X\n", ec_slave[cnt].inputs);
+
+ printf(" Man: %8.8x ID: %8.8x Rev: %8.8x\n", (int)ec_slave[cnt].eep_man, (int)ec_slave[cnt].eep_id, (int)ec_slave[cnt].eep_rev);
+ for(nSM = 0 ; nSM < EC_MAXSM ; nSM++)
+ {
+ if(ec_slave[cnt].SM[nSM].StartAddr > 0)
+ printf(" SM%1d A:%4.4x L:%4d F:%8.8x Type:%d\n",nSM, ec_slave[cnt].SM[nSM].StartAddr, ec_slave[cnt].SM[nSM].SMlength,
+ (int)ec_slave[cnt].SM[nSM].SMflags, ec_slave[cnt].SMtype[nSM]);
+ }
+ for(j = 0 ; j < ec_slave[cnt].FMMUunused ; j++)
+ {
+ printf(" FMMU%1d Ls:%8.8x Ll:%4d Lsb:%d Leb:%d Ps:%4.4x Psb:%d Ty:%2.2x Act:%2.2x\n", j,
+ (int)ec_slave[cnt].FMMU[j].LogStart, ec_slave[cnt].FMMU[j].LogLength, ec_slave[cnt].FMMU[j].LogStartbit,
+ ec_slave[cnt].FMMU[j].LogEndbit, ec_slave[cnt].FMMU[j].PhysStart, ec_slave[cnt].FMMU[j].PhysStartBit,
+ ec_slave[cnt].FMMU[j].FMMUtype, ec_slave[cnt].FMMU[j].FMMUactive);
+ }
+ printf(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n",
+ ec_slave[cnt].FMMU0func, ec_slave[cnt].FMMU1func, ec_slave[cnt].FMMU2func, ec_slave[cnt].FMMU3func);
+ printf(" MBX length wr: %d rd: %d MBX protocols : %2.2x\n", ec_slave[cnt].mbx_l, ec_slave[cnt].mbx_rl, ec_slave[cnt].mbx_proto);
+ ssigen = ec_siifind(cnt, ECT_SII_GENERAL);
+
+ if (ssigen) // SII general section
+ {
+ ec_slave[cnt].CoEdetails = ec_siigetbyte(cnt, ssigen + 0x07);
+ ec_slave[cnt].FoEdetails = ec_siigetbyte(cnt, ssigen + 0x08);
+ ec_slave[cnt].EoEdetails = ec_siigetbyte(cnt, ssigen + 0x09);
+ ec_slave[cnt].SoEdetails = ec_siigetbyte(cnt, ssigen + 0x0a);
+ if((ec_siigetbyte(cnt, ssigen + 0x0d) & 0x02) > 0)
+ {
+ ec_slave[cnt].blockLRW = 1;
+ ec_slave[0].blockLRW++;
+ }
+ ec_slave[cnt].Ebuscurrent = ec_siigetbyte(cnt, ssigen + 0x0e);
+ ec_slave[cnt].Ebuscurrent += ec_siigetbyte(cnt, ssigen + 0x0f) << 8;
+ ec_slave[0].Ebuscurrent += ec_slave[cnt].Ebuscurrent;
+ }
+ printf(" CoE details: %2.2x FoE details: %2.2x EoE details: %2.2x SoE details: %2.2x\n",
+ ec_slave[cnt].CoEdetails, ec_slave[cnt].FoEdetails, ec_slave[cnt].EoEdetails, ec_slave[cnt].SoEdetails);
+ printf(" Ebus current: %d[mA]\n only LRD/LWR:%d\n",
+ ec_slave[cnt].Ebuscurrent, ec_slave[cnt].blockLRW);
+
+ if ((ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE) && printSDO)
+ si_sdo(cnt);
+
+ if(printMAP)
+ {
+ if (ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE)
+ si_map_sdo(cnt);
+ else
+ si_map_sii(cnt);
+ }
+ }
+ return 1;
+ }
+
+ else
+ {
+ // ec_close(); //******//
+
+ return 0; // no slaves found
+ }
+}
+
+
+
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/soem_start.h Tue Jun 11 10:19:08 2019 +0000 @@ -0,0 +1,9 @@ + +#ifndef soem_start_H +#define soem_start_H + + +bool network_scanning(void); + + +#endif
