/* * CANopen SYNC object. * * @file CO_SYNC.c * @ingroup CO_SYNC * @author Janez Paternoster * @copyright 2021 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 "301/CO_SYNC.h" #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE /* * Read received message from CAN module. * * Function will be called (by CAN receive interrupt) every time, when CAN * message with correct identifier will be received. For more information and * description of parameters see file CO_driver.h. */ static void CO_SYNC_receive(void *object, void *msg) { CO_SYNC_t *SYNC = object; uint8_t DLC = CO_CANrxMsg_readDLC(msg); bool_t syncReceived = false; if (SYNC->counterOverflowValue == 0) { if (DLC == 0) { syncReceived = true; } else { SYNC->receiveError = DLC | 0x40; } } else { if (DLC == 1) { uint8_t *data = CO_CANrxMsg_readData(msg); SYNC->counter = data[0]; syncReceived = true; } else { SYNC->receiveError = DLC | 0x80; } } if (syncReceived) { /* toggle PDO receive buffer */ SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true; CO_FLAG_SET(SYNC->CANrxNew); #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_CALLBACK_PRE /* Optional signal to RTOS, which can resume task, which handles SYNC.*/ if (SYNC->pFunctSignalPre != NULL) { SYNC->pFunctSignalPre(SYNC->functSignalObjectPre); } #endif } } #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_OD_DYNAMIC /* * Custom function for writing OD object "COB-ID sync message" * * For more information see file CO_ODinterface.h, OD_IO_t. */ static ODR_t OD_write_1005(OD_stream_t *stream, const void *buf, OD_size_t count, OD_size_t *countWritten) { if (stream == NULL || stream->subIndex != 0 || buf == NULL || count != sizeof(uint32_t) || countWritten == NULL ) { return ODR_DEV_INCOMPAT; } CO_SYNC_t *SYNC = stream->object; uint32_t cobIdSync = CO_getUint32(buf); uint16_t CAN_ID = (uint16_t)(cobIdSync & 0x7FF); /* verify written value */ #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER bool_t isProducer = (cobIdSync & 0x40000000) != 0; if ((cobIdSync & 0xBFFF8000) != 0 || (SYNC->isProducer && isProducer && CAN_ID != SYNC->CAN_ID) ) { return ODR_INVALID_VALUE; } #else if ((cobIdSync & 0xFFFF8000) != 0) { return ODR_INVALID_VALUE; } #endif /* Configure CAN receive and transmit buffers */ if (CAN_ID != SYNC->CAN_ID) { CO_ReturnError_t CANret = CO_CANrxBufferInit( SYNC->CANdevRx, /* CAN device */ SYNC->CANdevRxIdx, /* rx buffer index */ CAN_ID, /* CAN identifier */ 0x7FF, /* mask */ 0, /* rtr */ (void*)SYNC, /* object passed to receive function */ CO_SYNC_receive); /* this function will process received message*/ if (CANret != CO_ERROR_NO) { return ODR_DEV_INCOMPAT; } #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER SYNC->CANtxBuff = CO_CANtxBufferInit( SYNC->CANdevTx, /* CAN device */ SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */ CAN_ID, /* CAN identifier */ 0, /* rtr */ SYNC->counterOverflowValue != 0 ? 1 : 0, /* number of data bytes */ 0); /* synchronous message flag bit */ if (SYNC->CANtxBuff == NULL) { SYNC->isProducer = false; return ODR_DEV_INCOMPAT; } #endif SYNC->CAN_ID = CAN_ID; } #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER SYNC->isProducer = isProducer; if (isProducer) { SYNC->counter = 0; SYNC->timer = 0; } #endif /* CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER */ /* write value to the original location in the Object Dictionary */ return OD_writeOriginal(stream, buf, count, countWritten); } #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER /* * Custom function for writing OD object "Synchronous counter overflow value" * * For more information see file CO_ODinterface.h, OD_IO_t. */ static ODR_t OD_write_1019(OD_stream_t *stream, const void *buf, OD_size_t count, OD_size_t *countWritten) { if (stream == NULL || stream->subIndex != 0 || buf == NULL || count != sizeof(uint8_t) || countWritten == NULL ) { return ODR_DEV_INCOMPAT; } CO_SYNC_t *SYNC = stream->object; uint8_t syncCounterOvf = CO_getUint8(buf); /* verify written value */ if (syncCounterOvf == 1 || syncCounterOvf > 240) { return ODR_INVALID_VALUE; } if (*SYNC->OD_1006_period != 0 && SYNC->isProducer) { return ODR_DATA_DEV_STATE; } /* Configure CAN transmit buffer */ SYNC->CANtxBuff = CO_CANtxBufferInit( SYNC->CANdevTx, /* CAN device */ SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */ SYNC->CAN_ID, /* CAN identifier */ 0, /* rtr */ syncCounterOvf != 0 ? 1 : 0, /* number of data bytes */ 0); /* synchronous message flag bit */ if (SYNC->CANtxBuff == NULL) { SYNC->isProducer = false; return ODR_DEV_INCOMPAT; } SYNC->counterOverflowValue = syncCounterOvf; /* write value to the original location in the Object Dictionary */ return OD_writeOriginal(stream, buf, count, countWritten); } #endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER */ #endif /* (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_OD_DYNAMIC */ /******************************************************************************/ CO_ReturnError_t CO_SYNC_init(CO_SYNC_t *SYNC, CO_EM_t *em, OD_entry_t *OD_1005_cobIdSync, OD_entry_t *OD_1006_commCyclePeriod, OD_entry_t *OD_1007_syncWindowLen, OD_entry_t *OD_1019_syncCounterOvf, CO_CANmodule_t *CANdevRx, uint16_t CANdevRxIdx, #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER CO_CANmodule_t *CANdevTx, uint16_t CANdevTxIdx, #endif uint32_t *errInfo) { ODR_t odRet; /* verify arguments */ if (SYNC == NULL || em == NULL || OD_1005_cobIdSync == NULL #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER || OD_1006_commCyclePeriod == NULL || CANdevTx == NULL #endif || CANdevRx == NULL ) { return CO_ERROR_ILLEGAL_ARGUMENT; } /* clear object */ memset(SYNC, 0, sizeof(CO_SYNC_t)); /* get and verify "COB-ID SYNC message" from OD and configure extension */ uint32_t cobIdSync = 0x00000080; odRet = OD_get_u32(OD_1005_cobIdSync, 0, &cobIdSync, true); if (odRet != ODR_OK) { if (errInfo != NULL) *errInfo = OD_getIndex(OD_1005_cobIdSync); return CO_ERROR_OD_PARAMETERS; } #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_OD_DYNAMIC SYNC->OD_1005_extension.object = SYNC; SYNC->OD_1005_extension.read = OD_readOriginal; SYNC->OD_1005_extension.write = OD_write_1005; OD_extension_init(OD_1005_cobIdSync, &SYNC->OD_1005_extension); #endif /* get and verify "Communication cycle period" from OD */ SYNC->OD_1006_period = OD_getPtr(OD_1006_commCyclePeriod, 0, sizeof(uint32_t), NULL); #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER if (SYNC->OD_1006_period == NULL) { if (errInfo != NULL) *errInfo = OD_getIndex(OD_1006_commCyclePeriod); return CO_ERROR_OD_PARAMETERS; } #else if (OD_1006_commCyclePeriod != NULL && SYNC->OD_1006_period == NULL) { if (errInfo != NULL) *errInfo = OD_getIndex(OD_1006_commCyclePeriod); return CO_ERROR_OD_PARAMETERS; } #endif /* get "Synchronous window length" from OD (optional parameter) */ SYNC->OD_1007_window = OD_getPtr(OD_1007_syncWindowLen, 0, sizeof(uint32_t), NULL); if (OD_1007_syncWindowLen != NULL && SYNC->OD_1007_window == NULL) { if (errInfo != NULL) *errInfo = OD_getIndex(OD_1007_syncWindowLen); return CO_ERROR_OD_PARAMETERS; } /* get and verify optional "Synchronous counter overflow value" from OD and * configure extension */ uint8_t syncCounterOvf = 0; if (OD_1019_syncCounterOvf != NULL) { odRet = OD_get_u8(OD_1019_syncCounterOvf, 0, &syncCounterOvf, true); if (odRet != ODR_OK) { if (errInfo != NULL) *errInfo = OD_getIndex(OD_1019_syncCounterOvf); return CO_ERROR_OD_PARAMETERS; } if (syncCounterOvf == 1) syncCounterOvf = 2; else if (syncCounterOvf > 240) syncCounterOvf = 240; #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_OD_DYNAMIC #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER SYNC->OD_1019_extension.object = SYNC; SYNC->OD_1019_extension.read = OD_readOriginal; SYNC->OD_1019_extension.write = OD_write_1019; OD_extension_init(OD_1019_syncCounterOvf, &SYNC->OD_1019_extension); #endif #endif } SYNC->counterOverflowValue = syncCounterOvf; /* Configure object variables */ SYNC->em = em; #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER SYNC->isProducer = (cobIdSync & 0x40000000) != 0; #endif #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_OD_DYNAMIC SYNC->CAN_ID = cobIdSync & 0x7FF; SYNC->CANdevRx = CANdevRx; SYNC->CANdevRxIdx = CANdevRxIdx; #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER SYNC->CANdevTx = CANdevTx; SYNC->CANdevTxIdx = CANdevTxIdx; #endif #endif /* configure SYNC CAN reception and transmission */ CO_ReturnError_t ret = CO_CANrxBufferInit( CANdevRx, /* CAN device */ CANdevRxIdx, /* rx buffer index */ cobIdSync & 0x7FF, /* CAN identifier */ 0x7FF, /* mask */ 0, /* rtr */ (void*)SYNC, /* object passed to receive function */ CO_SYNC_receive); /* this function will process received message*/ if (ret != CO_ERROR_NO) return ret; #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER SYNC->CANtxBuff = CO_CANtxBufferInit( CANdevTx, /* CAN device */ CANdevTxIdx, /* index of specific buffer inside CAN module */ cobIdSync & 0x7FF, /* CAN identifier */ 0, /* rtr */ syncCounterOvf != 0 ? 1 : 0, /* number of data bytes */ 0); /* synchronous message flag bit */ if (SYNC->CANtxBuff == NULL) return CO_ERROR_ILLEGAL_ARGUMENT; #endif return CO_ERROR_NO; } #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_CALLBACK_PRE /******************************************************************************/ void CO_SYNC_initCallbackPre( CO_SYNC_t *SYNC, void *object, void (*pFunctSignalPre)(void *object)) { if (SYNC != NULL) { SYNC->functSignalObjectPre = object; SYNC->pFunctSignalPre = pFunctSignalPre; } } #endif /******************************************************************************/ CO_SYNC_status_t CO_SYNC_process(CO_SYNC_t *SYNC, bool_t NMTisPreOrOperational, uint32_t timeDifference_us, uint32_t *timerNext_us) { (void)timerNext_us; /* may be unused */ CO_SYNC_status_t syncStatus = CO_SYNC_NONE; if (NMTisPreOrOperational) { /* update sync timer, no overflow */ uint32_t timerNew = SYNC->timer + timeDifference_us; if (timerNew > SYNC->timer) SYNC->timer = timerNew; /* was SYNC just received */ if (CO_FLAG_READ(SYNC->CANrxNew)) { SYNC->timer = 0; syncStatus = CO_SYNC_RX_TX; CO_FLAG_CLEAR(SYNC->CANrxNew); } uint32_t OD_1006_period = SYNC->OD_1006_period != NULL ? *SYNC->OD_1006_period : 0; if (OD_1006_period > 0) { #if (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER if (SYNC->isProducer) { if (SYNC->timer >= OD_1006_period) { syncStatus = CO_SYNC_RX_TX; CO_SYNCsend(SYNC); } #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_TIMERNEXT /* Calculate when next SYNC needs to be sent */ if (timerNext_us != NULL) { uint32_t diff = OD_1006_period - SYNC->timer; if (*timerNext_us > diff) { *timerNext_us = diff; } } #endif } else #endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_PRODUCER */ /* Verify timeout of SYNC */ if (SYNC->timeoutError == 1) { /* periodTimeout is 1,5 * OD_1006_period, no overflow */ uint32_t periodTimeout = OD_1006_period + (OD_1006_period >> 1); if (periodTimeout < OD_1006_period) periodTimeout = 0xFFFFFFFF; if (SYNC->timer > periodTimeout) { CO_errorReport(SYNC->em, CO_EM_SYNC_TIME_OUT, CO_EMC_COMMUNICATION, SYNC->timer); SYNC->timeoutError = 2; } #if (CO_CONFIG_SYNC) & CO_CONFIG_FLAG_TIMERNEXT else if (timerNext_us != NULL) { uint32_t diff = periodTimeout - SYNC->timer; if (*timerNext_us > diff) { *timerNext_us = diff; } } #endif } } /* if (OD_1006_period > 0) */ /* Synchronous PDOs are allowed only inside time window */ if (SYNC->OD_1007_window != NULL && *SYNC->OD_1007_window > 0 && SYNC->timer > *SYNC->OD_1007_window ) { if (!SYNC->syncIsOutsideWindow) { syncStatus = CO_SYNC_PASSED_WINDOW; } SYNC->syncIsOutsideWindow = true; } else { SYNC->syncIsOutsideWindow = false; } /* verify error from receive function */ if (SYNC->receiveError != 0) { CO_errorReport(SYNC->em, CO_EM_SYNC_LENGTH, CO_EMC_SYNC_DATA_LENGTH, SYNC->receiveError); SYNC->receiveError = 0; } } /* if (NMTisPreOrOperational) */ else { CO_FLAG_CLEAR(SYNC->CANrxNew); SYNC->receiveError = 0; SYNC->counter = 0; SYNC->timer = 0; } if (syncStatus == CO_SYNC_RX_TX) { if (SYNC->timeoutError == 2) { CO_errorReset(SYNC->em, CO_EM_SYNC_TIME_OUT, 0); } SYNC->timeoutError = 1; } return syncStatus; } #endif /* (CO_CONFIG_SYNC) & CO_CONFIG_SYNC_ENABLE */