Add basic ENI support

Change-Id: I6abcb3b4a1248acadbbe2ed93fcd901304320d02
This commit is contained in:
Mattias Nilsson
2025-07-09 17:21:28 +02:00
committed by Hans-Erik Floryd
parent 08064ae3fa
commit 2eec21727e
7 changed files with 590 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
/** \file
\section eni Basic ENI support
With the supplied Python script "eniconv.py", it is possible to extract the CoE
initialization commands from an ENI file, and make them available to SOEM, as a
C file that can be compiled and linked together with the SOEM application.
The generated C file defines the (extern) variable "ec_eni", which can be used
to initializethe "ENI" field of the appropriate "ecx_contextt" instance, before
calling the "ec_config_map" function (or equivalent, depending on the
application).
Currently, the main goal of this is to support PDO mapping and assignment.
This means that some information that is mandatory in an ENI file is simply
ignored by the conversion program. The included "sample-eni.xml" minimal ENI
file example has been provided, together with the generated "sample-eni.c" file,
for a quick review of what gets extracted, and how it ends up as C data.
*/
+101
View File
@@ -0,0 +1,101 @@
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatmain.h"
static uint8 s1_coeData[6] = {
0,
2, 26,
5, 26,
2
};
static ec_enicoecmdt s1_coeCmds[4] = {
{
// Clear SM3 PDO count
.Transition = ECT_ESMTRANS_PS,
.CA = FALSE,
.Ccs = 2,
.Index = 0x1C13,
.SubIdx = 0,
.Timeout = 3000,
.DataSize = 1,
.Data = (s1_coeData + 0)
},
{
// SM3 RxPDO #1: 0x1A02
.Transition = ECT_ESMTRANS_PS,
.CA = FALSE,
.Ccs = 2,
.Index = 0x1C13,
.SubIdx = 1,
.Timeout = 3000,
.DataSize = 2,
.Data = (s1_coeData + 1)
},
{
// SM3 RxPDO #2: 0x1A05
.Transition = ECT_ESMTRANS_PS,
.CA = FALSE,
.Ccs = 2,
.Index = 0x1C13,
.SubIdx = 2,
.Timeout = 3000,
.DataSize = 2,
.Data = (s1_coeData + 3)
},
{
// Set SM3 PDO count
.Transition = ECT_ESMTRANS_PS,
.CA = FALSE,
.Ccs = 2,
.Index = 0x1C13,
.SubIdx = 0,
.Timeout = 3000,
.DataSize = 1,
.Data = (s1_coeData + 5)
}
};
static uint8 s2_coeData[8] = {
3, 0, 1, 22, 7, 22, 28, 22
};
static ec_enicoecmdt s2_coeCmds[1] = {
{
// SM2 PDO assignment, using CA
.Transition = ECT_ESMTRANS_PS,
.CA = TRUE,
.Ccs = 2,
.Index = 0x1C12,
.SubIdx = 0,
.Timeout = 3000,
.DataSize = 8,
.Data = (s2_coeData + 0)
}
};
static ec_enislavet eniSlave[2] = {
{
.Slave = 1,
.VendorId = 1,
.ProductCode = 4660,
.RevisionNo = 0,
.CoECmds = s1_coeCmds,
.CoECmdCount = 4
},
{
.Slave = 2,
.VendorId = 1,
.ProductCode = 16799146,
.RevisionNo = 0,
.CoECmds = s2_coeCmds,
.CoECmdCount = 1
}
};
ec_enit ec_eni = {
.slave = eniSlave,
.slavecount = 2
};
+107
View File
@@ -0,0 +1,107 @@
<?xml version="1.0"?>
<EtherCATConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="EtherCATConfig.xsd" Version="1.3">
<Config>
<Master>
<Info>
<Name><![CDATA[SOEM sample master]]></Name>
<Destination>ffffffffffff</Destination>
<Source>010101010101</Source>
</Info>
</Master>
<Slave>
<Info>
<Name><![CDATA[Sample device A]]></Name>
<VendorId>1</VendorId>
<ProductCode>0x1234</ProductCode>
<RevisionNo>0</RevisionNo>
<SerialNo>0</SerialNo>
</Info>
<Mailbox>
<Send>
<Start>0x1000</Start>
<Length>128</Length>
</Send>
<Receive>
<Start>0x1100</Start>
<Length>128</Length>
<PollTime>5</PollTime>
</Receive>
<Protocol>CoE</Protocol>
<CoE>
<InitCmds>
<InitCmd>
<Comment><![CDATA[Clear SM3 PDO count]]></Comment>
<Transition>PS</Transition>
<Timeout>3</Timeout>
<Ccs>2</Ccs>
<Index>0x1c13</Index>
<SubIndex>0</SubIndex>
<Data>00</Data>
</InitCmd>
<InitCmd>
<Comment><![CDATA[SM3 RxPDO #1: 0x1A02]]></Comment>
<Transition>PS</Transition>
<Timeout>3</Timeout>
<Ccs>2</Ccs>
<Index>0x1c13</Index>
<SubIndex>1</SubIndex>
<Data>021a</Data>
</InitCmd>
<InitCmd>
<Comment><![CDATA[SM3 RxPDO #2: 0x1A05]]></Comment>
<Transition>PS</Transition>
<Timeout>3</Timeout>
<Ccs>2</Ccs>
<Index>0x1c13</Index>
<SubIndex>2</SubIndex>
<Data>051a</Data>
</InitCmd>
<InitCmd>
<Comment><![CDATA[Set SM3 PDO count]]></Comment>
<Transition>PS</Transition>
<Timeout>3</Timeout>
<Ccs>2</Ccs>
<Index>0x1c13</Index>
<SubIndex>0</SubIndex>
<Data>02</Data>
</InitCmd>
</InitCmds>
</CoE>
</Mailbox>
</Slave>
<Slave>
<Info>
<Name><![CDATA[Sample device B]]></Name>
<VendorId>1</VendorId>
<ProductCode>0x10055aa</ProductCode>
<RevisionNo>0</RevisionNo>
<SerialNo>0</SerialNo>
</Info>
<Mailbox>
<Send>
<Start>0x1000</Start>
<Length>256</Length>
</Send>
<Receive>
<Start>0x1200</Start>
<Length>256</Length>
<PollTime>5</PollTime>
</Receive>
<Protocol>CoE</Protocol>
<CoE>
<InitCmds>
<InitCmd CompleteAccess="1">
<Comment><![CDATA[SM2 PDO assignment, using CA]]></Comment>
<Transition>PS</Transition>
<Timeout>3</Timeout>
<Ccs>2</Ccs>
<Index>0x1c12</Index>
<SubIndex>0</SubIndex>
<Data>0300011607161c16</Data>
</InitCmd>
</InitCmds>
</CoE>
</Mailbox>
</Slave>
</Config>
</EtherCATConfig>
+57
View File
@@ -339,6 +339,60 @@ typedef struct ec_group
ec_mbxqueuet mbxtxqueue;
} ec_groupt;
#define ECT_ESMTRANS_IP 0x0001
#define ECT_ESMTRANS_PS 0x0002
#define ECT_ESMTRANS_PI 0x0004
#define ECT_ESMTRANS_SP 0x0008
#define ECT_ESMTRANS_SO 0x0010
#define ECT_ESMTRANS_SI 0x0020
#define ECT_ESMTRANS_OS 0x0040
#define ECT_ESMTRANS_OP 0x0080
#define ECT_ESMTRANS_OI 0x0100
#define ECT_ESMTRANS_IB 0x0200
#define ECT_ESMTRANS_BI 0x0400
#define ECT_ESMTRANS_II 0x0800
#define ECT_ESMTRANS_PP 0x1000
#define ECT_ESMTRANS_SS 0x2000
/** ENI CoE command structure */
typedef struct ec_enicoecmd
{
/** transition(s) during which command should be sent */
uint16 Transition;
/** complete access flag */
boolean CA;
/** ccs (1 = read, 2 = write) */
uint8 Ccs;
/** object index */
uint16 Index;
/** object subindex */
uint8 SubIdx;
/** timeout in us */
int Timeout;
/** size in bytes of parameter buffer */
int DataSize;
/** pointer to parameter buffer */
void *Data;
} ec_enicoecmdt;
/** ENI slave structure */
typedef struct ec_enislave
{
uint16 Slave;
uint32 VendorId;
uint32 ProductCode;
uint32 RevisionNo;
ec_enicoecmdt *CoECmds;
int CoECmdCount;
} ec_enislavet;
/** ENI structure */
typedef struct ec_eni
{
ec_enislavet *slave;
int slavecount;
} ec_enit;
/** SII FMMU structure */
typedef struct ec_eepromFMMU
{
@@ -487,6 +541,8 @@ struct ecx_context
ec_eepromFMMUt *eepFMMU;
/** internal, mailbox pool */
ec_mbxpoolt *mbxpool;
/** network information hook */
ec_enit *ENI;
/** registered FoE hook */
int (*FOEhook)(uint16 slave, int packetnumber, int datasize);
/** registered EoE hook */
@@ -522,6 +578,7 @@ int ecx_mbxhandler(ecx_contextt *context, uint8 group, int limit);
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);
int ecx_mbxENIinitcmds(ecx_contextt *context, uint16 slave, uint16_t transition);
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);
+231
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -525,6 +525,10 @@ static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, int thread_n)
EC_PRINT(" >Slave %d, configadr %x, state %2.2x\n",
slave, context->slavelist[slave].configadr, context->slavelist[slave].state);
if (context->ENI)
{
(void)ecx_mbxENIinitcmds(context, slave, ECT_ESMTRANS_PS);
}
/* execute slave configuration hook Pre-Op to Safe-OP */
if (context->slavelist[slave].PO2SOconfig) /* only if registered */
{
@@ -1642,6 +1646,10 @@ int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)
state = ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* check state change pre-op */
if (state == EC_STATE_PRE_OP)
{
if (context->ENI)
{
(void)ecx_mbxENIinitcmds(context, slave, ECT_ESMTRANS_PS);
}
/* execute slave configuration hook Pre-Op to Safe-OP */
if (context->slavelist[slave].PO2SOconfig) /* only if registered */
{
+67
View File
@@ -1644,6 +1644,73 @@ int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft **mbx, int ti
return wkc;
}
/** Send ENI mailbox protocol initcmds to a slave for a given transition.
* Currently, only CoE commands are supported.
* @param[in] context = context struct
* @param[in] slave = Slave number
* @param[in] transition = transition (ECT_ESMTRANS_*) for which to send commands
* @return 1 on success, 0 on failure
*/
int ecx_mbxENIinitcmds(ecx_contextt *context, uint16 slave, uint16_t transition)
{
int slavecount = (context->ENI ? context->ENI->slavecount : 0);
if (slavecount > 0)
{
int i;
ec_enislavet *eni_slave = context->ENI->slave;
for (i = 0; i < (slavecount - 1); ++i, ++eni_slave)
{
if (slave <= eni_slave->Slave)
{
break;
}
}
if (slave == eni_slave->Slave)
{
if ((eni_slave->VendorId == context->slavelist[slave].eep_man) &&
(eni_slave->ProductCode == context->slavelist[slave].eep_id) &&
(eni_slave->RevisionNo == context->slavelist[slave].eep_rev))
{
int wkc;
if (context->slavelist[slave].mbx_proto & ECT_MBXPROT_COE)
{
ec_enicoecmdt *cmd = eni_slave->CoECmds;
for (i = 0; i < eni_slave->CoECmdCount; ++i, ++cmd)
{
if (cmd->Transition & transition)
{
if (cmd->Ccs == 2)
{
wkc = ecx_SDOwrite(context, slave, cmd->Index, cmd->SubIdx,
cmd->CA, cmd->DataSize, cmd->Data, cmd->Timeout);
if (wkc < 1)
{
return 0;
}
}
else if (cmd->Ccs == 1)
{
int size = cmd->DataSize;
wkc = ecx_SDOread(context, slave, cmd->Index, cmd->SubIdx,
cmd->CA, &size, cmd->Data, cmd->Timeout);
if (wkc < 1)
{
return 0;
}
}
}
}
}
}
}
}
return 1;
}
/** Dump complete EEPROM data from slave in buffer.
* @param[in] context = context struct
* @param[in] slave = Slave number