添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
# Ethernet Examples
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
## Common Pin Assignments
### Using ESP32 internal MAC
* RMII PHY wiring is fixed and can not be changed through either IOMUX or GPIO Matrix. By default, they're connected as follows:
| GPIO | RMII Signal | Notes |
| ------ | ----------- | ------------ |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
* SMI (Serial Management Interface) wiring is not fixed. You may need to changed it according to your board schematic. By default they're connected as follows:
| GPIO | SMI Signal | Notes |
| ------ | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
* PHY chip has a reset pin, if want to do a hardware reset during initialization, then you have to connect it with one GPIO on ESP32. See more information from [here](#configure-the-project). The default GPIO used for resetting PHY chip is GPIO5.
### Using DM9051
* DM9051 Ethernet module consumes one SPI interface plus an interrupt and reset GPIO. By default they're connected as follows:
| GPIO | DM9051 |
| ------ | ----------- |
| GPIO19 | SPI_CLK |
| GPIO23 | SPI_MOSI |
| GPIO25 | SPI_MISO |
| GPIO22 | SPI_CS |
| GPIO4 | Interrupt |
| GPIO5 | Reset |
## Common Configurations
1. In the `Example Configuration` menu:
* Choose the kind of Ethernet 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.
* Set GPIO number used by SMI signal under `SMI MDC GPIO number` and `SMI MDIO GPIO number` respectively.
* If `DM9051 Module` is selected:
* Set SPI specific configuration, including SPI host number, GPIO number and clock rate.
* Set GPIO number used by PHY chip reset under `PHY Reset GPIO number`, you may have to change the default value according to your board schematic. **PHY hardware reset can be disabled by set this value to -1**.
* Set PHY address under `PHY Address`, you may have to change the default value according to your board schematic.
2. In the `Component config > Ethernet` menu:
* Under `Support ESP32 internal EMAC controller` sub-menu:
* In the `PHY interface`, select `Reduced Media Independent Interface (RMII)`, ESP-IDF currently only support RMII mode.
* In the `RMII clock mode`, select one of the source that RMII clock (50MHz) comes from: `Input RMII clock from external` or `Output RMII clock from internal`.
* If `Output RMII clock from internal` is enabled, you also have to set the GPIO number that used to output the RMII clock, under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17.
* If `Output RMII clock from GPIO0 (Experimental!)` is also enabled, then you have no choice but GPIO0 to output the RMII clock.
* In `Amount of Ethernet DMA Rx buffers` and `Amount of Ethernet DMA Tx buffers`, you can set the amount of DMA buffers used for Tx and Rx.
* Under `Support SPI to Ethernet Module` sub-menu, select the SPI module that you used for this example. Currently ESP-IDF only supports `DM9051`.
## Common Troubleshooting
* The data panel between ESP32's MAC and PHY needs a fixed 50MHz clock to do synchronization, which also called RMII clock. It can either be provided by an external oscillator or generated from internal APLL. The signal integrity of RMII clock is strict, so keep the trace as short as possible!
* If the RMII clock is generated from internal APLL, then APLL can't be used for other purpose (e.g. I2S).

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ethernet_basic)

View File

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ethernet_basic
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,69 @@
# Ethernet Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of `Ethernet driver` together with `tcpip_adapter`. The work flow of the example could be as follows:
1. Install Ethernet driver
2. Send DHCP requests and wait for a DHCP lease
3. If get IP address successfully, then you will be able to ping the device
If you have a new Ethernet application to go (for example, connect to IoT cloud via Ethernet), try this as a basic template, then add your own code.
## How to use example
### 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 up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the **DM9051** as an example, illustrating how to install the Ethernet driver in the same manner.
#### Pin Assignment
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
### Configure the project
```
idf.py menuconfig
```
See common configurations for Ethernet examples from [upper level](../README.md#common-configurations).
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```bash
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
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "ethernet_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,134 @@
menu "Example Configuration"
choice EXAMPLE_USE_ETHERNET
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_DM9051 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_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module (DM9051).
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
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with the SPI Ethernet Controller.
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 5 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
endmenu

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,130 @@
/* Ethernet Basic Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
static const char *TAG = "eth_example";
/** 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");
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
}
void app_main(void)
{
// Initialize TCP/IP network interface (should be called only once in application)
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&cfg);
// Set default handlers to process TCP/IP stuffs
ESP_ERROR_CHECK(esp_eth_set_default_handlers(eth_netif));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
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();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
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
esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_LAN8720
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_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &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_eth_handle_t eth_handle = NULL;
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));
/* attach Ethernet driver to TCP/IP stack */
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
/* start Ethernet driver state machine */
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
}

View File

@@ -0,0 +1 @@
CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(enc28j60)

View File

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := enc28j60
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,85 @@
# ENC28J60 Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
ENC28J60 is a standalone Ethernet controller with a standard SPI interface. This example demonstrates how to drive this controller as an SPI device and then attach to TCP/IP stack.
This is also an example of how to integrate a new Ethernet MAC driver into the `esp_eth` component, without needing to modify the ESP-IDF component.
If you have a more complicated application to go (for example, connect to some IoT cloud via MQTT), you can always reuse the initialization codes in this example.
## How to use example
### Hardware Required
To run this example, you need to prepare following hardwares:
* [ESP32 board](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html) (e.g. ESP32-PICO, ESP32 DevKitC, etc)
* ENC28J60 module (the latest revision should be 6)
#### Pin Assignment
* ENC28J60 Ethernet module consumes one SPI interface plus an interrupt GPIO. By default they're connected as follows:
| GPIO | ENC28J60 |
| ------ | ----------- |
| GPIO19 | SPI_CLK |
| GPIO23 | SPI_MOSI |
| GPIO25 | SPI_MISO |
| GPIO22 | SPI_CS |
| GPIO4 | Interrupt |
### Configure the project
```
idf.py menuconfig
```
In the `Example Configuration` menu, set SPI specific configuration, such as SPI host number, GPIO used for MISO/MOSI/CS signal, GPIO for interrupt event and the SPI clock rate.
**Note:** According to ENC28J60 data sheet, SPI clock could reach up to 20MHz, but in practice, the clock speed will depend on your PCB layout (in this example, the default clock rate is set to 6MHz, just to make sure that most modules on the market can work at this speed).
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```bash
I (0) cpu_start: Starting scheduler on APP CPU.
I (401) enc28j60: revision: 6
I (411) esp_eth.netif.glue: 00:04:a3:12:34:56
I (411) esp_eth.netif.glue: ethernet attached to netif
I (421) eth_example: Ethernet Started
I (2421) enc28j60: working in 10Mbps
I (2421) enc28j60: working in half duplex
I (2421) eth_example: Ethernet Link Up
I (2421) eth_example: Ethernet HW Addr 00:04:a3:12:34:56
I (4391) esp_netif_handlers: eth ip: 192.168.2.34, mask: 255.255.255.0, gw: 192.168.2.2
I (4391) eth_example: Ethernet Got IP Address
I (4391) eth_example: ~~~~~~~~~~~
I (4391) eth_example: ETHIP:192.168.2.34
I (4401) eth_example: ETHMASK:255.255.255.0
I (4401) eth_example: ETHGW:192.168.2.2
I (4411) eth_example: ~~~~~~~~~~~
```
Now you can ping your ESP32 in the terminal by entering `ping 192.168.2.34` (it depends on the actual IP address you get).
**Notes:**
1. ENC28J60 hasn't burned any valid MAC address in the chip, you need to write an unique MAC address into its internal MAC address register before any traffic happened on TX and RX line.
2. ENC28J60 does not support automatic duplex negotiation. If it is connected to an automatic duplex negotiation enabled network switch or Ethernet controller, then ENC28J60 will be detected as a half-duplex device. To communicate in Full-Duplex mode, ENC28J60 and the remote node (switch, router or Ethernet controller) must be manually configured for full-duplex operation.
## Troubleshooting
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@@ -0,0 +1,6 @@
set(srcs "enc28j60_example_main.c"
"esp_eth_mac_enc28j60.c"
"esp_eth_phy_enc28j60.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,50 @@
menu "Example Configuration"
config EXAMPLE_ENC28J60_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with the SPI Ethernet Controller.
config EXAMPLE_ENC28J60_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ENC28J60_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ENC28J60_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ENC28J60_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_ENC28J60_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 5 20
default 6
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_ENC28J60_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by ENC28J60 interrupt.
endmenu

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,284 @@
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_eth_mac.h"
#include "esp_eth_phy.h"
#include "driver/spi_master.h"
/**
* @brief SPI Instruction Set
*
*/
#define ENC28J60_SPI_CMD_RCR (0x00) // Read Control Register
#define ENC28J60_SPI_CMD_RBM (0x01) // Read Buffer Memory
#define ENC28J60_SPI_CMD_WCR (0x02) // Write Control Register
#define ENC28J60_SPI_CMD_WBM (0x03) // Write Buffer Memory
#define ENC28J60_SPI_CMD_BFS (0x04) // Bit Field Set
#define ENC28J60_SPI_CMD_BFC (0x05) // Bit Field Clear
#define ENC28J60_SPI_CMD_SRC (0x07) // Soft Reset
/**
* @brief Shared Registers in ENC28J60 (accessible on each bank)
*
*/
#define ENC28J60_EIE (0x1B) // Ethernet Interrupt Enable
#define ENC28J60_EIR (0x1C) // Ethernet Interrupt flags
#define ENC28J60_ESTAT (0x1D) // Ethernet Status
#define ENC28J60_ECON2 (0x1E) // Ethernet Control Register2
#define ENC28J60_ECON1 (0x1F) // Ethernet Control Register1
/**
* @brief Per-bank Registers in ENC28J60
* @note Address[15:12]: Register Type, 0 -> ETH, 1 -> MII/MAC
* Address[11:8] : Bank address
* Address[7:0] : Register Index
*/
// Bank 0 Registers
#define ENC28J60_ERDPTL (0x0000) // Read Pointer Low Byte ERDPT<7:0>)
#define ENC28J60_ERDPTH (0x0001) // Read Pointer High Byte (ERDPT<12:8>)
#define ENC28J60_EWRPTL (0x0002) // Write Pointer Low Byte (EWRPT<7:0>)
#define ENC28J60_EWRPTH (0x0003) // Write Pointer High Byte (EWRPT<12:8>)
#define ENC28J60_ETXSTL (0x0004) // TX Start Low Byte (ETXST<7:0>)
#define ENC28J60_ETXSTH (0x0005) // TX Start High Byte (ETXST<12:8>)
#define ENC28J60_ETXNDL (0x0006) // TX End Low Byte (ETXND<7:0>)
#define ENC28J60_ETXNDH (0x0007) // TX End High Byte (ETXND<12:8>)
#define ENC28J60_ERXSTL (0x0008) // RX Start Low Byte (ERXST<7:0>)
#define ENC28J60_ERXSTH (0x0009) // RX Start High Byte (ERXST<12:8>)
#define ENC28J60_ERXNDL (0x000A) // RX End Low Byte (ERXND<7:0>)
#define ENC28J60_ERXNDH (0x000B) // RX End High Byte (ERXND<12:8>)
#define ENC28J60_ERXRDPTL (0x000C) // RX RD Pointer Low Byte (ERXRDPT<7:0>)
#define ENC28J60_ERXRDPTH (0x000D) // RX RD Pointer High Byte (ERXRDPT<12:8>)
#define ENC28J60_ERXWRPTL (0x000E) // RX WR Pointer Low Byte (ERXWRPT<7:0>)
#define ENC28J60_ERXWRPTH (0x000F) // RX WR Pointer High Byte (ERXWRPT<12:8>)
#define ENC28J60_EDMASTL (0x0010) // DMA Start Low Byte (EDMAST<7:0>)
#define ENC28J60_EDMASTH (0x0011) // DMA Start High Byte (EDMAST<12:8>)
#define ENC28J60_EDMANDL (0x0012) // DMA End Low Byte (EDMAND<7:0>)
#define ENC28J60_EDMANDH (0x0013) // DMA End High Byte (EDMAND<12:8>)
#define ENC28J60_EDMADSTL (0x0014) // DMA Destination Low Byte (EDMADST<7:0>)
#define ENC28J60_EDMADSTH (0x0015) // DMA Destination High Byte (EDMADST<12:8>)
#define ENC28J60_EDMACSL (0x0016) // DMA Checksum Low Byte (EDMACS<7:0>)
#define ENC28J60_EDMACSH (0x0017) // DMA Checksum High Byte (EDMACS<15:8>)
// Bank 1 Registers
#define ENC28J60_EHT0 (0x0100) // Hash Table Byte 0 (EHT<7:0>)
#define ENC28J60_EHT1 (0x0101) // Hash Table Byte 1 (EHT<15:8>)
#define ENC28J60_EHT2 (0x0102) // Hash Table Byte 2 (EHT<23:16>)
#define ENC28J60_EHT3 (0x0103) // Hash Table Byte 3 (EHT<31:24>)
#define ENC28J60_EHT4 (0x0104) // Hash Table Byte 4 (EHT<39:32>)
#define ENC28J60_EHT5 (0x0105) // Hash Table Byte 5 (EHT<47:40>)
#define ENC28J60_EHT6 (0x0106) // Hash Table Byte 6 (EHT<55:48>)
#define ENC28J60_EHT7 (0x0107) // Hash Table Byte 7 (EHT<63:56>)
#define ENC28J60_EPMM0 (0x0108) // Pattern Match Mask Byte 0 (EPMM<7:0>)
#define ENC28J60_EPMM1 (0x0109) // Pattern Match Mask Byte 1 (EPMM<15:8>)
#define ENC28J60_EPMM2 (0x010A) // Pattern Match Mask Byte 2 (EPMM<23:16>)
#define ENC28J60_EPMM3 (0x010B) // Pattern Match Mask Byte 3 (EPMM<31:24>)
#define ENC28J60_EPMM4 (0x010C) // Pattern Match Mask Byte 4 (EPMM<39:32>)
#define ENC28J60_EPMM5 (0x010D) // Pattern Match Mask Byte 5 (EPMM<47:40>)
#define ENC28J60_EPMM6 (0x010E) // Pattern Match Mask Byte 6 (EPMM<55:48>)
#define ENC28J60_EPMM7 (0x010F) // Pattern Match Mask Byte 7 (EPMM<63:56>)
#define ENC28J60_EPMCSL (0x0110) // Pattern Match Checksum Low Byte (EPMCS<7:0>)
#define ENC28J60_EPMCSH (0x0111) // Pattern Match Checksum High Byte (EPMCS<15:0>)
#define ENC28J60_EPMOL (0x0114) // Pattern Match Offset Low Byte (EPMO<7:0>)
#define ENC28J60_EPMOH (0x0115) // Pattern Match Offset High Byte (EPMO<12:8>)
#define ENC28J60_ERXFCON (0x0118) // Receive Fileter Control
#define ENC28J60_EPKTCNT (0x0119) // Ethernet Packet Count
// Bank 2 Register
#define ENC28J60_MACON1 (0x1200) // MAC Control Register 1
#define ENC28J60_MACON2 (0x1201) // MAC Control Register 2
#define ENC28J60_MACON3 (0x1202) // MAC Control Register 3
#define ENC28J60_MACON4 (0x1203) // MAC Control Register 4
#define ENC28J60_MABBIPG (0x1204) // Back-to-Back Inter-Packet Gap (BBIPG<6:0>)
#define ENC28J60_MAIPGL (0x1206) // Non-Back-to-Back Inter-Packet Gap Low Byte (MAIPGL<6:0>)
#define ENC28J60_MAIPGH (0x1207) // Non-Back-to-Back Inter-Packet Gap High Byte (MAIPGH<6:0>)
#define ENC28J60_MACLCON1 (0x1208) // Retransmission Maximum (RETMAX<3:0>)
#define ENC28J60_MACLCON2 (0x1209) // Collision Window (COLWIN<5:0>)
#define ENC28J60_MAMXFLL (0x120A) // Maximum Frame Length Low Byte (MAMXFL<7:0>)
#define ENC28J60_MAMXFLH (0x120B) // Maximum Frame Length High Byte (MAMXFL<15:8>)
#define ENC28J60_MICMD (0x1212) // MII Command Register
#define ENC28J60_MIREGADR (0x1214) // MII Register Address (MIREGADR<4:0>)
#define ENC28J60_MIWRL (0x1216) // MII Write Data Low Byte (MIWR<7:0>)
#define ENC28J60_MIWRH (0x1217) // MII Write Data High Byte (MIWR<15:8>)
#define ENC28J60_MIRDL (0x1218) // MII Read Data Low Byte (MIRD<7:0>)
#define ENC28J60_MIRDH (0x1219) // MII Read Data High Byte(MIRD<15:8>)
// Bank 3 Registers
#define ENC28J60_MAADR5 (0x1300) // MAC Address Byte 5 (MAADR<15:8>)
#define ENC28J60_MAADR6 (0x1301) // MAC Address Byte 6 (MAADR<7:0>)
#define ENC28J60_MAADR3 (0x1302) // MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3
#define ENC28J60_MAADR4 (0x1303) // MAC Address Byte 4 (MAADR<23:16>)
#define ENC28J60_MAADR1 (0x1304) // MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1
#define ENC28J60_MAADR2 (0x1305) // MAC Address Byte 2 (MAADR<39:32>), OUI Byte 2
#define ENC28J60_EBSTSD (0x0306) // Built-in Self-Test Fill Seed (EBSTSD<7:0>)
#define ENC28J60_EBSTCON (0x0307) // Built-in Self-Test Control
#define ENC28J60_EBSTCSL (0x0308) // Built-in Self-Test Checksum Low Byte (EBSTCS<7:0>)
#define ENC28J60_EBSTCSH (0x0309) // Built-in Self-Test Checksum High Byte (EBSTCS<15:8>)
#define ENC28J60_MISTAT (0x130A) // MII Status Register
#define ENC28J60_EREVID (0x0312) // Ethernet Revision ID (EREVID<4:0>)
#define ENC28J60_ECOCON (0x0315) // Clock Output Control Register
#define ENC28J60_EFLOCON (0x0317) // Ethernet Flow Control
#define ENC28J60_EPAUSL (0x0318) // Pause Timer Value Low Byte (EPAUS<7:0>)
#define ENC28J60_EPAUSH (0x0319) // Pause Timer Value High Byte (EPAUS<15:8>)
/**
* @brief status and flag of ENC28J60 specific registers
*
*/
// EIE bit definitions
#define EIE_INTIE (1<<7) // Global INT Interrupt Enable
#define EIE_PKTIE (1<<6) // Receive Packet Pending Interrupt Enable
#define EIE_DMAIE (1<<5) // DMA Interrupt Enable
#define EIE_LINKIE (1<<4) // Link Status Change Interrupt Enable
#define EIE_TXIE (1<<3) // Transmit Enable
#define EIE_TXERIE (1<<1) // Transmit Error Interrupt Enable
#define EIE_RXERIE (1<<0) // Receive Error Interrupt Enable
// EIR bit definitions
#define EIR_PKTIF (1<<6) // Receive Packet Pending Interrupt Flag
#define EIR_DMAIF (1<<5) // DMA Interrupt Flag
#define EIR_LINKIF (1<<4) // Link Change Interrupt Flag
#define EIR_TXIF (1<<3) // Transmit Interrupt Flag
#define EIR_TXERIF (1<<1) // Transmit Error Interrupt Flag
#define EIR_RXERIF (1<<0) // Receive Error Interrupt Flag
// ESTAT bit definitions
#define ESTAT_INT (1<<7) // INT Interrupt Flag
#define ESTAT_BUFER (1<<6) // Buffer Error Status
#define ESTAT_LATECOL (1<<4) // Late Collision Error
#define ESTAT_RXBUSY (1<<2) // Receive Busy
#define ESTAT_TXABRT (1<<1) // Transmit Abort Error
#define ESTAT_CLKRDY (1<<0) // Clock Ready
// ECON2 bit definitions
#define ECON2_AUTOINC (1<<7) // Automatic Buffer Pointer Increment Enable
#define ECON2_PKTDEC (1<<6) // Packet Decrement
#define ECON2_PWRSV (1<<5) // Power Save Enable
#define ECON2_VRPS (1<<3) // Voltage Regulator Power Save Enable
// ECON1 bit definitions
#define ECON1_TXRST (1<<7) // Transmit Logic Reset
#define ECON1_RXRST (1<<6) // Receive Logic Reset
#define ECON1_DMAST (1<<5) // DMA Start and Busy Status
#define ECON1_CSUMEN (1<<4) // DMA Checksum Enable
#define ECON1_TXRTS (1<<3) // Transmit Request to Send
#define ECON1_RXEN (1<<2) // Receive Enable
#define ECON1_BSEL1 (1<<1) // Bank Select1
#define ECON1_BSEL0 (1<<0) // Bank Select0
// ERXFCON bit definitions
#define ERXFCON_UCEN (1<<7) // Unicast Filter Enable
#define ERXFCON_ANDOR (1<<6) // AND/OR Filter Select
#define ERXFCON_CRCEN (1<<5) // Post-Filter CRC Check Enable
#define ERXFCON_PMEN (1<<4) // Pattern Match Filter Enable
#define ERXFCON_MPEN (1<<3) // Magic Packet Filter Enable
#define ERXFCON_HTEN (1<<2) // Hash Table Filter Enable
#define ERXFCON_MCEN (1<<1) // Multicast Filter Enable
#define ERXFCON_BCEN (1<<0) // Broadcast Filter Enable
// MACON1 bit definitions
#define MACON1_TXPAUS (1<<3) // Pause Control Frame Transmission Enable
#define MACON1_RXPAUS (1<<2) // Pause Control Frame Reception Enable
#define MACON1_PASSALL (1<<1) // Pass All Received Frames Enable
#define MACON1_MARXEN (1<<0) // MAC Receive Enable
// MACON3 bit definitions
#define MACON3_PADCFG2 (1<<7) // Automatic Pad and CRC Configuration bit 2
#define MACON3_PADCFG1 (1<<6) // Automatic Pad and CRC Configuration bit 1
#define MACON3_PADCFG0 (1<<5) // Automatic Pad and CRC Configuration bit 0
#define MACON3_TXCRCEN (1<<4) // Transmit CRC Enable
#define MACON3_PHDRLEN (1<<3) // Proprietary Header Enable
#define MACON3_HFRMLEN (1<<2) // Huge Frame Enable
#define MACON3_FRMLNEN (1<<1) // Frame Length Checking Enable
#define MACON3_FULDPX (1<<0) // MAC Full-Duplex Enable
// MACON4 bit definitions
#define MACON4_DEFER (1<<6) // Defer Transmission Enable
#define MACON4_BPEN (1<<5) // No Backoff During Backpressure Enable
#define MACON4_NOBKFF (1<<4) // No Backoff Enable
// MICMD bit definitions
#define MICMD_MIISCAN (1<<1) // MII Scan Enable
#define MICMD_MIIRD (1<<0) // MII Read Enable
// EBSTCON bit definitions
#define EBSTCON_PSV2 (1<<7) // Pattern Shift Value 2
#define EBSTCON_PSV1 (1<<6) // Pattern Shift Value 1
#define EBSTCON_PSV0 (1<<5) // Pattern Shift Value 0
#define EBSTCON_PSEL (1<<4) // Port Select
#define EBSTCON_TMSEL1 (1<<3) // Test Mode Select 1
#define EBSTCON_TMSEL0 (1<<2) // Test Mode Select 0
#define EBSTCON_TME (1<<1) // Test Mode Enable
#define EBSTCON_BISTST (1<<0) // Built-in Self-Test Start/Busy
// MISTAT bit definitions
#define MISTAT_NVALID (1<<2) // MII Management Read Data Not Valid
#define MISTAT_SCAN (1<<1) // MII Management Scan Operation in Progress
#define MISTAT_BUSY (1<<0) // MII Management Busy
// EFLOCON bit definitions
#define EFLOCON_FULDPXS (1<<2) // Full-Duplex Shadown
#define EFLOCON_FCEN1 (1<<1) // Flow Control Enable 1
#define EFLOCON_FCEN0 (1<<0) // Flow Control Enable 0
/**
* @brief ENC28J60 specific configuration
*
*/
typedef struct {
spi_device_handle_t spi_hdl; /*!< Handle of SPI device driver */
int int_gpio_num; /*!< Interrupt GPIO number */
} eth_enc28j60_config_t;
/**
* @brief Default ENC28J60 specific configuration
*
*/
#define ETH_ENC28J60_DEFAULT_CONFIG(spi_device) \
{ \
.spi_hdl = spi_device, \
.int_gpio_num = 4, \
}
/**
* @brief Create ENC28J60 Ethernet MAC instance
*
* @param[in] enc28j60_config: ENC28J60 specific configuration
* @param[in] mac_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_enc28j60(const eth_enc28j60_config_t *enc28j60_config, const eth_mac_config_t *mac_config);
/**
* @brief Create a PHY instance of ENC28J60
*
* @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_enc28j60(const eth_phy_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,130 @@
/* ENC28J60 Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "enc28j60.h"
#include "sdkconfig.h"
static const char *TAG = "eth_example";
/** 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");
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
}
void app_main(void)
{
ESP_ERROR_CHECK(gpio_install_isr_service(0));
// Initialize TCP/IP network interface (should be called only once in application)
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
// Set default handlers to process TCP/IP stuffs
ESP_ERROR_CHECK(esp_eth_set_default_handlers(eth_netif));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ENC28J60_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ENC28J60_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ENC28J60_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ENC28J60_SPI_HOST, &buscfg, 1));
/* ENC28J60 ethernet driver is based on spi driver */
spi_device_interface_config_t devcfg = {
.command_bits = 3,
.address_bits = 5,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ENC28J60_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ENC28J60_CS_GPIO,
.queue_size = 20
};
spi_device_handle_t spi_handle = NULL;
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ENC28J60_SPI_HOST, &devcfg, &spi_handle));
eth_enc28j60_config_t enc28j60_config = ETH_ENC28J60_DEFAULT_CONFIG(spi_handle);
enc28j60_config.int_gpio_num = CONFIG_EXAMPLE_ENC28J60_INT_GPIO;
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.smi_mdc_gpio_num = -1; // ENC28J60 doesn't have SMI interface
mac_config.smi_mdio_gpio_num = -1;
esp_eth_mac_t *mac = esp_eth_mac_new_enc28j60(&enc28j60_config, &mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.autonego_timeout_ms = 0; // ENC28J60 doesn't support auto-negotiation
phy_config.reset_gpio_num = -1; // ENC28J60 doesn't have a pin to reset internal PHY
esp_eth_phy_t *phy = esp_eth_phy_new_enc28j60(&phy_config);
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
ESP_ERROR_CHECK(esp_eth_driver_install(&eth_config, &eth_handle));
/* ENC28J60 doesn't burn any factory MAC address, we need to set it manually.
02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control.
*/
mac->set_addr(mac, (uint8_t[]) {
0x02, 0x00, 0x00, 0x12, 0x34, 0x56
});
/* attach Ethernet driver to TCP/IP stack */
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
/* start Ethernet driver state machine */
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
}

View File

@@ -0,0 +1,943 @@
// 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 "esp_attr.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 "hal/cpu_hal.h"
#include "enc28j60.h"
#include "sdkconfig.h"
static const char *TAG = "enc28j60";
#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 ENC28J60_SPI_LOCK_TIMEOUT_MS (50)
#define ENC28J60_PHY_OPERATION_TIMEOUT_US (1000)
#define ENC28J60_SYSTEM_RESET_ADDITION_TIME_US (1000)
#define ENC28J60_BUFFER_SIZE (0x2000) // 8KB built-in buffer
/**
* ______
* |__TX__| TX: 2 KB : [0x1800, 0x2000)
* | |
* | RX | RX: 6 KB : [0x0000, 0x1800)
* |______|
*
*/
#define ENC28J60_BUF_RX_START (0)
#define ENC28J60_BUF_RX_END (ENC28J60_BUF_TX_START - 1)
#define ENC28J60_BUF_TX_START ((ENC28J60_BUFFER_SIZE / 4) * 3)
#define ENC28J60_BUF_TX_END (ENC28J60_BUFFER_SIZE - 1)
#define ENC28J60_RSV_SIZE (6) // Receive Status Vector Size
typedef struct {
uint8_t next_packet_low;
uint8_t next_packet_high;
uint8_t length_low;
uint8_t length_high;
uint8_t status_low;
uint8_t status_high;
} enc28j60_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;
uint32_t next_packet_ptr;
int int_gpio_num;
uint8_t addr[6];
uint8_t last_bank;
bool packets_remain;
} emac_enc28j60_t;
static inline bool enc28j60_lock(emac_enc28j60_t *emac)
{
return xSemaphoreTake(emac->spi_lock, pdMS_TO_TICKS(ENC28J60_SPI_LOCK_TIMEOUT_MS)) == pdTRUE;
}
static inline bool enc28j60_unlock(emac_enc28j60_t *emac)
{
return xSemaphoreGive(emac->spi_lock) == pdTRUE;
}
/**
* @brief ERXRDPT need to be set always at odd addresses
*/
static inline uint32_t enc28j60_next_ptr_align_odd(uint32_t next_packet_ptr, uint32_t start, uint32_t end)
{
uint32_t erxrdpt;
if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end)) {
erxrdpt = end;
} else {
erxrdpt = next_packet_ptr - 1;
}
return erxrdpt;
}
/**
* @brief Calculate wrap around when reading beyond the end of the RX buffer
*/
static inline uint32_t enc28j60_rx_packet_start(uint32_t start_addr, uint32_t off)
{
if (start_addr + off > ENC28J60_BUF_RX_END) {
return (start_addr + off) - (ENC28J60_BUF_RX_END - ENC28J60_BUF_RX_START + 1);
} else {
return start_addr + off;
}
}
/**
* @brief SPI operation wrapper for writing ENC28J60 internal register
*/
static esp_err_t enc28j60_do_register_write(emac_enc28j60_t *emac, uint8_t reg_addr, uint8_t value)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_WCR, // Write control register
.addr = reg_addr,
.length = 8,
.flags = SPI_TRANS_USE_TXDATA,
.tx_data = {
[0] = value
}
};
if (enc28j60_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;
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief SPI operation wrapper for reading ENC28J60 internal register
*/
static esp_err_t enc28j60_do_register_read(emac_enc28j60_t *emac, bool is_eth_reg, uint8_t reg_addr, uint8_t *value)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_RCR, // Read control register
.addr = reg_addr,
.length = is_eth_reg ? 8 : 16, // read operation is different for ETH register and non-ETH register
.flags = SPI_TRANS_USE_RXDATA
};
if (enc28j60_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 = is_eth_reg ? trans.rx_data[0] : trans.rx_data[1];
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief SPI operation wrapper for bitwise setting ENC28J60 internal register
* @note can only be used for ETH registers
*/
static esp_err_t enc28j60_do_bitwise_set(emac_enc28j60_t *emac, uint8_t reg_addr, uint8_t mask)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_BFS, // Bit field set
.addr = reg_addr,
.length = 8,
.flags = SPI_TRANS_USE_TXDATA,
.tx_data = {
[0] = mask
}
};
if (enc28j60_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;
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief SPI operation wrapper for bitwise clearing ENC28J60 internal register
* @note can only be used for ETH registers
*/
static esp_err_t enc28j60_do_bitwise_clr(emac_enc28j60_t *emac, uint8_t reg_addr, uint8_t mask)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_BFC, // Bit field clear
.addr = reg_addr,
.length = 8,
.flags = SPI_TRANS_USE_TXDATA,
.tx_data = {
[0] = mask
}
};
if (enc28j60_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;
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief SPI operation wrapper for writing ENC28J60 internal memory
*/
static esp_err_t enc28j60_do_memory_write(emac_enc28j60_t *emac, uint8_t *buffer, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_WBM, // Write buffer memory
.addr = 0x1A,
.length = len * 8,
.tx_buffer = buffer
};
if (enc28j60_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;
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief SPI operation wrapper for reading ENC28J60 internal memory
*/
static esp_err_t enc28j60_do_memory_read(emac_enc28j60_t *emac, uint8_t *buffer, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_RBM, // Read buffer memory
.addr = 0x1A,
.length = len * 8,
.rx_buffer = buffer
};
if (enc28j60_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;
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief SPI operation wrapper for resetting ENC28J60
*/
static esp_err_t enc28j60_do_reset(emac_enc28j60_t *emac)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = ENC28J60_SPI_CMD_SRC, // Soft reset
.addr = 0x1F,
};
if (enc28j60_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;
}
enc28j60_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
// After reset, wait at least 1ms for the device to be ready
ets_delay_us(ENC28J60_SYSTEM_RESET_ADDITION_TIME_US);
return ret;
}
/**
* @brief Switch ENC28J60 register bank
*/
static esp_err_t enc28j60_switch_register_bank(emac_enc28j60_t *emac, uint8_t bank)
{
esp_err_t ret = ESP_OK;
if (bank != emac->last_bank) {
MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_ECON1, 0x03) == ESP_OK,
"clear ECON1[1:0] failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, bank & 0x03) == ESP_OK,
"set ECON1[1:0] failed", out, ESP_FAIL);
emac->last_bank = bank;
}
out:
return ret;
}
/**
* @brief Write ENC28J60 register
*/
static esp_err_t enc28j60_register_write(emac_enc28j60_t *emac, uint16_t reg_addr, uint8_t value)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(enc28j60_switch_register_bank(emac, (reg_addr & 0xF00) >> 8) == ESP_OK,
"switch bank failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_do_register_write(emac, reg_addr & 0xFF, value) == ESP_OK,
"write register failed", out, ESP_FAIL);
out:
return ret;
}
/**
* @brief Read ENC28J60 register
*/
static esp_err_t enc28j60_register_read(emac_enc28j60_t *emac, uint16_t reg_addr, uint8_t *value)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(enc28j60_switch_register_bank(emac, (reg_addr & 0xF00) >> 8) == ESP_OK,
"switch bank failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_do_register_read(emac, !(reg_addr & 0xF000), reg_addr & 0xFF, value) == ESP_OK,
"read register failed", out, ESP_FAIL);
out:
return ret;
}
/**
* @brief Read ENC28J60 internal memroy
*/
static esp_err_t enc28j60_read_packet(emac_enc28j60_t *emac, uint32_t addr, uint8_t *packet, uint32_t len)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTL, addr & 0xFF) == ESP_OK,
"write ERDPTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTH, (addr & 0xFF00) >> 8) == ESP_OK,
"write ERDPTH failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_do_memory_read(emac, packet, len) == ESP_OK,
"read memory failed", out, ESP_FAIL);
out:
return ret;
}
/**
* @brief Write ENC28J60 internal PHY register
*/
static esp_err_t emac_enc28j60_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_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
uint8_t mii_status;
/* check if phy access is in progress */
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK,
"read MISTAT failed", out, ESP_FAIL);
MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_INVALID_STATE);
/* tell the PHY address to write */
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIREGADR, phy_reg & 0xFF) == ESP_OK,
"write MIREGADR failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIWRL, reg_value & 0xFF) == ESP_OK,
"write MIWRL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIWRH, (reg_value & 0xFF00) >> 8) == ESP_OK,
"write MIWRH failed", out, ESP_FAIL);
/* polling the busy flag */
uint32_t to = 0;
do {
ets_delay_us(100);
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK,
"read MISTAT failed", out, ESP_FAIL);
to += 100;
} while ((mii_status & MISTAT_BUSY) && to < ENC28J60_PHY_OPERATION_TIMEOUT_US);
MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_TIMEOUT);
out:
return ret;
}
/**
* @brief Read ENC28J60 internal PHY register
*/
static esp_err_t emac_enc28j60_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", out, ESP_ERR_INVALID_ARG);
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
uint8_t mii_status;
uint8_t mii_cmd;
/* check if phy access is in progress */
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK,
"read MISTAT failed", out, ESP_FAIL);
MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_INVALID_STATE);
/* tell the PHY address to read */
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIREGADR, phy_reg & 0xFF) == ESP_OK,
"write MIREGADR failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MICMD, &mii_cmd) == ESP_OK,
"read MICMD failed", out, ESP_FAIL);
mii_cmd |= MICMD_MIIRD;
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MICMD, mii_cmd) == ESP_OK,
"write MICMD failed", out, ESP_FAIL);
/* polling the busy flag */
uint32_t to = 0;
do {
ets_delay_us(100);
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK,
"read MISTAT failed", out, ESP_FAIL);
to += 100;
} while ((mii_status & MISTAT_BUSY) && to < ENC28J60_PHY_OPERATION_TIMEOUT_US);
MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_TIMEOUT);
mii_cmd &= (~MICMD_MIIRD);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MICMD, mii_cmd) == ESP_OK,
"write MICMD failed", out, ESP_FAIL);
uint8_t value_l = 0;
uint8_t value_h = 0;
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MIRDL, &value_l) == ESP_OK,
"read MIRDL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MIRDH, &value_h) == ESP_OK,
"read MIRDH failed", out, ESP_FAIL);
*reg_value = (value_h << 8) | value_l;
out:
return ret;
}
/**
* @brief Set mediator for Ethernet MAC
*/
static esp_err_t emac_enc28j60_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", out, ESP_ERR_INVALID_ARG);
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
emac->eth = eth;
out:
return ret;
}
/**
* @brief Verify chip ID
*/
static esp_err_t enc28j60_verify_id(emac_enc28j60_t *emac)
{
esp_err_t ret = ESP_OK;
uint8_t id;
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_EREVID, &id) == ESP_OK,
"read EREVID failed", out, ESP_FAIL);
ESP_LOGI(TAG, "revision: %d", id);
MAC_CHECK(id > 0 && id < 7, "wrong chip ID", out, ESP_ERR_INVALID_VERSION);
out:
return ret;
}
/**
* @brief Write mac address to internal registers
*/
static esp_err_t enc28j60_set_mac_addr(emac_enc28j60_t *emac)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR6, emac->addr[5]) == ESP_OK,
"write MAADR6 failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR5, emac->addr[4]) == ESP_OK,
"write MAADR5 failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR4, emac->addr[3]) == ESP_OK,
"write MAADR4 failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR3, emac->addr[2]) == ESP_OK,
"write MAADR3 failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR2, emac->addr[1]) == ESP_OK,
"write MAADR2 failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR1, emac->addr[0]) == ESP_OK,
"write MAADR1 failed", out, ESP_FAIL);
out:
return ret;
}
/**
* @brief Clear multicast hash table
*/
static esp_err_t enc28j60_clear_multicast_table(emac_enc28j60_t *emac)
{
esp_err_t ret = ESP_OK;
for (int i = 0; i < 7; i++) {
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_EHT0 + i, 0x00) == ESP_OK,
"write ENC28J60_EHT%d failed", out, ESP_FAIL, i);
}
out:
return ret;
}
/**
* @brief Default setup for ENC28J60 internal registers
*/
static esp_err_t enc28j60_setup_default(emac_enc28j60_t *emac)
{
esp_err_t ret = ESP_OK;
// set up receive buffer start + end
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXSTL, ENC28J60_BUF_RX_START & 0xFF) == ESP_OK,
"write ERXSTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXSTH, (ENC28J60_BUF_RX_START & 0xFF00) >> 8) == ESP_OK,
"write ERXSTH failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXNDL, ENC28J60_BUF_RX_END & 0xFF) == ESP_OK,
"write ERXNDL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXNDH, (ENC28J60_BUF_RX_END & 0xFF00) >> 8) == ESP_OK,
"write ERXNDH failed", out, ESP_FAIL);
uint32_t erxrdpt = enc28j60_next_ptr_align_odd(ENC28J60_BUF_RX_START, ENC28J60_BUF_RX_START, ENC28J60_BUF_RX_END);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTL, erxrdpt & 0xFF) == ESP_OK,
"write ERXRDPTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTH, (erxrdpt & 0xFF00) >> 8) == ESP_OK,
"write ERXRDPTH failed", out, ESP_FAIL);
// set up transmit buffer start + end
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXSTL, ENC28J60_BUF_TX_START & 0xFF) == ESP_OK,
"write ETXSTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXSTH, (ENC28J60_BUF_TX_START & 0xFF00) >> 8) == ESP_OK,
"write ETXSTH failed", out, ESP_FAIL);
// set up default filter mode: (unicast OR broadcast) AND crc valid
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN) == ESP_OK,
"write ERXFCON failed", out, ESP_FAIL);
// enable MAC receive, enable pause control frame on Tx and Rx path
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON1, MACON1_MARXEN | MACON1_RXPAUS | MACON1_TXPAUS) == ESP_OK,
"write MACON1 failed", out, ESP_FAIL);
// enable automatic padding, append CRC, check frame length, half duplex by default (can update at runtime)
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN) == ESP_OK, "write MACON3 failed", out, ESP_FAIL);
// enable defer transmission (effective only in half duplex)
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON4, MACON4_DEFER) == ESP_OK,
"write MACON4 failed", out, ESP_FAIL);
// set inter-frame gap (back-to-back)
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MABBIPG, 0x12) == ESP_OK,
"write MABBIPG failed", out, ESP_FAIL);
// set inter-frame gap (non-back-to-back)
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAIPGL, 0x12) == ESP_OK,
"write MAIPGL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAIPGH, 0x0C) == ESP_OK,
"write MAIPGH failed", out, ESP_FAIL);
out:
return ret;
}
/**
* @brief Start enc28j60: enable interrupt and start receive
*/
static esp_err_t emac_enc28j60_start(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
/* enable interrupt */
MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_EIR, 0xFF) == ESP_OK,
"clear EIR failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_EIE, EIE_PKTIE | EIE_INTIE) == ESP_OK,
"set EIE.[PKTIE|INTIE] failed", out, ESP_FAIL);
/* enable rx logic */
MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, ECON1_RXEN) == ESP_OK,
"set ECON1.RXEN failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTL, 0x00) == ESP_OK,
"write ERDPTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTH, 0x00) == ESP_OK,
"write ERDPTH failed", out, ESP_FAIL);
out:
return ret;
}
/**
* @brief Stop enc28j60: disable interrupt and stop receiving packets
*/
static esp_err_t emac_enc28j60_stop(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
/* disable interrupt */
MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_EIE, 0xFF) == ESP_OK,
"clear EIE failed", out, ESP_FAIL);
/* disable rx */
MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_ECON1, ECON1_RXEN) == ESP_OK,
"clear ECON1.RXEN failed", out, ESP_FAIL);
out:
return ret;
}
static esp_err_t emac_enc28j60_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", out, ESP_ERR_INVALID_ARG);
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
memcpy(emac->addr, addr, 6);
MAC_CHECK(enc28j60_set_mac_addr(emac) == ESP_OK, "set mac address failed", out, ESP_FAIL);
out:
return ret;
}
static esp_err_t emac_enc28j60_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", out, ESP_ERR_INVALID_ARG);
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
memcpy(addr, emac->addr, 6);
out:
return ret;
}
static void enc28j60_isr_handler(void *arg)
{
emac_enc28j60_t *emac = (emac_enc28j60_t *)arg;
BaseType_t high_task_wakeup = pdFALSE;
/* notify enc28j60 task */
vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup);
if (high_task_wakeup != pdFALSE) {
portYIELD_FROM_ISR();
}
}
static void emac_enc28j60_task(void *arg)
{
emac_enc28j60_t *emac = (emac_enc28j60_t *)arg;
uint8_t status = 0;
uint8_t *buffer = NULL;
uint32_t length = 0;
while (1) {
// block indefinitely until some task notifies me
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
/* clear interrupt status */
enc28j60_do_register_read(emac, true, ENC28J60_EIR, &status);
/* packet received */
if (status & EIR_PKTIF) {
do {
length = ETH_MAX_PACKET_SIZE;
buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
if (!buffer) {
ESP_LOGE(TAG, "no mem for receive buffer");
} else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
/* pass the buffer to stack (e.g. TCP/IP layer) */
if (length) {
emac->eth->stack_input(emac->eth, buffer, length);
} else {
free(buffer);
}
} else {
free(buffer);
}
} while (emac->packets_remain);
}
}
vTaskDelete(NULL);
}
static esp_err_t emac_enc28j60_set_link(esp_eth_mac_t *mac, eth_link_t link)
{
esp_err_t ret = ESP_OK;
switch (link) {
case ETH_LINK_UP:
MAC_CHECK(mac->start(mac) == ESP_OK, "enc28j60 start failed", out, ESP_FAIL);
break;
case ETH_LINK_DOWN:
MAC_CHECK(mac->stop(mac) == ESP_OK, "enc28j60 stop failed", out, ESP_FAIL);
break;
default:
MAC_CHECK(false, "unknown link status", out, ESP_ERR_INVALID_ARG);
break;
}
out:
return ret;
}
static esp_err_t emac_enc28j60_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
{
esp_err_t ret = ESP_OK;
switch (speed) {
case ETH_SPEED_10M:
ESP_LOGI(TAG, "working in 10Mbps");
break;
default:
MAC_CHECK(false, "100Mbps unsupported", out, ESP_ERR_NOT_SUPPORTED);
break;
}
out:
return ret;
}
static esp_err_t emac_enc28j60_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
uint8_t mac3 = 0;
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MACON3, &mac3) == ESP_OK,
"read MACON3 failed", out, ESP_FAIL);
switch (duplex) {
case ETH_DUPLEX_HALF:
mac3 &= ~MACON3_FULDPX;
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MABBIPG, 0x12) == ESP_OK,
"write MABBIPG failed", out, ESP_FAIL);
ESP_LOGI(TAG, "working in half duplex");
break;
case ETH_DUPLEX_FULL:
mac3 |= MACON3_FULDPX;
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MABBIPG, 0x15) == ESP_OK,
"write MABBIPG failed", out, ESP_FAIL);
ESP_LOGI(TAG, "working in full duplex");
break;
default:
MAC_CHECK(false, "unknown duplex", out, ESP_ERR_INVALID_ARG);
break;
}
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON3, mac3) == ESP_OK,
"write MACON3 failed", out, ESP_FAIL);
out:
return ret;
}
static esp_err_t emac_enc28j60_set_promiscuous(esp_eth_mac_t *mac, bool enable)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
if (enable) {
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXFCON, 0x00) == ESP_OK,
"write ERXFCON failed", out, ESP_FAIL);
}
out:
return ret;
}
static esp_err_t emac_enc28j60_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
uint8_t econ1 = 0;
/* Check if last transmit complete */
MAC_CHECK(enc28j60_do_register_read(emac, true, ENC28J60_ECON1, &econ1) == ESP_OK,
"read ECON1 failed", out, ESP_FAIL);
MAC_CHECK(!(econ1 & ECON1_TXRTS), "last transmit still in progress", out, ESP_ERR_INVALID_STATE);
/* Set the write pointer to start of transmit buffer area */
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_EWRPTL, ENC28J60_BUF_TX_START & 0xFF) == ESP_OK,
"write EWRPTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_EWRPTH, (ENC28J60_BUF_TX_START & 0xFF00) >> 8) == ESP_OK,
"write EWRPTH failed", out, ESP_FAIL);
/* Set the end pointer to correspond to the packet size given */
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXNDL, (ENC28J60_BUF_TX_START + length) & 0xFF) == ESP_OK,
"write ETXNDL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXNDH, ((ENC28J60_BUF_TX_START + length) & 0xFF00) >> 8) == ESP_OK,
"write ETXNDH failed", out, ESP_FAIL);
/* copy data to tx memory */
uint8_t per_pkt_control = 0; // MACON3 will be used to determine how the packet will be transmitted
MAC_CHECK(enc28j60_do_memory_write(emac, &per_pkt_control, 1) == ESP_OK,
"write packet control byte failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_do_memory_write(emac, buf, length) == ESP_OK,
"buffer memory write failed", out, ESP_FAIL);
/* issue tx polling command */
MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, ECON1_TXRTS) == ESP_OK,
"set ECON1.TXRTS failed", out, ESP_FAIL);
out:
return ret;
}
static esp_err_t emac_enc28j60_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
uint8_t pk_counter = 0;
uint16_t rx_len = 0;
uint32_t next_packet_addr = 0;
__attribute__((aligned(4))) enc28j60_rx_header_t header; // SPI driver needs the rx buffer 4 byte align
// read packet header
MAC_CHECK(enc28j60_read_packet(emac, emac->next_packet_ptr, (uint8_t *)&header, sizeof(header)) == ESP_OK,
"read header failed", out, ESP_FAIL);
// get packets' length, address
rx_len = header.length_low + (header.length_high << 8);
next_packet_addr = header.next_packet_low + (header.next_packet_high << 8);
// read packet content
MAC_CHECK(enc28j60_read_packet(emac, enc28j60_rx_packet_start(emac->next_packet_ptr, ENC28J60_RSV_SIZE), buf, rx_len) == ESP_OK,
"read packet content failed", out, ESP_FAIL);
// free receive buffer space
uint32_t erxrdpt = enc28j60_next_ptr_align_odd(next_packet_addr, ENC28J60_BUF_RX_START, ENC28J60_BUF_RX_END);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTL, (erxrdpt & 0xFF)) == ESP_OK,
"write ERXRDPTL failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTH, (erxrdpt & 0xFF00) >> 8) == ESP_OK,
"write ERXRDPTH failed", out, ESP_FAIL);
emac->next_packet_ptr = next_packet_addr;
MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON2, ECON2_PKTDEC) == ESP_OK,
"set ECON2.PKTDEC failed", out, ESP_FAIL);
MAC_CHECK(enc28j60_register_read(emac, ENC28J60_EPKTCNT, &pk_counter) == ESP_OK,
"read EPKTCNT failed", out, ESP_FAIL);
*length = rx_len - 4; // substract the CRC length
emac->packets_remain = pk_counter > 0;
out:
return ret;
}
static esp_err_t emac_enc28j60_init(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
esp_eth_mediator_t *eth = emac->eth;
/* init gpio used for reporting enc28j60 interrupt */
gpio_pad_select_gpio(emac->int_gpio_num);
gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT);
gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY);
gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE);
gpio_intr_enable(emac->int_gpio_num);
gpio_isr_handler_add(emac->int_gpio_num, enc28j60_isr_handler, emac);
MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK,
"lowlevel init failed", out, ESP_FAIL);
/* reset enc28j60 */
MAC_CHECK(enc28j60_do_reset(emac) == ESP_OK, "reset enc28j60 failed", out, ESP_FAIL);
/* verify chip id */
MAC_CHECK(enc28j60_verify_id(emac) == ESP_OK, "vefiry chip ID failed", out, ESP_FAIL);
/* default setup of internal registers */
MAC_CHECK(enc28j60_setup_default(emac) == ESP_OK, "enc28j60 default setup failed", out, ESP_FAIL);
/* clear multicast hash table */
MAC_CHECK(enc28j60_clear_multicast_table(emac) == ESP_OK, "clear multicast table failed", out, ESP_FAIL);
return ESP_OK;
out:
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ret;
}
static esp_err_t emac_enc28j60_deinit(esp_eth_mac_t *mac)
{
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
esp_eth_mediator_t *eth = emac->eth;
mac->stop(mac);
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ESP_OK;
}
static esp_err_t emac_enc28j60_del(esp_eth_mac_t *mac)
{
emac_enc28j60_t *emac = __containerof(mac, emac_enc28j60_t, parent);
vTaskDelete(emac->rx_task_hdl);
vSemaphoreDelete(emac->spi_lock);
free(emac);
return ESP_OK;
}
esp_eth_mac_t *esp_eth_mac_new_enc28j60(const eth_enc28j60_config_t *enc28j60_config, const eth_mac_config_t *mac_config)
{
esp_eth_mac_t *ret = NULL;
emac_enc28j60_t *emac = NULL;
MAC_CHECK(enc28j60_config, "can't set enc28j60 specific config to null", err, NULL);
MAC_CHECK(mac_config, "can't set mac config to null", err, NULL);
emac = calloc(1, sizeof(emac_enc28j60_t));
MAC_CHECK(emac, "calloc emac failed", err, NULL);
/* enc28j60 driver is interrupt driven */
MAC_CHECK(enc28j60_config->int_gpio_num >= 0, "error interrupt gpio number", err, NULL);
emac->last_bank = 0xFF;
emac->next_packet_ptr = ENC28J60_BUF_RX_START;
/* bind methods and attributes */
emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms;
emac->int_gpio_num = enc28j60_config->int_gpio_num;
emac->spi_hdl = enc28j60_config->spi_hdl;
emac->parent.set_mediator = emac_enc28j60_set_mediator;
emac->parent.init = emac_enc28j60_init;
emac->parent.deinit = emac_enc28j60_deinit;
emac->parent.start = emac_enc28j60_start;
emac->parent.stop = emac_enc28j60_stop;
emac->parent.del = emac_enc28j60_del;
emac->parent.write_phy_reg = emac_enc28j60_write_phy_reg;
emac->parent.read_phy_reg = emac_enc28j60_read_phy_reg;
emac->parent.set_addr = emac_enc28j60_set_addr;
emac->parent.get_addr = emac_enc28j60_get_addr;
emac->parent.set_speed = emac_enc28j60_set_speed;
emac->parent.set_duplex = emac_enc28j60_set_duplex;
emac->parent.set_link = emac_enc28j60_set_link;
emac->parent.set_promiscuous = emac_enc28j60_set_promiscuous;
emac->parent.transmit = emac_enc28j60_transmit;
emac->parent.receive = emac_enc28j60_receive;
/* create mutex */
emac->spi_lock = xSemaphoreCreateMutex();
MAC_CHECK(emac->spi_lock, "create lock failed", err, NULL);
/* create enc28j60 task */
BaseType_t core_num = tskNO_AFFINITY;
if (mac_config->flags & ETH_MAC_FLAG_PIN_TO_CORE) {
core_num = cpu_hal_get_core_id();
}
BaseType_t xReturned = xTaskCreatePinnedToCore(emac_enc28j60_task, "enc28j60_tsk", mac_config->rx_task_stack_size, emac,
mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
MAC_CHECK(xReturned == pdPASS, "create enc28j60 task failed", err, NULL);
return &(emac->parent);
err:
if (emac) {
if (emac->rx_task_hdl) {
vTaskDelete(emac->rx_task_hdl);
}
if (emac->spi_lock) {
vSemaphoreDelete(emac->spi_lock);
}
free(emac);
}
return ret;
}

View File

@@ -0,0 +1,304 @@
// 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"
#include "driver/gpio.h"
static const char *TAG = "enc28j60";
#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 PHCON2(PHY Control Register 2)
*
*/
typedef union {
struct {
uint32_t reserved_7_0 : 8; // Reserved
uint32_t hdldis : 1; // Half-Duplex Loopback Disable
uint32_t reserved_9: 1; // Reserved
uint32_t jabber: 1; // Disable Jabber Correction
uint32_t reserved_12_11: 2; // Reserved
uint32_t txdis: 1; // Disable Twist-Pair Transmitter
uint32_t frclnk: 1; // Force Linkup
uint32_t reserved_15: 1; //Reserved
};
uint32_t val;
} phcon2_reg_t;
#define ETH_PHY_PHCON2_REG_ADDR (0x10)
/**
* @brief PHSTAT2(PHY Status Register 2)
*
*/
typedef union {
struct {
uint32_t reserved_4_0 : 5; // Reserved
uint32_t plrity : 1; // Polarity Status
uint32_t reserved_8_6 : 3; // Reserved
uint32_t dpxstat : 1; // PHY Duplex Status
uint32_t lstat : 1; // PHY Link Status (non-latching)
uint32_t colstat : 1; // PHY Collision Status
uint32_t rxstat : 1; // PHY Receive Status
uint32_t txstat : 1; // PHY Transmit Status
uint32_t reserved_15_14 : 2; // Reserved
};
uint32_t val;
} phstat2_reg_t;
#define ETH_PHY_PHSTAT2_REG_ADDR (0x11)
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
uint32_t addr;
uint32_t reset_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_enc28j60_t;
static esp_err_t enc28j60_update_link_duplex_speed(phy_enc28j60_t *enc28j60)
{
esp_eth_mediator_t *eth = enc28j60->eth;
eth_speed_t speed = ETH_SPEED_10M; // enc28j60 speed is fixed to 10Mbps
eth_duplex_t duplex = ETH_DUPLEX_HALF;
phstat2_reg_t phstat;
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_PHSTAT2_REG_ADDR, &(phstat.val)) == ESP_OK,
"read PHSTAT2 failed", err);
eth_link_t link = phstat.lstat ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (enc28j60->link_status != link) {
/* when link up, read result */
if (link == ETH_LINK_UP) {
if (phstat.dpxstat) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
enc28j60->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t enc28j60_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator for enc28j60 to null", err);
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
enc28j60->eth = eth;
return ESP_OK;
err:
return ESP_ERR_INVALID_ARG;
}
static esp_err_t enc28j60_get_link(esp_eth_phy_t *phy)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
/* Updata information about link, speed, duplex */
PHY_CHECK(enc28j60_update_link_duplex_speed(enc28j60) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t enc28j60_reset(esp_eth_phy_t *phy)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
enc28j60->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = enc28j60->eth;
bmcr_reg_t bmcr = {.reset = 1};
PHY_CHECK(eth->phy_reg_write(eth, enc28j60->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 < enc28j60->reset_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!bmcr.reset) {
break;
}
}
PHY_CHECK(to < enc28j60->reset_timeout_ms / 10, "PHY reset timeout", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t enc28j60_reset_hw(esp_eth_phy_t *phy)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
// set reset_gpio_num minus zero can skip hardware reset phy chip
if (enc28j60->reset_gpio_num >= 0) {
gpio_pad_select_gpio(enc28j60->reset_gpio_num);
gpio_set_direction(enc28j60->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(enc28j60->reset_gpio_num, 0);
gpio_set_level(enc28j60->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t enc28j60_negotiate(esp_eth_phy_t *phy)
{
/**
* ENC28J60 does not support automatic duplex negotiation.
* If it is connected to an automatic duplex negotiation enabled network switch,
* ENC28J60 will be detected as a half-duplex device.
* To communicate in Full-Duplex mode, ENC28J60 and the remote node
* must be manually configured for full-duplex operation.
*/
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
/* Updata information about link, speed, duplex */
PHY_CHECK(enc28j60_update_link_duplex_speed(enc28j60) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t enc28j60_pwrctl(esp_eth_phy_t *phy, bool enable)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
esp_eth_mediator_t *eth = enc28j60->eth;
bmcr_reg_t bmcr;
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->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, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->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 enc28j60_set_addr(esp_eth_phy_t *phy, uint32_t addr)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
enc28j60->addr = addr;
return ESP_OK;
}
static esp_err_t enc28j60_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "addr can't be null", err);
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
*addr = enc28j60->addr;
return ESP_OK;
err:
return ESP_ERR_INVALID_ARG;
}
static esp_err_t enc28j60_del(esp_eth_phy_t *phy)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
free(enc28j60);
return ESP_OK;
}
static esp_err_t enc28j60_init(esp_eth_phy_t *phy)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
esp_eth_mediator_t *eth = enc28j60->eth;
/* Power on Ethernet PHY */
PHY_CHECK(enc28j60_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(enc28j60_reset(phy) == ESP_OK, "reset failed", err);
/* Check PHY ID */
phyidr1_reg_t id1;
phyidr2_reg_t id2;
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
"read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
"read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x0083 && id2.oui_lsb == 0x05 && id2.vendor_model == 0x00,
"wrong chip ID", err);
/* Disable half duplex loopback */
phcon2_reg_t phcon2;
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_PHCON2_REG_ADDR, &(phcon2.val)) == ESP_OK,
"read PHCON2 failed", err);
phcon2.hdldis = 1;
PHY_CHECK(eth->phy_reg_write(eth, enc28j60->addr, ETH_PHY_PHCON2_REG_ADDR, phcon2.val) == ESP_OK,
"write PHCON2 failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t enc28j60_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(enc28j60_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_enc28j60(const eth_phy_config_t *config)
{
PHY_CHECK(config, "can't set phy config to null", err);
phy_enc28j60_t *enc28j60 = calloc(1, sizeof(phy_enc28j60_t));
PHY_CHECK(enc28j60, "calloc enc28j60 failed", err);
enc28j60->addr = config->phy_addr; // although PHY addr is meaningless to ENC28J60
enc28j60->reset_timeout_ms = config->reset_timeout_ms;
enc28j60->reset_gpio_num = config->reset_gpio_num;
enc28j60->link_status = ETH_LINK_DOWN;
enc28j60->parent.reset = enc28j60_reset;
enc28j60->parent.reset_hw = enc28j60_reset_hw;
enc28j60->parent.init = enc28j60_init;
enc28j60->parent.deinit = enc28j60_deinit;
enc28j60->parent.set_mediator = enc28j60_set_mediator;
enc28j60->parent.negotiate = enc28j60_negotiate;
enc28j60->parent.get_link = enc28j60_get_link;
enc28j60->parent.pwrctl = enc28j60_pwrctl;
enc28j60->parent.get_addr = enc28j60_get_addr;
enc28j60->parent.set_addr = enc28j60_set_addr;
enc28j60->parent.del = enc28j60_del;
return &(enc28j60->parent);
err:
return NULL;
}

View File

@@ -0,0 +1 @@
CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n

View File

@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(eth2ap)

View File

@@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := eth2ap
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,111 @@
# eth2ap Example
(See the README.md file in the upper level 'examples' directory for more information about examples. To try a more complex application about Ethernet to WiFi data forwarding, please go to [iot-solution](https://github.com/espressif/esp-iot-solution/tree/master/examples/eth2wifi).)
**Note:** This example uses some internal APIs (e.g. `esp_wifi_internal_tx`) which might get changed between minor versions of ESP-IDF.
## Overview
![eth2ap](eth2ap.png)
The similarities on MAC layer between Ethernet and Wi-Fi make it easy to forward packets from Ethernet to Wi-Fi and vice versa. This example illustrates how to implement a simple "router" which only supports forwarding packets between Ethernet port and Wi-Fi AP interface. In this case, the Ethernet should play the role of WAN (i.e. it can access outside network) so that a mobile device could get access to the Internet when it gets connected to ESP32 through Wi-Fi.
**Note:** In this example, ESP32 works like a *bridge* between Ethernet and Wi-Fi, and it won't perform any actions on Layer3 and higher layer, which means there's no need to initialize the TCP/IP stack.
## How to use example
### 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 up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the **DM9051** as an example, illustrating how to install the Ethernet driver in the same manner.
#### Pin Assignment
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
### Configure the project
```
idf.py menuconfig
```
In addition to the common configurations for Ethernet examples from [upper level](../README.md#common-configurations), you might also need to update the default value of following configurations:
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`.
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
### Step 1: Initialize Ethernet and Wi-Fi (AP mode)
```bash
I (508) example: Power On Ethernet PHY
I (518) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (518) emac: emac reset done
I (518) example: Ethernet Started
......
I (538) wifi: wifi driver task: 3ffc7fbc, prio:23, stack:3584, core=0
I (538) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (538) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (568) wifi: wifi firmware version: ec61a20
I (568) wifi: config NVS flash: enabled
I (568) wifi: config nano formating: disabled
I (568) wifi: Init dynamic tx buffer num: 32
I (568) wifi: Init data frame dynamic rx buffer num: 32
I (578) wifi: Init management frame dynamic rx buffer num: 32
I (588) wifi: Init management short buffer num: 32
I (588) wifi: Init static rx buffer size: 1600
I (588) wifi: Init static rx buffer num: 10
I (598) wifi: Init dynamic rx buffer num: 32
```
### Step 2: Ethernet Connects to Router/Switch/PC (with DHCP server enabled)
```bash
I (4518) example: Ethernet Link Up
```
### Step 3: Start Wi-Fi AP
```bash
I (4618) phy: phy_version: 4100, 2a5dd04, Jan 23 2019, 21:00:07, 0, 0
I (4618) wifi: mode : softAP (30:ae:a4:c6:87:5b)
I (4628) wifi: Total power save buffer number: 16
I (4628) wifi: Init max length of beacon: 752/752
I (4628) wifi: Init max length of beacon: 752/752
```
### Step 4: Wi-Fi station (e.g. mobile phone) connects to ESP32's Wi-Fi
```bash
I (10168) wifi: new:<1,0>, old:<1,0>, ap:<1,1>, sta:<255,255>, prof:1
I (10168) wifi: station: c4:0b:cb:ec:9a:84 join, AID=1, bgn, 20
I (10258) example: AP got a station connected
```
Now your mobile phone should get access to the Internet.
## Troubleshooting
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
* If you got error message like `WiFi send packet failed` when running the example, you may need to enlarge the value of `FLOW_CONTROL_WIFI_SEND_DELAY_MS` in "ethernet_example_main.c", because Ethernet process packets faster than Wi-Fi on ESP32.
* If you got error message like `send flow control message failed or timeout` when running the example, you may need to enlarge the value of `FLOW_CONTROL_QUEUE_LENGTH` in "ethernet_example_main".
* Wi-Fi station doesn't receive any IP via DHCP?
* All Layer 3 (TCP/IP functions) on the ESP32 are disabled, including the SoftAP DHCP server. This means that devices must be able to access another DHCP server (for example on a Wi-Fi router connected via ethernet) or should use statically assigned IP addresses.
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "ethernet_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,159 @@
menu "Example Configuration"
choice EXAMPLE_USE_ETHERNET
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_DM9051 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_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
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
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
config EXAMPLE_WIFI_SSID
string "Wi-Fi SSID"
default "eth2ap"
help
Set the SSID of Wi-Fi ap interface.
config EXAMPLE_WIFI_PASSWORD
string "Wi-Fi Password"
default "12345678"
help
Set the password of Wi-Fi ap interface.
config EXAMPLE_WIFI_CHANNEL
int "WiFi channel"
range 1 13
default 1
help
Set the channel of Wi-Fi ap.
config EXAMPLE_MAX_STA_CONN
int "Maximum STA connections"
default 4
help
Maximum number of the station that allowed to connect to current Wi-Fi hotspot.
endmenu

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,254 @@
/* eth2ap (Ethernet to Wi-Fi AP packet forwarding) Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_private/wifi.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
static const char *TAG = "eth_example";
static esp_eth_handle_t s_eth_handle = NULL;
static xQueueHandle flow_control_queue = NULL;
static bool s_sta_is_connected = false;
static bool s_ethernet_is_connected = false;
static uint8_t s_eth_mac[6];
#define FLOW_CONTROL_QUEUE_TIMEOUT_MS (100)
#define FLOW_CONTROL_QUEUE_LENGTH (40)
#define FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS (100)
typedef struct {
void *packet;
uint16_t length;
} flow_control_msg_t;
// Forward packets from Wi-Fi to Ethernet
static esp_err_t pkt_wifi2eth(void *buffer, uint16_t len, void *eb)
{
if (s_ethernet_is_connected) {
if (esp_eth_transmit(s_eth_handle, buffer, len) != ESP_OK) {
ESP_LOGE(TAG, "Ethernet send packet failed");
}
}
esp_wifi_internal_free_rx_buffer(eb);
return ESP_OK;
}
// Forward packets from Ethernet to Wi-Fi
// Note that, Ethernet works faster than Wi-Fi on ESP32,
// so we need to add an extra queue to balance their speed difference.
static esp_err_t pkt_eth2wifi(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t len, void* priv)
{
esp_err_t ret = ESP_OK;
flow_control_msg_t msg = {
.packet = buffer,
.length = len
};
if (xQueueSend(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) != pdTRUE) {
ESP_LOGE(TAG, "send flow control message failed or timeout");
free(buffer);
ret = ESP_FAIL;
}
return ret;
}
// This task will fetch the packet from the queue, and then send out through Wi-Fi.
// Wi-Fi handles packets slower than Ethernet, we might add some delay between each transmitting.
static void eth2wifi_flow_control_task(void *args)
{
flow_control_msg_t msg;
int res = 0;
uint32_t timeout = 0;
while (1) {
if (xQueueReceive(flow_control_queue, &msg, pdMS_TO_TICKS(FLOW_CONTROL_QUEUE_TIMEOUT_MS)) == pdTRUE) {
timeout = 0;
if (s_sta_is_connected && msg.length) {
do {
vTaskDelay(pdMS_TO_TICKS(timeout));
timeout += 2;
res = esp_wifi_internal_tx(ESP_IF_WIFI_AP, msg.packet, msg.length);
} while (res && timeout < FLOW_CONTROL_WIFI_SEND_TIMEOUT_MS);
if (res != ESP_OK) {
ESP_LOGE(TAG, "WiFi send packet failed: %d", res);
}
}
free(msg.packet);
}
}
vTaskDelete(NULL);
}
// Event handler for Ethernet
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
s_ethernet_is_connected = true;
esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, s_eth_mac);
esp_wifi_set_mac(WIFI_IF_AP, s_eth_mac);
ESP_ERROR_CHECK(esp_wifi_start());
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
s_ethernet_is_connected = false;
ESP_ERROR_CHECK(esp_wifi_stop());
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
// Event handler for Wi-Fi
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
static uint8_t s_con_cnt = 0;
switch (event_id) {
case WIFI_EVENT_AP_STACONNECTED:
ESP_LOGI(TAG, "Wi-Fi AP got a station connected");
if (!s_con_cnt) {
s_sta_is_connected = true;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, pkt_wifi2eth);
}
s_con_cnt++;
break;
case WIFI_EVENT_AP_STADISCONNECTED:
ESP_LOGI(TAG, "Wi-Fi AP got a station disconnected");
s_con_cnt--;
if (!s_con_cnt) {
s_sta_is_connected = false;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, NULL);
}
break;
default:
break;
}
}
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();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
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
esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_LAN8720
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_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &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;
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle));
esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, (void *)true);
esp_eth_start(s_eth_handle);
}
static void initialize_wifi(void)
{
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
wifi_config_t wifi_config = {
.ap = {
.ssid = CONFIG_EXAMPLE_WIFI_SSID,
.ssid_len = strlen(CONFIG_EXAMPLE_WIFI_SSID),
.password = CONFIG_EXAMPLE_WIFI_PASSWORD,
.max_connection = CONFIG_EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.channel = CONFIG_EXAMPLE_WIFI_CHANNEL // default: channel 1
},
};
if (strlen(CONFIG_EXAMPLE_WIFI_PASSWORD) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
}
static esp_err_t initialize_flow_control(void)
{
flow_control_queue = xQueueCreate(FLOW_CONTROL_QUEUE_LENGTH, sizeof(flow_control_msg_t));
if (!flow_control_queue) {
ESP_LOGE(TAG, "create flow control queue failed");
return ESP_FAIL;
}
BaseType_t ret = xTaskCreate(eth2wifi_flow_control_task, "flow_ctl", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL);
if (ret != pdTRUE) {
ESP_LOGE(TAG, "create flow control task failed");
return ESP_FAIL;
}
return ESP_OK;
}
void app_main(void)
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(initialize_flow_control());
initialize_ethernet();
initialize_wifi();
}

View File

@@ -0,0 +1 @@
CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n

View File

@@ -0,0 +1,9 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components
$ENV{IDF_PATH}/examples/wifi/iperf/components)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ethernet_iperf)

View File

@@ -0,0 +1,13 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ethernet_iperf
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/system/console/components
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/wifi/iperf/components
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,156 @@
# Ethernet iperf Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of [iperf](https://iperf.fr/) protocol to measure the throughout/bandwidth of Ethernet.
The cli environment in the example is based on the [console component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html).
## How to use example
### 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 up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the **DM9051** as an example, illustrating how to install the Ethernet driver in the same manner.
#### Pin Assignment
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
### Software Tools Preparation
1. Install iperf tool on PC
* Debian/Ubuntu: `sudo apt-get install iperf`
* macOS: `brew install iperf`(if using Homebrew) or `sudo port install iperf`(if using MacPorts)
* Windows(MSYS2): Downloads binaries from [here]( https://iperf.fr/iperf-download.php#windows)
### Configure the project
```
idf.py menuconfig
```
In addition to the common configurations for Ethernet examples from [upper level](../README.md#common-configurations), you might also need to update the default value of following configurations:
1. In the `Example Configuration` menu:
* Enable storing history commands in flash under `Store command history in flash`.
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
### Test uplink bandwidth
* PC: run command: `iperf -u -s -i 3` to start iperf server in UDP mode, and report interval is 3 seconds.
* ESP32: run command: `iperf -u -c PC_IP -i 3 -t 30` to start iperf client in UDP mode, and the test will last 30 seconds.
#### PC output
```bash
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
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 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
```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 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
* PC: run command: `iperf -u -c ESP_IP -b 80M -t 30 -i 3` to start iperf client in UDP mode with estimated bandwidth 100M, and report interval is 3 seconds.
* ESP32: run command: `iperf -u -s -t 30 -i 3` to start iperf server in UDP mode, and the test will last 30 seconds.
#### PC output
```bash
------------------------------------------------------------
Client connecting to 192.168.2.156, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size: 208 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.2.160 port 59581 connected with 192.168.2.156 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 3.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 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.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
```bash
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- 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
1. Higher MCU working frequency will get higher bandwidth.
2. Put frequency invoked function into IRAM via macro `IRAM_ATTR` in code.
3. Priority of iperf task may also have effect.
## Troubleshooting
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "cmd_ethernet.c"
"ethernet_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,141 @@
menu "Example Configuration"
config EXAMPLE_STORE_HISTORY
bool "Store command history in flash"
default y
help
Linenoise line editing library provides functions to save and load
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_DM9051 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_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
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
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
endmenu

View File

@@ -0,0 +1,267 @@
/* Console example — Ethernet commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_event.h"
#include "esp_eth.h"
#include "driver/gpio.h"
#include "argtable3/argtable3.h"
#include "iperf.h"
#include "sdkconfig.h"
static esp_netif_ip_info_t ip;
static bool started = false;
static EventGroupHandle_t eth_event_group;
static const int GOTIP_BIT = BIT0;
static esp_eth_handle_t eth_handle = NULL;
static esp_netif_t *eth_netif = NULL;
/* "ethernet" command */
static struct {
struct arg_str *control;
struct arg_end *end;
} eth_control_args;
static int eth_cmd_control(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&eth_control_args);
if (nerrors != 0) {
arg_print_errors(stderr, eth_control_args.end, argv[0]);
return 1;
}
if (!strncmp(eth_control_args.control->sval[0], "info", 4)) {
uint8_t mac_addr[6];
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
printf("HW ADDR: " MACSTR "\r\n", MAC2STR(mac_addr));
esp_netif_get_ip_info(eth_netif, &ip);
printf("ETHIP: " IPSTR "\r\n", IP2STR(&ip.ip));
printf("ETHMASK: " IPSTR "\r\n", IP2STR(&ip.netmask));
printf("ETHGW: " IPSTR "\r\n", IP2STR(&ip.gw));
}
return 0;
}
/* "iperf" command */
static struct {
struct arg_str *ip;
struct arg_lit *server;
struct arg_lit *udp;
struct arg_int *port;
struct arg_int *interval;
struct arg_int *time;
struct arg_lit *abort;
struct arg_end *end;
} iperf_args;
static int eth_cmd_iperf(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
iperf_cfg_t cfg;
if (nerrors != 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 0;
}
memset(&cfg, 0, sizeof(cfg));
/* iperf -a */
if (iperf_args.abort->count != 0) {
iperf_stop();
return 0;
}
if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
return 0;
}
/* iperf -s */
if (iperf_args.ip->count == 0) {
cfg.flag |= IPERF_FLAG_SERVER;
}
/* iperf -c SERVER_ADDRESS */
else {
cfg.dip = esp_ip4addr_aton(iperf_args.ip->sval[0]);
cfg.flag |= IPERF_FLAG_CLIENT;
}
/* acquiring for ip, could blocked here */
xEventGroupWaitBits(eth_event_group, GOTIP_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
cfg.sip = ip.ip.addr;
if (cfg.sip == 0) {
return 0;
}
/* iperf -u */
if (iperf_args.udp->count == 0) {
cfg.flag |= IPERF_FLAG_TCP;
} else {
cfg.flag |= IPERF_FLAG_UDP;
}
/* iperf -p */
if (iperf_args.port->count == 0) {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = IPERF_DEFAULT_PORT;
} else {
if (cfg.flag & IPERF_FLAG_SERVER) {
cfg.sport = iperf_args.port->ival[0];
cfg.dport = IPERF_DEFAULT_PORT;
} else {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = iperf_args.port->ival[0];
}
}
/* iperf -i */
if (iperf_args.interval->count == 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
} else {
cfg.interval = iperf_args.interval->ival[0];
if (cfg.interval <= 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
}
}
/* iperf -t */
if (iperf_args.time->count == 0) {
cfg.time = IPERF_DEFAULT_TIME;
} else {
cfg.time = iperf_args.time->ival[0];
if (cfg.time <= cfg.interval) {
cfg.time = cfg.interval;
}
}
printf("mode=%s-%s sip=%d.%d.%d.%d:%d, dip=%d.%d.%d.%d:%d, interval=%d, time=%d\r\n",
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
cfg.sip & 0xFF, (cfg.sip >> 8) & 0xFF, (cfg.sip >> 16) & 0xFF, (cfg.sip >> 24) & 0xFF, cfg.sport,
cfg.dip & 0xFF, (cfg.dip >> 8) & 0xFF, (cfg.dip >> 16) & 0xFF, (cfg.dip >> 24) & 0xFF, cfg.dport,
cfg.interval, cfg.time);
iperf_start(&cfg);
return 0;
}
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_START) {
started = true;
} else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_STOP) {
xEventGroupClearBits(eth_event_group, GOTIP_BIT);
started = false;
} else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
memcpy(&ip, &event->ip_info, sizeof(ip));
xEventGroupSetBits(eth_event_group, GOTIP_BIT);
}
}
void register_ethernet(void)
{
eth_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
eth_netif = esp_netif_new(&cfg);
ESP_ERROR_CHECK(esp_eth_set_default_handlers(eth_netif));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
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();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
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
esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_LAN8720
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_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &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));
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
eth_control_args.control = arg_str1(NULL, NULL, "<info>", "Get info of Ethernet");
eth_control_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "ethernet",
.help = "Ethernet interface IO control",
.hint = NULL,
.func = eth_cmd_control,
.argtable = &eth_control_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
iperf_args.ip = arg_str0("c", "client", "<ip>",
"run in client mode, connecting to <host>");
iperf_args.server = arg_lit0("s", "server", "run in server mode");
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
iperf_args.port = arg_int0("p", "port", "<port>",
"server port to listen on/connect to");
iperf_args.interval = arg_int0("i", "interval", "<interval>",
"seconds between periodic bandwidth reports");
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
iperf_args.end = arg_end(1);
const esp_console_cmd_t iperf_cmd = {
.command = "iperf",
.help = "iperf command",
.hint = NULL,
.func = &eth_cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
}

View File

@@ -0,0 +1,20 @@
/* Console example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Register Ethernet functions
void register_ethernet(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,71 @@
/* Ethernet iperf example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_fat.h"
#include "cmd_system.h"
#include "cmd_ethernet.h"
static const char *TAG = "eth_example";
#if CONFIG_EXAMPLE_STORE_HISTORY
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
static void initialize_filesystem(void)
{
static wl_handle_t wl_handle;
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true
};
esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
return;
}
}
#endif // CONFIG_EXAMPLE_STORE_HISTORY
void app_main(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
#if CONFIG_EXAMPLE_STORE_HISTORY
initialize_filesystem();
repl_config.history_save_path = HISTORY_PATH;
#endif
repl_config.prompt = "iperf>";
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
/* Register commands */
register_system();
register_ethernet();
printf("\n =======================================================\n");
printf(" | Steps to Test Ethernet Bandwidth |\n");
printf(" | |\n");
printf(" | 1. Enter 'help', check all supported commands |\n");
printf(" | 2. Wait ESP32 to get IP from DHCP |\n");
printf(" | 3. Enter 'ethernet info', optional |\n");
printf(" | 4. Server: 'iperf -u -s -i 3' |\n");
printf(" | 5. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
printf(" | |\n");
printf(" =======================================================\n\n");
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 1M,

View File

@@ -0,0 +1,15 @@
# Increase main task stack size
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7168
# Enable filesystem
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
# Enable FreeRTOS stats formatting functions, needed for 'tasks' command
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n

View File

@@ -0,0 +1,2 @@
# ESP32-specific
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y

View File

@@ -0,0 +1,2 @@
# ESP32-S2-specific
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y