diff options
| author | Ulf Lilleengen <[email protected]> | 2024-10-23 10:30:13 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-10-23 10:30:13 +0000 |
| commit | 8803128707b8bd9fc9dcea392a62dfd42aa822d2 (patch) | |
| tree | f7ce53e194af927e02b015bf8e5dcd837169a70e | |
| parent | 8eb80c6816a1f6ea7814acd4f48f07e79891f804 (diff) | |
| parent | f2646b29a6b0a741fc424f88c5ca3dc25fce9369 (diff) | |
Merge pull request #3317 from GrantM11235/simplepwmchannel
embassy-stm32: Add SimplePwmChannel
| -rw-r--r-- | embassy-stm32/src/timer/low_level.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/mod.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/timer/simple_pwm.rs | 270 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/pwm.rs | 19 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/ws2812_pwm.rs | 4 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/input_capture.rs | 8 | ||||
| -rw-r--r-- | examples/stm32g0/src/bin/pwm_input.rs | 10 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/pwm.rs | 19 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/pwm.rs | 19 |
9 files changed, 269 insertions, 90 deletions
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index e643722aa..3136ea4e9 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | //! | 6 | //! |
| 7 | //! The available functionality depends on the timer type. | 7 | //! The available functionality depends on the timer type. |
| 8 | 8 | ||
| 9 | use core::mem::ManuallyDrop; | ||
| 10 | |||
| 9 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 11 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 10 | // Re-export useful enums | 12 | // Re-export useful enums |
| 11 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; |
| @@ -198,6 +200,11 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 198 | Self { tim } | 200 | Self { tim } |
| 199 | } | 201 | } |
| 200 | 202 | ||
| 203 | pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop<Self> { | ||
| 204 | let tim = unsafe { self.tim.clone_unchecked() }; | ||
| 205 | ManuallyDrop::new(Self { tim }) | ||
| 206 | } | ||
| 207 | |||
| 201 | /// Get access to the virutal core 16bit timer registers. | 208 | /// Get access to the virutal core 16bit timer registers. |
| 202 | /// | 209 | /// |
| 203 | /// Note: This works even if the timer is more capable, because registers | 210 | /// Note: This works even if the timer is more capable, because registers |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 25782ee13..aa9dd91d9 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::Peripheral; | ||
| 5 | use embassy_sync::waitqueue::AtomicWaker; | 6 | use embassy_sync::waitqueue::AtomicWaker; |
| 6 | 7 | ||
| 7 | #[cfg(not(stm32l0))] | 8 | #[cfg(not(stm32l0))] |
| @@ -66,7 +67,7 @@ impl State { | |||
| 66 | } | 67 | } |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | trait SealedInstance: RccPeripheral { | 70 | trait SealedInstance: RccPeripheral + Peripheral<P = Self> { |
| 70 | /// Async state for this timer | 71 | /// Async state for this timer |
| 71 | fn state() -> &'static State; | 72 | fn state() -> &'static State; |
| 72 | } | 73 | } |
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index b7771bd64..9e4a09095 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | //! Simple PWM driver. | 1 | //! Simple PWM driver. |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::mem::ManuallyDrop; | ||
| 4 | 5 | ||
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 6 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 6 | 7 | ||
| @@ -51,6 +52,111 @@ channel_impl!(new_ch2, Ch2, Channel2Pin); | |||
| 51 | channel_impl!(new_ch3, Ch3, Channel3Pin); | 52 | channel_impl!(new_ch3, Ch3, Channel3Pin); |
| 52 | channel_impl!(new_ch4, Ch4, Channel4Pin); | 53 | channel_impl!(new_ch4, Ch4, Channel4Pin); |
| 53 | 54 | ||
| 55 | /// A single channel of a pwm, obtained from [`SimplePwm::split`], | ||
| 56 | /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. | ||
| 57 | /// | ||
| 58 | /// It is not possible to change the pwm frequency because | ||
| 59 | /// the frequency configuration is shared with all four channels. | ||
| 60 | pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { | ||
| 61 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 62 | channel: Channel, | ||
| 63 | } | ||
| 64 | |||
| 65 | // TODO: check for RMW races | ||
| 66 | impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | ||
| 67 | /// Enable the given channel. | ||
| 68 | pub fn enable(&mut self) { | ||
| 69 | self.timer.enable_channel(self.channel, true); | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Disable the given channel. | ||
| 73 | pub fn disable(&mut self) { | ||
| 74 | self.timer.enable_channel(self.channel, false); | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Check whether given channel is enabled | ||
| 78 | pub fn is_enabled(&self) -> bool { | ||
| 79 | self.timer.get_channel_enable_state(self.channel) | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Get max duty value. | ||
| 83 | /// | ||
| 84 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 85 | pub fn max_duty_cycle(&self) -> u16 { | ||
| 86 | let max = self.timer.get_max_compare_value(); | ||
| 87 | assert!(max < u16::MAX as u32); | ||
| 88 | max as u16 + 1 | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Set the duty for a given channel. | ||
| 92 | /// | ||
| 93 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. | ||
| 94 | pub fn set_duty_cycle(&mut self, duty: u16) { | ||
| 95 | assert!(duty <= (*self).max_duty_cycle()); | ||
| 96 | self.timer.set_compare_value(self.channel, duty.into()) | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Set the duty cycle to 0%, or always inactive. | ||
| 100 | pub fn set_duty_cycle_fully_off(&mut self) { | ||
| 101 | self.set_duty_cycle(0); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Set the duty cycle to 100%, or always active. | ||
| 105 | pub fn set_duty_cycle_fully_on(&mut self) { | ||
| 106 | self.set_duty_cycle((*self).max_duty_cycle()); | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Set the duty cycle to `num / denom`. | ||
| 110 | /// | ||
| 111 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, | ||
| 112 | /// and that `denom` is not zero. | ||
| 113 | pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { | ||
| 114 | assert!(denom != 0); | ||
| 115 | assert!(num <= denom); | ||
| 116 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); | ||
| 117 | |||
| 118 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) | ||
| 119 | #[allow(clippy::cast_possible_truncation)] | ||
| 120 | self.set_duty_cycle(duty as u16); | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Set the duty cycle to `percent / 100` | ||
| 124 | /// | ||
| 125 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. | ||
| 126 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { | ||
| 127 | self.set_duty_cycle_fraction(u16::from(percent), 100) | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Get the duty for a given channel. | ||
| 131 | /// | ||
| 132 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. | ||
| 133 | pub fn current_duty_cycle(&self) -> u16 { | ||
| 134 | unwrap!(self.timer.get_compare_value(self.channel).try_into()) | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Set the output polarity for a given channel. | ||
| 138 | pub fn set_polarity(&mut self, polarity: OutputPolarity) { | ||
| 139 | self.timer.set_output_polarity(self.channel, polarity); | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Set the output compare mode for a given channel. | ||
| 143 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { | ||
| 144 | self.timer.set_output_compare_mode(self.channel, mode); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | ||
| 149 | pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> { | ||
| 150 | /// Channel 1 | ||
| 151 | pub ch1: SimplePwmChannel<'d, T>, | ||
| 152 | /// Channel 2 | ||
| 153 | pub ch2: SimplePwmChannel<'d, T>, | ||
| 154 | /// Channel 3 | ||
| 155 | pub ch3: SimplePwmChannel<'d, T>, | ||
| 156 | /// Channel 4 | ||
| 157 | pub ch4: SimplePwmChannel<'d, T>, | ||
| 158 | } | ||
| 159 | |||
| 54 | /// Simple PWM driver. | 160 | /// Simple PWM driver. |
| 55 | pub struct SimplePwm<'d, T: GeneralInstance4Channel> { | 161 | pub struct SimplePwm<'d, T: GeneralInstance4Channel> { |
| 56 | inner: Timer<'d, T>, | 162 | inner: Timer<'d, T>, |
| @@ -89,19 +195,76 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 89 | this | 195 | this |
| 90 | } | 196 | } |
| 91 | 197 | ||
| 92 | /// Enable the given channel. | 198 | /// Get a single channel |
| 93 | pub fn enable(&mut self, channel: Channel) { | 199 | /// |
| 94 | self.inner.enable_channel(channel, true); | 200 | /// If you need to use multiple channels, use [`Self::split`]. |
| 201 | pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { | ||
| 202 | SimplePwmChannel { | ||
| 203 | timer: unsafe { self.inner.clone_unchecked() }, | ||
| 204 | channel, | ||
| 205 | } | ||
| 95 | } | 206 | } |
| 96 | 207 | ||
| 97 | /// Disable the given channel. | 208 | /// Channel 1 |
| 98 | pub fn disable(&mut self, channel: Channel) { | 209 | /// |
| 99 | self.inner.enable_channel(channel, false); | 210 | /// This is just a convenience wrapper around [`Self::channel`]. |
| 211 | /// | ||
| 212 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 213 | pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 214 | self.channel(Channel::Ch1) | ||
| 100 | } | 215 | } |
| 101 | 216 | ||
| 102 | /// Check whether given channel is enabled | 217 | /// Channel 2 |
| 103 | pub fn is_enabled(&self, channel: Channel) -> bool { | 218 | /// |
| 104 | self.inner.get_channel_enable_state(channel) | 219 | /// This is just a convenience wrapper around [`Self::channel`]. |
| 220 | /// | ||
| 221 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 222 | pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 223 | self.channel(Channel::Ch2) | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Channel 3 | ||
| 227 | /// | ||
| 228 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 229 | /// | ||
| 230 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 231 | pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 232 | self.channel(Channel::Ch3) | ||
| 233 | } | ||
| 234 | |||
| 235 | /// Channel 4 | ||
| 236 | /// | ||
| 237 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 238 | /// | ||
| 239 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 240 | pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 241 | self.channel(Channel::Ch4) | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Splits a [`SimplePwm`] into four pwm channels. | ||
| 245 | /// | ||
| 246 | /// This returns all four channels, including channels that | ||
| 247 | /// aren't configured with a [`PwmPin`]. | ||
| 248 | // TODO: I hate the name "split" | ||
| 249 | pub fn split(self) -> SimplePwmChannels<'static, T> | ||
| 250 | where | ||
| 251 | // must be static because the timer will never be dropped/disabled | ||
| 252 | 'd: 'static, | ||
| 253 | { | ||
| 254 | // without this, the timer would be disabled at the end of this function | ||
| 255 | let timer = ManuallyDrop::new(self.inner); | ||
| 256 | |||
| 257 | let ch = |channel| SimplePwmChannel { | ||
| 258 | timer: unsafe { timer.clone_unchecked() }, | ||
| 259 | channel, | ||
| 260 | }; | ||
| 261 | |||
| 262 | SimplePwmChannels { | ||
| 263 | ch1: ch(Channel::Ch1), | ||
| 264 | ch2: ch(Channel::Ch2), | ||
| 265 | ch3: ch(Channel::Ch3), | ||
| 266 | ch4: ch(Channel::Ch4), | ||
| 267 | } | ||
| 105 | } | 268 | } |
| 106 | 269 | ||
| 107 | /// Set PWM frequency. | 270 | /// Set PWM frequency. |
| @@ -109,6 +272,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 109 | /// Note: when you call this, the max duty value changes, so you will have to | 272 | /// Note: when you call this, the max duty value changes, so you will have to |
| 110 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 273 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. |
| 111 | pub fn set_frequency(&mut self, freq: Hertz) { | 274 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 275 | // TODO: prevent ARR = u16::MAX? | ||
| 112 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 276 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 113 | 2u8 | 277 | 2u8 |
| 114 | } else { | 278 | } else { |
| @@ -120,33 +284,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 120 | /// Get max duty value. | 284 | /// Get max duty value. |
| 121 | /// | 285 | /// |
| 122 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 286 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 123 | pub fn get_max_duty(&self) -> u32 { | 287 | pub fn max_duty_cycle(&self) -> u16 { |
| 124 | self.inner.get_max_compare_value() + 1 | 288 | let max = self.inner.get_max_compare_value(); |
| 125 | } | 289 | assert!(max < u16::MAX as u32); |
| 126 | 290 | max as u16 + 1 | |
| 127 | /// Set the duty for a given channel. | ||
| 128 | /// | ||
| 129 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 130 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { | ||
| 131 | assert!(duty <= self.get_max_duty()); | ||
| 132 | self.inner.set_compare_value(channel, duty) | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Get the duty for a given channel. | ||
| 136 | /// | ||
| 137 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 138 | pub fn get_duty(&self, channel: Channel) -> u32 { | ||
| 139 | self.inner.get_compare_value(channel) | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Set the output polarity for a given channel. | ||
| 143 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | ||
| 144 | self.inner.set_output_polarity(channel, polarity); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Set the output compare mode for a given channel. | ||
| 148 | pub fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { | ||
| 149 | self.inner.set_output_compare_mode(channel, mode); | ||
| 150 | } | 291 | } |
| 151 | 292 | ||
| 152 | /// Generate a sequence of PWM waveform | 293 | /// Generate a sequence of PWM waveform |
| @@ -164,8 +305,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 164 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 305 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 165 | let req = dma.request(); | 306 | let req = dma.request(); |
| 166 | 307 | ||
| 167 | let original_duty_state = self.get_duty(channel); | 308 | let original_duty_state = self.channel(channel).current_duty_cycle(); |
| 168 | let original_enable_state = self.is_enabled(channel); | 309 | let original_enable_state = self.channel(channel).is_enabled(); |
| 169 | let original_update_dma_state = self.inner.get_update_dma_state(); | 310 | let original_update_dma_state = self.inner.get_update_dma_state(); |
| 170 | 311 | ||
| 171 | if !original_update_dma_state { | 312 | if !original_update_dma_state { |
| @@ -173,7 +314,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 173 | } | 314 | } |
| 174 | 315 | ||
| 175 | if !original_enable_state { | 316 | if !original_enable_state { |
| 176 | self.enable(channel); | 317 | self.channel(channel).enable(); |
| 177 | } | 318 | } |
| 178 | 319 | ||
| 179 | unsafe { | 320 | unsafe { |
| @@ -201,10 +342,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 201 | 342 | ||
| 202 | // restore output compare state | 343 | // restore output compare state |
| 203 | if !original_enable_state { | 344 | if !original_enable_state { |
| 204 | self.disable(channel); | 345 | self.channel(channel).disable(); |
| 205 | } | 346 | } |
| 206 | 347 | ||
| 207 | self.set_duty(channel, original_duty_state); | 348 | self.channel(channel).set_duty_cycle(original_duty_state); |
| 208 | 349 | ||
| 209 | // Since DMA is closed before timer update event trigger DMA is turn off, | 350 | // Since DMA is closed before timer update event trigger DMA is turn off, |
| 210 | // this can almost always trigger a DMA FIFO error. | 351 | // this can almost always trigger a DMA FIFO error. |
| @@ -234,8 +375,8 @@ macro_rules! impl_waveform_chx { | |||
| 234 | 375 | ||
| 235 | let cc_channel = Channel::$cc_ch; | 376 | let cc_channel = Channel::$cc_ch; |
| 236 | 377 | ||
| 237 | let original_duty_state = self.get_duty(cc_channel); | 378 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); |
| 238 | let original_enable_state = self.is_enabled(cc_channel); | 379 | let original_enable_state = self.channel(cc_channel).is_enabled(); |
| 239 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; | 380 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; |
| 240 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | 381 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); |
| 241 | 382 | ||
| @@ -249,7 +390,7 @@ macro_rules! impl_waveform_chx { | |||
| 249 | } | 390 | } |
| 250 | 391 | ||
| 251 | if !original_enable_state { | 392 | if !original_enable_state { |
| 252 | self.enable(cc_channel); | 393 | self.channel(cc_channel).enable(); |
| 253 | } | 394 | } |
| 254 | 395 | ||
| 255 | unsafe { | 396 | unsafe { |
| @@ -277,10 +418,10 @@ macro_rules! impl_waveform_chx { | |||
| 277 | 418 | ||
| 278 | // restore output compare state | 419 | // restore output compare state |
| 279 | if !original_enable_state { | 420 | if !original_enable_state { |
| 280 | self.disable(cc_channel); | 421 | self.channel(cc_channel).disable(); |
| 281 | } | 422 | } |
| 282 | 423 | ||
| 283 | self.set_duty(cc_channel, original_duty_state); | 424 | self.channel(cc_channel).set_duty_cycle(original_duty_state); |
| 284 | 425 | ||
| 285 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | 426 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, |
| 286 | // this can almost always trigger a DMA FIFO error. | 427 | // this can almost always trigger a DMA FIFO error. |
| @@ -304,6 +445,41 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); | |||
| 304 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); | 445 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); |
| 305 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); | 446 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); |
| 306 | 447 | ||
| 448 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { | ||
| 449 | type Error = core::convert::Infallible; | ||
| 450 | } | ||
| 451 | |||
| 452 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { | ||
| 453 | fn max_duty_cycle(&self) -> u16 { | ||
| 454 | self.max_duty_cycle() | ||
| 455 | } | ||
| 456 | |||
| 457 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { | ||
| 458 | self.set_duty_cycle(duty); | ||
| 459 | Ok(()) | ||
| 460 | } | ||
| 461 | |||
| 462 | fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { | ||
| 463 | self.set_duty_cycle_fully_off(); | ||
| 464 | Ok(()) | ||
| 465 | } | ||
| 466 | |||
| 467 | fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { | ||
| 468 | self.set_duty_cycle_fully_on(); | ||
| 469 | Ok(()) | ||
| 470 | } | ||
| 471 | |||
| 472 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { | ||
| 473 | self.set_duty_cycle_fraction(num, denom); | ||
| 474 | Ok(()) | ||
| 475 | } | ||
| 476 | |||
| 477 | fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { | ||
| 478 | self.set_duty_cycle_percent(percent); | ||
| 479 | Ok(()) | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 307 | impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { | 483 | impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { |
| 308 | type Channel = Channel; | 484 | type Channel = Channel; |
| 309 | type Time = Hertz; | 485 | type Time = Hertz; |
| @@ -330,7 +506,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { | |||
| 330 | } | 506 | } |
| 331 | 507 | ||
| 332 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 508 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
| 333 | assert!(duty <= self.get_max_duty()); | 509 | assert!(duty <= self.max_duty_cycle() as u32); |
| 334 | self.inner.set_compare_value(channel, duty) | 510 | self.inner.set_compare_value(channel, duty) |
| 335 | } | 511 | } |
| 336 | 512 | ||
diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 8844a9f0e..04811162b 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs | |||
| @@ -6,7 +6,6 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | ||
| 10 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| @@ -15,22 +14,22 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 17 | 16 | ||
| 18 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); | 17 | let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull); |
| 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); | 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 20 | let max = pwm.get_max_duty(); | 19 | let mut ch1 = pwm.ch1(); |
| 21 | pwm.enable(Channel::Ch1); | 20 | ch1.enable(); |
| 22 | 21 | ||
| 23 | info!("PWM initialized"); | 22 | info!("PWM initialized"); |
| 24 | info!("PWM max duty {}", max); | 23 | info!("PWM max duty {}", ch1.max_duty_cycle()); |
| 25 | 24 | ||
| 26 | loop { | 25 | loop { |
| 27 | pwm.set_duty(Channel::Ch1, 0); | 26 | ch1.set_duty_cycle_fully_off(); |
| 28 | Timer::after_millis(300).await; | 27 | Timer::after_millis(300).await; |
| 29 | pwm.set_duty(Channel::Ch1, max / 4); | 28 | ch1.set_duty_cycle_fraction(1, 4); |
| 30 | Timer::after_millis(300).await; | 29 | Timer::after_millis(300).await; |
| 31 | pwm.set_duty(Channel::Ch1, max / 2); | 30 | ch1.set_duty_cycle_fraction(1, 2); |
| 32 | Timer::after_millis(300).await; | 31 | Timer::after_millis(300).await; |
| 33 | pwm.set_duty(Channel::Ch1, max - 1); | 32 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); |
| 34 | Timer::after_millis(300).await; | 33 | Timer::after_millis(300).await; |
| 35 | } | 34 | } |
| 36 | } | 35 | } |
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index cbaff75fc..3ab93d6e0 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs | |||
| @@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) { | |||
| 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit | 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit |
| 62 | // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low | 62 | // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low |
| 63 | 63 | ||
| 64 | let max_duty = ws2812_pwm.get_max_duty() as u16; | 64 | let max_duty = ws2812_pwm.max_duty_cycle(); |
| 65 | let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing | 65 | let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing |
| 66 | let n1 = 2 * n0; // ws2812 Bit 1 high level timing | 66 | let n1 = 2 * n0; // ws2812 Bit 1 high level timing |
| 67 | 67 | ||
| @@ -84,7 +84,7 @@ async fn main(_spawner: Spawner) { | |||
| 84 | let pwm_channel = Channel::Ch1; | 84 | let pwm_channel = Channel::Ch1; |
| 85 | 85 | ||
| 86 | // make sure PWM output keep low on first start | 86 | // make sure PWM output keep low on first start |
| 87 | ws2812_pwm.set_duty(pwm_channel, 0); | 87 | ws2812_pwm.channel(pwm_channel).set_duty_cycle(0); |
| 88 | 88 | ||
| 89 | // flip color at 2 Hz | 89 | // flip color at 2 Hz |
| 90 | let mut ticker = Ticker::every(Duration::from_millis(500)); | 90 | let mut ticker = Ticker::every(Duration::from_millis(500)); |
diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 69fdae96d..bc814cb13 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs | |||
| @@ -47,10 +47,10 @@ async fn main(spawner: Spawner) { | |||
| 47 | unwrap!(spawner.spawn(blinky(p.PB1))); | 47 | unwrap!(spawner.spawn(blinky(p.PB1))); |
| 48 | 48 | ||
| 49 | // Connect PB1 and PA8 with a 1k Ohm resistor | 49 | // Connect PB1 and PA8 with a 1k Ohm resistor |
| 50 | let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 50 | let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); |
| 51 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(1), Default::default()); | 51 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); |
| 52 | pwm.enable(Channel::Ch1); | 52 | pwm.ch1().enable(); |
| 53 | pwm.set_duty(Channel::Ch1, 50); | 53 | pwm.ch1().set_duty_cycle(50); |
| 54 | 54 | ||
| 55 | let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); | 55 | let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); |
| 56 | let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); | 56 | let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); |
diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 152ecda86..db9cf4f8a 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs | |||
| @@ -14,7 +14,6 @@ use embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed}; | |||
| 14 | use embassy_stm32::time::khz; | 14 | use embassy_stm32::time::khz; |
| 15 | use embassy_stm32::timer::pwm_input::PwmInput; | 15 | use embassy_stm32::timer::pwm_input::PwmInput; |
| 16 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 16 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 17 | use embassy_stm32::timer::Channel; | ||
| 18 | use embassy_stm32::{bind_interrupts, peripherals, timer}; | 17 | use embassy_stm32::{bind_interrupts, peripherals, timer}; |
| 19 | use embassy_time::Timer; | 18 | use embassy_time::Timer; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -43,11 +42,10 @@ async fn main(spawner: Spawner) { | |||
| 43 | 42 | ||
| 44 | unwrap!(spawner.spawn(blinky(p.PB1))); | 43 | unwrap!(spawner.spawn(blinky(p.PB1))); |
| 45 | // Connect PA8 and PA6 with a 1k Ohm resistor | 44 | // Connect PA8 and PA6 with a 1k Ohm resistor |
| 46 | let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 45 | let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); |
| 47 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(1), Default::default()); | 46 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); |
| 48 | let max = pwm.get_max_duty(); | 47 | pwm.ch1().set_duty_cycle_fraction(1, 4); |
| 49 | pwm.set_duty(Channel::Ch1, max / 4); | 48 | pwm.ch1().enable(); |
| 50 | pwm.enable(Channel::Ch1); | ||
| 51 | 49 | ||
| 52 | let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); | 50 | let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); |
| 53 | pwm_input.enable(); | 51 | pwm_input.enable(); |
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index d4809a481..6c965012c 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs | |||
| @@ -6,7 +6,6 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | ||
| 10 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| @@ -15,22 +14,22 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 17 | 16 | ||
| 18 | let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); | 17 | let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull); |
| 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); | 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 20 | let max = pwm.get_max_duty(); | 19 | let mut ch1 = pwm.ch1(); |
| 21 | pwm.enable(Channel::Ch1); | 20 | ch1.enable(); |
| 22 | 21 | ||
| 23 | info!("PWM initialized"); | 22 | info!("PWM initialized"); |
| 24 | info!("PWM max duty {}", max); | 23 | info!("PWM max duty {}", ch1.max_duty_cycle()); |
| 25 | 24 | ||
| 26 | loop { | 25 | loop { |
| 27 | pwm.set_duty(Channel::Ch1, 0); | 26 | ch1.set_duty_cycle_fully_off(); |
| 28 | Timer::after_millis(300).await; | 27 | Timer::after_millis(300).await; |
| 29 | pwm.set_duty(Channel::Ch1, max / 4); | 28 | ch1.set_duty_cycle_fraction(1, 4); |
| 30 | Timer::after_millis(300).await; | 29 | Timer::after_millis(300).await; |
| 31 | pwm.set_duty(Channel::Ch1, max / 2); | 30 | ch1.set_duty_cycle_fraction(1, 2); |
| 32 | Timer::after_millis(300).await; | 31 | Timer::after_millis(300).await; |
| 33 | pwm.set_duty(Channel::Ch1, max - 1); | 32 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); |
| 34 | Timer::after_millis(300).await; | 33 | Timer::after_millis(300).await; |
| 35 | } | 34 | } |
| 36 | } | 35 | } |
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 1e48ba67b..a1c53fc3f 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs | |||
| @@ -6,7 +6,6 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | ||
| 10 | use embassy_stm32::Config; | 9 | use embassy_stm32::Config; |
| 11 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -37,22 +36,22 @@ async fn main(_spawner: Spawner) { | |||
| 37 | let p = embassy_stm32::init(config); | 36 | let p = embassy_stm32::init(config); |
| 38 | info!("Hello World!"); | 37 | info!("Hello World!"); |
| 39 | 38 | ||
| 40 | let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); | 39 | let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); |
| 41 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); | 40 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 42 | let max = pwm.get_max_duty(); | 41 | let mut ch1 = pwm.ch1(); |
| 43 | pwm.enable(Channel::Ch1); | 42 | ch1.enable(); |
| 44 | 43 | ||
| 45 | info!("PWM initialized"); | 44 | info!("PWM initialized"); |
| 46 | info!("PWM max duty {}", max); | 45 | info!("PWM max duty {}", ch1.max_duty_cycle()); |
| 47 | 46 | ||
| 48 | loop { | 47 | loop { |
| 49 | pwm.set_duty(Channel::Ch1, 0); | 48 | ch1.set_duty_cycle_fully_off(); |
| 50 | Timer::after_millis(300).await; | 49 | Timer::after_millis(300).await; |
| 51 | pwm.set_duty(Channel::Ch1, max / 4); | 50 | ch1.set_duty_cycle_fraction(1, 4); |
| 52 | Timer::after_millis(300).await; | 51 | Timer::after_millis(300).await; |
| 53 | pwm.set_duty(Channel::Ch1, max / 2); | 52 | ch1.set_duty_cycle_fraction(1, 2); |
| 54 | Timer::after_millis(300).await; | 53 | Timer::after_millis(300).await; |
| 55 | pwm.set_duty(Channel::Ch1, max - 1); | 54 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); |
| 56 | Timer::after_millis(300).await; | 55 | Timer::after_millis(300).await; |
| 57 | } | 56 | } |
| 58 | } | 57 | } |
