parent
a2d1e7f02c
commit
219ef5b37a
@ -6,8 +6,8 @@ use atomic_polyfill::{AtomicBool, AtomicU16, Ordering};
|
|||||||
use embassy_hal_common::{into_ref, Peripheral};
|
use embassy_hal_common::{into_ref, Peripheral};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use embassy_usb_driver::{
|
use embassy_usb_driver::{
|
||||||
self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut,
|
self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo,
|
||||||
EndpointType, Event, Unsupported,
|
EndpointOut, EndpointType, Event, Unsupported,
|
||||||
};
|
};
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
let state = T::state();
|
let state = T::state();
|
||||||
|
|
||||||
let ints = r.gintsts().read();
|
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
|
// Mask interrupts and notify `Bus` to process them
|
||||||
r.gintmsk().write(|_| {});
|
r.gintmsk().write(|_| {});
|
||||||
T::state().bus_waker.wake();
|
T::state().bus_waker.wake();
|
||||||
@ -256,7 +256,34 @@ struct EndpointData {
|
|||||||
fifo_size_words: u16,
|
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> {
|
pub struct Driver<'d, T: Instance> {
|
||||||
|
config: Config,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
@ -279,6 +306,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
||||||
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
||||||
ep_out_buffer: &'d mut [u8],
|
ep_out_buffer: &'d mut [u8],
|
||||||
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(dp, dm);
|
into_ref!(dp, dm);
|
||||||
|
|
||||||
@ -286,6 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
|
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
config,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
ep_in: [None; MAX_EP_COUNT],
|
ep_in: [None; MAX_EP_COUNT],
|
||||||
ep_out: [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<P = impl UlpiD6Pin<T>> + 'd,
|
ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
|
||||||
ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
|
ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
|
||||||
ep_out_buffer: &'d mut [u8],
|
ep_out_buffer: &'d mut [u8],
|
||||||
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB");
|
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 {
|
Self {
|
||||||
|
config,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
ep_in: [None; MAX_EP_COUNT],
|
ep_in: [None; MAX_EP_COUNT],
|
||||||
ep_out: [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 {
|
Bus {
|
||||||
|
config: self.config,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
ep_in: self.ep_in,
|
ep_in: self.ep_in,
|
||||||
ep_out: self.ep_out,
|
ep_out: self.ep_out,
|
||||||
phy_type: self.phy_type,
|
phy_type: self.phy_type,
|
||||||
enabled: false,
|
inited: false,
|
||||||
},
|
},
|
||||||
ControlPipe {
|
ControlPipe {
|
||||||
_phantom: PhantomData,
|
_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> {
|
pub struct Bus<'d, T: Instance> {
|
||||||
|
config: Config,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_in: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
ep_out: [Option<EndpointData>; MAX_EP_COUNT],
|
||||||
phy_type: PhyType,
|
phy_type: PhyType,
|
||||||
enabled: bool,
|
inited: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Bus<'d, T> {
|
impl<'d, T: Instance> Bus<'d, T> {
|
||||||
@ -498,11 +531,202 @@ impl<'d, T: Instance> Bus<'d, T> {
|
|||||||
w.set_iepint(true);
|
w.set_iepint(true);
|
||||||
w.set_oepint(true);
|
w.set_oepint(true);
|
||||||
w.set_rxflvlm(true);
|
w.set_rxflvlm(true);
|
||||||
|
w.set_srqim(true);
|
||||||
|
w.set_otgint(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Bus<'d, T> {
|
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);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<T as RccPeripheral>::enable();
|
||||||
|
<T as RccPeripheral>::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::<T>::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) {
|
fn init_fifo(&mut self) {
|
||||||
trace!("init_fifo");
|
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) {
|
fn disable(&mut self) {
|
||||||
T::Interrupt::disable();
|
T::Interrupt::disable();
|
||||||
|
|
||||||
@ -627,16 +858,47 @@ impl<'d, T: Instance> Bus<'d, T> {
|
|||||||
impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
||||||
async fn poll(&mut self) -> Event {
|
async fn poll(&mut self) -> Event {
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| {
|
||||||
// TODO: implement VBUS detection
|
if !self.inited {
|
||||||
if !self.enabled {
|
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);
|
return Poll::Ready(Event::PowerDetected);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
T::state().bus_waker.register(cx.waker());
|
T::state().bus_waker.register(cx.waker());
|
||||||
|
|
||||||
let ints = r.gintsts().read();
|
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() {
|
if ints.usbrst() {
|
||||||
trace!("reset");
|
trace!("reset");
|
||||||
|
|
||||||
@ -801,203 +1063,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
|
|||||||
|
|
||||||
async fn enable(&mut self) {
|
async fn enable(&mut self) {
|
||||||
trace!("enable");
|
trace!("enable");
|
||||||
|
// TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb
|
||||||
#[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);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
<T as RccPeripheral>::enable();
|
|
||||||
<T as RccPeripheral>::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::<T>::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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn disable(&mut self) {
|
async fn disable(&mut self) {
|
||||||
trace!("disable");
|
trace!("disable");
|
||||||
|
|
||||||
Bus::disable(self);
|
// TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb
|
||||||
|
//Bus::disable(self);
|
||||||
self.enabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
|
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());
|
state.ep_in_wakers[index].register(cx.waker());
|
||||||
|
|
||||||
let diepctl = r.diepctl(index).read();
|
let diepctl = r.diepctl(index).read();
|
||||||
|
let dtxfsts = r.dtxfsts(index).read();
|
||||||
|
info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0);
|
||||||
if !diepctl.usbaep() {
|
if !diepctl.usbaep() {
|
||||||
|
trace!("write ep={:?} wait for prev: error disabled", self.info.addr);
|
||||||
Poll::Ready(Err(EndpointError::Disabled))
|
Poll::Ready(Err(EndpointError::Disabled))
|
||||||
} else if !diepctl.epena() {
|
} else if !diepctl.epena() {
|
||||||
|
trace!("write ep={:?} wait for prev: ready", self.info.addr);
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
|
trace!("write ep={:?} wait for prev: pending", self.info.addr);
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1169,6 +1247,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
|
|||||||
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
|
trace!("write ep={:?} wait for fifo: ready", self.info.addr);
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -52,7 +52,9 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let ep_out_buffer = &mut make_static!([0; 256])[..];
|
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
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
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
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
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
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
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
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
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
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
@ -31,7 +31,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Create the driver, from the HAL.
|
// Create the driver, from the HAL.
|
||||||
let mut ep_out_buffer = [0u8; 256];
|
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
|
// Create embassy-usb Config
|
||||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
Loading…
Reference in New Issue
Block a user