Adding WIP experimental code

This commit is contained in:
Ryan Summers 2020-10-22 16:16:38 +02:00
parent db182b923d
commit 071ccd17dc
5 changed files with 82 additions and 142 deletions

3
Cargo.lock generated
View File

@ -462,7 +462,7 @@ dependencies = [
"serde-json-core", "serde-json-core",
"smoltcp", "smoltcp",
"stm32h7-ethernet", "stm32h7-ethernet",
"stm32h7xx-hal 0.5.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/pounder-support)", "stm32h7xx-hal 0.5.0",
] ]
[[package]] [[package]]
@ -497,7 +497,6 @@ dependencies = [
[[package]] [[package]]
name = "stm32h7xx-hal" name = "stm32h7xx-hal"
version = "0.5.0" version = "0.5.0"
source = "git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/pounder-support#ff00e938f2b226211c178f26c092f36462c44404"
dependencies = [ dependencies = [
"bare-metal", "bare-metal",
"cast", "cast",

View File

@ -59,8 +59,9 @@ features = ["stm32h743v"]
[dependencies.stm32h7xx-hal] [dependencies.stm32h7xx-hal]
features = ["stm32h743v", "rt", "unproven"] features = ["stm32h743v", "rt", "unproven"]
git = "https://github.com/quartiq/stm32h7xx-hal.git" # git = "https://github.com/quartiq/stm32h7xx-hal.git"
branch = "feature/pounder-support" # branch = "feature/pounder-support"
path = "../stm32h7xx-hal"
[features] [features]
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]

View File

@ -14,11 +14,12 @@ use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin};
/// The chip supports a number of serial interfaces to improve data throughput, including normal, /// The chip supports a number of serial interfaces to improve data throughput, including normal,
/// dual, and quad SPI configurations. /// dual, and quad SPI configurations.
pub struct Ad9959<INTERFACE, DELAY, UPDATE> { pub struct Ad9959<INTERFACE, DELAY, UPDATE> {
interface: INTERFACE, pub interface: INTERFACE,
delay: DELAY, delay: DELAY,
reference_clock_frequency: f32, reference_clock_frequency: f32,
system_clock_multiplier: u8, system_clock_multiplier: u8,
io_update: UPDATE, io_update: UPDATE,
communication_mode: Mode,
} }
/// A trait that allows a HAL to provide a means of communicating with the AD9959. /// A trait that allows a HAL to provide a means of communicating with the AD9959.
@ -30,6 +31,8 @@ pub trait Interface {
fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>; fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>;
fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>;
fn write_profile(&mut self, data: [u32; 4]) -> Result<(), Self::Error>;
} }
/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to /// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to
@ -125,6 +128,7 @@ where
delay, delay,
reference_clock_frequency: clock_frequency, reference_clock_frequency: clock_frequency,
system_clock_multiplier: 1, system_clock_multiplier: 1,
communication_mode: desired_mode,
}; };
ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?;
@ -176,7 +180,8 @@ where
} }
/// Latch the DDS configuration to ensure it is active on the output channels. /// Latch the DDS configuration to ensure it is active on the output channels.
fn latch_configuration(&mut self) -> Result<(), Error> { pub fn latch_configuration(&mut self) -> Result<(), Error> {
self.delay.delay_ms(2);
self.io_update.set_high().or_else(|_| Err(Error::Pin))?; self.io_update.set_high().or_else(|_| Err(Error::Pin))?;
// The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one
// full SYNC_CLK pulse to register. For safety, we latch for 5 here. // full SYNC_CLK pulse to register. For safety, we latch for 5 here.
@ -199,7 +204,7 @@ where
&mut self, &mut self,
reference_clock_frequency: f32, reference_clock_frequency: f32,
multiplier: u8, multiplier: u8,
) -> Result<f64, Error> { ) -> Result<f32, Error> {
self.reference_clock_frequency = reference_clock_frequency; self.reference_clock_frequency = reference_clock_frequency;
if multiplier != 1 && (multiplier > 20 || multiplier < 4) { if multiplier != 1 && (multiplier > 20 || multiplier < 4) {
@ -207,8 +212,8 @@ where
} }
let frequency = let frequency =
multiplier as f64 * self.reference_clock_frequency as f64; multiplier as f32 * self.reference_clock_frequency as f32;
if frequency > 500_000_000.0f64 { if frequency > 500_000_000.0f32 {
return Err(Error::Frequency); return Err(Error::Frequency);
} }
@ -227,6 +232,8 @@ where
.map_err(|_| Error::Interface)?; .map_err(|_| Error::Interface)?;
self.system_clock_multiplier = multiplier; self.system_clock_multiplier = multiplier;
self.latch_configuration()?;
Ok(self.system_clock_frequency()) Ok(self.system_clock_frequency())
} }
@ -299,47 +306,9 @@ where
} }
/// Get the current system clock frequency in Hz. /// Get the current system clock frequency in Hz.
fn system_clock_frequency(&self) -> f64 { fn system_clock_frequency(&self) -> f32 {
self.system_clock_multiplier as f64 self.system_clock_multiplier as f32
* self.reference_clock_frequency as f64 * self.reference_clock_frequency as f32
}
/// Enable an output channel.
pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> {
let mut csr: [u8; 1] = [0];
self.interface
.read(Register::CSR as u8, &mut csr)
.map_err(|_| Error::Interface)?;
csr[0].set_bit(channel as usize + 4, true);
self.interface
.write(Register::CSR as u8, &csr)
.map_err(|_| Error::Interface)?;
Ok(())
}
/// Disable an output channel.
pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> {
let mut csr: [u8; 1] = [0];
self.interface
.read(Register::CSR as u8, &mut csr)
.map_err(|_| Error::Interface)?;
csr[0].set_bit(channel as usize + 4, false);
self.interface
.write(Register::CSR as u8, &csr)
.map_err(|_| Error::Interface)?;
Ok(())
}
/// Determine if an output channel is enabled.
pub fn is_enabled(&mut self, channel: Channel) -> Result<bool, Error> {
let mut csr: [u8; 1] = [0; 1];
self.interface
.read(Register::CSR as u8, &mut csr)
.map_err(|_| Error::Interface)?;
Ok(csr[0].get_bit(channel as usize + 4))
} }
/// Update an output channel configuration register. /// Update an output channel configuration register.
@ -356,17 +325,12 @@ where
) -> Result<(), Error> { ) -> Result<(), Error> {
// Disable all other outputs so that we can update the configuration register of only the // Disable all other outputs so that we can update the configuration register of only the
// specified channel. // specified channel.
let mut csr: [u8; 1] = [0]; let csr: u8 = *0x00_u8
self.interface .set_bits(1..=2, self.communication_mode as u8)
.read(Register::CSR as u8, &mut csr) .set_bit(4 + channel as usize, true);
.map_err(|_| Error::Interface)?;
let mut new_csr = csr;
new_csr[0].set_bits(4..8, 0);
new_csr[0].set_bit(4 + channel as usize, true);
self.interface self.interface
.write(Register::CSR as u8, &new_csr) .write(Register::CSR as u8, &[csr])
.map_err(|_| Error::Interface)?; .map_err(|_| Error::Interface)?;
self.interface self.interface
@ -530,8 +494,8 @@ where
pub fn set_frequency( pub fn set_frequency(
&mut self, &mut self,
channel: Channel, channel: Channel,
frequency: f64, frequency: f32,
) -> Result<f64, Error> { ) -> Result<f32, Error> {
if frequency < 0.0 || frequency > self.system_clock_frequency() { if frequency < 0.0 || frequency > self.system_clock_frequency() {
return Err(Error::Bounds); return Err(Error::Bounds);
} }
@ -539,15 +503,15 @@ where
// The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the
// frequency tuning word and f_s is the system clock rate. // frequency tuning word and f_s is the system clock rate.
let tuning_word: u32 = let tuning_word: u32 =
((frequency as f64 / self.system_clock_frequency()) ((frequency as f32 / self.system_clock_frequency())
* 1u64.wrapping_shl(32) as f64) as u32; * 1u64.wrapping_shl(32) as f32) as u32;
self.modify_channel( self.modify_channel(
channel, channel,
Register::CFTW0, Register::CFTW0,
&tuning_word.to_be_bytes(), &tuning_word.to_be_bytes(),
)?; )?;
Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) Ok((tuning_word as f32 / 1u64.wrapping_shl(32) as f32)
* self.system_clock_frequency()) * self.system_clock_frequency())
} }
@ -558,79 +522,50 @@ where
/// ///
/// Returns: /// Returns:
/// The frequency of the channel in Hz. /// The frequency of the channel in Hz.
pub fn get_frequency(&mut self, channel: Channel) -> Result<f64, Error> { pub fn get_frequency(&mut self, channel: Channel) -> Result<f32, Error> {
// Read the frequency tuning word for the channel. // Read the frequency tuning word for the channel.
let mut tuning_word: [u8; 4] = [0; 4]; let mut tuning_word: [u8; 4] = [0; 4];
self.read_channel(channel, Register::CFTW0, &mut tuning_word)?; self.read_channel(channel, Register::CFTW0, &mut tuning_word)?;
let tuning_word = u32::from_be_bytes(tuning_word); let tuning_word = u32::from_be_bytes(tuning_word);
// Convert the tuning word into a frequency. // Convert the tuning word into a frequency.
Ok(tuning_word as f64 * self.system_clock_frequency() Ok((tuning_word as f32 * self.system_clock_frequency()) / (1u64 << 32) as f32)
/ (1u64 << 32) as f64)
} }
pub fn write_profile(&mut self, channel: Channel, freq: f64, turns: f32, amplitude: f32) -> Result<(), Error> { pub fn write_profile(&mut self, channel: Channel, freq: f32, turns: f32, amplitude: f32) -> Result<(), Error> {
let csr: u8 = *0x00_u8
.set_bits(1..=2, self.communication_mode as u8)
.set_bit(4 + channel as usize, true);
// The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the
// frequency tuning word and f_s is the system clock rate. // frequency tuning word and f_s is the system clock rate.
let tuning_word: u32 = let tuning_word: u32 = ((freq * (1u64 << 32) as f32) / self.system_clock_frequency()) as u32;
((freq as f64 / self.system_clock_frequency())
* 1u64.wrapping_shl(32) as f64) as u32;
let phase_offset: u16 = (turns * (1 << 14) as f32) as u16 & 0x3FFFu16; let phase_offset: u16 = (turns * (1 << 14) as f32) as u16 & 0x3FFFu16;
let pow: u32 = *0u32.set_bits(24..32, Register::CPOW0 as u32)
.set_bits(8..24, phase_offset as u32)
.set_bits(0..8, Register::CFTW0 as u32);
let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16;
let mut acr: [u8; 3] = [0; 3];
// Enable the amplitude multiplier for the channel if required. The amplitude control has // Enable the amplitude multiplier for the channel if required. The amplitude control has
// full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever
// full-scale is used. // full-scale is used.
if amplitude_control < (1 << 10) { let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16;
let masked_control = amplitude_control & 0x3FF;
acr[1] = masked_control.to_be_bytes()[0];
acr[2] = masked_control.to_be_bytes()[1];
// Enable the amplitude multiplier let acr: u32 = *0u32.set_bits(24..32, Register::ACR as u32)
acr[1].set_bit(4, true); .set_bits(0..10, amplitude_control as u32 & 0x3FF)
} .set_bit(12, amplitude_control < (1 << 10));
self.modify_channel_closure(channel, |interface| { let serialized: [u32; 4] = [
let mut data: [u8; 11] = [0; 11]; u32::from_le_bytes([Register::CSR as u8, csr, Register::CSR as u8, csr]),
data[0..2].copy_from_slice(&phase_offset.to_be_bytes()); acr.to_be(),
data[2] = Register::CFTW0 as u8; pow.to_be(),
data[3..7].copy_from_slice(&tuning_word.to_be_bytes()); tuning_word.to_be(),
data[7] = Register::ACR as u8; ];
data[8..11].copy_from_slice(&acr);
interface.write(Register::CPOW0 as u8, &data).map_err(|_| Error::Interface) self.interface.write_profile(serialized).map_err(|_| Error::Interface)?;
})?;
Ok(()) Ok(())
} }
fn modify_channel_closure<F>(&mut self, channel: Channel, f: F) -> Result<(), Error>
where
F: FnOnce(&mut INTERFACE) -> Result<(), Error>,
{
// Disable all other outputs so that we can update the configuration register of only the
// specified channel.
let mut csr: [u8; 1] = [0];
self.interface
.read(Register::CSR as u8, &mut csr)
.map_err(|_| Error::Interface)?;
let mut new_csr = csr;
new_csr[0].set_bits(4..8, 0);
new_csr[0].set_bit(4 + channel as usize, true);
self.interface
.write(Register::CSR as u8, &new_csr)
.map_err(|_| Error::Interface)?;
let result = f(&mut self.interface);
// Latch the configuration and restore the previous CSR. Note that the re-enable of the
// channel happens immediately, so the CSR update does not need to be latched.
self.latch_configuration()?;
result
}
} }

View File

@ -629,6 +629,7 @@ const APP: () = {
}; };
cp.SCB.enable_icache(); cp.SCB.enable_icache();
//cp.SCB.enable_dcache(&mut cp.CPUID);
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
// info!("Built on {}", build_info::BUILT_TIME_UTC); // info!("Built on {}", build_info::BUILT_TIME_UTC);
@ -747,6 +748,7 @@ const APP: () = {
match c.resources.pounder { match c.resources.pounder {
Some(pounder) => { Some(pounder) => {
pounder.ad9959.interface.start_stream();
let state = pounder::ChannelState { let state = pounder::ChannelState {
parameters: pounder::DdsChannelState { parameters: pounder::DdsChannelState {
@ -755,21 +757,23 @@ const APP: () = {
amplitude: 1.0, amplitude: 1.0,
enabled: true, enabled: true,
}, },
attenuation: 10.0, attenuation: 0.0,
}; };
let state1 = pounder::ChannelState { let state1 = pounder::ChannelState {
parameters: pounder::DdsChannelState { parameters: pounder::DdsChannelState {
phase_offset: 0.5, phase_offset: 0.5,
frequency: 50_000_000.0, frequency: 100_000_000.0,
amplitude: 1.0, amplitude: 1.0,
enabled: true, enabled: true,
}, },
attenuation: 10.0, attenuation: 0.0,
}; };
pounder.set_channel_state(pounder::Channel::Out0, state).unwrap(); pounder.set_channel_state(pounder::Channel::Out0, state).unwrap();
pounder.set_channel_state(pounder::Channel::Out1, state1).unwrap(); pounder.set_channel_state(pounder::Channel::Out1, state1).unwrap();
pounder.ad9959.latch_configuration().unwrap();
}, },
_ => panic!("Failed"), _ => panic!("Failed"),
} }

View File

@ -43,7 +43,7 @@ pub enum Channel {
#[derive(Serialize, Deserialize, Copy, Clone, Debug)] #[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub struct DdsChannelState { pub struct DdsChannelState {
pub phase_offset: f32, pub phase_offset: f32,
pub frequency: f64, pub frequency: f32,
pub amplitude: f32, pub amplitude: f32,
pub enabled: bool, pub enabled: bool,
} }
@ -90,6 +90,7 @@ impl Into<ad9959::Channel> for Channel {
pub struct QspiInterface { pub struct QspiInterface {
pub qspi: hal::qspi::Qspi, pub qspi: hal::qspi::Qspi,
mode: ad9959::Mode, mode: ad9959::Mode,
streaming: bool,
} }
impl QspiInterface { impl QspiInterface {
@ -106,8 +107,14 @@ impl QspiInterface {
Ok(Self { Ok(Self {
qspi, qspi,
mode: ad9959::Mode::SingleBitTwoWire, mode: ad9959::Mode::SingleBitTwoWire,
streaming: false,
}) })
} }
pub fn start_stream(&mut self) {
self.streaming = true;
self.qspi.enter_write_stream_mode().unwrap();
}
} }
impl ad9959::Interface for QspiInterface { impl ad9959::Interface for QspiInterface {
@ -205,13 +212,23 @@ impl ad9959::Interface for QspiInterface {
.map_err(|_| Error::Qspi) .map_err(|_| Error::Qspi)
} }
ad9959::Mode::FourBitSerial => { ad9959::Mode::FourBitSerial => {
self.qspi.write(addr, &data).map_err(|_| Error::Qspi) if self.streaming {
Err(Error::Qspi)
} else {
self.qspi.write(addr, data).map_err(|_| Error::Qspi)?;
Ok(())
}
} }
_ => Err(Error::Qspi), _ => Err(Error::Qspi),
} }
} }
fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> { fn write_profile(&mut self, data: [u32; 4]) -> Result<(), Error> {
self.qspi.write_profile(data);
Ok(())
}
fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Error> {
if (addr & 0x80) != 0 { if (addr & 0x80) != 0 {
return Err(Error::InvalidAddress); return Err(Error::InvalidAddress);
} }
@ -221,9 +238,7 @@ impl ad9959::Interface for QspiInterface {
return Err(Error::Qspi); return Err(Error::Qspi);
} }
self.qspi self.qspi.read(0x80_u8 | addr, dest).map_err(|_| Error::Qspi)
.read(0x80_u8 | addr, &mut dest)
.map_err(|_| Error::Qspi)
} }
} }
@ -426,16 +441,12 @@ where
.ad9959 .ad9959
.get_amplitude(channel.into()) .get_amplitude(channel.into())
.map_err(|_| Error::Dds)?; .map_err(|_| Error::Dds)?;
let enabled = self
.ad9959
.is_enabled(channel.into())
.map_err(|_| Error::Dds)?;
Ok(DdsChannelState { Ok(DdsChannelState {
phase_offset, phase_offset,
frequency, frequency,
amplitude, amplitude,
enabled, enabled: true,
}) })
} }
@ -477,17 +488,7 @@ where
self.ad9959.write_profile(channel.into(), state.parameters.frequency, self.ad9959.write_profile(channel.into(), state.parameters.frequency,
state.parameters.phase_offset, state.parameters.amplitude).map_err(|_| Error::Dds)?; state.parameters.phase_offset, state.parameters.amplitude).map_err(|_| Error::Dds)?;
if state.parameters.enabled { //self.set_attenuation(channel, state.attenuation)?;
self.ad9959
.enable_channel(channel.into())
.map_err(|_| Error::Dds)?;
} else {
self.ad9959
.disable_channel(channel.into())
.map_err(|_| Error::Dds)?;
}
self.set_attenuation(channel, state.attenuation)?;
Ok(()) Ok(())
} }