From f8cab17ffc8af756f1d2bba617398fb46ed07b96 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 9 Dec 2020 13:44:26 +0100 Subject: [PATCH] Adding WIP for pounder timestamps --- src/main.rs | 15 ++++ src/pounder/mod.rs | 1 + src/pounder/timestamp.rs | 176 +++++++++++++++++++++++++++++++++++++++ src/timers.rs | 38 +++++++-- 4 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 src/pounder/timestamp.rs diff --git a/src/main.rs b/src/main.rs index 308a4f0..7cff452 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,6 +220,7 @@ const APP: () = { mac_addr: net::wire::EthernetAddress, pounder: Option, + pounder_stamper: pounder::timestamp::Timestamper, // Format: iir_state[ch][cascade-no][coeff] #[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])] @@ -850,6 +851,19 @@ const APP: () = { ) }; + let pounder_stamper = { + let etr_pin = gpioa.pa0.into_alternate_af3(); + let timestamp_timer = pounder::timestamp::Timer::new( + dp.TIM8, + ccdr.peripheral.TIM8, + etr_pin, + pounder::timestamp::Prescaler::Div4, + sampling_timer.update_event(), + 128, + ); + pounder::timestamp::Timestamper::new(timestamp_timer, dma_streams.7) + }; + // Start sampling ADCs. sampling_timer.start(); timestamp_timer.start(); @@ -863,6 +877,7 @@ const APP: () = { input_stamper, dds_output, pounder: pounder_devices, + pounder_stamper, eeprom_i2c, net_interface: network_interface, diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index c20d251..36a73fd 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; mod attenuators; mod dds_output; mod rf_power; +pub mod timestamp; pub use dds_output::DdsOutput; diff --git a/src/pounder/timestamp.rs b/src/pounder/timestamp.rs new file mode 100644 index 0000000..44cd668 --- /dev/null +++ b/src/pounder/timestamp.rs @@ -0,0 +1,176 @@ +///! ADC sample timestamper using external Pounder reference clock. +use stm32h7xx_hal as hal; + +use hal::{ + dma::{ + dma::{DMAReq, DmaConfig}, + traits::TargetAddress, + PeripheralToMemory, Transfer, + }, + rcc::ResetEnable, +}; + +use crate::{timers, SAMPLE_BUFFER_SIZE}; + +struct TimestampDma { + _dma_request: timers::tim2::UpdateEvent, +} + +pub struct Timer { + timer: hal::stm32::TIM8, + dma: Option, +} + +// Note(unsafe): This is safe to implement because we take ownership of the DMA request. +// Additionally, we only read the registers in PeripheralToMemory mode, so it is always safe to +// access them. +unsafe impl TargetAddress for TimestampDma { + // TIM8 is a 16-bit timer. + type MemSize = u16; + + // Note: It is safe for us to us the TIM2_UPDATE DMA request because the Timer + // maintains ownership of the UpdateEvent object for this timer. + const REQUEST_LINE: Option = Some(DMAReq::TIM2_UP as u8); + + fn address(&self) -> u32 { + let regs = unsafe { &*hal::stm32::TIM8::ptr() }; + ®s.cnt as *const _ as u32 + } +} + +pub enum Prescaler { + Div4, + Div8, +} + +impl Timer { + pub fn new( + timer: hal::stm32::TIM8, + prec: hal::rcc::rec::Tim8, + _external_source: hal::gpio::gpioa::PA0< + hal::gpio::Alternate, + >, + prescaler: Prescaler, + update_event: timers::tim2::UpdateEvent, + period: u16, + ) -> Self { + prec.reset().enable(); + + let divisor = match prescaler { + Prescaler::Div4 => hal::stm32::tim1::smcr::ETPS_A::DIV4, + Prescaler::Div8 => hal::stm32::tim1::smcr::ETPS_A::DIV8, + }; + + // Configure the timer to utilize an external clock source with the provided divider on the + // ETR. + timer + .smcr + .modify(|_, w| w.etps().variant(divisor).ece().set_bit()); + + // Set the timer period and generate an update of the timer registers so that ARR takes + // effect. + timer.arr.write(|w| w.arr().bits(period)); + + timer.egr.write(|w| w.ug().set_bit()); + + // Allow TIM2 updates to generate DMA requests. + update_event.listen_dma(); + + // Enable the timer. + timer.cr1.modify(|_, w| w.cen().set_bit()); + + let dma = TimestampDma { + _dma_request: update_event, + }; + + Self { + timer, + dma: Some(dma), + } + } + + /// Update the timer period. + /// + /// # Note + /// Timer period updates will take effect after the current period elapses. + fn set_period(&mut self, period: u16) { + // Modify the period register. + self.timer.arr.write(|w| w.arr().bits(period)); + } + + fn dma_transfer(&mut self) -> TimestampDma { + self.dma.take().unwrap() + } +} + +#[link_section = ".axisram.buffers"] +static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3]; + +pub struct Timestamper { + next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, + timer: Timer, + transfer: Transfer< + hal::dma::dma::Stream7, + TimestampDma, + PeripheralToMemory, + &'static mut [u16; SAMPLE_BUFFER_SIZE], + >, +} + +impl Timestamper { + pub fn new( + mut timer: Timer, + stream: hal::dma::dma::Stream7, + ) -> Self { + let config = DmaConfig::default() + .memory_increment(true) + .circular_buffer(true) + .double_buffer(true); + + // The data transfer is always a transfer of data from the peripheral to a RAM buffer. + let mut data_transfer: Transfer<_, _, PeripheralToMemory, _> = + Transfer::init( + stream, + timer.dma_transfer(), + // Note(unsafe): The BUF[0] and BUF[1] is "owned" by this peripheral. + // It shall not be used anywhere else in the module. + unsafe { &mut BUF[0] }, + unsafe { Some(&mut BUF[1]) }, + config, + ); + + data_transfer.start(|_| {}); + + Self { + timer, + transfer: data_transfer, + next_buffer: unsafe { Some(&mut BUF[2]) }, + } + } + + pub fn update_period(&mut self, period: u16) { + self.timer.set_period(period); + } + + /// Obtain a buffer filled with timestamps. + /// + /// # Returns + /// A reference to the underlying buffer that has been filled with timestamps. + pub fn acquire_buffer(&mut self) -> &[u16; SAMPLE_BUFFER_SIZE] { + // Wait for the transfer to fully complete before continuing. + // Note: If a device hangs up, check that this conditional is passing correctly, as there is + // no time-out checks here in the interest of execution speed. + while !self.transfer.get_transfer_complete_flag() {} + + let next_buffer = self.next_buffer.take().unwrap(); + + // Start the next transfer. + self.transfer.clear_interrupts(); + let (prev_buffer, _, _) = + self.transfer.next_transfer(next_buffer).unwrap(); + + self.next_buffer.replace(prev_buffer); // .unwrap_none() https://github.com/rust-lang/rust/issues/62633 + + self.next_buffer.as_ref().unwrap() + } +} diff --git a/src/timers.rs b/src/timers.rs index 03bc0aa..3025edf 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -9,6 +9,7 @@ macro_rules! timer_channels { pub struct $name { timer: hal::timer::Timer]>, channels: Option<[< $TY:lower >]::Channels>, + update_event: Option<[< $TY:lower >]::UpdateEvent>, } impl $name { @@ -18,12 +19,13 @@ macro_rules! timer_channels { Self { timer, - // Note(unsafe): Once these channels are taken, we guarantee that we do not modify any - // of the underlying timer channel registers, as ownership of the channels is now - // provided through the associated channel structures. We additionally guarantee this - // can only be called once because there is only one Timer2 and this resource takes - // ownership of it once instantiated. + // Note(unsafe): Once these channels are taken, we guarantee that we do not + // modify any of the underlying timer channel registers, as ownership of the + // channels is now provided through the associated channel structures. We + // additionally guarantee this can only be called once because there is only + // one Timer2 and this resource takes ownership of it once instantiated. channels: unsafe { Some([< $TY:lower >]::Channels::new()) }, + update_event: unsafe { Some([< $TY:lower >]::UpdateEvent::new()) }, } } @@ -32,6 +34,12 @@ macro_rules! timer_channels { self.channels.take().unwrap() } + /// Get the timer update event. + #[allow(dead_code)] + pub fn update_event(&mut self) -> [< $TY:lower >]::UpdateEvent { + self.update_event.take().unwrap() + } + /// Get the period of the timer. #[allow(dead_code)] pub fn get_period(&self) -> u32 { @@ -64,6 +72,26 @@ macro_rules! timer_channels { use hal::dma::{traits::TargetAddress, PeripheralToMemory, dma::DMAReq}; use hal::stm32::$TY; + pub struct UpdateEvent {} + + impl UpdateEvent { + /// Create a new update event + /// + /// Note(unsafe): This is only safe to call once. + #[allow(dead_code)] + pub unsafe fn new() -> Self { + Self {} + } + + /// Enable DMA requests upon timer updates. + #[allow(dead_code)] + pub fn listen_dma(&self) { + // Note(unsafe): We perofmr only atomic operations on the timer registers. + let regs = unsafe { &*<$TY>::ptr() }; + regs.dier.modify(|_, w| w.ude().set_bit()); + } + } + /// The channels representing the timer. pub struct Channels { pub ch1: Channel1,