基于ESP32的IDF开发,以太网(ETH)例程解析(内置mac和外置phy)

本人使用的esp32的以太网硬件组合为内置MAC和外置PHY

具体配置流程请参考我上一篇文章《(5条消息) ESP32以太网(ETH)环境和参数配置,基于内部mac和外部PHY(LAN8720A)_荆棘谷555的博客-CSDN博客 》

配置完成,编译正常之后进行代码解析(为什么有这系列的文章,因为小编打算制作一个基于ESP32的wifi和以太网自动切换的物联设备)

当拿到一个IDF程序时,我们首先找到入口函数

void app_main(void)

本人已经删除了大部分无用代码(使用人根据自己的实际硬件进行修改)

void app_main(void)

{

    //有的时候莫名其妙WiFi 、 mac地址都报废,用这个擦除nvs就好了(新增)

    ESP_ERROR_CHECK(nvs_flash_erase());

    nvs_flash_init();//(新增)

    // 初始化TCP/IP网络接口(在应用程序中只能调用一次)

    ESP_ERROR_CHECK(esp_netif_init());

    // 创建在后台运行的默认事件循环

    ESP_ERROR_CHECK(esp_event_loop_create_default());

#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET

     //为idf以太网创建一个新的默认实例

    esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();

    esp_netif_t *eth_netif = esp_netif_new(&cfg);

     // 设置默认处理程序来处理TCP/IP内容

    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();

    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();

    phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;

    phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;

    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();

    esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;

    esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;

    /* 创建ESP32以太网MAC实例 */

   esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);

     /* 创建一个PHY实例LAN8720 */

    esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);

    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);

    esp_eth_handle_t eth_handle = NULL;

    /* 以太网驱动程序安装 */

    ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));

    /*连接TCP/IP协议栈*/

    ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));

#endif //CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET

    // 注册用户定义的事件处理程序

    ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));//ETH 的事件处理程序

    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));//IP 的事件处理程序

    /* start Ethernet driver state machine */

#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET

    /*启动以太网驱动程序状态机*/

    ESP_ERROR_CHECK(esp_eth_start(eth_handle));

#endif // CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET

}

在上述代码中,由于大部分都是封装好的,因此我们只需要关心部分函数,在上述代码中,主要部分基本已经固定,我们只需要按模版调用即可,我们需要关心的只有:

①回调函数处理

// 注册用户定义的事件处理程序

    ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));//ETH 的事件处理程序

    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));//IP 的事件处理程序

②启动ETH的时机

/*启动以太网驱动程序状态机,此函数要放在回调注册之后,它会触发回调*/

    ESP_ERROR_CHECK(esp_eth_start(eth_handle));

对于函数中的宏定义使用,vs code无法跳转问题,至今仍然无法解决(有哪个大佬解决了希望给一下指导)。可在乐鑫官网查看,或者用vscode打开idf安装位置的demo路径搜索查看,然后找到定义位置,将头文件拿过来包含进去,然后打开头文件在转到定义就能出现了。

下面看一下回调函数内的使用,用户一般也在此处修改功能。

/**以太网事件处理程序,只有插拔线检测,没有联网是否成功*/

static void eth_event_handler(void *arg, esp_event_base_t event_base,

                              int32_t event_id, void *event_data)

{

    uint8_t mac_addr[6] = {0};

     /*我们可以从事件数据中获得以太网驱动程序句柄*/

    esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;

    switch (event_id) {

    case ETHERNET_EVENT_CONNECTED:

         //插入网线立即进入此处(作用不大,有网线但是没能获取IP,仍然采用ETH重连)

        esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);

        ESP_LOGI(TAG, "CYY network Link is in");

        ESP_LOGI(TAG, "Ethernet Link Up");

        ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",

               mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

        break;

    case ETHERNET_EVENT_DISCONNECTED:

    //拔出网线立即进入此处,等待用户使用(网线拔出,启动wifi)

        ESP_LOGI(TAG, "CYY network Link is out");

        ESP_LOGI(TAG, "Ethernet Link Down");

        break;

    case ETHERNET_EVENT_START:

        //开机启动完成进入此处

        ESP_LOGI(TAG, "Ethernet Started");

        break;

    case ETHERNET_EVENT_STOP:

        ESP_LOGI(TAG, "Ethernet Stopped");

        break;

    default:

        break;

    }

}

/** IP_EVENT_ETH_GOT_IP的事件处理程序,联网获取IP成功回调*/

static void got_ip_event_handler(void *arg, esp_event_base_t event_base,

                                 int32_t event_id, void *event_data)

{

    ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;

    const esp_netif_ip_info_t *ip_info = &event->ip_info;

    //to do 获取IP成功进入此处,需要用户自行处理(一般启动访问服务器)

    ESP_LOGI(TAG, "CYY get ip succeed");

    ESP_LOGI(TAG, "Ethernet Got IP Address");

    ESP_LOGI(TAG, "~~~~~~~~~~~");

    ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));

    ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));

    ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));

    ESP_LOGI(TAG, "~~~~~~~~~~~");

}

根据log打印查看调用逻辑

首先,因为我们在main函数中调用了

/*启动以太网驱动程序状态机,此函数要放在回调注册之后,它会触发回调*/

    ESP_ERROR_CHECK(esp_eth_start(eth_handle));

因此触发了回调中的,我们增加了打印,我们并没有进行处理,告诉开发者已经启动

case ETHERNET_EVENT_START:

        //开机启动完成进入此处

        ESP_LOGI(TAG, "Ethernet Started");

此时,底层驱动会检测是否有网线插入(如果没有就没有下一步了,要做灵活的切换,可以加个超时进入wifi启动)(如果有网线接入,则会进入如下回调)

ETHERNET_EVENT_CONNECTED

调用函数

esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);//获取以太网接口 MAC 地址。

        ESP_LOGI(TAG, "CYY network Link is in");

        ESP_LOGI(TAG, "Ethernet Link Up");

        ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",

               mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

将设备的mac地址打印出来。

之后,下层以太网驱动将会进行IP地址获取,获取成功后,会进入以下回调函数

/** IP_EVENT_ETH_GOT_IP的事件处理程序,联网获取IP成功回调*/

static void got_ip_event_handler(void *arg, esp_event_base_t event_base,

                                 int32_t event_id, void *event_data)

{

    ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;

    const esp_netif_ip_info_t *ip_info = &event->ip_info;

    //to do 获取IP成功进入此处,需要用户自行处理(一般启动访问服务器)

    ESP_LOGI(TAG, "CYY get ip succeed");

    ESP_LOGI(TAG, "Ethernet Got IP Address");

    ESP_LOGI(TAG, "~~~~~~~~~~~");

    ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));

    ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));

    ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));

    ESP_LOGI(TAG, "~~~~~~~~~~~");

}

此时,说明以太网连接已经成功,我们可以进行tcp通信请求,可以利用mqtt和https去尝试连接后台了。

另外,如果此时网线拔出(或者路由器坏了断电等),此时会进入回调

ETHERNET_EVENT_DISCONNECTED

此时,我们可以进行wifi开启等功能。

猜你喜欢

转载自blog.csdn.net/m0_45068979/article/details/131220234
今日推荐