feat(comm): LAN9252增加csrRead验证分级并实现PDO差分写功能
为提升EtherCAT通信性能,实现以下两项优化:
1. 对EscLan9252::csrRead方法增加verify参数,支持分级验证:
- verify=true(默认):保持原有的双读比对+重试机制,用于SM/AL/邮箱等
关键路径防止EXMC毛刺
- verify=false:单读模式,用于诊断回读/MII busy轮询/init轮询等低风
险场景,节省一半CSR间接访问时间
2. 实现PDO输入差分写ESC功能:
- 将输入PDO分为三区:段1桥状态(0-31)、段2电机密排(32-543)、段3+5
快照诊断(544-599)
- 采用3-buffer冲刷协议:变化后连续写3拍确保3个缓冲区全部更新
- 对内容稳定的段跳过写操作,显著减少PRAM字节搬运时间
- 全段跳过时强制写末2字节完成SM事件应答
同时更新配置版本至1.6.25,添加相关配置选项和文档说明。
This commit is contained in:
@@ -23,6 +23,15 @@ alignas(4) uint8_t EthercatPdo::s_in[EthercatPdo::kPdoInBytes] = {0};
|
||||
/* 输入 PDO 的工作缓冲:buildInput 填这里,填完整块发布到 s_in(见 process())。
|
||||
* 读侧 writeInputs 只读 s_in,因此永远看不到"填到一半"的镜像。 */
|
||||
alignas(4) uint8_t EthercatPdo::s_build[EthercatPdo::kPdoInBytes] = {0};
|
||||
alignas(4) uint8_t EthercatPdo::s_escSh1[32] = {0};
|
||||
alignas(4) uint8_t EthercatPdo::s_escSh2[ECAT_XRX_OFF_CH - 32U] = {0};
|
||||
alignas(4) uint8_t EthercatPdo::s_escSh3[kEcatPdoInBytes - ECAT_XRX_OFF_CH] = {0};
|
||||
uint8_t EthercatPdo::s_escFlush1 = 0U;
|
||||
uint8_t EthercatPdo::s_escFlush2 = 0U;
|
||||
uint8_t EthercatPdo::s_escFlush3 = 0U;
|
||||
bool EthercatPdo::s_escSh1Valid = false;
|
||||
bool EthercatPdo::s_escSh2Valid = false;
|
||||
bool EthercatPdo::s_escSh3Valid = false;
|
||||
|
||||
volatile uint32_t EthercatPdo::s_outputCycles = 0U;
|
||||
volatile uint32_t EthercatPdo::s_inputCycles = 0U;
|
||||
@@ -185,6 +194,100 @@ bool EthercatPdo::sparseEscWritePlan(uint16_t *off0, uint16_t *len0,
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
/** 段2 有效长度(稀疏:到 count*56 为止;否则整段)。 */
|
||||
uint16_t escSeg2WriteLen(const uint8_t *in)
|
||||
{
|
||||
#if CONFIG_ECAT_SPARSE_ESC_WRITE
|
||||
if (in[ECAT_MOTW_OFF_MAGIC] == kEcatMotLayoutMagic) {
|
||||
const uint8_t count = in[ECAT_MOTW_OFF_COUNT];
|
||||
if (count <= kEcatMotSlotsPerPdo) {
|
||||
const uint16_t headEnd =
|
||||
(uint16_t)(ECAT_MOTW_OFF_SLOTS + (uint16_t)count * kEcatMotSlotSize);
|
||||
if (headEnd < ECAT_XRX_OFF_CH) {
|
||||
return headEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)in;
|
||||
#endif
|
||||
return ECAT_XRX_OFF_CH;
|
||||
}
|
||||
|
||||
/** 判定是否需要写某区:无影子 / 冲刷中 / 内容变化。变化时刷新影子并重置冲刷=2。 */
|
||||
bool diffNeedWrite(const uint8_t *cur, uint16_t off, uint16_t cmpLen,
|
||||
uint8_t *shadow, bool &valid, uint8_t &flush)
|
||||
{
|
||||
const bool changed = !valid || memcmp(cur + off, shadow, cmpLen) != 0;
|
||||
if (changed) {
|
||||
memcpy(shadow, cur + off, cmpLen);
|
||||
valid = true;
|
||||
flush = 2U; /* 本拍 + 再 2 拍,冲完 3-buffer */
|
||||
return true;
|
||||
}
|
||||
if (flush > 0U) {
|
||||
--flush;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} /* anonymous namespace */
|
||||
|
||||
uint8_t EthercatPdo::escWritePlan(uint16_t *offs, uint16_t *lens)
|
||||
{
|
||||
if (offs == nullptr || lens == nullptr) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
#if !CONFIG_ECAT_DIFF_ESC_WRITE
|
||||
/* 回退:稀疏两段或整包 */
|
||||
uint8_t n = 0U;
|
||||
uint16_t o0, l0, o1, l1;
|
||||
if (sparseEscWritePlan(&o0, &l0, &o1, &l1)) {
|
||||
offs[n] = o0; lens[n] = l0; ++n;
|
||||
offs[n] = o1; lens[n] = l1; ++n;
|
||||
} else {
|
||||
offs[0] = 0U; lens[0] = kPdoInBytes; n = 1U;
|
||||
}
|
||||
return n;
|
||||
#else
|
||||
/* 三区差分:段1 桥状态 / 段2 电机密排(稀疏写有效长) / 段3+5 快照诊断。
|
||||
* 段1 计数器几乎每拍变,段2 稳定时省最大头,段3+5 多数拍稳定。 */
|
||||
uint8_t n = 0U;
|
||||
if (diffNeedWrite(s_in, 0U, 32U, s_escSh1, s_escSh1Valid, s_escFlush1)) {
|
||||
offs[n] = 0U;
|
||||
lens[n] = 32U;
|
||||
++n;
|
||||
}
|
||||
if (diffNeedWrite(s_in, 32U, (uint16_t)(ECAT_XRX_OFF_CH - 32U),
|
||||
s_escSh2, s_escSh2Valid, s_escFlush2)) {
|
||||
offs[n] = 32U;
|
||||
/* 写长:稀疏时只写到 count*56,但影子比对的是完整 32..543(含清零空洞)。
|
||||
* 空洞不写入 ESC 无妨:主站只按 count 迭代。 */
|
||||
const uint16_t wlen = escSeg2WriteLen(s_in);
|
||||
lens[n] = (wlen > 32U) ? (uint16_t)(wlen - 32U) : 0U;
|
||||
if (lens[n] == 0U) {
|
||||
/* count=0:段2 头 8 字节仍要写(count/active/magic) */
|
||||
lens[n] = 8U;
|
||||
}
|
||||
++n;
|
||||
}
|
||||
if (diffNeedWrite(s_in, ECAT_XRX_OFF_CH, (uint16_t)(kPdoInBytes - ECAT_XRX_OFF_CH),
|
||||
s_escSh3, s_escSh3Valid, s_escFlush3)) {
|
||||
offs[n] = ECAT_XRX_OFF_CH;
|
||||
lens[n] = (uint16_t)(kPdoInBytes - ECAT_XRX_OFF_CH);
|
||||
++n;
|
||||
}
|
||||
if (n == 0U) {
|
||||
offs[0] = (uint16_t)(kPdoInBytes - 2U);
|
||||
lens[0] = 2U;
|
||||
n = 1U;
|
||||
}
|
||||
return n;
|
||||
#endif
|
||||
}
|
||||
|
||||
void EthercatPdo::snapshotOutput(uint8_t *dst)
|
||||
{
|
||||
if (dst == nullptr) {
|
||||
@@ -225,4 +328,24 @@ uint8_t EcatPdo_SparseEscPlan(uint16_t *off0, uint16_t *len0,
|
||||
return EthercatPdo::sparseEscWritePlan(off0, len0, off1, len1) ? 1U : 0U;
|
||||
}
|
||||
|
||||
/* 1.6.25 差分写计划:SSC PDO_InputMapping 逐段 HW_EscWriteIsr。 */
|
||||
uint8_t EcatPdo_EscWritePlan(uint16_t *offs, uint16_t *lens, uint8_t maxSegs)
|
||||
{
|
||||
if (offs == nullptr || lens == nullptr || maxSegs == 0U
|
||||
|| maxSegs > EthercatPdo::kEscPlanMaxSegs) {
|
||||
return 0U;
|
||||
}
|
||||
uint16_t o[EthercatPdo::kEscPlanMaxSegs];
|
||||
uint16_t l[EthercatPdo::kEscPlanMaxSegs];
|
||||
const uint8_t n = EthercatPdo::escWritePlan(o, l);
|
||||
if (n == 0U || n > maxSegs) {
|
||||
return 0U;
|
||||
}
|
||||
for (uint8_t i = 0U; i < n; ++i) {
|
||||
offs[i] = o[i];
|
||||
lens[i] = l[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
Reference in New Issue
Block a user