CANopenNode
Zephyr’s branch on CANopenNode is currently outdated comapring to the latest version of CANopenNode, and the CANopenEditor does not support this legacy version of CANopenNode (it has a legacy exporter, but the generated code lacks some type definitions that it can’t compile).
Also, CANopen itself as a protocol controlled by CAN in Automation (CiA) is not that really that open as a lot of the specifications are not free. For example, now I need CiA 302 and it costs 512 Euros.
However, since CANopen provides the necessary application layer over the original CAN protocol that only covers the transmission layer, with the legacy Zephyr integration of CANopenNode available, it’s too good to not use it. We maintain a fork of Zephyr that contains the modifications necessary to use the latest version of CANopenNode.
Here are some notes for using the latest version of CANopenNode in Zephyr:
CAN Reception
Callbacks
CAN bus driver in Zephyr uses hardware filters to filter out messages, and only
messages that pass the filter will be received by the application using callback
functions from an interrupt context [1]. Since callbacks are called from ISR,
caution must be taken when setting callbacks related to CAN bus reception in
CANopenNode. For example, CO_EM_initCallbackRx()
and
CO_NMT_initCallback()
both execute in the ISR context, please
investigate the source code to see what context the callback is executed.
Filters
Filters are added using CO_CANrxBufferInit()
defined in CO_driver.c
.
The following is a list of filters added by CANopenNode:
CO_EM_init()
inCO_Emergency.c
CO_HBcons_monitoredNodeConfig()
inCO_HBconsumer.c
, one for each monitored nodeCO_LSSmaster_init()
inCO_LSSmaster.c
number not investigatedCO_LSSslave_init()
inCO_LSSslave.c
number not investigatedCO_NMT_init()
inCO_NMT_Heartbeat.c
CO_RPDO_init()
inCO_PDO.c
, called once for each RPDO byCO_CANopenInitPDO()
inCANopen.c
CO_SDO_init()
inCO_SDO.c
CO_SDOclient_setup()
inCO_SDOmaster.c
, one for each SDO clientCO_SYNC_init()
inCO_SYNC.c
CO_TIME_init()
inCO_TIME.c
Note
Since STM32 series with FDCAN only supports up to 28 standard and 8 extended ID filters [2], caution must be taken when configuring CANopenNode.
References
Object Dictionary (OD)
Each OD entry can be read/written by SDO via a callback function registered by OD_extension_init() instead of straightforwardly from/to the memory. However, CO_CONFIG_PDO_OD_IO_ACCESS should be set to enable the same behavior for PDOs.
Note
SDO and PDO internally get the OD entry read/write APIs are via
OD_getSub()
. However, SDO calls OD_getSub()
everytime a
request is processed [3], so the newly registered callback functions will
be used. On the other hand, PDOs only call OD_getSub()
once when
initialized [4]. So in order to make PDO use the callback functions to
access OD entries, the callbacks should be registered before PDOs are
initialized.
CANopenNode already registered some common OD entries to provide functionalities according to the CiA 301 standard. The following is a list of registered ODs:
0x1003: Pre-defined error field
0x1005: COB-ID SYNC message
0x100C: Guard time
0x100D: Life time factor
0x1010: Store parameters
0x1011: Restore default parameters
0x1012: COB-ID time stamp object
0x1014: COB-ID EMCY
0x1015: Inhibit time EMCY
0x1016: Consumer heartbeat time
0x1017: Producer heartbeat time
0x1019: Synchronous counter overflow value
0x1200: SDO server parameter
0x1400 to 0x15FF: RPDO communication parameter
0x1600 to 0x17FF: RPDO mapping parameter
0x1800 to 0x19FF TPDO communication parameter
0x1A00 to 0x1BFF TPDO mapping parameter
References
CO_SDOserver_process() source code
that calls OD_getSub()
to get the read/write APIs.
PDOconfigMap() source code that is called when TPDOs and RPDOs are initialized.
Process Data Objectss (PDOs)
Receive PDOs (RPDOs)
OD 0x1400 to 0x15FF define the communication parameters of RPDOs, here are the overview of them and the implementation of CANopenNode:
Transmission type (sub-index 0x02): There are two types of reception;
Synchronous (0x00 to 0xF0): The received data will be actuated after a SYNC message is received.
Event-Driven (0xFE, 0xFF): The received data will be actuated immediately.
Here actuated means the received data will be copied to the mapped OD entries defined in the RPDO mapping parameter (OD 0x1600 to 0x17FF) or the registered callback function will be called to process the received data.
Event-timer (sub-index 0x05): Define the deadline for the reception of RPDOs. If the RPDO is not received before the event-timer expires, CANopen error
RPDO timeout (0x8250)
will be reported. And if the RPDO is received after the event-timer expires, the error will be cleared and the timer will be reset.
Note
If CO_CONFIG_PDO_SYNC_ENABLE is not set, the received synchronous RPDOs will be actuated immediately.
Transmit PDOs (TPDOs)
OD 0x1800 to 0x19FF define the communication parameters of TPDOs, here are the overview of them and the implementation of CANopenNode:
Transmission type (sub-index 0x02): There are two types of transmission;
Synchronous (0x00 to 0xF0): Transmitted after a SYNC message is received. There are also two sub-types of synchronous transmission;
Acyclic (0x00): Transmitted when receiving the next SYNC message after requested by an event.
Cyclic (0x01 to 0xF0): Transmitted after Nth SYNC message is received, where N is the value of the transmission type.
Event-Driven (0xFE, 0xFF): Transmitted by an event.
Here event means transmission is requested by the application via
CO_TPDOsendRequest()
orOD_requestTPDO()
or when the event timer (sub-index 0x05) expires.Inhibit time (sub-index 0x03): Define the minimum time between two consecutive transmissions of event-driven TPDOs.
SYNC start value (sub-index 0x06): Define when the TPDO will start being transmitted after the SYNC counter is equal to the SYNC start value.
Note
If CO_CONFIG_PDO_SYNC_ENABLE is not set, synchronous and cyclic TPDOs will not be transmitted.
Error Handling
Error status bits
In addition to the standard CANopen error codes defined in CiA 301, CANopenNode
defines a set of error status bits
that can be used to indicate what errors are currently happening in the node.
When an error is reported or cleared using CO_error()
, the error status
bits will be set or cleared accordingly and if the same bit is already set or
cleared, no processing will happen. Such mutually exclusivity effectively making
the error status bits
the real error being tracked of and the CANopen error
codes being the additional information of the error. The number of error status
bits is defined by CO_CONFIG_EM_ERR_STATUS_BITS_COUNT.
If error status bits
are needed to be accessed via the object dictionary,
CO_CONFIG_EM_STATUS_BITS
should be set as well as define a OD entry Error status bits
of type
OCTET_STRING
with length of CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 4. You
are responsible for defining the OD entry and register it to CANopenNode using
CO_EM_init()
.
Error register
CANopenNode also manages Error register
of OD 0x1001 via a set of
CO_CONFIG_ERR_CONDITION_*
macros based on the error status bits
. However, the default behavior only
sets the generic bit when error status bits
between 0x28
to 0x2F
are
set, which does NOT adhere to the CANopen specification stating that: “The
generic error shall be signaled at any error situation [5].”
EMCY write
In order to transmit the error status bits
in the Emergency (EMCY) object,
the first byte of the manufacturer-specific error code is used to store the
error status bit
currently reported, NOT the error status bits
that
are currently set.
The standard CANopen EMCY write payload has the following format [6]:
0 1 2 3 7
+------------+----------------+----------------------------------+
| error code | error register | manufacturer-specific error code |
+------------+----------------+----------------------------------+
And CANopenNode uses the first byte of manufacturer-specific error code (the
byte of index 3) to transmit the reported error status bit
, so the payload
becomes:
0 1 2 3 4 7
+------------+----------------+------------------------------------------------------+
| error code | error register | error status bits | manufacturer-specific error code |
+------------+----------------+------------------------------------------------------+
CANopenNode also recognizes the first byte of manufacturer-specific error code
as error status bit
when receiving EMCY messages from other nodes. The
callback for receiving EMCY registered using CO_EM_initCallbackRx()
has
the prototype:
void pFunctSignalRx(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode);
Where infoCode
is the rest of the manufacturer-specific error code.
Pre-defined error fields
CANopenNode also helps to maintain Pre-defined error fields
of OD 0x1003 for
recording errors that happened if CO_CONFIG_EM_HISTORY
is set. Once an error is reported using CO_error()
, it will be recorded
to Pre-defined error fields
in the following format [7]:
32 24 16 0
+----------------+------+------------+
| error register | 0x00 | error code |
+----------------+------+------------+
MSB LSB
References
CiA 301, section 7.5.2.2 Error register
CiA 301, section 7.2.7.3.1 Protocol EMCY write