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:
@@ -16,9 +16,8 @@
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/task_runner.h"
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/esc_eeprom.h"
|
||||
#include "comm/ethercat_sync.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "comm/ssc/port/eeprom_image.h"
|
||||
|
||||
extern "C" {
|
||||
void MainLoop(void);
|
||||
@@ -272,6 +271,68 @@ bool TaskRunner::copyAdcSample(Adc::Sample *out)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* SSC 侧 AL 诊断快照(dc_app.c): [19:16]=AL 状态 [31:24]=AL 错误码 [15:0]=AL 事件寄存器 */
|
||||
extern "C" uint32_t EcatDbg_ReadAlDiag(void);
|
||||
|
||||
/**
|
||||
* @brief 1.5.32 ECAT 心跳:主循环存活证明 + ESC AL 状态/事件寄存器快照。
|
||||
* @note 仅在同步 ISR 激活(SSC 在跑)时打印。心跳停 = 主循环卡死;
|
||||
* al=0x1 且 evt bit0 长期置位 = SSC 没处理 AL 请求;
|
||||
* al 与主站读到的不一致 = ESC 写回路径问题。
|
||||
*/
|
||||
static void ethercatAlHeartbeat()
|
||||
{
|
||||
if (!EthercatSync::isActive()) {
|
||||
return;
|
||||
}
|
||||
const uint32_t alDiag = EcatDbg_ReadAlDiag();
|
||||
printf("[ECAT][HB] al=0x%lX code=0x%02lX evt=0x%04lX (uptime=%lus)\r\n",
|
||||
(unsigned long)((alDiag >> 16) & 0x0FU),
|
||||
(unsigned long)((alDiag >> 24) & 0xFFU),
|
||||
(unsigned long)(alDiag & 0xFFFFU),
|
||||
(unsigned long)g_app_uptime_sec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 打印 EtherCAT 同步中断计数与速率(DC 通路验证)。
|
||||
* @note 主站启用 DC(SYNC0 周期 T)后,sync0 速率应稳定 ≈ 1e6/T_us 次/秒;
|
||||
* free-run 模式下 SYNC0 引脚无脉冲,三个计数器恒为 0(本函数不打印)。
|
||||
* 仅在有增量时输出,避免未接主站时刷屏。
|
||||
*/
|
||||
static void ethercatSyncCounterPrint()
|
||||
{
|
||||
static uint32_t s_prevSync0 = 0U;
|
||||
static uint32_t s_prevSync1 = 0U;
|
||||
static uint32_t s_prevEscIrq = 0U;
|
||||
static uint32_t s_prevSec = 0U;
|
||||
|
||||
const uint32_t sync0 = EthercatSync::getSync0Count();
|
||||
const uint32_t sync1 = EthercatSync::getSync1Count();
|
||||
const uint32_t escIrq = EthercatSync::getEscIrqCount();
|
||||
|
||||
const uint32_t d0 = sync0 - s_prevSync0; /* 无符号回绕自然成立 */
|
||||
const uint32_t d1 = sync1 - s_prevSync1;
|
||||
const uint32_t di = escIrq - s_prevEscIrq;
|
||||
|
||||
const uint32_t now = g_app_uptime_sec;
|
||||
const uint32_t elapsed = (now > s_prevSec) ? (now - s_prevSec) : 1U;
|
||||
|
||||
s_prevSync0 = sync0;
|
||||
s_prevSync1 = sync1;
|
||||
s_prevEscIrq = escIrq;
|
||||
s_prevSec = now;
|
||||
|
||||
if (d0 == 0U && d1 == 0U && di == 0U) {
|
||||
return; /* 未接主站或 free-run:无脉冲,静默 */
|
||||
}
|
||||
|
||||
printf("[ECAT][SYNC] sync0=%lu(+%lu, %lu/s) sync1=%lu(+%lu) escirq=%lu(+%lu) dt=%lus\r\n",
|
||||
(unsigned long)sync0, (unsigned long)d0, (unsigned long)(d0 / elapsed),
|
||||
(unsigned long)sync1, (unsigned long)d1,
|
||||
(unsigned long)escIrq, (unsigned long)di,
|
||||
(unsigned long)elapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 5s 诊断任务:默认仅状态变化/错误增量;可选全量 UART 摘要。
|
||||
* @note CONFIG_UART_TEMP_PERIODIC_PRINT=1 时打印 ADC/CAN/ECAT 全量行(调试用)。
|
||||
@@ -326,19 +387,16 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
#endif
|
||||
|
||||
if (EthercatSlave::isEscReady()) {
|
||||
uint32_t maxCyc = 0U, avgCyc = 0U, cnt = 0U;
|
||||
EthercatSlave::getIsrStats(&maxCyc, &avgCyc, &cnt);
|
||||
/* 180MHz: cycles / 180 = µs;用 *1000/180 获得亚微秒精度避免浮点 */
|
||||
const uint32_t maxUs_x1000 = maxCyc * 1000U / 180U;
|
||||
const uint32_t avgUs_x1000 = avgCyc * 1000U / 180U;
|
||||
printf("[ECAT] periodicIsr cnt=%lu max=%lu.%03luus avg=%lu.%03luus\r\n",
|
||||
(unsigned long)cnt,
|
||||
(unsigned long)(maxUs_x1000 / 1000U), (unsigned long)(maxUs_x1000 % 1000U),
|
||||
(unsigned long)(avgUs_x1000 / 1000U), (unsigned long)(avgUs_x1000 % 1000U));
|
||||
/* 1.5.27:periodicIsr 已删除(死代码,无调用点),PDO 交换由 SSC
|
||||
* Sync0_Isr → ECAT_Application → dc_app.c APPL_* 完成。 */
|
||||
printf("[ECAT] ESC ready (chip id=0x%08lX), PDO via SSC Sync0_Isr\r\n",
|
||||
(unsigned long)EthercatSlave::getChipId());
|
||||
} else {
|
||||
printf("[ECAT] ESC not detected (chip id=0x%08lX), EtherCAT disabled\r\n",
|
||||
(unsigned long)EthercatSlave::getChipId());
|
||||
}
|
||||
ethercatSyncCounterPrint();
|
||||
ethercatAlHeartbeat();
|
||||
}
|
||||
#else
|
||||
/* ---- 默认静默路径:仅错误增量 / 状态变化,无 uptime 心跳 ---- */
|
||||
@@ -351,7 +409,6 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
static uint8_t s_prev_adc_valid = 0xFFU; /* 0xFF=尚未采样 */
|
||||
static bool s_prev_esc_ready = false;
|
||||
static bool s_esc_ready_inited = false;
|
||||
static uint32_t s_prev_ecat_max_cyc = 0U;
|
||||
|
||||
const uint32_t rx_drop = diag->rx_drop_count;
|
||||
const uint32_t rx_full = diag->rx_fifo_full_count;
|
||||
@@ -403,39 +460,14 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
(unsigned long)g_app_uptime_sec);
|
||||
s_prev_esc_ready = esc_ready;
|
||||
}
|
||||
|
||||
if (esc_ready) {
|
||||
uint32_t maxCyc = 0U, avgCyc = 0U, cnt = 0U;
|
||||
EthercatSlave::getIsrStats(&maxCyc, &avgCyc, &cnt);
|
||||
/* 阈值单位 µs → CPU 周期(180 MHz) */
|
||||
const uint32_t threshCyc = CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD * 180U;
|
||||
if (maxCyc > threshCyc && maxCyc > s_prev_ecat_max_cyc) {
|
||||
const uint32_t maxUs_x1000 = maxCyc * 1000U / 180U;
|
||||
printf("[ECAT] ISR max over threshold: max=%lu.%03luus thr=%luus cnt=%lu (uptime=%lus)\r\n",
|
||||
(unsigned long)(maxUs_x1000 / 1000U),
|
||||
(unsigned long)(maxUs_x1000 % 1000U),
|
||||
(unsigned long)CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD,
|
||||
(unsigned long)cnt,
|
||||
(unsigned long)g_app_uptime_sec);
|
||||
s_prev_ecat_max_cyc = maxCyc;
|
||||
}
|
||||
}
|
||||
/* 1.5.27:periodicIsr ISR 统计随死代码一并删除 */
|
||||
/* DC SYNC0 通路验证:仅在有脉冲增量时打印(未接主站时静默)。 */
|
||||
ethercatSyncCounterPrint();
|
||||
ethercatAlHeartbeat();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行 EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 由周期 ISR(EXTI0 SYNC0)或 ESC 事件 ISR(EXTI1 IRQ)置位 TASK_ETHERCAT_PROCESS。
|
||||
*/
|
||||
void TaskRunner::taskEthercatProcess()
|
||||
{
|
||||
EthercatSlave::process();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化任务执行器运行时上下文。
|
||||
* @param[in] 无。
|
||||
@@ -468,41 +500,21 @@ void TaskRunner::runPending()
|
||||
{
|
||||
Watchdog::feed();
|
||||
|
||||
/* === HBI 周期诊断:每 20 秒重新跑一次 EXMC/HBI 探活,方便热插拔/焊接后观察 === */
|
||||
/* === HBI 周期诊断:每 20 秒重新跑一次 EXMC/HBI 探活,方便热插拔/焊接后观察 ===
|
||||
* 1.5.27:EtherCAT 同步 ISR 激活后停止(diagDump 会把 EXMC 引脚切成 GPIO,
|
||||
* 拆毁运行中的总线;诊断的 bit-bang 也与 HBI 事务互斥冲突)。 */
|
||||
static uint32_t s_lastDiagSec = 0xFFFFFFFFU;
|
||||
if (g_app_uptime_sec >= s_lastDiagSec + 20U || s_lastDiagSec == 0xFFFFFFFFU) {
|
||||
if (!EthercatSync::isActive()
|
||||
&& (g_app_uptime_sec >= s_lastDiagSec + 20U || s_lastDiagSec == 0xFFFFFFFFU)) {
|
||||
s_lastDiagSec = g_app_uptime_sec;
|
||||
printf("[ECAT][DIAG] === 20s periodic HBI probe (uptime=%lus) ===\r\n",
|
||||
(unsigned long)g_app_uptime_sec);
|
||||
EscExmcBus::diagDump();
|
||||
}
|
||||
|
||||
/* 延迟 EEPROM 烧录:init 不阻塞,主循环第一轮执行一次。
|
||||
* 校验编译期镜像与 AT24C32 内容,不匹配则全量烧录(~20s,期间看门狗正常喂)。 */
|
||||
static bool s_eepromDone = false;
|
||||
if (!s_eepromDone) {
|
||||
s_eepromDone = true;
|
||||
if (EscEeprom::init()) {
|
||||
if (EscEeprom::verifyEsi(ESI_EEPROM_IMAGE, ESI_EEPROM_SIZE)) {
|
||||
printf("[ECAT][EEPROM] content OK, skip burn\r\n");
|
||||
} else {
|
||||
printf("[ECAT][EEPROM] content mismatch, burning ESI (%u bytes)...\r\n",
|
||||
(unsigned)ESI_EEPROM_SIZE);
|
||||
if (EscEeprom::writeEsi(ESI_EEPROM_IMAGE, ESI_EEPROM_SIZE)) {
|
||||
if (EscEeprom::verifyEsi(ESI_EEPROM_IMAGE, ESI_EEPROM_SIZE)) {
|
||||
EscEeprom::reload();
|
||||
printf("[ECAT][EEPROM] burn+verify OK, reload issued\r\n");
|
||||
} else {
|
||||
printf("[ECAT][EEPROM] post-burn verify FAIL\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("[ECAT][EEPROM] burn FAIL\r\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("[ECAT][EEPROM] init FAIL (PDI control not acquired)\r\n");
|
||||
}
|
||||
}
|
||||
/* 延迟 EEPROM 烧录已删除(1.5.29):ESI 镜像改由 EtherCAT 主站侧刷写
|
||||
* (tools/ecat/ecat_flash_eeprom.py 经 ESC 0x0500 EEPROM 接口),
|
||||
* 固件不再承担烧录/校验职责。 */
|
||||
|
||||
/* poll() 含按铃超时看门狗(cancelRing),必须每轮无条件执行。
|
||||
* 主动推送已随 2026-08 请求驱动改造移除,此处仅需消费请求响应的按铃。 */
|
||||
|
||||
Reference in New Issue
Block a user