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