REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
drivers.cpp
Go to the documentation of this file.
1#include "run/manager.h"
2#include "mode/counters.h"
3
4#include <Arduino.h>
5#include <cmath>
6#include <cstdlib>
7
8#include <daq/daq.h>
9#include <utils/logging.h>
10
11#include <mode/mode.h>
12#include <mode/teensy/mode.h>
13#include <run/handlers.h>
14#include <run/run.h>
15
16#include "run/teensy/drivers.h"
17
18
19FLASHMEM void run::drivers::SleepRun::run() {
20 auto run = *this; // leftover
21 // run_data_handler->prepare(run);
22
23 // here, one sample is one data aquisition which always yields all channels.
24 constexpr uint32_t max_num_samples = 16'384;
25 const int num_channels = run.daq_config.get_num_channels();
26 const uint32_t optime_us = run.config.op_time / 1000;
27
28 uint32_t sampling_time_us = 0;
29 if (num_channels > 0 && run.daq_config.get_sample_rate() != 0) {
30 // measure how long an single ADC sampling takes with the current implementation.
31 // This is right now around 15us.
32 elapsedMicros sampling_time_us_counter; // class from teensy elpasedMillis.h
33 volatile auto hopefully_not_optimized_out = daq::sample_raw();
34 sampling_time_us = sampling_time_us_counter;
35 // TODO: There is a slight dither around 15us -- 16us, could do the averaging call to
36 // improve the statistics.
37 }
38
39 /*
40 The guiding principle of the following math is this:
41
42 sampling_total_us == sampling_time_us + sampling_sleep_us // for up to full 8 channels
43 optime_us == num_samples*num_channels*sampling_total_us
44
45 We precompute all busy waits (sleeps) in order to match the requested optime as precise
46 as possible, with this means. Without interrupts.
47
48 We ignore run.daq_config.get_sample_rate() because it is much simpler to just always
49 fill up the buffer. Furthermore the manual DAQ is pretty slow (about 15us per sample).
50
51 There are two main scenarios:
52
53 - asked for fast sampling = short runtime. This is where sampling_time_us dominates.
54 This code does it's job when optime_us > sampling_time_us but it's not great, of
55 course.
56 - asked for slow sampling = long runtime. This is where this code shines, because there
57 is no limit in runtime or similar, compared to the FlexIO code.
58
59 Of course, the overall code is still an order of magnitude simpler then the FlexIO/DMA
60 code. And in the same way less precise for short runtimes and fast sampling times.
61
62 Given the unit of time basically measured in sampling_time_us, there is no need to
63 compute in nanoseconds here. Therefore the code computes in microseconds.
64 */
65
66 uint32_t sampling_total_us = optime_us / max_num_samples;
67
68 // either no sleep at all or sleep the difference between total time and aquisition time.
69 uint32_t sampling_sleep_us =
70 (sampling_total_us < sampling_time_us) ? 0 : (sampling_total_us - sampling_time_us);
71
72 uint32_t num_samples = optime_us / (sampling_sleep_us + sampling_time_us);
73 uint32_t optime_left_us = optime_us % (sampling_sleep_us + sampling_time_us);
74
75 // This situation actually should not happen at all.
76 if (num_samples > max_num_samples)
77 num_samples = max_num_samples;
78
79 const int num_buffer_entries = num_samples * num_channels;
80 if (num_channels > 0)
81 LOGMEV("optime_us=%d, sampling_time_us=%d, sampling_sleep_us=%d, num_samples=%d, num_channels=%d",
82 optime_us, sampling_time_us, sampling_sleep_us, num_samples, num_channels);
83
84 // This would not be necessary anymore
85 // just to be able to use daq.sample_raw(uint16_t*) instead of copying over to an intermediate buffer
86 // of size std::vector, we add a bit safety padding to the right for the last datum obtained.
87 // Actually needed only if num_samples < daq::NUM_CHANNELS = 8.
88 constexpr int padding = daq::NUM_CHANNELS;
89
90 // we use malloc because new[] raises an exception if allocation fails but we cannot catch it with
91 // -fno-exception. we don't use the stack because we expect the heap to have more memory left...
92 int16_t *buffer = nullptr;
93 if (num_channels > 0) {
94 // allocate only if any data aquisition was asked for
95 buffer = (int16_t *)malloc((num_buffer_entries + padding) * sizeof(int16_t));
96 if (!buffer) {
97 LOG_ERROR("Could not allocated a large run buffer for a traditional Run.");
98 }
99 }
100
101 mode::RealManualControl::enable();
102
103 LOGMEV("IC TIME: %lld", run.config.ic_time);
104 LOGMEV("OP TIME: %lld", run.config.op_time);
105
106 mode::RealManualControl::to_ic();
107 // 32bit nanosecond delay can sleep maximum 4sec, however
108 // an overlap will happen instead.
109 /*if (run.config.ic_time > 100'000'000) // 100ms
110 delay(run.config.ic_time / 1'000'000); // milisecond resolution sleep
111 else if (run.config.ic_time > 65'000) // 16bit nanoseconds
112 delayMicroseconds(run.config.ic_time / 1000);
113 else*/
114 delayNanoseconds(run.config.ic_time);
115 mode::RealManualControl::to_op();
116 elapsedMicros actual_op_time_timer;
117
118 if (buffer) {
119 for (uint32_t sample = 0; sample < num_samples; sample++) {
120 // Assumption: Passing the next line requires the amount of time "sampling_time_us"
121 // It's no longer possible to directly sample into a buffer, since we always need to copy out of the
122 // flexIO shift buffers
123 auto data = daq::sample_raw();
124 memcpy(buffer + sample * num_channels, data.data(), num_channels);
125 // if (sampling_sleep_us)
126 delayMicroseconds(sampling_sleep_us);
127 }
128 // if (optime_left_us)
129 delayMicroseconds(optime_left_us);
130 } else {
131 /*if (run.config.op_time > 100'000'000) // 100ms
132 delay(run.config.op_time / 1'000'000); // millisecond resolution sleep
133 else if (run.config.op_time > 65'000) // 16bit nanoseconds
134 delayMicroseconds(run.config.op_time / 1000);
135 else*/
136 delayNanoseconds(run.config.op_time);
137 }
138
139 uint32_t actual_op_time_us = actual_op_time_timer;
140 mode::RealManualControl::to_halt();
141
142 if (buffer) {
143 if(alt_data_handler)
144 alt_data_handler->handle((uint16_t *)buffer, num_samples, num_channels, run);
145 free(buffer);
146 }
147
148 // untested option
149 if(run.daq_config.should_sample_op_end() && alt_data_handler) {
150 auto singlebuf = daq::sample_raw();
151 // assert: singlebuf.size() <= num_channels ...
152 alt_data_handler->handle(singlebuf.data(), 1, num_channels, run);
153 }
154
155 auto res = (num_channels && !buffer) ? RunState::ERROR : RunState::DONE;
156 auto result = run.to(res, actual_op_time_us * 1000);
157 if (run.config.write_run_state_changes && state_change_handler)
158 state_change_handler->handle(result, run);
159}
160
161FLASHMEM void run::drivers::FlexIORun::run() {
162 auto run = *this; // leftover
163 if(data_handler)
164 data_handler->prepare(run);
165 bool daq_error = false;
166
168 if (!mode::FlexIOControl::init(run.config.ic_time, run.config.op_time,
169 run.config.halt_on_overload ? mode::OnOverload::HALT
170 : mode::OnOverload::IGNORE,
171 mode::OnExtHalt::IGNORE)) {
172 LOG_ERROR("Error while initializing state machine.")
173 auto change = run.to(RunState::ERROR, 0);
174 if(state_change_handler)
175 state_change_handler->handle(change, run);
176 return;
177 }
178
179 auto data_stream = daq::stream::get(run, data_handler);
180 if(data_handler)
181 data_handler->init();
182
183 mode::FlexIOControl::force_start();
184 delayMicroseconds(1);
185
186 while (!mode::FlexIOControl::is_done()) {
187 if (!data_stream.process()) {
188 LOG_ERROR("Streaming error, most likely data overflow.");
189 daq_error = true;
190 break;
191 }
192 }
194
195 // When a data sample must be gathered very close to the end of OP duration,
196 // it takes a few microseconds for it to end up in the DMA buffer.
197 // This is hard to check for, since the DMA active flag is only set once the DMA
198 // is triggered by the last CLK pulse.
199 // Easiest solution is to wait for it.
200 delayMicroseconds(20);
201 // Stream out remaining partially filled buffer
202 if (!data_stream.process(true)) {
203 LOG_ERROR("Streaming error during final partial stream.");
204 daq_error = true;
205 }
206
207 auto actual_op_time = mode::FlexIOControl::get_actual_op_time();
208
209 auto &perf = mode::PerformanceCounter::get();
210 perf.add(mode::Mode::IC, run.config.ic_time / 1000);
211 perf.add(mode::Mode::OP, actual_op_time / 1000);
212 perf.increase_run();
213
214 if (daq_error) {
215 auto change = run.to(RunState::ERROR, actual_op_time);
216 if(state_change_handler)
217 state_change_handler->handle(change, run);
218 return;
219 }
220
221 // DONE
222 auto change = run.to(RunState::DONE, actual_op_time);
223 if(state_change_handler)
224 state_change_handler->handle(change, run);
225}
226
227FLASHMEM void run::drivers::SeriesRun::run() {
228 const int num_channels = daq_config.get_num_channels();
229 int16_t* raw_results = nullptr;
230 if(alt_data_handler) {
231 raw_results = (int16_t*) malloc(num_channels * series_length * sizeof(int16_t));
232 if(!raw_results) {
233 LOG_ERROR("Could not allocate a large run buffer for Series Run.");
234 }
235 }
236
237 elapsedMicros timer_us;
238
239 mode::RealManualControl::enable();
240 int16_t* raw_results_cur = raw_results;
241 for(size_t cur_series = 0; cur_series < series_length; cur_series++) {
242 mode::RealManualControl::to_ic();
243 delayNanoseconds(config.ic_time);
244 mode::RealManualControl::to_op();
245 delayNanoseconds(config.op_time);
246 mode::RealManualControl::to_halt();
247
248 if(raw_results) {
249 auto samples = daq::sample_raw();
250 for(size_t channel=0; channel<num_channels; channel++)
251 *(raw_results_cur++) = samples[channel];
252 }
253 }
254
255 auto result = to(RunState::DONE, timer_us*1000);
256 if(state_change_handler)
257 state_change_handler->handle(result, *this);
258
259 if(raw_results && data_handler)
260 alt_data_handler->handle((uint16_t *)raw_results, series_length, num_channels, *this);
261
262 if(raw_results)
263 free(raw_results);
264}
static void to_end()
Definition mode.cpp:416
static unsigned long long get_actual_op_time()
Definition mode.cpp:478
static void reset()
Definition mode.cpp:422
static bool init(unsigned long long ic_time_ns, unsigned long long op_time_ns, mode::OnOverload on_overload=mode::OnOverload::HALT, mode::OnExtHalt on_ext_halt=mode::OnExtHalt::IGNORE, mode::Sync sync=mode::Sync::NONE)
Definition mode.cpp:112
uint32_t
Definition flasher.cpp:195
if(((src) >=(0x60000000) &&(src)<(0x60000000)+(0x800000)))
Definition flasher.cpp:177
while(offset< size &&error==0)
Definition flasher.cpp:135
Routines for data acquisition (DAQ) using the internal analog-to-digital converters (ADC).
Definition daq.cpp:16
Definition mode.h:14
Definition drivers.h:3