diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-12-19 17:35:38 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-12-19 17:35:38 +0100 |
| commit | 189b15c426a3a9ef7d4024ba7e5de6a255f88ee7 (patch) | |
| tree | 50a5c64c14cd2b920bc99ebcbd7fdf276ebe0ae0 | |
| parent | 41c3c26beb5d9ef683073c257c1269307be6ad43 (diff) | |
stm32/timer: docs.
| -rw-r--r-- | embassy-stm32/src/hrtim/mod.rs | 16 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 25 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/complementary_pwm.rs | 31 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 158 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/qei.rs | 23 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 45 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/ws2812_pwm_dma.rs | 2 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/low_level_timer_api.rs | 10 |
8 files changed, 248 insertions, 62 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 { | |||
| 68 | pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {} | 68 | pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {} |
| 69 | 69 | ||
| 70 | /// HRTIM PWM pin. | 70 | /// HRTIM PWM pin. |
| 71 | pub struct PwmPin<'d, Perip, Channel> { | 71 | pub struct PwmPin<'d, T, C> { |
| 72 | _pin: PeripheralRef<'d, AnyPin>, | 72 | _pin: PeripheralRef<'d, AnyPin>, |
| 73 | phantom: PhantomData<(Perip, Channel)>, | 73 | phantom: PhantomData<(T, C)>, |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | /// HRTIM complementary PWM pin. | 76 | /// HRTIM complementary PWM pin. |
| 77 | pub struct ComplementaryPwmPin<'d, Perip, Channel> { | 77 | pub struct ComplementaryPwmPin<'d, T, C> { |
| 78 | _pin: PeripheralRef<'d, AnyPin>, | 78 | _pin: PeripheralRef<'d, AnyPin>, |
| 79 | phantom: PhantomData<(Perip, Channel)>, | 79 | phantom: PhantomData<(T, C)>, |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | macro_rules! advanced_channel_impl { | 82 | macro_rules! advanced_channel_impl { |
| 83 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { | 83 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { |
| 84 | impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel<Perip>> { | 84 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { |
| 85 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 85 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 86 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self { | 86 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self { |
| 87 | into_ref!(pin); | 87 | into_ref!(pin); |
| 88 | critical_section::with(|_| { | 88 | critical_section::with(|_| { |
| 89 | pin.set_low(); | 89 | pin.set_low(); |
| @@ -98,9 +98,9 @@ macro_rules! advanced_channel_impl { | |||
| 98 | } | 98 | } |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel<Perip>> { | 101 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { |
| 102 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] | 102 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] |
| 103 | pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self { | 103 | pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<T>> + 'd) -> Self { |
| 104 | into_ref!(pin); | 104 | into_ref!(pin); |
| 105 | critical_section::with(|_| { | 105 | critical_section::with(|_| { |
| 106 | pin.set_low(); | 106 | 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 { | |||
| 79 | #![allow(dead_code)] | 79 | #![allow(dead_code)] |
| 80 | #![allow(unused_imports)] | 80 | #![allow(unused_imports)] |
| 81 | #![allow(non_snake_case)] | 81 | #![allow(non_snake_case)] |
| 82 | #![allow(missing_docs)] | ||
| 82 | 83 | ||
| 83 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); | 84 | include!(concat!(env!("OUT_DIR"), "/_generated.rs")); |
| 84 | } | 85 | } |
| @@ -149,15 +150,33 @@ use crate::interrupt::Priority; | |||
| 149 | pub use crate::pac::NVIC_PRIO_BITS; | 150 | pub use crate::pac::NVIC_PRIO_BITS; |
| 150 | use crate::rcc::sealed::RccPeripheral; | 151 | use crate::rcc::sealed::RccPeripheral; |
| 151 | 152 | ||
| 153 | /// `embassy-stm32` global configuration. | ||
| 152 | #[non_exhaustive] | 154 | #[non_exhaustive] |
| 153 | pub struct Config { | 155 | pub struct Config { |
| 156 | /// RCC config. | ||
| 154 | pub rcc: rcc::Config, | 157 | pub rcc: rcc::Config, |
| 158 | |||
| 159 | /// Enable debug during sleep. | ||
| 160 | /// | ||
| 161 | /// May incrase power consumption. Defaults to true. | ||
| 155 | #[cfg(dbgmcu)] | 162 | #[cfg(dbgmcu)] |
| 156 | pub enable_debug_during_sleep: bool, | 163 | pub enable_debug_during_sleep: bool, |
| 164 | |||
| 165 | /// BDMA interrupt priority. | ||
| 166 | /// | ||
| 167 | /// Defaults to P0 (highest). | ||
| 157 | #[cfg(bdma)] | 168 | #[cfg(bdma)] |
| 158 | pub bdma_interrupt_priority: Priority, | 169 | pub bdma_interrupt_priority: Priority, |
| 170 | |||
| 171 | /// DMA interrupt priority. | ||
| 172 | /// | ||
| 173 | /// Defaults to P0 (highest). | ||
| 159 | #[cfg(dma)] | 174 | #[cfg(dma)] |
| 160 | pub dma_interrupt_priority: Priority, | 175 | pub dma_interrupt_priority: Priority, |
| 176 | |||
| 177 | /// GPDMA interrupt priority. | ||
| 178 | /// | ||
| 179 | /// Defaults to P0 (highest). | ||
| 161 | #[cfg(gpdma)] | 180 | #[cfg(gpdma)] |
| 162 | pub gpdma_interrupt_priority: Priority, | 181 | pub gpdma_interrupt_priority: Priority, |
| 163 | } | 182 | } |
| @@ -178,7 +197,11 @@ impl Default for Config { | |||
| 178 | } | 197 | } |
| 179 | } | 198 | } |
| 180 | 199 | ||
| 181 | /// Initialize embassy. | 200 | /// Initialize the `embassy-stm32` HAL with the provided configuration. |
| 201 | /// | ||
| 202 | /// This returns the peripheral singletons that can be used for creating drivers. | ||
| 203 | /// | ||
| 204 | /// This should only be called once at startup, otherwise it panics. | ||
| 182 | pub fn init(config: Config) -> Peripherals { | 205 | pub fn init(config: Config) -> Peripherals { |
| 183 | critical_section::with(|cs| { | 206 | critical_section::with(|cs| { |
| 184 | let p = Peripherals::take_with_cs(cs); | 207 | 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}; | |||
| 13 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 14 | use crate::Peripheral; | 14 | use crate::Peripheral; |
| 15 | 15 | ||
| 16 | pub struct ComplementaryPwmPin<'d, Perip, Channel> { | 16 | /// Complementary PWM pin wrapper. |
| 17 | /// | ||
| 18 | /// This wraps a pin to make it usable with PWM. | ||
| 19 | pub struct ComplementaryPwmPin<'d, T, C> { | ||
| 17 | _pin: PeripheralRef<'d, AnyPin>, | 20 | _pin: PeripheralRef<'d, AnyPin>, |
| 18 | phantom: PhantomData<(Perip, Channel)>, | 21 | phantom: PhantomData<(T, C)>, |
| 19 | } | 22 | } |
| 20 | 23 | ||
| 21 | macro_rules! complementary_channel_impl { | 24 | macro_rules! complementary_channel_impl { |
| 22 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 25 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
| 23 | impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { | 26 | impl<'d, T: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> { |
| 24 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd, output_type: OutputType) -> Self { | 27 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] |
| 28 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self { | ||
| 25 | into_ref!(pin); | 29 | into_ref!(pin); |
| 26 | critical_section::with(|_| { | 30 | critical_section::with(|_| { |
| 27 | pin.set_low(); | 31 | pin.set_low(); |
| @@ -43,11 +47,13 @@ complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin); | |||
| 43 | complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); | 47 | complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); |
| 44 | complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); | 48 | complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); |
| 45 | 49 | ||
| 50 | /// PWM driver with support for standard and complementary outputs. | ||
| 46 | pub struct ComplementaryPwm<'d, T> { | 51 | pub struct ComplementaryPwm<'d, T> { |
| 47 | inner: PeripheralRef<'d, T>, | 52 | inner: PeripheralRef<'d, T>, |
| 48 | } | 53 | } |
| 49 | 54 | ||
| 50 | impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { | 55 | impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { |
| 56 | /// Create a new complementary PWM driver. | ||
| 51 | pub fn new( | 57 | pub fn new( |
| 52 | tim: impl Peripheral<P = T> + 'd, | 58 | tim: impl Peripheral<P = T> + 'd, |
| 53 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 59 | _ch1: Option<PwmPin<'d, T, Ch1>>, |
| @@ -72,7 +78,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { | |||
| 72 | let mut this = Self { inner: tim }; | 78 | let mut this = Self { inner: tim }; |
| 73 | 79 | ||
| 74 | this.inner.set_counting_mode(counting_mode); | 80 | this.inner.set_counting_mode(counting_mode); |
| 75 | this.set_freq(freq); | 81 | this.set_frequency(freq); |
| 76 | this.inner.start(); | 82 | this.inner.start(); |
| 77 | 83 | ||
| 78 | this.inner.enable_outputs(); | 84 | this.inner.enable_outputs(); |
| @@ -88,17 +94,23 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { | |||
| 88 | this | 94 | this |
| 89 | } | 95 | } |
| 90 | 96 | ||
| 97 | /// Enable the given channel. | ||
| 91 | pub fn enable(&mut self, channel: Channel) { | 98 | pub fn enable(&mut self, channel: Channel) { |
| 92 | self.inner.enable_channel(channel, true); | 99 | self.inner.enable_channel(channel, true); |
| 93 | self.inner.enable_complementary_channel(channel, true); | 100 | self.inner.enable_complementary_channel(channel, true); |
| 94 | } | 101 | } |
| 95 | 102 | ||
| 103 | /// Disable the given channel. | ||
| 96 | pub fn disable(&mut self, channel: Channel) { | 104 | pub fn disable(&mut self, channel: Channel) { |
| 97 | self.inner.enable_complementary_channel(channel, false); | 105 | self.inner.enable_complementary_channel(channel, false); |
| 98 | self.inner.enable_channel(channel, false); | 106 | self.inner.enable_channel(channel, false); |
| 99 | } | 107 | } |
| 100 | 108 | ||
| 101 | pub fn set_freq(&mut self, freq: Hertz) { | 109 | /// Set PWM frequency. |
| 110 | /// | ||
| 111 | /// Note: when you call this, the max duty value changes, so you will have to | ||
| 112 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | ||
| 113 | pub fn set_frequency(&mut self, freq: Hertz) { | ||
| 102 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 114 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 103 | 2u8 | 115 | 2u8 |
| 104 | } else { | 116 | } else { |
| @@ -107,15 +119,22 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { | |||
| 107 | self.inner.set_frequency(freq * multiplier); | 119 | self.inner.set_frequency(freq * multiplier); |
| 108 | } | 120 | } |
| 109 | 121 | ||
| 122 | /// Get max duty value. | ||
| 123 | /// | ||
| 124 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 110 | pub fn get_max_duty(&self) -> u16 { | 125 | pub fn get_max_duty(&self) -> u16 { |
| 111 | self.inner.get_max_compare_value() + 1 | 126 | self.inner.get_max_compare_value() + 1 |
| 112 | } | 127 | } |
| 113 | 128 | ||
| 129 | /// Set the duty for a given channel. | ||
| 130 | /// | ||
| 131 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 114 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { | 132 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { |
| 115 | assert!(duty <= self.get_max_duty()); | 133 | assert!(duty <= self.get_max_duty()); |
| 116 | self.inner.set_compare_value(channel, duty) | 134 | self.inner.set_compare_value(channel, duty) |
| 117 | } | 135 | } |
| 118 | 136 | ||
| 137 | /// Set the output polarity for a given channel. | ||
| 119 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | 138 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { |
| 120 | self.inner.set_output_polarity(channel, polarity); | 139 | self.inner.set_output_polarity(channel, polarity); |
| 121 | self.inner.set_complementary_output_polarity(channel, polarity); | 140 | 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 { | |||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | pub(crate) mod sealed { | 19 | pub(crate) mod sealed { |
| 20 | |||
| 21 | use super::*; | 20 | use super::*; |
| 21 | |||
| 22 | /// Basic 16-bit timer instance. | ||
| 22 | pub trait Basic16bitInstance: RccPeripheral { | 23 | pub trait Basic16bitInstance: RccPeripheral { |
| 24 | /// Interrupt for this timer. | ||
| 23 | type Interrupt: interrupt::typelevel::Interrupt; | 25 | type Interrupt: interrupt::typelevel::Interrupt; |
| 24 | 26 | ||
| 27 | /// Get access to the basic 16bit timer registers. | ||
| 28 | /// | ||
| 29 | /// Note: This works even if the timer is more capable, because registers | ||
| 30 | /// for the less capable timers are a subset. This allows writing a driver | ||
| 31 | /// for a given set of capabilities, and having it transparently work with | ||
| 32 | /// more capable timers. | ||
| 25 | fn regs() -> crate::pac::timer::TimBasic; | 33 | fn regs() -> crate::pac::timer::TimBasic; |
| 26 | 34 | ||
| 35 | /// Start the timer. | ||
| 27 | fn start(&mut self) { | 36 | fn start(&mut self) { |
| 28 | Self::regs().cr1().modify(|r| r.set_cen(true)); | 37 | Self::regs().cr1().modify(|r| r.set_cen(true)); |
| 29 | } | 38 | } |
| 30 | 39 | ||
| 40 | /// Stop the timer. | ||
| 31 | fn stop(&mut self) { | 41 | fn stop(&mut self) { |
| 32 | Self::regs().cr1().modify(|r| r.set_cen(false)); | 42 | Self::regs().cr1().modify(|r| r.set_cen(false)); |
| 33 | } | 43 | } |
| @@ -63,6 +73,9 @@ pub(crate) mod sealed { | |||
| 63 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); | 73 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); |
| 64 | } | 74 | } |
| 65 | 75 | ||
| 76 | /// Clear update interrupt. | ||
| 77 | /// | ||
| 78 | /// Returns whether the update interrupt flag was set. | ||
| 66 | fn clear_update_interrupt(&mut self) -> bool { | 79 | fn clear_update_interrupt(&mut self) -> bool { |
| 67 | let regs = Self::regs(); | 80 | let regs = Self::regs(); |
| 68 | let sr = regs.sr().read(); | 81 | let sr = regs.sr().read(); |
| @@ -76,14 +89,17 @@ pub(crate) mod sealed { | |||
| 76 | } | 89 | } |
| 77 | } | 90 | } |
| 78 | 91 | ||
| 92 | /// Enable/disable the update interrupt. | ||
| 79 | fn enable_update_interrupt(&mut self, enable: bool) { | 93 | fn enable_update_interrupt(&mut self, enable: bool) { |
| 80 | Self::regs().dier().write(|r| r.set_uie(enable)); | 94 | Self::regs().dier().write(|r| r.set_uie(enable)); |
| 81 | } | 95 | } |
| 82 | 96 | ||
| 97 | /// Enable/disable autoreload preload. | ||
| 83 | fn set_autoreload_preload(&mut self, enable: bool) { | 98 | fn set_autoreload_preload(&mut self, enable: bool) { |
| 84 | Self::regs().cr1().modify(|r| r.set_arpe(enable)); | 99 | Self::regs().cr1().modify(|r| r.set_arpe(enable)); |
| 85 | } | 100 | } |
| 86 | 101 | ||
| 102 | /// Get the timer frequency. | ||
| 87 | fn get_frequency(&self) -> Hertz { | 103 | fn get_frequency(&self) -> Hertz { |
| 88 | let timer_f = Self::frequency(); | 104 | let timer_f = Self::frequency(); |
| 89 | 105 | ||
| @@ -95,9 +111,17 @@ pub(crate) mod sealed { | |||
| 95 | } | 111 | } |
| 96 | } | 112 | } |
| 97 | 113 | ||
| 114 | /// Gneral-purpose 16-bit timer instance. | ||
| 98 | pub trait GeneralPurpose16bitInstance: Basic16bitInstance { | 115 | pub trait GeneralPurpose16bitInstance: Basic16bitInstance { |
| 116 | /// Get access to the general purpose 16bit timer registers. | ||
| 117 | /// | ||
| 118 | /// Note: This works even if the timer is more capable, because registers | ||
| 119 | /// for the less capable timers are a subset. This allows writing a driver | ||
| 120 | /// for a given set of capabilities, and having it transparently work with | ||
| 121 | /// more capable timers. | ||
| 99 | fn regs_gp16() -> crate::pac::timer::TimGp16; | 122 | fn regs_gp16() -> crate::pac::timer::TimGp16; |
| 100 | 123 | ||
| 124 | /// Set counting mode. | ||
| 101 | fn set_counting_mode(&mut self, mode: CountingMode) { | 125 | fn set_counting_mode(&mut self, mode: CountingMode) { |
| 102 | let (cms, dir) = mode.into(); | 126 | let (cms, dir) = mode.into(); |
| 103 | 127 | ||
| @@ -110,19 +134,29 @@ pub(crate) mod sealed { | |||
| 110 | Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) | 134 | Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) |
| 111 | } | 135 | } |
| 112 | 136 | ||
| 137 | /// Get counting mode. | ||
| 113 | fn get_counting_mode(&self) -> CountingMode { | 138 | fn get_counting_mode(&self) -> CountingMode { |
| 114 | let cr1 = Self::regs_gp16().cr1().read(); | 139 | let cr1 = Self::regs_gp16().cr1().read(); |
| 115 | (cr1.cms(), cr1.dir()).into() | 140 | (cr1.cms(), cr1.dir()).into() |
| 116 | } | 141 | } |
| 117 | 142 | ||
| 143 | /// Set clock divider. | ||
| 118 | fn set_clock_division(&mut self, ckd: vals::Ckd) { | 144 | fn set_clock_division(&mut self, ckd: vals::Ckd) { |
| 119 | Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); | 145 | Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); |
| 120 | } | 146 | } |
| 121 | } | 147 | } |
| 122 | 148 | ||
| 149 | /// Gneral-purpose 32-bit timer instance. | ||
| 123 | pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { | 150 | pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { |
| 151 | /// Get access to the general purpose 32bit timer registers. | ||
| 152 | /// | ||
| 153 | /// Note: This works even if the timer is more capable, because registers | ||
| 154 | /// for the less capable timers are a subset. This allows writing a driver | ||
| 155 | /// for a given set of capabilities, and having it transparently work with | ||
| 156 | /// more capable timers. | ||
| 124 | fn regs_gp32() -> crate::pac::timer::TimGp32; | 157 | fn regs_gp32() -> crate::pac::timer::TimGp32; |
| 125 | 158 | ||
| 159 | /// Set timer frequency. | ||
| 126 | fn set_frequency(&mut self, frequency: Hertz) { | 160 | fn set_frequency(&mut self, frequency: Hertz) { |
| 127 | let f = frequency.0; | 161 | let f = frequency.0; |
| 128 | assert!(f > 0); | 162 | assert!(f > 0); |
| @@ -140,6 +174,7 @@ pub(crate) mod sealed { | |||
| 140 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); | 174 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); |
| 141 | } | 175 | } |
| 142 | 176 | ||
| 177 | /// Get timer frequency. | ||
| 143 | fn get_frequency(&self) -> Hertz { | 178 | fn get_frequency(&self) -> Hertz { |
| 144 | let timer_f = Self::frequency(); | 179 | let timer_f = Self::frequency(); |
| 145 | 180 | ||
| @@ -151,141 +186,177 @@ pub(crate) mod sealed { | |||
| 151 | } | 186 | } |
| 152 | } | 187 | } |
| 153 | 188 | ||
| 189 | /// Advanced control timer instance. | ||
| 154 | pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { | 190 | pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { |
| 191 | /// Get access to the advanced timer registers. | ||
| 155 | fn regs_advanced() -> crate::pac::timer::TimAdv; | 192 | fn regs_advanced() -> crate::pac::timer::TimAdv; |
| 156 | } | 193 | } |
| 157 | 194 | ||
| 195 | /// Capture/Compare 16-bit timer instance. | ||
| 158 | pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { | 196 | pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { |
| 197 | /// Set input capture filter. | ||
| 159 | fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { | 198 | fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { |
| 160 | let raw_channel = channel.raw(); | 199 | let raw_channel = channel.index(); |
| 161 | Self::regs_gp16() | 200 | Self::regs_gp16() |
| 162 | .ccmr_input(raw_channel / 2) | 201 | .ccmr_input(raw_channel / 2) |
| 163 | .modify(|r| r.set_icf(raw_channel % 2, icf)); | 202 | .modify(|r| r.set_icf(raw_channel % 2, icf)); |
| 164 | } | 203 | } |
| 165 | 204 | ||
| 205 | /// Clear input interrupt. | ||
| 166 | fn clear_input_interrupt(&mut self, channel: Channel) { | 206 | fn clear_input_interrupt(&mut self, channel: Channel) { |
| 167 | Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.raw(), false)); | 207 | Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); |
| 168 | } | 208 | } |
| 169 | 209 | ||
| 210 | /// Enable input interrupt. | ||
| 170 | fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { | 211 | fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { |
| 171 | Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.raw(), enable)); | 212 | Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); |
| 172 | } | 213 | } |
| 214 | |||
| 215 | /// Set input capture prescaler. | ||
| 173 | fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { | 216 | fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { |
| 174 | let raw_channel = channel.raw(); | 217 | let raw_channel = channel.index(); |
| 175 | Self::regs_gp16() | 218 | Self::regs_gp16() |
| 176 | .ccmr_input(raw_channel / 2) | 219 | .ccmr_input(raw_channel / 2) |
| 177 | .modify(|r| r.set_icpsc(raw_channel % 2, factor)); | 220 | .modify(|r| r.set_icpsc(raw_channel % 2, factor)); |
| 178 | } | 221 | } |
| 179 | 222 | ||
| 223 | /// Set input TI selection. | ||
| 180 | fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { | 224 | fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { |
| 181 | let raw_channel = channel.raw(); | 225 | let raw_channel = channel.index(); |
| 182 | Self::regs_gp16() | 226 | Self::regs_gp16() |
| 183 | .ccmr_input(raw_channel / 2) | 227 | .ccmr_input(raw_channel / 2) |
| 184 | .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); | 228 | .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); |
| 185 | } | 229 | } |
| 230 | |||
| 231 | /// Set input capture mode. | ||
| 186 | fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { | 232 | fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { |
| 187 | Self::regs_gp16().ccer().modify(|r| match mode { | 233 | Self::regs_gp16().ccer().modify(|r| match mode { |
| 188 | InputCaptureMode::Rising => { | 234 | InputCaptureMode::Rising => { |
| 189 | r.set_ccnp(channel.raw(), false); | 235 | r.set_ccnp(channel.index(), false); |
| 190 | r.set_ccp(channel.raw(), false); | 236 | r.set_ccp(channel.index(), false); |
| 191 | } | 237 | } |
| 192 | InputCaptureMode::Falling => { | 238 | InputCaptureMode::Falling => { |
| 193 | r.set_ccnp(channel.raw(), false); | 239 | r.set_ccnp(channel.index(), false); |
| 194 | r.set_ccp(channel.raw(), true); | 240 | r.set_ccp(channel.index(), true); |
| 195 | } | 241 | } |
| 196 | InputCaptureMode::BothEdges => { | 242 | InputCaptureMode::BothEdges => { |
| 197 | r.set_ccnp(channel.raw(), true); | 243 | r.set_ccnp(channel.index(), true); |
| 198 | r.set_ccp(channel.raw(), true); | 244 | r.set_ccp(channel.index(), true); |
| 199 | } | 245 | } |
| 200 | }); | 246 | }); |
| 201 | } | 247 | } |
| 248 | |||
| 249 | /// Enable timer outputs. | ||
| 202 | fn enable_outputs(&mut self); | 250 | fn enable_outputs(&mut self); |
| 203 | 251 | ||
| 252 | /// Set output compare mode. | ||
| 204 | fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { | 253 | fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { |
| 205 | let r = Self::regs_gp16(); | 254 | let r = Self::regs_gp16(); |
| 206 | let raw_channel: usize = channel.raw(); | 255 | let raw_channel: usize = channel.index(); |
| 207 | r.ccmr_output(raw_channel / 2) | 256 | r.ccmr_output(raw_channel / 2) |
| 208 | .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); | 257 | .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); |
| 209 | } | 258 | } |
| 210 | 259 | ||
| 260 | /// Set output polarity. | ||
| 211 | fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | 261 | fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { |
| 212 | Self::regs_gp16() | 262 | Self::regs_gp16() |
| 213 | .ccer() | 263 | .ccer() |
| 214 | .modify(|w| w.set_ccp(channel.raw(), polarity.into())); | 264 | .modify(|w| w.set_ccp(channel.index(), polarity.into())); |
| 215 | } | 265 | } |
| 216 | 266 | ||
| 267 | /// Enable/disable a channel. | ||
| 217 | fn enable_channel(&mut self, channel: Channel, enable: bool) { | 268 | fn enable_channel(&mut self, channel: Channel, enable: bool) { |
| 218 | Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.raw(), enable)); | 269 | Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); |
| 219 | } | 270 | } |
| 220 | 271 | ||
| 272 | /// Set compare value for a channel. | ||
| 221 | fn set_compare_value(&mut self, channel: Channel, value: u16) { | 273 | fn set_compare_value(&mut self, channel: Channel, value: u16) { |
| 222 | Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); | 274 | Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); |
| 223 | } | 275 | } |
| 224 | 276 | ||
| 277 | /// Get capture value for a channel. | ||
| 225 | fn get_capture_value(&mut self, channel: Channel) -> u16 { | 278 | fn get_capture_value(&mut self, channel: Channel) -> u16 { |
| 226 | Self::regs_gp16().ccr(channel.raw()).read().ccr() | 279 | Self::regs_gp16().ccr(channel.index()).read().ccr() |
| 227 | } | 280 | } |
| 228 | 281 | ||
| 282 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. | ||
| 229 | fn get_max_compare_value(&self) -> u16 { | 283 | fn get_max_compare_value(&self) -> u16 { |
| 230 | Self::regs_gp16().arr().read().arr() | 284 | Self::regs_gp16().arr().read().arr() |
| 231 | } | 285 | } |
| 232 | 286 | ||
| 287 | /// Get compare value for a channel. | ||
| 233 | fn get_compare_value(&self, channel: Channel) -> u16 { | 288 | fn get_compare_value(&self, channel: Channel) -> u16 { |
| 234 | Self::regs_gp16().ccr(channel.raw()).read().ccr() | 289 | Self::regs_gp16().ccr(channel.index()).read().ccr() |
| 235 | } | 290 | } |
| 236 | } | 291 | } |
| 237 | 292 | ||
| 293 | /// Capture/Compare 16-bit timer instance with complementary pin support. | ||
| 238 | pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance { | 294 | pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance { |
| 295 | /// Set complementary output polarity. | ||
| 239 | fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | 296 | fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { |
| 240 | Self::regs_advanced() | 297 | Self::regs_advanced() |
| 241 | .ccer() | 298 | .ccer() |
| 242 | .modify(|w| w.set_ccnp(channel.raw(), polarity.into())); | 299 | .modify(|w| w.set_ccnp(channel.index(), polarity.into())); |
| 243 | } | 300 | } |
| 244 | 301 | ||
| 302 | /// Set clock divider for the dead time. | ||
| 245 | fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { | 303 | fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { |
| 246 | Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); | 304 | Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); |
| 247 | } | 305 | } |
| 248 | 306 | ||
| 307 | /// Set dead time, as a fraction of the max duty value. | ||
| 249 | fn set_dead_time_value(&mut self, value: u8) { | 308 | fn set_dead_time_value(&mut self, value: u8) { |
| 250 | Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); | 309 | Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); |
| 251 | } | 310 | } |
| 252 | 311 | ||
| 312 | /// Enable/disable a complementary channel. | ||
| 253 | fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { | 313 | fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { |
| 254 | Self::regs_advanced() | 314 | Self::regs_advanced() |
| 255 | .ccer() | 315 | .ccer() |
| 256 | .modify(|w| w.set_ccne(channel.raw(), enable)); | 316 | .modify(|w| w.set_ccne(channel.index(), enable)); |
| 257 | } | 317 | } |
| 258 | } | 318 | } |
| 259 | 319 | ||
| 320 | /// Capture/Compare 32-bit timer instance. | ||
| 260 | pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance { | 321 | pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance { |
| 322 | /// Set comapre value for a channel. | ||
| 261 | fn set_compare_value(&mut self, channel: Channel, value: u32) { | 323 | fn set_compare_value(&mut self, channel: Channel, value: u32) { |
| 262 | Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); | 324 | Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value)); |
| 263 | } | 325 | } |
| 264 | 326 | ||
| 327 | /// Get capture value for a channel. | ||
| 265 | fn get_capture_value(&mut self, channel: Channel) -> u32 { | 328 | fn get_capture_value(&mut self, channel: Channel) -> u32 { |
| 266 | Self::regs_gp32().ccr(channel.raw()).read().ccr() | 329 | Self::regs_gp32().ccr(channel.index()).read().ccr() |
| 267 | } | 330 | } |
| 268 | 331 | ||
| 332 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. | ||
| 269 | fn get_max_compare_value(&self) -> u32 { | 333 | fn get_max_compare_value(&self) -> u32 { |
| 270 | Self::regs_gp32().arr().read().arr() | 334 | Self::regs_gp32().arr().read().arr() |
| 271 | } | 335 | } |
| 272 | 336 | ||
| 337 | /// Get compare value for a channel. | ||
| 273 | fn get_compare_value(&self, channel: Channel) -> u32 { | 338 | fn get_compare_value(&self, channel: Channel) -> u32 { |
| 274 | Self::regs_gp32().ccr(channel.raw()).read().ccr() | 339 | Self::regs_gp32().ccr(channel.index()).read().ccr() |
| 275 | } | 340 | } |
| 276 | } | 341 | } |
| 277 | } | 342 | } |
| 278 | 343 | ||
| 344 | /// Timer channel. | ||
| 279 | #[derive(Clone, Copy)] | 345 | #[derive(Clone, Copy)] |
| 280 | pub enum Channel { | 346 | pub enum Channel { |
| 347 | /// Channel 1. | ||
| 281 | Ch1, | 348 | Ch1, |
| 349 | /// Channel 2. | ||
| 282 | Ch2, | 350 | Ch2, |
| 351 | /// Channel 3. | ||
| 283 | Ch3, | 352 | Ch3, |
| 353 | /// Channel 4. | ||
| 284 | Ch4, | 354 | Ch4, |
| 285 | } | 355 | } |
| 286 | 356 | ||
| 287 | impl Channel { | 357 | impl Channel { |
| 288 | pub fn raw(&self) -> usize { | 358 | /// Get the channel index (0..3) |
| 359 | pub fn index(&self) -> usize { | ||
| 289 | match self { | 360 | match self { |
| 290 | Channel::Ch1 => 0, | 361 | Channel::Ch1 => 0, |
| 291 | Channel::Ch2 => 1, | 362 | Channel::Ch2 => 1, |
| @@ -295,17 +366,25 @@ impl Channel { | |||
| 295 | } | 366 | } |
| 296 | } | 367 | } |
| 297 | 368 | ||
| 369 | /// Input capture mode. | ||
| 298 | #[derive(Clone, Copy)] | 370 | #[derive(Clone, Copy)] |
| 299 | pub enum InputCaptureMode { | 371 | pub enum InputCaptureMode { |
| 372 | /// Rising edge only. | ||
| 300 | Rising, | 373 | Rising, |
| 374 | /// Falling edge only. | ||
| 301 | Falling, | 375 | Falling, |
| 376 | /// Both rising or falling edges. | ||
| 302 | BothEdges, | 377 | BothEdges, |
| 303 | } | 378 | } |
| 304 | 379 | ||
| 380 | /// Input TI selection. | ||
| 305 | #[derive(Clone, Copy)] | 381 | #[derive(Clone, Copy)] |
| 306 | pub enum InputTISelection { | 382 | pub enum InputTISelection { |
| 383 | /// Normal | ||
| 307 | Normal, | 384 | Normal, |
| 385 | /// Alternate | ||
| 308 | Alternate, | 386 | Alternate, |
| 387 | /// TRC | ||
| 309 | TRC, | 388 | TRC, |
| 310 | } | 389 | } |
| 311 | 390 | ||
| @@ -319,6 +398,7 @@ impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs { | |||
| 319 | } | 398 | } |
| 320 | } | 399 | } |
| 321 | 400 | ||
| 401 | /// Timer counting mode. | ||
| 322 | #[repr(u8)] | 402 | #[repr(u8)] |
| 323 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] | 403 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] |
| 324 | pub enum CountingMode { | 404 | pub enum CountingMode { |
| @@ -345,6 +425,7 @@ pub enum CountingMode { | |||
| 345 | } | 425 | } |
| 346 | 426 | ||
| 347 | impl CountingMode { | 427 | impl CountingMode { |
| 428 | /// Return whether this mode is edge-aligned (up or down). | ||
| 348 | pub fn is_edge_aligned(&self) -> bool { | 429 | pub fn is_edge_aligned(&self) -> bool { |
| 349 | match self { | 430 | match self { |
| 350 | CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true, | 431 | CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true, |
| @@ -352,6 +433,7 @@ impl CountingMode { | |||
| 352 | } | 433 | } |
| 353 | } | 434 | } |
| 354 | 435 | ||
| 436 | /// Return whether this mode is center-aligned. | ||
| 355 | pub fn is_center_aligned(&self) -> bool { | 437 | pub fn is_center_aligned(&self) -> bool { |
| 356 | match self { | 438 | match self { |
| 357 | CountingMode::CenterAlignedDownInterrupts | 439 | CountingMode::CenterAlignedDownInterrupts |
| @@ -386,16 +468,34 @@ impl From<(vals::Cms, vals::Dir)> for CountingMode { | |||
| 386 | } | 468 | } |
| 387 | } | 469 | } |
| 388 | 470 | ||
| 471 | /// Output compare mode. | ||
| 389 | #[derive(Clone, Copy)] | 472 | #[derive(Clone, Copy)] |
| 390 | pub enum OutputCompareMode { | 473 | pub enum OutputCompareMode { |
| 474 | /// The comparison between the output compare register TIMx_CCRx and | ||
| 475 | /// the counter TIMx_CNT has no effect on the outputs. | ||
| 476 | /// (this mode is used to generate a timing base). | ||
| 391 | Frozen, | 477 | Frozen, |
| 478 | /// Set channel to active level on match. OCxREF signal is forced high when the | ||
| 479 | /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). | ||
| 392 | ActiveOnMatch, | 480 | ActiveOnMatch, |
| 481 | /// Set channel to inactive level on match. OCxREF signal is forced low when the | ||
| 482 | /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). | ||
| 393 | InactiveOnMatch, | 483 | InactiveOnMatch, |
| 484 | /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx. | ||
| 394 | Toggle, | 485 | Toggle, |
| 486 | /// Force inactive level - OCxREF is forced low. | ||
| 395 | ForceInactive, | 487 | ForceInactive, |
| 488 | /// Force active level - OCxREF is forced high. | ||
| 396 | ForceActive, | 489 | ForceActive, |
| 490 | /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx | ||
| 491 | /// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as | ||
| 492 | /// TIMx_CNT>TIMx_CCRx else active (OCxREF=1). | ||
| 397 | PwmMode1, | 493 | PwmMode1, |
| 494 | /// PWM mode 2 - In upcounting, channel is inactive as long as | ||
| 495 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as | ||
| 496 | /// TIMx_CNT>TIMx_CCRx else inactive. | ||
| 398 | PwmMode2, | 497 | PwmMode2, |
| 498 | // TODO: there's more modes here depending on the chip family. | ||
| 399 | } | 499 | } |
| 400 | 500 | ||
| 401 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | 501 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { |
| @@ -413,9 +513,12 @@ impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | |||
| 413 | } | 513 | } |
| 414 | } | 514 | } |
| 415 | 515 | ||
| 516 | /// Timer output pin polarity. | ||
| 416 | #[derive(Clone, Copy)] | 517 | #[derive(Clone, Copy)] |
| 417 | pub enum OutputPolarity { | 518 | pub enum OutputPolarity { |
| 519 | /// Active high (higher duty value makes the pin spend more time high). | ||
| 418 | ActiveHigh, | 520 | ActiveHigh, |
| 521 | /// Active low (higher duty value makes the pin spend more time low). | ||
| 419 | ActiveLow, | 522 | ActiveLow, |
| 420 | } | 523 | } |
| 421 | 524 | ||
| @@ -428,24 +531,31 @@ impl From<OutputPolarity> for bool { | |||
| 428 | } | 531 | } |
| 429 | } | 532 | } |
| 430 | 533 | ||
| 534 | /// Basic 16-bit timer instance. | ||
| 431 | pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} | 535 | pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} |
| 432 | 536 | ||
| 537 | /// Gneral-purpose 16-bit timer instance. | ||
| 433 | pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} | 538 | pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} |
| 434 | 539 | ||
| 540 | /// Gneral-purpose 32-bit timer instance. | ||
| 435 | pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} | 541 | pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} |
| 436 | 542 | ||
| 543 | /// Advanced control timer instance. | ||
| 437 | pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} | 544 | pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} |
| 438 | 545 | ||
| 546 | /// Capture/Compare 16-bit timer instance. | ||
| 439 | pub trait CaptureCompare16bitInstance: | 547 | pub trait CaptureCompare16bitInstance: |
| 440 | sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static | 548 | sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static |
| 441 | { | 549 | { |
| 442 | } | 550 | } |
| 443 | 551 | ||
| 552 | /// Capture/Compare 16-bit timer instance with complementary pin support. | ||
| 444 | pub trait ComplementaryCaptureCompare16bitInstance: | 553 | pub trait ComplementaryCaptureCompare16bitInstance: |
| 445 | sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static | 554 | sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static |
| 446 | { | 555 | { |
| 447 | } | 556 | } |
| 448 | 557 | ||
| 558 | /// Capture/Compare 32-bit timer instance. | ||
| 449 | pub trait CaptureCompare32bitInstance: | 559 | pub trait CaptureCompare32bitInstance: |
| 450 | sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static | 560 | sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static |
| 451 | { | 561 | { |
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; | |||
| 9 | use crate::gpio::AnyPin; | 9 | use crate::gpio::AnyPin; |
| 10 | use crate::Peripheral; | 10 | use crate::Peripheral; |
| 11 | 11 | ||
| 12 | /// Counting direction | ||
| 12 | pub enum Direction { | 13 | pub enum Direction { |
| 14 | /// Counting up. | ||
| 13 | Upcounting, | 15 | Upcounting, |
| 16 | /// Counting down. | ||
| 14 | Downcounting, | 17 | Downcounting, |
| 15 | } | 18 | } |
| 16 | 19 | ||
| 17 | pub struct Ch1; | 20 | /// Channel 1 marker type. |
| 18 | pub struct Ch2; | 21 | pub enum Ch1 {} |
| 22 | /// Channel 2 marker type. | ||
| 23 | pub enum Ch2 {} | ||
| 19 | 24 | ||
| 20 | pub struct QeiPin<'d, Perip, Channel> { | 25 | /// Wrapper for using a pin with QEI. |
| 26 | pub struct QeiPin<'d, T, Channel> { | ||
| 21 | _pin: PeripheralRef<'d, AnyPin>, | 27 | _pin: PeripheralRef<'d, AnyPin>, |
| 22 | phantom: PhantomData<(Perip, Channel)>, | 28 | phantom: PhantomData<(T, Channel)>, |
| 23 | } | 29 | } |
| 24 | 30 | ||
| 25 | macro_rules! channel_impl { | 31 | macro_rules! channel_impl { |
| 26 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 32 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
| 27 | impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> { | 33 | impl<'d, T: CaptureCompare16bitInstance> QeiPin<'d, T, $channel> { |
| 28 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self { | 34 | #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] |
| 35 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self { | ||
| 29 | into_ref!(pin); | 36 | into_ref!(pin); |
| 30 | critical_section::with(|_| { | 37 | critical_section::with(|_| { |
| 31 | pin.set_low(); | 38 | pin.set_low(); |
| @@ -45,11 +52,13 @@ macro_rules! channel_impl { | |||
| 45 | channel_impl!(new_ch1, Ch1, Channel1Pin); | 52 | channel_impl!(new_ch1, Ch1, Channel1Pin); |
| 46 | channel_impl!(new_ch2, Ch2, Channel2Pin); | 53 | channel_impl!(new_ch2, Ch2, Channel2Pin); |
| 47 | 54 | ||
| 55 | /// Quadrature decoder driver. | ||
| 48 | pub struct Qei<'d, T> { | 56 | pub struct Qei<'d, T> { |
| 49 | _inner: PeripheralRef<'d, T>, | 57 | _inner: PeripheralRef<'d, T>, |
| 50 | } | 58 | } |
| 51 | 59 | ||
| 52 | impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { | 60 | impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { |
| 61 | /// Create a new quadrature decoder driver. | ||
| 53 | pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { | 62 | pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { |
| 54 | Self::new_inner(tim) | 63 | Self::new_inner(tim) |
| 55 | } | 64 | } |
| @@ -84,6 +93,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { | |||
| 84 | Self { _inner: tim } | 93 | Self { _inner: tim } |
| 85 | } | 94 | } |
| 86 | 95 | ||
| 96 | /// Get direction. | ||
| 87 | pub fn read_direction(&self) -> Direction { | 97 | pub fn read_direction(&self) -> Direction { |
| 88 | match T::regs_gp16().cr1().read().dir() { | 98 | match T::regs_gp16().cr1().read().dir() { |
| 89 | vals::Dir::DOWN => Direction::Downcounting, | 99 | vals::Dir::DOWN => Direction::Downcounting, |
| @@ -91,6 +101,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { | |||
| 91 | } | 101 | } |
| 92 | } | 102 | } |
| 93 | 103 | ||
| 104 | /// Get count. | ||
| 94 | pub fn count(&self) -> u16 { | 105 | pub fn count(&self) -> u16 { |
| 95 | T::regs_gp16().cnt().read().cnt() | 106 | T::regs_gp16().cnt().read().cnt() |
| 96 | } | 107 | } |
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}; | |||
| 11 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 12 | use crate::Peripheral; | 12 | use crate::Peripheral; |
| 13 | 13 | ||
| 14 | pub struct Ch1; | 14 | /// Channel 1 marker type. |
| 15 | pub struct Ch2; | 15 | pub enum Ch1 {} |
| 16 | pub struct Ch3; | 16 | /// Channel 2 marker type. |
| 17 | pub struct Ch4; | 17 | pub enum Ch2 {} |
| 18 | 18 | /// Channel 3 marker type. | |
| 19 | pub struct PwmPin<'d, Perip, Channel> { | 19 | pub enum Ch3 {} |
| 20 | /// Channel 4 marker type. | ||
| 21 | pub enum Ch4 {} | ||
| 22 | |||
| 23 | /// PWM pin wrapper. | ||
| 24 | /// | ||
| 25 | /// This wraps a pin to make it usable with PWM. | ||
| 26 | pub struct PwmPin<'d, T, C> { | ||
| 20 | _pin: PeripheralRef<'d, AnyPin>, | 27 | _pin: PeripheralRef<'d, AnyPin>, |
| 21 | phantom: PhantomData<(Perip, Channel)>, | 28 | phantom: PhantomData<(T, C)>, |
| 22 | } | 29 | } |
| 23 | 30 | ||
| 24 | macro_rules! channel_impl { | 31 | macro_rules! channel_impl { |
| 25 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 32 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
| 26 | impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { | 33 | impl<'d, T: CaptureCompare16bitInstance> PwmPin<'d, T, $channel> { |
| 27 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd, output_type: OutputType) -> Self { | 34 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 35 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self { | ||
| 28 | into_ref!(pin); | 36 | into_ref!(pin); |
| 29 | critical_section::with(|_| { | 37 | critical_section::with(|_| { |
| 30 | pin.set_low(); | 38 | pin.set_low(); |
| @@ -46,11 +54,13 @@ channel_impl!(new_ch2, Ch2, Channel2Pin); | |||
| 46 | channel_impl!(new_ch3, Ch3, Channel3Pin); | 54 | channel_impl!(new_ch3, Ch3, Channel3Pin); |
| 47 | channel_impl!(new_ch4, Ch4, Channel4Pin); | 55 | channel_impl!(new_ch4, Ch4, Channel4Pin); |
| 48 | 56 | ||
| 57 | /// Simple PWM driver. | ||
| 49 | pub struct SimplePwm<'d, T> { | 58 | pub struct SimplePwm<'d, T> { |
| 50 | inner: PeripheralRef<'d, T>, | 59 | inner: PeripheralRef<'d, T>, |
| 51 | } | 60 | } |
| 52 | 61 | ||
| 53 | impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | 62 | impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { |
| 63 | /// Create a new simple PWM driver. | ||
| 54 | pub fn new( | 64 | pub fn new( |
| 55 | tim: impl Peripheral<P = T> + 'd, | 65 | tim: impl Peripheral<P = T> + 'd, |
| 56 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 66 | _ch1: Option<PwmPin<'d, T, Ch1>>, |
| @@ -71,7 +81,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 71 | let mut this = Self { inner: tim }; | 81 | let mut this = Self { inner: tim }; |
| 72 | 82 | ||
| 73 | this.inner.set_counting_mode(counting_mode); | 83 | this.inner.set_counting_mode(counting_mode); |
| 74 | this.set_freq(freq); | 84 | this.set_frequency(freq); |
| 75 | this.inner.start(); | 85 | this.inner.start(); |
| 76 | 86 | ||
| 77 | this.inner.enable_outputs(); | 87 | this.inner.enable_outputs(); |
| @@ -87,15 +97,21 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 87 | this | 97 | this |
| 88 | } | 98 | } |
| 89 | 99 | ||
| 100 | /// Enable the given channel. | ||
| 90 | pub fn enable(&mut self, channel: Channel) { | 101 | pub fn enable(&mut self, channel: Channel) { |
| 91 | self.inner.enable_channel(channel, true); | 102 | self.inner.enable_channel(channel, true); |
| 92 | } | 103 | } |
| 93 | 104 | ||
| 105 | /// Disable the given channel. | ||
| 94 | pub fn disable(&mut self, channel: Channel) { | 106 | pub fn disable(&mut self, channel: Channel) { |
| 95 | self.inner.enable_channel(channel, false); | 107 | self.inner.enable_channel(channel, false); |
| 96 | } | 108 | } |
| 97 | 109 | ||
| 98 | pub fn set_freq(&mut self, freq: Hertz) { | 110 | /// Set PWM frequency. |
| 111 | /// | ||
| 112 | /// Note: when you call this, the max duty value changes, so you will have to | ||
| 113 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | ||
| 114 | pub fn set_frequency(&mut self, freq: Hertz) { | ||
| 99 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 115 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 100 | 2u8 | 116 | 2u8 |
| 101 | } else { | 117 | } else { |
| @@ -104,15 +120,22 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { | |||
| 104 | self.inner.set_frequency(freq * multiplier); | 120 | self.inner.set_frequency(freq * multiplier); |
| 105 | } | 121 | } |
| 106 | 122 | ||
| 123 | /// Get max duty value. | ||
| 124 | /// | ||
| 125 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 107 | pub fn get_max_duty(&self) -> u16 { | 126 | pub fn get_max_duty(&self) -> u16 { |
| 108 | self.inner.get_max_compare_value() + 1 | 127 | self.inner.get_max_compare_value() + 1 |
| 109 | } | 128 | } |
| 110 | 129 | ||
| 130 | /// Set the duty for a given channel. | ||
| 131 | /// | ||
| 132 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 111 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { | 133 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { |
| 112 | assert!(duty <= self.get_max_duty()); | 134 | assert!(duty <= self.get_max_duty()); |
| 113 | self.inner.set_compare_value(channel, duty) | 135 | self.inner.set_compare_value(channel, duty) |
| 114 | } | 136 | } |
| 115 | 137 | ||
| 138 | /// Set the output polarity for a given channel. | ||
| 116 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | 139 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { |
| 117 | self.inner.set_output_polarity(channel, polarity); | 140 | self.inner.set_output_polarity(channel, polarity); |
| 118 | } | 141 | } |
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) { | |||
| 110 | &mut dp.DMA1_CH2, | 110 | &mut dp.DMA1_CH2, |
| 111 | 5, | 111 | 5, |
| 112 | color_list[color_list_index], | 112 | color_list[color_list_index], |
| 113 | pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _, | 113 | pac::TIM3.ccr(pwm_channel.index()).as_ptr() as *mut _, |
| 114 | dma_transfer_option, | 114 | dma_transfer_option, |
| 115 | ) | 115 | ) |
| 116 | .await; | 116 | .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> { | |||
| 85 | 85 | ||
| 86 | let mut this = Self { inner: tim }; | 86 | let mut this = Self { inner: tim }; |
| 87 | 87 | ||
| 88 | this.set_freq(freq); | 88 | this.set_frequency(freq); |
| 89 | this.inner.start(); | 89 | this.inner.start(); |
| 90 | 90 | ||
| 91 | let r = T::regs_gp32(); | 91 | let r = T::regs_gp32(); |
| @@ -102,14 +102,14 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | pub fn enable(&mut self, channel: Channel) { | 104 | pub fn enable(&mut self, channel: Channel) { |
| 105 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); | 105 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true)); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | pub fn disable(&mut self, channel: Channel) { | 108 | pub fn disable(&mut self, channel: Channel) { |
| 109 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); | 109 | T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false)); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | pub fn set_freq(&mut self, freq: Hertz) { | 112 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 113 | <T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq); | 113 | <T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| @@ -119,6 +119,6 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { | |||
| 119 | 119 | ||
| 120 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { | 120 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { |
| 121 | defmt::assert!(duty < self.get_max_duty()); | 121 | defmt::assert!(duty < self.get_max_duty()); |
| 122 | T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) | 122 | T::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(duty)) |
| 123 | } | 123 | } |
| 124 | } | 124 | } |
