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