feat(project): 更新项目配置并增加ESC EEPROM功能

- 添加新的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初始化逻辑,添加重试机制和诊断功能
This commit is contained in:
cbb
2026-08-09 03:27:21 +08:00
parent 8e5da18100
commit a2d0b28b3d
49 changed files with 13258 additions and 554 deletions
+182 -63
View File
@@ -1,6 +1,13 @@
/**
* @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"
@@ -10,15 +17,86 @@
#include CMSIS_device_header
namespace {
/* LAN9252 ESC 关键寄存器地址(字节偏移,EXMC 访问) */
constexpr uint16_t LAN9252_REG_CHIP_ID_L = 0x0000U; /**< 芯片 ID 低 16 位(=0x4321)。 */
constexpr uint16_t LAN9252_REG_CHIP_ID_H = 0x0002U; /**< 芯片 ID 高 16 位(=0x8765)。 */
constexpr uint32_t LAN9252_CHIP_ID_VALUE = 0x87654321U; /**< 期望的 32-bit 芯片 ID。 */
/* 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) */
/* PDI 内存基址偏移(实际取决于 LAN9252 PDI 配置,这里为示意值) */
constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x1000U;
/* 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()
@@ -33,52 +111,44 @@ bool EscLan9252::init()
printf("[ECAT][ESC] init: FAIL, EXMC bus not ready (see [ECAT][EXMC] log)\r\n");
return false;
}
printf("[ECAT][ESC] init: EXMC bus ready\r\n");
/* ============================================================
* TODO(spec): 完整 ESC 寄存器配置(需 SSC 或等价协议栈代码,本 spec 不实现)
* ============================================================
* 当前仅完成 EXMC 总线初始化与芯片 ID 读取。要使 SYNC0 实际触发,还需配置:
*
* 1. SyncManager(SM)初始化:
* - SM0/SM1:邮箱输入/输出(IN/OUT),地址 0x0520-0x0530
* - SM2:TxPDO(输入),地址 0x0530,配置为 3-buffer 模式
* - SM3:RxPDO(输出),地址 0x0540,配置为 3-buffer 模式
* - 各 SM 的 StartAddr/Length/Control/Status/Activate 寄存器
*
* 2. FMMU(Fieldbus Memory Mapping Unit)配置:
* - FMMU0:TxPDO 映射(逻辑地址 → 物理地址,读)
* - FMMU1:RxPDO 映射(逻辑地址 → 物理地址,写)
* - 地址 0x0580-0x0590
*
* 3. AL Control(0x0120):写入 0x08 请求 OP 状态(需主站配合完成状态迁移)
*
* 4. SYNC0 生成寄存器:
* - 0x0981-0x0982:SYNC0 周期(cycle time,单位 ns)
* - 0x0983:SYNC0 脉冲长度
* - 0x0984:SYNC0 控制(使能/触发模式)
* - 需主站 DC 同步后由 ESC 自动生成 SYNC0 脉冲
*
* 这些步骤通常由 EtherCAT SSC(Slave Stack Code)或等价协议栈代码完成,
* 涉及 AL 状态机管理、邮箱通信、PDO 映射协商等复杂逻辑。
* 本 spec 仅通过 EthercatSlave::isEscReady() 的 AL 状态守卫使
* "SYNC0 不会触发"在运行期可观测,完整配置留待 SSC 集成 spec。
* ============================================================ */
/* 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 阶段中断未使能,但仍走 initOnly 路径保持一致性 */
const uint16_t id_low = initOnlyReadReg(LAN9252_REG_CHIP_ID_L);
const uint16_t id_high = initOnlyReadReg(LAN9252_REG_CHIP_ID_H);
return ((uint32_t)id_high << 16) | id_low;
/* INIT-ONLY:init 阶段中断未使能,直接读取 BYTE_TEST(32-bit) */
return readReg32(LAN9252_REG_BYTE_TEST);
}
uint16_t EscLan9252::readReg(uint16_t addr)
{
/* ESC 寄存器地址按字节寻址,EXMC 16-bit 访问需要地址左移 1 位 */
return EscExmcBus::read16((uint32_t)addr << 1U);
return readReg16(addr);
}
void EscLan9252::writeReg(uint16_t addr, uint16_t v)
{
writeReg16(addr, v);
}
__attribute__((section(".ramfunc"), noinline))
@@ -87,18 +157,34 @@ void EscLan9252::readPdi(uint16_t offset, uint8_t *buf, size_t len)
if (buf == nullptr || len == 0U) {
return;
}
/* PDI 按 16-bit 读取,非对齐字节需特殊处理(此处简化为按半字读取) */
const uint32_t exmc_offset = (uint32_t)(LAN9252_PDI_BASE_OFFSET + offset) << 1U;
const size_t half_count = len / 2U;
for (size_t i = 0U; i < half_count; ++i) {
uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(i * 2U));
buf[i * 2U] = (uint8_t)(w & 0xFFU);
buf[i * 2U + 1] = (uint8_t)((w >> 8) & 0xFFU);
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;
}
/* 处理奇数字节 */
if ((len & 1U) != 0U) {
uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(half_count * 2U));
buf[half_count * 2U] = (uint8_t)(w & 0xFFU);
/* 剩余 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);
}
}
@@ -108,20 +194,53 @@ void EscLan9252::writePdi(uint16_t offset, const uint8_t *buf, size_t len)
if (buf == nullptr || len == 0U) {
return;
}
const uint32_t exmc_offset = (uint32_t)(LAN9252_PDI_BASE_OFFSET + offset) << 1U;
const size_t half_count = len / 2U;
for (size_t i = 0U; i < half_count; ++i) {
uint16_t w = (uint16_t)buf[i * 2U] | ((uint16_t)buf[i * 2U + 1] << 8);
EscExmcBus::write16(exmc_offset + (uint32_t)(i * 2U), w);
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;
}
if ((len & 1U) != 0U) {
EscExmcBus::write16(exmc_offset + (uint32_t)(half_count * 2U), (uint16_t)buf[half_count * 2U]);
/* 剩余 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);
}
}
/* ---- initOnly* 接口:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用 ----
* 运行期禁止调用;需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
uint16_t EscLan9252::initOnlyReadReg(uint16_t addr)
{
return readReg(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);
}