diff --git a/components/esp_eth/CMakeLists.txt b/components/esp_eth/CMakeLists.txt index 5c17f8679..7f41a8409 100644 --- a/components/esp_eth/CMakeLists.txt +++ b/components/esp_eth/CMakeLists.txt @@ -8,6 +8,11 @@ if(CONFIG_IDF_TARGET_ESP32) list(APPEND esp_eth_srcs "src/esp_eth_mac_esp32.c") endif() +if(CONFIG_ETH_SPI_ETHERNET_DM9051) + list(APPEND esp_eth_srcs "src/esp_eth_mac_dm9051.c" + "src/esp_eth_phy_dm9051.c") +endif() + idf_component_register(SRCS "${esp_eth_srcs}" INCLUDE_DIRS "include" LDFRAGMENTS "linker.lf" diff --git a/components/esp_eth/Kconfig b/components/esp_eth/Kconfig index 275e0d75e..40e7ee36d 100644 --- a/components/esp_eth/Kconfig +++ b/components/esp_eth/Kconfig @@ -2,7 +2,7 @@ menu "Ethernet" menuconfig ETH_USE_ESP32_EMAC depends on IDF_TARGET_ESP32 - bool "Use ESP32 internal EMAC controller" + bool "Support ESP32 internal EMAC controller" default y help ESP32 integrates a 10/100M Ethernet MAC controller. @@ -128,4 +128,29 @@ menu "Ethernet" Number of DMA transmit buffers. Each buffer's size is ETH_DMA_BUFFER_SIZE. Larger number of buffers could increase throughput somehow. endif + + menuconfig ETH_USE_SPI_ETHERNET + bool "Support SPI to Ethernet Module" + default y + help + ESP-IDF can also support some SPI-Ethernet module. + + if ETH_USE_SPI_ETHERNET + menuconfig ETH_SPI_ETHERNET_DM9051 + bool "Use DM9051" + default y + help + DM9051 is a fast Ethernet controller with an SPI interface. + It's also integrated with a 10/100M PHY and MAC. + Set true to enable DM9051 driver. + + if ETH_SPI_ETHERNET_DM9051 + config ETH_DM9051_INT_GPIO + int "DM9051 Interrupt GPIO number" + default 4 + range 0 33 + help + Set the GPIO number used by DM9051's Interrupt pin. + endif + endif endmenu diff --git a/components/esp_eth/component.mk b/components/esp_eth/component.mk index 3f0290192..2a5ba23b3 100644 --- a/components/esp_eth/component.mk +++ b/components/esp_eth/component.mk @@ -8,3 +8,7 @@ COMPONENT_ADD_LDFRAGMENTS += linker.lf ifndef CONFIG_IDF_TARGET_ESP32 COMPONENT_OBJEXCLUDE += src/esp_eth_mac_esp32.o endif + +ifndef CONFIG_ETH_SPI_ETHERNET_DM9051 + COMPONENT_OBJEXCLUDE += src/esp_eth_mac_dm9051.o src/esp_eth_phy_dm9051.o +endif diff --git a/components/esp_eth/include/esp_eth_mac.h b/components/esp_eth/include/esp_eth_mac.h index 3c3bd7032..07507cefe 100644 --- a/components/esp_eth/include/esp_eth_mac.h +++ b/components/esp_eth/include/esp_eth_mac.h @@ -20,6 +20,11 @@ extern "C" { #include #include "esp_eth_com.h" #include "sdkconfig.h" +#if CONFIG_ETH_USE_SPI_ETHERNET +#include "driver/gpio.h" +#include "driver/spi_master.h" +#endif + /** * @brief Ethernet MAC @@ -115,6 +120,7 @@ struct esp_eth_mac_s { * - ESP_OK: read PHY register successfully * - ESP_ERR_INVALID_ARG: read PHY register failed because of invalid argument * - ESP_ERR_INVALID_STATE: read PHY register failed because of wrong state of MAC + * - ESP_ERR_TIMEOUT: read PHY register failed because of timeout * - ESP_FAIL: read PHY register failed because some other error occurred * */ @@ -131,6 +137,7 @@ struct esp_eth_mac_s { * @return * - ESP_OK: write PHY register successfully * - ESP_ERR_INVALID_STATE: write PHY register failed because of wrong state of MAC + * - ESP_ERR_TIMEOUT: write PHY register failed because of timeout * - ESP_FAIL: write PHY register failed because some other error occurred * */ @@ -241,6 +248,10 @@ typedef struct { uint32_t rx_task_stack_size; /*!< Stack size of the receive task */ uint32_t rx_task_prio; /*!< Priority of the receive task */ uint32_t queue_len; /*!< Length of the transaction queue */ +#if CONFIG_ETH_USE_SPI_ETHERNET + spi_device_handle_t spi_hdl; /*!< Handle of spi device */ +#endif + } eth_mac_config_t; /** @@ -255,6 +266,7 @@ typedef struct { .queue_len = 100, \ } +#if CONFIG_ETH_USE_ESP32_EMAC /** * @brief Create ESP32 Ethernet MAC instance * @@ -265,7 +277,20 @@ typedef struct { * - NULL: create MAC instance failed because some error occurred */ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config); +#endif +#if CONFIG_ETH_SPI_ETHERNET_DM9051 +/** +* @brief Create DM9051 Ethernet MAC instance +* +* @param config: Ethernet MAC configuration +* +* @return +* - instance: create MAC instance successfully +* - NULL: create MAC instance failed because some error occurred +*/ +esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_mac_config_t *config); +#endif #ifdef __cplusplus } #endif diff --git a/components/esp_eth/include/esp_eth_phy.h b/components/esp_eth/include/esp_eth_phy.h index 9d29b3149..b30fdad5d 100644 --- a/components/esp_eth/include/esp_eth_phy.h +++ b/components/esp_eth/include/esp_eth_phy.h @@ -222,6 +222,18 @@ esp_eth_phy_t *esp_eth_phy_new_lan8720(const eth_phy_config_t *config); */ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config); +#if CONFIG_ETH_SPI_ETHERNET_DM9051 +/** +* @brief Create a PHY instance of DM9051 +* +* @param[in] config: configuration of PHY +* +* @return +* - instance: create PHY instance successfully +* - NULL: create PHY instance failed because some error occurred +*/ +esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config); +#endif #ifdef __cplusplus } #endif diff --git a/components/esp_eth/linker.lf b/components/esp_eth/linker.lf index e8ec8886c..69f864d8d 100644 --- a/components/esp_eth/linker.lf +++ b/components/esp_eth/linker.lf @@ -1,8 +1,11 @@ [mapping:esp_eth] archive: libesp_eth.a entries: + if ETH_USE_ESP32_EMAC = y: esp_eth_mac_esp32:emac_hal_tx_complete_cb (noflash_text) esp_eth_mac_esp32:emac_hal_tx_unavail_cb (noflash_text) esp_eth_mac_esp32:emac_hal_rx_complete_cb (noflash_text) esp_eth_mac_esp32:emac_hal_rx_early_cb (noflash_text) esp_eth_mac_esp32:emac_hal_rx_unavail_cb (noflash_text) + if ETH_SPI_ETHERNET_DM9051 = y: + esp_eth_mac_dm9051:dm9051_isr_handler (noflash_text) diff --git a/components/esp_eth/src/esp_eth_mac_dm9051.c b/components/esp_eth/src/esp_eth_mac_dm9051.c new file mode 100644 index 000000000..b76c5a4fd --- /dev/null +++ b/components/esp_eth/src/esp_eth_mac_dm9051.c @@ -0,0 +1,852 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_log.h" +#include "esp_eth.h" +#include "esp_system.h" +#include "esp_intr_alloc.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "sdkconfig.h" + +static const char *TAG = "emac_dm9051"; +#define MAC_CHECK(a, str, goto_tag, ret_value, ...) \ + do \ + { \ + if (!(a)) \ + { \ + ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret = ret_value; \ + goto goto_tag; \ + } \ + } while (0) + +#define RX_QUEUE_WAIT_MS (100) +#define DM9051_SPI_LOCK_TIMEOUT_MS (50) +#define DM9051_PHY_OPERATION_TIMEOUT_US (1000) + +/** + * @brief Registers in DM9051 + * + */ +#define DM9051_NCR (0x00) // Network Control Register +#define DM9051_NSR (0x01) // Network Status Register +#define DM9051_TCR (0x02) // Tx Control Register +#define DM9051_TSR1 (0x03) // Tx Status Register I +#define DM9051_TSR2 (0x04) // Tx Status Register II +#define DM9051_RCR (0x05) // Rx Control Register +#define DM9051_RSR (0x06) // Rx Status Register +#define DM9051_ROCR (0x07) // Receive Overflow Counter Register +#define DM9051_BPTR (0x08) // Back Pressure Threshold Register +#define DM9051_FCTR (0x09) // Flow Control Threshold Register +#define DM9051_FCR (0x0A) // Rx/Tx Flow Control Register +#define DM9051_EPCR (0x0B) // EEPROM & PHY Control Register +#define DM9051_EPAR (0x0C) // EEPROM & PHY Address Register +#define DM9051_EPDRL (0x0D) // EEPROM & PHY Data Register Low +#define DM9051_EPDRH (0x0E) // EEPROM & PHY Data Register High +#define DM9051_WCR (0x0F) // Wake Up Control Register +#define DM9051_PAR (0x10) // Physical Address Register +#define DM9051_MAR (0x16) // Multicast Address Hash Table Register +#define DM9051_GPCR (0x1E) // General Purpose Control Register +#define DM9051_GPR (0x1F) // General Purpose Register +#define DM9051_TRPAL (0x22) // Tx Memory Read Pointer Address Low Byte +#define DM9051_TRPAH (0x23) // Tx Memory Read Pointer Address High Byte +#define DM9051_RWPAL (0x24) // Rx Memory Read Pointer Address Low Byte +#define DM9051_RWPAH (0x25) // Rx Memory Read Pointer Address High Byte +#define DM9051_VIDL (0x28) // Vendor ID Low Byte +#define DM9051_VIDH (0x29) // Vendor ID High Byte +#define DM9051_PIDL (0x2A) // Product ID Low Byte +#define DM9051_PIDH (0x2B) // Product ID High Byte +#define DM9051_CHIPR (0x2C) // CHIP Revision +#define DM9051_TCR2 (0x2D) // Transmit Control Register 2 +#define DM9051_ATCR (0x30) // Auto-Transmit Control Register +#define DM9051_TCSCR (0x31) // Transmit Check Sum Control Register +#define DM9051_RCSCSR (0x32) // Receive Check Sum Control Status Register +#define DM9051_SBCR (0x38) // SPI Bus Control Register +#define DM9051_INTCR (0x39) // INT Pin Control Register +#define DM9051_PPCSR (0x3D) // Pause Packet Control Status Register +#define DM9051_EEE_IN (0x3E) // IEEE 802.3az Enter Counter Register +#define DM9051_EEE_OUT (0x3F) // IEEE 802.3az Leave Counter Register +#define DM9051_ALNCR (0x4A) // SPI Byte Align Error Counter Register +#define DM9051_RLENCR (0x52) // Rx Packet Length Control Register +#define DM9051_BCASTCR (0x53) // RX Broadcast Control Register +#define DM9051_INTCKCR (0x54) // INT Pin Clock Output Control Register +#define DM9051_MPTRCR (0x55) // Memory Pointer Control Register +#define DM9051_MLEDCR (0x57) // More LED Control Register +#define DM9051_MEMSCR (0x59) // Memory Control Register +#define DM9051_TMEMR (0x5A) // Transmit Memory Size Register +#define DM9051_MBSR (0x5D) // Memory BIST Status Register +#define DM9051_MRCMDX (0x70) // Memory Data Pre-Fetch Read Command Without Address Increment Register +#define DM9051_MRCMDX1 (0x71) // Memory Read Command Without Pre-Fetch and Without Address Increment Register +#define DM9051_MRCMD (0x72) // Memory Data Read Command With Address Increment Register +#define DM9051_SDR_DLY (0x73) // SPI Data Read Delay Counter Register +#define DM9051_MRRL (0x74) // Memory Data Read Address Register Low Byte +#define DM9051_MRRH (0x75) // Memory Data Read Address Register High Byte +#define DM9051_MWCMDX (0x76) // Memory Data Write Command Without Address Increment Register +#define DM9051_MWCMD (0x78) // Memory Data Write Command With Address Increment Register +#define DM9051_MWRL (0x7A) // Memory Data Write Address Register Low Byte +#define DM9051_MWRH (0x7B) // Memory Data Write Address Register High Byte +#define DM9051_TXPLL (0x7C) // TX Packet Length Low Byte Register +#define DM9051_TXPLH (0x7D) // TX Packet Length High Byte Register +#define DM9051_ISR (0x7E) // Interrupt Status Register +#define DM9051_IMR (0x7F) // Interrupt Mask Register + +/** + * @brief status and flag of DM9051 specific registers + * + */ +#define DM9051_SPI_RD (0) // Burst Read Command +#define DM9051_SPI_WR (1) // Burst Write Command + +#define NCR_WAKEEN (1 << 6) // Enable Wakeup Function +#define NCR_FDX (1 << 3) // Duplex Mode of the Internal PHY +#define NCR_RST (1 << 0) // Software Reset and Auto-Clear after 10us + +#define NSR_SPEED (1 << 7) // Speed of Internal PHY +#define NSR_LINKST (1 << 6) // Link Status of Internal PHY +#define NSR_WAKEST (1 << 5) // Wakeup Event Status +#define NSR_TX2END (1 << 3) // TX Packet Index II Complete Status +#define NSR_TX1END (1 << 2) // TX Packet Index I Complete Status +#define NSR_RXOV (1 << 1) // RX Memory Overflow Status +#define NSR_RXRDY (1 << 0) // RX Packet Ready + +#define TCR_TXREQ (1 << 0) // TX Request. Auto-Clear after Sending Completely + +#define RCR_WTDIS (1 << 6) // Watchdog Timer Disable +#define RCR_DIS_LONG (1 << 5) // Discard Long Packet +#define RCR_DIS_CRC (1 << 4) // Discard CRC Error Packet +#define RCR_ALL (1 << 3) // Receive All Multicast +#define RCR_RUNT (1 << 2) // Receive Runt Packet +#define RCR_PRMSC (1 << 1) // Promiscuous Mode +#define RCR_RXEN (1 << 0) // RX Enable + +#define RSR_RF (1 << 7) // Runt Frame +#define RSR_MF (1 << 6) // Multicast Frame +#define RSR_LCS (1 << 5) // Late Collision Seen +#define RSR_RWTO (1 << 4) // Receive Watchdog Time-Out +#define RSR_PLE (1 << 3) // Physical Layer Error +#define RSR_AE (1 << 2) // Alignment Error +#define RSR_CE (1 << 1) // CRC Error +#define RSR_FOE (1 << 0) // RX Memory Overflow Error + +#define FCR_FLOW_ENABLE (0x39) // Enable Flow Control + +#define EPCR_REEP (1 << 5) // Reload EEPROM +#define EPCR_WEP (1 << 4) // Write EEPROM Enable +#define EPCR_EPOS (1 << 3) // EEPROM or PHY Operation Select +#define EPCR_ERPRR (1 << 2) // EEPROM Read or PHY Register Read Command +#define EPCR_ERPRW (1 << 1) // EEPROM Write or PHY Register Write Command +#define EPCR_ERRE (1 << 0) // EEPROM Access Status or PHY Access Status + +#define TCR2_RLCP (1 << 6) // Retry Late Collision Packet + +#define ATCR_AUTO_TX (1 << 7) // Auto-Transmit Control + +#define TCSCR_UDPCSE (1 << 2) // UDP CheckSum Generation +#define TCSCR_TCPCSE (1 << 1) // TCP CheckSum Generation +#define TCSCR_IPCSE (1 << 0) // IPv4 CheckSum Generation + +#define MPTRCR_RST_TX (1 << 1) // Reset TX Memory Pointer +#define MPTRCR_RST_RX (1 << 0) // Reset RX Memory Pointer + +#define ISR_LNKCHGS (1 << 5) // Link Status Change +#define ISR_ROO (1 << 3) // Receive Overflow Counter Overflow +#define ISR_ROS (1 << 2) // Receive Overflow +#define ISR_PT (1 << 1) // Packet Transmitted +#define ISR_PR (1 << 0) // Packet Received +#define ISR_CLR_STATUS (ISR_LNKCHGS | ISR_ROO | ISR_ROS | ISR_PT | ISR_PR) + +#define IMR_PAR (1 << 7) // Pointer Auto-Return Mode +#define IMR_LNKCHGI (1 << 5) // Enable Link Status Change Interrupt +#define IMR_ROOI (1 << 3) // Enable Receive Overflow Counter Overflow Interrupt +#define IMR_ROI (1 << 2) // Enable Receive Overflow Interrupt +#define IMR_PTI (1 << 1) // Enable Packet Transmitted Interrupt +#define IMR_PRI (1 << 0) // Enable Packet Received Interrupt +#define IMR_ALL (IMR_PAR | IMR_LNKCHGI | IMR_ROOI | IMR_ROI | IMR_PTI | IMR_PRI) + +typedef struct { + uint8_t flag; + uint8_t status; + uint8_t length_low; + uint8_t length_high; +} dm9051_rx_header_t; + +typedef struct { + esp_eth_mac_t parent; + esp_eth_mediator_t *eth; + spi_device_handle_t spi_hdl; + SemaphoreHandle_t spi_lock; + TaskHandle_t rx_task_hdl; + uint32_t sw_reset_timeout_ms; + uint8_t addr[6]; + bool packets_remain; +} emac_dm9051_t; + +static inline bool dm9051_lock(emac_dm9051_t *emac) +{ + return xSemaphoreTake(emac->spi_lock, DM9051_SPI_LOCK_TIMEOUT_MS) == pdTRUE; +} + +static inline bool dm9051_unlock(emac_dm9051_t *emac) +{ + return xSemaphoreGive(emac->spi_lock) == pdTRUE; +} + +/** + * @brief write value to dm9051 internal register + */ +static esp_err_t dm9051_register_write(emac_dm9051_t *emac, uint8_t reg_addr, uint8_t value) +{ + esp_err_t ret = ESP_OK; + spi_transaction_t trans = { + .cmd = DM9051_SPI_WR, + .addr = reg_addr, + .length = 8, + .flags = SPI_TRANS_USE_TXDATA + }; + trans.tx_data[0] = value; + if (dm9051_lock(emac)) { + if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { + ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); + ret = ESP_FAIL; + } + dm9051_unlock(emac); + } else { + ret = ESP_ERR_TIMEOUT; + } + return ret; +} + +/** + * @brief read value from dm9051 internal register + */ +static esp_err_t dm9051_register_read(emac_dm9051_t *emac, uint8_t reg_addr, uint8_t *value) +{ + esp_err_t ret = ESP_OK; + spi_transaction_t trans = { + .cmd = DM9051_SPI_RD, + .addr = reg_addr, + .length = 8, + .flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA + }; + if (dm9051_lock(emac)) { + if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { + ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); + ret = ESP_FAIL; + } else { + *value = trans.rx_data[0]; + } + dm9051_unlock(emac); + } else { + ret = ESP_ERR_TIMEOUT; + } + return ret; +} + +/** + * @brief write buffer to dm9051 internal memory + */ +static esp_err_t dm9051_memory_write(emac_dm9051_t *emac, uint8_t *buffer, uint32_t len) +{ + esp_err_t ret = ESP_OK; + spi_transaction_t trans = { + .cmd = DM9051_SPI_WR, + .addr = DM9051_MWCMD, + .length = len * 8, + .tx_buffer = buffer + }; + if (dm9051_lock(emac)) { + if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { + ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); + ret = ESP_FAIL; + } + dm9051_unlock(emac); + } else { + ret = ESP_ERR_TIMEOUT; + } + return ret; +} + +/** + * @brief read buffer from dm9051 internal memory + */ +static esp_err_t dm9051_memory_read(emac_dm9051_t *emac, uint8_t *buffer, uint32_t len) +{ + esp_err_t ret = ESP_OK; + spi_transaction_t trans = { + .cmd = DM9051_SPI_RD, + .addr = DM9051_MRCMD, + .length = len * 8, + .rx_buffer = buffer + }; + if (dm9051_lock(emac)) { + if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { + ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); + ret = ESP_FAIL; + } + dm9051_unlock(emac); + } else { + ret = ESP_ERR_TIMEOUT; + } + return ret; +} + +/** + * @brief read mac address from internal registers + */ +static esp_err_t dm9051_get_mac_addr(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + for (int i = 0; i < 6; i++) { + MAC_CHECK(dm9051_register_read(emac, DM9051_PAR + i, &emac->addr[i]) == ESP_OK, "read PAR failed", err, ESP_FAIL); + } + return ESP_OK; +err: + return ret; +} + +/** + * @brief set new mac address to internal registers + */ +static esp_err_t dm9051_set_mac_addr(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + for (int i = 0; i < 6; i++) { + MAC_CHECK(dm9051_register_write(emac, DM9051_PAR + i, emac->addr[i]) == ESP_OK, "write PAR failed", err, ESP_FAIL); + } + return ESP_OK; +err: + return ret; +} + +/** + * @brief clear multicast hash table + */ +static esp_err_t dm9051_clear_multicast_table(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + /* rx broadcast packet control by bit7 of MAC register 1DH */ + MAC_CHECK(dm9051_register_write(emac, DM9051_BCASTCR, 0x00) == ESP_OK, "write BCASTCR failed", err, ESP_FAIL); + for (int i = 0; i < 7; i++) { + MAC_CHECK(dm9051_register_write(emac, DM9051_MAR + i, 0x00) == ESP_OK, "write MAR failed", err, ESP_FAIL); + } + /* enable receive broadcast paclets */ + MAC_CHECK(dm9051_register_write(emac, DM9051_MAR + 7, 0x80) == ESP_OK, "write MAR failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +/** + * @brief software reset dm9051 internal register + */ +static esp_err_t dm9051_reset(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + /* power on phy */ + MAC_CHECK(dm9051_register_write(emac, DM9051_GPR, 0x00) == ESP_OK, "write GPR failed", err, ESP_FAIL); + /* mac and phy register won't be accesable within at least 1ms */ + vTaskDelay(pdMS_TO_TICKS(10)); + /* software reset */ + uint8_t ncr = NCR_RST; + MAC_CHECK(dm9051_register_write(emac, DM9051_NCR, ncr) == ESP_OK, "write NCR failed", err, ESP_FAIL); + uint32_t to = 0; + for (to = 0; to < emac->sw_reset_timeout_ms / 10; to++) { + MAC_CHECK(dm9051_register_read(emac, DM9051_NCR, &ncr) == ESP_OK, "read NCR failed", err, ESP_FAIL); + if (!(ncr & NCR_RST)) { + break; + } + vTaskDelay(pdMS_TO_TICKS(10)); + } + MAC_CHECK(to < emac->sw_reset_timeout_ms / 10, "reset timeout", err, ESP_ERR_TIMEOUT); + return ESP_OK; +err: + return ret; +} + +/** + * @brief verify dm9051 chip ID + */ +static esp_err_t dm9051_verify_id(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + uint8_t id[2]; + MAC_CHECK(dm9051_register_read(emac, DM9051_VIDL, &id[0]) == ESP_OK, "read VIDL failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_read(emac, DM9051_VIDH, &id[1]) == ESP_OK, "read VIDH failed", err, ESP_FAIL); + MAC_CHECK(0x0A46 == *(uint16_t *)id, "wrong Vendor ID", err, ESP_ERR_INVALID_VERSION); + MAC_CHECK(dm9051_register_read(emac, DM9051_PIDL, &id[0]) == ESP_OK, "read PIDL failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_read(emac, DM9051_PIDH, &id[1]) == ESP_OK, "read PIDH failed", err, ESP_FAIL); + MAC_CHECK(0x9051 == *(uint16_t *)id, "wrong Product ID", err, ESP_ERR_INVALID_VERSION); + return ESP_OK; +err: + return ret; +} + +/** + * @brief default setup for dm9051 internal registers + */ +static esp_err_t dm9051_setup_default(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + /* disable wakeup */ + MAC_CHECK(dm9051_register_write(emac, DM9051_NCR, 0x00) == ESP_OK, "write NCR failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_write(emac, DM9051_WCR, 0x00) == ESP_OK, "write WCR failed", err, ESP_FAIL); + /* stop transmitting, enable appending pad, crc for packets */ + MAC_CHECK(dm9051_register_write(emac, DM9051_TCR, 0x00) == ESP_OK, "write TCR failed", err, ESP_FAIL); + /* stop receiving, no promiscuous mode, no runt packet(size < 64bytes), not all multicast packets*/ + /* discard long packet(size > 1522bytes) and crc error packet, enable watchdog */ + MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC) == ESP_OK, "write RCR failed", err, ESP_FAIL); + /* send jam pattern (duration time = 1.15ms) when rx free space < 3k bytes */ + MAC_CHECK(dm9051_register_write(emac, DM9051_BPTR, 0x3F) == ESP_OK, "write BPTR failed", err, ESP_FAIL); + /* flow control: high water threshold = 3k bytes, low water threshold = 8k bytes */ + MAC_CHECK(dm9051_register_write(emac, DM9051_FCTR, 0x38) == ESP_OK, "write FCTR failed", err, ESP_FAIL); + /* enable flow control */ + MAC_CHECK(dm9051_register_write(emac, DM9051_FCR, FCR_FLOW_ENABLE) == ESP_OK, "write FCR failed", err, ESP_FAIL); + /* retry late collision packet, at most two transmit command can be issued before transmit complete */ + MAC_CHECK(dm9051_register_write(emac, DM9051_TCR2, TCR2_RLCP) == ESP_OK, "write TCR2 failed", err, ESP_FAIL); + /* enable auto transmit */ + MAC_CHECK(dm9051_register_write(emac, DM9051_ATCR, ATCR_AUTO_TX) == ESP_OK, "write ATCR failed", err, ESP_FAIL); + /* generate checksum for UDP, TCP and IPv4 packets */ + MAC_CHECK(dm9051_register_write(emac, DM9051_TCSCR, TCSCR_IPCSE | TCSCR_TCPCSE | TCSCR_UDPCSE) == ESP_OK, + "write TCSCR failed", err, ESP_FAIL); + /* disable check sum for receive packets */ + MAC_CHECK(dm9051_register_write(emac, DM9051_RCSCSR, 0x00) == ESP_OK, "write RCSCSR failed", err, ESP_FAIL); + /* interrupt pin config: push-pull output, active high */ + MAC_CHECK(dm9051_register_write(emac, DM9051_INTCR, 0x00) == ESP_OK, "write INTCR failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_write(emac, DM9051_INTCKCR, 0x00) == ESP_OK, "write INTCKCR failed", err, ESP_FAIL); + /* no length limitation for rx packets */ + MAC_CHECK(dm9051_register_write(emac, DM9051_RLENCR, 0x00) == ESP_OK, "write RLENCR failed", err, ESP_FAIL); + /* 3K-byte for TX and 13K-byte for RX */ + MAC_CHECK(dm9051_register_write(emac, DM9051_MEMSCR, 0x00) == ESP_OK, "write MEMSCR failed", err, ESP_FAIL); + /* reset tx and rx memory pointer */ + MAC_CHECK(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX | MPTRCR_RST_TX) == ESP_OK, + "write MPTRCR failed", err, ESP_FAIL); + /* clear network status: wakeup event, tx complete */ + MAC_CHECK(dm9051_register_write(emac, DM9051_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END) == ESP_OK, "write NSR failed", err, ESP_FAIL); + /* clear interrupt status */ + MAC_CHECK(dm9051_register_write(emac, DM9051_ISR, ISR_CLR_STATUS) == ESP_OK, "write ISR failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +/** + * @brief start dm9051: enable interrupt and start receive + */ +static esp_err_t dm9051_start(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + /* enable interrupt */ + MAC_CHECK(dm9051_register_write(emac, DM9051_IMR, IMR_ALL) == ESP_OK, "write IMR failed", err, ESP_FAIL); + /* enable rx */ + uint8_t rcr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_RCR, &rcr) == ESP_OK, "read RCR failed", err, ESP_FAIL); + rcr |= RCR_RXEN; + MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, rcr) == ESP_OK, "write RCR failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +/** + * @brief stop dm9051: disable interrupt and stop receive + */ +static esp_err_t dm9051_stop(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + /* disable interrupt */ + MAC_CHECK(dm9051_register_write(emac, DM9051_IMR, 0x00) == ESP_OK, "write IMR failed", err, ESP_FAIL); + /* disable rx */ + uint8_t rcr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_RCR, &rcr) == ESP_OK, "read RCR failed", err, ESP_FAIL); + rcr &= ~RCR_RXEN; + MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, rcr) == ESP_OK, "write RCR failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +static void dm9051_isr_handler(void *arg) +{ + emac_dm9051_t *emac = (emac_dm9051_t *)arg; + BaseType_t high_task_wakeup = pdFALSE; + /* notify dm9051 task */ + vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup); + if (high_task_wakeup != pdFALSE) { + portYIELD_FROM_ISR(); + } +} + +static void emac_dm9051_task(void *arg) +{ + emac_dm9051_t *emac = (emac_dm9051_t *)arg; + uint8_t status = 0; + uint8_t *buffer = NULL; + uint32_t length = 0; + while (1) { + if (ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(RX_QUEUE_WAIT_MS))) { + /* clear interrupt status */ + dm9051_register_read(emac, DM9051_ISR, &status); + dm9051_register_write(emac, DM9051_ISR, status); + /* packet received */ + if (status & ISR_PR) { + do { + buffer = (uint8_t *)heap_caps_malloc(ETH_MAX_PACKET_SIZE, MALLOC_CAP_DMA); + if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) { + /* pass the buffer to stack (e.g. TCP/IP layer) */ + emac->eth->stack_input(emac->eth, buffer, length); + } else { + free(buffer); + } + } while (emac->packets_remain); + } + } + } + vTaskDelete(NULL); +} + +static esp_err_t emac_dm9051_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth) +{ + esp_err_t ret = ESP_OK; + MAC_CHECK(eth, "can't set mac's mediator to null", err, ESP_ERR_INVALID_ARG); + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + emac->eth = eth; + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_write_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + /* check if phy access is in progress */ + uint8_t epcr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL); + MAC_CHECK(!(epcr & EPCR_ERRE), "phy is busy", err, ESP_ERR_INVALID_STATE); + MAC_CHECK(dm9051_register_write(emac, DM9051_EPAR, (uint8_t)(((phy_addr << 6) & 0xFF) | phy_reg)) == ESP_OK, + "write EPAR failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_write(emac, DM9051_EPDRL, (uint8_t)(reg_value & 0xFF)) == ESP_OK, + "write EPDRL failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_write(emac, DM9051_EPDRH, (uint8_t)((reg_value >> 8) & 0xFF)) == ESP_OK, + "write EPDRH failed", err, ESP_FAIL); + /* select PHY and select write operation */ + MAC_CHECK(dm9051_register_write(emac, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW) == ESP_OK, "write EPCR failed", err, ESP_FAIL); + /* polling the busy flag */ + uint32_t to = 0; + do { + ets_delay_us(100); + MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL); + to += 100; + } while ((epcr & EPCR_ERRE) && to < DM9051_PHY_OPERATION_TIMEOUT_US); + MAC_CHECK(!(epcr & EPCR_ERRE), "phy is busy", err, ESP_ERR_TIMEOUT); + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_read_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value) +{ + esp_err_t ret = ESP_OK; + MAC_CHECK(reg_value, "can't set reg_value to null", err, ESP_ERR_INVALID_ARG); + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + /* check if phy access is in progress */ + uint8_t epcr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL); + MAC_CHECK(!(epcr & 0x01), "phy is busy", err, ESP_ERR_INVALID_STATE); + MAC_CHECK(dm9051_register_write(emac, DM9051_EPAR, (uint8_t)(((phy_addr << 6) & 0xFF) | phy_reg)) == ESP_OK, + "write EPAR failed", err, ESP_FAIL); + /* Select PHY and select read operation */ + MAC_CHECK(dm9051_register_write(emac, DM9051_EPCR, 0x0C) == ESP_OK, "write EPCR failed", err, ESP_FAIL); + /* polling the busy flag */ + uint32_t to = 0; + do { + ets_delay_us(100); + MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL); + to += 100; + } while ((epcr & EPCR_ERRE) && to < DM9051_PHY_OPERATION_TIMEOUT_US); + MAC_CHECK(!(epcr & EPCR_ERRE), "phy is busy", err, ESP_ERR_TIMEOUT); + uint8_t value_h = 0; + uint8_t value_l = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_EPDRH, &value_h) == ESP_OK, "read EPDRH failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_read(emac, DM9051_EPDRL, &value_l) == ESP_OK, "read EPDRL failed", err, ESP_FAIL); + *reg_value = (value_h << 8) | value_l; + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_set_addr(esp_eth_mac_t *mac, uint8_t *addr) +{ + esp_err_t ret = ESP_OK; + MAC_CHECK(addr, "can't set mac addr to null", err, ESP_ERR_INVALID_ARG); + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + memcpy(emac->addr, addr, 6); + MAC_CHECK(dm9051_set_mac_addr(emac) == ESP_OK, "set mac address failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_get_addr(esp_eth_mac_t *mac, uint8_t *addr) +{ + esp_err_t ret = ESP_OK; + MAC_CHECK(addr, "can't set mac addr to null", err, ESP_ERR_INVALID_ARG); + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + memcpy(addr, emac->addr, 6); + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_set_link(esp_eth_mac_t *mac, eth_link_t link) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + uint8_t nsr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_NSR, &nsr) == ESP_OK, "read NSR failed", err, ESP_FAIL); + switch (link) { + case ETH_LINK_UP: + MAC_CHECK(nsr & NSR_LINKST, "phy is not link up", err, ESP_ERR_INVALID_STATE); + MAC_CHECK(dm9051_start(emac) == ESP_OK, "dm9051 start failed", err, ESP_FAIL); + break; + case ETH_LINK_DOWN: + MAC_CHECK(!(nsr & NSR_LINKST), "phy is not link down", err, ESP_ERR_INVALID_STATE); + MAC_CHECK(dm9051_stop(emac) == ESP_OK, "dm9051 stop failed", err, ESP_FAIL); + break; + default: + MAC_CHECK(false, "unknown link status", err, ESP_ERR_INVALID_ARG); + break; + } + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_set_speed(esp_eth_mac_t *mac, eth_speed_t speed) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + uint8_t nsr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_NSR, &nsr) == ESP_OK, "read NSR failed", err, ESP_FAIL); + switch (speed) { + case ETH_SPEED_10M: + MAC_CHECK(nsr & NSR_SPEED, "phy speed is not at 10Mbps", err, ESP_ERR_INVALID_STATE); + break; + case ETH_SPEED_100M: + MAC_CHECK(!(nsr & NSR_SPEED), "phy speed is not at 100Mbps", err, ESP_ERR_INVALID_STATE); + break; + default: + MAC_CHECK(false, "unknown speed", err, ESP_ERR_INVALID_ARG); + break; + } + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + uint8_t ncr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_NCR, &ncr) == ESP_OK, "read NCR failed", err, ESP_FAIL); + switch (duplex) { + case ETH_DUPLEX_HALF: + MAC_CHECK(!(ncr & NCR_FDX), "phy is not at half duplex", err, ESP_ERR_INVALID_STATE); + break; + case ETH_DUPLEX_FULL: + MAC_CHECK(ncr & NCR_FDX, "phy is not at full duplex", err, ESP_ERR_INVALID_STATE); + break; + default: + MAC_CHECK(false, "unknown duplex", err, ESP_ERR_INVALID_ARG); + break; + } + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_set_promiscuous(esp_eth_mac_t *mac, bool enable) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + uint8_t rcr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_EPDRL, &rcr) == ESP_OK, "read RCR failed", err, ESP_FAIL); + if (enable) { + rcr |= RCR_PRMSC; + } else { + rcr &= ~RCR_PRMSC; + } + MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, rcr) == ESP_OK, "write RCR failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + MAC_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG); + MAC_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG); + /* Check if last transmit complete */ + uint8_t tcr = 0; + MAC_CHECK(dm9051_register_read(emac, DM9051_TCR, &tcr) == ESP_OK, "read TCR failed", err, ESP_FAIL); + MAC_CHECK(!(tcr & TCR_TXREQ), "last transmit still in progress", err, ESP_ERR_INVALID_STATE); + /* set tx length */ + MAC_CHECK(dm9051_register_write(emac, DM9051_TXPLL, length & 0xFF) == ESP_OK, "write TXPLL failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_write(emac, DM9051_TXPLH, (length >> 8) & 0xFF) == ESP_OK, "write TXPLH failed", err, ESP_FAIL); + /* copy data to tx memory */ + MAC_CHECK(dm9051_memory_write(emac, buf, length) == ESP_OK, "write memory failed", err, ESP_FAIL); + /* issue tx polling command */ + tcr |= TCR_TXREQ; + MAC_CHECK(dm9051_register_write(emac, DM9051_TCR, tcr) == ESP_OK, "write TCR failed", err, ESP_FAIL); + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + MAC_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG); + uint8_t rxbyte = 0; + uint16_t rx_len = 0; + __attribute__((aligned(4))) dm9051_rx_header_t header; // SPI driver needs the rx buffer 4 byte align + emac->packets_remain = false; + /* dummy read, get the most updated data */ + MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL); + /* rxbyte must be 0xFF, 0 or 1 */ + if (rxbyte > 1) { + MAC_CHECK(dm9051_stop(emac) == ESP_OK, "stop dm9051 failed", err, ESP_FAIL); + /* reset rx fifo pointer */ + MAC_CHECK(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX) == ESP_OK, "write MPTRCR failed", err, ESP_FAIL); + ets_delay_us(10); + MAC_CHECK(dm9051_start(emac) == ESP_OK, "start dm9051 failed", err, ESP_FAIL); + MAC_CHECK(false, "reset rx fifo pointer", err, ESP_FAIL); + } else if (rxbyte) { + MAC_CHECK(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK, "read rx header failed", err, ESP_FAIL); + rx_len = header.length_low + (header.length_high << 8); + MAC_CHECK(dm9051_memory_read(emac, buf, rx_len) == ESP_OK, "read rx data failed", err, ESP_FAIL); + MAC_CHECK(!(header.status & 0xBF), "receive status error: %xH", err, ESP_FAIL, header.status); + *length = rx_len - 4; // substract the CRC length (4Bytes) + /* dummy read, get the most updated data */ + MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL); + MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL); + emac->packets_remain = rxbyte > 0; + } + return ESP_OK; +err: + return ret; +} + +static esp_err_t emac_dm9051_init(esp_eth_mac_t *mac) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + esp_eth_mediator_t *eth = emac->eth; + /* init gpio used by spi-ethernet interrupt */ + gpio_pad_select_gpio(CONFIG_ETH_DM9051_INT_GPIO); + gpio_set_direction(CONFIG_ETH_DM9051_INT_GPIO, GPIO_MODE_INPUT); + gpio_set_pull_mode(CONFIG_ETH_DM9051_INT_GPIO, GPIO_PULLDOWN_ONLY); + gpio_set_intr_type(CONFIG_ETH_DM9051_INT_GPIO, GPIO_INTR_POSEDGE); + gpio_intr_enable(CONFIG_ETH_DM9051_INT_GPIO); + gpio_isr_handler_add(CONFIG_ETH_DM9051_INT_GPIO, dm9051_isr_handler, emac); + MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, "lowlevel init failed", err, ESP_FAIL); + /* reset dm9051 */ + MAC_CHECK(dm9051_reset(emac) == ESP_OK, "reset dm9051 failed", err, ESP_FAIL); + /* verify chip id */ + MAC_CHECK(dm9051_verify_id(emac) == ESP_OK, "vefiry chip ID failed", err, ESP_FAIL); + /* default setup of internal registers */ + MAC_CHECK(dm9051_setup_default(emac) == ESP_OK, "dm9051 default setup failed", err, ESP_FAIL); + /* clear multicast hash table */ + MAC_CHECK(dm9051_clear_multicast_table(emac) == ESP_OK, "clear multicast table failed", err, ESP_FAIL); + /* get emac address from eeprom */ + MAC_CHECK(dm9051_get_mac_addr(emac) == ESP_OK, "fetch ethernet mac address failed", err, ESP_FAIL); + return ESP_OK; +err: + gpio_isr_handler_remove(CONFIG_ETH_DM9051_INT_GPIO); + gpio_reset_pin(CONFIG_ETH_DM9051_INT_GPIO); + eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); + return ret; +} + +static esp_err_t emac_dm9051_deinit(esp_eth_mac_t *mac) +{ + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + esp_eth_mediator_t *eth = emac->eth; + dm9051_stop(emac); + gpio_isr_handler_remove(CONFIG_ETH_DM9051_INT_GPIO); + gpio_reset_pin(CONFIG_ETH_DM9051_INT_GPIO); + eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); + return ESP_OK; +} + +static esp_err_t emac_dm9051_del(esp_eth_mac_t *mac) +{ + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + vTaskDelete(emac->rx_task_hdl); + vSemaphoreDelete(emac->spi_lock); + free(emac); + return ESP_OK; +} + +esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_mac_config_t *config) +{ + esp_eth_mac_t *ret = NULL; + MAC_CHECK(config, "can't set mac config to null", err, NULL); + MAC_CHECK(config->spi_hdl, "can't set spi handle to null", err, NULL); + emac_dm9051_t *emac = calloc(1, sizeof(emac_dm9051_t)); + MAC_CHECK(emac, "calloc emac failed", err, NULL); + /* bind methods and attributes */ + emac->sw_reset_timeout_ms = config->sw_reset_timeout_ms; + emac->spi_hdl = config->spi_hdl; + emac->parent.set_mediator = emac_dm9051_set_mediator; + emac->parent.init = emac_dm9051_init; + emac->parent.deinit = emac_dm9051_deinit; + emac->parent.del = emac_dm9051_del; + emac->parent.write_phy_reg = emac_dm9051_write_phy_reg; + emac->parent.read_phy_reg = emac_dm9051_read_phy_reg; + emac->parent.set_addr = emac_dm9051_set_addr; + emac->parent.get_addr = emac_dm9051_get_addr; + emac->parent.set_speed = emac_dm9051_set_speed; + emac->parent.set_duplex = emac_dm9051_set_duplex; + emac->parent.set_link = emac_dm9051_set_link; + emac->parent.set_promiscuous = emac_dm9051_set_promiscuous; + emac->parent.transmit = emac_dm9051_transmit; + emac->parent.receive = emac_dm9051_receive; + /* create mutex */ + emac->spi_lock = xSemaphoreCreateMutex(); + MAC_CHECK(emac->spi_lock, "create lock failed", err_lock, NULL); + /* create dm9051 task */ + BaseType_t xReturned = xTaskCreate(emac_dm9051_task, "dm9051_tsk", config->rx_task_stack_size, emac, + config->rx_task_prio, &emac->rx_task_hdl); + MAC_CHECK(xReturned == pdPASS, "create dm9051 task failed", err_tsk, NULL); + return &(emac->parent); +err_tsk: + vSemaphoreDelete(emac->spi_lock); +err_lock: + free(emac); +err: + return ret; +} diff --git a/components/esp_eth/src/esp_eth_mac_esp32.c b/components/esp_eth/src/esp_eth_mac_esp32.c index f68a16ac9..1c1fbcc90 100644 --- a/components/esp_eth/src/esp_eth_mac_esp32.c +++ b/components/esp_eth/src/esp_eth_mac_esp32.c @@ -41,6 +41,7 @@ static const char *TAG = "emac_esp32"; } while (0) #define RX_QUEUE_WAIT_MS (20) +#define PHY_OPERATION_TIMEOUT_US (1000) typedef struct { esp_eth_mac_t parent; @@ -49,12 +50,8 @@ typedef struct { intr_handle_t intr_hdl; SemaphoreHandle_t rx_counting_sem; TaskHandle_t rx_task_hdl; - const char *name; uint32_t sw_reset_timeout_ms; uint32_t frames_remain; - uint32_t rx_task_stack_size; - uint32_t rx_task_prio; - uint32_t queue_len; uint8_t addr[6]; uint8_t *rx_buf[CONFIG_ETH_DMA_RX_BUFFER_NUM]; uint8_t *tx_buf[CONFIG_ETH_DMA_TX_BUFFER_NUM]; @@ -78,9 +75,15 @@ static esp_err_t emac_esp32_write_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE); emac_hal_set_phy_data(emac->hal, reg_value); emac_hal_set_phy_cmd(emac->hal, phy_addr, phy_reg, true); - /* Delay some time and Check for the Busy flag */ - ets_delay_us(1000); - MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE); + /* polling the busy flag */ + uint32_t to = 0; + bool busy = true; + do { + ets_delay_us(100); + busy = emac_hal_is_mii_busy(emac->hal); + to += 100; + } while (busy && to < PHY_OPERATION_TIMEOUT_US); + MAC_CHECK(!busy, "phy is busy", err, ESP_ERR_TIMEOUT); return ESP_OK; err: return ret; @@ -93,9 +96,15 @@ static esp_err_t emac_esp32_read_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent); MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE); emac_hal_set_phy_cmd(emac->hal, phy_addr, phy_reg, false); - /* Delay some time and Check for the Busy flag */ - ets_delay_us(1000); - MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE); + /* polling the busy flag */ + uint32_t to = 0; + bool busy = true; + do { + ets_delay_us(100); + busy = emac_hal_is_mii_busy(emac->hal); + to += 100; + } while (busy && to < PHY_OPERATION_TIMEOUT_US); + MAC_CHECK(!busy, "phy is busy", err, ESP_ERR_TIMEOUT); /* Store value */ *reg_value = emac_hal_get_phy_data(emac->hal); return ESP_OK; @@ -224,16 +233,14 @@ err: static void emac_esp32_rx_task(void *arg) { emac_esp32_t *emac = (emac_esp32_t *)arg; - esp_eth_mediator_t *eth = emac->eth; uint8_t *buffer = NULL; uint32_t length = 0; - while (1) { if (xSemaphoreTake(emac->rx_counting_sem, pdMS_TO_TICKS(RX_QUEUE_WAIT_MS)) == pdTRUE) { buffer = (uint8_t *)malloc(ETH_MAX_PACKET_SIZE); if (emac_esp32_receive(&emac->parent, buffer, &length) == ESP_OK) { /* pass the buffer to stack (e.g. TCP/IP layer) */ - eth->stack_input(eth, buffer, length); + emac->eth->stack_input(emac->eth, buffer, length); } else { free(buffer); } @@ -298,50 +305,32 @@ static esp_err_t emac_esp32_init(esp_eth_mac_t *mac) MAC_CHECK(esp_read_mac(emac->addr, ESP_MAC_ETH) == ESP_OK, "fetch ethernet mac address failed", err, ESP_FAIL); /* set MAC address to emac register */ emac_hal_set_address(emac->hal, emac->addr); - /* Interrupt configuration */ - MAC_CHECK(esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_hal_isr, - &emac->hal, &(emac->intr_hdl)) == ESP_OK, - "alloc emac interrupt failed", err, ESP_FAIL); - /* create counting semaphore */ - emac->rx_counting_sem = xSemaphoreCreateCounting(emac->queue_len, 0); - MAC_CHECK(emac->rx_counting_sem, "create semaphore failed", err_sem, ESP_FAIL); - /* create rx task */ - BaseType_t xReturned = xTaskCreate(emac_esp32_rx_task, "emac_rx", emac->rx_task_stack_size, emac, - emac->rx_task_prio, &emac->rx_task_hdl); - MAC_CHECK(xReturned == pdPASS, "create emac_rx task failed", err_task, ESP_FAIL); return ESP_OK; -err_task: - vSemaphoreDelete(emac->rx_counting_sem); -err_sem: - esp_intr_free(emac->intr_hdl); err: - periph_module_disable(PERIPH_EMAC_MODULE); eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); + periph_module_disable(PERIPH_EMAC_MODULE); return ret; } static esp_err_t emac_esp32_deinit(esp_eth_mac_t *mac) { - esp_err_t ret = ESP_OK; emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent); esp_eth_mediator_t *eth = emac->eth; #if CONFIG_ETH_PHY_USE_RST gpio_set_level(CONFIG_ETH_PHY_RST_GPIO, 0); #endif emac_hal_stop(emac->hal); - MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL) == ESP_OK, "lowlevel deinit failed", err, ESP_FAIL); - MAC_CHECK(esp_intr_free(emac->intr_hdl) == ESP_OK, "free emac interrupt failed", err, ESP_FAIL); - vTaskDelete(emac->rx_task_hdl); - vSemaphoreDelete(emac->rx_counting_sem); + eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); periph_module_disable(PERIPH_EMAC_MODULE); return ESP_OK; -err: - return ret; } static esp_err_t emac_esp32_del(esp_eth_mac_t *mac) { emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent); + esp_intr_free(emac->intr_hdl); + vTaskDelete(emac->rx_task_hdl); + vSemaphoreDelete(emac->rx_counting_sem); int i = 0; for (i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) { free(emac->hal->rx_buf[i]); @@ -400,10 +389,6 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config) /* initialize hal layer driver */ emac_hal_init(emac->hal, descriptors, emac->rx_buf, emac->tx_buf); emac->sw_reset_timeout_ms = config->sw_reset_timeout_ms; - emac->rx_task_stack_size = config->rx_task_stack_size; - emac->rx_task_prio = config->rx_task_prio; - emac->queue_len = config->queue_len; - emac->name = "emac_esp32"; emac->parent.set_mediator = emac_esp32_set_mediator; emac->parent.init = emac_esp32_init; emac->parent.deinit = emac_esp32_deinit; @@ -418,7 +403,29 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config) emac->parent.set_promiscuous = emac_esp32_set_promiscuous; emac->parent.transmit = emac_esp32_transmit; emac->parent.receive = emac_esp32_receive; + /* Interrupt configuration */ + MAC_CHECK(esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_hal_isr, + &emac->hal, &(emac->intr_hdl)) == ESP_OK, + "alloc emac interrupt failed", err_intr, NULL); + /* create counting semaphore */ + emac->rx_counting_sem = xSemaphoreCreateCounting(config->queue_len, 0); + MAC_CHECK(emac->rx_counting_sem, "create semaphore failed", err_sem, NULL); + /* create rx task */ + BaseType_t xReturned = xTaskCreate(emac_esp32_rx_task, "emac_rx", config->rx_task_stack_size, emac, + config->rx_task_prio, &emac->rx_task_hdl); + MAC_CHECK(xReturned == pdPASS, "create emac_rx task failed", err_task, NULL); return &(emac->parent); +err_task: + vSemaphoreDelete(emac->rx_counting_sem); +err_sem: + esp_intr_free(emac->intr_hdl); +err_intr: + for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) { + free(emac->tx_buf[i]); + } + for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) { + free(emac->rx_buf[i]); + } err_buffer: free(emac->hal); err_hal: diff --git a/components/esp_eth/src/esp_eth_phy_dm9051.c b/components/esp_eth/src/esp_eth_phy_dm9051.c new file mode 100644 index 000000000..cb658c181 --- /dev/null +++ b/components/esp_eth/src/esp_eth_phy_dm9051.c @@ -0,0 +1,302 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "esp_log.h" +#include "esp_eth.h" +#include "eth_phy_regs_struct.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "dm9051"; +#define PHY_CHECK(a, str, goto_tag, ...) \ + do \ + { \ + if (!(a)) \ + { \ + ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ + } while (0) + +/***************Vendor Specific Register***************/ + +/** + * @brief DSCR(DAVICOM Specified Configuration Register) + * + */ +typedef union { + struct { + uint32_t reserved1 : 1; /* Reserved */ + uint32_t sleep : 1; /* Set 1 to enable PHY into sleep mode */ + uint32_t mfpsc : 1; /* MII frame preamble suppression control bit */ + uint32_t smrst : 1; /* Set 1 to reset all state machines of PHY */ + uint32_t rpdctr_en : 1; /* Set 1 to enable automatic reduced power down */ + uint32_t reserved2 : 2; /* Reserved */ + uint32_t flink100 : 1; /* Force Good Link in 100Mbps */ + uint32_t reserved3 : 2; /* Reserved */ + uint32_t tx_fx : 1; /* 100BASE-TX or FX Mode Control */ + uint32_t reserved4 : 1; /* Reserved */ + uint32_t bp_adpok : 1; /* BYPASS ADPOK */ + uint32_t bp_align : 1; /* Bypass Symbol Alignment Function */ + uint32_t bp_scr : 1; /* Bypass Scrambler/Descrambler Function */ + uint32_t bp_4b5b : 1; /* Bypass 4B5B Encoding and 5B4B Decoding */ + }; + uint32_t val; +} dscr_reg_t; +#define ETH_PHY_DSCR_REG_ADDR (0x10) + +/** + * @brief DSCSR(DAVICOM Specified Configuration and Status Register) + * + */ +typedef union { + struct { + uint32_t anmb : 4; /* Auto-Negotiation Monitor Bits */ + uint32_t phy_addr : 5; /* PHY Address */ + uint32_t reserved : 3; /* Reserved */ + uint32_t hdx10 : 1; /* 10M Half-Duplex Operation Mode */ + uint32_t fdx10 : 1; /* 10M Full-Duplex Operation Mode */ + uint32_t hdx100 : 1; /* 100M Half-Duplex Operation Mode */ + uint32_t fdx100 : 1; /* 100M Full-Duplex Operation Mode */ + }; + uint32_t val; +} dscsr_reg_t; +#define ETH_PHY_DSCSR_REG_ADDR (0x11) + +typedef struct { + esp_eth_phy_t parent; + esp_eth_mediator_t *eth; + const char *name; + uint32_t addr; + uint32_t reset_timeout_ms; + uint32_t autonego_timeout_ms; + eth_link_t link_status; +} phy_dm9051_t; + +static esp_err_t dm9051_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth) +{ + PHY_CHECK(eth, "can't set mediator for dm9051 to null", err); + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + dm9051->eth = eth; + return ESP_OK; +err: + return ESP_ERR_INVALID_ARG; +} + +static esp_err_t dm9051_get_link(esp_eth_phy_t *phy) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + esp_eth_mediator_t *eth = dm9051->eth; + bmsr_reg_t bmsr; + + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err); + eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + if (dm9051->link_status != link) { + if (link == ETH_LINK_UP) { + phy->negotiate(phy); + } else { + PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err); + dm9051->link_status = link; + } + } + return ESP_OK; +err: + return ESP_FAIL; +} + +static esp_err_t dm9051_reset(esp_eth_phy_t *phy) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + esp_eth_mediator_t *eth = dm9051->eth; + dscr_reg_t dscr; + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)) == ESP_OK, "read DSCR failed", err); + dscr.smrst = 1; + PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, dscr.val) == ESP_OK, "write DSCR failed", err); + bmcr_reg_t bmcr = {.reset = 1}; + PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err); + /* Wait for reset complete */ + uint32_t to = 0; + for (to = 0; to < dm9051->reset_timeout_ms / 10; to++) { + vTaskDelay(pdMS_TO_TICKS(10)); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)) == ESP_OK, "read DSCR failed", err); + if (!bmcr.reset && !dscr.smrst) { + break; + } + } + PHY_CHECK(to < dm9051->reset_timeout_ms / 10, "PHY reset timeout", err); + return ESP_OK; +err: + return ESP_FAIL; +} + +static esp_err_t dm9051_negotiate(esp_eth_phy_t *phy) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + esp_eth_mediator_t *eth = dm9051->eth; + /* Start auto negotiation */ + bmcr_reg_t bmcr = { + .speed_select = 1, /* 100Mbps */ + .duplex_mode = 1, /* Full Duplex */ + .en_auto_nego = 1, /* Auto Negotiation */ + .restart_auto_nego = 1 /* Restart Auto Negotiation */ + }; + PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err); + /* Wait for auto negotiation complete */ + bmsr_reg_t bmsr; + dscsr_reg_t dscsr; + uint32_t to = 0; + for (to = 0; to < dm9051->autonego_timeout_ms / 10; to++) { + vTaskDelay(pdMS_TO_TICKS(10)); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCSR_REG_ADDR, &(dscsr.val)) == ESP_OK, "read DSCSR failed", err); + if (bmsr.auto_nego_complete && dscsr.anmb & 0x08) { + break; + } + } + if (to >= dm9051->autonego_timeout_ms / 10) { + ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout"); + } + /* Updata information about link, speed, duplex */ + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCSR_REG_ADDR, &(dscsr.val)) == ESP_OK, "read DSCSR failed", err); + eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + eth_speed_t speed = ETH_SPEED_10M; + eth_duplex_t duplex = ETH_DUPLEX_HALF; + if (dscsr.fdx100 || dscsr.hdx100) { + speed = ETH_SPEED_100M; + } else { + speed = ETH_SPEED_10M; + } + if (dscsr.fdx100 || dscsr.fdx10) { + duplex = ETH_DUPLEX_FULL; + } else { + duplex = ETH_DUPLEX_HALF; + } + PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err); + PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err); + if (dm9051->link_status != link) { + PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err); + dm9051->link_status = link; + } + return ESP_OK; +err: + return ESP_FAIL; +} + +static esp_err_t dm9051_pwrctl(esp_eth_phy_t *phy, bool enable) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + esp_eth_mediator_t *eth = dm9051->eth; + bmcr_reg_t bmcr; + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err); + if (!enable) { + /* Enable IEEE Power Down Mode */ + bmcr.power_down = 1; + } else { + /* Disable IEEE Power Down Mode */ + bmcr.power_down = 0; + } + PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err); + if (!enable) { + PHY_CHECK(bmcr.power_down == 1, "power down failed", err); + } else { + PHY_CHECK(bmcr.power_down == 0, "power up failed", err); + } + return ESP_OK; +err: + return ESP_FAIL; +} + +static esp_err_t dm9051_set_addr(esp_eth_phy_t *phy, uint32_t addr) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + dm9051->addr = addr; + return ESP_OK; +} + +static esp_err_t dm9051_get_addr(esp_eth_phy_t *phy, uint32_t *addr) +{ + PHY_CHECK(addr, "get phy address failed", err); + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + *addr = dm9051->addr; + return ESP_OK; +err: + return ESP_ERR_INVALID_ARG; +} + +static esp_err_t dm9051_del(esp_eth_phy_t *phy) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + free(dm9051); + return ESP_OK; +} + +static esp_err_t dm9051_init(esp_eth_phy_t *phy) +{ + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + esp_eth_mediator_t *eth = dm9051->eth; + /* Power on Ethernet PHY */ + PHY_CHECK(dm9051_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err); + /* Reset Ethernet PHY */ + PHY_CHECK(dm9051_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err); + /* Check PHY ID */ + phyidr1_reg_t id1; + phyidr2_reg_t id2; + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err); + PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err); + PHY_CHECK(id1.oui_msb == 0x0181 && id2.oui_lsb == 0x2E && id2.vendor_model == 0x0A, "wrong PHY chip ID", err); + return ESP_OK; +err: + return ESP_FAIL; +} + +static esp_err_t dm9051_deinit(esp_eth_phy_t *phy) +{ + /* Power off Ethernet PHY */ + PHY_CHECK(dm9051_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err); + return ESP_OK; +err: + return ESP_FAIL; +} + +esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config) +{ + PHY_CHECK(config, "can't set phy config to null", err); + PHY_CHECK(config->phy_addr == 1, "dm9051's phy address can only set to 1", err); + phy_dm9051_t *dm9051 = calloc(1, sizeof(phy_dm9051_t)); + PHY_CHECK(dm9051, "calloc dm9051 object failed", err); + dm9051->name = "dm9051"; + dm9051->addr = config->phy_addr; + dm9051->reset_timeout_ms = config->reset_timeout_ms; + dm9051->link_status = ETH_LINK_DOWN; + dm9051->autonego_timeout_ms = config->autonego_timeout_ms; + dm9051->parent.reset = dm9051_reset; + dm9051->parent.init = dm9051_init; + dm9051->parent.deinit = dm9051_deinit; + dm9051->parent.set_mediator = dm9051_set_mediator; + dm9051->parent.negotiate = dm9051_negotiate; + dm9051->parent.get_link = dm9051_get_link; + dm9051->parent.pwrctl = dm9051_pwrctl; + dm9051->parent.get_addr = dm9051_get_addr; + dm9051->parent.set_addr = dm9051_set_addr; + dm9051->parent.del = dm9051_del; + + return &(dm9051->parent); +err: + return NULL; +} diff --git a/components/esp_eth/test/test_emac.c b/components/esp_eth/test/test_emac.c index ac2d10a75..141bf3e1d 100644 --- a/components/esp_eth/test/test_emac.c +++ b/components/esp_eth/test/test_emac.c @@ -8,6 +8,7 @@ #include "test_utils.h" #include "esp_eth.h" #include "esp_log.h" +#include "sdkconfig.h" static const char *TAG = "esp_eth_test"; /** Event handler for Ethernet events */ @@ -89,3 +90,48 @@ TEST_CASE("ethernet tcpip_adapter", "[ethernet][ignore]") TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle)); vTaskDelay(portMAX_DELAY); } + +#if CONFIG_ETH_USE_SPI_ETHERNET +TEST_CASE("dm9051 io test", "[ethernet][ignore]") +{ + spi_device_handle_t spi_handle = NULL; + spi_bus_config_t buscfg = { + .miso_io_num = 25, + .mosi_io_num = 23, + .sclk_io_num = 19, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, 1)); + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .mode = 0, + .clock_speed_hz = 20 * 1000 * 1000, + .spics_io_num = 22, + .queue_size = 20 + }; + TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, &spi_handle)); + gpio_install_isr_service(0); + tcpip_adapter_init(); + TEST_ESP_OK(esp_event_loop_create_default()); + TEST_ESP_OK(tcpip_adapter_set_default_eth_handlers()); + TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL)); + TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac_config.spi_hdl = spi_handle; + esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&mac_config); + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config); + esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); + esp_eth_handle_t eth_handle = NULL; + TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle)); + vTaskDelay(pdMS_TO_TICKS(portMAX_DELAY)); + TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle)); + TEST_ESP_OK(phy->del(phy)); + TEST_ESP_OK(mac->del(mac)); + TEST_ESP_OK(esp_event_loop_delete_default()); + TEST_ESP_OK(spi_bus_remove_device(spi_handle)); + TEST_ESP_OK(spi_bus_free(HSPI_HOST)); +} +#endif diff --git a/components/soc/esp32/emac_hal.c b/components/soc/esp32/emac_hal.c index f7c6fb38c..4722ee4ec 100644 --- a/components/soc/esp32/emac_hal.c +++ b/components/soc/esp32/emac_hal.c @@ -163,8 +163,6 @@ void emac_hal_reset_desc_chain(emac_hal_context_t *hal) hal->tx_desc[i].TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE; /* Enable Ethernet DMA Tx Descriptor interrupt */ hal->tx_desc[1].TDES0.InterruptOnComplete = 1; - /* Set the DMA Tx descriptors checksum insertion */ - hal->tx_desc[i].TDES0.ChecksumInsertControl = EMAC_DMATXDESC_CHECKSUM_TCPUDPICMPFULL; /* Enable Transmit Timestamp */ hal->tx_desc[i].TDES0.TransmitTimestampEnable = 1; /* point to the buffer */ diff --git a/examples/common_components/protocol_examples_common/Kconfig.projbuild b/examples/common_components/protocol_examples_common/Kconfig.projbuild index 7de1c4359..f5d288e9a 100644 --- a/examples/common_components/protocol_examples_common/Kconfig.projbuild +++ b/examples/common_components/protocol_examples_common/Kconfig.projbuild @@ -1,5 +1,4 @@ menu "Example Connection Configuration" - choice EXAMPLE_CONNECT_INTERFACE prompt "Connect using" default EXAMPLE_CONNECT_WIFI @@ -15,53 +14,120 @@ menu "Example Connection Configuration" endchoice - config EXAMPLE_WIFI_SSID - depends on EXAMPLE_CONNECT_WIFI - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. - - config EXAMPLE_WIFI_PASSWORD - depends on EXAMPLE_CONNECT_WIFI - string "WiFi Password" - default "mypassword" - help - WiFi password (WPA or WPA2) for the example to use. - Can be left blank if the network has no security set. - - choice EXAMPLE_ETH_PHY_MODEL - depends on EXAMPLE_CONNECT_ETHERNET - prompt "Ethernet PHY Device" - default EXAMPLE_ETH_PHY_IP101 - help - Select the PHY driver to use for the example. - - config EXAMPLE_ETH_PHY_IP101 - bool "IP101" + if EXAMPLE_CONNECT_WIFI + config EXAMPLE_WIFI_SSID + string "WiFi SSID" + default "myssid" help - IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. - Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + SSID (network name) for the example to connect to. - config EXAMPLE_ETH_PHY_RTL8201 - bool "RTL8201/SR8201" + config EXAMPLE_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" help - RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. - Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. + WiFi password (WPA or WPA2) for the example to use. + Can be left blank if the network has no security set. + endif - config EXAMPLE_ETH_PHY_LAN8720 - bool "LAN8720" + if EXAMPLE_CONNECT_ETHERNET + choice EXAMPLE_USE_ETHERNET + prompt "Ethernet Type" + default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32 + default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32 help - LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. - Goto https://www.microchip.com/LAN8720A for more information about it. + Select which kind of Ethernet will be used in the example. - config EXAMPLE_ETH_PHY_DP83848 - bool "DP83848" - help - DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. - Goto http://www.ti.com/product/DP83848J for more information about it. + config EXAMPLE_USE_INTERNAL_ETHERNET + depends on IDF_TARGET_ESP32 + select ETH_USE_ESP32_EMAC + bool "Internal EMAC" + help + Select internal Ethernet MAC controller. - endchoice + config EXAMPLE_USE_SPI_ETHERNET + bool "SPI Ethernet Module" + select ETH_USE_SPI_ETHERNET + help + Select external SPI-Ethernet module. + endchoice + + if EXAMPLE_USE_INTERNAL_ETHERNET + choice EXAMPLE_ETH_PHY_MODEL + prompt "Ethernet PHY Device" + default EXAMPLE_ETH_PHY_IP101 + help + Select the Ethernet PHY device to use in the example. + + config EXAMPLE_ETH_PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + + config EXAMPLE_ETH_PHY_RTL8201 + bool "RTL8201/SR8201" + help + RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. + Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. + + config EXAMPLE_ETH_PHY_LAN8720 + bool "LAN8720" + help + LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. + Goto https://www.microchip.com/LAN8720A for more information about it. + + config EXAMPLE_ETH_PHY_DP83848 + bool "DP83848" + help + DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. + Goto http://www.ti.com/product/DP83848J for more information about it. + endchoice + endif + + if EXAMPLE_USE_SPI_ETHERNET + config EXAMPLE_ETH_SPI_HOST + int "SPI Host Number" + range 0 2 + default 1 + help + Set the SPI host used to communicate with DM9051. + + config EXAMPLE_ETH_SCLK_GPIO + int "SPI SCLK GPIO number" + range 0 33 + default 19 + help + Set the GPIO number used by SPI SCLK. + + config EXAMPLE_ETH_MOSI_GPIO + int "SPI MOSI GPIO number" + range 0 33 + default 23 + help + Set the GPIO number used by SPI MOSI. + + config EXAMPLE_ETH_MISO_GPIO + int "SPI MISO GPIO number" + range 0 33 + default 25 + help + Set the GPIO number used by SPI MISO. + + config EXAMPLE_ETH_CS_GPIO + int "SPI CS GPIO number" + range 0 33 + default 22 + help + Set the GPIO number used by SPI CS. + + config EXAMPLE_ETH_SPI_CLOCK_MHZ + int "SPI clock speed (MHz)" + range 20 80 + default 20 + help + Set the clock speed (MHz) of SPI interface. + endif + endif config EXAMPLE_CONNECT_IPV6 bool "Obtain IPv6 link-local address" @@ -69,5 +135,4 @@ menu "Example Connection Configuration" help By default, examples will wait until IPv4 and IPv6 addresses are obtained. Disable this option if the network does not support IPv6. - endmenu diff --git a/examples/common_components/protocol_examples_common/connect.c b/examples/common_components/protocol_examples_common/connect.c index f12d40dda..68895905a 100644 --- a/examples/common_components/protocol_examples_common/connect.c +++ b/examples/common_components/protocol_examples_common/connect.c @@ -187,8 +187,9 @@ static void start() ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL)); #endif eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - s_mac = esp_eth_mac_new_esp32(&mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); +#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET + s_mac = esp_eth_mac_new_esp32(&mac_config); #if CONFIG_EXAMPLE_ETH_PHY_IP101 s_phy = esp_eth_phy_new_ip101(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 @@ -197,9 +198,33 @@ static void start() s_phy = esp_eth_phy_new_lan8720(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_DP83848 s_phy = esp_eth_phy_new_dp83848(&phy_config); +#endif +#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET + gpio_install_isr_service(0); + spi_device_handle_t spi_handle = NULL; + spi_bus_config_t buscfg = { + .miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO, + .mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO, + .sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1)); + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .mode = 0, + .clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000, + .spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO, + .queue_size = 20 + }; + ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle)); + /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */ + mac_config.spi_hdl = spi_handle; + s_mac = esp_eth_mac_new_dm9051(&mac_config); + s_phy = esp_eth_phy_new_dm9051(&phy_config); #endif esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy); - ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle)); s_connection_name = "Ethernet"; } diff --git a/examples/ethernet/basic/README.md b/examples/ethernet/basic/README.md index fead5dd44..57f4b472c 100644 --- a/examples/ethernet/basic/README.md +++ b/examples/ethernet/basic/README.md @@ -15,35 +15,47 @@ If you have a new Ethernet application to go (for example, connect to IoT cloud ### Hardware Required -To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves. +To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves. + +`esp_eth` component not only supports ESP32 internal Ethernet MAC controller, but also can drive third-party Ethernet module which integrates MAC and PHY and provides SPI interface. This example also take the **DM9051** as an example, illustrating how to install the Ethernet driver with only a little different configuration. ### Project configuration in menuconfig Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system. 1. In the `Example Configuration` menu: + * Choose the kind of Ethernet this example will run on under `Ethernet Type`. + * If `Internal EMAC` is selected: + * Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. -* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. + * If `SPI Ethernet Module` is selected: + * Set SPI specific configuration, including GPIO and clock speed. 2. In the `Component config > Ethernet` menu: + * If `Internal EMAC` is selected: + * Enable `Use ESP32 internal EMAC controller`, and then go into this menu. + * In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins. + * In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`. + * Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17. + * Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock. + * Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below: -* Enable `Use ESP32 internal EMAC controller`, and then go into this menu. -* In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins. -* In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`. -* Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17. -* Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock. -* Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below: + | Default Example GPIO | RMII Signal | Notes | + | -------------------- | ----------- | ------------- | + | GPIO23 | MDC | Output to PHY | + | GPIO18 | MDIO | Bidirectional | - | Default Example GPIO | RMII Signal | Notes | - | -------------------- | ----------- | ------------- | - | GPIO23 | MDC | Output to PHY | - | GPIO18 | MDIO | Bidirectional | + * If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`. + + * If `SPI Ethernet Module` is selected: + * Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`. -* If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`. ### Extra configuration in the code (Optional) * By default Ethernet driver will assume the PHY address to `1`, but you can alway reconfigure this value after `eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();`. The actual PHY address should depend on the hardware you use, so make sure to consult the schematic and datasheet. + +**Note:** DM9051 has a fixed PHY address `1`, which cannot be modified. ### Build and Flash @@ -56,20 +68,20 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l ## Example Output ```bash -I (336) cpu_start: Starting scheduler on PRO CPU. -I (0) cpu_start: Starting scheduler on APP CPU. -I (382) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE -I (392) eth_example: Ethernet Started -I (3922) eth_example: Ethernet Link Up -I (5862) tcpip_adapter: eth ip: 192.168.2.151, mask: 255.255.255.0, gw: 192.168.2.2 -I (5862) eth_example: Ethernet Got IP Address -I (5862) eth_example: ~~~~~~~~~~~ -I (5862) eth_example: ETHIP:192.168.2.151 -I (5872) eth_example: ETHMASK:255.255.255.0 -I (5872) eth_example: ETHGW:192.168.2.2 -I (5882) eth_example: ~~~~~~~~~~~ +I (394) eth_example: Ethernet Started +I (3934) eth_example: Ethernet Link Up +I (3934) eth_example: Ethernet HW Addr 30:ae:a4:c6:87:5b +I (5864) tcpip_adapter: eth ip: 192.168.2.151, mask: 255.255.255.0, gw: 192.168.2.2 +I (5864) eth_example: Ethernet Got IP Address +I (5864) eth_example: ~~~~~~~~~~~ +I (5864) eth_example: ETHIP:192.168.2.151 +I (5874) eth_example: ETHMASK:255.255.255.0 +I (5874) eth_example: ETHGW:192.168.2.2 +I (5884) eth_example: ~~~~~~~~~~~ ``` +Now you can ping your ESP32 in the terminal by entering `ping 192.168.2.151` (it depends on the actual IP address you get). + ## Troubleshooting * RMII Clock diff --git a/examples/ethernet/basic/main/Kconfig.projbuild b/examples/ethernet/basic/main/Kconfig.projbuild index 104011654..9c6cae1f7 100644 --- a/examples/ethernet/basic/main/Kconfig.projbuild +++ b/examples/ethernet/basic/main/Kconfig.projbuild @@ -1,34 +1,99 @@ menu "Example Configuration" - - choice EXAMPLE_ETH_PHY_MODEL - prompt "Ethernet PHY Device" - default EXAMPLE_ETH_PHY_IP101 + choice EXAMPLE_USE_ETHERNET + prompt "Ethernet Type" + default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32 + default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32 help - Select the Ethernet PHY device to use in the example. + Select which kind of Ethernet will be used in the example. - config EXAMPLE_ETH_PHY_IP101 - bool "IP101" + config EXAMPLE_USE_INTERNAL_ETHERNET + depends on IDF_TARGET_ESP32 + select ETH_USE_ESP32_EMAC + bool "Internal EMAC" help - IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. - Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + Select internal Ethernet MAC controller. - config EXAMPLE_ETH_PHY_RTL8201 - bool "RTL8201/SR8201" + config EXAMPLE_USE_SPI_ETHERNET + bool "SPI Ethernet Module" + select ETH_USE_SPI_ETHERNET help - RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. - Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. - - config EXAMPLE_ETH_PHY_LAN8720 - bool "LAN8720" - help - LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. - Goto https://www.microchip.com/LAN8720A for more information about it. - - config EXAMPLE_ETH_PHY_DP83848 - bool "DP83848" - help - DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. - Goto http://www.ti.com/product/DP83848J for more information about it. + Select external SPI-Ethernet module. endchoice + if EXAMPLE_USE_INTERNAL_ETHERNET + choice EXAMPLE_ETH_PHY_MODEL + prompt "Ethernet PHY Device" + default EXAMPLE_ETH_PHY_IP101 + help + Select the Ethernet PHY device to use in the example. + + config EXAMPLE_ETH_PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + + config EXAMPLE_ETH_PHY_RTL8201 + bool "RTL8201/SR8201" + help + RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. + Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. + + config EXAMPLE_ETH_PHY_LAN8720 + bool "LAN8720" + help + LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. + Goto https://www.microchip.com/LAN8720A for more information about it. + + config EXAMPLE_ETH_PHY_DP83848 + bool "DP83848" + help + DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. + Goto http://www.ti.com/product/DP83848J for more information about it. + endchoice + endif + + if EXAMPLE_USE_SPI_ETHERNET + config EXAMPLE_ETH_SPI_HOST + int "SPI Host Number" + range 0 2 + default 1 + help + Set the SPI host used to communicate with DM9051. + + config EXAMPLE_ETH_SCLK_GPIO + int "SPI SCLK GPIO number" + range 0 33 + default 19 + help + Set the GPIO number used by SPI SCLK. + + config EXAMPLE_ETH_MOSI_GPIO + int "SPI MOSI GPIO number" + range 0 33 + default 23 + help + Set the GPIO number used by SPI MOSI. + + config EXAMPLE_ETH_MISO_GPIO + int "SPI MISO GPIO number" + range 0 33 + default 25 + help + Set the GPIO number used by SPI MISO. + + config EXAMPLE_ETH_CS_GPIO + int "SPI CS GPIO number" + range 0 33 + default 22 + help + Set the GPIO number used by SPI CS. + + config EXAMPLE_ETH_SPI_CLOCK_MHZ + int "SPI clock speed (MHz)" + range 20 80 + default 20 + help + Set the clock speed (MHz) of SPI interface. + endif endmenu diff --git a/examples/ethernet/basic/main/ethernet_example_main.c b/examples/ethernet/basic/main/ethernet_example_main.c index 4c52b3381..ed794a32b 100644 --- a/examples/ethernet/basic/main/ethernet_example_main.c +++ b/examples/ethernet/basic/main/ethernet_example_main.c @@ -17,15 +17,21 @@ #include "sdkconfig.h" static const char *TAG = "eth_example"; -static esp_eth_handle_t s_eth_handle = NULL; /** Event handler for Ethernet events */ static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + uint8_t mac_addr[6] = {0}; + /* we can get the ethernet driver handle from event data */ + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + switch (event_id) { case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); break; case ETHERNET_EVENT_DISCONNECTED: ESP_LOGI(TAG, "Ethernet Link Down"); @@ -66,8 +72,9 @@ void app_main() ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); +#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); #if CONFIG_EXAMPLE_ETH_PHY_IP101 esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 @@ -76,7 +83,33 @@ void app_main() esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_DP83848 esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config); +#endif +#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET + gpio_install_isr_service(0); + spi_device_handle_t spi_handle = NULL; + spi_bus_config_t buscfg = { + .miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO, + .mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO, + .sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1)); + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .mode = 0, + .clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000, + .spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO, + .queue_size = 20 + }; + ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle)); + /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */ + mac_config.spi_hdl = spi_handle; + esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&mac_config); + esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config); #endif esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); - ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle)); + esp_eth_handle_t eth_handle = NULL; + ESP_ERROR_CHECK(esp_eth_driver_install(&config, ð_handle)); } diff --git a/examples/ethernet/eth2ap/README.md b/examples/ethernet/eth2ap/README.md index 4d1c8e0f2..26d81db12 100644 --- a/examples/ethernet/eth2ap/README.md +++ b/examples/ethernet/eth2ap/README.md @@ -12,38 +12,48 @@ The similarities on MAC layer between Ethernet and Wi-Fi make it easy to forward ### Hardware Required -To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves. +To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves. + +`esp_eth` component not only supports ESP32 internal Ethernet MAC controller, but also can drive third-party Ethernet module which integrates MAC and PHY and provides SPI interface. This example also take the **DM9051** as an example, illustrating how to install the Ethernet driver with only a little different configuration. ### Project configuration in menuconfig Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system. 1. In the `Example Configuration` menu: + * Set the SSID and password for Wi-Fi ap interface under `Wi-Fi SSID` and `Wi-Fi Password`. + * Set the maximum connection number under `Maximum STA connections`. + * Choose the kind of Ethernet this example will run on under `Ethernet Type`. + * If `Internal EMAC` is selected: + * Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. -* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. -* Set the SSID and password for Wi-Fi ap interface under `Wi-Fi SSID` and `Wi-Fi Password`. -* Set the maximum connection number under `Maximum STA connections`. + * If `SPI Ethernet Module` is selected: + * Set SPI specific configuration, including GPIO and clock speed. 2. In the `Component config > Ethernet` menu: + * If `Internal EMAC` is selected: + * Enable `Use ESP32 internal EMAC controller`, and then go into this menu. + * In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins. + * In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`. + * Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17. + * Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock. + * Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below: -* Enable `Use ESP32 internal EMAC controller`, and then go into this menu. -* In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins. -* In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`. -* Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17. -* Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock. -* Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below: + | Default Example GPIO | RMII Signal | Notes | + | -------------------- | ----------- | ------------- | + | GPIO23 | MDC | Output to PHY | + | GPIO18 | MDIO | Bidirectional | - | Default Example GPIO | RMII Signal | Notes | - | -------------------- | ----------- | ------------- | - | GPIO23 | MDC | Output to PHY | - | GPIO18 | MDIO | Bidirectional | + * If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`. -* If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`. + * If `SPI Ethernet Module` is selected: + * Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`. ### Extra configuration in the code (Optional) * By default Ethernet driver will assume the PHY address to `1`, but you can alway reconfigure this value after `eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();`. The actual PHY address should depend on the hardware you use, so make sure to consult the schematic and datasheet.peripheral (e.g. I²S), you'd better choose the external clock. +**Note:** DM9051 has a fixed PHY address `1`, which cannot be modified. ### Build and Flash diff --git a/examples/ethernet/eth2ap/main/Kconfig.projbuild b/examples/ethernet/eth2ap/main/Kconfig.projbuild index 7c6b87810..40b54b9ad 100644 --- a/examples/ethernet/eth2ap/main/Kconfig.projbuild +++ b/examples/ethernet/eth2ap/main/Kconfig.projbuild @@ -1,36 +1,102 @@ menu "Example Configuration" - - choice EXAMPLE_ETH_PHY_MODEL - prompt "Ethernet PHY Device" - default EXAMPLE_ETH_PHY_IP101 + choice EXAMPLE_USE_ETHERNET + prompt "Ethernet Type" + default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32 + default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32 help - Select the Ethernet PHY device to use in the example. + Select which kind of Ethernet will be used in the example. - config EXAMPLE_ETH_PHY_IP101 - bool "IP101" + config EXAMPLE_USE_INTERNAL_ETHERNET + depends on IDF_TARGET_ESP32 + select ETH_USE_ESP32_EMAC + bool "Internal EMAC" help - IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. - Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + Select internal Ethernet MAC controller. - config EXAMPLE_ETH_PHY_RTL8201 - bool "RTL8201/SR8201" + config EXAMPLE_USE_SPI_ETHERNET + bool "SPI Ethernet Module" + select ETH_USE_SPI_ETHERNET help - RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. - Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. - - config EXAMPLE_ETH_PHY_LAN8720 - bool "LAN8720" - help - LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. - Goto https://www.microchip.com/LAN8720A for more information about it. - - config EXAMPLE_ETH_PHY_DP83848 - bool "DP83848" - help - DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. - Goto http://www.ti.com/product/DP83848J for more information about it. + Select external SPI-Ethernet module. endchoice + if EXAMPLE_USE_INTERNAL_ETHERNET + choice EXAMPLE_ETH_PHY_MODEL + prompt "Ethernet PHY Device" + default EXAMPLE_ETH_PHY_IP101 + help + Select the Ethernet PHY device to use in the example. + + config EXAMPLE_ETH_PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + + config EXAMPLE_ETH_PHY_RTL8201 + bool "RTL8201/SR8201" + help + RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. + Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. + + config EXAMPLE_ETH_PHY_LAN8720 + bool "LAN8720" + help + LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. + Goto https://www.microchip.com/LAN8720A for more information about it. + + config EXAMPLE_ETH_PHY_DP83848 + bool "DP83848" + help + DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. + Goto http://www.ti.com/product/DP83848J for more information about it. + endchoice + endif + + if EXAMPLE_USE_SPI_ETHERNET + config EXAMPLE_ETH_SPI_HOST + int "SPI Host Number" + range 0 2 + default 1 + help + Set the SPI host used to communicate with DM9051. + + config EXAMPLE_ETH_SCLK_GPIO + int "SPI SCLK GPIO number" + range 0 33 + default 19 + help + Set the GPIO number used by SPI SCLK. + + config EXAMPLE_ETH_MOSI_GPIO + int "SPI MOSI GPIO number" + range 0 33 + default 23 + help + Set the GPIO number used by SPI MOSI. + + config EXAMPLE_ETH_MISO_GPIO + int "SPI MISO GPIO number" + range 0 33 + default 25 + help + Set the GPIO number used by SPI MISO. + + config EXAMPLE_ETH_CS_GPIO + int "SPI CS GPIO number" + range 0 33 + default 22 + help + Set the GPIO number used by SPI CS. + + config EXAMPLE_ETH_SPI_CLOCK_MHZ + int "SPI clock speed (MHz)" + range 20 80 + default 20 + help + Set the clock speed (MHz) of SPI interface. + endif + config EXAMPLE_WIFI_SSID string "Wi-Fi SSID" default "eth2ap" diff --git a/examples/ethernet/eth2ap/main/ethernet_example_main.c b/examples/ethernet/eth2ap/main/ethernet_example_main.c index a28bd7740..6ea43dd3b 100644 --- a/examples/ethernet/eth2ap/main/ethernet_example_main.c +++ b/examples/ethernet/eth2ap/main/ethernet_example_main.c @@ -145,8 +145,9 @@ static void initialize_ethernet(void) { ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL)); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); +#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); #if CONFIG_EXAMPLE_ETH_PHY_IP101 esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 @@ -155,6 +156,31 @@ static void initialize_ethernet(void) esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_DP83848 esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config); +#endif +#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET + gpio_install_isr_service(0); + spi_device_handle_t spi_handle = NULL; + spi_bus_config_t buscfg = { + .miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO, + .mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO, + .sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1)); + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .mode = 0, + .clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000, + .spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO, + .queue_size = 20 + }; + ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle)); + /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */ + mac_config.spi_hdl = spi_handle; + esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&mac_config); + esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config); #endif esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); config.stack_input = pkt_eth2wifi; diff --git a/examples/ethernet/iperf/README.md b/examples/ethernet/iperf/README.md index 47ed723f8..c75e90096 100644 --- a/examples/ethernet/iperf/README.md +++ b/examples/ethernet/iperf/README.md @@ -10,9 +10,13 @@ The cli environment in the example is based on the [console component](https://d ## How to use example -To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves. +### Hardware Required -### Prepare work +To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves. + +`esp_eth` component not only supports ESP32 internal Ethernet MAC controller, but also can drive third-party Ethernet module which integrates MAC and PHY and provides SPI interface. This example also take the **DM9051** as an example, illustrating how to install the Ethernet driver with only a little different configuration. + +### Other Preparation 1. Install iperf tool on PC * Debian/Ubuntu: `sudo apt-get install iperf` @@ -24,30 +28,39 @@ To run this example, it's recommended that you have an official ESP32 Ethernet d Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system. 1. In the `Example Configuration` menu: + * Enable storing history commands in flash under `Store command history in flash`. + * Choose the kind of Ethernet this example will run on under `Ethernet Type`. + * If `Internal EMAC` is selected: + * Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. -* Enable storing history commands in flash under `Store command history in flash`. -* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board. + * If `SPI Ethernet Module` is selected: + * Set SPI specific configuration, including GPIO and clock speed. 2. In the `Component config > Ethernet` menu: + * If `Internal EMAC` is selected: + * Enable `Use ESP32 internal EMAC controller`, and then go into this menu. + * In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins. + * In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`. + * Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17. + * Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock. + * Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below: -* Enable `Use ESP32 internal EMAC controller`, and then go into this menu. -* In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins. -* In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`. -* Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17. -* Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock. -* Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below: + | Default Example GPIO | RMII Signal | Notes | + | -------------------- | ----------- | ------------- | + | GPIO23 | MDC | Output to PHY | + | GPIO18 | MDIO | Bidirectional | - | Default Example GPIO | RMII Signal | Notes | - | -------------------- | ----------- | ------------- | - | GPIO23 | MDC | Output to PHY | - | GPIO18 | MDIO | Bidirectional | + * If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`. -* If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`. + * If `SPI Ethernet Module` is selected: + * Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`. ### Extra configuration in the code (Optional) * By default Ethernet driver will assume the PHY address to `1`, but you can alway reconfigure this value after `eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();`. The actual PHY address should depend on the hardware you use, so make sure to consult the schematic and datasheet. +**Note:** DM9051 has a fixed PHY address `1`, which cannot be modified. + ### Build and Flash Enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you' are using CMake based build system. @@ -73,15 +86,15 @@ UDP buffer size: 208 KByte (default) ------------------------------------------------------------ [ 3] local 192.168.2.160 port 5001 connected with 192.168.2.156 port 49154 [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams -[ 3] 0.0- 3.0 sec 24.8 MBytes 69.5 Mbits/sec 0.172 ms 1/17721 (0.0056%) -[ 3] 3.0- 6.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17719 (0%) -[ 3] 6.0- 9.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17719 (0%) -[ 3] 9.0-12.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17718 (0%) -[ 3] 12.0-15.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17717 (0%) -[ 3] 15.0-18.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17720 (0%) -[ 3] 18.0-21.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17721 (0%) -[ 3] 21.0-24.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17720 (0%) -[ 3] 24.0-27.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17723 (0%) +[ 3] 0.0- 3.0 sec 26.1 MBytes 72.8 Mbits/sec 0.198 ms 1/18585 (0.0054%) +[ 3] 3.0- 6.0 sec 26.3 MBytes 73.7 Mbits/sec 0.192 ms 0/18792 (0%) +[ 3] 6.0- 9.0 sec 26.3 MBytes 73.5 Mbits/sec 0.189 ms 0/18741 (0%) +[ 3] 9.0-12.0 sec 26.2 MBytes 73.3 Mbits/sec 0.191 ms 43/18739 (0.23%) +[ 3] 12.0-15.0 sec 26.3 MBytes 73.5 Mbits/sec 0.194 ms 0/18739 (0%) +[ 3] 15.0-18.0 sec 26.3 MBytes 73.5 Mbits/sec 0.191 ms 0/18741 (0%) +[ 3] 18.0-21.0 sec 26.3 MBytes 73.5 Mbits/sec 0.187 ms 0/18752 (0%) +[ 3] 21.0-24.0 sec 26.3 MBytes 73.4 Mbits/sec 0.192 ms 0/18737 (0%) +[ 3] 24.0-27.0 sec 26.3 MBytes 73.5 Mbits/sec 0.188 ms 0/18739 (0%) ``` #### ESP32 output @@ -89,16 +102,17 @@ UDP buffer size: 208 KByte (default) ```bash mode=udp-client sip=192.168.2.156:5001, dip=192.168.2.160:5001, interval=3, time=30 Interval Bandwidth - 0- 3 sec 69.34 Mbits/sec - 3- 6 sec 69.55 Mbits/sec - 6- 9 sec 69.55 Mbits/sec - 9- 12 sec 69.55 Mbits/sec - 12- 15 sec 69.55 Mbits/sec - 15- 18 sec 69.56 Mbits/sec - 18- 21 sec 69.56 Mbits/sec - 21- 24 sec 69.56 Mbits/sec - 24- 27 sec 69.56 Mbits/sec - 27- 30 sec 69.56 Mbits/sec + 0- 3 sec 72.92 Mbits/sec + 3- 6 sec 73.76 Mbits/sec + 6- 9 sec 73.56 Mbits/sec + 9- 12 sec 73.56 Mbits/sec + 12- 15 sec 73.56 Mbits/sec + 15- 18 sec 73.56 Mbits/sec + 18- 21 sec 73.61 Mbits/sec + 21- 24 sec 73.55 Mbits/sec + 24- 27 sec 73.56 Mbits/sec + 27- 30 sec 73.56 Mbits/sec + 0- 30 sec 73.52 Mbits/sec ``` ### Test downlink bandwidth @@ -119,11 +133,13 @@ UDP buffer size: 208 KByte (default) [ 3] 3.0- 6.0 sec 28.6 MBytes 80.0 Mbits/sec [ 3] 6.0- 9.0 sec 28.6 MBytes 80.0 Mbits/sec [ 3] 9.0-12.0 sec 28.6 MBytes 80.0 Mbits/sec -[ 3] 12.0-15.0 sec 28.6 MBytes 79.9 Mbits/sec -[ 3] 15.0-18.0 sec 28.6 MBytes 80.0 Mbits/sec -[ 3] 18.0-21.0 sec 28.6 MBytes 80.0 Mbits/sec -[ 3] 21.0-24.0 sec 28.6 MBytes 80.0 Mbits/sec +[ 3] 12.0-15.0 sec 28.4 MBytes 79.5 Mbits/sec +[ 3] 15.0-18.0 sec 28.6 MBytes 79.9 Mbits/sec +[ 3] 18.0-21.0 sec 28.6 MBytes 79.9 Mbits/sec +[ 3] 21.0-24.0 sec 28.6 MBytes 79.9 Mbits/sec [ 3] 24.0-27.0 sec 28.6 MBytes 80.0 Mbits/sec +[ 3] 27.0-30.0 sec 28.5 MBytes 79.7 Mbits/sec +[ 3] 0.0-30.0 sec 286 MBytes 79.9 Mbits/sec ``` #### ESP32 output @@ -131,16 +147,17 @@ UDP buffer size: 208 KByte (default) mode=udp-server sip=192.168.2.156:5001, dip=0.0.0.0:5001, interval=3, time=30 Interval Bandwidth I (2534456) iperf: want recv=16384 - 0- 30 sec 72.67 Mbits/sec - 3- 6 sec 74.18 Mbits/sec - 6- 9 sec 73.14 Mbits/sec - 9- 12 sec 73.65 Mbits/sec - 12- 15 sec 72.87 Mbits/sec - 15- 18 sec 73.29 Mbits/sec - 18- 21 sec 74.35 Mbits/sec - 21- 24 sec 72.28 Mbits/sec - 24- 27 sec 73.39 Mbits/sec - 27- 30 sec 73.49 Mbits/sec + 0- 3 sec 79.36 Mbits/sec + 3- 6 sec 79.56 Mbits/sec + 6- 9 sec 79.51 Mbits/sec + 9- 12 sec 79.24 Mbits/sec + 12- 15 sec 77.33 Mbits/sec + 15- 18 sec 79.01 Mbits/sec + 18- 21 sec 78.58 Mbits/sec + 21- 24 sec 78.24 Mbits/sec + 24- 27 sec 79.56 Mbits/sec + 27- 30 sec 77.20 Mbits/sec + 0- 30 sec 78.76 Mbits/sec ``` ## Suggestions of getting higher bandwidth diff --git a/examples/ethernet/iperf/main/Kconfig.projbuild b/examples/ethernet/iperf/main/Kconfig.projbuild index 001b648a5..b84eefa7e 100644 --- a/examples/ethernet/iperf/main/Kconfig.projbuild +++ b/examples/ethernet/iperf/main/Kconfig.projbuild @@ -1,5 +1,4 @@ menu "Example Configuration" - config EXAMPLE_STORE_HISTORY bool "Store command history in flash" default y @@ -8,35 +7,101 @@ menu "Example Configuration" command history. If this option is enabled, initalizes a FAT filesystem and uses it to store command history. - choice EXAMPLE_ETH_PHY_MODEL - prompt "Ethernet PHY Device" - default EXAMPLE_ETH_PHY_IP101 + choice EXAMPLE_USE_ETHERNET + prompt "Ethernet Type" + default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32 + default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32 help - Select the Ethernet PHY device to use in the example. + Select which kind of Ethernet will be used in the example. - config EXAMPLE_ETH_PHY_IP101 - bool "IP101" + config EXAMPLE_USE_INTERNAL_ETHERNET + depends on IDF_TARGET_ESP32 + select ETH_USE_ESP32_EMAC + bool "Internal EMAC" help - IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. - Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + Select internal Ethernet MAC controller. - config EXAMPLE_ETH_PHY_RTL8201 - bool "RTL8201/SR8201" + config EXAMPLE_USE_SPI_ETHERNET + bool "SPI Ethernet Module" + select ETH_USE_SPI_ETHERNET help - RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. - Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. - - config EXAMPLE_ETH_PHY_LAN8720 - bool "LAN8720" - help - LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. - Goto https://www.microchip.com/LAN8720A for more information about it. - - config EXAMPLE_ETH_PHY_DP83848 - bool "DP83848" - help - DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. - Goto http://www.ti.com/product/DP83848J for more information about it. + Select external SPI-Ethernet module. endchoice + if EXAMPLE_USE_INTERNAL_ETHERNET + choice EXAMPLE_ETH_PHY_MODEL + prompt "Ethernet PHY Device" + default EXAMPLE_ETH_PHY_IP101 + help + Select the Ethernet PHY device to use in the example. + + config EXAMPLE_ETH_PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + + config EXAMPLE_ETH_PHY_RTL8201 + bool "RTL8201/SR8201" + help + RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX. + Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it. + + config EXAMPLE_ETH_PHY_LAN8720 + bool "LAN8720" + help + LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support. + Goto https://www.microchip.com/LAN8720A for more information about it. + + config EXAMPLE_ETH_PHY_DP83848 + bool "DP83848" + help + DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver. + Goto http://www.ti.com/product/DP83848J for more information about it. + endchoice + endif + + if EXAMPLE_USE_SPI_ETHERNET + config EXAMPLE_ETH_SPI_HOST + int "SPI Host Number" + range 0 2 + default 1 + help + Set the SPI host used to communicate with DM9051. + + config EXAMPLE_ETH_SCLK_GPIO + int "SPI SCLK GPIO number" + range 0 33 + default 19 + help + Set the GPIO number used by SPI SCLK. + + config EXAMPLE_ETH_MOSI_GPIO + int "SPI MOSI GPIO number" + range 0 33 + default 23 + help + Set the GPIO number used by SPI MOSI. + + config EXAMPLE_ETH_MISO_GPIO + int "SPI MISO GPIO number" + range 0 33 + default 25 + help + Set the GPIO number used by SPI MISO. + + config EXAMPLE_ETH_CS_GPIO + int "SPI CS GPIO number" + range 0 33 + default 22 + help + Set the GPIO number used by SPI CS. + + config EXAMPLE_ETH_SPI_CLOCK_MHZ + int "SPI clock speed (MHz)" + range 20 80 + default 20 + help + Set the clock speed (MHz) of SPI interface. + endif endmenu diff --git a/examples/ethernet/iperf/main/cmd_ethernet.c b/examples/ethernet/iperf/main/cmd_ethernet.c index 1692a5dd6..832d049b0 100644 --- a/examples/ethernet/iperf/main/cmd_ethernet.c +++ b/examples/ethernet/iperf/main/cmd_ethernet.c @@ -183,8 +183,9 @@ void register_ethernet() ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &event_handler, NULL)); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); +#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); #if CONFIG_EXAMPLE_ETH_PHY_IP101 esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_RTL8201 @@ -193,6 +194,31 @@ void register_ethernet() esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config); #elif CONFIG_EXAMPLE_ETH_PHY_DP83848 esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config); +#endif +#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET + gpio_install_isr_service(0); + spi_device_handle_t spi_handle = NULL; + spi_bus_config_t buscfg = { + .miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO, + .mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO, + .sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1)); + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .mode = 0, + .clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000, + .spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO, + .queue_size = 20 + }; + ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle)); + /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */ + mac_config.spi_hdl = spi_handle; + esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&mac_config); + esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config); #endif esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); ESP_ERROR_CHECK(esp_eth_driver_install(&config, ð_handle)); diff --git a/examples/ethernet/iperf/main/ethernet_example_main.c b/examples/ethernet/iperf/main/ethernet_example_main.c index 53e5cfb98..1f0718fd0 100644 --- a/examples/ethernet/iperf/main/ethernet_example_main.c +++ b/examples/ethernet/iperf/main/ethernet_example_main.c @@ -85,7 +85,7 @@ static void initialize_console() /* Initialize the console */ esp_console_config_t console_config = { - .max_cmdline_args = 8, + .max_cmdline_args = 16, .max_cmdline_length = 256, #if CONFIG_LOG_COLORS .hint_color = atoi(LOG_COLOR_CYAN)