diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 2d119c973..9c37d2c04 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs @@ -42,8 +42,8 @@ pub(crate) fn init( // strobe will apply the CC pin configuration from the control register // (which is why we need to be careful about when we call this) crate::pac::SYSCFG.cfgr1().modify(|w| { - w.set_ucpd1_strobe(ucpd1_db_enable); - w.set_ucpd2_strobe(ucpd2_db_enable); + w.set_ucpd1_strobe(!ucpd1_db_enable); + w.set_ucpd2_strobe(!ucpd2_db_enable); }); } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bfe003a11..e42470004 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -13,7 +13,7 @@ stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] -stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"] +stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] @@ -47,6 +47,7 @@ mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] not-gpdma = [] dac = [] +ucpd = [] cm0 = ["portable-atomic/unsafe-assume-single-core"] @@ -160,6 +161,11 @@ name = "timer" path = "src/bin/timer.rs" required-features = [] +[[bin]] +name = "ucpd" +path = "src/bin/ucpd.rs" +required-features = [ "ucpd",] + [[bin]] name = "usart" path = "src/bin/usart.rs" diff --git a/tests/stm32/src/bin/ucpd.rs b/tests/stm32/src/bin/ucpd.rs new file mode 100644 index 000000000..c09334ec8 --- /dev/null +++ b/tests/stm32/src/bin/ucpd.rs @@ -0,0 +1,120 @@ +// required-features: ucpd +#![no_std] +#![no_main] +#[path = "../common.rs"] +mod common; + +use common::*; +use defmt::{assert, assert_eq}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, RxError, Ucpd}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; + +bind_interrupts!(struct Irqs { + UCPD1_2 => ucpd::InterruptHandler, ucpd::InterruptHandler; +}); + +static SRC_TO_SNK: [u8; 6] = [0, 1, 2, 3, 4, 5]; +static SNK_TO_SRC: [u8; 4] = [9, 8, 7, 6]; + +async fn wait_for_vstate(cc_phy: &mut CcPhy<'_, T>, vstate: CcVState) { + let (mut cc1, mut _cc2) = cc_phy.vstate(); + while cc1 != vstate { + (cc1, _cc2) = cc_phy.wait_for_vstate_change().await; + } +} + +async fn source( + mut ucpd: Ucpd<'static, peripherals::UCPD1>, + rx_dma: peripherals::DMA1_CH1, + tx_dma: peripherals::DMA1_CH2, +) { + debug!("source: setting default current pull-up"); + ucpd.cc_phy().set_pull(CcPull::SourceDefaultUsb); + + // Wait for default sink. + debug!("source: wait for sink"); + wait_for_vstate(ucpd.cc_phy(), CcVState::LOW).await; + + // Advertise a higher current by changing the pull-up resistor. + debug!("source: sink detected, setting 3.0A current pull-up"); + ucpd.cc_phy().set_pull(CcPull::Source3_0A); + + let (_, mut pd_phy) = ucpd.split_pd_phy(rx_dma, tx_dma, CcSel::CC1); + + // Listen for an incoming message + debug!("source: wait for message from sink"); + let mut snk_to_src_buf = [0_u8; 30]; + let n = unwrap!(pd_phy.receive(snk_to_src_buf.as_mut()).await); + assert_eq!(n, SNK_TO_SRC.len()); + assert_eq!(&snk_to_src_buf[..n], SNK_TO_SRC.as_slice()); + + // Send message + debug!("source: message received, sending message"); + unwrap!(pd_phy.transmit(SRC_TO_SNK.as_slice()).await); + + // Wait for hard-reset + debug!("source: message sent, waiting for hard-reset"); + assert!(matches!( + pd_phy.receive(snk_to_src_buf.as_mut()).await, + Err(RxError::HardReset) + )); +} + +async fn sink( + mut ucpd: Ucpd<'static, peripherals::UCPD2>, + rx_dma: peripherals::DMA1_CH3, + tx_dma: peripherals::DMA1_CH4, +) { + debug!("sink: setting pull down"); + ucpd.cc_phy().set_pull(CcPull::Sink); + + // Wait for default source. + debug!("sink: waiting for default vstate"); + wait_for_vstate(ucpd.cc_phy(), CcVState::LOW).await; + + // Wait higher current pull-up. + //debug!("sink: source default vstate detected, waiting for 3.0A vstate"); + //wait_for_vstate(ucpd.cc_phy(), CcVState::HIGHEST).await; + //debug!("sink: source 3.0A vstate detected"); + // TODO: not working yet, why? no idea, replace with timer for now + Timer::after_millis(100).await; + + let (_, mut pd_phy) = ucpd.split_pd_phy(rx_dma, tx_dma, CcSel::CC1); + + // Send message + debug!("sink: sending message"); + unwrap!(pd_phy.transmit(SNK_TO_SRC.as_slice()).await); + + // Listen for an incoming message + debug!("sink: message sent, waiting for message from source"); + let mut src_to_snk_buf = [0_u8; 30]; + let n = unwrap!(pd_phy.receive(src_to_snk_buf.as_mut()).await); + assert_eq!(n, SRC_TO_SNK.len()); + assert_eq!(&src_to_snk_buf[..n], SRC_TO_SNK.as_slice()); + + // Send hard reset + debug!("sink: message received, sending hard-reset"); + unwrap!(pd_phy.transmit_hardreset().await); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + // Wire between PD0 and PA8 + let ucpd1 = Ucpd::new(p.UCPD1, Irqs {}, p.PA8, p.PB15); + let ucpd2 = Ucpd::new(p.UCPD2, Irqs {}, p.PD0, p.PD2); + + join( + source(ucpd1, p.DMA1_CH1, p.DMA1_CH2), + sink(ucpd2, p.DMA1_CH3, p.DMA1_CH4), + ) + .await; + + info!("Test OK"); + cortex_m::asm::bkpt(); +}