feat(comm): 1.6.51 CAN 空通道自动静默 + PDO 诊断 + OTA 布局共用头
变更点: 1. CAN-FD(device/CANFD.cpp、sys/config.h) - 发送统一 FD+BRS(DLC≤8 不再回退经典 CAN);OTA 期仍按通道压制 BRS - 空通道自动识别:连续 5 个 1Hz 窗口无有效帧且有错误 → 静默上报;有帧即恢复 - 三路 CAN 默认全开;RX parse budget 48→64 2. EtherCAT 桥 / PDO - 段1 增 txtxid/rx00ff;版本应答 0x00FF 粘滞发布,避免被 CONFIG 冲掉 - STATUS 禁止写入 xrx 快照;processSeq 新鲜度救援 3. OTA / 配置 - sys/config.h include 仓库内 shared/ota_layout.h(与 BOOT 共用,不再依赖仓外路径) - ota_boot_request 对齐配置页 word 布局 4. docs/audit_ecat_spi_wifi_ota_compatibility.md:ECAT/SPI/WiFi/OTA 兼容性审计 5. .gitignore:忽略 .trae/、docs/traework-import、评审过程稿
This commit is contained in:
@@ -237,7 +237,7 @@ void TaskRunner::taskCanRxParse()
|
||||
s_runtime.last_rx_data,
|
||||
s_runtime.last_rx_len);
|
||||
#endif
|
||||
/* 1.5.53:旁路投递给 EtherCAT PDO 桥。放在分发链末尾且**不消费**帧
|
||||
/* 旁路投递给 EtherCAT PDO 桥。放在分发链末尾且**不消费**帧
|
||||
* (不 return、不 continue),因此 OTA relay / 电机状态的取帧不受影响——
|
||||
* 本队列只有 taskCanRxParse 一个消费者,新增第二个消费者会互相抢帧。 */
|
||||
EthercatCanBridge::onCanFrame(s_runtime.last_rx_channel,
|
||||
@@ -284,31 +284,26 @@ bool TaskRunner::copyAdcSample(Adc::Sample *out)
|
||||
|
||||
/* SSC 侧 AL 诊断快照(dc_app.c): [19:16]=AL 状态 [31:24]=AL 错误码 [15:0]=AL 事件寄存器 */
|
||||
extern "C" uint32_t EcatDbg_ReadAlDiag(void);
|
||||
/* SSC 侧 AL Event Mask(0x204) 守护(dc_app.c, 1.5.48):缺位自愈+WARN,返回实际值/0xFFFF=不适用 */
|
||||
/* SSC 侧 AL Event Mask(0x204) 守护(dc_app.c):缺位自愈+WARN,返回实际值/0xFFFF=不适用 */
|
||||
extern "C" uint32_t EcatDbg_GuardAleventMask(void);
|
||||
/* 1.6.33:OP 输入新鲜度不变量 — 输入 PDO 停更则主循环强制重写(根治假连接) */
|
||||
/* OP 输入新鲜度不变量 — 输入 PDO 停更则主循环强制重写 */
|
||||
extern "C" void EcatDbg_OpInputFreshness(void);
|
||||
|
||||
/**
|
||||
* @brief 1.5.48/1.5.49 AL Event Mask(0x204) 守护 + SM2 事件楔死自愈 + 1.5.52 邮箱
|
||||
* SM 地址/长度失配自愈:250ms 周期。
|
||||
* @note 实测"停止回 INIT 后重进 OP"概率性挂起(SAFEOP+0x001B, WKC=3/3 但
|
||||
* bEcatFirstOutputsReceived 不置位,0x220 bit10 挂起 10s 无人处理)——
|
||||
* PDI_Isr 通路楔死。守护在 SAFEOP 后 1s 内替 PDI_Isr 完成工作(置位
|
||||
* 标志+应答事件),主站 12s OP 等待窗口内可自愈;WARN 携带全部内部旗标,
|
||||
* 下次复现一行日志定位楔死路径(详见 dc_app.c 注释)。
|
||||
* 1.5.52:周期 1s→250ms——新加的邮箱地址自愈(1.5.52 根治 SSC 侧
|
||||
* u16EscAddrSendMbx 错位楔死,详见 dc_app.c)必须快过主站 SDO 超时
|
||||
* (~700ms),否则自愈永远落在主站放弃之后,形同虚设。
|
||||
* 1.5.59:邮箱楔死已根治(mailbox.c 几何常量化,CSR 错值无进入缓存路径),
|
||||
* 本守护降级为运行期内存踩踏兜底;且修复了 1.5.52~1.5.58 的 SWAPWORD
|
||||
* 字节序 bug——那段时期本守护的失配判定恒短路,实际从未生效过。
|
||||
* @brief AL Event Mask(0x204) 守护 + SM2 事件楔死自愈 + 邮箱 SM 地址/长度失配自愈:
|
||||
* 250ms 周期。
|
||||
* @note PDI_Isr 通路楔死时(0x220 bit10 挂起)守护在 SAFEOP 后 1s 内替 PDI_Isr
|
||||
* 完成工作(置位标志+应答事件),主站 12s OP 等待窗口内可自愈;WARN 携带
|
||||
* 全部内部旗标,下次复现一行日志定位楔死路径(详见 dc_app.c 注释)。
|
||||
* 周期必须快过主站 SDO 超时(~700ms),否则自愈永远落在主站放弃之后。
|
||||
* 邮箱几何已构建期常量化(mailbox.c),CSR 错值无进入缓存路径;
|
||||
* 本守护降级为运行期内存踩踏兜底。
|
||||
*/
|
||||
static void ethercatAleMaskGuard()
|
||||
{
|
||||
static uint32_t s_lastGuardMs = 0U;
|
||||
if (!EthercatSync::isActive() || EscLan9252::pmSuspended()) {
|
||||
/* 1.6.27:睡眠挂起期一并静默——守护内部读 SM 起止/长度与 0x204/0x220
|
||||
/* 睡眠挂起期一并静默——守护内部读 SM 起止/长度与 0x204/0x220
|
||||
* 全是 HBI 访问,会把睡着的芯片叫醒 */
|
||||
return;
|
||||
}
|
||||
@@ -320,7 +315,7 @@ static void ethercatAleMaskGuard()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 1.5.32 ECAT 心跳:主循环存活证明 + ESC AL 状态/事件寄存器快照。
|
||||
* @brief ECAT 心跳:主循环存活证明 + ESC AL 状态/事件寄存器快照。
|
||||
* @note 仅在同步 ISR 激活(SSC 在跑)时打印。心跳停 = 主循环卡死;
|
||||
* al=0x1 且 evt bit0 长期置位 = SSC 没处理 AL 请求;
|
||||
* al 与主站读到的不一致 = ESC 写回路径问题。
|
||||
@@ -328,7 +323,7 @@ static void ethercatAleMaskGuard()
|
||||
static void ethercatAlHeartbeat()
|
||||
{
|
||||
if (!EthercatSync::isActive() || EscLan9252::pmSuspended()) {
|
||||
/* 1.6.27:睡眠挂起期静默——读 0x0130/0x0220 即 HBI 访问,读即唤醒 */
|
||||
/* 睡眠挂起期静默——读 0x0130/0x0220 即 HBI 访问,读即唤醒 */
|
||||
return;
|
||||
}
|
||||
const uint32_t alDiag = EcatDbg_ReadAlDiag();
|
||||
@@ -433,8 +428,7 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
#endif
|
||||
|
||||
if (EthercatSlave::isEscReady()) {
|
||||
/* 1.5.27:periodicIsr 已删除(死代码,无调用点),PDO 交换由 SSC
|
||||
* Sync0_Isr → ECAT_Application → dc_app.c APPL_* 完成。 */
|
||||
/* PDO 交换由 SSC Sync0_Isr → ECAT_Application → dc_app.c APPL_* 完成。 */
|
||||
printf("[ECAT] ESC ready (chip id=0x%08lX), PDO via SSC Sync0_Isr\r\n",
|
||||
(unsigned long)EthercatSlave::getChipId());
|
||||
} else {
|
||||
@@ -506,7 +500,6 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
(unsigned long)g_app_uptime_sec);
|
||||
s_prev_esc_ready = esc_ready;
|
||||
}
|
||||
/* 1.5.27:periodicIsr ISR 统计随死代码一并删除 */
|
||||
/* DC SYNC0 通路验证:仅在有脉冲增量时打印(未接主站时静默)。 */
|
||||
ethercatSyncCounterPrint();
|
||||
ethercatAlHeartbeat();
|
||||
@@ -546,11 +539,23 @@ void TaskRunner::runPending()
|
||||
{
|
||||
Watchdog::feed();
|
||||
|
||||
/* 高 PDO 频率时:有帧就解析,但**每圈限一次**且预算内尽量清空。
|
||||
* 每圈都跑满 budget 在 2kHz 可能抢太多主循环(实测达成率掉到 85%)。
|
||||
* 8kHz 下主循环间隙极短,仍需入口排水,否则 gseq/s 从 ~3750 掉到数百。 */
|
||||
if (CanFd::hasRxFrame()) {
|
||||
CpuLoad::beginWork();
|
||||
taskCanRxParse();
|
||||
CpuLoad::endWork();
|
||||
}
|
||||
|
||||
/* 喂狗稳定满 30s 才清 BOOT 崩溃计数(每上电一次)。init 就清会让
|
||||
* 主循环 3s 内 FWDGT 崩溃永远攒不到 OTA_CRASH_STAY_MAX。 */
|
||||
OtaBootRequest::maybeClearCrashCount(g_app_uptime_sec);
|
||||
|
||||
/* === HBI 周期诊断:每 20 秒重新跑一次 EXMC/HBI 探活,方便热插拔/焊接后观察 ===
|
||||
* 1.5.27:EtherCAT 同步 ISR 激活后停止(diagDump 会把 EXMC 引脚切成 GPIO,
|
||||
* EtherCAT 同步 ISR 激活后停止(diagDump 会把 EXMC 引脚切成 GPIO,
|
||||
* 拆毁运行中的总线;诊断的 bit-bang 也与 HBI 事务互斥冲突)。
|
||||
* 1.5.33:整块由 CONFIG_ESC_EXMC_DIAG 门控,默认关闭——硬件已确认全通,
|
||||
* 不再每 20s 拆总线做探针;换板/补焊排查浮空时置 1 恢复。 */
|
||||
* 整块由 CONFIG_ESC_EXMC_DIAG 门控,默认关闭;换板/补焊排查浮空时置 1 恢复。 */
|
||||
#if CONFIG_ESC_EXMC_DIAG
|
||||
static uint32_t s_lastDiagSec = 0xFFFFFFFFU;
|
||||
if (!EthercatSync::isActive()
|
||||
@@ -562,9 +567,9 @@ void TaskRunner::runPending()
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 延迟 EEPROM 烧录已删除(1.5.29):ESI 镜像改由 EtherCAT 主站侧刷写
|
||||
/* ESI 镜像由 EtherCAT 主站侧刷写
|
||||
* (tools/ecat/ecat_flash_eeprom.py 经 ESC 0x0500 EEPROM 接口),
|
||||
* 固件不再承担烧录/校验职责。 */
|
||||
* 固件不承担烧录/校验职责。 */
|
||||
|
||||
/* poll() 含按铃超时看门狗(cancelRing),必须每轮无条件执行。
|
||||
* 主动推送已随 2026-08 请求驱动改造移除,此处仅需消费请求响应的按铃。 */
|
||||
@@ -580,7 +585,7 @@ void TaskRunner::runPending()
|
||||
|
||||
/* 电机 OTA relay:活跃时每圈推进状态机(等 ACK / 超时 / 发下一包)。
|
||||
* 非活跃时立即返回,开销可忽略。 */
|
||||
/* 负载率挂钩(1.5.63):relay 活跃时才计 busy——isActive() 是廉价枚举判断,
|
||||
/* 负载率挂钩:relay 活跃时才计 busy——isActive() 是廉价枚举判断,
|
||||
* 空闲路径零挂钩开销;计数段覆盖等 ACK/超时/发包的真实工作。 */
|
||||
const bool motorOtaActive = MotorOtaRelay::isActive();
|
||||
if (motorOtaActive) {
|
||||
@@ -594,26 +599,28 @@ void TaskRunner::runPending()
|
||||
CpuLoad::endWork();
|
||||
}
|
||||
|
||||
/* 2026-08 请求驱动改造:0xC1 CAN 反馈 / 0xC5 电机状态 / 0xC4 ADC 已改为
|
||||
* ESP32 周期查询(主动按铃推送全部移除,避免 OTA 期间悬空按铃刷屏)。
|
||||
* 仅保留事件驱动、低频的 0xC6 电机版本推送。 */
|
||||
/* 仅保留事件驱动、低频的 0xC6 电机版本推送。 */
|
||||
motorVersionPushStep();
|
||||
|
||||
/* SSC 协议栈主循环:AL 状态机 + 邮箱处理 + FreeRun PDO。
|
||||
* 每轮无条件调用,SSC 内部自行判断是否有待处理事件。
|
||||
* DC 模式下 PDO 交换由 SYNC0 ISR (Sync0_Isr) 完成,此处仅处理低优先级逻辑。
|
||||
* 1.6.27:ESC 睡眠(D1-D3)挂起期整段停调——MainLoop 每圈必读 0x220(AL 事件
|
||||
* 寄存器,经 HBI),而睡眠中任何 HBI 访问(含读)都会把芯片打回 D0,
|
||||
* 2026-09-12 实测休眠后 ~20s 即被"复活"。 */
|
||||
* ESC 睡眠(D1-D3)挂起期整段停调——MainLoop 每圈必读 0x220(AL 事件
|
||||
* 寄存器,经 HBI),而睡眠中任何 HBI 访问(含读)都会把芯片打回 D0。
|
||||
*
|
||||
* 负载口径(恢复 1.5.63):MainLoop **不**计入 busy。
|
||||
* 实时模式主循环是 NOP 忙等,每圈都调 MainLoop;整段计入会把
|
||||
* load_perm 抬到恒 ~99%(250Hz 也「占满」,与历史 4kHz≈80% 曲线对不上)。
|
||||
* 过程数据成本应由 PDI/Sync0 ISR 挂钩体现,见 sys/cpu_load.h。 */
|
||||
if (!EscLan9252::pmSuspended()) {
|
||||
MainLoop();
|
||||
/* 1.6.33:OP 输入新鲜度不变量。与 MainLoop 同拍调用——正常时
|
||||
/* OP 输入新鲜度不变量。与 MainLoop 同拍调用——正常时
|
||||
* PDI_Isr 已在刷新,这里只做廉价 seq 比对;停更超 5ms 才强制重写。
|
||||
* 睡眠期与 MainLoop 一并跳过(读 HBI 会唤醒芯片)。 */
|
||||
EcatDbg_OpInputFreshness();
|
||||
}
|
||||
|
||||
/* 1.5.48:AL Event Mask(0x204) 守护——每圈入口执行,内部 250ms 节流。
|
||||
/* AL Event Mask(0x204) 守护——每圈入口执行,内部 250ms 节流。
|
||||
* 放 MainLoop 旁而非诊断任务:守护必须独立于诊断打印频率,
|
||||
* OP 等待期(12s)内从站进 SAFEOP 后 1s 内即介入自愈。 */
|
||||
ethercatAleMaskGuard();
|
||||
@@ -621,7 +628,7 @@ void TaskRunner::runPending()
|
||||
const uint32_t pending = TaskManager::takePending();
|
||||
if(pending == 0U) return;
|
||||
|
||||
/* 负载率挂钩(1.5.63):派发的周期任务属"非延迟工作",整段计入 busy;
|
||||
/* 负载率挂钩:派发的周期任务属"非延迟工作",整段计入 busy;
|
||||
* 前面的常开轮询(Watchdog/poll/MainLoop/guard)不计入,见 sys/cpu_load.h。 */
|
||||
CpuLoad::beginWork();
|
||||
if(pending & (1UL << TASK_ADC_VREF_SAMPLE)) taskAdcVrefSample();
|
||||
|
||||
Reference in New Issue
Block a user