embassy_stm32/timer/
pwm_input.rs

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