Integrate LSS slave into CANopenNode more directly.
- CO_LSSslave: move expensive code from CAN receive (interrupt) to mainline CO_LSSslave_process() function. - LSS slave now runs in parallel to other CANopen objects. - LSS slave and master can run both on same device. - LSS slave, LSS master and gateway-ascii(CiA309) LSS functions tested. - LSSusage.md updated.
This commit is contained in:
parent
ae1b66e6ea
commit
dd1677b5bc
16 changed files with 712 additions and 802 deletions
|
|
@ -501,7 +501,10 @@ typedef enum {
|
|||
CO_ERROR_WRONG_NMT_STATE = -16, /**< Command can't be processed in current
|
||||
state */
|
||||
CO_ERROR_SYSCALL = -17, /**< Syscall failed */
|
||||
CO_ERROR_INVALID_STATE = -18 /**< Driver not ready */
|
||||
CO_ERROR_INVALID_STATE = -18, /**< Driver not ready */
|
||||
CO_ERROR_NODE_ID_UNCONFIGURED_LSS = -19 /**< Node-id is in LSS unconfigured
|
||||
state. If objects are handled properly,
|
||||
this may not be an error. */
|
||||
} CO_ReturnError_t;
|
||||
|
||||
|
||||
|
|
|
|||
10
305/CO_LSS.h
10
305/CO_LSS.h
|
|
@ -94,16 +94,6 @@ typedef enum {
|
|||
CO_LSS_INQUIRE_NODE_ID = 0x5EU, /**< Inquire node-ID protocol */
|
||||
} CO_LSS_cs_t;
|
||||
|
||||
/**
|
||||
* Macro to get service type group from command specifier
|
||||
* @{*/
|
||||
#define CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs) (cs == CO_LSS_SWITCH_STATE_GLOBAL)
|
||||
#define CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs) (cs >= CO_LSS_SWITCH_STATE_SEL_VENDOR && cs <= CO_LSS_SWITCH_STATE_SEL)
|
||||
#define CO_LSS_CS_SERVICE_IS_CONFIG(cs) (cs >= CO_LSS_CFG_NODE_ID && cs <= CO_LSS_CFG_STORE)
|
||||
#define CO_LSS_CS_SERVICE_IS_INQUIRE(cs) (cs >= CO_LSS_INQUIRE_VENDOR && cs <= CO_LSS_INQUIRE_NODE_ID)
|
||||
#define CO_LSS_CS_SERVICE_IS_IDENT(cs) (cs==CO_LSS_IDENT_SLAVE || cs==CO_LSS_IDENT_FASTSCAN)
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Error codes for Configure node ID protocol
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ typedef enum {
|
|||
* LSS master object.
|
||||
*/
|
||||
typedef struct{
|
||||
uint16_t timeout_us; /**< LSS response timeout in us */
|
||||
uint32_t timeout_us; /**< LSS response timeout in us */
|
||||
|
||||
uint8_t state; /**< Node is currently selected */
|
||||
uint8_t command; /**< Active command */
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* @file CO_LSSslave.c
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
|
|
@ -30,305 +31,6 @@
|
|||
#include "301/CO_SDOserver.h" /* for helper functions */
|
||||
#include "305/CO_LSSslave.h"
|
||||
|
||||
/*
|
||||
* Helper function - Handle service "switch state global"
|
||||
*/
|
||||
static void CO_LSSslave_serviceSwitchStateGlobal(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_cs_t service,
|
||||
void *msg)
|
||||
{
|
||||
(void)service; /* unused */
|
||||
|
||||
uint8_t *data = CO_CANrxMsg_readData(msg);
|
||||
uint8_t mode = data[1];
|
||||
|
||||
switch (mode) {
|
||||
case CO_LSS_STATE_WAITING:
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
memset(&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect));
|
||||
break;
|
||||
case CO_LSS_STATE_CONFIGURATION:
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - Handle service "switch state selective"
|
||||
*/
|
||||
static void CO_LSSslave_serviceSwitchStateSelective(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_cs_t service,
|
||||
void *msg)
|
||||
{
|
||||
uint32_t value;
|
||||
uint8_t *data = CO_CANrxMsg_readData(msg);
|
||||
CO_memcpySwap4(&value, &data[1]);
|
||||
|
||||
if(LSSslave->lssState != CO_LSS_STATE_WAITING) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (service) {
|
||||
case CO_LSS_SWITCH_STATE_SEL_VENDOR:
|
||||
LSSslave->lssSelect.identity.vendorID = value;
|
||||
break;
|
||||
case CO_LSS_SWITCH_STATE_SEL_PRODUCT:
|
||||
LSSslave->lssSelect.identity.productCode = value;
|
||||
break;
|
||||
case CO_LSS_SWITCH_STATE_SEL_REV:
|
||||
LSSslave->lssSelect.identity.revisionNumber = value;
|
||||
break;
|
||||
case CO_LSS_SWITCH_STATE_SEL_SERIAL:
|
||||
LSSslave->lssSelect.identity.serialNumber = value;
|
||||
|
||||
if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) {
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL;
|
||||
memset(&LSSslave->TXbuff->data[1], 0, sizeof(LSSslave->TXbuff->data) - 1);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - Handle service "configure"
|
||||
*
|
||||
* values inside message have different meaning, depending on the selected
|
||||
* configuration type
|
||||
*/
|
||||
static void CO_LSSslave_serviceConfig(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_cs_t service,
|
||||
void *msg)
|
||||
{
|
||||
uint8_t nid;
|
||||
uint8_t tableSelector;
|
||||
uint8_t tableIndex;
|
||||
uint8_t errorCode;
|
||||
uint8_t *data;
|
||||
|
||||
if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = CO_CANrxMsg_readData(msg);
|
||||
|
||||
switch (service) {
|
||||
case CO_LSS_CFG_NODE_ID:
|
||||
nid = data[1];
|
||||
errorCode = CO_LSS_CFG_NODE_ID_OK;
|
||||
|
||||
if (CO_LSS_NODE_ID_VALID(nid)) {
|
||||
LSSslave->pendingNodeID = nid;
|
||||
}
|
||||
else {
|
||||
errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_CFG_NODE_ID;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
memset(&LSSslave->TXbuff->data[2], 0, sizeof(LSSslave->TXbuff->data) - 2);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
break;
|
||||
case CO_LSS_CFG_BIT_TIMING:
|
||||
if (LSSslave->pFunctLSScheckBitRate == NULL) {
|
||||
/* setting bit timing is not supported. Drop request */
|
||||
break;
|
||||
}
|
||||
|
||||
tableSelector = data[1];
|
||||
tableIndex = data[2];
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OK;
|
||||
|
||||
if (tableSelector==0 && CO_LSS_BIT_TIMING_VALID(tableIndex)) {
|
||||
uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex];
|
||||
bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate(
|
||||
LSSslave->functLSScheckBitRateObject, bit);
|
||||
|
||||
if (bit_rate_supported) {
|
||||
LSSslave->pendingBitRate = bit;
|
||||
}
|
||||
else {
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* we currently only support CiA301 bit timing table */
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_CFG_BIT_TIMING;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
memset(&LSSslave->TXbuff->data[2], 0, sizeof(LSSslave->TXbuff->data) - 2);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
break;
|
||||
case CO_LSS_CFG_ACTIVATE_BIT_TIMING:
|
||||
if (LSSslave->pFunctLSScheckBitRate == NULL) {
|
||||
/* setting bit timing is not supported. Drop request */
|
||||
break;
|
||||
}
|
||||
|
||||
/* notify application */
|
||||
if (LSSslave->pFunctLSSactivateBitRate != NULL) {
|
||||
uint16_t delay;
|
||||
CO_memcpySwap2(&delay, &data[1]);
|
||||
LSSslave->pFunctLSSactivateBitRate(
|
||||
LSSslave->functLSSactivateBitRateObject, delay);
|
||||
}
|
||||
break;
|
||||
case CO_LSS_CFG_STORE:
|
||||
errorCode = CO_LSS_CFG_STORE_OK;
|
||||
|
||||
if (LSSslave->pFunctLSScfgStore == NULL) {
|
||||
/* storing is not supported. Reply error */
|
||||
errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED;
|
||||
}
|
||||
else {
|
||||
bool_t result;
|
||||
/* Store "pending" to "persistent" */
|
||||
result = LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStore,
|
||||
LSSslave->pendingNodeID, LSSslave->pendingBitRate);
|
||||
if (!result) {
|
||||
errorCode = CO_LSS_CFG_STORE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_CFG_STORE;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
memset(&LSSslave->TXbuff->data[2], 0, sizeof(LSSslave->TXbuff->data) - 2);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - Handle service "inquire"
|
||||
*/
|
||||
static void CO_LSSslave_serviceInquire(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_cs_t service,
|
||||
void *msg)
|
||||
{
|
||||
(void)msg; /* unused */
|
||||
|
||||
uint32_t value;
|
||||
|
||||
if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (service) {
|
||||
case CO_LSS_INQUIRE_VENDOR:
|
||||
value = LSSslave->lssAddress.identity.vendorID;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_PRODUCT:
|
||||
value = LSSslave->lssAddress.identity.productCode;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_REV:
|
||||
value = LSSslave->lssAddress.identity.revisionNumber;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_SERIAL:
|
||||
value = LSSslave->lssAddress.identity.serialNumber;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_NODE_ID:
|
||||
value = (uint32_t)LSSslave->activeNodeID;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
/* send response */
|
||||
LSSslave->TXbuff->data[0] = service;
|
||||
CO_memcpySwap4(&LSSslave->TXbuff->data[1], &value);
|
||||
memset(&LSSslave->TXbuff->data[5], 0, sizeof(LSSslave->TXbuff->data) - 5);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - Handle service "identify"
|
||||
*/
|
||||
static void CO_LSSslave_serviceIdent(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_cs_t service,
|
||||
void *msg)
|
||||
{
|
||||
uint32_t idNumber;
|
||||
uint8_t bitCheck;
|
||||
uint8_t lssSub;
|
||||
uint8_t lssNext;
|
||||
bool_t ack;
|
||||
uint8_t *data = CO_CANrxMsg_readData(msg);
|
||||
|
||||
if (LSSslave->lssState != CO_LSS_STATE_WAITING) {
|
||||
/* fastscan is only allowed in waiting state */
|
||||
return;
|
||||
}
|
||||
if (service != CO_LSS_IDENT_FASTSCAN) {
|
||||
/* we only support "fastscan" identification */
|
||||
return;
|
||||
}
|
||||
if (LSSslave->pendingNodeID!=CO_LSS_NODE_ID_ASSIGNMENT ||
|
||||
LSSslave->activeNodeID!=CO_LSS_NODE_ID_ASSIGNMENT) {
|
||||
/* fastscan is only active on unconfigured nodes */
|
||||
return;
|
||||
}
|
||||
|
||||
CO_memcpySwap4(&idNumber, &data[1]);
|
||||
bitCheck = data[5];
|
||||
lssSub = data[6];
|
||||
lssNext = data[7];
|
||||
|
||||
if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) ||
|
||||
!CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub) ||
|
||||
!CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) {
|
||||
/* Invalid request */
|
||||
return;
|
||||
}
|
||||
|
||||
ack = false;
|
||||
if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) {
|
||||
/* Confirm, Reset */
|
||||
ack = true;
|
||||
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
|
||||
memset(&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan));
|
||||
}
|
||||
else if (LSSslave->fastscanPos == lssSub) {
|
||||
uint32_t mask = 0xFFFFFFFF << bitCheck;
|
||||
|
||||
if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) {
|
||||
/* all requested bits match */
|
||||
ack = true;
|
||||
LSSslave->fastscanPos = lssNext;
|
||||
|
||||
if (bitCheck==0 && lssNext<lssSub) {
|
||||
/* complete match, enter configuration state */
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ack) {
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE;
|
||||
memset(&LSSslave->TXbuff->data[1], 0, sizeof(LSSslave->TXbuff->data) - 1);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read received message from CAN module.
|
||||
|
|
@ -339,32 +41,146 @@ static void CO_LSSslave_serviceIdent(
|
|||
*/
|
||||
static void CO_LSSslave_receive(void *object, void *msg)
|
||||
{
|
||||
CO_LSSslave_t *LSSslave;
|
||||
CO_LSSslave_t *LSSslave = (CO_LSSslave_t*)object;
|
||||
uint8_t DLC = CO_CANrxMsg_readDLC(msg);
|
||||
uint8_t *data = CO_CANrxMsg_readData(msg);
|
||||
|
||||
LSSslave = (CO_LSSslave_t*)object; /* this is the correct pointer type of the first argument */
|
||||
|
||||
if(DLC == 8){
|
||||
if(DLC == 8U && !CO_FLAG_READ(LSSslave->sendResponse)) {
|
||||
bool_t request_LSSslave_process = false;
|
||||
uint8_t *data = CO_CANrxMsg_readData(msg);
|
||||
CO_LSS_cs_t cs = (CO_LSS_cs_t) data[0];
|
||||
|
||||
if (CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs)) {
|
||||
CO_LSSslave_serviceSwitchStateGlobal(LSSslave, cs, msg);
|
||||
if (cs == CO_LSS_SWITCH_STATE_GLOBAL) {
|
||||
uint8_t mode = data[1];
|
||||
|
||||
switch (mode) {
|
||||
case CO_LSS_STATE_WAITING:
|
||||
if (LSSslave->lssState == CO_LSS_STATE_CONFIGURATION &&
|
||||
LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT &&
|
||||
*LSSslave->pendingNodeID != CO_LSS_NODE_ID_ASSIGNMENT)
|
||||
{
|
||||
/* Slave process function will request NMT Reset comm.*/
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
}
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
memset(&LSSslave->lssSelect, 0,
|
||||
sizeof(LSSslave->lssSelect));
|
||||
break;
|
||||
case CO_LSS_STATE_CONFIGURATION:
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs)) {
|
||||
CO_LSSslave_serviceSwitchStateSelective(LSSslave, cs, msg);
|
||||
else if(LSSslave->lssState == CO_LSS_STATE_WAITING) {
|
||||
switch (cs) {
|
||||
case CO_LSS_SWITCH_STATE_SEL_VENDOR: {
|
||||
CO_memcpySwap4(&LSSslave->lssSelect.identity.vendorID,
|
||||
&data[1]);
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_PRODUCT: {
|
||||
CO_memcpySwap4(&LSSslave->lssSelect.identity.productCode,
|
||||
&data[1]);
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_REV: {
|
||||
CO_memcpySwap4(&LSSslave->lssSelect.identity.revisionNumber,
|
||||
&data[1]);
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_SERIAL: {
|
||||
CO_memcpySwap4(&LSSslave->lssSelect.identity.serialNumber,
|
||||
&data[1]);
|
||||
|
||||
if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress,
|
||||
LSSslave->lssSelect)
|
||||
) {
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CO_LSS_IDENT_FASTSCAN: {
|
||||
/* fastscan is only active on unconfigured nodes */
|
||||
if (*LSSslave->pendingNodeID == CO_LSS_NODE_ID_ASSIGNMENT &&
|
||||
LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)
|
||||
{
|
||||
uint8_t bitCheck = data[5];
|
||||
uint8_t lssSub = data[6];
|
||||
uint8_t lssNext = data[7];
|
||||
uint32_t idNumber;
|
||||
bool_t ack;
|
||||
|
||||
if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) ||
|
||||
!CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub) ||
|
||||
!CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) {
|
||||
/* Invalid request */
|
||||
break;
|
||||
}
|
||||
|
||||
CO_memcpySwap4(&idNumber, &data[1]);
|
||||
ack = false;
|
||||
|
||||
if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) {
|
||||
/* Confirm, Reset */
|
||||
ack = true;
|
||||
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
|
||||
memset(&LSSslave->lssFastscan, 0,
|
||||
sizeof(LSSslave->lssFastscan));
|
||||
}
|
||||
else if (LSSslave->fastscanPos == lssSub) {
|
||||
uint32_t mask = 0xFFFFFFFF << bitCheck;
|
||||
|
||||
if ((LSSslave->lssAddress.addr[lssSub] & mask)
|
||||
== (idNumber & mask))
|
||||
{
|
||||
/* all requested bits match */
|
||||
ack = true;
|
||||
LSSslave->fastscanPos = lssNext;
|
||||
|
||||
if (bitCheck == 0 && lssNext < lssSub) {
|
||||
/* complete match, enter configuration state */
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ack) {
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE;
|
||||
memset(&LSSslave->TXbuff->data[1], 0,
|
||||
sizeof(LSSslave->TXbuff->data) - 1);
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
#else
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CO_LSS_CS_SERVICE_IS_CONFIG(cs)) {
|
||||
CO_LSSslave_serviceConfig(LSSslave, cs, msg);
|
||||
else { /* LSSslave->lssState == CO_LSS_STATE_CONFIGURATION */
|
||||
memcpy(&LSSslave->CANdata, &data[0], sizeof(LSSslave->CANdata));
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
}
|
||||
else if (CO_LSS_CS_SERVICE_IS_INQUIRE(cs)) {
|
||||
CO_LSSslave_serviceInquire(LSSslave, cs, msg);
|
||||
}
|
||||
else if (CO_LSS_CS_SERVICE_IS_IDENT(cs)) {
|
||||
CO_LSSslave_serviceIdent(LSSslave, cs, msg);
|
||||
}
|
||||
else {
|
||||
/* No Ack -> Unsupported commands are dropped */
|
||||
|
||||
if (request_LSSslave_process) {
|
||||
CO_FLAG_SET(LSSslave->sendResponse);
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_FLAG_CALLBACK_PRE
|
||||
/* Optional signal to RTOS, which can resume task,
|
||||
* which handles further processing. */
|
||||
if (LSSslave->pFunctSignalPre != NULL) {
|
||||
LSSslave->pFunctSignalPre(LSSslave->functSignalObjectPre);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -374,8 +190,8 @@ static void CO_LSSslave_receive(void *object, void *msg)
|
|||
CO_ReturnError_t CO_LSSslave_init(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_address_t lssAddress,
|
||||
uint16_t pendingBitRate,
|
||||
uint8_t pendingNodeID,
|
||||
uint16_t *pendingBitRate,
|
||||
uint8_t *pendingNodeID,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
uint32_t CANidLssMaster,
|
||||
|
|
@ -386,33 +202,27 @@ CO_ReturnError_t CO_LSSslave_init(
|
|||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
|
||||
/* verify arguments */
|
||||
if (LSSslave==NULL || CANdevRx==NULL || CANdevTx==NULL ||
|
||||
!CO_LSS_NODE_ID_VALID(pendingNodeID)) {
|
||||
if (LSSslave==NULL || pendingBitRate == NULL || pendingNodeID == NULL ||
|
||||
CANdevRx==NULL || CANdevTx==NULL ||
|
||||
!CO_LSS_NODE_ID_VALID(*pendingNodeID)
|
||||
) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* check LSS address for plausibility. As a bare minimum, the vendor
|
||||
* ID and serial number must be set */
|
||||
if (lssAddress.identity.vendorID==0 || lssAddress.identity.serialNumber==0) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
/* Application must make sure that lssAddress is filled with data. */
|
||||
|
||||
/* clear the object */
|
||||
memset(LSSslave, 0, sizeof(CO_LSSslave_t));
|
||||
|
||||
/* Configure object variables */
|
||||
memcpy(&LSSslave->lssAddress, &lssAddress, sizeof(LSSslave->lssAddress));
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
memset(&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect));
|
||||
|
||||
memset(&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan));
|
||||
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
|
||||
|
||||
LSSslave->pendingBitRate = pendingBitRate;
|
||||
LSSslave->pendingNodeID = pendingNodeID;
|
||||
LSSslave->activeNodeID = CO_LSS_NODE_ID_ASSIGNMENT;
|
||||
LSSslave->pFunctLSScheckBitRate = NULL;
|
||||
LSSslave->functLSScheckBitRateObject = NULL;
|
||||
LSSslave->pFunctLSSactivateBitRate = NULL;
|
||||
LSSslave->functLSSactivateBitRateObject = NULL;
|
||||
LSSslave->pFunctLSScfgStore = NULL;
|
||||
LSSslave->functLSScfgStore = NULL;
|
||||
LSSslave->activeNodeID = *pendingNodeID;
|
||||
CO_FLAG_CLEAR(LSSslave->sendResponse);
|
||||
|
||||
/* configure LSS CAN Master message reception */
|
||||
ret = CO_CANrxBufferInit(
|
||||
|
|
@ -442,6 +252,21 @@ CO_ReturnError_t CO_LSSslave_init(
|
|||
}
|
||||
|
||||
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_FLAG_CALLBACK_PRE
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_initCallbackPre(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
void (*pFunctSignalPre)(void *object))
|
||||
{
|
||||
if(LSSslave != NULL){
|
||||
LSSslave->functSignalObjectPre = object;
|
||||
LSSslave->pFunctSignalPre = pFunctSignalPre;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_initCheckBitRateCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
|
|
@ -475,72 +300,183 @@ void CO_LSSslave_initCfgStoreCallback(
|
|||
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate))
|
||||
{
|
||||
if(LSSslave != NULL){
|
||||
LSSslave->functLSScfgStore = object;
|
||||
LSSslave->functLSScfgStoreObject = object;
|
||||
LSSslave->pFunctLSScfgStore = pFunctLSScfgStore;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_process(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
uint16_t activeBitRate,
|
||||
uint8_t activeNodeId,
|
||||
uint16_t *pendingBitRate,
|
||||
uint8_t *pendingNodeId)
|
||||
{
|
||||
(void)activeBitRate; /* unused */
|
||||
bool_t CO_LSSslave_process(CO_LSSslave_t *LSSslave) {
|
||||
bool_t resetCommunication = false;
|
||||
|
||||
LSSslave->activeNodeID = activeNodeId;
|
||||
*pendingBitRate = LSSslave->pendingBitRate;
|
||||
*pendingNodeId = LSSslave->pendingNodeID;
|
||||
}
|
||||
if (CO_FLAG_READ(LSSslave->sendResponse)) {
|
||||
uint8_t nid;
|
||||
uint8_t errorCode;
|
||||
uint8_t errorCodeManuf;
|
||||
uint8_t tableSelector;
|
||||
uint8_t tableIndex;
|
||||
bool_t CANsend = false;
|
||||
|
||||
memset(&LSSslave->TXbuff->data[0], 0, sizeof(LSSslave->TXbuff->data));
|
||||
|
||||
/******************************************************************************/
|
||||
CO_LSS_state_t CO_LSSslave_getState(
|
||||
CO_LSSslave_t *LSSslave)
|
||||
{
|
||||
if(LSSslave != NULL){
|
||||
return LSSslave->lssState;
|
||||
}
|
||||
return CO_LSS_STATE_WAITING;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
bool_t CO_LSSslave_LEDprocess(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
uint32_t timeDifference_us,
|
||||
bool_t *LEDon)
|
||||
{
|
||||
static uint32_t ms50 = 0;
|
||||
static int8_t flash1, flash2;
|
||||
|
||||
if (LSSslave == NULL || LEDon == NULL)
|
||||
return false;
|
||||
ms50 += timeDifference_us;
|
||||
if(ms50 >= 50000) {
|
||||
ms50 -= 50000;
|
||||
/* 4 cycles on, 50 cycles off */
|
||||
if(++flash1 >= 4) flash1 = -50;
|
||||
|
||||
/* 4 cycles on, 4 cycles off, 4 cycles on, 50 cycles off */
|
||||
switch(++flash2){
|
||||
case 4: flash2 = -104; break;
|
||||
case -100: flash2 = 100; break;
|
||||
case 104: flash2 = -50; break;
|
||||
switch (LSSslave->service) {
|
||||
case CO_LSS_SWITCH_STATE_GLOBAL: {
|
||||
/* Node-Id was unconfigured before, now it is configured,
|
||||
* enter the NMT Reset communication autonomously. */
|
||||
resetCommunication = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_SERIAL: {
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_NODE_ID: {
|
||||
nid = LSSslave->CANdata[1];
|
||||
errorCode = CO_LSS_CFG_NODE_ID_OK;
|
||||
|
||||
if (CO_LSS_NODE_ID_VALID(nid)) {
|
||||
*LSSslave->pendingNodeID = nid;
|
||||
}
|
||||
else {
|
||||
errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_BIT_TIMING: {
|
||||
if (LSSslave->pFunctLSScheckBitRate == NULL) {
|
||||
/* setting bit timing is not supported. Drop request */
|
||||
break;
|
||||
}
|
||||
|
||||
tableSelector = LSSslave->CANdata[1];
|
||||
tableIndex = LSSslave->CANdata[2];
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OK;
|
||||
errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OK;
|
||||
|
||||
if (tableSelector == 0 && CO_LSS_BIT_TIMING_VALID(tableIndex)) {
|
||||
uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex];
|
||||
bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate(
|
||||
LSSslave->functLSScheckBitRateObject, bit);
|
||||
|
||||
if (bit_rate_supported) {
|
||||
*LSSslave->pendingBitRate = bit;
|
||||
}
|
||||
else {
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_MANUFACTURER;
|
||||
errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* we currently only support CiA301 bit timing table */
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
LSSslave->TXbuff->data[2] = errorCodeManuf;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_ACTIVATE_BIT_TIMING: {
|
||||
if (LSSslave->pFunctLSScheckBitRate == NULL) {
|
||||
/* setting bit timing is not supported. Drop request */
|
||||
break;
|
||||
}
|
||||
|
||||
/* notify application */
|
||||
if (LSSslave->pFunctLSSactivateBitRate != NULL) {
|
||||
uint16_t delay;
|
||||
CO_memcpySwap2(&delay, &LSSslave->CANdata[1]);
|
||||
LSSslave->pFunctLSSactivateBitRate(
|
||||
LSSslave->functLSSactivateBitRateObject, delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_STORE: {
|
||||
errorCode = CO_LSS_CFG_STORE_OK;
|
||||
|
||||
if (LSSslave->pFunctLSScfgStore == NULL) {
|
||||
/* storing is not supported. Reply error */
|
||||
errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED;
|
||||
}
|
||||
else {
|
||||
bool_t result;
|
||||
/* Store "pending" to "persistent" */
|
||||
result =
|
||||
LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStoreObject,
|
||||
*LSSslave->pendingNodeID,
|
||||
*LSSslave->pendingBitRate);
|
||||
if (!result) {
|
||||
errorCode = CO_LSS_CFG_STORE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_VENDOR: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
CO_memcpySwap4(&LSSslave->TXbuff->data[1],
|
||||
&LSSslave->lssAddress.identity.vendorID);
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_PRODUCT: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
CO_memcpySwap4(&LSSslave->TXbuff->data[1],
|
||||
&LSSslave->lssAddress.identity.productCode);
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_REV: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
CO_memcpySwap4(&LSSslave->TXbuff->data[1],
|
||||
&LSSslave->lssAddress.identity.revisionNumber);
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_SERIAL: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
CO_memcpySwap4(&LSSslave->TXbuff->data[1],
|
||||
&LSSslave->lssAddress.identity.serialNumber);
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_NODE_ID: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = LSSslave->activeNodeID;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_IDENT_FASTSCAN: {
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(CANsend) {
|
||||
CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
}
|
||||
|
||||
CO_FLAG_CLEAR(LSSslave->sendResponse);
|
||||
}
|
||||
if (LSSslave->lssState == CO_LSS_STATE_CONFIGURATION)
|
||||
{
|
||||
*LEDon = (flash2 >= 0);
|
||||
return true;
|
||||
}
|
||||
else if (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)
|
||||
{
|
||||
*LEDon = (flash1 >= 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return resetCommunication;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* @file CO_LSSslave.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
|
|
@ -53,178 +54,33 @@ extern "C" {
|
|||
*
|
||||
* After CAN module start, the LSS slave and NMT slave are started and then
|
||||
* coexist alongside each other. To achieve this behaviour, the CANopen node
|
||||
* startup process has to be conrolled more detailled. Therefore, the function
|
||||
* CO_init() is split up into the functions CO_new(), CO_CANinit(), CO_LSSinit()
|
||||
* and CO_CANopenInit().
|
||||
* startup process has to be controlled more detailed. Therefore, CO_LSSinit()
|
||||
* must be invoked between CO_CANinit() and CO_CANopenInit() in the
|
||||
* communication reset section.
|
||||
*
|
||||
* Moreover, the LSS slave needs to pause the NMT slave initialization in case
|
||||
* no valid node ID is available at start up.
|
||||
* no valid node ID is available at start up. In that case CO_CANopenInit()
|
||||
* skips initialization of other CANopen modules and CO_process() skips
|
||||
* processing of other modules than LSS slave automatically.
|
||||
*
|
||||
* ###Example
|
||||
* Variables for CAN-bitrate and CANopen node-id must be initialized by
|
||||
* application from non-volatile memory or dip switches. Pointers to them are
|
||||
* passed to CO_LSSinit() function. Those variables represents pending values.
|
||||
* If node-id is valid in the moment it enters CO_LSSinit(), it also becomes
|
||||
* active node-id and the stack initialises normally. Otherwise, node-id must be
|
||||
* configured by lss and after successful configuration stack passes reset
|
||||
* communication autonomously.
|
||||
*
|
||||
* It is strongly recommended that the user already has a fully working application
|
||||
* running with the standard (non LSS) version of CANopenNode. This is required
|
||||
* to understand what this example does and where you need to change it for your
|
||||
* requirements.
|
||||
* Device with all threads can be normally initialized and running despite that
|
||||
* node-id is not valid. Application must take care, because CANopen is not
|
||||
* initialized. In that case CO_CANopenInit() returns error condition
|
||||
* CO_ERROR_NODE_ID_UNCONFIGURED_LSS which must be handled properly. Status can
|
||||
* also be checked with CO->nodeIdUnconfigured variable.
|
||||
*
|
||||
* The following code is only a suggestion on how to use the LSS slave. It is
|
||||
* not a working example! To simplify the code, no error handling is
|
||||
* included. For stable code, proper error handling has to be added to the user
|
||||
* code.
|
||||
*
|
||||
* This example is not intended for bare metal targets. If you intend to do CAN
|
||||
* message receiving inside interrupt, be aware that the callback functions
|
||||
* will be called inside the interrupt handler context!
|
||||
*
|
||||
* \code{.c}
|
||||
|
||||
const uint16_t FIRST_BIT = 125;
|
||||
queue changeBitRate;
|
||||
uint8_t activeNid;
|
||||
uint16_t activeBit;
|
||||
|
||||
bool_t checkBitRateCallback(void *object, uint16_t bitRate)
|
||||
{
|
||||
if (validBit(bitRate)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void activateBitRateCallback(void *object, uint16_t delay)
|
||||
{
|
||||
int time = getCurrentTime();
|
||||
queueSend(&changeBitRate, time, delay);
|
||||
}
|
||||
|
||||
bool_t cfgStoreCallback(void *object, uint8_t id, uint16_t bitRate)
|
||||
{
|
||||
savePersistent(id, bitRate);
|
||||
return true;
|
||||
}
|
||||
|
||||
void start_canopen(uint8_t nid)
|
||||
{
|
||||
uint8_t persistentNid;
|
||||
uint8_t pendingNid;
|
||||
uint16_t persistentBit;
|
||||
uint16_t pendingBit;
|
||||
|
||||
loadPersistent(&persistentNid, &persistentBit);
|
||||
|
||||
if ( ! validBit(persistentBit)) {
|
||||
printf("no bit rate found, defaulting to %d", FIRST_BIT);
|
||||
pendingBit = FIRST_BIT;
|
||||
}
|
||||
else {
|
||||
printf("loaded bit rate from nvm: %d", persistentBit);
|
||||
pendingBit = persistentBit;
|
||||
}
|
||||
|
||||
if (nid == 0) {
|
||||
if ( ! validNid(persistentNid)) {
|
||||
pendingNid = CO_LSS_NODE_ID_ASSIGNMENT;
|
||||
printf("no node id found, needs to be set by LSS. NMT will"
|
||||
"not be started until valid node id is set");
|
||||
}
|
||||
else {
|
||||
printf("loaded node id from nvm: %d", persistentNid);
|
||||
pendingNid = persistentNid;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("node id provided by application: %d", nid);
|
||||
pendingNid = nid;
|
||||
}
|
||||
|
||||
CO_new();
|
||||
CO_CANinit(0, pendingBit);
|
||||
CO_LSSinit(pendingNid, pendingBit);
|
||||
CO_CANsetNormalMode(CO->CANmodule[0]);
|
||||
activeBit = pendingBit;
|
||||
|
||||
CO_LSSslave_initCheckBitRateCallback(CO->LSSslave, NULL, checkBitRateCallback);
|
||||
CO_LSSslave_initActivateBitRateCallback(CO->LSSslave, NULL, activateBitRateCallback);
|
||||
CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL, cfgStoreCallback);
|
||||
|
||||
while (1) {
|
||||
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
|
||||
&pendingBit, &pendingNid);
|
||||
if (pendingNid!=CO_LSS_NODE_ID_ASSIGNMENT &&
|
||||
CO_LSSslave_getState(CO->LSSslave)==CO_LSS_STATE_WAITING) {
|
||||
printf("node ID has been found: %d", pendingNid);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! queueEmpty(&changeBitRate)) {
|
||||
printf("bit rate change requested: %d", pendingBit);
|
||||
int time;
|
||||
uint16_t delay;
|
||||
queueReceive(&changeBitRate, time, delay);
|
||||
delayUntil(time + delay);
|
||||
CO_CANsetBitrate(CO->CANmodule[0], pendingBit);
|
||||
delay(delay);
|
||||
}
|
||||
|
||||
printf("waiting for node id");
|
||||
CO_CANrxWait(CO->CANmodule[0]);
|
||||
}
|
||||
|
||||
CO_CANopenInit(pendingNid);
|
||||
activeNid = pendingNid;
|
||||
|
||||
printf("from this on, initialization doesn't differ to non-LSS version"
|
||||
"You can now intialize your CO_CANrxWait() thread or interrupt");
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
uint8_t pendingNid;
|
||||
uint16_t pendingBit;
|
||||
|
||||
printf("like example in dir \"example\"");
|
||||
|
||||
CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
|
||||
uint16_t timer1msPrevious;
|
||||
|
||||
start_canopen(0);
|
||||
|
||||
reset = CO_RESET_NOT;
|
||||
timer1msPrevious = CO_timer1ms;
|
||||
while(reset == CO_RESET_NOT){
|
||||
printf("loop for normal program execution");
|
||||
uint16_t timer1msCopy, timer1msDiff;
|
||||
|
||||
timer1msCopy = CO_timer1ms;
|
||||
timer1msDiff = timer1msCopy - timer1msPrevious;
|
||||
timer1msPrevious = timer1msCopy;
|
||||
|
||||
reset = CO_process(CO, timer1msDiff, NULL);
|
||||
|
||||
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
|
||||
&pendingBit, &pendingNid);
|
||||
if (reset == CO_RESET_COMM) {
|
||||
printf("restarting CANopen using pending node ID %d", pendingNid);
|
||||
CO_delete(0);
|
||||
start_canopen(pendingNid);
|
||||
reset = CO_RESET_NOT;
|
||||
}
|
||||
if ( ! queueEmpty(&changeBitRate)) {
|
||||
printf("bit rate change requested: %d", pendingBit);
|
||||
int time;
|
||||
uint16_t delay;
|
||||
queueReceive(&changeBitRate, time, delay);
|
||||
printf("Disabling CANopen for givent time");
|
||||
pauseReceiveThread();
|
||||
delayUntil(time + delay);
|
||||
CO_CANsetBitrate(CO->CANmodule[0], pendingBit);
|
||||
delay(delay);
|
||||
resumeReceiveThread();
|
||||
printf("Re-enabling CANopen after bit rate switch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* \endcode
|
||||
* Some callback functions may be initialized by application with
|
||||
* CO_LSSslave_initCheckBitRateCallback(),
|
||||
* CO_LSSslave_initActivateBitRateCallback() and
|
||||
* CO_LSSslave_initCfgStoreCallback().
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -238,16 +94,24 @@ typedef struct{
|
|||
CO_LSS_address_t lssFastscan; /**< Received LSS Address by fastscan */
|
||||
uint8_t fastscanPos; /**< Current state of fastscan */
|
||||
|
||||
uint16_t pendingBitRate; /**< Bit rate value that is temporarily configured in volatile memory */
|
||||
uint8_t pendingNodeID; /**< Node ID that is temporarily configured in volatile memory */
|
||||
uint16_t *pendingBitRate; /**< Bit rate value that is temporarily configured */
|
||||
uint8_t *pendingNodeID; /**< Node ID that is temporarily configured */
|
||||
uint8_t activeNodeID; /**< Node ID used at the CAN interface */
|
||||
|
||||
volatile void *sendResponse; /**< Variable indicates, if LSS response has to be sent by mainline processing function */
|
||||
CO_LSS_cs_t service; /**< Service, which will have to be processed by mainline processing function */
|
||||
uint8_t CANdata[8]; /**< Received CAN data, which will be processed by mainline processing function */
|
||||
|
||||
#if ((CO_CONFIG_LSS) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
|
||||
void (*pFunctSignalPre)(void *object); /**< From CO_LSSslave_initCallbackPre() or NULL */
|
||||
void *functSignalObjectPre;/**< Pointer to object */
|
||||
#endif
|
||||
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate); /**< From CO_LSSslave_initCheckBitRateCallback() or NULL */
|
||||
void *functLSScheckBitRateObject; /** Pointer to object */
|
||||
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay); /**< From CO_LSSslave_initActivateBitRateCallback() or NULL. Delay is in ms */
|
||||
void *functLSSactivateBitRateObject; /** Pointer to object */
|
||||
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate); /**< From CO_LSSslave_initCfgStoreCallback() or NULL */
|
||||
void *functLSScfgStore; /** Pointer to object */
|
||||
void *functLSScfgStoreObject; /** Pointer to object */
|
||||
|
||||
CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */
|
||||
CO_CANtx_t *TXbuff; /**< CAN transmit buffer */
|
||||
|
|
@ -258,20 +122,32 @@ typedef struct{
|
|||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* Depending on the startup type, pending bit rate and node ID have to be
|
||||
* supplied differently. After #CO_NMT_RESET_NODE or at power up they should
|
||||
* be restored from persitent bit rate and node id. After #CO_NMT_RESET_COMMUNICATION
|
||||
* they have to be supplied from the application and are generally the values
|
||||
* that have been last returned by #CO_LSSslave_process() before resetting.
|
||||
* pendingBitRate and pendingNodeID must be pointers to external variables. Both
|
||||
* variables must be initialized on program startup (after #CO_NMT_RESET_NODE)
|
||||
* from non-volatile memory, dip switches or similar. They must not change
|
||||
* during #CO_NMT_RESET_COMMUNICATION. Both variables can be changed by
|
||||
* CO_LSSslave_process(), depending on commands from the LSS master.
|
||||
*
|
||||
* If pendingNodeID is valid (1 <= pendingNodeID <= 0x7F), then this becomes
|
||||
* valid active nodeId just after exit of this function. In that case all other
|
||||
* CANopen objects may be initialized and processed in run time.
|
||||
*
|
||||
* If pendingNodeID is not valid (pendingNodeID == 0xFF), then only LSS slave is
|
||||
* initialized and processed in run time. In that state pendingNodeID can be
|
||||
* configured and after successful configuration reset communication with all
|
||||
* CANopen object is activated automatically.
|
||||
*
|
||||
* @remark The LSS address needs to be unique on the network. For this, the 128
|
||||
* bit wide identity object (1018h) is used. Therefore, this object has to be fully
|
||||
* initalized before passing it to this function.
|
||||
* bit wide identity object (1018h) is used. Therefore, this object has to be
|
||||
* fully initialized before passing it to this function (vendorID, product
|
||||
* code, revisionNo, serialNo are set to 0 by default). Otherwise, if
|
||||
* non-configured devices are present on CANopen network, LSS configuration may
|
||||
* behave unpredictable.
|
||||
*
|
||||
* @param LSSslave This object will be initialized.
|
||||
* @param lssAddress LSS address
|
||||
* @param pendingBitRate Bit rate of the CAN interface.
|
||||
* @param pendingNodeID Node ID or 0xFF - invalid.
|
||||
* @param [in,out] pendingBitRate Pending bit rate of the CAN interface
|
||||
* @param [in,out] pendingNodeID Pending node ID or 0xFF - invalid
|
||||
* @param CANdevRx CAN device for LSS slave reception.
|
||||
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
|
||||
* @param CANidLssMaster COB ID for reception.
|
||||
|
|
@ -283,8 +159,8 @@ typedef struct{
|
|||
CO_ReturnError_t CO_LSSslave_init(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_address_t lssAddress,
|
||||
uint16_t pendingBitRate,
|
||||
uint8_t pendingNodeID,
|
||||
uint16_t *pendingBitRate,
|
||||
uint8_t *pendingNodeID,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
uint32_t CANidLssMaster,
|
||||
|
|
@ -295,21 +171,17 @@ CO_ReturnError_t CO_LSSslave_init(
|
|||
/**
|
||||
* Process LSS communication
|
||||
*
|
||||
* - sets currently active node ID and bit rate so master can read it
|
||||
* - hands over pending node ID and bit rate to user application
|
||||
* Object is partially pre-processed after LSS message received. Further
|
||||
* processing is inside this function.
|
||||
*
|
||||
* In case that Node-Id is unconfigured, then this function may request CANopen
|
||||
* communication reset. This happens, when valid node-id is configured by LSS
|
||||
* master.
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param activeBitRate Currently active bit rate
|
||||
* @param activeNodeId Currently active node ID
|
||||
* @param [out] pendingBitRate Requested bit rate
|
||||
* @param [out] pendingNodeId Requested node id
|
||||
* @return True, if #CO_NMT_RESET_COMMUNICATION is requested
|
||||
*/
|
||||
void CO_LSSslave_process(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
uint16_t activeBitRate,
|
||||
uint8_t activeNodeId,
|
||||
uint16_t *pendingBitRate,
|
||||
uint8_t *pendingNodeId);
|
||||
bool_t CO_LSSslave_process(CO_LSSslave_t *LSSslave);
|
||||
|
||||
/**
|
||||
* Get current LSS state
|
||||
|
|
@ -317,30 +189,30 @@ void CO_LSSslave_process(
|
|||
* @param LSSslave This object.
|
||||
* @return #CO_LSS_state_t
|
||||
*/
|
||||
CO_LSS_state_t CO_LSSslave_getState(
|
||||
CO_LSSslave_t *LSSslave);
|
||||
static inline CO_LSS_state_t CO_LSSslave_getState(CO_LSSslave_t *LSSslave) {
|
||||
return (LSSslave == NULL) ? CO_LSS_STATE_WAITING : LSSslave->lssState;
|
||||
}
|
||||
|
||||
|
||||
#if ((CO_CONFIG_LSS) & CO_CONFIG_FLAG_CALLBACK_PRE) || defined CO_DOXYGEN
|
||||
/**
|
||||
* Process LSS LED
|
||||
* Initialize LSSslaveRx callback function.
|
||||
*
|
||||
* Returns the status of the LSS LED (if LSS is involved)
|
||||
* with the following meaning:
|
||||
*
|
||||
* UNCONFIGURED (activeNodeId is unconfigured) --> single flash
|
||||
* SELECTED --> double flash
|
||||
*
|
||||
* If none of above conditions apply, returns false.
|
||||
* Function initializes optional callback function, which should immediately
|
||||
* start further LSS processing. Callback is called after LSS message is
|
||||
* received from the CAN bus. It should signal the RTOS to resume corresponding
|
||||
* task.
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param timeDifference_us The amount of time elapsed since the last call
|
||||
* @param [out] LEDon LED state
|
||||
*
|
||||
* @return true if LSS is involved (unconfigured node or selected node)
|
||||
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
|
||||
* @param pFunctSignalPre Pointer to the callback function. Not called if NULL.
|
||||
*/
|
||||
bool_t CO_LSSslave_LEDprocess(
|
||||
void CO_LSSslave_initCallbackPre(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
uint32_t timeDifference_us,
|
||||
bool_t *LEDon);
|
||||
void *object,
|
||||
void (*pFunctSignalPre)(void *object));
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Initialize verify bit rate callback
|
||||
|
|
@ -351,9 +223,6 @@ bool_t CO_LSSslave_LEDprocess(
|
|||
* When no callback is set the LSS slave will no-ack the request, indicating to
|
||||
* the master that bit rate change is not supported.
|
||||
*
|
||||
* @remark Depending on the CAN driver implementation, this function is called
|
||||
* inside an ISR
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param object Pointer to object, which will be passed to pFunctLSScheckBitRate(). Can be NULL
|
||||
* @param pFunctLSScheckBitRate Pointer to the callback function. Not called if NULL.
|
||||
|
|
@ -371,10 +240,7 @@ void CO_LSSslave_initCheckBitRateCallback(
|
|||
* allow setting a timer or do calculations based on the exact time the request
|
||||
* arrived.
|
||||
* According to DSP 305 6.4.4, the delay has to be applied once before and once after
|
||||
* switching bit rates. During this time, a device musn't send any messages.
|
||||
*
|
||||
* @remark Depending on the CAN driver implementation, this function is called
|
||||
* inside an ISR
|
||||
* switching bit rates. During this time, a device mustn't send any messages.
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL
|
||||
|
|
@ -395,9 +261,6 @@ void CO_LSSslave_initActivateBitRateCallback(
|
|||
* callback is set the LSS slave will no-ack the request, indicating to the master
|
||||
* that storing is not supported.
|
||||
*
|
||||
* @remark Depending on the CAN driver implementation, this function is called
|
||||
* inside an ISR
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param object Pointer to object, which will be passed to pFunctLSScfgStore(). Can be NULL
|
||||
* @param pFunctLSScfgStore Pointer to the callback function. Not called if NULL.
|
||||
|
|
|
|||
|
|
@ -200,14 +200,14 @@ static const char CO_GTWA_helpStringLss[] =
|
|||
"lss_get_node # Inquire node-ID.\n" \
|
||||
"_lss_fastscan [<timeout_ms>] # Identify fastscan, non-standard.\n" \
|
||||
"lss_allnodes [<timeout_ms> [<nodeStart=1..127> <store=0|1>\\\n" \
|
||||
" <scanType0=0..2> <vendorId> <scanType1=0..2> <productCode>\\\n" \
|
||||
" <scanType2=0..2> <revisionNo> <scanType3=0..2> <serialNo>]]\n" \
|
||||
" [<scanType0> <vendorId> <scanType1> <productCode>\\\n" \
|
||||
" <scanType2> <revisionNo> <scanType3> <serialNo>]]]\n" \
|
||||
" # Node-ID configuration of all nodes.\n" \
|
||||
"\n" \
|
||||
"<table_index>: 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" \
|
||||
"\n" \
|
||||
"All LSS commands start with '\"[\"<sequence>\"]\" [<net>]'.\r\n";
|
||||
"* All LSS commands start with '\"[\"<sequence>\"]\" [<net>]'.\n" \
|
||||
"* <table_index>: 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" \
|
||||
"* <scanType>: 0=fastscan, 1=ignore, 2=match value in next parameter\r\n";
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS
|
||||
|
|
@ -396,7 +396,7 @@ static const errorDescs_t errorDescs[] = {
|
|||
{100, "Request not supported."},
|
||||
{101, "Syntax error."},
|
||||
{102, "Request not processed due to internal state."},
|
||||
{103, "Time-out (where applicable)."},
|
||||
{103, "Time-out."},
|
||||
{104, "No default net set."},
|
||||
{105, "No default node set."},
|
||||
{106, "Unsupported net."},
|
||||
|
|
@ -1348,17 +1348,26 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
|
|||
/* prepare lssFastscan, all zero */
|
||||
memset(>wa->lssFastscan, 0, sizeof(gtwa->lssFastscan));
|
||||
}
|
||||
else {
|
||||
CO_LSSmaster_fastscan_t *fs = >wa->lssFastscan;
|
||||
/* read other arguments */
|
||||
if (closed == 0) {
|
||||
/* more arguments follow */
|
||||
CO_fifo_readToken(>wa->commFifo,tok,sizeof(tok),&closed,&err);
|
||||
gtwa->lssNID = getU32(tok, 1, 127, &err);
|
||||
if (err) break;
|
||||
|
||||
closed = -1;
|
||||
CO_fifo_readToken(>wa->commFifo,tok,sizeof(tok),&closed,&err);
|
||||
gtwa->lssStore = (bool_t)getU32(tok, 0, 1, &err);
|
||||
if (err) break;
|
||||
|
||||
if (closed == 1) {
|
||||
/* No other arguments, prepare lssFastscan, all zero */
|
||||
memset(>wa->lssFastscan, 0, sizeof(gtwa->lssFastscan));
|
||||
}
|
||||
}
|
||||
if (closed == 0) {
|
||||
/* more arguments follow */
|
||||
CO_LSSmaster_fastscan_t *fs = >wa->lssFastscan;
|
||||
|
||||
CO_fifo_readToken(>wa->commFifo,tok,sizeof(tok),&closed,&err);
|
||||
fs->scan[CO_LSS_FASTSCAN_VENDOR_ID] = getU32(tok, 0, 2, &err);
|
||||
if (err) break;
|
||||
|
|
@ -1704,10 +1713,9 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
|
|||
}
|
||||
else if (gtwa->state == CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
CO_LSS_address_t lssAddress;
|
||||
|
||||
ret = CO_LSSmaster_InquireLssAddress(gtwa->LSSmaster, timeDifference_us,
|
||||
&lssAddress);
|
||||
>wa->lssAddress);
|
||||
if (ret != CO_LSSmaster_WAIT_SLAVE) {
|
||||
if (ret == CO_LSSmaster_OK) {
|
||||
gtwa->respBufCount =
|
||||
|
|
@ -1715,10 +1723,10 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
|
|||
"[%"PRId32"] 0x%08"PRIX32" 0x%08"PRIX32 \
|
||||
" 0x%08"PRIX32" 0x%08"PRIX32"\r\n",
|
||||
gtwa->sequence,
|
||||
lssAddress.identity.vendorID,
|
||||
lssAddress.identity.productCode,
|
||||
lssAddress.identity.revisionNumber,
|
||||
lssAddress.identity.serialNumber);
|
||||
gtwa->lssAddress.identity.vendorID,
|
||||
gtwa->lssAddress.identity.productCode,
|
||||
gtwa->lssAddress.identity.revisionNumber,
|
||||
gtwa->lssAddress.identity.serialNumber);
|
||||
respBufTransfer(gtwa);
|
||||
}
|
||||
else {
|
||||
|
|
@ -1839,6 +1847,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" \
|
||||
"[%"PRId32"] OK\r\n";
|
||||
char msg2[sizeof(msg2Fmt)+10] = {0};
|
||||
|
|
@ -1863,7 +1872,7 @@ void CO_GTWA_process(CO_GTWA_t *gtwa,
|
|||
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",
|
||||
gtwa->lssNID,
|
||||
lssNidAssigned,
|
||||
gtwa->lssFastscan.found.identity.vendorID,
|
||||
gtwa->lssFastscan.found.identity.productCode,
|
||||
gtwa->lssFastscan.found.identity.revisionNumber,
|
||||
|
|
|
|||
|
|
@ -133,14 +133,14 @@ lss_inquire_addr [<LSSSUB=0..3>] # Inquire LSS address.
|
|||
lss_get_node # Inquire node-ID.
|
||||
_lss_fastscan [<timeout_ms>] # Identify fastscan, non-standard.
|
||||
lss_allnodes [<timeout_ms> [<nodeStart=1..127> <store=0|1>\\
|
||||
<scanType0=0..2> <vendorId> <scanType1=0..2> <productCode>\\
|
||||
<scanType2=0..2> <revisionNo> <scanType3=0..2> <serialNo>]]
|
||||
[<scanType0> <vendorId> <scanType1> <productCode>\\
|
||||
<scanType2> <revisionNo> <scanType3> <serialNo>]]]
|
||||
# Node-ID configuration of all nodes.
|
||||
|
||||
<table_index>: 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s,
|
||||
4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto
|
||||
|
||||
All LSS commands start with '"["<sequence>"]" [<net>]'.
|
||||
* All LSS commands start with '\"[\"<sequence>\"]\" [<net>]'.
|
||||
* <table_index>: 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s,
|
||||
4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto
|
||||
* <scanType>: 0=fastscan, 1=ignore, 2=match value in next parameter
|
||||
* @endcode
|
||||
*
|
||||
* This help text is the same as variable contents in CO_GTWA_helpString.
|
||||
|
|
|
|||
75
CANopen.c
75
CANopen.c
|
|
@ -66,8 +66,7 @@ static uint32_t CO_traceBufferSize[CO_NO_TRACE];
|
|||
|| ODL_consumerHeartbeatTime_arrayLength == 0 \
|
||||
|| ODL_errorStatusBits_stringLength < 10 \
|
||||
|| CO_NO_LSS_SLAVE > 1 \
|
||||
|| CO_NO_LSS_MASTER > 1 \
|
||||
|| (CO_NO_LSS_SLAVE > 0 && CO_NO_LSS_MASTER > 0)
|
||||
|| CO_NO_LSS_MASTER > 1
|
||||
#error Features from CO_OD.h file are not corectly configured for this project!
|
||||
#endif
|
||||
|
||||
|
|
@ -80,7 +79,8 @@ static uint32_t CO_traceBufferSize[CO_NO_TRACE];
|
|||
#define CO_RXCAN_SDO_SRV (CO_RXCAN_RPDO + CO_NO_RPDO)
|
||||
#define CO_RXCAN_SDO_CLI (CO_RXCAN_SDO_SRV + CO_NO_SDO_SERVER)
|
||||
#define CO_RXCAN_CONS_HB (CO_RXCAN_SDO_CLI + CO_NO_SDO_CLIENT)
|
||||
#define CO_RXCAN_LSS (CO_RXCAN_CONS_HB + CO_NO_HB_CONS)
|
||||
#define CO_RXCAN_LSS_SLV (CO_RXCAN_CONS_HB + CO_NO_HB_CONS)
|
||||
#define CO_RXCAN_LSS_MST (CO_RXCAN_LSS_SLV + CO_NO_LSS_SLAVE)
|
||||
#define CO_RXCAN_NO_MSGS (CO_NO_NMT + \
|
||||
CO_NO_SYNC + \
|
||||
CO_NO_EM_CONS + \
|
||||
|
|
@ -101,7 +101,8 @@ static uint32_t CO_traceBufferSize[CO_NO_TRACE];
|
|||
#define CO_TXCAN_SDO_SRV (CO_TXCAN_TPDO + CO_NO_TPDO)
|
||||
#define CO_TXCAN_SDO_CLI (CO_TXCAN_SDO_SRV + CO_NO_SDO_SERVER)
|
||||
#define CO_TXCAN_HB (CO_TXCAN_SDO_CLI + CO_NO_SDO_CLIENT)
|
||||
#define CO_TXCAN_LSS (CO_TXCAN_HB + CO_NO_HB_PROD)
|
||||
#define CO_TXCAN_LSS_SLV (CO_TXCAN_HB + CO_NO_HB_PROD)
|
||||
#define CO_TXCAN_LSS_MST (CO_TXCAN_LSS_SLV + CO_NO_LSS_SLAVE)
|
||||
#define CO_TXCAN_NO_MSGS (CO_NO_NMT_MST + \
|
||||
CO_NO_SYNC + \
|
||||
CO_NO_EMERGENCY + \
|
||||
|
|
@ -538,8 +539,8 @@ CO_ReturnError_t CO_CANinit(void *CANptr,
|
|||
|
||||
/******************************************************************************/
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
CO_ReturnError_t CO_LSSinit(uint8_t nodeId,
|
||||
uint16_t bitRate)
|
||||
CO_ReturnError_t CO_LSSinit(uint8_t *nodeId,
|
||||
uint16_t *bitRate)
|
||||
{
|
||||
CO_LSS_address_t lssAddress;
|
||||
CO_ReturnError_t err;
|
||||
|
|
@ -555,10 +556,10 @@ CO_ReturnError_t CO_LSSinit(uint8_t nodeId,
|
|||
bitRate,
|
||||
nodeId,
|
||||
CO->CANmodule[0],
|
||||
CO_RXCAN_LSS,
|
||||
CO_RXCAN_LSS_SLV,
|
||||
CO_CAN_ID_LSS_SRV,
|
||||
CO->CANmodule[0],
|
||||
CO_TXCAN_LSS,
|
||||
CO_TXCAN_LSS_SLV,
|
||||
CO_CAN_ID_LSS_CLI);
|
||||
|
||||
return err;
|
||||
|
|
@ -572,6 +573,13 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId) {
|
|||
CO_ReturnError_t err;
|
||||
|
||||
/* Verify CANopen Node-ID */
|
||||
CO->nodeIdUnconfigured = false;
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
if (nodeId == CO_LSS_NODE_ID_ASSIGNMENT) {
|
||||
CO->nodeIdUnconfigured = true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (nodeId < 1 || nodeId > 127) {
|
||||
return CO_ERROR_PARAMETERS;
|
||||
}
|
||||
|
|
@ -597,8 +605,8 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId) {
|
|||
#endif
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
if (CO->nodeIdUnconfiguredLSS) {
|
||||
return CO_ERROR_NO;
|
||||
if (CO->nodeIdUnconfigured) {
|
||||
return CO_ERROR_NODE_ID_UNCONFIGURED_LSS;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -783,10 +791,10 @@ CO_ReturnError_t CO_CANopenInit(uint8_t nodeId) {
|
|||
err = CO_LSSmaster_init(CO->LSSmaster,
|
||||
CO_LSSmaster_DEFAULT_TIMEOUT,
|
||||
CO->CANmodule[0],
|
||||
CO_RXCAN_LSS,
|
||||
CO_RXCAN_LSS_MST,
|
||||
CO_CAN_ID_LSS_CLI,
|
||||
CO->CANmodule[0],
|
||||
CO_TXCAN_LSS,
|
||||
CO_TXCAN_LSS_MST,
|
||||
CO_CAN_ID_LSS_SRV);
|
||||
|
||||
if (err) return err;
|
||||
|
|
@ -848,15 +856,15 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
|
|||
bool_t NMTisPreOrOperational = false;
|
||||
CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
bool_t resetLSS = CO_LSSslave_process(co->LSSslave);
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE
|
||||
CO_LEDs_process(co->LEDs,
|
||||
timeDifference_us,
|
||||
CO_isError(co->em, CO_EM_CAN_TX_BUS_OFF),
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
co->nodeIdUnconfiguredLSS,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
co->nodeIdUnconfigured,
|
||||
0, /* RPDO event timer timeout */
|
||||
CO_isError(co->em, CO_EM_SYNC_TIME_OUT),
|
||||
CO_isError(co->em, CO_EM_HEARTBEAT_CONSUMER)
|
||||
|
|
@ -876,6 +884,15 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
|
|||
timerNext_us);
|
||||
#endif /* (CO_CONFIG_LEDS) & CO_CONFIG_LEDS_ENABLE */
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
if (resetLSS) {
|
||||
reset = CO_RESET_COMM;
|
||||
}
|
||||
if (co->nodeIdUnconfigured) {
|
||||
return reset;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (co->NMT->operatingState == CO_NMT_PRE_OPERATIONAL ||
|
||||
co->NMT->operatingState == CO_NMT_OPERATIONAL)
|
||||
NMTisPreOrOperational = true;
|
||||
|
|
@ -918,7 +935,7 @@ CO_NMT_reset_cmd_t CO_process(CO_t *co,
|
|||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
/* Gateway-ascii */
|
||||
CO_GTWA_process(CO->gtwa,
|
||||
CO_GTWA_process(co->gtwa,
|
||||
CO_GTWA_ENABLE,
|
||||
timeDifference_us,
|
||||
timerNext_us);
|
||||
|
|
@ -934,13 +951,19 @@ bool_t CO_process_SYNC(CO_t *co,
|
|||
uint32_t timeDifference_us,
|
||||
uint32_t *timerNext_us)
|
||||
{
|
||||
bool_t syncWas = false;
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
if (co->nodeIdUnconfigured) {
|
||||
return syncWas;
|
||||
}
|
||||
#endif
|
||||
|
||||
const CO_SYNC_status_t sync_process = CO_SYNC_process(co->SYNC,
|
||||
timeDifference_us,
|
||||
OD_synchronousWindowLength,
|
||||
timerNext_us);
|
||||
|
||||
bool_t syncWas = false;
|
||||
|
||||
switch (sync_process) {
|
||||
case CO_SYNC_NONE:
|
||||
break;
|
||||
|
|
@ -963,6 +986,12 @@ void CO_process_RPDO(CO_t *co,
|
|||
{
|
||||
int16_t i;
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
if (co->nodeIdUnconfigured) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < CO_NO_RPDO; i++) {
|
||||
CO_RPDO_process(co->RPDO[i], syncWas);
|
||||
}
|
||||
|
|
@ -977,6 +1006,12 @@ void CO_process_TPDO(CO_t *co,
|
|||
{
|
||||
int16_t i;
|
||||
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
if (co->nodeIdUnconfigured) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Verify PDO Change Of State and process PDOs */
|
||||
for (i = 0; i < CO_NO_TPDO; i++) {
|
||||
if (!co->TPDO[i]->sendRequest)
|
||||
|
|
|
|||
19
CANopen.h
19
CANopen.h
|
|
@ -258,6 +258,7 @@ extern "C" {
|
|||
* CANopen object with pointers to all CANopenNode objects.
|
||||
*/
|
||||
typedef struct {
|
||||
bool_t nodeIdUnconfigured; /**< True in unconfigured LSS slave */
|
||||
CO_CANmodule_t *CANmodule[1]; /**< CAN module objects */
|
||||
CO_SDO_t *SDO[CO_NO_SDO_SERVER]; /**< SDO object */
|
||||
CO_EM_t *em; /**< Emergency report object */
|
||||
|
|
@ -341,15 +342,16 @@ CO_ReturnError_t CO_CANinit(void *CANptr,
|
|||
/**
|
||||
* Initialize CANopen LSS slave
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
* Function must be called before CO_CANopenInit.
|
||||
*
|
||||
* @param nodeId Node ID of the CANopen device (1 ... 127) or
|
||||
* CO_LSS_NODE_ID_ASSIGNMENT
|
||||
* @param bitRate CAN bit rate.
|
||||
* See #CO_LSSslave_init() for description of parameters.
|
||||
*
|
||||
* @param [in,out] pendingNodeID Pending node ID or 0xFF(unconfigured)
|
||||
* @param [in,out] pendingBitRate Pending bit rate of the CAN interface
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSinit(uint8_t nodeId,
|
||||
uint16_t bitRate);
|
||||
CO_ReturnError_t CO_LSSinit(uint8_t *pendingNodeID,
|
||||
uint16_t *pendingBitRate);
|
||||
#endif /* CO_NO_LSS_SLAVE == 1 */
|
||||
|
||||
|
||||
|
|
@ -358,7 +360,10 @@ CO_ReturnError_t CO_LSSinit(uint8_t nodeId,
|
|||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param nodeId Node ID of the CANopen device (1 ... 127).
|
||||
* @param nodeId CANopen Node ID (1 ... 127) or 0xFF(unconfigured). In the
|
||||
* CANopen initialization it is the same as pendingBitRate from CO_LSSinit().
|
||||
* If it is unconfigured, then some CANopen objects will not be initialized nor
|
||||
* processed.
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_ReturnError_t CO_CANopenInit(uint8_t nodeId);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Change Log
|
|||
- Heartbeat is send immediately after NMT state changes.
|
||||
- SDO client is rewritten. Now includes read/write fifo interface to transfer data.
|
||||
- LED indicator indication (CiA303-3) moved from NMT into own files. Now fully comply to standard.
|
||||
- LSS slave is integrated into CANopenNode more directly.
|
||||
### Changed SocketCAN
|
||||
- ./stack/socketCAN removed from the project, ./stack/Neuberger-socketCAN moved to ./socketCAN
|
||||
- driver API updated
|
||||
|
|
|
|||
|
|
@ -2,74 +2,107 @@ LSS usage
|
|||
=========
|
||||
|
||||
LSS (Layer settings service) is an extension to CANopen described in CiA DSP 305. The
|
||||
interface is described in CiA DS 309 2.1.0 (ASCII mapping).
|
||||
interface is described in CiA DS 309 3.0.0 (ASCII mapping).
|
||||
LSS allows the user to change node ID and bitrate, as well as setting the node ID on an
|
||||
unconfigured node.
|
||||
|
||||
LSS uses the the OD Identity register as an unique value to select a node. Therefore
|
||||
LSS uses the the OD Identity register (0x1018) as an unique value to select a node. Therefore
|
||||
the LSS address always consists of four 32 bit values. This also means that LSS relies
|
||||
on this register to actually be unique.
|
||||
on this register to actually be unique. (_vendorID_, _productCode_, _revisionNumber_ and
|
||||
_serialNumber_ must be configured and unique on each device.)
|
||||
|
||||
To use LSS, a compatible node is needed. Note that canopend only includes LSS master
|
||||
functionality.
|
||||
### Preparation for testing on Linux virtual CAN
|
||||
LSS can be tested on Linux virtual CAN, similar as in gettingStarted.md.
|
||||
|
||||
The following example show some typical use cases for LSS:
|
||||
1. Open terminal, setup _vcan0_ and _cd_ to CANopenNode directory.
|
||||
2. Make some unique CANopen devices:
|
||||
- Edit CO_OD.c, change initialization for identity, for example change line to `/*1018*/ {0x4, 0x1L, 0x2L, 0x3L, 0x4L},`
|
||||
- `make`
|
||||
- `mv canopend canopend4`
|
||||
- Edit CO_OD.c, for example: `/*1018*/ {0x4, 0x1L, 0x2L, 0x3L, 0x5L},`
|
||||
- `make`
|
||||
- `mv canopend canopend5`
|
||||
- Repeat this step and create three further "unique" CANopen devices.
|
||||
3. Clear default OD storage file. We will use default (empty) storage for all instances:
|
||||
- `echo "-" > od_storage`
|
||||
4. Run "master" with command interface and node-id = 1. Note that this device
|
||||
has enabled both: LSS master and LSS slave. But LSS master does not 'see' own LSS slave.
|
||||
- `make`
|
||||
- `./canopend vcan0 -i1 -c "stdio"`
|
||||
5. Run one CANopen device with node-id=22 in own terminal:
|
||||
- `./canopend4 vcan0 -i22`
|
||||
6. Run other unique CANopen devices with unconfigured node-id, each in own terminal:
|
||||
- `./canopend5 vcan0 -i0xFF`
|
||||
- `./canopend6 vcan0 -i0xFF`
|
||||
- `./canopend7 vcan0 -i0xFF`
|
||||
- `./canopend8 vcan0 -i0xFF`
|
||||
7. Note that `lss_store` does not work in this example. For it to work, OD storage must be used properly.
|
||||
|
||||
### Typical usage of LSS
|
||||
- Changing the node ID for a known slave, store the new node ID to eeprom, apply new node ID.
|
||||
The node currently has the node ID 22.
|
||||
|
||||
$ ./canopencomm lss_switch_sel 0x00000428 0x00000431 0x00000002 0x5C17EEBC
|
||||
$ ./canopencomm lss_set_node 4
|
||||
$ ./canopencomm lss_store
|
||||
$ ./canopencomm lss_switch_glob 0
|
||||
$ ./canopencomm 22 reset communication
|
||||
help lss
|
||||
|
||||
Note that the node ID change is not done until reset communication/node
|
||||
lss_switch_sel 0x00000001 0x00000002 0x00000003 0x00000004
|
||||
lss_set_node 10
|
||||
lss_store
|
||||
lss_switch_glob 0
|
||||
22 reset communication
|
||||
|
||||
Note that the node ID change is not done until reset communication/node.
|
||||
|
||||
- Changing the node ID for a known slave, store the new node ID to eeprom, apply new node ID.
|
||||
The node currently has an invalid node ID.
|
||||
|
||||
$ ./canopencomm lss_switch_sel 0x00000428 0x00000431 0x00000002 0x5C17EEBC
|
||||
$ ./canopencomm lss_set_node 4
|
||||
$ ./canopencomm lss_store
|
||||
$ ./canopencomm lss_switch_glob 0
|
||||
lss_switch_sel 0x00000001 0x00000002 0x00000003 0x00000005
|
||||
lss_set_node 11
|
||||
lss_store
|
||||
lss_switch_glob 0
|
||||
|
||||
Note that the node ID is automatically applied.
|
||||
Note that the node ID is automatically applied. This can be seen on `candump`.
|
||||
|
||||
### LSS fastscan
|
||||
- Search for a node via LSS fastscan, store the new node ID to eeprom, apply new node ID
|
||||
|
||||
$ ./canopencomm [1] _lss_fastscan
|
||||
_lss_fastscan
|
||||
|
||||
[1] 0x00000428 0x00000432 0x00000002 0x6C81413C
|
||||
[0] 0x00000001 0x00000002 0x00000003 0x00000006
|
||||
|
||||
$ ./canopencomm lss_set_node 4
|
||||
$ ./canopencomm lss_store
|
||||
$ ./canopencomm lss_switch_glob 0
|
||||
lss_set_node 12
|
||||
lss_store
|
||||
lss_switch_glob 0
|
||||
|
||||
To increase scanning speed, you can use
|
||||
|
||||
$ ./canopencomm [1] _lss_fastscan 25
|
||||
_lss_fastscan 25
|
||||
|
||||
where 25 is the scan step delay in ms. Be aware that the scan will become unreliable when
|
||||
the delay is set to low.
|
||||
|
||||
We won't configure this node now, reset LSS. Now we have 1+3 nodes operational in our example.
|
||||
|
||||
lss_switch_glob 0
|
||||
|
||||
### Auto enumerate all nodes
|
||||
- Auto enumerate all nodes via LSS fastscan. Enumeration automatically begins at node ID 2
|
||||
and node ID is automatically stored to eeprom. Like with _lss_fastscan, an optional
|
||||
parameter can be used to change default delay time.
|
||||
|
||||
$ ./canopencomm lss_allnodes
|
||||
lss_allnodes
|
||||
|
||||
[1] OK, found 3 nodes starting at node ID 2.
|
||||
# Node-ID 2 assigned to: 0x00000001 0x00000002 0x00000003 0x00000007
|
||||
# Node-ID 3 assigned to: 0x00000001 0x00000002 0x00000003 0x00000008
|
||||
# Found 2 nodes, search finished.
|
||||
[0] OK
|
||||
|
||||
- To get further control over the fastscan process, the lss_allnodes command supports
|
||||
an extended parameter set. If you want to use this set, all parameters are mandatory.
|
||||
an extended parameter set.
|
||||
Auto enumerate all nodes via LSS fastscan. Set delay time to 25ms, set enumeration start
|
||||
to node ID 7, do not store LSS address in eeprom, enumerate only devices with vendor ID
|
||||
"0x428", ignore product code and software revision, scan for serial number
|
||||
|
||||
$ ./canopencomm lss_allnodes 25 7 0 2 0x428 1 0 1 0 0 0
|
||||
|
||||
[1] OK, found 2 nodes starting at node ID 7.
|
||||
lss_allnodes 25 7 0 2 0x428 1 0 1 0 0 0
|
||||
|
||||
The parameters are as following:
|
||||
- 25 scan step delay time in ms
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ Now you can enter the big world of [CANopen devices](http://can-newsletter.org/h
|
|||
|
||||
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.
|
||||
|
||||
Assigning Node-ID or CAN bitrate to unconfigured nodes, which support LSS configuration, is described in *LSSusage.md*.
|
||||
Assigning Node-ID or CAN bitrate, which support LSS configuration, is described in *LSSusage.md*.
|
||||
|
||||
Some further CANopenNode related Linux tools are available in [CANopenSocket](https://github.com/CANopenNode/CANopenSocket).
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
|
||||
/* Global variables and objects */
|
||||
volatile static bool_t CANopenConfiguredOK = false; /* Indication if CANopen modules are configured */
|
||||
volatile uint16_t CO_timer1ms = 0U; /* variable increments each millisecond */
|
||||
uint8_t LED_red, LED_green;
|
||||
|
||||
|
|
@ -48,6 +49,10 @@ int main (void){
|
|||
CO_ReturnError_t err;
|
||||
CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
|
||||
uint32_t heapMemoryUsed;
|
||||
void *CANmoduleAddress = NULL; /* CAN module address */
|
||||
uint8_t pendingNodeId = 10; /* read from dip switches or nonvolatile memory, configurable by LSS slave */
|
||||
uint8_t activeNodeId = 10; /* Copied from CO_pendingNodeId in the communication reset section */
|
||||
uint16_t pendingBitRate = 125; /* read from dip switches or nonvolatile memory, configurable by LSS slave */
|
||||
|
||||
/* Configure microcontroller. */
|
||||
|
||||
|
|
@ -75,21 +80,30 @@ int main (void){
|
|||
/* CANopen communication reset - initialize CANopen objects *******************/
|
||||
uint16_t timer1msPrevious;
|
||||
|
||||
log_printf("CANopenNode - Reset communication\n");
|
||||
log_printf("CANopenNode - Reset communication...\n");
|
||||
|
||||
/* disable CAN and CAN interrupts */
|
||||
CANopenConfiguredOK = false;
|
||||
|
||||
/* initialize CANopen */
|
||||
err = CO_CANinit(NULL /* CAN module address */, 125 /* bit rate */);
|
||||
err = CO_CANinit(CANmoduleAddress, pendingBitRate);
|
||||
if (err != CO_ERROR_NO) {
|
||||
log_printf("Error: CAN initialization failed: %d\n", err);
|
||||
return 0;
|
||||
}
|
||||
err = CO_CANopenInit(10 /* NodeID */);
|
||||
err = CO_LSSinit(&pendingNodeId, &pendingBitRate);
|
||||
if(err != CO_ERROR_NO) {
|
||||
log_printf("Error: LSS slave initialization failed: %d\n", err);
|
||||
return 0;
|
||||
}
|
||||
activeNodeId = pendingNodeId;
|
||||
err = CO_CANopenInit(activeNodeId);
|
||||
if(err == CO_ERROR_NO) {
|
||||
CANopenConfiguredOK = true;
|
||||
}
|
||||
else if(err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) {
|
||||
log_printf("Error: CANopen initialization failed: %d\n", err);
|
||||
return 0;
|
||||
/* CO_errorReport(CO->em, CO_EM_MEMORY_ALLOCATION_ERROR, CO_EMC_SOFTWARE_INTERNAL, err); */
|
||||
}
|
||||
|
||||
/* Configure Timer interrupt function for execution every 1 millisecond */
|
||||
|
|
@ -98,6 +112,12 @@ int main (void){
|
|||
/* Configure CAN transmit and receive interrupt */
|
||||
|
||||
|
||||
/* Configure CANopen callbacks, etc */
|
||||
if(CANopenConfiguredOK) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* start CAN */
|
||||
CO_CANsetNormalMode(CO->CANmodule[0]);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,8 +116,19 @@ static void threadMainWait_callback(void *object)
|
|||
}
|
||||
}
|
||||
|
||||
void threadMainWait_init(void)
|
||||
void threadMainWait_init(bool_t CANopenConfiguredOK)
|
||||
{
|
||||
/* Configure LSS slave callback function */
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE
|
||||
CO_LSSslave_initCallbackPre(CO->LSSslave, NULL, threadMainWait_callback);
|
||||
#endif
|
||||
|
||||
/* Initial value for time calculation */
|
||||
tmw.start = CO_LinuxThreads_clock_gettime_us();
|
||||
|
||||
if (!CANopenConfiguredOK)
|
||||
return;
|
||||
|
||||
/* Configure callback functions */
|
||||
CO_NMT_initCallbackPre(CO->NMT, NULL, threadMainWait_callback);
|
||||
CO_SDO_initCallbackPre(CO->SDO[0], NULL, threadMainWait_callback);
|
||||
|
|
@ -127,14 +138,19 @@ void threadMainWait_init(void)
|
|||
CO_SDOclient_initCallbackPre(CO->SDOclient[0], NULL,
|
||||
threadMainWait_callback);
|
||||
#endif
|
||||
#if (CO_CONFIG_TIME) & CO_CONFIG_FLAG_CALLBACK_PRE
|
||||
CO_TIME_initCallbackPre(CO->TIME, NULL, threadMainWait_callback);
|
||||
#endif
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_FLAG_CALLBACK_PRE
|
||||
#if (CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER
|
||||
CO_LSSmaster_initCallbackPre(CO->LSSmaster, NULL, threadMainWait_callback);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII
|
||||
CO_GTWA_initRead(CO->gtwa, gtwa_write_response, (void *)&tmw.gtwa_fd);
|
||||
tmw.freshCommand = true;
|
||||
#endif
|
||||
|
||||
/* Initial value for time calculation */
|
||||
tmw.start = CO_LinuxThreads_clock_gettime_us();
|
||||
}
|
||||
|
||||
void threadMainWait_initOnce(uint32_t interval_us,
|
||||
|
|
@ -296,11 +312,6 @@ void threadMainWait_initOnce(uint32_t interval_us,
|
|||
|
||||
void threadMainWait_close(void)
|
||||
{
|
||||
CO_NMT_initCallbackPre(CO->NMT, NULL, NULL);
|
||||
CO_SDO_initCallbackPre(CO->SDO[0], NULL, NULL);
|
||||
CO_EM_initCallbackPre(CO->em, NULL, NULL);
|
||||
CO_HBconsumer_initCallbackPre(CO->HBcons, NULL, NULL);
|
||||
|
||||
close(tmw.epoll_fd);
|
||||
tmw.epoll_fd = -1;
|
||||
|
||||
|
|
@ -402,10 +413,16 @@ uint32_t threadMainWait_process(CO_NMT_reset_cmd_t *reset)
|
|||
}
|
||||
else if (ev.data.fd == tmw.gtwa_fd) {
|
||||
char buf[CO_CONFIG_GTWA_COMM_BUF_SIZE];
|
||||
size_t space = CO_GTWA_write_getSpace(CO->gtwa);
|
||||
size_t space = CO->nodeIdUnconfigured ?
|
||||
CO_CONFIG_GTWA_COMM_BUF_SIZE :
|
||||
CO_GTWA_write_getSpace(CO->gtwa);
|
||||
|
||||
s = read(tmw.gtwa_fd, buf, space);
|
||||
if (s < 0 && errno != EAGAIN) {
|
||||
|
||||
if (CO->nodeIdUnconfigured) {
|
||||
/* purge data */
|
||||
}
|
||||
else if (s < 0 && errno != EAGAIN) {
|
||||
log_printf(LOG_DEBUG, DBG_ERRNO, "read(gtwa_fd)");
|
||||
}
|
||||
else if (s >= 0) {
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ typedef enum {
|
|||
* CANopenNode runs in two threads:
|
||||
* - timer based real-time thread for CAN receive, SYNC and PDO, see
|
||||
* CANrx_threadTmr_process()
|
||||
* - mainline thread for other processing, see threadMain_process() or
|
||||
* threadMainWait_process()
|
||||
* - mainline thread for other processing, see threadMainWait_process()
|
||||
*
|
||||
* The "threads" specified here do not fork threads themselves, but require
|
||||
* that two threads are provided by the calling application.
|
||||
|
|
@ -72,48 +71,17 @@ typedef enum {
|
|||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Initialize mainline thread - basic.
|
||||
*
|
||||
* @param callback this function is called to indicate #threadMain_process() has
|
||||
* work to do
|
||||
* @param object this pointer is given to _callback()_
|
||||
*/
|
||||
void threadMain_init(void (*callback)(void*), void *object);
|
||||
|
||||
|
||||
/**
|
||||
* Cleanup mainline thread - basic.
|
||||
*/
|
||||
void threadMain_close(void);
|
||||
|
||||
|
||||
/**
|
||||
* Process mainline thread - basic.
|
||||
*
|
||||
* threadMain is non-realtime thread for CANopenNode processing. It is
|
||||
* initialized by threadMain_init(). There is no configuration for CANopen
|
||||
* objects. There is also no configuration for epool or interval timer or
|
||||
* eventfd. These must be specified externally. For more complete function see
|
||||
* threadMainWait_process(), they are included.
|
||||
*
|
||||
* threadMain_process() calls CO_process() function for processing mainline
|
||||
* CANopen objects. It is non-blocking and should be called cyclically in 50 ms
|
||||
* intervals (typically). Function must also be called immediately after
|
||||
* callback provided in threadMain_init() is called.
|
||||
*
|
||||
* @param reset return value from CO_process() function.
|
||||
*/
|
||||
void threadMain_process(CO_NMT_reset_cmd_t *reset);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize mainline thread - blocking.
|
||||
*
|
||||
* Function must be called always in communication reset section, after
|
||||
* CO_CANopenInit().
|
||||
*
|
||||
* @param CANopenConfiguredOK True, if node has successfully passed NMT
|
||||
* initialization - it has a valid CANopen node-id, all CANopen objects
|
||||
* are configured and CANopen runs normally.
|
||||
*/
|
||||
void threadMainWait_init(void);
|
||||
void threadMainWait_init(bool_t CANopenConfiguredOK);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -71,8 +71,12 @@
|
|||
|
||||
|
||||
/* Other variables and objects */
|
||||
volatile static bool_t CANopenConfiguredOK = false; /* Indication if CANopen modules are configured */
|
||||
static int rtPriority = -1; /* Real time priority, configurable by arguments. (-1=RT disabled) */
|
||||
static int CO_ownNodeId = -1; /* Use value from Object Dictionary or set to 1..127 by arguments */
|
||||
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 */
|
||||
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 */
|
||||
static char *odStorFile_rom = "od_storage"; /* Name of the file */
|
||||
|
|
@ -127,7 +131,7 @@ static void EmergencyRxCallback(const uint16_t ident,
|
|||
const uint8_t errorBit,
|
||||
const uint32_t infoCode)
|
||||
{
|
||||
int16_t nodeIdRx = ident ? (ident&0x7F) : CO_ownNodeId;
|
||||
int16_t nodeIdRx = ident ? (ident&0x7F) : CO_activeNodeId;
|
||||
|
||||
log_printf(LOG_NOTICE, DBG_EMERGENCY_RX, nodeIdRx, errorCode,
|
||||
errorRegister, errorBit, infoCode);
|
||||
|
|
@ -161,6 +165,14 @@ static void HeartbeatNmtChangedCallback(uint8_t nodeId,
|
|||
nodeId, NmtState2Str(state), state);
|
||||
}
|
||||
|
||||
/* callback for storing node id and bitrate */
|
||||
static bool_t LSScfgStoreCallback(void *object, uint8_t id, uint16_t bitRate) {
|
||||
(void)object;
|
||||
OD_CANNodeID = id;
|
||||
OD_CANBitRate = bitRate;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Print usage */
|
||||
static void printUsage(char *progName) {
|
||||
printf(
|
||||
|
|
@ -168,8 +180,8 @@ printf(
|
|||
printf(
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -i <Node ID> CANopen Node-id (1..127). If not specified, value from\n"
|
||||
" Object dictionary (0x2101) is used.\n"
|
||||
" -i <Node ID> CANopen Node-id (1..127) or 0xFF(unconfigured). If not\n"
|
||||
" specified, value from Object dictionary (0x2101) is used.\n"
|
||||
" -p <RT priority> Real-time priority of RT thread (1 .. 99). If not set or\n"
|
||||
" set to -1, then normal scheduler is used for RT thread.\n"
|
||||
" -r Enable reboot on CANopen NMT reset_node command. \n"
|
||||
|
|
@ -238,7 +250,7 @@ int main (int argc, char *argv[]) {
|
|||
const char comm_tcp[] = "tcp-";
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
CO_ownNodeId = strtol(optarg, NULL, 0);
|
||||
CO_pendingNodeId = (uint8_t)strtol(optarg, NULL, 0);
|
||||
nodeIdFromArgs = true;
|
||||
break;
|
||||
case 'p': rtPriority = strtol(optarg, NULL, 0);
|
||||
|
|
@ -288,8 +300,17 @@ int main (int argc, char *argv[]) {
|
|||
CANdevice0Index = if_nametoindex(CANdevice);
|
||||
}
|
||||
|
||||
if(nodeIdFromArgs && (CO_ownNodeId < 1 || CO_ownNodeId > 127)) {
|
||||
log_printf(LOG_CRIT, DBG_WRONG_NODE_ID, CO_ownNodeId);
|
||||
if(!nodeIdFromArgs) {
|
||||
/* use value from Object dictionary, if not set by program arguments */
|
||||
CO_pendingNodeId = OD_CANNodeID;
|
||||
}
|
||||
|
||||
if((CO_pendingNodeId < 1 || CO_pendingNodeId > 127)
|
||||
#if CO_NO_LSS_SLAVE == 1
|
||||
&& CO_pendingNodeId != CO_LSS_NODE_ID_ASSIGNMENT
|
||||
#endif
|
||||
) {
|
||||
log_printf(LOG_CRIT, DBG_WRONG_NODE_ID, CO_pendingNodeId);
|
||||
printUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
|
@ -307,7 +328,7 @@ int main (int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_ownNodeId, "starting");
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_pendingNodeId, "starting");
|
||||
|
||||
|
||||
/* Allocate memory for CANopen objects */
|
||||
|
|
@ -352,9 +373,6 @@ int main (int argc, char *argv[]) {
|
|||
while(reset != CO_RESET_APP && reset != CO_RESET_QUIT && CO_endProgram == 0) {
|
||||
/* CANopen communication reset - initialize CANopen objects *******************/
|
||||
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_ownNodeId, "communication reset");
|
||||
|
||||
|
||||
/* Wait rt_thread. */
|
||||
if(!firstRun) {
|
||||
CO_LOCK_OD();
|
||||
|
|
@ -364,50 +382,62 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
|
||||
/* Enter CAN configuration. */
|
||||
CANopenConfiguredOK = false;
|
||||
CO_CANsetConfigurationMode((void *)CANdevice0Index);
|
||||
|
||||
|
||||
/* initialize CANopen */
|
||||
if(!nodeIdFromArgs) {
|
||||
/* use value from Object dictionary, if not set by program arguments */
|
||||
CO_ownNodeId = OD_CANNodeID;
|
||||
}
|
||||
|
||||
err = CO_CANinit((void *)CANdevice0Index, 0 /* bit rate not used */);
|
||||
if(err != CO_ERROR_NO) {
|
||||
log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANinit()", err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
err = CO_CANopenInit(CO_ownNodeId);
|
||||
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);
|
||||
}
|
||||
|
||||
CO_activeNodeId = CO_pendingNodeId;
|
||||
|
||||
err = CO_CANopenInit(CO_activeNodeId);
|
||||
if(err == CO_ERROR_NO) {
|
||||
CANopenConfiguredOK = true;
|
||||
}
|
||||
else if(err != CO_ERROR_NODE_ID_UNCONFIGURED_LSS) {
|
||||
log_printf(LOG_CRIT, DBG_CAN_OPEN, "CO_CANopenInit()", err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* initialize part of threadMain and callbacks */
|
||||
threadMainWait_init();
|
||||
CO_EM_initCallbackRx(CO->em, EmergencyRxCallback);
|
||||
CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback);
|
||||
CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL,
|
||||
HeartbeatNmtChangedCallback);
|
||||
|
||||
|
||||
/* 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);
|
||||
CO_OD_configure(CO->SDO[0], OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)&odStor, 0, 0U);
|
||||
if(odStorStatus_rom != CO_ERROR_NO) {
|
||||
CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)odStorStatus_rom);
|
||||
}
|
||||
if(odStorStatus_eeprom != CO_ERROR_NO) {
|
||||
CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)odStorStatus_eeprom + 1000);
|
||||
}
|
||||
|
||||
threadMainWait_init(CANopenConfiguredOK);
|
||||
CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL,
|
||||
LSScfgStoreCallback);
|
||||
if(CANopenConfiguredOK) {
|
||||
CO_EM_initCallbackRx(CO->em, EmergencyRxCallback);
|
||||
CO_NMT_initCallbackChanged(CO->NMT, NmtChangedCallback);
|
||||
CO_HBconsumer_initCallbackNmtChanged(CO->HBcons, NULL,
|
||||
HeartbeatNmtChangedCallback);
|
||||
/* 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);
|
||||
CO_OD_configure(CO->SDO[0], OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)&odStor, 0, 0U);
|
||||
if(odStorStatus_rom != CO_ERROR_NO) {
|
||||
CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)odStorStatus_rom);
|
||||
}
|
||||
if(odStorStatus_eeprom != CO_ERROR_NO) {
|
||||
CO_errorReport(CO->em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)odStorStatus_eeprom + 1000);
|
||||
}
|
||||
|
||||
#if CO_NO_TRACE > 0
|
||||
/* Initialize time */
|
||||
CO_time_init(&CO_time, CO->SDO[0], &OD_time.epochTimeBaseMs, &OD_time.epochTimeOffsetMs, 0x2130);
|
||||
/* Initialize time */
|
||||
CO_time_init(&CO_time, CO->SDO[0], &OD_time.epochTimeBaseMs, &OD_time.epochTimeOffsetMs, 0x2130);
|
||||
#endif
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_activeNodeId, "communication reset");
|
||||
}
|
||||
else {
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_activeNodeId, "node-id not initialized");
|
||||
}
|
||||
|
||||
/* First time only initialization. */
|
||||
if(firstRun) {
|
||||
|
|
@ -438,14 +468,14 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
#ifdef CO_USE_APPLICATION
|
||||
/* Execute optional additional application code */
|
||||
app_programStart();
|
||||
app_programStart(CANopenConfiguredOK);
|
||||
#endif
|
||||
} /* if(firstRun) */
|
||||
|
||||
|
||||
#ifdef CO_USE_APPLICATION
|
||||
/* Execute optional additional application code */
|
||||
app_communicationReset();
|
||||
app_communicationReset(CANopenConfiguredOK);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -454,7 +484,7 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
reset = CO_RESET_NOT;
|
||||
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_ownNodeId, "running ...");
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_activeNodeId, "running ...");
|
||||
|
||||
|
||||
while(reset == CO_RESET_NOT && CO_endProgram == 0) {
|
||||
|
|
@ -462,7 +492,7 @@ int main (int argc, char *argv[]) {
|
|||
uint32_t timer1usDiff = threadMainWait_process(&reset);
|
||||
|
||||
#ifdef CO_USE_APPLICATION
|
||||
app_programAsync(timer1usDiff);
|
||||
app_programAsync(CANopenConfiguredOK, timer1usDiff);
|
||||
#endif
|
||||
|
||||
CO_OD_storage_autoSave(&odStorAuto, timer1usDiff, 60000000);
|
||||
|
|
@ -492,7 +522,7 @@ int main (int argc, char *argv[]) {
|
|||
threadMainWait_close();
|
||||
CO_delete((void *)CANdevice0Index);
|
||||
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_ownNodeId, "finished");
|
||||
log_printf(LOG_INFO, DBG_CAN_OPEN_INFO, CO_activeNodeId, "finished");
|
||||
|
||||
/* Flush all buffers (and reboot) */
|
||||
if(rebootEnable && reset == CO_RESET_APP) {
|
||||
|
|
@ -528,7 +558,7 @@ static void* rt_thread(void* arg) {
|
|||
|
||||
#ifdef CO_USE_APPLICATION
|
||||
/* Execute optional additional application code */
|
||||
app_program1ms();
|
||||
app_program1ms(CANopenConfiguredOK);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue