diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index b2f7eb85..6c00c93d 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -6,8 +6,8 @@ use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ - self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, - EndpointType, Event, Unsupported, + self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, + EndpointOut, EndpointType, Event, Unsupported, }; use futures::future::poll_fn; @@ -31,7 +31,7 @@ impl interrupt::typelevel::Handler for InterruptHandl let state = T::state(); let ints = r.gintsts().read(); - if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() { + if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() { // Mask interrupts and notify `Bus` to process them r.gintmsk().write(|_| {}); T::state().bus_waker.wake(); @@ -256,7 +256,34 @@ struct EndpointData { fifo_size_words: u16, } +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Config { + /// Enable VBUS detection. + /// + /// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly. + /// This is done by checkihg whether there is 5V on the VBUS pin or not. + /// + /// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional. + /// (if there's no power in VBUS your device would be off anyway, so it's fine to always assume + /// there's power in VBUS, i.e. the USB cable is always plugged in.) + /// + /// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and + /// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true. + /// + /// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a + /// voltage divider. See ST application note AN4879 and the reference manual for more details. + pub vbus_detection: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { vbus_detection: true } + } +} + pub struct Driver<'d, T: Instance> { + config: Config, phantom: PhantomData<&'d mut T>, ep_in: [Option; MAX_EP_COUNT], ep_out: [Option; MAX_EP_COUNT], @@ -279,6 +306,7 @@ impl<'d, T: Instance> Driver<'d, T> { dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd, ep_out_buffer: &'d mut [u8], + config: Config, ) -> Self { into_ref!(dp, dm); @@ -286,6 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> { dm.set_as_af(dm.af_num(), AFType::OutputPushPull); Self { + config, phantom: PhantomData, ep_in: [None; MAX_EP_COUNT], ep_out: [None; MAX_EP_COUNT], @@ -318,6 +347,7 @@ impl<'d, T: Instance> Driver<'d, T> { ulpi_d6: impl Peripheral

> + 'd, ulpi_d7: impl Peripheral

> + 'd, ep_out_buffer: &'d mut [u8], + config: Config, ) -> Self { assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); @@ -327,6 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> { ); Self { + config, phantom: PhantomData, ep_in: [None; MAX_EP_COUNT], ep_out: [None; MAX_EP_COUNT], @@ -464,11 +495,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { ( Bus { + config: self.config, phantom: PhantomData, ep_in: self.ep_in, ep_out: self.ep_out, phy_type: self.phy_type, - enabled: false, + inited: false, }, ControlPipe { _phantom: PhantomData, @@ -481,11 +513,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { } pub struct Bus<'d, T: Instance> { + config: Config, phantom: PhantomData<&'d mut T>, ep_in: [Option; MAX_EP_COUNT], ep_out: [Option; MAX_EP_COUNT], phy_type: PhyType, - enabled: bool, + inited: bool, } impl<'d, T: Instance> Bus<'d, T> { @@ -498,11 +531,202 @@ impl<'d, T: Instance> Bus<'d, T> { w.set_iepint(true); w.set_oepint(true); w.set_rxflvlm(true); + w.set_srqim(true); + w.set_otgint(true); }); } } impl<'d, T: Instance> Bus<'d, T> { + fn init(&mut self) { + #[cfg(stm32l4)] + { + crate::peripherals::PWR::enable(); + critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); + } + + #[cfg(stm32f7)] + { + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpien(ulpien); + } else { + w.set_usb_otg_hsen(ulpien); + } + }); + + // Low power mode + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpilpen(ulpien); + } else { + w.set_usb_otg_hslpen(ulpien); + } + }); + }); + } + + #[cfg(stm32h7)] + { + // If true, VDD33USB is generated by internal regulator from VDD50USB + // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) + // TODO: unhardcode + let internal_regulator = false; + + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.cr3().modify(|w| { + w.set_usb33den(true); + w.set_usbregen(internal_regulator); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.cr3().read().usb33rdy() {} + + // Use internal 48MHz HSI clock. Should be enabled in RCC by default. + critical_section::with(|_| { + crate::pac::RCC + .d2ccip2r() + .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) + }); + + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpien(ulpien); + } else { + w.set_usb_otg_fs_ulpien(ulpien); + } + }); + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpilpen(ulpien); + } else { + w.set_usb_otg_fs_ulpilpen(ulpien); + } + }); + }); + } + + #[cfg(stm32u5)] + { + // Enable USB power + critical_section::with(|_| { + crate::pac::RCC.ahb3enr().modify(|w| { + w.set_pwren(true); + }); + cortex_m::asm::delay(2); + + crate::pac::PWR.svmcr().modify(|w| { + w.set_usv(true); + w.set_uvmen(true); + }); + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.svmsr().read().vddusbrdy() {} + + // Select HSI48 as USB clock source. + critical_section::with(|_| { + crate::pac::RCC.ccipr1().modify(|w| { + w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); + }) + }); + } + + ::enable(); + ::reset(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + let r = T::regs(); + let core_id = r.cid().read().0; + info!("Core id {:08x}", core_id); + + // Wait for AHB ready. + while !r.grstctl().read().ahbidl() {} + + // Configure as device. + r.gusbcfg().write(|w| { + // Force device mode + w.set_fdmod(true); + // Enable internal full-speed PHY + w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); + }); + + // Configuring Vbus sense and SOF output + match core_id { + 0x0000_1200 | 0x0000_1100 => { + assert!(self.phy_type != PhyType::InternalHighSpeed); + + r.gccfg_v1().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal()); + }); + + // F429-like chips have the GCCFG.NOVBUSSENS bit + r.gccfg_v1().modify(|w| { + w.set_novbussens(!self.config.vbus_detection); + w.set_vbusasen(false); + w.set_vbusbsen(self.config.vbus_detection); + w.set_sofouten(false); + }); + } + 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { + // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning + r.gccfg_v2().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); + w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); + }); + + r.gccfg_v2().modify(|w| { + w.set_vbden(self.config.vbus_detection); + }); + + // Force B-peripheral session + r.gotgctl().modify(|w| { + w.set_bvaloen(!self.config.vbus_detection); + w.set_bvaloval(true); + }); + } + _ => unimplemented!("Unknown USB core id {:X}", core_id), + } + + // Soft disconnect. + r.dctl().write(|w| w.set_sdis(true)); + + // Set speed. + r.dcfg().write(|w| { + w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); + w.set_dspd(self.phy_type.to_dspd()); + }); + + // Unmask transfer complete EP interrupt + r.diepmsk().write(|w| { + w.set_xfrcm(true); + }); + + // Unmask and clear core interrupts + Bus::::restore_irqs(); + r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); + + // Unmask global interrupt + r.gahbcfg().write(|w| { + w.set_gint(true); // unmask global interrupt + }); + + // Connect + r.dctl().write(|w| w.set_sdis(false)); + } + fn init_fifo(&mut self) { trace!("init_fifo"); @@ -613,6 +837,13 @@ impl<'d, T: Instance> Bus<'d, T> { }); } + fn disable_all_endpoints(&mut self) { + for i in 0..T::ENDPOINT_COUNT { + self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false); + self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false); + } + } + fn disable(&mut self) { T::Interrupt::disable(); @@ -627,9 +858,14 @@ impl<'d, T: Instance> Bus<'d, T> { impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { async fn poll(&mut self) -> Event { poll_fn(move |cx| { - // TODO: implement VBUS detection - if !self.enabled { - return Poll::Ready(Event::PowerDetected); + if !self.inited { + self.init(); + self.inited = true; + + // If no vbus detection, just return a single PowerDetected event at startup. + if !self.config.vbus_detection { + return Poll::Ready(Event::PowerDetected); + } } let r = T::regs(); @@ -637,6 +873,32 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { T::state().bus_waker.register(cx.waker()); let ints = r.gintsts().read(); + + if ints.srqint() { + trace!("vbus detected"); + + r.gintsts().write(|w| w.set_srqint(true)); // clear + Self::restore_irqs(); + + if self.config.vbus_detection { + return Poll::Ready(Event::PowerDetected); + } + } + + if ints.otgint() { + let otgints = r.gotgint().read(); + r.gotgint().write_value(otgints); // clear all + Self::restore_irqs(); + + if otgints.sedet() { + trace!("vbus removed"); + if self.config.vbus_detection { + self.disable_all_endpoints(); + return Poll::Ready(Event::PowerRemoved); + } + } + } + if ints.usbrst() { trace!("reset"); @@ -801,203 +1063,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { async fn enable(&mut self) { trace!("enable"); - - #[cfg(stm32l4)] - { - crate::peripherals::PWR::enable(); - critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); - } - - #[cfg(stm32f7)] - { - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpien(ulpien); - } else { - w.set_usb_otg_hsen(ulpien); - } - }); - - // Low power mode - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpilpen(ulpien); - } else { - w.set_usb_otg_hslpen(ulpien); - } - }); - }); - } - - #[cfg(stm32h7)] - { - // If true, VDD33USB is generated by internal regulator from VDD50USB - // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) - // TODO: unhardcode - let internal_regulator = false; - - // Enable USB power - critical_section::with(|_| { - crate::pac::PWR.cr3().modify(|w| { - w.set_usb33den(true); - w.set_usbregen(internal_regulator); - }) - }); - - // Wait for USB power to stabilize - while !crate::pac::PWR.cr3().read().usb33rdy() {} - - // Use internal 48MHz HSI clock. Should be enabled in RCC by default. - critical_section::with(|_| { - crate::pac::RCC - .d2ccip2r() - .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) - }); - - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpien(ulpien); - } else { - w.set_usb_otg_fs_ulpien(ulpien); - } - }); - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpilpen(ulpien); - } else { - w.set_usb_otg_fs_ulpilpen(ulpien); - } - }); - }); - } - - #[cfg(stm32u5)] - { - // Enable USB power - critical_section::with(|_| { - crate::pac::RCC.ahb3enr().modify(|w| { - w.set_pwren(true); - }); - cortex_m::asm::delay(2); - - crate::pac::PWR.svmcr().modify(|w| { - w.set_usv(true); - w.set_uvmen(true); - }); - }); - - // Wait for USB power to stabilize - while !crate::pac::PWR.svmsr().read().vddusbrdy() {} - - // Select HSI48 as USB clock source. - critical_section::with(|_| { - crate::pac::RCC.ccipr1().modify(|w| { - w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); - }) - }); - } - - ::enable(); - ::reset(); - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - let r = T::regs(); - let core_id = r.cid().read().0; - info!("Core id {:08x}", core_id); - - // Wait for AHB ready. - while !r.grstctl().read().ahbidl() {} - - // Configure as device. - r.gusbcfg().write(|w| { - // Force device mode - w.set_fdmod(true); - // Enable internal full-speed PHY - w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); - }); - - // Configuring Vbus sense and SOF output - match core_id { - 0x0000_1200 | 0x0000_1100 => { - assert!(self.phy_type != PhyType::InternalHighSpeed); - - r.gccfg_v1().modify(|w| { - // Enable internal full-speed PHY, logic is inverted - w.set_pwrdwn(self.phy_type.internal()); - }); - - // F429-like chips have the GCCFG.NOVBUSSENS bit - r.gccfg_v1().modify(|w| { - w.set_novbussens(true); - w.set_vbusasen(false); - w.set_vbusbsen(false); - w.set_sofouten(false); - }); - } - 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { - // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning - r.gccfg_v2().modify(|w| { - // Enable internal full-speed PHY, logic is inverted - w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); - w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); - }); - - r.gccfg_v2().modify(|w| { - w.set_vbden(false); - }); - - // Force B-peripheral session - r.gotgctl().modify(|w| { - w.set_bvaloen(true); - w.set_bvaloval(true); - }); - } - _ => unimplemented!("Unknown USB core id {:X}", core_id), - } - - // Soft disconnect. - r.dctl().write(|w| w.set_sdis(true)); - - // Set speed. - r.dcfg().write(|w| { - w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); - w.set_dspd(self.phy_type.to_dspd()); - }); - - // Unmask transfer complete EP interrupt - r.diepmsk().write(|w| { - w.set_xfrcm(true); - }); - - // Unmask and clear core interrupts - Bus::::restore_irqs(); - r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); - - // Unmask global interrupt - r.gahbcfg().write(|w| { - w.set_gint(true); // unmask global interrupt - }); - - // Connect - r.dctl().write(|w| w.set_sdis(false)); - - self.enabled = true; + // TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb } async fn disable(&mut self) { trace!("disable"); - Bus::disable(self); - - self.enabled = false; + // TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb + //Bus::disable(self); } async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { @@ -1140,11 +1213,16 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { state.ep_in_wakers[index].register(cx.waker()); let diepctl = r.diepctl(index).read(); + let dtxfsts = r.dtxfsts(index).read(); + info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0); if !diepctl.usbaep() { + trace!("write ep={:?} wait for prev: error disabled", self.info.addr); Poll::Ready(Err(EndpointError::Disabled)) } else if !diepctl.epena() { + trace!("write ep={:?} wait for prev: ready", self.info.addr); Poll::Ready(Ok(())) } else { + trace!("write ep={:?} wait for prev: pending", self.info.addr); Poll::Pending } }) @@ -1169,6 +1247,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { Poll::Pending } else { + trace!("write ep={:?} wait for fifo: ready", self.info.addr); Poll::Ready(()) } }) diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 953d99a4..b1f01417 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -52,7 +52,9 @@ async fn main(spawner: Spawner) { // Create the driver, from the HAL. let ep_out_buffer = &mut make_static!([0; 256])[..]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index f8f5940a..4ff6452e 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 763309ce..a2c76178 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index c622f19f..97291f60 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 80811a43..410d6891 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index f36daf91..9e47fb18 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -31,7 +31,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);