|
Lely core libraries
1.9.2
|
Contrary to most other CANopen stacks, this implementation is completely passive; the library does not perform any I/O (besides may reading some files from disk), it does not create threads nor does it access the system clock. Instead, it relies on the user to send and receive CAN frames and update the clock. This allows the library to be easily embedded in a wide variety of applications.
The library is also asynchronous. Issuing a request is always a non-blocking operation. If the request is confirmed, the API accepts a callback function which is invoked once the request completes (with success or failure). This allows the stack to run in a single thread, even when processing dozens of simultaneous requests (not uncommon for an NMT master).
The interface between the CANopen stack and the CAN bus (and system clock) is provided by the CAN network object (can_net_t/CANNet) from the Lely CAN library (liblely-can). When the CANopen stack needs to send a CAN frame, it hands it over to a CAN network object, which in turn invokes a user-defined callback function to write the frame to the bus. Similarly, when a user reads a CAN frame from the bus, he gives it to a CAN network object, which in turn distributes it to the registered receivers in the CANopen stack. Additionally, the user periodically checks the current time and tells the CAN network object, which then executes the actions for timers that have elapsed.
Sometimes a CANopen application runs on a device which does not have direct access to a CAN bus. CiA 315 defines a protocol for tunneling CAN frames over wired or Wireless Transmission Media (WTM). The interface for this protocol can be found in lely/co/wtm.h (co_wtm_t) and lely/co/wtm.hpp (lely::COWTM). Additionally, the can2udp tool can be run as a daemon or service to act as a proxy between a CAN bus and UDP.
The first task when writing a CANopen application is connecting a CAN network object to the CAN bus. The following example shows how to do this using the Lely I/O library (liblely-io), which provides a platform-independent interface to the CAN bus.
C example:
C++11 example:
The object dictionary is the central concept of the CANopen protocol. It contains almost the entire state of a CANopen device, including process data and communication parameters. Communication between nodes consists primarily of reading from and writing to each others object dictionary. Even writing CANopen applications is mostly a matter of configuring the object dictionary.
Together with the Node-ID, the object dictionary is managed by a CANopen device object (co_dev_t/lely::CODev). Although it is possible to construct the object dictionary from scratch with the C and C++ API, it is more convenient to read it from an Electronic Data Sheet (EDS) or Device Configuration File (DCF) (see CiA 306). Functions to parse EDS/DCF files can be found in lely/co/dcf.h and lely/co/dcf.hpp.
Embedded devices often do not have the resources to parse an EDS/DCF file at runtime. Using the dcf2c tool it is possible to create a C file containing a static object dictionary, which can be compiled with the application. The static object dictionary can then be converted to a dynamic one at runtime (see lely/co/sdev.h and lely/co/sdev.hpp).
The CANopen stack implements the following services:
While it is possible to create these services by hand, it is almost always more convenient to let them be managed by a single Network Management (NMT) service object (co_nmt_t/lely::CONMT). An NMT object manages the state of a CANopen device and creates and destroys the other services as needed.
Like all CANopen services, the NMT service gets its configuration from the object dictionary. A typical CANopen application therefore consists of
Note, however, that a newly created NMT service starts out in the 'Initialisation' state and does not create any services or perform any communication. This allows the application to register callback functions before the node becomes operational. The NMT service, including the entire boot-up sequence, can be started by giving it the RESET NODE command.
The following example is a minimal CANopen application. Depending on the EDS/DCF file, it can be a simple slave that does nothing besides producing a heartbeat message, or a full-fledged master managing a network of slaves (including automatic firmware upgrades!).
C example:
C++11 example:
As mentioned before, the CANopen stack is asynchronous. Requests return immediately, but take a callback function which is invoked once the request succeeds (or fails). See the documentation of the Lely utilities library (liblely-util) for a description of the general callback interface.
As an example, define the following callback function for reading a 32-bit unsigned integer from a remote object dictionary:
Before the main event loop we obtain a Client-SDO service object from the NMT service and issue the request:
The upReq() function returns immediately; onUp() is called from the main event loop when the request succeeds (or fails).
This is an example for a Client-SDO requests, but all services follow this pattern. Note that by the time the callback function is called, the request has been completed. It is therefore possible to issue a new request directly from the callback function. This pattern is used extensively by the master when booting slaves.