Lely core libraries 1.9.2
pdo.c
Go to the documentation of this file.
1
24#include "co.h"
25
26#include <lely/can/msg.h>
27#include <lely/co/dev.h>
28#include <lely/co/obj.h>
29#include <lely/co/pdo.h>
30#include <lely/co/sdo.h>
31#include <lely/util/endian.h>
32
33#include <assert.h>
34
35static co_unsigned32_t co_dev_cfg_pdo_comm(const co_dev_t *dev,
36 co_unsigned16_t idx, const struct co_pdo_comm_par *par);
37
38static co_unsigned32_t co_dev_cfg_pdo_map(const co_dev_t *dev,
39 co_unsigned16_t num, const struct co_pdo_map_par *par);
40
41co_unsigned32_t
42co_dev_chk_rpdo(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
43{
44 assert(dev);
45
46 if (co_type_is_basic(idx) && !subidx) {
47 // If the object is a dummy entry, check if it is enabled.
48 if (__unlikely(!(co_dev_get_dummy(dev) & (1 << idx))))
49 return CO_SDO_AC_NO_OBJ;
50 } else {
51 co_obj_t *obj = co_dev_find_obj(dev, idx);
52 if (__unlikely(!obj))
53 return CO_SDO_AC_NO_OBJ;
54
55 co_sub_t *sub = co_obj_find_sub(obj, subidx);
56 if (__unlikely(!sub))
57 return CO_SDO_AC_NO_SUB;
58
59 unsigned int access = co_sub_get_access(sub);
60 if (__unlikely(!(access & CO_ACCESS_WRITE)))
61 return CO_SDO_AC_NO_WRITE;
62
63 // clang-format off
65 || !(access & CO_ACCESS_RPDO)))
66 // clang-format on
67 return CO_SDO_AC_NO_PDO;
68 }
69
70 return 0;
71}
72
73co_unsigned32_t
74co_dev_cfg_rpdo(const co_dev_t *dev, co_unsigned16_t num,
75 const struct co_pdo_comm_par *comm,
76 const struct co_pdo_map_par *map)
77{
78 assert(comm);
79
80 co_unsigned32_t ac = 0;
81
82 struct co_pdo_comm_par par = *comm;
83 // Disable the RPDO service before configuring any parameters.
85 ac = co_dev_cfg_rpdo_comm(dev, num, &par);
86 if (__unlikely(ac))
87 return ac;
88
89 ac = co_dev_cfg_rpdo_map(dev, num, map);
90 if (__unlikely(ac))
91 return ac;
92
93 // Re-enable the RPDO service if necessary.
94 if (!(comm->cobid & CO_PDO_COBID_VALID))
95 ac = co_dev_cfg_rpdo_comm(dev, num, comm);
96 return ac;
97}
98
99co_unsigned32_t
100co_dev_cfg_rpdo_comm(const co_dev_t *dev, co_unsigned16_t num,
101 const struct co_pdo_comm_par *par)
102{
103 if (__unlikely(!num || num > 512))
104 return CO_SDO_AC_NO_OBJ;
105
106 return co_dev_cfg_pdo_comm(dev, 0x1400 + num - 1, par);
107}
108
109co_unsigned32_t
110co_dev_cfg_rpdo_map(const co_dev_t *dev, co_unsigned16_t num,
111 const struct co_pdo_map_par *par)
112{
113 if (__unlikely(!num || num > 512))
114 return CO_SDO_AC_NO_OBJ;
115
116 return co_dev_cfg_pdo_map(dev, 0x1600 + num - 1, par);
117}
118
119co_unsigned32_t
120co_dev_chk_tpdo(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
121{
122 assert(dev);
123
124 co_obj_t *obj = co_dev_find_obj(dev, idx);
125 if (__unlikely(!obj))
126 return CO_SDO_AC_NO_OBJ;
127
128 co_sub_t *sub = co_obj_find_sub(obj, subidx);
129 if (__unlikely(!sub))
130 return CO_SDO_AC_NO_SUB;
131
132 unsigned int access = co_sub_get_access(sub);
133 if (__unlikely(!(access & CO_ACCESS_READ)))
134 return CO_SDO_AC_NO_READ;
135
136 // clang-format off
138 || !(access & CO_ACCESS_TPDO)))
139 // clang-format on
140 return CO_SDO_AC_NO_PDO;
141
142 return 0;
143}
144
145co_unsigned32_t
146co_dev_cfg_tpdo(const co_dev_t *dev, co_unsigned16_t num,
147 const struct co_pdo_comm_par *comm,
148 const struct co_pdo_map_par *map)
149{
150 assert(comm);
151
152 co_unsigned32_t ac = 0;
153
154 struct co_pdo_comm_par par = *comm;
155 // Disable the TPDO service before configuring any parameters.
157 ac = co_dev_cfg_tpdo_comm(dev, num, &par);
158 if (__unlikely(ac))
159 return ac;
160
161 ac = co_dev_cfg_tpdo_map(dev, num, map);
162 if (__unlikely(ac))
163 return ac;
164
165 // Re-enable the TPDO service if necessary.
166 if (!(comm->cobid & CO_PDO_COBID_VALID))
167 ac = co_dev_cfg_tpdo_comm(dev, num, comm);
168 return ac;
169}
170
171co_unsigned32_t
172co_dev_cfg_tpdo_comm(const co_dev_t *dev, co_unsigned16_t num,
173 const struct co_pdo_comm_par *par)
174{
175 if (__unlikely(!num || num > 512))
176 return CO_SDO_AC_NO_OBJ;
177
178 return co_dev_cfg_pdo_comm(dev, 0x1800 + num - 1, par);
179}
180
181co_unsigned32_t
182co_dev_cfg_tpdo_map(const co_dev_t *dev, co_unsigned16_t num,
183 const struct co_pdo_map_par *par)
184{
185 if (__unlikely(!num || num > 512))
186 return CO_SDO_AC_NO_OBJ;
187
188 return co_dev_cfg_pdo_map(dev, 0x1a00 + num - 1, par);
189}
190
191co_unsigned32_t
192co_pdo_map(const struct co_pdo_map_par *par, const co_unsigned64_t *val,
193 co_unsigned8_t n, uint8_t *buf, size_t *pn)
194{
195 assert(par);
196 assert(val);
197
198 if (__unlikely(par->n > 0x40 || n != par->n))
199 return CO_SDO_AC_PDO_LEN;
200
201 size_t offset = 0;
202 for (size_t i = 0; i < par->n; i++) {
203 co_unsigned8_t len = par->map[i] & 0xff;
204
205 if (__unlikely(offset + len > CAN_MAX_LEN * 8))
206 return CO_SDO_AC_PDO_LEN;
207
208 uint8_t tmp[sizeof(co_unsigned64_t)] = { 0 };
209 stle_u64(tmp, val[i]);
210 if (buf && pn && offset + len <= *pn * 8)
211 bcpyle(buf, offset, tmp, 0, len);
212
213 offset += len;
214 }
215
216 if (pn)
217 *pn = (offset + 7) / 8;
218
219 return 0;
220}
221
222co_unsigned32_t
223co_pdo_unmap(const struct co_pdo_map_par *par, const uint8_t *buf, size_t n,
224 co_unsigned64_t *val, co_unsigned8_t *pn)
225{
226 assert(par);
227 assert(buf);
228
229 if (__unlikely(par->n > 0x40))
230 return CO_SDO_AC_PDO_LEN;
231
232 size_t offset = 0;
233 for (size_t i = 0; i < par->n; i++) {
234 co_unsigned8_t len = par->map[i] & 0xff;
235
236 if (__unlikely(offset + len > n * 8))
237 return CO_SDO_AC_PDO_LEN;
238
239 uint8_t tmp[sizeof(co_unsigned64_t)] = { 0 };
240 bcpyle(tmp, 0, buf, offset, len);
241 if (val && pn && i < *pn)
242 val[i] = ldle_u64(tmp);
243
244 offset += len;
245 }
246
247 if (pn)
248 *pn = par->n;
249
250 return 0;
251}
252
253co_unsigned32_t
254co_pdo_dn(const struct co_pdo_map_par *par, co_dev_t *dev,
255 struct co_sdo_req *req, const uint8_t *buf, size_t n)
256{
257 assert(par);
258 assert(dev);
259 assert(req);
260 assert(buf);
261
262 if (__unlikely(n > CAN_MAX_LEN))
263 return CO_SDO_AC_PDO_LEN;
264
265 co_unsigned32_t ac = 0;
266
267 size_t offset = 0;
268 for (size_t i = 0; i < MIN(par->n, 0x40u); i++) {
269 co_unsigned32_t map = par->map[i];
270 co_unsigned16_t idx = (map >> 16) & 0xffff;
271 co_unsigned8_t subidx = (map >> 8) & 0xff;
272 co_unsigned8_t len = map & 0xff;
273
274 // Check the PDO length.
275 if (__unlikely(offset + len > n * 8))
276 return CO_SDO_AC_PDO_LEN;
277
278 // Check whether the sub-object exists and can be mapped into a
279 // PDO (or is a valid dummy entry).
280 ac = co_dev_chk_rpdo(dev, idx, subidx);
281 if (__unlikely(ac))
282 return ac;
283
284 co_sub_t *sub = co_dev_find_sub(dev, idx, subidx);
285 if (sub) {
286 // Copy the value and download it into the sub-object.
287 uint8_t tmp[CAN_MAX_LEN] = { 0 };
288 bcpyle(tmp, 0, buf, offset, len);
289 co_sdo_req_clear(req);
290 req->size = (len + 7) / 8;
291 req->buf = tmp;
292 req->nbyte = req->size;
293 ac = co_sub_dn_ind(sub, req);
294 if (__unlikely(ac))
295 return ac;
296 }
297
298 offset += len;
299 }
300
301 return ac;
302}
303
304co_unsigned32_t
305co_pdo_up(const struct co_pdo_map_par *par, const co_dev_t *dev,
306 struct co_sdo_req *req, uint8_t *buf, size_t *pn)
307{
308 assert(par);
309 assert(dev);
310 assert(req);
311
312 co_unsigned32_t ac = 0;
313
314 size_t offset = 0;
315 for (size_t i = 0; i < MIN(par->n, 0x40u); i++) {
316 co_unsigned32_t map = par->map[i];
317 co_unsigned16_t idx = (map >> 16) & 0xffff;
318 co_unsigned8_t subidx = (map >> 8) & 0xff;
319 co_unsigned8_t len = map & 0xff;
320
321 // Check the PDO length.
322 if (__unlikely(offset + len > CAN_MAX_LEN * 8))
323 return CO_SDO_AC_PDO_LEN;
324
325 // Check whether the sub-object exists and can be mapped into a
326 // PDO.
327 ac = co_dev_chk_tpdo(dev, idx, subidx);
328 if (__unlikely(ac))
329 return ac;
330
331 // Upload the value of the sub-object and copy the value.
332 co_sdo_req_clear(req);
333 ac = co_sub_up_ind(co_dev_find_sub(dev, idx, subidx), req);
334 if (__unlikely(ac))
335 return ac;
336 if (__unlikely(!co_sdo_req_first(req) || !co_sdo_req_last(req)))
337 return CO_SDO_AC_PDO_LEN;
338 if (buf && pn && offset + len <= *pn * 8)
339 bcpyle(buf, offset, req->buf, 0, len);
340
341 offset += len;
342 }
343
344 if (pn)
345 *pn = (offset + 7) / 8;
346
347 return ac;
348}
349
350static co_unsigned32_t
351co_dev_cfg_pdo_comm(const co_dev_t *dev, co_unsigned16_t idx,
352 const struct co_pdo_comm_par *par)
353{
354 assert(dev);
355 assert(par);
356
357 co_unsigned32_t ac = 0;
358
359 co_obj_t *obj = co_dev_find_obj(dev, idx);
360 if (__unlikely(!obj))
361 return CO_SDO_AC_NO_OBJ;
362
363 // Check if all sub-objects are available.
364 co_unsigned8_t n = co_obj_get_val_u8(obj, 0x00);
365 if (__unlikely(par->n > n))
366 return CO_SDO_AC_NO_SUB;
367
368 // Configure the COB-ID.
369 if (par->n >= 1 && !ac) {
370 co_sub_t *sub = co_obj_find_sub(obj, 0x01);
371 if (__unlikely(!sub))
372 return CO_SDO_AC_NO_SUB;
374 }
375
376 // Configure the transmission type.
377 if (par->n >= 2 && !ac) {
378 co_sub_t *sub = co_obj_find_sub(obj, 0x02);
379 if (__unlikely(!sub))
380 return CO_SDO_AC_NO_SUB;
382 }
383
384 // Configure the inhibit time.
385 if (par->n >= 3 && !ac) {
386 co_sub_t *sub = co_obj_find_sub(obj, 0x03);
387 if (__unlikely(!sub))
388 return CO_SDO_AC_NO_SUB;
390 sub, CO_DEFTYPE_UNSIGNED16, &par->inhibit);
391 }
392
393 // Configure the event timer.
394 if (par->n >= 5 && !ac) {
395 co_sub_t *sub = co_obj_find_sub(obj, 0x05);
396 if (__unlikely(!sub))
397 return CO_SDO_AC_NO_SUB;
399 }
400
401 // Configure the SYNC start value.
402 if (par->n >= 6 && !ac) {
403 co_sub_t *sub = co_obj_find_sub(obj, 0x06);
404 if (__unlikely(!sub))
405 return CO_SDO_AC_NO_SUB;
407 }
408
409 return ac;
410}
411
412static co_unsigned32_t
413co_dev_cfg_pdo_map(const co_dev_t *dev, co_unsigned16_t idx,
414 const struct co_pdo_map_par *par)
415{
416 assert(dev);
417 assert(par);
418
419 co_unsigned32_t ac = 0;
420
421 co_obj_t *obj = co_dev_find_obj(dev, idx);
422 if (__unlikely(!obj))
423 return CO_SDO_AC_NO_OBJ;
424
425 co_sub_t *sub_00 = co_obj_find_sub(obj, 0x00);
426 if (__unlikely(!sub_00))
427 return CO_SDO_AC_NO_SUB;
428 // Disable mapping by setting subindex 0x00 to zero.
430 sub_00, CO_DEFTYPE_UNSIGNED8, &(co_unsigned8_t){ 0 });
431 if (__unlikely(ac))
432 return ac;
433
434 // Copy the mapping parameters.
435 for (co_unsigned8_t i = 1; i <= par->n; i++) {
436 co_sub_t *sub = co_obj_find_sub(obj, i);
437 if (__unlikely(!sub))
438 return CO_SDO_AC_NO_SUB;
440 sub, CO_DEFTYPE_UNSIGNED32, &par->map[i - 1]);
441 if (__unlikely(ac))
442 return ac;
443 }
444
445 // Enable mapping.
446 return co_sub_dn_ind_val(sub_00, CO_DEFTYPE_UNSIGNED8, &par->n);
447}
This header file is part of the CANopen library; it contains the device description declarations.
co_obj_t * co_dev_find_obj(const co_dev_t *dev, co_unsigned16_t idx)
Finds an object in the object dictionary of a CANopen device.
Definition: dev.c:279
co_sub_t * co_dev_find_sub(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Finds a sub-object in the object dictionary of a CANopen device.
Definition: dev.c:290
co_unsigned32_t co_dev_get_dummy(const co_dev_t *dev)
Returns the data types supported by a CANopen device for mapping dummy entries in PDOs (one bit for e...
Definition: dev.c:513
This header file is part of the utilities library; it contains the byte order (endianness) function d...
void bcpyle(void *dst, int dstbit, const void *src, int srcbit, size_t n)
Copies n bits from a source to a destination buffer.
Definition: endian.c:128
void stle_u64(void *ptr, uint_least64_t x)
Stores a 64-bit unsigned integer in little-endian byte order.
Definition: endian.h:588
uint_least64_t ldle_u64(const void *ptr)
Loads a 64-bit unsigned integer in little-endian byte order.
Definition: endian.h:595
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
This header file is part of the CANopen library; it contains the object dictionary declarations.
#define CO_ACCESS_READ
The object can be read.
Definition: obj.h:57
co_unsigned32_t co_sub_dn_ind(co_sub_t *sub, struct co_sdo_req *req)
Invokes the download indication function of a CANopen sub-object, registered with co_sub_set_dn_ind()...
Definition: obj.c:794
co_unsigned32_t co_sub_up_ind(const co_sub_t *sub, struct co_sdo_req *req)
Invokes the upload indication function of a CANopen sub-object, registered with co_sub_set_up_ind().
Definition: obj.c:890
co_unsigned32_t co_sub_dn_ind_val(co_sub_t *sub, co_unsigned16_t type, const void *val)
Invokes the download indication function of a CANopen sub-object, registered with co_sub_set_dn_ind()...
Definition: obj.c:810
#define CO_ACCESS_RPDO
The object can be mapped to an RPDO.
Definition: obj.h:66
int co_sub_get_pdo_mapping(const co_sub_t *sub)
Returns 1 if it is possible to map the specified CANopen sub-object into a PDO, and 0 if not.
Definition: obj.c:706
co_sub_t * co_obj_find_sub(const co_obj_t *obj, co_unsigned8_t subidx)
Finds a sub-object in a CANopen object.
Definition: obj.c:207
#define CO_ACCESS_TPDO
The object can be mapped to a TPDO.
Definition: obj.h:63
#define CO_ACCESS_WRITE
The object can be written.
Definition: obj.h:60
unsigned int co_sub_get_access(const co_sub_t *sub)
Returns the access type of a CANopen sub-object.
Definition: obj.c:682
This header file is part of the CANopen library; it contains the Service Data Object (SDO) declaratio...
int co_sdo_req_first(const struct co_sdo_req *req)
Returns 1 if the specified request includes the first segment, and 0 otherwise.
Definition: sdo.h:340
#define CO_SDO_AC_NO_READ
SDO abort code: Attempt to read a write only object.
Definition: sdo.h:87
#define CO_SDO_AC_NO_OBJ
SDO abort code: Object does not exist in the object dictionary.
Definition: sdo.h:93
void co_sdo_req_clear(struct co_sdo_req *req)
Clears a CANopen SDO upload/download request, including its buffer.
Definition: sdo.c:129
#define CO_SDO_AC_NO_SUB
SDO abort code: Sub-index does not exist.
Definition: sdo.h:132
int co_sdo_req_last(const struct co_sdo_req *req)
Returns 1 if the specified request includes the last segment, and 0 otherwise.
Definition: sdo.h:346
#define CO_SDO_AC_PDO_LEN
SDO abort code: The number and length of the objects to be mapped would exceed the PDO length.
Definition: sdo.h:102
#define CO_SDO_AC_NO_PDO
SDO abort code: Object cannot be mapped to the PDO.
Definition: sdo.h:96
#define CO_SDO_AC_NO_WRITE
SDO abort code: Attempt to write a read only object.
Definition: sdo.h:90
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
This header file is part of the CAN library; it contains the CAN frame declarations.
#define CAN_MAX_LEN
The maximum number of bytes in the payload of a CAN format frame.
Definition: msg.h:73
co_unsigned32_t co_dev_chk_rpdo(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Checks if the specified object is valid and can be mapped into a Receive-PDO.
Definition: pdo.c:42
co_unsigned32_t co_dev_cfg_rpdo_comm(const co_dev_t *dev, co_unsigned16_t num, const struct co_pdo_comm_par *par)
Configures the communication parameters of a Receive-PDO service by updating CANopen object 1400 - 15...
Definition: pdo.c:100
co_unsigned32_t co_dev_cfg_tpdo_comm(const co_dev_t *dev, co_unsigned16_t num, const struct co_pdo_comm_par *par)
Configures the communication parameters of a Transmit-PDO service by updating CANopen object 1800 - 1...
Definition: pdo.c:172
co_unsigned32_t co_dev_cfg_rpdo(const co_dev_t *dev, co_unsigned16_t num, const struct co_pdo_comm_par *comm, const struct co_pdo_map_par *map)
Configures the communication and parameters of a Receive-PDO service.
Definition: pdo.c:74
co_unsigned32_t co_pdo_up(const struct co_pdo_map_par *par, const co_dev_t *dev, struct co_sdo_req *req, uint8_t *buf, size_t *pn)
Reads mapped PDO values from the object dictionary through a local SDO upload request.
Definition: pdo.c:305
co_unsigned32_t co_dev_cfg_tpdo(const co_dev_t *dev, co_unsigned16_t num, const struct co_pdo_comm_par *comm, const struct co_pdo_map_par *map)
Configures the communication and parameters of a Transmit-PDO service.
Definition: pdo.c:146
co_unsigned32_t co_dev_chk_tpdo(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Checks if the specified object is valid and can be mapped into a Transmit-PDO.
Definition: pdo.c:120
co_unsigned32_t co_pdo_unmap(const struct co_pdo_map_par *par, const uint8_t *buf, size_t n, co_unsigned64_t *val, co_unsigned8_t *pn)
Unmaps a PDO into its constituent values.
Definition: pdo.c:223
co_unsigned32_t co_pdo_dn(const struct co_pdo_map_par *par, co_dev_t *dev, struct co_sdo_req *req, const uint8_t *buf, size_t n)
Writes mapped PDO values to the object dictionary through a local SDO download request.
Definition: pdo.c:254
co_unsigned32_t co_dev_cfg_tpdo_map(const co_dev_t *dev, co_unsigned16_t num, const struct co_pdo_map_par *par)
Configures the mapping parameters of a Transmit-PDO service by updating CANopen object 1A00 - 1BFF (T...
Definition: pdo.c:182
co_unsigned32_t co_dev_cfg_rpdo_map(const co_dev_t *dev, co_unsigned16_t num, const struct co_pdo_map_par *par)
Configures the mapping parameters of a Receive-PDO service by updating CANopen object 1600 - 17FF (RP...
Definition: pdo.c:110
co_unsigned32_t co_pdo_map(const struct co_pdo_map_par *par, const co_unsigned64_t *val, co_unsigned8_t n, uint8_t *buf, size_t *pn)
Maps values into a PDO.
Definition: pdo.c:192
This header file is part of the CANopen library; it contains the Process Data Object (PDO) declaratio...
#define CO_PDO_COBID_VALID
The bit in the PDO COB-ID specifying whether the PDO exists and is valid.
Definition: pdo.h:28
This is the internal header file of the CANopen library.
A CANopen device.
Definition: dev.c:38
A CANopen object.
Definition: obj.h:32
A CANopen sub-object.
Definition: obj.h:54
A PDO communication parameter record.
Definition: pdo.h:43
co_unsigned8_t sync
SYNC start value.
Definition: pdo.h:56
co_unsigned16_t inhibit
Inhibit time.
Definition: pdo.h:51
co_unsigned16_t event
Event timer.
Definition: pdo.h:54
co_unsigned32_t cobid
COB-ID.
Definition: pdo.h:47
co_unsigned8_t trans
Transmission type.
Definition: pdo.h:49
co_unsigned8_t n
Highest sub-index supported.
Definition: pdo.h:45
A PDO mapping parameter record.
Definition: pdo.h:69
co_unsigned8_t n
Number of mapped objects in PDO.
Definition: pdo.h:71
co_unsigned32_t map[0x40]
An array of objects to be mapped.
Definition: pdo.h:73
A CANopen SDO upload/download request.
Definition: sdo.h:178
size_t size
The total size (in bytes) of the value to be uploaded/downloaded.
Definition: sdo.h:184
const void * buf
A pointer to the next bytes to be uploaded/downloaded.
Definition: sdo.h:186
size_t nbyte
The number of bytes available at buf.
Definition: sdo.h:188
#define CO_DEFTYPE_UNSIGNED16
The data type (and object index) of a 16-bit unsigned integer.
Definition: type.h:47
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
int co_type_is_basic(co_unsigned16_t type)
Returns 1 if the specified (static) data type is a basic type, and 0 if not.
Definition: type.c:28