Files
DLD154Pro/docs/modbus-tx-blocking-worst-case.md
T
wangfq 0be7af8aa3 docs: 活动上报+继电器翻转 最坏情况对线圈检测的影响分析
关键发现:
- 两个 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 标志位, 不会因主循环阻塞丢失
2026-07-30 17:52:01 +08:00

5.2 KiB
Raw Permalink Blame History

最坏情况:活动上报 + 继电器翻转 对线圈检测的影响

场景:变化量持续活跃 (150ms 上报) + 车辆进出触发继电器翻转 (即时上报) 波特率:115200 (Modbus 默认)


1. 为什么两个 TX 不会背靠背?

modbus_report_schedule() 的结构:

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 次连续确认 ≈ 26msENTRY_CONFIRM=3 × 8.74ms)。

4.3ms 额外延迟 / 26ms 确认窗口 = +16%

3.2 会丢边沿吗?

不会。 CAP_OK 是 TMR0 ISR 置位的标志位,cjq_srvif(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 占满 92mscjq_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。