aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml5
-rw-r--r--embassy-stm32/build.rs2
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/usb/mod.rs36
-rw-r--r--embassy-stm32/src/usb/usb.rs1064
-rw-r--r--examples/stm32f1/Cargo.toml2
-rw-r--r--examples/stm32f1/src/bin/usb_serial.rs117
-rw-r--r--examples/stm32f3/.cargo/config.toml2
-rw-r--r--examples/stm32f3/Cargo.toml5
-rw-r--r--examples/stm32f3/src/bin/usb_serial.rs116
-rw-r--r--examples/stm32l5/.cargo/config.toml6
-rw-r--r--examples/stm32l5/Cargo.toml30
-rw-r--r--examples/stm32l5/build.rs5
-rw-r--r--examples/stm32l5/src/bin/button_exti.rs28
-rw-r--r--examples/stm32l5/src/bin/rng.rs34
-rw-r--r--examples/stm32l5/src/bin/usb_ethernet.rs290
-rw-r--r--examples/stm32l5/src/bin/usb_hid_mouse.rs136
-rw-r--r--examples/stm32l5/src/bin/usb_serial.rs112
m---------stm32-data0
19 files changed, 1988 insertions, 4 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 32311cc75..01a96a5fb 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -37,6 +37,7 @@ embassy = { version = "0.1.0", path = "../embassy" }
37embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } 37embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] }
38embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } 38embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
39embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } 39embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
40embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
40 41
41embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 42embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
42embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} 43embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
@@ -71,7 +72,7 @@ quote = "1.0.15"
71stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} 72stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
72 73
73[features] 74[features]
74defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt" ] 75defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"]
75sdmmc-rs = ["embedded-sdmmc"] 76sdmmc-rs = ["embedded-sdmmc"]
76net = ["embassy-net" ] 77net = ["embassy-net" ]
77memory-x = ["stm32-metapac/memory-x"] 78memory-x = ["stm32-metapac/memory-x"]
@@ -90,7 +91,7 @@ time-driver-tim12 = ["_time-driver"]
90time-driver-tim15 = ["_time-driver"] 91time-driver-tim15 = ["_time-driver"]
91 92
92# Enable nightly-only features 93# Enable nightly-only features
93nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] 94nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb"]
94 95
95# Reexport stm32-metapac at `embassy_stm32::pac`. 96# Reexport stm32-metapac at `embassy_stm32::pac`.
96# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. 97# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 490f2d8f2..7b1376f0b 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -279,6 +279,8 @@ fn main() {
279 (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)), 279 (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)),
280 (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)), 280 (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)),
281 (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), 281 (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)),
282 (("usb", "DP"), quote!(crate::usb::DpPin)),
283 (("usb", "DM"), quote!(crate::usb::DmPin)),
282 (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)), 284 (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)),
283 (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)), 285 (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)),
284 (("otghs", "DP"), quote!(crate::usb_otg::DpPin)), 286 (("otghs", "DP"), quote!(crate::usb_otg::DpPin)),
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index fbd2008c8..bb70faab1 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -63,6 +63,8 @@ pub mod sdmmc;
63pub mod spi; 63pub mod spi;
64#[cfg(usart)] 64#[cfg(usart)]
65pub mod usart; 65pub mod usart;
66#[cfg(usb)]
67pub mod usb;
66#[cfg(any(otgfs, otghs))] 68#[cfg(any(otgfs, otghs))]
67pub mod usb_otg; 69pub mod usb_otg;
68 70
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs
new file mode 100644
index 000000000..71b407cbd
--- /dev/null
+++ b/embassy-stm32/src/usb/mod.rs
@@ -0,0 +1,36 @@
1use embassy::interrupt::Interrupt;
2
3use crate::rcc::RccPeripheral;
4
5#[cfg(feature = "nightly")]
6mod usb;
7#[cfg(feature = "nightly")]
8pub use usb::*;
9
10pub(crate) mod sealed {
11 pub trait Instance {
12 fn regs() -> crate::pac::usb::Usb;
13 }
14}
15
16pub trait Instance: sealed::Instance + RccPeripheral + 'static {
17 type Interrupt: Interrupt;
18}
19
20// Internal PHY pins
21pin_trait!(DpPin, Instance);
22pin_trait!(DmPin, Instance);
23
24foreach_interrupt!(
25 ($inst:ident, usb, $block:ident, LP, $irq:ident) => {
26 impl sealed::Instance for crate::peripherals::$inst {
27 fn regs() -> crate::pac::usb::Usb {
28 crate::pac::$inst
29 }
30 }
31
32 impl Instance for crate::peripherals::$inst {
33 type Interrupt = crate::interrupt::$irq;
34 }
35 };
36);
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
new file mode 100644
index 000000000..113b20262
--- /dev/null
+++ b/embassy-stm32/src/usb/usb.rs
@@ -0,0 +1,1064 @@
1#![macro_use]
2
3use atomic_polyfill::{AtomicBool, AtomicU8};
4use core::marker::PhantomData;
5use core::sync::atomic::Ordering;
6use core::task::Poll;
7use embassy::interrupt::InterruptExt;
8use embassy::time::{block_for, Duration};
9use embassy::util::Unborrow;
10use embassy::waitqueue::AtomicWaker;
11use embassy_hal_common::unborrow;
12use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported};
13use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
14use futures::future::poll_fn;
15use futures::Future;
16use pac::common::{Reg, RW};
17use pac::usb::vals::{EpType, Stat};
18
19use crate::gpio::sealed::AFType;
20use crate::pac;
21use crate::pac::usb::regs;
22use crate::rcc::sealed::RccPeripheral;
23
24use super::{DmPin, DpPin, Instance};
25
26const EP_COUNT: usize = 8;
27
28#[cfg(any(usb_v1_x1, usb_v1_x2))]
29const EP_MEMORY_SIZE: usize = 512;
30#[cfg(not(any(usb_v1_x1, usb_v1_x2)))]
31const EP_MEMORY_SIZE: usize = 1024;
32
33const NEW_AW: AtomicWaker = AtomicWaker::new();
34static BUS_WAKER: AtomicWaker = NEW_AW;
35static EP0_SETUP: AtomicBool = AtomicBool::new(false);
36static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
37static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
38static IRQ_FLAGS: AtomicU8 = AtomicU8::new(0);
39const IRQ_FLAG_RESET: u8 = 0x01;
40const IRQ_FLAG_SUSPEND: u8 = 0x02;
41const IRQ_FLAG_RESUME: u8 = 0x04;
42
43fn convert_type(t: EndpointType) -> EpType {
44 match t {
45 EndpointType::Bulk => EpType::BULK,
46 EndpointType::Control => EpType::CONTROL,
47 EndpointType::Interrupt => EpType::INTERRUPT,
48 EndpointType::Isochronous => EpType::ISO,
49 }
50}
51
52fn invariant(mut r: regs::Epr) -> regs::Epr {
53 r.set_ctr_rx(true); // don't clear
54 r.set_ctr_tx(true); // don't clear
55 r.set_dtog_rx(false); // don't toggle
56 r.set_dtog_tx(false); // don't toggle
57 r.set_stat_rx(Stat(0));
58 r.set_stat_tx(Stat(0));
59 r
60}
61
62// Returns (actual_len, len_bits)
63fn calc_out_len(len: u16) -> (u16, u16) {
64 match len {
65 2..=62 => ((len + 1) / 2 * 2, ((len + 1) / 2) << 10),
66 63..=480 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000),
67 _ => panic!("invalid OUT length {}", len),
68 }
69}
70fn ep_in_addr<T: Instance>(index: usize) -> Reg<u16, RW> {
71 T::regs().ep_mem(index * 4 + 0)
72}
73fn ep_in_len<T: Instance>(index: usize) -> Reg<u16, RW> {
74 T::regs().ep_mem(index * 4 + 1)
75}
76fn ep_out_addr<T: Instance>(index: usize) -> Reg<u16, RW> {
77 T::regs().ep_mem(index * 4 + 2)
78}
79fn ep_out_len<T: Instance>(index: usize) -> Reg<u16, RW> {
80 T::regs().ep_mem(index * 4 + 3)
81}
82
83struct EndpointBuffer<T: Instance> {
84 addr: u16,
85 len: u16,
86 _phantom: PhantomData<T>,
87}
88
89impl<T: Instance> EndpointBuffer<T> {
90 fn read(&mut self, buf: &mut [u8]) {
91 assert!(buf.len() <= self.len as usize);
92 for i in 0..((buf.len() + 1) / 2) {
93 let val = unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).read() };
94 buf[i * 2] = val as u8;
95 if i * 2 + 1 < buf.len() {
96 buf[i * 2 + 1] = (val >> 8) as u8;
97 }
98 }
99 }
100
101 fn write(&mut self, buf: &[u8]) {
102 assert!(buf.len() <= self.len as usize);
103 for i in 0..((buf.len() + 1) / 2) {
104 let mut val = buf[i * 2] as u16;
105 if i * 2 + 1 < buf.len() {
106 val |= (buf[i * 2 + 1] as u16) << 8;
107 }
108 unsafe {
109 T::regs()
110 .ep_mem(self.addr as usize / 2 + i)
111 .write_value(val)
112 };
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy)]
118#[cfg_attr(feature = "defmt", derive(defmt::Format))]
119struct EndpointData {
120 ep_type: EndpointType, // only valid if used_in || used_out
121 used_in: bool,
122 used_out: bool,
123}
124
125pub struct Driver<'d, T: Instance> {
126 phantom: PhantomData<&'d mut T>,
127 alloc: [EndpointData; EP_COUNT],
128 ep_mem_free: u16, // first free address in EP mem, in bytes.
129}
130
131impl<'d, T: Instance> Driver<'d, T> {
132 pub fn new(
133 _usb: impl Unborrow<Target = T> + 'd,
134 irq: impl Unborrow<Target = T::Interrupt> + 'd,
135 dp: impl Unborrow<Target = impl DpPin<T>> + 'd,
136 dm: impl Unborrow<Target = impl DmPin<T>> + 'd,
137 ) -> Self {
138 unborrow!(irq, dp, dm);
139 irq.set_handler(Self::on_interrupt);
140 irq.unpend();
141 irq.enable();
142
143 let regs = T::regs();
144
145 #[cfg(stm32l5)]
146 unsafe {
147 crate::peripherals::PWR::enable();
148
149 pac::PWR
150 .cr2()
151 .modify(|w| w.set_usv(pac::pwr::vals::Usv::VALID));
152 }
153
154 unsafe {
155 <T as RccPeripheral>::enable();
156 <T as RccPeripheral>::reset();
157
158 regs.cntr().write(|w| {
159 w.set_pdwn(false);
160 w.set_fres(true);
161 });
162
163 block_for(Duration::from_millis(100));
164
165 regs.btable().write(|w| w.set_btable(0));
166
167 dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
168 dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
169 }
170
171 Self {
172 phantom: PhantomData,
173 alloc: [EndpointData {
174 ep_type: EndpointType::Bulk,
175 used_in: false,
176 used_out: false,
177 }; EP_COUNT],
178 ep_mem_free: EP_COUNT as u16 * 8, // for each EP, 4 regs, so 8 bytes
179 }
180 }
181
182 fn on_interrupt(_: *mut ()) {
183 unsafe {
184 let regs = T::regs();
185 //let x = regs.istr().read().0;
186 //trace!("USB IRQ: {:08x}", x);
187
188 let istr = regs.istr().read();
189
190 let mut flags: u8 = 0;
191
192 if istr.susp() {
193 //trace!("USB IRQ: susp");
194 flags |= IRQ_FLAG_SUSPEND;
195 regs.cntr().modify(|w| {
196 w.set_fsusp(true);
197 w.set_lpmode(true);
198 })
199 }
200
201 if istr.wkup() {
202 //trace!("USB IRQ: wkup");
203 flags |= IRQ_FLAG_RESUME;
204 regs.cntr().modify(|w| {
205 w.set_fsusp(false);
206 w.set_lpmode(false);
207 })
208 }
209
210 if istr.reset() {
211 //trace!("USB IRQ: reset");
212 flags |= IRQ_FLAG_RESET;
213
214 // Write 0 to clear.
215 let mut clear = regs::Istr(!0);
216 clear.set_reset(false);
217 regs.istr().write_value(clear);
218 }
219
220 if flags != 0 {
221 // Send irqs to main thread.
222 IRQ_FLAGS.fetch_or(flags, Ordering::AcqRel);
223 BUS_WAKER.wake();
224
225 // Clear them
226 let mut mask = regs::Istr(0);
227 mask.set_wkup(true);
228 mask.set_susp(true);
229 mask.set_reset(true);
230 regs.istr().write_value(regs::Istr(!(istr.0 & mask.0)));
231 }
232
233 if istr.ctr() {
234 let index = istr.ep_id() as usize;
235 let mut epr = regs.epr(index).read();
236 if epr.ctr_rx() {
237 if index == 0 && epr.setup() {
238 EP0_SETUP.store(true, Ordering::Relaxed);
239 }
240 //trace!("EP {} RX, setup={}", index, epr.setup());
241 EP_OUT_WAKERS[index].wake();
242 }
243 if epr.ctr_tx() {
244 //trace!("EP {} TX", index);
245 EP_IN_WAKERS[index].wake();
246 }
247 epr.set_dtog_rx(false);
248 epr.set_dtog_tx(false);
249 epr.set_stat_rx(Stat(0));
250 epr.set_stat_tx(Stat(0));
251 epr.set_ctr_rx(!epr.ctr_rx());
252 epr.set_ctr_tx(!epr.ctr_tx());
253 regs.epr(index).write_value(epr);
254 }
255 }
256 }
257
258 fn alloc_ep_mem(&mut self, len: u16) -> u16 {
259 let addr = self.ep_mem_free;
260 if addr + len > EP_MEMORY_SIZE as _ {
261 panic!("Endpoint memory full");
262 }
263 self.ep_mem_free += len;
264 addr
265 }
266
267 fn alloc_endpoint<D: Dir>(
268 &mut self,
269 ep_type: EndpointType,
270 max_packet_size: u16,
271 interval: u8,
272 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
273 trace!(
274 "allocating type={:?} mps={:?} interval={}, dir={:?}",
275 ep_type,
276 max_packet_size,
277 interval,
278 D::dir()
279 );
280
281 let index = self.alloc.iter_mut().enumerate().find(|(i, ep)| {
282 if *i == 0 && ep_type != EndpointType::Control {
283 return false; // reserved for control pipe
284 }
285 let used = ep.used_out || ep.used_in;
286 let used_dir = match D::dir() {
287 UsbDirection::Out => ep.used_out,
288 UsbDirection::In => ep.used_in,
289 };
290 !used || (ep.ep_type == ep_type && !used_dir)
291 });
292
293 let (index, ep) = match index {
294 Some(x) => x,
295 None => return Err(EndpointAllocError),
296 };
297
298 ep.ep_type = ep_type;
299
300 let buf = match D::dir() {
301 UsbDirection::Out => {
302 assert!(!ep.used_out);
303 ep.used_out = true;
304
305 let (len, len_bits) = calc_out_len(max_packet_size);
306 let addr = self.alloc_ep_mem(len);
307
308 trace!(" len_bits = {:04x}", len_bits);
309 unsafe {
310 ep_out_addr::<T>(index).write_value(addr);
311 ep_out_len::<T>(index).write_value(len_bits);
312 }
313
314 EndpointBuffer {
315 addr,
316 len,
317 _phantom: PhantomData,
318 }
319 }
320 UsbDirection::In => {
321 assert!(!ep.used_in);
322 ep.used_in = true;
323
324 let len = (max_packet_size + 1) / 2 * 2;
325 let addr = self.alloc_ep_mem(len);
326
327 unsafe {
328 ep_in_addr::<T>(index).write_value(addr);
329 // ep_in_len is written when actually TXing packets.
330 }
331
332 EndpointBuffer {
333 addr,
334 len,
335 _phantom: PhantomData,
336 }
337 }
338 };
339
340 trace!(" index={} addr={} len={}", index, buf.addr, buf.len);
341
342 Ok(Endpoint {
343 _phantom: PhantomData,
344 info: EndpointInfo {
345 addr: EndpointAddress::from_parts(index, D::dir()),
346 ep_type,
347 max_packet_size,
348 interval,
349 },
350 buf,
351 })
352 }
353}
354
355impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
356 type EndpointOut = Endpoint<'d, T, Out>;
357 type EndpointIn = Endpoint<'d, T, In>;
358 type ControlPipe = ControlPipe<'d, T>;
359 type Bus = Bus<'d, T>;
360
361 fn alloc_endpoint_in(
362 &mut self,
363 ep_type: EndpointType,
364 max_packet_size: u16,
365 interval: u8,
366 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
367 self.alloc_endpoint(ep_type, max_packet_size, interval)
368 }
369
370 fn alloc_endpoint_out(
371 &mut self,
372 ep_type: EndpointType,
373 max_packet_size: u16,
374 interval: u8,
375 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
376 self.alloc_endpoint(ep_type, max_packet_size, interval)
377 }
378
379 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
380 let ep_out = self
381 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0)
382 .unwrap();
383 let ep_in = self
384 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0)
385 .unwrap();
386 assert_eq!(ep_out.info.addr.index(), 0);
387 assert_eq!(ep_in.info.addr.index(), 0);
388
389 let regs = T::regs();
390
391 unsafe {
392 regs.cntr().write(|w| {
393 w.set_pdwn(false);
394 w.set_fres(false);
395 w.set_resetm(true);
396 w.set_suspm(true);
397 w.set_wkupm(true);
398 w.set_ctrm(true);
399 });
400
401 #[cfg(usb_v3)]
402 regs.bcdr().write(|w| w.set_dppu(true))
403 }
404
405 trace!("enabled");
406
407 let mut ep_types = [EpType::BULK; EP_COUNT - 1];
408 for i in 1..EP_COUNT {
409 ep_types[i - 1] = convert_type(self.alloc[i].ep_type);
410 }
411
412 (
413 Bus {
414 phantom: PhantomData,
415 ep_types,
416 },
417 ControlPipe {
418 _phantom: PhantomData,
419 max_packet_size: control_max_packet_size,
420 ep_out,
421 ep_in,
422 },
423 )
424 }
425}
426
427pub struct Bus<'d, T: Instance> {
428 phantom: PhantomData<&'d mut T>,
429 ep_types: [EpType; EP_COUNT - 1],
430}
431
432impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
433 type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a;
434
435 fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> {
436 poll_fn(move |cx| unsafe {
437 BUS_WAKER.register(cx.waker());
438 let regs = T::regs();
439
440 let flags = IRQ_FLAGS.load(Ordering::Acquire);
441
442 if flags & IRQ_FLAG_RESUME != 0 {
443 IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
444 return Poll::Ready(Event::Resume);
445 }
446
447 if flags & IRQ_FLAG_RESET != 0 {
448 IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel);
449
450 trace!("RESET REGS WRITINGINGING");
451 regs.daddr().write(|w| {
452 w.set_ef(true);
453 w.set_add(0);
454 });
455
456 regs.epr(0).write(|w| {
457 w.set_ep_type(EpType::CONTROL);
458 w.set_stat_rx(Stat::NAK);
459 w.set_stat_tx(Stat::NAK);
460 });
461
462 for i in 1..EP_COUNT {
463 regs.epr(i).write(|w| {
464 w.set_ea(i as _);
465 w.set_ep_type(self.ep_types[i - 1]);
466 })
467 }
468
469 for w in &EP_IN_WAKERS {
470 w.wake()
471 }
472 for w in &EP_OUT_WAKERS {
473 w.wake()
474 }
475
476 return Poll::Ready(Event::Reset);
477 }
478
479 if flags & IRQ_FLAG_SUSPEND != 0 {
480 IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel);
481 return Poll::Ready(Event::Suspend);
482 }
483
484 Poll::Pending
485 })
486 }
487
488 #[inline]
489 fn set_address(&mut self, addr: u8) {
490 let regs = T::regs();
491 trace!("setting addr: {}", addr);
492 unsafe {
493 regs.daddr().write(|w| {
494 w.set_ef(true);
495 w.set_add(addr);
496 })
497 }
498 }
499
500 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
501 // This can race, so do a retry loop.
502 let reg = T::regs().epr(ep_addr.index() as _);
503 match ep_addr.direction() {
504 UsbDirection::In => {
505 loop {
506 let r = unsafe { reg.read() };
507 match r.stat_tx() {
508 Stat::DISABLED => break, // if disabled, stall does nothing.
509 Stat::STALL => break, // done!
510 _ => {
511 let want_stat = match stalled {
512 false => Stat::NAK,
513 true => Stat::STALL,
514 };
515 let mut w = invariant(r);
516 w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
517 unsafe { reg.write_value(w) };
518 }
519 }
520 }
521 EP_IN_WAKERS[ep_addr.index()].wake();
522 }
523 UsbDirection::Out => {
524 loop {
525 let r = unsafe { reg.read() };
526 match r.stat_rx() {
527 Stat::DISABLED => break, // if disabled, stall does nothing.
528 Stat::STALL => break, // done!
529 _ => {
530 let want_stat = match stalled {
531 false => Stat::VALID,
532 true => Stat::STALL,
533 };
534 let mut w = invariant(r);
535 w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
536 unsafe { reg.write_value(w) };
537 }
538 }
539 }
540 EP_OUT_WAKERS[ep_addr.index()].wake();
541 }
542 }
543 }
544
545 fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
546 let regs = T::regs();
547 let epr = unsafe { regs.epr(ep_addr.index() as _).read() };
548 match ep_addr.direction() {
549 UsbDirection::In => epr.stat_tx() == Stat::STALL,
550 UsbDirection::Out => epr.stat_rx() == Stat::STALL,
551 }
552 }
553
554 fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
555 trace!("set_enabled {:x} {}", ep_addr, enabled);
556 // This can race, so do a retry loop.
557 let reg = T::regs().epr(ep_addr.index() as _);
558 trace!("EPR before: {:04x}", unsafe { reg.read() }.0);
559 match ep_addr.direction() {
560 UsbDirection::In => {
561 loop {
562 let want_stat = match enabled {
563 false => Stat::DISABLED,
564 true => Stat::NAK,
565 };
566 let r = unsafe { reg.read() };
567 if r.stat_tx() == want_stat {
568 break;
569 }
570 let mut w = invariant(r);
571 w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
572 unsafe { reg.write_value(w) };
573 }
574 EP_IN_WAKERS[ep_addr.index()].wake();
575 }
576 UsbDirection::Out => {
577 loop {
578 let want_stat = match enabled {
579 false => Stat::DISABLED,
580 true => Stat::VALID,
581 };
582 let r = unsafe { reg.read() };
583 if r.stat_rx() == want_stat {
584 break;
585 }
586 let mut w = invariant(r);
587 w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
588 unsafe { reg.write_value(w) };
589 }
590 EP_OUT_WAKERS[ep_addr.index()].wake();
591 }
592 }
593 trace!("EPR after: {:04x}", unsafe { reg.read() }.0);
594 }
595
596 type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
597
598 fn enable(&mut self) -> Self::EnableFuture<'_> {
599 async move {}
600 }
601
602 type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
603
604 fn disable(&mut self) -> Self::DisableFuture<'_> {
605 async move {}
606 }
607
608 type RemoteWakeupFuture<'a> = impl Future<Output = Result<(), Unsupported>> + 'a where Self: 'a;
609
610 fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> {
611 async move { Err(Unsupported) }
612 }
613}
614
615trait Dir {
616 fn dir() -> UsbDirection;
617 fn waker(i: usize) -> &'static AtomicWaker;
618}
619
620pub enum In {}
621impl Dir for In {
622 fn dir() -> UsbDirection {
623 UsbDirection::In
624 }
625
626 #[inline]
627 fn waker(i: usize) -> &'static AtomicWaker {
628 &EP_IN_WAKERS[i]
629 }
630}
631
632pub enum Out {}
633impl Dir for Out {
634 fn dir() -> UsbDirection {
635 UsbDirection::Out
636 }
637
638 #[inline]
639 fn waker(i: usize) -> &'static AtomicWaker {
640 &EP_OUT_WAKERS[i]
641 }
642}
643
644pub struct Endpoint<'d, T: Instance, D> {
645 _phantom: PhantomData<(&'d mut T, D)>,
646 info: EndpointInfo,
647 buf: EndpointBuffer<T>,
648}
649
650impl<'d, T: Instance, D> Endpoint<'d, T, D> {
651 fn write_data(&mut self, buf: &[u8]) {
652 let index = self.info.addr.index();
653 self.buf.write(buf);
654 unsafe { ep_in_len::<T>(index).write_value(buf.len() as _) };
655 }
656
657 fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
658 let index = self.info.addr.index();
659 let rx_len = unsafe { ep_out_len::<T>(index).read() as usize } & 0x3FF;
660 trace!("READ DONE, rx_len = {}", rx_len);
661 if rx_len > buf.len() {
662 return Err(EndpointError::BufferOverflow);
663 }
664 self.buf.read(&mut buf[..rx_len]);
665 Ok(rx_len)
666 }
667}
668
669impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
670 fn info(&self) -> &EndpointInfo {
671 &self.info
672 }
673
674 type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
675
676 fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
677 async move {
678 trace!("wait_enabled OUT WAITING");
679 let index = self.info.addr.index();
680 poll_fn(|cx| {
681 EP_OUT_WAKERS[index].register(cx.waker());
682 let regs = T::regs();
683 if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED {
684 Poll::Pending
685 } else {
686 Poll::Ready(())
687 }
688 })
689 .await;
690 trace!("wait_enabled OUT OK");
691 }
692 }
693}
694
695impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> {
696 fn info(&self) -> &EndpointInfo {
697 &self.info
698 }
699
700 type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
701
702 fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
703 async move {
704 trace!("wait_enabled OUT WAITING");
705 let index = self.info.addr.index();
706 poll_fn(|cx| {
707 EP_OUT_WAKERS[index].register(cx.waker());
708 let regs = T::regs();
709 if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED {
710 Poll::Pending
711 } else {
712 Poll::Ready(())
713 }
714 })
715 .await;
716 trace!("wait_enabled OUT OK");
717 }
718 }
719}
720
721impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
722 type ReadFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
723
724 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
725 async move {
726 trace!("READ WAITING, buf.len() = {}", buf.len());
727 let index = self.info.addr.index();
728 let stat = poll_fn(|cx| {
729 EP_OUT_WAKERS[index].register(cx.waker());
730 let regs = T::regs();
731 let stat = unsafe { regs.epr(index).read() }.stat_rx();
732 if matches!(stat, Stat::NAK | Stat::DISABLED) {
733 Poll::Ready(stat)
734 } else {
735 Poll::Pending
736 }
737 })
738 .await;
739
740 if stat == Stat::DISABLED {
741 return Err(EndpointError::Disabled);
742 }
743
744 let rx_len = self.read_data(buf)?;
745
746 let regs = T::regs();
747 unsafe {
748 regs.epr(index).write(|w| {
749 w.set_ep_type(convert_type(self.info.ep_type));
750 w.set_ea(self.info.addr.index() as _);
751 w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
752 w.set_stat_tx(Stat(0));
753 w.set_ctr_rx(true); // don't clear
754 w.set_ctr_tx(true); // don't clear
755 })
756 };
757 trace!("READ OK, rx_len = {}", rx_len);
758
759 Ok(rx_len)
760 }
761 }
762}
763
764impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
765 type WriteFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
766
767 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
768 async move {
769 if buf.len() > self.info.max_packet_size as usize {
770 return Err(EndpointError::BufferOverflow);
771 }
772
773 let index = self.info.addr.index();
774
775 trace!("WRITE WAITING");
776 let stat = poll_fn(|cx| {
777 EP_IN_WAKERS[index].register(cx.waker());
778 let regs = T::regs();
779 let stat = unsafe { regs.epr(index).read() }.stat_tx();
780 if matches!(stat, Stat::NAK | Stat::DISABLED) {
781 Poll::Ready(stat)
782 } else {
783 Poll::Pending
784 }
785 })
786 .await;
787
788 if stat == Stat::DISABLED {
789 return Err(EndpointError::Disabled);
790 }
791
792 self.write_data(buf);
793
794 let regs = T::regs();
795 unsafe {
796 regs.epr(index).write(|w| {
797 w.set_ep_type(convert_type(self.info.ep_type));
798 w.set_ea(self.info.addr.index() as _);
799 w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
800 w.set_stat_rx(Stat(0));
801 w.set_ctr_rx(true); // don't clear
802 w.set_ctr_tx(true); // don't clear
803 })
804 };
805
806 trace!("WRITE OK");
807
808 Ok(())
809 }
810 }
811}
812
813pub struct ControlPipe<'d, T: Instance> {
814 _phantom: PhantomData<&'d mut T>,
815 max_packet_size: u16,
816 ep_in: Endpoint<'d, T, In>,
817 ep_out: Endpoint<'d, T, Out>,
818}
819
820impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
821 type SetupFuture<'a> = impl Future<Output = [u8;8]> + 'a where Self: 'a;
822 type DataOutFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
823 type DataInFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
824 type AcceptFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
825 type RejectFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
826
827 fn max_packet_size(&self) -> usize {
828 usize::from(self.max_packet_size)
829 }
830
831 fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> {
832 async move {
833 loop {
834 trace!("SETUP read waiting");
835 poll_fn(|cx| {
836 EP_OUT_WAKERS[0].register(cx.waker());
837 if EP0_SETUP.load(Ordering::Relaxed) {
838 Poll::Ready(())
839 } else {
840 Poll::Pending
841 }
842 })
843 .await;
844
845 let mut buf = [0; 8];
846 let rx_len = self.ep_out.read_data(&mut buf);
847 if rx_len != Ok(8) {
848 trace!("SETUP read failed: {:?}", rx_len);
849 continue;
850 }
851
852 EP0_SETUP.store(false, Ordering::Relaxed);
853
854 trace!("SETUP read ok");
855 return buf;
856 }
857 }
858 }
859
860 fn data_out<'a>(
861 &'a mut self,
862 buf: &'a mut [u8],
863 first: bool,
864 last: bool,
865 ) -> Self::DataOutFuture<'a> {
866 async move {
867 let regs = T::regs();
868
869 // When a SETUP is received, Stat/Stat is set to NAK.
870 // On first transfer, we must set Stat=VALID, to get the OUT data stage.
871 // We want Stat=STALL so that the host gets a STALL if it switches to the status
872 // stage too soon, except in the last transfer we set Stat=NAK so that it waits
873 // for the status stage, which we will ACK or STALL later.
874 if first || last {
875 let mut stat_rx = 0;
876 let mut stat_tx = 0;
877 if first {
878 // change NAK -> VALID
879 stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0;
880 stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0;
881 }
882 if last {
883 // change STALL -> VALID
884 stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0;
885 }
886 // Note: if this is the first AND last transfer, the above effectively
887 // changes stat_tx like NAK -> NAK, so noop.
888 unsafe {
889 regs.epr(0).write(|w| {
890 w.set_ep_type(EpType::CONTROL);
891 w.set_stat_rx(Stat(stat_rx));
892 w.set_stat_tx(Stat(stat_tx));
893 w.set_ctr_rx(true); // don't clear
894 w.set_ctr_tx(true); // don't clear
895 })
896 }
897 }
898
899 trace!("data_out WAITING, buf.len() = {}", buf.len());
900 poll_fn(|cx| {
901 EP_OUT_WAKERS[0].register(cx.waker());
902 let regs = T::regs();
903 if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK {
904 Poll::Ready(())
905 } else {
906 Poll::Pending
907 }
908 })
909 .await;
910
911 if EP0_SETUP.load(Ordering::Relaxed) {
912 trace!("received another SETUP, aborting data_out.");
913 return Err(EndpointError::Disabled);
914 }
915
916 let rx_len = self.ep_out.read_data(buf)?;
917
918 unsafe {
919 regs.epr(0).write(|w| {
920 w.set_ep_type(EpType::CONTROL);
921 w.set_stat_rx(Stat(match last {
922 // If last, set STAT_RX=STALL.
923 true => Stat::NAK.0 ^ Stat::STALL.0,
924 // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
925 false => Stat::NAK.0 ^ Stat::VALID.0,
926 }));
927 w.set_ctr_rx(true); // don't clear
928 w.set_ctr_tx(true); // don't clear
929 })
930 };
931
932 Ok(rx_len)
933 }
934 }
935
936 fn data_in<'a>(&'a mut self, buf: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a> {
937 async move {
938 trace!("control: data_in");
939
940 if buf.len() > self.ep_in.info.max_packet_size as usize {
941 return Err(EndpointError::BufferOverflow);
942 }
943
944 let regs = T::regs();
945
946 // When a SETUP is received, Stat is set to NAK.
947 // We want it to be STALL in non-last transfers.
948 // We want it to be VALID in last transfer, so the HW does the status stage.
949 if first || last {
950 let mut stat_rx = 0;
951 if first {
952 // change NAK -> STALL
953 stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0;
954 }
955 if last {
956 // change STALL -> VALID
957 stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0;
958 }
959 // Note: if this is the first AND last transfer, the above effectively
960 // does a change of NAK -> VALID.
961 unsafe {
962 regs.epr(0).write(|w| {
963 w.set_ep_type(EpType::CONTROL);
964 w.set_stat_rx(Stat(stat_rx));
965 w.set_ep_kind(last); // set OUT_STATUS if last.
966 w.set_ctr_rx(true); // don't clear
967 w.set_ctr_tx(true); // don't clear
968 })
969 }
970 }
971
972 trace!("WRITE WAITING");
973 poll_fn(|cx| {
974 EP_IN_WAKERS[0].register(cx.waker());
975 EP_OUT_WAKERS[0].register(cx.waker());
976 let regs = T::regs();
977 if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
978 Poll::Ready(())
979 } else {
980 Poll::Pending
981 }
982 })
983 .await;
984
985 if EP0_SETUP.load(Ordering::Relaxed) {
986 trace!("received another SETUP, aborting data_in.");
987 return Err(EndpointError::Disabled);
988 }
989
990 self.ep_in.write_data(buf);
991
992 let regs = T::regs();
993 unsafe {
994 regs.epr(0).write(|w| {
995 w.set_ep_type(EpType::CONTROL);
996 w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
997 w.set_ep_kind(last); // set OUT_STATUS if last.
998 w.set_ctr_rx(true); // don't clear
999 w.set_ctr_tx(true); // don't clear
1000 })
1001 };
1002
1003 trace!("WRITE OK");
1004
1005 Ok(())
1006 }
1007 }
1008
1009 fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> {
1010 async move {
1011 let regs = T::regs();
1012 trace!("control: accept");
1013
1014 self.ep_in.write_data(&[]);
1015
1016 // Set OUT=stall, IN=accept
1017 unsafe {
1018 let epr = regs.epr(0).read();
1019 regs.epr(0).write(|w| {
1020 w.set_ep_type(EpType::CONTROL);
1021 w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
1022 w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0));
1023 w.set_ctr_rx(true); // don't clear
1024 w.set_ctr_tx(true); // don't clear
1025 });
1026 }
1027 trace!("control: accept WAITING");
1028
1029 // Wait is needed, so that we don't set the address too soon, breaking the status stage.
1030 // (embassy-usb sets the address after accept() returns)
1031 poll_fn(|cx| {
1032 EP_IN_WAKERS[0].register(cx.waker());
1033 let regs = T::regs();
1034 if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
1035 Poll::Ready(())
1036 } else {
1037 Poll::Pending
1038 }
1039 })
1040 .await;
1041
1042 trace!("control: accept OK");
1043 }
1044 }
1045
1046 fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> {
1047 async move {
1048 let regs = T::regs();
1049 trace!("control: reject");
1050
1051 // Set IN+OUT to stall
1052 unsafe {
1053 let epr = regs.epr(0).read();
1054 regs.epr(0).write(|w| {
1055 w.set_ep_type(EpType::CONTROL);
1056 w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
1057 w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0));
1058 w.set_ctr_rx(true); // don't clear
1059 w.set_ctr_tx(true); // don't clear
1060 });
1061 }
1062 }
1063 }
1064}
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index e09e17fcd..8de736d6b 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -8,6 +8,8 @@ resolver = "2"
8[dependencies] 8[dependencies]
9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] }
10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } 10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] }
11embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
12embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
11 13
12defmt = "0.3" 14defmt = "0.3"
13defmt-rtt = "0.3" 15defmt-rtt = "0.3"
diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs
new file mode 100644
index 000000000..fe4aa4cc9
--- /dev/null
+++ b/examples/stm32f1/src/bin/usb_serial.rs
@@ -0,0 +1,117 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::panic;
6use defmt::*;
7use defmt_rtt as _; // global logger
8use embassy::executor::Spawner;
9use embassy::time::Duration;
10use embassy::time::Timer;
11use embassy_stm32::gpio::Level;
12use embassy_stm32::gpio::Output;
13use embassy_stm32::gpio::Speed;
14use embassy_stm32::interrupt;
15use embassy_stm32::time::Hertz;
16use embassy_stm32::usb::{Driver, Instance};
17use embassy_stm32::{Config, Peripherals};
18use embassy_usb::driver::EndpointError;
19use embassy_usb::Builder;
20use embassy_usb_serial::{CdcAcmClass, State};
21use futures::future::join;
22use panic_probe as _;
23
24fn config() -> Config {
25 let mut config = Config::default();
26 config.rcc.hse = Some(Hertz(8_000_000));
27 config.rcc.sys_ck = Some(Hertz(48_000_000));
28 config.rcc.pclk1 = Some(Hertz(24_000_000));
29 config
30}
31
32#[embassy::main(config = "config()")]
33async fn main(_spawner: Spawner, mut p: Peripherals) {
34 info!("Hello World!");
35
36 {
37 // BluePill board has a pull-up resistor on the D+ line.
38 // Pull the D+ pin down to send a RESET condition to the USB bus.
39 // This forced reset is needed only for development, without it host
40 // will not reset your device when you upload new firmware.
41 let _dp = Output::new(&mut p.PA12, Level::Low, Speed::Low);
42 Timer::after(Duration::from_millis(10)).await;
43 }
44
45 // Create the driver, from the HAL.
46 let irq = interrupt::take!(USB_LP_CAN1_RX0);
47 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
48
49 // Create embassy-usb Config
50 let config = embassy_usb::Config::new(0xc0de, 0xcafe);
51 //config.max_packet_size_0 = 64;
52
53 // Create embassy-usb DeviceBuilder using the driver and config.
54 // It needs some buffers for building the descriptors.
55 let mut device_descriptor = [0; 256];
56 let mut config_descriptor = [0; 256];
57 let mut bos_descriptor = [0; 256];
58 let mut control_buf = [0; 7];
59
60 let mut state = State::new();
61
62 let mut builder = Builder::new(
63 driver,
64 config,
65 &mut device_descriptor,
66 &mut config_descriptor,
67 &mut bos_descriptor,
68 &mut control_buf,
69 None,
70 );
71
72 // Create classes on the builder.
73 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
74
75 // Build the builder.
76 let mut usb = builder.build();
77
78 // Run the USB device.
79 let usb_fut = usb.run();
80
81 // Do stuff with the class!
82 let echo_fut = async {
83 loop {
84 class.wait_connection().await;
85 info!("Connected");
86 let _ = echo(&mut class).await;
87 info!("Disconnected");
88 }
89 };
90
91 // Run everything concurrently.
92 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
93 join(usb_fut, echo_fut).await;
94}
95
96struct Disconnected {}
97
98impl From<EndpointError> for Disconnected {
99 fn from(val: EndpointError) -> Self {
100 match val {
101 EndpointError::BufferOverflow => panic!("Buffer overflow"),
102 EndpointError::Disabled => Disconnected {},
103 }
104 }
105}
106
107async fn echo<'d, T: Instance + 'd>(
108 class: &mut CdcAcmClass<'d, Driver<'d, T>>,
109) -> Result<(), Disconnected> {
110 let mut buf = [0; 64];
111 loop {
112 let n = class.read_packet(&mut buf).await?;
113 let data = &buf[..n];
114 info!("data: {:x}", data);
115 class.write_packet(data).await?;
116 }
117}
diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml
index eb8a8b335..84b4b2f19 100644
--- a/examples/stm32f3/.cargo/config.toml
+++ b/examples/stm32f3/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` 2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32F303VCTx" 3runner = "probe-run --chip STM32F303ZETx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index de81f1002..15128ecc9 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -7,7 +7,10 @@ resolver = "2"
7 7
8[dependencies] 8[dependencies]
9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] }
10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303vc", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
11embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
12embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
13embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] }
11 14
12defmt = "0.3" 15defmt = "0.3"
13defmt-rtt = "0.3" 16defmt-rtt = "0.3"
diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs
new file mode 100644
index 000000000..fc33d0bc7
--- /dev/null
+++ b/examples/stm32f3/src/bin/usb_serial.rs
@@ -0,0 +1,116 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::panic;
6use defmt::*;
7use defmt_rtt as _; // global logger
8use embassy::executor::Spawner;
9use embassy::time::Duration;
10use embassy::time::Timer;
11use embassy_stm32::gpio::Level;
12use embassy_stm32::gpio::Output;
13use embassy_stm32::gpio::Speed;
14use embassy_stm32::interrupt;
15use embassy_stm32::time::U32Ext;
16use embassy_stm32::usb::{Driver, Instance};
17use embassy_stm32::{Config, Peripherals};
18use embassy_usb::driver::EndpointError;
19use embassy_usb::Builder;
20use embassy_usb_serial::{CdcAcmClass, State};
21use futures::future::join;
22use panic_probe as _;
23
24fn config() -> Config {
25 let mut config = Config::default();
26
27 config.rcc.hse = Some(8.mhz().into());
28 config.rcc.sysclk = Some(48.mhz().into());
29 config.rcc.pclk1 = Some(24.mhz().into());
30 config.rcc.pclk2 = Some(24.mhz().into());
31 config.rcc.pll48 = true;
32
33 config
34}
35
36#[embassy::main(config = "config()")]
37async fn main(_spawner: Spawner, p: Peripherals) {
38 info!("Hello World!");
39
40 // Needed for nucleo-stm32f303ze
41 let mut dp_pullup = Output::new(p.PG6, Level::Low, Speed::Medium);
42 Timer::after(Duration::from_millis(10)).await;
43 dp_pullup.set_high();
44
45 // Create the driver, from the HAL.
46 let irq = interrupt::take!(USB_LP_CAN_RX0);
47 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
48
49 // Create embassy-usb Config
50 let config = embassy_usb::Config::new(0xc0de, 0xcafe);
51
52 // Create embassy-usb DeviceBuilder using the driver and config.
53 // It needs some buffers for building the descriptors.
54 let mut device_descriptor = [0; 256];
55 let mut config_descriptor = [0; 256];
56 let mut bos_descriptor = [0; 256];
57 let mut control_buf = [0; 7];
58
59 let mut state = State::new();
60
61 let mut builder = Builder::new(
62 driver,
63 config,
64 &mut device_descriptor,
65 &mut config_descriptor,
66 &mut bos_descriptor,
67 &mut control_buf,
68 None,
69 );
70
71 // Create classes on the builder.
72 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
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 // Do stuff with the class!
81 let echo_fut = async {
82 loop {
83 class.wait_connection().await;
84 info!("Connected");
85 let _ = echo(&mut class).await;
86 info!("Disconnected");
87 }
88 };
89
90 // Run everything concurrently.
91 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
92 join(usb_fut, echo_fut).await;
93}
94
95struct Disconnected {}
96
97impl From<EndpointError> for Disconnected {
98 fn from(val: EndpointError) -> Self {
99 match val {
100 EndpointError::BufferOverflow => panic!("Buffer overflow"),
101 EndpointError::Disabled => Disconnected {},
102 }
103 }
104}
105
106async fn echo<'d, T: Instance + 'd>(
107 class: &mut CdcAcmClass<'d, Driver<'d, T>>,
108) -> Result<(), Disconnected> {
109 let mut buf = [0; 64];
110 loop {
111 let n = class.read_packet(&mut buf).await?;
112 let data = &buf[..n];
113 info!("data: {:x}", data);
114 class.write_packet(data).await?;
115 }
116}
diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml
new file mode 100644
index 000000000..e63fe37e2
--- /dev/null
+++ b/examples/stm32l5/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L552ZETxQ"
4
5[build]
6target = "thumbv8m.main-none-eabihf"
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
new file mode 100644
index 000000000..7f60e26da
--- /dev/null
+++ b/examples/stm32l5/Cargo.toml
@@ -0,0 +1,30 @@
1[package]
2authors = ["Dario Nieuwenhuis <[email protected]>"]
3edition = "2018"
4name = "embassy-stm32l5-examples"
5version = "0.1.0"
6resolver = "2"
7
8[features]
9
10[dependencies]
11embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] }
12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
15embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] }
16embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] }
17embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
18usbd-hid = "0.5.2"
19
20defmt = "0.3"
21defmt-rtt = "0.3"
22panic-probe = { version = "0.3", features = ["print-defmt"] }
23
24cortex-m = "0.7.3"
25cortex-m-rt = "0.7.0"
26embedded-hal = "0.2.6"
27futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
28heapless = { version = "0.7.5", default-features = false }
29rand_core = { version = "0.6.3", default-features = false }
30embedded-io = { version = "0.3.0", features = ["async"] }
diff --git a/examples/stm32l5/build.rs b/examples/stm32l5/build.rs
new file mode 100644
index 000000000..8cd32d7ed
--- /dev/null
+++ b/examples/stm32l5/build.rs
@@ -0,0 +1,5 @@
1fn main() {
2 println!("cargo:rustc-link-arg-bins=--nmagic");
3 println!("cargo:rustc-link-arg-bins=-Tlink.x");
4 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
5}
diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs
new file mode 100644
index 000000000..304ce0a8a
--- /dev/null
+++ b/examples/stm32l5/src/bin/button_exti.rs
@@ -0,0 +1,28 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use defmt_rtt as _; // global logger
7use embassy::executor::Spawner;
8use embassy_stm32::exti::ExtiInput;
9use embassy_stm32::gpio::{Input, Pull};
10use embassy_stm32::Peripherals;
11use panic_probe as _;
12
13#[embassy::main]
14async fn main(_spawner: Spawner, p: Peripherals) {
15 info!("Hello World!");
16
17 let button = Input::new(p.PC13, Pull::Down);
18 let mut button = ExtiInput::new(button, p.EXTI13);
19
20 info!("Press the USER button...");
21
22 loop {
23 button.wait_for_falling_edge().await;
24 info!("Pressed!");
25 button.wait_for_rising_edge().await;
26 info!("Released!");
27 }
28}
diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs
new file mode 100644
index 000000000..5f75c1ff1
--- /dev/null
+++ b/examples/stm32l5/src/bin/rng.rs
@@ -0,0 +1,34 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use defmt_rtt as _; // global logger
7use embassy::executor::Spawner;
8use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
9use embassy_stm32::rng::Rng;
10use embassy_stm32::{Config, Peripherals};
11use panic_probe as _;
12
13fn config() -> Config {
14 let mut config = Config::default();
15 config.rcc.mux = ClockSrc::PLL(
16 PLLSource::HSI16,
17 PLLClkDiv::Div2,
18 PLLSrcDiv::Div1,
19 PLLMul::Mul8,
20 Some(PLLClkDiv::Div2),
21 );
22 config
23}
24
25#[embassy::main(config = "config()")]
26async fn main(_spawner: Spawner, p: Peripherals) {
27 info!("Hello World!");
28
29 let mut rng = Rng::new(p.RNG);
30
31 let mut buf = [0u8; 16];
32 unwrap!(rng.async_fill_bytes(&mut buf).await);
33 info!("random bytes: {:02x}", buf);
34}
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs
new file mode 100644
index 000000000..fa445eece
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_ethernet.rs
@@ -0,0 +1,290 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use core::sync::atomic::{AtomicBool, Ordering};
7use core::task::Waker;
8use defmt::*;
9use defmt_rtt as _; // global logger
10use embassy::blocking_mutex::raw::ThreadModeRawMutex;
11use embassy::channel::Channel;
12use embassy::executor::Spawner;
13use embassy::util::Forever;
14use embassy_net::tcp::TcpSocket;
15use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources};
16use embassy_stm32::interrupt;
17use embassy_stm32::rcc::*;
18use embassy_stm32::rng::Rng;
19use embassy_stm32::time::Hertz;
20use embassy_stm32::usb::Driver;
21use embassy_stm32::{Config, Peripherals};
22use embassy_usb::{Builder, UsbDevice};
23use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
24use panic_probe as _;
25
26use defmt_rtt as _;
27use embedded_io::asynch::{Read, Write};
28// global logger
29use panic_probe as _;
30use rand_core::RngCore;
31
32type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>;
33
34macro_rules! forever {
35 ($val:expr) => {{
36 type T = impl Sized;
37 static FOREVER: Forever<T> = Forever::new();
38 FOREVER.put_with(move || $val)
39 }};
40}
41
42#[embassy::task]
43async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
44 device.run().await
45}
46
47#[embassy::task]
48async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) {
49 loop {
50 warn!("WAITING for connection");
51 LINK_UP.store(false, Ordering::Relaxed);
52
53 class.wait_connection().await.unwrap();
54
55 warn!("Connected");
56 LINK_UP.store(true, Ordering::Relaxed);
57
58 loop {
59 let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new()));
60 let n = match class.read_packet(&mut p[..]).await {
61 Ok(n) => n,
62 Err(e) => {
63 warn!("error reading packet: {:?}", e);
64 break;
65 }
66 };
67
68 let buf = p.slice(0..n);
69 if RX_CHANNEL.try_send(buf).is_err() {
70 warn!("Failed pushing rx'd packet to channel.");
71 }
72 }
73 }
74}
75
76#[embassy::task]
77async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) {
78 loop {
79 let pkt = TX_CHANNEL.recv().await;
80 if let Err(e) = class.write_packet(&pkt[..]).await {
81 warn!("Failed to TX packet: {:?}", e);
82 }
83 }
84}
85
86#[embassy::task]
87async fn net_task(stack: &'static Stack<Device>) -> ! {
88 stack.run().await
89}
90
91fn config() -> Config {
92 let mut config = Config::default();
93 config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
94
95 config.rcc.mux = ClockSrc::PLL(
96 PLLSource::HSI16,
97 PLLClkDiv::Div2,
98 PLLSrcDiv::Div1,
99 PLLMul::Mul10,
100 None,
101 );
102 config.rcc.hsi48 = true;
103
104 config
105}
106
107#[embassy::main(config = "config()")]
108async fn main(spawner: Spawner, p: Peripherals) {
109 // Create the driver, from the HAL.
110 let irq = interrupt::take!(USB_FS);
111 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
112
113 // Create embassy-usb Config
114 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
115 config.manufacturer = Some("Embassy");
116 config.product = Some("USB-Ethernet example");
117 config.serial_number = Some("12345678");
118 config.max_power = 100;
119 config.max_packet_size_0 = 64;
120
121 // Required for Windows support.
122 config.composite_with_iads = true;
123 config.device_class = 0xEF;
124 config.device_sub_class = 0x02;
125 config.device_protocol = 0x01;
126
127 struct Resources {
128 device_descriptor: [u8; 256],
129 config_descriptor: [u8; 256],
130 bos_descriptor: [u8; 256],
131 control_buf: [u8; 128],
132 serial_state: State<'static>,
133 }
134 let res: &mut Resources = forever!(Resources {
135 device_descriptor: [0; 256],
136 config_descriptor: [0; 256],
137 bos_descriptor: [0; 256],
138 control_buf: [0; 128],
139 serial_state: State::new(),
140 });
141
142 // Create embassy-usb DeviceBuilder using the driver and config.
143 let mut builder = Builder::new(
144 driver,
145 config,
146 &mut res.device_descriptor,
147 &mut res.config_descriptor,
148 &mut res.bos_descriptor,
149 &mut res.control_buf,
150 None,
151 );
152
153 // WARNINGS for Android ethernet tethering:
154 // - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
155 // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
156 // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
157 // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
158 // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
159
160 // Our MAC addr.
161 let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];
162 // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has.
163 let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];
164
165 // Create classes on the builder.
166 let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64);
167
168 // Build the builder.
169 let usb = builder.build();
170
171 unwrap!(spawner.spawn(usb_task(usb)));
172
173 let (tx, rx) = class.split();
174 unwrap!(spawner.spawn(usb_ncm_rx_task(rx)));
175 unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
176
177 let config = embassy_net::ConfigStrategy::Dhcp;
178 //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
179 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
180 // dns_servers: Vec::new(),
181 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
182 //});
183
184 // Generate random seed
185 let mut rng = Rng::new(p.RNG);
186 let seed = rng.next_u64();
187
188 // Init network stack
189 let device = Device {
190 mac_addr: our_mac_addr,
191 };
192 let stack = &*forever!(Stack::new(
193 device,
194 config,
195 forever!(StackResources::<1, 2, 8>::new()),
196 seed
197 ));
198
199 unwrap!(spawner.spawn(net_task(stack)));
200
201 // And now we can use it!
202
203 let mut rx_buffer = [0; 4096];
204 let mut tx_buffer = [0; 4096];
205 let mut buf = [0; 4096];
206
207 loop {
208 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
209 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
210
211 info!("Listening on TCP:1234...");
212 if let Err(e) = socket.accept(1234).await {
213 warn!("accept error: {:?}", e);
214 continue;
215 }
216
217 info!("Received connection from {:?}", socket.remote_endpoint());
218
219 loop {
220 let n = match socket.read(&mut buf).await {
221 Ok(0) => {
222 warn!("read EOF");
223 break;
224 }
225 Ok(n) => n,
226 Err(e) => {
227 warn!("read error: {:?}", e);
228 break;
229 }
230 };
231
232 info!("rxd {:02x}", &buf[..n]);
233
234 match socket.write_all(&buf[..n]).await {
235 Ok(()) => {}
236 Err(e) => {
237 warn!("write error: {:?}", e);
238 break;
239 }
240 };
241 }
242 }
243}
244
245static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
246static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
247static LINK_UP: AtomicBool = AtomicBool::new(false);
248
249struct Device {
250 mac_addr: [u8; 6],
251}
252
253impl embassy_net::Device for Device {
254 fn register_waker(&mut self, waker: &Waker) {
255 // loopy loopy wakey wakey
256 waker.wake_by_ref()
257 }
258
259 fn link_state(&mut self) -> embassy_net::LinkState {
260 match LINK_UP.load(Ordering::Relaxed) {
261 true => embassy_net::LinkState::Up,
262 false => embassy_net::LinkState::Down,
263 }
264 }
265
266 fn capabilities(&self) -> embassy_net::DeviceCapabilities {
267 let mut caps = embassy_net::DeviceCapabilities::default();
268 caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
269 caps.medium = embassy_net::Medium::Ethernet;
270 caps
271 }
272
273 fn is_transmit_ready(&mut self) -> bool {
274 true
275 }
276
277 fn transmit(&mut self, pkt: PacketBuf) {
278 if TX_CHANNEL.try_send(pkt).is_err() {
279 warn!("TX failed")
280 }
281 }
282
283 fn receive<'a>(&mut self) -> Option<PacketBuf> {
284 RX_CHANNEL.try_recv().ok()
285 }
286
287 fn ethernet_address(&self) -> [u8; 6] {
288 self.mac_addr
289 }
290}
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs
new file mode 100644
index 000000000..d275aba36
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs
@@ -0,0 +1,136 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use defmt::*;
7use embassy::executor::Spawner;
8use embassy::time::{Duration, Timer};
9use embassy_stm32::interrupt;
10use embassy_stm32::rcc::*;
11use embassy_stm32::time::Hertz;
12use embassy_stm32::usb::Driver;
13use embassy_stm32::{Config, Peripherals};
14use embassy_usb::control::OutResponse;
15use embassy_usb::Builder;
16use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
17use futures::future::join;
18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
19
20use defmt_rtt as _; // global logger
21use panic_probe as _;
22
23fn config() -> Config {
24 let mut config = Config::default();
25 config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
26
27 config.rcc.mux = ClockSrc::PLL(
28 PLLSource::HSI16,
29 PLLClkDiv::Div2,
30 PLLSrcDiv::Div1,
31 PLLMul::Mul10,
32 None,
33 );
34 config.rcc.hsi48 = true;
35
36 config
37}
38
39#[embassy::main(config = "config()")]
40async fn main(_spawner: Spawner, p: Peripherals) {
41 // Create the driver, from the HAL.
42 let irq = interrupt::take!(USB_FS);
43 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
44
45 // Create embassy-usb Config
46 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
47 config.manufacturer = Some("Embassy");
48 config.product = Some("HID mouse example");
49 config.serial_number = Some("12345678");
50 config.max_power = 100;
51 config.max_packet_size_0 = 64;
52
53 // Create embassy-usb DeviceBuilder using the driver and config.
54 // It needs some buffers for building the descriptors.
55 let mut device_descriptor = [0; 256];
56 let mut config_descriptor = [0; 256];
57 let mut bos_descriptor = [0; 256];
58 let mut control_buf = [0; 64];
59 let request_handler = MyRequestHandler {};
60
61 let mut state = State::new();
62
63 let mut builder = Builder::new(
64 driver,
65 config,
66 &mut device_descriptor,
67 &mut config_descriptor,
68 &mut bos_descriptor,
69 &mut control_buf,
70 None,
71 );
72
73 // Create classes on the builder.
74 let config = embassy_usb_hid::Config {
75 report_descriptor: MouseReport::desc(),
76 request_handler: Some(&request_handler),
77 poll_ms: 60,
78 max_packet_size: 8,
79 };
80
81 let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config);
82
83 // Build the builder.
84 let mut usb = builder.build();
85
86 // Run the USB device.
87 let usb_fut = usb.run();
88
89 // Do stuff with the class!
90 let hid_fut = async {
91 let mut y: i8 = 5;
92 loop {
93 Timer::after(Duration::from_millis(500)).await;
94
95 y = -y;
96 let report = MouseReport {
97 buttons: 0,
98 x: 0,
99 y,
100 wheel: 0,
101 pan: 0,
102 };
103 match writer.write_serialize(&report).await {
104 Ok(()) => {}
105 Err(e) => warn!("Failed to send report: {:?}", e),
106 }
107 }
108 };
109
110 // Run everything concurrently.
111 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
112 join(usb_fut, hid_fut).await;
113}
114
115struct MyRequestHandler {}
116
117impl RequestHandler for MyRequestHandler {
118 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
119 info!("Get report for {:?}", id);
120 None
121 }
122
123 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
124 info!("Set report for {:?}: {=[u8]}", id, data);
125 OutResponse::Accepted
126 }
127
128 fn set_idle(&self, id: Option<ReportId>, dur: Duration) {
129 info!("Set idle rate for {:?} to {:?}", id, dur);
130 }
131
132 fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
133 info!("Get idle rate for {:?}", id);
134 None
135 }
136}
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs
new file mode 100644
index 000000000..987f1b692
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_serial.rs
@@ -0,0 +1,112 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::panic;
6use defmt::*;
7use defmt_rtt as _; // global logger
8use embassy::executor::Spawner;
9use embassy_stm32::interrupt;
10use embassy_stm32::rcc::*;
11use embassy_stm32::time::Hertz;
12use embassy_stm32::usb::{Driver, Instance};
13use embassy_stm32::{Config, Peripherals};
14use embassy_usb::driver::EndpointError;
15use embassy_usb::Builder;
16use embassy_usb_serial::{CdcAcmClass, State};
17use futures::future::join;
18use panic_probe as _;
19
20fn config() -> Config {
21 let mut config = Config::default();
22 config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
23
24 config.rcc.mux = ClockSrc::PLL(
25 PLLSource::HSI16,
26 PLLClkDiv::Div2,
27 PLLSrcDiv::Div1,
28 PLLMul::Mul10,
29 None,
30 );
31 config.rcc.hsi48 = true;
32
33 config
34}
35
36#[embassy::main(config = "config()")]
37async fn main(_spawner: Spawner, p: Peripherals) {
38 info!("Hello World!");
39
40 // Create the driver, from the HAL.
41 let irq = interrupt::take!(USB_FS);
42 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
43
44 // Create embassy-usb Config
45 let config = embassy_usb::Config::new(0xc0de, 0xcafe);
46 //config.max_packet_size_0 = 64;
47
48 // Create embassy-usb DeviceBuilder using the driver and config.
49 // It needs some buffers for building the descriptors.
50 let mut device_descriptor = [0; 256];
51 let mut config_descriptor = [0; 256];
52 let mut bos_descriptor = [0; 256];
53 let mut control_buf = [0; 7];
54
55 let mut state = State::new();
56
57 let mut builder = Builder::new(
58 driver,
59 config,
60 &mut device_descriptor,
61 &mut config_descriptor,
62 &mut bos_descriptor,
63 &mut control_buf,
64 None,
65 );
66
67 // Create classes on the builder.
68 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
69
70 // Build the builder.
71 let mut usb = builder.build();
72
73 // Run the USB device.
74 let usb_fut = usb.run();
75
76 // Do stuff with the class!
77 let echo_fut = async {
78 loop {
79 class.wait_connection().await;
80 info!("Connected");
81 let _ = echo(&mut class).await;
82 info!("Disconnected");
83 }
84 };
85
86 // Run everything concurrently.
87 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
88 join(usb_fut, echo_fut).await;
89}
90
91struct Disconnected {}
92
93impl From<EndpointError> for Disconnected {
94 fn from(val: EndpointError) -> Self {
95 match val {
96 EndpointError::BufferOverflow => panic!("Buffer overflow"),
97 EndpointError::Disabled => Disconnected {},
98 }
99 }
100}
101
102async fn echo<'d, T: Instance + 'd>(
103 class: &mut CdcAcmClass<'d, Driver<'d, T>>,
104) -> Result<(), Disconnected> {
105 let mut buf = [0; 64];
106 loop {
107 let n = class.read_packet(&mut buf).await?;
108 let data = &buf[..n];
109 info!("data: {:x}", data);
110 class.write_packet(data).await?;
111 }
112}
diff --git a/stm32-data b/stm32-data
Subproject aa2e63996c0fe35d680c9c48917c07b042905e4 Subproject fa294eae79c0f33f4cde1e73b4e69db59f7429e