1 /*
2 * CANopen NMT and Heartbeat producer object.
3 *
4 * @file CO_NMT_Heartbeat.c
5 * @ingroup CO_NMT_Heartbeat
6 * @author Janez Paternoster
7 * @copyright 2004 - 2020 Janez Paternoster
8 *
9 * This file is part of CANopenNode, an opensource CANopen Stack.
10 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
11 * For more information on CANopen see <http://www.can-cia.org/>.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 */
25
26
27 #include "CO_driver.h"
28 #include "CO_SDO.h"
29 #include "CO_Emergency.h"
30 #include "CO_NMT_Heartbeat.h"
31
32 /*
33 * Read received message from CAN module.
34 *
35 * Function will be called (by CAN receive interrupt) every time, when CAN
36 * message with correct identifier will be received. For more information and
37 * description of parameters see file CO_driver.h.
38 */
CO_NMT_receive(void * object,const CO_CANrxMsg_t * msg)39 static void CO_NMT_receive(void *object, const CO_CANrxMsg_t *msg){
40 CO_NMT_t *NMT;
41 uint8_t nodeId;
42
43 NMT = (CO_NMT_t*)object; /* this is the correct pointer type of the first argument */
44
45 nodeId = msg->data[1];
46
47 if((msg->DLC == 2) && ((nodeId == 0) || (nodeId == NMT->nodeId))){
48 uint8_t command = msg->data[0];
49 uint8_t currentOperatingState = NMT->operatingState;
50
51 switch(command){
52 case CO_NMT_ENTER_OPERATIONAL:
53 if((*NMT->emPr->errorRegister) == 0U){
54 NMT->operatingState = CO_NMT_OPERATIONAL;
55 }
56 break;
57 case CO_NMT_ENTER_STOPPED:
58 NMT->operatingState = CO_NMT_STOPPED;
59 break;
60 case CO_NMT_ENTER_PRE_OPERATIONAL:
61 NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
62 break;
63 case CO_NMT_RESET_NODE:
64 NMT->resetCommand = CO_RESET_APP;
65 break;
66 case CO_NMT_RESET_COMMUNICATION:
67 NMT->resetCommand = CO_RESET_COMM;
68 break;
69 default:
70 break;
71 }
72
73 if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){
74 NMT->pFunctNMT(NMT->operatingState);
75 }
76 }
77 }
78
79
80 /******************************************************************************/
CO_NMT_init(CO_NMT_t * NMT,CO_EMpr_t * emPr,uint8_t nodeId,uint16_t firstHBTime,CO_CANmodule_t * NMT_CANdev,uint16_t NMT_rxIdx,uint16_t CANidRxNMT,CO_CANmodule_t * HB_CANdev,uint16_t HB_txIdx,uint16_t CANidTxHB)81 CO_ReturnError_t CO_NMT_init(
82 CO_NMT_t *NMT,
83 CO_EMpr_t *emPr,
84 uint8_t nodeId,
85 uint16_t firstHBTime,
86 CO_CANmodule_t *NMT_CANdev,
87 uint16_t NMT_rxIdx,
88 uint16_t CANidRxNMT,
89 CO_CANmodule_t *HB_CANdev,
90 uint16_t HB_txIdx,
91 uint16_t CANidTxHB)
92 {
93 /* verify arguments */
94 if(NMT==NULL || emPr==NULL || NMT_CANdev==NULL || HB_CANdev==NULL){
95 return CO_ERROR_ILLEGAL_ARGUMENT;
96 }
97
98 /* blinking bytes */
99 #ifdef CO_USE_LEDS
100 NMT->LEDflickering = 0;
101 NMT->LEDblinking = 0;
102 NMT->LEDsingleFlash = 0;
103 NMT->LEDdoubleFlash = 0;
104 NMT->LEDtripleFlash = 0;
105 NMT->LEDquadrupleFlash = 0;
106 #endif /* CO_USE_LEDS */
107
108 /* Configure object variables */
109 NMT->operatingState = CO_NMT_INITIALIZING;
110 #ifdef CO_USE_LEDS
111 NMT->LEDgreenRun = -1;
112 NMT->LEDredError = 1;
113 #endif /* CO_USE_LEDS */
114 NMT->nodeId = nodeId;
115 NMT->firstHBTime = firstHBTime;
116 NMT->resetCommand = 0;
117 NMT->HBproducerTimer = 0xFFFF;
118 NMT->emPr = emPr;
119 NMT->pFunctNMT = NULL;
120
121 /* configure NMT CAN reception */
122 CO_CANrxBufferInit(
123 NMT_CANdev, /* CAN device */
124 NMT_rxIdx, /* rx buffer index */
125 CANidRxNMT, /* CAN identifier */
126 0x7FF, /* mask */
127 0, /* rtr */
128 (void*)NMT, /* object passed to receive function */
129 CO_NMT_receive); /* this function will process received message */
130
131 /* configure HB CAN transmission */
132 NMT->HB_CANdev = HB_CANdev;
133 NMT->HB_TXbuff = CO_CANtxBufferInit(
134 HB_CANdev, /* CAN device */
135 HB_txIdx, /* index of specific buffer inside CAN module */
136 CANidTxHB, /* CAN identifier */
137 0, /* rtr */
138 1, /* number of data bytes */
139 0); /* synchronous message flag bit */
140
141 return CO_ERROR_NO;
142 }
143
144
145 /******************************************************************************/
CO_NMT_initCallback(CO_NMT_t * NMT,void (* pFunctNMT)(CO_NMT_internalState_t state))146 void CO_NMT_initCallback(
147 CO_NMT_t *NMT,
148 void (*pFunctNMT)(CO_NMT_internalState_t state))
149 {
150 if(NMT != NULL){
151 NMT->pFunctNMT = pFunctNMT;
152 if(NMT->pFunctNMT != NULL){
153 NMT->pFunctNMT(NMT->operatingState);
154 }
155 }
156 }
157
158
159 /******************************************************************************/
160 #ifdef CO_USE_LEDS
CO_NMT_blinkingProcess50ms(CO_NMT_t * NMT)161 void CO_NMT_blinkingProcess50ms(CO_NMT_t *NMT){
162
163 if(++NMT->LEDflickering >= 1) NMT->LEDflickering = -1;
164
165 if(++NMT->LEDblinking >= 4) NMT->LEDblinking = -4;
166
167 if(++NMT->LEDsingleFlash >= 4) NMT->LEDsingleFlash = -20;
168
169 switch(++NMT->LEDdoubleFlash){
170 case 4: NMT->LEDdoubleFlash = -104; break;
171 case -100: NMT->LEDdoubleFlash = 100; break;
172 case 104: NMT->LEDdoubleFlash = -20; break;
173 default: break;
174 }
175
176 switch(++NMT->LEDtripleFlash){
177 case 4: NMT->LEDtripleFlash = -104; break;
178 case -100: NMT->LEDtripleFlash = 100; break;
179 case 104: NMT->LEDtripleFlash = -114; break;
180 case -110: NMT->LEDtripleFlash = 110; break;
181 case 114: NMT->LEDtripleFlash = -20; break;
182 default: break;
183 }
184
185 switch(++NMT->LEDquadrupleFlash){
186 case 4: NMT->LEDquadrupleFlash = -104; break;
187 case -100: NMT->LEDquadrupleFlash = 100; break;
188 case 104: NMT->LEDquadrupleFlash = -114; break;
189 case -110: NMT->LEDquadrupleFlash = 110; break;
190 case 114: NMT->LEDquadrupleFlash = -124; break;
191 case -120: NMT->LEDquadrupleFlash = 120; break;
192 case 124: NMT->LEDquadrupleFlash = -20; break;
193 default: break;
194 }
195 }
196 #endif /* CO_USE_LEDS */
197
198
199 /******************************************************************************/
CO_NMT_process(CO_NMT_t * NMT,uint16_t timeDifference_ms,uint16_t HBtime,uint32_t NMTstartup,uint8_t errorRegister,const uint8_t errorBehavior[],uint16_t * timerNext_ms)200 CO_NMT_reset_cmd_t CO_NMT_process(
201 CO_NMT_t *NMT,
202 uint16_t timeDifference_ms,
203 uint16_t HBtime,
204 uint32_t NMTstartup,
205 uint8_t errorRegister,
206 const uint8_t errorBehavior[],
207 uint16_t *timerNext_ms)
208 {
209 uint8_t CANpassive;
210
211 uint8_t currentOperatingState = NMT->operatingState;
212
213 NMT->HBproducerTimer += timeDifference_ms;
214
215 /* Heartbeat producer message & Bootup message */
216 if((HBtime != 0 && NMT->HBproducerTimer >= HBtime) || NMT->operatingState == CO_NMT_INITIALIZING){
217
218 /* Start from the beginning. If OS is slow, time sliding may occur. However, heartbeat is
219 * not for synchronization, it is for health report. */
220 NMT->HBproducerTimer = 0;
221
222 NMT->HB_TXbuff->data[0] = NMT->operatingState;
223 CO_CANsend(NMT->HB_CANdev, NMT->HB_TXbuff);
224
225 if(NMT->operatingState == CO_NMT_INITIALIZING){
226 if(HBtime > NMT->firstHBTime) NMT->HBproducerTimer = HBtime - NMT->firstHBTime;
227 else NMT->HBproducerTimer = 0;
228
229 /* NMT slave self starting */
230 if (NMTstartup == 0x00000008U) NMT->operatingState = CO_NMT_OPERATIONAL;
231 else NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
232 }
233 }
234
235
236 /* Calculate, when next Heartbeat needs to be send and lower timerNext_ms if necessary. */
237 if(HBtime != 0 && timerNext_ms != NULL){
238 if(NMT->HBproducerTimer < HBtime){
239 uint16_t diff = HBtime - NMT->HBproducerTimer;
240 if(*timerNext_ms > diff){
241 *timerNext_ms = diff;
242 }
243 }else{
244 *timerNext_ms = 0;
245 }
246 }
247
248
249 /* CAN passive flag */
250 CANpassive = 0;
251 if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_PASSIVE) || CO_isError(NMT->emPr->em, CO_EM_CAN_RX_BUS_PASSIVE))
252 CANpassive = 1;
253
254
255 #ifdef CO_USE_LEDS
256 /* CANopen green RUN LED (DR 303-3) */
257 switch(NMT->operatingState){
258 case CO_NMT_STOPPED: NMT->LEDgreenRun = NMT->LEDsingleFlash; break;
259 case CO_NMT_PRE_OPERATIONAL: NMT->LEDgreenRun = NMT->LEDblinking; break;
260 case CO_NMT_OPERATIONAL: NMT->LEDgreenRun = 1; break;
261 default: break;
262 }
263
264
265 /* CANopen red ERROR LED (DR 303-3) */
266 if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF))
267 NMT->LEDredError = 1;
268
269 else if(CO_isError(NMT->emPr->em, CO_EM_SYNC_TIME_OUT))
270 NMT->LEDredError = NMT->LEDtripleFlash;
271
272 else if(CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER) || CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET))
273 NMT->LEDredError = NMT->LEDdoubleFlash;
274
275 else if(CANpassive || CO_isError(NMT->emPr->em, CO_EM_CAN_BUS_WARNING))
276 NMT->LEDredError = NMT->LEDsingleFlash;
277
278 else if(errorRegister)
279 NMT->LEDredError = (NMT->LEDblinking>=0)?-1:1;
280
281 else
282 NMT->LEDredError = -1;
283 #endif /* CO_USE_LEDS */
284
285
286 /* in case of error enter pre-operational state */
287 if(errorBehavior && (NMT->operatingState == CO_NMT_OPERATIONAL)){
288 if(CANpassive && (errorBehavior[2] == 0 || errorBehavior[2] == 2)) errorRegister |= 0x10;
289
290 if(errorRegister){
291 /* Communication error */
292 if(errorRegister & CO_ERR_REG_COMM_ERR){
293 if(errorBehavior[1] == 0){
294 NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
295 }
296 else if(errorBehavior[1] == 2){
297 NMT->operatingState = CO_NMT_STOPPED;
298 }
299 else if(CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF)
300 || CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER)
301 || CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET))
302 {
303 if(errorBehavior[0] == 0){
304 NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
305 }
306 else if(errorBehavior[0] == 2){
307 NMT->operatingState = CO_NMT_STOPPED;
308 }
309 }
310 }
311
312 /* Generic error */
313 if(errorRegister & CO_ERR_REG_GENERIC_ERR){
314 if (errorBehavior[3] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
315 else if (errorBehavior[3] == 2) NMT->operatingState = CO_NMT_STOPPED;
316 }
317
318 /* Device profile error */
319 if(errorRegister & CO_ERR_REG_DEV_PROFILE){
320 if (errorBehavior[4] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
321 else if (errorBehavior[4] == 2) NMT->operatingState = CO_NMT_STOPPED;
322 }
323
324 /* Manufacturer specific error */
325 if(errorRegister & CO_ERR_REG_MANUFACTURER){
326 if (errorBehavior[5] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
327 else if (errorBehavior[5] == 2) NMT->operatingState = CO_NMT_STOPPED;
328 }
329
330 /* if operational state is lost, send HB immediately. */
331 if(NMT->operatingState != CO_NMT_OPERATIONAL)
332 NMT->HBproducerTimer = HBtime;
333 }
334 }
335
336 if(NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){
337 NMT->pFunctNMT(NMT->operatingState);
338 }
339
340 return NMT->resetCommand;
341 }
342
343
344 /******************************************************************************/
CO_NMT_getInternalState(CO_NMT_t * NMT)345 CO_NMT_internalState_t CO_NMT_getInternalState(
346 CO_NMT_t *NMT)
347 {
348 if(NMT != NULL){
349 return NMT->operatingState;
350 }
351 return CO_NMT_INITIALIZING;
352 }
353
CO_NMT_setInternalState(CO_NMT_t * NMT,CO_NMT_internalState_t state)354 void CO_NMT_setInternalState(
355 CO_NMT_t *NMT,
356 CO_NMT_internalState_t state)
357 {
358 NMT->operatingState = state;
359 }
360