stm32f4xx_hal/timer/
pwm.rs

1//! Provides basic Pulse-width modulation (PWM) capabilities
2//!
3//! There are 2 main structures [`Pwm`] and [`PwmHz`]. Both structures implement [`embedded_hal_02::Pwm`] and have some additional API.
4//!
5//! First one is based on [`FTimer`] with fixed prescaler
6//! and easy to use with [`fugit::TimerDurationU32`] for setting pulse width and period without advanced calculations.
7//!
8//! Second one is based on [`Timer`] with dynamic internally calculated prescaler and require [`fugit::Hertz`] to set period.
9//!
10//! The main way to run PWM is calling [`FTimer::pwm`] with initial `period`/`frequency` corresponding PWM period.
11//! This returns [`PwmManager`] and a tuple of all [`PwmChannel`]s supported by timer.
12//! Also there is [`PwmExt`] trait implemented on `pac::TIMx` to simplify creating new structure.
13//!
14//! ```rust,ignore
15//! let (pwm_manager, (pwm_ch1, pwm_ch2, ..)) = dp.TIM1.pwm_us(100.micros(), &clocks);
16//! ```
17//!
18//! Each `PwmChannel` implements [`embedded_hal::pwm::SetDutyCycle`].
19//! They are disabled.
20//! To enable `PwmChannel` you need to pass one or more regular pins allowed by channel
21//! using `with` or `with_open_drain`.
22//! Also you can pass complementary pins by `.with_complementary(other_complementary_pin)`.
23//! After connecting pins you can dynamically enable main or complementary channels with `enable` and `enable_complementary`
24//! and change their polarity with `set_polarity` and `set_complementary_polarity`.
25//!
26//! ```rust,ignore
27//! let mut pwm_c1 = pwm_c1.with(gpioa.pa8).with_complementary(gpioa.pa7);
28//! pwm_c1.enable();
29//! pwm_c1.enable_complementary();
30//! ```
31//!
32//! By default `PwmChannel` contains information about connected pins to be possible to `release` them.
33//! But you can `erase` this information to constuct [`ErasedChannel`] which can be collected to array.
34//! Note that this operation is irreversible.
35//!
36//! `PwmManager` allows you to change PWM `period`/`frequency` and also has methods for advanced PWM control.
37
38use super::sealed::Split;
39use super::{
40    compute_arr_presc, Advanced, CPin, CenterAlignedMode, FTimer, IdleState, Instance, NCPin, Ocm,
41    Polarity, Timer, WithPwm,
42};
43pub use super::{Ch, C1, C2, C3, C4};
44use crate::gpio::{OpenDrain, PushPull};
45use crate::rcc::Clocks;
46use core::ops::{Deref, DerefMut};
47use fugit::{HertzU32 as Hertz, TimerDurationU32};
48
49pub trait PwmExt
50where
51    Self: Sized + Instance + WithPwm + Split,
52{
53    fn pwm<const FREQ: u32>(
54        self,
55        time: TimerDurationU32<FREQ>,
56        clocks: &Clocks,
57    ) -> (PwmManager<Self, FREQ>, Self::Channels);
58
59    fn pwm_hz(self, freq: Hertz, clocks: &Clocks) -> (PwmHzManager<Self>, Self::Channels);
60
61    fn pwm_us(
62        self,
63        time: TimerDurationU32<1_000_000>,
64        clocks: &Clocks,
65    ) -> (PwmManager<Self, 1_000_000>, Self::Channels) {
66        self.pwm::<1_000_000>(time, clocks)
67    }
68}
69
70impl<TIM> PwmExt for TIM
71where
72    Self: Sized + Instance + WithPwm + Split,
73{
74    fn pwm<const FREQ: u32>(
75        self,
76        time: TimerDurationU32<FREQ>,
77        clocks: &Clocks,
78    ) -> (PwmManager<Self, FREQ>, Self::Channels) {
79        FTimer::<Self, FREQ>::new(self, clocks).pwm(time)
80    }
81
82    fn pwm_hz(self, time: Hertz, clocks: &Clocks) -> (PwmHzManager<Self>, Self::Channels) {
83        Timer::new(self, clocks).pwm_hz(time)
84    }
85}
86
87impl<TIM: Instance + WithPwm + Split> Timer<TIM> {
88    pub fn pwm_hz(mut self, freq: Hertz) -> (PwmHzManager<TIM>, TIM::Channels) {
89        // The reference manual is a bit ambiguous about when enabling this bit is really
90        // necessary, but since we MUST enable the preload for the output channels then we
91        // might as well enable for the auto-reload too
92        self.tim.enable_preload(true);
93
94        let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw());
95        self.tim.set_prescaler(psc);
96        self.tim.set_auto_reload(arr).unwrap();
97
98        // Trigger update event to load the registers
99        self.tim.trigger_update();
100
101        self.tim.start_pwm();
102
103        (PwmHzManager { timer: self }, TIM::split())
104    }
105}
106
107impl<TIM: Instance + WithPwm + Split, const FREQ: u32> FTimer<TIM, FREQ> {
108    pub fn pwm(mut self, time: TimerDurationU32<FREQ>) -> (PwmManager<TIM, FREQ>, TIM::Channels) {
109        // The reference manual is a bit ambiguous about when enabling this bit is really
110        // necessary, but since we MUST enable the preload for the output channels then we
111        // might as well enable for the auto-reload too
112        self.tim.enable_preload(true);
113
114        self.tim.set_auto_reload(time.ticks() - 1).unwrap();
115
116        // Trigger update event to load the registers
117        self.tim.trigger_update();
118
119        self.tim.start_pwm();
120
121        (PwmManager { timer: self }, TIM::split())
122    }
123}
124
125pub struct PwmChannelDisabled<TIM, const C: u8> {
126    pub(super) tim: TIM,
127}
128
129impl<TIM: crate::Steal, const C: u8> PwmChannelDisabled<TIM, C> {
130    pub(crate) fn new() -> Self {
131        Self {
132            tim: unsafe { TIM::steal() },
133        }
134    }
135}
136impl<TIM: Instance + WithPwm + crate::Steal, const C: u8> PwmChannelDisabled<TIM, C>
137where
138    TIM: CPin<C>,
139{
140    pub fn with(
141        mut self,
142        pin: impl Into<TIM::Ch<PushPull>>,
143    ) -> PwmChannel<TIM, C, false, PushPull> {
144        self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1);
145        PwmChannel {
146            tim: self.tim,
147            lines: Lines::One(pin.into()),
148        }
149    }
150    pub fn with_open_drain(
151        mut self,
152        pin: impl Into<TIM::Ch<OpenDrain>>,
153    ) -> PwmChannel<TIM, C, false, OpenDrain> {
154        self.tim.preload_output_channel_in_mode(C, Ocm::PwmMode1);
155        PwmChannel {
156            tim: self.tim,
157            lines: Lines::One(pin.into()),
158        }
159    }
160}
161
162#[derive(Debug)]
163pub enum Lines<P> {
164    One(P),
165    Two(P, P),
166    Three(P, P, P),
167    Four(P, P, P, P),
168}
169impl<P> Lines<P> {
170    pub fn and(self, pin: P) -> Self {
171        match self {
172            Self::One(p) => Self::Two(p, pin),
173            Self::Two(p1, p2) => Self::Three(p1, p2, pin),
174            Self::Three(p1, p2, p3) => Self::Four(p1, p2, p3, pin),
175            Self::Four(_, _, _, _) => unreachable!(),
176        }
177    }
178}
179
180pub struct PwmChannel<TIM: CPin<C>, const C: u8, const COMP: bool = false, Otype = PushPull> {
181    pub(super) tim: TIM,
182    lines: Lines<TIM::Ch<Otype>>,
183    // TODO: add complementary pins
184}
185
186impl<TIM: Instance + WithPwm + CPin<C>, const C: u8, const COMP: bool, Otype>
187    PwmChannel<TIM, C, COMP, Otype>
188{
189    pub const fn channel(&self) -> u8 {
190        C
191    }
192    pub fn release(mut self) -> (PwmChannelDisabled<TIM, C>, Lines<TIM::Ch<Otype>>) {
193        self.tim.freeze_output_channel(C);
194        (PwmChannelDisabled { tim: self.tim }, self.lines)
195    }
196    pub fn erase(self) -> ErasedChannel<TIM> {
197        ErasedChannel {
198            _tim: self.tim,
199            channel: C,
200        }
201    }
202}
203impl<TIM: Instance + CPin<C>, const C: u8, const COMP: bool, Otype>
204    PwmChannel<TIM, C, COMP, Otype>
205{
206    pub fn with(self, pin: impl Into<TIM::Ch<Otype>>) -> Self {
207        Self {
208            tim: self.tim,
209            lines: self.lines.and(pin.into()),
210        }
211    }
212}
213impl<TIM: Instance + CPin<C> + NCPin<C>, const C: u8, const COMP: bool, Otype>
214    PwmChannel<TIM, C, COMP, Otype>
215{
216    pub fn with_complementary(
217        self,
218        pin: impl Into<TIM::ChN<Otype>>,
219    ) -> PwmChannel<TIM, C, true, Otype> {
220        let _pin = pin.into();
221        PwmChannel {
222            tim: self.tim,
223            lines: self.lines,
224        }
225    }
226}
227
228pub struct ErasedChannel<TIM> {
229    _tim: TIM,
230    channel: u8,
231}
232
233impl<TIM> ErasedChannel<TIM> {
234    pub const fn channel(&self) -> u8 {
235        self.channel
236    }
237}
238
239macro_rules! ch_impl {
240    () => {
241        /// Disable PWM channel
242        #[inline]
243        pub fn disable(&mut self) {
244            TIM::enable_channel(self.channel(), false);
245        }
246
247        /// Enable PWM channel
248        #[inline]
249        pub fn enable(&mut self) {
250            TIM::enable_channel(self.channel(), true);
251        }
252
253        /// Get PWM channel duty cycle
254        #[inline]
255        pub fn get_duty(&self) -> u16 {
256            TIM::read_cc_value(self.channel()) as u16
257        }
258
259        /// Get the maximum duty cycle value of the PWM channel
260        ///
261        /// If `0` returned means max_duty is 2^16
262        #[inline]
263        pub fn get_max_duty(&self) -> u16 {
264            (TIM::read_auto_reload() as u16).wrapping_add(1)
265        }
266
267        /// Set PWM channel duty cycle
268        #[inline]
269        pub fn set_duty(&mut self, duty: u16) {
270            TIM::set_cc_value(self.channel(), duty as u32)
271        }
272
273        /// Set PWM channel polarity
274        #[inline]
275        pub fn set_polarity(&mut self, p: Polarity) {
276            TIM::set_channel_polarity(self.channel(), p);
277        }
278
279        /// Set complementary PWM channel polarity
280        #[inline]
281        pub fn set_complementary_polarity(&mut self, p: Polarity) {
282            TIM::set_nchannel_polarity(self.channel(), p);
283        }
284    };
285}
286
287macro_rules! chN_impl {
288    () => {
289        /// Disable complementary PWM channel
290        #[inline]
291        pub fn disable_complementary(&mut self) {
292            TIM::enable_nchannel(self.channel(), false);
293        }
294
295        /// Enable complementary PWM channel
296        #[inline]
297        pub fn enable_complementary(&mut self) {
298            TIM::enable_nchannel(self.channel(), true);
299        }
300
301        /// Set PWM channel idle state
302        #[inline]
303        pub fn set_idle_state(&mut self, s: IdleState) {
304            TIM::idle_state(self.channel(), false, s);
305        }
306
307        /// Set complementary PWM channel idle state
308        #[inline]
309        pub fn set_complementary_idle_state(&mut self, s: IdleState) {
310            TIM::idle_state(self.channel(), true, s);
311        }
312    };
313}
314
315impl<TIM: Instance + WithPwm + CPin<C>, const C: u8, const COMP: bool, Otype>
316    PwmChannel<TIM, C, COMP, Otype>
317{
318    ch_impl!();
319}
320
321impl<TIM: Instance + WithPwm + Advanced + CPin<C>, const C: u8, Otype>
322    PwmChannel<TIM, C, true, Otype>
323{
324    chN_impl!();
325}
326
327impl<TIM: Instance + WithPwm> ErasedChannel<TIM> {
328    ch_impl!();
329}
330
331impl<TIM: Instance + WithPwm + Advanced> ErasedChannel<TIM> {
332    chN_impl!();
333}
334
335pub struct PwmManager<TIM, const FREQ: u32>
336where
337    TIM: Instance + WithPwm,
338{
339    pub(super) timer: FTimer<TIM, FREQ>,
340}
341
342impl<TIM, const FREQ: u32> PwmManager<TIM, FREQ>
343where
344    TIM: Instance + WithPwm + Split,
345{
346    pub fn release(mut self, _channels: TIM::Channels) -> FTimer<TIM, FREQ> {
347        // stop counter
348        self.tim.cr1_reset();
349        self.timer
350    }
351}
352
353impl<TIM, const FREQ: u32> Deref for PwmManager<TIM, FREQ>
354where
355    TIM: Instance + WithPwm,
356{
357    type Target = FTimer<TIM, FREQ>;
358    fn deref(&self) -> &Self::Target {
359        &self.timer
360    }
361}
362
363impl<TIM, const FREQ: u32> DerefMut for PwmManager<TIM, FREQ>
364where
365    TIM: Instance + WithPwm,
366{
367    fn deref_mut(&mut self) -> &mut Self::Target {
368        &mut self.timer
369    }
370}
371
372pub struct PwmHzManager<TIM>
373where
374    TIM: Instance + WithPwm,
375{
376    pub(super) timer: Timer<TIM>,
377}
378
379impl<TIM> PwmHzManager<TIM>
380where
381    TIM: Instance + WithPwm + Split,
382{
383    pub fn release(mut self, _channels: TIM::Channels) -> Timer<TIM> {
384        // stop timer
385        self.tim.cr1_reset();
386        self.timer
387    }
388}
389
390impl<TIM> Deref for PwmHzManager<TIM>
391where
392    TIM: Instance + WithPwm,
393{
394    type Target = Timer<TIM>;
395    fn deref(&self) -> &Self::Target {
396        &self.timer
397    }
398}
399
400impl<TIM> DerefMut for PwmHzManager<TIM>
401where
402    TIM: Instance + WithPwm,
403{
404    fn deref_mut(&mut self) -> &mut Self::Target {
405        &mut self.timer
406    }
407}
408
409impl<TIM, const FREQ: u32> PwmManager<TIM, FREQ>
410where
411    TIM: Instance + WithPwm,
412{
413    /// Get the maximum duty cycle value of the timer
414    ///
415    /// If `0` returned means max_duty is 2^16
416    pub fn get_max_duty(&self) -> u16 {
417        (TIM::read_auto_reload() as u16).wrapping_add(1)
418    }
419
420    /// Get the PWM frequency of the timer as a duration
421    pub fn get_period(&self) -> TimerDurationU32<FREQ> {
422        TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1)
423    }
424
425    /// Set the PWM frequency for the timer from a duration
426    pub fn set_period(&mut self, period: TimerDurationU32<FREQ>) {
427        self.tim.set_auto_reload(period.ticks() - 1).unwrap();
428        self.tim.cnt_reset();
429    }
430}
431
432impl<TIM> PwmHzManager<TIM>
433where
434    TIM: Instance + WithPwm,
435{
436    /// Get the maximum duty cycle value of the timer
437    ///
438    /// If `0` returned means max_duty is 2^16
439    pub fn get_max_duty(&self) -> u16 {
440        (TIM::read_auto_reload() as u16).wrapping_add(1)
441    }
442
443    /// Get the PWM frequency of the timer in Hertz
444    pub fn get_period(&self) -> Hertz {
445        let clk = self.clk;
446        let psc = self.tim.read_prescaler() as u32;
447        let arr = TIM::read_auto_reload();
448
449        // Length in ms of an internal clock pulse
450        clk / ((psc + 1) * (arr + 1))
451    }
452
453    /// Set the PWM frequency for the timer in Hertz
454    pub fn set_period(&mut self, period: Hertz) {
455        let clk = self.clk;
456
457        let (psc, arr) = compute_arr_presc(period.raw(), clk.raw());
458        self.tim.set_prescaler(psc);
459        self.tim.set_auto_reload(arr).unwrap();
460        self.tim.cnt_reset();
461    }
462}
463
464macro_rules! impl_advanced {
465    () => {
466        /// Set number DTS ticks during that the primary and complementary PWM pins are simultaneously forced to their inactive states
467        /// ( see [`Polarity`] setting ) when changing PWM state. This duration when both channels are in an 'off' state  is called 'dead time'.
468        ///
469        /// This is necessary in applications like motor control or power converters to prevent the destruction of the switching elements by
470        /// short circuit in the moment of switching.
471        #[inline]
472        pub fn set_dead_time(&mut self, dts_ticks: u16) {
473            let bits = pack_ceil_dead_time(dts_ticks);
474            TIM::set_dtg_value(bits);
475        }
476
477        /// Set raw dead time (DTG) bits
478        ///
479        /// The dead time generation is nonlinear and constrained by the DTS tick duration. DTG register configuration and calculation of
480        /// the actual resulting dead time is described in the application note RM0368 from ST Microelectronics
481        #[inline]
482        pub fn set_dead_time_bits(&mut self, bits: u8) {
483            TIM::set_dtg_value(bits);
484        }
485
486        /// Return dead time for complementary pins in the unit of DTS ticks
487        #[inline]
488        pub fn get_dead_time(&self) -> u16 {
489            unpack_dead_time(TIM::read_dtg_value())
490        }
491
492        /// Get raw dead time (DTG) bits
493        #[inline]
494        pub fn get_dead_time_bits(&self) -> u8 {
495            TIM::read_dtg_value()
496        }
497
498        /// Sets the alignment mode
499        #[inline]
500        pub fn set_cms(&mut self, mode: CenterAlignedMode) {
501            self.tim.enable_counter(false);
502            TIM::set_cms(mode);
503            self.tim.enable_counter(true);
504        }
505    };
506}
507
508impl<TIM, const FREQ: u32> PwmManager<TIM, FREQ>
509where
510    TIM: Instance + WithPwm + Advanced,
511{
512    impl_advanced!();
513}
514
515impl<TIM> PwmHzManager<TIM>
516where
517    TIM: Instance + WithPwm + Advanced,
518{
519    impl_advanced!();
520}
521
522/// Convert number dead time ticks to raw DTG register bits.
523/// Values greater than 1009 result in maximum dead time of 126 us
524const fn pack_ceil_dead_time(dts_ticks: u16) -> u8 {
525    match dts_ticks {
526        0..=127 => dts_ticks as u8,
527        128..=254 => ((((dts_ticks + 1) >> 1) - 64) as u8) | 0b_1000_0000,
528        255..=504 => ((((dts_ticks + 7) >> 3) - 32) as u8) | 0b_1100_0000,
529        505..=1008 => ((((dts_ticks + 15) >> 4) - 32) as u8) | 0b_1110_0000,
530        1009.. => 0xff,
531    }
532}
533
534/// Convert raw DTG register bits value to number of dead time ticks
535const fn unpack_dead_time(bits: u8) -> u16 {
536    if bits & 0b_1000_0000 == 0 {
537        bits as u16
538    } else if bits & 0b_0100_0000 == 0 {
539        (((bits & !0b_1000_0000) as u16) + 64) * 2
540    } else if bits & 0b_0010_0000 == 0 {
541        (((bits & !0b_1100_0000) as u16) + 32) * 8
542    } else {
543        (((bits & !0b_1110_0000) as u16) + 32) * 16
544    }
545}