修改SOEM2.0源码,倍福EL5101通过SDO配置PDO,国产I/O控制板EC-DO64 从站固件的 SM2/SM3 类型定义与标准相反问题

文章目录

    • [SOEM 源码修改总结](#SOEM 源码修改总结)
      • [修改一:CoE PDO 读取后更新 SMlength](#修改一:CoE PDO 读取后更新 SMlength)
      • [修改二:PO2SOconfig 钩子位置移动](#修改二:PO2SOconfig 钩子位置移动)
      • 函数调用链中的位置

SOEM 源码修改总结

为了让 EL5101 和 EC-DO64 正常运行,对 SOEM 源码做了2 处功能修改 ,均在src/ec_config.cecx_map_coe_soe() 函数中。


修改一:CoE PDO 读取后更新 SMlength

保证正常从站(EL5101)的 SM 缓冲区大小跟随 PDO 切换同步更新;

位置:src/ec_config.c:534-535

修改前:

c 复制代码
if (!rval) /* CA not available or not succeeded */
{
   rval = ecx_readPDOmap(context, slave, &Osize, &Isize);
}
EC_PRINT("  CoE Osize:%u Isize:%u\n", Osize, Isize);   // ← 只打印,不更新 SM
}
if ((!Isize && !Osize) && (...) ) /* has SoE */
{
rval = ecx_readIDNmap(context, slave, &Osize, &Isize);
context->slavelist[slave].SM[2].SMlength = htoes((uint16)((Osize + 7) / 8));  // ← SoE 有这行
context->slavelist[slave].SM[3].SMlength = htoes((uint16)((Isize + 7) / 8));  // ← SoE 有这行
}
context->slavelist[slave].Obits = (uint16)Osize;
context->slavelist[slave].Ibits = (uint16)Isize;

修改后:

c 复制代码
if (!rval)
{
   rval = ecx_readPDOmap(context, slave, &Osize, &Isize);
}
context->slavelist[slave].SM[2].SMlength = htoes((uint16)((Osize + 7) / 8));  // ← 新增
context->slavelist[slave].SM[3].SMlength = htoes((uint16)((Isize + 7) / 8));  // ← 新增
EC_PRINT("  CoE Osize:%u Isize:%u\n", Osize, Isize);
}

原因 :SoE 分支(第 545-546 行)一直有 SMlength 更新逻辑,但 CoE 分支缺少。ecx_readPDOmap()通过 SDO 读回 PDO 位宽后仅设置了 Obits/Ibits,SMlength 仍保留ecx_config_init()时从 SII EEPROM 读取的默认值。EL5101 默认 SM2=3 bytes / SM3=5 bytes,切换 PDO 后变为 6 bytes / 10 bytes,SMlength 不更新则后续ecx_map_sm() 写入 ESC 硬件的是错误值。

原理

bash 复制代码
ecx_readPDOmap() 通过 SDO 读:
  0x1C12:01 → PDO 索引 → 计算 bit 总和 → Osize
  0x1C13:01 → PDO 索引 → 计算 bit 总和 → Isize
SoE 分支有:  SM[2].SMlength = (Osize+7)/8   ← AMS/SoE 从站
CoE 分支无:  SM[2].SMlength = ???            ← EL5101/EL3052 等 CoE 从站
缺失导致 ecx_map_sm() 用错误长度写 SM 寄存器 → ESC 拒绝。

修改二:PO2SOconfig 钩子位置移动

保证异常从站(EC-DO64)的钩子修正在所有自动检测完成后成为最终值。

位置:src/ec_config.c:551-558(从 518-522 行移来)

修改前:

c 复制代码
static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, ...)
{
   ecx_statecheck(context, slave, EC_STATE_PRE_OP, ...);
   if (context->ENI) { ... }
   /* execute slave configuration hook Pre-Op to Safe-OP */
   if (context->slavelist[slave].PO2SOconfig)     // ← 钩子在 CoE 读之前
   {
      context->slavelist[slave].PO2SOconfig(context, slave);
   }
   /* Find IO mapping in slave */
   if (mbx_proto & ECT_MBXPROT_COE) {
      ecx_readPDOmap(context, slave, &Osize, &Isize);  // ← CoE 读会覆盖钩子的修正!
      ...
   }
   ...
   context->slavelist[slave].Obits = (uint16)Osize;    // ← 覆盖!
   context->slavelist[slave].Ibits = (uint16)Isize;    // ← 覆盖!
   return 1;
}

修改后:

c 复制代码
static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, ...)
{
   ecx_statecheck(context, slave, EC_STATE_PRE_OP, ...);
   if (context->ENI) { ... }
   /* Find IO mapping in slave */
   if (mbx_proto & ECT_MBXPROT_COE) {
      ecx_readPDOmap(context, slave, &Osize, &Isize);
      ...
   }
   ...
   context->slavelist[slave].Obits = (uint16)Osize;
   context->slavelist[slave].Ibits = (uint16)Isize;
   /* execute slave configuration hook Pre-Op to Safe-OP.
    * Called AFTER CoE/SoE/SII PDO reads, allowing the hook to
    * override automatically-discovered SM types, sizes, and lengths
    * for slaves with non-standard or incorrect firmware. */
   if (context->slavelist[slave].PO2SOconfig)     // ← 钩子移到 CoE 读之后
   {
      context->slavelist[slave].PO2SOconfig(context, slave);
   }
   return 1;
}

原因 :EC-DO64 从站固件的 SM2/SM3 类型定义与标准相反。原始代码中 PO2SOconfig 钩子在 CoE PDO 读取之前 执行,钩子设置的修正值(SMtype、Obits、Ibits、SMlength)会被后续ecx_readPDOmap()Obits/Ibits 赋值覆盖,导致修正不生效。

原理

bash 复制代码
EC-DO64 SII 读取值:     SM[2].SMtype=4(Inputs),  SM[3].SMtype=3(Outputs)
CoE PDO 读回值:         Osize=0, Isize=64           ← 因为 SM 类型反了
修改前:
  钩子修正: SMtype[2]=3, Obits=64  ✓
  CoE 读:   Osize=0                 ← 读回 0
  Obits=0                           ← 覆盖! 钩子白改了
  SM[2].SMlength = (0+7)/8 = 0     ← SM2 被禁用!
修改后:
  CoE 读:   Osize=0                  ← 先读完
  Obits=0                            ← 先赋值
  钩子修正: SMtype[2]=3, SM[2].SMlength=8, Obits=64  ← 最终裁定!
  → ecx_map_sm 写入正确的 SM 硬件值

函数调用链中的位置

bash 复制代码
ecx_config_map_group()
  └─ ecx_config_find_mappings()
       ├─ ecx_map_coe_soe(slave)        ← ★ 两处修改都在此函数内
       │   ├─ ecx_readPDOmap()          → Osize, Isize
       │   ├─ ★ SM[2/3].SMlength 更新   (修改一)
       │   ├─ Obits = Osize, Ibits = Isize
       │   └─ ★ PO2SOconfig 钩子        (修改二, 移到这里)
       ├─ ecx_map_sii(slave)            → SII 兜底 (若 Obits/Ibits=0)
       └─ ecx_map_sm(slave)             → 写 SM 寄存器到 ESC 硬件

两处修改缺一不可:修改一保证正常从站(EL5101)的 SM 缓冲区大小跟随 PDO 切换同步更新;修改二保证异常从站(EC-DO64)的钩子修正在所有自动检测完成后成为最终值。