diff options
| -rw-r--r-- | embassy-stm32/src/timer/input_capture.rs | 63 | ||||
| -rw-r--r-- | examples/stm32f4/.vscode/launch.json | 2 | ||||
| -rw-r--r-- | examples/stm32f4/.vscode/tasks.json | 2 |
3 files changed, 54 insertions, 13 deletions
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 {} | |||
| 26 | /// Channel 4 marker type. | 26 | /// Channel 4 marker type. |
| 27 | pub enum Ch4 {} | 27 | pub enum Ch4 {} |
| 28 | 28 | ||
| 29 | fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { | ||
| 30 | unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } | ||
| 31 | } | ||
| 32 | |||
| 29 | /// Capture pin wrapper. | 33 | /// Capture pin wrapper. |
| 30 | /// | 34 | /// |
| 31 | /// This wraps a pin to make it usable with capture. | 35 | /// This wraps a pin to make it usable with capture. |
| @@ -76,10 +80,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 76 | freq: Hertz, | 80 | freq: Hertz, |
| 77 | counting_mode: CountingMode, | 81 | counting_mode: CountingMode, |
| 78 | ) -> Self { | 82 | ) -> Self { |
| 79 | Self::new_inner(tim, freq, counting_mode) | ||
| 80 | } | ||
| 81 | |||
| 82 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { | ||
| 83 | let mut this = Self { inner: Timer::new(tim) }; | 83 | let mut this = Self { inner: Timer::new(tim) }; |
| 84 | 84 | ||
| 85 | this.inner.set_counting_mode(counting_mode); | 85 | this.inner.set_counting_mode(counting_mode); |
| @@ -148,11 +148,52 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> { | 150 | fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> { |
| 151 | self.inner.enable_channel(channel, true); | 151 | use stm32_metapac::timer::vals::*; |
| 152 | self.inner.set_input_capture_mode(channel, mode); | 152 | |
| 153 | self.inner.set_input_ti_selection(channel, tisel); | 153 | let regs = regs_gp16(T::regs()); |
| 154 | self.inner.clear_input_interrupt(channel); | 154 | let idx = channel.index(); |
| 155 | self.inner.enable_input_interrupt(channel, true); | 155 | |
| 156 | // Select the active input: TIMx_CCR1 must be linked to the TI1 input, so write the CC1S | ||
| 157 | // bits to 01 in the TIMx_CCMR1 register. As soon as CC1S becomes different from 00, | ||
| 158 | // the channel is configured in input and the TIMx_CCR1 register becomes read-only. | ||
| 159 | regs.ccmr_input(idx / 2) | ||
| 160 | .modify(|r| r.set_ccs(idx % 2, CcmrInputCcs::from(tisel))); | ||
| 161 | |||
| 162 | // Program the appropriate input filter duration in relation with the signal connected to the | ||
| 163 | // timer (by programming the ICxF bits in the TIMx_CCMRx register if the input is one of | ||
| 164 | // the TIx inputs). Let’s imagine that, when toggling, the input signal is not stable during at | ||
| 165 | // must 5 internal clock cycles. We must program a filter duration longer than these 5 | ||
| 166 | // clock cycles. We can validate a transition on TI1 when 8 consecutive samples with the | ||
| 167 | // new level have been detected (sampled at fDTS frequency). Then write IC1F bits to | ||
| 168 | // 0011 in the TIMx_CCMR1 register. | ||
| 169 | regs.ccmr_input(idx / 2) | ||
| 170 | .modify(|r| r.set_icf(idx % 2, FilterValue::NOFILTER)); | ||
| 171 | |||
| 172 | // Select the edge of the active transition on the TI1 channel by writing the CC1P and | ||
| 173 | // CC1NP bits to 00 in the TIMx_CCER register (rising edge in this case). | ||
| 174 | let ccpnp = match mode { | ||
| 175 | InputCaptureMode::Rising => (false, false), | ||
| 176 | InputCaptureMode::Falling => (false, true), | ||
| 177 | InputCaptureMode::BothEdges => (true, true), | ||
| 178 | }; | ||
| 179 | regs.ccer().modify(|r| { | ||
| 180 | r.set_ccp(idx, ccpnp.0); | ||
| 181 | r.set_ccnp(idx, ccpnp.1); | ||
| 182 | }); | ||
| 183 | |||
| 184 | // Program the input prescaler. In our example, we wish the capture to be performed at | ||
| 185 | // each valid transition, so the prescaler is disabled (write IC1PS bits to 00 in the | ||
| 186 | // TIMx_CCMR1 register). | ||
| 187 | regs.ccmr_input(idx / 2).modify(|r| r.set_icpsc(idx % 2, 0)); | ||
| 188 | |||
| 189 | // Enable capture from the counter into the capture register by setting the CC1E bit in the | ||
| 190 | // TIMx_CCER register. | ||
| 191 | regs.ccer().modify(|r| r.set_cce(idx, true)); | ||
| 192 | |||
| 193 | // If needed, enable the related interrupt request by setting the CC1IE bit in the | ||
| 194 | // TIMx_DIER register, and/or the DMA request by setting the CC1DE bit in the | ||
| 195 | // TIMx_DIER register. | ||
| 196 | regs.dier().modify(|r| r.set_ccie(idx, true)); | ||
| 156 | 197 | ||
| 157 | InputCaptureFuture { | 198 | InputCaptureFuture { |
| 158 | channel, | 199 | channel, |
| @@ -206,7 +247,7 @@ struct InputCaptureFuture<T: GeneralInstance4Channel> { | |||
| 206 | impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> { | 247 | impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> { |
| 207 | fn drop(&mut self) { | 248 | fn drop(&mut self) { |
| 208 | critical_section::with(|_| { | 249 | critical_section::with(|_| { |
| 209 | let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; | 250 | let regs = regs_gp16(T::regs()); |
| 210 | 251 | ||
| 211 | // disable interrupt enable | 252 | // disable interrupt enable |
| 212 | regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); | 253 | regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); |
| @@ -220,7 +261,7 @@ impl<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> { | |||
| 220 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | 261 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 221 | T::state().cc_waker[self.channel.index()].register(cx.waker()); | 262 | T::state().cc_waker[self.channel.index()].register(cx.waker()); |
| 222 | 263 | ||
| 223 | let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; | 264 | let regs = regs_gp16(T::regs()); |
| 224 | 265 | ||
| 225 | let dier = regs.dier().read(); | 266 | let dier = regs.dier().read(); |
| 226 | if !dier.ccie(self.channel.index()) { | 267 | 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 @@ | |||
| 15 | "cwd": "${workspaceRoot}", | 15 | "cwd": "${workspaceRoot}", |
| 16 | "preLaunchTask": "Cargo Build (debug)", | 16 | "preLaunchTask": "Cargo Build (debug)", |
| 17 | "runToEntryPoint": "main", | 17 | "runToEntryPoint": "main", |
| 18 | "executable": "./target/thumbv7em-none-eabihf/debug/pwm_input", | 18 | "executable": "./target/thumbv7em-none-eabihf/debug/input_capture", |
| 19 | /* Run `cargo build --example itm` and uncomment this line to run itm example */ | 19 | /* Run `cargo build --example itm` and uncomment this line to run itm example */ |
| 20 | // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", | 20 | // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", |
| 21 | "device": "STM32F446RET6", | 21 | "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 @@ | |||
| 9 | ], | 9 | ], |
| 10 | "args": [ | 10 | "args": [ |
| 11 | "--bin", | 11 | "--bin", |
| 12 | "pwm_input" | 12 | "input_capture" |
| 13 | ], | 13 | ], |
| 14 | "group": { | 14 | "group": { |
| 15 | "kind": "build", | 15 | "kind": "build", |
