diff --git a/embedded-examples/stm32h7-rtic/Cargo.toml b/embedded-examples/stm32h7-rtic/Cargo.toml index bbd5514..e933cf0 100644 --- a/embedded-examples/stm32h7-rtic/Cargo.toml +++ b/embedded-examples/stm32h7-rtic/Cargo.toml @@ -27,7 +27,6 @@ version = "0.11.0" default-features = false features = ["medium-ethernet", "proto-ipv4", "socket-raw"] -optional = true [dependencies.rtic] version = "2" features = ["thumbv7-backend"] diff --git a/embedded-examples/stm32h7-rtic/src/main.rs b/embedded-examples/stm32h7-rtic/src/main.rs index 54236b9..d476daf 100644 --- a/embedded-examples/stm32h7-rtic/src/main.rs +++ b/embedded-examples/stm32h7-rtic/src/main.rs @@ -1,36 +1,50 @@ #![no_main] #![no_std] +use core::mem::MaybeUninit; + use rtic::app; use satrs_stm32h7_nucleo_rtic as _; // global logger + panicking-behavior + memory layout +use stm32h7xx_hal::ethernet; const DEFAULT_BLINK_FREQ_MS: u32 = 1000; +/// Ethernet descriptor rings are a global singleton +#[link_section = ".sram3.eth"] +static mut DES_RING: MaybeUninit> = MaybeUninit::uninit(); + +/// Locally administered MAC address +const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; + #[app(device = stm32h7xx_hal::stm32, peripherals = true)] mod app { use super::*; use rtic_monotonics::systick::fugit::MillisDurationU32; use rtic_monotonics::systick::Systick; + use stm32h7xx_hal::ethernet::{EthernetMAC, PHY}; use stm32h7xx_hal::gpio::{Output, Pin}; use stm32h7xx_hal::prelude::*; + use stm32h7xx_hal::stm32::Interrupt; #[shared] struct Shared { blink_freq: MillisDurationU32, + eth_link_up: bool, } - struct Leds { - led0: Pin<'B', 0, Output>, + struct BlinkyLeds { led1: Pin<'B', 7, Output>, led2: Pin<'B', 14, Output>, } #[local] struct Local { - leds: Leds, + leds: BlinkyLeds, + link_led: Pin<'B', 0, Output>, + phy: ethernet::phy::LAN8742A, } #[init] - fn init(cx: init::Context) -> (Shared, Local) { + fn init(mut cx: init::Context) -> (Shared, Local) { defmt::println!("Starting sat-rs demo application for the STM32H743ZIT"); let pwr = cx.device.PWR.constrain(); @@ -58,18 +72,80 @@ mod app { systick_mono_token, ); - let gpiob = cx.device.GPIOB.split(ccdr.peripheral.GPIOB); - let led0 = gpiob.pb0.into_push_pull_output(); - let led1 = gpiob.pb7.into_push_pull_output(); - let led2 = gpiob.pb14.into_push_pull_output(); - let leds = Leds { led0, led1, led2 }; + // Those are used in the example, I am not fully sure what they are good for. + cx.core.SCB.enable_icache(); + cx.core.DWT.enable_cycle_counter(); + let gpioa = cx.device.GPIOA.split(ccdr.peripheral.GPIOA); + let gpiob = cx.device.GPIOB.split(ccdr.peripheral.GPIOB); + let gpioc = cx.device.GPIOC.split(ccdr.peripheral.GPIOC); + let gpiog = cx.device.GPIOG.split(ccdr.peripheral.GPIOG); + + let link_led = gpiob.pb0.into_push_pull_output(); + let mut led1 = gpiob.pb7.into_push_pull_output(); + let mut led2 = gpiob.pb14.into_push_pull_output(); + + // Criss-cross pattern looks cooler. + led1.set_high(); + led2.set_low(); + let leds = BlinkyLeds { led1, led2 }; + + let rmii_ref_clk = gpioa.pa1.into_alternate::<11>(); + let rmii_mdio = gpioa.pa2.into_alternate::<11>(); + let rmii_mdc = gpioc.pc1.into_alternate::<11>(); + let rmii_crs_dv = gpioa.pa7.into_alternate::<11>(); + let rmii_rxd0 = gpioc.pc4.into_alternate::<11>(); + let rmii_rxd1 = gpioc.pc5.into_alternate::<11>(); + let rmii_tx_en = gpiog.pg11.into_alternate::<11>(); + let rmii_txd0 = gpiog.pg13.into_alternate::<11>(); + 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( + cx.device.ETHERNET_MAC, + cx.device.ETHERNET_MTL, + cx.device.ETHERNET_DMA, + ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ), + // SAFETY: We do not move the returned DMA struct anymore, so this should be safe + // according to the docs. + unsafe { DES_RING.assume_init_mut() }, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + // Initialise ethernet PHY... + let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac.set_phy_addr(0)); + lan8742a.phy_reset(); + lan8742a.phy_init(); + + unsafe { + ethernet::enable_interrupt(); + cx.core.NVIC.set_priority(Interrupt::ETH, 196); // Mid prio + cortex_m::peripheral::NVIC::unmask(Interrupt::ETH); + } + + eth_link_check::spawn().expect("eth link check failed"); blink::spawn().expect("spawning blink task failed"); ( Shared { blink_freq: MillisDurationU32::from_ticks(DEFAULT_BLINK_FREQ_MS), + eth_link_up: false, + }, + Local { + link_led, + leds, + phy: lan8742a, }, - Local { leds }, ) } @@ -77,12 +153,39 @@ mod app { async fn blink(mut cx: blink::Context) { let leds = cx.local.leds; loop { - defmt::info!("toggling LEDs"); - leds.led0.toggle(); leds.led1.toggle(); leds.led2.toggle(); let current_blink_freq = cx.shared.blink_freq.lock(|current| *current); Systick::delay(current_blink_freq).await; } } + + #[task(local=[link_led, phy], shared=[eth_link_up])] + async fn eth_link_check(mut cx: eth_link_check::Context) { + let phy = cx.local.phy; + let link_led = cx.local.link_led; + loop { + let link_was_up = cx.shared.eth_link_up.lock(|link_up| *link_up); + if phy.poll_link() { + if !link_was_up { + link_led.set_high(); + cx.shared.eth_link_up.lock(|link_up| *link_up = true); + defmt::info!("Ethernet link up"); + } + } else if link_was_up { + link_led.set_low(); + cx.shared.eth_link_up.lock(|link_up| *link_up = false); + defmt::info!("Ethernet link down"); + } + Systick::delay(100.millis()).await; + } + } + + #[task(binds=ETH)] + fn eth_isr(_: eth_isr::Context) { + // SAFETY: We do not write the register mentioned inside the docs anywhere else. + unsafe { + ethernet::interrupt_handler(); + } + } }