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