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() in CO_Emergency.c

  • CO_HBcons_monitoredNodeConfig() in CO_HBconsumer.c, one for each monitored node

  • CO_LSSmaster_init() in CO_LSSmaster.c number not investigated

  • CO_LSSslave_init() in CO_LSSslave.c number not investigated

  • CO_NMT_init() in CO_NMT_Heartbeat.c

  • CO_RPDO_init() in CO_PDO.c, called once for each RPDO by CO_CANopenInitPDO() in CANopen.c

  • CO_SDO_init() in CO_SDO.c

  • CO_SDOclient_setup() in CO_SDOmaster.c, one for each SDO client

  • CO_SYNC_init() in CO_SYNC.c

  • CO_TIME_init() in CO_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

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() or OD_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