From 7d34f4f538f88d84d499173e15f63e676d4af9c0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:37:02 +0200 Subject: [PATCH] stm32/usb_otg: Add F4 usb_ethernet example --- examples/stm32f4/Cargo.toml | 6 +- examples/stm32f4/src/bin/usb_ethernet.rs | 169 +++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f4/src/bin/usb_ethernet.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 252d6085..e2b17bfc 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -4,13 +4,13 @@ name = "embassy-stm32f4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" - [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -27,5 +27,9 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" +[[bin]] +name = "usb_ethernet" +required-features = ["embassy-net"] + [profile.release] debug = 2 diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs new file mode 100644 index 00000000..cf2885ae --- /dev/null +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -0,0 +1,169 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::Driver; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; +use embassy_usb::{Builder, UsbDevice}; +use embedded_io::asynch::Write; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +const MTU: usize = 1514; + +#[embassy_executor::task] +async fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! { + device.run().await +} + +#[embassy_executor::task] +async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { + class.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(48)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let ep_out_buffer = &mut singleton!([0; 256])[..]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, ep_out_buffer); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + let mut builder = Builder::new( + driver, + config, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + None, + ); + + // 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, singleton!(State::new()), host_mac_addr, 64); + + // Build the builder. + let usb = builder.build(); + + unwrap!(spawner.spawn(usb_task(usb))); + + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); + + 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 mut seed = [0; 8]; + unwrap!(rng.async_fill_bytes(&mut seed).await); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + config, + singleton!(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; + } + }; + } + } +}