/** * @file esc_lan9252.cpp * @brief LAN9252 EtherCAT ESC 驱动实现。 * * 硬件使用 16-bit HBI Indexed Address Mode: * - A2=0,A1=0 (偏移 0x00000) -> HBI_IDX_0 低 16 位 * - A2=1,A1=0 (偏移 0x20000) -> HBI_DATA_0 低 16 位 * - A2=1,A1=1 (偏移 0x30000) -> HBI_DATA_0 高 16 位 * * 所有 ESC 寄存器/PDI 内存访问均通过"写 IDX -> 读写 DATA"完成。 */ #include "comm/esc_lan9252.h" #include "comm/esc_exmc_bus.h" #include "sys/config.h" #include #include "RTE_Components.h" #include CMSIS_device_header namespace { /* HBI Indexed Mode 下 EXMC 偏移(A2=EXMC_A17=PD12, A1=EXMC_A16=PD11) * 注意: A16=bit16=0x10000, A17=bit17=0x20000 (不是 0x40000!) */ constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */ constexpr uint32_t HBI_DATA0_LOW_OFF = 0x20000U; /* A2=1(A17), A1=0(A16) */ constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x30000U; /* A2=1(A17), A1=1(A16) */ /* ESC 关键寄存器字节地址。 * 注: LAN9252 的 HBI 在 EEPROM 空白/无效时依然工作(数据手册 9.5.4.2: * 先轮询 HBI_IDX_0 默认值 0x1234,再轮询 BYTE_TEST(0x064)=0x87654321)。 * 芯片身份探针用 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 默认值(小端序)。 */ /* PDI 内存基址偏移。 * CONFIG_ESC_PDI_INPUT_OFFSET / CONFIG_ESC_PDI_OUTPUT_OFFSET 已包含 SyncManager * 在 ESC 内存空间中的实际地址,因此这里基址为 0。 */ constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x0000U; __attribute__((section(".ramfunc"), noinline)) void setHbiIndex(uint16_t addr) { EscExmcBus::write16(HBI_IDX0_OFFSET, addr); } __attribute__((section(".ramfunc"), noinline)) uint16_t readDataLow() { return EscExmcBus::read16(HBI_DATA0_LOW_OFF); } __attribute__((section(".ramfunc"), noinline)) uint16_t readDataHigh() { return EscExmcBus::read16(HBI_DATA0_HIGH_OFF); } __attribute__((section(".ramfunc"), noinline)) void writeDataLow(uint16_t v) { EscExmcBus::write16(HBI_DATA0_LOW_OFF, v); } __attribute__((section(".ramfunc"), noinline)) void writeDataHigh(uint16_t v) { EscExmcBus::write16(HBI_DATA0_HIGH_OFF, v); } __attribute__((section(".ramfunc"), noinline)) uint16_t readReg16(uint16_t addr) { setHbiIndex(addr); return readDataLow(); } __attribute__((section(".ramfunc"), noinline)) uint32_t readReg32(uint16_t addr) { setHbiIndex(addr); uint16_t lo = readDataLow(); uint16_t hi = readDataHigh(); return ((uint32_t)hi << 16) | lo; } __attribute__((section(".ramfunc"), noinline)) void writeReg16(uint16_t addr, uint16_t v) { setHbiIndex(addr); writeDataLow(v); } __attribute__((section(".ramfunc"), noinline)) void writeReg32(uint16_t addr, uint32_t v) { setHbiIndex(addr); writeDataLow((uint16_t)(v & 0xFFFFU)); writeDataHigh((uint16_t)((v >> 16) & 0xFFFFU)); } } /* anonymous namespace */ bool EscLan9252::s_busReady = false; bool EscLan9252::init() { /* 使能 DWT 周期计数器,供 periodicIsr 执行时间测量 */ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0U; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; s_busReady = EscExmcBus::init(); if (!s_busReady) { printf("[ECAT][ESC] init: FAIL, EXMC bus not ready (see [ECAT][EXMC] log)\r\n"); return false; } /* HBI 存活探针(数据手册 9.5.4.2):先直接读 HBI_IDX_0(offset 0,不写任何东西), * 其复位默认值 = 0x1234。若读回 != 0x1234(通常是 0x0000/0xFFFF 或上次写入的回显), * 说明 LAN9252 完全没有驱动总线——CS 未拉低、CHIP_MODE strap 非 00(三端口=SPI only) * 或芯片未复位/未上电,而不是 EEPROM 问题(HBI 在 EEPROM 空白时也工作)。 */ const uint16_t idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET); if (idx0 != 0x1234U) { printf("[ECAT][ESC] init: FAIL, HBI_IDX_0=0x%04X (expect 0x1234) -> LAN not driving bus\r\n", (unsigned)idx0); printf("[ECAT][ESC] hint: check CS(PD7) pulse, CHIP_MODE0/1 straps=00, RST#/power\r\n"); return false; } /* 再读 BYTE_TEST(0x064)确认接口完全就绪,默认 0x87654321(小端)。 */ const uint32_t byteTest = readReg32(LAN9252_REG_BYTE_TEST); if (byteTest != LAN9252_BYTE_TEST_VALUE) { printf("[ECAT][ESC] init: FAIL, BYTE_TEST=0x%08lX (expect 0x87654321)\r\n", (unsigned long)byteTest); return false; } printf("[ECAT][ESC] init: OK, HBI alive (IDX=0x1234, BYTE_TEST=0x87654321)\r\n"); return true; } uint32_t EscLan9252::readChipId() { /* INIT-ONLY:init 阶段中断未使能,直接读取 BYTE_TEST(32-bit) */ return readReg32(LAN9252_REG_BYTE_TEST); } uint16_t EscLan9252::readReg(uint16_t addr) { return readReg16(addr); } void EscLan9252::writeReg(uint16_t addr, uint16_t v) { writeReg16(addr, v); } __attribute__((section(".ramfunc"), noinline)) void EscLan9252::readPdi(uint16_t offset, uint8_t *buf, size_t len) { if (buf == nullptr || len == 0U) { return; } uint16_t addr = LAN9252_PDI_BASE_OFFSET + offset; size_t i = 0U; /* 按 32-bit 字读取 */ while ((i + 4U) <= len) { const uint32_t w = readReg32(addr); buf[i + 0U] = (uint8_t)(w & 0xFFU); buf[i + 1U] = (uint8_t)((w >> 8) & 0xFFU); buf[i + 2U] = (uint8_t)((w >> 16) & 0xFFU); buf[i + 3U] = (uint8_t)((w >> 24) & 0xFFU); addr += 4U; i += 4U; } /* 剩余 2 字节 */ if ((i + 2U) <= len) { const uint16_t w = readReg16(addr); buf[i + 0U] = (uint8_t)(w & 0xFFU); buf[i + 1U] = (uint8_t)((w >> 8) & 0xFFU); addr += 2U; i += 2U; } /* 剩余 1 字节 */ if (i < len) { const uint16_t w = readReg16(addr); buf[i] = (uint8_t)(w & 0xFFU); } } __attribute__((section(".ramfunc"), noinline)) void EscLan9252::writePdi(uint16_t offset, const uint8_t *buf, size_t len) { if (buf == nullptr || len == 0U) { return; } uint16_t addr = LAN9252_PDI_BASE_OFFSET + offset; size_t i = 0U; /* 按 32-bit 字写入 */ while ((i + 4U) <= len) { const uint32_t w = ((uint32_t)buf[i + 3U] << 24) | ((uint32_t)buf[i + 2U] << 16) | ((uint32_t)buf[i + 1U] << 8) | (uint32_t)buf[i + 0U]; writeReg32(addr, w); addr += 4U; i += 4U; } /* 剩余 2 字节 */ if ((i + 2U) <= len) { const uint16_t w = ((uint16_t)buf[i + 1U] << 8) | (uint16_t)buf[i + 0U]; writeReg16(addr, w); addr += 2U; i += 2U; } /* 剩余 1 字节 */ if (i < len) { const uint16_t w = (uint16_t)buf[i]; writeReg16(addr, w); } } uint16_t EscLan9252::initOnlyReadReg(uint16_t addr) { return readReg16(addr); } void EscLan9252::initOnlyWriteReg(uint16_t addr, uint16_t v) { writeReg16(addr, v); } void EscLan9252::initOnlyReadPdi(uint16_t offset, uint8_t *buf, size_t len) { readPdi(offset, buf, len); } void EscLan9252::initOnlyWritePdi(uint16_t offset, const uint8_t *buf, size_t len) { writePdi(offset, buf, len); }