From 4d306fb4367e67fcc6004b34e65def64fa8e1bcc Mon Sep 17 00:00:00 2001 From: "Simon B. Gasse" Date: Fri, 19 Jan 2024 21:10:03 +0100 Subject: [PATCH] Implement MII interface with feature flag Extend the eth/v2 module to support MII besides RMII and introduce feature flags for RMII/MII. Add eth_client_mii.rs example. --- embassy-stm32/Cargo.toml | 9 +- embassy-stm32/build.rs | 16 +++ embassy-stm32/src/eth/mod.rs | 16 +++ embassy-stm32/src/eth/v2/mod.rs | 47 ++++++- examples/stm32h7/.cargo/config.toml | 2 +- examples/stm32h7/Cargo.toml | 3 +- examples/stm32h7/src/bin/eth_client_mii.rs | 146 +++++++++++++++++++++ 7 files changed, 232 insertions(+), 7 deletions(-) create mode 100644 examples/stm32h7/src/bin/eth_client_mii.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 865970dfb3..64cae133a6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -91,7 +91,7 @@ stm32-metapac = { version = "15", default-features = false, features = ["metadat [features] -default = ["rt"] +default = ["rt", "eth-rmii"] ## Enable `stm32-metapac`'s `rt` feature rt = ["stm32-metapac/rt"] @@ -161,6 +161,13 @@ split-pc3 = ["_split-pins-enabled"] ## internal use only _split-pins-enabled = [] +#! ## Network-selection features + +## Use MII for ethernet (media independent interface): 14 pins +eth-mii = [] +## Use RMII for ethernet (reduced media independent interface): 9 pins +eth-rmii = [] + #! ## Chip-selection features #! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`. #! Check the `Cargo.toml` for the latest list of supported chips. diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3afff..c82e73433d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -734,14 +734,30 @@ fn main() { (("otg", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)), (("can", "TX"), quote!(crate::can::TxPin)), (("can", "RX"), quote!(crate::can::RxPin)), + #[cfg(feature = "eth-rmii")] (("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)), + #[cfg(feature = "eth-mii")] + (("eth", "RX_CLK"), quote!(crate::eth::RXClkPin)), + #[cfg(feature = "eth-mii")] + (("eth", "TX_CLK"), quote!(crate::eth::TXClkPin)), (("eth", "MDIO"), quote!(crate::eth::MDIOPin)), (("eth", "MDC"), quote!(crate::eth::MDCPin)), + #[cfg(feature = "eth-rmii")] (("eth", "CRS_DV"), quote!(crate::eth::CRSPin)), + #[cfg(feature = "eth-mii")] + (("eth", "RX_DV"), quote!(crate::eth::RXDVPin)), (("eth", "RXD0"), quote!(crate::eth::RXD0Pin)), (("eth", "RXD1"), quote!(crate::eth::RXD1Pin)), + #[cfg(feature = "eth-mii")] + (("eth", "RXD2"), quote!(crate::eth::RXD2Pin)), + #[cfg(feature = "eth-mii")] + (("eth", "RXD3"), quote!(crate::eth::RXD3Pin)), (("eth", "TXD0"), quote!(crate::eth::TXD0Pin)), (("eth", "TXD1"), quote!(crate::eth::TXD1Pin)), + #[cfg(feature = "eth-mii")] + (("eth", "TXD2"), quote!(crate::eth::TXD2Pin)), + #[cfg(feature = "eth-mii")] + (("eth", "TXD3"), quote!(crate::eth::TXD3Pin)), (("eth", "TX_EN"), quote!(crate::eth::TXEnPin)), (("fmc", "A0"), quote!(crate::fmc::A0Pin)), (("fmc", "A1"), quote!(crate::fmc::A1Pin)), diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 4484055078..6377ec75f6 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -192,12 +192,28 @@ impl sealed::Instance for crate::peripherals::ETH { } impl Instance for crate::peripherals::ETH {} +#[cfg(feature = "eth-mii")] +pin_trait!(RXClkPin, Instance); +#[cfg(feature = "eth-mii")] +pin_trait!(TXClkPin, Instance); +#[cfg(feature = "eth-rmii")] pin_trait!(RefClkPin, Instance); pin_trait!(MDIOPin, Instance); pin_trait!(MDCPin, Instance); +#[cfg(feature = "eth-mii")] +pin_trait!(RXDVPin, Instance); +#[cfg(feature = "eth-rmii")] pin_trait!(CRSPin, Instance); pin_trait!(RXD0Pin, Instance); pin_trait!(RXD1Pin, Instance); +#[cfg(feature = "eth-mii")] +pin_trait!(RXD2Pin, Instance); +#[cfg(feature = "eth-mii")] +pin_trait!(RXD3Pin, Instance); pin_trait!(TXD0Pin, Instance); pin_trait!(TXD1Pin, Instance); +#[cfg(feature = "eth-mii")] +pin_trait!(TXD2Pin, Instance); +#[cfg(feature = "eth-mii")] +pin_trait!(TXD3Pin, Instance); pin_trait!(TXEnPin, Instance); diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 59745cba0c..c5de99b84d 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -39,6 +39,9 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { _peri: PeripheralRef<'d, T>, pub(crate) tx: TDesRing<'d>, pub(crate) rx: RDesRing<'d>, + #[cfg(feature = "eth-mii")] + pins: [PeripheralRef<'d, AnyPin>; 14], + #[cfg(feature = "eth-rmii")] pins: [PeripheralRef<'d, AnyPin>; 9], pub(crate) phy: P, pub(crate) station_management: EthernetStationManagement, @@ -58,22 +61,36 @@ macro_rules! config_pins { impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { /// Create a new Ethernet driver. + /// + /// Depending on whether the Media-Independent-Interface (MII) or + /// Reduced-Media-Independent-Interface (RMII) is used, we need 14 or 9 + /// pins. pub fn new( queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding + 'd, - ref_clk: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] rx_clk: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] tx_clk: impl Peripheral

> + 'd, + #[cfg(feature = "eth-rmii")] ref_clk: impl Peripheral

> + 'd, mdio: impl Peripheral

> + 'd, mdc: impl Peripheral

> + 'd, - crs: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] rxdv: impl Peripheral

> + 'd, + #[cfg(feature = "eth-rmii")] crs: impl Peripheral

> + 'd, rx_d0: impl Peripheral

> + 'd, rx_d1: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] rx_d2: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] rx_d3: impl Peripheral

> + 'd, tx_d0: impl Peripheral

> + 'd, tx_d1: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] tx_d2: impl Peripheral

> + 'd, + #[cfg(feature = "eth-mii")] tx_d3: impl Peripheral

> + 'd, tx_en: impl Peripheral

> + 'd, phy: P, mac_addr: [u8; 6], ) -> Self { + #[cfg(feature = "eth-mii")] + into_ref!(peri, rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); + #[cfg(feature = "eth-rmii")] into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); // Enable the necessary Clocks @@ -85,7 +102,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_eth1rxen(true); }); - // RMII + #[cfg(feature = "eth-mii")] + crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b000)); + #[cfg(feature = "eth-rmii")] crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); }); @@ -99,12 +118,16 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_ethrxen(true); }); + // TODO: What would MII need here? // RMII crate::pac::SYSCFG .pmcr() .modify(|w| w.set_eth_sel_phy(crate::pac::syscfg::vals::EthSelPhy::B_0X4)); }); + #[cfg(feature = "eth-mii")] + config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); + #[cfg(feature = "eth-rmii")] config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); let dma = ETH.ethernet_dma(); @@ -182,6 +205,24 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } }; + #[cfg(feature = "eth-mii")] + let pins = [ + rx_clk.map_into(), + tx_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + rxdv.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + rx_d2.map_into(), + rx_d3.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_d2.map_into(), + tx_d3.map_into(), + tx_en.map_into(), + ]; + #[cfg(feature = "eth-rmii")] let pins = [ ref_clk.map_into(), mdio.map_into(), diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index 5f680dbce0..5ffa0851ef 100644 --- a/examples/stm32h7/.cargo/config.toml +++ b/examples/stm32h7/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv7em-none-eabihf] -runner = 'probe-rs run --chip STM32H743ZITx' +runner = 'probe-rs-cli run --chip STM32H745XIHx' [build] target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d9ea2626d5..397ac83ee8 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -5,8 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -# Change stm32h743bi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h745xi-cm7", "time-driver-any", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs new file mode 100644 index 0000000000..20e723dfa6 --- /dev/null +++ b/examples/stm32h7/src/bin/eth_client_mii.rs @@ -0,0 +1,146 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::client::{TcpClient, TcpClientState}; +use embassy_net::{Stack, StackResources}; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue}; +use embassy_stm32::peripherals::ETH; +use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_time::Timer; +use embedded_io_async::Write; +use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; +use embassy_net::{Ipv4Address, Ipv4Cidr}; +use rand_core::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; +use heapless::Vec; + +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; +}); + +type Device = Ethernet<'static, ETH, GenericSMI>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + config.rcc.hsi48 = Some(Default::default()); // needed for RNG + config.rcc.pll1 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } + 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]; + rng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + static PACKETS: StaticCell> = StaticCell::new(); + + // Note: You need to deactivate the `eth-rmii` and activate the `eth-mii` + // feature. + let device = Ethernet::new( + PACKETS.init(PacketQueue::<16, 16>::new()), + p.ETH, + Irqs, + p.PA1, + p.PC3, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PB0, + p.PB1, + p.PG13, + p.PG12, + p.PC2, + p.PE2, + p.PG11, + GenericSMI::new(1), + mac_addr, + ); + info!("Device created"); + + // let config = embassy_net::Config::dhcpv4(Default::default()); + let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 100, 5), 24), + dns_servers: Vec::new(), + gateway: Some(Ipv4Address::new(192, 168, 100, 1)), + }); + + // Init network stack + static STACK: StaticCell> = StaticCell::new(); + static RESOURCES: StaticCell> = StaticCell::new(); + let stack = &*STACK.init(Stack::new( + device, + config, + RESOURCES.init(StackResources::<3>::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"); + + let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); + let client = TcpClient::new(&stack, &state); + + loop { + // You need to start a server on the host machine, for example: `nc -l 8000` + let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 100, 1), 8000)); + + info!("connecting..."); + let r = client.connect(addr).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + Timer::after_secs(1).await; + continue; + } + let mut connection = r.unwrap(); + info!("connected!"); + loop { + let r = connection.write_all(b"Hello\n").await; + if let Err(e) = r { + info!("write error: {:?}", e); + break; + } + Timer::after_secs(1).await; + } + } +}