stm32f1xx_hal/timer/
pwm.rs

1/*!
2  # Pulse width modulation
3
4  The general purpose timers (`TIM2`, `TIM3`, and `TIM4`) can be used to output
5  pulse width modulated signals on some pins. The timers support up to 4
6  simultaneous pwm outputs in separate `Channels`
7
8  ## Usage for pre-defined channel combinations
9
10  This crate only defines basic channel combinations for default AFIO remappings,
11  where all the channels are enabled. Start by setting all the pins for the
12  timer you want to use to alternate push pull pins:
13
14  ```rust
15  let gpioa = ..; // Set up and split GPIOA
16  // Select the pins you want to use
17  let pins = (
18      gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl),
19      gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl),
20      gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl),
21      gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl),
22  );
23
24  // Set up the timer as a PWM output. If selected pins may correspond to different remap options,
25  // then you must specify the remap generic parameter. Otherwise, if there is no such ambiguity,
26  // the remap generic parameter can be omitted without complains from the compiler.
27  let (c1, c2, c3, c4) = Timer::tim2(p.TIM2, &clocks)
28      .pwm_hz::<Tim2NoRemap, _, _>(pins, &mut afio.mapr, 1.kHz())
29      .3;
30
31  // Start using the channels
32  c1.set_duty(c1.get_max_duty());
33  // ...
34  ```
35
36  Then call the `pwm` function on the corresponding timer.
37
38  NOTE: In some cases you need to specify remap you need, especially for TIM2
39  (see [Alternate function remapping](super)):
40
41  ```
42    let device: pac::Peripherals = ..;
43
44    // Put the timer in PWM mode using the specified pins
45    // with a frequency of 100 Hz.
46    let (c0, c1, c2, c3) = Timer::tim2(device.TIM2, &clocks)
47        .pwm_hz::<Tim2NoRemap, _, _>(pins, &mut afio.mapr, 100.Hz());
48
49    // Set the duty cycle of channel 0 to 50%
50    c0.set_duty(c0.get_max_duty() / 2);
51    // PWM outputs are disabled by default
52    c0.enable()
53  ```
54*/
55
56use crate::afio::MAPR;
57use crate::gpio::{self, Alternate};
58
59use super::{compute_arr_presc, Channel, FTimer, Instance, Ocm, Timer, WithPwm};
60use crate::rcc::Clocks;
61use core::marker::PhantomData;
62use core::ops::{Deref, DerefMut};
63use fugit::{HertzU32 as Hertz, TimerDurationU32};
64
65pub trait Pins<REMAP, P> {
66    const C1: bool = false;
67    const C2: bool = false;
68    const C3: bool = false;
69    const C4: bool = false;
70    type Channels;
71
72    fn check_used(c: Channel) -> Channel {
73        if (c == Channel::C1 && Self::C1)
74            || (c == Channel::C2 && Self::C2)
75            || (c == Channel::C3 && Self::C3)
76            || (c == Channel::C4 && Self::C4)
77        {
78            c
79        } else {
80            panic!("Unused channel")
81        }
82    }
83
84    fn split() -> Self::Channels;
85}
86
87pub use super::{pins::sealed::Remap, CPin, Ch, C1, C2, C3, C4};
88
89pub struct PwmChannel<TIM, const C: u8> {
90    pub(super) _tim: PhantomData<TIM>,
91}
92
93macro_rules! pins_impl {
94    ( $( ( $($PINX:ident),+ ), ( $($ENCHX:ident),+ ); )+ ) => {
95        $(
96            #[allow(unused_parens)]
97            impl<TIM, REMAP, OUTMODE, $($PINX,)+> Pins<REMAP, ($(Ch<$ENCHX>),+)> for ($($PINX),+)
98            where
99                TIM: Instance + WithPwm,
100                REMAP: Remap<Periph = TIM>,
101                $($PINX: CPin<REMAP, $ENCHX> + gpio::PinExt<Mode=Alternate<OUTMODE>>,)+
102            {
103                $(const $ENCHX: bool = true;)+
104                type Channels = ($(PwmChannel<TIM, $ENCHX>),+);
105                fn split() -> Self::Channels {
106                    ($(PwmChannel::<TIM, $ENCHX>::new()),+)
107                }
108            }
109        )+
110    };
111}
112
113pins_impl!(
114    (P1, P2, P3, P4), (C1, C2, C3, C4);
115    (P2, P3, P4), (C2, C3, C4);
116    (P1, P3, P4), (C1, C3, C4);
117    (P1, P2, P4), (C1, C2, C4);
118    (P1, P2, P3), (C1, C2, C3);
119    (P3, P4), (C3, C4);
120    (P2, P4), (C2, C4);
121    (P2, P3), (C2, C3);
122    (P1, P4), (C1, C4);
123    (P1, P3), (C1, C3);
124    (P1, P2), (C1, C2);
125    (P1), (C1);
126    (P2), (C2);
127    (P3), (C3);
128    (P4), (C4);
129);
130
131pub trait PwmExt
132where
133    Self: Sized + Instance + WithPwm,
134{
135    fn pwm<REMAP, P, PINS, const FREQ: u32>(
136        self,
137        pins: PINS,
138        mapr: &mut MAPR,
139        time: TimerDurationU32<FREQ>,
140        clocks: &Clocks,
141    ) -> Pwm<Self, REMAP, P, PINS, FREQ>
142    where
143        REMAP: Remap<Periph = Self>,
144        PINS: Pins<REMAP, P>;
145
146    fn pwm_hz<REMAP, P, PINS>(
147        self,
148        pins: PINS,
149        mapr: &mut MAPR,
150        freq: Hertz,
151        clocks: &Clocks,
152    ) -> PwmHz<Self, REMAP, P, PINS>
153    where
154        REMAP: Remap<Periph = Self>,
155        PINS: Pins<REMAP, P>;
156
157    fn pwm_us<REMAP, P, PINS>(
158        self,
159        pins: PINS,
160        mapr: &mut MAPR,
161        time: TimerDurationU32<1_000_000>,
162        clocks: &Clocks,
163    ) -> Pwm<Self, REMAP, P, PINS, 1_000_000>
164    where
165        REMAP: Remap<Periph = Self>,
166        PINS: Pins<REMAP, P>,
167    {
168        self.pwm::<_, _, _, 1_000_000>(pins, mapr, time, clocks)
169    }
170}
171
172impl<TIM> PwmExt for TIM
173where
174    Self: Sized + Instance + WithPwm,
175{
176    fn pwm<REMAP, P, PINS, const FREQ: u32>(
177        self,
178        pins: PINS,
179        mapr: &mut MAPR,
180        time: TimerDurationU32<FREQ>,
181        clocks: &Clocks,
182    ) -> Pwm<TIM, REMAP, P, PINS, FREQ>
183    where
184        REMAP: Remap<Periph = Self>,
185        PINS: Pins<REMAP, P>,
186    {
187        FTimer::<Self, FREQ>::new(self, clocks).pwm(pins, mapr, time)
188    }
189
190    fn pwm_hz<REMAP, P, PINS>(
191        self,
192        pins: PINS,
193        mapr: &mut MAPR,
194        time: Hertz,
195        clocks: &Clocks,
196    ) -> PwmHz<TIM, REMAP, P, PINS>
197    where
198        REMAP: Remap<Periph = Self>,
199        PINS: Pins<REMAP, P>,
200    {
201        Timer::new(self, clocks).pwm_hz(pins, mapr, time)
202    }
203}
204
205impl<TIM: Instance + WithPwm, const C: u8> PwmChannel<TIM, C> {
206    pub(crate) fn new() -> Self {
207        Self {
208            _tim: core::marker::PhantomData,
209        }
210    }
211}
212
213impl<TIM: Instance + WithPwm, const C: u8> PwmChannel<TIM, C> {
214    #[inline]
215    pub fn disable(&mut self) {
216        TIM::enable_channel(C, false);
217    }
218
219    #[inline]
220    pub fn enable(&mut self) {
221        TIM::enable_channel(C, true);
222    }
223
224    #[inline]
225    pub fn get_duty(&self) -> u16 {
226        TIM::read_cc_value(C) as u16
227    }
228
229    /// If `0` returned means max_duty is 2^16
230    #[inline]
231    pub fn get_max_duty(&self) -> u16 {
232        (TIM::read_auto_reload() as u16).wrapping_add(1)
233    }
234
235    #[inline]
236    pub fn set_duty(&mut self, duty: u16) {
237        TIM::set_cc_value(C, duty as u32)
238    }
239}
240
241pub struct PwmHz<TIM, REMAP, P, PINS>
242where
243    TIM: Instance + WithPwm,
244    REMAP: Remap<Periph = TIM>,
245    PINS: Pins<REMAP, P>,
246{
247    timer: Timer<TIM>,
248    _pins: PhantomData<(REMAP, P, PINS)>,
249}
250
251impl<TIM, REMAP, P, PINS> PwmHz<TIM, REMAP, P, PINS>
252where
253    TIM: Instance + WithPwm,
254    REMAP: Remap<Periph = TIM>,
255    PINS: Pins<REMAP, P>,
256{
257    pub fn release(mut self) -> Timer<TIM> {
258        // stop timer
259        self.tim.cr1_reset();
260        self.timer
261    }
262
263    pub fn split(self) -> PINS::Channels {
264        PINS::split()
265    }
266}
267
268impl<TIM, REMAP, P, PINS> Deref for PwmHz<TIM, REMAP, P, PINS>
269where
270    TIM: Instance + WithPwm,
271    REMAP: Remap<Periph = TIM>,
272    PINS: Pins<REMAP, P>,
273{
274    type Target = Timer<TIM>;
275    fn deref(&self) -> &Self::Target {
276        &self.timer
277    }
278}
279
280impl<TIM, REMAP, P, PINS> DerefMut for PwmHz<TIM, REMAP, P, PINS>
281where
282    TIM: Instance + WithPwm,
283    REMAP: Remap<Periph = TIM>,
284    PINS: Pins<REMAP, P>,
285{
286    fn deref_mut(&mut self) -> &mut Self::Target {
287        &mut self.timer
288    }
289}
290
291impl<TIM: Instance + WithPwm> Timer<TIM> {
292    pub fn pwm_hz<REMAP, P, PINS>(
293        mut self,
294        _pins: PINS,
295        mapr: &mut MAPR,
296        freq: Hertz,
297    ) -> PwmHz<TIM, REMAP, P, PINS>
298    where
299        REMAP: Remap<Periph = TIM>,
300        PINS: Pins<REMAP, P>,
301    {
302        REMAP::remap(mapr);
303
304        if PINS::C1 {
305            self.tim
306                .preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1);
307        }
308        if PINS::C2 && TIM::CH_NUMBER > 1 {
309            self.tim
310                .preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1);
311        }
312        if PINS::C3 && TIM::CH_NUMBER > 2 {
313            self.tim
314                .preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1);
315        }
316        if PINS::C4 && TIM::CH_NUMBER > 3 {
317            self.tim
318                .preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1);
319        }
320
321        // The reference manual is a bit ambiguous about when enabling this bit is really
322        // necessary, but since we MUST enable the preload for the output channels then we
323        // might as well enable for the auto-reload too
324        self.tim.enable_preload(true);
325
326        let (psc, arr) = compute_arr_presc(freq.raw(), self.clk.raw());
327        self.tim.set_prescaler(psc);
328        self.tim.set_auto_reload(arr).unwrap();
329
330        // Trigger update event to load the registers
331        self.tim.trigger_update();
332
333        self.tim.start_pwm();
334
335        PwmHz {
336            timer: self,
337            _pins: PhantomData,
338        }
339    }
340}
341
342impl<TIM, REMAP, P, PINS> PwmHz<TIM, REMAP, P, PINS>
343where
344    TIM: Instance + WithPwm,
345    REMAP: Remap<Periph = TIM>,
346    PINS: Pins<REMAP, P>,
347{
348    pub fn enable(&mut self, channel: Channel) {
349        TIM::enable_channel(PINS::check_used(channel) as u8, true)
350    }
351
352    pub fn disable(&mut self, channel: Channel) {
353        TIM::enable_channel(PINS::check_used(channel) as u8, false)
354    }
355
356    pub fn get_duty(&self, channel: Channel) -> u16 {
357        TIM::read_cc_value(PINS::check_used(channel) as u8) as u16
358    }
359
360    pub fn set_duty(&mut self, channel: Channel, duty: u16) {
361        TIM::set_cc_value(PINS::check_used(channel) as u8, duty as u32)
362    }
363
364    /// If `0` returned means max_duty is 2^16
365    pub fn get_max_duty(&self) -> u16 {
366        (TIM::read_auto_reload() as u16).wrapping_add(1)
367    }
368
369    pub fn get_period(&self) -> Hertz {
370        let clk = self.clk;
371        let psc = self.tim.read_prescaler() as u32;
372        let arr = TIM::read_auto_reload();
373
374        // Length in ms of an internal clock pulse
375        clk / ((psc + 1) * (arr + 1))
376    }
377
378    pub fn set_period(&mut self, period: Hertz) {
379        let clk = self.clk;
380
381        let (psc, arr) = compute_arr_presc(period.raw(), clk.raw());
382        self.tim.set_prescaler(psc);
383        self.tim.set_auto_reload(arr).unwrap();
384    }
385}
386
387pub struct Pwm<TIM, REMAP, P, PINS, const FREQ: u32>
388where
389    TIM: Instance + WithPwm,
390    REMAP: Remap<Periph = TIM>,
391    PINS: Pins<REMAP, P>,
392{
393    timer: FTimer<TIM, FREQ>,
394    _pins: PhantomData<(REMAP, P, PINS)>,
395}
396
397impl<TIM, REMAP, P, PINS, const FREQ: u32> Pwm<TIM, REMAP, P, PINS, FREQ>
398where
399    TIM: Instance + WithPwm,
400    REMAP: Remap<Periph = TIM>,
401    PINS: Pins<REMAP, P>,
402{
403    pub fn split(self) -> PINS::Channels {
404        PINS::split()
405    }
406
407    pub fn release(mut self) -> FTimer<TIM, FREQ> {
408        // stop counter
409        self.tim.cr1_reset();
410        self.timer
411    }
412}
413
414impl<TIM, REMAP, P, PINS, const FREQ: u32> Deref for Pwm<TIM, REMAP, P, PINS, FREQ>
415where
416    TIM: Instance + WithPwm,
417    REMAP: Remap<Periph = TIM>,
418    PINS: Pins<REMAP, P>,
419{
420    type Target = FTimer<TIM, FREQ>;
421    fn deref(&self) -> &Self::Target {
422        &self.timer
423    }
424}
425
426impl<TIM, REMAP, P, PINS, const FREQ: u32> DerefMut for Pwm<TIM, REMAP, P, PINS, FREQ>
427where
428    TIM: Instance + WithPwm,
429    REMAP: Remap<Periph = TIM>,
430    PINS: Pins<REMAP, P>,
431{
432    fn deref_mut(&mut self) -> &mut Self::Target {
433        &mut self.timer
434    }
435}
436
437impl<TIM: Instance + WithPwm, const FREQ: u32> FTimer<TIM, FREQ> {
438    pub fn pwm<REMAP, P, PINS>(
439        mut self,
440        _pins: PINS,
441        mapr: &mut MAPR,
442        time: TimerDurationU32<FREQ>,
443    ) -> Pwm<TIM, REMAP, P, PINS, FREQ>
444    where
445        REMAP: Remap<Periph = TIM>,
446        PINS: Pins<REMAP, P>,
447    {
448        REMAP::remap(mapr);
449
450        if PINS::C1 {
451            self.tim
452                .preload_output_channel_in_mode(Channel::C1, Ocm::PwmMode1);
453        }
454        if PINS::C2 && TIM::CH_NUMBER > 1 {
455            self.tim
456                .preload_output_channel_in_mode(Channel::C2, Ocm::PwmMode1);
457        }
458        if PINS::C3 && TIM::CH_NUMBER > 2 {
459            self.tim
460                .preload_output_channel_in_mode(Channel::C3, Ocm::PwmMode1);
461        }
462        if PINS::C4 && TIM::CH_NUMBER > 3 {
463            self.tim
464                .preload_output_channel_in_mode(Channel::C4, Ocm::PwmMode1);
465        }
466
467        // The reference manual is a bit ambiguous about when enabling this bit is really
468        // necessary, but since we MUST enable the preload for the output channels then we
469        // might as well enable for the auto-reload too
470        self.tim.enable_preload(true);
471
472        self.tim.set_auto_reload(time.ticks() - 1).unwrap();
473
474        // Trigger update event to load the registers
475        self.tim.trigger_update();
476
477        self.tim.start_pwm();
478
479        Pwm {
480            timer: self,
481            _pins: PhantomData,
482        }
483    }
484}
485
486impl<TIM, REMAP, P, PINS, const FREQ: u32> Pwm<TIM, REMAP, P, PINS, FREQ>
487where
488    TIM: Instance + WithPwm,
489    REMAP: Remap<Periph = TIM>,
490    PINS: Pins<REMAP, P>,
491{
492    pub fn enable(&mut self, channel: Channel) {
493        TIM::enable_channel(PINS::check_used(channel) as u8, true)
494    }
495
496    pub fn disable(&mut self, channel: Channel) {
497        TIM::enable_channel(PINS::check_used(channel) as u8, false)
498    }
499
500    pub fn get_duty(&self, channel: Channel) -> u16 {
501        TIM::read_cc_value(PINS::check_used(channel) as u8) as u16
502    }
503
504    pub fn set_duty(&mut self, channel: Channel, duty: u16) {
505        TIM::set_cc_value(PINS::check_used(channel) as u8, duty.into())
506    }
507
508    /// If `0` returned means max_duty is 2^16
509    pub fn get_max_duty(&self) -> u16 {
510        (TIM::read_auto_reload() as u16).wrapping_add(1)
511    }
512
513    pub fn get_period(&self) -> TimerDurationU32<FREQ> {
514        TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1)
515    }
516
517    pub fn set_period(&mut self, period: TimerDurationU32<FREQ>) {
518        self.tim.set_auto_reload(period.ticks() - 1).unwrap();
519    }
520}