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故障诊断的寄存器偏移和位设置错
This commit is contained in:
@@ -65,14 +65,51 @@ void Adc::initDma()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 ADC、DMA 与规则通道扫描配置。
|
||||
* @brief 带超时等待 ADC_CTL1 指定位清零。
|
||||
* @param[in] bit ADC_CTL1 位(RSTCLB/CLB)。
|
||||
* @param[in] timeout_ms 超时毫秒数。
|
||||
* @retval bool true=位已清零;false=超时(硬件未响应)。
|
||||
* @note 替代库函数 adc_calibration_enable 内部的无超时死等——
|
||||
* ADC 硬件异常时库函数会永久挂住 init,且无任何诊断。
|
||||
*/
|
||||
static bool waitAdcCtl1BitClear(uint32_t bit, uint32_t timeout_ms)
|
||||
{
|
||||
const uint32_t start = SysTickTimer::getMs();
|
||||
while ((ADC_CTL1(CONFIG_ADC_INSTANCE) & bit) != 0U) {
|
||||
if ((SysTickTimer::getMs() - start) >= timeout_ms) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带超时执行 ADC 校准(RSTCLB → CLB 两段各等清零)。
|
||||
* @retval uint8_t Adc::InitFault 故障码(0=校准完成;1=RSTCLB 超时;2=CLB 超时)。
|
||||
*/
|
||||
static uint8_t runCalibrationWithTimeout()
|
||||
{
|
||||
constexpr uint32_t kCalTimeoutMs = 10U; /* 校准实际仅数十 µs,10ms 裕量极大 */
|
||||
ADC_CTL1(CONFIG_ADC_INSTANCE) |= ADC_CTL1_RSTCLB;
|
||||
if (!waitAdcCtl1BitClear(ADC_CTL1_RSTCLB, kCalTimeoutMs)) {
|
||||
return Adc::kFaultCalRstTimeout;
|
||||
}
|
||||
ADC_CTL1(CONFIG_ADC_INSTANCE) |= ADC_CTL1_CLB;
|
||||
if (!waitAdcCtl1BitClear(ADC_CTL1_CLB, kCalTimeoutMs)) {
|
||||
return Adc::kFaultCalTimeout;
|
||||
}
|
||||
return Adc::kFaultOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 ADC、DMA 与规则通道扫描配置,并验证采样链有效。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @retval uint8_t Adc::InitFault 故障码(0=校准完成且 VREFINT→VDDA 换算在合理区间)。
|
||||
* @note 按 `VIN_ADC / TEMP / VREFINT` 三通道顺序启用连续扫描与 DMA 采样。
|
||||
* 参考 GD32E508 文档要求,内部温度与 VREFINT 通道使用长采样时间,以满足 ts_temp 要求。
|
||||
*/
|
||||
void Adc::init()
|
||||
uint8_t Adc::init()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_ADC0);
|
||||
rcu_periph_clock_enable(RCU_GPIOA);
|
||||
@@ -103,10 +140,17 @@ void Adc::init()
|
||||
|
||||
adc_enable(CONFIG_ADC_INSTANCE);
|
||||
SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS);
|
||||
adc_calibration_enable(CONFIG_ADC_INSTANCE);
|
||||
SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS);
|
||||
const uint8_t calFault = runCalibrationWithTimeout();
|
||||
if (calFault != kFaultOk) {
|
||||
return calFault;
|
||||
}
|
||||
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
|
||||
SysTickTimer::delayMs(1U);
|
||||
SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS);
|
||||
|
||||
/* 验证采样链:DMA 循环搬运在跑且 VREFINT 换算 VDDA 落在合理区间。
|
||||
* calculateVdda 对非法 raw/超范围返回 0.0f。 */
|
||||
const float vdda = calculateVdda(readVrefRaw());
|
||||
return ((vdda >= kMinValidVdda) && (vdda <= kMaxValidVdda)) ? kFaultOk : kFaultVddaInvalid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -399,19 +399,24 @@ void CanFd::initChannelHardware(uint8_t i)
|
||||
const uint8_t rxLevelAfter = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
|
||||
const uint32_t tecAfterExit = (errAfterExit >> 16) & 0xFFU;
|
||||
const uint32_t recAfterExit = (errAfterExit >> 24) & 0xFFU;
|
||||
CAN_LOGI("[CAN-DIAG] CH%u init_nom=%u init_fd=%u exit_ok=%u wait=%lu rx_before=%u rx_after=%u stb=%u STAT=0x%08lX ERR=0x%08lX TEC=%lu REC=%lu\r\n",
|
||||
static_cast<unsigned>(i),
|
||||
nominalOk ? 1U : 0U,
|
||||
fdOk ? 1U : 0U,
|
||||
((statAfterExit & CAN_STAT_IWS) == 0U) ? 1U : 0U,
|
||||
static_cast<unsigned long>(exitWait),
|
||||
static_cast<unsigned>(rxLevelBefore),
|
||||
static_cast<unsigned>(rxLevelAfter),
|
||||
static_cast<unsigned>(stbLevel),
|
||||
static_cast<unsigned long>(statAfterExit),
|
||||
static_cast<unsigned long>(errAfterExit),
|
||||
static_cast<unsigned long>(tecAfterExit),
|
||||
static_cast<unsigned long>(recAfterExit));
|
||||
/* 正常上电静默:只在本端控制器初始化失败(can_init/can_fd_init 失败或卡 init mode)
|
||||
* 时打印一行诊断。TEC/REC 属总线侧状态,未接从站时浮空噪声会累积,不作为打印条件。 */
|
||||
const bool exitedInitMode = ((statAfterExit & CAN_STAT_IWS) == 0U);
|
||||
if (!nominalOk || !fdOk || !exitedInitMode) {
|
||||
LOG_ERR("[CAN-DIAG] CH%u init_nom=%u init_fd=%u exit_ok=%u wait=%lu rx_before=%u rx_after=%u stb=%u STAT=0x%08lX ERR=0x%08lX TEC=%lu REC=%lu\r\n",
|
||||
static_cast<unsigned>(i),
|
||||
nominalOk ? 1U : 0U,
|
||||
fdOk ? 1U : 0U,
|
||||
exitedInitMode ? 1U : 0U,
|
||||
static_cast<unsigned long>(exitWait),
|
||||
static_cast<unsigned>(rxLevelBefore),
|
||||
static_cast<unsigned>(rxLevelAfter),
|
||||
static_cast<unsigned>(stbLevel),
|
||||
static_cast<unsigned long>(statAfterExit),
|
||||
static_cast<unsigned long>(errAfterExit),
|
||||
static_cast<unsigned long>(tecAfterExit),
|
||||
static_cast<unsigned long>(recAfterExit));
|
||||
}
|
||||
|
||||
CAN_TSTAT(ch.canPeriph) = CAN_TSTAT_MTF0 | CAN_TSTAT_MTF1 | CAN_TSTAT_MTF2;
|
||||
can_interrupt_enable(ch.canPeriph, CAN_INT_TME | CAN_INT_RFNE0 | CAN_INT_RFF0 | CAN_INT_RFO0);
|
||||
@@ -782,13 +787,16 @@ void CanFd::init()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief L0 轻量自检:无总线发帧,仅校验使能通道硬件与收发器状态。
|
||||
* @brief L0 轻量自检:无总线发帧,仅校验使能通道控制器自身状态(GD32 侧故障)。
|
||||
* @note 1.5.35:不再检查 BUS_OFF/TEC/REC——未接从站的通道 RX 浮空噪声会累积 REC,
|
||||
* 「没接设备」不等于错误;总线侧健康由 0xC1 反馈帧运行期上报。
|
||||
*/
|
||||
bool CanFd::selfTestL0()
|
||||
bool CanFd::selfTestL0(uint8_t *fault_code, uint8_t *fault_channel)
|
||||
{
|
||||
constexpr uint32_t kTecRecLimit = 16U;
|
||||
bool anyEnabled = false;
|
||||
bool allOk = true;
|
||||
uint8_t firstCode = kSelfTestOk;
|
||||
uint8_t firstCh = 0U;
|
||||
|
||||
for (uint8_t i = 0U; i < static_cast<uint8_t>(kChannelCount); ++i) {
|
||||
if (!kChannelEnabled[i]) {
|
||||
@@ -796,38 +804,37 @@ bool CanFd::selfTestL0()
|
||||
}
|
||||
anyEnabled = true;
|
||||
const auto &ch = kChannels[i];
|
||||
uint8_t code = kSelfTestOk;
|
||||
|
||||
if (!s_channelHwOk[i]) {
|
||||
allOk = false;
|
||||
continue;
|
||||
code = kSelfTestHwInit;
|
||||
} else if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) != 0U) {
|
||||
/* 仍停在初始化模式 */
|
||||
code = kSelfTestStuckInit;
|
||||
} else if (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) {
|
||||
/* STB=0 为 Normal;输出寄存器回读确认配置生效 */
|
||||
code = kSelfTestStbLevel;
|
||||
}
|
||||
|
||||
/* 仍停在初始化模式 */
|
||||
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) != 0U) {
|
||||
if (code != kSelfTestOk) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* STB=0 为 Normal;高电平视为仍待机 */
|
||||
if (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t errReg = CAN_ERR(ch.canPeriph);
|
||||
if ((errReg & CAN_ERR_BOERR) != 0U) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t tec = (errReg >> 16) & 0xFFU;
|
||||
const uint32_t rec = (errReg >> 24) & 0xFFU;
|
||||
if (tec > kTecRecLimit || rec > kTecRecLimit) {
|
||||
allOk = false;
|
||||
continue;
|
||||
if (firstCode == kSelfTestOk) {
|
||||
firstCode = code;
|
||||
firstCh = i;
|
||||
}
|
||||
LOG_ERR("[CAN] L0 selftest CH%u FAIL code=%u (STAT=0x%08lX ERR=0x%08lX)\r\n",
|
||||
static_cast<unsigned>(i), static_cast<unsigned>(code),
|
||||
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)),
|
||||
static_cast<unsigned long>(CAN_ERR(ch.canPeriph)));
|
||||
}
|
||||
}
|
||||
|
||||
if (fault_code != nullptr) {
|
||||
*fault_code = firstCode;
|
||||
}
|
||||
if (fault_channel != nullptr) {
|
||||
*fault_channel = firstCh;
|
||||
}
|
||||
return anyEnabled && allOk;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "main.h"
|
||||
#include "device/timer.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/systick.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
/**
|
||||
@@ -48,16 +49,38 @@ void SchedulerTimer::configureNvic()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化用户调度定时器模块。
|
||||
* @brief 初始化用户调度定时器模块并验证节拍运行。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 依次完成定时器本体配置与 NVIC 中断配置。
|
||||
* @retval uint8_t SchedulerTimer::InitFault 故障码(0=回读一致且 tick_100us 已前进)。
|
||||
* @note 依次完成定时器本体配置与 NVIC 中断配置;
|
||||
* 验证覆盖"中断向量/优先级配错导致调度器静默停摆"这类纯写配置发现不了的故障。
|
||||
*/
|
||||
void SchedulerTimer::init()
|
||||
uint8_t SchedulerTimer::init()
|
||||
{
|
||||
configureTimer();
|
||||
configureNvic();
|
||||
|
||||
/* 回读:计数器已使能、PSC/CAR 与配置一致 */
|
||||
if ((TIMER_CTL0(CONFIG_SCHED_TIMER_INSTANCE) & TIMER_CTL0_CEN) == 0U) {
|
||||
return kFaultNotEnabled;
|
||||
}
|
||||
if (TIMER_PSC(CONFIG_SCHED_TIMER_INSTANCE) != CONFIG_SCHED_TIMER_PRESCALER) {
|
||||
return kFaultPscMismatch;
|
||||
}
|
||||
if (TIMER_CAR(CONFIG_SCHED_TIMER_INSTANCE) != CONFIG_SCHED_TIMER_PERIOD) {
|
||||
return kFaultCarMismatch;
|
||||
}
|
||||
|
||||
/* 有界等待 100us 中断节拍前进(50ms 预算 = 500 个 tick) */
|
||||
const uint32_t start = g_scheduler.tick_100us;
|
||||
const uint32_t deadline = SysTickTimer::getMs() + 50U;
|
||||
while (g_scheduler.tick_100us == start) {
|
||||
if ((int32_t)(SysTickTimer::getMs() - deadline) >= 0) {
|
||||
return kFaultNoTick;
|
||||
}
|
||||
}
|
||||
return kFaultOk;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,14 +7,23 @@
|
||||
*/
|
||||
class SchedulerTimer {
|
||||
public:
|
||||
/** init 故障码(0=成功;经 0xA3 INIT_STATUS fault_codes[] 上报) */
|
||||
enum InitFault : uint8_t {
|
||||
kFaultOk = 0U, /**< 成功 */
|
||||
kFaultNotEnabled = 1U, /**< CTL0.CEN 未置位(定时器未使能) */
|
||||
kFaultPscMismatch = 2U, /**< PSC 回读与配置不一致 */
|
||||
kFaultCarMismatch = 3U, /**< CAR 回读与配置不一致 */
|
||||
kFaultNoTick = 4U, /**< 100us 中断节拍未前进(NVIC/向量/优先级异常) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 初始化用户调度定时器及其中断。
|
||||
* @brief 初始化用户调度定时器及其中断,并验证节拍真实运行。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @retval uint8_t InitFault 故障码(0=寄存器回读一致且 tick_100us 中断节拍已前进)。
|
||||
* @note 当前用于建立 100us 基准节拍,驱动 TaskManager::tick100us()。
|
||||
*/
|
||||
static void init();
|
||||
static uint8_t init();
|
||||
|
||||
/**
|
||||
* @brief TIMER2 中断处理入口。
|
||||
|
||||
@@ -23,14 +23,22 @@ public:
|
||||
uint8_t valid; /**< 采样是否有效:1=有效,0=存在原始值/换算异常。 */
|
||||
};
|
||||
|
||||
/** init 故障码(0=成功;经 0xA3 INIT_STATUS fault_codes[] 上报,网页解码文字见 initial.h 注释) */
|
||||
enum InitFault : uint8_t {
|
||||
kFaultOk = 0U, /**< 成功 */
|
||||
kFaultCalRstTimeout = 1U, /**< 校准复位(RSTCLB)等待超时,ADC 硬件无响应 */
|
||||
kFaultCalTimeout = 2U, /**< 校准(CLB)等待超时 */
|
||||
kFaultVddaInvalid = 3U, /**< 校准后 VREFINT→VDDA 采样链无效(DMA/通道/供电异常) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 初始化 ADC 硬件、扫描通道与 DMA 循环缓存。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @retval uint8_t InitFault 故障码(0=校准完成且 VDDA 采样链有效)。
|
||||
* @note 完成 ADC 三通道扫描、DMA 循环搬运、PA6(VIN_ADC) 模拟输入配置以及内部通道校准。
|
||||
*/
|
||||
static void init();
|
||||
static uint8_t init();
|
||||
|
||||
/**
|
||||
* @brief 读取指定通道在 DMA 缓存中的原始 ADC 值。
|
||||
|
||||
@@ -118,12 +118,25 @@ public:
|
||||
|
||||
static void init();
|
||||
|
||||
/** L0 自检故障原因码(selfTestL0 输出;经 0xA3 fault_codes[] 上报,高 3 位带通道号见 initial.h) */
|
||||
enum SelfTestFault : uint8_t {
|
||||
kSelfTestOk = 0U, /**< 通过 */
|
||||
kSelfTestHwInit = 1U, /**< 控制器初始化失败(重试 5 次后仍不成功) */
|
||||
kSelfTestStuckInit = 2U, /**< 卡在初始化模式(IWS 未清零) */
|
||||
kSelfTestStbLevel = 3U, /**< 收发器 STB 输出未拉低(配置未生效) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief L0 轻量自检(不向物理总线发帧)。
|
||||
* @brief L0 轻量自检(不向物理总线发帧),仅校验 GD32 侧控制器自身状态。
|
||||
* @param[out] fault_code 首个失败通道的 SelfTestFault 原因码;全部通过时为 kSelfTestOk。允许空。
|
||||
* @param[out] fault_channel 首个失败通道号 0/1/2;全部通过时无意义。允许空。
|
||||
* @retval true 所有已使能通道通过;false 无使能通道或任一路失败。
|
||||
* @note 检查:硬件 init 结果、退出 init mode、收发器 Normal、无 BUS_OFF、TEC/REC 阈值。
|
||||
* @note 检查:硬件 init 结果、退出 init mode、收发器 STB 已置 Normal。
|
||||
* 1.5.35 移除 BUS_OFF 与 TEC/REC 检查:它们是总线侧状态,
|
||||
* 通道未接从站(RX 浮空噪声)时会误报——「没接设备」不等于错误;
|
||||
* 总线健康经 0xC1 反馈帧(bus_state/TEC/REC)运行期上报,网页 CAN 页可见。
|
||||
*/
|
||||
static bool selfTestL0();
|
||||
static bool selfTestL0(uint8_t *fault_code = nullptr, uint8_t *fault_channel = nullptr);
|
||||
|
||||
/**
|
||||
* @brief 查询指定通道是否已使能。
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file app_spi_cmd_init_status.cpp
|
||||
* @brief SPI 0xA3 INIT_STATUS 命令:向 ESP32 上报 APP 初始化状态(1.5.34 新增,1.5.35 扩展故障码)。
|
||||
*
|
||||
* 响应 ACK 25B 布局(与 app_spi_command_defs.h kTypeInitStatus 注释一致):
|
||||
* [0..3] module_mask LE AppInitializer::ModuleBit 成功位图
|
||||
* [4..7] critical_fail_mask LE 失败的 critical 模块位图(0=无 critical 失败)
|
||||
* [8] reset_cause ResetCauseBit 复位源位图
|
||||
* [9] init_state 0=OK 1=degraded 2=critical_fail
|
||||
* [10..11] reserved (0)
|
||||
* [12..24] fault_codes[13] 每模块 1B 故障码(0=成功;编码见 sys/initial.h,
|
||||
* canfd 高 4 位为首个失败通道号)
|
||||
* 1.5.34 只回前 12B;ESP32 按 resp_len≥25 判定故障码数组是否存在,前后兼容。
|
||||
*/
|
||||
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "sys/initial.h"
|
||||
|
||||
namespace {
|
||||
|
||||
/** @brief 0xA3 响应 payload 长度(12B 基础 + 13B 故障码)。 */
|
||||
static constexpr size_t kInitStatusLen = 12U + 13U;
|
||||
|
||||
/** @brief SPI 0xA3 INIT_STATUS 处理器:应答初始化状态 25B。 */
|
||||
class SpiInitStatusHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
/** @brief 是否处理该帧类型。 */
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeInitStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 INIT_STATUS:填充初始化状态与模块故障码并回 0x80。
|
||||
* @param bus 命令总线
|
||||
* @param ctx 请求上下文
|
||||
*/
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
uint8_t payload[kInitStatusLen] = {0U};
|
||||
writeU32Le(&payload[0], AppInitializer::moduleMask());
|
||||
writeU32Le(&payload[4], AppInitializer::criticalFailMask());
|
||||
payload[8] = AppInitializer::resetCause();
|
||||
payload[9] = AppInitializer::initState();
|
||||
AppInitializer::faultCodes(&payload[12]);
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, payload, sizeof(payload));
|
||||
}
|
||||
};
|
||||
|
||||
static SpiInitStatusHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
/** @brief 注册 0xA3 INIT_STATUS 命令处理器。 */
|
||||
void registerSpiInitStatusCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/initial.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/systick.h"
|
||||
#include "main.h"
|
||||
@@ -68,7 +69,10 @@ void fillGd32AppIdentityPayload(uint8_t *out)
|
||||
|
||||
out[12] = V2_FEAT_MAP_VER;
|
||||
writeU16Le(&out[13], buildAppFeatureFlags());
|
||||
/* [15] reserved = 0 */
|
||||
/* [15] init_state(1.5.34 起):0=OK 1=degraded 2=critical_fail。
|
||||
* 复用原 reserved 字节——旧 ESP32 忽略该字节、旧 GD32 恒 0(恰与 OK 同义),
|
||||
* 因此不 bump feat_map_ver 即前后兼容;详情经 0xA3 INIT_STATUS 查询。 */
|
||||
out[15] = AppInitializer::initState();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -382,6 +382,7 @@ void AppSpiCommand::configureExti()
|
||||
extern void registerSpiBootRequestCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiOtaWaitCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiAppPingCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiInitStatusCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiCanBridgeCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiCanBurstCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiCanFeedbackCommand(AppSpiCommand &bus);
|
||||
@@ -407,6 +408,7 @@ void AppSpiCommand::init()
|
||||
registerSpiBootRequestCommand(AppSpiCommandBus);
|
||||
registerSpiOtaWaitCommand(AppSpiCommandBus);
|
||||
registerSpiAppPingCommand(AppSpiCommandBus);
|
||||
registerSpiInitStatusCommand(AppSpiCommandBus);
|
||||
registerSpiCanBridgeCommand(AppSpiCommandBus);
|
||||
registerSpiCanBurstCommand(AppSpiCommandBus);
|
||||
registerSpiCanFeedbackCommand(AppSpiCommandBus);
|
||||
|
||||
@@ -23,6 +23,15 @@ static constexpr uint8_t kTypeAppPing = 0xA1U;
|
||||
* 0=stop(恢复AF_PP), 1-4=数据pattern(0xFFFF/0x0000/0xAAAA/0x5555),
|
||||
* 5=CS低, 6=RD低, 7=WR低, 8=A1高, 9=A2高。用于 LAN9252 通断测试。 */
|
||||
static constexpr uint8_t kTypeExmcDiag = 0xA2U;
|
||||
/** @brief APP 初始化状态查询(ESP32→GD32,1.5.34 新增):无 payload。
|
||||
* 响应 ACK 25B(1.5.35 起;1.5.34 仅前 12B):
|
||||
* [0..3] module_mask LE(AppInitializer::ModuleBit 成功位图)
|
||||
* [4..7] critical_fail_mask LE(失败的 critical 模块位图,0=无)
|
||||
* [8] reset_cause(ResetCauseBit 位图) [9] init_state(0=OK 1=degraded 2=critical_fail)
|
||||
* [10..11] reserved
|
||||
* [12..24] fault_codes[13](每模块 1B 故障码,0=成功;编码见 sys/initial.h,
|
||||
* canfd = 原因码|(首个失败通道<<4))。位定义与 sys/initial.h 一致。 */
|
||||
static constexpr uint8_t kTypeInitStatus = 0xA3U;
|
||||
static constexpr uint8_t kTypeBootReq = 0xB0U;
|
||||
static constexpr uint8_t kTypeCanSend = 0xC0U;
|
||||
static constexpr uint8_t kTypeCanFeedback = 0xC1U; /**< ESP32 -> GD32 轮询 CAN 反馈快照 */
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
uint8_t Usart0::s_txBuf[Usart0::kTxBufSize];
|
||||
volatile uint16_t Usart0::s_txHead = 0U;
|
||||
volatile uint16_t Usart0::s_txTail = 0U;
|
||||
volatile uint32_t Usart0::s_txDropped = 0U;
|
||||
|
||||
/**
|
||||
* @brief 计算发送环形缓冲区中的下一个索引。
|
||||
@@ -35,19 +36,54 @@ bool Usart0::isBufferEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 以非阻塞方式向 USART0 发送缓冲写入一个字符。
|
||||
* @brief 在临界区内直接轮询 TBE 发出一个字节,腾出一个缓冲槽位。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval bool true 表示成功发出一字节(s_txTail 已前进),false 表示等待 TBE 超时。
|
||||
* @note 调用者必须已持有临界区:USART0 中断为 preempt=3,已被 BASEPRI 屏蔽,
|
||||
* 故此处独占 s_txTail,不会与 onInterrupt() 取到同一字节。
|
||||
* 走轮询而非等中断,因此在中断上下文里调用 printf 也不会死锁。
|
||||
*/
|
||||
bool Usart0::drainOneByteLocked()
|
||||
{
|
||||
if(isBufferEmpty()) {
|
||||
return false;
|
||||
}
|
||||
uint32_t spin = kTbeSpinMax;
|
||||
while(usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {
|
||||
if(--spin == 0U) {
|
||||
return false; /* USART 未在发送(外设异常/时钟丢失),交由调用者计丢弃 */
|
||||
}
|
||||
}
|
||||
usart_data_transmit(USART0, s_txBuf[s_txTail]);
|
||||
s_txTail = nextIndex(s_txTail);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向 USART0 发送缓冲写入一个字符,缓冲满时自旋腾位而非丢弃。
|
||||
* @param[in] ch 待发送字符。
|
||||
* @param[out] 无。
|
||||
* @retval bool true 表示成功写入发送缓冲,false 表示缓冲已满而被丢弃。
|
||||
* @note 当前统一由 TBE 中断从队列中取字节发送;此处只负责入队并使能发送中断。
|
||||
* @retval bool true 表示成功写入发送缓冲,false 表示 USART 异常导致该字节被丢弃。
|
||||
* @note 正常路径仍是"入队 + 使能 TBE 中断"的非阻塞发送。仅在缓冲写满时
|
||||
* (3M 波特率下 init 阶段诊断突发写入会写满)就地轮询发出一字节腾位,
|
||||
* 以字节级背压换取日志完整——旧版此处静默丢弃且 putChar 忽略返回值,
|
||||
* 实测使 [ECAT] 诊断出现 "bus eaabled"/"pdi_type=0x8Dexect" 等掉字,
|
||||
* 曾导致 LAN9252 故障被两次误定性,故日志完整性优先于此处的等待开销。
|
||||
* 仅当 USART 外设本身不发送时才真正丢弃并累加 s_txDropped。
|
||||
*/
|
||||
bool Usart0::putCharNonBlocking(int ch)
|
||||
{
|
||||
uint32_t save = RT_CRITICAL_SAVE();
|
||||
const uint16_t next = nextIndex(s_txHead);
|
||||
uint16_t next = nextIndex(s_txHead);
|
||||
if(next == s_txTail) {
|
||||
RT_CRITICAL_RESTORE(save);
|
||||
return false;
|
||||
/* 缓冲已满:就地发出最老的一字节腾出槽位,失败则说明 USART 真的没在发。 */
|
||||
if(!drainOneByteLocked()) {
|
||||
++s_txDropped;
|
||||
RT_CRITICAL_RESTORE(save);
|
||||
return false;
|
||||
}
|
||||
next = nextIndex(s_txHead);
|
||||
}
|
||||
|
||||
s_txBuf[s_txHead] = static_cast<uint8_t>(ch);
|
||||
@@ -110,10 +146,10 @@ void Usart0::configurePins()
|
||||
* @brief 初始化 USART0 外设与中断。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @retval uint8_t Usart0::InitFault 故障码(0=USART 已使能,CTL0.UEN 回读置位)。
|
||||
* @note 使用 `CONFIG_USART0_BAUDRATE` 配置串口参数,并使能 USART0 TBE 中断用于队列发送。
|
||||
*/
|
||||
void Usart0::init()
|
||||
uint8_t Usart0::init()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_USART0);
|
||||
usart_deinit(USART0);
|
||||
@@ -125,6 +161,7 @@ void Usart0::init()
|
||||
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
|
||||
usart_enable(USART0);
|
||||
nvic_irq_enable(USART0_IRQn, CONFIG_IRQ_PREEMPT_SPI_USART, 0U);
|
||||
return ((USART_CTL0(USART0) & USART_CTL0_UEN) != 0U) ? kFaultOk : kFaultNotEnabled;
|
||||
}
|
||||
|
||||
/* ----- 中断入口 / printf 重定向的 C 包装 --------------------------- */
|
||||
|
||||
@@ -19,14 +19,20 @@ public:
|
||||
*/
|
||||
static void configurePins();
|
||||
|
||||
/** init 故障码(0=成功;经 0xA3 INIT_STATUS fault_codes[] 上报) */
|
||||
enum InitFault : uint8_t {
|
||||
kFaultOk = 0U, /**< 成功 */
|
||||
kFaultNotEnabled = 1U, /**< CTL0.UEN 回读未置位(使能未生效) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 初始化 USART0 外设与 NVIC。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @retval uint8_t InitFault 故障码(0=USART 已使能,CTL0.UEN 回读置位)。
|
||||
* @note 使用 8N1 格式,收发使能,同步配置 NVIC 以便 TXE 中断驱动发送。
|
||||
*/
|
||||
static void init();
|
||||
static uint8_t init();
|
||||
|
||||
/**
|
||||
* @brief 将字符写入 USART0 发送环形缓冲。
|
||||
@@ -66,10 +72,36 @@ private:
|
||||
|
||||
static bool putCharNonBlocking(int ch);
|
||||
|
||||
static constexpr uint16_t kTxBufSize = 1024U;
|
||||
/**
|
||||
* @brief 在临界区内直接轮询 TBE 并发出一个字节,腾出一个缓冲槽位。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval bool true 表示成功发出一字节(tail 已前进),false 表示等待 TBE 超时。
|
||||
* @note 调用者必须已持有临界区(USART0 中断 preempt=3 被 BASEPRI 屏蔽),
|
||||
* 因此这里独占 s_txTail,不会与 onInterrupt() 争用同一字节。
|
||||
* 不依赖中断投递,故在任意上下文(含 ISR 内 printf)均可安全使用。
|
||||
*/
|
||||
static bool drainOneByteLocked();
|
||||
|
||||
/** @brief 3M 波特率下 init 阶段诊断是突发写入,1024 字节会溢出致日志掉字。 */
|
||||
static constexpr uint16_t kTxBufSize = 4096U;
|
||||
/** @brief 等待 TBE 的自旋上限:3M 下单字节约 3.3us,此值远大于之,仅防 USART 失效死等。 */
|
||||
static constexpr uint32_t kTbeSpinMax = 200000U;
|
||||
static uint8_t s_txBuf[kTxBufSize];
|
||||
static volatile uint16_t s_txHead;
|
||||
static volatile uint16_t s_txTail;
|
||||
static volatile uint32_t s_txDropped;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 读取因发送缓冲溢出而被丢弃的字节数。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval uint32_t 累计丢弃字节数(0 表示日志完整可信)。
|
||||
* @note 非 0 说明串口日志有掉字,诊断结论不可全信——历史上掉字日志曾导致
|
||||
* LAN9252 故障两次误定性,故显式暴露该计数供 init 报告核对。
|
||||
*/
|
||||
static uint32_t droppedBytes() { return s_txDropped; }
|
||||
};
|
||||
|
||||
#endif /* DEVICE_USART0_H */
|
||||
@@ -11,13 +11,39 @@
|
||||
#include "sys/config.h"
|
||||
#include "main.h"
|
||||
|
||||
void Watchdog::init()
|
||||
uint8_t Watchdog::init()
|
||||
{
|
||||
/* FWDGT 的 PSC/RLD 写入经 LSI(IRC40K) 时钟域同步(PUD/RUD 由硬件清零)。
|
||||
* BOOT 只检测 FWDGT 复位标志、从不启动看门狗,冷启动时 LSI 未开——
|
||||
* 直接配置会让 PUD/RUD 永远等不到清零(库超时返回 ERROR,1.5.34 起暴露为 init 失败;
|
||||
* 此前静默:FWDGT 实际按复位默认值 DIV4/0xFFF 运行,超时就与配置不符)。
|
||||
* 必须先手动开 LSI 并等就绪,再配置。 */
|
||||
rcu_osci_on(RCU_IRC40K);
|
||||
if (rcu_osci_stab_wait(RCU_IRC40K) != SUCCESS) {
|
||||
return kFaultLsiNotReady;
|
||||
}
|
||||
|
||||
/* PSC/RLD 配置函数自带 ErrStatus(等待 PUD/RUD 清零带超时);
|
||||
* 此前返回值被忽略,写入失败会无声变成"看门狗实际未按预期运行"。 */
|
||||
fwdgt_write_enable();
|
||||
fwdgt_prescaler_value_config(CONFIG_FWDGT_PSC);
|
||||
fwdgt_reload_value_config(CONFIG_FWDGT_RLD);
|
||||
uint8_t fault = kFaultOk;
|
||||
if (fwdgt_prescaler_value_config((uint16_t)CONFIG_FWDGT_PSC) != SUCCESS) {
|
||||
fault = kFaultPsc;
|
||||
}
|
||||
if (fwdgt_reload_value_config((uint16_t)CONFIG_FWDGT_RLD) != SUCCESS) {
|
||||
if (fault == kFaultOk) {
|
||||
fault = kFaultRld;
|
||||
}
|
||||
}
|
||||
fwdgt_write_disable();
|
||||
fwdgt_enable();
|
||||
/* 使能后确认无挂起更新(PUD/RUD 应已清零) */
|
||||
if ((FWDGT_STAT & (FWDGT_STAT_PUD | FWDGT_STAT_RUD)) != 0U) {
|
||||
if (fault == kFaultOk) {
|
||||
fault = kFaultPending;
|
||||
}
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
void Watchdog::feed()
|
||||
|
||||
@@ -12,8 +12,18 @@
|
||||
|
||||
class Watchdog {
|
||||
public:
|
||||
/** @brief 初始化并启动 FWDGT,超时约 CONFIG_FWDGT_TIMEOUT_MS 毫秒。 */
|
||||
static void init();
|
||||
/** init 故障码(0=成功;经 0xA3 INIT_STATUS fault_codes[] 上报) */
|
||||
enum InitFault : uint8_t {
|
||||
kFaultOk = 0U, /**< 成功 */
|
||||
kFaultPsc = 1U, /**< 预分频写入未被硬件确认(PUD 超时) */
|
||||
kFaultRld = 2U, /**< 重载值写入未被硬件确认(RUD 超时) */
|
||||
kFaultPending = 3U, /**< 使能后仍有挂起的配置更新(PUD/RUD 未清零) */
|
||||
kFaultLsiNotReady = 4U, /**< LSI(IRC40K) 使能后稳定等待超时 */
|
||||
};
|
||||
|
||||
/** @brief 初始化并启动 FWDGT,超时约 CONFIG_FWDGT_TIMEOUT_MS 毫秒。
|
||||
* @retval uint8_t InitFault 故障码(0=PSC/RLD 写入均经硬件确认且无挂起更新)。 */
|
||||
static uint8_t init();
|
||||
|
||||
/** @brief 喂狗(重载计数器),应在主循环中周期调用。 */
|
||||
static void feed();
|
||||
|
||||
Reference in New Issue
Block a user