10#include <carrier/carrier.h>
12#include <utils/logging.h>
13#include <utils/running_avg.h>
28__attribute__((aligned(BUFFER_SIZE * 4))) std::array<volatile uint32_t, BUFFER_SIZE> buffer = {0};
30DMAChannel channel(
false);
31const run::Run *
run =
nullptr;
32run::RunDataHandler *run_data_handler =
nullptr;
33volatile bool first_data =
false;
34volatile bool last_data =
false;
35volatile bool overflow_data =
false;
40 auto is_half = (channel.TCD->CITER != channel.TCD->BITER);
42 overflow_data |= first_data;
45 overflow_data |= last_data;
50 channel.clearInterrupt();
63FLASHMEM
void daq::enable() { details::flexio::get_flexio()->port().CTRL |= FLEXIO_CTRL_FLEXEN; }
65FLASHMEM
void daq::disable() { details::flexio::get_flexio()->port().CTRL &= ~FLEXIO_CTRL_FLEXEN; }
67FLASHMEM
void daq::reset() {
69 details::flexio::get_flexio()->port().CTRL |= FLEXIO_CTRL_SWRST;
70 delayNanoseconds(100);
71 details::flexio::get_flexio()->port().CTRL &= ~FLEXIO_CTRL_SWRST;
72 delayNanoseconds(100);
75FLASHMEM
status daq::details::flexio::_init_once() {
77 get_flexio()->setClockSettings(3, 0, 0);
78 get_flexio()->hardware().clock_gate_register |= get_flexio()->hardware().clock_gate_mask;
81 uint8_t _flexio_pin_cnvst = get_flexio()->mapIOPinToFlexPin(PIN_CNVST);
82 uint8_t _flexio_pin_clk = get_flexio()->mapIOPinToFlexPin(PIN_CLK);
83 uint8_t _flexio_pin_gate = get_flexio()->mapIOPinToFlexPin(PIN_GATE);
85 if (_flexio_pin_cnvst == 0xff or _flexio_pin_clk == 0xff or _flexio_pin_gate == 0xff)
86 return status(
"Could not map pins to FlexIO module.");
87 get_flexio()->setIOPinToFlexMode(PIN_CNVST);
88 get_flexio()->setIOPinToFlexMode(PIN_CLK);
89 get_flexio()->setIOPinToFlexMode(PIN_GATE);
92 for (
auto pin_miso : PINS_MISO) {
93 if (get_flexio()->mapIOPinToFlexPin(pin_miso) == 0xff)
94 return status(
"Could not map pins to FlexIO module.");
95 get_flexio()->setIOPinToFlexMode(pin_miso);
98 return status::success();
101FLASHMEM
void daq::details::flexio::_init_timers() {
102 get_flexio()->port().TIMCTL[_gated_timer_idx] = 0;
103 get_flexio()->port().TIMCFG[_gated_timer_idx] = 0;
104 get_flexio()->port().TIMCMP[_gated_timer_idx] = 0;
106 get_flexio()->port().TIMCTL[_sample_timer_idx] = 0;
107 get_flexio()->port().TIMCFG[_sample_timer_idx] = 0;
108 get_flexio()->port().TIMCMP[_sample_timer_idx] = 0;
110 get_flexio()->port().TIMCTL[_cnvst_timer_idx] =
111 FLEXIO_TIMCTL_PINSEL(get_flexio()->mapIOPinToFlexPin(PIN_CNVST)) | FLEXIO_TIMCTL_PINCFG(0b11) |
112 FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TRGSEL(4 * _sample_timer_idx + 3) | FLEXIO_TIMCTL_TIMOD(0b10);
113 get_flexio()->port().TIMCFG[_cnvst_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010) | FLEXIO_TIMCFG_TIMENA(0b111);
114 get_flexio()->port().TIMCMP[_cnvst_timer_idx] = 0x0000'10'FF;
118 get_flexio()->port().TIMCTL[_delay_timer_idx] =
119 FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TRGSEL(4 * _sample_timer_idx + 3) | FLEXIO_TIMCTL_TIMOD(0b11);
120 get_flexio()->port().TIMCFG[_delay_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010) | FLEXIO_TIMCFG_TIMENA(0b111);
121 get_flexio()->port().TIMCMP[_delay_timer_idx] = 300;
123 get_flexio()->port().TIMCTL[_clk_timer_idx] =
124 FLEXIO_TIMCTL_PINSEL(get_flexio()->mapIOPinToFlexPin(PIN_CLK)) | FLEXIO_TIMCTL_PINCFG(0b11) |
125 FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TRGSEL(4 * _delay_timer_idx + 3) | FLEXIO_TIMCTL_TRGPOL |
126 FLEXIO_TIMCTL_TIMOD(0b01);
127 get_flexio()->port().TIMCFG[_clk_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010) | FLEXIO_TIMCFG_TIMENA(0b110);
131 get_flexio()->port().TIMCMP[_clk_timer_idx] = 0x0000'1B'07;
136FLASHMEM
void daq::details::flexio::_init_shifters() {
137 for (
auto _pin_miso_idx = 0u; _pin_miso_idx < PINS_MISO.size(); _pin_miso_idx++) {
139 get_flexio()->port().SHIFTCTL[_pin_miso_idx] =
140 FLEXIO_SHIFTCTL_TIMSEL(_clk_timer_idx) | FLEXIO_SHIFTCTL_TIMPOL |
141 FLEXIO_SHIFTCTL_PINSEL(get_flexio()->mapIOPinToFlexPin(PINS_MISO[_pin_miso_idx])) |
142 FLEXIO_SHIFTCTL_SMOD(1);
143 get_flexio()->port().SHIFTCFG[_pin_miso_idx] = 0;
147FLASHMEM
status daq::init() {
148 RETURN_IF_FAILED(details::flexio::_init_once());
149 details::flexio::_init_timers();
150 details::flexio::_init_shifters();
152 return status::success();
156 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
157 LOG(ANABRID_DEBUG_CALIBRATION,
"Resetting current ADC connections...");
159 auto old_adc_channels =
carrier.get_adc_channels();
162 auto old_adc_bus =
carrier.ctrl_block->get_adc_bus();
163 carrier.ctrl_block->reset_adc_bus();
165 if (!
carrier.write_to_hardware())
166 return status(
"Error writing disconnect of ADCs to hardware.");
169 LOG(ANABRID_DEBUG_CALIBRATION,
"Reading ADC zero offsets:");
170 auto zeros_raw = daq::average(daq::sample_raw);
171 std::transform(zeros_raw.begin(), zeros_raw.end(), details::raw_zero_offsets.begin(),
172 [](
auto zero_raw) { return details::RAW_ZERO_IDEAL - zero_raw; });
173 for (
auto raw_zero_offset : details::raw_zero_offsets)
174 LOG(ANABRID_DEBUG_CALIBRATION, raw_zero_offset);
177 LOG(ANABRID_DEBUG_CALIBRATION,
"Restoring previous ADC connections...");
178 static_cast<void>(
carrier.set_adc_channels(old_adc_channels));
179 carrier.ctrl_block->set_adc_bus(old_adc_bus);
181 if (!
carrier.write_to_hardware())
182 return status(
"Error writing previous connections of ADCs to hardware.");
185 if (std::any_of(details::raw_zero_offsets.begin(), details::raw_zero_offsets.end(),
186 [](
auto value) {
return abs(value) > 100; }))
187 return status(
"Zero offsets of ADCs are suspiciously large.");
188 return status::success();
191FLASHMEM std::array<uint16_t, daq::NUM_CHANNELS> daq::sample_raw() {
192 using namespace details::flexio;
195 get_flexio()->port().SHIFTSTAT = 0xFF;
198 get_flexio()->port().TIMCTL[_sample_timer_idx] = 0;
199 get_flexio()->port().TIMCFG[_sample_timer_idx] = 0;
200 delayNanoseconds(42);
202 get_flexio()->port().TIMCMP[_sample_timer_idx] = 1;
203 get_flexio()->port().TIMCTL[_sample_timer_idx] = FLEXIO_TIMCTL_TIMOD(0b11);
204 get_flexio()->port().TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010);
207 for (
auto _ = 0u; _ < 50; _++) {
208 if (get_flexio()->port().SHIFTSTAT & 0xFF)
209 return {
static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[0]),
210 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[1]),
211 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[2]),
212 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[3]),
213 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[4]),
214 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[5]),
215 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[6]),
216 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[7])};
217 delayNanoseconds(100);
221 return {details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE,
222 details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE,
223 details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE};
226FLASHMEM std::array<float, daq::NUM_CHANNELS> daq::sample() {
return raw_to_float(sample_raw()); }
228FLASHMEM std::array<float, daq::NUM_CHANNELS> daq::average(
size_t samples,
unsigned int delay_us) {
229 return average(daq::sample, samples, delay_us);
232FLASHMEM daq::stream::Scope daq::stream::get(
const run::Run &
run, run::RunDataHandler *
const data_handler,
234 return {
run, data_handler, start};
237FLASHMEM
unsigned int daq::stream::details::get_number_of_data_vectors_in_buffer() {
239 return channel.TCD->BITER - channel.TCD->CITER;
242FLASHMEM std::array<volatile uint32_t, daq::stream::details::BUFFER_SIZE> daq::stream::details::get_buffer() {
247status daq::stream::process(
const run::Run &
run, run::RunDataHandler *
const data_handler,
bool partial) {
249 static auto partial_buffer_part = details::buffer.data();
252 return status(
"Invalid DAQ configuration.");
253 if (details::overflow_data)
254 return status(
"DMA overflow.");
257 volatile uint32_t *active_buffer_part;
258 size_t outer_count = details::BUFFER_SIZE /
run.daq_config.get_num_channels_min_power_of_two() / 2;
261 if (details::first_data) {
262 active_buffer_part = details::buffer.data();
263 partial_buffer_part = details::buffer.data() + details::BUFFER_SIZE / 2;
264 details::first_data =
false;
265 }
else if (details::last_data) {
266 active_buffer_part = details::buffer.data() + details::BUFFER_SIZE / 2;
267 partial_buffer_part = details::buffer.data();
268 details::last_data =
false;
269 }
else if (partial) {
272 active_buffer_part = partial_buffer_part;
273 if (partial_buffer_part == details::buffer.data())
278 outer_count = details::get_number_of_data_vectors_in_buffer();
285 outer_count = details::get_number_of_data_vectors_in_buffer() - outer_count;
289 details::channel.TCD->CITER = details::channel.TCD->BITER;
290 details::channel.TCD->DADDR = details::buffer.data();
291 details::first_data = details::last_data = details::overflow_data =
false;
293 return status::success();
296 return status::success();
298 data_handler->handle(active_buffer_part, outer_count,
run.daq_config.get_num_channels_min_power_of_two(),
run);
299 return status::success();
302FLASHMEM
status daq::stream::stop(
const run::Run &
run) {
303 details::channel.disable();
308 auto flexio = FlexIOHandler::flexIOHandler_list[1];
310 flexio->port().SHIFTSDEN = 0;
312 details::run_data_handler =
nullptr;
313 details::run =
nullptr;
314 details::first_data = details::last_data = details::overflow_data =
false;
320 (
run.daq_config.get_num_channels_min_power_of_two() > 1) ? ((1u <<
run.daq_config.get_num_channels_min_power_of_two()) - 1) : 1u;
321 if (flexio->port().SHIFTERR & _shifterr_mask)
322 return status(
"SHIFTERR was asserted during stream.");
324 flexio->port().SHIFTERR = 0xFF;
326 return status::success();
329FLASHMEM
status daq::stream::start(
const run::Run &
run, run::RunDataHandler *
const data_handler) {
330 using namespace daq::details::flexio;
340 auto _flexio_pin_gate = get_flexio()->mapIOPinToFlexPin(daq::details::PIN_GATE);
341 get_flexio()->port().TIMCTL[_gated_timer_idx] = FLEXIO_TIMCTL_TRGSEL(2 * _flexio_pin_gate) |
342 FLEXIO_TIMCTL_TRGPOL | FLEXIO_TIMCTL_TRGSRC |
343 FLEXIO_TIMCTL_TIMOD(0b11);
344 get_flexio()->port().TIMCFG[_gated_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b011) | FLEXIO_TIMCFG_TIMENA(0b110);
345 get_flexio()->port().TIMCMP[_gated_timer_idx] = 239;
347 get_flexio()->port().TIMCTL[_sample_timer_idx] =
348 FLEXIO_TIMCTL_TRGSEL(4 * _gated_timer_idx + 3) | FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TIMOD(0b11);
349 get_flexio()->port().TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDEC(0b01) | FLEXIO_TIMCFG_TIMENA(0b110);
350 get_flexio()->port().TIMCMP[_sample_timer_idx] = (1'000'000 /
run.daq_config.get_sample_rate()) * 2 - 1;
355 details::run_data_handler = data_handler;
364 uint8_t shifter_dma_idx = 0;
366 get_flexio()->port().SHIFTSDEN = 1 << shifter_dma_idx;
368 details::channel.begin();
385 details::channel.TCD->BITER = details::buffer.size() /
run.daq_config.get_num_channels_min_power_of_two();
388 details::channel.TCD->CITER = details::buffer.size() /
run.daq_config.get_num_channels_min_power_of_two();
397 details::channel.TCD->SADDR = get_flexio()->port().SHIFTBUFBIS;
400 details::channel.TCD->NBYTES = 4 *
run.daq_config.get_num_channels_min_power_of_two();
403 details::channel.TCD->SOFF = 4;
411 uint8_t MOD_SRC = __builtin_ctz(
run.daq_config.get_num_channels_min_power_of_two() * 4);
412 details::channel.TCD->ATTR_SRC = ((MOD_SRC & 0b11111) << 3) | 0b010;
415 details::channel.TCD->SLAST = 0;
423 details::channel.TCD->DADDR = details::buffer.data();
426 details::channel.TCD->DOFF = 4;
431 uint8_t MOD = __builtin_ctz(details::BUFFER_SIZE * 4);
433 if (
reinterpret_cast<uintptr_t
>(details::buffer.data()) & ~(~
static_cast<uintptr_t
>(0) << MOD)) {
434 return (
"DMA buffer memory range is not sufficiently aligned.");
436 details::channel.TCD->ATTR_DST = ((MOD & 0b11111) << 3) | 0b010;
439 details::channel.TCD->SLAST = 0;
442 details::channel.attachInterrupt(details::interrupt);
443 details::channel.interruptAtCompletion();
444 details::channel.interruptAtHalf();
446 details::channel.triggerAtHardwareEvent(get_flexio()->shiftersDMAChannel(shifter_dma_idx));
448 details::channel.enable();
449 if (details::channel.error()) {
450 return (
"dma::channel.error");
453 return status::success();
Routines for data acquisition (DAQ) using the internal analog-to-digital converters (ADC).