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) {
233 return {
run, data_handler};
236FLASHMEM
unsigned int daq::stream::details::get_number_of_data_vectors_in_buffer() {
238 return channel.TCD->BITER - channel.TCD->CITER;
241FLASHMEM std::array<volatile uint32_t, daq::stream::details::BUFFER_SIZE> daq::stream::details::get_buffer() {
246status daq::stream::process(
const run::Run &
run, run::RunDataHandler *
const data_handler,
bool partial) {
248 static auto partial_buffer_part = details::buffer.data();
251 return status(
"Invalid DAQ configuration.");
252 if (details::overflow_data)
253 return status(
"DMA overflow.");
256 volatile uint32_t *active_buffer_part;
257 size_t outer_count = details::BUFFER_SIZE /
run.daq_config.get_num_channels() / 2;
260 if (details::first_data) {
261 active_buffer_part = details::buffer.data();
262 partial_buffer_part = details::buffer.data() + details::BUFFER_SIZE / 2;
263 details::first_data =
false;
264 }
else if (details::last_data) {
265 active_buffer_part = details::buffer.data() + details::BUFFER_SIZE / 2;
266 partial_buffer_part = details::buffer.data();
267 details::last_data =
false;
268 }
else if (partial) {
271 active_buffer_part = partial_buffer_part;
272 if (partial_buffer_part == details::buffer.data())
277 outer_count = details::get_number_of_data_vectors_in_buffer();
284 outer_count = details::get_number_of_data_vectors_in_buffer() - outer_count;
288 details::channel.TCD->CITER = details::channel.TCD->BITER;
289 details::channel.TCD->DADDR = details::buffer.data();
290 details::first_data = details::last_data = details::overflow_data =
false;
292 return status::success();
295 return status::success();
297 data_handler->handle(active_buffer_part, outer_count,
run.daq_config.get_num_channels(),
run);
298 return status::success();
301FLASHMEM
status daq::stream::stop(
const run::Run &
run) {
302 details::channel.disable();
307 auto flexio = FlexIOHandler::flexIOHandler_list[1];
309 flexio->port().SHIFTSDEN = 0;
311 details::run_data_handler =
nullptr;
312 details::run =
nullptr;
313 details::first_data = details::last_data = details::overflow_data =
false;
319 (
run.daq_config.get_num_channels() > 1) ? ((1u <<
run.daq_config.get_num_channels()) - 1) : 1u;
320 if (flexio->port().SHIFTERR & _shifterr_mask)
321 return status(
"SHIFTERR was asserted during stream.");
323 flexio->port().SHIFTERR = 0xFF;
325 return status::success();
328FLASHMEM
status daq::stream::start(
const run::Run &
run, run::RunDataHandler *
const data_handler) {
329 using namespace daq::details::flexio;
339 auto _flexio_pin_gate = get_flexio()->mapIOPinToFlexPin(daq::details::PIN_GATE);
340 get_flexio()->port().TIMCTL[_gated_timer_idx] = FLEXIO_TIMCTL_TRGSEL(2 * _flexio_pin_gate) |
341 FLEXIO_TIMCTL_TRGPOL | FLEXIO_TIMCTL_TRGSRC |
342 FLEXIO_TIMCTL_TIMOD(0b11);
343 get_flexio()->port().TIMCFG[_gated_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b011) | FLEXIO_TIMCFG_TIMENA(0b110);
344 get_flexio()->port().TIMCMP[_gated_timer_idx] = 239;
346 get_flexio()->port().TIMCTL[_sample_timer_idx] =
347 FLEXIO_TIMCTL_TRGSEL(4 * _gated_timer_idx + 3) | FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TIMOD(0b11);
348 get_flexio()->port().TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDEC(0b01) | FLEXIO_TIMCFG_TIMENA(0b110);
349 get_flexio()->port().TIMCMP[_sample_timer_idx] = (1'000'000 /
run.daq_config.get_sample_rate()) * 2 - 1;
354 details::run_data_handler = data_handler;
363 uint8_t shifter_dma_idx = 0;
365 get_flexio()->port().SHIFTSDEN = 1 << shifter_dma_idx;
367 details::channel.begin();
384 details::channel.TCD->BITER = details::buffer.size() /
run.daq_config.get_num_channels();
387 details::channel.TCD->CITER = details::buffer.size() /
run.daq_config.get_num_channels();
396 details::channel.TCD->SADDR = get_flexio()->port().SHIFTBUFBIS;
399 details::channel.TCD->NBYTES = 4 *
run.daq_config.get_num_channels();
402 details::channel.TCD->SOFF = 4;
410 uint8_t MOD_SRC = __builtin_ctz(
run.daq_config.get_num_channels() * 4);
411 details::channel.TCD->ATTR_SRC = ((MOD_SRC & 0b11111) << 3) | 0b010;
414 details::channel.TCD->SLAST = 0;
422 details::channel.TCD->DADDR = details::buffer.data();
425 details::channel.TCD->DOFF = 4;
430 uint8_t MOD = __builtin_ctz(details::BUFFER_SIZE * 4);
432 if (
reinterpret_cast<uintptr_t
>(details::buffer.data()) & ~(~
static_cast<uintptr_t
>(0) << MOD)) {
433 return (
"DMA buffer memory range is not sufficiently aligned.");
435 details::channel.TCD->ATTR_DST = ((MOD & 0b11111) << 3) | 0b010;
438 details::channel.TCD->SLAST = 0;
441 details::channel.attachInterrupt(details::interrupt);
442 details::channel.interruptAtCompletion();
443 details::channel.interruptAtHalf();
445 details::channel.triggerAtHardwareEvent(get_flexio()->shiftersDMAChannel(shifter_dma_idx));
447 details::channel.enable();
448 if (details::channel.error()) {
449 return (
"dma::channel.error");
452 return status::success();
Routines for data acquisition (DAQ) using the internal analog-to-digital converters (ADC).