mpll/
mpll.rs

1#![cfg_attr(target_os = "none", no_std)]
2#![cfg_attr(target_os = "none", no_main)]
3
4use core::sync::atomic::{Ordering, fence};
5
6use fugit::ExtU32;
7use miniconf::{Leaf, Tree};
8use rtic_monotonics::Monotonic;
9
10use serde::Serialize;
11use stabilizer::{convert::Gain, mpll::*, statistics};
12
13use platform::{AppSettings, NetSettings};
14
15#[derive(Clone, Debug, Tree, Default)]
16#[tree(meta(doc, typename))]
17pub struct Settings {
18    mpll: App,
19    net: NetSettings,
20}
21
22impl AppSettings for Settings {
23    fn new(net: NetSettings) -> Self {
24        Self {
25            net,
26            mpll: App::default(),
27        }
28    }
29
30    fn net(&self) -> &NetSettings {
31        &self.net
32    }
33}
34
35impl serial_settings::Settings for Settings {
36    fn reset(&mut self) {
37        *self = Self {
38            mpll: App::default(),
39            net: NetSettings::new(self.net.mac),
40        }
41    }
42}
43
44#[derive(Clone, Debug, Tree)]
45#[tree(meta(doc, typename))]
46pub struct App {
47    /// AFE gain
48    afe: [Leaf<Gain>; 2],
49
50    mpll: MpllConfig,
51
52    /// Specifies the telemetry output period in seconds.
53    telemetry_period: f32,
54
55    /// Specifies the target for data streaming.
56    #[tree(with=miniconf::leaf)]
57    stream: stream::Target,
58
59    /// Activate settings
60    ///
61    /// If `activate = true` each `mpll` settings change immediately results
62    /// in activation.
63    activate: bool,
64}
65
66impl Default for App {
67    fn default() -> Self {
68        Self {
69            afe: [Leaf(Gain::G10); 2],
70            telemetry_period: 10.,
71            stream: Default::default(),
72            mpll: Default::default(),
73            activate: true,
74        }
75    }
76}
77
78/// Channel Telemetry
79#[derive(Default, Clone)]
80pub struct TelemetryState {
81    demod: [[statistics::State; 2]; 2],
82    phase: statistics::State,
83    frequency: statistics::State,
84}
85
86#[derive(Default, Clone, Serialize)]
87pub struct TelemetryCooked {
88    demod: [[statistics::ScaledStatistics; 2]; 2],
89    phase: statistics::ScaledStatistics,
90    frequency: statistics::ScaledStatistics,
91}
92
93impl From<TelemetryState> for TelemetryCooked {
94    fn from(t: TelemetryState) -> Self {
95        const VOLT_PER_LSB: f32 = 10.24 /* V FS */ / Gain::G10.gain() * 2.0 /* conversion */ / (1u64 << 31) as f32;
96        Self {
97            demod: t.demod.map(|d| d.map(|p| p.get_scaled(VOLT_PER_LSB))),
98            phase: t.phase.get_scaled(UNITS.x),
99            frequency: t.frequency.get_scaled(UNITS.y),
100        }
101    }
102}
103
104#[derive(Default, Clone, Serialize)]
105pub struct Telemetry {
106    mpll: TelemetryCooked,
107    cpu_temp: f32,
108}
109
110#[cfg(not(target_os = "none"))]
111fn main() {
112    use miniconf::{json::to_json_value, json_schema::TreeJsonSchema};
113    let s = Settings::default();
114    println!(
115        "{}",
116        serde_json::to_string_pretty(&to_json_value(&s).unwrap()).unwrap()
117    );
118    let mut schema = TreeJsonSchema::new(Some(&s)).unwrap();
119    schema
120        .root
121        .insert("title".to_string(), "Stabilizer mpll".into());
122    println!("{}", serde_json::to_string_pretty(&schema.root).unwrap());
123}
124
125#[cfg(target_os = "none")]
126#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, SDMMC])]
127mod app {
128    use super::*;
129    use stabilizer::hardware::{
130        self, SerialTerminal, SystemTimer, Systick, UsbDevice,
131        adc::{Adc0Input, Adc1Input},
132        dac::{Dac0Output, Dac1Output},
133        hal,
134        net::{NetworkState, NetworkUsers},
135        timers::SamplingTimer,
136    };
137    use stream::FrameGenerator;
138
139    #[shared]
140    struct Shared {
141        usb: UsbDevice,
142        network: NetworkUsers<App>,
143        settings: Settings,
144        active_settings: Mpll,
145        telemetry: TelemetryState,
146    }
147
148    #[local]
149    struct Local {
150        usb_terminal: SerialTerminal<Settings>,
151        sampling_timer: SamplingTimer,
152        adcs: (Adc0Input, Adc1Input),
153        dacs: (Dac0Output, Dac1Output),
154        state: MpllState,
155        generator: FrameGenerator,
156        cpu_temp_sensor: stabilizer::hardware::cpu_temp_sensor::CpuTempSensor,
157        afes: [stabilizer::hardware::Pgia; 2],
158    }
159
160    #[init]
161    fn init(c: init::Context) -> (Shared, Local) {
162        let clock = SystemTimer::new(|| Systick::now().ticks());
163
164        // Configure the microcontroller
165        let (mut carrier, _mezzanine, _eem) = hardware::setup::setup::<Settings>(
166            c.core,
167            c.device,
168            clock,
169            BATCH_SIZE,
170            SAMPLE_TICKS,
171        );
172
173        carrier.afes[0].set_gain(carrier.settings.mpll.afe[0].0);
174        carrier.afes[1].set_gain(carrier.settings.mpll.afe[1].0);
175
176        let mut network = NetworkUsers::new(
177            carrier.network_devices.stack,
178            carrier.network_devices.phy,
179            clock,
180            env!("CARGO_BIN_NAME"),
181            &carrier.settings.net,
182            carrier.metadata,
183        );
184
185        let generator = network.configure_streaming(stream::Format::Mpll);
186
187        let shared = Shared {
188            network,
189            usb: carrier.usb,
190            telemetry: Default::default(),
191            active_settings: carrier.settings.mpll.mpll.build(),
192            settings: carrier.settings,
193        };
194
195        let mut local = Local {
196            usb_terminal: carrier.usb_serial,
197            sampling_timer: carrier.sampling_timer,
198            adcs: carrier.adcs,
199            dacs: carrier.dacs,
200            cpu_temp_sensor: carrier.temperature_sensor,
201            state: Default::default(),
202            generator,
203            afes: carrier.afes,
204        };
205
206        // Enable ADC/DAC events
207        local.adcs.0.start();
208        local.adcs.1.start();
209        local.dacs.0.start();
210        local.dacs.1.start();
211
212        settings_update::spawn().unwrap();
213        telemetry::spawn().unwrap();
214        ethernet_link::spawn().unwrap();
215        start::spawn().unwrap();
216        usb::spawn().unwrap();
217
218        (shared, local)
219    }
220
221    #[task(priority = 1, local=[sampling_timer])]
222    async fn start(c: start::Context) {
223        Systick::delay(100.millis()).await;
224        // Start sampling ADCs and DACs.
225        c.local.sampling_timer.start();
226    }
227
228    #[task(binds=DMA1_STR4, shared=[active_settings, telemetry], local=[adcs, dacs, state, generator], priority=3)]
229    #[unsafe(link_section = ".itcm.process")]
230    fn process(c: process::Context) {
231        let process::SharedResources {
232            active_settings,
233            telemetry,
234            ..
235        } = c.shared;
236
237        let process::LocalResources {
238            adcs: (adc0, adc1),
239            dacs: (dac0, dac1),
240            state,
241            generator,
242            ..
243        } = c.local;
244
245        (active_settings, telemetry).lock(|settings, telemetry| {
246            (adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| {
247                // Preserve instruction and data ordering w.r.t. DMA flag access.
248                fence(Ordering::SeqCst);
249                let adc: [&[u16; BATCH_SIZE]; 2] = [
250                    (**adc0).try_into().unwrap(),
251                    (**adc1).try_into().unwrap(),
252                ];
253                let dac: [&mut [u16; BATCH_SIZE]; 2] =
254                    [(*dac0).try_into().unwrap(), (*dac1).try_into().unwrap()];
255
256                let stream = settings.process(state, adc, dac);
257
258                telemetry.phase.update(stream.phase.0);
259                telemetry.frequency.update(stream.frequency.0);
260                telemetry.demod[0][0].update(stream.demod[0][0]);
261                telemetry.demod[0][1].update(stream.demod[0][1]);
262                telemetry.demod[1][0].update(stream.demod[1][0]);
263                telemetry.demod[1][1].update(stream.demod[1][1]);
264
265                const N: usize = core::mem::size_of::<Stream>();
266                generator.add(|buf| {
267                    buf[..N].copy_from_slice(bytemuck::cast_slice(
268                        bytemuck::bytes_of(&stream),
269                    ));
270                    N
271                });
272
273                // Preserve instruction and data ordering w.r.t. DMA flag access.
274                fence(Ordering::SeqCst);
275            });
276        });
277    }
278
279    #[idle(shared=[settings, network, usb])]
280    fn idle(mut c: idle::Context) -> ! {
281        loop {
282            match (&mut c.shared.network, &mut c.shared.settings)
283                .lock(|net, settings| net.update(&mut settings.mpll))
284            {
285                NetworkState::SettingsChanged => {
286                    settings_update::spawn().unwrap()
287                }
288                NetworkState::Updated => {}
289                NetworkState::NoChange => {
290                    // We can't sleep if USB is not in suspend.
291                    if c.shared.usb.lock(|usb| {
292                        usb.state()
293                            == usb_device::device::UsbDeviceState::Suspend
294                    }) {
295                        cortex_m::asm::wfi();
296                    }
297                }
298            }
299        }
300    }
301
302    #[task(priority = 1, shared=[network, settings, active_settings], local=[afes])]
303    async fn settings_update(mut c: settings_update::Context) {
304        c.shared.settings.lock(|settings| {
305            c.shared
306                .network
307                .lock(|net| net.direct_stream(settings.mpll.stream));
308            c.local.afes[0].set_gain(settings.mpll.afe[0].0);
309            c.local.afes[1].set_gain(settings.mpll.afe[1].0);
310            if settings.mpll.activate {
311                let new = settings.mpll.mpll.build();
312                c.shared.active_settings.lock(|current| *current = new);
313            }
314        });
315    }
316
317    #[task(priority = 1, local=[cpu_temp_sensor], shared=[network, settings, telemetry])]
318    async fn telemetry(mut c: telemetry::Context) -> ! {
319        loop {
320            let tele = Telemetry {
321                mpll: c.shared.telemetry.lock(|t| core::mem::take(t)).into(),
322                cpu_temp: c
323                    .local
324                    .cpu_temp_sensor
325                    .get_temperature()
326                    .unwrap_or_default(),
327            };
328            c.shared.network.lock(|net| {
329                net.telemetry.publish_telemetry("/telemetry", &tele)
330            });
331
332            let delay = c.shared.settings.lock(|s| s.mpll.telemetry_period);
333            Systick::delay(((delay * 1000.0) as u32).millis()).await;
334        }
335    }
336
337    #[task(priority = 1, shared=[usb, settings], local=[usb_terminal])]
338    async fn usb(mut c: usb::Context) -> ! {
339        loop {
340            // Handle the USB serial terminal.
341            c.shared.usb.lock(|usb| {
342                usb.poll(&mut [c
343                    .local
344                    .usb_terminal
345                    .interface_mut()
346                    .inner_mut()]);
347            });
348
349            c.shared.settings.lock(|settings| {
350                if c.local.usb_terminal.poll(settings).unwrap() {
351                    settings_update::spawn().unwrap()
352                }
353            });
354
355            Systick::delay(10.millis()).await;
356        }
357    }
358
359    #[task(priority = 1, shared=[network])]
360    async fn ethernet_link(mut c: ethernet_link::Context) -> ! {
361        loop {
362            c.shared.network.lock(|net| net.processor.handle_link());
363            Systick::delay(1.secs()).await;
364        }
365    }
366
367    #[task(binds = ETH, priority = 1)]
368    fn eth(_: eth::Context) {
369        unsafe { hal::ethernet::interrupt_handler() }
370    }
371}