This commit is contained in:
xin
2025-07-08 08:54:35 +08:00
parent bc81bd41ac
commit 6de3458dfc
376 changed files with 68605 additions and 246 deletions

View File

@ -0,0 +1,93 @@
/****************************************************************************************************************************
WebServer_ESP32_W5500.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#pragma once
#ifndef WEBSERVER_ESP32_W5500_H
#define WEBSERVER_ESP32_W5500_H
//////////////////////////////////////////////////////////////
//#if !defined(USING_CORE_ESP32_CORE_V200_PLUS)
#if ( ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) && ( ARDUINO_ESP32_GIT_VER != 0x46d5afb1 ) )
#define USING_CORE_ESP32_CORE_V200_PLUS true
#if (_ETHERNET_WEBSERVER_LOGLEVEL_ > 3)
#warning Using code for ESP32 core v2.0.0+ in WebServer_ESP32_W5500.h
#endif
#define WEBSERVER_ESP32_W5500_VERSION "WebServer_ESP32_W5500 v1.5.3 for core v2.0.0+"
#else
#if (_ETHERNET_WEBSERVER_LOGLEVEL_ > 3)
#warning Using code for ESP32 core v1.0.6- in WebServer_ESP32_W5500.h
#endif
#define WEBSERVER_ESP32_W5500_VERSION "WebServer_ESP32_W5500 v1.5.3 for core v1.0.6-"
#endif
#define WEBSERVER_ESP32_W5500_VERSION_MAJOR 1
#define WEBSERVER_ESP32_W5500_VERSION_MINOR 5
#define WEBSERVER_ESP32_W5500_VERSION_PATCH 3
#define WEBSERVER_ESP32_W5500_VERSION_INT 1005003
//////////////////////////////////////////////////////////////
#if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \
ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \
ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM )
#error ESP32_S2 not supported. Use WebServer_ESP32_SC_W5500 library
#elif ( ARDUINO_ESP32C3_DEV )
#error ESP32_C3 not supported. Use WebServer_ESP32_SC_W5500 library
#elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \
defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) )
#error ESP32_S3 not supported. Use WebServer_ESP32_SC_W5500 library
#elif ESP32
#if (_ETHERNET_WEBSERVER_LOGLEVEL_ > 3)
#warning Using ESP32 architecture for WebServer_ESP32_W5500
#endif
#define BOARD_NAME ARDUINO_BOARD
#else
#error This code is designed to run on ESP32 platform! Please check your Tools->Board setting.
#endif
//////////////////////////////////////////////////////////////
#include <Arduino.h>
#include "WebServer_ESP32_W5500_Debug.h"
//////////////////////////////////////////////////////////////
#include "w5500/esp32_w5500.h"
#include "WebServer_ESP32_W5500.hpp"
#include "WebServer_ESP32_W5500_Impl.h"
#endif // WEBSERVER_ESP32_W5500_H

View File

@ -0,0 +1,88 @@
/****************************************************************************************************************************
WebServer_ESP32_W5500.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#pragma once
#ifndef WEBSERVER_ESP32_W5500_HPP
#define WEBSERVER_ESP32_W5500_HPP
//////////////////////////////////////////////////////////////
//#define CONFIG_ETH_SPI_ETHERNET_W5500 true
//////////////////////////////////////////////////////////////
#include <WiFi.h>
#include <WebServer.h> // Introduce corresponding libraries
#include <hal/spi_types.h>
//////////////////////////////////////////////////////////////
#if !defined(ETH_SPI_HOST)
#define ETH_SPI_HOST SPI3_HOST
#endif
#if !defined(SPI_CLOCK_MHZ)
// Using 25MHz for W5500, 14MHz for W5100
#define SPI_CLOCK_MHZ 25
#endif
#if !defined(INT_GPIO)
#define INT_GPIO 4
#endif
#if !defined(MISO_GPIO)
#define MISO_GPIO 19
#endif
#if !defined(MOSI_GPIO)
#define MOSI_GPIO 23
#endif
#if !defined(SCK_GPIO)
#define SCK_GPIO 18
#endif
#if !defined(CS_GPIO)
#define CS_GPIO 5
#endif
//////////////////////////////////////////////////////////////
#ifndef SHIELD_TYPE
#define SHIELD_TYPE "ESP32_W5500"
#endif
//////////////////////////////////////////////////////////////
extern bool ESP32_W5500_eth_connected;
extern void ESP32_W5500_onEvent();
extern void ESP32_W5500_waitForConnect();
extern bool ESP32_W5500_isConnected();
extern void ESP32_W5500_event(WiFiEvent_t event);
//////////////////////////////////////////////////////////////
#endif // WEBSERVER_ESP32_W5500_HPP

View File

@ -0,0 +1,111 @@
/****************************************************************************************************************************
WebServer_ESP32_W5500_Debug.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP8266 https://github.com/esp8266/Arduino/releases
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#pragma once
#ifndef WEBSERVER_ESP32_W5500_DEBUG_H
#define WEBSERVER_ESP32_W5500_DEBUG_H
#include <Arduino.h>
#include <stdio.h>
///////////////////////////////////////
#ifdef DEBUG_ETHERNET_WEBSERVER_PORT
#define ET_DEBUG_OUTPUT DEBUG_ETHERNET_WEBSERVER_PORT
#else
#define ET_DEBUG_OUTPUT Serial
#endif
///////////////////////////////////////
// Change _ETHERNET_WEBSERVER_LOGLEVEL_ to set tracing and logging verbosity
// 0: DISABLED: no logging
// 1: ERROR: errors
// 2: WARN: errors and warnings
// 3: INFO: errors, warnings and informational (default)
// 4: DEBUG: errors, warnings, informational and debug
#ifndef _ETHERNET_WEBSERVER_LOGLEVEL_
#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0
#endif
///////////////////////////////////////
const char EWS_MARK[] = "[EWS] ";
const char EWS_SPACE[] = " ";
const char EWS_LINE[] = "========================================\n";
#define EWS_PRINT_MARK EWS_PRINT(EWS_MARK)
#define EWS_PRINT_SP EWS_PRINT(EWS_SPACE)
#define EWS_PRINT_LINE EWS_PRINT(EWS_LINE)
#define EWS_PRINT ET_DEBUG_OUTPUT.print
#define EWS_PRINTLN ET_DEBUG_OUTPUT.println
///////////////////////////////////////
#define ET_LOG(x) { EWS_PRINTLN(x); }
#define ET_LOG0(x) { EWS_PRINT(x); }
#define ET_LOG1(x,y) { EWS_PRINT(x); EWS_PRINTLN(y); }
#define ET_LOG2(x,y,z) { EWS_PRINT(x); EWS_PRINT(y); EWS_PRINTLN(z); }
#define ET_LOG3(x,y,z,w) { EWS_PRINT(x); EWS_PRINT(y); EWS_PRINT(z); EWS_PRINTLN(w); }
///////////////////////////////////////
#define ET_LOGERROR(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>0) { EWS_PRINT_MARK; EWS_PRINTLN(x); }
#define ET_LOGERROR_LINE(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>0) { EWS_PRINT_MARK; EWS_PRINTLN(x); EWS_PRINT_LINE; }
#define ET_LOGERROR0(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>0) { EWS_PRINT(x); }
#define ET_LOGERROR1(x,y) if(_ETHERNET_WEBSERVER_LOGLEVEL_>0) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINTLN(y); }
#define ET_LOGERROR2(x,y,z) if(_ETHERNET_WEBSERVER_LOGLEVEL_>0) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINTLN(z); }
#define ET_LOGERROR3(x,y,z,w) if(_ETHERNET_WEBSERVER_LOGLEVEL_>0) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINT(z); EWS_PRINT_SP; EWS_PRINTLN(w); }
///////////////////////////////////////
#define ET_LOGWARN(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>1) { EWS_PRINT_MARK; EWS_PRINTLN(x); }
#define ET_LOGWARN_LINE(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>1) { EWS_PRINT_MARK; EWS_PRINTLN(x); EWS_PRINT_LINE; }
#define ET_LOGWARN0(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>1) { EWS_PRINT(x); }
#define ET_LOGWARN1(x,y) if(_ETHERNET_WEBSERVER_LOGLEVEL_>1) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINTLN(y); }
#define ET_LOGWARN2(x,y,z) if(_ETHERNET_WEBSERVER_LOGLEVEL_>1) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINTLN(z); }
#define ET_LOGWARN3(x,y,z,w) if(_ETHERNET_WEBSERVER_LOGLEVEL_>1) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINT(z); EWS_PRINT_SP; EWS_PRINTLN(w); }
///////////////////////////////////////
#define ET_LOGINFO(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>2) { EWS_PRINT_MARK; EWS_PRINTLN(x); }
#define ET_LOGINFO_LINE(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>2) { EWS_PRINT_MARK; EWS_PRINTLN(x); EWS_PRINT_LINE; }
#define ET_LOGINFO0(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>2) { EWS_PRINT(x); }
#define ET_LOGINFO1(x,y) if(_ETHERNET_WEBSERVER_LOGLEVEL_>2) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINTLN(y); }
#define ET_LOGINFO2(x,y,z) if(_ETHERNET_WEBSERVER_LOGLEVEL_>2) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINTLN(z); }
#define ET_LOGINFO3(x,y,z,w) if(_ETHERNET_WEBSERVER_LOGLEVEL_>2) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINT(z); EWS_PRINT_SP; EWS_PRINTLN(w); }
///////////////////////////////////////
#define ET_LOGDEBUG(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>3) { EWS_PRINT_MARK; EWS_PRINTLN(x); }
#define ET_LOGDEBUG_LINE(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>3) { EWS_PRINT_MARK; EWS_PRINTLN(x); EWS_PRINT_LINE; }
#define ET_LOGDEBUG0(x) if(_ETHERNET_WEBSERVER_LOGLEVEL_>3) { EWS_PRINT(x); }
#define ET_LOGDEBUG1(x,y) if(_ETHERNET_WEBSERVER_LOGLEVEL_>3) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINTLN(y); }
#define ET_LOGDEBUG2(x,y,z) if(_ETHERNET_WEBSERVER_LOGLEVEL_>3) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINTLN(z); }
#define ET_LOGDEBUG3(x,y,z,w) if(_ETHERNET_WEBSERVER_LOGLEVEL_>3) { EWS_PRINT_MARK; EWS_PRINT(x); EWS_PRINT_SP; EWS_PRINT(y); EWS_PRINT_SP; EWS_PRINT(z); EWS_PRINT_SP; EWS_PRINTLN(w); }
///////////////////////////////////////
#endif // WEBSERVER_ESP32_W5500_DEBUG_H

View File

@ -0,0 +1,161 @@
/****************************************************************************************************************************
WebServer_ESP32_W5500_Impl.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#pragma once
#ifndef WEBSERVER_ESP32_W5500_IMPL_H
#define WEBSERVER_ESP32_W5500_IMPL_H
//////////////////////////////////////////////////////////////
bool ESP32_W5500_eth_connected = false;
//////////////////////////////////////////////////////////////
void ESP32_W5500_onEvent()
{
WiFi.onEvent(ESP32_W5500_event);
}
//////////////////////////////////////////////////////////////
void ESP32_W5500_waitForConnect()
{
while (!ESP32_W5500_eth_connected)
delay(100);
}
//////////////////////////////////////////////////////////////
bool ESP32_W5500_isConnected()
{
return ESP32_W5500_eth_connected;
}
//////////////////////////////////////////////////////////////
void ESP32_W5500_event(WiFiEvent_t event)
{
switch (event)
{
//#if USING_CORE_ESP32_CORE_V200_PLUS
#if ( ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) && ( ARDUINO_ESP32_GIT_VER != 0x46d5afb1 ) )
// For breaking core v2.0.0
// Why so strange to define a breaking enum arduino_event_id_t in WiFiGeneric.h
// compared to the old system_event_id_t, now in tools/sdk/esp32/include/esp_event/include/esp_event_legacy.h
// You can preserve the old enum order and just adding new items to do no harm
case ARDUINO_EVENT_ETH_START:
ET_LOG(F("\nETH Started"));
//set eth hostname here
ETH.setHostname("ESP32_W5500");
break;
case ARDUINO_EVENT_ETH_CONNECTED:
ET_LOG(F("ETH Connected"));
break;
case ARDUINO_EVENT_ETH_GOT_IP:
if (!ESP32_W5500_eth_connected)
{
ET_LOG3(F("ETH MAC: "), ETH.macAddress(), F(", IPv4: "), ETH.localIP());
if (ETH.fullDuplex())
{
ET_LOG0(F("FULL_DUPLEX, "));
}
else
{
ET_LOG0(F("HALF_DUPLEX, "));
}
ET_LOG1(ETH.linkSpeed(), F("Mbps"));
ESP32_W5500_eth_connected = true;
}
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
ET_LOG("ETH Disconnected");
ESP32_W5500_eth_connected = false;
break;
case ARDUINO_EVENT_ETH_STOP:
ET_LOG("\nETH Stopped");
ESP32_W5500_eth_connected = false;
break;
#else
// For old core v1.0.6-
// Core v2.0.0 defines a stupid enum arduino_event_id_t, breaking any code for ESP32_W5500 written for previous core
// Why so strange to define a breaking enum arduino_event_id_t in WiFiGeneric.h
// compared to the old system_event_id_t, now in tools/sdk/esp32/include/esp_event/include/esp_event_legacy.h
// You can preserve the old enum order and just adding new items to do no harm
case SYSTEM_EVENT_ETH_START:
ET_LOG(F("\nETH Started"));
//set eth hostname here
ETH.setHostname("ESP32_W5500");
break;
case SYSTEM_EVENT_ETH_CONNECTED:
ET_LOG(F("ETH Connected"));
break;
case SYSTEM_EVENT_ETH_GOT_IP:
if (!ESP32_W5500_eth_connected)
{
ET_LOG3(F("ETH MAC: "), ETH.macAddress(), F(", IPv4: "), ETH.localIP());
if (ETH.fullDuplex())
{
ET_LOG0(F("FULL_DUPLEX, "));
}
else
{
ET_LOG0(F("HALF_DUPLEX, "));
}
ET_LOG1(ETH.linkSpeed(), F("Mbps"));
ESP32_W5500_eth_connected = true;
}
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
ET_LOG("ETH Disconnected");
ESP32_W5500_eth_connected = false;
break;
case SYSTEM_EVENT_ETH_STOP:
ET_LOG("\nETH Stopped");
ESP32_W5500_eth_connected = false;
break;
#endif
default:
break;
}
}
//////////////////////////////////////////////////////////////
#endif // WEBSERVER_ESP32_W5500_IMPL_H

View File

@ -0,0 +1,439 @@
/****************************************************************************************************************************
esp32_w5500.cpp
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#include "WebServer_ESP32_W5500_Debug.h"
#include "esp32_w5500.h"
extern "C"
{
esp_eth_mac_t* w5500_begin(int MISO, int MOSI, int SCLK, int CS, int INT, int SPICLOCK_MHZ,
int SPIHOST);
#include "esp_eth/esp_eth_w5500.h"
}
#include "esp_event.h"
#include "esp_eth_phy.h"
#include "esp_eth_mac.h"
#include "esp_eth_com.h"
#if CONFIG_IDF_TARGET_ESP32
#include "soc/emac_ext_struct.h"
#include "soc/rtc.h"
#endif
#include "lwip/err.h"
#include "lwip/dns.h"
extern void tcpipInit();
////////////////////////////////////////
ESP32_W5500::ESP32_W5500()
: initialized(false)
, staticIP(false)
, eth_handle(NULL)
, started(false)
, eth_link(ETH_LINK_DOWN)
{
}
////////////////////////////////////////
ESP32_W5500::~ESP32_W5500()
{}
////////////////////////////////////////
bool ESP32_W5500::begin(int MISO, int MOSI, int SCLK, int CS, int INT, int SPICLOCK_MHZ, int SPIHOST,
uint8_t *W5500_Mac)
{
tcpipInit();
//esp_base_mac_addr_set( W5500_Mac );
if ( esp_read_mac(mac_eth, ESP_MAC_ETH) == ESP_OK )
{
char macStr[18] = { 0 };
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac_eth[0], mac_eth[1], mac_eth[2],
mac_eth[3], mac_eth[4], mac_eth[5]);
ET_LOGINFO1("Using built-in mac_eth =", macStr);
esp_base_mac_addr_set( mac_eth );
}
else
{
ET_LOGINFO("Using user mac_eth");
memcpy(mac_eth, W5500_Mac, sizeof(mac_eth));
esp_base_mac_addr_set( W5500_Mac );
}
tcpip_adapter_set_default_eth_handlers();
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&cfg);
esp_eth_mac_t *eth_mac = w5500_begin(MISO, MOSI, SCLK, CS, INT, SPICLOCK_MHZ, SPIHOST);
if (eth_mac == NULL)
{
ET_LOGERROR("esp_eth_mac_new_esp32 failed");
return false;
}
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.autonego_timeout_ms = 0; // W5500 doesn't support auto-negotiation
phy_config.reset_gpio_num = -1; // W5500 doesn't have a pin to reset internal PHY
esp_eth_phy_t *eth_phy = esp_eth_phy_new_w5500(&phy_config);
if (eth_phy == NULL)
{
ET_LOGERROR("esp_eth_phy_new failed");
return false;
}
eth_handle = NULL;
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy);
if (esp_eth_driver_install(&eth_config, &eth_handle) != ESP_OK || eth_handle == NULL)
{
ET_LOG("esp_eth_driver_install failed");
return false;
}
eth_mac->set_addr(eth_mac, mac_eth);
#if 1
if ( (SPICLOCK_MHZ < 14) || (SPICLOCK_MHZ > 25) )
{
ET_LOGERROR("SPI Clock must be >= 8 and <= 25 MHz for W5500");
ESP_ERROR_CHECK(ESP_FAIL);
}
#endif
/* attach Ethernet driver to TCP/IP stack */
if (esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)) != ESP_OK)
{
ET_LOGERROR("esp_netif_attach failed");
return false;
}
if (esp_eth_start(eth_handle) != ESP_OK)
{
ET_LOG("esp_eth_start failed");
return false;
}
// holds a few microseconds to let DHCP start and enter into a good state
// FIX ME -- addresses issue https://github.com/espressif/arduino-esp32/issues/5733
delay(50);
return true;
}
////////////////////////////////////////
bool ESP32_W5500::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2)
{
esp_err_t err = ESP_OK;
tcpip_adapter_ip_info_t info;
if (static_cast<uint32_t>(local_ip) != 0)
{
info.ip.addr = static_cast<uint32_t>(local_ip);
info.gw.addr = static_cast<uint32_t>(gateway);
info.netmask.addr = static_cast<uint32_t>(subnet);
}
else
{
info.ip.addr = 0;
info.gw.addr = 0;
info.netmask.addr = 0;
}
err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH);
if (err != ESP_OK && err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED)
{
ET_LOGERROR1("DHCP could not be stopped! Error =", err);
return false;
}
err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH, &info);
if (err != ERR_OK)
{
ET_LOGERROR1("STA IP could not be configured! Error = ", err);
return false;
}
if (info.ip.addr)
{
staticIP = true;
}
else
{
err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_ETH);
if (err != ESP_OK && err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED)
{
ET_LOGWARN1("DHCP could not be started! Error =", err);
return false;
}
staticIP = false;
}
ip_addr_t d;
d.type = IPADDR_TYPE_V4;
if (static_cast<uint32_t>(dns1) != 0)
{
// Set DNS1-Server
d.u_addr.ip4.addr = static_cast<uint32_t>(dns1);
dns_setserver(0, &d);
}
if (static_cast<uint32_t>(dns2) != 0)
{
// Set DNS2-Server
d.u_addr.ip4.addr = static_cast<uint32_t>(dns2);
dns_setserver(1, &d);
}
return true;
}
////////////////////////////////////////
IPAddress ESP32_W5500::localIP()
{
tcpip_adapter_ip_info_t ip;
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip))
{
ET_LOGDEBUG("localIP NULL");
return IPAddress();
}
ET_LOGDEBUG1("localIP =", IPAddress(ip.ip.addr));
return IPAddress(ip.ip.addr);
}
////////////////////////////////////////
IPAddress ESP32_W5500::subnetMask()
{
tcpip_adapter_ip_info_t ip;
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip))
{
return IPAddress();
}
return IPAddress(ip.netmask.addr);
}
////////////////////////////////////////
IPAddress ESP32_W5500::gatewayIP()
{
tcpip_adapter_ip_info_t ip;
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip))
{
return IPAddress();
}
return IPAddress(ip.gw.addr);
}
////////////////////////////////////////
IPAddress ESP32_W5500::dnsIP(uint8_t dns_no)
{
const ip_addr_t * dns_ip = dns_getserver(dns_no);
return IPAddress(dns_ip->u_addr.ip4.addr);
}
////////////////////////////////////////
IPAddress ESP32_W5500::broadcastIP()
{
tcpip_adapter_ip_info_t ip;
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip))
{
return IPAddress();
}
return WiFiGenericClass::calculateBroadcast(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr));
}
////////////////////////////////////////
IPAddress ESP32_W5500::networkID()
{
tcpip_adapter_ip_info_t ip;
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip))
{
return IPAddress();
}
return WiFiGenericClass::calculateNetworkID(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr));
}
////////////////////////////////////////
uint8_t ESP32_W5500::subnetCIDR()
{
tcpip_adapter_ip_info_t ip;
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip))
{
return (uint8_t)0;
}
return WiFiGenericClass::calculateSubnetCIDR(IPAddress(ip.netmask.addr));
}
////////////////////////////////////////
const char * ESP32_W5500::getHostname()
{
const char * hostname;
if (tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_ETH, &hostname))
{
return NULL;
}
return hostname;
}
////////////////////////////////////////
bool ESP32_W5500::setHostname(const char * hostname)
{
return tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_ETH, hostname) == 0;
}
////////////////////////////////////////
bool ESP32_W5500::fullDuplex()
{
#ifdef ESP_IDF_VERSION_MAJOR
return true;//todo: do not see an API for this
#else
return eth_config.phy_get_duplex_mode();
#endif
}
////////////////////////////////////////
bool ESP32_W5500::linkUp()
{
#ifdef ESP_IDF_VERSION_MAJOR
return eth_link == ETH_LINK_UP;
#else
return eth_config.phy_check_link();
#endif
}
////////////////////////////////////////
uint8_t ESP32_W5500::linkSpeed()
{
#ifdef ESP_IDF_VERSION_MAJOR
eth_speed_t link_speed;
esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &link_speed);
return (link_speed == ETH_SPEED_10M) ? 10 : 100;
#else
return eth_config.phy_get_speed_mode() ? 100 : 10;
#endif
}
////////////////////////////////////////
bool ESP32_W5500::enableIpV6()
{
return tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_ETH) == 0;
}
////////////////////////////////////////
IPv6Address ESP32_W5500::localIPv6()
{
static ip6_addr_t addr;
if (tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_ETH, &addr))
{
return IPv6Address();
}
return IPv6Address(addr.addr);
}
////////////////////////////////////////
uint8_t * ESP32_W5500::macAddress(uint8_t* mac)
{
if (!mac)
{
return NULL;
}
#ifdef ESP_IDF_VERSION_MAJOR
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac);
#else
esp_eth_get_mac(mac);
#endif
return mac;
}
////////////////////////////////////////
String ESP32_W5500::macAddress()
{
uint8_t mac[6] = {0, 0, 0, 0, 0, 0};
char macStr[18] = { 0 };
macAddress(mac);
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(macStr);
}
////////////////////////////////////////
ESP32_W5500 ETH;

View File

@ -0,0 +1,104 @@
/****************************************************************************************************************************
esp32_w5500.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#ifndef _ESP32_W5500_H_
#define _ESP32_W5500_H_
#include "WiFi.h"
#include "esp_system.h"
#include "esp_eth.h"
#include <hal/spi_types.h>
////////////////////////////////////////
#if ESP_IDF_VERSION_MAJOR < 4 || ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,4,0)
#error "This version of Arduino is too old"
#endif
////////////////////////////////////////
static uint8_t W5500_Default_Mac[] = { 0xFE, 0xED, 0xDE, 0xAD, 0xBE, 0xEF };
////////////////////////////////////////
class ESP32_W5500
{
private:
bool initialized;
bool staticIP;
uint8_t mac_eth[6] = { 0xFE, 0xED, 0xDE, 0xAD, 0xBE, 0xEF };
#if ESP_IDF_VERSION_MAJOR > 3
esp_eth_handle_t eth_handle;
protected:
bool started;
eth_link_t eth_link;
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
#else
bool started;
eth_config_t eth_config;
#endif
public:
ESP32_W5500();
~ESP32_W5500();
bool begin(int MISO, int MOSI, int SCLK, int CS, int INT, int SPICLOCK_MHZ = 25, int SPIHOST = SPI3_HOST,
uint8_t *W5500_Mac = W5500_Default_Mac);
bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000,
IPAddress dns2 = (uint32_t)0x00000000);
const char * getHostname();
bool setHostname(const char * hostname);
bool fullDuplex();
bool linkUp();
uint8_t linkSpeed();
bool enableIpV6();
IPv6Address localIPv6();
IPAddress localIP();
IPAddress subnetMask();
IPAddress gatewayIP();
IPAddress dnsIP(uint8_t dns_no = 0);
IPAddress broadcastIP();
IPAddress networkID();
uint8_t subnetCIDR();
uint8_t * macAddress(uint8_t* mac);
String macAddress();
friend class WiFiClient;
friend class WiFiServer;
};
////////////////////////////////////////
extern ESP32_W5500 ETH;
////////////////////////////////////////
#endif /* _ESP32_W5500_H_ */

View File

@ -0,0 +1,958 @@
/****************************************************************************************************************************
esp_eth_mac_w5500.c
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_eth.h"
#include "esp_system.h"
#include "esp_intr_alloc.h"
#include "esp_heap_caps.h"
#include "esp_rom_gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "hal/cpu_hal.h"
#include "w5500.h"
#include "sdkconfig.h"
////////////////////////////////////////
static const char *TAG = "w5500.mac";
#define W5500_SPI_LOCK_TIMEOUT_MS (50)
#define W5500_TX_MEM_SIZE (0x4000)
#define W5500_RX_MEM_SIZE (0x4000)
////////////////////////////////////////
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;
int int_gpio_num;
uint8_t addr[6];
bool packets_remain;
} emac_w5500_t;
////////////////////////////////////////
static inline bool w5500_lock(emac_w5500_t *emac)
{
return xSemaphoreTake(emac->spi_lock, pdMS_TO_TICKS(W5500_SPI_LOCK_TIMEOUT_MS)) == pdTRUE;
}
////////////////////////////////////////
static inline bool w5500_unlock(emac_w5500_t *emac)
{
return xSemaphoreGive(emac->spi_lock) == pdTRUE;
}
////////////////////////////////////////
static esp_err_t w5500_write(emac_w5500_t *emac, uint32_t address, const void *value, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans =
{
.cmd = (address >> W5500_ADDR_OFFSET),
.addr = ((address & 0xFFFF) | (W5500_ACCESS_MODE_WRITE << W5500_RWB_OFFSET) | W5500_SPI_OP_MODE_VDM),
.length = 8 * len,
.tx_buffer = value
};
if (w5500_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;
}
w5500_unlock(emac);
}
else
{
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_read(emac_w5500_t *emac, uint32_t address, void *value, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans =
{
// use direct reads for registers to prevent overwrites by 4-byte boundary writes
.flags = len <= 4 ? SPI_TRANS_USE_RXDATA : 0,
.cmd = (address >> W5500_ADDR_OFFSET),
.addr = ((address & 0xFFFF) | (W5500_ACCESS_MODE_READ << W5500_RWB_OFFSET) | W5500_SPI_OP_MODE_VDM),
.length = 8 * len,
.rx_buffer = value
};
if (w5500_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;
}
w5500_unlock(emac);
}
else
{
ret = ESP_ERR_TIMEOUT;
}
if ((trans.flags & SPI_TRANS_USE_RXDATA) && len <= 4)
{
memcpy(value, trans.rx_data, len); // copy register values to output
}
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_send_command(emac_w5500_t *emac, uint8_t command, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_CR(0), &command, sizeof(command)), err, TAG, "Write SCR failed");
// after W5500 accepts the command, the command register will be cleared automatically
uint32_t to = 0;
for (to = 0; to < timeout_ms / 10; to++)
{
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_CR(0), &command, sizeof(command)), err, TAG, "Read SCR failed");
if (!command)
{
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
ESP_GOTO_ON_FALSE(to < timeout_ms / 10, ESP_ERR_TIMEOUT, err, TAG, "Send command timeout");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_get_tx_free_size(emac_w5500_t *emac, uint16_t *size)
{
esp_err_t ret = ESP_OK;
uint16_t free0, free1 = 0;
// read TX_FSR register more than once, until we get the same value
// this is a trick because we might be interrupted between reading the high/low part of the TX_FSR register (16 bits in length)
do
{
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_TX_FSR(0), &free0, sizeof(free0)), err, TAG, "Read TX FSR failed");
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_TX_FSR(0), &free1, sizeof(free1)), err, TAG, "Read TX FSR failed");
} while (free0 != free1);
*size = __builtin_bswap16(free0);
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_get_rx_received_size(emac_w5500_t *emac, uint16_t *size)
{
esp_err_t ret = ESP_OK;
uint16_t received0, received1 = 0;
do
{
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &received0, sizeof(received0)), err, TAG,
"Read RX RSR failed");
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &received1, sizeof(received1)), err, TAG,
"Read RX RSR failed");
} while (received0 != received1);
*size = __builtin_bswap16(received0);
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_write_buffer(emac_w5500_t *emac, const void *buffer, uint32_t len, uint16_t offset)
{
esp_err_t ret = ESP_OK;
uint32_t remain = len;
const uint8_t *buf = buffer;
offset %= W5500_TX_MEM_SIZE;
if (offset + len > W5500_TX_MEM_SIZE)
{
remain = (offset + len) % W5500_TX_MEM_SIZE;
len = W5500_TX_MEM_SIZE - offset;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buf, len), err, TAG, "Write TX buffer failed");
offset += len;
buf += len;
}
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buf, remain), err, TAG, "Write TX buffer failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_read_buffer(emac_w5500_t *emac, void *buffer, uint32_t len, uint16_t offset)
{
esp_err_t ret = ESP_OK;
uint32_t remain = len;
uint8_t *buf = buffer;
offset %= W5500_RX_MEM_SIZE;
if (offset + len > W5500_RX_MEM_SIZE)
{
remain = (offset + len) % W5500_RX_MEM_SIZE;
len = W5500_RX_MEM_SIZE - offset;
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buf, len), err, TAG, "Read RX buffer failed");
offset += len;
buf += len;
}
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buf, remain), err, TAG, "Read RX buffer failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_set_mac_addr(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_MAC, emac->addr, 6), err, TAG, "Write MAC address register failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_reset(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
/* software reset */
uint8_t mr = W5500_MR_RST; // Set RST bit (auto clear)
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_MR, &mr, sizeof(mr)), err, TAG, "Write MR failed");
uint32_t to = 0;
for (to = 0; to < emac->sw_reset_timeout_ms / 10; to++)
{
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_MR, &mr, sizeof(mr)), err, TAG, "Read MR failed");
if (!(mr & W5500_MR_RST))
{
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
ESP_GOTO_ON_FALSE(to < emac->sw_reset_timeout_ms / 10, ESP_ERR_TIMEOUT, err, TAG, "Reset timeout");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_verify_id(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
uint8_t version = 0;
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_VERSIONR, &version, sizeof(version)), err, TAG, "Read VERSIONR failed");
// W5500 doesn't have chip ID, we just print the version number instead
ESP_LOGI(TAG, "version=%x", version);
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_setup_default(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
uint8_t reg_value = 16;
// Only SOCK0 can be used as MAC RAW mode, so we give the whole buffer (16KB TX and 16KB RX) to SOCK0
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_RXBUF_SIZE(0), &reg_value, sizeof(reg_value)), err, TAG,
"Set rx buffer size failed");
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_TXBUF_SIZE(0), &reg_value, sizeof(reg_value)), err, TAG,
"Set tx buffer size failed");
reg_value = 0;
for (int i = 1; i < 8; i++)
{
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_RXBUF_SIZE(i), &reg_value, sizeof(reg_value)), err, TAG,
"Set SOCK_RXBUF_SIZE failed");
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_TXBUF_SIZE(i), &reg_value, sizeof(reg_value)), err, TAG,
"Set SOCK_TXBUF_SIZE failed");
}
/* Enable ping block, disable PPPoE, WOL */
reg_value = W5500_MR_PB;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_MR, &reg_value, sizeof(reg_value)), err, TAG, "Write MR failed");
/* Disable interrupt for all sockets by default */
reg_value = 0;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SIMR, &reg_value, sizeof(reg_value)), err, TAG, "Write SIMR failed");
/* Enable MAC RAW mode for SOCK0, enable MAC filter, no blocking broadcast and multicast */
reg_value = W5500_SMR_MAC_RAW | W5500_SMR_MAC_FILTER;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_MR(0), &reg_value, sizeof(reg_value)), err, TAG,
"Write SOCK0 MR failed");
/* Enable receive event for SOCK0 */
reg_value = W5500_SIR_RECV;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_IMR(0), &reg_value, sizeof(reg_value)), err, TAG,
"Write SOCK0 IMR failed");
/* Set the interrupt re-assert level to maximum (~1.5ms) to lower the chances of missing it */
uint16_t int_level = __builtin_bswap16(0xFFFF);
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_INTLEVEL, &int_level, sizeof(int_level)), err, TAG,
"Write INTLEVEL failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_start(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint8_t reg_value = 0;
/* open SOCK0 */
ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_OPEN, 100), err, TAG, "Issue OPEN command failed");
/* enable interrupt for SOCK0 */
reg_value = W5500_SIMR_SOCK0;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SIMR, &reg_value, sizeof(reg_value)), err, TAG, "Write SIMR failed");
err:
return ret;
}
static esp_err_t emac_w5500_stop(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint8_t reg_value = 0;
/* disable interrupt */
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SIMR, &reg_value, sizeof(reg_value)), err, TAG, "Write SIMR failed");
/* close SOCK0 */
ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_CLOSE, 100), err, TAG, "Issue SCR_CLOSE command failed");
err:
return ret;
}
////////////////////////////////////////
IRAM_ATTR static void w5500_isr_handler(void *arg)
{
emac_w5500_t *emac = (emac_w5500_t *)arg;
BaseType_t high_task_wakeup = pdFALSE;
/* notify w5500 task */
vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup);
if (high_task_wakeup != pdFALSE)
{
portYIELD_FROM_ISR();
}
}
////////////////////////////////////////
static void emac_w5500_task(void *arg)
{
emac_w5500_t *emac = (emac_w5500_t *)arg;
uint8_t status = 0;
uint8_t *buffer = NULL;
uint32_t length = 0;
while (1)
{
// check if the task receives any notification
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ...
gpio_get_level(emac->int_gpio_num) != 0)
{
// ...and no interrupt asserted
continue; // -> just continue to check again
}
/* read interrupt status */
w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
/* packet received */
if (status & W5500_SIR_RECV)
{
status = W5500_SIR_RECV;
// clear interrupt status
w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status));
do
{
length = ETH_MAX_PACKET_SIZE;
buffer = heap_caps_malloc(length, MALLOC_CAP_DMA);
if (!buffer)
{
ESP_LOGE(TAG, "No mem for receive buffer");
break;
}
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_w5500_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "Can't set mac's mediator to null");
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
emac->eth = eth;
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_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_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
// PHY register and MAC registers are mixed together in W5500
// The only PHY register is PHYCFGR
ESP_GOTO_ON_FALSE(phy_reg == W5500_REG_PHYCFGR, ESP_FAIL, err, TAG, "Wrong PHY register");
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_PHYCFGR, &reg_value, sizeof(uint8_t)), err, TAG,
"write PHY register failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_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;
ESP_GOTO_ON_FALSE(reg_value, ESP_ERR_INVALID_ARG, err, TAG, "Can't set reg_value to null");
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
// PHY register and MAC registers are mixed together in W5500
// The only PHY register is PHYCFGR
ESP_GOTO_ON_FALSE(phy_reg == W5500_REG_PHYCFGR, ESP_FAIL, err, TAG, "Wrong PHY register");
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_PHYCFGR, reg_value, sizeof(uint8_t)), err, TAG,
"read PHY register failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_set_addr(esp_eth_mac_t *mac, uint8_t *addr)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "Invalid argument");
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
memcpy(emac->addr, addr, 6);
ESP_GOTO_ON_ERROR(w5500_set_mac_addr(emac), err, TAG, "Set mac address failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_get_addr(esp_eth_mac_t *mac, uint8_t *addr)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "Invalid argument");
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
memcpy(addr, emac->addr, 6);
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_set_link(esp_eth_mac_t *mac, eth_link_t link)
{
esp_err_t ret = ESP_OK;
switch (link)
{
case ETH_LINK_UP:
ESP_LOGD(TAG, "Link is up");
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "W5500 start failed");
break;
case ETH_LINK_DOWN:
ESP_LOGD(TAG, "link is down");
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "W5500 stop failed");
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "Unknown link status");
break;
}
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
{
esp_err_t ret = ESP_OK;
switch (speed)
{
case ETH_SPEED_10M:
ESP_LOGD(TAG, "Setting to 10Mbps");
break;
case ETH_SPEED_100M:
ESP_LOGD(TAG, "Setting to 100Mbps");
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "Unknown speed");
break;
}
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex)
{
esp_err_t ret = ESP_OK;
switch (duplex)
{
case ETH_DUPLEX_HALF:
ESP_LOGD(TAG, "Setting to HALF_DUPLEX");
break;
case ETH_DUPLEX_FULL:
ESP_LOGD(TAG, "Setting to FULL_DUPLEX");
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "Unknown duplex");
break;
}
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_set_promiscuous(esp_eth_mac_t *mac, bool enable)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint8_t smr = 0;
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_MR(0), &smr, sizeof(smr)), err, TAG, "Read SOCK0 MR failed");
if (enable)
{
smr &= ~W5500_SMR_MAC_FILTER;
}
else
{
smr |= W5500_SMR_MAC_FILTER;
}
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_MR(0), &smr, sizeof(smr)), err, TAG, "Write SOCK0 MR failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
{
/* w5500 doesn't support flow control function, so accept any value */
return ESP_ERR_NOT_SUPPORTED;
}
////////////////////////////////////////
static esp_err_t emac_w5500_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
{
/* w5500 doesn't support PAUSE function, so accept any value */
return ESP_ERR_NOT_SUPPORTED;
}
////////////////////////////////////////
static inline bool is_w5500_sane_for_rxtx(emac_w5500_t *emac)
{
uint8_t phycfg;
/* phy is ok for rx and tx operations if bits RST and LNK are set (no link down, no reset) */
if (w5500_read(emac, W5500_REG_PHYCFGR, &phycfg, 1) == ESP_OK && (phycfg & 0x8001))
{
return true;
}
return false;
}
////////////////////////////////////////
static esp_err_t emac_w5500_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint16_t offset = 0;
// check if there're free memory to store this packet
uint16_t free_size = 0;
ESP_GOTO_ON_ERROR(w5500_get_tx_free_size(emac, &free_size), err, TAG, "Get free size failed");
ESP_GOTO_ON_FALSE(length <= free_size, ESP_ERR_NO_MEM, err, TAG, "Free size (%d) < send length (%d)", length,
free_size);
// get current write pointer
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_TX_WR(0), &offset, sizeof(offset)), err, TAG, "Read TX WR failed");
offset = __builtin_bswap16(offset);
// copy data to tx memory
ESP_GOTO_ON_ERROR(w5500_write_buffer(emac, buf, length, offset), err, TAG, "Write frame failed");
// update write pointer
offset += length;
offset = __builtin_bswap16(offset);
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_TX_WR(0), &offset, sizeof(offset)), err, TAG, "Write TX WR failed");
// issue SEND command
ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_SEND, 100), err, TAG, "Issue SEND command failed");
// pooling the TX done event
int retry = 0;
uint8_t status = 0;
while (!(status & W5500_SIR_SEND))
{
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)), err, TAG, "Read SOCK0 IR failed");
if ((retry++ > 3 && !is_w5500_sane_for_rxtx(emac)) || retry > 10)
{
return ESP_FAIL;
}
}
// clear the event bit
status = W5500_SIR_SEND;
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)), err, TAG, "Write SOCK0 IR failed");
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint16_t offset = 0;
uint16_t rx_len = 0;
uint16_t remain_bytes = 0;
emac->packets_remain = false;
w5500_get_rx_received_size(emac, &remain_bytes);
if (remain_bytes)
{
// get current read pointer
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)), err, TAG, "Read RX RD failed");
offset = __builtin_bswap16(offset);
// read head first
ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset), err, TAG, "Read frame header failed");
rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header
offset += 2;
// read the payload
ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, buf, rx_len, offset), err, TAG, "Read payload failed, len=%d, offset=%d",
rx_len, offset);
offset += rx_len;
// update read pointer
offset = __builtin_bswap16(offset);
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)), err, TAG, "Write RX RD failed");
/* issue RECV command */
ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_RECV, 100), err, TAG, "Issue RECV command failed");
// check if there're more data need to process
remain_bytes -= rx_len + 2;
emac->packets_remain = remain_bytes > 0;
}
*length = rx_len;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t emac_w5500_init(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
esp_eth_mediator_t *eth = emac->eth;
esp_rom_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); // active low
gpio_intr_enable(emac->int_gpio_num);
gpio_isr_handler_add(emac->int_gpio_num, w5500_isr_handler, emac);
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "Lowlevel init failed");
/* reset w5500 */
ESP_GOTO_ON_ERROR(w5500_reset(emac), err, TAG, "Reset w5500 failed");
/* verify chip id */
ESP_GOTO_ON_ERROR(w5500_verify_id(emac), err, TAG, "Verify chip ID failed");
/* default setup of internal registers */
ESP_GOTO_ON_ERROR(w5500_setup_default(emac), err, TAG, "W5500 default setup failed");
return ESP_OK;
err:
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_w5500_deinit(esp_eth_mac_t *mac)
{
emac_w5500_t *emac = __containerof(mac, emac_w5500_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_w5500_del(esp_eth_mac_t *mac)
{
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
vTaskDelete(emac->rx_task_hdl);
vSemaphoreDelete(emac->spi_lock);
free(emac);
return ESP_OK;
}
////////////////////////////////////////
esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, const eth_mac_config_t *mac_config)
{
esp_eth_mac_t *ret = NULL;
emac_w5500_t *emac = NULL;
ESP_GOTO_ON_FALSE(w5500_config && mac_config, NULL, err, TAG, "Invalid argument");
emac = calloc(1, sizeof(emac_w5500_t));
ESP_GOTO_ON_FALSE(emac, NULL, err, TAG, "No mem for MAC instance");
/* w5500 driver is interrupt driven */
ESP_GOTO_ON_FALSE(w5500_config->int_gpio_num >= 0, NULL, err, TAG, "Invalid interrupt gpio number");
/* bind methods and attributes */
emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms;
emac->int_gpio_num = w5500_config->int_gpio_num;
emac->spi_hdl = w5500_config->spi_hdl;
emac->parent.set_mediator = emac_w5500_set_mediator;
emac->parent.init = emac_w5500_init;
emac->parent.deinit = emac_w5500_deinit;
emac->parent.start = emac_w5500_start;
emac->parent.stop = emac_w5500_stop;
emac->parent.del = emac_w5500_del;
emac->parent.write_phy_reg = emac_w5500_write_phy_reg;
emac->parent.read_phy_reg = emac_w5500_read_phy_reg;
emac->parent.set_addr = emac_w5500_set_addr;
emac->parent.get_addr = emac_w5500_get_addr;
emac->parent.set_speed = emac_w5500_set_speed;
emac->parent.set_duplex = emac_w5500_set_duplex;
emac->parent.set_link = emac_w5500_set_link;
emac->parent.set_promiscuous = emac_w5500_set_promiscuous;
emac->parent.set_peer_pause_ability = emac_w5500_set_peer_pause_ability;
emac->parent.enable_flow_ctrl = emac_w5500_enable_flow_ctrl;
emac->parent.transmit = emac_w5500_transmit;
emac->parent.receive = emac_w5500_receive;
/* create mutex */
emac->spi_lock = xSemaphoreCreateMutex();
ESP_GOTO_ON_FALSE(emac->spi_lock, NULL, err, TAG, "Create lock failed");
/* create w5500 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_w5500_task, "w5500_tsk", mac_config->rx_task_stack_size, emac,
mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "Create w5500 task failed");
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,379 @@
/****************************************************************************************************************************
esp_eth_phy_w5500.c
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
// Copyright 2020 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_check.h"
#include "esp_eth.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "esp_rom_sys.h"
#include "w5500.h"
////////////////////////////////////////
static const char *TAG = "w5500.phy";
////////////////////////////////////////
/***************Vendor Specific Register***************/
/**
@brief PHYCFGR(PHY Configuration Register)
*/
typedef union
{
struct
{
uint8_t link: 1; /*!< Link status */
uint8_t speed: 1; /*!< Speed status */
uint8_t duplex: 1; /*!< Duplex status */
uint8_t opmode: 3; /*!< Operation mode */
uint8_t opsel: 1; /*!< Operation select */
uint8_t reset: 1; /*!< Reset, when this bit is '0', PHY will get reset */
};
uint8_t val;
} phycfg_reg_t;
////////////////////////////////////////
typedef struct
{
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
int addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_w5500_t;
////////////////////////////////////////
static esp_err_t w5500_update_link_duplex_speed(phy_w5500_t *w5500)
{
esp_err_t ret = ESP_OK;
esp_eth_mediator_t *eth = w5500->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
phycfg_reg_t phycfg;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG,
"Read PHYCFG failed");
eth_link_t link = phycfg.link ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (w5500->link_status != link)
{
/* when link up, read negotiation result */
if (link == ETH_LINK_UP)
{
if (phycfg.speed)
{
speed = ETH_SPEED_100M;
}
else
{
speed = ETH_SPEED_10M;
}
if (phycfg.duplex)
{
duplex = ETH_DUPLEX_FULL;
}
else
{
duplex = ETH_DUPLEX_HALF;
}
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed), err, TAG, "Change speed failed");
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex), err, TAG, "Change duplex failed");
}
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "Change link failed");
w5500->link_status = link;
}
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "Can't set mediator to null");
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
w5500->eth = eth;
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_get_link(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
/* Updata information about link, speed, duplex */
ESP_GOTO_ON_ERROR(w5500_update_link_duplex_speed(w5500), err, TAG, "Update link duplex speed failed");
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_reset(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
w5500->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = w5500->eth;
phycfg_reg_t phycfg;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG,
"Read PHYCFG failed");
phycfg.reset = 0; // set to '0' will reset internal PHY
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "Write PHYCFG failed");
vTaskDelay(pdMS_TO_TICKS(10));
phycfg.reset = 1; // set to '1' after reset
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "Write PHYCFG failed");
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_reset_hw(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
// set reset_gpio_num to a negative value can skip hardware reset phy chip
if (w5500->reset_gpio_num >= 0)
{
esp_rom_gpio_pad_select_gpio(w5500->reset_gpio_num);
gpio_set_direction(w5500->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(w5500->reset_gpio_num, 0);
esp_rom_delay_us(100); // insert min input assert time
gpio_set_level(w5500->reset_gpio_num, 1);
}
return ESP_OK;
}
////////////////////////////////////////
static esp_err_t w5500_negotiate(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
esp_eth_mediator_t *eth = w5500->eth;
/* in case any link status has changed, let's assume we're in link down status */
w5500->link_status = ETH_LINK_DOWN;
phycfg_reg_t phycfg;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG,
"Read PHYCFG failed");
phycfg.opsel = 1; // PHY working mode configured by register
phycfg.opmode = 7; // all capable, auto-negotiation enabled
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "Write PHYCFG failed");
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_pwrctl(esp_eth_phy_t *phy, bool enable)
{
// power control is not supported for W5500 internal PHY
return ESP_OK;
}
////////////////////////////////////////
static esp_err_t w5500_set_addr(esp_eth_phy_t *phy, uint32_t addr)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
w5500->addr = addr;
return ESP_OK;
}
////////////////////////////////////////
static esp_err_t w5500_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "Addr can't be null");
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
*addr = w5500->addr;
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_del(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
free(w5500);
return ESP_OK;
}
////////////////////////////////////////
static esp_err_t w5500_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
// pause ability advertisement is not supported for W5500 internal PHY
return ESP_OK;
}
////////////////////////////////////////
static esp_err_t w5500_loopback(esp_eth_phy_t *phy, bool enable)
{
// Loopback is not supported for W5500 internal PHY
return ESP_ERR_NOT_SUPPORTED;
}
////////////////////////////////////////
static esp_err_t w5500_init(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
/* Power on Ethernet PHY */
ESP_GOTO_ON_ERROR(w5500_pwrctl(phy, true), err, TAG, "Power control failed");
/* Reset Ethernet PHY */
ESP_GOTO_ON_ERROR(w5500_reset(phy), err, TAG, "Reset failed");
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
static esp_err_t w5500_deinit(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
/* Power off Ethernet PHY */
ESP_GOTO_ON_ERROR(w5500_pwrctl(phy, false), err, TAG, "Power control failed");
return ESP_OK;
err:
return ret;
}
////////////////////////////////////////
esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config)
{
esp_eth_phy_t *ret = NULL;
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "Invalid arguments");
phy_w5500_t *w5500 = calloc(1, sizeof(phy_w5500_t));
ESP_GOTO_ON_FALSE(w5500, NULL, err, TAG, "No mem for PHY instance");
/* bind methods and attributes */
w5500->addr = config->phy_addr;
w5500->reset_timeout_ms = config->reset_timeout_ms;
w5500->reset_gpio_num = config->reset_gpio_num;
w5500->link_status = ETH_LINK_DOWN;
w5500->autonego_timeout_ms = config->autonego_timeout_ms;
w5500->parent.reset = w5500_reset;
w5500->parent.reset_hw = w5500_reset_hw;
w5500->parent.init = w5500_init;
w5500->parent.deinit = w5500_deinit;
w5500->parent.set_mediator = w5500_set_mediator;
w5500->parent.negotiate = w5500_negotiate;
w5500->parent.get_link = w5500_get_link;
w5500->parent.pwrctl = w5500_pwrctl;
w5500->parent.get_addr = w5500_get_addr;
w5500->parent.set_addr = w5500_set_addr;
w5500->parent.advertise_pause_ability = w5500_advertise_pause_ability;
w5500->parent.loopback = w5500_loopback;
w5500->parent.del = w5500_del;
return &(w5500->parent);
err:
return ret;
}
////////////////////////////////////////

View File

@ -0,0 +1,110 @@
/****************************************************************************************************************************
esp_eth_spi_w5500.c
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "driver/gpio.h"
#include "esp_eth_w5500.h"
#include "driver/spi_master.h"
#include "esp_log.h"
#include "esp_check.h"
static const char *TAG = "w5500.spi";
////////////////////////////////////////
esp_eth_mac_t* w5500_new_mac( spi_device_handle_t *spi_handle, int INT_GPIO )
{
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG( *spi_handle );
w5500_config.int_gpio_num = INT_GPIO;
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
//eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
//phy_config.reset_gpio_num = -1;
mac_config.smi_mdc_gpio_num = -1; // w5500 doesn't have SMI interface
mac_config.smi_mdio_gpio_num = -1;
mac_config.rx_task_prio = 1;
return esp_eth_mac_new_w5500( &w5500_config, &mac_config );
}
////////////////////////////////////////
esp_eth_mac_t* w5500_begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPICLOCK_MHZ,
int SPIHOST)
{
if (ESP_OK != gpio_install_isr_service(0))
{
ESP_LOGE(TAG, "%s(%d): Error gpio_install_isr_service", __FUNCTION__, __LINE__);
return NULL;
}
/* w5500 ethernet driver is based on spi driver */
spi_bus_config_t buscfg =
{
.miso_io_num = MISO_GPIO,
.mosi_io_num = MOSI_GPIO,
.sclk_io_num = SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
if ( ESP_OK != spi_bus_initialize( SPIHOST, &buscfg, SPI_DMA_CH_AUTO ))
{
ESP_LOGE(TAG, "%s(%d): Error spi_bus_initialize", __FUNCTION__, __LINE__);
return NULL;
}
spi_device_interface_config_t devcfg =
{
.command_bits = 16,
.address_bits = 8,
.mode = 0,
.clock_speed_hz = SPICLOCK_MHZ * 1000 * 1000,
.spics_io_num = CS_GPIO,
.queue_size = 20,
.cs_ena_posttrans = w5500_cal_spi_cs_hold_time(SPICLOCK_MHZ),
};
spi_device_handle_t spi_handle = NULL;
if (ESP_OK != spi_bus_add_device( SPIHOST, &devcfg, &spi_handle ))
{
ESP_LOGE(TAG, "%s(%d): Error spi_bus_add_device", __FUNCTION__, __LINE__);
return NULL;
}
return w5500_new_mac( &spi_handle, INT_GPIO );
}
////////////////////////////////////////

View File

@ -0,0 +1,146 @@
/****************************************************************************************************************************
esp_eth_w5500.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
// Copyright 2021 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_phy.h"
#include "esp_eth_mac.h"
#include "driver/spi_master.h"
////////////////////////////////////////
#define CS_HOLD_TIME_MIN_NS 210
////////////////////////////////////////
/*
// From tools/sdk/esp32/include/esp_eth/include/esp_eth_mac.h
typedef struct
{
void *spi_hdl; //!< Handle of SPI device driver
int int_gpio_num; //!< Interrupt GPIO number
} eth_w5500_config_t;
#define ETH_W5500_DEFAULT_CONFIG(spi_device) \
{ \
.spi_hdl = spi_device, \
.int_gpio_num = 4, \
}
*/
////////////////////////////////////////
/**
@brief Compute amount of SPI bit-cycles the CS should stay active after the transmission
to meet w5500 CS Hold Time specification.
@param clock_speed_mhz SPI Clock frequency in MHz (valid range is <1, 20>)
@return uint8_t
*/
static inline uint8_t w5500_cal_spi_cs_hold_time(int clock_speed_mhz)
{
if (clock_speed_mhz <= 0 || clock_speed_mhz > 20)
{
return 0;
}
int temp = clock_speed_mhz * CS_HOLD_TIME_MIN_NS;
uint8_t cs_posttrans = temp / 1000;
if (temp % 1000)
{
cs_posttrans += 1;
}
return cs_posttrans;
}
////////////////////////////////////////
/**
@brief Create w5500 Ethernet MAC instance
@param[in] w5500_config: w5500 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_w5500(const eth_w5500_config_t *w5500_config,
const eth_mac_config_t *mac_config);
////////////////////////////////////////
/**
@brief Create a PHY instance of w5500
@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_w5500(const eth_phy_config_t *config);
////////////////////////////////////////
// todo: the below functions should be accessed through ioctl in the future
/**
@brief Set w5500 Duplex mode. It sets Duplex mode first to the PHY and then
MAC is set based on what PHY indicates.
@param phy w5500 PHY Handle
@param duplex Duplex mode
@return esp_err_t
- ESP_OK when PHY registers were correctly written.
*/
esp_err_t w5500_set_phy_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex);
////////////////////////////////////////
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,126 @@
/****************************************************************************************************************************
w5500.h
For Ethernet shields using ESP32_W5500 (ESP32 + W5500)
WebServer_ESP32_W5500 is a library for the ESP32 with Ethernet W5500 to run WebServer
Based on and modified from ESP32-IDF https://github.com/espressif/esp-idf
Built by Khoi Hoang https://github.com/khoih-prog/WebServer_ESP32_W5500
Licensed under GPLv3 license
Version: 1.5.3
Version Modified By Date Comments
------- ----------- ---------- -----------
1.5.1 K Hoang 29/11/2022 Initial coding for ESP32_W5500 (ESP32 + W5500). Sync with WebServer_WT32_ETH01 v1.5.1
1.5.2 K Hoang 06/01/2023 Suppress compile error when using aggressive compile settings
1.5.3 K Hoang 11/01/2023 Using `SPI_DMA_CH_AUTO` and built-in ESP32 MAC
*****************************************************************************************************************************/
// Copyright 2020 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.
////////////////////////////////////////
#define W5500_ADDR_OFFSET (16) // Address length
#define W5500_BSB_OFFSET (3) // Block Select Bits offset
#define W5500_RWB_OFFSET (2) // Read Write Bits offset
////////////////////////////////////////
#define W5500_BSB_COM_REG (0x00) // Common Register
#define W5500_BSB_SOCK_REG(s) ((s)*4+1) // Socket Register
#define W5500_BSB_SOCK_TX_BUF(s) ((s)*4+2) // Socket TX Buffer
#define W5500_BSB_SOCK_RX_BUF(s) ((s)*4+3) // Socket RX Buffer
////////////////////////////////////////
#define W5500_ACCESS_MODE_READ (0) // Read Mode
#define W5500_ACCESS_MODE_WRITE (1) // Write Mode
////////////////////////////////////////
#define W5500_SPI_OP_MODE_VDM (0x00) // Variable Data Length Mode (SPI frame is controlled by CS line)
#define W5500_SPI_OP_MODE_FDM_1 (0x01) // Fixed Data Length Mode, 1 Byte Length
#define W5500_SPI_OP_MODE_FDM_2 (0x02) // Fixed Data Length Mode, 2 Bytes Length
#define W5500_SPI_OP_MODE_FDM_4 (0x03) // Fixed Data Length Mode, 4 Bytes Length
////////////////////////////////////////
#define W5500_MAKE_MAP(offset, bsb) ((offset) << W5500_ADDR_OFFSET | (bsb) << W5500_BSB_OFFSET)
////////////////////////////////////////
#define W5500_REG_MR W5500_MAKE_MAP(0x0000, W5500_BSB_COM_REG) // Mode
#define W5500_REG_MAC W5500_MAKE_MAP(0x0009, W5500_BSB_COM_REG) // MAC Address
#define W5500_REG_INTLEVEL W5500_MAKE_MAP(0x0013, W5500_BSB_COM_REG) // Interrupt Level Timeout
#define W5500_REG_IR W5500_MAKE_MAP(0x0015, W5500_BSB_COM_REG) // Interrupt
#define W5500_REG_IMR W5500_MAKE_MAP(0x0016, W5500_BSB_COM_REG) // Interrupt Mask
#define W5500_REG_SIR W5500_MAKE_MAP(0x0017, W5500_BSB_COM_REG) // Socket Interrupt
#define W5500_REG_SIMR W5500_MAKE_MAP(0x0018, W5500_BSB_COM_REG) // Socket Interrupt Mask
#define W5500_REG_RTR W5500_MAKE_MAP(0x0019, W5500_BSB_COM_REG) // Retry Time
#define W5500_REG_RCR W5500_MAKE_MAP(0x001B, W5500_BSB_COM_REG) // Retry Count
#define W5500_REG_PHYCFGR W5500_MAKE_MAP(0x002E, W5500_BSB_COM_REG) // PHY Configuration
#define W5500_REG_VERSIONR W5500_MAKE_MAP(0x0039, W5500_BSB_COM_REG) // Chip version
////////////////////////////////////////
#define W5500_REG_SOCK_MR(s) W5500_MAKE_MAP(0x0000, W5500_BSB_SOCK_REG(s)) // Socket Mode
#define W5500_REG_SOCK_CR(s) W5500_MAKE_MAP(0x0001, W5500_BSB_SOCK_REG(s)) // Socket Command
#define W5500_REG_SOCK_IR(s) W5500_MAKE_MAP(0x0002, W5500_BSB_SOCK_REG(s)) // Socket Interrupt
#define W5500_REG_SOCK_SR(s) W5500_MAKE_MAP(0x0004, W5500_BSB_SOCK_REG(s)) // Socket Status
#define W5500_REG_SOCK_RXBUF_SIZE(s) W5500_MAKE_MAP(0x001E, W5500_BSB_SOCK_REG(s)) // Socket Receive Buffer Size
#define W5500_REG_SOCK_TXBUF_SIZE(s) W5500_MAKE_MAP(0x001F, W5500_BSB_SOCK_REG(s)) // Socket Transmit Buffer Size
#define W5500_REG_SOCK_TX_FSR(s) W5500_MAKE_MAP(0x0020, W5500_BSB_SOCK_REG(s)) // Socket TX Free Size
#define W5500_REG_SOCK_TX_RD(s) W5500_MAKE_MAP(0x0022, W5500_BSB_SOCK_REG(s)) // Socket TX Read Pointer
#define W5500_REG_SOCK_TX_WR(s) W5500_MAKE_MAP(0x0024, W5500_BSB_SOCK_REG(s)) // Socket TX Write Pointer
#define W5500_REG_SOCK_RX_RSR(s) W5500_MAKE_MAP(0x0026, W5500_BSB_SOCK_REG(s)) // Socket RX Received Size
#define W5500_REG_SOCK_RX_RD(s) W5500_MAKE_MAP(0x0028, W5500_BSB_SOCK_REG(s)) // Socket RX Read Pointer
#define W5500_REG_SOCK_RX_WR(s) W5500_MAKE_MAP(0x002A, W5500_BSB_SOCK_REG(s)) // Socket RX Write Pointer
#define W5500_REG_SOCK_IMR(s) W5500_MAKE_MAP(0x002C, W5500_BSB_SOCK_REG(s)) // Socket Interrupt Mask
////////////////////////////////////////
#define W5500_MEM_SOCK_TX(s,addr) W5500_MAKE_MAP(addr, W5500_BSB_SOCK_TX_BUF(s)) // Socket TX buffer address
#define W5500_MEM_SOCK_RX(s,addr) W5500_MAKE_MAP(addr, W5500_BSB_SOCK_RX_BUF(s)) // Socket RX buffer address
////////////////////////////////////////
#define W5500_MR_RST (1<<7) // Software reset
#define W5500_MR_PB (1<<4) // Ping block (block the response to a ping request)
////////////////////////////////////////
#define W5500_SIMR_SOCK0 (1<<0) // Socket 0 interrupt
////////////////////////////////////////
#define W5500_SMR_MAC_RAW (1<<2) // MAC RAW mode
#define W5500_SMR_MAC_FILTER (1<<7) // MAC filter
////////////////////////////////////////
#define W5500_SCR_OPEN (0x01) // Open command
#define W5500_SCR_CLOSE (0x10) // Close command
#define W5500_SCR_SEND (0x20) // Send command
#define W5500_SCR_RECV (0x40) // Recv command
////////////////////////////////////////
#define W5500_SIR_RECV (1<<2) // Receive done
#define W5500_SIR_SEND (1<<4) // Send done
////////////////////////////////////////