REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
carrier.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 <carrier/carrier.h>
6#include <mode/mode.h>
7#include <net/settings.h>
8#include <utils/is_number.h>
9
10#ifdef ANABRID_BROKEN_EEPROM_WORKAROUND
11#include "block/teensy/ctrlblock.h" // in hardware library
12#endif
13
14// Keep in mind:
15// Carrier is not a regular Entity: It lacks an EEPROM.
16// However it has a temperature sensor.
17
18FLASHMEM carrier::Carrier::Carrier(std::vector<platform::Cluster> clusters, carrier::Carrier_HAL *hardware)
19 : Entity("", hardware), hardware(hardware), clusters(std::move(clusters)) {
20 classifier.class_enum = CLASS_;
21}
22
23FLASHMEM utils::status carrier::Carrier::better_init() {
24 LOG(ANABRID_DEBUG_INIT, __PRETTY_FUNCTION__);
25
26 entity_id = net::StartupConfig::get().mac;
27
28 if (entity_id.empty())
29 return utils::status("Cannot determine Carrier MAC");
30
31 // Detect CTRL-block
32 ctrl_block = entities::detect<blocks::CTRLBlock>(bus::address_from_tuple(1, 0));
33 if (!ctrl_block) {
34 #ifndef ANABRID_BROKEN_EEPROM_WORKAROUND
35 return utils::status("Missing CTRL Block");
36 #else
37 LOG_ALWAYS("Error: Cannot detect Control Block. Continuing");
38 ctrl_block = new blocks::CTRLBlock(new blocks::CTRLBlockHAL_V_1_0_2(1));
39 #endif
40 }
41
42 for (auto &cluster : clusters) {
43 if (!cluster.init())
44 return utils::status("Cannot initialize cluster");
45 }
46
47 reset(entities::ResetAction::EVERYTHING);
48
49 return utils::status::success();
50}
51
52FLASHMEM std::vector<entities::Entity *> carrier::Carrier::get_child_entities() {
53 std::vector<entities::Entity *> children;
54 for (auto &cluster : clusters) {
55 children.push_back(&cluster);
56 }
57 if (ctrl_block)
58 children.push_back(ctrl_block);
59 return children;
60}
61
62FLASHMEM entities::Entity *carrier::Carrier::get_child_entity(const std::string &child_id) {
63 if (utils::is_number(child_id.begin(), child_id.end())) {
64 auto cluster_idx = std::stol(child_id);
65 if (cluster_idx < 0 or clusters.size() < cluster_idx)
66 return nullptr;
67 return &clusters[cluster_idx];
68 }
69 if (child_id == "CTRL")
70 return ctrl_block;
71 return nullptr;
72}
73
74FLASHMEM utils::status carrier::Carrier::config_self_from_json(JsonObjectConst cfg) {
75 // TODO: Have an option to fail on unexpected configuration
76 return utils::status::success();
77}
78
79FLASHMEM utils::status carrier::Carrier::write_to_hardware() {
80 utils::status error;
81 int cluster_index = 1;
82 for (auto &cluster : clusters) {
83 if (!cluster.write_to_hardware()) {
84 error.code += cluster_index; // maxes to 6 = 0b110
85 error.msg += "Cluster write failed.";
86 }
87 cluster_index++;
88 }
89 if (ctrl_block && !ctrl_block->write_to_hardware()) {
90 error.code |= 0x8;
91 error.msg += "CTRL Block write failed.";
92 }
93 if (!hardware->write_adc_bus_mux(adc_channels)) {
94 error.code |= 0x10;
95 error.msg += "ADC Bus write failed.";
96 }
97 return error;
98}
99
100FLASHMEM bool carrier::Carrier::calibrate_offset() {
101 for (auto &cluster : clusters)
102 if (!cluster.calibrate_offsets())
103 return false;
104 return true;
105}
106
107FLASHMEM bool carrier::Carrier::calibrate_routes_in_cluster(platform::Cluster &cluster) {
108 // Save and change ADC bus selection
109 auto old_adcbus = ctrl_block->get_adc_bus();
110 ctrl_block->set_adc_bus_to_cluster_gain(cluster.get_cluster_idx());
111 if (!ctrl_block->write_to_hardware())
112 return false;
113
114 // Calibrate routes in cluster
115 if (!cluster.calibrate_routes())
116 return false;
117
118 // Restore ADC bus selection
119 ctrl_block->set_adc_bus(old_adcbus);
120 if (!ctrl_block->write_to_hardware())
121 return false;
122
123 return true;
124}
125
126FLASHMEM bool carrier::Carrier::calibrate_routes() {
127 for (auto &cluster : clusters) {
128 if (!calibrate_routes_in_cluster(cluster))
129 return false;
130 }
131 return true;
132}
133
134FLASHMEM bool carrier::Carrier::calibrate_mblock(platform::Cluster &cluster, blocks::MBlock &mblock) {
135 // CARE: This function does not preserve the currently configured routes
136 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
137
138 bool success = true;
139
140 // The calibration for each M-Block is prepared by connecting all outputs
141 // to the ADCs. This limits the calibration to one M-Block (and one cluster) at a time,
142 // which is not a problem though.
143 // Each M-Block must connect required reference signals by itself.
144
145 LOG(ANABRID_DEBUG_CALIBRATION, "Connecting outputs to ADC...");
146 for (auto output_idx : blocks::MBlock::SLOT_OUTPUT_IDX_RANGE()) {
147 auto lane_idx = mblock.slot_to_global_io_index(output_idx);
148 if (!set_adc_channel(output_idx, lane_idx))
149 return false;
150 }
151 // Write to hardware
152 if (!write_to_hardware())
153 return false;
154
155 // Pass to calibration function
156 LOG(ANABRID_DEBUG_CALIBRATION, "Passing control to M-block...");
157 success &= mblock.calibrate(&cluster, this);
158
159 LOG(ANABRID_DEBUG_CALIBRATION, "Cleanup ADC connections...");
160 reset_adc_channels();
161
162 LOG(ANABRID_DEBUG_CALIBRATION, "Cleaning up calibration signals...");
163 reset(entities::ResetAction::CIRCUIT_RESET);
164
165 // Write final clean-up to hardware
166 if (!write_to_hardware())
167 return false;
168
169 LOG(ANABRID_DEBUG_CALIBRATION, "Calibration done.");
170
171 return success;
172}
173
174FLASHMEM bool carrier::Carrier::calibrate_m_blocks() {
175 bool success = true;
176 for (auto &cluster : clusters)
177 for (auto mblock : {cluster.m0block, cluster.m1block}) {
178 if (mblock)
179 if (!calibrate_mblock(cluster, *mblock))
180 success = false;
181 }
182 return success;
183}
184
185FLASHMEM void carrier::Carrier::reset(entities::ResetAction action) {
186 for (auto &cluster : clusters) {
187 cluster.reset(action);
188 }
189 if (ctrl_block)
190 ctrl_block->reset(action);
191 reset_adc_channels();
192}
193
194FLASHMEM const std::array<int8_t, 8> &carrier::Carrier::get_adc_channels() const { return adc_channels; }
195
196FLASHMEM bool carrier::Carrier::set_adc_channels(const std::array<int8_t, 8> &channels) {
197 // Check that all inputs are < 15
198 for (auto channel : channels) {
199 if (channel > 15)
200 return false;
201 }
202 // Check that all inputs are different
203 // This is actually not necessary, in principle one could split one signal to multiple ADC.
204 // But for now we prohibit this, as it's more likely an error than an actual use-case.
205 for (auto &channel : channels) {
206 if (channel < 0)
207 continue;
208 for (auto &other_channel : channels) {
209 if (std::addressof(channel) == std::addressof(other_channel))
210 continue;
211 if (channel == other_channel)
212 return false;
213 }
214 }
215 adc_channels = channels;
216 return true;
217}
218
219FLASHMEM bool carrier::Carrier::set_adc_channel(uint8_t idx, int8_t adc_channel) {
220 if (idx >= adc_channels.size())
221 return false;
222 if (adc_channel < 0)
223 adc_channel = ADC_CHANNEL_DISABLED;
224
225 // Check if channel is already routed to an ADC
226 // This is not really necessary, but probably a good idea (see set_adc_channels)
227 if (adc_channel != ADC_CHANNEL_DISABLED)
228 for (auto other_idx = 0u; other_idx < adc_channels.size(); other_idx++)
229 if (idx != other_idx and adc_channels[other_idx] == adc_channel)
230 return false;
231
232 adc_channels[idx] = adc_channel;
233 return true;
234}
235
236FLASHMEM void carrier::Carrier::reset_adc_channels() {
237 std::fill(adc_channels.begin(), adc_channels.end(), ADC_CHANNEL_DISABLED);
238}
239
240FLASHMEM utils::status carrier::Carrier::user_set_extended_config(JsonObjectConst msg_in,
241 JsonObject &msg_out) {
242#ifdef ANABRID_DEBUG_COMMS
243 Serial.println(__PRETTY_FUNCTION__);
244#endif
245
246 bool default_reset_before = true, default_sh_kludge = true, default_mul_calib_kludge = true,
247 default_calibrate_mblock = false, default_calibrate_offset = false, default_calibrate_routes = false;
248
249 if (msg_in["reset_before"] | default_reset_before) {
250 reset(entities::ResetAction::CIRCUIT_RESET | entities::ResetAction::OVERLOAD_RESET |
251 entities::ResetAction::CALIBRATION_RESET);
252 write_to_hardware();
253 }
254
255 if (msg_in["sh_kludge"] | default_sh_kludge) {
256 for (auto &cluster : clusters) {
257 if (cluster.shblock) {
258 cluster.shblock->set_state(blocks::SHState::TRACK);
259 /*cluster.shblock->*/ write_to_hardware();
260 delayMicroseconds(1000);
261 cluster.shblock->set_state(blocks::SHState::INJECT);
262 /*cluster.shblock->*/ write_to_hardware();
263 }
264 }
265 }
266
267 if ((msg_in["calibrate_mblock"] | default_calibrate_mblock) && !calibrate_m_blocks())
268 return utils::status(10, "Calibrate MBlocks failed");
269
270 auto res = user_set_config(msg_in, msg_out);
271
272 if (!res)
273 return res;
274
275 if ((msg_in["calibrate_offset"] | default_calibrate_offset) && !calibrate_offset())
276 return utils::status(10, "Calibrate-offset failed");
277
278 if ((msg_in["calibrate_routes"] | default_calibrate_routes) && !calibrate_routes())
279 return utils::status(10, "Calibrate-Routes failed");
280
281 return utils::status::success();
282}
283
284FLASHMEM utils::status carrier::Carrier::user_get_overload_status(JsonObjectConst msg_in,
285 JsonObject &msg_out) {
286 msg_out["global_overload"] = mode::is_global_overload_active();
287
288 auto flags = msg_out.createNestedObject("entity_overload");
289 for (auto &cluster : clusters) {
290 auto cluster_obj = flags.createNestedObject("/" + cluster.get_entity_id());
291 for (auto &block : cluster.get_blocks()) {
292 /*if (block && block->is_entity_class(blocks::MMulBlock::CLASS_)) {
293 auto *mblock = static_cast<blocks::MBlock *>(block);
294 auto block_obj = cluster_obj.createNestedArray("/" + block->get_entity_id());
295 mblock->overload_flags_to_json(block_obj);
296 }*/
297 }
298 }
299
300 return utils::status::success();
301}
static constexpr int success
Definition flasher.cpp:275