aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/timer/simple_pwm.rs
diff options
context:
space:
mode:
authormelvdl <[email protected]>2025-06-27 01:08:28 +0200
committermelvdl <[email protected]>2025-06-27 01:08:28 +0200
commit6f88c2c73caa63a6e534130f4a064cb95d3e9d7d (patch)
treefdddad93e4f48f32ff15a3b8ad6cd0ae12095fd4 /embassy-stm32/src/timer/simple_pwm.rs
parentcbd24bf2eece65a787fc085c255e9b2932ea73e3 (diff)
stm32: rename timer channel trait; replace impls via macro with impls generic over timer channels
Diffstat (limited to 'embassy-stm32/src/timer/simple_pwm.rs')
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs279
1 files changed, 128 insertions, 151 deletions
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 02835c379..c04b1ab97 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,7 +4,7 @@ use core::marker::PhantomData;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::{Ch1, Ch2, Ch3, Ch4, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; 7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin};
8#[cfg(gpio_v2)] 8#[cfg(gpio_v2)]
9use crate::gpio::Pull; 9use crate::gpio::Pull;
10use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 10use crate::gpio::{AfType, AnyPin, OutputType, Speed};
@@ -34,46 +34,37 @@ pub struct PwmPinConfig {
34 pub pull: Pull, 34 pub pull: Pull,
35} 35}
36 36
37macro_rules! channel_impl { 37impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> {
38 ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { 38 /// Create a new PWM pin instance.
39 impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { 39 pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self {
40 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] 40 critical_section::with(|_| {
41 pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T, $channel>>, output_type: OutputType) -> Self { 41 pin.set_low();
42 critical_section::with(|_| { 42 pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
43 pin.set_low(); 43 });
44 pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); 44 PwmPin {
45 }); 45 _pin: pin.into(),
46 PwmPin { 46 phantom: PhantomData,
47 _pin: pin.into(),
48 phantom: PhantomData,
49 }
50 }
51
52 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")]
53 pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T, $channel>>, pin_config: PwmPinConfig) -> Self {
54 critical_section::with(|_| {
55 pin.set_low();
56 pin.set_as_af(
57 pin.af_num(),
58 #[cfg(gpio_v1)]
59 AfType::output(pin_config.output_type, pin_config.speed),
60 #[cfg(gpio_v2)]
61 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
62 );
63 });
64 PwmPin {
65 _pin: pin.into(),
66 phantom: PhantomData,
67 }
68 }
69 } 47 }
70 }; 48 }
71}
72 49
73channel_impl!(new_ch1, new_ch1_with_config, Ch1, TimerPin); 50 /// Create a new PWM pin instance with config.
74channel_impl!(new_ch2, new_ch2_with_config, Ch2, TimerPin); 51 pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self {
75channel_impl!(new_ch3, new_ch3_with_config, Ch3, TimerPin); 52 critical_section::with(|_| {
76channel_impl!(new_ch4, new_ch4_with_config, Ch4, TimerPin); 53 pin.set_low();
54 pin.set_as_af(
55 pin.af_num(),
56 #[cfg(gpio_v1)]
57 AfType::output(pin_config.output_type, pin_config.speed),
58 #[cfg(gpio_v2)]
59 AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),
60 );
61 });
62 PwmPin {
63 _pin: pin.into(),
64 phantom: PhantomData,
65 }
66 }
67}
77 68
78/// A single channel of a pwm, obtained from [`SimplePwm::split`], 69/// A single channel of a pwm, obtained from [`SimplePwm::split`],
79/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. 70/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
@@ -82,7 +73,7 @@ channel_impl!(new_ch4, new_ch4_with_config, Ch4, TimerPin);
82/// the frequency configuration is shared with all four channels. 73/// the frequency configuration is shared with all four channels.
83pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { 74pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {
84 timer: ManuallyDrop<Timer<'d, T>>, 75 timer: ManuallyDrop<Timer<'d, T>>,
85 channel: TimerChannel, 76 channel: Channel,
86} 77}
87 78
88// TODO: check for RMW races 79// TODO: check for RMW races
@@ -207,18 +198,13 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
207 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 198 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
208 this.inner.start(); 199 this.inner.start();
209 200
210 [ 201 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
211 TimerChannel::Ch1, 202 .iter()
212 TimerChannel::Ch2, 203 .for_each(|&channel| {
213 TimerChannel::Ch3, 204 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
214 TimerChannel::Ch4, 205
215 ] 206 this.inner.set_output_compare_preload(channel, true);
216 .iter() 207 });
217 .for_each(|&channel| {
218 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
219
220 this.inner.set_output_compare_preload(channel, true);
221 });
222 208
223 this 209 this
224 } 210 }
@@ -226,7 +212,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
226 /// Get a single channel 212 /// Get a single channel
227 /// 213 ///
228 /// If you need to use multiple channels, use [`Self::split`]. 214 /// If you need to use multiple channels, use [`Self::split`].
229 pub fn channel(&mut self, channel: TimerChannel) -> SimplePwmChannel<'_, T> { 215 pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> {
230 SimplePwmChannel { 216 SimplePwmChannel {
231 timer: unsafe { self.inner.clone_unchecked() }, 217 timer: unsafe { self.inner.clone_unchecked() },
232 channel, 218 channel,
@@ -239,7 +225,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
239 /// 225 ///
240 /// If you need to use multiple channels, use [`Self::split`]. 226 /// If you need to use multiple channels, use [`Self::split`].
241 pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { 227 pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> {
242 self.channel(TimerChannel::Ch1) 228 self.channel(Channel::Ch1)
243 } 229 }
244 230
245 /// Channel 2 231 /// Channel 2
@@ -248,7 +234,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
248 /// 234 ///
249 /// If you need to use multiple channels, use [`Self::split`]. 235 /// If you need to use multiple channels, use [`Self::split`].
250 pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { 236 pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> {
251 self.channel(TimerChannel::Ch2) 237 self.channel(Channel::Ch2)
252 } 238 }
253 239
254 /// Channel 3 240 /// Channel 3
@@ -257,7 +243,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
257 /// 243 ///
258 /// If you need to use multiple channels, use [`Self::split`]. 244 /// If you need to use multiple channels, use [`Self::split`].
259 pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { 245 pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> {
260 self.channel(TimerChannel::Ch3) 246 self.channel(Channel::Ch3)
261 } 247 }
262 248
263 /// Channel 4 249 /// Channel 4
@@ -266,7 +252,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
266 /// 252 ///
267 /// If you need to use multiple channels, use [`Self::split`]. 253 /// If you need to use multiple channels, use [`Self::split`].
268 pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { 254 pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> {
269 self.channel(TimerChannel::Ch4) 255 self.channel(Channel::Ch4)
270 } 256 }
271 257
272 /// Splits a [`SimplePwm`] into four pwm channels. 258 /// Splits a [`SimplePwm`] into four pwm channels.
@@ -288,10 +274,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
288 }; 274 };
289 275
290 SimplePwmChannels { 276 SimplePwmChannels {
291 ch1: ch(TimerChannel::Ch1), 277 ch1: ch(Channel::Ch1),
292 ch2: ch(TimerChannel::Ch2), 278 ch2: ch(Channel::Ch2),
293 ch3: ch(TimerChannel::Ch3), 279 ch3: ch(Channel::Ch3),
294 ch4: ch(TimerChannel::Ch4), 280 ch4: ch(Channel::Ch4),
295 } 281 }
296 } 282 }
297 283
@@ -322,7 +308,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
322 /// 308 ///
323 /// Note: 309 /// Note:
324 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 310 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
325 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: TimerChannel, duty: &[u16]) { 311 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
326 #[allow(clippy::let_unit_value)] // eg. stm32f334 312 #[allow(clippy::let_unit_value)] // eg. stm32f334
327 let req = dma.request(); 313 let req = dma.request();
328 314
@@ -405,8 +391,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
405 pub async fn waveform_up_multi_channel( 391 pub async fn waveform_up_multi_channel(
406 &mut self, 392 &mut self,
407 dma: Peri<'_, impl super::UpDma<T>>, 393 dma: Peri<'_, impl super::UpDma<T>>,
408 starting_channel: TimerChannel, 394 starting_channel: Channel,
409 ending_channel: TimerChannel, 395 ending_channel: Channel,
410 duty: &[u16], 396 duty: &[u16],
411 ) { 397 ) {
412 let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; 398 let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32;
@@ -462,106 +448,97 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
462 } 448 }
463} 449}
464 450
465macro_rules! impl_waveform_chx { 451impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
466 ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { 452 /// Generate a sequence of PWM waveform
467 impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { 453 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
468 /// Generate a sequence of PWM waveform 454 use crate::pac::timer::vals::Ccds;
469 pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T, $cc_ch>>, duty: &[u16]) {
470 use crate::pac::timer::vals::Ccds;
471 455
472 #[allow(clippy::let_unit_value)] // eg. stm32f334 456 #[allow(clippy::let_unit_value)] // eg. stm32f334
473 let req = dma.request(); 457 let req = dma.request();
474 458
475 let cc_channel = TimerChannel::$cc_ch; 459 let cc_channel = C::CHANNEL;
476 460
477 let original_duty_state = self.channel(cc_channel).current_duty_cycle(); 461 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
478 let original_enable_state = self.channel(cc_channel).is_enabled(); 462 let original_enable_state = self.channel(cc_channel).is_enabled();
479 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; 463 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
480 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); 464 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
481 465
482 // redirect CC DMA request onto Update Event 466 // redirect CC DMA request onto Update Event
483 if !original_cc_dma_on_update { 467 if !original_cc_dma_on_update {
484 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) 468 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
485 } 469 }
486 470
487 if !original_cc_dma_enabled { 471 if !original_cc_dma_enabled {
488 self.inner.set_cc_dma_enable_state(cc_channel, true); 472 self.inner.set_cc_dma_enable_state(cc_channel, true);
489 } 473 }
490 474
491 if !original_enable_state { 475 if !original_enable_state {
492 self.channel(cc_channel).enable(); 476 self.channel(cc_channel).enable();
493 } 477 }
494 478
495 unsafe { 479 unsafe {
480 #[cfg(not(any(bdma, gpdma)))]
481 use crate::dma::{Burst, FifoThreshold};
482 use crate::dma::{Transfer, TransferOptions};
483
484 let dma_transfer_option = TransferOptions {
485 #[cfg(not(any(bdma, gpdma)))]
486 fifo_threshold: Some(FifoThreshold::Full),
487 #[cfg(not(any(bdma, gpdma)))]
488 mburst: Burst::Incr8,
489 ..Default::default()
490 };
491
492 match self.inner.bits() {
493 TimerBits::Bits16 => {
494 Transfer::new_write(
495 dma,
496 req,
497 duty,
498 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
499 dma_transfer_option,
500 )
501 .await
502 }
503 #[cfg(not(any(stm32l0)))]
504 TimerBits::Bits32 => {
496 #[cfg(not(any(bdma, gpdma)))] 505 #[cfg(not(any(bdma, gpdma)))]
497 use crate::dma::{Burst, FifoThreshold}; 506 panic!("unsupported timer bits");
498 use crate::dma::{Transfer, TransferOptions}; 507
499 508 #[cfg(any(bdma, gpdma))]
500 let dma_transfer_option = TransferOptions { 509 Transfer::new_write(
501 #[cfg(not(any(bdma, gpdma)))] 510 dma,
502 fifo_threshold: Some(FifoThreshold::Full), 511 req,
503 #[cfg(not(any(bdma, gpdma)))] 512 duty,
504 mburst: Burst::Incr8, 513 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
505 ..Default::default() 514 dma_transfer_option,
506 }; 515 )
507 516 .await
508 match self.inner.bits() {
509 TimerBits::Bits16 => {
510 Transfer::new_write(
511 dma,
512 req,
513 duty,
514 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
515 dma_transfer_option,
516 )
517 .await
518 }
519 #[cfg(not(any(stm32l0)))]
520 TimerBits::Bits32 => {
521 #[cfg(not(any(bdma, gpdma)))]
522 panic!("unsupported timer bits");
523
524 #[cfg(any(bdma, gpdma))]
525 Transfer::new_write(
526 dma,
527 req,
528 duty,
529 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
530 dma_transfer_option,
531 )
532 .await
533 }
534 };
535 };
536
537 // restore output compare state
538 if !original_enable_state {
539 self.channel(cc_channel).disable();
540 } 517 }
518 };
519 };
541 520
542 self.channel(cc_channel).set_duty_cycle(original_duty_state); 521 // restore output compare state
522 if !original_enable_state {
523 self.channel(cc_channel).disable();
524 }
543 525
544 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, 526 self.channel(cc_channel).set_duty_cycle(original_duty_state);
545 // this can almost always trigger a DMA FIFO error.
546 //
547 // optional TODO:
548 // clean FEIF after disable UDE
549 if !original_cc_dma_enabled {
550 self.inner.set_cc_dma_enable_state(cc_channel, false);
551 }
552 527
553 if !original_cc_dma_on_update { 528 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
554 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) 529 // this can almost always trigger a DMA FIFO error.
555 } 530 //
556 } 531 // optional TODO:
532 // clean FEIF after disable UDE
533 if !original_cc_dma_enabled {
534 self.inner.set_cc_dma_enable_state(cc_channel, false);
557 } 535 }
558 };
559}
560 536
561impl_waveform_chx!(waveform_ch1, Dma, Ch1); 537 if !original_cc_dma_on_update {
562impl_waveform_chx!(waveform_ch2, Dma, Ch2); 538 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
563impl_waveform_chx!(waveform_ch3, Dma, Ch3); 539 }
564impl_waveform_chx!(waveform_ch4, Dma, Ch4); 540 }
541}
565 542
566impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { 543impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
567 type Error = core::convert::Infallible; 544 type Error = core::convert::Infallible;
@@ -599,7 +576,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for Simpl
599} 576}
600 577
601impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { 578impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
602 type Channel = TimerChannel; 579 type Channel = Channel;
603 type Time = Hertz; 580 type Time = Hertz;
604 type Duty = u32; 581 type Duty = u32;
605 582