Fixing timing synchronization

master
Ryan Summers 2021-01-12 13:29:15 +01:00
parent 91975993cf
commit 6b170c25ed
3 changed files with 18 additions and 20 deletions

View File

@ -1,12 +1,12 @@
///! Stabilizer ADC management interface ///! Stabilizer ADC management interface
///! ///!
///! The Stabilizer ADCs utilize three DMA channels: one to trigger sampling, one to collect ///! The Stabilizer ADCs utilize three DMA channels: one to trigger sampling, one to collect
///! samples, and one to clear the TXTF flag betwen samples. The SPI interfaces are configured ///! samples, and one to clear the EOT flag betwen samples. The SPI interfaces are configured
///! for receiver-only operation. A timer channel is ///! for receiver-only operation. A timer channel is
///! configured to generate a DMA write into the SPI CR1 register, which initiates a SPI transfer and ///! configured to generate a DMA write into the SPI CR1 register, which initiates a SPI transfer and
///! results in a single ADC sample read for both channels. A separate timer channel is configured to ///! results in a single ADC sample read for both channels. A separate timer channel is configured to
///! occur immediately before the trigger channel, which initiates a write to the IFCR (flag-clear) ///! occur immediately before the trigger channel, which initiates a write to the IFCR (flag-clear)
///! register to clear the TXTF flag, which allows for a new transmission to be generated by the ///! register to clear the EOT flag, which allows for a new transmission to be generated by the
///! trigger channel. ///! trigger channel.
///! ///!
///! In order to read multiple samples without interrupting the CPU, a separate DMA transfer is ///! In order to read multiple samples without interrupting the CPU, a separate DMA transfer is
@ -26,13 +26,13 @@ use super::{
// transfer. Data in AXI SRAM is not initialized on boot, so the contents are random. This value is // transfer. Data in AXI SRAM is not initialized on boot, so the contents are random. This value is
// initialized during setup. // initialized during setup.
#[link_section = ".axisram.buffers"] #[link_section = ".axisram.buffers"]
static mut SPI_START: [u32; 1] = [0x00]; static mut SPI_START: [u32; 1] = [0x00; 1];
// The following data is written by the timer flag clear trigger into the SPI IFCR register to clear // The following data is written by the timer flag clear trigger into the SPI IFCR register to clear
// the TXTF flag. Data in AXI SRAM is not initialized on boot, so the contents are random. This // the EOT flag. Data in AXI SRAM is not initialized on boot, so the contents are random. This
// value is initialized during setup. // value is initialized during setup.
#[link_section = ".axisram.buffers"] #[link_section = ".axisram.buffers"]
static mut SPI_TXTF_CLEAR: [u32; 1] = [0x00]; static mut SPI_EOT_CLEAR: [u32; 1] = [0x00];
// The following global buffers are used for the ADC sample DMA transfers. Two buffers are used for // The following global buffers are used for the ADC sample DMA transfers. Two buffers are used for
// each transfer in a ping-pong buffer configuration (one is being acquired while the other is being // each transfer in a ping-pong buffer configuration (one is being acquired while the other is being
@ -101,7 +101,7 @@ macro_rules! adc_input {
const REQUEST_LINE: Option<u8> = Some(DMAReq::$dma_clear_req as u8); const REQUEST_LINE: Option<u8> = Some(DMAReq::$dma_clear_req as u8);
/// Whenever the DMA request occurs, it should write into SPI's IFCR to clear the /// Whenever the DMA request occurs, it should write into SPI's IFCR to clear the
/// TXTF flag to allow the next transmission. /// EOT flag to allow the next transmission.
fn address(&self) -> usize { fn address(&self) -> usize {
// Note(unsafe): It is assumed that SPI is owned by another DMA transfer and // Note(unsafe): It is assumed that SPI is owned by another DMA transfer and
// this DMA is only used for writing to the configuration registers. // this DMA is only used for writing to the configuration registers.
@ -141,10 +141,10 @@ macro_rules! adc_input {
/// * `trigger_stream` - The DMA stream used to trigger each ADC transfer by /// * `trigger_stream` - The DMA stream used to trigger each ADC transfer by
/// writing a word into the SPI TX FIFO. /// writing a word into the SPI TX FIFO.
/// * `data_stream` - The DMA stream used to read samples received over SPI into a data buffer. /// * `data_stream` - The DMA stream used to read samples received over SPI into a data buffer.
/// * `clear_stream` - The DMA stream used to clear the TXTF flag in the SPI peripheral. /// * `clear_stream` - The DMA stream used to clear the EOT flag in the SPI peripheral.
/// * `trigger_channel` - The ADC sampling timer output compare channel for read triggers. /// * `trigger_channel` - The ADC sampling timer output compare channel for read triggers.
/// * `clear_channel` - The shadow sampling timer output compare channel used for /// * `clear_channel` - The shadow sampling timer output compare channel used for
/// clearing the SPI TXTF flag. /// clearing the SPI EOT flag.
pub fn new( pub fn new(
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Enabled, u16>, spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Enabled, u16>,
trigger_stream: hal::dma::dma::$trigger_stream< trigger_stream: hal::dma::dma::$trigger_stream<
@ -155,7 +155,7 @@ macro_rules! adc_input {
trigger_channel: timers::tim2::$trigger_channel, trigger_channel: timers::tim2::$trigger_channel,
clear_channel: timers::tim3::$clear_channel, clear_channel: timers::tim3::$clear_channel,
) -> Self { ) -> Self {
// The flag clear DMA transfer always clears the TXTF flag in the SPI // The flag clear DMA transfer always clears the EOT flag in the SPI
// peripheral. It has the highest priority to ensure it is completed before the // peripheral. It has the highest priority to ensure it is completed before the
// transfer trigger. // transfer trigger.
let clear_config = DmaConfig::default() let clear_config = DmaConfig::default()
@ -163,7 +163,7 @@ macro_rules! adc_input {
.circular_buffer(true); .circular_buffer(true);
unsafe { unsafe {
SPI_TXTF_CLEAR[0] = 1 << 4; SPI_EOT_CLEAR[0] = 1 << 3;
} }
// Generate DMA events when the timer hits zero (roll-over). This must be before // Generate DMA events when the timer hits zero (roll-over). This must be before
@ -183,7 +183,7 @@ macro_rules! adc_input {
// Note(unsafe): Because this is a Memory->Peripheral transfer, this data is // Note(unsafe): Because this is a Memory->Peripheral transfer, this data is
// never actually modified. It technically only needs to be immutably // never actually modified. It technically only needs to be immutably
// borrowed, but the current HAL API only supports mutable borrows. // borrowed, but the current HAL API only supports mutable borrows.
unsafe { &mut SPI_TXTF_CLEAR }, unsafe { &mut SPI_EOT_CLEAR },
None, None,
clear_config, clear_config,
); );
@ -191,7 +191,7 @@ macro_rules! adc_input {
// Generate DMA events when an output compare of the timer hits the specified // Generate DMA events when an output compare of the timer hits the specified
// value. // value.
trigger_channel.listen_dma(); trigger_channel.listen_dma();
trigger_channel.to_output_compare(1); trigger_channel.to_output_compare(2);
// The trigger stream constantly writes to the SPI CR1 using a static word // The trigger stream constantly writes to the SPI CR1 using a static word
// (which is a static value to enable the SPI transfer). Thus, neither the // (which is a static value to enable the SPI transfer). Thus, neither the
@ -262,10 +262,7 @@ macro_rules! adc_input {
// Each transaction is 1 word (16 bytes). // Each transaction is 1 word (16 bytes).
spi.inner().cr2.modify(|_, w| w.tsize().bits(1)); spi.inner().cr2.modify(|_, w| w.tsize().bits(1));
// Enable SPI and start it in infinite transaction mode.
spi.inner().cr1.modify(|_, w| w.spe().set_bit()); spi.inner().cr1.modify(|_, w| w.spe().set_bit());
spi.inner().cr1.modify(|_, w| w.cstart().started());
}); });
clear_transfer.start(|_| {}); clear_transfer.start(|_| {});

View File

@ -298,6 +298,7 @@ const APP: () = {
// Configure the timer to count at the designed tick rate. We will manually set the // Configure the timer to count at the designed tick rate. We will manually set the
// period below. // period below.
timer2.pause(); timer2.pause();
timer2.reset_counter();
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY); timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
let mut sampling_timer = timers::SamplingTimer::new(timer2); let mut sampling_timer = timers::SamplingTimer::new(timer2);
@ -318,6 +319,7 @@ const APP: () = {
// Configure the timer to count at the designed tick rate. We will manually set the // Configure the timer to count at the designed tick rate. We will manually set the
// period below. // period below.
timer3.pause(); timer3.pause();
timer3.reset_counter();
timer3.set_tick_freq(design_parameters::TIMER_FREQUENCY); timer3.set_tick_freq(design_parameters::TIMER_FREQUENCY);
let mut shadow_sampling_timer = let mut shadow_sampling_timer =
@ -925,10 +927,6 @@ const APP: () = {
#[cfg(not(feature = "pounder_v1_1"))] #[cfg(not(feature = "pounder_v1_1"))]
let pounder_stamper = None; let pounder_stamper = None;
// Force an update of the shadow sampling timer configuration and enable it. It will not
// start counting until the sampling timer starts due to the slave mode configuration.
shadow_sampling_timer.start();
// Start sampling ADCs. // Start sampling ADCs.
sampling_timer.start(); sampling_timer.start();
timestamp_timer.start(); timestamp_timer.start();
@ -951,7 +949,7 @@ const APP: () = {
} }
} }
#[task(binds=DMA1_STR3, resources=[pounder_stamper, adcs, dacs, iir_state, iir_ch, dds_output, input_stamper], priority=2)] #[task(binds=DMA1_STR4, resources=[pounder_stamper, adcs, dacs, iir_state, iir_ch, dds_output, input_stamper], priority=2)]
fn process(c: process::Context) { fn process(c: process::Context) {
if let Some(stamper) = c.resources.pounder_stamper { if let Some(stamper) = c.resources.pounder_stamper {
let pounder_timestamps = stamper.acquire_buffer(); let pounder_timestamps = stamper.acquire_buffer();

View File

@ -98,6 +98,9 @@ macro_rules! timer_channels {
pub fn set_period_ticks(&mut self, period: $size) { pub fn set_period_ticks(&mut self, period: $size) {
let regs = unsafe { &*hal::stm32::$TY::ptr() }; let regs = unsafe { &*hal::stm32::$TY::ptr() };
regs.arr.write(|w| w.arr().bits(period)); regs.arr.write(|w| w.arr().bits(period));
// Force the new period to take effect immediately.
self.timer.apply_freq();
} }
/// Clock the timer from an external source. /// Clock the timer from an external source.