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