From 3dbb7c9e159aa456c1d85cb4d5c8d1299013d0cc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Apr 2022 02:24:55 +0200 Subject: [PATCH] usb/hid: add keyboard example. --- examples/nrf/src/bin/usb_hid_keyboard.rs | 148 ++++++++++++++++++ .../src/bin/{usb_hid.rs => usb_hid_mouse.rs} | 0 2 files changed, 148 insertions(+) create mode 100644 examples/nrf/src/bin/usb_hid_keyboard.rs rename examples/nrf/src/bin/{usb_hid.rs => usb_hid_mouse.rs} (100%) diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..af70a9a60 --- /dev/null +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -0,0 +1,148 @@ +#![no_std] +#![no_main] +#![feature(generic_associated_types)] +#![feature(type_alias_impl_trait)] + +use core::mem; +use defmt::*; +use embassy::executor::Spawner; +use embassy::time::Duration; +use embassy_nrf::gpio::{Input, Pin, Pull}; +use embassy_nrf::interrupt; +use embassy_nrf::pac; +use embassy_nrf::usb::Driver; +use embassy_nrf::Peripherals; +use embassy_usb::control::OutResponse; +use embassy_usb::{Config, UsbDeviceBuilder}; +use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; +use futures::future::join; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; + +use defmt_rtt as _; // global logger +use panic_probe as _; + +#[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; + config.max_packet_size_0 = 64; + + // 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::<64, 64>::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 hid = HidClass::with_output_ep( + &mut builder, + &mut state, + KeyboardReport::desc(), + Some(&request_handler), + 60, + 64, + ); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + let mut button = Input::new(p.P0_11.degrade(), Pull::Up); + + let (mut hid_in, hid_out) = hid.split(); + + // Do stuff with the class! + let in_fut = async { + loop { + button.wait_for_low().await; + info!("PRESSED"); + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match hid_in.serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + + button.wait_for_high().await; + info!("RELEASED"); + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match hid_in.serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + }; + + let out_fut = async { + hid_out.run(&MyRequestHandler {}).await; + }; + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(in_fut, out_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_report(&self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + 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 + } +} diff --git a/examples/nrf/src/bin/usb_hid.rs b/examples/nrf/src/bin/usb_hid_mouse.rs similarity index 100% rename from examples/nrf/src/bin/usb_hid.rs rename to examples/nrf/src/bin/usb_hid_mouse.rs