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 status res;
75
76 // Write IC values one channel at a time
77 for (decltype(ic_values.size()) i = 0; i < ic_values.size(); i++) {
78 if (!hardware->write_ic(i, ic_values[i])) {
79 LOG(ANABRID_PEDANTIC, __PRETTY_FUNCTION__);
80 return "Error writing IC values to hardware.";
81 }
82 }
83
84 // Write time factor switches by converting to bitset
85 std::bitset<NUM_INTEGRATORS> time_factor_switches{};
86 for (auto idx = 0u; idx < time_factors.size(); idx++)
87 if (time_factors[idx] != DEFAULT_TIME_FACTOR)
88 time_factor_switches.set(idx);
89
90 if (!hardware->write_time_factor_switches_and_limiters_enable(time_factor_switches, _limiters_enabled))
91 res.attach("Error writing time factors or limiters");
93 return res;
94}
95
96status blocks::MIntBlock_V_1_1_X::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
97 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
98
100
101 success.attach(MBlock::calibrate(cluster, carrier));
102
103 // TODO: keep old circuits alive
104 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
105 _limiters_enabled.reset();
106
107 LOG(ANABRID_DEBUG_CALIBRATION, "Connecting calibration signals!");
108
109 // Connect +0.1 to all integrator inputs. 0.1 because we have high precision and a long integration time.
110 for (auto idx : SLOT_INPUT_IDX_RANGE())
111 success.attach(cluster->add_constant(UBlock::Transmission_Mode::POS_REF, slot_to_global_io_index(idx),
112 1.0f, slot_to_global_io_index(idx)));
113
114 cluster->ublock->change_reference_magnitude(UBlock::Reference_Magnitude::ONE_TENTH);
115
116 success.attach(cluster->write_to_hardware());
117
118 // When setting routes we need to calibrate them
119 success.attach(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 success.attach(write_to_hardware());
125
126 success.attach(_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 success.attach(write_to_hardware());
132
133 success.attach(_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 success;
140}
141
143 for (size_t i = 0; i < NUM_INTEGRATORS; i++) {
144 if (!hardware->write_time_factor_gain(i, time_factors[i] == 10000 ? calibration[i].time_factor_gain_fast
145 : calibration[i].time_factor_gain_slow))
146 return status::failure();
147 }
148 return status::success();
149}
150
153 const float target_precision = 0.01f;
154 const int max_loops = 100;
155
156 // TODO: currently we cant reach the other four channels
157 std::bitset<NUM_INTEGRATORS> done_channels;
158 int loop_count = 0;
159
160 // start varying the x input offsets
161 while (!done_channels.all()) {
162 // Start calculation
164 if (!mode::FlexIOControl::init(mode::DEFAULT_IC_TIME, use_slow_integration ? 200'000'000 : 2'000'000,
165 mode::OnOverload::IGNORE, mode::OnExtHalt::IGNORE))
166 LOG(ANABRID_DEBUG_CALIBRATION, "Timer failed!");
167
170 }
171
172 auto read_outputs = daq::average(daq::sample, 4, 10);
173
174 for (auto idx = 0u; idx < NUM_INTEGRATORS; idx++) {
175 uint8_t *time_factor_gain = use_slow_integration ? &calibration[idx].time_factor_gain_slow
176 : &calibration[idx].time_factor_gain_fast;
177 if (fabs(read_outputs[idx] + 1.0f) < target_precision) {
178 done_channels[idx] = true; // Already precice
179 continue;
180 }
181 done_channels[idx] = false;
182
183 // Little bounded proportional controller, to decrease calibration time
184 int step = abs_clamp((read_outputs[idx] + 1.0f) * -800.0f, 1, 70);
185
186 if (*time_factor_gain + step > 0xff || *time_factor_gain + step < 0) {
187 *time_factor_gain = std::clamp(static_cast<int>(*time_factor_gain) + step, 0, 0xff);
188 done_channels[idx] = true;
189 success.attach("Time factor / gain out of bounds");
190 } else {
191 *time_factor_gain += step;
192 }
193
194 if (!hardware->write_time_factor_gain(idx, *time_factor_gain)) {
195 done_channels[idx] = true;
196 success.attach("Writing of time factor gain failed");
197 continue;
198 }
199 }
200 delay(10); // Small transient time
201
202 loop_count++;
203 if (loop_count > max_loops)
204 return success.attach("Calibration timed out!");
205 }
206
207 return success;
208}
209
212
213ConfigResult blocks::MIntBlock_V_1_1_X::config(const pb_Config &config) {
214 auto result = MIntBlock::config(config);
215 if (result.is_err() || result.ok_value())
216 return result;
217
218 if (config.which_kind == pb_Config_limiter_config_tag) {
219 auto& limiter_config = config.kind.limiter_config;
220 for (size_t idx = 0; idx < limiter_config.elements_count; idx++) {
221 auto& elem = limiter_config.elements[idx];
222 _limiters_enabled[elem.idx] = elem.enable;
223 }
224
225 return ConfigResult::ok(true);
226 }
227
228 return ConfigResult::ok(false);
229}
230
231// Hardware abstraction layer
232
234 : blocks::MIntBlockHAL_Parent(block_address, 14),
235 f_ic_dac(bus::replace_function_idx(block_address, 4), 14, 2.0f),
236 f_time_factor(bus::replace_function_idx(block_address, 5), 14, true),
237 f_time_factor_sync(bus::replace_function_idx(block_address, 6)),
238 f_time_factor_reset(bus::replace_function_idx(block_address, 7)),
239 f_overload_flags(bus::replace_function_idx(block_address, 2), 14),
240 f_overload_flags_reset(bus::replace_function_idx(block_address, 3)) {}
241
243 if (!MIntBlockHAL::init())
244 return false;
245 return f_ic_dac.init() and f_ic_dac.set_external_reference(true) and f_ic_dac.set_double_gain(true);
246}
247
248bool blocks::MIntBlockHAL_V_1_0_X::write_ic(uint8_t idx, float ic) {
249 if (idx >= MIntBlock::NUM_INTEGRATORS)
250 return false;
251 // Note: The output is level-shifted, such that IC = 2V - output.
252 return f_ic_dac.set_channel(idx, ic + 1.0f);
253}
254
256 if (!f_time_factor.transfer8(static_cast<uint8_t>(switches.to_ulong())))
257 return false;
258 f_time_factor_sync.trigger();
259 return true;
260}
261
263 return f_overload_flags.read8();
264}
265
267
269 : MIntBlockHAL_V_1_0_X(block_address),
270 f_time_factor_gain_0_3(bus::replace_function_idx(block_address, 8), 14),
271 f_time_factor_gain_4_7(bus::replace_function_idx(block_address, 9), 14) {}
272
273bool
275 std::bitset<8> limiters) {
276 // In version 1.1, a new shift register has been chained behind the time factors shift register,
277 // instead of recieving a separate function address, so we can only set them together.
278 if (!f_time_factor.transfer16(static_cast<uint16_t>(limiters.to_ulong()) << 8 |
279 static_cast<uint8_t>(switches.to_ulong())))
280 return false;
281 f_time_factor_sync.trigger();
282 return true;
283}
284
285bool blocks::MIntBlockHAL_V_1_1_X::write_ic(uint8_t idx, float ic) {
286 if (idx >= MIntBlock::NUM_INTEGRATORS)
287 return false;
288 // Note: The output is level-shifted differently than on Version 1.0, such that IC = output - 2V.
289 return f_ic_dac.set_channel(idx, 1.0f - ic);
290}
291
293 if (idx >= MIntBlock::NUM_INTEGRATORS)
294 return false;
295
296 if (idx < 4)
297 return f_time_factor_gain_0_3.write_channel_raw(idx, gain);
298 else
299 return f_time_factor_gain_4_7.write_channel_raw(idx - 4, gain);
300}
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
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
status calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) override
status _gain_calibration(bool use_slow_integration)
utils::status write_to_hardware() override
MIntBlockHAL_V_1_1_X * hardware
Definition mblock_int.h:76
MIntBlock_V_1_1_X(SLOT slot, MIntBlockHAL_V_1_1_X *hardware)
const std::array< IntegratorCalibration, NUM_INTEGRATORS > & get_calibration() const
utils::status write_calibration_to_hardware()
std::bitset< NUM_INTEGRATORS > _limiters_enabled
Definition mblock_int.h:79
ConfigResult config(const pb_Config &cfg) override
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
utils::status status
Definition daq.h:21
static constexpr int success
Definition flasher.cpp:275
entities::EntitySharedHardware< MIntBlockHAL > MIntBlockHAL_Parent
Definition mblock_int.h:18
Definition bus.h:21
Definition daq.h:14