dds/
dds.rs

1//! Urukul as a downstream EEM on stabilizer.
2//!
3//! This requires the alternate direction EEM transceiver configuration.
4//! It exposes the Urukul CPLD and DDS settings via miniconf (MQTT and USB).
5//!
6//! Note that several values are not range checked and out-of-range values
7//! will lead to panics.
8#![cfg_attr(target_os = "none", no_std)]
9#![cfg_attr(target_os = "none", no_main)]
10
11use arbitrary_int::{u2, u5};
12use fugit::ExtU32;
13use miniconf::Tree;
14use rtic_monotonics::Monotonic;
15
16use platform::{AppSettings, NetSettings};
17
18#[derive(Clone, Debug, Tree, Default)]
19#[tree(meta(doc, typename))]
20pub struct Settings {
21    urukul: App,
22    net: NetSettings,
23}
24
25impl AppSettings for Settings {
26    fn new(net: NetSettings) -> Self {
27        Self {
28            net,
29            urukul: App::default(),
30        }
31    }
32
33    fn net(&self) -> &NetSettings {
34        &self.net
35    }
36}
37
38impl serial_settings::Settings for Settings {
39    fn reset(&mut self) {
40        *self = Self {
41            urukul: App::default(),
42            net: NetSettings::new(self.net.mac),
43        }
44    }
45}
46
47#[derive(Clone, Debug, Tree)]
48#[tree(meta(doc, typename))]
49pub struct Channel {
50    #[tree(with=miniconf::leaf)]
51    pll_n: Option<u5>,
52    pll_doubler: bool,
53    frequency: f64,
54    phase: f32,
55    full_scale_current: f32,
56    attenuation: f32,
57    enable: bool,
58    update: bool,
59}
60
61impl Default for Channel {
62    fn default() -> Self {
63        Self {
64            frequency: 0.0,
65            phase: 0.0,
66            full_scale_current: 20e-3,
67            attenuation: 31.5,
68            enable: false,
69            pll_n: Some(u5::new(3)),
70            pll_doubler: false,
71            update: true,
72        }
73    }
74}
75
76#[derive(Clone, Debug, Tree)]
77#[tree(meta(doc, typename))]
78pub struct App {
79    refclk: f64,
80    #[tree(with=miniconf::leaf)]
81    clk_sel: urukul::ClkSel,
82    #[tree(with=miniconf::leaf)]
83    div_sel: urukul::DivSel,
84    update: bool,
85    ch: [Channel; 4],
86}
87
88impl Default for App {
89    fn default() -> Self {
90        Self {
91            clk_sel: urukul::ClkSel::Osc,
92            div_sel: urukul::DivSel::One,
93            update: true,
94            refclk: 100.0e6,
95            ch: Default::default(),
96        }
97    }
98}
99
100#[cfg(not(target_os = "none"))]
101fn main() {
102    use miniconf::{json::to_json_value, json_schema::TreeJsonSchema};
103    let s = Settings::default();
104    println!(
105        "{}",
106        serde_json::to_string_pretty(&to_json_value(&s).unwrap()).unwrap()
107    );
108    let mut schema = TreeJsonSchema::new(Some(&s)).unwrap();
109    schema
110        .root
111        .insert("title".to_string(), "Stabilizer dds".into());
112    println!("{}", serde_json::to_string_pretty(&schema.root).unwrap());
113}
114
115#[cfg(target_os = "none")]
116#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, LTDC, SDMMC])]
117mod app {
118    use super::*;
119
120    use stabilizer::hardware::{
121        self, SerialTerminal, SystemTimer, Systick, Urukul, UsbDevice, hal,
122        net::{NetworkState, NetworkUsers},
123    };
124
125    #[shared]
126    struct Shared {
127        usb: UsbDevice,
128        network: NetworkUsers<App>,
129        settings: Settings,
130    }
131
132    #[local]
133    struct Local {
134        urukul: Urukul,
135        usb_terminal: SerialTerminal<Settings>,
136    }
137
138    #[init]
139    fn init(c: init::Context) -> (Shared, Local) {
140        let clock = SystemTimer::new(|| Systick::now().ticks());
141
142        let (stabilizer, _mezzanine, eem) = hardware::setup::setup::<Settings>(
143            c.core,
144            c.device,
145            clock,
146            8,
147            1 << 7,
148        );
149
150        let stabilizer::hardware::Eem::Urukul(urukul) = eem else {
151            panic!("No Urukul detected.")
152        };
153
154        let network = NetworkUsers::new(
155            stabilizer.network_devices.stack,
156            stabilizer.network_devices.phy,
157            clock,
158            env!("CARGO_BIN_NAME"),
159            &stabilizer.settings.net,
160            stabilizer.metadata,
161        );
162
163        let shared = Shared {
164            usb: stabilizer.usb,
165            network,
166            settings: stabilizer.settings,
167        };
168
169        let local = Local {
170            urukul,
171            usb_terminal: stabilizer.usb_serial,
172        };
173
174        // Spawn a settings update for default settings.
175        settings_update::spawn().unwrap();
176        ethernet_link::spawn().unwrap();
177        usb::spawn().unwrap();
178
179        (shared, local)
180    }
181
182    #[idle(shared=[network, settings, usb])]
183    fn idle(mut c: idle::Context) -> ! {
184        loop {
185            match (&mut c.shared.network, &mut c.shared.settings)
186                .lock(|net, settings| net.update(&mut settings.urukul))
187            {
188                NetworkState::SettingsChanged => {
189                    settings_update::spawn().unwrap()
190                }
191                NetworkState::Updated => {}
192                NetworkState::NoChange => {
193                    // We can't sleep if USB is not in suspend.
194                    if c.shared.usb.lock(|usb| {
195                        usb.state()
196                            == usb_device::device::UsbDeviceState::Suspend
197                    }) {
198                        cortex_m::asm::wfi();
199                    }
200                }
201            }
202        }
203    }
204
205    #[task(priority = 1, shared=[settings], local=[urukul])]
206    async fn settings_update(mut c: settings_update::Context) {
207        let u = c.local.urukul;
208        c.shared.settings.lock(|s| {
209            let s = &mut s.urukul;
210            if s.update {
211                s.update = false;
212                u.set_cfg(
213                    u.cfg().with_clk_sel(s.clk_sel).with_div_sel(s.div_sel),
214                )
215                .unwrap();
216            }
217            let power = ad9912::Power::builder()
218                .with_digital_pd(false)
219                .with_full_pd(false)
220                .with_pll_pd(true)
221                .with_output_doubler_en(false)
222                .with_cmos_en(false)
223                .with_hstl_pd(true)
224                .build();
225            for (i, ch) in s.ch.iter_mut().enumerate() {
226                if ch.update {
227                    ch.update = false;
228                    let refclk = s.refclk / s.div_sel.divider() as f64;
229                    let i = u2::new(i as _);
230                    let sysclk = if let Some(pll_n) = ch.pll_n {
231                        u.dds(i).set_power(power.with_pll_pd(false)).unwrap();
232                        u.dds(i).set_ndiv(pll_n).unwrap();
233                        let mut pll = ad9912::Pll::default()
234                            .with_charge_pump(ad9912::ChargePump::Ua375)
235                            .with_ref_doubler(ch.pll_doubler);
236                        let sysclk = pll.set_refclk(pll_n, refclk);
237                        u.dds(i).set_pll(pll).unwrap();
238                        sysclk
239                    } else {
240                        u.dds(i).set_power(power.with_pll_pd(true)).unwrap();
241                        refclk
242                    };
243                    u.dds(i).set_frequency(ch.frequency, sysclk).unwrap();
244                    u.dds(i).set_phase(ch.phase).unwrap();
245                    u.io_update().unwrap();
246                    u.dds(i)
247                        .set_full_scale_current(ch.full_scale_current, 10e3)
248                        .unwrap();
249                    u.set_att(i, urukul::att_to_mu(ch.attenuation)).unwrap();
250                    u.set_rf_sw(i, ch.enable).unwrap();
251                }
252            }
253        });
254    }
255
256    #[task(priority = 1, shared=[usb, settings], local=[usb_terminal])]
257    async fn usb(mut c: usb::Context) {
258        loop {
259            c.shared.usb.lock(|usb| {
260                usb.poll(&mut [c
261                    .local
262                    .usb_terminal
263                    .interface_mut()
264                    .inner_mut()]);
265            });
266
267            c.shared.settings.lock(|settings| {
268                if c.local.usb_terminal.poll(settings).unwrap() {
269                    settings_update::spawn().unwrap()
270                }
271            });
272
273            Systick::delay(10.millis()).await;
274        }
275    }
276
277    #[task(priority = 1, shared=[network])]
278    async fn ethernet_link(mut c: ethernet_link::Context) {
279        loop {
280            c.shared.network.lock(|net| net.processor.handle_link());
281            Systick::delay(1.secs()).await;
282        }
283    }
284
285    #[task(binds = ETH, priority = 1)]
286    fn eth(_: eth::Context) {
287        unsafe { hal::ethernet::interrupt_handler() }
288    }
289}