REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
ublock.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 anabrid GmbH
2// Contact: https://www.anabrid.com/licensing/
3// SPDX-License-Identifier: MIT OR GPL-2.0-or-later
4
5#include <algorithm>
6
7#include "ublock.h"
8
9#include "utils/logging.h"
10
11FLASHMEM void utils::shift_5_left(uint8_t *buffer, size_t size) {
12 for (size_t idx = 0; idx < size - 1; idx++) {
13 auto next = buffer[idx + 1];
14 // Shift by 5, leaving zeros at 0-4
15 buffer[idx] <<= 5;
16 buffer[idx] |= (next >> 3) & 0x1F;
17 }
18 buffer[size - 1] <<= 5;
19}
20
21const SPISettings functions::UMatrixFunction::DEFAULT_SPI_SETTINGS{4'000'000, MSBFIRST, SPI_MODE2};
22
24 : functions::DataFunction(address, DEFAULT_SPI_SETTINGS) {}
25
27 : FunctionBlock("U"), hardware(hardware), output_input_map{} {
28 classifier.class_enum = CLASS_;
30}
31
33 if (hardware)
34 return hardware->get_entity_eui();
35 return {};
36}
37
38FLASHMEM bool blocks::UBlock::_i_sanity_check(const uint8_t input) { return input < NUM_OF_INPUTS; }
39
40FLASHMEM bool blocks::UBlock::_o_sanity_check(const uint8_t output) { return output < NUM_OF_OUTPUTS; }
41
42FLASHMEM bool blocks::UBlock::_io_sanity_check(const uint8_t input, const uint8_t output) {
43 return _i_sanity_check(input) && _o_sanity_check(output);
44}
45
46FLASHMEM void blocks::UBlock::_connect(const uint8_t input, const uint8_t output) {
47 output_input_map[output] = input;
48}
49
50FLASHMEM bool blocks::UBlock::connect(const uint8_t input, const uint8_t output, bool force) {
51 if (!_io_sanity_check(input, output))
52 return false;
53
54 // Check for other connections on the same output, unless we don't care if we overwrite them
55 if (!force and _is_output_connected(output))
56 return false;
57
58 // Check if the transmission mode makes the specified connection impossible and if the mode can or should be
59 // chnged
60 if (a_side_mode != Transmission_Mode::ANALOG_INPUT &&
61 ((output < 16 && input != 15) || (output >= 16 && input != 14))) {
62 if (!force)
63 if (is_input_connected(input))
64 return false;
65 change_a_side_transmission_mode(Transmission_Mode::ANALOG_INPUT);
66 }
67
68 if (b_side_mode != Transmission_Mode::ANALOG_INPUT &&
69 ((output < 16 && input == 15) || (output >= 16 && input == 14))) {
70 if (!force)
71 if (is_input_connected(input))
72 return false;
73 change_b_side_transmission_mode(Transmission_Mode::ANALOG_INPUT);
74 }
75
76 _connect(input, output);
77 return true;
78}
79
80FLASHMEM bool blocks::UBlock::connect_alternative(Transmission_Mode signal_mode, const uint8_t output,
81 bool force, bool use_a_side) {
82 if (!_o_sanity_check(output))
83 return false;
84
85 // Do not consider Transmission_Mode::ANALOG_INPUT a valid mode
86 if (signal_mode == Transmission_Mode::ANALOG_INPUT)
87 return false;
88
89 // When we want to connect a reference signal, we expect the reference magnitude to be one
90 if ((signal_mode == Transmission_Mode::POS_REF or signal_mode == Transmission_Mode::NEG_REF) and
91 ref_magnitude != Reference_Magnitude::ONE and !force)
92 return false;
93
94 // Check for other connections on the same output, unless we don't care if we overwrite them
95 if (!force and _is_output_connected(output))
96 return false;
97
98 // Check if the transmission mode makes the specified connection impossible and if the mode can or should be
99 // chnged
100 if (a_side_mode != signal_mode && use_a_side) {
101 // TODO: force for a-side could be added, but since there wont be much use for an a-side
102 // alternative signal, you have to use force or set the correct signal mode
103 if (!force)
104 return false;
105 change_a_side_transmission_mode(signal_mode);
106 }
107
108 if (b_side_mode != signal_mode && !use_a_side) {
109 if (!force)
110 if (is_input_connected(14) || is_input_connected(15))
111 return false;
112 change_b_side_transmission_mode(signal_mode);
114 }
115
116 // Make a one to one connection if possible. Uses Input zero as fallback
117 uint8_t input;
118 if (use_a_side) {
119 if (output < 15)
120 input = output;
121 else if (output == 15)
122 input = 0;
123 else if (output == 30)
124 input = 0;
125 else
126 input = output - 16;
127 } else {
128 if (output < 16)
129 input = 15;
130 else
131 input = 14;
132 }
133
134 _connect(input, output);
135 return true;
136}
137
138FLASHMEM void blocks::UBlock::_disconnect(const uint8_t output) { output_input_map[output] = -1; }
139
140FLASHMEM bool blocks::UBlock::disconnect(const uint8_t input, const uint8_t output) {
141 if (!_io_sanity_check(input, output))
142 return false;
143
144 if (_is_connected(input, output)) {
145 _disconnect(output);
146 return true;
147 } else {
148 return false;
149 }
150}
151
152FLASHMEM bool blocks::UBlock::disconnect(const uint8_t output) {
153 if (!_o_sanity_check(output))
154 return false;
155 _disconnect(output);
156 return true;
157}
158
159FLASHMEM bool blocks::UBlock::_is_connected(const uint8_t input, const uint8_t output) const {
160 return output_input_map[output] == input;
161}
162
163FLASHMEM bool blocks::UBlock::is_connected(const uint8_t input, const uint8_t output) const {
164 if (!_io_sanity_check(input, output))
165 return false;
166
167 return _is_connected(input, output);
168}
169
170FLASHMEM bool blocks::UBlock::_is_output_connected(const uint8_t output) const {
171 return output_input_map[output] >= 0;
172}
173
174FLASHMEM bool blocks::UBlock::is_output_connected(const uint8_t output) const {
175 if (!_o_sanity_check(output))
176 return false;
177
178 return _is_output_connected(output);
179}
180
181FLASHMEM bool blocks::UBlock::_is_input_connected(const uint8_t input) const {
182 for (const auto &output : output_input_map)
183 if (output == input)
184 return true;
185 return false;
186}
187
188FLASHMEM bool blocks::UBlock::is_input_connected(const uint8_t input) const {
189 if (!_i_sanity_check(input))
190 return false;
191 return _is_input_connected(input);
192}
193
195 for (auto output : OUTPUT_IDX_RANGE())
196 if (_is_output_connected(output))
197 return true;
198 return false;
199}
200
202 a_side_mode = mode;
203}
204
206 b_side_mode = mode;
207}
208
210 change_a_side_transmission_mode(mode);
211 change_b_side_transmission_mode(mode);
212}
213
214FLASHMEM void
215blocks::UBlock::change_all_transmission_modes(const std::pair<Transmission_Mode, Transmission_Mode> modes) {
216 change_a_side_transmission_mode(modes.first);
217 change_b_side_transmission_mode(modes.second);
218}
219
220FLASHMEM std::pair<blocks::UBlock::Transmission_Mode, blocks::UBlock::Transmission_Mode>
222 return std::make_pair(a_side_mode, b_side_mode);
223}
224
228
232
234 if (!hardware->write_outputs(output_input_map) or
235 !hardware->write_transmission_modes_and_ref({a_side_mode, b_side_mode}, ref_magnitude)) {
236 LOG(ANABRID_PEDANTIC, __PRETTY_FUNCTION__);
237 return utils::status::failure();
238 }
239 return utils::status::success();
240}
241
243 std::fill(begin(output_input_map), end(output_input_map), -1);
244}
245
247 FunctionBlock::reset(action);
248
250 change_all_transmission_modes(blocks::UBlock::Transmission_Mode::ANALOG_INPUT);
251 reset_connections();
252 reset_reference_magnitude();
253 }
254}
255
256FLASHMEM
258#ifdef ANABRID_DEBUG_ENTITY_CONFIG
259 Serial.println(__PRETTY_FUNCTION__);
260#endif
261 for (auto cfgItr = cfg.begin(); cfgItr != cfg.end(); ++cfgItr) {
262 if (cfgItr->key() == "outputs") {
263 auto ret = _config_outputs_from_json(cfgItr->value());
264 if (!ret)
265 return ret;
266 } else if (cfgItr->key() == "constant") {
267 auto ret = _config_constants_from_json(cfgItr->value());
268 if (!ret)
269 return ret;
270 } else {
271 return utils::status("UBlock: Unknown configuration key");
272 }
273 }
274 return utils::status::success();
275}
276
277FLASHMEM
279 // Handle an array of outputs
280 // In this case, all outputs are reset
281 if (cfg.is<JsonArrayConst>()) {
282 auto outputs = cfg.as<JsonArrayConst>();
283 if (outputs.size() != NUM_OF_OUTPUTS)
284 return utils::status("Nblock: Wrong number of configuration elements. Given %d, expected %d",
285 outputs.size(), NUM_OF_OUTPUTS);
286 reset_connections();
287 uint8_t idx = 0;
288 for (JsonVariantConst input : outputs) {
289 if (input.isNull()) {
290 idx++;
291 continue;
292 } else if (!input.is<uint8_t>()) {
293 return utils::status("UBlock: Input is not an integer");
294 }
295 if (!connect(input.as<uint8_t>(), idx++))
296 return utils::status("UBlock: Cannot connect input at idx");
297 }
298 return utils::status::success();
299 }
300 // Handle a mapping of outputs
301 // In this case, only explicitly passed outputs are changed
302 // To allow reconnections (swapping), we first clear all outputs and set them afterwards.
303 else if (cfg.is<JsonObjectConst>()) {
304 for (JsonPairConst keyval : cfg.as<JsonObjectConst>()) {
305 // Sanity check that any input passed is a number
306 if (!(keyval.value().is<uint8_t>() or keyval.value().isNull()))
307 return utils::status("UBlock: Given input is not a number '%s'", keyval.value().as<const char *>());
308 // TODO: Check conversion from string to number
309 auto output = std::stoul(keyval.key().c_str());
310 !disconnect(output);
311 }
312 for (JsonPairConst keyval : cfg.as<JsonObjectConst>()) {
313 // TODO: Check conversion from string to number
314 auto output = std::stoul(keyval.key().c_str());
315 // Type is checked above
316 if (keyval.value().isNull())
317 continue;
318 auto input = keyval.value().as<uint8_t>();
319 if (!connect(input, output))
320 return utils::status("UBlock: Could not connect %d -> %d", input, output);
321 }
322 return utils::status::success();
323 }
324 return utils::status("UBlock either requires array or object configuration");
325}
326
327FLASHMEM
329 float constant = cfg.as<float>();
330 if (cfg.is<bool>())
331 constant = cfg.as<bool>() ? 1 : 0;
332 if (cfg.isNull())
333 constant = 0;
334 if (constant == 0) {
335 // Turn OFF constant usage. We don't use Transmission_Mode::GROUND because
336 // that's really pointless, we then also can just not connect the relevant UBlock switch.
337 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::ANALOG_INPUT);
338 reset_reference_magnitude();
340 }
341 if (constant > 0)
342 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::POS_REF);
343 if (constant < 0)
344 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::NEG_REF);
345 if (abs(constant) == 0.1f)
346 change_reference_magnitude(blocks::UBlock::Reference_Magnitude::ONE_TENTH);
347 else if (abs(constant) == 1.0f)
348 change_reference_magnitude(blocks::UBlock::Reference_Magnitude::ONE);
349 else
350 return utils::status("UBlock::config_self_from_json: Pass +-0.1 or +-1.");
351 return utils::status::success();
352}
353
354FLASHMEM void blocks::UBlock::config_self_to_json(JsonObject &cfg) {
355 Entity::config_self_to_json(cfg);
356 // Save outputs into cfg
357 auto outputs_cfg = cfg.createNestedArray("outputs");
358 for (const auto &output : output_input_map) {
359 if (output >= 0)
360 outputs_cfg.add(output);
361 else
362 outputs_cfg.add(nullptr);
363 }
364 // Constant setting to JSON
365 if (b_side_mode == blocks::UBlock::Transmission_Mode::ANALOG_INPUT) {
366 cfg["constant"] = false;
367 } else {
368 float val = -123;
369 // we support here much more then at the setter side, because the firmware
370 // side has so much more states that simply have no representation at client side.
371 switch (b_side_mode) {
372 case blocks::UBlock::Transmission_Mode::POS_REF:
373 val = +1.0;
374 break;
375 case blocks::UBlock::Transmission_Mode::NEG_REF:
376 val = -1.0;
377 break;
378 case blocks::UBlock::Transmission_Mode::GROUND:
379 val = 0;
380 break;
381 default:
382 break;
383 }
384 if (ref_magnitude == blocks::UBlock::Reference_Magnitude::ONE_TENTH) {
385 val *= 0.1;
386 }
387 cfg["constant"] = val;
388 }
389}
390
391FLASHMEM
392blocks::UBlock *blocks::UBlock::from_entity_classifier(entities::EntityClassifier classifier,
393 bus::addr_t block_address) {
394 if (!classifier or classifier.class_enum != CLASS_ or classifier.type != TYPE)
395 return nullptr;
396
397 if (classifier.version < entities::Version(1, 2))
398 return nullptr;
399 if (classifier.version < entities::Version(1, 3)) {
400 auto *new_block = new UBlock(new UBlockHAL_V_1_2_X(block_address));
401 new_block->classifier = classifier;
402 return new_block;
403 }
404 return nullptr;
405}
406
407FLASHMEM void blocks::UBlock::reset_reference_magnitude() { ref_magnitude = Reference_Magnitude::ONE; }
408
409FLASHMEM bool blocks::UBlockHAL_Dummy::write_outputs(std::array<int8_t, 32> outputs) { return true; }
410
412 std::pair<Transmission_Mode, Transmission_Mode> modes, blocks::UBlockHAL::Reference_Magnitude ref) {
413 return true;
414}
415
417
418FLASHMEM blocks::UBlockHAL_Common::UBlockHAL_Common(bus::addr_t block_address, const uint8_t f_umatrix_cs,
419 const uint8_t f_umatrix_sync_cs,
420 const uint8_t f_transmission_mode_register_cs,
421 const uint8_t f_transmission_mode_sync_cs,
422 const uint8_t f_transmission_mode_reset_cs)
423 : f_meta(block_address), f_umatrix(bus::replace_function_idx(block_address, f_umatrix_cs)),
424 f_umatrix_sync(bus::replace_function_idx(block_address, f_umatrix_sync_cs)),
425 f_transmission_mode_register(bus::replace_function_idx(block_address, f_transmission_mode_register_cs),
426 true),
427 f_transmission_mode_sync(bus::replace_function_idx(block_address, f_transmission_mode_sync_cs)),
428 f_transmission_mode_reset(bus::replace_function_idx(block_address, f_transmission_mode_reset_cs)) {}
429
430FLASHMEM bool blocks::UBlockHAL_Common::write_outputs(std::array<int8_t, 32> outputs) {
431 if (!f_umatrix.transfer(outputs))
432 return false;
433 f_umatrix_sync.trigger();
434 return true;
435}
436
438 std::pair<Transmission_Mode, Transmission_Mode> modes, Reference_Magnitude ref) {
439 // Reference magnitude is defined by lowest bit
440 // Group "A" mode is defined by the next two bits
441 // Group "B" mode is defined by the next two bits
442 // Remaining bits are not used
443 uint8_t data = 0b000'00'00'0;
444 data |= static_cast<uint8_t>(ref);
445 data |= (static_cast<uint8_t>(modes.first) << 1);
446 data |= (static_cast<uint8_t>(modes.second) << 3);
447
448 if (!f_transmission_mode_register.transfer8(data))
449 return false;
450 f_transmission_mode_sync.trigger();
451 return true;
452}
453
455 f_transmission_mode_reset.trigger();
456}
A function block represents one module in a cluster, such as an M-Block, C-Block, I-Block or U-Block.
Definition base.h:29
bool write_outputs(std::array< int8_t, 32 > outputs) override
Definition ublock.cpp:430
void reset_transmission_modes_and_ref() override
Definition ublock.cpp:454
bool write_transmission_modes_and_ref(std::pair< Transmission_Mode, Transmission_Mode > modes, Reference_Magnitude ref) override
Definition ublock.cpp:437
UBlockHAL_Common(bus::addr_t block_address, uint8_t f_umatrix_cs, uint8_t f_umatrix_sync_cs, uint8_t f_transmission_mode_register_cs, uint8_t f_transmission_mode_sync_cs, uint8_t f_transmission_mode_reset_cs)
Definition ublock.cpp:418
void reset_transmission_modes_and_ref() override
Definition ublock.cpp:416
bool write_transmission_modes_and_ref(std::pair< Transmission_Mode, Transmission_Mode > modes, Reference_Magnitude ref) override
Definition ublock.cpp:411
bool write_outputs(std::array< int8_t, 32 > outputs) override
Definition ublock.cpp:409
The Lucidac U-Block (U for Voltage) is represented by this class.
Definition ublock.h:123
bool connect(const uint8_t input, const uint8_t output, const bool force=false)
Connects a block input to a block output.
Definition ublock.cpp:50
metadata::eui_t get_entity_eui() const override
Definition ublock.cpp:32
bool disconnect(const uint8_t input, const uint8_t output)
Disconnect an input from an output, if they are connected. Both input and output are zero-based indiz...
Definition ublock.cpp:140
utils::status write_to_hardware() override
returns true in case of success
Definition ublock.cpp:233
bool is_connected(const uint8_t input, const uint8_t output) const
Check whether an chip input is connected to an chip / block output.
Definition ublock.cpp:163
utils::status config_self_from_json(JsonObjectConst cfg) override
Deserialize a new configuration for this entity from a JsonObject.
Definition ublock.cpp:257
utils::status _config_outputs_from_json(const JsonVariantConst &cfg)
Definition ublock.cpp:278
bool _is_connected(const uint8_t input, const uint8_t output) const
Check whether an input is connected to an output, without sanity checks.
Definition ublock.cpp:159
static bool _i_sanity_check(const uint8_t input)
Definition ublock.cpp:38
void reset(entities::ResetAction action) override
Definition ublock.cpp:246
bool is_anything_connected() const
Definition ublock.cpp:194
void _connect(const uint8_t input, const uint8_t output)
Connects output with given input, without sanity checks or disconnection prevention.
Definition ublock.cpp:46
void change_b_side_transmission_mode(const Transmission_Mode mode)
Changes the transmission mode of the alternative ublock switches.
Definition ublock.cpp:205
static UBlock * from_entity_classifier(entities::EntityClassifier classifier, bus::addr_t block_address)
Definition ublock.cpp:392
void change_a_side_transmission_mode(const Transmission_Mode mode)
Changes the transmission mode of the regular ublock switches.
Definition ublock.cpp:201
bool is_input_connected(const uint8_t input) const
Check whether an chip input is connected to any output.
Definition ublock.cpp:188
bool is_output_connected(const uint8_t output) const
Check whether an chip / block output is connected to any chip input.
Definition ublock.cpp:174
static constexpr auto CLASS_
Definition ublock.h:126
void config_self_to_json(JsonObject &cfg) override
Serialize the configuration of this entity to a JsonObject.
Definition ublock.cpp:354
static bool _o_sanity_check(const uint8_t output)
Definition ublock.cpp:40
void change_all_transmission_modes(const Transmission_Mode mode)
Changes the transmission mode for all ublock switches.
Definition ublock.cpp:209
Reference_Magnitude get_reference_magnitude()
Definition ublock.cpp:225
void reset_connections()
Definition ublock.cpp:242
static bool _io_sanity_check(const uint8_t input, const uint8_t output)
Definition ublock.cpp:42
void reset_reference_magnitude()
Definition ublock.cpp:407
bool _is_input_connected(const uint8_t input) const
Definition ublock.cpp:181
void change_reference_magnitude(Reference_Magnitude ref)
Definition ublock.cpp:229
bool connect_alternative(Transmission_Mode signal_type, const uint8_t output, const bool force=false, bool use_a_side=false)
Connects an alternative input / non block input specified by signal_mode from the a- or b-side to a b...
Definition ublock.cpp:80
std::pair< Transmission_Mode, Transmission_Mode > get_all_transmission_modes() const
Definition ublock.cpp:221
utils::status _config_constants_from_json(const JsonVariantConst &cfg)
Definition ublock.cpp:328
void _disconnect(const uint8_t output)
Disconnects output, without sanity checks.
Definition ublock.cpp:138
bool _is_output_connected(const uint8_t output) const
Check whether an output is connected to any input, without sanity checks.
Definition ublock.cpp:170
EntityClassifier classifier
Definition base.h:217
virtual void reset(ResetAction action)
Definition base.h:155
A DataFunction class wraps SPI communication over the digital bus.
Definition functions.h:47
UMatrixFunction(bus::addr_t address)
Definition ublock.cpp:23
static const SPISettings DEFAULT_SPI_SETTINGS
Definition ublock.h:21
A recoverable error, inspired from https://abseil.io/docs/cpp/guides/status and https://github....
Definition error.h:35
static status failure()
Syntactic sugar for failure.
Definition error.h:107
static status success()
Syntactic sugar for success.
Definition error.h:104
uint32_t uint32_t size
Definition flasher.cpp:63
#define LOG(LOG_FLAG, message)
Definition logging.h:36
Definition bus.h:22
uint16_t addr_t
Definition bus.h:27
The AD8402 is a two channel, 8 bit digital potentiometer.
Definition functions.h:12
std::array< uint8_t, 8 > eui_t
Definition base.h:20
void shift_5_left(uint8_t *buffer, size_t size)
Definition ublock.cpp:11
bool has(uint8_t other)
Definition base.h:111
static constexpr uint8_t CIRCUIT_RESET
Definition base.h:102