From 123f3bf5dbea070e6c40d8a6d0020f2ff93d3eb1 Mon Sep 17 00:00:00 2001 From: Bogdan Kolendovskyy Date: Tue, 9 Apr 2024 12:03:52 +0200 Subject: [PATCH] Add multidrop support to LAN867x tcp transfer example --- lan867x/README.md | 57 ++++++++++++------- .../client/main/Kconfig.projbuild | 15 +++++ .../lan867x_tcp_sockets/client/main/client.c | 21 ++++++- .../server/main/Kconfig.projbuild | 15 +++++ .../lan867x_tcp_sockets/server/main/server.c | 47 ++++++++++++--- lan867x/idf_component.yml | 2 +- lan867x/include/esp_eth_phy_lan867x.h | 2 + lan867x/src/esp_eth_phy_lan867x.c | 19 +++++++ 8 files changed, 145 insertions(+), 33 deletions(-) create mode 100644 lan867x/examples/lan867x_tcp_sockets/client/main/Kconfig.projbuild create mode 100644 lan867x/examples/lan867x_tcp_sockets/server/main/Kconfig.projbuild diff --git a/lan867x/README.md b/lan867x/README.md index 2dc4060..c74c41b 100644 --- a/lan867x/README.md +++ b/lan867x/README.md @@ -24,25 +24,38 @@ phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO; esp_eth_phy_t *phy = esp_eth_phy_new_lan867x(&phy_config); ``` -#### You may need to setup the PLCA - -Depending on the task you are doing some additional setup may be required. -PLCA configuration is done using `esp_eth_ioctl` function with one of following commands. - -| Command | Argument | Action | -|------------------------------------|------------------|-----------------------------------------------------------------------------------------------------------| -| LAN867X_ETH_CMD_S_EN_PLCA | bool* enable | Enable (if true) or disable PLCA | -| LAN867X_ETH_CMD_G_EN_PLCA | bool* enable | Write PLCA status (true if enabled) to the location via pointer | -| LAN867X_ETH_CMD_S_PLCA_NCNT | uint8_t* count | Set node count to the value passed through the pointer | -| LAN867X_ETH_CMD_G_PLCA_NCNT | uint8_t* count | Write node count configured in the PLCA to the location via pointer | -| LAN867X_ETH_CMD_S_PLCA_ID | uint8_t* id | Set ID to the value passed through the pointer | -| LAN867X_ETH_CMD_G_PLCA_ID | uint8_t* id | Write ID configured in the PLCA to the location via pointer | -| LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY | uint8_t* node_id | Add additional transmit opportunity for chosen node | -| LAN867X_ETH_CMD_RM_TX_OPPORTUNITY | uint8_t* node_id | Remove additional transmit opportunity for chosen node | -| LAN867X_ETH_CMD_S_MAX_BURST_COUNT | uint8_t* maxcnt | Set max count of additonal packets transmitted during one frame, or 0 to disable PLCA burst | -| LAN867X_ETH_CMD_G_MAX_BURST_COUNT | uint8_t* maxcnt | Write max count of additonal packets transmitted during one frame to the location via pointer | -| LAN867X_ETH_CMD_S_BURST_TIMER | uint8_t* time | Set time during which additional packets can be sent in incriments of 100ns | -| LAN867X_ETH_CMD_G_BURST_TIMER | uint8_t* time | Write time during which additional packets can be sent in increments of 100ns to the location via pointer | -| LAN768X_ETH_CMD_PLCA_RST | | Perform reset of the PLCA | - -and you are ready to use the Ethernet driver as you are used to. For more information of how to use ESP-IDF Ethernet driver, visit [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html). +#### Collision avoidance methods: CSMA/CD and PLCA + +In most cases you can rely on the default CSMA/CD (Carrier Sense Multiple Access / Collision Detection) provided by the MAC layer, to avoid collisions when connecting multiple nodes. It requires zero configuration to work and does a good job of coordinating when data is sent. However, due to its nature, the delays between transmissions are random. For timing-critical applications, deterministic PLCA (PHY-Level Collision Avoidance) provides a guaranteed time period between transmissions along with improved throughput. Read more about PLCA and its usage at [ieee802.org](https://www.ieee802.org/3/cg/public/July2018/PLCA%20FAQ.pdf). + +PLCA requires setting at least PLCA ID, node count, and selecting one node as a coordinator but ensures precise and repeatable time between transmissions, and due to the flexibility of the transmit opportunity timer, burst mode and additional transmit opportunities you can fine-tune it to your needs. + +#### Configuring PLCA + +PLCA configuration is done using the `esp_eth_ioctl` function with one of the following commands. + +| Command | Argument | Action | +|------------------------------------|------------------|-----------------------------------------------------------------------------------------------| +| LAN867X_ETH_CMD_S_EN_PLCA | bool* enable | Enable (if true) or disable PLCA, PHY will use CDMA/CD if disabled | +| LAN867X_ETH_CMD_G_EN_PLCA | bool* enable | Write PLCA status (true if enabled) to the location via pointer | +| LAN867X_ETH_CMD_S_PLCA_NCNT | uint8_t* count | Set node count to the value passed through the pointer | +| LAN867X_ETH_CMD_G_PLCA_NCNT | uint8_t* count | Write node count configured in the PLCA to the location via pointer | +| LAN867X_ETH_CMD_S_PLCA_ID | uint8_t* id | Set ID to the value passed through the pointer | +| LAN867X_ETH_CMD_G_PLCA_ID | uint8_t* id | Write ID configured in the PLCA to the location via pointer | +| LAN867X_ETH_CMD_S_PLCA_TOT | uint8_t* time | Set PLCA Transmit Opportunity Timer in BTs | +| LAN867X_ETH_CMD_G_PLCA_TOT | uint8_t* time | Write Transmit Opportunity Timer value in BTs | +| LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY | uint8_t* node_id | Add additional transmit opportunity for chosen node | +| LAN867X_ETH_CMD_RM_TX_OPPORTUNITY | uint8_t* node_id | Remove additional transmit opportunity for chosen node | +| LAN867X_ETH_CMD_S_MAX_BURST_COUNT | uint8_t* maxcnt | Set max count of additonal packets transmitted during one frame, or 0 to disable PLCA burst | +| LAN867X_ETH_CMD_G_MAX_BURST_COUNT | uint8_t* maxcnt | Write max count of additonal packets transmitted during one frame to the location via pointer | +| LAN867X_ETH_CMD_S_BURST_TIMER | uint8_t* time | Set time during which additional packets in BTs | +| LAN867X_ETH_CMD_G_BURST_TIMER | uint8_t* time | Write time during which additional packets can be sent in BTs to the location via pointer | +| LAN768X_ETH_CMD_PLCA_RST | | Perform reset of the PLCA | + +One of the devices on the network must be a **coordinator** for which you need to set _node count_ to amount of connected nodes, and _ID_ to 0. +On all other nodes, only _ID_ is required, which must be unique for every node. + +After that the device is ready and the Ethernet driver can be used as normal. For more information on how to use the ESP-IDF Ethernet driver, visit the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html). + +--- +BT - Bit time with a value of 100 ns \ No newline at end of file diff --git a/lan867x/examples/lan867x_tcp_sockets/client/main/Kconfig.projbuild b/lan867x/examples/lan867x_tcp_sockets/client/main/Kconfig.projbuild new file mode 100644 index 0000000..e07ebba --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/client/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + config EXAMPLE_LAN867X_USE_PLCA + bool "Use PLCA" + default n + help + Use PLCA istead of CSMA/CD, in some cases PLCA may be more robust against collisions + + config EXAMPLE_LAN867X_PLCA_ID + depends on EXAMPLE_LAN867X_USE_PLCA + int "LAN867x PLCA id" + range 1 255 + default 1 + help + Set PLCA id (each device in the network must have unique PLCA id) +endmenu \ No newline at end of file diff --git a/lan867x/examples/lan867x_tcp_sockets/client/main/client.c b/lan867x/examples/lan867x_tcp_sockets/client/main/client.c index a28945c..6df1a06 100644 --- a/lan867x/examples/lan867x_tcp_sockets/client/main/client.c +++ b/lan867x/examples/lan867x_tcp_sockets/client/main/client.c @@ -4,10 +4,12 @@ #include "freertos/task.h" #include "esp_netif.h" #include "esp_eth.h" +#include "esp_eth_phy_lan867x.h" #include "esp_event.h" #include "esp_log.h" #include "ethernet_init.h" #include "lwip/sockets.h" +#include "sdkconfig.h" #define SOCKET_ADDRESS "192.168.1.1" #define SOCKET_PORT 5000 @@ -49,8 +51,21 @@ void app_main(void) ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); // Register user defined event handers ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + // Configure PLCA +#if CONFIG_EXAMPLE_LAN867X_USE_PLCA + // Configure PLCA with node number from config + uint8_t plca_id = CONFIG_EXAMPLE_LAN867X_PLCA_ID; + esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_PLCA_ID, &plca_id); + bool plca_en = false; + esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_EN_PLCA, &plca_en); +#endif // otherwise rely on CSMA/CD // Start Ethernet driver esp_eth_start(eth_handles[0]); + // Get mac address and save it as a string + char mac_str[18]; + uint8_t mac_data[6]; + esp_eth_ioctl(eth_handles[0], ETH_CMD_G_MAC_ADDR, &mac_data); + sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", mac_data[0], mac_data[1], mac_data[2], mac_data[3], mac_data[4], mac_data[5]); // Initialize Berkley socket char txbuffer[SOCKET_MAX_LENGTH] = {0}; int client_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -61,10 +76,10 @@ void app_main(void) // Wait until IP address is assigned to this device xSemaphoreTake(xGotIpSemaphore, portMAX_DELAY); int transmission_cnt = 0; + connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); while (1) { - connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); - snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 via LAN867x", ++transmission_cnt); - int bytesSent = send(client_fd, txbuffer, strlen(txbuffer), 0); + snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 (%s) via LAN867x", ++transmission_cnt, mac_str); + int bytesSent = send(client_fd, txbuffer, SOCKET_MAX_LENGTH, 0); ESP_LOGI(TAG, "Sent tranmission #%d which was %d bytes long.", transmission_cnt, bytesSent); vTaskDelay(pdMS_TO_TICKS(500)); } diff --git a/lan867x/examples/lan867x_tcp_sockets/server/main/Kconfig.projbuild b/lan867x/examples/lan867x_tcp_sockets/server/main/Kconfig.projbuild new file mode 100644 index 0000000..a8b39e0 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/server/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + config EXAMPLE_LAN867X_USE_PLCA + bool "Use PLCA" + default n + help + Use PLCA istead of CSMA/CD, in some cases PLCA may be more robust against collisions + + config EXAMPLE_LAN867X_PLCA_NODE_COUNT + depends on EXAMPLE_LAN867X_USE_PLCA + int "PLCA node count" + range 2 255 + default 2 + help + Set PLCA node count (amount of nodes in the network including this one) +endmenu \ No newline at end of file diff --git a/lan867x/examples/lan867x_tcp_sockets/server/main/server.c b/lan867x/examples/lan867x_tcp_sockets/server/main/server.c index eb6d370..09f8af9 100644 --- a/lan867x/examples/lan867x_tcp_sockets/server/main/server.c +++ b/lan867x/examples/lan867x_tcp_sockets/server/main/server.c @@ -5,18 +5,25 @@ #include "esp_netif.h" #include "esp_netif_net_stack.h" #include "esp_eth.h" +#include "esp_eth_phy_lan867x.h" #include "esp_event.h" #include "esp_log.h" #include "ethernet_init.h" #include "lwip/sockets.h" #define SOCKET_PORT 5000 -#define LISTENER_MAX_QUEUE 1 +#define LISTENER_MAX_QUEUE 10 #define SOCKET_MAX_LENGTH 128 static const char *TAG = "lan867x_server"; -/** Event handler for IP_EVENT_ETH_GOT_IP */ +/* Structure to store information about individual connection */ +struct connection_info { + int fd; + struct sockaddr_in address; +}; + +/* 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 *data) { ip_event_got_ip_t *event = (ip_event_got_ip_t *) data; @@ -69,6 +76,17 @@ void app_main(void) // Stop dhcp client and set a static IP address esp_netif_dhcpc_stop(eth_netif); esp_netif_set_ip_info(eth_netif, &ip_info); +#if CONFIG_EXAMPLE_LAN867X_USE_PLCA + // Configure PLCA as coordinator + uint8_t plca_nodes_count = CONFIG_EXAMPLE_LAN867X_PLCA_NODE_COUNT; + esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_PLCA_NCNT, &plca_nodes_count); + uint8_t plca_id = 0; + esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_PLCA_ID, &plca_id); + uint8_t plca_max_burst_count = 0; + esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_MAX_BURST_COUNT, &plca_max_burst_count); + bool plca_en = false; + esp_eth_ioctl(eth_handles[0], LAN867X_ETH_CMD_S_EN_PLCA, &plca_en); +#endif // otherwise rely on CSMA/CD // Start Ethernet driver state machine ESP_ERROR_CHECK(esp_eth_start(eth_handles[0])); // Initialize Berkley socket which will listen on port SOCKET_PORT for transmission from client @@ -76,6 +94,8 @@ void app_main(void) int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in address; socklen_t addrlen = sizeof(address); + fd_set ready; + struct timeval to = {.tv_sec = 0, .tv_usec = 500000}; int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); address.sin_family = AF_INET; @@ -83,12 +103,25 @@ void app_main(void) address.sin_port = htons(SOCKET_PORT); bind(server_fd, (struct sockaddr *)&address, sizeof(address)); // listen and wait for transmission to come + struct connection_info connections[LISTENER_MAX_QUEUE]; + int active_connections_count = 0; listen(server_fd, LISTENER_MAX_QUEUE); - int new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen); while (1) { - // when transmission comes - print it - recv(new_socket, rxbuffer, SOCKET_MAX_LENGTH, 0); - printf("Received: \"%s\" from %s.\n", rxbuffer, inet_ntoa(address.sin_addr)); + // Check if any connections are pending + FD_ZERO(&ready); + FD_SET(server_fd, &ready); + select(server_fd + 1, &ready, 0, 0, &to); + if (FD_ISSET(server_fd, &ready)) { + // accept new connection + struct sockaddr_in *current_address_ptr = &connections[active_connections_count].address; + connections[active_connections_count].fd = accept(server_fd, (struct sockaddr *) current_address_ptr, &addrlen); + active_connections_count++; + } + for (int i = 0; i < active_connections_count; i++) { + // when transmission comes - print it + memset(rxbuffer, 0, SOCKET_MAX_LENGTH); + read(connections[i].fd, rxbuffer, SOCKET_MAX_LENGTH); + printf("Received: \"%s\" from %s.\n", rxbuffer, inet_ntoa(connections[i].address.sin_addr)); + } } - close(new_socket); } \ No newline at end of file diff --git a/lan867x/idf_component.yml b/lan867x/idf_component.yml index a8f9daa..6381852 100644 --- a/lan867x/idf_component.yml +++ b/lan867x/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.9.0" +version: "1.0.0" targets: - esp32 description: LAN867x Ethernet PHY Driver diff --git a/lan867x/include/esp_eth_phy_lan867x.h b/lan867x/include/esp_eth_phy_lan867x.h index 8effc80..498406b 100644 --- a/lan867x/include/esp_eth_phy_lan867x.h +++ b/lan867x/include/esp_eth_phy_lan867x.h @@ -19,6 +19,8 @@ typedef enum { LAN867X_ETH_CMD_G_PLCA_NCNT, /*!< Get PLCA node count */ LAN867X_ETH_CMD_S_PLCA_ID, /*!< Set PLCA ID */ LAN867X_ETH_CMD_G_PLCA_ID, /*!< Get PLCA ID */ + LAN867x_ETH_CMD_S_PLCA_TOT, /*!< Set PLCA Transmit Opportunity Timer in incriments of 100ns */ + LAN867x_ETH_CMD_G_PLCA_TOT, /*!< Get PLCA Transmit Opportunity Timer in incriments of 100ns */ LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY, /*!< Add additional transmit opportunity for chosen node */ LAN867X_ETH_CMD_RM_TX_OPPORTUNITY, /*!< Remove additional transmit opportunity for chosen node */ LAN867X_ETH_CMD_S_MAX_BURST_COUNT, /*!< Set max count of additional packets, set to 0 to disable */ diff --git a/lan867x/src/esp_eth_phy_lan867x.c b/lan867x/src/esp_eth_phy_lan867x.c index 331811c..181d9ae 100644 --- a/lan867x/src/esp_eth_phy_lan867x.c +++ b/lan867x/src/esp_eth_phy_lan867x.c @@ -61,6 +61,15 @@ typedef union { } lan867x_plca_ctrl1_reg_t; #define ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR (0xCA02) +typedef union { + struct { + uint8_t reserved1; // Reserved + uint8_t totmr; // Transmit Opportunity Timer + }; + uint32_t val; +} lan867x_plca_totmr_reg_t; +#define ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR (0xCA04) + typedef union { struct { uint8_t maxbc; // Maximum burst count @@ -207,6 +216,7 @@ static esp_err_t lan867x_custom_ioctl(esp_eth_phy_t *phy, uint32_t cmd, void *da esp_eth_mediator_t *eth = phy_802_3->eth; lan867x_plca_ctrl0_reg_t plca_ctrl0; lan867x_plca_ctrl1_reg_t plca_ctrl1; + lan867x_plca_totmr_reg_t plca_totmr; lan867x_plca_burst_reg_t plca_burst_reg; lan867x_plca_multiple_id_reg_t plca_multiple_id_reg; switch (cmd) { @@ -244,6 +254,15 @@ static esp_err_t lan867x_custom_ioctl(esp_eth_phy_t *phy, uint32_t cmd, void *da ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, &plca_ctrl1.val), err, TAG, "read PLCA_CTRL1 failed"); *((uint8_t *) data) = plca_ctrl1.id; break; + case LAN867x_ETH_CMD_S_PLCA_TOT: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR, &plca_totmr.val), err, TAG, "read PLCA_TOTMR failed"); + plca_totmr.totmr = *((uint8_t *) data); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR, plca_totmr.val), err, TAG, "write PLCA_TOTMR failed"); + break; + case LAN867x_ETH_CMD_G_PLCA_TOT: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_TOTMR_REG_MMD_ADDR, &plca_totmr.val), err, TAG, "read PLCA_TOTMR failed"); + *((uint8_t *) data) = plca_totmr.totmr; + break; case LAN768X_ETH_CMD_PLCA_RST: ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, &plca_ctrl0.val), err, TAG, "read PLCA_CTRL0 failed"); plca_ctrl0.rst = true;