//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. //! //! This creates a USB serial port that echos. #![no_std] #![no_main] use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::UsbDevice; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); #[embassy_executor::main] async fn main(spawner: Spawner) { info!("Hello there!"); let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let config = { let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); config.manufacturer = Some("Embassy"); config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; config }; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. let mut builder = { static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); static BOS_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new(); let builder = embassy_usb::Builder::new( driver, config, CONFIG_DESCRIPTOR.init([0; 256]), BOS_DESCRIPTOR.init([0; 256]), &mut [], // no msos descriptors CONTROL_BUF.init([0; 64]), ); builder }; // Create classes on the builder. let mut class = { static STATE: StaticCell = StaticCell::new(); let state = STATE.init(State::new()); CdcAcmClass::new(&mut builder, state, 64) }; // Build the builder. let usb = builder.build(); // Run the USB device. unwrap!(spawner.spawn(usb_task(usb))); // Do stuff with the class! loop { class.wait_connection().await; info!("Connected"); let _ = echo(&mut class).await; info!("Disconnected"); } } type MyUsbDriver = Driver<'static, USB>; type MyUsbDevice = UsbDevice<'static, MyUsbDriver>; #[embassy_executor::task] async fn usb_task(mut usb: MyUsbDevice) -> ! { usb.run().await } struct Disconnected {} impl From for Disconnected { fn from(val: EndpointError) -> Self { match val { EndpointError::BufferOverflow => panic!("Buffer overflow"), EndpointError::Disabled => Disconnected {}, } } } async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = class.read_packet(&mut buf).await?; let data = &buf[..n]; info!("data: {:x}", data); class.write_packet(data).await?; } }