当前位置: 首页 > news >正文

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(&eth_handles, &eth_port_cnt));esp_err_t ret = aws_eth_drv_init(&eth_handles, &eth_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, &eth_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(&eth_config_spi, &eth_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();
}


http://www.mrgr.cn/news/67861.html

相关文章:

  • 21-Terms 不正确的案例
  • Jetson Orin安装部署和使用(2)-远程控制和文件传输操作
  • 什么是电子邮件营销软件?最全百科
  • 阿里云多端低代码开发平台魔笔使用测评
  • 城镇住房保障:SpringBoot系统优化技巧
  • elementplus+vue3显示第几周(el-date-picker)
  • C++之priority_queue容器
  • Ethernet 系列(8)-- 基础学习::ARP
  • DeepSpeed分布式训练框架深度学习指南
  • day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
  • Linux 学习笔记(十八)—— 动静态库
  • python语言基础-4 常用模块-4.2 time模块
  • C++之unordered_set容器的使用
  • 罗德里格斯公式-计算一个点绕着任意直线旋转一定角度后的新位置
  • Java15
  • Easyconnect官网下载安装使用教程
  • Windows命令行常用快捷指令
  • UE5.4 PCG 自定义PCG蓝图节点
  • 函数式编程
  • 数据结构------栈(Java语言描述)
  • 前向-后向卡尔曼滤波器(Forward-Backward Kalman Filter)资料汇总
  • [CARLA系列--02]CARLA 0.9.15 在Windows下的安装教程(二)
  • 国药准字生发产品有哪些?这几款不错
  • CC协议解读
  • <网络> 协议
  • 【vue2.7.16系列】手把手教你搭建后台系统__登录接口返回信息调整(16)