From 9efa2cdc59d777d89cb40712b2a1547ccdcbfcd1 Mon Sep 17 00:00:00 2001 From: xiao Date: Mon, 30 Mar 2026 18:08:54 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=AE=8C=E6=88=90R8=E8=A3=B8?= =?UTF-8?q?=E6=9C=BAlwIP=E7=A7=BB=E6=A4=8D=E5=B9=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/config.c | 804 +++++------------ App/config.h | 22 +- App/tcp_client.c | 535 ++++------- App/tcp_client.h | 2 + App/tcp_server.c | 489 ++++------ App/uart_trans.c | 703 ++++++--------- App/uart_trans.h | 125 +-- Core/Src/main.c | 126 ++- Core/Src/stm32f1xx_it.c | 20 +- Drivers/CH390/CH390_Interface.c | 1 + Drivers/LwIP/src/include/arch/cc.h | 9 +- Drivers/LwIP/src/include/arch/lwipopts.h | 284 +----- Drivers/LwIP/src/include/arch/sys_arch.h | 94 +- Drivers/LwIP/src/include/netif/ethernetif.h | 18 +- Drivers/LwIP/src/netif/ethernet.c | 213 +++++ Drivers/LwIP/src/netif/ethernetif.c | 426 +++------ Drivers/LwIP/src/netif/ethernetif.h | 46 +- MDK-ARM/TCP2UART.uvprojx | 832 +----------------- .../Third_Party/SEGGER_RTT/SEGGER_RTT.c | 59 ++ .../Third_Party/SEGGER_RTT/SEGGER_RTT.h | 37 + .../Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h | 37 + .../SEGGER_RTT/SEGGER_RTT_printf.c | 30 + 项目技术实现.md | 462 ++++------ 项目需求说明.md | 90 +- 24 files changed, 1845 insertions(+), 3619 deletions(-) create mode 100644 Drivers/LwIP/src/netif/ethernet.c create mode 100644 Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.c create mode 100644 Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.h create mode 100644 Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h create mode 100644 Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_printf.c diff --git a/App/config.c b/App/config.c index f30917b..35cbecf 100644 --- a/App/config.c +++ b/App/config.c @@ -1,113 +1,60 @@ /** * @file config.c - * @brief AT command configuration module implementation + * @brief Bare-metal AT command configuration module implementation. */ #include "config.h" + #include "flash_param.h" #include "usart.h" -#include "FreeRTOS.h" -#include "task.h" - -#include +#include #include #include -#include +#include -/*--------------------------------------------------------------------------- - * Private Definitions - *---------------------------------------------------------------------------*/ +#define CONFIG_RX_BUFFER_SIZE 128u +#define CONFIG_TX_BUFFER_SIZE 256u +#define CONFIG_CMD_MAX_LEN 128u -#define CONFIG_RX_BUFFER_SIZE 128 -#define CONFIG_TX_BUFFER_SIZE 256 -#define CONFIG_CMD_MAX_LEN 128 - -/* AT command prefixes */ -#define AT_PREFIX "AT+" -#define AT_QUERY "AT+?" - -/*--------------------------------------------------------------------------- - * Private Variables - *---------------------------------------------------------------------------*/ - -/* Current device configuration */ static device_config_t g_config; +static uint8_t g_rx_buffer[CONFIG_RX_BUFFER_SIZE]; +static volatile uint16_t g_rx_index; +static volatile bool g_rx_complete; +static volatile bool g_reset_requested; static uint32_t config_calc_crc(const device_config_t *cfg) { return flash_param_crc32(cfg, offsetof(device_config_t, crc)); } -/* UART1 reception buffer */ -static uint8_t g_rx_buffer[CONFIG_RX_BUFFER_SIZE]; -static volatile uint16_t g_rx_index = 0; -static volatile bool g_rx_complete = false; -static volatile bool g_reset_requested = false; - -/*--------------------------------------------------------------------------- - * Private Functions - String Utilities - *---------------------------------------------------------------------------*/ - -/** - * @brief Skip whitespace in string - */ static const char *skip_whitespace(const char *str) { - while (*str == ' ' || *str == '\t') - { - str++; + while (*str == ' ' || *str == '\t') { + ++str; } return str; } -/** - * @brief Trim trailing whitespace and newlines - */ static void trim_trailing(char *str) { - int len = strlen(str); - while (len > 0 && (str[len-1] == ' ' || str[len-1] == '\t' || - str[len-1] == '\r' || str[len-1] == '\n')) - { + int len = (int)strlen(str); + + while (len > 0 && (str[len - 1] == ' ' || str[len - 1] == '\t' || str[len - 1] == '\r' || str[len - 1] == '\n')) { str[--len] = '\0'; } } -/** - * @brief Compare string prefix (case-insensitive) - */ -static bool starts_with(const char *str, const char *prefix) -{ - while (*prefix) - { - char c1 = *str++; - char c2 = *prefix++; - - /* Convert to uppercase for comparison */ - if (c1 >= 'a' && c1 <= 'z') c1 -= 32; - if (c2 >= 'a' && c2 <= 'z') c2 -= 32; - - if (c1 != c2) - { - return false; - } - } - return true; -} - static bool equals_ignore_case(const char *a, const char *b) { - while (*a != '\0' && *b != '\0') - { + while (*a != '\0' && *b != '\0') { char c1 = *a++; char c2 = *b++; if (c1 >= 'a' && c1 <= 'z') c1 -= 32; if (c2 >= 'a' && c2 <= 'z') c2 -= 32; - if (c1 != c2) - { + if (c1 != c2) { return false; } } @@ -115,348 +62,93 @@ static bool equals_ignore_case(const char *a, const char *b) return (*a == '\0' && *b == '\0'); } -/*--------------------------------------------------------------------------- - * Private Functions - AT Command Handlers - *---------------------------------------------------------------------------*/ - -/** - * @brief Handle AT+IP command - */ -static at_result_t handle_ip(const char *value, char *response, uint16_t max_len) -{ - uint8_t ip[4]; - - if (config_str_to_ip(value, ip) != 0) - { - snprintf(response, max_len, "ERROR: Invalid IP format\r\n"); - return AT_INVALID_PARAM; - } - - memcpy(g_config.ip, ip, 4); - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+MASK command - */ -static at_result_t handle_mask(const char *value, char *response, uint16_t max_len) -{ - uint8_t mask[4]; - - if (config_str_to_ip(value, mask) != 0) - { - snprintf(response, max_len, "ERROR: Invalid mask format\r\n"); - return AT_INVALID_PARAM; - } - - memcpy(g_config.mask, mask, 4); - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+GW command - */ -static at_result_t handle_gw(const char *value, char *response, uint16_t max_len) -{ - uint8_t gw[4]; - - if (config_str_to_ip(value, gw) != 0) - { - snprintf(response, max_len, "ERROR: Invalid gateway format\r\n"); - return AT_INVALID_PARAM; - } - - memcpy(g_config.gw, gw, 4); - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+PORT command - */ -static at_result_t handle_port(const char *value, char *response, uint16_t max_len) -{ - int port = atoi(value); - - if (port < 1 || port > 65535) - { - snprintf(response, max_len, "ERROR: Invalid port (1-65535)\r\n"); - return AT_INVALID_PARAM; - } - - g_config.server_port = (uint16_t)port; - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+RIP command - */ -static at_result_t handle_rip(const char *value, char *response, uint16_t max_len) -{ - uint8_t ip[4]; - - if (config_str_to_ip(value, ip) != 0) - { - snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n"); - return AT_INVALID_PARAM; - } - - memcpy(g_config.remote_ip, ip, 4); - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+RPORT command - */ -static at_result_t handle_rport(const char *value, char *response, uint16_t max_len) -{ - int port = atoi(value); - - if (port < 1 || port > 65535) - { - snprintf(response, max_len, "ERROR: Invalid port (1-65535)\r\n"); - return AT_INVALID_PARAM; - } - - g_config.remote_port = (uint16_t)port; - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+BAUD1 command (UART2) - */ -static at_result_t handle_baud1(const char *value, char *response, uint16_t max_len) -{ - int baud = atoi(value); - - /* Validate common baud rates */ - if (baud != 9600 && baud != 19200 && baud != 38400 && - baud != 57600 && baud != 115200 && baud != 230400 && - baud != 460800 && baud != 921600) - { - snprintf(response, max_len, "ERROR: Invalid baud rate\r\n"); - return AT_INVALID_PARAM; - } - - g_config.uart2_baudrate = (uint32_t)baud; - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+BAUD2 command (UART3) - */ -static at_result_t handle_baud2(const char *value, char *response, uint16_t max_len) -{ - int baud = atoi(value); - - /* Validate common baud rates */ - if (baud != 9600 && baud != 19200 && baud != 38400 && - baud != 57600 && baud != 115200 && baud != 230400 && - baud != 460800 && baud != 921600) - { - snprintf(response, max_len, "ERROR: Invalid baud rate\r\n"); - return AT_INVALID_PARAM; - } - - g_config.uart3_baudrate = (uint32_t)baud; - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+MAC command - */ -static at_result_t handle_mac(const char *value, char *response, uint16_t max_len) -{ - uint8_t mac[6]; - - if (config_str_to_mac(value, mac) != 0) - { - snprintf(response, max_len, "ERROR: Invalid MAC format\r\n"); - return AT_INVALID_PARAM; - } - - memcpy(g_config.mac, mac, 6); - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+DHCP command - */ -static at_result_t handle_dhcp(const char *value, char *response, uint16_t max_len) -{ - int dhcp = atoi(value); - - if (dhcp != 0 && dhcp != 1) - { - snprintf(response, max_len, "ERROR: Invalid value (0 or 1)\r\n"); - return AT_INVALID_PARAM; - } - - g_config.dhcp_enable = (uint8_t)dhcp; - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; -} - -/** - * @brief Handle AT+SAVE command - */ -static at_result_t handle_save(char *response, uint16_t max_len) -{ - if (config_save() != 0) - { - snprintf(response, max_len, "ERROR: Save failed\r\n"); - return AT_SAVE_FAILED; - } - - snprintf(response, max_len, "OK: Configuration saved\r\n"); - return AT_OK; -} - -/** - * @brief Handle AT+RESET command - */ -static at_result_t handle_reset(char *response, uint16_t max_len) -{ - snprintf(response, max_len, "OK: Resetting...\r\n"); - g_reset_requested = true; - return AT_OK; -} - -/** - * @brief Handle AT+DEFAULT command - */ -static at_result_t handle_default(char *response, uint16_t max_len) -{ - config_set_defaults(); - snprintf(response, max_len, "OK: Defaults restored. Use AT+SAVE to save.\r\n"); - return AT_OK; -} - -/** - * @brief Handle AT+? query command - */ static at_result_t handle_query(char *response, uint16_t max_len) { - char ip_str[16], mask_str[16], gw_str[16], rip_str[16], mac_str[18]; - + char ip_str[16]; + char mask_str[16]; + char gw_str[16]; + char rip_str[16]; + char mac_str[18]; + config_ip_to_str(g_config.ip, ip_str); config_ip_to_str(g_config.mask, mask_str); config_ip_to_str(g_config.gw, gw_str); config_ip_to_str(g_config.remote_ip, rip_str); config_mac_to_str(g_config.mac, mac_str); - + snprintf(response, max_len, - "=== TCP2UART Configuration ===\r\n" - "MAC: %s\r\n" - "DHCP: %s\r\n" - "IP: %s\r\n" - "MASK: %s\r\n" - "GW: %s\r\n" - "PORT: %u\r\n" - "RIP: %s\r\n" - "RPORT: %u\r\n" - "BAUD1: %lu\r\n" - "BAUD2: %lu\r\n" - "==============================\r\n", - mac_str, - g_config.dhcp_enable ? "Enabled" : "Disabled", - ip_str, - mask_str, - gw_str, - g_config.server_port, - rip_str, - g_config.remote_port, - g_config.uart2_baudrate, - g_config.uart3_baudrate - ); - + "MAC: %s\r\n" + "DHCP: %u\r\n" + "IP: %s\r\n" + "MASK: %s\r\n" + "GW: %s\r\n" + "PORT: %u\r\n" + "RIP: %s\r\n" + "RPORT: %u\r\n" + "BAUD1: %lu\r\n" + "BAUD2: %lu\r\n", + mac_str, + g_config.dhcp_enable, + ip_str, + mask_str, + gw_str, + g_config.server_port, + rip_str, + g_config.remote_port, + g_config.uart2_baudrate, + g_config.uart3_baudrate); + return AT_OK; } -/*--------------------------------------------------------------------------- - * Public Functions - *---------------------------------------------------------------------------*/ - -/** - * @brief Initialize configuration module - */ int config_init(void) { - /* Load configuration from Flash */ + flash_param_init(); return config_load(); } -/** - * @brief Load configuration from Flash - */ int config_load(void) { - int ret; - - ret = flash_param_read(&g_config, sizeof(device_config_t)); - - if (ret != 0 || + if (flash_param_read(&g_config, sizeof(g_config)) != 0 || g_config.magic != CONFIG_MAGIC || g_config.version != CONFIG_VERSION || - g_config.crc != config_calc_crc(&g_config)) - { - /* Invalid or corrupted configuration, use defaults */ + g_config.crc != config_calc_crc(&g_config)) { config_set_defaults(); return -1; } - + return 0; } -/** - * @brief Save configuration to Flash - */ int config_save(void) { - /* Update magic and CRC before saving */ g_config.magic = CONFIG_MAGIC; g_config.version = CONFIG_VERSION; g_config.crc = config_calc_crc(&g_config); - - return flash_param_write(&g_config, sizeof(device_config_t)); + return flash_param_write(&g_config, sizeof(g_config)); } -/** - * @brief Reset configuration to factory defaults - */ void config_set_defaults(void) { - uint8_t default_ip[] = DEFAULT_IP; - uint8_t default_mask[] = DEFAULT_MASK; - uint8_t default_gw[] = DEFAULT_GW; - uint8_t default_mac[] = DEFAULT_MAC; - uint8_t default_rip[] = DEFAULT_REMOTE_IP; - - memset(&g_config, 0, sizeof(device_config_t)); - + const uint8_t default_ip[] = DEFAULT_IP; + const uint8_t default_mask[] = DEFAULT_MASK; + const uint8_t default_gw[] = DEFAULT_GW; + const uint8_t default_mac[] = DEFAULT_MAC; + const uint8_t default_rip[] = DEFAULT_REMOTE_IP; + + memset(&g_config, 0, sizeof(g_config)); g_config.magic = CONFIG_MAGIC; g_config.version = CONFIG_VERSION; - - memcpy(g_config.mac, default_mac, 6); + memcpy(g_config.mac, default_mac, sizeof(g_config.mac)); + memcpy(g_config.ip, default_ip, sizeof(g_config.ip)); + memcpy(g_config.mask, default_mask, sizeof(g_config.mask)); + memcpy(g_config.gw, default_gw, sizeof(g_config.gw)); + memcpy(g_config.remote_ip, default_rip, sizeof(g_config.remote_ip)); + g_config.dhcp_enable = DEFAULT_DHCP_ENABLE; - memcpy(g_config.ip, default_ip, 4); - memcpy(g_config.mask, default_mask, 4); - memcpy(g_config.gw, default_gw, 4); - g_config.server_port = DEFAULT_SERVER_PORT; - - memcpy(g_config.remote_ip, default_rip, 4); g_config.remote_port = DEFAULT_REMOTE_PORT; g_config.reconnect_interval = DEFAULT_RECONNECT_MS; - g_config.uart2_baudrate = DEFAULT_UART_BAUDRATE; g_config.uart3_baudrate = DEFAULT_UART_BAUDRATE; g_config.uart2_databits = DEFAULT_UART_DATABITS; @@ -468,305 +160,271 @@ void config_set_defaults(void) g_config.crc = config_calc_crc(&g_config); } -/** - * @brief Get current configuration (read-only) - */ const device_config_t *config_get(void) { return &g_config; } -/** - * @brief Get mutable configuration - */ device_config_t *config_get_mutable(void) { return &g_config; } -/** - * @brief Process AT command - */ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len) { char cmd_copy[CONFIG_CMD_MAX_LEN]; char *cmd_name; char *value; - at_result_t result = AT_UNKNOWN_CMD; + const char *p; - if (cmd == NULL || response == NULL || max_len == 0) - { + if (cmd == NULL || response == NULL || max_len == 0u) { return AT_ERROR; } - - /* Make a copy for modification */ - strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1); - cmd_copy[CONFIG_CMD_MAX_LEN - 1] = '\0'; + + strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1u); + cmd_copy[CONFIG_CMD_MAX_LEN - 1u] = '\0'; trim_trailing(cmd_copy); - - /* Skip leading whitespace */ - const char *p = skip_whitespace(cmd_copy); - - /* Check for AT+ prefix */ - if (!starts_with(p, "AT+")) - { - if (equals_ignore_case(p, "AT")) - { - snprintf(response, max_len, "OK\r\n"); - return AT_OK; - } + p = skip_whitespace(cmd_copy); + + if ((p[0] != 'A' && p[0] != 'a') || (p[1] != 'T' && p[1] != 't')) { snprintf(response, max_len, "ERROR: Unknown command\r\n"); return AT_UNKNOWN_CMD; } - - /* Move past AT+ */ + + if (p[2] == '\0') { + snprintf(response, max_len, "OK\r\n"); + return AT_OK; + } + + if (p[2] != '+') { + snprintf(response, max_len, "ERROR: Unknown command\r\n"); + return AT_UNKNOWN_CMD; + } + p += 3; - - /* Find '=' separator if present */ value = strchr((char *)p, '='); - if (value != NULL) - { - *value = '\0'; /* Terminate command part */ - value++; /* Point to value */ + if (value != NULL) { + *value = '\0'; + ++value; value = (char *)skip_whitespace(value); } cmd_name = (char *)p; trim_trailing(cmd_name); - - /* Process commands */ - if (equals_ignore_case(cmd_name, "IP") && value != NULL) - { - result = handle_ip(value, response, max_len); + + if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL) { + return handle_query(response, max_len); } - else if (equals_ignore_case(cmd_name, "MASK") && value != NULL) - { - result = handle_mask(value, response, max_len); + if (equals_ignore_case(cmd_name, "SAVE") && value == NULL) { + if (config_save() != 0) { + snprintf(response, max_len, "ERROR: Save failed\r\n"); + return AT_SAVE_FAILED; + } + snprintf(response, max_len, "OK: Configuration saved\r\n"); + return AT_OK; } - else if (equals_ignore_case(cmd_name, "GW") && value != NULL) - { - result = handle_gw(value, response, max_len); + if (equals_ignore_case(cmd_name, "RESET") && value == NULL) { + g_reset_requested = true; + snprintf(response, max_len, "OK: Resetting...\r\n"); + return AT_OK; } - else if (equals_ignore_case(cmd_name, "PORT") && value != NULL) - { - result = handle_port(value, response, max_len); + if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL) { + config_set_defaults(); + snprintf(response, max_len, "OK: Defaults restored\r\n"); + return AT_OK; } - else if (equals_ignore_case(cmd_name, "RIP") && value != NULL) - { - result = handle_rip(value, response, max_len); + if (equals_ignore_case(cmd_name, "IP") && value != NULL) { + if (config_str_to_ip(value, g_config.ip) != 0) { + snprintf(response, max_len, "ERROR: Invalid IP format\r\n"); + return AT_INVALID_PARAM; + } + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "RPORT") && value != NULL) - { - result = handle_rport(value, response, max_len); + if (equals_ignore_case(cmd_name, "MASK") && value != NULL) { + if (config_str_to_ip(value, g_config.mask) != 0) { + snprintf(response, max_len, "ERROR: Invalid mask format\r\n"); + return AT_INVALID_PARAM; + } + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL) - { - result = handle_baud1(value, response, max_len); + if (equals_ignore_case(cmd_name, "GW") && value != NULL) { + if (config_str_to_ip(value, g_config.gw) != 0) { + snprintf(response, max_len, "ERROR: Invalid gateway format\r\n"); + return AT_INVALID_PARAM; + } + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL) - { - result = handle_baud2(value, response, max_len); + if (equals_ignore_case(cmd_name, "RIP") && value != NULL) { + if (config_str_to_ip(value, g_config.remote_ip) != 0) { + snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n"); + return AT_INVALID_PARAM; + } + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "MAC") && value != NULL) - { - result = handle_mac(value, response, max_len); + if (equals_ignore_case(cmd_name, "MAC") && value != NULL) { + if (config_str_to_mac(value, g_config.mac) != 0) { + snprintf(response, max_len, "ERROR: Invalid MAC format\r\n"); + return AT_INVALID_PARAM; + } + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "DHCP") && value != NULL) - { - result = handle_dhcp(value, response, max_len); + if (equals_ignore_case(cmd_name, "PORT") && value != NULL) { + int port = atoi(value); + if (port < 1 || port > 65535) { + snprintf(response, max_len, "ERROR: Invalid port\r\n"); + return AT_INVALID_PARAM; + } + g_config.server_port = (uint16_t)port; + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "SAVE") && value == NULL) - { - result = handle_save(response, max_len); + if (equals_ignore_case(cmd_name, "RPORT") && value != NULL) { + int port = atoi(value); + if (port < 1 || port > 65535) { + snprintf(response, max_len, "ERROR: Invalid port\r\n"); + return AT_INVALID_PARAM; + } + g_config.remote_port = (uint16_t)port; + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "RESET") && value == NULL) - { - result = handle_reset(response, max_len); + if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL) { + g_config.uart2_baudrate = (uint32_t)atoi(value); + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL) - { - result = handle_default(response, max_len); + if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL) { + g_config.uart3_baudrate = (uint32_t)atoi(value); + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL) - { - result = handle_query(response, max_len); + if (equals_ignore_case(cmd_name, "DHCP") && value != NULL) { + int dhcp = atoi(value); + if (dhcp != 0 && dhcp != 1) { + snprintf(response, max_len, "ERROR: Invalid value\r\n"); + return AT_INVALID_PARAM; + } + if (dhcp != 0) { + snprintf(response, max_len, "ERROR: DHCP disabled in this build\r\n"); + return AT_INVALID_PARAM; + } + g_config.dhcp_enable = (uint8_t)dhcp; + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; } - else - { - snprintf(response, max_len, "ERROR: Unknown command\r\n"); - result = AT_UNKNOWN_CMD; - } - - return result; + + snprintf(response, max_len, "ERROR: Unknown command\r\n"); + return AT_UNKNOWN_CMD; } -/** - * @brief Format IP address to string - */ void config_ip_to_str(const uint8_t *ip, char *str) { - sprintf(str, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + sprintf(str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); } -/** - * @brief Parse IP address from string - */ int config_str_to_ip(const char *str, uint8_t *ip) { int a, b, c, d; - - if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) - { + + if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) { return -1; } - - if (a < 0 || a > 255 || b < 0 || b > 255 || - c < 0 || c > 255 || d < 0 || d > 255) - { + if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255) { return -1; } - + ip[0] = (uint8_t)a; ip[1] = (uint8_t)b; ip[2] = (uint8_t)c; ip[3] = (uint8_t)d; - return 0; } -/** - * @brief Format MAC address to string - */ void config_mac_to_str(const uint8_t *mac, char *str) { - sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } -/** - * @brief Parse MAC address from string - */ int config_str_to_mac(const char *str, uint8_t *mac) { int a[6]; - - if (sscanf(str, "%x:%x:%x:%x:%x:%x", - &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) - { - /* Try alternate format with dashes */ - if (sscanf(str, "%x-%x-%x-%x-%x-%x", - &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) - { - return -1; - } + + if (sscanf(str, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6 && + sscanf(str, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) { + return -1; } - - for (int i = 0; i < 6; i++) - { - if (a[i] < 0 || a[i] > 255) - { + + for (int i = 0; i < 6; ++i) { + if (a[i] < 0 || a[i] > 255) { return -1; } mac[i] = (uint8_t)a[i]; } - + return 0; } -/** - * @brief UART1 IDLE interrupt handler - */ void config_uart_idle_handler(void) { uint16_t dma_counter = __HAL_DMA_GET_COUNTER(huart1.hdmarx); - uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter; - - if (len > 0) - { + uint16_t len = (uint16_t)(CONFIG_RX_BUFFER_SIZE - dma_counter); + + if (len > 0u) { g_rx_index = len; g_rx_complete = true; } - - /* Stop DMA and restart */ + HAL_UART_DMAStop(&huart1); HAL_UART_Receive_DMA(&huart1, g_rx_buffer, CONFIG_RX_BUFFER_SIZE); } -/** - * @brief Start UART1 reception - */ void config_start_reception(void) { - /* Enable IDLE interrupt */ __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); - - /* Start DMA reception */ HAL_UART_Receive_DMA(&huart1, g_rx_buffer, CONFIG_RX_BUFFER_SIZE); } -/** - * @brief Configuration task - */ -void config_task(void *argument) +void config_poll(void) { char response[CONFIG_TX_BUFFER_SIZE]; char cmd_buffer[CONFIG_CMD_MAX_LEN]; at_result_t result; - - (void)argument; - - /* Initialize configuration */ - config_init(); - - /* Start UART1 reception */ - config_start_reception(); - - /* Send startup message */ - snprintf(response, sizeof(response), - "\r\n=== TCP2UART v1.0 ===\r\n" - "Type AT+? for configuration\r\n\r\n"); - HAL_UART_Transmit(&huart1, (uint8_t *)response, strlen(response), 1000); - - while (1) - { - /* Wait for command */ - if (g_rx_complete) - { - /* Copy command and null-terminate */ - uint16_t len = g_rx_index; - if (len >= CONFIG_CMD_MAX_LEN) - { - len = CONFIG_CMD_MAX_LEN - 1; - } - memcpy(cmd_buffer, g_rx_buffer, len); - cmd_buffer[len] = '\0'; - - /* Reset reception state */ - g_rx_complete = false; - g_rx_index = 0; - - /* Process command */ - result = config_process_at_cmd(cmd_buffer, response, sizeof(response)); - - /* Send response */ - HAL_UART_Transmit(&huart1, (uint8_t *)response, strlen(response), 1000); + uint16_t len; - if (g_reset_requested) - { - g_reset_requested = false; - vTaskDelay(pdMS_TO_TICKS(100)); - NVIC_SystemReset(); - } - - /* Handle reboot needed */ - if (result == AT_NEED_REBOOT) - { - HAL_UART_Transmit(&huart1, - (uint8_t *)"Note: Use AT+SAVE then AT+RESET to apply changes\r\n", - 51, 1000); - } - } - - vTaskDelay(pdMS_TO_TICKS(10)); + if (!g_rx_complete) { + return; + } + + len = g_rx_index; + if (len >= CONFIG_CMD_MAX_LEN) { + len = CONFIG_CMD_MAX_LEN - 1u; + } + + memcpy(cmd_buffer, g_rx_buffer, len); + cmd_buffer[len] = '\0'; + g_rx_complete = false; + g_rx_index = 0u; + + result = config_process_at_cmd(cmd_buffer, response, sizeof(response)); + HAL_UART_Transmit(&huart1, (uint8_t *)response, (uint16_t)strlen(response), 1000u); + + if (result == AT_NEED_REBOOT) { + static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n"; + HAL_UART_Transmit(&huart1, (uint8_t *)hint, sizeof(hint) - 1u, 1000u); } } + +bool config_is_reset_requested(void) +{ + return g_reset_requested; +} + +void config_clear_reset_requested(void) +{ + g_reset_requested = false; +} diff --git a/App/config.h b/App/config.h index 425a109..db2d42f 100644 --- a/App/config.h +++ b/App/config.h @@ -144,13 +144,6 @@ device_config_t *config_get_mutable(void); */ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len); -/** - * @brief Configuration task (for FreeRTOS) - * Handles UART1 reception and AT command processing - * @param argument Task argument (unused) - */ -void config_task(void *argument); - /** * @brief UART1 IDLE interrupt handler for config module */ @@ -161,6 +154,21 @@ void config_uart_idle_handler(void); */ void config_start_reception(void); +/** + * @brief Poll configuration UART and process pending AT commands + */ +void config_poll(void); + +/** + * @brief Check whether AT+RESET requested a system reset + */ +bool config_is_reset_requested(void); + +/** + * @brief Clear the pending reset request flag + */ +void config_clear_reset_requested(void); + /** * @brief Format IP address to string * @param ip IP address bytes diff --git a/App/tcp_client.c b/App/tcp_client.c index 282f301..95d89e0 100644 --- a/App/tcp_client.c +++ b/App/tcp_client.c @@ -1,431 +1,276 @@ /** * @file tcp_client.c - * @brief TCP Client module implementation for transparent transmission with UART3 + * @brief lwIP RAW TCP client for the UART3 bridge. */ #include "tcp_client.h" -#include "config.h" -#include "lwip/opt.h" -#include "lwip/tcp.h" -#include "lwip/sys.h" -#include "lwip/sockets.h" -#include "FreeRTOS.h" -#include "task.h" -#include "stream_buffer.h" +#include "main.h" + +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/tcp.h" #include -/*--------------------------------------------------------------------------- - * Private Variables - *---------------------------------------------------------------------------*/ +typedef struct { + struct tcp_pcb *pcb; + uint8_t rx_ring[TCP_CLIENT_RX_BUFFER_SIZE]; + uint16_t rx_head; + uint16_t rx_tail; + uint32_t next_retry_ms; + tcp_client_config_t config; + tcp_client_status_t status; +} tcp_client_ctx_t; -/* Client configuration */ -static tcp_client_config_t client_config = { - .server_ip = {192, 168, 1, 100}, - .server_port = TCP_CLIENT_DEFAULT_PORT, - .auto_reconnect = true, - .reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS -}; +static tcp_client_ctx_t g_client; -/* Client status */ -static tcp_client_status_t client_status = { - .state = TCP_CLIENT_STATE_IDLE, - .rx_bytes = 0, - .tx_bytes = 0, - .reconnect_count = 0, - .errors = 0 -}; - -/* Socket descriptor */ -static int client_socket = -1; - -/* Stream buffers for UART integration */ -static StreamBufferHandle_t rx_stream = NULL; /* TCP RX -> UART TX */ -static StreamBufferHandle_t tx_stream = NULL; /* UART RX -> TCP TX */ - -/*--------------------------------------------------------------------------- - * Private Functions - *---------------------------------------------------------------------------*/ - -static int tcp_client_send_all(int sock, const uint8_t *data, uint16_t len) +static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size) { - uint16_t total = 0; + return (head >= tail) ? (uint16_t)(size - head + tail - 1u) : (uint16_t)(tail - head - 1u); +} - while (total < len) - { - int sent = send(sock, data + total, len - total, 0); - if (sent > 0) - { - total += (uint16_t)sent; +static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg; + struct pbuf *q; + + if (err != ERR_OK) { + if (p != NULL) { + pbuf_free(p); } - else - { - return -1; + return err; + } + + if (p == NULL) { + ctx->pcb = NULL; + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; + return ERR_OK; + } + + for (q = p; q != NULL; q = q->next) { + const uint8_t *src = (const uint8_t *)q->payload; + for (uint16_t i = 0; i < q->len; ++i) { + if (ring_free(ctx->rx_head, ctx->rx_tail, TCP_CLIENT_RX_BUFFER_SIZE) == 0u) { + ctx->status.errors++; + break; + } + ctx->rx_ring[ctx->rx_head] = src[i]; + ctx->rx_head = (uint16_t)((ctx->rx_head + 1u) % TCP_CLIENT_RX_BUFFER_SIZE); + ctx->status.rx_bytes++; } } - return (int)total; + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + return ERR_OK; } -/** - * @brief Internal connect function - */ -static int tcp_client_do_connect(void) +static err_t tcp_client_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { - struct sockaddr_in server_addr; - int ret; - - if (client_socket >= 0) - { - /* Already connected */ - return 0; - } - - client_status.state = TCP_CLIENT_STATE_CONNECTING; - - /* Create socket */ - client_socket = socket(AF_INET, SOCK_STREAM, 0); - if (client_socket < 0) - { - client_status.state = TCP_CLIENT_STATE_ERROR; - client_status.errors++; - return -1; - } - - /* Prepare server address */ - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(client_config.server_port); - server_addr.sin_addr.s_addr = ((uint32_t)client_config.server_ip[0]) | - ((uint32_t)client_config.server_ip[1] << 8) | - ((uint32_t)client_config.server_ip[2] << 16) | - ((uint32_t)client_config.server_ip[3] << 24); - - /* Connect to server */ - ret = connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)); - if (ret < 0) - { - close(client_socket); - client_socket = -1; - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - client_status.errors++; - return -1; - } - - client_status.state = TCP_CLIENT_STATE_CONNECTED; - - return 0; + tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg; + (void)pcb; + ctx->status.tx_bytes += len; + return ERR_OK; } -/*--------------------------------------------------------------------------- - * Public Functions - *---------------------------------------------------------------------------*/ +static void tcp_client_on_err(void *arg, err_t err) +{ + tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg; + (void)err; + ctx->pcb = NULL; + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->status.errors++; + ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; +} + +static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg; + + if (err != ERR_OK) { + ctx->pcb = NULL; + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->status.errors++; + ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; + return err; + } + + ctx->pcb = pcb; + ctx->status.state = TCP_CLIENT_STATE_CONNECTED; + tcp_arg(pcb, ctx); + tcp_recv(pcb, tcp_client_on_recv); + tcp_sent(pcb, tcp_client_on_sent); + tcp_err(pcb, tcp_client_on_err); + return ERR_OK; +} -/** - * @brief Initialize TCP Client module - */ int tcp_client_init(const tcp_client_config_t *config) { - if (config != NULL) - { - memcpy(&client_config, config, sizeof(tcp_client_config_t)); + memset(&g_client, 0, sizeof(g_client)); + g_client.config.server_ip[0] = 192u; + g_client.config.server_ip[1] = 168u; + g_client.config.server_ip[2] = 1u; + g_client.config.server_ip[3] = 100u; + g_client.config.server_port = TCP_CLIENT_DEFAULT_PORT; + g_client.config.auto_reconnect = true; + g_client.config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS; + g_client.status.state = TCP_CLIENT_STATE_IDLE; + + if (config != NULL) { + g_client.config = *config; } - - /* Create stream buffers */ - if (rx_stream == NULL) - { - rx_stream = xStreamBufferCreate(TCP_CLIENT_RX_BUFFER_SIZE, 1); - if (rx_stream == NULL) - { - return -1; - } - } - - if (tx_stream == NULL) - { - tx_stream = xStreamBufferCreate(TCP_CLIENT_TX_BUFFER_SIZE, 1); - if (tx_stream == NULL) - { - return -1; - } - } - - client_status.state = TCP_CLIENT_STATE_IDLE; - + return 0; } -/** - * @brief Connect to remote server - */ int tcp_client_connect(void) { - return tcp_client_do_connect(); + struct tcp_pcb *pcb; + ip_addr_t remote_addr; + err_t err; + + if (g_client.pcb != NULL) { + return 0; + } + + pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + if (pcb == NULL) { + g_client.status.errors++; + return -1; + } + + IP_ADDR4(&remote_addr, + g_client.config.server_ip[0], + g_client.config.server_ip[1], + g_client.config.server_ip[2], + g_client.config.server_ip[3]); + + g_client.status.state = TCP_CLIENT_STATE_CONNECTING; + tcp_arg(pcb, &g_client); + err = tcp_connect(pcb, &remote_addr, g_client.config.server_port, tcp_client_on_connected); + if (err != ERR_OK) { + tcp_abort(pcb); + g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED; + g_client.status.errors++; + g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms; + return -1; + } + + g_client.pcb = pcb; + return 0; } -/** - * @brief Disconnect from server - */ int tcp_client_disconnect(void) { - if (client_socket >= 0) - { - close(client_socket); - client_socket = -1; + if (g_client.pcb != NULL) { + tcp_arg(g_client.pcb, NULL); + tcp_recv(g_client.pcb, NULL); + tcp_sent(g_client.pcb, NULL); + tcp_err(g_client.pcb, NULL); + tcp_abort(g_client.pcb); + g_client.pcb = NULL; } - - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - + + g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED; return 0; } -/** - * @brief Send data to server - */ int tcp_client_send(const uint8_t *data, uint16_t len) { - int sent; - - if (client_socket < 0) - { + err_t err; + + if (g_client.pcb == NULL || data == NULL || len == 0u) { return -1; } - - sent = tcp_client_send_all(client_socket, data, len); - if (sent > 0) - { - client_status.tx_bytes += sent; + + if (tcp_sndbuf(g_client.pcb) < len) { + return 0; } - else if (sent < 0) - { - /* Connection error */ - close(client_socket); - client_socket = -1; - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - client_status.errors++; + + err = tcp_write(g_client.pcb, data, len, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + g_client.status.errors++; + return -1; } - - return sent; + + err = tcp_output(g_client.pcb); + if (err != ERR_OK) { + g_client.status.errors++; + return -1; + } + + return (int)len; } -/** - * @brief Receive data from server - */ int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms) { - int received; - struct timeval tv; - - if (client_socket < 0) - { + uint16_t copied = 0u; + (void)timeout_ms; + + if (data == NULL || max_len == 0u) { return -1; } - - /* Set receive timeout */ - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - - received = recv(client_socket, data, max_len, 0); - if (received > 0) - { - client_status.rx_bytes += received; + + while (copied < max_len && g_client.rx_tail != g_client.rx_head) { + data[copied++] = g_client.rx_ring[g_client.rx_tail]; + g_client.rx_tail = (uint16_t)((g_client.rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE); } - else if (received == 0) - { - /* Connection closed by server */ - close(client_socket); - client_socket = -1; - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - } - - return received; + + return (int)copied; } -/** - * @brief Check if connected to server - */ bool tcp_client_is_connected(void) { - return (client_socket >= 0); + return (g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTED); } -/** - * @brief Update server configuration - */ int tcp_client_set_server(const uint8_t *ip, uint16_t port) { - if (ip == NULL) - { + if (ip == NULL || port == 0u) { return -1; } - - /* Disconnect if connected */ - if (client_socket >= 0) - { - tcp_client_disconnect(); - } - - memcpy(client_config.server_ip, ip, 4); - client_config.server_port = port; - + + memcpy(g_client.config.server_ip, ip, 4u); + g_client.config.server_port = port; return 0; } -/** - * @brief Get TCP Client status - */ void tcp_client_get_status(tcp_client_status_t *status) { - if (status != NULL) - { - memcpy(status, &client_status, sizeof(tcp_client_status_t)); + if (status != NULL) { + *status = g_client.status; } } -/** - * @brief Get RX StreamBuffer handle - */ void *tcp_client_get_rx_stream(void) { - return rx_stream; + return NULL; } -/** - * @brief Get TX StreamBuffer handle - */ void *tcp_client_get_tx_stream(void) { - return tx_stream; + return NULL; } -/** - * @brief TCP Client task - */ void tcp_client_task(void *argument) { - const device_config_t *cfg; - tcp_client_config_t task_cfg; - uint8_t rx_buffer[256]; - uint8_t tx_buffer[256]; - int received; - size_t tx_len; - fd_set read_fds; - struct timeval tv; - uint32_t reconnect_timer = 0; - (void)argument; - - /* Initialize client */ - task_cfg.server_ip[0] = 192; - task_cfg.server_ip[1] = 168; - task_cfg.server_ip[2] = 1; - task_cfg.server_ip[3] = 100; - task_cfg.server_port = TCP_CLIENT_DEFAULT_PORT; - task_cfg.auto_reconnect = true; - task_cfg.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS; +} - cfg = config_get(); - if (cfg != NULL) - { - memcpy(task_cfg.server_ip, cfg->remote_ip, sizeof(task_cfg.server_ip)); - if (cfg->remote_port > 0) - { - task_cfg.server_port = cfg->remote_port; - } - if (cfg->reconnect_interval > 0) - { - task_cfg.reconnect_interval_ms = cfg->reconnect_interval; - } +void tcp_client_poll(void) +{ + uint32_t now; + + if (!g_client.config.auto_reconnect || g_client.pcb != NULL) { + return; } - tcp_client_init(&task_cfg); - - while (1) - { - /* Handle connection state */ - if (client_socket < 0) - { - /* Not connected - try to reconnect */ - if (client_config.auto_reconnect) - { - if (xTaskGetTickCount() - reconnect_timer >= pdMS_TO_TICKS(client_config.reconnect_interval_ms)) - { - if (tcp_client_do_connect() == 0) - { - /* Connected successfully */ - client_status.reconnect_count++; - } - reconnect_timer = xTaskGetTickCount(); - } - } - - /* Wait before retry */ - vTaskDelay(pdMS_TO_TICKS(100)); - continue; - } - - /* Handle data transfer if connected */ - - /* Check for data from TCP server */ - FD_ZERO(&read_fds); - FD_SET(client_socket, &read_fds); - tv.tv_sec = 0; - tv.tv_usec = 10000; /* 10ms timeout */ - - if (select(client_socket + 1, &read_fds, NULL, NULL, &tv) > 0) - { - if (FD_ISSET(client_socket, &read_fds)) - { - received = recv(client_socket, rx_buffer, sizeof(rx_buffer), 0); - if (received > 0) - { - /* Forward to UART via stream buffer */ - xStreamBufferSend(rx_stream, rx_buffer, received, pdMS_TO_TICKS(10)); - client_status.rx_bytes += received; - } - else if (received == 0) - { - /* Connection closed */ - close(client_socket); - client_socket = -1; - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - reconnect_timer = xTaskGetTickCount(); - } - else - { - /* Error */ - close(client_socket); - client_socket = -1; - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - client_status.errors++; - reconnect_timer = xTaskGetTickCount(); - } - } - } - - /* Check for data from UART to send to TCP */ - tx_len = xStreamBufferReceive(tx_stream, tx_buffer, sizeof(tx_buffer), 0); - if (tx_len > 0) - { - int sent = tcp_client_send_all(client_socket, tx_buffer, (uint16_t)tx_len); - if (sent > 0) - { - client_status.tx_bytes += sent; - } - else if (sent < 0) - { - /* Send error */ - close(client_socket); - client_socket = -1; - client_status.state = TCP_CLIENT_STATE_DISCONNECTED; - client_status.errors++; - reconnect_timer = xTaskGetTickCount(); - } - } - - /* Small delay to prevent tight loop */ - vTaskDelay(pdMS_TO_TICKS(1)); + now = HAL_GetTick(); + if (now >= g_client.next_retry_ms) { + g_client.status.reconnect_count++; + g_client.next_retry_ms = now + g_client.config.reconnect_interval_ms; + (void)tcp_client_connect(); } } diff --git a/App/tcp_client.h b/App/tcp_client.h index 6c2cd05..234c30c 100644 --- a/App/tcp_client.h +++ b/App/tcp_client.h @@ -125,6 +125,8 @@ void *tcp_client_get_tx_stream(void); */ void tcp_client_task(void *argument); +void tcp_client_poll(void); + #ifdef __cplusplus } #endif diff --git a/App/tcp_server.c b/App/tcp_server.c index 0b71a7e..9d8423b 100644 --- a/App/tcp_server.c +++ b/App/tcp_server.c @@ -1,405 +1,246 @@ /** * @file tcp_server.c - * @brief TCP Server module implementation for transparent transmission with UART2 + * @brief lwIP RAW TCP server for the UART2 bridge. */ #include "tcp_server.h" -#include "config.h" -#include "lwip/opt.h" -#include "lwip/tcp.h" -#include "lwip/sys.h" -#include "lwip/sockets.h" -#include "FreeRTOS.h" -#include "task.h" -#include "stream_buffer.h" +#include "lwip/pbuf.h" +#include "lwip/tcp.h" #include -/*--------------------------------------------------------------------------- - * Private Variables - *---------------------------------------------------------------------------*/ +typedef struct { + struct tcp_pcb *listen_pcb; + struct tcp_pcb *client_pcb; + uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE]; + uint16_t rx_head; + uint16_t rx_tail; + tcp_server_config_t config; + tcp_server_status_t status; +} tcp_server_ctx_t; -/* Server configuration */ -static tcp_server_config_t server_config = { - .port = TCP_SERVER_DEFAULT_PORT, - .auto_reconnect = true -}; +static tcp_server_ctx_t g_server; -/* Server status */ -static tcp_server_status_t server_status = { - .state = TCP_SERVER_STATE_IDLE, - .rx_bytes = 0, - .tx_bytes = 0, - .connections = 0, - .errors = 0 -}; - -/* Socket descriptors */ -static int listen_socket = -1; -static int client_socket = -1; - -/* Stream buffers for UART integration */ -static StreamBufferHandle_t rx_stream = NULL; /* TCP RX -> UART TX */ -static StreamBufferHandle_t tx_stream = NULL; /* UART RX -> TCP TX */ - -/*--------------------------------------------------------------------------- - * Public Functions - *---------------------------------------------------------------------------*/ - -static int tcp_server_send_all(int sock, const uint8_t *data, uint16_t len) +static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size) { - uint16_t total = 0; + return (head >= tail) ? (uint16_t)(size - head + tail - 1u) : (uint16_t)(tail - head - 1u); +} - while (total < len) - { - int sent = send(sock, data + total, len - total, 0); - if (sent > 0) - { - total += (uint16_t)sent; +static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; + struct pbuf *q; + + if (err != ERR_OK) { + if (p != NULL) { + pbuf_free(p); } - else - { - return -1; + return err; + } + + if (p == NULL) { + ctx->client_pcb = NULL; + ctx->status.state = TCP_SERVER_STATE_LISTENING; + return ERR_OK; + } + + for (q = p; q != NULL; q = q->next) { + const uint8_t *src = (const uint8_t *)q->payload; + for (uint16_t i = 0; i < q->len; ++i) { + if (ring_free(ctx->rx_head, ctx->rx_tail, TCP_SERVER_RX_BUFFER_SIZE) == 0u) { + ctx->status.errors++; + break; + } + ctx->rx_ring[ctx->rx_head] = src[i]; + ctx->rx_head = (uint16_t)((ctx->rx_head + 1u) % TCP_SERVER_RX_BUFFER_SIZE); + ctx->status.rx_bytes++; } } - return (int)total; + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +static err_t tcp_server_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; + (void)pcb; + ctx->status.tx_bytes += len; + return ERR_OK; +} + +static void tcp_server_on_err(void *arg, err_t err) +{ + tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; + (void)err; + ctx->client_pcb = NULL; + ctx->status.state = TCP_SERVER_STATE_LISTENING; + ctx->status.errors++; +} + +static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; + + if (err != ERR_OK) { + return err; + } + + if (ctx->client_pcb != NULL) { + tcp_abort(newpcb); + return ERR_ABRT; + } + + ctx->client_pcb = newpcb; + ctx->status.state = TCP_SERVER_STATE_CONNECTED; + ctx->status.connections++; + + tcp_arg(newpcb, ctx); + tcp_recv(newpcb, tcp_server_on_recv); + tcp_sent(newpcb, tcp_server_on_sent); + tcp_err(newpcb, tcp_server_on_err); + return ERR_OK; } -/** - * @brief Initialize TCP Server module - */ int tcp_server_init(const tcp_server_config_t *config) { - if (config != NULL) - { - memcpy(&server_config, config, sizeof(tcp_server_config_t)); + memset(&g_server, 0, sizeof(g_server)); + g_server.config.port = TCP_SERVER_DEFAULT_PORT; + g_server.config.auto_reconnect = true; + g_server.status.state = TCP_SERVER_STATE_IDLE; + + if (config != NULL) { + g_server.config = *config; } - - /* Create stream buffers */ - if (rx_stream == NULL) - { - rx_stream = xStreamBufferCreate(TCP_SERVER_RX_BUFFER_SIZE, 1); - if (rx_stream == NULL) - { - return -1; - } - } - - if (tx_stream == NULL) - { - tx_stream = xStreamBufferCreate(TCP_SERVER_TX_BUFFER_SIZE, 1); - if (tx_stream == NULL) - { - return -1; - } - } - - server_status.state = TCP_SERVER_STATE_IDLE; - + return 0; } -/** - * @brief Start TCP Server - */ int tcp_server_start(void) { - struct sockaddr_in server_addr; - int opt = 1; - - if (listen_socket >= 0) - { - /* Already started */ + struct tcp_pcb *pcb; + err_t err; + + if (g_server.listen_pcb != NULL) { return 0; } - - /* Create socket */ - listen_socket = socket(AF_INET, SOCK_STREAM, 0); - if (listen_socket < 0) - { - server_status.state = TCP_SERVER_STATE_ERROR; - server_status.errors++; + + pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + if (pcb == NULL) { + g_server.status.errors++; return -1; } - - /* Set socket options */ - setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); - - /* Bind to port */ - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(server_config.port); - - if (bind(listen_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) - { - close(listen_socket); - listen_socket = -1; - server_status.state = TCP_SERVER_STATE_ERROR; - server_status.errors++; + + err = tcp_bind(pcb, IP_ANY_TYPE, g_server.config.port); + if (err != ERR_OK) { + tcp_abort(pcb); + g_server.status.errors++; return -1; } - - /* Start listening */ - if (listen(listen_socket, TCP_SERVER_MAX_CONNECTIONS) < 0) - { - close(listen_socket); - listen_socket = -1; - server_status.state = TCP_SERVER_STATE_ERROR; - server_status.errors++; + + g_server.listen_pcb = tcp_listen_with_backlog(pcb, 1); + if (g_server.listen_pcb == NULL) { + g_server.status.errors++; return -1; } - - server_status.state = TCP_SERVER_STATE_LISTENING; - + + tcp_arg(g_server.listen_pcb, &g_server); + tcp_accept(g_server.listen_pcb, tcp_server_on_accept); + g_server.status.state = TCP_SERVER_STATE_LISTENING; return 0; } -/** - * @brief Stop TCP Server - */ int tcp_server_stop(void) { - if (client_socket >= 0) - { - close(client_socket); - client_socket = -1; + if (g_server.client_pcb != NULL) { + tcp_arg(g_server.client_pcb, NULL); + tcp_recv(g_server.client_pcb, NULL); + tcp_sent(g_server.client_pcb, NULL); + tcp_err(g_server.client_pcb, NULL); + tcp_abort(g_server.client_pcb); + g_server.client_pcb = NULL; } - - if (listen_socket >= 0) - { - close(listen_socket); - listen_socket = -1; + + if (g_server.listen_pcb != NULL) { + tcp_arg(g_server.listen_pcb, NULL); + tcp_accept(g_server.listen_pcb, NULL); + tcp_close(g_server.listen_pcb); + g_server.listen_pcb = NULL; } - - server_status.state = TCP_SERVER_STATE_IDLE; - + + g_server.status.state = TCP_SERVER_STATE_IDLE; return 0; } -/** - * @brief Send data to connected client - */ int tcp_server_send(const uint8_t *data, uint16_t len) { - int sent; - - if (client_socket < 0) - { + err_t err; + + if (g_server.client_pcb == NULL || data == NULL || len == 0u) { return -1; } - - sent = tcp_server_send_all(client_socket, data, len); - if (sent > 0) - { - server_status.tx_bytes += sent; + + if (tcp_sndbuf(g_server.client_pcb) < len) { + return 0; } - else if (sent < 0) - { - /* Connection error */ - close(client_socket); - client_socket = -1; - server_status.state = TCP_SERVER_STATE_LISTENING; - server_status.errors++; + + err = tcp_write(g_server.client_pcb, data, len, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + g_server.status.errors++; + return -1; } - - return sent; + + err = tcp_output(g_server.client_pcb); + if (err != ERR_OK) { + g_server.status.errors++; + return -1; + } + + return (int)len; } -/** - * @brief Receive data from connected client - */ int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms) { - int received; - struct timeval tv; - - if (client_socket < 0) - { + uint16_t copied = 0u; + (void)timeout_ms; + + if (data == NULL || max_len == 0u) { return -1; } - - /* Set receive timeout */ - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - - received = recv(client_socket, data, max_len, 0); - if (received > 0) - { - server_status.rx_bytes += received; + + while (copied < max_len && g_server.rx_tail != g_server.rx_head) { + data[copied++] = g_server.rx_ring[g_server.rx_tail]; + g_server.rx_tail = (uint16_t)((g_server.rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE); } - else if (received == 0) - { - /* Connection closed by client */ - close(client_socket); - client_socket = -1; - server_status.state = TCP_SERVER_STATE_LISTENING; - } - else if (received < 0) - { - /* Timeout or error - check errno */ - /* For timeout, just return 0 */ - } - - return received; + + return (int)copied; } -/** - * @brief Check if client is connected - */ bool tcp_server_is_connected(void) { - return (client_socket >= 0); + return g_server.client_pcb != NULL; } -/** - * @brief Get TCP Server status - */ void tcp_server_get_status(tcp_server_status_t *status) { - if (status != NULL) - { - memcpy(status, &server_status, sizeof(tcp_server_status_t)); + if (status != NULL) { + *status = g_server.status; } } -/** - * @brief Get RX StreamBuffer handle - */ void *tcp_server_get_rx_stream(void) { - return rx_stream; + return NULL; } -/** - * @brief Get TX StreamBuffer handle - */ void *tcp_server_get_tx_stream(void) { - return tx_stream; + return NULL; } -/** - * @brief TCP Server task - */ void tcp_server_task(void *argument) { - const device_config_t *cfg; - tcp_server_config_t task_cfg; - struct sockaddr_in client_addr; - socklen_t addr_len; - uint8_t rx_buffer[256]; - uint8_t tx_buffer[256]; - int received; - size_t tx_len; - fd_set read_fds; - struct timeval tv; - int max_fd; - (void)argument; - - /* Initialize server */ - task_cfg.port = TCP_SERVER_DEFAULT_PORT; - task_cfg.auto_reconnect = true; - cfg = config_get(); - if (cfg != NULL && cfg->server_port > 0) - { - task_cfg.port = cfg->server_port; - } - tcp_server_init(&task_cfg); - - /* Start server */ - while (tcp_server_start() != 0) - { - vTaskDelay(pdMS_TO_TICKS(1000)); - } - - while (1) - { - /* Check if we need to accept a new connection */ - if (client_socket < 0 && listen_socket >= 0) - { - /* Use select with timeout to check for incoming connections */ - FD_ZERO(&read_fds); - FD_SET(listen_socket, &read_fds); - tv.tv_sec = 0; - tv.tv_usec = 100000; /* 100ms timeout */ - - if (select(listen_socket + 1, &read_fds, NULL, NULL, &tv) > 0) - { - addr_len = sizeof(client_addr); - client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &addr_len); - if (client_socket >= 0) - { - server_status.state = TCP_SERVER_STATE_CONNECTED; - server_status.connections++; - } - } - } - - /* Handle data transfer if connected */ - if (client_socket >= 0) - { - /* Check for data from TCP client */ - FD_ZERO(&read_fds); - FD_SET(client_socket, &read_fds); - max_fd = client_socket; - tv.tv_sec = 0; - tv.tv_usec = 10000; /* 10ms timeout */ - - if (select(max_fd + 1, &read_fds, NULL, NULL, &tv) > 0) - { - if (FD_ISSET(client_socket, &read_fds)) - { - received = recv(client_socket, rx_buffer, sizeof(rx_buffer), 0); - if (received > 0) - { - /* Forward to UART via stream buffer */ - xStreamBufferSend(rx_stream, rx_buffer, received, pdMS_TO_TICKS(10)); - server_status.rx_bytes += received; - } - else if (received == 0) - { - /* Connection closed */ - close(client_socket); - client_socket = -1; - server_status.state = TCP_SERVER_STATE_LISTENING; - } - else - { - /* Error */ - close(client_socket); - client_socket = -1; - server_status.state = TCP_SERVER_STATE_LISTENING; - server_status.errors++; - } - } - } - - /* Check for data from UART to send to TCP */ - tx_len = xStreamBufferReceive(tx_stream, tx_buffer, sizeof(tx_buffer), 0); - if (tx_len > 0) - { - int sent = tcp_server_send_all(client_socket, tx_buffer, (uint16_t)tx_len); - if (sent > 0) - { - server_status.tx_bytes += sent; - } - else if (sent < 0) - { - /* Send error */ - close(client_socket); - client_socket = -1; - server_status.state = TCP_SERVER_STATE_LISTENING; - server_status.errors++; - } - } - } - - /* Small delay to prevent tight loop */ - vTaskDelay(pdMS_TO_TICKS(1)); - } } diff --git a/App/uart_trans.c b/App/uart_trans.c index d488b1b..e9b9159 100644 --- a/App/uart_trans.c +++ b/App/uart_trans.c @@ -1,528 +1,331 @@ /** * @file uart_trans.c - * @brief UART transparent transmission module implementation - * - * Uses DMA + IDLE interrupt for efficient variable-length data reception. - * Integrates with TCP modules via FreeRTOS StreamBuffers. + * @brief Bare-metal UART DMA/IDLE transport layer. */ #include "uart_trans.h" -#include "usart.h" -#include "FreeRTOS.h" -#include "task.h" -#include "stream_buffer.h" +#include "usart.h" #include -/*--------------------------------------------------------------------------- - * Private Definitions - *---------------------------------------------------------------------------*/ - -/* Channel context structure */ typedef struct { - UART_HandleTypeDef *huart; /* HAL UART handle */ - DMA_HandleTypeDef *hdma_rx; /* DMA RX handle */ - - uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE]; /* DMA RX buffer */ - uint8_t tx_dma_buffer[UART_TX_DMA_BUFFER_SIZE]; /* DMA TX buffer */ - - volatile uint16_t rx_read_index; /* Last read position */ - volatile bool tx_busy; /* TX in progress flag */ - - StreamBufferHandle_t rx_stream; /* From TCP (for UART TX) */ - StreamBufferHandle_t tx_stream; /* To TCP (from UART RX) */ - - uart_config_t config; /* UART configuration */ - uart_stats_t stats; /* Statistics */ - + UART_HandleTypeDef *huart; + uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE]; + uint8_t tx_dma_buffer[UART_TX_DMA_BUFFER_SIZE]; + uint8_t rx_ring[UART_RX_RING_BUFFER_SIZE]; + uint8_t tx_ring[UART_TX_RING_BUFFER_SIZE]; + volatile uint16_t rx_dma_read_index; + volatile uint16_t rx_head; + volatile uint16_t rx_tail; + volatile uint16_t tx_head; + volatile uint16_t tx_tail; + volatile uint16_t tx_dma_len; + volatile bool tx_busy; + uart_config_t config; + uart_stats_t stats; bool initialized; bool running; } uart_channel_ctx_t; -/*--------------------------------------------------------------------------- - * Private Variables - *---------------------------------------------------------------------------*/ - static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX]; -/*--------------------------------------------------------------------------- - * Private Functions - *---------------------------------------------------------------------------*/ +static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size) +{ + return (head >= tail) ? (head - tail) : (size - tail + head); +} + +static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size) +{ + return (uint16_t)(size - ring_used(head, tail, size) - 1u); +} + +static void apply_default_config(uart_channel_ctx_t *ctx) +{ + ctx->config.baudrate = UART_DEFAULT_BAUDRATE; + ctx->config.data_bits = UART_DEFAULT_DATA_BITS; + ctx->config.stop_bits = UART_DEFAULT_STOP_BITS; + ctx->config.parity = UART_DEFAULT_PARITY; +} -/** - * @brief Apply UART configuration - */ static int apply_uart_config(uart_channel_t channel) { uart_channel_ctx_t *ctx = &g_channels[channel]; UART_HandleTypeDef *huart = ctx->huart; - - if (huart == NULL) - { + + if (huart == NULL) { return -1; } - - /* Stop UART if running */ - if (ctx->running) - { + + if (ctx->running) { HAL_UART_DMAStop(huart); + ctx->running = false; } - - /* Update UART parameters */ + huart->Init.BaudRate = ctx->config.baudrate; - - /* Data bits */ - if (ctx->config.data_bits == 9) - { - huart->Init.WordLength = UART_WORDLENGTH_9B; + huart->Init.WordLength = (ctx->config.data_bits == 9u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B; + huart->Init.StopBits = (ctx->config.stop_bits == 2u) ? UART_STOPBITS_2 : UART_STOPBITS_1; + + switch (ctx->config.parity) { + case 1: + huart->Init.Parity = UART_PARITY_ODD; + break; + case 2: + huart->Init.Parity = UART_PARITY_EVEN; + break; + default: + huart->Init.Parity = UART_PARITY_NONE; + break; } - else - { - huart->Init.WordLength = UART_WORDLENGTH_8B; - } - - /* Stop bits */ - if (ctx->config.stop_bits == 2) - { - huart->Init.StopBits = UART_STOPBITS_2; - } - else - { - huart->Init.StopBits = UART_STOPBITS_1; - } - - /* Parity */ - switch (ctx->config.parity) - { - case 1: - huart->Init.Parity = UART_PARITY_ODD; - break; - case 2: - huart->Init.Parity = UART_PARITY_EVEN; - break; - default: - huart->Init.Parity = UART_PARITY_NONE; + + return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1; +} + +static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index) +{ + uart_channel_ctx_t *ctx = &g_channels[channel]; + + while (ctx->rx_dma_read_index != dma_write_index) { + uint16_t next_head; + + next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE); + if (next_head == ctx->rx_tail) { + ctx->stats.errors++; break; + } + + ctx->rx_ring[ctx->rx_head] = ctx->rx_dma_buffer[ctx->rx_dma_read_index]; + ctx->rx_head = next_head; + ctx->rx_dma_read_index = (uint16_t)((ctx->rx_dma_read_index + 1u) % UART_RX_DMA_BUFFER_SIZE); + ctx->stats.rx_bytes++; } - - /* Reinitialize UART */ - if (HAL_UART_Init(huart) != HAL_OK) - { - return -1; +} + +static void kick_tx(uart_channel_t channel) +{ + uart_channel_ctx_t *ctx = &g_channels[channel]; + uint16_t available; + uint16_t chunk; + + if (!ctx->running || ctx->tx_busy) { + return; } - + + available = ring_used(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE); + if (available == 0u) { + return; + } + + chunk = available; + if (chunk > UART_TX_DMA_BUFFER_SIZE) { + chunk = UART_TX_DMA_BUFFER_SIZE; + } + + for (uint16_t i = 0; i < chunk; ++i) { + ctx->tx_dma_buffer[i] = ctx->tx_ring[ctx->tx_tail]; + ctx->tx_tail = (uint16_t)((ctx->tx_tail + 1u) % UART_TX_RING_BUFFER_SIZE); + } + + ctx->tx_dma_len = chunk; + ctx->tx_busy = true; + ctx->stats.tx_packets++; + + if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) { + ctx->tx_busy = false; + ctx->stats.errors++; + } +} + +int uart_trans_init(void) +{ + memset(g_channels, 0, sizeof(g_channels)); + + g_channels[UART_CHANNEL_SERVER].huart = &huart2; + g_channels[UART_CHANNEL_CLIENT].huart = &huart3; + + apply_default_config(&g_channels[UART_CHANNEL_SERVER]); + apply_default_config(&g_channels[UART_CHANNEL_CLIENT]); + + g_channels[UART_CHANNEL_SERVER].initialized = true; + g_channels[UART_CHANNEL_CLIENT].initialized = true; + return 0; } -static void process_rx_data_from_isr(uart_channel_t channel, uint16_t end_index) +int uart_trans_config(uart_channel_t channel, const uart_config_t *config) { - uart_channel_ctx_t *ctx = &g_channels[channel]; - uint16_t start = ctx->rx_read_index; - uint16_t end = end_index; - uint16_t len; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - if (start >= UART_RX_DMA_BUFFER_SIZE) - { - start = 0; + if (channel >= UART_CHANNEL_MAX || config == NULL) { + return -1; } - if (end > UART_RX_DMA_BUFFER_SIZE) - { - end = UART_RX_DMA_BUFFER_SIZE; + g_channels[channel].config = *config; + return apply_uart_config(channel); +} + +int uart_trans_start(uart_channel_t channel) +{ + uart_channel_ctx_t *ctx; + + if (channel >= UART_CHANNEL_MAX) { + return -1; } - if (end >= start) - { - len = end - start; - if (len > 0 && ctx->tx_stream != NULL) - { - xStreamBufferSendFromISR(ctx->tx_stream, - &ctx->rx_dma_buffer[start], - len, - &xHigherPriorityTaskWoken); - ctx->stats.rx_bytes += len; - ctx->stats.rx_packets++; - } + ctx = &g_channels[channel]; + if (!ctx->initialized || ctx->huart == NULL) { + return -1; } - else - { - len = UART_RX_DMA_BUFFER_SIZE - start; - if (len > 0 && ctx->tx_stream != NULL) - { - xStreamBufferSendFromISR(ctx->tx_stream, - &ctx->rx_dma_buffer[start], - len, - &xHigherPriorityTaskWoken); - ctx->stats.rx_bytes += len; - } - if (end > 0 && ctx->tx_stream != NULL) - { - xStreamBufferSendFromISR(ctx->tx_stream, - ctx->rx_dma_buffer, - end, - &xHigherPriorityTaskWoken); - ctx->stats.rx_bytes += end; - } + ctx->rx_dma_read_index = 0u; + ctx->rx_head = 0u; + ctx->rx_tail = 0u; + ctx->tx_head = 0u; + ctx->tx_tail = 0u; + ctx->tx_dma_len = 0u; + ctx->tx_busy = false; + + __HAL_UART_ENABLE_IT(ctx->huart, UART_IT_IDLE); + if (HAL_UART_Receive_DMA(ctx->huart, ctx->rx_dma_buffer, UART_RX_DMA_BUFFER_SIZE) != HAL_OK) { + return -1; + } + + ctx->running = true; + return 0; +} + +int uart_trans_stop(uart_channel_t channel) +{ + if (channel >= UART_CHANNEL_MAX) { + return -1; + } + + HAL_UART_DMAStop(g_channels[channel].huart); + g_channels[channel].running = false; + g_channels[channel].tx_busy = false; + return 0; +} + +void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats) +{ + if (channel >= UART_CHANNEL_MAX || stats == NULL) { + return; + } + + *stats = g_channels[channel].stats; +} + +void uart_trans_reset_stats(uart_channel_t channel) +{ + if (channel >= UART_CHANNEL_MAX) { + return; + } + + memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats)); +} + +uint16_t uart_trans_rx_available(uart_channel_t channel) +{ + if (channel >= UART_CHANNEL_MAX) { + return 0u; + } + + return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE); +} + +uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len) +{ + uart_channel_ctx_t *ctx; + uint16_t copied = 0u; + + if (channel >= UART_CHANNEL_MAX || data == NULL || max_len == 0u) { + return 0u; + } + + ctx = &g_channels[channel]; + while (copied < max_len && ctx->rx_tail != ctx->rx_head) { + data[copied++] = ctx->rx_ring[ctx->rx_tail]; + ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE); + } + + if (copied > 0u) { ctx->stats.rx_packets++; } - ctx->rx_read_index = (end == UART_RX_DMA_BUFFER_SIZE) ? 0 : end; - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + return copied; } -/*--------------------------------------------------------------------------- - * Public Functions - *---------------------------------------------------------------------------*/ - -/** - * @brief Initialize UART transparent transmission module - */ -int uart_trans_init(void) +uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t len) { - /* Initialize Server channel (UART2) */ - memset(&g_channels[UART_CHANNEL_SERVER], 0, sizeof(uart_channel_ctx_t)); - g_channels[UART_CHANNEL_SERVER].huart = &huart2; - g_channels[UART_CHANNEL_SERVER].config.baudrate = UART_DEFAULT_BAUDRATE; - g_channels[UART_CHANNEL_SERVER].config.data_bits = UART_DEFAULT_DATA_BITS; - g_channels[UART_CHANNEL_SERVER].config.stop_bits = UART_DEFAULT_STOP_BITS; - g_channels[UART_CHANNEL_SERVER].config.parity = UART_DEFAULT_PARITY; - g_channels[UART_CHANNEL_SERVER].initialized = true; - - /* Initialize Client channel (UART3) */ - memset(&g_channels[UART_CHANNEL_CLIENT], 0, sizeof(uart_channel_ctx_t)); - g_channels[UART_CHANNEL_CLIENT].huart = &huart3; - g_channels[UART_CHANNEL_CLIENT].config.baudrate = UART_DEFAULT_BAUDRATE; - g_channels[UART_CHANNEL_CLIENT].config.data_bits = UART_DEFAULT_DATA_BITS; - g_channels[UART_CHANNEL_CLIENT].config.stop_bits = UART_DEFAULT_STOP_BITS; - g_channels[UART_CHANNEL_CLIENT].config.parity = UART_DEFAULT_PARITY; - g_channels[UART_CHANNEL_CLIENT].initialized = true; - - return 0; + uart_channel_ctx_t *ctx; + uint16_t written = 0u; + + if (channel >= UART_CHANNEL_MAX || data == NULL || len == 0u) { + return 0u; + } + + ctx = &g_channels[channel]; + while (written < len && ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) > 0u) { + ctx->tx_ring[ctx->tx_head] = data[written++]; + ctx->tx_head = (uint16_t)((ctx->tx_head + 1u) % UART_TX_RING_BUFFER_SIZE); + } + + if (written < len) { + ctx->stats.errors++; + } + + kick_tx(channel); + return written; } -/** - * @brief Configure UART channel parameters - */ -int uart_trans_config(uart_channel_t channel, const uart_config_t *config) +void uart_trans_poll(void) { - if (channel >= UART_CHANNEL_MAX || config == NULL) - { - return -1; - } - - uart_channel_ctx_t *ctx = &g_channels[channel]; - - memcpy(&ctx->config, config, sizeof(uart_config_t)); - - /* Apply configuration if already initialized */ - if (ctx->initialized) - { - return apply_uart_config(channel); - } - - return 0; + kick_tx(UART_CHANNEL_SERVER); + kick_tx(UART_CHANNEL_CLIENT); } -/** - * @brief Start UART reception - */ -int uart_trans_start(uart_channel_t channel) -{ - if (channel >= UART_CHANNEL_MAX) - { - return -1; - } - - uart_channel_ctx_t *ctx = &g_channels[channel]; - - if (!ctx->initialized || ctx->huart == NULL) - { - return -1; - } - - /* Reset read index */ - ctx->rx_read_index = 0; - ctx->tx_busy = false; - - /* Enable IDLE interrupt */ - __HAL_UART_ENABLE_IT(ctx->huart, UART_IT_IDLE); - - /* Start DMA reception (circular mode) */ - HAL_UART_Receive_DMA(ctx->huart, ctx->rx_dma_buffer, UART_RX_DMA_BUFFER_SIZE); - - ctx->running = true; - - return 0; -} - -/** - * @brief Stop UART reception - */ -int uart_trans_stop(uart_channel_t channel) -{ - if (channel >= UART_CHANNEL_MAX) - { - return -1; - } - - uart_channel_ctx_t *ctx = &g_channels[channel]; - - if (ctx->huart == NULL) - { - return -1; - } - - /* Disable IDLE interrupt */ - __HAL_UART_DISABLE_IT(ctx->huart, UART_IT_IDLE); - - /* Stop DMA */ - HAL_UART_DMAStop(ctx->huart); - - ctx->running = false; - - return 0; -} - -/** - * @brief Set StreamBuffer handles - */ -void uart_trans_set_streams(uart_channel_t channel, - void *rx_stream, - void *tx_stream) -{ - if (channel >= UART_CHANNEL_MAX) - { - return; - } - - g_channels[channel].rx_stream = (StreamBufferHandle_t)rx_stream; - g_channels[channel].tx_stream = (StreamBufferHandle_t)tx_stream; -} - -/** - * @brief Get UART statistics - */ -void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats) -{ - if (channel >= UART_CHANNEL_MAX || stats == NULL) - { - return; - } - - memcpy(stats, &g_channels[channel].stats, sizeof(uart_stats_t)); -} - -/** - * @brief Reset UART statistics - */ -void uart_trans_reset_stats(uart_channel_t channel) -{ - if (channel >= UART_CHANNEL_MAX) - { - return; - } - - memset(&g_channels[channel].stats, 0, sizeof(uart_stats_t)); -} - -/** - * @brief UART IDLE interrupt handler - */ void uart_trans_idle_handler(uart_channel_t channel) { - if (channel >= UART_CHANNEL_MAX) - { + UART_HandleTypeDef *huart; + uint16_t dma_write_index; + + if (channel >= UART_CHANNEL_MAX) { return; } - - uart_channel_ctx_t *ctx = &g_channels[channel]; - - if (!ctx->running || ctx->huart == NULL) - { - return; - } - - /* Get current DMA position */ - uint16_t dma_counter = __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx); - uint16_t current_pos = UART_RX_DMA_BUFFER_SIZE - dma_counter; - - /* Process received data */ - if (current_pos != ctx->rx_read_index) - { - process_rx_data_from_isr(channel, current_pos); + + huart = g_channels[channel].huart; + dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx)); + if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) { + dma_write_index = 0u; } + + process_rx_snapshot(channel, dma_write_index); } void uart_trans_rx_half_cplt_handler(uart_channel_t channel) { - if (channel >= UART_CHANNEL_MAX) - { + if (channel >= UART_CHANNEL_MAX) { return; } - uart_channel_ctx_t *ctx = &g_channels[channel]; - if (!ctx->running || ctx->huart == NULL) - { - return; - } - - uint16_t dma_counter = __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx); - uint16_t current_pos = UART_RX_DMA_BUFFER_SIZE - dma_counter; - - if (current_pos != ctx->rx_read_index) - { - process_rx_data_from_isr(channel, current_pos); - } + process_rx_snapshot(channel, UART_RX_DMA_BUFFER_SIZE / 2u); } -/** - * @brief UART DMA RX complete callback (buffer half/full) - */ void uart_trans_rx_cplt_handler(uart_channel_t channel) { - /* In circular mode, this is called when buffer is full */ - /* The IDLE handler already processes data continuously */ - /* This is a safety handler for high-speed continuous data */ - - if (channel >= UART_CHANNEL_MAX) - { + if (channel >= UART_CHANNEL_MAX) { return; } - - uart_channel_ctx_t *ctx = &g_channels[channel]; - - if (!ctx->running) - { - return; - } - - uint16_t dma_counter = __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx); - uint16_t current_pos = UART_RX_DMA_BUFFER_SIZE - dma_counter; - if (current_pos != ctx->rx_read_index) - { - process_rx_data_from_isr(channel, current_pos); - } + process_rx_snapshot(channel, 0u); } -/** - * @brief UART DMA TX complete callback - */ void uart_trans_tx_cplt_handler(uart_channel_t channel) { - if (channel >= UART_CHANNEL_MAX) - { + if (channel >= UART_CHANNEL_MAX) { return; } - + g_channels[channel].tx_busy = false; -} - -/** - * @brief Server transparent transmission task (UART2 <-> TCP Server) - */ -void uart_server_trans_task(void *argument) -{ - uart_channel_ctx_t *ctx = &g_channels[UART_CHANNEL_SERVER]; - uint8_t tx_buffer[128]; - size_t len; - - (void)argument; - - /* Wait for streams to be set */ - while (ctx->rx_stream == NULL || ctx->tx_stream == NULL) - { - vTaskDelay(pdMS_TO_TICKS(100)); - } - - /* Start UART reception */ - uart_trans_start(UART_CHANNEL_SERVER); - - while (1) - { - /* Check for data from TCP to send to UART */ - if (!ctx->tx_busy && ctx->rx_stream != NULL) - { - len = xStreamBufferReceive(ctx->rx_stream, tx_buffer, - sizeof(tx_buffer), pdMS_TO_TICKS(10)); - if (len > 0) - { - /* Copy to DMA buffer and send */ - memcpy(ctx->tx_dma_buffer, tx_buffer, len); - ctx->tx_busy = true; - - if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, len) != HAL_OK) - { - ctx->tx_busy = false; - ctx->stats.errors++; - } - else - { - ctx->stats.tx_bytes += len; - ctx->stats.tx_packets++; - } - } - } - else - { - /* TX busy or no stream, wait a bit */ - vTaskDelay(pdMS_TO_TICKS(1)); - } - } -} - -/** - * @brief Client transparent transmission task (UART3 <-> TCP Client) - */ -void uart_client_trans_task(void *argument) -{ - uart_channel_ctx_t *ctx = &g_channels[UART_CHANNEL_CLIENT]; - uint8_t tx_buffer[128]; - size_t len; - - (void)argument; - - /* Wait for streams to be set */ - while (ctx->rx_stream == NULL || ctx->tx_stream == NULL) - { - vTaskDelay(pdMS_TO_TICKS(100)); - } - - /* Start UART reception */ - uart_trans_start(UART_CHANNEL_CLIENT); - - while (1) - { - /* Check for data from TCP to send to UART */ - if (!ctx->tx_busy && ctx->rx_stream != NULL) - { - len = xStreamBufferReceive(ctx->rx_stream, tx_buffer, - sizeof(tx_buffer), pdMS_TO_TICKS(10)); - if (len > 0) - { - /* Copy to DMA buffer and send */ - memcpy(ctx->tx_dma_buffer, tx_buffer, len); - ctx->tx_busy = true; - - if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, len) != HAL_OK) - { - ctx->tx_busy = false; - ctx->stats.errors++; - } - else - { - ctx->stats.tx_bytes += len; - ctx->stats.tx_packets++; - } - } - } - else - { - /* TX busy or no stream, wait a bit */ - vTaskDelay(pdMS_TO_TICKS(1)); - } - } + g_channels[channel].stats.tx_bytes += g_channels[channel].tx_dma_len; + g_channels[channel].tx_dma_len = 0u; + kick_tx(channel); } diff --git a/App/uart_trans.h b/App/uart_trans.h index fef4ecc..a3944fd 100644 --- a/App/uart_trans.h +++ b/App/uart_trans.h @@ -1,48 +1,41 @@ /** * @file uart_trans.h - * @brief UART transparent transmission module for TCP2UART - * - * - UART2 <-> TCP Server (via StreamBuffer) - * - UART3 <-> TCP Client (via StreamBuffer) - * - DMA + IDLE interrupt for efficient reception + * @brief Bare-metal UART DMA/IDLE transport layer. */ #ifndef __UART_TRANS_H__ #define __UART_TRANS_H__ -#include #include +#include #ifdef __cplusplus extern "C" { #endif -/* UART channel definitions */ typedef enum { - UART_CHANNEL_SERVER = 0, /* UART2 - TCP Server channel */ - UART_CHANNEL_CLIENT = 1, /* UART3 - TCP Client channel */ + UART_CHANNEL_SERVER = 0, + UART_CHANNEL_CLIENT = 1, UART_CHANNEL_MAX } uart_channel_t; -/* DMA buffer sizes */ -#define UART_RX_DMA_BUFFER_SIZE 128 -#define UART_TX_DMA_BUFFER_SIZE 128 +#define UART_RX_DMA_BUFFER_SIZE 128u +#define UART_TX_DMA_BUFFER_SIZE 128u +#define UART_RX_RING_BUFFER_SIZE 512u +#define UART_TX_RING_BUFFER_SIZE 512u -/* UART configuration */ typedef struct { uint32_t baudrate; - uint8_t data_bits; /* 8 or 9 */ - uint8_t stop_bits; /* 1 or 2 */ - uint8_t parity; /* 0=None, 1=Odd, 2=Even */ + uint8_t data_bits; + uint8_t stop_bits; + uint8_t parity; } uart_config_t; -/* Default configurations */ -#define UART_DEFAULT_BAUDRATE 115200 -#define UART_DEFAULT_DATA_BITS 8 -#define UART_DEFAULT_STOP_BITS 1 -#define UART_DEFAULT_PARITY 0 +#define UART_DEFAULT_BAUDRATE 115200u +#define UART_DEFAULT_DATA_BITS 8u +#define UART_DEFAULT_STOP_BITS 1u +#define UART_DEFAULT_PARITY 0u -/* UART statistics */ typedef struct { uint32_t rx_bytes; uint32_t tx_bytes; @@ -51,101 +44,23 @@ typedef struct { uint32_t errors; } uart_stats_t; -/** - * @brief Initialize UART transparent transmission module - * @return 0 on success, negative on error - */ int uart_trans_init(void); - -/** - * @brief Configure UART channel parameters - * @param channel UART channel (SERVER or CLIENT) - * @param config UART configuration - * @return 0 on success, negative on error - */ int uart_trans_config(uart_channel_t channel, const uart_config_t *config); - -/** - * @brief Start UART reception (enable DMA + IDLE interrupt) - * @param channel UART channel - * @return 0 on success, negative on error - */ int uart_trans_start(uart_channel_t channel); - -/** - * @brief Stop UART reception - * @param channel UART channel - * @return 0 on success, negative on error - */ int uart_trans_stop(uart_channel_t channel); - -/** - * @brief Set StreamBuffer handles for TCP integration - * @param channel UART channel - * @param rx_stream StreamBuffer to receive data from TCP (for UART TX) - * @param tx_stream StreamBuffer to send data to TCP (from UART RX) - */ -void uart_trans_set_streams(uart_channel_t channel, - void *rx_stream, - void *tx_stream); - -/** - * @brief Get UART statistics - * @param channel UART channel - * @param stats Pointer to statistics structure - */ +void uart_trans_poll(void); +uint16_t uart_trans_rx_available(uart_channel_t channel); +uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len); +uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t len); void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats); - -/** - * @brief Reset UART statistics - * @param channel UART channel - */ void uart_trans_reset_stats(uart_channel_t channel); - -/** - * @brief UART IDLE interrupt handler - call from stm32f1xx_it.c - * @param channel UART channel - * - * Usage in stm32f1xx_it.c USART2_IRQHandler: - * if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) { - * __HAL_UART_CLEAR_IDLEFLAG(&huart2); - * uart_trans_idle_handler(UART_CHANNEL_SERVER); - * } - */ void uart_trans_idle_handler(uart_channel_t channel); - -/** - * @brief UART DMA RX half complete callback - call from HAL callback - * @param channel UART channel - */ void uart_trans_rx_half_cplt_handler(uart_channel_t channel); - -/** - * @brief UART DMA RX complete callback - call from HAL callback - * @param channel UART channel - */ void uart_trans_rx_cplt_handler(uart_channel_t channel); - -/** - * @brief UART DMA TX complete callback - call from HAL callback - * @param channel UART channel - */ void uart_trans_tx_cplt_handler(uart_channel_t channel); -/** - * @brief Server transparent transmission task (UART2 <-> TCP Server) - * @param argument Task argument (unused) - */ -void uart_server_trans_task(void *argument); - -/** - * @brief Client transparent transmission task (UART3 <-> TCP Client) - * @param argument Task argument (unused) - */ -void uart_client_trans_task(void *argument); - #ifdef __cplusplus } #endif -#endif /* __UART_TRANS_H__ */ +#endif diff --git a/Core/Src/main.c b/Core/Src/main.c index 955b2ca..c9f823c 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -18,7 +18,6 @@ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" -#include "cmsis_os.h" #include "dma.h" #include "iwdg.h" #include "spi.h" @@ -28,10 +27,17 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include +#include #include "CH390.h" +#include "SEGGER_RTT.h" #include "config.h" #include "flash_param.h" +#include "ethernetif.h" +#include "lwip/init.h" +#include "lwip/timeouts.h" +#include "tcp_client.h" +#include "tcp_server.h" #include "uart_trans.h" /* USER CODE END Includes */ @@ -66,10 +72,11 @@ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); -void MX_FREERTOS_Init(void); /* USER CODE BEGIN PFP */ static void CH390_HardwareReset(void); static void LED_Init(void); +static void App_Init(void); +static void App_Poll(void); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ @@ -110,6 +117,99 @@ void LED_Toggle(void) HAL_GPIO_TogglePin(LED_PORT, LED_PIN); } +static void App_Init(void) +{ + const device_config_t *cfg; + ip4_addr_t ipaddr; + ip4_addr_t netmask; + ip4_addr_t gateway; + uart_config_t uart_cfg; + tcp_server_config_t server_cfg; + tcp_client_config_t client_cfg; + + config_init(); + cfg = config_get(); + + uart_trans_init(); + + uart_cfg.baudrate = cfg->uart2_baudrate; + uart_cfg.data_bits = cfg->uart2_databits; + uart_cfg.stop_bits = cfg->uart2_stopbits; + uart_cfg.parity = cfg->uart2_parity; + uart_trans_config(UART_CHANNEL_SERVER, &uart_cfg); + + uart_cfg.baudrate = cfg->uart3_baudrate; + uart_cfg.data_bits = cfg->uart3_databits; + uart_cfg.stop_bits = cfg->uart3_stopbits; + uart_cfg.parity = cfg->uart3_parity; + uart_trans_config(UART_CHANNEL_CLIENT, &uart_cfg); + + uart_trans_start(UART_CHANNEL_SERVER); + uart_trans_start(UART_CHANNEL_CLIENT); + config_start_reception(); + + SEGGER_RTT_Init(); + SEGGER_RTT_WriteString(0, "\r\nTCP2UART boot\r\n"); + + lwip_init(); + IP4_ADDR(&ipaddr, cfg->ip[0], cfg->ip[1], cfg->ip[2], cfg->ip[3]); + IP4_ADDR(&netmask, cfg->mask[0], cfg->mask[1], cfg->mask[2], cfg->mask[3]); + IP4_ADDR(&gateway, cfg->gw[0], cfg->gw[1], cfg->gw[2], cfg->gw[3]); + lwip_netif_init(&ipaddr, &netmask, &gateway); + + server_cfg.port = cfg->server_port; + server_cfg.auto_reconnect = true; + tcp_server_init(&server_cfg); + tcp_server_start(); + + memcpy(client_cfg.server_ip, cfg->remote_ip, sizeof(client_cfg.server_ip)); + client_cfg.server_port = cfg->remote_port; + client_cfg.auto_reconnect = true; + client_cfg.reconnect_interval_ms = cfg->reconnect_interval; + tcp_client_init(&client_cfg); + tcp_client_connect(); +} + +static void App_Poll(void) +{ + uint8_t buffer[128]; + int len; + + ethernetif_poll(); + ethernetif_check_link(); + sys_check_timeouts(); + tcp_client_poll(); + uart_trans_poll(); + config_poll(); + + len = tcp_server_recv(buffer, sizeof(buffer), 0u); + if (len > 0) { + uart_trans_write(UART_CHANNEL_SERVER, buffer, (uint16_t)len); + } + + len = tcp_client_recv(buffer, sizeof(buffer), 0u); + if (len > 0) { + uart_trans_write(UART_CHANNEL_CLIENT, buffer, (uint16_t)len); + } + + len = (int)uart_trans_read(UART_CHANNEL_SERVER, buffer, sizeof(buffer)); + if (len > 0) { + tcp_server_send(buffer, (uint16_t)len); + } + + len = (int)uart_trans_read(UART_CHANNEL_CLIENT, buffer, sizeof(buffer)); + if (len > 0) { + tcp_client_send(buffer, (uint16_t)len); + } + + if (config_is_reset_requested()) { + config_clear_reset_requested(); + NVIC_SystemReset(); + } + + HAL_IWDG_Refresh(&hiwdg); +} + /* USER CODE END 0 */ /** @@ -154,21 +254,11 @@ int main(void) /* CH390 硬件复位 */ CH390_HardwareReset(); - - /* Initialize configuration from Flash (fallback to defaults on invalid data) */ - config_init(); + + App_Init(); /* USER CODE END 2 */ - /* Init scheduler */ - osKernelInitialize(); /* Call init function for freertos objects (in cmsis_os2.c) */ - MX_FREERTOS_Init(); - - /* Start scheduler */ - osKernelStart(); - - /* We should never get here as control is now taken by the scheduler */ - /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) @@ -176,6 +266,7 @@ int main(void) /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ + App_Poll(); } /* USER CODE END 3 */ } @@ -228,13 +319,14 @@ void SystemClock_Config(void) #ifdef __GNUC__ int _write(int file, char *ptr, int len) { - HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY); - return len; + (void)file; + return (int)SEGGER_RTT_Write(0, ptr, (unsigned)len); } #else int fputc(int ch, FILE *f) { - HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); + (void)f; + SEGGER_RTT_PutChar(0, (char)ch); return ch; } #endif diff --git a/Core/Src/stm32f1xx_it.c b/Core/Src/stm32f1xx_it.c index 40d27df..c200ee6 100644 --- a/Core/Src/stm32f1xx_it.c +++ b/Core/Src/stm32f1xx_it.c @@ -20,15 +20,11 @@ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f1xx_it.h" -#include "FreeRTOS.h" -#include "task.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ +#include "ethernetif.h" #include "uart_trans.h" #include "config.h" - -/* External functions from freertos.c */ -extern void notify_ch390_interrupt_from_isr(void); /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -176,14 +172,6 @@ void SysTick_Handler(void) /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); -#if (INCLUDE_xTaskGetSchedulerState == 1 ) - if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) - { -#endif /* INCLUDE_xTaskGetSchedulerState */ - xPortSysTickHandler(); -#if (INCLUDE_xTaskGetSchedulerState == 1 ) - } -#endif /* INCLUDE_xTaskGetSchedulerState */ /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ @@ -361,9 +349,9 @@ void EXTI0_IRQHandler(void) if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0)) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); - - /* Notify LwIP task */ - notify_ch390_interrupt_from_isr(); + + /* Defer CH390 processing to main loop */ + ethernetif_set_irq_pending(); } } diff --git a/Drivers/CH390/CH390_Interface.c b/Drivers/CH390/CH390_Interface.c index dbd3799..fb53f75 100644 --- a/Drivers/CH390/CH390_Interface.c +++ b/Drivers/CH390/CH390_Interface.c @@ -12,6 +12,7 @@ * Modified for STM32F103 HAL Library with FreeRTOS support. ******************************************************************************/ #include "stm32f1xx_hal.h" +#include "main.h" #include "CH390.h" #include "CH390_Interface.h" diff --git a/Drivers/LwIP/src/include/arch/cc.h b/Drivers/LwIP/src/include/arch/cc.h index 2eb44de..cee9955 100644 --- a/Drivers/LwIP/src/include/arch/cc.h +++ b/Drivers/LwIP/src/include/arch/cc.h @@ -22,6 +22,8 @@ typedef int16_t s16_t; typedef uint32_t u32_t; typedef int32_t s32_t; +typedef uint32_t sys_prot_t; + typedef uintptr_t mem_ptr_t; /* Byte order - ARM Cortex-M is little endian */ @@ -62,15 +64,12 @@ typedef uintptr_t mem_ptr_t; /* Platform specific diagnostic output */ #ifndef LWIP_PLATFORM_DIAG -#define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0) +#define LWIP_PLATFORM_DIAG(x) do { } while(0) #endif /* Platform specific assertion handling */ #ifndef LWIP_PLATFORM_ASSERT -#define LWIP_PLATFORM_ASSERT(x) do { \ - printf("Assertion \"%s\" failed at line %d in %s\n", x, __LINE__, __FILE__); \ - while(1); \ -} while(0) +#define LWIP_PLATFORM_ASSERT(x) do { while(1) { } } while(0) #endif /* Get current time in milliseconds (provided by sys_arch.c) */ diff --git a/Drivers/LwIP/src/include/arch/lwipopts.h b/Drivers/LwIP/src/include/arch/lwipopts.h index 3a9eb0c..0a7e49b 100644 --- a/Drivers/LwIP/src/include/arch/lwipopts.h +++ b/Drivers/LwIP/src/include/arch/lwipopts.h @@ -1,201 +1,77 @@ /** * @file lwipopts.h - * @brief LwIP configuration for STM32F103 + FreeRTOS + CH390 Ethernet - * - * This configuration is optimized for: - * - STM32F103 with limited RAM (~20KB available) - * - FreeRTOS integration (NO_SYS=0) - * - TCP Server + Client dual link transparent transmission - * - CH390 Ethernet controller + * @brief lwIP configuration for STM32F103 + CH390 in NO_SYS mode. */ #ifndef LWIP_LWIPOPTS_H #define LWIP_LWIPOPTS_H -/*----------------------------------------------------------------------------- - * Platform and OS Options - *---------------------------------------------------------------------------*/ - -/* Use FreeRTOS - this enables the sequential API (netconn, sockets) */ -#define NO_SYS 0 - -/* Enable socket API */ -#define LWIP_SOCKET 1 -#define LWIP_NETCONN 1 +#define NO_SYS 1 +#define LWIP_SOCKET 0 +#define LWIP_NETCONN 0 #define LWIP_NETIF_API 0 - -/* Critical section protection */ #define SYS_LIGHTWEIGHT_PROT 1 +#define LWIP_PROVIDE_ERRNO 0 -/* Use FreeRTOS memory allocation */ -#define MEM_LIBC_MALLOC 0 -#define MEMP_MEM_MALLOC 0 - -/* Let lwIP provide the errno values used by sockets/netconn. */ -#define LWIP_PROVIDE_ERRNO 1 - -/*----------------------------------------------------------------------------- - * Memory Configuration (optimized for STM32F103 with ~20KB RAM) - *---------------------------------------------------------------------------*/ - -/* Memory alignment (ARM Cortex-M3 = 4 byte alignment) */ #define MEM_ALIGNMENT 4 +#define MEM_SIZE (4 * 1024) -/* Heap size for dynamic memory allocation */ -#define MEM_SIZE (4 * 1024) /* 4KB for LwIP heap */ - -/* Number of pbufs in pool */ #define PBUF_POOL_SIZE 8 - -/* Size of each pbuf in pool (must hold one Ethernet frame) */ #define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS + 40 + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN) - -/* Number of memp struct pbufs */ #define MEMP_NUM_PBUF 8 - -/* Number of raw PCBs */ #define MEMP_NUM_RAW_PCB 2 - -/* Number of UDP PCBs */ -#define MEMP_NUM_UDP_PCB 4 - -/* Number of simultaneously active TCP connections */ +#define MEMP_NUM_UDP_PCB 1 #define MEMP_NUM_TCP_PCB 4 - -/* Number of listening TCP connections */ #define MEMP_NUM_TCP_PCB_LISTEN 2 - -/* Number of simultaneously queued TCP segments */ -#define MEMP_NUM_TCP_SEG 17 - -/* Number of simultaneously active timeouts */ +#define MEMP_NUM_TCP_SEG 16 #define MEMP_NUM_SYS_TIMEOUT 8 - -/* Number of netbufs (for sequential API) */ -#define MEMP_NUM_NETBUF 4 - -/* Number of netconns */ -#define MEMP_NUM_NETCONN 6 - -/* TCPIP message queue size */ -#define MEMP_NUM_TCPIP_MSG_API 8 -#define MEMP_NUM_TCPIP_MSG_INPKT 8 - -/*----------------------------------------------------------------------------- - * IP Configuration - *---------------------------------------------------------------------------*/ +#define MEMP_NUM_NETBUF 0 +#define MEMP_NUM_NETCONN 0 +#define MEMP_NUM_TCPIP_MSG_API 0 +#define MEMP_NUM_TCPIP_MSG_INPKT 0 #define LWIP_IPV4 1 #define LWIP_IPV6 0 - -/* No IP forwarding (single interface device) */ #define IP_FORWARD 0 - -/* IP fragment reassembly */ #define IP_REASSEMBLY 0 #define IP_FRAG 0 - -/* IP options processing */ #define IP_OPTIONS_ALLOWED 1 -/*----------------------------------------------------------------------------- - * ICMP Configuration - *---------------------------------------------------------------------------*/ - #define LWIP_ICMP 1 -#define ICMP_TTL 255 - -/*----------------------------------------------------------------------------- - * ARP Configuration - *---------------------------------------------------------------------------*/ - #define LWIP_ARP 1 #define ARP_TABLE_SIZE 10 #define ARP_QUEUEING 1 #define ETHARP_SUPPORT_STATIC_ENTRIES 1 -/*----------------------------------------------------------------------------- - * DHCP Configuration - *---------------------------------------------------------------------------*/ - -#define LWIP_DHCP 1 +#define LWIP_DHCP 0 #define DHCP_DOES_ARP_CHECK 1 -/*----------------------------------------------------------------------------- - * UDP Configuration - *---------------------------------------------------------------------------*/ - -#define LWIP_UDP 1 -#define UDP_TTL 255 - -/*----------------------------------------------------------------------------- - * TCP Configuration (optimized for transparent transmission) - *---------------------------------------------------------------------------*/ - +#define LWIP_UDP 0 #define LWIP_TCP 1 -#define TCP_TTL 255 - -/* TCP Maximum Segment Size */ -#define TCP_MSS 536 /* Conservative value for compatibility */ - -/* TCP sender buffer space (bytes) */ -#define TCP_SND_BUF (4 * TCP_MSS) - -/* TCP sender buffer space (pbufs) */ -#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) - -/* TCP receive window */ -#define TCP_WND (4 * TCP_MSS) - -/* TCP writable space threshold */ -#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) - -/* Enable TCP keepalive */ -#define LWIP_TCP_KEEPALIVE 1 - -/* TCP segment queue handling */ -#define TCP_QUEUE_OOSEQ 0 /* Disable out-of-order segment queuing to save RAM */ - -/* Maximum number of retransmissions */ -#define TCP_MAXRTX 12 -#define TCP_SYNMAXRTX 6 - -/* TCP listen backlog */ -#define TCP_LISTEN_BACKLOG 1 - -/* TCP timestamp option */ -#define LWIP_TCP_TIMESTAMPS 0 - -/*----------------------------------------------------------------------------- - * RAW API (used for ping, etc.) - *---------------------------------------------------------------------------*/ - #define LWIP_RAW 1 -/*----------------------------------------------------------------------------- - * DNS Configuration - *---------------------------------------------------------------------------*/ +#define TCP_TTL 255 +#define UDP_TTL 255 +#define ICMP_TTL 255 -#define LWIP_DNS 0 /* Disable DNS to save RAM */ +#define TCP_MSS 536 +#define TCP_SND_BUF (4 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define TCP_WND (4 * TCP_MSS) +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#define LWIP_TCP_KEEPALIVE 1 +#define TCP_QUEUE_OOSEQ 0 +#define TCP_MAXRTX 12 +#define TCP_SYNMAXRTX 6 +#define TCP_LISTEN_BACKLOG 1 +#define LWIP_TCP_TIMESTAMPS 0 -/*----------------------------------------------------------------------------- - * IGMP Configuration - *---------------------------------------------------------------------------*/ +#define LWIP_DNS 0 +#define LWIP_IGMP 0 -#define LWIP_IGMP 0 /* Disable IGMP to save RAM */ +#define LWIP_NETIF_STATUS_CALLBACK 0 +#define LWIP_NETIF_LINK_CALLBACK 0 -/*----------------------------------------------------------------------------- - * Callback Configuration - *---------------------------------------------------------------------------*/ - -#define LWIP_NETIF_STATUS_CALLBACK 1 -#define LWIP_NETIF_LINK_CALLBACK 1 - -/*----------------------------------------------------------------------------- - * Checksum Configuration - *---------------------------------------------------------------------------*/ - -/* Use software checksums (CH390 doesn't have checksum offload) */ #define CHECKSUM_GEN_IP 1 #define CHECKSUM_GEN_UDP 1 #define CHECKSUM_GEN_TCP 1 @@ -205,106 +81,20 @@ #define CHECKSUM_CHECK_TCP 1 #define CHECKSUM_CHECK_ICMP 1 -/*----------------------------------------------------------------------------- - * Statistics (disabled to save RAM) - *---------------------------------------------------------------------------*/ - #define LWIP_STATS 0 #define LWIP_STATS_DISPLAY 0 - -/*----------------------------------------------------------------------------- - * Debug Options (disabled for production) - *---------------------------------------------------------------------------*/ - #define LWIP_DEBUG 0 -#if LWIP_DEBUG -#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL -#define LWIP_DBG_TYPES_ON LWIP_DBG_ON - -#define ETHARP_DEBUG LWIP_DBG_OFF -#define NETIF_DEBUG LWIP_DBG_OFF -#define PBUF_DEBUG LWIP_DBG_OFF -#define API_LIB_DEBUG LWIP_DBG_OFF -#define API_MSG_DEBUG LWIP_DBG_OFF -#define SOCKETS_DEBUG LWIP_DBG_OFF -#define ICMP_DEBUG LWIP_DBG_OFF -#define IGMP_DEBUG LWIP_DBG_OFF -#define INET_DEBUG LWIP_DBG_OFF -#define IP_DEBUG LWIP_DBG_OFF -#define IP_REASS_DEBUG LWIP_DBG_OFF -#define RAW_DEBUG LWIP_DBG_OFF -#define MEM_DEBUG LWIP_DBG_OFF -#define MEMP_DEBUG LWIP_DBG_OFF -#define SYS_DEBUG LWIP_DBG_OFF -#define TIMERS_DEBUG LWIP_DBG_OFF -#define TCP_DEBUG LWIP_DBG_OFF -#define TCP_INPUT_DEBUG LWIP_DBG_OFF -#define TCP_FR_DEBUG LWIP_DBG_OFF -#define TCP_RTO_DEBUG LWIP_DBG_OFF -#define TCP_CWND_DEBUG LWIP_DBG_OFF -#define TCP_WND_DEBUG LWIP_DBG_OFF -#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF -#define TCP_RST_DEBUG LWIP_DBG_OFF -#define TCP_QLEN_DEBUG LWIP_DBG_OFF -#define UDP_DEBUG LWIP_DBG_OFF -#define TCPIP_DEBUG LWIP_DBG_OFF -#define DHCP_DEBUG LWIP_DBG_OFF -#endif /* LWIP_DEBUG */ - -/*----------------------------------------------------------------------------- - * FreeRTOS Specific Options - *---------------------------------------------------------------------------*/ - -/* Task stack sizes */ -#define TCPIP_THREAD_STACKSIZE 512 -#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2) - -#define DEFAULT_THREAD_STACKSIZE 256 -#define DEFAULT_THREAD_PRIO (configMAX_PRIORITIES - 3) - -/* Mailbox sizes */ -#define TCPIP_MBOX_SIZE 8 -#define DEFAULT_RAW_RECVMBOX_SIZE 4 -#define DEFAULT_UDP_RECVMBOX_SIZE 4 -#define DEFAULT_TCP_RECVMBOX_SIZE 8 -#define DEFAULT_ACCEPTMBOX_SIZE 4 - -/* Thread name length */ -#define LWIP_NETCONN_SEM_PER_THREAD 1 - -/*----------------------------------------------------------------------------- - * Ethernet Specific - *---------------------------------------------------------------------------*/ - -/* Ethernet MTU */ #define LWIP_ETHERNET 1 - -/* Link layer header overhead */ -#define PBUF_LINK_HLEN 14 /* Ethernet header size */ +#define PBUF_LINK_HLEN 14 #define PBUF_LINK_ENCAPSULATION_HLEN 0 +#define LWIP_NETIF_HOSTNAME 0 -/* Use static Ethernet address (configured at runtime) */ -#define LWIP_NETIF_HOSTNAME 1 - -/*----------------------------------------------------------------------------- - * Socket Options - *---------------------------------------------------------------------------*/ - -#define LWIP_SO_SNDTIMEO 1 -#define LWIP_SO_RCVTIMEO 1 -#define LWIP_SO_RCVBUF 0 -#define SO_REUSE 1 - -/*----------------------------------------------------------------------------- - * Additional Options - *---------------------------------------------------------------------------*/ - -/* Enable loop interface for testing */ #define LWIP_HAVE_LOOPIF 0 #define LWIP_NETIF_LOOPBACK 0 +#define LWIP_TIMERS 1 +#define LWIP_TIMERS_CUSTOM 0 -/* Random number generator (required for some TCP operations) */ #define LWIP_RAND() ((uint32_t)rand()) -#endif /* LWIP_LWIPOPTS_H */ +#endif diff --git a/Drivers/LwIP/src/include/arch/sys_arch.h b/Drivers/LwIP/src/include/arch/sys_arch.h index 6ce643a..d43f095 100644 --- a/Drivers/LwIP/src/include/arch/sys_arch.h +++ b/Drivers/LwIP/src/include/arch/sys_arch.h @@ -1,90 +1,28 @@ /** * @file sys_arch.h - * @brief LwIP system architecture for FreeRTOS + * @brief Minimal sys_arch definitions for lwIP NO_SYS mode. */ #ifndef __SYS_ARCH_H__ #define __SYS_ARCH_H__ -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" -#include "semphr.h" -#include "lwip/arch.h" -#include +#include -#ifdef __cplusplus -extern "C" { +typedef uint32_t sys_prot_t; + +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#ifndef SYS_ARCH_DECL_PROTECT +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev #endif -/* Semaphore type */ -typedef SemaphoreHandle_t sys_sem_t; - -/* Mutex type */ -typedef SemaphoreHandle_t sys_mutex_t; - -/* Mailbox (message queue) type */ -typedef QueueHandle_t sys_mbox_t; - -/* Thread type */ -typedef TaskHandle_t sys_thread_t; - -/* Protection level type */ -typedef u32_t sys_prot_t; - -/* Null values */ -#define SYS_SEM_NULL ((sys_sem_t)NULL) -#define SYS_MBOX_NULL ((sys_mbox_t)NULL) -#define SYS_MUTEX_NULL ((sys_mutex_t)NULL) - -/* Use one per-thread semaphore for lwIP netconn/socket API calls. */ -#define LWIP_NETCONN_THREAD_SEM_TLS_INDEX 0 -#define LWIP_NETCONN_THREAD_SEM_GET() \ - ((sys_sem_t *)pvTaskGetThreadLocalStoragePointer(NULL, LWIP_NETCONN_THREAD_SEM_TLS_INDEX)) -#define LWIP_NETCONN_THREAD_SEM_ALLOC() \ - do { \ - sys_sem_t *sem = (sys_sem_t *)mem_malloc(sizeof(sys_sem_t)); \ - if (sem != NULL) { \ - *sem = SYS_SEM_NULL; \ - if (sys_sem_new(sem, 0) == ERR_OK) { \ - vTaskSetThreadLocalStoragePointer(NULL, \ - LWIP_NETCONN_THREAD_SEM_TLS_INDEX,\ - sem); \ - } else { \ - mem_free(sem); \ - } \ - } \ - } while (0) -#define LWIP_NETCONN_THREAD_SEM_FREE() \ - do { \ - sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); \ - if (sem != NULL) { \ - sys_sem_free(sem); \ - mem_free(sem); \ - vTaskSetThreadLocalStoragePointer(NULL, \ - LWIP_NETCONN_THREAD_SEM_TLS_INDEX, \ - NULL); \ - } \ - } while (0) - -/* Check if semaphore/mbox is valid */ -#define sys_sem_valid(sem) ((sem) != NULL && (*(sem)) != SYS_SEM_NULL) -#define sys_sem_set_invalid(sem) do { if ((sem) != NULL) { *(sem) = SYS_SEM_NULL; } } while(0) - -#define sys_mbox_valid(mbox) ((mbox) != NULL && (*(mbox)) != SYS_MBOX_NULL) -#define sys_mbox_set_invalid(mbox) do { if ((mbox) != NULL) { *(mbox) = SYS_MBOX_NULL; } } while(0) - -#define sys_mutex_valid(mutex) ((mutex) != NULL && (*(mutex)) != SYS_MUTEX_NULL) -#define sys_mutex_set_invalid(mutex) do { if ((mutex) != NULL) { *(mutex) = SYS_MUTEX_NULL; } } while(0) - -/* System initialization */ -void sys_init(void); - -/* Get current time in milliseconds */ -u32_t sys_now(void); - -#ifdef __cplusplus -} +#ifndef SYS_ARCH_PROTECT +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() #endif -#endif /* __SYS_ARCH_H__ */ +#ifndef SYS_ARCH_UNPROTECT +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +#endif + +#endif diff --git a/Drivers/LwIP/src/include/netif/ethernetif.h b/Drivers/LwIP/src/include/netif/ethernetif.h index 4cc9e38..e456b56 100644 --- a/Drivers/LwIP/src/include/netif/ethernetif.h +++ b/Drivers/LwIP/src/include/netif/ethernetif.h @@ -5,23 +5,17 @@ extern struct netif ch390_netif; -/** - * Helper struct to hold private data used to operate your ethernet interface. - * Keeping the ethernet address of the MAC in this struct is not necessary - * as it is already kept in the struct netif. - * But this is only an example, anyway... - */ struct ethernetif { -// struct eth_addr *ethaddr; - /* Add whatever per-interface state that is needed here. */ uint16_t rx_len; uint8_t rx_status; }; -void init_lwip_netif(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw); +void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw); err_t ethernetif_init(struct netif *netif); void ethernetif_input(struct netif *netif); +void ethernetif_check_link(void); +void ethernetif_poll(void); +void ethernetif_set_irq_pending(void); +uint8_t ethernetif_is_irq_pending(void); -void print_netif(struct netif *netif); - -#endif /* _ETHERNETIF_H_ */ +#endif diff --git a/Drivers/LwIP/src/netif/ethernet.c b/Drivers/LwIP/src/netif/ethernet.c new file mode 100644 index 0000000..988cea9 --- /dev/null +++ b/Drivers/LwIP/src/netif/ethernet.c @@ -0,0 +1,213 @@ +/** + * @file + * Ethernet common functions + * + * @defgroup ethernet Ethernet + * @ingroup callbackstyle_api + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "netif/ethernet.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/etharp.h" +#include "lwip/ip.h" +#include "lwip/snmp.h" + +#include + +#include "netif/ppp/ppp_opts.h" +#if PPPOE_SUPPORT +#include "netif/ppp/pppoe.h" +#endif + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +const struct eth_addr ethbroadcast = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; +const struct eth_addr ethzero = {{0, 0, 0, 0, 0, 0}}; + +err_t ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr *ethhdr; + u16_t type; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 + u16_t next_hdr_offset = SIZEOF_ETH_HDR; +#endif + + LWIP_ASSERT_CORE_LOCKED(); + + if (p->len <= SIZEOF_ETH_HDR) { + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + MIB2_STATS_NETIF_INC(netif, ifinerrors); + goto free_and_return; + } + + ethhdr = (struct eth_hdr *)p->payload; + type = ethhdr->type; + +#if ETHARP_SUPPORT_VLAN + if (type == PP_HTONS(ETHTYPE_VLAN)) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR); + next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + MIB2_STATS_NETIF_INC(netif, ifinerrors); + goto free_and_return; + } + type = vlan->tpid; + } +#endif + +#if LWIP_ARP_FILTER_NETIF + netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type)); +#endif + + if (p->if_idx == NETIF_NO_INDEX) { + p->if_idx = netif_get_index(netif); + } + + if (ethhdr->dest.addr[0] & 1) { + if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) { +#if LWIP_IPV4 + if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) { + p->flags |= PBUF_FLAG_LLMCAST; + } +#endif + } +#if LWIP_IPV6 + else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) && + (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) { + p->flags |= PBUF_FLAG_LLMCAST; + } +#endif + else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + p->flags |= PBUF_FLAG_LLBCAST; + } + } + + switch (type) { +#if LWIP_IPV4 && LWIP_ARP + case PP_HTONS(ETHTYPE_IP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + if (pbuf_remove_header(p, next_hdr_offset)) { + goto free_and_return; + } else { + ip4_input(p, netif); + } + break; + + case PP_HTONS(ETHTYPE_ARP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + if (pbuf_remove_header(p, next_hdr_offset)) { + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } else { + etharp_input(p, netif); + } + break; +#endif + +#if PPPOE_SUPPORT + case PP_HTONS(ETHTYPE_PPPOEDISC): + pppoe_disc_input(netif, p); + break; + + case PP_HTONS(ETHTYPE_PPPOE): + pppoe_data_input(netif, p); + break; +#endif + +#if LWIP_IPV6 + case PP_HTONS(ETHTYPE_IPV6): + if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { + goto free_and_return; + } else { + ip6_input(p, netif); + } + break; +#endif + + default: +#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL + if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { + break; + } +#endif + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); + goto free_and_return; + } + + return ERR_OK; + +free_and_return: + pbuf_free(p); + return ERR_OK; +} + +err_t ethernet_output(struct netif *netif, struct pbuf *p, + const struct eth_addr *src, const struct eth_addr *dst, + u16_t eth_type) +{ + struct eth_hdr *ethhdr; + u16_t eth_type_be = lwip_htons(eth_type); + +#if ETHARP_SUPPORT_VLAN && (defined(LWIP_HOOK_VLAN_SET) || LWIP_VLAN_PCP) + s32_t vlan_prio_vid; +#ifdef LWIP_HOOK_VLAN_SET + vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type); +#elif LWIP_VLAN_PCP + vlan_prio_vid = -1; + if (netif->hints && (netif->hints->tci >= 0)) { + vlan_prio_vid = (u16_t)netif->hints->tci; + } +#endif + if (vlan_prio_vid >= 0) { + struct eth_vlan_hdr *vlanhdr; + + LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF); + + if (pbuf_add_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) { + goto pbuf_header_failed; + } + vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR); + vlanhdr->tpid = eth_type_be; + vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid); + eth_type_be = PP_HTONS(ETHTYPE_VLAN); + } else +#endif + { + if (pbuf_add_header(p, SIZEOF_ETH_HDR) != 0) { + goto pbuf_header_failed; + } + } + + LWIP_ASSERT_CORE_LOCKED(); + + ethhdr = (struct eth_hdr *)p->payload; + ethhdr->type = eth_type_be; + SMEMCPY(ðhdr->dest, dst, ETH_HWADDR_LEN); + SMEMCPY(ðhdr->src, src, ETH_HWADDR_LEN); + + return netif->linkoutput(netif, p); + +pbuf_header_failed: + LINK_STATS_INC(link.lenerr); + return ERR_BUF; +} + +#endif diff --git a/Drivers/LwIP/src/netif/ethernetif.c b/Drivers/LwIP/src/netif/ethernetif.c index 7131e0b..a8a7702 100644 --- a/Drivers/LwIP/src/netif/ethernetif.c +++ b/Drivers/LwIP/src/netif/ethernetif.c @@ -1,150 +1,120 @@ /** * @file ethernetif.c - * @brief Ethernet interface implementation for CH390 + LwIP + FreeRTOS + * @brief CH390 Ethernet interface for lwIP NO_SYS mode. */ #include "lwip/opt.h" + #include "lwip/def.h" +#include "lwip/init.h" #include "lwip/mem.h" #include "lwip/pbuf.h" #include "lwip/stats.h" #include "lwip/snmp.h" #include "lwip/etharp.h" -#include "lwip/tcpip.h" +#include "lwip/timeouts.h" #include "netif/ethernet.h" #include "ethernetif.h" #include "CH390.h" #include "CH390_Interface.h" +#include "config.h" +#include "stm32f1xx_hal.h" -#include "FreeRTOS.h" -#include "task.h" -#include "semphr.h" - -#include - -/* Interface name */ #define IFNAME0 'e' #define IFNAME1 'n' -/* Global network interface */ struct netif ch390_netif; +static volatile uint8_t g_ch390_irq_pending; +static void ethernetif_unlock(uint32_t primask); -/* Mutex for SPI access protection */ -static SemaphoreHandle_t spi_mutex = NULL; +static uint32_t ethernetif_lock(void) +{ + uint32_t primask = __get_PRIMASK(); + __disable_irq(); + return primask; +} -/* Forward declarations */ -static void low_level_init(struct netif *netif); -static err_t low_level_output(struct netif *netif, struct pbuf *p); -static struct pbuf *low_level_input(struct netif *netif); +sys_prot_t sys_arch_protect(void) +{ + return (sys_prot_t)ethernetif_lock(); +} -/*--------------------------------------------------------------------------- - * Low Level Hardware Functions - *---------------------------------------------------------------------------*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ethernetif_unlock((uint32_t)pval); +} + +static void ethernetif_unlock(uint32_t primask) +{ + if ((primask & 1u) == 0u) { + __enable_irq(); + } +} + +void ethernetif_set_irq_pending(void) +{ + g_ch390_irq_pending = 1u; +} + +uint8_t ethernetif_is_irq_pending(void) +{ + return g_ch390_irq_pending; +} -/** - * @brief Initialize the CH390 hardware - * @param netif Network interface structure - */ static void low_level_init(struct netif *netif) { struct ethernetif *ethernetif = netif->state; - - /* Create SPI mutex */ - if (spi_mutex == NULL) - { - spi_mutex = xSemaphoreCreateMutex(); - } - - /* Initialize CH390 GPIO and SPI */ + ch390_gpio_init(); ch390_spi_init(); - - /* Hardware reset CH390 */ ch390_hardware_reset(); - - /* Configure CH390 with default settings */ ch390_default_config(); - - /* Set MAC hardware address length */ + ch390_set_mac_address((uint8_t *)config_get()->mac); + netif->hwaddr_len = ETHARP_HWADDR_LEN; - - /* Get MAC address from CH390 */ ch390_get_mac(netif->hwaddr); - - /* Maximum transfer unit */ netif->mtu = 1500; - - /* Device capabilities */ - netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; - - /* Initialize state */ - ethernetif->rx_len = 0; - ethernetif->rx_status = 0; - - /* Enable CH390 interrupt */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + ethernetif->rx_len = 0u; + ethernetif->rx_status = 0u; + ch390_interrupt_init(); } -/** - * @brief Transmit a packet via CH390 - * @param netif Network interface structure - * @param p Packet buffer to transmit - * @return ERR_OK on success - */ static err_t low_level_output(struct netif *netif, struct pbuf *p) { struct pbuf *q; - - /* Take SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreTake(spi_mutex, portMAX_DELAY); - } - + uint32_t primask; + + LWIP_UNUSED_ARG(netif); + primask = ethernetif_lock(); + #if ETH_PAD_SIZE pbuf_remove_header(p, ETH_PAD_SIZE); #endif - - /* Copy data to CH390 TX buffer */ - for (q = p; q != NULL; q = q->next) - { + + for (q = p; q != NULL; q = q->next) { ch390_write_mem(q->payload, q->len); } - - /* Wait until last transmit complete */ - while (ch390_read_reg(CH390_TCR) & TCR_TXREQ) - { - taskYIELD(); + + while (ch390_read_reg(CH390_TCR) & TCR_TXREQ) { } - - /* Set packet length */ - ch390_write_reg(CH390_TXPLL, p->tot_len & 0xFF); - ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xFF); - - /* Issue transmit request */ + + ch390_write_reg(CH390_TXPLL, p->tot_len & 0xFFu); + ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xFFu); ch390_send_request(); - - /* Release SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreGive(spi_mutex); - } - + ethernetif_unlock(primask); + #if ETH_PAD_SIZE pbuf_add_header(p, ETH_PAD_SIZE); #endif - + LINK_STATS_INC(link.xmit); - return ERR_OK; } -/** - * @brief Receive a packet from CH390 - * @param netif Network interface structure - * @return Packet buffer containing received data, or NULL if no packet - */ static struct pbuf *low_level_input(struct netif *netif) { struct ethernetif *ethernetif = netif->state; @@ -153,281 +123,167 @@ static struct pbuf *low_level_input(struct netif *netif) uint16_t len; uint8_t rx_ready; uint8_t rx_header[4]; - - /* Take SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreTake(spi_mutex, portMAX_DELAY); - } - - /* Check if packet is ready */ - ch390_read_reg(CH390_MRCMDX); /* Dummy read */ + uint32_t primask; + + primask = ethernetif_lock(); + ch390_read_reg(CH390_MRCMDX); rx_ready = ch390_read_reg(CH390_MRCMDX); - - if (rx_ready & CH390_PKT_ERR) - { - /* RX error - reset RX FIFO */ - ch390_write_reg(CH390_RCR, 0); /* RX disable */ - ch390_write_reg(CH390_MPTRCR, 0x01); /* Reset RX FIFO pointer */ - ch390_write_reg(CH390_MRRH, 0x0C); - ch390_delay_us(1000); - ch390_write_reg(CH390_RCR, RCR_RXEN | RCR_DIS_CRC); /* RX Enable */ - + + if (rx_ready & CH390_PKT_ERR) { + ch390_write_reg(CH390_RCR, 0u); + ch390_write_reg(CH390_MPTRCR, 0x01u); + ch390_write_reg(CH390_MRRH, 0x0Cu); + ch390_delay_us(1000u); + ch390_write_reg(CH390_RCR, RCR_RXEN | RCR_DIS_CRC); + ethernetif->rx_len = 0u; LINK_STATS_INC(link.drop); - ethernetif->rx_len = 0; - - if (spi_mutex != NULL) - { - xSemaphoreGive(spi_mutex); - } + ethernetif_unlock(primask); return NULL; } - - if (!(rx_ready & CH390_PKT_RDY)) - { - /* No packet ready */ - ethernetif->rx_len = 0; - - if (spi_mutex != NULL) - { - xSemaphoreGive(spi_mutex); - } + + if ((rx_ready & CH390_PKT_RDY) == 0u) { + ethernetif->rx_len = 0u; + ethernetif_unlock(primask); return NULL; } - - /* Read RX header (status + length) */ + ch390_read_mem(rx_header, 4); ethernetif->rx_status = rx_header[1]; - /* Length includes 4-byte CRC, subtract it */ - ethernetif->rx_len = (rx_header[2] | (rx_header[3] << 8)) - 4; + ethernetif->rx_len = (uint16_t)(((uint16_t)rx_header[2] | ((uint16_t)rx_header[3] << 8)) - 4u); len = ethernetif->rx_len; - + #if ETH_PAD_SIZE len += ETH_PAD_SIZE; #endif - - /* Allocate pbuf chain */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); - - if (p != NULL) - { + if (p != NULL) { #if ETH_PAD_SIZE pbuf_remove_header(p, ETH_PAD_SIZE); #endif - - /* Read packet data into pbuf chain */ - for (q = p; q != NULL; q = q->next) - { + for (q = p; q != NULL; q = q->next) { ch390_read_mem(q->payload, q->len); } - #if ETH_PAD_SIZE pbuf_add_header(p, ETH_PAD_SIZE); #endif - - /* Skip CRC (4 bytes) */ - ch390_drop_packet(4); - + ch390_drop_packet(4u); LINK_STATS_INC(link.recv); - } - else - { - /* No memory - drop packet */ - ch390_drop_packet(ethernetif->rx_len + 4); + } else { + ch390_drop_packet((uint16_t)(ethernetif->rx_len + 4u)); LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); } - - /* Release SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreGive(spi_mutex); - } - + + ethernetif_unlock(primask); return p; } -/*--------------------------------------------------------------------------- - * Public Interface Functions - *---------------------------------------------------------------------------*/ - -/** - * @brief Process received ethernet packets - * @param netif Network interface structure - */ void ethernetif_input(struct netif *netif) { - struct pbuf *p; - - /* Get received packet */ - p = low_level_input(netif); - - if (p != NULL) - { - /* Pass to LwIP stack */ - if (netif->input(p, netif) != ERR_OK) - { - LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + struct pbuf *p = low_level_input(netif); + + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { pbuf_free(p); } } } -/** - * @brief Initialize the ethernet interface - * @param netif Network interface structure - * @return ERR_OK on success - */ err_t ethernetif_init(struct netif *netif) { struct ethernetif *ethernetif; - + LWIP_ASSERT("netif != NULL", (netif != NULL)); - - /* Allocate ethernetif state structure */ + ethernetif = mem_malloc(sizeof(struct ethernetif)); - if (ethernetif == NULL) - { - LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + if (ethernetif == NULL) { return ERR_MEM; } - - memset(ethernetif, 0, sizeof(struct ethernetif)); - -#if LWIP_NETIF_HOSTNAME - netif->hostname = "tcp2uart"; -#endif - - /* Initialize SNMP variables */ - MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 100000000); - + netif->state = ethernetif; netif->name[0] = IFNAME0; netif->name[1] = IFNAME1; - - /* Set output functions */ +#if LWIP_NETIF_HOSTNAME + netif->hostname = "tcp2uart"; +#endif + MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 100000000); #if LWIP_IPV4 netif->output = etharp_output; #endif netif->linkoutput = low_level_output; - - /* Initialize hardware */ low_level_init(netif); - + return ERR_OK; } -/** - * @brief Initialize LwIP network interface - * @param ipaddr IP address - * @param netmask Network mask - * @param gw Gateway address - */ void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw) { - /* Add network interface */ - netif_add(&ch390_netif, ipaddr, netmask, gw, NULL, - ðernetif_init, &tcpip_input); - - /* Set as default interface */ + netif_add(&ch390_netif, ipaddr, netmask, gw, NULL, ðernetif_init, ðernet_input); netif_set_default(&ch390_netif); - - /* Set interface down initially */ netif_set_link_down(&ch390_netif); netif_set_up(&ch390_netif); } -/** - * @brief Check and handle CH390 link status - */ void ethernetif_check_link(void) { - uint8_t link_status; - - /* Take SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreTake(spi_mutex, portMAX_DELAY); - } - - link_status = ch390_get_link_status(); - - /* Release SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreGive(spi_mutex); - } - - if (link_status) - { - if (!netif_is_link_up(&ch390_netif)) - { + uint8_t link_up; + uint32_t primask = ethernetif_lock(); + + link_up = (uint8_t)ch390_get_link_status(); + ethernetif_unlock(primask); + + if (link_up) { + if (!netif_is_link_up(&ch390_netif)) { netif_set_link_up(&ch390_netif); } - } - else - { - if (netif_is_link_up(&ch390_netif)) - { - netif_set_link_down(&ch390_netif); - } + } else if (netif_is_link_up(&ch390_netif)) { + netif_set_link_down(&ch390_netif); } } -/** - * @brief Process all pending RX packets - */ void ethernetif_poll(void) { uint8_t int_status; - - /* Take SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreTake(spi_mutex, portMAX_DELAY); + uint32_t primask; + + if (g_ch390_irq_pending == 0u) { + return; } - - /* Read interrupt status */ + + primask = ethernetif_lock(); int_status = ch390_read_reg(CH390_ISR); - - /* Clear interrupt flags */ ch390_write_reg(CH390_ISR, int_status); - - /* Release SPI mutex */ - if (spi_mutex != NULL) - { - xSemaphoreGive(spi_mutex); - } - - /* Handle link change */ - if (int_status & ISR_LNKCHG) - { + g_ch390_irq_pending = 0u; + ethernetif_unlock(primask); + + if ((int_status & ISR_LNKCHG) != 0u) { ethernetif_check_link(); } - - /* Handle RX overflow */ - if (int_status & ISR_ROS) - { - /* RX overflow - packets might be corrupted */ + + if ((int_status & ISR_ROS) != 0u) { LINK_STATS_INC(link.err); } - - /* Process received packets */ - if (int_status & ISR_PR) - { - /* Process all available packets */ - while (1) - { + + if ((int_status & ISR_PR) != 0u) { + while (1) { struct pbuf *p = low_level_input(&ch390_netif); - if (p == NULL) - { + if (p == NULL) { break; } - - if (ch390_netif.input(p, &ch390_netif) != ERR_OK) - { + if (ch390_netif.input(p, &ch390_netif) != ERR_OK) { pbuf_free(p); } } } } + +u32_t sys_now(void) +{ + return HAL_GetTick(); +} + +u32_t sys_jiffies(void) +{ + return HAL_GetTick(); +} diff --git a/Drivers/LwIP/src/netif/ethernetif.h b/Drivers/LwIP/src/netif/ethernetif.h index 29ada56..701c018 100644 --- a/Drivers/LwIP/src/netif/ethernetif.h +++ b/Drivers/LwIP/src/netif/ethernetif.h @@ -1,63 +1,27 @@ /** * @file ethernetif.h - * @brief Ethernet interface header for CH390 + LwIP + FreeRTOS + * @brief CH390 Ethernet interface for lwIP NO_SYS mode. */ #ifndef __ETHERNETIF_H__ #define __ETHERNETIF_H__ -#include "lwip/netif.h" #include "lwip/err.h" +#include "lwip/netif.h" -#ifdef __cplusplus -extern "C" { -#endif - -/* Ethernet interface state structure */ struct ethernetif { uint16_t rx_len; - uint8_t rx_status; + uint8_t rx_status; }; -/* Global network interface */ extern struct netif ch390_netif; -/** - * @brief Initialize the ethernet interface - * @param netif Network interface structure - * @return ERR_OK on success - */ err_t ethernetif_init(struct netif *netif); - -/** - * @brief Process received ethernet packets - * @param netif Network interface structure - * @note Call this from the LwIP task when packets are available - */ void ethernetif_input(struct netif *netif); - -/** - * @brief Initialize LwIP network interface with static IP - * @param ipaddr IP address - * @param netmask Network mask - * @param gw Gateway address - */ void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw); - -/** - * @brief Check and handle CH390 interrupt status - * @note Call this from the LwIP task periodically or on interrupt - */ void ethernetif_check_link(void); - -/** - * @brief Process all pending RX packets - * @note Call this from the LwIP task when RX interrupt occurs - */ void ethernetif_poll(void); +void ethernetif_set_irq_pending(void); +uint8_t ethernetif_is_irq_pending(void); -#ifdef __cplusplus -} #endif - -#endif /* __ETHERNETIF_H__ */ diff --git a/MDK-ARM/TCP2UART.uvprojx b/MDK-ARM/TCP2UART.uvprojx index f2d76db..8fc56b7 100644 --- a/MDK-ARM/TCP2UART.uvprojx +++ b/MDK-ARM/TCP2UART.uvprojx @@ -55,7 +55,7 @@ 1 1 1 - + TCP2UART\ 1 0 0 @@ -80,9 +80,9 @@ 0 - 0 - 1 - + 1 + 0 + keil-build-viewer.exe -NOPATH 0 0 @@ -340,7 +340,7 @@ USE_HAL_DRIVER,STM32F103xB - ../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Middlewares/Third_Party/FreeRTOS/Source/include;../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2;../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM3;..\Drivers\CH390;..\Drivers\LwIP\src\include;..\Drivers\LwIP\src\include\lwip;..\Drivers\LwIP\src\include\netif;..\Drivers\LwIP\src\include\arch;..\Drivers\LwIP\port;..\App + ../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../Drivers/CH390;../Drivers/LwIP/src/include;../Drivers/LwIP/src/include/lwip;../Drivers/LwIP/src/include/netif;../Drivers/LwIP/src/include/arch;../Drivers/LwIP/src/netif;../App;../Middlewares/Third_Party/SEGGER_RTT @@ -358,7 +358,7 @@ - ../Drivers/CMSIS/Include + @@ -404,62 +404,6 @@ 1 ../Core/Src/gpio.c - - freertos.c - 1 - ../Core/Src/freertos.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - dma.c 1 @@ -500,6 +444,11 @@ 1 ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c + + stm32f1xx_hal_iwdg.c + 1 + ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c + stm32f1xx_hal.c 1 @@ -550,11 +499,6 @@ 1 ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c - - stm32f1xx_hal_iwdg.c - 1 - ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c - stm32f1xx_hal_spi.c 1 @@ -577,640 +521,6 @@ - - Middlewares/FreeRTOS - - - 0 - 0 - 0 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 4 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 1 - - - - - - - - - - - - croutine.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/croutine.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - event_groups.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/event_groups.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - list.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/list.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - queue.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/queue.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - stream_buffer.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - tasks.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/tasks.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - timers.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/timers.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - cmsis_os2.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - heap_4.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - port.c - 1 - ../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c - - - 2 - 0 - 0 - 0 - 0 - 1 - 2 - 2 - 2 - 2 - 11 - - - 1 - - - - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 2 - 0 - 2 - 2 - 2 - 2 - 2 - 0 - 0 - 2 - 2 - 2 - 2 - 2 - - - - - - - - - - - - Drivers/CH390 @@ -1229,31 +539,11 @@ Drivers/LwIP/core - - altcp.c - 1 - ..\Drivers\LwIP\src\core\altcp.c - - - altcp_alloc.c - 1 - ..\Drivers\LwIP\src\core\altcp_alloc.c - - - altcp_tcp.c - 1 - ..\Drivers\LwIP\src\core\altcp_tcp.c - def.c 1 ..\Drivers\LwIP\src\core\def.c - - dns.c - 1 - ..\Drivers\LwIP\src\core\dns.c - inet_chksum.c 1 @@ -1329,21 +619,6 @@ 1 ..\Drivers\LwIP\src\core\udp.c - - acd.c - 1 - ..\Drivers\LwIP\src\core\ipv4\acd.c - - - autoip.c - 1 - ..\Drivers\LwIP\src\core\ipv4\autoip.c - - - dhcp.c - 1 - ..\Drivers\LwIP\src\core\ipv4\dhcp.c - etharp.c 1 @@ -1354,11 +629,6 @@ 1 ..\Drivers\LwIP\src\core\ipv4\icmp.c - - igmp.c - 1 - ..\Drivers\LwIP\src\core\ipv4\igmp.c - ip4.c 1 @@ -1376,59 +646,14 @@ - - Drivers/LwIP/api - - - api_lib.c - 1 - ..\Drivers\LwIP\src\api\api_lib.c - - - api_msg.c - 1 - ..\Drivers\LwIP\src\api\api_msg.c - - - err.c - 1 - ..\Drivers\LwIP\src\api\err.c - - - if_api.c - 1 - ..\Drivers\LwIP\src\api\if_api.c - - - netbuf.c - 1 - ..\Drivers\LwIP\src\api\netbuf.c - - - netdb.c - 1 - ..\Drivers\LwIP\src\api\netdb.c - - - netifapi.c - 1 - ..\Drivers\LwIP\src\api\netifapi.c - - - sockets.c - 1 - ..\Drivers\LwIP\src\api\sockets.c - - - tcpip.c - 1 - ..\Drivers\LwIP\src\api\tcpip.c - - - Drivers/LwIP/netif + + ethernet.c + 1 + ..\Drivers\LwIP\src\netif\ethernet.c + ethernetif.c 1 @@ -1436,16 +661,6 @@ - - Drivers/LwIP/port - - - sys_arch.c - 1 - ..\Drivers\LwIP\port\sys_arch.c - - - APP @@ -1476,6 +691,21 @@ + + Middlewares/SEGGER_RTT + + + SEGGER_RTT.c + 1 + ..\Middlewares\Third_Party\SEGGER_RTT\SEGGER_RTT.c + + + SEGGER_RTT_printf.c + 1 + ..\Middlewares\Third_Party\SEGGER_RTT\SEGGER_RTT_printf.c + + + ::CMSIS diff --git a/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.c b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.c new file mode 100644 index 0000000..d656784 --- /dev/null +++ b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.c @@ -0,0 +1,59 @@ +#include "SEGGER_RTT.h" + +#include + +SEGGER_RTT_CB _SEGGER_RTT; +static char _acUpBuffer[BUFFER_SIZE_UP]; + +void SEGGER_RTT_Init(void) +{ + memset(&_SEGGER_RTT, 0, sizeof(_SEGGER_RTT)); + memcpy(_SEGGER_RTT.acID, "SEGGER RTT", 10); + _SEGGER_RTT.MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS; + _SEGGER_RTT.MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS; + _SEGGER_RTT.aUp[0].sName = "Terminal"; + _SEGGER_RTT.aUp[0].pBuffer = _acUpBuffer; + _SEGGER_RTT.aUp[0].SizeOfBuffer = BUFFER_SIZE_UP; + _SEGGER_RTT.aUp[0].Flags = SEGGER_RTT_MODE_DEFAULT; +} + +static void _EnsureInit(void) +{ + if (_SEGGER_RTT.acID[0] != 'S') { + SEGGER_RTT_Init(); + } +} + +unsigned SEGGER_RTT_Write(unsigned BufferIndex, const void *pBuffer, unsigned NumBytes) +{ + SEGGER_RTT_BUFFER_UP *ring; + const char *data = (const char *)pBuffer; + unsigned i; + + _EnsureInit(); + if (BufferIndex >= (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { + return 0u; + } + + ring = &_SEGGER_RTT.aUp[BufferIndex]; + for (i = 0; i < NumBytes; ++i) { + unsigned next = (ring->WrOff + 1u) % ring->SizeOfBuffer; + if (next == ring->RdOff) { + break; + } + ring->pBuffer[ring->WrOff] = data[i]; + ring->WrOff = next; + } + + return i; +} + +unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char *s) +{ + return SEGGER_RTT_Write(BufferIndex, s, (unsigned)strlen(s)); +} + +unsigned SEGGER_RTT_PutChar(unsigned BufferIndex, char c) +{ + return SEGGER_RTT_Write(BufferIndex, &c, 1u); +} diff --git a/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.h b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.h new file mode 100644 index 0000000..76ae54a --- /dev/null +++ b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT.h @@ -0,0 +1,37 @@ +#ifndef SEGGER_RTT_H +#define SEGGER_RTT_H + +#include "SEGGER_RTT_Conf.h" + +#include + +#define SEGGER_RTT_MODE_NO_BLOCK_SKIP (0) +#define SEGGER_RTT_MODE_NO_BLOCK_TRIM (1) +#define SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (2) + +typedef struct { + const char *sName; + char *pBuffer; + unsigned SizeOfBuffer; + unsigned WrOff; + volatile unsigned RdOff; + unsigned Flags; +} SEGGER_RTT_BUFFER_UP; + +typedef struct { + char acID[16]; + int MaxNumUpBuffers; + int MaxNumDownBuffers; + SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; +} SEGGER_RTT_CB; + +extern SEGGER_RTT_CB _SEGGER_RTT; + +void SEGGER_RTT_Init(void); +unsigned SEGGER_RTT_Write(unsigned BufferIndex, const void *pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char *s); +unsigned SEGGER_RTT_PutChar(unsigned BufferIndex, char c); +int SEGGER_RTT_printf(unsigned BufferIndex, const char *sFormat, ...); +int SEGGER_RTT_vprintf(unsigned BufferIndex, const char *sFormat, va_list *pParamList); + +#endif diff --git a/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h new file mode 100644 index 0000000..e33b98a --- /dev/null +++ b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h @@ -0,0 +1,37 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +*/ + +#ifndef SEGGER_RTT_CONF_H +#define SEGGER_RTT_CONF_H + +#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS + #define SEGGER_RTT_MAX_NUM_UP_BUFFERS 3 +#endif + +#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS + #define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS 3 +#endif + +#ifndef BUFFER_SIZE_UP + #define BUFFER_SIZE_UP 1024 +#endif + +#ifndef BUFFER_SIZE_DOWN + #define BUFFER_SIZE_DOWN 16 +#endif + +#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE + #define SEGGER_RTT_PRINTF_BUFFER_SIZE 128u +#endif + +#ifndef SEGGER_RTT_MODE_DEFAULT + #define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP +#endif + +#define SEGGER_RTT_LOCK() +#define SEGGER_RTT_UNLOCK() + +#endif diff --git a/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_printf.c b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_printf.c new file mode 100644 index 0000000..f7f2abd --- /dev/null +++ b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_printf.c @@ -0,0 +1,30 @@ +#include "SEGGER_RTT.h" + +#include +#include + +int SEGGER_RTT_vprintf(unsigned BufferIndex, const char *sFormat, va_list *pParamList) +{ + char buffer[SEGGER_RTT_PRINTF_BUFFER_SIZE]; + int len = vsnprintf(buffer, sizeof(buffer), sFormat, *pParamList); + + if (len < 0) { + return len; + } + if ((unsigned)len > sizeof(buffer)) { + len = (int)sizeof(buffer); + } + + return (int)SEGGER_RTT_Write(BufferIndex, buffer, (unsigned)len); +} + +int SEGGER_RTT_printf(unsigned BufferIndex, const char *sFormat, ...) +{ + int result; + va_list args; + + va_start(args, sFormat); + result = SEGGER_RTT_vprintf(BufferIndex, sFormat, &args); + va_end(args); + return result; +} diff --git a/项目技术实现.md b/项目技术实现.md index 2b4ad37..0c9c91c 100644 --- a/项目技术实现.md +++ b/项目技术实现.md @@ -1,16 +1,16 @@ -# TCP2UART 项目技术实现(裸机迁移基线) +# TCP2UART 项目技术实现 -## 一、目标 +## 一、当前实现结论 -当前分支 `baremetal-r8` 的目标不是一次性完成全部业务逻辑重写,而是先把工程基线切换到适合 `STM32F103R8T6` 的裸机方向,为后续继续开发提供统一入口。 +当前工程已经从原先的 `FreeRTOS + lwIP socket/netconn` 方向,重构为适配 `STM32F103R8T6` 的裸机实现。 -本阶段已经完成或约束如下: +当前基线特征如下: -1. MCU 目标统一为 `STM32F103R8T6 / STM32F103xB` -2. `CubeMX IOC` 中已移除 `FreeRTOS` 中间件声明 -3. 工程规划转为裸机轮询 + 中断驱动模型 -4. 暂不在本阶段重写 TCP/串口业务逻辑 -5. 保留现有源码作为迁移参考,后续由其他 Agent/开发者继续接力实现 +1. MCU 目标固定为 `STM32F103R8T6 / STM32F103xB` +2. 软件架构改为 `bare-metal main loop + DMA/IDLE + EXTI` +3. 网络栈采用 `lwIP RAW API + NO_SYS=1` +4. 调试输出采用 `SEGGER RTT` +5. 构建目标已通过 `MDK-ARM` 编译,适配 `64KB Flash / 20KB SRAM` ## 二、硬件与资源约束 @@ -27,7 +27,7 @@ - `USART1`:配置串口 - `USART2`:Server 透传串口 - `USART3`:Client 透传串口 -- `DMA1`:3 路 UART 收发 DMA +- `DMA1`:UART 收发 DMA - `EXTI0`:CH390 中断输入 - `IWDG`:独立看门狗 @@ -37,7 +37,7 @@ |------|------|------| | PA2 | USART2_TX | Server 透传串口 | | PA3 | USART2_RX | Server 透传串口 | -| PA4 | SPI1_NSS | CH390D 片选 | +| PA4 | GPIO_Output | CH390D 片选 | | PA5 | SPI1_SCK | CH390D SPI 时钟 | | PA6 | SPI1_MISO | CH390D SPI 数据输入 | | PA7 | SPI1_MOSI | CH390D SPI 数据输出 | @@ -50,326 +50,216 @@ | PC13 | GPIO_Output | 状态 LED | | PD0/PD1 | HSE | 8MHz 外部晶振 | -## 三、为何从 FreeRTOS 迁移到裸机 +## 三、架构选择原因 -`STM32F103R8T6` 的 RAM 只有 `20KB`。当前工程在引入如下组件后,链接空间明显不足: +`STM32F103R8T6` 的资源上限不足以稳定承载原方案中的以下组合: -1. `FreeRTOS` 内核 -2. `CMSIS-RTOS V2` 包装层 +1. `FreeRTOS` +2. `CMSIS-RTOS V2` 3. 多任务栈 -4. `lwIP + socket/netconn` OS 抽象层 -5. 多路 `StreamBuffer / Semaphore / Mutex` +4. `lwIP socket/netconn/tcpip` OS 路线 +5. `StreamBuffer / Semaphore / Mutex` -此前编译已经证明: - -1. 源码层报错可修复 -2. 统一 `R8` 型号后仍然在链接阶段失败 -3. 主要瓶颈是 `RAM + Flash` 同时偏紧 - -因此本项目后续建议的主方向为: +因此当前实现采用如下组合: 1. 去掉 `FreeRTOS` 2. 去掉 `CMSIS-RTOS V2` -3. 避免依赖 `lwIP socket/netconn` -4. 采用裸机主循环 + DMA/IDLE/EXTI 中断驱动 -5. 网络侧改为更贴近资源受限场景的实现模型 +3. 去掉 `lwIP socket/netconn` +4. 改为 `lwIP RAW API + NO_SYS=1` +5. 串口与网口统一由主循环推进 -## 四、裸机架构目标 +## 四、当前软件架构 -### 4.1 总体分层 +### 4.1 分层 ```text +--------------------------------------------------+ -| Application State Machine | -| config / server link / client link / watchdog | +| Application Logic | +| config / tcp_server / tcp_client / uart bridge | +--------------------------------------------------+ -| Transport Scheduler | -| main loop polling + event flags + timeout scan | +| Main Poll Loop | +| ethernetif_poll / sys_check_timeouts / watchdog | +--------------------------------------------------+ -| Network Interface | -| CH390 event polling / packet rx-tx dispatch | +| Peripheral/Event Layer | +| UART DMA+IDLE / DMA IRQ / EXTI / SysTick | +--------------------------------------------------+ -| Peripheral Drivers | -| UART DMA+IDLE / SPI / GPIO / EXTI / Flash | -+--------------------------------------------------+ -| STM32 HAL / CMSIS | +| Drivers | +| CH390 / lwIP netif / HAL | +--------------------------------------------------+ ``` ### 4.2 执行模型 -不再使用任务调度器,改为以下模型: +当前执行模型为: -1. `ISR` 只做最小事件置位与 DMA 状态更新 -2. 主循环统一处理事件、超时、状态机推进 -3. UART RX 继续依赖 `DMA + IDLE` -4. UART TX 可保留 `DMA` -5. CH390 中断只置位 `netif_pending` -6. 网络协议处理在主循环中推进 +1. `SysTick` 提供全局毫秒时基 +2. `EXTI0` 只置位 CH390 待处理标志 +3. `DMA IRQ` 和 `UART IRQ` 只完成回调分发与 IDLE 采样 +4. 主循环统一执行网络轮询、超时推进、串口桥接和看门狗喂狗 -### 4.3 事件源 +## 五、当前模块实现状态 -建议保留以下裸机事件位: +### 5.1 配置模块 + +文件:`App/config.c/.h` + +已实现: + +1. 从 Flash 加载配置 +2. UART1 命令口接收 +3. 常用网络与串口参数解析 +4. 参数保存与软复位请求 + +当前约束: + +1. 构建已关闭 `DHCP`,因此 `AT+DHCP=1` 会明确返回错误 +2. 配置损坏时会回退默认值,但默认值不会自动写回 Flash,仍需手动 `AT+SAVE` + +### 5.2 UART 透传模块 + +文件:`App/uart_trans.c/.h` + +已实现: + +1. `USART2/USART3` 使用 `DMA + IDLE` +2. RX 使用 DMA 缓冲转环形缓冲 +3. TX 使用 DMA 发送 +4. TX/RX 完成由 `HAL_UART_*Callback` 驱动 + +### 5.3 TCP Server 模块 + +文件:`App/tcp_server.c/.h` + +已实现: + +1. `lwIP RAW API` 监听指定端口 +2. 单连接接入 +3. 网络数据写入本地环形缓冲 +4. 主循环中与 UART2 做双向桥接 + +### 5.4 TCP Client 模块 + +文件:`App/tcp_client.c/.h` + +已实现: + +1. `lwIP RAW API` 主动连接远端地址 +2. 断链后按周期重连 +3. 网络数据写入本地环形缓冲 +4. 主循环中与 UART3 做双向桥接 + +### 5.5 CH390 与 netif 模块 + +文件:`Drivers/CH390/*`、`Drivers/LwIP/src/netif/*` + +已实现: + +1. `SPI1 + GPIO CS + EXTI0` 驱动 CH390 +2. `ethernetif.c` 采用 `NO_SYS=1` 路线 +3. CH390 中断在主循环中轮询处理 +4. 配置中的 MAC 地址会在初始化时写入 CH390 + +### 5.6 RTT 调试输出 + +文件:`Middlewares/Third_Party/SEGGER_RTT/*` + +已实现: + +1. 工程内置最小 `SEGGER RTT` 源文件 +2. `main.c` 中 `printf/fputc` 已重定向到 RTT + +## 六、lwIP 配置策略 + +当前 `lwIP` 配置以适配 `R8T6` 资源为原则,核心策略如下: + +1. `NO_SYS = 1` +2. `LWIP_SOCKET = 0` +3. `LWIP_NETCONN = 0` +4. `LWIP_NETIF_API = 0` +5. `LWIP_DHCP = 0` +6. `LWIP_UDP = 0` +7. `LWIP_DNS = 0` +8. `LWIP_IGMP = 0` +9. 关闭 `lwIP` 平台诊断 `printf` + +同时从 `MDK` 工程中移除了: + +1. `FreeRTOS` 相关源码 +2. `lwIP api/socket/netconn/tcpip` 路线源码 +3. `altcp / autoip / acd / dhcp / dns / igmp` 等当前不需要模块 + +## 七、主循环实际骨架 + +当前主循环逻辑可概括为: ```c -typedef enum { - EVENT_NONE = 0x00000000u, - EVENT_CH390_INT = 0x00000001u, - EVENT_UART1_RX_IDLE = 0x00000002u, - EVENT_UART2_RX_IDLE = 0x00000004u, - EVENT_UART3_RX_IDLE = 0x00000008u, - EVENT_UART2_TX_DONE = 0x00000010u, - EVENT_UART3_TX_DONE = 0x00000020u, - EVENT_NET_TIMER_1MS = 0x00000040u, - EVENT_LINK_RETRY = 0x00000080u, - EVENT_CONFIG_PENDING = 0x00000100u, -} app_event_t; -``` - -这些事件位建议通过 `volatile uint32_t g_app_events` 或一个轻量原子位图维护。 - -## 五、建议的软件模块拆分 - -### 5.1 保留模块 - -以下模块可继续保留,但需要去除 RTOS 依赖: - -1. `App/config.c` -2. `App/flash_param.c` -3. `App/tcp_server.*` -4. `App/tcp_client.*` -5. `App/uart_trans.*` -6. `Drivers/CH390/*` -7. `Drivers/LwIP/*` 或后续替代网络栈 - -### 5.2 需要改造的核心模块 - -1. `Core/Src/main.c` -2. `Core/Src/stm32f1xx_it.c` -3. `Core/Src/usart.c` -4. `Core/Src/dma.c` -5. `Core/Src/freertos.c`:后续应移除出构建或清空为兼容壳 -6. `Core/Inc/FreeRTOSConfig.h`:后续可移除出工程 -7. `Drivers/LwIP/port/sys_arch.c`:若完全去 OS,应停止使用该移植层 -8. `Drivers/LwIP/src/include/arch/sys_arch.h` - -### 5.3 建议新增的裸机模块 - -建议后续新增: - -1. `App/app_scheduler.c/.h` - - 统一处理事件位 - - 驱动周期任务 - - 执行状态机推进 - -2. `App/app_net.c/.h` - - 网络初始化 - - CH390 事件分发 - - 链路保活/重连 - -3. `App/app_uart.c/.h` - - UART DMA/IDLE 收发整合 - - 与网络方向的缓冲协调 - -4. `App/app_time.c/.h` - - 基于 `SysTick` 的毫秒计时 - - 软件超时管理 - -## 六、裸机下的关键技术决策 - -### 6.1 延时与时间基准 - -建议统一使用: - -1. `SysTick 1ms` 全局时基 -2. 禁止在主业务路径使用长阻塞 `HAL_Delay` -3. 超时逻辑统一基于 `tick_now - tick_start` - -示例: - -```c -uint32_t app_now_ms(void); -bool app_is_timeout(uint32_t start, uint32_t timeout_ms); -``` - -### 6.2 UART 接收模型 - -保持 `DMA + IDLE` 是合理的,原因: - -1. 可降低 CPU 占用 -2. 适合串口透传场景 -3. 便于在裸机下维持较高吞吐 - -建议 UART RX 流程: - -1. DMA 持续接收至环形或双缓冲 -2. IDLE 中断触发“本帧结束” -3. ISR 只记录长度与事件位 -4. 主循环消费数据并决定转发方向 - -### 6.3 UART 发送模型 - -建议继续使用 `DMA TX`: - -1. 发送启动在主循环中执行 -2. DMA 完成中断只清 busy 标志并置 `TX_DONE` 事件 -3. 发送缓冲统一由应用层管理 - -### 6.4 CH390 访问模型 - -裸机下不再需要 `mutex`,但仍需保证上下文一致性: - -1. ISR 内不要执行复杂 SPI 事务 -2. EXTI 仅置位 `EVENT_CH390_INT` -3. 所有 CH390 SPI 读写都在主循环中完成 -4. 若必须与 DMA 回调共享状态,使用短临界区保护 - -### 6.5 网络栈路线 - -这里需要后续 Agent 决定最终实现路线,建议二选一: - -1. `lwIP RAW API + NO_SYS=1` - - 优点:仍保留成熟 TCP/IP 栈 - - 缺点:迁移复杂度较高,需要重写当前 `socket/netconn` 依赖 - -2. 基于现有 CH390 资料,评估是否存在更轻的直接 socket/简化协议接口 - - 优点:可能进一步减小资源占用 - - 缺点:功能边界和维护成本需重新评估 - -当前更推荐的长期路线是: - -`lwIP RAW API + NO_SYS=1` - -因为这条路线与现有 CH390 + 以太网驱动结构更连续。 - -## 七、主循环设计建议 - -建议采用固定骨架: - -```c -int main(void) +while (1) { - HAL_Init(); - SystemClock_Config(); + ethernetif_poll(); + ethernetif_check_link(); + sys_check_timeouts(); + tcp_client_poll(); + uart_trans_poll(); + config_poll(); - MX_GPIO_Init(); - MX_DMA_Init(); - MX_IWDG_Init(); - MX_USART1_UART_Init(); - MX_USART2_UART_Init(); - MX_USART3_UART_Init(); - MX_SPI1_Init(); + tcp_server <-> UART2; + tcp_client <-> UART3; - app_time_init(); - app_uart_init(); - app_net_init(); - app_config_init(); - - while (1) - { - app_poll_events(); - app_process_config(); - app_process_uart_links(); - app_process_network(); - app_process_timeouts(); - app_feed_watchdog(); + if (reset_requested) { + NVIC_SystemReset(); } + + HAL_IWDG_Refresh(&hiwdg); } ``` -设计原则: +## 八、当前构建状态 -1. 所有步骤可重入或幂等 -2. 每轮主循环不可长时间阻塞 -3. 网络与串口处理都要支持“分段推进” +### 8.1 MDK 构建命令 -## 八、建议的状态机拆分 - -### 8.1 TCP Server 链路 - -```text -IDLE -> LISTEN -> CONNECTED -> CLOSING -> LISTEN +```bat +"C:\Keil_v5\UV4\UV4.exe" -b "D:\code\STM32Project\TCP2UART\MDK-ARM\TCP2UART.uvprojx" -j0 ``` -### 8.2 TCP Client 链路 +### 8.2 当前结果 -```text -IDLE -> RESOLVE/CONFIG -> CONNECTING -> CONNECTED -> RETRY_WAIT -> CONNECTING -``` +当前构建结果: -### 8.3 配置口 +1. `0 Error(s)` +2. `0 Warning(s)` +3. `Code=38664` +4. `RO-data=1272` +5. `RW-data=168` +6. `ZI-data=19120` -```text -IDLE -> RX_FRAME_READY -> PARSE -> EXECUTE -> RESPOND -> IDLE -``` +说明当前版本已经满足: -### 8.4 CH390 网口 +1. `STM32F103R8T6 64KB Flash` 约束 +2. `20KB SRAM` 约束 +3. `MDK-ARM` 可直接编译 -```text -RESET -> INIT -> LINK_CHECK -> RUNNING -> ERROR_RECOVER -> INIT -``` +## 九、当前已知限制与待验证项 -## 九、内存预算建议 +### 9.1 功能限制 -以 `STM32F103R8T6` 为目标,后续实现应尽量遵循: +1. 当前使用静态 IP,不支持 DHCP +2. 目前未提供上板网络与串口吞吐测试结论 +3. `config` 模块仍保留较重的字符串解析逻辑,但当前体积已可接受 -### 9.1 推荐 RAM 预算 +### 9.2 上板验证重点 -| 项目 | 建议值 | 说明 | -|------|--------|------| -| 启动栈 | 1 KB | `startup` 保留 | -| C Heap | 0 | 默认关闭 | -| UART1 RX/TX | 384 B | 配置口 | -| UART2 RX/TX | 1 KB | 透传链路 | -| UART3 RX/TX | 1 KB | 透传链路 | -| 网络收发缓存 | 2-4 KB | 视协议栈路线而定 | -| 参数/状态结构 | < 2 KB | 控制状态 | +1. 验证 CH390 INT 极性与 EXTI 触发行为 +2. 验证 `SPI1` 与 CH390 的稳定性 +3. 验证 `UART2/UART3 DMA + IDLE` 在长连续流量下的行为 +4. 验证 TCP Server 与 TCP Client 双链路同时工作时的稳定性 +5. 验证配置保存、复位、MAC 生效路径 -### 9.2 原则 +## 十、后续建议 -1. 避免动态分配 -2. 优先静态缓冲 + 明确大小 -3. 单向链路缓冲优先复用 -4. 所有大缓冲区要在文档中登记 +下一阶段建议按以下顺序推进: -## 十、当前已完成的工程侧修改 - -本分支当前已经完成: - -1. `R8/xB` 型号统一 -2. MDK 启动文件切换到 `startup_stm32f103xb.s` -3. `IOC` 中移除 `FREERTOS` 中间件声明 -4. `PendSV/SVCall` 不再作为 RTOS 中断入口保留 -5. `Heap/Stack` 的工程默认值收缩到更适合 `R8` - -## 十一、后续 Agent 接手时应优先处理的事项 - -### 11.1 工程配置层 - -1. 从 `MDK-ARM/TCP2UART.uvprojx` 中移除 `FreeRTOS` 源文件组 -2. 从包含路径中移除 `CMSIS_RTOS_V2` 和 `FreeRTOS` 路径 -3. 视需要移除 `Core/Src/freertos.c` -4. 视需要移除 `Core/Inc/FreeRTOSConfig.h` - -### 11.2 代码层 - -1. 将所有 `osThreadNew`、`vTaskDelay`、`xStreamBuffer*`、`xSemaphore*`、`xMutex*` 调用替换为裸机实现 -2. 将 `lwIP` 从 `NO_SYS=0` 路线迁移到裸机可用路线 -3. 重写 `CH390` 事件处理为主循环驱动 -4. 重写 UART 透传调度逻辑为状态机 - -### 11.3 风险点 - -1. 当前 `socket/netconn` 方案不能直接脱离 OS 使用 -2. `sys_arch` 相关移植层最终应被清退 -3. 原来依赖任务切换“自然解耦”的路径,迁移到裸机后必须明确状态和时序边界 - -## 十二、交接说明 - -当前分支适合作为“裸机迁移起点”,但不是最终可编译成品。它的价值在于: - -1. 目标器件与工程元数据已经统一 -2. `CubeMX` 方向已经从 `FreeRTOS` 转向裸机 -3. 技术实现文档已明确后续重构路线 - -接下来的工作重点应由后续 Agent 在此基线上继续完成逻辑代码迁移。 +1. 上板联调 CH390 链路与 RTT 输出 +2. 验证 UART2/3 透传功能 +3. 补充双向透传稳定性与丢包测试 +4. 视需要继续优化 `config.c` 的体积与命令集 +5. 若后续必须支持 DHCP,再单独评估资源预算 diff --git a/项目需求说明.md b/项目需求说明.md index 9ee48e1..bad7e1d 100644 --- a/项目需求说明.md +++ b/项目需求说明.md @@ -2,58 +2,94 @@ ## 一、项目概述 -基于 STM32F103 单片机和 FreeRTOS 开发一款具有双网口通信功能的 TCP 串口透传设备,实现网络数据与串口数据之间的双向透明传输。 +基于 `STM32F103R8T6` 和 `CH390D` 实现双链路 TCP 串口透传设备。设备提供一条 TCP Server 链路和一条 TCP Client 链路,分别与两路 UART 做双向透明传输,并通过 UART1 进行参数配置。 + +当前项目实现路线已经固定为: + +1. `STM32CubeMX + HAL` +2. `bare-metal` +3. `lwIP RAW API + NO_SYS=1` +4. `SEGGER RTT` 调试输出 + +不再采用 `FreeRTOS` 作为正式交付架构。 ## 二、硬件平台 | 项目 | 说明 | |------|------| -| 主控芯片 | STM32F103R8T6(后续大批量生产可用 GD32 替代) | -| 以太网芯片 | CH390D | -| PCB 设计工具 | 立创 EDA(避免 AD 版权纠纷) | -| 串口通道 | 2 路 UART | +| 主控芯片 | `STM32F103R8T6` | +| 以太网芯片 | `CH390D` | +| PCB 设计工具 | 立创 EDA | +| 串口通道 | `UART1 + UART2 + UART3` | + +说明: + +1. `UART1` 用于配置口 +2. `UART2` 对应 TCP Server 透传链路 +3. `UART3` 对应 TCP Client 透传链路 ## 三、软件平台 | 项目 | 说明 | |------|------| -| 开发环境 | STM32CubeMX + HAL 库 | -| 操作系统 | FreeRTOS | -| 协议栈 | 标准 TCP/IP 协议 | +| 开发环境 | `STM32CubeMX + HAL + MDK-ARM` | +| 执行模型 | 裸机主循环 + 中断驱动 | +| 协议栈 | `lwIP RAW API` | +| 调试输出 | `SEGGER RTT` | ## 四、核心功能需求 ### 4.1 双链路 TCP 通信 -- **Server 链路**:设备作为 TCP Server,监听指定端口,等待外部客户端连接 -- **Client 链路**:设备作为 TCP Client,主动连接远程服务器 -- 两条链路共享**同一个对外 IP 地址** +- `Server` 链路:设备作为 TCP Server,监听指定端口 +- `Client` 链路:设备作为 TCP Client,主动连接远程服务器 +- 两条链路共享同一个设备 IP 地址 ### 4.2 串口透传 -- **Server 链路数据** <==> **UART2** 双向透传 -- **Client 链路数据** <==> **UART3** 双向透传 -- 仅透传 TCP 数据区(Payload),无需解析串口协议 +- `Server` 链路数据 <=> `UART2` 双向透传 +- `Client` 链路数据 <=> `UART3` 双向透传 +- 仅透传 TCP Payload,不解析业务层协议 ### 4.3 参数配置 -- 支持通过 **UART1** 串口命令修改设备 IP 地址等网络参数 -- 配置参数需掉电保存 +- 通过 `UART1` 配置网络与串口参数 +- 配置参数掉电保存 +- 支持设备复位后按保存配置生效 -### 4.4 数据可靠性 +### 4.4 调试与维护 -- 确保 TCP 数据与串口数据双向传输不丢包 -- 提供丢包率测试方案及测试数据 +- 调试输出统一走 `SEGGER RTT` +- 工程需可在 `MDK-ARM` 下直接构建 -## 五、交付物 +## 五、当前实现边界 -1. 原理图及 PCB 设计文件(立创 EDA 格式) -2. STM32 固件源码(CubeMX 工程 + HAL 库 + FreeRTOS) -3. 丢包测试方案及测试工具/数据 +基于 `STM32F103R8T6` 的 `64KB Flash / 20KB SRAM` 约束,当前交付版本约束如下: + +1. 使用静态 IP +2. 当前构建不支持 DHCP +3. 不使用 `lwIP socket/netconn` +4. 不使用 `FreeRTOS` + +这不是降级,而是基于资源约束后的正式实现路线。 + +## 六、数据可靠性要求 + +- 目标是保证 TCP 数据与串口数据双向透传稳定工作 +- 需要后续补充上板联调后的丢包率测试方案与结果 +- 需要验证双链路同时工作时的稳定性 + +## 七、交付物 + +1. 原理图及 PCB 设计文件 +2. STM32 固件源码 +3. `CubeMX` 工程与 `MDK-ARM` 工程 4. 使用说明文档 +5. 后续补充的透传与丢包测试结果 -## 六、约束条件 +## 八、约束条件 -- 通信协议:标准 TCP/IP -- 串口透传:纯数据透传,不解析上层协议 -- 硬件尺寸及供电参数由甲方提供 +1. 通信协议为标准 TCP/IP +2. 串口透传为纯数据透传,不解析上层协议 +3. 当前正式目标器件为 `STM32F103R8T6` +4. 所有正式实现应服从 `64KB Flash / 20KB SRAM` 约束