- 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.
15 KiB
15 KiB
清理剩余低风险可优化项 Plan
Summary
在上一轮 purge-legacy-dead-code spec(已清除 23 项 A 类死代码 + 11 项 B/C/D 类改进)基础上,对 GD32E508_DCU_APP/ 进行二次扫描,识别并处理 13 项剩余低风险可优化项(不改变运行期行为,构建可验证)。
覆盖三类:
- A 类残留死代码(6 项):死别名、写而不读成员/静态、空头文件、冗余类型别名
- 过期/缺失注释(3 项):PRIMASK 过期说明、taskHeartbeat 过期注释、复位前 PRIMASK 缺解释
- 重复代码提取(4 项):spinDelay 延时循环、armRingPending 赋值模式、writeU32Le 跨文件去重、adc.cpp namespace 合并
Current State Analysis
已完成(上一轮 spec)
- A1-A14:23 项死代码清除(CAN 兼容轮询链/SPI v1 状态机/EtherCAT 死 API/ADC/USART0/Watchdog/TempSensor 死接口等)
- B1-B6:6 项前瞻性桩加 TODO 标记
- C1-C4:4 项条件编译优化
- D1:1 项历史注释清理
- 构建验证通过(43/43,0 错误,Code=47462)
本轮新发现(经 Grep 交叉验证)
二次扫描发现 13 项上一轮未覆盖的低风险优化项,均不改变运行期行为。
Proposed Changes
第一组:A 类残留死代码清除(6 项)
1.1 删除死类型别名 canfd_diag_t
- 文件:
GD32E508_DCU_APP/device/canfd.h:211 - 现状:
using canfd_diag_t = CanFd::Diag;— Grep 全项目仅命中声明自身,无任何使用方 - 改法:删除第 211 行及相邻空行
- 风险:低(纯类型别名删除)
1.2 删除写而永不读成员 m_ringTxLen
- 文件:
GD32E508_DCU_APP/device/spi/app_spi_command.h:119-120(声明 + 注释)GD32E508_DCU_APP/device/spi/app_spi_command.cpp:407, 712, 745, 859, 867(5 处写入)
- 现状:
volatile uint16_t m_ringTxLen = 0U;头注释称"供 onCsAssert 装入 DMA",但onCsAssert实际使用的是m_txLen。在requestEsp32Read(745-746 行)中m_ringTxLen与m_txLen被赋同值,m_ringTxLen是冗余写者。Grep 确认 5 处写入、0 处读取。 - 改法:
- 删除
app_spi_command.h:119-120(注释 + 成员声明) - 删除
app_spi_command.cpp中 5 处m_ringTxLen = ...;赋值语句
- 删除
- 风险:低(仅删除写操作,
m_txLen与m_ringTxLen始终同值,读取路径不变) - 注意:745 行
m_ringTxLen = (uint16_t)built_len;的下一行m_txLen = (uint16_t)built_len;必须保留
1.3 删除写而永不读静态 s_pushCount
- 文件:
GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp:257, 282 - 现状:
static uint32_t s_pushCount = 0U;仅被自增(282 行++s_pushCount;),从未被读取、打印或暴露给诊断接口 - 改法:删除 257 行声明与 282 行自增语句(含其注释
/* 仅保留计数,不打印心跳日志 */) - 风险:低(纯死状态)
1.4 删除 4 个未使用本地别名
- 文件:
GD32E508_DCU_APP/device/spi/app_spi_command.cpp:49, 51, 54, 55 - 现状:本地别名块(48-56 行)共 9 个别名,其中
kTypeCanSend/kTypeAppPing/kRspAck/kRspWait4 个在文件内从未被引用(Grep 仅命中声明行)。其余 5 个(kTypeBootReq/kTypeOtaReq/kTypeOtaData/kTypeOtaCancel/kRspError)仍被使用。 - 改法:删除第 49、51、54、55 行
- 风险:低(删除
static constexpr未使用符号)
1.5 删除空头文件 sys/hardfault.h
- 文件:
GD32E508_DCU_APP/sys/hardfault.h(整文件,仅 3 行 include guard) - 现状:A13 清理后该头文件已无任何声明,仅剩
#ifndef HARDFAULT_H / #define HARDFAULT_H / #endif。sys/hardfault.cpp不包含此头,全项目无任何#include "sys/hardfault.h"或#include "hardfault.h"(Grep 确认 0 匹配)。 - 改法:删除整个文件
- 风险:低(无引用方)
- 注意:需确认构建脚本(CMakeLists.txt)中是否显式列出该头文件作为安装目标;若仅有 .cpp 参与编译则无影响
1.6 删除 TempSensor 冗余类型别名(方案 A)
- 文件:
GD32E508_DCU_APP/device/temp_sensor.h:12(using Data = Adc::Sample;)GD32E508_DCU_APP/device/temp_sensor.h:24(using temp_sensor_data_t = TempSensor::Data;)GD32E508_DCU_APP/sys/task_runner.cpp:62(消费点temp_sensor_data_t sample{};)
- 现状:两层
using别名链(temp_sensor_data_t→TempSensor::Data→Adc::Sample),仅 1 处消费(task_runner.cpp:62)。TempSensor类本身保留(init()仍被initial.cpp:45调用,作为语义门面有价值)。 - 改法:
- 删除
temp_sensor.h:12的using Data = Adc::Sample; - 删除
temp_sensor.h:24的using temp_sensor_data_t = TempSensor::Data; - 修改
task_runner.cpp:62将temp_sensor_data_t sample{};改为Adc::Sample sample{}; - 确认
task_runner.cpp已#include "device/adc.h"(若未包含则需添加,但 task_runner.cpp 大概率已包含)
- 删除
- 风险:低(仅类型别名替换,不改语义)
- 注意:
TempSensor类的init()声明与 doxygen 注释保留不动
第二组:过期/缺失注释修正(3 项)
2.1 修正 task_manager.h PRIMASK 过期注释
- 文件:
GD32E508_DCU_APP/sys/task_manager.h:45 - 现状:注释
内部通过保持当前 PRIMASK 状态的临界区保证可由主循环与 ISR 共享调用,但实现已迁移至 BASEPRI(RT_CRITICAL_SAVE/RESTORE) - 改法:改为
内部通过 BASEPRI 临界区(RT_CRITICAL_SAVE/RESTORE)保证可由主循环与 ISR 共享调用 - 风险:低(纯注释)
2.2 修正 task_runner.cpp taskHeartbeat 过期注释
- 文件:
GD32E508_DCU_APP/sys/task_runner.cpp:225 - 现状:
taskHeartbeat的 doxygen 注释@note 当前仅保留任务入口。已过期失真——函数体实际已有实现(227 行:OtaBootRequest::process(); CanFd::refreshFeedbackSnapshot(); canFeedbackStartRound();)。相邻的taskStatusLed(216 行)确实空体,其注释准确。 - 改法:将 225 行注释改为
@note 当前任务:推进 OTA Boot 请求、刷新 CAN 反馈快照、启动反馈推送轮次。 - 风险:低(纯注释)
- 注意:
taskStatusLed的 216 行注释保持不动(空体桩是 B 类前瞻性桩)
2.3 补充 ota_boot_request.cpp PRIMASK 解释注释
- 文件:
GD32E508_DCU_APP/sys/ota_boot_request.cpp:220 - 现状:
__disable_irq(); NVIC_SystemReset();是全项目唯一使用 PRIMASK 而非 BASEPRI 的位置。这是合法用法(系统复位前最终屏蔽全部中断,防止复位过程中断嵌套),但缺乏解释,后续维护者可能误判为违反项目硬约束。 - 改法:在 220 行前加注释:
/* 复位前最终关中断:必须用 PRIMASK 全屏蔽(非 BASEPRI),确保 NVIC_SystemReset 过程中无任何中断嵌套 */ - 风险:低(纯注释)
第三组:重复代码提取(4 项)
3.1 提取 spinDelay 延时循环(CANFD.cpp)
- 文件:
GD32E508_DCU_APP/device/CANFD.cpp - 现状:9 处
for (volatile uint32_t d = 0; d < NNNNN; ++d) {}纯延时循环散落在hardResetCan、runSelfTestFrame、init退出 init mode 兜底等多处。魔数{40000, 50000, 100000, 200000}无命名常量。 - 改法:
- 在文件顶部匿名 namespace 内新增:
/* 纯软件延时循环(不依赖 SysTick,可在任何中断状态下使用) */ static inline void spinDelay(uint32_t loops) { for (volatile uint32_t d = 0; d < loops; ++d) { } } - 把以下纯延时循环(无 break/条件)替换为
spinDelay(N);:- 94 行:
for (volatile uint32_t dly = 0; dly < 40000U; ++dly) { }→spinDelay(40000U); - 120 行:
for (volatile uint32_t d = 0; d < 100000U; ++d) {}→spinDelay(100000U); - 125 行:
for (volatile uint32_t d = 0; d < 50000U; ++d) {}→spinDelay(50000U); - 130 行:
for (volatile uint32_t d = 0; d < 200000U; ++d) {}→spinDelay(200000U); - 142 行:需确认是否纯延时(读取后判断)
- 382 行:需确认是否纯延时
- 384 行:需确认是否纯延时
- 94 行:
- 不替换带有条件 break 的轮询等待循环(如 355 行
for (...) { if ((CAN_STAT(...) & CAN_STAT_IWS) == 0U) break; })——这些不是纯延时,保持原样
- 在文件顶部匿名 namespace 内新增:
- 风险:低(纯结构提取,延时循环行为不变)
- 注意:执行时需逐一 Read 确认 134/142/382/384 行的实际内容,仅替换纯延时循环
3.2 提取 armRingPending 赋值模式(app_spi_command.cpp)
- 文件:
GD32E508_DCU_APP/device/spi/app_spi_command.cpp - 现状:7 处重复的"按铃挂起"赋值模式:
出现在 516-517, 556-557, 580-581, 604-605, 630-631, 699-700, 748-749 行。
m_ringPending = true; m_ringDeadline = SysTickTimer::getMs() + kRingTimeoutMs; - 改法:
- 在
app_spi_command.h的AppSpiCommandprivate 段新增声明:void armRingPending(); - 在
app_spi_command.cpp新增实现:void AppSpiCommand::armRingPending() { m_ringPending = true; m_ringDeadline = SysTickTimer::getMs() + kRingTimeoutMs; } - 把 7 处两行赋值替换为
armRingPending();
- 在
- 风险:低(语义等价提取,两个独立赋值无依赖关系,顺序无关)
- 注意:第 7 处(748-749)原顺序为先
m_ringDeadline后m_ringPending,提取后顺序统一为方法内顺序,不影响语义
3.3 writeU32Le 跨文件去重
- 文件:
GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp:53-59(static void writeU32Le)GD32E508_DCU_APP/device/spi/app_spi_cmd_id_snapshot.cpp:33-39(inline void writeU32Le)
- 现状:两个 SPI 命令处理器的匿名 namespace 各自定义了字节序相同的
writeU32Le(uint8_t *dst, uint32_t val),实现完全一致(仅参数名 value/val 不同)。 - 改法:
- 在
GD32E508_DCU_APP/device/spi/app_spi_command_defs.h末尾(SpiDefsnamespace 外或内,视现有结构而定)新增: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); } - 删除
app_spi_cmd_can_feedback.cpp:53-59的本地writeU32Le定义 - 删除
app_spi_cmd_id_snapshot.cpp:33-39的本地writeU32Le定义 - 确认两个 .cpp 已
#include "device/spi/app_spi_command_defs.h"(大概率已包含,因它们使用SpiDefs::kType*常量)
- 在
- 风险:低(纯函数提取,实现完全一致)
3.4 合并 adc.cpp 双重匿名 namespace
- 文件:
GD32E508_DCU_APP/device/adc.cpp:12-27, 29-35 - 现状:同一文件有两个独立
namespace { ... }块:- 第一个(12-27):含
kMinValidVdda等校验常量与isValidAdcRaw/isFiniteFloat - 第二个(29-35):含
kDmaPeriph/kDmaChannel/s_dmaBuffer等 DMA 配置 - 语义上都属于文件内静态,无必要分两块
- 第一个(12-27):含
- 改法:合并为单一
namespace { ... }块,用空行 + 注释分隔逻辑分组:namespace { /* ---- 校验常量与辅助函数 ---- */ constexpr float kMinValidVdda = 2.7f; ... (原第一块内容) /* ---- DMA 配置 ---- */ constexpr uint32_t kDmaPeriph = DMA0; ... (原第二块内容) } // namespace - 风险:低(纯结构调整)
Assumptions & Decisions
假设
- 上一轮
purge-legacy-dead-codespec 的所有修改已合并且构建通过(已确认:Code=47462) task_runner.cpp已#include "device/adc.h"(因Adc::collectSample在 319 行被调用)- 两个 SPI 命令处理器 .cpp 已
#include "device/spi/app_spi_command_defs.h"(因使用SpiDefs::kType*常量) - CMake 构建脚本未显式安装
sys/hardfault.h(头文件通常不被列为编译目标)
决策
- 3.1 spinDelay 仅提取纯延时循环:带条件 break 的轮询等待循环(如 355 行)保持原样,避免改变语义
- 1.6 TempSensor 保留类本身:仅删冗余别名,
TempSensor::init()作为语义门面保留(initial.cpp:45仍调用) - 不处理 3 项中风险项:CANFD.cpp 文件名大小写、init() 重构、volatile 审计——需独立 spec
- 不主动清理 #include:即使某头文件因移除类型而变冗余,也不删除 #include(避免过度改动,构建不会因此报错)
Verification Steps
1. Grep 验证(清除项零残留)
canfd_diag_t→ 0 匹配m_ringTxLen→ 0 匹配s_pushCount→ 0 匹配kTypeCanSend|kTypeAppPing|kRspAck|kRspWait(在 app_spi_command.cpp 内)→ 0 匹配#include.*hardfault.h→ 0 匹配temp_sensor_data_t→ 0 匹配writeU32Le(在 app_spi_cmd_can_feedback.cpp 和 app_spi_cmd_id_snapshot.cpp 内)→ 0 匹配(仅 app_spi_command_defs.h 中有定义)
2. Grep 验证(新增项存在)
spinDelay在 CANFD.cpp 中出现 ≥1 次(定义)+ N 次(调用)armRingPending在 app_spi_command.h 中出现 1 次(声明)+ app_spi_command.cpp 中出现 1 次(定义)+ 7 次(调用)writeU32Le在 app_spi_command_defs.h 中出现 1 次(定义)BASEPRI 临界区在 task_manager.h:45 出现 1 次推进 OTA Boot 请求在 task_runner.cpp 出现 1 次复位前最终关中断在 ota_boot_request.cpp 出现 1 次
3. 构建验证
cmake --build g:\Robot_v2\DCU\DCU_APP\tmp\1 --clean-first- 0 编译错误
- 0 新增警告(
#warning仅来自上一轮 C1 的 EXMC,不计入) - Program Size: Code 应略小于 47462(因删除了死代码)
4. 活跃代码完整性核查
Adc::collectSample仍被 task_runner.cpp 引用TempSensor::init仍被 initial.cpp:45 引用m_txLen/m_ringPending/m_ringDeadline成员完整保留SpiDefs::kTypeCanSend等常量在 app_spi_command_defs.h 中完整保留(仅本地别名删除)HardFault_Handler/HardFault_Handler_C在 hardfault.cpp 中完整保留
任务分组与执行顺序
可并行执行的三组(文件集不交叠)
组 A:device/spi/ 目录(1.2 + 1.3 + 1.4 + 3.2 + 3.3)
- 文件:
app_spi_command.h/.cpp、app_spi_cmd_can_feedback.cpp、app_spi_cmd_id_snapshot.cpp、app_spi_command_defs.h - 注意:3.2(armRingPending)和 1.2(m_ringTxLen)都改
app_spi_command.h/.cpp,需顺序执行
组 B:device/ 目录(1.1 + 1.6 + 3.1 + 3.4)
- 文件:
canfd.h、CANFD.cpp、temp_sensor.h、task_runner.cpp、adc.cpp - 注意:1.6 改
temp_sensor.h+task_runner.cpp;3.1 改CANFD.cpp
组 C:sys/ 目录(1.5 + 2.1 + 2.2 + 2.3)
- 文件:
hardfault.h(删除)、task_manager.h、task_runner.cpp、ota_boot_request.cpp - 注意:2.2 和 1.6 都改
task_runner.cpp,需顺序执行或合并到同一子代理
建议执行顺序
- 先并行执行组 A、组 B、组 C(三组文件集基本不交叠,仅
task_runner.cpp跨组) task_runner.cpp的改动(1.6 改类型 + 2.2 改注释)合并到同一子代理- 最后执行构建验证