尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

ESP32自定义以太网PHY驱动开发指南:以ADIN1200为例

ESP32自定义以太网PHY驱动开发指南:以ADIN1200为例 1. 项目概述为什么需要自定义PHY驱动在ESP32系列芯片上搞以太网开发尤其是当你手头的板子用的不是乐鑫官方SDK里已经内置支持的那几款PHY芯片时你大概率会卡在第一步网络初始化失败提示“PHY not found”。我最近就遇到了这个情况项目里用了一颗ADI的ADIN1200官方ESP-IDF的eth_phy组件里压根没它的身影。这时候你就得自己动手丰衣足食——编写一个自定义的以太网PHY驱动程序。这听起来有点硬核但说白了PHY驱动就是一个“翻译官”。ESP32内部的以太网MAC媒体访问控制器只会说一种标准的“语言”比如通过SMI/MII/RMII接口发送特定的寄存器读写命令而不同的PHY芯片如ADIN1200、LAN8720、IP101等各有各的“方言”寄存器地址、功能位定义不同。自定义驱动的核心任务就是为你的特定PHY芯片实现这套“方言”的翻译规则让MAC能够正确地配置它、读取它的状态比如链接是否建立、速度是10M还是100M、双工模式等从而建立起可靠的物理层连接。网上很多教程只讲如何配置官方已支持的PHY一旦遇到非常规芯片资料就寥寥无几。这篇指南就是来填这个坑的。我会以ADIN1200为例手把手带你走通在ESP-IDF框架下从零开始编写、集成并调试一个自定义PHY驱动的全过程。无论你用的是ADIN1200还是其他类似的需要通过SMI串行管理接口管理的PHY这套思路和方法都是通用的。2. 核心思路与驱动框架解析在动手写代码之前我们必须先吃透ESP-IDF的以太网驱动框架。它采用了典型的分层设计理解这个结构才能知道我们的自定义驱动应该插在哪个位置以及需要实现哪些功能。2.1 ESP-IDF以太网驱动分层模型ESP-IDF的以太网驱动可以粗略分为三层硬件抽象层HAL最底层直接操作ESP32的MAC和SMI控制器寄存器。这部分通常由乐鑫的esp_eth组件提供我们一般不直接触碰。PHY驱动层中间层也是我们自定义的核心。它通过标准的SMI接口与PHY芯片通信实现PHY的初始化、配置、状态查询、中断处理等。这一层需要针对具体的PHY芯片型号进行实现。网络接口层最上层基于LwIP协议栈提供标准的Socket API。驱动配置好后这一层会自动工作。当我们调用esp_eth_driver_install()来安装以太网驱动时函数需要一个eth_mac_config_t和一个eth_phy_config_t结构体作为参数。其中eth_phy_config_t里有一个关键成员phy_addrPHY的SMI地址和reset_timeout_ms等。但最重要的是我们需要提供一个eth_phy_t类型的驱动实例。eth_phy_t是什么它是一个结构体定义了一组函数指针这就是PHY驱动的“接口契约”或“操作集”。我们的自定义驱动本质上就是实现这个结构体里定义的所有函数并创建一个它的实例。2.2 自定义驱动的实现蓝图因此我们的工作就非常明确了实现驱动函数根据目标PHY芯片的数据手册编写一组C函数分别完成init,deinit,set_mediator,reset,negotiate,get_link,power_control,get_addr,del等操作。这些函数签名必须与eth_phy_t中定义的指针类型完全一致。封装驱动实例创建一个eth_phy_t类型的全局变量例如phy_adin1200将上一步实现的函数地址赋值给这个结构体的对应成员。集成到项目在应用程序的以太网初始化代码中不再使用默认的esp_eth_phy_new_xxx()而是直接使用我们自定义的phy_adin1200实例。这个过程中最耗时和最需要谨慎对待的就是第一步中的函数实现它们需要严格遵循芯片数据手册的时序和寄存器定义。注意在开始编码前请务必、反复、仔细阅读你的PHY芯片数据手册Datasheet中关于“SMI Management Interface”和“Register Set”的章节。这是你唯一且最重要的参考资料一切操作都以手册为准。3. 以ADIN1200为例的驱动实现详解接下来我们进入实战环节。我手头的ADIN1200是一颗支持10/100 Mbps的工业级单端口以太网PHY。我们假设它通过RMII接口连接ESP32并通过SMI管理最常用的方式。3.1 搭建驱动文件结构首先在项目内创建一个新的组件或目录来存放驱动代码。例如在components目录下创建phy_adin1200文件夹并包含以下文件your_project/ ├── main/ │ └── app_main.c └── components/ └── phy_adin1200/ ├── CMakeLists.txt ├── include/ │ └── phy_adin1200.h └── phy_adin1200.cCMakeLists.txt内容用于注册这个组件idf_component_register(SRCS “phy_adin1200.c INCLUDE_DIRS “include”)3.2 定义PHY寄存器与关键函数在phy_adin1200.h中我们定义芯片特定的寄存器地址。ADIN1200的寄存器地址空间与标准的IEEE 802.3 Clause 22 MII兼容但有很多扩展功能寄存器。// phy_adin1200.h #pragma once #include “esp_eth_phy.h” #include “esp_err.h” #ifdef __cplusplus extern “C” { #endif // 基础控制与状态寄存器Clause 22标准 #define ADIN1200_REG_BMCR 0x00 // 基本模式控制寄存器 #define ADIN1200_REG_BMSR 0x01 // 基本模式状态寄存器 #define ADIN1200_REG_PHYIDR1 0x02 // PHY标识符1 #define ADIN1200_REG_PHYIDR2 0x03 // PHY标识符2 #define ADIN1200_REG_ANAR 0x04 // 自协商通告寄存器 #define ADIN1200_REG_ANLPAR 0x05 // 自协商链路伙伴能力寄存器 #define ADIN1200_REG_ANER 0x06 // 自协商扩展寄存器 #define ADIN1200_REG_ANNPTR 0x07 // 自协商下一页发送 // ADIN1200特定功能寄存器需查阅手册 #define ADIN1200_REG_PHY_SCR 0x10 // 状态与控制寄存器用于获取链接状态、速度等 #define ADIN1200_REG_RX_ERR_CNT 0x1A // 接收错误计数器用于调试 // 常用寄存器位定义以BMCR为例 #define BMCR_RESET_BIT (1 15) #define BMCR_LOOPBACK_BIT (1 14) #define BMCR_AN_ENABLE_BIT (1 12) #define BMCR_POWER_DOWN_BIT (1 11) #define BMCR_ISOLATE_BIT (1 10) #define BMCR_RESTART_AN_BIT (1 9) #define BMCR_DUPLEX_MODE_BIT (1 8) #define BMCR_SPEED_SELECT_BIT (1 13) // 0 10Mbps, 1 100Mbps // 在状态寄存器或PHY_SCR中获取链接状态的位 #define PHY_SCR_LINK_STATUS_BIT (1 2) #define PHY_SCR_SPEED_BIT (1 1) // 假设此位表示速度具体看手册 #define PHY_SCR_DUPLEX_BIT (1 0) // 假设此位表示双工具体看手册 /** * brief 创建并初始化一个ADIN1200 PHY驱动实例 * * param[in] config PHY配置参数如SMI地址、复位超时等 * return eth_phy_t* 成功返回驱动实例句柄失败返回NULL */ eth_phy_t *esp_eth_phy_new_adin1200(const eth_phy_config_t *config); #ifdef __cplusplus } #endif3.3 核心驱动函数实现phy_adin1200.c这是最核心的部分。我们需要实现eth_phy_t定义的所有接口函数。这里我挑几个最关键的函数进行详细拆解。首先定义一个私有数据结构体用于保存驱动状态// phy_adin1200.c #include string.h #include “esp_log.h” #include “esp_eth.h” #include “phy_adin1200.h” static const char *TAG “adin1200_phy”; typedef struct { esp_eth_mediator_t *mediator; // 中介对象用于访问SMI读写函数 uint32_t addr; // PHY的SMI地址 bool link_status; // 当前链接状态缓存 int reset_timeout_ms; // 复位超时时间 } phy_adin1200_t;1.phy_adin1200_init- 初始化函数这个函数在驱动安装时被调用。它的核心任务是硬件复位PHY芯片。static esp_err_t phy_adin1200_init(esp_eth_phy_t *phy) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); esp_err_t ret ESP_OK; // 1. 执行硬件复位向BMCR寄存器的复位位写1 ret adin1200-mediator-phy_reg_write(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, BMCR_RESET_BIT); if (ret ! ESP_OK) { ESP_LOGE(TAG, “write BMCR reset failed”); return ret; } // 2. 等待复位完成复位位会自清除 uint32_t timeout_ms adin1200-reset_timeout_ms; uint16_t reg_val 0; while (timeout_ms--) { vTaskDelay(pdMS_TO_TICKS(10)); // 每10ms检查一次 ret adin1200-mediator-phy_reg_read(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, ®_val); if (ret ESP_OK !(reg_val BMCR_RESET_BIT)) { ESP_LOGI(TAG, “PHY reset completed”); break; } } if (reg_val BMCR_RESET_BIT) { ESP_LOGE(TAG, “PHY reset timeout”); return ESP_ERR_TIMEOUT; } // 3. 可选验证PHY ID确保通信正常 uint16_t id1, id2; adin1200-mediator-phy_reg_read(adin1200-mediator, adin1200-addr, ADIN1200_REG_PHYIDR1, id1); adin1200-mediator-phy_reg_read(adin1200-mediator, adin1200-addr, ADIN1200_REG_PHYIDR2, id2); ESP_LOGI(TAG, “PHY ID: 0x%04X%04X”, id1, id2); // 可以在这里检查ID是否与ADIN1200的预期值匹配查阅手册 // 4. 配置基础工作模式例如使能自协商 uint16_t bmcr_val BMCR_AN_ENABLE_BIT; // 使能自协商 // bmcr_val | BMCR_SPEED_SELECT_BIT; // 如果强制100M取消注释 // bmcr_val | BMCR_DUPLEX_MODE_BIT; // 如果强制全双工取消注释 ret adin1200-mediator-phy_reg_write(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, bmcr_val); if (ret ! ESP_OK) { ESP_LOGE(TAG, “configure BMCR failed”); } return ret; }实操心得复位后的等待循环是必须的。超时时间adin1200-reset_timeout_ms建议配置为500ms以上。有些PHY复位较慢时间不够会导致后续所有操作失败。2.phy_adin1200_negotiate- 启动自协商如果配置为自协商模式这个函数需要触发自协商过程。static esp_err_t phy_adin1200_negotiate(esp_eth_phy_t *phy) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); // 对于自协商通常在上一步init中配置BMCR时已经使能。 // 如果需要重新协商可以向BMCR写入 RESTART_AN 位。 uint16_t bmcr_val; esp_err_t ret adin1200-mediator-phy_reg_read(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, bmcr_val); if (ret ! ESP_OK) { return ret; } bmcr_val | BMCR_RESTART_AN_BIT; ret adin1200-mediator-phy_reg_write(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, bmcr_val); ESP_LOGI(TAG, “Auto-negotiation restarted”); return ret; }3.phy_adin1200_get_link- 获取链接状态这是驱动中最常被调用的函数之一由网络栈定期轮询用于判断网线是否插好、链接是否建立。static esp_err_t phy_adin1200_get_link(esp_eth_phy_t *phy) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); uint16_t status_reg; esp_err_t ret; // 读取状态寄存器这里以ADIN1200_REG_PHY_SCR为例具体寄存器需查手册 ret adin1200-mediator-phy_reg_read(adin1200-mediator, adin1200-addr, ADIN1200_REG_PHY_SCR, status_reg); if (ret ! ESP_OK) { return ret; } bool link_up (status_reg PHY_SCR_LINK_STATUS_BIT) ? true : false; // 链接状态发生变化时通知上层MAC驱动 if (adin1200-link_status ! link_up) { adin1200-link_status link_up; if (link_up) { uint16_t speed (status_reg PHY_SCR_SPEED_BIT) ? ETH_SPEED_100M : ETH_SPEED_10M; uint16_t duplex (status_reg PHY_SCR_DUPLEX_BIT) ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF; ESP_LOGI(TAG, “Link Up, Speed: %s, Duplex: %s”, speed ETH_SPEED_100M ? “100M” : “10M”, duplex ETH_DUPLEX_FULL ? “Full” : “Half”); // 通过mediator回调函数通知MAC驱动链接状态变化 adin1200-mediator-on_state_changed(adin1200-mediator, ETH_STATE_SPEED, (void *)speed); adin1200-mediator-on_state_changed(adin1200-mediator, ETH_STATE_DUPLEX, (void *)duplex); } else { ESP_LOGW(TAG, “Link Down”); } adin1200-mediator-on_state_changed(adin1200-mediator, ETH_STATE_LINK, (void *)link_up); } return ESP_OK; }注意事项get_link函数会被频繁调用因此其实现应尽可能高效。避免在每次调用时都进行复杂的寄存器读取或打印大量日志否则可能会影响系统性能。通常只读取一个关键的状态寄存器即可。4.phy_adin1200_reset- 软件复位除了上电硬件复位有时需要通过软件复位PHY。static esp_err_t phy_adin1200_reset(esp_eth_phy_t *phy) { // 软件复位通常就是再次触发BMCR的复位位 // 实现逻辑与init函数开头的复位部分类似但通常不需要再等待那么久或者复用init的复位流程。 // 一种简单的实现是直接调用 phy_adin1200_deinit 再调用 phy_adin1200_init。 // 这里为清晰起见我们实现一个独立的软复位 phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); esp_err_t ret adin1200-mediator-phy_reg_write(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, BMCR_RESET_BIT); if (ret ! ESP_OK) { return ret; } vTaskDelay(pdMS_TO_TICKS(50)); // 等待一个较短的时间通常软复位较快 ESP_LOGI(TAG, “Software reset performed”); return ESP_OK; }5. 其他必需函数set_mediator,get_addr,power_control,del等函数实现通常比较模板化。static esp_err_t phy_adin1200_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *mediator) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); if (mediator NULL) { return ESP_ERR_INVALID_ARG; } adin1200-mediator mediator; return ESP_OK; } static esp_err_t phy_adin1200_power_control(esp_eth_phy_t *phy, bool enable) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); uint16_t bmcr_val; esp_err_t ret adin1200-mediator-phy_reg_read(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, bmcr_val); if (ret ! ESP_OK) { return ret; } if (enable) { bmcr_val ~BMCR_POWER_DOWN_BIT; // 清除掉电位 } else { bmcr_val | BMCR_POWER_DOWN_BIT; // 置位掉电位 } return adin1200-mediator-phy_reg_write(adin1200-mediator, adin1200-addr, ADIN1200_REG_BMCR, bmcr_val); } static esp_err_t phy_adin1200_get_addr(esp_eth_phy_t *phy, uint32_t *addr) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); *addr adin1200-addr; return ESP_OK; } static esp_err_t phy_adin1200_del(esp_eth_phy_t *phy) { phy_adin1200_t *adin1200 __containerof(phy, phy_adin1200_t, parent); free(adin1200); return ESP_OK; }6. 驱动实例的创建函数这是对外暴露的API用于生成一个eth_phy_t实例。eth_phy_t *esp_eth_phy_new_adin1200(const eth_phy_config_t *config) { if (config NULL) { ESP_LOGE(TAG, “config can‘t be null”); return NULL; } phy_adin1200_t *adin1200 calloc(1, sizeof(phy_adin1200_t)); if (adin1200 NULL) { ESP_LOGE(TAG, “calloc adin1200 object failed”); return NULL; } // 填充父类 eth_phy_t 的函数指针 adin1200-parent.reset phy_adin1200_reset; adin1200-parent.init phy_adin1200_init; adin1200-parent.deinit NULL; // 如果不需特殊处理可设为NULL adin1200-parent.set_mediator phy_adin1200_set_mediator; adin1200-parent.negotiate phy_adin1200_negotiate; adin1200-parent.get_link phy_adin1200_get_link; adin1200-parent.power_control phy_adin1200_power_control; adin1200-parent.get_addr phy_adin1200_get_addr; adin1200-parent.del phy_adin1200_del; // 根据ESP-IDF版本可能还有 get_caps, set_duplex, set_speed 等函数需要查阅对应版本的 esp_eth_phy.h 并实现 // 保存配置 adin1200-addr config-phy_addr; adin1200-reset_timeout_ms config-reset_timeout_ms; adin1200-link_status false; return (adin1200-parent); }4. 在应用程序中集成与使用自定义驱动驱动编写完成后我们需要在应用程序中用它替换掉默认的PHY驱动。4.1 修改主程序以太网初始化代码在你的app_main.c或网络初始化文件中包含头文件并修改配置。#include “esp_eth.h” #include “esp_netif.h” #include “phy_adin1200.h” // 包含我们自定义驱动的头文件 void eth_init(void) { // 1. 创建网络接口 esp_netif_config_t netif_cfg ESP_NETIF_DEFAULT_ETH(); esp_netif_t *eth_netif esp_netif_new(netif_cfg); // 2. 配置MAC和PHY eth_mac_config_t mac_config ETH_MAC_DEFAULT_CONFIG(); // 根据你的板子调整引脚 eth_phy_config_t phy_config ETH_PHY_DEFAULT_CONFIG(); // 这里会设置phy_addr1, reset_timeout_ms100等默认值 // 非常重要根据你的硬件连接设置正确的PHY地址SMI地址 // ADIN1200的地址通常由硬件引脚决定常见为1或0 phy_config.phy_addr 1; phy_config.reset_timeout_ms 500; // 给复位留足时间 phy_config.reset_gpio_num -1; // 如果使用硬件复位引脚在这里指定GPIO号。我们示例中使用软件复位设为-1。 // 3. 创建MAC和PHY实例 // MAC使用ESP-IDF内置的EMAC驱动 esp_eth_mac_t *mac esp_eth_mac_new_esp32(mac_config); if (mac NULL) { ESP_LOGE(TAG, “create MAC failed”); return; } // 关键步骤使用我们自定义的ADIN1200 PHY驱动而不是 esp_eth_phy_new_xxx() esp_eth_phy_t *phy esp_eth_phy_new_adin1200(phy_config); // 调用我们写的创建函数 if (phy NULL) { ESP_LOGE(TAG, “create PHY (ADIN1200) failed”); free(mac); return; } // 4. 创建以太网驱动句柄并安装 esp_eth_config_t eth_config ETH_DEFAULT_CONFIG(mac, phy); esp_eth_handle_t eth_handle NULL; ESP_ERROR_CHECK(esp_eth_driver_install(eth_config, ð_handle)); // 5. 将驱动句柄附加到网络接口并设置事件处理器 ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle))); // 6. 注册以太网事件回调处理链接、断开等 ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL)); // 7. 启动以太网驱动 ESP_ERROR_CHECK(esp_eth_start(eth_handle)); ESP_LOGI(TAG, “Ethernet driver (with custom ADIN1200 PHY) installed and started”); }4.2 配置项目依赖确保你的CMakeLists.txt或component.mk正确包含了自定义驱动组件。 在主项目的CMakeLists.txt中# 将自定义驱动组件添加到依赖中 set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/components $ENV{IDF_PATH}/components/esp_wifi $ENV{IDF_PATH}/components/esp_eth ./components) # 或者使用 idf_component_register 的 REQUIRES 参数5. 调试与问题排查实录即使代码逻辑正确第一次调试也几乎不可能一帆风顺。以下是我在调试ADIN1200驱动时遇到的一些典型问题及解决方法。5.1 常见问题速查表问题现象可能原因排查步骤与解决方案esp_eth_driver_install失败返回ESP_ERR_TIMEOUT1. PHY硬件复位失败或超时。2. SMI通信不成功PHY地址错误、时钟极性不对、GPIO配置错误。3. 电源或晶振未正常工作。1.检查复位时序在phy_adin1200_init的复位等待循环中加入日志确认复位位是否被清除。适当增加reset_timeout_ms我试过加到1000ms。2.验证SMI通信在init函数读取PHY ID寄存器。如果读出来全是0xFF或0x00说明SMI通信失败。检查eth_mac_config_t中的smi_mdc_gpio_num和smi_mdio_gpio_num是否与硬件连接一致。用逻辑分析仪抓取MDC/MDIO波形是最直接的方法。3.检查硬件测量PHY芯片供电电压通常3.3V或2.5V、复位引脚电平、25MHz晶振是否起振。驱动安装成功但始终Link Down1. 网线未连接或对端设备问题。2. PHY自协商未成功。3.get_link函数读取的状态寄存器或状态位错误。1.换网线、换交换机端口测试排除物理层问题。2.检查自协商配置确认BMCR寄存器是否正确使能了自协商BMCR_AN_ENABLE_BIT。可以尝试强制模式禁用自协商强制设置速度和双工来测试。3.仔细核对数据手册确认用于判断链接状态的寄存器地址和位定义绝对正确。ADIN1200的链接状态可能在BMSR寄存器标准位或PHY_SCR寄存器芯片特定位。我最初就错用了寄存器导致永远读不到链接。链接状态不稳定时通时断1. 电磁干扰或信号完整性问题。2. 电源噪声。3. PHY的某些特定配置如节能模式导致。1.检查PCB布局RMII数据线、时钟线是否等长是否有包地远离噪声源。2.增加电源去耦电容在PHY的VDD引脚附近增加100nF和10uF电容。3.查阅PHY手册的“Application Notes”有些PHY需要配置额外的寄存器来优化性能或关闭某些测试模式。例如ADIN1200可能需要配置REG_CRSM_SFD等寄存器来适应特定环境。可以Ping通网关但无法访问外网或速度极慢1. MAC层配置问题如RMII时钟源选择错误。2. 网络协议栈LwIP配置问题。3. 驱动中速度/双工模式上报错误。1.确认RMII时钟ESP32作为MAC其RMII参考时钟通常50MHz必须由外部提供或内部生成配置在eth_mac_config_t的clock_config成员中。这是最容易出错的地方之一务必对照开发板原理图确认。2.检查LwIP配置在menuconfig中 (Component config - LWIP) 确认相关功能已开启如Enable IP forwarding,Enable SO_REUSE等。3.在get_link函数中打印日志确认上报给MAC的速度和双工模式是否与交换机显示的一致。5.2 高级调试技巧使用SMI读写进行寄存器探查当驱动不工作时最有效的调试手段是直接通过SMI接口读写PHY寄存器验证硬件和底层通信是否正常。你可以在驱动初始化之前或之外写一个简单的测试函数。#include “driver/gpio.h” #include “esp_eth.h” void test_phy_smi(uint32_t phy_addr) { // 临时创建一个MAC实例来获取mediator仅用于调试 eth_mac_config_t mac_cfg ETH_MAC_DEFAULT_CONFIG(); esp_eth_mac_t *test_mac esp_eth_mac_new_esp32(mac_cfg); if (test_mac NULL) return; // 获取mediator它提供了phy_reg_read/write函数 esp_eth_mediator_t *mediator test_mac-get_mediator(test_mac); uint16_t val; esp_err_t ret; // 尝试读取PHY ID寄存器 ret mediator-phy_reg_read(mediator, phy_addr, ADIN1200_REG_PHYIDR1, val); ESP_LOGI(“TEST”, “Read PHYIDR1 (0x%02X) 0x%04X, ret%d”, ADIN1200_REG_PHYIDR1, val, ret); ret mediator-phy_reg_read(mediator, phy_addr, ADIN1200_REG_PHYIDR2, val); ESP_LOGI(“TEST”, “Read PHYIDR2 (0x%02X) 0x%04X, ret%d”, ADIN1200_REG_PHYIDR2, val, ret); // 读取状态寄存器 ret mediator-phy_reg_read(mediator, phy_addr, ADIN1200_REG_BMSR, val); ESP_LOGI(“TEST”, “Read BMSR (0x%02X) 0x%04X, ret%d”, ADIN1200_REG_BMSR, val, ret); // 记得删除MAC实例 test_mac-del(test_mac); }在app_main中早期调用这个函数在以太网驱动安装前可以快速判断SMI总线是否通畅、PHY地址是否正确、芯片是否响应。如果读出的ID是0xFFFF或0x0000基本可以断定是硬件连接或SMI配置问题。5.3 利用ESP-IDF内置的调试日志确保在menuconfig中打开以太网组件的调试日志Component config - Ethernet - Enable Ethernet debug log (Verbose)这样ESP-IDF内部的以太网驱动会打印更详细的信息包括SMI读写操作、MAC状态变化等对于定位问题非常有帮助。编写自定义PHY驱动是一个需要耐心和细致的过程它紧密依赖于硬件和数据手册。成功的标志不仅仅是驱动能编译通过更是网络能够稳定地连接和通信。当你看到 “Link Up, Speed: 100M, Duplex: Full” 的日志并且能够成功Ping通网关时那种成就感是非常实在的。希望这篇基于ADIN1200的详细指南能为你实现其他PHY芯片的驱动提供清晰的路径和实用的排错方法。
返回列表