4996b451d9
集成CH390驱动、LwIP协议栈和FreeRTOS多任务透传框架,确保TCP Server/Client与UART链路按配置稳定联动。
529 lines
14 KiB
C
529 lines
14 KiB
C
/**
|
|
* @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));
|
|
}
|
|
}
|
|
}
|