feat(DCU_APP): 添加CPU负载监控和EtherCAT诊断功能

- 在DCU_APP项目中添加cpu_load.h和cpu_load.cpp模块用于CPU负载率监测
- 修改csolution配置将构建目标从Debug切换为Release
- 在EtherCAT PDO协议栈中新增段5诊断区(632-639字节),包含负载率和序列号
- 在所有关键中断服务程序中添加CpuLoad::isrEnter()/isrExit()钩子用于准确统计中断占用
- 增强EtherCAT同步中断处理,改进AL事件掩码守护机制,修复OP状态下PDI中断门控问题
- 解决SSC源码中指针地址对齐警告和打包成员访问警告问题
- 在主循环中集成CPU负载率采样tick()调用,实现1秒滚动窗口统计

BREAKING CHANGE: 构建配置从Debug改为Release,EtherCAT协议栈增加新的诊断数据区
This commit is contained in:
cbb
2026-09-05 20:07:22 +08:00
parent 2ebd4ff1d2
commit 70daa4b0c6
19 changed files with 398 additions and 27 deletions
+44 -3
View File
@@ -389,8 +389,8 @@ UINT32 EcatDbg_ReadAlDiag(void)
}
/*---------------------------------------------------------------------------
* AL Event Mask (0x204) 守护 + SM2 事件楔死自愈 — 1.5.48 新增, 1.5.49 扩展
* (C 链接, task_runner 1s 节流调用)
* AL Event Mask (0x204) 守护 + SM2 事件楔死自愈 — 1.5.48 新增, 1.5.49/1.5.60 扩展
* (C 链接, task_runner 250ms 节流调用)
*
* 背景:实测"停止回 INIT 后重进 OP"概率性失败——主站 LRW WKC=3/3(输出
* 确实写进 SM2),但从站 9s ESM 超时报 0x001B:bEcatFirstOutputsReceived 一直
@@ -401,6 +401,13 @@ UINT32 EcatDbg_ReadAlDiag(void)
*
* 守护(1)0x204 缺位自愈:从站 AL>=SAFEOP 且有输出 PDO 时,SSC 不变式是
* "0x204 ⊇ u16ALEventMask"。读实际值比对,缺位则补写并打 WARN。
* 1.5.60 加固:期望值 u16ALEventMask 本身也可能缺 SM2 位——下行转换路径
* (ecatslv.c AL_ControlInd 尾部)同时清 0x204 的 PDO 事件位、bEscIntEnabled、
* bEcatFirstOutputsReceived,恢复全靠 SAFEOP 重入的 StartInputHandler +
* SetALEventMask(ecatslv.c:1506);重入没走全时 0x204 丢位。旧判定只比
* "actual ⊇ 期望值",而期望值变量在下行路径并不清零——若错的是期望值本身,
* 比对恒通过、缺位永远补不回。SM-Synchron+有输出 PDO 的不变式是
* u16ALEventMask ⊇ PROCESS_OUTPUT_EVENT(ecatslv.c:959/1086),先补期望再比对。
*
* 自愈(2)SM2 事件楔死(1.5.49):SAFEOP 下 0x220 bit10 挂起 且 标志未置位
* = PDI_Isr 通路楔死。从主循环直接完成 PDI_Isr 该做的事:置位
@@ -408,6 +415,15 @@ UINT32 EcatDbg_ReadAlDiag(void)
* 协议等价:bit10 挂起 = 主站已写过 SM2 = "收到输出"成立;AL_ControlRes 下一个
* MainLoop 周期即完成 SAFEOP→OP。WARN 附带全部内部旗标(bEscInt/bDcSync/
* 期望与实际 mask/看门狗状态),下次复现一行日志即可定位楔死路径。
*
* 自愈(3)OP 态 PDI_Isr 门控(1.5.60):AL==OP 且 bEscIntEnabled==FALSE 不可
* 合法出现——门控只在下行转换尾部被清(ecatslv.c:1273),而下行必改 AL 状态;
* 守护与 ESM 同在主循环上下文串行,看到"OP 且门关着"即重入路径漏了恢复段。
* 此时 PDI_Isr 整体被门控(ecatappl.c:282),PDO_OutputMapping 停调、输入冻结
* ——主站侧表现即楔死看门狗盯的窗口回显冻结(2026-09-05 实测 250µs 档每
* 20~35s 触发一次重拉,wr 两小时涨 38)。修法:重开门 + 应答挂起的 SM2 事件
* (掩码补位由守护(1)在同次调用完成),下一帧 LRW 的 SM2 中断即恢复整条
* 过程数据链路,主站看门狗(2s 阈值)从此摸不到触发条件。
*---------------------------------------------------------------------------*/
UINT32 EcatDbg_GuardAleventMask(void)
{
@@ -474,7 +490,12 @@ UINT32 EcatDbg_GuardAleventMask(void)
evt = HW_GetALEventRegister();
HW_EscReadWord(actual, ESC_AL_EVENTMASK_OFFSET);
/* (1) 0x204 缺位自愈 */
/* (1) 0x204 缺位自愈(1.5.60 加固:先补期望值,再比对补实际值) */
if ((u16ALEventMask & PROCESS_OUTPUT_EVENT) == 0U) {
u16ALEventMask |= PROCESS_OUTPUT_EVENT;
printf("[ECAT][AEM] WARN: expected AL mask lacked SM2 bit -> 0x%04X\r\n",
(unsigned)u16ALEventMask);
}
if ((UINT16)(actual & u16ALEventMask) != u16ALEventMask) {
const UINT16 before = actual;
HW_SetALEventMask(u16ALEventMask);
@@ -498,5 +519,25 @@ UINT32 EcatDbg_GuardAleventMask(void)
(unsigned)(bDcSyncActive ? 1U : 0U),
(unsigned)u16ALEventMask, (unsigned)actual, (unsigned)wdState);
}
/* (3) OP 态 PDI_Isr 门控自愈(1.5.60 新增):OP 下 bEscIntEnabled==FALSE
* 不可合法出现——门控只在下行转换尾部被清(ecatslv.c:1273),而下行必改
* AL 状态;守护与 ESM 同在主循环上下文串行,看到"OP 且门关着"即 SAFEOP
* 重入路径漏了恢复段。此时 PDI_Isr 整体被门控(ecatappl.c:282),
* PDO_OutputMapping 停调、输入冻结,只能靠主站看门狗重拉总线。
* 修法:重开门 + 应答挂起的 SM2 事件(ISR 非 OP 分支同款读首/末字);
* 0x204 掩码补位已由上面(1)在同次调用完成,下一帧 LRW 即恢复。 */
if (al == 8U /* OP */ && nPdOutputSize > 0U && bEscIntEnabled == FALSE) {
HW_EscReadWord(wdState, ESC_PD_WD_STATE);
bEscIntEnabled = TRUE;
HW_EscReadWord(dummy, nEscAddrOutputData);
HW_EscReadWord(dummy, (UINT16)(nEscAddrOutputData + nPdOutputSize - 2U));
printf("[ECAT][AEM] WARN: OP with PDI_Isr gated, evt=0x%04X "
"(bDcSync=%u mask_exp=0x%04X mask_act=0x%04X wd=0x%04X) "
"-> gate reopened + event acked\r\n",
(unsigned)evt,
(unsigned)(bDcSyncActive ? 1U : 0U),
(unsigned)u16ALEventMask, (unsigned)actual, (unsigned)wdState);
}
return actual;
}
+3 -2
View File
@@ -887,14 +887,15 @@ UINT8 OBJ_Read( UINT16 index, UINT8 subindex, UINT32 objSize, OBJCONST TOBJECT O
p = (CHAR **) pVarPtr;
pVarPtr = (UINT16 MBXMEM *)p[subindex-1];
if((((UINT16)pVarPtr) & 0x1) == 0x1)
if((((UINT32)pVarPtr) & 0x1) == 0x1)
{
/*enum is stored at an odd address*/
UINT16 cnt = 0;
//get last even WORD address
/*ECATCHANGE_START(V5.11) SDO3*/
pVarPtr = (UINT16 MBXMEM *)(((MEM_ADDR)pVarPtr)& ~(MEM_ADDR)0x1);
/*MEM_ADDR=UINT16 会把 32 位指针截成 16 位,必须用完整位宽清 bit0*/
pVarPtr = (UINT16 MBXMEM *)(((UINT32)pVarPtr) & ~(UINT32)0x1);
/*ECATCHANGE_END(V5.11) SDO3*/
for(cnt = 0; cnt < (size / 2);cnt++)
+4 -3
View File
@@ -1025,7 +1025,8 @@ UINT8 SDOS_SdoInfoInd( TSDOINFORMATION MBXMEM *pSdoInfoInd )
UINT16 n = OBJ_GetNoOfObjects(i);
/* copy the number of objects of the list type in the SDO Information response */
((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[(SIZEOF_SDOINFOLISTSTRUCT>>1)+i] = SWAPWORD(n);
/*(UINT32) 中转:对 packed 成员直接取址强转 UINT16 指针会报 -Waddress-of-packed-member(邮箱缓冲实际偶对齐)*/
((UINT16 MBXMEM *)(UINT32)&pSdoInfoInd->CoeHeader)[(SIZEOF_SDOINFOLISTSTRUCT>>1)+i] = SWAPWORD(n);
}
/* size of the mailbox service response */
@@ -1045,7 +1046,7 @@ UINT8 SDOS_SdoInfoInd( TSDOINFORMATION MBXMEM *pSdoInfoInd )
size = u16SendMbxSize - SIZEOF_SDOINFO - MBX_HEADER_SIZE;
}
/* initialize pData with the pointer where the fragment has to be copied */
pData = &((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFO>>1];
pData = &((UINT16 MBXMEM *)(UINT32)&pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFO>>1];
/* initialize index with the next index to be sent */
index = nSdoInfoIndex;
/* decrement the number of fragments to be sent */
@@ -1063,7 +1064,7 @@ UINT8 SDOS_SdoInfoInd( TSDOINFORMATION MBXMEM *pSdoInfoInd )
size = u16SendMbxSize - SIZEOF_SDOINFOLISTSTRUCT - MBX_HEADER_SIZE;
}
/* initialize pData with the pointer where the fragment has to be copied */
pData = &((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFOLISTSTRUCT>>1];
pData = &((UINT16 MBXMEM *)(UINT32)&pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFOLISTSTRUCT>>1];
/*Check if List need to be send in fragments*/
if( (n<<1) > size)
{