Files
ESP32_COMM/.zcode/plans/plan-sess_a3047185-7ab8-4573-993b-07e65f6fed29.md
T
小龙虾 f9cbcc396b feat(spi): 增强 SPI master:性能剖析与执行耗时跟踪
- 新增 `lastWorkerExecUs()`:获取最近一次 worker 任务执行耗时(µs)。
- 引入 `SpiProfile` 结构体:事务级详细剖析,记录 SPI 事务各阶段的时间戳。
- 实现 `lastProfile()`:诊断用,获取最近一次事务剖析快照。
- SPI 时钟配置提升至 13.3 MHz,改善性能。
- 修改前端 JavaScript 轮询逻辑:新增 uptime 检查,更好地处理 ESP32 重启状态。
- SPI 测速 HTML 的计时单位由毫秒改为微秒显示。
- 新建 `spi_ring_cache.h`:共享 SPI ring push 缓存工具。
- 新增 `http_util.h`:共享 HTTP 处理器工具,含 JSON 拼接与查询参数解析。
- 引入 `json_parse.h`:轻量级 JSON 解析器,针对扁平 JSON 对象设计。
- 新增 Windows 环境下的项目构建批处理脚本。
- 重构 CAN 反馈处理:从推送式改为请求驱动架构,降低内存占用、提升效率。
2026-08-02 17:37:19 +08:00

4.4 KiB
Raw Blame History

方案:/can/feedback 改为请求驱动架构

目标

  • ESP32 不再被动缓存 GD32 的 0xC1 推送,改为前端请求时主动向 GD32 查询
  • GD32 停止主动推送 0xC1(降低 SPI 资源占用)
  • 删除 s_latest[3]/s_work[3] 常驻 BSS(省 ~7.8KB)+ handler 栈上 results[3](省 ~3.9KB 栈)

依据

  • GD32 handleFeedback 已支持 0xC1 请求查询([channel, page] → ACK + 页数据),无需新增协议
  • ESP32 transact(0xC1, ...) 是现成请求/响应机制,canFdSend/canFdSetRxMode 已在用
  • canFdParseFeedbackPage 可直接复用(只解析 payload 字节,不关心来源)
  • 0xC4/0xC5/0xC6/0xD2 推送独立,不受影响

改动清单

1. ESP32 侧 — main/transport/canfd_bridge.cpp / .h

新增 canFdQueryFeedback(channel, page, can_feedback_result_t *out, uint8_t *entryIdx):

int canFdQueryFeedback(uint8_t channel, uint8_t page,
                        can_feedback_result_t *out, uint8_t *entryIdx);
  • 发 transact(0xC1, {channel, page}, ...),超时 200ms
  • 成功(resp_type==ACK)→ 调 canFdParseFeedbackPage(resp_buf, resp_len, out, entryIdx)
  • 返回 0=成功,-1=SPI/参数失败,-2=GD32 拒绝(ERROR)

2. ESP32 侧 — main/server/http_handlers.cpp can_feedback_handler

  • 删除 results[CAN_CHANNEL_MAX] 栈数组 + getLatestCanFeedback 循环
  • 改为:对每个使能通道,调 canFdQueryFeedback(ch, 0, &result, &entryIdx) + canFdQueryFeedback(ch, 1, &result, &entryIdx),在栈上用单个 can_feedback_result_t result(1.3KB,用完即释放)
  • lite 路径:只需 page 0(统计字段在 page0 header),1 次 SPI 查询即可
  • full 路径:page 0 + page 1,2 次查询,然后序列化
  • 查询失败 → 该通道 valid=false(与原来无快照时行为一致)
  • pauseCanFeedbackTask 调用保留(send_batch 仍需独占 SPI)

3. ESP32 侧 — can_mode_handler GET

  • 原来 getLatestCanFeedback(0, &fb) 读缓存 mode → 改为 canFdQueryFeedback(0, 0, &fb, &entryIdx) 取 page0 的 mode 字段
  • 原来的 fallback canFdSetRxMode 保留(查询也失败时兜底)

4. ESP32 侧 — main/monitor/can_feedback_task.cpp

  • 删除 s_latest[3]、s_valid[3]、s_work[3]、s_workIdx[3]、s_lastLoggedError[3]、publish()、getLatestCanFeedback()
  • 删除 taskLoop 里的 0xC1 分支(canFdParseFeedbackPage + publish)
  • 保留 0xC4/0xC5/0xC6/0xD2 事件分发(这些仍走推送)
  • 保留 pauseCanFeedbackTask(send_batch / OTA 仍需暂停事件消费)
  • s_mutex 删除(仅保护 s_latest/s_valid,不再需要)

5. ESP32 侧 — main/monitor/can_feedback_task.h

  • 删除 getLatestCanFeedback 声明
  • 保留 startCanFeedbackTask、pauseCanFeedbackTask

6. GD32 侧 — app_spi_cmd_can_feedback.cpp

  • canFeedbackStartRound():改为空函数(或加 s_pushEnabled=false 永久禁用推送)
  • canFeedbackPushStep():开头加 if (!s_pushEnabled) return; 或直接 return
  • 保留 handleFeedback(请求查询处理程序不动)
  • 保留 buildFeedbackPayload(查询和推送共用)

7. GD32 侧 — task_runner.cpp

  • canFeedbackStartRound() 调用可保留(变成空函数无副作用)或删除

内存收益

项 改前 改后 省
s_latest[3] BSS 3.9KB 0 3.9KB
s_work[3] BSS 3.9KB 0 3.9KB
handler 栈 results[3] 3.9KB 1.3KB(单通道) 2.6KB 栈
合计常驻 BSS ~7.8KB

性能影响

  • 原来读缓存:<1ms
  • 改后查询:lite 路径 1 次 SPI 往返 ~2-4ms;full 路径 6 次往返(3ch×2page)~12-24ms
  • 前端 1Hz 轮询,24ms 完全可接受(httpd 单线程最坏阻塞 <50ms)
  • GD32 不再 1Hz 推送 6 帧,省下 SPI 带宽和 PB6 中断

风险与缓解

  • SPI 查询失败:GD32 不响应时 transact 超时返回,handler 返回 valid=false,前端显示"--"(与原无快照行为一致)
  • OTA 期间查询:send_batch/OTA 已 pauseCanFeedbackTask,且 SPI worker 串行化,查询不会与 OTA 数据流冲突
  • can_monitor.html full 路径延迟:6 次 SPI 查询 ~24ms + JSON 序列化,总 <50ms,远小于 1s 轮询间隔

不改动的部分

  • 0xC4 ADC、0xC5 motor state、0xC6 motor version、0xD2 motor OTA 推送机制不变
  • canFdParseFeedbackPage 解析逻辑不变
  • GD32 handleFeedback 查询处理程序不变
  • 前端 JS 不变(API 响应格式不变)