dongly 发表于 2022-6-18 09:45

LAN8720A 不能获取IP,求RT-Thread的驱动

本帖最后由 dongly 于 2022-6-18 11:31 编辑


[*]问题: 在rt-thread 最新版下,加入LAN8720A的支持,不能获取IP,但2个网络指示灯亮,debug时,有收发数据
[*]硬件: AT32F407VGT7 + LAN7820A,RMII_RX为PC4,PC5,LAN8720A 自带25MHz晶振
[*]用雅特力的例程可以工作,说明硬件没问题
[*]求指导,或移植好的驱动
[*]drv_emac.h/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date         Author       Notes
* 2022-05-16   shelton      first version
*/

#ifndef __DRV_EMAC_H__
#define __DRV_EMAC_H__

#include "drv_common.h"
#include <rtdevice.h>
#include <rthw.h>
#include <rtthread.h>

/* the phy basic control register */
#define PHY_BASIC_CONTROL_REG   0x00U
#define PHY_RESET_MASK            (1 << 15)
#define PHY_AUTO_NEGOTIATION_MASK (1 << 12)

/* the phy basic status register */
#define PHY_BASIC_STATUS_REG       0x01U
#define PHY_LINKED_STATUS_MASK   (1 << 2)
#define PHY_AUTONEGO_COMPLETE_MASK (1 << 5)

/* the phy id one register */
#define PHY_ID1_REG 0x02U
/* the phy id two register */
#define PHY_ID2_REG 0x03U
/* the phy auto-negotiate advertise register */
#define PHY_AUTONEG_ADVERTISE_REG 0x04U

#if defined(PHY_USING_DM9162)
#define PHY_CONTROL_REG      (0x00) /*!< basic mode control register */
#define PHY_STATUS_REG       (0x01) /*!< basic mode status register */
#define PHY_SPECIFIED_CS_REG (0x11) /*!< specified configuration and status register */
/* phy control register */
#define PHY_AUTO_NEGOTIATION_BIT (0x1000) /*!< enable auto negotiation */
#define PHY_LOOPBACK_BIT         (0x4000) /*!< enable loopback */
#define PHY_RESET_BIT            (0x8000) /*!< reset phy */
/* phy status register */
#define PHY_LINKED_STATUS_BIT (0x0004) /*!< link status */
#define PHY_NEGO_COMPLETE_BIT (0x0020) /*!< auto negotiation complete */
/* phy specified control/status register */
#define PHY_FULL_DUPLEX_100MBPS_BIT (0x8000) /*!< full duplex 100 mbps */
#define PHY_HALF_DUPLEX_100MBPS_BIT (0x4000) /*!< half duplex 100 mbps */
#define PHY_FULL_DUPLEX_10MBPS_BIT(0x2000) /*!< full duplex 10 mbps */
#define PHY_HALF_DUPLEX_10MBPS_BIT(0x1000) /*!< half duplex 10 mbps */
#define PHY_DUPLEX_MODE                                                                            \
    (PHY_FULL_DUPLEX_100MBPS_BIT | PHY_FULL_DUPLEX_10MBPS_BIT) /*!< full duplex mode */
#define PHY_SPEED_MODE (PHY_FULL_DUPLEX_10MBPS_BIT | PHY_HALF_DUPLEX_10MBPS_BIT) /*!< 10 mbps */
/*the phy interrupt source flag register. */
#define PHY_INTERRUPT_FLAG_REG 0x15U
/*the phy interrupt mask register. */
#define PHY_INTERRUPT_MASK_REG 0x15U
#define PHY_LINK_CHANGE_FLAG   (1 << 2)
#define PHY_LINK_CHANGE_MASK   (1 << 9)
#define PHY_INT_MASK         0
#elif defined(PHY_USING_DP83848)
#define PHY_CONTROL_REG          (0x00)   /*!< basic mode control register */
#define PHY_STATUS_REG         (0x01)   /*!< basic mode status register */
#define PHY_SPECIFIED_CS_REG   (0x10)   /*!< phy status register */
/* phy control register */
#define PHY_AUTO_NEGOTIATION_BIT (0x1000) /*!< enable auto negotiation */
#define PHY_LOOPBACK_BIT         (0x4000) /*!< enable loopback */
#define PHY_RESET_BIT            (0x8000) /*!< reset phy */
/* phy status register */
#define PHY_LINKED_STATUS_BIT    (0x0004) /*!< link status */
#define PHY_NEGO_COMPLETE_BIT    (0x0020) /*!< auto negotiation complete */

#define PHY_DUPLEX_MODE      (0x0004) /*!< full duplex mode */
#define PHY_SPEED_MODE         (0x0002) /*!< 10 mbps */

/*the phy interrupt source flag register. */
#define PHY_INTERRUPT_FLAG_REG 0x12U
#define PHY_LINK_CHANGE_FLAG   (1 << 13)
/*the phy interrupt control register. */
#define PHY_INTERRUPT_CTRL_REG 0x11U
#define PHY_INTERRUPT_EN       ((1 << 0) | (1 << 1))
/*the phy interrupt mask register. */
#define PHY_INTERRUPT_MASK_REG 0x12U
#define PHY_INT_MASK         (1 << 5)
#elif defined(PHY_USING_LAN8720A)
#define PHY_CONTROL_REG      (0x00) /*!< basic mode control register */
#define PHY_STATUS_REG       (0x01) /*!< basic mode status register */
#define PHY_SPECIFIED_CS_REG (0x1F) /*!< phy status register */
/* phy control register */
#define PHY_AUTO_NEGOTIATION_BIT (0x1000) /*!< enable auto negotiation */
#define PHY_LOOPBACK_BIT         (0x4000) /*!< enable loopback */
#define PHY_RESET_BIT            (0x8000) /*!< reset phy */
/* phy status register */
#define PHY_LINKED_STATUS_BIT    (0x0004) /*!< link status */
#define PHY_NEGO_COMPLETE_BIT    (0x0020) /*!< auto negotiation complete */

#define PHY_DUPLEX_MODE (0x0100) /*!< full duplex mode */
#define PHY_SPEED_MODE(0x2000) /*!< 100 mbps */

#define PHY_FULL_DUPLEX_100MBPS_BIT (0x0018) /*!< full duplex 100 mbps */
#define PHY_HALF_DUPLEX_100MBPS_BIT (0x0008) /*!< half duplex 100 mbps */
#define PHY_FULL_DUPLEX_10MBPS_BIT(0x0014) /*!< full duplex 10 mbps */
#define PHY_HALF_DUPLEX_10MBPS_BIT(0x0004) /*!< half duplex 10 mbps */
#define PHY_DUPLEX_MBPS_MSK         (0x001C)
/*The PHY interrupt source flag register. */
#define PHY_INTERRUPT_FLAG_REG      (0x01DU)
#endif

#endif /* __DRV_EMAC_H__ */

/*
[*]/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date         Author       Notes
* 2022-05-16   shelton      first version
*/

#include "drv_emac.h"
#include <netif/ethernetif.h>
#include <lwipopts.h>

/* debug option */
// #define EMAC_RX_DUMP
// #define EMAC_TX_DUMP
#define DRV_DEBUG
#define LOG_TAG                         "drv.emac"
#include <drv_log.h>

/* emac memory buffer configuration */
#define EMAC_NUM_RX_BUF               4    /* 0x1800 for rx (4 * 1536 = 6k)      */
#define EMAC_NUM_TX_BUF               2    /* 0x0600 for tx (2 * 1536 = 3k)      */

#define MAX_ADDR_LEN                  6

struct rt_at32_emac
{
    /* inherit from ethernet device */
    struct eth_device parent;
#ifndef PHY_USING_INTERRUPT_MODE
    rt_timer_t poll_link_timer;
#endif

    /* interface address info, hw address */
    rt_uint8_t dev_addr;
    /* emac_speed */
    emac_speed_type emac_speed;
    /* emac_duplex_mode */
    emac_duplex_type emac_mode;
};

static emac_dma_desc_type *dma_rx_dscr_tab, *dma_tx_dscr_tab;
extern emac_dma_desc_type *dma_rx_desc_to_get, *dma_tx_desc_to_set;

static rt_uint8_t *rx_buff, *tx_buff;
static struct rt_at32_emac at32_emac_device;
static uint8_t phy_addr = 0xFF;
static struct rt_semaphore tx_wait;
static rt_bool_t tx_is_waiting = RT_FALSE;

#if defined(EMAC_RX_DUMP) || defined(EMAC_TX_DUMP)
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
{
    unsigned char *buf = (unsigned char *)ptr;
    int i, j;

    for (i = 0; i < buflen; i += 16)
    {
      rt_kprintf("%08X: ", i);

      for (j = 0; j < 16; j++)
            if (i + j < buflen)
                rt_kprintf("%02X ", buf);
            else
                rt_kprintf("   ");
      rt_kprintf(" ");

      for (j = 0; j < 16; j++)
            if (i + j < buflen)
                rt_kprintf("%c", __is_print(buf) ? buf : '.');
      rt_kprintf("\n");
    }
}
#endif

// /**
//   * @brief phy reset
//   */
// static void phy_reset(void)
// {
//   gpio_init_type gpio_init_struct;

// #if defined (SOC_SERIES_AT32F437)
//   crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE);
//   crm_periph_clock_enable(CRM_GPIOG_PERIPH_CLOCK, TRUE);

//   gpio_default_para_init(&gpio_init_struct);
//   gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
//   gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
//   gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
//   gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
//   gpio_init_struct.gpio_pins = GPIO_PINS_15;
//   gpio_init(GPIOE, &gpio_init_struct);

//   gpio_init_struct.gpio_pins = GPIO_PINS_15;
//   gpio_init(GPIOG, &gpio_init_struct);

//   gpio_bits_reset(GPIOE, GPIO_PINS_15);
//   gpio_bits_reset(GPIOG, GPIO_PINS_15);
//   rt_thread_mdelay(2);
//   gpio_bits_set(GPIOE, GPIO_PINS_15);
// #endif
// #if defined (SOC_SERIES_AT32F407)
//   crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);

//   gpio_default_para_init(&gpio_init_struct);
//   gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
//   gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
//   gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
//   gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
//   gpio_init_struct.gpio_pins = GPIO_PINS_8;
//   gpio_init(GPIOC, &gpio_init_struct);

//   gpio_bits_reset(GPIOC, GPIO_PINS_8);
//   rt_thread_mdelay(2);
//   gpio_bits_set(GPIOC, GPIO_PINS_8);
// #endif
//   rt_thread_mdelay(2000);
// }


/**
* @brief reset phy register
*/
static error_status emac_phy_register_reset(void)
{
    uint16_t data = 0;
    uint32_t timeout = 0;
    uint32_t i = 0;

    if(emac_phy_register_write(phy_addr, PHY_CONTROL_REG, PHY_RESET_BIT) == ERROR)
    {
      return ERROR;
    }

    for(i = 0; i < 0x000FFFFF; i++);

    do
    {
      timeout++;
      if(emac_phy_register_read(phy_addr, PHY_CONTROL_REG, &data) == ERROR)
      {
            return ERROR;
      }
    } while((data & PHY_RESET_BIT) && (timeout < PHY_TIMEOUT));

    for(i = 0; i < 0x00FFFFF; i++);
    if(timeout == PHY_TIMEOUT)
    {
      return ERROR;
    }
    return SUCCESS;
}

/**
* @brief set mac speed related parameters
*/
static error_status emac_speed_config(emac_auto_negotiation_type nego, emac_duplex_type mode, emac_speed_type speed)
{
    uint16_t data = 0;
    uint32_t timeout = 0;
    if(nego == EMAC_AUTO_NEGOTIATION_ON)
    {
      do
      {
            timeout++;
            if(emac_phy_register_read(phy_addr, PHY_STATUS_REG, &data) == ERROR)
            {
            return ERROR;
            }
      } while(!(data & PHY_LINKED_STATUS_BIT) && (timeout < PHY_TIMEOUT));

      if(timeout == PHY_TIMEOUT)
      {
            return ERROR;
      }

      timeout = 0;

      if(emac_phy_register_write(phy_addr, PHY_CONTROL_REG, PHY_AUTO_NEGOTIATION_BIT) == ERROR)
      {
            return ERROR;
      }

      do
      {
            timeout++;
            if(emac_phy_register_read(phy_addr, PHY_STATUS_REG, &data) == ERROR)
            {
                return ERROR;
            }
      } while(!(data & PHY_NEGO_COMPLETE_BIT) && (timeout < PHY_TIMEOUT));

      if(timeout == PHY_TIMEOUT)
      {
            return ERROR;
      }

      if(emac_phy_register_read(phy_addr, PHY_SPECIFIED_CS_REG, &data) == ERROR)
      {
            return ERROR;
      }
#ifdef PHY_USING_DM9162
      if(data & PHY_FULL_DUPLEX_100MBPS_BIT)
      {
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
            emac_duplex_mode_set(EMAC_FULL_DUPLEX);
      }
      else if(data & PHY_HALF_DUPLEX_100MBPS_BIT)
      {
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
            emac_duplex_mode_set(EMAC_HALF_DUPLEX);
      }
      else if(data & PHY_FULL_DUPLEX_10MBPS_BIT)
      {
            emac_fast_speed_set(EMAC_SPEED_10MBPS);
            emac_duplex_mode_set(EMAC_FULL_DUPLEX);
      }
      else if(data & PHY_HALF_DUPLEX_10MBPS_BIT)
      {
            emac_fast_speed_set(EMAC_SPEED_10MBPS);
            emac_duplex_mode_set(EMAC_HALF_DUPLEX);
      }
#endif
#ifdef PHY_USING_DP83848
      if(data & PHY_DUPLEX_MODE)
      {
            emac_duplex_mode_set(EMAC_FULL_DUPLEX);
      }
      else
      {
            emac_duplex_mode_set(EMAC_HALF_DUPLEX);
      }
      if(data & PHY_SPEED_MODE)
      {
            emac_fast_speed_set(EMAC_SPEED_10MBPS);
      }
      else
      {
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
      }
#endif
#ifdef PHY_USING_LAN8720A
      if ((data & PHY_DUPLEX_MBPS_MSK) == PHY_FULL_DUPLEX_100MBPS_BIT)
      {
            emac_duplex_mode_set(EMAC_FULL_DUPLEX);
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
      }

      if ((data & PHY_DUPLEX_MBPS_MSK) == PHY_HALF_DUPLEX_100MBPS_BIT)
      {
            emac_duplex_mode_set(EMAC_HALF_DUPLEX);
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
      }

      if ((data & PHY_DUPLEX_MBPS_MSK) == PHY_FULL_DUPLEX_10MBPS_BIT)
      {
            emac_duplex_mode_set(EMAC_FULL_DUPLEX);
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
      }

      if ((data & PHY_DUPLEX_MBPS_MSK) == PHY_FULL_DUPLEX_10MBPS_BIT)
      {
            emac_duplex_mode_set(EMAC_HALF_DUPLEX);
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
      }
#endif
    }
    else
    {
      if(emac_phy_register_write(phy_addr, PHY_CONTROL_REG, (uint16_t)((mode << 8) | (speed << 13))) == ERROR)
      {
            return ERROR;
      }
      if(speed == EMAC_SPEED_100MBPS)
      {
            emac_fast_speed_set(EMAC_SPEED_100MBPS);
      }
      else
      {
            emac_fast_speed_set(EMAC_SPEED_10MBPS);
      }
      if(mode == EMAC_FULL_DUPLEX)
      {
            emac_duplex_mode_set(EMAC_FULL_DUPLEX);
      }
      else
      {
            emac_duplex_mode_set(EMAC_HALF_DUPLEX);
      }
    }

    return SUCCESS;
}

/**
* @brief initialize emac phy
*/
static error_status emac_phy_init(emac_control_config_type *control_para)
{
    emac_clock_range_set();
    if(emac_phy_register_reset() == ERROR)
    {
      return ERROR;
    }
    if(emac_speed_config(control_para->auto_nego, control_para->duplex_mode, control_para->fast_ethernet_speed) == ERROR)
    {
      return ERROR;
    }

    emac_control_config(control_para);
    return SUCCESS;
}

/**
* @brief emac initialization function
*/
static rt_err_t rt_at32_emac_init(rt_device_t dev)
{
    emac_control_config_type mac_control_para;
    emac_dma_config_type dma_control_para;

    /* check till phy detected */
    while(phy_addr == 0xFF)
    {
      rt_thread_mdelay(1000);
    }

    /* emac reset */
    emac_reset();

    /* software reset emac dma */
    emac_dma_software_reset_set();
    while(emac_dma_software_reset_get() == SET);

    emac_control_para_init(&mac_control_para);
    mac_control_para.auto_nego = EMAC_AUTO_NEGOTIATION_ON;
    if(emac_phy_init(&mac_control_para) == ERROR)
    {
      LOG_E("emac hardware init failed");
      return -RT_ERROR;
    }
    else
    {
      LOG_D("emac hardware init success");
    }

    emac_transmit_flow_control_enable(TRUE);
    emac_zero_quanta_pause_disable(TRUE);

    /* set mac address */
    emac_local_address_set(at32_emac_device.dev_addr);

    /* set emac dma rx link list */
    emac_dma_descriptor_list_address_set(EMAC_DMA_RECEIVE, dma_rx_dscr_tab, rx_buff, EMAC_NUM_RX_BUF);
    /* set emac dma tx link list */
    emac_dma_descriptor_list_address_set(EMAC_DMA_TRANSMIT, dma_tx_dscr_tab, tx_buff, EMAC_NUM_TX_BUF);

    /* emac interrupt init */
    emac_dma_config(&dma_control_para);
    emac_dma_interrupt_enable(EMAC_DMA_INTERRUPT_NORMAL_SUMMARY, TRUE);
    emac_dma_interrupt_enable(EMAC_DMA_INTERRUPT_RX, TRUE);
    nvic_irq_enable(EMAC_IRQn, 0x07, 0);

    /* enable emac */
    emac_start();

    return RT_EOK;
}

static rt_err_t rt_at32_emac_open(rt_device_t dev, rt_uint16_t oflag)
{
    LOG_D("emac open");
    return RT_EOK;
}

static rt_err_t rt_at32_emac_close(rt_device_t dev)
{
    LOG_D("emac close");
    return RT_EOK;
}

static rt_size_t rt_at32_emac_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    LOG_D("emac read");
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_size_t rt_at32_emac_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    LOG_D("emac write");
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_err_t rt_at32_emac_control(rt_device_t dev, int cmd, void *args)
{
    switch (cmd)
    {
    case NIOCTL_GADDR:
      /* get mac address */
      if (args)
      {
            SMEMCPY(args, at32_emac_device.dev_addr, 6);
      }
      else
      {
            return -RT_ERROR;
      }
      break;

    default :
      break;
    }

    return RT_EOK;
}

/**
* @brief transmit data
*/
rt_err_t rt_at32_emac_tx(rt_device_t dev, struct pbuf *p)
{
    struct pbuf *q;
    rt_uint32_t offset;

    while ((dma_tx_desc_to_set->status & EMAC_DMATXDESC_OWN) != RESET)
    {
      rt_err_t result;
      rt_uint32_t level;

      level = rt_hw_interrupt_disable();
      tx_is_waiting = RT_TRUE;
      rt_hw_interrupt_enable(level);

      /* it's own bit set, wait it */
      result = rt_sem_take(&tx_wait, RT_WAITING_FOREVER);
      if (result == RT_EOK) break;
      if (result == -RT_ERROR) return -RT_ERROR;
    }

    offset = 0;
    for (q = p; q != NULL; q = q->next)
    {
      uint8_t *buffer;

      /* copy the frame to be sent into memory pointed by the current ethernet dma tx descriptor */
      buffer = (uint8_t*)((dma_tx_desc_to_set->buf1addr) + offset);
      SMEMCPY(buffer, q->payload, q->len);
      offset += q->len;
    }

#ifdef EMAC_TX_DUMP
    dump_hex(p->payload, p->tot_len);
#endif
    /* prepare transmit descriptors to give to dma */
    LOG_D("transmit frame length :%d", p->tot_len);

    /* setting the frame length: bits */
    dma_tx_desc_to_set->controlsize = (p->tot_len & EMAC_DMATXDESC_TBS1);
    /* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */
    dma_tx_desc_to_set->status |= EMAC_DMATXDESC_LS | EMAC_DMATXDESC_FS;
    /* enable tx completion interrupt */
    dma_tx_desc_to_set->status |= EMAC_DMATXDESC_IC;
    /* set own bit of the tx descriptor status: gives the buffer back to ethernet dma */
    dma_tx_desc_to_set->status |= EMAC_DMATXDESC_OWN;

    /* When Tx Buffer unavailable flag is set: clear it and resume transmission */
    if(emac_dma_flag_get(EMAC_DMA_TBU_FLAG) != RESET)
    {
      emac_dma_flag_clear(EMAC_DMA_TBU_FLAG);
      emac_dma_poll_demand_set(EMAC_DMA_TRANSMIT, 0);
    }

    /* selects the next dma tx descriptor list for next buffer to send */
    dma_tx_desc_to_set = (emac_dma_desc_type*) (dma_tx_desc_to_set->buf2nextdescaddr);

    return ERR_OK;
}

/**
* @brief receive data
*/
struct pbuf *rt_at32_emac_rx(rt_device_t dev)
{
    struct pbuf *p = NULL;
    struct pbuf *q = NULL;
    rt_uint32_t offset = 0;
    uint16_t len = 0;
    uint8_t *buffer;

    /* get received frame */
    len = emac_received_packet_size_get();
    if(len > 0)
    {
      LOG_D("receive frame len : %d", len);
      /* we allocate a pbuf chain of pbufs from the lwip buffer pool */
      p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

      if(p != NULL)
      {
            for (q = p; q != RT_NULL; q= q->next)
            {
                /* get rx buffer */
                buffer = (uint8_t *)(dma_rx_desc_to_get->buf1addr);
#ifdef EMAC_RX_DUMP
                dump_hex(buffer, len);
#endif
                /* copy the received frame into buffer from memory pointed by the current ethernet dma rx descriptor */
                SMEMCPY(q->payload, (buffer + offset), q->len);
                offset += q->len;
            }
      }
    }
    else
    {
      return p;
    }

    /* release descriptors to dma */
    dma_rx_desc_to_get->status |= EMAC_DMARXDESC_OWN;

    /* when rx buffer unavailable flag is set: clear it and resume reception */
    if(emac_dma_flag_get(EMAC_DMA_RBU_FLAG) != RESET)
    {
      /* clear rbu ethernet dma flag */
      emac_dma_flag_clear(EMAC_DMA_RBU_FLAG);
      /* resume dma reception */
      emac_dma_poll_demand_set(EMAC_DMA_RECEIVE, 0);
    }

    /* update the ethernet dma global rx descriptor with next rx decriptor */
    /* chained mode */
    if((dma_rx_desc_to_get->controlsize & EMAC_DMARXDESC_RCH) != RESET)
    {
      /* selects the next dma rx descriptor list for next buffer to read */
      dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
    }
    /* ring mode */
    else
    {
      if((dma_rx_desc_to_get->controlsize & EMAC_DMARXDESC_RER) != RESET)
      {
            /* selects the first dma rx descriptor for next buffer to read: last rx descriptor was used */
            dma_rx_desc_to_get = (emac_dma_desc_type*) (EMAC_DMA->rdladdr);
      }
      else
      {
            /* selects the next dma rx descriptor list for next buffer to read */
            dma_rx_desc_to_get = (emac_dma_desc_type*) ((uint32_t)dma_rx_desc_to_get + 0x10 + ((EMAC_DMA->bm & 0x0000007C) >> 2));
      }
    }
    return p;
}

void EMAC_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    /* clear received it */
    if(emac_dma_flag_get(EMAC_DMA_NIS_FLAG) != RESET)
    {
      emac_dma_flag_clear(EMAC_DMA_NIS_FLAG);
    }
    if(emac_dma_flag_get(EMAC_DMA_AIS_FLAG) != RESET)
    {
      emac_dma_flag_clear(EMAC_DMA_AIS_FLAG);
    }
    if(emac_dma_flag_get(EMAC_DMA_OVF_FLAG) != RESET)
    {
      emac_dma_flag_clear(EMAC_DMA_OVF_FLAG);
    }
    if(emac_dma_flag_get(EMAC_DMA_RBU_FLAG) != RESET)
    {
      emac_dma_flag_clear(EMAC_DMA_RBU_FLAG);
    }

    /* packet receiption */
    if (emac_dma_flag_get(EMAC_DMA_RI_FLAG) == SET)
    {
      /* a frame has been received */
      eth_device_ready(&(at32_emac_device.parent));
      emac_dma_flag_clear(EMAC_DMA_RI_FLAG);
    }

    /* packet transmission */
    if (emac_dma_flag_get(EMAC_DMA_TI_FLAG) == SET)
    {
      if (tx_is_waiting == RT_TRUE)
      {
            tx_is_waiting = RT_FALSE;
            rt_sem_release(&tx_wait);
      }

      emac_dma_flag_clear(EMAC_DMA_TI_FLAG);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

enum {
    PHY_LINK      = (1 << 0),
    PHY_10M         = (1 << 1),
    PHY_FULLDUPLEX= (1 << 2),
};

static void phy_linkchange()
{
    static rt_uint8_t phy_speed = 0;
    rt_uint8_t phy_speed_new = 0;
    rt_uint16_t status;

    emac_phy_register_read(phy_addr, PHY_BASIC_STATUS_REG, (uint16_t *)&status);
    LOG_D("phy basic status reg is 0x%X", status);

    if (status & (PHY_AUTONEGO_COMPLETE_MASK | PHY_LINKED_STATUS_MASK))
    {
      rt_uint16_t SR = 0;

      phy_speed_new |= PHY_LINK;

      emac_phy_register_read(phy_addr, PHY_SPECIFIED_CS_REG, (uint16_t *)&SR);
      LOG_D("phy control status reg is 0x%X", SR);

      if (SR & (PHY_SPEED_MODE))
      {
            phy_speed_new |= PHY_10M;
      }

      if (SR & (PHY_DUPLEX_MODE))
      {
            phy_speed_new |= PHY_FULLDUPLEX;
      }
    }

    if (phy_speed != phy_speed_new)
    {
      phy_speed = phy_speed_new;
      if (phy_speed & PHY_LINK)
      {
            LOG_D("link up");
            if (phy_speed & PHY_10M)
            {
                LOG_D("10Mbps");
                at32_emac_device.emac_speed = EMAC_SPEED_10MBPS;
            }
            else
            {
                at32_emac_device.emac_speed = EMAC_SPEED_100MBPS;
                LOG_D("100Mbps");
            }

            if (phy_speed & PHY_FULLDUPLEX)
            {
                LOG_D("full-duplex");
                at32_emac_device.emac_mode = EMAC_FULL_DUPLEX;
            }
            else
            {
                LOG_D("half-duplex");
                at32_emac_device.emac_mode = EMAC_HALF_DUPLEX;
            }

            /* send link up. */
            eth_device_linkchange(&at32_emac_device.parent, RT_TRUE);
      }
      else
      {
            LOG_I("link down");
            eth_device_linkchange(&at32_emac_device.parent, RT_FALSE);
      }
    }
}

#ifdef PHY_USING_INTERRUPT_MODE
static void emac_phy_isr(void *args)
{
    rt_uint32_t status = 0;

    emac_phy_register_read(phy_addr, PHY_INTERRUPT_FLAG_REG, (uint16_t *)&status);
    LOG_D("phy interrupt status reg is 0x%X", status);

    phy_linkchange();
}
#endif /* PHY_USING_INTERRUPT_MODE */

static void phy_monitor_thread_entry(void *parameter)
{
    uint8_t detected_count = 0;

    while(phy_addr == 0xFF)
    {
      /* phy search */
      rt_uint32_t i, temp;
      for (i = 0; i <= 0x1F; i++)
      {
            emac_phy_register_read(i, PHY_BASIC_STATUS_REG, (uint16_t *)&temp);

            if (temp != 0xFFFF && temp != 0x00)
            {
                phy_addr = i;
                break;
            }
      }

      detected_count++;
      rt_thread_mdelay(1000);

      if (detected_count > 10)
      {
            LOG_E("No PHY device was detected, please check hardware!");
      }
    }

    LOG_D("Found a phy, address:0x%02X", phy_addr);

    /* reset phy */
    LOG_D("RESET PHY!");
    emac_phy_register_write(phy_addr, PHY_BASIC_CONTROL_REG, PHY_RESET_MASK);
    rt_thread_mdelay(2000);
    emac_phy_register_write(phy_addr, PHY_BASIC_CONTROL_REG, PHY_AUTO_NEGOTIATION_MASK);

    phy_linkchange();
#ifdef PHY_USING_INTERRUPT_MODE
    /* configuration intterrupt pin */
    rt_pin_mode(PHY_INT_PIN, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(PHY_INT_PIN, PIN_IRQ_MODE_FALLING, emac_phy_isr, (void *)"callbackargs");
    rt_pin_irq_enable(PHY_INT_PIN, PIN_IRQ_ENABLE);

    /* enable phy interrupt */
    emac_phy_register_write(phy_addr, PHY_INTERRUPT_MASK_REG, PHY_INT_MASK);
#if defined(PHY_INTERRUPT_CTRL_REG)
    emac_phy_register_write(phy_addr, PHY_INTERRUPT_CTRL_REG, PHY_INTERRUPT_EN);
#endif
#else /* PHY_USING_INTERRUPT_MODE */
    at32_emac_device.poll_link_timer = rt_timer_create("phylnk", (void (*)(void*))phy_linkchange,
                                        NULL, RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC);
    if (!at32_emac_device.poll_link_timer || rt_timer_start(at32_emac_device.poll_link_timer) != RT_EOK)
    {
      LOG_E("Start link change detection timer failed");
    }
#endif /* PHY_USING_INTERRUPT_MODE */
}

extern void phy_reset(void);

/* Register the EMAC device */
static int rt_hw_at32_emac_init(void)
{
    rt_err_t state = RT_EOK;

    /* Prepare receive and send buffers */
    rx_buff = (rt_uint8_t *)rt_calloc(EMAC_NUM_RX_BUF, EMAC_MAX_PACKET_LENGTH);
    if (rx_buff == RT_NULL)
    {
      LOG_E("No memory");
      state = -RT_ENOMEM;
      goto __exit;
    }

    tx_buff = (rt_uint8_t *)rt_calloc(EMAC_NUM_TX_BUF, EMAC_MAX_PACKET_LENGTH);
    if (tx_buff == RT_NULL)
    {
      LOG_E("No memory");
      state = -RT_ENOMEM;
      goto __exit;
    }

    dma_rx_dscr_tab = (emac_dma_desc_type *)rt_calloc(EMAC_NUM_RX_BUF, sizeof(emac_dma_desc_type));
    if (dma_rx_dscr_tab == RT_NULL)
    {
      LOG_E("No memory");
      state = -RT_ENOMEM;
      goto __exit;
    }

    dma_tx_dscr_tab = (emac_dma_desc_type *)rt_calloc(EMAC_NUM_TX_BUF, sizeof(emac_dma_desc_type));
    if (dma_tx_dscr_tab == RT_NULL)
    {
      LOG_E("No memory");
      state = -RT_ENOMEM;
      goto __exit;
    }

    // /* phy clock */
    // phy_clock_config();

    /* enable periph clock */
    crm_periph_clock_enable(CRM_EMAC_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_EMACTX_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_EMACRX_PERIPH_CLOCK, TRUE);

    /* interface mode */
#if defined (SOC_SERIES_AT32F407)
    gpio_pin_remap_config(MII_RMII_SEL_GMUX, TRUE);
#endif
#if defined (SOC_SERIES_AT32F437)
    scfg_emac_interface_set(SCFG_EMAC_SELECT_RMII);
#endif

    /* emac gpio init */
    at32_msp_emac_init(NULL);

    at32_emac_device.emac_speed = EMAC_SPEED_100MBPS;
    at32_emac_device.emac_mode= EMAC_FULL_DUPLEX;

    at32_emac_device.dev_addr = 0x00;
    at32_emac_device.dev_addr = 0x66;
    at32_emac_device.dev_addr = 0x88;
    /* generate mac addr from unique id (only for test). */
    at32_emac_device.dev_addr = *(rt_uint8_t *)(0x1FFFF7E8 + 4);
    at32_emac_device.dev_addr = *(rt_uint8_t *)(0x1FFFF7E8 + 2);
    at32_emac_device.dev_addr = *(rt_uint8_t *)(0x1FFFF7E8 + 0);

    at32_emac_device.parent.parent.init = rt_at32_emac_init;
    at32_emac_device.parent.parent.open = rt_at32_emac_open;
    at32_emac_device.parent.parent.close = rt_at32_emac_close;
    at32_emac_device.parent.parent.read = rt_at32_emac_read;
    at32_emac_device.parent.parent.write = rt_at32_emac_write;
    at32_emac_device.parent.parent.control = rt_at32_emac_control;
    at32_emac_device.parent.parent.user_data = RT_NULL;

    at32_emac_device.parent.eth_rx = rt_at32_emac_rx;
    at32_emac_device.parent.eth_tx = rt_at32_emac_tx;

    /* reset phy */
    phy_reset();
    rt_thread_mdelay(2000);

    /* start phy monitor */
    rt_thread_t tid;
    tid = rt_thread_create("phy",
                           phy_monitor_thread_entry,
                           RT_NULL,
                           1024,
                           RT_THREAD_PRIORITY_MAX - 2,
                           2);
    if (tid != RT_NULL)
    {
      rt_thread_startup(tid);
    }
    else
    {
      state = -RT_ERROR;
    }

    /* register eth device */
    state = eth_device_init(&(at32_emac_device.parent), "e0");
    if (RT_EOK == state)
    {
      LOG_D("emac device init success");
    }
    else
    {
      LOG_E("emac device init faild: %d", state);
      state = -RT_ERROR;
      goto __exit;
    }
__exit:
    if (state != RT_EOK)
    {
      if (rx_buff)
      {
            rt_free(rx_buff);
      }

      if (tx_buff)
      {
            rt_free(tx_buff);
      }

      if (dma_rx_dscr_tab)
      {
            rt_free(dma_rx_dscr_tab);
      }

      if (dma_tx_dscr_tab)
      {
            rt_free(dma_tx_dscr_tab);
      }
    }

    return state;
}

INIT_DEVICE_EXPORT(rt_hw_at32_emac_init);


   

diaos 发表于 2023-3-29 13:28

雅特力的例程
我没有找到lan8720a 的例程,楼主方便提供下不

muyichuan2012 发表于 2023-3-30 10:38

本帖最后由 muyichuan2012 于 2023-3-30 10:39 编辑

如附件,示例代码演示AT32F407/437 EMAC接不同PHY配置方法使用方法。如下型号PHY已打通

DM9162DP83848C
LAN8720A
AR8032
IP101GR
RTL8201F
YT8512
IP175LL
IP179N


页: [1]
查看完整版本: LAN8720A 不能获取IP,求RT-Thread的驱动