Refactor and clean up codebase

- Removed unused constants and type aliases in app_spi_command_defs.h and temp_sensor.h.
- Simplified ISpiCommandHandler destructor to default.
- Eliminated redundant methods and comments in temp_sensor and usart0 files.
- Consolidated writeU32Le function to app_spi_command_defs.h for reuse.
- Updated comments for clarity and accuracy in task_manager.h and task_runner.cpp.
- Removed hardfault.h as it contained no declarations.
- Cleaned up CANFD and watchdog implementations by removing dead code and unnecessary comments.
- Updated version numbers in config.h to reflect recent changes.
- Enhanced error handling and diagnostics in hardfault handling.
This commit is contained in:
cbb
2026-07-15 02:49:37 +08:00
parent a52d94687b
commit 76da7a3dd6
37 changed files with 1401 additions and 1955 deletions
@@ -25,7 +25,7 @@ project:
- file: ./device/adc.h
- file: ./device/adc.cpp
- file: ./device/canfd.h
- file: ./device/CANFD.cpp
- file: ./device/canfd.cpp
- file: ./device/timer.h
- file: ./device/timer.cpp
- file: ./device/usart0.h
+4 -2
View File
@@ -60,7 +60,7 @@ void EscExmcBus::init()
norsram_init.asyn_wait = DISABLE;
norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
/* 时序:按 LAN9252 HBI 要求配置 read/write wait state
/* TODO(spec): 时序:按 LAN9252 HBI 要求配置 read/write wait state
* LAN9252 HBI 最小 read/write cycle = 100ns。
* AHB=180MHz(RCU_MODIFY_UP 强制 /1),1 HCLK = 5.6ns。
* 6 HCLK = 33ns → 不满足,需 ≥ 18 HCLK 才能达到 100ns。
@@ -71,7 +71,9 @@ void EscExmcBus::init()
exmc_norsram_init(&norsram_init);
exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1);
#else
/* EXMC 外设库未安装:仅占位,启用 EtherCAT 时需先安装 EXMC 组件。 */
/* EXMC 外设库未安装(gd32e50x_exmc.h 缺失):EtherCAT ESC EXMC 总线初始化为空,
* 运行期 selfTest 必失败,EtherCAT 自动禁用。启用 ESC 需先安装 EXMC 组件。 */
#warning "EXMC peripheral library not included; EtherCAT ESC EXMC bus init is a no-op"
#endif
}
+2 -28
View File
@@ -28,7 +28,7 @@ void EscLan9252::init()
EscExmcBus::init();
/* ============================================================
* TODO: 完整 ESC 寄存器配置(需 SSC 或等价协议栈代码,本 spec 不实现)
* TODO(spec): 完整 ESC 寄存器配置(需 SSC 或等价协议栈代码,本 spec 不实现)
* ============================================================
* 当前仅完成 EXMC 总线初始化与芯片 ID 读取。要使 SYNC0 实际触发,还需配置:
*
@@ -58,11 +58,6 @@ void EscLan9252::init()
* ============================================================ */
}
bool EscLan9252::selfTest()
{
return (readChipId() == LAN9252_CHIP_ID_VALUE);
}
uint32_t EscLan9252::readChipId()
{
/* INIT-ONLY:init 阶段中断未使能,但仍走 initOnly 路径保持一致性 */
@@ -77,11 +72,6 @@ uint16_t EscLan9252::readReg(uint16_t addr)
return EscExmcBus::read16((uint32_t)addr << 1U);
}
void EscLan9252::writeReg(uint16_t addr, uint16_t val)
{
EscExmcBus::write16((uint32_t)addr << 1U, val);
}
__attribute__((section(".ramfunc"), noinline))
void EscLan9252::readPdi(uint16_t offset, uint8_t *buf, size_t len)
{
@@ -121,24 +111,8 @@ void EscLan9252::writePdi(uint16_t offset, const uint8_t *buf, size_t len)
}
/* ---- initOnly* 接口:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用 ----
* 运行期禁止调用;需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。
* 注:不再使用 PRIMASK 关中断(违反项目硬约束"Critical sections MUST use BASEPRI")。 */
* 运行期禁止调用;需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
uint16_t EscLan9252::initOnlyReadReg(uint16_t addr)
{
return readReg(addr);
}
void EscLan9252::initOnlyWriteReg(uint16_t addr, uint16_t val)
{
writeReg(addr, val);
}
void EscLan9252::initOnlyReadPdi(uint16_t offset, uint8_t *buf, size_t len)
{
readPdi(offset, buf, len);
}
void EscLan9252::initOnlyWritePdi(uint16_t offset, const uint8_t *buf, size_t len)
{
writePdi(offset, buf, len);
}
+2 -20
View File
@@ -4,8 +4,8 @@
* @note 通过 EscExmcBus 16-bit 并行总线访问 LAN9252 寄存器与 PDI 内存。
*
* 接口调用约束(避免 EXMC 总线争用):
* - readReg/writeReg/readPdi/writePdi:ISR-only,仅供 preempt=0 的 periodicIsr() 调用,无锁。
* - initOnlyReadReg/initOnlyWriteReg/initOnlyReadPdi/initOnlyWritePdi:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。
* - readReg/readPdi/writePdi:ISR-only,仅供 preempt=0 的 periodicIsr() 调用,无锁。
* - initOnlyReadReg:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。
*
* PDI offset 语义:offset 参数为 SyncManager 内存区相对偏移,输入 PDO(TxPDO)与输出 PDO(RxPDO)必须使用不同 offset,参见 CONFIG_ESC_PDI_INPUT_OFFSET / CONFIG_ESC_PDI_OUTPUT_OFFSET。
*/
@@ -24,12 +24,6 @@ public:
/** @brief 初始化 EXMC 总线与 ESC 基本寄存器。 */
static void init();
/**
* @brief ESC 自检:读取芯片 ID 寄存器校验。
* @return bool true 表示 ID 匹配(0x87654321),false 表示失败。
*/
static bool selfTest();
/**
* @brief 读取 ESC 芯片 ID(INIT-ONLY,供 init 打印诊断用)。
* @return uint32_t 32-bit 芯片 ID(期望值 0x87654321)。
@@ -39,9 +33,6 @@ public:
/** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */
static uint16_t readReg(uint16_t addr);
/** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */
static void writeReg(uint16_t addr, uint16_t val);
/** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */
static void readPdi(uint16_t offset, uint8_t *buf, size_t len);
@@ -50,15 +41,6 @@ public:
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
static uint16_t initOnlyReadReg(uint16_t addr);
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
static void initOnlyWriteReg(uint16_t addr, uint16_t val);
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
static void initOnlyReadPdi(uint16_t offset, uint8_t *buf, size_t len);
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
static void initOnlyWritePdi(uint16_t offset, const uint8_t *buf, size_t len);
};
#endif /* COMM_ESC_LAN9252_H */
+3 -2
View File
@@ -113,8 +113,9 @@ void EthercatSlave::process()
* 运行期 PDO 交换仅通过 getLatestInputs()/setOutputs() 三缓冲进行;ESC 寄存器访问仅限 init 阶段。
*
* 框架级桥接:从三缓冲取最新输入到 s_appInputs,把 s_appOutputs 推回三缓冲。
* TODO: 应用对象字典映射(具体输入字节对应哪个应用变量,如字节 0-3=温度浮点),
* 由后续应用层 spec 定义;应用层通过 getApplicationInputs()/setApplicationOutputs() 访问快照。 */
* TODO(spec): 应用对象字典映射(具体输入字节对应哪个应用变量,如字节 0-3=温度浮点)
* 待后续应用层 spec 定义;
* 应用层通过 getApplicationInputs()/setApplicationOutputs() 访问快照。 */
const PdoBuffer *latest = getLatestInputs();
if (latest != nullptr) {
s_appInputs = *latest;
+7 -7
View File
@@ -19,11 +19,7 @@
constexpr size_t CONFIG_ETHERCAT_PDO_SIZE = 32U;
/** @brief EtherCAT AL 状态码(EtherCAT 协议定义,ESC AL Status 寄存器低 4 位)。 */
constexpr uint8_t AL_STATE_INIT = 0x01U; /**< INIT 状态。 */
constexpr uint8_t AL_STATE_PREOP = 0x02U; /**< PREOP 状态。 */
constexpr uint8_t AL_STATE_BOOT = 0x03U; /**< BOOT 状态。 */
constexpr uint8_t AL_STATE_SAFEOP = 0x04U; /**< SAFEOP 状态(SYNC0 可触发下限)。 */
constexpr uint8_t AL_STATE_OP = 0x08U; /**< OP 状态。 */
/**
* @brief PDO 数据缓冲(输入/输出共用结构)。
@@ -75,14 +71,16 @@ public:
* @brief 获取应用层输入 PDO 快照(主循环调用)。
* @return const PdoBuffer* 最近一次 process() 拷贝的输入快照只读指针。
* @note 返回的指针在下次 process() 调用前有效;应用层应在 process() 后立即读取。
*/
* @note TODO(app-layer): 应用层 PDO 字典映射 spec 启用前无外部调用方;
* 保留接口契约,待未来 spec 实现后由应用层调用。 */
static const PdoBuffer* getApplicationInputs();
/**
* @brief 设置应用层输出 PDO 快照(主循环调用)。
* @param[in] outputs 输出数据指针(内容会被拷贝到内部快照)。
* @note 下次 process() 调用时,内部快照通过 setOutputs() 推入三缓冲。
*/
* @note TODO(app-layer): 应用层 PDO 字典映射 spec 启用前无外部调用方;
* 保留接口契约,待未来 spec 实现后由应用层调用。 */
static void setApplicationOutputs(const PdoBuffer *outputs);
/**
@@ -107,7 +105,9 @@ public:
/** @brief 读取 ESC 芯片 ID(供诊断打印;未就绪时为上次读到的值)。 */
static uint32_t getChipId() { return s_chipId; }
/** @brief 读取 ESC AL 状态码(供诊断打印与 SSC 集成;未就绪时为上次读到的值)。 */
/** @brief 读取 ESC AL 状态码(供诊断打印与 SSC 集成;未就绪时为上次读到的值)。
* @note TODO(app-layer): 当前仅 init 内部用作 SAFEOP 守卫,外部诊断未启用;
* SSC 集成后由应用层查询。 */
static uint8_t getAlState() { return s_alState; }
private:
+6 -18
View File
@@ -36,20 +36,6 @@ void EthercatSync::init()
exti_interrupt_enable(CONFIG_ESC_SYNC0_EXTI);
exti_interrupt_flag_clear(CONFIG_ESC_SYNC0_EXTI);
nvic_irq_enable(CONFIG_ESC_SYNC0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U);
/* ESC IRQ:PB1 -> EXTI1,下降沿,preempt=1 */
gpio_exti_source_select(CONFIG_ESC_IRQ_EXTI_SOURCE_PORT, CONFIG_ESC_IRQ_EXTI_SOURCE_PIN);
exti_init(CONFIG_ESC_IRQ_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
exti_interrupt_enable(CONFIG_ESC_IRQ_EXTI);
exti_interrupt_flag_clear(CONFIG_ESC_IRQ_EXTI);
nvic_irq_enable(CONFIG_ESC_IRQ_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_ESC, 0U);
/* SYNC1:PB2 -> EXTI2,下降沿,preempt=1(备用) */
gpio_exti_source_select(CONFIG_ESC_SYNC1_EXTI_SOURCE_PORT, CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN);
exti_init(CONFIG_ESC_SYNC1_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
exti_interrupt_enable(CONFIG_ESC_SYNC1_EXTI);
exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI);
nvic_irq_enable(CONFIG_ESC_SYNC1_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1, 0U);
#else
/* ---- 降级模式:TIMER0 内部定时 ---- */
rcu_periph_clock_enable(RCU_TIMER0);
@@ -66,21 +52,20 @@ void EthercatSync::init()
timer_interrupt_enable(TIMER0, TIMER_INT_UP);
nvic_irq_enable(TIMER0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U);
timer_enable(TIMER0);
#endif
/* ESC IRQ:PB1 -> EXTI1,下降沿,preempt=1 */
/* 公共:ESC IRQ (PB1->EXTI1) + SYNC1 (PB2->EXTI2) */
gpio_exti_source_select(CONFIG_ESC_IRQ_EXTI_SOURCE_PORT, CONFIG_ESC_IRQ_EXTI_SOURCE_PIN);
exti_init(CONFIG_ESC_IRQ_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
exti_interrupt_enable(CONFIG_ESC_IRQ_EXTI);
exti_interrupt_flag_clear(CONFIG_ESC_IRQ_EXTI);
nvic_irq_enable(CONFIG_ESC_IRQ_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_ESC, 0U);
/* SYNC1:PB2 -> EXTI2,下降沿,preempt=1(备用) */
gpio_exti_source_select(CONFIG_ESC_SYNC1_EXTI_SOURCE_PORT, CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN);
exti_init(CONFIG_ESC_SYNC1_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
exti_interrupt_enable(CONFIG_ESC_SYNC1_EXTI);
exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI);
nvic_irq_enable(CONFIG_ESC_SYNC1_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1, 0U);
#endif
}
__attribute__((section(".ramfunc"), noinline))
@@ -106,7 +91,8 @@ void EthercatSync::onSync1Exti()
{
if (exti_interrupt_flag_get(CONFIG_ESC_SYNC1_EXTI) != RESET) {
exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI);
/* SYNC1 备用,暂不处理 */
/* 预留:SYNC1 通道当前未承载业务,仅清 EXTI 标志防止中断挂起;
* 启用 SYNC1 业务时在此处添加处理逻辑。 */
}
}
@@ -117,6 +103,8 @@ void EthercatSync::onTimerIrq()
timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP);
EthercatSlave::periodicIsr();
}
#else
/* EXTI 模式下 TIMER0 不使用,本函数为占位;CONFIG_ETHERCAT_SYNC_MODE=1 时激活。 */
#endif
}
+2 -27
View File
@@ -10,6 +10,7 @@
#include <math.h>
namespace {
/* ---- 校验常量与辅助函数 ---- */
constexpr float kMinValidVdda = 2.7f;
constexpr float kMaxValidVdda = 3.6f;
constexpr float kMinValidTemperature = -40.0f;
@@ -24,16 +25,14 @@ static bool isFiniteFloat(float value)
{
return isfinite(value) != 0;
}
} // namespace
namespace {
/* ---- DMA 配置 ---- */
constexpr uint32_t kDmaPeriph = DMA0;
constexpr dma_channel_enum kDmaChannel = DMA_CH0;
constexpr uint32_t kChannelCount = 3U;
constexpr uint32_t kVinGpio = GPIOA;
constexpr uint32_t kVinPin = GPIO_PIN_6;
volatile uint16_t s_dmaBuffer[kChannelCount] = {0U, 0U, 0U};
volatile uint8_t s_dmaReady = 0U;
} // namespace
/**
@@ -108,30 +107,6 @@ void Adc::init()
SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
SysTickTimer::delayMs(1U);
s_dmaReady = 1U;
}
/**
* @brief 查询 ADC DMA 采样是否已经就绪。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示 DMA 已启动并完成初始稳定等待,false 表示尚未就绪。
*/
bool Adc::dmaReady()
{
return s_dmaReady != 0U;
}
/**
* @brief 获取 ADC DMA 原始缓冲区首地址。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回 DMA 原始缓冲区首地址。
* @note 缓冲顺序固定为 `VIN_ADC / TEMP / VREFINT`。
*/
const uint16_t *Adc::dmaBuffer()
{
return (const uint16_t *)s_dmaBuffer;
}
/**
+153 -251
View File
@@ -12,6 +12,12 @@
#include <cstring>
namespace {
/* 纯软件延时循环(不依赖 SysTick,可在任何中断状态下使用) */
static inline void spinDelay(uint32_t loops) {
for (volatile uint32_t d = 0; d < loops; ++d) { }
}
struct CanChannelConfig {
uint32_t canPeriph;
rcu_periph_enum canRcu;
@@ -91,7 +97,7 @@ static CanSelfTestResult runSelfTestFrame(uint8_t channelIndex, uint32_t extId,
result.mailbox = can_message_transmit(canPeriph, &tx_msg);
result.txReq = (result.mailbox != CAN_NOMAILBOX);
for (volatile uint32_t dly = 0; dly < 40000U; ++dly) { }
spinDelay(40000U);
if (result.txReq) {
result.txState = static_cast<uint8_t>(can_transmit_states(canPeriph, result.mailbox));
@@ -117,21 +123,21 @@ static void hardResetCan(uint32_t canPeriph, rcu_periph_enum canRcu)
{
/* 1. 触发 SWRST 软件复位 */
CAN_CTL(canPeriph) |= CAN_CTL_SWRST;
for (volatile uint32_t d = 0; d < 100000U; ++d) {}
spinDelay(100000U);
/* 2. 确认 SWRST 已被硬件自动清零;若未清零则强制写 0 并等待 */
if (CAN_CTL(canPeriph) & CAN_CTL_SWRST) {
CAN_CTL(canPeriph) &= ~CAN_CTL_SWRST;
for (volatile uint32_t d = 0; d < 50000U; ++d) {}
spinDelay(50000U);
}
/* 3. 关 RCU 时钟,等待外设域完全放电 */
rcu_periph_clock_disable(canRcu);
for (volatile uint32_t d = 0; d < 200000U; ++d) {} /* ~1.1ms @180MHz */
spinDelay(200000U); /* ~1.1ms @180MHz */
/* 4. 重新开时钟,等待外设域上电稳定 */
rcu_periph_clock_enable(canRcu);
for (volatile uint32_t d = 0; d < 200000U; ++d) {} /* ~1.1ms @180MHz */
spinDelay(200000U); /* ~1.1ms @180MHz */
/* 5. can_deinit() 走标准固件库复位流程 */
can_deinit(canPeriph);
@@ -303,123 +309,110 @@ bool CanFd::initFdMode(uint32_t canPeriph)
#endif
}
/**
* @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并使能 FIFO0 的非空/满/overfull 中断统计。
*/
void CanFd::init()
/** @brief 打印 SYS/APB1 时钟与目标波特率。 */
void CanFd::printClockInfo()
{
s_diag = {};
s_nextConsumeChannel = 0U;
s_mode = Mode::kModeAllPass;
for (size_t i = 0; i < kChannelCount; ++i) {
s_channelStats[i] = {};
s_feedback[i] = {};
}
s_feedbackSeq = 0U;
initGpio();
printf("[CAN-CLOCK] SYS=%luHz APB1=%luHz nominal=1Mbps data=2Mbps\r\n",
static_cast<unsigned long>(SystemCoreClock),
static_cast<unsigned long>(rcu_clock_freq_get(CK_APB1)));
}
for(size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
if (!kChannelEnabled[i]) {
printf("[CAN] CH%u DISABLED, skip init\r\n", static_cast<unsigned>(i));
continue;
}
rcu_periph_clock_enable(ch.canRcu);
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
const uint8_t rxLevelBefore = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
const uint8_t stbLevel = (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) ? 1U : 0U;
/** @brief 单通道硬件初始化(hardResetCan+重试+退出init mode+中断使能+RX队列清零)。 */
void CanFd::initChannelHardware(uint8_t i)
{
const auto &ch = kChannels[i];
if (!kChannelEnabled[i]) {
printf("[CAN] CH%u DISABLED, skip init\r\n", static_cast<unsigned>(i));
return;
}
rcu_periph_clock_enable(ch.canRcu);
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
const uint8_t rxLevelBefore = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
const uint8_t stbLevel = (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) ? 1U : 0U;
/* 强制复位 + 重试机制:使用 hardResetCan 完整电源周期解决 CAN1 卡死问题 */
bool nominalOk = false;
bool fdOk = false;
for (uint8_t retry = 0U; retry < 5U; ++retry) {
hardResetCan(ch.canPeriph, ch.canRcu);
/* 强制复位 + 重试机制:使用 hardResetCan 完整电源周期解决 CAN1 卡死问题 */
bool nominalOk = false;
bool fdOk = false;
for (uint8_t retry = 0U; retry < 5U; ++retry) {
hardResetCan(ch.canPeriph, ch.canRcu);
nominalOk = initNominalTiming(ch.canPeriph);
fdOk = initFdMode(ch.canPeriph);
if (nominalOk && fdOk) {
break; /* 初始化成功,无需重试 */
}
/* can_init 失败后外设可能卡在 init mode,先强制退出让下次重试从干净状态开始 */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
for (volatile uint32_t d = 0; d < 50000U; ++d) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
printf("[CAN-WARN] CH%u init failed (nom=%u fd=%u STAT=0x%08lX), retry %u/5\r\n",
static_cast<unsigned>(i),
nominalOk ? 1U : 0U, fdOk ? 1U : 0U,
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)),
static_cast<unsigned>(retry + 1U));
nominalOk = initNominalTiming(ch.canPeriph);
fdOk = initFdMode(ch.canPeriph);
if (nominalOk && fdOk) {
break; /* 初始化成功,无需重试 */
}
/* 滤波器配置延迟到循环结束后统一执行:
* CAN1 的 hardResetCan 含 SWRST,会清零 CAN0/CAN1 共享滤波器寄存器,
* 若在循环内配 CAN0 filter 后再做 CAN1 hardReset,CAN0 filter 会被破坏。 */
/* 退出 init mode,确保进入 NORMAL 工作模式 */
/* can_init 失败后外设可能卡在 init mode,先强制退出让下次重试从干净状态开始 */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
uint32_t exitWait = 0U;
for (; exitWait < 200000U; ++exitWait) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) {
break;
}
for (volatile uint32_t d = 0; d < 50000U; ++d) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
/* 兜底:若仍然卡在 init mode,做一次额外的 SWRST + 强制退出 */
if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) {
CAN_CTL(ch.canPeriph) |= CAN_CTL_SWRST;
for (volatile uint32_t d = 0; d < 50000U; ++d) {}
CAN_CTL(ch.canPeriph) &= ~(CAN_CTL_SWRST | CAN_CTL_IWMOD);
for (volatile uint32_t d = 0; d < 200000U; ++d) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
printf("[CAN-WARN] CH%u needed extra SWRST to exit init mode (STAT=0x%08lX)\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)));
}
const uint32_t statAfterExit = CAN_STAT(ch.canPeriph);
const uint32_t errAfterExit = CAN_ERR(ch.canPeriph);
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;
printf("[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",
printf("[CAN-WARN] CH%u init failed (nom=%u fd=%u STAT=0x%08lX), retry %u/5\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_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);
nvic_irq_enable(ch.txIrq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
s_rxQueues[i].head = 0U;
s_rxQueues[i].tail = 0U;
s_rxQueues[i].count = 0U;
nominalOk ? 1U : 0U, fdOk ? 1U : 0U,
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)),
static_cast<unsigned>(retry + 1U));
}
s_nextConsumeChannel = 0U;
/* 滤波器配置延迟到循环结束后统一执行:
* CAN1 的 hardResetCan 含 SWRST,会清零 CAN0/CAN1 共享滤波器寄存器,
* 若在循环内配 CAN0 filter 后再做 CAN1 hardReset,CAN0 filter 会被破坏。 */
/* 退出 init mode,确保进入 NORMAL 工作模式 */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
uint32_t exitWait = 0U;
for (; exitWait < 200000U; ++exitWait) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) {
break;
}
}
/* 兜底:若仍然卡在 init mode,做一次额外的 SWRST + 强制退出 */
if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) {
CAN_CTL(ch.canPeriph) |= CAN_CTL_SWRST;
spinDelay(50000U);
CAN_CTL(ch.canPeriph) &= ~(CAN_CTL_SWRST | CAN_CTL_IWMOD);
for (volatile uint32_t d = 0; d < 200000U; ++d) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
printf("[CAN-WARN] CH%u needed extra SWRST to exit init mode (STAT=0x%08lX)\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)));
}
const uint32_t statAfterExit = CAN_STAT(ch.canPeriph);
const uint32_t errAfterExit = CAN_ERR(ch.canPeriph);
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;
printf("[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_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);
nvic_irq_enable(ch.txIrq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
s_rxQueues[i].head = 0U;
s_rxQueues[i].tail = 0U;
s_rxQueues[i].count = 0U;
}
/** @brief 统一配置三路滤波器(HBC1F + per-channel filter)。 */
void CanFd::configureAllFilters()
{
/* ===== 统一配置滤波器(必须在所有 hardResetCan 完成之后) =====
* CAN1 hardResetCan 的 SWRST 会清零 CAN0/CAN1 共享滤波器寄存器域。
* 若在循环内先配 CAN0 filter、后做 CAN1 hardReset,CAN0 的 filter 0 会被
@@ -433,7 +426,11 @@ void CanFd::init()
printf("[CAN] filters configured post-init: HBC1F=%lu FW=0x%08lX\r\n",
static_cast<unsigned long>((CAN_FCTL(CAN0) >> 8) & 0x3FU),
static_cast<unsigned long>(CAN_FW(CAN0)));
}
/** @brief 打印初始化结果汇总(BT/DBT/滤波器状态)。 */
void CanFd::printInitResult()
{
/* ===== 初始化结果汇总 ===== */
printf("=== CAN INIT RESULT ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
@@ -462,7 +459,11 @@ void CanFd::init()
static_cast<unsigned long>(fw2),
static_cast<unsigned long>((fw2 >> 2) & 1U));
}
}
/** @brief 打印初始化后错误计数器快照(TEC/REC/BOFF/EPV/EW)。 */
void CanFd::printErrorState()
{
/* ===== 初始化后错误计数器快照(不发帧,避免启动时累积 TEC)===== */
printf("=== CAN INIT ERR STATE ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
@@ -479,7 +480,11 @@ void CanFd::init()
(err & CAN_ERR_PERR) ? 1U : 0U, /* EPV bit1 */
(err & CAN_ERR_WERR) ? 1U : 0U); /* EWARN bit0 */
}
}
/** @brief 物理总线自测(受 CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 控制)。 */
void CanFd::runSelfTest()
{
/* ===== Self-test (NORMAL mode, 发到物理总线测 ACK) ===== */
#if (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE != 0)
printf("=== CAN SELF-TEST (normal mode) ===\r\n");
@@ -514,8 +519,11 @@ void CanFd::init()
#else
printf("=== CAN SELF-TEST SKIPPED (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE=0) ===\r\n");
#endif
}
/** @brief 内部回环测试(受 CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 控制)。 */
void CanFd::runLoopbackTest()
{
/* ===== Loopback 回环测试 (内部 TX→RX,不走物理总线) ===== */
#if (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE != 0)
printf("=== CAN LOOPBACK (internal) ===\r\n");
@@ -658,7 +666,11 @@ void CanFd::init()
#else
printf("=== CAN LOOPBACK SKIPPED (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE=0) ===\r\n");
#endif
}
/** @brief 全寄存器快照转储(CTL/STAT/BT/DBT/ERR/INTEN/FCTL/FW/RFL0)。 */
void CanFd::dumpRegisters()
{
/* ===== 初始化后全寄存器快照:诊断 CAN0/CAN1 不收帧 ===== */
printf("=== POST-INIT FULL REG DUMP ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
@@ -702,6 +714,40 @@ void CanFd::init()
printf("===============================\r\n");
}
/**
* @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并使能 FIFO0 的非空/满/overfull 中断统计。
*/
void CanFd::init()
{
s_diag = {};
s_nextConsumeChannel = 0U;
s_mode = Mode::kModeAllPass;
for (size_t i = 0; i < kChannelCount; ++i) {
s_channelStats[i] = {};
s_feedback[i] = {};
}
s_feedbackSeq = 0U;
initGpio();
printClockInfo();
for (uint8_t i = 0; i < kChannelCount; ++i) {
initChannelHardware(i);
}
s_nextConsumeChannel = 0U;
configureAllFilters();
printInitResult();
printErrorState();
runSelfTest();
runLoopbackTest();
dumpRegisters();
}
/**
* @brief 切换 CANFD 接收过滤模式。
* @param[in] mode 目标过滤模式。
@@ -725,18 +771,6 @@ CanFd::Mode CanFd::mode()
return s_mode;
}
/**
* @brief 执行兼容保留的 CAN 轮询入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前三路接收均由 RX0 中断驱动,该函数仅保留旧调用兼容性。
*/
void CanFd::poll()
{
/* 兼容保留:三通道接收已由 RX0 中断驱动 */
}
/**
* @brief 查询三路 CAN 通道是否存在待消费接收报文。
* @param[in] 无。
@@ -963,34 +997,6 @@ const CanFd::Diag *CanFd::diag()
return &s_diag;
}
/**
* @brief 将三路 TCAN1057A 收发器置为 Normal 模式。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 对所有通道统一将 SLP/STB 控制脚拉低。
*/
void CanFd::setNormalMode()
{
for(size_t i = 0; i < kChannelCount; ++i) {
gpio_bit_reset(kChannels[i].slpPort, kChannels[i].slpPin);
}
}
/**
* @brief 将三路 TCAN1057A 收发器置为 Silent 模式。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 对所有通道统一将 SLP/STB 控制脚拉高。
*/
void CanFd::setSilentMode()
{
for(size_t i = 0; i < kChannelCount; ++i) {
gpio_bit_set(kChannels[i].slpPort, kChannels[i].slpPin);
}
}
/**
* @brief 透传式发送:按接收帧的原格式(标准/扩展、CAN-FD/经典、BRS/ESI、DLC)原样转发到目标 CAN 通道。
* @param[in] canPeriph 目标 CAN 外设实例。
@@ -1184,18 +1190,6 @@ void CanFd::handleRxInterrupt(uint8_t channelIndex)
}
}
/**
* @brief CAN0 兼容中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 兼容保留入口,内部统一转调 `handleRxInterrupt(0U)`。
*/
void CanFd::onRx0Interrupt()
{
handleRxInterrupt(0U);
}
/**
* @brief 处理指定通道的发送完成中断。
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
@@ -1334,98 +1328,6 @@ void CanFd::refreshFeedbackSnapshot()
}
}
/**
* @brief 运行时 BUS_OFF / 严重错误被动状态软件恢复。
* @note 由 1Hz 心跳任务调用。若某通道持续 2 秒以上处于 BUS_OFF 或 TEC>96,
* 通过收发器静默-唤醒切换让总线获得 128×11 个连续隐性位,协助硬件
* auto_bus_off_recovery 退出 BUS_OFF。本函数不执行 CAN 控制器 hardReset,
* 避免破坏 CAN0/CAN1 共享的 filter 配置;若切换后仍不健康,说明物理层
* 问题未消除,需要排查终端电阻/ACK/波特率/接线。
*/
void CanFd::recoverBusOffIfNeeded()
{
static uint32_t s_unhealthySinceSeq[kChannelCount] = {0U, 0U, 0U};
static bool s_recoveryInProgress[kChannelCount] = {false, false, false};
for (size_t i = 0; i < kChannelCount; ++i) {
if (!kChannelEnabled[i]) {
continue;
}
const auto &ch = kChannels[i];
const uint32_t errReg = CAN_ERR(ch.canPeriph);
const bool busOff = (errReg & CAN_ERR_BOERR) != 0U;
const uint32_t tec = (errReg >> 16) & 0xFFU;
const bool healthy = !busOff && (tec <= 96U);
if (healthy) {
if (s_unhealthySinceSeq[i] != 0U) {
printf("[CAN-RECOVER] CH%u healthy again (TEC=%lu) seq=%lu\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tec),
static_cast<unsigned long>(s_feedbackSeq));
s_unhealthySinceSeq[i] = 0U;
}
s_recoveryInProgress[i] = false;
continue;
}
if (s_unhealthySinceSeq[i] == 0U) {
s_unhealthySinceSeq[i] = s_feedbackSeq;
}
/* 仅在连续 2 秒不健康后才干预,避免对短暂波动过度反应 */
const uint32_t elapsed = s_feedbackSeq - s_unhealthySinceSeq[i];
if (elapsed < 2U) {
continue;
}
if (s_recoveryInProgress[i]) {
/* 上一轮恢复后仍在下一周期观察到不健康:物理层问题未解决,不要刷屏 */
static uint32_t s_lastStillBadPrintSeq[kChannelCount] = {0U, 0U, 0U};
if ((s_feedbackSeq - s_lastStillBadPrintSeq[i]) >= 10U) {
printf("[CAN-RECOVER] CH%u still unhealthy after toggle (TEC=%lu bus_off=%u) seq=%lu; check physical layer\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tec),
busOff ? 1U : 0U,
static_cast<unsigned long>(s_feedbackSeq));
s_lastStillBadPrintSeq[i] = s_feedbackSeq;
}
s_recoveryInProgress[i] = false; /* 允许下一轮再次尝试 */
continue;
}
printf("[CAN-RECOVER] CH%u attempting recovery (TEC=%lu bus_off=%u elapsed=%lus)\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tec),
busOff ? 1U : 0U,
static_cast<unsigned long>(elapsed));
/* 步骤:让收发器进入 standby 再切回 normal,强制总线出现连续隐性位,
* 帮助硬件 ABOR 完成 128×11 个连续隐性位的恢复序列。 */
gpio_bit_set(ch.slpPort, ch.slpPin);
SysTickTimer::delayMs(20U);
gpio_bit_reset(ch.slpPort, ch.slpPin);
SysTickTimer::delayMs(10U);
const uint32_t errAfterToggle = CAN_ERR(ch.canPeriph);
const bool stillBad = ((errAfterToggle & CAN_ERR_BOERR) != 0U) ||
(((errAfterToggle >> 16) & 0xFFU) > 96U);
if (stillBad) {
printf("[CAN-RECOVER] CH%u toggle done but still unhealthy (TEC=%lu bus_off=%u); need physical-layer fix\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>((errAfterToggle >> 16) & 0xFFU),
((errAfterToggle & CAN_ERR_BOERR) != 0U) ? 1U : 0U);
} else {
printf("[CAN-RECOVER] CH%u recovered by transceiver toggle\r\n",
static_cast<unsigned>(i));
}
s_recoveryInProgress[i] = true;
}
}
/**
* @brief 拷贝指定通道的最新反馈快照。
* @param[in] channelIndex 目标 CAN 通道号(0/1/2)。
-19
View File
@@ -32,23 +32,6 @@ public:
*/
static void init();
/**
* @brief 查询 ADC DMA 缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示 DMA 循环缓存已可用,false 表示仍未准备好。
*/
static bool dmaReady();
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 \p kChannelCount 的原始缓存指针。
* @note 缓冲区顺序为:VIN_ADC → 温度通道 → VREF 通道。
*/
static const uint16_t *dmaBuffer();
/**
* @brief 读取指定通道在 DMA 缓存中的原始 ADC 值。
* @param[in] channel 目标 ADC 通道号。
@@ -128,6 +111,4 @@ private:
static void initDma();
};
using adc_sample_t = Adc::Sample;
#endif /* DEVICE_ADC_H */
+9 -13
View File
@@ -132,7 +132,6 @@ public:
/** @brief 查询当前 CANFD 接收过滤模式。 */
static Mode mode();
static void poll();
static bool hasRxFrame();
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel = nullptr);
static CanTxResult transmitFrame(uint8_t channelIndex, uint32_t ext_id,
@@ -152,13 +151,6 @@ public:
*/
static void refreshFeedbackSnapshot();
/**
* @brief 保留的手动 BUS_OFF 恢复诊断入口。
* @note 正常周期任务不调用;控制器已启用硬件自动 BUS_OFF 恢复,避免在
* 发送重试期间切换收发器 S 脚而人为制造额外位错误。
*/
static void recoverBusOffIfNeeded();
/**
* @brief 拷贝指定通道的最新反馈快照到调用方缓冲。
* @param[in] channelIndex 目标 CAN 通道号(0/1/2)。
@@ -175,9 +167,6 @@ public:
*/
static bool copyIdSnapshotTable(uint8_t channelIndex, IdSnapshotTable *out);
static void setNormalMode();
static void setSilentMode();
static void onRx0Interrupt();
static void handleRxInterrupt(uint8_t channelIndex);
static void handleTxInterrupt(uint8_t channelIndex);
@@ -209,6 +198,15 @@ private:
static bool initNominalTiming(uint32_t canPeriph);
static bool initFdMode(uint32_t canPeriph);
static void printClockInfo();
static void initChannelHardware(uint8_t i);
static void configureAllFilters();
static void printInitResult();
static void printErrorState();
static void runSelfTest();
static void runLoopbackTest();
static void dumpRegisters();
static Diag s_diag;
static RxQueue s_rxQueues[3];
static uint8_t s_nextConsumeChannel;
@@ -219,6 +217,4 @@ private:
static IdSnapshotTable s_idSnapshot[3];
};
using canfd_diag_t = CanFd::Diag;
#endif /* DEVICE_CANFD_H */
@@ -32,12 +32,7 @@ void postCanSendLog(AppSpiCommand &bus, const char *result,
result ? result : "UNKNOWN");
bus.postLog(log_msg);
#else
(void)bus;
(void)result;
(void)can_ch;
(void)can_id;
(void)can_data;
(void)can_dlc;
(void)bus; (void)result; (void)can_ch; (void)can_id; (void)can_data; (void)can_dlc;
#endif
}
@@ -50,14 +50,6 @@ static constexpr uint16_t kFeedbackPayloadLen = 123U; /* kTxBufSize=128 - over
static constexpr uint8_t kPageEntries = 8U;
static constexpr uint16_t kFixedHeaderLen = 28U; /* ch(1)+page(1)+total(1)+cnt(1)+mode(1)+seq(4)+rx(4)+tx(4)+drop(4)+err(4)+tec(1)+rec(1)+bus_state(1) */
static void writeU32Le(uint8_t *dst, uint32_t value)
{
dst[0] = static_cast<uint8_t>(value & 0xFFU);
dst[1] = static_cast<uint8_t>((value >> 8) & 0xFFU);
dst[2] = static_cast<uint8_t>((value >> 16) & 0xFFU);
dst[3] = static_cast<uint8_t>((value >> 24) & 0xFFU);
}
/**
* @brief 构建一页 CAN 反馈 payload(与 ESP32 canFdPollFeedback 解析格式一致)。
* @param[in] channel 目标通道 0/1/2。
@@ -254,7 +246,6 @@ void canFeedbackPushStep()
{
static uint8_t s_channel = 0U;
static uint8_t s_page = 0U;
static uint32_t s_pushCount = 0U;
/* 已收到 0xB0 进入 Bootloader 请求,停止主动按铃避免干扰 OTA 流程。
* 复位前主循环仍在运行,但不再下拉 SPIready,让 ESP32 的
@@ -273,19 +264,12 @@ void canFeedbackPushStep()
return;
}
/* ESP32 尚未完成上一帧处理(未释放 SPIready),等待 EXTI6 上升沿。
* 防止 GD32 在 ESP32 回到 IDLE 前按铃导致下降沿被 ISR 丢弃。 */
if (!AppSpiCommandBus.canRingNow()) {
return;
}
uint8_t payload[kFeedbackPayloadLen] = {};
uint16_t payload_len = buildFeedbackPayload(s_channel, s_page,
payload, sizeof(payload));
if (payload_len > 0U) {
(void)AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeCanFeedback,
payload, payload_len);
++s_pushCount; /* 仅保留计数,不打印心跳日志 */
}
/* 推进到下一页/下一通道,循环遍历 3 通道 × 2 页 */
@@ -30,14 +30,6 @@ namespace {
constexpr uint8_t kPageEntries = 8U;
constexpr uint16_t kMaxPayloadLen = 123U; /* kTxBufSize=128 - overhead=5 */
inline void writeU32Le(uint8_t *dst, uint32_t val)
{
dst[0] = static_cast<uint8_t>(val & 0xFFU);
dst[1] = static_cast<uint8_t>((val >> 8) & 0xFFU);
dst[2] = static_cast<uint8_t>((val >> 16) & 0xFFU);
dst[3] = static_cast<uint8_t>((val >> 24) & 0xFFU);
}
class SpiIdSnapshotHandler final : public ISpiCommandHandler {
public:
bool canHandle(uint8_t frame_type) const override
File diff suppressed because it is too large Load Diff
+5 -50
View File
@@ -5,39 +5,6 @@
class ISpiCommandHandler;
/**
* @brief GD32 侧 SPI 链路状态机:封装按铃状态。
*
* v2 DATA_READY 语义:PB6 低 = 有待发数据,高 = 队列空。
* ESP32 不再操作 PB6,无独立「完成」信号;按铃完成由 ESP32
* 的 CS 读事务本身确认(onCsRelease 按铃响应路径调 onEsp32Complete)。
*
* canRingNow() 总是返回 true,由 requestEsp32Read() 的 m_ringPending
* 检查控制重复按铃。
*/
class SpiLinkState {
public:
/** 是否允许发起新按铃。
* v2 DATA_READY: ESP32 不再操作 PB6,无独立完成信号;
* 总是允许,由 requestEsp32Read() 的 m_ringPending 检查控制重复。 */
bool canRingNow() const { return true; }
/** 发起按铃时调用(v2 下仅保留兼容写法,无实际门禁作用)。 */
void onRingStart() { m_esp32Ready = false; }
/** ESP32 CS 读事务完成确认(onCsRelease 按铃响应路径调用)。 */
void onEsp32Complete() { m_esp32Ready = true; }
/** 按铃超时取消时调用(允许重试,不依赖 EXTI)。 */
void onRingTimeout() { m_esp32Ready = true; }
/** 初始化(首帧允许)。 */
void reset() { m_esp32Ready = true; }
private:
volatile bool m_esp32Ready = true;
};
/**
* @brief APP 运行期 SPI 链路与命令路由模块。
*
@@ -114,11 +81,6 @@ public:
*/
bool requestEsp32Read(uint8_t frame_type, const uint8_t *payload, uint16_t payload_len);
/** @brief L4 别名:主动事件入队(当前等同 requestEsp32Read)。 */
bool enqueueEvent(uint8_t frame_type, const uint8_t *payload, uint16_t payload_len) {
return requestEsp32Read(frame_type, payload, payload_len);
}
/** @brief 查询当前是否有未读按铃(ESP32 尚未来读)。 */
bool isRingPending() const { return m_ringPending; }
@@ -126,11 +88,6 @@ public:
* 0xB0 进 BOOT 等路径需公开调用,避免 0xC1 残留盖住 ACK。 */
void cancelRing();
/** @brief 查询 ESP32 是否已完成上一帧处理(SPIready 上升沿触发)。
* 初始为 true 允许第一次按铃;每次 requestEsp32Read 后置 false,
* ESP32 releaseReady 产生上升沿后由 EXTI 置 true。 */
bool canRingNow() const { return m_linkState.canRingNow(); }
static constexpr uint16_t kRxBufSize = 256U;
static constexpr uint16_t kTxBufSize = 256U;
static constexpr uint16_t kLogBufSize = 256U;
@@ -143,8 +100,7 @@ private:
volatile bool m_csActive = false;
/** 本笔 CS 实际收到的字节数(CS 上升沿时由 RX DMA CHCNT 反算得到)。 */
volatile uint16_t m_rxLen = 0U;
uint16_t m_txLen = 0U;
volatile uint8_t m_txPos = 0U;
volatile uint16_t m_txLen = 0U;
volatile bool m_logPending = false;
bool m_txActiveThisCs = false;
/** RX DMA 接收缓冲(替代旧 RBNE 逐字节累积的 m_rxBuf)。
@@ -159,11 +115,6 @@ private:
volatile bool m_ringPending = false;
/** 按铃超时截止时刻(ms,基于 SysTickTimer::getMs)。 */
volatile uint32_t m_ringDeadline = 0U;
/** 按铃期间暂存的请求帧长度(供 onCsAssert 装入 DMA)。 */
volatile uint16_t m_ringTxLen = 0U;
/** SPI 链路状态机:封装 ESP32 完成信号。 */
SpiLinkState m_linkState;
void configureGpio();
void configureSpi0();
@@ -173,6 +124,10 @@ private:
void onCsRelease();
void processCommand();
/** @brief 设置 m_ringPending=true 并启动超时看护截止时刻。
* 统一 7 处「m_ringPending=true; m_ringDeadline=now+timeout;」赋值模式。 */
void armRingPending();
/** @brief 在 CS 空闲期预装下一笔全双工 DMA(TX + RX)。
* 必须在首个 SCK 之前完成,否则 EXTI CS 下降相对 ESP32 时钟偏晚,
* 整笔事务会收 0 字节。对齐 BOOT SpiOta::armSpiDmasForNextCs。 */
@@ -1,4 +1,4 @@
#ifndef DEVICE_APP_SPI_COMMAND_DEFS_H
#ifndef DEVICE_APP_SPI_COMMAND_DEFS_H
#define DEVICE_APP_SPI_COMMAND_DEFS_H
#include <cstdint>
@@ -14,10 +14,6 @@
namespace SpiDefs {
/* 链路层帧类型(v2) */
static constexpr uint8_t kTypeLinkHello = 0x00U;
static constexpr uint8_t kTypeLinkError = 0x7FU;
/* 帧类型 */
static constexpr uint8_t kTypeOtaReq = 0x01U;
static constexpr uint8_t kTypeOtaData = 0x02U;
@@ -36,4 +32,12 @@ static constexpr uint8_t kRspError = 0x82U;
} // namespace SpiDefs
/** @brief 将 32 位值按小端写入 4 字节缓冲(供 CAN 反馈/ID 快照等 payload 复用)。 */
inline void writeU32Le(uint8_t *dst, uint32_t val) {
dst[0] = static_cast<uint8_t>(val & 0xFFU);
dst[1] = static_cast<uint8_t>((val >> 8) & 0xFFU);
dst[2] = static_cast<uint8_t>((val >> 16) & 0xFFU);
dst[3] = static_cast<uint8_t>((val >> 24) & 0xFFU);
}
#endif /* DEVICE_APP_SPI_COMMAND_DEFS_H */
@@ -1,4 +1,4 @@
#ifndef DEVICE_SPI_COMMAND_HANDLER_H
#ifndef DEVICE_SPI_COMMAND_HANDLER_H
#define DEVICE_SPI_COMMAND_HANDLER_H
#include <cstdint>
@@ -16,10 +16,10 @@ struct SpiCommandContext {
class ISpiCommandHandler {
public:
~ISpiCommandHandler() {}
virtual bool canHandle(uint8_t frame_type) const = 0;
virtual void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) = 0;
protected:
~ISpiCommandHandler() = default; /* 接口基类,不允许通过基类指针 delete;裸机不退出。 */
};
#endif /* DEVICE_SPI_COMMAND_HANDLER_H */
-12
View File
@@ -16,16 +16,4 @@
void TempSensor::init()
{
Adc::init();
}
/**
* @brief 获取一组温度传感相关采样结果。
* @param[in] data 指向输出采样结构体的指针。
* @param[out] data 输出温度、VREF 与 VIN 相关采样结果;允许为空。
* @retval 无返回值。
* @note 当前内部直接转调 `Adc::collectSample()`。
*/
void TempSensor::sample(Data *data)
{
Adc::collectSample(data);
}
-13
View File
@@ -9,8 +9,6 @@
*/
class TempSensor {
public:
using Data = Adc::Sample;
/**
* @brief 初始化温度采样依赖资源。
* @param[in] 无。
@@ -19,17 +17,6 @@ public:
* @note 当前通过调用 Adc::init() 完成内部通道准备。
*/
static void init();
/**
* @brief 采集一次温度相关样本。
* @param[in] 无。
* @param[out] data 指向输出 Data 结构体的指针。
* @retval 无返回值。
* @note 内部调用 Adc::collectSample() 填充完整 ADC 结果。
*/
static void sample(Data *data);
};
using temp_sensor_data_t = TempSensor::Data;
#endif /* DEVICE_TEMP_SENSOR_H */
-13
View File
@@ -11,7 +11,6 @@
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_dropCount = 0U;
/**
* @brief 计算发送环形缓冲区中的下一个索引。
@@ -24,17 +23,6 @@ uint16_t Usart0::nextIndex(uint16_t idx)
return (idx + 1U) % kTxBufSize;
}
/**
* @brief 判断 USART0 发送缓冲区是否已满。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示发送缓冲区已满,false 表示仍有可写空间。
*/
bool Usart0::isBufferFull()
{
return nextIndex(s_txHead) == s_txTail;
}
/**
* @brief 判断 USART0 发送缓冲区是否为空。
* @param[in] 无。
@@ -59,7 +47,6 @@ bool Usart0::putCharNonBlocking(int ch)
const uint16_t next = nextIndex(s_txHead);
if(next == s_txTail) {
RT_CRITICAL_RESTORE(save);
s_dropCount++;
return false;
}
-9
View File
@@ -56,14 +56,6 @@ private:
*/
static uint16_t nextIndex(uint16_t idx);
/**
* @brief 判断环形发送缓冲是否已满。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示已满,false 表示可继续写入。
*/
static bool isBufferFull();
/**
* @brief 判断环形发送缓冲是否为空。
* @param[in] 无。
@@ -75,7 +67,6 @@ private:
static bool putCharNonBlocking(int ch);
static constexpr uint16_t kTxBufSize = 1024U;
static volatile uint32_t s_dropCount;
static uint8_t s_txBuf[kTxBufSize];
static volatile uint16_t s_txHead;
static volatile uint16_t s_txTail;
-5
View File
@@ -24,8 +24,3 @@ void Watchdog::feed()
{
fwdgt_counter_reload();
}
void Watchdog::abandon()
{
/* 不喂狗即可,FWDGT 超时后硬件自动复位。 */
}
+1 -4
View File
@@ -1,4 +1,4 @@
/**
/**
* @file watchdog.h
* @brief APP 独立看门狗模块(FWDGT)。
*
@@ -17,9 +17,6 @@ public:
/** @brief 喂狗(重载计数器),应在主循环中周期调用。 */
static void feed();
/** @brief 停止喂狗,让看门狗自然超时触发复位跳转 BOOT。 */
static void abandon();
};
#endif /* DEVICE_WATCHDOG_H */
-1
View File
@@ -15,7 +15,6 @@ enum system_task_t {
TASK_ADC_VREF_SAMPLE = 0, /**< 采样 VIN/VREF 原始值任务。 */
TASK_ADC_TEMP_SAMPLE, /**< 采样内部温度通道原始值任务。 */
TASK_ADC_PROCESS, /**< 对 ADC 原始值进行电压与温度计算任务。 */
TASK_CAN_RX_POLL, /**< 兼容保留的 CANFD 接收轮询任务。 */
TASK_CAN_RX_PARSE, /**< 解析并消费已接收 CANFD 帧任务。 */
TASK_CAN_TX_SEND, /**< CANFD 周期发送任务,每 1ms 每通道发送一帧。 */
TASK_STATUS_LED, /**< 状态灯维护任务。 */
+4 -4
View File
@@ -1,4 +1,4 @@
/**
/**
* @file config.h
* @brief 应用级集中配置项定义。
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
@@ -16,9 +16,9 @@ constexpr const char *APP_BANNER_TEXT = "GD32E508 DCU task scheduler start";
* APP 固件版本号
* ============================================================ */
constexpr uint32_t APP_FW_VERSION_MAJOR = 1U;
constexpr uint32_t APP_FW_VERSION_MINOR = 2U;
constexpr uint32_t APP_FW_VERSION_PATCH = 3U;
constexpr uint32_t APP_FW_VERSION_BUILD = 2U;
constexpr uint32_t APP_FW_VERSION_MINOR = 3U;
constexpr uint32_t APP_FW_VERSION_PATCH = 0U;
constexpr uint32_t APP_FW_VERSION_BUILD = 0U;
/** @brief USART0 默认波特率。 */
+2 -1
View File
@@ -92,7 +92,6 @@ static uint32_t detectApb2Clock(void) {
}
extern "C" void HardFault_Handler_C(uint32_t *stack_frame, uint32_t lr_value) {
(void)lr_value;
/* 确保 USART0 时钟可用 */
volatile uint32_t *rcu_apb2en = (volatile uint32_t *)0x40021018U;
@@ -116,6 +115,8 @@ extern "C" void HardFault_Handler_C(uint32_t *stack_frame, uint32_t lr_value) {
*usart_ctl0 = (1U << 3) | (1U << 13); /* TE | USARTEN */
rawPuts("\r\n!!! HardFault !!!\r\n");
rawPuts("EXC_RETURN="); rawHex32(lr_value); rawPuts("\r\n");
/* EXC_RETURN 解析:bit3=0(MSP)/1(PSP), bit2=0(handler mode)/1(thread mode) */
if (stack_frame) {
uint32_t pc = stack_frame[6];
-6
View File
@@ -1,6 +0,0 @@
#ifndef HARDFAULT_H
#define HARDFAULT_H
void HardFault_Diagnostic(void);
#endif
@@ -217,6 +217,7 @@ void OtaBootRequest::process()
* BOOT 起机后再由 HELLO role=boot 确认。 */
/* SysTick 中断仍开,delayMs 可用;SPI 相关 IRQ 已关。 */
SysTickTimer::delayMs(kRebootDelayMs);
/* 复位前最终关中断:必须用 PRIMASK 全屏蔽(非 BASEPRI),确保 NVIC_SystemReset 过程中无任何中断嵌套 */
__disable_irq();
NVIC_SystemReset();
while(1) {
+1 -1
View File
@@ -42,7 +42,7 @@ public:
* @param[in] taskMask 待置位的任务位图掩码。
* @param[out] 无。
* @retval 无返回值。
* @note 内部通过保持当前 PRIMASK 状态的临界区保证可由主循环与 ISR 共享调用。
* @note 内部通过 BASEPRI 临界区(RT_CRITICAL_SAVE/RESTORE)保证可由主循环与 ISR 共享调用。
*/
static void set(uint32_t taskMask);
+2 -12
View File
@@ -59,7 +59,7 @@ static float medianFilter(const float *buffer)
} // namespace
struct TaskRunner::Runtime {
temp_sensor_data_t sample{};
Adc::Sample sample{};
float temperature_samples[kAdcFilterWindow] = {0.0f};
float vdda_samples[kAdcFilterWindow] = {0.0f};
float vin_adc_samples[kAdcFilterWindow] = {0.0f};
@@ -134,15 +134,6 @@ void TaskRunner::taskAdcProcess()
s.valid = sampleValid ? 1U : 0U;
}
/**
* @brief 执行兼容保留的 CAN 接收轮询任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前三路 CANFD 接收已由 RX0 中断驱动,该任务仅保留接口兼容性。
*/
void TaskRunner::taskCanRxPoll() { CanFd::poll(); }
/**
* @brief 执行 CAN 周期发送任务:每 1ms 在每个使能通道上发送一帧 CANFD 测试帧。
* @param[in] 无。
@@ -231,7 +222,7 @@ void TaskRunner::taskStatusLed() { /* 预留 */ }
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前仅保留任务入口。
* @note 当前任务:推进 OTA Boot 请求、刷新 CAN 反馈快照、启动反馈推送轮次。
*/
void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); CanFd::refreshFeedbackSnapshot(); canFeedbackStartRound(); }
@@ -382,7 +373,6 @@ void TaskRunner::runPending()
if(pending & (1UL << TASK_ADC_VREF_SAMPLE)) taskAdcVrefSample();
if(pending & (1UL << TASK_ADC_TEMP_SAMPLE)) taskAdcTempSample();
if(pending & (1UL << TASK_ADC_PROCESS)) taskAdcProcess();
if(pending & (1UL << TASK_CAN_RX_POLL)) taskCanRxPoll();
if(pending & (1UL << TASK_CAN_RX_PARSE)) taskCanRxParse();
if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend();
if(pending & (1UL << TASK_STATUS_LED)) taskStatusLed();
-2
View File
@@ -35,8 +35,6 @@ private:
static void taskAdcTempSample();
/** @brief 根据采样值计算工程量。 */
static void taskAdcProcess();
/** @brief 兼容保留的 CAN 轮询任务。 */
static void taskCanRxPoll();
/** @brief 解析并消费接收帧。 */
static void taskCanRxParse();
/** @brief CAN 周期发送任务;每 1ms 每通道发送一帧测试报文。 */