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:
@@ -8,8 +8,8 @@
|
||||
*
|
||||
* HBI 寄存器 EXMC 偏移(A1=EXMC_A16=PD11, A2=EXMC_A17=PD12):
|
||||
* HBI_IDX_0 低 word: A2=0, A1=0 → EXMC 偏移 0x00000
|
||||
* HBI_DATA_0 低 word: A2=1, A1=0 → EXMC 偏移 0x40000
|
||||
* HBI_DATA_0 高 word: A2=1, A1=1 → EXMC 偏移 0x60000
|
||||
* HBI_DATA_0 低 word: A2=1, A1=0 → EXMC 偏移 0x20000
|
||||
* HBI_DATA_0 高 word: A2=1, A1=1 → EXMC 偏移 0x30000
|
||||
*
|
||||
* 注意:32-bit 寄存器访问时,写一次 IDX 后连续写/读两次 DATA_0(低 word 在前),
|
||||
* HBI 内部 BYTE/WORD 计数器会自动组装 DWORD,无需改 IDX 地址。
|
||||
@@ -24,13 +24,13 @@ namespace {
|
||||
/* HBI Indexed Mode 寄存器 EXMC 偏移
|
||||
* 地址解码(A2 是 IDX/DATA 选择位,必须接到 EXMC_A17=PD12):
|
||||
* A2=0, A1=0 → word 0 → 偏移 0x00000 → HBI_IDX_0 低 word
|
||||
* A2=0, A1=1 → word 1 → 偏移 0x20000 → HBI_IDX_0 高 word(A1=EXMC_A16=PD11)
|
||||
* A2=1, A1=0 → word 2 → 偏移 0x40000 → HBI_DATA_0 低 word(A2=EXMC_A17=PD12)
|
||||
* A2=1, A1=1 → word 3 → 偏移 0x60000 → HBI_DATA_0 高 word
|
||||
* 注:EXMC A16 对应 byte 偏移 0x20000,A17 对应 0x40000。 */
|
||||
* A2=0, A1=1 → word 1 → 偏移 0x10000 → HBI_IDX_0 高 word(A1=EXMC_A16=PD11)
|
||||
* A2=1, A1=0 → word 2 → 偏移 0x20000 → HBI_DATA_0 低 word(A2=EXMC_A17=PD12)
|
||||
* A2=1, A1=1 → word 3 → 偏移 0x30000 → HBI_DATA_0 高 word
|
||||
* 注:EXMC A16 对应 byte 偏移 0x10000,A17 对应 0x20000。 */
|
||||
constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1, A1=1 */
|
||||
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x20000U; /* A2=1, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x30000U; /* A2=1, A1=1 */
|
||||
|
||||
/* ESC EEPROM 寄存器字节地址 */
|
||||
constexpr uint16_t ESC_EEP_CFG = 0x0500U; /* 8-bit */
|
||||
@@ -76,14 +76,36 @@ bool waitIdle(uint32_t timeout_loops = 500000U)
|
||||
|
||||
uint8_t readEsc8(uint16_t esc_addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
return (uint8_t)(EscExmcBus::read16(HBI_DATA0_LOW_OFF) & 0x00FFU);
|
||||
const uint16_t idx = esc_addr & 0xFFFCU;
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, idx);
|
||||
const uint8_t byte_offset = esc_addr & 0x3U;
|
||||
if (byte_offset < 2U) {
|
||||
uint16_t w = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
return (byte_offset == 0U) ? (uint8_t)(w & 0xFFU) : (uint8_t)((w >> 8) & 0xFFU);
|
||||
} else {
|
||||
uint16_t w = EscExmcBus::read16(HBI_DATA0_HIGH_OFF);
|
||||
return (byte_offset == 2U) ? (uint8_t)(w & 0xFFU) : (uint8_t)((w >> 8) & 0xFFU);
|
||||
}
|
||||
}
|
||||
|
||||
void writeEsc8(uint16_t esc_addr, uint8_t val)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)val);
|
||||
const uint16_t idx = esc_addr & 0xFFFCU;
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, idx);
|
||||
const uint8_t byte_offset = esc_addr & 0x3U;
|
||||
if (byte_offset < 2U) {
|
||||
uint16_t w = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
w = (byte_offset == 0U) ? ((w & 0xFF00U) | (uint16_t)val)
|
||||
: ((w & 0x00FFU) | ((uint16_t)val << 8));
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, idx);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, w);
|
||||
} else {
|
||||
uint16_t w = EscExmcBus::read16(HBI_DATA0_HIGH_OFF);
|
||||
w = (byte_offset == 2U) ? ((w & 0xFF00U) | (uint16_t)val)
|
||||
: ((w & 0x00FFU) | ((uint16_t)val << 8));
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, idx);
|
||||
EscExmcBus::write16(HBI_DATA0_HIGH_OFF, w);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t readEsc16(uint16_t esc_addr)
|
||||
@@ -102,21 +124,30 @@ uint32_t readEsc32(uint16_t esc_addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
uint16_t lo = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
uint16_t hi = EscExmcBus::read16(HBI_DATA0_LOW_OFF); /* 计数器自增,读高 word */
|
||||
uint16_t hi = EscExmcBus::read16(HBI_DATA0_HIGH_OFF); /* HBI_DATA_0 高 16 位 */
|
||||
return ((uint32_t)hi << 16) | lo;
|
||||
}
|
||||
|
||||
void writeEsc32(uint16_t esc_addr, uint32_t val)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)(val & 0xFFFFU)); /* 低 word */
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)((val >> 16) & 0xFFFFU));/* 高 word */
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)(val & 0xFFFFU)); /* 低 word */
|
||||
EscExmcBus::write16(HBI_DATA0_HIGH_OFF, (uint16_t)((val >> 16) & 0xFFFFU));/* 高 word */
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool EscEeprom::s_inited = false;
|
||||
|
||||
namespace {
|
||||
/* 简单延时(~0.5ms @180MHz,volatile 防优化) */
|
||||
void delayApprox(void)
|
||||
{
|
||||
volatile uint32_t n = 22000U;
|
||||
while (n-- > 0U) { __NOP(); }
|
||||
}
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool EscEeprom::init()
|
||||
{
|
||||
s_inited = false;
|
||||
@@ -126,37 +157,72 @@ bool EscEeprom::init()
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 步骤 1:置 PDI EEPROM Control = 1,把 EEPROM 接口交给 PDI(GD32) */
|
||||
writeEsc8(ESC_EEP_CFG, EEP_CFG_PDI_CTRL);
|
||||
/* LAN9252 上电后 EEPROM 控制器先做初始加载(读 AT24C32),
|
||||
* 即使 EEPROM 空白也需要几毫秒完成/超时。此期间 PDI 写 0x0500 会被忽略。
|
||||
* 重试最多 50 次(~100ms),等待控制器释放后 PDI 才能接管。 */
|
||||
bool acquired = false;
|
||||
for (uint32_t attempt = 0U; attempt < 50U; ++attempt) {
|
||||
/* 首次重试前打印诊断:raw 读 + 直接 word 写(绕过 writeEsc8 字节 RMW) */
|
||||
if (attempt == 0U) {
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CFG);
|
||||
uint16_t raw_cfg = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CTRL);
|
||||
uint16_t raw_ctrl = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
printf("[EEPROM] diag: raw cfg(0x0500)=0x%04X ctrl(0x0502)=0x%04X\r\n",
|
||||
(unsigned)raw_cfg, (unsigned)raw_ctrl);
|
||||
if (raw_cfg == ESC_EEP_CFG && raw_ctrl == ESC_EEP_CTRL) {
|
||||
/* 读回值 == 刚写入的 IDX 值 => 数据总线回显,LAN9252 完全没有驱动总线。
|
||||
* 注意:LAN9252 的 HBI 在 EEPROM 空白时也工作(手册 9.5.4.2),
|
||||
* 所以这不是 EEPROM 问题,而是:CS(PD7/NE0)未拉低、
|
||||
* CHIP_MODE0/1 strap 非 00(三端口模式=SPI only)、
|
||||
* 或芯片未复位/未上电。先看 [ECAT][ESC] HBI_IDX_0 探针。 */
|
||||
printf("[EEPROM] diag: *** LAN9252 NOT RESPONDING (bus echo of IDX) ***\r\n");
|
||||
printf("[EEPROM] diag: *** check CS(PD7) pulse / CHIP_MODE straps=00 / RST# ***\r\n");
|
||||
}
|
||||
/* 尝试直接 16-bit word 写 0x0001 到 0x0500 */
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CFG);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, 0x0001U);
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CFG);
|
||||
uint16_t raw_after = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
printf("[EEPROM] diag: after direct word write, cfg=0x%04X\r\n", (unsigned)raw_after);
|
||||
}
|
||||
|
||||
/* 步骤 2:置 Access to EEPROM = 1,获取访问权 */
|
||||
writeEsc8(ESC_EEP_PDI_ACC, EEP_PDI_ACC_ACCESS);
|
||||
/* 步骤 1:置 PDI EEPROM Control = 1,把 EEPROM 接口交给 PDI(GD32) */
|
||||
writeEsc8(ESC_EEP_CFG, EEP_CFG_PDI_CTRL);
|
||||
|
||||
/* 步骤 3:等待接口空闲,读回状态确认 */
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] init: FAIL, EEPROM interface busy timeout\r\n");
|
||||
return false;
|
||||
/* 步骤 2:置 Access to EEPROM = 1,获取访问权 */
|
||||
writeEsc8(ESC_EEP_PDI_ACC, EEP_PDI_ACC_ACCESS);
|
||||
|
||||
/* 步骤 3:等待接口空闲 */
|
||||
if (!waitIdle()) {
|
||||
delayApprox();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 读回 Configuration 寄存器确认 PDI 控制已生效 */
|
||||
uint8_t cfg = readEsc8(ESC_EEP_CFG);
|
||||
if ((cfg & EEP_CFG_PDI_CTRL) != 0U) {
|
||||
acquired = true;
|
||||
|
||||
/* 确认 EEPROM 算法:AT24C32 = 32Kbit,需要 2 地址字节模式 */
|
||||
uint16_t ctrl = readEsc16(ESC_EEP_CTRL);
|
||||
if ((ctrl & EEP_CTRL_2ADDR) == 0U) {
|
||||
printf("[EEPROM] init: WARN, 2-address-byte mode not set (ctrl=0x%04X)\r\n",
|
||||
(unsigned)ctrl);
|
||||
}
|
||||
|
||||
s_inited = true;
|
||||
printf("[EEPROM] init: OK (attempt %u, cfg=0x%02X, ctrl=0x%04X)\r\n",
|
||||
(unsigned)(attempt + 1U), (unsigned)cfg, (unsigned)ctrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* PDI 控制位未生效,等待后重试 */
|
||||
delayApprox();
|
||||
}
|
||||
|
||||
/* 读回 Configuration 寄存器确认 PDI 控制已生效 */
|
||||
uint8_t cfg = readEsc8(ESC_EEP_CFG);
|
||||
if ((cfg & EEP_CFG_PDI_CTRL) == 0U) {
|
||||
printf("[EEPROM] init: FAIL, PDI EEPROM Control not set (cfg=0x%02X)\r\n", (unsigned)cfg);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 确认 EEPROM 算法:AT24C32 = 32Kbit,需要 2 地址字节模式 */
|
||||
uint16_t ctrl = readEsc16(ESC_EEP_CTRL);
|
||||
if ((ctrl & EEP_CTRL_2ADDR) == 0U) {
|
||||
printf("[EEPROM] init: WARN, 2-address-byte mode not set (ctrl=0x%04X), AT24C32 requires it\r\n",
|
||||
(unsigned)ctrl);
|
||||
/* 不直接 fail,允许继续尝试;可能由 strap 自动配置 */
|
||||
}
|
||||
|
||||
s_inited = true;
|
||||
printf("[EEPROM] init: OK, PDI has EEPROM control (cfg=0x%02X, ctrl=0x%04X)\r\n",
|
||||
(unsigned)cfg, (unsigned)ctrl);
|
||||
return true;
|
||||
printf("[EEPROM] init: FAIL, PDI control not acquired after 50 retries\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EscEeprom::readWord(uint16_t word_addr, uint16_t &out)
|
||||
|
||||
Reference in New Issue
Block a user