Tool Libs
UMIT-TIROL Institute of Automation and Control Engineering library collection
 
Loading...
Searching...
No Matches
canopen.h
1#pragma once
2#include "can.h"
3#include <utils/deadline.h>
4#include <core/kern.h>
5
6
7namespace CAN {
9namespace Open {
11enum class NMT : uint8_t {
12 START = 1,
13 STOP = 2,
14 PREOP = 0x80,
15 RESET_APP = 0x81,
16 RESET_COMM = 0x82,
17};
19enum class STATE : uint8_t {
20 STOPPED = 4,
21 OPERATIONAL = 5,
22 PREOP = 0x7f,
23};
29struct SDO {
30 uint32_t data;
31 uint16_t ix;
32 uint8_t sub;
33 uint8_t cmd;
34 uint8_t nodeID;
35
36 Message toMessage() {
37 return {
38 .data = cmd | ix << 8 | sub << 24 | (uint64_t)data << 32,
39 .id = (uint32_t)0x600 + nodeID,
40 .opts = { .rtr = 0, .ide = 0, .dlc = 8},
41 };
42 }
43 static SDO fromMessage(Message msg) {
44 return {
45 .data = (uint32_t)(msg.data >> 32),
46 .ix = (uint16_t)(msg.data >> 8),
47 .sub = (uint8_t)(msg.data >> 24),
48 .cmd = (uint8_t)msg.data,
49 .nodeID = (uint8_t)(msg.id % 0x80),
50 };
51 }
52};
54struct PDOMap {
55 uint16_t ix;
56 uint8_t sub;
57 uint8_t len;
58 int32_t data;
59};
61enum PDOType : uint8_t {
63 CYCLIC=254,
64 CHANGE=255,
65};
67struct RPDO {
68 uint8_t N;
70 uint32_t COB {};
72 Message toMessage() const {
73 uint64_t data = 0;
74 uint8_t shift = 0;
75 for (auto &i : map) {
76 data |= (i.data & (((uint64_t)1 << i.len)-1)) << shift;
77 shift += i.len;
78 }
79 return {.data=data, .id = COB, .opts = {.dlc=(uint8_t)(shift / 8)}};
80 }
81};
83struct TPDO {
84 uint8_t N;
86 uint32_t COB {};
87 uint32_t inhibit_time;
89 void receive(uint64_t d) {
90 uint8_t shift = 0;
91 for (auto &i : map) {
92 i.data = d >> shift & (((uint64_t)1 << i.len) - 1);
93 shift += i.len;
94 }
95 }
96};
97
108struct Dispatch : Sink<SDO>, Sink<Message> {
109 enum LOGLEVEL { NONE, WARN, INFO} log;
110 Dispatch(CAN &can, LOGLEVEL=NONE) : can(can) { }
111 CAN &can;
112 Sink<SDO> *ids[128] {};
113 struct PDO {
114 Sink<TPDO> *dev[8] {};
115 TPDO *pdo[8] {};
116 uint8_t n {};
117 } pdo;
118
120 void nmt(NMT command, uint8_t nodeid) {
121 can.trypush({.data = (uint64_t)(nodeid & 0x7f) << 8 | (uint8_t)command,
122 .id = 0, .opts = {.dlc = 2}});
123 }
125 void sync() {
126 can.trypush({.id = 0x80, .opts = {.dlc = 0}});
127 }
129 void heartbeat(uint8_t id, STATE state) {
130 can.trypush({.data = (uint8_t)state, .id = (uint32_t)0x700 + id, .opts = {.dlc = 1}});
131 }
133 void guard(uint8_t id) {
134 can.trypush({.id = (uint32_t)0x700 + id, .opts = {.rtr = true}});
135 }
137 void process() {
138 // TODO: what to do to CAN messages that _aren't_ CanOpen
139 while (!can.empty()) {
140 auto msg = can.pop();
141 if (handlePDO(msg)) continue;
142 if (handleSDO(msg)) continue;
143 // don't know what to do with received message!
144 if (log > NONE) k.log.warn("unhandled: [id: %x] [%02x %02x %02x %02x %02x %02x %02x %02x]\n",
145 msg.id,
146 (uint8_t)(msg.data),
147 (uint8_t)(msg.data>>8),
148 (uint8_t)(msg.data>>16),
149 (uint8_t)(msg.data>>24),
150 (uint8_t)(msg.data>>32),
151 (uint8_t)(msg.data>>40),
152 (uint8_t)(msg.data>>48),
153 (uint8_t)(msg.data>>56)
154 );
155 }
156 }
157
158 bool full() override {
159 return can.full();
160 }
161 using Sink<SDO>::push;
162 void push(SDO &&rq) override {
163 //assert(ids[rq.nodeID] != nullptr); //must register first!
164 if (ids[rq.nodeID] == nullptr) {
165 if (log > NONE) k.log.warn("ID [0x%x] NOT registered!\n", rq.nodeID);
166 return;
167 }
168 can.push(rq.toMessage());
169 }
170 using Sink<Message>::push;
171 void push(Message &&rq) override { can.push(rq); }
172
173 void registerSDO(uint16_t nodeID, Sink<SDO> *dev) {
174 assert(nodeID != 0);
175 assert(ids[nodeID] == nullptr);
176 ids[nodeID] = dev;
177 }
178 void registerPDO(TPDO *tpdo, Sink<TPDO> *dev) {
179 for (uint8_t i = 0; i < pdo.n; i++) {
180 if (pdo.pdo[i] == tpdo) {
181 assert(pdo.dev[i] == dev);
182 return;
183 }
184 }
185 assert ( pdo.n < 8 );
186 pdo.dev[pdo.n] = dev;
187 pdo.pdo[pdo.n] = tpdo;
188 pdo.n += 1;
189 }
190
191 bool handlePDO(Message msg) {
192 for (int i = 0; i < pdo.n; ++i) {
193 auto tpdo = pdo.pdo[i];
194 if (tpdo->COB == msg.id
195 && msg.opts.dlc) {
196 tpdo->receive(msg.data);
197 pdo.dev[i]->push(*tpdo);
198 if (log > WARN) {
199 k.log.info("handled PDO id: %x\n", msg.id);
200 k.log.info(" data: %x\n", msg.data);
201 k.log.info(" TPDO data0: %x\n",tpdo->map[0].data);
202 k.log.info(" TPDO data1: %x\n",tpdo->map[1].data);
203 }
204 return true;
205 }
206 }
207 return false;
208 }
209 bool handleSDO(Message msg) {
210 uint16_t service = msg.id & ~0x7f;
211 uint8_t id = msg.id & 0x7f;
212 if (id == 0) return false;
213 if (//service == 0x80 || // SYNC
214 service == 0x580 || // SDO server-to-client
215 //service == 0x600 || // SDO client-to-server
216 //service == 0x700 || // NMT control
217 false) {
218 if (ids[id]) {
219 ids[id]->push(SDO::fromMessage(msg));
220 }
221 if (log > WARN) k.log.info("handled SDO id: %x\n", msg.id);
222 return true;
223 }
224 return false;
225 }
226};
227
232struct Device : Sink<TPDO>, Sink<SDO> {
234 Device(Dispatch &canopen, uint8_t id) : id(id), out(canopen)
235 {
236 assert(id > 0 && id < 128);
237 canopen.registerSDO(id, this);
238 }
239 uint8_t const id {};
240 Dispatch &out;
241
242 struct State {
243 Queue<SDO> q{60};
244 Deadline next{};
245 } state;
247 virtual void callback(SDO rq) { assert(false); };
249 virtual void callback(TPDO rq) { assert(false); };
251 void read(uint16_t ix, uint8_t sub) {
252 pushorqueue({.ix=ix, .sub=sub, .cmd=0x40, .nodeID=id});
253 }
255 void w8(uint16_t ix, uint8_t sub, uint8_t val) {
256 pushorqueue({.data=(uint32_t)val, .ix=ix, .sub=sub, .cmd=0x2f, .nodeID=id});
257 }
259 void w16(uint16_t ix, uint8_t sub, uint16_t val) {
260 pushorqueue({.data=(uint32_t)val, .ix=ix, .sub=sub, .cmd=0x2b, .nodeID=id});
261 }
263 void w32(uint16_t ix, uint8_t sub, uint32_t val) {
264 pushorqueue({.data=(uint32_t)val, .ix=ix, .sub=sub, .cmd=0x23, .nodeID=id});
265 }
267 void wPDO(const RPDO &rpdo) {
268 (*(Sink<Message>*)&out).trypush(rpdo.toMessage());
269 }
271 void enablePDO(RPDO &pdo) {
272 if (pdo.COB == 0) pdo.COB = id + 0x100 * (pdo.N+1);
273 disablePDO(pdo);
274 w8(0x1400+pdo.N-1, 0x2, pdo.type); // set transmission type
275 w8(0x1600+pdo.N-1, 0x0, 0); // clear number of mapped objects
276 for (size_t i = 0; i < pdo.map.len; ++i) {
277 auto &d = pdo.map[i];
278 w32(0x1600+pdo.N-1, i+1, d.ix << 16 | d.sub << 8 | d.len);
279 }
280 w8(0x1600+pdo.N-1, 0x0, pdo.map.len); // set number of mapped objects
281 w32(0x1400+pdo.N-1, 0x1, 1<<30 | pdo.COB);
282 }
284 void disablePDO(RPDO &pdo) {
285 w32(0x1400+pdo.N-1, 0x1, 1<<31); // clear
286 }
288 void enablePDO(TPDO &pdo) {
289 if (pdo.COB == 0) pdo.COB = id + 0x80 + 0x100 * pdo.N;
290 disablePDO(pdo);
291 w8(0x1800+pdo.N-1, 0x2, pdo.type);
292 w16(0x1800+pdo.N-1, 0x3, pdo.inhibit_time);
293 /* w16(0x1800+pdo.N-1, 0x5, pdo.event_timer); */
294 w8(0x1a00+pdo.N-1, 0x0, 0); // clear number of mapped objects
295 for (size_t i = 0; i < pdo.map.len; ++i) {
296 auto &d = pdo.map[i];
297 w32(0x1a00+pdo.N-1, i+1, d.ix << 16 | d.sub << 8 | d.len);
298 }
299 w8(0x1a00+pdo.N-1, 0x0, pdo.map.len); // set number of mapped objects
300 w32(0x1800+pdo.N-1, 0x1, 1<<30 | pdo.COB); // enable PDO
301 out.registerPDO(&pdo, this);
302 };
304 void disablePDO(TPDO &pdo) {
305 w32(0x1800+pdo.N-1, 0x1, 1<<31); // clear
306 };
307 bool full() override {
308 return (*(Sink<Message>*)&out).full();
309 }
310 using Sink<SDO>::push;
311 void push(SDO &&sdo) override {
312 state.next = {0};
313 callback(std::move(sdo));
314 process();
315 }
316 using Sink<TPDO>::push;
317 void push(TPDO &&pdo) override {
318 callback(std::move(pdo));
319 process();
320 }
321 void process() {
322 if (state.q.empty()) return;
323 if (state.next.when && !state.next(k.time)) return;
324 out.push(state.q.pop());
325 state.next = {k.time + 2};
326 }
327 void pushorqueue(SDO &&sdo) {
328 if (state.next.when && !state.next(k.time)) {
329 state.q.trypush(std::move(sdo));
330 } else {
331 (*(Sink<SDO>*)&out).trypush(std::move(sdo));
332 state.next = {k.time + 2};
333 }
334 }
335};
336}
337}
const uint32_t & time
const access to global uptime
Definition kern.h:30
simple Buffer backed queue implementation
Definition queue.h:12
Copyright (c) 2023 IACE.
Copyright (c) 2023 IACE.
int32_t data
received data // data to send
Definition canopen.h:58
STATE
CANOpen Device states.
Definition canopen.h:19
@ STOPPED
STOPPED.
@ OPERATIONAL
OPERATIONAL.
uint8_t sub
subindex of Data Object
Definition canopen.h:56
uint16_t ix
index of Data Object
Definition canopen.h:55
uint8_t len
length of object [bits] {8, 16, 32}
Definition canopen.h:57
PDOType
Type of PDO transmission.
Definition canopen.h:61
@ SYNC
synchronous == on every NMT SYNC message
Definition canopen.h:62
@ CYCLIC
cyclic == after inhibit_time
Definition canopen.h:63
@ CHANGE
change == ASAP on change of parameters
Definition canopen.h:64
NMT
CANOpen Network ManagemenT Commands.
Definition canopen.h:11
@ STOP
enter STOPPED 0x4
@ PREOP
enter PREOP 0x7f
@ RESET_COMM
... -> PREOP
@ RESET_APP
... -> PREOP
@ START
enter OPERATIONAL 0x5
Single Entry in PDO Data List.
Definition canopen.h:54
dynamically allocated, but fixed-size buffer template
Definition buffer.h:18
CANOpen device wrapper class.
Definition canopen.h:232
void read(uint16_t ix, uint8_t sub)
read DO with given index and subindex of this device
Definition canopen.h:251
void disablePDO(TPDO &pdo)
disable sending PDO on device
Definition canopen.h:304
void wPDO(const RPDO &rpdo)
send PDO
Definition canopen.h:267
void enablePDO(TPDO &pdo)
enable sending PDO on device
Definition canopen.h:288
void w16(uint16_t ix, uint8_t sub, uint16_t val)
write given value to DO at given index and subindex
Definition canopen.h:259
virtual void callback(TPDO rq)
implement the callback method to handle incoming data
Definition canopen.h:249
void w8(uint16_t ix, uint8_t sub, uint8_t val)
write given value to DO at given index and subindex
Definition canopen.h:255
bool full() override
check if sink is full
Definition canopen.h:307
Device(Dispatch &canopen, uint8_t id)
Construct new CANOpen Device on network with given ID.
Definition canopen.h:234
void w32(uint16_t ix, uint8_t sub, uint32_t val)
write given value to DO at given index and subindex
Definition canopen.h:263
void disablePDO(RPDO &pdo)
disable receiving PDO on device
Definition canopen.h:284
virtual void callback(SDO rq)
implement the callback method to handle incoming data
Definition canopen.h:247
void enablePDO(RPDO &pdo)
enable receiving PDO on device
Definition canopen.h:271
uint8_t const id
CANOpen node ID [1 - 127].
Definition canopen.h:239
CANOpen dispatch class.
Definition canopen.h:108
void heartbeat(uint8_t id, STATE state)
send HEARTBEAT message to node id, signal own state
Definition canopen.h:129
void nmt(NMT command, uint8_t nodeid)
send NMT command to nodeid, rsp broadcast it (id = 0)
Definition canopen.h:120
void sync()
send SYNC message on network
Definition canopen.h:125
bool full() override
check if sink is full
Definition canopen.h:158
void guard(uint8_t id)
send GUARD message to node id
Definition canopen.h:133
void process()
call directly after CAN interrupt handler processed
Definition canopen.h:137
CANOpen Receive Process Data Object Type.
Definition canopen.h:67
uint32_t COB
CAN OBject ID – leave empty for defaults.
Definition canopen.h:70
uint8_t N
Index of PDO [1 .. x].
Definition canopen.h:68
PDOType type
reception type
Definition canopen.h:69
Buffer< PDOMap > map
PDO Data Map.
Definition canopen.h:71
CANOpen Service Data Object xfer request.
Definition canopen.h:29
uint16_t ix
index of DO
Definition canopen.h:31
uint8_t cmd
type of request
Definition canopen.h:33
uint8_t sub
subindex of DO
Definition canopen.h:32
uint8_t nodeID
node ID of device
Definition canopen.h:34
uint32_t data
up to 4 bytes of data
Definition canopen.h:30
CANOpen Transmit Process Data Object Type.
Definition canopen.h:83
uint8_t N
Index of PDO [1 .. x].
Definition canopen.h:84
uint32_t COB
CAN OBject ID – leave empty for defaults.
Definition canopen.h:86
uint32_t inhibit_time
inhibit time in 0.1ms steps
Definition canopen.h:87
Buffer< PDOMap > map
PDO Data Map.
Definition canopen.h:88
PDOType type
transmission type
Definition canopen.h:85
simple solution for keeping track of timeouts
Definition deadline.h:15
generic object sink, i.e.
Definition streams.h:15
virtual void push(const T &t)
copy semantics does not check for space. guard by using 'if (!full) { ... }'
Definition streams.h:20