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 uint16_t, BUFFER_SIZE> buffer = {0};
30DMAChannel channel(
false);
31const run::Run *
run =
nullptr;
32run::RunDataHandler *run_data_handler =
nullptr;
35volatile bool overflow =
false;
39volatile uint16_t head = 0;
40volatile uint16_t tail = 0;
43FASTRUN
void interrupt() {
44 head = channel.TCD->BITER - channel.TCD->CITER;
45 overflow |= head == tail;
48 channel.clearInterrupt();
61void daq::enable() { details::flexio::get_flexio()->port().CTRL |= FLEXIO_CTRL_FLEXEN; }
63void daq::disable() { details::flexio::get_flexio()->port().CTRL &= ~FLEXIO_CTRL_FLEXEN; }
67 details::flexio::get_flexio()->port().CTRL |= FLEXIO_CTRL_SWRST;
68 delayNanoseconds(100);
69 details::flexio::get_flexio()->port().CTRL &= ~FLEXIO_CTRL_SWRST;
70 delayNanoseconds(100);
73UnitResult daq::details::flexio::_init_once() {
75 get_flexio()->setClockSettings(3, 1, 1);
76 get_flexio()->hardware().clock_gate_register |= get_flexio()->hardware().clock_gate_mask;
79 uint8_t _flexio_pin_cnvst = get_flexio()->mapIOPinToFlexPin(PIN_CNVST);
80 uint8_t _flexio_pin_clk = get_flexio()->mapIOPinToFlexPin(PIN_CLK);
81 uint8_t _flexio_pin_gate = get_flexio()->mapIOPinToFlexPin(PIN_GATE);
83 if (_flexio_pin_cnvst == 0xff or _flexio_pin_clk == 0xff or _flexio_pin_gate == 0xff)
84 return UnitResult::err(
"Could not map pins to FlexIO module.");
85 get_flexio()->setIOPinToFlexMode(PIN_CNVST);
86 get_flexio()->setIOPinToFlexMode(PIN_CLK);
87 get_flexio()->setIOPinToFlexMode(PIN_GATE);
90 for (
auto pin_miso : PINS_MISO) {
91 if (get_flexio()->mapIOPinToFlexPin(pin_miso) == 0xff)
92 return UnitResult::err(
"Could not map pins to FlexIO module.");
93 get_flexio()->setIOPinToFlexMode(pin_miso);
96 return UnitResult::ok();
99void daq::details::flexio::_init_timers() {
100 get_flexio()->port().TIMCTL[_gated_timer_idx] = 0;
101 get_flexio()->port().TIMCFG[_gated_timer_idx] = 0;
102 get_flexio()->port().TIMCMP[_gated_timer_idx] = 0;
104 get_flexio()->port().TIMCTL[_sample_timer_idx] = 0;
105 get_flexio()->port().TIMCFG[_sample_timer_idx] = 0;
106 get_flexio()->port().TIMCMP[_sample_timer_idx] = 0;
108 get_flexio()->port().TIMCTL[_cnvst_timer_idx] =
109 FLEXIO_TIMCTL_PINSEL(get_flexio()->mapIOPinToFlexPin(PIN_CNVST)) | FLEXIO_TIMCTL_PINCFG(0b11) |
110 FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TRGSEL(4 * _sample_timer_idx + 3) | FLEXIO_TIMCTL_TIMOD(0b10);
111 get_flexio()->port().TIMCFG[_cnvst_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010) | FLEXIO_TIMCFG_TIMENA(0b111);
112 get_flexio()->port().TIMCMP[_cnvst_timer_idx] = 0x0000'10'60;
116 get_flexio()->port().TIMCTL[_delay_timer_idx] =
117 FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TRGSEL(4 * _sample_timer_idx + 3) | FLEXIO_TIMCTL_TIMOD(0b11);
118 get_flexio()->port().TIMCFG[_delay_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010) | FLEXIO_TIMCFG_TIMENA(0b111);
119 get_flexio()->port().TIMCMP[_delay_timer_idx] = 105;
121 get_flexio()->port().TIMCTL[_clk_timer_idx] =
122 FLEXIO_TIMCTL_PINSEL(get_flexio()->mapIOPinToFlexPin(PIN_CLK)) | FLEXIO_TIMCTL_PINCFG(0b11) |
123 FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TRGSEL(4 * _delay_timer_idx + 3) | FLEXIO_TIMCTL_TRGPOL |
124 FLEXIO_TIMCTL_TIMOD(0b01);
125 get_flexio()->port().TIMCFG[_clk_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010) | FLEXIO_TIMCFG_TIMENA(0b110);
126 get_flexio()->port().TIMCMP[_clk_timer_idx] = 0x0000'1B'03;
129void daq::details::flexio::_init_shifters() {
130 for (
auto _pin_miso_idx = 0u; _pin_miso_idx < PINS_MISO.size(); _pin_miso_idx++) {
132 get_flexio()->port().SHIFTCTL[_pin_miso_idx] =
133 FLEXIO_SHIFTCTL_TIMSEL(_clk_timer_idx) | FLEXIO_SHIFTCTL_TIMPOL |
134 FLEXIO_SHIFTCTL_PINSEL(get_flexio()->mapIOPinToFlexPin(PINS_MISO[_pin_miso_idx])) |
135 FLEXIO_SHIFTCTL_SMOD(1);
136 get_flexio()->port().SHIFTCFG[_pin_miso_idx] = 0;
140UnitResult daq::init() {
141 TRY(details::flexio::_init_once());
142 details::flexio::_init_timers();
143 details::flexio::_init_shifters();
146 pinMode(details::PIN_EN, OUTPUT);
147 digitalWriteFast(details::PIN_EN, HIGH);
151 return UnitResult::ok();
155 digitalWriteFast(details::PIN_EN, LOW);
157 digitalWriteFast(details::PIN_EN, HIGH);
161 for (
auto _ = 0; _ < 10; _++)
165UnitResult daq::calibrate(carrier::Carrier &
carrier) {
166 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
170 LOG(ANABRID_DEBUG_CALIBRATION,
"Resetting current ADC connections...");
172 auto old_adc_channels =
carrier.get_adc_channels();
175 auto old_adc_bus =
carrier.ctrl_block->get_adc_bus();
176 carrier.ctrl_block->reset_adc_bus();
178 TRY(
carrier.write_to_hardware());
181 LOG(ANABRID_DEBUG_CALIBRATION,
"Reading ADC zero offsets:");
182 auto zeros_raw = daq::average(daq::sample_raw);
183 std::transform(zeros_raw.begin(), zeros_raw.end(), details::raw_zero_offsets.begin(),
184 [](
auto zero_raw) { return details::RAW_ZERO_IDEAL - zero_raw; });
185 for (
auto raw_zero_offset : details::raw_zero_offsets)
186 LOG(ANABRID_DEBUG_CALIBRATION, raw_zero_offset);
189 LOG(ANABRID_DEBUG_CALIBRATION,
"Restoring previous ADC connections...");
190 TRY(
carrier.set_adc_channels(old_adc_channels));
191 carrier.ctrl_block->set_adc_bus(old_adc_bus);
193 TRY(
carrier.write_to_hardware());
196 if (std::any_of(details::raw_zero_offsets.begin(), details::raw_zero_offsets.end(),
197 [](
auto value) {
return abs(value) > 100; }))
198 return UnitResult::err(
"Zero offsets of ADCs are suspiciously large.");
200 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
202 return UnitResult::ok();
205std::array<uint16_t, daq::NUM_CHANNELS> daq::sample_raw() {
206 using namespace details::flexio;
209 get_flexio()->port().SHIFTSTAT = 0xFF;
212 get_flexio()->port().TIMCTL[_sample_timer_idx] = 0;
213 get_flexio()->port().TIMCFG[_sample_timer_idx] = 0;
214 delayNanoseconds(42);
216 get_flexio()->port().TIMCMP[_sample_timer_idx] = 1;
217 get_flexio()->port().TIMCTL[_sample_timer_idx] = FLEXIO_TIMCTL_TIMOD(0b11);
218 get_flexio()->port().TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010);
221 for (
auto _ = 0u; _ < 50; _++) {
222 if (get_flexio()->port().SHIFTSTAT & 0xFF)
223 return {
static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[0]),
224 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[1]),
225 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[2]),
226 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[3]),
227 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[4]),
228 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[5]),
229 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[6]),
230 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[7])};
231 delayNanoseconds(100);
235 return {details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE,
236 details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE,
237 details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE};
240std::array<float, daq::NUM_CHANNELS> daq::sample() {
return raw_to_float(sample_raw()); }
242std::array<float, daq::NUM_CHANNELS> daq::average(
size_t samples,
unsigned int delay_us) {
243 return average(daq::sample, samples, delay_us);
246daq::stream::Scope daq::stream::get(
const run::Run &
run, run::RunDataHandler *
const data_handler,
248 return {
run, data_handler, start};
251unsigned int daq::stream::details::remaining_data_count() {
252 uint16_t citer = channel.TCD->CITER;
253 uint16_t biter = channel.TCD->BITER;
255 uint16_t halt_biter = biter >> 1;
256 uint16_t count = biter - citer;
257 if (count > halt_biter) {
263std::array<volatile uint16_t, daq::stream::details::BUFFER_SIZE> daq::stream::details::get_buffer() {
268FASTRUN run::RunHandleResult daq::stream::process(
const run::Run &
run, run::RunDataHandler *
const data_handler,
bool partial) {
270 return run::RunHandleResult::err(
"Invalid DAQ configuration.");
271 if (details::overflow)
272 return run::RunHandleResult::err(
"DMA overflow.");
274 auto alignment =
run.daq_config.get_num_channels_min_power_of_two();
276 if (details::head != details::tail){
277 size_t sample_count = details::HALF_BUFFER_SIZE / alignment;
278 auto data_ptr =
const_cast<uint16_t*
>(details::buffer.data() + alignment * details::tail);
279 auto result = data_handler->handle(
run, data_ptr, sample_count);
280 details::tail = details::head;
286 size_t sample_count = details::remaining_data_count();
287 auto data_ptr =
const_cast<uint16_t*
>(details::buffer.data() + alignment * details::tail);
288 auto result = data_handler->handle(
run, data_ptr, sample_count);
293 return run::RunHandleResult::ok({});
296FASTRUN UnitResult daq::stream::stop(
const run::Run &
run) {
297 details::channel.disable();
302 auto flexio = FlexIOHandler::flexIOHandler_list[1];
304 flexio->port().SHIFTSDEN = 0;
306 details::run_data_handler =
nullptr;
307 details::run =
nullptr;
308 details::head = details::tail = 0;
309 details::overflow =
false;
314 uint32_t _shifterr_mask = (
run.daq_config.get_num_channels_min_power_of_two() > 1)
315 ? ((1u <<
run.daq_config.get_num_channels_min_power_of_two()) - 1)
317 if (flexio->port().SHIFTERR & _shifterr_mask)
318 return UnitResult::err_fmt(
"SHIFTERR was asserted during stream (%d).", flexio->port().SHIFTERR);
320 flexio->port().SHIFTERR = 0xFF;
322 return UnitResult::ok();
325FASTRUN UnitResult daq::stream::start(
const run::Run &
run, run::RunDataHandler *
const data_handler) {
326 using namespace daq::details::flexio;
336 auto& port = get_flexio()->port();
338 auto _flexio_pin_gate = get_flexio()->mapIOPinToFlexPin(daq::details::PIN_GATE);
339 port.TIMCTL[_gated_timer_idx] = FLEXIO_TIMCTL_TRGSEL(2 * _flexio_pin_gate) |
340 FLEXIO_TIMCTL_TRGPOL | FLEXIO_TIMCTL_TRGSRC |
341 FLEXIO_TIMCTL_TIMOD(0b11);
342 port.TIMCFG[_gated_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b011) | FLEXIO_TIMCFG_TIMENA(0b110);
343 port.TIMCMP[_gated_timer_idx] = 59;
345 port.TIMCTL[_sample_timer_idx] =
346 FLEXIO_TIMCTL_TRGSEL(4 * _gated_timer_idx + 3) | FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TIMOD(0b11);
347 port.TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDEC(0b01) | FLEXIO_TIMCFG_TIMENA(0b110);
348 port.TIMCMP[_sample_timer_idx] = (1'000'000 /
run.daq_config.get_sample_rate()) * 2 - 1;
353 details::run_data_handler = data_handler;
355 details::head = details::tail = 0;
356 details::overflow =
false;
364 uint8_t shifter_dma_idx = 0;
366 port.SHIFTSDEN = 1 << shifter_dma_idx;
368 details::channel.begin();
386 details::channel.TCD->BITER = details::buffer.size() /
run.daq_config.get_num_channels_min_power_of_two();
389 details::channel.TCD->CITER = details::channel.TCD->BITER;
398 details::channel.TCD->SADDR = port.SHIFTBUFBIS;
401 details::channel.TCD->NBYTES = 2 *
run.daq_config.get_num_channels_min_power_of_two();
404 details::channel.TCD->SOFF = 4;
412 uint8_t MOD_SRC = __builtin_ctz(
run.daq_config.get_num_channels_min_power_of_two() * 4);
413 details::channel.TCD->ATTR_SRC = (MOD_SRC << 3) | 0b001;
416 details::channel.TCD->SLAST = 0;
424 details::channel.TCD->DADDR = details::buffer.data();
427 details::channel.TCD->DOFF = 2;
432 uint8_t MOD = __builtin_ctz(details::BUFFER_SIZE * 2);
433 details::channel.TCD->ATTR_DST = (MOD << 3) | 0b001;
436 details::channel.TCD->SLAST = 0;
439 details::channel.attachInterrupt(details::interrupt);
440 details::channel.interruptAtCompletion();
441 details::channel.interruptAtHalf();
443 details::channel.triggerAtHardwareEvent(get_flexio()->shiftersDMAChannel(shifter_dma_idx));
445 details::channel.enable();
446 if (details::channel.error()) {
447 return UnitResult::err(
"dma::channel.error");
450 return UnitResult::ok();
__attribute__((section(".fastrun"), noinline, noclone, optimize("Os"))) int flash_sector_not_erased(uint32_t address)
Routines for data acquisition (DAQ) using the internal analog-to-digital converters (ADC).
void reboot()
Just a little generic Teensy reboot routine.