Files
DCU_APP/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp
T
cbb a2d0b28b3d 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初始化逻辑,添加重试机制和诊断功能
2026-08-09 03:27:21 +08:00

472 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file esc_exmc_bus.cpp
* @brief LAN9252 ESC 的 EXMC 16-bit 并行总线驱动实现。
*
* 使用 GigaDevice gd32e50x_exmc 标准库。
* 注意:DFP 只把 FMC 注册为 CMSIS 组件,EXMC 未进组件列表;
* 源文件放在 RTE/Device/... 并由 cproject group device_exmc 本地编译。
*/
#include "comm/esc_exmc_bus.h"
#include "sys/config.h"
#include "device/watchdog.h"
#include <stdio.h>
extern "C" {
#include "gd32e50x_exmc.h" /* RTE/Device/GD32E508VE 或 -I 同目录 */
#include "gd32e50x_gpio.h"
#include "gd32e50x_rcu.h"
}
/* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。 */
#define ESC_EXMC_REG16(offset) \
(*(volatile uint16_t *)(CONFIG_ESC_EXMC_BASE_ADDR + (offset)))
/* ========================================================================
* GPIO 位拍(bit-bang)辅助函数 —— 完全绕过 EXMC 外设,
* 用普通 GPIO 手动产生 LAN9252 HBI 时序,用于判定"芯片是否活着"。
* 数据线物理映射:D0=PD14 D1=PD15 D2=PD0 D3=PD1 D4..D12=PE7..PE15
* D13=PD8 D14=PD9 D15=PD10
* 控制线:CS#=PD7(NE0) RD#=PD4(NOE) WR#=PD5(NWE) A1=PD11 A2=PD12
* ======================================================================== */
namespace {
/* 通断测试模式状态(提前声明供 diagDump() 检查) */
static bool s_continuityActive = false;
/* GPIOD/GPIOE 上属于数据总线的引脚掩码 */
constexpr uint32_t kDataMaskD =
(1U << 0) | (1U << 1) | (1U << 8) | (1U << 9) | (1U << 10) | (1U << 14) |
(1U << 15);
constexpr uint32_t kDataMaskE = 0xFF80U; /* PE7..PE15 */
constexpr uint32_t kPinCs = 7U; /* PD7 */
constexpr uint32_t kPinRd = 4U; /* PD4 */
constexpr uint32_t kPinWr = 5U; /* PD5 */
constexpr uint32_t kPinA1 = 11U; /* PD11 */
constexpr uint32_t kPinA2 = 12U; /* PD12 */
/* LAN9252 MODE0/1 strap 引脚:临时用 PB8/PB9 模拟拉高/拉低测试。
* 实际硬件 MODE0(Pin48)=PB8, MODE1(Pin46)=PB9(若未接 PB8/PB9 则此测试无效)。 */
constexpr uint32_t kPinMode0 = 8U; /* PB8 -> LAN9252 Pin48 MODE0 */
constexpr uint32_t kPinMode1 = 9U; /* PB9 -> LAN9252 Pin46 MODE1 */
void bbDelay() {
for (volatile uint32_t i = 0U; i < 3000U; i++) {
} /* ~40-80us @180MHz,远大于 HBI 时序要求 */
}
uint16_t bbReadData() {
uint32_t d = GPIO_ISTAT(GPIOD);
uint32_t e = GPIO_ISTAT(GPIOE);
uint16_t v = 0U;
if (d & (1U << 14)) v |= (uint16_t)(1U << 0); /* D0 */
if (d & (1U << 15)) v |= (uint16_t)(1U << 1); /* D1 */
if (d & (1U << 0)) v |= (uint16_t)(1U << 2); /* D2 */
if (d & (1U << 1)) v |= (uint16_t)(1U << 3); /* D3 */
if (e & (1U << 7)) v |= (uint16_t)(1U << 4); /* D4 */
if (e & (1U << 8)) v |= (uint16_t)(1U << 5); /* D5 */
if (e & (1U << 9)) v |= (uint16_t)(1U << 6); /* D6 */
if (e & (1U << 10)) v |= (uint16_t)(1U << 7); /* D7 */
if (e & (1U << 11)) v |= (uint16_t)(1U << 8); /* D8 */
if (e & (1U << 12)) v |= (uint16_t)(1U << 9); /* D9 */
if (e & (1U << 13)) v |= (uint16_t)(1U << 10); /* D10 */
if (e & (1U << 14)) v |= (uint16_t)(1U << 11); /* D11 */
if (e & (1U << 15)) v |= (uint16_t)(1U << 12); /* D12 */
if (d & (1U << 8)) v |= (uint16_t)(1U << 13); /* D13 */
if (d & (1U << 9)) v |= (uint16_t)(1U << 14); /* D14 */
if (d & (1U << 10)) v |= (uint16_t)(1U << 15); /* D15 */
return v;
}
void bbDriveData(uint16_t v) {
uint32_t dset = 0U, dclr = 0U, eset = 0U, eclr = 0U;
/* GPIOD 数据位 */
const uint32_t dpins[7] = {14U, 15U, 0U, 1U, 8U, 9U, 10U};
const int dbits[7] = {0, 1, 2, 3, 13, 14, 15};
for (int i = 0; i < 7; ++i) {
if (v & (1U << dbits[i])) dset |= (1U << dpins[i]);
else dclr |= (1U << dpins[i]);
}
/* GPIOE 数据位:D4..D12 = PE7..PE15 = 数据位 4..12 */
for (int bit = 4; bit <= 12; ++bit) {
uint32_t pin = (uint32_t)(bit + 3); /* bit4->PE7 ... bit12->PE15 */
if (v & (1U << bit)) eset |= (1U << pin);
else eclr |= (1U << pin);
}
if (dset) GPIO_BOP(GPIOD) = dset;
if (dclr) GPIO_BC(GPIOD) = dclr;
if (eset) GPIO_BOP(GPIOE) = eset;
if (eclr) GPIO_BC(GPIOE) = eclr;
}
} /* anonymous namespace */
bool EscExmcBus::s_ok = false;
bool EscExmcBus::init() {
s_ok = false;
ECAT_LOGI("[ECAT][EXMC] init: enable RCU clocks\r\n");
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_EXMC);
/* 配置 16-bit 数据线 D0-D15 为 AF 推挽 */
gpio_init(CONFIG_EXMC_D0_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D0_PIN);
gpio_init(CONFIG_EXMC_D1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D1_PIN);
gpio_init(CONFIG_EXMC_D2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D2_PIN);
gpio_init(CONFIG_EXMC_D3_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D3_PIN);
gpio_init(CONFIG_EXMC_D4_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D4_PIN);
gpio_init(CONFIG_EXMC_D5_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D5_PIN);
gpio_init(CONFIG_EXMC_D6_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D6_PIN);
gpio_init(CONFIG_EXMC_D7_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D7_PIN);
gpio_init(CONFIG_EXMC_D8_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D8_PIN);
gpio_init(CONFIG_EXMC_D9_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D9_PIN);
gpio_init(CONFIG_EXMC_D10_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D10_PIN);
gpio_init(CONFIG_EXMC_D11_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D11_PIN);
gpio_init(CONFIG_EXMC_D12_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D12_PIN);
gpio_init(CONFIG_EXMC_D13_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D13_PIN);
gpio_init(CONFIG_EXMC_D14_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D14_PIN);
gpio_init(CONFIG_EXMC_D15_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_D15_PIN);
/* 控制线:A16 / A17 / NOE / NWE / NE1
* A17(PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0,已将 LAN9252 A2 (Pin 29)
* 接到 PD12。 */
gpio_init(CONFIG_EXMC_A16_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_A16_PIN);
gpio_init(CONFIG_EXMC_A17_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_A17_PIN);
gpio_init(CONFIG_EXMC_NOE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_NOE_PIN);
gpio_init(CONFIG_EXMC_NWE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_NWE_PIN);
gpio_init(CONFIG_EXMC_NE1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
CONFIG_EXMC_NE1_PIN);
ECAT_LOGI(
"[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE1)\r\n");
/*
* EXMC NOR/SRAM Region0:16-bit、非复用、异步写。
* 注意:GD32E508Vx (LQFP100) 只有 NE0 (PD7) 一个 NOR/SRAM 片选引脚,
* NE0 归属 Region0 (0x60000000, SNCTL0),必须用 Region0。
* 时序(AHB=180MHz,1 HCLK ≈ 5.56ns):
* LAN9252 HBI 最小读/写周期约 100ns → data_setup ≥ 18 HCLK。
* asyn_* 字段单位为 HCLK 计数(库文档范围
* address_setup 1..16,data_setup 2..256)。
*/
exmc_norsram_parameter_struct norsram_init;
exmc_norsram_timing_parameter_struct rw_timing;
exmc_norsram_timing_parameter_struct wr_timing;
norsram_init.read_write_timing = &rw_timing;
norsram_init.write_timing = &wr_timing;
exmc_norsram_struct_para_init(&norsram_init);
norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0;
norsram_init.write_mode = EXMC_ASYN_WRITE;
norsram_init.extended_mode = DISABLE;
norsram_init.asyn_wait = DISABLE;
norsram_init.nwait_signal = DISABLE;
norsram_init.memory_write = ENABLE;
norsram_init.nwait_config = EXMC_NWAIT_CONFIG_BEFORE;
norsram_init.wrap_burst_mode = DISABLE;
norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
norsram_init.burst_mode = DISABLE;
norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
norsram_init.memory_type = EXMC_MEMORY_TYPE_SRAM;
norsram_init.address_data_mux = DISABLE;
/* 读/写共用时序(非 extended mode) */
rw_timing.asyn_access_mode = EXMC_ACCESS_MODE_A;
rw_timing.syn_data_latency = EXMC_DATALAT_2_CLK;
rw_timing.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE;
rw_timing.bus_latency = 1U;
rw_timing.asyn_address_setuptime = 2U; /* ~11ns */
rw_timing.asyn_address_holdtime = 2U; /* min 2 */
rw_timing.asyn_data_setuptime = 20U; /* ~111ns ≥ 100ns HBI */
wr_timing = rw_timing;
ECAT_LOGI("[ECAT][EXMC] init: applying NOR/SRAM Region0 params (16b, async, "
"t_data=%u HCLK)\r\n",
(unsigned)rw_timing.asyn_data_setuptime);
exmc_norsram_init(&norsram_init);
exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0);
/* === 清除 reserved bit 7 ===
* 现象:exmc_norsram_init 后 EXMC_SNCTL0=0x00001091,bit 7(reserved)=1。
* DFP 复位值 0x000030DB 的 bit 7=1,可能被硬件保留或导致未定义行为。
* NRW(bits 4-5)=01 已经是 16-bit,无需修改。
* 修复:清除 bit 7,保持其他位不变。 */
uint32_t snctl_before = EXMC_SNCTL0;
EXMC_SNCTL0 = snctl_before & ~BIT(7);
uint32_t snctl_after = EXMC_SNCTL0;
ECAT_LOGI("[ECAT][EXMC] init: SNCTL0 fix: before=0x%08lX after=0x%08lX (bit7 reserved cleared)\r\n",
(unsigned long)snctl_before, (unsigned long)snctl_after);
/* 总线探活:LAN9252 处于 16-bit HBI Indexed Mode,无法通过固定偏移直接读取。
* 真实的芯片 ID / AL 状态校验由 EscLan9252::init() 通过 HBI_IDX_0/HBI_DATA_0
* 间接访问完成。 注意:LAN9252 上电后第一次 Host Bus
* 操作必须是读周期,否则后续写操作会被忽略; EscLan9252 在 init 中首先读取
* CHIP_ID,满足该约束。 */
ECAT_LOGI("[ECAT][EXMC] init: Region0 enabled, chip ID will be verified by "
"ESC driver\r\n");
/* ---- 硬件诊断: 转储关键寄存器 + GPIO 手动输出测试 ---- */
diagDump();
s_ok = true;
ECAT_LOGI("[ECAT][EXMC] init: OK, bus enabled\r\n");
return true;
}
/* ========================================================================
* 硬件诊断: 精简版 —— 只保留核心探活 + bit-bang + 位级回显
* ======================================================================== */
void EscExmcBus::diagDump() {
/* 通断测试模式活跃时跳过诊断,避免 20s 周期 probe 覆盖用户设置的电平 */
if (s_continuityActive) {
printf("[EXMC-DIAG] skipped: continuity test mode active\r\n");
return;
}
printf("[EXMC-DIAG] --- bit-bang probe (bypass EXMC) ---\r\n");
/* 保存原 GPIO 配置 */
uint32_t save_ctl0_d = GPIO_CTL0(GPIOD);
uint32_t save_ctl1_d = GPIO_CTL1(GPIOD);
uint32_t save_ctl0_e = GPIO_CTL0(GPIOE);
uint32_t save_ctl1_e = GPIO_CTL1(GPIOE);
/* 控制线 OUT_PP, 数据线 IN */
gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7|GPIO_PIN_11|GPIO_PIN_12);
gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, 0, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_8|
GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15);
gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, 0, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|
GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
/* 空闲:CS/RD/WR 高, A1=A2=0 */
GPIO_BOP(GPIOD) = (1U<<7)|(1U<<4)|(1U<<5);
GPIO_BC(GPIOD) = (1U<<11)|(1U<<12);
bbDelay();
/* 读 IDX_0 (A2=0) —— 复位默认值 0x1234 */
GPIO_BC(GPIOD) = (1U<<7); /* CS=低 */
bbDelay();
GPIO_BC(GPIOD) = (1U<<4); /* RD=低 */
bbDelay();
uint16_t bb_idx0 = bbReadData();
GPIO_BOP(GPIOD) = (1U<<4); /* RD=高 */
bbDelay();
GPIO_BOP(GPIOD) = (1U<<7); /* CS=高 */
bbDelay();
printf("[EXMC-DIAG] bit-bang IDX_0 = 0x%04X (expect 0x1234)\r\n",
(unsigned)bb_idx0);
/* 判读 */
if (bb_idx0 == 0x1234U) {
printf("[EXMC-DIAG] *** LAN9252 ALIVE ***\r\n");
} else if (bb_idx0 == 0x0000U || bb_idx0 == 0xFFFFU) {
printf("[EXMC-DIAG] FAIL: bus floating (0x%04X) -> data line broken or chip not driving\r\n",
(unsigned)bb_idx0);
} else {
printf("[EXMC-DIAG] UNEXPECTED: 0x%04X -> partial data line issue?\r\n",
(unsigned)bb_idx0);
}
/* 恢复 GPIO */
GPIO_CTL0(GPIOD) = save_ctl0_d;
GPIO_CTL1(GPIOD) = save_ctl1_d;
GPIO_CTL0(GPIOE) = save_ctl0_e;
GPIO_CTL1(GPIOE) = save_ctl1_e;
/* EXMC 寄存器回读(一行) */
printf("[EXMC-DIAG] SNCTL0=0x%08lX SNTCFG0=0x%08lX\r\n",
(unsigned long)EXMC_SNCTL0, (unsigned long)EXMC_SNTCFG0);
/* EXMC raw 读 + 位级回显(仅在未响应时) */
uint16_t idx0_raw = ESC_EXMC_REG16(0x00000);
printf("[EXMC-DIAG] EXMC raw IDX_0 = 0x%04X\r\n", (unsigned)idx0_raw);
if (idx0_raw != 0x1234U) {
/* 位级回显: 写 4 个 pattern 看哪些位回显(浮空)哪些位被驱动 */
const uint16_t patterns[4] = {0x0000U, 0xFFFFU, 0xAAAAU, 0x5555U};
uint16_t readbacks[4] = {0};
for (int i = 0; i < 4; ++i) {
ESC_EXMC_REG16(0x00000) = patterns[i];
for (volatile int d = 0; d < 50; d++) {}
readbacks[i] = ESC_EXMC_REG16(0x00000);
}
uint16_t echo_bits = 0U, driven_bits = 0U;
for (int b = 0; b < 16; ++b) {
uint16_t mask = (uint16_t)(1U << b);
bool all_echo = true;
for (int i = 0; i < 4; ++i) {
if ((readbacks[i] & mask) != (patterns[i] & mask)) { all_echo = false; break; }
}
if (all_echo) echo_bits |= mask; else driven_bits |= mask;
}
printf("[EXMC-DIAG] echo bits(disconnected): 0x%04X driven bits(connected): 0x%04X\r\n",
(unsigned)echo_bits, (unsigned)driven_bits);
if (echo_bits == 0xFFFFU) {
printf("[EXMC-DIAG] ALL 16 bits floating -> CS not reaching LAN9252 or chip dead\r\n");
printf("[EXMC-DIAG] -> run continuityTest() to check data lines with multimeter\r\n");
} else if (driven_bits == 0xFFFFU) {
printf("[EXMC-DIAG] ALL 16 bits driven -> HBI alive but IDX_0!=0x1234 (unexpected)\r\n");
} else {
printf("[EXMC-DIAG] PARTIAL echo -> broken data lines: ");
for (int b = 0; b < 16; ++b) if (echo_bits & (1U << b)) printf("D%d ", b);
printf("\r\n");
}
} else {
printf("[EXMC-DIAG] === HBI ALIVE ===\r\n");
}
}
/* ========================================================================
* 通断测试: 网页逐个 pattern 触发,GD32 驱动数据线/控制线输出已知电平,
* 用户用万用表在 LAN9252 端测量对应引脚电压。
* 设计:
* - continuityDrive(action) 不延时,驱动完立即返回,电平保持到下次调用
* - action=0(stop) 恢复 GPIO 为 EXMC AF_PP,回到正常工作模式
* - 不点按钮时引脚是高阻输入态(安全),手碰到也不短路
* 判据:
* - GD32 输出高(3.3V)但 LAN9252 端测到 0V/浮空 → 断线
* - GD32 输出低(0V)但 LAN9252 端测到 3.3V → 断线/短路
* - 电压跟随 → 连线正常
* ======================================================================== */
static uint32_t s_save_ctl0_d = 0, s_save_ctl1_d = 0;
static uint32_t s_save_ctl0_e = 0, s_save_ctl1_e = 0;
static void continuityEnterTestMode() {
if (s_continuityActive) return;
s_save_ctl0_d = GPIO_CTL0(GPIOD);
s_save_ctl1_d = GPIO_CTL1(GPIOD);
s_save_ctl0_e = GPIO_CTL0(GPIOE);
s_save_ctl1_e = GPIO_CTL1(GPIOE);
/* 全部数据线 + 控制线设为 OUT_PP */
gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
kDataMaskD | (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr) |
(1U<<kPinA1) | (1U<<kPinA2));
gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kDataMaskE);
/* 控制线空闲态: CS/RD/WR=高, A1=A2=低 */
GPIO_BOP(GPIOD) = (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr);
GPIO_BC(GPIOD) = (1U<<kPinA1) | (1U<<kPinA2);
s_continuityActive = true;
printf("[CONT] test mode entered (GPIO -> OUT_PP)\r\n");
}
static void continuityExitTestMode() {
if (!s_continuityActive) return;
GPIO_CTL0(GPIOD) = s_save_ctl0_d;
GPIO_CTL1(GPIOD) = s_save_ctl1_d;
GPIO_CTL0(GPIOE) = s_save_ctl0_e;
GPIO_CTL1(GPIOE) = s_save_ctl1_e;
s_continuityActive = false;
printf("[CONT] test mode exited (GPIO -> AF_PP restored)\r\n");
}
uint8_t EscExmcBus::continuityDrive(uint8_t action) {
switch (action) {
case 0: /* stop */
continuityExitTestMode();
return 0;
case 1: /* 0xFFFF ALL HIGH */
case 2: /* 0x0000 ALL LOW */
case 3: /* 0xAAAA ODD HIGH */
case 4: { /* 0x5555 EVEN HIGH */
continuityEnterTestMode();
static const uint16_t pats[4] = {0xFFFF, 0x0000, 0xAAAA, 0x5555};
uint16_t pat = pats[action - 1];
bbDriveData(pat);
/* 控制线恢复空闲态(避免上次控制线测试残留) */
GPIO_BOP(GPIOD) = (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr);
GPIO_BC(GPIOD) = (1U<<kPinA1) | (1U<<kPinA2);
printf("[CONT] pattern 0x%04X driven\r\n", (unsigned)pat);
return 0;
}
case 5: /* CS low */
case 6: /* RD low */
case 7: /* WR low */
case 8: /* A1 high */
case 9: { /* A2 high */
continuityEnterTestMode();
/* 数据线恢复空闲(避免上次数据 pattern 残留) */
bbDriveData(0x0000);
uint32_t pin;
bool drive_high;
switch (action) {
case 5: pin = kPinCs; drive_high = false; break; /* CS low */
case 6: pin = kPinRd; drive_high = false; break; /* RD low */
case 7: pin = kPinWr; drive_high = false; break; /* WR low */
case 8: pin = kPinA1; drive_high = true; break; /* A1 high */
case 9: pin = kPinA2; drive_high = true; break; /* A2 high */
default: return 0xEE;
}
/* 先把所有控制线恢复空闲,再驱动目标线 */
GPIO_BOP(GPIOD) = (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr);
GPIO_BC(GPIOD) = (1U<<kPinA1) | (1U<<kPinA2);
if (drive_high) {
GPIO_BOP(GPIOD) = (1U << pin);
} else {
GPIO_BC(GPIOD) = (1U << pin);
}
printf("[CONT] control line PD%lu driven %s\r\n",
(unsigned long)pin, drive_high ? "HIGH" : "LOW");
return 0;
}
case 10: /* MODE0 high (LAN9252 Pin48) via PB8 */
case 11: /* MODE1 high (LAN9252 Pin46) via PB9 */
case 12: /* MODE0 low (LAN9252 Pin48) via PB8 */
case 13: { /* MODE1 low (LAN9252 Pin46) via PB9 */
continuityEnterTestMode();
/* 先把 MODE0/MODE1 恢复空闲(低),再驱动目标线 */
GPIO_BOP(GPIOB) = 0U;
GPIO_BC(GPIOB) = (1U<<kPinMode0) | (1U<<kPinMode1);
/* 确保 PB8/PB9 为输出 */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
(1U<<kPinMode0) | (1U<<kPinMode1));
uint32_t pin = (action==10 || action==12) ? kPinMode0 : kPinMode1;
bool high = (action==10 || action==11);
if (high) {
GPIO_BOP(GPIOB) = (1U << pin);
} else {
GPIO_BC(GPIOB) = (1U << pin);
}
printf("[CONT] MODE%d line PB%lu driven %s (for strap check)\r\n",
(action==10||action==12)?0:1, (unsigned long)pin,
high ? "HIGH" : "LOW");
return 0;
}
default:
return 0xEE;
}
}
__attribute__((section(".ramfunc"), noinline)) uint16_t
EscExmcBus::read16(uint32_t offset) {
return ESC_EXMC_REG16(offset);
}
__attribute__((section(".ramfunc"), noinline)) void
EscExmcBus::write16(uint32_t offset, uint16_t data) {
ESC_EXMC_REG16(offset) = data;
}