Hướng dẫn nhanh trình điều khiển PHY Ethernet tùy chỉnh ESP-IDF
“Tôi muốn bắt đầu thiết kế sản phẩm mới với con chip yêu thích của mình nhưng nó đã hết hàng! Ôi không! Tôi sẽ phải thiết kế bố cục mới và phát triển trình điều khiển mới cho nó!” Ngày nay, mọi nhà thiết kế đều biết rất rõ cảm giác đó…
Tin tốt là bạn không phải lo lắng về điều đó nữa, ít nhất là về hỗ trợ trình điều khiển ESP-IDF Ethernet PHY. Trong bài viết này, tôi sẽ chứng minh cho bạn thấy việc tạo trình điều khiển Ethernet PHY mới đơn giản như thế nào.
Trước hết, bạn cần tìm một bộ thay thế cho chip PHY hiện tại của mình. Tôi có một người bạn làm việc cho công ty phân phối chất bán dẫn nên tôi vừa nhấc điện thoại và nhờ anh ấy cho một số lời khuyên. Anh ấy đã đề xuất ADIN1200. Đó là con chip cấp công nghiệp mạnh mẽ với nhiều tính năng. Mặt khác, nó đắt hơn và cũng có thể khó tìm thấy trong kho hơn nếu bạn không đủ may mắn. Trong mọi trường hợp, đây là ứng cử viên lý tưởng cho mục đích trình diễn của chúng tôi, vì đã tồn tại bảng đánh giá, chip đến từ nhà cung cấp hiện không được hỗ trợ bởi ESP-IDF (nghĩa là chúng tôi hạn chế khả năng khóa nhà cung cấp đối với khách hàng của mình) và chip là IEEE khiếu nại 802.3. Thực tế cuối cùng sẽ giúp chúng tôi nhiều nhất để giảm nỗ lực cần thiết cho việc tạo trình điều khiển mới do giao diện quản lý giữa EMAC và PHY được chuẩn hóa và ESP-IDFv5.0 tận dụng lợi thế của nó. Trình điều khiển Ethernet ESP-IDF về cơ bản bao gồm ba lớp:
- Bản thân các đối tượng Ethernet là API công khai và bao bọc các lớp MAC và PHY thành một đơn vị chức năng.
- Lớp MAC, kiểm soát hành vi của Bộ điều khiển truy cập phương tiện và cung cấp giao diện dữ liệu cho ứng dụng trình điều khiển.
- Lớp PHY, kiểm soát các thuộc tính của lớp vật lý và thu thập trạng thái của liên kết.
- Chỉ báo trạng thái liên kết hầu như luôn dành riêng cho chip.
- Khởi tạo chip. Phần này không bắt buộc, vì nó hầu như phổ biến. Thật tốt khi được thêm vào để đảm bảo rằng con chip mong muốn được sử dụng.
- Cấu hình các tính năng cụ thể của chip (chẳng hạn như Wake on LAN, Ethernet hiệu quả năng lượng, các khả năng chẩn đoán khác nhau, v.v.).
Về phần cứng, dễ dàng nhất là bắt đầu với bảng đánh giá EVAL-ADIN1200. Bảng đánh giá này chỉ yêu cầu một vài sửa đổi để được kết nối với ESP32 thông qua RMII. (Tất nhiên, bạn có thể bắt đầu với một thiết kế PCB mới nếu bạn thấy nó phù hợp hơn.)
Các bước chuẩn bị phần cứng:
1) Nghiên cứu tài liệu tại trang sản phẩm ADIN1200 và làm quen với chip và bảng đánh giá.
2) Khi đã quen thuộc với bảng đánh giá, cần có những sửa đổi sau:
- ADIN1200 yêu cầu RMII REF_CLK 50 MHz bên ngoài, do đó bộ tạo dao động Y1 khử mối hàn và các tụ điện ghép nối liên quan của nó.
- Hàn R120 với điện trở 0R
- Hàn các pull-up 10K ở RX_CTL(Cao) và RX_CLK (Cao) để định cấu hình ADIN1200 ở chế độ RMII.
- Để biết các tùy chọn cấu hình bổ sung, hãy tham khảo “ Bảng 5. Cài đặt cấu hình EVAL-ADIN1200FMCZ” trong Hướng dẫn sử dụng EVAL-ADIN1200FMCZ.
Lưu ý rằng RMII REF_CLK cần được tạo bên ngoài cho ADIN1200 bằng bộ tạo dao động 50 MHz bên ngoài hoặc bằng ESP32. Việc sử dụng ESP32 cho mục đích trình diễn của chúng tôi sẽ đơn giản hơn nên tôi đã sử dụng giải pháp đó, nhưng hãy nhớ rằng mô-đun WROOM ESP32 là bắt buộc.
╔════════════════════╦═════════════════╦═════════════╗
║ 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 ║
╚════════════════════╩═════════════════╩═════════════╝

Từ quan điểm phần mềm, nó thậm chí còn đơn giản hơn.
Các bước để tạo trình điều khiển Ethernet PHY mới:
1) Tạo một bản sao của esp_eth_phy_ip101.c hoặc bất kỳ tệp nguồn chip PHY tương thích IEEE 802.3 nào khác từ thư mục ESP-IDF /components/esp_eth/src/ sang một thư mục mới.
2) Đổi tên tất cả các lần xuất hiện của “ip101” thành “adin1200”.
3) Cập nhật phần mã "Đăng ký dành riêng cho nhà cung cấp" với các đăng ký ADIN1200 mà bạn định sử dụng. Tôi mới chỉ cập nhật “Đăng ký Trạng thái PHY 1” vì tôi chưa định sử dụng bất kỳ tính năng nâng cao nào.
/***************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;
}
...
… và trình điều khiển PHY mới đã hoàn tất và sẵn sàng để sử dụng!
Bây giờ, chúng ta chỉ cần tạo các đối tượng MAC và PHY như chúng ta đã quen và khởi tạo trình điều khiển Ethernet ESP-IDF trong ứng dụng của mình.
#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");