feat(motor): 自适应 PDO 窗口与通道活动指示,新增原生 SOEM 时序诊断
后端 PDO 循环改为按在线槽收缩状态窗口,稀疏总线下单槽每周期都能刷新, 并补上尾部槽的发现扫描游标;新增自旋节拍源(阈值/显式覆盖)、P 核识别 与线程级钉核,自旋期间释放 GIL 以免 HTTP 线程被饿死;启动时先关停旧实例, 避免两个进程争用 Npcap 适配器。 记录每拍完整时间线、GC stop-the-world 事件与网格跳拍数,经 /api/motor/dbg 导出用于归因固定频率的超时;配
This commit is contained in:
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user