ESP-IDF 맞춤형 이더넷 PHY 드라이버 퀵 가이드
“내가 가장 좋아하는 칩으로 새로운 제품 디자인을 시작하고 싶은데 품절입니다! 안 돼! 새 레이아웃을 디자인하고 새 드라이버를 개발해야 합니다!” 모든 디자이너는 요즘 그 느낌을 아주 잘 알고 있습니다…
좋은 소식은 적어도 ESP-IDF 이더넷 PHY 드라이버 지원 측면에서 더 이상 걱정할 필요가 없다는 것입니다. 이 기사에서는 새 이더넷 PHY 드라이버를 만드는 것이 얼마나 간단한지 보여드리겠습니다.
우선, 현재 PHY 칩의 대체품을 찾아야 합니다. 반도체 유통 회사에 다니는 친구가 있어서 그냥 전화를 들고 조언을 구했습니다. 그는 ADIN1200을 추천했습니다. 다양한 기능을 갖춘 견고한 산업용 등급 칩입니다.. 반면에 가격이 더 비싸고 운이 좋지 않으면 재고를 찾기가 더 어려워질 수도 있습니다. 어쨌든 평가 보드가 존재하고 칩이 현재 ESP-IDF에서 지원하지 않는 공급업체에서 제공되고(즉, 고객을 위한 공급업체 잠금 가능성을 제한함) 칩이 IEEE이기 때문에 데모 목적에 이상적인 후보입니다. 802.3 불만. 마지막 사실은 EMAC와 PHY 사이의 관리 인터페이스가 표준화되고 ESP-IDFv5.0이 이를 활용하기 때문에 새로운 드라이버 생성에 필요한 노력을 줄이는 데 가장 큰 도움이 될 것입니다. ESP-IDF 이더넷 드라이버 는 기본적으로 3개의 계층으로 구성됩니다.
- 공용 API이고 MAC 및 PHY 계층을 하나의 기능 단위로 래핑하는 이더넷 객체 자체.
- Media Access Controller의 동작을 제어하고 드라이버 애플리케이션에 데이터 인터페이스를 제공하는 MAC 계층.
- 물리 계층 속성을 제어하고 링크 상태를 수집하는 PHY 계층.
- 거의 항상 칩에 따라 달라지는 링크 상태 표시.
- 칩 초기화. 이 부분은 대부분 일반적이기 때문에 꼭 필요한 것은 아닙니다. 예상 칩이 사용되는지 확인하기 위해 추가하는 것이 좋습니다.
- 칩별 기능 구성(Wake on LAN, Energy Efficient Ethernet, 다양한 진단 기능 등).
하드웨어 측면에서 EVAL-ADIN1200 평가 보드로 시작하는 것이 가장 쉽습니다. 이 평가 보드는 RMII를 통해 ESP32에 연결하기 위해 몇 가지 수정만 필요합니다. (물론 더 적절하다고 생각되면 새로운 PCB 설계로 시작할 수 있습니다.)
하드웨어 준비 단계:
1) ADIN1200 제품 페이지 에서 자료를 공부 하고 칩과 평가 보드에 익숙해지십시오.
2) 평가 보드에 익숙해지면 다음 수정이 필요합니다.
- ADIN1200에는 외부 50MHz RMII REF_CLK가 필요하므로 Y1 발진기 및 관련 결합 커패시터를 제거해야 합니다.
- 0R 저항이 있는 납땜 R120
- RX_CTL(높음) 및 RX_CLK(높음)에서 10K 풀업을 납땜하여 ADIN1200을 RMII 모드로 구성합니다.
- 추가 구성 옵션은 EVAL-ADIN1200FMCZ 사용 설명서의 " 표 5. EVAL-ADIN1200FMCZ 구성 설정" 을 참조하십시오.
RMII REF_CLK는 외부 50MHz 발진기 또는 ESP32에 의해 ADIN1200 외부에서 생성되어야 합니다. 데모 목적으로 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 ║
╚════════════════════╩═════════════════╩═════════════╝

소프트웨어 관점에서 보면 훨씬 더 간단합니다.
새 이더넷 PHY 드라이버를 만드는 단계:
1) ESP-IDF /components/esp_eth/src/ 폴더에서 새 폴더 로 esp_eth_phy_ip101.c 또는 기타 IEEE 802.3 호환 PHY 칩 소스 파일 의 복사본을 만듭니다.
2) "ip101"의 모든 항목을 "adin1200"으로 이름을 바꿉니다.
3) 사용하려는 ADIN1200 레지스터로 "Vendor Specific Registers" 코드 섹션을 업데이트합니다. 아직 고급 기능을 사용할 계획이 없기 때문에 "PHY 상태 1 레지스터"만 업데이트했습니다.
/***************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");