ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
cia402.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file cia402.cpp
6 * @brief CiA 402 drive profile: state machine + Controlword/Statusword + CANopen object access.
7 */
8
10
11#if PC_ENABLE_CIA402
12
13#include <string.h>
14
16
17Cia402State pc_cia402_state(uint16_t sw)
18{
19 // Mask/value table from IEC 61800-7-201 (CiA 402). Order matters where masks differ.
20 if ((sw & 0x4F) == 0x00)
21 {
22 return Cia402State::not_ready_to_switch_on;
23 }
24 if ((sw & 0x4F) == 0x40)
25 {
26 return Cia402State::switch_on_disabled;
27 }
28 if ((sw & 0x6F) == 0x21)
29 {
30 return Cia402State::ready_to_switch_on;
31 }
32 if ((sw & 0x6F) == 0x23)
33 {
34 return Cia402State::switched_on;
35 }
36 if ((sw & 0x6F) == 0x27)
37 {
38 return Cia402State::operation_enabled;
39 }
40 if ((sw & 0x6F) == 0x07)
41 {
42 return Cia402State::quick_stop_active;
43 }
44 if ((sw & 0x4F) == 0x0F)
45 {
46 return Cia402State::fault_reaction_active;
47 }
48 if ((sw & 0x4F) == 0x08)
49 {
50 return Cia402State::fault;
51 }
52 return Cia402State::unknown;
53}
54
55uint16_t pc_cia402_controlword(Cia402Command cmd)
56{
57 switch (cmd)
58 {
59 case Cia402Command::shutdown:
60 return 0x0006; // enable voltage + quick stop, switch-on 0
61 case Cia402Command::switch_on:
62 case Cia402Command::disable_operation:
63 return 0x0007; // switch-on + enable voltage + quick stop
64 case Cia402Command::enable_operation:
65 return 0x000F; // + enable operation
66 case Cia402Command::disable_voltage:
67 return 0x0000;
68 case Cia402Command::quick_stop:
69 return 0x0002; // enable voltage, quick-stop bit cleared (active)
70 case Cia402Command::fault_reset:
71 return 0x0080; // bit 7 rising edge
72 }
73 return 0x0000;
74}
75
76uint16_t pc_cia402_enable_sequence(Cia402State state)
77{
78 switch (state)
79 {
80 case Cia402State::fault:
81 case Cia402State::fault_reaction_active:
82 return pc_cia402_controlword(Cia402Command::fault_reset);
83 case Cia402State::switch_on_disabled:
84 return pc_cia402_controlword(Cia402Command::shutdown);
85 case Cia402State::ready_to_switch_on:
86 return pc_cia402_controlword(Cia402Command::switch_on);
87 case Cia402State::switched_on:
88 case Cia402State::quick_stop_active:
89 case Cia402State::operation_enabled:
90 return pc_cia402_controlword(Cia402Command::enable_operation);
91 default: // not_ready_to_switch_on / unknown: wait, hold voltage off
92 return pc_cia402_controlword(Cia402Command::disable_voltage);
93 }
94}
95
96bool pc_cia402_sdo_set_controlword(CanFrame *out, uint8_t node, uint16_t controlword)
97{
98 uint8_t d[2];
99 pc_wr16le(d, controlword);
100 return pc_canopen_build_sdo_write(out, node, CIA402_OD_CONTROLWORD, 0, d, 2);
101}
102
103bool pc_cia402_sdo_set_mode(CanFrame *out, uint8_t node, Cia402Mode mode)
104{
105 uint8_t d = (uint8_t)(int8_t)mode; // wire byte
106 return pc_canopen_build_sdo_write(out, node, CIA402_OD_MODES_OF_OPERATION, 0, &d, 1);
107}
108
109bool pc_cia402_sdo_set_target_position(CanFrame *out, uint8_t node, int32_t position)
110{
111 uint8_t d[4];
112 pc_wr32le(d, (uint32_t)position);
113 return pc_canopen_build_sdo_write(out, node, CIA402_OD_TARGET_POSITION, 0, d, 4);
114}
115
116bool pc_cia402_sdo_set_target_velocity(CanFrame *out, uint8_t node, int32_t velocity)
117{
118 uint8_t d[4];
119 pc_wr32le(d, (uint32_t)velocity);
120 return pc_canopen_build_sdo_write(out, node, CIA402_OD_TARGET_VELOCITY, 0, d, 4);
121}
122
123bool pc_cia402_sdo_set_target_torque(CanFrame *out, uint8_t node, int16_t torque)
124{
125 uint8_t d[2];
126 pc_wr16le(d, (uint16_t)torque);
127 return pc_canopen_build_sdo_write(out, node, CIA402_OD_TARGET_TORQUE, 0, d, 2);
128}
129
130bool pc_cia402_sdo_read(CanFrame *out, uint8_t node, uint16_t index, uint8_t sub)
131{
132 return pc_canopen_build_sdo_read(out, node, index, sub);
133}
134
135// Validate an expedited SDO upload response and copy its inline payload into @p out (>= need
136// octets). No shared state - the parsed response lives on this call's stack.
137static bool sdo_upload_bytes(const CanFrame *f, uint16_t want_index, uint8_t need, uint8_t *out)
138{
139 CanopenSdoResponse resp;
140 if (!pc_canopen_parse_sdo_response(f, &resp))
141 {
142 return false;
143 }
144 if (resp.is_abort || !resp.is_upload || !resp.expedited || resp.len < need)
145 {
146 return false;
147 }
148 if (want_index != 0 && resp.index != want_index)
149 {
150 return false;
151 }
152 memcpy(out, resp.data, need);
153 return true;
154}
155
156bool pc_cia402_sdo_get_u16(const CanFrame *f, uint16_t want_index, uint16_t *value)
157{
158 uint8_t d[2];
159 if (!value || !sdo_upload_bytes(f, want_index, 2, d))
160 {
161 return false;
162 }
163 *value = pc_rd16le(d);
164 return true;
165}
166
167bool pc_cia402_sdo_get_i32(const CanFrame *f, uint16_t want_index, int32_t *value)
168{
169 uint8_t d[4];
170 if (!value || !sdo_upload_bytes(f, want_index, 4, d))
171 {
172 return false;
173 }
174 *value = (int32_t)pc_rd32le(d);
175 return true;
176}
177
178size_t pc_cia402_pack_command(uint8_t *buf, size_t cap, uint16_t controlword, int32_t target)
179{
180 if (!buf || cap < 6)
181 {
182 return 0;
183 }
184 size_t p = pc_wr16le(buf, controlword);
185 p += pc_wr32le(buf + p, (uint32_t)target);
186 return p; // 6
187}
188
189bool pc_cia402_unpack_status(const uint8_t *buf, size_t len, uint16_t *statusword, int32_t *actual)
190{
191 if (!buf || !statusword || !actual || len < 6)
192 {
193 return false;
194 }
195 *statusword = pc_rd16le(buf);
196 *actual = (int32_t)pc_rd32le(buf + 2);
197 return true;
198}
199
200#endif // PC_ENABLE_CIA402
CiA 402 / IEC 61800-7-201 drive + motion profile (PC_ENABLE_CIA402) over CANopen.
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
uint32_t pc_rd32le(const uint8_t *p)
Read a little-endian u32 at p.
Definition endian.h:66
size_t pc_wr32le(uint8_t *p, uint32_t v)
Write v little-endian at p.
Definition endian.h:40
uint16_t pc_rd16le(const uint8_t *p)
Read a little-endian u16 at p.
Definition endian.h:60
size_t pc_wr16le(uint8_t *p, uint16_t v)
Write v little-endian at p.
Definition endian.h:32
Definition can.h:44