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:
@@ -7,19 +7,17 @@
|
||||
* - A2=1,A1=0 (偏移 0x40000) -> HBI_DATA_0 低 16 位
|
||||
* - A2=1,A1=1 (偏移 0x60000) -> HBI_DATA_0 高 16 位
|
||||
*
|
||||
* 地址线换算(1.5.27 修正,此前 0x20000/0x30000 是按 8-bit 规则算的,DATA 访问全部
|
||||
* 落在 HBI_IDX_0 高半字,IDX_0=0x1234 探针假通过而 BYTE_TEST 永远失败):
|
||||
* 地址线换算(16-bit 数据宽度时 EXMC 内部将 HADDR[25:1] 接到 EXMC_A[24:0]):
|
||||
* 板线:LAN9252 A1(Pin25)→PD11=EXMC_A16,A2(Pin29)→PD12=EXMC_A17。
|
||||
* GD32E50x 用户手册 p869:16-bit 数据宽度时 EXMC 内部将 HADDR[25:1] 接到
|
||||
* EXMC_A[24:0],即 EXMC_A16=CPU 偏移 bit17(0x20000)、EXMC_A17=bit18(0x40000)。
|
||||
* LAN9252 手册 Table 9-3:A2=1 选 HBI_DATA_0,A1 选低/高半字。
|
||||
*
|
||||
* 访问模型(1.5.39 按 DS 12.11/表 12-13 修正):
|
||||
* 访问模型(DS 12.11/表 12-13):
|
||||
* HBI 索引直接寻址的只有 LAN9252 系统 CSR(BYTE_TEST 0x064 等)与访问端口;
|
||||
* EtherCAT 核心 CSR(0x0000-0x0FFF)经 ECAT_CSR_CMD(0x304)/ECAT_CSR_DATA(0x300)
|
||||
* 间接访问,过程 RAM(0x1000-0x1FFF)经 PRAM FIFO(0x000-0x03C + 0x308-0x314) 访问。
|
||||
* 1.5.37 及之前把索引空间当统一寻址,导致全部核心 CSR 读 0/写丢弃——
|
||||
* 主站恒读 AL=INIT 的真正根因(网络侧寄存器诊断证实 EEPROM/核心一直正常)。
|
||||
* 核心 CSR 不在 HBI 直接空间,直读恒 0、写丢弃。
|
||||
*/
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
@@ -59,7 +57,7 @@ constexpr uint32_t IRQ_CFG_IRQ_TYPE = 1UL << 0; /**< 1=推挽,0=开漏(开漏
|
||||
constexpr uint32_t INT_EN_ECAT_INT = 1UL << 0; /**< 允许 EtherCAT 事件驱动 IRQ 引脚。 */
|
||||
|
||||
/* ESC 过程数据看门狗(DS 12.14.41-43)。SII 不携带看门狗字段,芯片复位默认
|
||||
* divider=0x09C2(100µs)/WD Time PD=0x03E8(1000) 即 100ms——低于实测可用的
|
||||
* divider=0x09C2(100µs)/WD Time PD=0x03E8(1000) 即 100ms——等于
|
||||
* 100ms 手动调试周期,SM2 每 100ms 被判定超时,SSC 以 0x001B 拖回 SAFEOP。 */
|
||||
constexpr uint16_t ESC_REG_WD_DIVIDER = 0x0400U; /**< 看门狗分频(100µs 基准,复位默认 0x09C2)。 */
|
||||
constexpr uint16_t ESC_REG_WD_TIME_PD = 0x0420U; /**< 过程数据看门狗步数(复位默认 0x03E8=100ms)。 */
|
||||
@@ -233,9 +231,8 @@ bool EscLan9252::s_pmSuspended = false;
|
||||
void EscLan9252::hardwareReset()
|
||||
{
|
||||
/* 受控复位 ECAT_NRST(PB10 → LAN9252 Pin11 RST#,SCH 第1页 Pin47)。
|
||||
* 2026-08-22(1.5.25) 前固件从未初始化 PB10,RST# 仅靠 R44 10k 上拉自然释放,
|
||||
* 不满足手册 18.6.3:tpurstd(电源稳定→RST#释放)≥25ms、trstia(低脉宽)≥200µs,
|
||||
* 且要求释放前时钟输入已稳定。固件执行到此点时上电早已完成、晶振已稳定,
|
||||
* 手册 18.6.3 要求 tpurstd(电源稳定→RST#释放)≥25ms、trstia(低脉宽)≥200µs,
|
||||
* 且释放前时钟输入已稳定。固件执行到此点时上电早已完成、晶振已稳定,
|
||||
* 统一 30ms 低脉宽同时覆盖两个下限。RST# 释放瞬间重锁 CHIP_MODE0/1 strap
|
||||
* (LED_LINK1/2 经 R35/R36 10k 下拉 → 00b = 2 端口 HBI 模式)。
|
||||
* 释放后保持推挽输出高:空闲电平确定,并保留运行期软件复位手段;
|
||||
@@ -246,7 +243,10 @@ void EscLan9252::hardwareReset()
|
||||
gpio_bit_reset(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN); /* RST# 拉低 */
|
||||
SysTickTimer::delayMs(CONFIG_ESC_NRST_ASSERT_MS);
|
||||
gpio_bit_set(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN); /* 释放复位 */
|
||||
SysTickTimer::delayMs(CONFIG_ESC_HBI_POLL_STEP_MS);
|
||||
/* 释放后等 50ms:warm jump 后芯片不是冷上电,内部状态机/PHY
|
||||
* 重锁比 POR 慢;等太短时随后的 HBI 探针会读到启动中垃圾值。
|
||||
* 50ms 仍远小于 300ms 轮询窗,且 init 单次成本可接受。 */
|
||||
SysTickTimer::delayMs(50U);
|
||||
/* 回读实际电平:GD32 输入通路常连引脚,输出态也能读到真实电压。
|
||||
* 驱动高却读回低 => ECAT_NRST 网络被外部短路到地(或引脚损伤),
|
||||
* LAN9252 被按死在复位态 —— 直接解释 HBI 全浮空 + 晶振照振。 */
|
||||
@@ -255,7 +255,7 @@ void EscLan9252::hardwareReset()
|
||||
printf("[ECAT][ESC] hardwareReset: RST# low %ums -> released, pin reads %s\r\n",
|
||||
(unsigned)CONFIG_ESC_NRST_ASSERT_MS,
|
||||
pinHigh ? "HIGH" : "*** LOW (net shorted to GND / pin damaged) ***");
|
||||
/* RST# 复位清掉 PM 睡眠态(芯片回 D0),挂起门控随之解除(1.6.27) */
|
||||
/* RST# 复位清掉 PM 睡眠态(芯片回 D0),挂起门控随之解除 */
|
||||
s_pmSuspended = false;
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ uint8_t EscLan9252::nrstDrive(uint8_t action)
|
||||
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
|
||||
CONFIG_ESC_NRST_PIN);
|
||||
gpio_bit_set(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN);
|
||||
/* 复位释放后芯片重走上电流程回 D0,睡眠挂起随之解除(1.6.27) */
|
||||
/* 复位释放后芯片重走上电流程回 D0,睡眠挂起随之解除 */
|
||||
s_pmSuspended = false;
|
||||
break;
|
||||
case 1: /* 持续拉低(手动保持复位) */
|
||||
@@ -317,12 +317,12 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
|
||||
|
||||
uint8_t status;
|
||||
if (op == 0U) {
|
||||
/* 状态查询。注意:1.6.24 实测 HBI 读事务本身就会唤醒睡眠中的芯片,
|
||||
/* 状态查询。注意:HBI 读事务本身就会唤醒睡眠中的芯片,
|
||||
* 本操作的语义 = "读状态(若在睡眠则主动打破睡眠)",非无侵入观测。 */
|
||||
const uint32_t pmt = readReg32(LAN9252_REG_PMT_CTRL);
|
||||
status = pmPackStatus(pmt);
|
||||
/* 读到 D0(SLEEP_EN=0):芯片不在睡眠,若门控仍挂起(如 op4 唤醒超时后
|
||||
* 芯片延迟自醒)则自愈解除,恢复主循环/ISR 的 HBI 访问(1.6.27)。 */
|
||||
* 芯片延迟自醒)则自愈解除,恢复主循环/ISR 的 HBI 访问。 */
|
||||
if ((status & 0x02U) == 0U && s_pmSuspended) {
|
||||
s_pmSuspended = false;
|
||||
printf("[ECAT][PM] status: chip in D0, pm suspend gate released\r\n");
|
||||
@@ -344,15 +344,14 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
|
||||
/* PM_MODE 与 PM_SLEEP_EN 必须分两次写(DS 6.3.5.1 注:同一写中改 MODE 置 EN 无效)。 */
|
||||
writeReg32(LAN9252_REG_PMT_CTRL, v);
|
||||
writeReg32(LAN9252_REG_PMT_CTRL, v | PMT_CTRL_PM_SLEEP_EN);
|
||||
/* 1.6.27:立即挂起全部 HBI 消费者(task_runner 停 MainLoop/守护/心跳,
|
||||
/* 立即挂起全部 HBI 消费者(task_runner 停 MainLoop/守护/心跳,
|
||||
* ethercat_sync 三 ISR 清 pending 后直接返回)——本函数运行于 CS EXTI ISR,
|
||||
* 退出后主循环第一圈 ECAT_Main 读 0x220 就会把刚睡着的芯片叫醒;
|
||||
* 此前置位窗口内本 ISR 是唯一运行者(maskEnter 已屏 EXTI0/1),无竞态。 */
|
||||
s_pmSuspended = true;
|
||||
/* 1.6.24 实测:进入睡眠后任何 HBI 访问(含读!)都会立即触发唤醒——
|
||||
/* 进入睡眠后任何 HBI 访问(含读!)都会立即触发唤醒——
|
||||
* 手册 6.3.4.2 只说 HBI "write" 唤醒,但芯片实际按主机 CS 活动判定,
|
||||
* 写入后的读回验证会在 <50ms 内把芯片叫回 D0(板级复现:进 D1 后
|
||||
* 3s/6s 静默仍自动回 D0,唯读回验证为已知访问;去掉后待验证)。
|
||||
* 写入后的读回验证会把芯片叫回 D0。
|
||||
* 故此处置信构造返回值,不再读回;睡眠态观测交给"读状态"(读即唤醒,
|
||||
* 语义=主动打破睡眠),页面轮询须在睡眠态暂停。 */
|
||||
status = (uint8_t)(0x02U | ((uint32_t)op << 2)); /* READY=0 | SLEEP_EN=1 | MODE=op */
|
||||
@@ -390,7 +389,7 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
|
||||
restored ? "OK" : "FAIL (retry action 34)");
|
||||
}
|
||||
if (awake) {
|
||||
/* 唤醒成功:解除 HBI 挂起,主循环/ISR 恢复运行(1.6.27)。
|
||||
/* 唤醒成功:解除 HBI 挂起,主循环/ISR 恢复运行。
|
||||
* 超时则保持挂起——芯片大概率仍睡着;若实际已醒,下一次 op0
|
||||
* 状态查询读到 D0 会自愈解除。 */
|
||||
s_pmSuspended = false;
|
||||
@@ -407,69 +406,76 @@ uint8_t EscLan9252::pmDrive(uint8_t op)
|
||||
|
||||
bool EscLan9252::init()
|
||||
{
|
||||
s_busReady = false; /* 1.6.40:s_busReady = EXMC+BYTE_TEST 往返通过(IDX 残值不挡) */
|
||||
s_coreReady = false; /* 1.5.39:s_coreReady 语义 = EEPROM 加载完成、EtherCAT 核心使能 */
|
||||
s_busReady = false; /* EXMC+BYTE_TEST; warm 先探再定是否 RST# */
|
||||
s_coreReady = false; /* s_coreReady 语义 = EEPROM 加载完成、EtherCAT 核心使能 */
|
||||
|
||||
/* 使能 DWT 周期计数器,供 hw_gd32exmc/SSC 执行时间测量 */
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CYCCNT = 0U;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
|
||||
/* 受控 RST# 复位(仅首次;重复调用 init 不得二次复位,否则清掉 SSC 配置) */
|
||||
if (!s_resetDone) {
|
||||
hardwareReset();
|
||||
s_resetDone = true;
|
||||
}
|
||||
|
||||
/* 先起 EXMC 再谈复位。
|
||||
* warm jump:BOOT 刚用过 HBI,芯片活着;此时若先 RST# 再起 EXMC,等于
|
||||
* 把一条本来能通的总线按进复位重协商,一旦 RST# 释放时序不稳就整段假死。
|
||||
* cold:POR 后芯片也需要 EXMC 才谈得上探活。
|
||||
* 顺序固定为:EXMC → 无复位探 BYTE_TEST → 不通才 RST# 重来。 */
|
||||
if (!EscExmcBus::init()) {
|
||||
printf("[ECAT][ESC] init: FAIL, EXMC bus not ready (see [ECAT][EXMC] log)\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 1.6.40: 放行判据换到能兜住的那侧——BYTE_TEST 完整往返(索引写+数据低/高
|
||||
* 字读)才证明 HBI 通路通;HBI_IDX_0 只证明「芯片刚出过复位」(复位默认值
|
||||
* 0x1234,DS 9.5.4.2),证明不了通路。warm jump(BOOT 刷完直跳)时 BOOT 的
|
||||
* bootEcatDeinit 最后一条 HBI 写把索引寄存器留在 0x0054(IRQ_CFG),而跳转前
|
||||
* 那发硬复位已被证明会把 warm jump 打死(BOOT 1.1.0.18 已短路)——于是这条
|
||||
* 「读复位默认值」的探针对 warm 路径必然假阴性;cold 路径芯片自带 POR 天然
|
||||
* 0x1234,与那发复位有没有落地无关——这正是「reset 能起、刷完起不来」的根因。
|
||||
* BYTE_TEST 每次访问前都会重写索引,与复位态无关。
|
||||
* 结构:BYTE_TEST 为唯一放行门;不过才补一发 RST# 再探一轮(此刻 SSC 尚未
|
||||
* MainInit,二次复位零代价;s_resetDone 只保护运行期重复 init,不挡 init 内
|
||||
* 重试)。两轮 BYTE_TEST 仍不通 = 总线物理不通,维持降级。
|
||||
* warm 残值(如 0x0054)跳过 0x1234 轮询,避免无意义 300ms 等待 + 二次复位。 */
|
||||
uint16_t idx0 = 0U;
|
||||
uint32_t byteTest = 0U;
|
||||
for (uint8_t attempt = 0U; attempt < 2U; ++attempt) {
|
||||
if (attempt > 0U) {
|
||||
printf("[ECAT][ESC] probe#%u fail (idx0=0x%04X byte_test=0x%08lX), 补发 RST# 重探\r\n",
|
||||
(unsigned)attempt, (unsigned)idx0, (unsigned long)byteTest);
|
||||
hardwareReset();
|
||||
}
|
||||
idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET);
|
||||
/* 仅在「像复位中/未驱动」时轮询 0x1234;warm 残值(非 0/0xFFFF/0x1234)说明
|
||||
* 芯片活着且带着上次索引,直接测 BYTE_TEST,不空等。 */
|
||||
const bool idxLooksIdle = (idx0 == 0x0000U) || (idx0 == 0xFFFFU);
|
||||
if (idxLooksIdle || idx0 == 0x1234U) {
|
||||
for (uint32_t waited = 0U;
|
||||
idx0 != 0x1234U && waited < CONFIG_ESC_HBI_POLL_TIMEOUT_MS;
|
||||
waited += CONFIG_ESC_HBI_POLL_STEP_MS) {
|
||||
SysTickTimer::delayMs(CONFIG_ESC_HBI_POLL_STEP_MS);
|
||||
idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET);
|
||||
|
||||
/* 第 0 轮:不复位直接探。warm 残值下 BOOT 刚写过 HBI,BYTE_TEST 应立刻可读。 */
|
||||
idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET);
|
||||
byteTest = readReg32(LAN9252_REG_BYTE_TEST);
|
||||
if (byteTest == LAN9252_BYTE_TEST_VALUE) {
|
||||
printf("[ECAT][ESC] probe: BYTE_TEST ok without RST# (idx0=0x%04X) — 总线已活,跳过复位\r\n",
|
||||
(unsigned)idx0);
|
||||
s_resetDone = true;
|
||||
} else {
|
||||
printf("[ECAT][ESC] probe: BYTE_TEST=0x%08lX idx0=0x%04X, 补 RST# 最多 3 轮\r\n",
|
||||
(unsigned long)byteTest, (unsigned)idx0);
|
||||
bool alive = false;
|
||||
for (uint8_t attempt = 0U; attempt < 3U; ++attempt) {
|
||||
if (!s_resetDone || attempt > 0U) {
|
||||
hardwareReset();
|
||||
s_resetDone = true;
|
||||
}
|
||||
/* 复位后重配 EXMC:BOOT 关时钟时 PD/PE 停在 AF,MCU 侧寄存器也可能残留 */
|
||||
if (!EscExmcBus::init()) {
|
||||
continue;
|
||||
}
|
||||
idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET);
|
||||
/* 仅在「像复位中/未驱动」时轮询 0x1234;warm 残值直接测 BYTE_TEST */
|
||||
const bool idxLooksIdle = (idx0 == 0x0000U) || (idx0 == 0xFFFFU);
|
||||
if (idxLooksIdle || idx0 == 0x1234U) {
|
||||
for (uint32_t waited = 0U;
|
||||
idx0 != 0x1234U && waited < CONFIG_ESC_HBI_POLL_TIMEOUT_MS;
|
||||
waited += CONFIG_ESC_HBI_POLL_STEP_MS) {
|
||||
SysTickTimer::delayMs(CONFIG_ESC_HBI_POLL_STEP_MS);
|
||||
idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET);
|
||||
}
|
||||
}
|
||||
byteTest = readReg32(LAN9252_REG_BYTE_TEST);
|
||||
if (byteTest == LAN9252_BYTE_TEST_VALUE) {
|
||||
alive = true;
|
||||
printf("[ECAT][ESC] probe#%u: BYTE_TEST ok (idx0=0x%04X)\r\n",
|
||||
(unsigned)(attempt + 1U), (unsigned)idx0);
|
||||
break;
|
||||
}
|
||||
printf("[ECAT][ESC] probe#%u fail (idx0=0x%04X byte_test=0x%08lX)\r\n",
|
||||
(unsigned)(attempt + 1U), (unsigned)idx0, (unsigned long)byteTest);
|
||||
}
|
||||
byteTest = readReg32(LAN9252_REG_BYTE_TEST);
|
||||
if (byteTest == LAN9252_BYTE_TEST_VALUE) {
|
||||
break;
|
||||
if (!alive) {
|
||||
printf("[ECAT][ESC] init: FAIL, BYTE_TEST=0x%08lX (expect 0x87654321), idx0=0x%04X -> LAN not driving bus\r\n",
|
||||
(unsigned long)byteTest, (unsigned)idx0);
|
||||
printf("[ECAT][ESC] hint: check CS(PD7) pulse, CHIP_MODE0/1 straps=00, RST#/power\r\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (byteTest != LAN9252_BYTE_TEST_VALUE) {
|
||||
printf("[ECAT][ESC] init: FAIL, BYTE_TEST=0x%08lX (expect 0x87654321), idx0=0x%04X -> LAN not driving bus\r\n",
|
||||
(unsigned long)byteTest, (unsigned)idx0);
|
||||
printf("[ECAT][ESC] hint: check CS(PD7) pulse, CHIP_MODE0/1 straps=00, RST#/power\r\n");
|
||||
return false;
|
||||
}
|
||||
if (idx0 != 0x1234U) {
|
||||
printf("[ECAT][ESC] init: WARN idx0=0x%04X != 0x1234, BYTE_TEST ok -> 复位态未知但通路可用,放行\r\n",
|
||||
(unsigned)idx0);
|
||||
@@ -478,10 +484,9 @@ bool EscLan9252::init()
|
||||
(unsigned)idx0);
|
||||
s_busReady = true;
|
||||
|
||||
/* 等待 PDI 就绪、EtherCAT 核心可交互(1.5.37 新增,1.5.39 改经 CSR 间接端口读——
|
||||
* 此前直读 HBI 索引,DL_STATUS 属 EtherCAT 核心寄存器,根本不在直接地址空间)。
|
||||
/* 等待 PDI 就绪、EtherCAT 核心可交互。
|
||||
* DL_STATUS 属 EtherCAT 核心寄存器,不在 HBI 直接空间,必须经 CSR 间接端口读。
|
||||
* 判据:DL_STATUS(0x0110) bit0 = PDI operational / EEPROM loaded。
|
||||
* 网络侧诊断已证实本板 EEPROM 加载与核心均正常,此等待应几乎立即通过;
|
||||
* 超时意味着 PDI 真的未就绪(EEPROM 内容/I2C/strap 问题),此时 SSC 的
|
||||
* AL Event Mask/邮箱 SM 配置会落空,由 initial.cpp 按 isCoreReady() 门控跳过。 */
|
||||
uint16_t dlStatus = (uint16_t)csrRead(ESC_REG_DL_STATUS, 2U, false);
|
||||
@@ -509,11 +514,10 @@ bool EscLan9252::init()
|
||||
|
||||
/**
|
||||
* @brief 使能 LAN9252 的 IRQ 引脚输出,让 EtherCAT 事件真正到达 GD32 的 PB1。
|
||||
* @note 1.5.43 新增。IRQ_CFG(0x054)/INT_EN(0x05C) 都是 LAN9252 系统 CSR
|
||||
* @note IRQ_CFG(0x054)/INT_EN(0x05C) 都是 LAN9252 系统 CSR
|
||||
* (HBI 索引直接可达,不走 CSR 间接端口),复位默认值均为 0,即
|
||||
* IRQ_EN=0(引脚永久去断言) 且 ECAT_INT_EN=0(EtherCAT 事件不上报)。
|
||||
* 此前固件从未配置它们,导致 PDI_Isr 一次都没被调用过:SSC 因此
|
||||
* 永远等不到 PROCESS_OUTPUT_EVENT、bEcatFirstOutputsReceived 恒 FALSE,
|
||||
* 不配这两个寄存器,PDI_Isr 不会触发,SSC 永远等不到 PROCESS_OUTPUT_EVENT,
|
||||
* SAFEOP->OP 被无限挂起(表现为主站 "进 OP 失败 code=0x0000")。
|
||||
* 极性必须与 ethercat_sync.cpp 的 EXTI 配置一致:那里是上拉输入 +
|
||||
* 下降沿触发,故此处选推挽(IRQ_TYPE=1) + 低有效(IRQ_POL=0)。
|
||||
@@ -521,7 +525,7 @@ bool EscLan9252::init()
|
||||
*/
|
||||
void EscLan9252::configPdWatchdog()
|
||||
{
|
||||
/* 1.5.45:过程数据看门狗 100ms(复位默认)→1s。
|
||||
/* 过程数据看门狗 100ms(复位默认)→1s。
|
||||
* 主站侧手动单步/100ms 调试周期会触发 SM2 看门狗:SSC 在 CheckIfEcatError()
|
||||
* 读 0x0440 发现已过期,调 AL_ControlInd(STATE_SAFEOP, 0x001B) 把 OP 拖回 SAFEOP,
|
||||
* 表现为"100ms 周期全 WKC 错、10/50ms 正常"。分频保持默认 100µs,只延长步数;
|
||||
@@ -562,12 +566,12 @@ uint32_t EscLan9252::csrRead(uint16_t addr, uint8_t size, bool verify)
|
||||
{
|
||||
/* DS 12.11.1:写 CSR_CMD(BUSY|R_nW|SIZE|ADDR) 启动,busy 自清后读 CSR_DATA;
|
||||
* 有效数据总在 CSR_DATA 低位,硬件处理字节对齐。
|
||||
* 1.5.59 加固(verify=true):CSR_DATA 是锁存寄存器(非 FIFO),命令不变时连读两次必一致;
|
||||
* 不一致即本拍 EXMC 毛刺——历史上这类错读曾让 GetSyncMan 把 0x1204/0x1208
|
||||
* 缓存成邮箱几何(→SSC 邮箱楔死,根治见 mailbox.c 构建期常量),或让
|
||||
* CheckSmSettings 以 0x0016/0x001F 瞬态打断状态转换。对错读整事务重跑一次;
|
||||
* 两轮仍不一致则交出末拍(与旧行为一致的兜底,邮箱几何另有常量兜底)。
|
||||
* 1.6.25 verify=false:单读路径,供诊断回读/MII busy 轮询/init 轮询等低风险
|
||||
* verify=true(默认):CSR_DATA 是锁存寄存器(非 FIFO),命令不变时连读两次必一致;
|
||||
* 不一致即本拍 EXMC 毛刺——错读可能让 GetSyncMan 把错误值缓存成邮箱几何
|
||||
* (→SSC 邮箱楔死,邮箱几何另有 mailbox.c 构建期常量兜底),或让
|
||||
* CheckSmSettings 以瞬态值打断状态转换。对错读整事务重跑一次;
|
||||
* 两轮仍不一致则交出末拍。
|
||||
* verify=false:单读路径,供诊断回读/MII busy 轮询/init 轮询等低风险
|
||||
* 调用方使用;SM/AL/邮箱等状态机关键寄存器必须保持默认双读。 */
|
||||
if (!verify) {
|
||||
uint32_t n = POLL_MAX;
|
||||
@@ -611,13 +615,9 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
|
||||
* 首 DWORD 有效字节从 (addr&3) 起(表 12-7),末 DWORD 尾部无效(表 12-8)。
|
||||
* 别名口 0x00-0x1C 功能相同,轮换寻址即官方突发用法;每读满一个 DWORD 弹一项。
|
||||
*
|
||||
* 1.6.2 回退逐 DWORD 轮询,弹前必查 AVAIL 位。1.6.0 曾信 CMD 的
|
||||
* AVAIL_CNT(bits[12:8])一次弹一批:实测 12B 短读(1.6.0 输出探针,主站窗口
|
||||
* 请求 WIN@8 靠它送达)首 DWORD 新鲜、后续 DWORD 取到 FIFO 陈旧项,窗口
|
||||
* 基址冻结,槽位 7..26 的电机状态因此永不上报(仅 seq 沿触发的 384B 全量读
|
||||
* 路径能拿到新 WIN)。批量模式 1.6.0 上机对拍本来就是零收益,384/496B 长读
|
||||
* 未见损坏,但邮箱短读/探针这类小传输只验证过"逐 DWORD 查 AVAIL"的旧模式
|
||||
* ——在弄清该计数器对短读的精确语义之前,不要恢复批量弹 DWORD。 */
|
||||
* 必须逐 DWORD 轮询并弹前查 AVAIL 位。CMD 的 AVAIL_CNT(bits[12:8])批量弹模式
|
||||
* 对短读不可靠:首 DWORD 新鲜、后续 DWORD 可能取到 FIFO 陈旧项。邮箱短读/探针
|
||||
* 类小传输只验证过"逐 DWORD 查 AVAIL"模式,不要改回批量弹 DWORD。 */
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
@@ -646,8 +646,7 @@ void EscLan9252::pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
/* DS 12.11.4:写 ADDR_LEN 再置 BUSY 启动;首 DWORD 有效字节从 (addr&3) 起,
|
||||
* 引擎只取有效字节;别名口 0x20-0x3C 轮换;写满一个 DWORD 推一项。
|
||||
* 1.6.2 与 pramRead 同步回退逐 DWORD 轮询(推前必查 AVAIL 位):1.6.0 批量
|
||||
* 模式实测零收益,且与短读损坏同源(见 pramRead 注),邮箱小 PDU 写同类风险。 */
|
||||
* 与 pramRead 同样逐 DWORD 轮询(推前必查 AVAIL 位),邮箱小 PDU 写同类风险。 */
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
@@ -683,7 +682,7 @@ uint32_t EscLan9252::readChipId()
|
||||
|
||||
uint16_t EscLan9252::initOnlyReadReg(uint16_t addr)
|
||||
{
|
||||
/* 1.5.39:ESC 核心 CSR 经间接端口读(此前直读 HBI 索引,核心寄存器不在直接空间,恒 0) */
|
||||
/* ESC 核心 CSR 经间接端口读(核心寄存器不在 HBI 直接空间,直读恒 0) */
|
||||
return (uint16_t)csrRead(addr, 2U);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user