py32_hal/timer/
simple_pwm.rs

1//! Simple PWM driver.
2
3// The following code is modified from embassy-stm32
4// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32
5// Special thanks to the Embassy Project and its contributors for their work!
6
7use core::marker::PhantomData;
8use core::mem::ManuallyDrop;
9
10use embassy_hal_internal::{into_ref, PeripheralRef};
11
12use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
13use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel};
14use crate::gpio::{AfType, AnyPin, OutputType, Speed};
15use crate::time::Hertz;
16use crate::Peripheral;
17
18/// Channel 1 marker type.
19pub enum Ch1 {}
20/// Channel 2 marker type.
21pub enum Ch2 {}
22/// Channel 3 marker type.
23pub enum Ch3 {}
24/// Channel 4 marker type.
25pub enum Ch4 {}
26
27/// PWM pin wrapper.
28///
29/// This wraps a pin to make it usable with PWM.
30pub struct PwmPin<'d, T, C> {
31    _pin: PeripheralRef<'d, AnyPin>,
32    phantom: PhantomData<(T, C)>,
33}
34
35macro_rules! channel_impl {
36    ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
37        impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> {
38            #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
39            pub fn $new_chx(
40                pin: impl Peripheral<P = impl $pin_trait<T>> + 'd,
41                output_type: OutputType,
42            ) -> Self {
43                into_ref!(pin);
44                critical_section::with(|_| {
45                    pin.set_low();
46                    pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
47                });
48                PwmPin {
49                    _pin: pin.map_into(),
50                    phantom: PhantomData,
51                }
52            }
53        }
54    };
55}
56
57channel_impl!(new_ch1, Ch1, Channel1Pin);
58channel_impl!(new_ch2, Ch2, Channel2Pin);
59channel_impl!(new_ch3, Ch3, Channel3Pin);
60channel_impl!(new_ch4, Ch4, Channel4Pin);
61
62/// A single channel of a pwm, obtained from [`SimplePwm::split`],
63/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
64///
65/// It is not possible to change the pwm frequency because
66/// the frequency configuration is shared with all four channels.
67pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {
68    timer: ManuallyDrop<Timer<'d, T>>,
69    channel: Channel,
70}
71
72// TODO: check for RMW races
73impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
74    /// Enable the given channel.
75    pub fn enable(&mut self) {
76        self.timer.enable_channel(self.channel, true);
77    }
78
79    /// Disable the given channel.
80    pub fn disable(&mut self) {
81        self.timer.enable_channel(self.channel, false);
82    }
83
84    /// Check whether given channel is enabled
85    pub fn is_enabled(&self) -> bool {
86        self.timer.get_channel_enable_state(self.channel)
87    }
88
89    /// Get max duty value.
90    ///
91    /// This value depends on the configured frequency and the timer's clock rate from RCC.
92    pub fn max_duty_cycle(&self) -> u16 {
93        let max = self.timer.get_max_compare_value();
94        assert!(max < u16::MAX as u32);
95        max as u16 + 1
96    }
97
98    /// Set the duty for a given channel.
99    ///
100    /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
101    pub fn set_duty_cycle(&mut self, duty: u16) {
102        assert!(duty <= (*self).max_duty_cycle());
103        self.timer.set_compare_value(self.channel, duty.into())
104    }
105
106    /// Set the duty cycle to 0%, or always inactive.
107    pub fn set_duty_cycle_fully_off(&mut self) {
108        self.set_duty_cycle(0);
109    }
110
111    /// Set the duty cycle to 100%, or always active.
112    pub fn set_duty_cycle_fully_on(&mut self) {
113        self.set_duty_cycle((*self).max_duty_cycle());
114    }
115
116    /// Set the duty cycle to `num / denom`.
117    ///
118    /// The caller is responsible for ensuring that `num` is less than or equal to `denom`,
119    /// and that `denom` is not zero.
120    pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) {
121        assert!(denom != 0);
122        assert!(num <= denom);
123        let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);
124
125        // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)
126        #[allow(clippy::cast_possible_truncation)]
127        self.set_duty_cycle(duty as u16);
128    }
129
130    /// Set the duty cycle to `percent / 100`
131    ///
132    /// The caller is responsible for ensuring that `percent` is less than or equal to 100.
133    pub fn set_duty_cycle_percent(&mut self, percent: u8) {
134        self.set_duty_cycle_fraction(u16::from(percent), 100)
135    }
136
137    /// Get the duty for a given channel.
138    ///
139    /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
140    pub fn current_duty_cycle(&self) -> u16 {
141        unwrap!(self.timer.get_compare_value(self.channel).try_into())
142    }
143
144    /// Set the output polarity for a given channel.
145    pub fn set_polarity(&mut self, polarity: OutputPolarity) {
146        self.timer.set_output_polarity(self.channel, polarity);
147    }
148
149    /// Set the output compare mode for a given channel.
150    pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
151        self.timer.set_output_compare_mode(self.channel, mode);
152    }
153}
154
155/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
156pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> {
157    /// Channel 1
158    pub ch1: SimplePwmChannel<'d, T>,
159    /// Channel 2
160    pub ch2: SimplePwmChannel<'d, T>,
161    /// Channel 3
162    pub ch3: SimplePwmChannel<'d, T>,
163    /// Channel 4
164    pub ch4: SimplePwmChannel<'d, T>,
165}
166
167/// Simple PWM driver.
168pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
169    inner: Timer<'d, T>,
170}
171
172impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
173    /// Create a new simple PWM driver.
174    pub fn new(
175        tim: impl Peripheral<P = T> + 'd,
176        _ch1: Option<PwmPin<'d, T, Ch1>>,
177        _ch2: Option<PwmPin<'d, T, Ch2>>,
178        _ch3: Option<PwmPin<'d, T, Ch3>>,
179        _ch4: Option<PwmPin<'d, T, Ch4>>,
180        freq: Hertz,
181        counting_mode: CountingMode,
182    ) -> Self {
183        Self::new_inner(tim, freq, counting_mode)
184    }
185
186    fn new_inner(
187        tim: impl Peripheral<P = T> + 'd,
188        freq: Hertz,
189        counting_mode: CountingMode,
190    ) -> Self {
191        let mut this = Self {
192            inner: Timer::new(tim),
193        };
194
195        this.inner.set_counting_mode(counting_mode);
196        this.set_frequency(freq);
197        this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
198        this.inner.start();
199
200        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
201            .iter()
202            .for_each(|&channel| {
203                this.inner
204                    .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(freq * multiplier);
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(
312    //     &mut self,
313    //     dma: impl Peripheral<P = impl super::UpDma<T>>,
314    //     channel: Channel,
315    //     duty: &[u16],
316    // ) {
317    //     into_ref!(dma);
318
319    //     #[allow(clippy::let_unit_value)] // eg. stm32f334
320    //     let req = dma.request();
321
322    //     let original_duty_state = self.channel(channel).current_duty_cycle();
323    //     let original_enable_state = self.channel(channel).is_enabled();
324    //     let original_update_dma_state = self.inner.get_update_dma_state();
325
326    //     if !original_update_dma_state {
327    //         self.inner.enable_update_dma(true);
328    //     }
329
330    //     if !original_enable_state {
331    //         self.channel(channel).enable();
332    //     }
333
334    //     unsafe {
335    //         #[cfg(not(any(bdma, gpdma)))]
336    //         use crate::dma::{Burst, FifoThreshold};
337    //         use crate::dma::{Transfer, TransferOptions};
338
339    //         let dma_transfer_option = TransferOptions {
340    //             #[cfg(not(any(bdma, gpdma)))]
341    //             fifo_threshold: Some(FifoThreshold::Full),
342    //             #[cfg(not(any(bdma, gpdma)))]
343    //             mburst: Burst::Incr8,
344    //             ..Default::default()
345    //         };
346
347    //         Transfer::new_write(
348    //             &mut dma,
349    //             req,
350    //             duty,
351    //             self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
352    //             dma_transfer_option,
353    //         )
354    //         .await
355    //     };
356
357    //     // restore output compare state
358    //     if !original_enable_state {
359    //         self.channel(channel).disable();
360    //     }
361
362    //     self.channel(channel).set_duty_cycle(original_duty_state);
363
364    //     // Since DMA is closed before timer update event trigger DMA is turn off,
365    //     // this can almost always trigger a DMA FIFO error.
366    //     //
367    //     // optional TODO:
368    //     // clean FEIF after disable UDE
369    //     if !original_update_dma_state {
370    //         self.inner.enable_update_dma(false);
371    //     }
372    // }
373}
374
375// macro_rules! impl_waveform_chx {
376//     ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
377//         impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
378//             /// Generate a sequence of PWM waveform
379//             ///
380//             /// Note:
381//             /// you will need to provide corresponding TIMx_CHy DMA channel to use this method.
382//             pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) {
383//                 use crate::pac::timer::vals::Ccds;
384
385//                 into_ref!(dma);
386
387//                 #[allow(clippy::let_unit_value)] // eg. stm32f334
388//                 let req = dma.request();
389
390//                 let cc_channel = Channel::$cc_ch;
391
392//                 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
393//                 let original_enable_state = self.channel(cc_channel).is_enabled();
394//                 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE;
395//                 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
396
397//                 // redirect CC DMA request onto Update Event
398//                 if !original_cc_dma_on_update {
399//                     self.inner.set_cc_dma_selection(Ccds::ONUPDATE)
400//                 }
401
402//                 if !original_cc_dma_enabled {
403//                     self.inner.set_cc_dma_enable_state(cc_channel, true);
404//                 }
405
406//                 if !original_enable_state {
407//                     self.channel(cc_channel).enable();
408//                 }
409
410//                 unsafe {
411//                     #[cfg(not(any(bdma, gpdma)))]
412//                     use crate::dma::{Burst, FifoThreshold};
413//                     use crate::dma::{Transfer, TransferOptions};
414
415//                     let dma_transfer_option = TransferOptions {
416//                         #[cfg(not(any(bdma, gpdma)))]
417//                         fifo_threshold: Some(FifoThreshold::Full),
418//                         #[cfg(not(any(bdma, gpdma)))]
419//                         mburst: Burst::Incr8,
420//                         ..Default::default()
421//                     };
422
423//                     Transfer::new_write(
424//                         &mut dma,
425//                         req,
426//                         duty,
427//                         self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
428//                         dma_transfer_option,
429//                     )
430//                     .await
431//                 };
432
433//                 // restore output compare state
434//                 if !original_enable_state {
435//                     self.channel(cc_channel).disable();
436//                 }
437
438//                 self.channel(cc_channel).set_duty_cycle(original_duty_state);
439
440//                 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
441//                 // this can almost always trigger a DMA FIFO error.
442//                 //
443//                 // optional TODO:
444//                 // clean FEIF after disable UDE
445//                 if !original_cc_dma_enabled {
446//                     self.inner.set_cc_dma_enable_state(cc_channel, false);
447//                 }
448
449//                 if !original_cc_dma_on_update {
450//                     self.inner.set_cc_dma_selection(Ccds::ONCOMPARE)
451//                 }
452//             }
453//         }
454//     };
455// }
456
457// impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
458// impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
459// impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
460// impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
461
462impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
463    type Error = core::convert::Infallible;
464}
465
466impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
467    fn max_duty_cycle(&self) -> u16 {
468        self.max_duty_cycle()
469    }
470
471    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
472        self.set_duty_cycle(duty);
473        Ok(())
474    }
475
476    fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> {
477        self.set_duty_cycle_fully_off();
478        Ok(())
479    }
480
481    fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> {
482        self.set_duty_cycle_fully_on();
483        Ok(())
484    }
485
486    fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
487        self.set_duty_cycle_fraction(num, denom);
488        Ok(())
489    }
490
491    fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> {
492        self.set_duty_cycle_percent(percent);
493        Ok(())
494    }
495}
496
497impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
498    type Channel = Channel;
499    type Time = Hertz;
500    type Duty = u32;
501
502    fn disable(&mut self, channel: Self::Channel) {
503        self.inner.enable_channel(channel, false);
504    }
505
506    fn enable(&mut self, channel: Self::Channel) {
507        self.inner.enable_channel(channel, true);
508    }
509
510    fn get_period(&self) -> Self::Time {
511        self.inner.get_frequency()
512    }
513
514    fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
515        self.inner.get_compare_value(channel)
516    }
517
518    fn get_max_duty(&self) -> Self::Duty {
519        self.inner.get_max_compare_value() + 1
520    }
521
522    fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
523        assert!(duty <= self.max_duty_cycle() as u32);
524        self.inner.set_compare_value(channel, duty)
525    }
526
527    fn set_period<P>(&mut self, period: P)
528    where
529        P: Into<Self::Time>,
530    {
531        self.inner.set_frequency(period.into());
532    }
533}