Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f1e6ebb
[core] Add helper to check if in ISR context
WasabiFan Aug 10, 2026
c812ccc
[freertos] Rename Ethernet driver implementation
WasabiFan Aug 3, 2026
68a07fe
[architecture] Add Ethernet interface types
WasabiFan Aug 3, 2026
09631c1
[platform] Add unique MAC address helper from STM32 device ID
WasabiFan Aug 3, 2026
bea93a4
fixup! [platform] Add unique MAC address helper from STM32 device ID
WasabiFan Aug 10, 2026
29c4b64
[driver] Add LAN87xx Ethernet PHY drivers
WasabiFan Aug 3, 2026
ce18a52
[eth] Fix STM32F4/F7 PHY software reset
WasabiFan Aug 3, 2026
64fff06
[rcc] Add RX/TX clocks for STM32H7/H5 Ethernet
WasabiFan Aug 3, 2026
39aa734
fixup! [rcc] Add RX/TX clocks for STM32H7/H5 Ethernet
salkinium Aug 9, 2026
78430c6
[stm32] Enable SRAM3 clock at startup
WasabiFan Aug 3, 2026
ba5bfc8
[eth] Add STM32H7/H5 Ethernet MAC driver
WasabiFan Aug 3, 2026
3660fd0
fixup! [eth] Add STM32H7/H5 Ethernet MAC driver
salkinium Aug 9, 2026
e3fac5c
fixup! [eth] Add STM32H7/H5 Ethernet MAC driver
WasabiFan Aug 10, 2026
7f66cfc
fixup! [eth] Add STM32H7/H5 Ethernet MAC driver
WasabiFan Aug 10, 2026
dba3457
[ext] Add partial lwIP submodule
WasabiFan Aug 3, 2026
51fb703
[lwip] Add lwIP module and configs for NO_SYS API
WasabiFan Aug 3, 2026
2a0d579
fixup! [lwip] Add lwIP module and configs for NO_SYS API
WasabiFan Aug 10, 2026
439f459
fixup! [lwip] Add lwIP module and configs for NO_SYS API
WasabiFan Aug 10, 2026
4768ce5
[lwip] Add lwIP Ethernet adapter
WasabiFan Aug 3, 2026
72f1b65
fixup! [lwip] Add lwIP Ethernet adapter
WasabiFan Aug 10, 2026
48da939
[stdc] Support opting out of Newlib Nano
WasabiFan Aug 3, 2026
847e200
fixup! [stdc] Support opting out of Newlib Nano
WasabiFan Aug 10, 2026
03ab2d4
fixup! [stdc] Support opting out of Newlib Nano
WasabiFan Aug 10, 2026
bef7a37
[board] Add Ethernet clocks/pins to STM32H7/H5 boards
WasabiFan Aug 3, 2026
0e8a11d
fixup! [board] Add Ethernet clocks/pins to STM32H7/H5 boards
salkinium Aug 9, 2026
2345d85
[test] Add STM32H7 Ethernet and lwIP tests
WasabiFan Aug 3, 2026
f76faf7
fixup! [test] Add STM32H7 Ethernet and lwIP tests
WasabiFan Aug 10, 2026
f7158d6
fixup! [test] Add STM32H7 Ethernet and lwIP tests
WasabiFan Aug 10, 2026
9b9c6b9
[ci] Add lwip tests to CI
WasabiFan Aug 10, 2026
a15d138
[examples] Add STM32H7/H5 Ethernet examples
WasabiFan Aug 3, 2026
1d76ba6
fixup! [examples] Add STM32H7/H5 Ethernet examples
salkinium Aug 9, 2026
cb32847
fixup! [examples] Add STM32H7/H5 Ethernet examples
WasabiFan Aug 10, 2026
3b07782
fixup! [examples] Add STM32H7/H5 Ethernet examples
WasabiFan Aug 10, 2026
b313c2c
fixup! [examples] Add STM32H7/H5 Ethernet examples
WasabiFan Aug 10, 2026
a4832c7
[examples] Add lwIP echo host test
WasabiFan Aug 3, 2026
276fb5c
fixup! [examples] Add lwIP echo host test
WasabiFan Aug 10, 2026
542af58
[docs] Update support matrix for STM32H7 Ethernet
WasabiFan Aug 5, 2026
e5fed9a
fixup! [docs] Update support matrix for STM32H7 Ethernet
WasabiFan Aug 10, 2026
b739338
[assert] Remove packing from assertion info structs
WasabiFan Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ jobs:
if: always()
run: |
(cd test && make run-hosted-linux)
- name: Hosted lwIP Unittests
if: always()
run: |
(cd test && make run-hosted-lwip-linux)
- name: Compile STM32 Unittests
if: always()
run: |
Expand All @@ -82,6 +86,7 @@ jobs:
(cd test && make compile-nucleo-f767zi)
(cd test && make compile-nucleo-g474re)
(cd test && make compile-nucleo-h723zg)
(cd test && make compile-nucleo-h753zi-eth)
(cd test && make compile-nucleo-l432kc)
(cd test && make compile-nucleo-l496zg-p)
(cd test && make compile-nucleo-l552ze-q)
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@
[submodule "ext/arm/cmsis-dap"]
path = ext/arm/cmsis-dap
url = https://github.com/modm-ext/cmsis-dap-partial.git
[submodule "ext/lwip/lwip"]
path = ext/lwip/lwip
url = https://github.com/modm-ext/lwip-partial.git
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
<td align="center">✕</td>
<td align="center">✕</td>
<td align="center">○</td>
<td align="center"></td>
<td align="center"></td>
<td align="center">✕</td>
<td align="center">✕</td>
<td align="center">✕</td>
Expand Down Expand Up @@ -999,73 +999,76 @@ your specific needs.
<td align="center"><a href="https://modm.io/reference/module/modm-driver-l3gd20">L3GD20</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lan8720a">LAN8720A</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lan8742a">LAN8742A</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lan87xx">LAN87XX</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lawicel">LAWICEL</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lis302dl">LIS302DL</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lis3dsh">LIS3DSH</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lis3mdl">LIS3MDL</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lm75">LM75</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lp503x">LP503x</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lsm303a">LSM303A</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lsm6ds33">LSM6DS33</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-lsm6dso">LSM6DSO</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ltc2497">LTC2497</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ltc2499">LTC2499</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ltc2984">LTC2984</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max14661">MAX14661</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max31855">MAX31855</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max31865">MAX31865</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max6966">MAX6966</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max7219">MAX7219</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp23x08">MCP23X08</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp23x17">MCP23x17</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp2515">MCP2515</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp3008">MCP3008</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp7941x">MCP7941x</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp990x">MCP990X</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mmc5603">MMC5603</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ms5611">MS5611</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ms5837">MS5837</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-nokia5110">NOKIA5110</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-nrf24">NRF24</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-parallel_tft_display">TFT-DISPLAY</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pat9125el">PAT9125EL</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca8574">PCA8574</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9535">PCA9535</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9548a">PCA9548A</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9685">PCA9685</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pcal6524">PCAL6524</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-qmc5883l">QMC5883L</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sh1106">SH1106</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s65">SIEMENS-S65</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s75">SIEMENS-S75</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk6812">SK6812</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk9822">SK9822</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306">SSD1306</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stts22h">STTS22H</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stusb4500">STUSB4500</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx1276">SX1276</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx128x">SX128X</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594x</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp102">TMP102</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp12x">TMP12x</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp175">TMP175</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl6180">VL6180</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>
</tr><tr>
</tr>
</table>
<!--/drivertable-->
Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ supported development boards:
[CMSIS DSP](https://github.com/modm-io/modm/tree/develop/examples/nucleo_f429zi/cmsis_dsp).
- NUCLEO-F446RE:
[Internal Flash Programming](https://github.com/modm-io/modm/blob/develop/examples/nucleo_f446re/flash/main.cpp).
- NUCLEO-H753ZI:
[Ethernet MAC/DMA and LAN8742A](https://github.com/modm-io/modm/tree/develop/examples/generic/ethernet),
[lwIP Ping and UDP/TCP Echo](https://github.com/modm-io/modm/tree/develop/examples/generic/ethernet_lwip_raw).
- STM32F072 Discovery:
[Blinky](https://github.com/modm-io/modm/blob/develop/examples/stm32f072_discovery/blink/main.cpp),
[CAN](https://github.com/modm-io/modm/blob/develop/examples/stm32f072_discovery/can/main.cpp),
Expand Down
57 changes: 57 additions & 0 deletions examples/generic/ethernet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# STM32 Ethernet MAC

This example exercises the STM32H5/H7 Ethernet MAC and LAN8742A PHY directly,
without a network stack. It sends one broadcast Ethernet frame per second and
alternates between the copied transmit API and the acquire/fill/commit API.
The MAC configuration uses its default locally administered address derived
from the STM32 unique identifier. The lwIP example uses the same driver
helper. The frames use
`0x88b5`, which the
[IEEE Registration Authority EtherType registry](https://standards-oui.ieee.org/ethertype/eth.txt)
assigns as Local Experimental EtherType 1 for public prototype and
vendor-specific protocol development. They are not IP packets.
The typed MAC configuration selects `ChecksumMode::Software`, leaving hardware
checksum offload disabled because these frames contain no IP or transport
checksum.
Initialization succeeds with the cable unplugged; periodic link service reports
`down`, `negotiating`, and `up` transitions, and transmission begins only when
the negotiated link is up.

Applications that need IP networking should use the
[lwIP Ethernet example](../ethernet_lwip_raw) instead.

Build from this directory:

```sh
lbuild build
scons -Q build=release
scons -Q build=release program
```

Connect the board Ethernet port to the capture interface and start the capture
before running the `program` command. Monitor the ST-LINK virtual COM port at
115200 baud, 8 data bits, no parity, and 1 stop bit. The serial log reports
initialization, link transitions, the alternating transmit APIs used, and the
sequence number.

Use this Wireshark display filter:

```text
eth.type == 0x88b5
```

Or capture from a command prompt, replacing `<interface>` with the capture
interface reported by `tshark -D`:

```text
tshark -i <interface> -f "ether proto 0x88b5" -V
```

Both transmit APIs construct the same frame layout. Each 60-byte frame contains
the ASCII marker `modm-stm32h7-eth` followed by a little-endian 32-bit sequence
number. The destination is broadcast so the frames remain visible when the
board and capture host are connected through a switch.

Validation is manual: confirm that capture lengths are 60 bytes, source MAC
addresses match the serial log, destination addresses are broadcast, EtherType
is `0x88b5`, markers are intact, and sequence numbers increase without gaps.
174 changes: 174 additions & 0 deletions examples/generic/ethernet/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (c) 2026, Kaelin Laundry
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

// Sends raw IEEE 802 local experimental frames without a network stack.

#include <modm/board.hpp>
#include <modm/driver/ethernet/lan8742a.hpp>

#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <span>

using namespace Board;
using namespace std::chrono_literals;

namespace
{

using Mac = modm::platform::EthernetMac;
using Phy = modm::Lan8742a<Board::eth::PhyAddress>;

constexpr std::size_t FrameSize = 60;
constexpr uint16_t ExperimentalEtherType = 0x88b5;
constexpr std::array<uint8_t, 16> PayloadMarker{{
'm', 'o', 'd', 'm', '-', 's', 't', 'm', '3', '2', 'h', '7', '-', 'e', 't', 'h'
}};

static void
logMacAddress(const modm::ethernet::MacAddress& address)
{
MODM_LOG_INFO << "MAC ";
for (std::size_t index = 0; index < address.size(); ++index) {
MODM_LOG_INFO << modm::hex << address[index] << modm::ascii;
if (index + 1 < address.size()) {
MODM_LOG_INFO << ':';
}
}
MODM_LOG_INFO << modm::endl;
}

void
fillFrame(std::span<uint8_t> frame, const std::array<uint8_t, 6>& macAddress,
uint32_t sequence)
{
std::fill(frame.begin(), frame.end(), 0);
std::fill_n(frame.begin(), 6, uint8_t{0xff});
std::copy(macAddress.begin(), macAddress.end(), frame.begin() + 6);
frame[12] = uint8_t(ExperimentalEtherType >> 8);
frame[13] = uint8_t(ExperimentalEtherType);
std::copy(PayloadMarker.begin(), PayloadMarker.end(), frame.begin() + 14);
frame[30] = uint8_t(sequence);
frame[31] = uint8_t(sequence >> 8);
frame[32] = uint8_t(sequence >> 16);
frame[33] = uint8_t(sequence >> 24);
}

const char*
linkStateName(modm::ethernet::LinkState state)
{
switch (state)
{
case modm::ethernet::LinkState::Down: return "down";
case modm::ethernet::LinkState::Negotiating: return "negotiating";
case modm::ethernet::LinkState::Up: return "up";
}
return "unknown";
}

void
sendCopiedFrame(const std::array<uint8_t, 6>& macAddress, uint32_t sequence)
{
std::array<uint8_t, FrameSize> frame{};
fillFrame(frame, macAddress, sequence);
const auto result = Mac::transmit(frame);
modm_assert(result.error != Mac::TransmitError::Busy, "eth.tx.copy.busy",
"Copied Ethernet transmit ring is busy");
modm_assert(result, "eth.tx.copy", "Copied Ethernet transmit failed",
static_cast<uintptr_t>(result.error));
MODM_LOG_INFO << "TX copied sequence=" << sequence << modm::endl;
}

void
sendLeaseFrame(const std::array<uint8_t, 6>& macAddress, uint32_t sequence)
{
auto lease = Mac::acquireTransmitBuffer(FrameSize);
modm_assert(lease.error() != Mac::TransmitError::Busy, "eth.tx.acquire.busy",
"Ethernet transmit ring is busy");
modm_assert(lease, "eth.tx.acquire", "Failed to acquire Ethernet transmit buffer",
static_cast<uintptr_t>(lease.error()));

fillFrame(lease.buffer(), macAddress, sequence);
const auto result = lease.commit();
modm_assert(result, "eth.tx.commit", "Acquired Ethernet transmit commit failed",
static_cast<uintptr_t>(result.error));
MODM_LOG_INFO << "TX acquired sequence=" << sequence << modm::endl;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't modm_assert a bit heavy here? How severe are these failures? Does it warrant abandoning executing entirely?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a real app I would recommend handling these more gracefully. These calls would fail if the link went down, for example, which is probably not a good reason to crash. I figured it was easier to understand the logic flow if the example used asserts. Is the convention for examples that it should implement recommended error handling patterns instead?


} // namespace

int
main()
{
Board::initialize();
MODM_LOG_INFO << "\n\nReboot: STM32H7 raw Ethernet MAC example" << modm::endl;

Mac::connect<Board::eth::RefClk::Refclk,
Board::eth::Mdc::Mdc,
Board::eth::Mdio::Mdio,
Board::eth::CrsDv::Rcccrsdv,
Board::eth::TxEn::Txen,
Board::eth::Txd0::Txd0,
Board::eth::Txd1::Txd1,
Board::eth::Rxd0::Rxd0,
Board::eth::Rxd1::Rxd1>();

const Mac::Configuration configuration{
.checksumMode = Mac::ChecksumMode::Software,
};
const auto macAddress = configuration.macAddress;
const auto initialized =
Mac::initialize<Board::SystemClock, Mac::MediaInterface::RMII>(configuration);
modm_assert(initialized, "eth.initialize", "Ethernet MAC initialization failed",
static_cast<uintptr_t>(initialized.error));
const auto phyInitialized = Phy::initialize<Mac>();
modm_assert(phyInitialized, "eth.phy.initialize", "Ethernet PHY initialization failed",
static_cast<uintptr_t>(phyInitialized.error));

logMacAddress(macAddress);
MODM_LOG_INFO << "EtherType 0x88b5" << modm::endl;

bool haveLinkState = false;
modm::ethernet::LinkState previousLinkState = modm::ethernet::LinkState::Down;
uint32_t sequence = 0;
uint8_t serviceTicks = 0;
while (true)
{
const auto observed = Phy::readLinkStatus<Mac>();
modm_assert(observed, "eth.phy.link", "Ethernet PHY link read failed",
static_cast<uintptr_t>(observed.error));
const auto link = Mac::notifyUpdatedLinkStatus(observed.status);
modm_assert(link, "eth.link", "Ethernet MAC link update failed",
static_cast<uintptr_t>(link.error));
if (not haveLinkState or link.status.state != previousLinkState) {
previousLinkState = link.status.state;
haveLinkState = true;
MODM_LOG_INFO << "Link " << linkStateName(previousLinkState) << modm::endl;
}

if (++serviceTicks == 10) {
serviceTicks = 0;
if (previousLinkState == modm::ethernet::LinkState::Up) {
if ((sequence & 1u) == 0) {
sendCopiedFrame(macAddress, sequence);
}
else {
sendLeaseFrame(macAddress, sequence);
}
sequence++;
}
}

modm::delay(100ms);
}
}
14 changes: 14 additions & 0 deletions examples/generic/ethernet/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<library>
<extends>modm:nucleo-h753zi</extends>
<!-- <extends>modm:nucleo-h563zi</extends> -->
<!-- <extends>modm:nucleo-h743zi</extends> -->
<!-- <extends>modm:nucleo-h723zg</extends> -->
<options>
<option name="modm:build:build.path">../../../build/generic/ethernet</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:platform:eth</module>
<module>modm:driver:lan8742a</module>
</modules>
</library>
Loading
Loading