658 lines
32 KiB
C
658 lines
32 KiB
C
/**
|
|
* Interface between CAN hardware and CANopenNode.
|
|
*
|
|
* @file CO_driver.h
|
|
* @ingroup CO_driver
|
|
* @author Janez Paternoster
|
|
* @copyright 2004 - 2020 Janez Paternoster
|
|
*
|
|
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
|
* file except in compliance with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
|
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and limitations under the License.
|
|
*/
|
|
|
|
#ifndef CO_DRIVER_H
|
|
#define CO_DRIVER_H
|
|
|
|
#include <string.h>
|
|
|
|
#include "CO_config.h"
|
|
#include "CO_driver_target.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Stack configuration default global values. For more information see file CO_config.h. */
|
|
#ifndef CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE
|
|
#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE (0)
|
|
#endif
|
|
#ifndef CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE
|
|
#define CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE (0)
|
|
#endif
|
|
#ifndef CO_CONFIG_GLOBAL_FLAG_TIMERNEXT
|
|
#define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT (0)
|
|
#endif
|
|
#ifndef CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC
|
|
#define CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC CO_CONFIG_FLAG_OD_DYNAMIC
|
|
#endif
|
|
#ifdef CO_DEBUG_COMMON
|
|
#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_CLIENT
|
|
#define CO_DEBUG_SDO_CLIENT(msg) CO_DEBUG_COMMON(msg)
|
|
#endif
|
|
#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_SERVER
|
|
#define CO_DEBUG_SDO_SERVER(msg) CO_DEBUG_COMMON(msg)
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* @defgroup CO_driver Driver
|
|
* Interface between CAN hardware and CANopenNode.
|
|
*
|
|
* @ingroup CO_CANopen_301
|
|
* @{
|
|
* CANopenNode is designed for speed and portability. It runs efficiently on devices from simple 16-bit microcontrollers
|
|
* to PC computers. It can run in multiple threads. Reception of CAN messages is pre-processed with very fast functions.
|
|
* Time critical objects, such as PDO or SYNC are processed in real-time thread and other objects are processed in
|
|
* normal thread. See Flowchart in [README.md](index.html) for more information.
|
|
*
|
|
* @anchor CO_obj
|
|
* #### CANopenNode Object
|
|
* CANopenNode is implemented as a collection of different objects, for example SDO, SYNC, Emergency, PDO, NMT,
|
|
* Heartbeat, etc. Code is written in C language and tries to be object oriented. So each CANopenNode Object is
|
|
* implemented in a pair of .h/.c files. It basically contains a structure with all necessary variables and some
|
|
* functions which operates on it. CANopenNode Object is usually connected with one or more CAN receive or transmit
|
|
* Message Objects. (CAN message Object is a CAN message with specific 11-bit CAN identifier (usually one fixed or a
|
|
* range).)
|
|
*
|
|
* #### Hardware interface of CANopenNode
|
|
* It consists of minimum three files:
|
|
* - **CO_driver.h** file declares common functions. This file is part of the CANopenNode. It is included from each .c
|
|
* file from CANopenNode.
|
|
* - **CO_driver_target.h** file declares microcontroller specific type declarations and defines some macros, which are
|
|
* necessary for CANopenNode. This file is included from CO_driver.h.
|
|
* - **CO_driver.c** file defines functions declared in CO_driver.h.
|
|
*
|
|
* **CO_driver_target.h** and **CO_driver.c** files are specific for each different microcontroller and are not part of
|
|
* CANopenNode. There are separate projects for different microcontrollers, which usually include CANopenNode as a git
|
|
* submodule. CANopenNode only includes those two files in the `example` directory and they are basically empty. It
|
|
* should be possible to compile the `CANopenNode/example` on any system, however compiled program is not usable.
|
|
* CO_driver.h contains documentation for all necessary macros, types and functions.
|
|
*
|
|
* See [CANopenNode/Wiki](https://github.com/CANopenNode/CANopenNode/wiki) for a known list of available implementations
|
|
* of CANopenNode on different systems and microcontrollers. Everybody is welcome to extend the list with a link to his
|
|
* own implementation.
|
|
*
|
|
* Implementation of the hardware interface for specific microcontroller is not always an easy task. For reliable and
|
|
* efficient operation it is necessary to know some parts of the target microcontroller in detail (for example threads
|
|
* (or interrupts), CAN module, etc.).
|
|
*/
|
|
|
|
/** Major version number of CANopenNode */
|
|
#define CO_VERSION_MAJOR 4
|
|
/** Minor version number of CANopenNode */
|
|
#define CO_VERSION_MINOR 0
|
|
|
|
/* Macros and declarations in following part are only used for documentation. */
|
|
#ifdef CO_DOXYGEN
|
|
/**
|
|
* @defgroup CO_dataTypes Basic definitions
|
|
* @{
|
|
*
|
|
* Target specific basic definitions and data types.
|
|
*
|
|
* Must be defined in the **CO_driver_target.h** file.
|
|
*
|
|
* Depending on processor or compiler architecture, one of the two macros must be defined: CO_LITTLE_ENDIAN or
|
|
* CO_BIG_ENDIAN. CANopen itself is little endian.
|
|
*
|
|
* Basic data types may be specified differently on different architectures. Usually `true` and `false` are defined in
|
|
* `<stdbool.h>`, `NULL` is defined in `<stddef.h>`, `int8_t` to `uint64_t` are defined in `<stdint.h>`.
|
|
*/
|
|
#define CO_LITTLE_ENDIAN /**< CO_LITTLE_ENDIAN or CO_BIG_ENDIAN must be defined */
|
|
#define CO_SWAP_16(x) x /**< Macro must swap bytes, if CO_BIG_ENDIAN is defined */
|
|
#define CO_SWAP_32(x) x /**< Macro must swap bytes, if CO_BIG_ENDIAN is defined */
|
|
#define CO_SWAP_64(x) x /**< Macro must swap bytes, if CO_BIG_ENDIAN is defined */
|
|
#define NULL (0) /**< NULL, for general usage */
|
|
#define true 1 /**< Logical true, for general use */
|
|
#define false 0 /**< Logical false, for general use */
|
|
typedef uint_fast8_t bool_t; /**< Boolean data type for general use */
|
|
typedef signed char int8_t; /**< INTEGER8 in CANopen (0002h), 8-bit signed integer */
|
|
typedef signed int int16_t; /**< INTEGER16 in CANopen (0003h), 16-bit signed integer */
|
|
typedef signed long int int32_t; /**< INTEGER32 in CANopen (0004h), 32-bit signed integer */
|
|
typedef signed long long int int64_t; /**< INTEGER64 in CANopen (0015h), 64-bit signed integer */
|
|
typedef unsigned char uint8_t; /**< UNSIGNED8 in CANopen (0005h), 8-bit unsigned integer */
|
|
typedef unsigned int uint16_t; /**< UNSIGNED16 in CANopen (0006h), 16-bit unsigned integer */
|
|
typedef unsigned long int uint32_t; /**< UNSIGNED32 in CANopen (0007h), 32-bit unsigned integer */
|
|
typedef unsigned long long int uint64_t; /**< UNSIGNED64 in CANopen (001Bh), 64-bit unsigned integer */
|
|
typedef float float32_t; /**< REAL32 in CANopen (0008h), single precision floating point value, 32-bit */
|
|
typedef double float64_t; /**< REAL64 in CANopen (0011h), double precision floating point value, 64-bit */
|
|
/** @} */
|
|
|
|
/**
|
|
* @defgroup CO_CAN_Message_reception Reception of CAN messages
|
|
* @{
|
|
*
|
|
* Target specific definitions and description of CAN message reception
|
|
*
|
|
* CAN messages in CANopenNode are usually received by its own thread or higher priority interrupt. Received CAN
|
|
* messages are first filtered by hardware or by software. Thread then examines its 11-bit CAN-id and mask and
|
|
* determines, to which \ref CO_obj "CANopenNode Object" it belongs to. After that it calls predefined CANrx_callback()
|
|
* function, which quickly pre-processes the message and fetches the relevant data. CANrx_callback() function is defined
|
|
* by each \ref CO_obj "CANopenNode Object" separately. Pre-processed fetched data are later processed in another
|
|
* thread.
|
|
*
|
|
* If \ref CO_obj "CANopenNode Object" reception of specific CAN message, it must first configure its own CO_CANrx_t
|
|
* object with the CO_CANrxBufferInit() function.
|
|
*/
|
|
|
|
/**
|
|
* CAN receive callback function which pre-processes received CAN message
|
|
*
|
|
* It is called by fast CAN receive thread. Each \ref CO_obj "CANopenNode Object" defines its own and registers it with
|
|
* CO_CANrxBufferInit(), by passing function pointer.
|
|
*
|
|
* @param object pointer to specific \ref CO_obj "CANopenNode Object", registered with CO_CANrxBufferInit()
|
|
* @param rxMsg pointer to received CAN message
|
|
*/
|
|
void CANrx_callback(void* object, void* rxMsg);
|
|
|
|
/**
|
|
* CANrx_callback() can read CAN identifier from received CAN message
|
|
*
|
|
* Must be defined in the **CO_driver_target.h** file.
|
|
*
|
|
* This is target specific function and is specific for specific microcontroller. It is best to implement it by using
|
|
* inline function or macro. `rxMsg` parameter should cast to a pointer to structure. For best efficiency structure may
|
|
* have the same alignment as CAN registers inside CAN module.
|
|
*
|
|
* @param rxMsg Pointer to received message
|
|
* @return 11-bit CAN standard identifier.
|
|
*/
|
|
static inline uint16_t
|
|
CO_CANrxMsg_readIdent(void* rxMsg) {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* CANrx_callback() can read Data Length Code from received CAN message
|
|
*
|
|
* See also CO_CANrxMsg_readIdent():
|
|
*
|
|
* @param rxMsg Pointer to received message
|
|
* @return data length in bytes (0 to 8)
|
|
*/
|
|
static inline uint8_t
|
|
CO_CANrxMsg_readDLC(void* rxMsg) {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* CANrx_callback() can read pointer to data from received CAN message
|
|
*
|
|
* See also CO_CANrxMsg_readIdent():
|
|
*
|
|
* @param rxMsg Pointer to received message
|
|
* @return pointer to data buffer
|
|
*/
|
|
static inline const uint8_t*
|
|
CO_CANrxMsg_readData(void* rxMsg) {
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Configuration object for CAN received message for specific \ref CO_obj "CANopenNode Object".
|
|
*
|
|
* Must be defined in the **CO_driver_target.h** file.
|
|
*
|
|
* Data fields of this structure are used exclusively by the driver. Usually it has the following data fields, but they
|
|
* may differ for different microcontrollers. Array of multiple CO_CANrx_t objects is included inside CO_CANmodule_t.
|
|
*/
|
|
typedef struct {
|
|
uint16_t ident; /**< Standard CAN Identifier (bits 0..10) + RTR (bit 11) */
|
|
uint16_t mask; /**< Standard CAN Identifier mask with the same alignment as ident */
|
|
void* object; /**< \ref CO_obj "CANopenNode Object" initialized in from CO_CANrxBufferInit() */
|
|
void (*pCANrx_callback)(void* object,
|
|
void* message); /**< Pointer to CANrx_callback() initialized in CO_CANrxBufferInit() */
|
|
} CO_CANrx_t;
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @defgroup CO_CAN_Message_transmission Transmission of CAN messages
|
|
* @{
|
|
*
|
|
* Target specific definitions and description of CAN message transmission
|
|
*
|
|
* If \ref CO_obj "CANopenNode Object" needs transmitting CAN message, it must first configure its own CO_CANtx_t object
|
|
* with the CO_CANtxBufferInit() function. CAN message can then be sent with CO_CANsend() function. If at that moment
|
|
* CAN transmit buffer inside microcontroller's CAN module is free, message is copied directly to the CAN module.
|
|
* Otherwise CO_CANsend() function sets _bufferFull_ flag to true. Message will be then sent by CAN TX interrupt as soon
|
|
* as CAN module is freed. Until message is not copied to CAN module, its contents must not change. If there are
|
|
* multiple CO_CANtx_t objects with _bufferFull_ flag set to true, then CO_CANtx_t with lower index will be sent first.
|
|
*/
|
|
|
|
/**
|
|
* Configuration object for CAN transmit message for specific \ref CO_obj "CANopenNode Object".
|
|
*
|
|
* Must be defined in the **CO_driver_target.h** file.
|
|
*
|
|
* Data fields of this structure are used exclusively by the driver. Usually it has the following data fields, but they
|
|
* may differ for different microcontrollers. Array of multiple CO_CANtx_t objects is included inside CO_CANmodule_t.
|
|
*/
|
|
typedef struct {
|
|
uint32_t ident; /**< CAN identifier as aligned in CAN module */
|
|
uint8_t DLC; /**< Length of CAN message */
|
|
uint8_t data[8]; /**< 8 data bytes */
|
|
volatile bool_t bufferFull; /**< True if previous message is still in the buffer */
|
|
volatile bool_t syncFlag; /**< Synchronous PDO messages has this flag set. It prevents them to be sent outside the
|
|
synchronous window */
|
|
} CO_CANtx_t;
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* Complete CAN module object.
|
|
*
|
|
* Must be defined in the **CO_driver_target.h** file.
|
|
*
|
|
* Usually it has the following data fields, but they may differ for different microcontrollers.
|
|
*/
|
|
typedef struct {
|
|
void* CANptr; /**< From CO_CANmodule_init() */
|
|
CO_CANrx_t* rxArray; /**< From CO_CANmodule_init() */
|
|
uint16_t rxSize; /**< From CO_CANmodule_init() */
|
|
CO_CANtx_t* txArray; /**< From CO_CANmodule_init() */
|
|
uint16_t txSize; /**< From CO_CANmodule_init() */
|
|
uint16_t CANerrorStatus; /**< CAN error status bitfield, see @ref CO_CAN_ERR_status_t */
|
|
volatile bool_t CANnormal; /**< CAN module is in normal mode */
|
|
volatile bool_t useCANrxFilters; /**< Value different than zero indicates, that CAN module hardware filters are used
|
|
for CAN reception. If there is not enough hardware filters, they won't be used.
|
|
In this case will be *all* received CAN messages processed by software. */
|
|
volatile bool_t bufferInhibitFlag; /**< If flag is true, then message in transmit buffer is synchronous PDO message,
|
|
which will be aborted, if CO_clearPendingSyncPDOs() function will be called by
|
|
application. This may be necessary if Synchronous window time was expired. */
|
|
volatile bool_t
|
|
firstCANtxMessage; /**< Equal to 1, when the first transmitted message (bootup message) is in CAN TX buffers */
|
|
volatile uint16_t
|
|
CANtxCount; /**< Number of messages in transmit buffer, which are waiting to be copied to the CAN module */
|
|
uint32_t errOld; /**< Previous state of CAN errors */
|
|
} CO_CANmodule_t;
|
|
|
|
/**
|
|
* Data storage object for one entry.
|
|
*
|
|
* Must be defined in the **CO_driver_target.h** file.
|
|
*
|
|
* For more information on Data storage see @ref CO_storage or **CO_storage.h** file. Structure members documented here
|
|
* are always required or required with @ref CO_storage_eeprom. Target system may add own additional, hardware specific
|
|
* variables.
|
|
*/
|
|
typedef struct {
|
|
void* addr; /**< Address of data to store, always required. */
|
|
size_t len; /**< Length of data to store, always required. */
|
|
uint8_t subIndexOD; /**< Sub index in OD objects 1010 and 1011, from 2 to 127. Writing 0x65766173 to 1010,subIndexOD
|
|
will store data to non-volatile memory Writing 0x64616F6C to 1011,subIndexOD will restore
|
|
default data, always required. */
|
|
uint8_t attr; /**< Attribute from @ref CO_storage_attributes_t, always required. */
|
|
void* storageModule; /**< Pointer to storage module, target system specific usage, required with @ref
|
|
CO_storage_eeprom. */
|
|
uint16_t crc; /**< CRC checksum of the data stored in eeprom, set on store, required with @ref CO_storage_eeprom. */
|
|
size_t eepromAddrSignature; /**< Address of entry signature inside eeprom, set by init, required with @ref
|
|
CO_storage_eeprom. */
|
|
size_t eepromAddr; /**< Address of data inside eeprom, set by init, required with @ref CO_storage_eeprom. */
|
|
size_t offset; /**< Offset of next byte being updated by automatic storage, required with @ref CO_storage_eeprom. */
|
|
void* additionalParameters; /**< Additional target specific parameters, optional. */
|
|
} CO_storage_entry_t;
|
|
|
|
/**
|
|
* @defgroup CO_critical_sections Critical sections
|
|
* @{
|
|
*
|
|
* Protection of critical sections in multi-threaded operation.
|
|
*
|
|
* CANopenNode is designed to run in different threads, as described in [README.md](index.html). 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.
|
|
*
|
|
* #### Reentrant functions
|
|
* 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.
|
|
*
|
|
* #### Object Dictionary variables
|
|
* 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:
|
|
* - Whole real-time thread,
|
|
* - SDO server protects read/write access to OD variable. Locking of long OD variables, not accessible from real-time
|
|
* thread, may block RT thread.
|
|
* - Any mainline code, which accesses PDO-mappable OD variable, must protect read/write with locking macros. Use @ref
|
|
* OD_mappable() for check.
|
|
* - Other cases, where non-PDO-mappable OD variable is used inside real-time thread by some other part of the user
|
|
* application must be considered with special care. Also when there are multiple threads accessing the OD
|
|
* (e.g. when using a RTOS), you should always lock the OD.
|
|
*
|
|
* #### Synchronization functions for CAN receive
|
|
* 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](https://en.wikipedia.org/wiki/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](https://stackoverflow.com/questions/982129/what-does-sync-synchronize-do#982179).
|
|
*/
|
|
|
|
#define CO_LOCK_CAN_SEND(CAN_MODULE) /**< Lock critical section in CO_CANsend() */
|
|
#define CO_UNLOCK_CAN_SEND(CAN_MODULE) /**< Unlock critical section in CO_CANsend() */
|
|
#define CO_LOCK_EMCY(CAN_MODULE) /**< Lock critical section in CO_errorReport() or CO_errorReset() */
|
|
#define CO_UNLOCK_EMCY(CAN_MODULE) /**< Unlock critical section in CO_errorReport() or CO_errorReset() */
|
|
#define CO_LOCK_OD(CAN_MODULE) /**< Lock critical section when accessing Object Dictionary */
|
|
#define CO_UNLOCK_OD(CAN_MODULE) /**< Unock critical section when accessing Object Dictionary */
|
|
|
|
/** Check if new message has arrived */
|
|
#define CO_FLAG_READ(rxNew) ((rxNew) != NULL)
|
|
/** Set new message flag */
|
|
#define CO_FLAG_SET(rxNew) \
|
|
{ \
|
|
__sync_synchronize(); \
|
|
rxNew = (void*)1L; \
|
|
}
|
|
/** Clear new message flag */
|
|
#define CO_FLAG_CLEAR(rxNew) \
|
|
{ \
|
|
__sync_synchronize(); \
|
|
rxNew = NULL; \
|
|
}
|
|
|
|
/** @} */
|
|
#endif /* CO_DOXYGEN */
|
|
|
|
/**
|
|
* @defgroup CO_Default_CAN_ID_t Default CANopen identifiers
|
|
* @{
|
|
*
|
|
* Default CANopen identifiers for CANopen communication objects. Same as 11-bit addresses of CAN messages. These are
|
|
* default identifiers and can be changed in CANopen. Especially PDO identifiers are configured in PDO linking phase of
|
|
* the CANopen network configuration.
|
|
*/
|
|
#define CO_CAN_ID_NMT_SERVICE 0x000U /**< 0x000 Network management */
|
|
#define CO_CAN_ID_GFC 0x001U /**< 0x001 Global fail-safe command */
|
|
#define CO_CAN_ID_SYNC 0x080U /**< 0x080 Synchronous message */
|
|
#define CO_CAN_ID_EMERGENCY 0x080U /**< 0x080 Emergency messages (+nodeID) */
|
|
#define CO_CAN_ID_TIME 0x100U /**< 0x100 Time message */
|
|
#define CO_CAN_ID_SRDO_1 0x0FFU /**< 0x0FF Default SRDO1 (+2*nodeID) */
|
|
#define CO_CAN_ID_TPDO_1 0x180U /**< 0x180 Default TPDO1 (+nodeID) */
|
|
#define CO_CAN_ID_RPDO_1 0x200U /**< 0x200 Default RPDO1 (+nodeID) */
|
|
#define CO_CAN_ID_TPDO_2 0x280U /**< 0x280 Default TPDO2 (+nodeID) */
|
|
#define CO_CAN_ID_RPDO_2 0x300U /**< 0x300 Default RPDO2 (+nodeID) */
|
|
#define CO_CAN_ID_TPDO_3 0x380U /**< 0x380 Default TPDO3 (+nodeID) */
|
|
#define CO_CAN_ID_RPDO_3 0x400U /**< 0x400 Default RPDO3 (+nodeID) */
|
|
#define CO_CAN_ID_TPDO_4 0x480U /**< 0x480 Default TPDO4 (+nodeID) */
|
|
#define CO_CAN_ID_RPDO_4 0x500U /**< 0x500 Default RPDO5 (+nodeID) */
|
|
#define CO_CAN_ID_SDO_SRV 0x580U /**< 0x580 SDO response from server (+nodeID) */
|
|
#define CO_CAN_ID_SDO_CLI 0x600U /**< 0x600 SDO request from client (+nodeID) */
|
|
#define CO_CAN_ID_HEARTBEAT 0x700U /**< 0x700 Heartbeat message */
|
|
#define CO_CAN_ID_LSS_SLV 0x7E4U /**< 0x7E4 LSS response from slave */
|
|
#define CO_CAN_ID_LSS_MST 0x7E5U /**< 0x7E5 LSS request from master */
|
|
|
|
/** @} */ /* CO_Default_CAN_ID_t */
|
|
|
|
/**
|
|
* Restricted CAN-IDs
|
|
*
|
|
* Macro for verifying 'Restricted CAN-IDs', as specified by standard CiA301. They shall not be used for SYNC, TIME,
|
|
* EMCY, PDO and SDO.
|
|
*/
|
|
#ifndef CO_IS_RESTRICTED_CAN_ID
|
|
#define CO_IS_RESTRICTED_CAN_ID(CAN_ID) \
|
|
(((CAN_ID) <= 0x7FU) || (((CAN_ID) >= 0x101U) && ((CAN_ID) <= 0x180U)) \
|
|
|| (((CAN_ID) >= 0x581U) && ((CAN_ID) <= 0x5FFU)) || (((CAN_ID) >= 0x601U) && ((CAN_ID) <= 0x67FU)) \
|
|
|| (((CAN_ID) >= 0x6E0U) && ((CAN_ID) <= 0x6FFU)) || ((CAN_ID) >= 0x701U))
|
|
#endif
|
|
|
|
/**
|
|
* @defgroup CO_CAN_ERR_status_t CAN error status bitmasks
|
|
* @{
|
|
*
|
|
* CAN warning level is reached, if CAN transmit or receive error counter is more or equal to 96. CAN passive level is
|
|
* reached, if counters are more or equal to 128. Transmitter goes in error state 'bus off' if transmit error counter is
|
|
* more or equal to 256.
|
|
*/
|
|
#define CO_CAN_ERRTX_WARNING 0x0001U /**< 0x0001 CAN transmitter warning */
|
|
#define CO_CAN_ERRTX_PASSIVE 0x0002U /**< 0x0002 CAN transmitter passive */
|
|
#define CO_CAN_ERRTX_BUS_OFF 0x0004U /**< 0x0004 CAN transmitter bus off */
|
|
#define CO_CAN_ERRTX_OVERFLOW 0x0008U /**< 0x0008 CAN transmitter overflow */
|
|
#define CO_CAN_ERRTX_PDO_LATE 0x0080U /**< 0x0080 TPDO is outside sync window */
|
|
#define CO_CAN_ERRRX_WARNING 0x0100U /**< 0x0100 CAN receiver warning */
|
|
#define CO_CAN_ERRRX_PASSIVE 0x0200U /**< 0x0200 CAN receiver passive */
|
|
#define CO_CAN_ERRRX_OVERFLOW 0x0800U /**< 0x0800 CAN receiver overflow */
|
|
#define CO_CAN_ERR_WARN_PASSIVE 0x0303U /**< 0x0303 combination */
|
|
|
|
/** @} */ /* CO_CAN_ERR_status_t */
|
|
|
|
/**
|
|
* Return values of some CANopen functions. If function was executed successfully it returns 0 otherwise it returns <0.
|
|
*/
|
|
typedef enum {
|
|
CO_ERROR_NO = 0, /**< Operation completed successfully */
|
|
CO_ERROR_ILLEGAL_ARGUMENT = -1, /**< Error in function arguments */
|
|
CO_ERROR_OUT_OF_MEMORY = -2, /**< Memory allocation failed */
|
|
CO_ERROR_TIMEOUT = -3, /**< Function timeout */
|
|
CO_ERROR_ILLEGAL_BAUDRATE = -4, /**< Illegal baudrate passed to function CO_CANmodule_init() */
|
|
CO_ERROR_RX_OVERFLOW = -5, /**< Previous message was not processed yet */
|
|
CO_ERROR_RX_PDO_OVERFLOW = -6, /**< previous PDO was not processed yet */
|
|
CO_ERROR_RX_MSG_LENGTH = -7, /**< Wrong receive message length */
|
|
CO_ERROR_RX_PDO_LENGTH = -8, /**< Wrong receive PDO length */
|
|
CO_ERROR_TX_OVERFLOW = -9, /**< Previous message is still waiting, buffer full */
|
|
CO_ERROR_TX_PDO_WINDOW = -10, /**< Synchronous TPDO is outside window */
|
|
CO_ERROR_TX_UNCONFIGURED = -11, /**< Transmit buffer was not configured properly */
|
|
CO_ERROR_OD_PARAMETERS = -12, /**< Error in Object Dictionary parameters */
|
|
CO_ERROR_DATA_CORRUPT = -13, /**< Stored data are corrupt */
|
|
CO_ERROR_CRC = -14, /**< CRC does not match */
|
|
CO_ERROR_TX_BUSY = -15, /**< Sending rejected because driver is busy. Try again */
|
|
CO_ERROR_WRONG_NMT_STATE = -16, /**< Command can't be processed in current state */
|
|
CO_ERROR_SYSCALL = -17, /**< Syscall failed */
|
|
CO_ERROR_INVALID_STATE = -18, /**< Driver not ready */
|
|
CO_ERROR_NODE_ID_UNCONFIGURED_LSS =
|
|
-19 /**< Node-id is in LSS unconfigured state. If objects are handled properly, this may not be an error. */
|
|
} CO_ReturnError_t;
|
|
|
|
/**
|
|
* Request CAN configuration (stopped) mode and *wait* until it is set.
|
|
*
|
|
* @param CANptr Pointer to CAN device
|
|
*/
|
|
void CO_CANsetConfigurationMode(void* CANptr);
|
|
|
|
/**
|
|
* Request CAN normal (operational) mode and *wait* until it is set.
|
|
*
|
|
* @param CANmodule CO_CANmodule_t object.
|
|
*/
|
|
void CO_CANsetNormalMode(CO_CANmodule_t* CANmodule);
|
|
|
|
/**
|
|
* Initialize CAN module object.
|
|
*
|
|
* Function must be called in the communication reset section. CAN module must be in Configuration Mode before.
|
|
*
|
|
* @param CANmodule This object will be initialized.
|
|
* @param CANptr Pointer to CAN device.
|
|
* @param rxArray Array for handling received CAN messages
|
|
* @param rxSize Size of the above array. Must be equal to number of receiving CAN objects.
|
|
* @param txArray Array for handling transmitting CAN messages
|
|
* @param txSize Size of the above array. Must be equal to number of transmitting CAN objects.
|
|
* @param CANbitRate Valid values are (in kbps): 10, 20, 50, 125, 250, 500, 800, 1000. If value is illegal, bitrate
|
|
* defaults to 125.
|
|
*
|
|
* Return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
|
|
*/
|
|
CO_ReturnError_t CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], uint16_t rxSize,
|
|
CO_CANtx_t txArray[], uint16_t txSize, uint16_t CANbitRate);
|
|
|
|
/**
|
|
* Switch off CANmodule. Call at program exit.
|
|
*
|
|
* @param CANmodule CAN module object.
|
|
*/
|
|
void CO_CANmodule_disable(CO_CANmodule_t* CANmodule);
|
|
|
|
/**
|
|
* Configure CAN message receive buffer.
|
|
*
|
|
* Function configures specific CAN receive buffer. It sets CAN identifier and connects buffer with specific object.
|
|
* Function must be called for each member in _rxArray_ from CO_CANmodule_t.
|
|
*
|
|
* @param CANmodule This object.
|
|
* @param index Index of the specific buffer in _rxArray_.
|
|
* @param ident 11-bit standard CAN Identifier. If two or more CANrx buffers have the same _ident_, then buffer with
|
|
* lowest _index_ has precedence and other CANrx buffers will be ignored.
|
|
* @param mask 11-bit mask for identifier. Most usually set to 0x7FF. Received message (rcvMsg) will be accepted if the
|
|
* following condition is true: (((rcvMsgId ^ ident) & mask) == 0).
|
|
* @param rtr If true, 'Remote Transmit Request' messages will be accepted.
|
|
* @param object CANopen object, to which buffer is connected. It will be used as an argument to CANrx_callback. Its
|
|
* type is (void), CANrx_callback will change its type back to the correct object type.
|
|
* @param CANrx_callback Pointer to function, which will be called, if received CAN message matches the identifier. It
|
|
* must be fast function.
|
|
*
|
|
* Return #CO_ReturnError_t: CO_ERROR_NO CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (not enough masks for
|
|
* configuration).
|
|
*/
|
|
CO_ReturnError_t CO_CANrxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, uint16_t mask,
|
|
bool_t rtr, void* object, void (*CANrx_callback)(void* object, void* message));
|
|
|
|
/**
|
|
* Configure CAN message transmit buffer.
|
|
*
|
|
* Function configures specific CAN transmit buffer. Function must be called for each member in _txArray_ from
|
|
* CO_CANmodule_t.
|
|
*
|
|
* @param CANmodule This object.
|
|
* @param index Index of the specific buffer in _txArray_.
|
|
* @param ident 11-bit standard CAN Identifier.
|
|
* @param rtr If true, 'Remote Transmit Request' messages will be transmitted.
|
|
* @param noOfBytes Length of CAN message in bytes (0 to 8 bytes).
|
|
* @param syncFlag This flag bit is used for synchronous TPDO messages. If it is set, message will not be sent, if
|
|
* current time is outside synchronous window.
|
|
*
|
|
* @return Pointer to CAN transmit message buffer. 8 bytes data array inside buffer should be written, before
|
|
* CO_CANsend() function is called. Zero is returned in case of wrong arguments.
|
|
*/
|
|
CO_CANtx_t* CO_CANtxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, bool_t rtr, uint8_t noOfBytes,
|
|
bool_t syncFlag);
|
|
|
|
/**
|
|
* Send CAN message.
|
|
*
|
|
* @param CANmodule This object.
|
|
* @param buffer Pointer to transmit buffer, returned by CO_CANtxBufferInit(). Data bytes must be written in buffer
|
|
* before function call.
|
|
*
|
|
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_TX_OVERFLOW or CO_ERROR_TX_PDO_WINDOW (Synchronous TPDO is outside
|
|
* window).
|
|
*/
|
|
CO_ReturnError_t CO_CANsend(CO_CANmodule_t* CANmodule, CO_CANtx_t* buffer);
|
|
|
|
/**
|
|
* Clear all synchronous TPDOs from CAN module transmit buffers.
|
|
*
|
|
* CANopen allows synchronous PDO communication only inside time between SYNC message and SYNC Window. If time is
|
|
* outside this window, new synchronous PDOs must not be sent and all pending sync TPDOs, which may be on CAN TX
|
|
* buffers, may optionally be cleared.
|
|
*
|
|
* This function checks (and aborts transmission if necessary) CAN TX buffers when it is called. Function should be
|
|
* called by the stack in the moment, when SYNC time was just passed out of synchronous window.
|
|
*
|
|
* @param CANmodule This object.
|
|
*/
|
|
void CO_CANclearPendingSyncPDOs(CO_CANmodule_t* CANmodule);
|
|
|
|
/**
|
|
* Process can module - verify CAN errors
|
|
*
|
|
* Function must be called cyclically. It should calculate CANerrorStatus bitfield for CAN errors defined in @ref
|
|
* CO_CAN_ERR_status_t.
|
|
*
|
|
* @param CANmodule This object.
|
|
*/
|
|
void CO_CANmodule_process(CO_CANmodule_t* CANmodule);
|
|
|
|
/**
|
|
* Get uint8_t value from memory buffer
|
|
*
|
|
* @param buf Memory buffer to get value from.
|
|
*
|
|
* @return Value
|
|
*/
|
|
static inline uint8_t
|
|
CO_getUint8(const void* buf) {
|
|
uint8_t value;
|
|
(void)memmove((void*)&value, buf, sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
/** Get uint16_t value from memory buffer, see @ref CO_getUint8 */
|
|
static inline uint16_t
|
|
CO_getUint16(const void* buf) {
|
|
uint16_t value;
|
|
(void)memmove((void*)&value, buf, sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
/** Get uint32_t value from memory buffer, see @ref CO_getUint8 */
|
|
static inline uint32_t
|
|
CO_getUint32(const void* buf) {
|
|
uint32_t value;
|
|
(void)memmove((void*)&value, buf, sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Write uint8_t value into memory buffer
|
|
*
|
|
* @param buf Memory buffer.
|
|
* @param value Value to be written into buf.
|
|
*
|
|
* @return number of bytes written.
|
|
*/
|
|
static inline uint8_t
|
|
CO_setUint8(void* buf, uint8_t value) {
|
|
(void)memmove(buf, (const void*)&value, sizeof(value));
|
|
return (uint8_t)(sizeof(value));
|
|
}
|
|
|
|
/** Write uint16_t value into memory buffer, see @ref CO_setUint8 */
|
|
static inline uint8_t
|
|
CO_setUint16(void* buf, uint16_t value) {
|
|
(void)memmove(buf, (const void*)&value, sizeof(value));
|
|
return (uint8_t)(sizeof(value));
|
|
}
|
|
|
|
/** Write uint32_t value into memory buffer, see @ref CO_setUint8 */
|
|
static inline uint8_t
|
|
CO_setUint32(void* buf, uint32_t value) {
|
|
(void)memmove(buf, (const void*)&value, sizeof(value));
|
|
return (uint8_t)(sizeof(value));
|
|
}
|
|
|
|
/** @} */ /* CO_driver */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
|
|
#endif /* CO_DRIVER_H */
|