From 25f8363e54430a47b45a0536eccdf138f45f83a5 Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 18 Sep 2020 12:25:39 +0800 Subject: [PATCH] dds: add RAM mode --- Cargo.toml | 13 ++++- src/dds.rs | 158 +++++++++++++++++++++++++++++++++++++++-------------- src/lib.rs | 64 ++++++---------------- 3 files changed, 147 insertions(+), 88 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5a858d5..043890b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ embedded-hal = "0.2.4" stm32h7xx-hal = {version = "0.7.1", features = [ "stm32h743v", "rt", "unproven", "ethernet", "phy_lan8742a" ] } smoltcp = { version = "0.6.0", default-features = false, features = [ "ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp", "log" ] } nb = "1.0.0" -libm = "0.2.0" embedded-nal = "0.1.0" minimq = { git = "https://github.com/quartiq/minimq.git", branch = "master" } @@ -33,6 +32,18 @@ branch = "issue-4" default-features = false features = [ "build-info", "unit-frequency", "unit-angle" ] +# [dependencies.uom] +# version = "0.29.0" +# default-features = false +# features = [ +# "autoconvert", +# "usize", "u8", "u16", "u32", "u64", +# "isize", "i8", "i16", "i32", "i64", +# "f32", "f64", +# "si", +# "try-from" +# ] + # Use below SCPI dependency when need to modify SCPI fork offline # [dependencies.scpi] # path = "../scpi-fork/scpi" diff --git a/src/dds.rs b/src/dds.rs index 4cb51d5..12cd08b 100644 --- a/src/dds.rs +++ b/src/dds.rs @@ -1,7 +1,8 @@ use embedded_hal::blocking::spi::Transfer; use crate::Error; use core::mem::size_of; -use libm::round; +use core::convert::TryInto; +use arrayvec::ArrayVec; /* * Bitmask for all configurations (Order: CFR3, CFR2, CFR1) @@ -63,6 +64,23 @@ construct_bitmask!(DDSCFRMask; u32; const WRITE_MASK :u8 = 0x00; const READ_MASK :u8 = 0x80; +#[derive(Clone)] +pub enum RAMDestination { + Frequency = 0, + Phase = 1, + Amplitude = 2, + Polar = 3, +} + +#[derive(Clone)] +pub enum RAMOperationMode { + DirectSwitch = 0, + RampUp = 1, + BidirectionalRamp = 2, + ContinuousBidirectionalRamp = 3, + ContinuousRecirculate = 4, +} + pub struct DDS { spi: SPI, f_ref_clk: f64, @@ -143,7 +161,7 @@ where // Get a divider let divider = (f_sys_clk / self.f_ref_clk) as u64; // Reject extreme divider values. However, accept no frequency division - if ((divider > 127 || divider < 12) && divider != 1) { + if (divider > 127 || divider < 12) && divider != 1 { // panic!("Invalid divider value for PLL!"); return Err(Error::DDSCLKError); } @@ -345,16 +363,10 @@ where } else { ((resolutions[2] as f64) * amp_scale_factor) as u16 }; + // Setup configuration registers before writing single tone register - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 0), - (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), - (DDSCFRMask::OSK_ENABLE, 0), - (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), - ])?; + self.enable_single_tone_configuration()?; + // Transfer single tone profile data self.write_register(0x0E + profile, &mut [ ((asf >> 8 ) & 0xFF) as u8, @@ -376,15 +388,7 @@ where pub fn set_single_tone_profile_frequency(&mut self, profile: u8, f_out: f64) -> Result<(), Error> { // Setup configuration registers before writing single tone register - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 0), - (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), - (DDSCFRMask::OSK_ENABLE, 0), - (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), - ])?; + self.enable_single_tone_configuration()?; // Calculate frequency tuning work (FTW) let f_res: u64 = 1 << 32; @@ -412,15 +416,7 @@ where pub fn set_single_tone_profile_phase(&mut self, profile: u8, phase_offset: f64) -> Result<(), Error> { // Setup configuration registers before writing single tone register - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 0), - (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), - (DDSCFRMask::OSK_ENABLE, 0), - (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), - ])?; + self.enable_single_tone_configuration()?; // Calculate phase offset work (POW) let phase_res: u64 = 1 << 16; @@ -446,15 +442,7 @@ where pub fn set_single_tone_profile_amplitude(&mut self, profile: u8, amp_scale_factor: f64) -> Result<(), Error> { // Setup configuration registers before writing single tone register - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 0), - (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), - (DDSCFRMask::OSK_ENABLE, 0), - (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), - ])?; + self.enable_single_tone_configuration()?; // Calculate amplitude_scale_factor (ASF) let amp_res: u64 = 1 << 14; @@ -476,6 +464,96 @@ where self.write_register(0x0E + profile, &mut register) } + // Helper function to switch into single tone mode + // Need to setup configuration registers before writing single tone register + fn enable_single_tone_configuration(&mut self) -> Result<(), Error> { + + self.set_configurations(&mut [ + (DDSCFRMask::RAM_ENABLE, 0), + (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), + (DDSCFRMask::OSK_ENABLE, 0), + (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), + ])?; + self.set_configurations(&mut [ + (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), + ]) + } + + // Helper function to switch into RAM mode + // Need to setup configuration registers before writing into RAM profile register + fn enable_ram_configuration(&mut self, ram_dst: RAMDestination) -> Result<(), Error> { + self.set_configurations(&mut [ + (DDSCFRMask::RAM_ENABLE, 1), + (DDSCFRMask::RAM_PLAYBACK_DST, ram_dst as u32), + ]) + } + + // Helper function to switch out of RAM mode + // Need to setup configuration registers before writing into RAM profile register + fn disable_ram_configuration(&mut self) -> Result<(), Error> { + self.set_configurations(&mut [ + (DDSCFRMask::RAM_ENABLE, 0), + ]) + } + + /* + * Configure a RAM mode profile + * + */ + pub fn set_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + ram_dst: RAMDestination, no_dwell_high: bool, zero_crossing: bool, + op_mode: RAMOperationMode, playback_rate: f64, data: &[u8] + ) -> Result<(), Error> { + + // Check the legality of this setup + assert!(profile < 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(data.len() as u16, (end_addr - start_addr + 1) * 4); + + // Calculate address step rate, and check legality + let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64; + if (step_rate == 0 || step_rate > 0xFFFF) { + return Err(Error::ParameterError); + } + + // Before setting up RAM, disable RAM_ENABLE + self.enable_ram_configuration(ram_dst.clone())?; + + // Write a RAM profile, but include all data in RAM + self.write_register(0x0E + profile, &mut [ + 0x00, + ((step_rate >> 8) & 0xFF).try_into().unwrap(), + ((step_rate >> 0) & 0xFF).try_into().unwrap(), + ((end_addr >> 2) & 0xFF).try_into().unwrap(), + ((end_addr & 0x3) << 6).try_into().unwrap(), + ((start_addr >> 2) & 0xFF).try_into().unwrap(), + ((start_addr & 0x3) << 6).try_into().unwrap(), + ((no_dwell_high as u8) << 5) | ((zero_crossing as u8) << 3) | (op_mode as u8) + ])?; + + // Temporarily disable RAM mode while accessing into RAM + self.disable_ram_configuration(); + self.write_ram(data)?; + + // Properly configure start_addr and end_addr + self.enable_ram_configuration(ram_dst) + + } + + // Helper function to write data in RAM + // Need address range for data size check + fn write_ram(&mut self, data: &[u8]) -> Result<(), Error> { + let mut vec: ArrayVec<[u8; 8192]> = ArrayVec::new(); + vec.try_push(0x16) + .map_err(|_| Error::DDSRAMError)?; + vec.try_extend_from_slice(data) + .map_err(|_| Error::DDSRAMError)?; + let mut data_slice = vec.as_mut_slice(); + self.spi.transfer(&mut data_slice) + .map(|_| ()) + .map_err(Error::SPI) + } /* * Test method for DDS. @@ -497,9 +575,9 @@ where } Ok(error_count) } - } +// Strong check for bytes passed to a register macro_rules! impl_register_io { ($($reg_addr: expr, $reg_byte_size: expr),+) => { impl DDS @@ -572,6 +650,4 @@ impl_register_io!( 0x13, 8, 0x14, 8, 0x15, 8 - // RAM works in other way - // 0x16, 4 ); diff --git a/src/lib.rs b/src/lib.rs index 7c9c2a0..3d35399 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,29 +1,14 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(str_strip)] 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; @@ -36,11 +21,12 @@ use crate::attenuator::Attenuator; pub mod dds; use crate::dds::DDS; -pub mod scpi; - +// pub mod scpi; pub mod translation; - pub mod nal_tcp_client; +pub mod flash; + +pub mod mqtt_mux; /* * Enum for structuring error @@ -55,7 +41,9 @@ pub enum Error { DDSError, ConfigRegisterError, DDSCLKError, + DDSRAMError, ParameterError, + MqttCommandError, } #[derive(Debug)] @@ -156,28 +144,12 @@ where } } -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, frequency: f64) -> Result<(), Self::Error>; - fn set_clock_division(&mut self, division: u8) -> Result<(), Self::Error>; - fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Self::Error>; - fn set_profile(&mut self, profile: u8) -> Result<(), Self::Error>; - fn set_channel_single_tone_profile(&mut self, channel: u8, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Self::Error>; - fn set_channel_single_tone_profile_frequency(&mut self, channel: u8, profile: u8, frequency: f64)-> Result<(), Self::Error>; - fn set_channel_single_tone_profile_phase(&mut self, channel: u8, profile: u8, phase: f64)-> Result<(), Self::Error>; - fn set_channel_single_tone_profile_amplitude(&mut self, channel: u8, profile: u8, amplitude: f64)-> Result<(), Self::Error>; - fn set_channel_sys_clk(&mut self, channel: u8, sys_clk: f64) -> Result<(), Self::Error>; -} - -impl UrukulTraits for Urukul +impl Urukul where SPI: Transfer { - type Error = Error; - fn get_channel_switch_status(&mut self, channel: u32) -> Result { + fn get_channel_switch_status(&mut self, channel: u32) -> Result> { if channel < 4 { self.config_register.get_status(StatusMask::RF_SW).map(|val| (val & (1 << channel)) != 0) } else { @@ -185,7 +157,7 @@ where } } - fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Self::Error> { + fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Error> { if channel < 4 { let prev = u32::from(self.config_register.get_status(StatusMask::RF_SW)?); let next = { @@ -203,7 +175,7 @@ where } } - fn set_clock_source(&mut self, source: ClockSource, frequency: f64) -> Result<(), Self::Error> { + fn set_clock_source(&mut self, source: ClockSource, frequency: f64) -> Result<(), Error> { // Change clock source through configuration register match source { ClockSource::OSC => self.config_register.set_configurations(&mut [ @@ -232,7 +204,7 @@ where Ok(()) } - fn set_clock_division(&mut self, division: u8) -> Result<(), Self::Error> { + fn set_clock_division(&mut self, division: u8) -> Result<(), Error> { match division { 1 => self.config_register.set_configurations(&mut [ (CFGMask::DIV, 1), @@ -256,33 +228,33 @@ where Ok(()) } - fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Self::Error> { + fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error> { self.attenuator.set_channel_attenuation(channel, attenuation) } - fn set_profile(&mut self, profile: u8) -> Result<(), Self::Error> { + fn set_profile(&mut self, profile: u8) -> Result<(), Error> { self.config_register.set_configurations(&mut [ (CFGMask::PROFILE, profile.into()) ]).map(|_| ()) } - fn set_channel_single_tone_profile(&mut self, channel: u8, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Self::Error> { + fn set_channel_single_tone_profile(&mut self, channel: u8, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Error> { self.dds[usize::from(channel)].set_single_tone_profile(profile, frequency, phase, amplitude) } - fn set_channel_single_tone_profile_frequency(&mut self, channel: u8, profile: u8, frequency: f64)-> Result<(), Self::Error> { + fn set_channel_single_tone_profile_frequency(&mut self, channel: u8, profile: u8, frequency: f64)-> Result<(), Error> { self.dds[usize::from(channel)].set_single_tone_profile_frequency(profile, frequency) } - fn set_channel_single_tone_profile_phase(&mut self, channel: u8, profile: u8, phase: f64)-> Result<(), Self::Error> { + fn set_channel_single_tone_profile_phase(&mut self, channel: u8, profile: u8, phase: f64)-> Result<(), Error> { self.dds[usize::from(channel)].set_single_tone_profile_phase(profile, phase) } - fn set_channel_single_tone_profile_amplitude(&mut self, channel: u8, profile: u8, amplitude: f64)-> Result<(), Self::Error> { + fn set_channel_single_tone_profile_amplitude(&mut self, channel: u8, profile: u8, amplitude: f64)-> Result<(), Error> { self.dds[usize::from(channel)].set_single_tone_profile_amplitude(profile, amplitude) } - fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Self::Error> { + fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Error> { self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk) } }