Merge pull request #4348 from melvdlin/stm32-ch-generic-timer-traits

[stm32]: Replace per-channel traits in `timer` module with generic versions
This commit is contained in:
Dario Nieuwenhuis 2025-06-27 00:03:47 +00:00 committed by GitHub
commit 77fc1bac2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 367 additions and 298 deletions

View File

@ -1090,21 +1090,21 @@ fn main() {
(("fmc", "CLK"), quote!(crate::fmc::ClkPin)),
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
(("timer", "CH1"), quote!(crate::timer::Channel1Pin)),
(("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)),
(("timer", "CH2"), quote!(crate::timer::Channel2Pin)),
(("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)),
(("timer", "CH3"), quote!(crate::timer::Channel3Pin)),
(("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)),
(("timer", "CH4"), quote!(crate::timer::Channel4Pin)),
(("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)),
(("timer", "CH1"), quote!(crate::timer::TimerPin<Ch1>)),
(("timer", "CH1N"), quote!(crate::timer::TimerComplementaryPin<Ch1>)),
(("timer", "CH2"), quote!(crate::timer::TimerPin<Ch2>)),
(("timer", "CH2N"), quote!(crate::timer::TimerComplementaryPin<Ch2>)),
(("timer", "CH3"), quote!(crate::timer::TimerPin<Ch3>)),
(("timer", "CH3N"), quote!(crate::timer::TimerComplementaryPin<Ch3>)),
(("timer", "CH4"), quote!(crate::timer::TimerPin<Ch4>)),
(("timer", "CH4N"), quote!(crate::timer::TimerComplementaryPin<Ch4>)),
(("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)),
(("timer", "BKIN"), quote!(crate::timer::BreakInputPin)),
(("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)),
(("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)),
(("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)),
(("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)),
(("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)),
(("timer", "BKIN"), quote!(crate::timer::BreakInputPin<BkIn1>)),
(("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn1>)),
(("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn1>)),
(("timer", "BKIN2"), quote!(crate::timer::BreakInputPin<BkIn2>)),
(("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInputComparator1Pin<BkIn2>)),
(("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInputComparator2Pin<BkIn2>)),
(("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)),
(("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)),
(("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)),
@ -1475,10 +1475,10 @@ fn main() {
(("hash", "IN"), quote!(crate::hash::Dma)),
(("cryp", "IN"), quote!(crate::cryp::DmaIn)),
(("cryp", "OUT"), quote!(crate::cryp::DmaOut)),
(("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
(("timer", "CH4"), quote!(crate::timer::Ch4Dma)),
(("timer", "CH1"), quote!(crate::timer::Dma<Ch1>)),
(("timer", "CH2"), quote!(crate::timer::Dma<Ch2>)),
(("timer", "CH3"), quote!(crate::timer::Dma<Ch3>)),
(("timer", "CH4"), quote!(crate::timer::Dma<Ch4>)),
(("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver
(("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver
]

View File

@ -5,14 +5,12 @@ use core::marker::PhantomData;
use stm32_metapac::timer::vals::Ckd;
use super::low_level::{CountingMode, OutputPolarity, Timer};
use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin};
use super::{
AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin,
Channel4ComplementaryPin,
};
use super::simple_pwm::PwmPin;
use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};
use crate::gpio::{AnyPin, OutputType};
use crate::time::Hertz;
use crate::timer::low_level::OutputCompareMode;
use crate::timer::TimerChannel;
use crate::Peri;
/// Complementary PWM pin wrapper.
@ -23,32 +21,23 @@ pub struct ComplementaryPwmPin<'d, T, C> {
phantom: PhantomData<(T, C)>,
}
macro_rules! complementary_channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(
pin.af_num(),
crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh),
);
});
ComplementaryPwmPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> {
/// Create a new complementary PWM pin instance.
pub fn new(pin: Peri<'d, impl TimerComplementaryPin<T, C>>, output_type: OutputType) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(
pin.af_num(),
crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh),
);
});
ComplementaryPwmPin {
_pin: pin.into(),
phantom: PhantomData,
}
};
}
}
complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin);
complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin);
complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
/// PWM driver with support for standard and complementary outputs.
pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {
inner: Timer<'d, T>,

View File

@ -6,14 +6,12 @@ use core::pin::Pin;
use core::task::{Context, Poll};
use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer};
use super::{
CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin,
GeneralInstance4Channel,
};
use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin};
pub use super::{Ch1, Ch2, Ch3, Ch4};
use crate::gpio::{AfType, AnyPin, Pull};
use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::time::Hertz;
use crate::timer::TimerChannel;
use crate::Peri;
/// Capture pin wrapper.
@ -23,27 +21,17 @@ pub struct CapturePin<'d, T, C> {
_pin: Peri<'d, AnyPin>,
phantom: PhantomData<(T, C)>,
}
macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")]
pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self {
pin.set_as_af(pin.af_num(), AfType::input(pull));
CapturePin {
_pin: pin.into(),
phantom: PhantomData,
}
}
impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> {
/// Create a new capture pin instance.
pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, pull: Pull) -> Self {
pin.set_as_af(pin.af_num(), AfType::input(pull));
CapturePin {
_pin: pin.into(),
phantom: PhantomData,
}
};
}
}
channel_impl!(new_ch1, Ch1, Channel1Pin);
channel_impl!(new_ch2, Ch2, Channel2Pin);
channel_impl!(new_ch3, Ch3, Channel3Pin);
channel_impl!(new_ch4, Ch4, Channel4Pin);
/// Input capture driver.
pub struct InputCapture<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,

View File

@ -51,6 +51,80 @@ pub enum Ch3 {}
/// Channel 4 marker type.
pub enum Ch4 {}
/// Timer channel trait.
#[allow(private_bounds)]
pub trait TimerChannel: SealedTimerChannel {
/// The runtime channel.
const CHANNEL: Channel;
}
trait SealedTimerChannel {}
impl TimerChannel for Ch1 {
const CHANNEL: Channel = Channel::Ch1;
}
impl TimerChannel for Ch2 {
const CHANNEL: Channel = Channel::Ch2;
}
impl TimerChannel for Ch3 {
const CHANNEL: Channel = Channel::Ch3;
}
impl TimerChannel for Ch4 {
const CHANNEL: Channel = Channel::Ch4;
}
impl SealedTimerChannel for Ch1 {}
impl SealedTimerChannel for Ch2 {}
impl SealedTimerChannel for Ch3 {}
impl SealedTimerChannel for Ch4 {}
/// Timer break input.
#[derive(Clone, Copy)]
pub enum BkIn {
/// Break input 1.
BkIn1,
/// Break input 2.
BkIn2,
}
impl BkIn {
/// Get the channel index (0..3)
pub fn index(&self) -> usize {
match self {
BkIn::BkIn1 => 0,
BkIn::BkIn2 => 1,
}
}
}
/// Break input 1 marker type.
pub enum BkIn1 {}
/// Break input 2 marker type.
pub enum BkIn2 {}
/// Timer channel trait.
#[allow(private_bounds)]
pub trait BreakInput: SealedBreakInput {
/// The runtim timer channel.
const INPUT: BkIn;
}
trait SealedBreakInput {}
impl BreakInput for BkIn1 {
const INPUT: BkIn = BkIn::BkIn1;
}
impl BreakInput for BkIn2 {
const INPUT: BkIn = BkIn::BkIn2;
}
impl SealedBreakInput for BkIn1 {}
impl SealedBreakInput for BkIn2 {}
/// Amount of bits of a timer.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -149,33 +223,20 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad
/// Advanced 16-bit timer with 4 channels instance.
pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {}
pin_trait!(Channel1Pin, GeneralInstance4Channel);
pin_trait!(Channel2Pin, GeneralInstance4Channel);
pin_trait!(Channel3Pin, GeneralInstance4Channel);
pin_trait!(Channel4Pin, GeneralInstance4Channel);
pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel);
pin_trait!(ExternalTriggerPin, GeneralInstance4Channel);
pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel);
pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel);
pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel);
pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel);
pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel);
pin_trait!(BreakInputPin, AdvancedInstance4Channel);
pin_trait!(BreakInput2Pin, AdvancedInstance4Channel);
pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput);
pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel);
pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel);
pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel);
pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel);
pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput);
pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput);
// Update Event trigger DMA for every timer
dma_trait!(UpDma, BasicInstance);
dma_trait!(Ch1Dma, GeneralInstance4Channel);
dma_trait!(Ch2Dma, GeneralInstance4Channel);
dma_trait!(Ch3Dma, GeneralInstance4Channel);
dma_trait!(Ch4Dma, GeneralInstance4Channel);
dma_trait!(Dma, GeneralInstance4Channel, TimerChannel);
#[allow(unused)]
macro_rules! impl_core_timer {

View File

@ -7,11 +7,9 @@ use core::pin::Pin;
use core::task::{Context, Poll};
use super::low_level::{
CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource,
};
use super::{
CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel,
CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts,
};
use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin};
pub use super::{Ch1, Ch2};
use crate::gpio::{AfType, AnyPin, Pull};
use crate::interrupt::typelevel::{Binding, Interrupt};
@ -48,24 +46,78 @@ pub struct TriggerPin<'d, T, C> {
phantom: PhantomData<(T, C)>,
}
macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")]
pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self {
pin.set_as_af(pin.af_num(), AfType::input(pull));
TriggerPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
}
};
trait SealedTriggerSource {}
/// Marker trait for a trigger source.
#[expect(private_bounds)]
pub trait TriggerSource: SealedTriggerSource {}
impl TriggerSource for Ch1 {}
impl TriggerSource for Ch2 {}
impl TriggerSource for Ext {}
impl SealedTriggerSource for Ch1 {}
impl SealedTriggerSource for Ch2 {}
impl SealedTriggerSource for Ext {}
trait SealedTimerTriggerPin<T, S>: crate::gpio::Pin {}
/// Marker trait for a trigger pin.
#[expect(private_bounds)]
// TODO: find better naming scheme than prefixing all pin traits with "Timer".
// The trait name cannot conflict with the corresponding type's name.
// Applies to other timer submodules as well.
pub trait TimerTriggerPin<T, S>: SealedTimerTriggerPin<T, S> {
/// Get the AF number needed to use this pin as a trigger source.
fn af_num(&self) -> u8;
}
channel_impl!(new_ch1, Ch1, Channel1Pin);
channel_impl!(new_ch2, Ch2, Channel2Pin);
channel_impl!(new_ext, Ext, ExternalTriggerPin);
impl<T, P, C> TimerTriggerPin<T, C> for P
where
T: GeneralInstance4Channel,
P: TimerPin<T, C>,
C: super::TimerChannel + TriggerSource,
{
fn af_num(&self) -> u8 {
TimerPin::af_num(self)
}
}
impl<T, P> TimerTriggerPin<T, Ext> for P
where
T: GeneralInstance4Channel,
P: ExternalTriggerPin<T>,
{
fn af_num(&self) -> u8 {
ExternalTriggerPin::af_num(self)
}
}
impl<T, P, C> SealedTimerTriggerPin<T, C> for P
where
T: GeneralInstance4Channel,
P: TimerPin<T, C>,
C: super::TimerChannel + TriggerSource,
{
}
impl<T, P> SealedTimerTriggerPin<T, Ext> for P
where
T: GeneralInstance4Channel,
P: ExternalTriggerPin<T>,
{
}
impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> {
/// "Create a new Ch1 trigger pin instance.
pub fn new(pin: Peri<'d, impl TimerTriggerPin<T, C>>, pull: Pull) -> Self {
pin.set_as_af(pin.af_num(), AfType::input(pull));
TriggerPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
}
/// One pulse driver.
///
@ -89,7 +141,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
) -> Self {
let mut this = Self { inner: Timer::new(tim) };
this.inner.set_trigger_source(TriggerSource::TI1F_ED);
this.inner.set_trigger_source(Ts::TI1F_ED);
this.inner
.set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
this.inner
@ -114,7 +166,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
) -> Self {
let mut this = Self { inner: Timer::new(tim) };
this.inner.set_trigger_source(TriggerSource::TI1FP1);
this.inner.set_trigger_source(Ts::TI1FP1);
this.inner
.set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);
this.inner
@ -140,7 +192,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
) -> Self {
let mut this = Self { inner: Timer::new(tim) };
this.inner.set_trigger_source(TriggerSource::TI2FP2);
this.inner.set_trigger_source(Ts::TI2FP2);
this.inner
.set_input_ti_selection(Channel::Ch2, InputTISelection::Normal);
this.inner
@ -172,7 +224,7 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {
// No filtering
r.set_etf(FilterValue::NO_FILTER);
});
this.inner.set_trigger_source(TriggerSource::ETRF);
this.inner.set_trigger_source(Ts::ETRF);
this.new_inner(freq, pulse_end, counting_mode);
this

View File

@ -1,7 +1,7 @@
//! PWM Input driver.
use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};
use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel};
use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin};
use crate::gpio::{AfType, Pull};
use crate::time::Hertz;
use crate::Peri;
@ -14,14 +14,14 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> {
impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
/// Create a new PWM input driver.
pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl Channel1Pin<T>>, pull: Pull, freq: Hertz) -> Self {
pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch1>>, pull: Pull, freq: Hertz) -> Self {
pin.set_as_af(pin.af_num(), AfType::input(pull));
Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
}
/// Create a new PWM input driver.
pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl Channel2Pin<T>>, pull: Pull, freq: Hertz) -> Self {
pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin<T, Ch2>>, pull: Pull, freq: Hertz) -> Self {
pin.set_as_af(pin.af_num(), AfType::input(pull));
Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)

View File

@ -6,8 +6,9 @@ use stm32_metapac::timer::vals;
use super::low_level::Timer;
pub use super::{Ch1, Ch2};
use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel};
use super::{GeneralInstance4Channel, TimerPin};
use crate::gpio::{AfType, AnyPin, Pull};
use crate::timer::TimerChannel;
use crate::Peri;
/// Counting direction
@ -24,26 +25,31 @@ pub struct QeiPin<'d, T, Channel> {
phantom: PhantomData<(T, Channel)>,
}
macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")]
pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(pin.af_num(), AfType::input(Pull::None));
});
QeiPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> {
/// Create a new QEI pin instance.
pub fn new(pin: Peri<'d, impl TimerPin<T, C>>) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(pin.af_num(), AfType::input(Pull::None));
});
QeiPin {
_pin: pin.into(),
phantom: PhantomData,
}
};
}
}
channel_impl!(new_ch1, Ch1, Channel1Pin);
channel_impl!(new_ch2, Ch2, Channel2Pin);
trait SealedQeiChannel: TimerChannel {}
/// Marker trait for a timer channel eligible for use with QEI.
#[expect(private_bounds)]
pub trait QeiChannel: SealedQeiChannel {}
impl QeiChannel for Ch1 {}
impl QeiChannel for Ch2 {}
impl SealedQeiChannel for Ch1 {}
impl SealedQeiChannel for Ch2 {}
/// Quadrature decoder driver.
pub struct Qei<'d, T: GeneralInstance4Channel> {

View File

@ -4,22 +4,13 @@ use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, TimerBits};
use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin};
#[cfg(gpio_v2)]
use crate::gpio::Pull;
use crate::gpio::{AfType, AnyPin, OutputType, Speed};
use crate::time::Hertz;
use crate::Peri;
/// Channel 1 marker type.
pub enum Ch1 {}
/// Channel 2 marker type.
pub enum Ch2 {}
/// Channel 3 marker type.
pub enum Ch3 {}
/// Channel 4 marker type.
pub enum Ch4 {}
/// PWM pin wrapper.
///
/// This wraps a pin to make it usable with PWM.
@ -43,46 +34,37 @@ pub struct PwmPinConfig {
pub pull: Pull,
}
macro_rules! channel_impl {
($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
});
PwmPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")]
pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(
pin.af_num(),
#[cfg(gpio_v1)]
AfType::output(pin_config.output_type, pin_config.speed),
#[cfg(gpio_v2)]
AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
);
});
PwmPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> {
/// Create a new PWM pin instance.
pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
});
PwmPin {
_pin: pin.into(),
phantom: PhantomData,
}
};
}
}
channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin);
channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin);
channel_impl!(new_ch3, new_ch3_with_config, Ch3, Channel3Pin);
channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin);
/// Create a new PWM pin instance with config.
pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self {
critical_section::with(|_| {
pin.set_low();
pin.set_as_af(
pin.af_num(),
#[cfg(gpio_v1)]
AfType::output(pin_config.output_type, pin_config.speed),
#[cfg(gpio_v2)]
AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
);
});
PwmPin {
_pin: pin.into(),
phantom: PhantomData,
}
}
}
/// A single channel of a pwm, obtained from [`SimplePwm::split`],
/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
@ -466,106 +448,97 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
}
}
macro_rules! impl_waveform_chx {
($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
/// Generate a sequence of PWM waveform
pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T>>, duty: &[u16]) {
use crate::pac::timer::vals::Ccds;
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
/// Generate a sequence of PWM waveform
pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
use crate::pac::timer::vals::Ccds;
#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();
#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();
let cc_channel = Channel::$cc_ch;
let cc_channel = C::CHANNEL;
let original_duty_state = self.channel(cc_channel).current_duty_cycle();
let original_enable_state = self.channel(cc_channel).is_enabled();
let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
let original_duty_state = self.channel(cc_channel).current_duty_cycle();
let original_enable_state = self.channel(cc_channel).is_enabled();
let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
// redirect CC DMA request onto Update Event
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
}
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, true);
}
if !original_enable_state {
self.channel(cc_channel).enable();
}
unsafe {
#[cfg(not(any(bdma, gpdma)))]
use crate::dma::{Burst, FifoThreshold};
use crate::dma::{Transfer, TransferOptions};
let dma_transfer_option = TransferOptions {
#[cfg(not(any(bdma, gpdma)))]
fifo_threshold: Some(FifoThreshold::Full),
#[cfg(not(any(bdma, gpdma)))]
mburst: Burst::Incr8,
..Default::default()
};
match self.inner.bits() {
TimerBits::Bits16 => {
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
dma_transfer_option,
)
.await
}
#[cfg(not(any(stm32l0)))]
TimerBits::Bits32 => {
#[cfg(not(any(bdma, gpdma)))]
panic!("unsupported timer bits");
#[cfg(any(bdma, gpdma))]
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
dma_transfer_option,
)
.await
}
};
};
// restore output compare state
if !original_enable_state {
self.channel(cc_channel).disable();
}
self.channel(cc_channel).set_duty_cycle(original_duty_state);
// Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
// this can almost always trigger a DMA FIFO error.
//
// optional TODO:
// clean FEIF after disable UDE
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, false);
}
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
}
}
// redirect CC DMA request onto Update Event
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
}
};
}
impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, true);
}
if !original_enable_state {
self.channel(cc_channel).enable();
}
unsafe {
#[cfg(not(any(bdma, gpdma)))]
use crate::dma::{Burst, FifoThreshold};
use crate::dma::{Transfer, TransferOptions};
let dma_transfer_option = TransferOptions {
#[cfg(not(any(bdma, gpdma)))]
fifo_threshold: Some(FifoThreshold::Full),
#[cfg(not(any(bdma, gpdma)))]
mburst: Burst::Incr8,
..Default::default()
};
match self.inner.bits() {
TimerBits::Bits16 => {
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
dma_transfer_option,
)
.await
}
#[cfg(not(any(stm32l0)))]
TimerBits::Bits32 => {
#[cfg(not(any(bdma, gpdma)))]
panic!("unsupported timer bits");
#[cfg(any(bdma, gpdma))]
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
dma_transfer_option,
)
.await
}
};
};
// restore output compare state
if !original_enable_state {
self.channel(cc_channel).disable();
}
self.channel(cc_channel).set_duty_cycle(original_duty_state);
// Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
// this can almost always trigger a DMA FIFO error.
//
// optional TODO:
// clean FEIF after disable UDE
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, false);
}
if !original_cc_dma_on_update {
self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
}
}
}
impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
type Error = core::convert::Infallible;

View File

@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(blinky(p.PC13)));
let ch3 = CapturePin::new_ch3(p.PA2, Pull::None);
let ch3 = CapturePin::new(p.PA2, Pull::None);
let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());
loop {

View File

@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(blinky(p.PB2)));
let ch3 = CapturePin::new_ch3(p.PB10, Pull::None);
let ch3 = CapturePin::new(p.PB10, Pull::None);
let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());
loop {

View File

@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull);
let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());
let mut ch1 = pwm.ch1();
ch1.enable();

View File

@ -16,8 +16,8 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull);
let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull);
let ch1 = PwmPin::new(p.PE9, OutputType::PushPull);
let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);
let mut pwm = ComplementaryPwm::new(
p.TIM1,
Some(ch1),

View File

@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) {
let mut ws2812_pwm = SimplePwm::new(
dp.TIM3,
Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)),
Some(PwmPin::new(dp.PB4, OutputType::PushPull)),
None,
None,
None,

View File

@ -37,8 +37,8 @@ async fn main(_spawner: Spawner) {
}
let p = embassy_stm32::init(config);
let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull);
let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull);
let ch1 = PwmPin::new(p.PA8, OutputType::PushPull);
let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);
let mut pwm = ComplementaryPwm::new(
p.TIM1,

View File

@ -47,12 +47,12 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(blinky(p.PB1)));
// Connect PB1 and PA8 with a 1k Ohm resistor
let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull);
let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull);
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default());
pwm.ch1().enable();
pwm.ch1().set_duty_cycle(50);
let ch1 = CapturePin::new_ch1(p.PA0, Pull::None);
let ch1 = CapturePin::new(p.PA0, Pull::None);
let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default());
let mut old_capture = 0;

View File

@ -26,10 +26,10 @@ use {defmt_rtt as _, panic_probe as _};
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull);
let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull);
let ch2 = PwmPin::new_ch2(p.PB3, OutputType::PushPull);
let ch2n = ComplementaryPwmPin::new_ch2(p.PB0, OutputType::PushPull);
let ch1 = PwmPin::new(p.PA8, OutputType::PushPull);
let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);
let ch2 = PwmPin::new(p.PB3, OutputType::PushPull);
let ch2n = ComplementaryPwmPin::new(p.PB0, OutputType::PushPull);
let mut pwm = ComplementaryPwm::new(
p.TIM1,

View File

@ -42,7 +42,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(blinky(p.PB1)));
// Connect PA8 and PA6 with a 1k Ohm resistor
let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull);
let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull);
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default());
pwm.ch1().set_duty_cycle_fraction(1, 4);
pwm.ch1().enable();

View File

@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull);
let ch1_pin = PwmPin::new(p.PC0, OutputType::PushPull);
let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());
let mut ch1 = pwm.ch1();
ch1.enable();

View File

@ -6,7 +6,7 @@ use embassy_executor::Spawner;
use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed};
use embassy_stm32::time::{khz, Hertz};
use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer};
use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel};
use embassy_stm32::timer::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance32bit4Channel, TimerPin};
use embassy_stm32::{Config, Peri};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@ -67,10 +67,10 @@ pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> {
impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> {
pub fn new(
tim: Peri<'d, T>,
ch1: Peri<'d, impl Channel1Pin<T>>,
ch2: Peri<'d, impl Channel2Pin<T>>,
ch3: Peri<'d, impl Channel3Pin<T>>,
ch4: Peri<'d, impl Channel4Pin<T>>,
ch1: Peri<'d, impl TimerPin<T, Ch1>>,
ch2: Peri<'d, impl TimerPin<T, Ch2>>,
ch3: Peri<'d, impl TimerPin<T, Ch3>>,
ch4: Peri<'d, impl TimerPin<T, Ch4>>,
freq: Hertz,
) -> Self {
let af1 = ch1.af_num();

View File

@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(config);
info!("Hello World!");
let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull);
let ch1_pin = PwmPin::new(p.PA6, OutputType::PushPull);
let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default());
let mut ch1 = pwm.ch1();
ch1.enable();

View File

@ -11,7 +11,7 @@ use embassy_stm32::rcc::*;
use embassy_stm32::time::hz;
use embassy_stm32::timer::low_level::{Timer as LLTimer, *};
use embassy_stm32::timer::simple_pwm::PwmPin;
use embassy_stm32::timer::Channel;
use embassy_stm32::timer::{Ch3, Channel};
use embassy_stm32::{interrupt, pac, Config};
use panic_probe as _;
@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(config);
// setup PWM pin in AF mode
let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull);
let _ch3 = PwmPin::<_, Ch3>::new(p.PA2, OutputType::PushPull);
// initialize timer
// we cannot use SimplePWM here because the Time is privately encapsulated