#![no_std] #![feature(generic_associated_types)] extern crate embedded_hal; use embedded_hal::{ digital::v2::OutputPin, blocking::spi::Transfer, }; use core::{ cell, marker::PhantomData, }; use cortex_m; #[macro_use] pub mod bitmask_macro; pub mod spi_slave; use crate::spi_slave::{ Parts, SPISlave, }; pub mod cpld; use crate::cpld::CPLD; use crate::cpld::DoOnGetRefMutData; pub mod config_register; use crate::config_register::ConfigRegister; use crate::config_register::CFGMask; use crate::config_register::StatusMask; pub mod attenuator; use crate::attenuator::Attenuator; pub mod dds; use crate::dds::DDS; pub mod scpi; pub mod nal_tcp_client; /* * Enum for structuring error */ #[derive(Debug)] pub enum Error { SPI(E), CSError, GetRefMutDataError, AttenuatorError, IOUpdateError, DDSError, ConfigRegisterError, DDSCLKError, ParameterError, } pub enum ClockSource { OSC, SMA, MMCX, } /* * Struct for Urukul master device */ pub struct Urukul { config_register: ConfigRegister, attenuator: Attenuator, dds: [DDS; 4], } impl Urukul where SPI: Transfer, { /* * Master constructor for the entire Urukul device * Supply 7 SPI channels to Urukul and 4 reference clock frequencies */ pub fn new(spi1: SPI, spi2: SPI, spi3: SPI, spi4: SPI, spi5: SPI, spi6: SPI, spi7: SPI, f_ref_clks: [u64; 4]) -> Self { // Construct Urukul Urukul { config_register: ConfigRegister::new(spi1), attenuator: Attenuator::new(spi2), dds: [ DDS::new(spi4, f_ref_clks[1]), DDS::new(spi5, f_ref_clks[1]), DDS::new(spi6, f_ref_clks[2]), DDS::new(spi7, f_ref_clks[3]), ], } } /* * Reset method. To be invoked by initialization and manual reset. * Only Urukul struct provides reset method. * DDS reset is controlled by Urukul (RST). * Attenuators only have shift register reset, which does not affect its data * CPLD only has a "all-zero" default state. */ pub fn reset(&mut self) -> Result<(), Error> { // Reset DDS and attenuators self.config_register.set_configurations(&mut [ (CFGMask::RST, 1), (CFGMask::IO_RST, 1), (CFGMask::IO_UPDATE, 0) ])?; // Set 0 to all fields on configuration register. self.config_register.set_configurations(&mut [ (CFGMask::RF_SW, 0), (CFGMask::LED, 0), (CFGMask::PROFILE, 0), (CFGMask::IO_UPDATE, 0), (CFGMask::MASK_NU, 0), (CFGMask::CLK_SEL0, 0), (CFGMask::SYNC_SEL, 0), (CFGMask::RST, 0), (CFGMask::IO_RST, 0), (CFGMask::CLK_SEL1, 0), (CFGMask::DIV, 0), ])?; // Init all DDS chips. Configure SDIO as input only. for chip_no in 0..4 { self.dds[chip_no].init()?; } // Clock tree reset. CPLD divides clock frequency by 4 by default. for chip_no in 0..4 { self.dds[chip_no].set_ref_clk_frequency(25_000_000)?; } Ok(()) } /* * Test method fo Urukul. * Return the number of test failed. */ pub fn test(&mut self) -> Result> { let mut count = self.config_register.test()?; count += self.attenuator.test()?; for chip_no in 0..4 { count += self.dds[chip_no].test()?; } Ok(count) } } pub trait UrukulTraits { type Error; fn get_channel_switch_status(&mut self, channel: u32) -> Result; fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Self::Error>; fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Self::Error>; fn set_clock_division(&mut self, division: u8) -> Result<(), Self::Error>; } impl UrukulTraits for Urukul where SPI: Transfer { type Error = Error; fn get_channel_switch_status(&mut self, channel: u32) -> Result { if channel < 4 { match self.config_register.get_status(StatusMask::RF_SW) { Ok(val) => Ok((val & (1 << channel)) != 0), Err(_e) => Err(_e), } } else { Err(Error::ParameterError) } } fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Self::Error> { if channel < 4 { let prev = u32::from(self.config_register.get_status(StatusMask::RF_SW)?); let next = { if status { prev | (1 << channel) } else { prev & (!(1 << channel)) } }; match self.config_register.set_configurations(&mut [ (CFGMask::RF_SW, next), ]) { Ok(_) => Ok(()), Err(_e) => Err(_e), } } else { Err(Error::ParameterError) } } fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Self::Error> { let result = match source { ClockSource::OSC => self.config_register.set_configurations(&mut [ (CFGMask::CLK_SEL0, 0), (CFGMask::CLK_SEL1, 0), ]), ClockSource::MMCX => self.config_register.set_configurations(&mut [ (CFGMask::CLK_SEL0, 0), (CFGMask::CLK_SEL1, 1), ]), ClockSource::SMA => self.config_register.set_configurations(&mut [ (CFGMask::CLK_SEL0, 1), ]), }; match result { Ok(_) => Ok(()), Err(_e) => Err(_e), } } fn set_clock_division(&mut self, division: u8) -> Result<(), Self::Error> { let result = match division { 1 => self.config_register.set_configurations(&mut [ (CFGMask::DIV, 1), ]), 2 => self.config_register.set_configurations(&mut [ (CFGMask::DIV, 2), ]), 4 => self.config_register.set_configurations(&mut [ (CFGMask::DIV, 3), ]), _ => Err(Error::ParameterError), }; match result { Ok(_) => Ok(()), Err(_e) => Err(_e), } } }