Added 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. Added CO_fifo.h/c for fifo data buffer, used with rewritten SDO client, etc SDO client is rewritten. Now includes r/w fifo interface to transfer data. doc/gettingStarted.md is updated.
This commit is contained in:
parent
61fc785fab
commit
b5bfeec42a
26 changed files with 5187 additions and 1502 deletions
|
|
@ -27,6 +27,9 @@
|
|||
#ifndef CO_NMT_HEARTBEAT_H
|
||||
#define CO_NMT_HEARTBEAT_H
|
||||
|
||||
#include "301/CO_SDOserver.h"
|
||||
#include "301/CO_Emergency.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
|
|||
2081
301/CO_SDOclient.c
2081
301/CO_SDOclient.c
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@
|
|||
* @ingroup CO_SDOclient
|
||||
* @author Janez Paternoster
|
||||
* @author Matej Severkar
|
||||
* @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>.
|
||||
|
|
@ -28,6 +28,14 @@
|
|||
#ifndef CO_SDO_CLIENT_H
|
||||
#define CO_SDO_CLIENT_H
|
||||
|
||||
#include "301/CO_driver.h"
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL) || defined CO_DOXYGEN
|
||||
#include "301/CO_SDOserver.h"
|
||||
#endif
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED) || defined CO_DOXYGEN
|
||||
#include "301/CO_fifo.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -46,16 +54,19 @@ extern "C" {
|
|||
/**
|
||||
* Return values of SDO client functions.
|
||||
*/
|
||||
typedef enum{
|
||||
/** Transmit buffer is full. Waiting */
|
||||
typedef enum {
|
||||
/** Data buffer is full, data must be read before next upload cycle begins*/
|
||||
CO_SDOcli_uploadDataBufferFull = 5,
|
||||
/** CAN transmit buffer is full. Waiting. */
|
||||
CO_SDOcli_transmittBufferFull = 4,
|
||||
/** Block download is in progress. Sending train of messages */
|
||||
/** Block download is in progress. Sending train of messages. */
|
||||
CO_SDOcli_blockDownldInProgress = 3,
|
||||
/** Block upload in progress. Receiving train of messages */
|
||||
/** Block upload is in progress. Receiving train of messages. Data must not
|
||||
* be read in this state. */
|
||||
CO_SDOcli_blockUploadInProgress = 2,
|
||||
/** Waiting server response */
|
||||
CO_SDOcli_waitingServerResponse = 1,
|
||||
/** Success, end of communication */
|
||||
/** Success, end of communication. Uploaded data must be read. */
|
||||
CO_SDOcli_ok_communicationEnd = 0,
|
||||
/** Error in arguments */
|
||||
CO_SDOcli_wrongArguments = -2,
|
||||
|
|
@ -63,16 +74,14 @@ typedef enum{
|
|||
CO_SDOcli_endedWithClientAbort = -9,
|
||||
/** Communication ended with server abort */
|
||||
CO_SDOcli_endedWithServerAbort = -10,
|
||||
/** Communication ended with timeout */
|
||||
CO_SDOcli_endedWithTimeout = -11
|
||||
}CO_SDOclient_return_t;
|
||||
} CO_SDOclient_return_t;
|
||||
|
||||
|
||||
/**
|
||||
* SDO Client Parameter. The same as record from Object dictionary
|
||||
* (index 0x1280+).
|
||||
*/
|
||||
typedef struct{
|
||||
typedef struct {
|
||||
/** Equal to 3 */
|
||||
uint8_t maxSubIndex;
|
||||
/** Communication object identifier for client transmission.
|
||||
|
|
@ -89,88 +98,89 @@ typedef struct{
|
|||
uint32_t COB_IDServerToClient;
|
||||
/** Node-ID of the SDO server */
|
||||
uint8_t nodeIDOfTheSDOServer;
|
||||
}CO_SDOclientPar_t;
|
||||
} CO_SDOclientPar_t;
|
||||
|
||||
|
||||
/**
|
||||
* SDO client object
|
||||
*/
|
||||
typedef struct{
|
||||
typedef struct {
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL) || defined CO_DOXYGEN
|
||||
/** From CO_SDOclient_init() */
|
||||
CO_SDOclientPar_t *SDOClientPar;
|
||||
/** From CO_SDOclient_init() */
|
||||
CO_SDO_t *SDO;
|
||||
/** Internal state of the SDO client */
|
||||
uint8_t state;
|
||||
/** Pointer to data buffer supplied by user */
|
||||
uint8_t *buffer;
|
||||
/** By download application indicates data size in buffer.
|
||||
By upload application indicates buffer size */
|
||||
uint32_t bufferSize;
|
||||
/** Offset in buffer of next data segment being read/written */
|
||||
uint32_t bufferOffset;
|
||||
/** Acknowledgement */
|
||||
uint32_t bufferOffsetACK;
|
||||
/** data length to be uploaded in block transfer */
|
||||
uint32_t dataSize;
|
||||
/** Data length transferred in block transfer */
|
||||
uint32_t dataSizeTransfered;
|
||||
/** Maximum timeout time between request and response in microseconds. */
|
||||
uint32_t SDOtimeoutTime_us;
|
||||
/** Half of maximum timeout time between request and response. */
|
||||
uint32_t SDOtimeoutTimeHalf_us;
|
||||
/** Timeout timer for SDO communication */
|
||||
uint32_t timeoutTimer;
|
||||
/** Timeout timer for SDO block transfer */
|
||||
uint32_t timeoutTimerBLOCK;
|
||||
/** Index of current object in Object Dictionary */
|
||||
uint16_t index;
|
||||
/** Subindex of current object in Object Dictionary */
|
||||
uint8_t subIndex;
|
||||
/** From CO_SDOclient_init() */
|
||||
CO_CANmodule_t *CANdevRx;
|
||||
/** From CO_SDOclient_init() */
|
||||
uint16_t CANdevRxIdx;
|
||||
/** Indicates, if new SDO message received from CAN bus.
|
||||
It is not cleared, until received message is completely processed. */
|
||||
volatile void *CANrxNew;
|
||||
/** 8 data bytes of the received message */
|
||||
uint8_t CANrxData[8];
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
|
||||
/** From CO_SDOclient_initCallbackPre() or NULL */
|
||||
void (*pFunctSignal)(void *object);
|
||||
/** From CO_SDOclient_initCallbackPre() or NULL */
|
||||
void *functSignalObject;
|
||||
CO_SDO_t *SDO;
|
||||
#endif
|
||||
/** From CO_SDOclient_init() */
|
||||
CO_CANmodule_t *CANdevTx;
|
||||
/** CAN transmit buffer inside CANdevTx for CAN tx message */
|
||||
CO_CANtx_t *CANtxBuff;
|
||||
CO_SDOclientPar_t *SDOClientPar;
|
||||
/** From CO_SDOclient_init() */
|
||||
uint16_t CANdevTxIdx;
|
||||
/** Toggle bit toggled with each subsequent in segmented transfer */
|
||||
uint8_t toggle;
|
||||
/** Server threshold for switch back to segmented transfer, if data size is
|
||||
* small. Set in CO_SDOclient_init(). Can be changed by application.
|
||||
* 0 Disables switching. */
|
||||
uint8_t pst;
|
||||
/** Maximum number of segments in one block. Set in CO_SDOclient_init(). Can
|
||||
be changed by application to 2 .. 127. */
|
||||
uint8_t block_size_max;
|
||||
/** Last sector number */
|
||||
uint8_t block_seqno;
|
||||
/** Block size in current transfer */
|
||||
uint8_t block_blksize;
|
||||
/** Number of bytes in last segment that do not contain data */
|
||||
uint8_t block_noData;
|
||||
/** Server CRC support in block transfer */
|
||||
uint8_t crcEnabled;
|
||||
CO_CANmodule_t *CANdevRx;
|
||||
/** From CO_SDOclient_init() */
|
||||
uint16_t CANdevRxIdx;
|
||||
/** From CO_SDOclient_init() */
|
||||
CO_CANmodule_t *CANdevTx;
|
||||
/** From CO_SDOclient_init() */
|
||||
uint16_t CANdevTxIdx;
|
||||
/** CAN transmit buffer inside CANdevTx for CAN tx message */
|
||||
CO_CANtx_t *CANtxBuff;
|
||||
/** Index of current object in Object Dictionary */
|
||||
uint16_t index;
|
||||
/** Subindex of current object in Object Dictionary */
|
||||
uint8_t subIndex;
|
||||
/* If true, then data transfer is finished */
|
||||
bool_t finished;
|
||||
/** Size of data, which will be transferred. It is optionally indicated by
|
||||
* client in case of download or by server in case of upload. */
|
||||
size_t sizeInd;
|
||||
/** Size of data which is actually transferred. */
|
||||
size_t sizeTran;
|
||||
/** Internal state of the SDO client */
|
||||
volatile CO_SDO_state_t state;
|
||||
/** Maximum timeout time between request and response in microseconds */
|
||||
uint32_t SDOtimeoutTime_us;
|
||||
/** Timeout timer for SDO communication */
|
||||
uint32_t timeoutTimer;
|
||||
/** CO_fifo_t object for data buffer (not pointer) */
|
||||
CO_fifo_t bufFifo;
|
||||
/** Data buffer of usable size @ref CO_CONFIG_SDO_CLI_BUFFER_SIZE, used
|
||||
* inside bufFifo. Must be one byte larger for fifo usage. */
|
||||
char buf[CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1];
|
||||
/** Indicates, if new SDO message received from CAN bus. It is not cleared,
|
||||
* until received message is completely processed. */
|
||||
volatile void *CANrxNew;
|
||||
/** 8 data bytes of the received message */
|
||||
uint8_t CANrxData[8];
|
||||
/** Previous value of the COB_IDClientToServer */
|
||||
uint32_t COB_IDClientToServerPrev;
|
||||
uint32_t COB_IDClientToServerPrev;
|
||||
/** Previous value of the COB_IDServerToClient */
|
||||
uint32_t COB_IDServerToClientPrev;
|
||||
|
||||
}CO_SDOclient_t;
|
||||
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);
|
||||
/** From CO_SDOclient_initCallbackPre() or NULL */
|
||||
void *functSignalObject;
|
||||
#endif
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED) || defined CO_DOXYGEN
|
||||
/** Toggle bit toggled in each segment in segmented transfer */
|
||||
uint8_t toggle;
|
||||
#endif
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK) || defined CO_DOXYGEN
|
||||
/** Timeout time for SDO sub-block upload, half of #SDOtimeoutTime_us */
|
||||
uint32_t block_SDOtimeoutTime_us;
|
||||
/** Timeout timer for SDO sub-block upload */
|
||||
uint32_t block_timeoutTimer;
|
||||
/** Sequence number of segment in block, 1..127 */
|
||||
uint8_t block_seqno;
|
||||
/** Number of segments per block, 1..127 */
|
||||
uint8_t block_blksize;
|
||||
/** Number of bytes in last segment that do not contain data */
|
||||
uint8_t block_noData;
|
||||
/** Server CRC support in block transfer */
|
||||
bool_t block_crcEnabled;
|
||||
/** Last 7 bytes of data at block download */
|
||||
uint8_t block_dataUploadLast[7];
|
||||
/** Calculated CRC checksum */
|
||||
uint16_t block_crc;
|
||||
#endif
|
||||
} CO_SDOclient_t;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -190,14 +200,13 @@ 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,
|
||||
CO_SDO_t *SDO,
|
||||
CO_SDOclientPar_t *SDOClientPar,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx);
|
||||
CO_ReturnError_t CO_SDOclient_init(CO_SDOclient_t *SDO_C,
|
||||
void *SDO,
|
||||
CO_SDOclientPar_t *SDOClientPar,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx);
|
||||
|
||||
|
||||
#if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
|
||||
|
|
@ -215,10 +224,9 @@ CO_ReturnError_t CO_SDOclient_init(
|
|||
* be NULL.
|
||||
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
|
||||
*/
|
||||
void CO_SDOclient_initCallbackPre(
|
||||
CO_SDOclient_t *SDOclient,
|
||||
void *object,
|
||||
void (*pFunctSignal)(void *object));
|
||||
void CO_SDOclient_initCallbackPre(CO_SDOclient_t *SDOclient,
|
||||
void *object,
|
||||
void (*pFunctSignal)(void *object));
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -243,11 +251,10 @@ void CO_SDOclient_initCallbackPre(
|
|||
*
|
||||
* @return #CO_SDOclient_return_t
|
||||
*/
|
||||
CO_SDOclient_return_t CO_SDOclient_setup(
|
||||
CO_SDOclient_t *SDO_C,
|
||||
uint32_t COB_IDClientToServer,
|
||||
uint32_t COB_IDServerToClient,
|
||||
uint8_t nodeIDOfTheSDOServer);
|
||||
CO_SDOclient_return_t CO_SDOclient_setup(CO_SDOclient_t *SDO_C,
|
||||
uint32_t COB_IDClientToServer,
|
||||
uint32_t COB_IDServerToClient,
|
||||
uint8_t nodeIDOfTheSDOServer);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -260,26 +267,65 @@ CO_SDOclient_return_t CO_SDOclient_setup(
|
|||
* @param SDO_C This object.
|
||||
* @param index Index of object in object dictionary in remote node.
|
||||
* @param subIndex Subindex of object in object dictionary in remote node.
|
||||
* @param dataTx Pointer to data to be written. Data must be valid until end
|
||||
* of communication. Note that data are aligned in little-endian
|
||||
* format, because CANopen itself uses little-endian. Take care,
|
||||
* when using processors with big-endian.
|
||||
* @param dataSize Size of data in dataTx.
|
||||
* @param sizeIndicated Optionally indicate size of data to be downloaded.
|
||||
* Actual data are written with one or multiple CO_SDOclientDownloadBufWrite()
|
||||
* calls.
|
||||
* - If sizeIndicated is different than 0, then total number of data
|
||||
* written by CO_SDOclientDownloadBufWrite() will be compared against
|
||||
* sizeIndicated. Also sizeIndicated info will be passed to the server, which
|
||||
* will compare actual data size downloaded. In case of mismatch, SDO abort
|
||||
* message will be generated.
|
||||
* - If sizeIndicated is 0, then actual data size will not be verified.
|
||||
* @param SDOtimeoutTime_ms Timeout time for SDO communication in milliseconds.
|
||||
* @param blockEnable Try to initiate block transfer.
|
||||
* @param [out] timerNext_us info to OS - see CO_process(). Ignored if NULL.
|
||||
*
|
||||
* @return #CO_SDOclient_return_t
|
||||
*/
|
||||
CO_SDOclient_return_t CO_SDOclientDownloadInitiate(
|
||||
CO_SDOclient_t *SDO_C,
|
||||
uint16_t index,
|
||||
uint8_t subIndex,
|
||||
uint8_t *dataTx,
|
||||
uint32_t dataSize,
|
||||
uint16_t SDOtimeoutTime_ms,
|
||||
uint8_t blockEnable,
|
||||
uint32_t *timerNext_us);
|
||||
CO_SDOclient_return_t CO_SDOclientDownloadInitiate(CO_SDOclient_t *SDO_C,
|
||||
uint16_t index,
|
||||
uint8_t subIndex,
|
||||
size_t sizeIndicated,
|
||||
uint16_t SDOtimeoutTime_ms,
|
||||
bool_t blockEnable);
|
||||
|
||||
|
||||
/**
|
||||
* Initiate SDO download communication - update size.
|
||||
*
|
||||
* This is optional function, which updates sizeIndicated, if it was not known
|
||||
* in the CO_SDOclientDownloadInitiate() function call. This function can be
|
||||
* used after CO_SDOclientDownloadBufWrite(), but must be used before
|
||||
* CO_SDOclientDownload().
|
||||
*
|
||||
* @param SDO_C This object.
|
||||
* @param sizeIndicated Same as in CO_SDOclientDownloadInitiate().
|
||||
*/
|
||||
void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C,
|
||||
size_t sizeIndicated);
|
||||
|
||||
|
||||
/**
|
||||
* Write data into SDO client buffer
|
||||
*
|
||||
* This function copies data from buf into internal SDO client fifo buffer.
|
||||
* Function returns number of bytes successfully copied. If there is not enough
|
||||
* space in destination, not all bytes will be copied. Additional data can be
|
||||
* copied in next cycles. If there is enough space in destination and
|
||||
* sizeIndicated is different than zero, then all data must be written at once.
|
||||
*
|
||||
* This function is basically a wrapper for CO_fifo_write() function. As
|
||||
* alternative, other functions from CO_fifo can be used directly, for example
|
||||
* CO_fifo_cpyTok2U8() or similar.
|
||||
*
|
||||
* @param SDO_C This object.
|
||||
* @param buf Buffer which will be copied
|
||||
* @param count Number of bytes in buf
|
||||
*
|
||||
* @return number of bytes actually written.
|
||||
*/
|
||||
size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C,
|
||||
const char *buf,
|
||||
size_t count);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -289,20 +335,31 @@ CO_SDOclient_return_t CO_SDOclientDownloadInitiate(
|
|||
* download communication initiated with CO_SDOclientDownloadInitiate().
|
||||
* Function is non-blocking.
|
||||
*
|
||||
* If function returns #CO_SDOcli_blockDownldInProgress and OS has buffer for
|
||||
* CAN tx messages, then this function may be called multiple times within own
|
||||
* loop. This can speed-up SDO block transfer.
|
||||
*
|
||||
* @param SDO_C This object.
|
||||
* @param timeDifference_us Time difference from previous function call in
|
||||
* [microseconds].
|
||||
* @param [out] pSDOabortCode Pointer to external variable written by this
|
||||
* function in case of error in communication.
|
||||
* @param abort If true, SDO client will send abort message from SDOabortCode
|
||||
* and transmission will be aborted.
|
||||
* @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
|
||||
* @param [out] timerNext_us info to OS - see CO_process(). Ignored if NULL.
|
||||
*
|
||||
* @return #CO_SDOclient_return_t
|
||||
* @return #CO_SDOclient_return_t. If less than 0, then error occurred,
|
||||
* SDOabortCode contains reason and state becomes idle. If 0, communication
|
||||
* ends successfully and state becomes idle. If greater than 0, then
|
||||
* communication is in progress.
|
||||
*/
|
||||
CO_SDOclient_return_t CO_SDOclientDownload(
|
||||
CO_SDOclient_t *SDO_C,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *pSDOabortCode,
|
||||
uint32_t *timerNext_us);
|
||||
CO_SDOclient_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);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -315,26 +372,16 @@ CO_SDOclient_return_t CO_SDOclientDownload(
|
|||
* @param SDO_C This object.
|
||||
* @param index Index of object in object dictionary in remote node.
|
||||
* @param subIndex Subindex of object in object dictionary in remote node.
|
||||
* @param dataRx Pointer to data buffer, into which received data will be
|
||||
* written. Buffer must be valid until end of communication. Note that data are
|
||||
* aligned in little-endian format, because CANopen itself uses little-endian.
|
||||
* Take care, when using processors with big-endian.
|
||||
* @param dataRxSize Size of dataRx.
|
||||
* @param SDOtimeoutTime_ms Timeout time for SDO communication in milliseconds.
|
||||
* @param blockEnable Try to initiate block transfer.
|
||||
* @param [out] timerNext_us info to OS - see CO_process(). Ignored if NULL.
|
||||
*
|
||||
* @return #CO_SDOclient_return_t
|
||||
*/
|
||||
CO_SDOclient_return_t CO_SDOclientUploadInitiate(
|
||||
CO_SDOclient_t *SDO_C,
|
||||
uint16_t index,
|
||||
uint8_t subIndex,
|
||||
uint8_t *dataRx,
|
||||
uint32_t dataRxSize,
|
||||
uint16_t SDOtimeoutTime_ms,
|
||||
uint8_t blockEnable,
|
||||
uint32_t *timerNext_us);
|
||||
CO_SDOclient_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C,
|
||||
uint16_t index,
|
||||
uint8_t subIndex,
|
||||
uint16_t SDOtimeoutTime_ms,
|
||||
bool_t blockEnable);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -344,23 +391,58 @@ CO_SDOclient_return_t CO_SDOclientUploadInitiate(
|
|||
* upload communication initiated with CO_SDOclientUploadInitiate().
|
||||
* Function is non-blocking.
|
||||
*
|
||||
* If this function returns #CO_SDOcli_uploadDataBufferFull, then data must be
|
||||
* read from fifo buffer to make it empty. This function can then be called
|
||||
* once again immediately to speed-up block transfer. Note also, that remaining
|
||||
* data must be read after function returns #CO_SDOcli_ok_communicationEnd.
|
||||
* Data must not be read, if function returns #CO_SDOcli_blockUploadInProgress.
|
||||
*
|
||||
* @param SDO_C This object.
|
||||
* @param timeDifference_us Time difference from previous function call in
|
||||
* [microseconds].
|
||||
* @param [out] pDataSize pointer to external variable, where size of received
|
||||
* data will be written.
|
||||
* @param [out] pSDOabortCode Pointer to external variable written by this
|
||||
* function in case of error in communication.
|
||||
* @param [out] SDOabortCode In case of error in communication, SDO abort code
|
||||
* contains reason of error. Ignored if NULL.
|
||||
* @param [out] sizeIndicated If larger than 0, then SDO server has indicated
|
||||
* size of data transfer. Ignored if NULL.
|
||||
* @param [out] sizeTransferred Actual size of data transferred. Ignored if NULL
|
||||
* @param [out] timerNext_us info to OS - see CO_process(). Ignored if NULL.
|
||||
*
|
||||
* @return #CO_SDOclient_return_t
|
||||
* @return #CO_SDOclient_return_t. If less than 0, then error occurred,
|
||||
* SDOabortCode contains reason and state becomes idle. If 0, communication
|
||||
* ends successfully and state becomes idle. If greater than 0, then
|
||||
* communication is in progress.
|
||||
*/
|
||||
CO_SDOclient_return_t CO_SDOclientUpload(
|
||||
CO_SDOclient_t *SDO_C,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *pDataSize,
|
||||
uint32_t *pSDOabortCode,
|
||||
uint32_t *timerNext_us);
|
||||
CO_SDOclient_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C,
|
||||
uint32_t timeDifference_us,
|
||||
CO_SDO_abortCode_t *SDOabortCode,
|
||||
size_t *sizeIndicated,
|
||||
size_t *sizeTransferred,
|
||||
uint32_t *timerNext_us);
|
||||
|
||||
|
||||
/**
|
||||
* Read data from SDO client buffer.
|
||||
*
|
||||
* This function copies data from internal fifo buffer of SDO client into buf.
|
||||
* Function returns number of bytes successfully copied. It can be called in
|
||||
* multiple cycles, if data length is large.
|
||||
*
|
||||
* This function is basically a wrapper for CO_fifo_read() function. As
|
||||
* alternative, other functions from CO_fifo can be used directly, for example
|
||||
* CO_fifo_readU82a() or similar.
|
||||
*
|
||||
* @warning This function (or similar) must NOT be called when
|
||||
* CO_SDOclientUpload() returns #CO_SDOcli_blockUploadInProgress!
|
||||
*
|
||||
* @param SDO_C This object.
|
||||
* @param buf Buffer into which data will be copied
|
||||
* @param count Copy up to count bytes into buffer
|
||||
*
|
||||
* @return number of bytes actually read.
|
||||
*/
|
||||
size_t CO_SDOclientUploadBufRead(CO_SDOclient_t *SDO_C,
|
||||
char *buf,
|
||||
size_t count);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -82,9 +82,10 @@ typedef enum {
|
|||
* - SDO server is waiting for client request. */
|
||||
CO_SDO_ST_IDLE = 0x00U,
|
||||
/**
|
||||
* - 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 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_LOCAL_TRANSFER = 0x01U,
|
||||
/**
|
||||
* - SDO client or server may send SDO abort message in case of error:
|
||||
|
|
@ -149,7 +150,7 @@ 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. */
|
||||
* - SDO server waits for segment request. */
|
||||
CO_SDO_ST_UPLOAD_SEGMENT_REQ = 0x23U,
|
||||
/**
|
||||
* - SDO client waits for response.
|
||||
|
|
@ -255,12 +256,6 @@ CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2 = 0x43U,
|
|||
* 1..127.
|
||||
* - byte 1..7: At most 7 bytes of segment data to be uploaded. */
|
||||
CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ = 0x44U,
|
||||
/**
|
||||
* This is interim state after server finished sending sequence of segments in
|
||||
* one sub-block. This state is valid one cycle. In that state data should be
|
||||
* emptied from internal buffer. In the next cycle client will send response and
|
||||
* server will send another sequence of segments. */
|
||||
CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_DATA_RDY = 0x45U,
|
||||
/**
|
||||
* - SDO client responses:
|
||||
* - byte 0: @b 10100010 binary.
|
||||
|
|
@ -274,7 +269,7 @@ CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_DATA_RDY = 0x45U,
|
|||
* - 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 = 0x46U,
|
||||
CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP = 0x45U,
|
||||
/**
|
||||
* - SDO client waits for server request.
|
||||
* - SDO server sends SDO block upload end:
|
||||
|
|
@ -282,7 +277,7 @@ CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP = 0x46U,
|
|||
* 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 = 0x47U,
|
||||
CO_SDO_ST_UPLOAD_BLK_END_SREQ = 0x46U,
|
||||
/**
|
||||
* - SDO client responses:
|
||||
* - byte 0: @b 10100001 binary.
|
||||
|
|
@ -292,7 +287,7 @@ CO_SDO_ST_UPLOAD_BLK_END_SREQ = 0x47U,
|
|||
* with client response. Client may then start next SDO communication
|
||||
* immediately.
|
||||
*/
|
||||
CO_SDO_ST_UPLOAD_BLK_END_CRSP = 0x48U,
|
||||
CO_SDO_ST_UPLOAD_BLK_END_CRSP = 0x47U,
|
||||
|
||||
/* old state names, will be removed */
|
||||
CO_SDO_ST_DOWNLOAD_INITIATE = 0xA1U,
|
||||
|
|
@ -839,7 +834,7 @@ static inline void CO_setUint32(uint8_t data[], const uint32_t value){
|
|||
#ifdef CO_LITTLE_ENDIAN
|
||||
#define CO_memcpySwap2(dest, src) memcpy(dest, src, 2)
|
||||
#endif
|
||||
#ifdef CO_BIG_ENDIAN
|
||||
#if defined CO_BIG_ENDIAN || defined CO_DOXYGEN
|
||||
static inline void CO_memcpySwap2(void* dest, const void* src){
|
||||
char *cdest;
|
||||
char *csrc;
|
||||
|
|
@ -861,7 +856,7 @@ static inline void CO_memcpySwap2(void* dest, const void* src){
|
|||
#ifdef CO_LITTLE_ENDIAN
|
||||
#define CO_memcpySwap4(dest, src) memcpy(dest, src, 4)
|
||||
#endif
|
||||
#ifdef CO_BIG_ENDIAN
|
||||
#if defined CO_BIG_ENDIAN || defined CO_DOXYGEN
|
||||
static inline void CO_memcpySwap4(void* dest, const void* src){
|
||||
char *cdest;
|
||||
char *csrc;
|
||||
|
|
@ -885,7 +880,7 @@ static inline void CO_memcpySwap4(void* dest, const void* src){
|
|||
#ifdef CO_LITTLE_ENDIAN
|
||||
#define CO_memcpySwap8(dest, src) memcpy(dest, src, 8)
|
||||
#endif
|
||||
#ifdef CO_BIG_ENDIAN
|
||||
#if defined CO_BIG_ENDIAN || defined CO_DOXYGEN
|
||||
static inline void CO_memcpySwap8(void* dest, const void* src){
|
||||
char *cdest;
|
||||
char *csrc;
|
||||
|
|
|
|||
|
|
@ -302,24 +302,40 @@ extern "C" {
|
|||
*
|
||||
* Possible flags, can be ORed:
|
||||
* - CO_CONFIG_GTW_MULTI_NET - Enable multiple network interfaces in gateway
|
||||
* device. This functionality is currntly not implemented.
|
||||
* device. This functionality is currently not implemented.
|
||||
* - CO_CONFIG_GTW_ASCII - Enable gateway device with ASCII mapping (CiA 309-3)
|
||||
* - CO_CONFIG_GTW_ASCII_ERROR_DESC - Print error description as additional
|
||||
* comments in gateway-ascii device for SDO and gateway errors.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_GTW (CO_CONFIG_GTW_MULTI_NET | CO_CONFIG_GTW_ASCII)
|
||||
#define CO_CONFIG_GTW (CO_CONFIG_GTW_MULTI_NET | CO_CONFIG_GTW_ASCII | CO_CONFIG_GTW_ASCII_ERROR_DESC | CO_CONFIG_GTW_ASCII_PRINT_HELP)
|
||||
#endif
|
||||
#define CO_CONFIG_GTW_MULTI_NET 0x01
|
||||
#define CO_CONFIG_GTW_ASCII 0x02
|
||||
#define CO_CONFIG_GTW_ASCII_ERROR_DESC 0x04
|
||||
#define CO_CONFIG_GTW_ASCII_PRINT_HELP 0x08
|
||||
|
||||
|
||||
/**
|
||||
* Number of loops of #CO_SDOclientDownload() in case of block download
|
||||
*
|
||||
* If SDO clint has block download in progress and OS has buffer for CAN tx
|
||||
* messages, then #CO_SDOclientDownload() functionion can be called multiple
|
||||
* times within own loop (up to 127). This can speed-up SDO block transfer.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_GTW_BLOCK_DL_LOOP 1
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Size of command buffer in ASCII gateway object.
|
||||
*
|
||||
* If large amount of data is transferred (block transfer), the this should be
|
||||
* If large amount of data is transferred (block transfer), then this should be
|
||||
* increased to 1000 or more. Buffer may be refilled between the block transfer.
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_GTWA_COMM_BUF_SIZE 100
|
||||
#define CO_CONFIG_GTWA_COMM_BUF_SIZE 200
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -82,12 +82,6 @@ extern "C" {
|
|||
#define CO_CONFIG_GTW (0)
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
#ifndef CO_CONFIG_GTWA_COMM_BUF_SIZE
|
||||
#define CO_CONFIG_GTWA_COMM_BUF_SIZE 100
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_driver Driver
|
||||
* @ingroup CO_CANopen_301
|
||||
|
|
@ -100,7 +94,7 @@ extern "C" {
|
|||
* multiple threads. Reception of CAN messages is pre-processed with very fast
|
||||
* functions. Time critical objects, such as PDO or SYNC are processed in
|
||||
* real-time thread and other objects are processed in normal thread. See
|
||||
* Flowchart in [README.md](README.md) for more information.
|
||||
* Flowchart in [README.md](index.html) for more information.
|
||||
*
|
||||
* @anchor CO_obj
|
||||
* #### CANopenNode Object
|
||||
|
|
@ -164,6 +158,12 @@ extern "C" {
|
|||
*/
|
||||
/** CO_LITTLE_ENDIAN or CO_BIG_ENDIAN must be defined */
|
||||
#define CO_LITTLE_ENDIAN
|
||||
/** Macro must swap bytes, if CO_BIG_ENDIAN is defined */
|
||||
#define CO_SWAP_16(x) x
|
||||
/** Macro must swap bytes, if CO_BIG_ENDIAN is defined */
|
||||
#define CO_SWAP_32(x) x
|
||||
/** Macro must swap bytes, if CO_BIG_ENDIAN is defined */
|
||||
#define CO_SWAP_64(x) x
|
||||
/** NULL, for general usage */
|
||||
#define NULL (0)
|
||||
/** Logical true, for general use */
|
||||
|
|
@ -191,7 +191,7 @@ typedef unsigned long long int uint64_t;
|
|||
/** REAL32 in CANopen (0008h), single precision floating point value, 32-bit */
|
||||
typedef float float32_t;
|
||||
/** REAL64 in CANopen (0011h), double precision floating point value, 64-bit */
|
||||
typedef long double float64_t;
|
||||
typedef double float64_t;
|
||||
/** VISIBLE_STRING in CANopen (0009h), string of signed 8-bit values */
|
||||
typedef char char_t;
|
||||
/** OCTET_STRING in CANopen (000Ah), string of unsigned 8-bit values */
|
||||
|
|
@ -377,7 +377,7 @@ typedef struct {
|
|||
* @defgroup CO_critical_sections Critical sections
|
||||
* @{
|
||||
* CANopenNode is designed to run in different threads, as described in
|
||||
* [README.md](README.md). Threads are implemented differently in different
|
||||
* [README.md](index.html). Threads are implemented differently in different
|
||||
* systems. In microcontrollers threads are interrupts with different
|
||||
* priorities, for example. It is necessary to protect sections, where different
|
||||
* threads access to the same resource. In simple systems interrupts or
|
||||
|
|
|
|||
1159
301/CO_fifo.c
Normal file
1159
301/CO_fifo.c
Normal file
File diff suppressed because it is too large
Load diff
476
301/CO_fifo.h
Normal file
476
301/CO_fifo.h
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
/**
|
||||
* FIFO circular buffer
|
||||
*
|
||||
* @file CO_fifo.h
|
||||
* @ingroup CO_CANopen_301_fifo
|
||||
* @author 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>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* 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_FIFO_H
|
||||
#define CO_FIFO_H
|
||||
|
||||
|
||||
#include "301/CO_driver.h"
|
||||
|
||||
|
||||
#if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK
|
||||
#ifndef CO_CONFIG_FIFO_ALT_READ
|
||||
#define CO_CONFIG_FIFO_ALT_READ 1
|
||||
#endif
|
||||
#ifndef CO_CONFIG_FIFO_CRC16_CCITT
|
||||
#define CO_CONFIG_FIFO_CRC16_CCITT 1
|
||||
#endif
|
||||
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
#ifndef CO_CONFIG_FIFO_ASCII_COMMANDS
|
||||
#define CO_CONFIG_FIFO_ASCII_COMMANDS 1
|
||||
#endif
|
||||
#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_CANopen_301_fifo FIFO circular buffer
|
||||
* @ingroup CO_CANopen_301
|
||||
* @{
|
||||
*
|
||||
* FIFO is organized as circular buffer with predefined capacity. It must be
|
||||
* initialized by CO_fifo_init(). Functions are not not thread safe.
|
||||
*
|
||||
* It can be used as general purpose FIFO circular buffer for any data. Data can
|
||||
* be written by CO_fifo_write() and read by CO_fifo_read() functions.
|
||||
*
|
||||
* Buffer has additional functions for usage with CiA309-3 standard. It acts as
|
||||
* circular buffer for storing ascii commands and fetching tokens from them.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fifo object
|
||||
*/
|
||||
typedef struct {
|
||||
/** Buffer of size bufSize. Initialized by CO_fifo_init() */
|
||||
char *buf;
|
||||
/** Initialized by CO_fifo_init() */
|
||||
size_t bufSize;
|
||||
/** Location in the buffer, which will be next written. */
|
||||
size_t writePtr;
|
||||
/** Location in the buffer, which will be next read. */
|
||||
size_t readPtr;
|
||||
#if CO_CONFIG_FIFO_ALT_READ > 0 || defined CO_DOXYGEN
|
||||
/** Location in the buffer, which will be next read. */
|
||||
size_t altReadPtr;
|
||||
#endif
|
||||
#if CO_CONFIG_FIFO_ASCII_COMMANDS > 0 || defined CO_DOXYGEN
|
||||
/** helper variable, set to false in CO_fifo_reset(), used in some
|
||||
* functions. */
|
||||
bool_t started;
|
||||
/** auxiliary variable, used in some functions. */
|
||||
uint32_t aux;
|
||||
#endif
|
||||
} CO_fifo_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialize fifo object
|
||||
*
|
||||
* @param fifo This object will be initialized
|
||||
* @param buf Pointer to externally defined buffer
|
||||
* @param bufSize Size of the above buffer. Usable size of the buffer will be
|
||||
* one byte less than bufSize, it is used for operation of the circular buffer.
|
||||
*/
|
||||
void CO_fifo_init(CO_fifo_t *fifo, char *buf, size_t bufSize);
|
||||
|
||||
|
||||
/**
|
||||
* Reset fifo object, make it empty
|
||||
*
|
||||
* @param fifo This object
|
||||
*/
|
||||
static inline void CO_fifo_reset(CO_fifo_t *fifo) {
|
||||
if (fifo != NULL) {
|
||||
fifo->readPtr = 0;
|
||||
fifo->writePtr = 0;
|
||||
#if CO_CONFIG_FIFO_ASCII_COMMANDS > 0
|
||||
fifo->started = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get free buffer space in CO_fifo_t object
|
||||
*
|
||||
* @param fifo This object
|
||||
*
|
||||
* @return number of available bytes
|
||||
*/
|
||||
static inline size_t CO_fifo_getSpace(CO_fifo_t *fifo) {
|
||||
int sizeLeft = (int)fifo->readPtr - fifo->writePtr - 1;
|
||||
if (sizeLeft < 0) {
|
||||
sizeLeft += fifo->bufSize;
|
||||
}
|
||||
|
||||
return (size_t) sizeLeft;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get size of data inside CO_fifo_t buffer object
|
||||
*
|
||||
* @param fifo This object
|
||||
*
|
||||
* @return number of occupied bytes
|
||||
*/
|
||||
static inline size_t CO_fifo_getOccupied(CO_fifo_t *fifo) {
|
||||
int sizeOccupied = (int)fifo->writePtr - fifo->readPtr;
|
||||
if (sizeOccupied < 0) {
|
||||
sizeOccupied += fifo->bufSize;
|
||||
}
|
||||
|
||||
return (size_t) sizeOccupied;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Put one character into CO_fifo_t buffer object
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param c Character to put
|
||||
*
|
||||
* @return true, if write was successful (enough space in fifo buffer)
|
||||
*/
|
||||
static inline bool_t CO_fifo_putc(CO_fifo_t *fifo, const char c) {
|
||||
if (fifo != NULL) {
|
||||
size_t writePtrNext = fifo->writePtr + 1;
|
||||
if (writePtrNext != fifo->readPtr &&
|
||||
!(writePtrNext == fifo->bufSize && fifo->readPtr == 0))
|
||||
{
|
||||
fifo->buf[fifo->writePtr] = c;
|
||||
fifo->writePtr = (writePtrNext == fifo->bufSize) ? 0 : writePtrNext;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get one character from CO_fifo_t buffer object
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param buf Buffer of length one byte, where character will be copied
|
||||
*
|
||||
* @return true, if read was successful (non-empty fifo buffer)
|
||||
*/
|
||||
static inline bool_t CO_fifo_getc(CO_fifo_t *fifo, char *buf) {
|
||||
if (fifo != NULL && buf != NULL && fifo->readPtr != fifo->writePtr) {
|
||||
*buf = fifo->buf[fifo->readPtr];
|
||||
if (++fifo->readPtr == fifo->bufSize) {
|
||||
fifo->readPtr = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write data into CO_fifo_t object.
|
||||
*
|
||||
* This function copies data from buf into internal buffer of CO_fifo_t
|
||||
* object. Function returns number of bytes successfully copied.
|
||||
* If there is not enough space in destination, not all bytes will be copied.
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param buf Buffer which will be copied
|
||||
* @param count Number of bytes in buf
|
||||
* @param [in,out] crc Externally defined variable for CRC checksum, ignored if
|
||||
* NULL
|
||||
*
|
||||
* @return number of bytes actually written.
|
||||
*/
|
||||
size_t CO_fifo_write(CO_fifo_t *fifo,
|
||||
const char *buf,
|
||||
size_t count,
|
||||
uint16_t *crc);
|
||||
|
||||
|
||||
/**
|
||||
* Read data from CO_fifo_t object.
|
||||
*
|
||||
* This function copies data from internal buffer of CO_fifo_t object into
|
||||
* buf. Function returns number of bytes successfully copied. Function also
|
||||
* writes true into eof argument, if command delimiter character is reached.
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param buf Buffer into which data will be copied
|
||||
* @param count Copy up to count bytes into buffer
|
||||
* @param [out] eof If different than NULL, then search for delimiter character.
|
||||
* If found, then read up to (including) that character and set *eof to true.
|
||||
* Otherwise set *eof to false.
|
||||
*
|
||||
* @return number of bytes actually read.
|
||||
*/
|
||||
size_t CO_fifo_read(CO_fifo_t *fifo, char *buf, size_t count, bool_t *eof);
|
||||
|
||||
|
||||
#if CO_CONFIG_FIFO_ALT_READ > 0 || defined CO_DOXYGEN
|
||||
/**
|
||||
* Initializes alternate read with #CO_fifo_altRead
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param offset Offset in bytes from original read pointer
|
||||
*
|
||||
* @return same as offset or lower, if there is not enough data.
|
||||
*/
|
||||
size_t CO_fifo_altBegin(CO_fifo_t *fifo, size_t offset);
|
||||
|
||||
|
||||
/**
|
||||
* Ends alternate read with #CO_fifo_altRead and calculate crc checksum
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param [in,out] crc Externally defined variable for CRC checksum, ignored if
|
||||
* NULL
|
||||
*/
|
||||
void CO_fifo_altFinish(CO_fifo_t *fifo, uint16_t *crc);
|
||||
|
||||
|
||||
/**
|
||||
* Get alternate size of remaining data, see #CO_fifo_altRead
|
||||
*
|
||||
* @param fifo This object
|
||||
*
|
||||
* @return number of occupied bytes.
|
||||
*/
|
||||
static inline size_t CO_fifo_altGetOccupied(CO_fifo_t *fifo) {
|
||||
int sizeOccupied = (int)fifo->writePtr - fifo->altReadPtr;
|
||||
if (sizeOccupied < 0) {
|
||||
sizeOccupied += fifo->bufSize;
|
||||
}
|
||||
|
||||
return (size_t) sizeOccupied;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Alternate read data from CO_fifo_t object.
|
||||
*
|
||||
* This function is similar as CO_fifo_read(), but uses alternate read pointer
|
||||
* inside circular buffer. It reads data from the buffer and data remains in it.
|
||||
* This function uses alternate read pointer and keeps original read pointer
|
||||
* unchanged. Before using this function, alternate read pointer must be
|
||||
* initialized with CO_fifo_altBegin(). CO_fifo_altFinish() sets original read
|
||||
* pointer to alternate read pointer and so empties the buffer.
|
||||
*
|
||||
* @param fifo This object
|
||||
* @param buf Buffer into which data will be copied
|
||||
* @param count Copy up to count bytes into buffer
|
||||
*
|
||||
* @return number of bytes actually read.
|
||||
*/
|
||||
size_t CO_fifo_altRead(CO_fifo_t *fifo, char *buf, size_t count);
|
||||
#endif /* CO_CONFIG_FIFO_ALT_READ > 0 */
|
||||
|
||||
|
||||
#if CO_CONFIG_FIFO_ASCII_COMMANDS > 0 || defined CO_DOXYGEN
|
||||
/**
|
||||
* Search command inside FIFO
|
||||
*
|
||||
* If there are some data inside the FIFO, then search for command delimiter.
|
||||
*
|
||||
* If command is long, then in the buffer may not be enough space for it.
|
||||
* In that case buffer is full and no delimiter is present. Function then
|
||||
* returns true and command should be processed for the starting tokens.
|
||||
* Buffer can later be refilled multiple times, until command is closed by
|
||||
* command delimiter.
|
||||
*
|
||||
* If this function returns different than 0, then buffer is usually read
|
||||
* by multiple CO_fifo_readToken() calls. If reads was successful, then
|
||||
* delimiter is reached and fifo->readPtr is set after the command. If any
|
||||
* CO_fifo_readToken() returns nonzero *err, then there is an error and command
|
||||
* should be cleared. All this procedure must be implemented inside single
|
||||
* function call.
|
||||
*
|
||||
* @param fifo This object.
|
||||
* @param clear If true, then command will be cleared from the
|
||||
* buffer. If there is no delimiter, buffer will be cleared entirely.
|
||||
*
|
||||
* @return true if command with delimiter found or buffer full.
|
||||
*/
|
||||
bool_t CO_fifo_CommSearch(CO_fifo_t *fifo, bool_t clear);
|
||||
|
||||
|
||||
/**
|
||||
* Trim spaces inside FIFO
|
||||
*
|
||||
* Function removes all non graphical characters and comments from fifo
|
||||
* buffer. It stops on first graphical character or on command delimiter (later
|
||||
* is also removed).
|
||||
*
|
||||
* @param fifo This object.
|
||||
*
|
||||
* @return true if command delimiter was found.
|
||||
*/
|
||||
bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo);
|
||||
|
||||
|
||||
/**
|
||||
* Get token from FIFO buffer
|
||||
*
|
||||
* Function search FIFO buffer for token. Token is string of only graphical
|
||||
* characters. Graphical character is any printable character except space with
|
||||
* acsii code within limits: 0x20 < code < 0x7F (see isgraph() function).
|
||||
*
|
||||
* If token is found, then copy it to the buf, if count is large enough. On
|
||||
* success also set readPtr to point to the next graphical character.
|
||||
*
|
||||
* Each token must have at least one empty space after it (space, command
|
||||
* delimiter, '\0', etc.). Delimiter must not be graphical character.
|
||||
*
|
||||
* If comment delimiter (delimComment, see #CO_fifo_init) character is found,
|
||||
* then all string till command delimiter (delimCommand, see #CO_fifo_init) will
|
||||
* be erased from the buffer.
|
||||
*
|
||||
* See also #CO_fifo_CommSearch().
|
||||
*
|
||||
* @param fifo This object.
|
||||
* @param buf Buffer into which data will be copied. Will be terminated by '\0'.
|
||||
* @param count Copy up to count bytes into buffer
|
||||
* @param [in,out] closed This is input/output variable. Not used if NULL.
|
||||
* - As output variable it is set to 1, if command delimiter (delimCommand,
|
||||
* see #CO_fifo_init) is found after the token and set to 0 otherwise.
|
||||
* - As input variable it is used for verifying error condition:
|
||||
* - *closed = 0: Set *err to true if token is empty or command delimiter
|
||||
* is found.
|
||||
* - *closed = 1: Set *err to true if token is empty or command delimiter
|
||||
* is NOT found.
|
||||
* - *closed = any other value: No checking of token size or command delimiter.
|
||||
* @param [out] err If not NULL, it is set to true if token is larger than buf
|
||||
* or in matching combination in 'closed' argument. If it is already true, then
|
||||
* function returns immediately.
|
||||
*
|
||||
* @return Number of bytes read.
|
||||
*/
|
||||
size_t CO_fifo_readToken(CO_fifo_t *fifo,
|
||||
char *buf,
|
||||
size_t count,
|
||||
char *closed,
|
||||
bool_t *err);
|
||||
|
||||
|
||||
/**
|
||||
* Read uint8_t variable from fifo and output as ascii string.
|
||||
*
|
||||
* @param fifo This object.
|
||||
* @param buf Buffer into which ascii string will be copied.
|
||||
* @param count Available count of bytes inside the buf.
|
||||
* @param end If true, then fifo contains last bytes, which has to be read.
|
||||
*
|
||||
* @return Number of ascii bytes written into buf.
|
||||
*/
|
||||
size_t CO_fifo_readU82a (CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read uint16_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readU162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read uint32_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readU322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read uint64_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readU642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read int8_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readI82a (CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read int16_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readI162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read int32_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readI322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read int64_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readI642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read float32_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readR322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read float64_t variable from fifo as ascii string, see CO_fifo_readU82a */
|
||||
size_t CO_fifo_readR642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read data from fifo and output as space separated two digit ascii string,
|
||||
* see also CO_fifo_readU82a */
|
||||
size_t CO_fifo_readHex2a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
/** Read data from fifo and output as visible string. A visible string is
|
||||
* enclosed with double quotes. If a double quote is used within the string,
|
||||
* the quotes are escaped by a second quotes, e.g. “Hello “”World””, CANopen
|
||||
* is great”. See also CO_fifo_readU82a */
|
||||
size_t CO_fifo_readVs2a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end);
|
||||
|
||||
|
||||
/**
|
||||
* Read ascii string from src fifo and copy as uint8_t variable to dest fifo.
|
||||
*
|
||||
* @param dest destination fifo buffer object.
|
||||
* @param src source fifo buffer object.
|
||||
* @param [out] status bitfield with the following bits:
|
||||
* - bit0: if 1, command delimiter is reached in src
|
||||
* - bit1: if 1, copy was partial, more data are available. If 0 and no error
|
||||
* bit is set, then all data was successfully copied.
|
||||
* - bit4: if 1, error: no valid token found
|
||||
* - bit5: if 1, error: value is not valid or out of limits
|
||||
* - bit6: if 1, error: destination buffer to small
|
||||
* - bit7: if 1, error: internal
|
||||
*
|
||||
* @return Number of bytes written into dest.
|
||||
*/
|
||||
size_t CO_fifo_cpyTok2U8 (CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to uint16_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2U16(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to uint32_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2U32(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to uint64_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2U64(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to int8_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2I8 (CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to int16_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2I16(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to int32_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2I32(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to int64_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2I64(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to float32_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2R32(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy ascii string to float64_t variable, see CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2R64(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy bytes written as two hex digits into to data. Bytes may be space
|
||||
* separated. See CO_fifo_cpyTok2U8 for parameters. */
|
||||
size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
/** Copy visible string to data. A visible string must be enclosed with double
|
||||
* quotes, if it contains space. If a double quote is used within the string,
|
||||
* the quotes are escaped by a second quotes. See CO_fifo_cpyTok2U8 */
|
||||
size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, uint8_t *status);
|
||||
|
||||
#endif /* CO_CONFIG_FIFO_ASCII_COMMANDS > 0 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
/** @} */
|
||||
#endif /* CO_FIFO_H */
|
||||
1103
309/CO_gateway_ascii.c
Normal file
1103
309/CO_gateway_ascii.c
Normal file
File diff suppressed because it is too large
Load diff
404
309/CO_gateway_ascii.h
Normal file
404
309/CO_gateway_ascii.h
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/**
|
||||
* CANopen access from other networks - ASCII mapping (CiA 309-3 DS v2.1.0)
|
||||
*
|
||||
* @file CO_gateway_ascii.h
|
||||
* @ingroup CO_CANopen_309_3
|
||||
* @author 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>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* 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_GATEWAY_ASCII_H
|
||||
#define CO_GATEWAY_ASCII_H
|
||||
|
||||
#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"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_CANopen_309_3 ASCII mapping
|
||||
* @ingroup CO_CANopen_309
|
||||
* @{
|
||||
*
|
||||
* CANopen access from other networks - ASCII mapping (CiA 309-3 DS v2.1.0)
|
||||
*
|
||||
* This module enables ascii command interface (CAN gateway), which can be used
|
||||
* for master interaction with CANopen network. Some sort of string input/output
|
||||
* stream can be used, for example serial port + terminal on microcontroller or
|
||||
* stdio in OS or sockets, etc.
|
||||
*
|
||||
* For example, one wants to read 'Heartbeat producer time' parameter (0x1017,0)
|
||||
* on remote node (with id=4). Parameter is 16-bit integer. He can can enter
|
||||
* command string: `[1] 4 read 0x1017 0 i16`. CANopenNode will use SDO client,
|
||||
* send request to remote node via CAN, wait for response via CAN and prints
|
||||
* `[1] OK` to output stream on success.
|
||||
*
|
||||
* This module is usually initialized and processed in CANopen.c file.
|
||||
* Application should register own callback function for reading the output
|
||||
* stream. Application writes new commands with CO_GTWA_write().
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CO_CANopen_309_3_Syntax Command syntax
|
||||
* @{
|
||||
*
|
||||
* @code{.unparsed}
|
||||
Command strings start with '"["<sequence>"]"' followed by:
|
||||
[[<net>] <node>] r[ead] <index> <subindex> [<datatype>] # SDO upload.
|
||||
[[<net>] <node>] w[rite] <index> <subindex> <datatype> <value> # SDO download.
|
||||
|
||||
[[<net>] <node>] start # NMT Start node.
|
||||
[[<net>] <node>] stop # NMT Stop node.
|
||||
[[<net>] <node>] preop[erational] # NMT Set node to pre-operational.
|
||||
[[<net>] <node>] reset node # NMT Reset node.
|
||||
[[<net>] <node>] reset comm[unication] # NMT Reset communication.
|
||||
|
||||
[<net>] set network <value> # Set default net.
|
||||
[<net>] set node <value> # Set default node.
|
||||
[<net>] set sdo_timeout <value> # Configure SDO time-out.
|
||||
[<net>] set sdo_block <value> # Enable/disable SDO block transfer.
|
||||
|
||||
help # Print this help.
|
||||
|
||||
Datatypes:
|
||||
b # Boolean.
|
||||
u8, u16, u32, u64 # Unsigned integers.
|
||||
i8, i16, i32, i64 # Signed integers.
|
||||
r32, r64 # Real numbers.
|
||||
t, td # Time of day, time difference.
|
||||
vs # Visible string (between double quotes if multi-word).
|
||||
os, us, d # Octet string, unicode string, domain (mime-base64
|
||||
# (RFC2045) based, one line).
|
||||
hex # Hexagonal data, optionally space separated, non-standard.
|
||||
|
||||
Response:
|
||||
"["<sequence>"]" OK | <value> |
|
||||
ERROR: <SDO-abort-code> | ERROR: <internal-error-code>
|
||||
|
||||
Every command must be terminated with <CR><LF> ('\\r\\n'). characters. Same is
|
||||
response. String is not null terminated, <CR> is optional in command.
|
||||
|
||||
Comments started with '#' are ignored. They may be on the beginning of the
|
||||
line or after the command string.
|
||||
|
||||
'sdo_timeout' is in milliseconds, 500 by default. Block transfer is disabled
|
||||
by default.
|
||||
|
||||
If '<net>' or '<node>' is not specified within commands, then value defined by
|
||||
'set network' or 'set node' command is used.
|
||||
* @endcode
|
||||
*
|
||||
* This help text is the same as variable contents in CO_GTWA_helpString.
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/** Size of response string buffer. This is intermediate buffer. If there is
|
||||
* larger amount of data to transfer, then multiple transfers will occur. */
|
||||
#ifndef CO_GTWA_RESP_BUF_SIZE
|
||||
#define CO_GTWA_RESP_BUF_SIZE 200
|
||||
#endif
|
||||
|
||||
|
||||
/** Timeout time in microseconds for some internal states. */
|
||||
#ifndef CO_GTWA_STATE_TIMEOUT_TIME_US
|
||||
#define CO_GTWA_STATE_TIMEOUT_TIME_US 1000000
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Response error codes as specified by CiA 309-3. Values less or equal to 0
|
||||
* are used for control for some functions and are not part of the standard.
|
||||
*/
|
||||
typedef enum {
|
||||
/** 0 - No error or idle */
|
||||
CO_GTWA_respErrorNone = 0,
|
||||
/** 100 - Request not supported */
|
||||
CO_GTWA_respErrorReqNotSupported = 100,
|
||||
/** 101 - Syntax error */
|
||||
CO_GTWA_respErrorSyntax = 101,
|
||||
/** 102 - Request not processed due to internal state */
|
||||
CO_GTWA_respErrorInternalState = 102,
|
||||
/** 103 - Time-out (where applicable) */
|
||||
CO_GTWA_respErrorTimeOut = 103,
|
||||
/** 104 - No default net set */
|
||||
CO_GTWA_respErrorNoDefaultNetSet = 104,
|
||||
/** 105 - No default node set */
|
||||
CO_GTWA_respErrorNoDefaultNodeSet = 105,
|
||||
/** 106 - Unsupported net */
|
||||
CO_GTWA_respErrorUnsupportedNet = 106,
|
||||
/** 107 - Unsupported node */
|
||||
CO_GTWA_respErrorUnsupportedNode = 107,
|
||||
/** 200 - Lost guarding message */
|
||||
CO_GTWA_respErrorLostGuardingMessage = 200,
|
||||
/** 201 - Lost connection */
|
||||
CO_GTWA_respErrorLostConnection = 201,
|
||||
/** 202 - Heartbeat started */
|
||||
CO_GTWA_respErrorHeartbeatStarted = 202,
|
||||
/** 203 - Heartbeat lost */
|
||||
CO_GTWA_respErrorHeartbeatLost = 203,
|
||||
/** 204 - Wrong NMT state */
|
||||
CO_GTWA_respErrorWrongNMTstate = 204,
|
||||
/** 205 - Boot-up */
|
||||
CO_GTWA_respErrorBootUp = 205,
|
||||
/** 300 - Error passive */
|
||||
CO_GTWA_respErrorErrorPassive = 300,
|
||||
/** 301 - Bus off */
|
||||
CO_GTWA_respErrorBusOff = 301,
|
||||
/** 303 - CAN buffer overflow */
|
||||
CO_GTWA_respErrorCANbufferOverflow = 303,
|
||||
/** 304 - CAN init */
|
||||
CO_GTWA_respErrorCANinit = 304,
|
||||
/** 305 - CAN active (at init or start-up) */
|
||||
CO_GTWA_respErrorCANactive = 305,
|
||||
/** 400 - PDO already used */
|
||||
CO_GTWA_respErrorPDOalreadyUsed = 400,
|
||||
/** 401 - PDO length exceeded */
|
||||
CO_GTWA_respErrorPDOlengthExceeded = 401,
|
||||
/** 501 - LSS implementation- / manufacturer-specific error */
|
||||
CO_GTWA_respErrorLSSmanufacturer = 501,
|
||||
/** 502 - LSS node-ID not supported */
|
||||
CO_GTWA_respErrorLSSnodeIdNotSupported = 502,
|
||||
/** 503 - LSS bit-rate not supported */
|
||||
CO_GTWA_respErrorLSSbitRateNotSupported = 503,
|
||||
/** 504 - LSS parameter storing failed */
|
||||
CO_GTWA_respErrorLSSparameterStoringFailed = 504,
|
||||
/** 505 - LSS command failed because of media error */
|
||||
CO_GTWA_respErrorLSSmediaError = 505,
|
||||
/** 600 - Running out of memory */
|
||||
CO_GTWA_respErrorRunningOutOfMemory = 600
|
||||
} CO_GTWA_respErrorCode_t;
|
||||
|
||||
|
||||
/**
|
||||
* Internal states of the Gateway-ascii state machine.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Gateway is idle, no command is processing. This state is starting point
|
||||
* for new commands, which are parsed here. */
|
||||
CO_GTWA_ST_IDLE = 0x00U,
|
||||
/** SDO 'read' (upload) */
|
||||
CO_GTWA_ST_READ = 0x10U,
|
||||
/** SDO 'write' (download) */
|
||||
CO_GTWA_ST_WRITE = 0x11U,
|
||||
/** print 'help' text */
|
||||
CO_GTWA_ST_HELP = 0x90U
|
||||
} CO_GTWA_state_t;
|
||||
|
||||
|
||||
/*
|
||||
* CANopen Gateway-ascii data types structure
|
||||
*/
|
||||
typedef struct {
|
||||
/** Data type syntax, as defined in CiA309-3 */
|
||||
char* syntax;
|
||||
/** Data type length in bytes, 0 if size is not known */
|
||||
size_t length;
|
||||
/** Function, which reads data of specific data type from fifo buffer and
|
||||
* writes them as corresponding ascii string. It is a pointer to
|
||||
* #CO_fifo_readU82a function or similar and is used with SDO upload. For
|
||||
* description of parameters see #CO_fifo_readU82a */
|
||||
size_t (*dataTypePrint)(CO_fifo_t *fifo,
|
||||
char *buf,
|
||||
size_t count,
|
||||
bool_t end);
|
||||
/** Function, which reads ascii-data of specific data type from fifo buffer
|
||||
* and copies them to another fifo buffer as binary data. It is a pointer to
|
||||
* #CO_fifo_cpyTok2U8 function or similar and is used with SDO download. For
|
||||
* description of parameters see #CO_fifo_cpyTok2U8 */
|
||||
size_t (*dataTypeScan)(CO_fifo_t *dest,
|
||||
CO_fifo_t *src,
|
||||
uint8_t *status);
|
||||
} CO_GTWA_dataType_t;
|
||||
|
||||
|
||||
/**
|
||||
* CANopen Gateway-ascii object
|
||||
*/
|
||||
typedef struct {
|
||||
/** Pointer to external function for reading response from Gateway-ascii
|
||||
* object. Pointer is initialized in CO_GTWA_initRead().
|
||||
*
|
||||
* @param object Void pointer to custom object
|
||||
* @param buf Buffer from which data can be read
|
||||
* @param count Count of bytes available inside buffer
|
||||
*
|
||||
* @return Count of bytes actually transferred.
|
||||
*/
|
||||
size_t (*readCallback)(void *object, const char *buf, size_t count);
|
||||
/** Pointer to object, which will be used inside readCallback, from
|
||||
* CO_GTWA_init() */
|
||||
void *readCallbackObject;
|
||||
/** Sequence number of the command */
|
||||
uint32_t sequence;
|
||||
/** Default CANopen Net number is undefined (-1) at startup */
|
||||
int32_t net_default;
|
||||
/** Default CANopen Node ID number is undefined (-1) at startup */
|
||||
int16_t node_default;
|
||||
/** Current CANopen Net number */
|
||||
uint16_t net;
|
||||
/** Current CANopen Node ID */
|
||||
uint8_t node;
|
||||
/** CO_fifo_t object for command (not pointer) */
|
||||
CO_fifo_t commFifo;
|
||||
/** Command buffer of usable size @ref CO_CONFIG_GTWA_COMM_BUF_SIZE */
|
||||
char commBuf[CO_CONFIG_GTWA_COMM_BUF_SIZE + 1];
|
||||
/** Response buffer of usable size @ref CO_GTWA_RESP_BUF_SIZE */
|
||||
char respBuf[CO_GTWA_RESP_BUF_SIZE];
|
||||
/** Actual size of data in respBuf */
|
||||
size_t respBufCount;
|
||||
/** If only part of data has been successfully written into external
|
||||
* application (with readCallback()), then Gateway-ascii object will stay
|
||||
* in current state. This situation is indicated with respHold variable and
|
||||
* respBufOffset indicates offset to untransferred data inside respBuf. */
|
||||
size_t respBufOffset;
|
||||
/** See respBufOffset above */
|
||||
bool_t respHold;
|
||||
/** Sum of time difference from CO_GTWA_process() in case of respHold */
|
||||
uint32_t timeDifference_us_cumulative;
|
||||
/** Current state of the gateway object */
|
||||
CO_GTWA_state_t state;
|
||||
/** Timeout timer for the current state */
|
||||
uint32_t stateTimeoutTmr;
|
||||
/** SDO client object from CO_GTWA_init() */
|
||||
CO_SDOclient_t *SDO_C;
|
||||
/** Timeout time for SDO transfer in milliseconds, if no response */
|
||||
uint16_t SDOtimeoutTime;
|
||||
/** SDO block transfer enabled? */
|
||||
bool_t SDOblockTransferEnable;
|
||||
/** Indicate status of data copy from / to SDO buffer */
|
||||
bool_t SDOdataCopyStatus;
|
||||
/** Data type of variable in current SDO communication */
|
||||
const CO_GTWA_dataType_t *SDOdataType;
|
||||
/** NMT object from CO_GTWA_init() */
|
||||
CO_NMT_t *NMT;
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_HELP) || defined CO_DOXYGEN
|
||||
/** Offset, when printing help text */
|
||||
size_t helpStringOffset;
|
||||
#endif
|
||||
} CO_GTWA_t;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize Gateway-ascii object
|
||||
*
|
||||
* @param gtwa This object will be initialized
|
||||
* @param SDO_C SDO client object
|
||||
* @param SDOtimeoutTimeDefault in milliseconds, 500 typically
|
||||
* @param SDOblockTransferEnableDefault true or false
|
||||
* @param NMT NMT object
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa,
|
||||
CO_SDOclient_t* SDO_C,
|
||||
uint16_t SDOtimeoutTimeDefault,
|
||||
bool_t SDOblockTransferEnableDefault,
|
||||
CO_NMT_t *NMT);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize read callback in Gateway-ascii object
|
||||
*
|
||||
* Callback will used for transfer data to output stream of the application. It
|
||||
* will be called from CO_GTWA_process() zero or multiple times, depending on
|
||||
* the data available. If readCallback is uninitialized or NULL, then output
|
||||
* data will be purged.
|
||||
*
|
||||
* @param gtwa This object will be initialized
|
||||
* @param readCallback Pointer to external function for reading response from
|
||||
* Gateway-ascii object. See #CO_GTWA_t for parameters.
|
||||
* @param readCallbackObject Pointer to object, which will be used inside
|
||||
* readCallback
|
||||
*/
|
||||
void CO_GTWA_initRead(CO_GTWA_t* gtwa,
|
||||
size_t (*readCallback)(void *object,
|
||||
const char *buf,
|
||||
size_t count),
|
||||
void *readCallbackObject);
|
||||
|
||||
|
||||
/**
|
||||
* Get free write buffer space
|
||||
*
|
||||
* @param gtwa This object
|
||||
*
|
||||
* @return number of available bytes
|
||||
*/
|
||||
static inline size_t CO_GTWA_write_getSpace(CO_GTWA_t* gtwa) {
|
||||
return CO_fifo_getSpace(>wa->commFifo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write command into CO_GTWA_t object.
|
||||
*
|
||||
* This function copies ascii command from buf into internal fifo buffer.
|
||||
* Command must be closed with '\n' character. Function returns number of bytes
|
||||
* successfully copied. If there is not enough space in destination, not all
|
||||
* bytes will be copied and data can be refilled later (in case of large SDO
|
||||
* download).
|
||||
*
|
||||
* @param gtwa This object
|
||||
* @param buf Buffer which will be copied
|
||||
* @param count Number of bytes in buf
|
||||
*
|
||||
* @return number of bytes actually written.
|
||||
*/
|
||||
static inline size_t CO_GTWA_write(CO_GTWA_t* gtwa,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
return CO_fifo_write(>wa->commFifo, buf, count, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process Gateway-ascii object
|
||||
*
|
||||
* This is non-blocking function and must be called cyclically
|
||||
*
|
||||
* @param gtwa This object will be initialized.
|
||||
* @param enable If true, gateway operates normally. If false, gateway is
|
||||
* completely disabled and no command interaction is possible. Can be connected
|
||||
* to hardware switch, for example.
|
||||
* @param timeDifference_us Time difference from previous function call in
|
||||
* [microseconds].
|
||||
* @param [out] timerNext_us info to OS - see CO_process().
|
||||
*
|
||||
* @return CO_ReturnError_t: CO_ERROR_NO on success or CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
void CO_GTWA_process(CO_GTWA_t *gtwa,
|
||||
bool_t enable,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *timerNext_us);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
/** @} */
|
||||
#endif /* CO_GATEWAY_ASCII_H */
|
||||
44
CANopen.c
44
CANopen.c
|
|
@ -42,6 +42,9 @@ static CO_CANtx_t *CO_CANmodule_txArray0;
|
|||
static CO_OD_extension_t *CO_SDO_ODExtensions;
|
||||
static CO_HBconsNode_t *CO_HBcons_monitoredNodes;
|
||||
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII) && !defined CO_GTWA_ENABLE
|
||||
#define CO_GTWA_ENABLE true
|
||||
#endif
|
||||
#if CO_NO_TRACE > 0
|
||||
static uint32_t *CO_traceTimeBuffers[CO_NO_TRACE];
|
||||
static int32_t *CO_traceValueBuffers[CO_NO_TRACE];
|
||||
|
|
@ -224,6 +227,13 @@ CO_ReturnError_t CO_new(uint32_t *heapMemoryUsed) {
|
|||
CO_memoryUsed += sizeof(CO_LSSmaster_t);
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Gateway-ascii */
|
||||
CO->gtwa = (CO_GTWA_t *)calloc(1, sizeof(CO_GTWA_t));
|
||||
if (CO->gtwa == NULL) errCnt++;
|
||||
CO_memoryUsed += sizeof(CO_GTWA_t);
|
||||
#endif
|
||||
|
||||
#if CO_NO_TRACE > 0
|
||||
/* Trace */
|
||||
for (i = 0; i < CO_NO_TRACE; i++) {
|
||||
|
|
@ -276,6 +286,11 @@ void CO_delete(void *CANptr) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Gateway-ascii */
|
||||
free(CO->gtwa);
|
||||
#endif
|
||||
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
/* LSSmaster */
|
||||
free(CO->LSSmaster);
|
||||
|
|
@ -370,6 +385,9 @@ void CO_delete(void *CANptr) {
|
|||
#if CO_NO_LSS_CLIENT == 1
|
||||
static CO_LSSmaster_t COO_LSSmaster;
|
||||
#endif
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
static CO_GTWA_t COO_gtwa;
|
||||
#endif
|
||||
#if CO_NO_TRACE > 0
|
||||
#ifndef CO_TRACE_BUFFER_SIZE_FIXED
|
||||
#define CO_TRACE_BUFFER_SIZE_FIXED 100
|
||||
|
|
@ -453,6 +471,11 @@ CO_ReturnError_t CO_new(uint32_t *heapMemoryUsed) {
|
|||
CO->LSSmaster = &COO_LSSmaster;
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Gateway-ascii */
|
||||
CO->gtwa = &COO_gtwa;
|
||||
#endif
|
||||
|
||||
#if CO_NO_TRACE > 0
|
||||
/* Trace */
|
||||
for (i = 0; i < CO_NO_TRACE; i++) {
|
||||
|
|
@ -710,12 +733,11 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId) {
|
|||
|
||||
if (err) return err;
|
||||
|
||||
|
||||
#if CO_NO_SDO_CLIENT != 0
|
||||
/* SDOclient */
|
||||
for (i = 0; i < CO_NO_SDO_CLIENT; i++) {
|
||||
err = CO_SDOclient_init(CO->SDOclient[i],
|
||||
CO->SDO[0],
|
||||
(void *)CO->SDO[0],
|
||||
(CO_SDOclientPar_t *)&OD_SDOClientParameter[i],
|
||||
CO->CANmodule[0],
|
||||
CO_RXCAN_SDO_CLI + i,
|
||||
|
|
@ -738,7 +760,17 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId) {
|
|||
CO_CAN_ID_LSS_SRV);
|
||||
|
||||
if (err) return err;
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Gateway-ascii */
|
||||
err = CO_GTWA_init(CO->gtwa,
|
||||
CO->SDOclient[0],
|
||||
500,
|
||||
false,
|
||||
CO->NMT);
|
||||
|
||||
if (err) return err;
|
||||
#endif
|
||||
|
||||
#if CO_NO_TRACE > 0
|
||||
|
|
@ -816,6 +848,14 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
|
|||
timeDifference_us,
|
||||
timerNext_us);
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Gateway-ascii */
|
||||
CO_GTWA_process(CO->gtwa,
|
||||
CO_GTWA_ENABLE,
|
||||
timeDifference_us,
|
||||
timerNext_us);
|
||||
#endif
|
||||
|
||||
return reset;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,9 @@ extern "C" {
|
|||
#if CO_NO_LSS_CLIENT != 0
|
||||
#include "305/CO_LSSmaster.h"
|
||||
#endif
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
#include "309/CO_gateway_ascii.h"
|
||||
#endif
|
||||
#if CO_NO_TRACE != 0
|
||||
#include "extra/CO_trace.h"
|
||||
#endif
|
||||
|
|
@ -241,6 +244,9 @@ typedef struct {
|
|||
#if CO_NO_LSS_CLIENT == 1
|
||||
CO_LSSmaster_t *LSSmaster; /**< LSS master object */
|
||||
#endif
|
||||
#if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII) || defined CO_DOXYGEN
|
||||
CO_GTWA_t *gtwa; /**< Gateway-ascii object (CiA309-3) */
|
||||
#endif
|
||||
#if CO_NO_TRACE > 0
|
||||
CO_trace_t *trace[CO_NO_TRACE]; /**< Trace object for recording variables */
|
||||
#endif
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -31,8 +31,10 @@ SOURCES = \
|
|||
$(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)/305/CO_LSSslave.c \
|
||||
$(CANOPEN_SRC)/305/CO_LSSmaster.c \
|
||||
$(CANOPEN_SRC)/309/CO_gateway_ascii.c \
|
||||
$(CANOPEN_SRC)/extra/CO_trace.c \
|
||||
$(CANOPEN_SRC)/CANopen.c \
|
||||
$(OD_SRC)/CO_OD.c \
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ Change Log
|
|||
- Added `void *object` argument to CO_*_initCallback() functions. API clarified.
|
||||
- Add emergency receive callback also for own emergency messages.
|
||||
- Heartbeat is send immediately after NMT state changes.
|
||||
- SDO client is rewritten. Now includes read/write fifo interface to transfer data.
|
||||
### Changed SocketCAN
|
||||
- ./stack/socketCAN removed from the project, ./stack/Neuberger-socketCAN moved to ./socketCAN
|
||||
- driver API updated
|
||||
|
|
@ -33,13 +34,15 @@ Change Log
|
|||
- 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.
|
||||
- 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.
|
||||
|
||||
[v1.3] - 2020-04-27
|
||||
-------------------
|
||||
- [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.2...v1.3)
|
||||
### Changed
|
||||
- License changed to Apache 2.0.
|
||||
- NMT self start functionality (OD object 1F80) implemented to strictly folow standard. Default value for object 1F80 have to be updated in OD editor. See README.md.
|
||||
- NMT self start functionality (OD object 1F80) implemented to strictly follow standard. Default value for object 1F80 have to be updated in OD editor. See README.md.
|
||||
### Fixed
|
||||
- Various fixes.
|
||||
- neuberger-socketCAN fixed.
|
||||
|
|
|
|||
|
|
@ -15,194 +15,156 @@ CANopen network usually has one device with master functionalities for network c
|
|||
|
||||
CANopenNode on Linux
|
||||
--------------------
|
||||
CANopenNode should run on any Linux machine. Examples below was tested on Debian based machines, including **Ubuntu** and **Raspberry PI**. It is possible to run tests described below without real CAN interface, because Linux kernel already contains virtual CAN interface.
|
||||
CANopenNode should run on any Linux machine. Examples below was tested on Debian based machines, including Ubuntu and Raspberry PI. It is possible to run tests described below without real CAN interface, because Linux kernel already contains virtual CAN interface.
|
||||
All necessary Linux specific files are included in socketCAN directory of CANopenNode and Makefile is included in base directory.
|
||||
|
||||
----
|
||||
### Preparation
|
||||
We will use Linux command line interface (Terminal) for all examples below. Open the terminal and cd to your working directory.
|
||||
First install supporting packages: [can-utils](https://github.com/linux-can/can-utils), which is very useful tool for working with CAN interface and [git](https://git-scm.com/), which is recommended for working with repositories.
|
||||
Then clone [CANopenNode](https://github.com/CANopenNode/CANopenNode) from Github and build the executable program.
|
||||
|
||||
TODO update all below. (This is currently part of https://github.com/CANopenNode/CANopenSocket, but will become part of CANopenNode.)
|
||||
sudo apt-get install git
|
||||
sudo apt-get install can-utils
|
||||
git clone https://github.com/CANopenNode/CANopenNode.git
|
||||
cd CANopenNode
|
||||
make
|
||||
|
||||
CANopenSocket consists of two applications: **canopend**, which runs in background, and **canopencomm**, command interface for SDO and NMT master.
|
||||
Now prepare CAN virtual device and run _candump_, which will show all CAN traffic. Use a second terminal:
|
||||
|
||||
sudo modprobe vcan
|
||||
sudo ip link add dev vcan0 type vcan
|
||||
sudo ip link set up vcan0
|
||||
candump vcan0
|
||||
|
||||
|
||||
### canopend
|
||||
**canopend** is an implementation of CANopen device with master functionality. It runs within three threads. Realtime thread processes CANopen SYNC and PDO objects. Mainline thread processes other non time critical objects. Both are nonblocking. Command interface thread is blocking. It accepts commands from socket connection from external application and executes master SDO and NMT tasks.
|
||||
### First CANopen device
|
||||
Go to the first terminal, where we have recently build executable, named _canopend_.
|
||||
First print help, then run the program with some options.
|
||||
|
||||
./canopend --help
|
||||
./canopend vcan0 -i 4 -s od4_storage -a od4_storage_auto
|
||||
|
||||
### canopencomm
|
||||
**canopencomm** is the other end of the Command interface. It accepts text commands form arguments or from standard input or from file. It sends commands to *canopend* via socket, line after line. Received result is printed to standard output. It is implementation of the CiA 309 standard.
|
||||
You are now running a fully functional CANopen device on virtual CAN network. It is running in background until you terminate the process (with CTRL+C for example) or it receives a reset message from CAN network. By default process also shows some info messages on terminal, for example changes of NMT state or emergency messages, own and remote.
|
||||
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
We will run two instances of *CANopend*. First will be basic node with ID=4,
|
||||
second, with nodeID = 3, will have master functionality.
|
||||
|
||||
|
||||
### Get the project
|
||||
|
||||
Clone the project from git repository and get submodules:
|
||||
|
||||
$ git clone https://github.com/CANopenNode/CANopenSocket.git
|
||||
$ cd CANopenSocket
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
|
||||
(If you want to work on submodule CANopenNode, you can `cd CANopenNode`,
|
||||
and apply git commands directly on it. Initially is in head detached state,
|
||||
so you have to `git checkout master` first. Then you can control submodule
|
||||
separately, for example `git remote add {yourName} {url-of-your-git-repository}`,
|
||||
and `git pull {yourName} {yourbranch}`)
|
||||
|
||||
|
||||
### First terminal: CAN dump
|
||||
|
||||
Prepare CAN virtual (or real) device:
|
||||
|
||||
$ sudo modprobe vcan
|
||||
$ sudo ip link add dev vcan0 type vcan
|
||||
$ sudo ip link set up vcan0
|
||||
|
||||
Run candump from [can-utils](https://github.com/linux-can/can-utils):
|
||||
|
||||
$ sudo apt-get install can-utils
|
||||
$ candump vcan0
|
||||
|
||||
It will show all CAN traffic on vcan0.
|
||||
|
||||
|
||||
### Second terminal: canopend
|
||||
|
||||
Start second terminal, compile and start *canopend*.
|
||||
|
||||
$ cd CANopenSocket/canopend
|
||||
$ make
|
||||
$ app/canopend --help
|
||||
$ app/canopend vcan0 -i 4 -s od4_storage -a od4_storage_auto
|
||||
|
||||
You should now see CAN messages on CAN dump terminal. Wait few seconds and
|
||||
press CTRL-C.
|
||||
On the second terminal you can see some CAN traffic. After _canopend_ startup, first messages are:
|
||||
|
||||
vcan0 704 [1] 00 # Bootup message.
|
||||
vcan0 084 [8] 00 50 01 2F F3 FF FF FF # Emergency message.
|
||||
vcan0 704 [1] 7F # Heartbeat messages
|
||||
vcan0 704 [1] 7F # one per second.
|
||||
vcan0 704 [1] 7F # Heartbeat messages each second
|
||||
vcan0 704 [1] 7F
|
||||
|
||||
Heartbeat messages shows pre-operational state (0x7F). If you follow byte 4 of the
|
||||
Emergency message into [CANopenNode/stack/CO_Emergency.h],
|
||||
CO_EM_errorStatusBits, you will see under 0x2F "CO_EM_NON_VOLATILE_MEMORY",
|
||||
which is generic, critical error with access to non volatile device memory.
|
||||
This byte is CANopenNode specific. You can observe also first two bytes,
|
||||
which shows standard error code (0x5000 - Device Hardware) or third byte,
|
||||
which shows error register. If error register is different than zero, then
|
||||
node is not able to enter operational and PDOs can not be exchanged with it.
|
||||
Bootup and Heartbeat messages of node 4 have CAN-ID equal to 0x704. CAN-ID is 11-bit standard CAN identifier. 0x7F in heartbeat message means, that node is in NMT pre-operational state.
|
||||
|
||||
You can follow the reason of the problem inside the source code. However,
|
||||
there are missing non-default storage files. Add them and run it again.
|
||||
Also, both, first and second terminal shows, that there is an Emergency message after the bootup. Also Heartbeat messages shows NMT pre-operational state.
|
||||
|
||||
$ echo - > od4_storage
|
||||
$ echo - > od4_storage_auto
|
||||
$ app/canopend vcan0 -i 4 -s od4_storage -a od4_storage_auto
|
||||
The easiest way to find the reason of the emergency message is to check the byte 4 (errorBit). It has value of 0x2F. Go to CANopenNode source code and open the file "301/CO_Emergency.h", section "Error status bits". 0x2F means "CO_EM_NON_VOLATILE_MEMORY", which is generic, critical error with access to non volatile device memory.
|
||||
|
||||
This byte is CANopenNode specific. You can observe also first two bytes, which shows standard error code (0x5000 - Device Hardware) or third byte, which shows error register. If error register is different than zero, then node may be prohibited to enter operational and PDOs can not be exchanged with it.
|
||||
|
||||
You can follow the reason of the problem inside the source code. However, there are missing non-default storage files. Go to the first terminal, terminate the application with CTRL+C, add files and run _canopend_ again.
|
||||
|
||||
echo "-" > od4_storage
|
||||
echo "-" > od4_storage_auto
|
||||
./canopend vcan0 -i 4 -s od4_storage -a od4_storage_auto
|
||||
|
||||
Second terminal now shows operational state (0x05) and one pre-defined PDO message with CAN-ID 0x184 and two bytes of data. To learn more about PDOs, how to configure communication and mapping parameters and how to use them see other sources of CANopen documentation. For example, you can read the article of PDO re-mapping procedure in [CAN newsletter magazine, June 2016](http://can-newsletter.org/engineering/engineering-miscellaneous/160601_can-newsletter-magazine-june-2016).
|
||||
|
||||
vcan0 704 [1] 00
|
||||
vcan0 184 [2] 00 00 # PDO message
|
||||
vcan0 184 [2] 00 00 # PDO message
|
||||
vcan0 704 [1] 05
|
||||
|
||||
Now there is operational state (0x05) and there shows one PDO on CAN
|
||||
address 0x184. To learn more about PDOs, how to configure communication
|
||||
and mapping parameters and how to use them see other sources of CANopen
|
||||
documentation (For example article of PDO re-mapping procedure in [CAN
|
||||
newsletter magazine, June 2016](http://can-newsletter.org/engineering/engineering-miscellaneous/160601_can-newsletter-magazine-june-2016) ).
|
||||
|
||||
Start also second instance of *canopend* (master on nodeID=3) in the same
|
||||
window (*canopend terminal*). Use default od_storage files and default
|
||||
socket for command interface.
|
||||
### Second CANopen device
|
||||
Open the third terminal and cd to the same directory as is in the first terminal. First generate default storage files. Then start second instance of _canopend_ with NodeID = 3. Use default od_storage files and enable command interface on standard IO (terminal).
|
||||
|
||||
$ # press CTRL-Z
|
||||
$ bg
|
||||
$ app/canopend vcan0 -i 3 -c ""
|
||||
echo "-" > od_storage
|
||||
echo "-" > od_storage_auto
|
||||
./canopend vcan0 -i3 -c "stdio"
|
||||
|
||||
Now you should see in second terminal (_candump_) two CANopen devices sending heartbeats in one second interval each. One with node-ID = 4 and one with node-ID = 3. Both should be operational.
|
||||
|
||||
|
||||
### Third terminal: canopencomm
|
||||
### CANopen command interface
|
||||
Second instance of _canopend_ was started with command interface enabled. This is CANopen gateway interface with ascii mapping, as specified in standard CiA309-3. This enables usage of CANopen master functionalities via basic terminal. Go to third terminal, type "help" and press enter to see its functionalities.
|
||||
|
||||
Start third terminal, compile and start canopencomm.
|
||||
help
|
||||
|
||||
$ cd CANopenSocket/canopencomm
|
||||
$ make
|
||||
$ ./canopencomm --help
|
||||
#### SDO client
|
||||
For example read Heartbeat producer parameter on CANopen device with ID=4. Parameter is located at index 0x1017, subindex 0, it is 16-bit integer. (It is basically unsigned 16 bit integer (u16), but i16 give us more readable output in our case.)
|
||||
|
||||
#### SDO master
|
||||
[1] 4 read 0x1017 0 i16
|
||||
|
||||
Play with it and also observe CAN dump terminal. First Heartbeat at
|
||||
index 0x1017, subindex 0, 16-bit integer, on nodeID 4.
|
||||
You should see the response, which says that Heartbeats are transmitted in 1000 ms intervals:
|
||||
|
||||
$ ./canopencomm [1] 4 read 0x1017 0 i16
|
||||
$ ./canopencomm [1] 4 write 0x1017 0 i16 5000
|
||||
[1] 1000
|
||||
|
||||
In CAN dump you can see some SDO communication. You will notice, that
|
||||
Heartbeats from node 4 are coming in 5 second interval now. You can do
|
||||
the same also for node 3. Now store Object dictionary, so it will preserve
|
||||
variables on next start of the program.
|
||||
In CAN dump you can see some SDO communication. SDO communication can be quite complicated, if observing on _candump_, especially if larger data is split between multiple segments. However, there is no need to know the details, everything should work correctly in the background. Details about SDO communication can be found in CiA301 standard and partly also in 301/CO_SDOserver.h file, description of CO_SDO_state_t enumerator.
|
||||
|
||||
$ ./canopencomm 4 w 0x1010 1 u32 0x65766173
|
||||
[2] 4 write 0x1017 0 u16 5000
|
||||
[2] OK #response
|
||||
|
||||
You can read more about Object dictionary variables for this
|
||||
CANopenNode in [canopend/CANopenSocket.html].
|
||||
In _candump_ you will notice, that heartbeats from node 4 are coming in 5 second interval now. You can do the same also for node 3, but you won't see anything on _candump_, because data are written into itself directly. In "stdio" you can omit sequence number, to make typing easier.
|
||||
|
||||
3 w 0x1017 0 u16 2500
|
||||
[0] OK
|
||||
|
||||
Now store Object dictionary on node-ID 4, so it will preserve variables on next start of the program.
|
||||
|
||||
4 w 0x1010 1 u32 0x65766173
|
||||
[0] OK
|
||||
|
||||
More details about Object dictionary variables can be found in CiA301 standard or in example/IO.html file.
|
||||
|
||||
|
||||
#### NMT master
|
||||
If node is operational (started), it can exchange all objects, including
|
||||
PDO, SDO, etc. In pre-operational, PDOs are disabled, SDOs works. In stopped
|
||||
only NMT messages are accepted.
|
||||
If node is operational (started), it can exchange all objects, including PDO, SDO, etc. In NMT pre-operational, PDOs are disabled, SDOs works. In stopped only NMT messages are accepted. Try following commands and observe _candump_.
|
||||
|
||||
$ ./canopencomm 4 preop
|
||||
$ ./canopencomm 4 start
|
||||
$ ./canopencomm 4 stop
|
||||
$ ./canopencomm 4 r 0x1017 0 i16 # time out
|
||||
$ ./canopencomm 4 reset communication
|
||||
$ ./canopencomm 4 reset node
|
||||
$ ./canopencomm 3 reset node
|
||||
set node 4
|
||||
[0] OK
|
||||
|
||||
In *canopend terminal* you see, that both devices finished. Further commands
|
||||
are not possible. If you set so, last command can also reset computer.
|
||||
preop
|
||||
[0] OK
|
||||
|
||||
#### Combining NMT commands into a single file
|
||||
start
|
||||
[0] OK
|
||||
|
||||
Create a `commands.txt` file, and for its content enter your commands.
|
||||
Example:
|
||||
stop
|
||||
[0] OK
|
||||
|
||||
[1] 3 start
|
||||
[2] 4 start
|
||||
r 0x1017 0 i16
|
||||
[0] ERROR: 0x05040000 #SDO protocol timed out.
|
||||
|
||||
Make canopencomm use that file:
|
||||
reset communication
|
||||
[0] OK
|
||||
|
||||
$ ./canopencomm -f commands.txt
|
||||
[1] OK
|
||||
[2] OK
|
||||
reset node
|
||||
[0] OK
|
||||
|
||||
3 reset communication
|
||||
[0] OK
|
||||
|
||||
3 reset node
|
||||
[0] OK
|
||||
|
||||
|
||||
#### Other communication channels
|
||||
We used simple stdio for command interface. In Linux also sockets can be used, either local or tcp. See `./canopend --help` for options. Simple Linux tool for establishing socket connection is _netcat_ or _nc_. For example `nc -U /tmp/CO_command_socket` for local socket or `nc <host> 60000` for tcp socket. There are also some tools from [CANopenSocket](https://github.com/CANopenNode/CANopenSocket) project.
|
||||
|
||||
Please be careful when exposing your CANopen network to the outside world, it is your responsibility, if something dangerous happen.
|
||||
|
||||
|
||||
### Next steps
|
||||
Now you can learn more skills on CANopen from some other sources:
|
||||
books, data sheet of some CANopen device, standard CiA 301(it's free), etc.
|
||||
Then you can enter the big world of [CANopen devices](http://can-newsletter.org/hardware).
|
||||
|
||||
|
||||
Accessing real CANopen devices is the same as described above for virtual CAN interface.
|
||||
Some tested USB to CAN interfaces, which are natively integrated into Linux are:
|
||||
Now you can enter the big world of [CANopen devices](http://can-newsletter.org/hardware).
|
||||
|
||||
Accessing real CANopen devices is the same as described above for virtual CAN interface. Some tested USB to CAN interfaces, which are native integrated into Linux kernel are:
|
||||
- Simple serial [USBtin](http://www.fischl.de/usbtin/) - Start with: `sudo slcand -f -o -c -s8 /dev/ttyACM0 can0; sudo ip link set up can0`
|
||||
- [EMS CPC-USB](http://www.ems-wuensche.com/product/datasheet/html/can-usb-adapter-converter-interface-cpcusb.html) - Start with: `sudo ip link set up can0 type can bitrate 250000`
|
||||
- [PCAN-USB FD](http://www.peak-system.com/PCAN-USB-FD.365.0.html?&L=1) - Needs newer Linux kernel, supports CAN flexible data rate.
|
||||
- [EMS CPC-USB](https://www.ems-wuensche.com/?post_type=product&p=746) or [PCAN-USB FD](http://www.peak-system.com/PCAN-USB-FD.365.0.html?&L=1) - Start with: `sudo ip link set up can0 type can bitrate 250000`
|
||||
- You can get the idea of other supported CAN interfaces in [Linux kernel source](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/net/can) (Kconfig files).
|
||||
- Beaglebone or Paspberry PI or similar has CAN capes available. On RPI worked
|
||||
also the above USB interfaces, but it was necessary to compile the kernel.
|
||||
- Beaglebone or Paspberry PI or similar has CAN capes available. On RPI worked also the above USB interfaces, but it was necessary to compile the kernel.
|
||||
|
||||
|
||||
With [CANopenNode](https://github.com/CANopenNode/CANopenNode) you can also design your
|
||||
own device. There are many very useful and high quality specifications for different
|
||||
[device profiles](http://www.can-cia.org/standardization/specifications/),
|
||||
some of them are public and free to download.
|
||||
With [CANopenNode](https://github.com/CANopenNode/CANopenNode) you can also design your own device. There are many very useful and high quality specifications for different [device profiles](http://www.can-cia.org/standardization/specifications/), some of them are public and free to download.
|
||||
|
||||
|
||||
Here we played with virtual CAN interface and result shows as pixels on
|
||||
screen. If you connect real CAN interface to your computer, things may
|
||||
Here we played with virtual CAN interface and result shows as pixels on screen. If you connect real CAN interface to your computer, things may
|
||||
become dangerous. Keep control and safety on your machines!
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ struct sCO_OD_RAM CO_OD_RAM = {
|
|||
/*1003*/ {0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L, 0x0L},
|
||||
/*1010*/ {0x3L},
|
||||
/*1011*/ {0x1L},
|
||||
/*1280*/{{0x3, 0x0L, 0x0L, 0x0}},
|
||||
/*2100*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
/*2103*/ 0x0,
|
||||
/*2104*/ 0x0,
|
||||
|
|
@ -135,6 +136,11 @@ struct sCO_OD_EEPROM CO_OD_EEPROM = {
|
|||
{(void*)&CO_OD_ROM.SDOServerParameter[0].maxSubIndex, 0x05, 1},
|
||||
{(void*)&CO_OD_ROM.SDOServerParameter[0].COB_IDClientToServer, 0x85, 4},
|
||||
{(void*)&CO_OD_ROM.SDOServerParameter[0].COB_IDServerToClient, 0x85, 4}};
|
||||
/*0x1280*/ const CO_OD_entryRecord_t OD_record1280[4] = {
|
||||
{(void*)&CO_OD_RAM.SDOClientParameter[0].maxSubIndex, 0x06, 1},
|
||||
{(void*)&CO_OD_RAM.SDOClientParameter[0].COB_IDClientToServer, 0xBE, 4},
|
||||
{(void*)&CO_OD_RAM.SDOClientParameter[0].COB_IDServerToClient, 0xBE, 4},
|
||||
{(void*)&CO_OD_RAM.SDOClientParameter[0].nodeIDOfTheSDOServer, 0x0E, 1}};
|
||||
/*0x1400*/ const CO_OD_entryRecord_t OD_record1400[3] = {
|
||||
{(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].maxSubIndex, 0x05, 1},
|
||||
{(void*)&CO_OD_ROM.RPDOCommunicationParameter[0].COB_IDUsedByRPDO, 0x8D, 4},
|
||||
|
|
@ -302,6 +308,7 @@ const CO_OD_entry_t CO_OD[CO_OD_NoOfElements] = {
|
|||
{0x1019, 0x00, 0x0D, 1, (void*)&CO_OD_ROM.synchronousCounterOverflowValue},
|
||||
{0x1029, 0x06, 0x0D, 1, (void*)&CO_OD_ROM.errorBehavior[0]},
|
||||
{0x1200, 0x02, 0x00, 0, (void*)&OD_record1200},
|
||||
{0x1280, 0x03, 0x00, 0, (void*)&OD_record1280},
|
||||
{0x1400, 0x02, 0x00, 0, (void*)&OD_record1400},
|
||||
{0x1401, 0x02, 0x00, 0, (void*)&OD_record1401},
|
||||
{0x1402, 0x02, 0x00, 0, (void*)&OD_record1402},
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@
|
|||
#define CO_NO_TIME 1 //Associated objects: 1012-1013
|
||||
#define CO_NO_EMERGENCY 1 //Associated objects: 1014, 1015
|
||||
#define CO_NO_SDO_SERVER 1 //Associated objects: 1200
|
||||
#define CO_NO_SDO_CLIENT 0
|
||||
#define CO_NO_SDO_CLIENT 1 //Associated objects: 1280
|
||||
#define CO_NO_RPDO 4 //Associated objects: 1400, 1401, 1402, 1403, 1600, 1601, 1602, 1603
|
||||
#define CO_NO_TPDO 4 //Associated objects: 1800, 1801, 1802, 1803, 1A00, 1A01, 1A02, 1A03
|
||||
#define CO_NO_NMT_MASTER 0
|
||||
|
|
@ -104,7 +104,7 @@
|
|||
/*******************************************************************************
|
||||
OBJECT DICTIONARY
|
||||
*******************************************************************************/
|
||||
#define CO_OD_NoOfElements 56
|
||||
#define CO_OD_NoOfElements 57
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
|
@ -124,6 +124,13 @@
|
|||
UNSIGNED32 COB_IDServerToClient;
|
||||
} OD_SDOServerParameter_t;
|
||||
|
||||
/*1280[1] */ typedef struct{
|
||||
UNSIGNED8 maxSubIndex;
|
||||
UNSIGNED32 COB_IDClientToServer;
|
||||
UNSIGNED32 COB_IDServerToClient;
|
||||
UNSIGNED8 nodeIDOfTheSDOServer;
|
||||
} OD_SDOClientParameter_t;
|
||||
|
||||
/*1400[4] */ typedef struct{
|
||||
UNSIGNED8 maxSubIndex;
|
||||
UNSIGNED32 COB_IDUsedByRPDO;
|
||||
|
|
@ -195,6 +202,7 @@ struct sCO_OD_RAM{
|
|||
/*1003 */ UNSIGNED32 preDefinedErrorField[8];
|
||||
/*1010 */ UNSIGNED32 storeParameters[1];
|
||||
/*1011 */ UNSIGNED32 restoreDefaultParameters[1];
|
||||
/*1280[1] */ OD_SDOClientParameter_t SDOClientParameter[1];
|
||||
/*2100 */ OCTET_STRING errorStatusBits[10];
|
||||
/*2103 */ UNSIGNED16 SYNCCounter;
|
||||
/*2104 */ UNSIGNED16 SYNCTime;
|
||||
|
|
@ -346,6 +354,9 @@ extern struct sCO_OD_ROM CO_OD_ROM;
|
|||
/*1200[1], Data Type: OD_SDOServerParameter_t, Array[1] */
|
||||
#define OD_SDOServerParameter CO_OD_ROM.SDOServerParameter
|
||||
|
||||
/*1280[1], Data Type: OD_SDOClientParameter_t, Array[1] */
|
||||
#define OD_SDOClientParameter CO_OD_RAM.SDOClientParameter
|
||||
|
||||
/*1400[4], Data Type: OD_RPDOCommunicationParameter_t, Array[4] */
|
||||
#define OD_RPDOCommunicationParameter CO_OD_ROM.RPDOCommunicationParameter
|
||||
|
||||
|
|
|
|||
|
|
@ -112,19 +112,25 @@ extern "C" {
|
|||
|
||||
#ifndef CO_CONFIG_GTW
|
||||
#define CO_CONFIG_GTW (CO_CONFIG_GTW_MULTI_NET | \
|
||||
CO_CONFIG_GTW_ASCII)
|
||||
CO_CONFIG_GTW_ASCII | \
|
||||
CO_CONFIG_GTW_ASCII_ERROR_DESC | \
|
||||
CO_CONFIG_GTW_ASCII_PRINT_HELP)
|
||||
#define CO_CONFIG_GTW_BLOCK_DL_LOOP 1
|
||||
#define CO_CONFIG_GTWA_COMM_BUF_SIZE 2000
|
||||
#endif
|
||||
|
||||
|
||||
/* Basic definitions */
|
||||
/* Basic definitions. If big endian, CO_SWAP_xx macros must swap bytes. */
|
||||
#define CO_LITTLE_ENDIAN
|
||||
#define CO_SWAP_16(x) x
|
||||
#define CO_SWAP_32(x) x
|
||||
#define CO_SWAP_64(x) x
|
||||
/* NULL is defined in stddef.h */
|
||||
/* true and false are defined in stdbool.h */
|
||||
/* int8_t to uint64_t are defined in stdint.h */
|
||||
typedef unsigned char bool_t;
|
||||
typedef float float32_t;
|
||||
typedef long double float64_t;
|
||||
typedef double float64_t;
|
||||
typedef char char_t;
|
||||
typedef unsigned char oChar_t;
|
||||
typedef unsigned char domain_t;
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ SOURCES = \
|
|||
$(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)/305/CO_LSSslave.c \
|
||||
$(CANOPEN_SRC)/305/CO_LSSmaster.c \
|
||||
$(CANOPEN_SRC)/309/CO_gateway_ascii.c \
|
||||
$(CANOPEN_SRC)/extra/CO_trace.c \
|
||||
$(CANOPEN_SRC)/CANopen.c \
|
||||
$(OD_SRC)/CO_OD.c \
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ SDO server is implemented on all CANopen devices.
|
|||
Permissible value for SDO sever is 0 or 1.</description>
|
||||
<associatedObject index="1200" indexMax="1201" indexStep="1"/>
|
||||
</feature>
|
||||
<feature name="SDO client" value="0">
|
||||
<feature name="SDO client" value="1">
|
||||
<label lang="en">Service Data Object (SDO)</label>
|
||||
<description lang="en" URI="http://www.can-cia.org/canopen/protocol/sdo.html">A Service Data Object (SDO) reads from entries or writes to entries of the Object Dictionary.
|
||||
SDO client is usually a master device in a CANopen network.
|
||||
|
|
@ -253,7 +253,7 @@ bit 0-7: Node ID (optional)</description>
|
|||
<CANopenSubObject subIndex="01" name="COB-ID client to server" objectType="7" dataType="07" accessType="ro" PDOmapping="no" defaultValue="$NODEID+0x600"/>
|
||||
<CANopenSubObject subIndex="02" name="COB-ID server to client" objectType="7" dataType="07" accessType="ro" PDOmapping="no" defaultValue="$NODEID+0x580"/>
|
||||
</CANopenObject>
|
||||
<CANopenObject index="1280" name="SDO client parameter" objectType="9" subNumber="4" memoryType="RAM" disabled="true">
|
||||
<CANopenObject index="1280" name="SDO client parameter" objectType="9" subNumber="4" memoryType="RAM">
|
||||
<label lang="en">SDO client parameter</label>
|
||||
<description lang="en">0x1280 - 0x12FF SDO client parameter
|
||||
max sub-index
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* following macro is necessary for accept4() function call (sockets) */
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "CANopen.h"
|
||||
#include "CO_Linux_threads.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
|
@ -32,12 +38,22 @@
|
|||
#include <sys/eventfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <fcntl.h>
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */
|
||||
|
||||
#include "CANopen.h"
|
||||
|
||||
#ifndef LISTEN_BACKLOG
|
||||
#define LISTEN_BACKLOG 50
|
||||
#endif
|
||||
|
||||
|
||||
/* Helper function - get monotonic clock time in microseconds */
|
||||
static uint64_t CO_LinuxThreads_clock_gettime_us(void)
|
||||
static inline uint64_t CO_LinuxThreads_clock_gettime_us(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
|
|
@ -46,67 +62,51 @@ static uint64_t CO_LinuxThreads_clock_gettime_us(void)
|
|||
}
|
||||
|
||||
|
||||
/* Mainline thread - basic (threadMain) ***************************************/
|
||||
static struct
|
||||
{
|
||||
uint64_t start; /* time value CO_process() was called last time in us */
|
||||
} threadMain;
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* write response string from gateway-ascii object */
|
||||
static size_t gtwa_write_response(void *object, const char *buf, size_t count) {
|
||||
int* fd = (int *)object;
|
||||
size_t nWritten = 0;
|
||||
|
||||
void threadMain_init(void (*callback)(void*), void *object)
|
||||
{
|
||||
threadMain.start = CO_LinuxThreads_clock_gettime_us();
|
||||
|
||||
CO_NMT_initCallbackPre(CO->NMT, object, callback);
|
||||
CO_SDO_initCallbackPre(CO->SDO[0], object, callback);
|
||||
CO_EM_initCallbackPre(CO->em, object, callback);
|
||||
CO_HBconsumer_initCallbackPre(CO->HBcons, object, callback);
|
||||
}
|
||||
|
||||
void threadMain_close(void)
|
||||
{
|
||||
CO_NMT_initCallbackPre(CO->NMT, NULL, NULL);
|
||||
CO_SDO_initCallbackPre(CO->SDO[0], NULL, NULL);
|
||||
CO_EM_initCallbackPre(CO->em, NULL, NULL);
|
||||
CO_HBconsumer_initCallbackPre(CO->HBcons, NULL, NULL);
|
||||
}
|
||||
|
||||
void threadMain_process(CO_NMT_reset_cmd_t *reset)
|
||||
{
|
||||
uint32_t finished;
|
||||
uint32_t diff;
|
||||
uint64_t now;
|
||||
|
||||
now = CO_LinuxThreads_clock_gettime_us();
|
||||
diff = (uint32_t)(now - threadMain.start);
|
||||
threadMain.start = now;
|
||||
|
||||
/* we use timerNext_us in CO_process() as indication if processing is
|
||||
* finished. We ignore any calculated values for maximum delay times. */
|
||||
do {
|
||||
finished = 1;
|
||||
*reset = CO_process(CO, diff, &finished);
|
||||
diff = 0;
|
||||
} while ((*reset == CO_RESET_NOT) && (finished == 0));
|
||||
if (fd != NULL && *fd >= 0) {
|
||||
ssize_t n = write(*fd, (const void *)buf, count);
|
||||
if (n > 0) {
|
||||
nWritten = (size_t)n;
|
||||
}
|
||||
else {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "write(gtwa_response)");
|
||||
}
|
||||
}
|
||||
return nWritten;
|
||||
}
|
||||
#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */
|
||||
|
||||
|
||||
/* Mainline thread - Blocking (threadMainWait) ********************************/
|
||||
static struct
|
||||
{
|
||||
static struct {
|
||||
uint64_t start; /* time value CO_process() was called last time in us */
|
||||
int epoll_fd; /* epoll file descriptor */
|
||||
int event_fd; /* notification event file descriptor */
|
||||
int timer_fd; /* interval timer file descriptor */
|
||||
uint32_t interval_us; /* interval for threadMainWait_process */
|
||||
struct itimerspec tm; /* structure for timer configuration */
|
||||
} threadMainWait;
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
int32_t commandInterface; /* command interface type or tcp port */
|
||||
uint32_t socketTimeout_us;
|
||||
uint32_t socketTimeoutTmr_us;
|
||||
char *localSocketPath;/* path in case of local socket */
|
||||
int gtwa_fdSocket; /* gateway socket file descriptor */
|
||||
int gtwa_fd; /* gateway io stream file descriptor */
|
||||
bool_t freshCommand;
|
||||
#endif
|
||||
} tmw;
|
||||
|
||||
static void threadMainWait_callback(void *object)
|
||||
{
|
||||
/* send event to wake threadMainWait_process() */
|
||||
uint64_t u = 1;
|
||||
ssize_t s;
|
||||
s = write(threadMainWait.event_fd, &u, sizeof(uint64_t));
|
||||
s = write(tmw.event_fd, &u, sizeof(uint64_t));
|
||||
if (s != sizeof(uint64_t)) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "write()");
|
||||
}
|
||||
|
|
@ -119,60 +119,175 @@ void threadMainWait_init(void)
|
|||
CO_SDO_initCallbackPre(CO->SDO[0], NULL, threadMainWait_callback);
|
||||
CO_EM_initCallbackPre(CO->em, NULL, threadMainWait_callback);
|
||||
CO_HBconsumer_initCallbackPre(CO->HBcons, NULL, threadMainWait_callback);
|
||||
#if CO_NO_SDO_CLIENT != 0
|
||||
CO_SDOclient_initCallbackPre(CO->SDOclient[0], NULL,
|
||||
threadMainWait_callback);
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
CO_GTWA_initRead(CO->gtwa, gtwa_write_response, (void *)&tmw.gtwa_fd);
|
||||
tmw.freshCommand = true;
|
||||
#endif
|
||||
|
||||
/* Initial value for time calculation */
|
||||
threadMainWait.start = CO_LinuxThreads_clock_gettime_us();
|
||||
tmw.start = CO_LinuxThreads_clock_gettime_us();
|
||||
}
|
||||
|
||||
void threadMainWait_initOnce(uint32_t interval_us)
|
||||
void threadMainWait_initOnce(uint32_t interval_us,
|
||||
int32_t commandInterface,
|
||||
uint32_t socketTimeout_ms,
|
||||
char *localSocketPath)
|
||||
{
|
||||
int32_t ret;
|
||||
int ret;
|
||||
struct epoll_event ev;
|
||||
|
||||
/* Configure epoll for mainline */
|
||||
threadMainWait.epoll_fd = epoll_create(1);
|
||||
if (threadMainWait.epoll_fd < 0) {
|
||||
tmw.epoll_fd = epoll_create(1);
|
||||
if (tmw.epoll_fd < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_create()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Configure eventfd for notifications and add it to epoll */
|
||||
threadMainWait.event_fd = eventfd(0, EFD_NONBLOCK);
|
||||
if (threadMainWait.event_fd < 0) {
|
||||
tmw.event_fd = eventfd(0, EFD_NONBLOCK);
|
||||
if (tmw.event_fd < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "eventfd()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = threadMainWait.event_fd;
|
||||
ret = epoll_ctl(threadMainWait.epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev);
|
||||
if (ret < 0){
|
||||
ev.data.fd = tmw.event_fd;
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(event_fd)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Configure timer for interval_us and add it to epoll */
|
||||
threadMainWait.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
||||
if (threadMainWait.timer_fd < 0) {
|
||||
tmw.timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
||||
if (tmw.timer_fd < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "timerfd_create()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
threadMainWait.interval_us = interval_us;
|
||||
threadMainWait.tm.it_interval.tv_sec = interval_us / 1000000;
|
||||
threadMainWait.tm.it_interval.tv_nsec = (interval_us % 1000000) * 1000;
|
||||
threadMainWait.tm.it_value.tv_sec = 0;
|
||||
threadMainWait.tm.it_value.tv_nsec = 1;
|
||||
ret = timerfd_settime(threadMainWait.timer_fd, 0, &threadMainWait.tm, NULL);
|
||||
if (ret < 0){
|
||||
tmw.interval_us = interval_us;
|
||||
tmw.tm.it_interval.tv_sec = interval_us / 1000000;
|
||||
tmw.tm.it_interval.tv_nsec = (interval_us % 1000000) * 1000;
|
||||
tmw.tm.it_value.tv_sec = 0;
|
||||
tmw.tm.it_value.tv_nsec = 1;
|
||||
ret = timerfd_settime(tmw.timer_fd, 0, &tmw.tm, NULL);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "timerfd_settime");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = threadMainWait.timer_fd;
|
||||
ret = epoll_ctl(threadMainWait.epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev);
|
||||
if (ret < 0){
|
||||
ev.data.fd = tmw.timer_fd;
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(timer_fd)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Configure gateway-ascii command interface (CiA309-3) */
|
||||
tmw.commandInterface = commandInterface;
|
||||
tmw.socketTimeout_us = (socketTimeout_ms < (UINT_MAX / 1000 - 1000000)) ?
|
||||
socketTimeout_ms * 1000 : (UINT_MAX - 1000000);
|
||||
tmw.gtwa_fdSocket = -1;
|
||||
tmw.gtwa_fd = -1;
|
||||
|
||||
if (commandInterface == CO_COMMAND_IF_STDIO) {
|
||||
tmw.gtwa_fd = STDIN_FILENO;
|
||||
log_printf(LOG_INFO, DBG_COMMAND_STDIO_INFO);
|
||||
}
|
||||
else if (commandInterface == CO_COMMAND_IF_LOCAL_SOCKET) {
|
||||
struct sockaddr_un addr;
|
||||
tmw.localSocketPath = localSocketPath;
|
||||
|
||||
/* Create, bind and listen local socket */
|
||||
tmw.gtwa_fdSocket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if(tmw.gtwa_fdSocket < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "socket(local)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, localSocketPath, sizeof(addr.sun_path) - 1);
|
||||
ret = bind(tmw.gtwa_fdSocket, (struct sockaddr *) &addr,
|
||||
sizeof(struct sockaddr_un));
|
||||
if(ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_COMMAND_LOCAL_BIND, localSocketPath);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = listen(tmw.gtwa_fdSocket, LISTEN_BACKLOG);
|
||||
if(ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "listen(local)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_printf(LOG_INFO, DBG_COMMAND_LOCAL_INFO, localSocketPath);
|
||||
}
|
||||
else if (commandInterface >= CO_COMMAND_IF_TCP_SOCKET_MIN &&
|
||||
commandInterface <= CO_COMMAND_IF_TCP_SOCKET_MAX
|
||||
) {
|
||||
struct sockaddr_in addr;
|
||||
const int yes = 1;
|
||||
|
||||
/* Create, bind and listen socket */
|
||||
tmw.gtwa_fdSocket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if(tmw.gtwa_fdSocket < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "socket(tcp)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
setsockopt(tmw.gtwa_fdSocket, SOL_SOCKET, SO_REUSEADDR,
|
||||
&yes, sizeof(int));
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(commandInterface);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
ret = bind(tmw.gtwa_fdSocket, (struct sockaddr *) &addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
if(ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_COMMAND_TCP_BIND, commandInterface);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = listen(tmw.gtwa_fdSocket, LISTEN_BACKLOG);
|
||||
if(ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "listen(tcp)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_printf(LOG_INFO, DBG_COMMAND_TCP_INFO, commandInterface);
|
||||
}
|
||||
else {
|
||||
tmw.commandInterface = CO_COMMAND_IF_DISABLED;
|
||||
}
|
||||
|
||||
if (tmw.gtwa_fd >= 0) {
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = tmw.gtwa_fd;
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(gtwa_fd)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (tmw.gtwa_fdSocket >= 0) {
|
||||
/* prepare epool for listening for new socket connection. After
|
||||
* connection will be accepted, fd for io operation will be defined. */
|
||||
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||
ev.data.fd = tmw.gtwa_fdSocket;
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(gtwa_fdSocket)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */
|
||||
}
|
||||
|
||||
void threadMainWait_close(void)
|
||||
|
|
@ -182,65 +297,201 @@ void threadMainWait_close(void)
|
|||
CO_EM_initCallbackPre(CO->em, NULL, NULL);
|
||||
CO_HBconsumer_initCallbackPre(CO->HBcons, NULL, NULL);
|
||||
|
||||
close(threadMainWait.epoll_fd);
|
||||
close(threadMainWait.event_fd);
|
||||
close(threadMainWait.timer_fd);
|
||||
threadMainWait.epoll_fd = -1;
|
||||
threadMainWait.event_fd = -1;
|
||||
threadMainWait.timer_fd = -1;
|
||||
close(tmw.epoll_fd);
|
||||
tmw.epoll_fd = -1;
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
if (tmw.commandInterface == CO_COMMAND_IF_LOCAL_SOCKET) {
|
||||
if (tmw.gtwa_fd > 0) {
|
||||
close(tmw.gtwa_fd);
|
||||
}
|
||||
close(tmw.gtwa_fdSocket);
|
||||
/* Remove local socket file from filesystem. */
|
||||
if(remove(tmw.localSocketPath) < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "remove(local)");
|
||||
}
|
||||
}
|
||||
else if (tmw.commandInterface >= CO_COMMAND_IF_TCP_SOCKET_MIN) {
|
||||
if (tmw.gtwa_fd > 0) {
|
||||
close(tmw.gtwa_fd);
|
||||
}
|
||||
close(tmw.gtwa_fdSocket);
|
||||
}
|
||||
tmw.gtwa_fd = -1;
|
||||
tmw.gtwa_fdSocket = -1;
|
||||
#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */
|
||||
|
||||
close(tmw.event_fd);
|
||||
tmw.event_fd = -1;
|
||||
|
||||
close(tmw.timer_fd);
|
||||
tmw.timer_fd = -1;
|
||||
}
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
static inline void socetAcceptEnableForEpoll(void) {
|
||||
struct epoll_event ev;
|
||||
int ret;
|
||||
|
||||
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||
ev.data.fd = tmw.gtwa_fdSocket;
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_MOD, ev.data.fd, &ev);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(gtwa_fdSocket)");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t threadMainWait_process(CO_NMT_reset_cmd_t *reset)
|
||||
{
|
||||
int ready, ret;
|
||||
int ready;
|
||||
struct epoll_event ev;
|
||||
uint64_t ull;
|
||||
ssize_t s;
|
||||
uint32_t diff, timerNext_us;
|
||||
|
||||
/* wait for event or timer expiration and read data from file descriptors */
|
||||
ready = epoll_wait(threadMainWait.epoll_fd, &ev, 1, -1);
|
||||
ready = epoll_wait(tmw.epoll_fd, &ev, 1, -1);
|
||||
if (ready != 1 && errno != EINTR) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "epoll_wait");
|
||||
}
|
||||
else if (ev.data.fd == threadMainWait.event_fd) {
|
||||
s = read(threadMainWait.event_fd, &ull, sizeof(uint64_t));
|
||||
else if (ev.data.fd == tmw.event_fd) {
|
||||
s = read(tmw.event_fd, &ull, sizeof(uint64_t));
|
||||
if (s != sizeof(uint64_t)) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "read(event_fd)");
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "read(event_fd)");
|
||||
}
|
||||
}
|
||||
else if (ev.data.fd == threadMainWait.timer_fd) {
|
||||
s = read(threadMainWait.timer_fd, &ull, sizeof(uint64_t));
|
||||
else if (ev.data.fd == tmw.timer_fd) {
|
||||
s = read(tmw.timer_fd, &ull, sizeof(uint64_t));
|
||||
if (s != sizeof(uint64_t) && errno != EAGAIN) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "read(timer_fd)");
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "read(timer_fd)");
|
||||
}
|
||||
}
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
else if (ev.data.fd == tmw.gtwa_fdSocket) {
|
||||
bool_t fail = false;
|
||||
|
||||
tmw.gtwa_fd = accept4(tmw.gtwa_fdSocket, NULL, NULL, SOCK_NONBLOCK);
|
||||
if (tmw.gtwa_fd < 0) {
|
||||
fail = true;
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "accept(gtwa_fdSocket)");
|
||||
}
|
||||
}
|
||||
else {
|
||||
int ret;
|
||||
/* add fd to epoll */
|
||||
struct epoll_event ev2;
|
||||
ev2.events = EPOLLIN;
|
||||
ev2.data.fd = tmw.gtwa_fd;
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_ADD, ev2.data.fd, &ev2);
|
||||
if (ret < 0) {
|
||||
fail = true;
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(add, gtwa_fd)");
|
||||
}
|
||||
tmw.socketTimeoutTmr_us = 0;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
socetAcceptEnableForEpoll();
|
||||
}
|
||||
}
|
||||
else if (ev.data.fd == tmw.gtwa_fd) {
|
||||
char buf[CO_CONFIG_GTWA_COMM_BUF_SIZE];
|
||||
size_t space = CO_GTWA_write_getSpace(CO->gtwa);
|
||||
|
||||
s = read(tmw.gtwa_fd, buf, space);
|
||||
if (s < 0 && errno != EAGAIN) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "read(gtwa_fd)");
|
||||
}
|
||||
else if (s >= 0) {
|
||||
if (tmw.commandInterface == CO_COMMAND_IF_STDIO) {
|
||||
/* simplify command interface on stdio, make hard to type
|
||||
* sequence optional, prepend "[0] " to string, if missing */
|
||||
const char sequence[] = "[0] ";
|
||||
bool_t closed = (buf[s-1] == '\n'); /* is command closed? */
|
||||
|
||||
if (buf[0] != '[' && (space - s) >= strlen(sequence)
|
||||
&& s > 1 && closed && tmw.freshCommand
|
||||
) {
|
||||
CO_GTWA_write(CO->gtwa, sequence, strlen(sequence));
|
||||
}
|
||||
tmw.freshCommand = closed;
|
||||
CO_GTWA_write(CO->gtwa, buf, s);
|
||||
}
|
||||
else { /* socket, local or tcp */
|
||||
if (s == 0) {
|
||||
int ret;
|
||||
/* EOF received, close connection and enable socket accept*/
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_DEL,
|
||||
tmw.gtwa_fd, NULL);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO,
|
||||
"epoll_ctl(del, gtwa_fd)");
|
||||
}
|
||||
if (close(tmw.gtwa_fd) < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "close(gtwa_fd)");
|
||||
}
|
||||
tmw.gtwa_fd = -1;
|
||||
socetAcceptEnableForEpoll();
|
||||
}
|
||||
else {
|
||||
CO_GTWA_write(CO->gtwa, buf, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
tmw.socketTimeoutTmr_us = 0;
|
||||
}
|
||||
#endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */
|
||||
|
||||
/* calculate time difference since last call */
|
||||
ull = CO_LinuxThreads_clock_gettime_us();
|
||||
diff = (uint32_t)(ull - threadMainWait.start);
|
||||
threadMainWait.start = ull;
|
||||
diff = (uint32_t)(ull - tmw.start);
|
||||
tmw.start = ull;
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* if socket connection is established, verify timeout */
|
||||
if (tmw.socketTimeout_us > 0 && tmw.gtwa_fdSocket > 0 && tmw.gtwa_fd > 0) {
|
||||
if (tmw.socketTimeoutTmr_us > tmw.socketTimeout_us) {
|
||||
int ret;
|
||||
/* timout expired, close current connection and accept next */
|
||||
ret = epoll_ctl(tmw.epoll_fd, EPOLL_CTL_DEL, tmw.gtwa_fd, NULL);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "epoll_ctl(del, gtwa_fd), tmo");
|
||||
}
|
||||
if (close(tmw.gtwa_fd) < 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "close(gtwa_fd), tmo");
|
||||
}
|
||||
tmw.gtwa_fd = -1;
|
||||
socetAcceptEnableForEpoll();
|
||||
}
|
||||
else {
|
||||
tmw.socketTimeoutTmr_us += diff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* stack will lower this, if necessary */
|
||||
timerNext_us = threadMainWait.interval_us;
|
||||
timerNext_us = tmw.interval_us;
|
||||
|
||||
/* process CANopen objects */
|
||||
*reset = CO_process(CO, diff, &timerNext_us);
|
||||
|
||||
/* lower next timer interval if necessary */
|
||||
if (timerNext_us < threadMainWait.interval_us) {
|
||||
if (timerNext_us < tmw.interval_us) {
|
||||
int ret;
|
||||
/* add one microsecond extra delay and make sure it is not zero */
|
||||
timerNext_us += 1;
|
||||
if (threadMainWait.interval_us < 1000000) {
|
||||
threadMainWait.tm.it_value.tv_nsec = timerNext_us * 1000;
|
||||
} else {
|
||||
threadMainWait.tm.it_value.tv_sec = timerNext_us / 1000000;
|
||||
threadMainWait.tm.it_value.tv_nsec = (timerNext_us % 1000000) * 1000;
|
||||
if (tmw.interval_us < 1000000) {
|
||||
tmw.tm.it_value.tv_nsec = timerNext_us * 1000;
|
||||
}
|
||||
ret = timerfd_settime(threadMainWait.timer_fd, 0,
|
||||
&threadMainWait.tm, NULL);
|
||||
if (ret < 0){
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "timerfd_settime");
|
||||
else {
|
||||
tmw.tm.it_value.tv_sec = timerNext_us / 1000000;
|
||||
tmw.tm.it_value.tv_nsec = (timerNext_us % 1000000) * 1000;
|
||||
}
|
||||
ret = timerfd_settime(tmw.timer_fd, 0, &tmw.tm, NULL);
|
||||
if (ret < 0) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "timerfd_settime");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +536,7 @@ uint32_t CANrx_threadTmr_process(void)
|
|||
if (result < 0) {
|
||||
result = read(threadRT.interval_fd, &missed, sizeof(missed));
|
||||
if (result > 0) {
|
||||
/* at least one timer interval occured */
|
||||
/* at least one timer interval occurred */
|
||||
CO_LOCK_OD();
|
||||
|
||||
if(CO->CANmodule[0]->CANnormal) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,20 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/**
|
||||
* Command interface type for gateway-ascii
|
||||
*/
|
||||
typedef enum {
|
||||
CO_COMMAND_IF_DISABLED = -100,
|
||||
CO_COMMAND_IF_STDIO = -2,
|
||||
CO_COMMAND_IF_LOCAL_SOCKET = -1,
|
||||
CO_COMMAND_IF_TCP_SOCKET_MIN = 0,
|
||||
CO_COMMAND_IF_TCP_SOCKET_MAX = 0xFFFF
|
||||
} CO_commandInterface_t;
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup CO_socketCAN socketCAN
|
||||
* @{
|
||||
|
|
@ -104,9 +118,15 @@ void threadMainWait_init(void);
|
|||
*
|
||||
* Function must be called only once, before node starts operating.
|
||||
*
|
||||
* @param interval_us interval of the threadMainWait_process()
|
||||
* @param interval_us Interval of the threadMainWait_process()
|
||||
* @param commandInterface Command interface type from CO_commandInterface_t
|
||||
* @param socketTimeout_ms Timeout for established socket connection in [ms]
|
||||
* @param localSocketPath File path, if commandInterface is local socket
|
||||
*/
|
||||
void threadMainWait_initOnce(uint32_t interval_us);
|
||||
void threadMainWait_initOnce(uint32_t interval_us,
|
||||
int32_t commandInterface,
|
||||
uint32_t socketTimeout_ms,
|
||||
char *localSocketPath);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -169,7 +189,7 @@ void CANrx_threadTmr_close(void);
|
|||
*
|
||||
* @return Number of interval timer passes since last call.
|
||||
*/
|
||||
void CANrx_threadTmr_process(void);
|
||||
uint32_t CANrx_threadTmr_process(void);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
|
|
|||
|
|
@ -128,10 +128,10 @@ int CO_OD_storage_saveSecure(
|
|||
FILE *fp = fopen(filename, "w");
|
||||
if(fp != NULL) {
|
||||
|
||||
CO_LOCK_OD();
|
||||
//CO_LOCK_OD();
|
||||
fwrite((const void *)odAddress, 1, odSize, fp);
|
||||
CRC = crc16_ccitt((unsigned char*)odAddress, odSize, 0);
|
||||
CO_UNLOCK_OD();
|
||||
//CO_UNLOCK_OD();
|
||||
|
||||
fwrite((const void *)&CRC, 1, 2, fp);
|
||||
fclose(fp);
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@
|
|||
*/
|
||||
|
||||
|
||||
#ifndef CO_DRIVER_TARGET
|
||||
#define CO_DRIVER_TARGET
|
||||
|
||||
/* This file contains device and application specific definitions.
|
||||
* It is included from CO_driver.h, which contains documentation
|
||||
* for common definitions below. */
|
||||
|
||||
#ifndef CO_DRIVER_TARGET
|
||||
#define CO_DRIVER_TARGET
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
#include <linux/can.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#if __has_include("CO_driver_custom.h")
|
||||
#ifdef CO_DRIVER_CUSTOM
|
||||
#include "CO_driver_custom.h"
|
||||
#endif
|
||||
#include "CO_error.h"
|
||||
|
|
@ -117,7 +117,10 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#ifndef CO_CONFIG_GTW
|
||||
#define CO_CONFIG_GTW (CO_CONFIG_GTW_ASCII)
|
||||
#define CO_CONFIG_GTW (CO_CONFIG_GTW_ASCII | \
|
||||
CO_CONFIG_GTW_ASCII_ERROR_DESC | \
|
||||
CO_CONFIG_GTW_ASCII_PRINT_HELP)
|
||||
#define CO_CONFIG_GTW_BLOCK_DL_LOOP 3
|
||||
#define CO_CONFIG_GTWA_COMM_BUF_SIZE 2000
|
||||
#endif
|
||||
|
||||
|
|
@ -194,8 +197,15 @@ extern "C" {
|
|||
#ifdef __BYTE_ORDER
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define CO_LITTLE_ENDIAN
|
||||
#define CO_SWAP_16(x) x
|
||||
#define CO_SWAP_32(x) x
|
||||
#define CO_SWAP_64(x) x
|
||||
#else
|
||||
#define CO_BIG_ENDIAN
|
||||
#include <byteswap.h>
|
||||
#define CO_SWAP_16(x) bswap_16(x)
|
||||
#define CO_SWAP_32(x) bswap_32(x)
|
||||
#define CO_SWAP_64(x) bswap_64(x)
|
||||
#endif
|
||||
#endif
|
||||
/* #define CO_USE_LEDS */
|
||||
|
|
@ -204,7 +214,7 @@ extern "C" {
|
|||
/* int8_t to uint64_t are defined in stdint.h */
|
||||
typedef unsigned char bool_t;
|
||||
typedef float float32_t;
|
||||
typedef long double float64_t;
|
||||
typedef double float64_t;
|
||||
typedef char char_t;
|
||||
typedef unsigned char oChar_t;
|
||||
typedef unsigned char domain_t;
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ extern "C" {
|
|||
/*
|
||||
* Message definitions for Linux CANopen socket driver (notice and errors)
|
||||
*/
|
||||
#define CAN_NOT_FOUND "CAN Interface \"%s\" not found"
|
||||
#define CAN_INIT_FAILED "CAN Interface \"%s\" Init failed"
|
||||
#define CAN_NAMETOINDEX "Interface \"%s\" -> Index %d"
|
||||
#define CAN_NOT_FOUND "(%s) CAN Interface \"%s\" not found", __func__
|
||||
#define CAN_INIT_FAILED "(%s) CAN Interface \"%s\" Init failed", __func__
|
||||
#define CAN_BINDING_FAILED "(%s) Binding CAN Interface \"%s\" failed", __func__
|
||||
#define CAN_ERROR_FILTER_FAILED "(%s) Setting CAN Interface \"%s\" error filter failed", __func__
|
||||
#define CAN_FILTER_FAILED "(%s) Setting CAN Interface \"%s\" message filter failed", __func__
|
||||
#define CAN_NAMETOINDEX "CAN Interface \"%s\" -> Index %d"
|
||||
#define CAN_SOCKET_BUF_SIZE "CAN Interface \"%s\" Buffer set to %d messages (%d Bytes)"
|
||||
#define CAN_BINDING_FAILED "Binding CAN Interface \"%s\" failed"
|
||||
#define CAN_ERROR_FILTER_FAILED "Setting CAN Interface \"%s\" error filter failed"
|
||||
#define CAN_FILTER_FAILED "Setting CAN Interface \"%s\" message filter failed"
|
||||
#define CAN_RX_SOCKET_QUEUE_OVERFLOW "CAN Interface \"%s\" has lost %d messages"
|
||||
#define CAN_BUSOFF "CAN Interface \"%s\" changed to \"Bus Off\". Switching to Listen Only mode..."
|
||||
#define CAN_NOACK "CAN Interface \"%s\" no \"ACK\" received. Switching to Listen Only mode..."
|
||||
|
|
@ -81,15 +81,21 @@ extern "C" {
|
|||
#define DBG_EMERGENCY_RX "CANopen Emergency message from node 0x%02X: errorCode=0x%04X, errorRegister=0x%02X, errorBit=0x%02X, infoCode=0x%08X"
|
||||
#define DBG_NMT_CHANGE "CANopen NMT state changed to: \"%s\" (%d)"
|
||||
#define DBG_HB_CONS_NMT_CHANGE "CANopen Remote node ID = 0x%02X: NMT state changed to: \"%s\" (%d)"
|
||||
#define DBG_NOT_TCP_PORT "(%s) -t argument \'%s\' is not a valid tcp port", __func__
|
||||
#define DBG_ARGUMENT_UNKNOWN "(%s) Unknown %s argument: \"%s\"", __func__
|
||||
#define DBG_NOT_TCP_PORT "(%s) -c argument \"%s\" is not a valid tcp port", __func__
|
||||
#define DBG_WRONG_NODE_ID "(%s) Wrong node ID \"%d\"", __func__
|
||||
#define DBG_WRONG_PRIORITY "(%s) Wrong RT priority \"%d\"", __func__
|
||||
#define DBG_NO_CAN_DEVICE "(%s) Can't find CAN device \"%s\"", __func__
|
||||
#define DBG_OBJECT_DICTIONARY "(%s) Error in Object Dictionary \"%s\"", __func__
|
||||
#define DBG_CAN_OPEN "(%s) CANopen error in %s, err=%d", __func__
|
||||
#define DBG_CAN_OPEN_INFO "(%s) CANopen device, Node ID = 0x%02X, %s", __func__
|
||||
#define DBG_COMMAND_LOCAL_INFO "(%s) Command interface on socket \"%s\" started", __func__
|
||||
#define DBG_COMMAND_TCP_INFO "(%s) Command interface on tcp port \"%hu\" started", __func__
|
||||
#define DBG_CAN_OPEN_INFO "CANopen device, Node ID = 0x%02X, %s"
|
||||
|
||||
/* CO_Linux_threads */
|
||||
#define DBG_COMMAND_LOCAL_BIND "(%s) Can't bind local socket to path \"%s\"", __func__
|
||||
#define DBG_COMMAND_TCP_BIND "(%s) Can't bind tcp socket to port \"%d\"", __func__
|
||||
#define DBG_COMMAND_STDIO_INFO "CANopen command interface on \"standard IO\" started"
|
||||
#define DBG_COMMAND_LOCAL_INFO "CANopen command interface on local socket \"%s\" started"
|
||||
#define DBG_COMMAND_TCP_INFO "CANopen command interface on tcp port \"%d\" started"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@
|
|||
|
||||
/* Use DS309-3 standard - ASCII command interface to CANopen: NMT master,
|
||||
* LSS master and SDO client */
|
||||
#if CO_CONFIG_309 > 0
|
||||
#include "CO_command.h"
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
#include "309/CO_gateway_ascii.h"
|
||||
#endif
|
||||
|
||||
/* Interval of mainline and real-time thread in microseconds */
|
||||
|
|
@ -67,12 +67,6 @@
|
|||
#endif
|
||||
|
||||
|
||||
#if CO_CONFIG_309 > 0
|
||||
/* Mutex is locked, when CAN is not valid (configuration state). May be used
|
||||
* from command interface. RT threads may use CO->CANmodule[0]->CANnormal instead. */
|
||||
pthread_mutex_t CO_CAN_VALID_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
#endif
|
||||
|
||||
/* Other variables and objects */
|
||||
static int rtPriority = -1; /* Real time priority, configurable by arguments. (-1=RT disabled) */
|
||||
static int CO_ownNodeId = -1; /* Use value from Object Dictionary or set to 1..127 by arguments */
|
||||
|
|
@ -80,9 +74,6 @@ static CO_OD_storage_t odStor; /* Object Dictionary storage obj
|
|||
static CO_OD_storage_t odStorAuto; /* Object Dictionary storage object for CO_OD_EEPROM */
|
||||
static char *odStorFile_rom = "od_storage"; /* Name of the file */
|
||||
static char *odStorFile_eeprom = "od_storage_auto"; /* Name of the file */
|
||||
#if CO_CONFIG_309 > 0
|
||||
static in_port_t CO_command_socket_tcp_port = 60000; /* default port when used in tcp gateway mode */
|
||||
#endif
|
||||
#if CO_NO_TRACE > 0
|
||||
static CO_time_t CO_time; /* Object for current time */
|
||||
#endif
|
||||
|
|
@ -152,19 +143,19 @@ printf(
|
|||
" -s <ODstorage file> Set Filename for OD storage ('od_storage' is default).\n"
|
||||
" -a <ODstorageAuto> Set Filename for automatic storage variables from\n"
|
||||
" Object dictionary. ('od_storage_auto' is default).\n");
|
||||
#if CO_CONFIG_309 > 0
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
printf(
|
||||
" -c <Socket path> Enable command interface for master functionality. \n"
|
||||
" If socket path is specified as empty string \"\",\n"
|
||||
" default '%s' will be used.\n"
|
||||
" Note that location of socket path may affect security.\n"
|
||||
" See 'canopencomm/canopencomm --help' for more info.\n"
|
||||
, CO_command_socketPath);
|
||||
printf(
|
||||
" -t <port> Enable command interface for master functionality over\n"
|
||||
" tcp, listen to <port>.\n"
|
||||
" Note that using this mode may affect security.\n"
|
||||
);
|
||||
" -c <interface> Enable command interface for master functionality.\n"
|
||||
" One of three types of interfaces can be specified as:\n"
|
||||
" 1. \"stdio\" - Standard IO of a program (terminal).\n"
|
||||
" 2. \"local-<file path>\" - Local socket interface on file\n"
|
||||
" path, for example \"local-/tmp/CO_command_socket\".\n"
|
||||
" 3. \"tcp-<port>\" - Tcp socket interface on specified \n"
|
||||
" port, for example \"tcp-60000\".\n"
|
||||
" Note that this option may affect security of the CAN.\n"
|
||||
" -T <timeout_time> If -c is specified as local or tcp socket, then this\n"
|
||||
" parameter specifies socket timeout time in milliseconds.\n"
|
||||
" Default is 0 - no timeout on established connection.\n");
|
||||
#endif
|
||||
printf(
|
||||
"\n"
|
||||
|
|
@ -188,9 +179,15 @@ int main (int argc, char *argv[]) {
|
|||
char* CANdevice = NULL; /* CAN device, configurable by arguments. */
|
||||
bool_t nodeIdFromArgs = false; /* True, if program arguments are used for CANopen Node Id */
|
||||
bool_t rebootEnable = false; /* Configurable by arguments */
|
||||
#if CO_CONFIG_309 > 0
|
||||
typedef enum CMD_MODE {CMD_NONE, CMD_LOCAL, CMD_REMOTE} cmdMode_t;
|
||||
cmdMode_t commandEnable = CMD_NONE; /* Configurable by arguments */
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* values from CO_commandInterface_t */
|
||||
int32_t commandInterface = CO_COMMAND_IF_DISABLED;
|
||||
/* local socket path if commandInterface == CO_COMMAND_IF_LOCAL_SOCKET */
|
||||
char *localSocketPath = NULL;
|
||||
uint32_t socketTimeout_ms = 0;
|
||||
#else
|
||||
#define commandInterface 0
|
||||
#define localSocketPath NULL
|
||||
#endif
|
||||
|
||||
/* configure system log */
|
||||
|
|
@ -202,37 +199,51 @@ int main (int argc, char *argv[]) {
|
|||
printUsage(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
while((opt = getopt(argc, argv, "i:p:rc:t:s:a:")) != -1) {
|
||||
while((opt = getopt(argc, argv, "i:p:rc:T:s:a:")) != -1) {
|
||||
const char comm_stdio[] = "stdio";
|
||||
const char comm_local[] = "local-";
|
||||
const char comm_tcp[] = "tcp-";
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
CO_ownNodeId = strtol(optarg, NULL, 0);
|
||||
nodeIdFromArgs = true;
|
||||
break;
|
||||
case 'p': rtPriority = strtol(optarg, NULL, 0); break;
|
||||
case 'r': rebootEnable = true; break;
|
||||
#if CO_CONFIG_309 > 0
|
||||
case 'c':
|
||||
/* In case of empty string keep default name, just enable interface. */
|
||||
if(strlen(optarg) != 0) {
|
||||
CO_command_socketPath = optarg;
|
||||
}
|
||||
commandEnable = CMD_LOCAL;
|
||||
case 'p': rtPriority = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 't':
|
||||
/* In case of empty string keep default port, just enable interface. */
|
||||
if(strlen(optarg) != 0) {
|
||||
//CO_command_socket_tcp_port = optarg;
|
||||
int scanResult = sscanf(optarg, "%hu", &CO_command_socket_tcp_port);
|
||||
if(scanResult != 1){ //expect one argument to be extracted
|
||||
log_printf(LOG_CRIT, DBG_NOT_TCP_PORT, optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
case 'r': rebootEnable = true;
|
||||
break;
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
case 'c':
|
||||
if (strcmp(optarg, comm_stdio) == 0) {
|
||||
commandInterface = CO_COMMAND_IF_STDIO;
|
||||
}
|
||||
commandEnable = CMD_REMOTE;
|
||||
else if (strncmp(optarg, comm_local, strlen(comm_local)) == 0) {
|
||||
commandInterface = CO_COMMAND_IF_LOCAL_SOCKET;
|
||||
localSocketPath = &optarg[6];
|
||||
}
|
||||
else if (strncmp(optarg, comm_tcp, strlen(comm_tcp)) == 0) {
|
||||
const char *portStr = &optarg[4];
|
||||
uint16_t port;
|
||||
int nMatch = sscanf(portStr, "%hu", &port);
|
||||
if(nMatch != 1) {
|
||||
log_printf(LOG_CRIT, DBG_NOT_TCP_PORT, portStr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
commandInterface = port;
|
||||
}
|
||||
else {
|
||||
log_printf(LOG_CRIT, DBG_ARGUMENT_UNKNOWN, "-c", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
socketTimeout_ms = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
#endif
|
||||
case 's': odStorFile_rom = optarg; break;
|
||||
case 'a': odStorFile_eeprom = optarg; break;
|
||||
case 's': odStorFile_rom = optarg;
|
||||
break;
|
||||
case 'a': odStorFile_eeprom = optarg;
|
||||
break;
|
||||
default:
|
||||
printUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
@ -311,11 +322,6 @@ int main (int argc, char *argv[]) {
|
|||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_ownNodeId, "communication reset");
|
||||
|
||||
|
||||
#if CO_CONFIG_309 > 0
|
||||
/* Wait other threads (command interface). */
|
||||
pthread_mutex_lock(&CO_CAN_VALID_mtx);
|
||||
#endif
|
||||
|
||||
/* Wait rt_thread. */
|
||||
if(!firstRun) {
|
||||
CO_LOCK_OD();
|
||||
|
|
@ -376,8 +382,8 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
|
||||
/* Init threadMainWait structure and file descriptors */
|
||||
threadMainWait_initOnce(MAIN_THREAD_INTERVAL_US);
|
||||
|
||||
threadMainWait_initOnce(MAIN_THREAD_INTERVAL_US, commandInterface,
|
||||
socketTimeout_ms, localSocketPath);
|
||||
|
||||
/* Init threadRT structure and file descriptors */
|
||||
CANrx_threadTmr_init(TMR_THREAD_INTERVAL_US);
|
||||
|
|
@ -397,26 +403,6 @@ int main (int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
#if CO_CONFIG_309 > 0
|
||||
/* Initialize socket command interface */
|
||||
switch(commandEnable) {
|
||||
case CMD_LOCAL:
|
||||
if(CO_command_init() != 0) {
|
||||
CO_errExit("Socket command interface initialization failed");
|
||||
}
|
||||
log_printf(LOG_INFO, DBG_COMMAND_LOCAL_INFO, CO_command_socketPath);
|
||||
break;
|
||||
case CMD_REMOTE:
|
||||
if(CO_command_init_tcp(CO_command_socket_tcp_port) != 0) {
|
||||
CO_errExit("Socket command interface initialization failed");
|
||||
}
|
||||
log_printf(LOG_INFO, DBG_COMMAND_TCP_INFO, CO_command_socket_tcp_port);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CO_USE_APPLICATION
|
||||
/* Execute optional additional application code */
|
||||
app_programStart();
|
||||
|
|
@ -433,11 +419,6 @@ int main (int argc, char *argv[]) {
|
|||
/* start CAN */
|
||||
CO_CANsetNormalMode(CO->CANmodule[0]);
|
||||
|
||||
#if CO_CONFIG_309 > 0
|
||||
pthread_mutex_unlock(&CO_CAN_VALID_mtx);
|
||||
#endif
|
||||
|
||||
|
||||
reset = CO_RESET_NOT;
|
||||
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_ownNodeId, "running ...");
|
||||
|
|
@ -458,22 +439,6 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
/* program exit ***************************************************************/
|
||||
/* join threads */
|
||||
#if CO_CONFIG_309 > 0
|
||||
switch (commandEnable)
|
||||
{
|
||||
case CMD_LOCAL:
|
||||
if (CO_command_clear() != 0) {
|
||||
CO_errExit("Socket command interface removal failed");
|
||||
}
|
||||
break;
|
||||
case CMD_REMOTE:
|
||||
//nothing to do yet
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
CO_endProgram = 1;
|
||||
if (pthread_join(rt_thread_id, NULL) != 0) {
|
||||
log_printf(LOG_CRIT, DBG_ERRNO, "pthread_join()");
|
||||
|
|
@ -517,6 +482,7 @@ static void* rt_thread(void* arg) {
|
|||
/* Endless loop */
|
||||
while(CO_endProgram == 0) {
|
||||
|
||||
/* function may skip some milliseconds. Number of missed is returned */
|
||||
CANrx_threadTmr_process();
|
||||
|
||||
#if CO_NO_TRACE > 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue