Refactoring timer timestamping

This commit is contained in:
Ryan Summers 2020-12-07 17:58:36 +01:00
parent fc81c8b5d8
commit ec046bc42d
5 changed files with 91 additions and 75 deletions

View File

@ -14,8 +14,8 @@
///! both transfers are completed before reading the data. This is usually not significant for ///! both transfers are completed before reading the data. This is usually not significant for
///! busy-waiting because the transfers should complete at approximately the same time. ///! busy-waiting because the transfers should complete at approximately the same time.
use super::{ use super::{
hal, sampling_timer, DMAReq, DmaConfig, MemoryToPeripheral, hal, timers, DMAReq, DmaConfig, MemoryToPeripheral, PeripheralToMemory,
PeripheralToMemory, Priority, TargetAddress, Transfer, SAMPLE_BUFFER_SIZE, Priority, TargetAddress, Transfer, SAMPLE_BUFFER_SIZE,
}; };
// The following data is written by the timer ADC sample trigger into each of the SPI TXFIFOs. Note // The following data is written by the timer ADC sample trigger into each of the SPI TXFIFOs. Note
@ -38,12 +38,10 @@ macro_rules! adc_input {
/// $spi is used as a type for indicating a DMA transfer into the SPI TX FIFO /// $spi is used as a type for indicating a DMA transfer into the SPI TX FIFO
/// whenever the tim2 update dma request occurs. /// whenever the tim2 update dma request occurs.
struct $spi { struct $spi {
_channel: sampling_timer::tim2::$trigger_channel, _channel: timers::tim2::$trigger_channel,
} }
impl $spi { impl $spi {
pub fn new( pub fn new(_channel: timers::tim2::$trigger_channel) -> Self {
_channel: sampling_timer::tim2::$trigger_channel,
) -> Self {
Self { _channel } Self { _channel }
} }
} }
@ -100,7 +98,7 @@ macro_rules! adc_input {
hal::stm32::DMA1, hal::stm32::DMA1,
>, >,
data_stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>, data_stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>,
trigger_channel: sampling_timer::tim2::$trigger_channel, trigger_channel: timers::tim2::$trigger_channel,
) -> Self { ) -> Self {
// Generate DMA events when an output compare of the timer hitting zero (timer roll over) // Generate DMA events when an output compare of the timer hitting zero (timer roll over)
// occurs. // occurs.

View File

@ -4,7 +4,7 @@
///! configured to generate a DMA write into the SPI TXFIFO, which initiates a SPI transfer and ///! configured to generate a DMA write into the SPI TXFIFO, which initiates a SPI transfer and
///! results in DAC update for both channels. ///! results in DAC update for both channels.
use super::{ use super::{
hal, sampling_timer, DMAReq, DmaConfig, MemoryToPeripheral, TargetAddress, hal, timers, DMAReq, DmaConfig, MemoryToPeripheral, TargetAddress,
Transfer, SAMPLE_BUFFER_SIZE, Transfer, SAMPLE_BUFFER_SIZE,
}; };
@ -22,12 +22,12 @@ macro_rules! dac_output {
/// $spi is used as a type for indicating a DMA transfer into the SPI TX FIFO /// $spi is used as a type for indicating a DMA transfer into the SPI TX FIFO
struct $spi { struct $spi {
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>, spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>,
_channel: sampling_timer::tim2::$trigger_channel, _channel: timers::tim2::$trigger_channel,
} }
impl $spi { impl $spi {
pub fn new( pub fn new(
_channel: sampling_timer::tim2::$trigger_channel, _channel: timers::tim2::$trigger_channel,
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>, spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>,
) -> Self { ) -> Self {
Self { _channel, spi } Self { _channel, spi }
@ -73,7 +73,7 @@ macro_rules! dac_output {
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>,
stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>, stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>,
trigger_channel: sampling_timer::tim2::$trigger_channel, trigger_channel: timers::tim2::$trigger_channel,
) -> Self { ) -> Self {
// Generate DMA events when an output compare of the timer hitting zero (timer roll over) // Generate DMA events when an output compare of the timer hitting zero (timer roll over)
// occurs. // occurs.

View File

@ -1,20 +1,17 @@
use super::{hal, sampling_timer, DmaConfig, PeripheralToMemory, Transfer}; use super::{hal, timers, DmaConfig, PeripheralToMemory, Transfer};
const INPUT_BUFFER_SIZE: usize = 1; const INPUT_BUFFER_SIZE: usize = 1;
#[link_section = ".axisram.buffers"] #[link_section = ".axisram.buffers"]
static mut BUF0: [u16; INPUT_BUFFER_SIZE] = [0; INPUT_BUFFER_SIZE]; static mut BUF: [[u16; INPUT_BUFFER_SIZE]; 2] = [[0; INPUT_BUFFER_SIZE]; 2];
#[link_section = ".axisram.buffers"]
static mut BUF1: [u16; INPUT_BUFFER_SIZE] = [0; INPUT_BUFFER_SIZE];
pub struct InputStamper { pub struct InputStamper {
_di0_trigger: hal::gpio::gpioa::PA3<hal::gpio::Alternate<hal::gpio::AF1>>, _di0_trigger: hal::gpio::gpioa::PA3<hal::gpio::Alternate<hal::gpio::AF2>>,
timestamp_buffer: heapless::Vec<u16, heapless::consts::U128>, timestamp_buffer: heapless::Vec<u16, heapless::consts::U128>,
next_buffer: Option<&'static mut [u16; INPUT_BUFFER_SIZE]>, next_buffer: Option<&'static mut [u16; INPUT_BUFFER_SIZE]>,
transfer: Transfer< transfer: Transfer<
hal::dma::dma::Stream6<hal::stm32::DMA1>, hal::dma::dma::Stream6<hal::stm32::DMA1>,
sampling_timer::tim2::Channel4InputCapture, timers::tim5::Channel4InputCapture,
PeripheralToMemory, PeripheralToMemory,
&'static mut [u16; INPUT_BUFFER_SIZE], &'static mut [u16; INPUT_BUFFER_SIZE],
>, >,
@ -22,17 +19,19 @@ pub struct InputStamper {
impl InputStamper { impl InputStamper {
pub fn new( pub fn new(
trigger: hal::gpio::gpioa::PA3<hal::gpio::Alternate<hal::gpio::AF1>>, trigger: hal::gpio::gpioa::PA3<hal::gpio::Alternate<hal::gpio::AF2>>,
stream: hal::dma::dma::Stream6<hal::stm32::DMA1>, stream: hal::dma::dma::Stream6<hal::stm32::DMA1>,
timer_channel: sampling_timer::tim2::Channel4, timer_channel: timers::tim5::Channel4,
) -> Self { ) -> Self {
// Utilize the TIM2 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the // Utilize the TIM5 CH4 as an input capture channel - use TI4 (the DI0 input trigger) as the
// capture source. // capture source.
timer_channel.listen_dma(); timer_channel.listen_dma();
let input_capture = timer_channel.to_input_capture(sampling_timer::tim2::CC4S_A::TI4); let input_capture =
timer_channel.to_input_capture(timers::tim5::CC4S_A::TI4);
// Set up the DMA transfer. // Set up the DMA transfer.
let dma_config = DmaConfig::default() let dma_config = DmaConfig::default()
.transfer_complete_interrupt(true)
.memory_increment(true) .memory_increment(true)
.peripheral_increment(false); .peripheral_increment(false);
@ -40,7 +39,7 @@ impl InputStamper {
Transfer::init( Transfer::init(
stream, stream,
input_capture, input_capture,
unsafe { &mut BUF0 }, unsafe { &mut BUF[0] },
None, None,
dma_config, dma_config,
); );
@ -49,7 +48,7 @@ impl InputStamper {
Self { Self {
timestamp_buffer: heapless::Vec::new(), timestamp_buffer: heapless::Vec::new(),
next_buffer: unsafe { Some(&mut BUF1) }, next_buffer: unsafe { Some(&mut BUF[1]) },
transfer: timestamp_transfer, transfer: timestamp_transfer,
_di0_trigger: trigger, _di0_trigger: trigger,
} }

View File

@ -69,13 +69,13 @@ static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new();
mod adc; mod adc;
mod afe; mod afe;
mod dac; mod dac;
mod digital_input_stamper;
mod hrtimer;
mod design_parameters; mod design_parameters;
mod digital_input_stamper;
mod eeprom; mod eeprom;
mod hrtimer;
mod pounder; mod pounder;
mod sampling_timer;
mod server; mod server;
mod timers;
use adc::{Adc0Input, Adc1Input}; use adc::{Adc0Input, Adc1Input};
use dac::{Dac0Output, Dac1Output}; use dac::{Dac0Output, Dac1Output};
@ -285,9 +285,22 @@ const APP: () = {
&ccdr.clocks, &ccdr.clocks,
); );
let mut sampling_timer = sampling_timer::SamplingTimer::new(timer2); let mut sampling_timer = timers::SamplingTimer::new(timer2);
let sampling_timer_channels = sampling_timer.channels(); let sampling_timer_channels = sampling_timer.channels();
let mut timestamp_timer = {
// TODO: This needs to be precisely controlled via the prescaler of the timer.
let timer5 = dp.TIM5.timer(
(SAMPLE_FREQUENCY_KHZ / SAMPLE_BUFFER_SIZE as u32).khz(),
ccdr.peripheral.TIM5,
&ccdr.clocks,
);
timers::TimestampTimer::new(timer5)
};
let timestamp_timer_channels = timestamp_timer.channels();
// Configure the SPI interfaces to the ADCs and DACs. // Configure the SPI interfaces to the ADCs and DACs.
let adcs = { let adcs = {
let adc0 = { let adc0 = {
@ -770,16 +783,17 @@ const APP: () = {
cp.DWT.enable_cycle_counter(); cp.DWT.enable_cycle_counter();
let input_stamper = { let input_stamper = {
let trigger = gpioa.pa3.into_alternate_af1(); let trigger = gpioa.pa3.into_alternate_af2();
digital_input_stamper::InputStamper::new( digital_input_stamper::InputStamper::new(
trigger, trigger,
dma_streams.6, dma_streams.6,
sampling_timer_channels.ch4, timestamp_timer_channels.ch4,
) )
}; };
// Start sampling ADCs. // Start sampling ADCs.
sampling_timer.start(); sampling_timer.start();
timestamp_timer.start();
init::LateResources { init::LateResources {
afes: (afe0, afe1), afes: (afe0, afe1),
@ -797,11 +811,6 @@ const APP: () = {
} }
} }
#[task(binds=DMA1_STR6, resources=[input_stamper], priority = 2)]
fn digital_stamper(c: digital_stamper::Context) {
panic!("Timestamp overflow")
}
#[task(binds=DMA1_STR3, resources=[adcs, dacs, iir_state, iir_ch], priority=2)] #[task(binds=DMA1_STR3, resources=[adcs, dacs, iir_state, iir_ch], priority=2)]
fn process(c: process::Context) { fn process(c: process::Context) {
let adc_samples = [ let adc_samples = [
@ -833,6 +842,11 @@ const APP: () = {
c.resources.dacs.1.release_buffer(dac1); c.resources.dacs.1.release_buffer(dac1);
} }
#[task(binds=DMA1_STR6, priority = 2)]
fn digital_stamper(_: digital_stamper::Context) {
panic!("Timestamp overflow")
}
#[idle(resources=[net_interface, pounder, mac_addr, eth_mac, iir_state, iir_ch, afes])] #[idle(resources=[net_interface, pounder, mac_addr, eth_mac, iir_state, iir_ch, afes])]
fn idle(mut c: idle::Context) -> ! { fn idle(mut c: idle::Context) -> ! {
let mut socket_set_entries: [_; 8] = Default::default(); let mut socket_set_entries: [_; 8] = Default::default();

View File

@ -1,50 +1,51 @@
///! The sampling timer is used for managing ADC sampling and external reference timestamping. ///! The sampling timer is used for managing ADC sampling and external reference timestamping.
use super::hal; use super::hal;
/// The timer used for managing ADC sampling.
pub struct SamplingTimer {
timer: hal::timer::Timer<hal::stm32::TIM2>,
channels: Option<tim2::Channels>,
}
impl SamplingTimer {
/// Construct the sampling timer.
pub fn new(mut timer: hal::timer::Timer<hal::stm32::TIM2>) -> Self {
timer.pause();
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.
channels: unsafe { Some(tim2::Channels::new()) },
}
}
/// Get the timer capture/compare channels.
pub fn channels(&mut self) -> tim2::Channels {
self.channels.take().unwrap()
}
/// Start the sampling timer.
pub fn start(&mut self) {
self.timer.reset_counter();
self.timer.resume();
}
}
macro_rules! timer_channels { macro_rules! timer_channels {
($TY:ty) => { ($name:ident, $TY:ident) => {
paste::paste! { paste::paste! {
/// The timer used for managing ADC sampling.
pub struct $name {
timer: hal::timer::Timer<hal::stm32::[< $TY >]>,
channels: Option<[< $TY:lower >]::Channels>,
}
impl $name {
/// Construct the sampling timer.
pub fn new(mut timer: hal::timer::Timer<hal::stm32::[< $TY>]>) -> Self {
timer.pause();
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.
channels: unsafe { Some([< $TY:lower >]::Channels::new()) },
}
}
/// Get the timer capture/compare channels.
pub fn channels(&mut self) -> [< $TY:lower >]::Channels {
self.channels.take().unwrap()
}
/// Start the sampling timer.
pub fn start(&mut self) {
self.timer.reset_counter();
self.timer.resume();
}
}
pub mod [< $TY:lower >] { pub mod [< $TY:lower >] {
pub use hal::stm32::[< $TY:lower >]::ccmr1_input::{CC1S_A, CC2S_A}; pub use hal::stm32::tim2::ccmr1_input::{CC1S_A, CC2S_A};
pub use hal::stm32::[< $TY:lower >]::ccmr2_input::{CC3S_A, CC4S_A}; pub use hal::stm32::tim2::ccmr2_input::{CC3S_A, CC4S_A};
use stm32h7xx_hal as hal; use stm32h7xx_hal as hal;
use hal::dma::{traits::TargetAddress, PeripheralToMemory, dma::DMAReq}; use hal::dma::{traits::TargetAddress, PeripheralToMemory, dma::DMAReq};
use hal::stm32::TIM2; use hal::stm32::$TY;
/// The channels representing the timer. /// The channels representing the timer.
pub struct Channels { pub struct Channels {
@ -92,6 +93,7 @@ macro_rules! timer_channels {
} }
/// Allow the channel to generate DMA requests. /// Allow the channel to generate DMA requests.
#[allow(dead_code)]
pub fn listen_dma(&self) { pub fn listen_dma(&self) {
let regs = unsafe { &*<$TY>::ptr() }; let regs = unsafe { &*<$TY>::ptr() };
regs.dier.modify(|_, w| w.[< cc $index de >]().set_bit()); regs.dier.modify(|_, w| w.[< cc $index de >]().set_bit());
@ -101,6 +103,7 @@ macro_rules! timer_channels {
/// ///
/// # Args /// # Args
/// * `value` - The value to compare the sampling timer's counter against. /// * `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: u32) {
let regs = unsafe { &*<$TY>::ptr() }; let regs = unsafe { &*<$TY>::ptr() };
assert!(value <= regs.arr.read().bits()); assert!(value <= regs.arr.read().bits());
@ -113,7 +116,8 @@ macro_rules! timer_channels {
/// ///
/// # Args /// # Args
/// * `input` - The input source for the input capture event. /// * `input` - The input source for the input capture event.
pub fn to_input_capture(self, input: hal::stm32::[<$TY:lower>]::[< $ccmrx _input >]::[< CC $index S_A >]) -> [< Channel $index InputCapture >]{ #[allow(dead_code)]
pub fn to_input_capture(self, input: hal::stm32::tim2::[< $ccmrx _input >]::[< CC $index S_A >]) -> [< Channel $index InputCapture >]{
let regs = unsafe { &*<$TY>::ptr() }; let regs = unsafe { &*<$TY>::ptr() };
regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input)); regs.[< $ccmrx _input >]().modify(|_, w| w.[< cc $index s>]().variant(input));
@ -135,4 +139,5 @@ macro_rules! timer_channels {
}; };
} }
timer_channels!(TIM2); timer_channels!(SamplingTimer, TIM2);
timer_channels!(TimestampTimer, TIM5);