summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2022-05-30 00:36:30 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2022-06-07 03:29:00 +0200
commit3e4bead32161604c08e2dcae1acea695db851f34 (patch)
tree41b0334cad6fef5a54e28789ec0320f21000b2ac /examples
parent0aa73f58e2f71f4578ff23f79f3b1a2c9d6d9098 (diff)
downloadembassy-3e4bead32161604c08e2dcae1acea695db851f34.zip
stm32: add USB driver.
Diffstat (limited to 'examples')
-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
13 files changed, 881 insertions, 2 deletions
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index e09e17fc..8de736d6 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -8,6 +8,8 @@ resolver = "2"
[dependencies]
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] }
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
+embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
defmt = "0.3"
defmt-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 00000000..fe4aa4cc
--- /dev/null
+++ b/examples/stm32f1/src/bin/usb_serial.rs
@@ -0,0 +1,117 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::panic;
+use defmt::*;
+use defmt_rtt as _; // global logger
+use embassy::executor::Spawner;
+use embassy::time::Duration;
+use embassy::time::Timer;
+use embassy_stm32::gpio::Level;
+use embassy_stm32::gpio::Output;
+use embassy_stm32::gpio::Speed;
+use embassy_stm32::interrupt;
+use embassy_stm32::time::Hertz;
+use embassy_stm32::usb::{Driver, Instance};
+use embassy_stm32::{Config, Peripherals};
+use embassy_usb::driver::EndpointError;
+use embassy_usb::Builder;
+use embassy_usb_serial::{CdcAcmClass, State};
+use futures::future::join;
+use panic_probe as _;
+
+fn config() -> Config {
+ let mut config = Config::default();
+ config.rcc.hse = Some(Hertz(8_000_000));
+ config.rcc.sys_ck = Some(Hertz(48_000_000));
+ config.rcc.pclk1 = Some(Hertz(24_000_000));
+ config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(_spawner: Spawner, mut p: Peripherals) {
+ info!("Hello World!");
+
+ {
+ // BluePill board has a pull-up resistor on the D+ line.
+ // Pull the D+ pin down to send a RESET condition to the USB bus.
+ // This forced reset is needed only for development, without it host
+ // will not reset your device when you upload new firmware.
+ let _dp = Output::new(&mut p.PA12, Level::Low, Speed::Low);
+ Timer::after(Duration::from_millis(10)).await;
+ }
+
+ // Create the driver, from the HAL.
+ let irq = interrupt::take!(USB_LP_CAN1_RX0);
+ let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
+
+ // Create embassy-usb Config
+ let config = embassy_usb::Config::new(0xc0de, 0xcafe);
+ //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];
+ let mut control_buf = [0; 7];
+
+ let mut state = State::new();
+
+ let mut builder = Builder::new(
+ driver,
+ config,
+ &mut device_descriptor,
+ &mut config_descriptor,
+ &mut bos_descriptor,
+ &mut control_buf,
+ None,
+ );
+
+ // Create classes on the builder.
+ let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
+
+ // Build the builder.
+ let mut usb = builder.build();
+
+ // Run the USB device.
+ let usb_fut = usb.run();
+
+ // Do stuff with the class!
+ let echo_fut = async {
+ loop {
+ class.wait_connection().await;
+ info!("Connected");
+ let _ = echo(&mut class).await;
+ info!("Disconnected");
+ }
+ };
+
+ // Run everything concurrently.
+ // If we had made everything `'static` above instead, we could do this using separate tasks instead.
+ join(usb_fut, echo_fut).await;
+}
+
+struct Disconnected {}
+
+impl From<EndpointError> for Disconnected {
+ fn from(val: EndpointError) -> Self {
+ match val {
+ EndpointError::BufferOverflow => panic!("Buffer overflow"),
+ EndpointError::Disabled => Disconnected {},
+ }
+ }
+}
+
+async fn echo<'d, T: Instance + 'd>(
+ class: &mut CdcAcmClass<'d, Driver<'d, T>>,
+) -> Result<(), Disconnected> {
+ let mut buf = [0; 64];
+ loop {
+ let n = class.read_packet(&mut buf).await?;
+ let data = &buf[..n];
+ info!("data: {:x}", data);
+ class.write_packet(data).await?;
+ }
+}
diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml
index eb8a8b33..84b4b2f1 100644
--- a/examples/stm32f3/.cargo/config.toml
+++ b/examples/stm32f3/.cargo/config.toml
@@ -1,6 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
-runner = "probe-run --chip STM32F303VCTx"
+runner = "probe-run --chip STM32F303ZETx"
[build]
target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index de81f100..15128ecc 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -7,7 +7,10 @@ resolver = "2"
[dependencies]
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] }
-embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303vc", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
+embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
+embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] }
defmt = "0.3"
defmt-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 00000000..fc33d0bc
--- /dev/null
+++ b/examples/stm32f3/src/bin/usb_serial.rs
@@ -0,0 +1,116 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::panic;
+use defmt::*;
+use defmt_rtt as _; // global logger
+use embassy::executor::Spawner;
+use embassy::time::Duration;
+use embassy::time::Timer;
+use embassy_stm32::gpio::Level;
+use embassy_stm32::gpio::Output;
+use embassy_stm32::gpio::Speed;
+use embassy_stm32::interrupt;
+use embassy_stm32::time::U32Ext;
+use embassy_stm32::usb::{Driver, Instance};
+use embassy_stm32::{Config, Peripherals};
+use embassy_usb::driver::EndpointError;
+use embassy_usb::Builder;
+use embassy_usb_serial::{CdcAcmClass, State};
+use futures::future::join;
+use panic_probe as _;
+
+fn config() -> Config {
+ let mut config = Config::default();
+
+ config.rcc.hse = Some(8.mhz().into());
+ config.rcc.sysclk = Some(48.mhz().into());
+ config.rcc.pclk1 = Some(24.mhz().into());
+ config.rcc.pclk2 = Some(24.mhz().into());
+ config.rcc.pll48 = true;
+
+ config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ info!("Hello World!");
+
+ // Needed for nucleo-stm32f303ze
+ let mut dp_pullup = Output::new(p.PG6, Level::Low, Speed::Medium);
+ Timer::after(Duration::from_millis(10)).await;
+ dp_pullup.set_high();
+
+ // Create the driver, from the HAL.
+ let irq = interrupt::take!(USB_LP_CAN_RX0);
+ let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
+
+ // Create embassy-usb Config
+ let config = embassy_usb::Config::new(0xc0de, 0xcafe);
+
+ // 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];
+ let mut control_buf = [0; 7];
+
+ let mut state = State::new();
+
+ let mut builder = Builder::new(
+ driver,
+ config,
+ &mut device_descriptor,
+ &mut config_descriptor,
+ &mut bos_descriptor,
+ &mut control_buf,
+ None,
+ );
+
+ // Create classes on the builder.
+ let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
+
+ // Build the builder.
+ let mut usb = builder.build();
+
+ // Run the USB device.
+ let usb_fut = usb.run();
+
+ // Do stuff with the class!
+ let echo_fut = async {
+ loop {
+ class.wait_connection().await;
+ info!("Connected");
+ let _ = echo(&mut class).await;
+ info!("Disconnected");
+ }
+ };
+
+ // Run everything concurrently.
+ // If we had made everything `'static` above instead, we could do this using separate tasks instead.
+ join(usb_fut, echo_fut).await;
+}
+
+struct Disconnected {}
+
+impl From<EndpointError> for Disconnected {
+ fn from(val: EndpointError) -> Self {
+ match val {
+ EndpointError::BufferOverflow => panic!("Buffer overflow"),
+ EndpointError::Disabled => Disconnected {},
+ }
+ }
+}
+
+async fn echo<'d, T: Instance + 'd>(
+ class: &mut CdcAcmClass<'d, Driver<'d, T>>,
+) -> Result<(), Disconnected> {
+ let mut buf = [0; 64];
+ loop {
+ let n = class.read_packet(&mut buf).await?;
+ let data = &buf[..n];
+ info!("data: {:x}", data);
+ class.write_packet(data).await?;
+ }
+}
diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml
new file mode 100644
index 00000000..e63fe37e
--- /dev/null
+++ b/examples/stm32l5/.cargo/config.toml
@@ -0,0 +1,6 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
+runner = "probe-run --chip STM32L552ZETxQ"
+
+[build]
+target = "thumbv8m.main-none-eabihf"
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
new file mode 100644
index 00000000..7f60e26d
--- /dev/null
+++ b/examples/stm32l5/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
+edition = "2018"
+name = "embassy-stm32l5-examples"
+version = "0.1.0"
+resolver = "2"
+
+[features]
+
+[dependencies]
+embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
+embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
+embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] }
+embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] }
+embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
+usbd-hid = "0.5.2"
+
+defmt = "0.3"
+defmt-rtt = "0.3"
+panic-probe = { version = "0.3", features = ["print-defmt"] }
+
+cortex-m = "0.7.3"
+cortex-m-rt = "0.7.0"
+embedded-hal = "0.2.6"
+futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
+heapless = { version = "0.7.5", default-features = false }
+rand_core = { version = "0.6.3", default-features = false }
+embedded-io = { version = "0.3.0", features = ["async"] }
diff --git a/examples/stm32l5/build.rs b/examples/stm32l5/build.rs
new file mode 100644
index 00000000..8cd32d7e
--- /dev/null
+++ b/examples/stm32l5/build.rs
@@ -0,0 +1,5 @@
+fn main() {
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs
new file mode 100644
index 00000000..304ce0a8
--- /dev/null
+++ b/examples/stm32l5/src/bin/button_exti.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::*;
+use defmt_rtt as _; // global logger
+use embassy::executor::Spawner;
+use embassy_stm32::exti::ExtiInput;
+use embassy_stm32::gpio::{Input, Pull};
+use embassy_stm32::Peripherals;
+use panic_probe as _;
+
+#[embassy::main]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ info!("Hello World!");
+
+ let button = Input::new(p.PC13, Pull::Down);
+ let mut button = ExtiInput::new(button, p.EXTI13);
+
+ info!("Press the USER button...");
+
+ loop {
+ button.wait_for_falling_edge().await;
+ info!("Pressed!");
+ button.wait_for_rising_edge().await;
+ info!("Released!");
+ }
+}
diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs
new file mode 100644
index 00000000..5f75c1ff
--- /dev/null
+++ b/examples/stm32l5/src/bin/rng.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::*;
+use defmt_rtt as _; // global logger
+use embassy::executor::Spawner;
+use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
+use embassy_stm32::rng::Rng;
+use embassy_stm32::{Config, Peripherals};
+use panic_probe as _;
+
+fn config() -> Config {
+ let mut config = Config::default();
+ config.rcc.mux = ClockSrc::PLL(
+ PLLSource::HSI16,
+ PLLClkDiv::Div2,
+ PLLSrcDiv::Div1,
+ PLLMul::Mul8,
+ Some(PLLClkDiv::Div2),
+ );
+ config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ info!("Hello World!");
+
+ let mut rng = Rng::new(p.RNG);
+
+ let mut buf = [0u8; 16];
+ unwrap!(rng.async_fill_bytes(&mut buf).await);
+ info!("random bytes: {:02x}", buf);
+}
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs
new file mode 100644
index 00000000..fa445eec
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_ethernet.rs
@@ -0,0 +1,290 @@
+#![no_std]
+#![no_main]
+#![feature(generic_associated_types)]
+#![feature(type_alias_impl_trait)]
+
+use core::sync::atomic::{AtomicBool, Ordering};
+use core::task::Waker;
+use defmt::*;
+use defmt_rtt as _; // global logger
+use embassy::blocking_mutex::raw::ThreadModeRawMutex;
+use embassy::channel::Channel;
+use embassy::executor::Spawner;
+use embassy::util::Forever;
+use embassy_net::tcp::TcpSocket;
+use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources};
+use embassy_stm32::interrupt;
+use embassy_stm32::rcc::*;
+use embassy_stm32::rng::Rng;
+use embassy_stm32::time::Hertz;
+use embassy_stm32::usb::Driver;
+use embassy_stm32::{Config, Peripherals};
+use embassy_usb::{Builder, UsbDevice};
+use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
+use panic_probe as _;
+
+use defmt_rtt as _;
+use embedded_io::asynch::{Read, Write};
+// global logger
+use panic_probe as _;
+use rand_core::RngCore;
+
+type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>;
+
+macro_rules! forever {
+ ($val:expr) => {{
+ type T = impl Sized;
+ static FOREVER: Forever<T> = Forever::new();
+ FOREVER.put_with(move || $val)
+ }};
+}
+
+#[embassy::task]
+async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
+ device.run().await
+}
+
+#[embassy::task]
+async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) {
+ loop {
+ warn!("WAITING for connection");
+ LINK_UP.store(false, Ordering::Relaxed);
+
+ class.wait_connection().await.unwrap();
+
+ warn!("Connected");
+ LINK_UP.store(true, Ordering::Relaxed);
+
+ loop {
+ let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new()));
+ let n = match class.read_packet(&mut p[..]).await {
+ Ok(n) => n,
+ Err(e) => {
+ warn!("error reading packet: {:?}", e);
+ break;
+ }
+ };
+
+ let buf = p.slice(0..n);
+ if RX_CHANNEL.try_send(buf).is_err() {
+ warn!("Failed pushing rx'd packet to channel.");
+ }
+ }
+ }
+}
+
+#[embassy::task]
+async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) {
+ loop {
+ let pkt = TX_CHANNEL.recv().await;
+ if let Err(e) = class.write_packet(&pkt[..]).await {
+ warn!("Failed to TX packet: {:?}", e);
+ }
+ }
+}
+
+#[embassy::task]
+async fn net_task(stack: &'static Stack<Device>) -> ! {
+ stack.run().await
+}
+
+fn config() -> Config {
+ let mut config = Config::default();
+ config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
+
+ config.rcc.mux = ClockSrc::PLL(
+ PLLSource::HSI16,
+ PLLClkDiv::Div2,
+ PLLSrcDiv::Div1,
+ PLLMul::Mul10,
+ None,
+ );
+ config.rcc.hsi48 = true;
+
+ config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(spawner: Spawner, p: Peripherals) {
+ // Create the driver, from the HAL.
+ let irq = interrupt::take!(USB_FS);
+ let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
+
+ // Create embassy-usb Config
+ let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
+ config.manufacturer = Some("Embassy");
+ config.product = Some("USB-Ethernet example");
+ config.serial_number = Some("12345678");
+ config.max_power = 100;
+ config.max_packet_size_0 = 64;
+
+ // Required for Windows support.
+ config.composite_with_iads = true;
+ config.device_class = 0xEF;
+ config.device_sub_class = 0x02;
+ config.device_protocol = 0x01;
+
+ struct Resources {
+ device_descriptor: [u8; 256],
+ config_descriptor: [u8; 256],
+ bos_descriptor: [u8; 256],
+ control_buf: [u8; 128],
+ serial_state: State<'static>,
+ }
+ let res: &mut Resources = forever!(Resources {
+ device_descriptor: [0; 256],
+ config_descriptor: [0; 256],
+ bos_descriptor: [0; 256],
+ control_buf: [0; 128],
+ serial_state: State::new(),
+ });
+
+ // Create embassy-usb DeviceBuilder using the driver and config.
+ let mut builder = Builder::new(
+ driver,
+ config,
+ &mut res.device_descriptor,
+ &mut res.config_descriptor,
+ &mut res.bos_descriptor,
+ &mut res.control_buf,
+ None,
+ );
+
+ // WARNINGS for Android ethernet tethering:
+ // - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
+ // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
+ // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
+ // 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
+ // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
+
+ // Our MAC addr.
+ let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];
+ // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has.
+ let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];
+
+ // Create classes on the builder.
+ let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64);
+
+ // Build the builder.
+ let usb = builder.build();
+
+ unwrap!(spawner.spawn(usb_task(usb)));
+
+ let (tx, rx) = class.split();
+ unwrap!(spawner.spawn(usb_ncm_rx_task(rx)));
+ unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
+
+ let config = embassy_net::ConfigStrategy::Dhcp;
+ //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
+ // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
+ // dns_servers: Vec::new(),
+ // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
+ //});
+
+ // Generate random seed
+ let mut rng = Rng::new(p.RNG);
+ let seed = rng.next_u64();
+
+ // Init network stack
+ let device = Device {
+ mac_addr: our_mac_addr,
+ };
+ let stack = &*forever!(Stack::new(
+ device,
+ config,
+ forever!(StackResources::<1, 2, 8>::new()),
+ seed
+ ));
+
+ unwrap!(spawner.spawn(net_task(stack)));
+
+ // And now we can use it!
+
+ let mut rx_buffer = [0; 4096];
+ let mut tx_buffer = [0; 4096];
+ let mut buf = [0; 4096];
+
+ loop {
+ let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
+ socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
+
+ info!("Listening on TCP:1234...");
+ if let Err(e) = socket.accept(1234).await {
+ warn!("accept error: {:?}", e);
+ continue;
+ }
+
+ info!("Received connection from {:?}", socket.remote_endpoint());
+
+ loop {
+ let n = match socket.read(&mut buf).await {
+ Ok(0) => {
+ warn!("read EOF");
+ break;
+ }
+ Ok(n) => n,
+ Err(e) => {
+ warn!("read error: {:?}", e);
+ break;
+ }
+ };
+
+ info!("rxd {:02x}", &buf[..n]);
+
+ match socket.write_all(&buf[..n]).await {
+ Ok(()) => {}
+ Err(e) => {
+ warn!("write error: {:?}", e);
+ break;
+ }
+ };
+ }
+ }
+}
+
+static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
+static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
+static LINK_UP: AtomicBool = AtomicBool::new(false);
+
+struct Device {
+ mac_addr: [u8; 6],
+}
+
+impl embassy_net::Device for Device {
+ fn register_waker(&mut self, waker: &Waker) {
+ // loopy loopy wakey wakey
+ waker.wake_by_ref()
+ }
+
+ fn link_state(&mut self) -> embassy_net::LinkState {
+ match LINK_UP.load(Ordering::Relaxed) {
+ true => embassy_net::LinkState::Up,
+ false => embassy_net::LinkState::Down,
+ }
+ }
+
+ fn capabilities(&self) -> embassy_net::DeviceCapabilities {
+ let mut caps = embassy_net::DeviceCapabilities::default();
+ caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
+ caps.medium = embassy_net::Medium::Ethernet;
+ caps
+ }
+
+ fn is_transmit_ready(&mut self) -> bool {
+ true
+ }
+
+ fn transmit(&mut self, pkt: PacketBuf) {
+ if TX_CHANNEL.try_send(pkt).is_err() {
+ warn!("TX failed")
+ }
+ }
+
+ fn receive<'a>(&mut self) -> Option<PacketBuf> {
+ RX_CHANNEL.try_recv().ok()
+ }
+
+ fn ethernet_address(&self) -> [u8; 6] {
+ self.mac_addr
+ }
+}
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 00000000..d275aba3
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs
@@ -0,0 +1,136 @@
+#![no_std]
+#![no_main]
+#![feature(generic_associated_types)]
+#![feature(type_alias_impl_trait)]
+
+use defmt::*;
+use embassy::executor::Spawner;
+use embassy::time::{Duration, Timer};
+use embassy_stm32::interrupt;
+use embassy_stm32::rcc::*;
+use embassy_stm32::time::Hertz;
+use embassy_stm32::usb::Driver;
+use embassy_stm32::{Config, Peripherals};
+use embassy_usb::control::OutResponse;
+use embassy_usb::Builder;
+use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
+use futures::future::join;
+use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
+
+use defmt_rtt as _; // global logger
+use panic_probe as _;
+
+fn config() -> Config {
+ let mut config = Config::default();
+ config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
+
+ config.rcc.mux = ClockSrc::PLL(
+ PLLSource::HSI16,
+ PLLClkDiv::Div2,
+ PLLSrcDiv::Div1,
+ PLLMul::Mul10,
+ None,
+ );
+ config.rcc.hsi48 = true;
+
+ config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ // Create the driver, from the HAL.
+ let irq = interrupt::take!(USB_FS);
+ let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
+
+ // Create embassy-usb Config
+ let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
+ config.manufacturer = Some("Embassy");
+ config.product = Some("HID mouse 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];
+ let mut control_buf = [0; 64];
+ let request_handler = MyRequestHandler {};
+
+ let mut state = State::new();
+
+ let mut builder = Builder::new(
+ driver,
+ config,
+ &mut device_descriptor,
+ &mut config_descriptor,
+ &mut bos_descriptor,
+ &mut control_buf,
+ None,
+ );
+
+ // Create classes on the builder.
+ let config = embassy_usb_hid::Config {
+ report_descriptor: MouseReport::desc(),
+ request_handler: Some(&request_handler),
+ poll_ms: 60,
+ max_packet_size: 8,
+ };
+
+ let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config);
+
+ // Build the builder.
+ let mut usb = builder.build();
+
+ // Run the USB device.
+ let usb_fut = usb.run();
+
+ // Do stuff with the class!
+ let hid_fut = async {
+ let mut y: i8 = 5;
+ loop {
+ Timer::after(Duration::from_millis(500)).await;
+
+ y = -y;
+ let report = MouseReport {
+ buttons: 0,
+ x: 0,
+ y,
+ wheel: 0,
+ pan: 0,
+ };
+ match writer.write_serialize(&report).await {
+ Ok(()) => {}
+ Err(e) => warn!("Failed to send report: {:?}", e),
+ }
+ }
+ };
+
+ // Run everything concurrently.
+ // If we had made everything `'static` above instead, we could do this using separate tasks instead.
+ join(usb_fut, hid_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(&self, id: Option<ReportId>, dur: Duration) {
+ info!("Set idle rate for {:?} to {:?}", id, dur);
+ }
+
+ fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
+ info!("Get idle rate for {:?}", id);
+ None
+ }
+}
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs
new file mode 100644
index 00000000..987f1b69
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_serial.rs
@@ -0,0 +1,112 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::panic;
+use defmt::*;
+use defmt_rtt as _; // global logger
+use embassy::executor::Spawner;
+use embassy_stm32::interrupt;
+use embassy_stm32::rcc::*;
+use embassy_stm32::time::Hertz;
+use embassy_stm32::usb::{Driver, Instance};
+use embassy_stm32::{Config, Peripherals};
+use embassy_usb::driver::EndpointError;
+use embassy_usb::Builder;
+use embassy_usb_serial::{CdcAcmClass, State};
+use futures::future::join;
+use panic_probe as _;
+
+fn config() -> Config {
+ let mut config = Config::default();
+ config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
+
+ config.rcc.mux = ClockSrc::PLL(
+ PLLSource::HSI16,
+ PLLClkDiv::Div2,
+ PLLSrcDiv::Div1,
+ PLLMul::Mul10,
+ None,
+ );
+ config.rcc.hsi48 = true;
+
+ config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ info!("Hello World!");
+
+ // Create the driver, from the HAL.
+ let irq = interrupt::take!(USB_FS);
+ let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
+
+ // Create embassy-usb Config
+ let config = embassy_usb::Config::new(0xc0de, 0xcafe);
+ //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];
+ let mut control_buf = [0; 7];
+
+ let mut state = State::new();
+
+ let mut builder = Builder::new(
+ driver,
+ config,
+ &mut device_descriptor,
+ &mut config_descriptor,
+ &mut bos_descriptor,
+ &mut control_buf,
+ None,
+ );
+
+ // Create classes on the builder.
+ let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
+
+ // Build the builder.
+ let mut usb = builder.build();
+
+ // Run the USB device.
+ let usb_fut = usb.run();
+
+ // Do stuff with the class!
+ let echo_fut = async {
+ loop {
+ class.wait_connection().await;
+ info!("Connected");
+ let _ = echo(&mut class).await;
+ info!("Disconnected");
+ }
+ };
+
+ // Run everything concurrently.
+ // If we had made everything `'static` above instead, we could do this using separate tasks instead.
+ join(usb_fut, echo_fut).await;
+}
+
+struct Disconnected {}
+
+impl From<EndpointError> for Disconnected {
+ fn from(val: EndpointError) -> Self {
+ match val {
+ EndpointError::BufferOverflow => panic!("Buffer overflow"),
+ EndpointError::Disabled => Disconnected {},
+ }
+ }
+}
+
+async fn echo<'d, T: Instance + 'd>(
+ class: &mut CdcAcmClass<'d, Driver<'d, T>>,
+) -> Result<(), Disconnected> {
+ let mut buf = [0; 64];
+ loop {
+ let n = class.read_packet(&mut buf).await?;
+ let data = &buf[..n];
+ info!("data: {:x}", data);
+ class.write_packet(data).await?;
+ }
+}