From 28cb3906acc4f545f70e81001669b9639b116bca Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Sat, 7 Nov 2020 11:01:48 +0100 Subject: [PATCH] Adding WIP HRTimer --- src/hrtimer.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 13 ++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/hrtimer.rs diff --git a/src/hrtimer.rs b/src/hrtimer.rs new file mode 100644 index 0000000..ff074d1 --- /dev/null +++ b/src/hrtimer.rs @@ -0,0 +1,81 @@ +use crate::hal; +use hal::rcc::{CoreClocks, ResetEnable, rec}; + +pub enum Channel { + One, + Two, +} + +struct HighResTimerE { + master: hal::stm32::HRTIM_MASTER, + timer: hal::stm32::HRTIM_TIME, + common: hal::stm32::HRTIM_COMMON, + + clocks: CoreClocks, +} + +impl HighResTimerE { + pub fn new(timer_regs: hal::stm32::HRTIM_TIME, clocks: CoreClocks, prec: rec::Hrtim) -> Self { + let master = unsafe { &*hal::stm32::HRTIM_MASTER::ptr() }; + let common = unsafe { &*hal::stm32::HRTIM_COMMON::ptr() }; + prec.reset().enable(); + + Self { master, timer: timer_regs, common, clocks } + } + + pub fn configure_single_shot(&mut self, channel: Channel, set_duration: f32, set_offset: f32) { + // Disable the timer before configuration. + self.master.mcr.modify(|_, w| w.tecen().clear_bit()); + + // Configure the desired timer for single shot mode with set and reset of the specified + // channel at the desired durations. The HRTIM is on APB2 (D2 domain), and the kernel clock + // is the APB bus clock. + let minimum_duration = set_duration + set_offset; + + let source_frequency = self.clocks.timy_ker_ck; + let source_cycles = minimum_duration * source_frequency; + + // Determine the clock divider, which may be 1, 2, or 4. We will choose a clock divider that + // allows us the highest resolution per tick, so lower dividers are favored. + let divider = if source_cycles < 0xFFDF { + 1 + } else if (source_cycles / 2) < 0xFFDF { + 2 + } else if (source_cycles / 4) < 0xFFDF { + 4 + } else { + panic!("Unattainable timing parameters!"); + }; + + // The period register must be greater than or equal to 3 cycles. + assert!((source_cycles / divider) > 2); + + // We now have the prescaler and the period registers. Configure the timer. + self.timer.timecr.modify(|_, w| unsafe{w.ck_pscx().bits(divider)}) + self.timer.perer.write(|w| unsafe{w.per().bits(source_cycles / divider)}); + + // Configure the comparator 1 level. + self.timer.cmpe1r.write(|w| unsafe{w.cmp1().bits(set_offset * source_frequency)}); + + // Configure the set/reset signals. + // Set on compare with CMP1, reset upon reaching PER + match channel { + Channel::One => { + self.timer.sete1r().write(|w| w.cmp1().set_bit()); + self.timer.resete1r().write(|w| w.per().set_bit()); + }, + Channel::Two => { + self.timer.sete2r().write(|w| w.cmp1().set_bit()); + self.timer.resete2r().write(|w| w.per().set_bit()); + }, + } + + // Enable the timer now that it is configured. + self.master.mcr.modify(|_, w| w.tecen().set_bit()); + } + + pub fn trigger(&mut self) { + // Generate a reset event to force the timer to start counting. + self.common.cr2.write(|w| w.terst().set_bit()); + } +} diff --git a/src/main.rs b/src/main.rs index fb55d23..2485133 100644 --- a/src/main.rs +++ b/src/main.rs @@ -445,6 +445,19 @@ const APP: () = { }; let mut reset_pin = gpioa.pa0.into_push_pull_output(); + + // Configure the IO_Update signal for the DDS. + let mut hrtimer = HighResTimerE::new(dp.HRTIM_TIME, ccdr.clocks, ccdr.peripheral.HRTIM); + + // IO_Update should be latched for 50ns after the QSPI profile write. Profile writes + // are always 16 bytes, with 2 cycles required per byte, coming out to a total of 32 + // QSPI clock cycles. The QSPI is configured for 10MHz, so this comes out to an + // offset of 3.2uS. + // TODO: This currently does not meet the 2uS timing that we have for profile + // updates, since we want to send a profile update for every DAC update. We should + // increase the QSPI clock frequency. + hrtimer.configure_single_shot(hrtimer::Channel::Two, 50e-9, 3.2e-6); + let io_update = gpiog.pg7.into_push_pull_output(); let asm_delay = {