REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
lucidac.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 anabrid GmbH
2// Contact: https://www.anabrid.com/licensing/
3//
4// SPDX-License-Identifier: MIT OR GPL-2.0-or-later
5
6#include "pb_decode.h"
7
8#include <entity/visitor.h>
9#include <lucidac/lucidac.h>
10#include <utils/mac.h>
11
12platform::LUCIDAC::LUCIDAC(LUCIDAC_HAL *hardware)
13 : Carrier({Cluster(0)}, hardware), hardware(hardware) {}
14
15UnitResult platform::LUCIDAC::init() {
16 LOG(ANABRID_DEBUG_INIT, __PRETTY_FUNCTION__);
17 TRY(Carrier::init());
18
19 LOG(ANABRID_DEBUG_INIT, "Detecting front panel...");
20 if (!front_panel) {
21 front_panel = entities::detect<LUCIDACFrontPanel>(bus::address_from_tuple(bus::FRONTPLANE_BADDR, 0));
22 if (!front_panel) {
23 LOG_ALWAYS("Warning: Front panel is missing or unknown.");
24 } else {
25 LOG(ANABRID_DEBUG_INIT, "Initialising detected front panel...");
26 auto front_panel_result = front_panel->init();
27 if (front_panel_result.is_err()) {
28 LOG_ALWAYS("Error: Could not initialize front panel");
29 return front_panel_result;
30 } else {
31 LOG(ANABRID_DEBUG_INIT, "Front panel initialized.");
32 }
33 }
34 }
35
36 return UnitResult::ok();
37}
38
39void platform::LUCIDAC::reset(entities::ResetAction action) {
40 Carrier::reset(action);
41 if (front_panel)
42 front_panel->reset(action);
43
44 if (action.has(entities::ResetAction::CIRCUIT_RESET)) {
45 reset_acl_select();
46 reset_adc_channels();
47 }
48}
49
50UnitResult platform::LUCIDAC::route_in_external(uint8_t in, uint8_t i_out) {
51 if (in > 7)
52 return UnitResult::err("in must be less or equal to 7");
53
54 TRY(clusters[0].iblock->connect(in + 24, i_out));
55 return UnitResult::ok();
56}
57
58UnitResult platform::LUCIDAC::route_out_external(uint8_t u_in, uint8_t out, float c_factor) {
59 if (out > 7)
60 return UnitResult::err_fmt("out must be less or equal to 7 but is %d", out);
61
62 TRY(clusters[0].ublock->connect(u_in, out + 24));
63 TRY(clusters[0].cblock->set_factor(out + 24, c_factor));
64 return UnitResult::ok();
65}
66
67std::vector<entities::Entity *> platform::LUCIDAC::get_child_entities() {
68 auto entities = this->Carrier::get_child_entities();
69 if (front_panel)
70 entities.push_back(front_panel);
71 return entities;
72}
73
74entities::Entity *platform::LUCIDAC::get_child_entity(std::string_view child_id) {
75 if (child_id == "FP")
76 return front_panel;
77 return this->carrier::Carrier::get_child_entity(child_id);
78}
79
80UnitResult platform::LUCIDAC::write_to_hardware() {
81 TRY(Carrier::write_to_hardware());
82 if (front_panel) {
83 auto result = front_panel->write_to_hardware();
84 if (result.is_err())
85 return UnitResult::err(result.err_value() + "Front Panel write failed.");
86 }
87 if (!hardware->write_acl(acl_select))
88 UnitResult::err("Write ACL failed");
89 return UnitResult::ok();
90}
91
92const std::array<platform::LUCIDAC::ACL, 8> &platform::LUCIDAC::get_acl_select() const {
93 return acl_select;
94}
95
96void platform::LUCIDAC::set_acl_select(const std::array<ACL, 8> &acl_select_) {
97 acl_select = acl_select_;
98}
99
100bool platform::LUCIDAC::set_acl_select(uint8_t idx, LUCIDAC::ACL acl) {
101 if (idx >= acl_select.size())
102 return false;
103 acl_select[idx] = acl;
104 return true;
105}
106
107void platform::LUCIDAC::reset_acl_select() {
108 std::fill(acl_select.begin(), acl_select.end(), ACL::INTERNAL_);
109}
110
111// this variant of the config saves full and partial connections from the U-block
112// as the Carrier class restores only
113UnitResult platform::LUCIDAC::calibrate_routes() {
114 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
115
116 // collect current I-block connections (includes partial connections due to
117 // analog IO)
118 auto& cluster = clusters[0];
119 std::array<std::vector<uint8_t>, blocks::IBlock::NUM_OUTPUTS> old_i_block_connections;
120 for (auto i_out_idx : blocks::IBlock::OUTPUT_IDX_RANGE())
121 for (auto i_in_idx : blocks::IBlock::INPUT_IDX_RANGE())
122 if (cluster.iblock->is_connected(i_in_idx, i_out_idx))
123 old_i_block_connections[i_out_idx].emplace_back(i_in_idx);
124
125 // disconnect ACLs for Carrier-based calibration
126 auto old_acl_selection = get_acl_select();
127 reset_acl_select();
128 if (!hardware->write_acl(acl_select))
129 return UnitResult::err("Writing ACL before calibrate_routes() failed");
130
131 TRY(Carrier::calibrate_routes());
132
133 set_acl_select(old_acl_selection);
134 if (!hardware->write_acl(acl_select))
135 return UnitResult::err("Writing ACL after calibrate_routes() failed");
136
137 // Restore I-block connections
138 for (auto i_out_idx : blocks::IBlock::OUTPUT_IDX_RANGE())
139 for (auto i_in_idx : old_i_block_connections[i_out_idx])
140 TRY (cluster.iblock->connect(i_in_idx, i_out_idx));
141
142 TRY(cluster.iblock->write_to_hardware());
143
144 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
145
146 return UnitResult::ok();
147}
148
149ConfigResult platform::LUCIDAC::config(const pb_Item &item) {
150 if (item.which_kind == pb_Item_port_config_tag) {
151 auto& port_config = item.kind.port_config;
152
153 reset_acl_select();
154 for (size_t idx = 0; idx < port_config.states_count ; ++idx) {
155 acl_select[idx] = (port_config.states[idx] == pb_PortConfig_AclState_EXTERNAL) ?
156 ACL::EXTERNAL_ : ACL::INTERNAL_;
157 }
158
159 if (!hardware->write_acl(acl_select))
160 return ConfigResult::err("cannot write acl select to hardware");
161
162 return ConfigResult::ok(true);
163 }
164
165 return Carrier::config(item);
166}
167
168void platform::LUCIDAC::extract(entities::ExtractVisitor &collector) {
169 Carrier::extract(collector);
170
171 if (!collector.include_configuration()) return;
172 auto& item = collector.create(pb_Item_port_config_tag);
173 auto& lucidac_config = item.kind.port_config;
174
175 lucidac_config.states_count = acl_select.size();
176 for (size_t idx = 0; idx < acl_select.size(); idx++) {
177 lucidac_config.states[idx] =
178 (acl_select[idx] == ACL::EXTERNAL_)
179 ? pb_PortConfig_AclState_EXTERNAL
180 : pb_PortConfig_AclState_INTERNAL;
181 }
182}
183
184UnitResult platform::LUCIDAC::calibrate_offsets() {
185 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
186
187 auto old_acl_selection = get_acl_select();
188 reset_acl_select();
189 if (!hardware->write_acl(acl_select))
190 UnitResult::err("Writing ACL before calibrate_offsets() failed");
191
192 TRY(Carrier::calibrate_offsets());
193
194 set_acl_select(old_acl_selection);
195 if (!hardware->write_acl(acl_select))
196 UnitResult::err("Writing ACL after calibrate_offsets() failed");
197
198 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
199
200 return UnitResult::ok();
201}
202
203UnitResult platform::LUCIDAC::calibrate(const pb_CalibrationConfig &item) {
204 TRY(Carrier::calibrate(item));
205
206 if (item.gain != pb_CalibrationConfig_Kind_Disabled)
207 TRY(calibrate_routes());
208
209 return UnitResult::ok();
210}