1#![cfg_attr(target_os = "none", no_std)]
29#![cfg_attr(target_os = "none", no_main)]
30
31use miniconf::Tree;
32
33use dsp_process::SplitProcess;
34use idsp::iir::{self, pid::Units};
35
36use platform::{AppSettings, NetSettings};
37use serde::{Deserialize, Serialize};
38use signal_generator::{self, Source};
39use stabilizer::convert::{AdcCode, DacCode, Gain};
40
41const IIR_CASCADE_LENGTH: usize = 1;
43
44const BATCH_SIZE: usize = 8;
46
47const SAMPLE_TICKS_LOG2: u8 = 7;
50const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2;
51const SAMPLE_PERIOD: f32 =
52 SAMPLE_TICKS as f32 * stabilizer::design_parameters::TIMER_PERIOD;
53
54const UNITS: Units<f32> = Units {
55 t: SAMPLE_PERIOD,
56 x: AdcCode::VOLT_PER_LSB,
57 y: DacCode::VOLT_PER_LSB,
58};
59
60#[derive(Clone, Debug, Tree, Default)]
61#[tree(meta(doc, typename))]
62pub struct Settings {
63 dual_iir: DualIir,
64 net: NetSettings,
65}
66
67impl AppSettings for Settings {
68 fn new(net: NetSettings) -> Self {
69 Self {
70 net,
71 dual_iir: DualIir::default(),
72 }
73 }
74
75 fn net(&self) -> &NetSettings {
76 &self.net
77 }
78}
79
80impl serial_settings::Settings for Settings {
81 fn reset(&mut self) {
82 *self = Self {
83 dual_iir: DualIir::default(),
84 net: NetSettings::new(self.net.mac),
85 }
86 }
87}
88
89#[derive(Clone, Debug, Tree)]
90#[tree(meta(doc, typename = "BiquadReprTree"))]
91pub struct BiquadRepr {
92 #[tree(rename="typ", typ="&str", with=miniconf::str_leaf, defer=self.repr)]
94 _typ: (),
95 repr: iir::repr::BiquadRepr<f32, f32>,
96}
97
98impl Default for BiquadRepr {
99 fn default() -> Self {
100 let mut i = iir::BiquadClamp::from(iir::Biquad::IDENTITY);
101 i.min = -i16::MAX as _;
102 i.max = i16::MAX as _;
103 Self {
104 _typ: (),
105 repr: iir::repr::BiquadRepr::Raw(i),
106 }
107 }
108}
109
110#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
111pub enum Run {
112 #[default]
113 Run,
115 Hold,
117 External,
119}
120
121impl Run {
122 fn run(&self, di: bool) -> bool {
123 match self {
124 Self::Run => true,
125 Self::Hold => false,
126 Self::External => di,
127 }
128 }
129}
130
131#[derive(Clone, Debug, Tree, Default)]
133#[tree(meta(doc, typename))]
134pub struct Channel {
135 #[tree(with=miniconf::leaf)]
137 gain: Gain,
138 biquad: [BiquadRepr; IIR_CASCADE_LENGTH],
140 #[tree(with=miniconf::leaf)]
142 run: Run,
143 source: signal_generator::Config,
145}
146
147impl Channel {
148 fn build(&self) -> Result<Active, signal_generator::Error> {
149 Ok(Active {
150 source: self
151 .source
152 .build(SAMPLE_PERIOD, DacCode::FULL_SCALE.recip())
153 .unwrap(),
154 state: Default::default(),
155 run: self.run,
156 biquad: self
157 .biquad
158 .each_ref()
159 .map(|biquad| biquad.repr.build(&UNITS)),
160 })
161 }
162}
163
164#[derive(Clone, Debug, Tree)]
165#[tree(meta(doc, typename))]
166pub struct DualIir {
167 ch: [Channel; 2],
169 #[tree(with=miniconf::leaf)]
171 trigger: bool,
172 #[tree(with=miniconf::leaf)]
174 telemetry_period: f32,
175 #[tree(with=miniconf::leaf)]
179 stream: stream::Target,
180}
181
182impl Default for DualIir {
183 fn default() -> Self {
184 Self {
185 telemetry_period: 10.0,
186 trigger: false,
187 stream: Default::default(),
188 ch: Default::default(),
189 }
190 }
191}
192
193#[derive(Clone, Debug)]
194pub struct Active {
195 run: Run,
196 biquad: [iir::BiquadClamp<f32, f32>; IIR_CASCADE_LENGTH],
197 state: [iir::DirectForm1<f32>; IIR_CASCADE_LENGTH],
198 source: Source,
199}
200
201#[cfg(not(target_os = "none"))]
202fn main() {
203 use miniconf::{json::to_json_value, json_schema::TreeJsonSchema};
204 let s = Settings::default();
205 println!(
206 "{}",
207 serde_json::to_string_pretty(&to_json_value(&s).unwrap()).unwrap()
208 );
209 let mut schema = TreeJsonSchema::new(Some(&s)).unwrap();
210 schema
211 .root
212 .insert("title".to_string(), "Stabilizer dual-iir".into());
213 println!("{}", serde_json::to_string_pretty(&schema.root).unwrap());
214}
215
216#[cfg(target_os = "none")]
217#[cfg_attr(target_os = "none", rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, LTDC, SDMMC]))]
218mod app {
219 use super::*;
220 use core::sync::atomic::{Ordering, fence};
221 use fugit::ExtU32 as _;
222 use rtic_monotonics::Monotonic;
223
224 use stabilizer::{
225 hardware::{
226 self, DigitalInput0, DigitalInput1, Pgia, SerialTerminal,
227 SystemTimer, Systick, UsbDevice,
228 adc::{Adc0Input, Adc1Input},
229 dac::{Dac0Output, Dac1Output},
230 hal,
231 net::{NetworkState, NetworkUsers},
232 timers::SamplingTimer,
233 },
234 telemetry::TelemetryBuffer,
235 };
236 use stream::FrameGenerator;
237
238 #[shared]
239 struct Shared {
240 usb: UsbDevice,
241 network: NetworkUsers<DualIir>,
242 settings: Settings,
243 active: [Active; 2],
244 telemetry: TelemetryBuffer,
245 }
246
247 #[local]
248 struct Local {
249 usb_terminal: SerialTerminal<Settings>,
250 sampling_timer: SamplingTimer,
251 digital_inputs: (DigitalInput0, DigitalInput1),
252 afes: [Pgia; 2],
253 adcs: (Adc0Input, Adc1Input),
254 dacs: (Dac0Output, Dac1Output),
255 generator: FrameGenerator,
256 cpu_temp_sensor: stabilizer::hardware::cpu_temp_sensor::CpuTempSensor,
257 }
258
259 #[init]
260 fn init(c: init::Context) -> (Shared, Local) {
261 let clock = SystemTimer::new(|| Systick::now().ticks());
262
263 let (stabilizer, _mezzanine, _eem) = hardware::setup::setup::<Settings>(
265 c.core,
266 c.device,
267 clock,
268 BATCH_SIZE,
269 SAMPLE_TICKS,
270 );
271
272 let mut network = NetworkUsers::new(
273 stabilizer.network_devices.stack,
274 stabilizer.network_devices.phy,
275 clock,
276 env!("CARGO_BIN_NAME"),
277 &stabilizer.settings.net,
278 stabilizer.metadata,
279 );
280
281 let generator = network.configure_streaming(stream::Format::AdcDacData);
282
283 let shared = Shared {
284 usb: stabilizer.usb,
285 network,
286 active: stabilizer
287 .settings
288 .dual_iir
289 .ch
290 .each_ref()
291 .map(|a| a.build().unwrap()),
292 telemetry: TelemetryBuffer::default(),
293 settings: stabilizer.settings,
294 };
295
296 let mut local = Local {
297 usb_terminal: stabilizer.usb_serial,
298 sampling_timer: stabilizer.sampling_timer,
299 digital_inputs: stabilizer.digital_inputs,
300 afes: stabilizer.afes,
301 adcs: stabilizer.adcs,
302 dacs: stabilizer.dacs,
303 generator,
304 cpu_temp_sensor: stabilizer.temperature_sensor,
305 };
306
307 local.adcs.0.start();
309 local.adcs.1.start();
310 local.dacs.0.start();
311 local.dacs.1.start();
312
313 settings_update::spawn().unwrap();
315 telemetry::spawn().unwrap();
316 ethernet_link::spawn().unwrap();
317 usb::spawn().unwrap();
318 start::spawn().unwrap();
319
320 (shared, local)
321 }
322
323 #[task(priority = 1, local=[sampling_timer])]
324 async fn start(c: start::Context) {
325 Systick::delay(100.millis()).await;
326 c.local.sampling_timer.start();
328 }
329
330 #[task(
347 binds=DMA1_STR4,
348 local=[digital_inputs, adcs, dacs, generator, source: [[i16; BATCH_SIZE]; 2] = [[0; BATCH_SIZE]; 2]],
349 shared=[active, telemetry],
350 priority=3)]
351 #[unsafe(link_section = ".itcm.process")]
352 fn process(c: process::Context) {
353 let process::SharedResources {
354 active, telemetry, ..
355 } = c.shared;
356
357 let process::LocalResources {
358 digital_inputs,
359 adcs: (adc0, adc1),
360 dacs: (dac0, dac1),
361 generator,
362 source,
363 ..
364 } = c.local;
365
366 (active, telemetry).lock(|active, telemetry| {
367 (adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| {
368 fence(Ordering::SeqCst);
370 let adc: [&[u16; BATCH_SIZE]; 2] = [
371 (**adc0).try_into().unwrap(),
372 (**adc1).try_into().unwrap(),
373 ];
374 let mut dac: [&mut [u16; BATCH_SIZE]; 2] =
375 [(*dac0).try_into().unwrap(), (*dac1).try_into().unwrap()];
376
377 for ((((adc, dac), active), di), source) in adc
378 .into_iter()
379 .zip(dac.iter_mut())
380 .zip(active.iter_mut())
381 .zip(telemetry.digital_inputs)
382 .zip(source.iter())
383 {
384 for ((adc, dac), source) in
385 adc.iter().zip(dac.iter_mut()).zip(source)
386 {
387 let x = f32::from(*adc as i16);
388 let y = active
389 .biquad
390 .iter()
391 .zip(active.state.iter_mut())
392 .fold(x, |y, (ch, state)| {
393 if active.run.run(di) {
394 ch.process(state, y)
395 } else {
396 iir::Biquad::<f32>::HOLD.process(state, y)
397 }
398 });
399
400 let y: i16 = unsafe { y.to_int_unchecked() };
403 *dac = DacCode::from(y.saturating_add(*source)).0;
404 }
405 }
406 telemetry.adcs = [AdcCode(adc[0][0]), AdcCode(adc[1][0])];
407 telemetry.dacs = [DacCode(dac[0][0]), DacCode(dac[1][0])];
408
409 const N: usize = BATCH_SIZE * size_of::<i16>();
410 generator.add(|buf| {
411 [adc[0], adc[1], dac[0], dac[1]]
412 .into_iter()
413 .zip(buf.chunks_exact_mut(N))
414 .map(|(data, buf)| {
415 buf.copy_from_slice(bytemuck::cast_slice(data))
416 })
417 .count()
418 * N
419 });
420
421 fence(Ordering::SeqCst);
422 });
423 *source = active.each_mut().map(|ch| {
424 core::array::from_fn(|_| (ch.source.next().unwrap() >> 16) as _)
425 });
426 telemetry.digital_inputs =
427 [digital_inputs.0.is_high(), digital_inputs.1.is_high()];
428 });
429 }
430
431 #[idle(shared=[network, settings, usb])]
432 fn idle(mut c: idle::Context) -> ! {
433 loop {
434 match (&mut c.shared.network, &mut c.shared.settings)
435 .lock(|net, settings| net.update(&mut settings.dual_iir))
436 {
437 NetworkState::SettingsChanged => {
438 settings_update::spawn().unwrap();
439 }
440 NetworkState::Updated => {}
441 NetworkState::NoChange => {
442 if c.shared.usb.lock(|usb| {
444 usb.state()
445 == usb_device::device::UsbDeviceState::Suspend
446 }) {
447 cortex_m::asm::wfi();
448 }
449 }
450 }
451 }
452 }
453
454 #[task(priority = 1, local=[afes], shared=[network, settings, active])]
455 async fn settings_update(mut c: settings_update::Context) {
456 c.shared.settings.lock(|settings| {
457 c.local.afes[0].set_gain(settings.dual_iir.ch[0].gain);
458 c.local.afes[1].set_gain(settings.dual_iir.ch[1].gain);
459
460 if settings.dual_iir.trigger {
461 settings.dual_iir.trigger = false;
462 let s = settings.dual_iir.ch.each_ref().map(|ch| {
463 let s = ch
464 .source
465 .build(SAMPLE_PERIOD, DacCode::FULL_SCALE.recip());
466 if let Err(err) = &s {
467 log::error!("Failed to update source: {:?}", err);
468 }
469 s
470 });
471 c.shared.active.lock(|ch| {
472 for (ch, s) in ch.iter_mut().zip(s) {
473 if let Ok(s) = s {
474 ch.source = s;
475 }
476 }
477 });
478 }
479 let b = settings.dual_iir.ch.each_ref().map(|ch| {
480 (ch.run, ch.biquad.each_ref().map(|b| b.repr.build(&UNITS)))
481 });
482 c.shared.active.lock(|active| {
483 for (a, b) in active.iter_mut().zip(b) {
484 (a.run, a.biquad) = b;
485 }
486 });
487 c.shared
488 .network
489 .lock(|net| net.direct_stream(settings.dual_iir.stream));
490 });
491 }
492
493 #[task(priority = 1, shared=[network, settings, telemetry], local=[cpu_temp_sensor])]
494 async fn telemetry(mut c: telemetry::Context) -> ! {
495 loop {
496 let telemetry =
497 c.shared.telemetry.lock(|telemetry| telemetry.clone());
498
499 let (gains, telemetry_period) =
500 c.shared.settings.lock(|settings| {
501 (
502 settings.dual_iir.ch.each_ref().map(|ch| ch.gain),
503 settings.dual_iir.telemetry_period,
504 )
505 });
506
507 c.shared.network.lock(|net| {
508 net.telemetry.publish_telemetry(
509 "/telemetry",
510 &telemetry.finalize(
511 gains[0],
512 gains[1],
513 c.local.cpu_temp_sensor.get_temperature().unwrap(),
514 ),
515 )
516 });
517
518 Systick::delay(((telemetry_period * 1000.0) as u32).millis()).await;
519 }
520 }
521
522 #[task(priority = 1, shared=[usb, settings], local=[usb_terminal])]
523 async fn usb(mut c: usb::Context) -> ! {
524 loop {
525 c.shared.usb.lock(|usb| {
527 usb.poll(&mut [c
528 .local
529 .usb_terminal
530 .interface_mut()
531 .inner_mut()]);
532 });
533
534 c.shared.settings.lock(|settings| {
535 if c.local.usb_terminal.poll(settings).unwrap() {
536 settings_update::spawn().unwrap()
537 }
538 });
539
540 Systick::delay(10.millis()).await;
541 }
542 }
543
544 #[task(priority = 1, shared=[network])]
545 async fn ethernet_link(mut c: ethernet_link::Context) -> ! {
546 loop {
547 c.shared.network.lock(|net| net.processor.handle_link());
548 Systick::delay(1.secs()).await;
549 }
550 }
551
552 #[task(binds = ETH, priority = 1)]
553 fn eth(_: eth::Context) {
554 unsafe { hal::ethernet::interrupt_handler() }
555 }
556
557 #[task(binds = SPI2, priority = 4)]
558 fn spi2(_: spi2::Context) {
559 panic!("ADC0 SPI error");
560 }
561
562 #[task(binds = SPI3, priority = 4)]
563 fn spi3(_: spi3::Context) {
564 panic!("ADC1 SPI error");
565 }
566
567 #[task(binds = SPI4, priority = 4)]
568 fn spi4(_: spi4::Context) {
569 panic!("DAC0 SPI error");
570 }
571
572 #[task(binds = SPI5, priority = 4)]
573 fn spi5(_: spi5::Context) {
574 panic!("DAC1 SPI error");
575 }
576}