From 3dbb7c9e159aa456c1d85cb4d5c8d1299013d0cc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Apr 2022 02:24:55 +0200 Subject: usb/hid: add keyboard example. --- examples/nrf/src/bin/usb_hid.rs | 129 --------------------------- examples/nrf/src/bin/usb_hid_keyboard.rs | 148 +++++++++++++++++++++++++++++++ examples/nrf/src/bin/usb_hid_mouse.rs | 129 +++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 129 deletions(-) delete mode 100644 examples/nrf/src/bin/usb_hid.rs create mode 100644 examples/nrf/src/bin/usb_hid_keyboard.rs create mode 100644 examples/nrf/src/bin/usb_hid_mouse.rs (limited to 'examples') diff --git a/examples/nrf/src/bin/usb_hid.rs b/examples/nrf/src/bin/usb_hid.rs deleted file mode 100644 index 741e234b6..000000000 --- a/examples/nrf/src/bin/usb_hid.rs +++ /dev/null @@ -1,129 +0,0 @@ -#![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, Timer}; -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::{MouseReport, 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; - - // 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 control = State::<5, 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 hid = HidClass::new( - &mut builder, - &mut control, - MouseReport::desc(), - Some(&request_handler), - 60, - 8, - ); - - // 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 { - let mut y: i8 = 5; - loop { - Timer::after(Duration::from_millis(500)).await; - - y = -y; - let report = MouseReport { - buttons: 0, - x: 0, - y, - wheel: 0, - pan: 0, - }; - match hid.input().serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - } - } - }; - - // 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_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_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_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs new file mode 100644 index 000000000..741e234b6 --- /dev/null +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -0,0 +1,129 @@ +#![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, Timer}; +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::{MouseReport, 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; + + // 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 control = State::<5, 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 hid = HidClass::new( + &mut builder, + &mut control, + MouseReport::desc(), + Some(&request_handler), + 60, + 8, + ); + + // 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 { + let mut y: i8 = 5; + loop { + Timer::after(Duration::from_millis(500)).await; + + y = -y; + let report = MouseReport { + buttons: 0, + x: 0, + y, + wheel: 0, + pan: 0, + }; + match hid.input().serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } + } + }; + + // 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_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 + } +} -- cgit