From 2d7590a407a4ec983f4c904992a4f475f6164bf8 Mon Sep 17 00:00:00 2001 From: Miles Simpson Date: Thu, 8 Oct 2020 23:15:50 -0700 Subject: [PATCH 01/21] Added additional missing time object checks (#234) --- CANopen.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CANopen.c b/CANopen.c index 2c193d229..8f69001e5 100644 --- a/CANopen.c +++ b/CANopen.c @@ -188,8 +188,6 @@ CO_ReturnError_t CO_new(uint32_t *heapMemoryUsed) { CO->TIME = (CO_TIME_t *)calloc(1, sizeof(CO_TIME_t)); if (CO->TIME == NULL) errCnt++; CO_memoryUsed += sizeof(CO_TIME_t); -#else - CO->TIME = NULL; #endif #if CO_NO_GFC == 1 @@ -492,8 +490,6 @@ CO_ReturnError_t CO_new(uint32_t *heapMemoryUsed) { #if CO_NO_TIME == 1 /* TIME */ CO->TIME = &COO_TIME; -#else - CO->TIME = NULL; #endif #if CO_NO_GFC == 1 From 5350ecbda01ed044bb5cfd3e5842e57d4906517a Mon Sep 17 00:00:00 2001 From: Miles Simpson Date: Thu, 8 Oct 2020 23:18:31 -0700 Subject: [PATCH 02/21] Suppressed unused heapMemoryUsed warning when using CO_USE_GLOBAL (#235) --- CANopen.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CANopen.c b/CANopen.c index 8f69001e5..c14f61449 100644 --- a/CANopen.c +++ b/CANopen.c @@ -456,6 +456,8 @@ void CO_delete(void *CANptr) { CO_ReturnError_t CO_new(uint32_t *heapMemoryUsed) { int16_t i; + (void)heapMemoryUsed; + /* If CANopen was initialized before, return. */ if (CO != NULL) { return CO_ERROR_NO; From a128fd699eb9a044b07529376c152e1af7350bef Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Fri, 9 Oct 2020 09:34:55 +0200 Subject: [PATCH 03/21] Make "remote NMT changed" callback of HB consumer node-specific (#237) * Implement a better solution for #229 Instead of playing with incrementing pointers in various places it is better to just set it once at the start of loop. * Make "remote NMT changed" callback of HB consumer node-specific This way "remote NMT changed" callback is much more similar to other "multi" callbacks of HB consumer, and it was node-specific anyway, as you have to first configure the monitoring node to have that callback executed. This change breaks the API. * Merge CO_CONFIG_HB_CONS_CALLBACK_CHANGE with ..._HB_CONS_CALLBACK_MULTI These settings are now very similar, so there's no need to have two separate config options. This change breaks the configuration API. --- 301/CO_HBconsumer.c | 43 ++++++++++++++++-------------------- 301/CO_HBconsumer.h | 29 ++++++++++-------------- 301/CO_config.h | 9 +++----- example/CO_driver_target.h | 1 - socketCAN/CO_driver_target.h | 2 +- socketCAN/CO_main_basic.c | 7 +++--- 6 files changed, 39 insertions(+), 52 deletions(-) diff --git a/301/CO_HBconsumer.c b/301/CO_HBconsumer.c index a8515985f..5e71af339 100644 --- a/301/CO_HBconsumer.c +++ b/301/CO_HBconsumer.c @@ -125,9 +125,6 @@ CO_ReturnError_t CO_HBconsumer_init( HBcons->NMTisPreOrOperationalPrev = false; HBcons->CANdevRx = CANdevRx; HBcons->CANdevRxIdxStart = CANdevRxIdxStart; -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE - HBcons->pFunctSignalNmtChanged = NULL; -#endif for(i=0; inumberOfMonitoredNodes; i++) { uint8_t nodeId = (HBcons->HBconsTime[i] >> 16U) & 0xFFU; @@ -137,6 +134,7 @@ CO_ReturnError_t CO_HBconsumer_init( HBcons->monitoredNodes[i].pFunctSignalPre = NULL; #endif #if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI + HBcons->monitoredNodes[i].pFunctSignalNmtChanged = NULL; HBcons->monitoredNodes[i].pFunctSignalHbStarted = NULL; HBcons->monitoredNodes[i].pFunctSignalTimeout = NULL; HBcons->monitoredNodes[i].pFunctSignalRemoteReset = NULL; @@ -185,7 +183,7 @@ CO_ReturnError_t CO_HBconsumer_initEntry( monitoredNode->nodeId = nodeId; monitoredNode->time_us = (int32_t)consumerTime_ms * 1000; monitoredNode->NMTstate = CO_NMT_UNKNOWN; -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI monitoredNode->NMTstatePrev = CO_NMT_UNKNOWN; #endif CO_FLAG_CLEAR(monitoredNode->CANrxNew); @@ -234,26 +232,26 @@ void CO_HBconsumer_initCallbackPre( #endif -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI /******************************************************************************/ void CO_HBconsumer_initCallbackNmtChanged( CO_HBconsumer_t *HBcons, + uint8_t idx, void *object, - void (*pFunctSignal)(uint8_t nodeId, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t NMTstate, void *object)) { - if (HBcons==NULL) { + if (HBcons==NULL || idx>=HBcons->numberOfMonitoredNodes) { return; } - HBcons->pFunctSignalNmtChanged = pFunctSignal; - HBcons->pFunctSignalObjectNmtChanged = object; + CO_HBconsNode_t * const monitoredNode = &HBcons->monitoredNodes[idx]; + monitoredNode->pFunctSignalNmtChanged = pFunctSignal; + monitoredNode->pFunctSignalObjectNmtChanged = object; } -#endif -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI /******************************************************************************/ void CO_HBconsumer_initCallbackHeartbeatStarted( CO_HBconsumer_t *HBcons, @@ -321,18 +319,16 @@ void CO_HBconsumer_process( { (void)timerNext_us; /* may be unused */ - uint8_t i; bool_t allMonitoredActiveCurrent = true; uint8_t allMonitoredOperationalCurrent = CO_NMT_OPERATIONAL; - CO_HBconsNode_t *monitoredNode = &HBcons->monitoredNodes[0]; if (NMTisPreOrOperational && HBcons->NMTisPreOrOperationalPrev) { - for (i=0; inumberOfMonitoredNodes; i++) { + for (uint8_t i=0; inumberOfMonitoredNodes; i++) { uint32_t timeDifference_us_copy = timeDifference_us; + CO_HBconsNode_t * const monitoredNode = &HBcons->monitoredNodes[i]; if (monitoredNode->HBstate == CO_HBconsumer_UNCONFIGURED) { /* continue, if node is not monitored */ - monitoredNode++; continue; } /* Verify if received message is heartbeat or bootup */ @@ -409,32 +405,31 @@ void CO_HBconsumer_process( if (monitoredNode->NMTstate != CO_NMT_OPERATIONAL) { allMonitoredOperationalCurrent = CO_NMT_UNKNOWN; } -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI /* Verify, if NMT state of monitored node changed */ if(monitoredNode->NMTstate != monitoredNode->NMTstatePrev) { - if (HBcons->pFunctSignalNmtChanged != NULL) { - HBcons->pFunctSignalNmtChanged( - monitoredNode->nodeId, monitoredNode->NMTstate, - HBcons->pFunctSignalObjectNmtChanged); + if (monitoredNode->pFunctSignalNmtChanged != NULL) { + monitoredNode->pFunctSignalNmtChanged( + monitoredNode->nodeId, i, monitoredNode->NMTstate, + monitoredNode->pFunctSignalObjectNmtChanged); } monitoredNode->NMTstatePrev = monitoredNode->NMTstate; } #endif - monitoredNode++; } } else if (NMTisPreOrOperational || HBcons->NMTisPreOrOperationalPrev) { /* (pre)operational state changed, clear variables */ - for(i=0; inumberOfMonitoredNodes; i++) { + for(uint8_t i=0; inumberOfMonitoredNodes; i++) { + CO_HBconsNode_t * const monitoredNode = &HBcons->monitoredNodes[i]; monitoredNode->NMTstate = CO_NMT_UNKNOWN; -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI monitoredNode->NMTstatePrev = CO_NMT_UNKNOWN; #endif CO_FLAG_CLEAR(monitoredNode->CANrxNew); if (monitoredNode->HBstate != CO_HBconsumer_UNCONFIGURED) { monitoredNode->HBstate = CO_HBconsumer_UNKNOWN; } - monitoredNode++; } allMonitoredActiveCurrent = false; allMonitoredOperationalCurrent = CO_NMT_UNKNOWN; diff --git a/301/CO_HBconsumer.h b/301/CO_HBconsumer.h index 2a317599e..73d1aba7a 100644 --- a/301/CO_HBconsumer.h +++ b/301/CO_HBconsumer.h @@ -91,11 +91,16 @@ typedef struct { /** From CO_HBconsumer_initCallbackPre() or NULL */ void *functSignalObjectPre; #endif -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) || defined CO_DOXYGEN +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN /** Previous value of the remote node (Heartbeat payload) */ CO_NMT_internalState_t NMTstatePrev; -#endif -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN + /** Callback for remote NMT changed event. + * From CO_HBconsumer_initCallbackNmtChanged() or NULL. */ + void (*pFunctSignalNmtChanged)(uint8_t nodeId, uint8_t idx, + CO_NMT_internalState_t state, + void *object); + /** Pointer to object */ + void *pFunctSignalObjectNmtChanged; /** Callback for heartbeat state change to active event. * From CO_HBconsumer_initCallbackHeartbeatStarted() or NULL. */ void (*pFunctSignalHbStarted)(uint8_t nodeId, uint8_t idx, void *object); @@ -135,15 +140,6 @@ typedef struct{ bool_t NMTisPreOrOperationalPrev; /**< previous state of var */ CO_CANmodule_t *CANdevRx; /**< From CO_HBconsumer_init() */ uint16_t CANdevRxIdxStart; /**< From CO_HBconsumer_init() */ -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) || defined CO_DOXYGEN - /** Callback for remote NMT changed event. - * From CO_HBconsumer_initCallbackNmtChanged() or NULL. */ - void (*pFunctSignalNmtChanged)(uint8_t nodeId, - CO_NMT_internalState_t state, - void *object); - /** Pointer to object */ - void *pFunctSignalObjectNmtChanged; -#endif }CO_HBconsumer_t; @@ -214,7 +210,7 @@ void CO_HBconsumer_initCallbackPre( void (*pFunctSignal)(void *object)); #endif -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) || defined CO_DOXYGEN +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN /** * Initialize Heartbeat consumer NMT changed callback function. * @@ -222,19 +218,19 @@ void CO_HBconsumer_initCallbackPre( * state from the remote node changes. * * @param HBcons This object. + * @param idx index of the node in HBcons object * @param object Pointer to object, which will be passed to pFunctSignal(). * Can be NULL. * @param pFunctSignal Pointer to the callback function. Not called if NULL. */ void CO_HBconsumer_initCallbackNmtChanged( CO_HBconsumer_t *HBcons, + uint8_t idx, void *object, - void (*pFunctSignal)(uint8_t nodeId, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t state, void *object)); -#endif -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN /** * Initialize Heartbeat consumer started callback function. * @@ -253,7 +249,6 @@ void CO_HBconsumer_initCallbackHeartbeatStarted( void *object, void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, void *object)); - /** * Initialize Heartbeat consumer timeout callback function. * diff --git a/301/CO_config.h b/301/CO_config.h index d455b99ec..887ef474e 100644 --- a/301/CO_config.h +++ b/301/CO_config.h @@ -135,11 +135,9 @@ extern "C" { * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable * inside CO_HBconsumer_process(). * - CO_CONFIG_HB_CONS_ENABLE - Enable heartbeat consumer. - * - CO_CONFIG_HB_CONS_CALLBACK_CHANGE - Enable custom callback after NMT - * state of the monitored node changes. Callback is configured by - * CO_HBconsumer_initCallbackNmtChanged(). * - CO_CONFIG_HB_CONS_CALLBACK_MULTI - Enable multiple custom callbacks, which * can be configured for each monitored node. Callback are configured by + * CO_HBconsumer_initCallbackNmtChanged(), * CO_HBconsumer_initCallbackHeartbeatStarted(), * CO_HBconsumer_initCallbackTimeout() and * CO_HBconsumer_initCallbackRemoteReset() functions. @@ -150,9 +148,8 @@ extern "C" { #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE) #endif #define CO_CONFIG_HB_CONS_ENABLE 0x01 -#define CO_CONFIG_HB_CONS_CALLBACK_CHANGE 0x02 -#define CO_CONFIG_HB_CONS_CALLBACK_MULTI 0x04 -#define CO_CONFIG_HB_CONS_QUERY_FUNCT 0x08 +#define CO_CONFIG_HB_CONS_CALLBACK_MULTI 0x02 +#define CO_CONFIG_HB_CONS_QUERY_FUNCT 0x04 /** * Number of heartbeat consumer objects, where each object corresponds to one diff --git a/example/CO_driver_target.h b/example/CO_driver_target.h index c573a5201..babdf6994 100644 --- a/example/CO_driver_target.h +++ b/example/CO_driver_target.h @@ -53,7 +53,6 @@ extern "C" { #ifndef CO_CONFIG_HB_CONS #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | \ - CO_CONFIG_HB_CONS_CALLBACK_CHANGE | \ CO_CONFIG_HB_CONS_CALLBACK_MULTI | \ CO_CONFIG_HB_CONS_QUERY_FUNCT | \ CO_CONFIG_FLAG_CALLBACK_PRE | \ diff --git a/socketCAN/CO_driver_target.h b/socketCAN/CO_driver_target.h index 6d22de939..7c1632de0 100644 --- a/socketCAN/CO_driver_target.h +++ b/socketCAN/CO_driver_target.h @@ -70,7 +70,7 @@ extern "C" { #ifndef CO_CONFIG_HB_CONS #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | \ - CO_CONFIG_HB_CONS_CALLBACK_CHANGE | \ + CO_CONFIG_HB_CONS_CALLBACK_MULTI | \ CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ CO_CONFIG_FLAG_TIMERNEXT) #endif diff --git a/socketCAN/CO_main_basic.c b/socketCAN/CO_main_basic.c index 3987f668d..f6ba3ac2f 100644 --- a/socketCAN/CO_main_basic.c +++ b/socketCAN/CO_main_basic.c @@ -167,7 +167,7 @@ static void NmtChangedCallback(CO_NMT_internalState_t state) } /* callback for monitoring Heartbeat remote NMT state change */ -static void HeartbeatNmtChangedCallback(uint8_t nodeId, +static void HeartbeatNmtChangedCallback(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t state, void *object) { @@ -479,8 +479,9 @@ int main (int argc, char *argv[]) { if(!CO->nodeIdUnconfigured) { CO_EM_initCallbackRx(CO->em, EmergencyRxCallback); CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback); - CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL, - HeartbeatNmtChangedCallback); + for (size_t idx = 0; idx < CO->HBcons->numberOfMonitoredNodes; ++idx) + CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, idx, NULL, + HeartbeatNmtChangedCallback); #if CO_OD_STORAGE == 1 /* initialize OD objects 1010 and 1011 and verify errors. */ CO_OD_configure(CO->SDO[0], OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)&odStor, 0, 0U); From 2c9d07f2652ffce2a48e44b27d140d95cde92ab0 Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Sat, 24 Oct 2020 14:42:38 +0200 Subject: [PATCH 04/21] Restore CO_CONFIG_HB_CONS_CALLBACK_CHANGE option The user may decide whether he/she wants one common callback for all monitored nodes (CO_CONFIG_HB_CONS_CALLBACK_CHANGE) or maybe separate callbacks configured individually for each monitored mode (CO_CONFIG_HB_CONS_CALLBACK_MULTI). These options cannot be set simultaneously. This change is related to the discussion in #237 --- 301/CO_HBconsumer.c | 44 +++++++++++++++++++++++++++++++++--- 301/CO_HBconsumer.h | 26 ++++++++++++++++++--- 301/CO_config.h | 14 ++++++++---- socketCAN/CO_driver_target.h | 2 +- socketCAN/CO_error_msgs.h | 2 +- socketCAN/CO_main_basic.c | 7 +++--- 6 files changed, 79 insertions(+), 16 deletions(-) diff --git a/301/CO_HBconsumer.c b/301/CO_HBconsumer.c index 5e71af339..e2b09f62a 100644 --- a/301/CO_HBconsumer.c +++ b/301/CO_HBconsumer.c @@ -31,6 +31,12 @@ #if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE +/* Verify HB consumer configuration *******************************************/ +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE \ + && (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI +#error CO_CONFIG_HB_CONS_CALLBACK_CHANGE and CO_CONFIG_HB_CONS_CALLBACK_MULTI cannot be set simultaneously! +#endif + /* * Read received message from CAN module. * @@ -125,6 +131,9 @@ CO_ReturnError_t CO_HBconsumer_init( HBcons->NMTisPreOrOperationalPrev = false; HBcons->CANdevRx = CANdevRx; HBcons->CANdevRxIdxStart = CANdevRxIdxStart; +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE + HBcons->pFunctSignalNmtChanged = NULL; +#endif for(i=0; inumberOfMonitoredNodes; i++) { uint8_t nodeId = (HBcons->HBconsTime[i] >> 16U) & 0xFFU; @@ -183,7 +192,8 @@ CO_ReturnError_t CO_HBconsumer_initEntry( monitoredNode->nodeId = nodeId; monitoredNode->time_us = (int32_t)consumerTime_ms * 1000; monitoredNode->NMTstate = CO_NMT_UNKNOWN; -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE \ + || (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI monitoredNode->NMTstatePrev = CO_NMT_UNKNOWN; #endif CO_FLAG_CLEAR(monitoredNode->CANrxNew); @@ -232,6 +242,25 @@ void CO_HBconsumer_initCallbackPre( #endif +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE +/******************************************************************************/ +void CO_HBconsumer_initCallbackNmtChanged( + CO_HBconsumer_t *HBcons, + void *object, + void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, + CO_NMT_internalState_t NMTstate, + void *object)) +{ + if (HBcons==NULL) { + return; + } + + HBcons->pFunctSignalNmtChanged = pFunctSignal; + HBcons->pFunctSignalObjectNmtChanged = object; +} +#endif + + #if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI /******************************************************************************/ void CO_HBconsumer_initCallbackNmtChanged( @@ -405,13 +434,21 @@ void CO_HBconsumer_process( if (monitoredNode->NMTstate != CO_NMT_OPERATIONAL) { allMonitoredOperationalCurrent = CO_NMT_UNKNOWN; } -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE \ + || (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI /* Verify, if NMT state of monitored node changed */ if(monitoredNode->NMTstate != monitoredNode->NMTstatePrev) { +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE + if (HBcons->pFunctSignalNmtChanged != NULL) { + HBcons->pFunctSignalNmtChanged( + monitoredNode->nodeId, i, monitoredNode->NMTstate, + HBcons->pFunctSignalObjectNmtChanged); +#else if (monitoredNode->pFunctSignalNmtChanged != NULL) { monitoredNode->pFunctSignalNmtChanged( monitoredNode->nodeId, i, monitoredNode->NMTstate, monitoredNode->pFunctSignalObjectNmtChanged); +#endif } monitoredNode->NMTstatePrev = monitoredNode->NMTstate; } @@ -423,7 +460,8 @@ void CO_HBconsumer_process( for(uint8_t i=0; inumberOfMonitoredNodes; i++) { CO_HBconsNode_t * const monitoredNode = &HBcons->monitoredNodes[i]; monitoredNode->NMTstate = CO_NMT_UNKNOWN; -#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE \ + || (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI monitoredNode->NMTstatePrev = CO_NMT_UNKNOWN; #endif CO_FLAG_CLEAR(monitoredNode->CANrxNew); diff --git a/301/CO_HBconsumer.h b/301/CO_HBconsumer.h index 73d1aba7a..421032ff5 100644 --- a/301/CO_HBconsumer.h +++ b/301/CO_HBconsumer.h @@ -91,9 +91,13 @@ typedef struct { /** From CO_HBconsumer_initCallbackPre() or NULL */ void *functSignalObjectPre; #endif -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) \ + || ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) \ + || defined CO_DOXYGEN /** Previous value of the remote node (Heartbeat payload) */ CO_NMT_internalState_t NMTstatePrev; +#endif +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN /** Callback for remote NMT changed event. * From CO_HBconsumer_initCallbackNmtChanged() or NULL. */ void (*pFunctSignalNmtChanged)(uint8_t nodeId, uint8_t idx, @@ -140,6 +144,15 @@ typedef struct{ bool_t NMTisPreOrOperationalPrev; /**< previous state of var */ CO_CANmodule_t *CANdevRx; /**< From CO_HBconsumer_init() */ uint16_t CANdevRxIdxStart; /**< From CO_HBconsumer_init() */ +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) || defined CO_DOXYGEN + /** Callback for remote NMT changed event. + * From CO_HBconsumer_initCallbackNmtChanged() or NULL. */ + void (*pFunctSignalNmtChanged)(uint8_t nodeId, uint8_t idx, + CO_NMT_internalState_t state, + void *object); + /** Pointer to object */ + void *pFunctSignalObjectNmtChanged; +#endif }CO_HBconsumer_t; @@ -210,7 +223,9 @@ void CO_HBconsumer_initCallbackPre( void (*pFunctSignal)(void *object)); #endif -#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) \ + || ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) \ + || defined CO_DOXYGEN /** * Initialize Heartbeat consumer NMT changed callback function. * @@ -218,19 +233,24 @@ void CO_HBconsumer_initCallbackPre( * state from the remote node changes. * * @param HBcons This object. - * @param idx index of the node in HBcons object + * @param idx index of the node in HBcons object (only when + * CO_CONFIG_HB_CONS_CALLBACK_MULTI is enabled) * @param object Pointer to object, which will be passed to pFunctSignal(). * Can be NULL. * @param pFunctSignal Pointer to the callback function. Not called if NULL. */ void CO_HBconsumer_initCallbackNmtChanged( CO_HBconsumer_t *HBcons, +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN uint8_t idx, +#endif void *object, void (*pFunctSignal)(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t state, void *object)); +#endif +#if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_MULTI) || defined CO_DOXYGEN /** * Initialize Heartbeat consumer started callback function. * diff --git a/301/CO_config.h b/301/CO_config.h index 887ef474e..58872e64a 100644 --- a/301/CO_config.h +++ b/301/CO_config.h @@ -135,21 +135,27 @@ extern "C" { * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable * inside CO_HBconsumer_process(). * - CO_CONFIG_HB_CONS_ENABLE - Enable heartbeat consumer. + * - CO_CONFIG_HB_CONS_CALLBACK_CHANGE - Enable custom common callback after NMT + * state of the monitored node changes. Callback is configured by + * CO_HBconsumer_initCallbackNmtChanged(). * - CO_CONFIG_HB_CONS_CALLBACK_MULTI - Enable multiple custom callbacks, which - * can be configured for each monitored node. Callback are configured by - * CO_HBconsumer_initCallbackNmtChanged(), + * can be configured individually for each monitored node. Callbacks are + * configured by CO_HBconsumer_initCallbackNmtChanged(), * CO_HBconsumer_initCallbackHeartbeatStarted(), * CO_HBconsumer_initCallbackTimeout() and * CO_HBconsumer_initCallbackRemoteReset() functions. * - CO_CONFIG_HB_CONS_QUERY_FUNCT - Enable functions for query HB state or * NMT state of the specific monitored node. + * Note that CO_CONFIG_HB_CONS_CALLBACK_CHANGE and + * CO_CONFIG_HB_CONS_CALLBACK_MULTI cannot be set simultaneously. */ #ifdef CO_DOXYGEN #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE) #endif #define CO_CONFIG_HB_CONS_ENABLE 0x01 -#define CO_CONFIG_HB_CONS_CALLBACK_MULTI 0x02 -#define CO_CONFIG_HB_CONS_QUERY_FUNCT 0x04 +#define CO_CONFIG_HB_CONS_CALLBACK_CHANGE 0x02 +#define CO_CONFIG_HB_CONS_CALLBACK_MULTI 0x04 +#define CO_CONFIG_HB_CONS_QUERY_FUNCT 0x08 /** * Number of heartbeat consumer objects, where each object corresponds to one diff --git a/socketCAN/CO_driver_target.h b/socketCAN/CO_driver_target.h index 7c1632de0..6d22de939 100644 --- a/socketCAN/CO_driver_target.h +++ b/socketCAN/CO_driver_target.h @@ -70,7 +70,7 @@ extern "C" { #ifndef CO_CONFIG_HB_CONS #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | \ - CO_CONFIG_HB_CONS_CALLBACK_MULTI | \ + CO_CONFIG_HB_CONS_CALLBACK_CHANGE | \ CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ CO_CONFIG_FLAG_TIMERNEXT) #endif diff --git a/socketCAN/CO_error_msgs.h b/socketCAN/CO_error_msgs.h index fc799f20d..655e816e5 100644 --- a/socketCAN/CO_error_msgs.h +++ b/socketCAN/CO_error_msgs.h @@ -70,7 +70,7 @@ extern "C" { /* mainline */ #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_HB_CONS_NMT_CHANGE "CANopen Remote node ID = 0x%02X (index = %d): NMT state changed to: \"%s\" (%d)" #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__ diff --git a/socketCAN/CO_main_basic.c b/socketCAN/CO_main_basic.c index f6ba3ac2f..2685c1846 100644 --- a/socketCAN/CO_main_basic.c +++ b/socketCAN/CO_main_basic.c @@ -173,7 +173,7 @@ static void HeartbeatNmtChangedCallback(uint8_t nodeId, uint8_t idx, { (void)object; log_printf(LOG_NOTICE, DBG_HB_CONS_NMT_CHANGE, - nodeId, NmtState2Str(state), state); + nodeId, idx, NmtState2Str(state), state); } #if CO_OD_STORAGE == 1 @@ -479,9 +479,8 @@ int main (int argc, char *argv[]) { if(!CO->nodeIdUnconfigured) { CO_EM_initCallbackRx(CO->em, EmergencyRxCallback); CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback); - for (size_t idx = 0; idx < CO->HBcons->numberOfMonitoredNodes; ++idx) - CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, idx, NULL, - HeartbeatNmtChangedCallback); + CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL, + HeartbeatNmtChangedCallback); #if CO_OD_STORAGE == 1 /* initialize OD objects 1010 and 1011 and verify errors. */ CO_OD_configure(CO->SDO[0], OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)&odStor, 0, 0U); From ddc2443cfd2765e6132c3a32e4f1c0ccf44ce5a4 Mon Sep 17 00:00:00 2001 From: Freddie Chopin Date: Sat, 24 Oct 2020 14:46:34 +0200 Subject: [PATCH 05/21] Reduce scope of local variables to avoid warning about shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After adding -Wshadow to build options, gcc produced following warning: 305/CO_LSSslave.c: In function ‘CO_LSSslave_receive’: 305/CO_LSSslave.c:114:30: warning: declaration of ‘valSw’ shadows a previous local [-Wshadow] 114 | uint32_t valSw; | ^~~~~ 305/CO_LSSslave.c:76:22: note: shadowed declaration is here 76 | uint32_t valSw; | ^~~~~ --- 305/CO_LSSslave.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/305/CO_LSSslave.c b/305/CO_LSSslave.c index 6e2f7e2cd..72c7c2ea6 100644 --- a/305/CO_LSSslave.c +++ b/305/CO_LSSslave.c @@ -73,24 +73,27 @@ static void CO_LSSslave_receive(void *object, void *msg) } } else if(LSSslave->lssState == CO_LSS_STATE_WAITING) { - uint32_t valSw; switch (cs) { case CO_LSS_SWITCH_STATE_SEL_VENDOR: { + uint32_t valSw; memcpy(&valSw, &data[1], sizeof(valSw)); LSSslave->lssSelect.identity.vendorID = CO_SWAP_32(valSw); break; } case CO_LSS_SWITCH_STATE_SEL_PRODUCT: { + uint32_t valSw; memcpy(&valSw, &data[1], sizeof(valSw)); LSSslave->lssSelect.identity.productCode = CO_SWAP_32(valSw); break; } case CO_LSS_SWITCH_STATE_SEL_REV: { + uint32_t valSw; memcpy(&valSw, &data[1], sizeof(valSw)); LSSslave->lssSelect.identity.revisionNumber = CO_SWAP_32(valSw); break; } case CO_LSS_SWITCH_STATE_SEL_SERIAL: { + uint32_t valSw; memcpy(&valSw, &data[1], sizeof(valSw)); LSSslave->lssSelect.identity.serialNumber = CO_SWAP_32(valSw); From 65464f8f5af250bc7eb0401320a04e2c6ec6ffe5 Mon Sep 17 00:00:00 2001 From: Janez Date: Mon, 26 Oct 2020 17:53:41 +0100 Subject: [PATCH 06/21] Fix gateway socket hangup handling. Speed-up gateway processing. --- socketCAN/CO_epoll_interface.c | 11 +++++++---- socketCAN/CO_main_basic.c | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/socketCAN/CO_epoll_interface.c b/socketCAN/CO_epoll_interface.c index 4a6e5d622..3f1a7715e 100644 --- a/socketCAN/CO_epoll_interface.c +++ b/socketCAN/CO_epoll_interface.c @@ -533,10 +533,6 @@ void CO_epoll_processGtw(CO_epoll_gtw_t *epGtw, && (ep->ev.data.fd == epGtw->gtwa_fdSocket || ep->ev.data.fd == epGtw->gtwa_fd) ) { - if ((ep->ev.events & (EPOLLERR | EPOLLHUP)) != 0) { - log_printf(LOG_DEBUG, DBG_GENERAL, - "socket error or hangup, event=", ep->ev.events); - } if ((ep->ev.events & EPOLLIN) != 0 && ep->ev.data.fd == epGtw->gtwa_fdSocket ) { @@ -626,6 +622,13 @@ void CO_epoll_processGtw(CO_epoll_gtw_t *epGtw, ep->epoll_new = false; } + else if ((ep->ev.events & (EPOLLERR | EPOLLHUP)) != 0) { + log_printf(LOG_DEBUG, DBG_GENERAL, + "socket error or hangup, event=", ep->ev.events); + if (close(epGtw->gtwa_fd) < 0) { + log_printf(LOG_CRIT, DBG_ERRNO, "close(gtwa_fd, hangup)"); + } + } } /* if (ep->epoll_new) */ /* if socket connection is established, verify timeout */ diff --git a/socketCAN/CO_main_basic.c b/socketCAN/CO_main_basic.c index 2685c1846..4e42e83df 100644 --- a/socketCAN/CO_main_basic.c +++ b/socketCAN/CO_main_basic.c @@ -549,10 +549,10 @@ int main (int argc, char *argv[]) { #ifdef CO_SINGLE_THREAD CO_epoll_processRT(&epMain, CO, false); #endif - CO_epoll_processMain(&epMain, CO, &reset); #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII CO_epoll_processGtw(&epGtw, CO, &epMain); #endif + CO_epoll_processMain(&epMain, CO, &reset); CO_epoll_processLast(&epMain); #ifdef CO_USE_APPLICATION From c075bdff3c289107eb03bfca375764b66ef143ad Mon Sep 17 00:00:00 2001 From: Janez Date: Mon, 26 Oct 2020 19:02:43 +0100 Subject: [PATCH 07/21] Remove orphan CO_ODinterface.c from compiling. It is part of next release. Fix configuration macros in socketCAN/CO_main_basic.c --- 301/CO_ODinterface.c | 669 --------------------------------- 301/CO_ODinterface.h | 754 -------------------------------------- Makefile | 1 - doc/CHANGELOG.md | 3 +- doc/objectDictionary.md | 194 ++++++---- example/Makefile | 1 - socketCAN/CO_main_basic.c | 6 +- 7 files changed, 124 insertions(+), 1504 deletions(-) delete mode 100644 301/CO_ODinterface.c delete mode 100644 301/CO_ODinterface.h diff --git a/301/CO_ODinterface.c b/301/CO_ODinterface.c deleted file mode 100644 index cdd2b8d91..000000000 --- a/301/CO_ODinterface.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * CANopen Object Dictionary interface - * - * @file CO_ODinterface.c - * @author Janez Paternoster - * @copyright 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#define OD_DEFINITION -#include "301/CO_ODinterface.h" - - -/******************************************************************************/ -OD_size_t OD_readOriginal(OD_stream_t *stream, uint8_t subIndex, - void *buf, OD_size_t count, ODR_t *returnCode) -{ - (void) subIndex; - - if (stream == NULL || buf == NULL || returnCode == NULL) { - if (returnCode != NULL) *returnCode = ODR_DEV_INCOMPAT; - return 0; - } - - OD_size_t dataLenToCopy = stream->dataLength; /* length of OD variable */ - const char *odData = (const char *)stream->dataObjectOriginal; - - if (odData == NULL) { - *returnCode = ODR_SUB_NOT_EXIST; - return 0; - } - - *returnCode = ODR_OK; - - /* If previous read was partial or OD variable length is larger than - * current buffer len, then data was (will be) read in several segments */ - if (stream->dataOffset > 0 || dataLenToCopy > count) { - if (stream->dataOffset >= dataLenToCopy) { - *returnCode = ODR_DEV_INCOMPAT; - return 0; - } - /* reduce for already copied data */ - dataLenToCopy -= stream->dataOffset; - odData += stream->dataOffset; - - if (dataLenToCopy > count) { - /* not enough space in destionation buffer */ - dataLenToCopy = count; - stream->dataOffset += dataLenToCopy; - *returnCode = ODR_PARTIAL; - } - else { - stream->dataOffset = 0; /* copy finished, reset offset */ - } - } - - CO_LOCK_OD(); - memcpy(buf, odData, dataLenToCopy); - CO_UNLOCK_OD(); - return dataLenToCopy; -} - -/******************************************************************************/ -OD_size_t OD_writeOriginal(OD_stream_t *stream, uint8_t subIndex, - const void *buf, OD_size_t count, ODR_t *returnCode) -{ - (void) subIndex; - - if (stream == NULL || buf == NULL || returnCode == NULL) { - if (returnCode != NULL) *returnCode = ODR_DEV_INCOMPAT; - return 0; - } - - OD_size_t dataLenToCopy = stream->dataLength; /* length of OD variable */ - char *odData = (char *)stream->dataObjectOriginal; - - if (odData == NULL) { - *returnCode = ODR_SUB_NOT_EXIST; - return 0; - } - - *returnCode = ODR_OK; - - /* If previous write was partial or OD variable length is larger than - * current data len, then data was (will be) written in several segments */ - if (stream->dataOffset > 0 || dataLenToCopy > count) { - if (stream->dataOffset >= dataLenToCopy) { - *returnCode = ODR_DEV_INCOMPAT; - return 0; - } - /* reduce for already copied data */ - dataLenToCopy -= stream->dataOffset; - odData += stream->dataOffset; - - if (dataLenToCopy > count) { - /* Reamining data space in OD variable is larger than current count - * of data, so only current count of data will be copied */ - dataLenToCopy = count; - stream->dataOffset += dataLenToCopy; - *returnCode = ODR_PARTIAL; - } - else { - stream->dataOffset = 0; /* copy finished, reset offset */ - } - } - - if (dataLenToCopy < count) { - /* OD variable is smaller than current ammount of data */ - *returnCode = ODR_DATA_LONG; - return 0; - } - else { - CO_LOCK_OD(); - memcpy(odData, buf, dataLenToCopy); - CO_UNLOCK_OD(); - return dataLenToCopy; - } -} - -/* Read value from variable from Object Dictionary disabled, see OD_subEntry_t*/ -static OD_size_t OD_readDisabled(OD_stream_t *stream, uint8_t subIndex, - void *buf, OD_size_t count, - ODR_t *returnCode) -{ - (void) stream; (void) subIndex; (void) buf; (void) count; - - if (returnCode != NULL) *returnCode = ODR_WRITEONLY; - return 0; -} - -/* Write value to variable from Object Dictionary disabled, see OD_subEntry_t */ -static OD_size_t OD_writeDisabled(OD_stream_t *stream, uint8_t subIndex, - const void *buf, OD_size_t count, - ODR_t *returnCode) -{ - (void) stream; (void) subIndex; (void) buf; (void) count; - - if (returnCode != NULL) *returnCode = ODR_READONLY; - return 0; -} - - -/******************************************************************************/ -const OD_entry_t *OD_find(const OD_t *od, uint16_t index) { - unsigned int cur; - unsigned int min = 0; - unsigned int max = od->size - 1; - - if (od == NULL || od->size == 0) { - return NULL; - } - - /* Fast search in ordered Object Dictionary. If indexes are mixed, - * this won't work. If Object Dictionary has up to 2^N entries, then N is - * max number of loop passes. */ - while (min < max) { - /* get entry between min and max */ - cur = (min + max) >> 1; - const OD_entry_t* entry = &od->list[cur]; - - if (index == entry->index) { - return entry; - } - else if (index < entry->index) { - max = cur; - if(max > 0) max--; - } - else { - min = cur + 1; - } - } - - if (min == max) { - const OD_entry_t* entry = &od->list[min]; - if (index == entry->index) { - return entry; - } - } - - return NULL; /* entry does not exist in OD */ -} - -/******************************************************************************/ -ODR_t OD_getSub(const OD_entry_t *entry, uint8_t subIndex, - OD_subEntry_t *subEntry, OD_stream_t *stream, bool_t odOrig) -{ - if (entry == NULL || entry->odObject == NULL) return ODR_IDX_NOT_EXIST; - else if (subIndex > entry->maxSubIndex) return ODR_SUB_NOT_EXIST; - else if (subEntry == NULL || stream == NULL) return ODR_DEV_INCOMPAT; - - const void *odObjectOrig = entry->odObject; - const OD_obj_extended_t *odObjectExt = NULL; - uint8_t odBasicType = entry->odObjectType & ODT_TYPE_MASK; - - /* Is object type extended? */ - if ((entry->odObjectType & ODT_EXTENSION_MASK) != 0) { - odObjectExt = (const OD_obj_extended_t *)odObjectOrig; - odObjectOrig = odObjectExt->odObjectOriginal; - if (odObjectOrig == NULL) return ODR_DEV_INCOMPAT; - } - - /* common properties */ - subEntry->index = entry->index; - subEntry->subIndex = subIndex; - subEntry->maxSubIndex = entry->maxSubIndex; - subEntry->storageGroup = entry->storageGroup; - subEntry->flagsPDO = (odObjectExt != NULL ) ? odObjectExt->flagsPDO : NULL; - stream->dataOffset = 0; - - /* attribute, dataObjectOriginal and dataLength, depends on object type */ - if (odBasicType == ODT_VAR) { - const OD_obj_var_t *odo = (const OD_obj_var_t *)odObjectOrig; - - subEntry->attribute = odo->attribute; - stream->dataObjectOriginal = odo->data; - stream->dataLength = odo->dataLength; - } - else if (odBasicType == ODT_ARR) { - const OD_obj_array_t *odo = (const OD_obj_array_t *)odObjectOrig; - - if (subIndex == 0) { - subEntry->attribute = odo->attribute0; - stream->dataObjectOriginal = odo->data0; - stream->dataLength = 1; - } - else { - char *data = (char *)odo->data; - int i = subIndex - 1; - - subEntry->attribute = odo->attribute; - if (data == NULL) return ODR_DEV_INCOMPAT; - stream->dataObjectOriginal = data + odo->dataElementSizeof * i; - stream->dataLength = odo->dataElementLength; - } - } - else if (odBasicType == ODT_REC) { - const OD_obj_var_t *odo_rec = (const OD_obj_var_t *)odObjectOrig; - const OD_obj_var_t *odo = &odo_rec[subIndex]; - - subEntry->attribute = odo->attribute; - stream->dataObjectOriginal = odo->data; - stream->dataLength = odo->dataLength; - } - else { - return ODR_DEV_INCOMPAT; - } - - /* read, write and dataObject, direct or with IO extension */ - if (odObjectExt == NULL || odObjectExt->extIO == NULL || odOrig) { - subEntry->read = OD_readOriginal; - subEntry->write = OD_writeOriginal; - stream->object = NULL; - } - else { - subEntry->read = odObjectExt->extIO->read != NULL ? - odObjectExt->extIO->read : OD_readDisabled; - subEntry->write = odObjectExt->extIO->write != NULL ? - odObjectExt->extIO->write : OD_writeDisabled; - stream->object = odObjectExt->extIO->object; - } - - return ODR_OK; -} - -/******************************************************************************/ -uint32_t OD_getSDOabCode(ODR_t returnCode) { - static const uint32_t abortCodes[ODR_COUNT] = { - 0x00000000UL, /* No abort */ - 0x05040005UL, /* Out of memory */ - 0x06010000UL, /* Unsupported access to an object */ - 0x06010001UL, /* Attempt to read a write only object */ - 0x06010002UL, /* Attempt to write a read only object */ - 0x06020000UL, /* Object does not exist in the object dictionary */ - 0x06040041UL, /* Object cannot be mapped to the PDO */ - 0x06040042UL, /* Num and len of object to be mapped exceeds PDO len */ - 0x06040043UL, /* General parameter incompatibility reasons */ - 0x06040047UL, /* General internal incompatibility in device */ - 0x06060000UL, /* Access failed due to hardware error */ - 0x06070010UL, /* Data type does not match, length does not match */ - 0x06070012UL, /* Data type does not match, length too high */ - 0x06070013UL, /* Data type does not match, length too short */ - 0x06090011UL, /* Sub index does not exist */ - 0x06090030UL, /* Invalid value for parameter (download only). */ - 0x06090031UL, /* Value range of parameter written too high */ - 0x06090032UL, /* Value range of parameter written too low */ - 0x06090036UL, /* Maximum value is less than minimum value. */ - 0x060A0023UL, /* Resource not available: SDO connection */ - 0x08000000UL, /* General error */ - 0x08000020UL, /* Data cannot be transferred or stored to application */ - 0x08000021UL, /* Data cannot be transf. because of local control */ - 0x08000022UL, /* Data cannot be tran. because of present device state */ - 0x08000023UL, /* Object dict. not present or dynamic generation fails */ - 0x08000024UL /* No data available */ - }; - - return (returnCode < 0 || returnCode >= ODR_COUNT) ? - abortCodes[ODR_DEV_INCOMPAT] : abortCodes[returnCode]; -} - -/******************************************************************************/ -bool_t OD_extensionIO_init(const OD_entry_t *entry, - void *object, - OD_size_t (*read)(OD_stream_t *stream, - uint8_t subIndex, - void *buf, - OD_size_t count, - ODR_t *returnCode), - OD_size_t (*write)(OD_stream_t *stream, - uint8_t subIndex, - const void *buf, - OD_size_t count, - ODR_t *returnCode)) -{ - if (entry == NULL || (entry->odObjectType & ODT_EXTENSION_MASK) == 0) { - return false; - } - - const OD_obj_extended_t *odo = (const OD_obj_extended_t *) entry->odObject; - - if (odo->extIO == NULL) { - return false; - } - - odo->extIO->object = object; - odo->extIO->read = read; - odo->extIO->write = write; - - return true; -} - - -/******************************************************************************/ -ODR_t OD_get_i8(const OD_entry_t *entry, uint8_t subIndex, - int8_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_i16(const OD_entry_t *entry, uint8_t subIndex, - int16_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_i32(const OD_entry_t *entry, uint8_t subIndex, - int32_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_i64(const OD_entry_t *entry, uint8_t subIndex, - int64_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_u8(const OD_entry_t *entry, uint8_t subIndex, - uint8_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_u16(const OD_entry_t *entry, uint8_t subIndex, - uint16_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_u32(const OD_entry_t *entry, uint8_t subIndex, - uint32_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_u64(const OD_entry_t *entry, uint8_t subIndex, - uint64_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_r32(const OD_entry_t *entry, uint8_t subIndex, - float32_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -ODR_t OD_get_r64(const OD_entry_t *entry, uint8_t subIndex, - float64_t *val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(*val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.read(&st, subIndex, val, sizeof(*val), &ret); - return ret; -} - -/******************************************************************************/ -ODR_t OD_set_i8(const OD_entry_t *entry, uint8_t subIndex, - int8_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_i16(const OD_entry_t *entry, uint8_t subIndex, - int16_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_i32(const OD_entry_t *entry, uint8_t subIndex, - int32_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_i64(const OD_entry_t *entry, uint8_t subIndex, - int64_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_u8(const OD_entry_t *entry, uint8_t subIndex, - uint8_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_u16(const OD_entry_t *entry, uint8_t subIndex, - uint16_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_u32(const OD_entry_t *entry, uint8_t subIndex, - uint32_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_u64(const OD_entry_t *entry, uint8_t subIndex, - uint64_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_r32(const OD_entry_t *entry, uint8_t subIndex, - float32_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -ODR_t OD_set_r64(const OD_entry_t *entry, uint8_t subIndex, - float64_t val, bool_t odOrig) -{ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, odOrig); - - if (ret == ODR_OK && st.dataLength != sizeof(val)) ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) subEntry.write(&st, subIndex, &val, sizeof(val), &ret); - return ret; -} - -/******************************************************************************/ -ODR_t OD_getPtr_i8(const OD_entry_t *entry, uint8_t subIndex, int8_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (int8_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_i16(const OD_entry_t *entry, uint8_t subIndex, int16_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (int16_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_i32(const OD_entry_t *entry, uint8_t subIndex, int32_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (int32_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_i64(const OD_entry_t *entry, uint8_t subIndex, int64_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (int64_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_u8(const OD_entry_t *entry, uint8_t subIndex, uint8_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (uint8_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_u16(const OD_entry_t *entry, uint8_t subIndex, uint16_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (uint16_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_u32(const OD_entry_t *entry, uint8_t subIndex, uint32_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (uint32_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_u64(const OD_entry_t *entry, uint8_t subIndex, uint64_t **val) { - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (uint64_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_r32(const OD_entry_t *entry, uint8_t subIndex, float32_t **val){ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (float32_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} - -ODR_t OD_getPtr_r64(const OD_entry_t *entry, uint8_t subIndex, float64_t **val){ - OD_subEntry_t subEntry; OD_stream_t st; - ODR_t ret = OD_getSub(entry, subIndex, &subEntry, &st, true); - - if (ret == ODR_OK && st.dataLength != sizeof(**val))ret = ODR_TYPE_MISMATCH; - if (ret == ODR_OK) *val = (float64_t *)st.dataObjectOriginal; - if (*val == NULL) ret = ODR_DEV_INCOMPAT; - return ret; -} diff --git a/301/CO_ODinterface.h b/301/CO_ODinterface.h deleted file mode 100644 index 3040461c6..000000000 --- a/301/CO_ODinterface.h +++ /dev/null @@ -1,754 +0,0 @@ -/** - * CANopen Object Dictionary interface - * - * @file CO_ODinterface.h - * @ingroup CO_ODinterface - * @author Janez Paternoster - * @copyright 2020 Janez Paternoster - * - * This file is part of CANopenNode, an opensource CANopen Stack. - * Project home page is . - * For more information on CANopen see . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CO_OD_INTERFACE_H -#define CO_OD_INTERFACE_H - -#include "301/CO_driver.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup CO_ODinterface OD interface - * @ingroup CO_CANopen_301 - * @{ - * See @ref doc/objectDictionary.md - */ - -#ifndef OD_size_t -/** Variable of type OD_size_t contains data length in bytes of OD variable */ -#define OD_size_t uint32_t -/** Type of flagsPDO variable from OD_subEntry_t */ -#define OD_flagsPDO_t uint32_t -#endif - -/** Size of Object Dictionary attribute */ -#define OD_attr_t uint8_t - - -/** - * Common DS301 object dictionary entries. - */ -typedef enum { - OD_H1000_DEV_TYPE = 0x1000U,/**< Device type */ - OD_H1001_ERR_REG = 0x1001U,/**< Error register */ - OD_H1002_MANUF_STATUS_REG = 0x1002U,/**< Manufacturer status register */ - OD_H1003_PREDEF_ERR_FIELD = 0x1003U,/**< Predefined error field */ - OD_H1004_RSV = 0x1004U,/**< Reserved */ - OD_H1005_COBID_SYNC = 0x1005U,/**< Sync message cob-id */ - OD_H1006_COMM_CYCL_PERIOD = 0x1006U,/**< Communication cycle period */ - OD_H1007_SYNC_WINDOW_LEN = 0x1007U,/**< Sync windows length */ - OD_H1008_MANUF_DEV_NAME = 0x1008U,/**< Manufacturer device name */ - OD_H1009_MANUF_HW_VERSION = 0x1009U,/**< Manufacturer hardware version */ - OD_H100A_MANUF_SW_VERSION = 0x100AU,/**< Manufacturer software version */ - OD_H100B_RSV = 0x100BU,/**< Reserved */ - OD_H100C_GUARD_TIME = 0x100CU,/**< Guard time */ - OD_H100D_LIFETIME_FACTOR = 0x100DU,/**< Life time factor */ - OD_H100E_RSV = 0x100EU,/**< Reserved */ - OD_H100F_RSV = 0x100FU,/**< Reserved */ - OD_H1010_STORE_PARAM_FUNC = 0x1010U,/**< Store params in persistent mem.*/ - OD_H1011_REST_PARAM_FUNC = 0x1011U,/**< Restore default parameters */ - OD_H1012_COBID_TIME = 0x1012U,/**< Timestamp message cob-id */ - OD_H1013_HIGH_RES_TIMESTAMP = 0x1013U,/**< High resolution timestamp */ - OD_H1014_COBID_EMERGENCY = 0x1014U,/**< Emergency message cob-id */ - OD_H1015_INHIBIT_TIME_EMCY = 0x1015U,/**< Inhibit time emergency message */ - OD_H1016_CONSUMER_HB_TIME = 0x1016U,/**< Consumer heartbeat time */ - OD_H1017_PRODUCER_HB_TIME = 0x1017U,/**< Producer heartbeat time */ - OD_H1018_IDENTITY_OBJECT = 0x1018U,/**< Identity object */ - OD_H1019_SYNC_CNT_OVERFLOW = 0x1019U,/**< Sync counter overflow value */ - OD_H1020_VERIFY_CONFIG = 0x1020U,/**< Verify configuration */ - OD_H1021_STORE_EDS = 0x1021U,/**< Store EDS */ - OD_H1022_STORE_FORMAT = 0x1022U,/**< Store format */ - OD_H1023_OS_CMD = 0x1023U,/**< OS command */ - OD_H1024_OS_CMD_MODE = 0x1024U,/**< OS command mode */ - OD_H1025_OS_DBG_INTERFACE = 0x1025U,/**< OS debug interface */ - OD_H1026_OS_PROMPT = 0x1026U,/**< OS prompt */ - OD_H1027_MODULE_LIST = 0x1027U,/**< Module list */ - OD_H1028_EMCY_CONSUMER = 0x1028U,/**< Emergency consumer object */ - OD_H1029_ERR_BEHAVIOR = 0x1029U,/**< Error behaviour */ - OD_H1200_SDO_SERVER_1_PARAM = 0x1200U,/**< SDO server parameter */ - OD_H1280_SDO_CLIENT_1_PARAM = 0x1280U,/**< SDO client parameter */ - OD_H1300_GFC_PARAM = 0x1300U,/**< Global fail-safe command param */ - OD_H1301_SRDO_1_PARAM = 0x1301U,/**< SRDO communication parameter */ - OD_H1381_SRDO_1_MAPPING = 0x1381U,/**< SRDO mapping parameter */ - OD_H13FE_SRDO_VALID = 0x13FEU,/**< SRDO Configuration valid */ - OD_H13FF_SRDO_CHECKSUM = 0x13FFU,/**< SRDO configuration checksum */ - OD_H1400_RXPDO_1_PARAM = 0x1400U,/**< RXPDO communication parameter */ - OD_H1600_RXPDO_1_MAPPING = 0x1600U,/**< RXPDO mapping parameters */ - OD_H1800_TXPDO_1_PARAM = 0x1800U,/**< TXPDO communication parameter */ - OD_H1A00_TXPDO_1_MAPPING = 0x1A00U,/**< TXPDO mapping parameters */ -} OD_ObjDicId_30x_t; - - -/** - * Attributes (bit masks) for OD sub-object. - */ -typedef enum { - ODA_SDO_R = 0x01, /**< SDO server may read from the variable */ - ODA_SDO_W = 0x02, /**< SDO server may write to the variable */ - ODA_SDO_RW = 0x03, /**< SDO server may read from or write to the variable */ - ODA_TPDO = 0x04, /**< Variable is mappable into TPDO (can be read) */ - ODA_RPDO = 0x08, /**< Variable is mappable into RPDO (can be written) */ - ODA_TRPDO = 0x0C, /**< Variable is mappable into TPDO or RPDO */ - ODA_TSRDO = 0x10, /**< Variable is mappable into transmitting SRDO */ - ODA_RSRDO = 0x20, /**< Variable is mappable into receiving SRDO */ - ODA_TRSRDO = 0x30, /**< Variable is mappable into tx or rx SRDO */ - ODA_MB = 0x40, /**< Variable is multi-byte ((u)int16_t to (u)int64_t) */ - ODA_RESERVED = 0x80, /**< Reserved for further use */ -} OD_attributes_t; - - -/** - * Return codes from OD access functions. - * - * @ref OD_getSDOabCode() can be used to retrive corresponding SDO abort code. - */ -typedef enum { -/* !!!! WARNING !!!! - * If changing these values, change also OD_getSDOabCode() function! - */ - /** Read/write is only partial, make more calls */ - ODR_PARTIAL = -1, - /** SDO abort 0x00000000 - Read/write successfully finished */ - ODR_OK = 0, - /** SDO abort 0x05040005 - Out of memory */ - ODR_OUT_OF_MEM = 1, - /** SDO abort 0x06010000 - Unsupported access to an object */ - ODR_UNSUPP_ACCESS = 2, - /** SDO abort 0x06010001 - Attempt to read a write only object */ - ODR_WRITEONLY = 3, - /** SDO abort 0x06010002 - Attempt to write a read only object */ - ODR_READONLY = 4, - /** SDO abort 0x06020000 - Object does not exist in the object dict. */ - ODR_IDX_NOT_EXIST = 5, - /** SDO abort 0x06040041 - Object cannot be mapped to the PDO */ - ODR_NO_MAP = 6, - /** SDO abort 0x06040042 - PDO length exceeded */ - ODR_MAP_LEN = 7, - /** SDO abort 0x06040043 - General parameter incompatibility reasons */ - ODR_PAR_INCOMPAT = 8, - /** SDO abort 0x06040047 - General internal incompatibility in device */ - ODR_DEV_INCOMPAT = 9, - /** SDO abort 0x06060000 - Access failed due to hardware error */ - ODR_HW = 10, - /** SDO abort 0x06070010 - Data type does not match */ - ODR_TYPE_MISMATCH = 11, - /** SDO abort 0x06070012 - Data type does not match, length too high */ - ODR_DATA_LONG = 12, - /** SDO abort 0x06070013 - Data type does not match, length too short */ - ODR_DATA_SHORT = 13, - /** SDO abort 0x06090011 - Sub index does not exist */ - ODR_SUB_NOT_EXIST = 14, - /** SDO abort 0x06090030 - Invalid value for parameter (download only) */ - ODR_INVALID_VALUE = 15, - /** SDO abort 0x06090031 - Value range of parameter written too high */ - ODR_VALUE_HIGH = 16, - /** SDO abort 0x06090032 - Value range of parameter written too low */ - ODR_VALUE_LOW = 17, - /** SDO abort 0x06090036 - Maximum value is less than minimum value */ - ODR_MAX_LESS_MIN = 18, - /** SDO abort 0x060A0023 - Resource not available: SDO connection */ - ODR_NO_RESOURCE = 19, - /** SDO abort 0x08000000 - General error */ - ODR_GENERAL = 20, - /** SDO abort 0x08000020 - Data cannot be transferred or stored to app */ - ODR_DATA_TRANSF = 21, - /** SDO abort 0x08000021 - Data can't be transf (local control) */ - ODR_DATA_LOC_CTRL = 22, - /** SDO abort 0x08000022 - Data can't be transf (present device state) */ - ODR_DATA_DEV_STATE = 23, - /** SDO abort 0x08000023 - Object dictionary not present */ - ODR_OD_MISSING = 23, - /** SDO abort 0x08000024 - No data available */ - ODR_NO_DATA = 25, - /** Last element, number of responses */ - ODR_COUNT = 26 -} ODR_t; - - -/** - * IO stream structure, used for read/write access to OD variable. - * - * Structure is initialized with @ref OD_getSub() function. - */ -typedef struct { - /** Pointer to original data object, defined by Object Dictionary. If - * memory for data object is not specified by Object Dictionary, then - * dataObjectOriginal is NULL. Default read/write functions operate on it. - */ - void *dataObjectOriginal; - /** Pointer to object, passed by @ref OD_extensionIO_init(). Can be used - * inside read / write functions from IO extension. - */ - void *object; - /** Data length in bytes or 0, if length is not specified */ - OD_size_t dataLength; - /** In case of large data, dataOffset indicates position of already - * transferred data */ - OD_size_t dataOffset; -} OD_stream_t; - - -/** - * Structure describing properties of a variable, located in specific index and - * sub-index inside the Object Dictionary. - * - * Structure is initialized with @ref OD_getSub() function. - */ -typedef struct { - /** Object Dictionary index */ - uint16_t index; - /** Object Dictionary sub-index */ - uint8_t subIndex; - /** Maximum sub-index in the OD object */ - uint8_t maxSubIndex; - /** Group for non-volatile storage of the OD object */ - uint8_t storageGroup; - /** Attribute bit-field of the OD sub-object, see @ref OD_attributes_t */ - OD_attr_t attribute; - /** - * Pointer to PDO flags bit-field. This is optional extension of OD object. - * If OD object has enabled this extension, then each sub-element is coupled - * with own flagsPDO variable of size 8 to 64 bits (size is configurable - * by @ref OD_flagsPDO_t). Flag is useful, when variable is mapped to RPDO - * or TPDO. - * - * If sub-element is mapped to RPDO, then bit0 is set to 1 each time, when - * any RPDO writes new data into variable. Application may clear bit0. - * - * If sub-element is mapped to TPDO, then TPDO will set one bit on the time, - * it is sent. First TPDO will set bit1, second TPDO will set bit2, etc. Up - * to 63 TPDOs can use flagsPDO. - * - * Another functionality is with asynchronous TPDOs, to which variable may - * be mapped. If corresponding bit is 0, TPDO will be sent. This means, that - * if application sets variable pointed by flagsPDO to zero, it will trigger - * sending all asynchronous TPDOs (up to first 63), to which variable is - * mapped. - */ - OD_flagsPDO_t *flagsPDO; - /** - * Function pointer for reading value from specified variable from Object - * Dictionary. If OD variable is larger than buf, then this function must - * be called several times. After completed successful read function returns - * 'ODR_OK'. If read is partial, it returns 'ODR_PARTIAL'. In case of errors - * function returns code similar to SDO abort code. - * - * Read can be restarted with @ref OD_rwRestart() function. - * - * At the moment, when Object Dictionary is initialised, every variable has - * assigned the same "read" function. This default function simply copies - * data from Object Dictionary variable. Application can bind its own "read" - * function for specific object. In that case application is able to - * calculate data for reading from own internal state at the moment of - * "read" function call. For this functionality OD object must have IO - * extension enabled. OD object must also be initialised with - * @ref OD_extensionIO_init() function call. - * - * "read" function must always copy all own data to buf, except if "buf" is - * not large enough. ("*returnCode" must not return 'ODR_PARTIAL', if there - * is still space in "buf".) - * - * @param stream Object Dictionary stream object, returned from - * @ref OD_getSub() function, see @ref OD_stream_t. - * @param subIndex Object Dictionary subIndex of the accessed element. - * @param buf Pointer to external buffer, where to data will be copied. - * @param count Size of the external buffer in bytes. - * @param [out] returnCode Return value from @ref ODR_t. - * - * @return Number of bytes successfully read. - */ - OD_size_t (*read)(OD_stream_t *stream, uint8_t subIndex, - void *buf, OD_size_t count, ODR_t *returnCode); - /** - * Function pointer for writing value into specified variable inside Object - * Dictionary. If OD variable is larger than buf, then this function must - * be called several times. After completed successful write function - * returns 'ODR_OK'. If write is partial, it returns 'ODR_PARTIAL'. In case - * of errors function returns code similar to SDO abort code. - * - * Write can be restarted with @ref OD_rwRestart() function. - * - * At the moment, when Object Dictionary is initialised, every variable has - * assigned the same "write" function, which simply copies data to Object - * Dictionary variable. Application can bind its own "write" function, - * similar as it can bind "read" function. - * - * "write" function must always copy all available data from buf. If OD - * variable expect more data, then "*returnCode" must return 'ODR_PARTIAL'. - * - * @param stream Object Dictionary stream object, returned from - * @ref OD_getSub() function, see @ref OD_stream_t. - * @param subIndex Object Dictionary subIndex of the accessed element. - * @param buf Pointer to external buffer, from where data will be copied. - * @param count Size of the external buffer in bytes. - * @param [out] returnCode Return value from ODR_t. - * - * @return Number of bytes successfully written, must be equal to count on - * success or zero on error. - */ - OD_size_t (*write)(OD_stream_t *stream, uint8_t subIndex, - const void *buf, OD_size_t count, ODR_t *returnCode); -} OD_subEntry_t; - - -/** - * Helper structure for storing all objects necessary for frequent read from or - * write to specific OD variable. Structure can be used by application and can - * be filled inside and after @ref OD_getSub() function call. - */ -typedef struct { - /** Object passed to read or write */ - OD_stream_t stream; - /** Read function pointer, see @ref OD_subEntry_t */ - OD_size_t (*read)(OD_stream_t *stream, uint8_t subIndex, - void *buf, OD_size_t count, ODR_t *returnCode); - /** Write function pointer, see @ref OD_subEntry_t */ - OD_size_t (*write)(OD_stream_t *stream, uint8_t subIndex, - const void *buf, OD_size_t count, ODR_t *returnCode); -} OD_IO_t; - - -/** - * Object Dictionary entry for one OD object. - * - * OD entries are collected inside OD_t as array (list). Each OD entry contains - * basic information about OD object (index, maxSubIndex and storageGroup) and - * access function together with a pointer to other details of the OD object. - */ -typedef struct { - /** Object Dictionary index */ - uint16_t index; - /** Maximum sub-index in the OD object */ - uint8_t maxSubIndex; - /** Group for non-volatile storage of the OD object */ - uint8_t storageGroup; - /** Type of the odObject, indicated by @ref OD_objectTypes_t enumerator. */ - uint8_t odObjectType; - /** OD object of type indicated by odObjectType, from which @ref OD_getSub() - * fetches the information */ - const void *odObject; -} OD_entry_t; - - -/** - * Object Dictionary - */ -typedef struct { - /** Number of elements in the list, without last element, which is blank */ - uint16_t size; - /** List OD entries (table of contents), ordered by index */ - const OD_entry_t *list; -} OD_t; - - -/** - * Read value from original OD location - * - * This function can be used inside read / write functions, specified by - * @ref OD_extensionIO_init(). It reads data directly from memory location - * specified by Object dictionary. If no IO extension is used on OD entry, then - * subEntry->read returned by @ref OD_getSub() equals to this function. See - * also @ref OD_subEntry_t. - */ -OD_size_t OD_readOriginal(OD_stream_t *stream, uint8_t subIndex, - void *buf, OD_size_t count, ODR_t *returnCode); - - -/** - * Write value to original OD location - * - * This function can be used inside read / write functions, specified by - * @ref OD_extensionIO_init(). It writes data directly to memory location - * specified by Object dictionary. If no IO extension is used on OD entry, then - * subEntry->write returned by @ref OD_getSub() equals to this function. See - * also @ref OD_subEntry_t. - */ -OD_size_t OD_writeOriginal(OD_stream_t *stream, uint8_t subIndex, - const void *buf, OD_size_t count, ODR_t *returnCode); - - -/** - * Find OD entry in Object Dictionary - * - * @param od Object Dictionary - * @param index CANopen Object Dictionary index of object in Object Dictionary - * - * @return Pointer to OD entry or NULL if not found - */ -const OD_entry_t *OD_find(const OD_t *od, uint16_t index); - - -/** - * Find sub-object with specified sub-index on OD entry returned by OD_find. - * Function populates subEntry and stream structures with sub-object data. - * - * @param entry OD entry returned by @ref OD_find(). - * @param subIndex Sub-index of the variable from the OD object. - * @param [out] subEntry Structure will be populated on success. - * @param [out] stream Structure will be populated on success. - * @param odOrig If true, then potential IO extension on entry will be - * ignored and access to data entry in the original OD location will be returned - * - * @return Value from @ref ODR_t, "ODR_OK" in case of success. - */ -ODR_t OD_getSub(const OD_entry_t *entry, uint8_t subIndex, - OD_subEntry_t *subEntry, OD_stream_t *stream, bool_t odOrig); - - -/** - * Return index from OD entry - * - * @param entry OD entry returned by @ref OD_find(). - * - * @return OD index - */ -static inline uint16_t OD_getIndex(const OD_entry_t *entry) { - return entry->index; -} - - -/** - * Return maxSubIndex from OD entry - * - * @param entry OD entry returned by @ref OD_find(). - * - * @return OD maxSubIndex - */ -static inline uint8_t OD_getMaxSubIndex(const OD_entry_t *entry) { - return entry->maxSubIndex; -} - - -/** - * Restart read or write operation on OD variable - * - * It is not necessary to call this function, if stream was initialised by - * @ref OD_getSub(). It is also not necessary to call this function, if prevous - * read or write was successfully finished. - * - * @param stream Object Dictionary stream object, returned from - * @ref OD_getSub() function, see @ref OD_stream_t. - */ -static inline void OD_rwRestart(OD_stream_t *stream) { - stream->dataOffset = 0; -} - - -/** - * Get SDO abort code from returnCode - * - * @param returnCode Returned from some OD access functions - * - * @return Corresponding @ref CO_SDO_abortCode_t - */ -uint32_t OD_getSDOabCode(ODR_t returnCode); - - -/** - * Initialise extended OD object with own read/write functions - * - * This function works on OD object, which has IO extension enabled. It gives - * application very powerful tool: definition of own IO access on own OD - * object. Structure and attributes are the same as defined in original OD - * object, but data are read directly from (or written directly to) application - * specified object via custom function calls. - * - * If this function is not called yet, then normal access ("odOrig" argument is - * false) to OD entry is disabled. - * - * @warning - * Object dictionary storage works only directly on OD variables. It does not - * access read function specified here. So, if extended OD objects needs to be - * preserved, then @ref OD_writeOriginal can be used inside custom write - * function. - * - * @warning - * Read and write functions may be called from different threads, so critical - * sections in custom functions must be protected with @ref CO_LOCK_OD() and - * @ref CO_UNLOCK_OD(). - * - * @param entry OD entry returned by @ref OD_find(). - * @param object Object, which will be passed to read or write function. - * @param read Read function pointer. If NULL, then read will be disabled. - * @ref OD_readOriginal can be used here to keep original read function. - * For function description see @ref OD_subEntry_t. - * @param write Write function pointer. If NULL, then write will be disabled. - * @ref OD_writeOriginal can be used here to keep original write function. - * For function description see @ref OD_subEntry_t. - * - * @return true on success, false if OD object doesn't exist or is not extended. - */ -bool_t OD_extensionIO_init(const OD_entry_t *entry, - void *object, - OD_size_t (*read)(OD_stream_t *stream, - uint8_t subIndex, - void *buf, - OD_size_t count, - ODR_t *returnCode), - OD_size_t (*write)(OD_stream_t *stream, - uint8_t subIndex, - const void *buf, - OD_size_t count, - ODR_t *returnCode)); - - -/** - * @defgroup CO_ODgetSetters Getters and setters - * @{ - * - * Getter and setter helper functions for accessing different types of Object - * Dictionary variables. - */ -/** - * Get int8_t variable from Object Dictionary - * - * @param entry OD entry returned by @ref OD_find(). - * @param subIndex Sub-index of the variable from the OD object. - * @param [out] val Value will be written here. - * @param odOrig If true, then potential IO extension on entry will be - * ignored and data in the original OD location will be returned. - * - * @return Value from @ref ODR_t, "ODR_OK" in case of success. Error, if - * variable does not exist in object dictionary or it does not have the correct - * length or other reason. - */ -ODR_t OD_get_i8(const OD_entry_t *entry, uint8_t subIndex, - int8_t *val, bool_t odOrig); -/** Get int16_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_i16(const OD_entry_t *entry, uint8_t subIndex, - int16_t *val, bool_t odOrig); -/** Get int32_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_i32(const OD_entry_t *entry, uint8_t subIndex, - int32_t *val, bool_t odOrig); -/** Get int64_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_i64(const OD_entry_t *entry, uint8_t subIndex, - int64_t *val, bool_t odOrig); -/** Get uint8_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_u8(const OD_entry_t *entry, uint8_t subIndex, - uint8_t *val, bool_t odOrig); -/** Get uint16_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_u16(const OD_entry_t *entry, uint8_t subIndex, - uint16_t *val, bool_t odOrig); -/** Get uint32_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_u32(const OD_entry_t *entry, uint8_t subIndex, - uint32_t *val, bool_t odOrig); -/** Get uint64_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_u64(const OD_entry_t *entry, uint8_t subIndex, - uint64_t *val, bool_t odOrig); -/** Get float32_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_r32(const OD_entry_t *entry, uint8_t subIndex, - float32_t *val, bool_t odOrig); -/** Get float64_t variable from Object Dictionary, see @ref OD_get_i8 */ -ODR_t OD_get_r64(const OD_entry_t *entry, uint8_t subIndex, - float64_t *val, bool_t odOrig); - -/** - * Set int8_t variable in Object Dictionary - * - * @param entry OD entry returned by @ref OD_find(). - * @param subIndex Sub-index of the variable from the OD object. - * @param val Value to write. - * @param odOrig If true, then potential IO extension on entry will be - * ignored and data in the original OD location will be written. - * - * @return Value from @ref ODR_t, "ODR_OK" in case of success. Error, if - * variable does not exist in object dictionary or it does not have the correct - * length or other reason. - */ -ODR_t OD_set_i8(const OD_entry_t *entry, uint8_t subIndex, - int8_t val, bool_t odOrig); -/** Set int16_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_i16(const OD_entry_t *entry, uint8_t subIndex, - int16_t val, bool_t odOrig); -/** Set int16_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_i32(const OD_entry_t *entry, uint8_t subIndex, - int32_t val, bool_t odOrig); -/** Set int16_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_i64(const OD_entry_t *entry, uint8_t subIndex, - int64_t val, bool_t odOrig); -/** Set uint8_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_u8(const OD_entry_t *entry, uint8_t subIndex, - uint8_t val, bool_t odOrig); -/** Set uint16_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_u16(const OD_entry_t *entry, uint8_t subIndex, - uint16_t val, bool_t odOrig); -/** Set uint32_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_u32(const OD_entry_t *entry, uint8_t subIndex, - uint32_t val, bool_t odOrig); -/** Set uint64_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_u64(const OD_entry_t *entry, uint8_t subIndex, - uint64_t val, bool_t odOrig); -/** Set float32_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_r32(const OD_entry_t *entry, uint8_t subIndex, - float32_t val, bool_t odOrig); -/** Set float64_t variable in Object Dictionary, see @ref OD_set_i8 */ -ODR_t OD_set_r64(const OD_entry_t *entry, uint8_t subIndex, - float64_t val, bool_t odOrig); - -/** - * Get pointer to int8_t variable from Object Dictionary - * - * Function always returns "dataObjectOriginal" pointer, which points to data - * in the original OD location. Take care, if IO extension is enabled on OD - * entry. - * - * @param entry OD entry returned by @ref OD_find(). - * @param subIndex Sub-index of the variable from the OD object. - * @param [out] val Pointer to variable will be written here. - * - * @return Value from @ref ODR_t, "ODR_OK" in case of success. Error, if - * variable does not exist in object dictionary or it does not have the correct - * length or other reason. - */ -ODR_t OD_getPtr_i8(const OD_entry_t *entry, uint8_t subIndex, int8_t **val); -/** Get pointer to int16_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_i16(const OD_entry_t *entry, uint8_t subIndex, int16_t **val); -/** Get pointer to int32_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_i32(const OD_entry_t *entry, uint8_t subIndex, int32_t **val); -/** Get pointer to int64_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_i64(const OD_entry_t *entry, uint8_t subIndex, int64_t **val); -/** Get pointer to uint8_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_u8(const OD_entry_t *entry, uint8_t subIndex, uint8_t **val); -/** Get pointer to uint16_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_u16(const OD_entry_t *entry, uint8_t subIndex, uint16_t **val); -/** Get pointer to uint32_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_u32(const OD_entry_t *entry, uint8_t subIndex, uint32_t **val); -/** Get pointer to uint64_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_u64(const OD_entry_t *entry, uint8_t subIndex, uint64_t **val); -/** Get pointer to float32_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_r32(const OD_entry_t *entry, uint8_t subIndex, float32_t **val); -/** Get pointer to float64_t variable from OD, see @ref OD_getPtr_i8 */ -ODR_t OD_getPtr_r64(const OD_entry_t *entry, uint8_t subIndex, float64_t **val); -/** @} */ /* CO_ODgetSetters */ - - -#if defined OD_DEFINITION || defined CO_DOXYGEN -/** - * @defgroup CO_ODdefinition OD definition objects - * @{ - * - * Types and functions used only for definition of Object Dictionary - */ -/** - * Types for OD object. - */ -typedef enum { - /** This type corresponds to CANopen Object Dictionary object with object - * code equal to VAR. OD object is type of @ref OD_obj_var_t and represents - * single variable of any type (any length), located on sub-index 0. Other - * sub-indexes are not used. */ - ODT_VAR = 0x01, - /** This type corresponds to CANopen Object Dictionary object with object - * code equal to ARRAY. OD object is type of @ref OD_obj_array_t and - * represents array of variables with the same type, located on sub-indexes - * above 0. Sub-index 0 is of type uint8_t and usually represents length of - * the array. */ - ODT_ARR = 0x02, - /** This type corresponds to CANopen Object Dictionary object with object - * code equal to RECORD. This type of OD object represents structure of - * the variables. Each variable from the structure can have own type and - * own attribute. OD object is an array of elements of type - * @ref OD_obj_var_t. Variable at sub-index 0 is of type uint8_t and usually - * represents number of sub-elements in the structure. */ - ODT_REC = 0x03, - - /** Same as ODT_VAR, but extended with OD_obj_extended_t type. It includes - * additional pointer to IO extension and PDO flags */ - ODT_EVAR = 0x11, - /** Same as ODT_ARR, but extended with OD_obj_extended_t type */ - ODT_EARR = 0x12, - /** Same as ODT_REC, but extended with OD_obj_extended_t type */ - ODT_EREC = 0x13, - - /** Mask for basic type */ - ODT_TYPE_MASK = 0x0F, - /** Mask for extension */ - ODT_EXTENSION_MASK = 0x10 -} OD_objectTypes_t; - -/** - * Object for single OD variable, used for "VAR" and "RECORD" type OD objects - */ -typedef struct { - void *data; /**< Pointer to data */ - OD_attr_t attribute; /**< Attribute bitfield, see @ref OD_attributes_t */ - OD_size_t dataLength; /**< Data length in bytes */ -} OD_obj_var_t; - -/** - * Object for OD array of variables, used for "ARRAY" type OD objects - */ -typedef struct { - uint8_t *data0; /**< Pointer to data for sub-index 0 */ - void *data; /**< Pointer to array of data */ - OD_attr_t attribute0; /**< Attribute bitfield for sub-index 0, see - @ref OD_attributes_t */ - OD_attr_t attribute; /**< Attribute bitfield for array elements */ - OD_size_t dataElementLength; /**< Data length of array elements in bytes */ - OD_size_t dataElementSizeof; /**< Sizeof one array element in bytes */ -} OD_obj_array_t; - -/** - * Object pointed by @ref OD_obj_extended_t contains application specified - * parameters for extended OD object - */ -typedef struct { - /** Object on which read and write will operate */ - void *object; - /** Application specified function pointer, see @ref OD_subEntry_t. */ - OD_size_t (*read)(OD_stream_t *stream, uint8_t subIndex, - void *buf, OD_size_t count, ODR_t *returnCode); - /** Application specified function pointer, see @ref OD_subEntry_t. */ - OD_size_t (*write)(OD_stream_t *stream, uint8_t subIndex, - const void *buf, OD_size_t count, ODR_t *returnCode); -} OD_extensionIO_t; - -/** - * Object for extended type of OD variable, configurable by - * @ref OD_extensionIO_init() function - */ -typedef struct { - /** Pointer to PDO flags bit-field, see @ref OD_subEntry_t, may be NULL. */ - OD_flagsPDO_t *flagsPDO; - /** Pointer to application specified IO extension, may be NULL. */ - OD_extensionIO_t *extIO; - /** Pointer to original odObject, see @ref OD_entry_t. */ - const void *odObjectOriginal; -} OD_obj_extended_t; - -/** @} */ /* CO_ODdefinition */ - -#endif /* defined OD_DEFINITION */ - -/** @} */ /* CO_ODinterface */ - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -#endif /* CO_OD_INTERFACE_H */ diff --git a/Makefile b/Makefile index 4ce4a9ac2..98a6d91e7 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,6 @@ SOURCES = \ $(DRV_SRC)/CO_error.c \ $(DRV_SRC)/CO_epoll_interface.c \ $(DRV_SRC)/CO_OD_storage.c \ - $(CANOPEN_SRC)/301/CO_ODinterface.c \ $(CANOPEN_SRC)/301/CO_SDOserver.c \ $(CANOPEN_SRC)/301/CO_Emergency.c \ $(CANOPEN_SRC)/301/CO_NMT_Heartbeat.c \ diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index e4ba956a8..f6732556d 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -4,7 +4,6 @@ Change Log [Unreleased master] ------------------------- - [Full ChangeLog](https://github.com/CANopenNode/CANopenNode/compare/v1.3...master) -- See {TODO diff} for example of change in user application interface. ### Removed - All drivers removed from this project, except Neuberger-socketCAN for Linux. ### Changed @@ -38,7 +37,7 @@ Change Log ### Added - Documentation added to `doc` directory: CHANGELOG.md, deviceSupport.md, gettingStarted.md, LSSusage.md and traceUsage.md. - All CANopen objects calculates next timer info for OS. Useful for energy saving. -- Added file CO_config.h for stack configuration. Can be overridden by target specific or by custom definitions. +- Added file CO_config.h for stack configuration. Can be overridden by target specific or by custom definitions. It enables/disables whole CanOpenNode objects or parts of them. It also specifies some constants. - CO_fifo.h/c for fifo data buffer, used with rewritten SDO client, etc. - CANopen gateway-ascii command interface according to CiA309-3 as a microcontroller independent module. It includes NMT master, LSS master and SDO client interface. Interface is non-blocking, it is added to mainline. Example for Linux stdio and socket is included. diff --git a/doc/objectDictionary.md b/doc/objectDictionary.md index eebcab4eb..09e10322c 100644 --- a/doc/objectDictionary.md +++ b/doc/objectDictionary.md @@ -1,6 +1,8 @@ Object Dictionary ================= +**THIS IS AVAILABLE IN THE NEXT VERSION** + Definitions from CiA 301 ------------------------ The **Object Dictionary** is a collection of all the data items which have an influence on the behavior of the application objects, the communication objects and the state machine used on this device. It serves as an interface between the communication and the application. @@ -99,9 +101,9 @@ Pair of ODxyz.h/.c files can be generated by OD editor tool. The tool can edit s ### Example ODxyz.h file ```c +/* OD data declaration of all groups ******************************************/ typedef struct { uint32_t x1000_deviceType; - uint8_t x1001_errorRegister; struct { uint8_t maxSubIndex; uint32_t vendorID; @@ -109,19 +111,28 @@ typedef struct { uint32_t revisionNumber; uint32_t serialNumber; } x1018_identity; -} ODxyz_0_t; +} ODxyz_PERSIST_COMM_t; typedef struct { uint8_t x1001_errorRegister; -} ODxyz_1_t; + uint8_t x1003_preDefinedErrorField_sub0; + uint32_t x1003_preDefinedErrorField[8]; +} ODxyz_RAM_t; -extern ODxyz_0_t ODxyz_0; -extern ODxyz_1_t ODxyz_1; +extern ODxyz_PERSIST_COMM_t ODxyz_PERSIST_COMM; +extern ODxyz_RAM_t ODxyz_RAM; extern const OD_t ODxyz; +/* Object dictionary entries - shortcuts **************************************/ #define ODxyz_ENTRY_H1000 &ODxyz.list[0] #define ODxyz_ENTRY_H1001 &ODxyz.list[1] -#define ODxyz_ENTRY_H1018 &ODxyz.list[2] +#define ODxyz_ENTRY_H1003 &ODxyz.list[2] +#define ODxyz_ENTRY_H1018 &ODxyz.list[3] + +#define ODxyz_ENTRY_H1000_deviceType &ODxyz.list[0] +#define ODxyz_ENTRY_H1001_errorRegister &ODxyz.list[1] +#define ODxyz_ENTRY_H1003_preDefinedErrorField &ODxyz.list[2] +#define ODxyz_ENTRY_H1018_identity &ODxyz.list[3] ``` ### Example ODxyz.c file @@ -130,89 +141,110 @@ extern const OD_t ODxyz; #include "301/CO_ODinterface.h" #include "ODxyz.h" -typedef struct { - OD_extensionIO_t xio_1001_errorRegister; -} ODxyz_ext_t; - -typedef struct { - OD_obj_var_t o_1000_deviceType; - OD_obj_var_t orig_1001_errorRegister; - OD_obj_extended_t o_1001_errorRegister; - OD_obj_var_t o_1018_identity[5]; -} ODxyz_objs_t; - -ODxyz_0_t ODxyz_0 = { +/* OD data initialization of all groups ***************************************/ +ODxyz_PERSIST_COMM_t ODxyz_PERSIST_COMM = { .x1000_deviceType = 0L, .x1018_identity = { .maxSubIndex = 4, .vendorID = 0L, .productCode = 0L, .revisionNumber = 0L, - .serialNumber = 0L, - }, + .serialNumber = 0L + } }; -ODxyz_1_t ODxyz_1 = { +ODxyz_RAM_t ODxyz_RAM = { .x1001_errorRegister = 0, + .x1003_preDefinedErrorField_sub0 = 0, + .x1003_preDefinedErrorField = {0, 0, 0, 0, 0, 0, 0, 0} }; -static ODxyz_ext_t ODxyz_ext = {0}; +/* IO extensions and flagsPDO (configurable by application) *******************/ +typedef struct { + OD_extensionIO_t xio_1003_preDefinedErrorField; +} ODxyzExts_t; + +static ODxyzExts_t ODxyzExts = {0}; + +/* All OD objects (constant) **************************************************/ +typedef struct { + OD_obj_var_t o_1000_deviceType; + OD_obj_var_t o_1001_errorRegister; + OD_obj_array_t o_1003_preDefinedErrorField; + OD_obj_extended_t oE_1003_preDefinedErrorField; + OD_obj_record_t o_1018_identity[5]; +} ODxyzObjs_t; -static const ODxyz_objs_t ODxyz_objs = { +static const ODxyzObjs_t ODxyzObjs = { .o_1000_deviceType = { - .data = &ODxyz_0.x1000_deviceType, + .data = &ODxyz_PERSIST_COMM.x1000_deviceType, .attribute = ODA_SDO_R | ODA_MB, - .dataLength = 4, + .dataLength = 4 }, - .orig_1001_errorRegister = { - .data = &ODxyz_1.x1001_errorRegister, + .o_1001_errorRegister = { + .data = &ODxyz_RAM.x1001_errorRegister, .attribute = ODA_SDO_R, - .dataLength = 1, + .dataLength = 1 }, - .o_1001_errorRegister = { + .o_1003_preDefinedErrorField = { + .data0 = &ODxyz_RAM.x1003_preDefinedErrorField_sub0, + .data = &ODxyz_RAM.x1003_preDefinedErrorField[0], + .attribute0 = ODA_SDO_RW, + .attribute = ODA_SDO_R | ODA_MB, + .dataElementLength = 4, + .dataElementSizeof = sizeof(uint32_t) + }, + .oE_1003_preDefinedErrorField = { + .extIO = &ODxyzExts.xio_1003_preDefinedErrorField, .flagsPDO = NULL, - .extIO = &ODxyz_ext.xio_1001_errorRegister, - .odObjectOriginal = &ODxyz_objs.orig_1001_errorRegister, + .odObjectOriginal = &ODxyzObjs.o_1003_preDefinedErrorField }, .o_1018_identity = { { - .data = &ODxyz_0.x1018_identity.maxSubIndex, + .data = &ODxyz_PERSIST_COMM.x1018_identity.maxSubIndex, + .subIndex = 0, .attribute = ODA_SDO_R, - .dataLength = 1, + .dataLength = 1 }, { - .data = &ODxyz_0.x1018_identity.vendorID, + .data = &ODxyz_PERSIST_COMM.x1018_identity.vendorID, + .subIndex = 1, .attribute = ODA_SDO_R | ODA_MB, - .dataLength = 4, + .dataLength = 4 }, { - .data = &ODxyz_0.x1018_identity.productCode, + .data = &ODxyz_PERSIST_COMM.x1018_identity.productCode, + .subIndex = 2, .attribute = ODA_SDO_R | ODA_MB, - .dataLength = 4, + .dataLength = 4 }, { - .data = &ODxyz_0.x1018_identity.revisionNumber, + .data = &ODxyz_PERSIST_COMM.x1018_identity.revisionNumber, + .subIndex = 3, .attribute = ODA_SDO_R | ODA_MB, - .dataLength = 4, + .dataLength = 4 }, { - .data = &ODxyz_0.x1018_identity.serialNumber, + .data = &ODxyz_PERSIST_COMM.x1018_identity.serialNumber, + .subIndex = 4, .attribute = ODA_SDO_R | ODA_MB, - .dataLength = 4, - }, + .dataLength = 4 + } } }; -static const OD_entry_t ODxyz_list[] = { - {0x1000, 0, 0, ODT_VAR, &ODxyz_objs.o_1000_deviceType}, - {0x1001, 0, 1, ODT_EVAR, &ODxyz_objs.o_1001_errorRegister}, - {0x1018, 4, 0, ODT_REC, &ODxyz_objs.o_1018_identity}, - {0x0000, 0, 0, 0, NULL} +/* Object dictionary **********************************************************/ +static const OD_entry_t ODxyzList[] = { + {0x1000, 1, ODT_VAR, &ODxyzObjs.o_1000_deviceType}, + {0x1001, 1, ODT_VAR, &ODxyzObjs.o_1001_errorRegister}, + {0x1003, 9, ODT_EVAR, &ODxyzObjs.oE_1003_preDefinedErrorField}, + {0x1018, 5, ODT_REC, &ODxyzObjs.o_1018_identity}, + {0x0000, 0, 0, NULL} }; -const OD_t OD = { - (sizeof(OD_list) / sizeof(OD_list[0])) - 1, - &ODxyz_list[0] +const OD_t ODxyz = { + (sizeof(ODxyzList) / sizeof(ODxyzList[0])) - 1, + &ODxyzList[0] }; ``` @@ -258,7 +290,7 @@ CANopenNode includes multiple profile definition files, one for each CANopen obj ... - + @@ -391,40 +423,50 @@ If "uniqueIDRef" attribute is not specified and "objectType" is 7(VAR), then "CA * "noAccess" - object will be in object dictionary, but no access. * <label lang="en"> (required) * <description lang="en"> (required) - * <UINT and similar/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then <dataTypeIDRef> must be specified and entry must be added in the <dataTypeList>. Such definition of complex data types is required by the standard, but it is not required by CANopenNode. - * <defaultValue> (optional for VAR) - Default value for the variable. + * <INT and similar/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then <dataTypeIDRef> must be specified and entry must be added in the <dataTypeList>. Such definition of complex data types is required by the standard, but it is not required by CANopenNode. + * <defaultValue> (optional for VAR) - Default value for the variable. If it is empty, then data is not stored inside object dictionary. Application should provide own data via IO extension. Additional, optional, CANopenNode specific properties, which can be used inside parameters describing <CANopenObject>: - * <property name="CO_storageGroup" value="..."> - group into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. Valid value is from 0x00 (default - not stored) to 0x7F. + * <property name="CO_storageGroup" value="..."> - group name (string) into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. * <property name="CO_extensionIO" value="..."> - Valid value is "false" (default) or "true", if IO extension is enabled. * <property name="CO_flagsPDO" value="..."> - Valid value is "false" (default) or "true", if PDO flags are enabled. * <property name="CO_countLabel" value="..."> - Valid value is any string without spaces. OD exporter will generate a macro for each different string. For example, if four OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_CNT_TPDO 4" will be generated by OD exporter. Additional, optional, CANopenNode specific property, which can be used inside parameters describing "VAR": * <property name="CO_accessSRDO" value="..."> - Valid values are: "tx", "rx", "trx", "no"(default). - * <property name="CO_byteLength" value="..."> - Length of the variable in bytes, optionally used by string or domain. If CO_byteLength is not specified for string, then length is calculated from string <defaultValue>. + * <property name="CO_stringLength" value="..."> - Minimum length of the string. Used with "VISIBLE_STRING", "OCTET_STRING" and "UNICODE_STRING". If CO_stringLength smaller than length of string in <defaultValue>, then it is ignored. Byte length of unicode string is 2 * CO_stringLength. If <defaultValue> is empty and CO_stringLength is 0, then data is not stored inside object dictionary. #### CANopen basic data types -| CANopenNode | IEC 61131-3 | CANopen | dataType | -| ------------ | ------------ | --------------- | -------- | -| bool_t | BOOL | BOOLEAN | 0x01 | -| int8_t | CHAR, SINT | INTEGER8 | 0x02 | -| int16_t | INT | INTEGER16 | 0x03 | -| int32_t | DINT | INTEGER32 | 0x04 | -| int64_t | LINT | INTEGER64 | 0x15 | -| uint8_t | BYTE, USINT | UNSIGNED8 | 0x05 | -| uint16_t | WORD, UINT | UNSIGNED16 | 0x06 | -| uint32_t | DWORD, UDINT | UNSIGNED32 | 0x07 | -| uint64_t | LWORD, ULINT | UNSIGNED64 | 0x1B | -| float32_t | REAL | REAL32 | 0x08 | -| float64_t | LREAL | REAL64 | 0x11 | -| bytestring_t | STRING | VISIBLE_STRING | 0x09 | -| bytestring_t | - | OCTET_STRING | 0x0A | -| bytestring_t | WSTRING | UNICODE_STRING | 0x0B | -| bytestring_t | - | TIME_OF_DAY | 0x0C | -| bytestring_t | - | TIME_DIFFERENCE | 0x0D | -| bytestring_t | - | DOMAIN | 0x0F | -| bytestring_t | BITSTRING | - | - | +| CANopenNode | IEC 61131-3 | CANopen | dataType | +| -------------- | -------------- | --------------- | -------- | +| bool_t | BOOL | BOOLEAN | 0x01 | +| int8_t | SINT, (CHAR) | INTEGER8 | 0x02 | +| int16_t | INT | INTEGER16 | 0x03 | +| int32_t | DINT | INTEGER32 | 0x04 | +| int64_t | LINT | INTEGER64 | 0x15 | +| uint8_t | USINT, (BYTE) | UNSIGNED8 | 0x05 | +| uint16_t | UINT, (WORD) | UNSIGNED16 | 0x06 | +| uint32_t | UDINT, (DWORD) | UNSIGNED32 | 0x07 | +| uint64_t | ULINT, (LWORD) | UNSIGNED64 | 0x1B | +| float32_t | REAL | REAL32 | 0x08 | +| float64_t | LREAL | REAL64 | 0x11 | +| uint8_t [] (1) | BITSTRING (2) | INTEGER24 | 0x10 | +| uint8_t [] (1) | BITSTRING (2) | INTEGER40 | 0x12 | +| uint8_t [] (1) | BITSTRING (2) | INTEGER48 | 0x13 | +| uint8_t [] (1) | BITSTRING (2) | INTEGER56 | 0x14 | +| uint8_t [] (1) | BITSTRING (2) | UNSIGNED24 | 0x16 | +| uint8_t [] (1) | BITSTRING (2) | UNSIGNED40 | 0x18 | +| uint8_t [] (1) | BITSTRING (2) | UNSIGNED48 | 0x19 | +| uint8_t [] (1) | BITSTRING (2) | UNSIGNED56 | 0x1A | +| char [] | STRING | VISIBLE_STRING | 0x09 | +| uint8_t [] | BITSTRING (2) | OCTET_STRING | 0x0A | +| uint16_t [] | WSTRING | UNICODE_STRING | 0x0B | +| uint8_t [] (1) | BITSTRING (2) | TIME_OF_DAY | 0x0C | +| uint8_t [] (1) | BITSTRING (2) | TIME_DIFFERENCE | 0x0D | +| not used | BITSTRING (2) | DOMAIN | 0x0F | +(1) Data is stored in little-endian format. + +(2) CANopen specific type is stored as <BITSTRING/> in <parameter> and additional CANopen "dataType" attribute is stored in <CANopenObject> or <CANopenSubObject>. #### <parameterGroupList> This is optional element and is not required by standard, nor by CANopenNode. This can be very useful for documentation, which can be organised into multiple chapters with multiple levels. CANopen objects can then be organised in any way, not only by index. @@ -464,7 +506,7 @@ Other elements listed in the above XML example are required by the standard and | 1014 | COB-ID EMCY | EM_PRODUCER, req | required | EM_PROD | | 1015 | Inhibit time EMCY | EM_PROD_INHIBIT, opt | optional | | | 1016 | Consumer heartbeat time | HB_CONS, req | optional | HB_CONS | -| 1017 | Producer heartbeat time | CANopen, NMT, req | optional | HB_PROD | +| 1017 | Producer heartbeat time | CANopen, NMT, req | required | HB_PROD | | 1018 | Identity object | CANopen, LSS_SL, req | no | | | 1019 | Synch. counter overflow value | SYNC, opt | no | | | 1020 | Verify configuration | | | | diff --git a/example/Makefile b/example/Makefile index 48e4876b1..b4264b7a1 100644 --- a/example/Makefile +++ b/example/Makefile @@ -20,7 +20,6 @@ INCLUDE_DIRS = \ SOURCES = \ $(DRV_SRC)/CO_driver_blank.c \ $(DRV_SRC)/eeprom.c \ - $(CANOPEN_SRC)/301/CO_ODinterface.c \ $(CANOPEN_SRC)/301/CO_SDOserver.c \ $(CANOPEN_SRC)/301/CO_Emergency.c \ $(CANOPEN_SRC)/301/CO_NMT_Heartbeat.c \ diff --git a/socketCAN/CO_main_basic.c b/socketCAN/CO_main_basic.c index 4e42e83df..e660a041e 100644 --- a/socketCAN/CO_main_basic.c +++ b/socketCAN/CO_main_basic.c @@ -166,6 +166,7 @@ static void NmtChangedCallback(CO_NMT_internalState_t state) log_printf(LOG_NOTICE, DBG_NMT_CHANGE, NmtState2Str(state), state); } +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE /* callback for monitoring Heartbeat remote NMT state change */ static void HeartbeatNmtChangedCallback(uint8_t nodeId, uint8_t idx, CO_NMT_internalState_t state, @@ -175,6 +176,7 @@ static void HeartbeatNmtChangedCallback(uint8_t nodeId, uint8_t idx, log_printf(LOG_NOTICE, DBG_HB_CONS_NMT_CHANGE, nodeId, idx, NmtState2Str(state), state); } +#endif #if CO_OD_STORAGE == 1 /* callback for storing node id and bitrate */ @@ -371,6 +373,7 @@ int main (int argc, char *argv[]) { } +#if CO_OD_STORAGE == 1 /* Verify, if OD structures have proper alignment of initial values */ if(CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) { log_printf(LOG_CRIT, DBG_OBJECT_DICTIONARY, "CO_OD_RAM"); @@ -386,7 +389,6 @@ int main (int argc, char *argv[]) { } -#if CO_OD_STORAGE == 1 /* initialize Object Dictionary storage */ odStorStatus_rom = CO_OD_storage_init(&odStor, (uint8_t*) &CO_OD_ROM, sizeof(CO_OD_ROM), odStorFile_rom); odStorStatus_eeprom = CO_OD_storage_init(&odStorAuto, (uint8_t*) &CO_OD_EEPROM, sizeof(CO_OD_EEPROM), odStorFile_eeprom); @@ -479,8 +481,10 @@ int main (int argc, char *argv[]) { if(!CO->nodeIdUnconfigured) { CO_EM_initCallbackRx(CO->em, EmergencyRxCallback); CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback); +#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL, HeartbeatNmtChangedCallback); +#endif #if CO_OD_STORAGE == 1 /* initialize OD objects 1010 and 1011 and verify errors. */ CO_OD_configure(CO->SDO[0], OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)&odStor, 0, 0U); From 893cbc693b7e766425f896acd1efa693dd605262 Mon Sep 17 00:00:00 2001 From: Janez Date: Thu, 29 Oct 2020 09:37:18 +0100 Subject: [PATCH 08/21] Set CANopenNode version V2.0 Added macros CO_VERSION_MAJOR and CO_VERSION_MINOR into driver.h --- 301/CO_driver.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/301/CO_driver.h b/301/CO_driver.h index 1d87ab2cb..62a491b1f 100644 --- a/301/CO_driver.h +++ b/301/CO_driver.h @@ -89,6 +89,11 @@ extern "C" { * (or interrupts), CAN module, etc.). */ +/** Major version number of CANopenNode */ +#define CO_VERSION_MAJOR 2 +/** Minor version number of CANopenNode */ +#define CO_VERSION_MINOR 0 + /* Macros and declarations in following part are only used for documentation. */ #ifdef CO_DOXYGEN From 02ffc74e60a7eaf1061567647340791870fd9cb4 Mon Sep 17 00:00:00 2001 From: Geoffrey Van Landeghem Date: Wed, 25 Nov 2020 19:16:50 +0100 Subject: [PATCH 09/21] move timeOfDay_t union to CO_TIME.h --- 301/CO_TIME.h | 16 +++++++++++++++- example/CO_OD.h | 15 --------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/301/CO_TIME.h b/301/CO_TIME.h index f7dcefa7a..689ae248d 100644 --- a/301/CO_TIME.h +++ b/301/CO_TIME.h @@ -27,7 +27,6 @@ #define CO_TIME_H #include "301/CO_driver.h" -#include "CO_OD.h" /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_TIME @@ -79,6 +78,21 @@ extern "C" { #define TIME_MSG_LENGTH 6U +#ifndef timeOfDay_t +typedef union { + unsigned long long ullValue; + struct { + unsigned long ms:28; + unsigned reserved:4; + unsigned days:16; + unsigned reserved2:16; + }; +}timeOfDay_t; +#endif + +typedef timeOfDay_t TIME_OF_DAY; +typedef timeOfDay_t TIME_DIFFERENCE; + /** * TIME producer and consumer object. */ diff --git a/example/CO_OD.h b/example/CO_OD.h index 4e2f31e2c..82b523574 100644 --- a/example/CO_OD.h +++ b/example/CO_OD.h @@ -51,21 +51,6 @@ typedef oChar_t OCTET_STRING; typedef domain_t DOMAIN; - #ifndef timeOfDay_t - typedef union { - unsigned long long ullValue; - struct { - unsigned long ms:28; - unsigned reserved:4; - unsigned days:16; - unsigned reserved2:16; - }; - }timeOfDay_t; -#endif - - typedef timeOfDay_t TIME_OF_DAY; - typedef timeOfDay_t TIME_DIFFERENCE; - /******************************************************************************* FILE INFO: From dc6c78f3171d2d7e5edd2b217e3446b64466cf09 Mon Sep 17 00:00:00 2001 From: Geoffrey Van Landeghem Date: Thu, 26 Nov 2020 17:31:07 +0100 Subject: [PATCH 10/21] removed symlink to doc.html, not supported on Windows --- doc/index.html | 1 - 1 file changed, 1 deletion(-) delete mode 120000 doc/index.html diff --git a/doc/index.html b/doc/index.html deleted file mode 120000 index 5bc112887..000000000 --- a/doc/index.html +++ /dev/null @@ -1 +0,0 @@ -html/index.html \ No newline at end of file From 9dda17ed1658cfa96cdc5989d10e0c33281be270 Mon Sep 17 00:00:00 2001 From: Geoffrey Van Landeghem Date: Wed, 2 Dec 2020 16:50:19 +0100 Subject: [PATCH 11/21] fixed CO_fifo compiler warnings (#249) --- 301/CO_fifo.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/301/CO_fifo.c b/301/CO_fifo.c index 1e362d949..66c0706ac 100644 --- a/301/CO_fifo.c +++ b/301/CO_fifo.c @@ -472,7 +472,7 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, #if (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_DATATYPES /******************************************************************************/ size_t CO_fifo_readU82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint8_t n; + uint8_t n=0; if (fifo != NULL && count >= 6 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -484,7 +484,7 @@ size_t CO_fifo_readU82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readU162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint16_t n; + uint16_t n=0; if (fifo != NULL && count >= 8 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -496,7 +496,7 @@ size_t CO_fifo_readU162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readU322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint32_t n; + uint32_t n=0; if (fifo != NULL && count >= 12 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -508,7 +508,7 @@ size_t CO_fifo_readU322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readU642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint64_t n; + uint64_t n=0; if (fifo != NULL && count >= 20 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -520,7 +520,7 @@ size_t CO_fifo_readU642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readX82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint8_t n; + uint8_t n=0; if (fifo != NULL && count >= 6 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -532,7 +532,7 @@ size_t CO_fifo_readX82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readX162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint16_t n; + uint16_t n=0; if (fifo != NULL && count >= 8 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -544,7 +544,7 @@ size_t CO_fifo_readX162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readX322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint32_t n; + uint32_t n=0; if (fifo != NULL && count >= 12 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -556,7 +556,7 @@ size_t CO_fifo_readX322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readX642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - uint64_t n; + uint64_t n=0; if (fifo != NULL && count >= 20 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -568,7 +568,7 @@ size_t CO_fifo_readX642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readI82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - int8_t n; + int8_t n=0; if (fifo != NULL && count >= 6 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -580,7 +580,7 @@ size_t CO_fifo_readI82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readI162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - int16_t n; + int16_t n=0; if (fifo != NULL && count >= 8 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -592,7 +592,7 @@ size_t CO_fifo_readI162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readI322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - int32_t n; + int32_t n=0; if (fifo != NULL && count >= 13 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -604,7 +604,7 @@ size_t CO_fifo_readI322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readI642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - int64_t n; + int64_t n=0; if (fifo != NULL && count >= 23 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -616,7 +616,7 @@ size_t CO_fifo_readI642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readR322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - float32_t n; + float32_t n=0; if (fifo != NULL && count >= 20 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); @@ -628,7 +628,7 @@ size_t CO_fifo_readR322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } size_t CO_fifo_readR642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { - float64_t n; + float64_t n=0; if (fifo != NULL && count >= 30 && CO_fifo_getOccupied(fifo) == sizeof(n)) { CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); From aada195b1538ebd00e14314c3c24bc27840e5d9f Mon Sep 17 00:00:00 2001 From: Janez Date: Thu, 10 Dec 2020 12:50:49 +0100 Subject: [PATCH 12/21] Fix for #include directives in CO_OD.c from latest libedssharp --- Makefile | 4 ++-- README.md | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 98a6d91e7..12a5fc03d 100644 --- a/Makefile +++ b/Makefile @@ -46,8 +46,8 @@ SOURCES = \ OBJS = $(SOURCES:%.c=%.o) CC ?= gcc -OPT = -g -#OPT = -g -DCO_SINGLE_THREAD +OPT = -g -DCO_VERSION_MAJOR=2 +#OPT = -g -DCO_VERSION_MAJOR=2 -DCO_SINGLE_THREAD CFLAGS = -Wall $(OPT) $(INCLUDE_DIRS) LDFLAGS = -pthread #LDFLAGS = diff --git a/README.md b/README.md index 8c1040fe1..d997cf971 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,9 @@ Windows or Linux with Mono. Please note: since rearrangement in directory structure it is necessary to manually update CO_OD.c file - it must include: `301/CO_driver.h`, `CO_OD.h` and -`301/CO_SDOserver.h`. +`301/CO_SDOserver.h`. Latest libedssharp includes `CO_VERSION_MAJOR` macro, to +include the correct files. For this to work, C macro `CO_VERSION_MAJOR=2` has to +be added to project options (or `-DCO_VERSION_MAJOR=2` to makefile CFLAGS). Device support From aa2eb59df601c84bf227747f308e8bb37c8f58c9 Mon Sep 17 00:00:00 2001 From: Janez Date: Thu, 10 Dec 2020 12:50:49 +0100 Subject: [PATCH 13/21] Fix for #include directives in CO_OD.c from latest libedssharp --- Makefile | 4 ++-- README.md | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 98a6d91e7..12a5fc03d 100644 --- a/Makefile +++ b/Makefile @@ -46,8 +46,8 @@ SOURCES = \ OBJS = $(SOURCES:%.c=%.o) CC ?= gcc -OPT = -g -#OPT = -g -DCO_SINGLE_THREAD +OPT = -g -DCO_VERSION_MAJOR=2 +#OPT = -g -DCO_VERSION_MAJOR=2 -DCO_SINGLE_THREAD CFLAGS = -Wall $(OPT) $(INCLUDE_DIRS) LDFLAGS = -pthread #LDFLAGS = diff --git a/README.md b/README.md index 8c1040fe1..d997cf971 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,9 @@ Windows or Linux with Mono. Please note: since rearrangement in directory structure it is necessary to manually update CO_OD.c file - it must include: `301/CO_driver.h`, `CO_OD.h` and -`301/CO_SDOserver.h`. +`301/CO_SDOserver.h`. Latest libedssharp includes `CO_VERSION_MAJOR` macro, to +include the correct files. For this to work, C macro `CO_VERSION_MAJOR=2` has to +be added to project options (or `-DCO_VERSION_MAJOR=2` to makefile CFLAGS). Device support From 9e053e05011988e9fb892181ecd12c15086301f1 Mon Sep 17 00:00:00 2001 From: Geoffrey Van Landeghem Date: Thu, 3 Dec 2020 23:15:46 +0100 Subject: [PATCH 14/21] setup CI build --- .github/workflows/c-cpp.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 000000000..23aa0bfc5 --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,17 @@ +name: C/C++ CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: make + run: make From 0c000b59242e006630fe326eb3ef5a8b78ada38f Mon Sep 17 00:00:00 2001 From: Janez Date: Mon, 28 Dec 2020 13:19:55 +0100 Subject: [PATCH 15/21] Multiple updates related to newOD branch, commit 9e9fce74 Testing and multiple fixes were made in newOD branch. This commit includes relevant code from newOD branch, synchronized to this branch. Following modules were updated: - SDO client, CO_gateway_ascii, CO_fifo - socketCAN : CO_epoll_interface, CO_main_basic SDO client (with gateway and fifo) which was rewritten after v1.3 is now tested and works the same as in newOD branch. Same is socketCAN. Works as here: https://github.com/CANopenNode/CANopenSocket/tree/master/examples/basicDevice SDO server is quite the same as in v1.3. I made no additional testing at this point. There are some other updates: - CO_driver.h, CO_config.h: additional CO_CONFIG_DEBUG macro. - CO_driver.h: fixed some typos, added optional CO_errinfo() macro. - CO_NMT_Heartbeat: added CO_NMT_setInternalState(). - documentation synchronized. - example/_project.xml updated, can be used as template with libedssharp. --- 301/CO_NMT_Heartbeat.h | 18 +- 301/CO_SDOclient.c | 151 ++++++++---- 301/CO_SDOclient.h | 26 +- 301/CO_TIME.h | 2 +- 301/CO_config.h | 33 ++- 301/CO_driver.h | 40 +++- 301/CO_fifo.c | 426 +++++++++++++++++++++++++-------- 301/CO_fifo.h | 40 +++- 309/CO_gateway_ascii.c | 311 +++++++++++++++--------- 309/CO_gateway_ascii.h | 26 +- README.md | 47 +++- doc/gettingStarted.md | 28 ++- doc/objectDictionary.md | 135 +++++------ example/CO_OD.c | 16 +- example/CO_driver_target.h | 11 +- example/Makefile | 2 +- example/_project.html | 311 ------------------------ example/_project.xml | 6 + socketCAN/CO_driver_target.h | 19 +- socketCAN/CO_epoll_interface.c | 35 ++- socketCAN/CO_error_msgs.h | 2 + socketCAN/CO_main_basic.c | 85 +++++-- 22 files changed, 1021 insertions(+), 749 deletions(-) delete mode 100644 example/_project.html diff --git a/301/CO_NMT_Heartbeat.h b/301/CO_NMT_Heartbeat.h index 36816cdb6..83661c1b3 100644 --- a/301/CO_NMT_Heartbeat.h +++ b/301/CO_NMT_Heartbeat.h @@ -248,13 +248,29 @@ CO_NMT_reset_cmd_t CO_NMT_process( * * @param NMT This object. * - * @return #CO_NMT_internalState_t + * @return @ref CO_NMT_internalState_t */ static inline CO_NMT_internalState_t CO_NMT_getInternalState(CO_NMT_t *NMT) { return (NMT == NULL) ? CO_NMT_INITIALIZING : NMT->operatingState; } +/** + * Set internal NMT state + * + * Functions sets state directly, without any checking. @ref CO_NMT_process() + * may also switch between states automatically, see @ref CO_NMT_control_t. + * + * @param NMT This object. + * @param state New state. + */ +static inline void CO_NMT_setInternalState(CO_NMT_t *NMT, + CO_NMT_internalState_t state) +{ + if (NMT != NULL) NMT->operatingState = state; +} + + #if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER) || defined CO_DOXYGEN /** * Send NMT master command. diff --git a/301/CO_SDOclient.c b/301/CO_SDOclient.c index 4e859ef2d..0d037e1fa 100644 --- a/301/CO_SDOclient.c +++ b/301/CO_SDOclient.c @@ -61,20 +61,20 @@ * description of parameters see file CO_driver.h. */ static void CO_SDOclient_receive(void *object, void *msg) { - CO_SDOclient_t *SDO_C; + CO_SDOclient_t *SDO_C = (CO_SDOclient_t*)object; uint8_t DLC = CO_CANrxMsg_readDLC(msg); uint8_t *data = CO_CANrxMsg_readData(msg); - /* change void pointer to correct object type */ - SDO_C = (CO_SDOclient_t*)object; - - /* verify idle state, message length and message overflow (previous message - * was not processed yet) */ + /* Ignore messages in idle state and messages with wrong length. Ignore + * message also if previous message was not processed yet and not abort */ if (SDO_C->state != CO_SDO_ST_IDLE && DLC == 8U - && !CO_FLAG_READ(SDO_C->CANrxNew) + && (!CO_FLAG_READ(SDO_C->CANrxNew) || data[0] == 0x80) ) { #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK - if (SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { + if (data[0] == 0x80 /* abort from server */ + || (SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ + && SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP) + ) { #endif /* copy data and set 'new message' flag */ memcpy((void *)&SDO_C->CANrxData[0], (const void *)&data[0], 8); @@ -89,21 +89,17 @@ static void CO_SDOclient_receive(void *object, void *msg) { #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK } - else { + else if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { /* block upload, copy data directly */ CO_SDO_state_t state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ; - uint8_t seqno; - - seqno = data[0] & 0x7F; + uint8_t seqno = data[0] & 0x7F; SDO_C->timeoutTimer = 0; SDO_C->block_timeoutTimer = 0; - /* break sub-block if sequence number is too large */ - if (seqno > SDO_C->block_blksize) { - state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; - } /* verify if sequence number is correct */ - else if (seqno == (SDO_C->block_seqno + 1)) { + if (seqno <= SDO_C->block_blksize + && seqno == (SDO_C->block_seqno + 1) + ) { SDO_C->block_seqno = seqno; /* is this the last segment? */ @@ -128,18 +124,33 @@ static void CO_SDOclient_receive(void *object, void *msg) { } } } - else if (seqno == SDO_C->block_seqno || SDO_C->block_seqno == 0U) { - /* Ignore message, if it is duplicate or if sequence didn't - * start yet. */ + /* If message is duplicate or sequence didn't start yet, ignore + * it. Otherwise seqno is wrong, so break sub-block. Data after + * last good seqno will be re-transmitted. */ + else if (seqno != SDO_C->block_seqno && SDO_C->block_seqno != 0U) { + state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; +#ifdef CO_DEBUG_SDO_CLIENT + char msg[80]; + sprintf(msg, + "sub-block, rx WRONG: sequno=%02X, previous=%02X", + seqno, SDO_C->block_seqno); + CO_DEBUG_SDO_CLIENT(msg); +#endif } +#ifdef CO_DEBUG_SDO_CLIENT else { - /* seqno is totally wrong, break sub-block. Data after last good - * seqno will be re-transmitted. */ - state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP; + char msg[80]; + sprintf(msg, + "sub-block, rx ignored: sequno=%02X, expected=%02X", + seqno, SDO_C->block_seqno + 1); + CO_DEBUG_SDO_CLIENT(msg); } +#endif + + /* Is exit from sub-block receive state? */ if (state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) { - /* SDO_C->state has changed, processing will continue in another - * thread. Make memory barrier here with CO_FLAG_CLEAR() call.*/ + /* Processing will continue in another thread, so make memory + * barrier here with CO_FLAG_CLEAR() call. */ CO_FLAG_CLEAR(SDO_C->CANrxNew); SDO_C->state = state; #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE @@ -181,16 +192,15 @@ CO_ReturnError_t CO_SDOclient_init(CO_SDOclient_t *SDO_C, SDO_C->CANdevRxIdx = CANdevRxIdx; SDO_C->CANdevTx = CANdevTx; SDO_C->CANdevTxIdx = CANdevTxIdx; - - /* prepare circular fifo buffer */ - CO_fifo_init(&SDO_C->bufFifo, SDO_C->buf, - CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1); - #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_CALLBACK_PRE SDO_C->pFunctSignal = NULL; SDO_C->functSignalObject = NULL; #endif + /* prepare circular fifo buffer */ + CO_fifo_init(&SDO_C->bufFifo, SDO_C->buf, + CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1); + SDO_C->state = CO_SDO_ST_IDLE; CO_FLAG_CLEAR(SDO_C->CANrxNew); @@ -372,8 +382,8 @@ void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C, /******************************************************************************/ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, - const char *buf, - size_t count) + const char *buf, + size_t count) { size_t ret = 0; if (SDO_C != NULL && buf != NULL) { @@ -387,11 +397,12 @@ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, uint32_t timeDifference_us, bool_t abort, + bool_t bufferPartial, CO_SDO_abortCode_t *SDOabortCode, size_t *sizeTransferred, uint32_t *timerNext_us) { - (void)timerNext_us; /* may be unused */ + (void)timerNext_us; (void) bufferPartial; /* may be unused */ CO_SDO_return_t ret = CO_SDO_RT_waitingResponse; CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE; @@ -403,14 +414,9 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, else if (SDO_C->state == CO_SDO_ST_IDLE) { ret = CO_SDO_RT_ok_communicationEnd; } - else if (abort) { - abortCode = - (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; - SDO_C->state = CO_SDO_ST_ABORT; - } #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL /* Transfer data locally **************************************************/ - else if (SDO_C->state == CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER) { + else if (SDO_C->state == CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER && !abort) { if (SDO_C->SDO->state != CO_SDO_ST_IDLE) { abortCode = CO_SDO_AB_DEVICE_INCOMPAT; ret = CO_SDO_RT_endedWithClientAbort; @@ -443,7 +449,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, } SDO_C->state = CO_SDO_ST_IDLE; } -#endif +#endif /* CO_CONFIG_SDO_CLI_LOCAL */ /* CAN data received ******************************************************/ else if (CO_FLAG_READ(SDO_C->CANrxNew)) { /* is SDO abort */ @@ -454,6 +460,11 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, SDO_C->state = CO_SDO_ST_IDLE; ret = CO_SDO_RT_endedWithServerAbort; } + else if (abort) { + abortCode = (SDOabortCode != NULL) + ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else switch (SDO_C->state) { case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: { if (SDO_C->CANrxData[0] == 0x60) { @@ -613,6 +624,11 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, timeDifference_us = 0; CO_FLAG_CLEAR(SDO_C->CANrxNew); } + else if (abort) { + abortCode = (SDOabortCode != NULL) + ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } /* Timeout timers *********************************************************/ if (ret == CO_SDO_RT_waitingResponse) { @@ -720,7 +736,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, SDO_C->CANtxBuff->data[0] = SDO_C->toggle | ((7 - count) << 1); /* is end of transfer? Verify also sizeTran */ - if (CO_fifo_getOccupied(&SDO_C->bufFifo) == 0) { + if (CO_fifo_getOccupied(&SDO_C->bufFifo) == 0 && !bufferPartial) { if (SDO_C->sizeInd > 0 && SDO_C->sizeTran < SDO_C->sizeInd) { abortCode = CO_SDO_AB_DATA_SHORT; SDO_C->state = CO_SDO_ST_ABORT; @@ -760,9 +776,13 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, } case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: { + if (CO_fifo_altGetOccupied(&SDO_C->bufFifo) < 7 && bufferPartial) { + /* wait until data are refilled */ + break; + } SDO_C->CANtxBuff->data[0] = ++SDO_C->block_seqno; - /* fill data bytes */ + /* get up to 7 data bytes */ count = CO_fifo_altRead(&SDO_C->bufFifo, (char *)&SDO_C->CANtxBuff->data[1], 7); SDO_C->block_noData = 7 - count; @@ -777,7 +797,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, } /* is end of transfer? Verify also sizeTran */ - if (CO_fifo_altGetOccupied(&SDO_C->bufFifo) == 0) { + if (CO_fifo_altGetOccupied(&SDO_C->bufFifo) == 0 && !bufferPartial){ if (SDO_C->sizeInd > 0 && SDO_C->sizeTran < SDO_C->sizeInd) { abortCode = CO_SDO_AB_DATA_SHORT; SDO_C->state = CO_SDO_ST_ABORT; @@ -880,7 +900,7 @@ CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C, SDO_C->SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 1000; SDO_C->timeoutTimer = 0; #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK - SDO_C->block_SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 500; + SDO_C->block_SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 700; #endif #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL @@ -912,6 +932,7 @@ CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C, /******************************************************************************/ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, uint32_t timeDifference_us, + bool_t abort, CO_SDO_abortCode_t *SDOabortCode, size_t *sizeIndicated, size_t *sizeTransferred, @@ -931,7 +952,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, } #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_LOCAL /* Transfer data locally **************************************************/ - else if (SDO_C->state == CO_SDO_ST_UPLOAD_LOCAL_TRANSFER) { + else if (SDO_C->state == CO_SDO_ST_UPLOAD_LOCAL_TRANSFER && !abort) { if (SDO_C->SDO->state != 0) { abortCode = CO_SDO_AB_DEVICE_INCOMPAT; ret = CO_SDO_RT_endedWithClientAbort; @@ -971,7 +992,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, } SDO_C->state = CO_SDO_ST_IDLE; } -#endif +#endif /* CO_CONFIG_SDO_CLI_LOCAL */ /* CAN data received ******************************************************/ else if (CO_FLAG_READ(SDO_C->CANrxNew)) { /* is SDO abort */ @@ -982,6 +1003,11 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, SDO_C->state = CO_SDO_ST_IDLE; ret = CO_SDO_RT_endedWithServerAbort; } + else if (abort) { + abortCode = (SDOabortCode != NULL) + ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } else switch (SDO_C->state) { case CO_SDO_ST_UPLOAD_INITIATE_RSP: { if ((SDO_C->CANrxData[0] & 0xF0) == 0x40) { @@ -1231,6 +1257,11 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, timeDifference_us = 0; CO_FLAG_CLEAR(SDO_C->CANrxNew); } + else if (abort) { + abortCode = (SDOabortCode != NULL) + ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT; + SDO_C->state = CO_SDO_ST_ABORT; + } /* Timeout timers *********************************************************/ if (ret == CO_SDO_RT_waitingResponse) { @@ -1289,7 +1320,6 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, } /* Transmit CAN data ******************************************************/ - if (ret == CO_SDO_RT_waitingResponse) { #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK size_t count; @@ -1374,6 +1404,10 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: { SDO_C->CANtxBuff->data[0] = 0xA2; SDO_C->CANtxBuff->data[1] = SDO_C->block_seqno; +#ifdef CO_DEBUG_SDO_CLIENT + bool_t transferShort = SDO_C->block_seqno != SDO_C->block_blksize; + uint8_t seqnoStart = SDO_C->block_seqno; +#endif /* Is last segment? */ if (SDO_C->finished) { @@ -1392,9 +1426,18 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, if (count >= 127) { count = 127; } - else if (CO_fifo_altGetOccupied(&SDO_C->bufFifo) > 0) { + else if (CO_fifo_getOccupied(&SDO_C->bufFifo) > 0) { /* application must empty data buffer first */ ret = CO_SDO_RT_uploadDataBufferFull; +#ifdef CO_DEBUG_SDO_CLIENT + if (transferShort) { + char msg[80]; + sprintf(msg, + "sub-block, uploadDataBufferFull: sequno=%02X", + seqnoStart); + CO_DEBUG_SDO_CLIENT(msg); + } +#endif #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_TIMERNEXT /* Inform OS to call this function again without delay. */ if (timerNext_us != NULL) { @@ -1413,10 +1456,18 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, SDO_C->CANtxBuff->data[2] = SDO_C->block_blksize; - /* reset timeout timer and send message */ - SDO_C->timeoutTimer = 0; + /* reset block_timeoutTimer, but not SDO_C->timeoutTimer */ SDO_C->block_timeoutTimer = 0; CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff); +#ifdef CO_DEBUG_SDO_CLIENT + if (transferShort && !SDO_C->finished) { + char msg[80]; + sprintf(msg, + "sub-block restarted: sequnoPrev=%02X, blksize=%02X", + seqnoStart, SDO_C->block_blksize); + CO_DEBUG_SDO_CLIENT(msg); + } +#endif break; } diff --git a/301/CO_SDOclient.h b/301/CO_SDOclient.h index d8bf90fef..5fcdb0090 100644 --- a/301/CO_SDOclient.h +++ b/301/CO_SDOclient.h @@ -36,7 +36,11 @@ #define CO_CONFIG_SDO_CLI (0) #endif #ifndef CO_CONFIG_SDO_CLI_BUFFER_SIZE -#define CO_CONFIG_SDO_CLI_BUFFER_SIZE 32 + #if (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK + #define CO_CONFIG_SDO_CLI_BUFFER_SIZE 1000 + #else + #define CO_CONFIG_SDO_CLI_BUFFER_SIZE 32 + #endif #endif #if ((CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_ENABLE) || defined CO_DOXYGEN @@ -303,8 +307,8 @@ void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C, * @return number of bytes actually written. */ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, - const char *buf, - size_t count); + const char *buf, + size_t count); /** @@ -323,6 +327,8 @@ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, * [microseconds]. * @param abort If true, SDO client will send abort message from SDOabortCode * and transmission will be aborted. + * @param bufferPartial True indicates, not all data were copied to internal + * buffer yet. Buffer will be refilled later with #CO_SDOclientDownloadBufWrite. * @param [out] SDOabortCode In case of error in communication, SDO abort code * contains reason of error. Ignored if NULL. * @param [out] sizeTransferred Actual size of data transferred. Ignored if NULL @@ -334,11 +340,12 @@ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, * communication is in progress. */ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, - uint32_t timeDifference_us, - bool_t abort, - CO_SDO_abortCode_t *SDOabortCode, - size_t *sizeTransferred, - uint32_t *timerNext_us); + uint32_t timeDifference_us, + bool_t abort, + bool_t bufferPartial, + CO_SDO_abortCode_t *SDOabortCode, + size_t *sizeTransferred, + uint32_t *timerNext_us); /** @@ -379,6 +386,8 @@ CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C, * @param SDO_C This object. * @param timeDifference_us Time difference from previous function call in * [microseconds]. + * @param abort If true, SDO client will send abort message from SDOabortCode + * and reception will be aborted. * @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 @@ -393,6 +402,7 @@ CO_SDO_return_t CO_SDOclientUploadInitiate(CO_SDOclient_t *SDO_C, */ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, uint32_t timeDifference_us, + bool_t abort, CO_SDO_abortCode_t *SDOabortCode, size_t *sizeIndicated, size_t *sizeTransferred, diff --git a/301/CO_TIME.h b/301/CO_TIME.h index 689ae248d..44cdf3cbc 100644 --- a/301/CO_TIME.h +++ b/301/CO_TIME.h @@ -87,7 +87,7 @@ typedef union { unsigned days:16; unsigned reserved2:16; }; -}timeOfDay_t; +} timeOfDay_t; #endif typedef timeOfDay_t TIME_OF_DAY; diff --git a/301/CO_config.h b/301/CO_config.h index 58872e64a..1b82de1cc 100644 --- a/301/CO_config.h +++ b/301/CO_config.h @@ -374,13 +374,13 @@ extern "C" { /** * Size of the internal data buffer for the SDO client. * - * Circular buffer is used for SDO communication. it can be read or written + * Circular buffer is used for SDO communication. It can be read or written * between successive SDO calls. So size of the buffer can be lower than size of * the actual size of data transferred. If only segmented transfer is used, then * buffer size can be as low as 7 bytes, if data are read/written each cycle. If - * block transfer is used, buffer size should be set to at least 889 bytes, so - * maximum blksize can be used. In that case data should be read/written proper - * time between cycles. + * block transfer is used, buffer size should be set to at least 1000 bytes, so + * maximum blksize can be used (blksize is calculated from free buffer space). + * Default value for block transfer is 1000, otherwise 32. */ #ifdef CO_DOXYGEN #define CO_CONFIG_SDO_CLI_BUFFER_SIZE 32 @@ -711,6 +711,31 @@ extern "C" { #define CO_CONFIG_TRACE_OWN_INTTYPES 0x02 /** @} */ /* CO_STACK_CONFIG_TRACE */ + +/** + * @defgroup CO_STACK_CONFIG_DEBUG Debug messages + * Messages from different parts of the stack. + * @{ + */ +/** + * Configuration of debug messages from different parts of the stack, which can + * be logged according to target specific function. + * + * Possible flags, can be ORed: + * - CO_CONFIG_DEBUG_COMMON - Define default CO_DEBUG_COMMON(msg) macro. This + * macro is target specific. This macro is then used as default macro in all + * other defined CO_DEBUG_XXX(msg) macros. + * - CO_CONFIG_DEBUG_SDO_CLIENT - Define default CO_DEBUG_SDO_CLIENT(msg) macro. + * - CO_CONFIG_DEBUG_SDO_SERVER - Define default CO_DEBUG_SDO_SERVER(msg) macro. + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_DEBUG (0) +#endif +#define CO_CONFIG_DEBUG_COMMON 0x01 +#define CO_CONFIG_DEBUG_SDO_CLIENT 0x02 +#define CO_CONFIG_DEBUG_SDO_SERVER 0x04 +/** @} */ /* CO_STACK_CONFIG_DEBUG */ + /** @} */ /* CO_STACK_CONFIG */ #ifdef __cplusplus diff --git a/301/CO_driver.h b/301/CO_driver.h index 62a491b1f..84841f973 100644 --- a/301/CO_driver.h +++ b/301/CO_driver.h @@ -35,6 +35,15 @@ extern "C" { #endif +#ifdef CO_DEBUG_COMMON +#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_CLIENT + #define CO_DEBUG_SDO_CLIENT(msg) CO_DEBUG_COMMON(msg) +#endif +#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_SERVER + #define CO_DEBUG_SDO_SERVER(msg) CO_DEBUG_COMMON(msg) +#endif +#endif + /** * @defgroup CO_driver Driver * @ingroup CO_CANopen_301 @@ -199,7 +208,7 @@ void CANrx_callback(void *object, void *rxMsg); * * This is target specific function and is specific for specific * microcontroller. It is best to implement it by using inline function or - * macro. `rxMsg` parameter should cast to a ponter to structure. For best + * macro. `rxMsg` parameter should cast to a pointer to structure. For best * efficiency structure may have the same alignment as CAN registers inside CAN * module. * @@ -329,6 +338,7 @@ typedef struct { volatile uint16_t CANtxCount; /**< Number of messages in transmit buffer, which are waiting to be copied to the CAN module */ uint32_t errOld; /**< Previous state of CAN errors */ + int32_t errinfo; /**< For use with @ref CO_errinfo() */ } CO_CANmodule_t; @@ -398,12 +408,30 @@ typedef struct { #endif /* CO_DOXYGEN */ +/** Macro for passing additional information about error. + * + * This macro is called from several CANopen init functions, which returns + * @ref CO_ReturnError_t. + * + * CO_driver_target.h may implement this macro. Usually macro only sets + * CANmodule->errinfo to err. Application may then use CANmodule->errinfo to + * determine the reason of failure. errinfo must be type of int32_t. By default + * macro does not record anything. + * + * CO_errinfo is called in following @ref CO_ReturnError_t reasons: + * - 'CO_ERROR_OD_PARAMETERS' - Index of erroneous OD parameter. + */ +#ifndef CO_errinfo +#define CO_errinfo(CANmodule, err) CANmodule->errinfo = err +#endif + + /** * Default CANopen identifiers. * * Default CANopen identifiers for CANopen communication objects. Same as * 11-bit addresses of CAN messages. These are default identifiers and - * can be changed in CANopen. Especially PDO identifiers are confgured + * can be changed in CANopen. Especially PDO identifiers are configured * in PDO linking phase of the CANopen network configuration. */ typedef enum { @@ -472,7 +500,7 @@ typedef enum { CO_ERROR_TX_OVERFLOW = -9, /**< Previous message is still waiting, buffer full */ CO_ERROR_TX_PDO_WINDOW = -10, /**< Synchronous TPDO is outside window */ - CO_ERROR_TX_UNCONFIGURED = -11, /**< Transmit buffer was not confugured + CO_ERROR_TX_UNCONFIGURED = -11, /**< Transmit buffer was not configured properly */ CO_ERROR_OD_PARAMETERS = -12, /**< Error in Object Dictionary parameters*/ CO_ERROR_DATA_CORRUPT = -13, /**< Stored data are corrupt */ @@ -490,7 +518,7 @@ typedef enum { /** - * Request CAN configuration (stopped) mode and *wait* untill it is set. + * Request CAN configuration (stopped) mode and *wait* until it is set. * * @param CANptr Pointer to CAN device */ @@ -498,7 +526,7 @@ void CO_CANsetConfigurationMode(void *CANptr); /** - * Request CAN normal (opearational) mode and *wait* untill it is set. + * Request CAN normal (operational) mode and *wait* until it is set. * * @param CANmodule CO_CANmodule_t object. */ @@ -588,7 +616,7 @@ CO_ReturnError_t CO_CANrxBufferInit(CO_CANmodule_t *CANmodule, * @param rtr If true, 'Remote Transmit Request' messages will be transmitted. * @param noOfBytes Length of CAN message in bytes (0 to 8 bytes). * @param syncFlag This flag bit is used for synchronous TPDO messages. If it is - * set, message will not be sent, if curent time is outside synchronous window. + * set, message will not be sent, if current time is outside synchronous window. * * @return Pointer to CAN transmit message buffer. 8 bytes data array inside * buffer should be written, before CO_CANsend() function is called. diff --git a/301/CO_fifo.c b/301/CO_fifo.c index 66c0706ac..6f49c54cf 100644 --- a/301/CO_fifo.c +++ b/301/CO_fifo.c @@ -308,27 +308,17 @@ bool_t CO_fifo_CommSearch(CO_fifo_t *fifo, bool_t clear) { /******************************************************************************/ -bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo) { +bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo, bool_t *insideComment) { bool_t delimCommandFound = false; - if (fifo != NULL) { + if (fifo != NULL && insideComment != NULL) { while (fifo->readPtr != fifo->writePtr) { char c = fifo->buf[fifo->readPtr]; if (c == DELIM_COMMENT) { - /* comment found, clear all until next DELIM_COMMAND */ - while (fifo->readPtr != fifo->writePtr) { - if (++fifo->readPtr == fifo->bufSize) { - fifo->readPtr = 0; - } - if (c == DELIM_COMMAND) { - delimCommandFound = true; - break; - } - } - break; + *insideComment = true; } - else if (isgraph((int)c) != 0) { + else if (isgraph((int)c) != 0 && !(*insideComment)) { break; } if (++fifo->readPtr == fifo->bufSize) { @@ -336,6 +326,7 @@ bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo) { } if (c == DELIM_COMMAND) { delimCommandFound = true; + *insideComment = false; break; } } @@ -471,6 +462,24 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, #if (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_DATATYPES /******************************************************************************/ +/* Tables for mime-base64 encoding, as specified in RFC 2045, (without CR-LF, + * but one long string). Base64 is used for encoding binary data into easy + * transferable printable characters. In general, each three bytes of binary + * data are translated into four characters, where characters are selected from + * 64 characters long table. See https://en.wikipedia.org/wiki/Base64 */ +static const char base64EncTable[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const char base64DecTable[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1,103,101, -1, -1,102, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 103, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,100, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; + size_t CO_fifo_readU82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint8_t n=0; @@ -684,11 +693,80 @@ size_t CO_fifo_readVs2a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } break; } - if (c == '"') { - buf[len++] = '"'; + else if (c != 0 && c != '\r') { /* skip null and CR inside string */ + buf[len++] = c; + if (c == '"') { + buf[len++] = c; + } + } + } + } + + return len; +} + +size_t CO_fifo_readB642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { + /* mime-base64 encoding, see description above base64EncTable */ + + size_t len = 0; + + if (fifo != NULL && count >= 4) { + uint8_t step; + uint16_t word; + + if (!fifo->started) { + fifo->started = true; + step = 0; + word = 0; + } + else { + /* get memorized variables from previous function calls */ + step = (uint8_t)(fifo->aux >> 16); + word = (uint16_t)fifo->aux; + } + + while ((len + 3) <= count) { + char c; + + if(!CO_fifo_getc(fifo, &c)) { + /* buffer is empty, is also SDO communication finished? */ + if (end) { + /* add padding if necessary */ + switch (step) { + case 1: + buf[len++] = base64EncTable[(word >> 4) & 0x3F]; + buf[len++] = '='; + buf[len++] = '='; + break; + case 2: + buf[len++] = base64EncTable[(word >> 6) & 0x3F]; + buf[len++] = '='; + break; + } + } + break; + } + + word |= (uint8_t)c; + + switch (step++) { + case 0: + buf[len++] = base64EncTable[(word >> 2) & 0x3F]; + break; + case 1: + buf[len++] = base64EncTable[(word >> 4) & 0x3F]; + break; + default: + buf[len++] = base64EncTable[(word >> 6) & 0x3F]; + buf[len++] = base64EncTable[word & 0x3F]; + step = 0; + break; } - buf[len++] = c; + word <<= 8; } + + /* memorize variables for next iteration */ + fifo->aux = (uint32_t)step << 16 | word; } return len; @@ -921,6 +999,8 @@ size_t CO_fifo_cpyTok2R64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t destSpace, destSpaceStart; bool_t finished = false; + uint8_t step; + char firstChar; CO_fifo_st st = 0; if (dest == NULL || src == NULL) { @@ -930,6 +1010,23 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { /* get free space of the dest fifo */ destSpaceStart = destSpace = CO_fifo_getSpace(dest); + /* is this the first write into dest? */ + if (!dest->started) { + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || insideComment) { + /* command delimiter found without string, this is an error */ + st |= CO_fifo_st_errTok; + } + dest->started = true; + step = 0; + firstChar = 0; + } + else { + /* get memorized variables from previous function calls */ + step = (uint8_t)(dest->aux >> 8); + firstChar = (char)(dest->aux & 0xFF); + } + /* repeat until destination space available and no error and not finished * and source characters available */ while (destSpace > 0 && (st & CO_fifo_st_errMask) == 0 && !finished) { @@ -938,81 +1035,68 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { break; } - if (dest->started == false) { - /* search for first of two hex digits in token */ - if (isxdigit((int)c) != 0) { - /* first hex digit is known */ - dest->aux = c; - dest->started = true; - } - else if (c == DELIM_COMMAND) { - /* newline found, finish */ + if (step == 6) { + /* command is inside comment, waiting for command delimiter */ + bool_t insideComment = true; + if (c == DELIM_COMMAND || CO_fifo_trimSpaces(src, &insideComment)) { st |= CO_fifo_st_closed; finished = true; } - else if (c == DELIM_COMMENT) { - /* comment found, clear line and finish */ - while (CO_fifo_getc(src, &c)) { - if (c == DELIM_COMMAND) { - st |= CO_fifo_st_closed; - break; - } - } - finished = true; - } - else if (isgraph((int)c) != 0) { - /* printable character, not hex digit, error */ - st |= CO_fifo_st_errTok; - } - /* else just skip empty space */ + continue; } - else { - /* one hex digit is known, what is next */ - if (isxdigit((int)c) != 0) { - /* two hex digits are known */ + + if (isxdigit((int)c) != 0) { + /* first or second hex digit */ + if (step == 0) { + firstChar = c; + step = 1; + } + else { + /* write the byte */ char s[3]; int32_t num; - s[0] = (char) dest->aux; s[1] = c, s[2] = 0; + s[0] = firstChar; s[1] = c; s[2] = 0; num = strtol(s, NULL, 16); - /* copy the character */ CO_fifo_putc(dest, (char) num); destSpace--; + step = 0; } - else if (isgraph((int)c) != 0 && c != DELIM_COMMENT) { - /* printable character, not hex digit, error */ + } + else if (isgraph((int)c) != 0) { + /* printable character, not hex digit */ + if (c == '#') /* comment start */ + step = 6; + else /* syntax error */ st |= CO_fifo_st_errTok; - } - else { - /* this is space or comment, single hex digit is known */ + } + else { + /* this is space or delimiter */ + if (step == 1) { + /* write the byte */ char s[2]; int32_t num; - s[0] = (char) dest->aux; s[1] = 0; + s[0] = firstChar; s[1] = 0; num = strtol(s, NULL, 16); - /* copy the character */ CO_fifo_putc(dest, (char) num); destSpace--; - if (c == DELIM_COMMAND) { - /* newline found, finish */ - st |= CO_fifo_st_closed; - finished = true; - } - else if (c == DELIM_COMMENT) { - /* comment found, clear line and finish */ - while (CO_fifo_getc(src, &c)) { - if (c == DELIM_COMMAND) { - st |= CO_fifo_st_closed; - break; - } - } - finished = true; - } + step = 0; + } + bool_t insideComment = false; + if (c == DELIM_COMMAND || CO_fifo_trimSpaces(src, &insideComment)) { + /* newline found, finish */ + st |= CO_fifo_st_closed; + finished = true; + } + else if (insideComment) { + step = 6; } - dest->started = false; } - } + } /* while ... */ if (!finished) { st |= CO_fifo_st_partial; + /* memorize variables for next iteration */ + dest->aux = (uint32_t)step << 8 | (uint8_t)firstChar; } if (status != NULL) *status = st; @@ -1023,6 +1107,7 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t destSpace, destSpaceStart; bool_t finished = false; + uint8_t step; CO_fifo_st st = 0; if (dest == NULL || src == NULL) { @@ -1034,9 +1119,17 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { /* is this the first write into dest? */ if (!dest->started) { - /* aux variable is used as state here */ - dest->aux = 0; + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || insideComment) { + /* command delimiter found without string, this is an error */ + st |= CO_fifo_st_errTok; + } dest->started = true; + step = 0; + } + else { + /* get memorized variables from previous function calls */ + step = (uint8_t)dest->aux; } /* repeat until destination space available and no error and not finished @@ -1047,43 +1140,40 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { break; } - switch (dest->aux) { + switch (step) { case 0: /* beginning of the string, first write into dest */ - if (isgraph((int)c) == 0 || c == DELIM_COMMENT) { - if (CO_fifo_trimSpaces(src)) { - /* newline found without string, this is an error */ - st |= CO_fifo_st_errTok; - } - } - else if (c == '"') { + if (c == '"') { /* Indicated beginning of the string, skip this character. */ - dest->aux = 1; + step = 1; } else { /* this must be a single word string without '"' */ /* copy the character */ CO_fifo_putc(dest, c); destSpace--; - dest->aux = 2; + step = 2; } break; case 1: /* inside string, quoted string */ case 2: /* inside string, single word, no quotes */ if (c == '"') { - /* double qoute found, this may be end of the string or escaped + /* double quote found, this may be end of the string or escaped * double quote (with two double quotes) */ - dest->aux += 2; + step += 2; } - else if (isgraph((int)c) == 0 && dest->aux == 2) { + else if (isgraph((int)c) == 0 && step == 2) { /* end of single word string */ - if (c == DELIM_COMMAND) { + bool_t insideComment = false; + if (c == DELIM_COMMAND + || CO_fifo_trimSpaces(src, &insideComment) + ) { st |= CO_fifo_st_closed; + finished = true; } - else if (CO_fifo_trimSpaces(src)) { - st |= CO_fifo_st_closed; + else { + step = insideComment ? 6 : 5; } - finished = true; } else if (c == DELIM_COMMAND) { /* no closing quote, error */ @@ -1102,24 +1192,27 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { /* escaped double quote, copy the character and continue */ CO_fifo_putc(dest, c); destSpace--; - dest->aux -= 2; + step -= 2; } else { /* previous character was closing double quote */ - if (dest->aux == 4) { + if (step == 4) { /* no opening double quote, syntax error */ st |= CO_fifo_st_errTok; } else { if (isgraph((int)c) == 0) { - /* string finished */ - if (c == DELIM_COMMAND) { + /* end of quoted string */ + bool_t insideComment = false; + if (c == DELIM_COMMAND + || CO_fifo_trimSpaces(src, &insideComment) + ) { st |= CO_fifo_st_closed; + finished = true; } - else if (CO_fifo_trimSpaces(src)) { - st |= CO_fifo_st_closed; + else { + step = insideComment ? 6 : 5; } - finished = true; } else { /* space must follow closing double quote, error */ @@ -1129,6 +1222,31 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { } break; + case 5: { /* String token is finished, waiting for command delimiter */ + bool_t insideComment = false; + if (c == DELIM_COMMAND || CO_fifo_trimSpaces(src, &insideComment)) { + st |= CO_fifo_st_closed; + finished = true; + } + else if (insideComment) { + step = 6; + } + else if (isgraph((int)c) != 0) { + if (c == '#') /* comment start */ + step = 6; + else /* syntax error */ + st |= CO_fifo_st_errTok; + } + break; + } + case 6: { /* String token is finished, waiting for command delimiter */ + bool_t insideComment = true; + if (c == DELIM_COMMAND || CO_fifo_trimSpaces(src, &insideComment)) { + st |= CO_fifo_st_closed; + finished = true; + } + break; + } default: /* internal error */ st |= CO_fifo_st_errInt; break; @@ -1137,6 +1255,120 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (!finished) { st |= CO_fifo_st_partial; + /* memorize variables for next iteration */ + dest->aux = step; + } + + if (status != NULL) *status = st; + + return destSpaceStart - destSpace; +} + +size_t CO_fifo_cpyTok2B64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { + /* mime-base64 decoding, see description above base64EncTable */ + + size_t destSpace, destSpaceStart; + bool_t finished = false; + uint8_t step; + uint32_t dword; + CO_fifo_st st = 0; + + if (dest == NULL || src == NULL) { + return 0; + } + + /* get free space of the dest fifo */ + destSpaceStart = destSpace = CO_fifo_getSpace(dest); + + /* is this the first write into dest? */ + if (!dest->started) { + bool_t insideComment = false; + if (CO_fifo_trimSpaces(src, &insideComment) || insideComment) { + /* command delimiter found without string, this is an error */ + st |= CO_fifo_st_errTok; + } + dest->started = true; + step = 0; + dword = 0; + } + else { + /* get memorized variables from previous function calls */ + step = (uint8_t)(dest->aux >> 24); + dword = dest->aux & 0xFFFFFF; + } + + /* repeat until destination space available and no error and not finished + * and source characters available */ + while (destSpace >= 3 && (st & CO_fifo_st_errMask) == 0 && !finished) { + char c; + if (!CO_fifo_getc(src, &c)) { + break; + } + + if (step >= 5) { + /* String token is finished, waiting for command delimiter */ + bool_t insideComment = step > 5; + if (c == DELIM_COMMAND || CO_fifo_trimSpaces(src, &insideComment)) { + st |= CO_fifo_st_closed; + finished = true; + } + else if (insideComment) { + step = 6; + } + else if (isgraph((int)c) != 0 && c != '=') { + if (c == '#') /* comment start */ + step = 6; + else /* syntax error */ + st |= CO_fifo_st_errTok; + } + continue; + } + + char code = base64DecTable[c & 0x7F]; + + if (c < 0 || code < 0) { + st |= CO_fifo_st_errTok; + } + else if (code >= 64 /* '=' (pad) or DELIM_COMMAND or space */) { + /* base64 string finished, write remaining bytes */ + switch (step) { + case 2: + CO_fifo_putc(dest, (char)(dword >> 4)); + destSpace --; + break; + case 3: + CO_fifo_putc(dest, (char)(dword >> 10)); + CO_fifo_putc(dest, (char)(dword >> 2)); + destSpace -= 2; + break; + } + + bool_t insideComment = false; + if (c == DELIM_COMMAND || CO_fifo_trimSpaces(src, &insideComment)) { + st |= CO_fifo_st_closed; + finished = true; + } + else { + step = insideComment ? 6 : 5; + } + } + else { + dword = (dword << 6) | code; + if (step++ == 3) { + CO_fifo_putc(dest, (char)((dword >> 16) & 0xFF)); + CO_fifo_putc(dest, (char)((dword >> 8) & 0xFF)); + CO_fifo_putc(dest, (char)(dword & 0xFF)); + destSpace -= 3; + dword = 0; + step = 0; + } + } + } /* while ... */ + + if (!finished) { + st |= CO_fifo_st_partial; + /* memorize variables for next iteration */ + dest->aux = (uint32_t)step << 24 | (dword & 0xFFFFFF); } if (status != NULL) *status = st; diff --git a/301/CO_fifo.h b/301/CO_fifo.h index 159ec29f7..968e39d1d 100644 --- a/301/CO_fifo.h +++ b/301/CO_fifo.h @@ -110,6 +110,24 @@ static inline void CO_fifo_reset(CO_fifo_t *fifo) { } +/** + * Purge all data in fifo object, keep other properties + * + * @param fifo This object + * + * @return true, if data were purged or false, if fifo were already empty + */ +static inline bool_t CO_fifo_purge(CO_fifo_t *fifo) { + if (fifo != NULL && fifo->readPtr != fifo->writePtr) { + fifo->readPtr = 0; + fifo->writePtr = 0; + return true; + } + + return false; +} + + /** * Get free buffer space in CO_fifo_t object * @@ -342,10 +360,13 @@ bool_t CO_fifo_CommSearch(CO_fifo_t *fifo, bool_t clear); * is also removed). * * @param fifo This object. + * @param [in, out] insideComment if set to true as input, it skips all + * characters and searches only for delimiter. As output it is set to true, if + * fifo is empty, is inside comment and command delimiter is not found. * * @return true if command delimiter was found. */ -bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo); +bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo, bool_t *insideComment); /** @@ -400,7 +421,7 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, * @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. + * @param end True indicates, that fifo contains last bytes of data. * * @return Number of ascii bytes written into buf. */ @@ -437,8 +458,14 @@ 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 */ + * is great”. UTF-8 characters and also line breaks works with this function. + * Function removes all NULL and CR characters from output string. + * See also CO_fifo_readU82a */ size_t CO_fifo_readVs2a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end); +/** Read data from fifo and output as mime-base64 encoded string. Encoding is as + * specified in RFC 2045, without CR-LF, but one long string. See also + * CO_fifo_readU82a */ +size_t CO_fifo_readB642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end); /** Bitfields for status argument from CO_fifo_cpyTok2U8 function and similar */ @@ -493,8 +520,13 @@ size_t CO_fifo_cpyTok2R64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status); size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *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 */ + * the quotes are escaped by a second quotes. Input string can not contain + * newline characters. See CO_fifo_cpyTok2U8 */ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status); +/** Read ascii mime-base64 encoded string from src fifo and copy as binary data + * to dest fifo. Encoding is as specified in RFC 2045, without CR-LF, but one + * long string in single line. See also CO_fifo_readU82a */ +size_t CO_fifo_cpyTok2B64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status); #endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_DATATYPES */ diff --git a/309/CO_gateway_ascii.c b/309/CO_gateway_ascii.c index dab6fb0d3..f65a2fa7d 100644 --- a/309/CO_gateway_ascii.c +++ b/309/CO_gateway_ascii.c @@ -51,8 +51,8 @@ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO) || defined CO_DOXYGEN CO_SDOclient_t* SDO_C, - uint16_t SDOtimeoutTimeDefault, - bool_t SDOblockTransferEnableDefault, + uint16_t SDOclientTimeoutTime_ms, + bool_t SDOclientBlockTransfer, #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT) || defined CO_DOXYGEN CO_NMT_t *NMT, @@ -69,7 +69,7 @@ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, /* verify arguments */ if (gtwa == NULL #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO - || SDO_C == NULL || SDOtimeoutTimeDefault == 0 + || SDO_C == NULL || SDOclientTimeoutTime_ms == 0 #endif #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT || NMT == NULL @@ -90,8 +90,8 @@ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, /* initialize variables */ #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO gtwa->SDO_C = SDO_C; - gtwa->SDOtimeoutTime = SDOtimeoutTimeDefault; - gtwa->SDOblockTransferEnable = SDOblockTransferEnableDefault; + gtwa->SDOtimeoutTime = SDOclientTimeoutTime_ms; + gtwa->SDOblockTransferEnable = SDOclientBlockTransfer; #endif #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT gtwa->NMT = NMT; @@ -125,7 +125,8 @@ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, void CO_GTWA_initRead(CO_GTWA_t* gtwa, size_t (*readCallback)(void *object, const char *buf, - size_t count), + size_t count, + uint8_t *connectionOK), void *readCallbackObject) { if (gtwa != NULL) { @@ -153,74 +154,73 @@ void CO_GTWA_log_print(CO_GTWA_t* gtwa, const char *message) { * HELPER FUNCTIONS ******************************************************************************/ #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_HELP -/* help strings */ +/* help strings ("\n" is used between string, "\r\n" closes the response.) */ static const char CO_GTWA_helpString[] = -"\r\nCommand strings start with '\"[\"\"]\"' followed by:\r\n" \ -"[[] ] r[ead] [] # SDO upload.\r\n" \ -"[[] ] w[rite] # SDO download.\r\n" \ -"\r\n" \ -"[[] ] start # NMT Start node.\r\n" \ -"[[] ] stop # NMT Stop node.\r\n" \ -"[[] ] preop[erational] # NMT Set node to pre-operational.\r\n" \ -"[[] ] reset node # NMT Reset node.\r\n" \ -"[[] ] reset comm[unication] # NMT Reset communication.\r\n" \ -"\r\n" \ -"[] set network # Set default net.\r\n" \ -"[] set node # Set default node.\r\n" \ -"[] set sdo_timeout # Configure SDO time-out.\r\n" \ -"[] set sdo_block # Enable/disable SDO block transfer.\r\n" \ -"\r\n" \ -"help [datatype|lss] # Print this or datatype or lss help.\r\n" \ -"led # Print status LED diodes.\r\n" \ -"log # Print message log.\r\n" \ -"\r\n" \ -"Response:\r\n" \ -"\"[\"\"]\" OK | |\r\n" \ -" ERROR: | ERROR:\r\n" \ -"\r\n" \ -"* Every command must be terminated with ('\\r\\n'). characters. Same\r\n" \ -" is response. String is not null terminated, is optional in command.\r\n" \ -"* Comments started with '#' are ignored. They may be on the beginning of the\r\n" \ -" line or after the command string.\r\n" \ -"* 'sdo_timeout' is in milliseconds, 500 by default. Block transfer is\r\n" \ -" disabled by default.\r\n" \ -"* If '' or '' is not specified within commands, then value defined\r\n" \ +"\nCommand strings start with '\"[\"\"]\"' followed by:\n" \ +"[[] ] r[ead] [] # SDO upload.\n" \ +"[[] ] w[rite] # SDO download.\n" \ +"\n" \ +"[[] ] start # NMT Start node.\n" \ +"[[] ] stop # NMT Stop node.\n" \ +"[[] ] preop[erational] # NMT Set node to pre-operational.\n" \ +"[[] ] reset node # NMT Reset node.\n" \ +"[[] ] reset comm[unication] # NMT Reset communication.\n" \ +"\n" \ +"[] set network # Set default net.\n" \ +"[] set node # Set default node.\n" \ +"[] set sdo_timeout # Configure SDO client time-out in ms.\n" \ +"[] set sdo_block <0|1> # Enable/disable SDO block transfer.\n" \ +"\n" \ +"help [datatype|lss] # Print this or datatype or lss help.\n" \ +"led # Print status LEDs of this device.\n" \ +"log # Print message log.\n" \ +"\n" \ +"Response:\n" \ +"\"[\"\"]\" OK | |\n" \ +" ERROR: | ERROR:\n" \ +"\n" \ +"* Every command must be terminated with ('\\r\\n'). characters. Same\n" \ +" is response. String is not null terminated, is optional in command.\n" \ +"* Comments started with '#' are ignored. They may be on the beginning of the\n" \ +" line or after the command string.\n" \ +"* 'sdo_timeout' is in milliseconds, 500 by default. Block transfer is\n" \ +" disabled by default.\n" \ +"* If '' or '' is not specified within commands, then value defined\n" \ " by 'set network' or 'set node' command is used.\r\n"; static const char CO_GTWA_helpStringDatatypes[] = -"\r\nDatatypes:\r\n" \ -"b # Boolean.\r\n" \ -"i8, i16, i32, i64 # Signed integers.\r\n" \ -"u8, u16, u32, u64 # Unsigned integers.\r\n" \ -"x8, x16, x32, x64 # Unsigned integers, displayed as hexadecimal, non-standard.\r\n" \ -"r32, r64 # Real numbers.\r\n" \ -"t, td # Time of day, time difference.\r\n" \ -"vs # Visible string (between double quotes if multi-word).\r\n" \ -"os, us # Octet, unicode string, (mime-base64 (RFC2045) based, line).\r\n" \ -"d # domain (mime-base64 (RFC2045) based, one line).\r\n" \ +"\nDatatypes:\n" \ +"b # Boolean.\n" \ +"i8, i16, i32, i64 # Signed integers.\n" \ +"u8, u16, u32, u64 # Unsigned integers.\n" \ +"x8, x16, x32, x64 # Unsigned integers, displayed as hexadecimal, non-standard.\n" \ +"r32, r64 # Real numbers.\n" \ +"vs # Visible string (between double quotes if multi-word).\n" \ +"os, us # Octet, unicode string, (mime-base64 (RFC2045) based, line).\n" \ +"d # domain (mime-base64 (RFC2045) based, one line).\n" \ "hex # Hexagonal data, optionally space separated, non-standard.\r\n"; static const char CO_GTWA_helpStringLss[] = -"\r\nLSS commands:\r\n" \ -"lss_switch_glob <0|1> # Switch state global command.\r\n" \ -"lss_switch_sel \\\r\n" \ -" #Switch state selective.\r\n" \ -"lss_set_node # Configure node-ID.\r\n" \ -"lss_conf_bitrate \\\r\n" \ -" # Configure bit-rate.\r\n" \ -"lss_activate_bitrate # Activate new bit-rate.\r\n" \ -"lss_store # LSS store configuration.\r\n" \ -"lss_inquire_addr [] # Inquire LSS address.\r\n" \ -"lss_get_node # Inquire node-ID.\r\n" \ -"_lss_fastscan [] # Identify fastscan, non-standard.\r\n" \ -"lss_allnodes [ [ \\\r\n" \ -" [ \\\r\n" \ -" ]]]\r\n" \ -" # Node-ID configuration of all nodes.\r\n" \ -"\r\n" \ -"* All LSS commands start with '\"[\"\"]\" []'.\r\n" \ -"* : 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s,\r\n" \ -" 4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto\r\n" \ +"\nLSS commands:\n" \ +"lss_switch_glob <0|1> # Switch state global command.\n" \ +"lss_switch_sel \\\n" \ +" #Switch state selective.\n" \ +"lss_set_node # Configure node-ID.\n" \ +"lss_conf_bitrate \\\n" \ +" # Configure bit-rate.\n" \ +"lss_activate_bitrate # Activate new bit-rate.\n" \ +"lss_store # LSS store configuration.\n" \ +"lss_inquire_addr [] # Inquire LSS address.\n" \ +"lss_get_node # Inquire node-ID.\n" \ +"_lss_fastscan [] # Identify fastscan, non-standard.\n" \ +"lss_allnodes [ [ \\\n" \ +" [ \\\n" \ +" ]]]\n" \ +" # Node-ID configuration of all nodes.\n" \ +"\n" \ +"* All LSS commands start with '\"[\"\"]\" []'.\n" \ +"* : 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s,\n" \ +" 4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto\n" \ "* : 0=fastscan, 1=ignore, 2=match value in next parameter\r\n"; #endif @@ -340,12 +340,10 @@ static const CO_GTWA_dataType_t dataTypes[] = { {"x64", 8, CO_fifo_readX642a, CO_fifo_cpyTok2U64}, /* UNSIGNED64 */ {"r32", 4, CO_fifo_readR322a, CO_fifo_cpyTok2R32}, /* REAL32 */ {"r64", 8, CO_fifo_readR642a, CO_fifo_cpyTok2R64}, /* REAL64 */ -// {"t", 0, CO_GWA_dtpHex, CO_DWA_dtsHex}, /* TIME_OF_DAY */ -// {"td", 0, CO_GWA_dtpHex, CO_DWA_dtsHex}, /* TIME_DIFFERENCE */ - {"vs", 0, CO_fifo_readVs2a, CO_fifo_cpyTok2Vs}//, -// {"os", 0, CO_GWA_dtpHex, CO_DWA_dtsHex}, /* ochar_t (OCTET_STRING) (mime-base64 (RFC2045) should be used here) */ -// {"us", 0, CO_GWA_dtpHex, CO_DWA_dtsHex}, /* (UNICODE_STRING) (mime-base64 (RFC2045) should be used here) */ -// {"d", 0, CO_GWA_dtpHex, CO_DWA_dtsHex} /* domain_t (DOMAIN) (mime-base64 (RFC2045) should be used here) */ + {"vs", 0, CO_fifo_readVs2a, CO_fifo_cpyTok2Vs}, /* VISIBLE_STRING */ + {"os", 0, CO_fifo_readB642a, CO_fifo_cpyTok2B64}, /* OCTET_STRING base64*/ + {"us", 0, CO_fifo_readB642a, CO_fifo_cpyTok2B64},/* UNICODE_STRING base64*/ + {"d", 0, CO_fifo_readB642a, CO_fifo_cpyTok2B64} /* DOMAIN - base64 */ }; @@ -369,8 +367,11 @@ static const CO_GTWA_dataType_t *CO_GTWA_getDataType(char *token, bool_t *err) { #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ -/* transfer response buffer and verify if all bytes was read */ -static void respBufTransfer(CO_GTWA_t *gtwa) { +/* transfer response buffer and verify if all bytes was read. Return true on + * success, or false, if communication is broken. */ +static bool_t respBufTransfer(CO_GTWA_t *gtwa) { + uint8_t connectionOK = 1; + if (gtwa->readCallback == NULL) { /* no callback registered, just purge the response */ gtwa->respBufOffset = 0; @@ -382,7 +383,8 @@ static void respBufTransfer(CO_GTWA_t *gtwa) { size_t countRead = gtwa->readCallback(gtwa->readCallbackObject, (const char *)>wa->respBuf[gtwa->respBufOffset], - gtwa->respBufCount); + gtwa->respBufCount, + &connectionOK); if (countRead < gtwa->respBufCount) { gtwa->respBufOffset += countRead; @@ -395,6 +397,7 @@ static void respBufTransfer(CO_GTWA_t *gtwa) { gtwa->respHold = false; } } + return connectionOK != 0; } @@ -495,10 +498,11 @@ static void responseWithError(CO_GTWA_t *gtwa, #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO static void responseWithErrorSDO(CO_GTWA_t *gtwa, - CO_SDO_abortCode_t abortCode) + CO_SDO_abortCode_t abortCode, + bool_t postponed) { int i; - int len = sizeof(errorDescs) / sizeof(errorDescs_t); + int len = sizeof(errorDescsSDO) / sizeof(errorDescs_t); const char *desc = "-"; for (i = 0; i < len; i++) { @@ -508,9 +512,17 @@ static void responseWithErrorSDO(CO_GTWA_t *gtwa, } } - gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, - "[%"PRId32"] ERROR:0x%08X #%s\r\n", - gtwa->sequence, abortCode, desc); + if (!postponed) { + gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%"PRId32"] ERROR:0x%08X #%s\r\n", + gtwa->sequence, abortCode, desc); + } + else { + gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "\n...ERROR:0x%08X #%s\r\n", + abortCode, desc); + } + respBufTransfer(gtwa); } #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ @@ -527,11 +539,20 @@ static inline void responseWithError(CO_GTWA_t *gtwa, #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO static inline void responseWithErrorSDO(CO_GTWA_t *gtwa, - CO_SDO_abortCode_t abortCode) + CO_SDO_abortCode_t abortCode, + bool_t postponed) { - gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, - "[%"PRId32"] ERROR:0x%08X\r\n", - gtwa->sequence, abortCode); + if (!postponed) { + gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "[%"PRId32"] ERROR:0x%08X\r\n", + gtwa->sequence, abortCode); + } + else { + gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "\n...ERROR:0x%08X\r\n", + abortCode); + } + respBufTransfer(gtwa); } #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ @@ -546,6 +567,13 @@ static inline void responseWithOK(CO_GTWA_t *gtwa) { } +static inline void responseWithEmpty(CO_GTWA_t *gtwa) { + gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, + "\r\n"); + respBufTransfer(gtwa); +} + + #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS static void responseLSS(CO_GTWA_t *gtwa, CO_LSSmaster_return_t lss_ret) { if (lss_ret == CO_LSSmaster_OK) { @@ -649,6 +677,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } /* If empty line or just comment, continue with next command */ else if (n == 0 && closed != 0) { + responseWithEmpty(gtwa); continue; } if (tok[0] != '[' || tok[strlen(tok)-1] != ']') { @@ -864,7 +893,10 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } /* setup client */ - SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, 0, 0, gtwa->node); + SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, + CO_CAN_ID_SDO_CLI + gtwa->node, + CO_CAN_ID_SDO_SRV + gtwa->node, + gtwa->node); if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { respErrorCode = CO_GTWA_respErrorInternalState; err = true; @@ -924,7 +956,10 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, if (err) break; /* setup client */ - SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, 0, 0, gtwa->node); + SDO_ret = CO_SDOclient_setup(gtwa->SDO_C, + CO_CAN_ID_SDO_CLI + gtwa->node, + CO_CAN_ID_SDO_SRV + gtwa->node, + gtwa->node); if (SDO_ret != CO_SDO_RT_ok_communicationEnd) { respErrorCode = CO_GTWA_respErrorInternalState; err = true; @@ -1505,26 +1540,28 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, gtwa->state = CO_GTWA_ST_IDLE; } - else if (gtwa->state == CO_GTWA_ST_IDLE) { - return; + else switch (gtwa->state) { + case CO_GTWA_ST_IDLE: { + return; /* skip timerNext_us calculation */ } #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO /* SDO upload state */ - else if (gtwa->state == CO_GTWA_ST_READ) { + case CO_GTWA_ST_READ: { CO_SDO_abortCode_t abortCode; size_t sizeTransferred; CO_SDO_return_t ret; ret = CO_SDOclientUpload(gtwa->SDO_C, timeDifference_us, + false, &abortCode, NULL, &sizeTransferred, timerNext_us); if (ret < 0) { - responseWithErrorSDO(gtwa, abortCode); + responseWithErrorSDO(gtwa, abortCode, gtwa->SDOdataCopyStatus); gtwa->state = CO_GTWA_ST_IDLE; } /* Response data must be read, partially or whole */ @@ -1562,21 +1599,34 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } /* transfer response to the application */ - respBufTransfer(gtwa); + if (respBufTransfer(gtwa) == false) { + /* broken communication, send SDO abort and force finish. */ + abortCode = CO_SDO_AB_DATA_TRANSF; + CO_SDOclientUpload(gtwa->SDO_C, + 0, + true, + &abortCode, + NULL, + NULL, + NULL); + gtwa->state = CO_GTWA_ST_IDLE; + break; + } } while (gtwa->respHold == false && fifoRemain > 0); } + break; } /* SDO download state */ - else if (gtwa->state == CO_GTWA_ST_WRITE) { + case CO_GTWA_ST_WRITE: + case CO_GTWA_ST_WRITE_ABORTED: { CO_SDO_abortCode_t abortCode; size_t sizeTransferred; bool_t abort = false; bool_t hold = false; CO_SDO_return_t ret; - int loop = 0; - /* copy data to the SDO buffer if more data available */ + /* copy data to the SDO buffer if previous dataTypeScan was partial */ if (gtwa->SDOdataCopyStatus) { CO_fifo_st status; gtwa->SDOdataType->dataTypeScan(>wa->SDO_C->bufFifo, @@ -1593,6 +1643,17 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, ) { abortCode = CO_SDO_AB_DEVICE_INCOMPAT; abort = true; /* abort SDO communication */ + /* clear the rest of the command, if necessary */ + if (closed != 1) + CO_fifo_CommSearch(>wa->commFifo, true); + } + if (gtwa->state == CO_GTWA_ST_WRITE_ABORTED) { + /* Stay in this state, until all data transferred via commFifo + * will be purged. */ + if (!CO_fifo_purge(>wa->SDO_C->bufFifo) || closed == 1) { + gtwa->state = CO_GTWA_ST_IDLE; + } + break; } } /* If not all data were transferred, make sure, there is enough data in @@ -1611,12 +1672,14 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, hold = true; } } - if (!hold) { + if (!hold || abort) { /* if OS has CANtx queue, speedup block transfer */ + int loop = 0; do { ret = CO_SDOclientDownload(gtwa->SDO_C, timeDifference_us, abort, + gtwa->SDOdataCopyStatus, &abortCode, &sizeTransferred, timerNext_us); @@ -1627,19 +1690,23 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, /* send response in case of error or finish */ if (ret < 0) { - responseWithErrorSDO(gtwa, abortCode); - gtwa->state = CO_GTWA_ST_IDLE; + responseWithErrorSDO(gtwa, abortCode, false); + /* purge remaining data if necessary */ + gtwa->state = gtwa->SDOdataCopyStatus + ? CO_GTWA_ST_WRITE_ABORTED + : CO_GTWA_ST_IDLE; } else if (ret == CO_SDO_RT_ok_communicationEnd) { responseWithOK(gtwa); gtwa->state = CO_GTWA_ST_IDLE; } } + break; } #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS - else if (gtwa->state == CO_GTWA_ST_LSS_SWITCH_GLOB) { + case CO_GTWA_ST_LSS_SWITCH_GLOB: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_switchStateSelect(gtwa->LSSmaster, timeDifference_us, @@ -1648,8 +1715,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, responseLSS(gtwa, ret); gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_SWITCH_SEL) { + case CO_GTWA_ST_LSS_SWITCH_SEL: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_switchStateSelect(gtwa->LSSmaster, timeDifference_us, @@ -1658,8 +1726,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, responseLSS(gtwa, ret); gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_SET_NODE) { + case CO_GTWA_ST_LSS_SET_NODE: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_configureNodeId(gtwa->LSSmaster, timeDifference_us, @@ -1674,8 +1743,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_CONF_BITRATE) { + case CO_GTWA_ST_LSS_CONF_BITRATE: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_configureBitTiming(gtwa->LSSmaster, timeDifference_us, @@ -1690,8 +1760,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_STORE) { + case CO_GTWA_ST_LSS_STORE: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_configureStore(gtwa->LSSmaster, timeDifference_us); @@ -1705,8 +1776,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_INQUIRE) { + case CO_GTWA_ST_LSS_INQUIRE: { CO_LSSmaster_return_t ret; uint32_t value; @@ -1732,8 +1804,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL) { + case CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_InquireLssAddress(gtwa->LSSmaster, timeDifference_us, @@ -1756,8 +1829,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, } gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST__LSS_FASTSCAN) { + case CO_GTWA_ST__LSS_FASTSCAN: { CO_LSSmaster_return_t ret; ret = CO_LSSmaster_IdentifyFastscan(gtwa->LSSmaster, timeDifference_us, @@ -1782,8 +1856,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, CO_LSSmaster_DEFAULT_TIMEOUT); gtwa->state = CO_GTWA_ST_IDLE; } + break; } - else if (gtwa->state == CO_GTWA_ST_LSS_ALLNODES) { + case CO_GTWA_ST_LSS_ALLNODES: { CO_LSSmaster_return_t ret; if (gtwa->lssSubState == 0) { /* _lss_fastscan */ ret = CO_LSSmaster_IdentifyFastscan(gtwa->LSSmaster, @@ -1797,7 +1872,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, /* no (more) nodes found, send report sum and finish */ gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, - "# Found %d nodes, search finished.\r\n" \ + "# Found %d nodes, search finished.\n" \ "[%"PRId32"] OK\r\n", gtwa->lssNodeCount, gtwa->sequence); @@ -1870,7 +1945,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, else { /* cycle finished successfully, send report */ uint8_t lssNidAssigned = gtwa->lssNID; - const char msg2Fmt[] = "# Not all nodes scanned!\r\n" \ + const char msg2Fmt[] = "# Not all nodes scanned!\n" \ "[%"PRId32"] OK\r\n"; char msg2[sizeof(msg2Fmt)+10] = {0}; @@ -1893,7 +1968,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, gtwa->respBufCount = snprintf(gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, "# Node-ID %d assigned to: 0x%08"PRIX32" 0x%08" \ - PRIX32" 0x%08"PRIX32" 0x%08"PRIX32"\r\n%s", + PRIX32" 0x%08"PRIX32" 0x%08"PRIX32"\n%s", lssNidAssigned, gtwa->lssFastscan.found.identity.vendorID, gtwa->lssFastscan.found.identity.productCode, @@ -1903,12 +1978,13 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, respBufTransfer(gtwa); } } - } /* else if (gtwa->state == CO_GTWA_ST_LSS_ALLNODES) */ + break; + } /* CO_GTWA_ST_LSS_ALLNODES */ #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS */ #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG /* print message log */ - else if (gtwa->state == CO_GTWA_ST_LOG) { + case CO_GTWA_ST_LOG: { do { gtwa->respBufCount = CO_fifo_read(>wa->logFifo, gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, NULL); @@ -1919,12 +1995,13 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, break; } } while (gtwa->respHold == false); + break; } #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG */ #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_HELP /* Print help string (in multiple segments if necessary) */ - else if (gtwa->state == CO_GTWA_ST_HELP) { + case CO_GTWA_ST_HELP: { size_t lenBuf = CO_GTWA_RESP_BUF_SIZE; size_t lenHelp = strlen(gtwa->helpString); @@ -1945,12 +2022,13 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, break; } } while (gtwa->respHold == false); + break; } #endif #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS /* print CANopen status LED diodes */ - else if (gtwa->state == CO_GTWA_ST_LED) { + case CO_GTWA_ST_LED: { uint8_t i; if (CO_fifo_CommSearch(>wa->commFifo, false)) { @@ -1970,15 +2048,18 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, respBufTransfer(gtwa); gtwa->ledStringPreviousIndex = i; } + break; } #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS */ /* illegal state */ - else { + default: { respErrorCode = CO_GTWA_respErrorInternalState; responseWithError(gtwa, respErrorCode); gtwa->state = CO_GTWA_ST_IDLE; + break; } + } /* switch (gtwa->state) */ /* execute next CANopen processing immediately, if idle and more commands * available */ diff --git a/309/CO_gateway_ascii.h b/309/CO_gateway_ascii.h index a03878198..19e63d2ad 100644 --- a/309/CO_gateway_ascii.h +++ b/309/CO_gateway_ascii.h @@ -29,7 +29,6 @@ #include "301/CO_driver.h" #include "301/CO_fifo.h" -#include "301/CO_SDOserver.h" #include "301/CO_SDOclient.h" #include "301/CO_NMT_Heartbeat.h" #include "305/CO_LSSmaster.h" @@ -155,7 +154,7 @@ lss_allnodes [ [ \\ /** Timeout time in microseconds for some internal states. */ #ifndef CO_GTWA_STATE_TIMEOUT_TIME_US -#define CO_GTWA_STATE_TIMEOUT_TIME_US 1000000 +#define CO_GTWA_STATE_TIMEOUT_TIME_US 1200000 #endif @@ -234,6 +233,8 @@ typedef enum { CO_GTWA_ST_READ = 0x10U, /** SDO 'write' (download) */ CO_GTWA_ST_WRITE = 0x11U, + /** SDO 'write' (download) - aborted, purging remaining data */ + CO_GTWA_ST_WRITE_ABORTED = 0x12U, /** LSS 'lss_switch_glob' */ CO_GTWA_ST_LSS_SWITCH_GLOB = 0x20U, /** LSS 'lss_switch_sel' */ @@ -299,10 +300,14 @@ typedef struct { * @param object Void pointer to custom object * @param buf Buffer from which data can be read * @param count Count of bytes available inside buffer + * @param [out] connectionOK different than 0 indicates connection is OK. * * @return Count of bytes actually transferred. */ - size_t (*readCallback)(void *object, const char *buf, size_t count); + size_t (*readCallback)(void *object, + const char *buf, + size_t count, + uint8_t *connectionOK); /** Pointer to object, which will be used inside readCallback, from * CO_GTWA_init() */ void *readCallbackObject; @@ -344,7 +349,9 @@ typedef struct { uint16_t SDOtimeoutTime; /** SDO block transfer enabled? */ bool_t SDOblockTransferEnable; - /** Indicate status of data copy from / to SDO buffer */ + /** Indicate status of data copy from / to SDO buffer. If reading, true + * indicates, that response has started. If writing, true indicates, that + * SDO buffer contains only part of data and more data will follow. */ bool_t SDOdataCopyStatus; /** Data type of variable in current SDO communication */ const CO_GTWA_dataType_t *SDOdataType; @@ -399,8 +406,8 @@ typedef struct { * * @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 SDOclientTimeoutTime_ms Default timeout in milliseconds, 500 typically + * @param SDOclientBlockTransfer If true, block transfer will be set by default * @param NMT NMT object * @param LSSmaster LSS master object * @param LEDs LEDs object @@ -411,8 +418,8 @@ typedef struct { CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO) || defined CO_DOXYGEN CO_SDOclient_t* SDO_C, - uint16_t SDOtimeoutTimeDefault, - bool_t SDOblockTransferEnableDefault, + uint16_t SDOclientTimeoutTime_ms, + bool_t SDOclientBlockTransfer, #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT) || defined CO_DOXYGEN CO_NMT_t *NMT, @@ -443,7 +450,8 @@ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, void CO_GTWA_initRead(CO_GTWA_t* gtwa, size_t (*readCallback)(void *object, const char *buf, - size_t count), + size_t count, + uint8_t *connectionOK), void *readCallbackObject); diff --git a/README.md b/README.md index d997cf971..29aa73d3c 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,9 @@ documentation [online](https://canopennode.github.io/CANopenSocket/). Report issues on https://github.com/CANopenNode/CANopenNode/issues -Older and still active discussion group is on Sourceforge -http://sourceforge.net/p/canopennode/discussion/387151/ +Join discussion on Slack: https://canopennode.slack.com/ + +Older discussion group is on Sourceforge: http://sourceforge.net/p/canopennode/discussion/387151/ Contributions are welcome. Best way to contribute your code is to fork a project, modify it and then send a pull request. Some basic formatting @@ -187,7 +188,6 @@ File structure - **deviceSupport.md** - Information about supported devices. - **gettingStarted.md, LSSusage.md, traceUsage.md** - Getting started and usage. - **objectDictionary.md** - Description of CANopen object dictionary interface. - - **index.html** - Soft link to html/md_README.html. - **html** - Directory with documentation - must be generated by Doxygen. - **CANopen.h/.c** - Initialization and processing of CANopen objects. - **codingStyle** - Example of the coding style. @@ -201,19 +201,42 @@ File structure Object dictionary editor ------------------------ -Object Dictionary is one of the most important parts of CANopen. Its -implementation in CANopenNode is quite outdated and there are efforts to -rewrite it. Anyway, currently it is fully operational and works well. +Object Dictionary is one of the most essential parts of CANopen. -To customize the Object Dictionary it is necessary to use external application: -[libedssharp](https://github.com/robincornelius/libedssharp). It can be used in -Windows or Linux with Mono. +Its implementation in this version of CANopenNode (up to v2) is outdated and +new version (v4 and up) contains new approach. Anyway, current version is fully +operational and works well. -Please note: since rearrangement in directory structure it is necessary to +To customize the Object Dictionary it is necessary to use external application: +[libedssharp](https://github.com/robincornelius/libedssharp). Latest pre-compiled +[binaries](https://github.com/robincornelius/libedssharp/raw/gh-pages/build/OpenEDSEditor-latest.zip) +are also available. Just extract the zip file and run the `EDSEditor.exe`. +In Linux it runs with mono, which is available by default on Ubuntu. Just set +file permissions to "executable" and then execute the program. + +In program, in preferences, set exporter to "CANopenNode_Legacy". Then open +`example/_project.xml` or your project file for editing. + +Many project file types are supported, EDS, XDD v1.0, XDD v1.1, old custom XML +format. Generated project file can then be saved in XDD v1.1 file format +(xmlns="http://www.canopen.org/xml/1.1"). Project file can also be exported to +other formats, it can be used to generate documentation and CANopenNode source +files for Object Dictionary. + +Main project file format for this (old) version of CANopenNode is customized XML. +It is advised to export project to this format also, before exporting to the old +CANopenNode source files (<= v2, legacy). + +To clone, add or delete, select object(s) and use right click. Some knowledge +of CANopen is required to correctly set-up the custom Object Dictionary. Separate +objects can also be inserted from another project. +`example/_project.xml` can be used as a reference for the (<= v2) projects. + +Please note: since rearrangement in directory structure it may be necessary to manually update CO_OD.c file - it must include: `301/CO_driver.h`, `CO_OD.h` and `301/CO_SDOserver.h`. Latest libedssharp includes `CO_VERSION_MAJOR` macro, to include the correct files. For this to work, C macro `CO_VERSION_MAJOR=2` has to -be added to project options (or `-DCO_VERSION_MAJOR=2` to makefile CFLAGS). +be added to project options (or `-DCO_VERSION_MAJOR=2` to Makefile CFLAGS). Device support @@ -244,7 +267,7 @@ DSP302-2 standard. Standard allows two values for slaves for object 0x1F80: start remote node with node-ID set to 0" ### Error control -When node is stated (in NMT operational state), it is allowed to send or +When node is started (in NMT operational state), it is allowed to send or receive Process Data Objects (PDO). If Error Register (object 0x1001) is set, then NMT operational state is not allowed. diff --git a/doc/gettingStarted.md b/doc/gettingStarted.md index 4739e6031..984cec7f2 100644 --- a/doc/gettingStarted.md +++ b/doc/gettingStarted.md @@ -30,6 +30,7 @@ Then clone [CANopenNode](https://github.com/CANopenNode/CANopenNode) from Github sudo apt-get install can-utils git clone https://github.com/CANopenNode/CANopenNode.git cd CANopenNode + # For update just use 'git pull' here make Now prepare CAN virtual device and run _candump_, which will show all CAN traffic. Use a second terminal: @@ -51,14 +52,14 @@ You are now running a fully functional CANopen device on virtual CAN network. It On the second terminal you can see some CAN traffic. After _canopend_ startup, first messages are: - vcan0 704 [1] 00 # Bootup message. + vcan0 704 [1] 00 # Boot-up message. vcan0 084 [8] 00 50 01 2F F3 FF FF FF # Emergency message. vcan0 704 [1] 7F # Heartbeat messages each second vcan0 704 [1] 7F -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. +Boot-up 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. -Also, both, first and second terminal shows, that there is an Emergency message after the bootup. Also Heartbeat messages shows NMT pre-operational state. +Also, both, first and second terminal shows, that there is an Emergency message after the boot-up. Also Heartbeat messages shows NMT pre-operational state. 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. @@ -158,13 +159,19 @@ Please be careful when exposing your CANopen network to the outside world, it is ### Next steps -Now you can enter the big world of [CANopen devices](http://can-newsletter.org/hardware). +Assigning Node-ID or CAN bitrate, which support LSS configuration, is described in *LSSusage.md*. -You can also build your own CANopen device with your favourite microcontroller, see *deviceSupport.md*. There is also a bare-metal demo for [PIC microcontrollers](https://github.com/CANopenNode/CANopenPIC), most complete example is for PIC32. +This version of CANopenNode (<= v2) contains old and outdated approach to Object Dictionary. You may try to switch to newer version, for which are available some further examples and tools. -Assigning Node-ID or CAN bitrate, which support LSS configuration, is described in *LSSusage.md*. +Further CANopenNode related tools and examples are available in [CANopenSocket](https://github.com/CANopenNode/CANopenSocket). Especially interesting is [basicDevice](https://github.com/CANopenNode/CANopenSocket/examples/basicDevice) + +Custom CANopen device can be created based on own Object Dictionary, see README.md. There are also 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, for example CiA401. -Some further CANopenNode related Linux tools are available in [CANopenSocket](https://github.com/CANopenNode/CANopenSocket). +For own CANopen device with own microcontroller, see *deviceSupport.md*. There is a bare-metal demo for [PIC microcontrollers](https://github.com/CANopenNode/CANopenPIC), most complete example is for PIC32. + +Another interesting tool is [CANopen for Python](https://github.com/christiansandberg/canopen). + +Examples here worked in virtual CAN interface, for simplicity. Virtual CAN runs inside Linux kernel only, it does not have much practical usability. If one has real CAN network configuration, then above examples are suitable also for this network, if Linux machine is connected to it and CAN interface is properly configured. When connecting your devices to real CAN network, make sure, you have at least two devices communicating, connected with ground and pair of wires, terminated with two 120ohm resistors, correct baudrate, etc. Accessing real CANopen devices is the same as described above for virtual CAN interface. Some tested USB to CAN interfaces, which are native in 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` @@ -172,7 +179,8 @@ Accessing real CANopen devices is the same as described above for virtual CAN in - 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). - Raspberry PI or similar has CAN capes available. -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. +Examples here run in Linux, for simplicity. However, real usability of CANopen network is, when simple, microcontroller based devices are connected together with or without more advanced commander device. CANopenNode is basically written for simple microcontrollers and also has more advanced commander features, like above used CANopen gateway with ascii command interface. + +Now you can enter the big world of [CANopen devices](http://can-newsletter.org/hardware). -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! +Here we played with virtual CAN interface and result shown as pixels on screen. If you connect a real CAN interface to your computer, things may become dangerous. Keep control and safety on your machines! diff --git a/doc/objectDictionary.md b/doc/objectDictionary.md index 09e10322c..61765d5d7 100644 --- a/doc/objectDictionary.md +++ b/doc/objectDictionary.md @@ -3,8 +3,8 @@ Object Dictionary **THIS IS AVAILABLE IN THE NEXT VERSION** -Definitions from CiA 301 ------------------------- +Definitions from CiA 301 {#definitions-from-cia-301} +---------------------------------------------------- The **Object Dictionary** is a collection of all the data items which have an influence on the behavior of the application objects, the communication objects and the state machine used on this device. It serves as an interface between the communication and the application. The object dictionary is essentially a grouping of objects accessible via the network in an ordered pre-defined fashion. Each object within the object dictionary is addressed using a 16-bit index and a 8-bit sub-index. @@ -13,13 +13,12 @@ A **SDO** (Service Data Object) is providing direct access to object entries of A **PDO** (Process Data Object) is providing real-time data transfer of object entries of a CANopen device's object dictionary. The transfer of PDO is performed with no protocol overhead. The PDO correspond to objects in the object dictionary and provide the interface to the application objects. Data type and mapping of application objects into a PDO is determined by a corresponding PDO mapping structure within the object dictionary. -Operation ---------- +Operation {#operation} +---------------------- ### Terms The term **OD object** means object from object dictionary located at specific 16-bit index. There are different types of OD objects in CANopen: variables, arrays and records (structures). Each OD object contains pointer to actual data, data length(s) and attribute(s). See @ref OD_objectTypes_t. -The term **OD variable** is basic variable of specified type. For example: int8_t, uint32_t, float64_t, ... or just sequence of binary data with known -or unknown data length. Each OD variable resides in Object dictionary at specified 16-bit index and 8-bit sub-index. +The term **OD variable** is basic variable of specified type. For example: int8_t, uint32_t, float64_t, ... or just sequence of binary data with known or unknown data length. Each OD variable resides in Object dictionary at specified 16-bit index and 8-bit sub-index. The term **OD entry** means structure element, which contains some basic properties of the OD object, indication of type of OD object and pointer to all necessary data for the OD object. An array of OD entries together with information about total number of OD entries represents object dictionary as defined inside CANopenNode. See @ref OD_entry_t and @ref OD_t. @@ -46,8 +45,7 @@ void myFunc(const OD_t *od) { /* Init IO for "Manufacturer device name" at index 0x1008, sub-index 0x00 */ entry = OD_find(od, 0x1008); - ret = OD_getSub(entry, 0x00, &subEntry, &io1008.stream, false); - io1008.read = subEntry.read; + ret = OD_getSub(entry, 0x00, &subEntry, &io1008, false); /* Read with io1008, subindex = 0x00 */ if (ret == ODR_OK) bytesRd = io1008.read(&io1008.stream, 0x00, &buf[0], sizeof(buf), &ret); @@ -84,8 +82,8 @@ void myFuncGlob(void) { ``` -Object dictionary example -------------------------- +Object Dictionary Example {#object-dictionary-example} +------------------------------------------------------ Actual Object dictionary for one CANopen device is defined by pair of OD_xyz.h and ODxyz.h files. Suffix "xyz" is unique name of the object dictionary. If single default object dictionary is used, suffix is omitted. Such way configuration with multiple object dictionaries is possible. @@ -249,15 +247,15 @@ const OD_t ODxyz = { ``` -XML device description ----------------------- +XML Device Description {#xml-device-description} +------------------------------------------------ CANopen device description - XML schema definition - is specified by CiA 311 standard. CiA 311 complies with standard ISO 15745-1:2005/Amd1 (Industrial automation systems and integration - Open systems application integration framework). CANopen device description is basically a XML file with all the information about CANopen device. The larges part of the file is a list of all object dictionary variables with all necessary properties and documentation. This file can be edited with OD editor application and can be used as data source, from which Object dictionary for CANopenNode is generated. Furthermore, this file can be used with CANopen configuration tool, which interacts with CANopen devices on running CANopen network. -XML schema definitions are available at: http://www.canopen.org/xml/1.1 One of the tools for viewing XML schemas is "xsddiagram" (https://github.com/dgis/xsddiagram). +XML schema definitions are available at: http://www.canopen.org/xml/1.1 One of the tools for viewing XML schemas and validating XDD project files is "xsddiagram" (https://github.com/dgis/xsddiagram). Command line alternative to XDD file validation against schema is: `xmlstarlet val --err --xsd 311/CANopen.xsd project_file.xdd` CANopen specifies also another type of files for CANopen device description. These are EDS files, which are in INI format. It is possible to convert between those two formats. But CANopenNode uses XML format. @@ -265,10 +263,12 @@ The device description file has "XDD" file extension. The name of this file shal CANopenNode includes multiple profile definition files, one for each CANopen object. Those files have "XPD" extension. They are in the same XML format as XDD files. The XML editor tool can use XPD files to insert prepared data into device description file (XDD), which is being edited. +There are also device configuration files with "XDC" extension. They are describing a configured CANopen device and include additional elements, such as default value, denominator and device commissioning elements. Similar as "dcf" files in INI format. + ### XDD, XPD file example ```xml - + ... @@ -335,19 +335,6 @@ CANopenNode includes multiple profile definition files, one for each CANopen obj - - - - ... - - - ... - - - - - - @@ -378,16 +365,16 @@ Above XML file example shows necessary data for OD interface used by CANopenNode XML file is divided into two parts: 1. "ProfileBody_Device_CANopen" - more standardized information - 2. "ProfileBody_CommunicationNetwork_CANopen" - communication related info + 2. "ProfileBody_CommunicationNetwork_CANopen" - CANopen specific information -Most important part of the XML file is definition of each OD object. All OD objects are listed in "CANopenObjectList", which resides in the second part of the XML file. Each "CANopenObject" and "CANopenSubObject" of the list contains a link to parameter ("uniqueIDRef"), which resides in the first part of the XML file. So data for each OD object is split between two parts of the XML file. +Largest part of the XML file is definition of each OD object. All OD objects are listed in "CANopenObjectList", which resides in the second part of the XML file. Each "CANopenObject" and "CANopenSubObject" of the list contains a link to parameter ("uniqueIDRef"), which resides in the first part of the XML file. So data for each OD object is split between two parts of the XML file. #### <CANopenObject> * "index" (required) - Object dictionary index * "name" (required) - Name of the parameter * "objectType" (required) - "7"=VAR, "8"=ARRAY, "9"=RECORD * "subNumber" (required if "objectType" is "8" or "9") - * "PDOmapping" (optional if "objectType" is "7", default is "no"): + * "PDOmapping" (optional if "objectType" is "7"): * "no" - mapping not allowed * "default" - not used, same as "optional" * "optional" - mapping allowed to TPDO or RPDO @@ -399,21 +386,20 @@ Most important part of the XML file is definition of each OD object. All OD obje * "subIndex" (required) - Object dictionary sub-index * "name" (required) - Name of the parameter * "objectType" (required, always "7") - * "PDOmapping" (optional, same as above, default is "no") + * "PDOmapping" (optional, same as above) * "uniqueIDRef" (required or see below) - Reference to <parameter> #### uniqueIDRef -This is required attribute from "CANopenObject" and "CANopenSubObject". It contains reference to <parameter> in "ProfileBody_Device_CANopen" section of the XML file. There are additional necessary properties. +This is required attribute from "CANopenObject" and "CANopenSubObject". It contains reference to <parameter> in "ProfileBody_Device_CANopen" section of the XML file. If "uniqueIDRef" attribute is not specified and "objectType" is 7(VAR), then "CANopenObject" or "CANopenSubObject" must contain additional attributes: * "dataType" (required for VAR) - CANopen basic data type, see below * "accessType" (required for VAR) - "ro", "wo", "rw" or "const" * "defaultValue" (optional) - Default value for the variable. - * "denotation" (optional) - Not used by CANopenNode. #### <parameter> - * "uniqueID" (required) - * "access" (required for VAR) - can be one of: + * "**uniqueID**" (required) + * "**access**" (required for VAR) - can be one of: * "const" - same as "read" * "read" - only read access with SDO or PDO * "write" - only write access with SDO or PDO @@ -421,22 +407,23 @@ If "uniqueIDRef" attribute is not specified and "objectType" is 7(VAR), then "CA * "readWriteInput" - same as "readWrite" * "readWriteOutput" - same as "readWrite" * "noAccess" - object will be in object dictionary, but no access. - * <label lang="en"> (required) - * <description lang="en"> (required) - * <INT and similar/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then <dataTypeIDRef> must be specified and entry must be added in the <dataTypeList>. Such definition of complex data types is required by the standard, but it is not required by CANopenNode. - * <defaultValue> (optional for VAR) - Default value for the variable. If it is empty, then data is not stored inside object dictionary. Application should provide own data via IO extension. - -Additional, optional, CANopenNode specific properties, which can be used inside parameters describing <CANopenObject>: - * <property name="CO_storageGroup" value="..."> - group name (string) into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. - * <property name="CO_extensionIO" value="..."> - Valid value is "false" (default) or "true", if IO extension is enabled. - * <property name="CO_flagsPDO" value="..."> - Valid value is "false" (default) or "true", if PDO flags are enabled. - * <property name="CO_countLabel" value="..."> - Valid value is any string without spaces. OD exporter will generate a macro for each different string. For example, if four OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_CNT_TPDO 4" will be generated by OD exporter. - -Additional, optional, CANopenNode specific property, which can be used inside parameters describing "VAR": - * <property name="CO_accessSRDO" value="..."> - Valid values are: "tx", "rx", "trx", "no"(default). - * <property name="CO_stringLength" value="..."> - Minimum length of the string. Used with "VISIBLE_STRING", "OCTET_STRING" and "UNICODE_STRING". If CO_stringLength smaller than length of string in <defaultValue>, then it is ignored. Byte length of unicode string is 2 * CO_stringLength. If <defaultValue> is empty and CO_stringLength is 0, then data is not stored inside object dictionary. - -#### CANopen basic data types + * <**label** lang="en">, <**description** lang="en"> (required) - several elements in multiple languages possible + * <**INT and similar**/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then **<dataTypeIDRef>** must be specified and entry must be added in the **<dataTypeList>**. Such definition of complex data types is required by the standard, but it is not required by CANopenNode. + * <**defaultValue**> (optional for VAR) - Default value for the variable. If it is empty string, then data is not stored inside object dictionary. Application should provide own data via IO extension. + * <**property** name="..." value="..."> (optional) - Multiple elements may contain additional custom properties. CANopenNode uses custom properties with following names: + * "**CO_disabled**" (used for base OD entry) - Valid value is "false" (default) or "true". if OD entry is disabled, then it will not be present in generated Object Dictionary .h and .c files. + * "**CO_countLabel**" (used for base OD entry) - Valid value is any string without spaces. OD exporter will generate a macro for each different string. For example, if four OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_CNT_TPDO 4" will be generated by OD exporter. "CO_countLabel" is not required for configuration with multiple object dictionaries (CO_MULTIPLE_OD), although may be useful. Default is "". + * "**CO_storageGroup**" (used for base OD entry) - group name (string) into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. Default is "RAM". Please note: if <defaultValue> is empty string at specific subindex, then no storage group will be used for that variable. + * "**CO_extensionIO**" (used for base OD entry) - Valid value is "false" (default) or "true", if IO extension is enabled. + * "**CO_flagsPDO**" (used for base OD entry) - Valid value is "false" (default) or "true", if PDO flags are enabled. + * "**CO_accessSRDO**" (used for each "VAR") - Valid values are: "tx", "rx", "trx", "no"(default). + * "**CO_stringLengthMin**" (used for each "VAR") - Minimum length of the string. Used with "VISIBLE_STRING" and "UNICODE_STRING". If CO_stringLengthMin is smaller than length of the string in <defaultValue>, then it is ignored. Byte length of unicode string is 2 * CO_stringLengthMin. If <defaultValue> is empty and CO_stringLengthMin is 0, then data is not stored inside object dictionary. Default is 0. + +#### Other elements +Other elements listed in the above XML example are required by the standard. There are also many other elements, not listed above. All those does not influence the CANopenNode object dictionary exporter. + + +### CANopen basic data types | CANopenNode | IEC 61131-3 | CANopen | dataType | | -------------- | -------------- | --------------- | -------- | | bool_t | BOOL | BOOLEAN | 0x01 | @@ -450,32 +437,32 @@ Additional, optional, CANopenNode specific property, which can be used inside pa | uint64_t | ULINT, (LWORD) | UNSIGNED64 | 0x1B | | float32_t | REAL | REAL32 | 0x08 | | float64_t | LREAL | REAL64 | 0x11 | -| uint8_t [] (1) | BITSTRING (2) | INTEGER24 | 0x10 | -| uint8_t [] (1) | BITSTRING (2) | INTEGER40 | 0x12 | -| uint8_t [] (1) | BITSTRING (2) | INTEGER48 | 0x13 | -| uint8_t [] (1) | BITSTRING (2) | INTEGER56 | 0x14 | -| uint8_t [] (1) | BITSTRING (2) | UNSIGNED24 | 0x16 | -| uint8_t [] (1) | BITSTRING (2) | UNSIGNED40 | 0x18 | -| uint8_t [] (1) | BITSTRING (2) | UNSIGNED48 | 0x19 | -| uint8_t [] (1) | BITSTRING (2) | UNSIGNED56 | 0x1A | -| char [] | STRING | VISIBLE_STRING | 0x09 | -| uint8_t [] | BITSTRING (2) | OCTET_STRING | 0x0A | -| uint16_t [] | WSTRING | UNICODE_STRING | 0x0B | -| uint8_t [] (1) | BITSTRING (2) | TIME_OF_DAY | 0x0C | -| uint8_t [] (1) | BITSTRING (2) | TIME_DIFFERENCE | 0x0D | -| not used | BITSTRING (2) | DOMAIN | 0x0F | -(1) Data is stored in little-endian format. - -(2) CANopen specific type is stored as <BITSTRING/> in <parameter> and additional CANopen "dataType" attribute is stored in <CANopenObject> or <CANopenSubObject>. - -#### <parameterGroupList> -This is optional element and is not required by standard, nor by CANopenNode. This can be very useful for documentation, which can be organised into multiple chapters with multiple levels. CANopen objects can then be organised in any way, not only by index. +| - | (1) | INTEGER24 | 0x10 | +| - | (1) | INTEGER40 | 0x12 | +| - | (1) | INTEGER48 | 0x13 | +| - | (1) | INTEGER56 | 0x14 | +| - | (1) | UNSIGNED24 | 0x16 | +| - | (1) | UNSIGNED40 | 0x18 | +| - | (1) | UNSIGNED48 | 0x19 | +| - | (1) | UNSIGNED56 | 0x1A | +| char [] | STRING (2) | VISIBLE_STRING | 0x09 | +| uint8_t [] | BITSTRING (3) | OCTET_STRING | 0x0A | +| uint16_t [] | WSTRING (2) | UNICODE_STRING | 0x0B | +| - | (1) | TIME_OF_DAY | 0x0C | +| - | (1) | TIME_DIFFERENCE | 0x0D | +| app. specific | BITSTRING (4) | DOMAIN | 0x0F | -#### Other elements -Other elements listed in the above XML example are required by the standard and does not influence the CANopenNode object dictionary generator. +(1) Data is translated into OCTET_STRING. + +(2) Additional property "CO_stringLengthMin" indicates the minimum length of the string in CANopenNode. Property is ignored, if strlen(defaultValue) is larger. Otherwise remaining data locations are filled with zeroes. Strings always have additional element on the end of the array with value=0, which is never written (string is always null terminated). All strings have additional attribute "ODA_STR" in object dictionary. Attribute enables SDO to transfer data of length, which corresponds to actual string length. (Actual size of data read or written by SDO may be smaller than data size of the OD variable.) VISIBLE_STRING may contain control characters and UTF-8 characters, which should work on most target systems (like printf("%s", ...)). So VISIBLE_STRING may be more usable for printing complete set of unicode characters than UNICODE_STRING. + +(3) Default value for BITSTRING (OCTET_STRING) is written as space separated, two hex digit bytes in little-endian format. + +(4) Default value for DOMAIN is stored as empty string. -### Object dictionary requirements by CANopenNode +Object Dictionary Requirements By CANopenNode {#object-dictionary-requirements-by-canopennode} +---------------------------------------------------------------------------------------------- * **Used by** column indicates CANopenNode object or its part, which uses the OD object. It also indicates, if OD object is required or optional for actual configuration. For the configuration of the CANopenNode objects see [Stack configuration](301/CO_config.h). If CANopenNode object or its part is disabled in stack configuration, then OD object is not used. Note that OD objects: 1000, 1001 and 1017 and 1018 are mandatory for CANopen. * **CO_extensionIO** column indicates, if OD object must have property "CO_extensionIO" set to true: * "no" - no IO extension used diff --git a/example/CO_OD.c b/example/CO_OD.c index 9b22f8406..0a5d68d28 100644 --- a/example/CO_OD.c +++ b/example/CO_OD.c @@ -30,10 +30,18 @@ */ -#include "301/CO_driver.h" -#include "CO_OD.h" -#include "301/CO_SDOserver.h" - +// For CANopenNode V2 users, C macro `CO_VERSION_MAJOR=2` has to be added to project options +#ifndef CO_VERSION_MAJOR + #include "CO_driver.h" + #include "CO_OD.h" + #include "CO_SDO.h" +#elif CO_VERSION_MAJOR < 4 + #include "301/CO_driver.h" + #include "CO_OD.h" + #include "301/CO_SDOserver.h" +#else + #error This Object dictionary is not compatible with CANopenNode v4.0 and up! +#endif /******************************************************************************* DEFINITION AND INITIALIZATION OF OBJECT DICTIONARY VARIABLES diff --git a/example/CO_driver_target.h b/example/CO_driver_target.h index babdf6994..6aac0c532 100644 --- a/example/CO_driver_target.h +++ b/example/CO_driver_target.h @@ -23,8 +23,8 @@ */ -#ifndef CO_DRIVER_TARGET -#define CO_DRIVER_TARGET +#ifndef CO_DRIVER_TARGET_H +#define CO_DRIVER_TARGET_H /* This file contains device and application specific definitions. * It is included from CO_driver.h, which contains documentation @@ -90,10 +90,6 @@ extern "C" { CO_CONFIG_FLAG_OD_DYNAMIC) #endif -#ifndef CO_CONFIG_SDO_CLI_BUFFER_SIZE -#define CO_CONFIG_SDO_CLI_BUFFER_SIZE 1000 -#endif - #ifndef CO_CONFIG_TIME #define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | \ CO_CONFIG_TIME_PRODUCER | \ @@ -231,6 +227,7 @@ typedef struct { volatile bool_t firstCANtxMessage; volatile uint16_t CANtxCount; uint32_t errOld; + int32_t errinfo; } CO_CANmodule_t; @@ -257,4 +254,4 @@ typedef struct { } #endif /* __cplusplus */ -#endif /* CO_DRIVER_TARGET */ +#endif /* CO_DRIVER_TARGET_H */ diff --git a/example/Makefile b/example/Makefile index b4264b7a1..2a22236e7 100644 --- a/example/Makefile +++ b/example/Makefile @@ -44,7 +44,7 @@ SOURCES = \ OBJS = $(SOURCES:%.c=%.o) CC ?= gcc -OPT = -g +OPT = -g -DCO_VERSION_MAJOR=2 CFLAGS = -Wall $(OPT) $(INCLUDE_DIRS) LDFLAGS = diff --git a/example/_project.html b/example/_project.html deleted file mode 100644 index d000a248b..000000000 --- a/example/_project.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - Project - CANopenNode - - - -
-
- --- - --- - - at location: - - -
- - - - - - - - - - - - -
-
- CANopenNode project file: - -
-
-
-
- Documentation HTML file: - - -
- -
-
- Source code CO_OD.h file: - -
- -
-
- Source code CO_OD.c file: - -
- -
-
- EDS specification file: - -
- -
-
- Errors and warnings: - -
- -
-CANopenNode Editor - project file. -
- - \ No newline at end of file diff --git a/example/_project.xml b/example/_project.xml index 26a476374..7e5bab26f 100644 --- a/example/_project.xml +++ b/example/_project.xml @@ -142,6 +142,12 @@ bit 31: set to 0 + + * bit 31: If set, CANopen device consumes TIME message +* bit 30: If set, CANopen device produces TIME message +* bit 11-29: set to 0 +* bit 0-10: 11-bit CAN-ID + bit 0-10: COB-ID diff --git a/socketCAN/CO_driver_target.h b/socketCAN/CO_driver_target.h index 6d22de939..ee2d25db4 100644 --- a/socketCAN/CO_driver_target.h +++ b/socketCAN/CO_driver_target.h @@ -26,8 +26,8 @@ */ -#ifndef CO_DRIVER_TARGET -#define CO_DRIVER_TARGET +#ifndef CO_DRIVER_TARGET_H +#define CO_DRIVER_TARGET_H /* This file contains device and application specific definitions. * It is included from CO_driver.h, which contains documentation @@ -106,10 +106,6 @@ extern "C" { CO_CONFIG_FLAG_OD_DYNAMIC) #endif -#ifndef CO_CONFIG_SDO_CLI_BUFFER_SIZE -#define CO_CONFIG_SDO_CLI_BUFFER_SIZE 1000 -#endif - #ifndef CO_CONFIG_TIME #define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | \ CO_CONFIG_TIME_PRODUCER | \ @@ -176,6 +172,14 @@ extern "C" { #endif +/* Print debug info from some internal parts of the stack */ +#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_COMMON +#include +#include +#define CO_DEBUG_COMMON(msg) log_printf(LOG_DEBUG, DBG_CO_DEBUG, msg); +#endif + + /** * @defgroup CO_socketCAN_driver_target CO_driver_target.h * @ingroup CO_socketCAN @@ -337,6 +341,7 @@ typedef struct { CO_CANtx_t *txArray; uint16_t txSize; uint16_t CANerrorStatus; + int32_t errinfo; volatile bool_t CANnormal; int epoll_fd; /* File descriptor for epoll, which waits for CAN receive event */ @@ -481,4 +486,4 @@ bool_t CO_CANrxFromEpoll(CO_CANmodule_t *CANmodule, } #endif /* __cplusplus */ -#endif /* CO_DRIVER_TARGET */ +#endif /* CO_DRIVER_TARGET_H */ diff --git a/socketCAN/CO_epoll_interface.c b/socketCAN/CO_epoll_interface.c index 3f1a7715e..ac5dca7a6 100644 --- a/socketCAN/CO_epoll_interface.c +++ b/socketCAN/CO_epoll_interface.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifndef LISTEN_BACKLOG #define LISTEN_BACKLOG 50 @@ -319,14 +320,11 @@ void CO_epoll_processRT(CO_epoll_t *ep, bool_t syncWas = false; #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE - /* Process Sync */ syncWas = CO_process_SYNC(co, ep->timeDifference_us, pTimerNext_us); #endif - /* Read inputs */ CO_process_RPDO(co, syncWas); - /* Write outputs */ CO_process_TPDO(co, syncWas, ep->timeDifference_us, pTimerNext_us); @@ -338,7 +336,11 @@ void CO_epoll_processRT(CO_epoll_t *ep, /* GATEWAY ********************************************************************/ #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) { +static size_t gtwa_write_response(void *object, + const char *buf, + size_t count, + uint8_t *connectionOK) +{ int* fd = (int *)object; /* nWritten = count -> in case of error (non-existing fd) data are purged */ size_t nWritten = count; @@ -349,9 +351,14 @@ static size_t gtwa_write_response(void *object, const char *buf, size_t count) { nWritten = (size_t)n; } else { + /* probably EAGAIN - "Resource temporarily unavailable". Retry. */ log_printf(LOG_DEBUG, DBG_ERRNO, "write(gtwa_response)"); + nWritten = 0; } } + else { + *connectionOK = 0; + } return nWritten; } @@ -420,6 +427,14 @@ CO_ReturnError_t CO_epoll_createGtw(CO_epoll_gtw_t *epGtw, return CO_ERROR_SYSCALL; } + /* Ignore the SIGPIPE signal, which may happen, if remote client broke + * the connection. Signal may be triggered by write call inside + * gtwa_write_response() function */ + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + log_printf(LOG_CRIT, DBG_ERRNO, "signal"); + return CO_ERROR_SYSCALL; + } + log_printf(LOG_INFO, DBG_COMMAND_LOCAL_INFO, localSocketPath); } else if (commandInterface >= CO_COMMAND_IF_TCP_SOCKET_MIN && @@ -456,6 +471,14 @@ CO_ReturnError_t CO_epoll_createGtw(CO_epoll_gtw_t *epGtw, return CO_ERROR_SYSCALL; } + /* Ignore the SIGPIPE signal, which may happen, if remote client broke + * the connection. Signal may be triggered by write call inside + * gtwa_write_response() function */ + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + log_printf(LOG_CRIT, DBG_ERRNO, "signal"); + return CO_ERROR_SYSCALL; + } + log_printf(LOG_INFO, DBG_COMMAND_TCP_INFO, commandInterface); } else { @@ -575,8 +598,8 @@ void CO_epoll_processGtw(CO_epoll_gtw_t *epGtw, ssize_t s = read(epGtw->gtwa_fd, buf, space); - if (co->nodeIdUnconfigured) { - /* purge data */ + if (space == 0 || co->nodeIdUnconfigured) { + /* continue or purge data */ } else if (s < 0 && errno != EAGAIN) { log_printf(LOG_DEBUG, DBG_ERRNO, "read(gtwa_fd)"); diff --git a/socketCAN/CO_error_msgs.h b/socketCAN/CO_error_msgs.h index 655e816e5..b038fdeee 100644 --- a/socketCAN/CO_error_msgs.h +++ b/socketCAN/CO_error_msgs.h @@ -59,6 +59,7 @@ extern "C" { */ #define DBG_GENERAL "(%s) Error: %s%d", __func__ #define DBG_ERRNO "(%s) OS error \"%s\" in %s", __func__, strerror(errno) +#define DBG_CO_DEBUG "(%s) CO_DEBUG: %s", __func__ #define DBG_CAN_TX_FAILED "(%s) Transmitting CAN msg OID 0x%08x failed(%s)", __func__ #define DBG_CAN_RX_PARAM_FAILED "(%s) Setting CAN rx buffer failed (%s)", __func__ #define DBG_CAN_RX_FAILED "(%s) Receiving CAN msg failed (%s)", __func__ @@ -77,6 +78,7 @@ extern "C" { #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_OD_ENTRY "(%s) Error in Object Dictionary entry: 0x%X", __func__ #define DBG_CAN_OPEN "(%s) CANopen error in %s, err=%d", __func__ #define DBG_CAN_OPEN_INFO "CANopen device, Node ID = 0x%02X, %s" diff --git a/socketCAN/CO_main_basic.c b/socketCAN/CO_main_basic.c index e660a041e..c3da3bf6c 100644 --- a/socketCAN/CO_main_basic.c +++ b/socketCAN/CO_main_basic.c @@ -1,14 +1,13 @@ /* - * CANopen main program file for Linux SocketCAN. + * CANopen main program file for CANopenNode on Linux. * - * @file main.c + * @file CO_main_basic.c * @author Janez Paternoster - * @copyright 2015 - 2020 Janez Paternoster + * @copyright 2020 Janez Paternoster * - * This file is part of CANopenSocket, a Linux implementation of CANopen - * stack with master functionality. Project home page is - * . CANopenSocket is based - * on CANopenNode: . + * This file is part of CANopenNode, an opensource CANopen Stack. + * Project home page is . + * For more information on CANopen see . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -50,9 +50,8 @@ #endif /* Call external application functions. */ -#if __has_include("CO_application.h") +#ifdef CO_USE_APPLICATION #include "CO_application.h" -#define CO_USE_APPLICATION #endif /* Add trace functionality for recording variables over time */ @@ -135,6 +134,7 @@ void log_printf(int priority, const char *format, ...) { #endif } +#if (CO_CONFIG_EM) & CO_CONFIG_EM_CONSUMER /* callback for emergency messages */ static void EmergencyRxCallback(const uint16_t ident, const uint16_t errorCode, @@ -147,7 +147,10 @@ static void EmergencyRxCallback(const uint16_t ident, log_printf(LOG_NOTICE, DBG_EMERGENCY_RX, nodeIdRx, errorCode, errorRegister, errorBit, infoCode); } +#endif +#if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE) \ + || ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE) /* return string description of NMT state. */ static char *NmtState2Str(CO_NMT_internalState_t state) { @@ -159,12 +162,15 @@ static char *NmtState2Str(CO_NMT_internalState_t state) default: return "unknown"; } } +#endif +#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE /* callback for NMT change messages */ static void NmtChangedCallback(CO_NMT_internalState_t state) { log_printf(LOG_NOTICE, DBG_NMT_CHANGE, NmtState2Str(state), state); } +#endif #if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE /* callback for monitoring Heartbeat remote NMT state change */ @@ -235,6 +241,7 @@ printf( * Mainline thread ******************************************************************************/ int main (int argc, char *argv[]) { + int programExit = EXIT_SUCCESS; CO_epoll_t epMain; #ifndef CO_SINGLE_THREAD pthread_t rt_thread_id; @@ -273,9 +280,6 @@ int main (int argc, char *argv[]) { exit(EXIT_SUCCESS); } 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_pendingNodeId = (uint8_t)strtol(optarg, NULL, 0); @@ -288,7 +292,10 @@ int main (int argc, char *argv[]) { case 'r': rebootEnable = true; break; #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII - case 'c': + case 'c': { + const char *comm_stdio = "stdio"; + const char *comm_local = "local-"; + const char *comm_tcp = "tcp-"; if (strcmp(optarg, comm_stdio) == 0) { commandInterface = CO_COMMAND_IF_STDIO; } @@ -311,6 +318,7 @@ int main (int argc, char *argv[]) { exit(EXIT_FAILURE); } break; + } case 'T': socketTimeout_ms = strtoul(optarg, NULL, 0); break; @@ -452,21 +460,33 @@ int main (int argc, char *argv[]) { err = CO_CANinit((void *)&CANptr, 0 /* bit rate not used */); if(err != CO_ERROR_NO) { log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANinit()", err); - exit(EXIT_FAILURE); + programExit = EXIT_FAILURE; + CO_endProgram = 1; + continue; } err = CO_LSSinit(&CO_pendingNodeId, &CO_pendingBitRate); if(err != CO_ERROR_NO) { log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_LSSinit()", err); - exit(EXIT_FAILURE); + programExit = EXIT_FAILURE; + CO_endProgram = 1; + continue; } CO_activeNodeId = CO_pendingNodeId; err = CO_CANopenInit(CO_activeNodeId); if(err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { - log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANopenInit()", err); - exit(EXIT_FAILURE); + if (err == CO_ERROR_OD_PARAMETERS) { + log_printf(LOG_CRIT, DBG_OD_ENTRY, + (uint16_t)CO->CANmodule[0]->errinfo); + } + else { + log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANopenInit()", err); + } + programExit = EXIT_FAILURE; + CO_endProgram = 1; + continue; } /* initialize part of threadMain and callbacks */ @@ -479,8 +499,12 @@ int main (int argc, char *argv[]) { LSScfgStoreCallback); #endif if(!CO->nodeIdUnconfigured) { +#if (CO_CONFIG_EM) & CO_CONFIG_EM_CONSUMER CO_EM_initCallbackRx(CO->em, EmergencyRxCallback); +#endif +#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback); +#endif #if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_CALLBACK_CHANGE CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL, HeartbeatNmtChangedCallback); @@ -514,7 +538,9 @@ int main (int argc, char *argv[]) { /* Create rt_thread and set priority */ if(pthread_create(&rt_thread_id, NULL, rt_thread, NULL) != 0) { log_printf(LOG_CRIT, DBG_ERRNO, "pthread_create(rt_thread)"); - exit(EXIT_FAILURE); + programExit = EXIT_FAILURE; + CO_endProgram = 1; + continue; } if(rtPriority > 0) { struct sched_param param; @@ -522,13 +548,27 @@ int main (int argc, char *argv[]) { param.sched_priority = rtPriority; if (pthread_setschedparam(rt_thread_id, SCHED_FIFO, ¶m) != 0) { log_printf(LOG_CRIT, DBG_ERRNO, "pthread_setschedparam()"); - exit(EXIT_FAILURE); + programExit = EXIT_FAILURE; + CO_endProgram = 1; + continue; } } #endif #ifdef CO_USE_APPLICATION /* Execute optional additional application code */ - app_programStart(!CO->nodeIdUnconfigured); + uint16_t errinfo = 0; + err = app_programStart(!CO->nodeIdUnconfigured, &errinfo); + if(err != CO_ERROR_NO) { + if (err == CO_ERROR_OD_PARAMETERS) { + log_printf(LOG_CRIT, DBG_OD_ENTRY, errinfo); + } + else { + log_printf(LOG_CRIT, DBG_CAN_OPEN, "app_programStart()", err); + } + programExit = EXIT_FAILURE; + CO_endProgram = 1; + continue; + } #endif } /* if(firstRun) */ @@ -599,6 +639,7 @@ int main (int argc, char *argv[]) { #if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII CO_epoll_closeGtw(&epGtw); #endif + CO_CANsetConfigurationMode((void *)&CANptr); CO_delete((void *)&CANptr); log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_activeNodeId, "finished"); @@ -612,7 +653,7 @@ int main (int argc, char *argv[]) { } } - exit(EXIT_SUCCESS); + exit(programExit); } #ifndef CO_SINGLE_THREAD @@ -638,7 +679,7 @@ static void* rt_thread(void* arg) { #ifdef CO_USE_APPLICATION /* Execute optional additional application code */ - app_program1ms(!CO->nodeIdUnconfigured); + app_program1ms(!CO->nodeIdUnconfigured, epRT.timeDifference_us); #endif } From 2874c4e0bba554a562cb45af324b5021126e604a Mon Sep 17 00:00:00 2001 From: Janez Date: Thu, 31 Dec 2020 14:01:02 +0100 Subject: [PATCH 16/21] 305/CO_LSS.h now passing -Wextra warnings, fix 0x1012 object, update docs --- 305/CO_LSS.h | 12 +++++++++--- README.md | 4 ---- doc/deviceSupport.md | 1 + example/CO_OD.c | 2 +- example/_project.xml | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/305/CO_LSS.h b/305/CO_LSS.h index 1540bc0e2..c78302752 100644 --- a/305/CO_LSS.h +++ b/305/CO_LSS.h @@ -141,7 +141,8 @@ typedef enum { CO_LSS_FASTSCAN_CONFIRM = 0x80U /**< All LSS slaves waiting for scan respond and previous scan is reset */ } CO_LSS_fastscan_bitcheck; -#define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) ((bit>=CO_LSS_FASTSCAN_BIT0 && bit<=CO_LSS_FASTSCAN_BIT31) || bit==CO_LSS_FASTSCAN_CONFIRM) + +#define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) (bit<=CO_LSS_FASTSCAN_BIT31 || bit==CO_LSS_FASTSCAN_CONFIRM) /** * Fastscan LSSsub, LSSnext @@ -153,7 +154,8 @@ typedef enum { CO_LSS_FASTSCAN_SERIAL = 3 /**< Serial number */ } CO_LSS_fastscan_lss_sub_next; -#define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index>=CO_LSS_FASTSCAN_VENDOR_ID && index<=CO_LSS_FASTSCAN_SERIAL) +/* comparision is always true: index>=CO_LSS_FASTSCAN_VENDOR_ID */ +#define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index<=CO_LSS_FASTSCAN_SERIAL) /** * The LSS address is a 128 bit number, uniquely identifying each node. It @@ -219,7 +221,11 @@ static const uint16_t CO_LSS_bitTimingTableLookup[] = { /** * Macro to check if index contains valid bit timing */ -#define CO_LSS_BIT_TIMING_VALID(index) (index != 5 && (index >= CO_LSS_BIT_TIMING_1000 && index <= CO_LSS_BIT_TIMING_AUTO)) +#if CO_LSS_BIT_TIMING_1000 != 0 +#error missing comprision index >= CO_LSS_BIT_TIMING_1000 +#endif +/* comparision is always true: index >= CO_LSS_BIT_TIMING_1000 */ +#define CO_LSS_BIT_TIMING_VALID(index) (index != 5 && index <= CO_LSS_BIT_TIMING_AUTO) /** * Invalid node ID triggers node ID assignment diff --git a/README.md b/README.md index 29aa73d3c..e8246108e 100644 --- a/README.md +++ b/README.md @@ -223,10 +223,6 @@ format. Generated project file can then be saved in XDD v1.1 file format other formats, it can be used to generate documentation and CANopenNode source files for Object Dictionary. -Main project file format for this (old) version of CANopenNode is customized XML. -It is advised to export project to this format also, before exporting to the old -CANopenNode source files (<= v2, legacy). - To clone, add or delete, select object(s) and use right click. Some knowledge of CANopen is required to correctly set-up the custom Object Dictionary. Separate objects can also be inserted from another project. diff --git a/doc/deviceSupport.md b/doc/deviceSupport.md index ab47409d9..0e9d4b1d8 100644 --- a/doc/deviceSupport.md +++ b/doc/deviceSupport.md @@ -96,6 +96,7 @@ S32DS (NXP S32 Design studio for Arm or Powerpc) Other ----- +* [ESP32](https://github.com/CANopenNode/CANopenNode/issues/198#issuecomment-658429391), 2020-07-14 * [FreeRTOS](https://github.com/martinwag/CANopenNode/tree/neuberger-freertos/stack/neuberger-FreeRTOS) by Neuberger, 2020-06-23, based on v1.3-master, see also [issue 198](https://github.com/CANopenNode/CANopenNode/issues/198). * [STM32CubeMX HAL](https://github.com/w1ne/CANOpenNode-CubeMX-HAL), 2019-05-03, demo project for Atollic studio, tested on Nucleo STM32L452xx board. * K64F_FreeRTOS, Kinetis SDK, 2018-02-13, [zip file](https://github.com/CANopenNode/CANopenNode/pull/28#issuecomment-365392867) diff --git a/example/CO_OD.c b/example/CO_OD.c index 0a5d68d28..a3cb80fb1 100644 --- a/example/CO_OD.c +++ b/example/CO_OD.c @@ -307,7 +307,7 @@ const CO_OD_entry_t CO_OD[CO_OD_NoOfElements] = { {0x100A, 0x00, 0x05, 4, (void*)&CO_OD_ROM.manufacturerSoftwareVersion[0]}, {0x1010, 0x01, 0x8E, 4, (void*)&CO_OD_RAM.storeParameters[0]}, {0x1011, 0x01, 0x8E, 4, (void*)&CO_OD_RAM.restoreDefaultParameters[0]}, -{0x1012, 0x00, 0x85, 4, (void*)&CO_OD_ROM.COB_ID_TIME}, +{0x1012, 0x00, 0x8D, 4, (void*)&CO_OD_ROM.COB_ID_TIME}, {0x1014, 0x00, 0x85, 4, (void*)&CO_OD_ROM.COB_ID_EMCY}, {0x1015, 0x00, 0x8D, 2, (void*)&CO_OD_ROM.inhibitTimeEMCY}, {0x1016, 0x04, 0x8D, 4, (void*)&CO_OD_ROM.consumerHeartbeatTime[0]}, diff --git a/example/_project.xml b/example/_project.xml index 7e5bab26f..678a7e513 100644 --- a/example/_project.xml +++ b/example/_project.xml @@ -142,7 +142,7 @@ bit 31: set to 0 - + * bit 31: If set, CANopen device consumes TIME message * bit 30: If set, CANopen device produces TIME message * bit 11-29: set to 0 From 689572afaf30531295d65ff1d59849ea02116288 Mon Sep 17 00:00:00 2001 From: Janez Date: Tue, 5 Jan 2021 08:58:09 +0100 Subject: [PATCH 17/21] Replace type char with uint8_t or int8_t, where appropriate --- 301/CO_SDOclient.c | 20 ++-- 301/CO_SDOclient.h | 6 +- 301/CO_fifo.c | 223 +++++++++++++++++++++-------------------- 301/CO_fifo.h | 18 ++-- 305/CO_LSS.h | 2 +- 309/CO_gateway_ascii.c | 7 +- 309/CO_gateway_ascii.h | 6 +- socketCAN/CO_error.c | 2 +- 8 files changed, 144 insertions(+), 140 deletions(-) diff --git a/301/CO_SDOclient.c b/301/CO_SDOclient.c index 0d037e1fa..215457c4a 100644 --- a/301/CO_SDOclient.c +++ b/301/CO_SDOclient.c @@ -115,7 +115,7 @@ static void CO_SDOclient_receive(void *object, void *msg) { /* Copy data. There is always enough space in fifo buffer, * because block_blksize was calculated before */ CO_fifo_write(&SDO_C->bufFifo, - (const char *)&data[1], + &data[1], 7, &SDO_C->block_crc); SDO_C->sizeTran += 7; /* all segments in sub-block has been transferred */ @@ -382,7 +382,7 @@ void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C, /******************************************************************************/ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, - const char *buf, + const uint8_t *buf, size_t count) { size_t ret = 0; @@ -689,7 +689,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, /* copy data */ CO_fifo_read(&SDO_C->bufFifo, - (char *)&SDO_C->CANtxBuff->data[4], count, NULL); + &SDO_C->CANtxBuff->data[4], count, NULL); SDO_C->sizeTran = count; SDO_C->finished = true; } @@ -720,7 +720,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: { /* fill data bytes */ count = CO_fifo_read(&SDO_C->bufFifo, - (char *)&SDO_C->CANtxBuff->data[1], + &SDO_C->CANtxBuff->data[1], 7, NULL); /* verify if sizeTran is too large */ @@ -784,7 +784,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, /* get up to 7 data bytes */ count = CO_fifo_altRead(&SDO_C->bufFifo, - (char *)&SDO_C->CANtxBuff->data[1], 7); + &SDO_C->CANtxBuff->data[1], 7); SDO_C->block_noData = 7 - count; /* verify if sizeTran is too large */ @@ -1032,7 +1032,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, } /* copy data, indicate size and finish */ CO_fifo_write(&SDO_C->bufFifo, - (const char *)&SDO_C->CANrxData[4], + &SDO_C->CANrxData[4], count, NULL); SDO_C->sizeTran = count; SDO_C->state = CO_SDO_ST_IDLE; @@ -1078,7 +1078,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, /* get data size and write data to the buffer */ count = 7 - ((SDO_C->CANrxData[0] >> 1) & 0x07); countWr = CO_fifo_write(&SDO_C->bufFifo, - (const char *)&SDO_C->CANrxData[1], + &SDO_C->CANrxData[1], count, NULL); SDO_C->sizeTran += countWr; @@ -1176,7 +1176,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, } /* copy data, indicate size and finish */ CO_fifo_write(&SDO_C->bufFifo, - (const char *)&SDO_C->CANrxData[4], + &SDO_C->CANrxData[4], count, NULL); SDO_C->sizeTran = count; SDO_C->state = CO_SDO_ST_IDLE; @@ -1211,7 +1211,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, * contain data. Then copy remaining data into fifo */ uint8_t noData = ((SDO_C->CANrxData[0] >> 2) & 0x07); CO_fifo_write(&SDO_C->bufFifo, - (const char *)&SDO_C->block_dataUploadLast[0], + &SDO_C->block_dataUploadLast[0], 7 - noData, &SDO_C->block_crc); SDO_C->sizeTran += 7 - noData; @@ -1524,7 +1524,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, /******************************************************************************/ size_t CO_SDOclientUploadBufRead(CO_SDOclient_t *SDO_C, - char *buf, + uint8_t *buf, size_t count) { size_t ret = 0; diff --git a/301/CO_SDOclient.h b/301/CO_SDOclient.h index 5fcdb0090..446beb5b8 100644 --- a/301/CO_SDOclient.h +++ b/301/CO_SDOclient.h @@ -125,7 +125,7 @@ typedef struct { 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]; + uint8_t 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; @@ -307,7 +307,7 @@ void CO_SDOclientDownloadInitiateSize(CO_SDOclient_t *SDO_C, * @return number of bytes actually written. */ size_t CO_SDOclientDownloadBufWrite(CO_SDOclient_t *SDO_C, - const char *buf, + const uint8_t *buf, size_t count); @@ -430,7 +430,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, * @return number of bytes actually read. */ size_t CO_SDOclientUploadBufRead(CO_SDOclient_t *SDO_C, - char *buf, + uint8_t *buf, size_t count); diff --git a/301/CO_fifo.c b/301/CO_fifo.c index 6f49c54cf..b00423831 100644 --- a/301/CO_fifo.c +++ b/301/CO_fifo.c @@ -37,9 +37,11 @@ #include /* Non-graphical character for command delimiter */ -#define DELIM_COMMAND '\n' +#define DELIM_COMMAND ((uint8_t)'\n') /* Graphical character for comment delimiter */ -#define DELIM_COMMENT '#' +#define DELIM_COMMENT ((uint8_t)'#') +/* Graphical character for double quotes */ +#define DELIM_DQUOTE ((uint8_t)'"') #endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_COMMANDS */ /* verify configuration */ @@ -50,7 +52,7 @@ #endif /******************************************************************************/ -void CO_fifo_init(CO_fifo_t *fifo, char *buf, size_t bufSize) { +void CO_fifo_init(CO_fifo_t *fifo, uint8_t *buf, size_t bufSize) { if (fifo == NULL || buf == NULL || bufSize < 2) { return; @@ -79,12 +81,12 @@ void CO_fifo_init(CO_fifo_t *fifo, char *buf, size_t bufSize) { * buffer in buff in buff full * ******************************************************************************/ size_t CO_fifo_write(CO_fifo_t *fifo, - const char *buf, + const uint8_t *buf, size_t count, uint16_t *crc) { size_t i; - char *bufDest; + uint8_t *bufDest; if (fifo == NULL || fifo->buf == NULL || buf == NULL) { return 0; @@ -104,7 +106,7 @@ size_t CO_fifo_write(CO_fifo_t *fifo, #if (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_CRC16_CCITT if (crc != NULL) { - crc16_ccitt_single(crc, (unsigned char)*buf); + crc16_ccitt_single(crc, *buf); } #endif @@ -125,9 +127,9 @@ size_t CO_fifo_write(CO_fifo_t *fifo, /******************************************************************************/ -size_t CO_fifo_read(CO_fifo_t *fifo, char *buf, size_t count, bool_t *eof) { +size_t CO_fifo_read(CO_fifo_t *fifo, uint8_t *buf, size_t count, bool_t *eof) { size_t i; - const char *bufSrc; + const uint8_t *bufSrc; if (eof != NULL) { *eof = false; @@ -138,7 +140,7 @@ size_t CO_fifo_read(CO_fifo_t *fifo, char *buf, size_t count, bool_t *eof) { bufSrc = &fifo->buf[fifo->readPtr]; for (i = count; i > 0; ) { - const char c = *bufSrc; + const uint8_t c = *bufSrc; /* is circular buffer empty */ if (fifo->readPtr == fifo->writePtr) { @@ -205,10 +207,10 @@ void CO_fifo_altFinish(CO_fifo_t *fifo, uint16_t *crc) { fifo->readPtr = fifo->altReadPtr; } else { - const char *bufSrc = &fifo->buf[fifo->readPtr]; + const uint8_t *bufSrc = &fifo->buf[fifo->readPtr]; while (fifo->readPtr != fifo->altReadPtr) { #if (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_CRC16_CCITT - crc16_ccitt_single(crc, (unsigned char)*bufSrc); + crc16_ccitt_single(crc, *bufSrc); #endif /* increment variable */ if (++fifo->readPtr == fifo->bufSize) { @@ -222,13 +224,13 @@ void CO_fifo_altFinish(CO_fifo_t *fifo, uint16_t *crc) { } } -size_t CO_fifo_altRead(CO_fifo_t *fifo, char *buf, size_t count) { +size_t CO_fifo_altRead(CO_fifo_t *fifo, uint8_t *buf, size_t count) { size_t i; - const char *bufSrc; + const uint8_t *bufSrc; bufSrc = &fifo->buf[fifo->altReadPtr]; for (i = count; i > 0; i--) { - const char c = *bufSrc; + const uint8_t c = *bufSrc; /* is there no more data */ if (fifo->altReadPtr == fifo->writePtr) { @@ -257,7 +259,7 @@ size_t CO_fifo_altRead(CO_fifo_t *fifo, char *buf, size_t count) { bool_t CO_fifo_CommSearch(CO_fifo_t *fifo, bool_t clear) { bool_t newCommand = false; size_t count; - char *commandEnd; + uint8_t *commandEnd; if (fifo == NULL || fifo->readPtr == fifo->writePtr) { return 0; @@ -269,17 +271,17 @@ bool_t CO_fifo_CommSearch(CO_fifo_t *fifo, bool_t clear) { } else { count = fifo->bufSize - fifo->readPtr; } - commandEnd = (char *)memchr((const void *)&fifo->buf[fifo->readPtr], - DELIM_COMMAND, - count); + commandEnd = (uint8_t *)memchr((const void *)&fifo->buf[fifo->readPtr], + (int)DELIM_COMMAND, + count); if (commandEnd != NULL) { newCommand = true; } else if (fifo->readPtr > fifo->writePtr) { /* not found, search in the beginning of the circular buffer */ - commandEnd = (char *)memchr((const void *)&fifo->buf[0], - DELIM_COMMAND, - fifo->writePtr); + commandEnd = (uint8_t *)memchr((const void *)&fifo->buf[0], + (int)DELIM_COMMAND, + fifo->writePtr); if (commandEnd != NULL || fifo->readPtr == (fifo->writePtr + 1)) { /* command delimiter found or buffer full */ newCommand = true; @@ -293,7 +295,7 @@ bool_t CO_fifo_CommSearch(CO_fifo_t *fifo, bool_t clear) { /* Clear buffer if set so */ if (clear) { if (commandEnd != NULL) { - fifo->readPtr = (int)(commandEnd - fifo->buf) + 1; + fifo->readPtr = (size_t)(commandEnd - fifo->buf) + 1; if (fifo->readPtr == fifo->bufSize) { fifo->readPtr = 0; } @@ -313,7 +315,7 @@ bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo, bool_t *insideComment) { if (fifo != NULL && insideComment != NULL) { while (fifo->readPtr != fifo->writePtr) { - char c = fifo->buf[fifo->readPtr]; + uint8_t c = fifo->buf[fifo->readPtr]; if (c == DELIM_COMMENT) { *insideComment = true; @@ -339,7 +341,7 @@ bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo, bool_t *insideComment) { size_t CO_fifo_readToken(CO_fifo_t *fifo, char *buf, size_t count, - char *closed, + int8_t *closed, bool_t *err) { bool_t delimCommandFound = false; @@ -350,9 +352,9 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, && fifo->readPtr != fifo->writePtr ) { bool_t finished = false; - char step = 0; + uint8_t step = 0; size_t ptr = fifo->readPtr; /* current pointer (integer, 0 based) */ - char *c = &fifo->buf[ptr]; /* current char */ + uint8_t *c = &fifo->buf[ptr]; /* current character */ do { switch (step) { case 0: /* skip leading empty characters, stop on delimiter */ @@ -360,7 +362,7 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, if (*c == DELIM_COMMENT) { delimCommentFound = true; } else { - buf[tokenSize++] = *c; + buf[tokenSize++] = (char)*c; step++; } } @@ -373,7 +375,7 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, if (*c == DELIM_COMMENT) { delimCommentFound = true; } else if (tokenSize < count) { - buf[tokenSize++] = *c; + buf[tokenSize++] = (char)*c; } } else { @@ -446,7 +448,7 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, *closed = delimCommandFound ? 1 : 0; } - /* token was larger then size of the buffer, all was cleaned, return '' */ + /* token was larger then size of the buffer, all was cleaned, return empty*/ if (tokenSize == count) { tokenSize = 0; } @@ -470,21 +472,21 @@ size_t CO_fifo_readToken(CO_fifo_t *fifo, static const char base64EncTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char base64DecTable[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1,103,101, -1, -1,102, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 103, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,100, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; +static const uint8_t base64DecTable[] = { + 255,255,255,255,255,255,255,255,255,103,101,255,255,102,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 103,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,100,255,255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255}; size_t CO_fifo_readU82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint8_t n=0; if (fifo != NULL && count >= 6 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, &n, sizeof(n), NULL); return sprintf(buf, "%"PRIu8, n); } else { @@ -496,7 +498,7 @@ size_t CO_fifo_readU162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint16_t n=0; if (fifo != NULL && count >= 8 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRIu16, CO_SWAP_16(n)); } else { @@ -508,7 +510,7 @@ size_t CO_fifo_readU322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint32_t n=0; if (fifo != NULL && count >= 12 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRIu32, CO_SWAP_32(n)); } else { @@ -520,7 +522,7 @@ size_t CO_fifo_readU642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint64_t n=0; if (fifo != NULL && count >= 20 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRIu64, CO_SWAP_64(n)); } else { @@ -532,7 +534,7 @@ size_t CO_fifo_readX82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint8_t n=0; if (fifo != NULL && count >= 6 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "0x%02"PRIX8, n); } else { @@ -544,7 +546,7 @@ size_t CO_fifo_readX162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint16_t n=0; if (fifo != NULL && count >= 8 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "0x%04"PRIX16, CO_SWAP_16(n)); } else { @@ -556,7 +558,7 @@ size_t CO_fifo_readX322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint32_t n=0; if (fifo != NULL && count >= 12 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "0x%08"PRIX32, CO_SWAP_32(n)); } else { @@ -568,7 +570,7 @@ size_t CO_fifo_readX642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { uint64_t n=0; if (fifo != NULL && count >= 20 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "0x%016"PRIX64, CO_SWAP_64(n)); } else { @@ -580,7 +582,7 @@ size_t CO_fifo_readI82a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { int8_t n=0; if (fifo != NULL && count >= 6 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRId8, n); } else { @@ -592,7 +594,7 @@ size_t CO_fifo_readI162a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { int16_t n=0; if (fifo != NULL && count >= 8 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRId16, CO_SWAP_16(n)); } else { @@ -604,7 +606,7 @@ size_t CO_fifo_readI322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { int32_t n=0; if (fifo != NULL && count >= 13 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRId32, CO_SWAP_32(n)); } else { @@ -616,7 +618,7 @@ size_t CO_fifo_readI642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { int64_t n=0; if (fifo != NULL && count >= 23 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%"PRId64, CO_SWAP_64(n)); } else { @@ -628,7 +630,7 @@ size_t CO_fifo_readR322a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { float32_t n=0; if (fifo != NULL && count >= 20 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%g", CO_SWAP_32(n)); } else { @@ -640,7 +642,7 @@ size_t CO_fifo_readR642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { float64_t n=0; if (fifo != NULL && count >= 30 && CO_fifo_getOccupied(fifo) == sizeof(n)) { - CO_fifo_read(fifo, (char *)&n, sizeof(n), NULL); + CO_fifo_read(fifo, (uint8_t *)&n, sizeof(n), NULL); return sprintf(buf, "%g", CO_SWAP_64(n)); } else { @@ -656,19 +658,19 @@ size_t CO_fifo_readHex2a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { if (fifo != NULL && count > 3) { /* Very first write is without leading space */ if (!fifo->started) { - char c; + uint8_t c; if(CO_fifo_getc(fifo, &c)) { - len = sprintf(&buf[0], "%02"PRIX8, (uint8_t)c); + len = sprintf(&buf[0], "%02"PRIX8, c); fifo->started = true; } } while ((len + 3) < count) { - char c; + uint8_t c; if(!CO_fifo_getc(fifo, &c)) { break; } - len += sprintf(&buf[len], " %02"PRIX8, (uint8_t)c); + len += sprintf(&buf[len], " %02"PRIX8, c); } } @@ -686,17 +688,18 @@ size_t CO_fifo_readVs2a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } while ((len + 2) < count) { - char c; + uint8_t c; if(!CO_fifo_getc(fifo, &c)) { if (end) { buf[len++] = '"'; } break; } - else if (c != 0 && c != '\r') { /* skip null and CR inside string */ - buf[len++] = c; - if (c == '"') { - buf[len++] = c; + else if (c != 0 && c != (uint8_t)'\r') { + /* skip null and CR inside string */ + buf[len++] = (char)c; + if (c == DELIM_DQUOTE) { + buf[len++] = '"'; } } } @@ -726,7 +729,7 @@ size_t CO_fifo_readB642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { } while ((len + 3) <= count) { - char c; + uint8_t c; if(!CO_fifo_getc(fifo, &c)) { /* buffer is empty, is also SDO communication finished? */ @@ -747,7 +750,7 @@ size_t CO_fifo_readB642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { break; } - word |= (uint8_t)c; + word |= c; switch (step++) { case 0: @@ -776,7 +779,7 @@ size_t CO_fifo_readB642a(CO_fifo_t *fifo, char *buf, size_t count, bool_t end) { /******************************************************************************/ size_t CO_fifo_cpyTok2U8(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[15]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -788,7 +791,7 @@ size_t CO_fifo_cpyTok2U8(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0') || u32 > UINT8_MAX) st |= CO_fifo_st_errVal; else { uint8_t num = (uint8_t) u32; - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, &num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -798,7 +801,7 @@ size_t CO_fifo_cpyTok2U8(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2U16(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[15]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -810,7 +813,7 @@ size_t CO_fifo_cpyTok2U16(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0') || u32 > UINT16_MAX) st |= CO_fifo_st_errVal; else { uint16_t num = CO_SWAP_16((uint16_t) u32); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -820,7 +823,7 @@ size_t CO_fifo_cpyTok2U16(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2U32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[15]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -832,7 +835,7 @@ size_t CO_fifo_cpyTok2U32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0')) st |= CO_fifo_st_errVal; else { uint32_t num = CO_SWAP_32(u32); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -842,7 +845,7 @@ size_t CO_fifo_cpyTok2U32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2U64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[25]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -854,7 +857,7 @@ size_t CO_fifo_cpyTok2U64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0')) st |= CO_fifo_st_errVal; else { uint64_t num = CO_SWAP_64(u64); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -864,7 +867,7 @@ size_t CO_fifo_cpyTok2U64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2I8(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[15]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -877,7 +880,7 @@ size_t CO_fifo_cpyTok2I8(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { st |= CO_fifo_st_errVal; } else { int8_t num = (int8_t) i32; - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -887,7 +890,7 @@ size_t CO_fifo_cpyTok2I8(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2I16(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[15]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -900,7 +903,7 @@ size_t CO_fifo_cpyTok2I16(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { st |= CO_fifo_st_errVal; } else { int16_t num = CO_SWAP_16((int16_t) i32); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -910,7 +913,7 @@ size_t CO_fifo_cpyTok2I16(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2I32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[15]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -922,7 +925,7 @@ size_t CO_fifo_cpyTok2I32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0')) st |= CO_fifo_st_errVal; else { int32_t num = CO_SWAP_32(i32); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -932,7 +935,7 @@ size_t CO_fifo_cpyTok2I32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2I64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[25]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -944,7 +947,7 @@ size_t CO_fifo_cpyTok2I64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0')) st |= CO_fifo_st_errVal; else { int64_t num = CO_SWAP_64(i64); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -954,7 +957,7 @@ size_t CO_fifo_cpyTok2I64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2R32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[30]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -966,7 +969,7 @@ size_t CO_fifo_cpyTok2R32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0')) st |= CO_fifo_st_errVal; else { float32_t num = CO_SWAP_32(f32); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -976,7 +979,7 @@ size_t CO_fifo_cpyTok2R32(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t CO_fifo_cpyTok2R64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { char buf[40]; - char closed = -1; + int8_t closed = -1; bool_t err = 0; size_t nWr = 0; size_t nRd = CO_fifo_readToken(src, buf, sizeof(buf), &closed, &err); @@ -988,7 +991,7 @@ size_t CO_fifo_cpyTok2R64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (sRet != strchr(buf, '\0')) st |= CO_fifo_st_errVal; else { float64_t num = CO_SWAP_64(f64); - nWr = CO_fifo_write(dest, (const char *)&num, sizeof(num), NULL); + nWr = CO_fifo_write(dest, (uint8_t *)&num, sizeof(num), NULL); if (nWr != sizeof(num)) st |= CO_fifo_st_errBuf; } } @@ -1000,7 +1003,7 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { size_t destSpace, destSpaceStart; bool_t finished = false; uint8_t step; - char firstChar; + uint8_t firstChar; CO_fifo_st st = 0; if (dest == NULL || src == NULL) { @@ -1024,13 +1027,13 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { else { /* get memorized variables from previous function calls */ step = (uint8_t)(dest->aux >> 8); - firstChar = (char)(dest->aux & 0xFF); + firstChar = (uint8_t)(dest->aux & 0xFF); } /* repeat until destination space available and no error and not finished * and source characters available */ while (destSpace > 0 && (st & CO_fifo_st_errMask) == 0 && !finished) { - char c; + uint8_t c; if (!CO_fifo_getc(src, &c)) { break; } @@ -1053,18 +1056,18 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { } else { /* write the byte */ - char s[3]; + uint8_t s[3]; int32_t num; s[0] = firstChar; s[1] = c; s[2] = 0; - num = strtol(s, NULL, 16); - CO_fifo_putc(dest, (char) num); + num = strtol((char *)&s[0], NULL, 16); + CO_fifo_putc(dest, (uint8_t) num); destSpace--; step = 0; } } else if (isgraph((int)c) != 0) { /* printable character, not hex digit */ - if (c == '#') /* comment start */ + if (c == DELIM_COMMENT) /* comment start */ step = 6; else /* syntax error */ st |= CO_fifo_st_errTok; @@ -1073,11 +1076,11 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { /* this is space or delimiter */ if (step == 1) { /* write the byte */ - char s[2]; + uint8_t s[2]; int32_t num; s[0] = firstChar; s[1] = 0; - num = strtol(s, NULL, 16); - CO_fifo_putc(dest, (char) num); + num = strtol((char *)&s[0], NULL, 16); + CO_fifo_putc(dest, (uint8_t) num); destSpace--; step = 0; } @@ -1096,7 +1099,7 @@ size_t CO_fifo_cpyTok2Hex(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { if (!finished) { st |= CO_fifo_st_partial; /* memorize variables for next iteration */ - dest->aux = (uint32_t)step << 8 | (uint8_t)firstChar; + dest->aux = (uint32_t)step << 8 | firstChar; } if (status != NULL) *status = st; @@ -1135,14 +1138,14 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { /* repeat until destination space available and no error and not finished * and source characters available */ while (destSpace > 0 && (st & CO_fifo_st_errMask) == 0 && !finished) { - char c; + uint8_t c; if (!CO_fifo_getc(src, &c)) { break; } switch (step) { case 0: /* beginning of the string, first write into dest */ - if (c == '"') { + if (c == DELIM_DQUOTE) { /* Indicated beginning of the string, skip this character. */ step = 1; } @@ -1157,7 +1160,7 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { case 1: /* inside string, quoted string */ case 2: /* inside string, single word, no quotes */ - if (c == '"') { + if (c == DELIM_DQUOTE) { /* double quote found, this may be end of the string or escaped * double quote (with two double quotes) */ step += 2; @@ -1188,7 +1191,7 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { case 3: /* previous was double quote, parsing quoted string */ case 4: /* previous was double quote, parsing no quoted word */ - if (c == '"') { + if (c == DELIM_DQUOTE) { /* escaped double quote, copy the character and continue */ CO_fifo_putc(dest, c); destSpace--; @@ -1232,7 +1235,7 @@ size_t CO_fifo_cpyTok2Vs(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { step = 6; } else if (isgraph((int)c) != 0) { - if (c == '#') /* comment start */ + if (c == DELIM_COMMENT) /* comment start */ step = 6; else /* syntax error */ st |= CO_fifo_st_errTok; @@ -1300,7 +1303,7 @@ size_t CO_fifo_cpyTok2B64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { /* repeat until destination space available and no error and not finished * and source characters available */ while (destSpace >= 3 && (st & CO_fifo_st_errMask) == 0 && !finished) { - char c; + uint8_t c; if (!CO_fifo_getc(src, &c)) { break; } @@ -1315,8 +1318,8 @@ size_t CO_fifo_cpyTok2B64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { else if (insideComment) { step = 6; } - else if (isgraph((int)c) != 0 && c != '=') { - if (c == '#') /* comment start */ + else if (isgraph((int)c) != 0 && c != (uint8_t)'=') { + if (c == DELIM_COMMENT) /* comment start */ step = 6; else /* syntax error */ st |= CO_fifo_st_errTok; @@ -1324,21 +1327,21 @@ size_t CO_fifo_cpyTok2B64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { continue; } - char code = base64DecTable[c & 0x7F]; + uint8_t code = base64DecTable[c & 0x7F]; - if (c < 0 || code < 0) { + if ((c & 0x80) != 0 || (code & 0x80) != 0) { st |= CO_fifo_st_errTok; } else if (code >= 64 /* '=' (pad) or DELIM_COMMAND or space */) { /* base64 string finished, write remaining bytes */ switch (step) { case 2: - CO_fifo_putc(dest, (char)(dword >> 4)); + CO_fifo_putc(dest, (uint8_t)(dword >> 4)); destSpace --; break; case 3: - CO_fifo_putc(dest, (char)(dword >> 10)); - CO_fifo_putc(dest, (char)(dword >> 2)); + CO_fifo_putc(dest, (uint8_t)(dword >> 10)); + CO_fifo_putc(dest, (uint8_t)(dword >> 2)); destSpace -= 2; break; } @@ -1355,9 +1358,9 @@ size_t CO_fifo_cpyTok2B64(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status) { else { dword = (dword << 6) | code; if (step++ == 3) { - CO_fifo_putc(dest, (char)((dword >> 16) & 0xFF)); - CO_fifo_putc(dest, (char)((dword >> 8) & 0xFF)); - CO_fifo_putc(dest, (char)(dword & 0xFF)); + CO_fifo_putc(dest, (uint8_t)((dword >> 16) & 0xFF)); + CO_fifo_putc(dest, (uint8_t)((dword >> 8) & 0xFF)); + CO_fifo_putc(dest, (uint8_t)(dword & 0xFF)); destSpace -= 3; dword = 0; step = 0; diff --git a/301/CO_fifo.h b/301/CO_fifo.h index 968e39d1d..b7034140a 100644 --- a/301/CO_fifo.h +++ b/301/CO_fifo.h @@ -59,7 +59,7 @@ extern "C" { */ typedef struct { /** Buffer of size bufSize. Initialized by CO_fifo_init() */ - char *buf; + uint8_t *buf; /** Initialized by CO_fifo_init() */ size_t bufSize; /** Location in the buffer, which will be next written. */ @@ -89,7 +89,7 @@ typedef struct { * @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); +void CO_fifo_init(CO_fifo_t *fifo, uint8_t *buf, size_t bufSize); /** @@ -170,7 +170,7 @@ static inline size_t CO_fifo_getOccupied(CO_fifo_t *fifo) { * * @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) { +static inline bool_t CO_fifo_putc(CO_fifo_t *fifo, const uint8_t c) { if (fifo != NULL && fifo->buf != NULL) { size_t writePtrNext = fifo->writePtr + 1; if (writePtrNext != fifo->readPtr && @@ -193,7 +193,7 @@ static inline bool_t CO_fifo_putc(CO_fifo_t *fifo, const char c) { * @param fifo This object * @param c Character to put */ -static inline void CO_fifo_putc_ov(CO_fifo_t *fifo, const char c) { +static inline void CO_fifo_putc_ov(CO_fifo_t *fifo, const uint8_t c) { if (fifo != NULL && fifo->buf != NULL) { fifo->buf[fifo->writePtr] = c; @@ -213,7 +213,7 @@ static inline void CO_fifo_putc_ov(CO_fifo_t *fifo, const char c) { * * @return true, if read was successful (non-empty fifo buffer) */ -static inline bool_t CO_fifo_getc(CO_fifo_t *fifo, char *buf) { +static inline bool_t CO_fifo_getc(CO_fifo_t *fifo, uint8_t *buf) { if (fifo != NULL && buf != NULL && fifo->readPtr != fifo->writePtr) { *buf = fifo->buf[fifo->readPtr]; if (++fifo->readPtr == fifo->bufSize) { @@ -241,7 +241,7 @@ static inline bool_t CO_fifo_getc(CO_fifo_t *fifo, char *buf) { * @return number of bytes actually written. */ size_t CO_fifo_write(CO_fifo_t *fifo, - const char *buf, + const uint8_t *buf, size_t count, uint16_t *crc); @@ -262,7 +262,7 @@ size_t CO_fifo_write(CO_fifo_t *fifo, * * @return number of bytes actually read. */ -size_t CO_fifo_read(CO_fifo_t *fifo, char *buf, size_t count, bool_t *eof); +size_t CO_fifo_read(CO_fifo_t *fifo, uint8_t *buf, size_t count, bool_t *eof); #if ((CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ALT_READ) || defined CO_DOXYGEN @@ -320,7 +320,7 @@ static inline size_t CO_fifo_altGetOccupied(CO_fifo_t *fifo) { * * @return number of bytes actually read. */ -size_t CO_fifo_altRead(CO_fifo_t *fifo, char *buf, size_t count); +size_t CO_fifo_altRead(CO_fifo_t *fifo, uint8_t *buf, size_t count); #endif /* #if (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ALT_READ */ @@ -409,7 +409,7 @@ bool_t CO_fifo_trimSpaces(CO_fifo_t *fifo, bool_t *insideComment); size_t CO_fifo_readToken(CO_fifo_t *fifo, char *buf, size_t count, - char *closed, + int8_t *closed, bool_t *err); #endif /* (CO_CONFIG_FIFO) & CO_CONFIG_FIFO_ASCII_COMMANDS */ diff --git a/305/CO_LSS.h b/305/CO_LSS.h index c78302752..b1d627484 100644 --- a/305/CO_LSS.h +++ b/305/CO_LSS.h @@ -141,7 +141,7 @@ typedef enum { CO_LSS_FASTSCAN_CONFIRM = 0x80U /**< All LSS slaves waiting for scan respond and previous scan is reset */ } CO_LSS_fastscan_bitcheck; - +/* comparision is always true: bit>=CO_LSS_FASTSCAN_BIT0 */ #define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) (bit<=CO_LSS_FASTSCAN_BIT31 || bit==CO_LSS_FASTSCAN_CONFIRM) /** diff --git a/309/CO_gateway_ascii.c b/309/CO_gateway_ascii.c index f65a2fa7d..cb5642c03 100644 --- a/309/CO_gateway_ascii.c +++ b/309/CO_gateway_ascii.c @@ -143,7 +143,7 @@ void CO_GTWA_log_print(CO_GTWA_t* gtwa, const char *message) { const char *c; for (c = &message[0]; *c != 0; c++) { - CO_fifo_putc_ov(>wa->logFifo, *c); + CO_fifo_putc_ov(>wa->logFifo, (const uint8_t)*c); } } } @@ -623,7 +623,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, (void)timerNext_us; /* may be unused */ bool_t err = false; /* syntax or other error, true or false, I/O variable */ - char closed; /* indication of command delimiter, I/O variable */ + int8_t closed; /* indication of command delimiter, I/O variable */ CO_GTWA_respErrorCode_t respErrorCode = CO_GTWA_respErrorNone; if (gtwa == NULL) { @@ -1986,7 +1986,8 @@ void CO_GTWA_process(CO_GTWA_t *gtwa, /* print message log */ case CO_GTWA_ST_LOG: { do { - gtwa->respBufCount = CO_fifo_read(>wa->logFifo, gtwa->respBuf, + gtwa->respBufCount = CO_fifo_read(>wa->logFifo, + (uint8_t *)gtwa->respBuf, CO_GTWA_RESP_BUF_SIZE, NULL); respBufTransfer(gtwa); diff --git a/309/CO_gateway_ascii.h b/309/CO_gateway_ascii.h index 19e63d2ad..b79fe42c7 100644 --- a/309/CO_gateway_ascii.h +++ b/309/CO_gateway_ascii.h @@ -324,7 +324,7 @@ typedef struct { /** 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]; + uint8_t 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 */ @@ -384,7 +384,7 @@ typedef struct { #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG) || defined CO_DOXYGEN /** Message log buffer of usable size @ref CO_CONFIG_GTWA_LOG_BUF_SIZE */ - char logBuf[CO_CONFIG_GTWA_LOG_BUF_SIZE + 1]; + uint8_t logBuf[CO_CONFIG_GTWA_LOG_BUF_SIZE + 1]; /** CO_fifo_t object for message log (not pointer) */ CO_fifo_t logFifo; #endif @@ -486,7 +486,7 @@ 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); + return CO_fifo_write(>wa->commFifo, (const uint8_t *)buf, count, NULL); } diff --git a/socketCAN/CO_error.c b/socketCAN/CO_error.c index 67a458b60..73d8f944f 100644 --- a/socketCAN/CO_error.c +++ b/socketCAN/CO_error.c @@ -40,7 +40,7 @@ */ static CO_CANinterfaceState_t CO_CANerrorSetListenOnly( CO_CANinterfaceErrorhandler_t *CANerrorhandler, - unsigned char resetIf) + bool_t resetIf) { char command[100]; From 887862c60553d2a54ed2436b87e68ad9a5ddbd00 Mon Sep 17 00:00:00 2001 From: Janez Date: Mon, 25 Jan 2021 22:29:00 +0100 Subject: [PATCH 18/21] Synchronize some parts from newOD: - CO_config.h: use global macros for common definitions 8cff68f1 - Remove CO_errinfo() from driver (unused) - Some reorder in Makefile and socketCAN/CO_main_basic.c --- 301/CO_Emergency.h | 4 +- 301/CO_HBconsumer.h | 4 +- 301/CO_NMT_Heartbeat.h | 3 +- 301/CO_PDO.h | 5 +- 301/CO_SDOserver.h | 5 +- 301/CO_SYNC.h | 6 +- 301/CO_config.h | 65 ++++++++--- 301/CO_driver.h | 45 ++++---- 303/CO_LEDs.h | 3 +- 305/CO_LSS.h | 3 +- Makefile | 31 +++--- README.md | 2 +- doc/objectDictionary.md | 202 +++++++++++++++-------------------- example/CO_driver_target.h | 58 +++++----- socketCAN/CO_driver_target.h | 53 +++++---- socketCAN/CO_main_basic.c | 53 ++++----- 16 files changed, 274 insertions(+), 268 deletions(-) diff --git a/301/CO_Emergency.h b/301/CO_Emergency.h index 73e871731..b9b970ce5 100644 --- a/301/CO_Emergency.h +++ b/301/CO_Emergency.h @@ -30,7 +30,9 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_EM -#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER) +#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | \ + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_EM_ERR_STATUS_BITS_COUNT #define CO_CONFIG_EM_ERR_STATUS_BITS_COUNT (10*8) diff --git a/301/CO_HBconsumer.h b/301/CO_HBconsumer.h index 421032ff5..96e862c9b 100644 --- a/301/CO_HBconsumer.h +++ b/301/CO_HBconsumer.h @@ -30,7 +30,9 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_HB_CONS -#define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE) +#define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | \ + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #if ((CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE) || defined CO_DOXYGEN diff --git a/301/CO_NMT_Heartbeat.h b/301/CO_NMT_Heartbeat.h index 83661c1b3..65c263e11 100644 --- a/301/CO_NMT_Heartbeat.h +++ b/301/CO_NMT_Heartbeat.h @@ -32,7 +32,8 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_NMT -#define CO_CONFIG_NMT (0) +#define CO_CONFIG_NMT (CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifdef __cplusplus diff --git a/301/CO_PDO.h b/301/CO_PDO.h index 09a658766..b90dee9c0 100644 --- a/301/CO_PDO.h +++ b/301/CO_PDO.h @@ -36,7 +36,10 @@ #ifndef CO_CONFIG_PDO #define CO_CONFIG_PDO (CO_CONFIG_RPDO_ENABLE | \ CO_CONFIG_TPDO_ENABLE | \ - CO_CONFIG_PDO_SYNC_ENABLE) + CO_CONFIG_PDO_SYNC_ENABLE | \ + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifdef __cplusplus diff --git a/301/CO_SDOserver.h b/301/CO_SDOserver.h index b9011d0b6..37b6df8ce 100644 --- a/301/CO_SDOserver.h +++ b/301/CO_SDOserver.h @@ -33,7 +33,10 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_SDO_SRV -#define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED) +#define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED | \ + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_SDO_SRV_BUFFER_SIZE #define CO_CONFIG_SDO_SRV_BUFFER_SIZE 32 diff --git a/301/CO_SYNC.h b/301/CO_SYNC.h index 47828aee8..4c79bc81a 100644 --- a/301/CO_SYNC.h +++ b/301/CO_SYNC.h @@ -30,7 +30,11 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_SYNC -#define CO_CONFIG_SYNC (CO_CONFIG_SYNC_ENABLE | CO_CONFIG_SYNC_PRODUCER) +#define CO_CONFIG_SYNC (CO_CONFIG_SYNC_ENABLE | \ + CO_CONFIG_SYNC_PRODUCER | \ + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #if ((CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE) || defined CO_DOXYGEN diff --git a/301/CO_config.h b/301/CO_config.h index 1b82de1cc..3e594dc34 100644 --- a/301/CO_config.h +++ b/301/CO_config.h @@ -98,6 +98,21 @@ extern "C" { * This flag is common to multiple configuration macros. */ #define CO_CONFIG_FLAG_OD_DYNAMIC 0x4000 + +/** This flag may be set globally to @ref CO_CONFIG_FLAG_CALLBACK_PRE */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE (0) +#endif + +/** This flag may be set globally to @ref CO_CONFIG_FLAG_TIMERNEXT */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT (0) +#endif + +/** This flag may be set globally to (0) */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC CO_CONFIG_FLAG_OD_DYNAMIC +#endif /** @} */ /* CO_STACK_CONFIG_COMMON */ @@ -120,7 +135,7 @@ extern "C" { * inside CO_NMT_process(). */ #ifdef CO_DOXYGEN -#define CO_CONFIG_NMT (0) +#define CO_CONFIG_NMT (CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #define CO_CONFIG_NMT_CALLBACK_CHANGE 0x01 #define CO_CONFIG_NMT_MASTER 0x02 @@ -129,11 +144,6 @@ extern "C" { * Configuration of @ref CO_HBconsumer * * Possible flags, can be ORed: - * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing - * received heartbeat CAN message. - * Callback is configured by CO_HBconsumer_initCallbackPre(). - * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable - * inside CO_HBconsumer_process(). * - CO_CONFIG_HB_CONS_ENABLE - Enable heartbeat consumer. * - CO_CONFIG_HB_CONS_CALLBACK_CHANGE - Enable custom common callback after NMT * state of the monitored node changes. Callback is configured by @@ -146,11 +156,16 @@ extern "C" { * CO_HBconsumer_initCallbackRemoteReset() functions. * - CO_CONFIG_HB_CONS_QUERY_FUNCT - Enable functions for query HB state or * NMT state of the specific monitored node. + * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing + * received heartbeat CAN message. + * Callback is configured by CO_HBconsumer_initCallbackPre(). + * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable + * inside CO_HBconsumer_process(). * Note that CO_CONFIG_HB_CONS_CALLBACK_CHANGE and * CO_CONFIG_HB_CONS_CALLBACK_MULTI cannot be set simultaneously. */ #ifdef CO_DOXYGEN -#define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE) +#define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #define CO_CONFIG_HB_CONS_ENABLE 0x01 #define CO_CONFIG_HB_CONS_CALLBACK_CHANGE 0x02 @@ -194,7 +209,7 @@ extern "C" { * inside CO_EM_process(). */ #ifdef CO_DOXYGEN -#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY) +#define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #define CO_CONFIG_EM_PRODUCER 0x01 #define CO_CONFIG_EM_PROD_CONFIGURABLE 0x02 @@ -326,7 +341,7 @@ extern "C" { * servers (Writing to object 0x1201+ re-configures the additional server). */ #ifdef CO_DOXYGEN -#define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED) +#define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #define CO_CONFIG_SDO_SRV_SEGMENTED 0x02 #define CO_CONFIG_SDO_SRV_BLOCK 0x04 @@ -401,9 +416,11 @@ extern "C" { * - #CO_CONFIG_FLAG_CALLBACK_PRE - Enable custom callback after preprocessing * received TIME CAN message. * Callback is configured by CO_TIME_initCallbackPre(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration - writing to + * object 0x1012 enables / disables time producer or consumer. */ #ifdef CO_DOXYGEN -#define CO_CONFIG_TIME (0) +#define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #define CO_CONFIG_TIME_ENABLE 0x01 #define CO_CONFIG_TIME_PRODUCER 0x02 @@ -425,9 +442,10 @@ extern "C" { * Callback is configured by CO_SYNC_initCallbackPre(). * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable * inside CO_SYNC_process(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of SYNC. */ #ifdef CO_DOXYGEN -#define CO_CONFIG_SYNC (CO_CONFIG_SYNC_ENABLE | CO_CONFIG_SYNC_PRODUCER) +#define CO_CONFIG_SYNC (CO_CONFIG_SYNC_ENABLE | CO_CONFIG_SYNC_PRODUCER | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #define CO_CONFIG_SYNC_ENABLE 0x01 #define CO_CONFIG_SYNC_PRODUCER 0x02 @@ -448,9 +466,10 @@ extern "C" { * Callback is configured by CO_RPDO_initCallbackPre(). * - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable * inside CO_TPDO_process(). + * - #CO_CONFIG_FLAG_OD_DYNAMIC - Enable dynamic configuration of PDO. */ #ifdef CO_DOXYGEN -#define CO_CONFIG_PDO (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE | CO_CONFIG_PDO_SYNC_ENABLE) +#define CO_CONFIG_PDO (CO_CONFIG_RPDO_ENABLE | CO_CONFIG_TPDO_ENABLE | CO_CONFIG_PDO_SYNC_ENABLE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #define CO_CONFIG_RPDO_ENABLE 0x01 #define CO_CONFIG_TPDO_ENABLE 0x02 @@ -460,6 +479,24 @@ extern "C" { /** @} */ /* CO_STACK_CONFIG_SYNC_PDO */ +/** + * @defgroup CO_STACK_CONFIG_STORAGE Data storage + * Data storage with CANopen OD objects 1010 and 1011 + * @{ + */ +/** + * Configuration of @ref CO_storage + * + * Possible flags, can be ORed: + * - CO_CONFIG_STORAGE_ENABLE - Enable data storage + */ +#ifdef CO_DOXYGEN +#define CO_CONFIG_STORAGE (CO_CONFIG_STORAGE_ENABLE) +#endif +#define CO_CONFIG_STORAGE_ENABLE 0x01 +/** @} */ /* CO_STACK_CONFIG_STORAGE */ + + /** * @defgroup CO_STACK_CONFIG_LEDS CANopen LED diodes * Specified in standard CiA 303-3 @@ -474,7 +511,7 @@ extern "C" { * inside CO_NMT_process(). */ #ifdef CO_DOXYGEN -#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE) +#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE | CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #define CO_CONFIG_LEDS_ENABLE 0x01 /** @} */ /* CO_STACK_CONFIG_LEDS */ @@ -554,7 +591,7 @@ extern "C" { * Callback is configured by CO_LSSmaster_initCallbackPre(). */ #ifdef CO_DOXYGEN -#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE) +#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) #endif #define CO_CONFIG_LSS_SLAVE 0x01 #define CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND 0x02 diff --git a/301/CO_driver.h b/301/CO_driver.h index 84841f973..0605ae8c3 100644 --- a/301/CO_driver.h +++ b/301/CO_driver.h @@ -35,13 +35,24 @@ extern "C" { #endif -#ifdef CO_DEBUG_COMMON -#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_CLIENT - #define CO_DEBUG_SDO_CLIENT(msg) CO_DEBUG_COMMON(msg) +/* Stack configuration default global values. + * For more information see file CO_config.h. */ +#ifndef CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE + #define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE (0) +#endif +#ifndef CO_CONFIG_GLOBAL_FLAG_TIMERNEXT + #define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT (0) #endif -#if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_SERVER - #define CO_DEBUG_SDO_SERVER(msg) CO_DEBUG_COMMON(msg) +#ifndef CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC + #define CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC CO_CONFIG_FLAG_OD_DYNAMIC #endif +#ifdef CO_DEBUG_COMMON + #if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_CLIENT + #define CO_DEBUG_SDO_CLIENT(msg) CO_DEBUG_COMMON(msg) + #endif + #if (CO_CONFIG_DEBUG) & CO_CONFIG_DEBUG_SDO_SERVER + #define CO_DEBUG_SDO_SERVER(msg) CO_DEBUG_COMMON(msg) + #endif #endif /** @@ -110,8 +121,7 @@ extern "C" { * @defgroup CO_dataTypes Basic definitions * @{ * - * Target specific basic definitions and data types according to Misra C - * specification. + * Target specific basic definitions and data types. * * Must be defined in the **CO_driver_target.h** file. * @@ -138,7 +148,7 @@ extern "C" { /** Logical false, for general use */ #define false 0 /** Boolean data type for general use */ -typedef unsigned char bool_t; +typedef uint_fast8_t bool_t; /** INTEGER8 in CANopen (0002h), 8-bit signed integer */ typedef signed char int8_t; /** INTEGER16 in CANopen (0003h), 16-bit signed integer */ @@ -338,7 +348,6 @@ typedef struct { volatile uint16_t CANtxCount; /**< Number of messages in transmit buffer, which are waiting to be copied to the CAN module */ uint32_t errOld; /**< Previous state of CAN errors */ - int32_t errinfo; /**< For use with @ref CO_errinfo() */ } CO_CANmodule_t; @@ -408,24 +417,6 @@ typedef struct { #endif /* CO_DOXYGEN */ -/** Macro for passing additional information about error. - * - * This macro is called from several CANopen init functions, which returns - * @ref CO_ReturnError_t. - * - * CO_driver_target.h may implement this macro. Usually macro only sets - * CANmodule->errinfo to err. Application may then use CANmodule->errinfo to - * determine the reason of failure. errinfo must be type of int32_t. By default - * macro does not record anything. - * - * CO_errinfo is called in following @ref CO_ReturnError_t reasons: - * - 'CO_ERROR_OD_PARAMETERS' - Index of erroneous OD parameter. - */ -#ifndef CO_errinfo -#define CO_errinfo(CANmodule, err) CANmodule->errinfo = err -#endif - - /** * Default CANopen identifiers. * diff --git a/303/CO_LEDs.h b/303/CO_LEDs.h index 75364f880..d8937f919 100644 --- a/303/CO_LEDs.h +++ b/303/CO_LEDs.h @@ -31,7 +31,8 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_LEDS -#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE) +#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #if ((CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE) || defined CO_DOXYGEN diff --git a/305/CO_LSS.h b/305/CO_LSS.h index b1d627484..70d6ebb3f 100644 --- a/305/CO_LSS.h +++ b/305/CO_LSS.h @@ -31,7 +31,8 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_LSS -#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE) +#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE \ + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) #endif #if ((CO_CONFIG_LSS) & (CO_CONFIG_LSS_SLAVE | CO_CONFIG_LSS_MASTER)) || defined CO_DOXYGEN diff --git a/Makefile b/Makefile index 12a5fc03d..bbf88e462 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,7 @@ DRV_SRC = socketCAN CANOPEN_SRC = . -OD_SRC = example -APPL_SRC = socketCAN +APPL_SRC = example LINK_TARGET = canopend @@ -13,7 +12,6 @@ LINK_TARGET = canopend INCLUDE_DIRS = \ -I$(DRV_SRC) \ -I$(CANOPEN_SRC) \ - -I$(OD_SRC) \ -I$(APPL_SRC) @@ -22,14 +20,14 @@ SOURCES = \ $(DRV_SRC)/CO_error.c \ $(DRV_SRC)/CO_epoll_interface.c \ $(DRV_SRC)/CO_OD_storage.c \ - $(CANOPEN_SRC)/301/CO_SDOserver.c \ - $(CANOPEN_SRC)/301/CO_Emergency.c \ $(CANOPEN_SRC)/301/CO_NMT_Heartbeat.c \ $(CANOPEN_SRC)/301/CO_HBconsumer.c \ + $(CANOPEN_SRC)/301/CO_Emergency.c \ + $(CANOPEN_SRC)/301/CO_SDOserver.c \ + $(CANOPEN_SRC)/301/CO_SDOclient.c \ + $(CANOPEN_SRC)/301/CO_TIME.c \ $(CANOPEN_SRC)/301/CO_SYNC.c \ $(CANOPEN_SRC)/301/CO_PDO.c \ - $(CANOPEN_SRC)/301/CO_TIME.c \ - $(CANOPEN_SRC)/301/CO_SDOclient.c \ $(CANOPEN_SRC)/301/crc16-ccitt.c \ $(CANOPEN_SRC)/301/CO_fifo.c \ $(CANOPEN_SRC)/303/CO_LEDs.c \ @@ -40,17 +38,24 @@ SOURCES = \ $(CANOPEN_SRC)/309/CO_gateway_ascii.c \ $(CANOPEN_SRC)/extra/CO_trace.c \ $(CANOPEN_SRC)/CANopen.c \ - $(OD_SRC)/CO_OD.c \ - $(APPL_SRC)/CO_main_basic.c + $(APPL_SRC)/CO_OD.c \ + $(DRV_SRC)/CO_main_basic.c OBJS = $(SOURCES:%.c=%.o) CC ?= gcc -OPT = -g -DCO_VERSION_MAJOR=2 -#OPT = -g -DCO_VERSION_MAJOR=2 -DCO_SINGLE_THREAD +OPT = +OPT += -DCO_VERSION_MAJOR=2 +OPT += -DCO_SINGLE_THREAD +OPT += -g +#OPT += -Wextra -Wshadow -pedantic -fanalyzer +#OPT += -DCO_USE_GLOBALS CFLAGS = -Wall $(OPT) $(INCLUDE_DIRS) -LDFLAGS = -pthread -#LDFLAGS = +LDFLAGS = +LDFLAGS += -g +LDFLAGS += -pthread + +#Options can be also passed via make: 'make OPT="-g" LDFLAGS="-pthread"' .PHONY: all clean diff --git a/README.md b/README.md index e8246108e..86fd87d51 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ documentation [online](https://canopennode.github.io/CANopenSocket/). Report issues on https://github.com/CANopenNode/CANopenNode/issues -Join discussion on Slack: https://canopennode.slack.com/ +For discussion on [Slack](https://canopennode.slack.com/) see: https://github.com/robincornelius/libedssharp Older discussion group is on Sourceforge: http://sourceforge.net/p/canopennode/discussion/387151/ diff --git a/doc/objectDictionary.md b/doc/objectDictionary.md index 61765d5d7..3d267e316 100644 --- a/doc/objectDictionary.md +++ b/doc/objectDictionary.md @@ -23,21 +23,15 @@ The term **OD variable** is basic variable of specified type. For example: int8_ The term **OD entry** means structure element, which contains some basic properties of the OD object, indication of type of OD object and pointer to all necessary data for the OD object. An array of OD entries together with information about total number of OD entries represents object dictionary as defined inside CANopenNode. See @ref OD_entry_t and @ref OD_t. ### Access -Application and the stack have access to OD objects via universal @ref OD_t object and @ref OD_find() function. No direct access to custom structures, which define object dictionary, is required. Properties for specific OD variable is fetched with @ref OD_getSub() function. Access to actual variable is via **read** and **write** functions. Pointer to those two functions is fetched by @ref OD_getSub(). See @ref OD_stream_t and @ref OD_subEntry_t. See also shortcuts: @ref CO_ODgetSetters, for access to data of different type. - -### Optional extensions -There are some optional extensions to the object dictionary: - * **PDO flags** informs application, if specific OD variable was received or sent by PDO. And also gives the application ability to request a TPDO, to which variable is possibly mapped. - * **IO extension** gives the application ability to take full control over the OD object. Application can specify own **read** and **write** functions and own object, on which they operate. +Application and the stack have access to OD objects via universal @ref OD_t object and @ref OD_find() function. No direct access to custom structures, which define object dictionary, is required. Properties for specific OD variable is fetched with @ref OD_getSub() function. Access to actual variable is via **read** and **write** functions. Pointer to those two functions is fetched by @ref OD_getSub(). See @ref OD_stream_t. See also shortcuts: @ref CO_ODgetSetters, for access to data of different type. ### Example usage ```c -extern const OD_t ODxyz; +extern OD_t *ODxyz; -void myFunc(const OD_t *od) { +void myFunc(OD_t *od) { ODR_t ret; - const OD_entry_t *entry; - OD_subEntry_t subEntry; + OD_entry_t *entry; OD_IO_t io1008; char buf[50]; OD_size_t bytesRd; @@ -45,7 +39,7 @@ void myFunc(const OD_t *od) { /* Init IO for "Manufacturer device name" at index 0x1008, sub-index 0x00 */ entry = OD_find(od, 0x1008); - ret = OD_getSub(entry, 0x00, &subEntry, &io1008, false); + ret = OD_getSub(entry, 0x00, &io1008, false); /* Read with io1008, subindex = 0x00 */ if (ret == ODR_OK) bytesRd = io1008.read(&io1008.stream, 0x00, &buf[0], sizeof(buf), &ret); @@ -66,14 +60,14 @@ If access to the same variable is very frequent, it is better to use first examp Some simple user applications can also access some OD variables directly via globals. @warning -If OD object has IO extension enabled, then direct access to its OD variables must not be used. Only valid access is via read or write or helper functions. +If OD object has OD extension enabled, then direct access to its OD variables must not be used. Only valid access is via read or write or helper functions. ```c #include ODxyz.h void myFuncGlob(void) { //Direct address instead of OD_find() - const OD_entry_t *entry_errReg = ODxyz_1001_errorRegister; + OD_entry_t *entry_errReg = ODxyz_1001_errorRegister; //Direct access to OD variable uint32_t devType = ODxyz_0.x1000_deviceType; @@ -119,18 +113,18 @@ typedef struct { extern ODxyz_PERSIST_COMM_t ODxyz_PERSIST_COMM; extern ODxyz_RAM_t ODxyz_RAM; -extern const OD_t ODxyz; +extern OD_t *ODxyz; /* Object dictionary entries - shortcuts **************************************/ -#define ODxyz_ENTRY_H1000 &ODxyz.list[0] -#define ODxyz_ENTRY_H1001 &ODxyz.list[1] -#define ODxyz_ENTRY_H1003 &ODxyz.list[2] -#define ODxyz_ENTRY_H1018 &ODxyz.list[3] - -#define ODxyz_ENTRY_H1000_deviceType &ODxyz.list[0] -#define ODxyz_ENTRY_H1001_errorRegister &ODxyz.list[1] -#define ODxyz_ENTRY_H1003_preDefinedErrorField &ODxyz.list[2] -#define ODxyz_ENTRY_H1018_identity &ODxyz.list[3] +#define ODxyz_ENTRY_H1000 &ODxyz->list[0] +#define ODxyz_ENTRY_H1001 &ODxyz->list[1] +#define ODxyz_ENTRY_H1003 &ODxyz->list[2] +#define ODxyz_ENTRY_H1018 &ODxyz->list[3] + +#define ODxyz_ENTRY_H1000_deviceType &ODxyz->list[0] +#define ODxyz_ENTRY_H1001_errorRegister &ODxyz->list[1] +#define ODxyz_ENTRY_H1003_preDefinedErrorField &ODxyz->list[2] +#define ODxyz_ENTRY_H1018_identity &ODxyz->list[3] ``` ### Example ODxyz.c file @@ -141,13 +135,13 @@ extern const OD_t ODxyz; /* OD data initialization of all groups ***************************************/ ODxyz_PERSIST_COMM_t ODxyz_PERSIST_COMM = { - .x1000_deviceType = 0L, + .x1000_deviceType = 0, .x1018_identity = { .maxSubIndex = 4, - .vendorID = 0L, - .productCode = 0L, - .revisionNumber = 0L, - .serialNumber = 0L + .vendorID = 0, + .productCode = 0, + .revisionNumber = 0, + .serialNumber = 0 } }; @@ -157,46 +151,33 @@ ODxyz_RAM_t ODxyz_RAM = { .x1003_preDefinedErrorField = {0, 0, 0, 0, 0, 0, 0, 0} }; -/* IO extensions and flagsPDO (configurable by application) *******************/ -typedef struct { - OD_extensionIO_t xio_1003_preDefinedErrorField; -} ODxyzExts_t; - -static ODxyzExts_t ODxyzExts = {0}; - /* All OD objects (constant) **************************************************/ typedef struct { OD_obj_var_t o_1000_deviceType; OD_obj_var_t o_1001_errorRegister; OD_obj_array_t o_1003_preDefinedErrorField; - OD_obj_extended_t oE_1003_preDefinedErrorField; OD_obj_record_t o_1018_identity[5]; } ODxyzObjs_t; -static const ODxyzObjs_t ODxyzObjs = { +static CO_PROGMEM ODxyzObjs_t ODxyzObjs = { .o_1000_deviceType = { - .data = &ODxyz_PERSIST_COMM.x1000_deviceType, + .dataOrig = &ODxyz_PERSIST_COMM.x1000_deviceType, .attribute = ODA_SDO_R | ODA_MB, .dataLength = 4 }, .o_1001_errorRegister = { - .data = &ODxyz_RAM.x1001_errorRegister, + .dataOrig = &ODxyz_RAM.x1001_errorRegister, .attribute = ODA_SDO_R, .dataLength = 1 }, .o_1003_preDefinedErrorField = { - .data0 = &ODxyz_RAM.x1003_preDefinedErrorField_sub0, - .data = &ODxyz_RAM.x1003_preDefinedErrorField[0], + .dataOrig0 = &ODxyz_RAM.x1003_preDefinedErrorField_sub0, + .dataOrig = &ODxyz_RAM.x1003_preDefinedErrorField[0], .attribute0 = ODA_SDO_RW, .attribute = ODA_SDO_R | ODA_MB, .dataElementLength = 4, .dataElementSizeof = sizeof(uint32_t) }, - .oE_1003_preDefinedErrorField = { - .extIO = &ODxyzExts.xio_1003_preDefinedErrorField, - .flagsPDO = NULL, - .odObjectOriginal = &ODxyzObjs.o_1003_preDefinedErrorField - }, .o_1018_identity = { { .data = &ODxyz_PERSIST_COMM.x1018_identity.maxSubIndex, @@ -232,18 +213,20 @@ static const ODxyzObjs_t ODxyzObjs = { }; /* Object dictionary **********************************************************/ -static const OD_entry_t ODxyzList[] = { - {0x1000, 1, ODT_VAR, &ODxyzObjs.o_1000_deviceType}, - {0x1001, 1, ODT_VAR, &ODxyzObjs.o_1001_errorRegister}, - {0x1003, 9, ODT_EVAR, &ODxyzObjs.oE_1003_preDefinedErrorField}, - {0x1018, 5, ODT_REC, &ODxyzObjs.o_1018_identity}, - {0x0000, 0, 0, NULL} +static OD_entry_t ODxyzList[] = { + {0x1000, 0x01, ODT_VAR, &ODxyzObjs.o_1000_deviceType, NULL}, + {0x1001, 0x01, ODT_VAR, &ODxyzObjs.o_1001_errorRegister, NULL}, + {0x1003, 0x09, ODT_VAR, &ODxyzObjs.o_1003_preDefinedErrorField, NULL}, + {0x1018, 0x05, ODT_REC, &ODxyzObjs.o_1018_identity, NULL}, + {0x0000, 0x00, 0, NULL, NULL} }; -const OD_t ODxyz = { +OD_t _ODxyz = { (sizeof(ODxyzList) / sizeof(ODxyzList[0])) - 1, &ODxyzList[0] }; + +OD_t *ODxyz = &_ODxyz; ``` @@ -291,7 +274,6 @@ There are also device configuration files with "XDC" extension. They are describ - @@ -409,13 +391,11 @@ If "uniqueIDRef" attribute is not specified and "objectType" is 7(VAR), then "CA * "noAccess" - object will be in object dictionary, but no access. * <**label** lang="en">, <**description** lang="en"> (required) - several elements in multiple languages possible * <**INT and similar**/> (required) - Basic or complex data type. Basic data type (for VAR) is specified in IEC 61131-3 (see below). If data type is complex (ARRAY or RECORD), then **<dataTypeIDRef>** must be specified and entry must be added in the **<dataTypeList>**. Such definition of complex data types is required by the standard, but it is not required by CANopenNode. - * <**defaultValue**> (optional for VAR) - Default value for the variable. If it is empty string, then data is not stored inside object dictionary. Application should provide own data via IO extension. + * <**defaultValue**> (optional for VAR) - Default value for the variable. If it is empty string, then data is not stored inside object dictionary. Application should provide own data via OD extension. * <**property** name="..." value="..."> (optional) - Multiple elements may contain additional custom properties. CANopenNode uses custom properties with following names: * "**CO_disabled**" (used for base OD entry) - Valid value is "false" (default) or "true". if OD entry is disabled, then it will not be present in generated Object Dictionary .h and .c files. * "**CO_countLabel**" (used for base OD entry) - Valid value is any string without spaces. OD exporter will generate a macro for each different string. For example, if four OD objects have "CO_countLabel" set to "TPDO", then macro "#define ODxyz_CNT_TPDO 4" will be generated by OD exporter. "CO_countLabel" is not required for configuration with multiple object dictionaries (CO_MULTIPLE_OD), although may be useful. Default is "". * "**CO_storageGroup**" (used for base OD entry) - group name (string) into which the C variable will belong. Variables from specific storage group may then be stored into non-volatile memory, automatically or by SDO command. Default is "RAM". Please note: if <defaultValue> is empty string at specific subindex, then no storage group will be used for that variable. - * "**CO_extensionIO**" (used for base OD entry) - Valid value is "false" (default) or "true", if IO extension is enabled. - * "**CO_flagsPDO**" (used for base OD entry) - Valid value is "false" (default) or "true", if PDO flags are enabled. * "**CO_accessSRDO**" (used for each "VAR") - Valid values are: "tx", "rx", "trx", "no"(default). * "**CO_stringLengthMin**" (used for each "VAR") - Minimum length of the string. Used with "VISIBLE_STRING" and "UNICODE_STRING". If CO_stringLengthMin is smaller than length of the string in <defaultValue>, then it is ignored. Byte length of unicode string is 2 * CO_stringLengthMin. If <defaultValue> is empty and CO_stringLengthMin is 0, then data is not stored inside object dictionary. Default is 0. @@ -464,62 +444,56 @@ Other elements listed in the above XML example are required by the standard. The Object Dictionary Requirements By CANopenNode {#object-dictionary-requirements-by-canopennode} ---------------------------------------------------------------------------------------------- * **Used by** column indicates CANopenNode object or its part, which uses the OD object. It also indicates, if OD object is required or optional for actual configuration. For the configuration of the CANopenNode objects see [Stack configuration](301/CO_config.h). If CANopenNode object or its part is disabled in stack configuration, then OD object is not used. Note that OD objects: 1000, 1001 and 1017 and 1018 are mandatory for CANopen. -* **CO_extensionIO** column indicates, if OD object must have property "CO_extensionIO" set to true: - * "no" - no IO extension used - * "optional" - If IO extension is enabled, then writing to the OD parameter will reflect in CANopen run time, otherwise reset communication is required for changes to take effect. - * "required" - IO extension is required on OD object and init function will return error if not enabled. - * "req if DYNAMIC" - IO extension is required on OD object if CO_CONFIG_FLAG_OD_DYNAMIC is set. - * "yes, own data" - OD object don't need own data and IO extension is necessary for OD object to work. Init function will not return error, if OD object does not exist or doesn't have IO extension enabled. * **CO_countLabel** column indicates, which value must have property "CO_countLabel" inside OD object. -| index | Description | Used by | CO_extensionIO | CO_countLabel | -| ----- | ----------------------------- | ---------------------| -------------- | ------------- | -| 1000 | Device type | CANopen, req | no | NMT | -| 1001 | Error register | CANopen, EM, req | no | EM | -| 1002 | Manufacturer status register | | | | -| 1003 | Pre-defined error field | EM_HISTORY, opt | yes, own data | | -| 1005 | COB-ID SYNC message | SYNC, req | req if DYNAMIC | SYNC | -| 1006 | Communication cycle period | SYNC_PRODUCER, req | opt | SYNC_PROD | -| 1007 | Synchronous window length | SYNC, opt | opt | | -| 1008 | Manufacturer device name | | | | -| 1009 | Manufacturer hardware version | | | | -| 100A | Manufacturer software version | | | | -| 100C | Guard time | | | | -| 100D | Life time factor | | | | -| 1010 | Store parameters | | | | -| 1011 | Restore default parameters | | | | -| 1012 | COB-ID time stamp object | TIME, req | required | TIME | -| 1013 | High resolution time stamp | | | | -| 1014 | COB-ID EMCY | EM_PRODUCER, req | required | EM_PROD | -| 1015 | Inhibit time EMCY | EM_PROD_INHIBIT, opt | optional | | -| 1016 | Consumer heartbeat time | HB_CONS, req | optional | HB_CONS | -| 1017 | Producer heartbeat time | CANopen, NMT, req | required | HB_PROD | -| 1018 | Identity object | CANopen, LSS_SL, req | no | | -| 1019 | Synch. counter overflow value | SYNC, opt | no | | -| 1020 | Verify configuration | | | | -| 1021 | Store EDS | | | | -| 1022 | Store format | | | | -| 1023 | OS command | | | | -| 1024 | OS command mode | | | | -| 1025 | OS debugger interface | | | | -| 1026 | OS prompt | | | | -| 1027 | Module list | | | | -| 1028 | Emergency consumer object | | | | -| 1029 | Error behavior object | | | | -| 1200 | SDO server parameter (first) | SDO optional | required | SDO_SRV | -| 1201+ | SDO server parameter | SDO+, req | req if DYNAMIC | SDO_SRV | -| 1280+ | SDO client parameter | SDO_CLI, req | req if DYNAMIC | SDO_CLI | -| 1300 | Global fail-safe command par | GFC, req | | GFC | -| 1301+ | SRDO communication parameter | SRDO, req | | SRDO | -| 1381+ | SRDO mapping parameter | SRDO, req | | | -| 13FE | Configuration valid | SRDO, req | | | -| 13FF | Safety configuration checksum | SRDO, req | | | -| 1400+ | RPDO communication parameter | RPDO, req | req if DYNAMIC | RPDO | -| 1600+ | RPDO mapping parameter | RPDO, req | req if DYNAMIC | | -| 1800+ | TPDO communication parameter | TPDO, req | req if DYNAMIC | TPDO | -| 1A00+ | TPDO mapping parameter | TPDO, req | req if DYNAMIC | | -| 1FA0+ | Object scanner list | | | | -| 1FD0+ | Object dispatching list | | | | -| | Custom objects | | | | -| any | Error status bits | EM_STATUS_BITS, opt | yes, own data | | -| any+ | Trace | TRACE, req | yes, own data | TRACE | +| index | Description | Used by | CO_countLabel | +| ----- | ----------------------------- | ---------------------| ------------- | +| 1000 | Device type | CANopen, req | NMT | +| 1001 | Error register | CANopen, EM, req | EM | +| 1002 | Manufacturer status register | | | +| 1003 | Pre-defined error field | EM_HISTORY, opt | | +| 1005 | COB-ID SYNC message | SYNC, req | SYNC | +| 1006 | Communication cycle period | SYNC_PRODUCER, req | SYNC_PROD | +| 1007 | Synchronous window length | SYNC, opt | | +| 1008 | Manufacturer device name | | | +| 1009 | Manufacturer hardware version | | | +| 100A | Manufacturer software version | | | +| 100C | Guard time | | | +| 100D | Life time factor | | | +| 1010 | Store parameters | | STORAGE | +| 1011 | Restore default parameters | | | +| 1012 | COB-ID time stamp object | TIME, req | TIME | +| 1013 | High resolution time stamp | | | +| 1014 | COB-ID EMCY | EM_PRODUCER, req | EM_PROD | +| 1015 | Inhibit time EMCY | EM_PROD_INHIBIT, opt | | +| 1016 | Consumer heartbeat time | HB_CONS, req | HB_CONS | +| 1017 | Producer heartbeat time | CANopen, NMT, req | HB_PROD | +| 1018 | Identity object | CANopen, LSS_SL, req | | +| 1019 | Synch. counter overflow value | SYNC, opt | | +| 1020 | Verify configuration | | | +| 1021 | Store EDS | | | +| 1022 | Store format | | | +| 1023 | OS command | | | +| 1024 | OS command mode | | | +| 1025 | OS debugger interface | | | +| 1026 | OS prompt | | | +| 1027 | Module list | | | +| 1028 | Emergency consumer object | | | +| 1029 | Error behavior object | | | +| 1200 | SDO server parameter (first) | SDO optional | SDO_SRV | +| 1201+ | SDO server parameter | SDO+, req | SDO_SRV | +| 1280+ | SDO client parameter | SDO_CLI, req | SDO_CLI | +| 1300 | Global fail-safe command par | GFC, req | GFC | +| 1301+ | SRDO communication parameter | SRDO, req | SRDO | +| 1381+ | SRDO mapping parameter | SRDO, req | | +| 13FE | Configuration valid | SRDO, req | | +| 13FF | Safety configuration checksum | SRDO, req | | +| 1400+ | RPDO communication parameter | RPDO, req | RPDO | +| 1600+ | RPDO mapping parameter | RPDO, req | | +| 1800+ | TPDO communication parameter | TPDO, req | TPDO | +| 1A00+ | TPDO mapping parameter | TPDO, req | | +| 1FA0+ | Object scanner list | | | +| 1FD0+ | Object dispatching list | | | +| | Custom objects | | | +| any | Error status bits | EM_STATUS_BITS, opt | | +| any+ | Trace | TRACE, req | TRACE | diff --git a/example/CO_driver_target.h b/example/CO_driver_target.h index 6aac0c532..8f18ac853 100644 --- a/example/CO_driver_target.h +++ b/example/CO_driver_target.h @@ -28,7 +28,7 @@ /* This file contains device and application specific definitions. * It is included from CO_driver.h, which contains documentation - * for definitions below. */ + * for common definitions below. */ #include #include @@ -42,38 +42,40 @@ extern "C" { #endif -/* Stack configuration override default values. +/* Stack configuration override default values (all enabled in this example). * For more information see file CO_config.h. */ +#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE CO_CONFIG_FLAG_CALLBACK_PRE +#define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT CO_CONFIG_FLAG_TIMERNEXT + #ifndef CO_CONFIG_NMT #define CO_CONFIG_NMT (CO_CONFIG_NMT_CALLBACK_CHANGE | \ CO_CONFIG_NMT_MASTER | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_HB_CONS #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | \ CO_CONFIG_HB_CONS_CALLBACK_MULTI | \ CO_CONFIG_HB_CONS_QUERY_FUNCT | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_EM #define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | \ CO_CONFIG_EM_HISTORY | \ CO_CONFIG_EM_CONSUMER | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT | \ - CO_CONFIG_FLAG_OD_DYNAMIC) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_SDO_SRV #define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED | \ CO_CONFIG_SDO_SRV_BLOCK | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT | \ - CO_CONFIG_FLAG_OD_DYNAMIC) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_SDO_SRV_BUFFER_SIZE @@ -85,22 +87,24 @@ extern "C" { CO_CONFIG_SDO_CLI_SEGMENTED | \ CO_CONFIG_SDO_CLI_BLOCK | \ CO_CONFIG_SDO_CLI_LOCAL | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT | \ - CO_CONFIG_FLAG_OD_DYNAMIC) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_TIME #define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | \ CO_CONFIG_TIME_PRODUCER | \ - CO_CONFIG_FLAG_CALLBACK_PRE) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_SYNC #define CO_CONFIG_SYNC (CO_CONFIG_SYNC_ENABLE | \ CO_CONFIG_SYNC_PRODUCER | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_PDO @@ -109,13 +113,8 @@ extern "C" { CO_CONFIG_PDO_SYNC_ENABLE | \ CO_CONFIG_RPDO_CALLS_EXTENSION | \ CO_CONFIG_TPDO_CALLS_EXTENSION | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT) -#endif - -#ifndef CO_CONFIG_LEDS -#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_GFC @@ -129,8 +128,8 @@ extern "C" { CO_CONFIG_SRDO_CHECK_TX | \ CO_CONFIG_RSRDO_CALLS_EXTENSION | \ CO_CONFIG_TSRDO_CALLS_EXTENSION | \ - CO_CONFIG_FLAG_CALLBACK_PRE | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_SRDO_MINIMUM_DELAY @@ -141,7 +140,7 @@ extern "C" { #define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | \ CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND | \ CO_CONFIG_LSS_MASTER | \ - CO_CONFIG_FLAG_CALLBACK_PRE) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) #endif #ifndef CO_CONFIG_GTW @@ -183,7 +182,7 @@ extern "C" { /* 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 uint_fast8_t bool_t; typedef float float32_t; typedef double float64_t; typedef char char_t; @@ -227,7 +226,6 @@ typedef struct { volatile bool_t firstCANtxMessage; volatile uint16_t CANtxCount; uint32_t errOld; - int32_t errinfo; } CO_CANmodule_t; diff --git a/socketCAN/CO_driver_target.h b/socketCAN/CO_driver_target.h index ee2d25db4..3cfc8c3ba 100644 --- a/socketCAN/CO_driver_target.h +++ b/socketCAN/CO_driver_target.h @@ -56,40 +56,40 @@ extern "C" { /* Stack configuration override default values. * For more information see file CO_config.h. */ #ifdef CO_SINGLE_THREAD -#define CO_CONFIG_FLAG_CALLBACK_PRE_USED 0 +#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE 0 #else -#define CO_CONFIG_FLAG_CALLBACK_PRE_USED CO_CONFIG_FLAG_CALLBACK_PRE +#define CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE CO_CONFIG_FLAG_CALLBACK_PRE #endif +#define CO_CONFIG_GLOBAL_FLAG_TIMERNEXT CO_CONFIG_FLAG_TIMERNEXT #ifndef CO_CONFIG_NMT #define CO_CONFIG_NMT (CO_CONFIG_NMT_CALLBACK_CHANGE | \ CO_CONFIG_NMT_MASTER | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_HB_CONS #define CO_CONFIG_HB_CONS (CO_CONFIG_HB_CONS_ENABLE | \ CO_CONFIG_HB_CONS_CALLBACK_CHANGE | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_EM #define CO_CONFIG_EM (CO_CONFIG_EM_PRODUCER | \ CO_CONFIG_EM_HISTORY | \ CO_CONFIG_EM_CONSUMER | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT | \ - CO_CONFIG_FLAG_OD_DYNAMIC) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_SDO_SRV #define CO_CONFIG_SDO_SRV (CO_CONFIG_SDO_SRV_SEGMENTED | \ CO_CONFIG_SDO_SRV_BLOCK | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT | \ - CO_CONFIG_FLAG_OD_DYNAMIC) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_SDO_SRV_BUFFER_SIZE @@ -101,22 +101,24 @@ extern "C" { CO_CONFIG_SDO_CLI_SEGMENTED | \ CO_CONFIG_SDO_CLI_BLOCK | \ CO_CONFIG_SDO_CLI_LOCAL | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT | \ - CO_CONFIG_FLAG_OD_DYNAMIC) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_TIME #define CO_CONFIG_TIME (CO_CONFIG_TIME_ENABLE | \ CO_CONFIG_TIME_PRODUCER | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_SYNC #define CO_CONFIG_SYNC (CO_CONFIG_SYNC_ENABLE | \ CO_CONFIG_SYNC_PRODUCER | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT | \ + CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC) #endif #ifndef CO_CONFIG_PDO @@ -125,20 +127,15 @@ extern "C" { CO_CONFIG_PDO_SYNC_ENABLE | \ CO_CONFIG_RPDO_CALLS_EXTENSION | \ CO_CONFIG_TPDO_CALLS_EXTENSION | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED | \ - CO_CONFIG_FLAG_TIMERNEXT) -#endif - -#ifndef CO_CONFIG_LEDS -#define CO_CONFIG_LEDS (CO_CONFIG_LEDS_ENABLE | \ - CO_CONFIG_FLAG_TIMERNEXT) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE | \ + CO_CONFIG_GLOBAL_FLAG_TIMERNEXT) #endif #ifndef CO_CONFIG_LSS #define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | \ CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND | \ CO_CONFIG_LSS_MASTER | \ - CO_CONFIG_FLAG_CALLBACK_PRE_USED) + CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) #endif #ifndef CO_CONFIG_GTW @@ -251,11 +248,10 @@ extern "C" { #define CO_SWAP_64(x) bswap_64(x) #endif #endif -/* #define CO_USE_LEDS */ /* 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 uint_fast8_t bool_t; typedef float float32_t; typedef double float64_t; typedef char char_t; @@ -341,7 +337,6 @@ typedef struct { CO_CANtx_t *txArray; uint16_t txSize; uint16_t CANerrorStatus; - int32_t errinfo; volatile bool_t CANnormal; int epoll_fd; /* File descriptor for epoll, which waits for CAN receive event */ diff --git a/socketCAN/CO_main_basic.c b/socketCAN/CO_main_basic.c index c3da3bf6c..94908e742 100644 --- a/socketCAN/CO_main_basic.c +++ b/socketCAN/CO_main_basic.c @@ -59,11 +59,6 @@ #include "CO_time_trace.h" #endif -/* Use DS309-3 standard - ASCII command interface to CANopen: NMT master, - * LSS master and SDO client */ -#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII -#include "309/CO_gateway_ascii.h" -#endif /* Interval of mainline and real-time thread in microseconds */ #ifndef MAIN_THREAD_INTERVAL_US @@ -73,16 +68,13 @@ #define TMR_THREAD_INTERVAL_US 1000 #endif +/* Configurable CAN bit-rate and CANopen node-id, store-able to non-volatile + * memory. Can be set by argument and changed by LSS slave. */ +typedef struct { uint16_t bitRate; uint8_t nodeId; } CO_pending_t; +CO_pending_t CO_pending = { .bitRate = 0, .nodeId = CO_LSS_NODE_ID_ASSIGNMENT }; +/* Active node-id, copied from CO_pending.nodeId in the communication reset */ +static uint8_t CO_activeNodeId = CO_LSS_NODE_ID_ASSIGNMENT; -/* Other variables and objects */ -#ifndef CO_SINGLE_THREAD -CO_epoll_t epRT; /* Epoll-timer object for realtime thread */ -static int rtPriority = -1; /* Real time priority, configurable by arguments. (-1=RT disabled) */ -#endif -static uint8_t CO_pendingNodeId = 0xFF;/* Use value from Object Dictionary or by arguments (set to 1..127 - * or unconfigured=0xFF). Can be changed by LSS slave. */ -static uint8_t CO_activeNodeId = 0xFF;/* Copied from CO_pendingNodeId in the communication reset section */ -static uint16_t CO_pendingBitRate = 0; /* CAN bitrate, not used here */ #if CO_OD_STORAGE == 1 static CO_OD_storage_t odStor; /* Object Dictionary storage object for CO_OD_ROM */ static CO_OD_storage_t odStorAuto; /* Object Dictionary storage object for CO_OD_EEPROM */ @@ -93,9 +85,11 @@ static char *odStorFile_eeprom = "od_storage_auto"; /* Name of t static CO_time_t CO_time; /* Object for current time */ #endif + /* Helper functions ***********************************************************/ #ifndef CO_SINGLE_THREAD /* Realtime thread */ +CO_epoll_t epRT; static void* rt_thread(void* arg); #endif @@ -245,6 +239,7 @@ int main (int argc, char *argv[]) { CO_epoll_t epMain; #ifndef CO_SINGLE_THREAD pthread_t rt_thread_id; + int rtPriority = -1; #endif CO_NMT_reset_cmd_t reset = CO_RESET_NOT; CO_ReturnError_t err; @@ -282,8 +277,8 @@ int main (int argc, char *argv[]) { while((opt = getopt(argc, argv, "i:p:rc:T:s:a:")) != -1) { switch (opt) { case 'i': - CO_pendingNodeId = (uint8_t)strtol(optarg, NULL, 0); nodeIdFromArgs = true; + CO_pending.nodeId = (uint8_t)strtol(optarg, NULL, 0); break; #ifndef CO_SINGLE_THREAD case 'p': rtPriority = strtol(optarg, NULL, 0); @@ -342,15 +337,15 @@ int main (int argc, char *argv[]) { if(!nodeIdFromArgs) { /* use value from Object dictionary, if not set by program arguments */ - CO_pendingNodeId = OD_CANNodeID; + CO_pending.nodeId = OD_CANNodeID; } - if((CO_pendingNodeId < 1 || CO_pendingNodeId > 127) + if((CO_pending.nodeId < 1 || CO_pending.nodeId > 127) #if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE - && CO_NO_LSS_SLAVE == 1 && CO_pendingNodeId != CO_LSS_NODE_ID_ASSIGNMENT + && CO_NO_LSS_SLAVE == 1 && CO_pending.nodeId != CO_LSS_NODE_ID_ASSIGNMENT #endif ) { - log_printf(LOG_CRIT, DBG_WRONG_NODE_ID, CO_pendingNodeId); + log_printf(LOG_CRIT, DBG_WRONG_NODE_ID, CO_pending.nodeId); printUsage(argv[0]); exit(EXIT_FAILURE); } @@ -370,7 +365,7 @@ int main (int argc, char *argv[]) { } - log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_pendingNodeId, "starting"); + log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_pending.nodeId, "starting"); /* Allocate memory for CANopen objects */ @@ -465,7 +460,7 @@ int main (int argc, char *argv[]) { continue; } - err = CO_LSSinit(&CO_pendingNodeId, &CO_pendingBitRate); + err = CO_LSSinit(&CO_pending.nodeId, &CO_pending.bitRate); if(err != CO_ERROR_NO) { log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_LSSinit()", err); programExit = EXIT_FAILURE; @@ -473,17 +468,11 @@ int main (int argc, char *argv[]) { continue; } - CO_activeNodeId = CO_pendingNodeId; + CO_activeNodeId = CO_pending.nodeId; err = CO_CANopenInit(CO_activeNodeId); if(err != CO_ERROR_NO && err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) { - if (err == CO_ERROR_OD_PARAMETERS) { - log_printf(LOG_CRIT, DBG_OD_ENTRY, - (uint16_t)CO->CANmodule[0]->errinfo); - } - else { - log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANopenInit()", err); - } + log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANopenInit()", err); programExit = EXIT_FAILURE; CO_endProgram = 1; continue; @@ -556,11 +545,11 @@ int main (int argc, char *argv[]) { #endif #ifdef CO_USE_APPLICATION /* Execute optional additional application code */ - uint16_t errinfo = 0; - err = app_programStart(!CO->nodeIdUnconfigured, &errinfo); + uint32_t errInfo = 0; + err = app_programStart(!CO->nodeIdUnconfigured, &errInfo); if(err != CO_ERROR_NO) { if (err == CO_ERROR_OD_PARAMETERS) { - log_printf(LOG_CRIT, DBG_OD_ENTRY, errinfo); + log_printf(LOG_CRIT, DBG_OD_ENTRY, errInfo); } else { log_printf(LOG_CRIT, DBG_CAN_OPEN, "app_programStart()", err); From 36f5b50ed9da15dc5d4606e9537bfd42721e15af Mon Sep 17 00:00:00 2001 From: Koen Dergent Date: Mon, 1 Feb 2021 11:47:49 +0100 Subject: [PATCH 19/21] Fix default CO_CONFIG_LSS value --- 305/CO_LSS.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/305/CO_LSS.h b/305/CO_LSS.h index 70d6ebb3f..dae96257b 100644 --- a/305/CO_LSS.h +++ b/305/CO_LSS.h @@ -31,7 +31,7 @@ /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_LSS -#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE \ +#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | \ CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE) #endif From acd719e27a1355470c90662c78c235b153a53dce Mon Sep 17 00:00:00 2001 From: Janez Date: Mon, 8 Feb 2021 16:57:36 +0100 Subject: [PATCH 20/21] Linux socketCAN driver, fix CANsend(). There was a problem with block transfer, when transmitting sequence of data. If CANsend() failed (socket buffer full), message was dropped. Now dropped message is marked with bufferFull flag and then it is re-transmitted later by the driver, as is in the original behaviour of CANopenNode. --- 301/CO_SDOclient.c | 4 +- doc/gettingStarted.md | 2 +- socketCAN/CO_driver.c | 98 ++++++++++++++++++++++++++++++++-- socketCAN/CO_driver_target.h | 3 +- socketCAN/CO_epoll_interface.c | 14 ++++- socketCAN/CO_error_msgs.h | 4 +- 6 files changed, 113 insertions(+), 12 deletions(-) diff --git a/301/CO_SDOclient.c b/301/CO_SDOclient.c index 215457c4a..8c790c8d3 100644 --- a/301/CO_SDOclient.c +++ b/301/CO_SDOclient.c @@ -630,7 +630,7 @@ CO_SDO_return_t CO_SDOclientDownload(CO_SDOclient_t *SDO_C, SDO_C->state = CO_SDO_ST_ABORT; } - /* Timeout timers *********************************************************/ + /* Timeout timers and transmit bufferFull flag ****************************/ if (ret == CO_SDO_RT_waitingResponse) { if (SDO_C->timeoutTimer < SDO_C->SDOtimeoutTime_us) { SDO_C->timeoutTimer += timeDifference_us; @@ -1263,7 +1263,7 @@ CO_SDO_return_t CO_SDOclientUpload(CO_SDOclient_t *SDO_C, SDO_C->state = CO_SDO_ST_ABORT; } - /* Timeout timers *********************************************************/ + /* Timeout timers and transmit bufferFull flag ****************************/ if (ret == CO_SDO_RT_waitingResponse) { if (SDO_C->timeoutTimer < SDO_C->SDOtimeoutTime_us) { SDO_C->timeoutTimer += timeDifference_us; diff --git a/doc/gettingStarted.md b/doc/gettingStarted.md index 984cec7f2..d5b40607a 100644 --- a/doc/gettingStarted.md +++ b/doc/gettingStarted.md @@ -174,7 +174,7 @@ Another interesting tool is [CANopen for Python](https://github.com/christiansan Examples here worked in virtual CAN interface, for simplicity. Virtual CAN runs inside Linux kernel only, it does not have much practical usability. If one has real CAN network configuration, then above examples are suitable also for this network, if Linux machine is connected to it and CAN interface is properly configured. When connecting your devices to real CAN network, make sure, you have at least two devices communicating, connected with ground and pair of wires, terminated with two 120ohm resistors, correct baudrate, etc. Accessing real CANopen devices is the same as described above for virtual CAN interface. Some tested USB to CAN interfaces, which are native in 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` + - Simple serial [USBtin](http://www.fischl.de/usbtin/) - Start with (-s5=250kbps): `sudo slcand -f -o -c -s5 /dev/ttyACM0 can0; sudo ip link set up can0` - [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). - Raspberry PI or similar has CAN capes available. diff --git a/socketCAN/CO_driver.c b/socketCAN/CO_driver.c index 8f22aae81..5a083d793 100644 --- a/socketCAN/CO_driver.c +++ b/socketCAN/CO_driver.c @@ -225,6 +225,8 @@ CO_ReturnError_t CO_CANmodule_init( CANmodule->txSize = txSize; CANmodule->CANerrorStatus = 0; CANmodule->CANnormal = false; + CANmodule->CANtxCount = 0; + #if CO_DRIVER_MULTI_INTERFACE > 0 for (i = 0; i < CO_CAN_MSG_SFF_MAX_COB_ID; i++) { CANmodule->rxIdentToIndex[i] = CO_INVALID_COB_ID; @@ -572,6 +574,7 @@ CO_ReturnError_t CO_CANtxBuffer_setInterface( } #endif /* CO_DRIVER_MULTI_INTERFACE */ +#if CO_DRIVER_MULTI_INTERFACE > 0 /* send CAN message ***********************************************************/ static CO_ReturnError_t CO_CANCheckSendInterface( @@ -702,6 +705,68 @@ CO_ReturnError_t CO_CANCheckSend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) return err; } +#warning CO_CANsend() is outdated for CO_DRIVER_MULTI_INTERFACE > 0 + +#endif /* CO_DRIVER_MULTI_INTERFACE > 0 */ + + +#if CO_DRIVER_MULTI_INTERFACE == 0 + +/* Change handling of tx buffer full in CO_CANsend(). Use CO_CANtx_t->bufferFull + * flag. Re-transmit undelivered message inside CO_CANmodule_process(). */ +CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) +{ + CO_ReturnError_t err = CO_ERROR_NO; + + if (CANmodule==NULL || buffer==NULL || CANmodule->CANinterfaceCount==0) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + CO_CANinterface_t *interface = &CANmodule->CANinterfaces[0]; + if (interface == NULL || interface->fd < 0) { + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + /* Verify overflow */ + if(buffer->bufferFull){ +#if CO_DRIVER_ERROR_REPORTING > 0 + interface->errorhandler.CANerrorStatus |= CO_CAN_ERRTX_OVERFLOW; +#endif + log_printf(LOG_ERR, DBG_CAN_TX_FAILED, buffer->ident, interface->ifName); + err = CO_ERROR_TX_OVERFLOW; + } + + errno = 0; + ssize_t n = send(interface->fd, buffer, CAN_MTU, MSG_DONTWAIT); + if (errno == 0 && n == CAN_MTU) { + /* success */ + if (buffer->bufferFull) { + buffer->bufferFull = false; + CANmodule->CANtxCount--; + } + } + else if (errno == EINTR || errno == EAGAIN || errno == ENOBUFS) { + /* Send failed, message will be re-sent by CO_CANmodule_process() */ + if (!buffer->bufferFull) { + buffer->bufferFull = true; + CANmodule->CANtxCount++; + } + err = CO_ERROR_TX_BUSY; + } + else { + /* Unknown error */ + log_printf(LOG_DEBUG, DBG_ERRNO, "send()"); +#if CO_DRIVER_ERROR_REPORTING > 0 + interface->errorhandler.CANerrorStatus |= CO_CAN_ERRTX_OVERFLOW; +#endif + err = CO_ERROR_SYSCALL; + } + + return err; +} + +#endif /* CO_DRIVER_MULTI_INTERFACE == 0 */ + /******************************************************************************/ void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) @@ -714,18 +779,41 @@ void CO_CANclearPendingSyncPDOs(CO_CANmodule_t *CANmodule) /******************************************************************************/ void CO_CANmodule_process(CO_CANmodule_t *CANmodule) { + if (CANmodule == NULL || CANmodule->CANinterfaceCount == 0) return; + +#if CO_DRIVER_ERROR_REPORTING > 0 /* socketCAN doesn't support microcontroller-like error counters. If an * error has occured, a special can message is created by the driver and * received by the application like a regular message. * Therefore, error counter evaluation is included in rx function. * Here we just copy evaluated CANerrorStatus from the first CAN interface. */ -#if CO_DRIVER_ERROR_REPORTING > 0 - if (CANmodule->CANinterfaceCount > 0) { - CANmodule->CANerrorStatus = - CANmodule->CANinterfaces[0].errorhandler.CANerrorStatus; - } + CANmodule->CANerrorStatus = + CANmodule->CANinterfaces[0].errorhandler.CANerrorStatus; #endif + +#if CO_DRIVER_MULTI_INTERFACE == 0 + /* recall CO_CANsend(), if message was unsent before */ + if (CANmodule->CANtxCount > 0) { + bool_t found = false; + + for (uint16_t i = 0; i < CANmodule->txSize; i++) { + CO_CANtx_t *buffer = &CANmodule->txArray[i]; + + if (buffer->bufferFull) { + buffer->bufferFull = false; + CANmodule->CANtxCount--; + CO_CANsend(CANmodule, buffer); + found = true; + break; + } + } + + if (!found) { + CANmodule->CANtxCount = 0; + } + } +#endif /* CO_DRIVER_MULTI_INTERFACE == 0 */ } diff --git a/socketCAN/CO_driver_target.h b/socketCAN/CO_driver_target.h index 3cfc8c3ba..9ca2b100c 100644 --- a/socketCAN/CO_driver_target.h +++ b/socketCAN/CO_driver_target.h @@ -298,7 +298,7 @@ typedef struct { uint8_t DLC; uint8_t padding[3]; /* ensure alignment */ uint8_t data[8]; - volatile bool_t bufferFull; /* not used */ + volatile bool_t bufferFull; volatile bool_t syncFlag; /* info about transmit message */ int can_ifindex; /* CAN Interface index to use */ } CO_CANtx_t; @@ -338,6 +338,7 @@ typedef struct { uint16_t txSize; uint16_t CANerrorStatus; volatile bool_t CANnormal; + volatile uint16_t CANtxCount; int epoll_fd; /* File descriptor for epoll, which waits for CAN receive event */ #if CO_DRIVER_MULTI_INTERFACE > 0 || defined CO_DOXYGEN diff --git a/socketCAN/CO_epoll_interface.c b/socketCAN/CO_epoll_interface.c index ac5dca7a6..ee23c108d 100644 --- a/socketCAN/CO_epoll_interface.c +++ b/socketCAN/CO_epoll_interface.c @@ -50,6 +50,11 @@ #endif #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */ +/* delay for recall CANsend(), if CAN TX buffer is full */ +#ifndef CANSEND_DELAY_US +#define CANSEND_DELAY_US 100 +#endif + /* EPOLL **********************************************************************/ /* Helper function - get monotonic clock time in microseconds */ @@ -292,7 +297,14 @@ void CO_epoll_processMain(CO_epoll_t *ep, } /* process CANopen objects */ - *reset = CO_process(co, ep->timeDifference_us, &ep->timerNext_us); + *reset = CO_process(co, + ep->timeDifference_us, + &ep->timerNext_us); + + /* If there are unsent CAN messages, call CO_CANmodule_process() earlier */ + if (co->CANmodule[0]->CANtxCount > 0 && ep->timerNext_us > CANSEND_DELAY_US) { + ep->timerNext_us = CANSEND_DELAY_US; + } } diff --git a/socketCAN/CO_error_msgs.h b/socketCAN/CO_error_msgs.h index b038fdeee..1ce9f822f 100644 --- a/socketCAN/CO_error_msgs.h +++ b/socketCAN/CO_error_msgs.h @@ -41,7 +41,7 @@ extern "C" { #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_SOCKET_BUF_SIZE "CAN Interface \"%s\" RX buffer set to %d messages (%d Bytes)" #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..." @@ -60,7 +60,7 @@ extern "C" { #define DBG_GENERAL "(%s) Error: %s%d", __func__ #define DBG_ERRNO "(%s) OS error \"%s\" in %s", __func__, strerror(errno) #define DBG_CO_DEBUG "(%s) CO_DEBUG: %s", __func__ -#define DBG_CAN_TX_FAILED "(%s) Transmitting CAN msg OID 0x%08x failed(%s)", __func__ +#define DBG_CAN_TX_FAILED "(%s) Transmitting CAN msg OID 0x%03x failed(%s)", __func__ #define DBG_CAN_RX_PARAM_FAILED "(%s) Setting CAN rx buffer failed (%s)", __func__ #define DBG_CAN_RX_FAILED "(%s) Receiving CAN msg failed (%s)", __func__ #define DBG_CAN_ERROR_GENERAL "(%s) Socket error msg ID: 0x%08x, Data[0..7]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x (%s)", __func__ From a705e76910e146e5ab74ee594b924286ef0feaa7 Mon Sep 17 00:00:00 2001 From: Geoffrey Van Landeghem Date: Tue, 16 Feb 2021 20:40:31 +0100 Subject: [PATCH 21/21] BUGFIX: updating nmt internal state doesn't trigger callback function --- 301/CO_NMT_Heartbeat.c | 19 +++++++++++++++++++ 301/CO_NMT_Heartbeat.h | 7 ++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/301/CO_NMT_Heartbeat.c b/301/CO_NMT_Heartbeat.c index c7513edb2..f355b2745 100644 --- a/301/CO_NMT_Heartbeat.c +++ b/301/CO_NMT_Heartbeat.c @@ -328,6 +328,25 @@ CO_NMT_reset_cmd_t CO_NMT_process( return (CO_NMT_reset_cmd_t) NMT->resetCommand; } +/******************************************************************************/ +void CO_NMT_setInternalState(CO_NMT_t *NMT, + CO_NMT_internalState_t state) +{ + if (NMT == NULL) + return; + + if (NMT->operatingState == state) + return; + + NMT->operatingState = state; + +#if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE + if (NMT->pFunctNMT != NULL) { + NMT->pFunctNMT(NMT->operatingState); + } +#endif +} + #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER /******************************************************************************/ diff --git a/301/CO_NMT_Heartbeat.h b/301/CO_NMT_Heartbeat.h index 65c263e11..e5477f33a 100644 --- a/301/CO_NMT_Heartbeat.h +++ b/301/CO_NMT_Heartbeat.h @@ -265,11 +265,8 @@ static inline CO_NMT_internalState_t CO_NMT_getInternalState(CO_NMT_t *NMT) { * @param NMT This object. * @param state New state. */ -static inline void CO_NMT_setInternalState(CO_NMT_t *NMT, - CO_NMT_internalState_t state) -{ - if (NMT != NULL) NMT->operatingState = state; -} +void CO_NMT_setInternalState(CO_NMT_t *NMT, + CO_NMT_internalState_t state); #if ((CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER) || defined CO_DOXYGEN