Panduan Cepat Driver PHY Ethernet Khusus ESP-IDF

Nov 29 2022
“Saya ingin memulai desain produk baru dengan chip favorit saya, tetapi stoknya sudah habis! Oh tidak! Saya harus mendesain layout baru dan mengembangkan driver baru untuk itu!” Setiap desainer tahu bahwa merasa sangat baik akhir-akhir ini… Kabar baiknya adalah Anda tidak perlu khawatir tentang hal seperti itu lagi, setidaknya dalam hal dukungan driver ESP-IDF Ethernet PHY. Pada artikel ini, saya akan menunjukkan kepada Anda betapa sederhananya membuat driver Ethernet PHY baru.

“Saya ingin memulai desain produk baru dengan chip favorit saya, tetapi stoknya sudah habis! Oh tidak! Saya harus mendesain tata letak baru dan mengembangkan driver baru untuk itu!” Setiap desainer tahu perasaan itu dengan sangat baik akhir-akhir ini…

Kabar baiknya adalah Anda tidak perlu khawatir tentang hal itu lagi, setidaknya dalam hal dukungan driver ESP-IDF Ethernet PHY. Pada artikel ini, saya akan menunjukkan kepada Anda betapa sederhananya membuat driver Ethernet PHY baru.

Pertama-tama, Anda perlu mencari pengganti chip PHY Anda saat ini. Saya punya teman yang bekerja untuk perusahaan distribusi semikonduktor jadi saya hanya mengangkat telepon saya dan meminta nasihat darinya. Dia merekomendasikan ADIN1200. Ini adalah chip kelas industri yang kuat dengan serangkaian fitur yang luas. Di sisi lain, ini lebih mahal dan mungkin juga lebih sulit ditemukan dalam stok jika Anda tidak cukup beruntung. Bagaimanapun, ini adalah kandidat yang ideal untuk tujuan demonstrasi kami, karena ada papan evaluasi, chip berasal dari vendor yang saat ini tidak didukung oleh ESP-IDF (yaitu kami membatasi kemungkinan penguncian vendor untuk pelanggan kami) dan chip tersebut adalah IEEE 802.3 keluhan. Fakta terakhir akan sangat membantu kami untuk mengurangi upaya yang diperlukan untuk pembuatan driver baru karena antarmuka manajemen antara EMAC dan PHY distandarisasi dan ESP-IDFv5.0 memanfaatkannya. Driver Ethernet ESP-IDF pada dasarnya terdiri dari tiga lapisan:

  • Objek Ethernet itu sendiri yang merupakan API publik dan yang membungkus lapisan MAC dan PHY menjadi satu unit fungsional.
  • Lapisan MAC, yang mengontrol perilaku Pengontrol Akses Media dan menyediakan antarmuka data ke aplikasi driver.
  • Lapisan PHY, yang mengontrol properti lapisan fisik dan mengumpulkan status tautan.
  • Indikasi status tautan yang hampir selalu khusus untuk chip.
  • inisialisasi chip. Bagian ini tidak terlalu dibutuhkan, karena sebagian besar umum. Ada baiknya ditambahkan untuk memastikan bahwa chip yang diharapkan digunakan.
  • Konfigurasi fitur khusus chip (seperti Wake on LAN, Energy Efficient Ethernet, berbagai kemampuan diagnostik, dll).

Dalam hal perangkat keras, paling mudah untuk memulai dengan papan evaluasi EVAL-ADIN1200. Papan evaluasi ini hanya memerlukan sedikit modifikasi untuk dihubungkan ke ESP32 melalui RMII. (Anda tentu saja dapat memulai dengan desain PCB baru jika menurut Anda lebih sesuai.)

Langkah-langkah mempersiapkan perangkat keras:

1) Pelajari materi di halaman produk ADIN1200 dan kenali chip dan papan evaluasi.

2) Setelah Anda terbiasa dengan papan evaluasi, modifikasi berikut diperlukan:

  • ADIN1200 memerlukan RMII REF_CLK 50 MHz eksternal, oleh karena itu de-solder osilator Y1 dan kapasitor kopling terkait.
  • Solder R120 dengan resistor 0R
  • Solder pull-up 10K pada RX_CTL(Tinggi) dan RX_CLK (Tinggi) untuk mengonfigurasi ADIN1200 ke mode RMII.
  • Untuk opsi konfigurasi tambahan, lihat “ Tabel 5. Pengaturan Konfigurasi EVAL-ADIN1200FMCZ” di Panduan Pengguna EVAL-ADIN1200FMCZ.

Perhatikan bahwa RMII REF_CLK perlu dibuat secara eksternal ke ADIN1200 baik oleh osilator 50 MHz eksternal atau oleh ESP32. Lebih mudah menggunakan ESP32 untuk tujuan demonstrasi kami, jadi saya menggunakan solusi itu, tetapi perlu diingat bahwa modul WROOM ESP32 diperlukan.

╔════════════════════╦═════════════════╦═════════════╗
║ 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 dengan ESP32 WROOM

Dari sudut pandang perangkat lunak, bahkan lebih sederhana.

Langkah-langkah untuk membuat driver Ethernet PHY baru:

1) Buat salinan esp_eth_phy_ip101.c atau file sumber chip PHY lain yang kompatibel dengan IEEE 802.3 dari folder ESP-IDF /components/esp_eth/src/ ke folder baru.

2) Ubah nama semua kemunculan "ip101" menjadi "adin1200".

3) Perbarui bagian kode “Vendor Specific Registers” dengan register ADIN1200 yang akan Anda gunakan. Saya baru memperbarui "Daftar Status 1 PHY" karena saya belum berencana untuk menggunakan fitur lanjutan apa pun.

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

… dan driver PHY baru selesai dan siap digunakan!

Sekarang, kita hanya membuat objek MAC dan PHY seperti biasa dan menginisialisasi driver ESP-IDF Ethernet di aplikasi kita.

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