硬件是ESP32-P4连接LAN8720A,正常初始化之后,设备DHCP失败

先给结论:这不像 DHCP 配置问题,更像冷上电时 LAN8720A 没有进入一个稳定的 RMII 接收状态 。你已经能看到 Link Up,并且电脑能抓到 ESP32 发出的 DHCP Discover,说明 MDIO、PHY 链路、TX 方向大概率是通的 ;但 ESP32 没拿到 IP,重点怀疑 RX 方向、RMII REF_CLK、PHY 上电/复位/strap 锁存、排线信号完整性。热插拔后恢复,本质上等价于给 LAN8720A 做了一次更"干净"的掉电重启和重新自协商。

为什么"拉低 100 µs 再拉高"不一定有效

LAN8720A 的要求不是只有 nRST 低电平 ≥100 µs。数据手册还要求:外部电源达到 80% 后,到 nRST 释放至少要 25 ms;strap 引脚必须在 nRST 释放前后满足建立/保持时间;nRST 释放沿还要单调。并且硬件复位期间,XTAL1/CLKIN 需要有时钟。也就是说,如果 50 MHz REF_CLK 此时还没稳定,或者 PHY 3.3 V 刚起来就释放复位,100 µs reset 仍然可能无效

ESP32-P4 这里还多一个坑:ESP-IDF 明确要求 RMII 的 REF_CLK 在访问 MAC/PHY 时保持稳定;P4 的 RMII 时钟若用 EMAC_CLK_EXT_IN,输入管脚只能选 GPIO32/GPIO44/GPIO50;若用 EMAC_CLK_OUT,输出只能选 GPIO23/GPIO39,并且还必须把输出时钟从外部回环到 P4 的 RMII 时钟输入脚。

所以如果你的 50 MHz 是 ESP32-P4 输出给 LAN8720A,而你在"初始化以太网栈前"就手动复位 PHY,那个时刻 P4 的 RMII clock 很可能还没输出,LAN8720A 复位条件并不满足。

我建议按这个优先级改

第一,先把 PHY 上电复位做成硬件确定的。

不要只靠软件延时。给 LAN8720A 的 nRST 加外部下拉、RC 或 reset supervisor,让它在 PHY 3.3 V 上升期间一定保持低电平,等 3.3 V 稳定后至少 25 ms 再释放。若 PHY 是通过排线供电,建议加一个受控 load switch/MOS 管,软件可以真正断电 100 ms 再上电。LAN8720A 数据手册还特别说明:输入信号不应在器件上电前被驱动为高电平;而排线连接时,ESP32 的 RMII/MDC/MDIO/RESET 线很容易通过 IO 保护结构对 PHY 形成"半上电/背供电"。这类问题单纯拉 nRST 经常解决不了。

第二,确认你的 REF_CLK 拓扑。

如果 LAN8720A 板上有独立 50 MHz 振荡器,P4 应配置为 EMAC_CLK_EXT_IN,并把 50 MHz 接到 GPIO32/GPIO44/GPIO50 之一。如果 LAN8720A 用 25 MHz 晶振并从 nINT/REFCLKO 输出 50 MHz,nINTSEL 必须通过 strap 选到 REFCLKO 模式;同时这根 50 MHz 不建议走长排线。如果是 P4 输出 50 MHz,则必须同时送到 LAN8720A CLKIN,并外部回环到 P4 的 RMII_CLK 输入脚。REF_CLK 是整个 RMII 的采样基准,Espressif 也要求这根线越短越好并保证信号完整性。

第三,检查 LAN8720A 的 strap。

LAN8720A 的 MODE[2:0]PHYAD0/RXERREGOFF/LED1nINTSEL/LED2 都是复用 strap,strap 会在 POR 或 nRST 释放时锁存;如果这些脚还连着 LED、ESP32 IO、排线负载,不能只依赖内部上下拉,要用 4.7 kΩ~10 kΩ 的外部电阻把上电瞬间的状态钉死。若冷启动和热插拔后的寄存器不同,基本就是 strap/复位时序问题。

第四,排线连接 RMII 风险很高。

RMII 是 50 MHz 单端同步接口,REF_CLKCRS_DVRXD0/1TXD0/1TX_EN 都不适合长排线飞线。TX 能发出 DHCP Discover,不代表 RX 方向采样可靠。建议至少做到:排线尽量短;REF_CLK 旁边有 GND;每几根信号夹一根 GND;P4 端或 PHY 端加 22 Ω~33 Ω 串阻做边沿阻尼;PHY 板本地 3.3 V 去耦充足;最终产品建议同板 PCB 走线。

软件上这样改,不要只改 100 µs

ESP-IDF 的 PHY 配置里本来就有硬复位拉低时间和硬复位后等待时间字段。你可以先把它们放大,排除默认时序过短的问题。

复制代码
复制代码
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();

phy_config.phy_addr = -1;                  // 调试阶段先自动探测,确认 strap 后再固定 0 或 1
phy_config.reset_gpio_num = PHY_RST_GPIO;  // 必须是真接到 LAN8720A nRST 的 GPIO

phy_config.hw_reset_assert_time_us = 10000;   // 10 ms,远大于 100 us
phy_config.post_hw_reset_delay_ms = 100;      // 复位释放后等待 PHY/REF_CLK 稳定

phy_config.reset_timeout_ms = 1000;
phy_config.autonego_timeout_ms = 5000;

// IDF v5+ 用 esp_eth_phy_new_lan87xx()
// 旧版本可能是 esp_eth_phy_new_lan8720()
esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);

但注意:如果 50 MHz 来自 ESP32-P4 的 EMAC_CLK_OUT,不要在 MAC 时钟输出前释放 LAN8720A 复位。这种情况下,手动"以太网栈初始化前 reset PHY"反而可能不满足 LAN8720A 的 reset 条件。更稳的方案是使用独立 50 MHz 振荡器同时送 PHY 和 P4,或者确保 P4 RMII clock 已经输出后再释放 PHY。

如果你要做一个和"热插拔 PHY 板"等效的软件补救,建议不是只 reset,而是受控 power-cycle PHY

复制代码
复制代码
static void phy_power_cycle(void)
{
    gpio_set_direction(PHY_RST_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_level(PHY_RST_GPIO, 0);        // 先压住复位

    gpio_set_direction(PHY_PWR_EN_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_level(PHY_PWR_EN_GPIO, 0);     // 关闭 PHY 3.3V
    vTaskDelay(pdMS_TO_TICKS(100));         // 放电,避免半上电

    gpio_set_level(PHY_PWR_EN_GPIO, 1);     // 打开 PHY 3.3V
    vTaskDelay(pdMS_TO_TICKS(50));          // 等电源稳定,≥25ms 留裕量

    // 这里必须保证 PHY 所需的 25/50MHz 时钟已经存在
    gpio_set_level(PHY_RST_GPIO, 1);        // 释放复位
    vTaskDelay(pdMS_TO_TICKS(100));         // 等 PHY strap/clock/autoneg 状态稳定
}

启动后如果 10~15 秒没有 IP_EVENT_ETH_GOT_IP,可以临时做:

复制代码
复制代码
esp_eth_stop(eth_handle);
phy_power_cycle();
esp_eth_start(eth_handle);

这不是根治,但它能验证"受控 PHY 掉电重启"是否等价于你现在的热插拔现象。

最快定位:冷启动失败和热插拔成功后各 dump 一次 PHY 寄存器

ESP-IDF 支持通过 ETH_CMD_READ_PHY_REG 读 PHY 寄存器。

复制代码
复制代码
#include "esp_eth_com.h"

static void dump_phy_regs(esp_eth_handle_t eth_handle, const char *stage)
{
    int phy_addr = -1;
    esp_eth_ioctl(eth_handle, ETH_CMD_G_PHY_ADDR, &phy_addr);
    ESP_LOGI("PHY", "[%s] phy_addr=%d", stage, phy_addr);

    const uint32_t regs[] = {0, 1, 2, 3, 4, 5, 6, 17, 18, 26, 27, 31};

    for (int i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) {
        uint32_t val = 0;
        esp_eth_phy_reg_rw_data_t r = {
            .reg_addr = regs[i],
            .reg_value_p = &val,
        };

        if (esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &r) == ESP_OK) {
            ESP_LOGI("PHY", "[%s] R%02lu = 0x%04lx",
                     stage, (unsigned long)regs[i], (unsigned long)val);
        } else {
            ESP_LOGW("PHY", "[%s] R%02lu read failed",
                     stage, (unsigned long)regs[i]);
        }
    }
}

重点看:

  • R2/R3:PHY ID 是否稳定,不应是 0x00000xffff
  • R0:是否误进 Power Down、Isolate、Loopback。
  • R1:Link、Auto-neg 是否完成。
  • R18MODE[2:0]PHYAD,冷启动失败与热插拔成功是否不同。
  • R31:AutoDone、速率/双工状态。

判断规则很直接:如果冷启动和热插拔后的寄存器不同,是 strap/复位/上电问题;如果寄存器几乎相同但冷启动收不到 DHCP Offer,是 RMII RX/REF_CLK/排线信号完整性问题

相关推荐
SUNNYSPY0012 小时前
BSS138-ASEMI中低压通用MOS管BSS138
单片机
国科安芯2 小时前
国科安芯推出商业航天级抗辐照半双工 RS485 收发器 ASC485S2Y
前端·单片机·嵌入式硬件·架构·安全性测试
嵌入式ZYXC4 小时前
第1篇:《面试题:画一个STM32最小系统电路,每个元件的作用》
stm32·单片机·嵌入式硬件·面试·职场和发展
振南的单片机世界5 小时前
printf重定向:一句fputc,串口打印任意变量
stm32·单片机·嵌入式硬件
eng八戒5 小时前
【RA-Eco-RA2L1开发板评测】基于 FSP 实现串口打印功能
单片机·嵌入式硬件
第二层皮-合肥6 小时前
【数据采集专栏】时钟同步(有时钟卡方案)
fpga开发
嵌入式ZYXC6 小时前
第2篇:《面试题:LDO和DC-DC的区别?分别用在什么场景?》
stm32·单片机·嵌入式硬件·面试·职场和发展
HAPPY酷7 小时前
单片机OLED进阶:打造赛博朋克风“碎片化消散”文字特效
单片机·嵌入式硬件·microsoft
czhaii7 小时前
GB2312简体中文编码表
单片机·算法