Adding WIP for pounder timestamps

master
Ryan Summers 2020-12-09 13:44:26 +01:00
parent 2e0681ebcc
commit f8cab17ffc
4 changed files with 225 additions and 5 deletions

View File

@ -220,6 +220,7 @@ const APP: () = {
mac_addr: net::wire::EthernetAddress,
pounder: Option<pounder::PounderDevices>,
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,

View File

@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
mod attenuators;
mod dds_output;
mod rf_power;
pub mod timestamp;
pub use dds_output::DdsOutput;

176
src/pounder/timestamp.rs Normal file
View File

@ -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<TimestampDma>,
}
// 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<PeripheralToMemory> 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<u8> = Some(DMAReq::TIM2_UP as u8);
fn address(&self) -> u32 {
let regs = unsafe { &*hal::stm32::TIM8::ptr() };
&regs.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<hal::gpio::AF3>,
>,
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<hal::stm32::DMA1>,
TimestampDma,
PeripheralToMemory,
&'static mut [u16; SAMPLE_BUFFER_SIZE],
>,
}
impl Timestamper {
pub fn new(
mut timer: Timer,
stream: hal::dma::dma::Stream7<hal::stm32::DMA1>,
) -> 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()
}
}

View File

@ -9,6 +9,7 @@ macro_rules! timer_channels {
pub struct $name {
timer: hal::timer::Timer<hal::stm32::[< $TY >]>,
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,