fix(esc_lan9252): 修复LAN9252 HBI访问模型和IRQ输出使能问题
- 修正HBI访问模型:将ESC核心CSR(0x0000-0x0FFF)改为通过ECAT_CSR_CMD/DATA 间接端口访问,解决此前直接寻址导致核心寄存器恒读0/写丢弃的问题 - 新增csrRead/csrWrite/pramRead/pramWrite接口,正确实现LAN9252访问端口操作 - 添加PDI就绪检测:等待DL_STATUS bit0置位确认EEPROM加载完成和EtherCAT核心使能 - 实现IRQ引脚输出使能:配置IRQ_CFG和INT_EN寄存器,使EtherCAT事件能到达PB1, 解决PDI_Isr未触发导致SAFEOP->OP无限挂起的问题 - 为ESC_EXMC硬件诊断添加CONFIG_ESC_EXMC_DIAG门控,默认关闭避免影响正常运行 - 修正system_gd32e50x.c中HXTAL故障诊断的寄存器偏移和位设置错
This commit is contained in:
@@ -14,7 +14,12 @@
|
||||
* EXMC_A[24:0],即 EXMC_A16=CPU 偏移 bit17(0x20000)、EXMC_A17=bit18(0x40000)。
|
||||
* LAN9252 手册 Table 9-3:A2=1 选 HBI_DATA_0,A1 选低/高半字。
|
||||
*
|
||||
* 所有 ESC 寄存器/PDI 内存访问均通过"写 IDX -> 读写 DATA"完成。
|
||||
* 访问模型(1.5.39 按 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/核心一直正常)。
|
||||
*/
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
@@ -39,6 +44,34 @@ constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1(EXMC_A17), A1=1(EXMC
|
||||
* 芯片身份探针用 BYTE_TEST 寄存器(0x064),不是 ESC 0x0000(那是类型寄存器=0xC0)。 */
|
||||
constexpr uint16_t LAN9252_REG_BYTE_TEST = 0x0064U; /**< 字节顺序测试寄存器(默认 0x87654321,LAN9252 手册 16.1.2)。 */
|
||||
constexpr uint32_t LAN9252_BYTE_TEST_VALUE = 0x87654321U; /**< BYTE_TEST 默认值(小端序)。 */
|
||||
constexpr uint16_t ESC_REG_DL_STATUS = 0x0110U; /**< DL Status:bit0 = PDI operational / EEPROM loaded。 */
|
||||
constexpr uint16_t ESC_REG_EEP_CSR = 0x0502U; /**< EEPROM Control/Status:bit11=校验错,bit12=加载错(0=OK)。 */
|
||||
|
||||
/* LAN9252 系统中断寄存器(HBI 索引直接可达,DS 8.3 表 0.1)。
|
||||
* IRQ 引脚上电默认全关:IRQ_CFG.IRQ_EN=0 且 INT_EN.ECAT_INT_EN=0,
|
||||
* 不配这两个寄存器则 PB1 永远收不到中断,SSC 的 PDI_Isr 从不执行。 */
|
||||
constexpr uint16_t LAN9252_REG_IRQ_CFG = 0x0054U; /**< 中断配置:bit8=IRQ_EN,bit4=IRQ_POL,bit0=IRQ_TYPE。 */
|
||||
constexpr uint16_t LAN9252_REG_INT_STS = 0x0058U; /**< 中断状态(R/WC):bit0=ECAT_INT。 */
|
||||
constexpr uint16_t LAN9252_REG_INT_EN = 0x005CU; /**< 中断使能:bit0=ECAT_INT_EN。 */
|
||||
constexpr uint32_t IRQ_CFG_IRQ_EN = 1UL << 8; /**< IRQ 引脚输出总闸。 */
|
||||
constexpr uint32_t IRQ_CFG_IRQ_POL = 1UL << 4; /**< 1=高有效,0=低有效(开漏时忽略本位)。 */
|
||||
constexpr uint32_t IRQ_CFG_IRQ_TYPE = 1UL << 0; /**< 1=推挽,0=开漏(开漏强制低有效)。 */
|
||||
constexpr uint32_t INT_EN_ECAT_INT = 1UL << 0; /**< 允许 EtherCAT 事件驱动 IRQ 引脚。 */
|
||||
|
||||
/* LAN9252 直接映射的 ESC 访问端口(DS 表 12-13;经 HBI 索引直接读写) */
|
||||
constexpr uint16_t ECAT_PRAM_RD_DATA = 0x0000U; /**< PRAM 读 FIFO(0x00-0x1C 别名,功能相同,突发用) */
|
||||
constexpr uint16_t ECAT_PRAM_WR_DATA = 0x0020U; /**< PRAM 写 FIFO(0x20-0x3C 别名) */
|
||||
constexpr uint16_t ECAT_CSR_DATA_REG = 0x0300U; /**< ESC 核心 CSR 间接访问数据寄存器 */
|
||||
constexpr uint16_t ECAT_CSR_CMD_REG = 0x0304U; /**< ESC 核心 CSR 间接访问命令寄存器 */
|
||||
constexpr uint16_t ECAT_PRAM_RD_ADDR_LEN = 0x0308U; /**< PRAM 读地址/长度 */
|
||||
constexpr uint16_t ECAT_PRAM_RD_CMD = 0x030CU; /**< PRAM 读命令/状态 */
|
||||
constexpr uint16_t ECAT_PRAM_WR_ADDR_LEN = 0x0310U; /**< PRAM 写地址/长度 */
|
||||
constexpr uint16_t ECAT_PRAM_WR_CMD = 0x0314U; /**< PRAM 写命令/状态 */
|
||||
constexpr uint32_t CSR_BUSY = 1UL << 31; /**< CSR_CMD:写 1 启动,完成自清 */
|
||||
constexpr uint32_t CSR_RNW = 1UL << 30; /**< CSR_CMD:1=读 0=写 */
|
||||
constexpr uint32_t PRAM_BUSY = 1UL << 31; /**< PRAM_*_CMD:写 1 启动,完成自清 */
|
||||
constexpr uint32_t PRAM_AVAIL = 1UL << 0; /**< PRAM_*_CMD:数据可用(读)/空间可用(写) */
|
||||
constexpr uint32_t POLL_MAX = 2000UL; /**< busy/avail 轮询上限(~ms 级,防芯片异常时死等) */
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
void setHbiIndex(uint16_t addr)
|
||||
@@ -65,10 +98,9 @@ void writeDataLow(uint16_t v)
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
uint16_t readReg16(uint16_t addr)
|
||||
void writeDataHigh(uint16_t v)
|
||||
{
|
||||
setHbiIndex(addr);
|
||||
return readDataLow();
|
||||
EscExmcBus::write16(HBI_DATA0_HIGH_OFF, v);
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
@@ -81,14 +113,16 @@ uint32_t readReg32(uint16_t addr)
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
void writeReg16(uint16_t addr, uint16_t v)
|
||||
void writeReg32(uint16_t addr, uint32_t v)
|
||||
{
|
||||
setHbiIndex(addr);
|
||||
writeDataLow(v);
|
||||
writeDataLow((uint16_t)(v & 0xFFFFU));
|
||||
writeDataHigh((uint16_t)(v >> 16));
|
||||
}
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool EscLan9252::s_busReady = false;
|
||||
bool EscLan9252::s_coreReady = false;
|
||||
bool EscLan9252::s_resetDone = false;
|
||||
|
||||
void EscLan9252::hardwareReset()
|
||||
@@ -155,6 +189,7 @@ uint8_t EscLan9252::nrstDrive(uint8_t action)
|
||||
bool EscLan9252::init()
|
||||
{
|
||||
s_busReady = false; /* 1.5.27:s_busReady 语义 = init 完整成功(EXMC+探针+BYTE_TEST) */
|
||||
s_coreReady = false; /* 1.5.39:s_coreReady 语义 = EEPROM 加载完成、EtherCAT 核心使能 */
|
||||
|
||||
/* 使能 DWT 周期计数器,供 hw_gd32exmc/SSC 执行时间测量 */
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
@@ -203,9 +238,151 @@ bool EscLan9252::init()
|
||||
}
|
||||
printf("[ECAT][ESC] init: OK, HBI alive (IDX=0x1234, BYTE_TEST=0x87654321)\r\n");
|
||||
s_busReady = true;
|
||||
|
||||
/* 等待 PDI 就绪、EtherCAT 核心可交互(1.5.37 新增,1.5.39 改经 CSR 间接端口读——
|
||||
* 此前直读 HBI 索引,DL_STATUS 属 EtherCAT 核心寄存器,根本不在直接地址空间)。
|
||||
* 判据: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);
|
||||
uint32_t waited = 0U;
|
||||
while ((dlStatus & 0x0001U) == 0U && waited < CONFIG_ESC_HBI_POLL_TIMEOUT_MS) {
|
||||
SysTickTimer::delayMs(CONFIG_ESC_HBI_POLL_STEP_MS);
|
||||
waited += CONFIG_ESC_HBI_POLL_STEP_MS;
|
||||
dlStatus = (uint16_t)csrRead(ESC_REG_DL_STATUS, 2U);
|
||||
}
|
||||
if ((dlStatus & 0x0001U) != 0U) {
|
||||
s_coreReady = true;
|
||||
printf("[ECAT][ESC] PDI operational, EtherCAT core ready (DL_STATUS=0x%04X, waited %ums)\r\n",
|
||||
(unsigned)dlStatus, (unsigned)waited);
|
||||
enableEcatIrqOutput();
|
||||
} else {
|
||||
printf("[ECAT][ESC] init: FAIL, PDI not operational after %ums "
|
||||
"(DL_STATUS=0x%04X EEP_CSR=0x%04X)\r\n",
|
||||
(unsigned)CONFIG_ESC_HBI_POLL_TIMEOUT_MS, (unsigned)dlStatus,
|
||||
(unsigned)csrRead(ESC_REG_EEP_CSR, 2U));
|
||||
printf("[ECAT][ESC] hint: 用 tools/ecat/ecat_esc_regs.py 从网络侧读 0x0502 错误位区分 EEPROM/I2C/内容问题\r\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使能 LAN9252 的 IRQ 引脚输出,让 EtherCAT 事件真正到达 GD32 的 PB1。
|
||||
* @note 1.5.43 新增。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,
|
||||
* SAFEOP->OP 被无限挂起(表现为主站 "进 OP 失败 code=0x0000")。
|
||||
* 极性必须与 ethercat_sync.cpp 的 EXTI 配置一致:那里是上拉输入 +
|
||||
* 下降沿触发,故此处选推挽(IRQ_TYPE=1) + 低有效(IRQ_POL=0)。
|
||||
* 注意 AL Event Mask(0x204) 由 SSC 在状态迁移时自行维护,不在此处设置。
|
||||
*/
|
||||
void EscLan9252::enableEcatIrqOutput()
|
||||
{
|
||||
/* 先清掉可能悬留的状态位(R/WC:写 1 清零),避免使能瞬间就有一个陈旧电平。 */
|
||||
writeReg32(LAN9252_REG_INT_STS, INT_EN_ECAT_INT);
|
||||
/* 允许 EtherCAT 事件汇入 IRQ。 */
|
||||
writeReg32(LAN9252_REG_INT_EN, INT_EN_ECAT_INT);
|
||||
/* 打开引脚总闸:推挽 + 低有效(IRQ_POL=0 即不置该位),去断言间隔保持 0=禁用。 */
|
||||
writeReg32(LAN9252_REG_IRQ_CFG, IRQ_CFG_IRQ_EN | IRQ_CFG_IRQ_TYPE);
|
||||
|
||||
const uint32_t cfg = readReg32(LAN9252_REG_IRQ_CFG);
|
||||
const uint32_t en = readReg32(LAN9252_REG_INT_EN);
|
||||
if ((cfg & IRQ_CFG_IRQ_EN) != 0U && (en & INT_EN_ECAT_INT) != 0U) {
|
||||
printf("[ECAT][ESC] IRQ output enabled (IRQ_CFG=0x%08lX INT_EN=0x%08lX, push-pull active-low)\r\n",
|
||||
(unsigned long)cfg, (unsigned long)en);
|
||||
} else {
|
||||
printf("[ECAT][ESC] WARN: IRQ enable readback failed (IRQ_CFG=0x%08lX INT_EN=0x%08lX)"
|
||||
" -> PDI_Isr 不会触发,SAFEOP->OP 会卡住\r\n",
|
||||
(unsigned long)cfg, (unsigned long)en);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EscLan9252::csrRead(uint16_t addr, uint8_t size)
|
||||
{
|
||||
/* DS 12.11.1:写 CSR_CMD(BUSY|R_nW|SIZE|ADDR) 启动,busy 自清后读 CSR_DATA;
|
||||
* 有效数据总在 CSR_DATA 低位,硬件处理字节对齐。 */
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
writeReg32(ECAT_CSR_CMD_REG, CSR_BUSY | CSR_RNW | ((uint32_t)size << 16) | addr);
|
||||
n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
return readReg32(ECAT_CSR_DATA_REG);
|
||||
}
|
||||
|
||||
void EscLan9252::csrWrite(uint16_t addr, uint32_t data, uint8_t size)
|
||||
{
|
||||
/* DS 12.11.2:先写 CSR_DATA(数据在低位),再写 CSR_CMD(BUSY|SIZE|ADDR,R_nW=0)。 */
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
writeReg32(ECAT_CSR_DATA_REG, data);
|
||||
writeReg32(ECAT_CSR_CMD_REG, CSR_BUSY | ((uint32_t)size << 16) | addr);
|
||||
n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
}
|
||||
|
||||
void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
|
||||
{
|
||||
/* DS 12.11.3:写 ADDR_LEN 再置 BUSY 启动;引擎按 DWORD 向下对齐填充读 FIFO,
|
||||
* 首 DWORD 有效字节从 (addr&3) 起(表 12-7),末 DWORD 尾部无效(表 12-8)。
|
||||
* 别名口 0x00-0x1C 功能相同,轮换寻址即官方突发用法;每读满一个 DWORD 弹一项。 */
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
writeReg32(ECAT_PRAM_RD_ADDR_LEN, ((uint32_t)len << 16) | addr);
|
||||
writeReg32(ECAT_PRAM_RD_CMD, PRAM_BUSY);
|
||||
uint16_t done = 0U;
|
||||
uint16_t alias = 0U;
|
||||
while (done < len) {
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_RD_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
if (n == 0U) {
|
||||
break; /* 芯片异常:避免死等,交出残缺数据 */
|
||||
}
|
||||
const uint32_t dw = readReg32((uint16_t)(ECAT_PRAM_RD_DATA + alias));
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)(addr & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
buf[done + i] = (uint8_t)(dw >> (8U * (lead + i)));
|
||||
}
|
||||
done = (uint16_t)(done + take);
|
||||
}
|
||||
}
|
||||
|
||||
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 推一项。 */
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
writeReg32(ECAT_PRAM_WR_ADDR_LEN, ((uint32_t)len << 16) | addr);
|
||||
writeReg32(ECAT_PRAM_WR_CMD, PRAM_BUSY);
|
||||
uint16_t done = 0U;
|
||||
uint16_t alias = 0U;
|
||||
while (done < len) {
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
if (n == 0U) {
|
||||
break; /* 芯片异常:放弃剩余字节 */
|
||||
}
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)((uint16_t)(addr + done) & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
uint32_t dw = 0U;
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
dw |= (uint32_t)buf[done + i] << (8U * (lead + i));
|
||||
}
|
||||
writeReg32((uint16_t)(ECAT_PRAM_WR_DATA + alias), dw);
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
done = (uint16_t)(done + take);
|
||||
}
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_BUSY) != 0U && --n != 0U) {}
|
||||
}
|
||||
|
||||
uint32_t EscLan9252::readChipId()
|
||||
{
|
||||
/* INIT-ONLY:init 阶段中断未使能,直接读取 BYTE_TEST(32-bit) */
|
||||
@@ -214,10 +391,11 @@ uint32_t EscLan9252::readChipId()
|
||||
|
||||
uint16_t EscLan9252::initOnlyReadReg(uint16_t addr)
|
||||
{
|
||||
return readReg16(addr);
|
||||
/* 1.5.39:ESC 核心 CSR 经间接端口读(此前直读 HBI 索引,核心寄存器不在直接空间,恒 0) */
|
||||
return (uint16_t)csrRead(addr, 2U);
|
||||
}
|
||||
|
||||
void EscLan9252::initOnlyWriteReg(uint16_t addr, uint16_t v)
|
||||
{
|
||||
writeReg16(addr, v);
|
||||
csrWrite(addr, v, 2U);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user