embassy_stm32/timer/
pwm_input.rs

1//! PWM Input driver.
2
3use embassy_hal_internal::into_ref;
4
5use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};
6use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel};
7use crate::gpio::{AfType, Pull};
8use crate::time::Hertz;
9use crate::Peripheral;
10
11/// PWM Input driver.
12pub struct PwmInput<'d, T: GeneralInstance4Channel> {
13    channel: Channel,
14    inner: Timer<'d, T>,
15}
16
17impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
18    /// Create a new PWM input driver.
19    pub fn new(
20        tim: impl Peripheral<P = T> + 'd,
21        pin: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
22        pull: Pull,
23        freq: Hertz,
24    ) -> Self {
25        into_ref!(pin);
26
27        pin.set_as_af(pin.af_num(), AfType::input(pull));
28
29        Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
30    }
31
32    /// Create a new PWM input driver.
33    pub fn new_alt(
34        tim: impl Peripheral<P = T> + 'd,
35        pin: impl Peripheral<P = impl Channel2Pin<T>> + 'd,
36        pull: Pull,
37        freq: Hertz,
38    ) -> Self {
39        into_ref!(pin);
40
41        pin.set_as_af(pin.af_num(), AfType::input(pull));
42
43        Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)
44    }
45
46    fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self {
47        let mut inner = Timer::new(tim);
48
49        inner.set_counting_mode(CountingMode::EdgeAlignedUp);
50        inner.set_tick_freq(freq);
51        inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
52        inner.start();
53
54        // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6
55        // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode
56        inner.set_input_ti_selection(ch1, InputTISelection::Normal);
57        inner.set_input_capture_mode(ch1, InputCaptureMode::Rising);
58
59        inner.set_input_ti_selection(ch2, InputTISelection::Alternate);
60        inner.set_input_capture_mode(ch2, InputCaptureMode::Falling);
61
62        inner.set_trigger_source(match ch1 {
63            Channel::Ch1 => TriggerSource::TI1FP1,
64            Channel::Ch2 => TriggerSource::TI2FP2,
65            _ => panic!("Invalid channel for PWM input"),
66        });
67
68        inner.set_slave_mode(SlaveMode::RESET_MODE);
69
70        // Must call the `enable` function after
71
72        Self { channel: ch1, inner }
73    }
74
75    /// Enable the given channel.
76    pub fn enable(&mut self) {
77        self.inner.enable_channel(Channel::Ch1, true);
78        self.inner.enable_channel(Channel::Ch2, true);
79    }
80
81    /// Disable the given channel.
82    pub fn disable(&mut self) {
83        self.inner.enable_channel(Channel::Ch1, false);
84        self.inner.enable_channel(Channel::Ch2, false);
85    }
86
87    /// Check whether given channel is enabled
88    pub fn is_enabled(&self) -> bool {
89        self.inner.get_channel_enable_state(Channel::Ch1)
90    }
91
92    /// Get the period tick count
93    pub fn get_period_ticks(&self) -> u32 {
94        self.inner.get_capture_value(self.channel)
95    }
96
97    /// Get the pulse width tick count
98    pub fn get_width_ticks(&self) -> u32 {
99        self.inner.get_capture_value(match self.channel {
100            Channel::Ch1 => Channel::Ch2,
101            Channel::Ch2 => Channel::Ch1,
102            _ => panic!("Invalid channel for PWM input"),
103        })
104    }
105
106    /// Get the duty cycle in 100%
107    pub fn get_duty_cycle(&self) -> f32 {
108        let period = self.get_period_ticks();
109        if period == 0 {
110            return 0.;
111        }
112        100. * (self.get_width_ticks() as f32) / (period as f32)
113    }
114}