进料口从「只有 ESP32 SPI 一条」扩成两条,中继引擎不变:主站经 CoE 0x2511 数据窗 把电机固件明文灌进 GD32 的 48KB SRAM 暂存区,再由 comm/ecat_motor_ota 泵进原有 4KB 环缓;AES 握手、CAN 单发停等、重发、0AAC 运行确认全部沿用 MotorOtaRelay。 纯网线(ESP32 WiFi 关断、/motor/ota 打不通)场合因此也能刷电机。 偏移/幂等判据仍只有一份:feedData 拆出 feedAt,两个进料口共用。 同时把审计报告 VULN-12/13/14 落地(此前只存在于文档,一行未实现):start() 第一条 语句在 PRIMASK 内原子置位 s_channel_lock_mask,EtherCAT 桥接 sendBatch/processOutput 与 SPI 0xC0/0xC7 对锁定通道回新码 0xE7=「被电机 OTA 独占」,不冒充「邮箱满」0xE4。 反向互斥一并补上:电机 OTA 活跃时 requestEnterBoot() 直接拒绝——SPI 0xB0、CoE 0x2502、 CAN 0x320 三条交接入口共用这一个判据,避免复位把刷写腰斩在 erase 之后。 完整性口径按链路分开,不互相冒充:块级 CRC32 + 整镜像 CRC32 都在从站核,且核过才 让电机进 BOOT——失败时电机仍跑旧 APP、擦除没开始,不存在「先擦再验」的变砖路径。 CRC 为 CRC-32/B(poly 0x04C11DB7,初值 0xFFFFFFFF,无反射无末尾异或),与 v2_crc32 同算法(用公开向量 123456789=0x0376E6E7 对过)。 块长取 1024 而不是 BOOT 用的 4096:SSC 的分段下载缓冲是全工程唯一的 malloc 用户, 而 APP 的 __HEAP_SIZE 只有 3KB,4096 会被从站当场回 abort 0x05040005 Out of memory (BOOT 的堆是 16KB 才撑得住 4096——块长能抄,堆抄不了)。这是跨仓常量,须与 tools/ecat 的 MFO_CHUNK_OBJ 同步。 APP 1.7.4(RAM ZI 97156)。已实测:0x2510 参数写与一块 1016B 数据写均被从站接受 (staged=1016、phase bit4);端到端电机刷写待复测(native 块长修复后未跑完整一轮)。
GD32E508_DCU_APP README
本 README 用来介绍 /workzone/ROBOT_V2/DCU/DCU_APP/GD32E508_DCU_APP 目录下各个代码文件的作用、分层关系和阅读顺序。
1. 项目概览
这是一个基于 GD32E508 的 DCU 应用工程,当前代码已经整理为:
- 应用入口层:
main.cpp/main.h - 系统层(sys):负责初始化、节拍、任务调度、任务执行
- 设备层(device):负责 ADC、CANFD、Timer、串口、温度采样封装,以及 SPI 命令总线与命令处理器拆分
- RTE / Device 层:芯片启动文件、链接脚本、厂商外设驱动
当前整体执行逻辑是:
main.cpp调用app_init()完成硬件初始化task_runner_init()初始化任务运行时数据Timer周期中断驱动task_manager置位任务标志- 主循环中
task_runner_run_pending()读取并执行任务 ADC / CANFD / temp_sensor / usart0等模块提供具体功能
2. 推荐阅读顺序
如果第一次接手这个工程,建议按这个顺序看:
main.cppmain.hsys/initial.*sys/task_manager.*sys/task_runner.*device/Timer.*device/ADC.*device/temp_sensor.*device/CANFD.*device/usart0.*sys/systick.*RTE/Device/GD32E508VE/*
这样能先看清主流程,再逐层下钻。
3. 应用层代码文件说明
3.1 根目录文件
main.cpp
作用:
- 应用主入口
- 调用系统初始化
- 启动任务运行时
- 进入主循环执行待处理任务
当前职责很轻:
- 不直接处理 ADC
- 不直接处理 CANFD
- 不直接实现业务任务逻辑
这是当前工程的“总入口 + 调度外壳”。
main.h
作用:
- 全局基础头文件
- 引入
RTE_Components.h和芯片头文件 - 定义系统任务枚举
system_task_t - 定义调度器状态结构
task_scheduler_t - 声明全局调度器实例
g_scheduler
这里的核心内容:
- 任务类型定义
- 调度节拍状态定义
可以把它理解为“任务系统公共协议头”。
4. 系统层 sys/ 文件说明
sys/initial.h
作用:
- 声明应用初始化函数
app_init()
这是初始化入口的头文件。
sys/initial.cpp
作用:
- 实现
app_init() - 负责按顺序初始化各基础模块
当前初始化内容包括:
- SysTick
- USART0 GPIO
- USART0 串口
- 温度采样模块
- CANFD 模块
- 任务调度器
- 用户定时器
这是系统启动时的“初始化编排器”。
sys/systick.h
作用:
- 声明系统毫秒节拍相关接口
当前提供:
systick_config_ms()delay_ms()
sys/systick.cpp
作用:
- 实现 SysTick 中断处理
- 维护毫秒计数器
- 提供毫秒级阻塞延时
当前功能:
SysTick_Handler():系统滴答中断delay_ms():基于滴答计数的阻塞延时systick_config_ms():配置 1ms 节拍
这是系统时间基准模块。
sys/task_manager.h
作用:
- 声明任务调度器接口
当前提供:
task_manager_init()task_manager_tick_100us()task_manager_take_pending()task_manager_set()
sys/task_manager.cpp
作用:
- 维护全局任务标志位和节拍计数
- 根据 100us 节拍分发不同周期任务
当前任务节拍划分:
- 500us:采样 VREF 原始值
- 1ms:采样温度原始值 + 计算工程值
- CAN RX 中断:置位接收解析任务,主循环按预算尽快消费
- 10ms:保留系统 10ms 节拍计数
- 100ms:CAN 发送准备 + CAN 发送 + 状态灯任务
- 1s:心跳任务 + 诊断打印任务
这是“什么时候执行任务”的核心模块。
sys/task_runner.h
作用:
- 声明任务执行器接口
当前提供:
task_runner_init()task_runner_run_pending()
sys/task_runner.cpp
作用:
- 真正执行任务逻辑
- 保存应用运行时缓存
- 将任务位映射到具体函数
当前内部任务包括:
- ADC 原始采样任务
- ADC 工程值计算任务
- CAN RX 轮询任务
- CAN RX 解析任务
- CAN TX 准备任务
- CAN TX 发送任务
- 状态灯任务(占位)
- 心跳任务(占位)
- 诊断打印任务
当前内部运行时缓存:
- 最近一次温度采样结果
- 最近一次 VREF 原始值
- 最近一次温度原始值
- 最近一次接收 CAN 帧 ID / 长度 / 数据
这是“执行什么任务”的核心模块。
5. 设备层 device/ 文件说明
SPI 命令总线拆分说明
device/spi_command_handler.h:SPI 命令处理器抽象与上下文结构。device/app_spi_command_defs.h:APP 运行期 SPI 协议公共常量。device/app_spi_command.cpp:当前仅负责 SPI 链路和命令路由。device/app_spi_cmd_boot_request.cpp:进入 Bootloader 的 SPI 命令处理器。device/app_spi_cmd_ota_wait.cpp:OTA 相关命令的 WAIT 响应处理器。device/app_spi_cmd_ping.cpp:APP Ping 命令处理器。device/app_spi_cmd_can_bridge.cpp:SPI→CAN 桥接命令处理器。- 新增 SPI 业务时优先新增
device/app_spi_cmd_*.cpp,而不是继续膨胀app_spi_command.cpp。
device/adc.h
作用:
- 定义 ADC 采样结果结构
adc_sample_t - 声明 ADC 硬件初始化、采样、换算接口
主要接口:
adc_hw_init()adc_single_read()adc_read_vref_raw()adc_read_temp_raw()adc_calculate_vdda()adc_calculate_temperature()adc_collect_sample()
device/ADC.cpp
作用:
- 实现内部温度/VREF 的 ADC 采样流程
- 提供原始值读取与工程值换算
当前能力:
- 初始化 ADC0
- 读取内部参考电压
- 读取内部温度通道
- 根据参考电压估算 VDDA
- 根据内部温度特性曲线估算温度
- 输出完整采样结构
这是 ADC 采样核心模块。
device/temp_sensor.h
作用:
- 对温度采样模块做轻量封装
- 将
adc_sample_t别名为temp_sensor_data_t
主要接口:
temp_sensor_init()temp_sensor_sample()
device/temp_sensor.cpp
作用:
- 基于 ADC 模块提供温度采样初始化和采样包装
本质上:
temp_sensor_init()→ 调用adc_hw_init()temp_sensor_sample()→ 调用adc_collect_sample()
这是一个轻量适配层,让上层按“温度传感器模块”来理解,而不直接依赖 ADC 细节。
device/usart0.h
作用:
- 声明 USART0 串口相关接口
当前提供:
usart0_gpio_config()usart0_config()fputc()
device/usart0.cpp
作用:
- 配置 USART0 的 GPIO 和串口参数
- 重定向
printf到 USART0
主要用途:
- 启动信息输出
- 运行时诊断打印
这是当前工程的串口调试输出模块。
device/Timer.h
作用:
- 声明用户任务定时器初始化接口
当前提供:
user_timer_init()
device/Timer.cpp
作用:
- 配置 TIMER2
- 配置 TIMER2 中断
- 在中断里驱动任务系统节拍
当前逻辑:
user_timer_init()完成 TIMER2 配置与 NVIC 配置TIMER2_IRQHandler()在每次更新中断中调用task_manager_tick_100us()
这是任务节拍的硬件入口模块。
device/canfd.h
作用:
- 定义 CANFD 模块对外接口
- 定义诊断结构
CanFd::Diag - 定义状态发送载荷结构
canfd_status_payload_t
主要接口:
canfd_init()canfd_poll()canfd_prepare_status()canfd_send_prepared()canfd_has_rx_frame()canfd_consume_rx_frame()canfd_get_diag()
device/canfd.cpp
作用:
- 初始化 CANFD 工作模式
- RX0 中断批量搬空硬件 FIFO0
- 按通道缓存接收帧
- 构建状态发送帧
- 发送准备好的 FD 帧
- 维护 CANFD 诊断统计信息
内部拆分特点:
- RX 和 TX 已拆成两段式
- 接收侧:
rx interrupt -> queue -> consume - 发送侧:
prepare -> send
当前内部缓存:
s_rxQueues:三路接收环形缓冲s_txQueues:三路待发送环形缓冲g_can_diag:收发与错误统计
这是当前最重要的通信模块之一。
6. RTE / Device 层文件说明
下面这些文件主要属于:
- 芯片启动代码
- 链接脚本
- 厂商标准外设驱动
- 自动生成的运行环境文件
原则:
- 这些文件通常不是业务逻辑主战场
- 不建议随意直接修改
- 如果一定要改,最好确认是可持续维护的变更
RTE/_Debug_GD32E508VE/RTE_Components.h
作用:
- 自动生成的组件配置头文件
- 指定当前工程启用哪些外设组件
- 定义
CMSIS_device_header
这是编译入口层配置文件之一。
RTE/Device/GD32E508VE/startup_gd32e50x_cl.s
作用:
- 启动汇编文件
- 定义中断向量表
- 复位入口
- 弱符号中断处理默认实现
这是 MCU 上电启动时最底层的入口。
RTE/Device/GD32E508VE/system_gd32e50x.c
作用:
- 系统时钟配置与系统变量维护
- 提供
SystemCoreClock相关支持
这是 CMSIS 系统层实现文件。
RTE/Device/GD32E508VE/ac6_linker_script.sct.src
作用:
- AC6 工具链链接脚本模板
- 控制代码段、数据段、栈、堆等布局
RTE/Device/GD32E508VE/regions_GD32E508VE.h
作用:
- 链接内存区域定义头文件
- 配合链接脚本使用
7. 厂商外设驱动源文件
以下文件属于厂商标准外设驱动,主要为 gd32e50x_xxx.c:
gd32e50x_adc.c
作用: ADC 外设驱动实现。
gd32e50x_can.c
作用: CAN / CANFD 相关底层驱动实现。 说明: 当前项目的 CANFD 能力依赖这个文件中的 GD32E508 分支实现。
gd32e50x_crc.c
作用: CRC 外设驱动实现。
gd32e50x_dbg.c
作用: 调试控制相关外设驱动实现。
gd32e50x_dma.c
作用: DMA 外设驱动实现。
gd32e50x_exti.c
作用: 外部中断控制驱动实现。
gd32e50x_fmc.c
作用: Flash / FMC 相关驱动实现。
gd32e50x_gpio.c
作用: GPIO 外设驱动实现。
gd32e50x_misc.c
作用: 中断优先级 / 杂项系统控制驱动实现。
gd32e50x_pmu.c
作用: 电源管理单元驱动实现。
gd32e50x_rcu.c
作用: 时钟控制单元驱动实现。
gd32e50x_spi.c
作用: SPI 外设驱动实现。
gd32e50x_usart.c
作用: USART 外设驱动实现。
8. .base@版本号 文件说明
例如:
gd32e50x_can.c.base@1.0.0gd32e50x_adc.c.base@1.5.0startup_gd32e50x_cl.s.base@1.5.0
这些文件通常表示:
- 原始基线版本
- 或工具生成时附带的参考副本
建议:
- 默认不要动
- 真要比对升级差异时再看
9. 当前应用层模块关系图(逻辑上)
main.cpp
├─ sys/initial.cpp
│ ├─ sys/systick.cpp
│ ├─ device/usart0.cpp
│ ├─ device/temp_sensor.cpp
│ │ └─ device/ADC.cpp
│ ├─ device/canfd.cpp
│ ├─ sys/task_manager.cpp
│ └─ device/Timer.cpp
│ └─ TIMER2_IRQHandler -> task_manager_tick_100us()
│
└─ sys/task_runner.cpp
├─ device/ADC.cpp
├─ device/canfd.cpp
└─ sys/task_manager.cpp
10. 当前代码特点总结
已完成的结构化工作
- 应用层
.c已整理为.cpp main已瘦身- 任务管理与任务执行已分离
- CANFD 收发流程已拆成两段式
- CANFD 接收已切换为中断事件驱动和环形缓冲
- ADC / 温度 / 定时器 / 串口 已模块化
当前仍可继续演进的方向
- 将
task_runner.cpp再拆成task_adc.cpp / task_can.cpp / task_diag.cpp - 将协议帧定义抽到单独文件中
- 完善状态灯任务与心跳任务
11. 维护建议
建议优先修改的位置
如果是改业务逻辑,优先看:
sys/task_runner.cppdevice/canfd.cppdevice/ADC.cppsys/task_manager.cpp
不建议随便改的位置
除非你明确知道在干什么,否则不要轻易改:
RTE/Device/GD32E508VE/startup_gd32e50x_cl.sRTE/Device/GD32E508VE/system_gd32e50x.cRTE/Device/GD32E508VE/gd32e50x_*.c- 链接脚本相关文件
12. 后续建议
如果准备继续把这个项目做成更长期可维护的结构,下一步最值的动作是:
- 增加
protocol/或frames/目录,专门放 CANFD 帧定义 - 将
task_runner.cpp按任务领域继续拆文件 - 给应用层全部源码统一补齐函数级注释和参数说明
- 根据目标总线速率进一步细化 CANFD nominal/data timing 参数
如果后面代码结构继续变化,记得同步更新本 README,避免它变成过期文档。