aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-07-16 11:17:49 +0000
committerGitHub <[email protected]>2023-07-16 11:17:49 +0000
commitc7ec45a004750f590c1d9ea4a721972efe133b8e (patch)
tree32cfdf85a1aa132cb0f1aac56a934ceed93fc597
parent25197308e3cd9694c37284b49ce1b482e22855ce (diff)
parent88d1976e812af995028025407872bf64264c4f94 (diff)
Merge pull request #1659 from maximedeboeck/hid-tests
Add usb-hid keyboard example for rp pico.
-rw-r--r--examples/rp/Cargo.toml1
-rw-r--r--examples/rp/src/bin/usb_hid_keyboard.rs188
2 files changed, 189 insertions, 0 deletions
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 7c5a9dfbc..c812cb3ee 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -40,6 +40,7 @@ display-interface = "0.4.1"
40byte-slice-cast = { version = "1.2.0", default-features = false } 40byte-slice-cast = { version = "1.2.0", default-features = false }
41smart-leds = "0.3.0" 41smart-leds = "0.3.0"
42heapless = "0.7.15" 42heapless = "0.7.15"
43usbd-hid = "0.6.1"
43 44
44embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } 45embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
45embedded-hal-async = "0.2.0-alpha.2" 46embedded-hal-async = "0.2.0-alpha.2"
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs
new file mode 100644
index 000000000..99af1f02f
--- /dev/null
+++ b/examples/rp/src/bin/usb_hid_keyboard.rs
@@ -0,0 +1,188 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use core::sync::atomic::{AtomicBool, Ordering};
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_futures::join::join;
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::{Input, Pull};
12use embassy_rp::peripherals::USB;
13use embassy_rp::usb::{Driver, InterruptHandler};
14use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State};
15use embassy_usb::control::OutResponse;
16use embassy_usb::{Builder, Config, Handler};
17use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
18use {defmt_rtt as _, panic_probe as _};
19
20bind_interrupts!(struct Irqs {
21 USBCTRL_IRQ => InterruptHandler<USB>;
22});
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = embassy_rp::init(Default::default());
27 // Create the driver, from the HAL.
28 let driver = Driver::new(p.USB, Irqs);
29
30 // Create embassy-usb Config
31 let mut config = Config::new(0xc0de, 0xcafe);
32 config.manufacturer = Some("Embassy");
33 config.product = Some("HID keyboard example");
34 config.serial_number = Some("12345678");
35 config.max_power = 100;
36 config.max_packet_size_0 = 64;
37
38 // Create embassy-usb DeviceBuilder using the driver and config.
39 // It needs some buffers for building the descriptors.
40 let mut device_descriptor = [0; 256];
41 let mut config_descriptor = [0; 256];
42 let mut bos_descriptor = [0; 256];
43 // You can also add a Microsoft OS descriptor.
44 // let mut msos_descriptor = [0; 256];
45 let mut control_buf = [0; 64];
46 let request_handler = MyRequestHandler {};
47 let mut device_handler = MyDeviceHandler::new();
48
49 let mut state = State::new();
50
51 let mut builder = Builder::new(
52 driver,
53 config,
54 &mut device_descriptor,
55 &mut config_descriptor,
56 &mut bos_descriptor,
57 // &mut msos_descriptor,
58 &mut control_buf,
59 );
60
61 builder.handler(&mut device_handler);
62
63 // Create classes on the builder.
64 let config = embassy_usb::class::hid::Config {
65 report_descriptor: KeyboardReport::desc(),
66 request_handler: Some(&request_handler),
67 poll_ms: 60,
68 max_packet_size: 64,
69 };
70 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
71
72 // Build the builder.
73 let mut usb = builder.build();
74
75 // Run the USB device.
76 let usb_fut = usb.run();
77
78 // Set up the signal pin that will be used to trigger the keyboard.
79 let mut signal_pin = Input::new(p.PIN_16, Pull::None);
80
81 let (reader, mut writer) = hid.split();
82
83 // Do stuff with the class!
84 let in_fut = async {
85 loop {
86 info!("Waiting for HIGH on pin 16");
87 signal_pin.wait_for_high().await;
88 info!("HIGH DETECTED");
89 // Create a report with the A key pressed. (no shift modifier)
90 let report = KeyboardReport {
91 keycodes: [4, 0, 0, 0, 0, 0],
92 leds: 0,
93 modifier: 0,
94 reserved: 0,
95 };
96 // Send the report.
97 match writer.write_serialize(&report).await {
98 Ok(()) => {}
99 Err(e) => warn!("Failed to send report: {:?}", e),
100 };
101 signal_pin.wait_for_low().await;
102 info!("LOW DETECTED");
103 let report = KeyboardReport {
104 keycodes: [0, 0, 0, 0, 0, 0],
105 leds: 0,
106 modifier: 0,
107 reserved: 0,
108 };
109 match writer.write_serialize(&report).await {
110 Ok(()) => {}
111 Err(e) => warn!("Failed to send report: {:?}", e),
112 };
113 }
114 };
115
116 let out_fut = async {
117 reader.run(false, &request_handler).await;
118 };
119
120 // Run everything concurrently.
121 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
122 join(usb_fut, join(in_fut, out_fut)).await;
123}
124
125struct MyRequestHandler {}
126
127impl RequestHandler for MyRequestHandler {
128 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
129 info!("Get report for {:?}", id);
130 None
131 }
132
133 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
134 info!("Set report for {:?}: {=[u8]}", id, data);
135 OutResponse::Accepted
136 }
137
138 fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) {
139 info!("Set idle rate for {:?} to {:?}", id, dur);
140 }
141
142 fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
143 info!("Get idle rate for {:?}", id);
144 None
145 }
146}
147
148struct MyDeviceHandler {
149 configured: AtomicBool,
150}
151
152impl MyDeviceHandler {
153 fn new() -> Self {
154 MyDeviceHandler {
155 configured: AtomicBool::new(false),
156 }
157 }
158}
159
160impl Handler for MyDeviceHandler {
161 fn enabled(&mut self, enabled: bool) {
162 self.configured.store(false, Ordering::Relaxed);
163 if enabled {
164 info!("Device enabled");
165 } else {
166 info!("Device disabled");
167 }
168 }
169
170 fn reset(&mut self) {
171 self.configured.store(false, Ordering::Relaxed);
172 info!("Bus reset, the Vbus current limit is 100mA");
173 }
174
175 fn addressed(&mut self, addr: u8) {
176 self.configured.store(false, Ordering::Relaxed);
177 info!("USB address set to: {}", addr);
178 }
179
180 fn configured(&mut self, configured: bool) {
181 self.configured.store(configured, Ordering::Relaxed);
182 if configured {
183 info!("Device configured, it may now draw up to the configured current limit from Vbus.")
184 } else {
185 info!("Device is no longer configured, the Vbus current limit is 100mA.");
186 }
187 }
188}