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:
@@ -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) {}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() 在主循环调用。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" */
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<!-- SM2: Process Data Output (RxPDO, Master -> Slave) -->
|
||||
<Sm DefaultSize="384" StartAddress="#x1400" ControlByte="#x64" Enable="1">Outputs</Sm>
|
||||
<!-- SM3: Process Data Input (TxPDO, Slave -> Master) -->
|
||||
<Sm DefaultSize="640" StartAddress="#x1880" ControlByte="#x20" Enable="1">Inputs</Sm>
|
||||
<Sm DefaultSize="496" StartAddress="#x1880" ControlByte="#x20" Enable="1">Inputs</Sm>
|
||||
<!-- RxPDO (Output, Master -> Slave): 索引 0x1600-0x17FF,映射 0x7000:01..48
|
||||
1.5.56 扩到 48 条目(384B):兼容单帧区 48B + CANFD 批量发送区 336B。
|
||||
与 dc_app.c 的 PDO_OUT_ENTRIES / gen_eeprom.py 的 PD_OUT_SIZE 必须一致,
|
||||
@@ -374,6 +374,8 @@
|
||||
1.5.56 扩到 80 条目(640B):电机状态窗口 + CANFD 批量接收区 280B。
|
||||
SM3 起址随之挪到 #x1880 —— SM2 是 3-buffer 模式,384B×3 占到 #x187F。
|
||||
(384+640)×3 = 3072 = 过程数据区 0x1400-0x1FFF 全长,恰好用满。
|
||||
1.6.1 输入瘦身 640→496: 批量接收条目数据 64→32B(截断见 bridge.h),
|
||||
条目 80→62, SM3 实占 3×496=1488B (0x1880-0x1E4F), DPRAM 留 432B。
|
||||
撞址会被 SSC ecatslv.c 重叠检查拦下,报 INVALIDSMOUTCFG。 -->
|
||||
<TxPdo Fixed="1" Mandatory="1" Sm="3">
|
||||
<Index>#x1A00</Index>
|
||||
@@ -812,132 +814,24 @@
|
||||
<Name>Status Data 62</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>63</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 63</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>64</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 64</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>65</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 65</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>66</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 66</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>67</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 67</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>68</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 68</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>69</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 69</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>70</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 70</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>71</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 71</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>72</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 72</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>73</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 73</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>74</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 74</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>75</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 75</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>76</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 76</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>77</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 77</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>78</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 78</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>79</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 79</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
<Entry>
|
||||
<Index>#x6000</Index>
|
||||
<SubIndex>80</SubIndex>
|
||||
<BitLen>64</BitLen>
|
||||
<Name>Status Data 80</Name>
|
||||
<DataType>ULINT</DataType>
|
||||
</Entry>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</TxPdo>
|
||||
<Mailbox DataLinkLayer="true">
|
||||
<CoE SdoInfo="true" CompleteAccess="true" SegmentedSdo="true"/>
|
||||
|
||||
@@ -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)是为了让主站
|
||||
|
||||
@@ -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 错误码。
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user