ESP-IDF Custom Ethernet PHY Driver Краткое руководство
«Я хотел бы начать разработку нового продукта с моим любимым чипом, но его нет в наличии! О, нет! Придется спроектировать новый макет и разработать для него новые драйверы!» Каждому дизайнеру хорошо знакомо это чувство в наши дни…
Хорошей новостью является то, что вам больше не нужно беспокоиться об этом, по крайней мере, с точки зрения поддержки драйвера 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 ║
╚════════════════════╩═════════════════╩═════════════╝

С точки зрения программного обеспечения все еще проще.
Шаги по созданию нового физического драйвера 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, ð_handle) == ESP_OK, NULL,
err, TAG, "Ethernet driver install failed");