stm32f7xx_hal/timer/
pwm_input.rs

1use super::{CPin, General, Instance, Timer, WithPwm};
2use core::convert::TryFrom;
3use core::ops::{Deref, DerefMut};
4use fugit::HertzU32 as Hertz;
5
6pub trait Pins<TIM> {}
7
8// implement the `Pins` trait wherever PC1 implements CPin<C1>
9impl<TIM, PC1> Pins<TIM> for PC1 where PC1: CPin<TIM, 0> {}
10
11/// Represents a TIMer configured as a PWM input.
12/// This peripheral will emit an interrupt on CC2 events, which occurs at two times in this mode:
13/// 1. When a new cycle is started: the duty cycle will be `1.00`
14/// 2. When the period is captured. the duty cycle will be an observable value.
15/// An example interrupt handler is provided:
16/// ```
17/// use stm32f4xx_hal::pac::TIM8;
18/// use stm32f4xx_hal::timer::Timer;
19/// use stm32f4xx_hal::pwm_input::PwmInput;
20/// use stm32f4xx_hal::gpio::gpioc::PC6;
21/// use stm32f4xx_hal::gpio::Alternate;
22///
23/// type Monitor = PwmInput<TIM8, PC6<Alternate<3>>>;
24///
25/// fn tim8_cc2(monitor: &Monitor) {
26///             let duty_clocks = monitor.get_duty_cycle_clocks();
27///             let period_clocks = monitor.get_period_clocks();
28///             // check if this interrupt was caused by a capture at the wrong CC2,
29///             // peripheral limitation.
30///             if !monitor.is_valid_capture(){
31///                 return;
32///             }
33///             let duty = monitor.get_duty_cycle();
34/// }
35/// ```
36pub struct PwmInput<TIM, PINS>
37where
38    TIM: Instance + WithPwm,
39    PINS: Pins<TIM>,
40{
41    timer: Timer<TIM>,
42    _pins: PINS,
43}
44
45impl<TIM, PINS> Deref for PwmInput<TIM, PINS>
46where
47    TIM: Instance + WithPwm,
48    PINS: Pins<TIM>,
49{
50    type Target = Timer<TIM>;
51    fn deref(&self) -> &Self::Target {
52        &self.timer
53    }
54}
55
56impl<TIM, PINS> DerefMut for PwmInput<TIM, PINS>
57where
58    TIM: Instance + WithPwm,
59    PINS: Pins<TIM>,
60{
61    fn deref_mut(&mut self) -> &mut Self::Target {
62        &mut self.timer
63    }
64}
65
66impl<TIM, PINS> PwmInput<TIM, PINS>
67where
68    TIM: Instance + WithPwm,
69    PINS: Pins<TIM>,
70{
71    pub fn release(mut self) -> Timer<TIM> {
72        self.tim.cr1_reset();
73        self.timer
74    }
75}
76
77#[cfg(not(feature = "stm32f410"))]
78macro_rules! hal {
79    ($($TIM:ident,)+) => {
80        $(
81        // Drag the associated TIM object into scope.
82        // Note: its drawn in via the macro to avoid duplicating the feature gate this macro is
83        //       expecting to be guarded by.
84        use crate::pac::$TIM;
85
86        impl Timer<$TIM> {
87            /// Configures this timer for PWM input. Accepts the `best_guess` frequency of the signal
88            /// Note: this should be as close as possible to the frequency of the PWM waveform for best
89            /// accuracy.
90            ///
91            /// This device will emit an interrupt on CC1, which occurs at two times in this mode:
92            /// 1. When a new cycle is started: the duty cycle will be `1.00`
93            /// 2. When the period is captured. the duty cycle will be an observable value.
94            /// See the pwm input example for an suitable interrupt handler.
95            #[allow(unused_unsafe)] //for some chips the operations are considered safe.
96            pub fn pwm_input<PINS>(mut self, best_guess: Hertz, pins: PINS) -> PwmInput<$TIM, PINS>
97            where
98                PINS: Pins<$TIM>,
99            {
100                /*
101                Borrowed from PWM implementation.
102                Sets the TIMer's prescaler such that the TIMer that it ticks at about the best-guess
103                 frequency.
104                */
105                let ticks = self.clk.raw() / best_guess.raw();
106                let psc = u16::try_from((ticks - 1) / (1 << 16)).unwrap();
107                self.tim.set_prescaler(psc);
108
109                // Seemingly this needs to be written to
110                // self.tim.arr.write(|w| w.arr().bits(u16::MAX));
111
112                /*
113                For example, one can measure the period (in TIMx_CCR1 register) and the duty cycle (in
114                TIMx_CCR2 register) of the PWM applied on TI1 using the following procedure (depending
115                on CK_INT frequency and prescaler value):
116
117                from RM0390 16.3.7
118                 */
119
120                // Select the active input for TIMx_CCR1: write the CC1S bits to 01 in the TIMx_CCMR1
121                // register (TI1 selected).
122                self.tim
123                    .ccmr1_input()
124                    .modify(|_, w| unsafe { w.cc1s().bits(0b01) });
125
126                // Select the active polarity for TI1FP1 (used both for capture in TIMx_CCR1 and counter
127                // clear): write the CC1P and CC1NP bits to ‘0’ (active on rising edge).
128
129                self.tim
130                    .ccer
131                    .modify(|_, w| w.cc1p().clear_bit().cc2p().clear_bit());
132
133                // disable filters and disable the input capture prescalers.
134                self.tim.ccmr1_input().modify(|_, w| unsafe {
135                    w.ic1f()
136                        .bits(0)
137                        .ic2f()
138                        .bits(0)
139                        .ic1psc()
140                        .bits(0)
141                        .ic2psc()
142                        .bits(0)
143                });
144
145                // Select the active input for TIMx_CCR2: write the CC2S bits to 10 in the TIMx_CCMR1
146                // register (TI1 selected)
147                self.tim
148                    .ccmr1_input()
149                    .modify(|_, w| unsafe { w.cc2s().bits(0b10) });
150
151                // Select the active polarity for TI1FP2 (used for capture in TIMx_CCR2): write the CC2P
152                // and CC2NP bits to ‘1’ (active on falling edge).
153                self.tim
154                    .ccer
155                    .modify(|_, w| w.cc2p().set_bit().cc2np().set_bit());
156
157                // Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register
158                // (TI1FP1 selected).
159                self.tim.smcr.modify(|_, w| unsafe { w.ts().bits(0b101) });
160
161                // Configure the slave mode controller in reset mode: write the SMS bits to 100 in the
162                // TIMx_SMCR register.
163                self.tim.smcr.modify(|_, w| unsafe { w.sms().bits(0b100) });
164
165                // Enable the captures: write the CC1E and CC2E bits to ‘1’ in the TIMx_CCER register.
166                self.tim
167                    .ccer
168                    .modify(|_, w| w.cc1e().set_bit().cc2e().set_bit());
169
170                // enable interrupts.
171                self.tim.dier.modify(|_, w| w.cc2ie().set_bit());
172                // enable the counter.
173                self.tim.enable_counter();
174
175                PwmInput { timer: self, _pins: pins }
176            }
177        }
178
179        impl<PINS> PwmInput<$TIM, PINS>
180        where
181            PINS: Pins<$TIM>,
182        {
183            /// Period of PWM signal in terms of clock cycles
184            pub fn get_period_clocks(&self) -> <$TIM as General>::Width {
185                self.tim.ccr1().read().ccr().bits()
186            }
187            /// Duty cycle in terms of clock cycles
188            pub fn get_duty_cycle_clocks(&self) -> <$TIM as General>::Width {
189                self.tim.ccr2().read().ccr().bits()
190            }
191            /// Observed duty cycle as a float in range [0.00, 1.00]
192            pub fn get_duty_cycle(&self) -> f32 {
193                let period_clocks = self.get_period_clocks();
194                if period_clocks == 0 {
195                    return 0.0;
196                };
197                return (self.get_duty_cycle_clocks() as f32 / period_clocks as f32) * 100f32;
198            }
199            /// Returns whether the timer's duty cycle is a valid observation
200            /// (Limitation of how the captures work is extra CC2 interrupts are generated when the
201            /// PWM cycle enters a new period).
202            pub fn is_valid_capture(&self) -> bool {
203                self.get_duty_cycle_clocks() != self.get_period_clocks()
204            }
205        }
206    )+
207}}
208
209#[cfg(any(feature = "stm32f411",))]
210/* red group */
211hal! {
212    TIM4,
213    TIM3,
214    TIM2,
215}
216
217/* orange group */
218#[cfg(any(
219    feature = "stm32f401",
220    feature = "stm32f405",
221    feature = "stm32f407",
222    feature = "stm32f412",
223    feature = "stm32f413",
224    feature = "stm32f415",
225    feature = "stm32f417",
226    feature = "stm32f423",
227    feature = "stm32f427",
228    feature = "stm32f429",
229    feature = "stm32f437",
230    feature = "stm32f439",
231    feature = "stm32f446",
232    feature = "stm32f469",
233    feature = "stm32f479",
234))]
235hal! {
236    TIM2,
237    TIM3,
238    TIM4,
239}
240/* green group */
241#[cfg(any(
242    feature = "stm32f405",
243    feature = "stm32f407",
244    feature = "stm32f412",
245    feature = "stm32f413",
246    feature = "stm32f415",
247    feature = "stm32f417",
248    feature = "stm32f423",
249    feature = "stm32f427",
250    feature = "stm32f429",
251    feature = "stm32f437",
252    feature = "stm32f439",
253    feature = "stm32f446",
254    feature = "stm32f469",
255    feature = "stm32f479",
256))]
257hal! {
258    TIM8,
259    TIM12,
260}
261
262/* every chip across the series have these timers with support for this feature.
263.. except for the 410 which, while the timers support this feature, has a different configuration
264   than the rest of the series.
265*/
266/* yellow group */
267#[cfg(not(feature = "stm32f410"))]
268hal! {
269    TIM1,
270    TIM5,
271    TIM9,
272}