Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| edfcc0991c | |||
| aceacbdba1 | |||
| b107a3169c | |||
| 495fbe4298 |
@@ -44,3 +44,8 @@ Desktop.ini
|
||||
|
||||
# Local packet captures
|
||||
WiresharkLog/
|
||||
|
||||
# Local build/session artifacts
|
||||
.embeddedskills/
|
||||
uv4_stdout.txt
|
||||
MDK-ARM/EventRecorderStub.scvd
|
||||
|
||||
+22
@@ -382,6 +382,28 @@ AT+RESET\r\n
|
||||
1. `AT+SAVE\r\n`
|
||||
2. `AT+RESET\r\n`
|
||||
|
||||
### 12.3 MUX 模式数据口有丢包
|
||||
|
||||
若 `MUX=1` 下出现“主机侧已发送,但设备对端收到数量明显偏少”的现象,优先按以下顺序检查:
|
||||
|
||||
1. 固件版本是否已经包含 `2026-04-18` 的 MUX 丢包修复。
|
||||
2. MUX 帧是否完整,尤其是:
|
||||
- `SYNC=0x7E`
|
||||
- `LEN_H/LEN_L`
|
||||
- `SRCID`
|
||||
- `DSTMASK`
|
||||
- `TAIL=0x7F`
|
||||
3. 上位机发送方式是否把一帧拆成多个不连续小片段,或在帧间插入无效字节。
|
||||
4. TCP 对端是否出现拥塞、窗口缩小或应用层不及时取数,导致发送路径出现背压。
|
||||
5. RTT 中是否存在链路错误、发送失败或持续重连现象。
|
||||
|
||||
当前版本的修复点如下:
|
||||
|
||||
1. MUX 解析器改为在整帧完整到齐前不推进 UART RX ring 读指针,避免半帧被破坏性消费。
|
||||
2. TCP 发送路径与 UART 写入路径不再把背压和短写静默视为成功,便于及早暴露链路承载问题。
|
||||
|
||||
现场回归结果:在修复后的固件中,MUX 模式持续发送 `670` 包,接收端 `670` 包全部到达,`0` 丢包。
|
||||
|
||||
## 13. 相关文件
|
||||
|
||||
- AT 命令实现:[config.c](/D:/code/STM32Project/TCP2UART/App/config.c)
|
||||
|
||||
@@ -239,14 +239,23 @@ int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
return -1;
|
||||
}
|
||||
if (tcp_sndbuf(ctx->pcb) < len) {
|
||||
ctx->status.errors++;
|
||||
return 0;
|
||||
}
|
||||
err = tcp_write(ctx->pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
if (err == ERR_MEM) {
|
||||
ctx->status.errors++;
|
||||
return 0;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
err = tcp_output(ctx->pcb);
|
||||
if (err == ERR_MEM) {
|
||||
ctx->status.errors++;
|
||||
return 0;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
|
||||
@@ -228,15 +228,24 @@ int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
return -1;
|
||||
}
|
||||
if (tcp_sndbuf(ctx->client_pcb) < len) {
|
||||
ctx->status.errors++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = tcp_write(ctx->client_pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
if (err == ERR_MEM) {
|
||||
ctx->status.errors++;
|
||||
return 0;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
err = tcp_output(ctx->client_pcb);
|
||||
if (err == ERR_MEM) {
|
||||
ctx->status.errors++;
|
||||
return 0;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
|
||||
+87
-17
@@ -43,6 +43,52 @@ 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];
|
||||
@@ -297,64 +343,88 @@ void uart_trans_tx_cplt_handler(uart_channel_t channel)
|
||||
|
||||
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame)
|
||||
{
|
||||
uint8_t sync_byte;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Scan for SYNC byte (0x7E) — discard non-matching bytes one at a time */
|
||||
if (uart_trans_read(channel, &sync_byte, 1u) != 1u) {
|
||||
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 (sync_byte != UART_MUX_SYNC) {
|
||||
if (byte == UART_MUX_SYNC) {
|
||||
sync_offset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sync_offset == available) {
|
||||
ring_drop_bytes(ctx, available);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Need at least: 2(len) + 1(src) + 1(dst) + payload + 1(tail) = 5 + payload */
|
||||
available = uart_trans_rx_available(channel);
|
||||
if (available < 4u) {
|
||||
if (sync_offset > 0u) {
|
||||
ring_drop_bytes(ctx, sync_offset);
|
||||
available = (uint16_t)(available - sync_offset);
|
||||
}
|
||||
|
||||
if (available < 6u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uart_trans_read(channel, header, sizeof(header)) != sizeof(header)) {
|
||||
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 (uart_trans_rx_available(channel) < (uint16_t)(payload_len + 1u)) {
|
||||
|
||||
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 (uart_trans_read(channel, frame->payload, payload_len) != payload_len) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t tail = 0u;
|
||||
if (uart_trans_read(channel, &tail, 1u) != 1u || tail != UART_MUX_TAIL) {
|
||||
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,
|
||||
|
||||
+89
-30
@@ -66,7 +66,9 @@ static void App_RouteMuxUartTraffic(void);
|
||||
static void App_RouteTcpTraffic(void);
|
||||
static void StackGuard_Init(void);
|
||||
static void StackGuard_Check(void);
|
||||
static void App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len);
|
||||
static bool App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len);
|
||||
static bool App_SendTcpServerPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||
static bool App_SendTcpClientPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||
/* USER CODE END PFP */
|
||||
|
||||
/* Private user code ---------------------------------------------------------*/
|
||||
@@ -261,19 +263,33 @@ static void App_Init(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len)
|
||||
static bool App_SendTcpServerPayload(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
return tcp_server_send(instance, data, len) == (int)len;
|
||||
}
|
||||
|
||||
static bool App_SendTcpClientPayload(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
return tcp_client_send(instance, data, len) == (int)len;
|
||||
}
|
||||
|
||||
static bool App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
const device_config_t *cfg = config_get();
|
||||
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||
uint16_t written;
|
||||
|
||||
if (cfg->mux_mode == MUX_MODE_FRAME) {
|
||||
uint8_t frame[APP_ROUTE_BUFFER_SIZE + 6u];
|
||||
uint16_t frame_len = 0u;
|
||||
if (uart_mux_encode_frame(src_id, dst_mask, data, len, frame, &frame_len, sizeof(frame))) {
|
||||
(void)uart_trans_write(channel, frame, frame_len);
|
||||
written = uart_trans_write(channel, frame, frame_len);
|
||||
return written == frame_len;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
(void)uart_trans_write(channel, data, len);
|
||||
written = uart_trans_write(channel, data, len);
|
||||
return written == len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,11 +302,13 @@ static void App_RouteTcpTraffic(void)
|
||||
int rc = tcp_server_recv(i, buffer, sizeof(buffer));
|
||||
if (rc > 0) {
|
||||
uint8_t link_index = (i == 0u) ? CONFIG_LINK_S1 : CONFIG_LINK_S2;
|
||||
App_SendToUart(cfg->links[link_index].uart,
|
||||
if (!App_SendToUart(cfg->links[link_index].uart,
|
||||
config_link_index_to_endpoint(link_index),
|
||||
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
||||
buffer,
|
||||
(uint16_t)rc);
|
||||
(uint16_t)rc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,11 +316,13 @@ static void App_RouteTcpTraffic(void)
|
||||
int rc = tcp_client_recv(i, buffer, sizeof(buffer));
|
||||
if (rc > 0) {
|
||||
uint8_t link_index = (i == 0u) ? CONFIG_LINK_C1 : CONFIG_LINK_C2;
|
||||
App_SendToUart(cfg->links[link_index].uart,
|
||||
if (!App_SendToUart(cfg->links[link_index].uart,
|
||||
config_link_index_to_endpoint(link_index),
|
||||
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
||||
buffer,
|
||||
(uint16_t)rc);
|
||||
(uint16_t)rc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,37 +335,57 @@ static void App_RouteRawUartTraffic(void)
|
||||
|
||||
len = uart_trans_read(UART_CHANNEL_U0, buffer, sizeof(buffer));
|
||||
if (len > 0u) {
|
||||
bool routed_ok = true;
|
||||
for (uint8_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
bool sent = true;
|
||||
if (cfg->links[i].enabled == 0u || cfg->links[i].uart != LINK_UART_U0) {
|
||||
continue;
|
||||
}
|
||||
if (i == CONFIG_LINK_S1) {
|
||||
(void)tcp_server_send(0u, buffer, len);
|
||||
sent = App_SendTcpServerPayload(0u, buffer, len);
|
||||
} else if (i == CONFIG_LINK_S2) {
|
||||
(void)tcp_server_send(1u, buffer, len);
|
||||
sent = App_SendTcpServerPayload(1u, buffer, len);
|
||||
} else if (i == CONFIG_LINK_C1) {
|
||||
(void)tcp_client_send(0u, buffer, len);
|
||||
sent = App_SendTcpClientPayload(0u, buffer, len);
|
||||
} else if (i == CONFIG_LINK_C2) {
|
||||
(void)tcp_client_send(1u, buffer, len);
|
||||
sent = App_SendTcpClientPayload(1u, buffer, len);
|
||||
}
|
||||
|
||||
if (!sent) {
|
||||
routed_ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!routed_ok) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
len = uart_trans_read(UART_CHANNEL_U1, buffer, sizeof(buffer));
|
||||
if (len > 0u) {
|
||||
bool routed_ok = true;
|
||||
for (uint8_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
bool sent = true;
|
||||
if (cfg->links[i].enabled == 0u || cfg->links[i].uart != LINK_UART_U1) {
|
||||
continue;
|
||||
}
|
||||
if (i == CONFIG_LINK_S1) {
|
||||
(void)tcp_server_send(0u, buffer, len);
|
||||
sent = App_SendTcpServerPayload(0u, buffer, len);
|
||||
} else if (i == CONFIG_LINK_S2) {
|
||||
(void)tcp_server_send(1u, buffer, len);
|
||||
sent = App_SendTcpServerPayload(1u, buffer, len);
|
||||
} else if (i == CONFIG_LINK_C1) {
|
||||
(void)tcp_client_send(0u, buffer, len);
|
||||
sent = App_SendTcpClientPayload(0u, buffer, len);
|
||||
} else if (i == CONFIG_LINK_C2) {
|
||||
(void)tcp_client_send(1u, buffer, len);
|
||||
sent = App_SendTcpClientPayload(1u, buffer, len);
|
||||
}
|
||||
|
||||
if (!sent) {
|
||||
routed_ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!routed_ok) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,6 +394,7 @@ static void App_RouteMuxUartTraffic(void)
|
||||
{
|
||||
uart_mux_frame_t frame;
|
||||
const device_config_t *cfg = config_get();
|
||||
bool routed_ok;
|
||||
|
||||
while (uart_mux_try_extract_frame(UART_CHANNEL_U0, &frame)) {
|
||||
#if defined(DEBUG) && (DEBUG != 0)
|
||||
@@ -366,33 +407,42 @@ static void App_RouteMuxUartTraffic(void)
|
||||
uint16_t response_len = (uint16_t)strlen(response_text);
|
||||
uint16_t frame_len = 0u;
|
||||
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U0), 0u, (const uint8_t *)response_text, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||
(void)uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len);
|
||||
if (uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len) != frame_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (result == AT_NEED_REBOOT) {
|
||||
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
||||
response_len = (uint16_t)strlen(hint);
|
||||
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U0), 0u, (const uint8_t *)hint, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||
(void)uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len);
|
||||
if (uart_trans_write(UART_CHANNEL_U0, g_mux_response_frame, frame_len) != frame_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
routed_ok = true;
|
||||
if ((frame.dst_mask & ENDPOINT_S1) != 0u) {
|
||||
(void)tcp_server_send(0u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpServerPayload(0u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_S2) != 0u) {
|
||||
(void)tcp_server_send(1u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpServerPayload(1u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_C1) != 0u) {
|
||||
(void)tcp_client_send(0u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpClientPayload(0u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_C2) != 0u) {
|
||||
(void)tcp_client_send(1u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpClientPayload(1u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_UART3) != 0u && cfg->links[CONFIG_LINK_S2].uart == LINK_UART_U1) {
|
||||
App_SendToUart(LINK_UART_U1, frame.src_id, ENDPOINT_UART3, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendToUart(LINK_UART_U1, frame.src_id, ENDPOINT_UART3, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
|
||||
if (!routed_ok) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,33 +457,42 @@ static void App_RouteMuxUartTraffic(void)
|
||||
uint16_t response_len = (uint16_t)strlen(response_text);
|
||||
uint16_t frame_len = 0u;
|
||||
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U1), 0u, (const uint8_t *)response_text, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||
(void)uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len);
|
||||
if (uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len) != frame_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (result == AT_NEED_REBOOT) {
|
||||
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
||||
response_len = (uint16_t)strlen(hint);
|
||||
if (uart_mux_encode_frame(config_uart_index_to_endpoint(LINK_UART_U1), 0u, (const uint8_t *)hint, response_len, g_mux_response_frame, &frame_len, sizeof(g_mux_response_frame))) {
|
||||
(void)uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len);
|
||||
if (uart_trans_write(UART_CHANNEL_U1, g_mux_response_frame, frame_len) != frame_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
routed_ok = true;
|
||||
if ((frame.dst_mask & ENDPOINT_S1) != 0u) {
|
||||
(void)tcp_server_send(0u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpServerPayload(0u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_S2) != 0u) {
|
||||
(void)tcp_server_send(1u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpServerPayload(1u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_C1) != 0u) {
|
||||
(void)tcp_client_send(0u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpClientPayload(0u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_C2) != 0u) {
|
||||
(void)tcp_client_send(1u, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendTcpClientPayload(1u, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
if ((frame.dst_mask & ENDPOINT_UART2) != 0u) {
|
||||
App_SendToUart(LINK_UART_U0, frame.src_id, ENDPOINT_UART2, frame.payload, frame.payload_len);
|
||||
routed_ok = App_SendToUart(LINK_UART_U0, frame.src_id, ENDPOINT_UART2, frame.payload, frame.payload_len) && routed_ok;
|
||||
}
|
||||
|
||||
if (!routed_ok) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
778 0 0 2 0 0 ip4.o
|
||||
46 0 4 0 0 0 ip4_addr.o
|
||||
0 0 0 0 12 0 iwdg.o
|
||||
2694 0 185 12 272 0 main.o
|
||||
2838 0 185 12 272 0 main.o
|
||||
828 0 0 12 4115 0 mem.o
|
||||
196 0 244 32 6464 0 memp.o
|
||||
582 0 0 12 0 0 netif.o
|
||||
@@ -43,13 +43,13 @@
|
||||
490 0 0 0 0 0 stm32f1xx_it.o
|
||||
2 0 24 4 0 0 system_stm32f1xx.o
|
||||
3474 0 193 32 0 0 tcp.o
|
||||
1212 0 0 0 1120 0 tcp_client.o
|
||||
1232 0 0 0 1120 0 tcp_client.o
|
||||
3684 0 0 36 20 0 tcp_in.o
|
||||
3862 0 0 0 0 0 tcp_out.o
|
||||
966 0 0 0 1104 0 tcp_server.o
|
||||
986 0 0 0 1104 0 tcp_server.o
|
||||
164 0 0 0 72 0 tim.o
|
||||
374 0 16 12 0 0 timeouts.o
|
||||
1296 0 0 0 2936 0 uart_trans.o
|
||||
1538 0 0 0 2936 0 uart_trans.o
|
||||
816 0 0 0 624 0 usart.o
|
||||
Object Totals
|
||||
|
||||
@@ -57,7 +57,7 @@ Memory Map of the image
|
||||
|
||||
Load Region LR_IROM1
|
||||
|
||||
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000D328, Max: 0x00010000, END)
|
||||
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000D4D0, Max: 0x00010000, END)
|
||||
|
||||
Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00005000, Max: 0x00005000, END)
|
||||
|
||||
|
||||
@@ -448,6 +448,40 @@ MUX 模式启动后,一段时间后网口失联。重新插拔网线无法恢
|
||||
|
||||
Keil MDK-ARM 构建 0 Error(s), 0 Warning(s)。Flash 52.7 KB / 64.0 KB (82.5%),RAM 20.0 KB / 20.0 KB (100%)。
|
||||
|
||||
### 9.5 2026-04-18 MUX 模式丢包修复记录
|
||||
|
||||
#### 现象
|
||||
|
||||
在 `MUX=1` 模式下进行持续发送测试时,主机侧发送 `500` 个数据包,只收到 `360` 个,存在明显丢包。
|
||||
|
||||
#### 根因
|
||||
|
||||
本轮定位确认软件侧至少存在以下两个直接丢包点:
|
||||
|
||||
1. `App/uart_trans.c` 中 `uart_mux_try_extract_frame()` 在确认整帧完整前,就先消费 `SYNC` 与 header。若 MUX 帧跨越多个 poll 周期到达,半帧会被提前移出 RX ring,导致当前帧失步并被直接丢弃。
|
||||
2. `App/tcp_server.c`、`App/tcp_client.c` 与 `Core/Src/main.c` 的发送路径对背压与短写处理不完整:
|
||||
- `tcp_sndbuf() < len`
|
||||
- `tcp_write()` / `tcp_output()` 返回 `ERR_MEM`
|
||||
- `uart_trans_write()` 只写入部分字节
|
||||
|
||||
以上情况在旧代码中会被上层静默忽略,表现为“发送函数返回但数据实际未完整进入下游链路”。
|
||||
|
||||
#### 修复内容
|
||||
|
||||
| 文件 | 修改 | 说明 |
|
||||
|------|------|------|
|
||||
| `App/uart_trans.c` | 将 `uart_mux_try_extract_frame()` 改为先窥视、后消费 | 只有在 `SYNC + header + payload + tail` 全部可用时才推进 `rx_tail`,避免半帧被破坏性消费 |
|
||||
| `App/tcp_server.c` | `tcp_server_send()` 对 `tcp_sndbuf()<len` 与 `ERR_MEM` 返回 `0` 并计入错误 | 明确表示本次发送未被底层接收,不再伪装成成功 |
|
||||
| `App/tcp_client.c` | `tcp_client_send()` 同步处理背压与 `ERR_MEM` | 逻辑与 server 侧保持一致 |
|
||||
| `Core/Src/main.c` | `App_SendToUart()` 检查 `uart_trans_write()` 是否完整写入 | TX ring 空间不足时立即显式失败 |
|
||||
| `Core/Src/main.c` | `App_RouteTcpTraffic()` / `App_RouteRawUartTraffic()` / `App_RouteMuxUartTraffic()` 统一检查发送结果 | 不再把背压、短写和未完整提交静默当成成功 |
|
||||
|
||||
#### 结果验证
|
||||
|
||||
1. Keil MDK-ARM 构建通过,`0 Error(s), 0 Warning(s)`。
|
||||
2. 在最新固件下重新进行 MUX 持续发送测试,主机侧发送 `670` 个数据包,接收 `670` 个,`0` 丢包。
|
||||
3. 本轮修复未增加新的常驻队列与缓冲区,保持当前 RAM 占用边界不变。
|
||||
|
||||
---
|
||||
|
||||
## 10. 常见误区
|
||||
|
||||
Reference in New Issue
Block a user