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:
@@ -51,6 +51,9 @@ constexpr uint32_t kPinWr = 5U; /* PD5 */
|
||||
constexpr uint32_t kPinA1 = 11U; /* PD11 */
|
||||
constexpr uint32_t kPinA2 = 12U; /* PD12 */
|
||||
|
||||
/* bbDelay/bbReadData 仅被 diagDump 使用,随之一起门控,避免默认关闭时
|
||||
* 触发 -Wunused-function;bbDriveData 被 continuityDrive 复用,保持常开。 */
|
||||
#if CONFIG_ESC_EXMC_DIAG
|
||||
void bbDelay() {
|
||||
for (volatile uint32_t i = 0U; i < 3000U; i++) {
|
||||
} /* ~40-80us @180MHz,远大于 HBI 时序要求 */
|
||||
@@ -78,6 +81,7 @@ uint16_t bbReadData() {
|
||||
if (d & (1U << 10)) v |= (uint16_t)(1U << 15); /* D15 */
|
||||
return v;
|
||||
}
|
||||
#endif /* CONFIG_ESC_EXMC_DIAG (bbDelay + bbReadData) */
|
||||
|
||||
void bbDriveData(uint16_t v) {
|
||||
uint32_t dset = 0U, dclr = 0U, eset = 0U, eclr = 0U;
|
||||
@@ -233,8 +237,12 @@ bool EscExmcBus::init() {
|
||||
ECAT_LOGI("[ECAT][EXMC] init: Region0 enabled, chip ID will be verified by "
|
||||
"ESC driver\r\n");
|
||||
|
||||
/* ---- 硬件诊断: 转储关键寄存器 + GPIO 手动输出测试 ---- */
|
||||
/* ---- 硬件诊断: 转储关键寄存器 + GPIO 手动输出测试 ----
|
||||
* 1.5.33: 默认关闭。硬件层已确认全通,探针仅换板/补焊排查浮空时经
|
||||
* CONFIG_ESC_EXMC_DIAG 临时开启(会拆 EXMC 引脚成 GPIO,正常运行不该跑)。 */
|
||||
#if CONFIG_ESC_EXMC_DIAG
|
||||
diagDump();
|
||||
#endif
|
||||
|
||||
s_ok = true;
|
||||
ECAT_LOGI("[ECAT][EXMC] init: OK, bus enabled\r\n");
|
||||
@@ -243,7 +251,10 @@ bool EscExmcBus::init() {
|
||||
|
||||
/* ========================================================================
|
||||
* 硬件诊断: 精简版 —— 只保留核心探活 + bit-bang + 位级回显
|
||||
* 1.5.33: 整段由 CONFIG_ESC_EXMC_DIAG 门控,默认不编译。bbDelay/bbReadData/
|
||||
* bbDriveData 等位拍辅助函数在上方匿名 namespace 中,continuityDrive 仍复用,保留。
|
||||
* ======================================================================== */
|
||||
#if CONFIG_ESC_EXMC_DIAG
|
||||
void EscExmcBus::diagDump() {
|
||||
/* EtherCAT 同步 ISR 激活期间禁止诊断:本函数会把 EXMC 数据/控制线切成 GPIO,
|
||||
* 并大量 printf,会周期性拆毁运行中的 EtherCAT 总线。 */
|
||||
@@ -389,6 +400,7 @@ void EscExmcBus::diagDump() {
|
||||
printf("[EXMC-DIAG] === HBI ALIVE ===\r\n");
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_ESC_EXMC_DIAG */
|
||||
|
||||
/* ========================================================================
|
||||
* 通断测试: 网页逐个 pattern 触发,GD32 驱动数据线/控制线输出已知电平,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
/**
|
||||
* @file esc_lan9252.h
|
||||
* @brief LAN9252 EtherCAT 从站控制器(ESC)驱动接口。
|
||||
* @note 通过 EscExmcBus 16-bit 并行总线,以 HBI Indexed Address Mode 访问
|
||||
* LAN9252 寄存器与 PDI 内存:先写 HBI_IDX_0,再读写 HBI_DATA_0。
|
||||
* @note 通过 EscExmcBus 16-bit 并行总线,以 HBI Indexed Address Mode 访问 LAN9252。
|
||||
* 访问模型(1.5.39 按 DS 12.11 修正,此前误以为整个 16 位索引空间统一可寻址):
|
||||
* - HBI 直接地址空间:仅 LAN9252 系统 CSR(BYTE_TEST 0x064 等)+ 访问端口
|
||||
* (PRAM FIFO 0x000-0x03C、ECAT_CSR_DATA/CMD 0x300/0x304、PRAM_ADDR_LEN/CMD
|
||||
* 0x308-0x314)——经 HBI_IDX_0/HBI_DATA_0 直接读写;
|
||||
* - EtherCAT 核心 CSR(0x0000-0x0FFF:AL Status、SyncManager、EEPROM 接口等)
|
||||
* 不在直接空间,必须经 ECAT_CSR_CMD/CSR_DATA 间接访问(csrRead/csrWrite);
|
||||
* - 过程 RAM(0x1000-0x1FFF:邮箱/PDO)必须经 PRAM FIFO(pramRead/pramWrite)。
|
||||
*
|
||||
* 接口调用约束(避免 EXMC 总线争用):
|
||||
* - 运行期 ESC 寄存器/PDI 访问统一走 SSC 移植层 hw_gd32exmc.cpp 的 HW 系列函数
|
||||
@@ -46,18 +52,49 @@ public:
|
||||
*/
|
||||
static uint32_t readChipId();
|
||||
|
||||
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SSC 移植层(HW_*)。 */
|
||||
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SSC 移植层(HW_*)。
|
||||
* @note 1.5.39 起经 CSR_CMD/CSR_DATA 间接端口读 ESC 核心 CSR(此前直读 HBI 索引,
|
||||
* 而 EtherCAT 核心寄存器根本不在 HBI 直接地址空间,恒读 0)。 */
|
||||
static uint16_t initOnlyReadReg(uint16_t addr);
|
||||
|
||||
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用。 */
|
||||
static void initOnlyWriteReg(uint16_t addr, uint16_t v);
|
||||
|
||||
/** @brief ESC 核心 CSR 间接读(ECAT_CSR_CMD/DATA 端口,0x0000-0x0FFF 地址空间)。
|
||||
* @note 1.5.39:LAN9252 的 HBI/SPI 直接地址空间只有 LAN9252 系统 CSR 与访问端口
|
||||
* (DS 12.11/表 12-13),EtherCAT 核心寄存器(0x0000-0x0FFF)必须经
|
||||
* CSR_CMD(0x304)/CSR_DATA(0x300) 间接访问;过程 RAM(0x1000-0x1FFF)
|
||||
* 必须经 PRAM FIFO。size=1/2/4(size2 需偶地址,size4 需 4 字节对齐)。
|
||||
* 调用方负责 HBI 互斥(主循环 EscExmcBus::maskEnter / 已在 ISR 上下文)。 */
|
||||
static uint32_t csrRead(uint16_t addr, uint8_t size);
|
||||
|
||||
/** @brief ESC 核心 CSR 间接写(ECAT_CSR_CMD/DATA 端口)。约束同 csrRead。 */
|
||||
static void csrWrite(uint16_t addr, uint32_t data, uint8_t size);
|
||||
|
||||
/** @brief 过程 RAM(0x1000-0x1FFF)经 PRAM 读 FIFO 读取 len 字节到 buf。 */
|
||||
static void pramRead(uint16_t addr, uint8_t *buf, uint16_t len);
|
||||
|
||||
/** @brief 过程 RAM(0x1000-0x1FFF)经 PRAM 写 FIFO 写入 len 字节。 */
|
||||
static void pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len);
|
||||
|
||||
/** @brief EXMC 总线 + HBI_IDX_0 探针 + BYTE_TEST 全部通过(init 成功)后为 true。
|
||||
* @note 与 init() 返回值一致,可作运行期查询。 */
|
||||
static bool isBusReady() { return s_busReady; }
|
||||
|
||||
/** @brief EEPROM(SII) 加载完成、EtherCAT 核心使能(1.5.39)。
|
||||
* @note false 时核心寄存器对 PDI 读全 0、写静默丢弃,SSC MainInit 会全部
|
||||
* 落空(AL Event Mask/邮箱 SM 配置写不进),调用方必须跳过 SSC 初始化。 */
|
||||
static bool isCoreReady() { return s_coreReady; }
|
||||
|
||||
private:
|
||||
/** @brief 使能 LAN9252 的 IRQ 引脚输出(IRQ_CFG 0x054 / INT_EN 0x05C)。
|
||||
* @note 1.5.43 新增,由 init() 在 PDI/核心就绪后调用。两寄存器复位默认全 0,
|
||||
* 不配则 EtherCAT 事件永不到达 PB1、SSC 的 PDI_Isr 从不执行,
|
||||
* bEcatFirstOutputsReceived 恒 FALSE 使 SAFEOP->OP 被无限挂起。 */
|
||||
static void enableEcatIrqOutput();
|
||||
|
||||
static bool s_busReady; /**< init 完整成功(EXMC+IDX 探针+BYTE_TEST)后置位。 */
|
||||
static bool s_coreReady; /**< EEPROM 加载完成、EtherCAT 核心使能后置位(1.5.39)。 */
|
||||
static bool s_resetDone; /**< hardwareReset 已执行门控(防二次复位清掉 SSC 配置)。 */
|
||||
};
|
||||
|
||||
|
||||
@@ -63,16 +63,109 @@ void EthercatSlave::init()
|
||||
/* AL Status Code(寄存器 0x0130 高 8 位):非 0 表示从站进入错误状态
|
||||
* (如 SM Watchdog=0x1B、FMMU 配置错、邮箱协议错),需主站介入。 */
|
||||
|
||||
/* ============================================================
|
||||
* bring-up 诊断(1.5.35 引入,1.5.39 重新定性):经 CSR 间接端口读三个关键寄存器
|
||||
* + 一次写回环,确认 EtherCAT 核心侧状态。
|
||||
* 注意主站读身份/AL 走 ESC 以太网侧 + EEPROM(SII),完全不经过 GD32 的 PDI,
|
||||
* 所以"主站能读到身份"不能证明下面任何一项成立。
|
||||
* 1.5.39 起访问已改经 ECAT_CSR_CMD/CSR_DATA(EtherCAT 核心寄存器不在 HBI 直接
|
||||
* 地址空间,DS 12.11);此前直读索引使本段恒读全 0,曾被误判为"EEPROM 加载竞态"
|
||||
* 与"HBI 写路径损坏"两个错误结论。现在读到全 0 才真正指向 EEPROM/PDI 故障,
|
||||
* 交叉验证用 tools/ecat/ecat_esc_regs.py 从网络侧读同样的寄存器。
|
||||
* 本段在 EthercatSync::init() 使能 EXTI 之前执行,满足 initOnly* 的 INIT-ONLY 约束。
|
||||
* ============================================================ */
|
||||
uint16_t dlStatus = 0U;
|
||||
if (chipIdOk) {
|
||||
/* (1) DL Status 0x0110 bit0 = PDI operational。
|
||||
* ESC 只有认为 PDI 就绪时,才接受 PDI 侧(GD32 经 HBI)写入的 AL Status;
|
||||
* 该位为 0 时 SSC 的 SetALStatus 被静默丢弃 -> 主站恒读 INIT/code=0。 */
|
||||
dlStatus = EscLan9252::initOnlyReadReg(0x0110U);
|
||||
/* (2) ESC Configuration 0x0140:PDI 类型是否真被 EEPROM 加载为 HBI 16-bit(0x8D)。
|
||||
* 读回 0x00/0xFF 说明 EEPROM 的 PDI Control 未生效、ESC 的 PDI 没启用
|
||||
* (此时 HBI 读寄存器仍可能照常工作,极易误判为"硬件全通")。 */
|
||||
const uint16_t escCfg = EscLan9252::initOnlyReadReg(0x0140U);
|
||||
/* (3) EEPROM 接口状态 0x0502-0x0503(16 位字)。位定义按 ETG.1000.4 表 49
|
||||
* 与 SSC esc.h:bit11=Checksum Error、bit12=EEPROM loading status(0=OK)、
|
||||
* bit13=Missing EEPROM Acknowledge、bit14=Write Error、bit15=Busy。
|
||||
* 注意 1.5.40 及之前本行把 bit11 打成 "loaded",语义还正好反了
|
||||
* (bit11=1 才是校验错),于是正常板子恒显示 "loaded=0",字面像"EEPROM 未加载",
|
||||
* 正是历史上 1.5.37 那个已撤回的错误结论的诱因;现按位分别命名,0 全部代表正常。
|
||||
* 真正的"EEPROM 已加载/PDI 可用"判据是 DL_STATUS(0x0110) bit0,见上方 (1)。 */
|
||||
const uint16_t eepCsr = EscLan9252::initOnlyReadReg(0x0502U);
|
||||
LOG_ERR("[ECAT][DIAG] DL_STATUS(0x110)=0x%04X pdi_oper=%u | "
|
||||
"ESC_CFG(0x140)=0x%04X pdi_type=0x%02X(expect 0x8D) | "
|
||||
"EEP_CSR(0x502)=0x%04X crc_err=%u load_err=%u ack_err=%u wr_err=%u busy=%u"
|
||||
" (全 0=正常)\r\n",
|
||||
(unsigned)dlStatus, (unsigned)(dlStatus & 0x0001U),
|
||||
(unsigned)escCfg, (unsigned)(escCfg & 0x00FFU),
|
||||
(unsigned)eepCsr,
|
||||
(unsigned)((eepCsr >> 11U) & 0x1U),
|
||||
(unsigned)((eepCsr >> 12U) & 0x1U),
|
||||
(unsigned)((eepCsr >> 13U) & 0x1U),
|
||||
(unsigned)((eepCsr >> 14U) & 0x1U),
|
||||
(unsigned)((eepCsr >> 15U) & 0x1U));
|
||||
|
||||
/* (4) CSR 端口写回环:用 AL Event Mask(0x0204)做写-读-还原。该寄存器按设计
|
||||
* 就是 PDI 侧可写(PDI 用它选择哪些 AL Event 产生 IRQ),不像 0x0012 Station
|
||||
* Alias 在部分 ESC 上对 PDI 只读(会造成假 FAIL);本段在 MainInit/HW_Init
|
||||
* 之前执行,还原后不影响 SSC 后续建立的 mask 影子。
|
||||
* 1.5.39 起本回环经 ECAT_CSR_CMD/CSR_DATA 间接端口执行。历史注意:1.5.35
|
||||
* 该回环报 FAIL 曾被误判为"HBI 写路径损坏",真因是当时直读直写 HBI 索引——
|
||||
* EtherCAT 核心寄存器不在 HBI 直接地址空间(DS 12.11),读恒 0、写被丢弃。
|
||||
* 网络侧诊断(tools/ecat/ecat_esc_regs.py)已证实 ESC 本体与 EEPROM 一直正常。 */
|
||||
const uint16_t maskSaved = EscLan9252::initOnlyReadReg(0x0204U);
|
||||
EscLan9252::initOnlyWriteReg(0x0204U, 0x0A50U);
|
||||
const uint16_t maskRead = EscLan9252::initOnlyReadReg(0x0204U);
|
||||
EscLan9252::initOnlyWriteReg(0x0204U, maskSaved); /* 还原,交回 SSC 管理 */
|
||||
if (maskRead == 0x0A50U) {
|
||||
LOG_ERR("[ECAT][DIAG] CSR write loopback PASS (0x204 wrote 0x0A50 read back OK)\r\n");
|
||||
} else {
|
||||
LOG_ERR("[ECAT][DIAG] CSR write loopback FAIL: wrote 0x0A50 read 0x%04X"
|
||||
" -> 先查 CSR_CMD(0x304) busy 是否自清/EXMC 时序,再疑硬件\r\n",
|
||||
(unsigned)maskRead);
|
||||
}
|
||||
}
|
||||
|
||||
/* 是否有主站在线:DL_STATUS(0x0110) 的 Physical Link 位 bit4/5/6 对应 Port0/1/2
|
||||
* (DS 12.14.19)。三个端口都无 link = 网线未插/无主站,此时 AL 状态机根本没人驱动
|
||||
* (AL 迁移由主站写 AL Control 0x0120 触发,从站不自跳),停在 INIT 是唯一正确行为。
|
||||
* 参考:接主站时实测 DL_STATUS=0x5611(Port0 有 link 有 comm);
|
||||
* 空载时为 0x5503(三口 loop closed、link/comm 全 0)。 */
|
||||
const bool anyPhyLink = chipIdOk && ((dlStatus & 0x0070U) != 0U);
|
||||
|
||||
if (!chipIdOk) {
|
||||
LOG_ERR("[ECAT][SLAVE] ESC NOT ready, sync ISR disabled (check EXMC wiring/timing)\r\n");
|
||||
} else if (s_alState < AL_STATE_SAFEOP) {
|
||||
LOG_ERR("[ECAT][SLAVE] chip id PASS, but AL state=0x%02X(%s) < SAFEOP(0x%02X), code=0x%02X\r\n",
|
||||
(unsigned)s_alState, alStateName, (unsigned)AL_STATE_SAFEOP, (unsigned)alCode);
|
||||
LOG_ERR("[ECAT][SLAVE] SYNC0 will not fire, full ESC config requires SSC integration\r\n");
|
||||
/* 区分"等主站"与"真故障":两者 AL 都 < SAFEOP,但只有后者需要排查。
|
||||
* 1.5.40 及之前一律打 FAIL(partial),而冷启动无主站时必然走到这里,
|
||||
* 与紧随其后的 "SSC MainInit OK" 自相矛盾,极易被误读为初始化失败。 */
|
||||
if (!anyPhyLink) {
|
||||
LOG_ERR("[ECAT][SLAVE] WAIT_MASTER: no PHY link (DL_STATUS=0x%04X), "
|
||||
"AL=0x%02X(%s) 由主站驱动,空载停在 INIT 属预期\r\n",
|
||||
(unsigned)dlStatus, (unsigned)s_alState, alStateName);
|
||||
} else {
|
||||
LOG_ERR("[ECAT][SLAVE] chip id PASS, PHY link up (DL_STATUS=0x%04X) but "
|
||||
"AL state=0x%02X(%s) < SAFEOP(0x%02X), code=0x%02X\r\n",
|
||||
(unsigned)dlStatus, (unsigned)s_alState, alStateName,
|
||||
(unsigned)AL_STATE_SAFEOP, (unsigned)alCode);
|
||||
LOG_ERR("[ECAT][SLAVE] SYNC0 未触发:检查主站是否逐级 write_state"
|
||||
" (PREOP->config_map->SAFEOP->OP) 并已配置 DC\r\n");
|
||||
}
|
||||
} else {
|
||||
ECAT_LOGI("[ECAT][SLAVE] ESC ready, chip id=0x%08lX, AL state=0x%02X(%s), code=0x%02X\r\n",
|
||||
(unsigned long)s_chipId, (unsigned)s_alState, alStateName, (unsigned)alCode);
|
||||
}
|
||||
|
||||
LOG_ERR("[ECAT][SLAVE] init: %s\r\n", s_escReady ? "OK" : "FAIL(partial)");
|
||||
/* 终判措辞:PDI/EEPROM 就绪但只差主站时报 WAIT_MASTER 而非 FAIL——本函数的
|
||||
* s_escReady 要求 AL>=SAFEOP,无主站永远达不到,而 SSC 放行门控实际用的是
|
||||
* isBusReady()&&isCoreReady()(见 initial.cpp),两者结论相反不应都叫 FAIL。 */
|
||||
const char *verdict;
|
||||
if (s_escReady) {
|
||||
verdict = "OK";
|
||||
} else if (chipIdOk && EscLan9252::isCoreReady() && !anyPhyLink) {
|
||||
verdict = "WAIT_MASTER (PDI 就绪,等主站接入)";
|
||||
} else {
|
||||
verdict = "FAIL(partial)";
|
||||
}
|
||||
LOG_ERR("[ECAT][SLAVE] init: %s\r\n", verdict);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "dc_app.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h> /* offsetof:下方布局守卫断言使用 */
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* el9800appl.h 桩 include (SSC 内部需要)
|
||||
@@ -49,6 +50,13 @@ TOBJ6000;
|
||||
static TOBJ7000 s_outputObj = {4, {0, 0, 0, 0}};
|
||||
static TOBJ6000 s_inputObj = {4, {0, 0, 0, 0}};
|
||||
|
||||
/* 布局守卫(1.5.42):SSC 的 SDO 单条目访问按"si0 占 16bit,其后按 BitLength 紧排"
|
||||
* 计算偏移(OBJ_GetEntryOffset),故 UINT64 数组必须紧跟在 UINT16 之后落在字节 2。
|
||||
* OBJ_STRUCT_PACKED_START 曾被留空导致其落在字节 8、SDO 读写错位,加断言防回归。 */
|
||||
_Static_assert(offsetof(TOBJ7000, u64Data) == 2, "TOBJ7000 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
_Static_assert(offsetof(TOBJ6000, u64Data) == 2, "TOBJ6000 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
_Static_assert(sizeof(TOBJ7000) == 34, "TOBJ7000 尺寸应为 2+32");
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* SYNC 诊断对象 0x2000 (只读, 主站 SDO 轮询)
|
||||
* sub1=SYNC0 计数 sub2=SYNC1 计数 sub3=ESC IRQ 计数 sub4=uptime 秒
|
||||
@@ -66,6 +74,9 @@ TOBJ2000;
|
||||
|
||||
static TOBJ2000 s_syncDiag = {4, 0, 0, 0, 0};
|
||||
|
||||
/* 同上:UINT32 首条目必须落在字节 2(未紧排时为 4)。 */
|
||||
_Static_assert(offsetof(TOBJ2000, u32Sync0) == 2, "TOBJ2000 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
|
||||
/* EthercatSync 计数器 getter (C++ 侧 extern "C" 导出) */
|
||||
extern UINT32 EcatDbg_GetSync0(void);
|
||||
extern UINT32 EcatDbg_GetSync1(void);
|
||||
@@ -113,29 +124,57 @@ static TOBJ_PDO_MAPPING sTxPDOMap = {4, {
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Entry Descriptions
|
||||
*
|
||||
* 重要(1.5.42 修正): OBJ_GetEntryDesc()(objdef.c:554) 只对 OBJCODE_ARR 复用
|
||||
* pEntryDesc[1] 描述所有子索引; 对 OBJCODE_REC 是逐下标索引 pEntryDesc[Subindex]。
|
||||
* 因此 REC 对象的描述数组必须给满(Sub0..SubN 共 N+1 项), 否则 SDO Info 越界读到
|
||||
* 相邻常量数组的垃圾, 主站 config_map 报 "Unsupported access to an object" /
|
||||
* "Attempt to read to a write only object"(实测 0x1600/0x1A00 的 sub0/3/4 报错
|
||||
* 而 sub1/2 侥幸通过, 正是越界特征)。
|
||||
* 另: 可被 PDO 映射的数据子条目必须带 OBJACCESS_RxPDOMAPPING/TxPDOMAPPING,
|
||||
* 否则主站校验 0x1600/0x1A00 的映射目标时会拒绝(官方 sampleappl.h 同此)。
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/* PDO Assign (0x1C12/0x1C13): Sub0=UINT8 count, Sub1..n=UINT16 index */
|
||||
/* PDO Assign (0x1C12/0x1C13): ARR, Sub0=UINT8 count, Sub1..n=UINT16 index。
|
||||
* ARR 只需 2 项(Sub0 + 通用项), 由 OBJ_GetEntryDesc 复用 [1]。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOAssignEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}
|
||||
};
|
||||
|
||||
/* PDO Mapping (0x1600/0x1A00): Sub0=UINT8 count, Sub1..n=UINT32 mapping */
|
||||
/* PDO Mapping (0x1600/0x1A00): REC 含 4 个映射条目 -> 必须给 Sub0..Sub4 共 5 项。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOMappingEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, /* Sub0: 条目数 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* Sub1 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* Sub2 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* Sub3 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ} /* Sub4 */
|
||||
};
|
||||
|
||||
/* Data object 0x7000/0x6000: Sub0=UINT8 count, Sub1..4=UINT64 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asDataEntryDesc[] = {
|
||||
/* 0x7000 Output Data: REC, Sub0..Sub4 共 5 项; 子条目可被 RxPDO 映射。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asOutputEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE}
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING}
|
||||
};
|
||||
|
||||
/* SYNC 诊断 0x2000: Sub0=UINT8 count, Sub1..4=UINT32 (只读) */
|
||||
/* 0x6000 Input Data: REC, Sub0..Sub4 共 5 项; 子条目可被 TxPDO 映射, 输入侧只读。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asInputEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING}
|
||||
};
|
||||
|
||||
/* SYNC 诊断 0x2000: REC, Sub0..Sub4 共 5 项, 纯只读不参与 PDO 映射。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asSyncDiagEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}
|
||||
};
|
||||
|
||||
@@ -156,29 +195,29 @@ OBJCONST UCHAR OBJMEM aName0x2000[] = "SYNC Diag";
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
TOBJECT OBJMEM ApplicationObjDicList[] = {
|
||||
/* 0x1C12: RxPDO Assign */
|
||||
/* 0x1C12: RxPDO Assign (ARR of UINT16; DEFTYPE 为元素类型) */
|
||||
{NULL, NULL, 0x1C12, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)},
|
||||
asPDOAssignEntryDesc, aName0x1C12, &sRxPDOassign, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x1600: RxPDO Mapping */
|
||||
/* 0x1600: RxPDO Mapping (REC, 4 条目 -> 描述数组必须 5 项) */
|
||||
{NULL, NULL, 0x1600, {DEFTYPE_PDOMAPPING, 4 | (OBJCODE_REC << 8)},
|
||||
asPDOMappingEntryDesc, aName0x1600, &sRxPDOMap, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x7000: Output Data (主站→从站) */
|
||||
{NULL, NULL, 0x7000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
asDataEntryDesc, aName0x7000, &s_outputObj, NULL, NULL, 0x0000},
|
||||
asOutputEntryDesc, aName0x7000, &s_outputObj, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x1C13: TxPDO Assign */
|
||||
{NULL, NULL, 0x1C13, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)},
|
||||
asPDOAssignEntryDesc, aName0x1C13, &sTxPDOassign, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x1A00: TxPDO Mapping */
|
||||
/* 0x1A00: TxPDO Mapping (REC, 4 条目 -> 描述数组必须 5 项) */
|
||||
{NULL, NULL, 0x1A00, {DEFTYPE_PDOMAPPING, 4 | (OBJCODE_REC << 8)},
|
||||
asPDOMappingEntryDesc, aName0x1A00, &sTxPDOMap, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x6000: Input Data (从站→主站) */
|
||||
{NULL, NULL, 0x6000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
asDataEntryDesc, aName0x6000, &s_inputObj, NULL, NULL, 0x0000},
|
||||
asInputEntryDesc, aName0x6000, &s_inputObj, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x2000: SYNC 诊断 (只读, 主站 SDO 轮询验证 DC 通路) */
|
||||
{NULL, NULL, 0x2000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
|
||||
@@ -517,12 +517,21 @@
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Struct Packing (ARM AC6: __attribute__((packed)) 不需要前后宏,留空即可)
|
||||
------ Struct Packing
|
||||
------
|
||||
------ 1.5.42 修正:此前三对宏全部留空,注释误以为"AC6 不需要"。实际这些宏就是 SSC
|
||||
------ 用来紧排结构体的唯一手段——SSC 的 SDO 读写按"si0 占 16bit,其后各条目按
|
||||
------ BitLength 紧排"计算字节偏移(OBJ_GetEntryOffset, objdef.c:609),留空则结构体
|
||||
------ 按编译器自然对齐,与该假设不符。armclang 实测:{UINT16; UINT64[4]} 的 UINT64
|
||||
------ 落在字节 8 而非 2(编译期断言验证),{UINT16; UINT32 x4} 落在字节 4 而非 2,
|
||||
------ 于是 0x7000/0x6000/0x2000 的 SDO 单条目访问全部读写错位内存;SSC 内部
|
||||
------ 0x1C3x(TSYNCMANPAR)/0x10F1 等同样受影响。
|
||||
------ AC6/armclang 支持 GCC 风格 __attribute__((packed)),置于 struct 关键字之后
|
||||
------ (即 START 位置)对整个结构体生效。
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef STRUCT_PACKED_START
|
||||
#define STRUCT_PACKED_START
|
||||
#define STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef STRUCT_PACKED_END
|
||||
@@ -530,7 +539,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef MBX_STRUCT_PACKED_START
|
||||
#define MBX_STRUCT_PACKED_START
|
||||
#define MBX_STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef MBX_STRUCT_PACKED_END
|
||||
@@ -538,7 +547,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef OBJ_STRUCT_PACKED_START
|
||||
#define OBJ_STRUCT_PACKED_START
|
||||
#define OBJ_STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef OBJ_STRUCT_PACKED_END
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
* @file hw_gd32exmc.cpp
|
||||
* @brief SSC V5.11 硬件 port 层 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode)
|
||||
*
|
||||
* 实现 el9800hw.h 中声明的全部 HW_* 函数,将 SSC 的 ESC 访问请求
|
||||
* 转发到 EscExmcBus (EXMC NOR/SRAM Region0, 16-bit 内存映射)。
|
||||
* 实现 el9800hw.h 中声明的全部 HW_* 函数,将 SSC 的 ESC 访问请求
|
||||
* 转发到 EscLan9252 的间接访问端口(1.5.39 按 DS 12.11 修正访问模型):
|
||||
* - EtherCAT 核心 CSR(0x0000-0x0FFF:AL Status/Event、SyncManager、LED 等)
|
||||
* → ECAT_CSR_CMD(0x304)/ECAT_CSR_DATA(0x300) 间接端口;
|
||||
* - 过程 RAM(0x1000-0x1FFF:邮箱 0x1000+、PDO 0x1400+)
|
||||
* → PRAM FIFO(0x000-0x03C 数据口 + 0x308-0x314 地址/命令)。
|
||||
* 1.5.37 及之前直读/直写 HBI 索引(误以为索引空间统一可寻址),核心 CSR 全部
|
||||
* 读 0/写丢弃——SSC 对主站 AL Control 完全失聪,主站恒读 AL=INIT。
|
||||
*
|
||||
* HBI Indexed 寻址协议(A2=LAN9252 Pin29←PD12=EXMC_A17, A1=Pin25←PD11=EXMC_A16;
|
||||
* EXMC 16-bit 模式地址左移 1 位,见 esc_lan9252.cpp 头注释):
|
||||
* 1. 写 HBI_IDX_0 (EXMC offset 0x00000) = ESC 寄存器字节地址
|
||||
* 2. 读/写 HBI_DATA_0 低 word (EXMC offset 0x40000, A2=1/A1=0)
|
||||
* 3. 读/写 HBI_DATA_0 高 word (EXMC offset 0x60000, A2=1/A1=1)
|
||||
*
|
||||
* 并发保护:
|
||||
* - HW_EscRead/HW_EscWrite 等主循环函数:内部经 EscExmcBus::maskEnter/Exit
|
||||
* 屏蔽 EXTI0+EXTI1(HBI Indexed 是两步事务,不可被 ISR 打断)。
|
||||
* - HW_EscReadIsr/HW_EscWriteIsr (ISR):在 preempt=0 的 SYNC0 ISR 内调用,
|
||||
* 天然不会被抢占;PDI_Isr/Sync1_Isr(preempt=1) 由 ethercat_sync.cpp 包装器
|
||||
* 并发保护(不变):
|
||||
* - HW_EscRead/HW_EscWrite 等主循环函数:内部经 EscExmcBus::maskEnter/Exit
|
||||
* 屏蔽 EXTI0+EXTI1(访问端口是多步事务,不可被 ISR 打断)。
|
||||
* - HW_EscReadIsr/HW_EscWriteIsr (ISR):在 preempt=0 的 SYNC0 ISR 内调用,
|
||||
* 天然不会被抢占;PDI_Isr/Sync1_Isr(preempt=1) 由 ethercat_sync.cpp 包装器
|
||||
* 屏蔽 EXTI0 后调用。
|
||||
*/
|
||||
|
||||
@@ -28,126 +28,76 @@
|
||||
|
||||
/* 项目硬件驱动 */
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* HBI Indexed Mode EXMC 偏移 (与 esc_lan9252.cpp 一致)
|
||||
*---------------------------------------------------------------------------*/
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1(EXMC_A17), A1=0(EXMC_A16) */
|
||||
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1(EXMC_A17), A1=1(EXMC_A16) */
|
||||
|
||||
/* AL Event Mask 寄存器影子 (ESC 0x0204) */
|
||||
volatile uint16_t s_alEventMask = 0xFFFFU;
|
||||
|
||||
/* SSC 看门狗软件定时器 (DWT CYCCNT 基准) */
|
||||
volatile uint32_t s_timerBase = 0U;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* 内部 HBI 访问原语 (无锁,调用者负责并发保护)
|
||||
*---------------------------------------------------------------------------*/
|
||||
constexpr uint16_t ESC_PRAM_BASE = 0x1000U; /**< 过程 RAM 起点(DS 12.11):≥此地址走 PRAM FIFO */
|
||||
|
||||
inline void hbiSetIndex(uint16_t addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, addr);
|
||||
}
|
||||
|
||||
inline uint16_t hbiReadLow(void)
|
||||
{
|
||||
return EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
}
|
||||
|
||||
inline uint16_t hbiReadHigh(void)
|
||||
{
|
||||
return EscExmcBus::read16(HBI_DATA0_HIGH_OFF);
|
||||
}
|
||||
|
||||
inline void hbiWriteLow(uint16_t v)
|
||||
{
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, v);
|
||||
}
|
||||
|
||||
inline void hbiWriteHigh(uint16_t v)
|
||||
{
|
||||
EscExmcBus::write16(HBI_DATA0_HIGH_OFF, v);
|
||||
}
|
||||
|
||||
/** 从 ESC 地址读取 Len 字节到 pData (按 16-bit word 对齐处理) */
|
||||
void hbiRead(void *pData, uint16_t address, uint16_t len)
|
||||
/** 从 ESC 地址读取 Len 字节到 pData(按地址自动路由 CSR 端口 / PRAM FIFO) */
|
||||
void escRead(MEM_ADDR *pData, uint16_t address, uint16_t len)
|
||||
{
|
||||
uint8_t *dst = (uint8_t *)pData;
|
||||
if (address >= ESC_PRAM_BASE) {
|
||||
EscLan9252::pramRead(address, dst, len);
|
||||
return;
|
||||
}
|
||||
/* CSR 端口:size 1 任意地址,size 2 需偶地址(DS 表 12-14) */
|
||||
uint16_t addr = address;
|
||||
uint16_t remaining = len;
|
||||
|
||||
/* 奇数起始地址:读低 byte */
|
||||
if ((addr & 1U) != 0U && remaining > 0U) {
|
||||
hbiSetIndex(addr & 0xFFFEU);
|
||||
uint16_t w = hbiReadLow();
|
||||
*dst++ = (uint8_t)((w >> 8) & 0xFFU);
|
||||
*dst++ = (uint8_t)EscLan9252::csrRead(addr, 1U);
|
||||
addr++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
/* 对齐的 16-bit word 读取 */
|
||||
while (remaining >= 2U) {
|
||||
hbiSetIndex(addr);
|
||||
uint16_t w = hbiReadLow();
|
||||
const uint16_t w = (uint16_t)EscLan9252::csrRead(addr, 2U);
|
||||
dst[0] = (uint8_t)(w & 0xFFU);
|
||||
dst[1] = (uint8_t)((w >> 8) & 0xFFU);
|
||||
dst[1] = (uint8_t)(w >> 8);
|
||||
dst += 2;
|
||||
addr += 2;
|
||||
remaining -= 2;
|
||||
}
|
||||
|
||||
/* 尾部奇数字节 */
|
||||
if (remaining > 0U) {
|
||||
hbiSetIndex(addr);
|
||||
uint16_t w = hbiReadLow();
|
||||
*dst = (uint8_t)(w & 0xFFU);
|
||||
*dst = (uint8_t)EscLan9252::csrRead(addr, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
/** 向 ESC 地址写入 Len 字节 (按 16-bit word 对齐处理) */
|
||||
void hbiWrite(const void *pData, uint16_t address, uint16_t len)
|
||||
/** 向 ESC 地址写入 Len 字节(按地址自动路由 CSR 端口 / PRAM FIFO) */
|
||||
void escWrite(const MEM_ADDR *pData, uint16_t address, uint16_t len)
|
||||
{
|
||||
const uint8_t *src = (const uint8_t *)pData;
|
||||
if (address >= ESC_PRAM_BASE) {
|
||||
EscLan9252::pramWrite(address, src, len);
|
||||
return;
|
||||
}
|
||||
uint16_t addr = address;
|
||||
uint16_t remaining = len;
|
||||
|
||||
/* 奇数起始地址:RMW 高 byte */
|
||||
if ((addr & 1U) != 0U && remaining > 0U) {
|
||||
uint16_t aligned = addr & 0xFFFEU;
|
||||
hbiSetIndex(aligned);
|
||||
uint16_t w = hbiReadLow();
|
||||
w = (w & 0x00FFU) | ((uint16_t)(*src++) << 8);
|
||||
hbiSetIndex(aligned);
|
||||
hbiWriteLow(w);
|
||||
EscLan9252::csrWrite(addr, *src, 1U);
|
||||
src++;
|
||||
addr++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
/* 对齐的 16-bit word 写入 */
|
||||
while (remaining >= 2U) {
|
||||
uint16_t w = (uint16_t)src[0] | ((uint16_t)src[1] << 8);
|
||||
hbiSetIndex(addr);
|
||||
hbiWriteLow(w);
|
||||
const uint16_t w = (uint16_t)src[0] | ((uint16_t)src[1] << 8);
|
||||
EscLan9252::csrWrite(addr, w, 2U);
|
||||
src += 2;
|
||||
addr += 2;
|
||||
remaining -= 2;
|
||||
}
|
||||
|
||||
/* 尾部奇数字节:RMW 低 byte */
|
||||
if (remaining > 0U) {
|
||||
hbiSetIndex(addr);
|
||||
uint16_t w = hbiReadLow();
|
||||
w = (w & 0xFF00U) | (uint16_t)(*src);
|
||||
hbiSetIndex(addr);
|
||||
hbiWriteLow(w);
|
||||
EscLan9252::csrWrite(addr, *src, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +123,7 @@ UINT8 HW_Init(void)
|
||||
s_timerBase = DWT->CYCCNT;
|
||||
|
||||
/* 读取当前 AL Event Mask */
|
||||
hbiSetIndex(ESC_AL_EVENTMASK_OFFSET);
|
||||
s_alEventMask = hbiReadLow();
|
||||
s_alEventMask = (uint16_t)EscLan9252::csrRead(ESC_AL_EVENTMASK_OFFSET, 2U);
|
||||
|
||||
return 0; /* 0 = success */
|
||||
}
|
||||
@@ -186,13 +135,12 @@ void HW_Release(void)
|
||||
|
||||
/**
|
||||
* @brief 读取 AL Event 寄存器 (0x0220),主循环上下文。
|
||||
* @note 内部屏蔽 EXTI0+EXTI1 保证 HBI 事务完整。
|
||||
* @note 内部屏蔽 EXTI0+EXTI1 保证访问端口事务完整。
|
||||
*/
|
||||
UINT16 HW_GetALEventRegister(void)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiSetIndex(ESC_AL_EVENT_OFFSET);
|
||||
const UINT16 v = hbiReadLow();
|
||||
const UINT16 v = (UINT16)EscLan9252::csrRead(ESC_AL_EVENT_OFFSET, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
return v;
|
||||
}
|
||||
@@ -202,8 +150,7 @@ UINT16 HW_GetALEventRegister(void)
|
||||
*/
|
||||
UINT16 HW_GetALEventRegister_Isr(void)
|
||||
{
|
||||
hbiSetIndex(ESC_AL_EVENT_OFFSET);
|
||||
return hbiReadLow();
|
||||
return (UINT16)EscLan9252::csrRead(ESC_AL_EVENT_OFFSET, 2U);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,8 +160,7 @@ void HW_ResetALEventMask(UINT16 intMask)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
s_alEventMask &= ~intMask;
|
||||
hbiSetIndex(ESC_AL_EVENTMASK_OFFSET);
|
||||
hbiWriteLow(s_alEventMask);
|
||||
EscLan9252::csrWrite(ESC_AL_EVENTMASK_OFFSET, s_alEventMask, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -225,8 +171,7 @@ void HW_SetALEventMask(UINT16 intMask)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
s_alEventMask |= intMask;
|
||||
hbiSetIndex(ESC_AL_EVENTMASK_OFFSET);
|
||||
hbiWriteLow(s_alEventMask);
|
||||
EscLan9252::csrWrite(ESC_AL_EVENTMASK_OFFSET, s_alEventMask, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -239,12 +184,9 @@ void HW_SetLed(UINT8 RunLed, UINT8 ErrLed)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
/* Run LED override (0x0138): bit0 = LED on */
|
||||
hbiSetIndex(ESC_RUN_LED_OVERRIDE);
|
||||
hbiWriteLow(RunLed ? 0x0001U : 0x0000U);
|
||||
|
||||
EscLan9252::csrWrite(ESC_RUN_LED_OVERRIDE, RunLed ? 0x0001U : 0x0000U, 2U);
|
||||
/* Error LED override (0x0139): bit0 = LED on */
|
||||
hbiSetIndex(ESC_ERROR_LED_OVERRIDE);
|
||||
hbiWriteLow(ErrLed ? 0x0001U : 0x0000U);
|
||||
EscLan9252::csrWrite(ESC_ERROR_LED_OVERRIDE, ErrLed ? 0x0001U : 0x0000U, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -255,14 +197,14 @@ void HW_SetLed(UINT8 RunLed, UINT8 ErrLed)
|
||||
void HW_EscRead(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiRead(pData, Address, Len);
|
||||
escRead(pData, Address, Len);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiWrite(pData, Address, Len);
|
||||
escWrite(pData, Address, Len);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -272,12 +214,12 @@ void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
|
||||
void HW_EscReadIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
hbiRead(pData, Address, Len);
|
||||
escRead(pData, Address, Len);
|
||||
}
|
||||
|
||||
void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
hbiWrite(pData, Address, Len);
|
||||
escWrite(pData, Address, Len);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
@@ -285,59 +227,49 @@ void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 禁用指定 SM 通道 (清 SM Activate 寄存器 bit0)。
|
||||
* @brief 禁用指定 SM 通道 (清 SM Activate 寄存器 0x0806+8n bit0)。
|
||||
* @param channel SM 通道号 (0-15)
|
||||
* @note 1.5.39:偏移由 +4 改为 +6——+4 是 SM Control(操作模式字段),
|
||||
* Activate(bit0=SM Enable)在 +6;旧值从未真正生效过(访问层全空)。
|
||||
*/
|
||||
void HW_DisableSyncManChannel(UINT8 channel)
|
||||
{
|
||||
/* SM 寄存器基址 0x0800,每个 SM 占 8 字节,Activate 在 offset +4 (bit0) */
|
||||
uint16_t smBase = 0x0800U + (uint16_t)channel * 8U;
|
||||
uint16_t activateAddr = smBase + 4U;
|
||||
|
||||
const uint16_t activateAddr = (uint16_t)(ESC_SYNCMAN_REG_OFFSET
|
||||
+ (uint16_t)channel * SIZEOF_SM_REGISTER + 6U);
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiSetIndex(activateAddr);
|
||||
uint16_t val = hbiReadLow();
|
||||
val &= ~0x0001U; /* 清 bit0 = deactivate */
|
||||
hbiSetIndex(activateAddr);
|
||||
hbiWriteLow(val);
|
||||
uint8_t val = (uint8_t)EscLan9252::csrRead(activateAddr, 1U);
|
||||
val &= (uint8_t)~0x01U; /* 清 bit0 = deactivate */
|
||||
EscLan9252::csrWrite(activateAddr, val, 1U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使能指定 SM 通道 (置 SM Activate 寄存器 bit0)。
|
||||
* @brief 使能指定 SM 通道 (置 SM Activate 寄存器 0x0806+8n bit0)。
|
||||
*/
|
||||
void HW_EnableSyncManChannel(UINT8 channel)
|
||||
{
|
||||
uint16_t smBase = 0x0800U + (uint16_t)channel * 8U;
|
||||
uint16_t activateAddr = smBase + 4U;
|
||||
|
||||
const uint16_t activateAddr = (uint16_t)(ESC_SYNCMAN_REG_OFFSET
|
||||
+ (uint16_t)channel * SIZEOF_SM_REGISTER + 6U);
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiSetIndex(activateAddr);
|
||||
uint16_t val = hbiReadLow();
|
||||
val |= 0x0001U;
|
||||
hbiSetIndex(activateAddr);
|
||||
hbiWriteLow(val);
|
||||
uint8_t val = (uint8_t)EscLan9252::csrRead(activateAddr, 1U);
|
||||
val |= 0x01U;
|
||||
EscLan9252::csrWrite(activateAddr, val, 1U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定 SM 通道的寄存器块指针。
|
||||
* @note SSC 用此函数读取 SM 配置 (StartAddr, Length, Control, Status, Activate)。
|
||||
* 由于 HBI 是间接访问,无法返回真实指针。SSC 在 EL9800_HW 模式下
|
||||
* 实际通过 HW_EscRead 读取 SM 寄存器,此函数返回 NULL 并由调用者
|
||||
* 使用 HW_EscRead 替代。
|
||||
*
|
||||
* 检查 ecatslv.c 中 HW_GetSyncMan 的使用方式:
|
||||
* 如果 SSC 直接解引用返回的指针,则需要提供一个本地缓冲。
|
||||
* 这里提供一个静态缓冲,每次调用时从 ESC 读入。
|
||||
* 访问是间接端口,无法返回真实指针;提供静态缓冲,每次调用时从 ESC 读入。
|
||||
*/
|
||||
static TSYNCMAN s_smBuf;
|
||||
|
||||
TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel)
|
||||
{
|
||||
uint16_t smBase = 0x0800U + (uint16_t)channel * 8U;
|
||||
const uint16_t smBase = (uint16_t)(ESC_SYNCMAN_REG_OFFSET
|
||||
+ (uint16_t)channel * SIZEOF_SM_REGISTER);
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiRead(&s_smBuf, smBase, SIZEOF_SM_REGISTER);
|
||||
escRead((MEM_ADDR *)&s_smBuf, smBase, SIZEOF_SM_REGISTER);
|
||||
EscExmcBus::maskExit(prev);
|
||||
return &s_smBuf;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user