|
 |
| ¸ñ·ÏÀ¸·Î °¡±â.. |
|
STM32H747I-DISCO ÀÌ´õ³Ý(RMII) ¹× LwIP ¿Ïº® ±¸Çö ¹× Æ®·¯ºí½´ÆÃ °¡À̵å |
±èÁ¾È£
|
»èÁ¦Çϱâ
|
|
# STM32H747I-DISCO Ethernet (RMII) + LwIP Complete Implementation & Troubleshooting Guide
# STM32H747I-DISCO ÀÌ´õ³Ý(RMII) ¹× LwIP ¿Ïº® ±¸Çö ¹× Æ®·¯ºí½´ÆÃ °¡À̵å
---
## 0. Introduction & Purpose / ¼·Ð ¹× ¸ñÀû
### [KO] ¼·Ð ¹× ¸ñÀû
ÀÌ ¹®¼´Â **STMicroelectronics STM32H747I-DISCO °³¹ß º¸µå**¿¡¼ °í¼º´É µà¾ó ÄÚ¾î(Cortex-M7)¿Í LAN8742A ÀÌ´õ³Ý PHY ĨÀ» °áÇÕÇÏ¿©, **LwIP ³×Æ®¿öÅ© ½ºÅðú FreeRTOS ±â¹ÝÀÇ ¾ç¹æÇâ ÀÌ´õ³Ý Åë½Å(TX/RX, ICMP Ping, °í¼Ó UDP ¼Û¼ö½Å)**À» ¿Ïº®ÇÏ°Ô ±¸ÇöÇϱâ À§ÇÑ ½ÇÀü ±â¼ú °¡À̵åÀÔ´Ï´Ù.
STM32H7 ½Ã¸®Áî´Â °í¼º´É Cortex-M7 ÄÚ¾îÀÇ Ãʰí¼Ó L1 µ¥ÀÌÅÍ Ä³½Ã(D-Cache)¿Í µ¶¸³µÈ µµ¸ÞÀκ° ¸Þ¸ð¸® ±¸Á¶(D1 AXI-SRAM, D2 SRAM1~3)¸¦ °®Ãß°í ÀÖ¾î, ÀÌÀü ¼¼´ë MCU(F4, F7)¿¡ ºñÇØ ÀÌ´õ³Ý DMA ±¸¼º ³À̵µ°¡ ¸Å¿ì ³ô½À´Ï´Ù. ƯÈ÷ ST °ø½Ä ÅøÃ¼ÀÎ(STM32CubeMX)ÀÇ ÀÚµ¿ »ý¼º Äڵ忡 Á¸ÀçÇÏ´Â ¸µÄ¿ ½ºÅ©¸³Æ® ¿ÀŸ, D-Cache µ¥ÀÌÅÍ ºÒÀÏÄ¡(Coherency) ¹®Á¦, ±×¸®°í º¸µå ³» ¿Âº¸µå ȸ·Î(MEMS ¸¶ÀÌÅ©)¿ÍÀÇ ÇÉ Ãæµ¹ µîÀ¸·Î ÀÎÇØ ¸¹Àº °³¹ßÀÚµéÀÌ **"Ping ÀÀ´ä ½ÇÆÐ", "¹«ÇÑ TX BUSY Àá±è", "MISCOMPARE µ¥ÀÌÅÍ ºÒÀÏÄ¡ ¿À·ù"**¸¦ °Þ°Ô µË´Ï´Ù.
**º» ¹®¼ÀÇ ¸ñÀûÀº ´ÙÀ½°ú °°½À´Ï´Ù:**
1. **Çϵå¿þ¾î °³Á¶ Æ÷ÀÎÆ® ¸íÈ®È**: DISCO º¸µåÀÇ ±âº» ¿Âº¸µå Ãæµ¹ ȸ·Î¸¦ ¾ÈÀüÇÏ°Ô ºÐ¸®Çϰí ÀÌ´õ³Ý RMII ½ÅÈ£¼±À» 100% Ȱ¼ºÈÇÏ´Â ¼Ö´õ ºê¸´Áö ³³¶« °³Á¶ Æ÷ÀÎÆ®¸¦ »ó¼¼È÷ Á¦½ÃÇÕ´Ï´Ù.
2. **¼ÒÇÁÆ®¿þ¾î ³Á¦ÀÇ ±Ùº» ¿øÀÎ ±Ô¸í ¹× ÇØ°á**: MPU(¸Þ¸ð¸® º¸È£ À¯´Ö) ºñij½Ã ¼³Á¤, ¸µÄ¿ ½ºÅ©¸³Æ® ¸ÅÇÎ ¹ö±× ÆÐÄ¡, Cortex-M7 D-Cache Clean/InvalidateÀÇ ¿Ïº®ÇÑ Å¸Àְ̹ú ¼ø¼¸¦ ¸íÄèÈ÷ ¼³¸íÇÕ´Ï´Ù.
3. **100% µ¿ÀÛ °ËÁõµÈ ¼Ò½º ÄÚµå Á¦°ø**: 1ms À̳»ÀÇ ¹«¼Õ½Ç Ping(ICMP) ÀÀ´ä°ú 100ms ÁÖ±âÀÇ °í¼Ó UDP Àü¼ÛÀÌ ¿Ïº®È÷ °ËÁõµÈ ½ÇÀü Äڵ带 °øÀ¯ÇÏ¿©, °³¹ßÀÚ°¡ ºÒÇÊ¿äÇÑ ½ÃÇàÂø¿À ¾øÀÌ Áï½Ã »ó¿ë ÀÓº£µðµå ÇÁ·ÎÁ§Æ®, ¼º¸ ¸ðÅÍ Á¦¾î, »ê¾÷¿ë IoT ½Ã½ºÅÛ¿¡ Àû¿ëÇÒ ¼ö ÀÖµµ·Ï µ½½À´Ï´Ù.
---
### [EN] Introduction & Purpose
This document serves as a comprehensive, step-by-step engineering and troubleshooting guide for establishing robust, bi-directional Ethernet communication (TX/RX, ICMP Echo Reply, high-speed UDP) on the **STMicroelectronics STM32H747I-DISCO** discovery kit using the **LAN8742A RMII PHY**, **FreeRTOS (CMSIS-RTOS V2)**, and the **LwIP 2.1.2** TCP/IP stack running on the high-performance ARM Cortex-M7 core.
The STM32H7 family incorporates a high-performance L1 Data Cache (D-Cache), an AXI bus matrix, and segmented memory domains (D1 AXI-SRAM, D2 SRAM1–3). While this architecture delivers remarkable throughput, it introduces intricate challenges when synchronizing Ethernet DMA hardware with the CPU. Developers frequently encounter pervasive issues such as **perpetual `TX BUSY` lockups, packet loss, `MISCOMPARE at offset 28` errors during Ping requests**, and silent network freezes caused by CubeMX linker script bugs, MPU misconfigurations, and cache coherency hazards.
**The primary purposes of this guide are:**
1. **Provide a Definitive Hardware Blueprint**: Detail the exact solder bridge rework required on the STM32H747I-DISCO to isolate onboard MEMS microphone conflicts and properly route all RMII interface signals.
2. **Resolve Fundamental Software & Cache Roadblocks**: Explain the correct setup for MPU non-cacheable descriptor regions, fix linker script section naming bugs, and demonstrate the precise placement of D-Cache `Clean` and `Invalidate` operations.
3. **Deliver a 100% Production-Verified Reference Codebase**: Supply fully verified, end-to-end source code achieving deterministic sub-millisecond (<1ms) Ping response times and continuous UDP transmission, empowering engineers to immediately integrate reliable networking into motor control, industrial IoT, and real-time embedded systems.
---
## 1. System Specifications / Çϵå¿þ¾î ¹× ½Ã½ºÅÛ »ç¾ç
### [KO] Çϵå¿þ¾î ¹× ¼ÒÇÁÆ®¿þ¾î »ç¾ç
- **´ë»ó º¸µå**: STMicroelectronics **STM32H747I-DISCO** (Discovery Kit)
- **¸ÞÀÎ MCU**: **STM32H747XIH6** Dual-core (ARM Cortex-M7 @ 400/480MHz + Cortex-M4 @ 200/240MHz)
- **ÀÌ´õ³Ý PHY Ĩ**: Microchip / SMSC **LAN8742A** (10/100 Mbps RMII Physical Layer Transceiver)
- **ÀÎÅÍÆäÀ̽º ¸ðµå**: RMII (Reduced Media Independent Interface, 50 MHz ±âÁØ Å¬·°)
- **Ŭ·° ±¸Á¶**:
- º¸µå ³»Àå 25 MHz Å©¸®½ºÅ»(X3) ¶Ç´Â MCU `PA8 (MCO1: 25MHz HSE)` $\rightarrow$ LAN8742A ³»ºÎ PLL·Î 50 MHz ü¹è $\rightarrow$ LAN8742A Pin 5(`CLKOUT`) $\rightarrow$ **SB17** $\rightarrow$ STM32 `PA1 (ETH_REF_CLK)`
- **¿î¿µÃ¼Á¦ ¹× ½ºÅÃ**: FreeRTOS (CMSIS-RTOS V2) + LwIP 2.1.2 (ST HAL Driver)
- **³×Æ®¿öÅ© ¼³Á¤**: °íÁ¤ IP `192.168.31.60` (¼ºê³Ý `255.255.255.0`, °ÔÀÌÆ®¿þÀÌ `192.168.31.1`), PC IP: `192.168.31.58`
### [EN] Hardware & Software Specifications
- **Target Board**: STMicroelectronics **STM32H747I-DISCO** (Discovery Kit)
- **Main MCU**: **STM32H747XIH6** Dual-core (ARM Cortex-M7 @ 400/480MHz + Cortex-M4 @ 200/240MHz)
- **Ethernet PHY Chip**: Microchip / SMSC **LAN8742A** (10/100 Mbps RMII Transceiver)
- **Interface Mode**: RMII (50 MHz Reference Clock)
- **Clock Architecture**:
- Onboard 25 MHz Crystal (X3) or MCU `PA8 (MCO1: 25MHz HSE)` $\rightarrow$ LAN8742A internal PLL generates 50 MHz $\rightarrow$ LAN8742A Pin 5(`CLKOUT`) $\rightarrow$ **SB17** $\rightarrow$ STM32 `PA1 (ETH_REF_CLK)`
- **OS & Network Stack**: FreeRTOS (CMSIS-RTOS V2) + LwIP 2.1.2 (ST HAL Driver)
- **Network Configuration**: Static IP `192.168.31.60` (Subnet `255.255.255.0`, Gateway `192.168.31.1`), Target PC IP: `192.168.31.58`
---
## 2. Hardware Modifications: Solder Bridges / Çϵå¿þ¾î ¼Ö´õ ºê¸´Áö °³Á¶
STM32H747I-DISCO º¸µå´Â ±âº» °øÀå ÃâÇÏ ½Ã ÀϺΠÀÌ´õ³Ý ÇÉÀÌ ¿Âº¸µå MEMS ¸¶ÀÌÅ© ¹× ¿Àµð¿À ÄÚµ¦°ú ÇÉÀ» °øÀ¯ÇÕ´Ï´Ù. ÀÌ´õ³Ý RMII ±â´ÉÀ» ¿ÂÀüÈ÷ »ç¿ëÇÏ·Á¸é ¾Æ·¡¿Í °°ÀÌ ¼Ö´õ ºê¸´Áö(³³¶« Á¡ÆÛ)¸¦ °³Á¶ÇØ¾ß ÇÕ´Ï´Ù.
On the STM32H747I-DISCO, certain Ethernet RMII pins are shared with onboard MEMS microphones and audio circuitry. To use Ethernet RMII, the following solder bridge modifications are required.
| Signal / ½ÅÈ£ | STM32 Pin | Solder Bridge to OPEN (´Ü¼±) | Solder Bridge to CLOSE (¼îÆ®) | Description / ¼³¸í |
| :--- | :--- | :--- | :--- | :--- |
| **ETH_REF_CLK** | `PA1` | - | **SB17** | 50 MHz RMII Reference Clock from PHY / PHY Ŭ·° °ø±Þ |
| **ETH_MDIO** | `PA2` | - | **SB8** | MDIO Management Data / PHY ·¹Áö½ºÅÍ µ¥ÀÌÅÍ Åë½Å |
| **ETH_CRS_DV** | `PA7` | **SB45** (Disconnect Mic) | **SB44** | Carrier Sense / Data Valid (¼ö½Å ij¸®¾î °¨Áö) |
| **ETH_MDC** | `PC1` | **R87** (Disconnect Mic) | **SB21** / Direct | Management Data Clock / PHY ·¹Áö½ºÅÍ Å¬·° |
| **ETH_RXD0** | `PC4` | **SB21** (Disconnect Mic) | **SB22** | Receive Data Bit 0 / ¼ö½Å µ¥ÀÌÅÍ ºñÆ® 0 |
| **ETH_RXD1** | `PC5` | - | **Direct** | Receive Data Bit 1 / ¼ö½Å µ¥ÀÌÅÍ ºñÆ® 1 |
| **ETH_TX_EN** | `PG11` | - | **Direct** | Transmit Enable / ¼Û½Å Ȱ¼ºÈ |
| **ETH_TXD1** | `PG12` | - | **Direct** | Transmit Data Bit 1 / ¼Û½Å µ¥ÀÌÅÍ ºñÆ® 1 |
| **ETH_TXD0** | `PG13` | - | **Direct** | Transmit Data Bit 0 / ¼Û½Å µ¥ÀÌÅÍ ºñÆ® 0 |
---
## 3. Root Causes of Failure & Solutions / ÇÙ½É ¹®Á¦ ¿øÀÎ ¹× ÇØ°áÃ¥
### [KO] 3´ë ÇÙ½É ³Á¦ ¹× ¿Ïº® ÇØ°áÃ¥
#### ¨ç ¸µÄ¿ ½ºÅ©¸³Æ® ¿ÀŸ ¹× D2 ºñij½Ã RAM ¸ÅÇÎ ¹ö±×
- **¹®Á¦Á¡**: `ethernetif.c`¿¡¼´Â DMA µð½ºÅ©¸³ÅÍ ¼½¼ÇÀ» `.RxDescripSection`, `.TxDescripSection`À¸·Î ¼±¾ðÇßÀ¸³ª, ST CubeMX »ý¼º ¸µÄ¿ ½ºÅ©¸³Æ®ÀÇ ¿ÀŸ ¹× À߸øµÈ `RAM_D1` ¸ÅÇÎÀ¸·Î ÀÎÇØ µð½ºÅ©¸³ÅͰ¡ ij½Ã°¡ ÄÑÁø ÀÏ¹Ý AXI SRAM(`0x24000000`)¿¡ ¹èÄ¡µÊ. ÀÌ·Î ÀÎÇØ CPUÀÇ Àü¼Û ¿äû(`OWN=1`)À» DMA°¡ ÀÐÁö ¸øÇØ ¹«ÇÑ `BUSY` ¹× ¸ØÃ㠹߻ý.
- **ÇØ°áÃ¥**: ¸µÄ¿ ½ºÅ©¸³Æ®ÀÇ `.lwip_sec` ¼½¼ÇÀ» `RAM_D2 (0x30040000, SRAM3)`¿¡ °íÁ¤ ¹èÄ¡ÇÏ°í ½ºÆç¸µ ¿ÀŸ ¹æ¾î ÄÚµå Àû¿ë.
#### ¨è MPU (Memory Protection Unit) ºñij½Ã ¹× DMA °øÀ¯ ¼³Á¤
- **¹®Á¦Á¡**: Cortex-M7ÀÇ °í¼º´É D-Cache·Î ÀÎÇØ DMA¿Í CPU °£ µð½ºÅ©¸³ÅÍ Ç÷¡±×°¡ ºÒÀÏÄ¡µÊ.
- **ÇØ°áÃ¥**: `MPU_Config()`¿¡¼ `0x30040000` (SRAM3, 32KB) ¿µ¿ªÀ» `MPU_ACCESS_NOT_CACHEABLE`, `MPU_ACCESS_NOT_BUFFERABLE`, `MPU_ACCESS_SHAREABLE`, `MPU_TEX_LEVEL0` (Strongly-Ordered/Device ¼Ó¼º)À¸·Î ¼³Á¤.
#### ¨é Cortex-M7 D-Cache Coherency (¼ö½Å Invalidate / ¼Û½Å Clean)
- **¹®Á¦Á¡**:
- ¼Û½Å ½Ã: CPU°¡ ÀÛ¼ºÇÑ Ping ÀÀ´ä ¹× UDP µ¥ÀÌÅͰ¡ D-Cache¿¡ ¸Ó¹°·¯ RAM¿¡ ¹Ý¿µµÇÁö ¾Ê¾Æ PC¿¡¼ `MISCOMPARE at offset 28` ¿¡·¯ ¹ß»ý.
- ¼ö½Å ½Ã: Invalidate ¸í·É¾î°¡ struct pbuf ¸ÞŸµ¥ÀÌÅÍ ÀÛ¼º µÚ¿¡ À§Ä¡ÇÏ¿© ÆÐŶ ±æÀÌ Á¤º¸°¡ ¼Ò½ÇµÊ.
- **ÇØ°áÃ¥**:
- `low_level_output`: ÆÐŶ ¼Û½Å Àü `SCB_CleanDCache_by_Addr`¸¦ È£ÃâÇÏ¿© CPU ij½Ã ³»¿ëÀ» ¹°¸® RAMÀ¸·Î Áï½Ã Flush.
- `HAL_ETH_RxLinkCallback`: ¸ÞŸµ¥ÀÌÅ͸¦ ¾²±â Àü ¸Ç ù ÁÙ¿¡¼ `SCB_InvalidateDCache_by_Addr`¸¦ È£ÃâÇÏ¿© ¹°¸® RAMÀÇ ¼ö½Å ÆÐŶÀ» ÃÖ½ÅÈ.
---
### [EN] 3 Root Causes & Solutions
#### ¨ç Linker Script Section Mismatch & D2 Non-Cacheable RAM Mapping
- **Issue**: Descriptors declared in `ethernetif.c` were incorrectly placed into Cacheable `RAM_D1` due to CubeMX section naming mismatches. The Ethernet DMA was unable to see CPU descriptor updates (`OWN=1`), causing perpetual `BUSY` lockups.
- **Solution**: Explicitly map `.lwip_sec` containing `.RxDescripSection`, `.TxDescripSection`, and `.Rx_PoolSection` into `RAM_D2 (0x30040000, SRAM3)`.
#### ¨è MPU (Memory Protection Unit) Non-Cacheable & Shareable Configuration
- **Issue**: Cache coherency mismatch between Ethernet DMA and Cortex-M7 CPU on descriptor ring buffers.
- **Solution**: Configure `0x30040000` (SRAM3, 32KB) as Non-Cacheable, Non-Bufferable, and Shareable with `MPU_TEX_LEVEL0`.
#### ¨é Cortex-M7 D-Cache Management (TX Clean / RX Invalidate)
- **Issue**:
- TX: CPU payload writes remained in D-Cache without being flushed to RAM, causing `MISCOMPARE at offset 28` on Ping replies.
- RX: Invalidate was called after writing pbuf metadata, wiping out the received packet length.
- **Solution**:
- `low_level_output`: Call `SCB_CleanDCache_by_Addr` before triggering DMA transmission.
- `HAL_ETH_RxLinkCallback`: Call `SCB_InvalidateDCache_by_Addr` on the raw payload buffer *before* writing pbuf metadata.
---
## 4. Complete Code Implementation / Àüü ÇÙ½É ÄÚµå
### 1) Linker Script (`stm32h747xx_flash_CM7.ld`)
```ld
/* Specify memory areas */
MEMORY
{
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D2 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3: Non-Cacheable Ethernet Area */
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
SDRAM (xrw) : ORIGIN = 0xD0000000, LENGTH = 32M
}
SECTIONS
{
/* ... standard flash, text, data, bss sections ... */
/* Force Ethernet Descriptors and Buffers into SRAM3 (0x30040000) */
.lwip_sec (NOLOAD) :
{
. = ABSOLUTE(0x30040000);
KEEP(*(.RxDescripSection))
KEEP(*(.RxDecripSection))
. = ABSOLUTE(0x300400A0);
KEEP(*(.TxDescripSection))
KEEP(*(.TxDecripSection))
. = ABSOLUTE(0x30040140);
KEEP(*(.Rx_PoolSection))
KEEP(*(.RxArraySection))
} >RAM_D2
}
```
---
### 2) MPU Configuration (`main.c`)
```c
void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Configure Ethernet DMA Descriptors Region (SRAM3: 0x30040000, 32KB) */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x30040000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; /* DMA Shareable */
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; /* Non-Cacheable */
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; /* Non-Bufferable */
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
```
---
### 3) Hardware MSP Initialization (`stm32h7xx_hal_msp.c`)
```c
void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(heth->Instance == ETH)
{
/* 1. CRITICAL: Select RMII mode in SYSCFG BEFORE enabling Ethernet peripheral clocks */
__HAL_RCC_SYSCFG_CLK_ENABLE();
HAL_SYSCFG_ETHInterfaceSelect(SYSCFG_ETH_RMII);
/* 2. Enable GPIO Clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/** ETH GPIO Configuration
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC1 ------> ETH_MDC
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PG11 ------> ETH_TX_EN
PG12 ------> ETH_TXD1
PG13 ------> ETH_TXD0
*/
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* MDIO requires Pull-Up */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* 3. Enable Ethernet Peripheral Clocks */
__HAL_RCC_ETH1MAC_CLK_ENABLE();
__HAL_RCC_ETH1TX_CLK_ENABLE();
__HAL_RCC_ETH1RX_CLK_ENABLE();
/* 4. Configure ETH Global Interrupt */
HAL_NVIC_SetPriority(ETH_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
}
}
```
---
### 4) Low-Level Ethernet Driver (`ethernetif.c`)
```c
extern volatile uint32_t eth_rx_packet_count;
/**
* @brief TX Output function: Transmits packet with D-Cache Clean
*/
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
uint32_t i = 0U;
struct pbuf *q = NULL;
err_t errval = ERR_OK;
ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT] = {0};
ETH_TxPacketConfig tx_config;
memset(Txbuffer, 0, ETH_TX_DESC_CNT * sizeof(ETH_BufferTypeDef));
memset(&tx_config, 0, sizeof(ETH_TxPacketConfig));
tx_config.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
tx_config.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
tx_config.CRCPadCtrl = ETH_CRC_PAD_INSERT;
for(q = p; q != NULL; q = q->next)
{
if(i >= ETH_TX_DESC_CNT)
return ERR_IF;
Txbuffer[i].buffer = q->payload;
Txbuffer[i].len = q->len;
/* CRITICAL: Flush outgoing packet data from D-Cache to physical RAM */
SCB_CleanDCache_by_Addr((uint32_t *)q->payload, q->len);
if(i > 0)
{
Txbuffer[i-1].next = &Txbuffer[i];
}
if(q->next == NULL)
{
Txbuffer[i].next = NULL;
}
i++;
}
tx_config.Length = p->tot_len;
tx_config.TxBuffer = Txbuffer;
tx_config.pData = p;
pbuf_ref(p);
do
{
if(HAL_ETH_Transmit_IT(&heth, &tx_config) == HAL_OK)
{
errval = ERR_OK;
}
else
{
if(HAL_ETH_GetError(&heth) & HAL_ETH_ERROR_BUSY)
{
/* Wait for descriptors to become available */
osSemaphoreAcquire(TxPktSemaphore, ETHIF_TX_TIMEOUT);
HAL_ETH_ReleaseTxPacket(&heth);
errval = ERR_BUF;
}
else
{
errval = ERR_IF;
}
}
} while(errval == ERR_BUF);
return errval;
}
/**
* @brief RX Link Callback: Invalidate D-Cache FIRST before writing metadata
*/
void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff, uint16_t Length)
{
/* 1. Invalidate cache on packet buffer FIRST before CPU writes pbuf metadata */
SCB_InvalidateDCache_by_Addr((uint32_t *)buff, Length);
struct pbuf **ppStart = (struct pbuf **)pStart;
struct pbuf **ppEnd = (struct pbuf **)pEnd;
struct pbuf *p = NULL;
/* Get struct pbuf from buff address */
p = (struct pbuf *)(buff - offsetof(RxBuff_t, buff));
p->next = NULL;
p->tot_len = 0;
p->len = Length;
/* Chain buffer */
if (!*ppStart)
{
*ppStart = p;
}
else
{
(*ppEnd)->next = p;
}
*ppEnd = p;
/* Update total length */
for (p = *ppStart; p != NULL; p = p->next)
{
p->tot_len += Length;
}
}
/**
* @brief RX Task thread: Receives packets and routes to LwIP Stack
*/
void ethernetif_input(void* argument)
{
struct pbuf *p = NULL;
struct netif *netif = (struct netif *) argument;
for( ;; )
{
if (osSemaphoreAcquire(RxPktSemaphore, TIME_WAITING_FOR_INPUT) == osOK)
{
do
{
p = low_level_input( netif );
if (p != NULL)
{
eth_rx_packet_count++;
printf("[RX] Packet Received! len=%d (Total: %lu)\r\n", (int)p->tot_len, (unsigned long)eth_rx_packet_count);
if (netif->input( p, netif) != ERR_OK )
{
pbuf_free(p);
}
}
} while(p != NULL);
}
}
}
```
---
### 5) Main Application Thread (`main.c`)
```c
volatile uint32_t eth_rx_packet_count = 0;
void myTaskFunc01(void *argument)
{
uint32_t cnt = 0;
char txBuffer[64];
uint32_t last_tick = 0;
uint32_t last_tx_tick = 0;
extern USBH_HandleTypeDef hUsbHostHS;
extern struct netif gnetif;
static uint8_t ip_assigned = 0;
printf("\r\n========================================\r\n");
printf("[ETH] Waiting for PHY Stabilization (500ms)...\r\n");
osDelay(500);
printf("[ETH] Starting MX_LWIP_Init()...\r\n");
MX_LWIP_Init();
printf("[ETH] MX_LWIP_Init Completed!\r\n");
/* LwIP UDP PCB Creation (TX Dedicated) */
LOCK_TCPIP_CORE();
struct udp_pcb *tx_pcb = udp_new();
ip_addr_t dest_ip;
IP4_ADDR(&dest_ip, 192, 168, 31, 58); /* Target PC IP */
UNLOCK_TCPIP_CORE();
printf("[ETH] UDP TX PCB Created! (Target: 192.168.31.58:5000)\r\n");
for (;;)
{
cnt = HAL_GetTick();
/* --- TX: Send UDP Packet every 100ms --- */
if (cnt - last_tx_tick >= 100) {
last_tx_tick = cnt;
LOCK_TCPIP_CORE();
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 64, PBUF_RAM);
if (p != NULL) {
int plen = snprintf((char*)p->payload, 64, "TICK=%lu\r\n", cnt);
p->len = plen;
p->tot_len = plen;
udp_sendto(tx_pcb, p, &dest_ip, 5000);
pbuf_free(p);
}
UNLOCK_TCPIP_CORE();
}
/* --- 1000ms Periodic Status Display (UART COM4) --- */
if (cnt - last_tick >= 1000) {
last_tick = cnt;
if (eth_rx_packet_count == 0) {
printf("[RX] Waiting for Ping (0 pkts)... | [TX] UDP Sending... Tick=%lu\r\n", cnt);
} else {
printf("[RX] Received! Total: %lu pkts | [TX] UDP Sending... Tick=%lu\r\n", (unsigned long)eth_rx_packet_count, cnt);
}
}
if (netif_is_link_up(&gnetif)) {
if (ip_assigned == 0) {
printf("[ETH] Link UP! Static IP: 192.168.31.60\r\n");
ip_assigned = 1;
}
}
osDelay(1);
}
}
```
---
## 5. Verification & Results / ÃÖÁ¾ °ËÁõ °á°ú
### 1) Ping Response Verification (ICMP) / Ping ÀÀ´ä °ËÁõ
```cmd
C:\> ping 192.168.31.60 -t
192.168.31.60ÀÇ ÀÀ´ä: ¹ÙÀÌÆ®=32 ½Ã°£=1ms TTL=255
192.168.31.60ÀÇ ÀÀ´ä: ¹ÙÀÌÆ®=32 ½Ã°£=1ms TTL=255
192.168.31.60ÀÇ ÀÀ´ä: ¹ÙÀÌÆ®=32 ½Ã°£=1ms TTL=255
192.168.31.60ÀÇ ÀÀ´ä: ¹ÙÀÌÆ®=32 ½Ã°£=1ms TTL=255
```
- **°á°ú / Result**: 0% Packet Loss, Steady 1ms latency, No payload corruption (MISCOMPARE resolved).
### 2) Serial Terminal Output (ST-Link VCP COM4 @ 115200 bps)
```text
========================================
[ETH] Waiting for PHY Stabilization (500ms)...
[ETH] Starting MX_LWIP_Init()...
[ETH] ethernet_link_thread started!
[ETH] LAN8742 PHY detected at Address 0 (Reg1=0x782D)
[ETH] Cable Connected (Link Up)! Static IP: 192.168.31.60
[ETH] MX_LWIP_Init Completed!
[ETH] UDP TX PCB Created! (Target: 192.168.31.58:5000)
[ETH] Link UP! Static IP: 192.168.31.60
[RX] Packet Received! len=60 (Total: 1)
[RX] Packet Received! len=74 (Total: 2)
[RX] Received! Total: 2 pkts | [TX] UDP Sending... Tick=3891
[RX] Received! Total: 3 pkts | [TX] UDP Sending... Tick=4895
```
|
| 
|