Module rp2040_hal::usb
source · Expand description
Universal Serial Bus (USB)
§Usage
Initialize the Usb Bus forcing the VBUS detection.
use rp2040_hal::{clocks::init_clocks_and_plls, pac, Sio, usb::UsbBus, watchdog::Watchdog};
use usb_device::class_prelude::UsbBusAllocator;
const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
let mut pac = pac::Peripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let mut clocks = init_clocks_and_plls(
XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog
).ok().unwrap();
let usb_bus = UsbBusAllocator::new(UsbBus::new(
pac.USBCTRL_REGS,
pac.USBCTRL_DPRAM,
clocks.usb_clock,
true,
&mut pac.RESETS,
));
// Use the usb_bus as usual.
See pico_usb_serial.rs for more complete examples
§Enumeration issue with small EP0 max packet size
During enumeration Windows hosts send a StatusOut
after the DataIn
packet of the first
Get Descriptor
request even if the DataIn
isn’t completed (typically when the max_packet_size_ep0
is less than 18bytes). The next request is a Set Address
that expect a StatusIn
.
The issue is that by the time the previous DataIn
packet is acknowledged and the StatusOut
followed by Setup
are received, the usb stack may have already prepared the next DataIn
payload
in the EP0 IN mailbox resulting in the payload being transmitted to the host instead of the
StatusIn
for the Set Address
request as expected by the host.
To avoid that issue, the EP0 In mailbox should be invalidated between the Setup
packet and the
next StatusIn
initiated by the host. The workaround implemented clears the available bit of the
EP0 In endpoint’s buffer to stop the device from sending the data instead of the status packet.
This workaround has the caveat that the poll function must be called between those two which
are only separated by a few microseconds.
If the required timing cannot be met, using an maximum packet size of the endpoint 0 above 18bytes
(e.g. .max_packet_size_ep0(64)
) should avoid that issue.
§Issue on RP2040B0 and RP2040B1: USB device fails to exit RESET state on busy USB bus.
The feature rp2040-e5
implements the workaround described by RP2040-E5.
The workaround requires the GPIO block to be released from its reset and has for side effect that GPIO15 will be stolen for a few hundred microseconds each time a Reset is detected on the USB bus.
The pin will be temporarily put in “bus keep” mode, weakly pulling the output towards its current logic level. In absence of external loads, the current logic level will be maintained. A user will lose control of the pin’s output and reading from it may not reflect the actual state of the external pin.
use rp2040_hal::{gpio::Pins, Sio};
// required for the errata 5's workaround to function properly.
let sio = Sio::new(pac.SIO);
let _pins = Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
Structs§
- Usb bus