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:
@@ -115,7 +115,7 @@ constexpr bool CONFIG_CAN_CH2_ENABLE = true;
|
||||
* ============================================================ */
|
||||
constexpr uint32_t APP_FW_VERSION_MAJOR = 1U;
|
||||
constexpr uint32_t APP_FW_VERSION_MINOR = 5U;
|
||||
constexpr uint32_t APP_FW_VERSION_PATCH = 5U; /* 1.5.5:电机 OTA relay SPI 断流超时(0x26) + 0x103 心跳不再为无包在途的 kTransfer 续命 */
|
||||
constexpr uint32_t APP_FW_VERSION_PATCH = 15U; /* 1.5.15:EXMC 通断测试新增 MODE0/MODE1 strap 驱动测试(action 0x0A-0x0D, PB8/PB9) */
|
||||
constexpr uint32_t APP_FW_VERSION_BUILD = 0U;
|
||||
|
||||
|
||||
@@ -303,13 +303,16 @@ constexpr uint32_t CONFIG_ESC_PDI_OUTPUT_OFFSET = 0x1200U;
|
||||
constexpr size_t CONFIG_ETHERCAT_PDO_SIZE = 32U;
|
||||
|
||||
/* ---------- EXMC 引脚配置(GD32E508 EXMC 标准映射) ---------- */
|
||||
constexpr uint32_t CONFIG_ESC_EXMC_BASE_ADDR = 0x64000000U;
|
||||
/* Region1 = NE1 @ 0x64000000(GD32E50x 用户手册 Rev2.2 图25-3: Bank0 四 Region 各 64MB,
|
||||
* HADDR[27:26]=01 选中 NE1)。LAN9252 CS 接 PD7=EXMC_NE1,故基址为 0x64000000。 */
|
||||
#ifndef EXMC_BANK0_NORSRAM_REGION1
|
||||
#define EXMC_BANK0_NORSRAM_REGION1 ((uint32_t)0x00000001U)
|
||||
constexpr uint32_t CONFIG_ESC_EXMC_BASE_ADDR = 0x60000000U;
|
||||
/* Region0 = NE0 @ 0x60000000(GD32E50x 用户手册 Rev2.2 图25-3: Bank0 四 Region 各 64MB,
|
||||
* HADDR[27:26]=00 选中 NE0)。LAN9252 CS 接 PD7=EXMC_NE0(数据手册 GD32E508xx
|
||||
* Datasheet Rev1.8 LQFP100 引脚表: PD7 Alternate2 = EXMC_NE0/EXMC_NCE1)。
|
||||
* 注意: GD32E508Vx (LQFP100) 封装只引出 NE0 一个 NOR/SRAM 片选引脚,
|
||||
* NE1~NE3 仅在 LQFP144 (Zx) 上存在;因此必须使用 Region0(SNCTL0),不能用 Region1。 */
|
||||
#ifndef EXMC_BANK0_NORSRAM_REGION0
|
||||
#define EXMC_BANK0_NORSRAM_REGION0 ((uint32_t)0x00000000U)
|
||||
#endif
|
||||
#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 EXMC_BANK0_NORSRAM_REGION1
|
||||
#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0 EXMC_BANK0_NORSRAM_REGION0
|
||||
|
||||
/* ---------- ESC 中断引脚(PB0/PB1/PB2,三个独立 EXTI 向量) ---------- */
|
||||
/** @note 保留 PA0 给未来 STANDBY 模式唤醒(WKUP_PIN0)。 */
|
||||
|
||||
@@ -20,9 +20,15 @@
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/config.h"
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/ethercat_sync.h"
|
||||
|
||||
extern "C" {
|
||||
uint16_t MainInit(void);
|
||||
void MainLoop(void);
|
||||
}
|
||||
|
||||
uint32_t AppInitializer::s_mask = 0U;
|
||||
|
||||
namespace {
|
||||
@@ -137,11 +143,20 @@ void AppInitializer::init()
|
||||
SchedulerTimer::init();
|
||||
mark(kScheduler);
|
||||
|
||||
EthercatSlave::init();
|
||||
if (EthercatSlave::isEscReady()) {
|
||||
mark(kEthercat);
|
||||
EthercatSync::init();
|
||||
mark(kEthercatSync);
|
||||
/* ---- EtherCAT: EXMC 总线探活 → SSC 协议栈初始化 → SYNC0/IRQ 中断使能 ----
|
||||
* EEPROM 校验/烧录延迟到主循环执行,避免阻塞 init(烧录需 ~20s)。 */
|
||||
if (EscLan9252::init()) {
|
||||
const uint16_t sscResult = MainInit();
|
||||
if (sscResult == 0U) {
|
||||
mark(kEthercat);
|
||||
EthercatSync::init();
|
||||
mark(kEthercatSync);
|
||||
printf("[ECAT] SSC MainInit OK, sync ISR enabled\r\n");
|
||||
} else {
|
||||
printf("[ECAT] SSC MainInit FAIL (code=0x%04X)\r\n", (unsigned)sscResult);
|
||||
}
|
||||
} else {
|
||||
printf("[ECAT] EXMC bus not ready, SSC skipped\r\n");
|
||||
}
|
||||
|
||||
Watchdog::init();
|
||||
|
||||
@@ -32,8 +32,6 @@ void TaskManager::init()
|
||||
g_scheduler.div_can_rx = 0U;
|
||||
g_scheduler.div_can_tx = 0U;
|
||||
g_scheduler.div_heartbeat = 0U;
|
||||
g_scheduler.div_motor_state = 0U;
|
||||
g_scheduler.motor_state_period_ticks = CONFIG_TASK_PERIOD_MOTOR_STATE_TICKS;
|
||||
g_scheduler.div_diag_print = 0U;
|
||||
}
|
||||
|
||||
@@ -107,13 +105,6 @@ void TaskManager::tick100us()
|
||||
set(1UL << TASK_HEARTBEAT);
|
||||
}
|
||||
|
||||
/* 多电机状态 0xC5 推送:250ms 独立节拍。
|
||||
* 不挂在 1Hz heartbeat 上,避免连带加速 OTA Boot 轮询/CAN快照/ADC推送/checkOffline。 */
|
||||
if(++g_scheduler.div_motor_state >= g_scheduler.motor_state_period_ticks) {
|
||||
g_scheduler.div_motor_state = 0U;
|
||||
set(1UL << TASK_MOTOR_STATE_PUSH);
|
||||
}
|
||||
|
||||
/* 始终 5s 调度诊断任务:默认静默检查错误增量,PERIODIC_PRINT=1 时打全量摘要 */
|
||||
if(++g_scheduler.div_diag_print >= CONFIG_TASK_PERIOD_DIAG_PRINT_TICKS) {
|
||||
g_scheduler.div_diag_print = 0U;
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/task_runner.h"
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/esc_eeprom.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "comm/ssc/port/eeprom_image.h"
|
||||
|
||||
extern "C" {
|
||||
void MainLoop(void);
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t kAdcFilterWindow = CONFIG_ADC_FILTER_SIZE + 1U;
|
||||
@@ -240,33 +247,17 @@ void TaskRunner::taskCanRxParse()
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 推进 OTA Boot 请求、刷新 CAN 反馈快照、启动 CAN/ADC 1Hz 推送轮次。
|
||||
* @note 推进 OTA Boot 请求、刷新 CAN 反馈快照、启动 ADC 1Hz 推送轮次。
|
||||
*/
|
||||
void TaskRunner::taskHeartbeat()
|
||||
{
|
||||
OtaBootRequest::process();
|
||||
CanFd::refreshFeedbackSnapshot();
|
||||
canFeedbackStartRound();
|
||||
/* ADC 调试反馈 1Hz:CONFIG_SPI_ADC_FEEDBACK_ENABLE=0 时为空操作 */
|
||||
adcFeedbackStartRound();
|
||||
#if (CONFIG_MOTOR_STATE_ENABLE != 0)
|
||||
MotorState::checkOffline();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动多电机状态 0xC5 推送轮次(250ms 独立节拍)。
|
||||
* @note 与 taskHeartbeat 解耦:不连带加速 OTA Boot 轮询 / CAN 快照 / ADC 推送 /
|
||||
* checkOffline 离线判定(3s 阈值)。仅触发本轮 P0/P1 分页推送,
|
||||
* 实际 SPI 传输由 motorStatePushStep() 在主循环按铃协议推进。
|
||||
*/
|
||||
void TaskRunner::taskMotorStatePush()
|
||||
{
|
||||
#if (CONFIG_MOTOR_STATE_ENABLE != 0)
|
||||
motorStateStartRound();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拷贝当前 ADC 工程量快照(供 SPI 0xC4 等只读路径)。
|
||||
* @param[out] out 非空时写入最新 Sample
|
||||
@@ -477,8 +468,44 @@ void TaskRunner::runPending()
|
||||
{
|
||||
Watchdog::feed();
|
||||
|
||||
/* === HBI 周期诊断:每 20 秒重新跑一次 EXMC/HBI 探活,方便热插拔/焊接后观察 === */
|
||||
static uint32_t s_lastDiagSec = 0xFFFFFFFFU;
|
||||
if (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");
|
||||
}
|
||||
}
|
||||
|
||||
/* poll() 含按铃超时看门狗(cancelRing),必须每轮无条件执行。
|
||||
* 放在 canFeedbackPushStep() 前确保卡死的按铃先被清掉再推送下一帧。 */
|
||||
* 主动推送已随 2026-08 请求驱动改造移除,此处仅需消费请求响应的按铃。 */
|
||||
AppSpiCommandBus.poll();
|
||||
|
||||
/* 0xB0 进 BOOT:必须每圈检查,不能只靠 1Hz TASK_HEARTBEAT。
|
||||
@@ -494,26 +521,15 @@ void TaskRunner::runPending()
|
||||
* (替代原靠 tick100us 计数 + TASK_MOTOR_OTA_TX 的方式,消除计数器竞争)。 */
|
||||
MotorOtaRelay::onTxTick();
|
||||
|
||||
/* CAN 反馈主动推送:GD32 APP 循环推送 3 通道 × 2 页反馈帧到 ESP32,
|
||||
* 替代 ESP32 主动轮询。非阻塞:上一帧未读走时立即返回。
|
||||
* 每次主循环迭代调用一次,ESP32 读走一帧后 CS 释放中断唤醒主循环继续推送。 */
|
||||
canFeedbackPushStep();
|
||||
/* 多电机状态(0xC5):优先级介于 CAN 反馈与 ADC 之间;ring 忙则跳过。 */
|
||||
motorStatePushStep();
|
||||
/* ADC 调试反馈(0xC4):优先级低于 CAN;ring 忙则跳过。可 config 关闭。 */
|
||||
adcFeedbackPushStep();
|
||||
/* 多电机版本(0xC6):事件驱动、低频,优先级最低;无 dirty 版本时立即返回。 */
|
||||
/* 2026-08 请求驱动改造:0xC1 CAN 反馈 / 0xC5 电机状态 / 0xC4 ADC 已改为
|
||||
* ESP32 周期查询(主动按铃推送全部移除,避免 OTA 期间悬空按铃刷屏)。
|
||||
* 仅保留事件驱动、低频的 0xC6 电机版本推送。 */
|
||||
motorVersionPushStep();
|
||||
|
||||
/* 周期路径( SYNC0 ISR preempt=0 )用 s_periodicSeq 序列号通知主循环,
|
||||
* 不再走 task_flags,避免 BASEPRI 临界区与 preempt=0 的 RMW 竞态。
|
||||
* 优先于 takePending() 处理,确保周期数据及时消费。
|
||||
* ESC 未焊接时 selfTest 失败,isEscReady()=false,中断未使能,
|
||||
* s_periodicSeq 不会递增,此分支自然不触发——双重守卫仅为防御性编程。 */
|
||||
if (EthercatSlave::isEscReady() && EthercatSlave::hasPendingPeriodic()) {
|
||||
EthercatSlave::markPeriodicProcessed();
|
||||
taskEthercatProcess();
|
||||
}
|
||||
/* SSC 协议栈主循环:AL 状态机 + 邮箱处理 + FreeRun PDO。
|
||||
* 每轮无条件调用,SSC 内部自行判断是否有待处理事件。
|
||||
* DC 模式下 PDO 交换由 SYNC0 ISR (Sync0_Isr) 完成,此处仅处理低优先级逻辑。 */
|
||||
MainLoop();
|
||||
|
||||
const uint32_t pending = TaskManager::takePending();
|
||||
if(pending == 0U) return;
|
||||
@@ -524,7 +540,5 @@ void TaskRunner::runPending()
|
||||
if(pending & (1UL << TASK_CAN_RX_PARSE)) taskCanRxParse();
|
||||
if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend();
|
||||
if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat();
|
||||
if(pending & (1UL << TASK_MOTOR_STATE_PUSH)) taskMotorStatePush();
|
||||
if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint();
|
||||
if(pending & (1UL << TASK_ETHERCAT_PROCESS)) taskEthercatProcess();
|
||||
}
|
||||
|
||||
@@ -50,8 +50,6 @@ private:
|
||||
static void taskCanTxSend();
|
||||
/** @brief 心跳维护任务。 */
|
||||
static void taskHeartbeat();
|
||||
/** @brief 多电机状态 0xC5 主动推送启动(250ms 独立节拍)。 */
|
||||
static void taskMotorStatePush();
|
||||
/** @brief 诊断打印任务。 */
|
||||
static void taskDiagnosticPrint();
|
||||
/** @brief EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。 */
|
||||
|
||||
Reference in New Issue
Block a user