คู่มือฉบับย่อไดรเวอร์ ESP-IDF Custom 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 ║
╚════════════════════╩═════════════════╩═════════════╝
จากมุมมองของซอฟต์แวร์ มันง่ายกว่าด้วยซ้ำ
ขั้นตอนในการสร้างไดรเวอร์ 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, ð_handle) == ESP_OK, NULL,
err, TAG, "Ethernet driver install failed");