feat!: 1.6.0 DWT 剖分探针 + 1.6.1 输入 PDO 瘦身 640→496(待重烧验收)

1.6.0 剖分探针(上一轮真机已验证, 随本轮一并入库):
- ecatappl.c: 0x2000 sub5..12 DWT 周期累加器(PDI_Isr/输入映射/640B 写细分)
- ethercat_pdo.cpp: 输出按需全量读(12B 探针判 seq 沿, 省每周期 ~190µs 全量读)
- 段5 诊断区(负载率+seq)随 PDO 上报, 主站 /api/app/load 轮询

1.6.1 输入 PDO 瘦身 640→496(62×64bit; 0..359 与 1.5.61 逐字节兼容):
- 依据: DWT 实测 640B 写恒 296µs/拍且与 SYNC0 相位无关(主站 dc_bench 五形态
  对拍极差 0.04%), PRAM ≈0.463µs/B 线性; 真机 6086 帧 DLC 仅 24/32
- 批量接收条目 8B 头+64B 数据 → 8B+32B(DLC 真值保留, 截断打 flags bit1,
  环形队列仍存全量 64B); 段5 诊断区 632→484
- 预期写侧 −67µs/拍(→~230µs, 占空 29.7%→23%); LRW 帧 1024→880B
- 五处一致: ethercat_pdo_size.h(真源) / dc_app.c(62 条目+映射初始化器) /
  gen_eeprom.py(PD_IN_SIZE, nEntry 自动推导) / DCU_ESI.xml(SM3 496+62 条目) /
  bridge.h(kEcatRxBatchDataMax=32, 断言更新)
- 主站 tools/ecat decode_input 按 Ibytes 双布局自适应, 固件/EEPROM 与主站可
  任意先后升级; 混烧(只烧一侧)会被 SM 长度校验拦下(AL 0x001A)
- AC6 编译 0 警告; 真机验收待: 烧 axf + ecat.bat flash + 断电冷启动
  (流程与复验判据见 tools/ecat/MIGRATION.md 1.6.1 节)
This commit is contained in:
cbb
2026-09-06 00:19:18 +08:00
parent 70daa4b0c6
commit 9f1c2d25dd
11 changed files with 315 additions and 185 deletions
+38 -4
View File
@@ -119,19 +119,26 @@ void EthercatCanBridge::fillRxBatch(uint8_t *in)
if (dlc > kEcatCanFdDataMax) {
dlc = kEcatCanFdDataMax;
}
/* 1.6.1: PDO 内数据条目 32B 上限——DLC 字节填**真值**,超出部分截断并打
* flags bit1;环形队列里仍是全量 64B,截断只发生在 PDO 快照这一步。
* 主站按 "8 + min(dlc, kEcatRxBatchDataMax)" 推进游标。 */
uint8_t dlcWire = dlc;
if (dlcWire > kEcatRxBatchDataMax) {
dlcWire = kEcatRxBatchDataMax;
}
/* 装不下就停,**且不推进 tail** —— 该帧留到下个周期,不丢。 */
if ((uint16_t)(off + ECAT_FRM_HDR_SIZE + dlc) > kEcatPdoInBytes) {
if ((uint16_t)(off + ECAT_FRM_HDR_SIZE + dlcWire) > kEcatPdoInBytes) {
break;
}
uint8_t *p = &in[off];
p[ECAT_FRM_OFF_CH] = it.ch;
p[ECAT_FRM_OFF_DLC] = dlc;
p[ECAT_FRM_OFF_FLAGS] = 0x01U;
p[ECAT_FRM_OFF_FLAGS] = (uint8_t)(0x01U | (dlc > kEcatRxBatchDataMax ? 0x02U : 0x00U));
wr32le(&p[ECAT_FRM_OFF_ID], it.id);
memcpy(&p[ECAT_FRM_HDR_SIZE], it.data, dlc);
memcpy(&p[ECAT_FRM_HDR_SIZE], it.data, dlcWire);
off = (uint16_t)(off + ECAT_FRM_HDR_SIZE + dlc);
off = (uint16_t)(off + ECAT_FRM_HDR_SIZE + dlcWire);
tail = (uint8_t)((tail + 1U) % kEcatRxRingSlots);
++count;
}
@@ -145,6 +152,33 @@ void EthercatCanBridge::fillRxBatch(uint8_t *in)
s_rxRingLost = 0U; /* 回填后清零:lost 是"自上次回填以来"的增量 */
}
bool EthercatCanBridge::peekOutput(const uint8_t *head)
{
if (head == nullptr) {
return false;
}
const uint8_t seq = head[ECAT_CAN_TX_OFF_SEQ];
/* 窗口下标每周期都必须刷新(主站靠滚动 WIN 扫 27 槽),与 seq 沿无关。
* 越界值钳到 0,避免读到槽数组外(与 processOutput 同款钳制)。 */
const uint8_t win = head[ECAT_CAN_TX_OFF_WIN];
s_winBase = (win < kMotorSlotCount) ? win : 0U;
/* 上电/重连后首帧只记基准(与 processOutput 的 priming 分支语义一致) */
if (!s_seqPrimed) {
s_seqPrimed = true;
s_lastSeq = seq;
return false;
}
/* seq 沿 → 全量读;FLAGS bit0(清统计请求)允许不带 seq 沿(同 processOutput)。
* s_lastSeq 不在这里推进:全量路径上 processOutput 的沿处理只做一次。 */
if (seq != s_lastSeq) {
return true;
}
return (head[ECAT_CAN_TX_OFF_FLAGS] & 0x01U) != 0U;
}
void EthercatCanBridge::processOutput(const uint8_t *out)
{
if (out == nullptr) {