1.6.14 CAN-FD 数据段 2Mbps→5Mbps:CONFIG_CAN_DATA_PRESCALER 9→3、BS1 3→4TQ (90MHz/(3×6TQ),采样点 80%→83.3%),仲裁段仍 1M。TDC filter/offset 维持 4 不做 等比缩放(TDCMOD=CALC_AND_OFFSET 下硬件已自动测回路延时;放到 12 会让本节点回采 错开约 6 个数据位,自发热帧被判错误帧)。配套电机 APP/BOOT data_prescaler=2。 1.6.15 段4 批量接收由"环形队列 + 变长紧排排空"改为 5×44B 定长覆盖窗口,输入 PDO 496→600(75×64bit),段5 诊断区 484→584。动机两条:现场 4000 帧/s 超旧容量 3 帧/拍 恒丢 1 帧;更关键是排空赤字把队列长期压在饱和态,而 1.6.9~1.6.13 追的错标/拼接竞态 正是饱和态喂出来的(判满用的 tail 滞后→生产者写进消费者刚读过的槽)。定长消灭 tail/判满/pending,边界越界写在结构上不可能再犯。每槽带 u32 seqlock:buildInput 在 PDI_Isr(preempt=0) 抢占主循环写侧,无它则主站会拿到"新 ID + 旧数据"拼出的假帧。 out+in=984 ≤ 1024 硬上限,DPRAM 留 120B。语义变更:批量区从"不丢的事件流"变"最近 N 帧窗口",去重与丢帧判据移交主站按 seq 归一。三端同步 size.h/bridge/dc_app/ gen_eeprom/DCU_ESI.xml,必须重烧固件 + 烧 EEPROM + 断电冷启动,只烧一边 SAFEOP 报 AL 0x001A/0x0011。 1.6.16 段1/段3 快照 drop 计数口径修复(纯判据,PDO 布局逐字节不动):旧判据 s_rxValid 置位后永不清零 ⇒ 开机首帧后每帧都算丢弃,drop ≡ rx−1,是零灵敏度假仪表 且把网页状态栏刷成 ERR。新增 s_rxPublished,判据改 s_rxValid && !s_rxPublished。 1.6.17~19 [DIFF-STATS] 差分写取证仪表,零行为变更,只加 0x2000 sub13..23(11 个只读 UINT32) ⇒ 不动 PDO 长度/映射,无需重生成 SII。实测:每拍 27.4% 字节真变化(164B)、 run 峰值 12~13、段2 占变化量 ~53%、段4 ~25%。 1.6.19 推翻 1.6.18 的自我诊断:比对开销虚报不是 -Oz 把 memcpy 编成调用(手拼移位 反而 21.3µs→41µs),真根因是 alignas(4) 到 const uint8_t* 形参处丢失,优化器无法 证明 4 字节对齐。__builtin_assume_aligned + memcpy 后循环体缩成 ldr/ldr/cmp/bne、 bl 归零。教训:测量工具自身的开销会直接变成结论。 1.6.20 基线对照版(当前提交状态 CONFIG_ECAT_DIFF_STATS=0):同工况(电机在线无指令) 实测 load 49.1% vs 仪表开的 51.6% ⇒ 仪表自身成本 2.4 个点,差分改造收益 = 0。 按实测变化量推算 A1(段粒度条件写) 只值约 1 点(段2/段4 每拍都变,必然整块重写), 仅 A2(8B 块粒度 + run 上限退化为整写) 有空间 —— 推算值,未做 A/B 实测不作为结论。
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,避免它变成过期文档。