Merge branch 'feature/support_spi_ethernet' into 'master'

ethernet: support dm9051

Closes IDFGH-1443 and IDFGH-1439

See merge request espressif/esp-idf!5345
This commit is contained in:
Ivan Grokhotkov 2019-07-04 21:30:52 +08:00
commit 8bed3a3dd2
23 changed files with 1945 additions and 256 deletions

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -20,6 +20,11 @@ extern "C" {
#include <stdbool.h>
#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

View file

@ -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

View file

@ -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)

View file

@ -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 <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#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;
}

View file

@ -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:

View file

@ -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 <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#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;
}

View file

@ -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, &eth_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, &eth_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, &eth_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

View file

@ -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 */

View file

@ -1,5 +1,4 @@
menu "Example Connection Configuration"
choice EXAMPLE_CONNECT_INTERFACE
prompt "Connect using"
default EXAMPLE_CONNECT_WIFI
@ -15,27 +14,49 @@ menu "Example Connection Configuration"
endchoice
if EXAMPLE_CONNECT_WIFI
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.
endif
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
Select which kind of Ethernet will be used in the example.
config EXAMPLE_USE_INTERNAL_ETHERNET
depends on IDF_TARGET_ESP32
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Select internal Ethernet MAC controller.
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
depends on EXAMPLE_CONNECT_ETHERNET
prompt "Ethernet PHY Device"
default EXAMPLE_ETH_PHY_IP101
help
Select the PHY driver to use for the example.
Select the Ethernet PHY device to use in the example.
config EXAMPLE_ETH_PHY_IP101
bool "IP101"
@ -60,8 +81,53 @@ menu "Example Connection Configuration"
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

View file

@ -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";
}

View file

@ -15,18 +15,24 @@ 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.
2. In the `Component config > Ethernet` menu:
* 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`.
@ -41,10 +47,16 @@ Enter `make menuconfig` if you are using GNU Make based build system or enter `i
* 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.
@ -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

View file

@ -1,5 +1,26 @@
menu "Example Configuration"
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 which kind of Ethernet will be used in the example.
config EXAMPLE_USE_INTERNAL_ETHERNET
depends on IDF_TARGET_ESP32
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Select internal Ethernet MAC controller.
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
@ -30,5 +51,49 @@ menu "Example Configuration"
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

View file

@ -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, &eth_handle));
}

View file

@ -12,20 +12,26 @@ 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:
* 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`.
* 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.
* 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`.
@ -40,10 +46,14 @@ Enter `make menuconfig` if you are using GNU Make based build system or enter `i
* 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

View file

@ -1,5 +1,26 @@
menu "Example Configuration"
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 which kind of Ethernet will be used in the example.
config EXAMPLE_USE_INTERNAL_ETHERNET
depends on IDF_TARGET_ESP32
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Select internal Ethernet MAC controller.
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
@ -30,6 +51,51 @@ menu "Example Configuration"
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"

View file

@ -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;

View file

@ -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,12 +28,16 @@ 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.
2. In the `Component config > Ethernet` menu:
* 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`.
@ -44,10 +52,15 @@ Enter `make menuconfig` if you are using GNU Make based build system or enter `i
* 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

View file

@ -1,5 +1,4 @@
menu "Example Configuration"
config EXAMPLE_STORE_HISTORY
bool "Store command history in flash"
default y
@ -8,6 +7,28 @@ menu "Example Configuration"
command history. If this option is enabled, initalizes a FAT filesystem
and uses it to store command history.
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 which kind of Ethernet will be used in the example.
config EXAMPLE_USE_INTERNAL_ETHERNET
depends on IDF_TARGET_ESP32
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Select internal Ethernet MAC controller.
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
@ -38,5 +59,49 @@ menu "Example Configuration"
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

View file

@ -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, &eth_handle));

View file

@ -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)