stm32f4xx_hal/timer/pwm_input.rs
1use super::{CPin, General, Instance, Timer, WithPwm};
2use crate::gpio::PushPull;
3use crate::pac;
4use core::convert::TryFrom;
5use core::ops::{Deref, DerefMut};
6use fugit::HertzU32 as Hertz;
7
8/// Represents a TIMer configured as a PWM input.
9/// This peripheral will emit an interrupt on CC2 events, which occurs at two times in this mode:
10/// 1. When a new cycle is started: the duty cycle will be `1.00`
11/// 2. When the period is captured. the duty cycle will be an observable value.
12///
13/// An example interrupt handler is provided:
14/// ```
15/// use stm32f4xx_hal::{pac::TIM8, pwm_input::PwmInput};
16///
17/// type Monitor = PwmInput<TIM8>;
18///
19/// fn tim8_cc2(monitor: &Monitor) {
20/// let duty_clocks = monitor.get_duty_cycle_clocks();
21/// let period_clocks = monitor.get_period_clocks();
22/// // check if this interrupt was caused by a capture at the wrong CC2,
23/// // peripheral limitation.
24/// if !monitor.is_valid_capture() {
25/// return;
26/// }
27/// let duty = monitor.get_duty_cycle();
28/// }
29/// ```
30pub struct PwmInput<TIM>
31where
32 TIM: Instance + WithPwm + CPin<0>,
33{
34 timer: Timer<TIM>,
35 _pins: TIM::Ch<PushPull>,
36}
37
38impl<TIM> Deref for PwmInput<TIM>
39where
40 TIM: Instance + WithPwm + CPin<0>,
41{
42 type Target = Timer<TIM>;
43 fn deref(&self) -> &Self::Target {
44 &self.timer
45 }
46}
47
48impl<TIM> DerefMut for PwmInput<TIM>
49where
50 TIM: Instance + WithPwm + CPin<0>,
51{
52 fn deref_mut(&mut self) -> &mut Self::Target {
53 &mut self.timer
54 }
55}
56
57impl<TIM> PwmInput<TIM>
58where
59 TIM: Instance + WithPwm + CPin<0>,
60{
61 pub fn release(mut self) -> Timer<TIM> {
62 self.tim.cr1_reset();
63 self.timer
64 }
65}
66
67#[cfg(not(feature = "gpio-f410"))]
68macro_rules! hal {
69 ($TIM:ty) => {
70 impl Timer<$TIM> {
71 /// Configures this timer for PWM input. Accepts the `best_guess` frequency of the signal
72 /// Note: this should be as close as possible to the frequency of the PWM waveform for best
73 /// accuracy.
74 ///
75 /// This device will emit an interrupt on CC1, which occurs at two times in this mode:
76 /// 1. When a new cycle is started: the duty cycle will be `1.00`
77 /// 2. When the period is captured. the duty cycle will be an observable value.
78 ///
79 /// See the pwm input example for an suitable interrupt handler.
80 pub fn pwm_input(
81 mut self,
82 best_guess: Hertz,
83 pins: impl Into<<$TIM as CPin<0>>::Ch<PushPull>>,
84 ) -> PwmInput<$TIM> {
85 let pins = pins.into();
86
87 /*
88 Borrowed from PWM implementation.
89 Sets the TIMer's prescaler such that the TIMer that it ticks at about the best-guess
90 frequency.
91 */
92 let ticks = self.clk.raw() / best_guess.raw();
93 let psc = u16::try_from((ticks - 1) / (1 << 16)).unwrap();
94 self.tim.set_prescaler(psc);
95
96 // Seemingly this needs to be written to
97 // self.tim.arr().write(|w| w.arr().bits(u16::MAX));
98
99 /*
100 For example, one can measure the period (in TIMx_CCR1 register) and the duty cycle (in
101 TIMx_CCR2 register) of the PWM applied on TI1 using the following procedure (depending
102 on CK_INT frequency and prescaler value):
103
104 from RM0390 16.3.7
105 */
106
107 // Select the active input for TIMx_CCR1: write the CC1S bits to 01 in the TIMx_CCMR1
108 // register (TI1 selected).
109 self.tim
110 .ccmr1_input()
111 .modify(|_, w| unsafe { w.cc1s().bits(0b01) });
112
113 // Select the active polarity for TI1FP1 (used both for capture in TIMx_CCR1 and counter
114 // clear): write the CC1P and CC1NP bits to ‘0’ (active on rising edge).
115
116 self.tim
117 .ccer()
118 .modify(|_, w| w.cc1p().clear_bit().cc2p().clear_bit());
119
120 // disable filters and disable the input capture prescalers.
121 self.tim.ccmr1_input().modify(|_, w| unsafe {
122 w.ic1f().bits(0).ic2f().bits(0);
123 w.ic1psc().bits(0).ic2psc().bits(0)
124 });
125
126 // Select the active input for TIMx_CCR2: write the CC2S bits to 10 in the TIMx_CCMR1
127 // register (TI1 selected)
128 self.tim
129 .ccmr1_input()
130 .modify(|_, w| unsafe { w.cc2s().bits(0b10) });
131
132 // Select the active polarity for TI1FP2 (used for capture in TIMx_CCR2): write the CC2P
133 // and CC2NP bits to ‘1’ (active on falling edge).
134 self.tim
135 .ccer()
136 .modify(|_, w| w.cc2p().set_bit().cc2np().set_bit());
137
138 // Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register
139 // (TI1FP1 selected).
140 self.tim.smcr().modify(|_, w| unsafe { w.ts().bits(0b101) });
141
142 // Configure the slave mode controller in reset mode: write the SMS bits to 100 in the
143 // TIMx_SMCR register.
144 self.tim
145 .smcr()
146 .modify(|_, w| unsafe { w.sms().bits(0b100) });
147
148 // Enable the captures: write the CC1E and CC2E bits to ‘1’ in the TIMx_CCER register.
149 self.tim
150 .ccer()
151 .modify(|_, w| w.cc1e().set_bit().cc2e().set_bit());
152
153 // enable interrupts.
154 self.tim.dier().modify(|_, w| w.cc2ie().set_bit());
155 // enable the counter.
156 self.tim.enable_counter(true);
157
158 PwmInput {
159 timer: self,
160 _pins: pins,
161 }
162 }
163 }
164
165 impl PwmInput<$TIM> {
166 /// Period of PWM signal in terms of clock cycles
167 pub fn get_period_clocks(&self) -> <$TIM as General>::Width {
168 self.tim.ccr1().read().ccr().bits()
169 }
170 /// Duty cycle in terms of clock cycles
171 pub fn get_duty_cycle_clocks(&self) -> <$TIM as General>::Width {
172 self.tim.ccr2().read().ccr().bits()
173 }
174 /// Observed duty cycle as a float in range [0.00, 1.00]
175 pub fn get_duty_cycle(&self) -> f32 {
176 let period_clocks = self.get_period_clocks();
177 if period_clocks == 0 {
178 return 0.;
179 };
180 (self.get_duty_cycle_clocks() as f32 / period_clocks as f32) * 100f32
181 }
182 /// Returns whether the timer's duty cycle is a valid observation
183 /// (Limitation of how the captures work is extra CC2 interrupts are generated when the
184 /// PWM cycle enters a new period).
185 pub fn is_valid_capture(&self) -> bool {
186 self.get_duty_cycle_clocks() != self.get_period_clocks()
187 }
188 }
189 };
190}
191
192#[cfg(feature = "tim1")]
193hal! { pac::TIM1 }
194#[cfg(feature = "tim2")]
195hal! { pac::TIM2 }
196#[cfg(feature = "tim3")]
197hal! { pac::TIM3 }
198#[cfg(feature = "tim4")]
199hal! { pac::TIM4 }
200#[cfg(feature = "tim5")]
201hal! { pac::TIM5 }
202#[cfg(feature = "tim8")]
203hal! { pac::TIM8 }
204#[cfg(feature = "tim9")]
205hal! { pac::TIM9 }
206#[cfg(feature = "tim12")]
207hal! { pac::TIM12 }