/** * CANopen Service Data Object - server protocol. * * @file CO_SDOserver.h * @ingroup CO_SDOserver * @author Janez Paternoster * @copyright 2004 - 2020 Janez Paternoster * * This file is part of CANopenNode, an opensource CANopen Stack. * Project home page is . * For more information on CANopen see . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CO_SDO_SERVER_H #define CO_SDO_SERVER_H #include #include "301/CO_driver.h" /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_SDO_SRV #define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED) #endif #ifndef CO_CONFIG_SDO_SRV_BUFFER_SIZE #define CO_CONFIG_SDO_SRV_BUFFER_SIZE 32 #endif #define CO_CONFIG_SDO CO_CONFIG_SDO_SRV #define CO_CONFIG_SDO_BUFFER_SIZE CO_CONFIG_SDO_SRV_BUFFER_SIZE #ifdef __cplusplus extern "C" { #endif /** * @defgroup CO_SDOserver SDO server * @ingroup CO_CANopen_301 * @{ * * CANopen Service Data Object - server protocol. * * Service data objects (SDOs) allow the access to any entry of the CANopen * Object dictionary. An SDO establishes a peer-to-peer communication channel * between two devices. In addition, the SDO protocol enables to transfer any * amount of data in a segmented way. Therefore the SDO protocol is mainly used * in order to communicate configuration data. * * All CANopen devices must have implemented SDO server and first SDO server * channel. Servers serves data from Object dictionary. Object dictionary * is a collection of variables, arrays or records (structures), which can be * used by the stack or by the application. This file (CO_SDOserver.h) * implements SDO server. * * SDO client can be (optionally) implemented on one (or multiple, if multiple * SDO channels are used) device in CANopen network. Usually this is master * device and provides also some kind of user interface, so configuration of * the network is possible. Code for the SDO client is in file CO_SDOclient.h. * * SDO communication cycle is initiated by the client. Client can upload (read) * data from device or can download (write) data to device. If data size is less * or equal to 4 bytes, communication is finished by one server response * (expedited transfer). If data size is longer, data are split into multiple * segments of request/response pairs (normal or segmented transfer). For longer * data there is also a block transfer protocol, which transfers larger block of * data in secure way with little protocol overhead. If error occurs during SDO * transfer #CO_SDO_abortCode_t is send by client or server and transfer is * terminated. For more details see #CO_SDO_state_t. */ /** * Internal state flags indicate type of transfer * * These flags correspond to the upper nibble of the SDO state machine states * and can be used to determine the type of state an SDO object is in. */ #define CO_SDO_ST_FLAG_DOWNLOAD 0x10U #define CO_SDO_ST_FLAG_UPLOAD 0x20U #define CO_SDO_ST_FLAG_BLOCK 0x40U /** * Internal states of the SDO state machine. * * Upper nibble of byte indicates type of state: * 0x10: Download * 0x20: Upload * 0x40: Block Mode * * Note: CANopen has little endian byte order. */ typedef enum { /** * - SDO client may start new download to or upload from specified node, * specified index and specified subindex. It can start normal or block * communication. * - SDO server is waiting for client request. */ CO_SDO_ST_IDLE = 0x00U, /** * - SDO client or server may send SDO abort message in case of error: * - byte 0: @b 10000000 binary. * - byte 1..3: Object index and subIndex. * - byte 4..7: #CO_SDO_abortCode_t. */ CO_SDO_ST_ABORT = 0x01U, /** * - SDO client: Node-ID of the SDO server is the same as node-ID of this node, * SDO client is the same device as SDO server. Transfer data directly without * communication on CAN. * - SDO server does not use this state. */ CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER = 0x10U, /** * - SDO client initiates SDO download: * - byte 0: @b 0010nnes binary: (nn: if e=s=1, number of data bytes, that do * @b not contain data; e=1 for expedited transfer; s=1 if data size is * indicated.) * - byte 1..3: Object index and subIndex. * - byte 4..7: If e=1, expedited data are here. If e=0 s=1, size of data for * segmented transfer is indicated here. * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ CO_SDO_ST_DOWNLOAD_INITIATE_REQ = 0x11U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 01100000 binary. * - byte 1..3: Object index and subIndex. * - byte 4..7: Reserved. * - In case of expedited transfer communication ends here. */ CO_SDO_ST_DOWNLOAD_INITIATE_RSP = 0x12U, /** * - SDO client sends SDO segment: * - byte 0: @b 000tnnnc binary: (t: toggle bit, set to 0 in first segment; * nnn: number of data bytes, that do @b not contain data; c=1 if this is the * last segment). * - byte 1..7: Data segment. * - SDO server waits for segment. */ CO_SDO_ST_DOWNLOAD_SEGMENT_REQ = 0x13U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 001t0000 binary: (t: toggle bit, set to 0 in first segment). * - byte 1..7: Reserved. * - If c was set to 1, then communication ends here. */ CO_SDO_ST_DOWNLOAD_SEGMENT_RSP = 0x14U, /** * - SDO client: Node-ID of the SDO server is the same as node-ID of this node, * SDO client is the same device as SDO server. Transfer data directly without * communication on CAN. * - SDO server does not use this state. */ CO_SDO_ST_UPLOAD_LOCAL_TRANSFER = 0x20U, /** * - SDO client initiates SDO upload: * - byte 0: @b 01000000 binary. * - byte 1..3: Object index and subIndex. * - byte 4..7: Reserved. * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ CO_SDO_ST_UPLOAD_INITIATE_REQ = 0x21U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 0100nnes binary: (nn: if e=s=1, number of data bytes, that do * @b not contain data; e=1 for expedited transfer; s=1 if data size is * indicated). * - byte 1..3: Object index and subIndex. * - byte 4..7: If e=1, expedited data are here. If e=0 s=1, size of data for * segmented transfer is indicated here. * - In case of expedited transfer communication ends here. */ CO_SDO_ST_UPLOAD_INITIATE_RSP = 0x22U, /** * - SDO client requests SDO segment: * - byte 0: @b 011t0000 binary: (t: toggle bit, set to 0 in first segment). * - byte 1..7: Reserved. * - SDO server waits for segment request. */ CO_SDO_ST_UPLOAD_SEGMENT_REQ = 0x23U, /** * - SDO client waits for response. * - SDO server responses with data: * - byte 0: @b 000tnnnc binary: (t: toggle bit, set to 0 in first segment; * nnn: number of data bytes, that do @b not contain data; c=1 if this is the * last segment). * - byte 1..7: Data segment. * - If c is set to 1, then communication ends here. */ CO_SDO_ST_UPLOAD_SEGMENT_RSP = 0x24U, /** * - SDO client initiates SDO block download: * - byte 0: @b 11000rs0 binary: (r=1 if client supports generating CRC on * data; s=1 if data size is indicated.) * - byte 1..3: Object index and subIndex. * - byte 4..7: If s=1, then size of data for block download is indicated here. * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ = 0x51U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 10100r00 binary: (r=1 if server supports generating CRC on * data.) * - byte 1..3: Object index and subIndex. * - byte 4: blksize: Number of segments per block that shall be used by the * client for the following block download with 0 < blksize < 128. * - byte 5..7: Reserved. */ CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP = 0x52U, /** * - SDO client sends 'blksize' segments of data in sequence: * - byte 0: @b cnnnnnnn binary: (c=1 if no more segments to be downloaded, * enter SDO block download end phase; nnnnnnn is sequence number of segment, * 1..127. * - byte 1..7: At most 7 bytes of segment data to be downloaded. * - SDO server reads sequence of 'blksize' blocks. */ CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ = 0x53U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 10100010 binary. * - byte 1: ackseq: sequence number of last segment that was received * successfully during the last block download. If ackseq is set to 0 the * server indicates the client that the segment with the sequence number 1 * was not received correctly and all segments shall be retransmitted by the * client. * - byte 2: Number of segments per block that shall be used by the client for * the following block download with 0 < blksize < 128. * - byte 3..7: Reserved. * - If c was set to 1, then communication enters SDO block download end phase. */ CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP = 0x54U, /** * - SDO client sends SDO block download end: * - byte 0: @b 110nnn01 binary: (nnn: number of data bytes, that do @b not * contain data) * - byte 1..2: 16 bit CRC for the data set, if enabled by client and server. * - byte 3..7: Reserved. * - SDO server waits for client request. */ CO_SDO_ST_DOWNLOAD_BLK_END_REQ = 0x55U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 10100001 binary. * - byte 1..7: Reserved. * - Block download successfully ends here. */ CO_SDO_ST_DOWNLOAD_BLK_END_RSP = 0x56U, /** * - SDO client initiates SDO block upload: * - byte 0: @b 10100r00 binary: (r=1 if client supports generating CRC on * data.) * - byte 1..3: Object index and subIndex. * - byte 4: blksize: Number of segments per block with 0 < blksize < 128. * - byte 5: pst - protocol switch threshold. If pst > 0 and size of the data * in bytes is less or equal pst, then the server may switch to the SDO * upload protocol #CO_SDO_ST_UPLOAD_INITIATE_RSP. * - byte 6..7: Reserved. * - SDO server is in #CO_SDO_ST_IDLE state and waits for client request. */ CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ = 0x61U, /** * - SDO client waits for response. * - SDO server responses: * - byte 0: @b 11000rs0 binary: (r=1 if server supports generating CRC on * data; s=1 if data size is indicated. ) * - byte 1..3: Object index and subIndex. * - byte 4..7: If s=1, then size of data for block upload is indicated here. * - If enabled by pst, then server may alternatively response with * #CO_SDO_ST_UPLOAD_INITIATE_RSP */ CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP = 0x62U, /** * - SDO client sends second initiate for SDO block upload: * - byte 0: @b 10100011 binary. * - byte 1..7: Reserved. * - SDO server waits for client request. */ CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2 = 0x63U, /** * - SDO client reads sequence of 'blksize' blocks. * - SDO server sends 'blksize' segments of data in sequence: * - byte 0: @b cnnnnnnn binary: (c=1 if no more segments to be uploaded, * enter SDO block upload end phase; nnnnnnn is sequence number of segment, * 1..127. * - byte 1..7: At most 7 bytes of segment data to be uploaded. */ CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ = 0x64U, /** * - SDO client responses: * - byte 0: @b 10100010 binary. * - byte 1: ackseq: sequence number of last segment that was received * successfully during the last block upload. If ackseq is set to 0 the * client indicates the server that the segment with the sequence number 1 * was not received correctly and all segments shall be retransmitted by the * server. * - byte 2: Number of segments per block that shall be used by the server for * the following block upload with 0 < blksize < 128. * - byte 3..7: Reserved. * - SDO server waits for response. * - If c was set to 1, then communication enters SDO block upload end phase. */ CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP = 0x65U, /** * - SDO client waits for server request. * - SDO server sends SDO block upload end: * - byte 0: @b 110nnn01 binary: (nnn: number of data bytes, that do @b not * contain data) * - byte 1..2: 16 bit CRC for the data set, if enabled by client and server. * - byte 3..7: Reserved. */ CO_SDO_ST_UPLOAD_BLK_END_SREQ = 0x66U, /** * - SDO client responses: * - byte 0: @b 10100001 binary. * - byte 1..7: Reserved. * - SDO server waits for response. * - Block download successfully ends here. Note that this communication ends * with client response. Client may then start next SDO communication * immediately. */ CO_SDO_ST_UPLOAD_BLK_END_CRSP = 0x67U, /* old state names, will be removed */ CO_SDO_ST_DOWNLOAD_INITIATE = 0xA1U, CO_SDO_ST_DOWNLOAD_SEGMENTED = 0xA2U, CO_SDO_ST_DOWNLOAD_BL_INITIATE = 0xA4U, CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK = 0xA5U, CO_SDO_ST_DOWNLOAD_BL_SUB_RESP = 0xA6U, CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2 = 0xA7U, CO_SDO_ST_DOWNLOAD_BL_END = 0xA8U, CO_SDO_ST_UPLOAD_INITIATE = 0xB1U, CO_SDO_ST_UPLOAD_SEGMENTED = 0xB2U, CO_SDO_ST_UPLOAD_BL_INITIATE = 0xB4U, CO_SDO_ST_UPLOAD_BL_INITIATE_2 = 0xB5U, CO_SDO_ST_UPLOAD_BL_SUBBLOCK = 0xB6U, CO_SDO_ST_UPLOAD_BL_END = 0xB7U } CO_SDO_state_t; /** * SDO abort codes. * * Send with Abort SDO transfer message. * * The abort codes not listed here are reserved. */ typedef enum{ CO_SDO_AB_NONE = 0x00000000UL, /**< 0x00000000, No abort */ CO_SDO_AB_TOGGLE_BIT = 0x05030000UL, /**< 0x05030000, Toggle bit not altered */ CO_SDO_AB_TIMEOUT = 0x05040000UL, /**< 0x05040000, SDO protocol timed out */ CO_SDO_AB_CMD = 0x05040001UL, /**< 0x05040001, Command specifier not valid or unknown */ CO_SDO_AB_BLOCK_SIZE = 0x05040002UL, /**< 0x05040002, Invalid block size in block mode */ CO_SDO_AB_SEQ_NUM = 0x05040003UL, /**< 0x05040003, Invalid sequence number in block mode */ CO_SDO_AB_CRC = 0x05040004UL, /**< 0x05040004, CRC error (block mode only) */ CO_SDO_AB_OUT_OF_MEM = 0x05040005UL, /**< 0x05040005, Out of memory */ CO_SDO_AB_UNSUPPORTED_ACCESS = 0x06010000UL, /**< 0x06010000, Unsupported access to an object */ CO_SDO_AB_WRITEONLY = 0x06010001UL, /**< 0x06010001, Attempt to read a write only object */ CO_SDO_AB_READONLY = 0x06010002UL, /**< 0x06010002, Attempt to write a read only object */ CO_SDO_AB_NOT_EXIST = 0x06020000UL, /**< 0x06020000, Object does not exist in the object dictionary */ CO_SDO_AB_NO_MAP = 0x06040041UL, /**< 0x06040041, Object cannot be mapped to the PDO */ CO_SDO_AB_MAP_LEN = 0x06040042UL, /**< 0x06040042, Number and length of object to be mapped exceeds PDO length */ CO_SDO_AB_PRAM_INCOMPAT = 0x06040043UL, /**< 0x06040043, General parameter incompatibility reasons */ CO_SDO_AB_DEVICE_INCOMPAT = 0x06040047UL, /**< 0x06040047, General internal incompatibility in device */ CO_SDO_AB_HW = 0x06060000UL, /**< 0x06060000, Access failed due to hardware error */ CO_SDO_AB_TYPE_MISMATCH = 0x06070010UL, /**< 0x06070010, Data type does not match, length of service parameter does not match */ CO_SDO_AB_DATA_LONG = 0x06070012UL, /**< 0x06070012, Data type does not match, length of service parameter too high */ CO_SDO_AB_DATA_SHORT = 0x06070013UL, /**< 0x06070013, Data type does not match, length of service parameter too short */ CO_SDO_AB_SUB_UNKNOWN = 0x06090011UL, /**< 0x06090011, Sub index does not exist */ CO_SDO_AB_INVALID_VALUE = 0x06090030UL, /**< 0x06090030, Invalid value for parameter (download only). */ CO_SDO_AB_VALUE_HIGH = 0x06090031UL, /**< 0x06090031, Value range of parameter written too high */ CO_SDO_AB_VALUE_LOW = 0x06090032UL, /**< 0x06090032, Value range of parameter written too low */ CO_SDO_AB_MAX_LESS_MIN = 0x06090036UL, /**< 0x06090036, Maximum value is less than minimum value. */ CO_SDO_AB_NO_RESOURCE = 0x060A0023UL, /**< 0x060A0023, Resource not available: SDO connection */ CO_SDO_AB_GENERAL = 0x08000000UL, /**< 0x08000000, General error */ CO_SDO_AB_DATA_TRANSF = 0x08000020UL, /**< 0x08000020, Data cannot be transferred or stored to application */ CO_SDO_AB_DATA_LOC_CTRL = 0x08000021UL, /**< 0x08000021, Data cannot be transferred or stored to application because of local control */ CO_SDO_AB_DATA_DEV_STATE = 0x08000022UL, /**< 0x08000022, Data cannot be transferred or stored to application because of present device state */ CO_SDO_AB_DATA_OD = 0x08000023UL, /**< 0x08000023, Object dictionary not present or dynamic generation fails */ CO_SDO_AB_NO_DATA = 0x08000024UL /**< 0x08000024, No data available */ }CO_SDO_abortCode_t; /** * @defgroup CO_SDO_objectDictionary Object dictionary * * CANopen Object dictionary implementation in CANopenNode. * * CANopen Object dictionary is a collection of different data items, which can * be used by the stack or by the application. * * Each Object dictionary entry is located under 16-bit index, as specified * by the CANopen: * - 0x0001..0x025F: Data type definitions. * - 0x1000..0x1FFF: Communication profile area. * - 0x2000..0x5FFF: Manufacturer-specific profile area. * - 0x6000..0x9FFF: Standardized device profile area for eight logical devices. * - 0xA000..0xAFFF: Standardized network variable area. * - 0xB000..0xBFFF: Standardized system variable area. * - Other: Reserved. * * If Object dictionary entry has complex data type (array or structure), * then 8-bit subIndex specifies the sub-member of the entry. In that case * subIndex 0x00 is encoded as uint8_t and specifies the highest available * subIndex with that entry. Subindex 0xFF has special meaning in the standard * and is not supported by CANopenNode. * * ####Object type of one Object dictionary entry * - NULL: Not used by CANopenNode. * - DOMAIN: Block of data of variable length. Data and length are * under control of the application. * - DEFTYPE: Definition of CANopen basic data type, for example * INTEGER16. * - DEFSTRUCT: Definition of complex data type - structure, which is * used with RECORD. * - VAR: Variable of CANopen basic data type. Subindex is 0. * - ARRAY: Array of multiple variables of the same CANopen basic * data type. Subindex 1..arrayLength specifies sub-member. * - RECORD: Record or structure of multiple variables of different * CANopen basic data type. Subindex specifies sub-member. * * * ####Implementation in CANopenNode * Object dictionary in CANopenNode is implemented in CO_OD.h and CO_OD.c files. * These files are application specific and must be generated by Object * dictionary editor (application is included by the stack). * * CO_OD.h and CO_OD.c files include: * - Structure definitions for records. * - Global declaration and initialization of all variables, arrays and records * mapped to Object dictionary. Variables are distributed in multiple objects, * depending on memory location. This eases storage to different memories in * microcontroller, like eeprom or flash. * - Constant array of multiple Object dictionary entries of type * CO_OD_entry_t. If object type is record, then entry includes additional * constant array with members of type CO_OD_entryRecord_t. Each OD entry * includes information: index, maxSubIndex, #CO_SDO_OD_attributes_t, data size and * pointer to variable. * * * Function CO_SDO_init() initializes object CO_SDO_t, which includes SDO * server and Object dictionary. * * Application doesn't need to know anything about the Object dictionary. It can * use variables specified in CO_OD.h file directly. If it needs more control * over the CANopen communication with the variables, it can configure additional * functionality with function CO_OD_configure(). Additional functionality * include: @ref CO_SDO_OD_function and #CO_SDO_OD_flags_t. * * Interface to Object dictionary is provided by following functions: CO_OD_find() * finds OD entry by index, CO_OD_getLength() returns length of variable, * CO_OD_getAttribute returns attribute and CO_OD_getDataPointer() returns pointer * to data. These functions are used by SDO server and by PDO configuration. They * can also be used to access the OD by index like this. * * \code{.c} * index = CO_OD_find(CO->SDO[0], OD_H1001_ERR_REG); * if (index == 0xffff) { * return; * } * length = CO_OD_getLength(CO->SDO[0], index, 1); * if (length != sizeof(new_data)) { * return; * } * * p = CO_OD_getDataPointer(CO->SDO[0], index, 1); * if (p == NULL) { * return; * } * CO_LOCK_OD(); * *p = new_data; * CO_UNLOCK_OD(); * \endcode * * Be aware that accessing the OD directly using CO_OD.h files is more CPU * efficient as CO_OD_find() has to do a search everytime it is called. * */ /** * @defgroup CO_SDO_OD_function Object Dictionary function * * Optional application specific function, which may manipulate data downloaded * or uploaded via SDO. * * Object dictionary function is external function defined by application or * by other stack files. It may be registered for specific Object dictionary * entry (with specific index). If it is registered, it is called (through * function pointer) from SDO server. It may verify and manipulate data during * SDO transfer. Object dictionary function can be registered by function * CO_OD_configure(). * * ####SDO download (writing to Object dictionary) * After SDO client transfers data to the server, data are stored in internal * buffer. If data contains multibyte variable and processor is big endian, * then data bytes are swapped. Object dictionary function is called if * registered. Data may be verified and manipulated inside that function. After * function exits, data are copied to location as specified in CO_OD_entry_t. * * ####SDO upload (reading from Object dictionary) * Before start of SDO upload, data are read from Object dictionary into * internal buffer. If necessary, bytes are swapped. * Object dictionary function is called if registered. Data may be * manipulated inside that function. After function exits, data are * transferred via SDO server. * * ####Domain data type * If data type is domain, then length is not specified by Object dictionary. * In that case Object dictionary function must be used. In case of * download it must store the data in own location. In case of upload it must * write the data (maximum size is specified by length) into data buffer and * specify actual length. With domain data type it is possible to transfer * data, which are longer than #CO_CONFIG_SDO_BUFFER_SIZE. In that case * Object dictionary function is called multiple times between SDO transfer. * * ####Parameter to function: * ODF_arg - Pointer to CO_ODF_arg_t object filled before function call. * * ####Return from function: * - 0: Data transfer is successful * - Different than 0: Failure. See #CO_SDO_abortCode_t. */ /** * Size of fifo queue for SDO received messages. * * If block transfers are used size of fifo queue should be more that 1 message * to avoid possible drops in consecutive SDO block upload transfers. * To increase performance, value can be set to 1 if block transfers are not used * * Min value is 1. */ #ifndef CO_SDO_RX_DATA_SIZE #define CO_SDO_RX_DATA_SIZE 2 #endif /** * Object Dictionary attributes. Bit masks for attribute in CO_OD_entry_t. */ typedef enum{ CO_ODA_MEM_ROM = 0x0001U, /**< Variable is located in ROM memory */ CO_ODA_MEM_RAM = 0x0002U, /**< Variable is located in RAM memory */ CO_ODA_MEM_EEPROM = 0x0003U, /**< Variable is located in EEPROM memory */ CO_ODA_READABLE = 0x0004U, /**< SDO server may read from the variable */ CO_ODA_WRITEABLE = 0x0008U, /**< SDO server may write to the variable */ CO_ODA_RPDO_MAPABLE = 0x0010U, /**< Variable is mappable for RPDO */ CO_ODA_TPDO_MAPABLE = 0x0020U, /**< Variable is mappable for TPDO */ CO_ODA_TPDO_DETECT_COS = 0x0040U, /**< If variable is mapped to any PDO, then PDO is automatically send, if variable changes its value */ CO_ODA_MB_VALUE = 0x0080U /**< True when variable is a multibyte value */ }CO_SDO_OD_attributes_t; /** * Common DS301 object dictionary entries. */ typedef enum{ OD_H1000_DEV_TYPE = 0x1000U,/**< Device type */ OD_H1001_ERR_REG = 0x1001U,/**< Error register */ OD_H1002_MANUF_STATUS_REG = 0x1002U,/**< Manufacturer status register */ OD_H1003_PREDEF_ERR_FIELD = 0x1003U,/**< Predefined error field */ OD_H1004_RSV = 0x1004U,/**< Reserved */ OD_H1005_COBID_SYNC = 0x1005U,/**< Sync message cob-id */ OD_H1006_COMM_CYCL_PERIOD = 0x1006U,/**< Communication cycle period */ OD_H1007_SYNC_WINDOW_LEN = 0x1007U,/**< Sync windows length */ OD_H1008_MANUF_DEV_NAME = 0x1008U,/**< Manufacturer device name */ OD_H1009_MANUF_HW_VERSION = 0x1009U,/**< Manufacturer hardware version */ OD_H100A_MANUF_SW_VERSION = 0x100AU,/**< Manufacturer software version */ OD_H100B_RSV = 0x100BU,/**< Reserved */ OD_H100C_GUARD_TIME = 0x100CU,/**< Guard time */ OD_H100D_LIFETIME_FACTOR = 0x100DU,/**< Life time factor */ OD_H100E_RSV = 0x100EU,/**< Reserved */ OD_H100F_RSV = 0x100FU,/**< Reserved */ OD_H1010_STORE_PARAM_FUNC = 0x1010U,/**< Store parameter in persistent memory function */ OD_H1011_REST_PARAM_FUNC = 0x1011U,/**< Restore default parameter function */ OD_H1012_COBID_TIME = 0x1012U,/**< Timestamp message cob-id */ OD_H1013_HIGH_RES_TIMESTAMP = 0x1013U,/**< High resolution timestamp */ OD_H1014_COBID_EMERGENCY = 0x1014U,/**< Emergency message cob-id */ OD_H1015_INHIBIT_TIME_MSG = 0x1015U,/**< Inhibit time message */ OD_H1016_CONSUMER_HB_TIME = 0x1016U,/**< Consumer heartbeat time */ OD_H1017_PRODUCER_HB_TIME = 0x1017U,/**< Producer heartbeat time */ OD_H1018_IDENTITY_OBJECT = 0x1018U,/**< Identity object */ OD_H1019_SYNC_CNT_OVERFLOW = 0x1019U,/**< Sync counter overflow value */ OD_H1020_VERIFY_CONFIG = 0x1020U,/**< Verify configuration */ OD_H1021_STORE_EDS = 0x1021U,/**< Store EDS */ OD_H1022_STORE_FORMAT = 0x1022U,/**< Store format */ OD_H1023_OS_CMD = 0x1023U,/**< OS command */ OD_H1024_OS_CMD_MODE = 0x1024U,/**< OS command mode */ OD_H1025_OS_DBG_INTERFACE = 0x1025U,/**< OS debug interface */ OD_H1026_OS_PROMPT = 0x1026U,/**< OS prompt */ OD_H1027_MODULE_LIST = 0x1027U,/**< Module list */ OD_H1028_EMCY_CONSUMER = 0x1028U,/**< Emergency consumer object */ OD_H1029_ERR_BEHAVIOR = 0x1029U,/**< Error behaviour */ OD_H1200_SDO_SERVER_PARAM = 0x1200U,/**< SDO server parameters */ OD_H1280_SDO_CLIENT_PARAM = 0x1280U,/**< SDO client parameters */ OD_H1300_GFC_PARAM = 0x1300U,/**< GFC parameter */ OD_H1301_SRDO_1_PARAM = 0x1301U,/**< SRDO communication parameters */ OD_H1381_SRDO_1_MAPPING = 0x1381U,/**< SRDO mapping parameters */ OD_H13FE_SRDO_VALID = 0x13FEU,/**< SRDO valid flag */ OD_H13FF_SRDO_CHECKSUM = 0x13FFU,/**< SRDO checksum */ OD_H1400_RXPDO_1_PARAM = 0x1400U,/**< RXPDO communication parameter */ OD_H1401_RXPDO_2_PARAM = 0x1401U,/**< RXPDO communication parameter */ OD_H1402_RXPDO_3_PARAM = 0x1402U,/**< RXPDO communication parameter */ OD_H1403_RXPDO_4_PARAM = 0x1403U,/**< RXPDO communication parameter */ OD_H1600_RXPDO_1_MAPPING = 0x1600U,/**< RXPDO mapping parameters */ OD_H1601_RXPDO_2_MAPPING = 0x1601U,/**< RXPDO mapping parameters */ OD_H1602_RXPDO_3_MAPPING = 0x1602U,/**< RXPDO mapping parameters */ OD_H1603_RXPDO_4_MAPPING = 0x1603U,/**< RXPDO mapping parameters */ OD_H1800_TXPDO_1_PARAM = 0x1800U,/**< TXPDO communication parameter */ OD_H1801_TXPDO_2_PARAM = 0x1801U,/**< TXPDO communication parameter */ OD_H1802_TXPDO_3_PARAM = 0x1802U,/**< TXPDO communication parameter */ OD_H1803_TXPDO_4_PARAM = 0x1803U,/**< TXPDO communication parameter */ OD_H1A00_TXPDO_1_MAPPING = 0x1A00U,/**< TXPDO mapping parameters */ OD_H1A01_TXPDO_2_MAPPING = 0x1A01U,/**< TXPDO mapping parameters */ OD_H1A02_TXPDO_3_MAPPING = 0x1A02U,/**< TXPDO mapping parameters */ OD_H1A03_TXPDO_4_MAPPING = 0x1A03U /**< TXPDO mapping parameters */ }CO_ObjDicId_t; /** * Bit masks for flags associated with variable from @ref CO_SDO_objectDictionary. * * This additional functionality of any variable in @ref CO_SDO_objectDictionary can be * enabled by function CO_OD_configure(). Location of the flag byte can be * get from function CO_OD_getFlagsPointer(). */ typedef enum{ /** Variable was written by RPDO. Flag can be cleared by application */ CO_ODFL_RPDO_WRITTEN = 0x01U, /** Variable is mapped to TPDO */ CO_ODFL_TPDO_MAPPED = 0x02U, /** Change of state bit, initially copy of attribute from CO_OD_entry_t. If set and variable is mapped to TPDO, TPDO will be automatically send, if variable changed */ CO_ODFL_TPDO_COS_ENABLE = 0x04U, /** PDO send bit, can be set by application. If variable is mapped into TPDO, TPDO will be send and bit will be cleared. */ CO_ODFL_TPDO_SEND = 0x08U, /** Variable was accessed by SDO download */ CO_ODFL_SDO_DOWNLOADED = 0x10U, /** Variable was accessed by SDO upload */ CO_ODFL_SDO_UPLOADED = 0x20U, /** Reserved */ CO_ODFL_BIT_6 = 0x40U, /** Reserved */ CO_ODFL_BIT_7 = 0x80U }CO_SDO_OD_flags_t; /** * Return values from SDO server or client functions. */ typedef enum { /** Data buffer is full. * SDO client: data must be read before next upload cycle begins. */ CO_SDO_RT_uploadDataBufferFull = 5, /** CAN transmit buffer is full. Waiting. */ CO_SDO_RT_transmittBufferFull = 4, /** Block download is in progress. Sending train of messages. */ CO_SDO_RT_blockDownldInProgress = 3, /** Block upload is in progress. Receiving train of messages. * SDO client: Data must not be read in this state. */ CO_SDO_RT_blockUploadInProgress = 2, /** Waiting server or client response */ CO_SDO_RT_waitingResponse = 1, /** Success, end of communication. SDO client: uploaded data must be read.*/ CO_SDO_RT_ok_communicationEnd = 0, /** Error in arguments */ CO_SDO_RT_wrongArguments = -2, /** Communication ended with client abort */ CO_SDO_RT_endedWithClientAbort = -9, /** Communication ended with server abort */ CO_SDO_RT_endedWithServerAbort = -10, } CO_SDO_return_t; /** * Object for one entry with specific index in @ref CO_SDO_objectDictionary. */ typedef struct { /** The index of Object from 0x1000 to 0xFFFF */ uint16_t index; /** Number of (sub-objects - 1). If Object Type is variable, then maxSubIndex is 0, otherwise maxSubIndex is equal or greater than 1. */ uint8_t maxSubIndex; /** If Object Type is record, attribute is set to zero. Attribute for each member is then set in special array with members of type CO_OD_entryRecord_t. If Object Type is Array, attribute is common for all array members. See #CO_SDO_OD_attributes_t. */ uint16_t attribute; /** If Object Type is Variable, length is the length of variable in bytes. If Object Type is Array, length is the length of one array member. If Object Type is Record, length is zero. Length for each member is set in special array with members of type CO_OD_entryRecord_t. If Object Type is Domain, length is zero. Length is specified by application in @ref CO_SDO_OD_function. */ uint16_t length; /** If Object Type is Variable, pData is pointer to data. If Object Type is Array, pData is pointer to data. Data doesn't include Sub-Object 0. If object type is Record, pData is pointer to special array with members of type CO_OD_entryRecord_t. If object type is Domain, pData is null. */ void *pData; }CO_OD_entry_t; /** * Object for record type entry in @ref CO_SDO_objectDictionary. * * See CO_OD_entry_t. */ typedef struct{ /** Pointer to data. If object type is Domain, pData is null */ void *pData; /** See #CO_SDO_OD_attributes_t */ uint16_t attribute; /** Length of variable in bytes. If object type is Domain, length is zero */ uint16_t length; }CO_OD_entryRecord_t; /** * Object contains all information about the object being transferred by SDO server. * * Object is used as an argument to @ref CO_SDO_OD_function. It is also * part of the CO_SDO_t object. */ typedef struct{ /** Informative parameter. It may point to object, which is connected with this OD entry. It can be used inside @ref CO_SDO_OD_function, ONLY if it was registered by CO_OD_configure() function before. */ void *object; /** SDO data buffer contains data, which are exchanged in SDO transfer. @ref CO_SDO_OD_function may verify or manipulate that data before (after) they are written to (read from) Object dictionary. Data have the same endianes as processor. Pointer must NOT be changed. (Data up to length can be changed.) */ uint8_t *data; /** Pointer to location in object dictionary, where data are stored. (informative reference to old data, read only). Data have the same endianes as processor. If data type is Domain, this variable is null. */ const void *ODdataStorage; /** Length of data in the above buffer. Read only, except for domain. If data type is domain see @ref CO_SDO_OD_function for special rules by upload. */ uint16_t dataLength; /** Attribute of object in Object dictionary (informative, must NOT be changed). */ uint16_t attribute; /** Pointer to the #CO_SDO_OD_flags_t byte. */ uint8_t *pFlags; /** Index of object in Object dictionary (informative, must NOT be changed). */ uint16_t index; /** Subindex of object in Object dictionary (informative, must NOT be changed). */ uint8_t subIndex; /** True, if SDO upload is in progress, false if SDO download is in progress. */ bool_t reading; /** Used by domain data type. Indicates the first segment. Variable is informative. */ bool_t firstSegment; /** Used by domain data type. If false by download, then application will receive more segments during SDO communication cycle. If uploading, application may set variable to false, so SDO server will call @ref CO_SDO_OD_function again for filling the next data. */ bool_t lastSegment; /** Used by domain data type. By upload @ref CO_SDO_OD_function may write total data length, so this information will be send in SDO upload initiate phase. It is not necessary to specify this variable. By download this variable contains total data size, if size is indicated in SDO download initiate phase */ uint32_t dataLengthTotal; /** Used by domain data type. In case of multiple segments, this indicates the offset into the buffer this segment starts at. */ uint32_t offset; }CO_ODF_arg_t; /** * Object is used as array inside CO_SDO_t, parallel to @ref CO_SDO_objectDictionary. * * Object is generated by function CO_OD_configure(). It is then used as * extension to Object dictionary entry at specific index. */ typedef struct{ /** Pointer to @ref CO_SDO_OD_function */ CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg); /** Pointer to object, which will be passed to @ref CO_SDO_OD_function */ void *object; /** Pointer to #CO_SDO_OD_flags_t. If object type is array or record, this variable points to array with length equal to number of subindexes. */ uint8_t *flags; }CO_OD_extension_t; /** * SDO server object. */ typedef struct{ /** FIFO queue of the received message 8 data bytes each */ uint8_t CANrxData[CO_SDO_RX_DATA_SIZE][8]; /** SDO data buffer of size #CO_CONFIG_SDO_BUFFER_SIZE. */ uint8_t databuffer[CO_CONFIG_SDO_BUFFER_SIZE]; /** Internal flag indicates, that this object has own OD */ bool_t ownOD; /** Pointer to the @ref CO_SDO_objectDictionary (array) */ const CO_OD_entry_t *OD; /** Size of the @ref CO_SDO_objectDictionary */ uint16_t ODSize; /** Pointer to array of CO_OD_extension_t objects. Size of the array is equal to ODSize. */ CO_OD_extension_t *ODExtensions; /** Offset in buffer of next data segment being read/written */ uint16_t bufferOffset; /** Sequence number of OD entry as returned from CO_OD_find() */ uint16_t entryNo; /** CO_ODF_arg_t object with additional variables. Reference to this object is passed to @ref CO_SDO_OD_function */ CO_ODF_arg_t ODF_arg; /** From CO_SDO_init() */ uint8_t nodeId; /** Current internal state of the SDO server state machine #CO_SDO_state_t */ CO_SDO_state_t state; /** Toggle bit in segmented transfer or block sequence in block transfer */ uint8_t sequence; /** Maximum timeout time between request and response in microseconds. */ uint32_t SDOtimeoutTime_us; /** Timeout timer for SDO communication */ uint32_t timeoutTimer; /** Number of segments per block with 1 <= blksize <= 127 */ uint8_t blksize; /** True, if CRC calculation by block transfer is enabled */ bool_t crcEnabled; /** Calculated CRC code */ uint16_t crc; /** Length of data in the last segment in block upload */ uint8_t lastLen; /** Indication timeout in sub-block transfer */ bool_t timeoutSubblockDownolad; /** Indication end of block transfer */ bool_t endOfTransfer; /** Variables indicates, if new SDO message received from CAN bus */ volatile void *CANrxNew[CO_SDO_RX_DATA_SIZE]; /** Index of CANrxData for new received SDO message */ uint8_t CANrxRcv; /** Index of CANrxData SDO message to processed */ uint8_t CANrxProc; /** Number of new SDO messages in CANrxData to process */ uint8_t CANrxSize; #if ((CO_CONFIG_SDO) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN /** From CO_SDO_initCallbackPre() or NULL */ void (*pFunctSignalPre)(void *object); /** From CO_SDO_initCallbackPre() or NULL */ void *functSignalObjectPre; #endif /** From CO_SDO_init() */ CO_CANmodule_t *CANdevTx; /** CAN transmit buffer inside CANdev for CAN tx message */ CO_CANtx_t *CANtxBuff; }CO_SDO_t; /** * Copy 2 data bytes from source to destination. Swap bytes if * microcontroller is big-endian. * * @param dest Destination location. * @param src Source location. */ #ifdef CO_LITTLE_ENDIAN #define CO_memcpySwap2(dest, src) memcpy(dest, src, 2) #endif #if defined CO_BIG_ENDIAN || defined CO_DOXYGEN static inline void CO_memcpySwap2(void* dest, const void* src){ char *cdest; char *csrc; cdest = (char *) dest; csrc = (char *) src; cdest[0] = csrc[1]; cdest[1] = csrc[0]; } #endif /** * Copy 4 data bytes from source to destination. Swap bytes if * microcontroller is big-endian. * * @param dest Destination location. * @param src Source location. */ #ifdef CO_LITTLE_ENDIAN #define CO_memcpySwap4(dest, src) memcpy(dest, src, 4) #endif #if defined CO_BIG_ENDIAN || defined CO_DOXYGEN static inline void CO_memcpySwap4(void* dest, const void* src){ char *cdest; char *csrc; cdest = (char *) dest; csrc = (char *) src; cdest[0] = csrc[3]; cdest[1] = csrc[2]; cdest[2] = csrc[1]; cdest[3] = csrc[0]; } #endif /** * Copy 8 data bytes from source to destination. Swap bytes if * microcontroller is big-endian. * * @param dest Destination location. * @param src Source location. */ #ifdef CO_LITTLE_ENDIAN #define CO_memcpySwap8(dest, src) memcpy(dest, src, 8) #endif #if defined CO_BIG_ENDIAN || defined CO_DOXYGEN static inline void CO_memcpySwap8(void* dest, const void* src){ char *cdest; char *csrc; cdest = (char *) dest; csrc = (char *) src; cdest[0] = csrc[7]; cdest[1] = csrc[6]; cdest[2] = csrc[5]; cdest[3] = csrc[4]; cdest[4] = csrc[3]; cdest[5] = csrc[2]; cdest[6] = csrc[1]; cdest[7] = csrc[0]; } #endif /** * Initialize SDO object. * * Function must be called in the communication reset section. * * @param SDO This object will be initialized. * @param COB_IDClientToServer COB ID for client to server for this SDO object. * @param COB_IDServerToClient COB ID for server to client for this SDO object. * @param ObjDictIndex_SDOServerParameter Index in Object dictionary. * @param parentSDO Pointer to SDO object, which contains object dictionary and * its extension. For first (default) SDO object this argument must be NULL. * If this argument is specified, then OD, ODSize and ODExtensions arguments * are ignored. * @param OD Pointer to @ref CO_SDO_objectDictionary array defined externally. * @param ODSize Size of the above array. * @param ODExtensions Pointer to the externally defined array of the same size * as ODSize. * @param nodeId CANopen Node ID of this device. * @param SDOtimeoutTime_ms Timeout time for SDO communication in milliseconds. * @param CANdevRx CAN device for SDO server reception. * @param CANdevRxIdx Index of receive buffer in the above CAN device. * @param CANdevTx CAN device for SDO server transmission. * @param CANdevTxIdx Index of transmit buffer in the above CAN device. * * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. */ CO_ReturnError_t CO_SDO_init( CO_SDO_t *SDO, uint32_t COB_IDClientToServer, uint32_t COB_IDServerToClient, uint16_t ObjDictIndex_SDOServerParameter, CO_SDO_t *parentSDO, const CO_OD_entry_t OD[], uint16_t ODSize, CO_OD_extension_t ODExtensions[], uint8_t nodeId, uint16_t SDOtimeoutTime_ms, CO_CANmodule_t *CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t *CANdevTx, uint16_t CANdevTxIdx); #if ((CO_CONFIG_SDO) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN /** * Initialize SDOrx callback function. * * Function initializes optional callback function, which should immediately * start processing of CO_SDO_process() function. * Callback is called after SDOserver message is received from the CAN bus or * when new call without delay is necessary (SDO block transfer is in progress). * * @param SDO This object. * @param object Pointer to object, which will be passed to pFunctSignalPre(). Can be NULL * @param pFunctSignalPre Pointer to the callback function. Not called if NULL. */ void CO_SDO_initCallbackPre( CO_SDO_t *SDO, void *object, void (*pFunctSignalPre)(void *object)); #endif /** * Process SDO communication. * * Function must be called cyclically. * * @param SDO This object. * @param NMTisPreOrOperational Different than zero, if #CO_NMT_internalState_t is * NMT_PRE_OPERATIONAL or NMT_OPERATIONAL. * @param timeDifference_us Time difference from previous function call in [microseconds]. * @param [out] timerNext_us info to OS - see CO_process(). * * @return 0: SDO server is idle. * @return 1: SDO server is in transfer state. * @return -1: SDO abort just occurred. */ int8_t CO_SDO_process( CO_SDO_t *SDO, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, uint32_t *timerNext_us); /** * Configure additional functionality to one @ref CO_SDO_objectDictionary entry. * * Additional functionality include: @ref CO_SDO_OD_function and * #CO_SDO_OD_flags_t. It is optional feature and can be used on any object in * Object dictionary. If OD entry does not exist, function returns silently. * * @param SDO This object. * @param index Index of object in the Object dictionary. * @param pODFunc Pointer to @ref CO_SDO_OD_function, specified by application. * If NULL, @ref CO_SDO_OD_function will not be used on this object. * @param object Pointer to object, which will be passed to @ref CO_SDO_OD_function. * @param flags Pointer to array of #CO_SDO_OD_flags_t defined externally. If * zero, #CO_SDO_OD_flags_t will not be used on this OD entry. * @param flagsSize Size of the above array. It must be equal to number * of sub-objects in object dictionary entry. Otherwise #CO_SDO_OD_flags_t will * not be used on this OD entry. */ void CO_OD_configure( CO_SDO_t *SDO, uint16_t index, CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg), void *object, uint8_t *flags, uint8_t flagsSize); /** * Find object with specific index in Object dictionary. * * @param SDO This object. * @param index Index of the object in Object dictionary. * * @return Sequence number of the @ref CO_SDO_objectDictionary entry, 0xFFFF if not found. */ uint16_t CO_OD_find(CO_SDO_t *SDO, uint16_t index); /** * Get length of the given object with specific subIndex. * * @param SDO This object. * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). * @param subIndex Sub-index of the object in Object dictionary. * * @return Data length of the variable. */ uint16_t CO_OD_getLength(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); /** * Get attribute of the given object with specific subIndex. See #CO_SDO_OD_attributes_t. * * If Object Type is array and subIndex is zero, function always returns * 'read-only' attribute. An exception to this rule is ID1003 (Error field). * However, this is supposed to be only written by network. * * @param SDO This object. * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). * @param subIndex Sub-index of the object in Object dictionary. * * @return Attribute of the variable. */ uint16_t CO_OD_getAttribute(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); /** * Get pointer to data of the given object with specific subIndex. * * If Object Type is array and subIndex is zero, function returns pointer to * object->maxSubIndex variable. * * @param SDO This object. * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). * @param subIndex Sub-index of the object in Object dictionary. * * @return Pointer to the variable in @ref CO_SDO_objectDictionary. */ void* CO_OD_getDataPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); /** * Get pointer to the #CO_SDO_OD_flags_t byte of the given object with * specific subIndex. * * @param SDO This object. * @param entryNo Sequence number of OD entry as returned from CO_OD_find(). * @param subIndex Sub-index of the object in Object dictionary. * * @return Pointer to the #CO_SDO_OD_flags_t of the variable. */ uint8_t* CO_OD_getFlagsPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex); /** * Initialize SDO transfer. * * Find object in OD, verify, fill ODF_arg s. * * @param SDO This object. * @param index Index of the object in Object dictionary. * @param subIndex subIndex of the object in Object dictionary. * * @return 0 on success, otherwise #CO_SDO_abortCode_t. */ uint32_t CO_SDO_initTransfer(CO_SDO_t *SDO, uint16_t index, uint8_t subIndex); /** * Read data from @ref CO_SDO_objectDictionary to internal buffer. * * ODF_arg s must be initialized before with CO_SDO_initTransfer(). * @ref CO_SDO_OD_function is called if configured. * * @param SDO This object. * @param SDOBufferSize Total size of the SDO buffer. * * @return 0 on success, otherwise #CO_SDO_abortCode_t. */ uint32_t CO_SDO_readOD(CO_SDO_t *SDO, uint16_t SDOBufferSize); /** * Write data from internal buffer to @ref CO_SDO_objectDictionary. * * ODF_arg s must be initialized before with CO_SDO_initTransfer(). * @ref CO_SDO_OD_function is called if configured. * * @param SDO This object. * @param length Length of data (received from network) to write. * * @return 0 on success, otherwise #CO_SDO_abortCode_t. */ uint32_t CO_SDO_writeOD(CO_SDO_t *SDO, uint16_t length); #ifdef __cplusplus } #endif /*__cplusplus*/ /** @} */ #endif