快速指南 :ESP-IDF 自定义以太网 PHY 驱动程序

“我想用我最喜欢的芯片开始新的产品设计,但它断货了!哦,不!我必须设计一个新的 PCB,并重新开发驱动程序!”如今,每个设计师都非常清楚这种感觉…

好消息是,至少在 ESP-IDF 以太网 PHY 驱动程序支持方面,您再也不用担心这种事情了。在本文中,我将向您演示创建一个新的以太网 PHY 驱动程序是多么简单。

首先,您需要找到当前 PHY 芯片的替代品。我刚好有个朋友在一家半导体经销公司工作,所以我直接拿起电话向他请教。这位朋友推荐了 ADIN1200。这是一款具有广泛功能的强大工业级芯片,对于我们的演示来说,是一个理想的候选产品,原因包括:

  1. 有现成的评估板;

  1. 该芯片来自目前 ESP-IDF 未支持的供应商(也就是说,我们有机会帮助客户解锁更多供应商),

  1. 该芯片符合 IEEE 802.3 标准。

正因如此,创建新驱动程序所需的工作量大大降低了,EMAC 和 PHY 之间的管理接口是标准化的,并且 ESP-IDF v5.0 也利用了这一点。ESP-IDF 以太网驱动程序ADIN1200 产品页面基本上由三层组成:

  • 以太网对象本身是公共 API,已经将 MAC 和 PHY 层封装为一个功能单元。

  • MAC 层控制媒体访问控制器的行为,并为驱动程序应用程序提供数据接口。

  • PHY 层控制物理层属性,并收集链路状态。

IEEE 802.3 标准的“22.2.4管理功能”章节定义了所谓的“MII 管理接口”,可控制 PHY 并通过 MAC 从PHY 收集状态;定义了一组管理寄存器,ESP-IDF 以太网驱动程序将此基本管理功能作为一组通用功能进行处理。因此,在准备新的 PHY 驱动程序时,您只需要关注芯片的特定功能,如:

  • 链路状态指示,这点几乎每款芯片都有不同。

  • 芯片初始化。这部分非常常见,不作严格要求。建议增加该部分,确保使用正确的芯片。

  • 芯片特定功能配置(如 LAN 唤醒、节能以太网、各种诊断功能等)。

接下来,让我们演示创建驱动程序的具体步骤

硬件方面建议可以先使用 EVAL-ADIN1200 评估板,您仅需进行少量修改即可通过 RMII 连接到ESP32。(当然,您也可以根据需求,选择从 PCB 设计从头开始。)

硬件准备:

  1. 查看 ADIN1200 产品页面,熟悉芯片和评估板的基本情况和特性。

  1. 在熟悉了评估版之后,需要进行以下修改:

  • ADIN1200 需外接一个 50 MHz RMI REF_CLK,因此需要拆焊 Y1 振荡器及其相关耦合电容器。

  • R120 处焊接 0R 电阻。

  • 在 RX_CTL (高)和 RX_CLK (高) 处焊接 10K 上拉电阻,配置 ADIN1200 进入 RMII 模式。

  • 有关其他配置选项,请见《EVAL-ADIN1200FMCZ 用户手册》中的“表5. EVAL-ADIN1200FMCZ 配置设置”。

3. 连接 ADIN1200 RMII 接口到 ESP32。我将电线焊接至暴露的 0R 电阻上(如下表所示)。请使电线尽可能短,并且长度相同。

注意,RMII REF_CLK 需要由外部 50 MHz 振荡器或 ESP32 在 ADIN1200 外部生成。在进行演示时,我使用了更简单的 ESP32,但实际上您需要使用 ESP32-WROOM 模组。

EVAL-ADIN1200 评估板的硬件修改结果见下图。

搭载 ESP32-WROOM 的 EVAL-ADIN1200

软件准备则更简单。

创建新的以太网 PHY 驱动程序的步骤:

  1. 前往 ESP-IDF 的 /components/esp_eth/src/ 文件夹,复制 esp_eth_phy_ip101.c 或任何其他 IEEE 802.3 兼容的 phy 芯片源文件至一个新的文件夹。

  1. 将所有 “ip101” 均替换为 “adin1200”。

  1. 前往“供应商特定寄存器”代码部分,将相关寄存器修改为 ADIN1200 的对应寄存器。由于我不准备使用任何高级功能,因此我这里只更新了“PHY Status 1 寄存器”。

/***************Vendor Specific Register***************/
/**
 * @brief PHY Status 1 Register
 *
 */
typedef union {
    struct {
        uint32_t lp_apause_adv : 1; /* The link partner has advertised asymmetric pause */
        uint32_t lp_pause_adv : 1; /* The link partner has advertised pause */
        uint32_t autoneg_sup : 1; /* Local and remote PHYs support autonegotiation */
        uint32_t col_stat : 1; /* Indicates that collision is asserted */
        uint32_t rx_dv_stat : 1; /* Indication that receive data valid (RX_DV) is asserted. */
        uint32_t tx_en_stat : 1; /* Indication that transmit enable (TX_EN) is asserted */
        uint32_t link_stat : 1; /* Link status */
        uint32_t hcd_tech : 3; /* Indication of the resolved technology after the link is established */
        uint32_t b_10_pol_inv : 1; /*  polarity of the 10BASE-T signal inversion */
        uint32_t pair_01_swap : 1; /* Pair 0 and Pair 1 swap */
        uint32_t autoneg_stat : 1; /* Autonegotiation Status Bit */
        uint32_t par_det_flt_stat: 1; /* Parallel Detection Fault Status Bit */
        uint32_t reserverd : 1; /* Reserved */
        uint32_t phy_in_stndby : 1; /* PHY is in standby state and does not attempt to bring up links */
    };
    uint32_t val;
} ps1r_reg_t;
#define ETH_PHY_PS1R_REG_ADDR (0x1A)
  1. 根据技术规格书中定义的“PHY Identifier ½ 寄存器”,更新 adin1200_init() 函数中的 oui 和 model。

/* Check PHY ID */
uint32_t oui;
uint8_t model;
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_manufac_info(phy_802_3, &model, NULL), err, TAG, "read manufacturer's info failed");
ESP_GOTO_ON_FALSE(oui == 0xa0ef && model == 0x02, ESP_FAIL, err, TAG, "wrong chip ID (read oui=0x%" PRIx32 ", model=0x%" PRIx8 ")", oui, model);

5. 修改 update_link_duplex_speed() 函数以读取实际协商结果。该信息在 IEEE 802.3 中并未进行标准化,因此所有 PHY 芯片都不同。ADIN1200 使用 “PHY Status 1 寄存器”存储该数据。

...    
    ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
    ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
    eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
    /* check if link status changed */
    if (adin1200->phy_802_3.link_status != link) {
        /* when link up, read negotiation result */
        if (link == ETH_LINK_UP) {
            ps1r_reg_t ps1r;
            ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_PS1R_REG_ADDR, &(ps1r.val)), err, TAG, "read PS1R failed");
            switch (ps1r.hcd_tech) {
            case 0: //10Base-T half-duplex
                speed = ETH_SPEED_10M;
                duplex = ETH_DUPLEX_HALF;
                break;
            case 1: //10Base-T full-duplex
                speed = ETH_SPEED_10M;
                duplex = ETH_DUPLEX_FULL;
                break;
            case 2: //100Base-TX half-duplex
                speed = ETH_SPEED_100M;
                duplex = ETH_DUPLEX_HALF;
                break;
            case 3: //100Base-TX full-duplex
                speed = ETH_SPEED_100M;
                duplex = ETH_DUPLEX_FULL;
                break;
            default:
                break;
            }
...

6. 使用esp_eth_phy_new_adin1200() 函数创建一个新的头文件。

…至此,新的 PHY 驱动程序已准备完成,并可以使用了!

现在,我们只需按照之前的方式创建 MAC 和 PHY 对象,并在我们的应用程序中初始化 ESP-IDF 以太网驱动程序。

#include "esp_eth_phy_adin1200.h"

// Init common MAC and PHY configs to default
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();

// Update PHY config based on board specific configuration
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
// Init vendor specific MAC config to default
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
// Update vendor specific MAC config based on board configuration
esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
// Create new ESP32 Ethernet MAC instance
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
// Create new PHY instance
esp_eth_phy_t *phy = esp_eth_phy_new_adin1200(&phy_config);
// Init Ethernet driver to default and install it
esp_eth_handle_t eth_handle = NULL;
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
ESP_GOTO_ON_FALSE(esp_eth_driver_install(&config, &eth_handle) == ESP_OK, NULL,
                    err, TAG, "Ethernet driver install failed");

ADIN1200 以太网 PHY 驱动程序的完整版本可见 ESP-IDF附加以太网驱动程序仓库或通过 IDF 组件管理器

猜你喜欢

转载自blog.csdn.net/espressif/article/details/128560147