feat: 完成TCP2UART透传核心集成

集成CH390驱动、LwIP协议栈和FreeRTOS多任务透传框架,确保TCP Server/Client与UART链路按配置稳定联动。
This commit is contained in:
2026-03-30 11:39:40 +08:00
parent d5803ca7dd
commit 4996b451d9
235 changed files with 80607 additions and 27 deletions
+772
View File
@@ -0,0 +1,772 @@
/**
* @file config.c
* @brief 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 <stdio.h>
#include <stdlib.h>
#include <stddef.h>
/*---------------------------------------------------------------------------
* Private Definitions
*---------------------------------------------------------------------------*/
#define CONFIG_RX_BUFFER_SIZE 256
#define CONFIG_TX_BUFFER_SIZE 512
#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 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++;
}
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'))
{
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')
{
char c1 = *a++;
char c2 = *b++;
if (c1 >= 'a' && c1 <= 'z') c1 -= 32;
if (c2 >= 'a' && c2 <= 'z') c2 -= 32;
if (c1 != c2)
{
return false;
}
}
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];
config_ip_to_str(g_config.ip, ip_str);
config_ip_to_str(g_config.mask, mask_str);
config_ip_to_str(g_config.gw, gw_str);
config_ip_to_str(g_config.remote_ip, rip_str);
config_mac_to_str(g_config.mac, mac_str);
snprintf(response, max_len,
"=== TCP2UART Configuration ===\r\n"
"MAC: %s\r\n"
"DHCP: %s\r\n"
"IP: %s\r\n"
"MASK: %s\r\n"
"GW: %s\r\n"
"PORT: %u\r\n"
"RIP: %s\r\n"
"RPORT: %u\r\n"
"BAUD1: %lu\r\n"
"BAUD2: %lu\r\n"
"==============================\r\n",
mac_str,
g_config.dhcp_enable ? "Enabled" : "Disabled",
ip_str,
mask_str,
gw_str,
g_config.server_port,
rip_str,
g_config.remote_port,
g_config.uart2_baudrate,
g_config.uart3_baudrate
);
return AT_OK;
}
/*---------------------------------------------------------------------------
* Public Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize configuration module
*/
int config_init(void)
{
/* Load configuration from Flash */
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 ||
g_config.magic != CONFIG_MAGIC ||
g_config.version != CONFIG_VERSION ||
g_config.crc != config_calc_crc(&g_config))
{
/* Invalid or corrupted configuration, use defaults */
config_set_defaults();
return -1;
}
return 0;
}
/**
* @brief Save configuration to Flash
*/
int config_save(void)
{
/* Update magic and CRC before saving */
g_config.magic = CONFIG_MAGIC;
g_config.version = CONFIG_VERSION;
g_config.crc = config_calc_crc(&g_config);
return flash_param_write(&g_config, sizeof(device_config_t));
}
/**
* @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));
g_config.magic = CONFIG_MAGIC;
g_config.version = CONFIG_VERSION;
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;
g_config.uart2_stopbits = DEFAULT_UART_STOPBITS;
g_config.uart2_parity = DEFAULT_UART_PARITY;
g_config.uart3_databits = DEFAULT_UART_DATABITS;
g_config.uart3_stopbits = DEFAULT_UART_STOPBITS;
g_config.uart3_parity = DEFAULT_UART_PARITY;
g_config.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;
if (cmd == NULL || response == NULL || max_len == 0)
{
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';
trim_trailing(cmd_copy);
/* Skip leading whitespace */
const char *p = skip_whitespace(cmd_copy);
/* Check for AT+ prefix */
if (!starts_with(p, "AT+"))
{
if (equals_ignore_case(p, "AT"))
{
snprintf(response, max_len, "OK\r\n");
return AT_OK;
}
snprintf(response, max_len, "ERROR: Unknown command\r\n");
return AT_UNKNOWN_CMD;
}
/* Move past AT+ */
p += 3;
/* Find '=' separator if present */
value = strchr((char *)p, '=');
if (value != NULL)
{
*value = '\0'; /* Terminate command part */
value++; /* Point to 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);
}
else if (equals_ignore_case(cmd_name, "MASK") && value != NULL)
{
result = handle_mask(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "GW") && value != NULL)
{
result = handle_gw(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "PORT") && value != NULL)
{
result = handle_port(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "RIP") && value != NULL)
{
result = handle_rip(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "RPORT") && value != NULL)
{
result = handle_rport(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL)
{
result = handle_baud1(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL)
{
result = handle_baud2(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "MAC") && value != NULL)
{
result = handle_mac(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "DHCP") && value != NULL)
{
result = handle_dhcp(value, response, max_len);
}
else if (equals_ignore_case(cmd_name, "SAVE") && value == NULL)
{
result = handle_save(response, max_len);
}
else if (equals_ignore_case(cmd_name, "RESET") && value == NULL)
{
result = handle_reset(response, max_len);
}
else if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL)
{
result = handle_default(response, max_len);
}
else if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL)
{
result = handle_query(response, max_len);
}
else
{
snprintf(response, max_len, "ERROR: Unknown command\r\n");
result = AT_UNKNOWN_CMD;
}
return result;
}
/**
* @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]);
}
/**
* @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)
{
return -1;
}
if (a < 0 || a > 255 || b < 0 || b > 255 ||
c < 0 || c > 255 || d < 0 || d > 255)
{
return -1;
}
ip[0] = (uint8_t)a;
ip[1] = (uint8_t)b;
ip[2] = (uint8_t)c;
ip[3] = (uint8_t)d;
return 0;
}
/**
* @brief Format MAC address to string
*/
void config_mac_to_str(const uint8_t *mac, char *str)
{
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
/**
* @brief Parse MAC address from string
*/
int config_str_to_mac(const char *str, uint8_t *mac)
{
int a[6];
if (sscanf(str, "%x:%x:%x:%x:%x:%x",
&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6)
{
/* Try alternate format with dashes */
if (sscanf(str, "%x-%x-%x-%x-%x-%x",
&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6)
{
return -1;
}
}
for (int i = 0; i < 6; i++)
{
if (a[i] < 0 || a[i] > 255)
{
return -1;
}
mac[i] = (uint8_t)a[i];
}
return 0;
}
/**
* @brief UART1 IDLE interrupt handler
*/
void config_uart_idle_handler(void)
{
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(huart1.hdmarx);
uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter;
if (len > 0)
{
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)
{
char response[CONFIG_TX_BUFFER_SIZE];
char cmd_buffer[CONFIG_CMD_MAX_LEN];
at_result_t result;
(void)argument;
/* Initialize configuration */
config_init();
/* Start UART1 reception */
config_start_reception();
/* Send startup message */
snprintf(response, sizeof(response),
"\r\n=== TCP2UART v1.0 ===\r\n"
"Type AT+? for configuration\r\n\r\n");
HAL_UART_Transmit(&huart1, (uint8_t *)response, strlen(response), 1000);
while (1)
{
/* Wait for command */
if (g_rx_complete)
{
/* Copy command and null-terminate */
uint16_t len = g_rx_index;
if (len >= CONFIG_CMD_MAX_LEN)
{
len = CONFIG_CMD_MAX_LEN - 1;
}
memcpy(cmd_buffer, g_rx_buffer, len);
cmd_buffer[len] = '\0';
/* Reset reception state */
g_rx_complete = false;
g_rx_index = 0;
/* Process command */
result = config_process_at_cmd(cmd_buffer, response, sizeof(response));
/* Send response */
HAL_UART_Transmit(&huart1, (uint8_t *)response, strlen(response), 1000);
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));
}
}
+198
View File
@@ -0,0 +1,198 @@
/**
* @file config.h
* @brief AT command configuration module for TCP2UART
*
* Handles UART1 AT commands for network and serial port configuration.
*
* Supported AT commands:
* - AT+IP=192.168.1.100 Set device IP
* - AT+MASK=255.255.255.0 Set subnet mask
* - AT+GW=192.168.1.1 Set gateway
* - AT+PORT=8080 Set TCP Server listen port
* - AT+RIP=192.168.1.200 Set TCP Client remote IP
* - AT+RPORT=9000 Set TCP Client remote port
* - AT+BAUD1=115200 Set UART2 baudrate
* - AT+BAUD2=115200 Set UART3 baudrate
* - AT+MAC=00:11:22:33:44:55 Set MAC address
* - AT+DHCP=0/1 Enable/disable DHCP
* - AT+SAVE Save parameters to Flash
* - AT+RESET Reset device
* - AT+DEFAULT Restore factory defaults
* - AT+? Query current configuration
*/
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Configuration magic number "TCPU" */
#define CONFIG_MAGIC 0x54435055
/* Configuration version for compatibility */
#define CONFIG_VERSION 0x0001
/* Device configuration structure */
typedef struct {
uint32_t magic; /* Magic number for validation */
uint16_t version; /* Configuration version */
uint16_t reserved; /* Reserved for alignment */
/* Network settings */
uint8_t mac[6]; /* MAC address */
uint8_t dhcp_enable; /* DHCP enable flag */
uint8_t reserved2; /* Reserved for alignment */
uint8_t ip[4]; /* Device IP address */
uint8_t mask[4]; /* Subnet mask */
uint8_t gw[4]; /* Gateway */
/* TCP Server settings */
uint16_t server_port; /* Server listen port */
uint16_t reserved3; /* Reserved for alignment */
/* TCP Client settings */
uint8_t remote_ip[4]; /* Remote server IP */
uint16_t remote_port; /* Remote server port */
uint16_t reconnect_interval;/* Reconnect interval (ms) */
/* UART settings */
uint32_t uart2_baudrate; /* UART2 (Server) baudrate */
uint32_t uart3_baudrate; /* UART3 (Client) baudrate */
uint8_t uart2_databits; /* UART2 data bits */
uint8_t uart2_stopbits; /* UART2 stop bits */
uint8_t uart2_parity; /* UART2 parity */
uint8_t uart3_databits; /* UART3 data bits */
uint8_t uart3_stopbits; /* UART3 stop bits */
uint8_t uart3_parity; /* UART3 parity */
uint16_t reserved4; /* Reserved for alignment */
/* CRC32 checksum (must be last) */
uint32_t crc;
} device_config_t;
/* Default configuration values */
#define DEFAULT_IP {192, 168, 1, 100}
#define DEFAULT_MASK {255, 255, 255, 0}
#define DEFAULT_GW {192, 168, 1, 1}
#define DEFAULT_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
#define DEFAULT_SERVER_PORT 8080
#define DEFAULT_REMOTE_IP {192, 168, 1, 200}
#define DEFAULT_REMOTE_PORT 9000
#define DEFAULT_UART_BAUDRATE 115200
#define DEFAULT_UART_DATABITS 8
#define DEFAULT_UART_STOPBITS 1
#define DEFAULT_UART_PARITY 0
#define DEFAULT_DHCP_ENABLE 0
#define DEFAULT_RECONNECT_MS 3000
/* AT command result codes */
typedef enum {
AT_OK = 0,
AT_ERROR,
AT_INVALID_PARAM,
AT_UNKNOWN_CMD,
AT_SAVE_FAILED,
AT_NEED_REBOOT
} at_result_t;
/**
* @brief Initialize configuration module
* @return 0 on success, negative on error
*/
int config_init(void);
/**
* @brief Load configuration from Flash
* @return 0 on success, negative on error (defaults loaded)
*/
int config_load(void);
/**
* @brief Save configuration to Flash
* @return 0 on success, negative on error
*/
int config_save(void);
/**
* @brief Reset configuration to factory defaults
*/
void config_set_defaults(void);
/**
* @brief Get current configuration
* @return Pointer to current configuration (read-only)
*/
const device_config_t *config_get(void);
/**
* @brief Get mutable configuration for modification
* @return Pointer to configuration structure
*/
device_config_t *config_get_mutable(void);
/**
* @brief Process AT command received from UART1
* @param cmd Command string (null-terminated)
* @param response Response buffer
* @param max_len Maximum response length
* @return AT command result code
*/
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
/**
* @brief 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
*/
void config_uart_idle_handler(void);
/**
* @brief Start UART1 reception for configuration
*/
void config_start_reception(void);
/**
* @brief Format IP address to string
* @param ip IP address bytes
* @param str Output string buffer (min 16 bytes)
*/
void config_ip_to_str(const uint8_t *ip, char *str);
/**
* @brief Parse IP address from string
* @param str IP address string (e.g. "192.168.1.100")
* @param ip Output IP address bytes
* @return 0 on success, negative on error
*/
int config_str_to_ip(const char *str, uint8_t *ip);
/**
* @brief Format MAC address to string
* @param mac MAC address bytes
* @param str Output string buffer (min 18 bytes)
*/
void config_mac_to_str(const uint8_t *mac, char *str);
/**
* @brief Parse MAC address from string
* @param str MAC address string (e.g. "00:11:22:33:44:55")
* @param mac Output MAC address bytes
* @return 0 on success, negative on error
*/
int config_str_to_mac(const char *str, uint8_t *mac);
#ifdef __cplusplus
}
#endif
#endif /* __CONFIG_H__ */
+278
View File
@@ -0,0 +1,278 @@
/**
* @file flash_param.c
* @brief Flash parameter storage module implementation
*/
#include "flash_param.h"
#include "stm32f1xx_hal.h"
#include <string.h>
/*---------------------------------------------------------------------------
* Private Definitions
*---------------------------------------------------------------------------*/
/* CRC32 polynomial (IEEE 802.3) */
#define CRC32_POLYNOMIAL 0xEDB88320
/*---------------------------------------------------------------------------
* Private Variables
*---------------------------------------------------------------------------*/
/* CRC32 lookup table */
static uint32_t g_crc_table[256];
static bool g_crc_table_initialized = false;
/*---------------------------------------------------------------------------
* Private Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize CRC32 lookup table
*/
static void crc32_init_table(void)
{
uint32_t i, j, crc;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 0; j < 8; j++)
{
if (crc & 1)
{
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
}
else
{
crc >>= 1;
}
}
g_crc_table[i] = crc;
}
g_crc_table_initialized = true;
}
/**
* @brief Unlock Flash for writing
*/
static HAL_StatusTypeDef flash_unlock(void)
{
return HAL_FLASH_Unlock();
}
/**
* @brief Lock Flash after writing
*/
static void flash_lock(void)
{
HAL_FLASH_Lock();
}
/**
* @brief Erase Flash page
*/
static HAL_StatusTypeDef flash_erase_page(uint32_t page_addr)
{
FLASH_EraseInitTypeDef erase_init;
uint32_t page_error;
HAL_StatusTypeDef status;
erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
erase_init.PageAddress = page_addr;
erase_init.NbPages = 1;
status = HAL_FLASHEx_Erase(&erase_init, &page_error);
return status;
}
/**
* @brief Program Flash half-word (16-bit)
*/
static HAL_StatusTypeDef flash_program_halfword(uint32_t addr, uint16_t data)
{
return HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr, data);
}
/*---------------------------------------------------------------------------
* Public Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize Flash parameter storage
*/
int flash_param_init(void)
{
/* Initialize CRC table */
if (!g_crc_table_initialized)
{
crc32_init_table();
}
return 0;
}
/**
* @brief Read parameters from Flash
*/
int flash_param_read(void *data, uint32_t len)
{
if (data == NULL || len == 0)
{
return -1;
}
/* Check if length exceeds available space */
if (len > FLASH_PARAM_PAGE_SIZE)
{
return -1;
}
/* Direct memory read from Flash */
memcpy(data, (const void *)FLASH_PARAM_START_ADDR, len);
return 0;
}
/**
* @brief Write parameters to Flash
*/
int flash_param_write(const void *data, uint32_t len)
{
HAL_StatusTypeDef status;
uint32_t addr;
const uint8_t *src;
uint16_t halfword;
uint32_t i;
if (data == NULL || len == 0)
{
return -1;
}
/* Check if length exceeds available space */
if (len > FLASH_PARAM_PAGE_SIZE)
{
return -1;
}
/* Unlock Flash */
status = flash_unlock();
if (status != HAL_OK)
{
return -1;
}
/* Erase the page */
status = flash_erase_page(FLASH_PARAM_START_ADDR);
if (status != HAL_OK)
{
flash_lock();
return -1;
}
/* Program Flash (half-word at a time for STM32F1) */
addr = FLASH_PARAM_START_ADDR;
src = (const uint8_t *)data;
for (i = 0; i < len; i += 2)
{
/* Build half-word (little-endian) */
halfword = src[i];
if (i + 1 < len)
{
halfword |= ((uint16_t)src[i + 1]) << 8;
}
else
{
halfword |= 0xFF00; /* Pad with 0xFF */
}
status = flash_program_halfword(addr, halfword);
if (status != HAL_OK)
{
flash_lock();
return -1;
}
addr += 2;
}
/* Lock Flash */
flash_lock();
/* Verify write */
if (memcmp((const void *)FLASH_PARAM_START_ADDR, data, len) != 0)
{
return -1;
}
return 0;
}
/**
* @brief Erase parameter storage area
*/
int flash_param_erase(void)
{
HAL_StatusTypeDef status;
/* Unlock Flash */
status = flash_unlock();
if (status != HAL_OK)
{
return -1;
}
/* Erase the page */
status = flash_erase_page(FLASH_PARAM_START_ADDR);
/* Lock Flash */
flash_lock();
return (status == HAL_OK) ? 0 : -1;
}
/**
* @brief Calculate CRC32
*/
uint32_t flash_param_crc32(const void *data, uint32_t len)
{
const uint8_t *p = (const uint8_t *)data;
uint32_t crc = 0xFFFFFFFF;
uint32_t i;
/* Initialize table if needed */
if (!g_crc_table_initialized)
{
crc32_init_table();
}
for (i = 0; i < len; i++)
{
crc = g_crc_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFF;
}
/**
* @brief Verify parameter storage integrity
*/
int flash_param_verify(void)
{
uint32_t magic;
/* Read magic number */
memcpy(&magic, (const void *)FLASH_PARAM_START_ADDR, sizeof(magic));
/* Check if Flash is erased (all 0xFF) */
if (magic == 0xFFFFFFFF)
{
return -1; /* Empty/erased */
}
return 0;
}
+75
View File
@@ -0,0 +1,75 @@
/**
* @file flash_param.h
* @brief Flash parameter storage module for TCP2UART
*
* Stores configuration parameters in STM32F103 internal Flash.
* Uses the last page of Flash (1KB) for parameter storage.
*/
#ifndef __FLASH_PARAM_H__
#define __FLASH_PARAM_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Flash configuration for STM32F103R8 (64KB Flash) */
#define FLASH_PARAM_PAGE_SIZE 1024 /* 1KB per page for STM32F103 */
#define FLASH_PARAM_START_ADDR 0x0800FC00 /* Last 1KB of 64KB Flash */
#define FLASH_PARAM_END_ADDR 0x08010000 /* End of Flash */
/* For STM32F103RC (256KB), use: 0x0803FC00 */
/* For STM32F103RB (128KB), use: 0x0801FC00 */
/**
* @brief Initialize Flash parameter storage
* @return 0 on success, negative on error
*/
int flash_param_init(void);
/**
* @brief Read parameters from Flash
* @param data Output buffer
* @param len Length to read
* @return 0 on success, negative on error
*/
int flash_param_read(void *data, uint32_t len);
/**
* @brief Write parameters to Flash
* @param data Data to write
* @param len Length to write
* @return 0 on success, negative on error
*
* Note: This function will erase the Flash page before writing.
*/
int flash_param_write(const void *data, uint32_t len);
/**
* @brief Erase parameter storage area
* @return 0 on success, negative on error
*/
int flash_param_erase(void);
/**
* @brief Calculate CRC32 for data
* @param data Data buffer
* @param len Data length
* @return CRC32 value
*/
uint32_t flash_param_crc32(const void *data, uint32_t len);
/**
* @brief Verify parameter storage integrity
* @return 0 if valid, negative if invalid or corrupted
*/
int flash_param_verify(void);
#ifdef __cplusplus
}
#endif
#endif /* __FLASH_PARAM_H__ */
+431
View File
@@ -0,0 +1,431 @@
/**
* @file tcp_client.c
* @brief TCP Client module implementation for transparent transmission with UART3
*/
#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 <string.h>
/*---------------------------------------------------------------------------
* Private Variables
*---------------------------------------------------------------------------*/
/* 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
};
/* 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)
{
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;
}
/**
* @brief Internal connect function
*/
static int tcp_client_do_connect(void)
{
struct sockaddr_in server_addr;
int ret;
if (client_socket >= 0)
{
/* Already connected */
return 0;
}
client_status.state = TCP_CLIENT_STATE_CONNECTING;
/* Create socket */
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0)
{
client_status.state = TCP_CLIENT_STATE_ERROR;
client_status.errors++;
return -1;
}
/* Prepare server address */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(client_config.server_port);
server_addr.sin_addr.s_addr = ((uint32_t)client_config.server_ip[0]) |
((uint32_t)client_config.server_ip[1] << 8) |
((uint32_t)client_config.server_ip[2] << 16) |
((uint32_t)client_config.server_ip[3] << 24);
/* Connect to server */
ret = connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret < 0)
{
close(client_socket);
client_socket = -1;
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
client_status.errors++;
return -1;
}
client_status.state = TCP_CLIENT_STATE_CONNECTED;
return 0;
}
/*---------------------------------------------------------------------------
* Public Functions
*---------------------------------------------------------------------------*/
/**
* @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));
}
/* Create stream buffers */
if (rx_stream == NULL)
{
rx_stream = xStreamBufferCreate(TCP_CLIENT_RX_BUFFER_SIZE, 1);
if (rx_stream == NULL)
{
return -1;
}
}
if (tx_stream == NULL)
{
tx_stream = xStreamBufferCreate(TCP_CLIENT_TX_BUFFER_SIZE, 1);
if (tx_stream == NULL)
{
return -1;
}
}
client_status.state = TCP_CLIENT_STATE_IDLE;
return 0;
}
/**
* @brief Connect to remote server
*/
int tcp_client_connect(void)
{
return tcp_client_do_connect();
}
/**
* @brief Disconnect from server
*/
int tcp_client_disconnect(void)
{
if (client_socket >= 0)
{
close(client_socket);
client_socket = -1;
}
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
return 0;
}
/**
* @brief Send data to server
*/
int tcp_client_send(const uint8_t *data, uint16_t len)
{
int sent;
if (client_socket < 0)
{
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++;
}
return sent;
}
/**
* @brief Receive data from server
*/
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
{
int received;
struct timeval tv;
if (client_socket < 0)
{
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;
}
return received;
}
/**
* @brief Check if connected to server
*/
bool tcp_client_is_connected(void)
{
return (client_socket >= 0);
}
/**
* @brief Update server configuration
*/
int tcp_client_set_server(const uint8_t *ip, uint16_t port)
{
if (ip == NULL)
{
return -1;
}
/* Disconnect if connected */
if (client_socket >= 0)
{
tcp_client_disconnect();
}
memcpy(client_config.server_ip, ip, 4);
client_config.server_port = port;
return 0;
}
/**
* @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));
}
}
/**
* @brief Get RX StreamBuffer handle
*/
void *tcp_client_get_rx_stream(void)
{
return rx_stream;
}
/**
* @brief Get TX StreamBuffer handle
*/
void *tcp_client_get_tx_stream(void)
{
return tx_stream;
}
/**
* @brief TCP Client task
*/
void tcp_client_task(void *argument)
{
const device_config_t *cfg;
tcp_client_config_t task_cfg;
uint8_t rx_buffer[256];
uint8_t tx_buffer[256];
int received;
size_t tx_len;
fd_set read_fds;
struct timeval tv;
uint32_t reconnect_timer = 0;
(void)argument;
/* Initialize client */
task_cfg.server_ip[0] = 192;
task_cfg.server_ip[1] = 168;
task_cfg.server_ip[2] = 1;
task_cfg.server_ip[3] = 100;
task_cfg.server_port = TCP_CLIENT_DEFAULT_PORT;
task_cfg.auto_reconnect = true;
task_cfg.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
cfg = config_get();
if (cfg != NULL)
{
memcpy(task_cfg.server_ip, cfg->remote_ip, sizeof(task_cfg.server_ip));
if (cfg->remote_port > 0)
{
task_cfg.server_port = cfg->remote_port;
}
if (cfg->reconnect_interval > 0)
{
task_cfg.reconnect_interval_ms = cfg->reconnect_interval;
}
}
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));
}
}
+132
View File
@@ -0,0 +1,132 @@
/**
* @file tcp_client.h
* @brief TCP Client module for transparent transmission with UART3
*/
#ifndef __TCP_CLIENT_H__
#define __TCP_CLIENT_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Default TCP Client settings */
#define TCP_CLIENT_DEFAULT_PORT 8081
#define TCP_CLIENT_DEFAULT_SERVER "192.168.1.100"
/* Reconnect settings */
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000
#define TCP_CLIENT_MAX_RECONNECT_TRIES 0 /* 0 = infinite */
/* Buffer sizes */
#define TCP_CLIENT_RX_BUFFER_SIZE 1024
#define TCP_CLIENT_TX_BUFFER_SIZE 1024
/* TCP Client state */
typedef enum {
TCP_CLIENT_STATE_IDLE,
TCP_CLIENT_STATE_CONNECTING,
TCP_CLIENT_STATE_CONNECTED,
TCP_CLIENT_STATE_DISCONNECTED,
TCP_CLIENT_STATE_ERROR
} tcp_client_state_t;
/* TCP Client configuration */
typedef struct {
uint8_t server_ip[4]; /* Server IP address */
uint16_t server_port; /* Server port */
bool auto_reconnect; /* Auto reconnect on disconnect */
uint16_t reconnect_interval_ms; /* Reconnect interval */
} tcp_client_config_t;
/* TCP Client status */
typedef struct {
tcp_client_state_t state;
uint32_t rx_bytes;
uint32_t tx_bytes;
uint32_t reconnect_count;
uint32_t errors;
} tcp_client_status_t;
/**
* @brief Initialize TCP Client module
* @param config Client configuration
* @return 0 on success, negative on error
*/
int tcp_client_init(const tcp_client_config_t *config);
/**
* @brief Connect to remote server
* @return 0 on success, negative on error
*/
int tcp_client_connect(void);
/**
* @brief Disconnect from server
* @return 0 on success, negative on error
*/
int tcp_client_disconnect(void);
/**
* @brief Send data to server
* @param data Data buffer
* @param len Data length
* @return Number of bytes sent, negative on error
*/
int tcp_client_send(const uint8_t *data, uint16_t len);
/**
* @brief Receive data from server
* @param data Data buffer
* @param max_len Maximum length to receive
* @param timeout_ms Timeout in milliseconds (0 = non-blocking)
* @return Number of bytes received, 0 if no data, negative on error
*/
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms);
/**
* @brief Check if connected to server
* @return true if connected
*/
bool tcp_client_is_connected(void);
/**
* @brief Update server configuration (for AT command)
* @param ip Server IP address (4 bytes)
* @param port Server port
* @return 0 on success, negative on error
*/
int tcp_client_set_server(const uint8_t *ip, uint16_t port);
/**
* @brief Get TCP Client status
* @param status Pointer to status structure
*/
void tcp_client_get_status(tcp_client_status_t *status);
/**
* @brief Get TCP Client RX StreamBuffer handle for UART integration
* @return StreamBuffer handle for receiving data from TCP
*/
void *tcp_client_get_rx_stream(void);
/**
* @brief Get TCP Client TX StreamBuffer handle for UART integration
* @return StreamBuffer handle for sending data to TCP
*/
void *tcp_client_get_tx_stream(void);
/**
* @brief TCP Client task function (for FreeRTOS)
* @param argument Task argument (unused)
*/
void tcp_client_task(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* __TCP_CLIENT_H__ */
+405
View File
@@ -0,0 +1,405 @@
/**
* @file tcp_server.c
* @brief TCP Server module implementation for transparent transmission with UART2
*/
#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 <string.h>
/*---------------------------------------------------------------------------
* Private Variables
*---------------------------------------------------------------------------*/
/* Server configuration */
static tcp_server_config_t server_config = {
.port = TCP_SERVER_DEFAULT_PORT,
.auto_reconnect = true
};
/* 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)
{
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;
}
/**
* @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));
}
/* Create stream buffers */
if (rx_stream == NULL)
{
rx_stream = xStreamBufferCreate(TCP_SERVER_RX_BUFFER_SIZE, 1);
if (rx_stream == NULL)
{
return -1;
}
}
if (tx_stream == NULL)
{
tx_stream = xStreamBufferCreate(TCP_SERVER_TX_BUFFER_SIZE, 1);
if (tx_stream == NULL)
{
return -1;
}
}
server_status.state = TCP_SERVER_STATE_IDLE;
return 0;
}
/**
* @brief Start TCP Server
*/
int tcp_server_start(void)
{
struct sockaddr_in server_addr;
int opt = 1;
if (listen_socket >= 0)
{
/* Already started */
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++;
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++;
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++;
return -1;
}
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 (listen_socket >= 0)
{
close(listen_socket);
listen_socket = -1;
}
server_status.state = TCP_SERVER_STATE_IDLE;
return 0;
}
/**
* @brief Send data to connected client
*/
int tcp_server_send(const uint8_t *data, uint16_t len)
{
int sent;
if (client_socket < 0)
{
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++;
}
return sent;
}
/**
* @brief Receive data from connected client
*/
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
{
int received;
struct timeval tv;
if (client_socket < 0)
{
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 */
}
return received;
}
/**
* @brief Check if client is connected
*/
bool tcp_server_is_connected(void)
{
return (client_socket >= 0);
}
/**
* @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));
}
}
/**
* @brief Get RX StreamBuffer handle
*/
void *tcp_server_get_rx_stream(void)
{
return rx_stream;
}
/**
* @brief Get TX StreamBuffer handle
*/
void *tcp_server_get_tx_stream(void)
{
return tx_stream;
}
/**
* @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));
}
}
+119
View File
@@ -0,0 +1,119 @@
/**
* @file tcp_server.h
* @brief TCP Server module for transparent transmission with UART2
*/
#ifndef __TCP_SERVER_H__
#define __TCP_SERVER_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Default TCP Server port */
#define TCP_SERVER_DEFAULT_PORT 8080
/* Maximum number of simultaneous connections */
#define TCP_SERVER_MAX_CONNECTIONS 1
/* Buffer sizes */
#define TCP_SERVER_RX_BUFFER_SIZE 1024
#define TCP_SERVER_TX_BUFFER_SIZE 1024
/* TCP Server state */
typedef enum {
TCP_SERVER_STATE_IDLE,
TCP_SERVER_STATE_LISTENING,
TCP_SERVER_STATE_CONNECTED,
TCP_SERVER_STATE_ERROR
} tcp_server_state_t;
/* TCP Server configuration */
typedef struct {
uint16_t port;
bool auto_reconnect;
} tcp_server_config_t;
/* TCP Server status */
typedef struct {
tcp_server_state_t state;
uint32_t rx_bytes;
uint32_t tx_bytes;
uint32_t connections;
uint32_t errors;
} tcp_server_status_t;
/**
* @brief Initialize TCP Server module
* @param config Server configuration
* @return 0 on success, negative on error
*/
int tcp_server_init(const tcp_server_config_t *config);
/**
* @brief Start TCP Server (begin listening)
* @return 0 on success, negative on error
*/
int tcp_server_start(void);
/**
* @brief Stop TCP Server
* @return 0 on success, negative on error
*/
int tcp_server_stop(void);
/**
* @brief Send data to connected client
* @param data Data buffer
* @param len Data length
* @return Number of bytes sent, negative on error
*/
int tcp_server_send(const uint8_t *data, uint16_t len);
/**
* @brief Receive data from connected client
* @param data Data buffer
* @param max_len Maximum length to receive
* @param timeout_ms Timeout in milliseconds (0 = non-blocking)
* @return Number of bytes received, 0 if no data, negative on error
*/
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms);
/**
* @brief Check if client is connected
* @return true if connected
*/
bool tcp_server_is_connected(void);
/**
* @brief Get TCP Server status
* @param status Pointer to status structure
*/
void tcp_server_get_status(tcp_server_status_t *status);
/**
* @brief Get TCP Server StreamBuffer handle for UART integration
* @return StreamBuffer handle for receiving data from TCP
*/
void *tcp_server_get_rx_stream(void);
/**
* @brief Get TCP Server TX StreamBuffer handle for UART integration
* @return StreamBuffer handle for sending data to TCP
*/
void *tcp_server_get_tx_stream(void);
/**
* @brief TCP Server task function (for FreeRTOS)
* @param argument Task argument (unused)
*/
void tcp_server_task(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* __TCP_SERVER_H__ */
+528
View File
@@ -0,0 +1,528 @@
/**
* @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.
*/
#include "uart_trans.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.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 */
bool initialized;
bool running;
} uart_channel_ctx_t;
/*---------------------------------------------------------------------------
* Private Variables
*---------------------------------------------------------------------------*/
static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX];
/*---------------------------------------------------------------------------
* Private Functions
*---------------------------------------------------------------------------*/
/**
* @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)
{
return -1;
}
/* Stop UART if running */
if (ctx->running)
{
HAL_UART_DMAStop(huart);
}
/* Update UART parameters */
huart->Init.BaudRate = ctx->config.baudrate;
/* 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)
{
case 1:
huart->Init.Parity = UART_PARITY_ODD;
break;
case 2:
huart->Init.Parity = UART_PARITY_EVEN;
break;
default:
huart->Init.Parity = UART_PARITY_NONE;
break;
}
/* Reinitialize UART */
if (HAL_UART_Init(huart) != HAL_OK)
{
return -1;
}
return 0;
}
static void process_rx_data_from_isr(uart_channel_t channel, uint16_t end_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;
}
if (end > UART_RX_DMA_BUFFER_SIZE)
{
end = UART_RX_DMA_BUFFER_SIZE;
}
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
*---------------------------------------------------------------------------*/
/**
* @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;
/* Initialize Client channel (UART3) */
memset(&g_channels[UART_CHANNEL_CLIENT], 0, sizeof(uart_channel_ctx_t));
g_channels[UART_CHANNEL_CLIENT].huart = &huart3;
g_channels[UART_CHANNEL_CLIENT].config.baudrate = UART_DEFAULT_BAUDRATE;
g_channels[UART_CHANNEL_CLIENT].config.data_bits = UART_DEFAULT_DATA_BITS;
g_channels[UART_CHANNEL_CLIENT].config.stop_bits = UART_DEFAULT_STOP_BITS;
g_channels[UART_CHANNEL_CLIENT].config.parity = UART_DEFAULT_PARITY;
g_channels[UART_CHANNEL_CLIENT].initialized = true;
return 0;
}
/**
* @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)
{
return -1;
}
uart_channel_ctx_t *ctx = &g_channels[channel];
memcpy(&ctx->config, config, sizeof(uart_config_t));
/* Apply configuration if already initialized */
if (ctx->initialized)
{
return apply_uart_config(channel);
}
return 0;
}
/**
* @brief Start UART reception
*/
int uart_trans_start(uart_channel_t channel)
{
if (channel >= UART_CHANNEL_MAX)
{
return -1;
}
uart_channel_ctx_t *ctx = &g_channels[channel];
if (!ctx->initialized || ctx->huart == NULL)
{
return -1;
}
/* Reset read index */
ctx->rx_read_index = 0;
ctx->tx_busy = false;
/* Enable IDLE interrupt */
__HAL_UART_ENABLE_IT(ctx->huart, UART_IT_IDLE);
/* Start DMA reception (circular mode) */
HAL_UART_Receive_DMA(ctx->huart, ctx->rx_dma_buffer, UART_RX_DMA_BUFFER_SIZE);
ctx->running = true;
return 0;
}
/**
* @brief Stop UART reception
*/
int uart_trans_stop(uart_channel_t channel)
{
if (channel >= UART_CHANNEL_MAX)
{
return -1;
}
uart_channel_ctx_t *ctx = &g_channels[channel];
if (ctx->huart == NULL)
{
return -1;
}
/* Disable IDLE interrupt */
__HAL_UART_DISABLE_IT(ctx->huart, UART_IT_IDLE);
/* Stop DMA */
HAL_UART_DMAStop(ctx->huart);
ctx->running = false;
return 0;
}
/**
* @brief Set StreamBuffer handles
*/
void uart_trans_set_streams(uart_channel_t channel,
void *rx_stream,
void *tx_stream)
{
if (channel >= UART_CHANNEL_MAX)
{
return;
}
g_channels[channel].rx_stream = (StreamBufferHandle_t)rx_stream;
g_channels[channel].tx_stream = (StreamBufferHandle_t)tx_stream;
}
/**
* @brief Get UART statistics
*/
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
{
if (channel >= UART_CHANNEL_MAX || stats == NULL)
{
return;
}
memcpy(stats, &g_channels[channel].stats, sizeof(uart_stats_t));
}
/**
* @brief Reset UART statistics
*/
void uart_trans_reset_stats(uart_channel_t channel)
{
if (channel >= UART_CHANNEL_MAX)
{
return;
}
memset(&g_channels[channel].stats, 0, sizeof(uart_stats_t));
}
/**
* @brief UART IDLE interrupt handler
*/
void uart_trans_idle_handler(uart_channel_t channel)
{
if (channel >= UART_CHANNEL_MAX)
{
return;
}
uart_channel_ctx_t *ctx = &g_channels[channel];
if (!ctx->running || ctx->huart == NULL)
{
return;
}
/* Get current DMA position */
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx);
uint16_t current_pos = UART_RX_DMA_BUFFER_SIZE - dma_counter;
/* Process received data */
if (current_pos != ctx->rx_read_index)
{
process_rx_data_from_isr(channel, current_pos);
}
}
void uart_trans_rx_half_cplt_handler(uart_channel_t channel)
{
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);
}
}
/**
* @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)
{
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);
}
}
/**
* @brief UART DMA TX complete callback
*/
void uart_trans_tx_cplt_handler(uart_channel_t channel)
{
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));
}
}
}
+151
View File
@@ -0,0 +1,151 @@
/**
* @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
*/
#ifndef __UART_TRANS_H__
#define __UART_TRANS_H__
#include <stdint.h>
#include <stdbool.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_MAX
} uart_channel_t;
/* DMA buffer sizes */
#define UART_RX_DMA_BUFFER_SIZE 256
#define UART_TX_DMA_BUFFER_SIZE 256
/* 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 */
} 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
/* UART statistics */
typedef struct {
uint32_t rx_bytes;
uint32_t tx_bytes;
uint32_t rx_packets;
uint32_t tx_packets;
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_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__ */