1 /*
2  * CANopen Process Data Object.
3  *
4  * @file        CO_PDO.c
5  * @ingroup     CO_PDO
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 #include "CO_PDO.h"
33 
34 /*
35  * Read received message from CAN module.
36  *
37  * Function will be called (by CAN receive interrupt) every time, when CAN
38  * message with correct identifier will be received. For more information and
39  * description of parameters see file CO_driver.h.
40  * If new message arrives and previous message wasn't processed yet, then
41  * previous message will be lost and overwritten by new message. That's OK with PDOs.
42  */
CO_PDO_receive(void * object,const CO_CANrxMsg_t * msg)43 static void CO_PDO_receive(void *object, const CO_CANrxMsg_t *msg){
44     CO_RPDO_t *RPDO;
45 
46     RPDO = (CO_RPDO_t*)object;   /* this is the correct pointer type of the first argument */
47 
48     if( (RPDO->valid) &&
49         (*RPDO->operatingState == CO_NMT_OPERATIONAL) &&
50         (msg->DLC >= RPDO->dataLength))
51     {
52         if(RPDO->SYNC && RPDO->synchronous && RPDO->SYNC->CANrxToggle) {
53             /* copy data into second buffer and set 'new message' flag */
54             RPDO->CANrxData[1][0] = msg->data[0];
55             RPDO->CANrxData[1][1] = msg->data[1];
56             RPDO->CANrxData[1][2] = msg->data[2];
57             RPDO->CANrxData[1][3] = msg->data[3];
58             RPDO->CANrxData[1][4] = msg->data[4];
59             RPDO->CANrxData[1][5] = msg->data[5];
60             RPDO->CANrxData[1][6] = msg->data[6];
61             RPDO->CANrxData[1][7] = msg->data[7];
62 
63             SET_CANrxNew(RPDO->CANrxNew[1]);
64         }
65         else {
66             /* copy data into default buffer and set 'new message' flag */
67             RPDO->CANrxData[0][0] = msg->data[0];
68             RPDO->CANrxData[0][1] = msg->data[1];
69             RPDO->CANrxData[0][2] = msg->data[2];
70             RPDO->CANrxData[0][3] = msg->data[3];
71             RPDO->CANrxData[0][4] = msg->data[4];
72             RPDO->CANrxData[0][5] = msg->data[5];
73             RPDO->CANrxData[0][6] = msg->data[6];
74             RPDO->CANrxData[0][7] = msg->data[7];
75 
76             SET_CANrxNew(RPDO->CANrxNew[0]);
77         }
78     }
79 }
80 
81 
82 /*
83  * Configure RPDO Communication parameter.
84  *
85  * Function is called from commuincation reset or when parameter changes.
86  *
87  * Function configures following variable from CO_RPDO_t: _valid_. It also
88  * configures CAN rx buffer. If configuration fails, emergency message is send
89  * and device is not able to enter NMT operational.
90  *
91  * @param RPDO RPDO object.
92  * @param COB_IDUsedByRPDO _RPDO communication parameter_, _COB-ID for PDO_ variable
93  * from Object dictionary (index 0x1400+, subindex 1).
94  */
CO_RPDOconfigCom(CO_RPDO_t * RPDO,uint32_t COB_IDUsedByRPDO)95 static void CO_RPDOconfigCom(CO_RPDO_t* RPDO, uint32_t COB_IDUsedByRPDO){
96     uint16_t ID;
97     CO_ReturnError_t r;
98 
99     ID = (uint16_t)COB_IDUsedByRPDO;
100 
101     /* is RPDO used? */
102     if((COB_IDUsedByRPDO & 0xBFFFF800L) == 0 && RPDO->dataLength && ID){
103         /* is used default COB-ID? */
104         if(ID == RPDO->defaultCOB_ID) ID += RPDO->nodeId;
105         RPDO->valid = true;
106         RPDO->synchronous = (RPDO->RPDOCommPar->transmissionType <= 240) ? true : false;
107     }
108     else{
109         ID = 0;
110         RPDO->valid = false;
111         CLEAR_CANrxNew(RPDO->CANrxNew[0]);
112         CLEAR_CANrxNew(RPDO->CANrxNew[1]);
113     }
114     r = CO_CANrxBufferInit(
115             RPDO->CANdevRx,         /* CAN device */
116             RPDO->CANdevRxIdx,      /* rx buffer index */
117             ID,                     /* CAN identifier */
118             0x7FF,                  /* mask */
119             0,                      /* rtr */
120             (void*)RPDO,            /* object passed to receive function */
121             CO_PDO_receive);        /* this function will process received message */
122     if(r != CO_ERROR_NO){
123         RPDO->valid = false;
124         CLEAR_CANrxNew(RPDO->CANrxNew[0]);
125         CLEAR_CANrxNew(RPDO->CANrxNew[1]);
126     }
127 }
128 
129 
130 /*
131  * Configure TPDO Communication parameter.
132  *
133  * Function is called from commuincation reset or when parameter changes.
134  *
135  * Function configures following variable from CO_TPDO_t: _valid_. It also
136  * configures CAN tx buffer. If configuration fails, emergency message is send
137  * and device is not able to enter NMT operational.
138  *
139  * @param TPDO TPDO object.
140  * @param COB_IDUsedByTPDO _TPDO communication parameter_, _COB-ID for PDO_ variable
141  * from Object dictionary (index 0x1400+, subindex 1).
142  * @param syncFlag Indicate, if TPDO is synchronous.
143  */
CO_TPDOconfigCom(CO_TPDO_t * TPDO,uint32_t COB_IDUsedByTPDO,uint8_t syncFlag)144 static void CO_TPDOconfigCom(CO_TPDO_t* TPDO, uint32_t COB_IDUsedByTPDO, uint8_t syncFlag){
145     uint16_t ID;
146 
147     ID = (uint16_t)COB_IDUsedByTPDO;
148 
149     /* is TPDO used? */
150     if((COB_IDUsedByTPDO & 0xBFFFF800L) == 0 && TPDO->dataLength && ID){
151         /* is used default COB-ID? */
152         if(ID == TPDO->defaultCOB_ID) ID += TPDO->nodeId;
153         TPDO->valid = true;
154     }
155     else{
156         ID = 0;
157         TPDO->valid = false;
158     }
159 
160     TPDO->CANtxBuff = CO_CANtxBufferInit(
161             TPDO->CANdevTx,            /* CAN device */
162             TPDO->CANdevTxIdx,         /* index of specific buffer inside CAN module */
163             ID,                        /* CAN identifier */
164             0,                         /* rtr */
165             TPDO->dataLength,          /* number of data bytes */
166             syncFlag);                 /* synchronous message flag bit */
167 
168     if(TPDO->CANtxBuff == 0){
169         TPDO->valid = false;
170     }
171 }
172 
173 
174 /*
175  * Find mapped variable in Object Dictionary.
176  *
177  * Function is called from CO_R(T)PDOconfigMap or when mapping parameter changes.
178  *
179  * @param SDO SDO object.
180  * @param map PDO mapping parameter.
181  * @param R_T 0 for RPDO map, 1 for TPDO map.
182  * @param ppData Pointer to returning parameter: pointer to data of mapped variable.
183  * @param pLength Pointer to returning parameter: *add* length of mapped variable.
184  * @param pSendIfCOSFlags Pointer to returning parameter: sendIfCOSFlags variable.
185  * @param pIsMultibyteVar Pointer to returning parameter: true for multibyte variable.
186  *
187  * @return 0 on success, otherwise SDO abort code.
188  */
CO_PDOfindMap(CO_SDO_t * SDO,uint32_t map,uint8_t R_T,uint8_t ** ppData,uint8_t * pLength,uint8_t * pSendIfCOSFlags,uint8_t * pIsMultibyteVar)189 static uint32_t CO_PDOfindMap(
190         CO_SDO_t               *SDO,
191         uint32_t                map,
192         uint8_t                 R_T,
193         uint8_t               **ppData,
194         uint8_t                *pLength,
195         uint8_t                *pSendIfCOSFlags,
196         uint8_t                *pIsMultibyteVar)
197 {
198     uint16_t entryNo;
199     uint16_t index;
200     uint8_t subIndex;
201     uint8_t dataLen;
202     uint8_t objectLen;
203     uint8_t attr;
204 
205     index = (uint16_t)(map>>16);
206     subIndex = (uint8_t)(map>>8);
207     dataLen = (uint8_t) map;   /* data length in bits */
208 
209     /* data length must be byte aligned */
210     if(dataLen&0x07) return CO_SDO_AB_NO_MAP;   /* Object cannot be mapped to the PDO. */
211 
212     dataLen >>= 3;    /* new data length is in bytes */
213     *pLength += dataLen;
214 
215     /* total PDO length can not be more than 8 bytes */
216     if(*pLength > 8) return CO_SDO_AB_MAP_LEN;  /* The number and length of the objects to be mapped would exceed PDO length. */
217 
218     /* is there a reference to dummy entries */
219     if(index <=7 && subIndex == 0){
220         static uint32_t dummyTX = 0;
221         static uint32_t dummyRX;
222         uint8_t dummySize = 4;
223 
224         if(index<2) dummySize = 0;
225         else if(index==2 || index==5) dummySize = 1;
226         else if(index==3 || index==6) dummySize = 2;
227 
228         /* is size of variable big enough for map */
229         if(dummySize < dataLen) return CO_SDO_AB_NO_MAP;   /* Object cannot be mapped to the PDO. */
230 
231         /* Data and ODE pointer */
232         if(R_T == 0) *ppData = (uint8_t*) &dummyRX;
233         else         *ppData = (uint8_t*) &dummyTX;
234 
235         return 0;
236     }
237 
238     /* find object in Object Dictionary */
239     entryNo = CO_OD_find(SDO, index);
240 
241     /* Does object exist in OD? */
242     if(entryNo == 0xFFFF || subIndex > SDO->OD[entryNo].maxSubIndex)
243         return CO_SDO_AB_NOT_EXIST;   /* Object does not exist in the object dictionary. */
244 
245     attr = CO_OD_getAttribute(SDO, entryNo, subIndex);
246     /* Is object Mappable for RPDO? */
247     if(R_T==0 && !((attr&CO_ODA_RPDO_MAPABLE) && (attr&CO_ODA_WRITEABLE))) return CO_SDO_AB_NO_MAP;   /* Object cannot be mapped to the PDO. */
248     /* Is object Mappable for TPDO? */
249     if(R_T!=0 && !((attr&CO_ODA_TPDO_MAPABLE) && (attr&CO_ODA_READABLE))) return CO_SDO_AB_NO_MAP;   /* Object cannot be mapped to the PDO. */
250 
251     /* is size of variable big enough for map */
252     objectLen = CO_OD_getLength(SDO, entryNo, subIndex);
253     if(objectLen < dataLen) return CO_SDO_AB_NO_MAP;   /* Object cannot be mapped to the PDO. */
254 
255     /* mark multibyte variable */
256     *pIsMultibyteVar = (attr&CO_ODA_MB_VALUE) ? 1 : 0;
257 
258     /* pointer to data */
259     *ppData = (uint8_t*) CO_OD_getDataPointer(SDO, entryNo, subIndex);
260 #ifdef CO_BIG_ENDIAN
261     /* skip unused MSB bytes */
262     if(*pIsMultibyteVar){
263         *ppData += objectLen - dataLen;
264     }
265 #endif
266 
267     /* setup change of state flags */
268     if(attr&CO_ODA_TPDO_DETECT_COS){
269         int16_t i;
270         for(i=*pLength-dataLen; i<*pLength; i++){
271             *pSendIfCOSFlags |= 1<<i;
272         }
273     }
274 
275     return 0;
276 }
277 
278 
279 /*
280  * Configure RPDO Mapping parameter.
281  *
282  * Function is called from communication reset or when parameter changes.
283  *
284  * Function configures following variables from CO_RPDO_t: _dataLength_ and
285  * _mapPointer_.
286  *
287  * @param RPDO RPDO object.
288  * @param noOfMappedObjects Number of mapped object (from OD).
289  *
290  * @return 0 on success, otherwise SDO abort code.
291  */
CO_RPDOconfigMap(CO_RPDO_t * RPDO,uint8_t noOfMappedObjects)292 static uint32_t CO_RPDOconfigMap(CO_RPDO_t* RPDO, uint8_t noOfMappedObjects){
293     int16_t i;
294     uint8_t length = 0;
295     uint32_t ret = 0;
296     const uint32_t* pMap = &RPDO->RPDOMapPar->mappedObject1;
297 
298     for(i=noOfMappedObjects; i>0; i--){
299         int16_t j;
300         uint8_t* pData;
301         uint8_t dummy = 0;
302         uint8_t prevLength = length;
303         uint8_t MBvar;
304         uint32_t map = *(pMap++);
305 
306         /* function do much checking of errors in map */
307         ret = CO_PDOfindMap(
308                 RPDO->SDO,
309                 map,
310                 0,
311                 &pData,
312                 &length,
313                 &dummy,
314                 &MBvar);
315         if(ret){
316             length = 0;
317             CO_errorReport(RPDO->em, CO_EM_PDO_WRONG_MAPPING, CO_EMC_PROTOCOL_ERROR, map);
318             break;
319         }
320 
321         /* write PDO data pointers */
322 #ifdef CO_BIG_ENDIAN
323         if(MBvar){
324             for(j=length-1; j>=prevLength; j--)
325                 RPDO->mapPointer[j] = pData++;
326         }
327         else{
328             for(j=prevLength; j<length; j++)
329                 RPDO->mapPointer[j] = pData++;
330         }
331 #else
332         for(j=prevLength; j<length; j++){
333             RPDO->mapPointer[j] = pData++;
334         }
335 #endif
336 
337     }
338 
339     RPDO->dataLength = length;
340 
341     return ret;
342 }
343 
344 
345 /*
346  * Configure TPDO Mapping parameter.
347  *
348  * Function is called from communication reset or when parameter changes.
349  *
350  * Function configures following variables from CO_TPDO_t: _dataLength_,
351  * _mapPointer_ and _sendIfCOSFlags_.
352  *
353  * @param TPDO TPDO object.
354  * @param noOfMappedObjects Number of mapped object (from OD).
355  *
356  * @return 0 on success, otherwise SDO abort code.
357  */
CO_TPDOconfigMap(CO_TPDO_t * TPDO,uint8_t noOfMappedObjects)358 static uint32_t CO_TPDOconfigMap(CO_TPDO_t* TPDO, uint8_t noOfMappedObjects){
359     int16_t i;
360     uint8_t length = 0;
361     uint32_t ret = 0;
362     const uint32_t* pMap = &TPDO->TPDOMapPar->mappedObject1;
363 
364     TPDO->sendIfCOSFlags = 0;
365 
366     for(i=noOfMappedObjects; i>0; i--){
367         int16_t j;
368         uint8_t* pData;
369         uint8_t prevLength = length;
370         uint8_t MBvar;
371         uint32_t map = *(pMap++);
372 
373         /* function do much checking of errors in map */
374         ret = CO_PDOfindMap(
375                 TPDO->SDO,
376                 map,
377                 1,
378                 &pData,
379                 &length,
380                 &TPDO->sendIfCOSFlags,
381                 &MBvar);
382         if(ret){
383             length = 0;
384             CO_errorReport(TPDO->em, CO_EM_PDO_WRONG_MAPPING, CO_EMC_PROTOCOL_ERROR, map);
385             break;
386         }
387 
388         /* write PDO data pointers */
389 #ifdef CO_BIG_ENDIAN
390         if(MBvar){
391             for(j=length-1; j>=prevLength; j--)
392                 TPDO->mapPointer[j] = pData++;
393         }
394         else{
395             for(j=prevLength; j<length; j++)
396                 TPDO->mapPointer[j] = pData++;
397         }
398 #else
399         for(j=prevLength; j<length; j++){
400             TPDO->mapPointer[j] = pData++;
401         }
402 #endif
403 
404     }
405 
406     TPDO->dataLength = length;
407 
408     return ret;
409 }
410 
411 
412 /*
413  * Function for accessing _RPDO communication parameter_ (index 0x1400+) from SDO server.
414  *
415  * For more information see file CO_SDO.h.
416  */
CO_ODF_RPDOcom(CO_ODF_arg_t * ODF_arg)417 static CO_SDO_abortCode_t CO_ODF_RPDOcom(CO_ODF_arg_t *ODF_arg){
418     CO_RPDO_t *RPDO;
419 
420     RPDO = (CO_RPDO_t*) ODF_arg->object;
421 
422     /* Reading Object Dictionary variable */
423     if(ODF_arg->reading){
424         if(ODF_arg->subIndex == 1){
425             uint32_t value = CO_getUint32(ODF_arg->data);
426 
427             /* if default COB ID is used, write default value here */
428             if(((value)&0xFFFF) == RPDO->defaultCOB_ID && RPDO->defaultCOB_ID)
429                 value += RPDO->nodeId;
430 
431             /* If PDO is not valid, set bit 31 */
432             if(!RPDO->valid) value |= 0x80000000L;
433 
434             CO_setUint32(ODF_arg->data, value);
435         }
436         return CO_SDO_AB_NONE;
437     }
438 
439     /* Writing Object Dictionary variable */
440     if(RPDO->restrictionFlags & 0x04)
441         return CO_SDO_AB_READONLY;  /* Attempt to write a read only object. */
442     if(*RPDO->operatingState == CO_NMT_OPERATIONAL && (RPDO->restrictionFlags & 0x01))
443         return CO_SDO_AB_DATA_DEV_STATE;   /* Data cannot be transferred or stored to the application because of the present device state. */
444 
445     if(ODF_arg->subIndex == 1){   /* COB_ID */
446         uint32_t value = CO_getUint32(ODF_arg->data);
447 
448         /* bits 11...29 must be zero */
449         if(value & 0x3FFF8000L)
450             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
451 
452         /* if default COB-ID is being written, write defaultCOB_ID without nodeId */
453         if(((value)&0xFFFF) == (RPDO->defaultCOB_ID + RPDO->nodeId)){
454             value &= 0xC0000000L;
455             value += RPDO->defaultCOB_ID;
456             CO_setUint32(ODF_arg->data, value);
457         }
458 
459         /* if PDO is valid, bits 0..29 can not be changed */
460         if(RPDO->valid && ((value ^ RPDO->RPDOCommPar->COB_IDUsedByRPDO) & 0x3FFFFFFFL))
461             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
462 
463         /* configure RPDO */
464         CO_RPDOconfigCom(RPDO, value);
465     }
466     else if(ODF_arg->subIndex == 2){   /* Transmission_type */
467         uint8_t *value = (uint8_t*) ODF_arg->data;
468         bool_t synchronousPrev = RPDO->synchronous;
469 
470         /* values from 241...253 are not valid */
471         if(*value >= 241 && *value <= 253)
472             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
473 
474         RPDO->synchronous = (*value <= 240) ? true : false;
475 
476         /* Remove old message from second buffer. */
477         if(RPDO->synchronous != synchronousPrev) {
478             CLEAR_CANrxNew(RPDO->CANrxNew[1]);
479         }
480     }
481 
482     return CO_SDO_AB_NONE;
483 }
484 
485 
486 /*
487  * Function for accessing _TPDO communication parameter_ (index 0x1800+) from SDO server.
488  *
489  * For more information see file CO_SDO.h.
490  */
CO_ODF_TPDOcom(CO_ODF_arg_t * ODF_arg)491 static CO_SDO_abortCode_t CO_ODF_TPDOcom(CO_ODF_arg_t *ODF_arg){
492     CO_TPDO_t *TPDO;
493 
494     TPDO = (CO_TPDO_t*) ODF_arg->object;
495 
496     if(ODF_arg->subIndex == 4) return CO_SDO_AB_SUB_UNKNOWN;  /* Sub-index does not exist. */
497 
498     /* Reading Object Dictionary variable */
499     if(ODF_arg->reading){
500         if(ODF_arg->subIndex == 1){   /* COB_ID */
501             uint32_t value = CO_getUint32(ODF_arg->data);
502 
503             /* if default COB ID is used, write default value here */
504             if(((value)&0xFFFF) == TPDO->defaultCOB_ID && TPDO->defaultCOB_ID)
505                 value += TPDO->nodeId;
506 
507             /* If PDO is not valid, set bit 31 */
508             if(!TPDO->valid) value |= 0x80000000L;
509 
510             CO_setUint32(ODF_arg->data, value);
511         }
512         return CO_SDO_AB_NONE;
513     }
514 
515     /* Writing Object Dictionary variable */
516     if(TPDO->restrictionFlags & 0x04)
517         return CO_SDO_AB_READONLY;  /* Attempt to write a read only object. */
518     if(*TPDO->operatingState == CO_NMT_OPERATIONAL && (TPDO->restrictionFlags & 0x01))
519         return CO_SDO_AB_DATA_DEV_STATE;   /* Data cannot be transferred or stored to the application because of the present device state. */
520 
521     if(ODF_arg->subIndex == 1){   /* COB_ID */
522         uint32_t value = CO_getUint32(ODF_arg->data);
523 
524         /* bits 11...29 must be zero */
525         if(value & 0x3FFF8000L)
526             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
527 
528         /* if default COB-ID is being written, write defaultCOB_ID without nodeId */
529         if(((value)&0xFFFF) == (TPDO->defaultCOB_ID + TPDO->nodeId)){
530             value &= 0xC0000000L;
531             value += TPDO->defaultCOB_ID;
532 
533             CO_setUint32(ODF_arg->data, value);
534         }
535 
536         /* if PDO is valid, bits 0..29 can not be changed */
537         if(TPDO->valid && ((value ^ TPDO->TPDOCommPar->COB_IDUsedByTPDO) & 0x3FFFFFFFL))
538             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
539 
540         /* configure TPDO */
541         CO_TPDOconfigCom(TPDO, value, TPDO->CANtxBuff->syncFlag);
542         TPDO->syncCounter = 255;
543     }
544     else if(ODF_arg->subIndex == 2){   /* Transmission_type */
545         uint8_t *value = (uint8_t*) ODF_arg->data;
546 
547         /* values from 241...253 are not valid */
548         if(*value >= 241 && *value <= 253)
549             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
550         TPDO->CANtxBuff->syncFlag = (*value <= 240) ? 1 : 0;
551         TPDO->syncCounter = 255;
552     }
553     else if(ODF_arg->subIndex == 3){   /* Inhibit_Time */
554         /* if PDO is valid, value can not be changed */
555         if(TPDO->valid)
556             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
557 
558         TPDO->inhibitTimer = 0;
559     }
560     else if(ODF_arg->subIndex == 5){   /* Event_Timer */
561         uint16_t value = CO_getUint16(ODF_arg->data);
562 
563         TPDO->eventTimer = ((uint32_t) value) * 1000;
564     }
565     else if(ODF_arg->subIndex == 6){   /* SYNC start value */
566         uint8_t *value = (uint8_t*) ODF_arg->data;
567 
568         /* if PDO is valid, value can not be changed */
569         if(TPDO->valid)
570             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
571 
572         /* values from 240...255 are not valid */
573         if(*value > 240)
574             return CO_SDO_AB_INVALID_VALUE;  /* Invalid value for parameter (download only). */
575     }
576 
577     return CO_SDO_AB_NONE;
578 }
579 
580 
581 /*
582  * Function for accessing _RPDO mapping parameter_ (index 0x1600+) from SDO server.
583  *
584  * For more information see file CO_SDO.h.
585  */
CO_ODF_RPDOmap(CO_ODF_arg_t * ODF_arg)586 static CO_SDO_abortCode_t CO_ODF_RPDOmap(CO_ODF_arg_t *ODF_arg){
587     CO_RPDO_t *RPDO;
588 
589     RPDO = (CO_RPDO_t*) ODF_arg->object;
590 
591     /* Reading Object Dictionary variable */
592     if(ODF_arg->reading){
593         uint8_t *value = (uint8_t*) ODF_arg->data;
594 
595         if(ODF_arg->subIndex == 0){
596             /* If there is error in mapping, dataLength is 0, so numberOfMappedObjects is 0. */
597             if(!RPDO->dataLength) *value = 0;
598         }
599         return CO_SDO_AB_NONE;
600     }
601 
602     /* Writing Object Dictionary variable */
603     if(RPDO->restrictionFlags & 0x08)
604         return CO_SDO_AB_READONLY;  /* Attempt to write a read only object. */
605     if(*RPDO->operatingState == CO_NMT_OPERATIONAL && (RPDO->restrictionFlags & 0x02))
606         return CO_SDO_AB_DATA_DEV_STATE;   /* Data cannot be transferred or stored to the application because of the present device state. */
607     if(RPDO->valid)
608         return CO_SDO_AB_UNSUPPORTED_ACCESS;  /* Unsupported access to an object. */
609 
610     /* numberOfMappedObjects */
611     if(ODF_arg->subIndex == 0){
612         uint8_t *value = (uint8_t*) ODF_arg->data;
613 
614         if(*value > 8)
615             return CO_SDO_AB_MAP_LEN;  /* Number and length of object to be mapped exceeds PDO length. */
616 
617         /* configure mapping */
618         return CO_RPDOconfigMap(RPDO, *value);
619     }
620 
621     /* mappedObject */
622     else{
623         uint32_t value = CO_getUint32(ODF_arg->data);
624         uint8_t* pData;
625         uint8_t length = 0;
626         uint8_t dummy = 0;
627         uint8_t MBvar;
628 
629         if(RPDO->dataLength)
630             return CO_SDO_AB_UNSUPPORTED_ACCESS;  /* Unsupported access to an object. */
631 
632         /* verify if mapping is correct */
633         return CO_PDOfindMap(
634                 RPDO->SDO,
635                 value,
636                 0,
637                &pData,
638                &length,
639                &dummy,
640                &MBvar);
641     }
642 
643     return CO_SDO_AB_NONE;
644 }
645 
646 
647 /*
648  * Function for accessing _TPDO mapping parameter_ (index 0x1A00+) from SDO server.
649  *
650  * For more information see file CO_SDO.h.
651  */
CO_ODF_TPDOmap(CO_ODF_arg_t * ODF_arg)652 static CO_SDO_abortCode_t CO_ODF_TPDOmap(CO_ODF_arg_t *ODF_arg){
653     CO_TPDO_t *TPDO;
654 
655     TPDO = (CO_TPDO_t*) ODF_arg->object;
656 
657     /* Reading Object Dictionary variable */
658     if(ODF_arg->reading){
659         uint8_t *value = (uint8_t*) ODF_arg->data;
660 
661         if(ODF_arg->subIndex == 0){
662             /* If there is error in mapping, dataLength is 0, so numberOfMappedObjects is 0. */
663             if(!TPDO->dataLength) *value = 0;
664         }
665         return CO_SDO_AB_NONE;
666     }
667 
668     /* Writing Object Dictionary variable */
669     if(TPDO->restrictionFlags & 0x08)
670         return CO_SDO_AB_READONLY;  /* Attempt to write a read only object. */
671     if(*TPDO->operatingState == CO_NMT_OPERATIONAL && (TPDO->restrictionFlags & 0x02))
672         return CO_SDO_AB_DATA_DEV_STATE;   /* Data cannot be transferred or stored to the application because of the present device state. */
673     if(TPDO->valid)
674         return CO_SDO_AB_UNSUPPORTED_ACCESS;  /* Unsupported access to an object. */
675 
676     /* numberOfMappedObjects */
677     if(ODF_arg->subIndex == 0){
678         uint8_t *value = (uint8_t*) ODF_arg->data;
679 
680         if(*value > 8)
681             return CO_SDO_AB_MAP_LEN;  /* Number and length of object to be mapped exceeds PDO length. */
682 
683         /* configure mapping */
684         return CO_TPDOconfigMap(TPDO, *value);
685     }
686 
687     /* mappedObject */
688     else{
689         uint32_t value = CO_getUint32(ODF_arg->data);
690         uint8_t* pData;
691         uint8_t length = 0;
692         uint8_t dummy = 0;
693         uint8_t MBvar;
694 
695         if(TPDO->dataLength)
696             return CO_SDO_AB_UNSUPPORTED_ACCESS;  /* Unsupported access to an object. */
697 
698         /* verify if mapping is correct */
699         return CO_PDOfindMap(
700                 TPDO->SDO,
701                 value,
702                 1,
703                &pData,
704                &length,
705                &dummy,
706                &MBvar);
707     }
708 
709     return CO_SDO_AB_NONE;
710 }
711 
712 
713 /******************************************************************************/
CO_RPDO_init(CO_RPDO_t * RPDO,CO_EM_t * em,CO_SDO_t * SDO,CO_SYNC_t * SYNC,uint8_t * operatingState,uint8_t nodeId,uint16_t defaultCOB_ID,uint8_t restrictionFlags,const CO_RPDOCommPar_t * RPDOCommPar,const CO_RPDOMapPar_t * RPDOMapPar,uint16_t idx_RPDOCommPar,uint16_t idx_RPDOMapPar,CO_CANmodule_t * CANdevRx,uint16_t CANdevRxIdx)714 CO_ReturnError_t CO_RPDO_init(
715         CO_RPDO_t              *RPDO,
716         CO_EM_t                *em,
717         CO_SDO_t               *SDO,
718         CO_SYNC_t              *SYNC,
719         uint8_t                *operatingState,
720         uint8_t                 nodeId,
721         uint16_t                defaultCOB_ID,
722         uint8_t                 restrictionFlags,
723         const CO_RPDOCommPar_t *RPDOCommPar,
724         const CO_RPDOMapPar_t  *RPDOMapPar,
725         uint16_t                idx_RPDOCommPar,
726         uint16_t                idx_RPDOMapPar,
727         CO_CANmodule_t         *CANdevRx,
728         uint16_t                CANdevRxIdx)
729 {
730     /* verify arguments */
731     if(RPDO==NULL || em==NULL || SDO==NULL || operatingState==NULL ||
732         RPDOCommPar==NULL || RPDOMapPar==NULL || CANdevRx==NULL){
733         return CO_ERROR_ILLEGAL_ARGUMENT;
734     }
735 
736     /* Configure object variables */
737     RPDO->em = em;
738     RPDO->SDO = SDO;
739     RPDO->SYNC = SYNC;
740     RPDO->RPDOCommPar = RPDOCommPar;
741     RPDO->RPDOMapPar = RPDOMapPar;
742     RPDO->operatingState = operatingState;
743     RPDO->nodeId = nodeId;
744     RPDO->defaultCOB_ID = defaultCOB_ID;
745     RPDO->restrictionFlags = restrictionFlags;
746 
747     /* Configure Object dictionary entry at index 0x1400+ and 0x1600+ */
748     CO_OD_configure(SDO, idx_RPDOCommPar, CO_ODF_RPDOcom, (void*)RPDO, 0, 0);
749     CO_OD_configure(SDO, idx_RPDOMapPar, CO_ODF_RPDOmap, (void*)RPDO, 0, 0);
750 
751     /* configure communication and mapping */
752     CLEAR_CANrxNew(RPDO->CANrxNew[0]);
753     CLEAR_CANrxNew(RPDO->CANrxNew[1]);
754     RPDO->CANdevRx = CANdevRx;
755     RPDO->CANdevRxIdx = CANdevRxIdx;
756 
757     CO_RPDOconfigMap(RPDO, RPDOMapPar->numberOfMappedObjects);
758     CO_RPDOconfigCom(RPDO, RPDOCommPar->COB_IDUsedByRPDO);
759 
760     return CO_ERROR_NO;
761 }
762 
763 
764 /******************************************************************************/
CO_TPDO_init(CO_TPDO_t * TPDO,CO_EM_t * em,CO_SDO_t * SDO,CO_SYNC_t * SYNC,uint8_t * operatingState,uint8_t nodeId,uint16_t defaultCOB_ID,uint8_t restrictionFlags,const CO_TPDOCommPar_t * TPDOCommPar,const CO_TPDOMapPar_t * TPDOMapPar,uint16_t idx_TPDOCommPar,uint16_t idx_TPDOMapPar,CO_CANmodule_t * CANdevTx,uint16_t CANdevTxIdx)765 CO_ReturnError_t CO_TPDO_init(
766         CO_TPDO_t              *TPDO,
767         CO_EM_t                *em,
768         CO_SDO_t               *SDO,
769         CO_SYNC_t              *SYNC,
770         uint8_t                *operatingState,
771         uint8_t                 nodeId,
772         uint16_t                defaultCOB_ID,
773         uint8_t                 restrictionFlags,
774         const CO_TPDOCommPar_t *TPDOCommPar,
775         const CO_TPDOMapPar_t  *TPDOMapPar,
776         uint16_t                idx_TPDOCommPar,
777         uint16_t                idx_TPDOMapPar,
778         CO_CANmodule_t         *CANdevTx,
779         uint16_t                CANdevTxIdx)
780 {
781     /* verify arguments */
782     if(TPDO==NULL || em==NULL || SDO==NULL || operatingState==NULL ||
783         TPDOCommPar==NULL || TPDOMapPar==NULL || CANdevTx==NULL){
784         return CO_ERROR_ILLEGAL_ARGUMENT;
785     }
786 
787     /* Configure object variables */
788     TPDO->em = em;
789     TPDO->SDO = SDO;
790     TPDO->SYNC = SYNC;
791     TPDO->TPDOCommPar = TPDOCommPar;
792     TPDO->TPDOMapPar = TPDOMapPar;
793     TPDO->operatingState = operatingState;
794     TPDO->nodeId = nodeId;
795     TPDO->defaultCOB_ID = defaultCOB_ID;
796     TPDO->restrictionFlags = restrictionFlags;
797 
798     /* Configure Object dictionary entry at index 0x1800+ and 0x1A00+ */
799     CO_OD_configure(SDO, idx_TPDOCommPar, CO_ODF_TPDOcom, (void*)TPDO, 0, 0);
800     CO_OD_configure(SDO, idx_TPDOMapPar, CO_ODF_TPDOmap, (void*)TPDO, 0, 0);
801 
802     /* configure communication and mapping */
803     TPDO->CANdevTx = CANdevTx;
804     TPDO->CANdevTxIdx = CANdevTxIdx;
805     TPDO->syncCounter = 255;
806     TPDO->inhibitTimer = 0;
807     TPDO->eventTimer = ((uint32_t) TPDOCommPar->eventTimer) * 1000;
808     if(TPDOCommPar->transmissionType>=254) TPDO->sendRequest = 1;
809 
810     CO_TPDOconfigMap(TPDO, TPDOMapPar->numberOfMappedObjects);
811     CO_TPDOconfigCom(TPDO, TPDOCommPar->COB_IDUsedByTPDO, ((TPDOCommPar->transmissionType<=240) ? 1 : 0));
812 
813     if((TPDOCommPar->transmissionType>240 &&
814          TPDOCommPar->transmissionType<254) ||
815          TPDOCommPar->SYNCStartValue>240){
816             TPDO->valid = false;
817     }
818 
819     return CO_ERROR_NO;
820 }
821 
822 
823 /******************************************************************************/
CO_TPDOisCOS(CO_TPDO_t * TPDO)824 uint8_t CO_TPDOisCOS(CO_TPDO_t *TPDO){
825 
826     /* Prepare TPDO data automatically from Object Dictionary variables */
827     uint8_t* pPDOdataByte;
828     uint8_t** ppODdataByte;
829 
830     pPDOdataByte = &TPDO->CANtxBuff->data[TPDO->dataLength];
831     ppODdataByte = &TPDO->mapPointer[TPDO->dataLength];
832 
833     switch(TPDO->dataLength){
834         case 8: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x80)) return 1; // fallthrough
835         case 7: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x40)) return 1; // fallthrough
836         case 6: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x20)) return 1; // fallthrough
837         case 5: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x10)) return 1; // fallthrough
838         case 4: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x08)) return 1; // fallthrough
839         case 3: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x04)) return 1; // fallthrough
840         case 2: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x02)) return 1; // fallthrough
841         case 1: if(*(--pPDOdataByte) != **(--ppODdataByte) && (TPDO->sendIfCOSFlags&0x01)) return 1; // fallthrough
842     }
843 
844     return 0;
845 }
846 
847 //#define TPDO_CALLS_EXTENSION
848 /******************************************************************************/
CO_TPDOsend(CO_TPDO_t * TPDO)849 int16_t CO_TPDOsend(CO_TPDO_t *TPDO){
850     int16_t i;
851     uint8_t* pPDOdataByte;
852     uint8_t** ppODdataByte;
853 
854 #ifdef TPDO_CALLS_EXTENSION
855     if(TPDO->SDO->ODExtensions){
856         /* for each mapped OD, check mapping to see if an OD extension is available, and call it if it is */
857         const uint32_t* pMap = &TPDO->TPDOMapPar->mappedObject1;
858         CO_SDO_t *pSDO = TPDO->SDO;
859 
860         for(i=TPDO->TPDOMapPar->numberOfMappedObjects; i>0; i--){
861             uint32_t map = *(pMap++);
862             uint16_t index = (uint16_t)(map>>16);
863             uint8_t subIndex = (uint8_t)(map>>8);
864             uint16_t entryNo = CO_OD_find(pSDO, index);
865             if ( entryNo == 0xFFFF ) continue;
866             CO_OD_extension_t *ext = &pSDO->ODExtensions[entryNo];
867             if( ext->pODFunc == NULL) continue;
868             CO_ODF_arg_t ODF_arg;
869             memset((void*)&ODF_arg, 0, sizeof(CO_ODF_arg_t));
870             ODF_arg.reading = true;
871             ODF_arg.index = index;
872             ODF_arg.subIndex = subIndex;
873             ODF_arg.object = ext->object;
874             ODF_arg.attribute = CO_OD_getAttribute(pSDO, entryNo, subIndex);
875             ODF_arg.pFlags = CO_OD_getFlagsPointer(pSDO, entryNo, subIndex);
876             ODF_arg.data = CO_OD_getDataPointer(pSDO, entryNo, subIndex); //https://github.com/CANopenNode/CANopenNode/issues/100
877             ODF_arg.dataLength = CO_OD_getLength(pSDO, entryNo, subIndex);
878             ext->pODFunc(&ODF_arg);
879         }
880     }
881 #endif
882     i = TPDO->dataLength;
883     pPDOdataByte = &TPDO->CANtxBuff->data[0];
884     ppODdataByte = &TPDO->mapPointer[0];
885 
886     /* Copy data from Object dictionary. */
887     for(; i>0; i--) {
888         *(pPDOdataByte++) = **(ppODdataByte++);
889     }
890 
891     TPDO->sendRequest = 0;
892 
893     return CO_CANsend(TPDO->CANdevTx, TPDO->CANtxBuff);
894 }
895 
896 //#define RPDO_CALLS_EXTENSION
897 /******************************************************************************/
CO_RPDO_process(CO_RPDO_t * RPDO,bool_t syncWas)898 void CO_RPDO_process(CO_RPDO_t *RPDO, bool_t syncWas){
899 
900     if(!RPDO->valid || !(*RPDO->operatingState == CO_NMT_OPERATIONAL))
901     {
902         CLEAR_CANrxNew(RPDO->CANrxNew[0]);
903         CLEAR_CANrxNew(RPDO->CANrxNew[1]);
904     }
905     else if(!RPDO->synchronous || syncWas)
906     {
907 #if defined(RPDO_CALLS_EXTENSION)
908         bool_t update = false;
909 #endif /* defined(RPDO_CALLS_EXTENSION) */
910 
911         uint8_t bufNo = 0;
912 
913         /* Determine, which of the two rx buffers, contains relevant message. */
914         if(RPDO->SYNC && RPDO->synchronous && !RPDO->SYNC->CANrxToggle) {
915             bufNo = 1;
916         }
917 
918         while(IS_CANrxNew(RPDO->CANrxNew[bufNo])){
919             int16_t i;
920             uint8_t* pPDOdataByte;
921             uint8_t** ppODdataByte;
922 
923             i = RPDO->dataLength;
924             pPDOdataByte = &RPDO->CANrxData[bufNo][0];
925             ppODdataByte = &RPDO->mapPointer[0];
926 
927             /* Copy data to Object dictionary. If between the copy operation CANrxNew
928              * is set to true by receive thread, then copy the latest data again. */
929             CLEAR_CANrxNew(RPDO->CANrxNew[bufNo]);
930             for(; i>0; i--) {
931                 **(ppODdataByte++) = *(pPDOdataByte++);
932             }
933 #if defined(RPDO_CALLS_EXTENSION)
934             update = true;
935 #endif /* defined(RPDO_CALLS_EXTENSION) */
936         }
937 #ifdef RPDO_CALLS_EXTENSION
938         if(update && RPDO->SDO->ODExtensions){
939             int16_t i;
940             /* for each mapped OD, check mapping to see if an OD extension is available, and call it if it is */
941             const uint32_t* pMap = &RPDO->RPDOMapPar->mappedObject1;
942             CO_SDO_t *pSDO = RPDO->SDO;
943 
944             for(i=RPDO->RPDOMapPar->numberOfMappedObjects; i>0; i--){
945                 uint32_t map = *(pMap++);
946                 uint16_t index = (uint16_t)(map>>16);
947                 uint8_t subIndex = (uint8_t)(map>>8);
948                 uint16_t entryNo = CO_OD_find(pSDO, index);
949                 if ( entryNo == 0xFFFF ) continue;
950                 CO_OD_extension_t *ext = &pSDO->ODExtensions[entryNo];
951                 if( ext->pODFunc == NULL) continue;
952                 CO_ODF_arg_t ODF_arg;
953                 memset((void*)&ODF_arg, 0, sizeof(CO_ODF_arg_t));
954                 ODF_arg.reading = false;
955                 ODF_arg.index = index;
956                 ODF_arg.subIndex = subIndex;
957                 ODF_arg.object = ext->object;
958                 ODF_arg.attribute = CO_OD_getAttribute(pSDO, entryNo, subIndex);
959                 ODF_arg.pFlags = CO_OD_getFlagsPointer(pSDO, entryNo, subIndex);
960                 ODF_arg.data = CO_OD_getDataPointer(pSDO, entryNo, subIndex); //https://github.com/CANopenNode/CANopenNode/issues/100
961                 ODF_arg.dataLength = CO_OD_getLength(pSDO, entryNo, subIndex);
962                 ext->pODFunc(&ODF_arg);
963             }
964         }
965 #endif
966     }
967 }
968 
969 
970 /******************************************************************************/
CO_TPDO_process(CO_TPDO_t * TPDO,bool_t syncWas,uint32_t timeDifference_us)971 void CO_TPDO_process(
972         CO_TPDO_t              *TPDO,
973         bool_t                  syncWas,
974         uint32_t                timeDifference_us)
975 {
976     if(TPDO->valid && *TPDO->operatingState == CO_NMT_OPERATIONAL){
977 
978         /* Send PDO by application request or by Event timer */
979         if(TPDO->TPDOCommPar->transmissionType >= 253){
980             if(TPDO->inhibitTimer == 0 && (TPDO->sendRequest || (TPDO->TPDOCommPar->eventTimer && TPDO->eventTimer == 0))){
981                 if(CO_TPDOsend(TPDO) == CO_ERROR_NO){
982                     /* successfully sent */
983                     TPDO->inhibitTimer = ((uint32_t) TPDO->TPDOCommPar->inhibitTime) * 100;
984                     TPDO->eventTimer = ((uint32_t) TPDO->TPDOCommPar->eventTimer) * 1000;
985                 }
986             }
987         }
988 
989         /* Synchronous PDOs */
990         else if(TPDO->SYNC && syncWas){
991             /* send synchronous acyclic PDO */
992             if(TPDO->TPDOCommPar->transmissionType == 0){
993                 if(TPDO->sendRequest) CO_TPDOsend(TPDO);
994             }
995             /* send synchronous cyclic PDO */
996             else{
997                 /* is the start of synchronous TPDO transmission */
998                 if(TPDO->syncCounter == 255){
999                     if(TPDO->SYNC->counterOverflowValue && TPDO->TPDOCommPar->SYNCStartValue)
1000                         TPDO->syncCounter = 254;   /* SYNCStartValue is in use */
1001                     else
1002                         TPDO->syncCounter = TPDO->TPDOCommPar->transmissionType;
1003                 }
1004                 /* if the SYNCStartValue is in use, start first TPDO after SYNC with matched SYNCStartValue. */
1005                 if(TPDO->syncCounter == 254){
1006                     if(TPDO->SYNC->counter == TPDO->TPDOCommPar->SYNCStartValue){
1007                         TPDO->syncCounter = TPDO->TPDOCommPar->transmissionType;
1008                         CO_TPDOsend(TPDO);
1009                     }
1010                 }
1011                 /* Send PDO after every N-th Sync */
1012                 else if(--TPDO->syncCounter == 0){
1013                     TPDO->syncCounter = TPDO->TPDOCommPar->transmissionType;
1014                     CO_TPDOsend(TPDO);
1015                 }
1016             }
1017         }
1018 
1019     }
1020     else{
1021         /* Not operational or valid. Force TPDO first send after operational or valid. */
1022         if(TPDO->TPDOCommPar->transmissionType>=254) TPDO->sendRequest = 1;
1023         else                                         TPDO->sendRequest = 0;
1024     }
1025 
1026     /* update timers */
1027     TPDO->inhibitTimer = (TPDO->inhibitTimer > timeDifference_us) ? (TPDO->inhibitTimer - timeDifference_us) : 0;
1028     TPDO->eventTimer = (TPDO->eventTimer > timeDifference_us) ? (TPDO->eventTimer - timeDifference_us) : 0;
1029 }
1030