/** * @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 /*--------------------------------------------------------------------------- * 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)); } } }