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(); }