diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-12-22 18:38:44 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-12-22 18:38:44 +0100 |
| commit | 8b3cbf80a4d69bc61b300b0e8da9030d2a56f40b (patch) | |
| tree | 9e6f359582b4e60c8218021e27c0605e743a9b32 | |
| parent | 1adee538ca5c11974fbb0d0ea4d486d5e0df6fac (diff) | |
| parent | b6e70c8eb32353d9c437ee614f6728c9fd9158b6 (diff) | |
Merge pull request #3678 from elagil/stm32h5_ucpd_example
STM32h5 UCPD example
| -rw-r--r-- | embassy-stm32/src/ucpd.rs | 42 | ||||
| -rw-r--r-- | examples/stm32h5/src/bin/usb_c_pd.rs | 92 |
2 files changed, 134 insertions, 0 deletions
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index ee0a2c7c1..403d54f4b 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -175,6 +175,23 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 175 | w.set_ucpden(true); | 175 | w.set_ucpden(true); |
| 176 | }); | 176 | }); |
| 177 | 177 | ||
| 178 | #[cfg(stm32h5)] | ||
| 179 | r.cfgr2().write(|w| { | ||
| 180 | w.set_rxafilten(true); | ||
| 181 | }); | ||
| 182 | |||
| 183 | // Software trim according to RM0481, p. 2650/2668 | ||
| 184 | #[cfg(stm32h5)] | ||
| 185 | { | ||
| 186 | let trim_rd_cc1 = unsafe { *(0x4002_242C as *const u32) & 0xF }; | ||
| 187 | let trim_rd_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 8) & 0xF }; | ||
| 188 | |||
| 189 | r.cfgr3().write(|w| { | ||
| 190 | w.set_trim_cc1_rd(trim_rd_cc1 as u8); | ||
| 191 | w.set_trim_cc2_rd(trim_rd_cc2 as u8); | ||
| 192 | }); | ||
| 193 | } | ||
| 194 | |||
| 178 | Self { | 195 | Self { |
| 179 | cc_phy: CcPhy { _lifetime: PhantomData }, | 196 | cc_phy: CcPhy { _lifetime: PhantomData }, |
| 180 | } | 197 | } |
| @@ -278,6 +295,31 @@ impl<'d, T: Instance> CcPhy<'d, T> { | |||
| 278 | }); | 295 | }); |
| 279 | }); | 296 | }); |
| 280 | 297 | ||
| 298 | // Software trim according to RM0481, p. 2650/2668 | ||
| 299 | #[cfg(stm32h5)] | ||
| 300 | T::REGS.cfgr3().modify(|w| match cc_pull { | ||
| 301 | CcPull::Source1_5A => { | ||
| 302 | #[cfg(stm32h5)] | ||
| 303 | { | ||
| 304 | let trim_1a5_cc1 = unsafe { *(0x08FF_F844 as *const u32) & 0xF }; | ||
| 305 | let trim_1a5_cc2 = unsafe { ((*(0x08FF_F844 as *const u32)) >> 16) & 0xF }; | ||
| 306 | |||
| 307 | w.set_trim_cc1_rp(trim_1a5_cc1 as u8); | ||
| 308 | w.set_trim_cc2_rp(trim_1a5_cc2 as u8); | ||
| 309 | }; | ||
| 310 | } | ||
| 311 | _ => { | ||
| 312 | #[cfg(stm32h5)] | ||
| 313 | { | ||
| 314 | let trim_3a0_cc1 = unsafe { (*(0x4002_242C as *const u32) >> 4) & 0xF }; | ||
| 315 | let trim_3a0_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 12) & 0xF }; | ||
| 316 | |||
| 317 | w.set_trim_cc1_rp(trim_3a0_cc1 as u8); | ||
| 318 | w.set_trim_cc2_rp(trim_3a0_cc2 as u8); | ||
| 319 | }; | ||
| 320 | } | ||
| 321 | }); | ||
| 322 | |||
| 281 | // Disable dead-battery pull-down resistors which are enabled by default on boot. | 323 | // Disable dead-battery pull-down resistors which are enabled by default on boot. |
| 282 | critical_section::with(|cs| { | 324 | critical_section::with(|cs| { |
| 283 | init( | 325 | init( |
diff --git a/examples/stm32h5/src/bin/usb_c_pd.rs b/examples/stm32h5/src/bin/usb_c_pd.rs new file mode 100644 index 000000000..00cb3b3da --- /dev/null +++ b/examples/stm32h5/src/bin/usb_c_pd.rs | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | //! This example targets the NUCLEO-H563ZI platform. | ||
| 2 | //! USB-C CC lines are protected by a TCPP01-M12 chipset. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::{error, info, Format}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::gpio::Output; | ||
| 9 | use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, Config}; | ||
| 11 | use embassy_time::{with_timeout, Duration}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | UCPD1 => ucpd::InterruptHandler<peripherals::UCPD1>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[derive(Debug, Format)] | ||
| 19 | enum CableOrientation { | ||
| 20 | Normal, | ||
| 21 | Flipped, | ||
| 22 | DebugAccessoryMode, | ||
| 23 | } | ||
| 24 | |||
| 25 | // Returns true when the cable | ||
| 26 | async fn wait_attached<T: ucpd::Instance>(cc_phy: &mut CcPhy<'_, T>) -> CableOrientation { | ||
| 27 | loop { | ||
| 28 | let (cc1, cc2) = cc_phy.vstate(); | ||
| 29 | if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST { | ||
| 30 | // Detached, wait until attached by monitoring the CC lines. | ||
| 31 | cc_phy.wait_for_vstate_change().await; | ||
| 32 | continue; | ||
| 33 | } | ||
| 34 | |||
| 35 | // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms). | ||
| 36 | if with_timeout(Duration::from_millis(100), cc_phy.wait_for_vstate_change()) | ||
| 37 | .await | ||
| 38 | .is_ok() | ||
| 39 | { | ||
| 40 | // State has changed, restart detection procedure. | ||
| 41 | continue; | ||
| 42 | }; | ||
| 43 | |||
| 44 | // State was stable for the complete debounce period, check orientation. | ||
| 45 | return match (cc1, cc2) { | ||
| 46 | (_, CcVState::LOWEST) => CableOrientation::Normal, // CC1 connected | ||
| 47 | (CcVState::LOWEST, _) => CableOrientation::Flipped, // CC2 connected | ||
| 48 | _ => CableOrientation::DebugAccessoryMode, // Both connected (special cable) | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | #[embassy_executor::main] | ||
| 54 | async fn main(_spawner: Spawner) { | ||
| 55 | let config = Config::default(); | ||
| 56 | let p = embassy_stm32::init(config); | ||
| 57 | |||
| 58 | info!("Hello World!"); | ||
| 59 | |||
| 60 | // This pin controls the dead-battery mode on the attached TCPP01-M12. | ||
| 61 | // If low, TCPP01-M12 disconnects CC lines and presents dead-battery resistance on CC lines, thus set high. | ||
| 62 | let _tcpp01_m12_ndb = Output::new(p.PA9, embassy_stm32::gpio::Level::High, embassy_stm32::gpio::Speed::Low); | ||
| 63 | |||
| 64 | let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB13, p.PB14, Default::default()); | ||
| 65 | ucpd.cc_phy().set_pull(CcPull::Sink); | ||
| 66 | |||
| 67 | info!("Waiting for USB connection..."); | ||
| 68 | let cable_orientation = wait_attached(ucpd.cc_phy()).await; | ||
| 69 | info!("USB cable connected, orientation: {}", cable_orientation); | ||
| 70 | |||
| 71 | let cc_sel = match cable_orientation { | ||
| 72 | CableOrientation::Normal => { | ||
| 73 | info!("Starting PD communication on CC1 pin"); | ||
| 74 | CcSel::CC1 | ||
| 75 | } | ||
| 76 | CableOrientation::Flipped => { | ||
| 77 | info!("Starting PD communication on CC2 pin"); | ||
| 78 | CcSel::CC2 | ||
| 79 | } | ||
| 80 | CableOrientation::DebugAccessoryMode => panic!("No PD communication in DAM"), | ||
| 81 | }; | ||
| 82 | let (_cc_phy, mut pd_phy) = ucpd.split_pd_phy(p.GPDMA1_CH0, p.GPDMA1_CH1, cc_sel); | ||
| 83 | |||
| 84 | loop { | ||
| 85 | // Enough space for the longest non-extended data message. | ||
| 86 | let mut buf = [0_u8; 30]; | ||
| 87 | match pd_phy.receive(buf.as_mut()).await { | ||
| 88 | Ok(n) => info!("USB PD RX: {=[u8]:?}", &buf[..n]), | ||
| 89 | Err(e) => error!("USB PD RX: {}", e), | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
