/** * CANopen LSS Master/Slave protocol. * * @file CO_LSSmaster.h * @ingroup CO_LSS * @author Martin Wagner * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH * * * This file is part of CANopenNode, an opensource CANopen Stack. * Project home page is . * For more information on CANopen see . * * 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 #ifdef __cplusplus extern "C" { #endif #if CO_NO_LSS_CLIENT == 1 #include "CO_LSS.h" /** * @addtogroup CO_LSS * @defgroup CO_LSSmaster LSS Master * @ingroup CO_LSS * @{ * * CANopen Layer Setting Service - client protocol * * 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 CANopenSocket application, * see CO_LSS_master.c / CO_LSS_master.h files. * * 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 server 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{ uint16_t timeout; /**< LSS response timeout in ms */ uint8_t state; /**< Node is currently selected */ uint8_t command; /**< Active command */ uint16_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 */ void (*pFunctSignal)(void *object); /**< From CO_LSSmaster_initCallback() or NULL */ void *functSignalObject;/**< Pointer to object */ CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_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() */ #define CO_LSSmaster_DEFAULT_TIMEOUT 1000U /* ms */ /** * 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, uint32_t CANidLssSlave, CO_CANmodule_t *CANdevTx, uint16_t CANdevTxIdx, uint32_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 * #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); /** * Initialize LSSserverRx callback function. * * Function initializes optional callback function, which is called after new * message is received from the CAN bus. Function may wake up external task, * which processes mainline CANopen functions. * * @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_initCallback( CO_LSSmaster_t *LSSmaster, void *object, void (*pFunctSignal)(void *object)); /** * 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_ms Time difference from previous function call in * [milliseconds]. 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_switchStateSelect( CO_LSSmaster_t *LSSmaster, uint16_t timeDifference_ms, 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_switchStateDeselect( 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_ms Time difference from previous function call in * [milliseconds]. 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, uint16_t timeDifference_ms, 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_ms Time difference from previous function call in * [milliseconds]. 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, uint16_t timeDifference_ms, 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_ms Time difference from previous function call in * [milliseconds]. 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, uint16_t timeDifference_ms); /** * 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_ms Time difference from previous function call in * [milliseconds]. Zero when request is started. * @param lssAddress [out] 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, uint16_t timeDifference_ms, CO_LSS_address_t *lssAddress); /** * Request LSS inquire node ID * * The node ID 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_ms Time difference from previous function call in * [milliseconds]. Zero when request is started. * @param nodeId [out] 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_InquireNodeId( CO_LSSmaster_t *LSSmaster, uint16_t timeDifference_ms, uint8_t *nodeId); /** * 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_ms Time difference from previous function call in * [milliseconds]. 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, uint16_t timeDifference_ms, CO_LSSmaster_fastscan_t *fastscan); #else /* CO_NO_LSS_CLIENT == 1 */ /** * @addtogroup CO_LSS * @{ * If you need documetation for LSS master usage, add "CO_NO_LSS_CLIENT=1" to doxygen * "PREDEFINED" variable. * */ #endif /* CO_NO_LSS_CLIENT == 1 */ #ifdef __cplusplus } #endif /*__cplusplus*/ /** @} */ #endif