GCC Code Coverage Report


Directory: ./
File: src/devices/Ex25xx.cpp
Date: 2026-09-23 16:22:15
Exec Total Coverage
Lines: 175 279 62.7%
Branches: 169 1528 11.1%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * This file is part of the ecapp library (EtherCAT application devices).
4 *
5 * Copyright (C) 2026 Florian Pose <fp@igh.de>
6 *
7 * The ecapp library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation, version 3 of the
10 * License.
11 *
12 * The ecapp library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with the ecapp library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 *
21 ****************************************************************************/
22
23 #include "devices/Ex25xx.h"
24
25 #include "SlaveContext.h"
26 #include "ecapp/Domain.h"
27 #include "ecapp/Exceptions.h"
28 #include "ecapp/Factory.h"
29 #include "ecapp/Master.h"
30 #include "ecapp/SwappedSync.h"
31
32 #include <memory>
33 #include <sstream>
34 #include <stdexcept>
35
36 /****************************************************************************/
37
38 using std::logic_error;
39 using std::make_unique;
40 using std::runtime_error;
41 using std::size_t;
42 using std::stringstream;
43 using std::unique_ptr;
44
45 namespace EcApp {
46
47 /* "EL2521 1K. Pulse Train Ausgang" (also -0024/-0025)
48 * Vendor ID: 0x00000002
49 * Product code: 0x09d93052
50 *
51 * "Erweiterter Betriebsmodus", "Standard 16 Bit (MDP 253/511)": PTO
52 * Control (0x1601) + PTO Target compact (0x1604) + ENC Control compact
53 * (0x1602) as outputs; PTO Status (0x1A01) + ENC Status compact (0x1A02)
54 * as inputs. Objects at slot 1 (0x6010/0x7010 PTO, 0x6020/0x7020 ENC) --
55 * 0x6000/0x7000 stay reserved for the "legacy" (normal-mode-compatible)
56 * Status/Data-In, Ctrl/Data-Out pairing, which this driver does not use.
57 */
58
59 ec_pdo_entry_info_t ex2521_pto_control_entries[] = {
60 {0x7010, 0x01, 1}, /* Frequency select */
61 {0x7010, 0x02, 1}, /* Disable ramp */
62 {0x7010, 0x03, 1}, /* Go counter */
63 {0x0000, 0x00, 13}, /* Gap */
64 {0x7010, 0x11, 16}, /* Frequency value */
65 };
66
67 ec_pdo_entry_info_t ex2521_pto_target_entries[] = {
68 {0x7010, 0x12, 16}, /* Target counter value */
69 };
70
71 ec_pdo_entry_info_t ex2521_enc_control_entries[] = {
72 {0x0000, 0x00, 2}, /* Gap */
73 {0x7020, 0x03, 1}, /* Set counter */
74 {0x0000, 0x00, 13}, /* Gap */
75 {0x7020, 0x11, 16}, /* Set counter value */
76 };
77
78 ec_pdo_entry_info_t ex2521_pto_status_entries[] = {
79 {0x6010, 0x01, 1}, /* Sel. Ack/End counter */
80 {0x6010, 0x02, 1}, /* Ramp active */
81 {0x0000, 0x00, 2}, /* Gap */
82 {0x6010, 0x05, 1}, /* Status of input target */
83 {0x6010, 0x06, 1}, /* Status of input zero */
84 {0x6010, 0x07, 1}, /* Error */
85 {0x0000, 0x00, 6}, /* Gap */
86 {0x1c32, 0x20, 1}, /* Sync error */
87 {0x0000, 0x00, 1}, /* Gap */
88 {0x1801, 0x09, 1}, /* TxPDO Toggle */
89 };
90
91 ec_pdo_entry_info_t ex2521_enc_status_entries[] = {
92 {0x0000, 0x00, 2}, /* Gap */
93 {0x6020, 0x03, 1}, /* Set counter done */
94 {0x6020, 0x04, 1}, /* Counter underflow */
95 {0x6020, 0x05, 1}, /* Counter overflow */
96 {0x0000, 0x00, 8}, /* Gap */
97 {0x1c32, 0x20, 1}, /* Sync error */
98 {0x0000, 0x00, 1}, /* Gap */
99 {0x1802, 0x09, 1}, /* TxPDO Toggle */
100 {0x6020, 0x11, 16}, /* Counter value */
101 };
102
103 ec_pdo_info_t ex2521_pdos[] = {
104 {0x1601, 5, ex2521_pto_control_entries},
105 {0x1604, 1, ex2521_pto_target_entries},
106 {0x1602, 4, ex2521_enc_control_entries},
107 {0x1a01, 10, ex2521_pto_status_entries},
108 {0x1a02, 9, ex2521_enc_status_entries},
109 };
110
111 ec_sync_info_t ex2521_syncs[] = {
112 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
113 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
114 {2, EC_DIR_OUTPUT, 3, ex2521_pdos + 0, EC_WD_ENABLE},
115 {3, EC_DIR_INPUT, 2, ex2521_pdos + 3, EC_WD_DISABLE},
116 {0xff}};
117
118 /* "EL2522 2K. Pulse Train Ausgang"
119 * Vendor ID: 0x00000002
120 * Product code: 0x09da3052
121 *
122 * "Erweiterter Betriebsmodus", "2 Ch. Standard 32 Bit (MDP 253/511)"
123 * (factory default, no Distributed Clocks required): per channel, PTO
124 * Control + PTO Target (32-bit) + ENC Control (32-bit) as outputs, PTO
125 * Status + ENC Status (32-bit) as inputs. Channel 1 at slot 0
126 * (0x6000/0x7000 PTO, 0x6020/0x7020 ENC), channel 2 at slot 1
127 * (0x6010/0x7010 PTO, 0x6030/0x7030 ENC).
128 */
129
130 ec_pdo_entry_info_t el2522_pto_control_ch1_entries[] = {
131 {0x7000, 0x01, 1}, /* Frequency select */
132 {0x7000, 0x02, 1}, /* Disable ramp */
133 {0x7000, 0x03, 1}, /* Go counter */
134 {0x0000, 0x00, 13}, /* Gap */
135 {0x7000, 0x11, 16}, /* Frequency value */
136 };
137
138 ec_pdo_entry_info_t el2522_pto_target_ch1_entries[] = {
139 {0x7000, 0x12, 32}, /* Target counter value */
140 };
141
142 ec_pdo_entry_info_t el2522_enc_control_ch1_entries[] = {
143 {0x0000, 0x00, 2}, /* Gap */
144 {0x7020, 0x03, 1}, /* Set counter */
145 {0x0000, 0x00, 12}, /* Gap */
146 {0x7020, 0x10, 1}, /* Reserved */
147 {0x7020, 0x11, 32}, /* Set counter value */
148 };
149
150 ec_pdo_entry_info_t el2522_pto_control_ch2_entries[] = {
151 {0x7010, 0x01, 1}, /* Frequency select */
152 {0x7010, 0x02, 1}, /* Disable ramp */
153 {0x7010, 0x03, 1}, /* Go counter */
154 {0x0000, 0x00, 13}, /* Gap */
155 {0x7010, 0x11, 16}, /* Frequency value */
156 };
157
158 ec_pdo_entry_info_t el2522_pto_target_ch2_entries[] = {
159 {0x7010, 0x12, 32}, /* Target counter value */
160 };
161
162 ec_pdo_entry_info_t el2522_enc_control_ch2_entries[] = {
163 {0x0000, 0x00, 2}, /* Gap */
164 {0x7030, 0x03, 1}, /* Set counter */
165 {0x0000, 0x00, 12}, /* Gap */
166 {0x7030, 0x10, 1}, /* Reserved */
167 {0x7030, 0x11, 32}, /* Set counter value */
168 };
169
170 ec_pdo_entry_info_t el2522_pto_status_ch1_entries[] = {
171 {0x6000, 0x01, 1}, /* Sel. Ack/End counter */
172 {0x6000, 0x02, 1}, /* Ramp active */
173 {0x0000, 0x00, 4}, /* Gap */
174 {0x6000, 0x07, 1}, /* Error */
175 {0x0000, 0x00, 6}, /* Gap */
176 {0x6000, 0x0e, 1}, /* Sync error */
177 {0x0000, 0x00, 1}, /* Gap */
178 {0x6000, 0x10, 1}, /* TxPDO Toggle */
179 };
180
181 ec_pdo_entry_info_t el2522_enc_status_ch1_entries[] = {
182 {0x0000, 0x00, 2}, /* Gap */
183 {0x6020, 0x03, 1}, /* Set counter done */
184 {0x6020, 0x04, 1}, /* Counter underflow */
185 {0x6020, 0x05, 1}, /* Counter overflow */
186 {0x0000, 0x00, 8}, /* Gap */
187 {0x6020, 0x0e, 1}, /* Sync error */
188 {0x6020, 0x0f, 1}, /* TxPDO State */
189 {0x6020, 0x10, 1}, /* TxPDO Toggle */
190 {0x6020, 0x11, 32}, /* Counter value */
191 };
192
193 ec_pdo_entry_info_t el2522_pto_status_ch2_entries[] = {
194 {0x6010, 0x01, 1}, /* Sel. Ack/End counter */
195 {0x6010, 0x02, 1}, /* Ramp active */
196 {0x0000, 0x00, 4}, /* Gap */
197 {0x6010, 0x07, 1}, /* Error */
198 {0x0000, 0x00, 6}, /* Gap */
199 {0x6010, 0x0e, 1}, /* Sync error */
200 {0x0000, 0x00, 1}, /* Gap */
201 {0x6010, 0x10, 1}, /* TxPDO Toggle */
202 };
203
204 ec_pdo_entry_info_t el2522_enc_status_ch2_entries[] = {
205 {0x0000, 0x00, 2}, /* Gap */
206 {0x6030, 0x03, 1}, /* Set counter done */
207 {0x6030, 0x04, 1}, /* Counter underflow */
208 {0x6030, 0x05, 1}, /* Counter overflow */
209 {0x0000, 0x00, 8}, /* Gap */
210 {0x6030, 0x0e, 1}, /* Sync error */
211 {0x6030, 0x0f, 1}, /* TxPDO State */
212 {0x6030, 0x10, 1}, /* TxPDO Toggle */
213 {0x6030, 0x11, 32}, /* Counter value */
214 };
215
216 ec_pdo_info_t el2522_pdos[] = {
217 {0x1600, 5, el2522_pto_control_ch1_entries},
218 {0x1603, 1, el2522_pto_target_ch1_entries},
219 {0x160b, 5, el2522_enc_control_ch1_entries},
220 {0x1605, 5, el2522_pto_control_ch2_entries},
221 {0x1608, 1, el2522_pto_target_ch2_entries},
222 {0x160d, 5, el2522_enc_control_ch2_entries},
223 {0x1a00, 8, el2522_pto_status_ch1_entries},
224 {0x1a03, 9, el2522_enc_status_ch1_entries},
225 {0x1a01, 8, el2522_pto_status_ch2_entries},
226 {0x1a05, 9, el2522_enc_status_ch2_entries},
227 };
228
229 ec_sync_info_t el2522_syncs[] = {
230 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
231 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
232 {2, EC_DIR_OUTPUT, 6, el2522_pdos + 0, EC_WD_ENABLE},
233 {3, EC_DIR_INPUT, 4, el2522_pdos + 6, EC_WD_DISABLE},
234 {0xff}};
235
236 /****************************************************************************/
237
238 struct Ex25xxType
239 {
240 const char *name;
241 uint32_t product_code;
242 unsigned int num_channels;
243 bool wide; // Target/Set-Counter/Counter value: 32-bit (true,
244 // EL2522) vs 16-bit (false, EL2521-xxxx)
245 bool hasInputTZ; // per-channel "Status of input target/zero" --
246 // EL2521-xxxx's Latch/T/Z digital inputs; EL2522
247 // has none
248 unsigned int slot[2]; // per-channel PTO/PTI CoE/PDO object-block
249 // index -- base object index + 0x10 *
250 // slot[channel]
251 unsigned int encSlot[2]; // per-channel ENC/ENI CoE/PDO object-block
252 // index, analogous to slot[] but tracked
253 // separately -- see the file-level comment
254 // in Ex25xx.h.
255 ec_sync_info_t *sync;
256 };
257
258 const static Ex25xxType ex25xxType[Ex25xx<Mode::Control>::NumTypes] = {
259 // EL2521-xxxx: one channel; PTO permanently at slot 1, ENC
260 // permanently at slot 0 -- see the file-level comment in
261 // Ex25xx.h.
262 {"EL2521", 0x09d93052, 1, false, true, {1, 0}, {0, 0}, ex2521_syncs},
263 {"EL2521-0024",
264 0x09d93052,
265 1,
266 false,
267 true,
268 {1, 0},
269 {0, 0},
270 ex2521_syncs},
271 {"EL2521-0025",
272 0x09d93052,
273 1,
274 false,
275 true,
276 {1, 0},
277 {0, 0},
278 ex2521_syncs},
279 // EL2522: two channels, PTO and ENC both at slot 0 and slot 1.
280 {"EL2522", 0x09da3052, 2, true, false, {0, 1}, {0, 1}, el2522_syncs},
281 };
282
283 /****************************************************************************/
284
285 template <Mode mode>
286 2 Ex25xx<mode>::Ex25xx(
287 Master *master,
288 uint16_t alias,
289 uint16_t position,
290 Domain *domainOut,
291 Domain *domainIn,
292 Type typeIdx) :
293 2 type(ex25xxType[typeIdx]),
294 sc(NULL),
295 domainOut(domainOut),
296 domainIn(domainIn),
297 2 configured(type.num_channels, false),
298 2 frequencySelect(type.num_channels, 0),
299 2 disableRamp(type.num_channels, 0),
300 2 goCounter(type.num_channels, 0),
301 2 frequencyValue(type.num_channels, 0),
302 2 targetCounterValue(type.num_channels, 0),
303 2 setCounter(type.num_channels, 0),
304 2 setCounterValue(type.num_channels, 0),
305 2 selAckEndCounter(type.num_channels, 0),
306 2 rampActive(type.num_channels, 0),
307 2 statusInputTarget(type.num_channels, 0),
308 2 statusInputZero(type.num_channels, 0),
309 2 error(type.num_channels, 0),
310 2 setCounterDone(type.num_channels, 0),
311 2 counterUnderflow(type.num_channels, 0),
312 2 counterOverflow(type.num_channels, 0),
313 2 counterValue(type.num_channels, 0),
314 2 offFrequencySelect(type.num_channels, -1),
315 2 offFrequencySelectBit(type.num_channels, 0),
316 2 offDisableRamp(type.num_channels, -1),
317 2 offDisableRampBit(type.num_channels, 0),
318 2 offGoCounter(type.num_channels, -1),
319 2 offGoCounterBit(type.num_channels, 0),
320 2 offFrequencyValue(type.num_channels, -1),
321 2 offTargetCounterValue(type.num_channels, -1),
322 2 offSetCounter(type.num_channels, -1),
323 2 offSetCounterBit(type.num_channels, 0),
324 2 offSetCounterValue(type.num_channels, -1),
325 2 offSelAckEndCounter(type.num_channels, -1),
326 2 offSelAckEndCounterBit(type.num_channels, 0),
327 2 offRampActive(type.num_channels, -1),
328 2 offRampActiveBit(type.num_channels, 0),
329 2 offStatusInputTarget(type.num_channels, -1),
330 2 offStatusInputTargetBit(type.num_channels, 0),
331 2 offStatusInputZero(type.num_channels, -1),
332 2 offStatusInputZeroBit(type.num_channels, 0),
333 2 offError(type.num_channels, -1),
334 2 offErrorBit(type.num_channels, 0),
335 2 offSetCounterDone(type.num_channels, -1),
336 2 offSetCounterDoneBit(type.num_channels, 0),
337 2 offCounterUnderflow(type.num_channels, -1),
338 2 offCounterUnderflowBit(type.num_channels, 0),
339 2 offCounterOverflow(type.num_channels, -1),
340 2 offCounterOverflowBit(type.num_channels, 0),
341
45/180
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 2 times.
✗ Branch 18 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 2 times.
✗ Branch 28 not taken.
✓ Branch 32 taken 2 times.
✗ Branch 33 not taken.
✓ Branch 37 taken 2 times.
✗ Branch 38 not taken.
✓ Branch 42 taken 2 times.
✗ Branch 43 not taken.
✓ Branch 47 taken 2 times.
✗ Branch 48 not taken.
✓ Branch 52 taken 2 times.
✗ Branch 53 not taken.
✓ Branch 57 taken 2 times.
✗ Branch 58 not taken.
✓ Branch 62 taken 2 times.
✗ Branch 63 not taken.
✓ Branch 67 taken 2 times.
✗ Branch 68 not taken.
✓ Branch 72 taken 2 times.
✗ Branch 73 not taken.
✓ Branch 77 taken 2 times.
✗ Branch 78 not taken.
✓ Branch 82 taken 2 times.
✗ Branch 83 not taken.
✓ Branch 87 taken 2 times.
✗ Branch 88 not taken.
✓ Branch 92 taken 2 times.
✗ Branch 93 not taken.
✓ Branch 97 taken 2 times.
✗ Branch 98 not taken.
✓ Branch 102 taken 2 times.
✗ Branch 103 not taken.
✓ Branch 107 taken 2 times.
✗ Branch 108 not taken.
✓ Branch 112 taken 2 times.
✗ Branch 113 not taken.
✓ Branch 117 taken 2 times.
✗ Branch 118 not taken.
✓ Branch 122 taken 2 times.
✗ Branch 123 not taken.
✓ Branch 127 taken 2 times.
✗ Branch 128 not taken.
✓ Branch 132 taken 2 times.
✗ Branch 133 not taken.
✓ Branch 137 taken 2 times.
✗ Branch 138 not taken.
✓ Branch 142 taken 2 times.
✗ Branch 143 not taken.
✓ Branch 147 taken 2 times.
✗ Branch 148 not taken.
✓ Branch 152 taken 2 times.
✗ Branch 153 not taken.
✓ Branch 157 taken 2 times.
✗ Branch 158 not taken.
✓ Branch 162 taken 2 times.
✗ Branch 163 not taken.
✓ Branch 167 taken 2 times.
✗ Branch 168 not taken.
✓ Branch 172 taken 2 times.
✗ Branch 173 not taken.
✓ Branch 177 taken 2 times.
✗ Branch 178 not taken.
✓ Branch 182 taken 2 times.
✗ Branch 183 not taken.
✓ Branch 187 taken 2 times.
✗ Branch 188 not taken.
✓ Branch 192 taken 2 times.
✗ Branch 193 not taken.
✓ Branch 197 taken 2 times.
✗ Branch 198 not taken.
✓ Branch 202 taken 2 times.
✗ Branch 203 not taken.
✓ Branch 207 taken 2 times.
✗ Branch 208 not taken.
✓ Branch 212 taken 2 times.
✗ Branch 213 not taken.
✓ Branch 217 taken 2 times.
✗ Branch 218 not taken.
✓ Branch 222 taken 2 times.
✗ Branch 223 not taken.
✗ Branch 318 not taken.
✗ Branch 319 not taken.
✗ Branch 323 not taken.
✗ Branch 324 not taken.
✗ Branch 328 not taken.
✗ Branch 329 not taken.
✗ Branch 333 not taken.
✗ Branch 334 not taken.
✗ Branch 338 not taken.
✗ Branch 339 not taken.
✗ Branch 343 not taken.
✗ Branch 344 not taken.
✗ Branch 348 not taken.
✗ Branch 349 not taken.
✗ Branch 353 not taken.
✗ Branch 354 not taken.
✗ Branch 358 not taken.
✗ Branch 359 not taken.
✗ Branch 363 not taken.
✗ Branch 364 not taken.
✗ Branch 368 not taken.
✗ Branch 369 not taken.
✗ Branch 373 not taken.
✗ Branch 374 not taken.
✗ Branch 378 not taken.
✗ Branch 379 not taken.
✗ Branch 383 not taken.
✗ Branch 384 not taken.
✗ Branch 388 not taken.
✗ Branch 389 not taken.
✗ Branch 393 not taken.
✗ Branch 394 not taken.
✗ Branch 398 not taken.
✗ Branch 399 not taken.
✗ Branch 403 not taken.
✗ Branch 404 not taken.
✗ Branch 408 not taken.
✗ Branch 409 not taken.
✗ Branch 413 not taken.
✗ Branch 414 not taken.
✗ Branch 418 not taken.
✗ Branch 419 not taken.
✗ Branch 423 not taken.
✗ Branch 424 not taken.
✗ Branch 428 not taken.
✗ Branch 429 not taken.
✗ Branch 433 not taken.
✗ Branch 434 not taken.
✗ Branch 438 not taken.
✗ Branch 439 not taken.
✗ Branch 443 not taken.
✗ Branch 444 not taken.
✗ Branch 448 not taken.
✗ Branch 449 not taken.
✗ Branch 453 not taken.
✗ Branch 454 not taken.
✗ Branch 458 not taken.
✗ Branch 459 not taken.
✗ Branch 463 not taken.
✗ Branch 464 not taken.
✗ Branch 468 not taken.
✗ Branch 469 not taken.
✗ Branch 473 not taken.
✗ Branch 474 not taken.
✗ Branch 478 not taken.
✗ Branch 479 not taken.
✗ Branch 483 not taken.
✗ Branch 484 not taken.
✗ Branch 488 not taken.
✗ Branch 489 not taken.
✗ Branch 493 not taken.
✗ Branch 494 not taken.
✗ Branch 498 not taken.
✗ Branch 499 not taken.
✗ Branch 503 not taken.
✗ Branch 504 not taken.
✗ Branch 508 not taken.
✗ Branch 509 not taken.
✗ Branch 513 not taken.
✗ Branch 514 not taken.
✗ Branch 518 not taken.
✗ Branch 519 not taken.
✗ Branch 523 not taken.
✗ Branch 524 not taken.
✗ Branch 528 not taken.
✗ Branch 529 not taken.
✗ Branch 533 not taken.
✗ Branch 534 not taken.
✗ Branch 538 not taken.
✗ Branch 539 not taken.
90 offCounterValue(type.num_channels, -1)
342 {
343 // pdserv publishing happens in Ex25xxAdapter's constructor below,
344 // via SubDevice::registerInputChannel()/registerOutputChannel() --
345 // that is also what makes each channel alias-able through
346 // SubDevice::setAlias().
347
348
1/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (!master) {
349 2 return;
350 }
351
352 ✗ sc = ecrt_master_slave_config(
353 master->getMaster(), alias, position, 0x00000002,
354 ✗ type.product_code);
355 ✗ if (!sc) {
356 ✗ stringstream err;
357 ✗ err << "Failed to get slave configuration for " << type.name << ".";
358 ✗ throw runtime_error(err.str());
359 }
360
361 ✗ if (ecrt_slave_config_pdos(
362 ✗ sc, EC_END, SwappedSync<mode>(type.sync).getSync())) {
363 ✗ stringstream err;
364 ✗ err << "Failed to configure PDOs of " << type.name << ".";
365 ✗ throw runtime_error(err.str());
366 }
367
368 // entryName is a human-readable PDO entry name distinct from `entry`
369 // itself -- `entry` is the off*/off*Bit member being filled in
370 // (always "off"-prefixed, e.g. offSetCounter), and stringifying that
371 // directly (as this used to do) reads misleadingly like "offset
372 // counter" rather than "the offset for the SetCounter PDO entry".
373 #define REG_BIT(entry, entryName, index, subindex, domain, c) \
374 do { \
375 offset = ecrt_slave_config_reg_pdo_entry( \
376 sc, index, subindex, (domain)->getDomain(), \
377 &(entry##Bit)[c]); \
378 if (offset < 0) { \
379 stringstream err; \
380 err << "Failed to register " << type.name << " PDO entry " \
381 << entryName << "."; \
382 throw runtime_error(err.str()); \
383 } \
384 (entry)[c] = offset; \
385 } while (0)
386
387 #define REG_VAL(entry, entryName, index, subindex, domain, c) \
388 do { \
389 offset = ecrt_slave_config_reg_pdo_entry( \
390 sc, index, subindex, (domain)->getDomain(), NULL); \
391 if (offset < 0) { \
392 stringstream err; \
393 err << "Failed to register " << type.name << " PDO entry " \
394 << entryName << "."; \
395 throw runtime_error(err.str()); \
396 } \
397 (entry)[c] = offset; \
398 } while (0)
399
400 int offset;
401
402 ✗ for (unsigned int c = 0; c < type.num_channels; c++) {
403 ✗ uint16_t pto = 0x7000 + 0x10 * type.slot[c];
404 ✗ uint16_t enc = 0x7020 + 0x10 * type.encSlot[c];
405 ✗ uint16_t pti = 0x6000 + 0x10 * type.slot[c];
406 ✗ uint16_t eni = 0x6020 + 0x10 * type.encSlot[c];
407
408 ✗ REG_BIT(offFrequencySelect, "FrequencySelect", pto, 0x01, domainOut,
409 c);
410 ✗ REG_BIT(offDisableRamp, "DisableRamp", pto, 0x02, domainOut, c);
411 ✗ REG_BIT(offGoCounter, "GoCounter", pto, 0x03, domainOut, c);
412 ✗ REG_VAL(offFrequencyValue, "FrequencyValue", pto, 0x11, domainOut, c);
413 ✗ REG_VAL(offTargetCounterValue, "TargetCounterValue", pto, 0x12,
414 domainOut, c);
415 ✗ REG_BIT(offSetCounter, "SetCounter", enc, 0x03, domainOut, c);
416 ✗ REG_VAL(offSetCounterValue, "SetCounterValue", enc, 0x11, domainOut,
417 c);
418
419 ✗ REG_BIT(offSelAckEndCounter, "SelAckEndCounter", pti, 0x01, domainIn,
420 c);
421 ✗ REG_BIT(offRampActive, "RampActive", pti, 0x02, domainIn, c);
422 ✗ if (type.hasInputTZ) {
423 ✗ REG_BIT(offStatusInputTarget, "StatusInputTarget", pti, 0x05,
424 domainIn, c);
425 ✗ REG_BIT(offStatusInputZero, "StatusInputZero", pti, 0x06,
426 domainIn, c);
427 }
428 ✗ REG_BIT(offError, "Error", pti, 0x07, domainIn, c);
429 ✗ REG_BIT(offSetCounterDone, "SetCounterDone", eni, 0x03, domainIn, c);
430 ✗ REG_BIT(offCounterUnderflow, "CounterUnderflow", eni, 0x04, domainIn,
431 c);
432 ✗ REG_BIT(offCounterOverflow, "CounterOverflow", eni, 0x05, domainIn,
433 c);
434 ✗ REG_VAL(offCounterValue, "CounterValue", eni, 0x11, domainIn, c);
435 }
436
437 #undef REG_BIT
438 #undef REG_VAL
439 }
440
441 /****************************************************************************/
442
443 template <Mode mode>
444 4 void Ex25xx<mode>::configure(
445 unsigned int channel,
446 PulseTrainOutputMode outputMode,
447 bool negativeLogic,
448 bool travelDistanceControl)
449 {
450
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if (channel >= type.num_channels) {
451 ✗ return;
452 }
453
2/4
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4 if (configured[channel]) {
454
1/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 stringstream err;
455
2/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 err << "Ex25xx::configure() was already called for channel "
456
1/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 << channel
457 << "; the EtherCAT master queues every SDO download rather "
458 "than replacing an earlier one for the same object, so a "
459 "second call would not overwrite the first, only waste "
460 "an extra download at activation time.";
461
2/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1 throw logic_error(err.str());
462 }
463 3 configured[channel] = true;
464
465
1/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (!sc) {
466 3 return;
467 }
468
469 uint8_t modeValue;
470 ✗ switch (outputMode) {
471 ✗ case PulseTrainOutputMode::PulseDirection:
472 ✗ modeValue = 1;
473 ✗ break;
474 ✗ case PulseTrainOutputMode::EncoderSimulation:
475 ✗ modeValue = 2;
476 ✗ break;
477 ✗ case PulseTrainOutputMode::FrequencyModulation:
478 default:
479 ✗ modeValue = 0;
480 ✗ break;
481 }
482
483 ✗ uint16_t settings = 0x8000 + 0x10 * type.slot[channel];
484 ✗ if (ecrt_slave_config_sdo8(sc, settings, 0x0e, modeValue)
485 ✗ || ecrt_slave_config_sdo8(sc, settings, 0x10, negativeLogic ? 1 : 0)
486 ✗ || ecrt_slave_config_sdo8(
487 sc, settings, 0x0a, travelDistanceControl ? 1 : 0)) {
488 ✗ stringstream err;
489 ✗ err << "Failed to configure " << type.name
490 ✗ << " PTO Settings SDOs for channel " << channel << ".";
491 ✗ throw runtime_error(err.str());
492 }
493 }
494
495 /****************************************************************************/
496
497 template <Mode mode>
498 41 unsigned int Ex25xx<mode>::numChannels() const
499 {
500 41 return type.num_channels;
501 }
502
503 /****************************************************************************/
504
505 template <Mode mode>
506 8 bool Ex25xx<mode>::hasInputTZ() const
507 {
508 8 return type.hasInputTZ;
509 }
510
511 /****************************************************************************/
512
513 namespace {
514
515 // Target/Set-Counter/Counter value: 16-bit hardware range on a non-
516 // "wide" type, always stored/exposed as int32_t -- see Ex25xx.h.
517 ✗ inline void writeWidth(uint8_t *data, int offset, bool wide, int32_t value)
518 {
519 ✗ if (wide) {
520 ✗ EC_WRITE_S32(data + offset, value);
521 }
522 else {
523 ✗ int16_t clamped = value > 32767 ? int16_t(32767)
524 ✗ : value < -32768 ? int16_t(-32768)
525 : int16_t(value);
526 ✗ EC_WRITE_S16(data + offset, clamped);
527 }
528 }
529
530 ✗ inline int32_t readWidth(const uint8_t *data, int offset, bool wide)
531 {
532 ✗ return wide ? EC_READ_S32(data + offset) : EC_READ_S16(data + offset);
533 }
534
535 } // namespace
536
537 /****************************************************************************/
538
539 template <Mode mode>
540 2 void Ex25xx<mode>::updateInputs()
541 {
542
2/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
5 for (unsigned int c = 0; c < type.num_channels; c++) {
543
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if (domainIn->getData()) {
544 ✗ selAckEndCounter[c] = EC_READ_BIT(
545 domainIn->getData() + offSelAckEndCounter[c],
546 offSelAckEndCounterBit[c]);
547 ✗ rampActive[c] = EC_READ_BIT(
548 domainIn->getData() + offRampActive[c],
549 offRampActiveBit[c]);
550 ✗ if (type.hasInputTZ) {
551 ✗ statusInputTarget[c] = EC_READ_BIT(
552 domainIn->getData() + offStatusInputTarget[c],
553 offStatusInputTargetBit[c]);
554 ✗ statusInputZero[c] = EC_READ_BIT(
555 domainIn->getData() + offStatusInputZero[c],
556 offStatusInputZeroBit[c]);
557 }
558 ✗ error[c] = EC_READ_BIT(
559 domainIn->getData() + offError[c], offErrorBit[c]);
560 ✗ setCounterDone[c] = EC_READ_BIT(
561 domainIn->getData() + offSetCounterDone[c],
562 offSetCounterDoneBit[c]);
563 ✗ counterUnderflow[c] = EC_READ_BIT(
564 domainIn->getData() + offCounterUnderflow[c],
565 offCounterUnderflowBit[c]);
566 ✗ counterOverflow[c] = EC_READ_BIT(
567 domainIn->getData() + offCounterOverflow[c],
568 offCounterOverflowBit[c]);
569 ✗ counterValue[c] = readWidth(
570 ✗ domainIn->getData(), offCounterValue[c], type.wide);
571 }
572 else {
573 3 selAckEndCounter[c] = 0;
574 3 rampActive[c] = 0;
575 3 statusInputTarget[c] = 0;
576 3 statusInputZero[c] = 0;
577 3 error[c] = 0;
578 3 setCounterDone[c] = 0;
579 3 counterUnderflow[c] = 0;
580 3 counterOverflow[c] = 0;
581 3 counterValue[c] = 0;
582 }
583 }
584 2 }
585
586 /****************************************************************************/
587
588 template <Mode mode>
589 2 void Ex25xx<mode>::updateOutputs()
590 {
591
1/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 if (!domainOut->getData()) {
592 2 return;
593 }
594
595 ✗ for (unsigned int c = 0; c < type.num_channels; c++) {
596 ✗ EC_WRITE_BIT(
597 domainOut->getData() + offFrequencySelect[c],
598 offFrequencySelectBit[c], frequencySelect[c]);
599 ✗ EC_WRITE_BIT(
600 domainOut->getData() + offDisableRamp[c],
601 offDisableRampBit[c], disableRamp[c]);
602 ✗ EC_WRITE_BIT(
603 domainOut->getData() + offGoCounter[c], offGoCounterBit[c],
604 goCounter[c]);
605 ✗ EC_WRITE_S16(
606 domainOut->getData() + offFrequencyValue[c],
607 frequencyValue[c]);
608 ✗ writeWidth(
609 ✗ domainOut->getData(), offTargetCounterValue[c], type.wide,
610 targetCounterValue[c]);
611 ✗ EC_WRITE_BIT(
612 domainOut->getData() + offSetCounter[c], offSetCounterBit[c],
613 setCounter[c]);
614 ✗ writeWidth(
615 ✗ domainOut->getData(), offSetCounterValue[c], type.wide,
616 setCounterValue[c]);
617 }
618 }
619
620 /****************************************************************************/
621
622 template class Ex25xx<Mode::Control>;
623 template class Ex25xx<Mode::Simulation>;
624
625 /****************************************************************************/
626 // Generic SubDevice adapter: per-channel bool/int kinds, one index per
627 // channel (index 0 = channel 1, etc.) -- shared by EL2521-xxxx (1
628 // channel) and EL2522 (2 channels). "StatusInputTarget"/
629 // "StatusInputZero" are only present for types that have them (see
630 // Ex25xx::hasInputTZ()), same idea as Ex20xxAdapter's conditional
631 // "Diagnosis" kind. Application code never names Ex25xx, only e.g.
632 // "EL2521" or "EL2522" -- the factory resolves it.
633 /****************************************************************************/
634
635 template <Mode mode>
636 4 class Ex25xxAdapter : public SubDevice, public PulseTrainOutput
637 {
638 public:
639 2 Ex25xxAdapter(const SlaveContext &ctx, typename Ex25xx<mode>::Type t) :
640 SubDevice(
641 2 ctx.domainOut,
642 2 ctx.domainIn,
643 2 ctx.pdServ,
644 2 ctx.task,
645 ctx.prefix),
646 2 device(ctx.master,
647 2 ctx.alias,
648 2 ctx.position,
649 2 ctx.domainOut,
650 2 ctx.domainIn,
651 t),
652 outputKinds_ {
653 {"FrequencySelect", device.numChannels(), false},
654 {"DisableRamp", device.numChannels(), false},
655 {"GoCounter", device.numChannels(), false},
656 {"FrequencyValue", device.numChannels(), int16_t(0)},
657 {"TargetCounterValue", device.numChannels(), int32_t(0)},
658 {"SetCounter", device.numChannels(), false},
659
19/76
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 16 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 26 taken 2 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✓ Branch 33 taken 2 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✓ Branch 40 taken 2 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✓ Branch 47 taken 2 times.
✗ Branch 48 not taken.
✓ Branch 50 taken 2 times.
✗ Branch 51 not taken.
✓ Branch 54 taken 2 times.
✗ Branch 55 not taken.
✓ Branch 57 taken 2 times.
✗ Branch 58 not taken.
✓ Branch 61 taken 2 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 14 times.
✓ Branch 65 taken 2 times.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 121 not taken.
✗ Branch 122 not taken.
✗ Branch 125 not taken.
✗ Branch 126 not taken.
✗ Branch 128 not taken.
✗ Branch 129 not taken.
✗ Branch 132 not taken.
✗ Branch 133 not taken.
✗ Branch 135 not taken.
✗ Branch 136 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 142 not taken.
✗ Branch 143 not taken.
✗ Branch 146 not taken.
✗ Branch 147 not taken.
✗ Branch 149 not taken.
✗ Branch 150 not taken.
✗ Branch 153 not taken.
✗ Branch 154 not taken.
✗ Branch 156 not taken.
✗ Branch 157 not taken.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
10 {"SetCounterValue", device.numChannels(), int32_t(0)}}
660 {
661
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back(
662 {"SelAckEndCounter", device.numChannels(), false});
663
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back({"RampActive", device.numChannels(), false});
664
3/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 if (device.hasInputTZ()) {
665
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
1 inputKinds_.push_back(
666 {"StatusInputTarget", device.numChannels(), false});
667
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
1 inputKinds_.push_back(
668 {"StatusInputZero", device.numChannels(), false});
669 }
670
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back({"Error", device.numChannels(), false});
671
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back(
672 {"SetCounterDone", device.numChannels(), false});
673
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back(
674 {"CounterUnderflow", device.numChannels(), false});
675
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back(
676 {"CounterOverflow", device.numChannels(), false});
677
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 inputKinds_.push_back(
678 {"CounterValue", device.numChannels(), int32_t(0)});
679
680
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel(
681 "SelAckEndCounter", device.selAckEndCounter, AsBoolean {});
682
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel("RampActive", device.rampActive, AsBoolean {});
683
3/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 if (device.hasInputTZ()) {
684
2/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1 registerInputChannel(
685 "StatusInputTarget", device.statusInputTarget,
686 AsBoolean {});
687
2/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1 registerInputChannel(
688 "StatusInputZero", device.statusInputZero, AsBoolean {});
689 }
690
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel("Error", device.error, AsBoolean {});
691
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel(
692 "SetCounterDone", device.setCounterDone, AsBoolean {});
693
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel(
694 "CounterUnderflow", device.counterUnderflow, AsBoolean {});
695
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel(
696 "CounterOverflow", device.counterOverflow, AsBoolean {});
697
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel("CounterValue", device.counterValue);
698
699
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel(
700 "FrequencySelect", device.frequencySelect, AsBoolean {});
701
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel(
702 "DisableRamp", device.disableRamp, AsBoolean {});
703
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel("GoCounter", device.goCounter, AsBoolean {});
704
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel("FrequencyValue", device.frequencyValue);
705
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel(
706 "TargetCounterValue", device.targetCounterValue);
707
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel("SetCounter", device.setCounter, AsBoolean {});
708
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerOutputChannel("SetCounterValue", device.setCounterValue);
709 2 }
710
711 2 void updateInputs() override { device.updateInputs(); }
712 2 void updateOutputs() override { device.updateOutputs(); }
713
714 10 const std::vector<ChannelKind> &inputKinds() const override
715 {
716 10 return inputKinds_;
717 }
718 10 const std::vector<ChannelKind> &outputKinds() const override
719 {
720 10 return outputKinds_;
721 }
722
723 4 void configure(
724 unsigned int channel,
725 PulseTrainOutputMode outputMode,
726 bool negativeLogic,
727 bool travelDistanceControl) override
728 {
729 4 device.configure(
730 channel, outputMode, negativeLogic, travelDistanceControl);
731 3 }
732
733 protected:
734 5 ChannelValue getInputAt(size_t i) const override
735 {
736 // Kind blocks concatenated in inputKinds() order (see the
737 // constructor above), numChannels() entries per kind.
738 5 unsigned int n = device.numChannels();
739 5 unsigned int block = static_cast<unsigned int>(i) / n;
740 5 unsigned int c = static_cast<unsigned int>(i) % n;
741
742 5 unsigned int k = 0;
743
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (block == k++) {
744 ✗ return uint8_t(device.selAckEndCounter[c]) != 0;
745 }
746
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (block == k++) {
747 1 return uint8_t(device.rampActive[c]) != 0;
748 }
749
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (device.hasInputTZ()) {
750
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (block == k++) {
751 1 return uint8_t(device.statusInputTarget[c]) != 0;
752 }
753
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (block == k++) {
754 ✗ return uint8_t(device.statusInputZero[c]) != 0;
755 }
756 }
757
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (block == k++) {
758 ✗ return uint8_t(device.error[c]) != 0;
759 }
760
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (block == k++) {
761 ✗ return uint8_t(device.setCounterDone[c]) != 0;
762 }
763
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (block == k++) {
764 ✗ return uint8_t(device.counterUnderflow[c]) != 0;
765 }
766
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (block == k++) {
767 ✗ return uint8_t(device.counterOverflow[c]) != 0;
768 }
769
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (block == k++) {
770 3 return device.counterValue[c];
771 }
772 ✗ throw UnknownChannel("Ex25xx: invalid channel index.");
773 }
774
775 6 void setOutputAt(size_t i, const ChannelValue &value) override
776 {
777 6 unsigned int n = device.numChannels();
778 6 unsigned int block = static_cast<unsigned int>(i) / n;
779 6 unsigned int c = static_cast<unsigned int>(i) % n;
780
781
3/16
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
6 switch (block) {
782 ✗ case 0:
783 ✗ device.frequencySelect[c] = std::get<bool>(value);
784 ✗ return;
785 ✗ case 1:
786 ✗ device.disableRamp[c] = std::get<bool>(value);
787 ✗ return;
788 2 case 2:
789 2 device.goCounter[c] = std::get<bool>(value);
790 2 return;
791 1 case 3:
792 1 device.frequencyValue[c] = std::get<int16_t>(value);
793 1 return;
794 3 case 4:
795 3 device.targetCounterValue[c] = std::get<int32_t>(value);
796 3 return;
797 ✗ case 5:
798 ✗ device.setCounter[c] = std::get<bool>(value);
799 ✗ return;
800 ✗ case 6:
801 ✗ device.setCounterValue[c] = std::get<int32_t>(value);
802 ✗ return;
803 ✗ default:
804 ✗ throw UnknownChannel("Ex25xx: invalid channel index.");
805 }
806 }
807
808 private:
809 Ex25xx<mode> device;
810 std::vector<ChannelKind> inputKinds_;
811 std::vector<ChannelKind> outputKinds_;
812 };
813
814 /****************************************************************************/
815
816 namespace {
817
818 48 DeviceFactoryFn makeEx25xxFactory(unsigned int typeIdx)
819 {
820 4 return [typeIdx](const SlaveContext &ctx) -> unique_ptr<SubDevice> {
821
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ctx.mode == Mode::Control) {
822 ✗ return make_unique<Ex25xxAdapter<Mode::Control>>(
823 ✗ ctx, static_cast<Ex25xx<Mode::Control>::Type>(typeIdx));
824 }
825 else {
826
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 return make_unique<Ex25xxAdapter<Mode::Simulation>>(
827 ctx,
828 6 static_cast<Ex25xx<Mode::Simulation>::Type>(typeIdx));
829 }
830 48 };
831 }
832
833 struct Ex25xxRegistrations
834 {
835 12 Ex25xxRegistrations()
836 {
837
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 12 times.
60 for (unsigned int i = 0; i < Ex25xx<Mode::Control>::NumTypes; i++) {
838 48 const auto &t = ex25xxType[i];
839
4/8
✓ Branch 3 taken 48 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 48 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 48 times.
✗ Branch 14 not taken.
240 Registry::instance().add(
840 96 {t.name, __FILE__, 0x00000002, t.product_code,
841
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
96 AnyRevision, wrapFactory(makeEx25xxFactory(i))});
842 }
843 12 }
844 12 } ex25xxRegistrations;
845
846 } // namespace
847
848 36 } // namespace EcApp
849
850 /****************************************************************************/
851