ESP32-S3 IDF以太网接口移植
ESP-IDF 提供一系列灵活度高且兼具一致性的 API,为内部以太网 MAC (EMAC) 控制器和外部 SPI-Ethernet 模块提供支持。
因为ESP32-S3芯片内部不具有EMAC,本章采用外置的W5500嵌入式以太网控制器,SPI通信接口驱动。
函数调用关系:
仅需配置几个关键宏
idf example/ethernet/basic/ethernet_example_main.c例程移植
1. 初始化以太网入口
/*** @brief 网口状态管理机制* @date 2023-02-22*/
typedef struct _anker_eth_register_status_callback
{void (*eth_get_ip_address)(void); ///< 获取到IP地址void (*eth_connect_success)(void); ///< 已插上网口void (*eth_connect_loss)(void); ///< 拔掉网口void (*eth_notify_net_ok)(void); ///< 连接外网成功
} anker_eth_register_status_callback_t;sdk_error_code_t aws_register_eth_status_change_callback(anker_eth_register_status_callback_t *callback);/*初始化以太网入口*/
void aws_eth_status_init(void)
{anker_eth_register_status_callback_t cb = {.eth_connect_success = eth_connect_success, //以太网连接成功通知.eth_get_ip_address = eth_get_ip_addr_info, //获取ip地址信息.eth_connect_loss = eth_connect_lost, //以太网断联成功通知.eth_notify_net_ok = aws_eth_network_ok, //};aws_register_eth_status_change_callback(&cb);
}
2. 注册以太网状态接口
void eth_connect_lost(void)
{extern void aws_wifi_connect_ap_not_ssid(void);extern bool aws_get_is_ap_connect(void);if (aws_get_is_ap_connect() == false){aws_wifi_connect_ap_not_ssid();}//判断eth_disconnect是否为NULLif (_notify.eth_disconnect != NULL){_notify.eth_disconnect(E_ETH_DISCONNECT_REASON_NO_CABLE);}}void eth_connect_success(void)
{}void eth_get_ip_addr_info(void)
{
#if ANKER_WIFI_SDK_NET_COM_ENABLEaws_p2p_need_tcp_connect();
#endif#if ANKER_WIFI_SDK_MQTT_ENABLEextern void aws_iot_mqtt_connect_status_get(unsigned int *connect_status);unsigned int state = 0;aws_iot_mqtt_connect_status_get(&state);if (aws_get_is_use_mqtt() && (state == false)){// aws_notify_mqtt_client_net_disconnect();aws_mqtt_need_resubscribed();aws_notify_mqtt_client_net_connect();}
#endif#if ANKER_WIFI_SDK_HTTP_ENABLEextern void aws_cloud_notice_ap_connect(void);aws_cloud_notice_ap_connect();
#endif#if ANKER_WIFI_SDK_WEBRTC_TRANSMIT_ENABLEif (aws_get_is_use_webrtc() && aws_get_real_ka_type() == AWS_DEVICE_CONNECT_ROUTE){aws_webrtc_notify_ap_connect();}
#endif//判断eth_connect是否为NULLif (_notify.eth_connect != NULL){_notify.eth_connect();}
}void eth_connect_lost(void)
{extern void aws_wifi_connect_ap_not_ssid(void);extern bool aws_get_is_ap_connect(void);if (aws_get_is_ap_connect() == false){aws_wifi_connect_ap_not_ssid();}//判断eth_disconnect是否为NULLif (_notify.eth_disconnect != NULL){_notify.eth_disconnect(E_ETH_DISCONNECT_REASON_NO_CABLE);}}void aws_eth_network_ok(void)
{}
3. 以太网业务初始化入口
/*** @brief Ethernet init* */
void aws_ethernet_init(void)
{// Initialize Ethernet driveruint8_t eth_port_cnt = 0;esp_eth_handle_t *eth_handles;ESP_LOGI(TAG, "Ethernet init start");// ESP_ERROR_CHECK(aws_eth_drv_init(ð_handles, ð_port_cnt));esp_err_t ret = aws_eth_drv_init(ð_handles, ð_port_cnt);if (ret != ESP_OK){ESP_LOGE(TAG, "Ethernet init failed");return;}// Initialize TCP/IP network interface aka the esp-netif (should be called only once in application)ESP_ERROR_CHECK(esp_netif_init());// Create default event loop that running in background// ESP_ERROR_CHECK(esp_event_loop_create_default());aws_event_loop_creat_default_init();// Create instance(s) of esp-netif for Ethernet(s)if (eth_port_cnt == 1) {// Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and you don't need to modify// default esp-netif configuration parameters.esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();esp_netif_t *eth_netif = esp_netif_new(&cfg);// Attach Ethernet driver to TCP/IP stackESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0])));} else{// Use ESP_NETIF_INHERENT_DEFAULT_ETH when multiple Ethernet interfaces are used and so you need to modify// esp-netif configuration parameters for each interface (name, priority, etc.).esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();esp_netif_config_t cfg_spi = {.base = &esp_netif_config,.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH};char if_key_str[10];char if_desc_str[10];char num_str[3];for (int i = 0; i < eth_port_cnt; i++) {itoa(i, num_str, 10);strcat(strcpy(if_key_str, "ETH_"), num_str);strcat(strcpy(if_desc_str, "eth"), num_str);esp_netif_config.if_key = if_key_str;esp_netif_config.if_desc = if_desc_str;esp_netif_config.route_prio -= i*5;esp_netif_t *eth_netif = esp_netif_new(&cfg_spi);// Attach Ethernet driver to TCP/IP stackESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i])));}}// Register user defined event handersESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL));ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));// Start Ethernet driver state machinefor (int i = 0; i < eth_port_cnt; i++) {ESP_ERROR_CHECK(esp_eth_start(eth_handles[i]));}
}
4. 以太网驱动初始化
这里仅初始化一个SPI及以太网芯片。
注:esp_event_loop_create_default和esp_netif_init接口仅能调用初始化一次,若Wi-Fi已初始化过,这里就不再需要初始化,否则会报错
esp_err_t aws_eth_drv_init(esp_eth_handle_t *eth_handles_out[], uint8_t *eth_cnt_out)
{esp_err_t ret = ESP_OK;esp_eth_handle_t *eth_handles = NULL;uint8_t eth_cnt = 0;#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET || CONFIG_EXAMPLE_USE_SPI_ETHERNETESP_GOTO_ON_FALSE(eth_handles_out != NULL && eth_cnt_out != NULL, ESP_ERR_INVALID_ARG,err, TAG, "invalid arguments: initialized handles array or number of interfaces");eth_handles = calloc(SPI_ETHERNETS_NUM + INTERNAL_ETHERNETS_NUM, sizeof(esp_eth_handle_t));ESP_GOTO_ON_FALSE(eth_handles != NULL, ESP_ERR_NO_MEM, err, TAG, "no memory");#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNETeth_handles[eth_cnt] = eth_init_internal(NULL, NULL);ESP_GOTO_ON_FALSE(eth_handles[eth_cnt], ESP_FAIL, err, TAG, "internal Ethernet init failed");eth_cnt++;
#endif //CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET#if CONFIG_EXAMPLE_USE_SPI_ETHERNETESP_GOTO_ON_ERROR(spi_bus_init(), err, TAG, "SPI bus init failed");// Init specific SPI Ethernet module configuration from Kconfig (CS GPIO, Interrupt GPIO, etc.)spi_eth_module_config_t spi_eth_module_config[CONFIG_EXAMPLE_SPI_ETHERNETS_NUM] = { 0 };// INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 0);/* 初始化SPI_Ethernet_Module 配置 */spi_eth_module_config[0].spi_cs_gpio = CONFIG_EXAMPLE_ETH_SPI_CS0_GPIO; spi_eth_module_config[0].int_gpio = CONFIG_EXAMPLE_ETH_SPI_INT0_GPIO; spi_eth_module_config[0].polling_ms = CONFIG_EXAMPLE_ETH_SPI_POLLING0_MS; //采用中断方式,不需要轮询,设置为0 spi_eth_module_config[0].phy_reset_gpio = CONFIG_EXAMPLE_ETH_SPI_PHY_RST0_GPIO; spi_eth_module_config[0].phy_addr = CONFIG_EXAMPLE_ETH_SPI_PHY_ADDR0; ESP_LOGI(TAG, "spi_eth_module_config 0: spi_cs_gpio:%d, int_gpio:%d, polling_ms:%d, phy_reset_gpio:%d, phy_addr:%d",spi_eth_module_config[0].spi_cs_gpio, spi_eth_module_config[0].int_gpio, spi_eth_module_config[0].polling_ms, spi_eth_module_config[0].phy_reset_gpio, spi_eth_module_config[0].phy_addr);// The SPI Ethernet module(s) might not have a burned factory MAC address, hence use manually configured address(es).// In this example, Locally Administered MAC address derived from ESP32x base MAC address is used.// Note that Locally Administered OUI range should be used only when testing on a LAN under your control!uint8_t base_mac_addr[ETH_ADDR_LEN];ESP_GOTO_ON_ERROR(esp_efuse_mac_get_default(base_mac_addr), err, TAG, "get EFUSE MAC failed");uint8_t local_mac_1[ETH_ADDR_LEN];esp_derive_local_mac(local_mac_1, base_mac_addr);spi_eth_module_config[0].mac_addr = local_mac_1;
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM > 1INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 1);uint8_t local_mac_2[ETH_ADDR_LEN];base_mac_addr[ETH_ADDR_LEN - 1] += 1;esp_derive_local_mac(local_mac_2, base_mac_addr);spi_eth_module_config[1].mac_addr = local_mac_2;
#endif
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM > 2
#error Maximum number of supported SPI Ethernet devices is currently limited to 2 by this example.
#endiffor (int i = 0; i < CONFIG_EXAMPLE_SPI_ETHERNETS_NUM; i++) {eth_handles[eth_cnt] = eth_init_spi(&spi_eth_module_config[i], NULL, NULL);ESP_GOTO_ON_FALSE(eth_handles[eth_cnt], ESP_FAIL, err, TAG, "SPI Ethernet init failed");eth_cnt++;}
#endif // CONFIG_ETH_USE_SPI_ETHERNET
#elseESP_LOGD(TAG, "no Ethernet device selected to init");
#endif // CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET || CONFIG_EXAMPLE_USE_SPI_ETHERNET*eth_handles_out = eth_handles;*eth_cnt_out = eth_cnt;//打印返回值ESP_LOGI(TAG, "aws_eth_drv_init return :%d", ret);return ret;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET || CONFIG_EXAMPLE_USE_SPI_ETHERNET
err:free(eth_handles);return ret;
#endif
}
5. 以太网SPI模组初始化
注:两种以太网获取数据方式:中断和轮询。二者不能并存。
a. 若配置中断模式,设置一个IO口中断脚,那轮询时间应设置为0
b. 若配置轮询模式,轮询时间设置为100ms,那IO口中断脚应小于0(我配置为-1)
/*** @brief Ethernet SPI modules initialization** @param[in] spi_eth_module_config specific SPI Ethernet module configuration* @param[out] mac_out optionally returns Ethernet MAC object* @param[out] phy_out optionally returns Ethernet PHY object* @return* - esp_eth_handle_t if init succeeded* - NULL if init failed*/
static esp_eth_handle_t eth_init_spi(spi_eth_module_config_t *spi_eth_module_config, esp_eth_mac_t **mac_out, esp_eth_phy_t **phy_out)
{esp_eth_handle_t ret = NULL;// Init common MAC and PHY configs to defaulteth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();// Update PHY config based on board specific configurationphy_config.phy_addr = spi_eth_module_config->phy_addr;phy_config.reset_gpio_num = spi_eth_module_config->phy_reset_gpio;// Configure SPI interface for specific SPI modulespi_device_interface_config_t spi_devcfg = {.mode = 0,.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,.queue_size = 20,.spics_io_num = spi_eth_module_config->spi_cs_gpio};// Init vendor specific MAC config to default, and create new SPI Ethernet MAC instance// and new PHY instance based on board configuration
#if CONFIG_EXAMPLE_USE_KSZ8851SNLeth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);ksz8851snl_config.int_gpio_num = spi_eth_module_config->int_gpio;ksz8851snl_config.poll_period_ms = spi_eth_module_config->polling_ms;esp_eth_mac_t *mac = esp_eth_mac_new_ksz8851snl(&ksz8851snl_config, &mac_config);esp_eth_phy_t *phy = esp_eth_phy_new_ksz8851snl(&phy_config);
#elif CONFIG_EXAMPLE_USE_DM9051eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);dm9051_config.int_gpio_num = spi_eth_module_config->int_gpio;dm9051_config.poll_period_ms = spi_eth_module_config->polling_ms;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_USE_W5500eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);w5500_config.int_gpio_num = spi_eth_module_config->int_gpio;w5500_config.poll_period_ms = spi_eth_module_config->polling_ms;ESP_LOGI(TAG, "w5500_config: int_gpio_num:%d, poll_period_ms:%d", w5500_config.int_gpio_num, w5500_config.poll_period_ms);// ESP_LOGI(TAG, "mac_config: %d, %d, %d", mac_config.sw_reset_timeout_ms, mac_config.sw_reset_delay_ms, mac_config.flags);esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);
#endif //CONFIG_EXAMPLE_USE_W5500// Init Ethernet driver to default and install itesp_eth_handle_t eth_handle = NULL;esp_eth_config_t eth_config_spi = ETH_DEFAULT_CONFIG(mac, phy);ESP_GOTO_ON_FALSE(esp_eth_driver_install(ð_config_spi, ð_handle) == ESP_OK, NULL, err, TAG, "SPI Ethernet driver install failed");// The SPI Ethernet module might not have a burned factory MAC address, we can set it manually.if (spi_eth_module_config->mac_addr != NULL) {ESP_GOTO_ON_FALSE(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, spi_eth_module_config->mac_addr) == ESP_OK,NULL, err, TAG, "SPI Ethernet MAC address config failed");}if (mac_out != NULL) {*mac_out = mac;}if (phy_out != NULL) {*phy_out = phy;}return eth_handle;
err:if (eth_handle != NULL) {esp_eth_driver_uninstall(eth_handle);}if (mac != NULL) {mac->del(mac);}if (phy != NULL) {phy->del(phy);}return ret;
}
6. 事件注册回调
/** Event handler for Ethernet events */
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};/* we can get the ethernet driver handle from event data */esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;switch (event_id) {case ETHERNET_EVENT_CONNECTED:{esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);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:{ESP_LOGI(TAG, "Ethernet Link Down");eth_is_got_ip = false;if (eth_cbs.eth_connect_loss != NULL){eth_cbs.eth_connect_loss();}}break;case ETHERNET_EVENT_START:ESP_LOGI(TAG, "Ethernet Started");break;case ETHERNET_EVENT_STOP:ESP_LOGI(TAG, "Ethernet Stopped");break;default:break;}
}/** Event handler for IP_EVENT_ETH_GOT_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;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, "~~~~~~~~~~~");eth_is_got_ip = true;if (eth_cbs.eth_get_ip_address != NULL){eth_cbs.eth_get_ip_address();}extern void anker_sntp_query_init(void);anker_sntp_query_init();
}