#stm32 #ethernet #stm32f4 #stm32f7 #eth

no-std stm32-eth

Embedded Rust Ethernet driver for the STM32 MCU series

14 releases (7 breaking)

0.7.0 Nov 3, 2024
0.6.0 Mar 16, 2024
0.5.2 Sep 21, 2023
0.5.0 Jul 26, 2023
0.0.0 Apr 25, 2018

#56 in Embedded development

44 downloads per month

Apache-2.0

145KB
3K SLoC

Rust Ethernet Driver for STM32F* microcontrollers

Supported microcontrollers

  • STM32F107
  • STM32F4xx
  • STM32F7xx

Pull requests are welcome :)

Usage

Add one of the following to the [dependencies] section in your Cargo.toml (with the correct MCU specified):

stm32-eth = { version = "0.6.0", features = ["stm32f429"] } # For stm32f4xx-like MCUs
stm32-eth = { version = "0.6.0", features = ["stm32f767"] } # For stm32f7xx-like MCUs
stm32-eth = { version = "0.6.0", features = ["stm32f107"] } # For stm32f107

stm32_eth re-exports the underlying HAL as stm32_eth::hal.

In src/main.rs add:

use stm32_eth::{
    hal::gpio::GpioExt,
    hal::rcc::RccExt,
    stm32::Peripherals,
    dma::{RxRingEntry, TxRingEntry},
    EthPins,
};
use fugit::RateExtU32;

fn main() {
    let p = Peripherals::take().unwrap();

    let rcc = p.RCC.constrain();
    // HCLK must be at least 25MHz to use the ethernet peripheral
    let clocks = rcc.cfgr.sysclk(32.MHz()).hclk(32.MHz()).freeze();

    let gpioa = p.GPIOA.split();
    let gpiob = p.GPIOB.split();
    let gpioc = p.GPIOC.split();
    let gpiog = p.GPIOG.split();

    let eth_pins = EthPins {
        ref_clk: gpioa.pa1,
        crs: gpioa.pa7,
        tx_en: gpiog.pg11,
        tx_d0: gpiog.pg13,
        tx_d1: gpiob.pb13,
        rx_d0: gpioc.pc4,
        rx_d1: gpioc.pc5,
    };

    let mut rx_ring: [RxRingEntry; 16] = Default::default();
    let mut tx_ring: [TxRingEntry; 8] = Default::default();

    let parts = stm32_eth::PartsIn {
        mac: p.ETHERNET_MAC,
        mmc: p.ETHERNET_MMC,
        dma: p.ETHERNET_DMA,
        ptp: p.ETHERNET_PTP,
    };

    let stm32_eth::Parts { dma: mut eth_dma, mac: _, ptp: _ } = stm32_eth::new(
        parts,
        &mut rx_ring[..],
        &mut tx_ring[..],
        clocks,
        eth_pins,
    )
    .unwrap();
    eth_dma.enable_interrupt();

    loop {
        if let Ok(pkt) = eth_dma.recv_next(None) {
            // handle received pkt
        }

        let size = 42;
        eth_dma.send(size, None, |buf| {
            // write up to `size` bytes into buf before it is being sent
        }).expect("send");
    }
}

use stm32_eth::stm32::interrupt;
#[interrupt]
fn ETH() {
    stm32_eth::eth_interrupt_handler();
}

smoltcp support

Use feature-flag smoltcp-phy.

To make proper use of smoltcp, you will also have to activate additional smoltcp features. You can do this by adding a dependency on the same version of smoltcp as stm32-eth to your own Cargo.toml with the features you require activated.

Examples

The examples should run and compile on any MCU that has an 802.3 compatible PHY capable of generating the required 50 MHz clock signal connected to the default RMII pins.

The examples use defmt and defmt_rtt for logging, and panic_probe over defmt_rtt for printing panic backtraces.

Alternative pin configuration, HSE & PPS

If the board you're developing for has a High Speed External oscillator connected to the correct pins, the HSE configuration can be activated by setting the STM32_ETH_EXAMPLE_HSE environment variable to one of oscillator or bypass when compiling.

If the board you're developing for uses the nucleo pinout (PG11 and PG13 instead of PB11 and PB12), the pin configuration can be changed by setting the STM32_ETH_EXAMPLE_PINS environment variable to nucleo when compiling.

If you wish to use the alternative PPS output pin (PG8 instead of PB5) for the rtic-timestamp example, the pin configuration can be changed by setting the STM32_ETH_EXAMPLE_PPS_PIN environment variable to alternate when compiling.

Building examples

To build an example, run the following command:

cargo build --release --example <example> \
    --features <MCU feature>,<additional required features> \
    --target <MCU compilation target>

For example, if we wish to build the ip example for an stm32f429, we should run the following command:

cargo build --release --example ip \
        --features stm32f429,smoltcp-phy \
        --target thumbv7em-none-eabihf

If we wish to build the arp example for a Nucleo-F767ZI with a HSE oscillator:

STM32_ETH_EXAMPLE_HSE=bypass STM32_ETH_EXAMPLE_PINS=nucleo \
cargo build --release --example arp \
    --features stm32f767

Running examples

Install probe-run with cargo install probe-run --version '~0.3'

Find the correct value for PROBE_RUN_CHIP for your MCU from the list provided by probe-run --list-chips.

Ensure that probe-run can attach to your MCU

Then, run the following command:

DEFMT_LOG=info PROBE_RUN_CHIP=<probe-run chip> \
cargo run --release --example <example> \
    --features <MCU feature>,<additional required features> \
    --target <MCU compilation target>

For example, if we wish to run the rtic-echo example on an STM32F107RCT6, we should run the following command:

DEFMT_LOG=info PROBE_RUN_CHIP=STM32F107RC \
cargo run --release --example rtic-echo \
    --features stm32f107,smoltcp-phy \
    --target thumbv7m-none-eabi

Or, if we want to run the arp example on a Nucleo-F767ZI with a HSE oscillator:

DEFMT_LOG=info PROBE_RUN_CHIP=STM32F767ZGTx \
STM32_ETH_EXAMPLE_PINS=nucleo STM32_ETH_EXAMPLE_HSE=oscillator \
cargo run --release --example arp \
    --features stm32f767 \
    --target thumbv7em-none-eabihf

License

All source code (including code snippets) is licensed under the Apache License, Version 2.0 (LICENSE) or https://www.apache.org/licenses/LICENSE-2.0

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

Dependencies

~1–27MB
~760K SLoC