#![no_std]
#![no_main]
use core::{
convert::TryFrom,
mem::MaybeUninit,
sync::atomic::{fence, Ordering},
};
use miniconf::{Leaf, Tree};
use rtic_monotonics::Monotonic;
use fugit::ExtU32;
use mutex_trait::prelude::*;
use idsp::{Accu, Complex, ComplexExt, Filter, Lowpass, Repeat, RPLL};
use stabilizer::{
hardware::{
self,
adc::{Adc0Input, Adc1Input, AdcCode},
afe::Gain,
dac::{Dac0Output, Dac1Output, DacCode},
hal,
input_stamper::InputStamper,
signal_generator,
timers::SamplingTimer,
DigitalInput0, DigitalInput1, SerialTerminal, SystemTimer, Systick,
UsbDevice, AFE0, AFE1,
},
net::{
data_stream::{FrameGenerator, StreamFormat, StreamTarget},
serde::{Deserialize, Serialize},
telemetry::TelemetryBuffer,
NetworkState, NetworkUsers,
},
settings::NetSettings,
};
const BATCH_SIZE_LOG2: u32 = 3;
const BATCH_SIZE: usize = 1 << BATCH_SIZE_LOG2;
const SAMPLE_TICKS_LOG2: u32 = 7;
const SAMPLE_TICKS: u32 = 1 << SAMPLE_TICKS_LOG2;
#[derive(Clone, Debug, Tree)]
pub struct Settings {
lockin: Lockin,
net: NetSettings,
}
impl stabilizer::settings::AppSettings for Settings {
fn new(net: NetSettings) -> Self {
Self {
net,
lockin: Lockin::default(),
}
}
fn net(&self) -> &NetSettings {
&self.net
}
}
impl serial_settings::Settings for Settings {
fn reset(&mut self) {
*self = Self {
lockin: Lockin::default(),
net: NetSettings::new(self.net.mac),
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
enum Conf {
Magnitude,
Phase,
ReferenceFrequency,
LogPower,
InPhase,
Quadrature,
Modulation,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
enum LockinMode {
Internal,
External,
}
#[derive(Clone, Debug, Tree)]
pub struct Lockin {
afe: [Leaf<Gain>; 2],
lockin_mode: Leaf<LockinMode>,
pll_tc: [Leaf<u32>; 2],
lockin_k: Leaf<<Lowpass<2> as Filter>::Config>,
lockin_harmonic: Leaf<i32>,
lockin_phase: Leaf<i32>,
output_conf: [Leaf<Conf>; 2],
telemetry_period: Leaf<u16>,
stream: Leaf<StreamTarget>,
}
impl Default for Lockin {
fn default() -> Self {
Self {
afe: [Gain::G1.into(); 2],
lockin_mode: LockinMode::External.into(),
pll_tc: [21.into(), 21.into()],
lockin_k: [0x8_0000, -0x400_0000].into(), lockin_harmonic: (-1).into(), lockin_phase: 0.into(),
output_conf: [Conf::InPhase.into(), Conf::Quadrature.into()],
telemetry_period: 10.into(),
stream: Default::default(),
}
}
}
#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, dispatchers=[DCMI, JPEG, SDMMC])]
mod app {
use super::*;
#[shared]
struct Shared {
usb: UsbDevice,
network: NetworkUsers<Lockin, 2>,
settings: Settings,
active_settings: Lockin,
telemetry: TelemetryBuffer,
}
#[local]
struct Local {
usb_terminal: SerialTerminal<Settings, 3>,
sampling_timer: SamplingTimer,
digital_inputs: (DigitalInput0, DigitalInput1),
timestamper: InputStamper,
afes: (AFE0, AFE1),
adcs: (Adc0Input, Adc1Input),
dacs: (Dac0Output, Dac1Output),
pll: RPLL,
lockin: idsp::Lockin<Repeat<2, Lowpass<2>>>,
source: signal_generator::SignalGenerator,
generator: FrameGenerator,
cpu_temp_sensor: stabilizer::hardware::cpu_temp_sensor::CpuTempSensor,
}
#[init]
fn init(c: init::Context) -> (Shared, Local) {
let clock = SystemTimer::new(|| Systick::now().ticks());
let (mut stabilizer, _pounder) = hardware::setup::setup::<Settings, 3>(
c.core,
c.device,
clock,
BATCH_SIZE,
SAMPLE_TICKS,
);
let mut network = NetworkUsers::new(
stabilizer.net.stack,
stabilizer.net.phy,
clock,
env!("CARGO_BIN_NAME"),
&stabilizer.settings.net,
stabilizer.metadata,
);
let generator = network.configure_streaming(StreamFormat::AdcDacData);
let shared = Shared {
network,
usb: stabilizer.usb,
telemetry: TelemetryBuffer::default(),
active_settings: stabilizer.settings.lockin.clone(),
settings: stabilizer.settings,
};
let signal_config = signal_generator::Config {
phase_increment: [1 << (32 - BATCH_SIZE_LOG2); 2],
amplitude: DacCode::try_from(1.0).unwrap().into(),
signal: signal_generator::Signal::Cosine,
phase_offset: 0,
};
let mut local = Local {
usb_terminal: stabilizer.usb_serial,
sampling_timer: stabilizer.adc_dac_timer,
digital_inputs: stabilizer.digital_inputs,
afes: stabilizer.afes,
adcs: stabilizer.adcs,
dacs: stabilizer.dacs,
timestamper: stabilizer.timestamper,
pll: RPLL::new(SAMPLE_TICKS_LOG2 + BATCH_SIZE_LOG2),
lockin: idsp::Lockin::default(),
source: signal_generator::SignalGenerator::new(signal_config),
generator,
cpu_temp_sensor: stabilizer.temperature_sensor,
};
local.adcs.0.start();
local.adcs.1.start();
local.dacs.0.start();
local.dacs.1.start();
settings_update::spawn().unwrap();
telemetry::spawn().unwrap();
ethernet_link::spawn().unwrap();
start::spawn().unwrap();
usb::spawn().unwrap();
stabilizer.timestamp_timer.start();
local.timestamper.start();
(shared, local)
}
#[task(priority = 1, local=[sampling_timer])]
async fn start(c: start::Context) {
Systick::delay(100.millis()).await;
c.local.sampling_timer.start();
}
#[task(binds=DMA1_STR4, shared=[active_settings, telemetry], local=[adcs, dacs, lockin, timestamper, pll, generator, source], priority=3)]
#[link_section = ".itcm.process"]
fn process(c: process::Context) {
let process::SharedResources {
active_settings,
telemetry,
..
} = c.shared;
let process::LocalResources {
timestamper,
adcs: (adc0, adc1),
dacs: (dac0, dac1),
pll,
lockin,
source,
generator,
..
} = c.local;
(active_settings, telemetry).lock(|settings, telemetry| {
let (reference_phase, reference_frequency) =
match *settings.lockin_mode {
LockinMode::External => {
let timestamp =
timestamper.latest_timestamp().unwrap_or(None); let (pll_phase, pll_frequency) = pll.update(
timestamp.map(|t| t as i32),
*settings.pll_tc[0],
*settings.pll_tc[1],
);
(pll_phase, (pll_frequency >> BATCH_SIZE_LOG2) as i32)
}
LockinMode::Internal => {
(1i32 << 30, 1i32 << (32 - BATCH_SIZE_LOG2))
}
};
let sample_frequency =
reference_frequency.wrapping_mul(*settings.lockin_harmonic);
let sample_phase = settings.lockin_phase.wrapping_add(
reference_phase.wrapping_mul(*settings.lockin_harmonic),
);
(adc0, adc1, dac0, dac1).lock(|adc0, adc1, dac0, dac1| {
let adc_samples = [adc0, adc1];
let mut dac_samples = [dac0, dac1];
fence(Ordering::SeqCst);
let output: Complex<i32> = adc_samples[0]
.iter()
.zip(Accu::new(sample_phase, sample_frequency))
.map(|(&sample, phase)| {
let s = (sample as i16 as i32) << 16;
lockin.update(s, phase, &settings.lockin_k)
})
.last()
.unwrap()
* 2;
for (channel, samples) in dac_samples.iter_mut().enumerate() {
for sample in samples.iter_mut() {
let value = match *settings.output_conf[channel] {
Conf::Magnitude => output.abs_sqr() as i32 >> 16,
Conf::Phase => output.arg() >> 16,
Conf::LogPower => output.log2() << 8,
Conf::ReferenceFrequency => {
reference_frequency >> 16
}
Conf::InPhase => output.re >> 16,
Conf::Quadrature => output.im >> 16,
Conf::Modulation => source.next().unwrap() as i32,
};
*sample = DacCode::from(value as i16).0;
}
}
const N: usize = BATCH_SIZE * size_of::<i16>()
/ size_of::<MaybeUninit<u8>>();
generator.add(|buf| {
for (data, buf) in adc_samples
.iter()
.chain(dac_samples.iter())
.zip(buf.chunks_exact_mut(N))
{
let data = unsafe {
core::slice::from_raw_parts(
data.as_ptr() as *const MaybeUninit<u8>,
N,
)
};
buf.copy_from_slice(data)
}
N * 4
});
telemetry.adcs =
[AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])];
telemetry.dacs =
[DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])];
fence(Ordering::SeqCst);
});
});
}
#[idle(shared=[settings, network, usb])]
fn idle(mut c: idle::Context) -> ! {
loop {
match (&mut c.shared.network, &mut c.shared.settings)
.lock(|net, settings| net.update(&mut settings.lockin))
{
NetworkState::SettingsChanged => {
settings_update::spawn().unwrap()
}
NetworkState::Updated => {}
NetworkState::NoChange => {
if c.shared.usb.lock(|usb| {
usb.state()
== usb_device::device::UsbDeviceState::Suspend
}) {
cortex_m::asm::wfi();
}
}
}
}
}
#[task(priority = 1, local=[afes], shared=[network, settings, active_settings])]
async fn settings_update(mut c: settings_update::Context) {
c.shared.settings.lock(|settings| {
c.local.afes.0.set_gain(*settings.lockin.afe[0]);
c.local.afes.1.set_gain(*settings.lockin.afe[1]);
c.shared
.network
.lock(|net| net.direct_stream(*settings.lockin.stream));
c.shared
.active_settings
.lock(|current| *current = settings.lockin.clone());
});
}
#[task(priority = 1, local=[digital_inputs, cpu_temp_sensor], shared=[network, settings, telemetry])]
async fn telemetry(mut c: telemetry::Context) {
loop {
let mut telemetry =
c.shared.telemetry.lock(|telemetry| telemetry.clone());
telemetry.digital_inputs = [
c.local.digital_inputs.0.is_high(),
c.local.digital_inputs.1.is_high(),
];
let (gains, telemetry_period) =
c.shared.settings.lock(|settings| {
(settings.lockin.afe, *settings.lockin.telemetry_period)
});
c.shared.network.lock(|net| {
net.telemetry.publish(&telemetry.finalize(
*gains[0],
*gains[1],
c.local.cpu_temp_sensor.get_temperature().unwrap(),
))
});
Systick::delay((telemetry_period as u32).secs()).await;
}
}
#[task(priority = 1, shared=[usb, settings], local=[usb_terminal])]
async fn usb(mut c: usb::Context) {
loop {
c.shared.usb.lock(|usb| {
usb.poll(&mut [c
.local
.usb_terminal
.interface_mut()
.inner_mut()]);
});
c.shared.settings.lock(|settings| {
if c.local.usb_terminal.poll(settings).unwrap() {
settings_update::spawn().unwrap()
}
});
Systick::delay(10.millis()).await;
}
}
#[task(priority = 1, shared=[network])]
async fn ethernet_link(mut c: ethernet_link::Context) {
loop {
c.shared.network.lock(|net| net.processor.handle_link());
Systick::delay(1.secs()).await;
}
}
#[task(binds = ETH, priority = 1)]
fn eth(_: eth::Context) {
unsafe { hal::ethernet::interrupt_handler() }
}
}