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 <mode/teensy/mode.h>
14
15#include <chips/AD840X.h>
16#include <chips/DAC60508.h>
17#include <chips/SR74HC16X.h>
18#include <chips/SR74HCT595.h>
19
20extern int abs_clamp(float in, int min, int max);
21
22blocks::MIntBlock *blocks::MIntBlock::from_entity_classifier(entities::EntityClassifier classifier,
23 const bus::addr_t block_address) {
24 if (!classifier or classifier.class_enum != CLASS_ or classifier.type != static_cast<uint8_t>(TYPE))
25 return nullptr;
26
27 // Currently, there are no different variants
28 if (classifier.variant != entities::EntityClassifier::DEFAULT_)
29 return nullptr;
30
31 SLOT slot = block_address % 8 == 4 ? SLOT::M0 : SLOT::M1;
32 if (classifier.version < entities::Version(1))
33 return nullptr;
34 if (classifier.version < entities::Version(1, 1)) {
35 auto *new_block = new MIntBlock(slot, new MIntBlockHAL_V_1_0_X(block_address));
36 new_block->classifier = classifier;
37 return new_block;
38 }
39 if (classifier.version < entities::Version(1, 2)) {
40 auto *new_block = new MIntBlock_V_1_1_X(slot, new MIntBlockHAL_V_1_1_X(block_address));
41 new_block->classifier = classifier;
42 return new_block;
43 }
44 return nullptr;
45}
46
47// V1.1.0
48
49void blocks::MIntBlock_V_1_1_X::reset(entities::ResetAction action) {
50 MIntBlock::reset(action);
51
52 if (action.has(entities::ResetAction::CALIBRATION_RESET)) {
53 for (auto idx = 0u; idx < MIntBlock::NUM_INTEGRATORS; idx++)
54 calibration[idx] = {};
55 }
56
57 if (action.has(entities::ResetAction::CIRCUIT_RESET))
58 _limiters_enabled.reset();
59}
60
61
62const std::array<blocks::IntegratorCalibration, blocks::MIntBlock_V_1_1_X::NUM_INTEGRATORS> &
66
67blocks::IntegratorCalibration blocks::MIntBlock_V_1_1_X::get_calibration(uint8_t int_idx) const {
68 if (int_idx >= NUM_INTEGRATORS)
69 return {};
70 return calibration[int_idx];
71}
72
74 // Write IC values one channel at a time
75 for (decltype(ic_values.size()) i = 0; i < ic_values.size(); i++) {
76 if (!hardware->write_ic(i, ic_values[i])) {
77 LOG(ANABRID_PEDANTIC, __PRETTY_FUNCTION__);
78 return UnitResult::err("Error writing IC values to hardware.");
79 }
80 }
81
82 // Write time factor switches by converting to bitset
83 std::bitset<NUM_INTEGRATORS> time_factor_switches{};
84 for (auto idx = 0u; idx < time_factors.size(); idx++)
85 if (time_factors[idx] != DEFAULT_TIME_FACTOR)
86 time_factor_switches.set(idx);
87
88 if (!hardware->write_time_factor_switches_and_limiters_enable(time_factor_switches, _limiters_enabled))
89 return UnitResult::err("Error writing time factors or limiters");
91}
92
93UnitResult blocks::MIntBlock_V_1_1_X::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
94 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
95
96 TRY(MBlock::calibrate(cluster, carrier));
97
98 // TODO: keep old circuits alive
99 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
100 _limiters_enabled.reset();
101
102 LOG(ANABRID_DEBUG_CALIBRATION, "Connecting calibration signals!");
103
104 // Connect +0.1 to all integrator inputs. 0.1 because we have high precision and a long integration time.
105 for (auto idx : SLOT_INPUT_IDX_RANGE()) {
106 TRY(cluster->add_constant(
107 UBlock::Transmission_Mode::POS_REF,
108 slot_to_global_io_index(idx),
109 1.0f,
110 slot_to_global_io_index(idx)
111 ));
112 }
113
114 cluster->ublock->change_reference_magnitude(UBlock::Reference_Magnitude::ONE_TENTH);
115
116 TRY(cluster->write_to_hardware());
117
118 // When setting routes we need to calibrate them
119 TRY(carrier->calibrate_routes());
120
121 LOG(ANABRID_DEBUG_CALIBRATION, "Starting ramp integration on fast mode!");
122 set_time_factors(10000);
123 set_ic_values(-1.0f);
124 TRY(write_to_hardware());
125
126 TRY(_gain_calibration(false)); // Fast time constant
127 LOG(ANABRID_DEBUG_CALIBRATION, "Finished ramp integration on fast mode!");
128
129 LOG(ANABRID_DEBUG_CALIBRATION, "Starting ramp integration on slow mode!");
130 set_time_factors(100);
131 TRY(write_to_hardware());
132
133 TRY(_gain_calibration(true)); // Slow time constant
134 LOG(ANABRID_DEBUG_CALIBRATION, "Finished ramp integration on slow mode!");
135
136 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
137
138 // Measure end value
139 return UnitResult::ok();
140}
141
143 for (size_t i = 0; i < NUM_INTEGRATORS; i++) {
144 auto gain = time_factors[i] == 10000
145 ? calibration[i].time_factor_gain_fast
146 : calibration[i].time_factor_gain_slow;
147 if (!hardware->write_time_factor_gain(i, gain))
148 return UnitResult::err_fmt("Error mint-block writing time factor gain: %d %f", i, gain);
149 }
150 return UnitResult::ok();
151}
152
153UnitResult blocks::MIntBlock_V_1_1_X::_gain_calibration(bool use_slow_integration) {
154 const float target_precision = 0.01f;
155 const int max_loops = 100;
156
157 // TODO: currently we cant reach the other four channels
158 std::bitset<NUM_INTEGRATORS> done_channels;
159 int loop_count = 0;
160
161 // start varying the x input offsets
162 while (!done_channels.all()) {
163 // Start calculation
165 if (!mode::FlexIOControl::init(mode::DEFAULT_IC_TIME, use_slow_integration ? 200'000'000 : 2'000'000,
166 mode::OnOverload::IGNORE, mode::OnExtHalt::IGNORE))
167 LOG(ANABRID_DEBUG_CALIBRATION, "Timer failed!");
168
171 }
172
173 auto read_outputs = daq::average(daq::sample, 4, 10);
174
175 for (auto idx = 0u; idx < NUM_INTEGRATORS; idx++) {
176 uint8_t *time_factor_gain = use_slow_integration ? &calibration[idx].time_factor_gain_slow
177 : &calibration[idx].time_factor_gain_fast;
178 if (fabs(read_outputs[idx] + 1.0f) < target_precision) {
179 done_channels[idx] = true; // Already precice
180 continue;
181 }
182 done_channels[idx] = false;
183
184 // Little bounded proportional controller, to decrease calibration time
185 int step = abs_clamp((read_outputs[idx] + 1.0f) * -800.0f, 1, 70);
186
187 if (*time_factor_gain + step > 0xff || *time_factor_gain + step < 0) {
188 *time_factor_gain = std::clamp(static_cast<int>(*time_factor_gain) + step, 0, 0xff);
189 done_channels[idx] = true;
190 return UnitResult::err("Time factor / gain out of bounds");
191 } else {
192 *time_factor_gain += step;
193 }
194
195 if (!hardware->write_time_factor_gain(idx, *time_factor_gain)) {
196 done_channels[idx] = true;
197 return UnitResult::err("Writing of time factor gain failed");
198 continue;
199 }
200 }
201 delay(10); // Small transient time
202
203 loop_count++;
204 if (loop_count > max_loops)
205 return UnitResult::err("Calibration timed out!");
206 }
207
208 return UnitResult::ok();
209}
210
213
214ConfigResult blocks::MIntBlock_V_1_1_X::config(const pb_Item &config) {
215 auto result = MIntBlock::config(config);
216 if (result.is_err() || result.ok_value())
217 return result;
218
219 if (config.which_kind == pb_Item_limiter_config_tag) {
220 auto& limiter_config = config.kind.limiter_config;
221 for (size_t idx = 0; idx < limiter_config.elements_count; idx++) {
222 auto& elem = limiter_config.elements[idx];
223 _limiters_enabled[elem.idx] = elem.enable;
224 }
225
226 return ConfigResult::ok(true);
227 }
228
229 return ConfigResult::ok(false);
230}
231
232// Hardware abstraction layer
233
235 : blocks::MIntBlockHAL_Parent(block_address, 14),
236 f_ic_dac(bus::replace_function_idx(block_address, 4), 14, 2.0f),
237 f_time_factor(bus::replace_function_idx(block_address, 5), 14, true),
238 f_time_factor_sync(bus::replace_function_idx(block_address, 6)),
239 f_time_factor_reset(bus::replace_function_idx(block_address, 7)),
240 f_overload_flags(bus::replace_function_idx(block_address, 2), 14),
241 f_overload_flags_reset(bus::replace_function_idx(block_address, 3)) {}
242
244 TRY(MIntBlockHAL::init());
245 if (!f_ic_dac.init())
246 return UnitResult::err("Failed to initialize IC dac");
247 if (!f_ic_dac.set_external_reference(true))
248 return UnitResult::err("Failed to set external reference");
249 if (!f_ic_dac.set_double_gain(true))
250 return UnitResult::err("Failed to set double gain");
251 return UnitResult::ok();
252}
253
254bool blocks::MIntBlockHAL_V_1_0_X::write_ic(uint8_t idx, float ic) {
255 if (idx >= MIntBlock::NUM_INTEGRATORS)
256 return false;
257 // Note: The output is level-shifted, such that IC = 2V - output.
258 return f_ic_dac.set_channel(idx, ic + 1.0f);
259}
260
262 if (!f_time_factor.transfer8(static_cast<uint8_t>(switches.to_ulong())))
263 return false;
264 f_time_factor_sync.trigger();
265 return true;
266}
267
269 return f_overload_flags.read8();
270}
271
273
275 : MIntBlockHAL_V_1_0_X(block_address),
276 f_time_factor_gain_0_3(bus::replace_function_idx(block_address, 8), 14),
277 f_time_factor_gain_4_7(bus::replace_function_idx(block_address, 9), 14) {}
278
279bool
281 std::bitset<8> limiters) {
282 // In version 1.1, a new shift register has been chained behind the time factors shift register,
283 // instead of recieving a separate function address, so we can only set them together.
284 if (!f_time_factor.transfer16(static_cast<uint16_t>(limiters.to_ulong()) << 8 |
285 static_cast<uint8_t>(switches.to_ulong())))
286 return false;
287 f_time_factor_sync.trigger();
288 return true;
289}
290
291bool blocks::MIntBlockHAL_V_1_1_X::write_ic(uint8_t idx, float ic) {
292 if (idx >= MIntBlock::NUM_INTEGRATORS)
293 return false;
294 // Note: The output is level-shifted differently than on Version 1.0, such that IC = output - 2V.
295 return f_ic_dac.set_channel(idx, 1.0f - ic);
296}
297
299 if (idx >= MIntBlock::NUM_INTEGRATORS)
300 return false;
301
302 if (idx < 4)
303 return f_time_factor_gain_0_3.write_channel_raw(idx, gain);
304 else
305 return f_time_factor_gain_4_7.write_channel_raw(idx - 4, gain);
306}
MIntBlockHAL_V_1_0_X(bus::addr_t block_address)
const functions::TriggerFunction f_overload_flags_reset
Definition mblock_int.h:27
std::bitset< 8 > read_overload_flags() override
UnitResult init() override
const functions::SR74HCT595 f_time_factor
Definition mblock_int.h:23
const functions::DAC60508 f_ic_dac
Definition mblock_int.h:22
const functions::SR74HC16X f_overload_flags
Definition mblock_int.h:26
void reset_overload_flags() override
const functions::TriggerFunction f_time_factor_sync
Definition mblock_int.h:24
bool write_time_factor_switches(std::bitset< 8 > switches) override
const functions::TriggerFunction f_time_factor_reset
Definition mblock_int.h:25
bool write_ic(uint8_t idx, float ic) override
virtual bool write_time_factor_gain(uint8_t idx, uint8_t gain)
virtual bool write_time_factor_switches_and_limiters_enable(std::bitset< 8 > switches, std::bitset< 8 > limiters)
functions::AD8403 f_time_factor_gain_4_7
Definition mblock_int.h:44
functions::AD8403 f_time_factor_gain_0_3
Definition mblock_int.h:43
MIntBlockHAL_V_1_1_X(bus::addr_t block_address)
bool write_ic(uint8_t idx, float ic) override
void reset(entities::ResetAction action) override
std::array< IntegratorCalibration, NUM_INTEGRATORS > calibration
Definition mblock_int.h:78
ConfigResult config(const pb_Item &item) override
UnitResult write_calibration_to_hardware()
MIntBlockHAL_V_1_1_X * hardware
Definition mblock_int.h:76
UnitResult write_to_hardware() override
MIntBlock_V_1_1_X(SLOT slot, MIntBlockHAL_V_1_1_X *hardware)
const std::array< IntegratorCalibration, NUM_INTEGRATORS > & get_calibration() const
UnitResult calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) override
std::bitset< NUM_INTEGRATORS > _limiters_enabled
Definition mblock_int.h:79
UnitResult _gain_calibration(bool use_slow_integration)
static bool is_done()
Definition mode.cpp:473
static void reset()
Definition mode.cpp:402
static void force_start()
Definition mode.cpp:370
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, SyncConfig sync_config={})
Definition mode.cpp:95
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:20
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:20
entities::EntitySharedHardware< MIntBlockHAL > MIntBlockHAL_Parent
Definition mblock_int.h:18
Definition bus.h:21
Definition daq.h:14