feat(motor): 自适应 PDO 窗口与通道活动指示,新增原生 SOEM 时序诊断

后端 PDO 循环改为按在线槽收缩状态窗口,稀疏总线下单槽每周期都能刷新,
并补上尾部槽的发现扫描游标;新增自旋节拍源(阈值/显式覆盖)、P 核识别
与线程级钉核,自旋期间释放 GIL 以免 HTTP 线程被饿死;启动时先关停旧实例,
避免两个进程争用 Npcap 适配器。

记录每拍完整时间线、GC stop-the-world 事件与网格跳拍数,经 /api/motor/dbg
导出用于归因固定频率的超时;配
This commit is contained in:
cbb
2026-09-05 01:20:14 +08:00
parent 979da72181
commit 79633a2b0f
121 changed files with 33088 additions and 75 deletions
+26
View File
@@ -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 */
+637
View File
@@ -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.
*/
uint16 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 = (uint16)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);
}
uint16 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
+63
View File
@@ -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);
uint16 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);
uint16 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
File diff suppressed because it is too large Load Diff
+95
View File
@@ -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, const void *p, int Timeout);
int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber , int psize, const void *p);
int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout);
int ec_readPDOmap(uint16 Slave, uint32 *Osize, uint32 *Isize);
int ec_readPDOmapCA(uint16 Slave, int Thread_n, uint32 *Osize, uint32 *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, const void *p, int Timeout);
int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber , int psize, const 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, uint32 *Osize, uint32 *Isize);
int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, uint32 *Osize, uint32 *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
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
/*
* 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_aligned(void *pIOmap);
int ec_config_map_group(void *pIOmap, uint8 group);
int ec_config_overlap_map_group(void *pIOmap, uint8 group);
int ec_config_map_group_aligned(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_config_map_group_aligned(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
+66
View File
@@ -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
+448
View File
@@ -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
+33
View File
@@ -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 */
+634
View File
@@ -0,0 +1,634 @@
/*
* 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 + MBX_HDR_SET_CNT(cnt); /* 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;
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 += EOE_IP4_LENGTH;
}
if (ipparam->subnet_set)
{
flags |= EOE_PARAM_SUBNET_IP_INCLUDE;
EOE_ip_uint32_to_byte(&ipparam->subnet, &EOEp->data[data_offset]);
data_offset += EOE_IP4_LENGTH;
}
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 += EOE_IP4_LENGTH;
}
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 += EOE_IP4_LENGTH;
}
if (ipparam->dns_name_set)
{
/* TwinCAT include EOE_DNS_NAME_LENGTH chars even if name is shorter */
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 + MBX_HDR_SET_CNT(cnt); /* 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
{
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 += EOE_IP4_LENGTH;
}
if (flags & EOE_PARAM_SUBNET_IP_INCLUDE)
{
EOE_ip_byte_to_uint32(&aEOEp->data[data_offset],
&ipparam->subnet);
ipparam->subnet_set = 1;
data_offset += EOE_IP4_LENGTH;
}
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 += EOE_IP4_LENGTH;
}
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 += EOE_IP4_LENGTH;
}
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;
uint8 cnt, txfragmentno;
boolean NotLast;
int wkc, maxdata, txframesize, txframeoffset;
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((uint16)(4 + txframesize)); /* no timestamp */
EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + MBX_HDR_SET_CNT(cnt); /* 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;
uint8 rxfragmentno, rxframeno;
boolean NotLast;
int wkc, buffersize, rxframesize, rxframeoffset, eoedatasize;
uint8 * buf = p;
ec_clearmbx(&MbxIn);
aEOEp = (ec_EOEt *)&MbxIn;
NotLast = TRUE;
buffersize = *psize;
rxfragmentno = 0;
rxframeno = 0xff;
rxframeoffset = 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)
{
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 = (uint16)(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;
}
+213
View File
@@ -0,0 +1,213 @@
/*
* 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 "ethercat.h"
/* use maximum size for EOE mailbox data - mbxheader and 2x frameinfo */
#define EC_MAXEOEDATA (EC_MAXMBX - (sizeof(ec_mbxheadert) +\
sizeof(uint16_t) +\
sizeof(uint16_t)))
/** DNS length according to ETG 1000.6 */
#define EOE_DNS_NAME_LENGTH 32
/** Ethernet address length not including VLAN */
#define EOE_ETHADDR_LENGTH 6
/** IPv4 address length */
#define EOE_IP4_LENGTH sizeof(uint32_t)
#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) ((uint16)(((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) ((uint16)(((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) ((uint16)(((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) ((uint16)(((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[EC_MAXEOEDATA];
} 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
+416
View File
@@ -0,0 +1,416 @@
/*
* 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"
/* use maximum size for FOE mailbox data - header and metadata */
#define EC_MAXFOEDATA \
(EC_MAXMBX - \
(sizeof(ec_mbxheadert) + \
sizeof(uint8_t) + \
sizeof(uint8_t) + \
sizeof(uint32_t)))
/** 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);
if (fnsize > EC_MAXFOEDATA)
{
fnsize = EC_MAXFOEDATA;
}
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 + MBX_HDR_SET_CNT(cnt); /* 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 + MBX_HDR_SET_CNT(cnt); /* 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 = TRUE;
fnsize = (uint16)strlen(filename);
if (fnsize > EC_MAXFOEDATA)
{
fnsize = EC_MAXFOEDATA;
}
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 + MBX_HDR_SET_CNT(cnt); /* 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((uint16)(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 + MBX_HDR_SET_CNT(cnt); /* 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)
{
psize += segmentdata;
p = (uint8 *)p - segmentdata;
--sendpacket;
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((uint16)(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 + MBX_HDR_SET_CNT(cnt); /* 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;
}
}
}
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
+33
View File
@@ -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
File diff suppressed because it is too large Load Diff
+539
View File
@@ -0,0 +1,539 @@
/*
* 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
typedef struct ecx_context ecx_contextt;
/** 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;
/** serial number from EEprom */
uint32 eep_sn;
/** 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, (DEPRECATED)*/
int (*PO2SOconfig)(uint16 slave, void *user);
/** registered configuration function PO->SO */
int (*PO2SOconfigx)(ecx_contextt * context, uint16 slave);
/** user data for PO2SOconfig */
void* user;
/** 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];
uint16 dcoffset[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*/
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);
/** flag to control legacy automatic state change or manual state change */
int manualstatechange;
/** userdata, promotes application configuration esp. in EC_VER2 with multiple
* ec_context instances. Note: userdata memory is managed by application, not SOEM */
void *userdata;
};
#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);
uint32 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);
uint32 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
+375
View File
@@ -0,0 +1,375 @@
/*
* 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;
}
/** Convert an error to text string.
*
* @param[in] Ec = Struct describing the error.
* @return readable string
*/
char* ecx_err2string(const ec_errort Ec)
{
char timestr[20];
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;
}
/** 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;
if (ecx_poperror(context, &Ec))
{
return ecx_err2string(Ec);
}
else
{
return "";
}
}
#ifdef EC_VER1
char* ec_elist2string(void)
{
return ecx_elist2string(&ecx_context);
}
#endif
+34
View File
@@ -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 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* ec_mbxerror2string( uint16 errorcode);
char* ecx_err2string(const ec_errort Ec);
char* ecx_elist2string(ecx_contextt *context);
#ifdef EC_VER1
char* ec_elist2string(void);
#endif
#ifdef __cplusplus
}
#endif
#endif
+389
View File
@@ -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;
int 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 + MBX_HDR_SET_CNT(cnt); /* 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;
int 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 = (uint16)(psize / maxdata);
}
SoEp->MbxHeader.length = htoes((uint16)(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 + MBX_HDR_SET_CNT(cnt); /* 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, uint32 *Osize, uint32 *Isize)
{
int retVal = 0;
int wkc;
int psize;
uint8 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, uint32 *Osize, uint32 *Isize)
{
return ecx_readIDNmap(&ecx_context, slave, Osize, Isize);
}
#endif
+130
View File
@@ -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, uint32 *Osize, uint32 *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, uint32 *Osize, uint32 *Isize);
#ifdef __cplusplus
}
#endif
#endif
+581
View File
@@ -0,0 +1,581 @@
/*
* 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
#include "osal.h"
#ifdef USE_SOEM_CONFIG_H
#include "soem_config.h"
#endif
/** define EC_VER1 if version 1 default context and functions are needed
* define EC_VER2 if application uses only ecx_ functions and own context */
#if !defined(EC_VER1) && !defined(EC_VER2)
# define EC_VER1
#endif
/** Define little endian target by default if no endian is set */
#if !defined(EC_LITTLE_ENDIAN) && !defined(EC_BIG_ENDIAN)
# define EC_LITTLE_ENDIAN
#endif
/** return value no frame returned */
#define EC_NOFRAME -1
/** return value unknown frame received */
#define EC_OTHERFRAME -2
/** return value general error */
#define EC_ERROR -3
/** return value too many slaves */
#define EC_SLAVECOUNTEXCEEDED -4
/** return value request timeout */
#define EC_TIMEOUT -5
/** 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
#ifndef USE_SOEM_CONFIG_H
/** timeout value in us for tx frame to return to rx */
#define EC_TIMEOUTRET 2000
/** 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
#endif
/** 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_SN = 0x000e,
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 */
/** Set the count value in the Mailbox header */
#define MBX_HDR_SET_CNT(cnt) ((uint8)((cnt) << 4))
/** 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 */