From 9f1c2d25dd3ca336c1b4eeea33a19565b8d3040d Mon Sep 17 00:00:00 2001
From: cbb <2625274797@qq.com>
Date: Sun, 6 Sep 2026 00:19:18 +0800
Subject: [PATCH] =?UTF-8?q?feat!:=201.6.0=20DWT=20=E5=89=96=E5=88=86?=
=?UTF-8?q?=E6=8E=A2=E9=92=88=20+=201.6.1=20=E8=BE=93=E5=85=A5=20PDO=20?=
=?UTF-8?q?=E7=98=A6=E8=BA=AB=20640=E2=86=92496(=E5=BE=85=E9=87=8D?=
=?UTF-8?q?=E7=83=A7=E9=AA=8C=E6=94=B6)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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 节)
---
GD32E508_DCU_APP/comm/esc_lan9252.cpp | 75 ++++++---
GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp | 42 ++++-
GD32E508_DCU_APP/comm/ethercat_can_bridge.h | 47 ++++--
GD32E508_DCU_APP/comm/ethercat_pdo.cpp | 12 ++
GD32E508_DCU_APP/comm/ethercat_pdo.h | 9 ++
GD32E508_DCU_APP/comm/ethercat_pdo_size.h | 20 ++-
GD32E508_DCU_APP/comm/ethercat_sync.cpp | 3 +
GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml | 148 +++---------------
GD32E508_DCU_APP/comm/ssc/port/dc_app.c | 60 +++++--
GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py | 5 +-
GD32E508_DCU_APP/comm/ssc/src/ecatappl.c | 79 +++++++++-
11 files changed, 315 insertions(+), 185 deletions(-)
diff --git a/GD32E508_DCU_APP/comm/esc_lan9252.cpp b/GD32E508_DCU_APP/comm/esc_lan9252.cpp
index f331d13..24c3b8b 100644
--- a/GD32E508_DCU_APP/comm/esc_lan9252.cpp
+++ b/GD32E508_DCU_APP/comm/esc_lan9252.cpp
@@ -367,7 +367,16 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
{
/* DS 12.11.3:写 ADDR_LEN 再置 BUSY 启动;引擎按 DWORD 向下对齐填充读 FIFO,
* 首 DWORD 有效字节从 (addr&3) 起(表 12-7),末 DWORD 尾部无效(表 12-8)。
- * 别名口 0x00-0x1C 功能相同,轮换寻址即官方突发用法;每读满一个 DWORD 弹一项。 */
+ * 别名口 0x00-0x1C 功能相同,轮换寻址即官方突发用法;每读满一个 DWORD 弹一项。
+ *
+ * 1.6.0 批量搬运(负载优化):旧实现每搬一个 DWORD 都重新轮询一次 CMD 的
+ * AVAIL 位——每次轮询是 setHbiIndex+读低/高半字共 3 次 EXMC 事务,数据本身
+ * 又 3 次,单周期 1024B 过程数据因此跑 ~1600 次 EXMC 访问,是 GD32 侧 CPU
+ * 占用的大头。DS 12.13.6:ECAT_PRAM_RD_CMD bits[12:8] = PRAM_READ_AVAIL_CNT,
+ * 指示"无需再查状态即可继续读 FIFO 的次数"(≤16,每次整 DWORD 读自减)。
+ * 轮询命中时顺手取该计数,一口气读 min(CNT, 8[别名窗口一程], 剩余 DWORD)个,
+ * 轮询开销从"每 DWORD 一次"降到"每批一次"。字节装配/lead 逻辑逐字节不变,
+ * 任意 (addr,len) 组合的搬运结果与旧实现完全一致。 */
if (len == 0U) {
return;
}
@@ -377,25 +386,41 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
uint16_t alias = 0U;
while (done < len) {
uint32_t n = POLL_MAX;
- while ((readReg32(ECAT_PRAM_RD_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
+ uint32_t cmd;
+ while (((cmd = readReg32(ECAT_PRAM_RD_CMD)) & PRAM_AVAIL) == 0U && --n != 0U) {}
if (n == 0U) {
break; /* 芯片异常:避免死等,交出残缺数据 */
}
- const uint32_t dw = readReg32((uint16_t)(ECAT_PRAM_RD_DATA + alias));
- alias = (uint16_t)((alias + 4U) & 0x1FU);
- const uint8_t lead = (done == 0U) ? (uint8_t)(addr & 3U) : 0U;
- const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
- for (uint8_t i = 0U; i < take; ++i) {
- buf[done + i] = (uint8_t)(dw >> (8U * (lead + i)));
+ uint32_t batch = (cmd >> 8) & 0x1FU;
+ if (batch == 0U || batch > 8U) {
+ batch = 1U; /* 计数字段异常时退回单拍,等价旧实现 */
+ }
+ /* 本批以"吃完全部剩余字节"为上限(lead 只在首 DWORD 出现) */
+ const uint32_t lead0 = (done == 0U) ? (uint32_t)(addr & 3U) : 0U;
+ const uint32_t needDW = ((uint32_t)(len - done) + lead0 + 3U) / 4U;
+ if (batch > needDW) {
+ batch = needDW;
+ }
+ while (batch-- != 0U) {
+ const uint32_t dw = readReg32((uint16_t)(ECAT_PRAM_RD_DATA + alias));
+ alias = (uint16_t)((alias + 4U) & 0x1FU);
+ const uint8_t lead = (done == 0U) ? (uint8_t)(addr & 3U) : 0U;
+ const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
+ for (uint8_t i = 0U; i < take; ++i) {
+ buf[done + i] = (uint8_t)(dw >> (8U * (lead + i)));
+ }
+ done = (uint16_t)(done + take);
}
- done = (uint16_t)(done + take);
}
}
void EscLan9252::pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len)
{
/* DS 12.11.4:写 ADDR_LEN 再置 BUSY 启动;首 DWORD 有效字节从 (addr&3) 起,
- * 引擎只取有效字节;别名口 0x20-0x3C 轮换;写满一个 DWORD 推一项。 */
+ * 引擎只取有效字节;别名口 0x20-0x3C 轮换;写满一个 DWORD 推一项。
+ * 1.6.0 批量搬运:与 pramRead 同理,DS 12.13.7 bits[12:8] =
+ * PRAM_WRITE_AVAIL_CNT 指示"无需再查状态即可继续写 FIFO 的次数",
+ * 轮询从"每 DWORD 一次"降到"每批一次",单周期 EXMC 事务约减半。 */
if (len == 0U) {
return;
}
@@ -405,19 +430,31 @@ void EscLan9252::pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len)
uint16_t alias = 0U;
while (done < len) {
uint32_t n = POLL_MAX;
- while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
+ uint32_t cmd;
+ while (((cmd = readReg32(ECAT_PRAM_WR_CMD)) & PRAM_AVAIL) == 0U && --n != 0U) {}
if (n == 0U) {
break; /* 芯片异常:放弃剩余字节 */
}
- const uint8_t lead = (done == 0U) ? (uint8_t)((uint16_t)(addr + done) & 3U) : 0U;
- const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
- uint32_t dw = 0U;
- for (uint8_t i = 0U; i < take; ++i) {
- dw |= (uint32_t)buf[done + i] << (8U * (lead + i));
+ uint32_t batch = (cmd >> 8) & 0x1FU;
+ if (batch == 0U || batch > 8U) {
+ batch = 1U; /* 计数字段异常时退回单拍,等价旧实现 */
+ }
+ const uint32_t lead0 = (uint32_t)((uint16_t)(addr + done) & 3U);
+ const uint32_t needDW = ((uint32_t)(len - done) + lead0 + 3U) / 4U;
+ if (batch > needDW) {
+ batch = needDW;
+ }
+ while (batch-- != 0U) {
+ const uint8_t lead = (done == 0U) ? (uint8_t)((uint16_t)(addr + done) & 3U) : 0U;
+ const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
+ uint32_t dw = 0U;
+ for (uint8_t i = 0U; i < take; ++i) {
+ dw |= (uint32_t)buf[done + i] << (8U * (lead + i));
+ }
+ writeReg32((uint16_t)(ECAT_PRAM_WR_DATA + alias), dw);
+ alias = (uint16_t)((alias + 4U) & 0x1FU);
+ done = (uint16_t)(done + take);
}
- writeReg32((uint16_t)(ECAT_PRAM_WR_DATA + alias), dw);
- alias = (uint16_t)((alias + 4U) & 0x1FU);
- done = (uint16_t)(done + take);
}
uint32_t n = POLL_MAX;
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_BUSY) != 0U && --n != 0U) {}
diff --git a/GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp b/GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp
index f697dd9..da2b8ba 100644
--- a/GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp
+++ b/GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp
@@ -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) {
diff --git a/GD32E508_DCU_APP/comm/ethercat_can_bridge.h b/GD32E508_DCU_APP/comm/ethercat_can_bridge.h
index b345d5c..dabbabe 100644
--- a/GD32E508_DCU_APP/comm/ethercat_can_bridge.h
+++ b/GD32E508_DCU_APP/comm/ethercat_can_bridge.h
@@ -37,7 +37,8 @@
*
* 新增两个批量区,每条帧带 8B 头(ch/dlc/flags/rsvd + id)+ 数据:
* 输出 48..383(336B):批量发送,一个 seq 沿一次性发 N 帧
- * 输入 360..639(280B):批量接收,一周期回收多帧而不是只带最近一帧
+ * 输入 360..483(124B):批量接收,一周期回收多帧而不是只带最近一帧
+ * (1.6.1 数据条目 64→32B,段 5 诊断区 484..491,原 360..639/280B)
*
* 三条兼容性铁律(改动只做追加,不动既有偏移):
* 1. 输出 0..47 与 1.5.55 单帧布局完全一致(seq/ch/dlc/flags/id/win/data32);
@@ -128,7 +129,9 @@ enum : uint16_t {
ECAT_XRX_OFF_DATA = 328U, /**< 数据区,32 字节完整 payload */
};
-/** 输入 PDO 段 4(360..639):CANFD 批量接收区(1.5.56 新增)。 */
+/** 输入 PDO 段 4(360..483):CANFD 批量接收区(1.5.56 新增,1.6.1 数据条目 64→32B)。
+ * 每项 8B 头 + 最多 32B 数据(kEcatRxBatchDataMax);DLC 字节仍填**原始值**,
+ * 超长帧被截断时 flags bit1=1 —— 主站按 "8 + min(dlc,32)" 推进游标。 */
enum : uint16_t {
ECAT_RXB_OFF_COUNT = 360U, /**< 本周期回收的帧数 0..kEcatRxBatchMax */
ECAT_RXB_OFF_LOST = 361U, /**< 本周期因批量区放不下而丢弃的帧数 */
@@ -137,15 +140,15 @@ enum : uint16_t {
ECAT_RXB_OFF_ITEMS = 364U, /**< 帧数组起点,项布局同 ECAT_FRM_OFF_* */
};
-/** 输入 PDO 段 5(632..639):设备诊断区(1.5.61 新增,尾部追加)。
- * 批量接收区最坏情况(全 DLC64 满帧)只用到 364+3×72=580,580..639 永久空闲,
- * 诊断区锚在 632 起的 8 字节,写侧是 EthercatPdo::process()。 */
+/** 输入 PDO 段 5(484..491):设备诊断区(1.5.61 尾部追加,1.6.1 随批量区
+ * 数据条目 64→32B 前移:364+3×40=484,诊断区锚在紧随其后的 8 字节,
+ * 写侧是 EthercatPdo::fillDiagTail())。 */
enum : uint16_t {
- ECAT_DIAG_OFF_LOAD = 632U, /**< 负载率,小端 uint16,单位 0.1%(1000=100%)。
+ ECAT_DIAG_OFF_LOAD = 484U, /**< 负载率,小端 uint16,单位 0.1%(1000=100%)。
中断 + 非延迟处理段(任务派发/OTA relay)占墙钟比,
不含常开轮询与空转;1s 滚动窗口(见 sys/cpu_load.h) */
- ECAT_DIAG_OFF_RSVD = 634U, /**< 保留 2B */
- ECAT_DIAG_OFF_SEQ = 636U, /**< 诊断区序号,小端 uint32,每完成一个统计窗口 +1,
+ ECAT_DIAG_OFF_RSVD = 486U, /**< 保留 2B */
+ ECAT_DIAG_OFF_SEQ = 488U, /**< 诊断区序号,小端 uint32,每完成一个统计窗口 +1,
主站据此判断负载值是否仍在滚动刷新 */
};
@@ -163,20 +166,25 @@ constexpr uint8_t kEcatMotSlotsPerPdo = 7U;
/** 过程数据长度,唯一真源见 comm/ethercat_pdo_size.h(C/C++ 共用)。 */
constexpr uint16_t kEcatPdoOutBytes = ECAT_PDO_OUT_BYTES; /* 48×64bit = 384B */
-constexpr uint16_t kEcatPdoInBytes = ECAT_PDO_IN_BYTES; /* 80×64bit = 640B */
+constexpr uint16_t kEcatPdoInBytes = ECAT_PDO_IN_BYTES; /* 62×64bit = 496B */
-/** CANFD 单帧数据上限(DLC 64)—— 批量区按此上限做容量推导。 */
+/** CANFD 单帧数据上限(DLC 64)—— 环形队列/发送批量区按此上限做容量推导。 */
constexpr uint8_t kEcatCanFdDataMax = 64U;
+/** 批量**接收**条目在 PDO 内的数据字节数上限(1.6.1 从 64 压到 32)。
+ * 真机采样 6086 帧 DLC 只有 24/32 两档;超长帧在装填时截断(flags bit1),
+ * 环形队列 RxRingItem 仍存全量 64B,截断只发生在 PDO 快照这一步。 */
+constexpr uint8_t kEcatRxBatchDataMax = 32U;
+
/** 批量发送区可用字节数与最坏情况(全 DLC64)帧数上限。 */
constexpr uint16_t kEcatTxBatchBytes = (uint16_t)(kEcatPdoOutBytes - ECAT_TXB_OFF_ITEMS);
constexpr uint8_t kEcatTxBatchMax =
(uint8_t)(kEcatTxBatchBytes / (ECAT_FRM_HDR_SIZE + kEcatCanFdDataMax));
-/** 批量接收区可用字节数与最坏情况帧数上限。 */
+/** 批量接收区可用字节数与最坏情况(全 DLC32 截断满帧)帧数上限。 */
constexpr uint16_t kEcatRxBatchBytes = (uint16_t)(kEcatPdoInBytes - ECAT_RXB_OFF_ITEMS);
constexpr uint8_t kEcatRxBatchMax =
- (uint8_t)(kEcatRxBatchBytes / (ECAT_FRM_HDR_SIZE + kEcatCanFdDataMax));
+ (uint8_t)(kEcatRxBatchBytes / (ECAT_FRM_HDR_SIZE + kEcatRxBatchDataMax));
/** 批量接收环形队列槽数。取 kEcatRxBatchMax 的 4 倍,让主站慢一两拍也不丢帧。
* 注意这是 RAM 成本(槽数 × 72B),不是 PDO 成本。 */
@@ -194,9 +202,9 @@ static_assert(ECAT_RXB_OFF_ITEMS < kEcatPdoInBytes, "批量接收区起点越界
static_assert(ECAT_TXB_OFF_ITEMS < kEcatPdoOutBytes, "批量发送区起点越界");
/* 批量区至少要装得下一帧满长 CANFD,否则"批量"没有意义 */
static_assert(kEcatTxBatchMax >= 1U, "批量发送区装不下一帧 DLC64");
-static_assert(kEcatRxBatchMax >= 1U, "批量接收区装不下一帧 DLC64");
+static_assert(kEcatRxBatchMax >= 1U, "批量接收区装不下一帧 DLC32");
/* 段 5 诊断区必须在批量接收区的最坏占用之外(改批量区布局时一起核对) */
-static_assert(ECAT_RXB_OFF_ITEMS + (uint16_t)kEcatRxBatchMax * (ECAT_FRM_HDR_SIZE + kEcatCanFdDataMax)
+static_assert(ECAT_RXB_OFF_ITEMS + (uint16_t)kEcatRxBatchMax * (ECAT_FRM_HDR_SIZE + kEcatRxBatchDataMax)
<= ECAT_DIAG_OFF_LOAD, "批量接收区最坏占用与段5诊断区重叠");
/**
@@ -215,6 +223,17 @@ public:
* @param[in] out kEcatPdoOutBytes 字节输出过程数据。
* @note 由 APPL_Application() 每周期调用。
*/
+ /** @brief 输出探针(1.6.0 输出按需全量读)。
+ *
+ * PDI_Isr 每周期先读输出 PDO 头 12B(覆盖 SEQ@0 与 WIN@8),由本函数判定
+ * 是否需要全量读 384B:
+ * - 始终从探针刷新 s_winBase(主站每周期移动窗口扫描,与 seq 沿无关);
+ * - 首帧只记 seq 基准(与 processOutput 的 priming 一致),返回 false;
+ * - seq 变化沿或 FLAGS bit0(清统计请求)→ 返回 true,调用方全量读。
+ * s_lastSeq 仍由 processOutput 在全量路径上推进,本函数不写,保证沿语义
+ * 只处理一次。 */
+ static bool peekOutput(const uint8_t *head);
+
static void processOutput(const uint8_t *out);
/**
diff --git a/GD32E508_DCU_APP/comm/ethercat_pdo.cpp b/GD32E508_DCU_APP/comm/ethercat_pdo.cpp
index d85b81c..ba09cf8 100644
--- a/GD32E508_DCU_APP/comm/ethercat_pdo.cpp
+++ b/GD32E508_DCU_APP/comm/ethercat_pdo.cpp
@@ -23,6 +23,11 @@ void EthercatPdo::readOutputs(const uint8_t *esc)
++s_outputCycles;
}
+bool EthercatPdo::outputProbe(const uint8_t *head)
+{
+ return EthercatCanBridge::peekOutput(head);
+}
+
void EthercatPdo::process()
{
/* PDO ↔ CAN 转发:输出 PDO 的 seq 字节变化沿触发一次 CAN 发送,
@@ -74,6 +79,13 @@ void EthercatPdo::snapshotOutput(uint8_t *dst)
extern "C" {
+/* 1.6.0:输出探针,SSC PDI_Isr 的 PDO_OutputMapping 每周期调用(C 链接)。
+ * 返回 0/1;SSC 侧声明为 UINT8,小返回值零扩展,调用约定兼容。 */
+uint8_t EcatPdo_OutputProbe(const uint8_t *head)
+{
+ return EthercatPdo::outputProbe(head) ? 1U : 0U;
+}
+
void EcatPdo_ReadOutputs(const uint8_t *esc)
{
EthercatPdo::readOutputs(esc);
diff --git a/GD32E508_DCU_APP/comm/ethercat_pdo.h b/GD32E508_DCU_APP/comm/ethercat_pdo.h
index 9e54a6a..64bf8bf 100644
--- a/GD32E508_DCU_APP/comm/ethercat_pdo.h
+++ b/GD32E508_DCU_APP/comm/ethercat_pdo.h
@@ -48,6 +48,15 @@ public:
*/
static void readOutputs(const uint8_t *esc);
+ /**
+ * @brief 输出探针(1.6.0 输出按需全量读,配合 SSC PDO_OutputMapping)。
+ * @param[in] head 输出 PDO 头 12B(覆盖 SEQ@0 / WIN@8 / FLAGS@3)。
+ * @return true=需要全量读(seq 沿或清统计请求);false=本周期跳过全量读。
+ * @note 每周期由 PDI_Isr 在读 ESC 前调用:内部刷新桥的 s_winBase 并做 seq
+ * 基准 priming;s_lastSeq 的推进仍留在全量路径(processOutput)。
+ */
+ static bool outputProbe(const uint8_t *head);
+
/**
* @brief 跑一个应用周期:消费输出、生成输入。
* @note 由 APPL_Application() 在主循环调用。
diff --git a/GD32E508_DCU_APP/comm/ethercat_pdo_size.h b/GD32E508_DCU_APP/comm/ethercat_pdo_size.h
index 623233a..7285d3c 100644
--- a/GD32E508_DCU_APP/comm/ethercat_pdo_size.h
+++ b/GD32E508_DCU_APP/comm/ethercat_pdo_size.h
@@ -50,8 +50,24 @@
/** 输出过程数据字节数(主站→从站,SM2):48×64bit。 */
#define ECAT_PDO_OUT_BYTES 384
-/** 输入过程数据字节数(从站→主站,SM3):80×64bit。 */
-#define ECAT_PDO_IN_BYTES 640
+/**
+ * 输入过程数据字节数(从站→主站,SM3):62×64bit = 496B。
+ *
+ * ── 1.6.1 瘦身 640 → 496(2026-09-05 真机剖分后的决定)──
+ *
+ * 固件 DWT 探针(0x2000 sub9/sub10)实测:PDO_InputMapping 里 HW_EscWriteIsr
+ * 640B 写恒为 296µs/拍(29.7% CPU),且与 SYNC0 相位**无关**(主站 dc_bench
+ * 五形态对拍极差 0.04%)——PRAM 通道 ≈0.463µs/B 线性,写多少省多少,唯一的
+ * 减法就是减字节。真机 CAN 流量采样(6086 帧)DLC 只有 24/32 两档,批量接收
+ * 区每帧 64B 数据是纯富余:
+ * - 批量接收条目 8B 头 + 64B 数据 → 8B 头 + 32B 数据(DLC 真值保留,
+ * 超长帧截断、flags bit1 标记;环形队列仍存全量 64B,不丢原始数据);
+ * - 段 5 诊断区从 632..639(批量区最坏占用后的尾部追加)挪到 484..491;
+ * - 段 1 / 电机窗口 / 长帧快照全部不动(0..359 与 1.5.61 逐字节兼容)。
+ * 496 = 62×8;SM3 实占 3×496=1488B(0x1880-0x1E4F),out+in=880 ≤ 1024 硬上限,
+ * DPRAM 反而留出 432B 余量。改这里必须同步重生成 SII(gen_eeprom.py)并断电冷启动。
+ */
+#define ECAT_PDO_IN_BYTES 496
/** DPRAM 中过程数据区起址与 SM 缓冲数 —— 上限推导的依据,供静态断言使用。 */
#define ECAT_PD_AREA_START 0x1400
diff --git a/GD32E508_DCU_APP/comm/ethercat_sync.cpp b/GD32E508_DCU_APP/comm/ethercat_sync.cpp
index 802096b..353640a 100644
--- a/GD32E508_DCU_APP/comm/ethercat_sync.cpp
+++ b/GD32E508_DCU_APP/comm/ethercat_sync.cpp
@@ -194,4 +194,7 @@ uint32_t EcatDbg_GetSync1(void) { return EthercatSync::getSync1Count(); }
uint32_t EcatDbg_GetEscIrq(void) { return EthercatSync::getEscIrqCount(); }
uint32_t EcatDbg_GetUptimeSec(void){ return g_app_uptime_sec; }
+/* 1.6.0 剖分埋点:供 ecatappl.c (C) 读取 DWT 周期计数 */
+uint32_t EcatDbg_Dwt(void) { return DWT->CYCCNT; }
+
} /* extern "C" */
diff --git a/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml b/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml
index a920756..e2f0a11 100644
--- a/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml
+++ b/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml
@@ -25,7 +25,7 @@
Outputs
- Inputs
+ Inputs
#x1A00
@@ -812,132 +814,24 @@
Status Data 62
ULINT
-
- #x6000
- 63
- 64
- Status Data 63
- ULINT
-
-
- #x6000
- 64
- 64
- Status Data 64
- ULINT
-
-
- #x6000
- 65
- 64
- Status Data 65
- ULINT
-
-
- #x6000
- 66
- 64
- Status Data 66
- ULINT
-
-
- #x6000
- 67
- 64
- Status Data 67
- ULINT
-
-
- #x6000
- 68
- 64
- Status Data 68
- ULINT
-
-
- #x6000
- 69
- 64
- Status Data 69
- ULINT
-
-
- #x6000
- 70
- 64
- Status Data 70
- ULINT
-
-
- #x6000
- 71
- 64
- Status Data 71
- ULINT
-
-
- #x6000
- 72
- 64
- Status Data 72
- ULINT
-
-
- #x6000
- 73
- 64
- Status Data 73
- ULINT
-
-
- #x6000
- 74
- 64
- Status Data 74
- ULINT
-
-
- #x6000
- 75
- 64
- Status Data 75
- ULINT
-
-
- #x6000
- 76
- 64
- Status Data 76
- ULINT
-
-
- #x6000
- 77
- 64
- Status Data 77
- ULINT
-
-
- #x6000
- 78
- 64
- Status Data 78
- ULINT
-
-
- #x6000
- 79
- 64
- Status Data 79
- ULINT
-
-
- #x6000
- 80
- 64
- Status Data 80
- ULINT
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GD32E508_DCU_APP/comm/ssc/port/dc_app.c b/GD32E508_DCU_APP/comm/ssc/port/dc_app.c
index 3a5d941..fa64ae4 100644
--- a/GD32E508_DCU_APP/comm/ssc/port/dc_app.c
+++ b/GD32E508_DCU_APP/comm/ssc/port/dc_app.c
@@ -40,13 +40,15 @@
* PDO 数据缓冲
*
* 1.5.56 扩到 DPRAM 硬上限:输出 48×64bit=384B,输入 80×64bit=640B(非对称)。
+ * 1.6.1 输入瘦身 640→496(62×64bit):批量接收条目数据 64→32B + 诊断区前移,
+ * 依据与推导见 comm/ethercat_pdo_size.h 的 1.6.1 注。
* 长度的唯一真源是 comm/ethercat_pdo_size.h;本文件用 _Static_assert 与之绑定,
* 改一处漏一处会编译失败。为何硬上限是 out+in≤1024B 见该头文件的推导。
*---------------------------------------------------------------------------*/
/** 输出/输入 PDO 的 64bit 条目数。 */
#define PDO_OUT_ENTRIES 48u /* 48×8 = 384 B */
-#define PDO_IN_ENTRIES 80u /* 80×8 = 640 B */
+#define PDO_IN_ENTRIES 62u /* 62×8 = 496 B (1.6.1 瘦身,原 80×8=640) */
/** 输出数据对象 0x7000 (主站→从站, RxPDO) */
typedef struct OBJ_STRUCT_PACKED_START {
@@ -71,7 +73,7 @@ static TOBJ6000 s_inputObj = {PDO_IN_ENTRIES, {0}};
_Static_assert(offsetof(TOBJ7000, u64Data) == 2, "TOBJ7000 未紧排:检查 OBJ_STRUCT_PACKED_START");
_Static_assert(offsetof(TOBJ6000, u64Data) == 2, "TOBJ6000 未紧排:检查 OBJ_STRUCT_PACKED_START");
_Static_assert(sizeof(TOBJ7000) == 2 + 8 * PDO_OUT_ENTRIES, "TOBJ7000 尺寸应为 2+384");
-_Static_assert(sizeof(TOBJ6000) == 2 + 8 * PDO_IN_ENTRIES, "TOBJ6000 尺寸应为 2+640");
+_Static_assert(sizeof(TOBJ6000) == 2 + 8 * PDO_IN_ENTRIES, "TOBJ6000 尺寸应为 2+496");
/* 与 C++ 侧桥接层的长度约定绑定:任一处改了尺寸而忘了另一处,这里直接编译失败,
* 而不是等到运行期 SAFEOP 报 AL 0x001A/0x0011 再回头查。 */
@@ -94,10 +96,19 @@ typedef struct OBJ_STRUCT_PACKED_START {
UINT32 u32Sync1; /* sub2 */
UINT32 u32EscIrq; /* sub3 */
UINT32 u32UptimeS; /* sub4 */
+ /* 1.6.0 剖分埋点(sub5..12,DWT 周期/计数累加,主站 1s 差分;定位完即移除) */
+ UINT32 u32PdiCyc; /* sub5: PDI_Isr 全函数累计 */
+ UINT32 u32OutCyc; /* sub6: PDO_OutputMapping(输出探针/全量读) */
+ UINT32 u32FreeRunCyc; /* sub7: MainLoop free-run 块(SM 同步下恒 0) */
+ UINT32 u32PdiCnt; /* sub8: PDI_Isr 触发次数 */
+ UINT32 u32InCyc; /* sub9: PDO_InputMapping 全程(APPL+输入PDO写) */
+ UINT32 u32InWriteCyc; /* sub10: 其中 HW_EscWriteIsr 输入PDO 过程数据写 */
+ UINT32 u32AlCyc; /* sub11: PDI_Isr 内 AL 事件读×2+超限应答 */
+ UINT32 u32AppCyc; /* sub12: ECAT_Application(桥 processOutput) */
} OBJ_STRUCT_PACKED_END
TOBJ2000;
-static TOBJ2000 s_syncDiag = {4, 0, 0, 0, 0};
+static TOBJ2000 s_syncDiag = {12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* 同上:UINT32 首条目必须落在字节 2(未紧排时为 4)。 */
_Static_assert(offsetof(TOBJ2000, u32Sync0) == 2, "TOBJ2000 未紧排:检查 OBJ_STRUCT_PACKED_START");
@@ -107,6 +118,16 @@ extern UINT32 EcatDbg_GetSync0(void);
extern UINT32 EcatDbg_GetSync1(void);
extern UINT32 EcatDbg_GetEscIrq(void);
extern UINT32 EcatDbg_GetUptimeSec(void);
+/* 1.6.0 剖分埋点:ecatappl.c 累加器 + ethercat_sync.cpp 的 DWT 读 */
+extern UINT32 EcatDbg_Dwt(void);
+extern volatile UINT32 g_dbg_pdiCyc;
+extern volatile UINT32 g_dbg_outCyc;
+extern volatile UINT32 g_dbg_freerunCyc;
+extern volatile UINT32 g_dbg_pdiCnt;
+extern volatile UINT32 g_dbg_inCyc;
+extern volatile UINT32 g_dbg_inWriteCyc;
+extern volatile UINT32 g_dbg_alCyc;
+extern volatile UINT32 g_dbg_appCyc;
/* 过程数据收发模块 (comm/ethercat_pdo.cpp 的 extern "C" shim, 1.5.54)。
* 1.5.53 时这里直接调 EcatCan_* 转发桥;1.5.54 把"一个 EtherCAT 周期怎么收发"
@@ -161,12 +182,13 @@ static TOBJ_RXPDO_MAPPING sRxPDOMap = {PDO_OUT_ENTRIES, {
PDO_MAP_8(0x7000, 24), PDO_MAP_8(0x7000, 32), PDO_MAP_8(0x7000, 40)
}};
-/* 0x1A00: 映射 0x6000:01..80, 每个 64 bits */
+/* 0x1A00: 映射 0x6000:01..62, 每个 64 bits(1.6.1 瘦身 80→62:7×8 + 6) */
static TOBJ_TXPDO_MAPPING sTxPDOMap = {PDO_IN_ENTRIES, {
PDO_MAP_8(0x6000, 0), PDO_MAP_8(0x6000, 8), PDO_MAP_8(0x6000, 16),
PDO_MAP_8(0x6000, 24), PDO_MAP_8(0x6000, 32), PDO_MAP_8(0x6000, 40),
- PDO_MAP_8(0x6000, 48), PDO_MAP_8(0x6000, 56), PDO_MAP_8(0x6000, 64),
- PDO_MAP_8(0x6000, 72)
+ PDO_MAP_8(0x6000, 48),
+ PDO_MAP_ENTRY(0x6000, 57), PDO_MAP_ENTRY(0x6000, 58), PDO_MAP_ENTRY(0x6000, 59),
+ PDO_MAP_ENTRY(0x6000, 60), PDO_MAP_ENTRY(0x6000, 61), PDO_MAP_ENTRY(0x6000, 62)
}};
_Static_assert(sizeof(sRxPDOMap.aEntries) / 4 == PDO_OUT_ENTRIES, "0x1600 条目数不符");
@@ -215,12 +237,21 @@ OBJCONST TSDOINFOENTRYDESC OBJMEM asInputEntryDesc[] = {
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING}
};
-/* SYNC 诊断 0x2000: REC, Sub0..Sub4 共 5 项, 纯只读不参与 PDO 映射。 */
+/* SYNC 诊断 0x2000: REC, Sub0..Sub12 共 13 项, 纯只读不参与 PDO 映射。
+ * sub5..12 为 1.6.0 剖分埋点(DWT 周期/计数累加), 定位负载构成后移除。 */
OBJCONST TSDOINFOENTRYDESC OBJMEM asSyncDiagEntryDesc[] = {
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
+ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}
};
@@ -265,8 +296,8 @@ TOBJECT OBJMEM ApplicationObjDicList[] = {
{NULL, NULL, 0x6000, {DEFTYPE_UNSIGNED64, PDO_IN_ENTRIES | (OBJCODE_ARR << 8)},
asInputEntryDesc, aName0x6000, &s_inputObj, NULL, NULL, 0x0000},
- /* 0x2000: SYNC 诊断 (只读, 主站 SDO 轮询验证 DC 通路) */
- {NULL, NULL, 0x2000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
+ /* 0x2000: SYNC 诊断 (只读, 主站 SDO 轮询验证 DC 通路; sub5..8 为 1.6.0 剖分埋点) */
+ {NULL, NULL, 0x2000, {DEFTYPE_RECORD, 12 | (OBJCODE_REC << 8)},
asSyncDiagEntryDesc, aName0x2000, &s_syncDiag, NULL, NULL, 0x0000},
/* 终止条目 */
@@ -280,7 +311,7 @@ TOBJECT OBJMEM * VARMEM ApplicationObjDic = ApplicationObjDicList;
*---------------------------------------------------------------------------*/
UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize)
{
- *pInputSize = ECAT_PDO_IN_BYTES; /* 80×UINT64 = 640 bytes */
+ *pInputSize = ECAT_PDO_IN_BYTES; /* 62×UINT64 = 496 bytes (1.6.1) */
*pOutputSize = ECAT_PDO_OUT_BYTES; /* 48×UINT64 = 384 bytes */
return 0;
}
@@ -340,6 +371,15 @@ void APPL_InputMapping(UINT16 *pInputData)
s_syncDiag.u32Sync1 = EcatDbg_GetSync1();
s_syncDiag.u32EscIrq = EcatDbg_GetEscIrq();
s_syncDiag.u32UptimeS = EcatDbg_GetUptimeSec();
+ /* 1.6.0 剖分埋点快照(sub5..12) */
+ s_syncDiag.u32PdiCyc = g_dbg_pdiCyc;
+ s_syncDiag.u32OutCyc = g_dbg_outCyc;
+ s_syncDiag.u32FreeRunCyc = g_dbg_freerunCyc;
+ s_syncDiag.u32PdiCnt = g_dbg_pdiCnt;
+ s_syncDiag.u32InCyc = g_dbg_inCyc;
+ s_syncDiag.u32InWriteCyc = g_dbg_inWriteCyc;
+ s_syncDiag.u32AlCyc = g_dbg_alCyc;
+ s_syncDiag.u32AppCyc = g_dbg_appCyc;
/* 1.5.54:先让 EthercatPdo 把本周期的输入数据写进 0x6000 的对象存储,
* 再交给 ESC。经由对象存储中转(而非直接写 pInputData)是为了让主站
diff --git a/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py b/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py
index 03b3cb1..6302870 100644
--- a/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py
+++ b/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py
@@ -79,7 +79,10 @@ MBX_TX_SIZE = 256
PD_OUT_OFFSET = 0x1400 # SM2: RxPDO (Master -> Slave outputs)
PD_OUT_SIZE = 384 # 48×64bit
PD_IN_OFFSET = 0x1880 # SM3: TxPDO (Slave -> Master inputs)
-PD_IN_SIZE = 640 # 80×64bit
+PD_IN_SIZE = 496 # 62×64bit (1.6.1 瘦身,原 80×64bit=640)
+# 1.6.1 瘦身 640→496:批量接收条目数据 64→32B + 段5诊断区前移到 484。
+# SM3 实占 3×496=1488B (0x1880-0x1E4F),DPRAM 留 432B 余量;
+# 依据见 comm/ethercat_pdo_size.h 的 1.6.1 注。改后必须重烧 EEPROM 并断电冷启动。
# 3-buffer 占用与 DPRAM 上限自检:改尺寸/偏移时立刻在生成阶段拦住,
# 而不是等烧进 EEPROM、冷启动后再看 AL 错误码。
diff --git a/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c b/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c
index 42c7525..ae7fac6 100644
--- a/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c
+++ b/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c
@@ -157,6 +157,24 @@ BOOL bCycleTimeMeasurementStarted; // indicates if the bus cycle measurement is
UINT16 aPdOutputData[(MAX_PD_OUTPUT_SIZE>>1)];
UINT16 aPdInputData[(MAX_PD_INPUT_SIZE>>1)];
+/*ECAT_DBG_START(1.6.0 临时剖分埋点)*/
+/* 1.6.0 负载归因埋点:DWT 周期累加器,经 0x2000 sub5..8 上报,主站 1s 差分出速率。
+ * 只为定位 free-run 模式下每周期 508µs 计入时间的真实去向;定位完成后整块移除。
+ * EcatDbg_Dwt() 定义在 comm/ethercat_sync.cpp (extern "C")。 */
+extern UINT32 EcatDbg_Dwt(void);
+#define ecatdbg_dwt() EcatDbg_Dwt()
+/* 1.6.0:输出探针(实现见 comm/ethercat_pdo.cpp,C 链接) */
+extern UINT8 EcatPdo_OutputProbe(UINT8 const *head);
+volatile UINT32 g_dbg_pdiCyc = 0; /* sub5: PDI_Isr 全函数累计周期 */
+volatile UINT32 g_dbg_outCyc = 0; /* sub6: PDO_OutputMapping(输出探针/全量读) */
+volatile UINT32 g_dbg_freerunCyc = 0; /* sub7: MainLoop free-run 分支(SM 同步下恒 0) */
+volatile UINT32 g_dbg_pdiCnt = 0; /* sub8: PDI_Isr 触发次数(=每周期 1 次) */
+volatile UINT32 g_dbg_inCyc = 0; /* sub9: PDO_InputMapping 全程(APPL+640B 写) */
+volatile UINT32 g_dbg_inWriteCyc = 0; /* sub10: 其中 HW_EscWriteIsr 640B 过程数据写 */
+volatile UINT32 g_dbg_alCyc = 0; /* sub11: PDI_Isr 内 AL 事件读×2+超限应答 */
+volatile UINT32 g_dbg_appCyc = 0; /* sub12: ECAT_Application(桥 processOutput) */
+/*ECAT_DBG_END*/
+
/*variables are declared in ecatslv.c*/
extern VARVOLATILE UINT16 u16dummy;
BOOL bInitFinished = FALSE; /** < \brief indicates if the initialization is finished*/
@@ -178,21 +196,48 @@ BOOL bInitFinished = FALSE; /** < \brief indicates if the initialization is fini
*////////////////////////////////////////////////////////////////////////////////////////
void PDO_InputMapping(void)
{
+ /*ECAT_DBG(1.6.0 剖分:输入映射全程 + 640B 写细分)*/
+ const UINT32 dbgT0 = ecatdbg_dwt();
APPL_InputMapping((UINT16*)aPdInputData);
+ const UINT32 dbgT1 = ecatdbg_dwt();
HW_EscWriteIsr(((MEM_ADDR *) aPdInputData), nEscAddrInputData, nPdInputSize );
+ g_dbg_inWriteCyc += ecatdbg_dwt() - dbgT1;
+ g_dbg_inCyc += ecatdbg_dwt() - dbgT0;
}
/////////////////////////////////////////////////////////////////////////////////////////
/**
\brief This function will copies the outputs from the ESC memory to the local memory
- to the hardware. This function is only called in case of an SM2
+ to the hardware. This function is only called in case of an SM2
(output process data) event.
+
+ 1.6.0 输出按需全量读(负载优化):每周期先读输出 PDO 头 12B 探针(覆盖 SEQ@0
+ 与 WIN@8),经 EcatPdo_OutputProbe 判定——seq 无变化沿且无清统计请求时,
+ 跳过 384B 全量读(只补读 SM2 末字完成事件应答),省掉每周期 ~190µs 的
+ PRAM 搬运(含 ESC 核仲裁等待)。主站滚动窗口扫槽依赖的 WIN 字节在探针内
+ 每周期刷新,窗口扫描不受影响。SM2 应答语义不变:首字(探针内)+末字
+ (两分支各自覆盖)。
*////////////////////////////////////////////////////////////////////////////////////////
void PDO_OutputMapping(void)
{
+ /*ECAT_DBG(1.6.0 剖分:输出映射全程)*/
+ const UINT32 dbgT0 = ecatdbg_dwt();
- HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize );
+ /* 1.6.0:输出按需全量读——探针 12B 判定是否真的有新输出 */
+ UINT8 head[12];
+ HW_EscReadIsr(((MEM_ADDR *) head), nEscAddrOutputData, 12U );
- APPL_OutputMapping((UINT16*) aPdOutputData);
+ if ( EcatPdo_OutputProbe(head) )
+ {
+ HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize );
+
+ APPL_OutputMapping((UINT16*) aPdOutputData);
+ }
+ else
+ {
+ /* 无新输出:补读 SM2 末字完成事件应答(首字已随探针读过) */
+ HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
+ }
+ g_dbg_outCyc += ecatdbg_dwt() - dbgT0;
}
/////////////////////////////////////////////////////////////////////////////////////////
@@ -279,10 +324,14 @@ void HandleBusCycleCalculation(void)
void PDI_Isr(void)
{
+ /*ECAT_DBG(1.6.0 剖分:ISR 全程)*/
+ const UINT32 dbgT0 = ecatdbg_dwt();
if(bEscIntEnabled)
{
/* get the AL event register */
+ const UINT32 dbgTa = ecatdbg_dwt();
UINT16 ALEvent = HW_GetALEventRegister_Isr();
+ g_dbg_alCyc += ecatdbg_dwt() - dbgTa;
ALEvent = SWAPWORD(ALEvent);
if ( ALEvent & PROCESS_OUTPUT_EVENT )
@@ -335,7 +384,9 @@ void PDI_Isr(void)
if (sSyncManOutPar.u16SyncType == SYNCTYPE_SM_SYNCHRON)
{
/* The Application is synchronized to process data Sync Manager event*/
+ const UINT32 dbgTb = ecatdbg_dwt();
ECAT_Application();
+ g_dbg_appCyc += ecatdbg_dwt() - dbgTb;
}
if ( bEcatInputUpdateRunning
@@ -345,14 +396,18 @@ void PDI_Isr(void)
)
{
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
+ const UINT32 dbgTc = ecatdbg_dwt();
PDO_InputMapping();
+ g_dbg_inCyc += ecatdbg_dwt() - dbgTc;
}
/*
Check if cycle exceed
*/
/*if next SM event was triggered during runtime increment cycle exceed counter*/
+ const UINT32 dbgTd = ecatdbg_dwt();
ALEvent = HW_GetALEventRegister_Isr();
+ g_dbg_alCyc += ecatdbg_dwt() - dbgTd;
ALEvent = SWAPWORD(ALEvent);
if ( ALEvent & PROCESS_OUTPUT_EVENT )
@@ -365,6 +420,9 @@ void PDI_Isr(void)
HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
}
} //if(bEscIntEnabled)
+ /*ECAT_DBG(1.6.0 剖分:ISR 全程 收尾)*/
+ g_dbg_pdiCyc += ecatdbg_dwt() - dbgT0;
+ g_dbg_pdiCnt++;
}
void Sync0_Isr(void)
@@ -577,12 +635,17 @@ void MainLoop(void)
}
DISABLE_ESC_INT();
- ECAT_Application();
-
- if ( bEcatInputUpdateRunning )
+ /*ECAT_DBG(1.6.0 剖分:free-run 应用+输入映射块)*/
{
- /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
- PDO_InputMapping();
+ const UINT32 dbgT1 = ecatdbg_dwt();
+ ECAT_Application();
+
+ if ( bEcatInputUpdateRunning )
+ {
+ /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
+ PDO_InputMapping();
+ }
+ g_dbg_freerunCyc += ecatdbg_dwt() - dbgT1;
}
ENABLE_ESC_INT();
}