1
0
Fork 0
CANopenNode/stack/CO_LSSslave.h
Martin Wagner 54f80b119b LSS slave: LSS startup did not confirm to CiA DSP 305 Figure 47
Added function and changed example code so that NMT boot-up is only started
after receiving LSS switch state global waiting. This also fixes problems
where doing CANopenNode initialization (-> change can HW Filter settings)
lead to missing the LSS switch state global waiting message.

However, now we do not fully comply to the specified sequence in 8.3.1.2
as we do all the startup stuff after switching to LSS waiting. This can't
be split in CANopenNode.
2018-12-14 09:04:49 +01:00

449 lines
16 KiB
C

/**
* CANopen LSS Master/Slave protocol.
*
* @file CO_LSSslave.h
* @ingroup CO_LSS
* @author Martin Wagner
* @copyright 2017 Neuberger Gebäudeautomation GmbH
*
* This file is part of CANopenNode, an opensource CANopen Stack.
* Project home page is <https://github.com/CANopenNode/CANopenNode>.
* For more information on CANopen see <http://www.can-cia.org/>.
*
* CANopenNode is free and open source software: you can redistribute
* it and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Following clarification and special exception to the GNU General Public
* License is included to the distribution terms of CANopenNode:
*
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole combination.
*
* As a special exception, the copyright holders of this library give
* you permission to link this library with independent modules to
* produce an executable, regardless of the license terms of these
* independent modules, and to copy and distribute the resulting
* executable under terms of your choice, provided that you also meet,
* for each linked independent module, the terms and conditions of the
* license of that module. An independent module is a module which is
* not derived from or based on this library. If you modify this
* library, you may extend this exception to your version of the
* library, but you are not obliged to do so. If you do not wish
* to do so, delete this exception statement from your version.
*/
#ifndef CO_LSSslave_H
#define CO_LSSslave_H
#ifdef __cplusplus
extern "C" {
#endif
#if CO_NO_LSS_SERVER == 1
#include "CO_LSS.h"
/**
* @addtogroup CO_LSS
* @defgroup CO_LSSslave LSS Slave
* @ingroup CO_LSS
* @{
*
* CANopen Layer Setting Service - server protocol
*
* The server/slave provides the following services
* - node selection via LSS address
* - node selection via LSS fastscan
* - Inquire LSS address of currently selected node
* - Inquire node ID
* - Configure bit timing
* - Configure node ID
* - Activate bit timing parameters
* - Store configuration (bit rate and node ID)
*
* After CAN module start, the LSS server and NMT server are started and then
* coexist alongside each other. To achieve this behaviour, the CANopen node
* startup process has to be conrolled more detailled. Therefore, the function
* CO_init() is split up into the functions #CO_new(), #CO_CANinit(), #CO_LSSinit()
* and #CO_CANopenInit().
* Moreover, the LSS server needs to pause the NMT server initialization in case
* no valid node ID is available at start up.
*
* ###Example
*
* It is strongly recommended that the user already has a fully working application
* running with the standard (non LSS) version of CANopenNode. This is required
* to understand what this example does and where you need to change it for your
* requirements.
*
* The following code is only a suggestion on how to use the LSS server. It is
* not a working example! To simplify the code, no error handling is
* included. For stable code, proper error handling has to be added to the user
* code.
*
* This example is not intended for bare metal targets. If you intend to do CAN
* message receiving inside interrupt, be aware that the callback functions
* will be called inside the interrupt handler context!
*
* \code{.c}
const uint16_t FIRST_BIT = 125;
queue changeBitRate;
uint8_t activeNid;
uint16_t activeBit;
bool_t checkBitRateCallback(void *object, uint16_t bitRate)
{
if (validBit(bitRate)) {
return true;
}
return false;
}
void activateBitRateCallback(void *object, uint16_t delay)
{
int time = getCurrentTime();
queueSend(&changeBitRate, time, delay);
}
bool_t cfgStoreCallback(void *object, uint8_t id, uint16_t bitRate)
{
savePersistent(id, bitRate);
return true;
}
void start_canopen(uint8_t nid)
{
uint8_t persistentNid;
uint8_t pendingNid;
uint16_t persistentBit;
uint16_t pendingBit;
loadPersistent(&persistentNid, &persistentBit);
if ( ! validBit(persistentBit)) {
printf("no bit rate found, defaulting to %d", FIRST_BIT);
pendingBit = FIRST_BIT;
}
else {
printf("loaded bit rate from nvm: %d", persistentBit);
pendingBit = persistentBit;
}
if (nid == 0) {
if ( ! validNid(persistentNid)) {
pendingNid = CO_LSS_NODE_ID_ASSIGNMENT;
printf("no node id found, needs to be set by LSS. NMT will"
"not be started until valid node id is set");
}
else {
printf("loaded node id from nvm: %d", persistentNid);
pendingNid = persistentNid;
}
}
else {
printf("node id provided by application: %d", nid);
pendingNid = nid;
}
CO_new();
CO_CANinit(0, pendingBit);
CO_LSSinit(pendingNid, pendingBit);
CO_CANsetNormalMode(CO->CANmodule[0]);
activeBit = pendingBit;
CO_LSSslave_initCheckBitRateCallback(CO->LSSslave, NULL, checkBitRateCallback);
CO_LSSslave_initActivateBitRateCallback(CO->LSSslave, NULL, activateBitRateCallback);
CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL, cfgStoreCallback);
while (1) {
CO_LSSslave_process(CO->LSSslave, activeBit, activeNid,
&pendingBit, &pendingNid);
if (pendingNid!=CO_LSS_NODE_ID_ASSIGNMENT &&
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