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

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

1.6.1 输入 PDO 瘦身 640→496(62×64bit; 0..359 与 1.5.61 逐字节兼容):
- 依据: DWT 实测 640B 写恒 296µs/拍且与 SYNC0 相位无关(主站 dc_bench 五形态
  对拍极差 0.04%), PRAM ≈0.463µs/B 线性; 真机 6086 帧 DLC 仅 24/32
- 批量接收条目 8B 头+64B 数据 → 8B+32B(DLC 真值保留, 截断打 flags bit1,
  环形队列仍存全量 64B); 段5 诊断区 632→484
- 预期写侧 −67µs/拍(→~230µs, 占空 29.7%→23%); LRW 帧 1024→880B
- 五处一致: ethercat_pdo_size.h(真源) / dc_app.c(62 条目+映射初始化器) /
  gen_eeprom.py(PD_IN_SIZE, nEntry 自动推导) / DCU_ESI.xml(SM3 496+62 条目) /
  bridge.h(kEcatRxBatchDataMax=32, 断言更新)
- 主站 tools/ecat decode_input 按 Ibytes 双布局自适应, 固件/EEPROM 与主站可
  任意先后升级; 混烧(只烧一侧)会被 SM 长度校验拦下(AL 0x001A)
- AC6 编译 0 警告; 真机验收待: 烧 axf + ecat.bat flash + 断电冷启动
  (流程与复验判据见 tools/ecat/MIGRATION.md 1.6.1 节)
This commit is contained in:
cbb
2026-09-06 00:19:18 +08:00
parent 70daa4b0c6
commit 9f1c2d25dd
11 changed files with 315 additions and 185 deletions
+21 -127
View File
@@ -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"/>
+50 -10
View File
@@ -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)是为了让主站
+4 -1
View File
@@ -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 错误码。