feat: 保存已验证的CH390网络打通基线
This commit is contained in:
+402
-473
@@ -1,528 +1,457 @@
|
||||
/**
|
||||
* @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
|
||||
*---------------------------------------------------------------------------*/
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "usart.h"
|
||||
|
||||
#include "app_runtime.h"
|
||||
#include "config.h"
|
||||
#include "debug_log.h"
|
||||
#include "route_msg.h"
|
||||
|
||||
#define UART_MUX_SYNC 0x7Eu
|
||||
#define UART_MUX_TAIL 0x7Fu
|
||||
|
||||
#define UART_NOTIFY_RX_U0 (1UL << 0)
|
||||
#define UART_NOTIFY_RX_U1 (1UL << 1)
|
||||
#define UART_NOTIFY_TX_U0 (1UL << 8)
|
||||
#define UART_NOTIFY_TX_U1 (1UL << 9)
|
||||
|
||||
/* 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_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 uint8_t tx_busy;
|
||||
} 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)
|
||||
static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size)
|
||||
{
|
||||
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;
|
||||
return (head >= tail) ? (head - tail) : (size - tail + head);
|
||||
}
|
||||
|
||||
static void process_rx_data_from_isr(uart_channel_t channel, uint16_t end_index)
|
||||
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 process_rx_snapshot(uart_channel_t channel)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
uint16_t start = ctx->rx_read_index;
|
||||
uint16_t end = end_index;
|
||||
uint16_t dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx));
|
||||
|
||||
if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) {
|
||||
dma_write_index = 0u;
|
||||
}
|
||||
|
||||
while (ctx->rx_dma_read_index != dma_write_index) {
|
||||
uint16_t next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE);
|
||||
if (next_head == ctx->rx_tail) {
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void kick_tx(uart_channel_t channel)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
uint16_t available;
|
||||
uint16_t chunk;
|
||||
uint16_t i;
|
||||
|
||||
if (ctx->tx_busy != 0u) {
|
||||
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 (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 = 1u;
|
||||
if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) {
|
||||
ctx->tx_busy = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t uart_ring_available(uart_channel_t channel)
|
||||
{
|
||||
return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
static uint16_t uart_ring_read(uart_channel_t channel, uint8_t *data, uint16_t max_len)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
uint16_t copied = 0u;
|
||||
|
||||
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);
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
static bool uart_ring_peek_byte(uart_channel_t channel, uint16_t offset, uint8_t *data)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
uint16_t available = ring_used(ctx->rx_head, ctx->rx_tail, UART_RX_RING_BUFFER_SIZE);
|
||||
|
||||
if (data == NULL || offset >= available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = ctx->rx_ring[(ctx->rx_tail + offset) % UART_RX_RING_BUFFER_SIZE];
|
||||
return true;
|
||||
}
|
||||
|
||||
static void uart_ring_drop(uart_channel_t channel, uint16_t len)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
|
||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + len) % UART_RX_RING_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
static void uart_route_raw_channel(uart_channel_t channel)
|
||||
{
|
||||
const device_config_t *cfg = config_get();
|
||||
uint8_t buffer[ROUTE_MSG_MAX_PAYLOAD];
|
||||
uint16_t len;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
uint8_t uart_endpoint = (channel == UART_CHANNEL_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
|
||||
uint32_t i;
|
||||
|
||||
if (start >= UART_RX_DMA_BUFFER_SIZE)
|
||||
{
|
||||
start = 0;
|
||||
len = uart_ring_read(channel, buffer, sizeof(buffer));
|
||||
if (len == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
if (cfg->links[i].enabled == 0u || cfg->links[i].uart != ((channel == UART_CHANNEL_U1) ? LINK_UART_U1 : LINK_UART_U0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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++;
|
||||
(void)route_send(xLinkTxQueues[i],
|
||||
uart_endpoint,
|
||||
config_link_index_to_endpoint((uint8_t)i),
|
||||
(channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2,
|
||||
buffer,
|
||||
len,
|
||||
pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
ctx->rx_read_index = (end == UART_RX_DMA_BUFFER_SIZE) ? 0 : end;
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Public Functions
|
||||
*---------------------------------------------------------------------------*/
|
||||
static void uart_send_tcp_msg_to_uarts(route_msg_t *msg)
|
||||
{
|
||||
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
|
||||
uint16_t frame_len = 0u;
|
||||
|
||||
if ((msg->dst_mask & ENDPOINT_UART2) != 0u) {
|
||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||
if (uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, msg->data, msg->len, frame, &frame_len, sizeof(frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U0, frame, frame_len);
|
||||
}
|
||||
} else {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U0, msg->data, msg->len);
|
||||
}
|
||||
}
|
||||
|
||||
if ((msg->dst_mask & ENDPOINT_UART3) != 0u) {
|
||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
||||
if (uart_mux_encode_frame(msg->src_id, ENDPOINT_UART3, msg->data, msg->len, frame, &frame_len, sizeof(frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len);
|
||||
}
|
||||
} else {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, msg->data, msg->len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_route_mux_frame(uart_channel_t source_channel, const uart_mux_frame_t *frame)
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t source_conn = (source_channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2;
|
||||
uint8_t out_frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
|
||||
uint16_t out_frame_len = 0u;
|
||||
|
||||
if (frame->dst_mask == 0u) {
|
||||
(void)route_send(xConfigQueue,
|
||||
frame->src_id,
|
||||
0u,
|
||||
source_conn,
|
||||
frame->payload,
|
||||
frame->payload_len,
|
||||
pdMS_TO_TICKS(10));
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
uint8_t endpoint = config_link_index_to_endpoint((uint8_t)i);
|
||||
if ((frame->dst_mask & endpoint) != 0u) {
|
||||
(void)route_send(xLinkTxQueues[i], frame->src_id, endpoint, source_conn, frame->payload, frame->payload_len, pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame->dst_mask & ENDPOINT_UART2) != 0u && source_channel != UART_CHANNEL_U0) {
|
||||
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART2, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U0, out_frame, out_frame_len);
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame->dst_mask & ENDPOINT_UART3) != 0u && source_channel != UART_CHANNEL_U1) {
|
||||
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART3, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
|
||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, out_frame, out_frame_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
memset(g_channels, 0, sizeof(g_channels));
|
||||
g_channels[UART_CHANNEL_U0].huart = &huart2;
|
||||
g_channels[UART_CHANNEL_U1].huart = &huart3;
|
||||
debug_log_printf("[UART] init u0=%p u1=%p\r\n", (void *)g_channels[UART_CHANNEL_U0].huart, (void *)g_channels[UART_CHANNEL_U1].huart);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure UART channel parameters
|
||||
*/
|
||||
int uart_trans_config(uart_channel_t channel, const uart_config_t *config)
|
||||
int uart_trans_config(uint8_t uart_index, uint32_t baudrate)
|
||||
{
|
||||
if (channel >= UART_CHANNEL_MAX || config == NULL)
|
||||
{
|
||||
return -1;
|
||||
UART_HandleTypeDef *huart = (uart_index == LINK_UART_U1) ? &huart3 : &huart2;
|
||||
huart->Init.BaudRate = baudrate;
|
||||
return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
int uart_trans_start_all(void)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < UART_CHANNEL_MAX; ++i) {
|
||||
if (g_channels[i].huart == NULL) {
|
||||
debug_log_printf("[UART] start fail null handle ch=%lu\r\n", (unsigned long)i);
|
||||
return -1;
|
||||
}
|
||||
g_channels[i].rx_dma_read_index = 0u;
|
||||
g_channels[i].rx_head = 0u;
|
||||
g_channels[i].rx_tail = 0u;
|
||||
g_channels[i].tx_head = 0u;
|
||||
g_channels[i].tx_tail = 0u;
|
||||
g_channels[i].tx_dma_len = 0u;
|
||||
g_channels[i].tx_busy = 0u;
|
||||
__HAL_UART_ENABLE_IT(g_channels[i].huart, UART_IT_IDLE);
|
||||
if (HAL_UART_Receive_DMA(g_channels[i].huart, g_channels[i].rx_dma_buffer, UART_RX_DMA_BUFFER_SIZE) != HAL_OK) {
|
||||
debug_log_printf("[UART] dma start fail ch=%lu\r\n", (unsigned long)i);
|
||||
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);
|
||||
}
|
||||
|
||||
debug_log_write("[UART] rx dma started\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start UART reception
|
||||
*/
|
||||
int uart_trans_start(uart_channel_t channel)
|
||||
bool uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (channel >= UART_CHANNEL_MAX)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
|
||||
if (!ctx->initialized || ctx->huart == NULL)
|
||||
{
|
||||
return -1;
|
||||
uint16_t written = 0u;
|
||||
|
||||
if (data == NULL || len == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
kick_tx(channel);
|
||||
return (written == len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop UART reception
|
||||
*/
|
||||
int uart_trans_stop(uart_channel_t channel)
|
||||
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken)
|
||||
{
|
||||
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);
|
||||
uint32_t notify = (channel == UART_CHANNEL_U1) ? UART_NOTIFY_RX_U1 : UART_NOTIFY_RX_U0;
|
||||
if (xUartRxTaskHandle != NULL) {
|
||||
xTaskNotifyFromISR(xUartRxTaskHandle, notify, eSetBits, xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
uint32_t notify = (channel == UART_CHANNEL_U1) ? UART_NOTIFY_TX_U1 : UART_NOTIFY_TX_U0;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
if (xUartRxTaskHandle != NULL) {
|
||||
xTaskNotifyFromISR(xUartRxTaskHandle, notify, eSetBits, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Client transparent transmission task (UART3 <-> TCP Client)
|
||||
*/
|
||||
void uart_client_trans_task(void *argument)
|
||||
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame)
|
||||
{
|
||||
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));
|
||||
uint16_t available;
|
||||
uint16_t payload_len;
|
||||
uint8_t sync_byte;
|
||||
uint16_t i;
|
||||
|
||||
if (frame == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
available = uart_ring_available(channel);
|
||||
if (available == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uart_ring_peek_byte(channel, 0u, &sync_byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sync_byte != UART_MUX_SYNC) {
|
||||
uart_ring_drop(channel, 1u);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (available < 6u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uart_ring_peek_byte(channel, 1u, &sync_byte)) {
|
||||
return false;
|
||||
}
|
||||
payload_len = (uint16_t)((uint16_t)sync_byte << 8);
|
||||
if (!uart_ring_peek_byte(channel, 2u, &sync_byte)) {
|
||||
return false;
|
||||
}
|
||||
payload_len = (uint16_t)(payload_len | sync_byte);
|
||||
if (payload_len > sizeof(frame->payload)) {
|
||||
uart_ring_drop(channel, 1u);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (available < (uint16_t)(payload_len + 6u)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uart_ring_peek_byte(channel, 5u + payload_len, &sync_byte)) {
|
||||
return false;
|
||||
}
|
||||
if (sync_byte != UART_MUX_TAIL) {
|
||||
uart_ring_drop(channel, 1u);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uart_ring_peek_byte(channel, 3u, &frame->src_id) ||
|
||||
!uart_ring_peek_byte(channel, 4u, &frame->dst_mask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame->payload_len = payload_len;
|
||||
for (i = 0u; i < payload_len; ++i) {
|
||||
if (!uart_ring_peek_byte(channel, (uint16_t)(5u + i), &frame->payload[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uart_ring_drop(channel, (uint16_t)(payload_len + 6u));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uart_mux_encode_frame(uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
const uint8_t *payload,
|
||||
uint16_t payload_len,
|
||||
uint8_t *out,
|
||||
uint16_t *out_len,
|
||||
uint16_t out_capacity)
|
||||
{
|
||||
uint16_t frame_len = (uint16_t)(payload_len + 6u);
|
||||
|
||||
if (out == NULL || out_len == NULL || frame_len > out_capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out[0] = UART_MUX_SYNC;
|
||||
out[1] = (uint8_t)(payload_len >> 8);
|
||||
out[2] = (uint8_t)(payload_len & 0xFFu);
|
||||
out[3] = src_id;
|
||||
out[4] = dst_mask;
|
||||
if (payload_len > 0u && payload != NULL) {
|
||||
memcpy(&out[5], payload, payload_len);
|
||||
}
|
||||
out[5 + payload_len] = UART_MUX_TAIL;
|
||||
*out_len = frame_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UartRxTask(void *argument)
|
||||
{
|
||||
uint32_t notify_value;
|
||||
route_msg_t *msg;
|
||||
uart_mux_frame_t frame;
|
||||
const device_config_t *cfg;
|
||||
|
||||
(void)argument;
|
||||
if (uart_trans_start_all() != 0) {
|
||||
Debug_TrapWithRttHint("uart-start-fail");
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
debug_log_boot("uart-task-started");
|
||||
|
||||
for (;;) {
|
||||
(void)xTaskNotifyWait(0u, 0xFFFFFFFFu, ¬ify_value, pdMS_TO_TICKS(10));
|
||||
|
||||
if ((notify_value & UART_NOTIFY_RX_U0) != 0u) {
|
||||
process_rx_snapshot(UART_CHANNEL_U0);
|
||||
}
|
||||
if ((notify_value & UART_NOTIFY_RX_U1) != 0u) {
|
||||
process_rx_snapshot(UART_CHANNEL_U1);
|
||||
}
|
||||
if ((notify_value & UART_NOTIFY_TX_U0) != 0u) {
|
||||
g_channels[UART_CHANNEL_U0].tx_busy = 0u;
|
||||
}
|
||||
if ((notify_value & UART_NOTIFY_TX_U1) != 0u) {
|
||||
g_channels[UART_CHANNEL_U1].tx_busy = 0u;
|
||||
}
|
||||
|
||||
while (xQueueReceive(xTcpRxQueue, &msg, 0) == pdPASS) {
|
||||
uart_send_tcp_msg_to_uarts(msg);
|
||||
route_msg_free(msg);
|
||||
}
|
||||
|
||||
cfg = config_get();
|
||||
if (cfg->mux_mode == MUX_MODE_FRAME) {
|
||||
while (uart_mux_try_extract_frame(UART_CHANNEL_U0, &frame)) {
|
||||
uart_route_mux_frame(UART_CHANNEL_U0, &frame);
|
||||
}
|
||||
while (uart_mux_try_extract_frame(UART_CHANNEL_U1, &frame)) {
|
||||
uart_route_mux_frame(UART_CHANNEL_U1, &frame);
|
||||
}
|
||||
} else {
|
||||
uart_route_raw_channel(UART_CHANNEL_U0);
|
||||
uart_route_raw_channel(UART_CHANNEL_U1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TX busy or no stream, wait a bit */
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
kick_tx(UART_CHANNEL_U0);
|
||||
kick_tx(UART_CHANNEL_U1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user