Created a single header file (`CO_driver.h`) to declare the functions of the drivers API. This forces drivers to use a common API. Previously, each driver declared these functions in its specific `CO_driver.h` file. This causes two issues: 1. while these functions are used in target-independent code, the declarations could be target specific, leading to API/ABI compatibility issues. 2. the declarations were duplicated for each driver. The `CO_driver.h` files for each driver were renamed to `CO_driver_target.h`. This file is included from `CO_driver.h`. Added a new header for types: `CO_types.h`. This file currently holds the definition of `CO_ReturnError_t` that was previously duplicated for all drivers. Added a few enum values to support the `neuberger-socketCAN` code.
311 lines
13 KiB
C
311 lines
13 KiB
C
/**
|
|
* CAN module object for generic microcontroller.
|
|
*
|
|
* This file is a template for other microcontrollers.
|
|
*
|
|
* @file CO_driver_target.h
|
|
* @ingroup CO_driver
|
|
* @author Janez Paternoster
|
|
* @copyright 2004 - 2015 Janez Paternoster
|
|
*
|
|
* This file is part of CANopenNode, an opensource CANopen Stack.
|
|
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
|
* For more information on CANopen see <http://www.can-cia.org/>.
|
|
*
|
|
* CANopenNode is free and open source software: you can redistribute
|
|
* it and/or modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Following clarification and special exception to the GNU General Public
|
|
* License is included to the distribution terms of CANopenNode:
|
|
*
|
|
* Linking this library statically or dynamically with other modules is
|
|
* making a combined work based on this library. Thus, the terms and
|
|
* conditions of the GNU General Public License cover the whole combination.
|
|
*
|
|
* As a special exception, the copyright holders of this library give
|
|
* you permission to link this library with independent modules to
|
|
* produce an executable, regardless of the license terms of these
|
|
* independent modules, and to copy and distribute the resulting
|
|
* executable under terms of your choice, provided that you also meet,
|
|
* for each linked independent module, the terms and conditions of the
|
|
* license of that module. An independent module is a module which is
|
|
* not derived from or based on this library. If you modify this
|
|
* library, you may extend this exception to your version of the
|
|
* library, but you are not obliged to do so. If you do not wish
|
|
* to do so, delete this exception statement from your version.
|
|
*/
|
|
|
|
|
|
#ifndef CO_DRIVER_TARGET_H
|
|
#define CO_DRIVER_TARGET_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Include processor header file */
|
|
#include <stddef.h> /* for 'NULL' */
|
|
#include <stdint.h> /* for 'int8_t' to 'uint64_t' */
|
|
#include <stdbool.h> /* for 'true', 'false' */
|
|
|
|
|
|
/**
|
|
* Endianness.
|
|
*
|
|
* 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.
|
|
*/
|
|
#define CO_LITTLE_ENDIAN
|
|
|
|
|
|
/**
|
|
* @defgroup CO_driver Driver
|
|
* @ingroup CO_CANopen
|
|
* @{
|
|
*
|
|
* Microcontroller specific code for CANopenNode.
|
|
*
|
|
* This file contains type definitions, functions and macros for:
|
|
* - Basic data types.
|
|
* - Receive and transmit buffers for CANopen messages.
|
|
* - Interaction with CAN module on the microcontroller.
|
|
* - CAN receive and transmit interrupts.
|
|
*
|
|
* This file is not only a CAN driver. There are no classic CAN queues for CAN
|
|
* messages. This file provides direct connection with other CANopen
|
|
* objects. It tries to provide fast responses and tries to avoid unnecessary
|
|
* calculations and memory consumptions.
|
|
*
|
|
* CO_CANmodule_t contains an array of _Received message objects_ (of type
|
|
* CO_CANrx_t) and an array of _Transmit message objects_ (of type CO_CANtx_t).
|
|
* Each CANopen communication object owns one member in one of the arrays.
|
|
* For example Heartbeat producer generates one CANopen transmitting object,
|
|
* so it has reserved one member in CO_CANtx_t array.
|
|
* SYNC module may produce sync or consume sync, so it has reserved one member
|
|
* in CO_CANtx_t and one member in CO_CANrx_t array.
|
|
*
|
|
* ###Reception of CAN messages.
|
|
* Before CAN messages can be received, each member in CO_CANrx_t must be
|
|
* initialized. CO_CANrxBufferInit() is called by CANopen module, which
|
|
* uses specific member. For example @ref CO_HBconsumer uses multiple members
|
|
* in CO_CANrx_t array. (It monitors multiple heartbeat messages from remote
|
|
* nodes.) It must call CO_CANrxBufferInit() multiple times.
|
|
*
|
|
* Main arguments to the CO_CANrxBufferInit() function are CAN identifier
|
|
* and a pointer to callback function. Those two arguments (and some others)
|
|
* are copied to the member of the CO_CANrx_t array.
|
|
*
|
|
* Callback function is a function, specified by specific CANopen module
|
|
* (for example by @ref CO_HBconsumer). Each CANopen module defines own
|
|
* callback function. Callback function will process the received CAN message.
|
|
* It will copy the necessary data from CAN message to proper place. It may
|
|
* also trigger additional task, which will further process the received message.
|
|
* Callback function must be fast and must only make the necessary calculations
|
|
* and copying.
|
|
*
|
|
* Received CAN messages are processed by CAN receive interrupt function.
|
|
* After CAN message is received, function first tries to find matching CAN
|
|
* identifier from CO_CANrx_t array. If found, then a corresponding callback
|
|
* function is called.
|
|
*
|
|
* Callback function accepts two parameters:
|
|
* - object is pointer to object registered by CO_CANrxBufferInit().
|
|
* - msg is pointer to CAN message of type CO_CANrxMsg_t.
|
|
*
|
|
* Callback function must return #CO_ReturnError_t: CO_ERROR_NO,
|
|
* CO_ERROR_RX_OVERFLOW, CO_ERROR_RX_PDO_OVERFLOW, CO_ERROR_RX_MSG_LENGTH or
|
|
* CO_ERROR_RX_PDO_LENGTH.
|
|
*
|
|
*
|
|
* ###Transmission of CAN messages.
|
|
* Before CAN messages can be transmitted, each member in CO_CANtx_t must be
|
|
* initialized. CO_CANtxBufferInit() is called by CANopen module, which
|
|
* uses specific member. For example Heartbeat producer must initialize it's
|
|
* member in CO_CANtx_t array.
|
|
*
|
|
* CO_CANtxBufferInit() returns a pointer of type CO_CANtx_t, which contains buffer
|
|
* where CAN message data can be written. CAN message is send with calling
|
|
* CO_CANsend() function. If at that moment CAN transmit buffer inside
|
|
* microcontroller's CAN module is free, message is copied directly to 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. There may be multiple
|
|
* _bufferFull_ flags in CO_CANtx_t array set to true. In that case messages with
|
|
* lower index inside array will be sent first.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @name Critical sections
|
|
* CANopenNode is designed to run in different threads, as described in README.
|
|
* 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, CO_errorReport() from CO_Emergency.h
|
|
* and CO_errorReset() from CO_Emergency.h may be called from different threads.
|
|
* Critical sections must be protected. Eather by disabling scheduler or
|
|
* interrupts or by mutexes or semaphores.
|
|
*
|
|
* ####Object Dictionary variables.
|
|
* In general, there are two threads, which accesses OD variables: mainline and
|
|
* timer. CANopenNode initialization and SDO server runs in mainline. PDOs runs
|
|
* in faster timer thread. Processing of PDOs must not be interrupted by
|
|
* mainline. Mainline thread must protect sections, which accesses the same OD
|
|
* variables as timer thread. This care must also take the application. Note
|
|
* that not all variables are allowed to be mapped to PDOs, so they may not need
|
|
* to be protected. SDO server protects sections with access to OD variables.
|
|
*
|
|
* ####CAN receive thread.
|
|
* It partially processes received CAN data and puts them into appropriate
|
|
* objects. Objects are later processed. It does not need protection of
|
|
* critical sections. There is one circumstance, where CANrx should be disabled:
|
|
* After presence of SYNC message on CANopen bus, CANrx should be temporary
|
|
* disabled until all receive PDOs are processed. See also CO_SYNC.h file and
|
|
* CO_SYNC_initCallback() function.
|
|
* @{
|
|
*/
|
|
#define CO_LOCK_CAN_SEND() /**< Lock critical section in CO_CANsend() */
|
|
#define CO_UNLOCK_CAN_SEND()/**< Unlock critical section in CO_CANsend() */
|
|
|
|
#define CO_LOCK_EMCY() /**< Lock critical section in CO_errorReport() or CO_errorReset() */
|
|
#define CO_UNLOCK_EMCY() /**< Unlock critical section in CO_errorReport() or CO_errorReset() */
|
|
|
|
#define CO_LOCK_OD() /**< Lock critical section when accessing Object Dictionary */
|
|
#define CO_UNLOCK_OD() /**< Unock critical section when accessing Object Dictionary */
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Syncronisation functions
|
|
* syncronisation for message buffer for communication between CAN receive and
|
|
* message processing threads.
|
|
*
|
|
* If receive function runs inside IRQ, no further synchronsiation is needed.
|
|
* Otherwise, some kind of synchronsiation has to be included. The following
|
|
* example uses GCC builtin memory barrier __sync_synchronize(). A comprehensive
|
|
* list can be found here: https://gist.github.com/leo-yuriev/ba186a6bf5cf3a27bae7
|
|
* \code{.c}
|
|
#define CANrxMemoryBarrier() {__sync_synchronize();}
|
|
* \endcode
|
|
* @{
|
|
*/
|
|
/** Memory barrier */
|
|
#define CANrxMemoryBarrier()
|
|
/** Check if new message has arrived */
|
|
#define IS_CANrxNew(rxNew) ((uintptr_t)rxNew)
|
|
/** Set new message flag */
|
|
#define SET_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)1L;}
|
|
/** Clear new message flag */
|
|
#define CLEAR_CANrxNew(rxNew) {CANrxMemoryBarrier(); rxNew = (void*)0L;}
|
|
/** @} */
|
|
|
|
/**
|
|
* @defgroup CO_dataTypes Data types
|
|
* @{
|
|
*
|
|
* According to Misra C
|
|
*/
|
|
/* int8_t to uint64_t are defined in stdint.h */
|
|
typedef unsigned char bool_t; /**< bool_t */
|
|
typedef float float32_t; /**< float32_t */
|
|
typedef long double float64_t; /**< float64_t */
|
|
typedef char char_t; /**< char_t */
|
|
typedef unsigned char oChar_t; /**< oChar_t */
|
|
typedef unsigned char domain_t; /**< domain_t */
|
|
/** @} */
|
|
|
|
|
|
/**
|
|
* CAN receive message structure as aligned in CAN module. It is different in
|
|
* different microcontrollers. It usually contains other variables.
|
|
*/
|
|
typedef struct{
|
|
/** CAN identifier. It must be read through CO_CANrxMsg_readIdent() function. */
|
|
uint32_t ident;
|
|
uint8_t DLC ; /**< Length of CAN message */
|
|
uint8_t data[8]; /**< 8 data bytes */
|
|
}CO_CANrxMsg_t;
|
|
|
|
|
|
/**
|
|
* Received message object
|
|
*/
|
|
typedef struct{
|
|
uint16_t ident; /**< Standard CAN Identifier (bits 0..10) + RTR (bit 11) */
|
|
uint16_t mask; /**< Standard Identifier mask with same alignment as ident */
|
|
void *object; /**< From CO_CANrxBufferInit() */
|
|
void (*pFunct)(void *object, const CO_CANrxMsg_t *message); /**< From CO_CANrxBufferInit() */
|
|
}CO_CANrx_t;
|
|
|
|
|
|
/**
|
|
* Transmit message object.
|
|
*/
|
|
typedef struct{
|
|
uint32_t ident; /**< CAN identifier as aligned in CAN module */
|
|
uint8_t DLC ; /**< Length of CAN message. (DLC may also be part of ident) */
|
|
uint8_t data[8]; /**< 8 data bytes */
|
|
volatile bool_t bufferFull; /**< True if previous message is still in buffer */
|
|
/** Synchronous PDO messages has this flag set. It prevents them to be sent outside the synchronous window */
|
|
volatile bool_t syncFlag;
|
|
}CO_CANtx_t;
|
|
|
|
|
|
/**
|
|
* CAN module object. It may be different in different microcontrollers.
|
|
*/
|
|
typedef struct{
|
|
void *CANdriverState; /**< 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() */
|
|
volatile bool_t CANnormal; /**< CAN module is in normal mode */
|
|
/** 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 useCANrxFilters;
|
|
/** If flag is true, then message in transmitt 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 bufferInhibitFlag;
|
|
/** Equal to 1, when the first transmitted message (bootup message) is in CAN TX buffers */
|
|
volatile bool_t firstCANtxMessage;
|
|
/** Number of messages in transmit buffer, which are waiting to be copied to the CAN module */
|
|
volatile uint16_t CANtxCount;
|
|
uint32_t errOld; /**< Previous state of CAN errors */
|
|
void *em; /**< Emergency object */
|
|
}CO_CANmodule_t;
|
|
|
|
|
|
/**
|
|
* Receives and transmits CAN messages.
|
|
*
|
|
* Function must be called directly from high priority CAN interrupt.
|
|
*
|
|
* @param CANmodule This object.
|
|
*/
|
|
void CO_CANinterrupt(CO_CANmodule_t *CANmodule);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
|
|
/** @} */
|
|
#endif /* CO_DRIVER_TARGET_H */
|