| 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 | |