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