Added usb-hid keyboard example for rp pico.
This commit is contained in:
		| @@ -40,6 +40,7 @@ display-interface = "0.4.1" | |||||||
| byte-slice-cast = { version = "1.2.0", default-features = false } | byte-slice-cast = { version = "1.2.0", default-features = false } | ||||||
| smart-leds = "0.3.0" | smart-leds = "0.3.0" | ||||||
| heapless = "0.7.15" | heapless = "0.7.15" | ||||||
|  | usbd-hid = "0.6.1" | ||||||
|  |  | ||||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } | ||||||
| embedded-hal-async = "0.2.0-alpha.2" | embedded-hal-async = "0.2.0-alpha.2" | ||||||
|   | |||||||
							
								
								
									
										188
									
								
								examples/rp/src/bin/usb_hid_keyboard.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								examples/rp/src/bin/usb_hid_keyboard.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![no_main] | ||||||
|  | #![feature(type_alias_impl_trait)] | ||||||
|  |  | ||||||
|  | use core::sync::atomic::{AtomicBool, Ordering}; | ||||||
|  |  | ||||||
|  | use defmt::*; | ||||||
|  | use embassy_executor::Spawner; | ||||||
|  | use embassy_futures::join::join; | ||||||
|  | use embassy_rp::bind_interrupts; | ||||||
|  | use embassy_rp::gpio::{Input, Pull}; | ||||||
|  | use embassy_rp::peripherals::USB; | ||||||
|  | use embassy_rp::usb::{Driver, InterruptHandler}; | ||||||
|  | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | ||||||
|  | use embassy_usb::control::OutResponse; | ||||||
|  | use embassy_usb::{Builder, Config, Handler}; | ||||||
|  | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||||||
|  | use {defmt_rtt as _, panic_probe as _}; | ||||||
|  |  | ||||||
|  | bind_interrupts!(struct Irqs { | ||||||
|  |     USBCTRL_IRQ => InterruptHandler<USB>; | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | #[embassy_executor::main] | ||||||
|  | async fn main(_spawner: Spawner) { | ||||||
|  |     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("HID keyboard example"); | ||||||
|  |     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]; | ||||||
|  |     // You can also add a Microsoft OS descriptor. | ||||||
|  |     // let mut msos_descriptor = [0; 256]; | ||||||
|  |     let mut control_buf = [0; 64]; | ||||||
|  |     let request_handler = MyRequestHandler {}; | ||||||
|  |     let mut device_handler = MyDeviceHandler::new(); | ||||||
|  |  | ||||||
|  |     let mut state = State::new(); | ||||||
|  |  | ||||||
|  |     let mut builder = Builder::new( | ||||||
|  |         driver, | ||||||
|  |         config, | ||||||
|  |         &mut device_descriptor, | ||||||
|  |         &mut config_descriptor, | ||||||
|  |         &mut bos_descriptor, | ||||||
|  |         // &mut msos_descriptor, | ||||||
|  |         &mut control_buf, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     builder.handler(&mut device_handler); | ||||||
|  |  | ||||||
|  |     // Create classes on the builder. | ||||||
|  |     let config = embassy_usb::class::hid::Config { | ||||||
|  |         report_descriptor: KeyboardReport::desc(), | ||||||
|  |         request_handler: Some(&request_handler), | ||||||
|  |         poll_ms: 60, | ||||||
|  |         max_packet_size: 64, | ||||||
|  |     }; | ||||||
|  |     let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | ||||||
|  |  | ||||||
|  |     // Build the builder. | ||||||
|  |     let mut usb = builder.build(); | ||||||
|  |  | ||||||
|  |     // Run the USB device. | ||||||
|  |     let usb_fut = usb.run(); | ||||||
|  |  | ||||||
|  |     // Set up the signal pin that will be used to trigger the keyboard. | ||||||
|  |     let mut signal_pin = Input::new(p.PIN_16, Pull::None); | ||||||
|  |  | ||||||
|  |     let (reader, mut writer) = hid.split(); | ||||||
|  |  | ||||||
|  |     // Do stuff with the class! | ||||||
|  |     let in_fut = async { | ||||||
|  |         loop { | ||||||
|  |             info!("Waiting for HIGH on pin 16"); | ||||||
|  |             signal_pin.wait_for_high().await; | ||||||
|  |             info!("HIGH DETECTED"); | ||||||
|  |             // Create a report with the A key pressed. (no shift modifier) | ||||||
|  |             let report = KeyboardReport { | ||||||
|  |                 keycodes: [4, 0, 0, 0, 0, 0], | ||||||
|  |                 leds: 0, | ||||||
|  |                 modifier: 0, | ||||||
|  |                 reserved: 0, | ||||||
|  |             }; | ||||||
|  |             // Send the report. | ||||||
|  |             match writer.write_serialize(&report).await { | ||||||
|  |                 Ok(()) => {} | ||||||
|  |                 Err(e) => warn!("Failed to send report: {:?}", e), | ||||||
|  |             }; | ||||||
|  |             signal_pin.wait_for_low().await; | ||||||
|  |             info!("LOW DETECTED"); | ||||||
|  |             let report = KeyboardReport { | ||||||
|  |                 keycodes: [0, 0, 0, 0, 0, 0], | ||||||
|  |                 leds: 0, | ||||||
|  |                 modifier: 0, | ||||||
|  |                 reserved: 0, | ||||||
|  |             }; | ||||||
|  |             match writer.write_serialize(&report).await { | ||||||
|  |                 Ok(()) => {} | ||||||
|  |                 Err(e) => warn!("Failed to send report: {:?}", e), | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let out_fut = async { | ||||||
|  |         reader.run(false, &request_handler).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<usize> { | ||||||
|  |         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_ms(&self, id: Option<ReportId>, dur: u32) { | ||||||
|  |         info!("Set idle rate for {:?} to {:?}", id, dur); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { | ||||||
|  |         info!("Get idle rate for {:?}", id); | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct MyDeviceHandler { | ||||||
|  |     configured: AtomicBool, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl MyDeviceHandler { | ||||||
|  |     fn new() -> Self { | ||||||
|  |         MyDeviceHandler { | ||||||
|  |             configured: AtomicBool::new(false), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Handler for MyDeviceHandler { | ||||||
|  |     fn enabled(&mut self, enabled: bool) { | ||||||
|  |         self.configured.store(false, Ordering::Relaxed); | ||||||
|  |         if enabled { | ||||||
|  |             info!("Device enabled"); | ||||||
|  |         } else { | ||||||
|  |             info!("Device disabled"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn reset(&mut self) { | ||||||
|  |         self.configured.store(false, Ordering::Relaxed); | ||||||
|  |         info!("Bus reset, the Vbus current limit is 100mA"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn addressed(&mut self, addr: u8) { | ||||||
|  |         self.configured.store(false, Ordering::Relaxed); | ||||||
|  |         info!("USB address set to: {}", addr); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn configured(&mut self, configured: bool) { | ||||||
|  |         self.configured.store(configured, Ordering::Relaxed); | ||||||
|  |         if configured { | ||||||
|  |             info!("Device configured, it may now draw up to the configured current limit from Vbus.") | ||||||
|  |         } else { | ||||||
|  |             info!("Device is no longer configured, the Vbus current limit is 100mA."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user