Adding support for pounder ETR timestamping
This commit is contained in:
parent
e9d74ae6da
commit
72d14adfbf
|
@ -517,7 +517,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stm32h7xx-hal"
|
name = "stm32h7xx-hal"
|
||||||
version = "0.8.0"
|
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 = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cast",
|
"cast",
|
||||||
|
|
|
@ -54,7 +54,7 @@ default-features = false
|
||||||
[dependencies.stm32h7xx-hal]
|
[dependencies.stm32h7xx-hal]
|
||||||
features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
|
features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
|
||||||
git = "https://github.com/quartiq/stm32h7xx-hal"
|
git = "https://github.com/quartiq/stm32h7xx-hal"
|
||||||
branch = "feature/number-of-transfers"
|
branch = "feature/dma-buffer-swap/num-transfers"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
|
||||||
|
|
|
@ -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
|
/// Whenever the DMA request occurs, it should write into SPI's TX FIFO to start a DMA
|
||||||
/// transfer.
|
/// 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
|
// 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.
|
// only used for the transmit-half of DMA.
|
||||||
let regs = unsafe { &*hal::stm32::$spi::ptr() };
|
let regs = unsafe { &*hal::stm32::$spi::ptr() };
|
||||||
®s.txdr as *const _ as u32
|
®s.txdr as *const _ as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ macro_rules! dac_output {
|
||||||
const REQUEST_LINE: Option<u8> = Some(DMAReq::$dma_req as u8);
|
const REQUEST_LINE: Option<u8> = Some(DMAReq::$dma_req as u8);
|
||||||
|
|
||||||
/// Whenever the DMA request occurs, it should write into SPI's TX FIFO.
|
/// Whenever the DMA request occurs, it should write into SPI's TX FIFO.
|
||||||
fn address(&self) -> u32 {
|
fn address(&self) -> usize {
|
||||||
&self.spi.inner().txdr as *const _ as u32
|
&self.spi.inner().txdr as *const _ as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl InputStamper {
|
||||||
// Utilize the TIM5 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.
|
||||||
let input_capture =
|
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
|
// 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
|
// timing is not met. The DMA requires 500ns overhead, whereas a direct register read only
|
||||||
|
|
31
src/main.rs
31
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
|
// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is
|
||||||
// equal to 10ns per tick.
|
// equal to 10ns per tick.
|
||||||
const ADC_SAMPLE_TICKS: u32 = 256;
|
const ADC_SAMPLE_TICKS: u32 = 128;
|
||||||
|
|
||||||
// The desired ADC sample processing buffer size.
|
// 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!
|
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
const IIR_CASCADE_LENGTH: usize = 1;
|
||||||
|
@ -853,15 +853,23 @@ const APP: () = {
|
||||||
|
|
||||||
let pounder_stamper = {
|
let pounder_stamper = {
|
||||||
let etr_pin = gpioa.pa0.into_alternate_af3();
|
let etr_pin = gpioa.pa0.into_alternate_af3();
|
||||||
let timestamp_timer = pounder::timestamp::Timer::new(
|
|
||||||
dp.TIM8,
|
// The frequency in the constructor is dont-care, as we will modify the period + clock
|
||||||
ccdr.peripheral.TIM8,
|
// 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,
|
etr_pin,
|
||||||
pounder::timestamp::Prescaler::Div4,
|
)
|
||||||
sampling_timer.update_event(),
|
|
||||||
128,
|
|
||||||
);
|
|
||||||
pounder::timestamp::Timestamper::new(timestamp_timer, dma_streams.7)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start sampling ADCs.
|
// 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)]
|
#[task(binds=DMA1_STR3, resources=[pounder_stamper, adcs, dacs, iir_state, iir_ch, dds_output, input_stamper], priority=2)]
|
||||||
fn process(c: process::Context) {
|
fn process(c: process::Context) {
|
||||||
|
let _pounder_timestamps = c.resources.pounder_stamper.acquire_buffer();
|
||||||
|
|
||||||
let adc_samples = [
|
let adc_samples = [
|
||||||
c.resources.adcs.0.acquire_buffer(),
|
c.resources.adcs.0.acquire_buffer(),
|
||||||
c.resources.adcs.1.acquire_buffer(),
|
c.resources.adcs.1.acquire_buffer(),
|
||||||
|
@ -897,7 +907,6 @@ const APP: () = {
|
||||||
c.resources.dacs.1.acquire_buffer(),
|
c.resources.dacs.1.acquire_buffer(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let _pounder_timestamps = c.resources.pounder_stamper.acquire_buffer();
|
|
||||||
let _timestamps = c.resources.input_stamper.acquire_buffer();
|
let _timestamps = c.resources.input_stamper.acquire_buffer();
|
||||||
|
|
||||||
for channel in 0..adc_samples.len() {
|
for channel in 0..adc_samples.len() {
|
||||||
|
|
|
@ -1,117 +1,19 @@
|
||||||
///! ADC sample timestamper using external Pounder reference clock.
|
///! ADC sample timestamper using external Pounder reference clock.
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use hal::{
|
use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer};
|
||||||
dma::{
|
|
||||||
dma::{DMAReq, DmaConfig},
|
|
||||||
traits::TargetAddress,
|
|
||||||
PeripheralToMemory, Transfer,
|
|
||||||
},
|
|
||||||
rcc::ResetEnable,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{timers, SAMPLE_BUFFER_SIZE};
|
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"]
|
#[link_section = ".axisram.buffers"]
|
||||||
static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3];
|
static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3];
|
||||||
|
|
||||||
pub struct Timestamper {
|
pub struct Timestamper {
|
||||||
next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>,
|
next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>,
|
||||||
timer: Timer,
|
timer: timers::PounderTimestampTimer,
|
||||||
transfer: Transfer<
|
transfer: Transfer<
|
||||||
hal::dma::dma::Stream7<hal::stm32::DMA1>,
|
hal::dma::dma::Stream7<hal::stm32::DMA1>,
|
||||||
TimestampDma,
|
timers::tim8::Channel1InputCapture,
|
||||||
PeripheralToMemory,
|
PeripheralToMemory,
|
||||||
&'static mut [u16; SAMPLE_BUFFER_SIZE],
|
&'static mut [u16; SAMPLE_BUFFER_SIZE],
|
||||||
>,
|
>,
|
||||||
|
@ -119,19 +21,36 @@ pub struct Timestamper {
|
||||||
|
|
||||||
impl Timestamper {
|
impl Timestamper {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut timer: Timer,
|
mut timestamp_timer: timers::PounderTimestampTimer,
|
||||||
stream: hal::dma::dma::Stream7<hal::stm32::DMA1>,
|
stream: hal::dma::dma::Stream7<hal::stm32::DMA1>,
|
||||||
|
capture_channel: timers::tim8::Channel1,
|
||||||
|
sampling_timer: &mut timers::SamplingTimer,
|
||||||
|
_clock_input: hal::gpio::gpioa::PA0<
|
||||||
|
hal::gpio::Alternate<hal::gpio::AF3>,
|
||||||
|
>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let config = DmaConfig::default()
|
let config = DmaConfig::default()
|
||||||
.memory_increment(true)
|
.memory_increment(true)
|
||||||
.circular_buffer(true)
|
.circular_buffer(true)
|
||||||
.double_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.
|
// The data transfer is always a transfer of data from the peripheral to a RAM buffer.
|
||||||
let mut data_transfer: Transfer<_, _, PeripheralToMemory, _> =
|
let mut data_transfer: Transfer<_, _, PeripheralToMemory, _> =
|
||||||
Transfer::init(
|
Transfer::init(
|
||||||
stream,
|
stream,
|
||||||
timer.dma_transfer(),
|
input_capture,
|
||||||
// Note(unsafe): The BUF[0] and BUF[1] is "owned" by this peripheral.
|
// Note(unsafe): The BUF[0] and BUF[1] is "owned" by this peripheral.
|
||||||
// It shall not be used anywhere else in the module.
|
// It shall not be used anywhere else in the module.
|
||||||
unsafe { &mut BUF[0] },
|
unsafe { &mut BUF[0] },
|
||||||
|
@ -139,10 +58,10 @@ impl Timestamper {
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
data_transfer.start(|_| {});
|
data_transfer.start(|capture_channel| capture_channel.enable());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
timer,
|
timer: timestamp_timer,
|
||||||
transfer: data_transfer,
|
transfer: data_transfer,
|
||||||
next_buffer: unsafe { Some(&mut BUF[2]) },
|
next_buffer: unsafe { Some(&mut BUF[2]) },
|
||||||
}
|
}
|
||||||
|
@ -165,7 +84,6 @@ impl Timestamper {
|
||||||
let next_buffer = self.next_buffer.take().unwrap();
|
let next_buffer = self.next_buffer.take().unwrap();
|
||||||
|
|
||||||
// Start the next transfer.
|
// Start the next transfer.
|
||||||
self.transfer.clear_interrupts();
|
|
||||||
let (prev_buffer, _, _) =
|
let (prev_buffer, _, _) =
|
||||||
self.transfer.next_transfer(next_buffer).unwrap();
|
self.transfer.next_transfer(next_buffer).unwrap();
|
||||||
|
|
||||||
|
|
119
src/timers.rs
119
src/timers.rs
|
@ -1,8 +1,40 @@
|
||||||
///! 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;
|
||||||
|
|
||||||
|
#[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 {
|
macro_rules! timer_channels {
|
||||||
($name:ident, $TY:ident, u32) => {
|
($name:ident, $TY:ident, $size:ty) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
|
|
||||||
/// The timer used for managing ADC sampling.
|
/// The timer used for managing ADC sampling.
|
||||||
|
@ -14,6 +46,7 @@ macro_rules! timer_channels {
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
/// Construct the sampling timer.
|
/// Construct the sampling timer.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn new(mut timer: hal::timer::Timer<hal::stm32::[< $TY>]>) -> Self {
|
pub fn new(mut timer: hal::timer::Timer<hal::stm32::[< $TY>]>) -> Self {
|
||||||
timer.pause();
|
timer.pause();
|
||||||
|
|
||||||
|
@ -30,6 +63,7 @@ macro_rules! timer_channels {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the timer capture/compare channels.
|
/// Get the timer capture/compare channels.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn channels(&mut self) -> [< $TY:lower >]::Channels {
|
pub fn channels(&mut self) -> [< $TY:lower >]::Channels {
|
||||||
self.channels.take().unwrap()
|
self.channels.take().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -42,19 +76,36 @@ macro_rules! timer_channels {
|
||||||
|
|
||||||
/// Get the period of the timer.
|
/// Get the period of the timer.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn get_period(&self) -> u32 {
|
pub fn get_period(&self) -> $size {
|
||||||
let regs = unsafe { &*hal::stm32::$TY::ptr() };
|
let regs = unsafe { &*hal::stm32::$TY::ptr() };
|
||||||
regs.arr.read().arr().bits()
|
regs.arr.read().arr().bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manually set the period of the timer.
|
/// Manually set the period of the timer.
|
||||||
#[allow(dead_code)]
|
#[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() };
|
let regs = unsafe { &*hal::stm32::$TY::ptr() };
|
||||||
regs.arr.write(|w| w.arr().bits(period));
|
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.
|
/// Start the timer.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn start(mut self) {
|
pub fn start(mut self) {
|
||||||
// Force a refresh of the frequency settings.
|
// Force a refresh of the frequency settings.
|
||||||
self.timer.apply_freq();
|
self.timer.apply_freq();
|
||||||
|
@ -62,12 +113,26 @@ macro_rules! timer_channels {
|
||||||
self.timer.reset_counter();
|
self.timer.reset_counter();
|
||||||
self.timer.resume();
|
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 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 stm32h7xx_hal as hal;
|
||||||
use hal::dma::{traits::TargetAddress, PeripheralToMemory, dma::DMAReq};
|
use hal::dma::{traits::TargetAddress, PeripheralToMemory, dma::DMAReq};
|
||||||
use hal::stm32::$TY;
|
use hal::stm32::$TY;
|
||||||
|
@ -86,10 +151,17 @@ macro_rules! timer_channels {
|
||||||
/// Enable DMA requests upon timer updates.
|
/// Enable DMA requests upon timer updates.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn listen_dma(&self) {
|
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() };
|
let regs = unsafe { &*<$TY>::ptr() };
|
||||||
regs.dier.modify(|_, w| w.ude().set_bit());
|
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.
|
/// The channels representing the timer.
|
||||||
|
@ -104,6 +176,7 @@ macro_rules! timer_channels {
|
||||||
/// Construct a new set of channels.
|
/// Construct a new set of channels.
|
||||||
///
|
///
|
||||||
/// Note(unsafe): This is only safe to call once.
|
/// Note(unsafe): This is only safe to call once.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub unsafe fn new() -> Self {
|
pub unsafe fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ch1: Channel1::new(),
|
ch1: Channel1::new(),
|
||||||
|
@ -114,15 +187,15 @@ macro_rules! timer_channels {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timer_channels!(1, $TY, ccmr1);
|
timer_channels!(1, $TY, ccmr1, $size);
|
||||||
timer_channels!(2, $TY, ccmr1);
|
timer_channels!(2, $TY, ccmr1, $size);
|
||||||
timer_channels!(3, $TY, ccmr2);
|
timer_channels!(3, $TY, ccmr2, $size);
|
||||||
timer_channels!(4, $TY, ccmr2);
|
timer_channels!(4, $TY, ccmr2, $size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
($index:expr, $TY:ty, $ccmrx:expr) => {
|
($index:expr, $TY:ty, $ccmrx:expr, $size:ty) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
/// A capture/compare channel of the timer.
|
/// A capture/compare channel of the timer.
|
||||||
pub struct [< Channel $index >] {}
|
pub struct [< Channel $index >] {}
|
||||||
|
@ -135,6 +208,7 @@ macro_rules! timer_channels {
|
||||||
///
|
///
|
||||||
/// Note(unsafe): This function must only be called once. Once constructed, the
|
/// Note(unsafe): This function must only be called once. Once constructed, the
|
||||||
/// constructee guarantees to never modify the timer channel.
|
/// constructee guarantees to never modify the timer channel.
|
||||||
|
#[allow(dead_code)]
|
||||||
unsafe fn new() -> Self {
|
unsafe fn new() -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
@ -151,9 +225,10 @@ 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)]
|
#[allow(dead_code)]
|
||||||
pub fn to_output_compare(&self, value: u32) {
|
pub fn to_output_compare(&self, value: $size) {
|
||||||
let regs = unsafe { &*<$TY>::ptr() };
|
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.[< ccr $index >].write(|w| w.ccr().bits(value));
|
||||||
regs.[< $ccmrx _output >]()
|
regs.[< $ccmrx _output >]()
|
||||||
.modify(|_, w| unsafe { w.[< cc $index s >]().bits(0) });
|
.modify(|_, w| unsafe { w.[< cc $index s >]().bits(0) });
|
||||||
|
@ -164,9 +239,12 @@ macro_rules! timer_channels {
|
||||||
/// # Args
|
/// # Args
|
||||||
/// * `input` - The input source for the input capture event.
|
/// * `input` - The input source for the input capture event.
|
||||||
#[allow(dead_code)]
|
#[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() };
|
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 >] {}
|
[< Channel $index InputCapture >] {}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +253,7 @@ macro_rules! timer_channels {
|
||||||
impl [< Channel $index InputCapture >] {
|
impl [< Channel $index InputCapture >] {
|
||||||
/// Get the latest capture from the channel.
|
/// Get the latest capture from the channel.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn latest_capture(&mut self) -> Option<u32> {
|
pub fn latest_capture(&mut self) -> Option<$size> {
|
||||||
// Note(unsafe): This channel owns all access to the specific timer channel.
|
// Note(unsafe): This channel owns all access to the specific timer channel.
|
||||||
// Only atomic operations on completed on the timer registers.
|
// Only atomic operations on completed on the timer registers.
|
||||||
let regs = unsafe { &*<$TY>::ptr() };
|
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
|
// is safe as it is only completed once per channel and each DMA request is allocated to
|
||||||
// each channel as the owner.
|
// each channel as the owner.
|
||||||
unsafe impl TargetAddress<PeripheralToMemory> for [< Channel $index InputCapture >] {
|
unsafe impl TargetAddress<PeripheralToMemory> for [< Channel $index InputCapture >] {
|
||||||
type MemSize = u32;
|
type MemSize = $size;
|
||||||
|
|
||||||
const REQUEST_LINE: Option<u8> = Some(DMAReq::[< $TY _CH $index >]as u8);
|
const REQUEST_LINE: Option<u8> = Some(DMAReq::[< $TY _CH $index >]as u8);
|
||||||
|
|
||||||
fn address(&self) -> u32 {
|
fn address(&self) -> usize {
|
||||||
let regs = unsafe { &*<$TY>::ptr() };
|
let regs = unsafe { &*<$TY>::ptr() };
|
||||||
®s.[<ccr $index >] as *const _ as u32
|
®s.[<ccr $index >] as *const _ as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,3 +314,4 @@ macro_rules! timer_channels {
|
||||||
|
|
||||||
timer_channels!(SamplingTimer, TIM2, u32);
|
timer_channels!(SamplingTimer, TIM2, u32);
|
||||||
timer_channels!(TimestampTimer, TIM5, u32);
|
timer_channels!(TimestampTimer, TIM5, u32);
|
||||||
|
timer_channels!(PounderTimestampTimer, TIM8, u16);
|
||||||
|
|
Loading…
Reference in New Issue