#![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; use cortex_m_semihosting::hprintln; #[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, } /* * 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>; } 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 switch_on(&mut self, channel: u32) -> Result<(), Self::Error>{ // if channel < 16 { // let switch_set = channel | u32::from(self.config_register.get_status(StatusMask::RF_SW)?); // match self.config_register.set_configurations(&mut [ // (CFGMask::RF_SW, switch_set), // ]) { // Ok(_) => Ok(()), // Err(_e) => Err(_e), // } // } else { // Err(Error::ParameterError) // } // } } // /* // * Struct for a better Urukul master device // */ // pub struct BetterUrukul<'a, SPI, CS0, CS1, CS2, GPIO> { // cpld: CPLD, // parts: Option, SPI, CS0, CS1, CS2, GPIO>>, // config_register: Option, SPI, CS0, CS1, CS2, GPIO>>>, // attenuator: Option, SPI, CS0, CS1, CS2, GPIO>>>, // dds: [Option, SPI, CS0, CS1, CS2, GPIO>>>; 4], // } // impl<'a, SPI, CS0, CS1, CS2, GPIO> BetterUrukul<'a, SPI, CS0, CS1, CS2, GPIO> // where // SPI: Transfer, // CS0: OutputPin, // CS1: OutputPin, // CS2: OutputPin, // GPIO: OutputPin, // { // pub fn new(spi: SPI, chip_select: (CS0, CS1, CS2), io_update: GPIO) -> Self { // // let switch = CPLD::new(spi, chip_select, io_update); // // let parts = switch.split(); // // Construct Urukul // BetterUrukul { // cpld: CPLD::new(spi, chip_select, io_update), // // parts: CPLD::new(spi, chip_select, io_update).split(), // // config_register: ConfigRegister::new(self.parts.spi1), // // attenuator: Attenuator::new(self.parts.spi2), // // dds: [ // // DDS::new(self.parts.spi4, f_ref_clks[1]), // // DDS::new(self.parts.spi5, f_ref_clks[1]), // // DDS::new(self.parts.spi6, f_ref_clks[2]), // // DDS::new(self.parts.spi7, f_ref_clks[3]), // // ], // parts: None, // config_register: None, // attenuator: None, // dds: [None, None, None, None], // } // } // pub fn init(&'a mut self, f_ref_clks:[u64; 4]) { // self.parts = Some(self.cpld.split()); // self.config_register = Some(ConfigRegister::new(self.parts.unwrap().spi1)); // self.attenuator = Some(Attenuator::new(self.parts.unwrap().spi2)); // self.dds[0] = Some(DDS::new(self.parts.unwrap().spi4, f_ref_clks[0])); // self.dds[1] = Some(DDS::new(self.parts.unwrap().spi5, f_ref_clks[1])); // self.dds[2] = Some(DDS::new(self.parts.unwrap().spi6, f_ref_clks[2])); // self.dds[3] = Some(DDS::new(self.parts.unwrap().spi7, f_ref_clks[3])); // } // }