diff --git a/embedded-examples/stm32h7-rtic/Cargo.toml b/embedded-examples/stm32h7-rtic/Cargo.toml index e933cf0..44a7079 100644 --- a/embedded-examples/stm32h7-rtic/Cargo.toml +++ b/embedded-examples/stm32h7-rtic/Cargo.toml @@ -25,7 +25,7 @@ stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] } [dependencies.smoltcp] version = "0.11.0" default-features = false -features = ["medium-ethernet", "proto-ipv4", "socket-raw"] +features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4"] [dependencies.rtic] version = "2" diff --git a/embedded-examples/stm32h7-rtic/src/main.rs b/embedded-examples/stm32h7-rtic/src/main.rs index d476daf..2965c7a 100644 --- a/embedded-examples/stm32h7-rtic/src/main.rs +++ b/embedded-examples/stm32h7-rtic/src/main.rs @@ -1,10 +1,15 @@ #![no_main] #![no_std] -use core::mem::MaybeUninit; - use rtic::app; +use rtic_monotonics::systick::Systick; +use rtic_monotonics::Monotonic; use satrs_stm32h7_nucleo_rtic as _; // global logger + panicking-behavior + memory layout + +use core::mem::MaybeUninit; +use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; +use smoltcp::time::Instant; +use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; use stm32h7xx_hal::ethernet; const DEFAULT_BLINK_FREQ_MS: u32 = 1000; @@ -13,11 +18,61 @@ const DEFAULT_BLINK_FREQ_MS: u32 = 1000; #[link_section = ".sram3.eth"] static mut DES_RING: MaybeUninit> = MaybeUninit::uninit(); +// This data will be held by Net through a mutable reference +pub struct NetStorageStatic<'a> { + socket_storage: [SocketStorage<'a>; 8], +} +// MaybeUninit allows us write code that is correct even if STORE is not +// initialised by the runtime +static mut STORE: MaybeUninit = MaybeUninit::uninit(); + /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; +pub struct Net<'a> { + iface: Interface, + ethdev: ethernet::EthernetDMA<4, 4>, + sockets: SocketSet<'a>, +} + +impl<'a> Net<'a> { + pub fn new( + store: &'a mut NetStorageStatic<'a>, + mut ethdev: ethernet::EthernetDMA<4, 4>, + ethernet_addr: HardwareAddress, + now: Instant, + ) -> Self { + let config = Config::new(ethernet_addr); + let mut iface = Interface::new(config, &mut ethdev, now); + // Set IP address + iface.update_ip_addrs(|addrs| { + let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0)); + }); + + let sockets = SocketSet::new(&mut store.socket_storage[..]); + + Net::<'a> { + iface, + ethdev, + sockets, + } + } + + /// Polls on the ethernet interface. You should refer to the smoltcp + /// documentation for poll() to understand how to call poll efficiently + pub fn poll(&mut self) -> bool { + let uptime = Systick::now() - Systick::ZERO; + let timestamp = smoltcp::time::Instant::from_millis(uptime.to_millis()); + + self.iface + .poll(timestamp, &mut self.ethdev, &mut self.sockets) + } +} + #[app(device = stm32h7xx_hal::stm32, peripherals = true)] mod app { + use core::ptr::addr_of_mut; + use super::*; use rtic_monotonics::systick::fugit::MillisDurationU32; use rtic_monotonics::systick::Systick; @@ -40,6 +95,7 @@ mod app { struct Local { leds: BlinkyLeds, link_led: Pin<'B', 0, Output>, + net: Net<'static>, phy: ethernet::phy::LAN8742A, } @@ -72,7 +128,8 @@ mod app { systick_mono_token, ); - // Those are used in the example, I am not fully sure what they are good for. + // Those are used in the smoltcp of the stm32h7xx-hal , I am not fully sure what they are + // good for. cx.core.SCB.enable_icache(); cx.core.DWT.enable_cycle_counter(); @@ -101,7 +158,7 @@ mod app { let rmii_txd1 = gpiob.pb13.into_alternate::<11>(); let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (_eth_dma, eth_mac) = ethernet::new( + let (eth_dma, eth_mac) = ethernet::new( cx.device.ETHERNET_MAC, cx.device.ETHERNET_MTL, cx.device.ETHERNET_DMA, @@ -116,8 +173,8 @@ mod app { rmii_txd0, rmii_txd1, ), - // SAFETY: We do not move the returned DMA struct anymore, so this should be safe - // according to the docs. + // SAFETY: We do not move the returned DMA struct across thread boundaries, so this + // should be safe according to the docs. unsafe { DES_RING.assume_init_mut() }, mac_addr, ccdr.peripheral.ETH1MAC, @@ -134,6 +191,22 @@ mod app { cortex_m::peripheral::NVIC::unmask(Interrupt::ETH); } + // unsafe: mutable reference to static storage, we only do this once + let store = unsafe { + let store_ptr = STORE.as_mut_ptr(); + + // Initialise the socket_storage field. Using `write` instead of + // assignment via `=` to not call `drop` on the old, uninitialised + // value + addr_of_mut!((*store_ptr).socket_storage).write([SocketStorage::EMPTY; 8]); + + // Now that all fields are initialised we can safely use + // assume_init_mut to return a mutable reference to STORE + STORE.assume_init_mut() + }; + + let net = Net::new(store, eth_dma, mac_addr.into(), Instant::ZERO); + eth_link_check::spawn().expect("eth link check failed"); blink::spawn().expect("spawning blink task failed"); ( @@ -144,6 +217,7 @@ mod app { Local { link_led, leds, + net, phy: lan8742a, }, ) @@ -181,11 +255,12 @@ mod app { } } - #[task(binds=ETH)] - fn eth_isr(_: eth_isr::Context) { + #[task(binds=ETH, local=[net])] + fn eth_isr(cx: eth_isr::Context) { // SAFETY: We do not write the register mentioned inside the docs anywhere else. unsafe { ethernet::interrupt_handler(); } + cx.local.net.poll(); } }