1 /** 2 * CANopen LSS Master/Slave protocol. 3 * 4 * @file CO_LSSslave.h 5 * @ingroup CO_LSS 6 * @author Martin Wagner 7 * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH 8 * 9 * 10 * This file is part of CANopenNode, an opensource CANopen Stack. 11 * Project home page is <https://github.com/CANopenNode/CANopenNode>. 12 * For more information on CANopen see <http://www.can-cia.org/>. 13 * 14 * Licensed under the Apache License, Version 2.0 (the "License"); 15 * you may not use this file except in compliance with the License. 16 * You may obtain a copy of the License at 17 * 18 * http://www.apache.org/licenses/LICENSE-2.0 19 * 20 * Unless required by applicable law or agreed to in writing, software 21 * distributed under the License is distributed on an "AS IS" BASIS, 22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 * See the License for the specific language governing permissions and 24 * limitations under the License. 25 */ 26 27 28 #ifndef CO_LSSslave_H 29 #define CO_LSSslave_H 30 31 #ifdef __cplusplus 32 extern "C" { 33 #endif 34 35 #if CO_NO_LSS_SERVER == 1 36 37 #include "CO_LSS.h" 38 39 /** 40 * @addtogroup CO_LSS 41 * @defgroup CO_LSSslave LSS Slave 42 * @ingroup CO_LSS 43 * @{ 44 * 45 * CANopen Layer Setting Service - server protocol 46 * 47 * The server/slave provides the following services 48 * - node selection via LSS address 49 * - node selection via LSS fastscan 50 * - Inquire LSS address of currently selected node 51 * - Inquire node ID 52 * - Configure bit timing 53 * - Configure node ID 54 * - Activate bit timing parameters 55 * - Store configuration (bit rate and node ID) 56 * 57 * After CAN module start, the LSS server and NMT server are started and then 58 * coexist alongside each other. To achieve this behaviour, the CANopen node 59 * startup process has to be conrolled more detailled. Therefore, the function 60 * CO_init() is split up into the functions #CO_new(), #CO_CANinit(), #CO_LSSinit() 61 * and #CO_CANopenInit(). 62 * Moreover, the LSS server needs to pause the NMT server initialization in case 63 * no valid node ID is available at start up. 64 * 65 * ###Example 66 * 67 * It is strongly recommended that the user already has a fully working application 68 * running with the standard (non LSS) version of CANopenNode. This is required 69 * to understand what this example does and where you need to change it for your 70 * requirements. 71 * 72 * The following code is only a suggestion on how to use the LSS server. It is 73 * not a working example! To simplify the code, no error handling is 74 * included. For stable code, proper error handling has to be added to the user 75 * code. 76 * 77 * This example is not intended for bare metal targets. If you intend to do CAN 78 * message receiving inside interrupt, be aware that the callback functions 79 * will be called inside the interrupt handler context! 80 * 81 * \code{.c} 82 83 const uint16_t FIRST_BIT = 125; 84 queue changeBitRate; 85 uint8_t activeNid; 86 uint16_t activeBit; 87 88 bool_t checkBitRateCallback(void *object, uint16_t bitRate) 89 { 90 if (validBit(bitRate)) { 91 return true; 92 } 93 return false; 94 } 95 96 void activateBitRateCallback(void *object, uint16_t delay) 97 { 98 int time = getCurrentTime(); 99 queueSend(&changeBitRate, time, delay); 100 } 101 102 bool_t cfgStoreCallback(void *object, uint8_t id, uint16_t bitRate) 103 { 104 savePersistent(id, bitRate); 105 return true; 106 } 107 108 void start_canopen(uint8_t nid) 109 { 110 uint8_t persistentNid; 111 uint8_t pendingNid; 112 uint16_t persistentBit; 113 uint16_t pendingBit; 114 115 loadPersistent(&persistentNid, &persistentBit); 116 117 if ( ! validBit(persistentBit)) { 118 printf("no bit rate found, defaulting to %d", FIRST_BIT); 119 pendingBit = FIRST_BIT; 120 } 121 else { 122 printf("loaded bit rate from nvm: %d", persistentBit); 123 pendingBit = persistentBit; 124 } 125 126 if (nid == 0) { 127 if ( ! validNid(persistentNid)) { 128 pendingNid = CO_LSS_NODE_ID_ASSIGNMENT; 129 printf("no node id found, needs to be set by LSS. NMT will" 130 "not be started until valid node id is set"); 131 } 132 else { 133 printf("loaded node id from nvm: %d", persistentNid); 134 pendingNid = persistentNid; 135 } 136 } 137 else { 138 printf("node id provided by application: %d", nid); 139 pendingNid = nid; 140 } 141 142 CO_new(); 143 CO_CANinit(0, pendingBit); 144 CO_LSSinit(pendingNid, pendingBit); 145 CO_CANsetNormalMode(CO->CANmodule[0]); 146 activeBit = pendingBit; 147 148 CO_LSSslave_initCheckBitRateCallback(CO->LSSslave, NULL, checkBitRateCallback); 149 CO_LSSslave_initActivateBitRateCallback(CO->LSSslave, NULL, activateBitRateCallback); 150 CO_LSSslave_initCfgStoreCallback(CO->LSSslave, NULL, cfgStoreCallback); 151 152 while (1) { 153 CO_LSSslave_process(CO->LSSslave, activeBit, activeNid, 154 &pendingBit, &pendingNid); 155 if (pendingNid!=CO_LSS_NODE_ID_ASSIGNMENT && 156 CO_LSSslave_getState(CO->LSSslave)==CO_LSS_STATE_WAITING) { 157 printf("node ID has been found: %d", pendingNid); 158 break; 159 } 160 161 if ( ! queueEmpty(&changeBitRate)) { 162 printf("bit rate change requested: %d", pendingBit); 163 int time; 164 uint16_t delay; 165 queueReceive(&changeBitRate, time, delay); 166 delayUntil(time + delay); 167 CO_CANsetBitrate(CO->CANmodule[0], pendingBit); 168 delay(delay); 169 } 170 171 printf("waiting for node id"); 172 CO_CANrxWait(CO->CANmodule[0]); 173 } 174 175 CO_CANopenInit(pendingNid); 176 activeNid = pendingNid; 177 178 printf("from this on, initialization doesn't differ to non-LSS version" 179 "You can now intialize your CO_CANrxWait() thread or interrupt"); 180 } 181 182 void main(void) 183 { 184 uint8_t pendingNid; 185 uint16_t pendingBit; 186 187 printf("like example in dir \"example\""); 188 189 CO_NMT_reset_cmd_t reset = CO_RESET_NOT; 190 uint16_t timer1msPrevious; 191 192 start_canopen(0); 193 194 reset = CO_RESET_NOT; 195 timer1msPrevious = CO_timer1ms; 196 while(reset == CO_RESET_NOT){ 197 printf("loop for normal program execution"); 198 uint16_t timer1msCopy, timer1msDiff; 199 200 timer1msCopy = CO_timer1ms; 201 timer1msDiff = timer1msCopy - timer1msPrevious; 202 timer1msPrevious = timer1msCopy; 203 204 reset = CO_process(CO, timer1msDiff, NULL); 205 206 CO_LSSslave_process(CO->LSSslave, activeBit, activeNid, 207 &pendingBit, &pendingNid); 208 if (reset == CO_RESET_COMM) { 209 printf("restarting CANopen using pending node ID %d", pendingNid); 210 CO_delete(0); 211 start_canopen(pendingNid); 212 reset = CO_RESET_NOT; 213 } 214 if ( ! queueEmpty(&changeBitRate)) { 215 printf("bit rate change requested: %d", pendingBit); 216 int time; 217 uint16_t delay; 218 queueReceive(&changeBitRate, time, delay); 219 printf("Disabling CANopen for givent time"); 220 pauseReceiveThread(); 221 delayUntil(time + delay); 222 CO_CANsetBitrate(CO->CANmodule[0], pendingBit); 223 delay(delay); 224 resumeReceiveThread(); 225 printf("Re-enabling CANopen after bit rate switch"); 226 } 227 } 228 } 229 230 * \endcode 231 */ 232 233 /** 234 * LSS slave object. 235 */ 236 typedef struct{ 237 CO_LSS_address_t lssAddress; /**< From #CO_LSSslave_init */ 238 CO_LSS_state_t lssState; /**< #CO_LSS_state_t */ 239 CO_LSS_address_t lssSelect; /**< Received LSS Address by select */ 240 241 CO_LSS_address_t lssFastscan; /**< Received LSS Address by fastscan */ 242 uint8_t fastscanPos; /**< Current state of fastscan */ 243 244 uint16_t pendingBitRate; /**< Bit rate value that is temporarily configured in volatile memory */ 245 uint8_t pendingNodeID; /**< Node ID that is temporarily configured in volatile memory */ 246 uint8_t activeNodeID; /**< Node ID used at the CAN interface */ 247 248 bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate); /**< From CO_LSSslave_initCheckBitRateCallback() or NULL */ 249 void *functLSScheckBitRateObject; /** Pointer to object */ 250 void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay); /**< From CO_LSSslave_initActivateBitRateCallback() or NULL. Delay is in ms */ 251 void *functLSSactivateBitRateObject; /** Pointer to object */ 252 bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate); /**< From CO_LSSslave_initCfgStoreCallback() or NULL */ 253 void *functLSScfgStore; /** Pointer to object */ 254 255 CO_CANmodule_t *CANdevTx; /**< From #CO_LSSslave_init() */ 256 CO_CANtx_t *TXbuff; /**< CAN transmit buffer */ 257 }CO_LSSslave_t; 258 259 /** 260 * Initialize LSS object. 261 * 262 * Function must be called in the communication reset section. 263 * 264 * Depending on the startup type, pending bit rate and node ID have to be 265 * supplied differently. After #CO_NMT_RESET_NODE or at power up they should 266 * be restored from persitent bit rate and node id. After #CO_NMT_RESET_COMMUNICATION 267 * they have to be supplied from the application and are generally the values 268 * that have been last returned by #CO_LSSslave_process() before resetting. 269 * 270 * @remark The LSS address needs to be unique on the network. For this, the 128 271 * bit wide identity object (1018h) is used. Therefore, this object has to be fully 272 * initalized before passing it to this function. 273 * 274 * @param LSSslave This object will be initialized. 275 * @param lssAddress LSS address 276 * @param pendingBitRate Bit rate of the CAN interface. 277 * @param pendingNodeID Node ID or 0xFF - invalid. 278 * @param CANdevRx CAN device for LSS slave reception. 279 * @param CANdevRxIdx Index of receive buffer in the above CAN device. 280 * @param CANidLssMaster COB ID for reception. 281 * @param CANdevTx CAN device for LSS slave transmission. 282 * @param CANdevTxIdx Index of transmit buffer in the above CAN device. 283 * @param CANidLssSlave COB ID for transmission. 284 * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT. 285 */ 286 CO_ReturnError_t CO_LSSslave_init( 287 CO_LSSslave_t *LSSslave, 288 CO_LSS_address_t lssAddress, 289 uint16_t pendingBitRate, 290 uint8_t pendingNodeID, 291 CO_CANmodule_t *CANdevRx, 292 uint16_t CANdevRxIdx, 293 uint32_t CANidLssMaster, 294 CO_CANmodule_t *CANdevTx, 295 uint16_t CANdevTxIdx, 296 uint32_t CANidLssSlave); 297 298 /** 299 * Process LSS communication 300 * 301 * - sets currently active node ID and bit rate so master can read it 302 * - hands over pending node ID and bit rate to user application 303 * 304 * @param LSSslave This object. 305 * @param activeBitRate Currently active bit rate 306 * @param activeNodeId Currently active node ID 307 * @param pendingBitRate [out] Requested bit rate 308 * @param pendingNodeId [out] Requested node id 309 */ 310 void CO_LSSslave_process( 311 CO_LSSslave_t *LSSslave, 312 uint16_t activeBitRate, 313 uint8_t activeNodeId, 314 uint16_t *pendingBitRate, 315 uint8_t *pendingNodeId); 316 317 /** 318 * Get current LSS state 319 * 320 * @param LSSslave This object. 321 * @return #CO_LSS_state_t 322 */ 323 CO_LSS_state_t CO_LSSslave_getState( 324 CO_LSSslave_t *LSSslave); 325 326 /** 327 * Process LSS LED 328 * 329 * Returns the status of the LSS LED (if LSS is involved) 330 * with the following meaning: 331 * 332 * UNCONFIGURED (activeNodeId is unconfigured) --> single flash 333 * SELECTED --> double flash 334 * 335 * If none of above conditions apply, returns false. 336 * 337 * @param LSSslave This object. 338 * @param timeDifference_ms The amount of time elapsed since the last call 339 * @param LEDon [out] LED state 340 * 341 * @return true if LSS is involved (unconfigured node or selected node) 342 */ 343 bool_t CO_LSSslave_LEDprocess( 344 CO_LSSslave_t *LSSslave, 345 uint16_t timeDifference_ms, 346 bool_t *LEDon); 347 348 /** 349 * Initialize verify bit rate callback 350 * 351 * Function initializes callback function, which is called when "config bit 352 * timing parameters" is used. The callback function needs to check if the new bit 353 * rate is supported by the CANopen device. Callback returns "true" if supported. 354 * When no callback is set the LSS server will no-ack the request, indicating to 355 * the master that bit rate change is not supported. 356 * 357 * @remark Depending on the CAN driver implementation, this function is called 358 * inside an ISR 359 * 360 * @param LSSslave This object. 361 * @param object Pointer to object, which will be passed to pFunctLSScheckBitRate(). Can be NULL 362 * @param pFunctLSScheckBitRate Pointer to the callback function. Not called if NULL. 363 */ 364 void CO_LSSslave_initCheckBitRateCallback( 365 CO_LSSslave_t *LSSslave, 366 void *object, 367 bool_t (*pFunctLSScheckBitRate)(void *object, uint16_t bitRate)); 368 369 /** 370 * Initialize activate bit rate callback 371 * 372 * Function initializes callback function, which is called when "activate bit 373 * timing parameters" is used. The callback function gives the user an event to 374 * allow setting a timer or do calculations based on the exact time the request 375 * arrived. 376 * According to DSP 305 6.4.4, the delay has to be applied once before and once after 377 * switching bit rates. During this time, a device musn't send any messages. 378 * 379 * @remark Depending on the CAN driver implementation, this function is called 380 * inside an ISR 381 * 382 * @param LSSslave This object. 383 * @param object Pointer to object, which will be passed to pFunctLSSactivateBitRate(). Can be NULL 384 * @param pFunctLSSactivateBitRate Pointer to the callback function. Not called if NULL. 385 */ 386 void CO_LSSslave_initActivateBitRateCallback( 387 CO_LSSslave_t *LSSslave, 388 void *object, 389 void (*pFunctLSSactivateBitRate)(void *object, uint16_t delay)); 390 391 /** 392 * Store configuration callback 393 * 394 * Function initializes callback function, which is called when "store configuration" is used. 395 * The callback function gives the user an event to store the corresponding node id and bit rate 396 * to NVM. Those values have to be supplied to the init function as "persistent values" 397 * after reset. If callback returns "true", success is send to the LSS master. When no 398 * callback is set the LSS server will no-ack the request, indicating to the master 399 * that storing is not supported. 400 * 401 * @remark Depending on the CAN driver implementation, this function is called 402 * inside an ISR 403 * 404 * @param LSSslave This object. 405 * @param object Pointer to object, which will be passed to pFunctLSScfgStore(). Can be NULL 406 * @param pFunctLSScfgStore Pointer to the callback function. Not called if NULL. 407 */ 408 void CO_LSSslave_initCfgStoreCallback( 409 CO_LSSslave_t *LSSslave, 410 void *object, 411 bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate)); 412 413 #else /* CO_NO_LSS_SERVER == 1 */ 414 415 /** 416 * @addtogroup CO_LSS 417 * @{ 418 * If you need documetation for LSS slave usage, add "CO_NO_LSS_SERVER=1" to doxygen 419 * "PREDEFINED" variable. 420 * 421 */ 422 423 #endif /* CO_NO_LSS_SERVER == 1 */ 424 425 #ifdef __cplusplus 426 } 427 #endif /*__cplusplus*/ 428 429 /** @} */ 430 #endif 431