1#![cfg_attr(target_os = "none", no_std)]
28#![cfg_attr(target_os = "none", no_main)]
29
30use core::{
31 iter,
32 mem::MaybeUninit,
33 sync::atomic::{Ordering, fence},
34};
35
36use fugit::ExtU32;
37use idsp::{Accu, Complex, ComplexExt, Filter, Lowpass, RPLL, Repeat};
38use miniconf::{Leaf, Tree};
39use rtic_monotonics::Monotonic;
40use serde::{Deserialize, Serialize};
41
42use stabilizer::convert::{AdcCode, DacCode, Gain};
43
44use platform::{AppSettings, NetSettings};
45
46const BATCH_SIZE_LOG2: u32 = 3;
49const BATCH_SIZE: usize = 1 << BATCH_SIZE_LOG2;
50
51const SAMPLE_TICKS_LOG2: u32 = 7;
55const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2;
56
57#[derive(Clone, Debug, Tree, Default)]
58#[tree(meta(doc, typename))]
59pub struct Settings {
60 lockin: Lockin,
61 net: NetSettings,
62}
63
64impl AppSettings for Settings {
65 fn new(net: NetSettings) -> Self {
66 Self {
67 net,
68 lockin: Lockin::default(),
69 }
70 }
71
72 fn net(&self) -> &NetSettings {
73 &self.net
74 }
75}
76
77impl serial_settings::Settings for Settings {
78 fn reset(&mut self) {
79 *self = Self {
80 lockin: Lockin::default(),
81 net: NetSettings::new(self.net.mac),
82 }
83 }
84}
85
86#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
87enum Conf {
88 Magnitude,
90 Phase,
92 ReferenceFrequency,
94 LogPower,
96 InPhase,
98 Quadrature,
100 Modulation,
102}
103
104#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
105enum LockinMode {
106 Internal,
108 External,
110}
111
112#[derive(Clone, Debug, Tree)]
113#[tree(meta(doc, typename))]
114pub struct Lockin {
115 afe: [Leaf<Gain>; 2],
117
118 #[tree(with=miniconf::leaf)]
120 lockin_mode: LockinMode,
121
122 pll_tc: [u32; 2],
126
127 #[tree(with=miniconf::leaf)]
129 lockin_k: <Lowpass<2> as Filter>::Config,
130
131 lockin_harmonic: i32,
135
136 lockin_phase: i32,
141
142 output_conf: [Leaf<Conf>; 2],
144
145 telemetry_period: u16,
147
148 #[tree(with=miniconf::leaf)]
150 stream: stream::Target,
151}
152
153impl Default for Lockin {
154 fn default() -> Self {
155 Self {
156 afe: [Leaf(Gain::G1); 2],
157
158 lockin_mode: LockinMode::External,
159
160 pll_tc: [21, 21], lockin_k: [0x8_0000, -0x400_0000], lockin_harmonic: -1, lockin_phase: 0, output_conf: [Leaf(Conf::InPhase), Leaf(Conf::Quadrature)],
167 telemetry_period: 10,
169
170 stream: Default::default(),
171 }
172 }
173}
174
175#[cfg(not(target_os = "none"))]
176fn main() {
177 use miniconf::{json::to_json_value, json_schema::TreeJsonSchema};
178 let s = Settings::default();
179 println!(
180 "{}",
181 serde_json::to_string_pretty(&to_json_value(&s).unwrap()).unwrap()
182 );
183 let mut schema = TreeJsonSchema::new(Some(&s)).unwrap();
184 schema
185 .root
186 .insert("title".to_string(), "Stabilizer lockin".into());
187 println!("{}", serde_json::to_string_pretty(&schema.root).unwrap());
188}
189
190#[cfg(target_os = "none")]
191#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, SDMMC])]
192mod app {
193 use super::*;
194 use stabilizer::{
195 hardware::{
196 self, DigitalInput0, DigitalInput1, Pgia, SerialTerminal,
197 SystemTimer, Systick, UsbDevice,
198 adc::{Adc0Input, Adc1Input},
199 dac::{Dac0Output, Dac1Output},
200 hal,
201 input_stamper::InputStamper,
202 net::{NetworkState, NetworkUsers},
203 timers::SamplingTimer,
204 },
205 telemetry::TelemetryBuffer,
206 };
207 use stream::FrameGenerator;
208
209 #[shared]
210 struct Shared {
211 usb: UsbDevice,
212 network: NetworkUsers<Lockin>,
213 settings: Settings,
214 active_settings: Lockin,
215 telemetry: TelemetryBuffer,
216 }
217
218 #[local]
219 struct Local {
220 usb_terminal: SerialTerminal<Settings>,
221 sampling_timer: SamplingTimer,
222 digital_inputs: (DigitalInput0, DigitalInput1),
223 timestamper: InputStamper,
224 afes: [Pgia; 2],
225 adcs: (Adc0Input, Adc1Input),
226 dacs: (Dac0Output, Dac1Output),
227 pll: RPLL,
228 lockin: idsp::Lockin<Repeat<2, Lowpass<2>>>,
229 source: idsp::AccuOsc<iter::Repeat<i64>>,
230 generator: FrameGenerator,
231 cpu_temp_sensor: stabilizer::hardware::cpu_temp_sensor::CpuTempSensor,
232 }
233
234 #[init]
235 fn init(c: init::Context) -> (Shared, Local) {
236 let clock = SystemTimer::new(|| Systick::now().ticks());
237
238 let (mut stabilizer, _mezzanine, _eem) =
240 hardware::setup::setup::<Settings>(
241 c.core,
242 c.device,
243 clock,
244 BATCH_SIZE,
245 SAMPLE_TICKS,
246 );
247
248 let mut network = NetworkUsers::new(
249 stabilizer.network_devices.stack,
250 stabilizer.network_devices.phy,
251 clock,
252 env!("CARGO_BIN_NAME"),
253 &stabilizer.settings.net,
254 stabilizer.metadata,
255 );
256
257 let generator = network.configure_streaming(stream::Format::AdcDacData);
258
259 let shared = Shared {
260 network,
261 usb: stabilizer.usb,
262 telemetry: TelemetryBuffer::default(),
263 active_settings: stabilizer.settings.lockin.clone(),
264 settings: stabilizer.settings,
265 };
266
267 let mut local = Local {
268 usb_terminal: stabilizer.usb_serial,
269 sampling_timer: stabilizer.sampling_timer,
270 digital_inputs: stabilizer.digital_inputs,
271 afes: stabilizer.afes,
272 adcs: stabilizer.adcs,
273 dacs: stabilizer.dacs,
274 timestamper: stabilizer.input_stamper,
275 cpu_temp_sensor: stabilizer.temperature_sensor,
276
277 pll: RPLL::new(SAMPLE_TICKS_LOG2 + BATCH_SIZE_LOG2),
278 lockin: idsp::Lockin::default(),
279 source: idsp::AccuOsc::new(iter::repeat(
280 1i64 << (64 - BATCH_SIZE_LOG2),
281 )),
282
283 generator,
284 };
285
286 local.adcs.0.start();
288 local.adcs.1.start();
289 local.dacs.0.start();
290 local.dacs.1.start();
291
292 settings_update::spawn().unwrap();
294 telemetry::spawn().unwrap();
295 ethernet_link::spawn().unwrap();
296 start::spawn().unwrap();
297 usb::spawn().unwrap();
298
299 stabilizer.timestamp_timer.start();
301
302 local.timestamper.start();
304
305 (shared, local)
306 }
307
308 #[task(priority = 1, local=[sampling_timer])]
309 async fn start(c: start::Context) {
310 Systick::delay(100.millis()).await;
311 c.local.sampling_timer.start();
313 }
314
315 #[task(binds=DMA1_STR4, shared=[active_settings, telemetry], local=[adcs, dacs, lockin, timestamper, pll, generator, source], priority=3)]
323 #[unsafe(link_section = ".itcm.process")]
324 fn process(c: process::Context) {
325 let process::SharedResources {
326 active_settings,
327 telemetry,
328 ..
329 } = c.shared;
330
331 let process::LocalResources {
332 timestamper,
333 adcs: (adc0, adc1),
334 dacs: (dac0, dac1),
335 pll,
336 lockin,
337 source,
338 generator,
339 ..
340 } = c.local;
341
342 (active_settings, telemetry).lock(|settings, telemetry| {
343 let (reference_phase, reference_frequency) =
344 match settings.lockin_mode {
345 LockinMode::External => {
346 let timestamp =
347 timestamper.latest_timestamp().unwrap_or(None); let (pll_phase, pll_frequency) = pll.update(
349 timestamp.map(|t| t as i32),
350 settings.pll_tc[0],
351 settings.pll_tc[1],
352 );
353 (pll_phase, (pll_frequency >> BATCH_SIZE_LOG2) as i32)
354 }
355 LockinMode::Internal => {
356 (1i32 << 30, 1i32 << (32 - BATCH_SIZE_LOG2))
358 }
359 };
360
361 let sample_frequency =
362 reference_frequency.wrapping_mul(settings.lockin_harmonic);
363 let sample_phase = settings.lockin_phase.wrapping_add(
364 reference_phase.wrapping_mul(settings.lockin_harmonic),
365 );
366
367 (adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| {
368 let adc_samples = [adc0, adc1];
369 let mut dac_samples = [dac0, dac1];
370
371 fence(Ordering::SeqCst);
373
374 let output: Complex<i32> = adc_samples[0]
375 .iter()
376 .zip(Accu::new(sample_phase, sample_frequency))
378 .map(|(&sample, phase)| {
380 let s = (sample as i16 as i32) << 16;
381 lockin.update(s, phase, &settings.lockin_k)
382 })
383 .last()
385 .unwrap()
386 * 2; for (channel, samples) in dac_samples.iter_mut().enumerate() {
390 for sample in samples.iter_mut() {
391 let value = match *settings.output_conf[channel] {
392 Conf::Magnitude => output.abs_sqr() as i32 >> 16,
393 Conf::Phase => output.arg() >> 16,
394 Conf::LogPower => output.log2() << 8,
395 Conf::ReferenceFrequency => {
396 reference_frequency >> 16
397 }
398 Conf::InPhase => output.re >> 16,
399 Conf::Quadrature => output.im >> 16,
400
401 Conf::Modulation => source.next().unwrap().re,
402 };
403
404 *sample = DacCode::from(value as i16).0;
405 }
406 }
407
408 const N: usize = BATCH_SIZE * size_of::<i16>()
410 / size_of::<MaybeUninit<u8>>();
411 generator.add(|buf| {
412 for (data, buf) in adc_samples
413 .iter()
414 .chain(dac_samples.iter())
415 .zip(buf.chunks_exact_mut(N))
416 {
417 let data = unsafe {
418 core::slice::from_raw_parts(
419 data.as_ptr() as *const MaybeUninit<u8>,
420 N,
421 )
422 };
423 buf.copy_from_slice(data)
424 }
425 N * 4
426 });
427
428 telemetry.adcs =
430 [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])];
431
432 telemetry.dacs =
433 [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])];
434
435 fence(Ordering::SeqCst);
437 });
438 });
439 }
440
441 #[idle(shared=[settings, network, usb])]
442 fn idle(mut c: idle::Context) -> ! {
443 loop {
444 match (&mut c.shared.network, &mut c.shared.settings)
445 .lock(|net, settings| net.update(&mut settings.lockin))
446 {
447 NetworkState::SettingsChanged => {
448 settings_update::spawn().unwrap()
449 }
450 NetworkState::Updated => {}
451 NetworkState::NoChange => {
452 if c.shared.usb.lock(|usb| {
454 usb.state()
455 == usb_device::device::UsbDeviceState::Suspend
456 }) {
457 cortex_m::asm::wfi();
458 }
459 }
460 }
461 }
462 }
463
464 #[task(priority = 1, local=[afes], shared=[network, settings, active_settings])]
465 async fn settings_update(mut c: settings_update::Context) {
466 c.shared.settings.lock(|settings| {
467 c.local.afes[0].set_gain(*settings.lockin.afe[0]);
468 c.local.afes[1].set_gain(*settings.lockin.afe[1]);
469
470 c.shared
471 .network
472 .lock(|net| net.direct_stream(settings.lockin.stream));
473
474 c.shared
475 .active_settings
476 .lock(|current| *current = settings.lockin.clone());
477 });
478 }
479
480 #[task(priority = 1, local=[digital_inputs, cpu_temp_sensor], shared=[network, settings, telemetry])]
481 async fn telemetry(mut c: telemetry::Context) {
482 loop {
483 let mut telemetry =
484 c.shared.telemetry.lock(|telemetry| telemetry.clone());
485
486 telemetry.digital_inputs = [
487 c.local.digital_inputs.0.is_high(),
488 c.local.digital_inputs.1.is_high(),
489 ];
490
491 let (gains, telemetry_period) =
492 c.shared.settings.lock(|settings| {
493 (settings.lockin.afe, settings.lockin.telemetry_period)
494 });
495
496 c.shared.network.lock(|net| {
497 net.telemetry.publish_telemetry(
498 "/telemetry",
499 &telemetry.finalize(
500 *gains[0],
501 *gains[1],
502 c.local.cpu_temp_sensor.get_temperature().unwrap(),
503 ),
504 )
505 });
506
507 Systick::delay((telemetry_period as u32).secs()).await;
509 }
510 }
511
512 #[task(priority = 1, shared=[usb, settings], local=[usb_terminal])]
513 async fn usb(mut c: usb::Context) {
514 loop {
515 c.shared.usb.lock(|usb| {
517 usb.poll(&mut [c
518 .local
519 .usb_terminal
520 .interface_mut()
521 .inner_mut()]);
522 });
523
524 c.shared.settings.lock(|settings| {
525 if c.local.usb_terminal.poll(settings).unwrap() {
526 settings_update::spawn().unwrap()
527 }
528 });
529
530 Systick::delay(10.millis()).await;
531 }
532 }
533
534 #[task(priority = 1, shared=[network])]
535 async fn ethernet_link(mut c: ethernet_link::Context) {
536 loop {
537 c.shared.network.lock(|net| net.processor.handle_link());
538 Systick::delay(1.secs()).await;
539 }
540 }
541
542 #[task(binds = ETH, priority = 1)]
543 fn eth(_: eth::Context) {
544 unsafe { hal::ethernet::interrupt_handler() }
545 }
546}