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