stm32_eth/
lib.rs

1//! An abstraction layer for ethernet periperhals embedded in STM32 processors.
2//!
3//! For initialisation, see [`new`], and [`new_with_mii`]
4#![no_std]
5#![deny(missing_docs)]
6
7#[cfg(not(feature = "device-selected"))]
8compile_error!("No device was selected! Exactly one stm32fxxx feature must be selected.");
9
10/// Re-export
11#[cfg(feature = "stm32f7xx-hal")]
12pub use stm32f7xx_hal as hal;
13
14/// Re-export
15#[cfg(feature = "stm32f4xx-hal")]
16pub use stm32f4xx_hal as hal;
17
18/// Re-export
19#[cfg(feature = "stm32f1xx-hal")]
20pub use stm32f1xx_hal as hal;
21
22#[cfg(feature = "device-selected")]
23pub use hal::pac as stm32;
24#[cfg(feature = "device-selected")]
25use hal::rcc::Clocks;
26
27#[cfg(feature = "device-selected")]
28pub mod dma;
29
30#[cfg(feature = "device-selected")]
31pub mod mac;
32
33#[cfg(feature = "device-selected")]
34pub mod setup;
35#[doc(inline)]
36#[cfg(feature = "device-selected")]
37pub use setup::{EthPins, Parts, PartsIn};
38
39#[cfg(feature = "device-selected")]
40pub(crate) mod peripherals;
41
42#[cfg(feature = "ptp")]
43pub mod ptp;
44
45#[cfg(feature = "device-selected")]
46use {
47    dma::{EthernetDMA, RxRingEntry, TxRingEntry},
48    mac::{EthernetMAC, EthernetMACWithMii, MdcPin, MdioPin, Speed, WrongClock},
49    setup::*,
50};
51
52#[cfg(all(feature = "device-selected", feature = "ptp"))]
53use ptp::EthernetPTP;
54
55/// A summary of the reasons for the occurence of an
56/// interrupt
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58#[derive(Clone, Copy, Debug, PartialEq)]
59pub struct InterruptReason {
60    /// A packet has arrived and is ready for processing.
61    pub rx: bool,
62    /// A packet was sent, and a TX slot has freed up.
63    pub tx: bool,
64    /// A DMA error occured.
65    pub dma_error: bool,
66    #[cfg(all(feature = "ptp", not(feature = "stm32f1xx-hal")))]
67    /// The target time configured for PTP has
68    /// passed.
69    pub time_passed: bool,
70}
71
72/// Handle the `ETH` interrupt.
73///
74/// This function wakes wakers and resets
75/// interrupt bits relevant in that interrupt.
76#[cfg(feature = "device-selected")]
77pub fn eth_interrupt_handler() -> InterruptReason {
78    let dma = EthernetDMA::interrupt_handler();
79
80    #[cfg(all(feature = "ptp", not(feature = "stm32f1xx-hal")))]
81    let is_time_trigger = EthernetPTP::interrupt_handler();
82
83    InterruptReason {
84        rx: dma.is_rx,
85        tx: dma.is_tx,
86        dma_error: dma.is_error,
87        #[cfg(all(feature = "ptp", not(feature = "stm32f1xx-hal")))]
88        time_passed: is_time_trigger,
89    }
90}
91
92/// Create and initialise the ethernet driver.
93///
94/// Initialize and start tx and rx DMA engines.
95/// Sets up the peripheral clocks and GPIO configuration,
96/// and configures the ETH MAC and DMA peripherals.
97/// Automatically sets slew rate to VeryHigh.
98///
99/// The speed of the MAC is set to [`Speed::FullDuplexBase100Tx`].
100/// This can be changed using [`EthernetMAC::set_speed`].
101///
102/// This method does not initialise the external PHY. Interacting with a PHY
103/// can be done by using the struct returned from [`EthernetMAC::mii`].
104///
105/// # Note
106/// - Make sure that the buffers reside in a memory region that is
107/// accessible by the peripheral. Core-Coupled Memory (CCM) is
108/// usually not accessible.
109/// - HCLK must be at least 25 MHz.
110#[cfg(feature = "device-selected")]
111pub fn new<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>(
112    parts: PartsIn,
113    rx_buffer: &'rx mut [RxRingEntry],
114    tx_buffer: &'tx mut [TxRingEntry],
115    clocks: Clocks,
116    pins: EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>,
117) -> Result<Parts<'rx, 'tx, EthernetMAC>, WrongClock>
118where
119    REFCLK: RmiiRefClk + AlternateVeryHighSpeed,
120    CRS: RmiiCrsDv + AlternateVeryHighSpeed,
121    TXEN: RmiiTxEN + AlternateVeryHighSpeed,
122    TXD0: RmiiTxD0 + AlternateVeryHighSpeed,
123    TXD1: RmiiTxD1 + AlternateVeryHighSpeed,
124    RXD0: RmiiRxD0 + AlternateVeryHighSpeed,
125    RXD1: RmiiRxD1 + AlternateVeryHighSpeed,
126{
127    // Configure all of the pins correctly
128    pins.setup_pins();
129
130    // Set up the clocks and reset the MAC periperhal
131    setup::setup();
132
133    let eth_mac = parts.mac.into();
134
135    // Congfigure and start up the ethernet DMA.
136    let dma = EthernetDMA::new(parts.dma.into(), rx_buffer, tx_buffer);
137
138    // Configure the ethernet PTP
139    #[cfg(feature = "ptp")]
140    let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma);
141
142    // Configure the ethernet MAC
143    let mac = EthernetMAC::new(eth_mac, parts.mmc, clocks, Speed::FullDuplexBase100Tx, &dma)?;
144
145    let parts = Parts {
146        mac,
147        dma,
148        #[cfg(feature = "ptp")]
149        ptp,
150    };
151
152    Ok(parts)
153}
154
155/// Create and initialise the ethernet driver.
156///
157/// Initialize and start tx and rx DMA engines.
158/// Sets up the peripheral clocks and GPIO configuration,
159/// and configures the ETH MAC and DMA peripherals.
160/// Automatically sets slew rate to VeryHigh.
161///
162/// This method does not initialise the external PHY.
163///
164/// The speed of the MAC is set to [`Speed::FullDuplexBase100Tx`].
165/// This can be changed using [`EthernetMAC::set_speed`].
166///
167/// The MII for the external PHY can be accessed through the
168/// returned [`EthernetMACWithMii`], .
169///
170/// # Note
171/// - Make sure that the buffers reside in a memory region that is
172/// accessible by the peripheral. Core-Coupled Memory (CCM) is
173/// usually not accessible.
174/// - HCLK must be at least 25 MHz.
175#[cfg(feature = "device-selected")]
176pub fn new_with_mii<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1, MDIO, MDC>(
177    parts: PartsIn,
178    rx_buffer: &'rx mut [RxRingEntry],
179    tx_buffer: &'tx mut [TxRingEntry],
180    clocks: Clocks,
181    pins: EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>,
182    mdio: MDIO,
183    mdc: MDC,
184) -> Result<Parts<'rx, 'tx, EthernetMACWithMii<MDIO, MDC>>, WrongClock>
185where
186    REFCLK: RmiiRefClk + AlternateVeryHighSpeed,
187    CRS: RmiiCrsDv + AlternateVeryHighSpeed,
188    TXEN: RmiiTxEN + AlternateVeryHighSpeed,
189    TXD0: RmiiTxD0 + AlternateVeryHighSpeed,
190    TXD1: RmiiTxD1 + AlternateVeryHighSpeed,
191    RXD0: RmiiRxD0 + AlternateVeryHighSpeed,
192    RXD1: RmiiRxD1 + AlternateVeryHighSpeed,
193    MDIO: MdioPin,
194    MDC: MdcPin,
195{
196    // Configure all of the pins correctly
197    pins.setup_pins();
198
199    // Set up the clocks and reset the MAC periperhal
200    setup::setup();
201
202    let eth_mac = parts.mac.into();
203
204    // Congfigure and start up the ethernet DMA.
205    let dma = EthernetDMA::new(parts.dma.into(), rx_buffer, tx_buffer);
206
207    // Configure the ethernet PTP
208    #[cfg(feature = "ptp")]
209    let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma);
210
211    // Configure the ethernet MAC
212    let mac = EthernetMAC::new(eth_mac, parts.mmc, clocks, Speed::FullDuplexBase100Tx, &dma)?
213        .with_mii(mdio, mdc);
214
215    let parts = Parts {
216        mac,
217        dma,
218        #[cfg(feature = "ptp")]
219        ptp,
220    };
221
222    Ok(parts)
223}
224
225/// This block ensures that README.md is checked when `cargo test` is run.
226///
227/// Taken from https://github.com/rp-rs/pio-rs/blob/b52d3ba9c031ffa72bdd6f16b5fa8c0c04f0e2e0/src/lib.rs#L963
228#[cfg(doctest)]
229mod test_readme {
230    macro_rules! external_doc_test {
231        ($x:expr) => {
232            #[doc = $x]
233            extern "C" {}
234        };
235    }
236    external_doc_test!(include_str!("../README.md"));
237}