Added stm32 counter abstraction
This commit is contained in:
@@ -11,6 +11,7 @@ license.workspace = true
|
||||
comms = []
|
||||
single-packet-msgs = []
|
||||
usb = ["embassy-usb"]
|
||||
counter = []
|
||||
stm32 = ["embassy-stm32", "physical/stm32"]
|
||||
|
||||
[dependencies.physical]
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
//! Hardware pulse counter using a timer in External Clock Mode.
|
||||
//!
|
||||
//! Counts edges on an external signal entirely in hardware. Supports three
|
||||
//! pin sources per timer: the dedicated ETR pin, or any CH1/CH2 pin.
|
||||
|
||||
use embassy_stm32::gpio::{AfType, Flex, Pull};
|
||||
use embassy_stm32::pac::timer::vals::{Etp, Etps};
|
||||
use embassy_stm32::timer::low_level::{
|
||||
FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource,
|
||||
};
|
||||
use embassy_stm32::timer::{
|
||||
Ch1, Ch2, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin,
|
||||
};
|
||||
use embassy_stm32::Peri;
|
||||
|
||||
/// Which edge increments the counter.
|
||||
#[derive(Clone, Copy, Default, defmt::Format)]
|
||||
pub enum CountEdge {
|
||||
#[default]
|
||||
Rising,
|
||||
Falling,
|
||||
/// Count on both rising and falling edges.
|
||||
/// Only supported with channel pins (CH1/CH2), not ETR.
|
||||
Both,
|
||||
}
|
||||
|
||||
/// Pulse counter configuration.
|
||||
#[derive(Clone)]
|
||||
pub struct PulseCounterConfig {
|
||||
pub edge: CountEdge,
|
||||
pub filter: FilterValue,
|
||||
pub pull: Pull,
|
||||
}
|
||||
|
||||
impl Default for PulseCounterConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
edge: CountEdge::Rising,
|
||||
filter: FilterValue::NO_FILTER,
|
||||
pull: Pull::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware pulse counter driven by an external signal.
|
||||
///
|
||||
/// Takes ownership of the timer and pin, preventing reuse elsewhere.
|
||||
pub struct PulseCounter<'d, T: GeneralInstance4Channel> {
|
||||
inner: Timer<'d, T>,
|
||||
_pin: Flex<'d>,
|
||||
}
|
||||
|
||||
// ---- Constructors ----
|
||||
|
||||
impl<'d, T: GeneralInstance4Channel> PulseCounter<'d, T> {
|
||||
/// Count pulses on the timer's ETR (External Trigger) pin.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `config.edge` is [`CountEdge::Both`] — ETR only supports single-edge detection.
|
||||
pub fn new_etr(
|
||||
tim: Peri<'d, T>,
|
||||
pin: Peri<'d, impl ExternalTriggerPin<T>>,
|
||||
config: PulseCounterConfig,
|
||||
) -> Self {
|
||||
assert!(
|
||||
!matches!(config.edge, CountEdge::Both),
|
||||
"ETR does not support both-edge detection"
|
||||
);
|
||||
|
||||
let af_num = pin.af_num();
|
||||
let mut flex_pin = Flex::new(pin);
|
||||
flex_pin.set_as_af_unchecked(af_num, AfType::input(config.pull));
|
||||
|
||||
let inner = Timer::new(tim);
|
||||
|
||||
inner.set_slave_mode(SlaveMode::EXT_CLOCK_MODE);
|
||||
inner.set_trigger_source(TriggerSource::ETRF);
|
||||
inner.set_external_trigger_filter(config.filter);
|
||||
inner.set_external_trigger_prescaler(Etps::DIV1);
|
||||
inner.set_external_trigger_polarity(match config.edge {
|
||||
CountEdge::Rising => Etp::from(0),
|
||||
CountEdge::Falling => Etp::from(1),
|
||||
CountEdge::Both => unreachable!(),
|
||||
});
|
||||
|
||||
inner.start();
|
||||
Self { inner, _pin: flex_pin }
|
||||
}
|
||||
|
||||
/// Count pulses on a CH1 pin.
|
||||
pub fn new_ch1(
|
||||
tim: Peri<'d, T>,
|
||||
pin: Peri<'d, impl TimerPin<T, Ch1>>,
|
||||
config: PulseCounterConfig,
|
||||
) -> Self {
|
||||
let af_num = pin.af_num();
|
||||
let mut flex_pin = Flex::new(pin);
|
||||
flex_pin.set_as_af_unchecked(af_num, AfType::input(config.pull));
|
||||
|
||||
let inner = Timer::new(tim);
|
||||
Self::configure_channel(&inner, Channel::Ch1, TriggerSource::TI1FP1, &config);
|
||||
|
||||
inner.start();
|
||||
Self { inner, _pin: flex_pin }
|
||||
}
|
||||
|
||||
/// Count pulses on a CH2 pin.
|
||||
pub fn new_ch2(
|
||||
tim: Peri<'d, T>,
|
||||
pin: Peri<'d, impl TimerPin<T, Ch2>>,
|
||||
config: PulseCounterConfig,
|
||||
) -> Self {
|
||||
let af_num = pin.af_num();
|
||||
let mut flex_pin = Flex::new(pin);
|
||||
flex_pin.set_as_af_unchecked(af_num, AfType::input(config.pull));
|
||||
|
||||
let inner = Timer::new(tim);
|
||||
Self::configure_channel(&inner, Channel::Ch2, TriggerSource::TI2FP2, &config);
|
||||
|
||||
inner.start();
|
||||
Self { inner, _pin: flex_pin }
|
||||
}
|
||||
|
||||
/// Common channel setup: put the channel in input mode, configure its
|
||||
/// filter and edge polarity, then wire it as the slave-mode trigger.
|
||||
fn configure_channel(
|
||||
inner: &Timer<'d, T>,
|
||||
channel: Channel,
|
||||
trigger: TriggerSource,
|
||||
config: &PulseCounterConfig,
|
||||
) {
|
||||
// Channel must be in input mode for the ICxF filter bits to exist.
|
||||
// Normal = direct mapping (TI1→IC1, TI2→IC2).
|
||||
inner.set_input_ti_selection(channel, InputTISelection::Normal);
|
||||
inner.set_input_capture_filter(channel, config.filter);
|
||||
inner.set_input_capture_mode(channel, match config.edge {
|
||||
CountEdge::Rising => InputCaptureMode::Rising,
|
||||
CountEdge::Falling => InputCaptureMode::Falling,
|
||||
CountEdge::Both => InputCaptureMode::BothEdges,
|
||||
});
|
||||
|
||||
inner.set_trigger_source(trigger);
|
||||
inner.set_slave_mode(SlaveMode::EXT_CLOCK_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Reading ----
|
||||
|
||||
impl<'d, T: GeneralInstance4Channel> PulseCounter<'d, T> {
|
||||
/// Read the current count (lower 16 bits).
|
||||
#[inline]
|
||||
pub fn count(&self) -> u16 {
|
||||
self.inner.regs_gp16().cnt().read().cnt()
|
||||
}
|
||||
|
||||
/// Reset the counter to zero.
|
||||
#[inline]
|
||||
pub fn reset(&self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: embassy_stm32::timer::GeneralInstance32bit4Channel> PulseCounter<'d, T> {
|
||||
/// Read the current count as a full 32-bit value.
|
||||
#[inline]
|
||||
pub fn count_32(&self) -> u32 {
|
||||
self.inner.regs_gp32().cnt().read()
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
#[cfg(feature = "usb")]
|
||||
pub mod usb;
|
||||
pub mod usb;
|
||||
#[cfg(feature = "counter")]
|
||||
pub mod counter;
|
||||
|
||||
Reference in New Issue
Block a user