REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
mblock_int.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#include <bitset>
7
9#include <utils/logging.h>
10
11#include <carrier/carrier.h>
12#include <carrier/cluster.h>
13#include <io/io.h>
14#include <mode/teensy/mode.h>
15
16#include <chips/AD840X.h>
17#include <chips/DAC60508.h>
18#include <chips/SR74HC16X.h>
19#include <chips/SR74HCT595.h>
20
21extern int abs_clamp(float in, int min, int max);
22
23FLASHMEM blocks::MIntBlock *blocks::MIntBlock::from_entity_classifier(entities::EntityClassifier classifier,
24 const bus::addr_t block_address) {
25 if (!classifier or classifier.class_enum != CLASS_ or classifier.type != static_cast<uint8_t>(TYPE))
26 return nullptr;
27
28 // Currently, there are no different variants
29 if (classifier.variant != entities::EntityClassifier::DEFAULT_)
30 return nullptr;
31
32 SLOT slot = block_address % 8 == 4 ? SLOT::M0 : SLOT::M1;
33 if (classifier.version < entities::Version(1))
34 return nullptr;
35 if (classifier.version < entities::Version(1, 1)) {
36 auto *new_block = new MIntBlock(slot, new MIntBlockHAL_V_1_0_X(block_address));
37 new_block->classifier = classifier;
38 return new_block;
39 }
40 if (classifier.version < entities::Version(1, 2)) {
41 auto *new_block = new MIntBlock_V_1_1_X(slot, new MIntBlockHAL_V_1_1_X(block_address));
42 new_block->classifier = classifier;
43 return new_block;
44 }
45 return nullptr;
46}
47
48// V1.1.0
49
50FLASHMEM void blocks::MIntBlock_V_1_1_X::reset(entities::ResetAction action) {
51 MIntBlock::reset(action);
52
53 if (action.has(entities::ResetAction::CALIBRATION_RESET)) {
54 for (auto idx = 0u; idx < MIntBlock::NUM_INTEGRATORS; idx++)
55 calibration[idx] = {};
56 }
57}
58
59FLASHMEM
60const std::array<blocks::IntegratorCalibration, blocks::MIntBlock_V_1_1_X::NUM_INTEGRATORS> &
62 return calibration;
63}
64
65FLASHMEM blocks::IntegratorCalibration blocks::MIntBlock_V_1_1_X::get_calibration(uint8_t int_idx) const {
66 if (int_idx >= NUM_INTEGRATORS)
67 return {};
68 return calibration[int_idx];
69}
70
72 auto res = MIntBlock::write_to_hardware();
73 if (!res)
74 return res;
75
76 return write_calibration_to_hardware();
77}
78
79FLASHMEM bool blocks::MIntBlock_V_1_1_X::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
80 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
81
82 if (!MBlock::calibrate(cluster, carrier))
83 return false;
84
85 // Currently this is the only integrator version which can be calibrated automatically
86 bool success = true;
87
88 // TODO: keep old circuits alive
89 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
90
91 LOG(ANABRID_DEBUG_CALIBRATION, "Connecting calibration signals!");
92
93 // Connect +0.1 to all integrator inputs. 0.1 because we have high precision and a long integration time.
94 for (auto idx : SLOT_INPUT_IDX_RANGE())
95 if (!cluster->add_constant(UBlock::Transmission_Mode::POS_REF, slot_to_global_io_index(idx), 1.0f,
96 slot_to_global_io_index(idx)))
97 return "Could not connect constants to inputs."; // Fatal error
98
99 cluster->ublock->change_reference_magnitude(UBlock::Reference_Magnitude::ONE_TENTH);
100
101 if (!cluster->write_to_hardware())
102 return "Error while writing new configuration to hardware."; // Fatal error
103
104 // When setting routes we need to calibrate them
105 if (!carrier->calibrate_routes_in_cluster(*cluster))
106 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating routes failed!");
107
108 LOG(ANABRID_DEBUG_CALIBRATION, "Starting ramp integration on fast mode!");
109 set_time_factors(10000);
110 set_ic_values(-1.0f);
111 if (!write_to_hardware())
112 return false;
113
114 success &= _gain_calibration(false); // Fast time constant
115 LOG(ANABRID_DEBUG_CALIBRATION, "Finished ramp integration on fast mode!");
116
117 LOG(ANABRID_DEBUG_CALIBRATION, "Starting ramp integration on slow mode!");
118 set_time_factors(100);
119 if (!write_to_hardware())
120 return false;
121
122 success &= _gain_calibration(true); // Slow time constant
123 LOG(ANABRID_DEBUG_CALIBRATION, "Finished ramp integration on slow mode!");
124
125 // Measure end value
126 return success;
127}
128
130 for (size_t i = 0; i < NUM_INTEGRATORS; i++) {
131 if (!hardware->write_time_factor_gain(i, time_factors[i] == 10000 ? calibration[i].time_factor_gain_fast
132 : calibration[i].time_factor_gain_slow))
133 return utils::status::failure();
134 }
135 return utils::status::success();
136}
137
138FLASHMEM bool blocks::MIntBlock_V_1_1_X::_gain_calibration(bool use_slow_integration) {
139 bool success = true;
140 const float target_precision = 0.001f;
141 const int max_loops = 100;
142
143 // TODO: currently we cant reach the other four channels
144 std::bitset<NUM_INTEGRATORS> done_channels;
145 int loop_count = 0;
146
147 // start varying the x input offsets
148 while (!done_channels.all()) {
149 // Start calculation
151 if (!mode::FlexIOControl::init(use_slow_integration ? mode::DEFAULT_IC_TIME * 100 : mode::DEFAULT_IC_TIME,
152 use_slow_integration ? 200'000'000 : 2'000'000, mode::OnOverload::IGNORE,
153 mode::OnExtHalt::IGNORE))
154 LOG(ANABRID_DEBUG_CALIBRATION, "Timer failed!");
155
158 }
159
160 auto read_outputs = daq::average(daq::sample, 4, 10);
161
162 for (auto idx = 0u; idx < NUM_INTEGRATORS; idx++) {
163 uint8_t *time_factor_gain = use_slow_integration ? &calibration[idx].time_factor_gain_slow
164 : &calibration[idx].time_factor_gain_fast;
165 if (fabs(read_outputs[idx] + 1.0f) < target_precision) {
166 done_channels[idx] = true; // Already precice
167 continue;
168 }
169 done_channels[idx] = false;
170
171 // Little bounded proportional controller, to decrease calibration time
172 int step = abs_clamp((read_outputs[idx] + 1.0f) * -800.0f, 1, 70);
173
174 if (*time_factor_gain + step > 0xff || *time_factor_gain + step < 0) {
175 *time_factor_gain = std::clamp((int)(*time_factor_gain) + step, 0, 0xff);
176 success = false; // Out of bounds for factor
177 done_channels[idx] = true;
178 } else {
179 *time_factor_gain += step;
180 }
181
182 if (!hardware->write_time_factor_gain(idx, *time_factor_gain)) {
183 success = false; // Other issues
184 done_channels[idx] = true;
185 continue;
186 }
187 }
188 delay(10); // Small transient time
189
190 loop_count++;
191 if (loop_count > max_loops) {
192 LOG(ANABRID_DEBUG_CALIBRATION, "Calibration timed out!");
193 return false;
194 }
195 }
196
197 return success;
198}
199
201 : MIntBlock(slot, hardware), hardware(hardware) {}
202
203// Hardware abstraction layer
204
205FLASHMEM blocks::MIntBlockHAL_V_1_0_X::MIntBlockHAL_V_1_0_X(bus::addr_t block_address)
206 : blocks::MIntBlockHAL_Parent(block_address),
207 f_ic_dac(bus::replace_function_idx(block_address, 4), 2.0f),
208 f_time_factor(bus::replace_function_idx(block_address, 5), true),
209 f_time_factor_sync(bus::replace_function_idx(block_address, 6)),
210 f_time_factor_reset(bus::replace_function_idx(block_address, 7)),
211 f_overload_flags(bus::replace_function_idx(block_address, 2)),
212 f_overload_flags_reset(bus::replace_function_idx(block_address, 3)) {}
213
215 if (!MIntBlockHAL::init())
216 return false;
217 return f_ic_dac.init() and f_ic_dac.set_external_reference(true) and f_ic_dac.set_double_gain(true);
218}
219
220FLASHMEM bool blocks::MIntBlockHAL_V_1_0_X::write_ic(uint8_t idx, float ic) {
221 if (idx >= MIntBlock::NUM_INTEGRATORS)
222 return false;
223 // Note: The output is level-shifted, such that IC = 2V - output.
224 return f_ic_dac.set_channel(idx, ic + 1.0f);
225}
226
227FLASHMEM bool blocks::MIntBlockHAL_V_1_0_X::write_time_factor_switches(std::bitset<8> switches) {
228 if (!f_time_factor.transfer8(static_cast<uint8_t>(switches.to_ulong())))
229 return false;
230 f_time_factor_sync.trigger();
231 return true;
232}
233
235 return f_overload_flags.read8();
236}
237
238FLASHMEM void blocks::MIntBlockHAL_V_1_0_X::reset_overload_flags() { f_overload_flags_reset.trigger(); }
239
240FLASHMEM blocks::MIntBlockHAL_V_1_1_X::MIntBlockHAL_V_1_1_X(bus::addr_t block_address)
241 : MIntBlockHAL_V_1_0_X(block_address), f_time_factor_gain_0_3(bus::replace_function_idx(block_address, 8)),
242 f_time_factor_gain_4_7(bus::replace_function_idx(block_address, 9)) {}
243
244FLASHMEM bool blocks::MIntBlockHAL_V_1_1_X::write_time_factor_switches(std::bitset<8> switches) {
245 // In version 1.1, a new shift register has been chained behind the time factors shift register,
246 // instead of recieving a separate function address, so we can only set them together.
247 // If we try to set only the time facors, we shift their old state into the shift register behind,
248 // and would change the limeters enable randomly.
249 // So we need to read out the current value and change only the lower 8 bits.
250 uint16_t current;
251 if (!f_time_factor.transfer16(static_cast<uint8_t>(switches.to_ulong()), &current))
252 return false;
253 auto new_ = (current & 0xFF00) | static_cast<uint8_t>(switches.to_ulong());
254 if (!f_time_factor.transfer16(new_))
255 return false;
256 f_time_factor_sync.trigger();
257 return true;
258}
259
260FLASHMEM bool blocks::MIntBlockHAL_V_1_1_X::write_limiters_enable(std::bitset<8> limiters) {
261 // In version 1.1, a new shift register has been chained behind the time factors shift register,
262 // instead of recieving a separate function address, so we can only set them together.
263 // If we try to set only the limiters, we also shift data into the time factors switches,
264 // and would change them.
265 // So we need to read out the current value and change only the upper 8 bits.
266 uint16_t current;
267 if (!f_time_factor.transfer16(static_cast<uint16_t>(limiters.to_ulong()) << 8, &current))
268 return false;
269 auto new_ = (current & 0x00FF) | (static_cast<uint16_t>(limiters.to_ulong()) << 8);
270 if (!f_time_factor.transfer16(new_))
271 return false;
272 f_time_factor_sync.trigger();
273 return true;
274}
275
276FLASHMEM bool
278 std::bitset<8> limiters) {
279 // In version 1.1, a new shift register has been chained behind the time factors shift register,
280 // instead of recieving a separate function address, so we can only set them together.
281 if (!f_time_factor.transfer16(static_cast<uint16_t>(limiters.to_ulong()) << 8 |
282 static_cast<uint8_t>(switches.to_ulong())))
283 return false;
284 f_time_factor_sync.trigger();
285 return true;
286}
287
288FLASHMEM bool blocks::MIntBlockHAL_V_1_1_X::write_ic(uint8_t idx, float ic) {
289 if (idx >= MIntBlock::NUM_INTEGRATORS)
290 return false;
291 // Note: The output is level-shifted differently than on Version 1.0, such that IC = output - 2V.
292 return f_ic_dac.set_channel(idx, 1.0f - ic);
293}
294
295FLASHMEM bool blocks::MIntBlockHAL_V_1_1_X::write_time_factor_gain(uint8_t idx, uint8_t gain) {
296 if (idx >= MIntBlock::NUM_INTEGRATORS)
297 return false;
298
299 if (idx < 4)
300 return f_time_factor_gain_0_3.write_channel_raw(idx, gain);
301 else
302 return f_time_factor_gain_4_7.write_channel_raw(idx - 4, gain);
303}
std::bitset< 8 > read_overload_flags() override
MIntBlockHAL_V_1_0_X(bus::addr_t block_address)
bool write_ic(uint8_t idx, float ic) override
bool write_time_factor_switches(std::bitset< 8 > switches) override
void reset_overload_flags() override
virtual bool write_time_factor_gain(uint8_t idx, uint8_t gain)
virtual bool write_limiters_enable(std::bitset< 8 > limiters)
bool write_time_factor_switches(std::bitset< 8 > switches) override
MIntBlockHAL_V_1_1_X(bus::addr_t block_address)
bool write_ic(uint8_t idx, float ic) override
virtual bool write_time_factor_switches_and_limiters_enable(std::bitset< 8 > switches, std::bitset< 8 > limiters)
std::array< IntegratorCalibration, NUM_INTEGRATORS > calibration
Definition mblock_int.h:76
utils::status write_calibration_to_hardware()
void reset(entities::ResetAction action) override
MIntBlock_V_1_1_X(SLOT slot, MIntBlockHAL_V_1_1_X *hardware)
bool _gain_calibration(bool use_slow_integration)
const std::array< IntegratorCalibration, NUM_INTEGRATORS > & get_calibration() const
utils::status write_to_hardware() override
bool calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) override
static bool is_done()
Definition mode.cpp:476
static void reset()
Definition mode.cpp:405
static void force_start()
Definition mode.cpp:373
static bool init(unsigned long long ic_time_ns, unsigned long long op_time_ns, mode::OnOverload on_overload=mode::OnOverload::HALT, mode::OnExtHalt on_ext_halt=mode::OnExtHalt::IGNORE, mode::Sync sync=mode::Sync::NONE)
Definition mode.cpp:95
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:16
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:16
static constexpr int success
Definition flasher.cpp:275
Definition bus.h:21
Definition daq.h:14