| 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 |