Adding support for hardware IO_update

master
Ryan Summers 2020-11-09 15:16:44 +01:00
parent c97e4d9d20
commit fca38e5d63
5 changed files with 98 additions and 37 deletions

2
Cargo.lock generated
View File

@ -567,7 +567,7 @@ dependencies = [
[[package]]
name = "stm32h7xx-hal"
version = "0.8.0"
source = "git+https://github.com/quartiq/stm32h7xx-hal?branch=feature/stabilizer-dma#5fbbfa9352f720994c210e5c21601f3acf9dc40c"
source = "git+https://github.com/quartiq/stm32h7xx-hal?branch=feature/stabilizer-dma#8516690d4f35bc4bb184eba2ee8b48d4490ec85b"
dependencies = [
"bare-metal 1.0.0",
"cast",

View File

@ -104,6 +104,7 @@ impl<I: Interface> Ad9959<I> {
pub fn new(
interface: I,
reset_pin: &mut impl OutputPin,
io_update: &mut impl OutputPin,
delay: &mut impl DelayMs<u8>,
desired_mode: Mode,
clock_frequency: f32,
@ -119,6 +120,8 @@ impl<I: Interface> Ad9959<I> {
// Reset the AD9959
reset_pin.set_high().or_else(|_| Err(Error::Pin))?;
io_update.set_low().or_else(|_| Err(Error::Pin))?;
// Delay for a clock cycle to allow the device to reset.
delay.delay_ms((1000.0 / clock_frequency as f32) as u8);
@ -137,6 +140,12 @@ impl<I: Interface> Ad9959<I> {
.write(Register::CSR as u8, &csr)
.map_err(|_| Error::Interface)?;
// Latch the new interface configuration.
io_update.set_high().or_else(|_| Err(Error::Pin))?;
// Delay for a clock cycle to allow the device to reset.
delay.delay_ms(2 * (1000.0 / clock_frequency as f32) as u8);
io_update.set_low().or_else(|_| Err(Error::Pin))?;
ad9959
.interface
.configure_mode(desired_mode)

View File

@ -47,7 +47,7 @@ impl HighResTimerE {
let minimum_duration = set_duration + set_offset;
let source_frequency: u32 = self.clocks.timy_ker_ck().0;
let source_cycles = (minimum_duration * source_frequency as f32) as u32;
let source_cycles = (minimum_duration * source_frequency as f32) as u32 + 1;
// Determine the clock divider, which may be 1, 2, or 4. We will choose a clock divider that
// allows us the highest resolution per tick, so lower dividers are favored.
@ -68,7 +68,7 @@ impl HighResTimerE {
// We now have the prescaler and the period registers. Configure the timer.
self.timer
.timecr
.modify(|_, w| unsafe { w.ck_pscx().bits(divider) });
.modify(|_, w| unsafe { w.ck_pscx().bits(divider + 4) });
self.timer.perer.write(|w| unsafe { w.perx().bits(period) });
// Configure the comparator 1 level.
@ -83,13 +83,16 @@ impl HighResTimerE {
Channel::One => {
self.timer.sete1r.write(|w| w.cmp1().set_bit());
self.timer.rste1r.write(|w| w.per().set_bit());
self.common.oenr.write(|w| w.te1oen().set_bit());
}
Channel::Two => {
self.timer.sete2r.write(|w| w.cmp1().set_bit());
self.timer.rste2r.write(|w| w.per().set_bit());
self.common.oenr.write(|w| w.te2oen().set_bit());
}
}
// Enable the timer now that it is configured.
self.master.mcr.modify(|_, w| w.tecen().set_bit());
}

View File

@ -183,6 +183,8 @@ const APP: () = {
timer: hal::timer::Timer<hal::stm32::TIM2>,
profiles: heapless::spsc::Queue<[u32; 4], heapless::consts::U32>,
// Note: It appears that rustfmt generates a format that GDB cannot recognize, which
// results in GDB breakpoints being set improperly.
#[rustfmt::skip]
@ -241,7 +243,7 @@ const APP: () = {
let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD);
let gpioe = dp.GPIOE.split(ccdr.peripheral.GPIOE);
let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF);
let gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG);
let mut gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG);
let afe0 = {
let a0_pin = gpiof.pf2.into_push_pull_output();
@ -469,16 +471,25 @@ const APP: () = {
};
let mut reset_pin = gpioa.pa0.into_push_pull_output();
let mut io_update = gpiog
.pg7
.into_push_pull_output();
ad9959::Ad9959::new(
let ad9959 = ad9959::Ad9959::new(
qspi_interface,
&mut reset_pin,
&mut io_update,
&mut delay,
ad9959::Mode::FourBitSerial,
100_000_000_f32,
5,
)
.unwrap()
.unwrap();
// Return IO_Update
gpiog.pg7 = io_update.into_analog();
ad9959
};
let io_expander = {
@ -573,6 +584,9 @@ const APP: () = {
900_e-9,
);
// Ensure that we have enough time for an IO-update every sample.
assert!(1.0 / (1000 * SAMPLE_FREQUENCY_KHZ) as f32 > 900_e-9);
hrtimer
};
@ -733,15 +747,24 @@ const APP: () = {
net_interface: network_interface,
eth_mac,
mac_addr,
profiles: heapless::spsc::Queue::new(),
}
}
#[task(binds = TIM3, resources=[dacs], priority = 3)]
#[task(binds = TIM3, resources=[dacs, profiles, pounder], priority = 3)]
fn dac_update(c: dac_update::Context) {
c.resources.dacs.update();
if let Some(pounder) = c.resources.pounder {
if let Some(profile) = c.resources.profiles.dequeue() {
pounder.ad9959.interface.write_profile(profile).unwrap();
pounder.io_update_trigger.trigger();
}
}
}
#[task(binds=DMA1_STR3, resources=[adcs, dacs, iir_state, iir_ch], priority=2)]
#[task(binds=DMA1_STR3, resources=[adcs, dacs, pounder, profiles, iir_state, iir_ch], priority=2)]
fn adc_update(mut c: adc_update::Context) {
let (adc0_samples, adc1_samples) =
c.resources.adcs.transfer_complete_handler();
@ -756,6 +779,20 @@ const APP: () = {
c.resources
.dacs
.lock(|dacs| dacs.push(result_adc0, result_adc1));
let profiles = &mut c.resources.profiles;
c.resources.pounder.lock(|pounder| {
if let Some(pounder) = pounder {
profiles.lock(|profiles| {
let profile = pounder.ad9959.serialize_profile(pounder::Channel::Out0.into(),
100_000_000_f32,
0.0_f32,
*adc0 as f32 / 0xFFFF as f32).unwrap();
profiles.enqueue(profile).unwrap();
});
}
});
}
}
@ -822,10 +859,12 @@ const APP: () = {
"stabilizer/afe0/gain": (|| c.resources.afe0.get_gain()),
"stabilizer/afe1/gain": (|| c.resources.afe1.get_gain()),
"pounder/dds/clock": (|| {
match c.resources.pounder {
Some(pounder) => pounder.get_dds_clock_config(),
_ => Err(pounder::Error::Access),
}
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) => pounder.get_dds_clock_config(),
_ => Err(pounder::Error::Access),
}
})
})
],
@ -853,38 +892,48 @@ const APP: () = {
})
}),
"pounder/in0": pounder::ChannelState, (|state| {
match c.resources.pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::In0, state),
_ => Err(pounder::Error::Access),
}
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::In0, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/in1": pounder::ChannelState, (|state| {
match c.resources.pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::In1, state),
_ => Err(pounder::Error::Access),
}
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::In1, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/out0": pounder::ChannelState, (|state| {
match c.resources.pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::Out0, state),
_ => Err(pounder::Error::Access),
}
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::Out0, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/out1": pounder::ChannelState, (|state| {
match c.resources.pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::Out1, state),
_ => Err(pounder::Error::Access),
}
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::Out1, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/dds/clock": pounder::DdsClockConfig, (|config| {
match c.resources.pounder {
Some(pounder) => pounder.configure_dds_clock(config),
_ => Err(pounder::Error::Access),
}
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) => pounder.configure_dds_clock(config),
_ => Err(pounder::Error::Access),
}
})
}),
"stabilizer/afe0/gain": afe::Gain, (|gain| {
Ok::<(), ()>(c.resources.afe0.set_gain(gain))

View File

@ -126,7 +126,7 @@ impl QspiInterface {
qspi_regs.dlr.write(|w| w.dl().bits(0xFFFF_FFFF));
qspi_regs
.ccr
.modify(|_, w| w.imode().bits(0).fmode().bits(1));
.modify(|_, w| w.imode().bits(0).fmode().bits(0).admode().bits(0));
}
self.streaming = true;