aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/ucpd.rs42
-rw-r--r--examples/stm32h5/src/bin/usb_c_pd.rs92
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
6use defmt::{error, info, Format};
7use embassy_executor::Spawner;
8use embassy_stm32::gpio::Output;
9use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd};
10use embassy_stm32::{bind_interrupts, peripherals, Config};
11use embassy_time::{with_timeout, Duration};
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 UCPD1 => ucpd::InterruptHandler<peripherals::UCPD1>;
16});
17
18#[derive(Debug, Format)]
19enum CableOrientation {
20 Normal,
21 Flipped,
22 DebugAccessoryMode,
23}
24
25// Returns true when the cable
26async 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]
54async 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}