diff --git a/.gitignore b/.gitignore index 86954f0..3f7fe19 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ Release/ MDK-ARM/DebugConfig/ MDK-ARM/TCP2UART/ +# CMake build +build/ + # OS Thumbs.db Desktop.ini diff --git a/App/config.c b/App/config.c index f5a88f0..cc195f7 100644 --- a/App/config.c +++ b/App/config.c @@ -1,32 +1,61 @@ /** * @file config.c - * @brief Bare-metal AT command configuration module implementation. + * @brief Bare-metal final AT configuration module implementation. */ #include "config.h" #include "flash_param.h" -#include "usart.h" +#include "../Core/Inc/usart.h" #include #include #include #include -#include "SEGGER_RTT.h" - -#define CONFIG_TX_BUFFER_SIZE 256u -#define CONFIG_CMD_MAX_LEN 128u +#define CONFIG_TX_BUFFER_SIZE 512u +#define CONFIG_CMD_MAX_LEN 160u #define CONFIG_UART_HANDLE huart1 +typedef struct { + uint32_t magic; + uint16_t version; + uint16_t reserved; + uint8_t mac[6]; + uint8_t dhcp_enable; + uint8_t reserved2; + uint8_t ip[4]; + uint8_t mask[4]; + uint8_t gw[4]; + uint16_t server_port; + uint16_t reserved3; + uint8_t remote_ip[4]; + uint16_t remote_port; + uint16_t reconnect_interval; + uint32_t uart2_baudrate; + uint32_t uart3_baudrate; + uint8_t uart2_databits; + uint8_t uart2_stopbits; + uint8_t uart2_parity; + uint8_t uart3_databits; + uint8_t uart3_stopbits; + uint8_t uart3_parity; + uint16_t reserved4; + uint32_t crc; +} legacy_device_config_v2_t; + static device_config_t g_config; static volatile bool g_reset_requested; -static char g_uart_cmd_buffer[CONFIG_CMD_MAX_LEN]; +static uint8_t g_uart_cmd_buffer[CONFIG_CMD_MAX_LEN]; static uint16_t g_uart_cmd_len; +static bool g_uart_rx_seen_cr; static char g_pending_cmd_buffer[CONFIG_CMD_MAX_LEN]; static volatile uint16_t g_pending_cmd_len; static volatile bool g_pending_cmd_ready; +static char g_at_response_buffer[CONFIG_TX_BUFFER_SIZE]; +static char g_cmd_parse_buffer[CONFIG_CMD_MAX_LEN]; +static char g_cmd_work_buffer[CONFIG_CMD_MAX_LEN]; static uint32_t config_calc_crc(const device_config_t *cfg) { @@ -56,8 +85,12 @@ static bool equals_ignore_case(const char *a, const char *b) char c1 = *a++; char c2 = *b++; - if (c1 >= 'a' && c1 <= 'z') c1 -= 32; - if (c2 >= 'a' && c2 <= 'z') c2 -= 32; + if (c1 >= 'a' && c1 <= 'z') { + c1 -= 32; + } + if (c2 >= 'a' && c2 <= 'z') { + c2 -= 32; + } if (c1 != c2) { return false; @@ -67,12 +100,31 @@ static bool equals_ignore_case(const char *a, const char *b) return (*a == '\0' && *b == '\0'); } -static int parse_baudrate_value(const char *value, uint32_t *baudrate) +static int prefix_equals_ignore_case(const char *str, const char *prefix) +{ + while (*prefix != '\0') { + char c1 = *str++; + char c2 = *prefix++; + + if (c1 >= 'a' && c1 <= 'z') { + c1 -= 32; + } + if (c2 >= 'a' && c2 <= 'z') { + c2 -= 32; + } + if (c1 != c2) { + return 0; + } + } + return 1; +} + +static int parse_u32_value(const char *value, uint32_t min_value, uint32_t max_value, uint32_t *parsed_value) { char *endptr; unsigned long parsed; - if (value == NULL || baudrate == NULL) { + if (value == NULL || parsed_value == NULL) { return -1; } @@ -80,54 +132,262 @@ static int parse_baudrate_value(const char *value, uint32_t *baudrate) if (endptr == value || *skip_whitespace(endptr) != '\0') { return -1; } - - if (parsed < 1200ul || parsed > 921600ul) { + if (parsed < min_value || parsed > max_value) { return -1; } - *baudrate = (uint32_t)parsed; + *parsed_value = (uint32_t)parsed; return 0; } -static at_result_t handle_query(char *response, uint16_t max_len) +static int parse_link_uart(const char *value, uint8_t *uart) +{ + if (equals_ignore_case(value, "U0")) { + *uart = LINK_UART_U0; + return 0; + } + if (equals_ignore_case(value, "U1")) { + *uart = LINK_UART_U1; + return 0; + } + return -1; +} + +static const char *link_uart_to_str(uint8_t uart) +{ + return (uart == LINK_UART_U1) ? "U1" : "U0"; +} + +static bool parse_command_with_value(const char *cmd, const char *name, const char **value) +{ + size_t name_len; + + if (cmd == NULL || name == NULL || value == NULL) { + return false; + } + + name_len = strlen(name); + if (!prefix_equals_ignore_case(cmd, name)) { + return false; + } + if (cmd[name_len] != '=') { + return false; + } + + *value = skip_whitespace(cmd + name_len + 1u); + return true; +} + +static char *config_next_token(char **cursor) +{ + char *start; + char *end; + + if (cursor == NULL || *cursor == NULL) { + return NULL; + } + + start = *cursor; + while (*start == ' ' || *start == '\t') { + ++start; + } + if (*start == '\0') { + *cursor = NULL; + return NULL; + } + + end = start; + while (*end != '\0' && *end != ',') { + ++end; + } + + if (*end == ',') { + *end = '\0'; + *cursor = end + 1; + } else { + *cursor = NULL; + } + + trim_trailing(start); + return start; +} + +static void set_link_defaults(void) +{ + static const uint8_t zero_ip[4] = {0u, 0u, 0u, 0u}; + static const uint8_t c1_ip[4] = {192u, 168u, 1u, 200u}; + static const uint8_t c2_ip[4] = {192u, 168u, 1u, 201u}; + + memset(g_config.links, 0, sizeof(g_config.links)); + + g_config.links[CONFIG_LINK_S1].enabled = 1u; + g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0; + g_config.links[CONFIG_LINK_S1].local_port = 8080u; + memcpy(g_config.links[CONFIG_LINK_S1].remote_ip, zero_ip, sizeof(zero_ip)); + + g_config.links[CONFIG_LINK_S2].enabled = 0u; + g_config.links[CONFIG_LINK_S2].uart = LINK_UART_U1; + g_config.links[CONFIG_LINK_S2].local_port = 8081u; + memcpy(g_config.links[CONFIG_LINK_S2].remote_ip, zero_ip, sizeof(zero_ip)); + + g_config.links[CONFIG_LINK_C1].enabled = 1u; + g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1; + g_config.links[CONFIG_LINK_C1].local_port = 9001u; + memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, c1_ip, sizeof(c1_ip)); + g_config.links[CONFIG_LINK_C1].remote_port = 9000u; + + g_config.links[CONFIG_LINK_C2].enabled = 0u; + g_config.links[CONFIG_LINK_C2].uart = LINK_UART_U0; + g_config.links[CONFIG_LINK_C2].local_port = 9002u; + memcpy(g_config.links[CONFIG_LINK_C2].remote_ip, c2_ip, sizeof(c2_ip)); + g_config.links[CONFIG_LINK_C2].remote_port = 9001u; +} + +static void migrate_legacy_config(const legacy_device_config_v2_t *legacy) +{ + config_set_defaults(); + + memcpy(g_config.net.mac, legacy->mac, sizeof(g_config.net.mac)); + memcpy(g_config.net.ip, legacy->ip, sizeof(g_config.net.ip)); + memcpy(g_config.net.mask, legacy->mask, sizeof(g_config.net.mask)); + memcpy(g_config.net.gw, legacy->gw, sizeof(g_config.net.gw)); + g_config.uart_baudrate[0] = legacy->uart2_baudrate; + g_config.uart_baudrate[1] = legacy->uart3_baudrate; + g_config.mux_mode = MUX_MODE_RAW; + + g_config.links[CONFIG_LINK_S1].enabled = (legacy->server_port != 0u) ? 1u : 0u; + g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0; + g_config.links[CONFIG_LINK_S1].local_port = legacy->server_port; + memset(g_config.links[CONFIG_LINK_S1].remote_ip, 0, sizeof(g_config.links[CONFIG_LINK_S1].remote_ip)); + g_config.links[CONFIG_LINK_S1].remote_port = 0u; + + g_config.links[CONFIG_LINK_S2].enabled = 0u; + + g_config.links[CONFIG_LINK_C1].enabled = (legacy->remote_port != 0u) ? 1u : 0u; + g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1; + g_config.links[CONFIG_LINK_C1].local_port = 8081u; + memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, legacy->remote_ip, sizeof(g_config.links[CONFIG_LINK_C1].remote_ip)); + g_config.links[CONFIG_LINK_C1].remote_port = legacy->remote_port; + + g_config.links[CONFIG_LINK_C2].enabled = 0u; + g_config.crc = config_calc_crc(&g_config); +} + +static bool try_load_legacy_config(void) +{ + legacy_device_config_v2_t legacy; + uint32_t expected_crc; + + if (flash_param_read(&legacy, sizeof(legacy)) != 0) { + return false; + } + + expected_crc = flash_param_crc32(&legacy, offsetof(legacy_device_config_v2_t, crc)); + if (legacy.magic != CONFIG_MAGIC || legacy.version != 0x0002u || legacy.crc != expected_crc) { + return false; + } + + migrate_legacy_config(&legacy); + return true; +} + +static at_result_t handle_summary_query(char *response, uint16_t max_len) { char ip_str[16]; char mask_str[16]; char gw_str[16]; - char rip_str[16]; char mac_str[18]; + char rip_str[CONFIG_LINK_COUNT][16]; - 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); + config_ip_to_str(g_config.net.ip, ip_str); + config_ip_to_str(g_config.net.mask, mask_str); + config_ip_to_str(g_config.net.gw, gw_str); + config_mac_to_str(g_config.net.mac, mac_str); + for (uint32_t i = 0; i < CONFIG_LINK_COUNT; ++i) { + config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]); + } snprintf(response, max_len, - "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); + "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n" + "+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+MUX:%u\r\n" + "+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20\r\n" + "+BAUD:U0=%lu,U1=%lu\r\n" + "OK\r\n", + ip_str, mask_str, gw_str, mac_str, + g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart), + g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart), + g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart), + g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart), + g_config.mux_mode, + g_config.uart_baudrate[0], + g_config.uart_baudrate[1]); return AT_OK; } +static at_result_t handle_net_query(char *response, uint16_t max_len) +{ + char ip_str[16]; + char mask_str[16]; + char gw_str[16]; + char mac_str[18]; + + config_ip_to_str(g_config.net.ip, ip_str); + config_ip_to_str(g_config.net.mask, mask_str); + config_ip_to_str(g_config.net.gw, gw_str); + config_mac_to_str(g_config.net.mac, mac_str); + snprintf(response, max_len, "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\nOK\r\n", ip_str, mask_str, gw_str, mac_str); + return AT_OK; +} + +static at_result_t handle_link_query(uint32_t index, char *response, uint16_t max_len) +{ + char rip_str[16]; + + if (index >= CONFIG_LINK_COUNT) { + snprintf(response, max_len, "ERROR: Invalid route field\r\n"); + return AT_INVALID_PARAM; + } + + config_ip_to_str(g_config.links[index].remote_ip, rip_str); + snprintf(response, + max_len, + "+LINK:%lu,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n", + index, + g_config.links[index].enabled, + g_config.links[index].local_port, + rip_str, + g_config.links[index].remote_port, + link_uart_to_str(g_config.links[index].uart)); + return AT_OK; +} + +static at_result_t handle_all_link_query(char *response, uint16_t max_len) +{ + char rip_str[CONFIG_LINK_COUNT][16]; + + for (uint32_t i = 0; i < CONFIG_LINK_COUNT; ++i) { + config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]); + } + + snprintf(response, + max_len, + "+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n" + "OK\r\n", + g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart), + g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart), + g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart), + g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart)); + return AT_OK; +} + int config_init(void) { flash_param_init(); @@ -136,15 +396,19 @@ int config_init(void) int config_load(void) { - 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)) { - config_set_defaults(); - return -1; + 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)) { + return 0; } - return 0; + if (try_load_legacy_config()) { + return 0; + } + + config_set_defaults(); + return -1; } int config_save(void) @@ -157,33 +421,22 @@ int config_save(void) void config_set_defaults(void) { - 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; + const uint8_t default_ip[] = DEFAULT_NET_IP; + const uint8_t default_mask[] = DEFAULT_NET_MASK; + const uint8_t default_gw[] = DEFAULT_NET_GW; + const uint8_t default_mac[] = DEFAULT_NET_MAC; memset(&g_config, 0, sizeof(g_config)); g_config.magic = CONFIG_MAGIC; g_config.version = CONFIG_VERSION; - 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; - g_config.server_port = DEFAULT_SERVER_PORT; - 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; - g_config.uart2_stopbits = DEFAULT_UART_STOPBITS; - g_config.uart2_parity = DEFAULT_UART_PARITY; - g_config.uart3_databits = DEFAULT_UART_DATABITS; - g_config.uart3_stopbits = DEFAULT_UART_STOPBITS; - g_config.uart3_parity = DEFAULT_UART_PARITY; + g_config.mux_mode = MUX_MODE_RAW; + memcpy(g_config.net.ip, default_ip, sizeof(g_config.net.ip)); + memcpy(g_config.net.mask, default_mask, sizeof(g_config.net.mask)); + memcpy(g_config.net.gw, default_gw, sizeof(g_config.net.gw)); + memcpy(g_config.net.mac, default_mac, sizeof(g_config.net.mac)); + set_link_defaults(); + g_config.uart_baudrate[0] = DEFAULT_UART_BAUDRATE; + g_config.uart_baudrate[1] = DEFAULT_UART_BAUDRATE; g_config.crc = config_calc_crc(&g_config); } @@ -199,50 +452,37 @@ device_config_t *config_get_mutable(void) 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; + const char *value; const char *p; if (cmd == NULL || response == NULL || max_len == 0u) { return AT_ERROR; } - strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1u); - cmd_copy[CONFIG_CMD_MAX_LEN - 1u] = '\0'; - trim_trailing(cmd_copy); - p = skip_whitespace(cmd_copy); + strncpy(g_cmd_work_buffer, cmd, sizeof(g_cmd_work_buffer) - 1u); + g_cmd_work_buffer[sizeof(g_cmd_work_buffer) - 1u] = '\0'; + trim_trailing(g_cmd_work_buffer); + p = skip_whitespace(g_cmd_work_buffer); 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; } - 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; - value = strchr((char *)p, '='); - if (value != NULL) { - *value = '\0'; - ++value; - value = (char *)skip_whitespace(value); - } - cmd_name = (char *)p; - trim_trailing(cmd_name); - - if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL) { - return handle_query(response, max_len); + if ((equals_ignore_case(p, "?") || equals_ignore_case(p, "QUERY"))) { + return handle_summary_query(response, max_len); } - if (equals_ignore_case(cmd_name, "SAVE") && value == NULL) { + if (equals_ignore_case(p, "SAVE")) { if (config_save() != 0) { snprintf(response, max_len, "ERROR: Save failed\r\n"); return AT_SAVE_FAILED; @@ -250,103 +490,139 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_ snprintf(response, max_len, "OK: Configuration saved\r\n"); return AT_OK; } - if (equals_ignore_case(cmd_name, "RESET") && value == NULL) { + if (equals_ignore_case(p, "RESET")) { g_reset_requested = true; snprintf(response, max_len, "OK: Resetting...\r\n"); return AT_OK; } - if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL) { + if (equals_ignore_case(p, "DEFAULT")) { config_set_defaults(); snprintf(response, max_len, "OK: Defaults restored\r\n"); return AT_OK; } - 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; + if (equals_ignore_case(p, "MUX?")) { + snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode); + return AT_OK; } - 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; - } - 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; - } - 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; - } - 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; - } - 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; - } - 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; - } - if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL) { - if (parse_baudrate_value(value, &g_config.uart2_baudrate) != 0) { - snprintf(response, max_len, "ERROR: Invalid baudrate\r\n"); - return AT_INVALID_PARAM; - } - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; - } - if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL) { - if (parse_baudrate_value(value, &g_config.uart3_baudrate) != 0) { - snprintf(response, max_len, "ERROR: Invalid baudrate\r\n"); - return AT_INVALID_PARAM; - } - snprintf(response, max_len, "OK\r\n"); - return AT_NEED_REBOOT; - } - if (equals_ignore_case(cmd_name, "DHCP") && value != NULL) { - int dhcp = atoi(value); - if (dhcp != 0 && dhcp != 1) { + if (parse_command_with_value(p, "MUX", &value)) { + uint32_t mux_value; + if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) { 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"); + g_config.mux_mode = (uint8_t)mux_value; + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; + } + if (equals_ignore_case(p, "NET?")) { + return handle_net_query(response, max_len); + } + if (parse_command_with_value(p, "NET", &value)) { + char value_copy[96]; + char *token; + char *cursor; + uint8_t ip[4]; + uint8_t mask[4]; + uint8_t gw[4]; + uint8_t mac[6]; + + strncpy(value_copy, value, sizeof(value_copy) - 1u); + value_copy[sizeof(value_copy) - 1u] = '\0'; + cursor = value_copy; + + token = config_next_token(&cursor); + if (token == NULL || config_str_to_ip(token, ip) != 0) { + snprintf(response, max_len, "ERROR: Invalid IP format\r\n"); return AT_INVALID_PARAM; } - g_config.dhcp_enable = (uint8_t)dhcp; + token = config_next_token(&cursor); + if (token == NULL || config_str_to_ip(token, mask) != 0) { + snprintf(response, max_len, "ERROR: Invalid mask format\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL || config_str_to_ip(token, gw) != 0) { + snprintf(response, max_len, "ERROR: Invalid gateway format\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL || config_str_to_mac(token, mac) != 0) { + snprintf(response, max_len, "ERROR: Invalid MAC format\r\n"); + return AT_INVALID_PARAM; + } + if (config_next_token(&cursor) != NULL) { + snprintf(response, max_len, "ERROR: Invalid value\r\n"); + return AT_INVALID_PARAM; + } + + memcpy(g_config.net.ip, ip, sizeof(ip)); + memcpy(g_config.net.mask, mask, sizeof(mask)); + memcpy(g_config.net.gw, gw, sizeof(gw)); + memcpy(g_config.net.mac, mac, sizeof(mac)); + snprintf(response, max_len, "OK\r\n"); + return AT_NEED_REBOOT; + } + if (equals_ignore_case(p, "LINK?")) { + return handle_all_link_query(response, max_len); + } + if (parse_command_with_value(p, "LINK", &value)) { + char value_copy[96]; + char *cursor; + char *token; + uint32_t index; + uint32_t enabled; + uint32_t local_port; + uint32_t remote_port; + uint8_t rip[4]; + uint8_t uart; + + strncpy(value_copy, value, sizeof(value_copy) - 1u); + value_copy[sizeof(value_copy) - 1u] = '\0'; + cursor = value_copy; + + token = config_next_token(&cursor); + if (token == NULL || parse_u32_value(token, 0u, CONFIG_LINK_COUNT - 1u, &index) != 0) { + snprintf(response, max_len, "ERROR: Invalid route field\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL) { + return handle_link_query(index, response, max_len); + } + if (parse_u32_value(token, 0u, 1u, &enabled) != 0) { + snprintf(response, max_len, "ERROR: Invalid value\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL || parse_u32_value(token, 1u, 65535u, &local_port) != 0) { + snprintf(response, max_len, "ERROR: Invalid port\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL || config_str_to_ip(token, rip) != 0) { + snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL || parse_u32_value(token, 0u, 65535u, &remote_port) != 0) { + snprintf(response, max_len, "ERROR: Invalid port\r\n"); + return AT_INVALID_PARAM; + } + token = config_next_token(&cursor); + if (token == NULL || parse_link_uart(token, &uart) != 0) { + snprintf(response, max_len, "ERROR: Invalid route field\r\n"); + return AT_INVALID_PARAM; + } + if (config_next_token(&cursor) != NULL) { + snprintf(response, max_len, "ERROR: Invalid value\r\n"); + return AT_INVALID_PARAM; + } + + g_config.links[index].enabled = (uint8_t)enabled; + g_config.links[index].local_port = (uint16_t)local_port; + memcpy(g_config.links[index].remote_ip, rip, sizeof(rip)); + g_config.links[index].remote_port = (uint16_t)remote_port; + g_config.links[index].uart = uart; snprintf(response, max_len, "OK\r\n"); return AT_NEED_REBOOT; } @@ -362,7 +638,10 @@ void config_ip_to_str(const uint8_t *ip, char *str) int config_str_to_ip(const char *str, uint8_t *ip) { - int a, b, c, d; + int a; + int b; + int c; + int d; if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) { return -1; @@ -398,7 +677,6 @@ int config_str_to_mac(const char *str, uint8_t *mac) } mac[i] = (uint8_t)a[i]; } - return 0; } @@ -406,7 +684,6 @@ void config_poll(void) { if (g_pending_cmd_ready) { uint16_t len = g_pending_cmd_len; - g_pending_cmd_ready = false; g_pending_cmd_len = 0u; (void)config_try_process_frame((const uint8_t *)g_pending_cmd_buffer, len); @@ -415,8 +692,13 @@ void config_poll(void) void config_uart_rx_byte(uint8_t byte) { - if (byte == '\r' || byte == '\n') { - if (g_uart_cmd_len > 0u) { + if (byte == '\r') { + g_uart_rx_seen_cr = true; + return; + } + + if (byte == '\n') { + if (g_uart_rx_seen_cr && g_uart_cmd_len > 0u) { if (!g_pending_cmd_ready) { memcpy(g_pending_cmd_buffer, g_uart_cmd_buffer, g_uart_cmd_len); g_pending_cmd_buffer[g_uart_cmd_len] = '\0'; @@ -425,43 +707,58 @@ void config_uart_rx_byte(uint8_t byte) } g_uart_cmd_len = 0u; } + g_uart_rx_seen_cr = false; return; } + if (g_uart_rx_seen_cr) { + g_uart_cmd_len = 0u; + g_uart_rx_seen_cr = false; + } + if (g_uart_cmd_len < (CONFIG_CMD_MAX_LEN - 1u)) { - g_uart_cmd_buffer[g_uart_cmd_len++] = (char)byte; + g_uart_cmd_buffer[g_uart_cmd_len++] = byte; g_uart_cmd_buffer[g_uart_cmd_len] = '\0'; } else { g_uart_cmd_len = 0u; } } +bool config_build_response_frame(const uint8_t *data, + uint16_t len, + char *response, + uint16_t max_len, + at_result_t *result) +{ + if (data == NULL || response == NULL || len < 2u || max_len == 0u) { + return false; + } + if (len >= CONFIG_CMD_MAX_LEN) { + return false; + } + + memcpy(g_cmd_parse_buffer, data, len); + g_cmd_parse_buffer[len] = '\0'; + if (((g_cmd_parse_buffer[0] != 'A') && (g_cmd_parse_buffer[0] != 'a')) || + ((g_cmd_parse_buffer[1] != 'T') && (g_cmd_parse_buffer[1] != 't'))) { + return false; + } + + *result = config_process_at_cmd(g_cmd_parse_buffer, response, max_len); + return true; +} + bool config_try_process_frame(const uint8_t *data, uint16_t len) { - char response[CONFIG_TX_BUFFER_SIZE]; - char cmd_buffer[CONFIG_CMD_MAX_LEN]; - at_result_t result; + at_result_t result = AT_ERROR; - if (data == NULL || len < 2u) { + if (!config_build_response_frame(data, len, g_at_response_buffer, sizeof(g_at_response_buffer), &result)) { return false; } - if (len >= CONFIG_CMD_MAX_LEN) { - len = CONFIG_CMD_MAX_LEN - 1u; - } - - memcpy(cmd_buffer, data, len); - cmd_buffer[len] = '\0'; - - if (((cmd_buffer[0] != 'A') && (cmd_buffer[0] != 'a')) || - ((cmd_buffer[1] != 'T') && (cmd_buffer[1] != 't'))) { - return false; - } - - result = config_process_at_cmd(cmd_buffer, response, sizeof(response)); if (HAL_UART_Transmit(&CONFIG_UART_HANDLE, - (uint8_t *)response, - (uint16_t)strlen(response), + (uint8_t *)g_at_response_buffer, + (uint16_t)strlen(g_at_response_buffer), 1000u) != HAL_OK) { return false; } @@ -488,3 +785,31 @@ void config_clear_reset_requested(void) { g_reset_requested = false; } + +uint8_t config_link_index_to_endpoint(uint8_t index) +{ + switch (index) { + case CONFIG_LINK_S1: + return ENDPOINT_S1; + case CONFIG_LINK_S2: + return ENDPOINT_S2; + case CONFIG_LINK_C1: + return ENDPOINT_C1; + case CONFIG_LINK_C2: + return ENDPOINT_C2; + default: + return 0u; + } +} + +uint8_t config_uart_index_to_endpoint(uint8_t uart_index) +{ + return (uart_index == LINK_UART_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2; +} + +bool config_endpoint_is_single(uint8_t endpoint) +{ + return endpoint == ENDPOINT_C1 || endpoint == ENDPOINT_C2 || + endpoint == ENDPOINT_UART2 || endpoint == ENDPOINT_UART3 || + endpoint == ENDPOINT_S1 || endpoint == ENDPOINT_S2; +} diff --git a/App/config.h b/App/config.h index 65403b6..aa3fc98 100644 --- a/App/config.h +++ b/App/config.h @@ -1,96 +1,78 @@ /** * @file config.h - * @brief AT command configuration module for TCP2UART - * - * Handles UART1 AT commands for network and serial port configuration. - * - * Supported AT commands: - * - AT+IP=192.168.1.100 Set device IP - * - AT+MASK=255.255.255.0 Set subnet mask - * - AT+GW=192.168.1.1 Set gateway - * - AT+PORT=8080 Set TCP Server listen port - * - AT+RIP=192.168.1.200 Set TCP Client remote IP - * - AT+RPORT=9000 Set TCP Client remote port - * - AT+BAUD1=115200 Set UART2 baudrate - * - AT+BAUD2=115200 Set UART3 baudrate - * - AT+MAC=00:11:22:33:44:55 Set MAC address - * - AT+DHCP=0/1 Enable/disable DHCP - * - AT+SAVE Save parameters to Flash - * - AT+RESET Reset device - * - AT+DEFAULT Restore factory defaults - * - AT+? Query current configuration + * @brief Final AT configuration model for TCP2UART. */ #ifndef __CONFIG_H__ #define __CONFIG_H__ -#include #include +#include #ifdef __cplusplus extern "C" { #endif -/* Configuration magic number "TCPU" */ -#define CONFIG_MAGIC 0x54435055 +#define CONFIG_MAGIC 0x54435055u +#define CONFIG_VERSION 0x0003u -/* Configuration version for compatibility */ -#define CONFIG_VERSION 0x0002 +#define CONFIG_UART_COUNT 2u +#define CONFIG_LINK_COUNT 4u + +#define CONFIG_LINK_S1 0u +#define CONFIG_LINK_S2 1u +#define CONFIG_LINK_C1 2u +#define CONFIG_LINK_C2 3u + +#define ENDPOINT_C1 0x01u +#define ENDPOINT_C2 0x02u +#define ENDPOINT_UART2 0x04u +#define ENDPOINT_UART3 0x08u +#define ENDPOINT_S1 0x10u +#define ENDPOINT_S2 0x20u + +#define LINK_UART_U0 0u +#define LINK_UART_U1 1u + +typedef enum { + MUX_MODE_RAW = 0, + MUX_MODE_FRAME = 1 +} mux_mode_t; -/* Device configuration structure */ typedef struct { - uint32_t magic; /* Magic number for validation */ - uint16_t version; /* Configuration version */ - uint16_t reserved; /* Reserved for alignment */ - - /* Network settings */ - uint8_t mac[6]; /* MAC address */ - uint8_t dhcp_enable; /* DHCP enable flag */ - uint8_t reserved2; /* Reserved for alignment */ - uint8_t ip[4]; /* Device IP address */ - uint8_t mask[4]; /* Subnet mask */ - uint8_t gw[4]; /* Gateway */ - - /* TCP Server settings */ - uint16_t server_port; /* Server listen port */ - uint16_t reserved3; /* Reserved for alignment */ - - /* TCP Client settings */ - uint8_t remote_ip[4]; /* Remote server IP */ - uint16_t remote_port; /* Remote server port */ - uint16_t reconnect_interval;/* Reconnect interval (ms) */ - - /* UART settings */ - uint32_t uart2_baudrate; /* UART2 (Server) baudrate */ - uint32_t uart3_baudrate; /* UART3 (Client) baudrate */ - uint8_t uart2_databits; /* UART2 data bits */ - uint8_t uart2_stopbits; /* UART2 stop bits */ - uint8_t uart2_parity; /* UART2 parity */ - uint8_t uart3_databits; /* UART3 data bits */ - uint8_t uart3_stopbits; /* UART3 stop bits */ - uint8_t uart3_parity; /* UART3 parity */ - uint16_t reserved4; /* Reserved for alignment */ - - /* CRC32 checksum (must be last) */ + uint8_t ip[4]; + uint8_t mask[4]; + uint8_t gw[4]; + uint8_t mac[6]; + uint8_t reserved[2]; +} net_config_t; + +typedef struct { + uint8_t enabled; + uint8_t uart; + uint16_t local_port; + uint8_t remote_ip[4]; + uint16_t remote_port; + uint16_t reserved; +} link_config_t; + +typedef struct { + uint32_t magic; + uint16_t version; + uint8_t mux_mode; + uint8_t reserved0; + net_config_t net; + link_config_t links[CONFIG_LINK_COUNT]; + uint32_t uart_baudrate[CONFIG_UART_COUNT]; uint32_t crc; } device_config_t; -/* Default configuration values */ -#define DEFAULT_IP {192, 168, 31, 100} -#define DEFAULT_MASK {255, 255, 255, 0} -#define DEFAULT_GW {192, 168, 31, 1} -#define DEFAULT_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01} -#define DEFAULT_SERVER_PORT 8080 -#define DEFAULT_REMOTE_IP {192, 168, 31, 1} -#define DEFAULT_REMOTE_PORT 8081 -#define DEFAULT_UART_BAUDRATE 115200 -#define DEFAULT_UART_DATABITS 8 -#define DEFAULT_UART_STOPBITS 1 -#define DEFAULT_UART_PARITY 0 -#define DEFAULT_DHCP_ENABLE 0 -#define DEFAULT_RECONNECT_MS 3000 +#define DEFAULT_NET_IP {192, 168, 1, 100} +#define DEFAULT_NET_MASK {255, 255, 255, 0} +#define DEFAULT_NET_GW {192, 168, 1, 1} +#define DEFAULT_NET_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01} +#define DEFAULT_UART_BAUDRATE 115200u -/* AT command result codes */ typedef enum { AT_OK = 0, AT_ERROR, @@ -100,108 +82,30 @@ typedef enum { AT_NEED_REBOOT } at_result_t; -/** - * @brief Initialize configuration module - * @return 0 on success, negative on error - */ int config_init(void); - -/** - * @brief Load configuration from Flash - * @return 0 on success, negative on error (defaults loaded) - */ int config_load(void); - -/** - * @brief Save configuration to Flash - * @return 0 on success, negative on error - */ int config_save(void); - -/** - * @brief Reset configuration to factory defaults - */ void config_set_defaults(void); - -/** - * @brief Get current configuration - * @return Pointer to current configuration (read-only) - */ const device_config_t *config_get(void); - -/** - * @brief Get mutable configuration for modification - * @return Pointer to configuration structure - */ device_config_t *config_get_mutable(void); - -/** - * @brief Process AT command received from UART1 - * @param cmd Command string (null-terminated) - * @param response Response buffer - * @param max_len Maximum response length - * @return AT command result code - */ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len); - -/** - * @brief Poll configuration UART and process pending AT commands - */ void config_poll(void); - -/** - * @brief Feed one byte received from the config UART. - * @param byte Received byte. - */ void config_uart_rx_byte(uint8_t byte); - -/** - * @brief Try to process one AT command frame from an external UART source. - * @param data Input bytes. - * @param len Input length. - * @return true if the frame was recognized as an AT/config command. - */ bool config_try_process_frame(const uint8_t *data, uint16_t len); - -/** - * @brief Check whether AT+RESET requested a system reset - */ +bool config_build_response_frame(const uint8_t *data, + uint16_t len, + char *response, + uint16_t max_len, + at_result_t *result); 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 - * @param str Output string buffer (min 16 bytes) - */ void config_ip_to_str(const uint8_t *ip, char *str); - -/** - * @brief Parse IP address from string - * @param str IP address string (e.g. "192.168.1.100") - * @param ip Output IP address bytes - * @return 0 on success, negative on error - */ int config_str_to_ip(const char *str, uint8_t *ip); - -/** - * @brief Format MAC address to string - * @param mac MAC address bytes - * @param str Output string buffer (min 18 bytes) - */ void config_mac_to_str(const uint8_t *mac, char *str); - -/** - * @brief Parse MAC address from string - * @param str MAC address string (e.g. "00:11:22:33:44:55") - * @param mac Output MAC address bytes - * @return 0 on success, negative on error - */ int config_str_to_mac(const char *str, uint8_t *mac); +uint8_t config_link_index_to_endpoint(uint8_t index); +uint8_t config_uart_index_to_endpoint(uint8_t uart_index); +bool config_endpoint_is_single(uint8_t endpoint); #ifdef __cplusplus } diff --git a/App/flash_param.c b/App/flash_param.c index 019124e..41e919f 100644 --- a/App/flash_param.c +++ b/App/flash_param.c @@ -19,41 +19,10 @@ * Private Variables *---------------------------------------------------------------------------*/ -/* CRC32 lookup table */ -static uint32_t g_crc_table[256]; -static bool g_crc_table_initialized = false; - /*--------------------------------------------------------------------------- * Private Functions *---------------------------------------------------------------------------*/ -/** - * @brief Initialize CRC32 lookup table - */ -static void crc32_init_table(void) -{ - uint32_t i, j, crc; - - for (i = 0; i < 256; i++) - { - crc = i; - for (j = 0; j < 8; j++) - { - if (crc & 1) - { - crc = (crc >> 1) ^ CRC32_POLYNOMIAL; - } - else - { - crc >>= 1; - } - } - g_crc_table[i] = crc; - } - - g_crc_table_initialized = true; -} - /** * @brief Unlock Flash for writing */ @@ -105,12 +74,6 @@ static HAL_StatusTypeDef flash_program_halfword(uint32_t addr, uint16_t data) */ int flash_param_init(void) { - /* Initialize CRC table */ - if (!g_crc_table_initialized) - { - crc32_init_table(); - } - return 0; } @@ -243,16 +206,22 @@ uint32_t flash_param_crc32(const void *data, uint32_t len) const uint8_t *p = (const uint8_t *)data; uint32_t crc = 0xFFFFFFFF; uint32_t i; - - /* Initialize table if needed */ - if (!g_crc_table_initialized) - { - crc32_init_table(); - } - + uint32_t j; + for (i = 0; i < len; i++) { - crc = g_crc_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8); + crc ^= p[i]; + for (j = 0; j < 8u; ++j) + { + if ((crc & 1u) != 0u) + { + crc = (crc >> 1) ^ CRC32_POLYNOMIAL; + } + else + { + crc >>= 1; + } + } } return crc ^ 0xFFFFFFFF; diff --git a/App/tcp_client.c b/App/tcp_client.c index 87bc6c5..e9235ec 100644 --- a/App/tcp_client.c +++ b/App/tcp_client.c @@ -1,17 +1,14 @@ /** * @file tcp_client.c - * @brief lwIP RAW TCP client for the UART3 bridge. + * @brief Indexed lwIP RAW TCP client manager. */ #include "tcp_client.h" -#include "main.h" - -#include "SEGGER_RTT.h" - -#include "lwip/ip_addr.h" -#include "lwip/pbuf.h" -#include "lwip/tcp.h" +#include "../Core/Inc/main.h" +#include "../Drivers/LwIP/src/include/lwip/ip_addr.h" +#include "../Drivers/LwIP/src/include/lwip/pbuf.h" +#include "../Drivers/LwIP/src/include/lwip/tcp.h" #include @@ -21,11 +18,12 @@ typedef struct { uint16_t rx_head; uint16_t rx_tail; uint32_t next_retry_ms; - tcp_client_config_t config; + uint8_t index; + tcp_client_instance_config_t config; tcp_client_status_t status; } tcp_client_ctx_t; -static tcp_client_ctx_t g_client; +static tcp_client_ctx_t g_clients[TCP_CLIENT_INSTANCE_COUNT]; static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size) { @@ -37,13 +35,18 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg; struct pbuf *q; + if (ctx == NULL) { + if (p != NULL) { + pbuf_free(p); + } + return ERR_ARG; + } if (err != ERR_OK) { if (p != NULL) { pbuf_free(p); } return err; } - if (p == NULL) { tcp_arg(pcb, NULL); tcp_recv(pcb, NULL); @@ -80,7 +83,9 @@ static err_t tcp_client_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg; (void)pcb; - ctx->status.tx_bytes += len; + if (ctx != NULL) { + ctx->status.tx_bytes += len; + } return ERR_OK; } @@ -90,34 +95,30 @@ static void tcp_client_on_err(void *arg, err_t err) if (ctx == NULL) { return; } - ctx->pcb = NULL; ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; ctx->status.errors++; ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; - SEGGER_RTT_printf(0, "TCP client error=%d, reconnect scheduled\r\n", (int)err); + (void)err; } 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 (ctx == NULL) { + return ERR_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; - SEGGER_RTT_printf(0, "TCP client connect callback failed err=%d\r\n", (int)err); return err; } ctx->pcb = pcb; ctx->status.state = TCP_CLIENT_STATE_CONNECTED; - SEGGER_RTT_printf(0, - "TCP client connected to %u.%u.%u.%u:%u\r\n", - ctx->config.server_ip[0], ctx->config.server_ip[1], - ctx->config.server_ip[2], ctx->config.server_ip[3], - ctx->config.server_port); tcp_nagle_disable(pcb); tcp_arg(pcb, ctx); tcp_recv(pcb, tcp_client_on_recv); @@ -126,207 +127,181 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err) return ERR_OK; } -int tcp_client_init(const tcp_client_config_t *config) +int tcp_client_init_all(void) { - 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] = 31u; - g_client.config.server_ip[3] = 1u; - g_client.config.local_port = TCP_CLIENT_DEFAULT_PORT; - 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; + memset(g_clients, 0, sizeof(g_clients)); + for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) { + g_clients[i].index = i; + g_clients[i].status.state = TCP_CLIENT_STATE_IDLE; + g_clients[i].config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS; + g_clients[i].config.auto_reconnect = true; } - return 0; } -int tcp_client_connect(void) +int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config) +{ + if (instance >= TCP_CLIENT_INSTANCE_COUNT || config == NULL) { + return -1; + } + g_clients[instance].config = *config; + return 0; +} + +int tcp_client_connect(uint8_t instance) { struct tcp_pcb *pcb; ip_addr_t remote_addr; err_t err; + tcp_client_ctx_t *ctx; - if (g_client.pcb != NULL) { + if (instance >= TCP_CLIENT_INSTANCE_COUNT) { + return -1; + } + ctx = &g_clients[instance]; + if (!ctx->config.enabled) { + return 0; + } + if (ctx->pcb != NULL) { return 0; } pcb = tcp_new_ip_type(IPADDR_TYPE_V4); if (pcb == NULL) { - g_client.status.errors++; - g_client.status.state = TCP_CLIENT_STATE_ERROR; - SEGGER_RTT_WriteString(0, "TCP client connect failed: no PCB\r\n"); + ctx->status.errors++; + ctx->status.state = TCP_CLIENT_STATE_ERROR; return -1; } - - if (g_client.config.local_port != 0u) { - err = tcp_bind(pcb, IP_ANY_TYPE, g_client.config.local_port); + if (ctx->config.local_port != 0u) { + err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.local_port); 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; - SEGGER_RTT_printf(0, "TCP client bind failed err=%d\r\n", (int)err); + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->status.errors++; + ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; 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]); + ctx->config.remote_ip[0], + ctx->config.remote_ip[1], + ctx->config.remote_ip[2], + ctx->config.remote_ip[3]); - g_client.status.state = TCP_CLIENT_STATE_CONNECTING; - tcp_arg(pcb, &g_client); + ctx->status.state = TCP_CLIENT_STATE_CONNECTING; + tcp_arg(pcb, ctx); tcp_err(pcb, tcp_client_on_err); - err = tcp_connect(pcb, &remote_addr, g_client.config.server_port, tcp_client_on_connected); + err = tcp_connect(pcb, &remote_addr, ctx->config.remote_port, tcp_client_on_connected); if (err != ERR_OK) { tcp_err(pcb, NULL); 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; - SEGGER_RTT_printf(0, "TCP client connect start failed err=%d\r\n", (int)err); + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->status.errors++; + ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms; return -1; } - g_client.pcb = pcb; - SEGGER_RTT_printf(0, - "TCP client connecting to %u.%u.%u.%u:%u\r\n", - 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.config.server_port); + ctx->pcb = pcb; return 0; } -int tcp_client_disconnect(void) +int tcp_client_disconnect(uint8_t instance) { - 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; - } + tcp_client_ctx_t *ctx; - g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED; - SEGGER_RTT_WriteString(0, "TCP client disconnected\r\n"); + if (instance >= TCP_CLIENT_INSTANCE_COUNT) { + return -1; + } + ctx = &g_clients[instance]; + if (ctx->pcb != NULL) { + tcp_arg(ctx->pcb, NULL); + tcp_recv(ctx->pcb, NULL); + tcp_sent(ctx->pcb, NULL); + tcp_err(ctx->pcb, NULL); + tcp_abort(ctx->pcb); + ctx->pcb = NULL; + } + ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED; + ctx->rx_head = 0u; + ctx->rx_tail = 0u; return 0; } -int tcp_client_send(const uint8_t *data, uint16_t len) +int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len) { err_t err; + tcp_client_ctx_t *ctx; - if (g_client.pcb == NULL || data == NULL || len == 0u) { + if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || len == 0u) { return -1; } - - if ((g_client.pcb->flags & TF_RXCLOSED) != 0u) { - g_client.status.errors++; + ctx = &g_clients[instance]; + if (ctx->pcb == NULL) { return -1; } - - if (tcp_sndbuf(g_client.pcb) < len) { + if (tcp_sndbuf(ctx->pcb) < len) { return 0; } - - err = tcp_write(g_client.pcb, data, len, TCP_WRITE_FLAG_COPY); + err = tcp_write(ctx->pcb, data, len, TCP_WRITE_FLAG_COPY); if (err != ERR_OK) { - g_client.status.errors++; + ctx->status.errors++; return -1; } - - err = tcp_output(g_client.pcb); + err = tcp_output(ctx->pcb); if (err != ERR_OK) { - g_client.status.errors++; + ctx->status.errors++; return -1; } - return (int)len; } -int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms) +int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len) { uint16_t copied = 0u; - (void)timeout_ms; + tcp_client_ctx_t *ctx; - if (data == NULL || max_len == 0u) { + if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || max_len == 0u) { return -1; } - - 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); + ctx = &g_clients[instance]; + 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) % TCP_CLIENT_RX_BUFFER_SIZE); } - return (int)copied; } -bool tcp_client_is_connected(void) +bool tcp_client_is_connected(uint8_t instance) { - return (g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTED); + return (instance < TCP_CLIENT_INSTANCE_COUNT) && + (g_clients[instance].pcb != NULL) && + (g_clients[instance].status.state == TCP_CLIENT_STATE_CONNECTED); } -int tcp_client_set_server(const uint8_t *ip, uint16_t port) +void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status) { - if (ip == NULL || port == 0u) { - return -1; + if (instance < TCP_CLIENT_INSTANCE_COUNT && status != NULL) { + *status = g_clients[instance].status; } - - memcpy(g_client.config.server_ip, ip, 4u); - g_client.config.server_port = port; - return 0; -} - -void tcp_client_get_status(tcp_client_status_t *status) -{ - if (status != NULL) { - *status = g_client.status; - } -} - -void *tcp_client_get_rx_stream(void) -{ - return NULL; -} - -void *tcp_client_get_tx_stream(void) -{ - return NULL; -} - -void tcp_client_task(void *argument) -{ - (void)argument; } void tcp_client_poll(void) { - uint32_t now; + uint32_t now = HAL_GetTick(); - if (!g_client.config.auto_reconnect || tcp_client_is_connected()) { - return; - } - - if ((g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTING)) { - return; - } - - 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; - SEGGER_RTT_printf(0, - "TCP client reconnect attempt %lu\r\n", - g_client.status.reconnect_count); - (void)tcp_client_connect(); + for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) { + tcp_client_ctx_t *ctx = &g_clients[i]; + if (!ctx->config.enabled || !ctx->config.auto_reconnect || tcp_client_is_connected(i)) { + continue; + } + if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) { + continue; + } + if (now >= ctx->next_retry_ms) { + ctx->status.reconnect_count++; + ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms; + (void)tcp_client_connect(i); + } } } diff --git a/App/tcp_client.h b/App/tcp_client.h index f8f5e38..ecf4532 100644 --- a/App/tcp_client.h +++ b/App/tcp_client.h @@ -1,49 +1,39 @@ /** * @file tcp_client.h - * @brief TCP Client module for transparent transmission with UART3 + * @brief Indexed lwIP RAW TCP client manager. */ #ifndef __TCP_CLIENT_H__ #define __TCP_CLIENT_H__ -#include #include +#include #ifdef __cplusplus extern "C" { #endif -/* Default TCP Client settings */ -#define TCP_CLIENT_DEFAULT_PORT 8081 -#define TCP_CLIENT_DEFAULT_SERVER "192.168.1.100" +#define TCP_CLIENT_INSTANCE_COUNT 2u +#define TCP_CLIENT_RX_BUFFER_SIZE 512u +#define TCP_CLIENT_RECONNECT_DELAY_MS 3000u -/* Reconnect settings */ -#define TCP_CLIENT_RECONNECT_DELAY_MS 3000 -#define TCP_CLIENT_MAX_RECONNECT_TRIES 0 /* 0 = infinite */ - -/* Buffer sizes */ -#define TCP_CLIENT_RX_BUFFER_SIZE 512 -#define TCP_CLIENT_TX_BUFFER_SIZE 512 - -/* TCP Client state */ typedef enum { - TCP_CLIENT_STATE_IDLE, + TCP_CLIENT_STATE_IDLE = 0, TCP_CLIENT_STATE_CONNECTING, TCP_CLIENT_STATE_CONNECTED, TCP_CLIENT_STATE_DISCONNECTED, TCP_CLIENT_STATE_ERROR } tcp_client_state_t; -/* TCP Client configuration */ typedef struct { - uint8_t server_ip[4]; /* Server IP address */ - uint16_t local_port; /* Local source port */ - uint16_t server_port; /* Server port */ - bool auto_reconnect; /* Auto reconnect on disconnect */ - uint16_t reconnect_interval_ms; /* Reconnect interval */ -} tcp_client_config_t; + uint8_t remote_ip[4]; + uint16_t local_port; + uint16_t remote_port; + uint16_t reconnect_interval_ms; + bool enabled; + bool auto_reconnect; +} tcp_client_instance_config_t; -/* TCP Client status */ typedef struct { tcp_client_state_t state; uint32_t rx_bytes; @@ -52,80 +42,14 @@ typedef struct { uint32_t errors; } tcp_client_status_t; -/** - * @brief Initialize TCP Client module - * @param config Client configuration - * @return 0 on success, negative on error - */ -int tcp_client_init(const tcp_client_config_t *config); - -/** - * @brief Connect to remote server - * @return 0 on success, negative on error - */ -int tcp_client_connect(void); - -/** - * @brief Disconnect from server - * @return 0 on success, negative on error - */ -int tcp_client_disconnect(void); - -/** - * @brief Send data to server - * @param data Data buffer - * @param len Data length - * @return Number of bytes sent, negative on error - */ -int tcp_client_send(const uint8_t *data, uint16_t len); - -/** - * @brief Receive data from server - * @param data Data buffer - * @param max_len Maximum length to receive - * @param timeout_ms Timeout in milliseconds (0 = non-blocking) - * @return Number of bytes received, 0 if no data, negative on error - */ -int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms); - -/** - * @brief Check if connected to server - * @return true if connected - */ -bool tcp_client_is_connected(void); - -/** - * @brief Update server configuration (for AT command) - * @param ip Server IP address (4 bytes) - * @param port Server port - * @return 0 on success, negative on error - */ -int tcp_client_set_server(const uint8_t *ip, uint16_t port); - -/** - * @brief Get TCP Client status - * @param status Pointer to status structure - */ -void tcp_client_get_status(tcp_client_status_t *status); - -/** - * @brief Get TCP Client RX StreamBuffer handle for UART integration - * @return StreamBuffer handle for receiving data from TCP - */ -void *tcp_client_get_rx_stream(void); - -/** - * @brief Get TCP Client TX StreamBuffer handle for UART integration - * @return StreamBuffer handle for sending data to TCP - */ -void *tcp_client_get_tx_stream(void); - -/** - * @brief TCP Client task function (for FreeRTOS) - * @param argument Task argument (unused) - */ -void tcp_client_task(void *argument); - +int tcp_client_init_all(void); +int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config); +int tcp_client_connect(uint8_t instance); +int tcp_client_disconnect(uint8_t instance); +int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len); +int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len); +bool tcp_client_is_connected(uint8_t instance); +void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status); void tcp_client_poll(void); #ifdef __cplusplus diff --git a/App/tcp_server.c b/App/tcp_server.c index 8499a04..d4367e8 100644 --- a/App/tcp_server.c +++ b/App/tcp_server.c @@ -1,12 +1,12 @@ /** * @file tcp_server.c - * @brief lwIP RAW TCP server for the UART2 bridge. + * @brief Indexed lwIP RAW TCP server manager. */ #include "tcp_server.h" -#include "lwip/pbuf.h" -#include "lwip/tcp.h" +#include "../Drivers/LwIP/src/include/lwip/pbuf.h" +#include "../Drivers/LwIP/src/include/lwip/tcp.h" #include "SEGGER_RTT.h" @@ -18,11 +18,12 @@ typedef struct { uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE]; uint16_t rx_head; uint16_t rx_tail; - tcp_server_config_t config; + uint8_t index; + tcp_server_instance_config_t config; tcp_server_status_t status; } tcp_server_ctx_t; -static tcp_server_ctx_t g_server; +static tcp_server_ctx_t g_servers[TCP_SERVER_INSTANCE_COUNT]; static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size) { @@ -34,13 +35,18 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; struct pbuf *q; + if (ctx == NULL) { + if (p != NULL) { + pbuf_free(p); + } + return ERR_ARG; + } if (err != ERR_OK) { if (p != NULL) { pbuf_free(p); } return err; } - if (p == NULL) { tcp_arg(pcb, NULL); tcp_recv(pcb, NULL); @@ -50,8 +56,7 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, tcp_abort(pcb); } ctx->client_pcb = NULL; - ctx->status.state = TCP_SERVER_STATE_LISTENING; - SEGGER_RTT_WriteString(0, "TCP server peer disconnected\r\n"); + ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE; return ERR_OK; } @@ -77,28 +82,31 @@ 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; + if (ctx != NULL) { + 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; + if (ctx == NULL) { + return; + } ctx->client_pcb = NULL; - ctx->status.state = TCP_SERVER_STATE_LISTENING; + ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE; ctx->status.errors++; - SEGGER_RTT_printf(0, "TCP server connection error=%d\r\n", (int)err); + SEGGER_RTT_printf(0, "TCP server[%u] connection error=%d\r\n", ctx->index, (int)err); } 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 == NULL || err != ERR_OK) { + return (ctx == NULL) ? ERR_ARG : err; } - if (ctx->client_pcb != NULL) { tcp_abort(newpcb); return ERR_ABRT; @@ -107,10 +115,7 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err) ctx->client_pcb = newpcb; ctx->status.state = TCP_SERVER_STATE_CONNECTED; ctx->status.connections++; - SEGGER_RTT_WriteString(0, "TCP server client connected\r\n"); - tcp_nagle_disable(newpcb); - tcp_arg(newpcb, ctx); tcp_recv(newpcb, tcp_server_on_recv); tcp_sent(newpcb, tcp_server_on_sent); @@ -118,149 +123,151 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err) return ERR_OK; } -int tcp_server_init(const tcp_server_config_t *config) +int tcp_server_init_all(void) { - 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; + memset(g_servers, 0, sizeof(g_servers)); + for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) { + g_servers[i].index = i; + g_servers[i].status.state = TCP_SERVER_STATE_IDLE; } - return 0; } -int tcp_server_start(void) +int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config) +{ + if (instance >= TCP_SERVER_INSTANCE_COUNT || config == NULL) { + return -1; + } + g_servers[instance].config = *config; + return 0; +} + +int tcp_server_start(uint8_t instance) { struct tcp_pcb *pcb; err_t err; + tcp_server_ctx_t *ctx; - if (g_server.listen_pcb != NULL) { + if (instance >= TCP_SERVER_INSTANCE_COUNT) { + return -1; + } + ctx = &g_servers[instance]; + if (!ctx->config.enabled) { + ctx->status.state = TCP_SERVER_STATE_IDLE; + return 0; + } + if (ctx->listen_pcb != NULL) { return 0; } pcb = tcp_new_ip_type(IPADDR_TYPE_V4); if (pcb == NULL) { - g_server.status.errors++; + ctx->status.errors++; return -1; } - err = tcp_bind(pcb, IP_ANY_TYPE, g_server.config.port); + err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.port); if (err != ERR_OK) { tcp_abort(pcb); - g_server.status.errors++; + ctx->status.errors++; return -1; } - g_server.listen_pcb = tcp_listen_with_backlog(pcb, 1); - if (g_server.listen_pcb == NULL) { - g_server.status.errors++; + ctx->listen_pcb = tcp_listen_with_backlog(pcb, 1); + if (ctx->listen_pcb == NULL) { + ctx->status.errors++; return -1; } - 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; - SEGGER_RTT_printf(0, "TCP server listening on %u\r\n", g_server.config.port); + tcp_arg(ctx->listen_pcb, ctx); + tcp_accept(ctx->listen_pcb, tcp_server_on_accept); + ctx->status.state = TCP_SERVER_STATE_LISTENING; return 0; } -int tcp_server_stop(void) +int tcp_server_stop(uint8_t instance) { - 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; + tcp_server_ctx_t *ctx; + + if (instance >= TCP_SERVER_INSTANCE_COUNT) { + return -1; + } + ctx = &g_servers[instance]; + + if (ctx->client_pcb != NULL) { + tcp_arg(ctx->client_pcb, NULL); + tcp_recv(ctx->client_pcb, NULL); + tcp_sent(ctx->client_pcb, NULL); + tcp_err(ctx->client_pcb, NULL); + tcp_abort(ctx->client_pcb); + ctx->client_pcb = NULL; + } + if (ctx->listen_pcb != NULL) { + tcp_arg(ctx->listen_pcb, NULL); + tcp_accept(ctx->listen_pcb, NULL); + tcp_close(ctx->listen_pcb); + ctx->listen_pcb = NULL; } - 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; - } - - g_server.status.state = TCP_SERVER_STATE_IDLE; + ctx->status.state = TCP_SERVER_STATE_IDLE; + ctx->rx_head = 0u; + ctx->rx_tail = 0u; return 0; } -int tcp_server_send(const uint8_t *data, uint16_t len) +int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len) { err_t err; + tcp_server_ctx_t *ctx; - if (g_server.client_pcb == NULL || data == NULL || len == 0u) { + if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || len == 0u) { return -1; } - - if ((g_server.client_pcb->flags & TF_RXCLOSED) != 0u) { - g_server.status.errors++; + ctx = &g_servers[instance]; + if (ctx->client_pcb == NULL) { return -1; } - - if (tcp_sndbuf(g_server.client_pcb) < len) { + if (tcp_sndbuf(ctx->client_pcb) < len) { return 0; } - err = tcp_write(g_server.client_pcb, data, len, TCP_WRITE_FLAG_COPY); + err = tcp_write(ctx->client_pcb, data, len, TCP_WRITE_FLAG_COPY); if (err != ERR_OK) { - g_server.status.errors++; + ctx->status.errors++; return -1; } - - err = tcp_output(g_server.client_pcb); + err = tcp_output(ctx->client_pcb); if (err != ERR_OK) { - g_server.status.errors++; + ctx->status.errors++; return -1; } - return (int)len; } -int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms) +int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len) { uint16_t copied = 0u; - (void)timeout_ms; + tcp_server_ctx_t *ctx; - if (data == NULL || max_len == 0u) { + if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || max_len == 0u) { return -1; } - - 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); + ctx = &g_servers[instance]; + 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) % TCP_SERVER_RX_BUFFER_SIZE); } - return (int)copied; } -bool tcp_server_is_connected(void) +bool tcp_server_is_connected(uint8_t instance) { - return g_server.client_pcb != NULL; + return (instance < TCP_SERVER_INSTANCE_COUNT) && (g_servers[instance].client_pcb != NULL); } -void tcp_server_get_status(tcp_server_status_t *status) +void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status) { - if (status != NULL) { - *status = g_server.status; + if (instance < TCP_SERVER_INSTANCE_COUNT && status != NULL) { + *status = g_servers[instance].status; } } - -void *tcp_server_get_rx_stream(void) -{ - return NULL; -} - -void *tcp_server_get_tx_stream(void) -{ - return NULL; -} - -void tcp_server_task(void *argument) -{ - (void)argument; -} diff --git a/App/tcp_server.h b/App/tcp_server.h index 2d99ce2..e5f5d64 100644 --- a/App/tcp_server.h +++ b/App/tcp_server.h @@ -1,43 +1,33 @@ /** * @file tcp_server.h - * @brief TCP Server module for transparent transmission with UART2 + * @brief Indexed lwIP RAW TCP server manager. */ #ifndef __TCP_SERVER_H__ #define __TCP_SERVER_H__ -#include #include +#include #ifdef __cplusplus extern "C" { #endif -/* Default TCP Server port */ -#define TCP_SERVER_DEFAULT_PORT 8080 +#define TCP_SERVER_INSTANCE_COUNT 2u +#define TCP_SERVER_RX_BUFFER_SIZE 512u -/* Maximum number of simultaneous connections */ -#define TCP_SERVER_MAX_CONNECTIONS 1 - -/* Buffer sizes */ -#define TCP_SERVER_RX_BUFFER_SIZE 512 -#define TCP_SERVER_TX_BUFFER_SIZE 512 - -/* TCP Server state */ typedef enum { - TCP_SERVER_STATE_IDLE, + TCP_SERVER_STATE_IDLE = 0, TCP_SERVER_STATE_LISTENING, TCP_SERVER_STATE_CONNECTED, TCP_SERVER_STATE_ERROR } tcp_server_state_t; -/* TCP Server configuration */ typedef struct { uint16_t port; - bool auto_reconnect; -} tcp_server_config_t; + bool enabled; +} tcp_server_instance_config_t; -/* TCP Server status */ typedef struct { tcp_server_state_t state; uint32_t rx_bytes; @@ -46,71 +36,14 @@ typedef struct { uint32_t errors; } tcp_server_status_t; -/** - * @brief Initialize TCP Server module - * @param config Server configuration - * @return 0 on success, negative on error - */ -int tcp_server_init(const tcp_server_config_t *config); - -/** - * @brief Start TCP Server (begin listening) - * @return 0 on success, negative on error - */ -int tcp_server_start(void); - -/** - * @brief Stop TCP Server - * @return 0 on success, negative on error - */ -int tcp_server_stop(void); - -/** - * @brief Send data to connected client - * @param data Data buffer - * @param len Data length - * @return Number of bytes sent, negative on error - */ -int tcp_server_send(const uint8_t *data, uint16_t len); - -/** - * @brief Receive data from connected client - * @param data Data buffer - * @param max_len Maximum length to receive - * @param timeout_ms Timeout in milliseconds (0 = non-blocking) - * @return Number of bytes received, 0 if no data, negative on error - */ -int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms); - -/** - * @brief Check if client is connected - * @return true if connected - */ -bool tcp_server_is_connected(void); - -/** - * @brief Get TCP Server status - * @param status Pointer to status structure - */ -void tcp_server_get_status(tcp_server_status_t *status); - -/** - * @brief Get TCP Server StreamBuffer handle for UART integration - * @return StreamBuffer handle for receiving data from TCP - */ -void *tcp_server_get_rx_stream(void); - -/** - * @brief Get TCP Server TX StreamBuffer handle for UART integration - * @return StreamBuffer handle for sending data to TCP - */ -void *tcp_server_get_tx_stream(void); - -/** - * @brief TCP Server task function (for FreeRTOS) - * @param argument Task argument (unused) - */ -void tcp_server_task(void *argument); +int tcp_server_init_all(void); +int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config); +int tcp_server_start(uint8_t instance); +int tcp_server_stop(uint8_t instance); +int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len); +int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len); +bool tcp_server_is_connected(uint8_t instance); +void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status); #ifdef __cplusplus } diff --git a/App/uart_trans.c b/App/uart_trans.c index 99b8075..42cfbbc 100644 --- a/App/uart_trans.c +++ b/App/uart_trans.c @@ -1,14 +1,17 @@ /** * @file uart_trans.c - * @brief Bare-metal UART DMA/IDLE transport layer. + * @brief Bare-metal UART DMA/IDLE transport and MUX helpers. */ #include "uart_trans.h" -#include "usart.h" +#include "../Core/Inc/usart.h" #include +#define UART_MUX_SYNC 0x7Eu +#define UART_MUX_TAIL 0x7Fu + typedef struct { UART_HandleTypeDef *huart; uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE]; @@ -40,55 +43,23 @@ 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; -} - static int apply_uart_config(uart_channel_t channel) { uart_channel_ctx_t *ctx = &g_channels[channel]; - UART_HandleTypeDef *huart = ctx->huart; - uint32_t word_length; - uint32_t parity; - - if (huart == NULL) { + if (ctx->huart == NULL) { return -1; } if (ctx->running) { - HAL_UART_DMAStop(huart); + HAL_UART_DMAStop(ctx->huart); ctx->running = false; } - huart->Init.BaudRate = ctx->config.baudrate; - huart->Init.StopBits = (ctx->config.stop_bits == 2u) ? UART_STOPBITS_2 : UART_STOPBITS_1; - - switch (ctx->config.parity) { - case 1: - parity = UART_PARITY_ODD; - break; - case 2: - parity = UART_PARITY_EVEN; - break; - default: - parity = UART_PARITY_NONE; - break; - } - - if (parity == UART_PARITY_NONE) { - word_length = (ctx->config.data_bits == 9u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B; - } else { - word_length = (ctx->config.data_bits >= 8u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B; - } - - huart->Init.WordLength = word_length; - huart->Init.Parity = parity; - - return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1; + ctx->huart->Init.BaudRate = ctx->config.baudrate; + ctx->huart->Init.WordLength = UART_WORDLENGTH_8B; + ctx->huart->Init.StopBits = UART_STOPBITS_1; + ctx->huart->Init.Parity = UART_PARITY_NONE; + return (HAL_UART_Init(ctx->huart) == HAL_OK) ? 0 : -1; } static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index) @@ -96,9 +67,7 @@ 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); + uint16_t next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE); if (next_head == ctx->rx_tail) { ctx->stats.errors++; break; @@ -149,16 +118,12 @@ static void kick_tx(uart_channel_t channel) 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; - + g_channels[UART_CHANNEL_U0].huart = &huart2; + g_channels[UART_CHANNEL_U1].huart = &huart3; + g_channels[UART_CHANNEL_U0].config.baudrate = UART_DEFAULT_BAUDRATE; + g_channels[UART_CHANNEL_U1].config.baudrate = UART_DEFAULT_BAUDRATE; + g_channels[UART_CHANNEL_U0].initialized = true; + g_channels[UART_CHANNEL_U1].initialized = true; return 0; } @@ -167,7 +132,6 @@ int uart_trans_config(uart_channel_t channel, const uart_config_t *config) if (channel >= UART_CHANNEL_MAX || config == NULL) { return -1; } - g_channels[channel].config = *config; return apply_uart_config(channel); } @@ -208,29 +172,16 @@ 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) +void uart_trans_poll(void) { - 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)); + kick_tx(UART_CHANNEL_U0); + kick_tx(UART_CHANNEL_U1); } uint16_t uart_trans_rx_available(uart_channel_t channel) @@ -238,7 +189,6 @@ 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); } @@ -256,11 +206,9 @@ uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len 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++; } - return copied; } @@ -287,10 +235,18 @@ uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t return written; } -void uart_trans_poll(void) +void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats) { - kick_tx(UART_CHANNEL_SERVER); - kick_tx(UART_CHANNEL_CLIENT); + if (channel < UART_CHANNEL_MAX && stats != NULL) { + *stats = g_channels[channel].stats; + } +} + +void uart_trans_reset_stats(uart_channel_t channel) +{ + if (channel < UART_CHANNEL_MAX) { + memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats)); + } } void uart_trans_idle_handler(uart_channel_t channel) @@ -301,14 +257,12 @@ void uart_trans_idle_handler(uart_channel_t channel) if (channel >= UART_CHANNEL_MAX) { return; } - huart = g_channels[channel].huart; g_channels[channel].stats.idle_events++; 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); } @@ -317,7 +271,6 @@ void uart_trans_rx_half_cplt_handler(uart_channel_t channel) if (channel >= UART_CHANNEL_MAX) { return; } - g_channels[channel].stats.rx_half_events++; process_rx_snapshot(channel, UART_RX_DMA_BUFFER_SIZE / 2u); } @@ -327,7 +280,6 @@ void uart_trans_rx_cplt_handler(uart_channel_t channel) if (channel >= UART_CHANNEL_MAX) { return; } - g_channels[channel].stats.rx_full_events++; process_rx_snapshot(channel, 0u); } @@ -337,9 +289,88 @@ void uart_trans_tx_cplt_handler(uart_channel_t channel) if (channel >= UART_CHANNEL_MAX) { return; } - g_channels[channel].tx_busy = false; g_channels[channel].stats.tx_bytes += g_channels[channel].tx_dma_len; g_channels[channel].tx_dma_len = 0u; kick_tx(channel); } + +bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame) +{ + uint8_t header[5]; + uint16_t available; + uint16_t payload_len; + + if (channel >= UART_CHANNEL_MAX || frame == NULL) { + return false; + } + + available = uart_trans_rx_available(channel); + if (available < 6u) { + return false; + } + + if (uart_trans_read(channel, header, sizeof(header)) != sizeof(header)) { + return false; + } + if (header[0] != UART_MUX_SYNC) { + return false; + } + + payload_len = (uint16_t)(((uint16_t)header[1] << 8) | header[2]); + if (payload_len > sizeof(frame->payload)) { + return false; + } + if (uart_trans_rx_available(channel) < (uint16_t)(payload_len + 1u)) { + return false; + } + + frame->src_id = header[3]; + frame->dst_mask = header[4]; + frame->payload_len = payload_len; + if (payload_len > 0u) { + if (uart_trans_read(channel, frame->payload, payload_len) != payload_len) { + return false; + } + } + + { + uint8_t tail = 0u; + if (uart_trans_read(channel, &tail, 1u) != 1u || tail != UART_MUX_TAIL) { + return false; + } + } + + return true; +} + +bool uart_mux_encode_frame(uint8_t src_id, + uint8_t dst_mask, + const uint8_t *payload, + uint16_t payload_len, + uint8_t *out, + uint16_t *out_len, + uint16_t out_capacity) +{ + uint16_t frame_len; + + if (out == NULL || out_len == NULL) { + return false; + } + frame_len = (uint16_t)(payload_len + 6u); + if (frame_len > out_capacity) { + return false; + } + + out[0] = UART_MUX_SYNC; + out[1] = (uint8_t)(payload_len >> 8); + out[2] = (uint8_t)(payload_len & 0xFFu); + out[3] = src_id; + out[4] = dst_mask; + if (payload_len > 0u && payload != NULL) { + memcpy(&out[5], payload, payload_len); + } + out[5 + payload_len] = UART_MUX_TAIL; + *out_len = frame_len; + return true; +} diff --git a/App/uart_trans.h b/App/uart_trans.h index 07d99ee..94dc4e4 100644 --- a/App/uart_trans.h +++ b/App/uart_trans.h @@ -1,6 +1,6 @@ /** * @file uart_trans.h - * @brief Bare-metal UART DMA/IDLE transport layer. + * @brief Bare-metal UART DMA/IDLE transport and MUX framing helpers. */ #ifndef __UART_TRANS_H__ @@ -14,28 +14,28 @@ extern "C" { #endif typedef enum { - UART_CHANNEL_SERVER = 0, - UART_CHANNEL_CLIENT = 1, + UART_CHANNEL_U0 = 0, + UART_CHANNEL_U1 = 1, UART_CHANNEL_MAX } uart_channel_t; +typedef struct { + uint8_t src_id; + uint8_t dst_mask; + uint16_t payload_len; + uint8_t payload[256]; +} uart_mux_frame_t; + #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 +#define UART_RX_RING_BUFFER_SIZE 256u +#define UART_TX_RING_BUFFER_SIZE 256u +#define UART_DEFAULT_BAUDRATE 115200u typedef struct { uint32_t baudrate; - uint8_t data_bits; - uint8_t stop_bits; - uint8_t parity; } uart_config_t; -#define UART_DEFAULT_BAUDRATE 115200u -#define UART_DEFAULT_DATA_BITS 8u -#define UART_DEFAULT_STOP_BITS 1u -#define UART_DEFAULT_PARITY 0u - typedef struct { uint32_t rx_bytes; uint32_t tx_bytes; @@ -61,9 +61,17 @@ void uart_trans_idle_handler(uart_channel_t channel); void uart_trans_rx_half_cplt_handler(uart_channel_t channel); void uart_trans_rx_cplt_handler(uart_channel_t channel); void uart_trans_tx_cplt_handler(uart_channel_t channel); +bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame); +bool uart_mux_encode_frame(uint8_t src_id, + uint8_t dst_mask, + const uint8_t *payload, + uint16_t payload_len, + uint8_t *out, + uint16_t *out_len, + uint16_t out_capacity); #ifdef __cplusplus } #endif -#endif +#endif /* __UART_TRANS_H__ */ diff --git a/Core/Src/main.c b/Core/Src/main.c index 9df4983..7eae588 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -4,16 +4,6 @@ * @file : main.c * @brief : Main program body ****************************************************************************** - * @attention - * - * Copyright (c) 2026 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ @@ -34,7 +24,6 @@ #include "CH390_Interface.h" #include "SEGGER_RTT.h" #include "config.h" -#include "flash_param.h" #include "ethernetif.h" #include "ch390_runtime.h" #include "lwip/init.h" @@ -44,71 +33,22 @@ #include "uart_trans.h" /* USER CODE END Includes */ -/* Private typedef -----------------------------------------------------------*/ -/* USER CODE BEGIN PTD */ - -/* USER CODE END PTD */ - /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ -/* LED 指示灯 */ #define LED_PIN GPIO_PIN_13 #define LED_PORT GPIOC +#define APP_ROUTE_BUFFER_SIZE 256u +#define STACK_GUARD_WORD 0xA5A5A5A5u /* USER CODE END PD */ -/* Private macro -------------------------------------------------------------*/ -/* USER CODE BEGIN PM */ - -/* USER CODE END PM */ - /* Private variables ---------------------------------------------------------*/ - /* USER CODE BEGIN PV */ static volatile uint16_t g_led_blink_ticks = 0; static uint8_t g_clock_fallback_to_hsi = 0u; volatile uint8_t g_uart1_rx_probe_byte = 0u; - -typedef struct { - uint8_t data[256]; - uint16_t len; -} tcp_bridge_buffer_t; - -static tcp_bridge_buffer_t g_server_to_client; -static tcp_bridge_buffer_t g_client_to_server; - -static void App_ForwardTcpPair(void) -{ - int rc; - - if ((g_server_to_client.len == 0u) && tcp_server_is_connected()) { - rc = tcp_server_recv(g_server_to_client.data, sizeof(g_server_to_client.data), 0u); - if (rc > 0) { - g_server_to_client.len = (uint16_t)rc; - } - } - - if ((g_server_to_client.len != 0u) && tcp_client_is_connected()) { - rc = tcp_client_send(g_server_to_client.data, g_server_to_client.len); - if (rc == (int)g_server_to_client.len) { - g_server_to_client.len = 0u; - } - } - - if ((g_client_to_server.len == 0u) && tcp_client_is_connected()) { - rc = tcp_client_recv(g_client_to_server.data, sizeof(g_client_to_server.data), 0u); - if (rc > 0) { - g_client_to_server.len = (uint16_t)rc; - } - } - - if ((g_client_to_server.len != 0u) && tcp_server_is_connected()) { - rc = tcp_server_send(g_client_to_server.data, g_client_to_server.len); - if (rc == (int)g_client_to_server.len) { - g_client_to_server.len = 0u; - } - } -} - +static uint8_t g_stack_guard_reported = 0u; +static uint8_t g_mux_response_frame[272]; +static uint8_t g_links_started = 0u; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ @@ -120,20 +60,42 @@ static void BootDiag_ReportCh390(void); static void App_PollUart1ConfigRx(void); static void App_Init(void); static void App_Poll(void); +static void App_ConfigureLinks(const device_config_t *cfg); +static void App_RouteRawUartTraffic(void); +static void App_RouteMuxUartTraffic(void); +static void App_RouteTcpTraffic(void); +static void StackGuard_Init(void); +static void StackGuard_Check(void); +static void App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ +extern uint32_t Stack_Mem[]; -/** - * @brief LED 初始化(点亮表示系统启动) - */ static void LED_Init(void) { - /* LED 灭(PC13 高电平灭,低电平亮) */ HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET); } +static void StackGuard_Init(void) +{ + Stack_Mem[0] = STACK_GUARD_WORD; + g_stack_guard_reported = 0u; +} + +static void StackGuard_Check(void) +{ + if (Stack_Mem[0] != STACK_GUARD_WORD) { + if (g_stack_guard_reported == 0u) { + g_stack_guard_reported = 1u; + SEGGER_RTT_WriteString(0, "ERROR: Main stack guard overwritten\r\n"); + } + __disable_irq(); + NVIC_SystemReset(); + } +} + static void LED_StartBlink(void) { if (HAL_TIM_Base_Start_IT(&htim4) != HAL_OK) { @@ -141,9 +103,6 @@ static void LED_StartBlink(void) } } -/** - * @brief LED 闪烁(用于指示系统运行状态) - */ void LED_Toggle(void) { HAL_GPIO_TogglePin(LED_PORT, LED_PIN); @@ -177,13 +136,11 @@ static void BootDiag_ReportCh390(void) diag.link_up, mac_hw[0], mac_hw[1], mac_hw[2], mac_hw[3], mac_hw[4], mac_hw[5]); SEGGER_RTT_printf(0, - "NET cfg IP=%u.%u.%u.%u MASK=%u.%u.%u.%u GW=%u.%u.%u.%u SrvPort=%u Cli=%u.%u.%u.%u:%u\r\n", - cfg->ip[0], cfg->ip[1], cfg->ip[2], cfg->ip[3], - cfg->mask[0], cfg->mask[1], cfg->mask[2], cfg->mask[3], - cfg->gw[0], cfg->gw[1], cfg->gw[2], cfg->gw[3], - cfg->server_port, - cfg->remote_ip[0], cfg->remote_ip[1], cfg->remote_ip[2], cfg->remote_ip[3], - cfg->remote_port); + "NET cfg IP=%u.%u.%u.%u MASK=%u.%u.%u.%u GW=%u.%u.%u.%u MUX=%u\r\n", + cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3], + cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3], + cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3], + cfg->mux_mode); } static void App_PollUart1ConfigRx(void) @@ -194,97 +151,302 @@ static void App_PollUart1ConfigRx(void) } } +static void App_ConfigureLinks(const device_config_t *cfg) +{ + tcp_server_instance_config_t server_cfg; + tcp_client_instance_config_t client_cfg; + + (void)tcp_server_init_all(); + (void)tcp_client_init_all(); + + server_cfg.enabled = (cfg->links[CONFIG_LINK_S1].enabled != 0u); + server_cfg.port = cfg->links[CONFIG_LINK_S1].local_port; + (void)tcp_server_config(0u, &server_cfg); + + server_cfg.enabled = (cfg->links[CONFIG_LINK_S2].enabled != 0u); + server_cfg.port = cfg->links[CONFIG_LINK_S2].local_port; + (void)tcp_server_config(1u, &server_cfg); + + memcpy(client_cfg.remote_ip, cfg->links[CONFIG_LINK_C1].remote_ip, sizeof(client_cfg.remote_ip)); + client_cfg.local_port = cfg->links[CONFIG_LINK_C1].local_port; + client_cfg.remote_port = cfg->links[CONFIG_LINK_C1].remote_port; + client_cfg.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS; + client_cfg.enabled = (cfg->links[CONFIG_LINK_C1].enabled != 0u); + client_cfg.auto_reconnect = true; + (void)tcp_client_config(0u, &client_cfg); + + memcpy(client_cfg.remote_ip, cfg->links[CONFIG_LINK_C2].remote_ip, sizeof(client_cfg.remote_ip)); + client_cfg.local_port = cfg->links[CONFIG_LINK_C2].local_port; + client_cfg.remote_port = cfg->links[CONFIG_LINK_C2].remote_port; + client_cfg.enabled = (cfg->links[CONFIG_LINK_C2].enabled != 0u); + (void)tcp_client_config(1u, &client_cfg); +} + +static void App_StartLinksIfNeeded(void) +{ + if ((g_links_started != 0u) || !netif_is_link_up(&ch390_netif)) { + return; + } + + for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) { + (void)tcp_server_start(i); + } + for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) { + (void)tcp_client_connect(i); + } + + g_links_started = 1u; + SEGGER_RTT_WriteString(0, "NET links started after link-up\r\n"); +} + +static void App_StopLinksIfNeeded(void) +{ + if (netif_is_link_up(&ch390_netif)) { + return; + } + + if (g_links_started != 0u) { + for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) { + (void)tcp_client_disconnect(i); + } + for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) { + (void)tcp_server_stop(i); + } + SEGGER_RTT_WriteString(0, "NET links stopped after link-down\r\n"); + } + + g_links_started = 0u; +} + static void App_Init(void) { - device_config_t *cfg_mut; 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_mut = config_get_mutable(); - cfg_mut->dhcp_enable = 0u; - cfg_mut->ip[0] = 192u; - cfg_mut->ip[1] = 168u; - cfg_mut->ip[2] = 31u; - cfg_mut->ip[3] = 100u; - cfg_mut->mask[0] = 255u; - cfg_mut->mask[1] = 255u; - cfg_mut->mask[2] = 255u; - cfg_mut->mask[3] = 0u; - cfg_mut->gw[0] = 192u; - cfg_mut->gw[1] = 168u; - cfg_mut->gw[2] = 31u; - cfg_mut->gw[3] = 1u; - cfg_mut->server_port = 8080u; - cfg_mut->remote_port = 8081u; + (void)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); + (void)uart_trans_init(); + uart_cfg.baudrate = cfg->uart_baudrate[0]; + (void)uart_trans_config(UART_CHANNEL_U0, &uart_cfg); + uart_cfg.baudrate = cfg->uart_baudrate[1]; + (void)uart_trans_config(UART_CHANNEL_U1, &uart_cfg); + (void)uart_trans_start(UART_CHANNEL_U0); + (void)uart_trans_start(UART_CHANNEL_U1); SEGGER_RTT_Init(); + StackGuard_Init(); SEGGER_RTT_WriteString(0, "\r\nTCP2UART boot\r\n"); if (g_clock_fallback_to_hsi != 0u) { SEGGER_RTT_WriteString(0, "WARN: HSE start failed, fallback to HSI PLL\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]); + IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]); + IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]); + IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]); lwip_netif_init(&ipaddr, &netmask, &gateway); + App_ConfigureLinks(cfg); BootDiag_ReportCh390(); - server_cfg.port = cfg->server_port; - server_cfg.auto_reconnect = true; - (void)tcp_server_init(&server_cfg); - (void)tcp_server_start(); - - memcpy(client_cfg.server_ip, cfg->remote_ip, sizeof(client_cfg.server_ip)); - client_cfg.local_port = 8081u; - client_cfg.server_port = cfg->remote_port; - client_cfg.auto_reconnect = true; - client_cfg.reconnect_interval_ms = cfg->reconnect_interval; - (void)tcp_client_init(&client_cfg); - (void)tcp_client_connect(); - - SEGGER_RTT_WriteString(0, "TCP bridge enabled\r\n"); - - /* Arm UART1 RX interrupt path so config commands can enter via USART1. */ if (HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u) != HAL_OK) { Error_Handler(); } } +static void App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len) +{ + const device_config_t *cfg = config_get(); + uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0; + + if (cfg->mux_mode == MUX_MODE_FRAME) { + uint8_t frame[APP_ROUTE_BUFFER_SIZE + 6u]; + uint16_t frame_len = 0u; + if (uart_mux_encode_frame(src_id, dst_mask, data, len, frame, &frame_len, sizeof(frame))) { + (void)uart_trans_write(channel, frame, frame_len); + } + } else { + (void)uart_trans_write(channel, data, len); + } +} + +static void App_RouteTcpTraffic(void) +{ + const device_config_t *cfg = config_get(); + uint8_t buffer[APP_ROUTE_BUFFER_SIZE]; + + for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) { + int rc = tcp_server_recv(i, buffer, sizeof(buffer)); + if (rc > 0) { + uint8_t link_index = (i == 0u) ? CONFIG_LINK_S1 : CONFIG_LINK_S2; + App_SendToUart(cfg->links[link_index].uart, + config_link_index_to_endpoint(link_index), + config_uart_index_to_endpoint(cfg->links[link_index].uart), + buffer, + (uint16_t)rc); + } + } + + for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) { + int rc = tcp_client_recv(i, buffer, sizeof(buffer)); + if (rc > 0) { + uint8_t link_index = (i == 0u) ? CONFIG_LINK_C1 : CONFIG_LINK_C2; + App_SendToUart(cfg->links[link_index].uart, + config_link_index_to_endpoint(link_index), + config_uart_index_to_endpoint(cfg->links[link_index].uart), + buffer, + (uint16_t)rc); + } + } +} + +static void App_RouteRawUartTraffic(void) +{ + const device_config_t *cfg = config_get(); + uint8_t buffer[APP_ROUTE_BUFFER_SIZE]; + uint16_t len; + + len = uart_trans_read(UART_CHANNEL_U0, buffer, sizeof(buffer)); + if (len > 0u) { + for (uint8_t i = 0; i < CONFIG_LINK_COUNT; ++i) { + if (cfg->links[i].enabled == 0u || cfg->links[i].uart != LINK_UART_U0) { + continue; + } + if (i == CONFIG_LINK_S1) { + (void)tcp_server_send(0u, buffer, len); + } else if (i == CONFIG_LINK_S2) { + (void)tcp_server_send(1u, buffer, len); + } else if (i == CONFIG_LINK_C1) { + (void)tcp_client_send(0u, buffer, len); + } else if (i == CONFIG_LINK_C2) { + (void)tcp_client_send(1u, buffer, len); + } + } + } + + len = uart_trans_read(UART_CHANNEL_U1, buffer, sizeof(buffer)); + if (len > 0u) { + for (uint8_t i = 0; i < CONFIG_LINK_COUNT; ++i) { + if (cfg->links[i].enabled == 0u || cfg->links[i].uart != LINK_UART_U1) { + continue; + } + if (i == CONFIG_LINK_S1) { + (void)tcp_server_send(0u, buffer, len); + } else if (i == CONFIG_LINK_S2) { + (void)tcp_server_send(1u, buffer, len); + } else if (i == CONFIG_LINK_C1) { + (void)tcp_client_send(0u, buffer, len); + } else if (i == CONFIG_LINK_C2) { + (void)tcp_client_send(1u, buffer, len); + } + } + } +} + +static void App_RouteMuxUartTraffic(void) +{ + uart_mux_frame_t frame; + const device_config_t *cfg = config_get(); + + while (uart_mux_try_extract_frame(UART_CHANNEL_U0, &frame)) { + if (frame.dst_mask == 0u) { + at_result_t result; + char *response_text = (char *)&g_mux_response_frame[5]; + if (config_build_response_frame(frame.payload, frame.payload_len, response_text, (uint16_t)(sizeof(g_mux_response_frame) - 6u), &result)) { + uint16_t response_len = (uint16_t)strlen(response_text); + uint16_t frame_len = 0u; + if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U0), 0u, (const uint8_t *)response_text, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) { + (void)uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len); + } + if (result == AT_NEED_REBOOT) { + static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n"; + response_len = (uint16_t)strlen(hint); + if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U0), 0u, (const uint8_t *)hint, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) { + (void)uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len); + } + } + } + continue; + } + + if ((frame.dst_mask & ENDPOINT_S1) != 0u) { + (void)tcp_server_send(0u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_S2) != 0u) { + (void)tcp_server_send(1u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_C1) != 0u) { + (void)tcp_client_send(0u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_C2) != 0u) { + (void)tcp_client_send(1u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_UART3) != 0u && cfg->links[CONFIG_LINK_S2].uart == LINK_UART_U1) { + App_SendToUart(LINK_UART_U1, frame.src_id, ENDPOINT_UART3, frame.payload, frame.payload_len); + } + } + + while (uart_mux_try_extract_frame(UART_CHANNEL_U1, &frame)) { + if (frame.dst_mask == 0u) { + at_result_t result; + char *response_text = (char *)&g_mux_response_frame[5]; + if (config_build_response_frame(frame.payload, frame.payload_len, response_text, (uint16_t)(sizeof(g_mux_response_frame) - 6u), &result)) { + uint16_t response_len = (uint16_t)strlen(response_text); + uint16_t frame_len = 0u; + if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U1), 0u, (const uint8_t *)response_text, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) { + (void)uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len); + } + if (result == AT_NEED_REBOOT) { + static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n"; + response_len = (uint16_t)strlen(hint); + if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U1), 0u, (const uint8_t *)hint, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) { + (void)uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len); + } + } + } + continue; + } + + if ((frame.dst_mask & ENDPOINT_S1) != 0u) { + (void)tcp_server_send(0u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_S2) != 0u) { + (void)tcp_server_send(1u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_C1) != 0u) { + (void)tcp_client_send(0u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_C2) != 0u) { + (void)tcp_client_send(1u, frame.payload, frame.payload_len); + } + if ((frame.dst_mask & ENDPOINT_UART2) != 0u) { + App_SendToUart(LINK_UART_U0, frame.src_id, ENDPOINT_UART2, frame.payload, frame.payload_len); + } + } +} + static void App_Poll(void) { ethernetif_poll(); ethernetif_check_link(); sys_check_timeouts(); + App_StopLinksIfNeeded(); + App_StartLinksIfNeeded(); tcp_client_poll(); - App_ForwardTcpPair(); uart_trans_poll(); App_PollUart1ConfigRx(); + StackGuard_Check(); config_poll(); + App_RouteTcpTraffic(); + + if (config_get()->mux_mode == MUX_MODE_FRAME) { + App_RouteMuxUartTraffic(); + } else { + App_RouteRawUartTraffic(); + } if (config_is_reset_requested()) { config_clear_reset_requested(); @@ -295,81 +457,36 @@ static void App_Poll(void) HAL_IWDG_Refresh(&hiwdg); } } - /* USER CODE END 0 */ -/** - * @brief The application entry point. - * @retval int - */ int main(void) { - - /* USER CODE BEGIN 1 */ - - /* USER CODE END 1 */ - - /* MCU Configuration--------------------------------------------------------*/ - - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); - - /* USER CODE BEGIN Init */ - - /* USER CODE END Init */ - - /* Configure the system clock */ SystemClock_Config(); - - /* USER CODE BEGIN SysInit */ - - /* USER CODE END SysInit */ - - /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); - // MX_IWDG_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); MX_USART3_UART_Init(); MX_SPI1_Init(); MX_TIM4_Init(); - /* USER CODE BEGIN 2 */ - /* LED 初始化 */ LED_Init(); LED_StartBlink(); - App_Init(); - - /* USER CODE END 2 */ - /* Infinite loop */ - /* USER CODE BEGIN WHILE */ while (1) { - /* USER CODE END WHILE */ - - /* USER CODE BEGIN 3 */ App_Poll(); } - /* USER CODE END 3 */ } -/** - * @brief System Clock Configuration - * @retval None - */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; g_clock_fallback_to_hsi = 0u; - - /** Initializes the RCC Oscillators according to the specified parameters - * in the RCC_OscInitTypeDef structure. - */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; @@ -379,11 +496,6 @@ void SystemClock_Config(void) RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { - /* - * Some bring-up boards fail to start the external crystal cleanly. - * Fall back to HSI-based PLL so the firmware can still boot and expose - * RTT / debugger evidence instead of trapping during clock init. - */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSEState = RCC_HSE_OFF; RCC_OscInitStruct.HSIState = RCC_HSI_ON; @@ -397,8 +509,6 @@ void SystemClock_Config(void) g_clock_fallback_to_hsi = 1u; } - /** Initializes the CPU, AHB and APB buses clocks - */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; @@ -412,11 +522,6 @@ void SystemClock_Config(void) } } -/* USER CODE BEGIN 4 */ - -/** - * @brief 重定向 printf 到 UART1(调试输出) - */ #ifdef __GNUC__ int _write(int file, char *ptr, int len) { @@ -444,35 +549,16 @@ void Debug_TrapWithRttHint(const char *tag) } } -/* USER CODE END 4 */ - -/** - * @brief This function is executed in case of error occurrence. - * @retval None - */ void Error_Handler(void) { - /* USER CODE BEGIN Error_Handler_Debug */ - /* User can add his own implementation to report the HAL error return state */ Debug_TrapWithRttHint("Error_Handler"); - /* USER CODE END Error_Handler_Debug */ } + #ifdef USE_FULL_ASSERT -/** - * @brief Reports the name of the source file and the source line number - * where the assert_param error has occurred. - * @param file: pointer to the source file name - * @param line: assert_param error line source number - * @retval None - */ void assert_failed(uint8_t *file, uint32_t line) { - /* USER CODE BEGIN 6 */ (void)file; (void)line; Debug_TrapWithRttHint("assert_failed"); - /* User can add his own implementation to report the file name and line number, - ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ - /* USER CODE END 6 */ } -#endif /* USE_FULL_ASSERT */ +#endif diff --git a/Core/Src/stm32f1xx_it.c b/Core/Src/stm32f1xx_it.c index a2bb9c8..d71dd3a 100644 --- a/Core/Src/stm32f1xx_it.c +++ b/Core/Src/stm32f1xx_it.c @@ -4,62 +4,15 @@ * @file stm32f1xx_it.c * @brief Interrupt Service Routines. ****************************************************************************** - * @attention - * - * Copyright (c) 2026 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** */ /* USER CODE END Header */ -/* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f1xx_it.h" -/* Private includes ----------------------------------------------------------*/ -/* USER CODE BEGIN Includes */ #include "ethernetif.h" -#include "ch390_runtime.h" -#include "SEGGER_RTT.h" #include "uart_trans.h" #include "config.h" -/* USER CODE END Includes */ -/* Private typedef -----------------------------------------------------------*/ -/* USER CODE BEGIN TD */ - -/* USER CODE END TD */ - -/* Private define ------------------------------------------------------------*/ -/* USER CODE BEGIN PD */ - -/* USER CODE END PD */ - -/* Private macro -------------------------------------------------------------*/ -/* USER CODE BEGIN PM */ - -/* USER CODE END PM */ - -/* Private variables ---------------------------------------------------------*/ -/* USER CODE BEGIN PV */ - -/* USER CODE END PV */ - -/* Private function prototypes -----------------------------------------------*/ -/* USER CODE BEGIN PFP */ - -/* USER CODE END PFP */ - -/* Private user code ---------------------------------------------------------*/ -/* USER CODE BEGIN 0 */ - -/* USER CODE END 0 */ - -/* External variables --------------------------------------------------------*/ extern SPI_HandleTypeDef hspi1; extern TIM_HandleTypeDef htim4; extern DMA_HandleTypeDef hdma_usart1_rx; @@ -71,332 +24,153 @@ extern DMA_HandleTypeDef hdma_usart3_tx; extern UART_HandleTypeDef huart1; extern UART_HandleTypeDef huart2; extern UART_HandleTypeDef huart3; -/* USER CODE BEGIN EV */ +extern volatile uint8_t g_uart1_rx_probe_byte; -/* USER CODE END EV */ - -/******************************************************************************/ -/* Cortex-M3 Processor Interruption and Exception Handlers */ -/******************************************************************************/ -/** - * @brief This function handles Non maskable interrupt. - */ void NMI_Handler(void) { - /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ - - /* USER CODE END NonMaskableInt_IRQn 0 */ - /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ Debug_TrapWithRttHint("NMI_Handler"); - /* USER CODE END NonMaskableInt_IRQn 1 */ } -/** - * @brief This function handles Hard fault interrupt. - */ void HardFault_Handler(void) { - /* USER CODE BEGIN HardFault_IRQn 0 */ - - /* USER CODE END HardFault_IRQn 0 */ Debug_TrapWithRttHint("HardFault_Handler"); } -/** - * @brief This function handles Memory management fault. - */ void MemManage_Handler(void) { - /* USER CODE BEGIN MemoryManagement_IRQn 0 */ - - /* USER CODE END MemoryManagement_IRQn 0 */ Debug_TrapWithRttHint("MemManage_Handler"); } -/** - * @brief This function handles Prefetch fault, memory access fault. - */ void BusFault_Handler(void) { - /* USER CODE BEGIN BusFault_IRQn 0 */ - - /* USER CODE END BusFault_IRQn 0 */ Debug_TrapWithRttHint("BusFault_Handler"); } -/** - * @brief This function handles Undefined instruction or illegal state. - */ void UsageFault_Handler(void) { - /* USER CODE BEGIN UsageFault_IRQn 0 */ - - /* USER CODE END UsageFault_IRQn 0 */ Debug_TrapWithRttHint("UsageFault_Handler"); } -/** - * @brief This function handles Debug monitor. - */ void DebugMon_Handler(void) { - /* USER CODE BEGIN DebugMonitor_IRQn 0 */ - - /* USER CODE END DebugMonitor_IRQn 0 */ - /* USER CODE BEGIN DebugMonitor_IRQn 1 */ - - /* USER CODE END DebugMonitor_IRQn 1 */ } -/** - * @brief This function handles System tick timer. - */ void SysTick_Handler(void) { - /* USER CODE BEGIN SysTick_IRQn 0 */ - - /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); - /* USER CODE BEGIN SysTick_IRQn 1 */ - - /* USER CODE END SysTick_IRQn 1 */ } -/******************************************************************************/ -/* STM32F1xx Peripheral Interrupt Handlers */ -/* Add here the Interrupt Handlers for the used peripherals. */ -/* For the available peripheral interrupt handler names, */ -/* please refer to the startup file (startup_stm32f1xx.s). */ -/******************************************************************************/ - -/** - * @brief This function handles DMA1 channel2 global interrupt. - */ void DMA1_Channel2_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel2_IRQn 0 */ - - /* USER CODE END DMA1_Channel2_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart3_tx); - /* USER CODE BEGIN DMA1_Channel2_IRQn 1 */ - - /* USER CODE END DMA1_Channel2_IRQn 1 */ } -/** - * @brief This function handles DMA1 channel3 global interrupt. - */ void DMA1_Channel3_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel3_IRQn 0 */ - - /* USER CODE END DMA1_Channel3_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart3_rx); - /* USER CODE BEGIN DMA1_Channel3_IRQn 1 */ - - /* USER CODE END DMA1_Channel3_IRQn 1 */ } -/** - * @brief This function handles DMA1 channel4 global interrupt. - */ void DMA1_Channel4_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel4_IRQn 0 */ - - /* USER CODE END DMA1_Channel4_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart1_tx); - /* USER CODE BEGIN DMA1_Channel4_IRQn 1 */ - - /* USER CODE END DMA1_Channel4_IRQn 1 */ } -/** - * @brief This function handles DMA1 channel5 global interrupt. - */ void DMA1_Channel5_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel5_IRQn 0 */ - - /* USER CODE END DMA1_Channel5_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart1_rx); - /* USER CODE BEGIN DMA1_Channel5_IRQn 1 */ - - /* USER CODE END DMA1_Channel5_IRQn 1 */ } -/** - * @brief This function handles DMA1 channel6 global interrupt. - */ void DMA1_Channel6_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */ - - /* USER CODE END DMA1_Channel6_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart2_rx); - /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */ - - /* USER CODE END DMA1_Channel6_IRQn 1 */ } -/** - * @brief This function handles DMA1 channel7 global interrupt. - */ void DMA1_Channel7_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel7_IRQn 0 */ - - /* USER CODE END DMA1_Channel7_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart2_tx); - /* USER CODE BEGIN DMA1_Channel7_IRQn 1 */ - - /* USER CODE END DMA1_Channel7_IRQn 1 */ } -/** - * @brief This function handles TIM4 global interrupt. - */ void TIM4_IRQHandler(void) { - /* USER CODE BEGIN TIM4_IRQn 0 */ - - /* USER CODE END TIM4_IRQn 0 */ HAL_TIM_IRQHandler(&htim4); - /* USER CODE BEGIN TIM4_IRQn 1 */ - - /* USER CODE END TIM4_IRQn 1 */ } -/** - * @brief This function handles SPI1 global interrupt. - */ void SPI1_IRQHandler(void) { - /* USER CODE BEGIN SPI1_IRQn 0 */ - - /* USER CODE END SPI1_IRQn 0 */ HAL_SPI_IRQHandler(&hspi1); - /* USER CODE BEGIN SPI1_IRQn 1 */ - - /* USER CODE END SPI1_IRQn 1 */ } -/** - * @brief This function handles USART1 global interrupt. - */ void USART1_IRQHandler(void) { - /* USER CODE BEGIN USART1_IRQn 0 */ - /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); - /* USER CODE BEGIN USART1_IRQn 1 */ - - /* USER CODE END USART1_IRQn 1 */ } -/** - * @brief This function handles USART2 global interrupt. - */ void USART2_IRQHandler(void) { - /* USER CODE BEGIN USART2_IRQn 0 */ - /* Handle IDLE interrupt for Server transparent transmission */ if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); - uart_trans_idle_handler(UART_CHANNEL_SERVER); + uart_trans_idle_handler(UART_CHANNEL_U0); } - /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); - /* USER CODE BEGIN USART2_IRQn 1 */ - - /* USER CODE END USART2_IRQn 1 */ } -/** - * @brief This function handles USART3 global interrupt. - */ void USART3_IRQHandler(void) { - /* USER CODE BEGIN USART3_IRQn 0 */ - /* Handle IDLE interrupt for Client transparent transmission */ if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart3); - uart_trans_idle_handler(UART_CHANNEL_CLIENT); + uart_trans_idle_handler(UART_CHANNEL_U1); } - /* USER CODE END USART3_IRQn 0 */ HAL_UART_IRQHandler(&huart3); - /* USER CODE BEGIN USART3_IRQn 1 */ - - /* USER CODE END USART3_IRQn 1 */ } -/* USER CODE BEGIN 1 */ -extern volatile uint8_t g_uart1_rx_probe_byte; - -/** - * @brief This function handles EXTI0 interrupt (CH390D INT pin). - */ void EXTI0_IRQHandler(void) { - /* Clear interrupt flag */ if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0)) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); - - /* Defer CH390 processing to main loop */ ethernetif_set_irq_pending(); - } + } } -/** - * @brief HAL UART TX Complete callback - */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { - uart_trans_tx_cplt_handler(UART_CHANNEL_SERVER); + uart_trans_tx_cplt_handler(UART_CHANNEL_U0); } else if (huart == &huart3) { - uart_trans_tx_cplt_handler(UART_CHANNEL_CLIENT); + uart_trans_tx_cplt_handler(UART_CHANNEL_U1); } } -/** - * @brief HAL UART RX Complete callback - */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { config_uart_rx_byte(g_uart1_rx_probe_byte); - HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u); + (void)HAL_UART_Receive_IT(&huart1, (uint8_t *)&g_uart1_rx_probe_byte, 1u); } else if (huart == &huart2) { - uart_trans_rx_cplt_handler(UART_CHANNEL_SERVER); + uart_trans_rx_cplt_handler(UART_CHANNEL_U0); } else if (huart == &huart3) { - uart_trans_rx_cplt_handler(UART_CHANNEL_CLIENT); + uart_trans_rx_cplt_handler(UART_CHANNEL_U1); } } -/** - * @brief HAL UART RX Half Complete callback - */ void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { - uart_trans_rx_half_cplt_handler(UART_CHANNEL_SERVER); + uart_trans_rx_half_cplt_handler(UART_CHANNEL_U0); } else if (huart == &huart3) { - uart_trans_rx_half_cplt_handler(UART_CHANNEL_CLIENT); + uart_trans_rx_half_cplt_handler(UART_CHANNEL_U1); } } -/* USER CODE END 1 */ diff --git a/Drivers/LwIP/src/netif/ethernetif.c b/Drivers/LwIP/src/netif/ethernetif.c index a832638..30a652e 100644 --- a/Drivers/LwIP/src/netif/ethernetif.c +++ b/Drivers/LwIP/src/netif/ethernetif.c @@ -52,7 +52,7 @@ uint8_t ethernetif_is_irq_pending(void) static void low_level_init(struct netif *netif) { - ch390_runtime_init(netif, config_get()->mac); + ch390_runtime_init(netif, config_get()->net.mac); } static err_t low_level_output(struct netif *netif, struct pbuf *p) diff --git a/MDK-ARM/TCP2UART.uvoptx b/MDK-ARM/TCP2UART.uvoptx index e7e2ef9..25488f9 100644 --- a/MDK-ARM/TCP2UART.uvoptx +++ b/MDK-ARM/TCP2UART.uvoptx @@ -103,7 +103,7 @@ 1 0 0 - 6 + 3 @@ -114,9 +114,34 @@ - STLink\ST-LINKIII-KEIL_SWO.dll + BIN\CMSIS_AGDI.dll + + 0 + ARMRTXEVENTFLAGS + -L70 -Z18 -C0 -M0 -T1 + + + 0 + DLGTARM + (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0) + + + 0 + ARMDBGFLAGS + + + + 0 + DLGUARM + + + + 0 + CMSIS_AGDI + -X"Any" -UAny -O206 -S8 -C0 -P00000000 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO65554 -TC10000000 -TT10000000 -TP20 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_128.FLM -FS08000000 -FL020000 -FP0($$Device:STM32F103R8$Flash\STM32F10x_128.FLM) + 0 UL2CM3 @@ -194,8 +219,8 @@ 0 0 0 - startup_stm32f103xe.s - startup_stm32f103xe.s + startup_stm32f103xb.s + startup_stm32f103xb.s 0 0 @@ -238,8 +263,8 @@ 0 0 0 - ../Core/Src/freertos.c - freertos.c + ../Core/Src/dma.c + dma.c 0 0 @@ -250,8 +275,8 @@ 0 0 0 - ../Core/Src/dma.c - dma.c + ../Core/Src/iwdg.c + iwdg.c 0 0 @@ -262,8 +287,8 @@ 0 0 0 - ../Core/Src/iwdg.c - iwdg.c + ../Core/Src/tim.c + tim.c 0 0 @@ -342,6 +367,18 @@ 0 0 0 + ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c + stm32f1xx_hal_iwdg.c + 0 + 0 + + + 3 + 13 + 1 + 0 + 0 + 0 ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c stm32f1xx_hal.c 0 @@ -349,7 +386,7 @@ 3 - 13 + 14 1 0 0 @@ -361,7 +398,7 @@ 3 - 14 + 15 1 0 0 @@ -373,7 +410,7 @@ 3 - 15 + 16 1 0 0 @@ -385,7 +422,7 @@ 3 - 16 + 17 1 0 0 @@ -397,7 +434,7 @@ 3 - 17 + 18 1 0 0 @@ -409,7 +446,7 @@ 3 - 18 + 19 1 0 0 @@ -421,7 +458,7 @@ 3 - 19 + 20 1 0 0 @@ -433,7 +470,7 @@ 3 - 20 + 21 1 0 0 @@ -445,7 +482,7 @@ 3 - 21 + 22 1 0 0 @@ -455,18 +492,6 @@ 0 0 - - 3 - 22 - 1 - 0 - 0 - 0 - ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c - stm32f1xx_hal_iwdg.c - 0 - 0 - 3 23 @@ -486,6 +511,30 @@ 0 0 0 + ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c + stm32f1xx_hal_tim.c + 0 + 0 + + + 3 + 25 + 1 + 0 + 0 + 0 + ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c + stm32f1xx_hal_tim_ex.c + 0 + 0 + + + 3 + 26 + 1 + 0 + 0 + 0 ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c stm32f1xx_hal_uart.c 0 @@ -501,7 +550,7 @@ 0 4 - 25 + 27 1 0 0 @@ -514,35 +563,11 @@ - Middlewares/FreeRTOS + Drivers/CH390 0 0 0 0 - - 5 - 26 - 1 - 0 - 0 - 0 - ../Middlewares/Third_Party/FreeRTOS/Source/croutine.c - croutine.c - 0 - 0 - - - 5 - 27 - 1 - 0 - 0 - 0 - ../Middlewares/Third_Party/FreeRTOS/Source/event_groups.c - event_groups.c - 0 - 0 - 5 28 @@ -550,8 +575,8 @@ 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/list.c - list.c + ..\Drivers\CH390\CH390.c + CH390.c 0 0 @@ -562,8 +587,8 @@ 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/queue.c - queue.c + ..\Drivers\CH390\CH390_Interface.c + CH390_Interface.c 0 0 @@ -574,68 +599,400 @@ 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c - stream_buffer.c + ..\Drivers\CH390\ch390_runtime.c + ch390_runtime.c 0 0 + + + + Drivers/LwIP/core + 0 + 0 + 0 + 0 - 5 + 6 31 1 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/tasks.c - tasks.c + ..\Drivers\LwIP\src\core\def.c + def.c 0 0 - 5 + 6 32 1 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/timers.c - timers.c + ..\Drivers\LwIP\src\core\inet_chksum.c + inet_chksum.c 0 0 - 5 + 6 33 1 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c - cmsis_os2.c + ..\Drivers\LwIP\src\core\init.c + init.c 0 0 - 5 + 6 34 1 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c - heap_4.c + ..\Drivers\LwIP\src\core\ip.c + ip.c 0 0 - 5 + 6 35 1 0 0 0 - ../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c - port.c + ..\Drivers\LwIP\src\core\mem.c + mem.c + 0 + 0 + + + 6 + 36 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\memp.c + memp.c + 0 + 0 + + + 6 + 37 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\netif.c + netif.c + 0 + 0 + + + 6 + 38 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\pbuf.c + pbuf.c + 0 + 0 + + + 6 + 39 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\raw.c + raw.c + 0 + 0 + + + 6 + 40 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\stats.c + stats.c + 0 + 0 + + + 6 + 41 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\sys.c + sys.c + 0 + 0 + + + 6 + 42 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\tcp.c + tcp.c + 0 + 0 + + + 6 + 43 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\tcp_in.c + tcp_in.c + 0 + 0 + + + 6 + 44 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\tcp_out.c + tcp_out.c + 0 + 0 + + + 6 + 45 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\timeouts.c + timeouts.c + 0 + 0 + + + 6 + 46 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\udp.c + udp.c + 0 + 0 + + + 6 + 47 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\ipv4\etharp.c + etharp.c + 0 + 0 + + + 6 + 48 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\ipv4\icmp.c + icmp.c + 0 + 0 + + + 6 + 49 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\ipv4\ip4.c + ip4.c + 0 + 0 + + + 6 + 50 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\ipv4\ip4_addr.c + ip4_addr.c + 0 + 0 + + + 6 + 51 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\core\ipv4\ip4_frag.c + ip4_frag.c + 0 + 0 + + + + + Drivers/LwIP/netif + 0 + 0 + 0 + 0 + + 7 + 52 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\netif\ethernet.c + ethernet.c + 0 + 0 + + + 7 + 53 + 1 + 0 + 0 + 0 + ..\Drivers\LwIP\src\netif\ethernetif.c + ethernetif.c + 0 + 0 + + + + + APP + 0 + 0 + 0 + 0 + + 8 + 54 + 1 + 0 + 0 + 0 + ..\App\config.c + config.c + 0 + 0 + + + 8 + 55 + 1 + 0 + 0 + 0 + ..\App\flash_param.c + flash_param.c + 0 + 0 + + + 8 + 56 + 1 + 0 + 0 + 0 + ..\App\tcp_client.c + tcp_client.c + 0 + 0 + + + 8 + 57 + 1 + 0 + 0 + 0 + ..\App\tcp_server.c + tcp_server.c + 0 + 0 + + + 8 + 58 + 1 + 0 + 0 + 0 + ..\App\uart_trans.c + uart_trans.c + 0 + 0 + + + + + Middlewares/SEGGER_RTT + 0 + 0 + 0 + 0 + + 9 + 59 + 1 + 0 + 0 + 0 + ..\Middlewares\Third_Party\SEGGER_RTT\SEGGER_RTT.c + SEGGER_RTT.c + 0 + 0 + + + 9 + 60 + 1 + 0 + 0 + 0 + ..\Middlewares\Third_Party\SEGGER_RTT\SEGGER_RTT_printf.c + SEGGER_RTT_printf.c 0 0 diff --git a/MDK-ARM/TCP2UART.uvprojx b/MDK-ARM/TCP2UART.uvprojx index 60826f5..7abe30f 100644 --- a/MDK-ARM/TCP2UART.uvprojx +++ b/MDK-ARM/TCP2UART.uvprojx @@ -337,7 +337,7 @@ 0 0 - + --diag_suppress=111,128 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;../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 diff --git a/MDK-ARM/startup_stm32f103xb.s b/MDK-ARM/startup_stm32f103xb.s index 92adae1..bcf16c0 100644 --- a/MDK-ARM/startup_stm32f103xb.s +++ b/MDK-ARM/startup_stm32f103xb.s @@ -32,6 +32,7 @@ Stack_Size EQU 0x400 AREA STACK, NOINIT, READWRITE, ALIGN=3 + EXPORT Stack_Mem Stack_Mem SPACE Stack_Size __initial_sp diff --git a/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h index e33b98a..ed22a7f 100644 --- a/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h +++ b/Middlewares/Third_Party/SEGGER_RTT/SEGGER_RTT_Conf.h @@ -16,7 +16,7 @@ #endif #ifndef BUFFER_SIZE_UP - #define BUFFER_SIZE_UP 1024 + #define BUFFER_SIZE_UP 256 #endif #ifndef BUFFER_SIZE_DOWN diff --git a/工程调试指南.md b/工程调试指南.md index f111f7b..b9b4f9c 100644 --- a/工程调试指南.md +++ b/工程调试指南.md @@ -1,171 +1,374 @@ -# TCP2UART 工程调试指南 +# TCP2UART 调试指导 ## 1. 适用范围 -本指南面向当前 `TCP2UART` 工程,目标是指导以下调试场景: +本指导面向当前 `TCP2UART` 工程,覆盖以下四类调试场景: 1. `STM32F103R8T6 + CH390D` 的基础 bring-up -2. CH390D 的寄存器读写、链路检测、中断与收发调试 -3. `lwIP RAW API + NO_SYS=1` 路线下的网络链路问题定位 -4. UART 透传与 TCP 双链路联调 +2. `SEGGER RTT`、异常陷阱与主循环运行状态确认 +3. `USART1` 配置口、`USART2/USART3` 数据口与 `MUX / NET / LINK[idx]` 协议联调 +4. `TCP Server / TCP Client / UART` 三层数据通路联调与问题隔离 -本指南默认基线: +本指导默认基线如下: -1. 当前工程已切换为裸机主循环架构 -2. CH390 运行时访问统一由 `ch390_runtime` 持有 -3. 调试输出通过 `SEGGER RTT` -4. 当前代码已经通过 `MDK-ARM` 构建,且构建结果为 `0 error / 0 warning` +1. 当前工程采用裸机主循环架构,未使用 FreeRTOS 参与主业务调度 +2. `CH390` 运行时访问统一由 `ch390_runtime` 持有 +3. 调试输出统一使用 `SEGGER RTT` +4. 当前应用层协议模型已经收敛到 `MUX / NET / LINK[idx]` +5. 当前代码应以 `MDK-ARM` 工程构建结果为准,而不是 `CMake + MSVC` 结果 -## 2. 当前工程调试边界 +--- -截至当前版本,工程状态应按以下层级理解: +## 2. 当前工程边界与真实状态 -1. MCU 启动、系统时钟、RTT、TIM4 心跳与主循环均可正常工作 -2. CH390D 已恢复基础寄存器读写,不再处于“全 `0xFF` / 全 `0x0000`”阶段 -3. 实机已读到可信芯片与 PHY 标识:`VID=0x1C00`、`PID=0x9151`、`REV=0x2B`、`PHYID1=0x7371`、`PHYID2=0x9011` -4. 实机已观察到主机 Realtek USB GbE 网卡为 `Connected 100 Mbps`,且设备侧 `lwIP netif` 标志包含 `LINK_UP` -5. 当前调试重点不再是“SPI 是否完全不通”,而是: - - 默认配置是否完整生效 - - MAC 写入与回读是否一致 - - PHY 协商、`INT/LINK/RX/TX` 是否进入正常工作态 - - 网络链路与 UART 透传是否稳定协同 +在进入现场调试前,先统一以下工程边界,避免沿用过时结论: -因此,后续调试应避免继续沿用“CH390 完全无响应”的旧结论,而应转入功能链路验证。 +1. 当前项目的主要软件路径已经切换为: + - `NET`:网络基础参数 + - `LINK[idx]`:链路配置记录 + - `MUX`:数据口承载模式 +2. 对外 AT 配置面应只围绕以下命令展开: + - `AT` + - `AT+?` + - `AT+QUERY` + - `AT+MUX` + - `AT+NET` + - `AT+LINK` + - `AT+SAVE` + - `AT+RESET` + - `AT+DEFAULT` +3. 当前 `CH390D` 的历史“全 `0xFF` / 全 `0x0000`”结论不应再直接沿用。 +4. 已有结论表明: + - MCU 启动、RTT、主循环、TIM4 心跳路径可工作 + - `CH390D` 基础寄存器读写与 `lwIP netif` 基本链路已经打通过一次 + - 真实硬件侧曾定位到 `CH390D` 供电滤波电容虚焊问题 +5. 因此,当前调试重点不再是“CH390 是否完全不通”,而是: + - 启动阶段是否稳定 + - `MUX / NET / LINK[idx]` 协议是否与代码一致 + - UART / TCP / CH390 三层通路是否协同稳定 + - 参数保存、复位和恢复流程是否可靠 -## 3. 代码入口与责任边界 +--- -### 3.1 启动路径 +## 3. 代码入口与调试责任边界 -当前 CH390 相关启动链路为: +### 3.1 启动与主循环入口 -1. `main()` -2. `App_Init()` -3. `lwip_netif_init()` -4. `ethernetif_init()` / `low_level_init()` -5. `ch390_runtime_init()` -6. `ch390_gpio_init()` -7. `ch390_spi_init()` -8. `ch390_hardware_reset()` -9. `ch390_runtime_probe_identity()` -10. `ch390_default_config()` -11. `ch390_set_mac_address()` / `ch390_get_mac()` -12. `ch390_interrupt_init()` +以下代码路径是 bring-up 的第一现场: -### 3.2 运行时责任边界 +1. `Core/Src/main.c` + - `main()`:总启动入口 + - `SystemClock_Config()`:时钟初始化 + - `App_Init()`:应用层初始化 + - `App_Poll()`:主循环核心路径 + - `BootDiag_ReportCh390()`:启动阶段 CH390 诊断输出 +2. `Core/Src/stm32f1xx_it.c` + - 故障与中断入口 + - `USART1/2/3`、`EXTI0`、DMA 回调等联调关键入口 -当前代码约束如下: +### 3.2 CH390 责任边界 + +当前 CH390 调试必须遵守以下责任边界: 1. `Drivers/CH390/CH390_Interface.c` - - 只负责底层 GPIO、SPI 与寄存器/内存访问 - - 当前寄存器读写路径使用 `Mode 3 + bytewise exchange` 事务模型 + - 只负责 GPIO / SPI / 寄存器与内存事务 2. `Drivers/CH390/CH390.c` - - 只负责芯片级 helper,例如 `default_config`、PHY 访问、MAC 操作 + - 只负责芯片级 helper,例如默认配置、PHY、MAC 读写 3. `Drivers/CH390/ch390_runtime.c` - - 唯一的 CH390 运行时拥有者 - - 负责初始化、链路查询、IRQ 消费、RX/TX 服务 + - 唯一的运行时拥有者 + - 负责初始化、链路检查、IRQ 消费、RX/TX 服务与诊断快照 4. `Drivers/LwIP/src/netif/ethernetif.c` - - 不再直接承担复杂 CH390 运行时事务,只做 netif glue + - 只承担 netif glue 与轮询桥接,不应重新下沉复杂 CH390 运行时事务 5. `Core/Src/main.c` - - 启动后只通过 `BootDiag_ReportCh390()` 读取 runtime 提供的启动快照 + - 启动后只通过 runtime 对外暴露的诊断与轮询接口工作 -调试时应优先维护这个边界,不要重新把原始寄存器访问散落回 `main.c` 或 EXTI 中断中。 +调试时不要把原始 CH390 寄存器访问重新散回 `main.c`、中断或多个业务层。 -## 4. 当前硬件连接事实 +### 3.3 配置口与业务口边界 -根据 `PCB/SCH_Schematic1_2026-03-26.pdf`,CH390D 关键硬件事实如下: +1. `USART1` + - 配置口 + - 负责接收 `AT` 命令 + - 当前接收逻辑在: + - `Core/Src/main.c` 的 `App_PollUart1ConfigRx()` + - `Core/Src/stm32f1xx_it.c` 的 `HAL_UART_RxCpltCallback()` + - `App/config.c` 的 `config_uart_rx_byte()` / `config_process_at_cmd()` +2. `USART2 / USART3` + - 数据口 + - 负责普通透传或 MUX 承载 + - 当前入口在 `App/uart_trans.c` -1. MCU 与 CH390D 的连接关系: - - `PA4 -> CH_CS -> U4.SCS` - - `PA5 -> CH_CLK -> R10(22Ω) -> U4.SCK` - - `PA6 <- CH_SDO <- U4.SDO/MISO` - - `PA7 -> CH_SDI -> U4.SDI/MOSI` - - `PB0 <- CH_INT <- U4.INT` - - `PB1 -> CH_RST -> R8(470Ω) -> U4.RSTB` -2. CH390D 使用 `25MHz` 晶振 `X2` -3. CH390D 存在独立相关电源域: - - `VDDK` - - `AVDD33/VDDIO` - - `AVDD33` -4. CH390D 周边去耦电容包括 `C18/C20/C21/C22` -5. 原理图上未看到独立的 `MISO` 外部上拉电阻 -6. 当前代码中的 `ch390_hardware_reset()` 采用: - - 复位前等待 `10ms` - - `RSTB` 拉低 `3ms` - - 释放后等待 `50ms` +--- -调试时必须优先在 **CH390 芯片脚侧** 量测,而不是只在 MCU GPIO 侧判断。 +## 4. 当前硬件与调试工具基线 -## 5. 推荐调试顺序 +### 4.1 核心硬件对象 -### 5.1 P0:先确认基础条件 +1. MCU:`STM32F103R8T6` +2. 以太网芯片:`CH390D` +3. 配置串口:`USART1` +4. 数据串口:`USART2 / USART3` +5. 调试输出:`SEGGER RTT` -每次进入功能调试前,先确认以下最小基线: +### 4.2 构建与下载基线 -1. `MDK` 可成功构建,且当前日志为 `0 error / 0 warning` -2. RTT 可输出启动日志 -3. `BootDiag_ReportCh390()` 能打印出 `VID/PID/REV/NSR/NCR/RCR/IMR/INTCR/GPR/ISR` -4. `App_Poll()` 正常运行,LED 心跳正常 +当前建议优先使用以下工程与产物: -### 5.2 P1:验证 CH390 初始化状态 +1. `MDK-ARM/TCP2UART.uvprojx` +2. `MDK-ARM/TCP2UART/TCP2UART.axf` +3. `MDK-ARM/TCP2UART/TCP2UART.hex` +4. `MDK-ARM/TCP2UART/TCP2UART.map` +5. `MDK-ARM/TCP2UART/TCP2UART.build_log.htm` +6. `build_keil.log` -优先检查以下问题: +说明: -1. `ETH init: probe/default/mac/getmac/irq/done` 是否完整打印 -2. `VID/PID/REV` 与 PHY ID 是否稳定、可信 -3. MAC 写入与 `ch390_get_mac()` 回读是否一致 -4. `RCR/IMR/INTCR/GPR` 是否进入预期工作态 -5. `link_up` 与 `lwIP netif` 的 `LINK_UP` 标志是否与物理网线状态一致 +1. 当前 `CMake configure` 可以完成,但 `CMake + MSVC` 不适合作为 STM32/CMSIS 的最终构建验收依据。 +2. 若需要验证“当前代码是否真实可编译”,优先看 `MDK-ARM` 构建产物与日志。 -如这一层失败,优先回到芯片侧量测: +### 4.3 常用调试工具 + +1. `Keil MDK-ARM` +2. `ST-Link / J-Link` +3. `SEGGER RTT Viewer` +4. `PowerShell` +5. `tools/start_tcp_debug_server.ps1` +6. `tools/tcp_debug_server.py` + +--- + +## 5. 启动阶段调试顺序 + +建议按 P0 ~ P5 顺序推进,不要跳层。 + +### 5.1 P0:确认最小基础条件 + +每次现场调试前,先确认: + +1. `MDK-ARM` 可构建并产出新的 `axf/hex/map` +2. 板卡可正常下载与复位 +3. RTT 可连接并看到启动输出 +4. LED 心跳可工作 +5. `App_Poll()` 已经进入稳定轮询 + +这一层若失败,不要进入网络或协议调试。 + +### 5.2 P1:确认启动日志与 trap 状态 + +上电或复位后,优先看 RTT 输出中是否出现: + +1. `TCP2UART boot` +2. 若 HSE 启动失败,则会出现: + - `WARN: HSE start failed, fallback to HSI PLL` +3. `BootDiag_ReportCh390()` 输出的 CH390 诊断与网络配置快照 + +若发生异常,优先观察是否打印: + +1. `TRAP: Error_Handler` +2. `TRAP: HardFault_Handler` +3. `TRAP: MemManage_Handler` +4. `TRAP: BusFault_Handler` +5. `TRAP: UsageFault_Handler` + +当前 trap 统一收敛到: + +1. `Core/Src/main.c` 的 `Debug_TrapWithRttHint()` +2. 它会打印 RTT、执行 `__BKPT(0)` 并停住 + +因此,若 RTT 中出现 `TRAP:`,应立即接调试器看断点现场,而不是继续盲猜高层逻辑。 + +### 5.3 P2:确认 CH390 初始化链路 + +启动阶段应重点关注 `Drivers/CH390/ch390_runtime.c` 中初始化阶段日志,理想情况下应能依次看到: + +1. `ETH init: gpio` +2. `ETH init: spi` +3. `ETH init: reset` +4. `ETH init: probe` +5. `ETH init: default` +6. `ETH init: mac` +7. `ETH init: getmac` +8. `ETH init: irq` +9. `ETH init: done` + +此阶段重点判定: + +1. `VID / PID / REV` 是否可信 +2. PHY 寄存器是否稳定可读 +3. MAC 写入与回读是否一致 +4. `link_up` 是否与真实网线状态一致 + +若这一层失败,优先做硬件侧量测,而不是先改业务层: 1. `RSTB` 2. `CS` 3. `SCK` 4. `MOSI` 5. `MISO` -6. `VDDK / AVDD33/VDDIO / AVDD33` +6. `INT` +7. `VDDK` +8. `AVDD33 / VDDIO / AVDD33` +9. `XI / XO` -### 5.3 P2:验证 IRQ 与链路检测 +--- -在基础寄存器读写可信后,重点验证: +## 6. USART1 配置口调试 -1. `PB0/EXTI0` 是否能接收到 CH390 的 `INT` -2. `ISR_LNKCHG` 发生时,`ch390_runtime_poll()` 是否正确更新链路状态,不再依赖阻塞式 `HAL_Delay(65)` -3. `ethernetif_check_link()` 与 `ch390_runtime_check_link()` 的结果是否一致 -4. 必须区分“启动快照中的 `g_diag.link_up`”与“主循环中实时更新后的 `netif->flags & NETIF_FLAG_LINK_UP`” +### 6.1 当前命令面 -### 5.4 P3:验证 RX/TX 数据通路 +根据当前代码与手册,配置口应围绕以下命令验证: -在 `link_up` 可信后,再继续验证数据通路: +1. `AT` +2. `AT+?` +3. `AT+QUERY` +4. `AT+MUX=...` +5. `AT+MUX?` +6. `AT+NET=...` +7. `AT+NET?` +8. `AT+LINK=...` +9. `AT+LINK?` +10. `AT+SAVE` +11. `AT+RESET` +12. `AT+DEFAULT` -1. 先确保主机网卡与设备处于同一子网;当前联调基线曾使用主机 `192.168.31.1` 与设备 `192.168.31.x` -2. `ch390_runtime_input()` 是否能稳定读取 `RX SRAM` -3. `rx_status / rx_len` 是否合理 -4. `ch390_runtime_output()` 是否能正确等待 `TCR_TXREQ` 清零并发送帧 -5. 网络端与 UART2/UART3 透传是否联通 +### 6.2 现场关键规则 -### 5.4.1 最小 TCP 调试工具 +根据已有联调记录,配置口最关键的 bench 规则是: -当需要验证 `TCP server(8080)` 到 `TCP client(8081)` 的原始桥接行为时,优先使用仓库内置的最小工具,而不是直接依赖 VOFA 这类带协议/显示假设的上位机工具。 +1. 当前现场验证时,配置命令必须保证以换行完成帧。 +2. 若主机侧发送方式不对,现象会很像“配置口完全无响应”。 +3. 因此,配置口不响应时,第一优先级不是改 parser,而是先验证主机端发送格式与接线。 -推荐工具如下: +### 6.3 最小验证步骤 + +建议按以下顺序验证: + +1. 连接 `USART1` +2. 先发 `AT` +3. 再发 `AT+QUERY` +4. 再发 `AT+NET?` +5. 再发 `AT+LINK?` +6. 修改一个最小参数,例如: + - `AT+MUX=1` +7. 执行: + - `AT+SAVE` + - `AT+RESET` +8. 复位后再次查询,确认配置是否保留 + +### 6.4 持久化失败时怎么查 + +优先检查以下路径: + +1. `App/config.c` + - `config_save()` + - `config_load()` + - `config_set_defaults()` +2. `App/flash_param.c` + - Flash 解锁 + - 页擦除 + - 半字编程 + - 写后校验 +3. 参数页地址: + - `0x0800FC00` + +不要在还没证明 `AT+SAVE` 已真正被接受之前,就直接把 `Flash 全 FFFFFFFF` 归因到 Flash 驱动错误。 + +--- + +## 7. MUX / NET / LINK[idx] 联调指导 + +### 7.1 协议总则 + +当前协议必须按以下模型理解: + +1. `MUX`:全局数据承载模式开关 +2. `NET`:IP / Mask / GW / MAC +3. `LINK[idx]`:链路配置项 + +固定链路索引映射为: + +1. `LINK[0] = S1` +2. `LINK[1] = S2` +3. `LINK[2] = C1` +4. `LINK[3] = C2` + +固定端点编码为: + +1. `C1 = 0x01` +2. `C2 = 0x02` +3. `UART2 = 0x04` +4. `UART3 = 0x08` +5. `S1 = 0x10` +6. `S2 = 0x20` + +### 7.2 MUX 数据口规则 + +当 `MUX=1` 时,数据口应使用 MUX 帧。 + +重点规则: + +1. `DSTMASK=0x00` 表示系统控制帧 +2. 控制帧中的 AT 文本必须严格按手册要求结束 +3. 普通数据帧走业务转发路径,不应进入配置解析器 + +### 7.3 调试时重点检查什么 + +若怀疑 `MUX` 模式不工作,优先检查: + +1. `App/uart_trans.c` + - `uart_mux_try_extract_frame()` + - `uart_mux_encode_frame()` +2. `Core/Src/main.c` + - `App_RouteMuxUartTraffic()` + - `App_RouteRawUartTraffic()` + - `App_RouteTcpTraffic()` +3. `App/config.c` + - `config_build_response_frame()` + - `config_process_at_cmd()` + +推荐最小 MUX 联调顺序: + +1. 先在 `MUX=0` 下跑通原始透传 +2. 再切换 `MUX=1` +3. 先发一个控制帧,确认 `DSTMASK=0x00` 路径可通 +4. 再发一个单目标数据帧,例如只打到 `S1` +5. 最后验证多目标位图转发 + +--- + +## 8. TCP / UART / CH390 联调顺序 + +### 8.1 先做链路,再做业务 + +在 `CH390` 初始化、链路和 IRQ 未被证明稳定前,不要先调高层 TCP/UART 业务。 + +### 8.2 推荐顺序 + +建议按以下顺序推进: + +1. RTT 启动与 trap 状态正常 +2. CH390 启动日志完整 +3. 链路检测可信 +4. `TCP server` / `TCP client` 建链可信 +5. UART 原始透传可信 +6. 再切入 `MUX` 模式联调 + +### 8.3 最小 TCP 调试工具 + +当需要验证板子是否真的把 payload 发到主机时,优先使用仓库内置最小工具: 1. `tools/tcp_debug_server.py` - - 纯 Python 原始 TCP 服务端 - - 可直接打印连接、收包、文本视图和十六进制视图 - - 适合确认“板子到底有没有把原始 payload 发到 PC 的 8081 端口” + - 打印连接、收包、文本视图和十六进制视图 2. `tools/start_tcp_debug_server.ps1` - - PowerShell 包装脚本 - - 会先清理本机 `8081` 端口上的现有监听,再启动 Python 调试服务端 - - 适合避免 `ncat`、遗留 Python、VOFA 等进程抢占 `8081` + - 会先清理冲突监听,再启动 Python 服务端 -推荐使用方法: +推荐命令: ```powershell powershell -ExecutionPolicy Bypass -File ".\tools\start_tcp_debug_server.ps1" -Port 8081 -NoStdin ``` -如需回显模式: +如需回显: ```powershell powershell -ExecutionPolicy Bypass -File ".\tools\start_tcp_debug_server.ps1" -Port 8081 -Echo @@ -177,92 +380,117 @@ powershell -ExecutionPolicy Bypass -File ".\tools\start_tcp_debug_server.ps1" -P python .\tools\tcp_debug_server.py --host 0.0.0.0 --port 8081 --no-stdin ``` -使用该工具时,推荐调试顺序如下: +### 8.4 推荐验证方法 1. 先关闭 VOFA、`ncat` 和其它可能占用 `8081` 的进程 2. 启动 `start_tcp_debug_server.ps1` -3. 再让板子连接 `192.168.31.1:8081` -4. 然后从 PC 连接板子的 `192.168.31.100:8080`,发送一段明确文本,例如 `hello` -5. 观察 Python 工具是否打印: - - 客户端已连接 - - 收到的原始字节长度 - - 文本视图与十六进制视图 +3. 让板子连接主机 `TCP client` 目标端口 +4. 再从主机连接板子的 `TCP server` 端口发送固定测试文本 +5. 同时观察: + - Python 工具是否收到连接与 payload + - 板子 RTT 是否出现连接或错误信息 -如果板子 RTT 显示 `TCP client connected to ...:8081`,但 Python 工具没有任何连接提示,优先检查本机端口占用: +若板子 RTT 显示已连接,但主机工具无数据,优先检查本机端口占用而不是先改板端逻辑。 -```powershell -Get-NetTCPConnection -LocalPort 8081 -ErrorAction SilentlyContinue | -Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort,State,OwningProcess -``` +--- -若存在多个监听者,优先清理后再测,否则板子可能连接到错误的本机进程。 +## 9. 异常、卡死与假死排查 -### 5.5 P4:验证系统级联调 +### 9.1 看到 `TRAP:` 时怎么做 -最终再验证: +1. 先记录 RTT 中的 trap 标签 +2. 立刻用调试器查看当前 PC / LR / 调用栈 +3. 结合 `Core/Src/stm32f1xx_it.c` 中对应 handler 定位异常类型 -1. TCP Server 与 UART2 双向透传 -2. TCP Client 与 UART3 双向透传 -3. 配置保存、复位与 MAC 生效路径 -4. 长时间运行稳定性 +### 9.2 没有 `TRAP:` 但系统不工作时怎么做 -## 6. 推荐的现场观测点 +若没有 `TRAP:`,但系统表现异常,应优先区分以下情况: -### 6.1 软件观测点 +1. 主循环仍在跑,只是业务路径没反应 +2. 中断未到或链路未更新 +3. 发生了阻塞式等待或超时问题 +4. 上层工具接错端口或被错误进程抢占 -优先关注以下函数与状态: +### 9.3 历史上已经确认过的典型软件问题 -1. `BootDiag_ReportCh390()` -2. `ch390_runtime_probe_identity()` -3. `ch390_runtime_init()` -4. `ch390_runtime_poll()` -5. `ch390_runtime_check_link()` -6. `ch390_runtime_input()` -7. `ch390_runtime_output()` +以下问题在历史排查中已经出现过,应优先复核,不要重复踩坑: -### 6.2 硬件观测点 +1. PHY 访问无超时,导致永久卡死 +2. 刷新未初始化的 IWDG 句柄导致 HardFault +3. 在长耗时 SPI 路径中错误扩大临界区,导致看似“系统假死” +4. 在多个层次同时触达 CH390 / SPI,导致运行时边界混乱 +5. 配置口命令结束方式不对,导致误判为 parser 无响应 -优先关注 CH390 芯片脚侧: +--- -1. `RSTB` -2. `SCS` -3. `SCK` -4. `SDI/MOSI` -5. `SDO/MISO` -6. `INT` -7. `VDDK` -8. `AVDD33/VDDIO` -9. `AVDD33` -10. `XI/XO` - -## 7. 常见误区 +## 10. 常见误区 调试当前工程时,应避免以下误区: -1. 不要再直接沿用“CH390 恒为全 `0xFF`”这一过时结论 +1. 不要继续沿用“CH390 恒为全 `0xFF`”这一过时结论 2. 不要在 `main.c`、IRQ、netif 多处重新插入原始 CH390 访问 -3. 不要在没有芯片脚侧证据前,只凭 MCU 侧 GPIO 就认定 `RST/CS/SCK/MISO` 正常 -4. 不要在基础寄存器读写尚不可信时,直接调高层 `TCP/UART` 业务逻辑 -5. 不要把一次性的 bring-up 实验代码长期留在正式启动路径中 -6. 不要让 `VOFA`、`ncat`、自写 Python 服务端等多个进程同时监听 `8081`;否则板子可能连接到错误的本机进程,导致 RTT 显示 `connected` 但目标工具无数据 +3. 不要在没有芯片脚侧证据前,只凭 MCU 侧 GPIO 判断总线正常 +4. 不要在基础寄存器读写尚不可信时,直接调高层 `TCP/UART/MUX` 业务逻辑 +5. 不要把一次性 bring-up 实验代码长期留在正式路径中 +6. 不要让多个本机进程同时监听板子要连接的 TCP 端口 +7. 不要在尚未证明命令已真正进入 parser 之前,直接归因到 Flash、协议或网络层 -## 8. 当前推荐结论表达方式 +--- -当需要向项目成员同步当前状态时,推荐使用如下表述: +## 11. 推荐的现场记录模板 + +建议每次现场调试至少记录以下信息: + +1. 日期时间 +2. 板卡编号 +3. 固件产物路径 +4. 下载方式 +5. RTT 关键日志 +6. 串口发送内容 +7. TCP 调试工具输出 +8. 关键波形或电压量测点 +9. 结论 +10. 下一步动作 + +建议记录格式: + +```text +时间: +板卡: +固件: +下载方式: +操作步骤: +RTT输出: +串口/TCP现象: +硬件量测: +结论: +下一步: +``` + +--- + +## 12. 当前推荐的结论表达方式 + +若需要向项目成员同步当前状态,建议采用以下口径: 1. 当前工程软件架构已稳定在 `bare-metal + lwIP RAW + ch390_runtime 单一拥有者` -2. 当前 CH390D 已恢复基础寄存器读写,调试重点已转入初始化完整性、链路、中断与收发功能 -3. 硬件验证仍必须以 CH390 芯片脚侧波形与电源域量测为准 -4. 上层 `TCP/UART` 联调应建立在 CH390 初始化、链路与供电稳定三者都可信之后 -5. 本项目已确认过一次真实硬件根因:CH390D 供电滤波电容虚焊会直接表现为网络收发异常 +2. 当前调试重点已经从“CH390 是否完全无响应”转移到协议、链路和系统级联调 +3. 当前对外协议和配置模型应以 `MUX / NET / LINK[idx]` 为准 +4. `USART1` 配置口、`USART2/3` 数据口与 TCP 路由必须按最新代码路径调试,不应再参照历史 `IP/MASK/GW/PORT/RIP/RPORT` 公开接口模型 +5. 硬件验证仍必须以 CH390 芯片脚侧波形和供电域量测为准 -## 9. 建议配套文档 +--- -建议与本指南配套阅读: +## 13. 建议配套阅读 -1. `项目技术实现.md` -2. `CH390D_硬件检查指南.md` -3. `uart-ch390-debug-handoff.md` -4. `PCB/SCH_Schematic1_2026-03-26.pdf` -5. `tools/tcp_debug_server.py` -6. `tools/start_tcp_debug_server.ps1` +建议与本指导配套阅读: + +1. `AT固件使用手册.md` +2. `项目技术实现.md` +3. `项目需求说明.md` +4. `uart-ch390-debug-handoff.md` +5. `CH390_最终结论报告.md` +6. `build_keil.log` +7. `PCB/SCH_Schematic1_2026-03-26.pdf` +8. `tools/tcp_debug_server.py` +9. `tools/start_tcp_debug_server.ps1`