1 | //! PWM Input driver. |
2 | |
3 | use embassy_hal_internal::into_ref; |
4 | |
5 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; |
6 | use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; |
7 | use crate::gpio::{AfType, Pull}; |
8 | use crate::time::Hertz; |
9 | use crate::Peripheral; |
10 | |
11 | /// PWM Input driver. |
12 | pub struct PwmInput<'d, T: GeneralInstance4Channel> { |
13 | channel: Channel, |
14 | inner: Timer<'d, T>, |
15 | } |
16 | |
17 | impl<'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 | } |
115 | |