คู่มือฉบับย่อไดรเวอร์ ESP-IDF Custom Ethernet PHY

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 ใช้ประโยชน์จากมัน ไดรเวอร์อีเธอร์เน็ต ESP-IDFโดยทั่วไปประกอบด้วยสามชั้น:

  • อีเธอร์เน็ตจะคัดค้านตัวเองซึ่งเป็น API สาธารณะและรวมเลเยอร์ MAC และ PHY ไว้ในหน่วยการทำงานเดียว
  • เลเยอร์ MAC ซึ่งควบคุมพฤติกรรมของ Media Access Controller และจัดเตรียมอินเทอร์เฟซข้อมูลไปยังแอปพลิเคชันไดรเวอร์
  • ชั้น PHY ซึ่งควบคุมคุณสมบัติของชั้นทางกายภาพและรวบรวมสถานะของลิงค์
  • ตัวบ่งชี้สถานะของลิงก์ซึ่งมักจะเป็นเฉพาะชิป
  • การเริ่มต้นชิป ส่วนนี้ไม่จำเป็นเนื่องจากเป็นส่วนที่พบบ่อย เป็นการดีที่จะเพิ่มเพื่อให้แน่ใจว่ามีการใช้ชิปที่คาดไว้
  • การกำหนดค่าคุณสมบัติเฉพาะของชิป (เช่น Wake on LAN, อีเธอร์เน็ตประหยัดพลังงาน, ความสามารถในการวินิจฉัยต่างๆ เป็นต้น)

ในแง่ของฮาร์ดแวร์ เริ่มต้นง่ายที่สุดด้วยบอร์ดประเมิน EVAL-ADIN1200 บอร์ดประเมินผลนี้ต้องการการปรับเปลี่ยนเล็กน้อยเพื่อเชื่อมต่อกับ ESP32 ผ่าน RMII (แน่นอนว่าคุณสามารถเริ่มต้นด้วยการออกแบบ PCB ใหม่ได้หากคุณเห็นว่าเหมาะสมกว่า)

ขั้นตอนในการเตรียมฮาร์ดแวร์:

1) ศึกษาเอกสารที่หน้าผลิตภัณฑ์ ADIN1200และทำความคุ้นเคยกับชิปและบอร์ดประเมินผล

2) เมื่อคุณคุ้นเคยกับบอร์ดประเมินผลแล้ว จำเป็นต้องมีการปรับเปลี่ยนดังต่อไปนี้:

  • ADIN1200 ต้องการภายนอก 50 MHz RMII REF_CLK ดังนั้นจึงยกเลิกการบัดกรีออสซิลเลเตอร์ Y1 และตัวเก็บประจุแบบคัปปลิ้งที่เกี่ยวข้อง
  • บัดกรี R120 พร้อมตัวต้านทาน 0R
  • ประสานการดึงขึ้น 10K ที่ RX_CTL (สูง) และ RX_CLK (สูง) เพื่อกำหนดค่า ADIN1200 เป็นโหมด RMII
  • สำหรับตัวเลือกการกำหนดค่าเพิ่มเติม โปรดดูที่ “ ตารางที่ 5 การตั้งค่าการกำหนดค่า EVAL-ADIN1200FMCZ”ในคู่มือผู้ใช้ EVAL-ADIN1200FMCZ

โปรดทราบว่าจำเป็นต้องสร้าง RMII REF_CLK จากภายนอกไปยัง ADIN1200 โดยใช้ออสซิลเลเตอร์ภายนอก 50 MHz หรือโดย 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 PHY ใหม่:

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 ตามที่เราคุ้นเคยและเริ่มต้นไดรเวอร์อีเทอร์เน็ต 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");