Guía rápida del controlador ESP-IDF Ethernet personalizado PHY

Nov 29 2022
“Me gustaría comenzar un nuevo diseño de producto con mi chip favorito, ¡pero está agotado! ¡Oh, no! ¡Tendré que diseñar un nuevo diseño y desarrollar nuevos controladores para él!” Todos los diseñadores conocen muy bien ese sentimiento en estos días... La buena noticia es que ya no tiene que preocuparse por eso, al menos en términos de compatibilidad con el controlador ESP-IDF Ethernet PHY. En este artículo, le demostraré lo simple que es crear un nuevo controlador Ethernet PHY.

“Me gustaría comenzar un nuevo diseño de producto con mi chip favorito, ¡pero está agotado! ¡Oh, no! ¡Tendré que diseñar un nuevo diseño y desarrollar nuevos controladores para él!” Todos los diseñadores conocen muy bien ese sentimiento en estos días...

La buena noticia es que ya no tiene que preocuparse por eso, al menos en términos de compatibilidad con el controlador ESP-IDF Ethernet PHY. En este artículo, le demostraré lo simple que es crear un nuevo controlador Ethernet PHY.

En primer lugar, debe encontrar un reemplazo para su chip PHY actual. Tengo un amigo que trabaja para una empresa de distribución de semiconductores, así que tomé mi teléfono y le pedí un consejo. Me recomendó ADIN1200. Es un chip robusto de grado industrial con un amplio conjunto de características. Por otro lado, es más caro y también puede ser más difícil encontrarlo en stock si no tienes la suerte. En cualquier caso, es el candidato ideal para nuestros fines de demostración, ya que existe una placa de evaluación, el chip es de un proveedor que actualmente no es compatible con ESP-IDF (es decir, limitamos la posibilidad de bloqueo del proveedor para nuestros clientes) y el chip es IEEE queja 802.3. Este último hecho es el que más nos ayudará a reducir el esfuerzo necesario para la creación de un nuevo controlador, ya que se estandariza una interfaz de gestión entre EMAC y PHY y el ESP-IDFv5.0 la aprovecha. El controlador Ethernet ESP-IDF consta básicamente de tres capas:

  • Los objetos de Ethernet en sí mismos, que son la API pública y que envuelven las capas MAC y PHY en una unidad funcional.
  • La capa MAC, que controla el comportamiento de Media Access Controller y proporciona una interfaz de datos para la aplicación del controlador.
  • La capa PHY, que controla las propiedades de la capa física y recopila el estado del enlace.
  • Indicación de estado de enlace que casi siempre es específica del chip.
  • Inicialización de chips. Esta parte no es estrictamente necesaria, ya que en su mayoría es común. Es bueno agregarlo para garantizar que se use el chip esperado.
  • Configuración de características específicas del chip (como Wake on LAN, Energy Efficient Ethernet, varias capacidades de diagnóstico, etc.).

En términos de hardware, es más fácil comenzar con la placa de evaluación EVAL-ADIN1200. Esta placa de evaluación solo requiere algunas modificaciones para conectarse a ESP32 a través de RMII. (Por supuesto, puede comenzar con un nuevo diseño de PCB si lo encuentra más apropiado).

Pasos para preparar el hardware:

1) Estudie los materiales en la página del producto ADIN1200 y familiarícese con el chip y la placa de evaluación.

2) Una vez que esté familiarizado con la placa de evaluación, se requieren las siguientes modificaciones:

  • ADIN1200 requiere un RMII REF_CLK externo de 50 MHz, por lo tanto, desuelde el oscilador Y1 y sus capacitores de acoplamiento asociados.
  • Soldadura R120 con resistencia 0R
  • Suelde pull-ups de 10K en RX_CTL (Alto) y RX_CLK (Alto) para configurar ADIN1200 en modo RMII.
  • Para obtener opciones de configuración adicionales, consulte la " Tabla 5. Ajustes de configuración de EVAL-ADIN1200FMCZ" en la Guía del usuario de EVAL-ADIN1200FMCZ.

Tenga en cuenta que RMII REF_CLK debe generarse externamente a ADIN1200 mediante un oscilador externo de 50 MHz o mediante ESP32. Es más simple usar ESP32 para nuestros propósitos de demostración, así que usé esa solución, pero tenga en cuenta que se requiere el módulo 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 con ESP32 WROOM

Desde el punto de vista del software, es aún más simple.

Pasos para crear el nuevo controlador Ethernet PHY:

1) Cree una copia de esp_eth_phy_ip101.c o cualquier otro archivo fuente de chip PHY compatible con IEEE 802.3 desde la carpeta ESP-IDF /components/esp_eth/src/ a una nueva carpeta.

2) Cambie el nombre de todas las apariciones de "ip101" a "adin1200".

3) Actualice la sección de código "Registros específicos del proveedor" con los registros ADIN1200 que planea usar. Solo actualicé el "Registro de estado 1 de PHY" ya que todavía no planeo usar ninguna de las funciones avanzadas.

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

… ¡y el nuevo controlador PHY está terminado y listo para usarse!

Ahora, solo creamos objetos MAC y PHY como estamos acostumbrados e inicializamos el controlador Ethernet ESP-IDF en nuestra aplicación.

#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");