feat(ethercat): LAN9252 bring-up完成,EEPROM烧录改主站侧并增强DC/AL可观测性
- 新增ESC硬件复位序列(1.5.25):PB10驱动LAN9252 RST#,拉低30ms+释放后300ms轮询HBI就绪, 满足tpurstd/trstia时序;此前RST#仅靠R44上拉自然释放 - 修正EXMC 16-bit地址左移(1.5.27):HBI DATA偏移0x20000/0x30000→0x40000/0x60000; ADDSET 2→4 HCLK;片选更名NE1→NE0(LQFP100仅Region0) - 按LAN9252手册/AN1907修正EEPROM镜像(1.5.28):word1高字节0x00→0xCC(SYNC0/1 Map+Output, 否则SYNC0留LATCH态DC过程数据死)、word2=5000(脉宽50µs)、尾部补真实0xFF; gen_eeprom.py按ETG.1000.6重写;DCU_ESI.xml PDI 0x05→0x8D、RxPdo/TxPdo互换改4×64bit对齐 - 删除GD32侧EEPROM烧录路径(1.5.29):移除EscEeprom/esc_eeprom、eeprom_image.h及主循环 烧录块,ESI镜像改由主站ecat_flash_eeprom.py经ESC 0x0500刷写(162字全校验通过); HBI互斥补屏蔽EXTI2(SYNC1),防Sync1_Isr两步事务打断主循环HBI访问致错址 - DC SYNC0通路可观测(1.5.30/31):EthercatSync新增sync0/sync1/escirq三个volatile计数器, task_runner 5s按增量打印速率;SSC OD新增只读0x2000 SYNC Diag,主站SDO轮询网页显示 - AL通路诊断(1.5.32):ECAT_Main/SetALStatus打印主站AL请求与写回状态+错误码, task_runner新增5s [ECAT][HB]心跳(仅同步ISR激活时);dc_app.c导出EcatDbg_ReadAlDiag - 删除死代码EthercatSlave::periodicIsr三缓冲PDO桥,PDO交换由SSC完成(4×64bit=32B); EthercatSlave::init()恢复调用并以isBusReady()(BYTE_TEST)门控 - CANFD修复同通道突发只发3帧:transmitFrame新增allow_abort区分突发/软件队列路径, 邮箱满落入软件TX队列(深度13)由TX完成中断drainTxQueueIsr回灌;BRS重新启用(0→1) - motor_state新增CONFIG帧缓存(0x00000101, DLC 24)记录目标值回报,条目44B→76B、每页5→3 - SPI 0xA2诊断扩展action 20~24(ECAT_NRST拉低/拉高/浮空/回读);新增ESP32 EN自检打印 - 版本号更新至1.5.32
This commit is contained in:
@@ -7,9 +7,12 @@
|
||||
* 源文件放在 RTE/Device/... 并由 cproject group device_exmc 本地编译。
|
||||
*/
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "comm/ethercat_sync.h"
|
||||
#include "sys/config.h"
|
||||
#include "device/watchdog.h"
|
||||
#include <stdio.h>
|
||||
#include "RTE_Components.h"
|
||||
#include CMSIS_device_header
|
||||
|
||||
extern "C" {
|
||||
#include "gd32e50x_exmc.h" /* RTE/Device/GD32E508VE 或 -I 同目录 */
|
||||
@@ -17,7 +20,10 @@ extern "C" {
|
||||
#include "gd32e50x_rcu.h"
|
||||
}
|
||||
|
||||
/* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。 */
|
||||
/* 内存映射访问宏:基址 + CPU 字节偏移。
|
||||
* 注意 EXMC 16-bit 模式地址左移 1 位(用户手册 p869:HADDR[25:1]→EXMC_A[24:0]):
|
||||
* EXMC_A16(PD11) 引脚对应偏移 bit17=0x20000,EXMC_A17(PD12) 对应 bit18=0x40000,
|
||||
* 即 1<<16 的 CPU 偏移驱动的是 A15 引脚而非 A16。 */
|
||||
#define ESC_EXMC_REG16(offset) \
|
||||
(*(volatile uint16_t *)(CONFIG_ESC_EXMC_BASE_ADDR + (offset)))
|
||||
|
||||
@@ -44,10 +50,6 @@ 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++) {
|
||||
@@ -142,9 +144,10 @@ bool EscExmcBus::init() {
|
||||
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。 */
|
||||
/* 控制线:A16 / A17 / NOE / NWE / NE0
|
||||
* A17(PD12) → LAN9252 A2 (Pin 29,IDX/DATA 选择);A16(PD11) → LAN9252 A1 (Pin 25)。
|
||||
* 注意 EXMC 16-bit 模式地址左移 1 位:HBI_DATA_0 的 CPU 偏移是 0x40000/0x60000,
|
||||
* 见 esc_lan9252.cpp 头注释。 */
|
||||
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,
|
||||
@@ -153,10 +156,10 @@ bool EscExmcBus::init() {
|
||||
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);
|
||||
gpio_init(CONFIG_EXMC_NE0_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,
|
||||
CONFIG_EXMC_NE0_PIN);
|
||||
ECAT_LOGI(
|
||||
"[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE1)\r\n");
|
||||
"[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE0)\r\n");
|
||||
|
||||
/*
|
||||
* EXMC NOR/SRAM Region0:16-bit、非复用、异步写。
|
||||
@@ -189,13 +192,17 @@ bool EscExmcBus::init() {
|
||||
norsram_init.memory_type = EXMC_MEMORY_TYPE_SRAM;
|
||||
norsram_init.address_data_mux = DISABLE;
|
||||
|
||||
/* 读/写共用时序(非 extended mode) */
|
||||
/* 读/写共用时序(非 extended mode)
|
||||
* 2026-08-20 慢档测试(16/16/256 HCLK)无效,已还原:慢档时序未带来任何
|
||||
* 读数变化,速度因素彻底排除(与 bit-bang 40-80us 仍失败一致)。 */
|
||||
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_address_setuptime = 4U; /* ~22ns。DS 表9-4/9-5: trdrd/twrwr/twrrd
|
||||
≥13ns——选通间隔=ADDSET 相,2 HCLK≈11.1ns
|
||||
不达标(仅靠 AHB 间隙兜底),4 HCLK 稳过 */
|
||||
rw_timing.asyn_address_holdtime = 2U; /* min 2,DS tah≥0 */
|
||||
rw_timing.asyn_data_setuptime = 20U; /* ~111ns ≥ 100ns HBI */
|
||||
|
||||
wr_timing = rw_timing;
|
||||
@@ -238,12 +245,23 @@ bool EscExmcBus::init() {
|
||||
* 硬件诊断: 精简版 —— 只保留核心探活 + bit-bang + 位级回显
|
||||
* ======================================================================== */
|
||||
void EscExmcBus::diagDump() {
|
||||
/* EtherCAT 同步 ISR 激活期间禁止诊断:本函数会把 EXMC 数据/控制线切成 GPIO,
|
||||
* 并大量 printf,会周期性拆毁运行中的 EtherCAT 总线。 */
|
||||
if (EthercatSync::isActive()) {
|
||||
printf("[EXMC-DIAG] skipped: EtherCAT sync ISR active (bus in use)\r\n");
|
||||
return;
|
||||
}
|
||||
/* 通断测试模式活跃时跳过诊断,避免 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");
|
||||
/* NRST 实际电平回读(1.5.26):输入通路常连引脚,输出态亦读真实电压。
|
||||
* 驱动高却 LOW => ECAT_NRST 被外部按低 => LAN9252 一直在复位态。 */
|
||||
printf("[EXMC-DIAG] NRST(PB10) reads %s\r\n",
|
||||
(gpio_input_bit_get(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN) == SET)
|
||||
? "HIGH" : "LOW");
|
||||
|
||||
/* 保存原 GPIO 配置 */
|
||||
uint32_t save_ctl0_d = GPIO_CTL0(GPIOD);
|
||||
@@ -289,6 +307,39 @@ void EscExmcBus::diagDump() {
|
||||
(unsigned)bb_idx0);
|
||||
}
|
||||
|
||||
/* --- 预驱动/释放/读周期三段采样: 区分"被动拉低"与"芯片主动驱动" ---
|
||||
* 背景: bit-bang 读到稳定 0x0000, 而纯浮空输入应保持上次残留值或漂移。
|
||||
* 两种解释: (a) 板上下拉/对地漏电(被动) (b) 芯片在 RD# 有效期驱动 0(主动)。
|
||||
* 序列: 驱动 pattern -> 切输入快采样(电容应保持) -> 慢采样(检测下拉)
|
||||
* -> 拉低 CS#+RD# 采样(芯片若响应应驱动总线, 例如 0x1234)。 */
|
||||
printf("[EXMC-DIAG] --- predrive/release/read probe ---\r\n");
|
||||
static const uint16_t probe_pats[2] = {0xAAAAU, 0x5555U};
|
||||
for (int p = 0; p < 2; ++p) {
|
||||
uint16_t pat = probe_pats[p];
|
||||
/* 1) 数据线推挽驱动 pattern(控制线保持空闲: CS/RD/WR 高, A1/A2 低) */
|
||||
gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kDataMaskD);
|
||||
gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kDataMaskE);
|
||||
bbDriveData(pat);
|
||||
bbDelay();
|
||||
/* 2) 切浮空输入, 快采样(自身电容应保持 pattern) */
|
||||
gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, 0, kDataMaskD);
|
||||
gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, 0, kDataMaskE);
|
||||
for (volatile int d = 0; d < 50; d++) {} /* ~微秒级 */
|
||||
uint16_t s_fast = bbReadData();
|
||||
/* 3) 慢采样(约 40-80us 后: 若板上有下拉会漏向 0) */
|
||||
bbDelay();
|
||||
uint16_t s_slow = bbReadData();
|
||||
/* 4) 读周期采样(CS#+RD# 低: 芯片若驱动会在总线上给出真实数据) */
|
||||
GPIO_BC(GPIOD) = (1U << 7) | (1U << 4);
|
||||
bbDelay();
|
||||
uint16_t s_rd = bbReadData();
|
||||
GPIO_BOP(GPIOD) = (1U << 4) | (1U << 7);
|
||||
bbDelay();
|
||||
printf("[EXMC-DIAG] pat=0x%04X fast=0x%04X slow=0x%04X rd=0x%04X\r\n",
|
||||
(unsigned)pat, (unsigned)s_fast, (unsigned)s_slow,
|
||||
(unsigned)s_rd);
|
||||
}
|
||||
|
||||
/* 恢复 GPIO */
|
||||
GPIO_CTL0(GPIOD) = save_ctl0_d;
|
||||
GPIO_CTL1(GPIOD) = save_ctl1_d;
|
||||
@@ -354,6 +405,7 @@ void EscExmcBus::diagDump() {
|
||||
|
||||
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 uint32_t s_maskPrev = 0U;
|
||||
|
||||
static void continuityEnterTestMode() {
|
||||
if (s_continuityActive) return;
|
||||
@@ -361,6 +413,9 @@ static void continuityEnterTestMode() {
|
||||
s_save_ctl1_d = GPIO_CTL1(GPIOD);
|
||||
s_save_ctl0_e = GPIO_CTL0(GPIOE);
|
||||
s_save_ctl1_e = GPIO_CTL1(GPIOE);
|
||||
/* 测试期屏蔽 SYNC0/ESC IRQ/SYNC1:ISR 的 HBI 访问落在已被切成 GPIO 态的
|
||||
* 总线上只会读回垃圾并错写 IDX,SSC 状态可能被污染(1.5.29)。 */
|
||||
s_maskPrev = EscExmcBus::maskEnter();
|
||||
/* 全部数据线 + 控制线设为 OUT_PP */
|
||||
gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
|
||||
kDataMaskD | (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr) |
|
||||
@@ -370,7 +425,7 @@ static void continuityEnterTestMode() {
|
||||
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");
|
||||
printf("[CONT] test mode entered (GPIO -> OUT_PP, ECAT ISRs masked)\r\n");
|
||||
}
|
||||
|
||||
static void continuityExitTestMode() {
|
||||
@@ -379,8 +434,9 @@ static void continuityExitTestMode() {
|
||||
GPIO_CTL1(GPIOD) = s_save_ctl1_d;
|
||||
GPIO_CTL0(GPIOE) = s_save_ctl0_e;
|
||||
GPIO_CTL1(GPIOE) = s_save_ctl1_e;
|
||||
EscExmcBus::maskExit(s_maskPrev);
|
||||
s_continuityActive = false;
|
||||
printf("[CONT] test mode exited (GPIO -> AF_PP restored)\r\n");
|
||||
printf("[CONT] test mode exited (GPIO -> AF_PP restored, ISRs unmasked)\r\n");
|
||||
}
|
||||
|
||||
uint8_t EscExmcBus::continuityDrive(uint8_t action) {
|
||||
@@ -432,28 +488,22 @@ uint8_t EscExmcBus::continuityDrive(uint8_t action) {
|
||||
(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");
|
||||
case 10: { /* READ HOLD: CS#+RD# 持续拉低, 数据线浮空输入
|
||||
* 用途: 万用表在 LAN9252 芯片侧引脚测量读周期数据电平。
|
||||
* 判据: 芯片 HBI 活着 => 驱动 IDX_0=0x1234 (0001 0010 0011 0100b) =>
|
||||
* 置位 bit2/4/5/9/12 => D2(Pin12)/D4(Pin49)/D5(Pin50)/D9(Pin19)/
|
||||
* D12(Pin21)=3.3V, 其余数据脚=0V(全部为确定电平)。
|
||||
* 全部中电平/漂移 => 芯片未驱动(区分"芯片不驱动"与"数据线断线":
|
||||
* GD32 侧 rd 采样无法区分两者, 芯片侧测量可以)。 */
|
||||
continuityEnterTestMode(); /* 幂等:已活跃则保持;内部已屏蔽 ECAT ISR */
|
||||
/* 数据线浮空输入(交给芯片驱动),控制线保持 OUT_PP */
|
||||
gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, 0, kDataMaskD);
|
||||
gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, 0, kDataMaskE);
|
||||
/* 持续读周期: WR 高, CS#+RD# 低, A1=A2=0 -> 读 IDX_0 */
|
||||
GPIO_BOP(GPIOD) = (1U<<kPinWr);
|
||||
GPIO_BC(GPIOD) = (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinA1) | (1U<<kPinA2);
|
||||
printf("[CONT] READ-HOLD: CS#+RD# held low, data Hi-Z\r\n");
|
||||
printf("[CONT] measure at chip: D2(P12)/D4(P49)/D5(P50)/D9(P19)/D12(P21)=3.3V & others 0V => alive(0x1234); all floating => not driving\r\n");
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
@@ -469,4 +519,38 @@ EscExmcBus::read16(uint32_t offset) {
|
||||
__attribute__((section(".ramfunc"), noinline)) void
|
||||
EscExmcBus::write16(uint32_t offset, uint16_t data) {
|
||||
ESC_EXMC_REG16(offset) = data;
|
||||
}
|
||||
|
||||
uint32_t EscExmcBus::maskEnter() {
|
||||
uint32_t prev = 0U;
|
||||
/* EXTI0=SYNC0(preempt=0) 无法被 BASEPRI 屏蔽,只能关 NVIC;
|
||||
* EXTI1=ESC IRQ / EXTI2=SYNC1 (均 preempt=1) 的 PDI_Isr/Sync1_Isr 同样做
|
||||
* HBI 两步事务访问,一并屏蔽——否则主循环事务会被 Sync1_Isr 的
|
||||
* PDO_InputMapping→HW_EscWriteIsr 打断而错址(1.5.29 修复)。 */
|
||||
if (NVIC_GetEnableIRQ(EXTI0_IRQn) != 0U) {
|
||||
prev |= 1U;
|
||||
NVIC_DisableIRQ(EXTI0_IRQn);
|
||||
}
|
||||
if (NVIC_GetEnableIRQ(EXTI1_IRQn) != 0U) {
|
||||
prev |= 2U;
|
||||
NVIC_DisableIRQ(EXTI1_IRQn);
|
||||
}
|
||||
if (NVIC_GetEnableIRQ(EXTI2_IRQn) != 0U) {
|
||||
prev |= 4U;
|
||||
NVIC_DisableIRQ(EXTI2_IRQn);
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
void EscExmcBus::maskExit(uint32_t prev) {
|
||||
/* 按保存的状态恢复:外层已屏蔽的(如 SSC DISABLE_ESC_INT)不会被误开 */
|
||||
if ((prev & 4U) != 0U) {
|
||||
NVIC_EnableIRQ(EXTI2_IRQn);
|
||||
}
|
||||
if ((prev & 2U) != 0U) {
|
||||
NVIC_EnableIRQ(EXTI1_IRQn);
|
||||
}
|
||||
if ((prev & 1U) != 0U) {
|
||||
NVIC_EnableIRQ(EXTI0_IRQn);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user