/** * @file uart_trans.c * @brief Bare-metal UART DMA/IDLE transport and MUX helpers. */ #include "uart_trans.h" #include "../Core/Inc/usart.h" #include #define UART_MUX_SYNC 0x7Eu #define UART_MUX_TAIL 0x7Fu typedef struct { UART_HandleTypeDef *huart; uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE]; uint8_t tx_dma_buffer[UART_TX_DMA_BUFFER_SIZE]; uint8_t rx_ring[UART_RX_RING_BUFFER_SIZE]; uint8_t tx_ring[UART_TX_RING_BUFFER_SIZE]; volatile uint16_t rx_dma_read_index; volatile uint16_t rx_head; volatile uint16_t rx_tail; volatile uint16_t tx_head; volatile uint16_t tx_tail; volatile uint16_t tx_dma_len; volatile bool tx_busy; uart_config_t config; uart_stats_t stats; bool initialized; bool running; } uart_channel_ctx_t; static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX]; static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size) { return (head >= tail) ? (head - tail) : (size - tail + head); } static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size) { return (uint16_t)(size - ring_used(head, tail, size) - 1u); } static bool ring_peek_byte(const uart_channel_ctx_t *ctx, uint16_t offset, uint8_t *out) { uint16_t head; uint16_t tail; if (ctx == NULL || out == NULL) { return false; } head = ctx->rx_head; tail = ctx->rx_tail; if (offset >= ring_used(head, tail, UART_RX_RING_BUFFER_SIZE)) { return false; } *out = ctx->rx_ring[(tail + offset) % UART_RX_RING_BUFFER_SIZE]; return true; } static bool ring_peek_span(const uart_channel_ctx_t *ctx, uint16_t offset, uint8_t *data, uint16_t len) { if (ctx == NULL || data == NULL) { return false; } for (uint16_t i = 0u; i < len; ++i) { if (!ring_peek_byte(ctx, (uint16_t)(offset + i), &data[i])) { return false; } } return true; } static void ring_drop_bytes(uart_channel_ctx_t *ctx, uint16_t len) { if (ctx == NULL) { return; } while (len > 0u && ctx->rx_tail != ctx->rx_head) { ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE); --len; } } static int apply_uart_config(uart_channel_t channel) { uart_channel_ctx_t *ctx = &g_channels[channel]; if (ctx->huart == NULL) { return -1; } if (ctx->running) { HAL_UART_DMAStop(ctx->huart); ctx->running = false; } ctx->huart->Init.BaudRate = ctx->config.baudrate; ctx->huart->Init.WordLength = UART_WORDLENGTH_8B; ctx->huart->Init.StopBits = UART_STOPBITS_1; ctx->huart->Init.Parity = UART_PARITY_NONE; return (HAL_UART_Init(ctx->huart) == HAL_OK) ? 0 : -1; } static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index) { uart_channel_ctx_t *ctx = &g_channels[channel]; 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) { ctx->stats.errors++; 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); ctx->stats.rx_bytes++; } } static void kick_tx(uart_channel_t channel) { uart_channel_ctx_t *ctx = &g_channels[channel]; uint16_t available; uint16_t chunk; if (!ctx->running || ctx->tx_busy) { return; } available = ring_used(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE); if (available == 0u) { return; } chunk = available; if (chunk > UART_TX_DMA_BUFFER_SIZE) { chunk = UART_TX_DMA_BUFFER_SIZE; } for (uint16_t i = 0; i < chunk; ++i) { ctx->tx_dma_buffer[i] = ctx->tx_ring[ctx->tx_tail]; ctx->tx_tail = (uint16_t)((ctx->tx_tail + 1u) % UART_TX_RING_BUFFER_SIZE); } ctx->tx_dma_len = chunk; ctx->tx_busy = true; ctx->stats.tx_packets++; if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) { ctx->tx_busy = false; ctx->stats.errors++; } } int uart_trans_init(void) { memset(g_channels, 0, sizeof(g_channels)); g_channels[UART_CHANNEL_U0].huart = &huart2; g_channels[UART_CHANNEL_U1].huart = &huart3; g_channels[UART_CHANNEL_U0].config.baudrate = UART_DEFAULT_BAUDRATE; g_channels[UART_CHANNEL_U1].config.baudrate = UART_DEFAULT_BAUDRATE; g_channels[UART_CHANNEL_U0].initialized = true; g_channels[UART_CHANNEL_U1].initialized = true; return 0; } int uart_trans_config(uart_channel_t channel, const uart_config_t *config) { if (channel >= UART_CHANNEL_MAX || config == NULL) { return -1; } g_channels[channel].config = *config; return apply_uart_config(channel); } int uart_trans_start(uart_channel_t channel) { uart_channel_ctx_t *ctx; if (channel >= UART_CHANNEL_MAX) { return -1; } ctx = &g_channels[channel]; if (!ctx->initialized || ctx->huart == NULL) { return -1; } ctx->rx_dma_read_index = 0u; ctx->rx_head = 0u; ctx->rx_tail = 0u; ctx->tx_head = 0u; ctx->tx_tail = 0u; ctx->tx_dma_len = 0u; ctx->tx_busy = false; memset(&ctx->stats, 0, sizeof(ctx->stats)); __HAL_UART_ENABLE_IT(ctx->huart, UART_IT_IDLE); if (HAL_UART_Receive_DMA(ctx->huart, ctx->rx_dma_buffer, UART_RX_DMA_BUFFER_SIZE) != HAL_OK) { return -1; } ctx->running = true; return 0; } int uart_trans_stop(uart_channel_t channel) { if (channel >= UART_CHANNEL_MAX) { return -1; } HAL_UART_DMAStop(g_channels[channel].huart); g_channels[channel].running = false; g_channels[channel].tx_busy = false; return 0; } void uart_trans_poll(void) { kick_tx(UART_CHANNEL_U0); kick_tx(UART_CHANNEL_U1); } uint16_t uart_trans_rx_available(uart_channel_t channel) { if (channel >= UART_CHANNEL_MAX) { return 0u; } return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE); } uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len) { uart_channel_ctx_t *ctx; uint16_t copied = 0u; if (channel >= UART_CHANNEL_MAX || data == NULL || max_len == 0u) { return 0u; } ctx = &g_channels[channel]; while (copied < max_len && ctx->rx_tail != ctx->rx_head) { data[copied++] = ctx->rx_ring[ctx->rx_tail]; ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE); } if (copied > 0u) { ctx->stats.rx_packets++; } return copied; } uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t len) { uart_channel_ctx_t *ctx; uint16_t written = 0u; if (channel >= UART_CHANNEL_MAX || data == NULL || len == 0u) { return 0u; } ctx = &g_channels[channel]; while (written < len && ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) > 0u) { ctx->tx_ring[ctx->tx_head] = data[written++]; ctx->tx_head = (uint16_t)((ctx->tx_head + 1u) % UART_TX_RING_BUFFER_SIZE); } if (written < len) { ctx->stats.errors++; } kick_tx(channel); return written; } void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats) { if (channel < UART_CHANNEL_MAX && stats != NULL) { *stats = g_channels[channel].stats; } } void uart_trans_reset_stats(uart_channel_t channel) { if (channel < UART_CHANNEL_MAX) { memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats)); } } void uart_trans_idle_handler(uart_channel_t channel) { UART_HandleTypeDef *huart; uint16_t dma_write_index; if (channel >= UART_CHANNEL_MAX) { return; } huart = g_channels[channel].huart; g_channels[channel].stats.idle_events++; dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx)); if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) { dma_write_index = 0u; } process_rx_snapshot(channel, dma_write_index); } void uart_trans_rx_half_cplt_handler(uart_channel_t channel) { if (channel >= UART_CHANNEL_MAX) { return; } g_channels[channel].stats.rx_half_events++; process_rx_snapshot(channel, UART_RX_DMA_BUFFER_SIZE / 2u); } void uart_trans_rx_cplt_handler(uart_channel_t channel) { if (channel >= UART_CHANNEL_MAX) { return; } g_channels[channel].stats.rx_full_events++; process_rx_snapshot(channel, 0u); } void uart_trans_tx_cplt_handler(uart_channel_t channel) { if (channel >= UART_CHANNEL_MAX) { return; } g_channels[channel].tx_busy = false; g_channels[channel].stats.tx_bytes += g_channels[channel].tx_dma_len; g_channels[channel].tx_dma_len = 0u; kick_tx(channel); } bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame) { uart_channel_ctx_t *ctx; uint8_t header[4]; uint8_t tail_byte; uint16_t available; uint16_t payload_len; uint16_t sync_offset; uint16_t total_len; if (channel >= UART_CHANNEL_MAX || frame == NULL) { return false; } ctx = &g_channels[channel]; for (;;) { available = uart_trans_rx_available(channel); if (available < 6u) { return false; } sync_offset = available; for (uint16_t i = 0u; i < available; ++i) { uint8_t byte = 0u; if (!ring_peek_byte(ctx, i, &byte)) { return false; } if (byte == UART_MUX_SYNC) { sync_offset = i; break; } } if (sync_offset == available) { ring_drop_bytes(ctx, available); return false; } if (sync_offset > 0u) { ring_drop_bytes(ctx, sync_offset); available = (uint16_t)(available - sync_offset); } if (available < 6u) { return false; } if (!ring_peek_span(ctx, 1u, header, sizeof(header))) { return false; } payload_len = (uint16_t)(((uint16_t)header[0] << 8) | header[1]); if (payload_len > sizeof(frame->payload)) { ring_drop_bytes(ctx, 1u); continue; } total_len = (uint16_t)(payload_len + 6u); if (available < total_len) { return false; } if (!ring_peek_byte(ctx, (uint16_t)(total_len - 1u), &tail_byte)) { return false; } if (tail_byte != UART_MUX_TAIL) { ring_drop_bytes(ctx, 1u); continue; } frame->src_id = header[2]; frame->dst_mask = header[3]; frame->payload_len = payload_len; if (payload_len > 0u) { if (!ring_peek_span(ctx, 5u, frame->payload, payload_len)) { return false; } } ring_drop_bytes(ctx, total_len); 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; if (out == NULL || out_len == NULL) { return false; } frame_len = (uint16_t)(payload_len + 6u); if (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; }