From 72c2b06520a2e25e180df6d89d5b3b39a8380f0c Mon Sep 17 00:00:00 2001 From: dvdsk <noreply@davidsk.dev> Date: Fri, 7 Jun 2024 23:06:30 +0200 Subject: [PATCH 1/3] Explain the const params N_RX & N_TX in the docs of State On chips with a low amount of ram it's easy to run out of ram. When looking at the current docs for the State struct it is not easy to see that these params can cause a lot of ram usage. --- embassy-net-wiznet/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-net-wiznet/src/lib.rs b/embassy-net-wiznet/src/lib.rs index da70d22bd..545b2a2e4 100644 --- a/embassy-net-wiznet/src/lib.rs +++ b/embassy-net-wiznet/src/lib.rs @@ -17,12 +17,22 @@ use embedded_hal_async::spi::SpiDevice; use crate::chip::Chip; use crate::device::WiznetDevice; +// If you change this update the docs of State const MTU: usize = 1514; /// Type alias for the embassy-net driver. pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; /// Internal state for the embassy-net integration. +/// +/// The two generic arguments `N_RX` and `N_TX` set the size of the receive and +/// send packet queue. With a the ethernet MTU of _1514_ this takes up `N_RX + +/// NTX * 1514` bytes. While setting these both to 1 is the minimum this might +/// hurt performance as a packet can not be received while processing another. +/// +/// # Warning +/// On devices with a small amount of ram (think ~64k) watch out with the size +/// of there parameters. They will quickly use too much RAM. pub struct State<const N_RX: usize, const N_TX: usize> { ch_state: ch::State<MTU, N_RX, N_TX>, } From 1812ccd276a6c277e7c99191c0aa5ce3d1c90e84 Mon Sep 17 00:00:00 2001 From: dvdsk <noreply@davidsk.dev> Date: Fri, 7 Jun 2024 23:46:59 +0200 Subject: [PATCH 2/3] Adds an example for using the w5500 with an stm32f4 This example takes into account the lower memory on the stm32f4. That should prevent anyone wanting to use the w5500 on any stm from adapting the w5500 example for the rp which uses a lot more RAM. --- examples/stm32f4/Cargo.toml | 2 + examples/stm32f4/src/bin/eth_w5500.rs | 139 ++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 examples/stm32f4/src/bin/eth_w5500.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 52b7ce9e8..11cb4cb3c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -12,6 +12,7 @@ embassy-executor = { version = "0.5.0", path = "../../embassy-executor", feature embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.2.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } +embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" @@ -20,6 +21,7 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" +embedded-hal-bus = { version = "0.2", features = ["async"] } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs new file mode 100644 index 000000000..827e74c51 --- /dev/null +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net_wiznet::{chip::W5500, Device, Runner, State}; +use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::rng::Rng; +use embassy_stm32::spi::Spi; +use embassy_stm32::mode::Async; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, peripherals, rng, spi, Config}; +use embassy_time::{Timer, Delay}; +use embedded_io_async::Write; +use embedded_hal_bus::spi::ExclusiveDevice; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + HASH_RNG => rng::InterruptHandler<peripherals::RNG>; +}); + +type EthernetSPI = ExclusiveDevice<Spi<'static, Async>, Output<'static>, Delay>; +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'static>, Output<'static>>, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL180, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + unwrap!(rng.async_fill_bytes(&mut seed).await); + let seed = u64::from_le_bytes(seed); + + let mut spi_cfg = spi::Config::default(); + spi_cfg.frequency = Hertz(50_000_000); // up to 50m works + let (miso, mosi, clk) = (p.PA6, p.PA7, p.PA5); + let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA2_CH3, p.DMA2_CH0, spi_cfg); + let cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); + let spi = unwrap!(ExclusiveDevice::new(spi, cs, Delay)); + + let w5500_int = ExtiInput::new(p.PB0, p.EXTI0, Pull::Up); + let w5500_reset = Output::new(p.PB1, Level::High, Speed::VeryHigh); + + let mac_addr = [0x02, 234, 3, 4, 82, 231]; + static STATE: StaticCell<State<2, 2>> = StaticCell::new(); + let state = STATE.init(State::<2, 2>::new()); + let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset).await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + static STACK: StaticCell<Stack<Device>> = StaticCell::new(); + static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); + let stack = &*STACK.init(Stack::new( + device, + config, + RESOURCES.init(StackResources::<2>::new()), + seed, + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(stack))); + + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 1024]; + let mut tx_buffer = [0; 1024]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); + info!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + Timer::after_secs(1).await; + continue; + } + info!("connected!"); + let buf = [0; 1024]; + loop { + let r = socket.write_all(&buf).await; + if let Err(e) = r { + info!("write error: {:?}", e); + break; + } + Timer::after_secs(1).await; + } + } +} From ab31a02e17b812a965842c1b14dc44b96a57932a Mon Sep 17 00:00:00 2001 From: dvdsk <noreply@davidsk.dev> Date: Sat, 8 Jun 2024 00:30:52 +0200 Subject: [PATCH 3/3] cargo fmt --- embassy-net-wiznet/src/lib.rs | 2 +- examples/stm32f4/src/bin/eth_w5500.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/embassy-net-wiznet/src/lib.rs b/embassy-net-wiznet/src/lib.rs index 545b2a2e4..90102196a 100644 --- a/embassy-net-wiznet/src/lib.rs +++ b/embassy-net-wiznet/src/lib.rs @@ -29,7 +29,7 @@ pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; /// send packet queue. With a the ethernet MTU of _1514_ this takes up `N_RX + /// NTX * 1514` bytes. While setting these both to 1 is the minimum this might /// hurt performance as a packet can not be received while processing another. -/// +/// /// # Warning /// On devices with a small amount of ram (think ~64k) watch out with the size /// of there parameters. They will quickly use too much RAM. diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index 827e74c51..c51111110 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -5,17 +5,18 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; -use embassy_net_wiznet::{chip::W5500, Device, Runner, State}; -use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::{Device, Runner, State}; use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::mode::Async; use embassy_stm32::rng::Rng; use embassy_stm32::spi::Spi; -use embassy_stm32::mode::Async; use embassy_stm32::time::Hertz; use embassy_stm32::{bind_interrupts, peripherals, rng, spi, Config}; -use embassy_time::{Timer, Delay}; -use embedded_io_async::Write; +use embassy_time::{Delay, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -25,9 +26,7 @@ bind_interrupts!(struct Irqs { type EthernetSPI = ExclusiveDevice<Spi<'static, Async>, Output<'static>, Delay>; #[embassy_executor::task] -async fn ethernet_task( - runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'static>, Output<'static>>, -) -> ! { +async fn ethernet_task(runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'static>, Output<'static>>) -> ! { runner.run().await }