From 0be7af8aa35992da598e99e382bc6da5f156a768 Mon Sep 17 00:00:00 2001 From: wangfq Date: Thu, 30 Jul 2026 17:52:01 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B4=BB=E5=8A=A8=E4=B8=8A=E6=8A=A5+?= =?UTF-8?q?=E7=BB=A7=E7=94=B5=E5=99=A8=E7=BF=BB=E8=BD=AC=20=E6=9C=80?= =?UTF-8?q?=E5=9D=8F=E6=83=85=E5=86=B5=E5=AF=B9=E7=BA=BF=E5=9C=88=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E7=9A=84=E5=BD=B1=E5=93=8D=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关键发现: - 两个 TX 不会背靠背: relay flip TX 在入口处 return, 活动 TX 在下方 必然间隔一次 cjq_srv (≥3ms) - @115200: 总干扰窗口 11.6ms, CAP_OK 最多积压 1 次, 检测延迟 +16% - @9600: 总干扰窗口 92ms, CAP_OK 积压 5 次, 检测延迟 +170% ← 不可接受 - loop1_CAP_OK 是 ISR 标志位, 不会因主循环阻塞丢失 --- docs/modbus-tx-blocking-worst-case.md | 142 ++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 docs/modbus-tx-blocking-worst-case.md diff --git a/docs/modbus-tx-blocking-worst-case.md b/docs/modbus-tx-blocking-worst-case.md new file mode 100644 index 0000000..1e578bf --- /dev/null +++ b/docs/modbus-tx-blocking-worst-case.md @@ -0,0 +1,142 @@ +# 最坏情况:活动上报 + 继电器翻转 对线圈检测的影响 + +> 场景:变化量持续活跃 (150ms 上报) + 车辆进出触发继电器翻转 (即时上报) +> 波特率:115200 (Modbus 默认) + +--- + +## 1. 为什么两个 TX 不会背靠背? + +`modbus_report_schedule()` 的结构: + +```c +void modbus_report_schedule(void) +{ + // ① 先检查继电器翻转 (入口判断) + if (relay != _last_relay_state) { + send_full_data(); // 4.3ms TX + return; // ← 立即返回,不往下走 + } + + // ② 再检查活动/空闲定时器 + if (is_active() && elapsed >= 150ms) { + send_full_data(); // 4.3ms TX + // ← 返回后,函数继续到末尾,退出 + } +} +``` + +**两个 `send_full_data()` 不在同一次 `modbus_poll` 调用中**。继电器翻转的 TX 在入口处拦截后直接 `return`,不会同时走活动上报路径。 + +--- + +## 2. 最坏情况时序 + +假设 t=0 时活动定时器到期,同时 TMR1 ISR 在 TX 期间翻转了继电器: + +``` +t=0.0 Main_Circulation: + cjq_srv() ← 正常处理 CAP_OK + opt_input_poll() + modbus_poll() + → modbus_report_schedule() + → relay 未翻转, 活动定时器到期 + → send_full_data() ─────────── TX 阻塞 4.3ms ─┐ +t=1.5 [TMR1 ISR] relay 翻转 (cjq_srv 上次已判定进车) │ ISR 不受阻 +t=4.3 TX 结束 ──────────────────────────────────────────┘ + → _rpt_last_ms = now, 函数返回 + +t=7.5 下一次 Main_Circulation (间隔 ~3ms): + TMOS_SystemProcess() + poll_dbn_ble() + cjq_srv() ← 处理中间的 CAP_OK (最多积压 1 次) + opt_input_poll() + modbus_poll() + → modbus_report_schedule() + → relay != _last_relay_state ← 翻转被检测到 + → update_misc_time() ← 算车间距/通过时间 + → send_full_data() ─────────── TX 阻塞 4.3ms ─┐ +t=11.8 TX 结束 ──────────────────────────────────────────┘ + +t=11.8+ 正常恢复 +``` + +**关键**:t=4.3 到 t=7.5 之间有 ~3ms 的正常主循环,`cjq_srv` 在这个窗口内运行。 + +--- + +## 3. 对线圈检测的量化影响 + +| 参数 | 值 | 说明 | +|------|-----|------| +| 单次 TX 阻塞 | **4.3 ms** | 115200, 43 字节 | +| 两次 TX 间隔 | **≥3 ms** | 主循环其他任务 (cjq_srv + opt + BLE) | +| 总干扰窗口 | **~11.6 ms** | 含中间的 3ms 正常处理 | +| CAP_OK 周期 | **8.74 ms** | 测量窗长度 | +| 最多积压 CAP_OK | **1 次** | 11.6ms ÷ 8.74ms = 1.3 | +| cjq_srv 处理机会 | **窗口中间有 1 次** | 两次 TX 之间 | + +### 3.1 检测延迟 + +``` +正常路径: CAP_OK → cjq_srv(立即) → 判定进出车 +阻塞路径: CAP_OK → 等待 ≤4.3ms → cjq_srv → 判定进出车 + +额外延迟: ≤4.3ms +``` + +进出车检测本身需要 **3 次连续确认 ≈ 26ms**(ENTRY_CONFIRM=3 × 8.74ms)。 + +4.3ms 额外延迟 / 26ms 确认窗口 = **+16%**。 + +### 3.2 会丢边沿吗? + +**不会。** CAP_OK 是 TMR0 ISR 置位的标志位,`cjq_srv` 用 `if(loop1_CAP_OK)` 检测。TX 阻塞期间 ISR 照常运行,标志位被置起,TX 结束后 `cjq_srv` 立即处理。 + +--- + +## 4. 极限压测:连续进出车 + +假设车辆快速连续进出(罕见,需要车长极短或车速极高): + +``` +进车检测 → relay ON → TX(4.3ms) → gap(3ms) → 出车检测 → relay OFF → TX(4.3ms) + ↑ ↑ ↑ + cjq_srv判定 cjq_srv正常处理 cjq_srv判定 +``` + +每次继电器翻转触发一次 4.3ms TX。两次 TX 之间有 ≥3ms 的 cjq_srv 窗口。 + +| 连续翻转次数 | 总 TX 阻塞 | CAP_OK 总延迟 | 影响 | +|-------------|-----------|--------------|------| +| 1 次 | 4.3ms | +4.3ms | 可忽略 | +| 2 次 (一进一出) | 8.6ms | +8.6ms | 可忽略 | +| 5 次/秒 | 21.5ms/s | 平均 +4.3ms/次 | 🟢 | + +--- + +## 5. 与 9600 波特率的对比 + +| | @9600 | @115200 | +|--|-------|---------| +| 单次 TX | **44.8ms** | **4.3ms** | +| 两次 TX 间隔 | ~3ms | ~3ms | +| 总干扰窗口 | **~92ms** | **~11.6ms** | +| CAP_OK 积压 | **5 次** | **1 次** | +| cjq_srv 处理机会 | **TX 之间有 1 次** | TX 之间有 1 次 | +| 进出车额外延迟 | +45ms (+170%) | **+4.3ms (+16%)** | + +> @9600 下两次 TX 占满 92ms,`cjq_srv` 只在中间的 3ms 跑了一次,算法面临 5 个 CAP_OK 的积压。@115200 下仅 1 次积压。 + +--- + +## 6. 结论 + +**@115200,活动上报 + 继电器翻转叠加场景下,线圈检测完全不受实质影响。** + +- 单次 TX 4.3ms < CAP_OK 周期 8.74ms → 不会跨测量窗积压 +- 两次 TX 必然间隔一次 cjq_srv → 算法不中断 +- 进出车额外延迟 +4.3ms,相对于 26ms 确认窗口仅 +16% +- `loop1_CAP_OK` 是 ISR 置位的标志位,不会因主循环阻塞而丢失 + +**如果波特率降到 9600,情况完全不同**——单次 TX 45ms 会积压 5 个 CAP_OK,进出车额外延迟 +170%。这就是为什么 Modbus 默认必须用 115200。