stm32f0xx_hal/
pwm.rs

1use cast::{u16, u32};
2use core::{marker::PhantomData, mem::MaybeUninit};
3
4use crate::rcc::Rcc;
5
6use crate::time::Hertz;
7use embedded_hal as hal;
8
9pub trait Pins<TIM, P> {
10    const C1: bool = false;
11    const C2: bool = false;
12    const C3: bool = false;
13    const C4: bool = false;
14    type Channels;
15}
16use crate::timers::PinC1;
17use crate::timers::PinC2;
18use crate::timers::PinC3;
19use crate::timers::PinC4;
20
21pub struct C1;
22pub struct C2;
23pub struct C3;
24pub struct C4;
25
26pub struct PwmChannels<TIM, CHANNELS> {
27    _channel: PhantomData<CHANNELS>,
28    _tim: PhantomData<TIM>,
29}
30
31macro_rules! pins_impl {
32    ( $( ( $($PINX:ident),+ ), ( $($TRAIT:ident),+ ), ( $($ENCHX:ident),* ); )+ ) => {
33        $(
34            #[allow(unused_parens)]
35            impl<TIM, $($PINX,)+> Pins<TIM, ($($ENCHX),+)> for ($($PINX),+)
36            where
37                $($PINX: $TRAIT<TIM>,)+
38            {
39                $(const $ENCHX: bool = true;)+
40                type Channels = ($(PwmChannels<TIM, $ENCHX>),+);
41            }
42        )+
43    };
44}
45
46pins_impl!(
47    (P1, P2, P3, P4), (PinC1, PinC2, PinC3, PinC4), (C1, C2, C3, C4);
48    (P2, P3, P4), (PinC2, PinC3, PinC4), (C2, C3, C4);
49    (P1, P3, P4), (PinC1, PinC3, PinC4), (C1, C3, C4);
50    (P1, P2, P4), (PinC1, PinC2, PinC4), (C1, C2, C4);
51    (P1, P2, P3), (PinC1, PinC2, PinC3), (C1, C2, C3);
52    (P3, P4), (PinC3, PinC4), (C3, C4);
53    (P2, P4), (PinC2, PinC4), (C2, C4);
54    (P2, P3), (PinC2, PinC3), (C2, C3);
55    (P1, P4), (PinC1, PinC4), (C1, C4);
56    (P1, P3), (PinC1, PinC3), (C1, C3);
57    (P1, P2), (PinC1, PinC2), (C1, C2);
58    (P1), (PinC1), (C1);
59    (P2), (PinC2), (C2);
60    (P3), (PinC3), (C3);
61    (P4), (PinC4), (C4);
62);
63
64impl<TIM, P1: PinC1<TIM>, P2: PinC1<TIM>> PinC1<TIM> for (P1, P2) {}
65impl<TIM, P1: PinC2<TIM>, P2: PinC2<TIM>> PinC2<TIM> for (P1, P2) {}
66impl<TIM, P1: PinC3<TIM>, P2: PinC3<TIM>> PinC3<TIM> for (P1, P2) {}
67impl<TIM, P1: PinC4<TIM>, P2: PinC4<TIM>> PinC4<TIM> for (P1, P2) {}
68
69impl<TIM, P1: PinC1<TIM>, P2: PinC1<TIM>, P3: PinC1<TIM>> PinC1<TIM> for (P1, P2, P3) {}
70impl<TIM, P1: PinC2<TIM>, P2: PinC2<TIM>, P3: PinC2<TIM>> PinC2<TIM> for (P1, P2, P3) {}
71impl<TIM, P1: PinC3<TIM>, P2: PinC3<TIM>, P3: PinC3<TIM>> PinC3<TIM> for (P1, P2, P3) {}
72impl<TIM, P1: PinC4<TIM>, P2: PinC4<TIM>, P3: PinC4<TIM>> PinC4<TIM> for (P1, P2, P3) {}
73
74impl<TIM, P1: PinC1<TIM>, P2: PinC1<TIM>, P3: PinC1<TIM>, P4: PinC1<TIM>> PinC1<TIM>
75    for (P1, P2, P3, P4)
76{
77}
78impl<TIM, P1: PinC2<TIM>, P2: PinC2<TIM>, P3: PinC2<TIM>, P4: PinC2<TIM>> PinC2<TIM>
79    for (P1, P2, P3, P4)
80{
81}
82impl<TIM, P1: PinC3<TIM>, P2: PinC3<TIM>, P3: PinC3<TIM>, P4: PinC3<TIM>> PinC3<TIM>
83    for (P1, P2, P3, P4)
84{
85}
86impl<TIM, P1: PinC4<TIM>, P2: PinC4<TIM>, P3: PinC4<TIM>, P4: PinC4<TIM>> PinC4<TIM>
87    for (P1, P2, P3, P4)
88{
89}
90
91// the following timer have a main output switch, enable the automatic output
92macro_rules! brk {
93    (TIM1, $tim:ident) => {
94        $tim.bdtr.modify(|_, w| w.aoe().set_bit());
95    };
96    (TIM15, $tim:ident) => {
97        $tim.bdtr.modify(|_, w| w.aoe().set_bit());
98    };
99    (TIM16, $tim:ident) => {
100        $tim.bdtr.modify(|_, w| w.aoe().set_bit());
101    };
102    (TIM17, $tim:ident) => {
103        $tim.bdtr.modify(|_, w| w.aoe().set_bit());
104    };
105    ($_other:ident, $_tim:ident) => {};
106}
107
108// Timer with four output channels 16 Bit Timer
109macro_rules! pwm_4_channels {
110    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
111        $(
112            pub fn $timX<P, PINS, T>(tim: $TIMX, _pins: PINS, rcc: &mut Rcc, freq: T) -> PINS::Channels
113            where
114                PINS: Pins<$TIMX, P>,
115                T: Into<Hertz>,
116            {
117                // enable and reset peripheral to a clean slate state
118                rcc.regs.$apbenr.modify(|_, w| w.$timXen().set_bit());
119                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().set_bit());
120                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().clear_bit());
121
122                if PINS::C1 {
123                    tim.ccmr1_output()
124                        .modify(|_, w| w.oc1pe().set_bit().oc1m().pwm_mode1() );
125                }
126                if PINS::C2 {
127                    tim.ccmr1_output()
128                        .modify(|_, w| w.oc2pe().set_bit().oc2m().pwm_mode1() );
129                }
130                if PINS::C3 {
131                    tim.ccmr2_output()
132                        .modify(|_, w| w.oc3pe().set_bit().oc3m().pwm_mode1() );
133                }
134                if PINS::C4 {
135                    tim.ccmr2_output()
136                        .modify(|_, w| w.oc4pe().set_bit().oc4m().pwm_mode1() );
137                }
138
139                // If pclk is prescaled from hclk, the frequency fed into the timers is doubled
140                let tclk = if rcc.clocks.hclk().0 == rcc.clocks.pclk().0 {
141                    rcc.clocks.pclk().0
142                } else {
143                    rcc.clocks.pclk().0 * 2
144                };
145                let ticks = tclk / freq.into().0;
146
147                let psc = u16((ticks - 1) / (1 << 16)).unwrap();
148                tim.psc.write(|w| w.psc().bits(psc) );
149                let arr = u16(ticks / u32(psc + 1)).unwrap();
150                tim.arr.write(|w| unsafe { w.bits(u32(arr)) });
151
152                // enable auto-reload preload
153                tim.cr1.modify(|_, w| w.arpe().set_bit());
154
155                // Trigger update event to load the registers
156                tim.cr1.modify(|_, w| w.urs().set_bit());
157                tim.egr.write(|w| w.ug().set_bit());
158                tim.cr1.modify(|_, w| w.urs().clear_bit());
159
160                brk!($TIMX, tim);
161                tim.cr1.write(|w|
162                    w.cms()
163                        .bits(0b00)
164                        .dir()
165                        .clear_bit()
166                        .opm()
167                        .clear_bit()
168                        .cen()
169                        .set_bit()
170                );
171                //NOTE(unsafe) `PINS::Channels` is a ZST
172                unsafe { MaybeUninit::uninit().assume_init() }
173            }
174
175            impl hal::PwmPin for PwmChannels<$TIMX, C1> {
176                type Duty = u16;
177
178                //NOTE(unsafe) atomic write with no side effects
179                fn disable(&mut self) {
180                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().clear_bit()) };
181                }
182
183                //NOTE(unsafe) atomic write with no side effects
184                fn enable(&mut self) {
185                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().set_bit()) };
186                }
187
188                //NOTE(unsafe) atomic read with no side effects
189                fn get_duty(&self) -> u16 {
190                    unsafe { (*$TIMX::ptr()).ccr1.read().ccr().bits() as u16 }
191                }
192
193                //NOTE(unsafe) atomic read with no side effects
194                fn get_max_duty(&self) -> u16 {
195                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
196                }
197
198                //NOTE(unsafe) atomic write with no side effects
199                fn set_duty(&mut self, duty: u16) {
200                    unsafe { (*$TIMX::ptr()).ccr1.write(|w| w.ccr().bits(duty.into())) }
201                }
202            }
203
204            impl hal::PwmPin for PwmChannels<$TIMX, C2> {
205                type Duty = u16;
206
207                //NOTE(unsafe) atomic write with no side effects
208                fn disable(&mut self) {
209                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc2e().clear_bit()) };
210                }
211
212                //NOTE(unsafe) atomic write with no side effects
213                fn enable(&mut self) {
214                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc2e().set_bit()) };
215                }
216
217                //NOTE(unsafe) atomic read with no side effects
218                fn get_duty(&self) -> u16 {
219                    unsafe { (*$TIMX::ptr()).ccr2.read().ccr().bits() as u16 }
220                }
221
222                //NOTE(unsafe) atomic read with no side effects
223                fn get_max_duty(&self) -> u16 {
224                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
225                }
226
227                //NOTE(unsafe) atomic write with no side effects
228                fn set_duty(&mut self, duty: u16) {
229                    unsafe { (*$TIMX::ptr()).ccr2.write(|w| w.ccr().bits(duty.into())) }
230                }
231            }
232
233            impl hal::PwmPin for PwmChannels<$TIMX, C3> {
234                type Duty = u16;
235
236                //NOTE(unsafe) atomic write with no side effects
237                fn disable(&mut self) {
238                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc3e().clear_bit()) };
239                }
240
241                //NOTE(unsafe) atomic write with no side effects
242                fn enable(&mut self) {
243                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc3e().set_bit()) };
244                }
245
246                //NOTE(unsafe) atomic read with no side effects
247                fn get_duty(&self) -> u16 {
248                    unsafe { (*$TIMX::ptr()).ccr3.read().ccr().bits() as u16 }
249                }
250
251                //NOTE(unsafe) atomic read with no side effects
252                fn get_max_duty(&self) -> u16 {
253                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
254                }
255
256                //NOTE(unsafe) atomic write with no side effects
257                fn set_duty(&mut self, duty: u16) {
258                    unsafe { (*$TIMX::ptr()).ccr3.write(|w| w.ccr().bits(duty.into())) }
259                }
260            }
261
262            impl hal::PwmPin for PwmChannels<$TIMX, C4> {
263                type Duty = u16;
264
265                //NOTE(unsafe) atomic write with no side effects
266                fn disable(&mut self) {
267                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc4e().clear_bit()) };
268                }
269
270                //NOTE(unsafe) atomic write with no side effects
271                fn enable(&mut self) {
272                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc4e().set_bit()) };
273                }
274
275                //NOTE(unsafe) atomic read with no side effects
276                fn get_duty(&self) -> u16 {
277                    unsafe { (*$TIMX::ptr()).ccr4.read().ccr().bits() as u16 }
278                }
279
280                //NOTE(unsafe) atomic read with no side effects
281                fn get_max_duty(&self) -> u16 {
282                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
283                }
284
285                //NOTE(unsafe) atomic write with no side effects
286                fn set_duty(&mut self, duty: u16) {
287                    unsafe { (*$TIMX::ptr()).ccr4.write(|w| w.ccr().bits(duty.into())) }
288                }
289            }
290        )+
291    };
292}
293
294// General purpose timer with two output channels
295#[cfg(any(
296    feature = "stm32f030x8",
297    feature = "stm32f030xc",
298    feature = "stm32f051",
299    feature = "stm32f058",
300    feature = "stm32f070xb",
301    feature = "stm32f071",
302    feature = "stm32f072",
303    feature = "stm32f078",
304    feature = "stm32f091",
305    feature = "stm32f098",
306))]
307macro_rules! pwm_2_channels {
308    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
309        $(
310            pub fn $timX<P, PINS, T>(tim: $TIMX, _pins: PINS, rcc: &mut Rcc, freq: T) -> PINS::Channels
311            where
312                PINS: Pins<$TIMX, P>,
313                T: Into<Hertz>,
314            {
315                // enable and reset peripheral to a clean slate state
316                rcc.regs.$apbenr.modify(|_, w| w.$timXen().set_bit());
317                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().set_bit());
318                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().clear_bit());
319
320                if PINS::C1 {
321                    tim.ccmr1_output().modify(|_, w| w.oc1pe().set_bit().oc1m().bits(6));
322                }
323                if PINS::C2 {
324                    tim.ccmr1_output().modify(|_, w| w.oc2pe().set_bit().oc1m().bits(6));
325                }
326
327                // If pclk is prescaled from hclk, the frequency fed into the timers is doubled
328                let tclk = if rcc.clocks.hclk().0 == rcc.clocks.pclk().0 {
329                    rcc.clocks.pclk().0
330                } else {
331                    rcc.clocks.pclk().0 * 2
332                };
333                let ticks = tclk / freq.into().0;
334
335                let psc = u16((ticks - 1) / (1 << 16)).unwrap();
336                tim.psc.write(|w| w.psc().bits(psc) );
337                let arr = u16(ticks / u32(psc + 1)).unwrap();
338                tim.arr.write(|w| unsafe { w.bits(u32(arr)) });
339
340                // enable auto-reload preload
341                tim.cr1.modify(|_, w| w.arpe().set_bit());
342
343                // Trigger update event to load the registers
344                tim.cr1.modify(|_, w| w.urs().set_bit());
345                tim.egr.write(|w| w.ug().set_bit());
346                tim.cr1.modify(|_, w| w.urs().clear_bit());
347
348                brk!($TIMX, tim);
349                tim.cr1.write(|w|
350                    w.opm()
351                        .clear_bit()
352                        .cen()
353                        .set_bit()
354                );
355                //NOTE(unsafe) `PINS::Channels` is a ZST
356                unsafe { MaybeUninit::uninit().assume_init() }
357            }
358
359            impl hal::PwmPin for PwmChannels<$TIMX, C1> {
360                type Duty = u16;
361
362                //NOTE(unsafe) atomic write with no side effects
363                fn disable(&mut self) {
364                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().clear_bit()) };
365                }
366
367                //NOTE(unsafe) atomic write with no side effects
368                fn enable(&mut self) {
369                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().set_bit()) };
370                }
371
372                //NOTE(unsafe) atomic read with no side effects
373                fn get_duty(&self) -> u16 {
374                    unsafe { (*$TIMX::ptr()).ccr1.read().ccr().bits() as u16 }
375                }
376
377                //NOTE(unsafe) atomic read with no side effects
378                fn get_max_duty(&self) -> u16 {
379                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
380                }
381
382                //NOTE(unsafe) atomic write with no side effects
383                fn set_duty(&mut self, duty: u16) {
384                    unsafe { (*$TIMX::ptr()).ccr1.write(|w| w.ccr().bits(duty.into())) }
385                }
386            }
387
388            impl hal::PwmPin for PwmChannels<$TIMX, C2> {
389                type Duty = u16;
390
391                //NOTE(unsafe) atomic write with no side effects
392                fn disable(&mut self) {
393                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc2e().clear_bit()) };
394                }
395
396                //NOTE(unsafe) atomic write with no side effects
397                fn enable(&mut self) {
398                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc2e().set_bit()) };
399                }
400
401                //NOTE(unsafe) atomic read with no side effects
402                fn get_duty(&self) -> u16 {
403                    unsafe { (*$TIMX::ptr()).ccr2.read().ccr().bits() as u16 }
404                }
405
406                //NOTE(unsafe) atomic read with no side effects
407                fn get_max_duty(&self) -> u16 {
408                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
409                }
410
411                //NOTE(unsafe) atomic write with no side effects
412                fn set_duty(&mut self, duty: u16) {
413                    unsafe { (*$TIMX::ptr()).ccr2.write(|w| w.ccr().bits(duty.into())) }
414                }
415            }
416        )+
417    };
418}
419
420// General purpose timer with one output channel (TIM14)
421macro_rules! pwm_1_channel {
422    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
423        $(
424            pub fn $timX<P, PINS, T>(tim: $TIMX, _pins: PINS, rcc: &mut Rcc, freq: T) -> PINS::Channels
425            where
426                PINS: Pins<$TIMX, P>,
427                T: Into<Hertz>,
428            {
429                // enable and reset peripheral to a clean slate state
430                rcc.regs.$apbenr.modify(|_, w| w.$timXen().set_bit());
431                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().set_bit());
432                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().clear_bit());
433
434                if PINS::C1 {
435                    tim.ccmr1_output().modify(|_, w| w.oc1pe().set_bit().oc1m().bits(6));
436                }
437
438                // If pclk is prescaled from hclk, the frequency fed into the timers is doubled
439                let tclk = if rcc.clocks.hclk().0 == rcc.clocks.pclk().0 {
440                    rcc.clocks.pclk().0
441                } else {
442                    rcc.clocks.pclk().0 * 2
443                };
444                let ticks = tclk / freq.into().0;
445
446                let psc = u16((ticks - 1) / (1 << 16)).unwrap();
447                tim.psc.write(|w| w.psc().bits(psc) );
448                let arr = u16(ticks / u32(psc + 1)).unwrap();
449                tim.arr.write(|w| unsafe { w.bits(u32(arr)) });
450
451                // enable auto-reload preload
452                tim.cr1.modify(|_, w| w.arpe().set_bit());
453
454                // Trigger update event to load the registers
455                tim.cr1.modify(|_, w| w.urs().set_bit());
456                tim.egr.write(|w| w.ug().set_bit());
457                tim.cr1.modify(|_, w| w.urs().clear_bit());
458
459                brk!($TIMX, tim);
460                tim.cr1.write(|w|
461                    w.cen()
462                        .set_bit()
463                );
464                //NOTE(unsafe) `PINS::Channels` is a ZST
465                unsafe { MaybeUninit::uninit().assume_init() }
466            }
467
468            impl hal::PwmPin for PwmChannels<$TIMX, C1> {
469                type Duty = u16;
470
471                //NOTE(unsafe) atomic write with no side effects
472                fn disable(&mut self) {
473                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().clear_bit()) };
474                }
475
476                //NOTE(unsafe) atomic write with no side effects
477                fn enable(&mut self) {
478                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().set_bit()) };
479                }
480
481                //NOTE(unsafe) atomic read with no side effects
482                fn get_duty(&self) -> u16 {
483                    unsafe { (*$TIMX::ptr()).ccr1.read().ccr().bits() as u16 }
484                }
485
486                //NOTE(unsafe) atomic read with no side effects
487                fn get_max_duty(&self) -> u16 {
488                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
489                }
490
491                //NOTE(unsafe) atomic write with no side effects
492                fn set_duty(&mut self, duty: u16) {
493                    unsafe { (*$TIMX::ptr()).ccr1.write(|w| w.ccr().bits(duty.into())) }
494                }
495            }
496        )+
497    };
498}
499
500// General purpose timer with one output channel (TIM16/TIM17)
501macro_rules! pwm_1_channel_with_complementary_outputs {
502    ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
503        $(
504            pub fn $timX<P, PINS, T>(tim: $TIMX, _pins: PINS, rcc: &mut Rcc, freq: T) -> PINS::Channels
505            where
506                PINS: Pins<$TIMX, P>,
507                T: Into<Hertz>,
508            {
509                // enable and reset peripheral to a clean slate state
510                rcc.regs.$apbenr.modify(|_, w| w.$timXen().set_bit());
511                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().set_bit());
512                rcc.regs.$apbrstr.modify(|_, w| w.$timXrst().clear_bit());
513
514                if PINS::C1 {
515                    tim.ccmr1_output().modify(|_, w| w.oc1pe().set_bit().oc1m().bits(6));
516                }
517
518                // If pclk is prescaled from hclk, the frequency fed into the timers is doubled
519                let tclk = if rcc.clocks.hclk().0 == rcc.clocks.pclk().0 {
520                    rcc.clocks.pclk().0
521                } else {
522                    rcc.clocks.pclk().0 * 2
523                };
524                let ticks = tclk / freq.into().0;
525
526                let psc = u16((ticks - 1) / (1 << 16)).unwrap();
527                tim.psc.write(|w| w.psc().bits(psc) );
528                let arr = u16(ticks / u32(psc + 1)).unwrap();
529                tim.arr.write(|w| unsafe { w.bits(u32(arr)) });
530
531                // enable auto-reload preload
532                tim.cr1.modify(|_, w| w.arpe().set_bit());
533
534                // Trigger update event to load the registers
535                tim.cr1.modify(|_, w| w.urs().set_bit());
536                tim.egr.write(|w| w.ug().set_bit());
537                tim.cr1.modify(|_, w| w.urs().clear_bit());
538
539                brk!($TIMX, tim);
540                tim.cr1.write(|w|
541                    w.opm()
542                        .clear_bit()
543                        .cen()
544                        .set_bit()
545                );
546
547                //NOTE(unsafe) `PINS::Channels` is a ZST
548                unsafe { MaybeUninit::uninit().assume_init() }
549            }
550
551            impl hal::PwmPin for PwmChannels<$TIMX, C1> {
552                type Duty = u16;
553
554                //NOTE(unsafe) atomic write with no side effects
555                fn disable(&mut self) {
556                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().clear_bit()) };
557                }
558
559                //NOTE(unsafe) atomic write with no side effects
560                fn enable(&mut self) {
561                    unsafe { (*($TIMX::ptr())).ccer.modify(|_, w| w.cc1e().set_bit()) };
562                }
563
564                //NOTE(unsafe) atomic read with no side effects
565                fn get_duty(&self) -> u16 {
566                    unsafe { (*$TIMX::ptr()).ccr1.read().ccr().bits() as u16 }
567                }
568
569                //NOTE(unsafe) atomic read with no side effects
570                fn get_max_duty(&self) -> u16 {
571                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() as u16 }
572                }
573
574                //NOTE(unsafe) atomic write with no side effects
575                fn set_duty(&mut self, duty: u16) {
576                    unsafe { (*$TIMX::ptr()).ccr1.write(|w| w.ccr().bits(duty.into())) }
577                }
578            }
579        )+
580    };
581}
582
583use crate::pac::*;
584
585pwm_4_channels!(
586    TIM1: (tim1, tim1en, tim1rst, apb2enr, apb2rstr),
587    TIM3: (tim3, tim3en, tim3rst, apb1enr, apb1rstr),
588);
589
590pwm_1_channel!(TIM14: (tim14, tim14en, tim14rst, apb1enr, apb1rstr),);
591
592pwm_1_channel_with_complementary_outputs!(
593    TIM16: (tim16, tim16en, tim16rst, apb2enr, apb2rstr),
594    TIM17: (tim17, tim17en, tim17rst, apb2enr, apb2rstr),
595);
596
597#[cfg(any(
598    feature = "stm32f030x8",
599    feature = "stm32f030xc",
600    feature = "stm32f051",
601    feature = "stm32f058",
602    feature = "stm32f070xb",
603    feature = "stm32f071",
604    feature = "stm32f072",
605    feature = "stm32f078",
606    feature = "stm32f091",
607    feature = "stm32f098",
608))]
609pwm_2_channels! {
610    TIM15: (tim15, tim15en, tim15rst, apb2enr, apb2rstr),
611}