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 
354