第一版代码,为了在EEPROM保存参数的时候走STM32的CRC,让Codex修改了一下,现在的效果是无法存储,codex表示原因是CRC方法不同,修改到一半今天的额度使用完了,有待后续解决CRC的bug
This commit is contained in:
223
Middleware/CANopenNode/305/CO_LSS.h
Normal file
223
Middleware/CANopenNode/305/CO_LSS.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* CANopen Layer Setting Services protocol (common).
|
||||
*
|
||||
* @file CO_LSS.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CO_LSS_H
|
||||
#define CO_LSS_H
|
||||
|
||||
#include "301/CO_driver.h"
|
||||
|
||||
/* default configuration, see CO_config.h */
|
||||
#ifndef CO_CONFIG_LSS
|
||||
#define CO_CONFIG_LSS (CO_CONFIG_LSS_SLAVE | CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE)
|
||||
#endif
|
||||
|
||||
#if (((CO_CONFIG_LSS) & (CO_CONFIG_LSS_SLAVE | CO_CONFIG_LSS_MASTER)) != 0) || defined CO_DOXYGEN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS LSS
|
||||
* CANopen Layer Setting Services protocol (common).
|
||||
*
|
||||
* @ingroup CO_CANopen_305
|
||||
* @{
|
||||
* 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!
|
||||
*
|
||||
* Using this implementation, only master or slave can be included in one node at a time.
|
||||
*
|
||||
* For CAN identifiers see @ref CO_Default_CAN_ID_t
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_command_specifiers CO_LSS 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.
|
||||
*/
|
||||
#define CO_LSS_SWITCH_STATE_GLOBAL 0x04U /**< Switch state global protocol */
|
||||
#define CO_LSS_SWITCH_STATE_SEL_VENDOR 0x40U /**< Switch state selective protocol - Vendor ID */
|
||||
#define CO_LSS_SWITCH_STATE_SEL_PRODUCT 0x41U /**< Switch state selective protocol - Product code */
|
||||
#define CO_LSS_SWITCH_STATE_SEL_REV 0x42U /**< Switch state selective protocol - Revision number */
|
||||
#define CO_LSS_SWITCH_STATE_SEL_SERIAL 0x43U /**< Switch state selective protocol - Serial number */
|
||||
#define CO_LSS_SWITCH_STATE_SEL 0x44U /**< Switch state selective protocol - Slave response */
|
||||
#define CO_LSS_CFG_NODE_ID 0x11U /**< Configure node ID protocol */
|
||||
#define CO_LSS_CFG_BIT_TIMING 0x13U /**< Configure bit timing parameter protocol */
|
||||
#define CO_LSS_CFG_ACTIVATE_BIT_TIMING 0x15U /**< Activate bit timing parameter protocol */
|
||||
#define CO_LSS_CFG_STORE 0x17U /**< Store configuration protocol */
|
||||
#define CO_LSS_IDENT_SLAVE 0x4FU /**< LSS Fastscan response */
|
||||
#define CO_LSS_IDENT_FASTSCAN 0x51U /**< LSS Fastscan protocol */
|
||||
#define CO_LSS_INQUIRE_VENDOR 0x5AU /**< Inquire identity vendor-ID protocol */
|
||||
#define CO_LSS_INQUIRE_PRODUCT 0x5BU /**< Inquire identity product-code protocol */
|
||||
#define CO_LSS_INQUIRE_REV 0x5CU /**< Inquire identity revision-number protocol */
|
||||
#define CO_LSS_INQUIRE_SERIAL 0x5DU /**< Inquire identity serial-number protocol */
|
||||
#define CO_LSS_INQUIRE_NODE_ID 0x5EU /**< Inquire node-ID protocol */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_CFG_NODE_ID_status CO_LSS_CFG_NODE_ID status
|
||||
* @{
|
||||
* Error codes for Configure node ID protocol
|
||||
*/
|
||||
#define CO_LSS_CFG_NODE_ID_OK 0x00U /**< Protocol successfully completed */
|
||||
#define CO_LSS_CFG_NODE_ID_OUT_OF_RANGE 0x01U /**< NID out of range */
|
||||
#define CO_LSS_CFG_NODE_ID_MANUFACTURER 0xFFU /**< Manufacturer specific error. No further support */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_CFG_BIT_TIMING_status CO_LSS_CFG_BIT_TIMING status
|
||||
* @{
|
||||
* Error codes for Configure bit timing parameters protocol
|
||||
*/
|
||||
#define CO_LSS_CFG_BIT_TIMING_OK 0x00U /**< Protocol successfully completed */
|
||||
#define CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE 0x01U /**< Bit timing / Bit rate not supported */
|
||||
#define CO_LSS_CFG_BIT_TIMING_MANUFACTURER 0xFFU /**< Manufacturer specific error. No further support */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_CFG_STORE_status CO_LSS_CFG_STORE status
|
||||
* @{
|
||||
* Error codes for Store configuration protocol
|
||||
*/
|
||||
#define CO_LSS_CFG_STORE_OK 0x00U /**< Protocol successfully completed */
|
||||
#define CO_LSS_CFG_STORE_NOT_SUPPORTED 0x01U /**< Store configuration not supported */
|
||||
#define CO_LSS_CFG_STORE_FAILED 0x02U /**< Storage media access error */
|
||||
#define CO_LSS_CFG_STORE_MANUFACTURER 0xFFU /**< Manufacturer specific error. No further support */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_FASTSCAN_bitcheck CO_LSS_FASTSCAN bitcheck
|
||||
* @{
|
||||
* Fastscan BitCheck. BIT0 means all bits are checked for equality by slave
|
||||
*/
|
||||
#define CO_LSS_FASTSCAN_BIT0 0x00U /**< Least significant bit of IDnumbners bit area to be checked */
|
||||
/* ... */
|
||||
#define CO_LSS_FASTSCAN_BIT31 0x1FU /**< dito */
|
||||
#define CO_LSS_FASTSCAN_CONFIRM 0x80U /**< All LSS slaves waiting for scan respond and previous scan is reset */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_FASTSCAN_lssSub_lssNext CO_LSS_FASTSCAN lssSub lssNext
|
||||
* @{
|
||||
*/
|
||||
#define CO_LSS_FASTSCAN_VENDOR_ID 0x00U /**< Vendor ID */
|
||||
#define CO_LSS_FASTSCAN_PRODUCT 0x01U /**< Product code */
|
||||
#define CO_LSS_FASTSCAN_REV 0x02U /**< Revision number */
|
||||
#define CO_LSS_FASTSCAN_SERIAL 0x03U /**< Serial number */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* The LSS address is a 128 bit number, uniquely identifying each node. It consists of the values in object 0x1018.
|
||||
*/
|
||||
typedef union {
|
||||
uint32_t addr[4];
|
||||
|
||||
struct {
|
||||
uint32_t vendorID;
|
||||
uint32_t productCode;
|
||||
uint32_t revisionNumber;
|
||||
uint32_t serialNumber;
|
||||
} identity;
|
||||
} CO_LSS_address_t;
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_STATE_state CO_LSS_STATE state
|
||||
* @{
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define CO_LSS_STATE_WAITING 0x00U /**< LSS FSA waiting for requests */
|
||||
#define CO_LSS_STATE_CONFIGURATION 0x01U /**< LSS FSA waiting for configuration */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSS_BIT_TIMING_table CO_LSS_BIT_TIMING table
|
||||
* @{
|
||||
* Definition of table_index for /CiA301/ bit timing table
|
||||
*/
|
||||
#define CO_LSS_BIT_TIMING_1000 0U /**< 1000kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_800 1U /**< 800kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_500 2U /**< 500kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_250 3U /**< 250kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_125 4U /**< 125kbit/s */
|
||||
/* 5U - reserved */
|
||||
#define CO_LSS_BIT_TIMING_50 6U /**< 50kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_20 7U /**< 20kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_10 8U /**< 10kbit/s */
|
||||
#define CO_LSS_BIT_TIMING_AUTO 9U /**< Automatic bit rate detection */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* 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};
|
||||
|
||||
/**
|
||||
* 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 >= 1U) && (nid <= 0x7FU)) || (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.identity.productCode == a2.identity.productCode) \
|
||||
&& (a1.identity.revisionNumber == a2.identity.revisionNumber) \
|
||||
&& (a1.identity.serialNumber == a2.identity.serialNumber) && (a1.identity.vendorID == a2.identity.vendorID))
|
||||
|
||||
/** @} */ /* @defgroup CO_LSS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* (CO_CONFIG_LSS) & (CO_CONFIG_LSS_SLAVE | CO_CONFIG_LSS_MASTER) */
|
||||
|
||||
#endif /* CO_LSS_H */
|
||||
965
Middleware/CANopenNode/305/CO_LSSmaster.c
Normal file
965
Middleware/CANopenNode/305/CO_LSSmaster.c
Normal file
@@ -0,0 +1,965 @@
|
||||
/*
|
||||
* CANopen LSS Master protocol.
|
||||
*
|
||||
* @file CO_LSSmaster.c
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "305/CO_LSSmaster.h"
|
||||
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0
|
||||
|
||||
/*
|
||||
* @defgroup CO_LSSmaster_state_t
|
||||
* @{
|
||||
* LSS master slave select state machine. Compared to @ref CO_LSS_STATE_state this has information if we
|
||||
* currently have selected one or all slaves. This allows for some basic error checking.
|
||||
*/
|
||||
#define CO_LSSmaster_STATE_WAITING 0x00U
|
||||
#define CO_LSSmaster_STATE_CFG_SLECTIVE 0x01U
|
||||
#define CO_LSSmaster_STATE_CFG_GLOBAL 0x02U
|
||||
/* @} */ /* CO_LSSmaster_state_t */
|
||||
|
||||
/*
|
||||
* @defgroup CO_LSSmaster_command_t LSS master slave command state machine
|
||||
* @{
|
||||
*/
|
||||
#define CO_LSSmaster_COMMAND_WAITING 0x00U
|
||||
#define CO_LSSmaster_COMMAND_SWITCH_STATE 0x01U
|
||||
#define CO_LSSmaster_COMMAND_CFG_BIT_TIMING 0x02U
|
||||
#define CO_LSSmaster_COMMAND_CFG_NODE_ID 0x03U
|
||||
#define CO_LSSmaster_COMMAND_CFG_STORE 0x04U
|
||||
#define CO_LSSmaster_COMMAND_INQUIRE_VENDOR 0x05U
|
||||
#define CO_LSSmaster_COMMAND_INQUIRE_PRODUCT 0x06U
|
||||
#define CO_LSSmaster_COMMAND_INQUIRE_REV 0x07U
|
||||
#define CO_LSSmaster_COMMAND_INQUIRE_SERIAL 0x08U
|
||||
#define CO_LSSmaster_COMMAND_INQUIRE 0x09U
|
||||
#define CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN 0x0AU
|
||||
/* @} */ /* CO_LSSmaster_command_t */
|
||||
|
||||
/*
|
||||
* @defgroup CO_LSSmaster_fs_t LSS master fastscan state machine
|
||||
* @{
|
||||
*/
|
||||
#define CO_LSSmaster_FS_STATE_CHECK 0x00U
|
||||
#define CO_LSSmaster_FS_STATE_SCAN 0x01U
|
||||
#define CO_LSSmaster_FS_STATE_VERIFY 0x02U
|
||||
|
||||
/* @} */ /* CO_LSSmaster_fs_t */
|
||||
|
||||
/*
|
||||
* 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_LSSmaster_receive(void* object, void* msg) {
|
||||
CO_LSSmaster_t* LSSmaster;
|
||||
uint8_t DLC = CO_CANrxMsg_readDLC(msg);
|
||||
const uint8_t* data = CO_CANrxMsg_readData(msg);
|
||||
|
||||
LSSmaster = (CO_LSSmaster_t*)object; /* this is the correct pointer type of the first argument */
|
||||
|
||||
/* verify message length and message overflow (previous message was not processed yet). */
|
||||
if ((DLC == 8U) && !CO_FLAG_READ(LSSmaster->CANrxNew) && (LSSmaster->command != CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
/* copy data and set 'new message' flag */
|
||||
(void)memcpy(LSSmaster->CANrxData, data, sizeof(LSSmaster->CANrxData));
|
||||
|
||||
CO_FLAG_SET(LSSmaster->CANrxNew);
|
||||
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
|
||||
/* Optional signal to RTOS, which can resume task, which handles further processing. */
|
||||
if (LSSmaster->pFunctSignal != NULL) {
|
||||
LSSmaster->pFunctSignal(LSSmaster->functSignalObject);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check LSS timeout.
|
||||
*
|
||||
* Generally, we do not really care if the message has been received before or after the timeout
|
||||
* expired. Only if no message has been received we have to check for timeouts.
|
||||
*/
|
||||
static inline CO_LSSmaster_return_t
|
||||
CO_LSSmaster_check_timeout(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
|
||||
LSSmaster->timeoutTimer += timeDifference_us;
|
||||
if (LSSmaster->timeoutTimer >= LSSmaster->timeout_us) {
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
ret = CO_LSSmaster_TIMEOUT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_ReturnError_t
|
||||
CO_LSSmaster_init(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx,
|
||||
uint16_t CANidLssSlave, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint16_t CANidLssMaster) {
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
|
||||
/* verify arguments */
|
||||
if ((LSSmaster == NULL) || (CANdevRx == NULL) || (CANdevTx == NULL)) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
LSSmaster->timeout_us = (uint32_t)timeout_ms * 1000U;
|
||||
LSSmaster->state = CO_LSSmaster_STATE_WAITING;
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
(void)memset(LSSmaster->CANrxData, 0, sizeof(LSSmaster->CANrxData));
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
|
||||
LSSmaster->pFunctSignal = NULL;
|
||||
LSSmaster->functSignalObject = NULL;
|
||||
#endif
|
||||
|
||||
/* configure LSS CAN Slave response message reception */
|
||||
ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CANidLssSlave, 0x7FF, false, (void*)LSSmaster,
|
||||
CO_LSSmaster_receive);
|
||||
|
||||
/* configure LSS CAN Master message transmission */
|
||||
LSSmaster->CANdevTx = CANdevTx;
|
||||
LSSmaster->TXbuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CANidLssMaster, false, 8, false);
|
||||
|
||||
if (LSSmaster->TXbuff == NULL) {
|
||||
ret = CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
CO_LSSmaster_changeTimeout(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms) {
|
||||
if (LSSmaster != NULL) {
|
||||
LSSmaster->timeout_us = (uint32_t)timeout_ms * 1000U;
|
||||
}
|
||||
}
|
||||
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
|
||||
void
|
||||
CO_LSSmaster_initCallbackPre(CO_LSSmaster_t* LSSmaster, void* object, void (*pFunctSignal)(void* object)) {
|
||||
if (LSSmaster != NULL) {
|
||||
LSSmaster->functSignalObject = object;
|
||||
LSSmaster->pFunctSignal = pFunctSignal;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper function - initiate switch state
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_switchStateSelectInitiate(CO_LSSmaster_t* LSSmaster, CO_LSS_address_t* lssAddress) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
if (lssAddress != NULL) {
|
||||
/* switch state select specific using LSS address */
|
||||
LSSmaster->state = CO_LSSmaster_STATE_CFG_SLECTIVE;
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_SWITCH_STATE;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
(void)memset(&LSSmaster->TXbuff->data[6], 0, sizeof(LSSmaster->TXbuff->data) - 6U);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_VENDOR;
|
||||
(void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.vendorID));
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_PRODUCT;
|
||||
(void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.productCode));
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_REV;
|
||||
(void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.revisionNumber));
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL_SERIAL;
|
||||
(void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(lssAddress->identity.serialNumber));
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
} else {
|
||||
/* switch state global */
|
||||
LSSmaster->state = CO_LSSmaster_STATE_CFG_GLOBAL;
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_GLOBAL;
|
||||
LSSmaster->TXbuff->data[1] = CO_LSS_STATE_CONFIGURATION;
|
||||
(void)memset(&LSSmaster->TXbuff->data[2], 0, sizeof(LSSmaster->TXbuff->data) - 2U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
/* This is non-confirmed service! */
|
||||
ret = CO_LSSmaster_OK;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - wait for confirmation
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_switchStateSelectWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
if (CO_FLAG_READ(LSSmaster->CANrxNew)) {
|
||||
uint8_t cs = LSSmaster->CANrxData[0];
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
|
||||
if (cs == CO_LSS_SWITCH_STATE_SEL) {
|
||||
/* confirmation received */
|
||||
ret = CO_LSSmaster_OK;
|
||||
} else {
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
}
|
||||
} else {
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_swStateSelect(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSS_address_t* lssAddress) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if (LSSmaster == NULL) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* Initiate select */
|
||||
if ((LSSmaster->state == CO_LSSmaster_STATE_WAITING) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
ret = CO_LSSmaster_switchStateSelectInitiate(LSSmaster, lssAddress);
|
||||
}
|
||||
/* Wait for confirmation */
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_SWITCH_STATE) {
|
||||
ret = CO_LSSmaster_switchStateSelectWait(LSSmaster, timeDifference_us);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
|
||||
if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
if (ret < CO_LSSmaster_OK) {
|
||||
/* switching failed, go back to waiting */
|
||||
LSSmaster->state = CO_LSSmaster_STATE_WAITING;
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_swStateDeselect(CO_LSSmaster_t* LSSmaster) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if (LSSmaster == NULL) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* We can always send this command to get into a clean state on the network.
|
||||
* If no slave is selected, this command is ignored. */
|
||||
LSSmaster->state = CO_LSSmaster_STATE_WAITING;
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
/* switch state global */
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_SWITCH_STATE_GLOBAL;
|
||||
LSSmaster->TXbuff->data[1] = CO_LSS_STATE_WAITING;
|
||||
(void)memset(&LSSmaster->TXbuff->data[2], 0, sizeof(LSSmaster->TXbuff->data) - 2U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
/* This is non-confirmed service! */
|
||||
ret = CO_LSSmaster_OK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - wait for confirmation, check for returned error code
|
||||
*
|
||||
* This uses the nature of the configure confirmation message design:
|
||||
* - byte 0 -> cs
|
||||
* - byte 1 -> Error Code, where
|
||||
* - 0 = OK
|
||||
* - 1 .. FE = Values defined by CiA. All currently defined values are slave rejects.
|
||||
* No further distinction on why the slave did reject the request.
|
||||
* - FF = Manufacturer Error Code in byte 2
|
||||
* - byte 2 -> Manufacturer Error, currently not used
|
||||
*
|
||||
* enums for the errorCode are
|
||||
* - CO_LSS_CFG_NODE_ID_status
|
||||
* - CO_LSS_CFG_BIT_TIMING
|
||||
* - CO_LSS_CFG_STORE_status
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_configureCheckWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t csWait) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
if (CO_FLAG_READ(LSSmaster->CANrxNew)) {
|
||||
uint8_t cs = LSSmaster->CANrxData[0];
|
||||
uint8_t errorCode = LSSmaster->CANrxData[1];
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
|
||||
if (cs == csWait) {
|
||||
if (errorCode == 0U) {
|
||||
ret = CO_LSSmaster_OK;
|
||||
} else if (errorCode == 0xFFU) {
|
||||
ret = CO_LSSmaster_OK_MANUFACTURER;
|
||||
} else {
|
||||
ret = CO_LSSmaster_OK_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
} else {
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
}
|
||||
} else {
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
}
|
||||
|
||||
if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_configureBitTiming(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint16_t bit) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
uint8_t bitTiming;
|
||||
|
||||
if (LSSmaster == NULL) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
switch (bit) {
|
||||
case 1000: bitTiming = CO_LSS_BIT_TIMING_1000; break;
|
||||
case 800: bitTiming = CO_LSS_BIT_TIMING_800; break;
|
||||
case 500: bitTiming = CO_LSS_BIT_TIMING_500; break;
|
||||
case 250: bitTiming = CO_LSS_BIT_TIMING_250; break;
|
||||
case 125: bitTiming = CO_LSS_BIT_TIMING_125; break;
|
||||
case 50: bitTiming = CO_LSS_BIT_TIMING_50; break;
|
||||
case 20: bitTiming = CO_LSS_BIT_TIMING_20; break;
|
||||
case 10: bitTiming = CO_LSS_BIT_TIMING_10; break;
|
||||
case 0: bitTiming = CO_LSS_BIT_TIMING_AUTO; break;
|
||||
default: return CO_LSSmaster_ILLEGAL_ARGUMENT; break;
|
||||
}
|
||||
|
||||
/* Initiate config bit */
|
||||
if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_CFG_BIT_TIMING;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_CFG_BIT_TIMING;
|
||||
LSSmaster->TXbuff->data[1] = 0;
|
||||
LSSmaster->TXbuff->data[2] = bitTiming;
|
||||
(void)memset(&LSSmaster->TXbuff->data[3], 0, sizeof(LSSmaster->TXbuff->data) - 3U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
/* Wait for confirmation */
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_BIT_TIMING) {
|
||||
|
||||
ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_us, CO_LSS_CFG_BIT_TIMING);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
|
||||
if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_configureNodeId(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t nodeId) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if ((LSSmaster == NULL) || !CO_LSS_NODE_ID_VALID(nodeId)) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* Initiate config node ID */
|
||||
if (((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) ||
|
||||
/* Let un-config node ID also be run in global mode for unconfiguring all nodes */
|
||||
((LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL) && (nodeId == CO_LSS_NODE_ID_ASSIGNMENT)))
|
||||
&& (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_CFG_NODE_ID;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_CFG_NODE_ID;
|
||||
LSSmaster->TXbuff->data[1] = nodeId;
|
||||
(void)memset(&LSSmaster->TXbuff->data[2], 0, sizeof(LSSmaster->TXbuff->data) - 2U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
/* Wait for confirmation */
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_NODE_ID) {
|
||||
|
||||
ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_us, CO_LSS_CFG_NODE_ID);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
|
||||
if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_configureStore(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if (LSSmaster == NULL) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* Initiate config store */
|
||||
if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_CFG_STORE;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_CFG_STORE;
|
||||
(void)memset(&LSSmaster->TXbuff->data[1], 0, sizeof(LSSmaster->TXbuff->data) - 1U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
/* Wait for confirmation */
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_CFG_STORE) {
|
||||
|
||||
ret = CO_LSSmaster_configureCheckWait(LSSmaster, timeDifference_us, CO_LSS_CFG_STORE);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
|
||||
if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_ActivateBit(CO_LSSmaster_t* LSSmaster, uint16_t switchDelay_ms) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if (LSSmaster == NULL) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* for activating bit timing, we need to have all slaves set to config
|
||||
* state. This check makes it a bit harder to shoot ourselves in the foot */
|
||||
if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL) && (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_CFG_ACTIVATE_BIT_TIMING;
|
||||
(void)CO_setUint16(&LSSmaster->TXbuff->data[1], CO_SWAP_16(switchDelay_ms));
|
||||
(void)memset(&LSSmaster->TXbuff->data[3], 0, sizeof(LSSmaster->TXbuff->data) - 3U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
/* This is non-confirmed service! */
|
||||
ret = CO_LSSmaster_OK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - send request
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_inquireInitiate(CO_LSSmaster_t* LSSmaster, uint8_t cs) {
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = cs;
|
||||
(void)memset(&LSSmaster->TXbuff->data[1], 0, sizeof(LSSmaster->TXbuff->data) - 1U);
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
|
||||
return CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - wait for confirmation
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_inquireCheckWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t csWait, uint32_t* value) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
if (CO_FLAG_READ(LSSmaster->CANrxNew)) {
|
||||
uint8_t cs = LSSmaster->CANrxData[0];
|
||||
*value = CO_getUint32(&LSSmaster->CANrxData[1]);
|
||||
*value = CO_SWAP_32(*value);
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
|
||||
if (cs == csWait) {
|
||||
ret = CO_LSSmaster_OK;
|
||||
} else {
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
}
|
||||
} else {
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_InquireLssAddress(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSS_address_t* lssAddress) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
uint8_t next = CO_LSSmaster_COMMAND_WAITING;
|
||||
|
||||
if ((LSSmaster == NULL) || (lssAddress == NULL)) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* Check for reply */
|
||||
if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_VENDOR) {
|
||||
|
||||
ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_VENDOR,
|
||||
&lssAddress->identity.vendorID);
|
||||
if (ret == CO_LSSmaster_OK) {
|
||||
/* Start next request */
|
||||
next = CO_LSSmaster_COMMAND_INQUIRE_PRODUCT;
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
} else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_PRODUCT) {
|
||||
|
||||
ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_PRODUCT,
|
||||
&lssAddress->identity.productCode);
|
||||
if (ret == CO_LSSmaster_OK) {
|
||||
/* Start next request */
|
||||
next = CO_LSSmaster_COMMAND_INQUIRE_REV;
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
} else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_REV) {
|
||||
|
||||
ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_REV,
|
||||
&lssAddress->identity.revisionNumber);
|
||||
if (ret == CO_LSSmaster_OK) {
|
||||
/* Start next request */
|
||||
next = CO_LSSmaster_COMMAND_INQUIRE_SERIAL;
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
} else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE_SERIAL) {
|
||||
|
||||
ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, CO_LSS_INQUIRE_SERIAL,
|
||||
&lssAddress->identity.serialNumber);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
|
||||
/* Check for next request */
|
||||
if ((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) || (LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL)) {
|
||||
if (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) {
|
||||
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_VENDOR;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_VENDOR);
|
||||
} else if (next == CO_LSSmaster_COMMAND_INQUIRE_PRODUCT) {
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_PRODUCT;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_PRODUCT);
|
||||
} else if (next == CO_LSSmaster_COMMAND_INQUIRE_REV) {
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_REV;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_REV);
|
||||
} else if (next == CO_LSSmaster_COMMAND_INQUIRE_SERIAL) {
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE_SERIAL;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, CO_LSS_INQUIRE_SERIAL);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret != CO_LSSmaster_INVALID_STATE) && (ret != CO_LSSmaster_WAIT_SLAVE)) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_Inquire(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t lssInquireCs, uint32_t* value) {
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
|
||||
if ((LSSmaster == NULL) || (value == NULL)) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* send request */
|
||||
if (((LSSmaster->state == CO_LSSmaster_STATE_CFG_SLECTIVE) || (LSSmaster->state == CO_LSSmaster_STATE_CFG_GLOBAL))
|
||||
&& (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING)) {
|
||||
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_INQUIRE;
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
ret = CO_LSSmaster_inquireInitiate(LSSmaster, lssInquireCs);
|
||||
}
|
||||
/* Check for reply */
|
||||
else if (LSSmaster->command == CO_LSSmaster_COMMAND_INQUIRE) {
|
||||
ret = CO_LSSmaster_inquireCheckWait(LSSmaster, timeDifference_us, lssInquireCs, value);
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
|
||||
if (ret != CO_LSSmaster_WAIT_SLAVE) {
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - send request
|
||||
*/
|
||||
static void
|
||||
CO_LSSmaster_FsSendMsg(CO_LSSmaster_t* LSSmaster, uint32_t idNumber, uint8_t bitCheck, uint8_t lssSub,
|
||||
uint8_t lssNext) {
|
||||
LSSmaster->timeoutTimer = 0;
|
||||
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
LSSmaster->TXbuff->data[0] = CO_LSS_IDENT_FASTSCAN;
|
||||
(void)CO_setUint32(&LSSmaster->TXbuff->data[1], CO_SWAP_32(idNumber));
|
||||
LSSmaster->TXbuff->data[5] = bitCheck;
|
||||
LSSmaster->TXbuff->data[6] = lssSub;
|
||||
LSSmaster->TXbuff->data[7] = lssNext;
|
||||
|
||||
(void)CO_CANsend(LSSmaster->CANdevTx, LSSmaster->TXbuff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - wait for confirmation
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_FsCheckWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
if (ret == CO_LSSmaster_TIMEOUT) {
|
||||
ret = CO_LSSmaster_SCAN_NOACK;
|
||||
|
||||
if (CO_FLAG_READ(LSSmaster->CANrxNew)) {
|
||||
uint8_t cs = LSSmaster->CANrxData[0];
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
|
||||
if (cs == CO_LSS_IDENT_SLAVE) {
|
||||
/* At least one node is waiting for fastscan */
|
||||
ret = CO_LSSmaster_SCAN_FINISHED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - initiate scan for 32 bit part of LSS address
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_FsScanInitiate(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan,
|
||||
uint8_t lssSub) {
|
||||
(void)timeDifference_us; /* unused */
|
||||
|
||||
LSSmaster->fsLssSub = lssSub;
|
||||
LSSmaster->fsIdNumber = 0;
|
||||
|
||||
switch (scan) {
|
||||
case CO_LSSmaster_FS_SCAN: break;
|
||||
case CO_LSSmaster_FS_MATCH:
|
||||
/* No scanning requested */
|
||||
return CO_LSSmaster_SCAN_FINISHED;
|
||||
break;
|
||||
case CO_LSSmaster_FS_SKIP:
|
||||
default: return CO_LSSmaster_SCAN_FAILED; break;
|
||||
}
|
||||
|
||||
LSSmaster->fsBitChecked = CO_LSS_FASTSCAN_BIT31;
|
||||
|
||||
/* trigger scan procedure by sending first message */
|
||||
CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, LSSmaster->fsLssSub,
|
||||
LSSmaster->fsLssSub);
|
||||
|
||||
return CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - scan for 32 bits of LSS address, one by one
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_FsScanWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
switch (scan) {
|
||||
case CO_LSSmaster_FS_SCAN: break;
|
||||
case CO_LSSmaster_FS_MATCH:
|
||||
/* No scanning requested */
|
||||
return CO_LSSmaster_SCAN_FINISHED;
|
||||
break;
|
||||
case CO_LSSmaster_FS_SKIP:
|
||||
default: return CO_LSSmaster_SCAN_FAILED; break;
|
||||
}
|
||||
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
if (ret == CO_LSSmaster_TIMEOUT) {
|
||||
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
|
||||
if (CO_FLAG_READ(LSSmaster->CANrxNew)) {
|
||||
uint8_t cs = LSSmaster->CANrxData[0];
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
|
||||
if (cs != CO_LSS_IDENT_SLAVE) {
|
||||
/* wrong response received. Can not continue */
|
||||
return CO_LSSmaster_SCAN_FAILED;
|
||||
}
|
||||
} else {
|
||||
/* no response received, assumption is wrong */
|
||||
LSSmaster->fsIdNumber |= 1UL << LSSmaster->fsBitChecked;
|
||||
}
|
||||
|
||||
if (LSSmaster->fsBitChecked == CO_LSS_FASTSCAN_BIT0) {
|
||||
/* Scanning cycle is finished, we now have 32 bit address data */
|
||||
ret = CO_LSSmaster_SCAN_FINISHED;
|
||||
} else {
|
||||
LSSmaster->fsBitChecked--;
|
||||
|
||||
CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, LSSmaster->fsLssSub,
|
||||
LSSmaster->fsLssSub);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - initiate check for 32 bit part of LSS address
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_FsVerifyInitiate(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan,
|
||||
uint32_t idNumberCheck, uint8_t lssNext) {
|
||||
(void)timeDifference_us; /* unused */
|
||||
|
||||
switch (scan) {
|
||||
case CO_LSSmaster_FS_SCAN:
|
||||
/* ID obtained by scan */
|
||||
break;
|
||||
case CO_LSSmaster_FS_MATCH:
|
||||
/* ID given by user */
|
||||
LSSmaster->fsIdNumber = idNumberCheck;
|
||||
break;
|
||||
case CO_LSSmaster_FS_SKIP:
|
||||
default: return CO_LSSmaster_SCAN_FAILED; break;
|
||||
}
|
||||
|
||||
LSSmaster->fsBitChecked = CO_LSS_FASTSCAN_BIT0;
|
||||
|
||||
/* send request */
|
||||
CO_LSSmaster_FsSendMsg(LSSmaster, LSSmaster->fsIdNumber, LSSmaster->fsBitChecked, LSSmaster->fsLssSub, lssNext);
|
||||
|
||||
return CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - verify 32 bit LSS address, request node(s) to switch their state machine to the next state
|
||||
*/
|
||||
static CO_LSSmaster_return_t
|
||||
CO_LSSmaster_FsVerifyWait(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, CO_LSSmaster_scantype_t scan,
|
||||
uint32_t* idNumberRet) {
|
||||
CO_LSSmaster_return_t ret;
|
||||
|
||||
if (scan == CO_LSSmaster_FS_SKIP) {
|
||||
return CO_LSSmaster_SCAN_FAILED;
|
||||
}
|
||||
|
||||
ret = CO_LSSmaster_check_timeout(LSSmaster, timeDifference_us);
|
||||
if (ret == CO_LSSmaster_TIMEOUT) {
|
||||
|
||||
*idNumberRet = 0;
|
||||
ret = CO_LSSmaster_SCAN_NOACK;
|
||||
|
||||
if (CO_FLAG_READ(LSSmaster->CANrxNew)) {
|
||||
uint8_t cs = LSSmaster->CANrxData[0];
|
||||
CO_FLAG_CLEAR(LSSmaster->CANrxNew);
|
||||
|
||||
if (cs == CO_LSS_IDENT_SLAVE) {
|
||||
*idNumberRet = LSSmaster->fsIdNumber;
|
||||
ret = CO_LSSmaster_SCAN_FINISHED;
|
||||
} else {
|
||||
ret = CO_LSSmaster_SCAN_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - check which 32 bit to scan for next, if any
|
||||
*/
|
||||
static uint8_t
|
||||
CO_LSSmaster_FsSearchNext(CO_LSSmaster_t* LSSmaster, const CO_LSSmaster_fastscan_t* fastscan) {
|
||||
uint8_t i;
|
||||
|
||||
/* we search for the next LSS address part to scan for, beginning with the
|
||||
* one after the current one. If there is none remaining, scanning is finished */
|
||||
for (i = LSSmaster->fsLssSub + 1U; i <= CO_LSS_FASTSCAN_SERIAL; i++) {
|
||||
if (fastscan->scan[i] != CO_LSSmaster_FS_SKIP) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
/* node selection is triggered by switching node state machine back to initial state */
|
||||
return CO_LSS_FASTSCAN_VENDOR_ID;
|
||||
}
|
||||
|
||||
CO_LSSmaster_return_t
|
||||
CO_LSSmaster_IdentifyFastscan(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us,
|
||||
CO_LSSmaster_fastscan_t* fastscan) {
|
||||
uint8_t i;
|
||||
uint8_t count;
|
||||
CO_LSSmaster_return_t ret = CO_LSSmaster_INVALID_STATE;
|
||||
uint8_t next;
|
||||
|
||||
/* parameter validation */
|
||||
if ((LSSmaster == NULL) || (fastscan == NULL)) {
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
if (fastscan->scan[0] == CO_LSSmaster_FS_SKIP) {
|
||||
/* vendor ID scan cannot be skipped */
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
count = 0;
|
||||
for (i = 0; i < (sizeof(fastscan->scan) / sizeof(fastscan->scan[0])); i++) {
|
||||
if (fastscan->scan[i] == CO_LSSmaster_FS_SKIP) {
|
||||
count++;
|
||||
}
|
||||
if (count > 2U) {
|
||||
/* Node selection needs the Vendor ID and at least one other value */
|
||||
return CO_LSSmaster_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
/* state machine validation */
|
||||
if ((LSSmaster->state != CO_LSSmaster_STATE_WAITING)
|
||||
|| ((LSSmaster->command != CO_LSSmaster_COMMAND_WAITING)
|
||||
&& (LSSmaster->command != CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN))) {
|
||||
/* state machine not ready, other command is already processed */
|
||||
return CO_LSSmaster_INVALID_STATE;
|
||||
}
|
||||
|
||||
/* evaluate LSS state machine */
|
||||
if (LSSmaster->command == CO_LSSmaster_COMMAND_WAITING) {
|
||||
/* start fastscan */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_IDENTIFY_FASTSCAN;
|
||||
|
||||
/* check if any nodes are waiting, if yes fastscan is reset */
|
||||
LSSmaster->fsState = CO_LSSmaster_FS_STATE_CHECK;
|
||||
CO_LSSmaster_FsSendMsg(LSSmaster, 0, CO_LSS_FASTSCAN_CONFIRM, 0, 0);
|
||||
|
||||
return CO_LSSmaster_WAIT_SLAVE;
|
||||
} else {
|
||||
/* continue with evaluating fastscan state machine */
|
||||
}
|
||||
|
||||
/*
|
||||
* evaluate fastscan state machine. The state machine is evaluated as following
|
||||
* - check for non-configured nodes
|
||||
* - scan for vendor ID
|
||||
* - verify vendor ID, switch node state
|
||||
* - scan for product code
|
||||
* - verify product code, switch node state
|
||||
* - scan for revision number
|
||||
* - verify revision number, switch node state
|
||||
* - scan for serial number
|
||||
* - verify serial number, switch node to LSS configuration mode
|
||||
* Certain steps can be skipped as mentioned in the function description. If one step is
|
||||
* not ack'ed by a node, the scanning process is terminated and the correspondign error is returned.
|
||||
*/
|
||||
switch (LSSmaster->fsState) {
|
||||
case CO_LSSmaster_FS_STATE_CHECK:
|
||||
ret = CO_LSSmaster_FsCheckWait(LSSmaster, timeDifference_us);
|
||||
if (ret == CO_LSSmaster_SCAN_FINISHED) {
|
||||
(void)memset(&fastscan->found, 0, sizeof(fastscan->found));
|
||||
|
||||
/* start scanning procedure by triggering vendor ID scan */
|
||||
(void)CO_LSSmaster_FsScanInitiate(LSSmaster, timeDifference_us,
|
||||
fastscan->scan[CO_LSS_FASTSCAN_VENDOR_ID], CO_LSS_FASTSCAN_VENDOR_ID);
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
|
||||
LSSmaster->fsState = CO_LSSmaster_FS_STATE_SCAN;
|
||||
}
|
||||
break;
|
||||
case CO_LSSmaster_FS_STATE_SCAN:
|
||||
ret = CO_LSSmaster_FsScanWait(LSSmaster, timeDifference_us, fastscan->scan[LSSmaster->fsLssSub]);
|
||||
if (ret == CO_LSSmaster_SCAN_FINISHED) {
|
||||
/* scanning finished, initiate verifcation. The verification message also contains
|
||||
* the node state machine "switch to next state" request */
|
||||
next = CO_LSSmaster_FsSearchNext(LSSmaster, fastscan);
|
||||
ret = CO_LSSmaster_FsVerifyInitiate(LSSmaster, timeDifference_us, fastscan->scan[LSSmaster->fsLssSub],
|
||||
fastscan->match.addr[LSSmaster->fsLssSub], next);
|
||||
|
||||
LSSmaster->fsState = CO_LSSmaster_FS_STATE_VERIFY;
|
||||
}
|
||||
break;
|
||||
case CO_LSSmaster_FS_STATE_VERIFY:
|
||||
ret = CO_LSSmaster_FsVerifyWait(LSSmaster, timeDifference_us, fastscan->scan[LSSmaster->fsLssSub],
|
||||
&fastscan->found.addr[LSSmaster->fsLssSub]);
|
||||
if (ret == CO_LSSmaster_SCAN_FINISHED) {
|
||||
/* verification successful:
|
||||
* - assumed node id is correct
|
||||
* - node state machine has switched to the requested state, mirror that in the local copy */
|
||||
next = CO_LSSmaster_FsSearchNext(LSSmaster, fastscan);
|
||||
if (next == CO_LSS_FASTSCAN_VENDOR_ID) {
|
||||
/* fastscan finished, one node is now in LSS configuration mode */
|
||||
LSSmaster->state = CO_LSSmaster_STATE_CFG_SLECTIVE;
|
||||
} else {
|
||||
/* initiate scan for next part of LSS address */
|
||||
ret = CO_LSSmaster_FsScanInitiate(LSSmaster, timeDifference_us, fastscan->scan[next], next);
|
||||
if (ret == CO_LSSmaster_SCAN_FINISHED) {
|
||||
/* Scanning is not requested. Initiate verification step in next function call */
|
||||
ret = CO_LSSmaster_WAIT_SLAVE;
|
||||
}
|
||||
|
||||
LSSmaster->fsState = CO_LSSmaster_FS_STATE_SCAN;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* none */
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != CO_LSSmaster_WAIT_SLAVE) {
|
||||
/* finished */
|
||||
LSSmaster->command = CO_LSSmaster_COMMAND_WAITING;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER */
|
||||
389
Middleware/CANopenNode/305/CO_LSSmaster.h
Normal file
389
Middleware/CANopenNode/305/CO_LSSmaster.h
Normal file
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
* CANopen Layer Setting Service - master protocol.
|
||||
*
|
||||
* @file CO_LSSmaster.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CO_LSSmaster_H
|
||||
#define CO_LSSmaster_H
|
||||
|
||||
#include "305/CO_LSS.h"
|
||||
|
||||
#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_MASTER) != 0) || defined CO_DOXYGEN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSSmaster LSS Master
|
||||
* CANopen Layer Setting Service - master protocol.
|
||||
*
|
||||
* @ingroup CO_CANopen_305
|
||||
* @{
|
||||
* The client/master can use 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
|
||||
*
|
||||
* The LSS master is initalized during the CANopenNode initialization process. Except for enabling the LSS master in the
|
||||
* configurator, no further run-time configuration is needed for basic operation. The LSS master does basic checking of
|
||||
* commands and command sequence.
|
||||
*
|
||||
* ###Usage
|
||||
* Usage of the CANopen LSS master is demonstrated in file 309/CO_gateway_ascii.c
|
||||
*
|
||||
* It essentially is always as following:
|
||||
* - select node(s)
|
||||
* - call master command(s)
|
||||
* - evaluate return value
|
||||
* - deselect nodes
|
||||
*
|
||||
* All commands need to be run cyclically, e.g. like this
|
||||
* \code{.c}
|
||||
interval = 0;
|
||||
do {
|
||||
ret = CO_LSSmaster_InquireNodeId(LSSmaster, interval, &outval);
|
||||
|
||||
interval = 1;
|
||||
ms sleep(interval);
|
||||
} while (ret == CO_LSSmaster_WAIT_SLAVE);
|
||||
* \endcode
|
||||
*
|
||||
* A more advanced implementation can make use of the callback function to shorten waiting times.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Return values of LSS master functions.
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSSmaster_SCAN_FINISHED = 2, /**< Scanning finished successful */
|
||||
CO_LSSmaster_WAIT_SLAVE = 1, /**< No response arrived from slave yet */
|
||||
CO_LSSmaster_OK = 0, /**< Success, end of communication */
|
||||
CO_LSSmaster_TIMEOUT = -1, /**< No reply received */
|
||||
CO_LSSmaster_ILLEGAL_ARGUMENT = -2, /**< Invalid argument */
|
||||
CO_LSSmaster_INVALID_STATE = -3, /**< State machine not ready or already processing a request */
|
||||
CO_LSSmaster_SCAN_NOACK = -4, /**< No node found that matches scan request */
|
||||
CO_LSSmaster_SCAN_FAILED = -5, /**< An error occurred while scanning. Try again */
|
||||
CO_LSSmaster_OK_ILLEGAL_ARGUMENT = -101, /**< LSS success, node rejected argument because of non-supported value */
|
||||
CO_LSSmaster_OK_MANUFACTURER = -102, /**< LSS success, node rejected argument with manufacturer error code */
|
||||
} CO_LSSmaster_return_t;
|
||||
|
||||
/**
|
||||
* LSS master object.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t timeout_us; /**< LSS response timeout in us */
|
||||
uint8_t state; /**< Node is currently selected */
|
||||
uint8_t command; /**< Active command */
|
||||
uint32_t timeoutTimer; /**< Timeout timer for LSS communication */
|
||||
uint8_t fsState; /**< Current state of fastscan master state machine */
|
||||
uint8_t fsLssSub; /**< Current state of node state machine */
|
||||
uint8_t fsBitChecked; /**< Current scan bit position */
|
||||
uint32_t fsIdNumber; /**< Current scan result */
|
||||
volatile void* CANrxNew; /**< Indication if new LSS message is received from CAN bus. It needs to be cleared when
|
||||
received message is completely processed. */
|
||||
uint8_t CANrxData[8]; /**< 8 data bytes of the received message */
|
||||
#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN
|
||||
void (*pFunctSignal)(void* object); /**< From CO_LSSmaster_initCallbackPre() or NULL */
|
||||
void* functSignalObject; /**< Pointer to object */
|
||||
#endif
|
||||
CO_CANmodule_t* CANdevTx; /**< From CO_LSSmaster_init() */
|
||||
CO_CANtx_t* TXbuff; /**< CAN transmit buffer */
|
||||
} CO_LSSmaster_t;
|
||||
|
||||
/**
|
||||
* Default timeout for LSS slave in ms. This is the same as for SDO. For more info about LSS timeout see
|
||||
* #CO_LSSmaster_changeTimeout()
|
||||
*/
|
||||
#ifndef CO_LSSmaster_DEFAULT_TIMEOUT
|
||||
#define CO_LSSmaster_DEFAULT_TIMEOUT 1000U /* ms */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize LSS object.
|
||||
*
|
||||
* Function must be called in the communication reset section.
|
||||
*
|
||||
* @param LSSmaster This object will be initialized.
|
||||
* @param timeout_ms slave response timeout in ms, for more detail see #CO_LSSmaster_changeTimeout()
|
||||
* @param CANdevRx CAN device for LSS master reception.
|
||||
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
|
||||
* @param CANidLssSlave COB ID for reception.
|
||||
* @param CANdevTx CAN device for LSS master transmission.
|
||||
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
|
||||
* @param CANidLssMaster COB ID for transmission.
|
||||
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
|
||||
*/
|
||||
CO_ReturnError_t CO_LSSmaster_init(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms, CO_CANmodule_t* CANdevRx,
|
||||
uint16_t CANdevRxIdx, uint16_t CANidLssSlave, CO_CANmodule_t* CANdevTx,
|
||||
uint16_t CANdevTxIdx, uint16_t CANidLssMaster);
|
||||
|
||||
/**
|
||||
* 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 protocol 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 low CAN network priority (see @ref CO_Default_CAN_ID_t).
|
||||
*
|
||||
* @remark Be aware that a "late response" will seriously mess up LSS, so this value must be selected "as high as
|
||||
* necessary and as low as possible". CiA does neither specify nor recommend a value.
|
||||
*
|
||||
* @remark This timeout is per-transfer. If a command internally needs multiple transfers to complete, this timeout is
|
||||
* applied on each transfer.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeout_ms timeout value in ms
|
||||
*/
|
||||
void CO_LSSmaster_changeTimeout(CO_LSSmaster_t* LSSmaster, uint16_t timeout_ms);
|
||||
|
||||
#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN
|
||||
/**
|
||||
* Initialize LSSmasterRx callback function.
|
||||
*
|
||||
* Function initializes optional callback function, which should immediately start further LSS processing. Callback is
|
||||
* called after LSS message is received from the CAN bus. It should signal the RTOS to resume corresponding task.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
|
||||
* @param pFunctSignal Pointer to the callback function. Not called if NULL.
|
||||
*/
|
||||
void CO_LSSmaster_initCallbackPre(CO_LSSmaster_t* LSSmaster, void* object, void (*pFunctSignal)(void* object));
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Request LSS switch state select
|
||||
*
|
||||
* This function can select one specific or all nodes.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE Function is non-blocking.
|
||||
*
|
||||
* @remark Only one selection can be active at any time.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @param lssAddress LSS target address. If NULL, all nodes are selected
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK,
|
||||
* #CO_LSSmaster_TIMEOUT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_swStateSelect(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us,
|
||||
CO_LSS_address_t* lssAddress);
|
||||
|
||||
/**
|
||||
* Request LSS switch state deselect
|
||||
*
|
||||
* This function deselects all nodes, so it doesn't matter if a specific node is selected.
|
||||
*
|
||||
* This function also resets the LSS master state machine to a clean state
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_OK
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_swStateDeselect(CO_LSSmaster_t* LSSmaster);
|
||||
|
||||
/**
|
||||
* Request LSS configure Bit Timing
|
||||
*
|
||||
* The new bit rate is set as new pending value.
|
||||
*
|
||||
* This function needs one specific node to be selected.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @param bit new bit rate
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK,
|
||||
* #CO_LSSmaster_TIMEOUT, #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_configureBitTiming(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us,
|
||||
uint16_t bit);
|
||||
|
||||
/**
|
||||
* Request LSS configure node ID
|
||||
*
|
||||
* The new node id is set as new pending node ID.
|
||||
*
|
||||
* This function needs one specific node to be selected.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @param nodeId new node ID. Special value #CO_LSS_NODE_ID_ASSIGNMENT can be used to invalidate node ID.
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK,
|
||||
* #CO_LSSmaster_TIMEOUT, #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_configureNodeId(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us,
|
||||
uint8_t nodeId);
|
||||
|
||||
/**
|
||||
* Request LSS store configuration
|
||||
*
|
||||
* The current "pending" values for bit rate and node ID in LSS slave are stored as "permanent" values.
|
||||
*
|
||||
* This function needs one specific node to be selected.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK,
|
||||
* #CO_LSSmaster_TIMEOUT, #CO_LSSmaster_OK_MANUFACTURER, #CO_LSSmaster_OK_ILLEGAL_ARGUMENT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_configureStore(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us);
|
||||
|
||||
/**
|
||||
* Request LSS activate bit timing
|
||||
*
|
||||
* The current "pending" bit rate in LSS slave is applied.
|
||||
*
|
||||
* Be aware that changing the bit rate is a critical step for the network. A failure will render the network unusable!
|
||||
* Therefore, this function only should be called if the following conditions are met:
|
||||
* - all nodes support changing bit timing
|
||||
* - new bit timing is successfully set as "pending" in all nodes
|
||||
* - all nodes have to activate the new bit timing roughly at the same time. Therefore this function needs all nodes
|
||||
* to be selected.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param switchDelay_ms delay that is applied by the slave once before and once after switching in ms.
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_OK
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_ActivateBit(CO_LSSmaster_t* LSSmaster, uint16_t switchDelay_ms);
|
||||
|
||||
/**
|
||||
* Request LSS inquire LSS address
|
||||
*
|
||||
* The LSS address value is read from the node. This is useful when the node was selected by fastscan.
|
||||
*
|
||||
* This function needs one specific node to be selected.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @param [out] lssAddress read result when function returns successfully
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK,
|
||||
* #CO_LSSmaster_TIMEOUT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_InquireLssAddress(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us,
|
||||
CO_LSS_address_t* lssAddress);
|
||||
|
||||
/**
|
||||
* Request LSS inquire node ID or part of LSS address
|
||||
*
|
||||
* The node-ID, identity vendor-ID, product-code, revision-number or serial-number value is read from the node.
|
||||
*
|
||||
* This function needs one specific node to be selected.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @param lssInquireCs One of CO_LSS_INQUIRE_xx commands from @ref CO_LSS_command_specifiers.
|
||||
* @param [out] value read result when function returns successfully
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE, #CO_LSSmaster_OK,
|
||||
* #CO_LSSmaster_TIMEOUT
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_Inquire(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us, uint8_t lssInquireCs,
|
||||
uint32_t* value);
|
||||
|
||||
/**
|
||||
* Scan type for #CO_LSSmaster_fastscan_t scan
|
||||
*/
|
||||
typedef enum {
|
||||
CO_LSSmaster_FS_SCAN = 0, /**< Do full 32 bit scan */
|
||||
CO_LSSmaster_FS_SKIP = 1, /**< Skip this value */
|
||||
CO_LSSmaster_FS_MATCH = 2, /**< Full 32 bit value is given as argument, just verify */
|
||||
} CO_LSSmaster_scantype_t;
|
||||
|
||||
/**
|
||||
* Parameters for LSS fastscan #CO_LSSmaster_IdentifyFastscan
|
||||
*/
|
||||
typedef struct {
|
||||
CO_LSSmaster_scantype_t scan[4]; /**< Scan type for each part of the LSS address */
|
||||
CO_LSS_address_t match; /**< Value to match in case of #CO_LSSmaster_FS_MATCH */
|
||||
CO_LSS_address_t found; /**< Scan result */
|
||||
} CO_LSSmaster_fastscan_t;
|
||||
|
||||
/**
|
||||
* Select a node by LSS identify fastscan
|
||||
*
|
||||
* This initiates searching for a unconfigured node by the means of LSS fastscan mechanism. When this function is
|
||||
* finished
|
||||
* - a (more or less) arbitrary node is selected and ready for node ID assingment
|
||||
* - no node is selected because the given criteria do not match a node
|
||||
* - no node is selected because all nodes are already configured
|
||||
*
|
||||
* There are multiple ways to scan for a node. Depending on those, the scan will take different amounts of time:
|
||||
* - full scan
|
||||
* - partial scan
|
||||
* - verification
|
||||
*
|
||||
* Most of the time, those are used in combination. Consider the following example:
|
||||
* - Vendor ID and product code are known
|
||||
* - Software version doesn't matter
|
||||
* - Serial number is unknown
|
||||
*
|
||||
* In this case, the fastscan structure should be set up as following:
|
||||
* \code{.c}
|
||||
CO_LSSmaster_fastscan_t fastscan;
|
||||
fastscan.scan[CO_LSS_FASTSCAN_VENDOR_ID] = CO_LSSmaster_FS_MATCH;
|
||||
fastscan.match.vendorID = YOUR_VENDOR_ID;
|
||||
fastscan.scan[CO_LSS_FASTSCAN_PRODUCT] = CO_LSSmaster_FS_MATCH;
|
||||
fastscan.match.productCode = YOUR_PRODUCT_CODE;
|
||||
fastscan.scan[CO_LSS_FASTSCAN_REV] = CO_LSSmaster_FS_SKIP;
|
||||
fastscan.scan[CO_LSS_FASTSCAN_SERIAL] = CO_LSSmaster_FS_SCAN;
|
||||
* \endcode
|
||||
*
|
||||
* This example will take 2 scan cyles for verifying vendor ID and product code and 33 scan cycles to find the serial
|
||||
* number.
|
||||
*
|
||||
* For scanning, the following limitations apply:
|
||||
* - No more than two values can be skipped
|
||||
* - Vendor ID cannot be skipped
|
||||
*
|
||||
* @remark When doing partial scans, it is in the responsibility of the user that the LSS address is unique.
|
||||
*
|
||||
* This function needs that no node is selected when starting the scan process.
|
||||
*
|
||||
* Function must be called cyclically until it returns != #CO_LSSmaster_WAIT_SLAVE. Function is non-blocking.
|
||||
*
|
||||
* @param LSSmaster This object.
|
||||
* @param timeDifference_us Time difference from previous function call in [microseconds]. Zero when request is started.
|
||||
* @param fastscan struct according to #CO_LSSmaster_fastscan_t.
|
||||
* @return #CO_LSSmaster_ILLEGAL_ARGUMENT, #CO_LSSmaster_INVALID_STATE, #CO_LSSmaster_WAIT_SLAVE,
|
||||
* #CO_LSSmaster_SCAN_FINISHED, #CO_LSSmaster_SCAN_NOACK, #CO_LSSmaster_SCAN_FAILED
|
||||
*/
|
||||
CO_LSSmaster_return_t CO_LSSmaster_IdentifyFastscan(CO_LSSmaster_t* LSSmaster, uint32_t timeDifference_us,
|
||||
CO_LSSmaster_fastscan_t* fastscan);
|
||||
|
||||
/** @} */ /* @defgroup CO_LSSmaster */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_MASTER */
|
||||
|
||||
#endif /* CO_LSSmaster_H */
|
||||
429
Middleware/CANopenNode/305/CO_LSSslave.c
Normal file
429
Middleware/CANopenNode/305/CO_LSSslave.c
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* CANopen LSS Slave protocol.
|
||||
*
|
||||
* @file CO_LSSslave.c
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "305/CO_LSSslave.h"
|
||||
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0
|
||||
|
||||
/* 'bit' must be unsigned or additional range check must be added: bit>=CO_LSS_FASTSCAN_BIT0 */
|
||||
#define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) ((bit <= CO_LSS_FASTSCAN_BIT31) || (bit == CO_LSS_FASTSCAN_CONFIRM))
|
||||
/* 'index' must be unsigned or additional range check must be added: index>=CO_LSS_FASTSCAN_VENDOR_ID */
|
||||
#define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index <= CO_LSS_FASTSCAN_SERIAL)
|
||||
/* 'index' must be unsigned or additional range check must be added: index>=CO_LSS_BIT_TIMING_1000 */
|
||||
#define CO_LSS_BIT_TIMING_VALID(index) ((index != 5U) && (index <= CO_LSS_BIT_TIMING_AUTO))
|
||||
|
||||
/*
|
||||
* 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, void* msg) {
|
||||
CO_LSSslave_t* LSSslave = (CO_LSSslave_t*)object;
|
||||
uint8_t DLC = CO_CANrxMsg_readDLC(msg);
|
||||
|
||||
if ((DLC == 8U) && !CO_FLAG_READ(LSSslave->sendResponse)) {
|
||||
bool_t request_LSSslave_process = false;
|
||||
const uint8_t* data = CO_CANrxMsg_readData(msg);
|
||||
uint8_t cs = data[0];
|
||||
|
||||
if (cs == CO_LSS_SWITCH_STATE_GLOBAL) {
|
||||
uint8_t mode = data[1];
|
||||
|
||||
switch (mode) {
|
||||
case CO_LSS_STATE_WAITING:
|
||||
if ((LSSslave->lssState == CO_LSS_STATE_CONFIGURATION)
|
||||
&& (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)
|
||||
&& (*LSSslave->pendingNodeID != CO_LSS_NODE_ID_ASSIGNMENT)) {
|
||||
/* Slave process function will request NMT Reset comm. */
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
}
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
(void)memset(&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect));
|
||||
break;
|
||||
case CO_LSS_STATE_CONFIGURATION: LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; break;
|
||||
default:
|
||||
/* none */
|
||||
break;
|
||||
}
|
||||
} else if (LSSslave->lssState == CO_LSS_STATE_WAITING) {
|
||||
switch (cs) {
|
||||
case CO_LSS_SWITCH_STATE_SEL_VENDOR: {
|
||||
uint32_t valSw;
|
||||
(void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw));
|
||||
LSSslave->lssSelect.identity.vendorID = CO_SWAP_32(valSw);
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_PRODUCT: {
|
||||
uint32_t valSw;
|
||||
(void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw));
|
||||
LSSslave->lssSelect.identity.productCode = CO_SWAP_32(valSw);
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_REV: {
|
||||
uint32_t valSw;
|
||||
(void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw));
|
||||
LSSslave->lssSelect.identity.revisionNumber = CO_SWAP_32(valSw);
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_SERIAL: {
|
||||
uint32_t valSw;
|
||||
(void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw));
|
||||
LSSslave->lssSelect.identity.serialNumber = CO_SWAP_32(valSw);
|
||||
|
||||
if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) {
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CO_LSS_IDENT_FASTSCAN: {
|
||||
/* fastscan is only active on unconfigured nodes */
|
||||
if ((*LSSslave->pendingNodeID == CO_LSS_NODE_ID_ASSIGNMENT)
|
||||
&& (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)) {
|
||||
uint8_t bitCheck = data[5];
|
||||
uint8_t lssSub = data[6];
|
||||
uint8_t lssNext = data[7];
|
||||
uint32_t valSw;
|
||||
uint32_t idNumber;
|
||||
bool_t ack;
|
||||
|
||||
if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) || !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub)
|
||||
|| !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) {
|
||||
/* Invalid request */
|
||||
break;
|
||||
}
|
||||
|
||||
(void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw));
|
||||
idNumber = CO_SWAP_32(valSw);
|
||||
ack = false;
|
||||
|
||||
if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) {
|
||||
/* Confirm, Reset */
|
||||
ack = true;
|
||||
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
|
||||
(void)memset(&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan));
|
||||
} else if (LSSslave->fastscanPos == lssSub) {
|
||||
uint32_t mask = 0xFFFFFFFFU << bitCheck;
|
||||
|
||||
if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) {
|
||||
/* all requested bits match */
|
||||
ack = true;
|
||||
LSSslave->fastscanPos = lssNext;
|
||||
|
||||
if ((bitCheck == 0U) && (lssNext < lssSub)) {
|
||||
/* complete match, enter configuration state */
|
||||
LSSslave->lssState = CO_LSS_STATE_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
} else { /* MISRA C 2004 14.10 */
|
||||
}
|
||||
if (ack) {
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND) != 0
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE;
|
||||
(void)memset(&LSSslave->TXbuff->data[1], 0, sizeof(LSSslave->TXbuff->data) - 1U);
|
||||
(void)CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
#else
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* none */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else { /* LSSslave->lssState == CO_LSS_STATE_CONFIGURATION */
|
||||
(void)memcpy((void*)(&LSSslave->CANdata[0]), (const void*)(&data[0]), sizeof(LSSslave->CANdata));
|
||||
LSSslave->service = cs;
|
||||
request_LSSslave_process = true;
|
||||
}
|
||||
|
||||
if (request_LSSslave_process) {
|
||||
CO_FLAG_SET(LSSslave->sendResponse);
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
|
||||
/* Optional signal to RTOS, which can resume task, which handles further processing. */
|
||||
if (LSSslave->pFunctSignalPre != NULL) {
|
||||
LSSslave->pFunctSignalPre(LSSslave->functSignalObjectPre);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, uint16_t CANidLssMaster,
|
||||
CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint16_t CANidLssSlave) {
|
||||
CO_ReturnError_t ret = CO_ERROR_NO;
|
||||
|
||||
/* verify arguments */
|
||||
if ((LSSslave == NULL) || (pendingBitRate == NULL) || (pendingNodeID == NULL) || (CANdevRx == NULL)
|
||||
|| (CANdevTx == NULL) || !CO_LSS_NODE_ID_VALID(*pendingNodeID)) {
|
||||
return CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
/* Application must make sure that lssAddress is filled with data. */
|
||||
|
||||
/* clear the object */
|
||||
(void)memset(LSSslave, 0, sizeof(CO_LSSslave_t));
|
||||
|
||||
/* Configure object variables */
|
||||
(void)memcpy(&LSSslave->lssAddress, lssAddress, sizeof(LSSslave->lssAddress));
|
||||
LSSslave->lssState = CO_LSS_STATE_WAITING;
|
||||
LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID;
|
||||
|
||||
LSSslave->pendingBitRate = pendingBitRate;
|
||||
LSSslave->pendingNodeID = pendingNodeID;
|
||||
LSSslave->activeNodeID = *pendingNodeID;
|
||||
CO_FLAG_CLEAR(LSSslave->sendResponse);
|
||||
|
||||
/* configure LSS CAN Master message reception */
|
||||
ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CANidLssMaster, 0x7FF, false, (void*)LSSslave, CO_LSSslave_receive);
|
||||
|
||||
/* configure LSS CAN Slave response message transmission */
|
||||
LSSslave->CANdevTx = CANdevTx;
|
||||
LSSslave->TXbuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CANidLssSlave, false, 8, false);
|
||||
|
||||
if (LSSslave->TXbuff == NULL) {
|
||||
ret = CO_ERROR_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
|
||||
void
|
||||
CO_LSSslave_initCallbackPre(CO_LSSslave_t* LSSslave, void* object, void (*pFunctSignalPre)(void* object)) {
|
||||
if (LSSslave != NULL) {
|
||||
LSSslave->functSignalObjectPre = object;
|
||||
LSSslave->pFunctSignalPre = pFunctSignalPre;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CO_LSSslave_initCkBitRateCall(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_initActBitRateCall(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_initCfgStoreCall(CO_LSSslave_t* LSSslave, void* object,
|
||||
bool_t (*pFunctLSScfgStore)(void* object, uint8_t id, uint16_t bitRate)) {
|
||||
if (LSSslave != NULL) {
|
||||
LSSslave->functLSScfgStoreObject = object;
|
||||
LSSslave->pFunctLSScfgStore = pFunctLSScfgStore;
|
||||
}
|
||||
}
|
||||
|
||||
bool_t
|
||||
CO_LSSslave_process(CO_LSSslave_t* LSSslave) {
|
||||
bool_t resetCommunication = false;
|
||||
|
||||
if (CO_FLAG_READ(LSSslave->sendResponse)) {
|
||||
uint8_t nid;
|
||||
uint8_t errorCode;
|
||||
uint8_t errorCodeManuf;
|
||||
uint8_t tableSelector;
|
||||
uint8_t tableIndex;
|
||||
bool_t CANsend = false;
|
||||
uint32_t valSw;
|
||||
|
||||
(void)memset(&LSSslave->TXbuff->data[0], 0, sizeof(LSSslave->TXbuff->data));
|
||||
|
||||
switch (LSSslave->service) {
|
||||
case CO_LSS_SWITCH_STATE_GLOBAL: {
|
||||
/* Node-Id was unconfigured before, now it is configured,
|
||||
* enter the NMT Reset communication autonomously. */
|
||||
resetCommunication = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_SWITCH_STATE_SEL_SERIAL: {
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_NODE_ID: {
|
||||
nid = LSSslave->CANdata[1];
|
||||
errorCode = CO_LSS_CFG_NODE_ID_OK;
|
||||
|
||||
if (CO_LSS_NODE_ID_VALID(nid)) {
|
||||
*LSSslave->pendingNodeID = nid;
|
||||
} else {
|
||||
errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_BIT_TIMING: {
|
||||
if (LSSslave->pFunctLSScheckBitRate == NULL) {
|
||||
/* setting bit timing is not supported. Drop request */
|
||||
break;
|
||||
}
|
||||
|
||||
tableSelector = LSSslave->CANdata[1];
|
||||
tableIndex = LSSslave->CANdata[2];
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OK;
|
||||
errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OK;
|
||||
|
||||
if ((tableSelector == 0U) && CO_LSS_BIT_TIMING_VALID(tableIndex)) {
|
||||
uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex];
|
||||
bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate(LSSslave->functLSScheckBitRateObject,
|
||||
bit);
|
||||
|
||||
if (bit_rate_supported) {
|
||||
*LSSslave->pendingBitRate = bit;
|
||||
} else {
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_MANUFACTURER;
|
||||
errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
/* we currently only support CiA301 bit timing table */
|
||||
errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
LSSslave->TXbuff->data[2] = errorCodeManuf;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_ACTIVATE_BIT_TIMING: {
|
||||
if (LSSslave->pFunctLSScheckBitRate == NULL) {
|
||||
/* setting bit timing is not supported. Drop request */
|
||||
break;
|
||||
}
|
||||
|
||||
/* notify application */
|
||||
if (LSSslave->pFunctLSSactivateBitRate != NULL) {
|
||||
uint16_t delay = ((uint16_t)LSSslave->CANdata[2]) << 8;
|
||||
delay |= LSSslave->CANdata[1];
|
||||
LSSslave->pFunctLSSactivateBitRate(LSSslave->functLSSactivateBitRateObject, delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CO_LSS_CFG_STORE: {
|
||||
errorCode = CO_LSS_CFG_STORE_OK;
|
||||
|
||||
if (LSSslave->pFunctLSScfgStore == NULL) {
|
||||
/* storing is not supported. Reply error */
|
||||
errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED;
|
||||
} else {
|
||||
bool_t result;
|
||||
/* Store "pending" to "persistent" */
|
||||
result = LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStoreObject, *LSSslave->pendingNodeID,
|
||||
*LSSslave->pendingBitRate);
|
||||
if (!result) {
|
||||
errorCode = CO_LSS_CFG_STORE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* send confirmation */
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = errorCode;
|
||||
/* we do not use spec-error, always 0 */
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_VENDOR: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
valSw = CO_SWAP_32(LSSslave->lssAddress.identity.vendorID);
|
||||
(void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw));
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_PRODUCT: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
valSw = CO_SWAP_32(LSSslave->lssAddress.identity.productCode);
|
||||
(void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw));
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_REV: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
valSw = CO_SWAP_32(LSSslave->lssAddress.identity.revisionNumber);
|
||||
(void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw));
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_SERIAL: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
valSw = CO_SWAP_32(LSSslave->lssAddress.identity.serialNumber);
|
||||
(void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw));
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_INQUIRE_NODE_ID: {
|
||||
LSSslave->TXbuff->data[0] = LSSslave->service;
|
||||
LSSslave->TXbuff->data[1] = LSSslave->activeNodeID;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
case CO_LSS_IDENT_FASTSCAN: {
|
||||
LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE;
|
||||
CANsend = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* none */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CANsend) {
|
||||
(void)CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff);
|
||||
}
|
||||
|
||||
CO_FLAG_CLEAR(LSSslave->sendResponse);
|
||||
}
|
||||
|
||||
return resetCommunication;
|
||||
}
|
||||
|
||||
#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE */
|
||||
238
Middleware/CANopenNode/305/CO_LSSslave.h
Normal file
238
Middleware/CANopenNode/305/CO_LSSslave.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* CANopen Layer Setting Service - slave protocol.
|
||||
*
|
||||
* @file CO_LSSslave.h
|
||||
* @ingroup CO_LSS
|
||||
* @author Martin Wagner
|
||||
* @author Janez Paternoster
|
||||
* @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH
|
||||
*
|
||||
*
|
||||
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CO_LSSslave_H
|
||||
#define CO_LSSslave_H
|
||||
|
||||
#include "305/CO_LSS.h"
|
||||
|
||||
#if (((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0) || defined CO_DOXYGEN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup CO_LSSslave LSS Slave
|
||||
* CANopen Layer Setting Service - slave protocol.
|
||||
*
|
||||
* @ingroup CO_CANopen_305
|
||||
* @{
|
||||
* The 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 slave and NMT slave are started and then coexist alongside each other. To achieve
|
||||
* this behaviour, the CANopen node startup process has to be controlled more detailed. Therefore, CO_LSSinit() must be
|
||||
* invoked between CO_CANinit() and CO_CANopenInit() in the communication reset section.
|
||||
*
|
||||
* Moreover, the LSS slave needs to pause the NMT slave initialization in case no valid node ID is available at start
|
||||
* up. In that case CO_CANopenInit() skips initialization of other CANopen modules and CO_process() skips processing of
|
||||
* other modules than LSS slave automatically.
|
||||
*
|
||||
* Variables for CAN-bitrate and CANopen node-id must be initialized by application from non-volatile memory or dip
|
||||
* switches. Pointers to them are passed to CO_LSSinit() function. Those variables represents pending values. If node-id
|
||||
* is valid in the moment it enters CO_LSSinit(), it also becomes active node-id and the stack initialises normally.
|
||||
* Otherwise, node-id must be configured by lss and after successful configuration stack passes reset communication
|
||||
* autonomously.
|
||||
*
|
||||
* Device with all threads can be normally initialized and running despite that node-id is not valid. Application must
|
||||
* take care, because CANopen is not initialized. In that case CO_CANopenInit() returns error condition
|
||||
* CO_ERROR_NODE_ID_UNCONFIGURED_LSS which must be handled properly. Status can also be checked with
|
||||
* CO->nodeIdUnconfigured variable.
|
||||
*
|
||||
* Some callback functions may be initialized by application with CO_LSSslave_initCkBitRateCall(),
|
||||
* CO_LSSslave_initActBitRateCall() and CO_LSSslave_initCfgStoreCall().
|
||||
*/
|
||||
|
||||
/**
|
||||
* LSS slave object.
|
||||
*/
|
||||
typedef struct {
|
||||
CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */
|
||||
uint8_t lssState; /**< @ref CO_LSS_STATE_state */
|
||||
CO_LSS_address_t lssSelect; /**< Received LSS Address by select */
|
||||
CO_LSS_address_t lssFastscan; /**< Received LSS Address by fastscan */
|
||||
uint8_t fastscanPos; /**< Current state of fastscan */
|
||||
uint16_t* pendingBitRate; /**< Bit rate value that is temporarily configured */
|
||||
uint8_t* pendingNodeID; /**< Node ID that is temporarily configured */
|
||||
uint8_t activeNodeID; /**< Node ID used at the CAN interface */
|
||||
volatile void*
|
||||
sendResponse; /**< Variable indicates, if LSS response has to be sent by mainline processing function */
|
||||
uint8_t service; /**< Service, which will have to be processed by mainline processing function */
|
||||
uint8_t CANdata[8]; /**< Received CAN data, which will be processed by mainline processing function */
|
||||
|
||||
#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN
|
||||
void (*pFunctSignalPre)(void* object); /**< From CO_LSSslave_initCallbackPre() or NULL */
|
||||
void* functSignalObjectPre; /**< Pointer to object */
|
||||
#endif
|
||||
bool_t (*pFunctLSScheckBitRate)(void* object,
|
||||
uint16_t bitRate); /**< From CO_LSSslave_initCkBitRateCall() or NULL */
|
||||
void* functLSScheckBitRateObject; /** Pointer to object */
|
||||
void (*pFunctLSSactivateBitRate)(
|
||||
void* object, uint16_t delay); /**< From CO_LSSslave_initActBitRateCall() 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_initCfgStoreCall() or NULL */
|
||||
void* functLSScfgStoreObject; /** 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.
|
||||
*
|
||||
* pendingBitRate and pendingNodeID must be pointers to external variables. Both variables must be initialized on
|
||||
* program startup (after #CO_NMT_RESET_NODE) from non-volatile memory, dip switches or similar. They must not change
|
||||
* during #CO_NMT_RESET_COMMUNICATION. Both variables can be changed by CO_LSSslave_process(), depending on commands
|
||||
* from the LSS master.
|
||||
*
|
||||
* If pendingNodeID is valid (1 <= pendingNodeID <= 0x7F), then this becomes valid active nodeId just after exit of this
|
||||
* function. In that case all other CANopen objects may be initialized and processed in run time.
|
||||
*
|
||||
* If pendingNodeID is not valid (pendingNodeID == 0xFF), then only LSS slave is initialized and processed in run time.
|
||||
* In that state pendingNodeID can be configured and after successful configuration reset communication with all CANopen
|
||||
* object is activated automatically.
|
||||
*
|
||||
* @remark The LSS address needs to be unique on the network. For this, the 128 bit wide identity object (1018h) is
|
||||
* used. Therefore, this object has to be fully initialized before passing it to this function (vendorID, product code,
|
||||
* revisionNo, serialNo are set to 0 by default). Otherwise, if non-configured devices are present on CANopen network,
|
||||
* LSS configuration may behave unpredictable.
|
||||
*
|
||||
* @param LSSslave This object will be initialized.
|
||||
* @param lssAddress LSS address
|
||||
* @param [in,out] pendingBitRate Pending bit rate of the CAN interface
|
||||
* @param [in,out] pendingNodeID Pending node ID or 0xFF - invalid
|
||||
* @param CANdevRx CAN device for LSS slave reception.
|
||||
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
|
||||
* @param CANidLssMaster COB ID for reception.
|
||||
* @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,
|
||||
uint16_t CANidLssMaster, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx,
|
||||
uint16_t CANidLssSlave);
|
||||
|
||||
/**
|
||||
* Process LSS communication
|
||||
*
|
||||
* Object is partially pre-processed after LSS message received. Further processing is inside this function.
|
||||
*
|
||||
* In case that Node-Id is unconfigured, then this function may request CANopen communication reset. This happens, when
|
||||
* valid node-id is configured by LSS master.
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @return True, if #CO_NMT_RESET_COMMUNICATION is requested
|
||||
*/
|
||||
bool_t CO_LSSslave_process(CO_LSSslave_t* LSSslave);
|
||||
|
||||
/**
|
||||
* Get current LSS state
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @return @ref CO_LSS_STATE_state
|
||||
*/
|
||||
static inline uint8_t
|
||||
CO_LSSslave_getState(CO_LSSslave_t* LSSslave) {
|
||||
return (LSSslave == NULL) ? CO_LSS_STATE_WAITING : LSSslave->lssState;
|
||||
}
|
||||
|
||||
#if (((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0) || defined CO_DOXYGEN
|
||||
/**
|
||||
* Initialize LSSslaveRx callback function.
|
||||
*
|
||||
* Function initializes optional callback function, which should immediately start further LSS processing. Callback is
|
||||
* called after LSS message is received from the CAN bus. It should signal the RTOS to resume corresponding task.
|
||||
*
|
||||
* @param LSSslave This object.
|
||||
* @param object Pointer to object, which will be passed to pFunctSignal(). Can be NULL
|
||||
* @param pFunctSignalPre Pointer to the callback function. Not called if NULL.
|
||||
*/
|
||||
void CO_LSSslave_initCallbackPre(CO_LSSslave_t* LSSslave, void* object, void (*pFunctSignalPre)(void* object));
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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 slave will no-ack the request, indicating to the master that bit rate change is not
|
||||
* supported.
|
||||
*
|
||||
* @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_initCkBitRateCall(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 mustn't send any messages.
|
||||
*
|
||||
* @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_initActBitRateCall(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 slave will no-ack the request, indicating to the master that storing is not
|
||||
* supported.
|
||||
*
|
||||
* @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_initCfgStoreCall(CO_LSSslave_t* LSSslave, void* object,
|
||||
bool_t (*pFunctLSScfgStore)(void* object, uint8_t id, uint16_t bitRate));
|
||||
|
||||
/** @} */ /* @defgroup CO_LSSslave */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE */
|
||||
|
||||
#endif /* CO_LSSslave_H */
|
||||
Reference in New Issue
Block a user