From 72d14adfbf8de8f4d12fdd0c9c010d0d666a81db Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 9 Dec 2020 18:19:33 +0100 Subject: [PATCH] Adding support for pounder ETR timestamping --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/adc.rs | 4 +- src/dac.rs | 4 +- src/digital_input_stamper.rs | 2 +- src/main.rs | 31 ++++++--- src/pounder/timestamp.rs | 130 +++++++---------------------------- src/timers.rs | 119 ++++++++++++++++++++++++++------ 8 files changed, 150 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edc2864..6a2a6f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -517,7 +517,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.8.0" -source = "git+https://github.com/quartiq/stm32h7xx-hal?branch=feature/number-of-transfers#e70a78788e74be5281321213b53e8cd1d213550e" +source = "git+https://github.com/quartiq/stm32h7xx-hal?branch=feature/dma-buffer-swap/num-transfers#b87614f432a635e904dea2383ff481f3cc002e80" dependencies = [ "bare-metal 1.0.0", "cast", diff --git a/Cargo.toml b/Cargo.toml index f1acbe0..9bfc570 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ default-features = false [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] git = "https://github.com/quartiq/stm32h7xx-hal" -branch = "feature/number-of-transfers" +branch = "feature/dma-buffer-swap/num-transfers" [features] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] diff --git a/src/adc.rs b/src/adc.rs index 8d2b61a..f6e937e 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -58,11 +58,11 @@ macro_rules! adc_input { /// Whenever the DMA request occurs, it should write into SPI's TX FIFO to start a DMA /// transfer. - fn address(&self) -> u32 { + fn address(&self) -> usize { // Note(unsafe): It is assumed that SPI is owned by another DMA transfer and this DMA is // only used for the transmit-half of DMA. let regs = unsafe { &*hal::stm32::$spi::ptr() }; - ®s.txdr as *const _ as u32 + ®s.txdr as *const _ as usize } } diff --git a/src/dac.rs b/src/dac.rs index 06a6362..abea097 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -45,8 +45,8 @@ macro_rules! dac_output { const REQUEST_LINE: Option = Some(DMAReq::$dma_req as u8); /// Whenever the DMA request occurs, it should write into SPI's TX FIFO. - fn address(&self) -> u32 { - &self.spi.inner().txdr as *const _ as u32 + fn address(&self) -> usize { + &self.spi.inner().txdr as *const _ as usize } } diff --git a/src/digital_input_stamper.rs b/src/digital_input_stamper.rs index d4204f3..8469195 100644 --- a/src/digital_input_stamper.rs +++ b/src/digital_input_stamper.rs @@ -75,7 +75,7 @@ impl InputStamper { // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // capture source. let input_capture = - timer_channel.to_input_capture(timers::tim5::CC4S_A::TI4); + timer_channel.to_input_capture(timers::CaptureTrigger::Input24); // For small batch sizes, the overhead of DMA can become burdensome to the point where // timing is not met. The DMA requires 500ns overhead, whereas a direct register read only diff --git a/src/main.rs b/src/main.rs index 9201de9..30ed33c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,10 +58,10 @@ use heapless::{consts::*, String}; // The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is // equal to 10ns per tick. -const ADC_SAMPLE_TICKS: u32 = 256; +const ADC_SAMPLE_TICKS: u32 = 128; // The desired ADC sample processing buffer size. -const SAMPLE_BUFFER_SIZE: usize = 1; +const SAMPLE_BUFFER_SIZE: usize = 8; // The number of cascaded IIR biquads per channel. Select 1 or 2! const IIR_CASCADE_LENGTH: usize = 1; @@ -853,15 +853,23 @@ 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, + + // The frequency in the constructor is dont-care, as we will modify the period + clock + // source manually below. + let tim8 = + dp.TIM8.timer(1.khz(), ccdr.peripheral.TIM8, &ccdr.clocks); + let mut timestamp_timer = timers::PounderTimestampTimer::new(tim8); + timestamp_timer.set_external_clock(timers::Prescaler::Div4); + timestamp_timer.set_period(128); + let tim8_channels = timestamp_timer.channels(); + + pounder::timestamp::Timestamper::new( + timestamp_timer, + dma_streams.7, + tim8_channels.ch1, + &mut sampling_timer, etr_pin, - pounder::timestamp::Prescaler::Div4, - sampling_timer.update_event(), - 128, - ); - pounder::timestamp::Timestamper::new(timestamp_timer, dma_streams.7) + ) }; // Start sampling ADCs. @@ -888,6 +896,8 @@ const APP: () = { #[task(binds=DMA1_STR3, resources=[pounder_stamper, adcs, dacs, iir_state, iir_ch, dds_output, input_stamper], priority=2)] fn process(c: process::Context) { + let _pounder_timestamps = c.resources.pounder_stamper.acquire_buffer(); + let adc_samples = [ c.resources.adcs.0.acquire_buffer(), c.resources.adcs.1.acquire_buffer(), @@ -897,7 +907,6 @@ const APP: () = { c.resources.dacs.1.acquire_buffer(), ]; - let _pounder_timestamps = c.resources.pounder_stamper.acquire_buffer(); let _timestamps = c.resources.input_stamper.acquire_buffer(); for channel in 0..adc_samples.len() { diff --git a/src/pounder/timestamp.rs b/src/pounder/timestamp.rs index 44cd668..295cb12 100644 --- a/src/pounder/timestamp.rs +++ b/src/pounder/timestamp.rs @@ -1,117 +1,19 @@ ///! 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 hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; 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, + timer: timers::PounderTimestampTimer, transfer: Transfer< hal::dma::dma::Stream7, - TimestampDma, + timers::tim8::Channel1InputCapture, PeripheralToMemory, &'static mut [u16; SAMPLE_BUFFER_SIZE], >, @@ -119,19 +21,36 @@ pub struct Timestamper { impl Timestamper { pub fn new( - mut timer: Timer, + mut timestamp_timer: timers::PounderTimestampTimer, stream: hal::dma::dma::Stream7, + capture_channel: timers::tim8::Channel1, + sampling_timer: &mut timers::SamplingTimer, + _clock_input: hal::gpio::gpioa::PA0< + hal::gpio::Alternate, + >, ) -> Self { let config = DmaConfig::default() .memory_increment(true) .circular_buffer(true) .double_buffer(true); + // The sampling timer should generate a trigger output when CH1 comparison occurs. + sampling_timer.generate_trigger(timers::TriggerGenerator::ComparePulse); + + // The timestamp timer trigger input should use TIM2 (SamplingTimer)'s trigger, which is + // mapped to ITR1. + timestamp_timer.set_trigger_source(timers::TriggerSource::Trigger1); + + // The capture channel should capture whenever the trigger input occurs. + let input_capture = capture_channel + .to_input_capture(timers::CaptureTrigger::TriggerInput); + input_capture.listen_dma(); + // 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(), + input_capture, // 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] }, @@ -139,10 +58,10 @@ impl Timestamper { config, ); - data_transfer.start(|_| {}); + data_transfer.start(|capture_channel| capture_channel.enable()); Self { - timer, + timer: timestamp_timer, transfer: data_transfer, next_buffer: unsafe { Some(&mut BUF[2]) }, } @@ -165,7 +84,6 @@ impl Timestamper { 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(); diff --git a/src/timers.rs b/src/timers.rs index 3025edf..1292dab 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -1,8 +1,40 @@ ///! The sampling timer is used for managing ADC sampling and external reference timestamping. use super::hal; +#[allow(dead_code)] +pub enum CaptureTrigger { + Input13 = 0b01, + Input24 = 0b10, + TriggerInput = 0b11, +} + +#[allow(dead_code)] +pub enum TriggerGenerator { + Reset = 0b000, + Enable = 0b001, + Update = 0b010, + ComparePulse = 0b011, + Ch1Compare = 0b100, + Ch2Compare = 0b101, + Ch3Compare = 0b110, + Ch4Compare = 0b111, +} + +#[allow(dead_code)] +pub enum TriggerSource { + Trigger0 = 0, + Trigger1 = 0b01, + Trigger2 = 0b10, + Trigger3 = 0b11, +} + +pub enum Prescaler { + Div4 = 0b10, + Div8 = 0b11, +} + macro_rules! timer_channels { - ($name:ident, $TY:ident, u32) => { + ($name:ident, $TY:ident, $size:ty) => { paste::paste! { /// The timer used for managing ADC sampling. @@ -14,6 +46,7 @@ macro_rules! timer_channels { impl $name { /// Construct the sampling timer. + #[allow(dead_code)] pub fn new(mut timer: hal::timer::Timer]>) -> Self { timer.pause(); @@ -30,6 +63,7 @@ macro_rules! timer_channels { } /// Get the timer capture/compare channels. + #[allow(dead_code)] pub fn channels(&mut self) -> [< $TY:lower >]::Channels { self.channels.take().unwrap() } @@ -42,19 +76,36 @@ macro_rules! timer_channels { /// Get the period of the timer. #[allow(dead_code)] - pub fn get_period(&self) -> u32 { + pub fn get_period(&self) -> $size { let regs = unsafe { &*hal::stm32::$TY::ptr() }; regs.arr.read().arr().bits() } /// Manually set the period of the timer. #[allow(dead_code)] - pub fn set_period(&mut self, period: u32) { + pub fn set_period(&mut self, period: $size) { let regs = unsafe { &*hal::stm32::$TY::ptr() }; regs.arr.write(|w| w.arr().bits(period)); } + /// Clock the timer from an external source. + /// + /// # Note: + /// * Currently, only an external source applied to ETR is supported. + /// + /// # Args + /// * `prescaler` - The prescaler to use for the external source. + #[allow(dead_code)] + pub fn set_external_clock(&mut self, prescaler: Prescaler) { + let regs = unsafe { &*hal::stm32::$TY::ptr() }; + regs.smcr.modify(|_, w| w.etps().bits(prescaler as u8).ece().set_bit()); + + // Use a DIV4 prescaler. + + } + /// Start the timer. + #[allow(dead_code)] pub fn start(mut self) { // Force a refresh of the frequency settings. self.timer.apply_freq(); @@ -62,12 +113,26 @@ macro_rules! timer_channels { self.timer.reset_counter(); self.timer.resume(); } + + #[allow(dead_code)] + pub fn generate_trigger(&mut self, source: TriggerGenerator) { + let regs = unsafe { &*hal::stm32::$TY::ptr() }; + // Note(unsafe) The TriggerGenerator enumeration is specified such that this is + // always in range. + regs.cr2.modify(|_, w| w.mms().bits(source as u8)); + + } + + #[allow(dead_code)] + pub fn set_trigger_source(&mut self, source: TriggerSource) { + let regs = unsafe { &*hal::stm32::$TY::ptr() }; + // Note(unsafe) The TriggerSource enumeration is specified such that this is + // always in range. + regs.smcr.modify(|_, w| unsafe { w.ts().bits(source as u8) } ); + } } pub mod [< $TY:lower >] { - pub use hal::stm32::tim2::ccmr1_input::{CC1S_A, CC2S_A}; - pub use hal::stm32::tim2::ccmr2_input::{CC3S_A, CC4S_A}; - use stm32h7xx_hal as hal; use hal::dma::{traits::TargetAddress, PeripheralToMemory, dma::DMAReq}; use hal::stm32::$TY; @@ -86,10 +151,17 @@ macro_rules! timer_channels { /// 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. + // Note(unsafe): We perform only atomic operations on the timer registers. let regs = unsafe { &*<$TY>::ptr() }; regs.dier.modify(|_, w| w.ude().set_bit()); } + + /// Trigger a DMA request manually + #[allow(dead_code)] + pub fn trigger(&self) { + let regs = unsafe { &*<$TY>::ptr() }; + regs.egr.write(|w| w.ug().set_bit()); + } } /// The channels representing the timer. @@ -104,6 +176,7 @@ macro_rules! timer_channels { /// Construct a new set of channels. /// /// Note(unsafe): This is only safe to call once. + #[allow(dead_code)] pub unsafe fn new() -> Self { Self { ch1: Channel1::new(), @@ -114,15 +187,15 @@ macro_rules! timer_channels { } } - timer_channels!(1, $TY, ccmr1); - timer_channels!(2, $TY, ccmr1); - timer_channels!(3, $TY, ccmr2); - timer_channels!(4, $TY, ccmr2); + timer_channels!(1, $TY, ccmr1, $size); + timer_channels!(2, $TY, ccmr1, $size); + timer_channels!(3, $TY, ccmr2, $size); + timer_channels!(4, $TY, ccmr2, $size); } } }; - ($index:expr, $TY:ty, $ccmrx:expr) => { + ($index:expr, $TY:ty, $ccmrx:expr, $size:ty) => { paste::paste! { /// A capture/compare channel of the timer. pub struct [< Channel $index >] {} @@ -135,6 +208,7 @@ macro_rules! timer_channels { /// /// Note(unsafe): This function must only be called once. Once constructed, the /// constructee guarantees to never modify the timer channel. + #[allow(dead_code)] unsafe fn new() -> Self { Self {} } @@ -151,9 +225,10 @@ macro_rules! timer_channels { /// # Args /// * `value` - The value to compare the sampling timer's counter against. #[allow(dead_code)] - pub fn to_output_compare(&self, value: u32) { + pub fn to_output_compare(&self, value: $size) { let regs = unsafe { &*<$TY>::ptr() }; - assert!(value <= regs.arr.read().bits()); + let arr = regs.arr.read().bits() as $size; + assert!(value <= arr); regs.[< ccr $index >].write(|w| w.ccr().bits(value)); regs.[< $ccmrx _output >]() .modify(|_, w| unsafe { w.[< cc $index s >]().bits(0) }); @@ -164,9 +239,12 @@ macro_rules! timer_channels { /// # Args /// * `input` - The input source for the input capture event. #[allow(dead_code)] - pub fn to_input_capture(self, input: hal::stm32::tim2::[< $ccmrx _input >]::[< CC $index S_A >]) -> [< Channel $index InputCapture >]{ + pub fn to_input_capture(self, input: super::CaptureTrigger) -> [< Channel $index InputCapture >]{ let regs = unsafe { &*<$TY>::ptr() }; - regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input)); + + // Note(unsafe): The bit configuration is guaranteed to be valid by the + // CaptureTrigger enum definition. + regs.[< $ccmrx _input >]().modify(|_, w| unsafe { w.[< cc $index s>]().bits(input as u8) }); [< Channel $index InputCapture >] {} } @@ -175,7 +253,7 @@ macro_rules! timer_channels { impl [< Channel $index InputCapture >] { /// Get the latest capture from the channel. #[allow(dead_code)] - pub fn latest_capture(&mut self) -> Option { + pub fn latest_capture(&mut self) -> Option<$size> { // Note(unsafe): This channel owns all access to the specific timer channel. // Only atomic operations on completed on the timer registers. let regs = unsafe { &*<$TY>::ptr() }; @@ -221,13 +299,13 @@ macro_rules! timer_channels { // is safe as it is only completed once per channel and each DMA request is allocated to // each channel as the owner. unsafe impl TargetAddress for [< Channel $index InputCapture >] { - type MemSize = u32; + type MemSize = $size; const REQUEST_LINE: Option = Some(DMAReq::[< $TY _CH $index >]as u8); - fn address(&self) -> u32 { + fn address(&self) -> usize { let regs = unsafe { &*<$TY>::ptr() }; - ®s.[] as *const _ as u32 + ®s.[] as *const _ as usize } } } @@ -236,3 +314,4 @@ macro_rules! timer_channels { timer_channels!(SamplingTimer, TIM2, u32); timer_channels!(TimestampTimer, TIM5, u32); +timer_channels!(PounderTimestampTimer, TIM8, u16);