1#![no_std]
28#![no_main]
29
30use core::{
31 iter,
32 mem::MaybeUninit,
33 sync::atomic::{fence, Ordering},
34};
35
36use miniconf::{Leaf, Tree};
37use rtic_monotonics::Monotonic;
38
39use fugit::ExtU32;
40
41use idsp::{Accu, Complex, ComplexExt, Filter, Lowpass, Repeat, RPLL};
42
43use stabilizer::{
44 hardware::{
45 self,
46 adc::{Adc0Input, Adc1Input, AdcCode},
47 afe::Gain,
48 dac::{Dac0Output, Dac1Output, DacCode},
49 hal,
50 input_stamper::InputStamper,
51 timers::SamplingTimer,
52 DigitalInput0, DigitalInput1, Pgia, SerialTerminal, SystemTimer,
53 Systick, UsbDevice,
54 },
55 net::{
56 data_stream::{FrameGenerator, StreamFormat, StreamTarget},
57 serde::{Deserialize, Serialize},
58 telemetry::TelemetryBuffer,
59 NetworkState, NetworkUsers,
60 },
61 settings::NetSettings,
62};
63
64const BATCH_SIZE_LOG2: u32 = 3;
67const BATCH_SIZE: usize = 1 << BATCH_SIZE_LOG2;
68
69const SAMPLE_TICKS_LOG2: u32 = 7;
73const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2;
74
75#[derive(Clone, Debug, Tree)]
76pub struct Settings {
77 lockin: Lockin,
78 net: NetSettings,
79}
80
81impl stabilizer::settings::AppSettings for Settings {
82 fn new(net: NetSettings) -> Self {
83 Self {
84 net,
85 lockin: Lockin::default(),
86 }
87 }
88
89 fn net(&self) -> &NetSettings {
90 &self.net
91 }
92}
93
94impl serial_settings::Settings for Settings {
95 fn reset(&mut self) {
96 *self = Self {
97 lockin: Lockin::default(),
98 net: NetSettings::new(self.net.mac),
99 }
100 }
101}
102
103#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
104enum Conf {
105 Magnitude,
107 Phase,
109 ReferenceFrequency,
111 LogPower,
113 InPhase,
115 Quadrature,
117 Modulation,
119}
120
121#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
122enum LockinMode {
123 Internal,
125 External,
127}
128
129#[derive(Clone, Debug, Tree)]
130pub struct Lockin {
131 afe: [Leaf<Gain>; 2],
141
142 lockin_mode: Leaf<LockinMode>,
150
151 pll_tc: [Leaf<u32>; 2],
161
162 lockin_k: Leaf<<Lowpass<2> as Filter>::Config>,
170
171 lockin_harmonic: Leaf<i32>,
179
180 lockin_phase: Leaf<i32>,
189
190 output_conf: [Leaf<Conf>; 2],
200
201 telemetry_period: Leaf<u16>,
209
210 stream: Leaf<StreamTarget>,
218}
219
220impl Default for Lockin {
221 fn default() -> Self {
222 Self {
223 afe: [Gain::G1.into(); 2],
224
225 lockin_mode: LockinMode::External.into(),
226
227 pll_tc: [21.into(), 21.into()], lockin_k: [0x8_0000, -0x400_0000].into(), lockin_harmonic: (-1).into(), lockin_phase: 0.into(), output_conf: [Conf::InPhase.into(), Conf::Quadrature.into()],
234 telemetry_period: 10.into(),
236
237 stream: Default::default(),
238 }
239 }
240}
241
242#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, SDMMC])]
243mod app {
244 use super::*;
245
246 #[shared]
247 struct Shared {
248 usb: UsbDevice,
249 network: NetworkUsers<Lockin, 2>,
250 settings: Settings,
251 active_settings: Lockin,
252 telemetry: TelemetryBuffer,
253 }
254
255 #[local]
256 struct Local {
257 usb_terminal: SerialTerminal<Settings, 3>,
258 sampling_timer: SamplingTimer,
259 digital_inputs: (DigitalInput0, DigitalInput1),
260 timestamper: InputStamper,
261 afes: [Pgia; 2],
262 adcs: (Adc0Input, Adc1Input),
263 dacs: (Dac0Output, Dac1Output),
264 pll: RPLL,
265 lockin: idsp::Lockin<Repeat<2, Lowpass<2>>>,
266 source: idsp::AccuOsc<iter::Repeat<i64>>,
267 generator: FrameGenerator,
268 cpu_temp_sensor: stabilizer::hardware::cpu_temp_sensor::CpuTempSensor,
269 }
270
271 #[init]
272 fn init(c: init::Context) -> (Shared, Local) {
273 let clock = SystemTimer::new(|| Systick::now().ticks());
274
275 let (mut stabilizer, _pounder) = hardware::setup::setup::<Settings, 3>(
277 c.core,
278 c.device,
279 clock,
280 BATCH_SIZE,
281 SAMPLE_TICKS,
282 );
283
284 let mut network = NetworkUsers::new(
285 stabilizer.net.stack,
286 stabilizer.net.phy,
287 clock,
288 env!("CARGO_BIN_NAME"),
289 &stabilizer.settings.net,
290 stabilizer.metadata,
291 );
292
293 let generator = network.configure_streaming(StreamFormat::AdcDacData);
294
295 let shared = Shared {
296 network,
297 usb: stabilizer.usb,
298 telemetry: TelemetryBuffer::default(),
299 active_settings: stabilizer.settings.lockin.clone(),
300 settings: stabilizer.settings,
301 };
302
303 let source =
304 idsp::AccuOsc::new(iter::repeat(1i64 << (64 - BATCH_SIZE_LOG2)));
305
306 let mut local = Local {
307 usb_terminal: stabilizer.usb_serial,
308 sampling_timer: stabilizer.adc_dac_timer,
309 digital_inputs: stabilizer.digital_inputs,
310 afes: stabilizer.afes,
311 adcs: stabilizer.adcs,
312 dacs: stabilizer.dacs,
313 timestamper: stabilizer.timestamper,
314
315 pll: RPLL::new(SAMPLE_TICKS_LOG2 + BATCH_SIZE_LOG2),
316 lockin: idsp::Lockin::default(),
317 source,
318
319 generator,
320 cpu_temp_sensor: stabilizer.temperature_sensor,
321 };
322
323 local.adcs.0.start();
325 local.adcs.1.start();
326 local.dacs.0.start();
327 local.dacs.1.start();
328
329 settings_update::spawn().unwrap();
331 telemetry::spawn().unwrap();
332 ethernet_link::spawn().unwrap();
333 start::spawn().unwrap();
334 usb::spawn().unwrap();
335
336 stabilizer.timestamp_timer.start();
338
339 local.timestamper.start();
341
342 (shared, local)
343 }
344
345 #[task(priority = 1, local=[sampling_timer])]
346 async fn start(c: start::Context) {
347 Systick::delay(100.millis()).await;
348 c.local.sampling_timer.start();
350 }
351
352 #[task(binds=DMA1_STR4, shared=[active_settings, telemetry], local=[adcs, dacs, lockin, timestamper, pll, generator, source], priority=3)]
360 #[link_section = ".itcm.process"]
361 fn process(c: process::Context) {
362 let process::SharedResources {
363 active_settings,
364 telemetry,
365 ..
366 } = c.shared;
367
368 let process::LocalResources {
369 timestamper,
370 adcs: (adc0, adc1),
371 dacs: (dac0, dac1),
372 pll,
373 lockin,
374 source,
375 generator,
376 ..
377 } = c.local;
378
379 (active_settings, telemetry).lock(|settings, telemetry| {
380 let (reference_phase, reference_frequency) =
381 match *settings.lockin_mode {
382 LockinMode::External => {
383 let timestamp =
384 timestamper.latest_timestamp().unwrap_or(None); let (pll_phase, pll_frequency) = pll.update(
386 timestamp.map(|t| t as i32),
387 *settings.pll_tc[0],
388 *settings.pll_tc[1],
389 );
390 (pll_phase, (pll_frequency >> BATCH_SIZE_LOG2) as i32)
391 }
392 LockinMode::Internal => {
393 (1i32 << 30, 1i32 << (32 - BATCH_SIZE_LOG2))
395 }
396 };
397
398 let sample_frequency =
399 reference_frequency.wrapping_mul(*settings.lockin_harmonic);
400 let sample_phase = settings.lockin_phase.wrapping_add(
401 reference_phase.wrapping_mul(*settings.lockin_harmonic),
402 );
403
404 (adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| {
405 let adc_samples = [adc0, adc1];
406 let mut dac_samples = [dac0, dac1];
407
408 fence(Ordering::SeqCst);
410
411 let output: Complex<i32> = adc_samples[0]
412 .iter()
413 .zip(Accu::new(sample_phase, sample_frequency))
415 .map(|(&sample, phase)| {
417 let s = (sample as i16 as i32) << 16;
418 lockin.update(s, phase, &settings.lockin_k)
419 })
420 .last()
422 .unwrap()
423 * 2; for (channel, samples) in dac_samples.iter_mut().enumerate() {
427 for sample in samples.iter_mut() {
428 let value = match *settings.output_conf[channel] {
429 Conf::Magnitude => output.abs_sqr() as i32 >> 16,
430 Conf::Phase => output.arg() >> 16,
431 Conf::LogPower => output.log2() << 8,
432 Conf::ReferenceFrequency => {
433 reference_frequency >> 16
434 }
435 Conf::InPhase => output.re >> 16,
436 Conf::Quadrature => output.im >> 16,
437
438 Conf::Modulation => source.next().unwrap().re,
439 };
440
441 *sample = DacCode::from(value as i16).0;
442 }
443 }
444
445 const N: usize = BATCH_SIZE * size_of::<i16>()
447 / size_of::<MaybeUninit<u8>>();
448 generator.add(|buf| {
449 for (data, buf) in adc_samples
450 .iter()
451 .chain(dac_samples.iter())
452 .zip(buf.chunks_exact_mut(N))
453 {
454 let data = unsafe {
455 core::slice::from_raw_parts(
456 data.as_ptr() as *const MaybeUninit<u8>,
457 N,
458 )
459 };
460 buf.copy_from_slice(data)
461 }
462 N * 4
463 });
464
465 telemetry.adcs =
467 [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])];
468
469 telemetry.dacs =
470 [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])];
471
472 fence(Ordering::SeqCst);
474 });
475 });
476 }
477
478 #[idle(shared=[settings, network, usb])]
479 fn idle(mut c: idle::Context) -> ! {
480 loop {
481 match (&mut c.shared.network, &mut c.shared.settings)
482 .lock(|net, settings| net.update(&mut settings.lockin))
483 {
484 NetworkState::SettingsChanged => {
485 settings_update::spawn().unwrap()
486 }
487 NetworkState::Updated => {}
488 NetworkState::NoChange => {
489 if c.shared.usb.lock(|usb| {
491 usb.state()
492 == usb_device::device::UsbDeviceState::Suspend
493 }) {
494 cortex_m::asm::wfi();
495 }
496 }
497 }
498 }
499 }
500
501 #[task(priority = 1, local=[afes], shared=[network, settings, active_settings])]
502 async fn settings_update(mut c: settings_update::Context) {
503 c.shared.settings.lock(|settings| {
504 c.local.afes[0].set_gain(*settings.lockin.afe[0]);
505 c.local.afes[1].set_gain(*settings.lockin.afe[1]);
506
507 c.shared
508 .network
509 .lock(|net| net.direct_stream(*settings.lockin.stream));
510
511 c.shared
512 .active_settings
513 .lock(|current| *current = settings.lockin.clone());
514 });
515 }
516
517 #[task(priority = 1, local=[digital_inputs, cpu_temp_sensor], shared=[network, settings, telemetry])]
518 async fn telemetry(mut c: telemetry::Context) {
519 loop {
520 let mut telemetry =
521 c.shared.telemetry.lock(|telemetry| telemetry.clone());
522
523 telemetry.digital_inputs = [
524 c.local.digital_inputs.0.is_high(),
525 c.local.digital_inputs.1.is_high(),
526 ];
527
528 let (gains, telemetry_period) =
529 c.shared.settings.lock(|settings| {
530 (settings.lockin.afe, *settings.lockin.telemetry_period)
531 });
532
533 c.shared.network.lock(|net| {
534 net.telemetry.publish(&telemetry.finalize(
535 *gains[0],
536 *gains[1],
537 c.local.cpu_temp_sensor.get_temperature().unwrap(),
538 ))
539 });
540
541 Systick::delay((telemetry_period as u32).secs()).await;
543 }
544 }
545
546 #[task(priority = 1, shared=[usb, settings], local=[usb_terminal])]
547 async fn usb(mut c: usb::Context) {
548 loop {
549 c.shared.usb.lock(|usb| {
551 usb.poll(&mut [c
552 .local
553 .usb_terminal
554 .interface_mut()
555 .inner_mut()]);
556 });
557
558 c.shared.settings.lock(|settings| {
559 if c.local.usb_terminal.poll(settings).unwrap() {
560 settings_update::spawn().unwrap()
561 }
562 });
563
564 Systick::delay(10.millis()).await;
565 }
566 }
567
568 #[task(priority = 1, shared=[network])]
569 async fn ethernet_link(mut c: ethernet_link::Context) {
570 loop {
571 c.shared.network.lock(|net| net.processor.handle_link());
572 Systick::delay(1.secs()).await;
573 }
574 }
575
576 #[task(binds = ETH, priority = 1)]
577 fn eth(_: eth::Context) {
578 unsafe { hal::ethernet::interrupt_handler() }
579 }
580}