CANopenNode
CANopen protocol stack
|
Macros | |
#define | CO_LOCK_CAN_SEND(CAN_MODULE) /**< Lock critical section in CO_CANsend() */ |
Lock critical section in CO_CANsend() | |
#define | CO_UNLOCK_CAN_SEND(CAN_MODULE) /**< Unlock critical section in CO_CANsend() */ |
Unlock critical section in CO_CANsend() | |
#define | CO_LOCK_EMCY(CAN_MODULE) /**< Lock critical section in CO_errorReport() or CO_errorReset() */ |
Lock critical section in CO_errorReport() or CO_errorReset() | |
#define | CO_UNLOCK_EMCY(CAN_MODULE) /**< Unlock critical section in CO_errorReport() or CO_errorReset() */ |
Unlock critical section in CO_errorReport() or CO_errorReset() | |
#define | CO_LOCK_OD(CAN_MODULE) /**< Lock critical section when accessing Object Dictionary */ |
Lock critical section when accessing Object Dictionary. | |
#define | CO_UNLOCK_OD(CAN_MODULE) /**< Unock critical section when accessing Object Dictionary */ |
Unock critical section when accessing Object Dictionary. | |
#define | CO_FLAG_READ(rxNew) ((rxNew) != NULL) |
Check if new message has arrived. | |
#define | CO_FLAG_SET(rxNew) |
Set new message flag. | |
#define | CO_FLAG_CLEAR(rxNew) |
Clear new message flag. | |
Protection of critical sections in multi-threaded operation.
CANopenNode is designed to run in different threads, as described in README.md. Threads are implemented differently in different systems. In microcontrollers threads are interrupts with different priorities, for example. It is necessary to protect sections, where different threads access to the same resource. In simple systems interrupts or scheduler may be temporary disabled between access to the shared resource. Otherwise mutexes or semaphores can be used.
Functions CO_CANsend() from C_driver.h, and CO_error() from CO_Emergency.h may be called from different threads. Critical sections must be protected. Either by disabling scheduler or interrupts or by mutexes or semaphores. Lock/unlock macro is called with pointer to CAN module, which may be used inside.
In general, there are two threads, which accesses OD variables: mainline (initialization, storage, SDO access) and timer (PDO access). CANopenNode uses locking mechanism, where SDO server (or other mainline code) prevents execution of the real-time thread at the moment it reads or writes OD variable. CO_LOCK_OD(CAN_MODULE) and CO_UNLOCK_OD(CAN_MODULE) macros are used to protect:
After CAN message is received, it is pre-processed in CANrx_callback(), which copies some data into appropriate object and at the end sets new_message flag. This flag is then pooled in another thread, which further processes the message. The problem is, that compiler optimization may shuffle memory operations, so it is necessary to ensure, that new_message flag is surely set at the end. It is necessary to use Memory barrier.
If receive function runs inside IRQ, no further synchronization is needed. Otherwise, some kind of synchronization has to be included. The following example uses GCC builtin memory barrier __sync_synchronize()
. More information can be found here.