1 /*
2  * CANopen SYNC object.
3  *
4  * @file        CO_SYNC.c
5  * @ingroup     CO_SYNC
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 #include "CO_SYNC.h"
32 
33 /*
34  * Read received message from CAN module.
35  *
36  * Function will be called (by CAN receive interrupt) every time, when CAN
37  * message with correct identifier will be received. For more information and
38  * description of parameters see file CO_driver.h.
39  */
CO_SYNC_receive(void * object,const CO_CANrxMsg_t * msg)40 static void CO_SYNC_receive(void *object, const CO_CANrxMsg_t *msg){
41     CO_SYNC_t *SYNC;
42     uint8_t operState;
43 
44     SYNC = (CO_SYNC_t*)object;   /* this is the correct pointer type of the first argument */
45     operState = *SYNC->operatingState;
46 
47     if((operState == CO_NMT_OPERATIONAL) || (operState == CO_NMT_PRE_OPERATIONAL)){
48         if(SYNC->counterOverflowValue == 0){
49             if(msg->DLC == 0U){
50                 SET_CANrxNew(SYNC->CANrxNew);
51             }
52             else{
53                 SYNC->receiveError = (uint16_t)msg->DLC | 0x0100U;
54             }
55         }
56         else{
57             if(msg->DLC == 1U){
58                 SYNC->counter = msg->data[0];
59                 SET_CANrxNew(SYNC->CANrxNew);
60             }
61             else{
62                 SYNC->receiveError = (uint16_t)msg->DLC | 0x0200U;
63             }
64         }
65         if(IS_CANrxNew(SYNC->CANrxNew)) {
66             SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true;
67         }
68     }
69 }
70 
71 
72 /*
73  * Function for accessing _COB ID SYNC Message_ (index 0x1005) from SDO server.
74  *
75  * For more information see file CO_SDO.h.
76  */
CO_ODF_1005(CO_ODF_arg_t * ODF_arg)77 static CO_SDO_abortCode_t CO_ODF_1005(CO_ODF_arg_t *ODF_arg){
78     CO_SYNC_t *SYNC;
79     uint32_t value;
80     CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
81 
82     SYNC = (CO_SYNC_t*) ODF_arg->object;
83     value = CO_getUint32(ODF_arg->data);
84 
85     if(!ODF_arg->reading){
86         uint8_t configureSyncProducer = 0;
87 
88         /* only 11-bit CAN identifier is supported */
89         if(value & 0x20000000UL){
90             ret = CO_SDO_AB_INVALID_VALUE;
91         }
92         else{
93             /* is 'generate Sync messge' bit set? */
94             if(value & 0x40000000UL){
95                 /* if bit was set before, value can not be changed */
96                 if(SYNC->isProducer){
97                     ret = CO_SDO_AB_DATA_DEV_STATE;
98                 }
99                 else{
100                     configureSyncProducer = 1;
101                 }
102             }
103         }
104 
105         /* configure sync producer and consumer */
106         if(ret == CO_SDO_AB_NONE){
107             SYNC->COB_ID = (uint16_t)(value & 0x7FFU);
108 
109             if(configureSyncProducer){
110                 uint8_t len = 0U;
111                 if(SYNC->counterOverflowValue != 0U){
112                     len = 1U;
113                     SYNC->counter = 0U;
114                     SYNC->timer = 0U;
115                 }
116                 SYNC->CANtxBuff = CO_CANtxBufferInit(
117                         SYNC->CANdevTx,         /* CAN device */
118                         SYNC->CANdevTxIdx,      /* index of specific buffer inside CAN module */
119                         SYNC->COB_ID,           /* CAN identifier */
120                         0,                      /* rtr */
121                         len,                    /* number of data bytes */
122                         0);                     /* synchronous message flag bit */
123                 SYNC->isProducer = true;
124             }
125             else{
126                 SYNC->isProducer = false;
127             }
128 
129             CO_CANrxBufferInit(
130                     SYNC->CANdevRx,         /* CAN device */
131                     SYNC->CANdevRxIdx,      /* rx buffer index */
132                     SYNC->COB_ID,           /* CAN identifier */
133                     0x7FF,                  /* mask */
134                     0,                      /* rtr */
135                     (void*)SYNC,            /* object passed to receive function */
136                     CO_SYNC_receive);       /* this function will process received message */
137         }
138     }
139 
140     return ret;
141 }
142 
143 
144 /*
145  * Function for accessing _Communication cycle period_ (index 0x1006) from SDO server.
146  *
147  * For more information see file CO_SDO.h.
148  */
CO_ODF_1006(CO_ODF_arg_t * ODF_arg)149 static CO_SDO_abortCode_t CO_ODF_1006(CO_ODF_arg_t *ODF_arg){
150     CO_SYNC_t *SYNC;
151     uint32_t value;
152     CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
153 
154     SYNC = (CO_SYNC_t*) ODF_arg->object;
155     value = CO_getUint32(ODF_arg->data);
156 
157     if(!ODF_arg->reading){
158         /* period transition from 0 to something */
159         if((SYNC->periodTime == 0) && (value != 0)){
160             SYNC->counter = 0;
161         }
162 
163         SYNC->periodTime = value;
164         SYNC->periodTimeoutTime = (value / 2U) * 3U;
165         /* overflow? */
166         if(SYNC->periodTimeoutTime < value){
167             SYNC->periodTimeoutTime = 0xFFFFFFFFUL;
168         }
169 
170         SYNC->timer = 0;
171     }
172 
173     return ret;
174 }
175 
176 
177 /**
178  * Function for accessing _Synchronous counter overflow value_ (index 0x1019) from SDO server.
179  *
180  * For more information see file CO_SDO.h.
181  */
CO_ODF_1019(CO_ODF_arg_t * ODF_arg)182 static CO_SDO_abortCode_t CO_ODF_1019(CO_ODF_arg_t *ODF_arg){
183     CO_SYNC_t *SYNC;
184     uint8_t value;
185     CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
186 
187     SYNC = (CO_SYNC_t*) ODF_arg->object;
188     value = ODF_arg->data[0];
189 
190     if(!ODF_arg->reading){
191         uint8_t len = 0U;
192 
193         if(SYNC->periodTime){
194             ret = CO_SDO_AB_DATA_DEV_STATE;
195         }
196         else if((value == 1) || (value > 240)){
197             ret = CO_SDO_AB_INVALID_VALUE;
198         }
199         else{
200             SYNC->counterOverflowValue = value;
201             if(value != 0){
202                 len = 1U;
203             }
204 
205             SYNC->CANtxBuff = CO_CANtxBufferInit(
206                     SYNC->CANdevTx,         /* CAN device */
207                     SYNC->CANdevTxIdx,      /* index of specific buffer inside CAN module */
208                     SYNC->COB_ID,           /* CAN identifier */
209                     0,                      /* rtr */
210                     len,                    /* number of data bytes */
211                     0);                     /* synchronous message flag bit */
212         }
213     }
214 
215     return ret;
216 }
217 
218 
219 /******************************************************************************/
CO_SYNC_init(CO_SYNC_t * SYNC,CO_EM_t * em,CO_SDO_t * SDO,uint8_t * operatingState,uint32_t COB_ID_SYNCMessage,uint32_t communicationCyclePeriod,uint8_t synchronousCounterOverflowValue,CO_CANmodule_t * CANdevRx,uint16_t CANdevRxIdx,CO_CANmodule_t * CANdevTx,uint16_t CANdevTxIdx)220 CO_ReturnError_t CO_SYNC_init(
221         CO_SYNC_t              *SYNC,
222         CO_EM_t                *em,
223         CO_SDO_t               *SDO,
224         uint8_t                *operatingState,
225         uint32_t                COB_ID_SYNCMessage,
226         uint32_t                communicationCyclePeriod,
227         uint8_t                 synchronousCounterOverflowValue,
228         CO_CANmodule_t         *CANdevRx,
229         uint16_t                CANdevRxIdx,
230         CO_CANmodule_t         *CANdevTx,
231         uint16_t                CANdevTxIdx)
232 {
233     uint8_t len = 0;
234 
235     /* verify arguments */
236     if(SYNC==NULL || em==NULL || SDO==NULL || operatingState==NULL ||
237         CANdevRx==NULL || CANdevTx==NULL){
238         return CO_ERROR_ILLEGAL_ARGUMENT;
239     }
240 
241     /* Configure object variables */
242     SYNC->isProducer = (COB_ID_SYNCMessage&0x40000000L) ? true : false;
243     SYNC->COB_ID = COB_ID_SYNCMessage&0x7FF;
244 
245     SYNC->periodTime = communicationCyclePeriod;
246     SYNC->periodTimeoutTime = communicationCyclePeriod / 2 * 3;
247     /* overflow? */
248     if(SYNC->periodTimeoutTime < communicationCyclePeriod) SYNC->periodTimeoutTime = 0xFFFFFFFFL;
249 
250     SYNC->counterOverflowValue = synchronousCounterOverflowValue;
251     if(synchronousCounterOverflowValue) len = 1;
252 
253     SYNC->curentSyncTimeIsInsideWindow = true;
254 
255     CLEAR_CANrxNew(SYNC->CANrxNew);
256     SYNC->CANrxToggle = false;
257     SYNC->timer = 0;
258     SYNC->counter = 0;
259     SYNC->receiveError = 0U;
260 
261     SYNC->em = em;
262     SYNC->operatingState = operatingState;
263 
264     SYNC->CANdevRx = CANdevRx;
265     SYNC->CANdevRxIdx = CANdevRxIdx;
266 
267     /* Configure Object dictionary entry at index 0x1005, 0x1006 and 0x1019 */
268     CO_OD_configure(SDO, OD_H1005_COBID_SYNC,        CO_ODF_1005, (void*)SYNC, 0, 0);
269     CO_OD_configure(SDO, OD_H1006_COMM_CYCL_PERIOD,  CO_ODF_1006, (void*)SYNC, 0, 0);
270     CO_OD_configure(SDO, OD_H1019_SYNC_CNT_OVERFLOW, CO_ODF_1019, (void*)SYNC, 0, 0);
271 
272     /* configure SYNC CAN reception */
273     CO_CANrxBufferInit(
274             CANdevRx,               /* CAN device */
275             CANdevRxIdx,            /* rx buffer index */
276             SYNC->COB_ID,           /* CAN identifier */
277             0x7FF,                  /* mask */
278             0,                      /* rtr */
279             (void*)SYNC,            /* object passed to receive function */
280             CO_SYNC_receive);       /* this function will process received message */
281 
282     /* configure SYNC CAN transmission */
283     SYNC->CANdevTx = CANdevTx;
284     SYNC->CANdevTxIdx = CANdevTxIdx;
285     SYNC->CANtxBuff = CO_CANtxBufferInit(
286             CANdevTx,               /* CAN device */
287             CANdevTxIdx,            /* index of specific buffer inside CAN module */
288             SYNC->COB_ID,           /* CAN identifier */
289             0,                      /* rtr */
290             len,                    /* number of data bytes */
291             0);                     /* synchronous message flag bit */
292 
293     return CO_ERROR_NO;
294 }
295 
296 
297 /******************************************************************************/
CO_SYNC_process(CO_SYNC_t * SYNC,uint32_t timeDifference_us,uint32_t ObjDict_synchronousWindowLength)298 uint8_t CO_SYNC_process(
299         CO_SYNC_t              *SYNC,
300         uint32_t                timeDifference_us,
301         uint32_t                ObjDict_synchronousWindowLength)
302 {
303     uint8_t ret = 0;
304     uint32_t timerNew;
305 
306     if(*SYNC->operatingState == CO_NMT_OPERATIONAL || *SYNC->operatingState == CO_NMT_PRE_OPERATIONAL){
307         /* update sync timer, no overflow */
308         timerNew = SYNC->timer + timeDifference_us;
309         if(timerNew > SYNC->timer) SYNC->timer = timerNew;
310 
311         /* was SYNC just received */
312         if(IS_CANrxNew(SYNC->CANrxNew)){
313             SYNC->timer = 0;
314             ret = 1;
315             CLEAR_CANrxNew(SYNC->CANrxNew);
316         }
317 
318         /* SYNC producer */
319         if(SYNC->isProducer && SYNC->periodTime){
320             if(SYNC->timer >= SYNC->periodTime){
321                 if(++SYNC->counter > SYNC->counterOverflowValue) SYNC->counter = 1;
322                 SYNC->timer = 0;
323                 ret = 1;
324                 SYNC->CANrxToggle = SYNC->CANrxToggle ? false : true;
325                 SYNC->CANtxBuff->data[0] = SYNC->counter;
326                 CO_CANsend(SYNC->CANdevTx, SYNC->CANtxBuff);
327             }
328         }
329 
330         /* Synchronous PDOs are allowed only inside time window */
331         if(ObjDict_synchronousWindowLength){
332             if(SYNC->timer > ObjDict_synchronousWindowLength){
333                 if(SYNC->curentSyncTimeIsInsideWindow){
334                     ret = 2;
335                 }
336                 SYNC->curentSyncTimeIsInsideWindow = false;
337             }
338             else{
339                 SYNC->curentSyncTimeIsInsideWindow = true;
340             }
341         }
342         else{
343             SYNC->curentSyncTimeIsInsideWindow = true;
344         }
345 
346         /* Verify timeout of SYNC */
347         if(SYNC->periodTime && SYNC->timer > SYNC->periodTimeoutTime && *SYNC->operatingState == CO_NMT_OPERATIONAL)
348             CO_errorReport(SYNC->em, CO_EM_SYNC_TIME_OUT, CO_EMC_COMMUNICATION, SYNC->timer);
349     }
350     else {
351         CLEAR_CANrxNew(SYNC->CANrxNew);
352     }
353 
354     /* verify error from receive function */
355     if(SYNC->receiveError != 0U){
356         CO_errorReport(SYNC->em, CO_EM_SYNC_LENGTH, CO_EMC_SYNC_DATA_LENGTH, (uint32_t)SYNC->receiveError);
357         SYNC->receiveError = 0U;
358     }
359 
360     return ret;
361 }
362