From 5ee7a85b33f83131fd42ce229d3aadaf2054f44a Mon Sep 17 00:00:00 2001 From: alexmoon Date: Tue, 29 Mar 2022 15:18:43 -0400 Subject: Async USB HID class --- examples/nrf/Cargo.toml | 6 +- examples/nrf/src/bin/usb_hid.rs | 131 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 examples/nrf/src/bin/usb_hid.rs (limited to 'examples') diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index aa30f3fa9..e944c171a 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -6,13 +6,14 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial"] +nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid"] [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } +embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } defmt = "0.3" defmt-rtt = "0.3" @@ -23,4 +24,5 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" - +usbd-hid = "0.5.2" +serde = { version = "1.0.136", default-features = false } diff --git a/examples/nrf/src/bin/usb_hid.rs b/examples/nrf/src/bin/usb_hid.rs new file mode 100644 index 000000000..1fd056d00 --- /dev/null +++ b/examples/nrf/src/bin/usb_hid.rs @@ -0,0 +1,131 @@ +#![no_std] +#![no_main] +#![feature(generic_associated_types)] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; + +use core::mem; +use defmt::*; +use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; +use embassy_nrf::interrupt; +use embassy_nrf::pac; +use embassy_nrf::usb::Driver; +use embassy_nrf::Peripherals; +use embassy_usb::{Config, UsbDeviceBuilder}; +use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; +use futures::future::join; +use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + let clock: pac::CLOCK = unsafe { mem::transmute(()) }; + let power: pac::POWER = unsafe { mem::transmute(()) }; + + info!("Enabling ext hfosc..."); + clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); + while clock.events_hfclkstarted.read().bits() != 1 {} + + info!("Waiting for vbus..."); + while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} + info!("vbus OK"); + + // Create the driver, from the HAL. + let irq = interrupt::take!(USBD); + let driver = Driver::new(p.USBD, irq); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Tactile Engineering"); + config.product = Some("Testy"); + config.serial_number = Some("12345678"); + config.max_power = 100; + + // 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; 16]; + let request_handler = MyRequestHandler {}; + + let mut state = State::<5, 0, 0>::new(); + + let mut builder = UsbDeviceBuilder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + // let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + let mut hid = HidClass::new( + &mut builder, + &mut state, + MouseReport::desc(), + Some(&request_handler), + 60, + ); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let hid_fut = async { + loop { + Timer::after(Duration::from_millis(500)).await; + hid.input() + .serialize(&MouseReport { + buttons: 0, + x: 0, + y: 4, + wheel: 0, + pan: 0, + }) + .await + .unwrap(); + + Timer::after(Duration::from_millis(500)).await; + hid.input() + .serialize(&MouseReport { + buttons: 0, + x: 0, + y: -4, + wheel: 0, + pan: 0, + }) + .await + .unwrap(); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, hid_fut).await; +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_idle(&self, id: Option, dur: Duration) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle(&self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} -- cgit