Fixing timing synchronization

This commit is contained in:
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
///!
///! 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
///! 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
///! 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.
///!
///! 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
// initialized during setup.
#[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 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.
#[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
// 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);
/// 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 {
// 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.
@ -141,10 +141,10 @@ macro_rules! adc_input {
/// * `trigger_stream` - The DMA stream used to trigger each ADC transfer by
/// writing a word into the SPI TX FIFO.
/// * `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.
/// * `clear_channel` - The shadow sampling timer output compare channel used for
/// clearing the SPI TXTF flag.
/// clearing the SPI EOT flag.
pub fn new(
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Enabled, u16>,
trigger_stream: hal::dma::dma::$trigger_stream<
@ -155,7 +155,7 @@ macro_rules! adc_input {
trigger_channel: timers::tim2::$trigger_channel,
clear_channel: timers::tim3::$clear_channel,
) -> 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
// transfer trigger.
let clear_config = DmaConfig::default()
@ -163,7 +163,7 @@ macro_rules! adc_input {
.circular_buffer(true);
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
@ -183,7 +183,7 @@ macro_rules! adc_input {
// Note(unsafe): Because this is a Memory->Peripheral transfer, this data is
// never actually modified. It technically only needs to be immutably
// borrowed, but the current HAL API only supports mutable borrows.
unsafe { &mut SPI_TXTF_CLEAR },
unsafe { &mut SPI_EOT_CLEAR },
None,
clear_config,
);
@ -191,7 +191,7 @@ macro_rules! adc_input {
// Generate DMA events when an output compare of the timer hits the specified
// value.
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
// (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).
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.cstart().started());
});
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
// period below.
timer2.pause();
timer2.reset_counter();
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
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
// period below.
timer3.pause();
timer3.reset_counter();
timer3.set_tick_freq(design_parameters::TIMER_FREQUENCY);
let mut shadow_sampling_timer =
@ -925,10 +927,6 @@ const APP: () = {
#[cfg(not(feature = "pounder_v1_1"))]
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.
sampling_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) {
if let Some(stamper) = c.resources.pounder_stamper {
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) {
let regs = unsafe { &*hal::stm32::$TY::ptr() };
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.