Node guarding added
This commit is contained in:
parent
361080da87
commit
b0b0724c6f
8 changed files with 939 additions and 11 deletions
460
301/CO_Node_Guarding.c
Normal file
460
301/CO_Node_Guarding.c
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* CANopen Node Guarding slave and master objects.
|
||||
*
|
||||
* @file CO_Node_Guarding.c
|
||||
* @ingroup CO_Node_Guarding
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2023 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 "301/CO_Node_Guarding.h"
|
||||
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_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_ngs_receive(void *object, void *msg) {
|
||||
(void) msg;
|
||||
CO_nodeGuardingSlave_t *ngs = (CO_nodeGuardingSlave_t*)object;
|
||||
|
||||
CO_FLAG_SET(ngs->CANrxNew);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Custom function for writing OD object "Guard time"
|
||||
*
|
||||
* For more information see file CO_ODinterface.h, OD_IO_t.
|
||||
*/
|
||||
static ODR_t OD_write_100C(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(uint16_t) || countWritten == NULL
|
||||
) {
|
||||
return ODR_DEV_INCOMPAT;
|
||||
}
|
||||
|
||||
CO_nodeGuardingSlave_t *ngs = (CO_nodeGuardingSlave_t *)stream->object;
|
||||
|
||||
/* update objects */
|
||||
ngs->guardTime_us = (uint32_t)CO_getUint16(buf) * 1000;
|
||||
ngs->lifeTime_us = ngs->guardTime_us * ngs->lifeTimeFactor;
|
||||
|
||||
/* reset running timer */
|
||||
if (ngs->lifeTimer > 0) {
|
||||
ngs->lifeTimer = ngs->lifeTime_us;
|
||||
}
|
||||
|
||||
/* write value to the original location in the Object Dictionary */
|
||||
return OD_writeOriginal(stream, buf, count, countWritten);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Custom function for writing OD object "Life time factor"
|
||||
*
|
||||
* For more information see file CO_ODinterface.h, OD_IO_t.
|
||||
*/
|
||||
static ODR_t OD_write_100D(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_nodeGuardingSlave_t *ngs = (CO_nodeGuardingSlave_t *)stream->object;
|
||||
|
||||
/* update objects */
|
||||
ngs->lifeTimeFactor = (uint8_t)CO_getUint8(buf);
|
||||
ngs->lifeTime_us = ngs->guardTime_us * ngs->lifeTimeFactor;
|
||||
|
||||
/* reset running timer */
|
||||
if (ngs->lifeTimer > 0) {
|
||||
ngs->lifeTimer = ngs->lifeTime_us;
|
||||
}
|
||||
|
||||
/* write value to the original location in the Object Dictionary */
|
||||
return OD_writeOriginal(stream, buf, count, countWritten);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_nodeGuardingSlave_init(CO_nodeGuardingSlave_t *ngs,
|
||||
OD_entry_t *OD_100C_GuardTime,
|
||||
OD_entry_t *OD_100D_LifeTimeFactor,
|
||||
CO_EM_t *em,
|
||||
uint16_t CANidNodeGuarding,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx,
|
||||
uint32_t *errInfo)
|
||||
{
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
|
||||
/* verify arguments */
|
||||
if (ngs == NULL || em == NULL || CANdevRx == NULL || CANdevTx == NULL
|
||||
|| OD_100C_GuardTime == NULL || OD_100D_LifeTimeFactor == NULL
|
||||
) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* clear the object */
|
||||
memset(ngs, 0, sizeof(CO_nodeGuardingSlave_t));
|
||||
|
||||
/* Configure object variables */
|
||||
ngs->em = em;
|
||||
|
||||
/* get and verify required "Guard time" from the Object Dictionary */
|
||||
uint16_t guardTime_ms;
|
||||
ODR_t odRet = OD_get_u16(OD_100C_GuardTime, 0, &guardTime_ms, true);
|
||||
if (odRet != ODR_OK) {
|
||||
if (errInfo != NULL) *errInfo = OD_getIndex(OD_100C_GuardTime);
|
||||
return CO_ERROR_OD_PARAMETERS;
|
||||
}
|
||||
ngs->guardTime_us = (uint32_t)guardTime_ms * 1000;
|
||||
|
||||
ngs->OD_100C_extension.object = ngs;
|
||||
ngs->OD_100C_extension.read = OD_readOriginal;
|
||||
ngs->OD_100C_extension.write = OD_write_100C;
|
||||
odRet = OD_extension_init(OD_100C_GuardTime, &ngs->OD_100C_extension);
|
||||
if (odRet != ODR_OK) {
|
||||
if (errInfo != NULL) *errInfo = OD_getIndex(OD_100C_GuardTime);
|
||||
return CO_ERROR_OD_PARAMETERS;
|
||||
}
|
||||
|
||||
/* get and verify required "Life time factor" from the Object Dictionary */
|
||||
uint8_t lifeTimeFactor;
|
||||
odRet = OD_get_u8(OD_100D_LifeTimeFactor, 0, &lifeTimeFactor, true);
|
||||
if (odRet != ODR_OK) {
|
||||
if (errInfo != NULL) *errInfo = OD_getIndex(OD_100D_LifeTimeFactor);
|
||||
return CO_ERROR_OD_PARAMETERS;
|
||||
}
|
||||
ngs->lifeTimeFactor = lifeTimeFactor;
|
||||
ngs->lifeTime_us = ngs->guardTime_us * ngs->lifeTimeFactor;
|
||||
|
||||
ngs->OD_100D_extension.object = ngs;
|
||||
ngs->OD_100D_extension.read = OD_readOriginal;
|
||||
ngs->OD_100D_extension.write = OD_write_100D;
|
||||
odRet = OD_extension_init(OD_100D_LifeTimeFactor, &ngs->OD_100D_extension);
|
||||
if (odRet != ODR_OK) {
|
||||
if (errInfo != NULL) *errInfo = OD_getIndex(OD_100D_LifeTimeFactor);
|
||||
return CO_ERROR_OD_PARAMETERS;
|
||||
}
|
||||
|
||||
/* configure CAN reception */
|
||||
ret = CO_CANrxBufferInit(
|
||||
CANdevRx, /* CAN device */
|
||||
CANdevRxIdx, /* rx buffer index */
|
||||
CANidNodeGuarding, /* CAN identifier */
|
||||
0x7FF, /* mask */
|
||||
true, /* rtr */
|
||||
(void*)ngs, /* object passed to receive function */
|
||||
CO_ngs_receive); /* this function will process received message*/
|
||||
if (ret != CO_ERROR_NO) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* configure CAN transmission */
|
||||
ngs->CANdevTx = CANdevTx;
|
||||
ngs->CANtxBuff = CO_CANtxBufferInit(
|
||||
CANdevTx, /* CAN device */
|
||||
CANdevTxIdx, /* index of specific buffer inside CAN module */
|
||||
CANidNodeGuarding, /* CAN identifier */
|
||||
0, /* rtr */
|
||||
1, /* number of data bytes */
|
||||
0); /* synchronous message flag bit */
|
||||
if (ngs->CANtxBuff == NULL) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_nodeGuardingSlave_process(CO_nodeGuardingSlave_t *ngs,
|
||||
CO_NMT_internalState_t NMTstate,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *timerNext_us)
|
||||
{
|
||||
(void)timerNext_us; /* may be unused */
|
||||
|
||||
/* was RTR just received */
|
||||
if (CO_FLAG_READ(ngs->CANrxNew)) {
|
||||
ngs->lifeTimer = ngs->lifeTime_us;
|
||||
|
||||
/* send response */
|
||||
ngs->CANtxBuff->data[0] = (uint8_t) NMTstate;
|
||||
if (ngs->toggle) {
|
||||
ngs->CANtxBuff->data[0] |= 0x80;
|
||||
ngs->toggle = false;
|
||||
}
|
||||
else {
|
||||
ngs->toggle = true;
|
||||
}
|
||||
CO_CANsend(ngs->CANdevTx, ngs->CANtxBuff);
|
||||
|
||||
if (ngs->lifeTimeTimeout) {
|
||||
/* error bit is shared with HB consumer */
|
||||
CO_errorReset(ngs->em, CO_EM_HEARTBEAT_CONSUMER, 0);
|
||||
ngs->lifeTimeTimeout = false;
|
||||
}
|
||||
|
||||
CO_FLAG_CLEAR(ngs->CANrxNew);
|
||||
}
|
||||
|
||||
/* verify "Life time" timeout and update the timer */
|
||||
else if (ngs->lifeTimer > 0) {
|
||||
if (timeDifference_us < ngs->lifeTimer) {
|
||||
ngs->lifeTimer -= timeDifference_us;
|
||||
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
|
||||
/* Calculate, when timeout expires */
|
||||
if (timerNext_us != NULL && *timerNext_us > ngs->lifeTimer) {
|
||||
*timerNext_us = ngs->lifeTimer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
ngs->lifeTimer = 0;
|
||||
ngs->lifeTimeTimeout = true;
|
||||
|
||||
/* error bit is shared with HB consumer */
|
||||
CO_errorReport(ngs->em, CO_EM_HEARTBEAT_CONSUMER,
|
||||
CO_EMC_HEARTBEAT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE */
|
||||
|
||||
|
||||
|
||||
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_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.
|
||||
*
|
||||
* Function receives messages from CAN identifier from 0x700 to 0x7FF. It
|
||||
* searches matching node->ident from nodes array.
|
||||
*/
|
||||
static void CO_ngm_receive(void *object, void *msg) {
|
||||
CO_nodeGuardingMaster_t *ngm = (CO_nodeGuardingMaster_t*)object;
|
||||
|
||||
uint8_t DLC = CO_CANrxMsg_readDLC(msg);
|
||||
uint8_t *data = CO_CANrxMsg_readData(msg);
|
||||
uint16_t ident = CO_CANrxMsg_readIdent(msg);
|
||||
CO_nodeGuardingMasterNode_t *node = &ngm->nodes[0];
|
||||
|
||||
if (DLC == 1) {
|
||||
for (uint8_t i=0; i<CO_CONFIG_NODE_GUARDING_MASTER_COUNT; i++) {
|
||||
if (ident == node->ident) {
|
||||
uint8_t toggle = data[0] & 0x80;
|
||||
if (toggle == node->toggle) {
|
||||
node->responseRecived = true;
|
||||
node->NMTstate = (CO_NMT_internalState_t)(data[0] & 0x7F);
|
||||
node->toggle = (toggle != 0) ? 0x00 : 0x80;
|
||||
}
|
||||
break;
|
||||
}
|
||||
node ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_nodeGuardingMaster_init(CO_nodeGuardingMaster_t *ngm,
|
||||
CO_EM_t *em,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx)
|
||||
{
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
|
||||
/* verify arguments */
|
||||
if (ngm == NULL || em == NULL || CANdevRx == NULL || CANdevTx == NULL) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* clear the object */
|
||||
memset(ngm, 0, sizeof(CO_nodeGuardingMaster_t));
|
||||
|
||||
/* Configure object variables */
|
||||
ngm->em = em;
|
||||
|
||||
/* configure CAN reception. One buffer will receive all messages
|
||||
* from CAN-id 0x700 to 0x7FF. */
|
||||
ret = CO_CANrxBufferInit(
|
||||
CANdevRx, /* CAN device */
|
||||
CANdevRxIdx, /* rx buffer index */
|
||||
CO_CAN_ID_HEARTBEAT,/* CAN identifier = 0x700 */
|
||||
0x780, /* mask - accept any combination of lower 7 bits*/
|
||||
false, /* rtr */
|
||||
(void*)ngm, /* object passed to receive function */
|
||||
CO_ngm_receive); /* this function will process received message*/
|
||||
if (ret != CO_ERROR_NO) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* configure CAN transmission */
|
||||
ngm->CANdevTx = CANdevTx;
|
||||
ngm->CANdevTxIdx = CANdevTxIdx;
|
||||
ngm->CANtxBuff = CO_CANtxBufferInit(
|
||||
CANdevTx, /* CAN device */
|
||||
CANdevTxIdx, /* index of specific buffer inside CAN module */
|
||||
CO_CAN_ID_HEARTBEAT,/* CAN identifier - will be changed later.*/
|
||||
true, /* rtr */
|
||||
1, /* number of data bytes (rtr indication only) */
|
||||
0); /* synchronous message flag bit */
|
||||
if (ngm->CANtxBuff == NULL) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_nodeGuardingMaster_initNode(CO_nodeGuardingMaster_t *ngm,
|
||||
uint8_t index,
|
||||
uint8_t nodeId,
|
||||
uint16_t guardTime_ms)
|
||||
{
|
||||
if (ngm == NULL || index >= CO_CONFIG_NODE_GUARDING_MASTER_COUNT
|
||||
|| nodeId < 1 || nodeId > 0x7F
|
||||
) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
CO_nodeGuardingMasterNode_t *node = &ngm->nodes[index];
|
||||
|
||||
node->guardTime_us = (uint32_t)guardTime_ms * 1000;
|
||||
node->guardTimer = 0;
|
||||
node->ident = CO_CAN_ID_HEARTBEAT + nodeId;
|
||||
node->NMTstate = CO_NMT_UNKNOWN; /* for the first time */
|
||||
node->toggle = false;
|
||||
node->responseRecived = true; /* for the first time */
|
||||
node->CANtxWasBusy = false;
|
||||
node->monitoringActive = false;
|
||||
|
||||
#if CO_CONFIG_NODE_GUARDING_MASTER_COUNT == 1
|
||||
ngm->CANtxBuff = CO_CANtxBufferInit(ngm->CANdevTx,
|
||||
ngm->CANdevTxIdx,
|
||||
node->ident,
|
||||
true, 1, 0);
|
||||
#endif
|
||||
|
||||
return CO_ERROR_NO;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_nodeGuardingMaster_process(CO_nodeGuardingMaster_t *ngm,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *timerNext_us)
|
||||
{
|
||||
(void)timerNext_us; /* may be unused */
|
||||
bool_t allMonitoredActiveCurrent = true;
|
||||
bool_t allMonitoredOperationalCurrent = true;
|
||||
CO_nodeGuardingMasterNode_t *node = &ngm->nodes[0];
|
||||
|
||||
for (uint8_t i=0; i<CO_CONFIG_NODE_GUARDING_MASTER_COUNT; i++) {
|
||||
if (node->guardTime_us > 0 && node->ident > CO_CAN_ID_HEARTBEAT) {
|
||||
if (timeDifference_us < node->guardTimer) {
|
||||
node->guardTimer -= timeDifference_us;
|
||||
#if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
|
||||
/* Calculate, when timeout expires */
|
||||
if (timerNext_us != NULL && *timerNext_us > node->guardTimer) {
|
||||
*timerNext_us = node->guardTimer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* it is time to send new rtr, but first verify last response */
|
||||
if (!node->CANtxWasBusy) {
|
||||
if (!node->responseRecived) {
|
||||
node->monitoringActive = false;
|
||||
/* error bit is shared with HB consumer */
|
||||
CO_errorReport(ngm->em, CO_EM_HEARTBEAT_CONSUMER,
|
||||
CO_EMC_HEARTBEAT, node->ident & 0x7F);
|
||||
}
|
||||
else if (node->NMTstate != CO_NMT_UNKNOWN) {
|
||||
node->monitoringActive = true;
|
||||
CO_errorReset(ngm->em, CO_EM_HEARTBEAT_CONSUMER,
|
||||
node->ident & 0x7F);
|
||||
}
|
||||
}
|
||||
|
||||
if (ngm->CANtxBuff->bufferFull) {
|
||||
node->guardTimer = 0;
|
||||
node->CANtxWasBusy = true;
|
||||
}
|
||||
else {
|
||||
#if CO_CONFIG_NODE_GUARDING_MASTER_COUNT > 1
|
||||
ngm->CANtxBuff = CO_CANtxBufferInit(ngm->CANdevTx,
|
||||
ngm->CANdevTxIdx,
|
||||
node->ident,
|
||||
true, 1, 0);
|
||||
#endif
|
||||
CO_CANsend(ngm->CANdevTx, ngm->CANtxBuff);
|
||||
node->CANtxWasBusy = false;
|
||||
node->responseRecived = false;
|
||||
node->guardTimer = node->guardTime_us;
|
||||
}
|
||||
}
|
||||
|
||||
if (allMonitoredActiveCurrent) {
|
||||
if (node->monitoringActive) {
|
||||
if (node->NMTstate != CO_NMT_OPERATIONAL) {
|
||||
allMonitoredOperationalCurrent = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
allMonitoredActiveCurrent = false;
|
||||
allMonitoredOperationalCurrent = false;
|
||||
}
|
||||
}
|
||||
} /* if node enabled */
|
||||
|
||||
node ++;
|
||||
} /* for */
|
||||
|
||||
ngm->allMonitoredActive = allMonitoredActiveCurrent;
|
||||
ngm->allMonitoredOperational = allMonitoredOperationalCurrent;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE */
|
||||
309
301/CO_Node_Guarding.h
Normal file
309
301/CO_Node_Guarding.h
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/**
|
||||
* CANopen Node Guarding slave and master objects.
|
||||
*
|
||||
* @file CO_Node_Guarding.h
|
||||
* @ingroup CO_Node_Guarding
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2023 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CO_NODE_GUARDING_H
|
||||
#define CO_NODE_GUARDING_H
|
||||
|
||||
#include "301/CO_driver.h"
|
||||
#include "301/CO_ODinterface.h"
|
||||
#include "301/CO_Emergency.h"
|
||||
#include "301/CO_NMT_Heartbeat.h"
|
||||
|
||||
/* default configuration, see CO_config.h */
|
||||
#ifndef CO_CONFIG_NODE_GUARDING
|
||||
#define CO_CONFIG_NODE_GUARDING (0)
|
||||
#endif
|
||||
#ifndef CO_CONFIG_NODE_GUARDING_MASTER_COUNT
|
||||
#define CO_CONFIG_NODE_GUARDING_MASTER_COUNT 0x7F
|
||||
#endif
|
||||
|
||||
#if ((CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) || defined CO_DOXYGEN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_Node_Guarding Node Guarding
|
||||
* CANopen Node Guarding, an older alternative to the Heartbeat protocol.
|
||||
*
|
||||
* @ingroup CO_CANopen_301
|
||||
* @{
|
||||
* Node guarding master pools each node guarding slave at time intervals, called
|
||||
* guard time. Master sends a CAN RTR message, and slave responds. Slave also
|
||||
* monitors presence of RTR message from master and indicates error, if it
|
||||
* wasn't received within life time. ('Life time' is 'Guard time' multiplied by
|
||||
* 'Life time factor').
|
||||
*
|
||||
* Adding Node guarding to the project:
|
||||
* - Make sure, driver supports it. RTR bit should be part of CAN identifier.
|
||||
* - Enable it with 'CO_CONFIG_NODE_GUARDING', see CO_config.h
|
||||
* - For slave add 0x100C and 0x100D objects to the Object dictionary.
|
||||
* - For master use CO_nodeGuardingMaster_initNode() to add monitored nodes.
|
||||
*
|
||||
* @warning Usage of Node guarding is not recommended, as it is outdated and
|
||||
* uses RTR CAN functionality, which is also not recommended. Use Heartbeat and
|
||||
* Heartbeat consumer, if possible.
|
||||
*
|
||||
* ### Node Guarding slave response message contents:
|
||||
*
|
||||
* Byte, bits | Description
|
||||
* ---------------|-----------------------------------------------------------
|
||||
* 0, bits 0..6 | @ref CO_NMT_internalState_t
|
||||
* 0, bit 7 | toggle bit
|
||||
*
|
||||
* See @ref CO_Default_CAN_ID_t for CAN identifiers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Node Guarding slave object
|
||||
*/
|
||||
typedef struct {
|
||||
/** From CO_nodeGuardingSlave_init() */
|
||||
CO_EM_t *em;
|
||||
/** Indicates, if new rtr message received from CAN bus */
|
||||
volatile void *CANrxNew;
|
||||
/** Guard time in microseconds, calculated from OD_0x100C */
|
||||
uint32_t guardTime_us;
|
||||
/** Life time in microseconds, calculated from guardTime_us * lifeTimeFactor */
|
||||
uint32_t lifeTime_us;
|
||||
/** Timer for monitoring Life time, counting down from lifeTime_us. */
|
||||
uint32_t lifeTimer;
|
||||
/** Life time factor, from OD_0x100D */
|
||||
uint8_t lifeTimeFactor;
|
||||
/** Toggle bit for response */
|
||||
bool_t toggle;
|
||||
/** True if rtr from master is missing */
|
||||
bool_t lifeTimeTimeout;
|
||||
/** Extension for OD object */
|
||||
OD_extension_t OD_100C_extension;
|
||||
/** Extension for OD object */
|
||||
OD_extension_t OD_100D_extension;
|
||||
/** From CO_nodeGuardingSlave_init() */
|
||||
CO_CANmodule_t *CANdevTx;
|
||||
/** CAN transmit buffer for the message */
|
||||
CO_CANtx_t *CANtxBuff;
|
||||
} CO_nodeGuardingSlave_t;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize Node Guarding slave object.
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param ngs This object will be initialized.
|
||||
* @param OD_100C_GuardTime OD entry for 0x100C -"Guard time",
|
||||
* entry is required.
|
||||
* @param OD_100D_LifeTimeFactor OD entry for 0x100D -"Life time factor",
|
||||
* entry is required.
|
||||
* @param em Emergency object.
|
||||
* @param CANidNodeGuarding CAN identifier for Node Guarding rtr and response
|
||||
* message (usually CO_CAN_ID_HEARTBEAT + nodeId).
|
||||
* @param CANdevRx CAN device for Node Guarding rtr reception.
|
||||
* @param CANdevRxIdx Index of the receive buffer in the above CAN device.
|
||||
* @param CANdevTx CAN device for Node Guarding response transmission.
|
||||
* @param CANdevTxIdx Index of the transmit buffer in the above CAN device.
|
||||
* @param [out] errInfo Additional information in case of error, may be NULL.
|
||||
*
|
||||
* @return #CO_ReturnError_t CO_ERROR_NO on success.
|
||||
*/
|
||||
CO_ReturnError_t CO_nodeGuardingSlave_init(CO_nodeGuardingSlave_t *ngs,
|
||||
OD_entry_t *OD_100C_GuardTime,
|
||||
OD_entry_t *OD_100D_LifeTimeFactor,
|
||||
CO_EM_t *em,
|
||||
uint16_t CANidNodeGuarding,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx,
|
||||
uint32_t *errInfo);
|
||||
|
||||
|
||||
/**
|
||||
* Process Node Guarding slave.
|
||||
*
|
||||
* Function must be called cyclically.
|
||||
*
|
||||
* @param ngs This object.
|
||||
* @param NMTstate NMT operating state.
|
||||
* @param timeDifference_us Time difference from previous function call in
|
||||
* microseconds.
|
||||
* @param [out] timerNext_us info to OS - see CO_process().
|
||||
*/
|
||||
void CO_nodeGuardingSlave_process(CO_nodeGuardingSlave_t *ngs,
|
||||
CO_NMT_internalState_t NMTstate,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *timerNext_us);
|
||||
|
||||
|
||||
/**
|
||||
* Inquire, if Node guarding slave detected life time timeout
|
||||
*
|
||||
* Error is reset after pool request from master.
|
||||
*
|
||||
* @param ngs This object.
|
||||
*
|
||||
* @return true, if life time timeout was detected.
|
||||
*/
|
||||
static inline bool_t CO_nodeGuardingSlave_isTimeout(CO_nodeGuardingSlave_t *ngs)
|
||||
{
|
||||
return (ngs == NULL) || ngs->lifeTimeTimeout;
|
||||
}
|
||||
|
||||
|
||||
/** @} */ /* CO_Node_Guarding */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE */
|
||||
|
||||
|
||||
|
||||
|
||||
#if ((CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) || defined CO_DOXYGEN
|
||||
|
||||
#if CO_CONFIG_NODE_GUARDING_MASTER_COUNT < 1 || CO_CONFIG_NODE_GUARDING_MASTER_COUNT > 127
|
||||
#error CO_CONFIG_NODE_GUARDING_MASTER_COUNT value is wrong!
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup CO_Node_Guarding
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Node Guarding master - monitored node
|
||||
*/
|
||||
typedef struct {
|
||||
/** Guard time in microseconds */
|
||||
uint32_t guardTime_us;
|
||||
/** Guard timer in microseconds, counting down */
|
||||
uint32_t guardTimer;
|
||||
/** CAN identifier (CO_CAN_ID_HEARTBEAT + Node Id) */
|
||||
uint16_t ident;
|
||||
/** NMT operating state */
|
||||
CO_NMT_internalState_t NMTstate;
|
||||
/** toggle bit7, expected from the next received message */
|
||||
uint8_t toggle;
|
||||
/** True, if response was received since last rtr message */
|
||||
bool_t responseRecived;
|
||||
/** True, if CANtxBuff was busy since last processing */
|
||||
bool_t CANtxWasBusy;
|
||||
/** True, if monitoring is active (response within time). */
|
||||
bool_t monitoringActive;
|
||||
} CO_nodeGuardingMasterNode_t;
|
||||
|
||||
/**
|
||||
* Node Guarding master object
|
||||
*/
|
||||
typedef struct {
|
||||
/** From CO_nodeGuardingMaster_init() */
|
||||
CO_EM_t *em;
|
||||
/** From CO_nodeGuardingMaster_init() */
|
||||
CO_CANmodule_t *CANdevTx;
|
||||
/** From CO_nodeGuardingMaster_init() */
|
||||
uint16_t CANdevTxIdx;
|
||||
/** CAN transmit buffer for the message */
|
||||
CO_CANtx_t *CANtxBuff;
|
||||
/** True, if all monitored nodes are active or no node is monitored. Can be
|
||||
* read by the application */
|
||||
bool_t allMonitoredActive;
|
||||
/** True, if all monitored nodes are NMT operational or no node is
|
||||
* monitored. Can be read by the application */
|
||||
bool_t allMonitoredOperational;
|
||||
/** Array of monitored nodes */
|
||||
CO_nodeGuardingMasterNode_t nodes[CO_CONFIG_NODE_GUARDING_MASTER_COUNT];
|
||||
} CO_nodeGuardingMaster_t;
|
||||
|
||||
/**
|
||||
* Initialize Node Guarding master object.
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param ngm This object will be initialized.
|
||||
* @param em Emergency object.
|
||||
* @param CANdevRx CAN device for Node Guarding reception.
|
||||
* @param CANdevRxIdx Index of the receive buffer in the above CAN device.
|
||||
* @param CANdevTx CAN device for Node Guarding rtr transmission.
|
||||
* @param CANdevTxIdx Index of the transmit buffer in the above CAN device.
|
||||
*
|
||||
* @return #CO_ReturnError_t CO_ERROR_NO on success.
|
||||
*/
|
||||
CO_ReturnError_t CO_nodeGuardingMaster_init(CO_nodeGuardingMaster_t *ngm,
|
||||
CO_EM_t *em,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx);
|
||||
|
||||
/**
|
||||
* Initialize node inside Node Guarding master object.
|
||||
*
|
||||
* Function may be called any time after CO_nodeGuardingMaster_init(). It
|
||||
* configures monitoring of the remote node.
|
||||
*
|
||||
* @param ngm Node Guarding master object.
|
||||
* @param em Emergency object.
|
||||
* @param index Index of the slot, which will be configured.
|
||||
* 0 <= index < CO_CONFIG_NODE_GUARDING_MASTER_COUNT.
|
||||
* @param nodeId Node Id of the monitored node.
|
||||
* @param guardTime_ms Guard time of the monitored node.
|
||||
*
|
||||
* @return #CO_ReturnError_t CO_ERROR_NO on success.
|
||||
*/
|
||||
CO_ReturnError_t CO_nodeGuardingMaster_initNode(CO_nodeGuardingMaster_t *ngm,
|
||||
uint8_t index,
|
||||
uint8_t nodeId,
|
||||
uint16_t guardTime_ms);
|
||||
|
||||
/**
|
||||
* Process Node Guarding master.
|
||||
*
|
||||
* Function must be called cyclically.
|
||||
*
|
||||
* @param ngm This object.
|
||||
* @param timeDifference_us Time difference from previous function call in
|
||||
* microseconds.
|
||||
* @param [out] timerNext_us info to OS - see CO_process().
|
||||
*/
|
||||
void CO_nodeGuardingMaster_process(CO_nodeGuardingMaster_t *ngm,
|
||||
uint32_t timeDifference_us,
|
||||
uint32_t *timerNext_us);
|
||||
|
||||
/** @} */ /* @addtogroup CO_Node_Guarding */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /* (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE */
|
||||
|
||||
#endif /* CO_NODE_GUARDING_H */
|
||||
|
|
@ -186,6 +186,35 @@ extern "C" {
|
|||
/** @} */ /* CO_STACK_CONFIG_NMT_HB */
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup CO_STACK_CONFIG_NODE_GUARDING CANopen Node Guarding slave and master objects.
|
||||
* Specified in standard CiA 301
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* Configuration of @ref CO_Node_Guarding
|
||||
*
|
||||
* Possible flags, can be ORed:
|
||||
* - CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE - Enable Node guarding slave.
|
||||
* - CO_CONFIG_NODE_GUARDING_MASTER_ENABLE - Enable Node guarding master.
|
||||
* - #CO_CONFIG_FLAG_TIMERNEXT - Enable calculation of timerNext_us variable
|
||||
* inside CO_nodeGuardingSlave_process().
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_NODE_GUARDING (0)
|
||||
#endif
|
||||
#define CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE 0x01
|
||||
#define CO_CONFIG_NODE_GUARDING_MASTER_ENABLE 0x02
|
||||
|
||||
/**
|
||||
* Maximum number of nodes monitored by master
|
||||
*/
|
||||
#ifdef CO_DOXYGEN
|
||||
#define CO_CONFIG_NODE_GUARDING_MASTER_COUNT 0x7F
|
||||
#endif
|
||||
/** @} */ /* CO_STACK_CONFIG_NODE_GUARDING */
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup CO_STACK_CONFIG_EMERGENCY Emergency producer/consumer
|
||||
* Specified in standard CiA 301
|
||||
|
|
|
|||
99
CANopen.c
99
CANopen.c
|
|
@ -4,7 +4,7 @@
|
|||
* @file CANopen.c
|
||||
* @ingroup CO_CANopen
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2010 - 2020 Janez Paternoster
|
||||
* @copyright 2010 - 2023 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
|
|
@ -72,6 +72,21 @@
|
|||
#define CO_RX_CNT_HB_CONS 0
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
#define CO_RX_CNT_NG_SLV 1
|
||||
#define CO_TX_CNT_NG_SLV 1
|
||||
#else
|
||||
#define CO_RX_CNT_NG_SLV 0
|
||||
#define CO_TX_CNT_NG_SLV 0
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
#define CO_RX_CNT_NG_MST 1
|
||||
#define CO_TX_CNT_NG_MST 1
|
||||
#else
|
||||
#define CO_RX_CNT_NG_MST 0
|
||||
#define CO_TX_CNT_NG_MST 0
|
||||
#endif
|
||||
|
||||
#if OD_CNT_EM != 1
|
||||
#error OD_CNT_EM from OD.h not correct!
|
||||
#endif
|
||||
|
|
@ -267,7 +282,9 @@
|
|||
#define CO_RX_IDX_SDO_SRV (CO_RX_IDX_RPDO + CO_RX_CNT_RPDO)
|
||||
#define CO_RX_IDX_SDO_CLI (CO_RX_IDX_SDO_SRV + CO_RX_CNT_SDO_SRV)
|
||||
#define CO_RX_IDX_HB_CONS (CO_RX_IDX_SDO_CLI + CO_RX_CNT_SDO_CLI)
|
||||
#define CO_RX_IDX_LSS_SLV (CO_RX_IDX_HB_CONS + CO_RX_CNT_HB_CONS)
|
||||
#define CO_RX_IDX_NG_SLV (CO_RX_IDX_HB_CONS + CO_RX_CNT_HB_CONS)
|
||||
#define CO_RX_IDX_NG_MST (CO_RX_IDX_NG_SLV + CO_RX_CNT_NG_SLV)
|
||||
#define CO_RX_IDX_LSS_SLV (CO_RX_IDX_NG_MST + CO_RX_CNT_NG_MST)
|
||||
#define CO_RX_IDX_LSS_MST (CO_RX_IDX_LSS_SLV + CO_RX_CNT_LSS_SLV)
|
||||
#define CO_CNT_ALL_RX_MSGS (CO_RX_IDX_LSS_MST + CO_RX_CNT_LSS_MST)
|
||||
|
||||
|
|
@ -281,7 +298,9 @@
|
|||
#define CO_TX_IDX_SDO_SRV (CO_TX_IDX_TPDO + CO_TX_CNT_TPDO)
|
||||
#define CO_TX_IDX_SDO_CLI (CO_TX_IDX_SDO_SRV + CO_TX_CNT_SDO_SRV)
|
||||
#define CO_TX_IDX_HB_PROD (CO_TX_IDX_SDO_CLI + CO_TX_CNT_SDO_CLI)
|
||||
#define CO_TX_IDX_LSS_SLV (CO_TX_IDX_HB_PROD + CO_TX_CNT_HB_PROD)
|
||||
#define CO_TX_IDX_NG_SLV (CO_TX_IDX_HB_PROD + CO_TX_CNT_HB_PROD)
|
||||
#define CO_TX_IDX_NG_MST (CO_TX_IDX_NG_SLV + CO_TX_CNT_NG_SLV)
|
||||
#define CO_TX_IDX_LSS_SLV (CO_TX_IDX_NG_MST + CO_TX_CNT_NG_MST)
|
||||
#define CO_TX_IDX_LSS_MST (CO_TX_IDX_LSS_SLV + CO_TX_CNT_LSS_SLV)
|
||||
#define CO_CNT_ALL_TX_MSGS (CO_TX_IDX_LSS_MST + CO_TX_CNT_LSS_MST)
|
||||
#endif /* #ifdef #else CO_MULTIPLE_OD */
|
||||
|
|
@ -383,6 +402,14 @@ CO_t *CO_new(CO_config_t *config, uint32_t *heapMemoryUsed) {
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Node guarding */
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
CO_alloc_break_on_fail(co->NGslave, 1, sizeof(*co->NGslave));
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
CO_alloc_break_on_fail(co->NGmaster, 1, sizeof(*co->NGmaster));
|
||||
#endif
|
||||
|
||||
/* Emergency */
|
||||
ON_MULTI_OD(uint8_t RX_CNT_EM_CONS = 0);
|
||||
ON_MULTI_OD(uint8_t TX_CNT_EM_PROD = 0);
|
||||
|
|
@ -549,6 +576,12 @@ CO_t *CO_new(CO_config_t *config, uint32_t *heapMemoryUsed) {
|
|||
#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE
|
||||
co->RX_IDX_HB_CONS = idxRx; idxRx += RX_CNT_HB_CONS;
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
co->RX_IDX_NG_SLV = idxRx; idxRx += 1;
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
co->RX_IDX_NG_MST = idxRx; idxRx += 1;
|
||||
#endif
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE
|
||||
co->RX_IDX_LSS_SLV = idxRx; idxRx += RX_CNT_LSS_SLV;
|
||||
#endif
|
||||
|
|
@ -580,6 +613,12 @@ CO_t *CO_new(CO_config_t *config, uint32_t *heapMemoryUsed) {
|
|||
co->TX_IDX_SDO_CLI = idxTx; idxTx += TX_CNT_SDO_CLI;
|
||||
#endif
|
||||
co->TX_IDX_HB_PROD = idxTx; idxTx += TX_CNT_HB_PROD;
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
co->TX_IDX_NG_SLV = idxTx; idxTx += 1;
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
co->TX_IDX_NG_MST = idxTx; idxTx += 1;
|
||||
#endif
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE
|
||||
co->TX_IDX_LSS_SLV = idxTx; idxTx += TX_CNT_LSS_SLV;
|
||||
#endif
|
||||
|
|
@ -682,6 +721,13 @@ void CO_delete(CO_t *co) {
|
|||
CO_free(co->em_fifo);
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
CO_free(co->NGslave);
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
CO_free(co->NGmaster);
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE
|
||||
CO_free(co->HBconsMonitoredNodes);
|
||||
CO_free(co->HBcons);
|
||||
|
|
@ -709,6 +755,12 @@ void CO_delete(CO_t *co) {
|
|||
#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE
|
||||
static CO_HBconsumer_t COO_HBcons;
|
||||
static CO_HBconsNode_t COO_HBconsMonitoredNodes[OD_CNT_ARR_1016];
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
static CO_nodeGuardingSlave_t COO_NGslave;
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
static CO_nodeGuardingMaster_t COO_NGmaster;
|
||||
#endif
|
||||
static CO_EM_t COO_EM;
|
||||
#if (CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)
|
||||
|
|
@ -771,6 +823,12 @@ CO_t *CO_new(CO_config_t *config, uint32_t *heapMemoryUsed) {
|
|||
#if (CO_CONFIG_HB_CONS) & CO_CONFIG_HB_CONS_ENABLE
|
||||
co->HBcons = &COO_HBcons;
|
||||
co->HBconsMonitoredNodes = &COO_HBconsMonitoredNodes[0];
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
co->NGslave = &COO_NGslave;
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
co->NGmaster = &COO_NGmaster;
|
||||
#endif
|
||||
co->em = &COO_EM;
|
||||
#if (CO_CONFIG_EM) & (CO_CONFIG_EM_PRODUCER | CO_CONFIG_EM_HISTORY)
|
||||
|
|
@ -1016,6 +1074,29 @@ CO_ReturnError_t CO_CANopenInit(CO_t *co,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
err = CO_nodeGuardingSlave_init(co->NGslave,
|
||||
OD_GET(H100C, OD_H100C_GUARD_TIME),
|
||||
OD_GET(H100D, OD_H100D_LIFETIME_FACTOR),
|
||||
em,
|
||||
CO_CAN_ID_HEARTBEAT + nodeId,
|
||||
co->CANmodule,
|
||||
CO_GET_CO(RX_IDX_NG_SLV),
|
||||
co->CANmodule,
|
||||
CO_GET_CO(TX_IDX_NG_SLV),
|
||||
errInfo);
|
||||
if (err) return err;
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
err = CO_nodeGuardingMaster_init(co->NGmaster,
|
||||
em,
|
||||
co->CANmodule,
|
||||
CO_GET_CO(RX_IDX_NG_MST),
|
||||
co->CANmodule,
|
||||
CO_GET_CO(TX_IDX_NG_MST));
|
||||
if (err) return err;
|
||||
#endif
|
||||
|
||||
/* SDOserver */
|
||||
if (CO_GET_CNT(SDO_SRV) > 0) {
|
||||
OD_entry_t *SDOsrvPar = OD_GET(H1200, OD_H1200_SDO_SERVER_1_PARAM);
|
||||
|
|
@ -1385,6 +1466,18 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
|
||||
CO_nodeGuardingSlave_process(co->NGslave,
|
||||
NMTstate,
|
||||
timeDifference_us,
|
||||
timerNext_us);
|
||||
#endif
|
||||
#if (CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
|
||||
CO_nodeGuardingMaster_process(co->NGmaster,
|
||||
timeDifference_us,
|
||||
timerNext_us);
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_TIME) & CO_CONFIG_TIME_ENABLE
|
||||
if (CO_GET_CNT(TIME) == 1) {
|
||||
CO_TIME_process(co->TIME, NMTisPreOrOperational, timeDifference_us);
|
||||
|
|
|
|||
23
CANopen.h
23
CANopen.h
|
|
@ -5,7 +5,7 @@
|
|||
* @ingroup CO_CANopen
|
||||
* @author Janez Paternoster
|
||||
* @author Uwe Kindler
|
||||
* @copyright 2010 - 2020 Janez Paternoster
|
||||
* @copyright 2010 - 2023 Janez Paternoster
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
#include "301/CO_ODinterface.h"
|
||||
#include "301/CO_NMT_Heartbeat.h"
|
||||
#include "301/CO_HBconsumer.h"
|
||||
#include "301/CO_Node_Guarding.h"
|
||||
#include "301/CO_Emergency.h"
|
||||
#include "301/CO_SDOserver.h"
|
||||
#include "301/CO_SDOclient.h"
|
||||
|
|
@ -207,6 +208,10 @@ typedef struct {
|
|||
* object, 1 to 127. */
|
||||
uint8_t CNT_ARR_1016;
|
||||
OD_entry_t *ENTRY_H1016; /**< OD entry for @ref CO_HBconsumer_init()*/
|
||||
/** OD entry for @ref CO_nodeGuardingSlave_init() */
|
||||
OD_entry_t *ENTRY_H100C;
|
||||
/** OD entry for @ref CO_nodeGuardingSlave_init() */
|
||||
OD_entry_t *ENTRY_H100D;
|
||||
/** Number of Emergency objects, 0 or 1: optional producer (CANtx) +
|
||||
* optional consumer (CANrx), configurable by @ref CO_CONFIG_EM.
|
||||
* There must be one Emergency object in the device. */
|
||||
|
|
@ -303,6 +308,22 @@ typedef struct {
|
|||
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
|
||||
uint16_t RX_IDX_HB_CONS; /**< Start index in CANrx. */
|
||||
#endif
|
||||
#endif
|
||||
#if ((CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE) || defined CO_DOXYGEN
|
||||
/** Node guarding slave object, initialised by @ref CO_nodeGuardingSlave_init() */
|
||||
CO_nodeGuardingSlave_t *NGslave;
|
||||
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
|
||||
uint16_t RX_IDX_NG_SLV; /**< Start index in CANrx. */
|
||||
uint16_t TX_IDX_NG_SLV; /**< Start index in CANtx. */
|
||||
#endif
|
||||
#endif
|
||||
#if ((CO_CONFIG_NODE_GUARDING) & CO_CONFIG_NODE_GUARDING_MASTER_ENABLE) || defined CO_DOXYGEN
|
||||
/** Node guarding master object, initialised by @ref CO_nodeGuardingMaster_init() */
|
||||
CO_nodeGuardingMaster_t *NGmaster;
|
||||
#if defined CO_MULTIPLE_OD || defined CO_DOXYGEN
|
||||
uint16_t RX_IDX_NG_MST; /**< Start index in CANrx. */
|
||||
uint16_t TX_IDX_NG_MST; /**< Start index in CANtx. */
|
||||
#endif
|
||||
#endif
|
||||
/** Emergency object, initialised by @ref CO_EM_init() */
|
||||
CO_EM_t *em;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ Characteristics
|
|||
### CANopen
|
||||
- [Object Dictionary](https://www.can-cia.org/can-knowledge/canopen/device-architecture/) offers clear and flexible organisation of any variables. Variables can be accessed directly or via read/write functions.
|
||||
- [NMT](https://www.can-cia.org/can-knowledge/canopen/network-management/) slave to start, stop, reset device. Simple NMT master.
|
||||
- [Heartbeat](https://www.can-cia.org/can-knowledge/canopen/error-control-protocols/) producer/consumer error control for monitoring of CANopen devices.
|
||||
- [Heartbeat](https://www.can-cia.org/can-knowledge/canopen/error-control-protocols/) producer/consumer error control for monitoring of CANopen devices. An older alternative, 'node guarding', is also available.
|
||||
- [PDO](https://www.can-cia.org/can-knowledge/canopen/pdo-protocol/) for broadcasting process data with high priority and no protocol overhead. Variables from Object Dictionary can be dynamically mapped to the TPDO, which is then transmitted according to communication rules and received as RPDO by another device.
|
||||
- [SDO](https://www.can-cia.org/can-knowledge/canopen/sdo-protocol/) server enables expedited, segmented and block transfer access to all Object Dictionary variables inside CANopen device.
|
||||
- [SDO](https://www.can-cia.org/can-knowledge/canopen/sdo-protocol/) client can access any Object Dictionary variable on any CANopen device inside the network.
|
||||
|
|
@ -200,7 +200,7 @@ It is not practical to have all device interfaces in a single project. Interface
|
|||
Some details
|
||||
------------
|
||||
### RTR
|
||||
RTR (remote transmission request) is a feature of CAN bus. Usage of RTR is not recommended for CANopen and it is not implemented in CANopenNode.
|
||||
RTR (remote transmission request) is a feature of CAN bus. Usage of RTR is not recommended for CANopen. RTR PDO is not implemented in CANopenNode.
|
||||
|
||||
### Error control
|
||||
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 may not be allowed.
|
||||
|
|
|
|||
|
|
@ -467,8 +467,8 @@ Object Dictionary Requirements By CANopenNode {#object-dictionary-requirements-b
|
|||
| 1008 | Manufacturer device name | | |
|
||||
| 1009 | Manufacturer hardware version | | |
|
||||
| 100A | Manufacturer software version | | |
|
||||
| 100C | Guard time | | |
|
||||
| 100D | Life time factor | | |
|
||||
| 100C | Guard time | Node guarding slave | |
|
||||
| 100D | Life time factor | Node guarding slave | |
|
||||
| 1010 | Store parameters | | STORAGE |
|
||||
| 1011 | Restore default parameters | | |
|
||||
| 1012 | COB-ID time stamp object | TIME, req | TIME |
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--File is generated by CANopenEditor v4.1-0-gb76e55f, URL: https://github.com/CANopenNode/CANopenEditor-->
|
||||
<!--File is generated by CANopenEditor v4.1-2-gff637a7, URL: https://github.com/CANopenNode/CANopenEditor-->
|
||||
<!--File includes additional custom properties for CANopenNode, CANopen protocol stack, URL: https://github.com/CANopenNode/CANopenNode-->
|
||||
<ISO15745ProfileContainer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.canopen.org/xml/1.1">
|
||||
<ISO15745Profile>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
<ProfileTechnology>CANopen</ProfileTechnology>
|
||||
</ISO15745Reference>
|
||||
</ProfileHeader>
|
||||
<ProfileBody xmlns:q1="http://www.canopen.org/xml/1.1" xsi:type="q1:ProfileBody_Device_CANopen" formatName="CANopen" formatVersion="1.0" fileName="DS301_profile.xpd" fileCreator="" fileCreationDate="2020-11-23" fileCreationTime="12:00:00.0000000+01:00" fileModifiedBy="" fileModificationDate="2023-03-17" fileModificationTime="02:07:28.5252460+01:00" fileVersion="1" supportedLanguages="en" xmlns="">
|
||||
<ProfileBody xmlns:q1="http://www.canopen.org/xml/1.1" xsi:type="q1:ProfileBody_Device_CANopen" formatName="CANopen" formatVersion="1.0" fileName="DS301_profile.xpd" fileCreator="" fileCreationDate="2020-11-23" fileCreationTime="13:00:00.0000000+02:00" fileModifiedBy="" fileModificationDate="2023-09-05" fileModificationTime="15:39:22.9764490+02:00" fileVersion="1" supportedLanguages="en" xmlns="">
|
||||
<q1:DeviceIdentity>
|
||||
<q1:vendorName></q1:vendorName>
|
||||
<q1:vendorID></q1:vendorID>
|
||||
|
|
@ -769,6 +769,20 @@
|
|||
<q1:property name="CO_disabled" value="true" />
|
||||
<q1:property name="CO_storageGroup" value="PERSIST_COMM" />
|
||||
</q1:parameter>
|
||||
<q1:parameter uniqueID="UID_OBJ_100C" access="readWrite">
|
||||
<label lang="en">Guard time</label>
|
||||
<UINT />
|
||||
<q1:defaultValue value="0" />
|
||||
<q1:property name="CO_disabled" value="true" />
|
||||
<q1:property name="CO_storageGroup" value="PERSIST_COMM" />
|
||||
</q1:parameter>
|
||||
<q1:parameter uniqueID="UID_OBJ_100D" access="readWrite">
|
||||
<label lang="en">Life time factor</label>
|
||||
<USINT />
|
||||
<q1:defaultValue value="0" />
|
||||
<q1:property name="CO_disabled" value="true" />
|
||||
<q1:property name="CO_storageGroup" value="PERSIST_COMM" />
|
||||
</q1:parameter>
|
||||
<q1:parameter uniqueID="UID_OBJ_1010">
|
||||
<description lang="en">Sub-indexes 1 and above:
|
||||
* Reading provides information about its storage functionality:
|
||||
|
|
@ -2277,7 +2291,7 @@
|
|||
<ProfileTechnology>CANopen</ProfileTechnology>
|
||||
</ISO15745Reference>
|
||||
</ProfileHeader>
|
||||
<ProfileBody xmlns:q2="http://www.canopen.org/xml/1.1" xsi:type="q2:ProfileBody_CommunicationNetwork_CANopen" formatName="CANopen" formatVersion="1.0" fileName="DS301_profile.xpd" fileCreator="" fileCreationDate="2020-11-23" fileCreationTime="12:00:00.0000000+01:00" fileModificationDate="2023-03-17" fileModificationTime="02:07:28.5252460+01:00" fileVersion="1" supportedLanguages="en" xmlns="">
|
||||
<ProfileBody xmlns:q2="http://www.canopen.org/xml/1.1" xsi:type="q2:ProfileBody_CommunicationNetwork_CANopen" formatName="CANopen" formatVersion="1.0" fileName="DS301_profile.xpd" fileCreator="" fileCreationDate="2020-11-23" fileCreationTime="13:00:00.0000000+02:00" fileModificationDate="2023-09-05" fileModificationTime="15:39:22.9764490+02:00" fileVersion="1" supportedLanguages="en" xmlns="">
|
||||
<ApplicationLayers>
|
||||
<q2:CANopenObjectList>
|
||||
<CANopenObject index="1000" name="Device type" objectType="7" PDOmapping="no" uniqueIDRef="UID_OBJ_1000" />
|
||||
|
|
@ -2308,6 +2322,8 @@
|
|||
<CANopenObject index="1008" name="Manufacturer device name" objectType="7" PDOmapping="no" uniqueIDRef="UID_OBJ_1008" />
|
||||
<CANopenObject index="1009" name="Manufacturer hardware version" objectType="7" PDOmapping="no" uniqueIDRef="UID_OBJ_1009" />
|
||||
<CANopenObject index="100A" name="Manufacturer software version" objectType="7" PDOmapping="no" uniqueIDRef="UID_OBJ_100A" />
|
||||
<CANopenObject index="100C" name="Guard time" objectType="7" PDOmapping="no" uniqueIDRef="UID_OBJ_100C" />
|
||||
<CANopenObject index="100D" name="Life time factor" objectType="7" PDOmapping="no" uniqueIDRef="UID_OBJ_100D" />
|
||||
<CANopenObject index="1010" name="Store parameters" objectType="8" uniqueIDRef="UID_OBJ_1010" subNumber="5">
|
||||
<CANopenSubObject subIndex="00" name="Highest sub-index supported" objectType="7" PDOmapping="no" uniqueIDRef="UID_SUB_101000" />
|
||||
<CANopenSubObject subIndex="01" name="Save all parameters" objectType="7" PDOmapping="no" uniqueIDRef="UID_SUB_101001" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue