Files
DCU_APP/GD32E508_DCU_APP/sys/initial.cpp
T
cbb 7d9d9c70a1 feat(app): 1.6.39 —— 1.6.29 起未上板业务改动入库,启动链与已提交基线对齐
主体为工作区累积的业务改动(CAN/PDO/桥/SSC/task_manager 等,详目见
config.h 版本注释链),新增 CoE 0x100A 真版本上报与 0x2502 APP→BOOT
总线交接。启动链(startup/SystemInit/initial/systick/hardfault)与
HEAD 逐字节一致;1.6.35~38 的跳转诊断实验(3M 化/BKP 面包屑/
haltWithWatchdog)未提交、已整体回退,补丁存 _revert_backup_20260916/。
真机实测(配对 BOOT 1.1.0.18):刷写 warm jump 与 reset cold jump 均可。
2026-09-16 00:48:34 +08:00

352 lines
13 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};
/* CoE 0x100A「Software version」写入口,实现在 comm/ssc/src/coeappl.c(C 链接)。
* APP 侧原本一直回 SSC 占位 "1.0",主站在总线上读不到版本,只能靠 SPI PING 的
* 4 字节二进制身份块;这条总线读数是刷写后「新 APP 真起来了」的自证。 */
extern "C" void AppSetSoftwareVersion(const char *version);
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);
/* 版本号写进 CoE 0x100A:唯一真源是 sys/config.h 的 APP_FW_VERSION_*,
* 不在别处再抄一遍字符串。 */
{
char ver[20];
snprintf(ver, sizeof ver, "%u.%u.%u",
(unsigned)APP_FW_VERSION_MAJOR, (unsigned)APP_FW_VERSION_MINOR,
(unsigned)APP_FW_VERSION_PATCH);
AppSetSoftwareVersion(ver);
}
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();
}