diff --git a/peripheral/ymodem.c b/peripheral/ymodem.c index 642f9bf..4b8f7c3 100644 --- a/peripheral/ymodem.c +++ b/peripheral/ymodem.c @@ -100,6 +100,34 @@ static enum rym_code _rym_read_code(struct rym_ctx *ctx, uint32_t timeout_ms) return RYM_CODE_NONE; } +/* 带杂字节容忍的协议码等待: 超时窗口内逐字节扫描, 只认 accept 集合, + * 其余字节忽略并记 debug 日志. + * 场景: MCU 固件里 elog 与 ymodem 共用控制台串口, FIN 间隙的对端日志 + * (ANSI 色码 0x1B 开头 / 文本行) 会插在协议码之前, "首字节即协议码" + * 的 _rym_read_code 会把它误判成协议错误 (实测 got 0x1B → -113) */ +static enum rym_code _rym_read_code_scan(struct rym_ctx *ctx, uint32_t timeout_ms, + const uint8_t *_pcAccept, size_t _nAccept) +{ + uint32_t start = Rd_GetTime(); + while (Rd_GetTime() - start < timeout_ms) + { + size_t rsz = ctx->dev.read(ctx->dev.opaque, ctx->buf, 1); + if (rsz == 1) + { + uint8_t ucByte = *ctx->buf; + for (size_t k = 0; k < _nAccept; k++) + { + if (ucByte == _pcAccept[k]) + return (enum rym_code)ucByte; + } + log_d("ymodem: skip junk byte 0x%02X while waiting code", (unsigned)ucByte); + continue; + } + Rd_Delay(1); + } + return RYM_CODE_NONE; +} + static ssize_t _rym_read_data(struct rym_ctx *ctx, size_t len) { uint8_t *buf = ctx->buf + 1; @@ -530,7 +558,13 @@ static int _rym_do_fin(struct rym_ctx *ctx) ctx->on_end(ctx, ctx->buf + 3, 128); _rym_putchar(ctx, RYM_CODE_NAK); - code = _rym_read_code(ctx, RYM_WAIT_PKG_MS); + /* 容忍杂字节扫描: 等发送端按双 EOT 约定补发的第二个 EOT. + * 发送端在收到 NAK 后可能正往同一条串口打日志 (共享控制台传输时), + * 首字节未必是 EOT, 不能用 _rym_read_code 直读 */ + { + const uint8_t ucAccept[1] = { RYM_CODE_EOT }; + code = _rym_read_code_scan(ctx, RYM_WAIT_PKG_MS, ucAccept, 1); + } if (code != RYM_CODE_EOT) { log_e("ymodem RX FIN phase: expected EOT, got 0x%02X (0x00=timeout)", (unsigned)code); @@ -540,13 +574,20 @@ static int _rym_do_fin(struct rym_ctx *ctx) _rym_putchar(ctx, RYM_CODE_ACK); _rym_putchar(ctx, RYM_CODE_C); - code = _rym_read_code(ctx, RYM_WAIT_PKG_MS); + /* 等结束帧/下一文件包头, 同样容忍日志杂字节 */ + { + const uint8_t ucAccept[2] = { RYM_CODE_SOH, RYM_CODE_STX }; + code = _rym_read_code_scan(ctx, RYM_WAIT_PKG_MS, ucAccept, 2); + } if (code == RYM_CODE_SOH) data_sz = _RYM_SOH_PKG_SZ; else if (code == RYM_CODE_STX) data_sz = _RYM_STX_PKG_SZ; else + { + log_e("ymodem RX FIN phase: expected header, got 0x%02X (0x00=timeout)", (unsigned)code); return -RYM_ERR_CODE; + } i = _rym_read_data(ctx, data_sz - 1); if (i != (data_sz - 1)) @@ -583,7 +624,11 @@ static int _rym_do_send_fin(struct rym_ctx *ctx) data_sz = _RYM_SOH_PKG_SZ; /* EOT 阶段: 对端回 NAK 表示"请再发一次 EOT", 直接回 ACK 表示收到, - * 不回(超时)也重发; 三种情况都不能当失败退出 (有些接收端要两三次才确认) */ + * 不回(超时)也重发; 三种情况都不能当失败退出 (有些接收端要两三次才确认) + * 【注意】NAK 是本工程接收端 (_rym_do_fin) 的标准首次应答, 属正常路径; + * 日志只能打 debug —— MCU 固件里 elog 与 ymodem 共用控制台串口, + * 在两次 EOT 之间往线上打 WARN 会污染协议流 (接收端会把 ANSI 色码 + * 0x1B 当协议码, 误报 -113), 见 _rym_read_code_scan 注释 */ for (retry = 0; ; retry++) { _rym_putchar(ctx, RYM_CODE_EOT); @@ -605,7 +650,7 @@ static int _rym_do_send_fin(struct rym_ctx *ctx) return -RYM_ERR_ACK; } - log_w("ymodem TX fin: EOT %s -> resend (%u/%u)", + log_d("ymodem TX fin: EOT %s -> resend (%u/%u)", (getc_ack == RYM_CODE_NAK) ? "NAKed" : "no reply", (unsigned)(retry + 1), (unsigned)RYM_TX_RETRY_MAX); }