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