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