ESP-IDF Custom Ethernet PHY Driver Краткое руководство

Nov 29 2022
«Я хотел бы начать разработку нового продукта с моим любимым чипом, но его нет в наличии! О, нет! Придется спроектировать новый макет и разработать для него новые драйверы!» Каждый дизайнер хорошо знает это чувство в наши дни… Хорошая новость заключается в том, что вам больше не нужно беспокоиться о таких вещах, по крайней мере, с точки зрения поддержки ESP-IDF драйвера Ethernet PHY. В этой статье я покажу вам, как просто создать новый драйвер Ethernet PHY.

«Я хотел бы начать разработку нового продукта с моим любимым чипом, но его нет в наличии! О, нет! Придется спроектировать новый макет и разработать для него новые драйверы!» Каждому дизайнеру хорошо знакомо это чувство в наши дни…

Хорошей новостью является то, что вам больше не нужно беспокоиться об этом, по крайней мере, с точки зрения поддержки драйвера ESP-IDF Ethernet PHY. В этой статье я покажу вам, как просто создать новый драйвер Ethernet PHY.

Прежде всего, вам нужно найти замену вашему текущему чипу PHY. У меня есть друг, работающий в дистрибьюторской компании полупроводников, поэтому я просто взял свой телефон и попросил у него совета. Он рекомендовал ADIN1200. Это надежный чип промышленного класса с широким набором функций.. С другой стороны, это более дорого, и его также может быть труднее найти на складе, если вам не повезет. В любом случае, это идеальный кандидат для наших демонстрационных целей, поскольку существует оценочная плата, чип от поставщика, который в настоящее время не поддерживается ESP-IDF (т. е. мы ограничиваем возможность блокировки поставщика для наших клиентов), и чип соответствует стандарту IEEE. жалоба 802.3. Последний факт больше всего поможет нам сократить усилия, необходимые для создания нового драйвера, поскольку интерфейс управления между EMAC и PHY стандартизирован, и ESP-IDFv5.0 использует его преимущества. Драйвер Ethernet ESP-IDF в основном состоит из трех уровней:

  • Сами объекты Ethernet, которые являются общедоступным API и объединяют уровни MAC и PHY в один функциональный блок.
  • Уровень MAC, который управляет поведением контроллера доступа к среде и обеспечивает интерфейс данных для приложения-драйвера.
  • Уровень PHY, который управляет свойствами физического уровня и собирает статус канала.
  • Индикация состояния канала, которая почти всегда зависит от чипа.
  • Инициализация чипа. Эта часть не является строго обязательной, так как она в основном распространена. Это хорошо, чтобы быть добавленным, чтобы гарантировать, что ожидаемый чип используется.
  • Конфигурация конкретных функций чипа (таких как Wake on LAN, Energy Efficient Ethernet, различные возможности диагностики и т. д.).

С точки зрения аппаратного обеспечения проще всего начать с оценочной платы EVAL-ADIN1200. Эта оценочная плата требует лишь нескольких модификаций для подключения к ESP32 через RMII. (Конечно, вы можете начать с нового дизайна печатной платы, если сочтете его более подходящим.)

Этапы подготовки оборудования:

1) Изучите материалы на странице продукта ADIN1200 и ознакомьтесь с чипом и оценочной платой.

2) После того, как вы ознакомитесь с оценочной платой, потребуются следующие модификации:

  • Для ADIN1200 требуется внешний 50 МГц RMII REF_CLK, поэтому выпаивайте генератор Y1 и связанные с ним разделительные конденсаторы.
  • Припой R120 с резистором 0R
  • Припаяйте подтяжки 10K к RX_CTL (высокий уровень) и RX_CLK (высокий уровень), чтобы настроить ADIN1200 в режиме RMII.
  • Дополнительные параметры конфигурации см. в « Таблице 5. Настройка конфигурации EVAL-ADIN1200FMCZ» в Руководстве пользователя EVAL-ADIN1200FMCZ.

Обратите внимание, что RMII REF_CLK должен быть сгенерирован извне для ADIN1200 либо с помощью внешнего генератора на 50 МГц, либо с помощью ESP32. Для наших демонстрационных целей проще использовать ESP32, поэтому я использовал это решение, но имейте в виду, что требуется модуль ESP32 WROOM.

╔════════════════════╦═════════════════╦═════════════╗
║ RMII Interface Pin ║  EVAL-ADIN1200  ║ ESP32 WROOM ║
╠════════════════════╬═════════════════╬═════════════╣
║ TX_EN              ║ R80             ║ GPIO21      ║
║ TXD[0]             ║ R90             ║ GPIO19      ║
║ TXD[1]             ║ R88             ║ GPIO22      ║
║ RXD[0]             ║ R99             ║ GPIO25      ║
║ RXD[1]             ║ R86             ║ GPIO26      ║
║ CRS_DV             ║ R81             ║ GPIO27      ║
║ RESET              ║ R213            ║ GPIO5       ║
║ REF_CLK            ║ C92             ║ GPIO17      ║
║ MDIO               ║ MDIO Test Point ║ GPIO18      ║
║ MDC                ║ MDC Test Point  ║ GPIO23      ║
╚════════════════════╩═════════════════╩═════════════╝

EVAL-ADIN1200 с ESP32 WROOM

С точки зрения программного обеспечения все еще проще.

Шаги по созданию нового физического драйвера Ethernet:

1) Создайте копию esp_eth_phy_ip101.c или любого другого исходного файла чипа PHY, совместимого с IEEE 802.3, из папки ESP-IDF /components/esp_eth/src/ в новую папку.

2) Переименуйте все вхождения «ip101» в «adin1200».

3) Обновите раздел кода «Регистры поставщика», указав регистры ADIN1200, которые вы планируете использовать. Я только обновил «PHY Status 1 Register», так как пока не планирую использовать какие-либо расширенные функции.

/***************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)

/* 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);

...    
    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;
            }
...

… и новый драйвер PHY готов к использованию!

Теперь мы просто создаем объекты MAC и PHY, как мы привыкли, и инициализируем Ethernet-драйвер 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");