- 添加新的SPI命令文件:app_spi_cmd_can_burst.cpp 和 app_spi_cmd_exmc_diag.cpp - 移除旧的SPI命令文件:app_spi_cmd_ota_wait.cpp, app_spi_cmd_ping.cpp, app_spi_cmd_can_bridge.cpp, app_spi_cmd_can_feedback.cpp, app_spi_cmd_id_snapshot.cpp, app_spi_cmd_adc_feedback.cpp, app_spi_cmd_motor_state.cpp, app_spi_cmd_motor_version.cpp, app_spi_cmd_motor_ota.cpp - 新增SSC EtherCAT从站协议栈相关文件组,包含头文件和源文件 - 添加ESC EEPROM操作相关的头文件和源文件 - 修正HBI寄存器EXMC偏移地址映射错误 - 实现8位、16位、32位ESC寄存器读写函数 - 改进EEPROM初始化逻辑,添加重试机制和诊断功能
247 lines
7.3 KiB
C++
247 lines
7.3 KiB
C++
/**
|
|
* @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 <stdio.h>
|
|
#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);
|
|
}
|