aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32f4
diff options
context:
space:
mode:
authorVo Trung Chi <[email protected]>2024-03-14 23:02:22 +0700
committerVo Trung Chi <[email protected]>2024-03-14 23:14:43 +0700
commit9f699e5772815d95da473dcec4f4f574e2659e06 (patch)
tree071ce4d8abcc1b898975cf74469f3c683a9db6ae /examples/stm32f4
parentbbcab556c8a4514cd9ceda49d64c5644c82ba9e8 (diff)
stm32: add usb_hid_keyboard example
Signed-off-by: Vo Trung Chi <[email protected]>
Diffstat (limited to 'examples/stm32f4')
-rw-r--r--examples/stm32f4/src/bin/usb_hid_keyboard.rs222
1 files changed, 222 insertions, 0 deletions
diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs
new file mode 100644
index 000000000..19b5971fb
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs
@@ -0,0 +1,222 @@
1#![no_std]
2#![no_main]
3
4use core::sync::atomic::{AtomicBool, Ordering};
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_stm32::exti::ExtiInput;
9use embassy_stm32::gpio::Pull;
10use embassy_stm32::time::Hertz;
11use embassy_stm32::usb_otg::Driver;
12use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
13use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State};
14use embassy_usb::control::OutResponse;
15use embassy_usb::{Builder, Handler};
16use futures::future::join;
17use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
18use {defmt_rtt as _, panic_probe as _};
19
20bind_interrupts!(struct Irqs {
21 OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>;
22});
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let mut config = Config::default();
27 {
28 use embassy_stm32::rcc::*;
29 config.rcc.hse = Some(Hse {
30 freq: Hertz(8_000_000),
31 mode: HseMode::Bypass,
32 });
33 config.rcc.pll_src = PllSource::HSE;
34 config.rcc.pll = Some(Pll {
35 prediv: PllPreDiv::DIV4,
36 mul: PllMul::MUL168,
37 divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
38 divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
39 divr: None,
40 });
41 config.rcc.ahb_pre = AHBPrescaler::DIV1;
42 config.rcc.apb1_pre = APBPrescaler::DIV4;
43 config.rcc.apb2_pre = APBPrescaler::DIV2;
44 config.rcc.sys = Sysclk::PLL1_P;
45 }
46 let p = embassy_stm32::init(config);
47
48 // Create the driver, from the HAL.
49 let mut ep_out_buffer = [0u8; 256];
50 let mut config = embassy_stm32::usb_otg::Config::default();
51 config.vbus_detection = true;
52 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
53
54 // Create embassy-usb Config
55 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
56 config.manufacturer = Some("Embassy");
57 config.product = Some("HID keyboard example");
58 config.serial_number = Some("12345678");
59 config.max_power = 100;
60 config.max_packet_size_0 = 64;
61
62 // Required for windows compatibility.
63 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
64 config.device_class = 0xEF;
65 config.device_sub_class = 0x02;
66 config.device_protocol = 0x01;
67 config.composite_with_iads = true;
68
69 // Create embassy-usb DeviceBuilder using the driver and config.
70 // It needs some buffers for building the descriptors.
71 let mut device_descriptor = [0; 256];
72 let mut config_descriptor = [0; 256];
73 let mut bos_descriptor = [0; 256];
74 // You can also add a Microsoft OS descriptor.
75 let mut msos_descriptor = [0; 256];
76 let mut control_buf = [0; 64];
77
78 let request_handler = MyRequestHandler {};
79 let mut device_handler = MyDeviceHandler::new();
80
81 let mut state = State::new();
82
83 let mut builder = Builder::new(
84 driver,
85 config,
86 &mut device_descriptor,
87 &mut config_descriptor,
88 &mut bos_descriptor,
89 &mut msos_descriptor,
90 &mut control_buf,
91 );
92
93 builder.handler(&mut device_handler);
94
95 // Create classes on the builder.
96 let config = embassy_usb::class::hid::Config {
97 report_descriptor: KeyboardReport::desc(),
98 request_handler: Some(&request_handler),
99 poll_ms: 60,
100 max_packet_size: 8,
101 };
102
103 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
104
105 // Build the builder.
106 let mut usb = builder.build();
107
108 // Run the USB device.
109 let usb_fut = usb.run();
110
111 let (reader, mut writer) = hid.split();
112
113 let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down);
114
115 // Do stuff with the class!
116 let in_fut = async {
117 loop {
118 button.wait_for_rising_edge().await;
119 // signal_pin.wait_for_high().await;
120 info!("Button pressed!");
121 // Create a report with the A key pressed. (no shift modifier)
122 let report = KeyboardReport {
123 keycodes: [4, 0, 0, 0, 0, 0],
124 leds: 0,
125 modifier: 0,
126 reserved: 0,
127 };
128 // Send the report.
129 match writer.write_serialize(&report).await {
130 Ok(()) => {}
131 Err(e) => warn!("Failed to send report: {:?}", e),
132 };
133
134 button.wait_for_falling_edge().await;
135 // signal_pin.wait_for_low().await;
136 info!("Button released!");
137 let report = KeyboardReport {
138 keycodes: [0, 0, 0, 0, 0, 0],
139 leds: 0,
140 modifier: 0,
141 reserved: 0,
142 };
143 match writer.write_serialize(&report).await {
144 Ok(()) => {}
145 Err(e) => warn!("Failed to send report: {:?}", e),
146 };
147 }
148 };
149
150 let out_fut = async {
151 reader.run(false, &request_handler).await;
152 };
153
154 // Run everything concurrently.
155 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
156 join(usb_fut, join(in_fut, out_fut)).await;
157}
158
159struct MyRequestHandler {}
160
161impl RequestHandler for MyRequestHandler {
162 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
163 info!("Get report for {:?}", id);
164 None
165 }
166
167 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
168 info!("Set report for {:?}: {=[u8]}", id, data);
169 OutResponse::Accepted
170 }
171
172 fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) {
173 info!("Set idle rate for {:?} to {:?}", id, dur);
174 }
175
176 fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
177 info!("Get idle rate for {:?}", id);
178 None
179 }
180}
181
182struct MyDeviceHandler {
183 configured: AtomicBool,
184}
185
186impl MyDeviceHandler {
187 fn new() -> Self {
188 MyDeviceHandler {
189 configured: AtomicBool::new(false),
190 }
191 }
192}
193
194impl Handler for MyDeviceHandler {
195 fn enabled(&mut self, enabled: bool) {
196 self.configured.store(false, Ordering::Relaxed);
197 if enabled {
198 info!("Device enabled");
199 } else {
200 info!("Device disabled");
201 }
202 }
203
204 fn reset(&mut self) {
205 self.configured.store(false, Ordering::Relaxed);
206 info!("Bus reset, the Vbus current limit is 100mA");
207 }
208
209 fn addressed(&mut self, addr: u8) {
210 self.configured.store(false, Ordering::Relaxed);
211 info!("USB address set to: {}", addr);
212 }
213
214 fn configured(&mut self, configured: bool) {
215 self.configured.store(configured, Ordering::Relaxed);
216 if configured {
217 info!("Device configured, it may now draw up to the configured current limit from Vbus.")
218 } else {
219 info!("Device is no longer configured, the Vbus current limit is 100mA.");
220 }
221 }
222}