stm32f7xx_hal/timer/pwm_input.rs
1use super::{CPin, General, Instance, Timer, WithPwm};
2use core::convert::TryFrom;
3use core::ops::{Deref, DerefMut};
4use fugit::HertzU32 as Hertz;
5
6pub trait Pins<TIM> {}
7
8// implement the `Pins` trait wherever PC1 implements CPin<C1>
9impl<TIM, PC1> Pins<TIM> for PC1 where PC1: CPin<TIM, 0> {}
10
11/// Represents a TIMer configured as a PWM input.
12/// This peripheral will emit an interrupt on CC2 events, which occurs at two times in this mode:
13/// 1. When a new cycle is started: the duty cycle will be `1.00`
14/// 2. When the period is captured. the duty cycle will be an observable value.
15/// An example interrupt handler is provided:
16/// ```
17/// use stm32f4xx_hal::pac::TIM8;
18/// use stm32f4xx_hal::timer::Timer;
19/// use stm32f4xx_hal::pwm_input::PwmInput;
20/// use stm32f4xx_hal::gpio::gpioc::PC6;
21/// use stm32f4xx_hal::gpio::Alternate;
22///
23/// type Monitor = PwmInput<TIM8, PC6<Alternate<3>>>;
24///
25/// fn tim8_cc2(monitor: &Monitor) {
26/// let duty_clocks = monitor.get_duty_cycle_clocks();
27/// let period_clocks = monitor.get_period_clocks();
28/// // check if this interrupt was caused by a capture at the wrong CC2,
29/// // peripheral limitation.
30/// if !monitor.is_valid_capture(){
31/// return;
32/// }
33/// let duty = monitor.get_duty_cycle();
34/// }
35/// ```
36pub struct PwmInput<TIM, PINS>
37where
38 TIM: Instance + WithPwm,
39 PINS: Pins<TIM>,
40{
41 timer: Timer<TIM>,
42 _pins: PINS,
43}
44
45impl<TIM, PINS> Deref for PwmInput<TIM, PINS>
46where
47 TIM: Instance + WithPwm,
48 PINS: Pins<TIM>,
49{
50 type Target = Timer<TIM>;
51 fn deref(&self) -> &Self::Target {
52 &self.timer
53 }
54}
55
56impl<TIM, PINS> DerefMut for PwmInput<TIM, PINS>
57where
58 TIM: Instance + WithPwm,
59 PINS: Pins<TIM>,
60{
61 fn deref_mut(&mut self) -> &mut Self::Target {
62 &mut self.timer
63 }
64}
65
66impl<TIM, PINS> PwmInput<TIM, PINS>
67where
68 TIM: Instance + WithPwm,
69 PINS: Pins<TIM>,
70{
71 pub fn release(mut self) -> Timer<TIM> {
72 self.tim.cr1_reset();
73 self.timer
74 }
75}
76
77#[cfg(not(feature = "stm32f410"))]
78macro_rules! hal {
79 ($($TIM:ident,)+) => {
80 $(
81 // Drag the associated TIM object into scope.
82 // Note: its drawn in via the macro to avoid duplicating the feature gate this macro is
83 // expecting to be guarded by.
84 use crate::pac::$TIM;
85
86 impl Timer<$TIM> {
87 /// Configures this timer for PWM input. Accepts the `best_guess` frequency of the signal
88 /// Note: this should be as close as possible to the frequency of the PWM waveform for best
89 /// accuracy.
90 ///
91 /// This device will emit an interrupt on CC1, which occurs at two times in this mode:
92 /// 1. When a new cycle is started: the duty cycle will be `1.00`
93 /// 2. When the period is captured. the duty cycle will be an observable value.
94 /// See the pwm input example for an suitable interrupt handler.
95 #[allow(unused_unsafe)] //for some chips the operations are considered safe.
96 pub fn pwm_input<PINS>(mut self, best_guess: Hertz, pins: PINS) -> PwmInput<$TIM, PINS>
97 where
98 PINS: Pins<$TIM>,
99 {
100 /*
101 Borrowed from PWM implementation.
102 Sets the TIMer's prescaler such that the TIMer that it ticks at about the best-guess
103 frequency.
104 */
105 let ticks = self.clk.raw() / best_guess.raw();
106 let psc = u16::try_from((ticks - 1) / (1 << 16)).unwrap();
107 self.tim.set_prescaler(psc);
108
109 // Seemingly this needs to be written to
110 // self.tim.arr.write(|w| w.arr().bits(u16::MAX));
111
112 /*
113 For example, one can measure the period (in TIMx_CCR1 register) and the duty cycle (in
114 TIMx_CCR2 register) of the PWM applied on TI1 using the following procedure (depending
115 on CK_INT frequency and prescaler value):
116
117 from RM0390 16.3.7
118 */
119
120 // Select the active input for TIMx_CCR1: write the CC1S bits to 01 in the TIMx_CCMR1
121 // register (TI1 selected).
122 self.tim
123 .ccmr1_input()
124 .modify(|_, w| unsafe { w.cc1s().bits(0b01) });
125
126 // Select the active polarity for TI1FP1 (used both for capture in TIMx_CCR1 and counter
127 // clear): write the CC1P and CC1NP bits to ‘0’ (active on rising edge).
128
129 self.tim
130 .ccer
131 .modify(|_, w| w.cc1p().clear_bit().cc2p().clear_bit());
132
133 // disable filters and disable the input capture prescalers.
134 self.tim.ccmr1_input().modify(|_, w| unsafe {
135 w.ic1f()
136 .bits(0)
137 .ic2f()
138 .bits(0)
139 .ic1psc()
140 .bits(0)
141 .ic2psc()
142 .bits(0)
143 });
144
145 // Select the active input for TIMx_CCR2: write the CC2S bits to 10 in the TIMx_CCMR1
146 // register (TI1 selected)
147 self.tim
148 .ccmr1_input()
149 .modify(|_, w| unsafe { w.cc2s().bits(0b10) });
150
151 // Select the active polarity for TI1FP2 (used for capture in TIMx_CCR2): write the CC2P
152 // and CC2NP bits to ‘1’ (active on falling edge).
153 self.tim
154 .ccer
155 .modify(|_, w| w.cc2p().set_bit().cc2np().set_bit());
156
157 // Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register
158 // (TI1FP1 selected).
159 self.tim.smcr.modify(|_, w| unsafe { w.ts().bits(0b101) });
160
161 // Configure the slave mode controller in reset mode: write the SMS bits to 100 in the
162 // TIMx_SMCR register.
163 self.tim.smcr.modify(|_, w| unsafe { w.sms().bits(0b100) });
164
165 // Enable the captures: write the CC1E and CC2E bits to ‘1’ in the TIMx_CCER register.
166 self.tim
167 .ccer
168 .modify(|_, w| w.cc1e().set_bit().cc2e().set_bit());
169
170 // enable interrupts.
171 self.tim.dier.modify(|_, w| w.cc2ie().set_bit());
172 // enable the counter.
173 self.tim.enable_counter();
174
175 PwmInput { timer: self, _pins: pins }
176 }
177 }
178
179 impl<PINS> PwmInput<$TIM, PINS>
180 where
181 PINS: Pins<$TIM>,
182 {
183 /// Period of PWM signal in terms of clock cycles
184 pub fn get_period_clocks(&self) -> <$TIM as General>::Width {
185 self.tim.ccr1().read().ccr().bits()
186 }
187 /// Duty cycle in terms of clock cycles
188 pub fn get_duty_cycle_clocks(&self) -> <$TIM as General>::Width {
189 self.tim.ccr2().read().ccr().bits()
190 }
191 /// Observed duty cycle as a float in range [0.00, 1.00]
192 pub fn get_duty_cycle(&self) -> f32 {
193 let period_clocks = self.get_period_clocks();
194 if period_clocks == 0 {
195 return 0.0;
196 };
197 return (self.get_duty_cycle_clocks() as f32 / period_clocks as f32) * 100f32;
198 }
199 /// Returns whether the timer's duty cycle is a valid observation
200 /// (Limitation of how the captures work is extra CC2 interrupts are generated when the
201 /// PWM cycle enters a new period).
202 pub fn is_valid_capture(&self) -> bool {
203 self.get_duty_cycle_clocks() != self.get_period_clocks()
204 }
205 }
206 )+
207}}
208
209#[cfg(any(feature = "stm32f411",))]
210/* red group */
211hal! {
212 TIM4,
213 TIM3,
214 TIM2,
215}
216
217/* orange group */
218#[cfg(any(
219 feature = "stm32f401",
220 feature = "stm32f405",
221 feature = "stm32f407",
222 feature = "stm32f412",
223 feature = "stm32f413",
224 feature = "stm32f415",
225 feature = "stm32f417",
226 feature = "stm32f423",
227 feature = "stm32f427",
228 feature = "stm32f429",
229 feature = "stm32f437",
230 feature = "stm32f439",
231 feature = "stm32f446",
232 feature = "stm32f469",
233 feature = "stm32f479",
234))]
235hal! {
236 TIM2,
237 TIM3,
238 TIM4,
239}
240/* green group */
241#[cfg(any(
242 feature = "stm32f405",
243 feature = "stm32f407",
244 feature = "stm32f412",
245 feature = "stm32f413",
246 feature = "stm32f415",
247 feature = "stm32f417",
248 feature = "stm32f423",
249 feature = "stm32f427",
250 feature = "stm32f429",
251 feature = "stm32f437",
252 feature = "stm32f439",
253 feature = "stm32f446",
254 feature = "stm32f469",
255 feature = "stm32f479",
256))]
257hal! {
258 TIM8,
259 TIM12,
260}
261
262/* every chip across the series have these timers with support for this feature.
263.. except for the 410 which, while the timers support this feature, has a different configuration
264 than the rest of the series.
265*/
266/* yellow group */
267#[cfg(not(feature = "stm32f410"))]
268hal! {
269 TIM1,
270 TIM5,
271 TIM9,
272}