ethernet: support esp_eth_deinit

1. change static emac-dma memory to dynamic
2. add esp_eth_deinit
3. modify ethernet example
4. add testcase for ethernet deinit function
5. GPIO0 could not output the 50MHz clock to PHY, so remove this configuration
This commit is contained in:
morris 2018-08-22 17:46:14 +08:00
parent cc8ad721f9
commit ec07112f5b
9 changed files with 431 additions and 229 deletions

View file

@ -35,6 +35,14 @@ config EMAC_L2_TO_L3_RX_BUF_MODE
If unsure, choose n.
config EMAC_CHECK_LINK_PERIOD_MS
int "Period(ms) of checking Ethernet linkup status"
range 1000 5000
default 2000
help
The emac driver uses an internal timer to check the ethernet linkup
status. Here you should choose a valid the interval time.
config EMAC_TASK_PRIORITY
int "EMAC_TASK_PRIORITY"
default 20

View file

@ -31,13 +31,13 @@ struct dma_desc {
uint32_t desc3;
};
struct dma_extended_desc {
typedef struct dma_extended_desc {
struct dma_desc basic;
uint32_t desc4;
uint32_t desc5;
uint32_t desc6;
uint32_t desc7;
};
}dma_extended_desc_t;
void emac_enable_clk(bool enable);
void emac_reset(void);

View file

@ -13,6 +13,7 @@
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rom/ets_sys.h"
@ -58,15 +59,15 @@
static struct emac_config_data emac_config;
static uint8_t emac_dma_rx_chain_buf[sizeof(struct dma_extended_desc) * DMA_RX_BUF_NUM];
static uint8_t emac_dma_tx_chain_buf[sizeof(struct dma_extended_desc) * DMA_TX_BUF_NUM];
static uint8_t emac_dma_rx_buf[DMA_RX_BUF_SIZE * DMA_RX_BUF_NUM];
static uint8_t emac_dma_tx_buf[DMA_TX_BUF_SIZE * DMA_TX_BUF_NUM];
static dma_extended_desc_t *emac_dma_rx_chain_buf[DMA_RX_BUF_NUM];
static dma_extended_desc_t *emac_dma_tx_chain_buf[DMA_TX_BUF_NUM];
static uint8_t *emac_dma_rx_buf[DMA_RX_BUF_NUM];
static uint8_t *emac_dma_tx_buf[DMA_TX_BUF_NUM];
static SemaphoreHandle_t emac_g_sem;
static SemaphoreHandle_t emac_g_sem = NULL;
static portMUX_TYPE g_emac_mux = portMUX_INITIALIZER_UNLOCKED;
static xTaskHandle emac_task_hdl;
static xQueueHandle emac_xqueue;
static xTaskHandle emac_task_hdl = NULL;
static xQueueHandle emac_xqueue = NULL;
static uint8_t emac_sig_cnt[EMAC_SIG_MAX] = {0};
static TimerHandle_t emac_timer = NULL;
static SemaphoreHandle_t emac_rx_xMutex = NULL;
@ -92,19 +93,23 @@ void esp_eth_get_mac(uint8_t mac[6])
esp_err_t esp_eth_set_mac(const uint8_t mac[6])
{
if((mac[0] & 0x01) == 0) {
memcpy(&(emac_config.macaddr[0]),mac, 6);
if ((mac[0] & 0x01) == 0) {
memcpy(&(emac_config.macaddr[0]), mac, 6);
return ESP_OK;
} else {
return ESP_ERR_INVALID_MAC;
}
}
static void emac_setup_tx_desc(struct dma_extended_desc *tx_desc , uint32_t size)
static void emac_setup_tx_desc(struct dma_extended_desc *tx_desc, uint32_t size)
{
tx_desc->basic.desc1 = size & 0xfff;
tx_desc->basic.desc0 = EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN;
tx_desc->basic.desc0 = EMAC_DESC_TX_OWN | EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN;
tx_desc->basic.desc0 = EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT |
EMAC_DESC_FIRST_SEGMENT |
EMAC_DESC_SECOND_ADDR_CHAIN;
tx_desc->basic.desc0 = EMAC_DESC_TX_OWN | EMAC_DESC_INT_COMPL |
EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT |
EMAC_DESC_SECOND_ADDR_CHAIN;
}
static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc)
@ -113,7 +118,8 @@ static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc)
tx_desc->basic.desc0 = 0;
}
static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc , uint32_t buf_ptr)
static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc,
uint32_t buf_ptr)
{
if (buf_ptr != 0) {
rx_desc->basic.desc2 = buf_ptr;
@ -133,23 +139,34 @@ static void emac_set_rx_base_reg(void)
}
/*
* dirty_rx indicates the hardware has been fed with data packets and is the first node software needs to handle;
* dirty_rx indicates the hardware has been fed with data packets and is the
* first node software needs to handle;
*
* cur_rx indicates the completion of software handling and is the last node hardware could use;
* cur_rx indicates the completion of software handling and is the last node
* hardware could use;
*
* cnt_rx is to count the numbers of packets handled by software, passed to protocol stack and not been freed.
* cnt_rx is to count the numbers of packets handled by software, passed to
* protocol stack and not been freed.
*
* (1) Initializing the Linked List. Connect the numerable nodes to a circular linked list, appoint one of the nodes as the head node, mark* the dirty_rx and cur_rx into the node, and mount the node on the hardware base address. Initialize cnt_rx into 0.
* (1) Initializing the Linked List. Connect the numerable nodes to a circular
* linked list, appoint one of the nodes as the head node, mark* the dirty_rx
* and cur_rx into the node, and mount the node on the hardware base address.
* Initialize cnt_rx into 0.
*
* (2) When hardware receives packets, nodes of linked lists will be fed with data packets from the base address by turns, marks the node
* (2) When hardware receives packets, nodes of linked lists will be fed with
* data packets from the base address by turns, marks the node
* of linked lists as HARDWARE UNUSABLE and reports interrupts.
*
* (3) When the software receives the interrupts, it will handle the linked lists by turns from dirty_rx, send data packets to protocol
* (3) When the software receives the interrupts, it will handle the linked
* lists by turns from dirty_rx, send data packets to protocol
* stack. dirty_rx will deviate backwards by turns and cnt_rx will by turns ++.
*
* (4) After the protocol stack handles all the data and calls the free function, it will deviate backwards by turns from cur_rx, mark the * node of linked lists as HARDWARE USABLE and cnt_rx will by turns --.
* (4) After the protocol stack handles all the data and calls the free function,
* it will deviate backwards by turns from cur_rx, mark the * node of linked
* lists as HARDWARE USABLE and cnt_rx will by turns --.
*
* (5) Cycle from Step 2 to Step 4 without break and build up circular linked list handling.
* (5) Cycle from Step 2 to Step 4 without break and build up circular linked
* list handling.
*/
static void emac_reset_dma_chain(void)
{
@ -165,48 +182,46 @@ static void emac_reset_dma_chain(void)
static void emac_init_dma_chain(void)
{
int i;
uint32_t dma_phy;
struct dma_extended_desc *p = NULL;
dma_extended_desc_t *p = NULL;
//init tx chain
emac_config.dma_etx = (struct dma_extended_desc *)(&emac_dma_tx_chain_buf[0]);
emac_config.dma_etx = emac_dma_tx_chain_buf[0];
emac_config.cnt_tx = 0;
emac_config.cur_tx = 0;
emac_config.dirty_tx = 0;
dma_phy = (uint32_t)(emac_config.dma_etx);
p = emac_config.dma_etx;
for (i = 0; i < (DMA_TX_BUF_NUM - 1); i++ ) {
dma_phy += sizeof(struct dma_extended_desc);
p = emac_dma_tx_chain_buf[0];
for (i = 0; i < (DMA_TX_BUF_NUM - 1); i++) {
emac_clean_tx_desc(p);
p->basic.desc3 = dma_phy;
p->basic.desc2 = (uint32_t)(&emac_dma_tx_buf[0]) + (i * DMA_TX_BUF_SIZE);
p++;
/* point to the buffer */
p->basic.desc2 = (uint32_t)(emac_dma_tx_buf[i]);
/* point to next descriptor */
p->basic.desc3 = (uint32_t)(emac_dma_tx_chain_buf[i + 1]);
p = emac_dma_tx_chain_buf[i + 1];
}
p->basic.desc3 = (uint32_t)(emac_config.dma_etx);
p->basic.desc2 = (uint32_t)(&emac_dma_tx_buf[0]) + (i * DMA_TX_BUF_SIZE);
//init desc0 desc1
emac_clean_tx_desc(p);
/* point to the buffer */
p->basic.desc2 = (uint32_t)(emac_dma_tx_buf[i]);
/* point to first descriptor */
p->basic.desc3 = (uint32_t)(emac_config.dma_etx);
//init rx chain
emac_config.dma_erx = (struct dma_extended_desc *)(&emac_dma_rx_chain_buf[0]);
emac_config.dma_erx = emac_dma_rx_chain_buf[0];
emac_config.cnt_rx = 0;
emac_config.cur_rx = 0;
emac_config.dirty_rx = 0;
dma_phy = (uint32_t)(emac_config.dma_erx);
p = emac_config.dma_erx;
for (i = 0; i < (DMA_RX_BUF_NUM - 1); i++ ) {
dma_phy += sizeof(struct dma_extended_desc);
emac_clean_rx_desc(p, (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE));
p->basic.desc3 = dma_phy;
p++;
p = emac_dma_rx_chain_buf[0];
for (i = 0; i < (DMA_RX_BUF_NUM - 1); i++) {
emac_clean_rx_desc(p, (uint32_t)(emac_dma_rx_buf[i]));
/* point to the buffer */
p->basic.desc3 = (uint32_t)(emac_dma_rx_chain_buf[i + 1]);
/* point to next descriptor */
p = emac_dma_rx_chain_buf[i + 1];
}
emac_clean_rx_desc(p, (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE));
/* point to the buffer */
emac_clean_rx_desc(p, (uint32_t)(emac_dma_rx_buf[i]));
/* point to first descriptor */
p->basic.desc3 = (uint32_t)(emac_config.dma_erx);
}
@ -214,13 +229,14 @@ void esp_eth_smi_write(uint32_t reg_num, uint16_t value)
{
uint32_t phy_num = emac_config.phy_addr;
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) {
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) {
}
REG_WRITE(EMAC_MIIDATA_REG, value);
REG_WRITE(EMAC_GMIIADDR_REG, 0x3 | ((reg_num & 0x1f) << 6) | ((phy_num & 0x1f) << 11) | ((0x3) << 2));
REG_WRITE(EMAC_GMIIADDR_REG, 0x3 | ((reg_num & 0x1f) << 6) |
((phy_num & 0x1f) << 11) | ((0x3) << 2));
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) {
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) {
}
}
@ -229,21 +245,24 @@ uint16_t esp_eth_smi_read(uint32_t reg_num)
uint32_t phy_num = emac_config.phy_addr;
uint16_t value = 0;
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) {
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) {
}
REG_WRITE(EMAC_GMIIADDR_REG, 0x1 | ((reg_num & 0x1f) << 6) | ((phy_num & 0x1f) << 11) | (0x3 << 2));
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) {
REG_WRITE(EMAC_GMIIADDR_REG, 0x1 | ((reg_num & 0x1f) << 6) |
((phy_num & 0x1f) << 11) | (0x3 << 2));
while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) {
}
value = (REG_READ(EMAC_MIIDATA_REG) & 0xffff);
return value;
}
esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t value_mask, int timeout_ms)
esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value,
uint16_t value_mask, int timeout_ms)
{
unsigned start = xTaskGetTickCount();
unsigned timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS;
unsigned timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) /
portTICK_PERIOD_MS;
uint16_t current_value = 0;
while (timeout_ticks == 0 || (xTaskGetTickCount() - start < timeout_ticks)) {
@ -253,13 +272,12 @@ esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t valu
}
vTaskDelay(1);
}
ESP_LOGE(TAG, "Timed out waiting for PHY register 0x%x to have value 0x%04x (mask 0x%04x). Current value 0x%04x",
ESP_LOGE(TAG, "Timed out waiting for PHY register 0x%x to have value 0x%04x(mask 0x%04x). Current value 0x%04x",
reg_num, value, value_mask, current_value);
return ESP_ERR_TIMEOUT;
}
static void emac_set_user_config_data(eth_config_t *config )
static void emac_set_user_config_data(eth_config_t *config)
{
emac_config.phy_addr = config->phy_addr;
emac_config.mac_mode = config->mac_mode;
@ -274,12 +292,13 @@ static void emac_set_user_config_data(eth_config_t *config )
#if DMA_RX_BUF_NUM > 9
emac_config.emac_flow_ctrl_enable = config->flow_ctrl_enable;
#else
if(config->flow_ctrl_enable == true) {
if (config->flow_ctrl_enable == true) {
ESP_LOGE(TAG, "eth flow ctrl init err!!! Please run make menuconfig and make sure DMA_RX_BUF_NUM > 9 .");
}
emac_config.emac_flow_ctrl_enable = false;
#endif
emac_config.emac_phy_get_partner_pause_enable = config->phy_get_partner_pause_enable;
emac_config.emac_phy_get_partner_pause_enable =
config->phy_get_partner_pause_enable;
emac_config.emac_phy_power_enable = config->phy_power_enable;
}
@ -347,12 +366,13 @@ static esp_err_t emac_verify_args(void)
ret = ESP_FAIL;
}
if (emac_config.emac_flow_ctrl_enable == true && emac_config.emac_phy_get_partner_pause_enable == NULL) {
if (emac_config.emac_flow_ctrl_enable == true &&
emac_config.emac_phy_get_partner_pause_enable == NULL) {
ESP_LOGE(TAG, "phy get partner pause enable func is null");
ret = ESP_FAIL;
}
if(emac_config.emac_phy_power_enable == NULL) {
if (emac_config.emac_phy_power_enable == NULL) {
ESP_LOGE(TAG, "phy power enable func is null");
ret = ESP_FAIL;
}
@ -368,12 +388,12 @@ static void emac_process_tx(void)
return;
}
xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY );
xSemaphoreTakeRecursive(emac_tx_xMutex, portMAX_DELAY);
while (((uint32_t) & (emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) {
emac_clean_tx_desc(&(emac_config.dma_etx[emac_config.dirty_tx]));
while ((uint32_t)(emac_dma_tx_chain_buf[emac_config.dirty_tx]) != cur_tx_desc) {
emac_clean_tx_desc(emac_dma_tx_chain_buf[emac_config.dirty_tx]);
emac_config.dirty_tx = (emac_config.dirty_tx + 1) % DMA_TX_BUF_NUM;
emac_config.cnt_tx --;
emac_config.cnt_tx--;
if (emac_config.cnt_tx < 0) {
ESP_LOGE(TAG, "emac tx chain err");
@ -381,14 +401,14 @@ static void emac_process_tx(void)
cur_tx_desc = emac_read_tx_cur_reg();
}
xSemaphoreGiveRecursive( emac_tx_xMutex );
xSemaphoreGiveRecursive(emac_tx_xMutex);
}
void esp_eth_free_rx_buf(void *buf)
{
xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY );
xSemaphoreTakeRecursive(emac_rx_xMutex, portMAX_DELAY);
emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]), (uint32_t) buf);
emac_clean_rx_desc(emac_dma_rx_chain_buf[emac_config.cur_rx], (uint32_t)buf);
emac_config.cur_rx = (emac_config.cur_rx + 1) % DMA_RX_BUF_NUM;
emac_config.cnt_rx--;
if (emac_config.cnt_rx < 0) {
@ -396,11 +416,12 @@ void esp_eth_free_rx_buf(void *buf)
}
emac_poll_rx_cmd();
xSemaphoreGiveRecursive( emac_rx_xMutex );
xSemaphoreGiveRecursive(emac_rx_xMutex);
if (emac_config.emac_flow_ctrl_partner_support == true) {
portENTER_CRITICAL(&g_emac_mux);
if (pause_send == true && emac_config.cnt_rx < FLOW_CONTROL_LOW_WATERMARK) {
if (pause_send == true && emac_config.cnt_rx <
FLOW_CONTROL_LOW_WATERMARK) {
emac_send_pause_zero_frame_enable();
pause_send = false;
}
@ -412,7 +433,7 @@ static uint32_t IRAM_ATTR emac_get_rxbuf_count_in_intr(void)
{
uint32_t cnt = 0;
uint32_t cur_rx_desc = emac_read_rx_cur_reg();
struct dma_extended_desc *cur_desc = (struct dma_extended_desc *)cur_rx_desc;
struct dma_extended_desc *cur_desc = (dma_extended_desc_t *)cur_rx_desc;
while (cur_desc->basic.desc0 == EMAC_DESC_RX_OWN && cnt < DMA_RX_BUF_NUM) {
cnt++;
@ -429,12 +450,16 @@ static void emac_process_rx(void)
}
uint32_t cur_rx_desc = emac_read_rx_cur_reg();
while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) {
while (((uint32_t)(emac_dma_rx_chain_buf[emac_config.dirty_rx]) != cur_rx_desc)) {
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2),
(((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
emac_config.emac_tcpip_input((emac_dma_rx_buf[emac_config.dirty_rx]),
(((emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0) >>
EMAC_DESC_FRAME_LENGTH_S) &
EMAC_DESC_FRAME_LENGTH),
NULL);
emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2));
emac_clean_rx_desc(emac_dma_rx_chain_buf[emac_config.dirty_rx],
(uint32_t)(emac_dma_rx_buf[emac_config.dirty_rx]));
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
//if open this ,one intr can do many intrs ?
@ -453,16 +478,20 @@ static void emac_process_rx_unavail(void)
uint32_t dirty_cnt = 0;
while (dirty_cnt < DMA_RX_BUF_NUM) {
if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) {
if (emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 == EMAC_DESC_RX_OWN) {
break;
}
dirty_cnt ++;
dirty_cnt++;
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2),
(((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
emac_config.emac_tcpip_input((emac_dma_rx_buf[emac_config.dirty_rx]),
(((emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0) >>
EMAC_DESC_FRAME_LENGTH_S) &
EMAC_DESC_FRAME_LENGTH),
NULL);
emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2));
emac_clean_rx_desc(emac_dma_rx_chain_buf[emac_config.dirty_rx],
(uint32_t)(emac_dma_rx_buf[emac_config.dirty_rx]));
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
}
emac_enable_rx_intr();
@ -477,11 +506,11 @@ static void emac_process_rx_unavail(void)
return;
}
xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY );
xSemaphoreTakeRecursive(emac_rx_xMutex, portMAX_DELAY);
while (emac_config.cnt_rx < DMA_RX_BUF_NUM) {
if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) {
if (emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 == EMAC_DESC_RX_OWN) {
break;
}
@ -493,13 +522,15 @@ static void emac_process_rx_unavail(void)
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2),
(((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
}
emac_config.emac_tcpip_input((emac_dma_rx_buf[tmp_dirty]),
(((emac_dma_rx_chain_buf[tmp_dirty]->basic.desc0) >>
EMAC_DESC_FRAME_LENGTH_S) &
EMAC_DESC_FRAME_LENGTH),
NULL);
}
emac_enable_rx_intr();
emac_enable_rx_unavail_intr();
xSemaphoreGiveRecursive( emac_rx_xMutex );
xSemaphoreGiveRecursive(emac_rx_xMutex);
}
static void emac_process_rx(void)
@ -510,31 +541,36 @@ static void emac_process_rx(void)
uint32_t cur_rx_desc = emac_read_rx_cur_reg();
xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY );
xSemaphoreTakeRecursive(emac_rx_xMutex, portMAX_DELAY);
if (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) {
if (((uint32_t)(emac_dma_rx_chain_buf[emac_config.dirty_rx])) !=
cur_rx_desc) {
while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) {
while (((uint32_t)(emac_dma_rx_chain_buf[emac_config.dirty_rx]) != cur_rx_desc) &&
emac_config.cnt_rx < DMA_RX_BUF_NUM) {
emac_config.cnt_rx++;
if (emac_config.cnt_rx > DMA_RX_BUF_NUM ) {
if (emac_config.cnt_rx > DMA_RX_BUF_NUM) {
ESP_LOGE(TAG, "emac rx buf err!!\n");
}
uint32_t tmp_dirty = emac_config.dirty_rx;
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2),
(((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
emac_config.emac_tcpip_input((emac_dma_rx_buf[tmp_dirty]),
(((emac_dma_rx_chain_buf[tmp_dirty]->basic.desc0) >>
EMAC_DESC_FRAME_LENGTH_S) &
EMAC_DESC_FRAME_LENGTH),
NULL);
cur_rx_desc = emac_read_rx_cur_reg();
}
} else {
if (emac_config.cnt_rx < DMA_RX_BUF_NUM) {
if ((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) {
if ((emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 &
EMAC_DESC_RX_OWN) == 0) {
while (emac_config.cnt_rx < DMA_RX_BUF_NUM) {
if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) {
if (emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 == EMAC_DESC_RX_OWN) {
break;
}
@ -543,18 +579,21 @@ static void emac_process_rx(void)
ESP_LOGE(TAG, "emac rx buf err!!!\n");
}
uint32_t tmp_dirty = emac_config.dirty_rx;
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
emac_config.dirty_rx = (emac_config.dirty_rx + 1) %
DMA_RX_BUF_NUM;
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2),
(((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
}
emac_config.emac_tcpip_input((emac_dma_rx_buf[tmp_dirty]),
(((emac_dma_rx_chain_buf[tmp_dirty]->basic.desc0) >>
EMAC_DESC_FRAME_LENGTH_S) &
EMAC_DESC_FRAME_LENGTH),
NULL);
}
}
}
}
emac_enable_rx_intr();
xSemaphoreGiveRecursive( emac_rx_xMutex );
xSemaphoreGiveRecursive(emac_rx_xMutex);
}
#endif
@ -570,7 +609,8 @@ static void IRAM_ATTR emac_process_intr(void *arg)
if (event & EMAC_RECV_INT) {
emac_disable_rx_intr();
if (emac_config.emac_flow_ctrl_partner_support == true) {
if (emac_get_rxbuf_count_in_intr() < FLOW_CONTROL_HIGH_WATERMARK && pause_send == false ) {
if (emac_get_rxbuf_count_in_intr() < FLOW_CONTROL_HIGH_WATERMARK &&
pause_send == false) {
pause_send = true;
emac_send_pause_frame_enable();
}
@ -591,7 +631,9 @@ static void IRAM_ATTR emac_process_intr(void *arg)
static void emac_set_macaddr_reg(void)
{
REG_SET_FIELD(EMAC_ADDR0HIGH_REG, EMAC_ADDRESS0_HI, (emac_config.macaddr[0] << 8) | (emac_config.macaddr[1]));
REG_WRITE(EMAC_ADDR0LOW_REG, (emac_config.macaddr[2] << 24) | (emac_config.macaddr[3] << 16) | (emac_config.macaddr[4] << 8) | (emac_config.macaddr[5]));
REG_WRITE(EMAC_ADDR0LOW_REG, (emac_config.macaddr[2] << 24) |
(emac_config.macaddr[3] << 16) | (emac_config.macaddr[4] << 8) |
(emac_config.macaddr[5]));
}
static void emac_check_phy_init(void)
@ -612,7 +654,8 @@ static void emac_check_phy_init(void)
emac_config.emac_flow_ctrl_partner_support = false;
#else
if (emac_config.emac_flow_ctrl_enable == true) {
if (emac_config.emac_phy_get_partner_pause_enable() == true && emac_config.emac_phy_get_duplex_mode() == ETH_MODE_FULLDUPLEX) {
if (emac_config.emac_phy_get_partner_pause_enable() == true &&
emac_config.emac_phy_get_duplex_mode() == ETH_MODE_FULLDUPLEX) {
emac_enable_flowctrl();
emac_config.emac_flow_ctrl_partner_support = true;
} else {
@ -635,7 +678,7 @@ static void emac_process_link_updown(bool link_status)
if (link_status == true) {
emac_check_phy_init();
ESP_LOGI(TAG, "eth link_up!!!");
ESP_LOGD(TAG, "eth link_up");
emac_enable_dma_tx();
emac_enable_dma_rx();
for (i = 0; i < PHY_LINK_CHECK_NUM; i++) {
@ -644,7 +687,7 @@ static void emac_process_link_updown(bool link_status)
evt.event_id = SYSTEM_EVENT_ETH_CONNECTED;
} else {
ESP_LOGI(TAG, "eth link_down!!!");
ESP_LOGD(TAG, "eth link_down");
emac_disable_dma_tx();
emac_disable_dma_rx();
evt.event_id = SYSTEM_EVENT_ETH_DISCONNECTED;
@ -667,31 +710,32 @@ esp_err_t esp_eth_tx(uint8_t *buf, uint16_t size)
{
esp_err_t ret = ESP_OK;
if (emac_config.emac_status != EMAC_RUNTIME_START || emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) {
ESP_LOGI(TAG, "tx netif close");
if (emac_config.emac_status != EMAC_RUNTIME_START) {
ESP_LOGE(TAG, "tx netif is not ready, emac_status=%d",
emac_config.emac_status);
ret = ERR_IF;
return ret;
}
xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY );
xSemaphoreTakeRecursive(emac_tx_xMutex, portMAX_DELAY);
if (emac_config.cnt_tx == DMA_TX_BUF_NUM - 1) {
ESP_LOGD(TAG, "tx buf full");
ret = ERR_MEM;
goto _exit;
}
memcpy((uint8_t *)(emac_config.dma_etx[emac_config.cur_tx].basic.desc2), (uint8_t *)buf, size);
memcpy(emac_dma_tx_buf[emac_config.cur_tx], buf, size);
emac_setup_tx_desc(&(emac_config.dma_etx[emac_config.cur_tx]), size);
emac_setup_tx_desc(emac_dma_tx_chain_buf[emac_config.cur_tx], size);
emac_config.cnt_tx ++;
emac_config.cur_tx = (emac_config.cur_tx + 1) % DMA_TX_BUF_NUM ;
emac_config.cnt_tx++;
emac_config.cur_tx = (emac_config.cur_tx + 1) % DMA_TX_BUF_NUM;
emac_poll_tx_cmd();
_exit:
xSemaphoreGiveRecursive( emac_tx_xMutex );
xSemaphoreGiveRecursive(emac_tx_xMutex);
return ret;
}
@ -702,17 +746,16 @@ static void emac_init_default_data(void)
void emac_process_link_check(void)
{
if (emac_config.emac_status != EMAC_RUNTIME_START ||
emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) {
if (emac_config.emac_status != EMAC_RUNTIME_START) {
return;
}
if (emac_config.emac_phy_check_link() == true ) {
if (emac_config.phy_link_up == false) {
if (emac_config.emac_phy_check_link()) {
if (!emac_config.phy_link_up) {
emac_process_link_updown(true);
}
} else {
if (emac_config.phy_link_up == true) {
if (emac_config.phy_link_up) {
emac_process_link_updown(false);
}
}
@ -725,8 +768,12 @@ void emac_link_check_func(void *pv_parameters)
static bool emac_link_check_timer_init(void)
{
emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_PERIOD_MS),
pdTRUE, (void *)rand(), emac_link_check_func);
emac_timer = xTimerCreate("emac_timer",
(CONFIG_EMAC_CHECK_LINK_PERIOD_MS /
portTICK_PERIOD_MS),
pdTRUE,
NULL,
emac_link_check_func);
if (emac_timer == NULL) {
return false;
} else {
@ -761,10 +808,10 @@ static bool emac_link_check_timer_delete(void)
static void emac_start(void *param)
{
struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param;
struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param;
struct emac_open_cmd *cmd = (struct emac_open_cmd *)(post_cmd->cmd);
ESP_LOGI(TAG , "emac start !!!\n");
ESP_LOGD(TAG, "emac start");
cmd->err = EMAC_CMD_OK;
emac_enable_clk(true);
@ -803,7 +850,7 @@ static void emac_start(void *param)
xSemaphoreGive(emac_g_sem);
}
ESP_LOGI(TAG, "emac start success !!!");
ESP_LOGD(TAG, "emac start success");
}
esp_err_t esp_eth_enable(void)
@ -849,8 +896,8 @@ cleanup:
static void emac_stop(void *param)
{
struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param;
ESP_LOGI(TAG, "emac stop");
struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param;
ESP_LOGD(TAG, "emac stop");
emac_link_check_timer_stop();
emac_link_check_timer_delete();
@ -871,7 +918,7 @@ static void emac_stop(void *param)
xSemaphoreGive(emac_g_sem);
}
ESP_LOGI(TAG, "emac stop success !!!");
ESP_LOGD(TAG, "emac stop success");
}
esp_err_t esp_eth_disable(void)
@ -906,7 +953,7 @@ esp_err_t esp_eth_disable(void)
static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par)
{
esp_err_t ret = ESP_OK;
struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)par;
struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)par;
xTaskHandle task_hdl = xTaskGetCurrentTaskHandle();
if (emac_task_hdl != task_hdl) {
@ -948,7 +995,7 @@ void emac_task(void *pv)
emac_event_t e;
for (;;) {
if (xQueueReceive(emac_xqueue, &e, (portTickType)portMAX_DELAY) == pdTRUE) {
if (xQueueReceive(emac_xqueue, &e, portMAX_DELAY) == pdTRUE) {
portENTER_CRITICAL(&g_emac_mux);
emac_sig_cnt[e.sig]--;
portEXIT_CRITICAL(&g_emac_mux);
@ -1020,8 +1067,18 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par)
esp_err_t esp_eth_init(eth_config_t *config)
{
esp_event_set_default_eth_handlers();
return esp_eth_init_internal(config);
/* dynamically alloc memory for ethernet dma */
for (int i = 0; i < DMA_RX_BUF_NUM; i++) {
emac_dma_rx_chain_buf[i] = (struct dma_extended_desc *)heap_caps_malloc(sizeof(struct dma_extended_desc), MALLOC_CAP_DMA);
emac_dma_rx_buf[i] = (uint8_t *)heap_caps_malloc(DMA_RX_BUF_SIZE, MALLOC_CAP_DMA);
}
for (int i = 0; i < DMA_TX_BUF_NUM; i++) {
emac_dma_tx_chain_buf[i] = (struct dma_extended_desc *)heap_caps_malloc(sizeof(struct dma_extended_desc), MALLOC_CAP_DMA);
emac_dma_tx_buf[i] = (uint8_t *)heap_caps_malloc(DMA_TX_BUF_SIZE, MALLOC_CAP_DMA);
}
esp_event_set_default_eth_handlers();
return esp_eth_init_internal(config);
}
esp_err_t esp_eth_init_internal(eth_config_t *config)
@ -1033,7 +1090,7 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
emac_init_default_data();
if (config != NULL ) {
if (config != NULL) {
emac_set_user_config_data(config);
}
@ -1051,31 +1108,28 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
if (emac_config.clock_mode != ETH_CLOCK_GPIO0_IN) {
// 50 MHz = 40MHz * (6 + 4) / (2 * (2 + 2) = 400MHz / 8
rtc_clk_apll_enable(1, 0, 0, 6, 2);
// the next to values have to be set AFTER "periph_module_enable" is called
REG_SET_FIELD(EMAC_EX_CLKOUT_CONF_REG, EMAC_EX_CLK_OUT_H_DIV_NUM, 0);
REG_SET_FIELD(EMAC_EX_CLKOUT_CONF_REG, EMAC_EX_CLK_OUT_DIV_NUM, 0);
if (emac_config.clock_mode == ETH_CLOCK_GPIO0_OUT) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 6);
ESP_LOGD(TAG, "EMAC 50MHz clock output on GPIO0");
} else if (emac_config.clock_mode == ETH_CLOCK_GPIO16_OUT) {
if (emac_config.clock_mode == ETH_CLOCK_GPIO16_OUT) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_EMAC_CLK_OUT);
ESP_LOGD(TAG, "EMAC 50MHz clock output on GPIO16");
} else if (emac_config.clock_mode == ETH_CLOCK_GPIO17_OUT) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_EMAC_CLK_OUT_180);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U,
FUNC_GPIO17_EMAC_CLK_OUT_180);
ESP_LOGD(TAG, "EMAC 50MHz inverted clock output on GPIO17");
}
}
emac_enable_clk(true);
REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL, EMAC_EX_PHY_INTF_RMII);
REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL,
EMAC_EX_PHY_INTF_RMII);
emac_dma_init();
if (emac_config.clock_mode == ETH_CLOCK_GPIO0_IN) {
// external clock on GPIO0
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN);
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN);
REG_SET_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL);
ESP_LOGD(TAG, "External clock input 50MHz on GPIO0");
if (emac_config.mac_mode == ETH_MODE_MII) {
@ -1084,8 +1138,8 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
}
} else {
// internal clock by APLL
REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN);
REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN);
REG_CLR_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL);
}
@ -1101,7 +1155,8 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
emac_rx_xMutex = xSemaphoreCreateRecursiveMutex();
emac_tx_xMutex = xSemaphoreCreateRecursiveMutex();
emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t));
xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl);
xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY,
&emac_task_hdl);
emac_enable_clk(false);
esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL);
@ -1111,3 +1166,43 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
_exit:
return ret;
}
esp_err_t esp_eth_deinit(void)
{
esp_err_t ret = ESP_FAIL;
if (!emac_task_hdl) {
ret = ESP_ERR_INVALID_STATE;
goto _exit;
}
vTaskDelete(emac_task_hdl);
emac_task_hdl = NULL;
vQueueDelete(emac_xqueue);
vSemaphoreDelete(emac_tx_xMutex);
vSemaphoreDelete(emac_rx_xMutex);
vSemaphoreDelete(emac_g_sem);
emac_reset_dma_chain();
emac_config.emac_phy_power_enable(false);
periph_module_disable(PERIPH_EMAC_MODULE);
emac_config.emac_status = EMAC_RUNTIME_NOT_INIT;
/* free memory that dynamically allocted */
for (int i = 0; i < DMA_RX_BUF_NUM; i++) {
free(emac_dma_rx_chain_buf[i]);
free(emac_dma_rx_buf[i]);
emac_dma_rx_chain_buf[i] = NULL;
emac_dma_rx_buf[i] = NULL;
}
for (int i = 0; i < DMA_TX_BUF_NUM; i++) {
free(emac_dma_tx_chain_buf[i]);
free(emac_dma_tx_buf[i]);
emac_dma_tx_chain_buf[i] = NULL;
emac_dma_tx_buf[i] = NULL;
}
ret = ESP_OK;
_exit:
return ret;
}

View file

@ -29,10 +29,9 @@ typedef enum {
} eth_mode_t;
typedef enum {
ETH_CLOCK_GPIO0_IN = 0,
ETH_CLOCK_GPIO0_OUT = 1,
ETH_CLOCK_GPIO0_IN = 0,
ETH_CLOCK_GPIO16_OUT = 2,
ETH_CLOCK_GPIO17_OUT = 3
ETH_CLOCK_GPIO17_OUT = 3,
} eth_clock_mode_t;
typedef enum {
@ -125,6 +124,16 @@ typedef struct {
*/
esp_err_t esp_eth_init(eth_config_t *config);
/**
* @brief Deinit ethernet mac
*
* @return
* - ESP_OK
* - ESP_FAIL
* - ESP_ERR_INVALID_STATE
*/
esp_err_t esp_eth_deinit(void);
/**
* @brief Init Ethernet mac driver only
*
@ -236,7 +245,8 @@ esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t valu
*
* @return ESP_OK if desired value matches, ESP_ERR_TIMEOUT if timed out.
*/
static inline esp_err_t esp_eth_smi_wait_set(uint32_t reg_num, uint16_t value_mask, int timeout_ms) {
static inline esp_err_t esp_eth_smi_wait_set(uint32_t reg_num, uint16_t value_mask, int timeout_ms)
{
return esp_eth_smi_wait_value(reg_num, value_mask, value_mask, timeout_ms);
}

View file

@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View file

@ -0,0 +1,87 @@
/**
* @brief This test has just run in the ESP32_Ethernet_V3 board, which featured
* in PoE submodule and TLK110 PHY. The 50MHz clock used by MAC and PHY is
* supplied by external oscillator through GPIO0.
*
* @file test_emac_deinit.c
* @author morris
* @date 2018-08-24
*/
#include <string.h>
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_err.h"
#include "esp_event_loop.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "rom/gpio.h"
#include "tcpip_adapter.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_wifi.h"
#include "eth_phy/phy_tlk110.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config
static const char *TAG = "eth_test_deinit";
#define PIN_PHY_POWER 17
#define PIN_SMI_MDC 23
#define PIN_SMI_MDIO 18
#define CONFIG_PHY_ADDRESS 31
#define CONFIG_PHY_CLOCK_MODE 0
static void phy_device_power_enable_via_gpio(bool enable)
{
if (!enable) {
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false);
}
gpio_pad_select_gpio(PIN_PHY_POWER);
gpio_set_direction(PIN_PHY_POWER, GPIO_MODE_OUTPUT);
if (enable == true) {
gpio_set_level(PIN_PHY_POWER, 1);
ESP_LOGI(TAG, "power on ethernet phy");
} else {
gpio_set_level(PIN_PHY_POWER, 0);
ESP_LOGI(TAG, "power off ethernet phy");
}
vTaskDelay(1); // Allow the power up/down to take effect, min 300us
if (enable) {
/* operates the default phy-specific power on function */
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true);
}
}
static void eth_gpio_config_rmii(void)
{
phy_rmii_configure_data_interface_pins();
phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO);
}
TEST_CASE("test emac deinit", "[ethernet][ignore]")
{
eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG;
config.phy_addr = CONFIG_PHY_ADDRESS;
config.gpio_config = eth_gpio_config_rmii;
config.tcpip_input = tcpip_adapter_eth_input;
config.clock_mode = CONFIG_PHY_CLOCK_MODE;
config.phy_power_enable = phy_device_power_enable_via_gpio;
ESP_ERROR_CHECK(esp_eth_init(&config));
ESP_ERROR_CHECK(esp_eth_enable());
vTaskDelay(2000 / portTICK_RATE_MS);
ESP_ERROR_CHECK(esp_eth_disable());
ESP_ERROR_CHECK(esp_eth_deinit());
}

View file

@ -25,7 +25,6 @@ Possible configurations of the 50MHz clock signal:
| Mode | GPIO Pin | Signal name | Notes |
| -------- | -------- | -------------- | -------------------------------------------------------------------------------------------------- |
| external | `GPIO0` | `EMAC_TX_CLK` | Input of 50MHz PHY clock |
| internal | `GPIO0` | `CLK_OUT1` | Output of 50MHz APLL clock. Signal quality might be an issue. |
| internal | `GPIO16` | `EMAC_CLK_OUT` | Output of 50MHz APLL clock. |
| internal | `GPIO17` | `EMAC_CLK_180` | Inverted output of 50MHz APLL clock. Found to be best suitable for LAN8720 with long signal lines. |
@ -36,8 +35,6 @@ The external reference clock of 50MHz must be supplied on `GPIO0`. See note abou
#### Internal PHY Clock
The ESP32 can generate a 50MHz clock using its APLL. When the APLL is already used as clock source for other purposes (most likely I²S) external PHY has to be used.
On different test setups clock output on `GPIO0` was found unstable because in most designs the signal path is not ideal for this high frequency (the PCB trace has several devices added to it and therefore the capacitive load is relatively high)
The inverted clock signal `EMAC_CLK_180` was found working best with a LAN8720 PHY.
## RMII PHY Wiring

View file

@ -41,11 +41,6 @@ config PHY_CLOCK_GPIO0_IN
help
Input of 50MHz refclock on GPIO0
config PHY_CLOCK_GPIO0_OUT
bool "GPIO0 output"
help
Output the internal 50MHz APLL clock on GPIO0
config PHY_CLOCK_GPIO16_OUT
bool "GPIO16 output"
help
@ -61,7 +56,6 @@ endchoice
config PHY_CLOCK_MODE
int
default 0 if PHY_CLOCK_GPIO0_IN
default 1 if PHY_CLOCK_GPIO0_OUT
default 2 if PHY_CLOCK_GPIO16_OUT
default 3 if PHY_CLOCK_GPIO17_OUT

View file

@ -16,27 +16,15 @@
#include "esp_err.h"
#include "esp_event_loop.h"
#include "esp_event.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "rom/ets_sys.h"
#include "rom/gpio.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/gpio_reg.h"
#include "soc/gpio_sig_map.h"
#include "tcpip_adapter.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "soc/emac_ex_reg.h"
#include "driver/periph_ctrl.h"
#ifdef CONFIG_PHY_LAN8720
#include "eth_phy/phy_lan8720.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
@ -49,103 +37,121 @@
static const char *TAG = "eth_example";
#define PIN_PHY_POWER CONFIG_PHY_POWER_PIN
#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN
#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN
#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN
#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN
#ifdef CONFIG_PHY_USE_POWER_PIN
/* This replaces the default PHY power on/off function with one that
also uses a GPIO for power on/off.
If this GPIO is not connected on your device (and PHY is always powered), you can use the default PHY-specific power
on/off function rather than overriding with this one.
*/
/**
* @brief re-define power enable func for phy
*
* @param enable true to enable, false to disable
*
* @note This function replaces the default PHY power on/off function.
* If this GPIO is not connected on your device (and PHY is always powered),
* you can use the default PHY-specific power on/off function.
*/
static void phy_device_power_enable_via_gpio(bool enable)
{
assert(DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable);
if (!enable) {
/* Do the PHY-specific power_enable(false) function before powering down */
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false);
}
gpio_pad_select_gpio(PIN_PHY_POWER);
gpio_set_direction(PIN_PHY_POWER,GPIO_MODE_OUTPUT);
if(enable == true) {
gpio_set_direction(PIN_PHY_POWER, GPIO_MODE_OUTPUT);
if (enable == true) {
gpio_set_level(PIN_PHY_POWER, 1);
ESP_LOGD(TAG, "phy_device_power_enable(TRUE)");
ESP_LOGI(TAG, "Power On Ethernet PHY");
} else {
gpio_set_level(PIN_PHY_POWER, 0);
ESP_LOGD(TAG, "power_enable(FALSE)");
ESP_LOGI(TAG, "Power Off Ethernet PHY");
}
// Allow the power up/down to take effect, min 300us
vTaskDelay(1);
vTaskDelay(1); // Allow the power up/down to take effect, min 300us
if (enable) {
/* Run the PHY-specific power on operations now the PHY has power */
/* call the default PHY-specific power on function */
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true);
}
}
#endif
/**
* @brief gpio specific init
*
* @note RMII data pins are fixed in esp32:
* TXD0 <=> GPIO19
* TXD1 <=> GPIO22
* TX_EN <=> GPIO21
* RXD0 <=> GPIO25
* RXD1 <=> GPIO26
* CLK <=> GPIO0
*
*/
static void eth_gpio_config_rmii(void)
{
// RMII data pins are fixed:
// TXD0 = GPIO19
// TXD1 = GPIO22
// TX_EN = GPIO21
// RXD0 = GPIO25
// RXD1 = GPIO26
// CLK == GPIO0
phy_rmii_configure_data_interface_pins();
// MDC is GPIO 23, MDIO is GPIO 18
phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO);
}
void eth_task(void *pvParameter)
/**
* @brief event handler for ethernet
*
* @param ctx
* @param event
* @return esp_err_t
*/
static esp_err_t eth_event_handler(void *ctx, system_event_t *event)
{
tcpip_adapter_ip_info_t ip;
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t));
vTaskDelay(2000 / portTICK_PERIOD_MS);
while (1) {
vTaskDelay(2000 / portTICK_PERIOD_MS);
if (tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip) == 0) {
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:"IPSTR, IP2STR(&ip.ip));
ESP_LOGI(TAG, "ETHPMASK:"IPSTR, IP2STR(&ip.netmask));
ESP_LOGI(TAG, "ETHPGW:"IPSTR, IP2STR(&ip.gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
}
switch (event->event_id) {
case SYSTEM_EVENT_ETH_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
break;
case SYSTEM_EVENT_ETH_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t));
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip));
ESP_LOGI(TAG, "Ethernet Got IP Addr");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip.ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip.netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip.gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
break;
case SYSTEM_EVENT_ETH_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
return ESP_OK;
}
void app_main()
{
esp_err_t ret = ESP_OK;
tcpip_adapter_init();
esp_event_loop_init(NULL, NULL);
ESP_ERROR_CHECK(esp_event_loop_init(eth_event_handler, NULL));
eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG;
/* Set the PHY address in the example configuration */
config.phy_addr = CONFIG_PHY_ADDRESS;
config.gpio_config = eth_gpio_config_rmii;
config.tcpip_input = tcpip_adapter_eth_input;
config.clock_mode = CONFIG_PHY_CLOCK_MODE;
#ifdef CONFIG_PHY_USE_POWER_PIN
/* Replace the default 'power enable' function with an example-specific
one that toggles a power GPIO. */
/* Replace the default 'power enable' function with an example-specific one
that toggles a power GPIO. */
config.phy_power_enable = phy_device_power_enable_via_gpio;
#endif
ret = esp_eth_init(&config);
if(ret == ESP_OK) {
esp_eth_enable();
xTaskCreate(eth_task, "eth_task", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL);
}
ESP_ERROR_CHECK(esp_eth_init(&config));
ESP_ERROR_CHECK(esp_eth_enable()) ;
}