From 189b15c426a3a9ef7d4024ba7e5de6a255f88ee7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Dec 2023 17:35:38 +0100 Subject: [PATCH 1/2] stm32/timer: docs. --- embassy-stm32/src/hrtim/mod.rs | 16 +- embassy-stm32/src/lib.rs | 25 ++- embassy-stm32/src/timer/complementary_pwm.rs | 31 +++- embassy-stm32/src/timer/mod.rs | 158 +++++++++++++++--- embassy-stm32/src/timer/qei.rs | 23 ++- embassy-stm32/src/timer/simple_pwm.rs | 43 +++-- examples/stm32f4/src/bin/ws2812_pwm_dma.rs | 2 +- .../stm32h7/src/bin/low_level_timer_api.rs | 10 +- 8 files changed, 247 insertions(+), 61 deletions(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 1e6626a58..faefaabbc 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 5d9b4e6a0..4952d26eb 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 e543a5b43..71d7110b5 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 42ef878f7..74120adad 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 9f9379c20..59efb72ba 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 234bbaff0..e6072aa15 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 52cc665c7..39f5d3421 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 e0be495d1..394ed3281 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)) } } From c8c8b89104acb396476b72ff9192d7b14a46752d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Dec 2023 18:03:20 +0100 Subject: [PATCH 2/2] stm32: doc everything else. --- embassy-stm32/src/dma/gpdma.rs | 18 ++++++ embassy-stm32/src/ipcc.rs | 8 +++ embassy-stm32/src/lib.rs | 1 + embassy-stm32/src/low_power.rs | 106 +++++++++++++++++-------------- embassy-stm32/src/opamp.rs | 9 +++ embassy-stm32/src/rng.rs | 1 + embassy-stm32/src/sdmmc/mod.rs | 2 + embassy-stm32/src/usb/mod.rs | 4 ++ embassy-stm32/src/usb/usb.rs | 7 ++ embassy-stm32/src/usb_otg/mod.rs | 2 + embassy-stm32/src/usb_otg/usb.rs | 13 +++- embassy-stm32/src/wdg/mod.rs | 4 ++ 12 files changed, 127 insertions(+), 48 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index b061415eb..34b2426b9 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -16,6 +16,7 @@ use crate::interrupt::Priority; use crate::pac; use crate::pac::gpdma::vals; +/// GPDMA transfer options. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -113,10 +114,13 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, in } } +/// DMA request type alias. (also known as DMA channel number in some chips) pub type Request = u8; +/// DMA channel. #[cfg(dmamux)] pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +/// DMA channel. #[cfg(not(dmamux))] pub trait Channel: sealed::Channel + Peripheral

+ 'static {} @@ -131,12 +135,14 @@ pub(crate) mod sealed { } } +/// DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, } impl<'a, C: Channel> Transfer<'a, C> { + /// Create a new read DMA transfer (peripheral to memory). pub unsafe fn new_read( channel: impl Peripheral

+ 'a, request: Request, @@ -147,6 +153,7 @@ impl<'a, C: Channel> Transfer<'a, C> { Self::new_read_raw(channel, request, peri_addr, buf, options) } + /// Create a new read DMA transfer (peripheral to memory), using raw pointers. pub unsafe fn new_read_raw( channel: impl Peripheral

+ 'a, request: Request, @@ -172,6 +179,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ) } + /// Create a new write DMA transfer (memory to peripheral). pub unsafe fn new_write( channel: impl Peripheral

+ 'a, request: Request, @@ -182,6 +190,7 @@ impl<'a, C: Channel> Transfer<'a, C> { Self::new_write_raw(channel, request, buf, peri_addr, options) } + /// Create a new write DMA transfer (memory to peripheral), using raw pointers. pub unsafe fn new_write_raw( channel: impl Peripheral

+ 'a, request: Request, @@ -207,6 +216,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ) } + /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. pub unsafe fn new_write_repeated( channel: impl Peripheral

+ 'a, request: Request, @@ -297,6 +307,9 @@ impl<'a, C: Channel> Transfer<'a, C> { this } + /// Request the transfer to stop. + /// + /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. pub fn request_stop(&mut self) { let ch = self.channel.regs().ch(self.channel.num()); ch.cr().modify(|w| { @@ -304,6 +317,10 @@ impl<'a, C: Channel> Transfer<'a, C> { }) } + /// Return whether this transfer is still running. + /// + /// If this returns `false`, it can be because either the transfer finished, or + /// it was requested to stop early with [`request_stop`](Self::request_stop). pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); let sr = ch.sr().read(); @@ -317,6 +334,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ch.br1().read().bndt() } + /// Blocking wait until the transfer finishes. pub fn blocking_wait(mut self) { while self.is_running() {} diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 4006dee19..663a7f59d 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,3 +1,5 @@ +//! Inter-Process Communication Controller (IPCC) + use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -41,6 +43,7 @@ impl interrupt::typelevel::Handler for Receive } } +/// TX interrupt handler. pub struct TransmitInterruptHandler {} impl interrupt::typelevel::Handler for TransmitInterruptHandler { @@ -72,6 +75,7 @@ impl interrupt::typelevel::Handler for Transmi } } +/// IPCC config. #[non_exhaustive] #[derive(Clone, Copy, Default)] pub struct Config { @@ -79,6 +83,8 @@ pub struct Config { // reserved for future use } +/// Channel. +#[allow(missing_docs)] #[derive(Debug, Clone, Copy)] #[repr(C)] pub enum IpccChannel { @@ -90,9 +96,11 @@ pub enum IpccChannel { Channel6 = 5, } +/// IPCC driver. pub struct Ipcc; impl Ipcc { + /// Enable IPCC. pub fn enable(_config: Config) { IPCC::enable_and_reset(); IPCC::set_cpu2(true); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 4952d26eb..207f7ed8f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(test), no_std)] #![allow(async_fn_in_trait)] +#![warn(missing_docs)] //! ## Feature flags #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 20d8f9045..a41c40eba 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -1,50 +1,53 @@ -/// The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating -/// to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which -/// can use knowledge of which peripherals are currently blocked upon to transparently and safely -/// enter such low-power modes (currently, only `STOP2`) when idle. -/// -/// The executor determines which peripherals are active by their RCC state; consequently, -/// low-power states can only be entered if all peripherals have been `drop`'d. There are a few -/// exceptions to this rule: -/// -/// * `GPIO` -/// * `RCC` -/// -/// Since entering and leaving low-power modes typically incurs a significant latency, the -/// low-power executor will only attempt to enter when the next timer event is at least -/// [`time_driver::MIN_STOP_PAUSE`] in the future. -/// -/// Currently there is no macro analogous to `embassy_executor::main` for this executor; -/// consequently one must define their entrypoint manually. Moveover, you must relinquish control -/// of the `RTC` peripheral to the executor. This will typically look like -/// -/// ```rust,no_run -/// use embassy_executor::Spawner; -/// use embassy_stm32::low_power::Executor; -/// use embassy_stm32::rtc::{Rtc, RtcConfig}; -/// use static_cell::make_static; -/// -/// #[cortex_m_rt::entry] -/// fn main() -> ! { -/// Executor::take().run(|spawner| { -/// unwrap!(spawner.spawn(async_main(spawner))); -/// }); -/// } -/// -/// #[embassy_executor::task] -/// async fn async_main(spawner: Spawner) { -/// // initialize the platform... -/// let mut config = embassy_stm32::Config::default(); -/// let p = embassy_stm32::init(config); -/// -/// // give the RTC to the executor... -/// let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); -/// let rtc = make_static!(rtc); -/// embassy_stm32::low_power::stop_with_rtc(rtc); -/// -/// // your application here... -/// } -/// ``` +//! Low-power support. +//! +//! The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating +//! to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which +//! can use knowledge of which peripherals are currently blocked upon to transparently and safely +//! enter such low-power modes (currently, only `STOP2`) when idle. +//! +//! The executor determines which peripherals are active by their RCC state; consequently, +//! low-power states can only be entered if all peripherals have been `drop`'d. There are a few +//! exceptions to this rule: +//! +//! * `GPIO` +//! * `RCC` +//! +//! Since entering and leaving low-power modes typically incurs a significant latency, the +//! low-power executor will only attempt to enter when the next timer event is at least +//! [`time_driver::MIN_STOP_PAUSE`] in the future. +//! +//! Currently there is no macro analogous to `embassy_executor::main` for this executor; +//! consequently one must define their entrypoint manually. Moveover, you must relinquish control +//! of the `RTC` peripheral to the executor. This will typically look like +//! +//! ```rust,no_run +//! use embassy_executor::Spawner; +//! use embassy_stm32::low_power::Executor; +//! use embassy_stm32::rtc::{Rtc, RtcConfig}; +//! use static_cell::make_static; +//! +//! #[cortex_m_rt::entry] +//! fn main() -> ! { +//! Executor::take().run(|spawner| { +//! unwrap!(spawner.spawn(async_main(spawner))); +//! }); +//! } +//! +//! #[embassy_executor::task] +//! async fn async_main(spawner: Spawner) { +//! // initialize the platform... +//! let mut config = embassy_stm32::Config::default(); +//! let p = embassy_stm32::init(config); +//! +//! // give the RTC to the executor... +//! let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); +//! let rtc = make_static!(rtc); +//! embassy_stm32::low_power::stop_with_rtc(rtc); +//! +//! // your application here... +//! } +//! ``` + use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; @@ -64,6 +67,7 @@ static mut EXECUTOR: Option = None; foreach_interrupt! { (RTC, rtc, $block:ident, WKUP, $irq:ident) => { #[interrupt] + #[allow(non_snake_case)] unsafe fn $irq() { EXECUTOR.as_mut().unwrap().on_wakeup_irq(); } @@ -75,10 +79,15 @@ pub(crate) unsafe fn on_wakeup_irq() { EXECUTOR.as_mut().unwrap().on_wakeup_irq(); } +/// Configure STOP mode with RTC. pub fn stop_with_rtc(rtc: &'static Rtc) { unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) } +/// Get whether the core is ready to enter the given stop mode. +/// +/// This will return false if some peripheral driver is in use that +/// prevents entering the given stop mode. pub fn stop_ready(stop_mode: StopMode) -> bool { match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { Some(StopMode::Stop2) => true, @@ -87,10 +96,13 @@ pub fn stop_ready(stop_mode: StopMode) -> bool { } } +/// Available stop modes. #[non_exhaustive] #[derive(PartialEq)] pub enum StopMode { + /// STOP 1 Stop1, + /// STOP 2 Stop2, } diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index e1eb031d1..df8a78bcc 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -1,9 +1,12 @@ +//! Operational Amplifier (OPAMP) #![macro_use] use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::Peripheral; +/// Gain +#[allow(missing_docs)] #[derive(Clone, Copy)] pub enum OpAmpGain { Mul1, @@ -13,6 +16,8 @@ pub enum OpAmpGain { Mul16, } +/// Speed +#[allow(missing_docs)] #[derive(Clone, Copy)] pub enum OpAmpSpeed { Normal, @@ -180,6 +185,7 @@ impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { } } +/// Opamp instance trait. pub trait Instance: sealed::Instance + 'static {} pub(crate) mod sealed { @@ -198,8 +204,11 @@ pub(crate) mod sealed { pub trait OutputPin {} } +/// Non-inverting pin trait. pub trait NonInvertingPin: sealed::NonInvertingPin {} +/// Inverting pin trait. pub trait InvertingPin: sealed::InvertingPin {} +/// Output pin trait. pub trait OutputPin: sealed::OutputPin {} macro_rules! impl_opamp_external_output { diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 6ee89a922..ca641f352 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -80,6 +80,7 @@ impl<'d, T: Instance> Rng<'d, T> { let _ = self.next_u32(); } + /// Reset the RNG. #[cfg(not(rng_v1))] pub fn reset(&mut self) { T::regs().cr().write(|reg| { diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ab142053a..10006baff 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -293,6 +293,7 @@ pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { #[cfg(sdmmc_v1)] impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { + /// Create a new SDMMC driver, with 1 data lane. pub fn new_1bit( sdmmc: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, @@ -327,6 +328,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { ) } + /// Create a new SDMMC driver, with 4 data lanes. pub fn new_4bit( sdmmc: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index d0b289462..4debd4e54 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -1,3 +1,5 @@ +//! Universal Serial Bus (USB) + use crate::interrupt; use crate::rcc::RccPeripheral; @@ -10,7 +12,9 @@ pub(crate) mod sealed { } } +/// USB instance trait. pub trait Instance: sealed::Instance + RccPeripheral + 'static { + /// Interrupt for this USB instance. type Interrupt: interrupt::typelevel::Interrupt; } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 295dc9198..a8aebfe1f 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -244,6 +244,7 @@ struct EndpointData { used_out: bool, } +/// USB driver. pub struct Driver<'d, T: Instance> { phantom: PhantomData<&'d mut T>, alloc: [EndpointData; EP_COUNT], @@ -251,6 +252,7 @@ pub struct Driver<'d, T: Instance> { } impl<'d, T: Instance> Driver<'d, T> { + /// Create a new USB driver. pub fn new( _usb: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, @@ -465,6 +467,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { } } +/// USB bus. pub struct Bus<'d, T: Instance> { phantom: PhantomData<&'d mut T>, ep_types: [EpType; EP_COUNT - 1], @@ -640,6 +643,7 @@ trait Dir { fn waker(i: usize) -> &'static AtomicWaker; } +/// Marker type for the "IN" direction. pub enum In {} impl Dir for In { fn dir() -> Direction { @@ -652,6 +656,7 @@ impl Dir for In { } } +/// Marker type for the "OUT" direction. pub enum Out {} impl Dir for Out { fn dir() -> Direction { @@ -664,6 +669,7 @@ impl Dir for Out { } } +/// USB endpoint. pub struct Endpoint<'d, T: Instance, D> { _phantom: PhantomData<(&'d mut T, D)>, info: EndpointInfo, @@ -813,6 +819,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { } } +/// USB control pipe. pub struct ControlPipe<'d, T: Instance> { _phantom: PhantomData<&'d mut T>, max_packet_size: u16, diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 1abd031dd..0649e684b 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -20,7 +20,9 @@ pub(crate) mod sealed { } } +/// USB OTG instance. pub trait Instance: sealed::Instance + RccPeripheral { + /// Interrupt for this USB OTG instance. type Interrupt: interrupt::typelevel::Interrupt; } diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index ba77bfb16..190fb274f 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -204,6 +204,7 @@ pub enum PhyType { } impl PhyType { + /// Get whether this PHY is any of the internal types. pub fn internal(&self) -> bool { match self { PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true, @@ -211,6 +212,7 @@ impl PhyType { } } + /// Get whether this PHY is any of the high-speed types. pub fn high_speed(&self) -> bool { match self { PhyType::InternalFullSpeed => false, @@ -218,7 +220,7 @@ impl PhyType { } } - pub fn to_dspd(&self) -> vals::Dspd { + fn to_dspd(&self) -> vals::Dspd { match self { PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL, PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED, @@ -230,6 +232,7 @@ impl PhyType { /// Indicates that [State::ep_out_buffers] is empty. const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX; +/// USB OTG driver state. pub struct State { /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true. ep0_setup_data: UnsafeCell<[u8; 8]>, @@ -247,6 +250,7 @@ unsafe impl Send for State {} unsafe impl Sync for State {} impl State { + /// Create a new State. pub const fn new() -> Self { const NEW_AW: AtomicWaker = AtomicWaker::new(); const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); @@ -271,6 +275,7 @@ struct EndpointData { fifo_size_words: u16, } +/// USB driver config. #[non_exhaustive] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Config { @@ -297,6 +302,7 @@ impl Default for Config { } } +/// USB driver. pub struct Driver<'d, T: Instance> { config: Config, phantom: PhantomData<&'d mut T>, @@ -527,6 +533,7 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { } } +/// USB bus. pub struct Bus<'d, T: Instance> { config: Config, phantom: PhantomData<&'d mut T>, @@ -1092,6 +1099,7 @@ trait Dir { fn dir() -> Direction; } +/// Marker type for the "IN" direction. pub enum In {} impl Dir for In { fn dir() -> Direction { @@ -1099,6 +1107,7 @@ impl Dir for In { } } +/// Marker type for the "OUT" direction. pub enum Out {} impl Dir for Out { fn dir() -> Direction { @@ -1106,6 +1115,7 @@ impl Dir for Out { } } +/// USB endpoint. pub struct Endpoint<'d, T: Instance, D> { _phantom: PhantomData<(&'d mut T, D)>, info: EndpointInfo, @@ -1299,6 +1309,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { } } +/// USB control pipe. pub struct ControlPipe<'d, T: Instance> { _phantom: PhantomData<&'d mut T>, max_packet_size: u16, diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 5751a9ff3..dc701ef64 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -6,6 +6,7 @@ use stm32_metapac::iwdg::vals::{Key, Pr}; use crate::rcc::LSI_FREQ; +/// Independent watchdog (IWDG) driver. pub struct IndependentWatchdog<'d, T: Instance> { wdg: PhantomData<&'d mut T>, } @@ -64,10 +65,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { IndependentWatchdog { wdg: PhantomData } } + /// Unleash (start) the watchdog. pub fn unleash(&mut self) { T::regs().kr().write(|w| w.set_key(Key::START)); } + /// Pet (reload, refresh) the watchdog. pub fn pet(&mut self) { T::regs().kr().write(|w| w.set_key(Key::RESET)); } @@ -79,6 +82,7 @@ mod sealed { } } +/// IWDG instance trait. pub trait Instance: sealed::Instance {} foreach_peripheral!(