embassy_stm32/timer/
simple_pwm.rs

1//! Simple PWM driver.
2
3use core::marker::PhantomData;
4use core::mem::ManuallyDrop;
5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin};
8#[cfg(gpio_v2)]
9use crate::gpio::Pull;
10use crate::gpio::{AfType, AnyPin, OutputType, Speed};
11use crate::time::Hertz;
12use crate::Peri;
13
14/// PWM pin wrapper.
15///
16/// This wraps a pin to make it usable with PWM.
17pub struct PwmPin<'d, T, C> {
18    _pin: Peri<'d, AnyPin>,
19    phantom: PhantomData<(T, C)>,
20}
21
22/// PWM pin config
23///
24/// This configures the pwm pin settings
25#[derive(Debug, Copy, Clone)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub struct PwmPinConfig {
28    /// PWM Pin output type
29    pub output_type: OutputType,
30    /// PWM Pin speed
31    pub speed: Speed,
32    /// PWM Pin pull type
33    #[cfg(gpio_v2)]
34    pub pull: Pull,
35}
36
37impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> {
38    /// Create a new PWM pin instance.
39    pub fn new(pin: Peri<'d, impl TimerPin<T, C>>, output_type: OutputType) -> Self {
40        critical_section::with(|_| {
41            pin.set_low();
42            pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
43        });
44        PwmPin {
45            _pin: pin.into(),
46            phantom: PhantomData,
47        }
48    }
49
50    /// Create a new PWM pin instance with config.
51    pub fn new_with_config(pin: Peri<'d, impl TimerPin<T, C>>, pin_config: PwmPinConfig) -> Self {
52        critical_section::with(|_| {
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}
68
69/// A single channel of a pwm, obtained from [`SimplePwm::split`],
70/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
71///
72/// It is not possible to change the pwm frequency because
73/// the frequency configuration is shared with all four channels.
74pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {
75    timer: ManuallyDrop<Timer<'d, T>>,
76    channel: Channel,
77}
78
79// TODO: check for RMW races
80impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
81    /// Enable the given channel.
82    pub fn enable(&mut self) {
83        self.timer.enable_channel(self.channel, true);
84    }
85
86    /// Disable the given channel.
87    pub fn disable(&mut self) {
88        self.timer.enable_channel(self.channel, false);
89    }
90
91    /// Check whether given channel is enabled
92    pub fn is_enabled(&self) -> bool {
93        self.timer.get_channel_enable_state(self.channel)
94    }
95
96    /// Get max duty value.
97    ///
98    /// This value depends on the configured frequency and the timer's clock rate from RCC.
99    pub fn max_duty_cycle(&self) -> u16 {
100        let max = self.timer.get_max_compare_value();
101        assert!(max < u16::MAX as u32);
102        max as u16 + 1
103    }
104
105    /// Set the duty for a given channel.
106    ///
107    /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
108    pub fn set_duty_cycle(&mut self, duty: u16) {
109        assert!(duty <= (*self).max_duty_cycle());
110        self.timer.set_compare_value(self.channel, duty.into())
111    }
112
113    /// Set the duty cycle to 0%, or always inactive.
114    pub fn set_duty_cycle_fully_off(&mut self) {
115        self.set_duty_cycle(0);
116    }
117
118    /// Set the duty cycle to 100%, or always active.
119    pub fn set_duty_cycle_fully_on(&mut self) {
120        self.set_duty_cycle((*self).max_duty_cycle());
121    }
122
123    /// Set the duty cycle to `num / denom`.
124    ///
125    /// The caller is responsible for ensuring that `num` is less than or equal to `denom`,
126    /// and that `denom` is not zero.
127    pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) {
128        assert!(denom != 0);
129        assert!(num <= denom);
130        let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);
131
132        // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)
133        #[allow(clippy::cast_possible_truncation)]
134        self.set_duty_cycle(duty as u16);
135    }
136
137    /// Set the duty cycle to `percent / 100`
138    ///
139    /// The caller is responsible for ensuring that `percent` is less than or equal to 100.
140    pub fn set_duty_cycle_percent(&mut self, percent: u8) {
141        self.set_duty_cycle_fraction(u16::from(percent), 100)
142    }
143
144    /// Get the duty for a given channel.
145    ///
146    /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
147    pub fn current_duty_cycle(&self) -> u16 {
148        unwrap!(self.timer.get_compare_value(self.channel).try_into())
149    }
150
151    /// Set the output polarity for a given channel.
152    pub fn set_polarity(&mut self, polarity: OutputPolarity) {
153        self.timer.set_output_polarity(self.channel, polarity);
154    }
155
156    /// Set the output compare mode for a given channel.
157    pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
158        self.timer.set_output_compare_mode(self.channel, mode);
159    }
160}
161
162/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
163pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> {
164    /// Channel 1
165    pub ch1: SimplePwmChannel<'d, T>,
166    /// Channel 2
167    pub ch2: SimplePwmChannel<'d, T>,
168    /// Channel 3
169    pub ch3: SimplePwmChannel<'d, T>,
170    /// Channel 4
171    pub ch4: SimplePwmChannel<'d, T>,
172}
173
174/// Simple PWM driver.
175pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
176    inner: Timer<'d, T>,
177}
178
179impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
180    /// Create a new simple PWM driver.
181    pub fn new(
182        tim: Peri<'d, T>,
183        _ch1: Option<PwmPin<'d, T, Ch1>>,
184        _ch2: Option<PwmPin<'d, T, Ch2>>,
185        _ch3: Option<PwmPin<'d, T, Ch3>>,
186        _ch4: Option<PwmPin<'d, T, Ch4>>,
187        freq: Hertz,
188        counting_mode: CountingMode,
189    ) -> Self {
190        Self::new_inner(tim, freq, counting_mode)
191    }
192
193    fn new_inner(tim: Peri<'d, T>, freq: Hertz, counting_mode: CountingMode) -> Self {
194        let mut this = Self { inner: Timer::new(tim) };
195
196        this.inner.set_counting_mode(counting_mode);
197        this.set_frequency(freq);
198        this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
199        this.inner.start();
200
201        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
202            .iter()
203            .for_each(|&channel| {
204                this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
205
206                this.inner.set_output_compare_preload(channel, true);
207            });
208
209        this
210    }
211
212    /// Get a single channel
213    ///
214    /// If you need to use multiple channels, use [`Self::split`].
215    pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> {
216        SimplePwmChannel {
217            timer: unsafe { self.inner.clone_unchecked() },
218            channel,
219        }
220    }
221
222    /// Channel 1
223    ///
224    /// This is just a convenience wrapper around [`Self::channel`].
225    ///
226    /// If you need to use multiple channels, use [`Self::split`].
227    pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> {
228        self.channel(Channel::Ch1)
229    }
230
231    /// Channel 2
232    ///
233    /// This is just a convenience wrapper around [`Self::channel`].
234    ///
235    /// If you need to use multiple channels, use [`Self::split`].
236    pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> {
237        self.channel(Channel::Ch2)
238    }
239
240    /// Channel 3
241    ///
242    /// This is just a convenience wrapper around [`Self::channel`].
243    ///
244    /// If you need to use multiple channels, use [`Self::split`].
245    pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> {
246        self.channel(Channel::Ch3)
247    }
248
249    /// Channel 4
250    ///
251    /// This is just a convenience wrapper around [`Self::channel`].
252    ///
253    /// If you need to use multiple channels, use [`Self::split`].
254    pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> {
255        self.channel(Channel::Ch4)
256    }
257
258    /// Splits a [`SimplePwm`] into four pwm channels.
259    ///
260    /// This returns all four channels, including channels that
261    /// aren't configured with a [`PwmPin`].
262    // TODO: I hate the name "split"
263    pub fn split(self) -> SimplePwmChannels<'static, T>
264    where
265        // must be static because the timer will never be dropped/disabled
266        'd: 'static,
267    {
268        // without this, the timer would be disabled at the end of this function
269        let timer = ManuallyDrop::new(self.inner);
270
271        let ch = |channel| SimplePwmChannel {
272            timer: unsafe { timer.clone_unchecked() },
273            channel,
274        };
275
276        SimplePwmChannels {
277            ch1: ch(Channel::Ch1),
278            ch2: ch(Channel::Ch2),
279            ch3: ch(Channel::Ch3),
280            ch4: ch(Channel::Ch4),
281        }
282    }
283
284    /// Set PWM frequency.
285    ///
286    /// Note: when you call this, the max duty value changes, so you will have to
287    /// call `set_duty` on all channels with the duty calculated based on the new max duty.
288    pub fn set_frequency(&mut self, freq: Hertz) {
289        // TODO: prevent ARR = u16::MAX?
290        let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
291            2u8
292        } else {
293            1u8
294        };
295        self.inner.set_frequency_internal(freq * multiplier, 16);
296    }
297
298    /// Get max duty value.
299    ///
300    /// This value depends on the configured frequency and the timer's clock rate from RCC.
301    pub fn max_duty_cycle(&self) -> u16 {
302        let max = self.inner.get_max_compare_value();
303        assert!(max < u16::MAX as u32);
304        max as u16 + 1
305    }
306
307    /// Generate a sequence of PWM waveform
308    ///
309    /// Note:
310    /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
311    pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
312        #[allow(clippy::let_unit_value)] // eg. stm32f334
313        let req = dma.request();
314
315        let original_duty_state = self.channel(channel).current_duty_cycle();
316        let original_enable_state = self.channel(channel).is_enabled();
317        let original_update_dma_state = self.inner.get_update_dma_state();
318
319        if !original_update_dma_state {
320            self.inner.enable_update_dma(true);
321        }
322
323        if !original_enable_state {
324            self.channel(channel).enable();
325        }
326
327        unsafe {
328            #[cfg(not(any(bdma, gpdma)))]
329            use crate::dma::{Burst, FifoThreshold};
330            use crate::dma::{Transfer, TransferOptions};
331
332            let dma_transfer_option = TransferOptions {
333                #[cfg(not(any(bdma, gpdma)))]
334                fifo_threshold: Some(FifoThreshold::Full),
335                #[cfg(not(any(bdma, gpdma)))]
336                mburst: Burst::Incr8,
337                ..Default::default()
338            };
339
340            Transfer::new_write(
341                dma,
342                req,
343                duty,
344                self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
345                dma_transfer_option,
346            )
347            .await
348        };
349
350        // restore output compare state
351        if !original_enable_state {
352            self.channel(channel).disable();
353        }
354
355        self.channel(channel).set_duty_cycle(original_duty_state);
356
357        // Since DMA is closed before timer update event trigger DMA is turn off,
358        // this can almost always trigger a DMA FIFO error.
359        //
360        // optional TODO:
361        // clean FEIF after disable UDE
362        if !original_update_dma_state {
363            self.inner.enable_update_dma(false);
364        }
365    }
366
367    /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
368    ///
369    /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
370    /// in sequence on each update event (UEV). The data is written via the DMAR register using the
371    /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
372    ///
373    /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
374    /// represents a single update event and each column corresponds to a specific timer channel (starting
375    /// from `starting_channel` up to and including `ending_channel`).
376    ///
377    /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
378    ///
379    /// let dma_buf: [u16; 16] = [
380    ///     ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
381    ///     ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
382    ///     ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
383    ///     ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
384    /// ];
385    ///
386    /// Each group of N values (where N = number of channels) is transferred on one update event,
387    /// updating the duty cycles of all selected channels simultaneously.
388    ///
389    /// Note:
390    /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
391    pub async fn waveform_up_multi_channel(
392        &mut self,
393        dma: Peri<'_, impl super::UpDma<T>>,
394        starting_channel: Channel,
395        ending_channel: Channel,
396        duty: &[u16],
397    ) {
398        let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32;
399        let start_ch_index = starting_channel.index();
400        let end_ch_index = ending_channel.index();
401
402        assert!(start_ch_index <= end_ch_index);
403
404        let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
405        self.inner
406            .regs_gp16()
407            .dcr()
408            .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
409        self.inner
410            .regs_gp16()
411            .dcr()
412            .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));
413
414        #[allow(clippy::let_unit_value)] // eg. stm32f334
415        let req = dma.request();
416
417        let original_update_dma_state = self.inner.get_update_dma_state();
418        if !original_update_dma_state {
419            self.inner.enable_update_dma(true);
420        }
421
422        unsafe {
423            #[cfg(not(any(bdma, gpdma)))]
424            use crate::dma::{Burst, FifoThreshold};
425            use crate::dma::{Transfer, TransferOptions};
426
427            let dma_transfer_option = TransferOptions {
428                #[cfg(not(any(bdma, gpdma)))]
429                fifo_threshold: Some(FifoThreshold::Full),
430                #[cfg(not(any(bdma, gpdma)))]
431                mburst: Burst::Incr4,
432                ..Default::default()
433            };
434
435            Transfer::new_write(
436                dma,
437                req,
438                duty,
439                self.inner.regs_gp16().dmar().as_ptr() as *mut u16,
440                dma_transfer_option,
441            )
442            .await
443        };
444
445        if !original_update_dma_state {
446            self.inner.enable_update_dma(false);
447        }
448    }
449}
450
451impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
452    /// Generate a sequence of PWM waveform
453    pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
454        use crate::pac::timer::vals::Ccds;
455
456        #[allow(clippy::let_unit_value)] // eg. stm32f334
457        let req = dma.request();
458
459        let cc_channel = C::CHANNEL;
460
461        let original_duty_state = self.channel(cc_channel).current_duty_cycle();
462        let original_enable_state = self.channel(cc_channel).is_enabled();
463        let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
464        let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
465
466        // redirect CC DMA request onto Update Event
467        if !original_cc_dma_on_update {
468            self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
469        }
470
471        if !original_cc_dma_enabled {
472            self.inner.set_cc_dma_enable_state(cc_channel, true);
473        }
474
475        if !original_enable_state {
476            self.channel(cc_channel).enable();
477        }
478
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 => {
505                    #[cfg(not(any(bdma, gpdma)))]
506                    panic!("unsupported timer bits");
507
508                    #[cfg(any(bdma, gpdma))]
509                    Transfer::new_write(
510                        dma,
511                        req,
512                        duty,
513                        self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
514                        dma_transfer_option,
515                    )
516                    .await
517                }
518            };
519        };
520
521        // restore output compare state
522        if !original_enable_state {
523            self.channel(cc_channel).disable();
524        }
525
526        self.channel(cc_channel).set_duty_cycle(original_duty_state);
527
528        // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
529        // this can almost always trigger a DMA FIFO error.
530        //
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);
535        }
536
537        if !original_cc_dma_on_update {
538            self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
539        }
540    }
541}
542
543impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
544    type Error = core::convert::Infallible;
545}
546
547impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
548    fn max_duty_cycle(&self) -> u16 {
549        self.max_duty_cycle()
550    }
551
552    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
553        self.set_duty_cycle(duty);
554        Ok(())
555    }
556
557    fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> {
558        self.set_duty_cycle_fully_off();
559        Ok(())
560    }
561
562    fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> {
563        self.set_duty_cycle_fully_on();
564        Ok(())
565    }
566
567    fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
568        self.set_duty_cycle_fraction(num, denom);
569        Ok(())
570    }
571
572    fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> {
573        self.set_duty_cycle_percent(percent);
574        Ok(())
575    }
576}
577
578impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
579    type Channel = Channel;
580    type Time = Hertz;
581    type Duty = u32;
582
583    fn disable(&mut self, channel: Self::Channel) {
584        self.inner.enable_channel(channel, false);
585    }
586
587    fn enable(&mut self, channel: Self::Channel) {
588        self.inner.enable_channel(channel, true);
589    }
590
591    fn get_period(&self) -> Self::Time {
592        self.inner.get_frequency()
593    }
594
595    fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
596        self.inner.get_compare_value(channel)
597    }
598
599    fn get_max_duty(&self) -> Self::Duty {
600        self.inner.get_max_compare_value() + 1
601    }
602
603    fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
604        assert!(duty <= self.max_duty_cycle() as u32);
605        self.inner.set_compare_value(channel, duty)
606    }
607
608    fn set_period<P>(&mut self, period: P)
609    where
610        P: Into<Self::Time>,
611    {
612        self.inner.set_frequency(period.into());
613    }
614}