- 添加新的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初始化逻辑,添加重试机制和诊断功能
472 lines
19 KiB
C++
472 lines
19 KiB
C++
/**
|
||
* @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;
|
||
} |