- 将原有的 monolithic app_spi_command.cpp 拆分为多个专门的命令处理器文件 - 新增 spi_command_handler.h 定义命令处理器接口和上下文结构 - 新增 app_spi_command_defs.h 提取公共协议常量定义 - 新增多个具体命令处理器实现文件: - app_spi_cmd_boot_request.cpp (进入Bootloader命令) - app_spi_cmd_ota_wait.cpp (OTA等待响应命令) - app_spi_cmd_ping.cpp (APP心跳ping命令) - app_spi_cmd_can_bridge.cpp (SPI到CAN桥接命令) - 修改 app_spi_command.cpp 仅负责SPI链路管理和命令路由逻辑 - 更新配置文件添加新源文件到项目编译列表 - 调整全局实例为对象实例(AppSpiCommandBus),移除静态方法依赖 - 更新README文档说明新的SPI命令总线拆分架构 - 微调版本号(patch: 0 -> 1, build: 2 -> 0)
551 lines
13 KiB
Markdown
551 lines
13 KiB
Markdown
# 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 层**:芯片启动文件、链接脚本、厂商外设驱动
|
||
|
||
当前整体执行逻辑是:
|
||
|
||
1. `main.cpp` 调用 `app_init()` 完成硬件初始化
|
||
2. `task_runner_init()` 初始化任务运行时数据
|
||
3. `Timer` 周期中断驱动 `task_manager` 置位任务标志
|
||
4. 主循环中 `task_runner_run_pending()` 读取并执行任务
|
||
5. `ADC / CANFD / temp_sensor / usart0` 等模块提供具体功能
|
||
|
||
---
|
||
|
||
## 2. 推荐阅读顺序
|
||
|
||
如果第一次接手这个工程,建议按这个顺序看:
|
||
|
||
1. `main.cpp`
|
||
2. `main.h`
|
||
3. `sys/initial.*`
|
||
4. `sys/task_manager.*`
|
||
5. `sys/task_runner.*`
|
||
6. `device/Timer.*`
|
||
7. `device/ADC.*`
|
||
8. `device/temp_sensor.*`
|
||
9. `device/CANFD.*`
|
||
10. `device/usart0.*`
|
||
11. `sys/systick.*`
|
||
12. `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_t`
|
||
- 定义状态发送载荷结构 `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.0`
|
||
- `gd32e50x_adc.c.base@1.5.0`
|
||
- `startup_gd32e50x_cl.s.base@1.5.0`
|
||
|
||
这些文件通常表示:
|
||
|
||
- 原始基线版本
|
||
- 或工具生成时附带的参考副本
|
||
|
||
**建议:**
|
||
- 默认不要动
|
||
- 真要比对升级差异时再看
|
||
|
||
---
|
||
|
||
## 9. 当前应用层模块关系图(逻辑上)
|
||
|
||
```text
|
||
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.cpp`
|
||
- `device/CANFD.cpp`
|
||
- `device/ADC.cpp`
|
||
- `sys/task_manager.cpp`
|
||
|
||
### 不建议随便改的位置
|
||
除非你明确知道在干什么,否则不要轻易改:
|
||
- `RTE/Device/GD32E508VE/startup_gd32e50x_cl.s`
|
||
- `RTE/Device/GD32E508VE/system_gd32e50x.c`
|
||
- `RTE/Device/GD32E508VE/gd32e50x_*.c`
|
||
- 链接脚本相关文件
|
||
|
||
---
|
||
|
||
## 12. 后续建议
|
||
|
||
如果准备继续把这个项目做成更长期可维护的结构,下一步最值的动作是:
|
||
|
||
1. 增加 `protocol/` 或 `frames/` 目录,专门放 CANFD 帧定义
|
||
2. 将 `task_runner.cpp` 按任务领域继续拆文件
|
||
3. 给应用层全部源码统一补齐函数级注释和参数说明
|
||
4. 根据目标总线速率进一步细化 CANFD nominal/data timing 参数
|
||
|
||
---
|
||
|
||
如果后面代码结构继续变化,记得同步更新本 README,避免它变成过期文档。
|