Add ENC28J60 Ethernet Driver

Merges https://github.com/espressif/esp-idf/pull/4435
This commit is contained in:
Henry Gilbert
2019-11-27 09:24:05 -07:00
committed by suda-morris
parent 966f4227ad
commit eda07acc81
7 changed files with 1559 additions and 5 deletions

View File

@@ -114,11 +114,17 @@ menu "Ethernet"
if ETH_USE_SPI_ETHERNET
config ETH_SPI_ETHERNET_DM9051
bool "Use DM9051"
default y
help
DM9051 is a fast Ethernet controller with an SPI interface.
It's also integrated with a 10/100M PHY and MAC.
Set true to enable DM9051 driver.
Select to enable DM9051 driver.
config ETH_SPI_ETHERNET_ENC28J60
bool "Use ENC28J60"
help
ENC28J60 is a Stand-Alone Ethernet Controller with SPI Interface.
It has an integrated MAC and 10Base-T PHY.
Select to enable ENC28J60 driver.
endif
menuconfig ETH_USE_OPENETH

View File

@@ -315,6 +315,14 @@ typedef struct {
*/
esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, const eth_mac_config_t *mac_config);
#endif
#if CONFIG_ETH_SPI_ETHERNET_ENC28J60
/**
* @brief ENC28J60 specific configuration
*
*/
typedef struct {
spi_device_handle_t spi_hdl; /*!< Handle of SPI device driver */
} eth_ENC28J60_config_t;
#if CONFIG_ETH_USE_OPENETH
esp_eth_mac_t *esp_eth_mac_new_openeth(const eth_mac_config_t *config);

View File

@@ -252,6 +252,19 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config);
*/
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
#endif
#if CONFIG_ETH_SPI_ETHERNET_ENC28J60
/**
* @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);
#endif
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,250 @@
#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 "esp_err.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)
/**
* @brief PHYIDR1(PHY Identifier Register 1)
*
*/
typedef union {
struct {
uint32_t PHY_ID_MSB : 16; /*!< Organizationally Unique Identifier(OUI) most significant bits */
};
uint32_t val;
} phy_id1_reg_t;
#define ETH_PHYID1_ADDR (0x02)
/**
* @brief PHYIDR2(PHY Identifier Register 2)
*
*/
typedef union {
struct {
uint32_t PHY_ID_LSB : 6; /*!< Model revision number */
uint32_t PHY_PN : 6; /*!< Vendor model number */
uint32_t PHY_rev : 4; /*!< Organizationally Unique Identifier(OUI) least significant bits */
};
uint32_t val;
} phy_id2_reg_t;
#define ETH_PHYID2_ADDR (0x03)
/**
* @brief PHSTAT2(PHY Configuration and Status Register)
*
*/
typedef union {
struct {
uint32_t reserved_4_0 : 5;
uint32_t PLRITY : 1;
uint32_t reserved_8_6 : 3;
uint32_t DPXSTAT : 1;
uint32_t LSTAT : 1;
uint32_t COLSTAT : 1;
uint32_t RXSTAT : 1;
uint32_t TXSTAT : 1;
uint32_t reserved_15_14 : 2;
};
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;
const char *name;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
} phy_ENC28J60_t;
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);
esp_eth_mediator_t *eth = ENC28J60->eth;
bmsr_reg_t PHstat1;
phstat2_reg_t PHstat2;
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHY_BMSR_REG_ADDR, &(PHstat1.val)) == ESP_OK, "read PHSTAT1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHY_PHSTAT2_REG_ADDR, &(PHstat2.val)) == ESP_OK, "read PHSTAT1 failed", err);
eth_link_t link = PHstat2.LSTAT ? ETH_LINK_UP : ETH_LINK_DOWN;
if (ENC28J60->link_status != link) {
if (link == ETH_LINK_UP) {
phy->negotiate(phy);
} else {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
ENC28J60->link_status = link;
}
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t ENC28J60_reset(esp_eth_phy_t *phy)
{
/*ENC28J60 should be reset using the MAC reset function*/
return ESP_OK;
}
static esp_err_t ENC28J60_negotiate(esp_eth_phy_t *phy)
{
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
esp_eth_mediator_t *eth = ENC28J60->eth;
/* Start auto negotiation */
bmcr_reg_t PHCON1 = {
.speed_select = 0, /* 10Mbps */
.duplex_mode = 0, /* Half Duplex */
.en_auto_nego = 0, /* no Auto Negotiation */
.restart_auto_nego = 0 /* dont Restart Auto Negotiation */
};
PHY_CHECK(eth->phy_reg_write(eth, ENC28J60->addr, ETH_PHY_BMCR_REG_ADDR, PHCON1.val) == ESP_OK, "write PHCON1 failed", err);
bmsr_reg_t PHSTAT1;
phstat2_reg_t PHSTAT2;
/* Update information about link, speed, duplex */
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHY_BMSR_REG_ADDR, &(PHSTAT1.val)) == ESP_OK, "read PHSTAT1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, 0x11, &(PHSTAT2.val)) == ESP_OK, "read PHSTAT2 failed", err);
eth_link_t link = PHSTAT2.LSTAT ? ETH_LINK_UP : ETH_LINK_DOWN;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
if (ENC28J60->link_status != link) {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
ENC28J60->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t ENC28J60_pwrctl(esp_eth_phy_t *phy, bool enable)
{
/*ToDo: Add power control function*/
return ESP_OK;
}
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, "get phy address failed", 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 on Ethernet PHY failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(ENC28J60_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
/* Check PHY ID */
phy_id1_reg_t id1;
phy_id2_reg_t id2;
uint32_t id3;
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHYID1_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHYID2_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
id3 = ((id1.PHY_ID_MSB << 16) | id2.PHY_ID_LSB);
PHY_CHECK(id3, "wrong PHY chip ID", 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_CHECK(config->phy_addr == 1, "ENC28J60's phy address can only set to 1", err);
phy_ENC28J60_t *ENC28J60 = calloc(1, sizeof(phy_ENC28J60_t));
PHY_CHECK(ENC28J60, "calloc ENC28J60 object failed", err);
ENC28J60->name = "ENC28J60";
ENC28J60->addr = config->phy_addr;
ENC28J60->reset_timeout_ms = config->reset_timeout_ms;
ENC28J60->link_status = ETH_LINK_DOWN;
ENC28J60->autonego_timeout_ms = config->autonego_timeout_ms;
ENC28J60->parent.reset = ENC28J60_reset;
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

@@ -18,7 +18,14 @@ menu "Example Configuration"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module.
Select external SPI-Ethernet module (DM9051).
config EXAMPLE_USE_ENC28J60
bool "ENC28J60 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_ENC28J60
help
Select external SPI-Ethernet module (ENC28J60).
endchoice
if EXAMPLE_USE_INTERNAL_ETHERNET
@@ -72,7 +79,7 @@ menu "Example Configuration"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
Set the SPI host used to communicate with the SPI Ethernet Controller.
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
@@ -104,7 +111,7 @@ menu "Example Configuration"
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
range 5 80
default 20
help
Set the clock speed (MHz) of SPI interface.

View File

@@ -119,6 +119,20 @@ void app_main(void)
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);
#elif CONFIG_EXAMPLE_ETH_PHY_ENC28J60
/* 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_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
eth_ENC28J60_config_t ENC28J60_config = ETH_ENC28J60_DEFAULT_CONFIG(spi_handle);
esp_eth_mac_t *mac = esp_eth_mac_new_ENC28J60(&ENC28J60_config, &mac_config);
esp_eth_phy_t *phy = esp_eth_phy_new_ENC28J60(&phy_config);
#endif
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;