diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index a1c1486f9..ab6f10a1d 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -26,6 +26,10 @@ pub enum Ch3 {} /// Channel 4 marker type. pub enum Ch4 {} +fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } +} + /// Capture pin wrapper. /// /// This wraps a pin to make it usable with capture. @@ -76,10 +80,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { freq: Hertz, counting_mode: CountingMode, ) -> Self { - Self::new_inner(tim, freq, counting_mode) - } - - fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.set_counting_mode(counting_mode); @@ -148,11 +148,52 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> { - self.inner.enable_channel(channel, true); - self.inner.set_input_capture_mode(channel, mode); - self.inner.set_input_ti_selection(channel, tisel); - self.inner.clear_input_interrupt(channel); - self.inner.enable_input_interrupt(channel, true); + use stm32_metapac::timer::vals::*; + + let regs = regs_gp16(T::regs()); + let idx = channel.index(); + + // Select the active input: TIMx_CCR1 must be linked to the TI1 input, so write the CC1S + // bits to 01 in the TIMx_CCMR1 register. As soon as CC1S becomes different from 00, + // the channel is configured in input and the TIMx_CCR1 register becomes read-only. + regs.ccmr_input(idx / 2) + .modify(|r| r.set_ccs(idx % 2, CcmrInputCcs::from(tisel))); + + // Program the appropriate input filter duration in relation with the signal connected to the + // timer (by programming the ICxF bits in the TIMx_CCMRx register if the input is one of + // the TIx inputs). Let’s imagine that, when toggling, the input signal is not stable during at + // must 5 internal clock cycles. We must program a filter duration longer than these 5 + // clock cycles. We can validate a transition on TI1 when 8 consecutive samples with the + // new level have been detected (sampled at fDTS frequency). Then write IC1F bits to + // 0011 in the TIMx_CCMR1 register. + regs.ccmr_input(idx / 2) + .modify(|r| r.set_icf(idx % 2, FilterValue::NOFILTER)); + + // Select the edge of the active transition on the TI1 channel by writing the CC1P and + // CC1NP bits to 00 in the TIMx_CCER register (rising edge in this case). + let ccpnp = match mode { + InputCaptureMode::Rising => (false, false), + InputCaptureMode::Falling => (false, true), + InputCaptureMode::BothEdges => (true, true), + }; + regs.ccer().modify(|r| { + r.set_ccp(idx, ccpnp.0); + r.set_ccnp(idx, ccpnp.1); + }); + + // Program the input prescaler. In our example, we wish the capture to be performed at + // each valid transition, so the prescaler is disabled (write IC1PS bits to 00 in the + // TIMx_CCMR1 register). + regs.ccmr_input(idx / 2).modify(|r| r.set_icpsc(idx % 2, 0)); + + // Enable capture from the counter into the capture register by setting the CC1E bit in the + // TIMx_CCER register. + regs.ccer().modify(|r| r.set_cce(idx, true)); + + // If needed, enable the related interrupt request by setting the CC1IE bit in the + // TIMx_DIER register, and/or the DMA request by setting the CC1DE bit in the + // TIMx_DIER register. + regs.dier().modify(|r| r.set_ccie(idx, true)); InputCaptureFuture { channel, @@ -206,7 +247,7 @@ struct InputCaptureFuture<T: GeneralInstance4Channel> { impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> { fn drop(&mut self) { critical_section::with(|_| { - let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + let regs = regs_gp16(T::regs()); // disable interrupt enable regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); @@ -220,7 +261,7 @@ impl<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { T::state().cc_waker[self.channel.index()].register(cx.waker()); - let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + let regs = regs_gp16(T::regs()); let dier = regs.dier().read(); if !dier.ccie(self.channel.index()) { diff --git a/examples/stm32f4/.vscode/launch.json b/examples/stm32f4/.vscode/launch.json index a9849e0da..20cd4d2e8 100644 --- a/examples/stm32f4/.vscode/launch.json +++ b/examples/stm32f4/.vscode/launch.json @@ -15,7 +15,7 @@ "cwd": "${workspaceRoot}", "preLaunchTask": "Cargo Build (debug)", "runToEntryPoint": "main", - "executable": "./target/thumbv7em-none-eabihf/debug/pwm_input", + "executable": "./target/thumbv7em-none-eabihf/debug/input_capture", /* Run `cargo build --example itm` and uncomment this line to run itm example */ // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", "device": "STM32F446RET6", diff --git a/examples/stm32f4/.vscode/tasks.json b/examples/stm32f4/.vscode/tasks.json index de7013b12..e153722da 100644 --- a/examples/stm32f4/.vscode/tasks.json +++ b/examples/stm32f4/.vscode/tasks.json @@ -9,7 +9,7 @@ ], "args": [ "--bin", - "pwm_input" + "input_capture" ], "group": { "kind": "build",