362 lines
12 KiB
C
362 lines
12 KiB
C
/*
|
|
* CANopen SYNC object.
|
|
*
|
|
* @file CO_SYNC.c
|
|
* @ingroup CO_SYNC
|
|
* @author Janez Paternoster
|
|
* @copyright 2004 - 2020 Janez Paternoster
|
|
*
|
|
* This file is part of CANopenNode, an opensource CANopen Stack.
|
|
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
|
* For more information on CANopen see <http://www.can-cia.org/>.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
#include "CO_driver.h"
|
|
#include "CO_SDO.h"
|
|
#include "CO_Emergency.h"
|
|
#include "CO_NMT_Heartbeat.h"
|
|
#include "CO_SYNC.h"
|
|
|
|
/*
|
|
* 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, const CO_CANrxMsg_t *msg){
|
|
CO_SYNC_t *SYNC;
|
|
uint8_t operState;
|
|
|
|
SYNC = (CO_SYNC_t*)object; /* this is the correct pointer type of the first argument */
|
|
operState = *SYNC->operatingState;
|
|
|
|
if((operState == CO_NMT_OPERATIONAL) || (operState == CO_NMT_PRE_OPERATIONAL)){
|
|
if(SYNC->counterOverflowValue == 0){
|
|
if(msg->DLC == 0U){
|
|
SET_CANrxNew(SYNC->CANrxNew);
|
|
}
|
|
else{
|
|
SYNC->receiveError = (uint16_t)msg->DLC | 0x0100U;
|
|
}
|
|
}
|
|
else{
|
|
if(msg->DLC == 1U){
|
|
SYNC->counter = msg->data[0];
|
|
SET_CANrxNew(SYNC->CANrxNew);
|
|
}
|
|
else{
|
|
SYNC->receiveError = (uint16_t)msg->DLC | 0x0200U;
|
|
}
|
|
}
|
|
if(IS_CANrxNew(SYNC->CANrxNew)) {
|
|
SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function for accessing _COB ID SYNC Message_ (index 0x1005) from SDO server.
|
|
*
|
|
* For more information see file CO_SDO.h.
|
|
*/
|
|
static CO_SDO_abortCode_t CO_ODF_1005(CO_ODF_arg_t *ODF_arg){
|
|
CO_SYNC_t *SYNC;
|
|
uint32_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
SYNC = (CO_SYNC_t*) ODF_arg->object;
|
|
value = CO_getUint32(ODF_arg->data);
|
|
|
|
if(!ODF_arg->reading){
|
|
uint8_t configureSyncProducer = 0;
|
|
|
|
/* only 11-bit CAN identifier is supported */
|
|
if(value & 0x20000000UL){
|
|
ret = CO_SDO_AB_INVALID_VALUE;
|
|
}
|
|
else{
|
|
/* is 'generate Sync messge' bit set? */
|
|
if(value & 0x40000000UL){
|
|
/* if bit was set before, value can not be changed */
|
|
if(SYNC->isProducer){
|
|
ret = CO_SDO_AB_DATA_DEV_STATE;
|
|
}
|
|
else{
|
|
configureSyncProducer = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* configure sync producer and consumer */
|
|
if(ret == CO_SDO_AB_NONE){
|
|
SYNC->COB_ID = (uint16_t)(value & 0x7FFU);
|
|
|
|
if(configureSyncProducer){
|
|
uint8_t len = 0U;
|
|
if(SYNC->counterOverflowValue != 0U){
|
|
len = 1U;
|
|
SYNC->counter = 0U;
|
|
SYNC->timer = 0U;
|
|
}
|
|
SYNC->CANtxBuff = CO_CANtxBufferInit(
|
|
SYNC->CANdevTx, /* CAN device */
|
|
SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */
|
|
SYNC->COB_ID, /* CAN identifier */
|
|
0, /* rtr */
|
|
len, /* number of data bytes */
|
|
0); /* synchronous message flag bit */
|
|
SYNC->isProducer = true;
|
|
}
|
|
else{
|
|
SYNC->isProducer = false;
|
|
}
|
|
|
|
CO_CANrxBufferInit(
|
|
SYNC->CANdevRx, /* CAN device */
|
|
SYNC->CANdevRxIdx, /* rx buffer index */
|
|
SYNC->COB_ID, /* CAN identifier */
|
|
0x7FF, /* mask */
|
|
0, /* rtr */
|
|
(void*)SYNC, /* object passed to receive function */
|
|
CO_SYNC_receive); /* this function will process received message */
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function for accessing _Communication cycle period_ (index 0x1006) from SDO server.
|
|
*
|
|
* For more information see file CO_SDO.h.
|
|
*/
|
|
static CO_SDO_abortCode_t CO_ODF_1006(CO_ODF_arg_t *ODF_arg){
|
|
CO_SYNC_t *SYNC;
|
|
uint32_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
SYNC = (CO_SYNC_t*) ODF_arg->object;
|
|
value = CO_getUint32(ODF_arg->data);
|
|
|
|
if(!ODF_arg->reading){
|
|
/* period transition from 0 to something */
|
|
if((SYNC->periodTime == 0) && (value != 0)){
|
|
SYNC->counter = 0;
|
|
}
|
|
|
|
SYNC->periodTime = value;
|
|
SYNC->periodTimeoutTime = (value / 2U) * 3U;
|
|
/* overflow? */
|
|
if(SYNC->periodTimeoutTime < value){
|
|
SYNC->periodTimeoutTime = 0xFFFFFFFFUL;
|
|
}
|
|
|
|
SYNC->timer = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function for accessing _Synchronous counter overflow value_ (index 0x1019) from SDO server.
|
|
*
|
|
* For more information see file CO_SDO.h.
|
|
*/
|
|
static CO_SDO_abortCode_t CO_ODF_1019(CO_ODF_arg_t *ODF_arg){
|
|
CO_SYNC_t *SYNC;
|
|
uint8_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
SYNC = (CO_SYNC_t*) ODF_arg->object;
|
|
value = ODF_arg->data[0];
|
|
|
|
if(!ODF_arg->reading){
|
|
uint8_t len = 0U;
|
|
|
|
if(SYNC->periodTime){
|
|
ret = CO_SDO_AB_DATA_DEV_STATE;
|
|
}
|
|
else if((value == 1) || (value > 240)){
|
|
ret = CO_SDO_AB_INVALID_VALUE;
|
|
}
|
|
else{
|
|
SYNC->counterOverflowValue = value;
|
|
if(value != 0){
|
|
len = 1U;
|
|
}
|
|
|
|
SYNC->CANtxBuff = CO_CANtxBufferInit(
|
|
SYNC->CANdevTx, /* CAN device */
|
|
SYNC->CANdevTxIdx, /* index of specific buffer inside CAN module */
|
|
SYNC->COB_ID, /* CAN identifier */
|
|
0, /* rtr */
|
|
len, /* number of data bytes */
|
|
0); /* synchronous message flag bit */
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
CO_ReturnError_t CO_SYNC_init(
|
|
CO_SYNC_t *SYNC,
|
|
CO_EM_t *em,
|
|
CO_SDO_t *SDO,
|
|
uint8_t *operatingState,
|
|
uint32_t COB_ID_SYNCMessage,
|
|
uint32_t communicationCyclePeriod,
|
|
uint8_t synchronousCounterOverflowValue,
|
|
CO_CANmodule_t *CANdevRx,
|
|
uint16_t CANdevRxIdx,
|
|
CO_CANmodule_t *CANdevTx,
|
|
uint16_t CANdevTxIdx)
|
|
{
|
|
uint8_t len = 0;
|
|
|
|
/* verify arguments */
|
|
if(SYNC==NULL || em==NULL || SDO==NULL || operatingState==NULL ||
|
|
CANdevRx==NULL || CANdevTx==NULL){
|
|
return CO_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
/* Configure object variables */
|
|
SYNC->isProducer = (COB_ID_SYNCMessage&0x40000000L) ? true : false;
|
|
SYNC->COB_ID = COB_ID_SYNCMessage&0x7FF;
|
|
|
|
SYNC->periodTime = communicationCyclePeriod;
|
|
SYNC->periodTimeoutTime = communicationCyclePeriod / 2 * 3;
|
|
/* overflow? */
|
|
if(SYNC->periodTimeoutTime < communicationCyclePeriod) SYNC->periodTimeoutTime = 0xFFFFFFFFL;
|
|
|
|
SYNC->counterOverflowValue = synchronousCounterOverflowValue;
|
|
if(synchronousCounterOverflowValue) len = 1;
|
|
|
|
SYNC->curentSyncTimeIsInsideWindow = true;
|
|
|
|
CLEAR_CANrxNew(SYNC->CANrxNew);
|
|
SYNC->CANrxToggle = false;
|
|
SYNC->timer = 0;
|
|
SYNC->counter = 0;
|
|
SYNC->receiveError = 0U;
|
|
|
|
SYNC->em = em;
|
|
SYNC->operatingState = operatingState;
|
|
|
|
SYNC->CANdevRx = CANdevRx;
|
|
SYNC->CANdevRxIdx = CANdevRxIdx;
|
|
|
|
/* Configure Object dictionary entry at index 0x1005, 0x1006 and 0x1019 */
|
|
CO_OD_configure(SDO, OD_H1005_COBID_SYNC, CO_ODF_1005, (void*)SYNC, 0, 0);
|
|
CO_OD_configure(SDO, OD_H1006_COMM_CYCL_PERIOD, CO_ODF_1006, (void*)SYNC, 0, 0);
|
|
CO_OD_configure(SDO, OD_H1019_SYNC_CNT_OVERFLOW, CO_ODF_1019, (void*)SYNC, 0, 0);
|
|
|
|
/* configure SYNC CAN reception */
|
|
CO_CANrxBufferInit(
|
|
CANdevRx, /* CAN device */
|
|
CANdevRxIdx, /* rx buffer index */
|
|
SYNC->COB_ID, /* CAN identifier */
|
|
0x7FF, /* mask */
|
|
0, /* rtr */
|
|
(void*)SYNC, /* object passed to receive function */
|
|
CO_SYNC_receive); /* this function will process received message */
|
|
|
|
/* configure SYNC CAN transmission */
|
|
SYNC->CANdevTx = CANdevTx;
|
|
SYNC->CANdevTxIdx = CANdevTxIdx;
|
|
SYNC->CANtxBuff = CO_CANtxBufferInit(
|
|
CANdevTx, /* CAN device */
|
|
CANdevTxIdx, /* index of specific buffer inside CAN module */
|
|
SYNC->COB_ID, /* CAN identifier */
|
|
0, /* rtr */
|
|
len, /* number of data bytes */
|
|
0); /* synchronous message flag bit */
|
|
|
|
return CO_ERROR_NO;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
uint8_t CO_SYNC_process(
|
|
CO_SYNC_t *SYNC,
|
|
uint32_t timeDifference_us,
|
|
uint32_t ObjDict_synchronousWindowLength)
|
|
{
|
|
uint8_t ret = 0;
|
|
uint32_t timerNew;
|
|
|
|
if(*SYNC->operatingState == CO_NMT_OPERATIONAL || *SYNC->operatingState == CO_NMT_PRE_OPERATIONAL){
|
|
/* update sync timer, no overflow */
|
|
timerNew = SYNC->timer + timeDifference_us;
|
|
if(timerNew > SYNC->timer) SYNC->timer = timerNew;
|
|
|
|
/* was SYNC just received */
|
|
if(IS_CANrxNew(SYNC->CANrxNew)){
|
|
SYNC->timer = 0;
|
|
ret = 1;
|
|
CLEAR_CANrxNew(SYNC->CANrxNew);
|
|
}
|
|
|
|
/* SYNC producer */
|
|
if(SYNC->isProducer && SYNC->periodTime){
|
|
if(SYNC->timer >= SYNC->periodTime){
|
|
if(++SYNC->counter > SYNC->counterOverflowValue) SYNC->counter = 1;
|
|
SYNC->timer = 0;
|
|
ret = 1;
|
|
SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true;
|
|
SYNC->CANtxBuff->data[0] = SYNC->counter;
|
|
CO_CANsend(SYNC->CANdevTx, SYNC->CANtxBuff);
|
|
}
|
|
}
|
|
|
|
/* Synchronous PDOs are allowed only inside time window */
|
|
if(ObjDict_synchronousWindowLength){
|
|
if(SYNC->timer > ObjDict_synchronousWindowLength){
|
|
if(SYNC->curentSyncTimeIsInsideWindow){
|
|
ret = 2;
|
|
}
|
|
SYNC->curentSyncTimeIsInsideWindow = false;
|
|
}
|
|
else{
|
|
SYNC->curentSyncTimeIsInsideWindow = true;
|
|
}
|
|
}
|
|
else{
|
|
SYNC->curentSyncTimeIsInsideWindow = true;
|
|
}
|
|
|
|
/* Verify timeout of SYNC */
|
|
if(SYNC->periodTime && SYNC->timer > SYNC->periodTimeoutTime && *SYNC->operatingState == CO_NMT_OPERATIONAL)
|
|
CO_errorReport(SYNC->em, CO_EM_SYNC_TIME_OUT, CO_EMC_COMMUNICATION, SYNC->timer);
|
|
}
|
|
else {
|
|
CLEAR_CANrxNew(SYNC->CANrxNew);
|
|
}
|
|
|
|
/* verify error from receive function */
|
|
if(SYNC->receiveError != 0U){
|
|
CO_errorReport(SYNC->em, CO_EM_SYNC_LENGTH, CO_EMC_SYNC_DATA_LENGTH, (uint32_t)SYNC->receiveError);
|
|
SYNC->receiveError = 0U;
|
|
}
|
|
|
|
return ret;
|
|
}
|