diff options
| author | Timo Kröger <[email protected]> | 2024-03-07 09:17:05 +0100 |
|---|---|---|
| committer | Timo Kröger <[email protected]> | 2024-03-12 08:14:42 +0100 |
| commit | a3b12226170d6b1a9ded47cc043cc09489cee278 (patch) | |
| tree | a57c8c51effc1ab2850ed186e91c49592b3a1709 | |
| parent | d99fcfd0c285be220c8f0004974567d7d4e2607b (diff) | |
[UCPD] Improve Type-C CC handling
* Improved interrupt handling: Clear flags in ISR, check state change in future
* Disable pull-up/pull-down resistors and voltage monitor on drop
* nightly rustfmt
| -rw-r--r-- | embassy-stm32/src/ucpd.rs | 62 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/usb_c_pd.rs | 12 |
2 files changed, 45 insertions, 29 deletions
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 98b82bacc..a366fadda 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -18,13 +18,14 @@ use core::future::poll_fn; | |||
| 18 | use core::marker::PhantomData; | 18 | use core::marker::PhantomData; |
| 19 | use core::task::Poll; | 19 | use core::task::Poll; |
| 20 | 20 | ||
| 21 | use crate::rcc::RccPeripheral; | 21 | use embassy_hal_internal::drop::OnDrop; |
| 22 | use crate::{interrupt, pac}; | ||
| 23 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 22 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 24 | use embassy_sync::waitqueue::AtomicWaker; | 23 | use embassy_sync::waitqueue::AtomicWaker; |
| 25 | use pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk}; | ||
| 26 | 24 | ||
| 27 | pub use pac::ucpd::vals::TypecVstateCc as CcVState; | 25 | use crate::interrupt; |
| 26 | pub use crate::pac::ucpd::vals::TypecVstateCc as CcVState; | ||
| 27 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk}; | ||
| 28 | use crate::rcc::RccPeripheral; | ||
| 28 | 29 | ||
| 29 | /// Pull-up or Pull-down resistor state of both CC lines. | 30 | /// Pull-up or Pull-down resistor state of both CC lines. |
| 30 | #[derive(Debug, Clone, Copy, PartialEq)] | 31 | #[derive(Debug, Clone, Copy, PartialEq)] |
| @@ -53,6 +54,16 @@ pub struct Ucpd<'d, T: Instance> { | |||
| 53 | _peri: PeripheralRef<'d, T>, | 54 | _peri: PeripheralRef<'d, T>, |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 57 | impl<'d, T: Instance> Drop for Ucpd<'d, T> { | ||
| 58 | fn drop(&mut self) { | ||
| 59 | T::REGS.cr().modify(|w| { | ||
| 60 | w.set_ccenable(Ccenable::DISABLED); | ||
| 61 | w.set_cc1tcdis(true); | ||
| 62 | w.set_cc2tcdis(true); | ||
| 63 | }); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 56 | impl<'d, T: Instance> Ucpd<'d, T> { | 67 | impl<'d, T: Instance> Ucpd<'d, T> { |
| 57 | /// Creates a new UCPD driver instance. | 68 | /// Creates a new UCPD driver instance. |
| 58 | pub fn new( | 69 | pub fn new( |
| @@ -113,13 +124,17 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 113 | } else { | 124 | } else { |
| 114 | Ccenable::DISABLED | 125 | Ccenable::DISABLED |
| 115 | }); | 126 | }); |
| 127 | |||
| 128 | // Make sure detector is enabled on both pins. | ||
| 129 | w.set_cc1tcdis(false); | ||
| 130 | w.set_cc2tcdis(false); | ||
| 116 | }); | 131 | }); |
| 117 | 132 | ||
| 118 | // Disable dead-battery pull-down resistors which are enabled by default on boot. | 133 | // Disable dead-battery pull-down resistors which are enabled by default on boot. |
| 119 | critical_section::with(|_| { | 134 | critical_section::with(|_| { |
| 120 | // TODO: other families | 135 | // TODO: other families |
| 121 | #[cfg(stm32g4)] | 136 | #[cfg(stm32g4)] |
| 122 | pac::PWR | 137 | crate::pac::PWR |
| 123 | .cr3() | 138 | .cr3() |
| 124 | .modify(|w| w.set_ucpd1_dbdis(cc_pull != CcPull::SinkDeadBattery)); | 139 | .modify(|w| w.set_ucpd1_dbdis(cc_pull != CcPull::SinkDeadBattery)); |
| 125 | }); | 140 | }); |
| @@ -138,26 +153,29 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 138 | } | 153 | } |
| 139 | 154 | ||
| 140 | /// Waits for a change in voltage state on either CC line. | 155 | /// Waits for a change in voltage state on either CC line. |
| 141 | pub async fn wait_for_cc_change(&mut self) { | 156 | pub async fn wait_for_cc_vstate_change(&self) -> (CcVState, CcVState) { |
| 142 | let r = T::REGS; | 157 | let _on_drop = OnDrop::new(|| critical_section::with(|_| self.enable_cc_interrupts(false))); |
| 158 | let prev_vstate = self.cc_vstate(); | ||
| 143 | poll_fn(|cx| { | 159 | poll_fn(|cx| { |
| 144 | let sr = r.sr().read(); | 160 | let vstate = self.cc_vstate(); |
| 145 | if sr.typecevt1() || sr.typecevt2() { | 161 | if vstate != prev_vstate { |
| 146 | r.icr().write(|w| { | 162 | Poll::Ready(vstate) |
| 147 | w.set_typecevt1cf(true); | ||
| 148 | w.set_typecevt2cf(true); | ||
| 149 | }); | ||
| 150 | Poll::Ready(()) | ||
| 151 | } else { | 163 | } else { |
| 152 | T::waker().register(cx.waker()); | 164 | T::waker().register(cx.waker()); |
| 153 | r.imr().modify(|w| { | 165 | self.enable_cc_interrupts(true); |
| 154 | w.set_typecevt1ie(true); | ||
| 155 | w.set_typecevt2ie(true); | ||
| 156 | }); | ||
| 157 | Poll::Pending | 166 | Poll::Pending |
| 158 | } | 167 | } |
| 159 | }) | 168 | }) |
| 160 | .await; | 169 | .await |
| 170 | } | ||
| 171 | |||
| 172 | fn enable_cc_interrupts(&self, enable: bool) { | ||
| 173 | critical_section::with(|_| { | ||
| 174 | T::REGS.imr().modify(|w| { | ||
| 175 | w.set_typecevt1ie(enable); | ||
| 176 | w.set_typecevt2ie(enable); | ||
| 177 | }) | ||
| 178 | }); | ||
| 161 | } | 179 | } |
| 162 | } | 180 | } |
| 163 | 181 | ||
| @@ -172,9 +190,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 172 | let sr = r.sr().read(); | 190 | let sr = r.sr().read(); |
| 173 | 191 | ||
| 174 | if sr.typecevt1() || sr.typecevt2() { | 192 | if sr.typecevt1() || sr.typecevt2() { |
| 175 | r.imr().modify(|w| { | 193 | r.icr().write(|w| { |
| 176 | w.set_typecevt1ie(true); | 194 | w.set_typecevt1cf(true); |
| 177 | w.set_typecevt2ie(true); | 195 | w.set_typecevt2cf(true); |
| 178 | }); | 196 | }); |
| 179 | } | 197 | } |
| 180 | 198 | ||
diff --git a/examples/stm32g4/src/bin/usb_c_pd.rs b/examples/stm32g4/src/bin/usb_c_pd.rs index c442ab0a7..7a0065087 100644 --- a/examples/stm32g4/src/bin/usb_c_pd.rs +++ b/examples/stm32g4/src/bin/usb_c_pd.rs | |||
| @@ -3,10 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::{info, Format}; | 4 | use defmt::{info, Format}; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::{ | 6 | use embassy_stm32::ucpd::{self, CcPull, CcVState, Ucpd}; |
| 7 | ucpd::{self, CcPull, CcVState, Ucpd}, | 7 | use embassy_stm32::Config; |
| 8 | Config, | ||
| 9 | }; | ||
| 10 | use embassy_time::{with_timeout, Duration}; | 8 | use embassy_time::{with_timeout, Duration}; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 10 | ||
| @@ -18,17 +16,17 @@ enum CableOrientation { | |||
| 18 | } | 16 | } |
| 19 | 17 | ||
| 20 | // Returns true when the cable | 18 | // Returns true when the cable |
| 21 | async fn wait_attached<'d, T: ucpd::Instance>(ucpd: &mut Ucpd<'d, T>) -> CableOrientation { | 19 | async fn wait_attached<T: ucpd::Instance>(ucpd: &mut Ucpd<'_, T>) -> CableOrientation { |
| 22 | loop { | 20 | loop { |
| 23 | let (cc1, cc2) = ucpd.cc_vstate(); | 21 | let (cc1, cc2) = ucpd.cc_vstate(); |
| 24 | if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST { | 22 | if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST { |
| 25 | // Detached, wait until attached by monitoring the CC lines. | 23 | // Detached, wait until attached by monitoring the CC lines. |
| 26 | ucpd.wait_for_cc_change().await; | 24 | ucpd.wait_for_cc_vstate_change().await; |
| 27 | continue; | 25 | continue; |
| 28 | } | 26 | } |
| 29 | 27 | ||
| 30 | // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms). | 28 | // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms). |
| 31 | if with_timeout(Duration::from_millis(100), ucpd.wait_for_cc_change()) | 29 | if with_timeout(Duration::from_millis(100), ucpd.wait_for_cc_vstate_change()) |
| 32 | .await | 30 | .await |
| 33 | .is_ok() | 31 | .is_ok() |
| 34 | { | 32 | { |
