From a57d383b1d2872759cf1a3ab7148cfa175ea7614 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 14 Oct 2023 04:20:59 +0200 Subject: embassy-usb: Add MIDI class --- examples/rp/src/bin/usb_midi.rs | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 examples/rp/src/bin/usb_midi.rs (limited to 'examples') diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs new file mode 100644 index 000000000..f0b03c81b --- /dev/null +++ b/examples/rp/src/bin/usb_midi.rs @@ -0,0 +1,110 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates a USB MIDI device that echoes MIDI messages back to the host. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, panic}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_usb::class::midi::MidiClass; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello world!"); + + 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 mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-MIDI example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = MidiClass::new(&mut builder, 1, 1, 64); + + // The `MidiClass` can be split into `Sender` and `Receiver`, to be used in separate tasks. + // let (sender, receiver) = class.split(); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Use the Midi class! + let midi_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = midi_echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, midi_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn midi_echo<'d, T: Instance + 'd>(class: &mut MidiClass<'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?; + } +} -- cgit