mqtt_mux: add cmd

This commit is contained in:
occheung 2020-09-22 13:40:46 +08:00
parent d47f9b4655
commit 9ea56ebde5
2 changed files with 271 additions and 30 deletions

View File

@ -176,7 +176,18 @@ where
} }
} }
fn set_clock_source(&mut self, source: ClockSource, frequency: f64) -> Result<(), Error<E>> { fn set_clock(&mut self, source: ClockSource, frequency: f64, division: u8) -> Result<(), Error<E>> {
// Change clock source through configuration register
self.set_clock_source(source)?;
// Modify the master clock frequency
// Prevent redundunt call to change f_ref_clk
self.f_master_clk = frequency;
self.set_clock_division(division)
}
fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Error<E>> {
// Change clock source through configuration register // Change clock source through configuration register
match source { match source {
ClockSource::OSC => self.config_register.set_configurations(&mut [ ClockSource::OSC => self.config_register.set_configurations(&mut [
@ -190,19 +201,15 @@ where
ClockSource::SMA => self.config_register.set_configurations(&mut [ ClockSource::SMA => self.config_register.set_configurations(&mut [
(CFGMask::CLK_SEL0, 1), (CFGMask::CLK_SEL0, 1),
]), ]),
}?; }.map(|_| ())
}
// Save the new master clock frequency fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), Error<E>> {
// Update master clock frequency
self.f_master_clk = frequency; self.f_master_clk = frequency;
// Calculate reference clock frequency after clock division from configuration register // Update all DDS f_ref_clk
let f_ref_clk = self.f_master_clk / (self.config_register.get_configuration(CFGMask::DIV) as f64); self.set_dds_ref_clk()
// 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_clock_division(&mut self, division: u8) -> Result<(), Error<E>> { fn set_clock_division(&mut self, division: u8) -> Result<(), Error<E>> {
@ -219,8 +226,12 @@ where
_ => Err(Error::ParameterError), _ => Err(Error::ParameterError),
}?; }?;
self.set_dds_ref_clk()
}
fn set_dds_ref_clk(&mut self) -> Result<(), Error<E>> {
// Calculate reference clock frequency after clock division from configuration register // Calculate reference clock frequency after clock division from configuration register
let f_ref_clk = self.f_master_clk / (division 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 // Update all DDS chips on reference clock frequency
for dds_channel in 0..4 { for dds_channel in 0..4 {

View File

@ -22,6 +22,11 @@ use crate::Error;
pub enum MqttTopic { pub enum MqttTopic {
Switch(u8), Switch(u8),
Attenuation(u8), Attenuation(u8),
Clock,
ClockSource,
ClockFrequency,
ClockDivision,
SystemClock(u8),
Singletone(u8, u8), Singletone(u8, u8),
SingletoneFrequency(u8, u8), SingletoneFrequency(u8, u8),
SingletoneAmplitude(u8, u8), SingletoneAmplitude(u8, u8),
@ -35,6 +40,11 @@ pub enum MqttTopic {
pub enum MqttCommand { pub enum MqttCommand {
Switch(u8, bool), Switch(u8, bool),
Attenuation(u8, f32), Attenuation(u8, f32),
Clock(UrukulClockSource, f64, u8),
ClockSource(UrukulClockSource),
ClockFrequency(f64),
ClockDivision(u8),
SystemClock(u8, f64),
Singletone(u8, u8, f64, f64, f64), Singletone(u8, u8, f64, f64, f64),
SingletoneFrequency(u8, u8, f64), SingletoneFrequency(u8, u8, f64),
SingletoneAmplitude(u8, u8, f64), SingletoneAmplitude(u8, u8, f64),
@ -76,7 +86,7 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
singletone_frequency, singletone_frequency,
singletone_amplitude, singletone_amplitude,
singletone_phase, singletone_phase,
profile // Note: Put profile at the end profile
)) ))
)(topic) )(topic)
} }
@ -85,6 +95,11 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
match topic { match topic {
MqttTopic::Switch(ch) => switch_message(ch, message), MqttTopic::Switch(ch) => switch_message(ch, message),
MqttTopic::Attenuation(ch) => attenuation_message(ch, message), MqttTopic::Attenuation(ch) => attenuation_message(ch, message),
MqttTopic::Clock => clock_message(message),
MqttTopic::ClockSource => clock_source_message(message),
MqttTopic::ClockFrequency => clock_frequency_message(message),
MqttTopic::ClockDivision => clock_division_message(message),
MqttTopic::SystemClock(ch) => system_clock_message(ch, message),
MqttTopic::Singletone(ch, prof) => singletone_message(ch, prof, message), MqttTopic::Singletone(ch, prof) => singletone_message(ch, prof, message),
MqttTopic::SingletoneFrequency(ch, prof) => singletone_frequency_message(ch, prof, message), MqttTopic::SingletoneFrequency(ch, prof) => singletone_frequency_message(ch, prof, message),
MqttTopic::SingletoneAmplitude(ch, prof) => singletone_amplitude_message(ch, prof, message), MqttTopic::SingletoneAmplitude(ch, prof) => singletone_amplitude_message(ch, prof, message),
@ -97,6 +112,11 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
match command { match command {
MqttCommand::Switch(ch, state) => self.urukul.set_channel_switch(ch.into(), state), MqttCommand::Switch(ch, state) => self.urukul.set_channel_switch(ch.into(), state),
MqttCommand::Attenuation(ch, ampl) => self.urukul.set_channel_attenuation(ch, ampl), MqttCommand::Attenuation(ch, ampl) => self.urukul.set_channel_attenuation(ch, ampl),
MqttCommand::Clock(src, freq, div) => self.urukul.set_clock(src, freq, div),
MqttCommand::ClockSource(src) => self.urukul.set_clock_source(src),
MqttCommand::ClockFrequency(freq) => self.urukul.set_clock_frequency(freq),
MqttCommand::ClockDivision(div) => self.urukul.set_clock_division(div),
MqttCommand::SystemClock(ch, freq) => self.urukul.set_channel_sys_clk(ch, freq),
MqttCommand::Singletone(ch, prof, freq, ampl, deg) => self.urukul.set_channel_single_tone_profile(ch, prof, freq, ampl, deg), MqttCommand::Singletone(ch, prof, freq, ampl, deg) => self.urukul.set_channel_single_tone_profile(ch, prof, freq, ampl, deg),
MqttCommand::SingletoneFrequency(ch, prof, freq) => self.urukul.set_channel_single_tone_profile_frequency(ch, prof, freq), MqttCommand::SingletoneFrequency(ch, prof, freq) => self.urukul.set_channel_single_tone_profile_frequency(ch, prof, freq),
MqttCommand::SingletoneAmplitude(ch, prof, ampl) => self.urukul.set_channel_single_tone_profile_amplitude(ch, prof, ampl), MqttCommand::SingletoneAmplitude(ch, prof, ampl) => self.urukul.set_channel_single_tone_profile_amplitude(ch, prof, ampl),
@ -152,6 +172,16 @@ fn select_profile<'a>(topic: &'a str) -> IResult<&'a str, u8> {
)(topic) )(topic)
} }
fn select_clock<'a>(topic: &'a str) -> IResult<&'a str, ()> {
value(
(),
preceded(
tag("Clock"),
topic_separator
)
)(topic)
}
// Selection parser for Singletone // Selection parser for Singletone
// Note: This fucntion assumes singletone is not the most specific sub-topic // Note: This fucntion assumes singletone is not the most specific sub-topic
fn select_singletone<'a>(topic: &'a str) -> IResult<&'a str, ()> { fn select_singletone<'a>(topic: &'a str) -> IResult<&'a str, ()> {
@ -161,12 +191,6 @@ fn select_singletone<'a>(topic: &'a str) -> IResult<&'a str, ()> {
)(topic) )(topic)
} }
fn check_end_slice(message: &[u8]) -> IResult<&[u8], ()> {
not(
take(1_usize)
)(message)
}
// Read whitespace // Read whitespace
fn whitespace(message: &[u8]) -> IResult<&[u8], ()> { fn whitespace(message: &[u8]) -> IResult<&[u8], ()> {
value((), take_while(is_space))(message) value((), take_while(is_space))(message)
@ -252,6 +276,175 @@ fn attenuation_message(channel: u8, message: &[u8]) -> IResult<&[u8], MqttComman
)(message) )(message)
} }
// Parser for Clock Source Command Topic
fn clock_source<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
all_consuming(
value(
MqttTopic::ClockSource,
preceded(
clock_source,
tag("Source")
)
)
)(topic)
}
// Parser for Clock Source Command Message
fn clock_source_message(message: &[u8]) -> IResult<&[u8], MqttCommand> {
all_consuming(
alt((
value(MqttCommand::ClockSource(UrukulClockSource::OSC), tag_no_case("OSC")),
value(MqttCommand::ClockSource(UrukulClockSource::MMCX), tag_no_case("MMCX")),
value(MqttCommand::ClockSource(UrukulClockSource::SMA), tag_no_case("SMA"))
))
)(message)
}
// Parser for Clock Frequency Command Topic
fn clock_frequency<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
all_consuming(
value(
MqttTopic::ClockFrequency,
preceded(
clock_source,
tag("Frequency")
)
)
)(topic)
}
// Parser for Clock Frequency Command Message
fn clock_frequency_message(message: &[u8]) -> IResult<&[u8], MqttCommand> {
all_consuming(
map(
read_frequency,
|freq: f64| MqttCommand::ClockFrequency(freq)
)
)(message)
}
// Parser for Clock Division Command Topic
fn clock_division<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
all_consuming(
value(
MqttTopic::ClockDivision,
preceded(
clock_source,
tag("Division")
)
)
)(topic)
}
// Parser for Clock Division Command Message
fn clock_division_message(message: &[u8]) -> IResult<&[u8], MqttCommand> {
all_consuming(
map(
digit1,
|div: &[u8]| MqttCommand::ClockDivision(
u8::from_str_radix(
core::str::from_utf8(div).unwrap(),
10
).unwrap()
)
)
)(message)
}
// Parser for one-command master clock setup topic
fn clock<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
all_consuming(
value(
MqttTopic::Clock,
tag("Clock")
)
)(topic)
}
// Parser for one-command master clock setup message
fn clock_message(message: &[u8]) -> IResult<&[u8], MqttCommand> {
all_consuming(
map(
delimited(
tag("{"),
tuple((
preceded(
whitespace,
preceded(
tag("\"source\":"),
preceded(
whitespace,
terminated(
alt((
value(UrukulClockSource::OSC, tag_no_case("OSC")),
value(UrukulClockSource::MMCX, tag_no_case("MMCX")),
value(UrukulClockSource::SMA, tag_no_case("SMA"))
)),
tag(",")
)
)
)
),
preceded(
whitespace,
preceded(
tag("\"frequency\":"),
preceded(
whitespace,
terminated(
read_frequency,
tag(",")
)
)
)
),
preceded(
whitespace,
preceded(
tag("\"division\":"),
preceded(
whitespace,
terminated(
map_res(
digit1,
|div: &[u8]| u8::from_str_radix(core::str::from_utf8(div).unwrap(), 10)
),
whitespace
)
)
)
)
)),
tag("}")
),
|(src, freq, div): (UrukulClockSource, f64, u8)| MqttCommand::Clock(src, freq, div)
)
)(message)
}
// Topic parser for f_sys_clk of any channels
fn system_clock<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
all_consuming(
map(
terminated(
select_channel,
tag("SystemClock")
),
|channel: u8| MqttTopic::SystemClock(channel)
)
)(topic)
}
// Message parser for f_sys_clk of any channels
fn system_clock_message(channel: u8, message: &[u8]) -> IResult<&[u8], MqttCommand> {
all_consuming(
map(
read_frequency,
|freq: f64| MqttCommand::SystemClock(channel, freq)
)
)(message)
}
// Parser for Singletone frequenct Command Topic // Parser for Singletone frequenct Command Topic
fn singletone_frequency<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> { fn singletone_frequency<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
all_consuming( all_consuming(
@ -364,28 +557,65 @@ fn singletone<'a>(topic: &'a str) -> IResult<&'a str, MqttTopic> {
} }
// Parser for one-command singletone profile Command // Parser for one-command singletone profile Command
// Using JSON like command structure
// Possible enhancement: further modularize parsing of all separate fields
fn singletone_message(channel: u8, profile: u8, message: &[u8]) -> IResult<&[u8], MqttCommand> { fn singletone_message(channel: u8, profile: u8, message: &[u8]) -> IResult<&[u8], MqttCommand> {
all_consuming( all_consuming(
map( map(
tuple(( tuple((
read_frequency,
preceded( preceded(
message_separator, tag("{"),
double preceded(
), whitespace,
preceded( preceded(
message_separator, tag("\"frequency\":"),
terminated(
double,
opt(
preceded( preceded(
whitespace, whitespace,
tag_no_case("deg") read_frequency
)
)
)
),
preceded(
tag(","),
preceded(
whitespace,
preceded(
tag("\"amplitude\":"),
preceded(
whitespace,
double
)
)
)
),
preceded(
tag(","),
preceded(
whitespace,
preceded(
tag("\"phase\":"),
preceded(
whitespace,
terminated(
double,
preceded(
opt(
preceded(
whitespace,
tag_no_case("deg")
)
),
preceded(
whitespace,
tag("}")
)
)
)
) )
) )
) )
) )
)), )),
|(freq, ampl, phase): (f64, f64, f64)| MqttCommand::Singletone(channel, profile, freq, ampl, phase) |(freq, ampl, phase): (f64, f64, f64)| MqttCommand::Singletone(channel, profile, freq, ampl, phase)
) )