1
0
Fork 0

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:
Janez 2020-06-09 14:55:07 +02:00
parent ae1b66e6ea
commit dd1677b5bc
16 changed files with 712 additions and 802 deletions

View file

@ -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;

View file

@ -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
*/

View file

@ -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 */

View file

@ -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;
}

View file

@ -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.

View file

@ -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(&gtwa->lssFastscan, 0, sizeof(gtwa->lssFastscan));
}
else {
CO_LSSmaster_fastscan_t *fs = &gtwa->lssFastscan;
/* read other arguments */
if (closed == 0) {
/* more arguments follow */
CO_fifo_readToken(&gtwa->commFifo,tok,sizeof(tok),&closed,&err);
gtwa->lssNID = getU32(tok, 1, 127, &err);
if (err) break;
closed = -1;
CO_fifo_readToken(&gtwa->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(&gtwa->lssFastscan, 0, sizeof(gtwa->lssFastscan));
}
}
if (closed == 0) {
/* more arguments follow */
CO_LSSmaster_fastscan_t *fs = &gtwa->lssFastscan;
CO_fifo_readToken(&gtwa->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);
&gtwa->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,

View file

@ -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.

View file

@ -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)

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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).

View file

@ -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]);

View file

@ -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) {

View file

@ -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);
/**

View file

@ -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
}