First try on LSS slave addon...
- needs #define CO_NO_LSS_SERVER 1 in CO_OD.h. This will need a change in the OD editor. - no Fastscan support yet - bitrate setting API untested - https://github.com/CANopenNode/CANopenNode/issues/46 (driver stuff) - changed some of the other stack files, tried my best to remain compatible with standard non LSS CANopenNode
This commit is contained in:
parent
efc6876c1b
commit
03982ad8e3
166
CANopen.c
166
CANopen.c
|
@ -92,7 +92,10 @@
|
|||
|| (CO_NO_RPDO < 1 || CO_NO_RPDO > 0x200) \
|
||||
|| (CO_NO_TPDO < 1 || CO_NO_TPDO > 0x200) \
|
||||
|| ODL_consumerHeartbeatTime_arrayLength == 0 \
|
||||
|| ODL_errorStatusBits_stringLength < 10
|
||||
|| ODL_errorStatusBits_stringLength < 10 \
|
||||
|| CO_NO_LSS_SERVER > 1 \
|
||||
|| CO_NO_LSS_CLIENT > 1 \
|
||||
|| (CO_NO_LSS_SERVER > 0 && CO_NO_LSS_CLIENT > 0)
|
||||
#error Features from CO_OD.h file are not corectly configured for this project!
|
||||
#endif
|
||||
|
||||
|
@ -110,8 +113,10 @@
|
|||
#define CO_RXCAN_SDO_SRV (CO_RXCAN_RPDO+CO_NO_RPDO) /* start index for SDO server message (request) */
|
||||
#define CO_RXCAN_SDO_CLI (CO_RXCAN_SDO_SRV+CO_NO_SDO_SERVER) /* start index for SDO client message (response) */
|
||||
#define CO_RXCAN_CONS_HB (CO_RXCAN_SDO_CLI+CO_NO_SDO_CLIENT) /* start index for Heartbeat Consumer messages */
|
||||
#define CO_RXCAN_LSS_SRV (CO_RXCAN_CONS_HB+CO_NO_LSS_SERVER) /* index for LSS server message (request) */
|
||||
#define CO_RXCAN_LSS_CLI (CO_RXCAN_LSS_SRV+CO_NO_LSS_CLIENT) /* index for LSS client message (response) */
|
||||
/* total number of received CAN messages */
|
||||
#define CO_RXCAN_NO_MSGS (1+CO_NO_SYNC+CO_NO_RPDO+CO_NO_SDO_SERVER+CO_NO_SDO_CLIENT+CO_NO_HB_CONS)
|
||||
#define CO_RXCAN_NO_MSGS (1+CO_NO_SYNC+CO_NO_RPDO+CO_NO_SDO_SERVER+CO_NO_SDO_CLIENT+CO_NO_HB_CONS+CO_NO_LSS_SERVER+CO_NO_LSS_CLIENT)
|
||||
|
||||
#define CO_TXCAN_NMT 0 /* index for NMT master message */
|
||||
#define CO_TXCAN_SYNC CO_TXCAN_NMT+CO_NO_NMT_MASTER /* index for SYNC message */
|
||||
|
@ -120,8 +125,10 @@
|
|||
#define CO_TXCAN_SDO_SRV (CO_TXCAN_TPDO+CO_NO_TPDO) /* start index for SDO server message (response) */
|
||||
#define CO_TXCAN_SDO_CLI (CO_TXCAN_SDO_SRV+CO_NO_SDO_SERVER) /* start index for SDO client message (request) */
|
||||
#define CO_TXCAN_HB (CO_TXCAN_SDO_CLI+CO_NO_SDO_CLIENT) /* index for Heartbeat message */
|
||||
#define CO_TXCAN_LSS_SRV (CO_TXCAN_HB+CO_NO_LSS_SERVER) /* index index for LSS server message (response) */
|
||||
#define CO_TXCAN_LSS_CLI (CO_TXCAN_LSS_SRV+CO_NO_LSS_CLIENT) /* index index for LSS client message (request) */
|
||||
/* total number of transmitted CAN messages */
|
||||
#define CO_TXCAN_NO_MSGS (CO_NO_NMT_MASTER+CO_NO_SYNC+CO_NO_EMERGENCY+CO_NO_TPDO+CO_NO_SDO_SERVER+CO_NO_SDO_CLIENT+1)
|
||||
#define CO_TXCAN_NO_MSGS (CO_NO_NMT_MASTER+CO_NO_SYNC+CO_NO_EMERGENCY+CO_NO_TPDO+CO_NO_SDO_SERVER+CO_NO_SDO_CLIENT+1+CO_NO_LSS_SERVER+CO_NO_LSS_CLIENT)
|
||||
|
||||
|
||||
#ifdef CO_USE_GLOBALS
|
||||
|
@ -138,6 +145,12 @@
|
|||
static CO_TPDO_t COO_TPDO[CO_NO_TPDO];
|
||||
static CO_HBconsumer_t COO_HBcons;
|
||||
static CO_HBconsNode_t COO_HBcons_monitoredNodes[CO_NO_HB_CONS];
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
static CO_LSSslave_t CO0_LSSslave;
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
static CO_LSSmaster_t CO0_LSSmaster;
|
||||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
static CO_SDOclient_t COO_SDOclient;
|
||||
#endif
|
||||
|
@ -189,15 +202,11 @@
|
|||
#endif
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_init(
|
||||
int32_t CANbaseAddress,
|
||||
uint8_t nodeId,
|
||||
uint16_t bitRate)
|
||||
{
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_new(void)
|
||||
{
|
||||
int16_t i;
|
||||
CO_ReturnError_t err;
|
||||
#ifndef CO_USE_GLOBALS
|
||||
uint16_t errCnt;
|
||||
#endif
|
||||
|
@ -241,6 +250,12 @@ CO_ReturnError_t CO_init(
|
|||
CO->TPDO[i] = &COO_TPDO[i];
|
||||
CO->HBcons = &COO_HBcons;
|
||||
CO_HBcons_monitoredNodes = &COO_HBcons_monitoredNodes[0];
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
CO->LSSslave = &CO0_LSSslave;
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
CO->CO_LSSmaster = &CO0_LSSmaster;
|
||||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
CO->SDOclient = &COO_SDOclient;
|
||||
#endif
|
||||
|
@ -274,6 +289,12 @@ CO_ReturnError_t CO_init(
|
|||
}
|
||||
CO->HBcons = (CO_HBconsumer_t *) calloc(1, sizeof(CO_HBconsumer_t));
|
||||
CO_HBcons_monitoredNodes = (CO_HBconsNode_t *) calloc(CO_NO_HB_CONS, sizeof(CO_HBconsNode_t));
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
CO->LSSslave = (CO_LSSslave_t *) calloc(1, sizeof(CO_LSSslave_t));
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
CO->LSSmaster = (CO_LSSmaster_t *) calloc(1, sizeof(CO_LSSmaster_t));
|
||||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
CO->SDOclient = (CO_SDOclient_t *) calloc(1, sizeof(CO_SDOclient_t));
|
||||
#endif
|
||||
|
@ -304,6 +325,12 @@ CO_ReturnError_t CO_init(
|
|||
+ sizeof(CO_TPDO_t) * CO_NO_TPDO
|
||||
+ sizeof(CO_HBconsumer_t)
|
||||
+ sizeof(CO_HBconsNode_t) * CO_NO_HB_CONS
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
+ sizeof(CO_LSSslave_t)
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
+ sizeof(CO_LSSmaster_t)
|
||||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
+ sizeof(CO_SDOclient_t)
|
||||
#endif
|
||||
|
@ -335,6 +362,12 @@ CO_ReturnError_t CO_init(
|
|||
}
|
||||
if(CO->HBcons == NULL) errCnt++;
|
||||
if(CO_HBcons_monitoredNodes == NULL) errCnt++;
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
if(CO->LSSslave == NULL) errCnt++;
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
if(CO->LSSmaster == NULL) errCnt++;
|
||||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
if(CO->SDOclient == NULL) errCnt++;
|
||||
#endif
|
||||
|
@ -346,19 +379,20 @@ CO_ReturnError_t CO_init(
|
|||
|
||||
if(errCnt != 0) return CO_ERROR_OUT_OF_MEMORY;
|
||||
#endif
|
||||
return CO_ERROR_NO;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_CANinit(
|
||||
int32_t CANbaseAddress,
|
||||
uint16_t bitRate)
|
||||
{
|
||||
CO_ReturnError_t err;
|
||||
|
||||
CO->CANmodule[0]->CANnormal = false;
|
||||
CO_CANsetConfigurationMode(CANbaseAddress);
|
||||
|
||||
/* Verify CANopen Node-ID */
|
||||
if(nodeId<1 || nodeId>127)
|
||||
{
|
||||
CO_delete(CANbaseAddress);
|
||||
return CO_ERROR_PARAMETERS;
|
||||
}
|
||||
|
||||
|
||||
err = CO_CANmodule_init(
|
||||
CO->CANmodule[0],
|
||||
CANbaseAddress,
|
||||
|
@ -368,7 +402,51 @@ CO_ReturnError_t CO_init(
|
|||
CO_TXCAN_NO_MSGS,
|
||||
bitRate);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
CO_ReturnError_t CO_LSSinit(
|
||||
uint8_t nodeId,
|
||||
uint16_t bitRate)
|
||||
{
|
||||
CO_LSS_address_t lssAddress;
|
||||
CO_ReturnError_t err;
|
||||
|
||||
lssAddress.productCode = OD_identity.productCode;
|
||||
lssAddress.revisionNumber = OD_identity.revisionNumber;
|
||||
lssAddress.serialNumber = OD_identity.serialNumber;
|
||||
lssAddress.vendorID = OD_identity.vendorID;
|
||||
err = CO_LSSslave_init(
|
||||
CO->LSSslave,
|
||||
lssAddress,
|
||||
bitRate,
|
||||
nodeId,
|
||||
CO->CANmodule[0],
|
||||
CO_RXCAN_LSS_SRV,
|
||||
CO_CAN_ID_LSS_SRV,
|
||||
CO->CANmodule[0],
|
||||
CO_TXCAN_LSS_CLI,
|
||||
CO_CAN_ID_LSS_CLI);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* CO_NO_LSS_SERVER == 1 */
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_CANopenInit(
|
||||
uint8_t nodeId)
|
||||
{
|
||||
int16_t i;
|
||||
CO_ReturnError_t err;
|
||||
|
||||
/* Verify CANopen Node-ID */
|
||||
if(nodeId<1 || nodeId>127) {
|
||||
return CO_ERROR_PARAMETERS;
|
||||
}
|
||||
|
||||
for (i=0; i<CO_NO_SDO_SERVER; i++)
|
||||
{
|
||||
|
@ -399,7 +477,7 @@ CO_ReturnError_t CO_init(
|
|||
CO_TXCAN_SDO_SRV+i);
|
||||
}
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
|
||||
|
||||
err = CO_EM_init(
|
||||
|
@ -415,7 +493,7 @@ CO_ReturnError_t CO_init(
|
|||
CO_TXCAN_EMERG,
|
||||
CO_CAN_ID_EMERGENCY + nodeId);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
|
||||
|
||||
err = CO_NMT_init(
|
||||
|
@ -430,7 +508,7 @@ CO_ReturnError_t CO_init(
|
|||
CO_TXCAN_HB,
|
||||
CO_CAN_ID_HEARTBEAT + nodeId);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
|
||||
|
||||
#if CO_NO_NMT_MASTER == 1
|
||||
|
@ -457,7 +535,7 @@ CO_ReturnError_t CO_init(
|
|||
CO->CANmodule[0],
|
||||
CO_TXCAN_SYNC);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
|
||||
|
||||
for(i=0; i<CO_NO_RPDO; i++){
|
||||
|
@ -480,7 +558,7 @@ CO_ReturnError_t CO_init(
|
|||
CANdevRx,
|
||||
CANdevRxIdx);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
}
|
||||
|
||||
|
||||
|
@ -500,7 +578,7 @@ CO_ReturnError_t CO_init(
|
|||
CO->CANmodule[0],
|
||||
CO_TXCAN_TPDO+i);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
}
|
||||
|
||||
|
||||
|
@ -514,7 +592,7 @@ CO_ReturnError_t CO_init(
|
|||
CO->CANmodule[0],
|
||||
CO_RXCAN_CONS_HB);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
|
||||
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
|
@ -527,7 +605,7 @@ CO_ReturnError_t CO_init(
|
|||
CO->CANmodule[0],
|
||||
CO_TXCAN_SDO_CLI);
|
||||
|
||||
if(err){CO_delete(CANbaseAddress); return err;}
|
||||
if(err){return err;}
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -553,6 +631,34 @@ CO_ReturnError_t CO_init(
|
|||
}
|
||||
#endif
|
||||
|
||||
return CO_ERROR_NO;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_init(
|
||||
int32_t CANbaseAddress,
|
||||
uint8_t nodeId,
|
||||
uint16_t bitRate)
|
||||
{
|
||||
CO_ReturnError_t err;
|
||||
|
||||
err = CO_new();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = CO_CANinit(CANbaseAddress, bitRate);
|
||||
if (err) {
|
||||
CO_delete(CANbaseAddress);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = CO_CANopenInit(nodeId);
|
||||
if (err) {
|
||||
CO_delete(CANbaseAddress);
|
||||
return err;
|
||||
}
|
||||
|
||||
return CO_ERROR_NO;
|
||||
}
|
||||
|
@ -577,6 +683,12 @@ void CO_delete(int32_t CANbaseAddress){
|
|||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
free(CO->SDOclient);
|
||||
#endif
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
free(CO->LSSslave);
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
free(CO->LSSmaster);
|
||||
#endif
|
||||
free(CO_HBcons_monitoredNodes);
|
||||
free(CO->HBcons);
|
||||
|
|
71
CANopen.h
71
CANopen.h
|
@ -89,6 +89,9 @@ extern "C" {
|
|||
#if CO_NO_TRACE > 0
|
||||
#include "CO_trace.h"
|
||||
#endif
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
#include "CO_LSSslave.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
|
@ -114,7 +117,9 @@ typedef enum{
|
|||
CO_CAN_ID_RPDO_4 = 0x500, /**< 0x500, Default RPDO5 (+nodeID) */
|
||||
CO_CAN_ID_TSDO = 0x580, /**< 0x580, SDO response from server (+nodeID) */
|
||||
CO_CAN_ID_RSDO = 0x600, /**< 0x600, SDO request from client (+nodeID) */
|
||||
CO_CAN_ID_HEARTBEAT = 0x700 /**< 0x700, Heartbeat message */
|
||||
CO_CAN_ID_HEARTBEAT = 0x700, /**< 0x700, Heartbeat message */
|
||||
CO_CAN_ID_LSS_CLI = 0x7E4, /**< 0x7E4, LSS response from server to client */
|
||||
CO_CAN_ID_LSS_SRV = 0x7E5 /**< 0x7E5, LSS request from client to server */
|
||||
}CO_Default_CAN_ID_t;
|
||||
|
||||
|
||||
|
@ -131,6 +136,12 @@ typedef struct{
|
|||
CO_RPDO_t *RPDO[CO_NO_RPDO];/**< RPDO objects */
|
||||
CO_TPDO_t *TPDO[CO_NO_TPDO];/**< TPDO objects */
|
||||
CO_HBconsumer_t *HBcons; /**< Heartbeat consumer object*/
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
CO_LSSslave_t *LSSslave; /**< LSS server/slave object */
|
||||
#endif
|
||||
#if CO_NO_LSS_CLIENT == 1
|
||||
CO_LSSmaster_t *LSSmaster; /**< LSS master/client object */
|
||||
#endif
|
||||
#if CO_NO_SDO_CLIENT == 1
|
||||
CO_SDOclient_t *SDOclient; /**< SDO client object */
|
||||
#endif
|
||||
|
@ -162,6 +173,60 @@ typedef struct{
|
|||
#endif
|
||||
|
||||
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
/**
|
||||
* Allocate and initialize memory for CANopen object
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
|
||||
* CO_ERROR_OUT_OF_MEMORY
|
||||
*/
|
||||
CO_ReturnError_t CO_new(void);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize CAN driver
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param CANbaseAddress Address of the CAN module, passed to CO_CANmodule_init().
|
||||
* @param bitRate CAN bit rate.
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
|
||||
* CO_ERROR_ILLEGAL_BAUDRATE, CO_ERROR_OUT_OF_MEMORY
|
||||
*/
|
||||
CO_ReturnError_t CO_CANinit(
|
||||
int32_t CANbaseAddress,
|
||||
uint16_t bitRate);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize CANopen LSS slave
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param nodeId Node ID of the CANopen device (1 ... 127) or CO_LSS_NODE_ID_ASSIGNMENT
|
||||
* @param bitRate CAN bit rate.
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSinit(
|
||||
uint8_t nodeId,
|
||||
uint16_t bitRate);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize CANopen stack.
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param nodeId Node ID of the CANopen device (1 ... 127).
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_ReturnError_t CO_CANopenInit(
|
||||
uint8_t nodeId);
|
||||
|
||||
|
||||
#else /* CO_NO_LSS_SERVER == 1 */
|
||||
/**
|
||||
* Initialize CANopen stack.
|
||||
*
|
||||
|
@ -169,7 +234,7 @@ typedef struct{
|
|||
*
|
||||
* @param CANbaseAddress Address of the CAN module, passed to CO_CANmodule_init().
|
||||
* @param nodeId Node ID of the CANopen device (1 ... 127).
|
||||
* @param nodeId CAN bit rate.
|
||||
* @param bitRate CAN bit rate.
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_ILLEGAL_ARGUMENT,
|
||||
* CO_ERROR_OUT_OF_MEMORY, CO_ERROR_ILLEGAL_BAUDRATE
|
||||
|
@ -179,6 +244,8 @@ CO_ReturnError_t CO_init(
|
|||
uint8_t nodeId,
|
||||
uint16_t bitRate);
|
||||
|
||||
#endif /* CO_NO_LSS_SERVER == 1 */
|
||||
|
||||
|
||||
/**
|
||||
* Delete CANopen object and free memory. Must be called at program exit.
|
||||
|
|
2
Doxyfile
2
Doxyfile
|
@ -341,7 +341,7 @@ IDL_PROPERTY_SUPPORT = YES
|
|||
# all members of a group must be documented explicitly.
|
||||
# The default value is: NO.
|
||||
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
DISTRIBUTE_GROUP_DOC = YES
|
||||
|
||||
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
|
||||
# (for instance a group of public functions) to be put as a subgroup of that
|
||||
|
|
2
Makefile
2
Makefile
|
@ -26,6 +26,8 @@ SOURCES = $(STACKDRV_SRC)/CO_driver.c \
|
|||
$(STACK_SRC)/CO_PDO.c \
|
||||
$(STACK_SRC)/CO_HBconsumer.c \
|
||||
$(STACK_SRC)/CO_SDOmaster.c \
|
||||
$(STACK_SRC)/CO_LSSmaster.c \
|
||||
$(STACK_SRC)/CO_LSSslave.c \
|
||||
$(STACK_SRC)/CO_trace.c \
|
||||
$(CANOPEN_SRC)/CANopen.c \
|
||||
$(APPL_SRC)/CO_OD.c \
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
/**
|
||||
* CANopen LSS Master/Slave protocol.
|
||||
*
|
||||
* @file CO_LSS.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 Neuberger Gebäudeautomation GmbH
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free and open source software: you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Following clarification and special exception to the GNU General Public
|
||||
* License is included to the distribution terms of CANopenNode:
|
||||
*
|
||||
* Linking this library statically or dynamically with other modules is
|
||||
* making a combined work based on this library. Thus, the terms and
|
||||
* conditions of the GNU General Public License cover the whole combination.
|
||||
*
|
||||
* As a special exception, the copyright holders of this library give
|
||||
* you permission to link this library with independent modules to
|
||||
* produce an executable, regardless of the license terms of these
|
||||
* independent modules, and to copy and distribute the resulting
|
||||
* executable under terms of your choice, provided that you also meet,
|
||||
* for each linked independent module, the terms and conditions of the
|
||||
* license of that module. An independent module is a module which is
|
||||
* not derived from or based on this library. If you modify this
|
||||
* library, you may extend this exception to your version of the
|
||||
* library, but you are not obliged to do so. If you do not wish
|
||||
* to do so, delete this exception statement from your version.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CO_LSS_H
|
||||
#define CO_LSS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS LSS
|
||||
* @ingroup CO_CANopen
|
||||
* @{
|
||||
*
|
||||
* CANopen Layer Setting Services protocol
|
||||
*
|
||||
* LSS protocol is according to CiA DSP 305 V3.0.0.
|
||||
*
|
||||
* LSS services and protocols are used to inquire or to change the settings
|
||||
* of three parameters of the physical layer, data link layer, and application
|
||||
* layer on a CANopen device with LSS slave capability by a CANopen device
|
||||
* with LSS master capability via the CAN network.
|
||||
*
|
||||
* The following parameters may be inquired or changed:
|
||||
* - Node-ID of the CANopen device
|
||||
* - Bit timing parameters of the physical layer (bit rate)
|
||||
* - LSS address compliant to the identity object (1018h)
|
||||
*
|
||||
* The connection is established in one of two ways:
|
||||
* - addressing a node by it's 128 bit LSS address. This requires that the
|
||||
* master already knows the node's LSS address.
|
||||
* - scanning the network for unknown nodes (Fastscan). Using this method,
|
||||
* unknown devices can be found and configured one by one.
|
||||
*
|
||||
* Be aware that changing the bit rate is a critical step for the network. A
|
||||
* failure will render the network unusable!
|
||||
*
|
||||
* For CAN identifiers see #CO_Default_CAN_ID_t
|
||||
*/
|
||||
|
||||
#if CO_NO_LSS_CLIENT == 1 || CO_NO_LSS_SERVER == 1
|
||||
|
||||
/**
|
||||
* LSS protocol command specifiers
|
||||
*
|
||||
* The LSS protocols are executed between the LSS master device and the LSS
|
||||
* slave device(s) to implement the LSS services. Some LSS protocols require
|
||||
* a sequence of CAN messages.
|
||||
*
|
||||
* As identifying method only "LSS fastscan" is supported.
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSS_SWITCH_STATE_GLOBAL = 0x04U, /**< Switch state global protocol */
|
||||
CO_LSS_SWITCH_STATE_SEL_VENDOR = 0x40U, /**< Switch state selective protocol - Vendor ID */
|
||||
CO_LSS_SWITCH_STATE_SEL_PRODUCT = 0x41U, /**< Switch state selective protocol - Product code */
|
||||
CO_LSS_SWITCH_STATE_SEL_REV = 0x42U, /**< Switch state selective protocol - Revision number */
|
||||
CO_LSS_SWITCH_STATE_SEL_SERIAL = 0x43U, /**< Switch state selective protocol - Serial number */
|
||||
CO_LSS_SWITCH_STATE_SEL = 0x44U, /**< Switch state selective protocol - Slave response */
|
||||
CO_LSS_CFG_NODE_ID = 0x11U, /**< Configure node ID protocol */
|
||||
CO_LSS_CFG_BIT_TIMING = 0x13U, /**< Configure bit timing parameter protocol */
|
||||
CO_LSS_CFG_ACTIVATE_BIT_TIMING = 0x15U, /**< Activate bit timing parameter protocol */
|
||||
CO_LSS_CFG_STORE = 0x17U, /**< Store configuration protocol */
|
||||
CO_LSS_IDENT_NON_CONFIG_REMOTE_SLAVE= 0x4CU, /**< Identify non configured slave - request */
|
||||
CO_LSS_IDENT_NON_CONFIG_SLAVE = 0x50U, /**< Identify non configured slave - response */
|
||||
CO_LSS_IDENT_FASTSCAN = 0x51U, /**< LSS Fastscan protocol */
|
||||
CO_LSS_INQUIRE_VENDOR = 0x5AU, /**< Inquire identity vendor-ID protocol */
|
||||
CO_LSS_INQUIRE_PRODUCT = 0x5BU, /**< Inquire identity product-code protocol */
|
||||
CO_LSS_INQUIRE_REV = 0x5CU, /**< Inquire identity revision-number protocol */
|
||||
CO_LSS_INQUIRE_SERIAL = 0x5DU, /**< Inquire identity serial-number protocol */
|
||||
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_NON_CONFIG_REMOTE_SLAVE && cs <= CO_LSS_IDENT_FASTSCAN)
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Error codes for Configure node ID protocol
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSS_CFG_NODE_ID_OK = 0x00U, /**< Protocol successfully completed */
|
||||
CO_LSS_CFG_NODE_ID_OUT_OF_RANGE = 0x01U /**< NID out of range */
|
||||
} CO_LSS_cfgNodeId_t;
|
||||
|
||||
/**
|
||||
* Error codes for Configure bit timing parameters protocol
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSS_CFG_BIT_TIMING_OK = 0x00U, /**< Protocol successfully completed */ //!< CO_LSS_CFG_BIT_TIMING_OK
|
||||
CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE = 0x01U /**< Bit timing / Bit rate not supported *///!< CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE
|
||||
} CO_LSS_cfgBitTiming_t;
|
||||
|
||||
/**
|
||||
* Error codes for Store configuration protocol
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSS_CFG_STORE_OK = 0x00U, /**< Protocol successfully completed */
|
||||
CO_LSS_CFG_STORE_NOT_SUPPORTED = 0x01U, /**< Store configuration not supported */
|
||||
CO_LSS_CFG_STORE_FAILED = 0x02U /**< Storage media access error */
|
||||
} CO_LSS_cfgStore_t;
|
||||
|
||||
/**
|
||||
* The LSS address is a 128 bit number, uniquely identifying each node. It
|
||||
* consists of the values in object 0x1018.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t vendorID;
|
||||
uint32_t productCode;
|
||||
uint32_t revisionNumber;
|
||||
uint32_t serialNumber;
|
||||
} CO_LSS_address_t;
|
||||
|
||||
/**
|
||||
* LSS finite state automaton
|
||||
*
|
||||
* The LSS FSA shall provide the following states:
|
||||
* - Initial: Pseudo state, indicating the activation of the FSA.
|
||||
* - LSS waiting: In this state, the LSS slave device waits for requests.
|
||||
* - LSS configuration: In this state variables may be configured in the LSS slave.
|
||||
* - Final: Pseudo state, indicating the deactivation of the FSA.
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSS_STATE_WAITING = 0, /**< LSS FSA waiting for requests*/
|
||||
CO_LSS_STATE_CONFIGURATION = 1, /**< LSS FSA waiting for configuration*/
|
||||
} CO_LSS_state_t;
|
||||
|
||||
/**
|
||||
* Definition of table_index for /CiA301/ bit timing table
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSS_BIT_TIMING_1000 = 0, /**< 1000kbit/s */
|
||||
CO_LSS_BIT_TIMING_800 = 1, /**< 800kbit/s */
|
||||
CO_LSS_BIT_TIMING_500 = 2, /**< 500kbit/s */
|
||||
CO_LSS_BIT_TIMING_250 = 3, /**< 250kbit/s */
|
||||
CO_LSS_BIT_TIMING_125 = 4, /**< 125kbit/s */
|
||||
/* reserved = 5 */
|
||||
CO_LSS_BIT_TIMING_50 = 6, /**< 50kbit/s */
|
||||
CO_LSS_BIT_TIMING_20 = 7, /**< 20kbit/s */
|
||||
CO_LSS_BIT_TIMING_10 = 8, /**< 10kbit/s */
|
||||
CO_LSS_BIT_TIMING_AUTO = 9, /**< Automatic bit rate detection */
|
||||
} CO_LSS_bitTimingTable_t;
|
||||
|
||||
/**
|
||||
* Lookup table for conversion between bit timing table and numerical
|
||||
* bit rate
|
||||
*/
|
||||
static const uint16_t CO_LSS_bitTimingTableLookup[] = {
|
||||
1000,
|
||||
800,
|
||||
500,
|
||||
250,
|
||||
125,
|
||||
0,
|
||||
50,
|
||||
20,
|
||||
10,
|
||||
0
|
||||
};
|
||||
|
||||
/**
|
||||
* Macro to check if index contains valid bit timing
|
||||
*/
|
||||
#define CO_LSS_BIT_TIMING_VALID(index) (index != 5 && (index >= CO_LSS_BIT_TIMING_1000 && index <= CO_LSS_BIT_TIMING_AUTO))
|
||||
|
||||
/**
|
||||
* Invalid node ID triggers node ID assignment
|
||||
*/
|
||||
#define CO_LSS_NODE_ID_ASSIGNMENT 0xFFU
|
||||
|
||||
/**
|
||||
* Macro to check if node id is valid
|
||||
*/
|
||||
#define CO_LSS_NODE_ID_VALID(nid) ((nid >= 1 && nid <= 0x7F) || nid == CO_LSS_NODE_ID_ASSIGNMENT)
|
||||
|
||||
/**
|
||||
* Macro to check if two LSS addresses are equal
|
||||
*/
|
||||
#define CO_LSS_ADDRESS_EQUAL(/*CO_LSS_address_t*/ a1, /*CO_LSS_address_t*/ a2) \
|
||||
(a1.productCode == a2.productCode && \
|
||||
a1.revisionNumber == a2.revisionNumber && \
|
||||
a1.serialNumber == a2.serialNumber && \
|
||||
a1.vendorID == a2.vendorID)
|
||||
|
||||
#endif /* CO_NO_LSS_CLIENT == 1 || CO_NO_LSS_SERVER == 1 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
/** @} */
|
||||
#endif
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* CANopen LSS Master/Slave protocol.
|
||||
*
|
||||
* @file CO_LSSmaster.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 Neuberger Gebäudeautomation GmbH
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free and open source software: you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Following clarification and special exception to the GNU General Public
|
||||
* License is included to the distribution terms of CANopenNode:
|
||||
*
|
||||
* Linking this library statically or dynamically with other modules is
|
||||
* making a combined work based on this library. Thus, the terms and
|
||||
* conditions of the GNU General Public License cover the whole combination.
|
||||
*
|
||||
* As a special exception, the copyright holders of this library give
|
||||
* you permission to link this library with independent modules to
|
||||
* produce an executable, regardless of the license terms of these
|
||||
* independent modules, and to copy and distribute the resulting
|
||||
* executable under terms of your choice, provided that you also meet,
|
||||
* for each linked independent module, the terms and conditions of the
|
||||
* license of that module. An independent module is a module which is
|
||||
* not derived from or based on this library. If you modify this
|
||||
* library, you may extend this exception to your version of the
|
||||
* library, but you are not obliged to do so. If you do not wish
|
||||
* to do so, delete this exception statement from your version.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CO_LSSmaster_H
|
||||
#define CO_LSSmaster_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "CO_LSS.h"
|
||||
|
||||
/**
|
||||
* @addtogroup CO_LSS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* LSS master object.
|
||||
*/
|
||||
typedef struct{
|
||||
uint16_t timeout; /**< LSS response timeout in ms */
|
||||
|
||||
uint8_t lssState; /**< #CO_LSS_state_t of the currently active slave */
|
||||
CO_LSS_address_t lssAddress; /**< #CO_LSS_address_t of the currently active slave */
|
||||
|
||||
}CO_LSSmaster_t;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize LSS object.
|
||||
*
|
||||
* Function must be called in the communication reset section. todo?
|
||||
*
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. todo
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSmaster_init(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
uint16_t timeout);
|
||||
|
||||
/**
|
||||
* Change LSS master timeout
|
||||
*
|
||||
* On LSS, a "negative ack" is signaled by the slave not answering. Because of
|
||||
* that, a low timeout value can significantly increase initialization speed in
|
||||
* some cases (e.g. fastscan). However, as soon as there is activity on the bus,
|
||||
* LSS messages can be delayed because of their high COB (see #CO_Default_CAN_ID_t).
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeout
|
||||
*/
|
||||
void CO_LSSmaster_changeTimeout(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
uint16_t timeout);
|
||||
|
||||
/**
|
||||
* Request LSS switch mode
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param command switch mode command
|
||||
* @param lssAddress LSS target address
|
||||
* @return todo
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSmaster_switchMode(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
CO_LSS_switch_t command,
|
||||
CO_LSS_address_t lssAddress);
|
||||
|
||||
/**
|
||||
* Request LSS configuration
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param command config command
|
||||
* @return todo
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSmaster_configure(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
CO_LSS_config_t command);
|
||||
|
||||
/**
|
||||
* Request LSS inquiry
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param command inquiry command
|
||||
* @return todo
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSmaster_inquire(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
CO_LSS_config_t command);
|
||||
|
||||
/**
|
||||
* Request LSS identification
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param command identification command
|
||||
* @return todo
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSmaster_identifify(
|
||||
CO_LSSmaster_t *LSSmaster,
|
||||
CO_LSS_config_t command);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
/** @} */
|
||||
#endif
|
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* CANopen LSS Slave protocol.
|
||||
*
|
||||
* @file CO_LSSslave.c
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 Neuberger Gebäudeautomation GmbH
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free and open source software: you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Following clarification and special exception to the GNU General Public
|
||||
* License is included to the distribution terms of CANopenNode:
|
||||
*
|
||||
* Linking this library statically or dynamically with other modules is
|
||||
* making a combined work based on this library. Thus, the terms and
|
||||
* conditions of the GNU General Public License cover the whole combination.
|
||||
*
|
||||
* As a special exception, the copyright holders of this library give
|
||||
* you permission to link this library with independent modules to
|
||||
* produce an executable, regardless of the license terms of these
|
||||
* independent modules, and to copy and distribute the resulting
|
||||
* executable under terms of your choice, provided that you also meet,
|
||||
* for each linked independent module, the terms and conditions of the
|
||||
* license of that module. An independent module is a module which is
|
||||
* not derived from or based on this library. If you modify this
|
||||
* library, you may extend this exception to your version of the
|
||||
* library, but you are not obliged to do so. If you do not wish
|
||||
* to do so, delete this exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "CANopen.h"
|
||||
#include "CO_LSSslave.h"
|
||||
|
||||
/*
|
||||
* Helper function - Handle service "switch state global"
|
||||
*/
|
||||
static void CO_LSSslave_serviceSwitchStateGlobal(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_cs_t service,
|
||||
const CO_CANrxMsg_t *msg)
|
||||
{
|
||||
uint8_t mode = msg->data[1];
|
||||
|
||||
switch (mode) {
|
||||
case CO_LSS_STATE_WAITING:
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
CO_memset((uint8_t*)&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,
|
||||
const CO_CANrxMsg_t *msg)
|
||||
{
|
||||
uint32_t value = CO_getUint32(&msg->data[1]);
|
||||
|
||||
if(LSSslave->lssState != CO_LSS_STATE_WAITING) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (service) {
|
||||
case CO_LSS_SWITCH_STATE_SEL_VENDOR:
|
||||
LSSslave->lssSelect.vendorID = value;
|
||||
break;
|
||||
case CO_LSS_SWITCH_STATE_SEL_PRODUCT:
|
||||
LSSslave->lssSelect.productCode = value;
|
||||
break;
|
||||
case CO_LSS_SWITCH_STATE_SEL_REV:
|
||||
LSSslave->lssSelect.revisionNumber = value;
|
||||
break;
|
||||
case CO_LSS_SWITCH_STATE_SEL_SERIAL:
|
||||
LSSslave->lssSelect.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;
|
||||
CO_memset(&LSSslave->TXbuff->data[1], 0, 7);
|
||||
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,
|
||||
const CO_CANrxMsg_t *msg)
|
||||
{
|
||||
uint8_t nid;
|
||||
uint8_t tableSelector;
|
||||
uint8_t tableIndex;
|
||||
uint8_t errorCode;
|
||||
|
||||
if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (service) {
|
||||
case CO_LSS_CFG_NODE_ID:
|
||||
nid = msg->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 */
|
||||
CO_memset(&LSSslave->TXbuff->data[2], 0, 6);
|
||||
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 = msg->data[1];
|
||||
tableIndex = msg->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 */
|
||||
CO_memset(&LSSslave->TXbuff->data[2], 0, 6);
|
||||
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_getUint16(&msg->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 != true) {
|
||||
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 */
|
||||
CO_memset(&LSSslave->TXbuff->data[2], 0, 6);
|
||||
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,
|
||||
const CO_CANrxMsg_t *msg)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (service) {
|
||||
case CO_LSS_INQUIRE_VENDOR:
|
||||
value = LSSslave->lssAddress.vendorID;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_PRODUCT:
|
||||
value = LSSslave->lssAddress.productCode;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_REV:
|
||||
value = LSSslave->lssAddress.revisionNumber;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_SERIAL:
|
||||
value = LSSslave->lssAddress.serialNumber;
|
||||
break;
|
||||
case CO_LSS_INQUIRE_NODE_ID:
|
||||
value = (uint32_t)LSSslave->activeNodeID;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
/* send response */
|
||||
LSSslave->TXbuff->data[0] = service;
|
||||
CO_setUint32(&LSSslave->TXbuff->data[1], value);
|
||||
CO_memset(&LSSslave->TXbuff->data[5], 0, 4);
|
||||
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,
|
||||
const CO_CANrxMsg_t *msg)
|
||||
{
|
||||
// uint32_t idNumber = CO_getUint32(&msg->data[1]);
|
||||
// uint8_t bitCheck = msg->data[5];
|
||||
// uint8_t lssSub = msg->data[6];
|
||||
// uint8_t lssNext = msg->data[7];
|
||||
//
|
||||
// if(LSSslave->lssState == CO_LSS_STATE_WAITING) {
|
||||
// //todo do fastscan
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* Read received message from CAN module.
|
||||
*
|
||||
* Function will be called (by CAN receive interrupt) every time, when CAN
|
||||
* message with correct identifier will be received. For more information and
|
||||
* description of parameters see file CO_driver.h.
|
||||
*/
|
||||
static void CO_LSSslave_receive(void *object, const CO_CANrxMsg_t *msg)
|
||||
{
|
||||
CO_LSSslave_t *LSSslave;
|
||||
|
||||
LSSslave = (CO_LSSslave_t*)object; /* this is the correct pointer type of the first argument */
|
||||
|
||||
if(msg->DLC == 8){
|
||||
CO_LSS_cs_t cs = msg->data[0];
|
||||
|
||||
if (CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs)) {
|
||||
CO_LSSslave_serviceSwitchStateGlobal(LSSslave, cs, msg);
|
||||
}
|
||||
else if (CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs)) {
|
||||
CO_LSSslave_serviceSwitchStateSelective(LSSslave, cs, msg);
|
||||
}
|
||||
else if (CO_LSS_CS_SERVICE_IS_CONFIG(cs)) {
|
||||
CO_LSSslave_serviceConfig(LSSslave, cs, msg);
|
||||
}
|
||||
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 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
CO_ReturnError_t CO_LSSslave_init(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_address_t lssAddress,
|
||||
uint16_t pendingBitRate,
|
||||
uint8_t pendingNodeID,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
uint32_t CANidLssMaster,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx,
|
||||
uint32_t CANidLssSlave)
|
||||
{
|
||||
/* verify arguments */
|
||||
if (LSSslave==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.vendorID==0 || lssAddress.serialNumber==0) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
CO_memcpy((uint8_t*)&LSSslave->lssAddress, (uint8_t*)&lssAddress, sizeof(LSSslave->lssAddress));
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect));
|
||||
|
||||
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;
|
||||
|
||||
/* configure LSS CAN Master message reception */
|
||||
CO_CANrxBufferInit(
|
||||
CANdevRx, /* CAN device */
|
||||
CANdevRxIdx, /* rx buffer index */
|
||||
CANidLssMaster, /* CAN identifier */
|
||||
0x7FF, /* mask */
|
||||
0, /* rtr */
|
||||
(void*)LSSslave, /* object passed to receive function */
|
||||
CO_LSSslave_receive); /* this function will process received message */
|
||||
|
||||
/* configure LSS CAN Slave response message transmission */
|
||||
LSSslave->CANdevTx = CANdevTx;
|
||||
LSSslave->TXbuff = CO_CANtxBufferInit(
|
||||
CANdevTx, /* CAN device */
|
||||
CANdevTxIdx, /* index of specific buffer inside CAN module */
|
||||
CANidLssSlave, /* CAN identifier */
|
||||
0, /* rtr */
|
||||
8, /* number of data bytes */
|
||||
0); /* synchronous message flag bit */
|
||||
|
||||
return CO_ERROR_NO;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_initCheckBitRateCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate))
|
||||
{
|
||||
if(LSSslave != NULL){
|
||||
LSSslave->functLSScheckBitRateObject = object;
|
||||
LSSslave->pFunctLSScheckBitRate = pFunctLSScheckBitRate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_initActivateBitRateCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay))
|
||||
{
|
||||
if(LSSslave != NULL){
|
||||
LSSslave->functLSSactivateBitRateObject = object;
|
||||
LSSslave->pFunctLSSactivateBitRate = pFunctLSSactivateBitRate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_initCfgStoreCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate))
|
||||
{
|
||||
if(LSSslave != NULL){
|
||||
LSSslave->functLSScfgStore = object;
|
||||
LSSslave->pFunctLSScfgStore = pFunctLSScfgStore;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
void CO_LSSslave_process(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
uint16_t activeBitRate,
|
||||
uint8_t activeNodeId,
|
||||
uint16_t *pendingBitRate,
|
||||
uint8_t *pendingNodeId)
|
||||
{
|
||||
LSSslave->activeNodeID = activeNodeId;
|
||||
*pendingBitRate = LSSslave->pendingBitRate;
|
||||
*pendingNodeId = LSSslave->pendingNodeID;
|
||||
}
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
/**
|
||||
* CANopen LSS Master/Slave protocol.
|
||||
*
|
||||
* @file CO_LSSslave.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 Neuberger Gebäudeautomation GmbH
|
||||
*
|
||||
* This file is part of CANopenNode, an opensource CANopen Stack.
|
||||
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
|
||||
* For more information on CANopen see <http://www.can-cia.org/>.
|
||||
*
|
||||
* CANopenNode is free and open source software: you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Following clarification and special exception to the GNU General Public
|
||||
* License is included to the distribution terms of CANopenNode:
|
||||
*
|
||||
* Linking this library statically or dynamically with other modules is
|
||||
* making a combined work based on this library. Thus, the terms and
|
||||
* conditions of the GNU General Public License cover the whole combination.
|
||||
*
|
||||
* As a special exception, the copyright holders of this library give
|
||||
* you permission to link this library with independent modules to
|
||||
* produce an executable, regardless of the license terms of these
|
||||
* independent modules, and to copy and distribute the resulting
|
||||
* executable under terms of your choice, provided that you also meet,
|
||||
* for each linked independent module, the terms and conditions of the
|
||||
* license of that module. An independent module is a module which is
|
||||
* not derived from or based on this library. If you modify this
|
||||
* library, you may extend this exception to your version of the
|
||||
* library, but you are not obliged to do so. If you do not wish
|
||||
* to do so, delete this exception statement from your version.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CO_LSSslave_H
|
||||
#define CO_LSSslave_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CO_NO_LSS_SERVER == 1
|
||||
|
||||
#include "CO_LSS.h"
|
||||
|
||||
/**
|
||||
* @addtogroup CO_LSS
|
||||
* @defgroup CO_LSSslave LSS Slave
|
||||
* @ingroup CO_LSS
|
||||
* @{
|
||||
*
|
||||
* CANopen Layer Setting Service - server protocol
|
||||
*
|
||||
* The server/slave provides the following services
|
||||
* - node selection via LSS address
|
||||
* - node selection via LSS fastscan
|
||||
* - Inquire LSS address of currently selected node
|
||||
* - Inquire node ID
|
||||
* - Configure bit timing
|
||||
* - Configure node ID
|
||||
* - Activate bit timing parameters
|
||||
* - Store configuration (bit rate and node ID)
|
||||
*
|
||||
* After CAN module start, the LSS server and NMT server 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().
|
||||
* Moreover, the LSS server needs to pause the NMT server initialization in case
|
||||
* no valid node ID is available at start up.
|
||||
*
|
||||
* ###Example
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The following code is only a suggestion on how to use the LSS server. 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) {
|
||||
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
|
||||
*/
|
||||
|
||||
/**
|
||||
* LSS slave object.
|
||||
*/
|
||||
typedef struct{
|
||||
CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */
|
||||
CO_LSS_state_t lssState; /**< #CO_LSS_state_t */
|
||||
CO_LSS_address_t lssSelect; /**< Received LSS Address */
|
||||
|
||||
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 */
|
||||
uint8_t activeNodeID; /**< Node ID used at the CAN interface */
|
||||
|
||||
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 */
|
||||
|
||||
CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */
|
||||
CO_CANtx_t *TXbuff; /**< CAN transmit buffer */
|
||||
}CO_LSSslave_t;
|
||||
|
||||
/**
|
||||
* Initialize LSS object.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 CANdevRx CAN device for LSS slave reception.
|
||||
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
|
||||
* @param CANidLssMaster COB ID for reception.
|
||||
* @param CANdevTx CAN device for LSS slave transmission.
|
||||
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
|
||||
* @param CANidLssSlave COB ID for transmission.
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSslave_init(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
CO_LSS_address_t lssAddress,
|
||||
uint16_t pendingBitRate,
|
||||
uint8_t pendingNodeID,
|
||||
CO_CANmodule_t *CANdevRx,
|
||||
uint16_t CANdevRxIdx,
|
||||
uint32_t CANidLssMaster,
|
||||
CO_CANmodule_t *CANdevTx,
|
||||
uint16_t CANdevTxIdx,
|
||||
uint32_t CANidLssSlave);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param activeBitRate Currently active bit rate
|
||||
* @param activeNodeId Currently active node ID
|
||||
* @param pendingBitRate [out] Requested bit rate
|
||||
* @param pendingNodeId [out] Requested node id
|
||||
*/
|
||||
void CO_LSSslave_process(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
uint16_t activeBitRate,
|
||||
uint8_t activeNodeId,
|
||||
uint16_t *pendingBitRate,
|
||||
uint8_t *pendingNodeId);
|
||||
|
||||
/**
|
||||
* Initialize verify bit rate callback
|
||||
*
|
||||
* Function initializes callback function, which is called when "config bit
|
||||
* timing parameters" is used. The callback function needs to check if the new bit
|
||||
* rate is supported by the CANopen device. Callback returns "true" if supported.
|
||||
* When no callback is set the LSS server 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.
|
||||
*/
|
||||
void CO_LSSslave_initCheckBitRateCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate));
|
||||
|
||||
/**
|
||||
* Initialize activate bit rate callback
|
||||
*
|
||||
* Function initializes callback function, which is called when "activate bit
|
||||
* timing parameters" is used. The callback function gives the user an event to
|
||||
* 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
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL
|
||||
* @param pFunctLSSactivateBitRate Pointer to the callback function. Not called if NULL.
|
||||
*/
|
||||
void CO_LSSslave_initActivateBitRateCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay));
|
||||
|
||||
/**
|
||||
* Store configuration callback
|
||||
*
|
||||
* Function initializes callback function, which is called when "store configuration" is used.
|
||||
* The callback function gives the user an event to store the corresponding node id and bit rate
|
||||
* to NVM. Those values have to be supplied to the init function as "persistent values"
|
||||
* after reset. If callback returns "true", success is send to the LSS master. When no
|
||||
* callback is set the LSS server 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.
|
||||
*/
|
||||
void CO_LSSslave_initCfgStoreCallback(
|
||||
CO_LSSslave_t *LSSslave,
|
||||
void *object,
|
||||
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate));
|
||||
|
||||
#else /* CO_NO_LSS_SERVER == 1 */
|
||||
|
||||
/**
|
||||
* @addtogroup CO_LSS
|
||||
* @{
|
||||
* If you need documetation for LSS slave usage, add "CO_NO_LSS_SERVER=1" to doxygen
|
||||
* "PREDEFINED" variable.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#endif /* CO_NO_LSS_SERVER == 1 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
/** @} */
|
||||
#endif
|
|
@ -72,6 +72,13 @@ void CO_memcpy(uint8_t dest[], const uint8_t src[], const uint16_t size){
|
|||
}
|
||||
}
|
||||
|
||||
void CO_memset(uint8_t dest[], uint8_t c, const uint16_t size){
|
||||
uint16_t i;
|
||||
for(i = 0; i < size; i++){
|
||||
dest[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CO_getUint16(const uint8_t data[]){
|
||||
CO_bytes_t b;
|
||||
b.u8[0] = data[0];
|
||||
|
|
|
@ -677,6 +677,17 @@ typedef union{
|
|||
*/
|
||||
void CO_memcpy(uint8_t dest[], const uint8_t src[], const uint16_t size);
|
||||
|
||||
/**
|
||||
* Helper function like memset.
|
||||
*
|
||||
* Function fills destination with char "c".
|
||||
*
|
||||
* @param dest Destination location.
|
||||
* @param c set value.
|
||||
* @param size Number of data bytes to be copied (max 0xFFFF).
|
||||
*/
|
||||
void CO_memset(uint8_t dest[], uint8_t c, const uint16_t size);
|
||||
|
||||
|
||||
/**
|
||||
* Helper function returns uint16 from byte array.
|
||||
|
|
|
@ -97,6 +97,8 @@ CO_ReturnError_t CO_CANmodule_init(
|
|||
|
||||
for(i=0U; i<rxSize; i++){
|
||||
rxArray[i].ident = 0U;
|
||||
rxArray[i].mask = 0xFFFFFFFFU;
|
||||
rxArray[i].object = NULL;
|
||||
rxArray[i].pFunct = NULL;
|
||||
}
|
||||
for(i=0U; i<txSize; i++){
|
||||
|
|
Loading…
Reference in New Issue