Adding WIP for pounder timestamps
This commit is contained in:
parent
2e0681ebcc
commit
f8cab17ffc
15
src/main.rs
15
src/main.rs
@ -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,
|
||||
|
@ -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
176
src/pounder/timestamp.rs
Normal 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() };
|
||||
®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<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()
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user