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