diff --git a/.gitignore b/.gitignore index 1c221e876c..7bd11d93d0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ Cargo.lock third_party /Cargo.toml out/ +Session.vim diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 523bacb115..8ba00f6d9d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,10 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645" } +#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645" } +# TODO: replace with embassy-rs/stm32-data-generated once https://github.com/embassy-rs/stm32-data/pull/495 is +# merged +stm32-metapac = { git = "https://github.com/honzasp/stm32-data-generated", features = ["metadata"] } vcell = "0.1.3" nb = "1.0.0" @@ -96,8 +99,9 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" -#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645", default-features = false, features = ["metadata"] } +#stm32-metapac = { version = "15" } +#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645" } +stm32-metapac = { git = "https://github.com/honzasp/stm32-data-generated", features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 8457e3a130..9fe4fed18d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -987,21 +987,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", "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", "CH1"), quote!(crate::timer::TimerPin)), + (("timer", "CH1N"), quote!(crate::timer::TimerPin)), + (("timer", "CH2"), quote!(crate::timer::TimerPin)), + (("timer", "CH2N"), quote!(crate::timer::TimerPin)), + (("timer", "CH3"), quote!(crate::timer::TimerPin)), + (("timer", "CH3N"), quote!(crate::timer::TimerPin)), + (("timer", "CH4"), quote!(crate::timer::TimerPin)), + (("timer", "CH4N"), quote!(crate::timer::TimerPin)), + (("timer", "ETR"), quote!(crate::timer::TimerPin)), + (("timer", "BKIN"), quote!(crate::timer::TimerPin)), + (("timer", "BKIN_COMP1"), quote!(crate::timer::TimerPin)), + (("timer", "BKIN_COMP2"), quote!(crate::timer::TimerPin)), + (("timer", "BKIN2"), quote!(crate::timer::TimerPin)), + (("timer", "BKIN2_COMP1"), quote!(crate::timer::TimerPin)), + (("timer", "BKIN2_COMP2"), quote!(crate::timer::TimerPin)), (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), @@ -1211,14 +1211,15 @@ fn main() { (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)), (("dac", "CH1"), quote!(crate::dac::DacDma1)), (("dac", "CH2"), quote!(crate::dac::DacDma2)), - (("timer", "UP"), quote!(crate::timer::UpDma)), (("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", "UP"), quote!(crate::timer::UpDma)), + (("timer", "TRIG"), quote!(crate::timer::TrigDma)), + (("timer", "CH1"), quote!(crate::timer::CcDma)), + (("timer", "CH2"), quote!(crate::timer::CcDma)), + (("timer", "CH3"), quote!(crate::timer::CcDma)), + (("timer", "CH4"), quote!(crate::timer::CcDma)), (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver ] diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 66c4aa53c1..bf283b5947 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -17,7 +17,7 @@ mod dmamux; pub(crate) use dmamux::*; mod util; -pub(crate) use util::*; +pub use util::ChannelAndRequest; pub(crate) mod ringbuffer; pub mod word; diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 5aaca57c9c..aad2955476 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs @@ -3,15 +3,18 @@ use embassy_hal_internal::PeripheralRef; use super::word::Word; use super::{AnyChannel, Request, Transfer, TransferOptions}; -/// Convenience wrapper, contains a channel and a request number. +/// Convenience wrapper, contains a DMA channel and a DMA request number. /// /// Commonly used in peripheral drivers that own DMA channels. -pub(crate) struct ChannelAndRequest<'d> { +pub struct ChannelAndRequest<'d> { + /// DMA channel. pub channel: PeripheralRef<'d, AnyChannel>, + /// DMA request. pub request: Request, } impl<'d> ChannelAndRequest<'d> { + /// See [`Transfer::new_read()`]. pub unsafe fn read<'a, W: Word>( &'a mut self, peri_addr: *mut W, @@ -21,6 +24,7 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_read(&mut self.channel, self.request, peri_addr, buf, options) } + /// See [`Transfer::new_read_raw()`]. pub unsafe fn read_raw<'a, W: Word>( &'a mut self, peri_addr: *mut W, @@ -30,6 +34,7 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_read_raw(&mut self.channel, self.request, peri_addr, buf, options) } + /// See [`Transfer::new_write()`]. pub unsafe fn write<'a, W: Word>( &'a mut self, buf: &'a [W], @@ -39,6 +44,7 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_write(&mut self.channel, self.request, buf, peri_addr, options) } + /// See [`Transfer::new_write_raw()`]. pub unsafe fn write_raw<'a, W: Word>( &'a mut self, buf: *const [W], @@ -48,7 +54,7 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_write_raw(&mut self.channel, self.request, buf, peri_addr, options) } - #[allow(dead_code)] + /// See [`Transfer::new_write_repeated()`]. pub unsafe fn write_repeated<'a, W: Word>( &'a mut self, repeated: &'a W, diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index ae53deb089..cf961b954a 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -66,7 +66,7 @@ macro_rules! dma_trait { ($signal:ident, $instance:path$(, $mode:path)?) => { #[doc = concat!(stringify!($signal), " DMA request trait")] pub trait $signal: crate::dma::Channel { - #[doc = concat!("Get the DMA request number needed to use this channel as", stringify!($signal))] + #[doc = concat!("Get the DMA request number needed to use this channel as ", stringify!($signal))] /// Note: in some chips, ST calls this the "channel", and calls channels "streams". /// `embassy-stm32` always uses the "channel" and "request number" names. fn request(&self) -> crate::dma::Request; diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index f8041bf1e4..706ed91a30 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -8,14 +8,14 @@ use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ}; -use stm32_metapac::timer::{regs, TimGp16}; +use stm32_metapac::timer::{regs, Tim4ch}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; use crate::rcc::{self, SealedRccPeripheral}; #[cfg(feature = "low-power")] use crate::rtc::Rtc; -use crate::timer::{CoreInstance, GeneralInstance1Channel}; +use crate::timer::{CoreInstance, General1ChInstance}; use crate::{interrupt, peripherals}; // NOTE regarding ALARM_COUNT: @@ -205,8 +205,8 @@ foreach_interrupt! { }; } -fn regs_gp16() -> TimGp16 { - unsafe { TimGp16::from_ptr(T::regs()) } +fn regs_4ch() -> Tim4ch { + unsafe { Tim4ch::from_ptr(T::regs()) } } // Clock timekeeping works with something we call "periods", which are time intervals @@ -274,7 +274,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { impl RtcDriver { fn init(&'static self, cs: critical_section::CriticalSection) { - let r = regs_gp16(); + let r = regs_4ch(); rcc::enable_and_reset_with_cs::(cs); @@ -306,14 +306,14 @@ impl RtcDriver { w.set_ccie(0, true); }); - ::CaptureCompareInterrupt::unpend(); - unsafe { ::CaptureCompareInterrupt::enable() }; + ::CaptureCompareInterrupt::unpend(); + unsafe { ::CaptureCompareInterrupt::enable() }; r.cr1().modify(|w| w.set_cen(true)); } fn on_interrupt(&self) { - let r = regs_gp16(); + let r = regs_4ch(); // XXX: reduce the size of this critical section ? critical_section::with(|cs| { @@ -323,7 +323,7 @@ impl RtcDriver { // Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT. // Other approaches such as writing all zeros, or RMWing won't work, they can // miss interrupts. - r.sr().write_value(regs::SrGp16(!sr.0)); + r.sr().write_value(regs::Sr4ch(!sr.0)); // Overflow if sr.uif() { @@ -344,7 +344,7 @@ impl RtcDriver { } fn next_period(&self) { - let r = regs_gp16(); + let r = regs_4ch(); // We only modify the period from the timer interrupt, so we know this can't race. let period = self.period.load(Ordering::Relaxed) + 1; @@ -408,7 +408,7 @@ impl RtcDriver { /// Add the given offset to the current time fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { let offset = offset.as_ticks(); - let cnt = regs_gp16().cnt().read().cnt() as u32; + let cnt = regs_4ch().cnt().read().cnt() as u32; let period = self.period.load(Ordering::SeqCst); // Correct the race, if it exists @@ -434,7 +434,7 @@ impl RtcDriver { let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; self.period.store(period, Ordering::SeqCst); - regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); + regs_4ch().cnt().write(|w| w.set_cnt(cnt as u16)); // Now, recompute all alarms for i in 0..ALARM_COUNT { @@ -491,7 +491,7 @@ impl RtcDriver { .unwrap() .start_wakeup_alarm(time_until_next_alarm, cs); - regs_gp16().cr1().modify(|w| w.set_cen(false)); + regs_4ch().cr1().modify(|w| w.set_cen(false)); Ok(()) } @@ -501,7 +501,7 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Resume the timer with the given offset pub(crate) fn resume_time(&self) { - if regs_gp16().cr1().read().cen() { + if regs_4ch().cr1().read().cen() { // Time isn't currently stopped return; @@ -510,14 +510,14 @@ impl RtcDriver { critical_section::with(|cs| { self.stop_wakeup_alarm(cs); - regs_gp16().cr1().modify(|w| w.set_cen(true)); + regs_4ch().cr1().modify(|w| w.set_cen(true)); }) } } impl Driver for RtcDriver { fn now(&self) -> u64 { - let r = regs_gp16(); + let r = regs_4ch(); let period = self.period.load(Ordering::Relaxed); compiler_fence(Ordering::Acquire); @@ -548,7 +548,7 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let r = regs_gp16(); + let r = regs_4ch(); let n = alarm.id() as usize; let alarm = self.get_alarm(cs, alarm); diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 46ccbf3df9..51b74d39e5 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -1,82 +1,140 @@ -//! PWM driver with complementary output support. +//! PWM driver with complementary (negated) output support. -use core::marker::PhantomData; +use core::array; use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals::Ckd; use super::low_level::{CountingMode, OutputPolarity, Timer}; -use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; +use super::raw::{RawTimer, RawTimerPin}; use super::{ - AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin, - Channel4ComplementaryPin, + Advanced4ChInstance, Advanced4ChTim, Ch1, Ch1N, Ch2, Ch2N, Ch3, Ch3N, Ch4, Ch4N, Channel, ChannelMarker, + NChannelMarker, TimerPin, }; -use crate::gpio::{AnyPin, OutputType}; +use crate::gpio::{AfType, OutputType, Speed}; use crate::time::Hertz; use crate::timer::low_level::OutputCompareMode; use crate::Peripheral; -/// Complementary PWM pin wrapper. +/// Builder for [`ComplementaryPwm`]. /// -/// This wraps a pin to make it usable with PWM. -pub struct ComplementaryPwmPin<'d, T, C> { - _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(T, C)>, +/// Create the builder using [`Builder::new()`], then attach output pins using methods on the +/// builder, and finally build the [`ComplementaryPwm`] driver using one of the `build` methods(). +pub struct Builder<'d, T> { + tim: PeripheralRef<'d, T>, + channel_pins: [Option>; 4], + n_channel_pins: [Option>; 4], } -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: impl Peripheral

> + 'd, output_type: OutputType) -> Self { - into_ref!(pin); - 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.map_into(), - phantom: PhantomData, - } +impl<'d, T: Advanced4ChInstance> Builder<'d, T> { + /// Create a builder for the PWM driver using timer peripheral `tim`. + pub fn new(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + Self { + tim, + channel_pins: array::from_fn(|_| None), + n_channel_pins: array::from_fn(|_| None), + } + } + + /// Attach an output pin to the PWM driver. + /// + /// You may use convenience methods [`ch1_pin()`][Self::ch1_pin()] to `ch4_pin()` to aid type + /// inference. + pub fn pin( + &mut self, + pin: impl Peripheral

> + 'd, + output_type: OutputType, + ) -> &mut Self { + let pin = RawTimerPin::new(pin, AfType::output(output_type, Speed::VeryHigh)); + self.channel_pins[C::CHANNEL.index()] = Some(pin); + self + } + + /// Attach a complementary (negated) output pin to the PWM driver. + /// + /// You may use convenience methods [`ch1n_pin()`][Self::ch1n_pin()] to `ch4n_pin()` to aid type + /// inference. + pub fn n_pin( + &mut self, + pin: impl Peripheral

> + 'd, + output_type: OutputType, + ) -> &mut Self { + let pin = RawTimerPin::new(pin, AfType::output(output_type, Speed::VeryHigh)); + self.n_channel_pins[C::N_CHANNEL.index()] = Some(pin); + self + } +} + +macro_rules! channel_impl { + ($chx_pin:ident, $chxn_pin:ident, $channel:ident, $nchannel:ident) => { + impl<'d, T: Advanced4ChInstance> Builder<'d, T> { + #[doc = concat!( + "Attach an output pin for channel ", + stringify!($channel), + " to the complementary PWM driver.\n\nSee [`pin()`][Self::pin()] for details.", + )] + pub fn $chx_pin( + &mut self, + pin: impl Peripheral

> + 'd, + output_type: OutputType, + ) -> &mut Self { + self.pin::<$channel>(pin, output_type) + } + + #[doc = concat!( + "Attach a complementary output pin for channel ", + stringify!($channel), + " to the complementary PWM driver.\n\nSee [`n_pin()`][Self::pin()] for details.", + )] + pub fn $chxn_pin( + &mut self, + pin: impl Peripheral

> + 'd, + output_type: OutputType, + ) -> &mut Self { + self.n_pin::<$nchannel>(pin, output_type) } } }; } +channel_impl!(ch1_pin, ch1n_pin, Ch1, Ch1N); +channel_impl!(ch2_pin, ch2n_pin, Ch2, Ch2N); +channel_impl!(ch3_pin, ch3n_pin, Ch3, Ch3N); +channel_impl!(ch4_pin, ch4n_pin, Ch4, Ch4N); + +impl<'d, T: Advanced4ChInstance> Builder<'d, T> +where + PeripheralRef<'d, T>: Peripheral

+ 'd, +{ + /// Initialize the complementary PWM driver. + pub fn build(self, freq: Hertz, counting_mode: CountingMode) -> ComplementaryPwm<'d> { + let raw = RawTimer::new_advanced_4ch(self.tim); + ComplementaryPwm::new_inner(raw, self.channel_pins, self.n_channel_pins, freq, counting_mode) + } +} -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>, +/// PWM driver with support for standard and complementary (negated) outputs. +/// +/// Use [`Builder`] to build an instance of this driver. +pub struct ComplementaryPwm<'d> { + inner: Timer<'d, Advanced4ChTim>, + _channel_pins: [Option>; 4], + _n_channel_pins: [Option>; 4], } -impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { - /// Create a new complementary PWM driver. - #[allow(clippy::too_many_arguments)] - pub fn new( - tim: impl Peripheral

+ 'd, - _ch1: Option>, - _ch1n: Option>, - _ch2: Option>, - _ch2n: Option>, - _ch3: Option>, - _ch3n: Option>, - _ch4: Option>, - _ch4n: Option>, +impl<'d> ComplementaryPwm<'d> { + fn new_inner( + raw: RawTimer<'d, Advanced4ChTim>, + channel_pins: [Option>; 4], + n_channel_pins: [Option>; 4], freq: Hertz, counting_mode: CountingMode, ) -> Self { - Self::new_inner(tim, freq, counting_mode) - } - - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - let mut this = Self { inner: Timer::new(tim) }; + let mut this = Self { + inner: Timer::new(raw), + _channel_pins: channel_pins, + _n_channel_pins: n_channel_pins, + }; this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); @@ -111,7 +169,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Note: when you call this, the max duty value changes, so you will have to /// call `set_duty` on all channels with the duty calculated based on the new max duty. pub fn set_frequency(&mut self, freq: Hertz) { - let multiplier = if self.inner.get_counting_mode().is_center_aligned() { + let multiplier = if self.inner.counting_mode().is_center_aligned() { 2u8 } else { 1u8 @@ -122,15 +180,15 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() as u16 + 1 + pub fn max_duty(&self) -> u16 { + self.inner.max_compare_value() as u16 + 1 } /// Set the duty for a given channel. /// - /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. + /// The value ranges from 0 for 0% duty, to [`max_duty`](Self::max_duty) for 100% duty, both included. pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty <= self.get_max_duty()); + assert!(duty <= self.max_duty()); self.inner.set_compare_value(channel, duty as _) } @@ -149,7 +207,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { } } -impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { +impl<'d> embedded_hal_02::Pwm for ComplementaryPwm<'d> { type Channel = Channel; type Time = Hertz; type Duty = u16; @@ -165,15 +223,15 @@ impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm< } fn get_period(&self) -> Self::Time { - self.inner.get_frequency() + self.inner.frequency() } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) as u16 + self.inner.compare_value(channel) as u16 } fn get_max_duty(&self) -> Self::Duty { - self.inner.get_max_compare_value() as u16 + 1 + self.inner.max_compare_value() as u16 + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 341ac2c042..452a98bd85 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -1,133 +1,202 @@ //! Input capture driver. +use core::array; use core::future::Future; -use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; use embassy_hal_internal::{into_ref, PeripheralRef}; use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; +use super::raw::{RawTimer, RawTimerPin}; use super::{ - CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, - GeneralInstance4Channel, + CaptureCompareInterruptHandler, Ch1, Ch2, Ch3, Ch4, Channel, ChannelMarker, CoreInstance, General1ChInstance, + General1ChTim, General4ChInstance, General4ChTim, IsGeneral1ChTim, State, TimerPin, }; -use crate::gpio::{AfType, AnyPin, Pull}; -use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::gpio::{AfType, Pull}; +use crate::interrupt::typelevel::{Binding, Interrupt as _}; +use crate::interrupt::{Interrupt, InterruptExt}; use crate::time::Hertz; use crate::Peripheral; -/// 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 {} +/// Builder for [`InputCapture`] driver. +pub struct Builder<'d, T> { + tim: PeripheralRef<'d, T>, + channel_pins: [Option>; 4], +} -/// Capture pin wrapper. -/// -/// This wraps a pin to make it usable with capture. -pub struct CapturePin<'d, T, C> { - _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(T, C)>, +impl<'d, T: General1ChInstance> Builder<'d, T> { + /// Start building an input capture driver from a timer peripheral. + pub fn new( + tim: impl Peripheral

+ 'd, + _irq: impl Binding> + 'd, + ) -> Self { + into_ref!(tim); + let channel_pins = array::from_fn(|_| None); + Self { tim, channel_pins } + } + + /// Attach an input pin to the input capture driver. + /// + /// You may use convenience methods [`ch1_pin()`][Self::ch1_pin()] to `ch4_pin()` to aid type + /// inference. + pub fn pin( + &mut self, + pin: impl Peripheral

> + 'd, + pull: Pull, + ) -> &mut Self { + let pin = RawTimerPin::new(pin, AfType::input(pull)); + self.channel_pins[C::CHANNEL.index()] = Some(pin); + self + } } +#[rustfmt::skip] 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: impl Peripheral

> + 'd, pull: Pull) -> Self { - into_ref!(pin); - pin.set_as_af(pin.af_num(), AfType::input(pull)); - CapturePin { - _pin: pin.map_into(), - phantom: PhantomData, - } + ($chx_pin:ident, $channel:ident) => { + impl<'d, T: General1ChInstance> Builder<'d, T> { + #[doc = concat!( + "Attach an input pin for channel ", + stringify!($channel), + " to the input capture driver.\n\nSee [`pin()`][Self::pin()] for details.", + )] + pub fn $chx_pin( + &mut self, + pin: impl Peripheral

> + 'd, + pull: Pull, + ) -> &mut Self { + self.pin::<$channel>(pin, pull) } } }; } +channel_impl!(ch1_pin, Ch1); +channel_impl!(ch2_pin, Ch2); +channel_impl!(ch3_pin, Ch3); +channel_impl!(ch4_pin, Ch4); + +impl<'d, T: CoreInstance> Builder<'d, T> +where + PeripheralRef<'d, T>: Peripheral

+ 'd, +{ + /// Initialize the input capture driver with tick frequency `freq`. + pub fn build(self, freq: Hertz) -> InputCapture<'d, General1ChTim> + where + T: General1ChInstance, + { + let raw = RawTimer::new_general_1ch(self.tim); + let cc_interrupt = T::CaptureCompareInterrupt::IRQ; + let state = T::state(); + InputCapture::new_inner(raw, self.channel_pins, cc_interrupt, state, |inner| { + inner.set_tick_frequency(freq); + }) + } -channel_impl!(new_ch1, Ch1, Channel1Pin); -channel_impl!(new_ch2, Ch2, Channel2Pin); -channel_impl!(new_ch3, Ch3, Channel3Pin); -channel_impl!(new_ch4, Ch4, Channel4Pin); + /// Initialize the input capture driver with tick frequency `freq` and `counting_mode`. + /// + /// This method is only available if the timer peripheral implements [`General4ChInstance`], + /// because less capable timers do not implement counting mode. + pub fn build_4ch(self, freq: Hertz, counting_mode: CountingMode) -> InputCapture<'d, General4ChTim> + where + T: General4ChInstance, + { + let raw = RawTimer::new_general_4ch(self.tim); + let cc_interrupt = T::CaptureCompareInterrupt::IRQ; + let state = T::state(); + InputCapture::new_inner(raw, self.channel_pins, cc_interrupt, state, |inner| { + inner.set_counting_mode(counting_mode); + inner.set_tick_frequency(freq); + }) + } +} /// Input capture driver. -pub struct InputCapture<'d, T: GeneralInstance4Channel> { - inner: Timer<'d, T>, +/// +/// Use [`Builder`] to build an instance of this driver. +pub struct InputCapture<'d, Tim> { + inner: Timer<'d, Tim>, + channel_pins: [Option>; 4], + cc_interrupt: Interrupt, + state: &'d State, } -impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { - /// Create a new input capture driver. - pub fn new( - tim: impl Peripheral

+ 'd, - _ch1: Option>, - _ch2: Option>, - _ch3: Option>, - _ch4: Option>, - _irq: impl Binding> + 'd, - freq: Hertz, - counting_mode: CountingMode, +impl<'d, Tim: IsGeneral1ChTim> InputCapture<'d, Tim> { + fn new_inner( + raw: RawTimer<'d, Tim>, + channel_pins: [Option>; 4], + cc_interrupt: Interrupt, + state: &'d State, + config_fn: impl FnOnce(&Timer<'d, Tim>), ) -> Self { - Self::new_inner(tim, freq, counting_mode) - } - - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - let mut this = Self { inner: Timer::new(tim) }; - - this.inner.set_counting_mode(counting_mode); - this.inner.set_tick_freq(freq); - this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + let this = Self { + inner: Timer::new(raw), + channel_pins, + cc_interrupt, + state, + }; + + config_fn(&this.inner); + this.inner.enable_outputs(); // Required for advanced timers, see [`RawTimer::enable_outputs()`] for details this.inner.start(); // enable NVIC interrupt - T::CaptureCompareInterrupt::unpend(); - unsafe { T::CaptureCompareInterrupt::enable() }; + this.cc_interrupt.unpend(); + unsafe { this.cc_interrupt.enable() }; this } /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.enable_channel(channel, true); } /// Disable the given channel. pub fn disable(&mut self, channel: Channel) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.enable_channel(channel, false); } /// Check whether given channel is enabled pub fn is_enabled(&self, channel: Channel) -> bool { - self.inner.get_channel_enable_state(channel) + assert!(self.channel_pins[channel.index()].is_some()); + self.inner.channel_enable_state(channel) } /// Set the input capture mode for a given channel. pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.set_input_capture_mode(channel, mode); } /// Set input TI selection. pub fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.set_input_ti_selection(channel, tisel) } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: Channel) -> u32 { - self.inner.get_capture_value(channel) + pub fn capture_value(&self, channel: Channel) -> u32 { + assert!(self.channel_pins[channel.index()].is_some()); + self.inner.capture_value(channel) } /// Get input interrupt. pub fn get_input_interrupt(&self, channel: Channel) -> bool { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.get_input_interrupt(channel) } - fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { + fn new_future<'f>( + &'f self, + channel: Channel, + mode: InputCaptureMode, + tisel: InputTISelection, + ) -> InputCaptureFuture<'f, Tim> { // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode + assert!(self.channel_pins[channel.index()].is_some()); self.inner.set_input_ti_selection(channel, tisel); self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER); self.inner.set_input_capture_mode(channel, mode); @@ -135,10 +204,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { self.inner.enable_channel(channel, true); self.inner.enable_input_interrupt(channel, true); - InputCaptureFuture { - channel, - phantom: PhantomData, - } + InputCaptureFuture { driver: self, channel } } /// Asynchronously wait until the pin sees a rising edge. @@ -179,33 +245,33 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } #[must_use = "futures do nothing unless you `.await` or poll them"] -struct InputCaptureFuture { +struct InputCaptureFuture<'f, Tim: IsGeneral1ChTim> { + driver: &'f InputCapture<'f, Tim>, channel: Channel, - phantom: PhantomData, } -impl Drop for InputCaptureFuture { +impl<'f, Tim: IsGeneral1ChTim> Drop for InputCaptureFuture<'f, Tim> { fn drop(&mut self) { critical_section::with(|_| { - let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; - // disable interrupt enable - regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); + self.driver + .inner + .raw + .dier_1ch() + .modify(|w| w.set_ccie(self.channel.index(), false)); }); } } -impl Future for InputCaptureFuture { +impl<'f, Tim: IsGeneral1ChTim> Future for InputCaptureFuture<'f, Tim> { type Output = u32; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - T::state().cc_waker[self.channel.index()].register(cx.waker()); - - let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + self.driver.state.cc_waker[self.channel.index()].register(cx.waker()); - let dier = regs.dier().read(); + let dier = self.driver.inner.raw.dier_1ch().read(); if !dier.ccie(self.channel.index()) { - let val = regs.ccr(self.channel.index()).read().0; + let val = self.driver.inner.raw.ccr(self.channel.index()).read().0; Poll::Ready(val) } else { Poll::Pending diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index e643722aaa..2b2d89766b 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -1,18 +1,18 @@ //! Low-level timer driver. //! -//! This is an unopinionated, very low-level driver for all STM32 timers. It allows direct register -//! manipulation with the `regs_*()` methods, and has utility functions that are thin wrappers -//! over the registers. +//! This is an unopinionated, low-level driver for all STM32 timers. It wraps [`RawTimer`] and adds +//! utility functions that are thin wrappers over the registers. //! //! The available functionality depends on the timer type. -use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; // Re-export useful enums pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; -use super::*; +use super::raw::RawTimer; +use super::{Channel, IsCcDmaTim, IsCoreTim, IsGeneral1ChTim, IsGeneral2ChTim, IsGeneral4ChTim, IsUpDmaTim}; +#[cfg(not(timer_l0))] +use super::{IsAdvanced1ChTim, IsAdvanced4ChTim}; use crate::pac::timer::vals; -use crate::rcc; use crate::time::Hertz; /// Input capture mode. @@ -178,54 +178,41 @@ impl From for bool { } /// Low-level timer driver. -pub struct Timer<'d, T: CoreInstance> { - tim: PeripheralRef<'d, T>, +/// +/// The low-level driver is just a wrapper around [`RawTimer`] that provides some convenience +/// methods which abstract away raw register access. +pub struct Timer<'d, Tim> { + /// The raw timer driver that is wrapped by this driver. + pub raw: RawTimer<'d, Tim>, } -impl<'d, T: CoreInstance> Drop for Timer<'d, T> { - fn drop(&mut self) { - rcc::disable::(); - } -} - -impl<'d, T: CoreInstance> Timer<'d, T> { - /// Create a new timer driver. - pub fn new(tim: impl Peripheral

+ 'd) -> Self { - into_ref!(tim); - - rcc::enable_and_reset::(); - - Self { tim } - } - - /// Get access to the virutal core 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_core(&self) -> crate::pac::timer::TimCore { - unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) } - } - - #[cfg(not(stm32l0))] - fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 { - unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } +impl<'d, Tim: IsCoreTim> Timer<'d, Tim> { + /// Create a new timer driver from a raw timer driver. + pub fn new(raw: RawTimer<'d, Tim>) -> Self { + Self { raw } } /// Start the timer. pub fn start(&self) { - self.regs_core().cr1().modify(|r| r.set_cen(true)); + self.raw.cr1_core().modify(|r| r.set_cen(true)); } /// Stop the timer. pub fn stop(&self) { - self.regs_core().cr1().modify(|r| r.set_cen(false)); + self.raw.cr1_core().modify(|r| r.set_cen(false)); + } + + /// Enable timer outputs. + /// + /// Calling this is necessary to enable outputs on advanced timers. See + /// [`RawTimer::enable_outputs()`] for details. + pub fn enable_outputs(&self) { + self.raw.enable_outputs() } /// Reset the counter value to 0 pub fn reset(&self) { - self.regs_core().cnt().write(|r| r.set_cnt(0)); + self.raw.cnt().write(|r| r.set_cnt(0)); } /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. @@ -237,69 +224,71 @@ impl<'d, T: CoreInstance> Timer<'d, T> { pub fn set_frequency(&self, frequency: Hertz) { let f = frequency.0; assert!(f > 0); - let timer_f = T::frequency().0; + let timer_f = self.raw.clock_frequency().0; - match T::BITS { - TimerBits::Bits16 => { - let pclk_ticks_per_timer_period = timer_f / f; - let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); - let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1); + #[cfg(not(timer_l0))] + if let Some(regs_32) = self.raw.try_get_32bit_regs() { + let pclk_ticks_per_timer_period = (timer_f / f) as u64; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); + let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr = unwrap!(u16::try_from(divide_by - 1)); + // the timer counts `0..=arr`, we want it to count `0..divide_by` + let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); - let regs = self.regs_core(); - regs.psc().write_value(psc); - regs.arr().write(|r| r.set_arr(arr)); + regs_32.psc().write_value(psc); + regs_32.arr().write_value(arr); - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - let pclk_ticks_per_timer_period = (timer_f / f) as u64; - let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); - let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); - - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); - - let regs = self.regs_gp32_unchecked(); - regs.psc().write_value(psc); - regs.arr().write_value(arr); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } + regs_32.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + regs_32.egr().write(|r| r.set_ug(true)); + regs_32.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + return; } + + let pclk_ticks_per_timer_period = timer_f / f; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); + let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1); + + // the timer counts `0..=arr`, we want it to count `0..divide_by` + let arr = unwrap!(u16::try_from(divide_by - 1)); + + self.raw.psc().write_value(psc); + self.raw.arr().write(|r| r.set_arr(arr)); + + self.raw.cr1_core().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + self.raw.egr_core().write(|r| r.set_ug(true)); + self.raw.cr1_core().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + + /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. + pub fn max_compare_value(&self) -> u32 { + #[cfg(not(timer_l0))] + if let Some(regs_32) = self.raw.try_get_32bit_regs() { + return regs_32.arr().read(); + } + self.raw.arr().read().arr() as u32 } /// Set tick frequency. - pub fn set_tick_freq(&mut self, freq: Hertz) { + pub fn set_tick_frequency(&self, freq: Hertz) { let f = freq; assert!(f.0 > 0); - let timer_f = self.get_clock_frequency(); + let timer_f = self.raw.clock_frequency(); let pclk_ticks_per_timer_period = timer_f / f; let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into()); - - let regs = self.regs_core(); - regs.psc().write_value(psc); + self.raw.psc().write_value(psc); // Generate an Update Request - regs.egr().write(|r| r.set_ug(true)); + self.raw.egr_core().write(|r| r.set_ug(true)); } /// Clear update interrupt. /// /// Returns whether the update interrupt flag was set. pub fn clear_update_interrupt(&self) -> bool { - let regs = self.regs_core(); - let sr = regs.sr().read(); + let sr = self.raw.sr_core().read(); if sr.uif() { - regs.sr().modify(|r| { + self.raw.sr_core().modify(|r| { r.set_uif(false); }); true @@ -310,371 +299,272 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// Enable/disable the update interrupt. pub fn enable_update_interrupt(&self, enable: bool) { - self.regs_core().dier().modify(|r| r.set_uie(enable)); + self.raw.dier_core().modify(|r| r.set_uie(enable)); } /// Enable/disable autoreload preload. pub fn set_autoreload_preload(&self, enable: bool) { - self.regs_core().cr1().modify(|r| r.set_arpe(enable)); + self.raw.cr1_core().modify(|r| r.set_arpe(enable)); } /// Get the timer frequency. - pub fn get_frequency(&self) -> Hertz { - let timer_f = T::frequency(); - - match T::BITS { - TimerBits::Bits16 => { - let regs = self.regs_core(); - let arr = regs.arr().read().arr(); - let psc = regs.psc().read(); - - timer_f / arr / (psc + 1) - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - let regs = self.regs_gp32_unchecked(); - let arr = regs.arr().read(); - let psc = regs.psc().read(); - - timer_f / arr / (psc + 1) - } + pub fn frequency(&self) -> Hertz { + let timer_f = self.raw.clock_frequency(); + + #[cfg(not(timer_l0))] + if let Some(regs_32) = self.raw.try_get_32bit_regs() { + let arr = regs_32.arr().read(); + let psc = regs_32.psc().read(); + return timer_f / arr / (psc + 1); } - } - /// Get the clock frequency of the timer (before prescaler is applied). - pub fn get_clock_frequency(&self) -> Hertz { - T::frequency() + let arr = self.raw.arr().read().arr(); + let psc = self.raw.psc().read(); + timer_f / arr / (psc + 1) } -} -impl<'d, T: BasicNoCr2Instance> Timer<'d, T> { - /// Get access to the Baisc 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 { - unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) } + /// Get the internal clock frequency of the timer (before prescaler is applied). + pub fn clock_frequency(&self) -> Hertz { + self.raw.clock_frequency() } +} +impl<'d, Tim: IsUpDmaTim> Timer<'d, Tim> { /// Enable/disable the update dma. pub fn enable_update_dma(&self, enable: bool) { - self.regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable)); + self.raw.dier_updma().modify(|r| r.set_ude(enable)); } /// Get the update dma enable/disable state. - pub fn get_update_dma_state(&self) -> bool { - self.regs_basic_no_cr2().dier().read().ude() + pub fn update_dma_state(&self) -> bool { + self.raw.dier_updma().read().ude() } } -impl<'d, T: BasicInstance> Timer<'d, T> { - /// Get access to the Baisc 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_basic(&self) -> crate::pac::timer::TimBasic { - unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) } - } -} - -impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { - /// Get access to the general purpose 1 channel 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch { - unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) } +impl<'d, Tim: IsGeneral1ChTim> Timer<'d, Tim> { + /// Convert a [`Channel`] enum to index, checking that it corresponds to a valid channel for + /// this timer. + fn to_index(&self, channel: Channel) -> usize { + let i = channel.index(); + assert!(i < self.raw.channel_count().to_usize()); + i } /// Set clock divider. pub fn set_clock_division(&self, ckd: vals::Ckd) { - self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd)); - } - - /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - pub fn get_max_compare_value(&self) -> u32 { - match T::BITS { - TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, - #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), - } - } -} - -impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { - /// Get access to the general purpose 2 channel 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch { - unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) } - } -} - -impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { - /// Get access to the general purpose 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } - } - - /// Enable timer outputs. - pub fn enable_outputs(&self) { - self.tim.enable_outputs() - } - - /// Set counting mode. - pub fn set_counting_mode(&self, mode: CountingMode) { - let (cms, dir) = mode.into(); - - let timer_enabled = self.regs_core().cr1().read().cen(); - // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. - // Changing direction is discouraged while the timer is running. - assert!(!timer_enabled); - - self.regs_gp16().cr1().modify(|r| r.set_dir(dir)); - self.regs_gp16().cr1().modify(|r| r.set_cms(cms)) - } - - /// Get counting mode. - pub fn get_counting_mode(&self) -> CountingMode { - let cr1 = self.regs_gp16().cr1().read(); - (cr1.cms(), cr1.dir()).into() + self.raw.cr1_1ch().modify(|r| r.set_ckd(ckd)); } /// Set input capture filter. pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) { - let raw_channel = channel.index(); - self.regs_gp16() - .ccmr_input(raw_channel / 2) - .modify(|r| r.set_icf(raw_channel % 2, icf)); + let i = self.to_index(channel); + self.raw.ccmr_input_1ch(i / 2).modify(|r| r.set_icf(i % 2, icf)); } /// Clear input interrupt. pub fn clear_input_interrupt(&self, channel: Channel) { - self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); + let i = self.to_index(channel); + self.raw.sr_1ch().modify(|r| r.set_ccif(i, false)); } /// Get input interrupt. pub fn get_input_interrupt(&self, channel: Channel) -> bool { - self.regs_gp16().sr().read().ccif(channel.index()) + let i = self.to_index(channel); + self.raw.sr_1ch().read().ccif(i) } /// Enable input interrupt. pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { - self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); + let i = self.to_index(channel); + self.raw.dier_1ch().modify(|r| r.set_ccie(i, enable)); } /// Set input capture prescaler. pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) { - let raw_channel = channel.index(); - self.regs_gp16() - .ccmr_input(raw_channel / 2) - .modify(|r| r.set_icpsc(raw_channel % 2, factor)); + let i = self.to_index(channel); + self.raw.ccmr_input_1ch(i / 2).modify(|r| r.set_icpsc(i % 2, factor)); } /// Set input TI selection. pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) { - let raw_channel = channel.index(); - self.regs_gp16() - .ccmr_input(raw_channel / 2) - .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); + let i = self.to_index(channel); + self.raw + .ccmr_input_1ch(i / 2) + .modify(|r| r.set_ccs(i % 2, tisel.into())); } /// Set input capture mode. pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) { - self.regs_gp16().ccer().modify(|r| match mode { + let i = self.to_index(channel); + self.raw.ccer_1ch().modify(|r| match mode { InputCaptureMode::Rising => { - r.set_ccnp(channel.index(), false); - r.set_ccp(channel.index(), false); + r.set_ccnp(i, false); + r.set_ccp(i, false); } InputCaptureMode::Falling => { - r.set_ccnp(channel.index(), false); - r.set_ccp(channel.index(), true); + r.set_ccnp(i, false); + r.set_ccp(i, true); } InputCaptureMode::BothEdges => { - r.set_ccnp(channel.index(), true); - r.set_ccp(channel.index(), true); + r.set_ccnp(i, true); + r.set_ccp(i, true); } }); } /// Set output compare mode. pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { - let raw_channel: usize = channel.index(); - self.regs_gp16() - .ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + let i = self.to_index(channel); + self.raw + .ccmr_output_1ch(i / 2) + .modify(|w| w.set_ocm(i % 2, mode.into())); } /// Set output polarity. pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { - self.regs_gp16() - .ccer() - .modify(|w| w.set_ccp(channel.index(), polarity.into())); + let i = self.to_index(channel); + self.raw.ccer_1ch().modify(|w| w.set_ccp(i, polarity.into())); } /// Enable/disable a channel. pub fn enable_channel(&self, channel: Channel, enable: bool) { - self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); + let i = self.to_index(channel); + self.raw.ccer_1ch().modify(|w| w.set_cce(i, enable)); } /// Get enable/disable state of a channel - pub fn get_channel_enable_state(&self, channel: Channel) -> bool { - self.regs_gp16().ccer().read().cce(channel.index()) + pub fn channel_enable_state(&self, channel: Channel) -> bool { + let i = self.to_index(channel); + self.raw.ccer_1ch().read().cce(i) } /// Set compare value for a channel. pub fn set_compare_value(&self, channel: Channel, value: u32) { - match T::BITS { - TimerBits::Bits16 => { - let value = unwrap!(u16::try_from(value)); - self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); - } - #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); - } + let i = self.to_index(channel); + + #[cfg(not(timer_l0))] + if let Some(regs_32) = self.raw.try_get_32bit_regs() { + regs_32.ccr(i).write_value(value); + return; } + + let value = unwrap!(u16::try_from(value)); + self.raw.ccr(i).modify(|w| w.set_ccr(value)); } /// Get compare value for a channel. - pub fn get_compare_value(&self, channel: Channel) -> u32 { - match T::BITS { - TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, - #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), + pub fn compare_value(&self, channel: Channel) -> u32 { + let i = self.to_index(channel); + + #[cfg(not(timer_l0))] + if let Some(regs_32) = self.raw.try_get_32bit_regs() { + return regs_32.ccr(i).read(); } + + self.raw.ccr(i).read().ccr() as u32 } /// Get capture value for a channel. - pub fn get_capture_value(&self, channel: Channel) -> u32 { - self.get_compare_value(channel) + pub fn capture_value(&self, channel: Channel) -> u32 { + self.compare_value(channel) } /// Set output compare preload. pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) { - let channel_index = channel.index(); - self.regs_gp16() - .ccmr_output(channel_index / 2) - .modify(|w| w.set_ocpe(channel_index % 2, preload)); + let i = self.to_index(channel); + self.raw.ccmr_output_1ch(i / 2).modify(|w| w.set_ocpe(i % 2, preload)); + } + + /// Set divider for the dead time and sampling clock. + pub fn set_dead_time_clock_division(&self, value: vals::Ckd) { + self.raw.cr1_1ch().modify(|w| w.set_ckd(value)); } +} +impl<'d, Tim: IsGeneral2ChTim> Timer<'d, Tim> { + /// Set Timer Slave Mode + pub fn set_slave_mode(&self, sms: SlaveMode) { + self.raw.smcr_2ch().modify(|r| r.set_sms(sms)); + } + + /// Set Timer Trigger Source + pub fn set_trigger_source(&self, ts: TriggerSource) { + self.raw.smcr_2ch().modify(|r| r.set_ts(ts)); + } +} + +impl<'d, Tim: IsCcDmaTim> Timer<'d, Tim> { /// Get capture compare DMA selection - pub fn get_cc_dma_selection(&self) -> vals::Ccds { - self.regs_gp16().cr2().read().ccds() + pub fn cc_dma_selection(&self) -> vals::Ccds { + self.raw.cr2_ccdma().read().ccds() } /// Set capture compare DMA selection pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) { - self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds)) + self.raw.cr2_ccdma().modify(|w| w.set_ccds(ccds)) } /// Get capture compare DMA enable state - pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { - self.regs_gp16().dier().read().ccde(channel.index()) + pub fn cc_dma_enable_state(&self, channel: Channel) -> bool { + let i = self.to_index(channel); + self.raw.dier_ccdma().read().ccde(i) } /// Set capture compare DMA enable state pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { - self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) - } - - /// Set Timer Slave Mode - pub fn set_slave_mode(&self, sms: SlaveMode) { - self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); - } - - /// Set Timer Trigger Source - pub fn set_trigger_source(&self, ts: TriggerSource) { - self.regs_gp16().smcr().modify(|r| r.set_ts(ts)); + let i = self.to_index(channel); + self.raw.dier_ccdma().modify(|w| w.set_ccde(i, ccde)) } } -#[cfg(not(stm32l0))] -impl<'d, T: GeneralInstance32bit4Channel> Timer<'d, T> { - /// Get access to the general purpose 32bit timer registers. +// TODO: on `timer_l0`, these methods are available also for `IsGeneral2ChTim` +impl<'d, Tim: IsGeneral4ChTim> Timer<'d, Tim> { + /// Set counting mode. /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_gp32(&self) -> crate::pac::timer::TimGp32 { - unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } - } -} + /// You can only call this method if the timer is not enabled, otherwise we panic. + pub fn set_counting_mode(&self, mode: CountingMode) { + let (cms, dir) = mode.into(); -#[cfg(not(stm32l0))] -impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> { - /// Get access to the general purpose 1 channel with one complementary 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp { - unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) } + let timer_enabled = self.raw.cr1_core().read().cen(); + // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. + // Changing direction is discouraged while the timer is running. + assert!(!timer_enabled); + + self.raw.cr1_4ch().modify(|r| r.set_dir(dir)); + self.raw.cr1_4ch().modify(|r| r.set_cms(cms)) } - /// Set clock divider for the dead time. - pub fn set_dead_time_clock_division(&self, value: vals::Ckd) { - self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value)); + /// Get counting mode. + pub fn counting_mode(&self) -> CountingMode { + let cr1 = self.raw.cr1_4ch().read(); + (cr1.cms(), cr1.dir()).into() } +} +#[cfg(not(timer_l0))] +impl<'d, Tim: IsAdvanced1ChTim> Timer<'d, Tim> { /// Set dead time, as a fraction of the max duty value. pub fn set_dead_time_value(&self, value: u8) { - self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value)); + self.raw.bdtr().modify(|w| w.set_dtg(value)); } /// Set state of MOE-bit in BDTR register to en-/disable output pub fn set_moe(&self, enable: bool) { - self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable)); + self.raw.bdtr().modify(|w| w.set_moe(enable)); } } -#[cfg(not(stm32l0))] -impl<'d, T: AdvancedInstance2Channel> Timer<'d, T> { - /// Get access to the general purpose 2 channel with one complementary 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp { - unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) } - } -} - -#[cfg(not(stm32l0))] -impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { - /// Get access to the advanced timer registers. - pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv { - unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) } - } - +#[cfg(not(timer_l0))] +impl<'d, Tim: IsAdvanced4ChTim> Timer<'d, Tim> { /// Set complementary output polarity. pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { - self.regs_advanced() - .ccer() - .modify(|w| w.set_ccnp(channel.index(), polarity.into())); + let i = self.to_index(channel); + self.raw.ccer_adv1ch().modify(|w| w.set_ccnp(i, polarity.into())); } /// Enable/disable a complementary channel. pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) { - self.regs_advanced() - .ccer() - .modify(|w| w.set_ccne(channel.index(), enable)); + let i = self.to_index(channel); + self.raw.ccer_adv1ch().modify(|w| w.set_ccne(i, enable)); } } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 25782ee138..13f96f1c7d 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,56 +1,590 @@ -//! Timers, PWM, quadrature decoder. - +//! General-purpose timers (TIM). +//! +//! Every STM32 microcontroller contains several general-purpose timer peripherals. A single chip +//! contains several types of timers with different capabilities: +//! +//! - [**Basic timers**][BasicInstance] (TIM6, TIM7) can be used for timekeeping but don't have any inputs or +//! outputs. +//! - **General timers** have input/output channels with output comparison and input capture. They +//! can have 1, 2 or 4 channels: +//! - [1-channel][General1ChInstance] timers (TIM10, TIM14, ...) and +//! [2-channel][General2ChInstance] timers (TIM9, TIM12, TIM21, ...) (_channel timers_) +//! have somewhat limited feature set and reduced channel count. +//! - [4-channel][General4ChInstance] timers (TIM3, TIM4, ...) (_general-purpose timer_) have the +//! full feature set. +//! - [32-bit timer][General32BitInstance] (TIM2, TIM5, ...) timers are a variant of the +//! 4-channel timers with a 32-bit counter instead of the standard 16-bit counter. +//! - **Advanced timers** have additional features not available on general timers. Advanced timers +//! can also have 1, 2 or 4 channels: +//! - [Advanced 1-channel][Advanced1ChInstance] timers (TIM16, TIM17) and [advanced +//! 2-channel][Advanced2ChInstance] timers (TIM15, TIM21, ...) have one complementary output +//! and some additional features. +//! - [Advanced 4-channel][Advanced4ChInstance] timers (TIM1, TIM8) have four complementary +//! outputs (in addition to the standard four input/output channels) and the most advanced +//! feature set related to motor control and power conversion applications. +//! - Some chips also have low-power timers (LPTIM) and high-resolution timers (HRTIM), but these +//! are not covered by the drivers in this module. +//! +//! Note that the numbering of timer peripherals is usually consistent across all chip families, so +//! for example `TIM1` is always an advanced 4-channel timer and `TIM6` is always a basic timer. +//! +//! ## Instance traits +//! +//! For each type of timer, we export a trait which is implemented by the timer peripherals of that +//! type: +//! +//! - [`BasicInstance`] for basic timers +//! - For general timers: +//! - [`General1ChInstance`] for 1-channel timers +//! - [`General2ChInstance`] for 2-channel timers +//! - [`General4ChInstance`] for 4-channel timers +//! - [`General32BitInstance`] for 32-bit, 4-channel timers +//! - For advanced timers: +//! - [`Advanced1ChInstance`] for advanced 1-channel timers +//! - [`Advanced2ChInstance`] for advanced 2-channel timers +//! - [`Advanced4ChInstance`] for advanced 4-channel timers +//! +//! Fortunately, the timers are bit-compatible on the register level, so a more capable timer can +//! be used in place of a less capable timer: +//! +//! - Timers with more channels are more capable than timers with fewer channels (so, for +//! example, [`General2ChInstance`] is more capable than [`General1ChInstance`], and +//! [`Advanced4ChInstance`] is more capable than [`Advanced2ChInstance`]). +//! - Advanced channels are more capable than general timers _with the same number of channels_ +//! (so for example [`Advanced2ChInstance`] can be used where [`General2ChInstance`] is expected). +//! +//! These relations are encoded in the type system, so for example [`Advanced2ChInstance`] has +//! [`Advanced1ChInstance`] and [`General2ChInstance`] as supertraits. +//! +//! However, there is not a simple linear ordering of timers from least to most capable: for +//! example, neither of [`Advanced2ChInstance`] and [`General4ChInstance`] is more capable than the +//! other. They have some functionality in common, but either of them has a feature that the other +//! doesn't have. To accomodate these common feature subsets, we introduce "virtual" timer traits: +//! +//! - [`UpDmaInstance`] represents the common parts of [`BasicInstance`], [`General4ChInstance`] +//! and [`Advanced1ChInstance`]. +//! - [`MmsInstance`] represents the common parts of [`BasicInstance`] and [`General2ChInstance`]. +//! - [`CcDmaInstance`] represents the common parts of [`General4ChInstance`] and +//! [`Advanced1ChInstance`]. +//! - [`TrigDmaInstance`] represents the common parts of [`General4ChInstance`] and +//! [`Advanced2ChInstance`]. +//! +//! All these relations are also expressed in the type system, so for example +//! [`General4ChInstance`] implies [`MmsInstance`] and [`TrigDmaInstance`] implies +//! [`General2ChInstance`]. +//! +//! This means that for every subset of timer features that we might need, there is always a +//! _single_ trait that exactly matches the timer peripherals that support all of these features. +//! +//! ## Marker types +//! +//! The traits described in previous section are implemented for timer peripherals such as +//! [`TIM2`][crate::peripherals::TIM2]. In timer drivers, we often want to have basic functionality +//! that is available for most timers (such as PWM generation in the +//! [`SimplePwm`][self::simple_pwm::SimplePwm] driver, which requires just [`General1ChInstance`]), +//! but we also want to expose some additional features for timers that support them (such as +//! waveform PWM generation using DMA in the `SimplePwm` driver, which needs [`CcDmaInstance`]). +//! However, we also don't want to include the type of the concrete timer peripheral (such as +//! `TIM2`) in the type of the driver, to reduce code duplication from monomorphization and to +//! avoid leaking implementation details to the users of the driver. +//! +//! For these reasons, we introduce marker types. Each peripheral instance trait (such as +//! [`General4ChInstance`]) has an associated marker type ([`General4ChTim`]) and marker type trait +//! ([`IsGeneral4ChTim`]). We use the marker type as a type-level "witness" that the driver was +//! instantiated with a timer peripheral that has at least the given capability. For example, you +//! can [build `SimplePwm`][self::simple_pwm::Builder::build_4ch()] only from a +//! timer peripheral that implements [`General4ChInstance`]. +//! +//! We also introduce marker type traits to implement the "is at least as capable as" relation on +//! marker types. For example, the marker type trait [`IsGeneral4ChTim`] is implemented for +//! [`General4ChTim`] and [`Advanced4ChTim`], but not for [`General2ChTim`] or [`Advanced2ChTim`]. +//! The relation implied by the marker type traits (`Is*Tim`) is the same as the relation implied +//! by the instance traits (`*Instance`). The marker type traits allow us to express that some +//! driver methods are only available for some timers; for example, +//! [`SimplePwm::waveform_cc_dma()`][simple_pwm::SimplePwm::waveform_cc_dma()] is only +//! available when `Tim: IsCcDmaTim`, so we can use it on `SimplePwm` but not on +//! `SimplePwm`. +//! +//! ## Drivers +//! +//! STM32 timers are very versatile and can be used for a wide variety of purposes, from dimming a +//! LED to controlling a brushless motor. Embassy will never be able to handle all use cases, so we +//! expose high-level drivers for simple tasks (such as dimming a LED using PWM) but give you all +//! tools that you need to implement your own type-safe drivers for more advanced tasks (such as +//! controlling a brushless motor using an advanced timer): +//! +//! - [`raw::RawTimer`] and other items of the [`raw`] submodule expose the raw functionality that +//! gives you type-safe access to timer registers and timer pins. +//! - [`low_level::Timer`] is a thin wrapper over the raw driver that wraps register operations +//! with convenient method calls. +//! - Other submodules contain high-level drivers for common timer tasks, such as PWM output +//! ([`simple_pwm`]) or quadrature decoder input ([`qei`]). use core::marker::PhantomData; use embassy_sync::waitqueue::AtomicWaker; -#[cfg(not(stm32l0))] +#[allow(unused_imports)] // used in documentation links +use crate::pac; +use crate::rcc::{RccInfo, RccPeripheral, SealedRccPeripheral}; +use crate::{dma, gpio, interrupt}; + +#[cfg(not(timer_l0))] pub mod complementary_pwm; pub mod input_capture; pub mod low_level; pub mod pwm_input; pub mod qei; +pub mod raw; pub mod simple_pwm; -use crate::interrupt; -use crate::rcc::RccPeripheral; +// === Instance traits and taxonomy === + +trait SealedCoreInstance { + fn info() -> &'static Info; + fn state() -> &'static State; +} + +trait SealedTimMarker {} + +/// Peripheral instance trait for all timers. +/// +/// This trait is implemented by all general-purpose timer peripherals. It is not implemented for +/// the low-power timer (LPTIM) or for the high-resolution timer (HRTIM). It corresponds to +/// [`TimCore`][crate::pac::timer::TimCore] in the PAC. +/// +/// Marker type for this trait is [`CoreTim`] and marker type trait is [`IsCoreTim`]. +#[allow(private_bounds)] +pub trait CoreInstance: SealedCoreInstance + RccPeripheral + 'static { + /// Update interrupt for this timer. + type UpdateInterrupt: interrupt::typelevel::Interrupt; + + /// Registers for this timer. + /// + /// This is a raw pointer to the register block. The actual register block layout varies + /// depending on the timer type. + fn regs() -> *mut (); +} + +/// Marker type for [`CoreInstance`]. +/// +/// Please see the [module documentation](super) for details. +pub enum CoreTim {} + +/// Marker type trait for [`CoreInstance`]. +/// +/// Please see the [module documentation](super) for details. +#[allow(private_bounds)] +pub trait IsCoreTim: SealedTimMarker {} +impl IsCoreTim for CoreTim {} +impl SealedTimMarker for CoreTim {} + +macro_rules! marker_type_and_trait { + ( + $instance_trait:ident, $marker_ty:ident, $marker_trait:ident : + $($marker_super_trait:ident),+ $(,)? ; + $($marker_implied_trait:ident),* $(,)? + ) => { + #[doc = concat!( + "Marker type for [`", stringify!($instance_trait), "`].\n\n", + "Please see the [module documentation](super) for details.", + )] + pub enum $marker_ty {} + + #[doc = concat!( + "Marker type trait for [`", stringify!($instance_trait), "`].\n\n", + "Please see the [module documentation](super) for details.", + )] + #[allow(private_bounds)] + pub trait $marker_trait: $($marker_super_trait +)* SealedTimMarker {} + + impl $marker_trait for $marker_ty {} + $( impl $marker_super_trait for $marker_ty {} )* + $( impl $marker_implied_trait for $marker_ty {} )* + impl SealedTimMarker for $marker_ty {} + }; + ( + $instance_trait:ident, $marker_ty:ident, $marker_trait:ident : + $($marker_super_trait:ident),+ $(,)? + ) => { + marker_type_and_trait! { + $instance_trait, $marker_ty, $marker_trait: + $($marker_super_trait),+ ; + } + }; +} + +/// Peripheral instance trait for basic, 4-channel and advanced timers. +/// +/// This trait is the intersection of [`BasicInstance`], [`General4ChInstance`] and +/// [`Advanced1ChInstance`]. It provides access to the UDE register field to enable the update DMA. +/// +/// Marker type for this trait is [`UpDmaTim`] and marker type trait is [`IsUpDmaTim`]. +pub trait UpDmaInstance: CoreInstance {} +marker_type_and_trait! { UpDmaInstance, UpDmaTim, IsUpDmaTim: IsCoreTim } + +/// Peripheral instance trait for basic and 2-channel timers. +/// +/// This trait is the intersection of [`BasicInstance`] and [`General2ChInstance`]. It provides +/// access to the master mode selection (MMS) register field. +/// +/// Marker type for this trait is [`MmsTim`] and marker type trait is [`IsMmsTim`]. +pub trait MmsInstance: CoreInstance {} +marker_type_and_trait! { MmsInstance, MmsTim, IsMmsTim: IsCoreTim } + +/// Peripheral instance trait for the basic timers. +/// +/// This trait is implemented for basic timer peripherals and corresponds to +/// [`TimBasic`][crate::pac::timer::TimBasic] in the PAC. +/// +/// Marker type for this trait is [`BasicTim`] and marker type trait is [`IsBasicTim`]. +pub trait BasicInstance: UpDmaInstance + MmsInstance {} +marker_type_and_trait! { BasicInstance, BasicTim, IsBasicTim: IsUpDmaTim, IsMmsTim; IsCoreTim } + +/// Peripheral instance trait for general-purpose 1-channel timers. +/// +/// This trait is implemented for all timers with channels and corresponds to +/// [`Tim1ch`][crate::pac::timer::Tim1ch] in the PAC. +pub trait General1ChInstance: CoreInstance { + /// Capture/compare interrupt for this timer. + type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; +} +marker_type_and_trait! { General1ChInstance, General1ChTim, IsGeneral1ChTim: IsCoreTim } + +/// Peripheral instance trait for 4-channel and advanced timers. +/// +/// This trait is the intersection of [`General4ChInstance`] and [`Advanced1ChInstance`]. It +/// provides access to register fields related to capture/compare DMA, timer DMA bursts (DMAR +/// register) and other functionality. +pub trait CcDmaInstance: General1ChInstance + UpDmaInstance {} +marker_type_and_trait! { CcDmaInstance, CcDmaTim, IsCcDmaTim: IsGeneral1ChTim, IsUpDmaTim; IsCoreTim } + +/// Peripheral instance trait for general-purpose 2-channel timers. +/// +/// This trait is implemented for all timers with at least two channels and corresponds to +/// [`Tim2ch`][crate::pac::timer::Tim2ch] in the PAC. +pub trait General2ChInstance: General1ChInstance + MmsInstance { + /// Trigger event interrupt for this timer. + type TriggerInterrupt: interrupt::typelevel::Interrupt; +} +marker_type_and_trait! { + General2ChInstance, General2ChTim, IsGeneral2ChTim: IsGeneral1ChTim, IsMmsTim; + IsCoreTim, +} + +/// Peripheral instance trait for 4-channel and advanced 2-channel timers. +/// +/// This trait is the intersection of [`General4ChInstance`] and [`Advanced2ChInstance`]. It +/// provides access to the TDE register field to enable the trigger DMA. +pub trait TrigDmaInstance: General2ChInstance + CcDmaInstance + BasicInstance {} +marker_type_and_trait! { + TrigDmaInstance, TrigDmaTim, IsTrigDmaTim: + IsGeneral2ChTim, IsCcDmaTim, IsBasicTim; + IsGeneral1ChTim, IsMmsTim, IsUpDmaTim, IsCoreTim, +} + +/// Peripheral instance trait for general-purpose 4-channel timers. +/// +/// This trait is implemented for all timers with four channels and corresponds to +/// [`Tim4ch`][crate::pac::timer::Tim4ch] in the PAC. +pub trait General4ChInstance: General2ChInstance + UpDmaInstance + TrigDmaInstance + BasicInstance {} +marker_type_and_trait! { + General4ChInstance, General4ChTim, IsGeneral4ChTim: + IsGeneral2ChTim, IsUpDmaTim, IsCcDmaTim, IsTrigDmaTim, IsBasicTim; + IsGeneral1ChTim, IsMmsTim, IsCoreTim, +} + +/// Peripheral instance trait for 4-channel timers with 32-bit counter. +/// +/// This trait is implemented for the 32-bit timer peripheral and corresponds to +/// [`Tim32bit`][crate::pac::timer::Tim32bit] in the PAC. +pub trait General32BitInstance: General4ChInstance {} +marker_type_and_trait! { + General32BitInstance, General32BitTim, IsGeneral32BitTim: + IsGeneral4ChTim; + IsGeneral2ChTim, IsGeneral1ChTim, IsUpDmaTim, IsCcDmaTim, IsTrigDmaTim, IsMmsTim, IsBasicTim, IsCoreTim, +} + +/// Peripheral instance trait for advanced 1-channel timers (1-channel timers with complementary +/// output). +/// +/// This trait is implemented for all advanced timer peripherals and corresponds to +/// [`TimAdv1ch`][crate::pac::timer::TimAdv1ch] in the PAC. +pub trait Advanced1ChInstance: General1ChInstance + UpDmaInstance + CcDmaInstance { + /// Communication interrupt for this timer. + type CommunicationInterrupt: interrupt::typelevel::Interrupt; + /// Break input interrupt for this timer. + type BreakInputInterrupt: interrupt::typelevel::Interrupt; +} +marker_type_and_trait! { + Advanced1ChInstance, Advanced1ChTim, IsAdvanced1ChTim: + IsGeneral1ChTim, IsUpDmaTim, IsCcDmaTim; + IsCoreTim, +} + +/// Peripheral instance trait for advanced 2-channel timers (2-channel timers with complementary +/// output). +/// +/// This trait is implemented for all advanced timers with at least two channels and corresponds to +/// [`TimAdv2ch`][crate::pac::timer::TimAdv2ch] in the PAC. +pub trait Advanced2ChInstance: General2ChInstance + Advanced1ChInstance + TrigDmaInstance + BasicInstance {} +marker_type_and_trait! { + Advanced2ChInstance, Advanced2ChTim, IsAdvanced2ChTim: + IsGeneral2ChTim, IsAdvanced1ChTim, IsTrigDmaTim, IsBasicTim; + IsGeneral1ChTim, IsUpDmaTim, IsCcDmaTim, IsMmsTim, IsCoreTim, +} + +/// Peripheral instance trait for advanced 4-channel timers (advanced timers). +/// +/// This trait is implemented for advanced 4-channel timer peripherals and corresponds to +/// [`TimAdv4ch`][crate::pac::timer::TimAdv4ch] in the PAC. +pub trait Advanced4ChInstance: Advanced2ChInstance + General4ChInstance {} +marker_type_and_trait! { + Advanced4ChInstance, Advanced4ChTim, IsAdvanced4ChTim: + IsAdvanced2ChTim, IsGeneral4ChTim; + IsAdvanced1ChTim, IsGeneral2ChTim, IsGeneral1ChTim, + IsTrigDmaTim, IsUpDmaTim, IsCcDmaTim, IsMmsTim, IsBasicTim, IsCoreTim, +} + +// === Pins === + +trait TimerPinMarkerSealed {} + +/// Trait for marker types that represent all possible timer pins. +#[allow(private_bounds)] +pub trait TimerPinMarker: TimerPinMarkerSealed {} + +/// Timer pin trait. +/// +/// If a pin peripheral implements `TimerPin`, it means that it can be used with timer `T` in +/// the role represented by marker type `M`. For example, `TimerPin` is implemented for +/// all pin peripherals that can be used as a pin for channel 2 for timer TIM1. +// developer note: implementations of this trait are in code generated by build.rs +pub trait TimerPin: gpio::Pin { + /// Get the AF number needed to use this pin with timer `T` as pin `M`. + fn af_num(&self) -> u8; +} + +macro_rules! impl_pin_marker { + ($marker_ty:ident) => { + impl TimerPinMarkerSealed for $marker_ty {} + impl TimerPinMarker for $marker_ty {} + }; +} + +/// Marker type for channel 1. +pub enum Ch1 {} +impl_pin_marker!(Ch1); + +/// Marker type for channel 2. +pub enum Ch2 {} +impl_pin_marker!(Ch2); + +/// Marker type for channel 3. +pub enum Ch3 {} +impl_pin_marker!(Ch3); + +/// Marker type for channel 4. +pub enum Ch4 {} +impl_pin_marker!(Ch4); + +/// Marker type for external trigger pin. +pub enum Etr {} +impl_pin_marker!(Etr); + +/// Marker type for channel 1 complementary pin. +pub enum Ch1N {} +impl_pin_marker!(Ch1N); + +/// Marker type for channel 2 complementary pin. +pub enum Ch2N {} +impl_pin_marker!(Ch2N); + +/// Marker type for channel 3 complementary pin. +pub enum Ch3N {} +impl_pin_marker!(Ch3N); + +/// Marker type for channel 4 complementary pin. +pub enum Ch4N {} +impl_pin_marker!(Ch4N); + +/// Marker type for break input pin. +pub enum Bkin {} +impl_pin_marker!(Bkin); + +/// Marker type for break input comparator 1 pin. +pub enum BkinComp1 {} +impl_pin_marker!(BkinComp1); + +/// Marker type for break input comparator 2 pin. +pub enum BkinComp2 {} +impl_pin_marker!(BkinComp2); + +/// Marker type for break 2 input pin. +pub enum Bkin2 {} +impl_pin_marker!(Bkin2); + +/// Marker type for break 2 input comparator 1 pin. +pub enum Bkin2Comp1 {} +impl_pin_marker!(Bkin2Comp1); + +/// Marker type for break 2 input comparator 2 pin. +pub enum Bkin2Comp2 {} +impl_pin_marker!(Bkin2Comp2); + +// === Channels === /// Timer channel. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(u8)] pub enum Channel { /// Channel 1. - Ch1, + Ch1 = 0, /// Channel 2. - Ch2, + Ch2 = 1, /// Channel 3. - Ch3, + Ch3 = 2, /// Channel 4. - Ch4, + Ch4 = 3, } impl Channel { /// Get the channel index (0..3) - pub fn index(&self) -> usize { - match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, - Channel::Ch3 => 2, - Channel::Ch4 => 3, + pub fn index(self) -> usize { + self as u8 as usize + } +} + +/// Trait for marker types that represent the four channel pins ([`Ch1`], [`Ch2`], [`Ch3`], +/// [`Ch4`]). +pub trait ChannelMarker: TimerPinMarker { + /// Representation of the channel number. + const CHANNEL: Channel; +} + +/// Trait for marker types that represent the four complementary channel pins ([`Ch1N`], [`Ch2N`], +/// [`Ch3N`], [`Ch4N`]). +pub trait NChannelMarker: TimerPinMarker { + /// Representation of the channel number. + const N_CHANNEL: Channel; +} + +macro_rules! impl_channel_marker { + ($marker_ty:ident, $n_marker_ty:ident, $channel:expr) => { + impl ChannelMarker for $marker_ty { + const CHANNEL: Channel = $channel; } + + impl NChannelMarker for $n_marker_ty { + const N_CHANNEL: Channel = $channel; + } + }; +} +impl_channel_marker!(Ch1, Ch1N, Channel::Ch1); +impl_channel_marker!(Ch2, Ch2N, Channel::Ch2); +impl_channel_marker!(Ch3, Ch3N, Channel::Ch3); +impl_channel_marker!(Ch4, Ch4N, Channel::Ch4); + +// === DMAs === + +/// Capture/compare DMA trait. +/// +/// If a DMA channel implements `CcDma`, it means that it can be used with the +/// capture/compare DMA request for channel `C` of timer `T`. For example, `CcDma` is implemented for DMA channels that can be used with capture/compare DMA request of +/// channel 1 on timer TIM2. +pub trait CcDma: dma::Channel { + /// Get the DMA request number needed to use this DMA channel as DMA request for channel `C` of + /// timer peripheral `T`. + /// + /// Note: in some chips, ST calls this the "channel", and calls channels "streams". + /// `embassy-stm32` always uses the "channel" and "request number" names. + fn request(&self) -> dma::Request; +} + +dma_trait!(UpDma, CoreInstance); +dma_trait!(TrigDma, CoreInstance); + +// === Runtime info and state === + +/// Number of channels of a timer. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(u8)] +pub enum ChannelCount { + /// Zero channels. + Zero = 0, + /// One channel. + One = 1, + /// Two channels. + Two = 2, + /// Four channels. + Four = 4, +} + +impl ChannelCount { + /// Get the number of channels as an integer. + pub fn to_usize(self) -> usize { + self as u8 as usize } } -/// Amount of bits of a timer. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Bit size of a timer. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TimerBits { /// 16 bits. Bits16, /// 32 bits. - #[cfg(not(stm32l0))] + #[cfg(not(timer_l0))] Bits32, } +/// Type of a timer peripheral. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum TimerType { + /// Basic timer ([`BasicInstance`]). + Basic, + /// General-purpose 1-channel timer ([`General1ChInstance`]). + General1Ch, + /// General-purpose 2-channel timer ([`General2ChInstance`]). + General2Ch, + /// General-purpose 4-channel timer ([`General4ChInstance`]). + General4Ch, + /// General-purpose 32-bit timer ([`General32BitInstance`]). + General32Bit, + /// Advanced 1-channel timer ([`Advanced1ChInstance`]). + Advanced1Ch, + /// Advanced 2-channel timer ([`Advanced2ChInstance`]). + Advanced2Ch, + /// Advanced 4-channel timer ([`Advanced4ChInstance`]). + Advanced4Ch, +} + +impl TimerType { + /// Get the number of channels supported by this timer. + pub const fn channel_count(self) -> ChannelCount { + match self { + Self::Basic => ChannelCount::Zero, + Self::General1Ch | Self::Advanced1Ch => ChannelCount::One, + Self::General2Ch | Self::Advanced2Ch => ChannelCount::Two, + Self::General4Ch | Self::General32Bit | Self::Advanced4Ch => ChannelCount::Four, + } + } + + /// Get the bit width of the counter for this timer. + pub const fn bits(self) -> TimerBits { + match self { + #[cfg(not(timer_l0))] + Self::General32Bit => TimerBits::Bits32, + _ => TimerBits::Bits16, + } + } + + /// Is this timer one of the advanced timers? + pub const fn is_advanced(self) -> bool { + matches!(self, Self::Advanced1Ch | Self::Advanced2Ch | Self::Advanced4Ch) + } +} + struct State { up_waker: AtomicWaker, cc_waker: [AtomicWaker; 4], @@ -66,111 +600,28 @@ impl State { } } -trait SealedInstance: RccPeripheral { - /// Async state for this timer - fn state() -> &'static State; +struct Info { + regs: *mut (), + rcc: RccInfo, + timer_type: TimerType, } -/// Core timer instance. -#[allow(private_bounds)] -pub trait CoreInstance: SealedInstance + 'static { - /// Update Interrupt for this timer. - type UpdateInterrupt: interrupt::typelevel::Interrupt; - - /// Amount of bits this timer has. - const BITS: TimerBits; +unsafe impl Sync for Info {} - /// Registers for this timer. - /// - /// This is a raw pointer to the register block. The actual register block layout varies depending on the timer type. - fn regs() -> *mut (); -} -/// Cut-down basic timer instance. -pub trait BasicNoCr2Instance: CoreInstance {} -/// Basic timer instance. -pub trait BasicInstance: BasicNoCr2Instance {} +// === Peripheral impls === -/// General-purpose 16-bit timer with 1 channel instance. -pub trait GeneralInstance1Channel: CoreInstance { - /// Capture compare interrupt for this timer. - type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; -} - -/// General-purpose 16-bit timer with 2 channels instance. -pub trait GeneralInstance2Channel: GeneralInstance1Channel { - /// Trigger event interrupt for this timer. - type TriggerInterrupt: interrupt::typelevel::Interrupt; -} - -// This trait add *extra* methods to GeneralInstance4Channel, -// that GeneralInstance4Channel doesn't use, but the "AdvancedInstance"s need. -// And it's a private trait, so it's content won't leak to outer namespace. -// -// If you want to add a new method to it, please leave a detail comment to explain it. -trait General4ChBlankSealed { - // SimplePwm<'d, T> is implemented for T: GeneralInstance4Channel - // Advanced timers implement this trait, but the output needs to be - // enabled explicitly. - // To support general-purpose and advanced timers, this function is added - // here defaulting to noop and overwritten for advanced timers. - // - // Enable timer outputs. - fn enable_outputs(&self) {} -} - -/// General-purpose 16-bit timer with 4 channels instance. -#[allow(private_bounds)] -pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel + General4ChBlankSealed {} - -/// General-purpose 32-bit timer with 4 channels instance. -pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {} - -/// Advanced 16-bit timer with 1 channel instance. -pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel { - /// Communication interrupt for this timer. - type CommunicationInterrupt: interrupt::typelevel::Interrupt; - /// Break input interrupt for this timer. - type BreakInputInterrupt: interrupt::typelevel::Interrupt; -} -/// Advanced 16-bit timer with 2 channels instance. - -pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + AdvancedInstance1Channel {} - -/// 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!(ExternalTriggerPin, GeneralInstance4Channel); - -pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel); -pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel); -pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel); -pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel); - -pin_trait!(BreakInputPin, AdvancedInstance4Channel); -pin_trait!(BreakInput2Pin, AdvancedInstance4Channel); - -pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel); -pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel); - -pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel); -pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel); - -// 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); +macro_rules! impl_core { + ($inst:ident, $timer_type:expr) => { + impl SealedCoreInstance for crate::peripherals::$inst { + fn info() -> &'static Info { + static INFO: Info = Info { + regs: crate::pac::$inst.as_ptr(), + rcc: crate::peripherals::$inst::RCC_INFO, + timer_type: $timer_type, + }; + &INFO + } -#[allow(unused)] -macro_rules! impl_core_timer { - ($inst:ident, $bits:expr) => { - impl SealedInstance for crate::peripherals::$inst { fn state() -> &'static State { static STATE: State = State::new(); &STATE @@ -180,8 +631,6 @@ macro_rules! impl_core_timer { impl CoreInstance for crate::peripherals::$inst { type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; - const BITS: TimerBits = $bits; - fn regs() -> *mut () { crate::pac::$inst.as_ptr() } @@ -189,137 +638,116 @@ macro_rules! impl_core_timer { }; } -#[allow(unused)] macro_rules! impl_general_1ch { ($inst:ident) => { - impl GeneralInstance1Channel for crate::peripherals::$inst { + impl General1ChInstance for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } }; } -#[allow(unused)] macro_rules! impl_general_2ch { ($inst:ident) => { - impl GeneralInstance2Channel for crate::peripherals::$inst { + impl General2ChInstance for crate::peripherals::$inst { type TriggerInterrupt = crate::_generated::peripheral_interrupts::$inst::TRG; } }; } -#[allow(unused)] +#[allow(unused_macros)] macro_rules! impl_advanced_1ch { ($inst:ident) => { - impl AdvancedInstance1Channel for crate::peripherals::$inst { + impl Advanced1ChInstance for crate::peripherals::$inst { type CommunicationInterrupt = crate::_generated::peripheral_interrupts::$inst::COM; type BreakInputInterrupt = crate::_generated::peripheral_interrupts::$inst::BRK; } }; } -// This macro only apply to "AdvancedInstance(s)", -// not "GeneralInstance4Channel" itself. -#[allow(unused)] -macro_rules! impl_general_4ch_blank_sealed { - ($inst:ident) => { - impl General4ChBlankSealed for crate::peripherals::$inst { - fn enable_outputs(&self) { - unsafe { crate::pac::timer::Tim1chCmp::from_ptr(Self::regs()) } - .bdtr() - .modify(|w| w.set_moe(true)); - } - } - }; -} - foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} + impl_core!($inst, TimerType::Basic); + impl UpDmaInstance for crate::peripherals::$inst {} + impl MmsInstance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + impl_core!($inst, TimerType::General1Ch); impl_general_1ch!($inst); - impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl General4ChBlankSealed for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + impl_core!($inst, TimerType::General2Ch); impl_general_1ch!($inst); impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl General4ChBlankSealed for crate::peripherals::$inst {} + impl MmsInstance for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + ($inst:ident, timer, TIM_4CH, UP, $irq:ident) => { + impl_core!($inst, TimerType::General4Ch); impl_general_1ch!($inst); impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl General4ChBlankSealed for crate::peripherals::$inst {} + impl UpDmaInstance for crate::peripherals::$inst {} + impl MmsInstance for crate::peripherals::$inst {} + impl TrigDmaInstance for crate::peripherals::$inst {} + impl CcDmaInstance for crate::peripherals::$inst {} + impl BasicInstance for crate::peripherals::$inst {} + impl General4ChInstance for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits32); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + ($inst:ident, timer, TIM_32BIT, UP, $irq:ident) => { + impl_core!($inst, TimerType::General32Bit); impl_general_1ch!($inst); impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl GeneralInstance32bit4Channel for crate::peripherals::$inst {} - impl General4ChBlankSealed for crate::peripherals::$inst {} + impl UpDmaInstance for crate::peripherals::$inst {} + impl MmsInstance for crate::peripherals::$inst {} + impl TrigDmaInstance for crate::peripherals::$inst {} + impl CcDmaInstance for crate::peripherals::$inst {} + impl BasicInstance for crate::peripherals::$inst {} + impl General4ChInstance for crate::peripherals::$inst {} + impl General32BitInstance for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + ($inst:ident, timer, TIM_ADV1CH, UP, $irq:ident) => { + impl_core!($inst, TimerType::Advanced1Ch); impl_general_1ch!($inst); - impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl_general_4ch_blank_sealed!($inst); impl_advanced_1ch!($inst); - impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl AdvancedInstance4Channel for crate::peripherals::$inst {} + impl UpDmaInstance for crate::peripherals::$inst {} + impl CcDmaInstance for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + ($inst:ident, timer, TIM_ADV2CH, UP, $irq:ident) => { + impl_core!($inst, TimerType::Advanced2Ch); impl_general_1ch!($inst); impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl_general_4ch_blank_sealed!($inst); impl_advanced_1ch!($inst); - impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl AdvancedInstance4Channel for crate::peripherals::$inst {} + impl UpDmaInstance for crate::peripherals::$inst {} + impl MmsInstance for crate::peripherals::$inst {} + impl CcDmaInstance for crate::peripherals::$inst {} + impl TrigDmaInstance for crate::peripherals::$inst {} + impl BasicInstance for crate::peripherals::$inst {} + impl Advanced2ChInstance for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl_core_timer!($inst, TimerBits::Bits16); - impl BasicNoCr2Instance for crate::peripherals::$inst {} - impl BasicInstance for crate::peripherals::$inst {} + ($inst:ident, timer, TIM_ADV4CH, UP, $irq:ident) => { + impl_core!($inst, TimerType::Advanced4Ch); impl_general_1ch!($inst); impl_general_2ch!($inst); - impl GeneralInstance4Channel for crate::peripherals::$inst {} - impl_general_4ch_blank_sealed!($inst); impl_advanced_1ch!($inst); - impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl AdvancedInstance4Channel for crate::peripherals::$inst {} + impl UpDmaInstance for crate::peripherals::$inst {} + impl MmsInstance for crate::peripherals::$inst {} + impl CcDmaInstance for crate::peripherals::$inst {} + impl TrigDmaInstance for crate::peripherals::$inst {} + impl BasicInstance for crate::peripherals::$inst {} + impl Advanced2ChInstance for crate::peripherals::$inst {} + impl General4ChInstance for crate::peripherals::$inst {} + impl Advanced4ChInstance for crate::peripherals::$inst {} }; } +// === Interrupt handlers === + /// Update interrupt handler. pub struct UpdateInterruptHandler { _phantom: PhantomData, @@ -349,18 +777,18 @@ impl interrupt::typelevel::Handler for Upda } /// Capture/Compare interrupt handler. -pub struct CaptureCompareInterruptHandler { +pub struct CaptureCompareInterruptHandler { _phantom: PhantomData, } -impl interrupt::typelevel::Handler +impl interrupt::typelevel::Handler for CaptureCompareInterruptHandler { unsafe fn on_interrupt() { #[cfg(feature = "low-power")] crate::low_power::on_wakeup_irq(); - let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); + let regs = crate::pac::timer::Tim4ch::from_ptr(T::regs()); // Read TIM interrupt flags. let sr = regs.sr().read(); diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index e3eb6042a9..24d0a503d8 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -1,54 +1,55 @@ //! PWM Input driver. -use embassy_hal_internal::into_ref; - -use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; -use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; +use super::low_level::{InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; +use super::raw::{RawTimer, RawTimerPin}; +use super::{Ch1, Ch2, Channel, General2ChInstance, General2ChTim, TimerPin}; use crate::gpio::{AfType, Pull}; use crate::time::Hertz; use crate::Peripheral; /// PWM Input driver. -pub struct PwmInput<'d, T: GeneralInstance4Channel> { +pub struct PwmInput<'d> { + inner: Timer<'d, General2ChTim>, + _pin: RawTimerPin<'d>, channel: Channel, - inner: Timer<'d, T>, } -impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { +impl<'d> PwmInput<'d> { /// Create a new PWM input driver. - pub fn new( + pub fn new( tim: impl Peripheral

+ 'd, - pin: impl Peripheral

> + 'd, + pin: impl Peripheral

> + 'd, pull: Pull, freq: Hertz, ) -> Self { - into_ref!(pin); - - pin.set_as_af(pin.af_num(), AfType::input(pull)); - - Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) + let raw = RawTimer::new_general_2ch(tim); + let pin = RawTimerPin::new(pin, AfType::input(pull)); + Self::new_inner(raw, pin, freq, Channel::Ch1, Channel::Ch2) } /// Create a new PWM input driver. - pub fn new_alt( + pub fn new_alt( tim: impl Peripheral

+ 'd, - pin: impl Peripheral

> + 'd, + pin: impl Peripheral

> + 'd, pull: Pull, freq: Hertz, ) -> Self { - into_ref!(pin); - - pin.set_as_af(pin.af_num(), AfType::input(pull)); - - Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) + let raw = RawTimer::new_general_2ch(tim); + let pin = RawTimerPin::new(pin, AfType::input(pull)); + Self::new_inner(raw, pin, freq, Channel::Ch2, Channel::Ch1) } - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { - let mut inner = Timer::new(tim); + fn new_inner( + raw: RawTimer<'d, General2ChTim>, + pin: RawTimerPin<'d>, + freq: Hertz, + ch1: Channel, + ch2: Channel, + ) -> Self { + let inner = Timer::new(raw); - inner.set_counting_mode(CountingMode::EdgeAlignedUp); - inner.set_tick_freq(freq); - inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + inner.set_tick_frequency(freq); + inner.enable_outputs(); // Required for advanced timers, see [`RawTimer::enable_outputs()`] for details inner.start(); // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 @@ -69,7 +70,11 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { // Must call the `enable` function after - Self { channel: ch1, inner } + Self { + inner, + _pin: pin, + channel: ch1, + } } /// Enable the given channel. @@ -86,17 +91,17 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Check whether given channel is enabled pub fn is_enabled(&self) -> bool { - self.inner.get_channel_enable_state(Channel::Ch1) + self.inner.channel_enable_state(Channel::Ch1) } /// Get the period tick count - pub fn get_period_ticks(&self) -> u32 { - self.inner.get_capture_value(self.channel) + pub fn period_ticks(&self) -> u32 { + self.inner.capture_value(self.channel) } /// Get the pulse width tick count - pub fn get_width_ticks(&self) -> u32 { - self.inner.get_capture_value(match self.channel { + pub fn width_ticks(&self) -> u32 { + self.inner.capture_value(match self.channel { Channel::Ch1 => Channel::Ch2, Channel::Ch2 => Channel::Ch1, _ => panic!("Invalid channel for PWM input"), @@ -104,11 +109,11 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { } /// Get the duty cycle in 100% - pub fn get_duty_cycle(&self) -> f32 { - let period = self.get_period_ticks(); + pub fn duty_cycle(&self) -> f32 { + let period = self.period_ticks(); if period == 0 { return 0.; } - 100. * (self.get_width_ticks() as f32) / (period as f32) + 100. * (self.width_ticks() as f32) / (period as f32) } } diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index fc5835414c..3691fb5bff 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -1,13 +1,11 @@ //! Quadrature decoder using a timer. -use core::marker::PhantomData; - -use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals; use super::low_level::Timer; -use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; -use crate::gpio::{AfType, AnyPin, Pull}; +use super::raw::{RawTimer, RawTimerPin}; +use super::{Ch1, Ch2, General4ChInstance, General4ChTim, TimerPin}; +use crate::gpio::{AfType, Pull}; use crate::Peripheral; /// Counting direction @@ -18,62 +16,36 @@ pub enum Direction { Downcounting, } -/// Channel 1 marker type. -pub enum Ch1 {} -/// Channel 2 marker type. -pub enum Ch2 {} - -/// Wrapper for using a pin with QEI. -pub struct QeiPin<'d, T, Channel> { - _pin: PeripheralRef<'d, AnyPin>, - 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: impl Peripheral

> + 'd) -> Self { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); - }); - QeiPin { - _pin: pin.map_into(), - phantom: PhantomData, - } - } - } - }; -} - -channel_impl!(new_ch1, Ch1, Channel1Pin); -channel_impl!(new_ch2, Ch2, Channel2Pin); - /// Quadrature decoder driver. -pub struct Qei<'d, T: GeneralInstance4Channel> { - inner: Timer<'d, T>, +pub struct Qei<'d> { + inner: Timer<'d, General4ChTim>, + _pins: [RawTimerPin<'d>; 2], } -impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { +impl<'d> Qei<'d> { /// Create a new quadrature decoder driver. - pub fn new(tim: impl Peripheral

+ 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { - Self::new_inner(tim) + pub fn new( + tim: impl Peripheral

+ 'd, + ch1_pin: impl Peripheral

> + 'd, + ch2_pin: impl Peripheral

> + 'd, + ) -> Self { + let raw = RawTimer::new_general_4ch(tim); + let ch1_pin = RawTimerPin::new(ch1_pin, AfType::input(Pull::None)); + let ch2_pin = RawTimerPin::new(ch2_pin, AfType::input(Pull::None)); + Self::new_inner(raw, [ch1_pin, ch2_pin]) } - fn new_inner(tim: impl Peripheral

+ 'd) -> Self { - let inner = Timer::new(tim); - let r = inner.regs_gp16(); + fn new_inner(raw: RawTimer<'d, General4ChTim>, pins: [RawTimerPin<'d>; 2]) -> Self { + let inner = Timer::new(raw); // Configure TxC1 and TxC2 as captures - r.ccmr_input(0).modify(|w| { + inner.raw.ccmr_input_1ch(0).modify(|w| { w.set_ccs(0, vals::CcmrInputCcs::TI4); w.set_ccs(1, vals::CcmrInputCcs::TI4); }); // enable and configure to capture on rising edge - r.ccer().modify(|w| { + inner.raw.ccer_1ch().modify(|w| { w.set_cce(0, true); w.set_cce(1, true); @@ -81,19 +53,19 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { w.set_ccp(1, false); }); - r.smcr().modify(|w| { + inner.raw.smcr_4ch().modify(|w| { w.set_sms(vals::Sms::ENCODER_MODE_3); }); - r.arr().modify(|w| w.set_arr(u16::MAX)); - r.cr1().modify(|w| w.set_cen(true)); + inner.raw.arr().modify(|w| w.set_arr(u16::MAX)); + inner.raw.cr1_core().modify(|w| w.set_cen(true)); - Self { inner } + Self { inner, _pins: pins } } /// Get direction. - pub fn read_direction(&self) -> Direction { - match self.inner.regs_gp16().cr1().read().dir() { + pub fn direction(&self) -> Direction { + match self.inner.raw.cr1_4ch().read().dir() { vals::Dir::DOWN => Direction::Downcounting, vals::Dir::UP => Direction::Upcounting, } @@ -101,6 +73,6 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { /// Get count. pub fn count(&self) -> u16 { - self.inner.regs_gp16().cnt().read().cnt() + self.inner.raw.cnt().read().cnt() } } diff --git a/embassy-stm32/src/timer/raw.rs b/embassy-stm32/src/timer/raw.rs new file mode 100644 index 0000000000..7ae4eeb4df --- /dev/null +++ b/embassy-stm32/src/timer/raw.rs @@ -0,0 +1,1049 @@ +//! Raw register-level timer driver. +//! +//! This module provides the core functionality for timer drivers. It provides type-safe access to +//! the timer registers, ensuring that only the registers which are available for the given timer +//! can be used. + +use core::marker::PhantomData; + +use embassy_hal_internal::PeripheralRef; + +use super::{ + Advanced1ChInstance, Advanced1ChTim, Advanced2ChInstance, Advanced2ChTim, Advanced4ChInstance, Advanced4ChTim, + BasicInstance, BasicTim, CcDma, CcDmaInstance, CcDmaTim, ChannelCount, ChannelMarker, CoreInstance, CoreTim, + General1ChInstance, General1ChTim, General2ChInstance, General2ChTim, General32BitInstance, General32BitTim, + General4ChInstance, General4ChTim, Info, IsCcDmaTim, IsCoreTim, IsGeneral1ChTim, IsGeneral2ChTim, IsGeneral4ChTim, + IsMmsTim, IsTrigDmaTim, IsUpDmaTim, MmsInstance, MmsTim, TimerPin, TimerPinMarker, TimerType, TrigDma, + TrigDmaInstance, TrigDmaTim, UpDma, UpDmaInstance, UpDmaTim, +}; +#[cfg(not(timer_l0))] +use super::{IsAdvanced1ChTim, IsAdvanced2ChTim, IsAdvanced4ChTim, IsGeneral32BitTim}; +use crate::gpio::{self, SealedPin as _}; +use crate::pac::common::{Reg, RW, W}; +use crate::pac::timer::regs; +use crate::time::Hertz; +use crate::{dma, into_ref, pac, rcc, Peripheral}; + +/// Get DMA request and channel that can be used as the update DMA for timer `T`. +pub fn up_dma<'d, T>(dma: impl Peripheral

> + 'd) -> dma::ChannelAndRequest<'d> +where + T: CoreInstance, +{ + into_ref!(dma); + let request = dma.request(); + dma::ChannelAndRequest { + channel: dma.map_into(), + request, + } +} + +/// Get DMA request and channel that can be used as the trigger DMA for timer `T`. +pub fn trig_dma<'d, T>(dma: impl Peripheral

> + 'd) -> dma::ChannelAndRequest<'d> +where + T: CoreInstance, +{ + into_ref!(dma); + let request = dma.request(); + dma::ChannelAndRequest { + channel: dma.map_into(), + request, + } +} + +/// Get DMA request and channel that can be used as the capture/compare DMA for channel `C` of timer `T`. +pub fn cc_dma<'d, T, C>(dma: impl Peripheral

> + 'd) -> dma::ChannelAndRequest<'d> +where + T: CoreInstance, + C: ChannelMarker, +{ + into_ref!(dma); + let request = dma.request(); + dma::ChannelAndRequest { + channel: dma.map_into(), + request, + } +} + +/// Raw timer pin. +/// +/// The only purpose of this struct is to correctly initialize the pin in the constructor and +/// deinitialize it (set it as disconnected) in the destructor. It can be used to implement pin +/// functionality in higher-level drivers. +pub struct RawTimerPin<'d> { + pin: PeripheralRef<'d, gpio::AnyPin>, +} + +impl<'d> RawTimerPin<'d> { + /// Initializes `pin` as a timer pin `M` for timer instance `T`. + pub fn new( + pin: impl Peripheral

> + 'd, + af_type: gpio::AfType, + ) -> Self { + into_ref!(pin); + let af_num = pin.af_num(); + let pin = pin.map_into(); + pin.set_as_af(af_num, af_type); + Self { pin } + } +} + +impl<'d> Drop for RawTimerPin<'d> { + fn drop(&mut self) { + self.pin.set_as_disconnected(); + } +} + +/// Raw timer driver. +/// +/// This driver provides direct access to the timer registers. Only those registers and register +/// fields which are implemented by timers of type `Tim` are accessible. +/// +/// Some registers or register fields (such as CCR) have multiple copies depending on the number of +/// channels provided by the timer peripheral. This driver allows you to access these registers or +/// fields for all channels, even if the `Tim` marker type does not guarantee that the channel is +/// available: for example, you can use [`ccr()`][Self::ccr()] with all four channels, even though +/// this method is available even for 1-channel timers. It is your responsibility to ensure that +/// you use only the channels that are available in the timer peripheral. +/// +/// The following table lists the registers and fields with the timer instance that implements them +/// and a method that you can use to access the register. +/// +/// | Register | Fields | Instance | Method | +/// | -------- | ------ | -------- | ------ | +// CR1 +#[cfg_attr( + timer_v1, + doc = "| CR1 | CEN, UDIS, URS, OPM, ARPE, UIFREMAP | [`CoreInstance`] | [`RawTimer::cr1_core()`] " +)] +#[cfg_attr( + timer_v2, + doc = "| CR1 | CEN, UDIS, URS, OPM, ARPE, UIFREMAP, DITHEN | [`CoreInstance`] | [`RawTimer::cr1_core()`] " +)] +#[cfg_attr( + timer_l0, + doc = "| CR1 | CEN, UDIS, URS, OPM, ARPE | [`CoreInstance`] | [`RawTimer::cr1_core()`] " +)] +#[doc = "| | CKD | [`General1ChInstance`] | [`RawTimer::cr1_1ch()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | DIR, CMS | [`General4ChInstance`] | [`RawTimer::cr1_4ch()`] " +)] +#[cfg_attr(timer_l0, doc = "| | DIR, CMS | [`General2ChInstance`] | [`RawTimer::cr1_2ch()`] ")] +// CR2 +#[doc = "| CR2 | MMS | [`MmsInstance`] | [`RawTimer::cr2_mms()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | TI1S | [`General2ChInstance`] | [`RawTimer::cr2_2ch()`], [`RawTimer::cr2_trigdma()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | CCDS | [`CcDmaInstance`] | [`RawTimer::cr2_ccdma()`], [`RawTimer::cr2_trigdma()`] " +)] +#[cfg_attr(timer_l0, doc = "| | TI1S | [`TrigDmaInstance`] | [`RawTimer::cr2_trigdma()`] ")] +#[cfg_attr( + not(timer_l0), + doc = "| | CCPC, CCUS, OIS, OISN | [`Advanced1ChInstance`] | [`RawTimer::cr2_adv1ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | MMS2 | [`Advanced4ChInstance`] | [`RawTimer::cr2_adv4ch()`] " +)] +// SMCR +#[cfg_attr( + timer_v1, + doc = "| SMCR | SMS, TS, MSM | [`General2ChInstance`] | [`RawTimer::smcr_2ch()`] " +)] +#[cfg_attr( + timer_v1, + doc = "| | ETF, ETPS, ECE, ETP | [`General4ChInstance`] | [`RawTimer::smcr_4ch()`] " +)] +#[cfg_attr( + timer_v2, + doc = "| SMCR | SMS, TS, MSM | [`General2ChInstance`] | [`RawTimer::smcr_2ch()`] " +)] +#[cfg_attr( + timer_v2, + doc = "| | ETF, ETPS, ECE, ETP, SMSPS | [`General4ChInstance`] | [`RawTimer::smcr_4ch()`] " +)] +#[cfg_attr(timer_v2, doc = "| | SMSPE | [`TrigDmaInstance`] | [`RawTimer::smcr_trigdma()`] ")] +#[cfg_attr(timer_v2, doc = "| | OCCS | [`Advanced4ChInstance`] | [`RawTimer::smcr_adv4ch()`] ")] +#[cfg_attr( + timer_l0, + doc = "| SMCR | SMS, TS, MSM, ETF, ETPS, ECE, ETP | [`General2ChInstance`] | [`RawTimer::smcr_2ch()`] " +)] +// DIER +#[doc = "| DIER | UIE | [`CoreInstance`] | [`RawTimer::dier_core()`] "] +#[doc = "| | UDE | [`UpDmaInstance`] | [`RawTimer::dier_updma()`] "] +#[doc = "| | CCIE | [`General1ChInstance`] | [`RawTimer::dier_1ch()`] "] +#[doc = "| | TIE | [`General2ChInstance`] | [`RawTimer::dier_2ch()`] "] +#[doc = "| | CCDE | [`CcDmaInstance`] | [`RawTimer::dier_ccdma()`] "] +#[doc = "| | TDE | [`TrigDmaInstance`] | [`RawTimer::dier_trigdma()`] "] +#[cfg_attr( + timer_v2, + doc = "| | IDXIE, DIRIE, IERRIE, TERRIE | [`General4ChInstance`] | [`RawTimer::dier_4ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | COMIE, BIE | [`Advanced1ChInstance`] | [`RawTimer::dier_adv1ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | COMDE | [`Advanced2ChInstance`] | [`RawTimer::dier_adv2ch()`], [`RawTimer::dier_adv4ch()`] " +)] +// SR +#[doc = "| SR | UIF | [`CoreInstance`] | [`RawTimer::sr_core()`] "] +#[doc = "| | CCIF, CCOF | [`General1ChInstance`] | [`RawTimer::sr_1ch()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | TIF | [`General2ChInstance`] | [`RawTimer::sr_2ch()`], [`RawTimer::sr_adv2ch()`] " +)] +#[cfg_attr( + timer_v2, + doc = "| | IDXIF, DIRIF, IERRIF, TERRIF | [`General4ChInstance`] | [`RawTimer::sr_4ch()`], [`RawTimer::sr_adv4ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | COMIF, BIF | [`Advanced1ChInstance`] | [`RawTimer::sr_adv1ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | SBIF, CCIF5, CCIF6 | [`Advanced4ChInstance`] | [`RawTimer::sr_adv4ch()`] " +)] +// EGR +#[doc = "| EGR | UG | [`CoreInstance`] | [`RawTimer::egr_core()`] "] +#[doc = "| | CCG | [`General1ChInstance`] | [`RawTimer::egr_1ch()`] "] +#[doc = "| | TG | [`General2ChInstance`] | [`RawTimer::egr_2ch()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | COMG, BG | [`Advanced1ChInstance`] | [`RawTimer::egr_adv1ch()`], [`RawTimer::egr_adv2ch()`] " +)] +// CCMR +#[doc = "| CCMR (input) | CCS, ICPSC, ICF | [`General1ChInstance`] | [`RawTimer::ccmr_input_1ch()`] "] +#[doc = "| CCMR (output) | CCS, OCFE, OCPE, OCM | [`General1ChInstance`] | [`RawTimer::ccmr_output_1ch()`] "] +#[doc = "| | OCCE | [`General4ChInstance`] | [`RawTimer::ccmr_output_4ch()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| CCMR3 (output) | OCFE, OCPE, OCM, OCCE | [`Advanced4ChInstance`] | [`RawTimer::ccmr3_output_adv4ch()`] " +)] +// CCER +#[doc = "| CCER | CCE, CCP, CCNP | [`General1ChInstance`] | [`RawTimer::ccer_1ch()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | CCNE | [`Advanced1ChInstance`] | [`RawTimer::ccer_adv1ch()`] " +)] +// CNT +#[doc = "| CNT | 16-bit | [`CoreInstance`] | [`RawTimer::cnt()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | 32-bit | [`General32BitInstance`] | [`RawTimer::cnt_32bit()`] " +)] +// PSC +#[doc = "| PSC | | [`CoreInstance`] | [`RawTimer::arr()`] "] +// ARR +#[doc = "| ARR | 16-bit | [`CoreInstance`] | [`RawTimer::arr()`] "] +#[cfg_attr( + not(timer_l0), + doc = "| | 32-bit | [`General32BitInstance`] | [`RawTimer::arr_32bit()`] " +)] +#[cfg_attr( + timer_v2, + doc = "| ARR (dither mode) | 16-bit | [`CoreInstance`] | [`RawTimer::arr_dither()`] " +)] +// RCR +#[cfg_attr( + not(timer_l0), + doc = "| RCR | REP (8-bit) | [`Advanced1ChInstance`] | [`RawTimer::rcr_adv1ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | REP (16-bit) | [`Advanced4ChInstance`] | [`RawTimer::rcr_adv4ch()`] " +)] +// CCR +#[cfg_attr(not(timer_l0), doc = "| CCR | | [`General1ChInstance`] | [`RawTimer::ccr()`] ")] +#[cfg_attr( + timer_v2, + doc = "| CCR (dither mode) | | [`General1ChInstance`] | [`RawTimer::ccr_dither()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| CCR (32-bit) | | [`General32BitInstance`] | [`RawTimer::ccr_32bit()`] " +)] +#[cfg_attr(not(timer_l0), doc = "| CCR5 | | [`Advanced4ChInstance`] | [`RawTimer::ccr5()`] ")] +#[cfg_attr(not(timer_l0), doc = "| | GC5C | [`Advanced4ChInstance`] | [`RawTimer::ccr5()`] ")] +#[cfg_attr( + timer_v2, + doc = "| CCR5 (dither mode) | | [`Advanced4ChInstance`] | [`RawTimer::ccr5_dither()`] " +)] +#[cfg_attr(not(timer_l0), doc = "| CCR6 | | [`Advanced4ChInstance`] | [`RawTimer::ccr6()`] ")] +#[cfg_attr( + timer_v2, + doc = "| CCR6 (dither mode) | | [`Advanced4ChInstance`] | [`RawTimer::ccr6_dither()`] " +)] +// BDTR +#[cfg_attr( + timer_v1, + doc = "| BDTR | DTG, LOCK, OSSI, OSSR, BKE, BKP, ADE, MOE, BKF | [`Advanced1ChInstance`] | [`RawTimer::bdtr()`] " +)] +#[cfg_attr( + timer_v2, + doc = "| BDTR | DTG, LOCK, OSSI, OSSR, BKE, BKP, ADE, MOE, BKF, BKDSRM, BKBID | [`Advanced1ChInstance`] | [`RawTimer::bdtr()`] " +)] +// DCR +#[cfg_attr(not(timer_v2), doc = "| DCR | DBA, DBL | [`CcDmaInstance`] | [`RawTimer::dcr()`] ")] +#[cfg_attr(timer_v2, doc = "| DCR | DBA, DBL, DBSS | [`CcDmaInstance`] | [`RawTimer::dcr()`] ")] +// DMAR +#[cfg_attr( + any(timer_v1, timer_l0), + doc = "| DMAR | (DMAB) | [`CcDmaInstance`] | [`RawTimer::dmar()`] " +)] +// DTR2 +#[cfg_attr( + timer_v2, + doc = "| DTR2 | DTGF, DTAE, DTPE | [`Advanced1ChInstance`] | [`RawTimer::dtr2()`] " +)] +// ECR +#[cfg_attr( + timer_v2, + doc = "| ECR | IE, IDIR, IBLK, FIDX, IPOS, PW, PWPRSC | [`General4ChInstance`] | [`RawTimer::ecr()`] " +)] +// AF1 +#[cfg_attr( + not(timer_l0), + doc = "| AF1 | ETRSEL | [`General4ChInstance`] | [`RawTimer::af1_4ch()`], [`RawTimer::af1_adv4ch()`] " +)] +#[cfg_attr( + not(timer_l0), + doc = "| | BKINE, BKCMPE, BKDF1BKE, BKINP, BKCMPP | [`Advanced1ChInstance`] | [`RawTimer::af1_adv1ch()`] " +)] +// AF2 +#[cfg_attr( + timer_v1, + doc = "| AF2 | BK2INE, BK2CMPE, BK2DF1BK1E, BK2INP, BK2CMPP | [`Advanced4ChInstance`] | [`RawTimer::af2_adv4ch()`] " +)] +#[cfg_attr(timer_v2, doc = "| AF2 | OCRSEL | [`CcDmaInstance`] | [`RawTimer::af2_ccdma()`] ")] +#[cfg_attr( + timer_v2, + doc = "| | BK2INE, BK2CMPE, BK2INP, BK2CMPP | [`Advanced4ChInstance`] | [`RawTimer::af2_adv4ch()`] " +)] +// TISEL +#[cfg_attr(not(timer_l0), doc = "| TISEL | | [`General1ChInstance`] | [`RawTimer::tisel()`] ")] +pub struct RawTimer<'d, Tim> { + info: &'d Info, + kernel_clock: Hertz, + _phantom: PhantomData, +} + +#[rustfmt::skip] +macro_rules! impl_new { + ($marker_ty:ident, $timer_trait:ident, $new:ident) => { + impl<'d> RawTimer<'d, $marker_ty> { + #[doc = concat!( + "Initializes the raw driver from timer `T`, treating it as [`", + stringify!($timer_trait), + "`].", + )] + pub fn $new(_tim: impl Peripheral

+ 'd) -> Self { + rcc::enable_and_reset::(); + Self { + info: T::info(), + kernel_clock: T::frequency(), + _phantom: PhantomData, + } + } + } + }; +} + +impl_new!(CoreTim, CoreInstance, new_core); +impl_new!(UpDmaTim, UpDmaInstance, new_up_dma); +impl_new!(MmsTim, MmsInstance, new_mms); +impl_new!(BasicTim, BasicInstance, new_basic); +impl_new!(General1ChTim, General1ChInstance, new_general_1ch); +impl_new!(CcDmaTim, CcDmaInstance, new_cc_dma); +impl_new!(General2ChTim, General2ChInstance, new_general_2ch); +impl_new!(TrigDmaTim, TrigDmaInstance, new_trig_dma); +impl_new!(General4ChTim, General4ChInstance, new_general_4ch); +impl_new!(General32BitTim, General32BitInstance, new_general_32bit); +impl_new!(Advanced1ChTim, Advanced1ChInstance, new_advanced_1ch); +impl_new!(Advanced2ChTim, Advanced2ChInstance, new_advanced_2ch); +impl_new!(Advanced4ChTim, Advanced4ChInstance, new_advanced_4ch); + +impl<'d, Tim: IsCoreTim> RawTimer<'d, Tim> { + /// Get a pointer to the register block for this timer. + /// + /// This is a raw pointer to the register block. The actual register block layout varies + /// depending on the timer type. + pub fn regs(&self) -> *mut () { + self.info.regs + } + + /// Get the kernel clock frequency for this timer. + /// + /// Unless you switch the timer to a different clock source, this is the frequency that is fed + /// into the prescaler to drive the timer. + pub fn clock_frequency(&self) -> Hertz { + self.kernel_clock + } + + /// Get the type of the timer. + /// + /// Note that this returns the actual type of the timer peripheral, regardless of the `Tim` + /// marker. + pub fn timer_type(&self) -> TimerType { + self.info.timer_type + } + + /// Get the number of channels in this timer. + /// + /// Note that this returns the actual number of channels supported by the timer peripheral, + /// regardless of the `Tim` marker. + pub fn channel_count(&self) -> ChannelCount { + self.info.timer_type.channel_count() + } + + /// Get 32-bit registers, if the timer is a [`General32BitInstance`]. + /// + /// This can be used to optionally use 32-bit counter resolution even if you don't know at + /// runtime whether you are working with a 32-bit timer or not (i.e., if `Tim: + /// IsGeneral32BitTim` does hold). + #[cfg(not(timer_l0))] + pub fn try_get_32bit_regs(&self) -> Option { + if matches!(self.info.timer_type, TimerType::General32Bit) { + Some(unsafe { pac::timer::Tim32bit::from_ptr(self.info.regs) }) + } else { + None + } + } + + /// Ensure that outputs are enabled if this is an advanced timer. + /// + /// For advanced timers, it is necessary to set bit MOE in register BDTR to enable timer + /// outputs. This method sets MOE if this is an advanced timer, and does nothing otherwise. + /// You should use this method when writing generic drivers, to make sure that they work for + /// both general-purpose and advanced timers. + pub fn enable_outputs(&self) { + #[cfg(not(timer_l0))] + if self.info.timer_type.is_advanced() { + let regs = unsafe { pac::timer::TimAdv1ch::from_ptr(self.info.regs) }; + regs.bdtr().modify(|w| w.set_moe(true)); + } + } +} + +impl<'d, Tim> Drop for RawTimer<'d, Tim> { + fn drop(&mut self) { + self.info.rcc.disable(); + } +} + +macro_rules! reg { + ($self:ident, $block:ident, $reg:ident) => { + reg!($self, $block, $reg()) + }; + ($self:ident, $block:ident, $reg:ident $args:tt) => { + unsafe { pac::timer::$block::from_ptr($self.info.regs) }.$reg $args + } +} + +macro_rules! reg_cast { + ($($reg:ident),* as $reg_ty:ident, $access:ident) => { + { + let reg = reg!($($reg),*); + unsafe { Reg::::from_ptr(reg.as_ptr() as *mut _) } + } + } +} + +impl<'d, Tim> RawTimer<'d, Tim> { + /// Control register 1. + pub fn cr1_core(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, cr1) + } + + /// Control register 1. + pub fn cr1_1ch(&self) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, cr1) + } + + /// Control register 1. + pub fn cr1_2ch(&self) -> Reg + where + Tim: IsGeneral2ChTim, + { + reg!(self, Tim2ch, cr1) + } + + /// Control register 1. + pub fn cr1_4ch(&self) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, cr1) + } + + /// Control register 2. + pub fn cr2_mms(&self) -> Reg + where + Tim: IsMmsTim, + { + reg!(self, TimBasic, cr2) + } + + /// Control register 2. + pub fn cr2_ccdma(&self) -> Reg + where + Tim: IsCcDmaTim, + { + reg_cast!(self, Tim4ch, cr2 as Cr2Ccdma, RW) + } + + /// Control register 2. + pub fn cr2_2ch(&self) -> Reg + where + Tim: IsGeneral2ChTim, + { + reg!(self, Tim2ch, cr2) + } + + /// Control register 2. + pub fn cr2_trigdma(&self) -> Reg + where + Tim: IsTrigDmaTim, + { + reg!(self, Tim4ch, cr2) + } + + /// Control register 2. + #[cfg(not(timer_l0))] + pub fn cr2_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, cr2) + } + + /// Control register 2. + #[cfg(not(timer_l0))] + pub fn cr2_adv2ch(&self) -> Reg + where + Tim: IsAdvanced2ChTim, + { + reg!(self, TimAdv2ch, cr2) + } + + /// Control register 2. + #[cfg(not(timer_l0))] + pub fn cr2_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, cr2) + } + + /// Slave mode control register. + pub fn smcr_2ch(&self) -> Reg + where + Tim: IsGeneral2ChTim, + { + reg!(self, Tim2ch, smcr) + } + + /// Slave mode control register. + pub fn smcr_4ch(&self) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, smcr) + } + + /// Slave mode control register. + pub fn smcr_trigdma(&self) -> Reg + where + Tim: IsTrigDmaTim, + { + reg_cast!(self, Tim4ch, smcr as SmcrTrigdma, RW) + } + + /// Slave mode control register. + #[cfg(not(timer_l0))] + pub fn smcr_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, smcr) + } + + /// DMA/Interrupt enable register. + pub fn dier_core(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, dier) + } + + /// DMA/Interrupt enable register + pub fn dier_updma(&self) -> Reg + where + Tim: IsUpDmaTim, + { + reg!(self, TimBasic, dier) + } + + /// DMA/Interrupt enable register. + pub fn dier_1ch(&self) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, dier) + } + + /// DMA/Interrupt enable register. + pub fn dier_2ch(&self) -> Reg + where + Tim: IsGeneral2ChTim, + { + reg!(self, Tim2ch, dier) + } + + /// DMA/Interrupt enable register. + pub fn dier_ccdma(&self) -> Reg + where + Tim: IsCcDmaTim, + { + reg_cast!(self, Tim4ch, dier as DierCcdma, RW) + } + + /// DMA/Interrupt enable register. + pub fn dier_trigdma(&self) -> Reg + where + Tim: IsTrigDmaTim, + { + reg_cast!(self, Tim4ch, dier as DierTrigdma, RW) + } + + /// DMA/Interrupt enable register. + pub fn dier_4ch(&self) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, dier) + } + + /// DMA/Interrupt enable register. + #[cfg(not(timer_l0))] + pub fn dier_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, dier) + } + + /// DMA/Interrupt enable register. + #[cfg(not(timer_l0))] + pub fn dier_adv2ch(&self) -> Reg + where + Tim: IsAdvanced2ChTim, + { + reg!(self, TimAdv2ch, dier) + } + + /// DMA/Interrupt enable register. + #[cfg(not(timer_l0))] + pub fn dier_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, dier) + } + + /// Status register. + pub fn sr_core(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, sr) + } + + /// Status register. + pub fn sr_1ch(&self) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, sr) + } + + /// Status register. + pub fn sr_2ch(&self) -> Reg + where + Tim: IsGeneral2ChTim, + { + reg!(self, Tim2ch, sr) + } + + /// Status register. + pub fn sr_4ch(&self) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, sr) + } + + /// Status register. + #[cfg(not(timer_l0))] + pub fn sr_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, sr) + } + + /// Status register. + #[cfg(not(timer_l0))] + pub fn sr_adv2ch(&self) -> Reg + where + Tim: IsAdvanced2ChTim, + { + reg!(self, TimAdv2ch, sr) + } + + /// Status register. + #[cfg(not(timer_l0))] + pub fn sr_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, sr) + } + + /// Event generation register. + pub fn egr_core(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, egr) + } + + /// Event generation register. + pub fn egr_1ch(&self) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, egr) + } + + /// Event generation register. + pub fn egr_2ch(&self) -> Reg + where + Tim: IsGeneral2ChTim, + { + reg!(self, Tim2ch, egr) + } + + /// Event generation register. + #[cfg(not(timer_l0))] + pub fn egr_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, egr) + } + + /// Event generation register. + #[cfg(not(timer_l0))] + pub fn egr_adv2ch(&self) -> Reg + where + Tim: IsAdvanced2ChTim, + { + reg!(self, TimAdv2ch, egr) + } + + /// Capture/compare mode register 1-2 (input mode), for `n` in `0..2` (one register per two + /// channels). + pub fn ccmr_input_1ch(&self, n: usize) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, ccmr_input(n)) + } + + /// Capture/compare mode register 1-2 (output mode), for `n` in `0..2` (one register per two + /// channels). + pub fn ccmr_output_1ch(&self, n: usize) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, ccmr_output(n)) + } + + /// Capture/compare mode register 1-2 (output mode), for `n` in `0..2` (one register per two + /// channels). + pub fn ccmr_output_4ch(&self, n: usize) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, ccmr_output(n)) + } + + /// Capture/compare mode register 3 (output mode). + /// + /// This register is for channels 5 and 6, which can only be configured as output. + #[cfg(not(timer_l0))] + pub fn ccmr3_output_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, ccmr3_output) + } + + /// Capture/compare enable register. + pub fn ccer_1ch(&self) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, ccer) + } + + /// Capture/compare enable register. + #[cfg(not(timer_l0))] + pub fn ccer_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, ccer) + } + + /// Counter. + pub fn cnt(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, cnt) + } + + /// Counter. + #[cfg(not(timer_l0))] + pub fn cnt_32bit(&self) -> Reg + where + Tim: IsGeneral32BitTim, + { + reg!(self, Tim32bit, cnt) + } + + /// Prescaler. + pub fn psc(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, psc) + } + + /// Auto-reload register. + pub fn arr(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, arr) + } + + /// Auto-reload register (dither mode enabled). + #[cfg(timer_v2)] + pub fn arr_dither(&self) -> Reg + where + Tim: IsCoreTim, + { + reg!(self, TimCore, arr_dither) + } + + /// Auto-reload register. + #[cfg(not(timer_l0))] + pub fn arr_32bit(&self) -> Reg + where + Tim: IsGeneral32BitTim, + { + reg!(self, Tim32bit, arr) + } + + /// Repetition counter register. + #[cfg(not(timer_l0))] + pub fn rcr_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, rcr) + } + + /// Repetition counter register. + #[cfg(not(timer_l0))] + pub fn rcr_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, rcr) + } + + /// Capture/compare register 1-4, for `n` in `0..4` (one register per channel). + pub fn ccr(&self, n: usize) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, ccr(n)) + } + + /// Capture/compare register 1-4 (dither mode enabled), for `n` in `0..4` (one register per + /// channel). + #[cfg(timer_v2)] + pub fn ccr_dither(&self, n: usize) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, ccr_dither(n)) + } + + /// Capture/compare register 1-4, for `n` in `0..4` (one register per channel). + #[cfg(not(timer_l0))] + pub fn ccr_32bit(&self, n: usize) -> Reg + where + Tim: IsGeneral32BitTim, + { + reg!(self, Tim32bit, ccr(n)) + } + + /// Capture/compare register 5. + #[cfg(not(timer_l0))] + pub fn ccr5(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, ccr5) + } + + /// Capture/compare register 5 (dither mode enabled). + #[cfg(timer_v2)] + pub fn ccr5_dither(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, ccr5_dither) + } + + /// Capture/compare register 6. + #[cfg(not(timer_l0))] + pub fn ccr6(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, ccr6) + } + + /// Capture/compare register 6 (dither mode enabled). + #[cfg(timer_v2)] + pub fn ccr6_dither(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, ccr6_dither) + } + + /// Break and dead-time register. + #[cfg(not(timer_l0))] + pub fn bdtr(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, bdtr) + } + + /// DMA control register. + pub fn dcr(&self) -> Reg + where + Tim: IsCcDmaTim, + { + reg!(self, Tim4ch, dcr) + } + + /// DMA address for full transfer. + pub fn dmar(&self) -> Reg + where + Tim: IsCcDmaTim, + { + reg!(self, Tim4ch, dmar) + } + + /// Deadtime register 2. + #[cfg(timer_v2)] + pub fn dtr2(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, dtr2) + } + + /// Encoder control register. + #[cfg(timer_v2)] + pub fn ecr(&self) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, ecr) + } + + /// Alternate function register 1. + #[cfg(not(timer_l0))] + pub fn af1_4ch(&self) -> Reg + where + Tim: IsGeneral4ChTim, + { + reg!(self, Tim4ch, af1) + } + + /// Alternate function register 1. + #[cfg(not(timer_l0))] + pub fn af1_adv1ch(&self) -> Reg + where + Tim: IsAdvanced1ChTim, + { + reg!(self, TimAdv1ch, af1) + } + + /// Alternate function register 1. + #[cfg(not(timer_l0))] + pub fn af1_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, af1) + } + + /// Alternate function register 2. + #[cfg(timer_v2)] + pub fn af2_ccdma(&self) -> Reg + where + Tim: IsCcDmaTim, + { + reg!(self, Tim4ch, af2) + } + + /// Alternate function register 2. + #[cfg(not(timer_l0))] + pub fn af2_adv4ch(&self) -> Reg + where + Tim: IsAdvanced4ChTim, + { + reg!(self, TimAdv4ch, af2) + } + + /// Input selection register. + #[cfg(not(timer_l0))] + pub fn tisel(&self) -> Reg + where + Tim: IsGeneral1ChTim, + { + reg!(self, Tim1ch, tisel) + } +} diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index b7771bd642..be2c436fab 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -1,89 +1,183 @@ //! Simple PWM driver. -use core::marker::PhantomData; - -use embassy_hal_internal::{into_ref, PeripheralRef}; +use core::array; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; -use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; -use crate::gpio::{AfType, AnyPin, OutputType, Speed}; +use super::raw::{self, RawTimer, RawTimerPin}; +use super::{ + CcDma, CcDmaInstance, CcDmaTim, Ch1, Ch2, Ch3, Ch4, Channel, ChannelMarker, CoreInstance, General1ChInstance, + General1ChTim, General4ChInstance, General4ChTim, IsCcDmaTim, IsGeneral1ChTim, IsGeneral4ChTim, TimerPin, UpDma, +}; +use crate::gpio::{AfType, OutputType, Speed}; use crate::time::Hertz; -use crate::Peripheral; - -/// 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. +use crate::{dma, into_ref, Peripheral, PeripheralRef}; + +/// Builder for [`SimplePwm`] driver. /// -/// This wraps a pin to make it usable with PWM. -pub struct PwmPin<'d, T, C> { - _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(T, C)>, +/// Create the builder using [`Builder::new()`], then attach output pins and DMAs using methods on +/// the builder, and finally build the [`SimplePwm`] driver using one of the `build` methods(). +pub struct Builder<'d, T> { + tim: PeripheralRef<'d, T>, + up_dma: Option>, + channel_pins: [Option>; 4], + cc_dmas: [Option>; 4], +} + +impl<'d, T: CoreInstance> Builder<'d, T> { + /// Start building a PWM driver from a timer peripheral. + pub fn new(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + Self { + tim, + up_dma: None, + channel_pins: array::from_fn(|_| None), + cc_dmas: array::from_fn(|_| None), + } + } + + /// Attach an output pin to the PWM driver. + /// + /// You may use convenience methods [`ch1_pin()`][Self::ch1_pin()] to `ch4_pin()` to aid type + /// inference. + pub fn pin( + &mut self, + pin: impl Peripheral

> + 'd, + output_type: OutputType, + ) -> &mut Self { + let pin = RawTimerPin::new(pin, AfType::output(output_type, Speed::VeryHigh)); + self.channel_pins[C::CHANNEL.index()] = Some(pin); + self + } + + /// Attach update DMA to the PWM driver. + /// + /// This enables you to use [`SimplePwm::waveform_up_dma()`]. + pub fn up_dma(&mut self, dma: impl Peripheral

> + 'd) -> &mut Self { + self.up_dma = Some(raw::up_dma(dma)); + self + } + + /// Attach capture/compare DMA to the PWM driver. + /// + /// This enables you to use [`SimplePwm::waveform_cc_dma()`] with the given channel. You may + /// use convenience methods [`ch1_cc_dma()`][Self::ch1_cc_dma()] to `ch4_cc_dma()`] to aid type + /// inference. + pub fn cc_dma(&mut self, dma: impl Peripheral

> + 'd) -> &mut Self { + self.cc_dmas[C::CHANNEL.index()] = Some(raw::cc_dma(dma)); + self + } } +#[rustfmt::skip] macro_rules! channel_impl { - ($new_chx: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: impl Peripheral

> + 'd, output_type: OutputType) -> Self { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); - }); - PwmPin { - _pin: pin.map_into(), - phantom: PhantomData, - } + ($chx_pin:ident, $chx_cc_dma:ident, $channel:ident) => { + impl<'d, T: CoreInstance> Builder<'d, T> { + #[doc = concat!( + "Attach an output pin for channel ", + stringify!($channel), + " to the PWM driver.\n\nSee [`pin()`][Self::pin()] for details.", + )] + pub fn $chx_pin( + &mut self, + pin: impl Peripheral

> + 'd, + output_type: OutputType, + ) -> &mut Self { + self.pin::<$channel>(pin, output_type) + } + + #[doc = concat!( + "Attach capture/compare DMA for channel ", + stringify!($channel), + " to the PWM driver.\n\nSee [`cc_dma()`][Self::cc_dma()] for details.", + )] + pub fn $chx_cc_dma(&mut self, dma: impl Peripheral

> + 'd) -> &mut Self { + self.cc_dma::<$channel>(dma) } } }; } +channel_impl!(ch1_pin, ch1_cc_dma, Ch1); +channel_impl!(ch2_pin, ch2_cc_dma, Ch2); +channel_impl!(ch3_pin, ch3_cc_dma, Ch3); +channel_impl!(ch4_pin, ch4_cc_dma, Ch4); + +impl<'d, T> Builder<'d, T> +where + PeripheralRef<'d, T>: Peripheral

+ 'd, +{ + /// Initialize the PWM driver for any timer peripheral with channels. + /// + /// PWM driver created using this method works with any timer peripheral, but it does not + /// support generating PWM waveforms using DMA and does not support changing the [counting + /// mode](CountingMode). + pub fn build(self, freq: Hertz) -> SimplePwm<'d, General1ChTim> + where + T: General1ChInstance, + { + let raw = RawTimer::new_general_1ch(self.tim); + SimplePwm::new_inner(raw, self.up_dma, self.channel_pins, self.cc_dmas, freq) + } -channel_impl!(new_ch1, Ch1, Channel1Pin); -channel_impl!(new_ch2, Ch2, Channel2Pin); -channel_impl!(new_ch3, Ch3, Channel3Pin); -channel_impl!(new_ch4, Ch4, Channel4Pin); + /// Initialize the PWM driver for a timer peripheral with DMA. + /// + /// Drivers created using this method support PWM waveforms using DMA. + pub fn build_dma(self, freq: Hertz) -> SimplePwm<'d, CcDmaTim> + where + T: CcDmaInstance, + { + let raw = RawTimer::new_cc_dma(self.tim); + SimplePwm::new_inner(raw, self.up_dma, self.channel_pins, self.cc_dmas, freq) + } + + /// Initialize the PWM driver for a 4-channel timer peripheral. + /// + /// Drivers created using this method support all features. The driver starts with + /// [`EdgeAlignedUp`](CountingMode::EdgeAlignedUp) counting mode, but you can call + /// [`SimplePwm::set_frequency_counting_mode()`] to change it. + pub fn build_4ch(self, freq: Hertz) -> SimplePwm<'d, General4ChTim> + where + T: General4ChInstance, + { + let raw = RawTimer::new_general_4ch(self.tim); + SimplePwm::new_inner(raw, self.up_dma, self.channel_pins, self.cc_dmas, freq) + } +} /// Simple PWM driver. -pub struct SimplePwm<'d, T: GeneralInstance4Channel> { - inner: Timer<'d, T>, +/// +/// Use [`Builder`] to build an instance of this driver. +pub struct SimplePwm<'d, Tim> { + inner: Timer<'d, Tim>, + up_dma: Option>, + channel_pins: [Option>; 4], + cc_dmas: [Option>; 4], } -impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { - /// Create a new simple PWM driver. - pub fn new( - tim: impl Peripheral

+ 'd, - _ch1: Option>, - _ch2: Option>, - _ch3: Option>, - _ch4: Option>, +impl<'d, Tim: IsGeneral1ChTim> SimplePwm<'d, Tim> { + fn new_inner( + raw: RawTimer<'d, Tim>, + up_dma: Option>, + channel_pins: [Option>; 4], + cc_dmas: [Option>; 4], freq: Hertz, - counting_mode: CountingMode, ) -> Self { - Self::new_inner(tim, freq, counting_mode) - } - - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - let mut this = Self { inner: Timer::new(tim) }; + let mut this = Self { + inner: Timer::new(raw), + up_dma, + channel_pins, + cc_dmas, + }; - this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); - this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + this.inner.enable_outputs(); // Required for advanced timers, see `RawTimer::enable_outputs()` for details this.inner.start(); [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() - .for_each(|&channel| { - this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); - - this.inner.set_output_compare_preload(channel, true); + .filter(|&ch| this.channel_pins[ch.index()].is_some()) + .for_each(|&ch| { + this.inner.set_output_compare_mode(ch, OutputCompareMode::PwmMode1); + this.inner.set_output_compare_preload(ch, true); }); this @@ -91,82 +185,80 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.enable_channel(channel, true); } /// Disable the given channel. pub fn disable(&mut self, channel: Channel) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.enable_channel(channel, false); } /// Check whether given channel is enabled pub fn is_enabled(&self, channel: Channel) -> bool { - self.inner.get_channel_enable_state(channel) + assert!(self.channel_pins[channel.index()].is_some()); + self.inner.channel_enable_state(channel) } /// Set PWM frequency. /// - /// Note: when you call this, the max duty value changes, so you will have to - /// call `set_duty` on all channels with the duty calculated based on the new max duty. + /// When you call this, the max duty value changes, so you will have to call + /// [`set_duty()`][Self::set_duty()] on all channels with the duty calculated based on the new + /// max duty. pub fn set_frequency(&mut self, freq: Hertz) { - let multiplier = if self.inner.get_counting_mode().is_center_aligned() { - 2u8 - } else { - 1u8 - }; - self.inner.set_frequency(freq * multiplier); + self.inner.set_frequency(freq); } /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn get_max_duty(&self) -> u32 { - self.inner.get_max_compare_value() + 1 + pub fn max_duty(&self) -> u32 { + self.inner.max_compare_value() + 1 } /// Set the duty for a given channel. /// - /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. + /// The value ranges from 0 for 0% duty, to [`max_duty`](Self::max_duty) for 100% duty, both included. pub fn set_duty(&mut self, channel: Channel, duty: u32) { - assert!(duty <= self.get_max_duty()); + assert!(self.channel_pins[channel.index()].is_some()); + assert!(duty <= self.max_duty()); self.inner.set_compare_value(channel, duty) } /// Get the duty for a given channel. /// - /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn get_duty(&self, channel: Channel) -> u32 { - self.inner.get_compare_value(channel) + /// The value ranges from 0 for 0% duty, to [`max_duty()`](Self::max_duty()) for 100% duty, both included. + pub fn duty(&self, channel: Channel) -> u32 { + assert!(self.channel_pins[channel.index()].is_some()); + self.inner.compare_value(channel) } /// Set the output polarity for a given channel. pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.set_output_polarity(channel, polarity); } /// Set the output compare mode for a given channel. pub fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { + assert!(self.channel_pins[channel.index()].is_some()); self.inner.set_output_compare_mode(channel, mode); } +} - /// Generate a sequence of PWM waveform +impl<'d, Tim: IsCcDmaTim> SimplePwm<'d, Tim> { + /// Generate a sequence of PWM values (a waveform) using the update DMA. /// - /// Note: - /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up( - &mut self, - dma: impl Peripheral

>, - channel: Channel, - duty: &[u16], - ) { - into_ref!(dma); - - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); + /// To use this method, you have to pass the update DMA to [`Builder::up_dma()`] and construct + /// the driver using [`Builder::build_dma()`] or [`Builder::build_4ch()`] (to ensure that the + /// underlying timer peripheral implements the update DMA). + pub async fn waveform_up_dma(&mut self, channel: Channel, duty: &[u16]) { + assert!(self.channel_pins[channel.index()].is_some()); - let original_duty_state = self.get_duty(channel); + let original_duty_state = self.duty(channel); let original_enable_state = self.is_enabled(channel); - let original_update_dma_state = self.inner.get_update_dma_state(); + let original_update_dma_state = self.inner.update_dma_state(); if !original_update_dma_state { self.inner.enable_update_dma(true); @@ -176,27 +268,23 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { self.enable(channel); } + let up_dma = unwrap!(self.up_dma.as_mut()); unsafe { - #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { + let dma_transfer_options = dma::TransferOptions { #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), + fifo_threshold: Some(dma::FifoThreshold::Full), #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, + mburst: dma::Burst::Incr8, ..Default::default() }; - Transfer::new_write( - &mut dma, - req, - duty, - self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _, - dma_transfer_option, - ) - .await + up_dma + .write( + duty, + self.inner.raw.ccr(channel.index()).as_ptr() as *mut u16, + dma_transfer_options, + ) + .await }; // restore output compare state @@ -206,7 +294,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { self.set_duty(channel, original_duty_state); - // Since DMA is closed before timer update event trigger DMA is turn off, + // Since DMA is closed before timer update event trigger DMA is turned off, // this can almost always trigger a DMA FIFO error. // // optional TODO: @@ -215,96 +303,97 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { self.inner.enable_update_dma(false); } } -} -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 - /// - /// Note: - /// you will need to provide corresponding TIMx_CHy DMA channel to use this method. - pub async fn $fn_name(&mut self, dma: impl Peripheral

>, duty: &[u16]) { - use crate::pac::timer::vals::Ccds; - - into_ref!(dma); - - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); - - let cc_channel = Channel::$cc_ch; - - let original_duty_state = self.get_duty(cc_channel); - let original_enable_state = self.is_enabled(cc_channel); - let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; - 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::ONUPDATE) - } - - if !original_cc_dma_enabled { - self.inner.set_cc_dma_enable_state(cc_channel, true); - } - - if !original_enable_state { - self.enable(cc_channel); - } - - 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() - }; - - Transfer::new_write( - &mut dma, - req, - duty, - self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _, - dma_transfer_option, - ) - .await - }; - - // restore output compare state - if !original_enable_state { - self.disable(cc_channel); - } - - self.set_duty(cc_channel, 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::ONCOMPARE) - } - } + /// Generate a sequence of PWM values (a waveform) using the capture/compare DMA for the + /// channel. + /// + /// To use this method, you have to pass the capture/compare DMA for the channel to + /// [`Builder::cc_dma()`] (or by using a convenience method such as [`Builder::ch1_cc_dma()`]) + /// and construct the driver using [`Builder::build_dma()`] or [`Builder::build_4ch()`] (to + /// ensure that the underlying timer peripheral implements the capture/compare DMA). + pub async fn waveform_cc_dma(&mut self, channel: Channel, duty: &[u16]) { + use crate::pac::timer::vals::Ccds; + assert!(self.channel_pins[channel.index()].is_some()); + + let original_duty_state = self.duty(channel); + let original_enable_state = self.is_enabled(channel); + let original_cc_dma_on_update = self.inner.cc_dma_selection() == Ccds::ONUPDATE; + let original_cc_dma_enabled = self.inner.cc_dma_enable_state(channel); + + // redirect CC DMA request onto Update Event + if !original_cc_dma_on_update { + self.inner.set_cc_dma_selection(Ccds::ONUPDATE) } - }; + + if !original_cc_dma_enabled { + self.inner.set_cc_dma_enable_state(channel, true); + } + + if !original_enable_state { + self.enable(channel); + } + + let cc_dma = unwrap!(self.cc_dmas[channel.index()].as_mut()); + unsafe { + let dma_transfer_option = dma::TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(dma::FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: dma::Burst::Incr8, + ..Default::default() + }; + + cc_dma + .write( + duty, + self.inner.raw.ccr(channel.index()).as_ptr() as *mut u16, + dma_transfer_option, + ) + .await + }; + + // restore output compare state + if !original_enable_state { + self.disable(channel); + } + + self.set_duty(channel, 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(channel, false); + } + + if !original_cc_dma_on_update { + self.inner.set_cc_dma_selection(Ccds::ONCOMPARE) + } + } } -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); +impl<'d, Tim: IsGeneral4ChTim> SimplePwm<'d, Tim> { + /// Set PWM frequency and counting mode. + /// + /// This method can only be used with [`General4ChInstance`] timer peripherals and you need to + /// construct the [`SimplePwm`] driver using [`build_4ch()`][Builder::build_4ch()]. + /// + /// When you call this, the max duty value changes, so you will have to call + /// [`set_duty()`][Self::set_duty()] on all channels with the duty calculated based on the new + /// max duty. + pub fn set_frequency_counting_mode(&mut self, freq: Hertz, counting_mode: CountingMode) { + let multiplier = match counting_mode.is_center_aligned() { + true => 2u8, + false => 1u8, + }; + self.inner.set_frequency(freq * multiplier); + self.inner.set_counting_mode(counting_mode); + } +} -impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { +impl<'d, Tim: IsGeneral1ChTim> embedded_hal_02::Pwm for SimplePwm<'d, Tim> { type Channel = Channel; type Time = Hertz; type Duty = u32; @@ -318,15 +407,16 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { } fn get_period(&self) -> Self::Time { - self.inner.get_frequency() + // TODO: we return frequency instead of period here?? + self.inner.frequency() } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) + self.inner.compare_value(channel) } fn get_max_duty(&self) -> Self::Duty { - self.inner.get_max_compare_value() + 1 + self.inner.max_compare_value() + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {