MindSDK_MM32F5270/components/canopen/CO_LSSslave.h

431 lines
15 KiB
C

/**
* CANopen LSS Master/Slave protocol.
*
* @file CO_LSSslave.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 <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* 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
#ifdef __cplusplus
extern "C" {
#endif
#if CO_NO_LSS_SERVER == 1
#include "CO_LSS.h"
/**
* @addtogroup CO_LSS
* @defgroup CO_LSSslave LSS Slave
* @ingroup CO_LSS
* @{
*
* CANopen Layer Setting Service - server protocol
*
* The server/slave provides the following services
* - node selection via LSS address
* - node selection via LSS fastscan
* - Inquire LSS address of currently selected node
* - Inquire node ID
* - Configure bit timing
* - Configure node ID
* - Activate bit timing parameters
* - Store configuration (bit rate and node ID)
*
* After CAN module start, the LSS server and NMT server are started and then
* coexist alongside each other. To achieve this behaviour, the CANopen node
* startup process has to be conrolled more detailled. Therefore, the function
* CO_init() is split up into the functions #CO_new(), #CO_CANinit(), #CO_LSSinit()
* and #CO_CANopenInit().
* Moreover, the LSS server needs to pause the NMT server initialization in case
* no valid node ID is available at start up.
*
* ###Example
*
* It is strongly recommended that the user already has a fully working application
* running with the standard (non LSS) version of CANopenNode. This is required
* to understand what this example does and where you need to change it for your
* requirements.
*
* The following code is only a suggestion on how to use the LSS server. It is
* not a working example! To simplify the code, no error handling is
* included. For stable code, proper error handling has to be added to the user
* code.
*
* This example is not intended for bare metal targets. If you intend to do CAN
* message receiving inside interrupt, be aware that the callback functions
* will be called inside the interrupt handler context!
*
* \code{.c}
const uint16_t FIRST_BIT = 125;
queue changeBitRate;
uint8_t activeNid;
uint16_t activeBit;
bool_t checkBitRateCallback(void *object, uint16_t bitRate)
{
if (validBit(bitRate)) {
return true;
}
return false;
}
void activateBitRateCallback(void *object, uint16_t delay)
{
int time = getCurrentTime();
queueSend(&changeBitRate, time, delay);
}
bool_t cfgStoreCallback(void *object, uint8_t id, uint16_t bitRate)
{
savePersistent(id, bitRate);
return true;
}
void start_canopen(uint8_t nid)
{
uint8_t persistentNid;
uint8_t pendingNid;
uint16_t persistentBit;
uint16_t pendingBit;
loadPersistent(&persistentNid, &persistentBit);
if ( ! validBit(persistentBit)) {
printf("no bit rate found, defaulting to %d", FIRST_BIT);
pendingBit = FIRST_BIT;
}
else {
printf("loaded bit rate from nvm: %d", persistentBit);
pendingBit = persistentBit;
}
if (nid == 0) {
if ( ! validNid(persistentNid)) {
pendingNid = CO_LSS_NODE_ID_ASSIGNMENT;
printf("no node id found, needs to be set by LSS. NMT will"
"not be started until valid node id is set");
}
else {
printf("loaded node id from nvm: %d", persistentNid);
pendingNid = persistentNid;
}
}
else {
printf("node id provided by application: %d", nid);
pendingNid = nid;
}
CO_new();
CO_CANinit(0, pendingBit);
CO_LSSinit(pendingNid, pendingBit);
CO_CANsetNormalMode(CO->CANmodule[0]);
activeBit = pendingBit;
CO_LSSslave_initCheckBitRateCallback(CO->LSSslave, NULL, checkBitRateCallback);
CO_LSSslave_initActivateBitRateCallback(CO->LSSslave, NULL, activateBitRateCallback);
CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL, cfgStoreCallback);
while (1) {
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
&pendingBit, &pendingNid);
if (pendingNid!=CO_LSS_NODE_ID_ASSIGNMENT &&
CO_LSSslave_getState(CO->LSSslave)==CO_LSS_STATE_WAITING) {
printf("node ID has been found: %d", pendingNid);
break;
}
if ( ! queueEmpty(&changeBitRate)) {
printf("bit rate change requested: %d", pendingBit);
int time;
uint16_t delay;
queueReceive(&changeBitRate, time, delay);
delayUntil(time + delay);
CO_CANsetBitrate(CO->CANmodule[0], pendingBit);
delay(delay);
}
printf("waiting for node id");
CO_CANrxWait(CO->CANmodule[0]);
}
CO_CANopenInit(pendingNid);
activeNid = pendingNid;
printf("from this on, initialization doesn't differ to non-LSS version"
"You can now intialize your CO_CANrxWait() thread or interrupt");
}
void main(void)
{
uint8_t pendingNid;
uint16_t pendingBit;
printf("like example in dir \"example\"");
CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
uint16_t timer1msPrevious;
start_canopen(0);
reset = CO_RESET_NOT;
timer1msPrevious = CO_timer1ms;
while(reset == CO_RESET_NOT){
printf("loop for normal program execution");
uint16_t timer1msCopy, timer1msDiff;
timer1msCopy = CO_timer1ms;
timer1msDiff = timer1msCopy - timer1msPrevious;
timer1msPrevious = timer1msCopy;
reset = CO_process(CO, timer1msDiff, NULL);
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
&pendingBit, &pendingNid);
if (reset == CO_RESET_COMM) {
printf("restarting CANopen using pending node ID %d", pendingNid);
CO_delete(0);
start_canopen(pendingNid);
reset = CO_RESET_NOT;
}
if ( ! queueEmpty(&changeBitRate)) {
printf("bit rate change requested: %d", pendingBit);
int time;
uint16_t delay;
queueReceive(&changeBitRate, time, delay);
printf("Disabling CANopen for givent time");
pauseReceiveThread();
delayUntil(time + delay);
CO_CANsetBitrate(CO->CANmodule[0], pendingBit);
delay(delay);
resumeReceiveThread();
printf("Re-enabling CANopen after bit rate switch");
}
}
}
* \endcode
*/
/**
* LSS slave object.
*/
typedef struct{
CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */
CO_LSS_state_t lssState; /**< #CO_LSS_state_t */
CO_LSS_address_t lssSelect; /**< Received LSS Address 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 in volatile memory */
uint8_t pendingNodeID; /**< Node ID that is temporarily configured in volatile memory */
uint8_t activeNodeID; /**< Node ID used at the CAN interface */
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate); /**< From CO_LSSslave_initCheckBitRateCallback() or NULL */
void *functLSScheckBitRateObject; /** Pointer to object */
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay); /**< From CO_LSSslave_initActivateBitRateCallback() or NULL. Delay is in ms */
void *functLSSactivateBitRateObject; /** Pointer to object */
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate); /**< From CO_LSSslave_initCfgStoreCallback() or NULL */
void *functLSScfgStore; /** Pointer to object */
CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */
CO_CANtx_t *TXbuff; /**< CAN transmit buffer */
}CO_LSSslave_t;
/**
* Initialize LSS object.
*
* Function must be called in the communication reset section.
*
* Depending on the startup type, pending bit rate and node ID have to be
* supplied differently. After #CO_NMT_RESET_NODE or at power up they should
* be restored from persitent bit rate and node id. After #CO_NMT_RESET_COMMUNICATION
* they have to be supplied from the application and are generally the values
* that have been last returned by #CO_LSSslave_process() before resetting.
*
* @remark The LSS address needs to be unique on the network. For this, the 128
* bit wide identity object (1018h) is used. Therefore, this object has to be fully
* initalized before passing it to this function.
*
* @param LSSslave This object will be initialized.
* @param lssAddress LSS address
* @param pendingBitRate Bit rate of the CAN interface.
* @param pendingNodeID Node ID or 0xFF - invalid.
* @param CANdevRx CAN device for LSS slave reception.
* @param CANdevRxIdx Index of receive buffer in the above CAN device.
* @param CANidLssMaster COB ID for reception.
* @param CANdevTx CAN device for LSS slave transmission.
* @param CANdevTxIdx Index of transmit buffer in the above CAN device.
* @param CANidLssSlave COB ID for transmission.
* @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT.
*/
CO_ReturnError_t CO_LSSslave_init(
CO_LSSslave_t *LSSslave,
CO_LSS_address_t lssAddress,
uint16_t pendingBitRate,
uint8_t pendingNodeID,
CO_CANmodule_t *CANdevRx,
uint16_t CANdevRxIdx,
uint32_t CANidLssMaster,
CO_CANmodule_t *CANdevTx,
uint16_t CANdevTxIdx,
uint32_t CANidLssSlave);
/**
* Process LSS communication
*
* - sets currently active node ID and bit rate so master can read it
* - hands over pending node ID and bit rate to user application
*
* @param LSSslave This object.
* @param activeBitRate Currently active bit rate
* @param activeNodeId Currently active node ID
* @param pendingBitRate [out] Requested bit rate
* @param pendingNodeId [out] Requested node id
*/
void CO_LSSslave_process(
CO_LSSslave_t *LSSslave,
uint16_t activeBitRate,
uint8_t activeNodeId,
uint16_t *pendingBitRate,
uint8_t *pendingNodeId);
/**
* Get current LSS state
*
* @param LSSslave This object.
* @return #CO_LSS_state_t
*/
CO_LSS_state_t CO_LSSslave_getState(
CO_LSSslave_t *LSSslave);
/**
* Process LSS LED
*
* Returns the status of the LSS LED (if LSS is involved)
* with the following meaning:
*
* UNCONFIGURED (activeNodeId is unconfigured) --> single flash
* SELECTED --> double flash
*
* If none of above conditions apply, returns false.
*
* @param LSSslave This object.
* @param timeDifference_ms The amount of time elapsed since the last call
* @param LEDon [out] LED state
*
* @return true if LSS is involved (unconfigured node or selected node)
*/
bool_t CO_LSSslave_LEDprocess(
CO_LSSslave_t *LSSslave,
uint16_t timeDifference_ms,
bool_t *LEDon);
/**
* Initialize verify bit rate callback
*
* Function initializes callback function, which is called when "config bit
* timing parameters" is used. The callback function needs to check if the new bit
* rate is supported by the CANopen device. Callback returns "true" if supported.
* When no callback is set the LSS server will no-ack the request, indicating to
* the master that bit rate change is not supported.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param LSSslave This object.
* @param object Pointer to object, which will be passed to pFunctLSScheckBitRate(). Can be NULL
* @param pFunctLSScheckBitRate Pointer to the callback function. Not called if NULL.
*/
void CO_LSSslave_initCheckBitRateCallback(
CO_LSSslave_t *LSSslave,
void *object,
bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate));
/**
* Initialize activate bit rate callback
*
* Function initializes callback function, which is called when "activate bit
* timing parameters" is used. The callback function gives the user an event to
* allow setting a timer or do calculations based on the exact time the request
* arrived.
* According to DSP 305 6.4.4, the delay has to be applied once before and once after
* switching bit rates. During this time, a device musn't send any messages.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param LSSslave This object.
* @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL
* @param pFunctLSSactivateBitRate Pointer to the callback function. Not called if NULL.
*/
void CO_LSSslave_initActivateBitRateCallback(
CO_LSSslave_t *LSSslave,
void *object,
void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay));
/**
* Store configuration callback
*
* Function initializes callback function, which is called when "store configuration" is used.
* The callback function gives the user an event to store the corresponding node id and bit rate
* to NVM. Those values have to be supplied to the init function as "persistent values"
* after reset. If callback returns "true", success is send to the LSS master. When no
* callback is set the LSS server will no-ack the request, indicating to the master
* that storing is not supported.
*
* @remark Depending on the CAN driver implementation, this function is called
* inside an ISR
*
* @param LSSslave This object.
* @param object Pointer to object, which will be passed to pFunctLSScfgStore(). Can be NULL
* @param pFunctLSScfgStore Pointer to the callback function. Not called if NULL.
*/
void CO_LSSslave_initCfgStoreCallback(
CO_LSSslave_t *LSSslave,
void *object,
bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate));
#else /* CO_NO_LSS_SERVER == 1 */
/**
* @addtogroup CO_LSS
* @{
* If you need documetation for LSS slave usage, add "CO_NO_LSS_SERVER=1" to doxygen
* "PREDEFINED" variable.
*
*/
#endif /* CO_NO_LSS_SERVER == 1 */
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/** @} */
#endif