stm32f1xx_hal/timer/
pwm_input.rs

1//! This module allows Timer peripherals to be configured as pwm input.
2//! In this mode, the timer sample a squared signal to find it's frequency and duty cycle.
3//!
4//! Also this module provides Quadrature Encoder Interface
5
6use crate::pac::{self, DBGMCU as DBG};
7
8use crate::afio::{RInto, Rmp, TimC};
9use crate::rcc::Rcc;
10use crate::time::Hertz;
11use crate::timer::{General, Timer};
12
13use embedded_hal_02 as hal;
14pub use hal::Direction;
15
16pub trait Instance: General + TimC<0> + TimC<1> {}
17
18#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))]
19impl Instance for pac::TIM1 {}
20impl Instance for pac::TIM2 {}
21impl Instance for pac::TIM3 {}
22#[cfg(feature = "medium")]
23impl Instance for pac::TIM4 {}
24
25/// PWM Input
26#[allow(unused)]
27pub struct PwmInput<TIM: Instance> {
28    timer: TIM,
29    pins: (<TIM as TimC<0>>::In, <TIM as TimC<1>>::In),
30}
31
32/// How the data is read from the timer
33pub enum ReadMode {
34    /// Return the latest captured data
35    Instant,
36    /// Wait for one period of the signal before computing the frequency and the duty cycle
37    /// The microcontroller will be halted for at most two period of the input signal.
38    WaitForNextCapture,
39}
40
41/// The error returned when reading a frequency from a timer
42#[derive(Debug)]
43pub enum Error {
44    /// The signal frequency is too low to be sampled by the current timer configuration
45    FrequencyTooLow,
46}
47
48/// Which frequency the timer will try to sample
49pub enum Configuration {
50    /// In this mode an algorithm calculates the optimal value for the autoreload register and the
51    /// prescaler register in order to be able to sample a wide range of frequency, at the expense
52    /// of resolution.
53    ///
54    /// The minimum frequency that can be sampled is 20% the provided frequency.
55    ///
56    /// Use this mode if you do not know what to choose.
57    Frequency(Hertz),
58
59    /// In this mode an algorithm calculates the optimal value for the autoreload register and the
60    /// prescaler register in order to sample the duty cycle with a high resolution.
61    /// This will limit the frequency range where the timer can operate.
62    ///
63    /// The minimum frequency that can be sampled is 90% the provided frequency
64    DutyCycle(Hertz),
65
66    /// In this mode an algorithm calculates the optimal value for the autoreload register and the
67    /// prescaler register in order to be able to sample signal with a frequency higher than the
68    /// provided value : there is no margin for lower frequencies.
69    RawFrequency(Hertz),
70
71    /// In this mode, the provided arr and presc are directly programmed in the register.
72    RawValues { arr: u16, presc: u16 },
73}
74
75/// SMS (Slave Mode Selection) register
76#[derive(Copy, Clone, Debug)]
77pub enum SlaveMode {
78    /// Counter counts up/down on TI2FP1 edge depending on TI1FP2 level.
79    EncoderMode1 = 0b001,
80    /// Encoder mode 2 - Counter counts up/down on TI1FP2 edge depending on TI2FP1 level.
81    EncoderMode2 = 0b010,
82    /// Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the
83    /// level of the other input.
84    EncoderMode3 = 0b011,
85    /// Reset Mode - Rising edge of the selected trigger input (TRGI) reinitializes the counter and
86    /// generates an update of the registers.
87    ResetMode = 0b100,
88    /// Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not
89    /// reset). Only the start of the counter is controlled.
90    TriggerMode = 0b110,
91    /// External Clock Mode 1 - Rising edges of the selected trigger (TRGI) clock the counter.
92    ExternalClockMode1 = 0b111,
93}
94
95/// Quadrature Encoder Interface (QEI) options
96///
97/// The `Default` implementation provides a configuration for a 4-count pulse which counts from
98/// 0-65535. The counter wraps back to 0 on overflow.
99#[derive(Copy, Clone, Debug)]
100pub struct QeiOptions {
101    /// Encoder slave mode
102    pub slave_mode: SlaveMode,
103
104    /// Autoreload value
105    ///
106    /// This value allows the maximum count to be configured, up to 65535. Setting a lower value
107    /// will overflow the counter to 0 sooner.
108    pub auto_reload_value: u16,
109}
110
111impl Default for QeiOptions {
112    fn default() -> Self {
113        Self {
114            slave_mode: SlaveMode::EncoderMode3,
115            auto_reload_value: u16::MAX,
116        }
117    }
118}
119
120/// Quadrature Encoder Interface (QEI)
121pub struct Qei<TIM: Instance> {
122    tim: TIM,
123    pins: (<TIM as TimC<0>>::In, <TIM as TimC<1>>::In),
124}
125
126pub trait PwmInputExt: Sized + Instance {
127    fn pwm_input(
128        self,
129        pins: (
130            impl RInto<<Self as TimC<0>>::In, 0>,
131            impl RInto<<Self as TimC<1>>::In, 0>,
132        ),
133        dbg: &mut DBG,
134        mode: Configuration,
135        rcc: &mut Rcc,
136    ) -> PwmInput<Self>;
137}
138
139pub trait QeiExt: Sized + Instance {
140    fn qei(
141        self,
142        pins: (
143            impl RInto<<Self as TimC<0>>::In, 0>,
144            impl RInto<<Self as TimC<1>>::In, 0>,
145        ),
146        options: QeiOptions,
147        rcc: &mut Rcc,
148    ) -> Qei<Self>;
149}
150
151/// Courtesy of @TeXitoi (https://github.com/stm32-rs/stm32f1xx-hal/pull/10#discussion_r259535503)
152fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) {
153    if freq == 0 {
154        return (u16::MAX, u16::MAX);
155    }
156    let presc = clock / freq.saturating_mul(u16::MAX as u32 + 1);
157    let arr = clock / freq.saturating_mul(presc + 1);
158    (core::cmp::max(1, arr as u16), presc as u16)
159}
160macro_rules! hal {
161    ($TIM:ty: $timX:ident, $qeix:ident) => {
162        impl Timer<$TIM> {
163            pub fn pwm_input(
164                mut self,
165                pins: (
166                    impl RInto<<$TIM as TimC<0>>::In, 0>,
167                    impl RInto<<$TIM as TimC<1>>::In, 0>,
168                ),
169                dbg: &mut DBG,
170                mode: Configuration,
171            ) -> PwmInput<$TIM> {
172                self.stop_in_debug(dbg, false);
173                let Self { tim, clk } = self;
174                $timX(tim, pins, clk, mode)
175            }
176            pub fn qei(
177                self,
178                pins: (
179                    impl RInto<<$TIM as TimC<0>>::In, 0>,
180                    impl RInto<<$TIM as TimC<1>>::In, 0>,
181                ),
182                options: QeiOptions,
183            ) -> Qei<$TIM> {
184                let Self { tim, clk: _ } = self;
185                $qeix(tim, pins, options)
186            }
187        }
188        impl PwmInputExt for $TIM {
189            fn pwm_input(
190                self,
191                pins: (
192                    impl RInto<<Self as TimC<0>>::In, 0>,
193                    impl RInto<<Self as TimC<1>>::In, 0>,
194                ),
195                dbg: &mut DBG,
196                mode: Configuration,
197                rcc: &mut Rcc,
198            ) -> PwmInput<Self> {
199                Timer::new(self, rcc).pwm_input(pins, dbg, mode)
200            }
201        }
202        impl QeiExt for $TIM {
203            fn qei(
204                self,
205                pins: (
206                    impl RInto<<Self as TimC<0>>::In, 0>,
207                    impl RInto<<Self as TimC<1>>::In, 0>,
208                ),
209                options: QeiOptions,
210                rcc: &mut Rcc,
211            ) -> Qei<Self> {
212                Timer::new(self, rcc).qei(pins, options)
213            }
214        }
215
216        impl<const R: u8> Rmp<$TIM, R> {
217            pub fn pwm_input(
218                self,
219                pins: (
220                    impl RInto<<$TIM as TimC<0>>::In, R>,
221                    impl RInto<<$TIM as TimC<1>>::In, R>,
222                ),
223                dbg: &mut DBG,
224                mode: Configuration,
225                rcc: &mut Rcc,
226            ) -> PwmInput<$TIM> {
227                let mut tim = Timer::new(self.0, rcc);
228                tim.stop_in_debug(dbg, false);
229                let Timer { tim, clk } = tim;
230                $timX(tim, pins, clk, mode)
231            }
232            pub fn qei(
233                self,
234                pins: (
235                    impl RInto<<$TIM as TimC<0>>::In, R>,
236                    impl RInto<<$TIM as TimC<1>>::In, R>,
237                ),
238                options: QeiOptions,
239                rcc: &mut Rcc,
240            ) -> Qei<$TIM> {
241                let Timer { tim, clk: _ } = Timer::new(self.0, rcc);
242                $qeix(tim, pins, options)
243            }
244        }
245
246        fn $timX<const R: u8>(
247            tim: $TIM,
248            pins: (
249                impl RInto<<$TIM as TimC<0>>::In, R>,
250                impl RInto<<$TIM as TimC<1>>::In, R>,
251            ),
252            clk: Hertz,
253            mode: Configuration,
254        ) -> PwmInput<$TIM> {
255            let pins = (pins.0.rinto(), pins.1.rinto());
256
257            use Configuration::*;
258            // Disable capture on both channels during setting
259            // (for Channel X bit is CCXE)
260            tim.ccer().modify(|_, w| {
261                w.cc1e().clear_bit();
262                w.cc2e().clear_bit();
263                w.cc1p().clear_bit();
264                w.cc2p().set_bit()
265            });
266
267            // Define the direction of the channel (input/output)
268            // and the used input
269            tim.ccmr1_input().modify(|_, w| w.cc1s().ti1().cc2s().ti1());
270
271            tim.dier().write(|w| w.cc1ie().set_bit());
272
273            // Configure slave mode control register
274            // Selects the trigger input to be used to synchronize the counter
275            // 101: Filtered Timer Input 1 (TI1FP1)
276            // ---------------------------------------
277            // Slave Mode Selection :
278            //  100: Reset Mode - Rising edge of the selected trigger input (TRGI)
279            //  reinitializes the counter and generates an update of the registers.
280            tim.smcr()
281                .modify(|_, w| unsafe { w.ts().bits(0b101).sms().bits(0b100) });
282
283            match mode {
284                Frequency(f) => {
285                    let freq = f.raw();
286                    let max_freq = if freq > 5 { freq / 5 } else { 1 };
287                    let (arr, presc) = compute_arr_presc(max_freq, clk.raw());
288                    tim.arr().write(|w| w.arr().set(arr));
289                    tim.psc().write(|w| w.psc().set(presc));
290                }
291                DutyCycle(f) => {
292                    let freq = f.raw();
293                    let max_freq = if freq > 2 {
294                        freq / 2 + freq / 4 + freq / 8
295                    } else {
296                        1
297                    };
298                    let (arr, presc) = compute_arr_presc(max_freq, clk.raw());
299                    tim.arr().write(|w| w.arr().set(arr));
300                    tim.psc().write(|w| w.psc().set(presc));
301                }
302                RawFrequency(f) => {
303                    let freq = f.raw();
304                    let (arr, presc) = compute_arr_presc(freq, clk.raw());
305                    tim.arr().write(|w| w.arr().set(arr));
306                    tim.psc().write(|w| w.psc().set(presc));
307                }
308                RawValues { arr, presc } => {
309                    tim.arr().write(|w| w.arr().set(arr));
310                    tim.psc().write(|w| w.psc().set(presc));
311                }
312            }
313
314            // Enable Capture on both channels
315            tim.ccer()
316                .modify(|_, w| w.cc1e().set_bit().cc2e().set_bit());
317
318            tim.cr1().modify(|_, w| w.cen().set_bit());
319            PwmInput { timer: tim, pins }
320        }
321
322        impl PwmInput<$TIM> {
323            /// Return the frequency sampled by the timer
324            pub fn read_frequency(&self, mode: ReadMode, clk: Hertz) -> Result<Hertz, Error> {
325                if let ReadMode::WaitForNextCapture = mode {
326                    self.wait_for_capture();
327                }
328
329                let presc = unsafe { (*<$TIM>::ptr()).psc().read().bits() as u16 };
330                let ccr1 = unsafe { (*<$TIM>::ptr()).ccr1().read().bits() as u16 };
331
332                // Formulas :
333                //
334                // F_timer = F_pclk / (PSC+1)*(ARR+1)
335                // Frac_arr = (CCR1+1)/(ARR+1)
336                // F_signal = F_timer/Frac_arr
337                // <=> F_signal = [(F_plck)/((PSC+1)*(ARR+1))] * [(ARR+1)/(CCR1+1)]
338                // <=> F_signal = F_pclk / ((PSC+1)*(CCR1+1))
339                //
340                // Where :
341                // * PSC is the prescaler register
342                // * ARR is the auto-reload register
343                // * F_timer is the number of time per second where the timer overflow under normal
344                // condition
345                //
346                if ccr1 == 0 {
347                    Err(Error::FrequencyTooLow)
348                } else {
349                    Ok(clk / ((presc + 1) as u32 * (ccr1 + 1) as u32))
350                }
351            }
352
353            /// Return the duty in the form of a fraction : (duty_cycle/period)
354            pub fn read_duty(&self, mode: ReadMode) -> Result<(u16, u16), Error> {
355                if let ReadMode::WaitForNextCapture = mode {
356                    self.wait_for_capture();
357                }
358
359                // Formulas :
360                // Duty_cycle = (CCR2+1)/(CCR1+1)
361                let ccr1 = unsafe { (*<$TIM>::ptr()).ccr1().read().bits() as u16 };
362                let ccr2 = unsafe { (*<$TIM>::ptr()).ccr2().read().bits() as u16 };
363                if ccr1 == 0 {
364                    Err(Error::FrequencyTooLow)
365                } else {
366                    Ok((ccr2, ccr1))
367                }
368            }
369
370            /// Wait until the timer has captured a period
371            fn wait_for_capture(&self) {
372                unsafe { &(*<$TIM>::ptr()) }.sr().write(|w| {
373                    w.uif().clear_bit();
374                    w.cc1if().clear_bit();
375                    w.cc1of().clear_bit()
376                });
377                while unsafe { (*<$TIM>::ptr()).sr().read().cc1if().bit_is_clear() } {}
378            }
379
380            pub fn release(self) -> ($TIM, (<$TIM as TimC<0>>::In, <$TIM as TimC<1>>::In)) {
381                (self.timer, self.pins)
382            }
383        }
384
385        fn $qeix<const R: u8>(
386            tim: $TIM,
387            pins: (
388                impl RInto<<$TIM as TimC<0>>::In, R>,
389                impl RInto<<$TIM as TimC<1>>::In, R>,
390            ),
391            options: QeiOptions,
392        ) -> Qei<$TIM> {
393            let pins = (pins.0.rinto(), pins.1.rinto());
394            // Configure TxC1 and TxC2 as captures
395            tim.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2());
396
397            // enable and configure to capture on rising edge
398            tim.ccer().write(|w| {
399                w.cc1e().set_bit();
400                w.cc1p().clear_bit();
401                w.cc2e().set_bit();
402                w.cc2p().clear_bit()
403            });
404
405            // configure as quadrature encoder
406            tim.smcr().write(|w| w.sms().set(options.slave_mode as u8));
407
408            tim.arr().write(|w| w.arr().set(options.auto_reload_value));
409            tim.cr1().write(|w| w.cen().set_bit());
410
411            Qei { tim, pins }
412        }
413
414        impl Qei<$TIM> {
415            pub fn release(self) -> ($TIM, (<$TIM as TimC<0>>::In, <$TIM as TimC<1>>::In)) {
416                (self.tim, self.pins)
417            }
418        }
419
420        impl hal::Qei for Qei<$TIM> {
421            type Count = u16;
422
423            fn count(&self) -> u16 {
424                self.tim.cnt().read().cnt().bits()
425            }
426
427            fn direction(&self) -> Direction {
428                if self.tim.cr1().read().dir().bit_is_clear() {
429                    Direction::Upcounting
430                } else {
431                    Direction::Downcounting
432                }
433            }
434        }
435    };
436}
437
438#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "connectivity"))]
439hal!(pac::TIM1: tim1, qei1);
440
441hal!(pac::TIM2: tim2, qei2);
442hal!(pac::TIM3: tim3, qei3);
443
444#[cfg(feature = "medium")]
445hal!(pac::TIM4: tim4, qei4);