dds: more clk ctrl; scpi: sys_clk ctrl
This commit is contained in:
parent
331d1ff86f
commit
e1abf87351
|
@ -18,7 +18,7 @@ libm = "0.2.0"
|
|||
embedded-nal = "0.1.0"
|
||||
minimq = { git = "https://github.com/quartiq/minimq.git", branch = "master" }
|
||||
heapless = "0.5.5"
|
||||
arrayvec = { version = "0.5.1", default-features = false, features = ["array-sizes-33-128", "array-sizes-129-255"] }
|
||||
arrayvec = { version = "0.5.1", default-features = false, features = [ "array-sizes-33-128", "array-sizes-129-255" ] }
|
||||
|
||||
# Logging and Panicking
|
||||
panic-itm = "0.4.1"
|
||||
|
@ -31,7 +31,7 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
|||
git = "https://github.com/occheung/scpi-rs"
|
||||
branch = "issue-4"
|
||||
default-features = false
|
||||
features = [ "build-info", "unit-frequency" ]
|
||||
features = [ "build-info", "unit-frequency", "unit-angle" ]
|
||||
|
||||
# Use below SCPI dependency when need to modify SCPI fork offline
|
||||
# [dependencies.scpi]
|
||||
|
|
|
@ -47,13 +47,15 @@ use firmware::{
|
|||
Channel1SwitchCommand,
|
||||
Channel2SwitchCommand,
|
||||
Channel3SwitchCommand,
|
||||
Channel0SystemClockCommand,
|
||||
Channel0AttenuationCommand,
|
||||
Channel1AttenuationCommand,
|
||||
Channel2AttenuationCommand,
|
||||
Channel3AttenuationCommand,
|
||||
ClockSourceCommand,
|
||||
ClockDivisionCommand,
|
||||
ProfileCommand
|
||||
ProfileCommand,
|
||||
Channel0Profile0Singletone
|
||||
},
|
||||
Urukul, scpi_root, recursive_scpi_tree, scpi_tree
|
||||
};
|
||||
|
@ -208,8 +210,7 @@ fn main() -> ! {
|
|||
let switch = CPLD::new(spi, (cs0, cs1, cs2), io_update);
|
||||
let parts = switch.split();
|
||||
let mut urukul = Urukul::new(
|
||||
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7,
|
||||
[25_000_000.0, 25_000_000.0, 25_000_000.0, 25_000_000.0]
|
||||
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7
|
||||
);
|
||||
|
||||
// Setup ethernet pins
|
||||
|
|
|
@ -44,6 +44,7 @@ use firmware::{
|
|||
ClockSourceCommand,
|
||||
ClockDivisionCommand,
|
||||
ProfileCommand,
|
||||
Channel0Profile0Singletone
|
||||
},
|
||||
Urukul, scpi_root, recursive_scpi_tree, scpi_tree
|
||||
};
|
||||
|
@ -209,8 +210,7 @@ fn main() -> ! {
|
|||
let parts = cpld.split();
|
||||
|
||||
let mut urukul = Urukul::new(
|
||||
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7,
|
||||
[25_000_000.0, 25_000_000.0, 25_000_000.0, 25_000_000.0]
|
||||
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7
|
||||
);
|
||||
|
||||
cp.SCB.invalidate_icache();
|
||||
|
|
20
src/dds.rs
20
src/dds.rs
|
@ -216,6 +216,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// Change sys_clk frequency, method to be determined
|
||||
pub fn set_sys_clk_frequency(&mut self, f_sys_clk: f64) -> Result<(), Error<E>> {
|
||||
// If f_sys_clk is exactly the same as f_ref_clk, then invoke enable_normal_ref_clk
|
||||
if f_sys_clk == self.f_ref_clk {
|
||||
self.enable_normal_ref_clk()
|
||||
}
|
||||
// Otherwise, if the requested sys_clk is half of ref_clk, invoke enable_divided_ref_clk
|
||||
else if f_sys_clk == (self.f_ref_clk / 2.0) {
|
||||
self.enable_divided_ref_clk()
|
||||
}
|
||||
// Finally, try enabling PLL
|
||||
else {
|
||||
self.enable_pll(f_sys_clk)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn get_VCO_no(&mut self, f_sys_clk: f64, divider: u8) -> Result<u8, Error<E>> {
|
||||
// Select a VCO
|
||||
|
@ -310,8 +326,8 @@ where
|
|||
|
||||
/*
|
||||
* Set a single tone profile
|
||||
* Phase: Expressed in positive degree
|
||||
* Frequency: Must be integer
|
||||
* Phase: Expressed in positive degree, i.e. [0.0, 360.0)
|
||||
* Frequency: Must be non-negative
|
||||
* Amplitude: In a scale from 0 to 1, taking float
|
||||
*/
|
||||
pub fn set_single_tone_profile(&mut self, profile: u8, f_out: f64, phase_offset: f64, amp_scale_factor: f64) -> Result<(), Error<E>> {
|
||||
|
|
50
src/lib.rs
50
src/lib.rs
|
@ -72,6 +72,7 @@ pub struct Urukul<SPI> {
|
|||
config_register: ConfigRegister<SPI>,
|
||||
attenuator: Attenuator<SPI>,
|
||||
dds: [DDS<SPI>; 4],
|
||||
f_master_clk: f64,
|
||||
}
|
||||
|
||||
impl<SPI, E> Urukul<SPI>
|
||||
|
@ -82,17 +83,21 @@ where
|
|||
* 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: [f64; 4]) -> Self {
|
||||
pub fn new(spi1: SPI, spi2: SPI, spi3: SPI, spi4: SPI, spi5: SPI, spi6: SPI, spi7: SPI) -> Self {
|
||||
// Construct Urukul
|
||||
Urukul {
|
||||
config_register: ConfigRegister::new(spi1),
|
||||
attenuator: Attenuator::new(spi2),
|
||||
// Create 4 DDS instances with fixed 25MHz clock
|
||||
// Counter-intuitive to assign urukul clock before having a urukul
|
||||
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]),
|
||||
DDS::new(spi4, 25_000_000.0),
|
||||
DDS::new(spi5, 25_000_000.0),
|
||||
DDS::new(spi6, 25_000_000.0),
|
||||
DDS::new(spi7, 25_000_000.0),
|
||||
],
|
||||
// Default clock selection: OSC, fixed 100MHz speed
|
||||
f_master_clk: 100_000_000.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,10 +133,11 @@ where
|
|||
for chip_no in 0..4 {
|
||||
self.dds[chip_no].init()?;
|
||||
}
|
||||
|
||||
// Clock tree reset. CPLD divides clock frequency by 4 by default.
|
||||
// Clock tree reset. OSC clock source by default
|
||||
self.f_master_clk = 100_000_000.0;
|
||||
// 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.0)?;
|
||||
self.dds[chip_no].set_ref_clk_frequency(self.f_master_clk / 4.0)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -158,6 +164,8 @@ pub trait UrukulTraits {
|
|||
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_sys_clk(&mut self, channel: u8, sys_clk: f64) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
impl<SPI, E> UrukulTraits for Urukul<SPI>
|
||||
|
@ -207,13 +215,16 @@ where
|
|||
(CFGMask::CLK_SEL0, 1),
|
||||
]),
|
||||
}?;
|
||||
|
||||
// Save the new master clock frequency
|
||||
self.f_master_clk = frequency;
|
||||
|
||||
// Calculate reference clock frequency after clock division from configuration register
|
||||
let frequency = frequency / (self.config_register.get_configuration(CFGMask::DIV) as f64);
|
||||
let f_ref_clk = self.f_master_clk / (self.config_register.get_configuration(CFGMask::DIV) as f64);
|
||||
|
||||
// Update all DDS chips on reference clock frequency
|
||||
for dds_channel in 0..4 {
|
||||
self.dds[dds_channel].set_ref_clk_frequency(frequency)?;
|
||||
self.dds[dds_channel].set_ref_clk_frequency(f_ref_clk)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -230,7 +241,16 @@ where
|
|||
(CFGMask::DIV, 3),
|
||||
]),
|
||||
_ => Err(Error::ParameterError),
|
||||
}.map(|_| ())
|
||||
}?;
|
||||
|
||||
// Calculate reference clock frequency after clock division from configuration register
|
||||
let f_ref_clk = self.f_master_clk / (division as f64);
|
||||
|
||||
// Update all DDS chips on reference clock frequency
|
||||
for dds_channel in 0..4 {
|
||||
self.dds[dds_channel].set_ref_clk_frequency(f_ref_clk)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Self::Error> {
|
||||
|
@ -242,4 +262,12 @@ where
|
|||
(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> {
|
||||
self.dds[usize::from(channel)].set_single_tone_profile(profile, frequency, phase, amplitude)
|
||||
}
|
||||
|
||||
fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Self::Error> {
|
||||
self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk)
|
||||
}
|
||||
}
|
||||
|
|
106
src/main.rs
106
src/main.rs
|
@ -112,7 +112,7 @@ fn main() -> ! {
|
|||
|
||||
let mut config = ConfigRegister::new(parts.spi1);
|
||||
let mut att = Attenuator::new(parts.spi2);
|
||||
let mut dds0 = DDS::new(parts.spi4, 25_000_000);
|
||||
let mut dds0 = DDS::new(parts.spi4, 25_000_000.0);
|
||||
|
||||
// Reset all DDS, set CLK_SEL to 0
|
||||
config.set_configurations(&mut [
|
||||
|
@ -135,75 +135,75 @@ fn main() -> ! {
|
|||
(DDSCFRMask::READ_EFFECTIVE_FTW, 1),
|
||||
]).unwrap();
|
||||
|
||||
dds0.enable_pll(1_000_000_000).unwrap();
|
||||
dds0.set_sys_clk_frequency(1_000_000_000.0).unwrap();
|
||||
|
||||
// Attenuator
|
||||
att.set_attenuation([
|
||||
0.0, 31.5, 24.0, 0.0
|
||||
5.0, 31.5, 24.0, 0.0
|
||||
]).unwrap();
|
||||
|
||||
dds0.set_single_tone_profile(1, 10_000_000, 0.0, 0.5).unwrap();
|
||||
dds0.set_single_tone_profile(1, 10_000_000.0, 0.0, 0.5).unwrap();
|
||||
config.set_configurations(&mut [
|
||||
(CFGMask::PROFILE, 1),
|
||||
]).unwrap();
|
||||
|
||||
// Setup RAM configuration
|
||||
dds0.set_configurations(&mut [
|
||||
(DDSCFRMask::RAM_ENABLE, 1),
|
||||
(DDSCFRMask::RAM_PLAYBACK_DST, 2),
|
||||
]).unwrap();
|
||||
// // Setup RAM configuration
|
||||
// dds0.set_configurations(&mut [
|
||||
// (DDSCFRMask::RAM_ENABLE, 1),
|
||||
// (DDSCFRMask::RAM_PLAYBACK_DST, 2),
|
||||
// ]).unwrap();
|
||||
|
||||
// Configure RAM profile 0
|
||||
dds0.write_register(0x0E, &mut [
|
||||
0x00, // Open
|
||||
0x09, 0xC4, // Address step rate (2500)
|
||||
0xFF, 0xC0, // End at address 1023
|
||||
0x00, 0x00, // Start at address 0
|
||||
0x04, // Recirculate mode
|
||||
]).unwrap();
|
||||
// // Configure RAM profile 0
|
||||
// dds0.write_register(0x0E, &mut [
|
||||
// 0x00, // Open
|
||||
// 0x09, 0xC4, // Address step rate (2500)
|
||||
// 0xFF, 0xC0, // End at address 1023
|
||||
// 0x00, 0x00, // Start at address 0
|
||||
// 0x04, // Recirculate mode
|
||||
// ]).unwrap();
|
||||
|
||||
debug!("{:#X?}", dds0.read_register(0x0E, &mut[
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
]).unwrap());
|
||||
// debug!("{:#X?}", dds0.read_register(0x0E, &mut[
|
||||
// 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00,
|
||||
// ]).unwrap());
|
||||
|
||||
// Choose profile 0
|
||||
config.set_configurations(&mut [
|
||||
(CFGMask::PROFILE, 0),
|
||||
]).unwrap();
|
||||
// // Choose profile 0
|
||||
// config.set_configurations(&mut [
|
||||
// (CFGMask::PROFILE, 0),
|
||||
// ]).unwrap();
|
||||
|
||||
// Set RAM to be amplitudes, disable RAM momentarily
|
||||
dds0.set_configurations(&mut [
|
||||
(DDSCFRMask::RAM_PLAYBACK_DST, 0),
|
||||
(DDSCFRMask::RAM_ENABLE, 0),
|
||||
]).unwrap();
|
||||
// // Set RAM to be amplitudes, disable RAM momentarily
|
||||
// dds0.set_configurations(&mut [
|
||||
// (DDSCFRMask::RAM_PLAYBACK_DST, 0),
|
||||
// (DDSCFRMask::RAM_ENABLE, 0),
|
||||
// ]).unwrap();
|
||||
|
||||
let mut ram_data: [u8; ((1024 * 4) + 1)] = [0; (1024 * 4) + 1];
|
||||
ram_data[0] = 0x16;
|
||||
for index in 0..1024 {
|
||||
if index % 2 == 1 {
|
||||
ram_data[(index * 4) + 1] = 0x3F;
|
||||
ram_data[(index * 4) + 2] = 0xFF;
|
||||
} else {
|
||||
ram_data[(index * 4) + 1] = 0x00;
|
||||
ram_data[(index * 4) + 2] = 0x00;
|
||||
}
|
||||
// ram_data[(index * 4) + 1] = ((index >> 2) & 0xFF) as u8;
|
||||
// ram_data[(index * 4) + 2] = ((index & 0x03) << 6) as u8;
|
||||
}
|
||||
dds0.transfer(&mut ram_data).unwrap();
|
||||
// let mut ram_data: [u8; ((1024 * 4) + 1)] = [0; (1024 * 4) + 1];
|
||||
// ram_data[0] = 0x16;
|
||||
// for index in 0..1024 {
|
||||
// if index % 2 == 1 {
|
||||
// ram_data[(index * 4) + 1] = 0x3F;
|
||||
// ram_data[(index * 4) + 2] = 0xFF;
|
||||
// } else {
|
||||
// ram_data[(index * 4) + 1] = 0x00;
|
||||
// ram_data[(index * 4) + 2] = 0x00;
|
||||
// }
|
||||
// // ram_data[(index * 4) + 1] = ((index >> 2) & 0xFF) as u8;
|
||||
// // ram_data[(index * 4) + 2] = ((index & 0x03) << 6) as u8;
|
||||
// }
|
||||
// dds0.transfer(&mut ram_data).unwrap();
|
||||
|
||||
config.set_configurations(&mut [
|
||||
(CFGMask::PROFILE, 1),
|
||||
]).unwrap();
|
||||
// config.set_configurations(&mut [
|
||||
// (CFGMask::PROFILE, 1),
|
||||
// ]).unwrap();
|
||||
|
||||
config.set_configurations(&mut [
|
||||
(CFGMask::PROFILE, 0),
|
||||
]).unwrap();
|
||||
// config.set_configurations(&mut [
|
||||
// (CFGMask::PROFILE, 0),
|
||||
// ]).unwrap();
|
||||
|
||||
dds0.set_configurations(&mut [
|
||||
(DDSCFRMask::RAM_ENABLE, 1),
|
||||
]).unwrap();
|
||||
// dds0.set_configurations(&mut [
|
||||
// (DDSCFRMask::RAM_ENABLE, 1),
|
||||
// ]).unwrap();
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
|
87
src/scpi.rs
87
src/scpi.rs
|
@ -13,6 +13,7 @@ use scpi::{
|
|||
};
|
||||
use scpi::suffix::{Amplitude, Db};
|
||||
use uom::si::frequency::{gigahertz, hertz, kilohertz, megahertz, Frequency};
|
||||
use uom::si::angle::{degree, gon, minute as aminute, radian, revolution, Angle};
|
||||
use uom::si::{f32, f64};
|
||||
|
||||
use embedded_hal::blocking::spi::Transfer;
|
||||
|
@ -116,7 +117,11 @@ macro_rules! scpi_tree {
|
|||
scpi_root!(
|
||||
"CHANNEL0" => {
|
||||
"SWitch" => Channel0SwitchCommand,
|
||||
"Attenuation" => Channel0AttenuationCommand
|
||||
"Attenuation" => Channel0AttenuationCommand,
|
||||
"SYSCLOCK" => Channel0SystemClockCommand,
|
||||
"PROFILE0" => {
|
||||
"SINGLEtone" => Channel0Profile0Singletone
|
||||
}
|
||||
},
|
||||
"CHANNEL1" => {
|
||||
"SWitch" => Channel1SwitchCommand,
|
||||
|
@ -164,11 +169,16 @@ pub struct Channel2SwitchCommand {}
|
|||
pub struct Channel3SwitchCommand {}
|
||||
pub struct ClockSourceCommand {}
|
||||
pub struct ClockDivisionCommand {}
|
||||
pub struct Channel0SystemClockCommand {}
|
||||
pub struct Channel0AttenuationCommand {}
|
||||
pub struct Channel1AttenuationCommand {}
|
||||
pub struct Channel2AttenuationCommand {}
|
||||
pub struct Channel3AttenuationCommand {}
|
||||
pub struct ProfileCommand {}
|
||||
pub struct Channel0Profile0Singletone {}
|
||||
pub struct Channel0Profile0SingletoneFrequency {}
|
||||
pub struct Channel0Profile0SingletonePhase {}
|
||||
pub struct Channel0Profile0SingletoneAmplitude {}
|
||||
|
||||
impl<T: Device + UrukulTraits> Command<T> for Channel0SwitchCommand {
|
||||
nquery!();
|
||||
|
@ -253,7 +263,7 @@ impl<T:Device + UrukulTraits> Command<T> for ClockSourceCommand {
|
|||
_ => Err(ErrorCode::IllegalParameterValue.into()),
|
||||
})
|
||||
})?;
|
||||
trace!("Received frequency: {:?}", frequency);
|
||||
trace!("Received master clock frequency: {:?}", frequency);
|
||||
|
||||
let clock_source = match s_str {
|
||||
source if source.eq_ignore_ascii_case("OSC") => {
|
||||
|
@ -302,6 +312,28 @@ impl<T:Device + UrukulTraits> Command<T> for ClockDivisionCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T:Device + UrukulTraits> Command<T> for Channel0SystemClockCommand {
|
||||
nquery!();
|
||||
|
||||
// Param: <frequency>
|
||||
// The exact method of generating this frequency is auto-decided
|
||||
// The process is delegated to individual DDS chip
|
||||
fn event(&self, context: &mut Context<T>, args: &mut Tokenizer) -> Result<()> {
|
||||
|
||||
let frequency: f64::Frequency = args.next_data(true)?
|
||||
.map_or(Ok(f64::Frequency::new::<hertz>(0.0)), |t| {
|
||||
t.numeric(|s| match s {
|
||||
NumericValues::Default => Ok(f64::Frequency::new::<hertz>(0.0)),
|
||||
_ => Err(ErrorCode::IllegalParameterValue.into()),
|
||||
})
|
||||
})?;
|
||||
trace!("Received channel 0 system clock frequency: {:?}", frequency);
|
||||
|
||||
// Setup sys_clk through urukul interface
|
||||
context.device.set_channel_sys_clk(0, frequency.get::<hertz>()).map_err(|_| Error::new(ErrorCode::IllegalParameterValue))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Device + UrukulTraits> Command<T> for Channel0AttenuationCommand {
|
||||
nquery!();
|
||||
|
||||
|
@ -376,6 +408,57 @@ impl<T:Device + UrukulTraits> Command<T> for ProfileCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T:Device + UrukulTraits> Command<T> for Channel0Profile0Singletone {
|
||||
nquery!();
|
||||
|
||||
// Params: frequency, phase, amplitude (all mandatory)
|
||||
fn event(&self, context: &mut Context<T>, args: &mut Tokenizer) -> Result<()> {
|
||||
|
||||
// Read output frequency
|
||||
let frequency: f64::Frequency = args.next_data(false)?
|
||||
.map_or(Err(Error::new(ErrorCode::MissingParameter)), |t| {
|
||||
t.numeric(|s| match s {
|
||||
NumericValues::Default => Ok(f64::Frequency::new::<hertz>(0.0)),
|
||||
_ => Err(ErrorCode::IllegalParameterValue.into()),
|
||||
})
|
||||
})?;
|
||||
trace!("Received channel 0 profile 0 output single tone frequency: {:?}", frequency);
|
||||
// Handle negative frequency
|
||||
if frequency.get::<hertz>() < 0.0 {
|
||||
return Err(ErrorCode::DataOutOfRange.into());
|
||||
}
|
||||
|
||||
// Read phase offset
|
||||
let phase: f64::Angle = args.next_data(false)?
|
||||
.map_or(Err(Error::new(ErrorCode::MissingParameter)), |t| {
|
||||
t.numeric(
|
||||
|s| match s {
|
||||
NumericValues::Default => Ok(f64::Angle::new::<degree>(0.0)),
|
||||
_ => Err(ErrorCode::IllegalParameterValue.into()),
|
||||
})
|
||||
})?;
|
||||
trace!("Received channel 0 profile 0 output single tone phase offset: {:?}", phase);
|
||||
// Handle out-of-bound phase offset
|
||||
if phase.get::<degree>() < 0.0 || phase.get::<degree>() >= 360.0 {
|
||||
return Err(ErrorCode::DataOutOfRange.into());
|
||||
}
|
||||
|
||||
// Read amplitude offset
|
||||
let amplitude: f64 = args.next_data(false)?
|
||||
.map_or(Err(Error::new(ErrorCode::MissingParameter)),
|
||||
|token| token.try_into())?;
|
||||
trace!("Received channel 0 profile 0 output single tone amplitude offset: {:?}", amplitude);
|
||||
// Handle out-of-bound phase offset
|
||||
if amplitude < 0.0 || amplitude > 1.0 {
|
||||
return Err(ErrorCode::DataOutOfRange.into());
|
||||
}
|
||||
|
||||
// TODO: Setup single tone on DDS
|
||||
context.device.set_channel_single_tone_profile(0, 0, frequency.get::<hertz>(), phase.get::<degree>(), amplitude)
|
||||
.map_err(|_| Error::new(ErrorCode::HardwareError))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implement "Device" trait from SCPI
|
||||
* TODO: Implement mandatory commands
|
||||
|
|
|
@ -5,8 +5,8 @@ use log::{trace, info};
|
|||
use arrayvec::{ArrayVec};
|
||||
|
||||
pub trait MqttScpiTranslator {
|
||||
// Convert an MQTT publish message into SCPI compatible command
|
||||
// The argument part/ MQTT message must follow SCPI standard for parameter formatting
|
||||
// Unwrap an MQTT publish message into SCPI compatible command
|
||||
// The command part/ MQTT message must follow SCPI standard for parameter formatting
|
||||
fn run_with_mqtt<FMT: Formatter>(&mut self, topic: &str, args: &str, response: &mut FMT) -> Result<()>;
|
||||
}
|
||||
|
||||
|
@ -20,27 +20,27 @@ impl<'a, T: Device> MqttScpiTranslator for Context<'a, T> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let command_topic = topic.strip_prefix("Urukul/Control/")
|
||||
.unwrap_or("");
|
||||
// let command_topic = topic.strip_prefix("Urukul/Control")
|
||||
// .unwrap_or("");
|
||||
|
||||
// Create a fixed-size buffer to handle slice operation
|
||||
let mut buffer = ArrayVec::<[u8; 1024]>::new();
|
||||
|
||||
// Copy MQTT topic, convert it into SCPI header format
|
||||
for i in command_topic.chars() {
|
||||
if i == '/' {
|
||||
// The topic separator is colon(':') in SCPI, and slash('/') in MQTT
|
||||
buffer.try_push(b':')
|
||||
.map_err(|_| ErrorCode::OutOfMemory)?;
|
||||
} else {
|
||||
buffer.try_push(i as u8)
|
||||
.map_err(|_| ErrorCode::OutOfMemory)?;
|
||||
}
|
||||
}
|
||||
// // Copy MQTT topic, convert it into SCPI header format
|
||||
// for i in command_topic.chars() {
|
||||
// if i == '/' {
|
||||
// // The topic separator is colon(':') in SCPI, and slash('/') in MQTT
|
||||
// buffer.try_push(b':')
|
||||
// .map_err(|_| ErrorCode::OutOfMemory)?;
|
||||
// } else {
|
||||
// buffer.try_push(i as u8)
|
||||
// .map_err(|_| ErrorCode::OutOfMemory)?;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Place a space bar between header and parameter
|
||||
buffer.try_push(b' ')
|
||||
.map_err(|_| ErrorCode::OutOfMemory)?;
|
||||
// // Place a space bar between header and parameter
|
||||
// buffer.try_push(b' ')
|
||||
// .map_err(|_| ErrorCode::OutOfMemory)?;
|
||||
|
||||
// Copy the arguments into the buffer
|
||||
for i in args.chars() {
|
||||
|
|
Loading…
Reference in New Issue