/** \file
\section general General
The SOEM is a library that provides the user application with the means to send
and receive EtherCAT frames. It is up to the application to provide means for:
- Reading and writing process data to be sent/received by SOEM
- Keeping local IO data synchronized with the global IOmap
- Detecting errors reported by SOEM
- Managing errors reported by SOEM
The following sections show some basic examples on how to get the SOEM up
and running, as well as making use of the process data and checking
for errors. Since all code is local to the application or global
variables, it is possible to tweak and optimize when possible.
The following example shows how to add a main function that will be
called by startup code. In this example main's only purpose is to
spawn a new task that executes SOEM.
\code
int main (void)
{
rprintp("SOEM (Simple Open EtherCAT Master)\nSimple test\n");
task_spawn ("simpletest", simpletest, 9, 8192, NULL);
\endcode
\section configuration Configuration
Followed by start of the application we need to set up the NIC to be used as
EtherCAT Ethernet interface. In a simple setup we call ec_init(ifname) and
if SOEM comes with support for cable redundancy we call ec_init_redundant
that will open a second port as backup. You can send NULL as ifname if you
have a dedicated NIC selected in the nicdrv.c. It returns >0 if succeeded.
\code
/* initialise SOEM, bind socket to ifname */
if (ec_init(ifname) > 0)
\endcode
SOEM is a light weight ethercat master library used in embedded systems, It
supports only runtime configuration. It requests a BRD (Broad Cast Read) of
address 0, all fully functional slaves in the network will respond to this
request, and therefore we will get a working counter equal to the number of
slaves in the network. ec_config_init also sets up the mailboxes for slaves
that support it. When ec_config_init finishes it will have requested all slaves
to state PRE_OP. All data read and configured are stored in a global array
which acts as a placeholder for key values, consult ec_slave for detailed
information.
\code
/* find and auto-config slaves */
if ( ec_config_init(FALSE) > 0 )
{
rprintp("%d slaves found and configured.\n",ec_slavecount);
\endcode
SOEM has now discovered and configured the network it is connected to.
Now we can verify that all slaves are present as expected. These
definitions could be generated by an external tool in an offline .h file.
The definitions could be replaced by a struct keeping slave number.
\code
#define EK1100_1 1
#define EL4001_1 2
...
#define EL2622_3 8
#define EL2622_4 9
#define NUMBER_OF_SLAVES 9
snippet
...
uint32 network_configuration(void)
{
/* Do we got expected number of slaves from config */
if (ec_slavecount < NUMBER_OF_SLAVES)
return 0;
/* Verify slave by slave that it is correct*/
if (strcmp(ec_slave[EK1100_1].name,"EK1100"))
return 0;
else if (strcmp(ec_slave[EL4001_1].name,"EL4001"))
return 0;
...
else if (strcmp(ec_slave[EL2622_4].name,"EL2622"))
return 0;
return 1;
}
simpletest
...
if (network_configuration())
...
else
rprintp("Mismatch of network units!\n");
\endcode
We now have the network up and configured. Mailboxes are up for slaves that support
it. Next we will create an IOmap and configure the SyncManager's and
FMMU's to link the EtherCAT master and the slaves. The IO mapping is done
automatically, SOEM strives to keep the logical process image as compact as
possible. It is done by trying to fit Bit oriented slaves together in single
bytes. Below is an example of 8 slaves and how they are ordered. During
mapping SOEM also calculates an expected WKC for the IO mapped together.
That is the primary key to detect errors.
- Outputs are placed together in the beginning of the IOmap
- Inputs follow
When the mapping is done SOEM requests slaves to enter SAFE_OP.
\code
char IOmap[128];
int usedmem;
...
usedmem = ec_config_map(&IOmap);
if (usedmem <= sizeof(IOmap))
...
\endcode
\image html memory_layout.png "memory layout, mapping between physical and logical"
\image latex memory_layout.png "memory layout, mapping between physical and logical" width=15cm
To enter state OP we need to send valid data to outputs. The EtherCAT frame
handling is split into ec_send_processdata and ec_receive_processdata.
- ec_send_processdata sends the frame on the NIC and saves the frame on
the stack for receive to fetch.
- ec_receive_processdata(EC_TIMEOUTRET) tries to fetch the frames on the
stack. We send an argument for how long we will try to fetch the frame.
ec_receive_processdata returns the working counter.
\code
/* send one valid process data to make outputs in slaves happy*/
ec_send_processdata();
wkc = ec_receive_processdata(EC_TIMEOUTRET);
...
ec_writestate(0);
/* wait for all slaves to reach OP state */
ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);
\endcode
- Now we have a system up and running, all slaves are in state operational.
\section configuration_custom Custom Configuration
\subsection iomap_config PDO Assign and PDO Config
Do custom configuration with PDO Assign or PDO Config. SOEM support custom configuration during start via a
PreOP to SafeOP configuration hook. It can be done per slave and should be set before calling
the configuration and mapping of process data, e.g. the call to ec_config_map. Setting the configuration
hook ensure that the custom configuration will be applied when calling recover and re-configuration
of a slave, as described below.
\code
int EL7031setup(uint16 slave)
{
int retval;
uint16 u16val;
retval = 0;
/* Map velocity PDO assignment via Complete Access*/
uint16 map_1c12[4] = {0x0003, 0x1601, 0x1602, 0x1604};
uint16 map_1c13[3] = {0x0002, 0x1a01, 0x1a03};
retval += ec_SDOwrite(slave, 0x1c12, 0x00, TRUE, sizeof(map_1c12), &map_1c12, EC_TIMEOUTSAFE);
retval += ec_SDOwrite(slave, 0x1c13, 0x00, TRUE, sizeof(map_1c13), &map_1c13, EC_TIMEOUTSAFE);
/* set some motor parameters, just as example */
u16val = 1200; // max motor current in mA
retval += ec_SDOwrite(slave, 0x8010, 0x01, FALSE, sizeof(u16val), &u16val, EC_TIMEOUTSAFE);
u16val = 150; // motor coil resistance in 0.01ohm
retval += ec_SDOwrite(slave, 0x8010, 0x04, FALSE, sizeof(u16val), &u16val, EC_TIMEOUTSAFE);
/* set other necessary parameters as needed */
...
printf("EL7031 slave %d set, retval = %d\n", slave, retval);
return 1;
}
void simpletest(char *ifname)
{
...
/* Detect slave beckhoff EL7031 from vendor ID and product code */
if((ec_slave[slc].eep_man == 0x00000002) && (ec_slave[slc].eep_id == 0x1b773052))
{
printf("Found %s at position %d\n", ec_slave[slc].name, slc);
/* link slave specific setup to preop->safeop hook */
ec_slave[slc].PO2SOconfig = EL7031setup;
}
...
\endcode
\subsection iomap_layout Legacy versus overlapping IOmap
IOmap options legacy versus overlapping. Overlapping IOmap was introduced to handle
the TI ESC that doesn't support RW access to non-interleaved input and output process
data of multiple slaves. The difference is that legacy IOmapping will send IOmap as is
on the EtherCAT network while the overlapping will re-use logic addressing per slave to
replace RxPDO process data coming from the Master with TxPDO process data generated by the slave
sent back to the master.
Overview of legacy pdo map
\image html legacy_iomap.png "Legacy IOmapping"
\image latex legacy_iomap.png "Legacy IOmapping" width=15cm
Overview of overlapping pdo map
\image html overlapping_iomap.png "Overlapping IOmapping"
\image latex overlapping_iomap.png "Overlapping IOmapping" width=15cm
\subsection iomap_groups EtherCAT slave groups
Slave groups can be used to group slaves into se