REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
mblock_mdr.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#include <algorithm>
5#include <bitset>
6
7#include "carrier/carrier.h"
8#include "carrier/cluster.h"
9
10#include "block/mblock_mdr.h"
12
13using namespace blocks;
14
15extern int abs_clamp(float in, int min, int max);
16
17blocks::MMDRBlock *blocks::MMDRBlock::from_entity_classifier(entities::EntityClassifier classifier,
18 const bus::addr_t block_address) {
19 if (!classifier or classifier.class_enum != CLASS_ or classifier.type != static_cast<uint8_t>(TYPE))
20 return nullptr;
21
22 // Currently, there are no different variants
23 if (classifier.variant != entities::EntityClassifier::DEFAULT_)
24 return nullptr;
25
26 SLOT slot = block_address % 8 == 4 ? SLOT::M0 : SLOT::M1;
27 // Return default implementation
28 if (classifier.version < entities::Version(1))
29 return nullptr;
30 if (classifier.version < entities::Version(1, 1)) {
31 auto *new_block = new MMDRBlock(slot, new MMDRBlockHAL_V_1_0_X(block_address));
32 new_block->classifier = classifier;
33 return new_block;
34 }
35 return nullptr;
36}
37
38status blocks::MMDRBlock::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
40 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
41
42 // Our first simple calibration process has been developed empirically and goes
43 // 1. Set all inputs to zero
44 // - The input offsets are rather small, so inputs are 0+-epsilon
45 // - The output is then roughly 0 + epsilon^2 - offset_z ~= -offset_z
46 // 2. Measure output and use it as a first estimate of offset_z
47 // 3. Set inputs to 0 and 1 and change the first one's offset to get an output close to zero
48
49 success.attach(MBlock::calibrate(cluster, carrier));
50
51 // TODO: keep old circuits alive
52 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
53
54 // We can't iterativly calibrate, so we need to delete the old calibration values.
55 reset(entities::ResetAction::CALIBRATION_RESET);
56
57 // Connect zeros to all our inputs
58 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating output offsets...");
59 for (auto idx : SLOT_INPUT_IDX_RANGE())
60 success.attach(cluster->add_constant(UBlock::Transmission_Mode::GROUND, slot_to_global_io_index(idx), 0.0f,
61 slot_to_global_io_index(idx)));
62 success.attach(cluster->write_to_hardware());
63
64 // When setting routes we need to calibrate them
65 success.attach(carrier->calibrate_routes());
66
67 // Measure offset_z and set it
68 auto read_outputs = daq::average(daq::sample, 4, 10);
69
70 for (auto idx = 0u; idx < NUM_CELLS; idx++) {
71 calibration[idx].offset_z = MMDRBlockHAL_V_1_0_X::float_to_raw_calibration(-read_outputs[idx]);
72 if (!hardware->write_calibration_output_offset(idx, calibration[idx].offset_z))
73 success.attach("Writing output offsets failed");
74 }
75 delay(10); // Small transient time
76
77 // Set some inputs to one
78 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating x input offsets...");
79 cluster->ublock->change_b_side_transmission_mode(UBlock::Transmission_Mode::POS_REF); // Set constant source
80 for (auto idx = 0u; idx < NUM_CELLS; idx++)
81 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2), 1.0f); // Setting 1.0 to Y inputs
82
83 if (!cluster->cblock->write_to_hardware())
84 success.attach("Writing to c-block failed");
85 // When changing a factor, we always have to calibrate offset
86 success.attach(cluster->calibrate_offsets());
87
88 delay(10);
89
90 const float target_precision = 0.0005f;
91 const int max_loops = 100;
92
93 std::bitset<NUM_CELLS> done_channels;
94 int loop_count = 0;
95
96 // start varying the x input offsets
97 while (!done_channels.all()) {
98 read_outputs = daq::average(daq::sample, 4, 10);
99
100 for (auto idx = 0u; idx < NUM_CELLS; idx++) {
101 if (fabs(read_outputs[idx]) < target_precision) {
102 done_channels[idx] = true; // Already precice
103 continue;
104 }
105 done_channels[idx] = false;
106
107 // Little bounded proportional controller, to decrease calibration time
108 calibration[idx].offset_x += abs_clamp(read_outputs[idx] * 6000.0f, 1, 50);
109
110 if (!hardware->write_calibration_input_offsets(idx, calibration[idx].offset_x,
111 calibration[idx].offset_y)) {
112 calibration[idx].offset_x = std::clamp(calibration[idx].offset_x, MMDRBlockHAL_V_1_0_X::RAW_ZERO,
114 success.attach("Input X offsets out of range");
115 done_channels[idx] = true;
116 continue;
117 }
118 }
119 delay(10); // Small transient time
120
121 loop_count++;
122 if (loop_count > max_loops) {
123 success.attach("Calibration timed out!");
124 break;
125 }
126 }
127
128 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating y input offsets...");
129 for (auto idx = 0u; idx < NUM_CELLS; idx++) {
130 // Setting 0.0 to Y inputs which previously were 1.0
131 // Setting 1.0 to X inputs
132 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2), 0.0f);
133 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2 + 1), 1.0f);
134 }
135 if (!cluster->cblock->write_to_hardware())
136 success.attach("Writing to c-block failed");
137 // When changing a factor, we always have to calibrate offset
138 success.attach(cluster->calibrate_offsets());
139
140 delay(10);
141
142 read_outputs = daq::average(daq::sample, 4, 10);
143
144 done_channels.reset();
145 loop_count = 0;
146
147 // start varying the y input offsets
148 while (!done_channels.all()) {
149 read_outputs = daq::average(daq::sample, 4, 10);
150
151 for (auto idx = 0u; idx < NUM_CELLS; idx++) {
152 if (fabs(read_outputs[idx]) < target_precision) {
153 done_channels[idx] = true; // Already precice
154 continue;
155 }
156 done_channels[idx] = false;
157
158 // Little bounded proportional controller, to decrease calibration time
159 calibration[idx].offset_y += abs_clamp(read_outputs[idx] * 6000.0f, 1, 50);
160
161 if (!hardware->write_calibration_input_offsets(idx, calibration[idx].offset_x,
162 calibration[idx].offset_y)) {
163 calibration[idx].offset_y = std::clamp(calibration[idx].offset_y, MMDRBlockHAL_V_1_0_X::RAW_ZERO,
165 success.attach("Input Y offsets out of range");
166 done_channels[idx] = true;
167 continue;
168 }
169 }
170 delay(10); // Small transient time
171
172 loop_count++;
173 if (loop_count > max_loops) {
174 success.attach("Calibration timed out!");
175 break;
176 }
177 }
178
179 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating output gains...");
180
181 // Setting 1.0 to Y inputs which previously were 0.0
182 for (auto idx = 0u; idx < NUM_CELLS; idx++)
183 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2), 1.0f);
184
185 if (!cluster->cblock->write_to_hardware())
186 success.attach("Writing to c-block failed");
187 // When changing a factor, we always have to calibrate offset
188 success.attach(cluster->calibrate_offsets());
189
190 delay(10);
191
192 loop_count = 0;
193 done_channels.reset();
194
195 // start varying the gain factors
196 while (!done_channels.all()) {
197 read_outputs = daq::average(daq::sample, 4, 10);
198
199 for (auto idx = 0u; idx < NUM_CELLS; idx++) {
200 if (fabs(read_outputs[idx] - 1.0f) < target_precision) {
201 done_channels[idx] = true; // Already precice
202 continue;
203 }
204 done_channels[idx] = false;
205
206 // Little bounded proportional controller, to decrease calibration time
207 int step = abs_clamp((read_outputs[idx] - 1.0f) * -1000.0f, 1, 70);
208
209 if (calibration[idx].gain + step > 0xff || calibration[idx].gain + step < 0) {
210 success.attach("Output gain out of range");
211 done_channels[idx] = true;
212 continue;
213 }
214
215 calibration[idx].gain += step;
216
217 if (!hardware->write_calibration_gain(idx, calibration[idx].gain)) {
218 success.attach("Writing output gain failed");
219 done_channels[idx] = true;
220 continue;
221 }
222 }
223 delay(10); // Small transient time
224
225 loop_count++;
226 if (loop_count > max_loops) {
227 success.attach("Calibration timed out!");
228 break;
229 }
230 }
231
232 return success;
233}
234
235bool blocks::MMDRBlockHAL::init() { return MBlockHAL::init(); }
236
238 : MMDRBlockHAL_Parent(block_address, 0xff),
239 f_overload_flags(bus::replace_function_idx(block_address, 2), 0xff),
240 f_overload_flags_reset(bus::replace_function_idx(block_address, 3)),
241 f_calibration_dac_0(bus::address_from_tuple(block_address, 4), 0xff, 2.0f),
242 f_calibration_dac_1(bus::address_from_tuple(block_address, 5), 0xff, 2.0f),
243 f_gain(bus::replace_function_idx(block_address, 6), 0xff),
244 f_mode_config(bus::address_from_tuple(block_address, 7), 0xff, true),
245 f_mode_config_sync(bus::address_from_tuple(block_address, 8)) {}
246
248
250
251bool MMDRBlockHAL_V_1_0_X::write_modes(MMDRBlock::CellMode m0, MMDRBlock::CellMode m1, MMDRBlock::CellMode m2,
252 MMDRBlock::CellMode m3) {
253 // Output of SR is
254 // Last bit shifted in > [4bit Cell 3][4 bit Cell 2][4 bit Cell 1][4 bit Cell 0] < First bit shifted in
255 // Data is transfered MSBFIRST, so should look like
256 // MSB > [4bit Cell 0][Cell 1][Cell 2][Cell 3] < LSB
257 uint16_t data = (static_cast<uint16_t>(m0) << 12) | (static_cast<uint16_t>(m1) << 8) |
258 (static_cast<uint16_t>(m2) << 4) | (static_cast<uint16_t>(m3) << 0);
259 if (!f_mode_config.transfer16(data))
260 return false;
261 f_mode_config_sync.trigger();
262
263 return true;
264}
265
267 uint16_t offset_y) {
268 return f_calibration_dac_0.set_channel_raw(idx * 2 + 1, offset_x) and
269 f_calibration_dac_0.set_channel_raw(idx * 2, offset_y);
270}
271
272bool MMDRBlockHAL_V_1_0_X::write_calibration_output_offset(uint8_t idx, uint16_t offset_z) {
273 return f_calibration_dac_1.set_channel_raw(idx, offset_z);
274}
275
276bool MMDRBlockHAL_V_1_0_X::write_calibration_gain(uint8_t idx, uint8_t gain) {
277 if (idx == 0)
278 return f_gain.write_channel_raw(2, gain);
279 else if (idx == 1)
280 return f_gain.write_channel_raw(0, gain);
281 else if (idx == 2)
282 return f_gain.write_channel_raw(1, gain);
283 if (idx == 3)
284 return f_gain.write_channel_raw(3, gain);
285 return false;
286}
const functions::SR74HC16X f_overload_flags
Definition mblock_mdr.h:20
functions::AD8403 f_gain
Definition mblock_mdr.h:24
bool write_calibration_gain(uint8_t idx, uint8_t gain) override
static uint16_t float_to_raw_calibration(float value)
Takes in voltage recieved by multiplier, returns DAC raw value.
Definition mblock_mdr.h:52
std::bitset< 8 > read_overload_flags() override
static constexpr uint16_t RAW_MAX
Definition mblock_mdr.h:58
const functions::SR74HCT595 f_mode_config
Definition mblock_mdr.h:25
const functions::DAC60508 f_calibration_dac_1
Definition mblock_mdr.h:23
void reset_overload_flags() override
const functions::DAC60508 f_calibration_dac_0
Definition mblock_mdr.h:22
bool write_calibration_output_offset(uint8_t idx, uint16_t offset_z) override
static constexpr uint16_t RAW_ZERO
Definition mblock_mdr.h:57
const functions::TriggerFunction f_mode_config_sync
Definition mblock_mdr.h:26
const functions::TriggerFunction f_overload_flags_reset
Definition mblock_mdr.h:21
bool write_calibration_input_offsets(uint8_t idx, uint16_t offset_x, uint16_t offset_y) override
bool write_modes(MMDRBlock::CellMode m0, MMDRBlock::CellMode m1, MMDRBlock::CellMode m2, MMDRBlock::CellMode m3) override
MMDRBlockHAL_V_1_0_X(bus::addr_t block_address)
utils::status status
Definition daq.h:21
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:20
static constexpr int success
Definition flasher.cpp:275
entities::EntitySharedHardware< MMDRBlockHAL > MMDRBlockHAL_Parent
Definition mblock_mdr.h:16
Definition bus.h:21
Definition daq.h:14