refactor: 完成R8裸机lwIP移植并更新文档

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