stm32g0xx_hal/timer/
pwm.rs

1//! # Pulse Width Modulation
2use core::marker::PhantomData;
3
4use crate::rcc::*;
5use crate::stm32::*;
6use crate::time::Hertz;
7use crate::timer::pins::TimerPin;
8use crate::timer::*;
9
10pub enum OutputCompareMode {
11    Frozen = 0,
12    MatchPos = 1,
13    MatchNeg = 2,
14    MatchToggle = 3,
15    ForceLow = 4,
16    ForceHigh = 5,
17    PwmMode1 = 6,
18    PmwMode2 = 7,
19    OpmMode1 = 8,
20    OomMode2 = 9,
21    CombinedMode1 = 12,
22    CombinedMode2 = 13,
23    AsyncMode1 = 14,
24    AsyncMode2 = 15,
25}
26
27pub struct Pwm<TIM> {
28    clk: Hertz,
29    tim: TIM,
30}
31
32pub struct PwmPin<TIM, CH> {
33    tim: PhantomData<TIM>,
34    channel: PhantomData<CH>,
35}
36
37enum ClockSource {
38    ApbTim,
39    #[allow(dead_code)]
40    Pllq,
41}
42
43pub trait PwmExt: Sized {
44    fn pwm(self, freq: Hertz, rcc: &mut Rcc) -> Pwm<Self>;
45}
46
47pub trait PwmQExt: Sized {
48    // Configures PWM using PLLQ as a clock source. Panics if PLLQ was not
49    // enabled when RCC was configured.
50    fn pwm_q(self, freq: Hertz, rcc: &mut Rcc) -> Pwm<Self>;
51}
52
53pub trait PwmPinMode {
54    fn set_compare_mode(&mut self, mode: OutputCompareMode);
55}
56
57impl<TIM> Pwm<TIM> {
58    pub fn bind_pin<PIN>(&self, pin: PIN) -> PwmPin<TIM, PIN::Channel>
59    where
60        PIN: TimerPin<TIM>,
61    {
62        pin.setup();
63        PwmPin {
64            tim: PhantomData,
65            channel: PhantomData,
66        }
67    }
68}
69
70macro_rules! pwm {
71    ($($TIMX:ident: ($timX:ident, $arr:ident $(,$arr_h:ident)*),)+) => {
72        $(
73            impl PwmExt for $TIMX {
74                fn pwm(self, freq: Hertz, rcc: &mut Rcc) -> Pwm<Self> {
75                    $timX(self, freq, rcc, ClockSource::ApbTim)
76                }
77            }
78
79            fn $timX(tim: $TIMX, freq: Hertz, rcc: &mut Rcc, clock_source: ClockSource) -> Pwm<$TIMX> {
80                $TIMX::enable(rcc);
81                $TIMX::reset(rcc);
82
83                let clk = match clock_source {
84                    ClockSource::ApbTim => {
85                        rcc.ccipr.modify(|_, w| w.tim1sel().clear_bit());
86                        rcc.clocks.apb_tim_clk
87                    }
88                    ClockSource::Pllq => {
89                        rcc.ccipr.modify(|_, w| w.tim1sel().set_bit());
90                        rcc.clocks.pll_clk.q.unwrap()
91                    }
92                };
93
94                let mut pwm = Pwm::<$TIMX> {
95                    clk,
96                    tim,
97                };
98                pwm.set_freq(freq);
99                pwm
100            }
101
102            impl Pwm<$TIMX> {
103                /// Set the PWM frequency. Actual frequency may differ from
104                /// requested due to precision of input clock. To check actual
105                /// frequency, call freq.
106                pub fn set_freq(&mut self, freq: Hertz) {
107                    let ratio = self.clk / freq;
108                    let psc = (ratio - 1) / 0xffff;
109                    let arr = ratio / (psc + 1) - 1;
110
111                    unsafe {
112                        self.tim.psc.write(|w| w.psc().bits(psc as u16));
113                        self.tim.arr.write(|w| w.$arr().bits(arr as u16));
114                        $(
115                            self.tim.arr.modify(|_, w| w.$arr_h().bits((arr >> 16) as u16));
116                        )*
117                        self.tim.cr1.write(|w| w.cen().set_bit())
118                    }
119                }
120                /// Starts listening
121                pub fn listen(&mut self) {
122                    self.tim.dier.write(|w| w.uie().set_bit());
123                }
124
125                /// Stops listening
126                pub fn unlisten(&mut self) {
127                    self.tim.dier.write(|w| w.uie().clear_bit());
128                }
129                /// Clears interrupt flag
130                pub fn clear_irq(&mut self) {
131                    self.tim.sr.modify(|_, w| w.uif().clear_bit());
132                }
133
134                /// Resets counter value
135                pub fn reset(&mut self) {
136                    self.tim.cnt.reset();
137                }
138
139                /// Returns the currently configured frequency
140                pub fn freq(&self) -> Hertz {
141                    Hertz::from_raw(self.clk.raw()
142                        / (self.tim.psc.read().bits() + 1)
143                        / (self.tim.arr.read().bits() + 1))
144                }
145            }
146        )+
147    }
148}
149
150#[allow(unused_macros)]
151macro_rules! pwm_q {
152    ($($TIMX:ident: $timX:ident,)+) => {
153        $(
154            impl PwmQExt for $TIMX {
155                fn pwm_q(self, freq: Hertz, rcc: &mut Rcc) -> Pwm<Self> {
156                    $timX(self, freq, rcc, ClockSource::Pllq)
157                }
158            }
159        )+
160    }
161}
162
163#[cfg(any(feature = "stm32g0x1", feature = "stm32g070"))]
164macro_rules! pwm_hal {
165    ($($TIMX:ident:
166        ($CH:ty, $ccxe:ident, $ccmrx_output:ident, $ocxpe:ident, $ocxm:ident, $ccrx:ident, $ccrx_l:ident, $ccrx_h:ident),)+
167    ) => {
168        $(
169            impl hal::PwmPin for PwmPin<$TIMX, $CH> {
170                type Duty = u32;
171
172                fn disable(&mut self) {
173                    unsafe {
174                        (*$TIMX::ptr()).ccer.modify(|_, w| w.$ccxe().clear_bit());
175                    }
176                }
177
178                fn enable(&mut self) {
179                    unsafe {
180                        let tim = &*$TIMX::ptr();
181                        tim.$ccmrx_output().modify(|_, w| w.$ocxpe().set_bit().$ocxm().bits(6));
182                        tim.ccer.modify(|_, w| w.$ccxe().set_bit());
183                    }
184                }
185
186                fn get_duty(&self) -> u32 {
187                    unsafe { (*$TIMX::ptr()).$ccrx.read().bits() }
188                }
189
190                fn get_max_duty(&self) -> u32 {
191                    unsafe { (*$TIMX::ptr()).arr.read().bits() }
192                }
193
194                fn set_duty(&mut self, duty: u32) {
195                    unsafe { (*$TIMX::ptr()).$ccrx.write(|w| w.bits(duty)) }
196                }
197            }
198        )+
199    };
200}
201
202macro_rules! pwm_advanced_hal {
203    ($($TIMX:ident: (
204        $CH:ty,
205        $ccxe:ident $(: $ccxne:ident)*,
206        $ccmrx_output:ident,
207        $ocxpe:ident,
208        $ocxm:ident,
209        $ccrx:ident
210        $(, $moe:ident)*
211    ) ,)+
212    ) => {
213        $(
214            impl hal::PwmPin for PwmPin<$TIMX, $CH> {
215                type Duty = u16;
216
217                fn disable(&mut self) {
218                    unsafe {
219                        (*$TIMX::ptr()).ccer.modify(|_, w| w.$ccxe().clear_bit());
220                    }
221                }
222
223                fn enable(&mut self) {
224                    unsafe {
225                        let tim = &*$TIMX::ptr();
226                        tim.$ccmrx_output().modify(|_, w| w.$ocxpe().set_bit().$ocxm().bits(6));
227                        tim.ccer.modify(|_, w| w.$ccxe().set_bit());
228                        $(
229                            tim.ccer.modify(|_, w| w.$ccxne().bit(true));
230                        )*
231                        $(
232                            tim.bdtr.modify(|_, w| w.$moe().set_bit());
233                        )*
234                    }
235                }
236
237                fn get_duty(&self) -> u16 {
238                    unsafe { (*$TIMX::ptr()).$ccrx.read().$ccrx().bits() }
239                }
240
241                fn get_max_duty(&self) -> u16 {
242                    unsafe { (*$TIMX::ptr()).arr.read().arr().bits() }
243                }
244
245                fn set_duty(&mut self, duty: u16) {
246                    unsafe { (*$TIMX::ptr()).$ccrx.write(|w| w.$ccrx().bits(duty)) }
247                }
248            }
249
250            impl PwmPinMode for PwmPin<$TIMX, $CH>{
251                fn set_compare_mode(&mut self, mode: OutputCompareMode) {
252                    unsafe {
253                        let tim = &*$TIMX::ptr();
254                        tim.$ccmrx_output().modify(|_, w| w.$ocxm().bits(mode as u8));
255                    }
256                }
257            }
258        )+
259    };
260}
261
262pwm_advanced_hal! {
263    TIM1:  (Channel1, cc1e: cc1ne, ccmr1_output, oc1pe, oc1m, ccr1, moe),
264    TIM1:  (Channel2, cc2e: cc2ne, ccmr1_output, oc2pe, oc2m, ccr2, moe),
265    TIM1:  (Channel3, cc3e: cc3ne, ccmr2_output, oc3pe, oc3m, ccr3, moe),
266    TIM1:  (Channel4, cc4e, ccmr2_output, oc4pe, oc4m, ccr4, moe),
267    TIM14: (Channel1, cc1e, ccmr1_output, oc1pe, oc1m, ccr1),
268    TIM16: (Channel1, cc1e: cc1ne, ccmr1_output, oc1pe, oc1m, ccr1, moe),
269    TIM17: (Channel1, cc1e: cc1ne, ccmr1_output, oc1pe, oc1m, ccr1, moe),
270}
271
272#[cfg(any(feature = "stm32g070"))]
273pwm_advanced_hal! {
274    TIM15: (Channel1, cc1e: cc1ne, ccmr1_output, oc1pe, oc1m1, ccr1, moe),
275}
276
277#[cfg(any(feature = "stm32g071", feature = "stm32g081"))]
278pwm_advanced_hal! {
279    TIM15: (Channel1, cc1e: cc1ne, ccmr1_output, oc1pe, oc1m, ccr1, moe),
280}
281
282#[cfg(feature = "stm32g0x1")]
283pwm_hal! {
284    TIM2: (Channel1, cc1e, ccmr1_output, oc1pe, oc1m, ccr1, ccr1_l, ccr1_h),
285    TIM2: (Channel2, cc2e, ccmr1_output, oc2pe, oc2m, ccr2, ccr2_l, ccr2_h),
286    TIM2: (Channel3, cc3e, ccmr2_output, oc3pe, oc3m, ccr3, ccr3_l, ccr3_h),
287    TIM2: (Channel4, cc4e, ccmr2_output, oc4pe, oc4m, ccr4, ccr4_l, ccr4_h),
288    TIM3: (Channel1, cc1e, ccmr1_output, oc1pe, oc1m, ccr1, ccr1_l, ccr1_h),
289    TIM3: (Channel2, cc2e, ccmr1_output, oc2pe, oc2m, ccr2, ccr2_l, ccr2_h),
290    TIM3: (Channel3, cc3e, ccmr2_output, oc3pe, oc3m, ccr3, ccr3_l, ccr3_h),
291    TIM3: (Channel4, cc4e, ccmr2_output, oc4pe, oc4m, ccr4, ccr4_l, ccr4_h),
292}
293
294#[cfg(feature = "stm32g070")]
295pwm_hal! {
296    TIM3: (Channel1, cc1e, ccmr1_output, oc1pe, oc1m, ccr1, ccr1_l, ccr1_h),
297    TIM3: (Channel2, cc2e, ccmr1_output, oc2pe, oc2m, ccr2, ccr2_l, ccr2_h),
298    TIM3: (Channel3, cc3e, ccmr2_output, oc3pe, oc3m, ccr3, ccr3_l, ccr3_h),
299    TIM3: (Channel4, cc4e, ccmr2_output, oc4pe, oc4m, ccr4, ccr4_l, ccr4_h),
300}
301
302pwm! {
303    TIM1: (tim1, arr),
304    TIM3: (tim3, arr_l, arr_h),
305    TIM14: (tim14, arr),
306    TIM16: (tim16, arr),
307    TIM17: (tim17, arr),
308}
309
310#[cfg(feature = "stm32g0x1")]
311pwm! {
312    TIM2: (tim2, arr_l, arr_h),
313}
314
315#[cfg(any(feature = "stm32g070", feature = "stm32g071", feature = "stm32g081"))]
316pwm! {
317    TIM15: (tim15, arr),
318}
319
320#[cfg(feature = "stm32g0x1")]
321pwm_q! {
322    TIM1: tim1,
323}
324
325#[cfg(any(feature = "stm32g071", feature = "stm32g081"))]
326pwm_q! {
327    TIM15: tim15,
328}