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);
73status 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 status(
"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 status(
"Could not map pins to FlexIO module.");
93 get_flexio()->setIOPinToFlexMode(pin_miso);
96 return status::success();
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;
141 RETURN_IF_FAILED(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 status::success();
155 digitalWriteFast(details::PIN_EN, LOW);
157 digitalWriteFast(details::PIN_EN, HIGH);
161 for (
auto _ = 0; _ < 10; _++)
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 if (!
carrier.write_to_hardware())
179 return status(
"Error writing disconnect of ADCs to hardware.");
182 LOG(ANABRID_DEBUG_CALIBRATION,
"Reading ADC zero offsets:");
183 auto zeros_raw = daq::average(daq::sample_raw);
184 std::transform(zeros_raw.begin(), zeros_raw.end(), details::raw_zero_offsets.begin(),
185 [](
auto zero_raw) { return details::RAW_ZERO_IDEAL - zero_raw; });
186 for (
auto raw_zero_offset : details::raw_zero_offsets)
187 LOG(ANABRID_DEBUG_CALIBRATION, raw_zero_offset);
190 LOG(ANABRID_DEBUG_CALIBRATION,
"Restoring previous ADC connections...");
191 static_cast<void>(
carrier.set_adc_channels(old_adc_channels));
192 carrier.ctrl_block->set_adc_bus(old_adc_bus);
194 if (!
carrier.write_to_hardware())
195 return status(
"Error writing previous connections of ADCs to hardware.");
198 if (std::any_of(details::raw_zero_offsets.begin(), details::raw_zero_offsets.end(),
199 [](
auto value) {
return abs(value) > 100; }))
200 return status(
"Zero offsets of ADCs are suspiciously large.");
202 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
204 return status::success();
207std::array<uint16_t, daq::NUM_CHANNELS> daq::sample_raw() {
208 using namespace details::flexio;
211 get_flexio()->port().SHIFTSTAT = 0xFF;
214 get_flexio()->port().TIMCTL[_sample_timer_idx] = 0;
215 get_flexio()->port().TIMCFG[_sample_timer_idx] = 0;
216 delayNanoseconds(42);
218 get_flexio()->port().TIMCMP[_sample_timer_idx] = 1;
219 get_flexio()->port().TIMCTL[_sample_timer_idx] = FLEXIO_TIMCTL_TIMOD(0b11);
220 get_flexio()->port().TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b010);
223 for (
auto _ = 0u; _ < 50; _++) {
224 if (get_flexio()->port().SHIFTSTAT & 0xFF)
225 return {
static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[0]),
226 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[1]),
227 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[2]),
228 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[3]),
229 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[4]),
230 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[5]),
231 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[6]),
232 static_cast<uint16_t
>(get_flexio()->port().SHIFTBUFBIS[7])};
233 delayNanoseconds(100);
237 return {details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE,
238 details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE,
239 details::RAW_SUSPICIOUS_VALUE, details::RAW_SUSPICIOUS_VALUE};
242std::array<float, daq::NUM_CHANNELS> daq::sample() {
return raw_to_float(sample_raw()); }
244std::array<float, daq::NUM_CHANNELS> daq::average(
size_t samples,
unsigned int delay_us) {
245 return average(daq::sample, samples, delay_us);
248daq::stream::Scope daq::stream::get(
const run::Run &
run, run::RunDataHandler *
const data_handler,
250 return {
run, data_handler, start};
253unsigned int daq::stream::details::remaining_data_count() {
254 uint16_t citer = channel.TCD->CITER;
255 uint16_t biter = channel.TCD->BITER;
257 uint16_t halt_biter = biter >> 1;
258 uint16_t count = biter - citer;
259 if (count > halt_biter) {
265std::array<volatile uint16_t, daq::stream::details::BUFFER_SIZE> daq::stream::details::get_buffer() {
270FASTRUN run::RunHandleResult daq::stream::process(
const run::Run &
run, run::RunDataHandler *
const data_handler,
bool partial) {
272 return run::RunHandleResult::err(
"Invalid DAQ configuration.");
273 if (details::overflow)
274 return run::RunHandleResult::err(
"DMA overflow.");
276 auto alignment =
run.daq_config.get_num_channels_min_power_of_two();
278 if (details::head != details::tail){
279 size_t sample_count = details::HALF_BUFFER_SIZE / alignment;
280 auto data_ptr =
const_cast<uint16_t*
>(details::buffer.data() + alignment * details::tail);
281 auto result = data_handler->handle(
run, data_ptr, sample_count);
282 details::tail = details::head;
288 size_t sample_count = details::remaining_data_count();
289 auto data_ptr =
const_cast<uint16_t*
>(details::buffer.data() + alignment * details::tail);
290 auto result = data_handler->handle(
run, data_ptr, sample_count);
295 return run::RunHandleResult::ok({});
298FASTRUN
status daq::stream::stop(
const run::Run &
run) {
299 details::channel.disable();
304 auto flexio = FlexIOHandler::flexIOHandler_list[1];
306 flexio->port().SHIFTSDEN = 0;
308 details::run_data_handler =
nullptr;
309 details::run =
nullptr;
310 details::head = details::tail = 0;
311 details::overflow =
false;
316 uint32_t _shifterr_mask = (
run.daq_config.get_num_channels_min_power_of_two() > 1)
317 ? ((1u <<
run.daq_config.get_num_channels_min_power_of_two()) - 1)
319 if (flexio->port().SHIFTERR & _shifterr_mask)
320 return status(
"SHIFTERR was asserted during stream (%d).", flexio->port().SHIFTERR);
322 flexio->port().SHIFTERR = 0xFF;
324 return status::success();
327FASTRUN
status daq::stream::start(
const run::Run &
run, run::RunDataHandler *
const data_handler) {
328 using namespace daq::details::flexio;
338 auto& port = get_flexio()->port();
340 auto _flexio_pin_gate = get_flexio()->mapIOPinToFlexPin(daq::details::PIN_GATE);
341 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 port.TIMCFG[_gated_timer_idx] = FLEXIO_TIMCFG_TIMDIS(0b011) | FLEXIO_TIMCFG_TIMENA(0b110);
345 port.TIMCMP[_gated_timer_idx] = 59;
347 port.TIMCTL[_sample_timer_idx] =
348 FLEXIO_TIMCTL_TRGSEL(4 * _gated_timer_idx + 3) | FLEXIO_TIMCTL_TRGSRC | FLEXIO_TIMCTL_TIMOD(0b11);
349 port.TIMCFG[_sample_timer_idx] = FLEXIO_TIMCFG_TIMDEC(0b01) | FLEXIO_TIMCFG_TIMENA(0b110);
350 port.TIMCMP[_sample_timer_idx] = (1'000'000 /
run.daq_config.get_sample_rate()) * 2 - 1;
355 details::run_data_handler = data_handler;
357 details::head = details::tail = 0;
358 details::overflow =
false;
366 uint8_t shifter_dma_idx = 0;
368 port.SHIFTSDEN = 1 << shifter_dma_idx;
370 details::channel.begin();
388 details::channel.TCD->BITER = details::buffer.size() /
run.daq_config.get_num_channels_min_power_of_two();
391 details::channel.TCD->CITER = details::channel.TCD->BITER;
400 details::channel.TCD->SADDR = port.SHIFTBUFBIS;
403 details::channel.TCD->NBYTES = 2 *
run.daq_config.get_num_channels_min_power_of_two();
406 details::channel.TCD->SOFF = 4;
414 uint8_t MOD_SRC = __builtin_ctz(
run.daq_config.get_num_channels_min_power_of_two() * 4);
415 details::channel.TCD->ATTR_SRC = (MOD_SRC << 3) | 0b001;
418 details::channel.TCD->SLAST = 0;
426 details::channel.TCD->DADDR = details::buffer.data();
429 details::channel.TCD->DOFF = 2;
434 uint8_t MOD = __builtin_ctz(details::BUFFER_SIZE * 2);
435 details::channel.TCD->ATTR_DST = (MOD << 3) | 0b001;
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();
__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.