The firmware of a PLC slice
Beckhoff slices and POD's generally handle some tasks locally without the intervention of the main controller. They store their own parameters and execute some predetermined low-level logic, allowing a higher level interaction with the PLC. These parameters are referred to as the CoE table, and they are crucial for the proper operation of IO interfaces.
What is the CoE?
CANopen over EtherCAT (CoE) is a way to use the CANopen protocol over an EtherCAT network. It allows devices that use the CANopen protocol to be integrated into an EtherCAT network, taking advantage of the benefits of both protocols.
CANopen provides a standardized way to communicate with devices on a CAN network, defining a set of communication objects and services that allow for easy configuration, control, and monitoring of devices. It supports many different types of devices, including sensors, actuators, and motor drives.
EtherCAT, on the other hand, is designed for high-speed, real-time communication between devices in a network. It uses a master/slave architecture, with a central EtherCAT master controlling multiple EtherCAT slaves. The master sends out control commands and receives feedback from the slaves, allowing for precise, synchronized control of multiple devices.
CoE defines a mapping of CANopen objects and services onto the EtherCAT protocol, allowing devices to communicate using CANopen messages over the EtherCAT network.
Overall, CANopen over EtherCAT is a powerful combination of two widely used communication protocols in industrial automation, providing flexible, standardized communication and real-time control for a wide range of devices.
How to read/write the CoE table manually
The picture below shows an IO tree with an EL7031 (stepper motor drive) connected to an EK1100 (EtherCAT adapter). Let's say that you need to replace the motor. You can check what maximal current the EL7031 driver was set for by clicking on its IO tree icon, navigating to the tab 'CoE - Online', and checking the parameter 8010.1 'Maximal current'.
Note: When reading, make sure that you unselect 'Show Offline Data'. Otherwise you may be looking at some default value from your IDE. Make sure that either 'Auto Update' or 'Single Update' is checked.
If a parameter is flagged as RW, that means that it can be read and written. Double click on it, prepare the new value, and click OK to write.
How to read/write the CoE table programmatically
The following code is a state machine that manages the sequence. You will need to provide some inputs such as:
sNetId: This is the AMS NET ID of your EtherCAT device. You can find it by right clicking on your EtherCAT device on your IO tree and selecting 'Change Net Id'. That will show you what the current number is. Note: You need to pass it as a string to the function block (e.g., '169.254.38.55.3.1').
nSlaveAddr: This is the slave address of your device. Click on the device on your IO tree and go to the tab EtherCAT to find this number (e.g., 1002).
nIndex and nIndexOffset: These are the index and the subindex of the CoE parameter you want to read (e.g., 8010, e.g., 01 respectively).
The Code
FUNCTION_BLOCK FB_ReadCoE
VAR_INPUT
sNetId : T_AmsNetId;
nSlaveAddr : UINT;
nIndex : WORD;
nIndexOffset : BYTE;
END_VAR
VAR_OUTPUT
nParameter : INT;
bDoneReading : BOOL;
bError : BOOL;
END_VAR
VAR
bExecute : BOOL;
fbCoERead : FB_EcCoESDoRead;
eCoEReadState : (INIT, TRIGGER, READ, DONE);
END_VAR
CASE eCoEReadState OF
INIT:
bDoneReading := FALSE;
bExecute := FALSE;
eCoEReadState := TRIGGER;
TRIGGER:
bExecute := TRUE;
eCoEReadState := READ;
READ:
fbCoERead(
sNetId:=sNetId,
nSlaveAddr:=nSlaveAddr,
nSubIndex:=nIndexOffset,
nIndex:=nIndex,
pDstBuf:=ADR(nParameter),
cbBufLen:=SIZEOF(nParameter),
bExecute:=bExecute);
bExecute := NOT bExecute;
IF NOT fbCoeRead.bBusy THEN
eCoEReadState := DONE;
END_IF
DONE:
IF fbCoeRead.bError THEN
bError := TRUE;
ELSE
bError := FALSE;
END_IF
bDoneReading := TRUE;
eCoEReadState := INIT;
END_CASE
A very similar process can be used to write to the CoE.
FUNCTION_BLOCK FB_WriteCoE
VAR_INPUT
sNetId : T_AmsNetId;
nSlaveAddr : UINT;
nIndex : WORD;
nIndexOffset : BYTE;
nParameter : INT;
END_VAR
VAR_OUTPUT
bDoneWriting : BOOL;
bError : BOOL;
END_VAR
VAR
bExecute : BOOL;
fbCoeWrite : FB_EcCoEsDoWrite;
eCoEWriteState : (INIT, TRIGGER, WRITE, DONE);
END_VAR
CASE eCoEWriteState OF
INIT:
bDoneWriting := FALSE;
bExecute := FALSE;
eCoEWriteState := TRIGGER;
TRIGGER:
bExecute := TRUE;
eCoEWriteState := WRITE;
WRITE:
fbCoeWrite(
sNetId:=sNetId,
nSlaveAddr:=nSlaveAddr,
nSubIndex:=nIndexOffset,
nIndex:=nIndex,
pSrcBuf:=ADR(nParameter),
cbBufLen:=SIZEOF(nParameter),
bExecute:=bExecute);
bExecute := NOT bExecute;
IF NOT fbCoeWrite.bBusy THEN
eCoeWriteState := DONE;
END_IF
DONE:
IF fbCoeWrite.bError THEN
bError := TRUE;
ELSE
bError := FALSE;
END_IF
bDoneWriting := TRUE;
eCoEWriteState := INIT;
END_CASE
Conclusion
One inconvenient aspect of this process is that you need to know the data type of your parameter beforehand.
That is because the source buffer needs to point to a variable, and the data type for that variable is only defined during its declaration. In other words, if you're reading a LREAL parameter, you would need to declare an fParameter variable of type LREAL to pass as a reference to your buffer (pSrcBuf := fParameter). This is especially frustrating if you're coming from a Python background.
Finally, devices have a limit on the number of times you can write. If you write too many times, you can damage your device memory chip for good. This is only a risk if you are writing programmatically since it takes several thousand writes to damage the hardware, but it is something to keep in mind when you are writing your code — a poorly implemented sequence can ruin your day.
Comments