16static void std_frame(
CanFrame *f, uint32_t
id, uint8_t dlc)
25static bool valid_node(uint8_t node_id)
27 return node_id >= 1 && node_id <= 127;
30bool pc_canopen_build_nmt(
CanFrame *out, uint8_t command, uint8_t node_id)
32 if (!out || node_id > 127)
36 std_frame(out, CANOPEN_COB_NMT, 2);
37 out->
data[0] = command;
38 out->
data[1] = node_id;
42bool pc_canopen_build_sync(
CanFrame *out)
48 std_frame(out, CANOPEN_COB_SYNC, 0);
52bool pc_canopen_build_time(
CanFrame *out, uint32_t ms_since_midnight, uint16_t days_since_1984)
58 std_frame(out, CANOPEN_COB_TIME, CANOPEN_TIME_LEN);
59 uint32_t ms = ms_since_midnight & CANOPEN_TIME_MS_MASK;
60 out->
data[0] = (uint8_t)ms;
61 out->
data[1] = (uint8_t)(ms >> 8);
62 out->
data[2] = (uint8_t)(ms >> 16);
63 out->
data[3] = (uint8_t)(ms >> 24);
64 out->
data[4] = (uint8_t)days_since_1984;
65 out->
data[5] = (uint8_t)(days_since_1984 >> 8);
69bool pc_canopen_build_heartbeat(
CanFrame *out, uint8_t node_id, uint8_t state)
71 if (!out || !valid_node(node_id))
75 std_frame(out, CANOPEN_COB_HEARTBEAT + node_id, 1);
80bool pc_canopen_build_emcy(
CanFrame *out, uint8_t node_id, uint16_t error_code, uint8_t error_reg,
81 const uint8_t msef[5])
83 if (!out || !valid_node(node_id))
87 std_frame(out, CANOPEN_COB_EMCY + node_id, 8);
88 out->
data[0] = (uint8_t)error_code;
89 out->
data[1] = (uint8_t)(error_code >> 8);
90 out->
data[2] = error_reg;
93 memcpy(out->
data + 3, msef, 5);
99static bool pdo_base(uint8_t pdo_num,
bool transmit, uint32_t *base)
101 if (pdo_num < 1 || pdo_num > 4)
105 static const uint32_t tx[4] = {CANOPEN_COB_TPDO1, CANOPEN_COB_TPDO2, CANOPEN_COB_TPDO3, CANOPEN_COB_TPDO4};
106 static const uint32_t rx[4] = {CANOPEN_COB_RPDO1, CANOPEN_COB_RPDO2, CANOPEN_COB_RPDO3, CANOPEN_COB_RPDO4};
107 *base = (transmit ? tx : rx)[pdo_num - 1];
111static bool build_pdo(
CanFrame *out, uint8_t pdo_num,
bool transmit, uint8_t node_id,
const uint8_t *data, uint8_t len)
114 if (!out || !valid_node(node_id) || len >
PC_CAN_MAX_DLC || (len && !data) || !pdo_base(pdo_num, transmit, &base))
118 std_frame(out, base + node_id, len);
121 memcpy(out->
data, data, len);
126bool pc_canopen_build_tpdo(
CanFrame *out, uint8_t pdo_num, uint8_t node_id,
const uint8_t *data, uint8_t len)
128 return build_pdo(out, pdo_num,
true, node_id, data, len);
131bool pc_canopen_build_rpdo(
CanFrame *out, uint8_t pdo_num, uint8_t node_id,
const uint8_t *data, uint8_t len)
133 return build_pdo(out, pdo_num,
false, node_id, data, len);
137static void sdo_set_object(
CanFrame *f, uint16_t index, uint8_t sub)
139 f->
data[1] = (uint8_t)index;
140 f->
data[2] = (uint8_t)(index >> 8);
144bool pc_canopen_build_sdo_read(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub)
146 if (!out || !valid_node(node_id))
150 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
151 out->
data[0] = (uint8_t)(CANOPEN_SDO_CCS_UPLOAD << 5);
152 sdo_set_object(out, index, sub);
156bool pc_canopen_build_sdo_write(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub,
const uint8_t *data,
159 if (!out || !valid_node(node_id) || len < 1 || len > 4 || !data)
163 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
165 out->
data[0] = (uint8_t)((CANOPEN_SDO_CCS_DOWNLOAD << 5) | (((4u - len) & 3u) << 2) | 0x03u);
166 sdo_set_object(out, index, sub);
167 memcpy(out->
data + 4, data, len);
171bool pc_canopen_build_sdo_abort(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub, uint32_t abort_code,
174 if (!out || !valid_node(node_id))
178 std_frame(out, (to_server ? CANOPEN_COB_SDO_RX : CANOPEN_COB_SDO_TX) + node_id, 8);
179 out->
data[0] = (uint8_t)(CANOPEN_SDO_ABORT << 5);
180 sdo_set_object(out, index, sub);
181 out->
data[4] = (uint8_t)abort_code;
182 out->
data[5] = (uint8_t)(abort_code >> 8);
183 out->
data[6] = (uint8_t)(abort_code >> 16);
184 out->
data[7] = (uint8_t)(abort_code >> 24);
188bool pc_canopen_parse(
const CanFrame *f, CanopenMsg *out)
195 uint32_t func =
id & CANOPEN_FUNC_MASK;
196 uint8_t node = (uint8_t)(
id & CANOPEN_NODE_MASK);
197 out->type = CanopenType::CANOPEN_T_UNKNOWN;
201 if (
id == CANOPEN_COB_NMT)
203 out->type = CanopenType::CANOPEN_T_NMT;
207 if (
id == CANOPEN_COB_SYNC)
209 out->type = CanopenType::CANOPEN_T_SYNC;
213 if (
id == CANOPEN_COB_TIME)
215 out->type = CanopenType::CANOPEN_T_TIME;
226 case CANOPEN_COB_EMCY:
227 out->type = CanopenType::CANOPEN_T_EMCY;
229 case CANOPEN_COB_TPDO1:
230 out->type = CanopenType::CANOPEN_T_TPDO;
233 case CANOPEN_COB_RPDO1:
234 out->type = CanopenType::CANOPEN_T_RPDO;
237 case CANOPEN_COB_TPDO2:
238 out->type = CanopenType::CANOPEN_T_TPDO;
241 case CANOPEN_COB_RPDO2:
242 out->type = CanopenType::CANOPEN_T_RPDO;
245 case CANOPEN_COB_TPDO3:
246 out->type = CanopenType::CANOPEN_T_TPDO;
249 case CANOPEN_COB_RPDO3:
250 out->type = CanopenType::CANOPEN_T_RPDO;
253 case CANOPEN_COB_TPDO4:
254 out->type = CanopenType::CANOPEN_T_TPDO;
257 case CANOPEN_COB_RPDO4:
258 out->type = CanopenType::CANOPEN_T_RPDO;
261 case CANOPEN_COB_SDO_TX:
262 out->type = CanopenType::CANOPEN_T_SDO_TX;
264 case CANOPEN_COB_SDO_RX:
265 out->type = CanopenType::CANOPEN_T_SDO_RX;
267 case CANOPEN_COB_HEARTBEAT:
268 out->type = CanopenType::CANOPEN_T_HEARTBEAT;
275bool pc_canopen_parse_emcy(
const CanFrame *f, uint8_t *node_id, uint16_t *error_code, uint8_t *error_reg,
283 uint8_t node = (uint8_t)(
id & CANOPEN_NODE_MASK);
284 if ((
id & CANOPEN_FUNC_MASK) != CANOPEN_COB_EMCY || node == 0)
294 *error_code = (uint16_t)(f->
data[0] | (f->
data[1] << 8));
298 *error_reg = f->
data[2];
302 memcpy(msef, f->
data + 3, 5);
307bool pc_canopen_parse_heartbeat(
const CanFrame *f, uint8_t *node_id, uint8_t *state)
314 uint8_t node = (uint8_t)(
id & CANOPEN_NODE_MASK);
315 if ((
id & CANOPEN_FUNC_MASK) != CANOPEN_COB_HEARTBEAT || node == 0)
325 *state = (uint8_t)(f->
data[0] & 0x7Fu);
330bool pc_canopen_parse_time(
const CanFrame *f, CanopenTime *out)
332 if (!f || !out || f->
extended || f->
dlc < CANOPEN_TIME_LEN)
340 uint32_t ms = (uint32_t)f->
data[0] | ((uint32_t)f->
data[1] << 8) | ((uint32_t)f->
data[2] << 16) |
341 ((uint32_t)f->
data[3] << 24);
342 out->ms_since_midnight = ms & CANOPEN_TIME_MS_MASK;
343 out->days_since_1984 = (uint16_t)(f->
data[4] | (f->
data[5] << 8));
347bool pc_canopen_parse_sdo_response(
const CanFrame *f, CanopenSdoResponse *out)
354 if ((
id & CANOPEN_FUNC_MASK) != CANOPEN_COB_SDO_TX || (
id & CANOPEN_NODE_MASK) == 0)
359 uint8_t cmd = f->
data[0];
360 uint8_t scs = (uint8_t)(cmd >> 5);
361 out->index = (uint16_t)(f->
data[1] | (f->
data[2] << 8));
362 out->sub = f->
data[3];
363 out->is_abort =
false;
365 out->is_upload =
false;
366 out->expedited =
false;
368 memset(out->data, 0,
sizeof(out->data));
370 if (scs == CANOPEN_SDO_ABORT)
372 out->is_abort =
true;
373 out->abort_code = (uint32_t)f->
data[4] | ((uint32_t)f->
data[5] << 8) | ((uint32_t)f->
data[6] << 16) |
374 ((uint32_t)f->
data[7] << 24);
377 if (scs == CANOPEN_SDO_SCS_UPLOAD)
379 out->is_upload =
true;
380 bool e = (cmd & 0x02u) != 0;
381 bool s = (cmd & 0x01u) != 0;
384 out->expedited =
true;
385 out->len = s ? (uint8_t)(4u - ((cmd >> 2) & 0x03u)) : 4u;
386 memcpy(out->data, f->
data + 4, out->len);
390 if (scs == CANOPEN_SDO_SCS_DOWNLOAD)
399bool pc_canopen_build_sdo_download_init(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub,
402 if (!out || !valid_node(node_id))
406 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
408 out->
data[0] = (uint8_t)((CANOPEN_SDO_CCS_DOWNLOAD << 5) | 0x01u);
409 sdo_set_object(out, index, sub);
410 out->
data[4] = (uint8_t)total_size;
411 out->
data[5] = (uint8_t)(total_size >> 8);
412 out->
data[6] = (uint8_t)(total_size >> 16);
413 out->
data[7] = (uint8_t)(total_size >> 24);
417bool pc_canopen_build_sdo_download_segment(
CanFrame *out, uint8_t node_id,
bool toggle,
const uint8_t *data,
418 uint8_t len,
bool last)
420 if (!out || !valid_node(node_id) || !data || len < 1 || len > CANOPEN_SDO_SEG_DATA)
424 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
426 uint8_t n = (uint8_t)(CANOPEN_SDO_SEG_DATA - len);
427 out->
data[0] = (uint8_t)((toggle ? 0x10u : 0u) | ((n & 0x07u) << 1) | (last ? 0x01u : 0u));
428 memcpy(out->
data + 1, data, len);
432bool pc_canopen_build_sdo_upload_segment_req(
CanFrame *out, uint8_t node_id,
bool toggle)
434 if (!out || !valid_node(node_id))
438 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
440 out->
data[0] = (uint8_t)((3u << 5) | (toggle ? 0x10u : 0u));
444bool pc_canopen_parse_sdo_segment(
const CanFrame *f,
bool *toggle, uint8_t *data, uint8_t *len,
bool *last)
446 if (!f || f->
dlc < 8 || (f->
data[0] & 0xE0u) != 0u)
450 uint8_t n = (uint8_t)((f->
data[0] >> 1) & 0x07u);
451 uint8_t seg_len = (uint8_t)(CANOPEN_SDO_SEG_DATA - n);
454 *toggle = (f->
data[0] & 0x10u) != 0u;
458 *last = (f->
data[0] & 0x01u) != 0u;
466 memcpy(data, f->
data + 1, seg_len);
471void pc_canopen_sdo_reasm_init(CanopenSdoReasm *r, uint8_t *buf,
size_t cap)
480 r->expect_toggle =
false;
484bool pc_canopen_sdo_reasm_feed(CanopenSdoReasm *r,
const uint8_t *data, uint8_t len,
bool toggle,
bool last)
486 if (!r || !r->buf || r->done || (len && !data))
490 if (toggle != r->expect_toggle)
494 if (len > r->cap - r->len)
500 memcpy(r->buf + r->len, data, len);
503 r->expect_toggle = !r->expect_toggle;
#define PC_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
#define PC_CAN_STD_ID_MASK
11-bit standard identifier.
CANopen (CiA 301) application-layer message codec (PC_ENABLE_CANOPEN).
uint8_t data[PC_CAN_MAX_DLC]