Szybki przewodnik po niestandardowym sterowniku PHY Ethernet ESP-IDF
„Chciałbym rozpocząć nowy projekt produktu z moim ulubionym chipem, ale nie ma go w magazynie! O nie! Będę musiał zaprojektować nowy układ i opracować dla niego nowe sterowniki!” Każdy projektant doskonale zna to uczucie w dzisiejszych czasach…
Dobra wiadomość jest taka, że nie musisz się już martwić o takie rzeczy, przynajmniej w zakresie obsługi sterowników ESP-IDF Ethernet PHY. W tym artykule pokażę, jak łatwo jest stworzyć nowy sterownik Ethernet PHY.
Przede wszystkim musisz znaleźć zamiennik swojego obecnego układu PHY. Mam przyjaciela pracującego w firmie zajmującej się dystrybucją półprzewodników, więc po prostu podniosłem telefon i poprosiłem go o radę. Polecił ADIN1200. To solidny układ klasy przemysłowej z szerokim zestawem funkcji. Z drugiej strony jest droższy i trudniej go znaleźć w magazynie, jeśli nie masz szczęścia. W każdym razie jest to idealny kandydat do naszych celów demonstracyjnych, ponieważ istnieje płytka ewaluacyjna, chip pochodzi od dostawcy, który obecnie nie jest obsługiwany przez ESP-IDF (tj. ograniczamy możliwość blokowania dostawcy dla naszych klientów), a chip jest IEEE skarga 802.3. Ten ostatni fakt najbardziej pomoże nam zredukować wysiłek potrzebny do stworzenia nowego sterownika, ponieważ interfejs zarządzania między EMAC a PHY jest ustandaryzowany i korzysta z niego ESP-IDFv5.0. Sterownik Ethernet ESP-IDF zasadniczo składa się z trzech warstw:
- Sam obiekt Ethernet, który jest publicznym API i który opakowuje warstwy MAC i PHY w jedną jednostkę funkcjonalną.
- Warstwa MAC, która kontroluje zachowanie kontrolera dostępu do nośnika i zapewnia interfejs danych do aplikacji sterownika.
- Warstwa PHY, która kontroluje właściwości warstwy fizycznej i gromadzi informacje o stanie łącza.
- Wskazanie stanu łącza, które prawie zawsze jest specyficzne dla chipa.
- Inicjalizacja chipa. Ta część nie jest ściśle wymagana, ponieważ jest w większości powszechna. Warto go dodać, aby upewnić się, że używany jest oczekiwany układ.
- Konfiguracja funkcji specyficznych dla chipa (takich jak Wake on LAN, Energy Efficient Ethernet, różne możliwości diagnostyczne itp.).
Jeśli chodzi o sprzęt, najłatwiej zacząć od płytki ewaluacyjnej EVAL-ADIN1200. Ta płytka ewaluacyjna wymaga tylko kilku modyfikacji, aby można ją było podłączyć do ESP32 przez RMII. (Możesz oczywiście zacząć od nowego projektu PCB, jeśli uznasz to za bardziej odpowiednie).
Kroki przygotowania sprzętu:
1) Przestudiuj materiały na stronie produktu ADIN1200 i zapoznaj się z chipem i płytką ewaluacyjną.
2) Po zapoznaniu się z tablicą ewaluacyjną wymagane są następujące modyfikacje:
- ADIN1200 wymaga zewnętrznego 50 MHz RMII REF_CLK, dlatego należy odlutować oscylator Y1 i związane z nim kondensatory sprzęgające.
- Lutować R120 z rezystorem 0R
- Przylutuj podciągnięcia 10K na RX_CTL (wysoki) i RX_CLK (wysoki), aby skonfigurować ADIN1200 w trybie RMII.
- Aby uzyskać informacje na temat dodatkowych opcji konfiguracji, zapoznaj się z „ Tabela 5. Ustawienia konfiguracji EVAL-ADIN1200FMCZ” w Podręczniku użytkownika EVAL-ADIN1200FMCZ.
Należy zauważyć, że RMII REF_CLK musi być generowany zewnętrznie do ADIN1200 albo przez zewnętrzny oscylator 50 MHz, albo przez ESP32. Łatwiej jest użyć ESP32 do naszych celów demonstracyjnych, więc użyłem tego rozwiązania, ale pamiętaj, że wymagany jest moduł 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 ║
╚════════════════════╩═════════════════╩═════════════╝

Z punktu widzenia oprogramowania jest to jeszcze prostsze.
Kroki, aby utworzyć nowy sterownik Ethernet PHY:
1) Utwórz kopię esp_eth_phy_ip101.c lub dowolnego innego pliku źródłowego układu PHY zgodnego z IEEE 802.3 z folderu ESP-IDF /components/esp_eth/src/ do nowego folderu.
2) Zmień nazwę wszystkich wystąpień „ip101” na „adin1200”.
3) Zaktualizuj sekcję kodu „Vendor Specific Registers” o rejestry ADIN1200, których planujesz użyć. Zaktualizowałem tylko „Rejestr statusu PHY 1”, ponieważ nie planuję jeszcze korzystać z żadnych zaawansowanych funkcji.
/***************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;
}
...
… a nowy sterownik PHY jest gotowy i gotowy do użycia!
Teraz po prostu tworzymy obiekty MAC i PHY, do których jesteśmy przyzwyczajeni, i inicjalizujemy sterownik Ethernet ESP-IDF w naszej aplikacji.
#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");