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: [Leaf<Gain>; 2],
49
50 mpll: MpllConfig,
51
52 telemetry_period: f32,
54
55 #[tree(with=miniconf::leaf)]
57 stream: stream::Target,
58
59 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#[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 / Gain::G10.gain() * 2.0 / (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 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 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 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 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 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 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 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}