Files
DCU_APP/GD32E508_DCU_APP/sys/initial.cpp
T
cbb 44d407390b fix(esc_lan9252): 修复LAN9252 HBI访问模型和IRQ输出使能问题
- 修正HBI访问模型:将ESC核心CSR(0x0000-0x0FFF)改为通过ECAT_CSR_CMD/DATA
  间接端口访问,解决此前直接寻址导致核心寄存器恒读0/写丢弃的问题

- 新增csrRead/csrWrite/pramRead/pramWrite接口,正确实现LAN9252访问端口操作

- 添加PDI就绪检测:等待DL_STATUS bit0置位确认EEPROM加载完成和EtherCAT核心使能

- 实现IRQ引脚输出使能:配置IRQ_CFG和INT_EN寄存器,使EtherCAT事件能到达PB1,
  解决PDI_Isr未触发导致SAFEOP->OP无限挂起的问题

- 为ESC_EXMC硬件诊断添加CONFIG_ESC_EXMC_DIAG门控,默认关闭避免影响正常运行

- 修正system_gd32e50x.c中HXTAL故障诊断的寄存器偏移和位设置错
2026-09-02 02:38:35 +08:00

337 lines
12 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 initial.cpp
* @brief 应用启动初始化:模块成功置位,结束统一报告结果。
*
* 顺序:
* 复位源采集 → NVIC → SysTick → ESP32_EN 保持 → USART0 → AppSpi(尽早 HELLO 探活)→
* ADC → CANFD → OtaBootRequest → TaskManager → Scheduler →
* EtherCAT(+Sync 可选) → Watchdog → report
*
* 1.5.34:各模块 init 从"盲配置"改为带检查(寄存器回读 / 节拍验证 / 校准超时),
* 失败不置位;复位源/模块位图/critical 失败位图经 getter 导出供 SPI 0xA3 上报。
*/
#include "sys/initial.h"
#include "device/canfd.h"
#include "device/usart0.h"
#include "device/timer.h"
#include "device/adc.h"
#include "device/watchdog.h"
#include "device/spi/app_spi_command.h"
#include "sys/systick.h"
#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;
uint8_t AppInitializer::s_initState = AppInitializer::kInitOk;
uint8_t AppInitializer::s_resetCause = 0U;
uint8_t AppInitializer::s_faultCodes[AppInitializer::kModuleCount] = {0U};
namespace {
struct InitEntry {
uint32_t bit;
const char *name;
bool critical;
};
static const InitEntry kEntries[] = {
{ AppInitializer::kNvic, "nvic", true },
{ AppInitializer::kSysTick, "systick", true },
{ AppInitializer::kEsp32Hold, "esp32_hold", true },
{ AppInitializer::kUsart0, "usart0", true },
{ AppInitializer::kAppSpi, "app_spi", true },
{ AppInitializer::kAdc, "adc", true },
{ AppInitializer::kCanFd, "canfd", true },
{ AppInitializer::kOtaBootReq, "ota_boot_req", true },
{ AppInitializer::kTaskManager, "task_manager", true },
{ AppInitializer::kScheduler, "scheduler", true },
{ AppInitializer::kEthercat, "ethercat", false },
{ AppInitializer::kEthercatSync, "ethercat_sync", false },
{ AppInitializer::kWatchdog, "watchdog", true },
};
} // namespace
void AppInitializer::mark(ModuleBit b)
{
s_mask |= static_cast<uint32_t>(b);
}
/** @brief ModuleBit(1<<N) → 数组索引 N。 */
static uint8_t moduleBitIndex(uint32_t bit)
{
uint8_t idx = 0U;
while (bit > 1U) {
bit >>= 1U;
++idx;
}
return idx;
}
void AppInitializer::setFault(ModuleBit b, uint8_t code)
{
const uint8_t idx = moduleBitIndex(static_cast<uint32_t>(b));
if (idx < kModuleCount) {
s_faultCodes[idx] = code;
}
}
uint8_t AppInitializer::faultCode(uint8_t module_index)
{
return (module_index < kModuleCount) ? s_faultCodes[module_index] : 0U;
}
uint8_t AppInitializer::faultCodes(uint8_t *out)
{
if (out == nullptr) {
return 0U;
}
for (uint8_t i = 0U; i < kModuleCount; ++i) {
out[i] = s_faultCodes[i];
}
return kModuleCount;
}
uint32_t AppInitializer::criticalFailMask()
{
uint32_t fail = 0U;
for (size_t i = 0; i < sizeof(kEntries) / sizeof(kEntries[0]); ++i) {
const InitEntry &e = kEntries[i];
if (e.critical && (s_mask & e.bit) == 0U) {
fail |= e.bit;
}
}
return fail;
}
/**
* @brief 采集 RCU_RSTSCK 复位源位图并清硬件标志。
* @note 必须在 init 最开头调用(早于任何可能读标志的模块);
* 清除不影响 BOOT 的 FWDGT 停留 OTA 判定——新的看门狗复位会由硬件重新置位。
*/
void AppInitializer::captureResetCause()
{
uint8_t cause = 0U;
if (rcu_flag_get(RCU_FLAG_PORRST) != RESET) cause |= kRstPor;
if (rcu_flag_get(RCU_FLAG_EPRST) != RESET) cause |= kRstEpr;
if (rcu_flag_get(RCU_FLAG_FWDGTRST) != RESET) cause |= kRstFwdgt;
if (rcu_flag_get(RCU_FLAG_WWDGTRST) != RESET) cause |= kRstWwdgt;
if (rcu_flag_get(RCU_FLAG_SWRST) != RESET) cause |= kRstSw;
if (rcu_flag_get(RCU_FLAG_LPRST) != RESET) cause |= kRstLp;
rcu_all_reset_flag_clear();
s_resetCause = cause;
}
void AppInitializer::report()
{
bool critical_ok = true;
bool all_ok = true;
char fail_list[160] = {0};
size_t fail_len = 0U;
for (size_t i = 0; i < sizeof(kEntries) / sizeof(kEntries[0]); ++i) {
const InitEntry &e = kEntries[i];
const bool ok = (s_mask & e.bit) != 0U;
if (ok) {
continue;
}
all_ok = false;
if (e.critical) {
critical_ok = false;
}
if (fail_len + 2U < sizeof(fail_list)) {
if (fail_len > 0U) {
fail_list[fail_len++] = ',';
}
const char *p = e.name;
while (*p != '\0' && fail_len + 1U < sizeof(fail_list)) {
fail_list[fail_len++] = *p++;
}
fail_list[fail_len] = '\0';
}
/* 故障码后缀 #XX(16 进制;canfd 高 4 位为通道号),0=成功不追加。
* 码值含义见 initial.h 注释与各模块头文件;网页按同一编码解码中文原因。 */
const uint8_t fc = s_faultCodes[moduleBitIndex(e.bit)];
if (fc != 0U && fail_len + 3U < sizeof(fail_list)) {
const char *hex = "0123456789ABCDEF";
fail_list[fail_len++] = '#';
fail_list[fail_len++] = hex[(fc >> 4) & 0xFU];
fail_list[fail_len++] = hex[fc & 0xFU];
fail_list[fail_len] = '\0';
}
}
/* 复位源随 init 报告输出一次(位图多位置位属正常,如 POR 常伴随 EPRST/SWRST) */
printf("[APP] reset_cause: 0x%02X (por=%d epr=%d fwdgt=%d wwdgt=%d sw=%d lp=%d)\r\n",
(unsigned)s_resetCause,
(s_resetCause & kRstPor) ? 1 : 0,
(s_resetCause & kRstEpr) ? 1 : 0,
(s_resetCause & kRstFwdgt) ? 1 : 0,
(s_resetCause & kRstWwdgt) ? 1 : 0,
(s_resetCause & kRstSw) ? 1 : 0,
(s_resetCause & kRstLp) ? 1 : 0);
if (critical_ok && all_ok) {
s_initState = kInitOk;
printf("[APP] init: OK (all modules)\r\n");
} else if (critical_ok) {
s_initState = kInitDegraded;
printf("[APP] init: OK (critical), skipped/failed: %s\r\n",
fail_list[0] != '\0' ? fail_list : "?");
} else {
s_initState = kInitCriticalFail;
printf("[APP] init: FAIL, modules: %s\r\n",
fail_list[0] != '\0' ? fail_list : "?");
}
/* 日志完整性自证:串口 TX 缓冲溢出会静默吞字节,1.5.40 及之前实测使 [ECAT] 诊断
* 出现 "bus eaabled"/"pdi_type=0x8Dexect" 等掉字,并两次导致 LAN9252 故障误定性。
* 1.5.41 起缓冲满时自旋腾位,仅 USART 外设异常才丢弃;非 0 即代表上面的日志不可全信。 */
const uint32_t dropped = Usart0::droppedBytes();
if (dropped != 0U) {
printf("[APP] WARN: uart dropped %u bytes, 以上日志可能不完整\r\n",
(unsigned)dropped);
}
}
void AppInitializer::init()
{
s_mask = 0U;
s_initState = kInitOk;
for (uint8_t i = 0U; i < kModuleCount; ++i) {
s_faultCodes[i] = 0U;
}
captureResetCause();
/* NVIC 优先级分组:写入后回读 AIRCR.PRIGROUP 验证(纯寄存器写,回读确认分组生效) */
nvic_priority_group_set(CONFIG_NVIC_PRIORITY_GROUP);
if ((SCB->AIRCR & (0x7UL << 8U)) == (CONFIG_NVIC_PRIORITY_GROUP & (0x7UL << 8U))) {
mark(kNvic);
} else {
setFault(kNvic, kFaultNvicPrigroupMismatch);
}
/* SysTick:内部验证节拍真实前进;失败不返回(FATAL 停机) */
if (SysTickTimer::initMs()) {
mark(kSysTick);
}
rcu_periph_clock_enable(RCU_GPIOC);
gpio_bit_set(CONFIG_ESP32_RESET_PORT, CONFIG_ESP32_RESET_PIN);
gpio_init(CONFIG_ESP32_RESET_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, CONFIG_ESP32_RESET_PIN);
/* ESP32 EN 自检:回读为 1 才算成功(被外部钳低属硬件异常,标记失败但不阻塞其余模块) */
const bool esp32_released =
(gpio_input_bit_get(CONFIG_ESP32_RESET_PORT, CONFIG_ESP32_RESET_PIN) == SET);
if (esp32_released) {
mark(kEsp32Hold);
} else {
setFault(kEsp32Hold, kFaultEsp32HoldClamped);
}
APP_LOGI("[APP] ESP32_EN PC9=%d (1=released)\r\n", esp32_released ? 1 : 0);
Usart0::configurePins();
const uint8_t usartFault = Usart0::init();
if (usartFault == Usart0::kFaultOk) {
mark(kUsart0);
} else {
setFault(kUsart0, usartFault);
}
/* SPI 尽早就绪:OTA FINISH 跳转后 ESP32 用 HELLO 探活,不能等 CAN 上电延时。
* 从机无硬件自证手段(配置即生效),探活依赖 ESP32 侧 HELLO/PING(运行期)。 */
AppSpiCommandBus.init();
mark(kAppSpi);
/* ADC:带超时校准 + VREFINT→VDDA 采样链验证 */
const uint8_t adcFault = Adc::init();
if (adcFault == Adc::kFaultOk) {
mark(kAdc);
} else {
setFault(kAdc, adcFault);
LOG_ERR("[APP] ADC init FAIL fault=%u (1=RSTCLB timeout 2=CLB timeout 3=VDDA invalid)\r\n",
(unsigned)adcFault);
}
CanFd::init();
/* L0 通过才置 canfd 位;故障码 = 原因 | (首个失败通道 << 4)。
* 无使能通道(canFault==Ok)属合法配置而非故障,照样置位。 */
uint8_t canFault = CanFd::kSelfTestOk;
uint8_t canFaultCh = 0U;
(void)CanFd::selfTestL0(&canFault, &canFaultCh);
if (canFault != CanFd::kSelfTestOk) {
setFault(kCanFd, (uint8_t)(canFault | (uint8_t)(canFaultCh << 4U)));
} else {
mark(kCanFd);
}
OtaBootRequest::init();
mark(kOtaBootReq);
TaskManager::init();
mark(kTaskManager);
/* 调度定时器:回读 + 100us 中断节拍前进验证 */
const uint8_t schedFault = SchedulerTimer::init();
if (schedFault == SchedulerTimer::kFaultOk) {
mark(kScheduler);
} else {
setFault(kScheduler, schedFault);
LOG_ERR("[APP] SchedulerTimer init FAIL fault=%u (1=CEN 2=PSC 3=CAR 4=no tick)\r\n",
(unsigned)schedFault);
}
/* ---- EtherCAT: EXMC 总线探活 → SSC 协议栈初始化 → SYNC0/IRQ 中断使能 ----
* 1.5.27:改经 EthercatSlave::init()(内部调用 EscLan9252::init 并记录
* chipId/AL 状态诊断,此前该函数无调用点、s_escReady 恒为 false)。
* 门控用 EscLan9252::isBusReady()(BYTE_TEST 通过)而非 isEscReady():
* 冷启动时 AL=INIT,SSC 必须照常启动并等待主站配置。
* 1.5.39:追加 isCoreReady() 门控——EscLan9252::init() 已等待 EEPROM(SII)
* 加载完成(DL_STATUS bit0);未加载时 EtherCAT 核心寄存器写静默丢弃,
* SSC 的 AL Event Mask/邮箱 SM 配置会全部落空(主站恒读 AL=INIT 的根因),
* 此时跳过 SSC 并以独立故障码上报。
* EEPROM 校验/烧录延迟到主循环执行,避免阻塞 init(烧录需 ~20s)。 */
EthercatSlave::init();
if (EscLan9252::isBusReady() && EscLan9252::isCoreReady()) {
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 {
setFault(kEthercat, kFaultEthercatSscFail);
printf("[ECAT] SSC MainInit FAIL (code=0x%04X)\r\n", (unsigned)sscResult);
}
} else if (EscLan9252::isBusReady()) {
setFault(kEthercat, kFaultEthercatCoreNotReady);
printf("[ECAT] EtherCAT core not ready (EEPROM not loaded), SSC skipped\r\n");
} else {
setFault(kEthercat, kFaultEthercatBusNotReady);
printf("[ECAT] EXMC bus not ready, SSC skipped\r\n");
}
/* 看门狗放最后:PSC/RLD 写入经硬件确认才置位 */
const uint8_t wdgFault = Watchdog::init();
if (wdgFault == Watchdog::kFaultOk) {
mark(kWatchdog);
} else {
setFault(kWatchdog, wdgFault);
LOG_ERR("[APP] Watchdog init FAIL fault=%u (1=PSC 2=RLD 3=pending 4=LSI not ready)\r\n",
(unsigned)wdgFault);
}
report();
}