1
0
Fork 0

CANopenNode V2.0 pre-release

This commit is contained in:
Janez 2020-10-07 16:53:00 +02:00
parent 7a43f98f24
commit f73025ef65
24 changed files with 5079 additions and 4467 deletions

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
* @file CO_Emergency.h
* @ingroup CO_Emergency
* @author Janez Paternoster
* @copyright 2004 - 2020 Janez Paternoster
* @copyright 2020 Janez Paternoster
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
@ -27,14 +27,19 @@
#define CO_EMERGENCY_H
#include "301/CO_driver.h"
#include "301/CO_ODinterface.h"
/* default configuration, see CO_config.h */
#ifndef CO_CONFIG_EM
#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER)
#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | \
CO_CONFIG_EM_HISTORY)
#endif
#ifndef CO_CONFIG_EM_ERR_STATUS_BITS_COUNT
#define CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (10*8)
#endif
#ifndef CO_CONFIG_EM_BUFFER_SIZE
#define CO_CONFIG_EM_BUFFER_SIZE 16
#endif
#ifndef CO_CONFIG_ERR_CONDITION_GENERIC
#define CO_CONFIG_ERR_CONDITION_GENERIC (em->errorStatusBits[5] != 0)
#endif
@ -64,352 +69,425 @@ extern "C" {
* In case of error condition stack or application calls CO_errorReport()
* function with indication of the error. Specific error condition is reported
* (with CANopen Emergency message) only the first time after it occurs.
* Internal state of the error condition is controlled with
* @ref CO_EM_errorStatusBits. Specific error condition can also be reset by
* CO_errorReset() function. If so, Emergency message is sent with
* CO_EM_NO_ERROR indication.
* Internal state of specific error condition is indicated by internal bitfield
* variable, with space for maximum @ref CO_CONFIG_EM_ERR_STATUS_BITS_COUNT
* bits. Meaning for each bit is described by @ref CO_EM_errorStatusBits_t.
* Specific error condition can be reset by CO_errorReset() function. In that
* case Emergency message is sent with CO_EM_NO_ERROR indication.
*
* Some error conditions are informative and some are critical. Critical error
* conditions sets the #CO_errorRegisterBitmask_t.
* conditions set the corresponding bit in @ref CO_errorRegister_t. Critical
* error conditions for generic error are specified by
* @ref CO_CONFIG_ERR_CONDITION_GENERIC macro. Similar macros are defined for
* other error bits in in @ref CO_errorRegister_t.
*
* Latest errors can be read from _Pre Defined Error Field_ (object dictionary,
* index 0x1003). @ref CO_EM_errorStatusBits can also be read form CANopen
* object dictionary.
*
* ###Emergency message contents:
* ### Emergency producer
* If @ref CO_CONFIG_EM has CO_CONFIG_EM_PRODUCER enabled, then CANopen
* Emergency message will be sent on each change of any error condition.
* Emergency message contents are:
*
* Byte | Description
* -----|-----------------------------------------------------------
* 0..1 | @ref CO_EM_errorCodes.
* 2 | #CO_errorRegisterBitmask_t.
* 3 | Index of error condition (see @ref CO_EM_errorStatusBits).
* 4..7 | Additional argument informative to CO_errorReport() function.
* 0..1 | @ref CO_EM_errorCode_t
* 2 | @ref CO_errorRegister_t
* 3 | Index of error condition (see @ref CO_EM_errorStatusBits_t).
* 4..7 | Additional informative argument to CO_errorReport() function.
*
* ####Contents of _Pre Defined Error Field_ (object dictionary, index 0x1003):
* bytes 0..3 are equal to bytes 0..3 in the Emergency message.
* ### Error history
* If @ref CO_CONFIG_EM has CO_CONFIG_EM_HISTORY enabled, then latest errors
* can be read from _Pre Defined Error Field_ (object dictionary, index 0x1003).
* Contents corresponds to bytes 0..3 from the Emergency message.
*
* @see #CO_Default_CAN_ID_t
* ### Emergency consumer
* If @ref CO_CONFIG_EM has CO_CONFIG_EM_CONSUMER enabled, then callback can be
* registered by @ref CO_EM_initCallbackRx() function.
*/
/**
* CANopen Error register.
*
* In object dictionary on index 0x1001.
* Mandatory for CANopen, resides in object dictionary, index 0x1001.
*
* Error register is calculated from critical internal @ref CO_EM_errorStatusBits.
* Generic and communication bits are calculated in CO_EM_process
* function, device profile or manufacturer specific bits may be calculated
* inside the application.
* Error register is calculated from internal bitfield variable, critical bits.
* See @ref CO_EM_errorStatusBits_t and @ref CO_STACK_CONFIG_EMERGENCY for error
* condition macros.
*
* Internal errors may prevent device to stay in NMT Operational state. Details
* are described in _Error Behavior_ object in Object Dictionary at index 0x1029.
* Internal errors may prevent device to stay in NMT Operational state and
* changes may switch between the states. See @ref CO_NMT_control_t for details.
*/
typedef enum{
CO_ERR_REG_GENERIC_ERR = 0x01U, /**< bit 0, generic error */
CO_ERR_REG_CURRENT = 0x02U, /**< bit 1, current */
CO_ERR_REG_VOLTAGE = 0x04U, /**< bit 2, voltage */
CO_ERR_REG_TEMPERATURE = 0x08U, /**< bit 3, temperature */
CO_ERR_REG_COMM_ERR = 0x10U, /**< bit 4, communication error (overrun, error state) */
CO_ERR_REG_DEV_PROFILE = 0x20U, /**< bit 5, device profile specific */
CO_ERR_REG_RESERVED = 0x40U, /**< bit 6, reserved (always 0) */
CO_ERR_REG_MANUFACTURER = 0x80U /**< bit 7, manufacturer specific */
}CO_errorRegisterBitmask_t;
typedef enum {
CO_ERR_REG_GENERIC_ERR = 0x01U, /**< bit 0, generic error */
CO_ERR_REG_CURRENT = 0x02U, /**< bit 1, current */
CO_ERR_REG_VOLTAGE = 0x04U, /**< bit 2, voltage */
CO_ERR_REG_TEMPERATURE = 0x08U, /**< bit 3, temperature */
CO_ERR_REG_COMMUNICATION = 0x10U, /**< bit 4, communication error */
CO_ERR_REG_DEV_PROFILE = 0x20U, /**< bit 5, device profile specific */
CO_ERR_REG_RESERVED = 0x40U, /**< bit 6, reserved (always 0) */
CO_ERR_REG_MANUFACTURER = 0x80U /**< bit 7, manufacturer specific */
} CO_errorRegister_t;
/**
* @defgroup CO_EM_errorCodes CANopen Error codes
* @{
* CANopen Error code
*
* Standard error codes according to CiA DS-301 and DS-401.
*/
#define CO_EMC_NO_ERROR 0x0000U /**< 0x00xx, error Reset or No Error */
#define CO_EMC_GENERIC 0x1000U /**< 0x10xx, Generic Error */
#define CO_EMC_CURRENT 0x2000U /**< 0x20xx, Current */
#define CO_EMC_CURRENT_INPUT 0x2100U /**< 0x21xx, Current, device input side */
#define CO_EMC_CURRENT_INSIDE 0x2200U /**< 0x22xx, Current inside the device */
#define CO_EMC_CURRENT_OUTPUT 0x2300U /**< 0x23xx, Current, device output side */
#define CO_EMC_VOLTAGE 0x3000U /**< 0x30xx, Voltage */
#define CO_EMC_VOLTAGE_MAINS 0x3100U /**< 0x31xx, Mains Voltage */
#define CO_EMC_VOLTAGE_INSIDE 0x3200U /**< 0x32xx, Voltage inside the device */
#define CO_EMC_VOLTAGE_OUTPUT 0x3300U /**< 0x33xx, Output Voltage */
#define CO_EMC_TEMPERATURE 0x4000U /**< 0x40xx, Temperature */
#define CO_EMC_TEMP_AMBIENT 0x4100U /**< 0x41xx, Ambient Temperature */
#define CO_EMC_TEMP_DEVICE 0x4200U /**< 0x42xx, Device Temperature */
#define CO_EMC_HARDWARE 0x5000U /**< 0x50xx, Device Hardware */
#define CO_EMC_SOFTWARE_DEVICE 0x6000U /**< 0x60xx, Device Software */
#define CO_EMC_SOFTWARE_INTERNAL 0x6100U /**< 0x61xx, Internal Software */
#define CO_EMC_SOFTWARE_USER 0x6200U /**< 0x62xx, User Software */
#define CO_EMC_DATA_SET 0x6300U /**< 0x63xx, Data Set */
#define CO_EMC_ADDITIONAL_MODUL 0x7000U /**< 0x70xx, Additional Modules */
#define CO_EMC_MONITORING 0x8000U /**< 0x80xx, Monitoring */
#define CO_EMC_COMMUNICATION 0x8100U /**< 0x81xx, Communication */
#define CO_EMC_CAN_OVERRUN 0x8110U /**< 0x8110, CAN Overrun (Objects lost) */
#define CO_EMC_CAN_PASSIVE 0x8120U /**< 0x8120, CAN in Error Passive Mode */
#define CO_EMC_HEARTBEAT 0x8130U /**< 0x8130, Life Guard Error or Heartbeat Error */
#define CO_EMC_BUS_OFF_RECOVERED 0x8140U /**< 0x8140, recovered from bus off */
#define CO_EMC_CAN_ID_COLLISION 0x8150U /**< 0x8150, CAN-ID collision */
#define CO_EMC_PROTOCOL_ERROR 0x8200U /**< 0x82xx, Protocol Error */
#define CO_EMC_PDO_LENGTH 0x8210U /**< 0x8210, PDO not processed due to length error */
#define CO_EMC_PDO_LENGTH_EXC 0x8220U /**< 0x8220, PDO length exceeded */
#define CO_EMC_DAM_MPDO 0x8230U /**< 0x8230, DAM MPDO not processed, destination object not available */
#define CO_EMC_SYNC_DATA_LENGTH 0x8240U /**< 0x8240, Unexpected SYNC data length */
#define CO_EMC_RPDO_TIMEOUT 0x8250U /**< 0x8250, RPDO timeout */
#define CO_EMC_TIME_DATA_LENGTH 0x8260U /**< 0x8260, Unexpected TIME data length */
#define CO_EMC_EXTERNAL_ERROR 0x9000U /**< 0x90xx, External Error */
#define CO_EMC_ADDITIONAL_FUNC 0xF000U /**< 0xF0xx, Additional Functions */
#define CO_EMC_DEVICE_SPECIFIC 0xFF00U /**< 0xFFxx, Device specific */
typedef enum {
/** 0x00xx, error Reset or No Error */
CO_EMC_NO_ERROR = 0x0000U,
/** 0x10xx, Generic Error */
CO_EMC_GENERIC = 0x1000U,
/** 0x20xx, Current */
CO_EMC_CURRENT = 0x2000U,
/** 0x21xx, Current, device input side */
CO_EMC_CURRENT_INPUT = 0x2100U,
/** 0x22xx, Current inside the device */
CO_EMC_CURRENT_INSIDE = 0x2200U,
/** 0x23xx, Current, device output side */
CO_EMC_CURRENT_OUTPUT = 0x2300U,
/** 0x30xx, Voltage */
CO_EMC_VOLTAGE = 0x3000U,
/** 0x31xx, Mains Voltage */
CO_EMC_VOLTAGE_MAINS = 0x3100U,
/** 0x32xx, Voltage inside the device */
CO_EMC_VOLTAGE_INSIDE = 0x3200U,
/** 0x33xx, Output Voltage */
CO_EMC_VOLTAGE_OUTPUT = 0x3300U,
/** 0x40xx, Temperature */
CO_EMC_TEMPERATURE = 0x4000U,
/** 0x41xx, Ambient Temperature */
CO_EMC_TEMP_AMBIENT = 0x4100U,
/** 0x42xx, Device Temperature */
CO_EMC_TEMP_DEVICE = 0x4200U,
/** 0x50xx, Device Hardware */
CO_EMC_HARDWARE = 0x5000U,
/** 0x60xx, Device Software */
CO_EMC_SOFTWARE_DEVICE = 0x6000U,
/** 0x61xx, Internal Software */
CO_EMC_SOFTWARE_INTERNAL = 0x6100U,
/** 0x62xx, User Software */
CO_EMC_SOFTWARE_USER = 0x6200U,
/** 0x63xx, Data Set */
CO_EMC_DATA_SET = 0x6300U,
/** 0x70xx, Additional Modules */
CO_EMC_ADDITIONAL_MODUL = 0x7000U,
/** 0x80xx, Monitoring */
CO_EMC_MONITORING = 0x8000U,
/** 0x81xx, Communication */
CO_EMC_COMMUNICATION = 0x8100U,
/** 0x8110, CAN Overrun (Objects lost) */
CO_EMC_CAN_OVERRUN = 0x8110U,
/** 0x8120, CAN in Error Passive Mode */
CO_EMC_CAN_PASSIVE = 0x8120U,
/** 0x8130, Life Guard Error or Heartbeat Error */
CO_EMC_HEARTBEAT = 0x8130U,
/** 0x8140, recovered from bus off */
CO_EMC_BUS_OFF_RECOVERED = 0x8140U,
/** 0x8150, CAN-ID collision */
CO_EMC_CAN_ID_COLLISION = 0x8150U,
/** 0x82xx, Protocol Error */
CO_EMC_PROTOCOL_ERROR = 0x8200U,
/** 0x8210, PDO not processed due to length error */
CO_EMC_PDO_LENGTH = 0x8210U,
/** 0x8220, PDO length exceeded */
CO_EMC_PDO_LENGTH_EXC = 0x8220U,
/** 0x8230, DAM MPDO not processed, destination object not available */
CO_EMC_DAM_MPDO = 0x8230U,
/** 0x8240, Unexpected SYNC data length */
CO_EMC_SYNC_DATA_LENGTH = 0x8240U,
/** 0x8250, RPDO timeout */
CO_EMC_RPDO_TIMEOUT = 0x8250U,
/** 0x8260, Unexpected TIME data length */
CO_EMC_TIME_DATA_LENGTH = 0x8260U,
/** 0x90xx, External Error */
CO_EMC_EXTERNAL_ERROR = 0x9000U,
/** 0xF0xx, Additional Functions */
CO_EMC_ADDITIONAL_FUNC = 0xF000U,
/** 0xFFxx, Device specific */
CO_EMC_DEVICE_SPECIFIC = 0xFF00U,
#define CO_EMC401_OUT_CUR_HI 0x2310U /**< 0x2310, DS401, Current at outputs too high (overload) */
#define CO_EMC401_OUT_SHORTED 0x2320U /**< 0x2320, DS401, Short circuit at outputs */
#define CO_EMC401_OUT_LOAD_DUMP 0x2330U /**< 0x2330, DS401, Load dump at outputs */
#define CO_EMC401_IN_VOLT_HI 0x3110U /**< 0x3110, DS401, Input voltage too high */
#define CO_EMC401_IN_VOLT_LOW 0x3120U /**< 0x3120, DS401, Input voltage too low */
#define CO_EMC401_INTERN_VOLT_HI 0x3210U /**< 0x3210, DS401, Internal voltage too high */
#define CO_EMC401_INTERN_VOLT_LO 0x3220U /**< 0x3220, DS401, Internal voltage too low */
#define CO_EMC401_OUT_VOLT_HIGH 0x3310U /**< 0x3310, DS401, Output voltage too high */
#define CO_EMC401_OUT_VOLT_LOW 0x3320U /**< 0x3320, DS401, Output voltage too low */
/** @} */
/** 0x2310, DS401, Current at outputs too high (overload) */
CO_EMC401_OUT_CUR_HI = 0x2310U,
/** 0x2320, DS401, Short circuit at outputs */
CO_EMC401_OUT_SHORTED = 0x2320U,
/** 0x2330, DS401, Load dump at outputs */
CO_EMC401_OUT_LOAD_DUMP = 0x2330U,
/** 0x3110, DS401, Input voltage too high */
CO_EMC401_IN_VOLT_HI = 0x3110U,
/** 0x3120, DS401, Input voltage too low */
CO_EMC401_IN_VOLT_LOW = 0x3120U,
/** 0x3210, DS401, Internal voltage too high */
CO_EMC401_INTERN_VOLT_HI = 0x3210U,
/** 0x3220, DS401, Internal voltage too low */
CO_EMC401_INTERN_VOLT_LO = 0x3220U,
/** 0x3310, DS401, Output voltage too high */
CO_EMC401_OUT_VOLT_HIGH = 0x3310U,
/** 0x3320, DS401, Output voltage too low */
CO_EMC401_OUT_VOLT_LOW = 0x3320U,
} CO_EM_errorCode_t;
/**
* @defgroup CO_EM_errorStatusBits Error status bits
* @{
* Error status bits
*
* Internal indication of the error condition.
*
* Each error condition is specified by unique index from 0x00 up to 0xFF.
* Variable (from manufacturer section in the Object
* Dictionary) contains up to 0xFF bits (32bytes) for the identification of the
* specific error condition. (Type of the variable is CANopen OCTET_STRING.)
* Bits for internal indication of the error condition. Each error condition is
* specified by unique index from 0x00 up to 0xFF.
*
* If specific error occurs in the stack or in the application, CO_errorReport()
* sets specific bit in the _Error Status Bits_ variable. If bit was already
* set, function returns without any action. Otherwise it prepares emergency
* message.
* sets specific bit in the _errorStatusBit_ variable from @ref CO_EM_t. If bit
* was already set, function returns without any action. Otherwise it prepares
* emergency message.
*
* CO_errorReport(), CO_errorReset() or CO_isError() functions are called
* with unique index as an argument. (However CO_errorReport(), for example, may
* be used with the same index on multiple places in the code.)
*
* Macros defined below are combination of two constants: index and
* @ref CO_EM_errorCodes. They represents specific error conditions. They are
* used as double argument for CO_errorReport(), CO_errorReset() and
* CO_isError() functions.
*
* Stack uses first 6 bytes of the _Error Status Bits_ variable. Device profile
* or application may define own macros for Error status bits using
* @ref CO_EM_MANUFACTURER_START and @ref CO_EM_MANUFACTURER_END values. Note that
* _Error Status Bits_ must be large enough (up to 32 bytes).
* Maximum size (in bits) of the _errorStatusBit_ variable is specified by
* @ref CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (set to 10*8 bits by default). Stack
* uses first 6 bytes. Additional 4 bytes are pre-defined for manufacturer
* or device specific error indications, by default.
*/
#define CO_EM_NO_ERROR 0x00U /**< 0x00, Error Reset or No Error */
#define CO_EM_CAN_BUS_WARNING 0x01U /**< 0x01, communication, info, CAN bus warning limit reached */
#define CO_EM_RXMSG_WRONG_LENGTH 0x02U /**< 0x02, communication, info, Wrong data length of the received CAN message */
#define CO_EM_RXMSG_OVERFLOW 0x03U /**< 0x03, communication, info, Previous received CAN message wasn't processed yet */
#define CO_EM_RPDO_WRONG_LENGTH 0x04U /**< 0x04, communication, info, Wrong data length of received PDO */
#define CO_EM_RPDO_OVERFLOW 0x05U /**< 0x05, communication, info, Previous received PDO wasn't processed yet */
#define CO_EM_CAN_RX_BUS_PASSIVE 0x06U /**< 0x06, communication, info, CAN receive bus is passive */
#define CO_EM_CAN_TX_BUS_PASSIVE 0x07U /**< 0x07, communication, info, CAN transmit bus is passive */
#define CO_EM_NMT_WRONG_COMMAND 0x08U /**< 0x08, communication, info, Wrong NMT command received */
#define CO_EM_TIME_TIMEOUT 0x09U /**< 0x09, communication, info, TIME message timeout */
#define CO_EM_TIME_LENGTH 0x0AU /**< 0x0A, communication, info, Unexpected TIME data length */
#define CO_EM_0B_unused 0x0BU /**< 0x0B, (unused) */
#define CO_EM_0C_unused 0x0CU /**< 0x0C, (unused) */
#define CO_EM_0D_unused 0x0DU /**< 0x0D, (unused) */
#define CO_EM_0E_unused 0x0EU /**< 0x0E, (unused) */
#define CO_EM_0F_unused 0x0FU /**< 0x0F, (unused) */
typedef enum {
/** 0x00, Error Reset or No Error */
CO_EM_NO_ERROR = 0x00U,
/** 0x01, communication, info, CAN bus warning limit reached */
CO_EM_CAN_BUS_WARNING = 0x01U,
/** 0x02, communication, info, Wrong data length of the received CAN
* message */
CO_EM_RXMSG_WRONG_LENGTH = 0x02U,
/** 0x03, communication, info, Previous received CAN message wasn't
* processed yet */
CO_EM_RXMSG_OVERFLOW = 0x03U,
/** 0x04, communication, info, Wrong data length of received PDO */
CO_EM_RPDO_WRONG_LENGTH = 0x04U,
/** 0x05, communication, info, Previous received PDO wasn't processed yet */
CO_EM_RPDO_OVERFLOW = 0x05U,
/** 0x06, communication, info, CAN receive bus is passive */
CO_EM_CAN_RX_BUS_PASSIVE = 0x06U,
/** 0x07, communication, info, CAN transmit bus is passive */
CO_EM_CAN_TX_BUS_PASSIVE = 0x07U,
/** 0x08, communication, info, Wrong NMT command received */
CO_EM_NMT_WRONG_COMMAND = 0x08U,
/** 0x09, communication, info, TIME message timeout */
CO_EM_TIME_TIMEOUT = 0x09U,
/** 0x0A, communication, info, Unexpected TIME data length */
CO_EM_TIME_LENGTH = 0x0AU,
/** 0x0B, communication, info, (unused) */
CO_EM_0B_unused = 0x0BU,
/** 0x0C, communication, info, (unused) */
CO_EM_0C_unused = 0x0CU,
/** 0x0D, communication, info, (unused) */
CO_EM_0D_unused = 0x0DU,
/** 0x0E, communication, info, (unused) */
CO_EM_0E_unused = 0x0EU,
/** 0x0F, communication, info, (unused) */
CO_EM_0F_unused = 0x0FU,
#define CO_EM_10_unused 0x10U /**< 0x10, (unused) */
#define CO_EM_11_unused 0x11U /**< 0x11, (unused) */
#define CO_EM_CAN_TX_BUS_OFF 0x12U /**< 0x12, communication, critical, CAN transmit bus is off */
#define CO_EM_CAN_RXB_OVERFLOW 0x13U /**< 0x13, communication, critical, CAN module receive buffer has overflowed */
#define CO_EM_CAN_TX_OVERFLOW 0x14U /**< 0x14, communication, critical, CAN transmit buffer has overflowed */
#define CO_EM_TPDO_OUTSIDE_WINDOW 0x15U /**< 0x15, communication, critical, TPDO is outside SYNC window */
#define CO_EM_16_unused 0x16U /**< 0x16, (unused) */
#define CO_EM_17_unused 0x17U /**< 0x17, (unused) */
#define CO_EM_SYNC_TIME_OUT 0x18U /**< 0x18, communication, critical, SYNC message timeout */
#define CO_EM_SYNC_LENGTH 0x19U /**< 0x19, communication, critical, Unexpected SYNC data length */
#define CO_EM_PDO_WRONG_MAPPING 0x1AU /**< 0x1A, communication, critical, Error with PDO mapping */
#define CO_EM_HEARTBEAT_CONSUMER 0x1BU /**< 0x1B, communication, critical, Heartbeat consumer timeout */
#define CO_EM_HB_CONSUMER_REMOTE_RESET 0x1CU /**< 0x1C, communication, critical, Heartbeat consumer detected remote node reset */
#define CO_EM_1D_unused 0x1DU /**< 0x1D, (unused) */
#define CO_EM_1E_unused 0x1EU /**< 0x1E, (unused) */
#define CO_EM_1F_unused 0x1FU /**< 0x1F, (unused) */
/** 0x10, communication, critical, (unused) */
CO_EM_10_unused = 0x10U,
/** 0x11, communication, critical, (unused) */
CO_EM_11_unused = 0x11U,
/** 0x12, communication, critical, CAN transmit bus is off */
CO_EM_CAN_TX_BUS_OFF = 0x12U,
/** 0x13, communication, critical, CAN module receive buffer has
* overflowed */
CO_EM_CAN_RXB_OVERFLOW = 0x13U,
/** 0x14, communication, critical, CAN transmit buffer has overflowed */
CO_EM_CAN_TX_OVERFLOW = 0x14U,
/** 0x15, communication, critical, TPDO is outside SYNC window */
CO_EM_TPDO_OUTSIDE_WINDOW = 0x15U,
/** 0x16, communication, critical, (unused) */
CO_EM_16_unused = 0x16U,
/** 0x17, communication, critical, (unused) */
CO_EM_17_unused = 0x17U,
/** 0x18, communication, critical, SYNC message timeout */
CO_EM_SYNC_TIME_OUT = 0x18U,
/** 0x19, communication, critical, Unexpected SYNC data length */
CO_EM_SYNC_LENGTH = 0x19U,
/** 0x1A, communication, critical, Error with PDO mapping */
CO_EM_PDO_WRONG_MAPPING = 0x1AU,
/** 0x1B, communication, critical, Heartbeat consumer timeout */
CO_EM_HEARTBEAT_CONSUMER = 0x1BU,
/** 0x1C, communication, critical, Heartbeat consumer detected remote node
* reset */
CO_EM_HB_CONSUMER_REMOTE_RESET = 0x1CU,
/** 0x1D, communication, critical, (unused) */
CO_EM_1D_unused = 0x1DU,
/** 0x1E, communication, critical, (unused) */
CO_EM_1E_unused = 0x1EU,
/** 0x1F, communication, critical, (unused) */
CO_EM_1F_unused = 0x1FU,
#define CO_EM_EMERGENCY_BUFFER_FULL 0x20U /**< 0x20, generic, info, Emergency buffer is full, Emergency message wasn't sent */
#define CO_EM_21_unused 0x21U /**< 0x21, (unused) */
#define CO_EM_MICROCONTROLLER_RESET 0x22U /**< 0x22, generic, info, Microcontroller has just started */
#define CO_EM_23_unused 0x23U /**< 0x23, (unused) */
#define CO_EM_24_unused 0x24U /**< 0x24, (unused) */
#define CO_EM_25_unused 0x25U /**< 0x25, (unused) */
#define CO_EM_26_unused 0x26U /**< 0x26, (unused) */
#define CO_EM_27_unused 0x27U /**< 0x27, (unused) */
/** 0x20, generic, info, Emergency buffer is full, Emergency message wasn't
* sent */
CO_EM_EMERGENCY_BUFFER_FULL = 0x20U,
/** 0x21, generic, info, (unused) */
CO_EM_21_unused = 0x21U,
/** 0x22, generic, info, Microcontroller has just started */
CO_EM_MICROCONTROLLER_RESET = 0x22U,
/** 0x23, generic, info, (unused) */
CO_EM_23_unused = 0x23U,
/** 0x24, generic, info, (unused) */
CO_EM_24_unused = 0x24U,
/** 0x25, generic, info, (unused) */
CO_EM_25_unused = 0x25U,
/** 0x26, generic, info, (unused) */
CO_EM_26_unused = 0x26U,
/** 0x27, generic, info, (unused) */
CO_EM_27_unused = 0x27U,
#define CO_EM_WRONG_ERROR_REPORT 0x28U /**< 0x28, generic, critical, Wrong parameters to CO_errorReport() function */
#define CO_EM_ISR_TIMER_OVERFLOW 0x29U /**< 0x29, generic, critical, Timer task has overflowed */
#define CO_EM_MEMORY_ALLOCATION_ERROR 0x2AU /**< 0x2A, generic, critical, Unable to allocate memory for objects */
#define CO_EM_GENERIC_ERROR 0x2BU /**< 0x2B, generic, critical, Generic error, test usage */
#define CO_EM_GENERIC_SOFTWARE_ERROR 0x2CU /**< 0x2C, generic, critical, Software error */
#define CO_EM_INCONSISTENT_OBJECT_DICT 0x2DU /**< 0x2D, generic, critical, Object dictionary does not match the software */
#define CO_EM_CALCULATION_OF_PARAMETERS 0x2EU /**< 0x2E, generic, critical, Error in calculation of device parameters */
#define CO_EM_NON_VOLATILE_MEMORY 0x2FU /**< 0x2F, generic, critical, Error with access to non volatile device memory */
/** 0x28, generic, critical, Wrong parameters to CO_errorReport() function*/
CO_EM_WRONG_ERROR_REPORT = 0x28U,
/** 0x29, generic, critical, Timer task has overflowed */
CO_EM_ISR_TIMER_OVERFLOW = 0x29U,
/** 0x2A, generic, critical, Unable to allocate memory for objects */
CO_EM_MEMORY_ALLOCATION_ERROR = 0x2AU,
/** 0x2B, generic, critical, Generic error, test usage */
CO_EM_GENERIC_ERROR = 0x2BU,
/** 0x2C, generic, critical, Software error */
CO_EM_GENERIC_SOFTWARE_ERROR = 0x2CU,
/** 0x2D, generic, critical, Object dictionary does not match the software*/
CO_EM_INCONSISTENT_OBJECT_DICT = 0x2DU,
/** 0x2E, generic, critical, Error in calculation of device parameters */
CO_EM_CALCULATION_OF_PARAMETERS = 0x2EU,
/** 0x2F, generic, critical, Error with access to non volatile device memory
*/
CO_EM_NON_VOLATILE_MEMORY = 0x2FU,
#define CO_EM_MANUFACTURER_START 0x30U /**< 0x30, manufacturer, info, This can be used by macros to calculate error codes */
#define CO_EM_MANUFACTURER_END 0xFFU /**< 0xFF, manufacturer, info, This can be used by macros to check error codes */
/** @} */
/** 0x30+, manufacturer, info or critical, Error status buts, free to use by
* manufacturer. By default bits 0x30..0x3F are set as informational and
* bits 0x40..0x4F are set as critical. Manufacturer critical bits sets the
* error register, as specified by @ref CO_CONFIG_ERR_CONDITION_MANUFACTURER
*/
CO_EM_MANUFACTURER_START = 0x30U,
/** (@ref CO_CONFIG_EM_ERR_STATUS_BITS_COUNT - 1), largest value of the
* Error status bit. */
CO_EM_MANUFACTURER_END = CO_CONFIG_EM_ERR_STATUS_BITS_COUNT - 1
} CO_EM_errorStatusBits_t;
/**
* Size of internal buffer, whwre emergencies are stored after CO_errorReport().
* Buffer is cleared by CO_EM_process().
* Emergency object.
*/
#define CO_EM_INTERNAL_BUFFER_SIZE 10
typedef struct {
/** Bitfield for the internal indication of the error condition. */
uint8_t errorStatusBits[CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8];
/** Pointer to error register in object dictionary at 0x1001,00. */
uint8_t *errorRegister;
/** Old CAN error status bitfield */
uint16_t CANerrorStatusOld;
/**
* Emergerncy object for CO_errorReport(). It contains error buffer, to which new emergency
* messages are written, when CO_errorReport() is called. This object is included in
* CO_EMpr_t object.
*/
typedef struct{
uint8_t *errorStatusBits; /**< From CO_EM_init() */
uint8_t errorStatusBitsSize; /**< From CO_EM_init() */
/** Internal buffer for storing unsent emergency messages.*/
uint8_t buf[CO_EM_INTERNAL_BUFFER_SIZE * 8];
uint8_t *bufEnd; /**< End+1 address of the above buffer */
uint8_t *bufWritePtr; /**< Write pointer in the above buffer */
uint8_t *bufReadPtr; /**< Read pointer in the above buffer */
uint8_t bufFull; /**< True if above buffer is full */
uint8_t wrongErrorReport; /**< Error in arguments to CO_errorReport() */
#if ((CO_CONFIG_EM) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
/** From CO_EM_initCallbackPre() or NULL */
void (*pFunctSignalPre)(void *object);
/** From CO_EM_initCallbackPre() or NULL */
void *functSignalObjectPre;
#if ((CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)) \
|| defined CO_DOXYGEN
/** Internal circular FIFO buffer for storing pre-processed emergency
* messages. Messages are added by @ref CO_error() function. All messages
* are later post-processed by @ref CO_EM_process() function. Fifo is also
* used for error history - OD object 0x1003, "Pre-defined error field". */
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER) || defined CO_DOXYGEN
uint32_t fifo[CO_CONFIG_EM_BUFFER_SIZE + 1][2];
#else
uint32_t fifo[CO_CONFIG_EM_BUFFER_SIZE + 1][1];
#endif
/** Pointer for the fifo buffer, where next emergency message will be
* written by @ref CO_error() function. */
uint8_t fifoWrPtr;
/** Pointer for the fifo, where next emergency message has to be
* post-processed by @ref CO_EM_process() function. If equal to bufWrPtr,
* then all messages has been post-processed. */
uint8_t fifoPpPtr;
/** Indication of overflow - messages in buffer are not post-processed */
uint8_t fifoOverflow;
/** Count of emergency messages in fifo, used for OD object 0x1003 */
uint8_t fifoCount;
#endif /* (CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY) */
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER) || defined CO_DOXYGEN
/** True, if emergency producer is enabled, from Object dictionary */
bool_t producerEnabled;
/** Copy of CANopen node ID, from CO_EM_init() */
uint8_t nodeId;
/** From CO_EM_init() */
CO_CANmodule_t *CANdevTx;
/** CAN transmit buffer */
CO_CANtx_t *CANtxBuff;
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PROD_CONFIGURABLE) || defined CO_DOXYGEN
/** COB ID of emergency message, from Object dictionary */
uint16_t producerCanId;
/** From CO_EM_init() */
uint16_t CANdevTxIdx;
#endif
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PROD_INHIBIT) || defined CO_DOXYGEN
/** Inhibit time for emergency message, from Object dictionary */
uint32_t inhibitEmTime_us;
/**< Internal timer for inhibit time */
uint32_t inhibitEmTimer;
#endif
#endif /* (CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER */
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_CONSUMER) || defined CO_DOXYGEN
/** From CO_EM_initCallbackRx() or NULL */
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode);
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode);
#endif
}CO_EM_t;
/**
* Report error condition.
*
* Function is called on any error condition inside CANopen stack and may also
* be called by application on custom error condition. Emergency message is sent
* after the first occurance of specific error. In case of critical error, device
* will not be able to stay in NMT_OPERATIONAL state.
*
* Function is short and may be used form any task or interrupt.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits.
* @param errorCode from @ref CO_EM_errorCodes.
* @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message.
* It contains optional additional information inside emergency message.
*/
void CO_errorReport(CO_EM_t *em, const uint8_t errorBit, const uint16_t errorCode, const uint32_t infoCode);
/**
* Reset error condition.
*
* Function is called if any error condition is solved. Emergency message is sent
* with @ref CO_EM_errorCodes 0x0000.
*
* Function is short and may be used form any task or interrupt.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits.
* @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency message.
*/
void CO_errorReset(CO_EM_t *em, const uint8_t errorBit, const uint32_t infoCode);
/**
* Check specific error condition.
*
* Function returns 1, if specific internal error is present. Otherwise it returns 0.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits.
*
* @return false: Error is not present.
* @return true: Error is present.
*/
bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit);
#ifdef CO_DOXYGEN
/** Skip section, if CO_SDOserver.h is not included */
#define CO_SDO_SERVER_H
#if ((CO_CONFIG_EM) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
/** From CO_EM_initCallbackPre() or NULL */
void (*pFunctSignalPre)(void *object);
/** From CO_EM_initCallbackPre() or NULL */
void *functSignalObjectPre;
#endif
#ifdef CO_SDO_SERVER_H
} CO_EM_t;
/**
* Error control and Emergency object. It controls internal error state and
* sends emergency message, if error condition was reported. Object is initialized
* by CO_EM_init(). It contains CO_EM_t object.
*/
typedef struct{
uint8_t *errorRegister; /**< From CO_EM_init() */
uint32_t *preDefErr; /**< From CO_EM_init() */
uint8_t preDefErrSize; /**< From CO_EM_init() */
uint8_t preDefErrNoOfErrors;/**< Number of active errors in preDefErr */
uint32_t inhibitEmTimer; /**< Internal timer for emergency message */
CO_EM_t *em; /**< CO_EM_t sub object is included here */
uint16_t CANerrorStatusOld;/**< Old CAN error status bitfield */
CO_CANmodule_t *CANdev; /**< From CO_EM_init() */
CO_CANtx_t *CANtxBuff; /**< CAN transmit buffer */
}CO_EMpr_t;
/**
* Initialize Error control and Emergency object.
* Initialize Emergency object.
*
* Function must be called in the communication reset section.
*
* @param emPr This object will be initialized.
* @param em Emergency object defined separately. Will be included in emPr and
* initialized too.
* @param SDO SDO server object.
* @param errorStatusBits Pointer to _Error Status Bits_ array from Object Dictionary
* (manufacturer specific section). See @ref CO_EM_errorStatusBits.
* @param errorStatusBitsSize Total size of the above array. Must be >= 6.
* @param errorRegister Pointer to _Error Register_ (Object dictionary, index 0x1001).
* @param preDefErr Pointer to _Pre defined error field_ array from Object
* dictionary, index 0x1003.
* @param preDefErrSize Size of the above array.
* @param CANdevRx CAN device for Emergency reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param em This object will be initialized.
* @param OD_1001_errReg OD entry for 0x1001 - "Error register", entry is
* required, without IO extension.
* @param OD_1014_cobIdEm OD entry for 0x1014 - "COB-ID EMCY", entry is
* required, IO extension is required.
* @param CANdevTx CAN device for Emergency transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
* @param CANidTxEM CAN identifier for Emergency message.
* @param OD_1015_InhTime OD entry for 0x1015 - "Inhibit time EMCY", entry is
* optional (can be NULL), IO extension is optional for runtime configuration.
* @param OD_1003_preDefErr OD entry for 0x1003 - "Pre-defined error field".
* Emergency object has own memory buffer for this entry. Entry is optional,
* IO extension is required.
* @param OD_statusBits Custom OD entry for accessing errorStatusBits from
* @ref CO_EM_t. Entry must have bytestring_t of size
* (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT/8) bytes available for read/write access
* on subindex 0. Emergency object has own memory buffer for this entry. Entry
* is optional, IO extension is required.
* @param CANdevRx CAN device for Emergency consumer reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param nodeId CANopen node ID of this device (for default emergency producer)
*
* @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
* @return @ref CO_ReturnError_t CO_ERROR_NO in case of success.
*/
CO_ReturnError_t CO_EM_init(
CO_EM_t *em,
CO_EMpr_t *emPr,
CO_SDO_t *SDO,
uint8_t *errorStatusBits,
uint8_t errorStatusBitsSize,
uint8_t *errorRegister,
uint32_t *preDefErr,
uint8_t preDefErrSize,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint16_t CANidTxEM);
CO_ReturnError_t CO_EM_init(CO_EM_t *em,
const OD_entry_t *OD_1001_errReg,
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER) || defined CO_DOXYGEN
const OD_entry_t *OD_1014_cobIdEm,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PROD_INHIBIT) || defined CO_DOXYGEN
const OD_entry_t *OD_1015_InhTime,
#endif
#endif
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_HISTORY) || defined CO_DOXYGEN
const OD_entry_t *OD_1003_preDefErr,
#endif
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_STATUS_BITS) || defined CO_DOXYGEN
const OD_entry_t *OD_statusBits,
#endif
#if ((CO_CONFIG_EM) & CO_CONFIG_EM_CONSUMER) || defined CO_DOXYGEN
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
#endif
const uint8_t nodeId);
#if ((CO_CONFIG_EM) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
@ -423,13 +501,13 @@ CO_ReturnError_t CO_EM_init(
* immediately start mainline thread, which calls CO_EM_process() function.
*
* @param em This object.
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
* @param object Pointer to object, which will be passed to pFunctSignal(). Can
* be NULL
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_EM_initCallbackPre(
CO_EM_t *em,
void *object,
void (*pFunctSignal)(void *object));
void CO_EM_initCallbackPre(CO_EM_t *em,
void *object,
void (*pFunctSignal)(void *object));
#endif
@ -449,13 +527,12 @@ void CO_EM_initCallbackPre(
* @param em This object.
* @param pFunctSignalRx Pointer to the callback function. Not called if NULL.
*/
void CO_EM_initCallbackRx(
CO_EM_t *em,
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode));
void CO_EM_initCallbackRx(CO_EM_t *em,
void (*pFunctSignalRx)(const uint16_t ident,
const uint16_t errorCode,
const uint8_t errorRegister,
const uint8_t errorBit,
const uint32_t infoCode));
#endif
@ -463,24 +540,86 @@ void CO_EM_initCallbackRx(
* Process Error control and Emergency object.
*
* Function must be called cyclically. It verifies some communication errors,
* calculates bit 0 and bit 4 from _Error register_ and sends emergency message
* calculates OD object 0x1001 - "Error register" and sends emergency message
* if necessary.
*
* @param emPr This object.
* @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or NMT_OPERATIONAL.
* @param timeDifference_us Time difference from previous function call in [microseconds].
* @param emInhTime _Inhibit time EMCY_ in [100*us] (object dictionary, index 0x1015).
* @param em This object.
* @param NMTisPreOrOperational True if this node is NMT_PRE_OPERATIONAL or
* NMT_OPERATIONAL state.
* @param timeDifference_us Time difference from previous function call in
* [microseconds].
* @param [out] timerNext_us info to OS - see CO_process().
*/
void CO_EM_process(
CO_EMpr_t *emPr,
bool_t NMTisPreOrOperational,
uint32_t timeDifference_us,
uint16_t emInhTime,
uint32_t *timerNext_us);
void CO_EM_process(CO_EM_t *em,
bool_t NMTisPreOrOperational,
uint32_t timeDifference_us,
uint32_t *timerNext_us);
#endif
/**
* Set or reset error condition.
*
* Function can be called on any error condition inside CANopen stack or
* application. Function first checks change of error condition (setError is
* true and error bit wasn't set or setError is false and error bit was set
* before). If changed, then Emergency message is prepared and record in history
* is added. Emergency message is later sent by CO_EM_process() function.
*
* Function is short and thread safe.
*
* @param em Emergency object.
* @param setError True if error occurred or false if error resolved.
* @param errorBit from @ref CO_EM_errorStatusBits_t.
* @param errorCode from @ref CO_EM_errorCode_t.
* @param infoCode 32 bit value is passed to bytes 4...7 of the Emergency
* message. It contains optional additional information.
*/
void CO_error(CO_EM_t *em, bool_t setError, const uint8_t errorBit,
uint16_t errorCode, uint32_t infoCode);
/**
* Report error condition, for description of parameters see @ref CO_error.
*/
#define CO_errorReport(em, errorBit, errorCode, infoCode) \
CO_error(em, true, errorBit, errorCode, infoCode)
/**
* Reset error condition, for description of parameters see @ref CO_error.
*/
#define CO_errorReset(em, errorBit, infoCode) \
CO_error(em, false, errorBit, CO_EMC_NO_ERROR, infoCode)
/**
* Check specific error condition.
*
* Function returns true, if specific internal error is present.
*
* @param em Emergency object.
* @param errorBit from @ref CO_EM_errorStatusBits_t.
*
* @return true if Error is present.
*/
static inline bool_t CO_isError(CO_EM_t *em, const uint8_t errorBit) {
uint8_t index = errorBit >> 3;
uint8_t bitmask = 1 << (errorBit & 0x7);
return (em == NULL || index >= (CO_CONFIG_EM_ERR_STATUS_BITS_COUNT / 8)
|| (em->errorStatusBits[index] & bitmask) != 0) ? true : false;
}
/**
* Get error register
*
* @param em Emergency object.
*
* @return Error register or 0 if doesn't exist.
*/
static inline uint8_t CO_getErrorRegister(CO_EM_t *em) {
return (em == NULL || em->errorRegister == NULL) ? 0 : *em->errorRegister;
}
/** @} */ /* CO_Emergency */

View file

@ -33,79 +33,78 @@
* message with correct identifier will be received. For more information and
* description of parameters see file CO_driver.h.
*/
static void CO_NMT_receive(void *object, void *msg){
CO_NMT_t *NMT;
uint8_t nodeId;
static void CO_NMT_receive(void *object, void *msg) {
uint8_t DLC = CO_CANrxMsg_readDLC(msg);
uint8_t *data = CO_CANrxMsg_readData(msg);
uint8_t command = data[0];
uint8_t nodeId = data[1];
NMT = (CO_NMT_t*)object; /* this is the correct pointer type of the first argument */
CO_NMT_t *NMT = (CO_NMT_t*)object;
nodeId = data[1];
if (DLC == 2 && (nodeId == 0 || nodeId == NMT->nodeId)) {
NMT->internalCommand = command;
if((DLC == 2) && ((nodeId == 0) || (nodeId == NMT->nodeId))){
uint8_t command = data[0];
#if (CO_CONFIG_NMT) & (CO_CONFIG_NMT_CALLBACK_CHANGE | CO_CONFIG_FLAG_CALLBACK_PRE)
CO_NMT_internalState_t currentOperatingState = NMT->operatingState;
#endif
switch(command){
case CO_NMT_ENTER_OPERATIONAL:
if((*NMT->emPr->errorRegister) == 0U){
NMT->operatingState = CO_NMT_OPERATIONAL;
}
break;
case CO_NMT_ENTER_STOPPED:
NMT->operatingState = CO_NMT_STOPPED;
break;
case CO_NMT_ENTER_PRE_OPERATIONAL:
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
break;
case CO_NMT_RESET_NODE:
NMT->resetCommand = CO_RESET_APP;
break;
case CO_NMT_RESET_COMMUNICATION:
NMT->resetCommand = CO_RESET_COMM;
break;
default:
break;
}
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE
if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){
NMT->pFunctNMT(NMT->operatingState);
}
#endif
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_CALLBACK_PRE
/* Optional signal to RTOS, which can resume task, which handles NMT. */
if(NMT->pFunctSignalPre != NULL && currentOperatingState!=NMT->operatingState) {
NMT->pFunctSignalPre(NMT->functSignalObjectPre);
}
/* Optional signal to RTOS, which can resume task, which handles NMT. */
if (NMT->pFunctSignalPre != NULL) {
NMT->pFunctSignalPre(NMT->functSignalObjectPre);
}
#endif
}
}
/*
* Custom function for writing OD variable "Producer heartbeat time"
*
* For more information see file CO_ODinterface.h, OD_subEntry_t.
*/
static OD_size_t OD_write_1017(OD_stream_t *stream, uint8_t subIndex,
const void *buf, OD_size_t count,
ODR_t *returnCode)
{
/* "count" is already verified in *_init() function */
if (stream == NULL || subIndex != 0 || buf == NULL || returnCode == NULL) {
if (returnCode != NULL) *returnCode = ODR_DEV_INCOMPAT;
return 0;
}
CO_NMT_t *NMT = (CO_NMT_t *)stream->object;
*returnCode = ODR_OK;
/* update object, send Heartbeat immediately */
NMT->HBproducerTime_us = (uint32_t)CO_getUint16(buf) * 1000;
NMT->HBproducerTimer = 0;
/* write value to the original location in the Object Dictionary */
return OD_writeOriginal(stream, subIndex, buf, count, returnCode);
}
/******************************************************************************/
CO_ReturnError_t CO_NMT_init(
CO_NMT_t *NMT,
CO_EMpr_t *emPr,
uint8_t nodeId,
uint16_t firstHBTime_ms,
CO_CANmodule_t *NMT_CANdevRx,
uint16_t NMT_rxIdx,
uint16_t CANidRxNMT,
CO_CANmodule_t *NMT_CANdevTx,
uint16_t NMT_txIdx,
uint16_t CANidTxNMT,
CO_CANmodule_t *HB_CANdevTx,
uint16_t HB_txIdx,
uint16_t CANidTxHB)
CO_ReturnError_t CO_NMT_init(CO_NMT_t *NMT,
const OD_entry_t *OD_1017_ProducerHbTime,
CO_EM_t *em,
uint8_t nodeId,
CO_NMT_control_t NMTcontrol,
uint16_t firstHBTime_ms,
CO_CANmodule_t *NMT_CANdevRx,
uint16_t NMT_rxIdx,
uint16_t CANidRxNMT,
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
CO_CANmodule_t *NMT_CANdevTx,
uint16_t NMT_txIdx,
uint16_t CANidTxNMT,
#endif
CO_CANmodule_t *HB_CANdevTx,
uint16_t HB_txIdx,
uint16_t CANidTxHB)
{
CO_ReturnError_t ret = CO_ERROR_NO;
/* verify arguments */
if (NMT == NULL || emPr == NULL || NMT_CANdevRx == NULL || HB_CANdevTx == NULL
if (NMT == NULL || OD_1017_ProducerHbTime == NULL || em == NULL
|| NMT_CANdevRx == NULL || HB_CANdevTx == NULL
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
|| NMT_CANdevTx == NULL
#endif
@ -117,11 +116,26 @@ CO_ReturnError_t CO_NMT_init(
memset(NMT, 0, sizeof(CO_NMT_t));
/* Configure object variables */
NMT->operatingState = CO_NMT_INITIALIZING;
NMT->operatingStatePrev = CO_NMT_INITIALIZING;
NMT->nodeId = nodeId;
NMT->firstHBTime = (int32_t)firstHBTime_ms * 1000;
NMT->emPr = emPr;
NMT->operatingState = CO_NMT_INITIALIZING;
NMT->operatingStatePrev = CO_NMT_INITIALIZING;
NMT->nodeId = nodeId;
NMT->NMTcontrol = NMTcontrol;
NMT->em = em;
NMT->HBproducerTimer = (int32_t)firstHBTime_ms * 1000;
/* get and verify required "Producer heartbeat time" from Object Dict. */
uint16_t HBprodTime_ms;
if (OD_get_u16(OD_1017_ProducerHbTime, 0, &HBprodTime_ms, true) != ODR_OK) {
return CO_ERROR_OD_PARAMETERS;
}
NMT->HBproducerTime_us = (uint32_t)HBprodTime_ms * 1000;
OD_extensionIO_init(OD_1017_ProducerHbTime,
(void *) NMT,
OD_readOriginal,
OD_write_1017);
if (NMT->HBproducerTimer > NMT->HBproducerTime_us) {
NMT->HBproducerTimer = NMT->HBproducerTime_us;
}
/* configure NMT CAN reception */
ret = CO_CANrxBufferInit(
@ -131,7 +145,10 @@ CO_ReturnError_t CO_NMT_init(
0x7FF, /* mask */
0, /* rtr */
(void*)NMT, /* object passed to receive function */
CO_NMT_receive); /* this function will process received message */
CO_NMT_receive); /* this function will process received message*/
if (ret != CO_ERROR_NO) {
return ret;
}
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
/* configure NMT CAN transmission */
@ -144,7 +161,7 @@ CO_ReturnError_t CO_NMT_init(
2, /* number of data bytes */
0); /* synchronous message flag bit */
if (NMT->NMT_TXbuff == NULL) {
ret = CO_ERROR_ILLEGAL_ARGUMENT;
return CO_ERROR_ILLEGAL_ARGUMENT;
}
#endif
@ -157,9 +174,8 @@ CO_ReturnError_t CO_NMT_init(
0, /* rtr */
1, /* number of data bytes */
0); /* synchronous message flag bit */
if (NMT->HB_TXbuff == NULL) {
ret = CO_ERROR_ILLEGAL_ARGUMENT;
return CO_ERROR_ILLEGAL_ARGUMENT;
}
return ret;
@ -167,10 +183,9 @@ CO_ReturnError_t CO_NMT_init(
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_CALLBACK_PRE
void CO_NMT_initCallbackPre(
CO_NMT_t *NMT,
void *object,
void (*pFunctSignal)(void *object))
void CO_NMT_initCallbackPre(CO_NMT_t *NMT,
void *object,
void (*pFunctSignal)(void *object))
{
if (NMT != NULL) {
NMT->pFunctSignalPre = pFunctSignal;
@ -182,13 +197,12 @@ void CO_NMT_initCallbackPre(
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE
/******************************************************************************/
void CO_NMT_initCallbackChanged(
CO_NMT_t *NMT,
void (*pFunctNMT)(CO_NMT_internalState_t state))
void CO_NMT_initCallbackChanged(CO_NMT_t *NMT,
void (*pFunctNMT)(CO_NMT_internalState_t state))
{
if(NMT != NULL){
if (NMT != NULL) {
NMT->pFunctNMT = pFunctNMT;
if(NMT->pFunctNMT != NULL){
if (NMT->pFunctNMT != NULL) {
NMT->pFunctNMT(NMT->operatingState);
}
}
@ -197,135 +211,112 @@ void CO_NMT_initCallbackChanged(
/******************************************************************************/
CO_NMT_reset_cmd_t CO_NMT_process(
CO_NMT_t *NMT,
uint32_t timeDifference_us,
uint16_t HBtime_ms,
uint32_t NMTstartup,
uint8_t errorRegister,
const uint8_t errorBehavior[],
uint32_t *timerNext_us)
CO_NMT_reset_cmd_t CO_NMT_process(CO_NMT_t *NMT,
CO_NMT_internalState_t *NMTstate,
uint32_t timeDifference_us,
uint32_t *timerNext_us)
{
(void)timerNext_us; /* may be unused */
CO_NMT_internalState_t NMTstateCpy = NMT->operatingState;
CO_NMT_reset_cmd_t resetCommand = CO_RESET_NOT;
bool_t NNTinit = NMTstateCpy == CO_NMT_INITIALIZING;
uint8_t CANpassive;
CO_NMT_internalState_t currentOperatingState = NMT->operatingState;
uint32_t HBtime = (uint32_t)HBtime_ms * 1000;
NMT->HBproducerTimer += timeDifference_us;
NMT->HBproducerTimer = (NMT->HBproducerTimer > timeDifference_us )
? (NMT->HBproducerTimer - timeDifference_us) : 0;
/* Send heartbeat producer message if:
* - First start, send bootup message or
* - HB producer and Timer expired or
* - HB producer and NMT->operatingState changed, but not from initialised */
if ((NMT->operatingState == CO_NMT_INITIALIZING) ||
(HBtime != 0 && (NMT->HBproducerTimer >= HBtime ||
NMT->operatingState != NMT->operatingStatePrev)
* - HB producer enabled and: Timer expired or NMT->operatingState changed*/
if (NNTinit || (NMT->HBproducerTime_us != 0
&& (NMT->HBproducerTimer == 0
|| NMTstateCpy != NMT->operatingStatePrev)
)) {
/* Start from the beginning. If OS is slow, time sliding may occur. However,
* heartbeat is not for synchronization, it is for health report. */
NMT->HBproducerTimer = 0;
NMT->HB_TXbuff->data[0] = (uint8_t) NMT->operatingState;
NMT->HB_TXbuff->data[0] = (uint8_t) NMTstateCpy;
CO_CANsend(NMT->HB_CANdevTx, NMT->HB_TXbuff);
if (NMT->operatingState == CO_NMT_INITIALIZING) {
/* After bootup messages send first heartbeat earlier */
if (HBtime > NMT->firstHBTime) {
NMT->HBproducerTimer = HBtime - NMT->firstHBTime;
}
if (NMTstateCpy == CO_NMT_INITIALIZING) {
/* NMT slave self starting */
if (NMTstartup == 0x00000008U) NMT->operatingState = CO_NMT_OPERATIONAL;
else NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
NMTstateCpy = (NMT->NMTcontrol & CO_NMT_STARTUP_TO_OPERATIONAL) != 0
? CO_NMT_OPERATIONAL : CO_NMT_PRE_OPERATIONAL;
}
else {
/* Start timer from the beginning. If OS is slow, time sliding may
* occur. However, heartbeat is not for synchronization, it is for
* health report. In case of initializing, timer is set in the
* CO_NMT_init() function with pre-defined value. */
NMT->HBproducerTimer = NMT->HBproducerTime_us;
}
}
NMT->operatingStatePrev = NMT->operatingState;
NMT->operatingStatePrev = NMTstateCpy;
/* CAN passive flag */
CANpassive = 0;
if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_PASSIVE) || CO_isError(NMT->emPr->em, CO_EM_CAN_RX_BUS_PASSIVE))
CANpassive = 1;
/* in case of error enter pre-operational state */
if(errorBehavior && (NMT->operatingState == CO_NMT_OPERATIONAL)){
if(CANpassive && (errorBehavior[2] == 0 || errorBehavior[2] == 2)) errorRegister |= 0x10;
if(errorRegister){
/* Communication error */
if(errorRegister & CO_ERR_REG_COMM_ERR){
if(errorBehavior[1] == 0){
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
}
else if(errorBehavior[1] == 2){
NMT->operatingState = CO_NMT_STOPPED;
}
else if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF)
|| CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER)
|| CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET))
{
if(errorBehavior[0] == 0){
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
}
else if(errorBehavior[0] == 2){
NMT->operatingState = CO_NMT_STOPPED;
}
}
}
/* Generic error */
if(errorRegister & CO_ERR_REG_GENERIC_ERR){
if (errorBehavior[3] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
else if (errorBehavior[3] == 2) NMT->operatingState = CO_NMT_STOPPED;
}
/* Device profile error */
if(errorRegister & CO_ERR_REG_DEV_PROFILE){
if (errorBehavior[4] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
else if (errorBehavior[4] == 2) NMT->operatingState = CO_NMT_STOPPED;
}
/* Manufacturer specific error */
if(errorRegister & CO_ERR_REG_MANUFACTURER){
if (errorBehavior[5] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
else if (errorBehavior[5] == 2) NMT->operatingState = CO_NMT_STOPPED;
}
/* if operational state is lost, send HB immediately. */
if(NMT->operatingState != CO_NMT_OPERATIONAL)
NMT->HBproducerTimer = HBtime;
/* process internal NMT commands, received from CO_NMT_receive() or
* CO_NMT_sendCommand() */
if (NMT->internalCommand != 0) {
switch (NMT->internalCommand) {
case CO_NMT_ENTER_OPERATIONAL:
NMTstateCpy = CO_NMT_OPERATIONAL;
break;
case CO_NMT_ENTER_STOPPED:
NMTstateCpy = CO_NMT_STOPPED;
break;
case CO_NMT_ENTER_PRE_OPERATIONAL:
NMTstateCpy = CO_NMT_PRE_OPERATIONAL;
break;
case CO_NMT_RESET_NODE:
resetCommand = CO_RESET_APP;
break;
case CO_NMT_RESET_COMMUNICATION:
resetCommand = CO_RESET_COMM;
break;
default:
break;
}
NMT->internalCommand = 0;
}
/* verify NMT transitions based on error register */
bool_t busOff_HB = (NMT->NMTcontrol & CO_NMT_ERR_ON_BUSOFF_HB) != 0
&& (CO_isError(NMT->em, CO_EM_CAN_TX_BUS_OFF)
|| CO_isError(NMT->em, CO_EM_HEARTBEAT_CONSUMER)
|| CO_isError(NMT->em, CO_EM_HB_CONSUMER_REMOTE_RESET));
bool_t errRegMasked = (NMT->NMTcontrol & CO_NMT_ERR_ON_ERR_REG) != 0
&& (CO_getErrorRegister(NMT->em) & NMT->NMTcontrol) != 0;
if (NMTstateCpy == CO_NMT_OPERATIONAL && (busOff_HB || errRegMasked)) {
NMTstateCpy = (NMT->NMTcontrol & CO_NMT_ERR_TO_STOPPED) != 0
? CO_NMT_STOPPED : CO_NMT_PRE_OPERATIONAL;
}
else if ((NMT->NMTcontrol & CO_NMT_ERR_FREE_TO_OPERATIONAL) != 0
&& NMTstateCpy == CO_NMT_PRE_OPERATIONAL && !busOff_HB && !errRegMasked
) {
NMTstateCpy = CO_NMT_OPERATIONAL;
}
if (currentOperatingState != NMT->operatingState) {
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE
/* Notify operating state change */
if (NMT->operatingStatePrev != NMTstateCpy || NNTinit) {
if (NMT->pFunctNMT != NULL) {
NMT->pFunctNMT(NMT->operatingState);
}
#endif
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
/* execute next CANopen processing immediately */
if (timerNext_us != NULL) {
*timerNext_us = 0;
}
#endif
}
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
/* Calculate, when next Heartbeat needs to be send and lower timerNext_us if necessary. */
if (HBtime != 0 && timerNext_us != NULL) {
if (NMT->HBproducerTimer < HBtime) {
uint32_t diff = HBtime - NMT->HBproducerTimer;
if (*timerNext_us > diff) {
*timerNext_us = diff;
}
} else {
*timerNext_us = 0;
NMT->pFunctNMT(NMTstateCpy);
}
}
#endif
return (CO_NMT_reset_cmd_t) NMT->resetCommand;
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
/* Calculate, when next Heartbeat needs to be send */
if (NMT->HBproducerTime_us != 0 && timerNext_us != NULL) {
if (NMT->operatingStatePrev != NMTstateCpy) {
*timerNext_us = 0;
}
else if (*timerNext_us > NMT->HBproducerTimer) {
*timerNext_us = NMT->HBproducerTimer;
}
}
#endif
NMT->operatingState = NMTstateCpy;
if (NMTstate != NULL) *NMTstate = NMTstateCpy;
return resetCommand;
}
@ -335,46 +326,19 @@ CO_ReturnError_t CO_NMT_sendCommand(CO_NMT_t *NMT,
CO_NMT_command_t command,
uint8_t nodeID)
{
CO_ReturnError_t error = CO_ERROR_NO;
/* verify arguments */
if (NMT == NULL) {
error = CO_ERROR_TX_UNCONFIGURED;
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Apply NMT command also to this node, if set so. */
if (error == CO_ERROR_NO && (nodeID == 0 || nodeID == NMT->nodeId)) {
switch (command) {
case CO_NMT_ENTER_OPERATIONAL:
if ((*NMT->emPr->errorRegister) == 0) {
NMT->operatingState = CO_NMT_OPERATIONAL;
}
break;
case CO_NMT_ENTER_STOPPED:
NMT->operatingState = CO_NMT_STOPPED;
break;
case CO_NMT_ENTER_PRE_OPERATIONAL:
NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
break;
case CO_NMT_RESET_NODE:
NMT->resetCommand = CO_RESET_APP;
break;
case CO_NMT_RESET_COMMUNICATION:
NMT->resetCommand = CO_RESET_COMM;
break;
default:
error = CO_ERROR_ILLEGAL_ARGUMENT;
break;
}
if (nodeID == 0 || nodeID == NMT->nodeId) {
NMT->internalCommand = command;
}
/* Send NMT master message. */
if (error == CO_ERROR_NO) {
NMT->NMT_TXbuff->data[0] = command;
NMT->NMT_TXbuff->data[1] = nodeID;
error = CO_CANsend(NMT->NMT_CANdevTx, NMT->NMT_TXbuff);
}
return error;
NMT->NMT_TXbuff->data[0] = command;
NMT->NMT_TXbuff->data[1] = nodeID;
return CO_CANsend(NMT->NMT_CANdevTx, NMT->NMT_TXbuff);
}
#endif

View file

@ -27,7 +27,7 @@
#define CO_NMT_HEARTBEAT_H
#include "301/CO_driver.h"
#include "301/CO_SDOserver.h"
#include "301/CO_ODinterface.h"
#include "301/CO_Emergency.h"
/* default configuration, see CO_config.h */
@ -46,95 +46,158 @@ extern "C" {
*
* CANopen Network management and Heartbeat producer protocol.
*
* CANopen device can be in one of the #CO_NMT_internalState_t
* CANopen device can be in one of the @ref CO_NMT_internalState_t
* - Initializing. It is active before CANopen is initialized.
* - Pre-operational. All CANopen objects are active, except PDOs.
* - Operational. Process data objects (PDOs) are active too.
* - Stopped. Only Heartbeat producer and NMT consumer are active.
*
* NMT master can change the internal state of the devices by sending
* #CO_NMT_command_t.
* @ref CO_NMT_command_t.
*
* ###NMT message contents:
* ### NMT message contents:
*
* Byte | Description
* -----|-----------------------------------------------------------
* 0 | #CO_NMT_command_t
* 0 | @ref CO_NMT_command_t
* 1 | Node ID. If zero, command addresses all nodes.
*
* ###Heartbeat message contents:
* ### Heartbeat message contents:
*
* Byte | Description
* -----|-----------------------------------------------------------
* 0 | #CO_NMT_internalState_t
* 0 | @ref CO_NMT_internalState_t
*
* @see #CO_Default_CAN_ID_t
* See @ref CO_Default_CAN_ID_t for CAN identifiers.
*/
/**
* Internal network state of the CANopen node
*/
typedef enum{
CO_NMT_UNKNOWN = -1, /**< Device state is unknown (for heartbeat consumer) */
CO_NMT_INITIALIZING = 0, /**< Device is initializing */
CO_NMT_PRE_OPERATIONAL = 127, /**< Device is in pre-operational state */
CO_NMT_OPERATIONAL = 5, /**< Device is in operational state */
CO_NMT_STOPPED = 4 /**< Device is stopped */
}CO_NMT_internalState_t;
typedef enum {
/** -1, Device state is unknown (for heartbeat consumer) */
CO_NMT_UNKNOWN = -1,
/** 0, Device is initializing */
CO_NMT_INITIALIZING = 0,
/** 127, Device is in pre-operational state */
CO_NMT_PRE_OPERATIONAL = 127,
/** 5, Device is in operational state */
CO_NMT_OPERATIONAL = 5,
/** 4, Device is stopped */
CO_NMT_STOPPED = 4
} CO_NMT_internalState_t;
/**
* Commands from NMT master.
*/
typedef enum{
CO_NMT_ENTER_OPERATIONAL = 1, /**< Start device */
CO_NMT_ENTER_STOPPED = 2, /**< Stop device */
CO_NMT_ENTER_PRE_OPERATIONAL = 128, /**< Put device into pre-operational */
CO_NMT_RESET_NODE = 129, /**< Reset device */
CO_NMT_RESET_COMMUNICATION = 130 /**< Reset CANopen communication on device */
}CO_NMT_command_t;
typedef enum {
/** 1, Start device */
CO_NMT_ENTER_OPERATIONAL = 1,
/** 2, Stop device */
CO_NMT_ENTER_STOPPED = 2,
/** 128, Put device into pre-operational */
CO_NMT_ENTER_PRE_OPERATIONAL = 128,
/** 129, Reset device */
CO_NMT_RESET_NODE = 129,
/** 130, Reset CANopen communication on device */
CO_NMT_RESET_COMMUNICATION = 130
} CO_NMT_command_t;
/**
* Return code for CO_NMT_process() that tells application code what to
* Return code from CO_NMT_process() that tells application code what to
* reset.
*/
typedef enum{
CO_RESET_NOT = 0,/**< Normal return, no action */
CO_RESET_COMM = 1,/**< Application must provide communication reset. */
CO_RESET_APP = 2,/**< Application must provide complete device reset */
CO_RESET_QUIT = 3 /**< Application must quit, no reset of microcontroller (command is not requested by the stack.) */
}CO_NMT_reset_cmd_t;
typedef enum {
/** 0, Normal return, no action */
CO_RESET_NOT = 0,
/** 1, Application must provide communication reset. */
CO_RESET_COMM = 1,
/** 2, Application must provide complete device reset */
CO_RESET_APP = 2,
/** 3, Application must quit, no reset of microcontroller (command is not
* requested by the stack.) */
CO_RESET_QUIT = 3
} CO_NMT_reset_cmd_t;
/**
* NMT consumer and Heartbeat producer object, initialized by CO_NMT_init()
* NMT control bitfield for NMT internal state.
*
* Variable of this type is passed to @ref CO_NMT_process() function. It
* controls behavior of the @ref CO_NMT_internalState_t of the device according
* to CANopen error register.
*
* Internal NMT state is controlled also with external NMT command,
* @ref CO_NMT_setInternalState() or @ref CO_NMT_sendCommand() functions.
*/
typedef struct{
CO_NMT_internalState_t operatingState; /**< Current NMT operating state. */
CO_NMT_internalState_t operatingStatePrev; /**< Previous NMT operating state. */
uint8_t resetCommand; /**< If different than zero, device will reset */
uint8_t nodeId; /**< CANopen Node ID of this device */
uint32_t HBproducerTimer;/**< Internal timer for HB producer */
uint32_t firstHBTime; /**< From CO_NMT_init() */
CO_EMpr_t *emPr; /**< From CO_NMT_init() */
typedef enum {
/** First 8 bits can be used to specify bitmask for the
* @ref CO_errorRegister_t, to get relevant bits for the calculation. */
CO_NMT_ERR_REG_MASK = 0x00FFU,
/** If bit is set, then device enters NMT operational state after the
* initialization phase, otherwise it enters NMT pre-operational state. */
CO_NMT_STARTUP_TO_OPERATIONAL = 0x0100U,
/** If bit is set and device is operational, it enters NMT pre-operational
* or stopped state, if CAN bus is off or heartbeat consumer timeout is
* detected. */
CO_NMT_ERR_ON_BUSOFF_HB = 0x1000U,
/** If bit is set and device is operational, it enters NMT pre-operational
* or stopped state, if masked CANopen error register is different than
* zero. */
CO_NMT_ERR_ON_ERR_REG = 0x2000U,
/** If bit is set and CO_NMT_ERR_ON_xx condition is met, then device will
* enter NMT stopped state, otherwise it will enter NMT pre-op state. */
CO_NMT_ERR_TO_STOPPED = 0x4000U,
/** If bit is set and device is pre-operational, it enters NMT operational
* state automatically, if conditions from CO_NMT_ERR_ON_xx are all false.*/
CO_NMT_ERR_FREE_TO_OPERATIONAL = 0x8000U
} CO_NMT_control_t;
/**
* NMT consumer and Heartbeat producer object
*/
typedef struct {
/** Current NMT operating state. */
uint8_t operatingState;
/** Previous NMT operating state. */
uint8_t operatingStatePrev;
/** NMT internal command from CO_NMT_receive() or CO_NMT_sendCommand(),
* processed in CO_NMT_process(). 0 if no command or CO_NMT_command_t */
uint8_t internalCommand;
/** From CO_NMT_init() */
uint8_t nodeId;
/** From CO_NMT_init() */
CO_NMT_control_t NMTcontrol;
/** Producer heartbeat time, calculated from OD 0x1017 */
uint32_t HBproducerTime_us;
/** Internal timer for HB producer */
uint32_t HBproducerTimer;
/** From CO_NMT_init() */
CO_EM_t *em;
#if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER) || defined CO_DOXYGEN
CO_CANmodule_t *NMT_CANdevTx; /**< From CO_NMT_init() */
CO_CANtx_t *NMT_TXbuff; /**< CAN transmit buffer for NMT master message */
/** From CO_NMT_init() */
CO_CANmodule_t *NMT_CANdevTx;
/** CAN transmit buffer for NMT master message */
CO_CANtx_t *NMT_TXbuff;
#endif
CO_CANmodule_t *HB_CANdevTx; /**< From CO_NMT_init() */
CO_CANtx_t *HB_TXbuff; /**< CAN transmit buffer for heartbeat message */
/** From CO_NMT_init() */
CO_CANmodule_t *HB_CANdevTx;
/** CAN transmit buffer for heartbeat message */
CO_CANtx_t *HB_TXbuff;
#if ((CO_CONFIG_NMT) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
/** From CO_NMT_initCallbackPre() or NULL */
void (*pFunctSignalPre)(void *object);
void (*pFunctSignalPre)(void *object);
/** From CO_NMT_initCallbackPre() or NULL */
void *functSignalObjectPre;
void *functSignalObjectPre;
#endif
#if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE) || defined CO_DOXYGEN
void (*pFunctNMT)(CO_NMT_internalState_t state); /**< From CO_NMT_initCallbackChanged() or NULL */
/** From CO_NMT_initCallbackChanged() or NULL */
void (*pFunctNMT)(CO_NMT_internalState_t state);
#endif
}CO_NMT_t;
} CO_NMT_t;
/**
@ -143,11 +206,16 @@ typedef struct{
* Function must be called in the communication reset section.
*
* @param NMT This object will be initialized.
* @param emPr Emergency main object.
* @param OD_1017_ProducerHbTime OD entry for 0x1017 -"Producer heartbeat time",
* entry is required, IO extension is optional for runtime configuration.
* @param em Emergency object.
* @param nodeId CANopen Node ID of this device.
* @param firstHBTime_ms Time between bootup and first heartbeat message in milliseconds.
* If firstHBTime is greater than _Producer Heartbeat time_
* (object dictionary, index 0x1017), latter is used instead.
* @param NMTcontrol Control variable for calculation of NMT internal state,
* based on error register, startup and runtime behavior.
* @param firstHBTime_ms Time between bootup and first heartbeat message in
* milliseconds. If firstHBTime_ms is greater than "Producer Heartbeat time"
* (OD object 0x1017), latter is used instead. Entry is required, IO extension
* is optional.
* @param NMT_CANdevRx CAN device for NMT reception.
* @param NMT_rxIdx Index of receive buffer in above CAN device.
* @param CANidRxNMT CAN identifier for NMT receive message.
@ -158,22 +226,25 @@ typedef struct{
* @param HB_txIdx Index of transmit buffer in the above CAN device.
* @param CANidTxHB CAN identifier for HB message.
*
* @return #CO_ReturnError_t CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
* @return #CO_ReturnError_t CO_ERROR_NO on success.
*/
CO_ReturnError_t CO_NMT_init(
CO_NMT_t *NMT,
CO_EMpr_t *emPr,
uint8_t nodeId,
uint16_t firstHBTime_ms,
CO_CANmodule_t *NMT_CANdevRx,
uint16_t NMT_rxIdx,
uint16_t CANidRxNMT,
CO_CANmodule_t *NMT_CANdevTx,
uint16_t NMT_txIdx,
uint16_t CANidTxNMT,
CO_CANmodule_t *HB_CANdevTx,
uint16_t HB_txIdx,
uint16_t CANidTxHB);
CO_ReturnError_t CO_NMT_init(CO_NMT_t *NMT,
const OD_entry_t *OD_1017_ProducerHbTime,
CO_EM_t *em,
uint8_t nodeId,
CO_NMT_control_t NMTcontrol,
uint16_t firstHBTime_ms,
CO_CANmodule_t *NMT_CANdevRx,
uint16_t NMT_rxIdx,
uint16_t CANidRxNMT,
#if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER) || defined CO_DOXYGEN
CO_CANmodule_t *NMT_CANdevTx,
uint16_t NMT_txIdx,
uint16_t CANidTxNMT,
#endif
CO_CANmodule_t *HB_CANdevTx,
uint16_t HB_txIdx,
uint16_t CANidTxHB);
#if ((CO_CONFIG_NMT) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
@ -185,13 +256,13 @@ CO_ReturnError_t CO_NMT_init(
* Callback is called after NMT message is received from the CAN bus.
*
* @param NMT This object.
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
* @param object Pointer to object, which will be passed to pFunctSignal().
* Can be NULL
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
*/
void CO_NMT_initCallbackPre(
CO_NMT_t *NMT,
void *object,
void (*pFunctSignal)(void *object));
void CO_NMT_initCallbackPre(CO_NMT_t *NMT,
void *object,
void (*pFunctSignal)(void *object));
#endif
@ -200,19 +271,15 @@ void CO_NMT_initCallbackPre(
* Initialize NMT callback function.
*
* Function initializes optional callback function, which is called after
* NMT State change has occured. Function may wake up external task which
* handles NMT events.
* The first call is made immediately to give the consumer the current NMT state.
*
* @remark Be aware that the callback function is run inside the CAN receive
* function context. Depending on the driver, this might be inside an interrupt!
* NMT State change has occurred. Function may wake up external task which
* handles NMT events. The first call is made immediately to give the consumer
* the current NMT state.
*
* @param NMT This object.
* @param pFunctNMT Pointer to the callback function. Not called if NULL.
*/
void CO_NMT_initCallbackChanged(
CO_NMT_t *NMT,
void (*pFunctNMT)(CO_NMT_internalState_t state));
void CO_NMT_initCallbackChanged(CO_NMT_t *NMT,
void (*pFunctNMT)(CO_NMT_internalState_t state));
#endif
@ -222,25 +289,17 @@ void CO_NMT_initCallbackChanged(
* Function must be called cyclically.
*
* @param NMT This object.
* @param timeDifference_us Time difference from previous function call in [microseconds].
* @param HBtime_ms _Producer Heartbeat time_ (object dictionary, index 0x1017).
* @param NMTstartup _NMT startup behavior_ (object dictionary, index 0x1F80).
* @param errorRegister _Error register_ (object dictionary, index 0x1001).
* @param errorBehavior pointer to _Error behavior_ array (object dictionary, index 0x1029).
* Object controls, if device should leave NMT operational state.
* Length of array must be 6. If pointer is NULL, no calculation is made.
* @param [out] NMTstate If not NULL, CANopen NMT internal state is returned.
* @param timeDifference_us Time difference from previous function call in
* microseconds.
* @param [out] timerNext_us info to OS - see CO_process().
*
* @return #CO_NMT_reset_cmd_t
*/
CO_NMT_reset_cmd_t CO_NMT_process(
CO_NMT_t *NMT,
uint32_t timeDifference_us,
uint16_t HBtime_ms,
uint32_t NMTstartup,
uint8_t errorRegister,
const uint8_t errorBehavior[],
uint32_t *timerNext_us);
CO_NMT_reset_cmd_t CO_NMT_process(CO_NMT_t *NMT,
CO_NMT_internalState_t *NMTstate,
uint32_t timeDifference_us,
uint32_t *timerNext_us);
/**
@ -248,28 +307,44 @@ CO_NMT_reset_cmd_t CO_NMT_process(
*
* @param NMT This object.
*
* @return #CO_NMT_internalState_t
* @return @ref CO_NMT_internalState_t
*/
static inline CO_NMT_internalState_t CO_NMT_getInternalState(CO_NMT_t *NMT) {
return (NMT == NULL) ? CO_NMT_INITIALIZING : NMT->operatingState;
}
/**
* Set internal NMT state
*
* Functions sets state directly, without any checking. @ref CO_NMT_process()
* may also switch between states automatically, see @ref CO_NMT_control_t.
*
* @param NMT This object.
* @param state New state.
*/
static inline void CO_NMT_setInternalState(CO_NMT_t *NMT,
CO_NMT_internalState_t state)
{
if (NMT != NULL) NMT->operatingState = state;
}
#if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER) || defined CO_DOXYGEN
/**
* Send NMT master command.
*
* This functionality can only be used from NMT master. There is one exception
* where application from slave node may send NMT master command: If CANopen
* object 0x1F80 has value of **0x2**, then NMT slave shall execute the NMT
* service start remote node (CO_NMT_ENTER_OPERATIONAL) with nodeID set to 0.
* This functionality may only be used from NMT master, as specified by
* standard CiA302-2. Standard provides one exception, where application from
* slave node may send NMT master command: "If CANopen object 0x1F80 has value
* of **0x2**, then NMT slave shall execute the NMT service start remote node
* (CO_NMT_ENTER_OPERATIONAL) with nodeID set to 0."
*
* @param NMT This object.
* @param command NMT command from CO_NMT_command_t.
* @param nodeID Node ID of the remote node. 0 for all nodes including self.
*
* @return 0: Operation completed successfully.
* @return other: same as CO_CANsend().
* @return CO_ERROR_NO on success or CO_ReturnError_t from CO_CANsend().
*/
CO_ReturnError_t CO_NMT_sendCommand(CO_NMT_t *NMT,
CO_NMT_command_t command,

View file

@ -28,6 +28,8 @@
#include "301/CO_PDO.h"
#if (CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE)
/*
* Read received message from CAN module.
*
@ -1093,3 +1095,4 @@ void CO_TPDO_process(
else TPDO->sendRequest = 0;
}
}
#endif /* (CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE) */

View file

@ -39,6 +39,8 @@
CO_CONFIG_PDO_SYNC_ENABLE)
#endif
#if ((CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE)) || defined CO_DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
@ -436,4 +438,6 @@ void CO_TPDO_process(
}
#endif /*__cplusplus*/
#endif /* (CO_CONFIG_PDO) & (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE) */
#endif /* CO_PDO_H */

View file

@ -156,17 +156,100 @@ static void CO_SDOclient_receive(void *object, void *msg) {
}
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC
/*
* Custom function for writing OD variable _SDO client parameter_
*
* For more information see file CO_ODinterface.h, OD_subEntry_t.
*/
static OD_size_t OD_write_1280(OD_stream_t *stream, uint8_t subIndex,
const void *buf, OD_size_t count,
ODR_t *returnCode)
{
/* "count" is already verified in *_init() function */
if (stream == NULL || buf == NULL || returnCode == NULL) {
if (returnCode != NULL) *returnCode = ODR_DEV_INCOMPAT;
return 0;
}
CO_SDOclient_t *SDO_C = (CO_SDOclient_t *)stream->object;
*returnCode = ODR_OK;
uint32_t COB_ID;
uint8_t nodeId;
switch (subIndex) {
case 0: /* Highest sub-index supported */
*returnCode = ODR_READONLY;
return 0;
case 1: /* COB-ID client -> server */
COB_ID = CO_getUint32(buf);
/* SDO client must not be valid when changing COB_ID */
if ((COB_ID & 0x3FFFF800) != 0
|| ((uint16_t)COB_ID != (uint16_t)SDO_C->COB_IDClientToServer
&& SDO_C->valid && (COB_ID & 0x80000000) == 0)
) {
*returnCode = ODR_INVALID_VALUE;
return 0;
}
CO_SDOclient_setup(SDO_C,
COB_ID,
SDO_C->COB_IDServerToClient,
SDO_C->nodeIDOfTheSDOServer);
break;
case 2: /* COB-ID server -> client */
COB_ID = CO_getUint32(buf);
/* SDO client must not be valid when changing COB_ID */
if ((COB_ID & 0x3FFFF800) != 0
|| ((uint16_t)COB_ID != (uint16_t)SDO_C->COB_IDServerToClient
&& SDO_C->valid && (COB_ID & 0x80000000) == 0)
) {
*returnCode = ODR_INVALID_VALUE;
return 0;
}
CO_SDOclient_setup(SDO_C,
SDO_C->COB_IDClientToServer,
COB_ID,
SDO_C->nodeIDOfTheSDOServer);
break;
case 3: /* Node-ID of the SDO server */
nodeId = CO_getUint8(buf);
if (nodeId > 127) {
*returnCode = ODR_INVALID_VALUE;
return 0;
}
SDO_C->nodeIDOfTheSDOServer = nodeId;
break;
default:
*returnCode = ODR_SUB_NOT_EXIST;
return 0;
}
/* write value to the original location in the Object Dictionary */
return OD_writeOriginal(stream, subIndex, buf, count, returnCode);
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC */
/******************************************************************************/
CO_ReturnError_t CO_SDOclient_init(CO_SDOclient_t *SDO_C,
void *SDO,
CO_SDOclientPar_t *SDOClientPar,
const OD_t *OD,
const OD_entry_t *OD_1280_SDOcliPar,
uint8_t nodeId,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx)
{
/* verify arguments */
if (SDO_C == NULL || SDOClientPar == NULL || SDOClientPar->maxSubIndex != 3
if (SDO_C == NULL || OD_1280_SDOcliPar == NULL
|| OD_getIndex(OD_1280_SDOcliPar) < OD_H1280_SDO_CLIENT_1_PARAM
|| OD_getIndex(OD_1280_SDOcliPar) > (OD_H1280_SDO_CLIENT_1_PARAM + 0x7F)
|| CANdevRx==NULL || CANdevTx==NULL
) {
return CO_ERROR_ILLEGAL_ARGUMENT;
@ -174,32 +257,57 @@ CO_ReturnError_t CO_SDOclient_init(CO_SDOclient_t *SDO_C,
/* Configure object variables */
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL
SDO_C->SDO = (CO_SDO_t *)SDO;
SDO_C->OD = OD;
SDO_C->nodeId = nodeId;
#endif
SDO_C->SDOClientPar = SDOClientPar;
SDO_C->CANdevRx = CANdevRx;
SDO_C->CANdevRxIdx = CANdevRxIdx;
SDO_C->CANdevTx = CANdevTx;
SDO_C->CANdevTxIdx = CANdevTxIdx;
/* prepare circular fifo buffer */
CO_fifo_init(&SDO_C->bufFifo, SDO_C->buf,
CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1);
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE
SDO_C->pFunctSignal = NULL;
SDO_C->functSignalObject = NULL;
#endif
SDO_C->state = CO_SDO_ST_IDLE;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
/* prepare circular fifo buffer */
CO_fifo_init(&SDO_C->bufFifo, SDO_C->buf,
CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1);
SDO_C->COB_IDClientToServerPrev = 0;
SDO_C->COB_IDServerToClientPrev = 0;
CO_SDOclient_setup(SDO_C,
SDO_C->SDOClientPar->COB_IDClientToServer,
SDO_C->SDOClientPar->COB_IDServerToClient,
SDO_C->SDOClientPar->nodeIDOfTheSDOServer);
/* Get parameters from Object Dictionary (initial values) */
uint8_t maxSubIndex, nodeIDOfTheSDOServer;
uint32_t COB_IDClientToServer, COB_IDServerToClient;
ODR_t odRet0 = OD_get_u8(OD_1280_SDOcliPar, 0, &maxSubIndex, true);
ODR_t odRet1 = OD_get_u32(OD_1280_SDOcliPar, 1, &COB_IDClientToServer, true);
ODR_t odRet2 = OD_get_u32(OD_1280_SDOcliPar, 2, &COB_IDServerToClient, true);
ODR_t odRet3 = OD_get_u8(OD_1280_SDOcliPar, 3, &nodeIDOfTheSDOServer, true);
if (odRet0 != ODR_OK || maxSubIndex != 3
|| odRet1 != ODR_OK || odRet2 != ODR_OK || odRet3 != ODR_OK
) {
return CO_ERROR_OD_PARAMETERS;
}
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC
if (!OD_extensionIO_init(OD_1280_SDOcliPar,
(void *)SDO_C,
OD_readOriginal,
OD_write_1280)) {
return CO_ERROR_OD_PARAMETERS;
}
/* set to zero to make sure CO_SDOclient_setup() will reconfigure CAN */
SDO_C->COB_IDClientToServer = 0;
SDO_C->COB_IDServerToClient = 0;
#endif
CO_SDO_return_t cliSetupRet = CO_SDOclient_setup(SDO_C,
COB_IDClientToServer,
COB_IDServerToClient,
nodeIDOfTheSDOServer);
if (cliSetupRet != CO_SDO_RT_ok_communicationEnd) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
return CO_ERROR_NO;
}
@ -225,76 +333,65 @@ CO_SDO_return_t CO_SDOclient_setup(CO_SDOclient_t *SDO_C,
uint32_t COB_IDServerToClient,
uint8_t nodeIDOfTheSDOServer)
{
uint32_t idCtoS, idStoC;
uint8_t idNode;
/* verify parameters */
if (SDO_C == NULL
|| (COB_IDClientToServer & 0x7FFFF800L) != 0
|| (COB_IDServerToClient & 0x7FFFF800L) != 0
|| nodeIDOfTheSDOServer > 127
) {
if (SDO_C == NULL) {
return CO_SDO_RT_wrongArguments;
}
/* Configure object variables */
SDO_C->state = CO_SDO_ST_IDLE;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
SDO_C->nodeIDOfTheSDOServer = nodeIDOfTheSDOServer;
/* setup Object Dictionary variables */
if ((COB_IDClientToServer & 0x80000000L) != 0
|| (COB_IDServerToClient & 0x80000000L) != 0
|| nodeIDOfTheSDOServer == 0
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC
/* proceed only, if parameters change */
if (COB_IDClientToServer == SDO_C->COB_IDClientToServer
&& COB_IDServerToClient == SDO_C->COB_IDServerToClient
) {
/* SDO is NOT used */
idCtoS = 0x80000000L;
idStoC = 0x80000000L;
idNode = 0;
return CO_SDO_RT_ok_communicationEnd;
}
/* store variables */
SDO_C->COB_IDClientToServer = COB_IDClientToServer;
SDO_C->COB_IDServerToClient = COB_IDServerToClient;
#endif
/* verify valid bit */
uint16_t CanIdC2S = ((COB_IDClientToServer & 0x80000000L) == 0) ?
(uint16_t)(COB_IDClientToServer & 0x7FF) : 0;
uint16_t CanIdS2C = ((COB_IDServerToClient & 0x80000000L) == 0) ?
(uint16_t)(COB_IDServerToClient & 0x7FF) : 0;
if (CanIdC2S != 0 && CanIdS2C != 0) {
SDO_C->valid = true;
}
else {
if (COB_IDClientToServer == 0 || COB_IDServerToClient == 0) {
idCtoS = 0x600 + nodeIDOfTheSDOServer;
idStoC = 0x580 + nodeIDOfTheSDOServer;
}
else {
idCtoS = COB_IDClientToServer;
idStoC = COB_IDServerToClient;
}
idNode = nodeIDOfTheSDOServer;
CanIdC2S = 0;
CanIdS2C = 0;
SDO_C->valid = false;
}
SDO_C->SDOClientPar->COB_IDClientToServer = idCtoS;
SDO_C->SDOClientPar->COB_IDServerToClient = idStoC;
SDO_C->SDOClientPar->nodeIDOfTheSDOServer = idNode;
/* configure SDO client CAN reception */
CO_ReturnError_t ret = CO_CANrxBufferInit(
SDO_C->CANdevRx, /* CAN device */
SDO_C->CANdevRxIdx, /* rx buffer index */
CanIdS2C, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)SDO_C, /* object passed to receive function */
CO_SDOclient_receive); /* this function will process rx msg */
/* configure SDO client CAN reception, if differs. */
if (SDO_C->COB_IDClientToServerPrev != idCtoS
|| SDO_C->COB_IDServerToClientPrev != idStoC
) {
CO_ReturnError_t ret = CO_CANrxBufferInit(
SDO_C->CANdevRx, /* CAN device */
SDO_C->CANdevRxIdx, /* rx buffer index */
(uint16_t)idStoC, /* CAN identifier */
0x7FF, /* mask */
0, /* rtr */
(void*)SDO_C, /* object passed to receive function */
CO_SDOclient_receive); /* this function will process rx msg */
/* configure SDO client CAN transmission */
SDO_C->CANtxBuff = CO_CANtxBufferInit(
SDO_C->CANdevTx, /* CAN device */
SDO_C->CANdevTxIdx, /* index of buffer inside CAN module */
CanIdC2S, /* CAN identifier */
0, /* rtr */
8, /* number of data bytes */
0); /* synchronous message flag bit */
/* configure SDO client CAN transmission */
SDO_C->CANtxBuff = CO_CANtxBufferInit(
SDO_C->CANdevTx, /* CAN device */
SDO_C->CANdevTxIdx, /* index of buffer inside CAN module */
(uint16_t)idCtoS, /* CAN identifier */
0, /* rtr */
8, /* number of data bytes */
0); /* synchronous message flag bit */
SDO_C->COB_IDClientToServerPrev = idCtoS;
SDO_C->COB_IDServerToClientPrev = idStoC;
if (ret != CO_ERROR_NO || SDO_C->CANtxBuff == NULL) {
return CO_SDO_RT_wrongArguments;
}
if (ret != CO_ERROR_NO || SDO_C->CANtxBuff == NULL) {
return CO_SDO_RT_wrongArguments;
SDO_C->valid = false;
}
return CO_SDO_RT_ok_communicationEnd;
@ -312,7 +409,7 @@ CO_SDO_return_t CO_SDOclientDownloadInitiate(CO_SDOclient_t *SDO_C,
bool_t blockEnable)
{
/* verify parameters */
if (SDO_C == NULL) {
if (SDO_C == NULL || !SDO_C->valid) {
return CO_SDO_RT_wrongArguments;
}
@ -329,9 +426,10 @@ CO_SDO_return_t CO_SDOclientDownloadInitiate(CO_SDOclient_t *SDO_C,
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL
/* if node-ID of the SDO server is the same as node-ID of this node, then
* transfer data within this node */
if (SDO_C->SDO != NULL
&& SDO_C->SDOClientPar->nodeIDOfTheSDOServer == SDO_C->SDO->nodeId
if (SDO_C->OD != NULL && SDO_C->nodeId != 0
&& SDO_C->nodeIDOfTheSDOServer == SDO_C->nodeId
) {
SDO_C->OD_IO.write = NULL;
SDO_C->state = CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER;
}
else
@ -372,8 +470,8 @@ void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C,
/******************************************************************************/
size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C,
const char *buf,
size_t count)
const char *buf,
size_t count)
{
size_t ret = 0;
if (SDO_C != NULL && buf != NULL) {
@ -387,16 +485,17 @@ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C,
CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C,
uint32_t timeDifference_us,
bool_t abort,
bool_t bufferPartial,
CO_SDO_abortCode_t *SDOabortCode,
size_t *sizeTransferred,
uint32_t *timerNext_us)
{
(void)timerNext_us; /* may be unused */
(void)timerNext_us; (void) bufferPartial; /* may be unused */
CO_SDO_return_t ret = CO_SDO_RT_waitingResponse;
CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE;
if (SDO_C == NULL) {
if (SDO_C == NULL || !SDO_C->valid) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_wrongArguments;
}
@ -411,37 +510,96 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C,
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL
/* Transfer data locally **************************************************/
else if (SDO_C->state == CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER) {
if (SDO_C->SDO->state != CO_SDO_ST_IDLE) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
}
else {
/* Max data size is limited to fifo size (for now). */
size_t count = CO_fifo_getOccupied(&SDO_C->bufFifo);
/* search object dictionary in first pass */
if (SDO_C->OD_IO.write == NULL) {
ODR_t odRet;
OD_subEntry_t subEntry;
if (SDO_C->sizeInd > 0 && SDO_C->sizeInd != count) {
abortCode = CO_SDO_AB_TYPE_MISMATCH;
odRet = OD_getSub(OD_find(SDO_C->OD, SDO_C->index), SDO_C->subIndex,
&subEntry, &SDO_C->OD_IO.stream, false);
if (odRet != ODR_OK) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithClientAbort;
}
else if ((subEntry.attribute & ODA_SDO_W) == 0) {
abortCode = CO_SDO_AB_READONLY;
ret = CO_SDO_RT_endedWithClientAbort;
}
else if (subEntry.write == NULL) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
}
else {
/* init ODF_arg */
abortCode = CO_SDO_initTransfer(SDO_C->SDO, SDO_C->index,
SDO_C->subIndex);
if (abortCode == CO_SDO_AB_NONE) {
/* set buffer and write data to the Object dictionary */
SDO_C->SDO->ODF_arg.data = (uint8_t *)SDO_C->buf;
abortCode = CO_SDO_writeOD(SDO_C->SDO, count);
SDO_C->OD_IO.write = subEntry.write;
}
}
/* write data, in several passes if necessary */
if (SDO_C->OD_IO.write != NULL) {
size_t count = CO_fifo_getOccupied(&SDO_C->bufFifo);
char buf[count];
CO_fifo_read(&SDO_C->bufFifo, buf, count, NULL);
SDO_C->sizeTran += count;
/* error: no data */
if (count == 0) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* verify if sizeTran is too large */
else if (SDO_C->sizeInd > 0 && SDO_C->sizeTran > SDO_C->sizeInd) {
SDO_C->sizeTran -= count;
abortCode = CO_SDO_AB_DATA_LONG;
ret = CO_SDO_RT_endedWithClientAbort;
}
else {
ODR_t odRet;
/* write data to Object Dictionary */
SDO_C->OD_IO.write(&SDO_C->OD_IO.stream, SDO_C->subIndex,
buf, count, &odRet);
/* verify for errors in write */
if (odRet != ODR_OK && odRet != ODR_PARTIAL) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithServerAbort;
}
if (abortCode == CO_SDO_AB_NONE) {
SDO_C->sizeTran = count;
ret = CO_SDO_RT_ok_communicationEnd;
/* error if OD variable was written completelly,
* but SDO download still has data */
else if (bufferPartial && odRet == ODR_OK) {
abortCode = CO_SDO_AB_DATA_LONG;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* is end of transfer? */
else if (!bufferPartial) {
/* error if OD variable was not written completely, but SDO
* download finished */
if (odRet == ODR_PARTIAL) {
abortCode = CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* Verify size transferred */
else if (SDO_C->sizeInd > 0
&& SDO_C->sizeTran < SDO_C->sizeInd
) {
abortCode = CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* data transfer finished successfully */
else {
ret = CO_SDO_RT_ok_communicationEnd;
}
}
else {
ret = CO_SDO_RT_endedWithServerAbort;
ret = CO_SDO_RT_waitingLocalTransfer;
}
}
}
SDO_C->state = CO_SDO_ST_IDLE;
if (ret != CO_SDO_RT_waitingLocalTransfer) {
SDO_C->state = CO_SDO_ST_IDLE;
}
}
#endif
/* CAN data received ******************************************************/
@ -720,7 +878,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C,
SDO_C->CANtxBuff->data[0] = SDO_C->toggle | ((7 - count) << 1);
/* is end of transfer? Verify also sizeTran */
if (CO_fifo_getOccupied(&SDO_C->bufFifo) == 0) {
if (CO_fifo_getOccupied(&SDO_C->bufFifo) == 0 && !bufferPartial) {
if (SDO_C->sizeInd > 0 && SDO_C->sizeTran < SDO_C->sizeInd) {
abortCode = CO_SDO_AB_DATA_SHORT;
SDO_C->state = CO_SDO_ST_ABORT;
@ -777,7 +935,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C,
}
/* is end of transfer? Verify also sizeTran */
if (CO_fifo_altGetOccupied(&SDO_C->bufFifo) == 0) {
if (CO_fifo_altGetOccupied(&SDO_C->bufFifo) == 0 && bufferPartial) {
if (SDO_C->sizeInd > 0 && SDO_C->sizeTran < SDO_C->sizeInd) {
abortCode = CO_SDO_AB_DATA_SHORT;
SDO_C->state = CO_SDO_ST_ABORT;
@ -866,7 +1024,7 @@ CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C,
bool_t blockEnable)
{
/* verify parameters */
if (SDO_C == NULL) {
if (SDO_C == NULL || !SDO_C->valid) {
return CO_SDO_RT_wrongArguments;
}
@ -886,9 +1044,10 @@ CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C,
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL
/* if node-ID of the SDO server is the same as node-ID of this node, then
* transfer data within this node */
if (SDO_C->SDO != NULL
&& SDO_C->SDOClientPar->nodeIDOfTheSDOServer == SDO_C->SDO->nodeId
if (SDO_C->OD != NULL && SDO_C->nodeId != 0
&& SDO_C->nodeIDOfTheSDOServer == SDO_C->nodeId
) {
SDO_C->OD_IO.read = NULL;
SDO_C->state = CO_SDO_ST_UPLOAD_LOCAL_TRANSFER;
}
else
@ -922,7 +1081,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C,
CO_SDO_return_t ret = CO_SDO_RT_waitingResponse;
CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE;
if (SDO_C == NULL) {
if (SDO_C == NULL || !SDO_C->valid) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_wrongArguments;
}
@ -932,44 +1091,88 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C,
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL
/* Transfer data locally **************************************************/
else if (SDO_C->state == CO_SDO_ST_UPLOAD_LOCAL_TRANSFER) {
if (SDO_C->SDO->state != 0) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
}
else {
/* Max data size is limited to fifo size (for now). */
size_t count = CO_fifo_getSpace(&SDO_C->bufFifo);
/* search object dictionary in first pass */
if (SDO_C->OD_IO.read == NULL) {
ODR_t odRet;
OD_subEntry_t subEntry;
/* init ODF_arg */
abortCode = CO_SDO_initTransfer(SDO_C->SDO, SDO_C->index,
SDO_C->subIndex);
if (abortCode == CO_SDO_AB_NONE) {
/* set buffer and read data from the Object dictionary */
SDO_C->SDO->ODF_arg.data = (uint8_t *)SDO_C->buf;
if (SDO_C->SDO->ODF_arg.ODdataStorage == 0) {
/* set length if domain */
SDO_C->SDO->ODF_arg.dataLength = count;
}
abortCode = CO_SDO_readOD(SDO_C->SDO, count);
odRet = OD_getSub(OD_find(SDO_C->OD, SDO_C->index), SDO_C->subIndex,
&subEntry, &SDO_C->OD_IO.stream, false);
if (odRet != ODR_OK) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithClientAbort;
}
if (abortCode == CO_SDO_AB_NONE) {
/* is SDO buffer too small */
if (SDO_C->SDO->ODF_arg.lastSegment == 0) {
abortCode = CO_SDO_AB_OUT_OF_MEM; /* Out of memory */
ret = CO_SDO_RT_endedWithServerAbort;
}
else {
SDO_C->sizeTran = (size_t)SDO_C->SDO->ODF_arg.dataLength;
/* fifo was written directly, indicate data size manually */
SDO_C->bufFifo.writePtr = SDO_C->sizeTran;
ret = CO_SDO_RT_ok_communicationEnd;
}
else if ((subEntry.attribute & ODA_SDO_R) == 0) {
abortCode = CO_SDO_AB_WRITEONLY;
ret = CO_SDO_RT_endedWithClientAbort;
}
else if (subEntry.read == NULL) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
}
else {
ret = CO_SDO_RT_endedWithServerAbort;
SDO_C->OD_IO.read = subEntry.read;
}
}
SDO_C->state = CO_SDO_ST_IDLE;
size_t countFifo = CO_fifo_getSpace(&SDO_C->bufFifo);
/* skip copying if buffer full */
if (countFifo == 0) {
ret = CO_SDO_RT_uploadDataBufferFull;
}
/* read data, in several passes if necessary */
else if (SDO_C->OD_IO.read != NULL) {
/* Get size of data in Object Dictionary. If size is not indicated
* use maximum SDO client buffer size. Prepare temp buffer. */
OD_size_t countData = SDO_C->OD_IO.stream.dataLength;
OD_size_t countBuf = (countData > 0 && countData <= countFifo)
? countData : countFifo;
char buf[countBuf];
ODR_t odRet;
/* load data from OD variable into the buffer */
OD_size_t countRd = SDO_C->OD_IO.read(&SDO_C->OD_IO.stream,
SDO_C->subIndex,
buf, countBuf, &odRet);
if (odRet != ODR_OK && odRet != ODR_PARTIAL) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithServerAbort;
}
else {
CO_fifo_write(&SDO_C->bufFifo, buf, countRd, NULL);
SDO_C->sizeTran += countRd;
/* verify if size of data uploaded is too large */
SDO_C->sizeInd = SDO_C->OD_IO.stream.dataLength;
if (SDO_C->sizeInd > 0 && SDO_C->sizeTran > SDO_C->sizeInd) {
abortCode = CO_SDO_AB_DATA_LONG;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* If no more segments to be upload, finish */
else if (odRet == ODR_OK) {
/* verify size of data uploaded */
if (SDO_C->sizeInd > 0 && SDO_C->sizeTran < SDO_C->sizeInd){
abortCode = CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
}
else {
ret = CO_SDO_RT_ok_communicationEnd;
}
}
else {
ret = CO_SDO_RT_waitingLocalTransfer;
}
}
}
if (ret != CO_SDO_RT_uploadDataBufferFull
&& ret != CO_SDO_RT_waitingLocalTransfer
) {
SDO_C->state = CO_SDO_ST_IDLE;
}
}
#endif
/* CAN data received ******************************************************/
@ -1289,7 +1492,6 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C,
}
/* Transmit CAN data ******************************************************/
if (ret == CO_SDO_RT_waitingResponse) {
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK
size_t count;

View file

@ -28,6 +28,7 @@
#define CO_SDO_CLIENT_H
#include "301/CO_driver.h"
#include "301/CO_ODinterface.h"
#include "301/CO_SDOserver.h"
#include "301/CO_fifo.h"
@ -56,40 +57,18 @@ extern "C" {
*/
/**
* SDO Client Parameter. The same as record from Object dictionary
* (index 0x1280+).
*/
typedef struct {
/** Equal to 3 */
uint8_t maxSubIndex;
/** Communication object identifier for client transmission.
* Meaning of the specific bits:
- Bit 0...10: 11-bit CAN identifier.
- Bit 11..30: reserved, set to 0.
- Bit 31: if 1, SDO client object is not used. */
uint32_t COB_IDClientToServer;
/** Communication object identifier for message received from server.
* Meaning of the specific bits:
- Bit 0...10: 11-bit CAN identifier.
- Bit 11..30: reserved, set to 0.
- Bit 31: if 1, SDO client object is not used. */
uint32_t COB_IDServerToClient;
/** Node-ID of the SDO server */
uint8_t nodeIDOfTheSDOServer;
} CO_SDOclientPar_t;
/**
* SDO client object
*/
typedef struct {
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL) || defined CO_DOXYGEN
/** From CO_SDOclient_init() */
CO_SDO_t *SDO;
#endif
const OD_t *OD;
/** From CO_SDOclient_init() */
CO_SDOclientPar_t *SDOClientPar;
uint8_t nodeId;
/** Object dictionary interface for locally transferred object */
OD_IO_t OD_IO;
#endif
/** From CO_SDOclient_init() */
CO_CANmodule_t *CANdevRx;
/** From CO_SDOclient_init() */
@ -100,6 +79,19 @@ typedef struct {
uint16_t CANdevTxIdx;
/** CAN transmit buffer inside CANdevTx for CAN tx message */
CO_CANtx_t *CANtxBuff;
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC) || defined CO_DOXYGEN
/** Copy of CANopen COB_ID Client -> Server, meaning of the specific bits:
- Bit 0...10: 11-bit CAN identifier.
- Bit 11..30: reserved, must be 0.
- Bit 31: if 1, SDO client object is not used. */
uint32_t COB_IDClientToServer;
/** Copy of CANopen COB_ID Server -> Client, similar as above */
uint32_t COB_IDServerToClient;
#endif
/** Node-ID of the SDO server */
uint8_t nodeIDOfTheSDOServer;
/* If true, SDO channel is valid */
bool_t valid;
/** Index of current object in Object Dictionary */
uint16_t index;
/** Subindex of current object in Object Dictionary */
@ -127,10 +119,6 @@ typedef struct {
volatile void *CANrxNew;
/** 8 data bytes of the received message */
uint8_t CANrxData[8];
/** Previous value of the COB_IDClientToServer */
uint32_t COB_IDClientToServerPrev;
/** Previous value of the COB_IDServerToClient */
uint32_t COB_IDServerToClientPrev;
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
/** From CO_SDOclient_initCallbackPre() or NULL */
void (*pFunctSignal)(void *object);
@ -168,10 +156,13 @@ typedef struct {
* Function must be called in the communication reset section.
*
* @param SDO_C This object will be initialized.
* @param SDO SDO server object. It is used in case, if client is accessing
* @param OD Object Dictionary. It is used in case, if client is accessing
* object dictionary from its own device. If NULL, it will be ignored.
* @param SDOClientPar Pointer to _SDO Client Parameter_ record from Object
* dictionary (index 0x1280+). Will be written.
* @param OD_1280_SDOcliPar OD entry for SDO client parameter (0x1280+). It
* may have IO extension enabled to allow dynamic configuration (see also
* @ref CO_CONFIG_FLAG_OD_DYNAMIC). Entry is required.
* @param nodeId CANopen Node ID of this device. It is used in case, if client
* is accessing object dictionary from its own device. If 0, it will be ignored.
* @param CANdevRx CAN device for SDO client reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANdevTx CAN device for SDO client transmission.
@ -180,8 +171,9 @@ typedef struct {
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_SDOclient_init(CO_SDOclient_t *SDO_C,
void *SDO,
CO_SDOclientPar_t *SDOClientPar,
const OD_t *OD,
const OD_entry_t *OD_1280_SDOcliPar,
uint8_t nodeId,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
CO_CANmodule_t *CANdevTx,
@ -212,23 +204,20 @@ void CO_SDOclient_initCallbackPre(CO_SDOclient_t *SDOclient,
/**
* Setup SDO client object.
*
* Function must be called before new SDO communication. If previous SDO
* communication was with the same node, function does not need to be called.
*
* @remark If configuring SDO client from network is required, this function
* should be set as callback for the corresponding SDO client parameter OD
* entry.
* Function is called in from CO_SDOclient_init() and each time when
* "SDO client parameter" is written. Application can call this function before
* new SDO communication. If parameters to this function are the same as before,
* then CAN is not reconfigured.
*
* @param SDO_C This object.
* @param COB_IDClientToServer See CO_SDOclientPar_t. If zero, then
* nodeIDOfTheSDOServer is used with default COB-ID.
* @param COB_IDServerToClient See CO_SDOclientPar_t. If zero, then
* nodeIDOfTheSDOServer is used with default COB-ID.
* @param nodeIDOfTheSDOServer Node-ID of the SDO server. If zero, SDO client
* object is not used. If it is the same as node-ID of this node, then data will
* be exchanged with this node (without CAN communication).
* @param COB_IDClientToServer See @ref CO_SDOclient_t.
* @param COB_IDServerToClient See @ref CO_SDOclient_t.
* @param nodeIDOfTheSDOServer Node-ID of the SDO server. If it is the same as
* node-ID of this node, then data will be exchanged with this node
* (without CAN communication).
*
* @return #CO_SDO_return_t
* @return #CO_SDO_return_t, CO_SDO_RT_ok_communicationEnd or
* CO_SDO_RT_wrongArguments
*/
CO_SDO_return_t CO_SDOclient_setup(CO_SDOclient_t *SDO_C,
uint32_t COB_IDClientToServer,
@ -303,8 +292,8 @@ void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C,
* @return number of bytes actually written.
*/
size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C,
const char *buf,
size_t count);
const char *buf,
size_t count);
/**
@ -323,6 +312,8 @@ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C,
* [microseconds].
* @param abort If true, SDO client will send abort message from SDOabortCode
* and transmission will be aborted.
* @param bufferPartial True indicates, not all data were copied to internal
* buffer yet. Buffer will be refilled later with #CO_SDOclientDownloadBufWrite.
* @param [out] SDOabortCode In case of error in communication, SDO abort code
* contains reason of error. Ignored if NULL.
* @param [out] sizeTransferred Actual size of data transferred. Ignored if NULL
@ -334,11 +325,12 @@ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C,
* communication is in progress.
*/
CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C,
uint32_t timeDifference_us,
bool_t abort,
CO_SDO_abortCode_t *SDOabortCode,
size_t *sizeTransferred,
uint32_t *timerNext_us);
uint32_t timeDifference_us,
bool_t abort,
bool_t bufferPartial,
CO_SDO_abortCode_t *SDOabortCode,
size_t *sizeTransferred,
uint32_t *timerNext_us);
/**

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -864,7 +864,10 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
}
/* setup client */
SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, 0, 0, gtwa->node);
SDO_ret = CO_SDOclient_setup(gtwa->SDO_C,
CO_CAN_ID_SDO_CLI + gtwa->node,
CO_CAN_ID_SDO_SRV + gtwa->node,
gtwa->node);
if (SDO_ret != CO_SDO_RT_ok_communicationEnd) {
respErrorCode = CO_GTWA_respErrorInternalState;
err = true;
@ -924,7 +927,10 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
if (err) break;
/* setup client */
SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, 0, 0, gtwa->node);
SDO_ret = CO_SDOclient_setup(gtwa->SDO_C,
CO_CAN_ID_SDO_CLI + gtwa->node,
CO_CAN_ID_SDO_SRV + gtwa->node,
gtwa->node);
if (SDO_ret != CO_SDO_RT_ok_communicationEnd) {
respErrorCode = CO_GTWA_respErrorInternalState;
err = true;
@ -1617,6 +1623,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
ret = CO_SDOclientDownload(gtwa->SDO_C,
timeDifference_us,
abort,
gtwa->SDOdataCopyStatus,
&abortCode,
&sizeTransferred,
timerNext_us);

View file

@ -29,7 +29,6 @@
#include "301/CO_driver.h"
#include "301/CO_fifo.h"
#include "301/CO_SDOserver.h"
#include "301/CO_SDOclient.h"
#include "301/CO_NMT_Heartbeat.h"
#include "305/CO_LSSmaster.h"

2078
CANopen.c

File diff suppressed because it is too large Load diff

476
CANopen.h
View file

@ -29,14 +29,15 @@
#define CANopen_H
#include "301/CO_driver.h"
#include "301/CO_ODinterface.h"
#include "301/CO_NMT_Heartbeat.h"
#include "301/CO_HBconsumer.h"
#include "301/CO_Emergency.h"
#include "301/CO_SDOserver.h"
#include "301/CO_SDOclient.h"
#include "301/CO_Emergency.h"
#include "301/CO_NMT_Heartbeat.h"
#include "301/CO_TIME.h"
#include "301/CO_SYNC.h"
#include "301/CO_PDO.h"
#include "301/CO_HBconsumer.h"
#include "301/CO_TIME.h"
#include "303/CO_LEDs.h"
#include "304/CO_GFC.h"
#include "304/CO_SRDO.h"
@ -44,7 +45,6 @@
#include "305/CO_LSSmaster.h"
#include "309/CO_gateway_ascii.h"
#include "extra/CO_trace.h"
#include "CO_OD.h"
#ifdef __cplusplus
extern "C" {
@ -63,12 +63,12 @@ extern "C" {
*
* CANopenNode homepage is https://github.com/CANopenNode/CANopenNode
*
* CANopen.h file combines Object dictionary (CO_OD) and all other CANopen
* source files. Configuration information are read from CO_OD.h file.
* CO_OD.h/.c files defines CANopen Object Dictionary and are generated by
* external tool.
* CANopen.h file contains most common configuration of CANopenNode objects
* and can also be a template for custom, more complex configurations.
* CANopen.h file combines all CANopenNode source files. @ref CO_STACK_CONFIG
* is first defined in "CO_config.h" file. Number of different CANopenNode
* objects used is configured with @ref CO_config_t structure or is read
* directly from "OD.h" file, if single object dictionary definition is used.
* "OD.h" and "OD.c" files defines CANopen Object Dictionary and are generated
* by external tool.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -112,8 +112,8 @@ extern "C" {
* CANopen Safety (EN 50325­-5:2010 (formerly CiA 304))
*
* Standard defines the usage of Safety Related Data Objects (SRDO) and the GFC.
* This is an additional protocol (to SDO, PDO) to exchange data.
* The meaning of "security" here refers not to security (crypto) but to data consistency.
* This is an additional protocol (to SDO, PDO) to exchange data. The meaning of
* "security" here refers not to security (crypto) but to data consistency.
* @}
*/
@ -159,178 +159,274 @@ extern "C" {
* @{
*/
#ifdef CO_DOXYGEN
/**
* @defgroup CO_NO_OBJ CANopen configuration
*
* Definitions specify, which and how many CANopenNode communication objects
* will be used in current configuration. Usage of some objects is mandatory and
* is fixed. Others are defined in CO_OD.h.
* @{
* If macro is defined externally, then configuration with multiple object
* dictionaries will be possible. If macro is not defined, default "OD.h" file
* with necessary definitions, such as CO_CNT_xxx, will be used, and also memory
* consumption and startup time will be lower.
*/
/* Definitions valid only for documentation. */
/** Number of NMT objects, fixed to 1 slave(CANrx) */
#define CO_NO_NMT (1)
/** Number of NMT master objects, 0 or 1 master(CANtx). It depends on
* @ref CO_CONFIG_NMT setting. */
#define CO_NO_NMT_MST (0 - 1)
/** Number of SYNC objects, 0 or 1 (consumer(CANrx) + producer(CANtx)) */
#define CO_NO_SYNC (0 - 1)
/** Number of Emergency producer objects, fixed to 1 producer(CANtx) */
#define CO_NO_EMERGENCY (1)
/** Number of Emergency consumer objects, 0 or 1 consumer(CANrx). It depends on
* @ref CO_CONFIG_EM setting. */
#define CO_NO_EM_CONS (0 - 1)
/** Number of Time-stamp objects, 0 or 1 (consumer(CANrx) + producer(CANtx)) */
#define CO_NO_TIME (0 - 1)
/** Number of GFC objects, 0 or 1 (consumer(CANrx) + producer(CANtx)) */
#define CO_NO_GFC (0 - 1)
/** Number of SRDO objects, 0 to 64 (consumer(CANrx) + producer(CANtx)) */
#define CO_NO_RPDO (0 - 64)
/** Number of RPDO objects, 1 to 512 consumers (CANrx) */
#define CO_NO_RPDO (1 - 512)
/** Number of TPDO objects, 1 to 512 producers (CANtx) */
#define CO_NO_TPDO (1 - 512)
/** Number of SDO server objects, from 1 to 128 (CANrx + CANtx) */
#define CO_NO_SDO_SERVER (1 - 128)
/** Number of SDO client objects, from 0 to 128 (CANrx + CANtx) */
#define CO_NO_SDO_CLIENT (0 - 128)
/** Number of HB producer objects, fixed to 1 producer(CANtx) */
#define CO_NO_HB_PROD (1)
/** Number of HB consumer objects, from 0 to 127 consumers(CANrx) */
#define CO_NO_HB_CONS (0 - 127)
/** Number of LSS slave objects, 0 or 1 (CANrx + CANtx). It depends on
* @ref CO_CONFIG_LSS setting. */
#define CO_NO_LSS_SLAVE (0 - 1)
/** Number of LSS master objects, 0 or 1 (CANrx + CANtx). It depends on
* @ref CO_CONFIG_LSS setting. */
#define CO_NO_LSS_MASTER (0 - 1)
/** Number of Trace objects, 0 to many */
#define CO_NO_TRACE (0 - )
/** @} */
#else /* CO_DOXYGEN */
/* Valid Definitions for program. */
/* NMT slave count (fixed) */
#define CO_NO_NMT 1
/* NMT master count depends on stack configuration */
#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
#define CO_NO_NMT_MST 1
#else
#define CO_NO_NMT_MST 0
#ifdef CO_DOXYGEN
#define CO_MULTIPLE_OD
#endif
/* Emergency consumer depends on stack configuration */
#if (CO_CONFIG_EM) & CO_CONFIG_EM_CONSUMER
#define CO_NO_EM_CONS 1
#else
#define CO_NO_EM_CONS 0
#endif
/* Heartbeat producer count (fixed) */
#define CO_NO_HB_PROD 1
/* Heartbeat consumer count depends on Object Dictionary configuration */
#ifdef ODL_consumerHeartbeatTime_arrayLength
#define CO_NO_HB_CONS ODL_consumerHeartbeatTime_arrayLength
#else
#define CO_NO_HB_CONS 0
#endif
/* LSS slave count depends on stack configuration */
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE
#define CO_NO_LSS_SLAVE 1
#else
#define CO_NO_LSS_SLAVE 0
#endif
/* LSS master count depends on stack configuration */
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER
#define CO_NO_LSS_MASTER 1
#else
#define CO_NO_LSS_MASTER 0
#endif
#endif /* CO_DOXYGEN */
/**
* CANopen object with pointers to all CANopenNode objects.
* If macro is defined externally, then global variables for CANopen objects
* will be used instead of heap. This is possible only if CO_MULTIPLE_OD is not
* defined.
*/
#ifdef CO_DOXYGEN
#define CO_USE_GLOBALS
#endif
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
/**
* CANopen configuration, used with @ref CO_new()
*
* This structure is used only, if @ref CO_MULTIPLE_OD is enabled. Otherwise
* parameters are retrieved from default "OD.h" file.
*/
typedef struct {
bool_t nodeIdUnconfigured; /**< True in unconfigured LSS slave */
CO_CANmodule_t *CANmodule[1]; /**< CAN module objects */
CO_SDO_t *SDO[CO_NO_SDO_SERVER]; /**< SDO object */
CO_EM_t *em; /**< Emergency report object */
CO_EMpr_t *emPr; /**< Emergency process object */
CO_NMT_t *NMT; /**< NMT object */
#if CO_NO_SYNC == 1 || defined CO_DOXYGEN
CO_SYNC_t *SYNC; /**< SYNC object */
/** Number of NMT objects, 0 or 1: NMT slave (CANrx) + Heartbeat producer
* (CANtx) + optional NMT master (CANtx), configurable by
* @ref CO_CONFIG_NMT. Start indexes inside CANrx and CANtx are always 0.
* There must be one NMT object in the device. */
uint8_t CNT_NMT;
const OD_entry_t *ENTRY_H1017; /**< OD entry for @ref CO_NMT_init() */
/** Number of Heartbeat consumer objects, 0 or 1: object uses from 1 to 127
* internal consumers (CANrx), as specified by @ref CO_CONFIG_HB_CONS_SIZE.
*/
uint8_t CNT_HB_CONS;
const OD_entry_t *ENTRY_H1016; /**< OD entry for @ref CO_HBconsumer_init()*/
/** Number of Emergency objects, 0 or 1: optional producer (CANtx) +
* optional consumer (CANrx), configurable by @ref CO_CONFIG_EM.
* There must be one Emergency object in the device. */
uint8_t CNT_EM;
const OD_entry_t *ENTRY_H1001; /**< OD entry for @ref CO_EM_init() */
const OD_entry_t *ENTRY_H1014; /**< OD entry for @ref CO_EM_init() */
const OD_entry_t *ENTRY_H1015; /**< OD entry for @ref CO_EM_init() */
const OD_entry_t *ENTRY_H1003; /**< OD entry for @ref CO_EM_init() */
/** Number of SDO server objects, from 0 to 128 (CANrx + CANtx). There must
* be at least one SDO server object in the device. */
uint8_t CNT_SDO_SRV;
const OD_entry_t *ENTRY_H1200; /**< OD entry for @ref CO_SDOserver_init()*/
/** Number of SDO client objects, from 0 to 128 (CANrx + CANtx). */
uint8_t CNT_SDO_CLI;
const OD_entry_t *ENTRY_H1280; /**< OD entry for @ref CO_SDOclient_init()*/
/** Number of TIME objects, 0 or 1: consumer (CANrx) + optional producer
* (CANtx), configurable by @ref CO_CONFIG_TIME. */
uint8_t CNT_TIME;
const OD_entry_t *ENTRY_H1012; /**< OD entry for @ref CO_TIME_init() */
/** Number of SYNC objects, 0 or 1: consumer (CANrx) + optional producer
* (CANtx), configurable by @ref CO_CONFIG_SYNC. */
uint8_t CNT_SYNC;
const OD_entry_t *ENTRY_H1005; /**< OD entry for @ref CO_SYNC_init() */
const OD_entry_t *ENTRY_H1006; /**< OD entry for @ref CO_SYNC_init() */
const OD_entry_t *ENTRY_H1007; /**< OD entry for @ref CO_SYNC_init() */
const OD_entry_t *ENTRY_H1019; /**< OD entry for @ref CO_SYNC_init() */
/** Number of RPDO objects, from 0 to 512 consumers (CANrx) */
uint16_t CNT_RPDO;
const OD_entry_t *ENTRY_H1400; /**< OD entry for @ref CO_RPDO_init() */
const OD_entry_t *ENTRY_H1600; /**< OD entry for @ref CO_RPDO_init() */
/** Number of TPDO objects, from 0 to 512 producers (CANtx) */
uint16_t CNT_TPDO;
const OD_entry_t *ENTRY_H1800; /**< OD entry for @ref CO_TPDO_init() */
const OD_entry_t *ENTRY_H1A00; /**< OD entry for @ref CO_TPDO_init() */
/** Number of LEDs objects, 0 or 1. */
uint8_t CNT_LEDS;
/** Number of GFC objects, 0 or 1 (CANrx + CANtx). */
uint8_t CNT_GFC;
const OD_entry_t *ENTRY_H1300; /**< OD entry for @ref CO_GFC_init() */
/** Number of SRDO objects, from 0 to 64 (2*CANrx + 2*CANtx). */
uint8_t CNT_SRDO;
const OD_entry_t *ENTRY_H1301; /**< OD entry for @ref CO_SRDO_init() */
const OD_entry_t *ENTRY_H1381; /**< OD entry for @ref CO_SRDO_init() */
const OD_entry_t *ENTRY_H13FE; /**< OD entry for @ref CO_SRDOGuard_init() */
const OD_entry_t *ENTRY_H13FF; /**< OD entry for @ref CO_SRDOGuard_init() */
/** Number of LSSslave objects, 0 or 1 (CANrx + CANtx). */
uint8_t CNT_LSS_SLV;
/** Number of LSSmaster objects, 0 or 1 (CANrx + CANtx). */
uint8_t CNT_LSS_MST;
/** Number of gateway ascii objects, 0 or 1. */
uint8_t CNT_GTWA;
/** Number of trace objects, 0 or more. */
uint16_t CNT_TRACE;
} CO_config_t;
#else
typedef void CO_config_t;
#endif /* CO_MULTIPLE_OD */
/**
* CANopen object - collection of all CANopenNode objects
*/
typedef struct {
bool_t nodeIdUnconfigured; /**< True in un-configured LSS slave */
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
CO_config_t *config; /**< Remember the configuration parameters */
#endif
/** One CAN module object, initialised by @ref CO_CANmodule_init() */
CO_CANmodule_t *CANmodule;
CO_CANrx_t *CANrx; /**< CAN receive message objects */
CO_CANtx_t *CANtx; /**< CAN transmit message objects */
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t CNT_ALL_RX_MSGS; /**< Number of all CAN receive message objects. */
uint16_t CNT_ALL_TX_MSGS; /**< Number of all CAN transmit message objects.*/
#endif
/** NMT and heartbeat object, initialised by @ref CO_NMT_init() */
CO_NMT_t *NMT;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_NMT_SLV; /**< Start index in CANrx. */
uint16_t TX_IDX_NMT_MST; /**< Start index in CANtx. */
uint16_t TX_IDX_HB_PROD; /**< Start index in CANtx. */
#endif
#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE) || defined CO_DOXYGEN
/** Heartbeat consumer object, initialised by @ref CO_HBconsumer_init() */
CO_HBconsumer_t *HBcons;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_HB_CONS; /**< Start index in CANrx. */
#endif
#endif
#if CO_NO_TIME == 1 || defined CO_DOXYGEN
CO_TIME_t *TIME; /**< TIME object */
/** Emergency object, initialised by @ref CO_EM_init() */
CO_EM_t *em;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_EM_CONS; /**< Start index in CANrx. */
uint16_t TX_IDX_EM_PROD; /**< Start index in CANtx. */
#endif
/** SDO server objects, initialised by @ref CO_SDOserver_init() */
CO_SDOserver_t *SDOserver;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_SDO_SRV; /**< Start index in CANrx. */
uint16_t TX_IDX_SDO_SRV; /**< Start index in CANtx. */
#endif
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_ENABLE) || defined CO_DOXYGEN
/** SDO client objects, initialised by @ref CO_SDOclient_init() */
CO_SDOclient_t *SDOclient;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_SDO_CLI; /**< Start index in CANrx. */
uint16_t TX_IDX_SDO_CLI; /**< Start index in CANtx. */
#endif
#endif
CO_RPDO_t *RPDO[CO_NO_RPDO]; /**< RPDO objects */
CO_TPDO_t *TPDO[CO_NO_TPDO]; /**< TPDO objects */
CO_HBconsumer_t *HBcons; /**< Heartbeat consumer object*/
#if CO_NO_SDO_CLIENT != 0 || defined CO_DOXYGEN
CO_SDOclient_t *SDOclient[CO_NO_SDO_CLIENT]; /**< SDO client object */
#if ((CO_CONFIG_TIME) & CO_CONFIG_TIME_ENABLE) || defined CO_DOXYGEN
/** TIME object, initialised by @ref CO_TIME_init() */
CO_TIME_t *TIME;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_TIME; /**< Start index in CANrx. */
uint16_t TX_IDX_TIME; /**< Start index in CANtx. */
#endif
#endif
#if ((CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE) || defined CO_DOXYGEN
/** SYNC object, initialised by @ref CO_SYNC_init() */
CO_SYNC_t *SYNC;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_SYNC; /**< Start index in CANrx. */
uint16_t TX_IDX_SYNC; /**< Start index in CANtx. */
#endif
#endif
#if ((CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE) || defined CO_DOXYGEN
/** RPDO objects, initialised by @ref CO_RPDO_init() */
CO_RPDO_t *RPDO;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_RPDO; /**< Start index in CANrx. */
#endif
#endif
#if ((CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE) || defined CO_DOXYGEN
/** TPDO objects, initialised by @ref CO_TPDO_init() */
CO_TPDO_t *TPDO;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t TX_IDX_TPDO; /**< Start index in CANtx. */
#endif
#endif
#if ((CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE) || defined CO_DOXYGEN
CO_LEDs_t *LEDs; /**< LEDs object */
/** LEDs object, initialised by @ref CO_LEDs_init() */
CO_LEDs_t *LEDs;
#endif
#if CO_NO_GFC != 0 || defined CO_DOXYGEN
CO_GFC_t *GFC; /**< GFC objects */
#if ((CO_CONFIG_GFC) & CO_CONFIG_GFC_ENABLE) || defined CO_DOXYGEN
/** GFC object, initialised by @ref CO_GFC_init() */
CO_GFC_t *GFC;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_GFC; /**< Start index in CANrx. */
uint16_t TX_IDX_GFC; /**< Start index in CANtx. */
#endif
#endif
#if CO_NO_SRDO != 0 || defined CO_DOXYGEN
CO_SRDOGuard_t *SRDOGuard; /**< SRDO objects */
CO_SRDO_t *SRDO[CO_NO_SRDO]; /**< SRDO objects */
#if ((CO_CONFIG_SRDO) & CO_CONFIG_SRDO_ENABLE) || defined CO_DOXYGEN
/** SRDO object, initialised by @ref CO_SRDOGuard_init(), single SRDOGuard
* object is included inside all SRDO objects */
CO_SRDOGuard_t *SRDOGuard;
/** SRDO objects, initialised by @ref CO_SRDO_init() */
CO_SRDO_t *SRDO;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_SRDO; /**< Start index in CANrx. */
uint16_t TX_IDX_SRDO; /**< Start index in CANtx. */
#endif
#endif
#if CO_NO_LSS_SLAVE == 1 || defined CO_DOXYGEN
CO_LSSslave_t *LSSslave; /**< LSS slave object */
#if ((CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE) || defined CO_DOXYGEN
/** LSS slave object, initialised by @ref CO_LSSslave_init(). */
CO_LSSslave_t *LSSslave;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_LSS_SLV; /**< Start index in CANrx. */
uint16_t TX_IDX_LSS_SLV; /**< Start index in CANtx. */
#endif
#endif
#if CO_NO_LSS_MASTER == 1 || defined CO_DOXYGEN
CO_LSSmaster_t *LSSmaster; /**< LSS master object */
#if ((CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER) || defined CO_DOXYGEN
/** LSS master object, initialised by @ref CO_LSSmaster_init(). */
CO_LSSmaster_t *LSSmaster;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
uint16_t RX_IDX_LSS_MST; /**< Start index in CANrx. */
uint16_t TX_IDX_LSS_MST; /**< Start index in CANtx. */
#endif
#endif
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII) || defined CO_DOXYGEN
CO_GTWA_t *gtwa; /**< Gateway-ascii object (CiA309-3) */
/** Gateway-ascii object, initialised by @ref CO_GTWA_init(). */
CO_GTWA_t *gtwa;
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
#endif
#endif
#if CO_NO_TRACE > 0 || defined CO_DOXYGEN
CO_trace_t *trace[CO_NO_TRACE]; /**< Trace object for recording variables */
#if ((CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE) || defined CO_DOXYGEN
/** Trace object, initialised by @ref CO_trace_init(). */
CO_trace_t *trace;
#endif
} CO_t;
/** CANopen object */
extern CO_t *CO;
/**
* Allocate and initialize memory for CANopen object
* Create new CANopen object
*
* Function must be called the first time, after program starts.
* If CO_USE_GLOBALS is defined, then function uses global static variables for
* all the CANopenNode objects. Otherwise it allocates all objects from heap.
*
* @remark
* With some microcontrollers it is necessary to specify Heap size within
* linker configuration.
* linker configuration, if heap is used.
*
* @param config Configuration structure, used if @ref CO_MULTIPLE_OD is
* defined. It must stay in memory permanently. If CO_MULTIPLE_OD is not
* defined, config should be NULL and parameters are retrieved from default
* "OD.h" file.
* @param [out] heapMemoryUsed Information about heap memory used. Ignored if
* NULL.
*
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
* CO_ERROR_OUT_OF_MEMORY
* @return Successfully allocated and configured CO_t object or NULL.
*/
CO_ReturnError_t CO_new(uint32_t *heapMemoryUsed);
CO_t *CO_new(CO_config_t *config, uint32_t *heapMemoryUsed);
/**
* Delete CANopen object and free memory. Must be called at program exit.
*
* @param CANptr Pointer to the user-defined CAN base structure, passed to
* CO_CANmodule_init().
* @param co CANopen object.
*/
void CO_delete(void *CANptr);
void CO_delete(CO_t *co);
/**
* Test if LSS slave is enabled
*
* @param co CANopen object.
*
* @return True if enabled
*/
bool_t CO_isLSSslaveEnabled(CO_t *co);
/**
@ -338,31 +434,35 @@ void CO_delete(void *CANptr);
*
* Function must be called in the communication reset section.
*
* @param co CANopen object.
* @param CANptr Pointer to the user-defined CAN base structure, passed to
* CO_CANmodule_init().
* @param bitRate CAN bit rate.
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
* CO_ERROR_ILLEGAL_BAUDRATE, CO_ERROR_OUT_OF_MEMORY
* @return CO_ERROR_NO in case of success.
*/
CO_ReturnError_t CO_CANinit(void *CANptr,
uint16_t bitRate);
CO_ReturnError_t CO_CANinit(CO_t *co, void *CANptr, uint16_t bitRate);
#if CO_NO_LSS_SLAVE == 1 || defined CO_DOXYGEN
#if ((CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE) || defined CO_DOXYGEN
/**
* Initialize CANopen LSS slave
*
* Function must be called before CO_CANopenInit.
*
* See #CO_LSSslave_init() for description of parameters.
* See @ref CO_LSSslave_init() for description of parameters.
*
* @param [in,out] pendingNodeID Pending node ID or 0xFF(unconfigured)
* @param co CANopen object.
* @param lssAddress LSS slave address, from OD object 0x1018
* @param [in,out] pendingNodeID Pending node ID or 0xFF (unconfigured)
* @param [in,out] pendingBitRate Pending bit rate of the CAN interface
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
*
* @return CO_ERROR_NO in case of success.
*/
CO_ReturnError_t CO_LSSinit(uint8_t *pendingNodeID,
CO_ReturnError_t CO_LSSinit(CO_t *co,
CO_LSS_address_t *lssAddress,
uint8_t *pendingNodeID,
uint16_t *pendingBitRate);
#endif /* CO_NO_LSS_SLAVE == 1 */
#endif
/**
@ -370,13 +470,34 @@ CO_ReturnError_t CO_LSSinit(uint8_t *pendingNodeID,
*
* Function must be called in the communication reset section.
*
* @param co CANopen object.
* @param em Emergency object, which is used inside different CANopen objects,
* usually for error reporting. If NULL, then 'co->em' will be used.
* if NULL and 'co->CNT_EM' is 0, then function returns with error.
* @param NMT If 'co->CNT_NMT' is 0, this object must be specified, If
* 'co->CNT_NMT' is 1,then it is ignored and can be NULL. NMT object is used for
* retrieving NMT internal state inside CO_process().
* @param od CANopen Object dictionary
* @param OD_statusBits Argument passed to @ref CO_EM_init(). May be NULL.
* @param NMTcontrol Argument passed to @ref CO_NMT_init().
* @param firstHBTime_ms Argument passed to @ref CO_NMT_init().
* @param SDOtimeoutTime_ms Argument passed to @ref CO_SDOserver_init().
* @param nodeId CANopen Node ID (1 ... 127) or 0xFF(unconfigured). In the
* CANopen initialization it is the same as pendingBitRate from CO_LSSinit().
* If it is unconfigured, then some CANopen objects will not be initialized nor
* processed.
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
*
* @return CO_ERROR_NO in case of success.
*/
CO_ReturnError_t CO_CANopenInit(uint8_t nodeId);
CO_ReturnError_t CO_CANopenInit(CO_t *co,
CO_NMT_t *NMT,
CO_EM_t *em,
const OD_t *od,
const OD_entry_t *OD_statusBits,
CO_NMT_control_t NMTcontrol,
uint16_t firstHBTime_ms,
uint16_t SDOtimeoutTime_ms,
uint8_t nodeId);
/**
@ -386,6 +507,7 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId);
* objects.
*
* @param co CANopen object.
* @param enableGateway If true, gateway to external world will be enabled.
* @param timeDifference_us Time difference from previous function call in
* microseconds.
* @param [out] timerNext_us info to OS - maximum delay time after this function
@ -398,19 +520,21 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId);
* trigger calling of CO_process() function. Parameter is ignored if
* NULL. See also @ref CO_CONFIG_FLAG_CALLBACK_PRE configuration macro.
*
* @return #CO_NMT_reset_cmd_t from CO_NMT_process().
* @return Node or communication reset request, from @ref CO_NMT_process().
*/
CO_NMT_reset_cmd_t CO_process(CO_t *co,
bool_t enableGateway,
uint32_t timeDifference_us,
uint32_t *timerNext_us);
#if CO_NO_SYNC == 1 || defined CO_DOXYGEN
#if ((CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE) || defined CO_DOXYGEN
/**
* Process CANopen SYNC objects.
*
* Function must be called cyclically from real time thread with constant
* interval (1ms typically). It processes SYNC CANopen objects.
* Function must be called cyclically. For time critical applications it may be
* called from real time thread with constant interval (1ms typically). It
* processes SYNC CANopen objects.
*
* @param co CANopen object.
* @param timeDifference_us Time difference from previous function call in
@ -422,27 +546,32 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
bool_t CO_process_SYNC(CO_t *co,
uint32_t timeDifference_us,
uint32_t *timerNext_us);
#endif /* CO_NO_SYNC == 1 */
#endif
#if (CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE
/**
* Process CANopen RPDO objects.
*
* Function must be called cyclically from real time thread with constant.
* interval (1ms typically). It processes receive PDO CANopen objects.
* Function must be called cyclically. For time critical applications it may be
* called from real time thread with constant interval (1ms typically). It
* processes receive PDO CANopen objects.
*
* @param co CANopen object.
* @param syncWas True, if CANopen SYNC message was just received or
* transmitted.
*/
void CO_process_RPDO(CO_t *co, bool_t syncWas);
#endif
#if (CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE
/**
* Process CANopen TPDO objects.
*
* Function must be called cyclically from real time thread with constant.
* interval (1ms typically). It processes transmit PDO CANopen objects.
* Function must be called cyclically. For time critical applications it may be
* called from real time thread with constant interval (1ms typically). It
* processes transmit PDO CANopen objects.
*
* @param co CANopen object.
* @param syncWas True, if CANopen SYNC message was just received or
@ -455,13 +584,16 @@ void CO_process_TPDO(CO_t *co,
bool_t syncWas,
uint32_t timeDifference_us,
uint32_t *timerNext_us);
#endif
#if CO_NO_SRDO != 0 || defined CO_DOXYGEN
#if ((CO_CONFIG_SRDO) & CO_CONFIG_SRDO_ENABLE) || defined CO_DOXYGEN
/**
* Process CANopen SRDO objects.
*
* Function must be called cyclically from real time thread with constant.
* interval (1ms typically). It processes receive SRDO CANopen objects.
* Function must be called cyclically. For time critical applications it may be
* called from real time thread with constant interval (1ms typically). It
* processes SRDO CANopen objects.
*
* @param co CANopen object.
* @param timeDifference_us Time difference from previous function call in
@ -471,7 +603,7 @@ void CO_process_TPDO(CO_t *co,
void CO_process_SRDO(CO_t *co,
uint32_t timeDifference_us,
uint32_t *timerNext_us);
#endif /* CO_NO_SRDO != 0 */
#endif
/** @} */ /* CO_CANopen */

View file

@ -16,21 +16,21 @@ INCLUDE_DIRS = \
-I$(OD_SRC) \
-I$(APPL_SRC)
# $(DRV_SRC)/CO_OD_storage.c \
SOURCES = \
$(DRV_SRC)/CO_driver.c \
$(DRV_SRC)/CO_error.c \
$(DRV_SRC)/CO_epoll_interface.c \
$(DRV_SRC)/CO_OD_storage.c \
$(CANOPEN_SRC)/301/CO_ODinterface.c \
$(CANOPEN_SRC)/301/CO_SDOserver.c \
$(CANOPEN_SRC)/301/CO_Emergency.c \
$(CANOPEN_SRC)/301/CO_NMT_Heartbeat.c \
$(CANOPEN_SRC)/301/CO_HBconsumer.c \
$(CANOPEN_SRC)/301/CO_Emergency.c \
$(CANOPEN_SRC)/301/CO_SDOserver.c \
$(CANOPEN_SRC)/301/CO_SDOclient.c \
$(CANOPEN_SRC)/301/CO_TIME.c \
$(CANOPEN_SRC)/301/CO_SYNC.c \
$(CANOPEN_SRC)/301/CO_PDO.c \
$(CANOPEN_SRC)/301/CO_TIME.c \
$(CANOPEN_SRC)/301/CO_SDOclient.c \
$(CANOPEN_SRC)/301/crc16-ccitt.c \
$(CANOPEN_SRC)/301/CO_fifo.c \
$(CANOPEN_SRC)/303/CO_LEDs.c \
@ -41,18 +41,23 @@ SOURCES = \
$(CANOPEN_SRC)/309/CO_gateway_ascii.c \
$(CANOPEN_SRC)/extra/CO_trace.c \
$(CANOPEN_SRC)/CANopen.c \
$(OD_SRC)/CO_OD.c \
$(OD_SRC)/OD.c \
$(APPL_SRC)/CO_main_basic.c
OBJS = $(SOURCES:%.c=%.o)
CC ?= gcc
OPT = -g
#OPT = -g -DCO_SINGLE_THREAD
OPT = -g -pedantic
#OPT = -g -pedantic -fanalyzer
#OPT = -g -pedantic -DCO_USE_GLOBALS
#OPT = -g -pedantic -DCO_MULTIPLE_OD
#OPT = -g -pedantic -DCO_SINGLE_THREAD
CFLAGS = -Wall $(OPT) $(INCLUDE_DIRS)
LDFLAGS = -pthread
#LDFLAGS =
#Options can be passed via make: 'make OPT="-g -DCO_SINGLE_THREAD" LDFLAGS=""'
.PHONY: all clean

View file

@ -232,19 +232,10 @@ Some details
RTR (remote transmission request) is a feature of CAN bus. Usage of RTR
is not recommended for CANopen and it is not implemented in CANopenNode.
### Self start
Object **0x1F80** from Object Dictionary enables the NMT slaves to start
automatically or allows it to start the whole network. It is specified in
DSP302-2 standard. Standard allows two values for slaves for object 0x1F80:
- Object 0x1F80, value = **0x8** - "NMT slave shall enter the NMT state
Operational after the NMT state Initialization autonomously (self starting)"
- Object 0x1F80, value = **0x2** - "NMT slave shall execute the NMT service
start remote node with node-ID set to 0"
### Error control
When node is stated (in NMT operational state), it is allowed to send or
When node is started (in NMT operational state), it is allowed to send or
receive Process Data Objects (PDO). If Error Register (object 0x1001) is set,
then NMT operational state is not allowed.
then NMT operational state may not be allowed.
### Power saving
All CANopen objects calculates next timer info for OS. Calculation is based on

View file

@ -1,10 +1,20 @@
Change Log
==========
[newOD]
-------------------------
- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/master...newOD)
### Changed
- New Object dictionary interface. It has very similar principles as before. All parts of CANopenNode objects, which works with OD entries, are rewritten.
- New OD.h and OD.c files, replaces CO_OD files.
- CANopen.c and CANopen.h files redesigned. Integration of "OD.h" is optional. Configuration of multiple object dictionaries is possible with one CANopen device. Interface is the same, with some changes to function arguments.
- Rewritten SDO server. Object dictionary part is removed.
- CO_Emergency is mostly rewritten. Now is much easier customization.
- CO_NMT_Heartbeat is redesigned.
[Unreleased master]
-------------------------
- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.3...master)
- See {TODO diff} for example of change in user application interface.
### Removed
- All drivers removed from this project, except Neuberger-socketCAN for Linux.
### Changed
@ -38,7 +48,7 @@ Change Log
### Added
- Documentation added to `doc` directory: CHANGELOG.md, deviceSupport.md, gettingStarted.md, LSSusage.md and traceUsage.md.
- All CANopen objects calculates next timer info for OS. Useful for energy saving.
- Added file CO_config.h for stack configuration. Can be overridden by target specific or by custom definitions.
- Added file CO_config.h for stack configuration. Can be overridden by target specific or by custom definitions. It enables/disables whole CanOpenNode objects or parts of them. It also specifies some constants.
- CO_fifo.h/c for fifo data buffer, used with rewritten SDO client, etc.
- CANopen gateway-ascii command interface according to CiA309-3 as a microcontroller independent module. It includes NMT master, LSS master and SDO client interface. Interface is non-blocking, it is added to mainline. Example for Linux stdio and socket is included.
@ -92,6 +102,7 @@ Change Log
Changelog written according to recommendations from https://keepachangelog.com/
[newOD]: https://github.com/CANopenNode/CANopenNode/tree/newOD
[Unreleased master]: https://github.com/CANopenNode/CANopenNode
[v1.3]: https://github.com/CANopenNode/CANopenNode/tree/v1.3
[v1.2]: https://github.com/CANopenNode/CANopenNode/tree/v1.2

View file

@ -62,11 +62,13 @@ extern "C" {
#ifndef CO_CONFIG_EM
#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | \
CO_CONFIG_EM_PROD_CONFIGURABLE | \
CO_CONFIG_EM_PROD_INHIBIT | \
CO_CONFIG_EM_HISTORY | \
CO_CONFIG_EM_STATUS_BITS | \
CO_CONFIG_EM_CONSUMER | \
CO_CONFIG_FLAG_CALLBACK_PRE | \
CO_CONFIG_FLAG_TIMERNEXT | \
CO_CONFIG_FLAG_OD_DYNAMIC)
CO_CONFIG_FLAG_TIMERNEXT)
#endif
#ifndef CO_CONFIG_SDO_SRV

12
example/OD.c Normal file
View file

@ -0,0 +1,12 @@
#define OD_DEFINITION
#include "301/CO_ODinterface.h"
#include "OD.h"
static const OD_entry_t OD_list[] = {
{0x1000, 0, 0, 0, NULL}
};
const OD_t OD = {
sizeof(OD_list) / sizeof(OD_list[0]),
&OD_list[0]
};

45
example/OD.h Normal file
View file

@ -0,0 +1,45 @@
/* blank OD for pre-release */
extern const OD_t OD;
#define CO_CNT_NMT 1
#define CO_CNT_HB_PROD 1
#define CO_CNT_EM 1
#define CO_CNT_EM_PROD 1
#define CO_CNT_SDO_SRV 1
#define CO_CNT_SDO_CLI 1
#if 0
#define CO_CNT_HB_CONS 1
#define CO_CNT_TIME 1
#define CO_CNT_SYNC 1
#define CO_CNT_SYNC_PROD 1
#define CO_CNT_RPDO 4
#define CO_CNT_TPDO 4
#define CO_CNT_GFC 0
#define CO_CNT_SRDO 0
#define CO_CNT_TRACE 0
#endif
#define OD_ENTRY_H1017 &OD.list[0]
#define OD_ENTRY_H1016 &OD.list[0]
#define OD_ENTRY_H1001 &OD.list[0]
#define OD_ENTRY_H1014 &OD.list[0]
#define OD_ENTRY_H1015 &OD.list[0]
#define OD_ENTRY_H1003 &OD.list[0]
#define OD_ENTRY_H1200 &OD.list[0]
#define OD_ENTRY_H1280 &OD.list[0]
#define OD_ENTRY_H1012 &OD.list[0]
#define OD_ENTRY_H1005 &OD.list[0]
#define OD_ENTRY_H1006 &OD.list[0]
#define OD_ENTRY_H1007 &OD.list[0]
#define OD_ENTRY_H1019 &OD.list[0]
#define OD_ENTRY_H1400 &OD.list[0]
#define OD_ENTRY_H1600 &OD.list[0]
#define OD_ENTRY_H1800 &OD.list[0]
#define OD_ENTRY_H1A00 &OD.list[0]
#define OD_ENTRY_H1300 &OD.list[0]
#define OD_ENTRY_H1301 &OD.list[0]
#define OD_ENTRY_H1381 &OD.list[0]
#define OD_ENTRY_H13FE &OD.list[0]
#define OD_ENTRY_H13FF &OD.list[0]

View file

@ -53,6 +53,14 @@
extern "C" {
#endif
/* TODO some parts are disabled in non-finished pre-release */
#define CO_CONFIG_HB_CONS (0)
#define CO_CONFIG_TIME (0)
#define CO_CONFIG_SYNC (0)
#define CO_CONFIG_PDO (0)
#define CO_CONFIG_TRACE (0)
/* Stack configuration override default values.
* For more information see file CO_config.h. */
#ifdef CO_SINGLE_THREAD
@ -77,11 +85,13 @@ extern "C" {
#ifndef CO_CONFIG_EM
#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | \
CO_CONFIG_EM_PROD_CONFIGURABLE | \
CO_CONFIG_EM_PROD_INHIBIT | \
CO_CONFIG_EM_HISTORY | \
CO_CONFIG_EM_STATUS_BITS | \
CO_CONFIG_EM_CONSUMER | \
CO_CONFIG_FLAG_CALLBACK_PRE_USED | \
CO_CONFIG_FLAG_TIMERNEXT | \
CO_CONFIG_FLAG_OD_DYNAMIC)
CO_CONFIG_FLAG_TIMERNEXT)
#endif
#ifndef CO_CONFIG_SDO_SRV
@ -172,7 +182,7 @@ extern "C" {
#endif
#ifndef CO_CONFIG_TRACE
//#define CO_CONFIG_TRACE (CO_CONFIG_TRACE_ENABLE)
#define CO_CONFIG_TRACE (CO_CONFIG_TRACE_ENABLE)
#endif

View file

@ -261,11 +261,11 @@ void CO_epoll_initCANopenMain(CO_epoll_t *ep, CO_t *co) {
(void *)ep, wakeupCallback);
#endif
#if (CO_CONFIG_SDO_SRV) & CO_CONFIG_FLAG_CALLBACK_PRE
CO_SDO_initCallbackPre(co->SDO[0],
(void *)ep, wakeupCallback);
CO_SDOserver_initCallbackPre(&co->SDOserver[0],
(void *)ep, wakeupCallback);
#endif
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE
CO_SDOclient_initCallbackPre(co->SDOclient[0],
CO_SDOclient_initCallbackPre(&co->SDOclient[0],
(void *)ep, wakeupCallback);
#endif
#if (CO_CONFIG_TIME) & CO_CONFIG_FLAG_CALLBACK_PRE
@ -284,6 +284,7 @@ void CO_epoll_initCANopenMain(CO_epoll_t *ep, CO_t *co) {
void CO_epoll_processMain(CO_epoll_t *ep,
CO_t *co,
bool_t enableGateway,
CO_NMT_reset_cmd_t *reset)
{
if (ep == NULL || co == NULL || reset == NULL) {
@ -291,7 +292,10 @@ void CO_epoll_processMain(CO_epoll_t *ep,
}
/* process CANopen objects */
*reset = CO_process(co, ep->timeDifference_us, &ep->timerNext_us);
*reset = CO_process(co,
enableGateway,
ep->timeDifference_us,
&ep->timerNext_us);
}
@ -306,7 +310,7 @@ void CO_epoll_processRT(CO_epoll_t *ep,
/* Verify for epoll events */
if (ep->epoll_new) {
if (CO_CANrxFromEpoll(co->CANmodule[0], &ep->ev, NULL, NULL)) {
if (CO_CANrxFromEpoll(co->CANmodule, &ep->ev, NULL, NULL)) {
ep->epoll_new = false;
}
}
@ -315,21 +319,21 @@ void CO_epoll_processRT(CO_epoll_t *ep,
uint32_t *pTimerNext_us = realtime ? NULL : &ep->timerNext_us;
CO_LOCK_OD();
if (!co->nodeIdUnconfigured && co->CANmodule[0]->CANnormal) {
if (!co->nodeIdUnconfigured && co->CANmodule->CANnormal) {
bool_t syncWas = false;
#if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE
/* Process Sync */
syncWas = CO_process_SYNC(co, ep->timeDifference_us,
pTimerNext_us);
#endif
/* Read inputs */
#if (CO_CONFIG_PDO) & CO_CONFIG_RPDO_ENABLE
CO_process_RPDO(co, syncWas);
/* Write outputs */
#endif
#if (CO_CONFIG_PDO) & CO_CONFIG_TPDO_ENABLE
CO_process_TPDO(co, syncWas, ep->timeDifference_us,
pTimerNext_us);
#endif
(void) syncWas; (void) pTimerNext_us;
}
CO_UNLOCK_OD();
}

View file

@ -179,10 +179,12 @@ void CO_epoll_initCANopenMain(CO_epoll_t *ep, CO_t *co);
*
* @param ep This object
* @param co CANopen object
* @param enableGateway If true, gateway to external world will be enabled.
* @param [out] reset Return from @ref CO_process().
*/
void CO_epoll_processMain(CO_epoll_t *ep,
CO_t *co,
bool_t enableGateway,
CO_NMT_reset_cmd_t *reset);

View file

@ -24,7 +24,7 @@
*/
#ifndef CO_OD_STORAGE
#define CO_OD_STORAGE 1
#define CO_OD_STORAGE 0
#endif
#include <stdio.h>
@ -43,6 +43,7 @@
#include <sys/reboot.h>
#include "CANopen.h"
#include "OD.h"
#include "CO_error.h"
#include "CO_epoll_interface.h"
#if CO_OD_STORAGE == 1
@ -74,14 +75,31 @@
#define TMR_THREAD_INTERVAL_US 1000
#endif
/* default values */
#ifndef NMT_CONTROL
#define NMT_CONTROL \
CO_NMT_STARTUP_TO_OPERATIONAL \
|| CO_NMT_ERR_ON_ERR_REG \
|| CO_ERR_REG_GENERIC_ERR \
|| CO_ERR_REG_COMMUNICATION
#endif
#ifndef FIRST_HB_TIME
#define FIRST_HB_TIME 500
#endif
#ifndef SDO_TIMEOUT_TIME
#define SDO_TIMEOUT_TIME 1000
#endif
#ifndef GATEWAY_ENABLE
#define GATEWAY_ENABLE true
#endif
/* Other variables and objects */
#ifndef CO_SINGLE_THREAD
CO_epoll_t epRT; /* Epoll-timer object for realtime thread */
static int rtPriority = -1; /* Real time priority, configurable by arguments. (-1=RT disabled) */
#endif
static uint8_t CO_pendingNodeId = 0xFF;/* Use value from Object Dictionary or by arguments (set to 1..127
* or unconfigured=0xFF). Can be changed by LSS slave. */
CO_t *CO = NULL; /* CANopen object */
static uint8_t CO_pendingNodeId = 0xFF; /* Set by arguments or by OD_CAN_NODE_ID macro, if defined. Can be changed by LSS slave. */
static uint8_t CO_activeNodeId = 0xFF;/* Copied from CO_pendingNodeId in the communication reset section */
static uint16_t CO_pendingBitRate = 0; /* CAN bitrate, not used here */
#if CO_OD_STORAGE == 1
@ -166,6 +184,7 @@ static void NmtChangedCallback(CO_NMT_internalState_t state)
log_printf(LOG_NOTICE, DBG_NMT_CHANGE, NmtState2Str(state), state);
}
#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE
/* callback for monitoring Heartbeat remote NMT state change */
static void HeartbeatNmtChangedCallback(uint8_t nodeId,
CO_NMT_internalState_t state,
@ -175,6 +194,7 @@ static void HeartbeatNmtChangedCallback(uint8_t nodeId,
log_printf(LOG_NOTICE, DBG_HB_CONS_NMT_CHANGE,
nodeId, NmtState2Str(state), state);
}
#endif
#if CO_OD_STORAGE == 1
/* callback for storing node id and bitrate */
@ -193,8 +213,7 @@ printf(
printf(
"\n"
"Options:\n"
" -i <Node ID> CANopen Node-id (1..127) or 0xFF(unconfigured). If not\n"
" specified, value from Object dictionary (0x2101) is used.\n");
" -i <Node ID> CANopen Node-id (1..127) or 0xFF (LSS unconfigured).\n");
#ifndef CO_SINGLE_THREAD
printf(
" -p <RT priority> Real-time priority of RT thread (1 .. 99). If not set or\n"
@ -331,14 +350,15 @@ int main (int argc, char *argv[]) {
}
if(!nodeIdFromArgs) {
#ifdef OD_CAN_NODE_ID
/* use value from Object dictionary, if not set by program arguments */
CO_pendingNodeId = OD_CANNodeID;
CO_pendingNodeId = OD_CAN_NODE_ID;
#endif
}
if((CO_pendingNodeId < 1 || CO_pendingNodeId > 127)
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE
&& CO_NO_LSS_SLAVE == 1 && CO_pendingNodeId != CO_LSS_NODE_ID_ASSIGNMENT
#endif
&& CO_isLSSslaveEnabled(CO)
&& CO_pendingNodeId != CO_LSS_NODE_ID_ASSIGNMENT
) {
log_printf(LOG_CRIT, DBG_WRONG_NODE_ID, CO_pendingNodeId);
printUsage(argv[0]);
@ -364,13 +384,16 @@ int main (int argc, char *argv[]) {
/* Allocate memory for CANopen objects */
err = CO_new(NULL);
if (err != CO_ERROR_NO) {
log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_new()", err);
uint32_t heapMemoryUsed = 0;
CO = CO_new(NULL, &heapMemoryUsed);
if (CO == NULL) {
log_printf(LOG_CRIT, DBG_GENERAL,
"CO_new(), heapMemoryUsed=", heapMemoryUsed);
exit(EXIT_FAILURE);
}
#if CO_OD_STORAGE == 1
/* Verify, if OD structures have proper alignment of initial values */
if(CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) {
log_printf(LOG_CRIT, DBG_OBJECT_DICTIONARY, "CO_OD_RAM");
@ -386,7 +409,6 @@ int main (int argc, char *argv[]) {
}
#if CO_OD_STORAGE == 1
/* initialize Object Dictionary storage */
odStorStatus_rom = CO_OD_storage_init(&odStor, (uint8_t*) &CO_OD_ROM, sizeof(CO_OD_ROM), odStorFile_rom);
odStorStatus_eeprom = CO_OD_storage_init(&odStorAuto, (uint8_t*) &CO_OD_EEPROM, sizeof(CO_OD_EEPROM), odStorFile_eeprom);
@ -437,23 +459,30 @@ int main (int argc, char *argv[]) {
/* Wait rt_thread. */
if(!firstRun) {
CO_LOCK_OD();
CO->CANmodule[0]->CANnormal = false;
CO->CANmodule->CANnormal = false;
CO_UNLOCK_OD();
}
/* Enter CAN configuration. */
CO_CANsetConfigurationMode((void *)&CANptr);
CO_CANmodule_disable(CO->CANmodule[0]);
CO_CANmodule_disable(CO->CANmodule);
/* initialize CANopen */
err = CO_CANinit((void *)&CANptr, 0 /* bit rate not used */);
err = CO_CANinit(CO, (void *)&CANptr, 0 /* bit rate not used */);
if(err != CO_ERROR_NO) {
log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANinit()", err);
exit(EXIT_FAILURE);
}
err = CO_LSSinit(&CO_pendingNodeId, &CO_pendingBitRate);
CO_LSS_address_t lssAddress = {.identity = {
.vendorID = 1,
.productCode = 2,
.revisionNumber = 3,
.serialNumber = 4
}};
err = CO_LSSinit(CO, &lssAddress,
&CO_pendingNodeId, &CO_pendingBitRate);
if(err != CO_ERROR_NO) {
log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_LSSinit()", err);
exit(EXIT_FAILURE);
@ -461,7 +490,15 @@ int main (int argc, char *argv[]) {
CO_activeNodeId = CO_pendingNodeId;
err = CO_CANopenInit(CO_activeNodeId);
err = CO_CANopenInit(CO, /* CANopen object */
NULL, /* alternate NMT */
NULL, /* alternate em */
&OD, /* Object dictionary */
NULL, /* Optional OD_statusBits */
NMT_CONTROL, /* CO_NMT_control_t */
FIRST_HB_TIME, /* firstHBTime_ms */
SDO_TIMEOUT_TIME, /* SDOtimeoutTime_ms */
CO_activeNodeId);
if(err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) {
log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANopenInit()", err);
exit(EXIT_FAILURE);
@ -479,8 +516,10 @@ int main (int argc, char *argv[]) {
if(!CO->nodeIdUnconfigured) {
CO_EM_initCallbackRx(CO->em, EmergencyRxCallback);
CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback);
#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE
CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL,
HeartbeatNmtChangedCallback);
#endif
#if CO_OD_STORAGE == 1
/* initialize OD objects 1010 and 1011 and verify errors. */
CO_OD_configure(CO->SDO[0], OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)&odStor, 0, 0U);
@ -536,7 +575,7 @@ int main (int argc, char *argv[]) {
/* start CAN */
CO_CANsetNormalMode(CO->CANmodule[0]);
CO_CANsetNormalMode(CO->CANmodule);
reset = CO_RESET_NOT;
@ -549,7 +588,7 @@ int main (int argc, char *argv[]) {
#ifdef CO_SINGLE_THREAD
CO_epoll_processRT(&epMain, CO, false);
#endif
CO_epoll_processMain(&epMain, CO, &reset);
CO_epoll_processMain(&epMain, CO, GATEWAY_ENABLE, &reset);
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
CO_epoll_processGtw(&epGtw, CO, &epMain);
#endif
@ -595,7 +634,8 @@ int main (int argc, char *argv[]) {
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
CO_epoll_closeGtw(&epGtw);
#endif
CO_delete((void *)&CANptr);
CO_CANsetConfigurationMode((void *)&CANptr);
CO_delete(CO);
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_activeNodeId, "finished");
@ -627,7 +667,7 @@ static void* rt_thread(void* arg) {
#if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE
/* Monitor variables with trace objects */
CO_time_process(&CO_time);
for(i=0; i<OD_traceEnable && i<CO_NO_TRACE; i++) {
for(i=0; i<OD_traceEnable && i<co->CNT_TRACE; i++) {
CO_trace_process(CO->trace[i], *CO_time.epochTimeOffsetMs);
}
#endif