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:
Martin Wagner 2017-06-08 16:06:38 +02:00
parent efc6876c1b
commit 03982ad8e3
11 changed files with 1475 additions and 30 deletions

166
CANopen.c
View File

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

View File

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

View File

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

View File

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

241
stack/CO_LSS.h Normal file
View File

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

150
stack/CO_LSSmaster.h Normal file
View File

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

438
stack/CO_LSSslave.c Normal file
View File

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

415
stack/CO_LSSslave.h Normal file
View File

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

View File

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

View File

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

View File

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