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