11#if DETWS_ENABLE_CANOPEN
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 canopen_build_nmt(
CanFrame *out, uint8_t command, uint8_t node_id)
32 if (!out || node_id > 127)
34 std_frame(out, CANOPEN_COB_NMT, 2);
35 out->
data[0] = command;
36 out->
data[1] = node_id;
40bool canopen_build_sync(
CanFrame *out)
44 std_frame(out, CANOPEN_COB_SYNC, 0);
48bool canopen_build_heartbeat(
CanFrame *out, uint8_t node_id, uint8_t state)
50 if (!out || !valid_node(node_id))
52 std_frame(out, CANOPEN_COB_HEARTBEAT + node_id, 1);
57bool canopen_build_emcy(
CanFrame *out, uint8_t node_id, uint16_t error_code, uint8_t error_reg,
const uint8_t msef[5])
59 if (!out || !valid_node(node_id))
61 std_frame(out, CANOPEN_COB_EMCY + node_id, 8);
62 out->
data[0] = (uint8_t)error_code;
63 out->
data[1] = (uint8_t)(error_code >> 8);
64 out->
data[2] = error_reg;
66 memcpy(out->
data + 3, msef, 5);
71static bool pdo_base(uint8_t pdo_num,
bool transmit, uint32_t *base)
73 if (pdo_num < 1 || pdo_num > 4)
75 static const uint32_t tx[4] = {CANOPEN_COB_TPDO1, CANOPEN_COB_TPDO2, CANOPEN_COB_TPDO3, CANOPEN_COB_TPDO4};
76 static const uint32_t rx[4] = {CANOPEN_COB_RPDO1, CANOPEN_COB_RPDO2, CANOPEN_COB_RPDO3, CANOPEN_COB_RPDO4};
77 *base = (transmit ? tx : rx)[pdo_num - 1];
81static bool build_pdo(
CanFrame *out, uint8_t pdo_num,
bool transmit, uint8_t node_id,
const uint8_t *data, uint8_t len)
84 if (!out || !valid_node(node_id) || len >
DET_CAN_MAX_DLC || (len && !data) || !pdo_base(pdo_num, transmit, &base))
86 std_frame(out, base + node_id, len);
88 memcpy(out->
data, data, len);
92bool canopen_build_tpdo(
CanFrame *out, uint8_t pdo_num, uint8_t node_id,
const uint8_t *data, uint8_t len)
94 return build_pdo(out, pdo_num,
true, node_id, data, len);
97bool canopen_build_rpdo(
CanFrame *out, uint8_t pdo_num, uint8_t node_id,
const uint8_t *data, uint8_t len)
99 return build_pdo(out, pdo_num,
false, node_id, data, len);
103static void sdo_set_object(
CanFrame *f, uint16_t index, uint8_t sub)
105 f->
data[1] = (uint8_t)index;
106 f->
data[2] = (uint8_t)(index >> 8);
110bool canopen_build_sdo_read(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub)
112 if (!out || !valid_node(node_id))
114 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
115 out->
data[0] = (uint8_t)(CANOPEN_SDO_CCS_UPLOAD << 5);
116 sdo_set_object(out, index, sub);
120bool canopen_build_sdo_write(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub,
const uint8_t *data,
123 if (!out || !valid_node(node_id) || len < 1 || len > 4 || !data)
125 std_frame(out, CANOPEN_COB_SDO_RX + node_id, 8);
127 out->
data[0] = (uint8_t)((CANOPEN_SDO_CCS_DOWNLOAD << 5) | (((4u - len) & 3u) << 2) | 0x03u);
128 sdo_set_object(out, index, sub);
129 memcpy(out->
data + 4, data, len);
133bool canopen_build_sdo_abort(
CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub, uint32_t abort_code,
136 if (!out || !valid_node(node_id))
138 std_frame(out, (to_server ? CANOPEN_COB_SDO_RX : CANOPEN_COB_SDO_TX) + node_id, 8);
139 out->
data[0] = (uint8_t)(CANOPEN_SDO_ABORT << 5);
140 sdo_set_object(out, index, sub);
141 out->
data[4] = (uint8_t)abort_code;
142 out->
data[5] = (uint8_t)(abort_code >> 8);
143 out->
data[6] = (uint8_t)(abort_code >> 16);
144 out->
data[7] = (uint8_t)(abort_code >> 24);
148bool canopen_parse(
const CanFrame *f, CanopenMsg *out)
153 uint32_t func =
id & CANOPEN_FUNC_MASK;
154 uint8_t node = (uint8_t)(
id & CANOPEN_NODE_MASK);
155 out->type = CanopenType::CANOPEN_T_UNKNOWN;
159 if (
id == CANOPEN_COB_NMT)
161 out->type = CanopenType::CANOPEN_T_NMT;
165 if (
id == CANOPEN_COB_SYNC)
167 out->type = CanopenType::CANOPEN_T_SYNC;
171 if (
id == CANOPEN_COB_TIME)
173 out->type = CanopenType::CANOPEN_T_TIME;
182 case CANOPEN_COB_EMCY:
183 out->type = CanopenType::CANOPEN_T_EMCY;
185 case CANOPEN_COB_TPDO1:
186 out->type = CanopenType::CANOPEN_T_TPDO;
189 case CANOPEN_COB_RPDO1:
190 out->type = CanopenType::CANOPEN_T_RPDO;
193 case CANOPEN_COB_TPDO2:
194 out->type = CanopenType::CANOPEN_T_TPDO;
197 case CANOPEN_COB_RPDO2:
198 out->type = CanopenType::CANOPEN_T_RPDO;
201 case CANOPEN_COB_TPDO3:
202 out->type = CanopenType::CANOPEN_T_TPDO;
205 case CANOPEN_COB_RPDO3:
206 out->type = CanopenType::CANOPEN_T_RPDO;
209 case CANOPEN_COB_TPDO4:
210 out->type = CanopenType::CANOPEN_T_TPDO;
213 case CANOPEN_COB_RPDO4:
214 out->type = CanopenType::CANOPEN_T_RPDO;
217 case CANOPEN_COB_SDO_TX:
218 out->type = CanopenType::CANOPEN_T_SDO_TX;
220 case CANOPEN_COB_SDO_RX:
221 out->type = CanopenType::CANOPEN_T_SDO_RX;
223 case CANOPEN_COB_HEARTBEAT:
224 out->type = CanopenType::CANOPEN_T_HEARTBEAT;
231bool canopen_parse_emcy(
const CanFrame *f, uint8_t *node_id, uint16_t *error_code, uint8_t *error_reg, uint8_t msef[5])
236 uint8_t node = (uint8_t)(
id & CANOPEN_NODE_MASK);
237 if ((
id & CANOPEN_FUNC_MASK) != CANOPEN_COB_EMCY || node == 0)
242 *error_code = (uint16_t)(f->
data[0] | (f->
data[1] << 8));
244 *error_reg = f->
data[2];
246 memcpy(msef, f->
data + 3, 5);
250bool canopen_parse_heartbeat(
const CanFrame *f, uint8_t *node_id, uint8_t *state)
255 uint8_t node = (uint8_t)(
id & CANOPEN_NODE_MASK);
256 if ((
id & CANOPEN_FUNC_MASK) != CANOPEN_COB_HEARTBEAT || node == 0)
261 *state = (uint8_t)(f->
data[0] & 0x7Fu);
265bool canopen_parse_sdo_response(
const CanFrame *f, CanopenSdoResponse *out)
270 if ((
id & CANOPEN_FUNC_MASK) != CANOPEN_COB_SDO_TX || (
id & CANOPEN_NODE_MASK) == 0)
273 uint8_t cmd = f->
data[0];
274 uint8_t scs = (uint8_t)(cmd >> 5);
275 out->index = (uint16_t)(f->
data[1] | (f->
data[2] << 8));
276 out->sub = f->
data[3];
277 out->is_abort =
false;
279 out->is_upload =
false;
280 out->expedited =
false;
282 memset(out->data, 0,
sizeof(out->data));
284 if (scs == CANOPEN_SDO_ABORT)
286 out->is_abort =
true;
287 out->abort_code = (uint32_t)f->
data[4] | ((uint32_t)f->
data[5] << 8) | ((uint32_t)f->
data[6] << 16) |
288 ((uint32_t)f->
data[7] << 24);
291 if (scs == CANOPEN_SDO_SCS_UPLOAD)
293 out->is_upload =
true;
294 bool e = (cmd & 0x02u) != 0;
295 bool s = (cmd & 0x01u) != 0;
298 out->expedited =
true;
299 out->len = s ? (uint8_t)(4u - ((cmd >> 2) & 0x03u)) : 4u;
300 memcpy(out->data, f->
data + 4, out->len);
304 if (scs == CANOPEN_SDO_SCS_DOWNLOAD)
#define DET_CAN_STD_ID_MASK
11-bit standard identifier.
#define DET_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
CANopen (CiA 301) application-layer message codec (DETWS_ENABLE_CANOPEN).
uint8_t data[DET_CAN_MAX_DLC]