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