diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 1e6626a5..faefaabb 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -68,22 +68,22 @@ mod sealed { pub trait AdvancedChannel: sealed::AdvancedChannel {} /// HRTIM PWM pin. -pub struct PwmPin<'d, Perip, Channel> { +pub struct PwmPin<'d, T, C> { _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, + phantom: PhantomData<(T, C)>, } /// HRTIM complementary PWM pin. -pub struct ComplementaryPwmPin<'d, Perip, Channel> { +pub struct ComplementaryPwmPin<'d, T, C> { _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, + phantom: PhantomData<(T, C)>, } macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel> { + impl<'d, T: Instance> PwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); @@ -98,9 +98,9 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 5d9b4e6a..4952d26e 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,6 +79,7 @@ pub(crate) mod _generated { #![allow(dead_code)] #![allow(unused_imports)] #![allow(non_snake_case)] + #![allow(missing_docs)] include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } @@ -149,15 +150,33 @@ use crate::interrupt::Priority; pub use crate::pac::NVIC_PRIO_BITS; use crate::rcc::sealed::RccPeripheral; +/// `embassy-stm32` global configuration. #[non_exhaustive] pub struct Config { + /// RCC config. pub rcc: rcc::Config, + + /// Enable debug during sleep. + /// + /// May incrase power consumption. Defaults to true. #[cfg(dbgmcu)] pub enable_debug_during_sleep: bool, + + /// BDMA interrupt priority. + /// + /// Defaults to P0 (highest). #[cfg(bdma)] pub bdma_interrupt_priority: Priority, + + /// DMA interrupt priority. + /// + /// Defaults to P0 (highest). #[cfg(dma)] pub dma_interrupt_priority: Priority, + + /// GPDMA interrupt priority. + /// + /// Defaults to P0 (highest). #[cfg(gpdma)] pub gpdma_interrupt_priority: Priority, } @@ -178,7 +197,11 @@ impl Default for Config { } } -/// Initialize embassy. +/// Initialize the `embassy-stm32` HAL with the provided configuration. +/// +/// This returns the peripheral singletons that can be used for creating drivers. +/// +/// This should only be called once at startup, otherwise it panics. pub fn init(config: Config) -> Peripherals { critical_section::with(|cs| { let p = Peripherals::take_with_cs(cs); diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index e543a5b4..71d7110b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -13,15 +13,19 @@ use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; -pub struct ComplementaryPwmPin<'d, Perip, Channel> { +/// Complementary PWM pin wrapper. +/// +/// This wraps a pin to make it usable with PWM. +pub struct ComplementaryPwmPin<'d, T, C> { _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, + phantom: PhantomData<(T, C)>, } macro_rules! complementary_channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { + impl<'d, T: CaptureCompare16bitInstance> 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(); @@ -43,11 +47,13 @@ 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> { inner: PeripheralRef<'d, T>, } impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { + /// Create a new complementary PWM driver. pub fn new( tim: impl Peripheral

+ 'd, _ch1: Option>, @@ -72,7 +78,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { let mut this = Self { inner: tim }; this.inner.set_counting_mode(counting_mode); - this.set_freq(freq); + this.set_frequency(freq); this.inner.start(); this.inner.enable_outputs(); @@ -88,17 +94,23 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { this } + /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); self.inner.enable_complementary_channel(channel, true); } + /// Disable the given channel. pub fn disable(&mut self, channel: Channel) { self.inner.enable_complementary_channel(channel, false); self.inner.enable_channel(channel, false); } - pub fn set_freq(&mut self, freq: Hertz) { + /// 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. + pub fn set_frequency(&mut self, freq: Hertz) { let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { @@ -107,15 +119,22 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { self.inner.set_frequency(freq * multiplier); } + /// 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() + 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. pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } + /// Set the output polarity for a given channel. pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { self.inner.set_output_polarity(channel, polarity); self.inner.set_complementary_output_polarity(channel, polarity); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 42ef878f..74120ada 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -17,17 +17,27 @@ pub mod low_level { } pub(crate) mod sealed { - use super::*; + + /// Basic 16-bit timer instance. pub trait Basic16bitInstance: RccPeripheral { + /// Interrupt for this timer. type Interrupt: interrupt::typelevel::Interrupt; + /// Get access to the basic 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. fn regs() -> crate::pac::timer::TimBasic; + /// Start the timer. fn start(&mut self) { Self::regs().cr1().modify(|r| r.set_cen(true)); } + /// Stop the timer. fn stop(&mut self) { Self::regs().cr1().modify(|r| r.set_cen(false)); } @@ -63,6 +73,9 @@ pub(crate) mod sealed { regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); } + /// Clear update interrupt. + /// + /// Returns whether the update interrupt flag was set. fn clear_update_interrupt(&mut self) -> bool { let regs = Self::regs(); let sr = regs.sr().read(); @@ -76,14 +89,17 @@ pub(crate) mod sealed { } } + /// Enable/disable the update interrupt. fn enable_update_interrupt(&mut self, enable: bool) { Self::regs().dier().write(|r| r.set_uie(enable)); } + /// Enable/disable autoreload preload. fn set_autoreload_preload(&mut self, enable: bool) { Self::regs().cr1().modify(|r| r.set_arpe(enable)); } + /// Get the timer frequency. fn get_frequency(&self) -> Hertz { let timer_f = Self::frequency(); @@ -95,9 +111,17 @@ pub(crate) mod sealed { } } + /// Gneral-purpose 16-bit timer instance. pub trait GeneralPurpose16bitInstance: Basic16bitInstance { + /// 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. fn regs_gp16() -> crate::pac::timer::TimGp16; + /// Set counting mode. fn set_counting_mode(&mut self, mode: CountingMode) { let (cms, dir) = mode.into(); @@ -110,19 +134,29 @@ pub(crate) mod sealed { Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) } + /// Get counting mode. fn get_counting_mode(&self) -> CountingMode { let cr1 = Self::regs_gp16().cr1().read(); (cr1.cms(), cr1.dir()).into() } + /// Set clock divider. fn set_clock_division(&mut self, ckd: vals::Ckd) { Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); } } + /// Gneral-purpose 32-bit timer instance. pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { + /// Get access to the general purpose 32bit 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. fn regs_gp32() -> crate::pac::timer::TimGp32; + /// Set timer frequency. fn set_frequency(&mut self, frequency: Hertz) { let f = frequency.0; assert!(f > 0); @@ -140,6 +174,7 @@ pub(crate) mod sealed { regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); } + /// Get timer frequency. fn get_frequency(&self) -> Hertz { let timer_f = Self::frequency(); @@ -151,141 +186,177 @@ pub(crate) mod sealed { } } + /// Advanced control timer instance. pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { + /// Get access to the advanced timer registers. fn regs_advanced() -> crate::pac::timer::TimAdv; } + /// Capture/Compare 16-bit timer instance. pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { + /// Set input capture filter. fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { - let raw_channel = channel.raw(); + let raw_channel = channel.index(); Self::regs_gp16() .ccmr_input(raw_channel / 2) .modify(|r| r.set_icf(raw_channel % 2, icf)); } + /// Clear input interrupt. fn clear_input_interrupt(&mut self, channel: Channel) { - Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.raw(), false)); + Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); } + /// Enable input interrupt. fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { - Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.raw(), enable)); + Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); } + + /// Set input capture prescaler. fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { - let raw_channel = channel.raw(); + let raw_channel = channel.index(); Self::regs_gp16() .ccmr_input(raw_channel / 2) .modify(|r| r.set_icpsc(raw_channel % 2, factor)); } + /// Set input TI selection. fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { - let raw_channel = channel.raw(); + let raw_channel = channel.index(); Self::regs_gp16() .ccmr_input(raw_channel / 2) .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); } + + /// Set input capture mode. fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { Self::regs_gp16().ccer().modify(|r| match mode { InputCaptureMode::Rising => { - r.set_ccnp(channel.raw(), false); - r.set_ccp(channel.raw(), false); + r.set_ccnp(channel.index(), false); + r.set_ccp(channel.index(), false); } InputCaptureMode::Falling => { - r.set_ccnp(channel.raw(), false); - r.set_ccp(channel.raw(), true); + r.set_ccnp(channel.index(), false); + r.set_ccp(channel.index(), true); } InputCaptureMode::BothEdges => { - r.set_ccnp(channel.raw(), true); - r.set_ccp(channel.raw(), true); + r.set_ccnp(channel.index(), true); + r.set_ccp(channel.index(), true); } }); } + + /// Enable timer outputs. fn enable_outputs(&mut self); + /// Set output compare mode. fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { let r = Self::regs_gp16(); - let raw_channel: usize = channel.raw(); + let raw_channel: usize = channel.index(); r.ccmr_output(raw_channel / 2) .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + /// Set output polarity. fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { Self::regs_gp16() .ccer() - .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + .modify(|w| w.set_ccp(channel.index(), polarity.into())); } + /// Enable/disable a channel. fn enable_channel(&mut self, channel: Channel, enable: bool) { - Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.raw(), enable)); + Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); } + /// Set compare value for a channel. fn set_compare_value(&mut self, channel: Channel, value: u16) { - Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); } + /// Get capture value for a channel. fn get_capture_value(&mut self, channel: Channel) -> u16 { - Self::regs_gp16().ccr(channel.raw()).read().ccr() + Self::regs_gp16().ccr(channel.index()).read().ccr() } + /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. fn get_max_compare_value(&self) -> u16 { Self::regs_gp16().arr().read().arr() } + /// Get compare value for a channel. fn get_compare_value(&self, channel: Channel) -> u16 { - Self::regs_gp16().ccr(channel.raw()).read().ccr() + Self::regs_gp16().ccr(channel.index()).read().ccr() } } + /// Capture/Compare 16-bit timer instance with complementary pin support. pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance { + /// Set complementary output polarity. fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { Self::regs_advanced() .ccer() - .modify(|w| w.set_ccnp(channel.raw(), polarity.into())); + .modify(|w| w.set_ccnp(channel.index(), polarity.into())); } + /// Set clock divider for the dead time. fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); } + /// Set dead time, as a fraction of the max duty value. fn set_dead_time_value(&mut self, value: u8) { Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); } + /// Enable/disable a complementary channel. fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { Self::regs_advanced() .ccer() - .modify(|w| w.set_ccne(channel.raw(), enable)); + .modify(|w| w.set_ccne(channel.index(), enable)); } } + /// Capture/Compare 32-bit timer instance. pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance { + /// Set comapre value for a channel. fn set_compare_value(&mut self, channel: Channel, value: u32) { - Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value)); } + /// Get capture value for a channel. fn get_capture_value(&mut self, channel: Channel) -> u32 { - Self::regs_gp32().ccr(channel.raw()).read().ccr() + Self::regs_gp32().ccr(channel.index()).read().ccr() } + /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. fn get_max_compare_value(&self) -> u32 { Self::regs_gp32().arr().read().arr() } + /// Get compare value for a channel. fn get_compare_value(&self, channel: Channel) -> u32 { - Self::regs_gp32().ccr(channel.raw()).read().ccr() + Self::regs_gp32().ccr(channel.index()).read().ccr() } } } +/// Timer channel. #[derive(Clone, Copy)] pub enum Channel { + /// Channel 1. Ch1, + /// Channel 2. Ch2, + /// Channel 3. Ch3, + /// Channel 4. Ch4, } impl Channel { - pub fn raw(&self) -> usize { + /// Get the channel index (0..3) + pub fn index(&self) -> usize { match self { Channel::Ch1 => 0, Channel::Ch2 => 1, @@ -295,17 +366,25 @@ impl Channel { } } +/// Input capture mode. #[derive(Clone, Copy)] pub enum InputCaptureMode { + /// Rising edge only. Rising, + /// Falling edge only. Falling, + /// Both rising or falling edges. BothEdges, } +/// Input TI selection. #[derive(Clone, Copy)] pub enum InputTISelection { + /// Normal Normal, + /// Alternate Alternate, + /// TRC TRC, } @@ -319,6 +398,7 @@ impl From for stm32_metapac::timer::vals::CcmrInputCcs { } } +/// Timer counting mode. #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum CountingMode { @@ -345,6 +425,7 @@ pub enum CountingMode { } impl CountingMode { + /// Return whether this mode is edge-aligned (up or down). pub fn is_edge_aligned(&self) -> bool { match self { CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true, @@ -352,6 +433,7 @@ impl CountingMode { } } + /// Return whether this mode is center-aligned. pub fn is_center_aligned(&self) -> bool { match self { CountingMode::CenterAlignedDownInterrupts @@ -386,16 +468,34 @@ impl From<(vals::Cms, vals::Dir)> for CountingMode { } } +/// Output compare mode. #[derive(Clone, Copy)] pub enum OutputCompareMode { + /// The comparison between the output compare register TIMx_CCRx and + /// the counter TIMx_CNT has no effect on the outputs. + /// (this mode is used to generate a timing base). Frozen, + /// Set channel to active level on match. OCxREF signal is forced high when the + /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). ActiveOnMatch, + /// Set channel to inactive level on match. OCxREF signal is forced low when the + /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). InactiveOnMatch, + /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx. Toggle, + /// Force inactive level - OCxREF is forced low. ForceInactive, + /// Force active level - OCxREF is forced high. ForceActive, + /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNTTIMx_CCRx else active (OCxREF=1). PwmMode1, + /// PWM mode 2 - In upcounting, channel is inactive as long as + /// TIMx_CNTTIMx_CCRx else inactive. PwmMode2, + // TODO: there's more modes here depending on the chip family. } impl From for stm32_metapac::timer::vals::Ocm { @@ -413,9 +513,12 @@ impl From for stm32_metapac::timer::vals::Ocm { } } +/// Timer output pin polarity. #[derive(Clone, Copy)] pub enum OutputPolarity { + /// Active high (higher duty value makes the pin spend more time high). ActiveHigh, + /// Active low (higher duty value makes the pin spend more time low). ActiveLow, } @@ -428,24 +531,31 @@ impl From for bool { } } +/// Basic 16-bit timer instance. pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} +/// Gneral-purpose 16-bit timer instance. pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} +/// Gneral-purpose 32-bit timer instance. pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} +/// Advanced control timer instance. pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} +/// Capture/Compare 16-bit timer instance. pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static { } +/// Capture/Compare 16-bit timer instance with complementary pin support. pub trait ComplementaryCaptureCompare16bitInstance: sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static { } +/// Capture/Compare 32-bit timer instance. pub trait CaptureCompare32bitInstance: sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static { diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 9f9379c2..59efb72b 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -9,23 +9,30 @@ use crate::gpio::sealed::AFType; use crate::gpio::AnyPin; use crate::Peripheral; +/// Counting direction pub enum Direction { + /// Counting up. Upcounting, + /// Counting down. Downcounting, } -pub struct Ch1; -pub struct Ch2; +/// Channel 1 marker type. +pub enum Ch1 {} +/// Channel 2 marker type. +pub enum Ch2 {} -pub struct QeiPin<'d, Perip, Channel> { +/// Wrapper for using a pin with QEI. +pub struct QeiPin<'d, T, Channel> { _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, + phantom: PhantomData<(T, Channel)>, } macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + impl<'d, T: CaptureCompare16bitInstance> 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(); @@ -45,11 +52,13 @@ macro_rules! channel_impl { channel_impl!(new_ch1, Ch1, Channel1Pin); channel_impl!(new_ch2, Ch2, Channel2Pin); +/// Quadrature decoder driver. pub struct Qei<'d, T> { _inner: PeripheralRef<'d, T>, } impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { + /// 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) } @@ -84,6 +93,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { Self { _inner: tim } } + /// Get direction. pub fn read_direction(&self) -> Direction { match T::regs_gp16().cr1().read().dir() { vals::Dir::DOWN => Direction::Downcounting, @@ -91,6 +101,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { } } + /// Get count. pub fn count(&self) -> u16 { T::regs_gp16().cnt().read().cnt() } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 234bbaff..e6072aa1 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -11,20 +11,28 @@ use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; -pub struct Ch1; -pub struct Ch2; -pub struct Ch3; -pub struct Ch4; +/// 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 {} -pub struct PwmPin<'d, Perip, Channel> { +/// PWM pin wrapper. +/// +/// This wraps a pin to make it usable with PWM. +pub struct PwmPin<'d, T, C> { _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, + phantom: PhantomData<(T, C)>, } macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { + impl<'d, T: CaptureCompare16bitInstance> 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(); @@ -46,11 +54,13 @@ channel_impl!(new_ch2, Ch2, Channel2Pin); channel_impl!(new_ch3, Ch3, Channel3Pin); channel_impl!(new_ch4, Ch4, Channel4Pin); +/// Simple PWM driver. pub struct SimplePwm<'d, T> { inner: PeripheralRef<'d, T>, } impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { + /// Create a new simple PWM driver. pub fn new( tim: impl Peripheral

+ 'd, _ch1: Option>, @@ -71,7 +81,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { let mut this = Self { inner: tim }; this.inner.set_counting_mode(counting_mode); - this.set_freq(freq); + this.set_frequency(freq); this.inner.start(); this.inner.enable_outputs(); @@ -87,15 +97,21 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { this } + /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); } + /// Disable the given channel. pub fn disable(&mut self, channel: Channel) { self.inner.enable_channel(channel, false); } - pub fn set_freq(&mut self, freq: Hertz) { + /// 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. + pub fn set_frequency(&mut self, freq: Hertz) { let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { @@ -104,15 +120,22 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { self.inner.set_frequency(freq * multiplier); } + /// 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() + 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. pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } + /// Set the output polarity for a given channel. pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { self.inner.set_output_polarity(channel, polarity); } diff --git a/examples/stm32f4/src/bin/ws2812_pwm_dma.rs b/examples/stm32f4/src/bin/ws2812_pwm_dma.rs index 52cc665c..39f5d342 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm_dma.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm_dma.rs @@ -110,7 +110,7 @@ async fn main(_spawner: Spawner) { &mut dp.DMA1_CH2, 5, color_list[color_list_index], - pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _, + pac::TIM3.ccr(pwm_channel.index()).as_ptr() as *mut _, dma_transfer_option, ) .await; diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index e0be495d..394ed328 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -85,7 +85,7 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { let mut this = Self { inner: tim }; - this.set_freq(freq); + this.set_frequency(freq); this.inner.start(); let r = T::regs_gp32(); @@ -102,14 +102,14 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { } pub fn enable(&mut self, channel: Channel) { - T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); + T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true)); } pub fn disable(&mut self, channel: Channel) { - T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); + T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false)); } - pub fn set_freq(&mut self, freq: Hertz) { + pub fn set_frequency(&mut self, freq: Hertz) { ::set_frequency(&mut self.inner, freq); } @@ -119,6 +119,6 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { pub fn set_duty(&mut self, channel: Channel, duty: u32) { defmt::assert!(duty < self.get_max_duty()); - T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) + T::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(duty)) } }