refactor(device): 移除备份配置文件并完善ADC模块注释

移除不再需要的 .cproject.yml.bak_dma 和 README.md 备份文件,
同时为 ADC.cpp 添加完整的 Doxygen 风格函数注释,提升代码可读性。
This commit is contained in:
cbb
2026-05-10 21:27:51 +08:00
parent 4b8b47bc9b
commit 078ea06317
77 changed files with 1326 additions and 6074 deletions
@@ -1,82 +0,0 @@
# A project translates into one executable or library.
project:
groups:
- group: Source Files
files:
- file: ./main.h
- file: ./main.cpp
- group: sys
files:
- file: ./sys/initial.h
- file: ./sys/initial.cpp
- file: ./sys/systick.h
- file: ./sys/systick.cpp
- file: ./sys/task_manager.h
- file: ./sys/task_manager.cpp
- file: ./sys/task_runner.h
- file: ./sys/task_runner.cpp
- file: ./sys/config.h
- group: device
files:
- file: ./device/usart0.h
- file: ./device/usart0.cpp
- file: ./device/adc.h
- file: ./device/ADC.cpp
- file: ./device/canfd.h
- file: ./device/CANFD.cpp
- file: ./device/Timer.h
- file: ./device/Timer.cpp
- file: ./device/temp_sensor.h
- file: ./device/temp_sensor.cpp
# List components to use for your application.
# A software component is a re-usable unit that may be configurable.
components:
- component: ARM::CMSIS:CORE
- component: Device:Startup
- component: Device:GD32E50x_StdPeripherals:ADC
- component: Device:GD32E50x_StdPeripherals:CAN
- component: Device:GD32E50x_StdPeripherals:CRC
- component: Device:GD32E50x_StdPeripherals:DBG
- component: Device:GD32E50x_StdPeripherals:DMA
- component: Device:GD32E50x_StdPeripherals:EXTI
- component: Device:GD32E50x_StdPeripherals:FMC
- component: Device:GD32E50x_StdPeripherals:GPIO
- component: Device:GD32E50x_StdPeripherals:MISC
- component: Device:GD32E50x_StdPeripherals:PMU
- component: Device:GD32E50x_StdPeripherals:RCU
- component: Device:GD32E50x_StdPeripherals:SPI_I2S
- component: Device:GD32E50x_StdPeripherals:TIMER
- component: Device:GD32E50x_StdPeripherals:USART
- component: Device:GD32E50x_libopt
# List executable file formats to be generated.
output:
type:
- elf
- hex
- map
misc:
- for-compiler: AC6
C-CPP:
- -DGD32E508
- -DGD32E50X
- -gdwarf-5
- -D__MICROLIB
- -fshort-enums
- -fshort-wchar
- -Wno-register
C:
- -std=gnu11
CPP:
- -std=gnu++17
ASM:
- -DGD32E508
- -DGD32E50X
- -gdwarf-5
- -D__MICROLIB
Link:
- --library_type=microlib
- --entry=Reset_Handler
-539
View File
@@ -1,539 +0,0 @@
# 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、串口、温度采样封装
- **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:采样温度原始值 + 计算工程值
- 10ms:CAN 接收轮询 + 接收解析
- 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/` 文件说明
### `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 工作模式
- 轮询接收 FIFO
- 缓存接收帧
- 构建状态发送帧
- 发送准备好的 FD 帧
- 维护 CANFD 诊断统计信息
**内部拆分特点:**
- RX 和 TX 已拆成两段式
- 接收侧:`poll -> cache -> consume`
- 发送侧:`prepare -> send`
**当前内部缓存:**
- `g_rx_frame`:单帧接收缓存
- `g_tx_buffer`:待发送帧缓存
- `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 收发流程已拆成两段式
- ADC / 温度 / 定时器 / 串口 已模块化
### 当前仍可继续演进的方向
- 将 `task_runner.cpp` 再拆成 `task_adc.cpp / task_can.cpp / task_diag.cpp`
- 将 CANFD 单帧缓存升级为轻量环形缓冲
- 将协议帧定义抽到单独文件中
- 完善状态灯任务与心跳任务
---
## 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,避免它变成过期文档。
+80
View File
@@ -18,6 +18,13 @@ volatile uint16_t s_dmaBuffer[kChannelCount] = {0U, 0U, 0U};
volatile uint8_t s_dmaReady = 0U;
} // namespace
/**
* @brief 初始化 ADC DMA 循环搬运。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 配置 DMA0 Channel0,将 ADC 规则组结果连续搬运到 `s_dmaBuffer`。
*/
void Adc::initDma()
{
dma_parameter_struct dma_initpara{};
@@ -40,6 +47,13 @@ void Adc::initDma()
dma_channel_enable(kDmaPeriph, kDmaChannel);
}
/**
* @brief 初始化 ADC、DMA 与规则通道扫描配置。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 按 `VIN_ADC / TEMP / VREFINT` 三通道顺序启用连续扫描与 DMA 采样。
*/
void Adc::init()
{
rcu_periph_clock_enable(RCU_ADC0);
@@ -74,16 +88,35 @@ void Adc::init()
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;
}
/**
* @brief 按逻辑通道号读取一个原始 ADC 数值。
* @param[in] channel 逻辑 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 返回对应通道的原始采样值;若通道不受支持则返回 0。
*/
uint16_t Adc::readRaw(uint8_t channel)
{
if(channel == CONFIG_ADC_VIN_CHANNEL) return s_dmaBuffer[0];
@@ -92,27 +125,74 @@ uint16_t Adc::readRaw(uint8_t channel)
return 0U;
}
/**
* @brief 读取 VIN_ADC 原始采样值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 返回 VIN_ADC 通道原始值。
*/
uint16_t Adc::readVinRaw() { return readRaw(CONFIG_ADC_VIN_CHANNEL); }
/**
* @brief 读取内部参考电压原始采样值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 返回 VREFINT 通道原始值。
*/
uint16_t Adc::readVrefRaw() { return readRaw(CONFIG_ADC_VREFINT_CHANNEL); }
/**
* @brief 读取内部温度通道原始采样值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 返回内部温度通道原始值。
*/
uint16_t Adc::readTempRaw() { return readRaw(CONFIG_ADC_TEMP_CHANNEL); }
/**
* @brief 将 VIN_ADC 原始值换算为 ADC 引脚电压。
* @param[in] vinRaw VIN_ADC 原始采样值。
* @param[in] vdda 当前 ADC 供电电压。
* @param[out] 无。
* @retval float 返回 ADC 引脚电压,单位 V。
*/
float Adc::calculateVinAdcVoltage(uint16_t vinRaw, float vdda)
{
return (static_cast<float>(vinRaw) * vdda) / CONFIG_ADC_MAX_VALUE;
}
/**
* @brief 根据内部参考电压原始值换算当前 VDDA。
* @param[in] vrefRaw 内部参考电压通道原始值。
* @param[out] 无。
* @retval float 返回换算得到的 VDDA,单位 V;若原始值为 0,则返回默认值。
*/
float Adc::calculateVdda(uint16_t vrefRaw)
{
if(vrefRaw == 0U) return CONFIG_ADC_DEFAULT_VDDA;
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vrefRaw);
}
/**
* @brief 根据温度通道原始值换算芯片温度。
* @param[in] tempRaw 温度通道原始值。
* @param[in] vdda 当前 ADC 供电电压。
* @param[out] 无。
* @retval float 返回换算得到的芯片温度,单位 ℃。
*/
float Adc::calculateTemperature(uint16_t tempRaw, float vdda)
{
const float sensorVoltage = (static_cast<float>(tempRaw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensorVoltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 收集一组完整 ADC 采样结果。
* @param[in] sample 指向输出采样结构体的指针。
* @param[out] sample 输出原始值与工程值结果;允许为空。
* @retval 无返回值。
* @note 若 `sample == nullptr`,函数直接返回。
*/
void Adc::collectSample(Sample *sample)
{
if(sample == nullptr) return;
-126
View File
@@ -1,126 +0,0 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#define TEMP_ADC ADC0
#define TEMP_ADC_CHANNEL ADC_CHANNEL_16
#define VREFINT_ADC_CHANNEL ADC_CHANNEL_17
#define ADC_MAX_VALUE 4095.0f
#define VREFINT_TYPICAL_VOLTAGE 1.20f
#define TEMP_V25 1.43f
#define TEMP_AVG_SLOPE 0.0043f
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
adc_deinit(TEMP_ADC);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(TEMP_ADC, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(TEMP_ADC, ADC_CONTINUOUS_MODE, DISABLE);
adc_data_alignment_config(TEMP_ADC, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(TEMP_ADC, ADC_REGULAR_CHANNEL, 1U);
adc_external_trigger_source_config(TEMP_ADC, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(TEMP_ADC, ADC_REGULAR_CHANNEL, DISABLE);
adc_tempsensor_vrefint_enable();
delay_ms(2U);
adc_enable(TEMP_ADC);
delay_ms(2U);
adc_calibration_enable(TEMP_ADC);
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 函数采用阻塞式等待,直到 EOC 置位后返回。
*/
uint16_t adc_single_read(uint8_t channel)
{
adc_regular_channel_config(TEMP_ADC, 0U, channel, ADC_SAMPLETIME_239POINT5);
adc_flag_clear(TEMP_ADC, ADC_FLAG_EOC);
adc_software_trigger_enable(TEMP_ADC, ADC_REGULAR_CHANNEL);
while(RESET == adc_flag_get(TEMP_ADC, ADC_FLAG_EOC)) {
}
return adc_regular_data_read(TEMP_ADC);
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(VREFINT_ADC_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(TEMP_ADC_CHANNEL);
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return 3.3f;
}
return (VREFINT_TYPICAL_VOLTAGE * ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / ADC_MAX_VALUE;
return ((TEMP_V25 - sensor_voltage) / TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->vref_raw = adc_read_vref_raw();
sample->temp_raw = adc_read_temp_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
-119
View File
@@ -1,119 +0,0 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, DISABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, 1U);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_tempsensor_vrefint_enable();
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 函数采用阻塞式等待,直到 EOC 置位后返回。
*/
uint16_t adc_single_read(uint8_t channel)
{
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, channel, CONFIG_ADC_SAMPLE_TIME);
adc_flag_clear(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
while(RESET == adc_flag_get(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC)) {
}
return adc_regular_data_read(CONFIG_ADC_INSTANCE);
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(CONFIG_ADC_TEMP_CHANNEL);
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return CONFIG_ADC_DEFAULT_VDDA;
}
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->vref_raw = adc_read_vref_raw();
sample->temp_raw = adc_read_temp_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
-218
View File
@@ -1,218 +0,0 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kAdcDmaPeriph = DMA0;
constexpr dma_channel_enum kAdcDmaChannel = DMA_CH0;
constexpr uint32_t kAdcChannelCount = 3U;
constexpr uint32_t kVinAdcGpio = GPIOA;
constexpr uint32_t kVinAdcPin = GPIO_PIN_6;
volatile uint16_t g_adc_dma_buffer[kAdcChannelCount] = {0U, 0U, 0U};
volatile uint8_t g_adc_dma_ready = 0U;
/**
* @brief 配置 ADC 原始数据到内存的 DMA 循环搬运。
*/
void adc_dma_init(void)
{
dma_parameter_struct dma_initpara{};
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(kAdcDmaPeriph, kAdcDmaChannel);
dma_struct_para_init(&dma_initpara);
dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE));
dma_initpara.memory_addr = (uint32_t)g_adc_dma_buffer;
dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_initpara.number = kAdcChannelCount;
dma_initpara.priority = DMA_PRIORITY_HIGH;
dma_init(kAdcDmaPeriph, kAdcDmaChannel, &dma_initpara);
dma_circulation_enable(kAdcDmaPeriph, kAdcDmaChannel);
dma_channel_enable(kAdcDmaPeriph, kAdcDmaChannel);
}
} // namespace
/**
* @brief 初始化 ADC 硬件及内部温度/VREF/VIN_ADC 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 三通道扫描、DMA 循环搬运、PA6(VIN_ADC) 模拟输入配置以及内部通道校准。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
/* 原理图:PA6 -> VIN_ADC,为外部电源分压检测输入 */
gpio_init(kVinAdcGpio, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, kVinAdcPin);
adc_dma_init();
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kAdcChannelCount);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_VIN_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 2U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_dma_mode_enable(CONFIG_ADC_INSTANCE);
adc_tempsensor_vrefint_enable();
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
delay_ms(1U);
g_adc_dma_ready = 1U;
}
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void)
{
return g_adc_dma_ready;
}
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 3 的原始缓存指针,顺序为 VIN_ADC、温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void)
{
return (const uint16_t *)g_adc_dma_buffer;
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel)
{
if(channel == CONFIG_ADC_VIN_CHANNEL) {
return g_adc_dma_buffer[0];
}
if(channel == CONFIG_ADC_TEMP_CHANNEL) {
return g_adc_dma_buffer[1];
}
if(channel == CONFIG_ADC_VREFINT_CHANNEL) {
return g_adc_dma_buffer[2];
}
return 0U;
}
/**
* @brief 读取外部 VIN_ADC(PA6)通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 外部 VIN_ADC 通道的原始 ADC 值。
*/
uint16_t adc_read_vin_raw(void)
{
return adc_single_read(CONFIG_ADC_VIN_CHANNEL);
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(CONFIG_ADC_TEMP_CHANNEL);
}
/**
* @brief 根据外部 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。
* @param[in] vin_raw 外部 VIN_ADC 通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float VIN_ADC 引脚电压,单位 V。
* @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。
*/
float adc_calculate_vin_adc_voltage(uint16_t vin_raw, float vdda)
{
return (static_cast<float>(vin_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return CONFIG_ADC_DEFAULT_VDDA;
}
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->vin_raw = adc_read_vin_raw();
sample->temp_raw = adc_read_temp_raw();
sample->vref_raw = adc_read_vref_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->vin_adc_v = adc_calculate_vin_adc_voltage(sample->vin_raw, sample->vdda);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
-182
View File
@@ -1,182 +0,0 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kAdcDmaPeriph = DMA0;
constexpr dma_channel_enum kAdcDmaChannel = DMA_CH0;
constexpr uint32_t kAdcChannelCount = 2U;
volatile uint16_t g_adc_dma_buffer[kAdcChannelCount] = {0U, 0U};
volatile uint8_t g_adc_dma_ready = 0U;
/**
* @brief 配置 ADC 原始数据到内存的 DMA 循环搬运。
*/
void adc_dma_init(void)
{
dma_parameter_struct dma_initpara{};
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(kAdcDmaPeriph, kAdcDmaChannel);
dma_struct_para_init(&dma_initpara);
dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE));
dma_initpara.memory_addr = (uint32_t)g_adc_dma_buffer;
dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_initpara.number = kAdcChannelCount;
dma_initpara.priority = DMA_PRIORITY_HIGH;
dma_init(kAdcDmaPeriph, kAdcDmaChannel, &dma_initpara);
dma_circulation_enable(kAdcDmaPeriph, kAdcDmaChannel);
dma_channel_enable(kAdcDmaPeriph, kAdcDmaChannel);
}
} // namespace
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 双通道扫描、DMA 循环搬运、内部通道使能与校准流程配置。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
adc_dma_init();
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kAdcChannelCount);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_dma_mode_enable(CONFIG_ADC_INSTANCE);
adc_tempsensor_vrefint_enable();
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
delay_ms(1U);
g_adc_dma_ready = 1U;
}
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void)
{
return g_adc_dma_ready;
}
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 2 的原始缓存指针,顺序为温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void)
{
return (const uint16_t *)g_adc_dma_buffer;
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel)
{
if(channel == CONFIG_ADC_TEMP_CHANNEL) {
return g_adc_dma_buffer[0];
}
if(channel == CONFIG_ADC_VREFINT_CHANNEL) {
return g_adc_dma_buffer[1];
}
return 0U;
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(CONFIG_ADC_TEMP_CHANNEL);
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return CONFIG_ADC_DEFAULT_VDDA;
}
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->temp_raw = adc_read_temp_raw();
sample->vref_raw = adc_read_vref_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
+394 -99
View File
@@ -8,36 +8,81 @@
#include "sys/config.h"
namespace {
constexpr uint32_t kSlpGpio = GPIOA;
constexpr uint32_t kSlpPin = GPIO_PIN_8;
struct CanFdTxBuffer {
struct TxFrame {
can_trasnmit_message_struct tx_message;
uint8_t payload[64];
uint8_t valid;
};
CanFdTxBuffer s_txBuffer;
struct TxQueue {
TxFrame frames[4];
uint8_t head;
uint8_t tail;
uint8_t count;
};
struct CanChannelConfig {
uint32_t canPeriph;
rcu_periph_enum canRcu;
uint32_t txPort;
uint32_t txPin;
uint32_t rxPort;
uint32_t rxPin;
uint32_t slpPort;
uint32_t slpPin;
IRQn_Type rx0Irq;
uint8_t filterNumber;
};
constexpr CanChannelConfig kChannels[] = {
{ CAN0, RCU_CAN0, GPIOA, GPIO_PIN_12, GPIOA, GPIO_PIN_11, GPIOA, GPIO_PIN_8, CAN0_RX0_IRQn, 0U },
{ CAN1, RCU_CAN1, GPIOB, GPIO_PIN_12, GPIOB, GPIO_PIN_13, GPIOB, GPIO_PIN_14, CAN1_RX0_IRQn, 1U },
{ CAN2, RCU_CAN2, GPIOE, GPIO_PIN_1, GPIOE, GPIO_PIN_0, GPIOB, GPIO_PIN_9, CAN2_RX0_IRQn, 2U },
};
constexpr size_t kChannelCount = sizeof(kChannels) / sizeof(kChannels[0]);
constexpr uint8_t kRxQueueDepth = 4U;
constexpr uint8_t kTxQueueDepth = 4U;
} // namespace
CanFd::Diag CanFd::s_diag;
CanFd::RxFrame CanFd::s_rxFrame;
CanFd::Diag CanFd::s_diag{};
CanFd::RxQueue CanFd::s_rxQueues[3]{};
uint8_t CanFd::s_nextConsumeChannel = 0U;
static TxQueue s_txQueues[3]{};
/**
* @brief 初始化三路 CANFD 相关 GPIO。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 为 CAN0/CAN1/CAN2 的 TX、RX 及收发器 SLP 控制脚统一完成 GPIO 时钟与模式配置。
*/
void CanFd::initGpio()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kSlpPin);
for(size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
gpio_init(ch.rxPort, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, ch.rxPin);
gpio_init(ch.txPort, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, ch.txPin);
gpio_init(ch.slpPort, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, ch.slpPin);
}
}
void CanFd::filterAcceptAll()
/**
* @brief 为指定 CAN 控制器配置全接收过滤器。
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[in] filterNumber 过滤器编号。
* @param[out] 无。
* @retval 无返回值。
* @note 当前使用 32 位 mask 模式放行全部报文,并统一进入 FIFO0。
*/
void CanFd::filterAcceptAll(uint32_t canPeriph, uint8_t filterNumber)
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_number = filterNumber;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
@@ -46,10 +91,17 @@ void CanFd::filterAcceptAll()
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
can_filter_init(canPeriph, &can_filter);
}
void CanFd::initNominalTiming()
/**
* @brief 配置指定 CAN 控制器的标称仲裁段位时序。
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[out] 无。
* @retval 无返回值。
* @note 当前按 1 Mbps 仲裁段参数完成普通 CAN 初始化。
*/
void CanFd::initNominalTiming(uint32_t canPeriph)
{
can_parameter_struct can_parameter{};
@@ -65,10 +117,17 @@ void CanFd::initNominalTiming()
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
can_init(canPeriph, &can_parameter);
}
void CanFd::initFdMode()
/**
* @brief 配置指定 CAN 控制器的 CAN FD 数据段参数。
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[out] 无。
* @retval 无返回值。
* @note 在 GD32E508 平台下启用 FD、BRS 与 TDC;非该平台则仅使能 FD 功能。
*/
void CanFd::initFdMode(uint32_t canPeriph)
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
@@ -88,30 +147,30 @@ void CanFd::initFdMode()
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
can_fd_init(canPeriph, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
can_fd_function_enable(canPeriph);
#endif
}
/**
* @brief 构造内部发送缓冲中的状态帧内容。
* @param[in] status 指向状态载荷的指针。
* @param[out] 无。
* @retval 无返回值。
* @note 当前实现中状态帧构造逻辑已内联至 `enqueueTxFrame()`,此函数仅保留接口兼容性。
*/
void CanFd::buildStatusFrame(const StatusPayload *status)
{
if(status == nullptr) {
return;
}
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &s_txBuffer.tx_message);
s_txBuffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
s_txBuffer.tx_message.tx_efid = 0U;
s_txBuffer.tx_message.tx_ff = CAN_FF_STANDARD;
s_txBuffer.tx_message.tx_ft = CAN_FT_DATA;
s_txBuffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
s_txBuffer.tx_message.fd_flag = ENABLE;
s_txBuffer.tx_message.fd_brs = ENABLE;
s_txBuffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = s_txBuffer.payload;
auto &frame = s_txQueues[0].frames[0];
auto &p = frame.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
@@ -124,109 +183,345 @@ void CanFd::buildStatusFrame(const StatusPayload *status)
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
}
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
s_txBuffer.tx_message.tx_data[i] = p[i];
/**
* @brief 向指定 CAN 通道的发送 FIFO 入队一帧状态报文。
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
* @param[in] status 指向状态载荷的指针。
* @param[out] 无。
* @retval bool true 表示成功入队,false 表示参数无效或该通道发送队列已满。
* @note 仅完成软件发送队列入队,不会立即触发硬件发送。
*/
bool CanFd::enqueueTxFrame(uint8_t channelIndex, const StatusPayload *status)
{
if(status == nullptr || channelIndex >= kChannelCount) {
return false;
}
s_txBuffer.valid = 1U;
}
void CanFd::init()
{
initGpio();
setNormalMode();
can_deinit(CONFIG_CAN_INSTANCE);
initNominalTiming();
initFdMode();
filterAcceptAll();
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
s_rxFrame.valid = 0U;
s_txBuffer.valid = 0U;
}
void CanFd::poll()
{
/* 已由 CAN0_RX0_IRQHandler 中断驱动 */
}
void CanFd::prepareStatus(const StatusPayload *status)
{
if(status == nullptr) {
s_txBuffer.valid = 0U;
return;
auto &queue = s_txQueues[channelIndex];
if(queue.count >= kTxQueueDepth) {
s_diag.tx_drop_count++;
return false;
}
buildStatusFrame(status);
}
void CanFd::sendPrepared()
{
if(s_txBuffer.valid == 0U) return;
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &s_txBuffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
s_diag.tx_count++;
s_diag.last_payload_length = s_txBuffer.tx_message.tx_dlen;
s_diag.last_tx_id = s_txBuffer.tx_message.tx_sfid;
} else {
s_diag.error_count++;
auto &frame = queue.frames[queue.tail];
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &frame.tx_message);
frame.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
frame.tx_message.tx_efid = 0U;
frame.tx_message.tx_ff = CAN_FF_STANDARD;
frame.tx_message.tx_ft = CAN_FT_DATA;
frame.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
frame.tx_message.fd_flag = ENABLE;
frame.tx_message.fd_brs = ENABLE;
frame.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
const auto &src = s_txQueues[0].frames[0].payload;
auto &p = frame.payload;
for(uint8_t j = 0; j < CANFD_STATUS_DLEN; ++j) {
p[j] = src[j];
frame.tx_message.tx_data[j] = p[j];
}
}
bool CanFd::hasRxFrame()
{
return s_rxFrame.valid != 0U;
}
bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(s_rxFrame.valid == 0U) return false;
if(id) *id = s_rxFrame.id;
if(length) *length = s_rxFrame.length;
if(data) { for(uint8_t i = 0; i < s_rxFrame.length; ++i) data[i] = s_rxFrame.data[i]; }
s_rxFrame.valid = 0U;
queue.tail = static_cast<uint8_t>((queue.tail + 1U) % kTxQueueDepth);
queue.count++;
return true;
}
/**
* @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并清零收发诊断状态。
*/
void CanFd::init()
{
s_diag = {};
s_nextConsumeChannel = 0U;
initGpio();
for(size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
rcu_periph_clock_enable(ch.canRcu);
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
can_deinit(ch.canPeriph);
initNominalTiming(ch.canPeriph);
initFdMode(ch.canPeriph);
filterAcceptAll(ch.canPeriph, ch.filterNumber);
can_interrupt_enable(ch.canPeriph, CAN_INT_RFNE0);
nvic_irq_enable(ch.rx0Irq, 1U, static_cast<uint8_t>(i));
s_rxQueues[i].head = 0U;
s_rxQueues[i].tail = 0U;
s_rxQueues[i].count = 0U;
s_txQueues[i].head = 0U;
s_txQueues[i].tail = 0U;
s_txQueues[i].count = 0U;
}
s_nextConsumeChannel = 0U;
}
/**
* @brief 执行兼容保留的 CAN 轮询入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前三路接收均由 RX0 中断驱动,该函数仅保留旧调用兼容性。
*/
void CanFd::poll()
{
/* 兼容保留:三通道接收已由 RX0 中断驱动 */
}
/**
* @brief 根据状态载荷为三路发送队列批量准备状态报文。
* @param[in] status 指向状态载荷的指针;为空时直接返回。
* @param[out] 无。
* @retval 无返回值。
* @note 当前默认不会周期性调用;仅在外部显式触发发送测试或业务发送时使用。
*/
void CanFd::prepareStatus(const StatusPayload *status)
{
if(status == nullptr) {
return;
}
for(size_t i = 0; i < kChannelCount; ++i) {
(void)enqueueTxFrame(static_cast<uint8_t>(i), status);
}
}
/**
* @brief 根据状态载荷仅为指定通道准备一帧状态报文。
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
* @param[in] status 指向状态载荷的指针;为空时返回失败。
* @param[out] 无。
* @retval bool true 表示成功入队,false 表示参数非法或发送队列已满。
*/
bool CanFd::prepareStatusForChannel(uint8_t channelIndex, const StatusPayload *status)
{
return enqueueTxFrame(channelIndex, status);
}
/**
* @brief 将当前各通道已准备好的状态报文送入硬件邮箱发送。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前默认不会周期性调用;仅处理调用方显式准备过的发送队列内容。
*/
void CanFd::sendPrepared()
{
for(size_t i = 0; i < kChannelCount; ++i) {
auto &queue = s_txQueues[i];
if(queue.count == 0U) {
continue;
}
auto &frame = queue.frames[queue.head];
const uint8_t mailbox = can_message_transmit(kChannels[i].canPeriph, &frame.tx_message);
if(mailbox != CAN_NOMAILBOX) {
s_diag.tx_count++;
s_diag.last_payload_length = frame.tx_message.tx_dlen;
s_diag.last_tx_id = frame.tx_message.tx_sfid;
s_diag.last_tx_channel = static_cast<uint8_t>(i);
queue.head = static_cast<uint8_t>((queue.head + 1U) % kTxQueueDepth);
queue.count--;
} else {
s_diag.error_count++;
}
}
}
/**
* @brief 查询三路 CAN 通道是否存在待消费接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示至少一条接收队列非空,false 表示当前三路均无待消费报文。
*/
bool CanFd::hasRxFrame()
{
for(size_t i = 0; i < kChannelCount; ++i) {
if(s_rxQueues[i].count != 0U) {
return true;
}
}
return false;
}
/**
* @brief 轮转读取并消费一帧接收报文。
* @param[in] 无。
* @param[out] id 输出报文标识符;允许为空。
* @param[out] data 输出报文数据缓存;允许为空。
* @param[out] length 输出报文长度;允许为空。
* @param[out] channel 输出报文来源通道号;允许为空。
* @retval bool true 表示成功消费一帧报文,false 表示当前三路接收队列均为空。
* @note 消费顺序采用从 `s_nextConsumeChannel` 开始的轮转扫描,避免长期偏向固定通道。
*/
bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel)
{
RxFrame localFrame{};
bool found = false;
__disable_irq();
for(size_t n = 0; n < kChannelCount; ++n) {
const uint8_t idx = static_cast<uint8_t>((s_nextConsumeChannel + n) % kChannelCount);
auto &queue = s_rxQueues[idx];
if(queue.count == 0U) {
continue;
}
localFrame = queue.frames[queue.head];
queue.head = static_cast<uint8_t>((queue.head + 1U) % kRxQueueDepth);
queue.count--;
s_nextConsumeChannel = static_cast<uint8_t>((idx + 1U) % kChannelCount);
found = true;
break;
}
__enable_irq();
if(!found) {
return false;
}
if(channel) *channel = localFrame.channel;
if(id) *id = localFrame.id;
if(length) *length = localFrame.length;
if(data) {
for(uint8_t i = 0; i < localFrame.length; ++i) {
data[i] = localFrame.data[i];
}
}
return true;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const CanFd::Diag* 返回内部诊断统计结构体只读指针。
*/
const CanFd::Diag *CanFd::diag()
{
return &s_diag;
}
/**
* @brief 将三路 TCAN1057A 收发器置为 Normal 模式。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 对所有通道统一将 SLP/STB 控制脚拉低。
*/
void CanFd::setNormalMode()
{
gpio_bit_reset(kSlpGpio, kSlpPin);
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()
{
gpio_bit_set(kSlpGpio, kSlpPin);
for(size_t i = 0; i < kChannelCount; ++i) {
gpio_bit_set(kChannels[i].slpPort, kChannels[i].slpPin);
}
}
void CanFd::onRx0Interrupt()
/**
* @brief 处理指定通道的 RX0 中断接收。
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
* @param[out] 无。
* @retval 无返回值。
* @note 从对应控制器 FIFO0 取出一帧报文,更新诊断统计,并写入该通道软件接收队列。
*/
void CanFd::handleRxInterrupt(uint8_t channelIndex)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) return;
if(channelIndex >= kChannelCount) return;
const auto &ch = kChannels[channelIndex];
if(can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFL0) == RESET) return;
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx_message);
s_diag.rx_count++;
s_diag.last_rx_channel = channelIndex;
s_diag.last_payload_length = rx_message.rx_dlen;
s_diag.last_rx_fd_flag = rx_message.fd_flag;
s_diag.last_rx_brs = rx_message.fd_brs;
s_diag.last_rx_esi = rx_message.fd_esi;
s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
s_rxFrame.id = s_diag.last_rx_id;
s_rxFrame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i)
s_rxFrame.data[i] = rx_message.rx_data[i];
s_rxFrame.valid = 1U;
auto &queue = s_rxQueues[channelIndex];
if(queue.count >= kRxQueueDepth) {
s_diag.rx_drop_count++;
} else {
auto &frame = queue.frames[queue.tail];
frame.channel = channelIndex;
frame.id = s_diag.last_rx_id;
frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
frame.data[i] = rx_message.rx_data[i];
}
queue.tail = static_cast<uint8_t>((queue.tail + 1U) % kRxQueueDepth);
queue.count++;
}
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFL0);
}
/**
* @brief CAN0 兼容中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 兼容保留入口,内部统一转调 `handleRxInterrupt(0U)`。
*/
void CanFd::onRx0Interrupt()
{
handleRxInterrupt(0U);
}
/* ----- 中断入口 C 包装 ------------------------------------------------- */
extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::onRx0Interrupt(); }
/**
* @brief CAN0 RX0 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(0U)`。
*/
extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(0U); }
/**
* @brief CAN1 RX0 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(1U)`。
*/
extern "C" void CAN1_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(1U); }
/**
* @brief CAN2 RX0 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(2U)`。
*/
extern "C" void CAN2_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(2U); }
@@ -1,278 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -1,283 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -1,274 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_8TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_3TQ;
can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = DISABLE;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 1U;
can_fd_parameter.data_resync_jump_width = 0U;
can_fd_parameter.data_time_segment_1 = 2U;
can_fd_parameter.data_time_segment_2 = 1U;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -1,274 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_INSTANCE);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_8TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_3TQ;
can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = DISABLE;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 1U;
can_fd_parameter.data_resync_jump_width = 0U;
can_fd_parameter.data_time_segment_1 = 2U;
can_fd_parameter.data_time_segment_2 = 1U;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -1,225 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信模块实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kSlpGpio = GPIOA;
constexpr uint32_t kSlpPin = GPIO_PIN_8;
} // namespace
CanFd::Diag CanFd::s_diag;
CanFd::RxFrame CanFd::s_rxFrame;
CanFd::TxBuffer CanFd::s_txBuffer;
void CanFd::initGpio()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kSlpPin);
}
void CanFd::filterAcceptAll()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
void CanFd::initNominalTiming()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
void CanFd::initFdMode()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
void CanFd::buildStatusFrame(const StatusPayload *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &s_txBuffer.tx_message);
s_txBuffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
s_txBuffer.tx_message.tx_efid = 0U;
s_txBuffer.tx_message.tx_ff = CAN_FF_STANDARD;
s_txBuffer.tx_message.tx_ft = CAN_FT_DATA;
s_txBuffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
s_txBuffer.tx_message.fd_flag = ENABLE;
s_txBuffer.tx_message.fd_brs = ENABLE;
s_txBuffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = s_txBuffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
s_txBuffer.tx_message.tx_data[i] = p[i];
}
s_txBuffer.valid = 1U;
}
void CanFd::init()
{
initGpio();
setNormalMode();
can_deinit(CONFIG_CAN_INSTANCE);
initNominalTiming();
initFdMode();
filterAcceptAll();
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
s_rxFrame.valid = 0U;
s_txBuffer.valid = 0U;
}
void CanFd::poll()
{
/* 已由 CAN0_RX0_IRQHandler 中断驱动 */
}
void CanFd::prepareStatus(const StatusPayload *status)
{
if(status == nullptr) {
s_txBuffer.valid = 0U;
return;
}
buildStatusFrame(status);
}
void CanFd::sendPrepared()
{
if(s_txBuffer.valid == 0U) return;
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &s_txBuffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
s_diag.tx_count++;
s_diag.last_payload_length = s_txBuffer.tx_message.tx_dlen;
s_diag.last_tx_id = s_txBuffer.tx_message.tx_sfid;
} else {
s_diag.error_count++;
}
}
bool CanFd::hasRxFrame()
{
return s_rxFrame.valid != 0U;
}
bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(s_rxFrame.valid == 0U) return false;
if(id) *id = s_rxFrame.id;
if(length) *length = s_rxFrame.length;
if(data) { for(uint8_t i = 0; i < s_rxFrame.length; ++i) data[i] = s_rxFrame.data[i]; }
s_rxFrame.valid = 0U;
return true;
}
const CanFd::Diag *CanFd::diag()
{
return &s_diag;
}
void CanFd::setNormalMode()
{
gpio_bit_reset(kSlpGpio, kSlpPin);
}
void CanFd::setSilentMode()
{
gpio_bit_set(kSlpGpio, kSlpPin);
}
void CanFd::onRx0Interrupt()
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) return;
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
s_diag.rx_count++;
s_diag.last_payload_length = rx_message.rx_dlen;
s_diag.last_rx_fd_flag = rx_message.fd_flag;
s_diag.last_rx_brs = rx_message.fd_brs;
s_diag.last_rx_esi = rx_message.fd_esi;
s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
s_rxFrame.id = s_diag.last_rx_id;
s_rxFrame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i)
s_rxFrame.data[i] = rx_message.rx_data[i];
s_rxFrame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/* ----- 中断入口 C 包装 ------------------------------------------------- */
extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::onRx0Interrupt(); }
-326
View File
@@ -1,326 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kCanSlpGpio = GPIOA;
constexpr uint32_t kCanSlpPin = GPIO_PIN_8;
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN0 外设所需 GPIO 引脚。
* @note 按原理图连接关系配置:
* - PA12 -> CAN0_TX
* - PA11 -> CAN0_RX
* - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚)
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin);
}
/**
* @brief 将 TCAN1057A #0 置为正常工作模式。
* @note 模式控制脚按 STB 语义处理:低电平为 Normal,高电平为 Silent。
*/
inline void can_transceiver_normal_mode()
{
gpio_bit_reset(kCanSlpGpio, kCanSlpPin);
}
/**
* @brief 将 TCAN1057A #0 置为静默模式。
* @note 静默模式下收发器不驱动总线显性位,可用于总线监听或受控隔离场景。
*/
inline void can_transceiver_silent_mode()
{
gpio_bit_set(kCanSlpGpio, kCanSlpPin);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief CAN0 RX0 中断服务例程。
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
*/
extern "C" void CAN0_RX0_IRQHandler(void)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
return;
}
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。
* 其中 PA8 已封装为正常/静默两个模式控制函数,初始化时默认进入 Normal 模式。
*/
void canfd_init(void)
{
can_gpio_init();
can_transceiver_normal_mode();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
/* 使能 FIFO0 接收中断,替代轮询 */
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
*/
void canfd_poll(void)
{
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -1,319 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kCanSlpGpio = GPIOA;
constexpr uint32_t kCanSlpPin = GPIO_PIN_8;
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN0 外设所需 GPIO 引脚。
* @note 按原理图连接关系配置:
* - PA12 -> CAN0_TX
* - PA11 -> CAN0_RX
* - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚)
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin);
}
/**
* @brief 将 TCAN1057A #0 置为正常工作模式。
* @note 结合 TCAN1057A-Q1 产品资料:器件工作模式为 Normal / Silent,
* 当前原理图网络名虽然写作 CAN0_SLP,但本质是模式控制脚;
* 这里按 STB=0 -> Normal,STB=1 -> Silent 处理,因此 PA8 拉低进入正常模式。
*/
inline void can_transceiver_normal_mode()
{
gpio_bit_reset(kCanSlpGpio, kCanSlpPin);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief CAN0 RX0 中断服务例程。
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
*/
extern "C" void CAN0_RX0_IRQHandler(void)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
return;
}
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。
* 其中 PA8 按 TCAN1057A 的模式控制脚处理:低电平为 Normal,高电平为 Silent。
*/
void canfd_init(void)
{
can_gpio_init();
can_transceiver_normal_mode();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
/* 使能 FIFO0 接收中断,替代轮询 */
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
*/
void canfd_poll(void)
{
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
-300
View File
@@ -1,300 +0,0 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief CAN0 RX0 中断服务例程。
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
*/
extern "C" void CAN0_RX0_IRQHandler(void)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
return;
}
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
/* 使能 FIFO0 接收中断,替代轮询 */
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
*/
void canfd_poll(void)
{
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
+36 -1
View File
@@ -8,6 +8,13 @@
#include "sys/task_manager.h"
#include "sys/config.h"
/**
* @brief 配置用户调度定时器本体参数。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 使用 `CONFIG_SCHED_TIMER_*` 系列配置完成 TIMER2 的 100us 调度时基初始化。
*/
void SchedulerTimer::configureTimer()
{
timer_parameter_struct timer_initpara;
@@ -28,18 +35,39 @@ void SchedulerTimer::configureTimer()
timer_enable(CONFIG_SCHED_TIMER_INSTANCE);
}
/**
* @brief 配置用户调度定时器对应的 NVIC。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 设置优先级分组,并为调度定时器中断向量配置抢占优先级与子优先级。
*/
void SchedulerTimer::configureNvic()
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO);
}
/**
* @brief 初始化用户调度定时器模块。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 依次完成定时器本体配置与 NVIC 中断配置。
*/
void SchedulerTimer::init()
{
configureTimer();
configureNvic();
}
/**
* @brief 用户调度定时器中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 响应更新中断,清除中断标志后推进一次 `TaskManager::tick100us()`。
*/
void SchedulerTimer::onInterrupt()
{
if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) return;
@@ -48,4 +76,11 @@ void SchedulerTimer::onInterrupt()
}
/* ----- 中断入口 C 包装 ---------------------------------------------- */
void TIMER2_IRQHandler(void) { SchedulerTimer::onInterrupt(); }
/**
* @brief TIMER2 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note C 向量包装函数,内部转调 `SchedulerTimer::onInterrupt()`。
*/
void TIMER2_IRQHandler(void) { SchedulerTimer::onInterrupt(); }
-65
View File
@@ -1,65 +0,0 @@
/**
* @file Timer.cpp
* @brief 用户定时器初始化与中断处理实现。
*/
#include "main.h"
#include "device/Timer.h"
#include "sys/task_manager.h"
#include "sys/config.h"
/**
* @brief 配置定时器 2 参数:180MHz / 180 / 100 → 100us 中断周期。
*/
static void timer2_config(void)
{
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU);
timer_deinit(CONFIG_SCHED_TIMER_INSTANCE);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara);
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP);
timer_enable(CONFIG_SCHED_TIMER_INSTANCE);
}
/**
* @brief 配置定时器 2 的中断优先级并使能中断。
*/
static void timer2_nvic_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO);
}
/**
* @brief 初始化用户定时器及其中断配置。
* @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。
*/
void user_timer_init(void)
{
timer2_config();
timer2_nvic_config();
}
/**
* @brief 定时器 2 中断服务例程。
* @note 每次计数溢出时清除标志并推进一次 100us 任务调度节拍。
*/
void TIMER2_IRQHandler(void)
{
if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) {
return;
}
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
task_manager_tick_100us();
}
-65
View File
@@ -1,65 +0,0 @@
/**
* @file Timer.cpp
* @brief 用户定时器初始化与中断处理实现。
*/
#include "main.h"
#include "device/Timer.h"
#include "sys/task_manager.h"
#include "sys/config.h"
/**
* @brief 配置定时器 2 参数:180MHz / 180 / 100 → 100us 中断周期。
*/
static void timer2_config(void)
{
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU);
timer_deinit(CONFIG_SCHED_TIMER_INSTANCE);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara);
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP);
timer_enable(CONFIG_SCHED_TIMER_INSTANCE);
}
/**
* @brief 配置定时器 2 的中断优先级并使能中断。
*/
static void timer2_nvic_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO);
}
/**
* @brief 初始化用户定时器及其中断配置。
* @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。
*/
void user_timer_init(void)
{
timer2_config();
timer2_nvic_config();
}
/**
* @brief 定时器 2 中断服务例程。
* @note 每次计数溢出时清除标志并推进一次 100us 任务调度节拍。
*/
void TIMER2_IRQHandler(void)
{
if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) {
return;
}
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
task_manager_tick_100us();
}
+29 -1
View File
@@ -7,12 +7,40 @@
*/
class SchedulerTimer {
public:
/**
* @brief 初始化用户调度定时器及其中断。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前用于建立 100us 基准节拍,驱动 TaskManager::tick100us()。
*/
static void init();
/**
* @brief TIMER2 中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 每次计数溢出时清除标志并调用 TaskManager::tick100us()。
*/
static void onInterrupt();
private:
/**
* @brief 配置 TIMER2 基本时序参数。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
static void configureTimer();
/**
* @brief 配置 TIMER2 的 NVIC 优先级。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
static void configureNvic();
};
#endif /* DEVICE_TIMER_H */
#endif /* DEVICE_TIMER_H */
-17
View File
@@ -1,17 +0,0 @@
/**
* @file Timer.h
* @brief 用户定时器初始化接口定义。
*/
#ifndef DEVICE_TIMER_H
#define DEVICE_TIMER_H
/**
* @brief 初始化用户定时器及其中断配置。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。
*/
void user_timer_init(void);
#endif /* DEVICE_TIMER_H */
+96 -7
View File
@@ -14,30 +14,119 @@ public:
* @note 保存外部 VIN_ADC、内部温度与内部参考电压的原始值,以及可直接使用的工程值。
*/
struct Sample {
uint16_t vin_raw; /**< @brief 外部 VIN_ADC(PA6)原始 ADC 数值。 */
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vin_adc_v; /**< @brief VIN_ADC 引脚处电压,单位 V(仅为分压后的 ADC 端电压)。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
uint16_t vin_raw; /**< 外部 VIN_ADC(PA6)原始 ADC 数值。 */
uint16_t temp_raw; /**< 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< 内部参考电压通道原始 ADC 数值。 */
float vin_adc_v; /**< VIN_ADC 引脚处电压(分压后的 ADC 端电压),单位 V。 */
float vdda; /**< 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< 根据温度通道换算得到的芯片温度,单位 ℃。 */
};
/**
* @brief 初始化 ADC 硬件、扫描通道与 DMA 循环缓存。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 三通道扫描、DMA 循环搬运、PA6(VIN_ADC) 模拟输入配置以及内部通道校准。
*/
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 通道号。
* @param[out] 无。
* @retval uint16_t 对应通道原始值;不受支持的通道返回 0。
*/
static uint16_t readRaw(uint8_t channel);
/**
* @brief 读取外部 VIN_ADC(PA6)通道原始值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t VIN_ADC 原始 ADC 数值。
*/
static uint16_t readVinRaw();
/**
* @brief 读取内部参考电压原始值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部 VREFINT 通道原始 ADC 数值。
*/
static uint16_t readVrefRaw();
/**
* @brief 读取内部温度通道原始值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道原始 ADC 数值。
*/
static uint16_t readTempRaw();
/**
* @brief 根据 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。
* @param[in] vinRaw VIN_ADC 通道原始 ADC 数值。
* @param[in] vdda 当前 MCU 供电电压,单位 V。
* @param[out] 无。
* @retval float VIN_ADC 引脚电压,单位 V。
* @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。
*/
static float calculateVinAdcVoltage(uint16_t vinRaw, float vdda);
/**
* @brief 根据内部参考电压原始值计算 VDDA。
* @param[in] vrefRaw 内部 VREFINT 通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 MCU 供电电压,单位 V。
* @note 当 vrefRaw 为 0 时返回默认值 VDDA,避免除零异常。
*/
static float calculateVdda(uint16_t vrefRaw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] tempRaw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前 MCU 供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
static float calculateTemperature(uint16_t tempRaw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出 Sample 的指针;允许为空。
* @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 nullptr 时直接返回,不执行写操作。
*/
static void collectSample(Sample *sample);
private:
/**
* @brief 初始化 ADC DMA 通道配置。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
static void initDma();
};
using adc_sample_t = Adc::Sample;
#endif /* DEVICE_ADC_H */
#endif /* DEVICE_ADC_H */
-78
View File
@@ -1,78 +0,0 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样结果结构体。
* @note 同时保存内部温度与内部参考电压的原始值及换算后的工程值。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} adc_sample_t;
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数完成 ADC 时钟、模式、校准以及内部通道使能。
*/
void adc_hw_init(void);
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 函数内部会阻塞等待本次转换完成。
*/
uint16_t adc_single_read(uint8_t channel);
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void);
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void);
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当输入为 0 时返回默认 3.3V,避免除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行写操作。
*/
void adc_collect_sample(adc_sample_t *sample);
#endif /* DEVICE_ADC_H */
-114
View File
@@ -1,114 +0,0 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样结果结构体。
* @note 保存外部 VIN_ADC、内部温度与内部参考电压的原始值,以及可直接使用的工程值。
*/
typedef struct {
uint16_t vin_raw; /**< @brief 外部 VIN_ADC(PA6)原始 ADC 数值。 */
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vin_adc_v; /**< @brief VIN_ADC 引脚处电压,单位 V(仅为分压后的 ADC 端电压)。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} adc_sample_t;
/**
* @brief 初始化 ADC 硬件及内部温度/VREF/VIN_ADC 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数完成 ADC 时钟、三通道扫描、DMA 循环缓存以及内部/外部采样通道使能。
*/
void adc_hw_init(void);
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void);
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 3 的原始缓存指针,顺序为 VIN_ADC、温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void);
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel);
/**
* @brief 读取外部 VIN_ADC(PA6)通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 外部 VIN_ADC 通道的原始 ADC 值。
*/
uint16_t adc_read_vin_raw(void);
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void);
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void);
/**
* @brief 根据外部 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。
* @param[in] vin_raw 外部 VIN_ADC 通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float VIN_ADC 引脚电压,单位 V。
* @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。
*/
float adc_calculate_vin_adc_voltage(uint16_t vin_raw, float vdda);
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当输入为 0 时返回默认 3.3V,避免除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行写操作。
*/
void adc_collect_sample(adc_sample_t *sample);
#endif /* DEVICE_ADC_H */
-94
View File
@@ -1,94 +0,0 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样结果结构体。
* @note 同时保存内部温度与内部参考电压的原始值及换算后的工程值。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} adc_sample_t;
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数完成 ADC 时钟、双通道扫描、DMA 循环缓存以及内部通道使能。
*/
void adc_hw_init(void);
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void);
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 2 的原始缓存指针,顺序为温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void);
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel);
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void);
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void);
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当输入为 0 时返回默认 3.3V,避免除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行写操作。
*/
void adc_collect_sample(adc_sample_t *sample);
#endif /* DEVICE_ADC_H */
+144 -27
View File
@@ -8,7 +8,7 @@
/**
* @brief CANFD 通信模块。
* @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与状态帧发送。
* @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与按需发送能力。
*/
class CanFd {
public:
@@ -17,15 +17,19 @@ public:
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
struct Diag {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
uint32_t tx_count; /**< 成功发送的报文计数。 */
uint32_t rx_count; /**< 成功接收的报文计数。 */
uint32_t error_count; /**< 发送失败或检测到的错误计数。 */
uint32_t rx_drop_count; /**< 接收 FIFO 满导致丢弃的报文计数。 */
uint32_t tx_drop_count; /**< 发送 FIFO 满导致丢弃的报文计数。 */
uint32_t last_rx_id; /**< 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< 最近一次发送报文的标识符。 */
uint8_t last_rx_channel; /**< 最近一次接收报文所在的 CAN 通道号(0/1/2)。 */
uint8_t last_tx_channel; /**< 最近一次发送报文所在的 CAN 通道号(0/1/2)。 */
uint8_t last_payload_length; /**< 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< 最近一次接收报文的错误状态指示位。 */
};
/**
@@ -33,42 +37,155 @@ public:
* @note 当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。
*/
struct StatusPayload {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
uint16_t temp_raw; /**< 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< 根据温度通道换算得到的芯片温度,单位 ℃。 */
};
/**
* @brief 初始化 CANFD 外设、GPIO、模式控制、过滤器与中断。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 初始化后默认进入 Normal 模式,并使能 3 路 CAN 控制器的 RX0 中断用于异步接收。
*/
static void init();
/**
* @brief 兼容保留的轮询接口,中断模式下为空壳。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 实际接收已由 CAN0/CAN1/CAN2 的 RX0 中断驱动。
*/
static void poll();
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文,并广播入队到 3 路发送 FIFO。
* @param[in] status 指向 StatusPayload 的指针;为空时不入队。
* @param[out] 无。
* @retval 无返回值。
* @note 当前模块默认不会周期性调用该接口;仅在外部显式触发发送测试或业务发送时使用。
*/
static void prepareStatus(const StatusPayload *status);
/**
* @brief 根据状态载荷仅向指定 CAN 通道的发送 FIFO 入队一帧状态报文。
* @param[in] channelIndex 目标 CAN 通道号(0/1/2)。
* @param[in] status 指向 StatusPayload 的指针;为空时不入队。
* @param[out] 无。
* @retval bool true 表示成功入队,false 表示通道非法或该通道发送 FIFO 已满。
* @note 当前模块默认不会周期性调用该接口;仅在外部显式触发指定通道发送时使用。
*/
static bool prepareStatusForChannel(uint8_t channelIndex, const StatusPayload *status);
/**
* @brief 发送当前已准备好的 CANFD 状态报文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前模块默认不会周期性调用该接口;仅发送调用方显式准备过的报文;若队列为空则直接返回。
*/
static void sendPrepared();
/**
* @brief 查询 3 个 CAN 通道中是否存在尚未消费的接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示至少有一路存在待消费报文,false 表示当前所有通道缓存为空。
*/
static bool hasRxFrame();
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length);
/**
* @brief 读取并消费最近一轮扫描到的某一路缓存接收帧。
* @param[in] 无。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @param[out] channel 输出报文来源的 CAN 通道号指针;允许为空。
* @retval bool true 表示成功消费一帧报文,false 表示当前 3 路缓存均为空。
* @note 每路通道各自保留最近一帧;消费顺序采用轮转扫描,避免长期偏向固定通道。
*/
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel = nullptr);
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const Diag* 指向内部诊断统计结构体的只读指针。
*/
static const Diag *diag();
/**
* @brief 将 TCAN1057A 收发器置为正常工作模式。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 模式控制脚按 STB 语义处理:低电平为 Normal,高电平为 Silent。
*/
static void setNormalMode();
/**
* @brief 将 TCAN1057A 收发器置为静默模式。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 静默模式下收发器不驱动总线显性位,可用于总线监听或受控隔离。
*/
static void setSilentMode();
/**
* @brief CAN0 兼容中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 兼容保留给 CAN0 中断包装使用;三路统一接收逻辑已收口到 handleRxInterrupt()。
*/
static void onRx0Interrupt();
/**
* @brief 处理指定 CAN 通道的 RX0 中断接收。
* @param[in] channelIndex CAN 通道索引(0/1/2)。
* @param[out] 无。
* @retval 无返回值。
*/
static void handleRxInterrupt(uint8_t channelIndex);
private:
struct RxFrame {
uint8_t channel;
uint32_t id;
uint8_t data[64];
uint8_t length;
uint8_t valid;
uint8_t data[64];
uint8_t length;
};
static void initGpio();
static void filterAcceptAll();
static void initNominalTiming();
static void initFdMode();
static void buildStatusFrame(const StatusPayload *status);
struct RxQueue {
RxFrame frames[4];
uint8_t head;
uint8_t tail;
uint8_t count;
};
static Diag s_diag;
static RxFrame s_rxFrame;
/** @brief 初始化 CAN0/CAN1/CAN2 相关 GPIO。 */
static void initGpio();
/** @brief 为指定控制器配置全接收过滤器。 */
static void filterAcceptAll(uint32_t canPeriph, uint8_t filterNumber);
/** @brief 配置指定控制器的 CAN 仲裁段标称位时序。 */
static void initNominalTiming(uint32_t canPeriph);
/** @brief 配置指定控制器的 CAN FD 数据段位时序与 TDC。 */
static void initFdMode(uint32_t canPeriph);
/** @brief 构造内部发送缓冲中的状态帧内容。 */
static void buildStatusFrame(const StatusPayload *status);
/** @brief 向指定 CAN 通道的发送 FIFO 写入一帧状态报文。 */
static bool enqueueTxFrame(uint8_t channelIndex, const StatusPayload *status);
static Diag s_diag;
static RxQueue s_rxQueues[3];
static uint8_t s_nextConsumeChannel;
};
using canfd_diag_t = CanFd::Diag;
using canfd_diag_t = CanFd::Diag;
using canfd_status_payload_t = CanFd::StatusPayload;
#endif /* DEVICE_CANFD_H */
#endif /* DEVICE_CANFD_H */
@@ -1,81 +0,0 @@
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 通信模块。
* @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与状态帧发送。
*/
class CanFd {
public:
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
struct Diag {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
};
/**
* @brief CANFD 状态发送载荷。
* @note 当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。
*/
struct StatusPayload {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
};
static void init();
static void poll();
static void prepareStatus(const StatusPayload *status);
static void sendPrepared();
static bool hasRxFrame();
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length);
static const Diag *diag();
static void setNormalMode();
static void setSilentMode();
static void onRx0Interrupt();
private:
struct RxFrame {
uint32_t id;
uint8_t data[64];
uint8_t length;
uint8_t valid;
};
struct TxBuffer {
can_trasnmit_message_struct tx_message;
uint8_t payload[64];
uint8_t valid;
};
static void initGpio();
static void filterAcceptAll();
static void initNominalTiming();
static void initFdMode();
static void buildStatusFrame(const StatusPayload *status);
static Diag s_diag;
static RxFrame s_rxFrame;
static TxBuffer s_txBuffer;
};
using canfd_diag_t = CanFd::Diag;
using canfd_status_payload_t = CanFd::StatusPayload;
#endif /* DEVICE_CANFD_H */
-99
View File
@@ -1,99 +0,0 @@
/**
* @file canfd.h
* @brief CANFD 通信接口、状态载荷与诊断信息定义。
*/
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
typedef struct {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
} canfd_diag_t;
/**
* @brief CANFD 状态发送载荷。
* @note 用于封装供 CAN 状态帧发送的核心状态数据。当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} canfd_status_payload_t;
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_init(void);
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有待接收报文,函数直接返回。
*/
void canfd_poll(void);
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_prepare_status(const canfd_status_payload_t *status);
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有有效待发送报文,函数直接返回。
*/
void canfd_send_prepared(void);
/**
* @brief 查询是否存在尚未消费的接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示存在待消费报文,返回 0 表示没有待消费报文。
*/
uint8_t canfd_has_rx_frame(void);
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval uint8_t 返回 1 表示成功消费一帧报文,返回 0 表示当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length);
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const canfd_diag_t* 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void);
#endif /* DEVICE_CANFD_H */
-99
View File
@@ -1,99 +0,0 @@
/**
* @file canfd.h
* @brief CANFD 通信接口、状态载荷与诊断信息定义。
*/
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
typedef struct {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
} canfd_diag_t;
/**
* @brief CANFD 状态发送载荷。
* @note 用于封装温度与供电电压相关的状态数据,供状态帧打包发送。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} canfd_status_payload_t;
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_init(void);
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有待接收报文,函数直接返回。
*/
void canfd_poll(void);
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_prepare_status(const canfd_status_payload_t *status);
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有有效待发送报文,函数直接返回。
*/
void canfd_send_prepared(void);
/**
* @brief 查询是否存在尚未消费的接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示存在待消费报文,返回 0 表示没有待消费报文。
*/
uint8_t canfd_has_rx_frame(void);
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval uint8_t 返回 1 表示成功消费一帧报文,返回 0 表示当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length);
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const canfd_diag_t* 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void);
#endif /* DEVICE_CANFD_H */
+14
View File
@@ -6,11 +6,25 @@
#include "device/adc.h"
#include "device/temp_sensor.h"
/**
* @brief 初始化温度传感采样依赖模块。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前内部直接复用 `Adc::init()` 完成温度与参考电压采样链路初始化。
*/
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);
@@ -1,25 +0,0 @@
/**
* @file temp_sensor.cpp
* @brief 温度传感采样实现。
*/
#include "device/adc.h"
#include "device/temp_sensor.h"
/**
* @brief 初始化温度传感采样所需硬件资源。
* @note 当前通过初始化 ADC 模块完成内部温度通道准备。
*/
void temp_sensor_init(void)
{
adc_hw_init();
}
/**
* @brief 采集一次温度传感样本并输出结果。
* @param[out] data 指向输出样本结构体的指针;其语义与 adc_collect_sample() 一致。
*/
void temp_sensor_sample(temp_sensor_data_t *data)
{
adc_collect_sample(data);
}
+16 -1
View File
@@ -11,10 +11,25 @@ class TempSensor {
public:
using Data = Adc::Sample;
/**
* @brief 初始化温度采样依赖资源。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @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 */
#endif /* DEVICE_TEMP_SENSOR_H */
@@ -1,33 +0,0 @@
/**
* @file temp_sensor.h
* @brief 温度传感采样接口定义。
*/
#ifndef DEVICE_TEMP_SENSOR_H
#define DEVICE_TEMP_SENSOR_H
#include <stdint.h>
#include "device/adc.h"
/**
* @brief 温度传感采样数据类型别名。
* @note 复用 ADC 采样结果结构,统一保存温度与参考电压原始值及换算值。
*/
typedef adc_sample_t temp_sensor_data_t;
/**
* @brief 初始化温度传感采样所需硬件资源。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前通过初始化 ADC 模块完成内部温度通道准备。
*/
void temp_sensor_init(void);
/**
* @brief 采集一次温度传感样本并输出结果。
* @param[out] data 指向输出样本结构体的指针;其语义与 adc_collect_sample() 一致。
* @retval 无返回值。
*/
void temp_sensor_sample(temp_sensor_data_t *data);
#endif /* DEVICE_TEMP_SENSOR_H */
+84 -4
View File
@@ -10,34 +10,84 @@
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 计算发送环形缓冲区中的下一个索引。
* @param[in] idx 当前索引值。
* @param[out] 无。
* @retval uint16_t 返回环形缓冲区中的下一个索引。
*/
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] 无。
* @param[out] 无。
* @retval bool true 表示发送缓冲区为空,false 表示仍有待发数据。
*/
bool Usart0::isBufferEmpty()
{
return s_txHead == s_txTail;
}
int Usart0::putChar(int ch, FILE *f)
/**
* @brief 以非阻塞方式向 USART0 发送缓冲写入一个字符。
* @param[in] ch 待发送字符。
* @param[out] 无。
* @retval bool true 表示成功写入发送缓冲,false 表示缓冲已满而被丢弃。
* @note 缓冲满时仅增加丢弃计数,不阻塞主流程。
*/
bool Usart0::putCharNonBlocking(int ch)
{
(void)f;
uint16_t next = nextIndex(s_txHead);
while(next == s_txTail) { /* 缓冲满,忙等重试 */ }
if(next == s_txTail) {
s_dropCount++;
return false;
}
s_txBuf[s_txHead] = static_cast<uint8_t>(ch);
s_txHead = next;
usart_interrupt_enable(USART0, USART_INT_TBE);
return true;
}
/**
* @brief 向 USART0 发送缓冲写入一个字符。
* @param[in] ch 待发送字符。
* @param[in] f 标准流指针。
* @param[out] 无。
* @retval int 返回写入缓冲的字符值。
* @note 当前采用非阻塞写入策略;若缓冲已满则丢弃该字符但仍返回原字符值。
*/
int Usart0::putChar(int ch, FILE *f)
{
(void)f;
(void)putCharNonBlocking(ch);
return ch;
}
/**
* @brief USART0 中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 处理发送缓冲区的 TBE 中断驱动输出;当缓冲为空时自动关闭发送中断。
*/
void Usart0::onInterrupt()
{
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE) != RESET) {
@@ -51,6 +101,13 @@ void Usart0::onInterrupt()
}
}
/**
* @brief 配置 USART0 对应 GPIO 引脚。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 将 PA9 配置为复用推挽发送脚,将 PA10 配置为浮空输入接收脚。
*/
void Usart0::configurePins()
{
rcu_periph_clock_enable(RCU_GPIOA);
@@ -58,6 +115,13 @@ void Usart0::configurePins()
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
/**
* @brief 初始化 USART0 外设与中断。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 使用 `CONFIG_USART0_BAUDRATE` 配置串口参数,并使能 USART0 中断。
*/
void Usart0::init()
{
rcu_periph_clock_enable(RCU_USART0);
@@ -73,5 +137,21 @@ void Usart0::init()
}
/* ----- 中断入口 / printf 重定向的 C 包装 --------------------------- */
/**
* @brief 标准库 fputc 重定向入口。
* @param[in] ch 待发送字符。
* @param[in] f 标准流指针。
* @param[out] 无。
* @retval int 返回写入缓冲的字符值。
* @note 该 C 包装函数内部转调 `Usart0::putChar()`。
*/
extern "C" int fputc(int ch, FILE *f) { return Usart0::putChar(ch, f); }
extern "C" void USART0_IRQHandler(void) { Usart0::onInterrupt(); }
/**
* @brief USART0 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该 C 包装函数内部转调 `Usart0::onInterrupt()`。
*/
extern "C" void USART0_IRQHandler(void) { Usart0::onInterrupt(); }
-109
View File
@@ -1,109 +0,0 @@
/**
* @file usart0.cpp
* @brief USART0 串口配置与 printf 重定向实现。
* @note 使用 TX 中断 + 环形缓冲替代逐字节忙等 TBE,降低 CPU 占用。
*/
#include "main.h"
#include "device/usart0.h"
#include "sys/config.h"
#define USARTx USART0
#define USART_TX_BUF_SIZE 256U
namespace {
uint8_t g_tx_buf[USART_TX_BUF_SIZE];
volatile uint16_t g_tx_head = 0U;
volatile uint16_t g_tx_tail = 0U;
inline uint16_t tx_buf_next(uint16_t idx)
{
return (idx + 1U) % USART_TX_BUF_SIZE;
}
inline bool tx_buf_full(void)
{
return tx_buf_next(g_tx_head) == g_tx_tail;
}
inline bool tx_buf_empty(void)
{
return g_tx_head == g_tx_tail;
}
} // namespace
/**
* @brief 重定向标准输出字符到 USART0。
* @param[in] ch 待输出的字符。
* @param[in] f 标准流指针;当前实现中未实际使用。
* @retval 返回已发送的字符值。
* @note 将字符写入环形发送缓冲,由 USART0 TXE 中断负责实际发送。
*/
extern "C" int fputc(int ch, FILE *f)
{
(void)f;
uint16_t next = tx_buf_next(g_tx_head);
while(next == g_tx_tail) {
/* 缓冲满,忙等一小段时间后重试;
* 在 115200 波特率下通常不会发生 */
}
g_tx_buf[g_tx_head] = static_cast<uint8_t>(ch);
g_tx_head = next;
/* 启动 TXE 中断以触发实际硬件发送 */
usart_interrupt_enable(USARTx, USART_INT_TBE);
return ch;
}
/**
* @brief USART0 中断服务例程。
* @note 处理 TXE 发送缓冲区空中断,从环形缓冲取下一字节发送。
*/
extern "C" void USART0_IRQHandler(void)
{
if(usart_interrupt_flag_get(USARTx, USART_INT_FLAG_TBE) != RESET) {
if(!tx_buf_empty()) {
usart_data_transmit(USARTx, g_tx_buf[g_tx_tail]);
g_tx_tail = tx_buf_next(g_tx_tail);
}
/* 缓冲已空,关 TXE 中断,等下次 fputc 再开 */
if(tx_buf_empty()) {
usart_interrupt_disable(USARTx, USART_INT_TBE);
}
}
}
/**
* @brief 配置 USART0 相关 GPIO 引脚。
*/
void usart0_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
/**
* @brief 配置并使能 USART0 外设。
* @note 当前使用 8N1 格式,115200 波特率,收发同时使能。
* 同步配置 NVIC 以便 TXE 中断驱动发送。
*/
void usart0_config(void)
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USARTx);
usart_baudrate_set(USARTx, CONFIG_USART0_BAUDRATE);
usart_word_length_set(USARTx, USART_WL_8BIT);
usart_stop_bit_set(USARTx, USART_STB_1BIT);
usart_parity_config(USARTx, USART_PM_NONE);
usart_receive_config(USARTx, USART_RECEIVE_ENABLE);
usart_transmit_config(USARTx, USART_TRANSMIT_ENABLE);
usart_enable(USARTx);
/* 使能 USART0 NVIC,用于 TXE 中断驱动发送 */
nvic_irq_enable(USART0_IRQn, 2U, 0U);
}
@@ -1,54 +0,0 @@
/**
* @file usart0.cpp
* @brief USART0 串口配置与 printf 重定向实现。
*/
#include "main.h"
#include "device/usart0.h"
#include "sys/config.h"
#define USARTx USART0
/**
* @brief 重定向标准输出字符到 USART0。
* @param[in] ch 待输出的字符。
* @param[in] f 标准流指针;当前实现中未实际使用。
* @retval 返回已发送的字符值。
*/
extern "C" int fputc(int ch, FILE *f)
{
(void)f;
while(RESET == usart_flag_get(USARTx, USART_FLAG_TBE)) {
}
usart_data_transmit(USARTx, (uint16_t)ch);
return ch;
}
/**
* @brief 配置 USART0 相关 GPIO 引脚。
*/
void usart0_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
/**
* @brief 配置并使能 USART0 外设。
* @note 当前使用 8N1 格式,115200 波特率,收发同时使能。
*/
void usart0_config(void)
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USARTx);
usart_baudrate_set(USARTx, CONFIG_USART0_BAUDRATE);
usart_word_length_set(USARTx, USART_WL_8BIT);
usart_stop_bit_set(USARTx, USART_STB_1BIT);
usart_parity_config(USARTx, USART_PM_NONE);
usart_receive_config(USARTx, USART_RECEIVE_ENABLE);
usart_transmit_config(USARTx, USART_TRANSMIT_ENABLE);
usart_enable(USARTx);
}
+58 -3
View File
@@ -6,24 +6,79 @@
/**
* @brief USART0 串口模块。
* @note 负责 GPIO 初始化、串口配置、TX 环形缓冲与中断驱动发送。
* @note 负责 GPIO 初始化、外设配置、TX 环形缓冲与中断驱动发送。
*/
class Usart0 {
public:
/**
* @brief 配置 USART0 的 GPIO 引脚。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前配置为:PA9=TX(AF_PP)、PA10=RX(IN_FLOATING)。
*/
static void configurePins();
/**
* @brief 初始化 USART0 外设与 NVIC。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 使用 8N1 格式,收发使能,同步配置 NVIC 以便 TXE 中断驱动发送。
*/
static void init();
/**
* @brief 将字符写入 USART0 发送环形缓冲。
* @param[in] ch 待发送字符。
* @param[in] f 标准流指针,实际未使用。
* @param[out] 无。
* @retval int 返回已写入缓冲的字符值。
* @note 新字符写入后自动启动 TXE 中断来推动实际硬件发送。
*/
static int putChar(int ch, FILE *f);
/**
* @brief USART0 中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 在 TXE 产生时从环形缓冲取下一字节发送;缓冲空后关闭 TXE 中断。
*/
static void onInterrupt();
private:
/**
* @brief 获取环形缓冲下一索引。
* @param[in] idx 当前索引。
* @param[out] 无。
* @retval uint16_t 下一索引值。
*/
static uint16_t nextIndex(uint16_t idx);
/**
* @brief 判断环形发送缓冲是否已满。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示已满,false 表示可继续写入。
*/
static bool isBufferFull();
/**
* @brief 判断环形发送缓冲是否为空。
* @param[in] 无。
* @param[out] 无。
* @retval bool true 表示空,false 表示仍有待发送数据。
*/
static bool isBufferEmpty();
static bool putCharNonBlocking(int ch);
static constexpr uint16_t kTxBufSize = 256U;
static uint8_t s_txBuf[kTxBufSize];
static volatile uint32_t s_dropCount;
static uint8_t s_txBuf[kTxBufSize];
static volatile uint16_t s_txHead;
static volatile uint16_t s_txTail;
};
#endif /* DEVICE_USART0_H */
#endif /* DEVICE_USART0_H */
-25
View File
@@ -1,25 +0,0 @@
/**
* @file usart0.h
* @brief USART0 串口打印与基础配置接口定义。
*/
#ifndef DEVICE_USART0_H
#define DEVICE_USART0_H
/**
* @brief 配置 USART0 相关 GPIO 引脚。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void usart0_gpio_config(void);
/**
* @brief 配置并使能 USART0 外设。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前串口参数由实现文件中的默认宏定义决定。
*/
void usart0_config(void);
#endif /* DEVICE_USART0_H */
+23 -1
View File
@@ -1,11 +1,20 @@
/**
* @file main.cpp
* @brief 应用主入口与顶层流程实现。
*/
#include "main.h"
#include "sys/initial.h"
#include "sys/task_manager.h"
#include "sys/task_runner.h"
#include "sys/config.h"
/**
* @brief 打印应用启动横幅与系统时钟信息。
* @note 该函数仅负责串口输出启动提示,不修改系统控制状态。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数仅负责串口输出启动提示,不修改系统控制状态。
*/
void App::printBanner() const
{
@@ -15,7 +24,10 @@ void App::printBanner() const
/**
* @brief 运行应用主循环。
* @param[in] 无。
* @param[out] 无。
* @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。
* @note 完成系统初始化、任务运行时初始化和启动横幅输出后,持续执行任务分发循环。
*/
int App::run()
{
@@ -25,11 +37,21 @@ int App::run()
while(1) {
TaskRunner::runPending();
if(TaskManager::state().task_flags == 0U) {
__WFI();
}
}
return 0;
}
/**
* @brief 程序入口函数。
* @param[in] 无。
* @param[out] 无。
* @retval int 返回应用主循环执行结果;在嵌入式常驻场景下通常不会返回。
* @note 创建应用顶层对象并转入 `App::run()`。
*/
int main(void)
{
App app;
-34
View File
@@ -1,34 +0,0 @@
#include "main.h"
#include "sys/initial.h"
#include "sys/task_runner.h"
/**
* @brief 打印应用启动横幅与系统时钟信息。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数仅负责串口输出启动提示,不修改系统控制状态。
*/
static void app_banner(void)
{
printf("\r\nGD32E508 DCU task scheduler start\r\n");
printf("SystemCoreClock=%u Hz\r\n", SystemCoreClock);
}
/**
* @brief 应用程序主入口函数。
* @param[in] 无。
* @param[out] 无。
* @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。
* @note 初始化硬件与任务运行环境后,进入永久任务执行循环。
*/
int main(void)
{
app_init();
task_runner_init();
app_banner();
while(1) {
task_runner_run_pending();
}
}
-35
View File
@@ -1,35 +0,0 @@
#include "main.h"
#include "sys/initial.h"
#include "sys/task_runner.h"
#include "sys/config.h"
/**
* @brief 打印应用启动横幅与系统时钟信息。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数仅负责串口输出启动提示,不修改系统控制状态。
*/
static void app_banner(void)
{
printf("\r\n%s\r\n", APP_BANNER_TEXT);
printf("SystemCoreClock=%u Hz\r\n", SystemCoreClock);
}
/**
* @brief 应用程序主入口函数。
* @param[in] 无。
* @param[out] 无。
* @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。
* @note 初始化硬件与任务运行环境后,进入永久任务执行循环。
*/
int main(void)
{
app_init();
task_runner_init();
app_banner();
while(1) {
task_runner_run_pending();
}
}
+41 -18
View File
@@ -12,17 +12,17 @@
* @note 每个枚举值对应任务调度器中的一个任务位,用于驱动主循环中的任务分发。
*/
enum system_task_t {
TASK_ADC_VREF_SAMPLE = 0, /**< @brief 采样 VIN/VREF 原始值任务。 */
TASK_ADC_TEMP_SAMPLE, /**< @brief 采样内部温度通道原始值任务。 */
TASK_ADC_PROCESS, /**< @brief 对 ADC 原始值进行电压与温度计算任务。 */
TASK_CAN_RX_POLL, /**< @brief 兼容保留的 CANFD 接收轮询任务。 */
TASK_CAN_RX_PARSE, /**< @brief 解析并消费已接收 CANFD 帧任务。 */
TASK_CAN_TX_PREPARE, /**< @brief 准备 CANFD 状态发送帧任务。 */
TASK_CAN_TX_SEND, /**< @brief 发送已准备好的 CANFD 帧任务。 */
TASK_STATUS_LED, /**< @brief 状态灯维护任务。 */
TASK_HEARTBEAT, /**< @brief 系统心跳维护任务。 */
TASK_DIAGNOSTIC_PRINT, /**< @brief 诊断信息打印任务。 */
TASK_COUNT /**< @brief 任务总数,用于边界或统计场景。 */
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_PREPARE, /**< 预留的 CANFD 发送准备任务,默认不主动置位。 */
TASK_CAN_TX_SEND, /**< 预留的 CANFD 发送执行任务,默认不主动置位。 */
TASK_STATUS_LED, /**< 状态灯维护任务。 */
TASK_HEARTBEAT, /**< 系统心跳维护任务。 */
TASK_DIAGNOSTIC_PRINT, /**< 诊断信息打印任务。 */
TASK_COUNT /**< 任务总数,用于边界或统计场景。 */
};
/**
@@ -30,12 +30,23 @@ enum system_task_t {
* @note 保存各类节拍计数器与待执行任务标志位,由中断与主循环共享访问。
*/
struct task_scheduler_t {
volatile uint32_t task_flags; /**< @brief 待执行任务位图,每一位对应一个任务。 */
volatile uint32_t tick_100us; /**< @brief 100us 基准节拍累计计数。 */
volatile uint32_t tick_1ms; /**< @brief 1ms 节拍累计计数。 */
volatile uint32_t tick_10ms; /**< @brief 10ms 节拍累计计数。 */
volatile uint32_t tick_100ms; /**< @brief 100ms 节拍累计计数。 */
volatile uint32_t tick_1s; /**< @brief 1s 节拍累计计数。 */
volatile uint32_t task_flags; /**< 待执行任务位图,每一位对应一个任务。 */
volatile uint32_t tick_100us; /**< 100us 基准节拍累计计数。 */
volatile uint32_t tick_1ms; /**< 1ms 节拍累计计数。 */
volatile uint32_t tick_10ms; /**< 10ms 节拍累计计数。 */
volatile uint32_t tick_100ms; /**< 100ms 节拍累计计数。 */
/** @brief 1s 节拍累计计数。 */
volatile uint32_t tick_1s;
/** @brief 调度分频计数:ADC VREF 采样。 */
volatile uint16_t div_vref_sample;
/** @brief 调度分频计数:ADC 温度采样/处理。 */
volatile uint16_t div_temp_sample;
/** @brief 调度分频计数:CAN 接收处理。 */
volatile uint16_t div_can_rx;
/** @brief 调度分频计数:状态灯。 */
volatile uint16_t div_can_tx;
/** @brief 调度分频计数:心跳与诊断打印。 */
volatile uint16_t div_heartbeat;
};
/**
@@ -50,10 +61,22 @@ extern task_scheduler_t g_scheduler;
*/
class App {
public:
/**
* @brief 运行应用主循环。
* @param[in] 无。
* @param[out] 无。
* @retval int 固定返回 0;在嵌入式常驻循环场景下通常不会真正返回。
*/
int run();
private:
/**
* @brief 打印应用启动横幅与系统时钟信息。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void printBanner() const;
};
#endif /* MAIN_H */
#endif /* MAIN_H */
-47
View File
@@ -1,47 +0,0 @@
#ifndef MAIN_H
#define MAIN_H
#include "RTE_Components.h"
#include CMSIS_device_header
#include <stdint.h>
#include <stdio.h>
/**
* @brief 系统任务枚举定义。
* @note 每个枚举值对应任务调度器中的一个任务位,用于驱动主循环中的任务分发。
*/
typedef enum {
TASK_ADC_VREF_SAMPLE = 0, /**< @brief 采样内部参考电压原始值任务。 */
TASK_ADC_TEMP_SAMPLE, /**< @brief 采样内部温度通道原始值任务。 */
TASK_ADC_PROCESS, /**< @brief 对 ADC 原始值进行电压与温度计算任务。 */
TASK_CAN_RX_POLL, /**< @brief 轮询 CANFD 接收 FIFO 任务。 */
TASK_CAN_RX_PARSE, /**< @brief 解析并消费已接收 CANFD 帧任务。 */
TASK_CAN_TX_PREPARE, /**< @brief 准备 CANFD 状态发送帧任务。 */
TASK_CAN_TX_SEND, /**< @brief 发送已准备好的 CANFD 帧任务。 */
TASK_STATUS_LED, /**< @brief 状态灯维护任务。 */
TASK_HEARTBEAT, /**< @brief 系统心跳维护任务。 */
TASK_DIAGNOSTIC_PRINT, /**< @brief 诊断信息打印任务。 */
TASK_COUNT /**< @brief 任务总数,用于边界或统计场景。 */
} system_task_t;
/**
* @brief 任务调度器状态结构体。
* @note 该结构体保存各类节拍计数器与待执行任务标志位,由中断与主循环共享访问。
*/
typedef struct {
volatile uint32_t task_flags; /**< @brief 待执行任务位图,每一位对应一个任务。 */
volatile uint32_t tick_100us; /**< @brief 100us 基准节拍累计计数。 */
volatile uint32_t tick_1ms; /**< @brief 1ms 节拍累计计数。 */
volatile uint32_t tick_10ms; /**< @brief 10ms 节拍累计计数。 */
volatile uint32_t tick_100ms; /**< @brief 100ms 节拍累计计数。 */
volatile uint32_t tick_1s; /**< @brief 1s 节拍累计计数。 */
} task_scheduler_t;
/**
* @brief 全局任务调度器实例。
* @note 由任务管理模块定义,由其他模块引用以观察或维护调度状态。
*/
extern task_scheduler_t g_scheduler;
#endif /* MAIN_H */
+3
View File
@@ -76,4 +76,7 @@
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
/** @brief UART 诊断打印级别:0=仅温度/电压,1=温度/电压+CAN 统计。 */
#define CONFIG_UART_DIAG_LEVEL 1
#endif /* SYS_CONFIG_H */
-79
View File
@@ -1,79 +0,0 @@
/**
* @file config.h
* @brief 应用级集中配置项定义。
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
*/
#ifndef SYS_CONFIG_H
#define SYS_CONFIG_H
/** @brief 应用启动横幅字符串。 */
#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start"
/** @brief USART0 默认波特率。 */
#define CONFIG_USART0_BAUDRATE 115200U
/** @brief ADC 外设实例。 */
#define CONFIG_ADC_INSTANCE ADC0
/** @brief ADC 内部温度通道号。 */
#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16
/** @brief ADC 内部参考电压通道号。 */
#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17
/** @brief ADC 满量程计数值。 */
#define CONFIG_ADC_MAX_VALUE 4095.0f
/** @brief 内部参考电压典型值,单位 V。 */
#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f
/** @brief 温度传感器 25℃ 对应电压,单位 V。 */
#define CONFIG_ADC_TEMP_V25 1.43f
/** @brief 温度传感器平均斜率,单位 V/℃。 */
#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f
/** @brief ADC 上电后稳定等待时间,单位 ms。 */
#define CONFIG_ADC_STABILIZE_DELAY_MS 2U
/** @brief ADC 规则通道采样时间配置。 */
#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5
/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */
#define CONFIG_ADC_DEFAULT_VDDA 3.3f
/** @brief CAN 外设实例。 */
#define CONFIG_CAN_INSTANCE CAN0
/** @brief CAN 外设时钟使能枚举。 */
#define CONFIG_CAN_RCU RCU_CAN0
/** @brief CAN 状态报文标准帧 ID。 */
#define CONFIG_CAN_STATUS_ID 0x321U
/** @brief CAN 标称波特率预分频值。 */
#define CONFIG_CAN_NOMINAL_BAUD_PRESCALER 12U
/** @brief CAN 状态载荷中调试标识字符 0。 */
#define CONFIG_CAN_STATUS_TAG0 0x43U
/** @brief CAN 状态载荷中调试标识字符 1。 */
#define CONFIG_CAN_STATUS_TAG1 0x41U
/** @brief CAN 状态载荷中调试标识字符 2。 */
#define CONFIG_CAN_STATUS_TAG2 0x4EU
/** @brief CAN 状态载荷中调试标识字符 3。 */
#define CONFIG_CAN_STATUS_TAG3 0x46U
/** @brief 用户调度定时器实例。 */
#define CONFIG_SCHED_TIMER_INSTANCE TIMER2
/** @brief 用户调度定时器中断号。 */
#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn
/** @brief 用户调度定时器时钟使能宏参数。 */
#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2
/** @brief 用户调度定时器预分频值。 */
#define CONFIG_SCHED_TIMER_PRESCALER 179U
/** @brief 用户调度定时器周期重装值。 */
#define CONFIG_SCHED_TIMER_PERIOD 99U
/** @brief 用户调度定时器 NVIC 抢占优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U
/** @brief 用户调度定时器 NVIC 子优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U
/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U
/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U
/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U
/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
#endif /* SYS_CONFIG_H */
-77
View File
@@ -1,77 +0,0 @@
/**
* @file config.h
* @brief 应用级集中配置项定义。
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
*/
#ifndef SYS_CONFIG_H
#define SYS_CONFIG_H
/** @brief 应用启动横幅字符串。 */
#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start"
/** @brief USART0 默认波特率。 */
#define CONFIG_USART0_BAUDRATE 115200U
/** @brief ADC 外设实例。 */
#define CONFIG_ADC_INSTANCE ADC0
/** @brief ADC 内部温度通道号。 */
#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16
/** @brief ADC 内部参考电压通道号。 */
#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17
/** @brief ADC 满量程计数值。 */
#define CONFIG_ADC_MAX_VALUE 4095.0f
/** @brief 内部参考电压典型值,单位 V。 */
#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f
/** @brief 温度传感器 25℃ 对应电压,单位 V。 */
#define CONFIG_ADC_TEMP_V25 1.43f
/** @brief 温度传感器平均斜率,单位 V/℃。 */
#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f
/** @brief ADC 上电后稳定等待时间,单位 ms。 */
#define CONFIG_ADC_STABILIZE_DELAY_MS 2U
/** @brief ADC 规则通道采样时间配置。 */
#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5
/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */
#define CONFIG_ADC_DEFAULT_VDDA 3.3f
/** @brief CAN 外设实例。 */
#define CONFIG_CAN_INSTANCE CAN0
/** @brief CAN 状态报文标准帧 ID。 */
#define CONFIG_CAN_STATUS_ID 0x321U
/** @brief CAN 标称波特率预分频值。 */
#define CONFIG_CAN_NOMINAL_BAUD_PRESCALER 12U
/** @brief CAN 状态载荷中调试标识字符 0。 */
#define CONFIG_CAN_STATUS_TAG0 0x43U
/** @brief CAN 状态载荷中调试标识字符 1。 */
#define CONFIG_CAN_STATUS_TAG1 0x41U
/** @brief CAN 状态载荷中调试标识字符 2。 */
#define CONFIG_CAN_STATUS_TAG2 0x4EU
/** @brief CAN 状态载荷中调试标识字符 3。 */
#define CONFIG_CAN_STATUS_TAG3 0x46U
/** @brief 用户调度定时器实例。 */
#define CONFIG_SCHED_TIMER_INSTANCE TIMER2
/** @brief 用户调度定时器中断号。 */
#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn
/** @brief 用户调度定时器时钟使能宏参数。 */
#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2
/** @brief 用户调度定时器预分频值。 */
#define CONFIG_SCHED_TIMER_PRESCALER 179U
/** @brief 用户调度定时器周期重装值。 */
#define CONFIG_SCHED_TIMER_PERIOD 99U
/** @brief 用户调度定时器 NVIC 抢占优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U
/** @brief 用户调度定时器 NVIC 子优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U
/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U
/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U
/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U
/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
#endif /* SYS_CONFIG_H */
-77
View File
@@ -1,77 +0,0 @@
/**
* @file config.h
* @brief 应用级集中配置项定义。
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
*/
#ifndef SYS_CONFIG_H
#define SYS_CONFIG_H
/** @brief 应用启动横幅字符串。 */
#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start"
/** @brief USART0 默认波特率。 */
#define CONFIG_USART0_BAUDRATE 115200U
/** @brief ADC 外设实例。 */
#define CONFIG_ADC_INSTANCE ADC0
/** @brief ADC 内部温度通道号。 */
#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16
/** @brief ADC 内部参考电压通道号。 */
#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17
/** @brief ADC 满量程计数值。 */
#define CONFIG_ADC_MAX_VALUE 4095.0f
/** @brief 内部参考电压典型值,单位 V。 */
#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f
/** @brief 温度传感器 25℃ 对应电压,单位 V。 */
#define CONFIG_ADC_TEMP_V25 1.43f
/** @brief 温度传感器平均斜率,单位 V/℃。 */
#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f
/** @brief ADC 上电后稳定等待时间,单位 ms。 */
#define CONFIG_ADC_STABILIZE_DELAY_MS 2U
/** @brief ADC 规则通道采样时间配置。 */
#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5
/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */
#define CONFIG_ADC_DEFAULT_VDDA 3.3f
/** @brief CAN 外设实例。 */
#define CONFIG_CAN_INSTANCE CAN0
/** @brief CAN 外设时钟使能枚举。 */
#define CONFIG_CAN_RCU RCU_CAN0
/** @brief CAN 状态报文标准帧 ID。 */
#define CONFIG_CAN_STATUS_ID 0x321U
/** @brief CAN 状态载荷中调试标识字符 0。 */
#define CONFIG_CAN_STATUS_TAG0 0x43U
/** @brief CAN 状态载荷中调试标识字符 1。 */
#define CONFIG_CAN_STATUS_TAG1 0x41U
/** @brief CAN 状态载荷中调试标识字符 2。 */
#define CONFIG_CAN_STATUS_TAG2 0x4EU
/** @brief CAN 状态载荷中调试标识字符 3。 */
#define CONFIG_CAN_STATUS_TAG3 0x46U
/** @brief 用户调度定时器实例。 */
#define CONFIG_SCHED_TIMER_INSTANCE TIMER2
/** @brief 用户调度定时器中断号。 */
#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn
/** @brief 用户调度定时器时钟使能宏参数。 */
#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2
/** @brief 用户调度定时器预分频值。 */
#define CONFIG_SCHED_TIMER_PRESCALER 179U
/** @brief 用户调度定时器周期重装值。 */
#define CONFIG_SCHED_TIMER_PERIOD 99U
/** @brief 用户调度定时器 NVIC 抢占优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U
/** @brief 用户调度定时器 NVIC 子优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U
/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U
/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U
/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U
/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
#endif /* SYS_CONFIG_H */
-77
View File
@@ -1,77 +0,0 @@
/**
* @file config.h
* @brief 应用级集中配置项定义。
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
*/
#ifndef SYS_CONFIG_H
#define SYS_CONFIG_H
/** @brief 应用启动横幅字符串。 */
#define APP_BANNER_TEXT "GD32E508 DCU task scheduler start"
/** @brief USART0 默认波特率。 */
#define CONFIG_USART0_BAUDRATE 115200U
/** @brief ADC 外设实例。 */
#define CONFIG_ADC_INSTANCE ADC0
/** @brief ADC 内部温度通道号。 */
#define CONFIG_ADC_TEMP_CHANNEL ADC_CHANNEL_16
/** @brief ADC 内部参考电压通道号。 */
#define CONFIG_ADC_VREFINT_CHANNEL ADC_CHANNEL_17
/** @brief ADC 满量程计数值。 */
#define CONFIG_ADC_MAX_VALUE 4095.0f
/** @brief 内部参考电压典型值,单位 V。 */
#define CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE 1.20f
/** @brief 温度传感器 25℃ 对应电压,单位 V。 */
#define CONFIG_ADC_TEMP_V25 1.43f
/** @brief 温度传感器平均斜率,单位 V/℃。 */
#define CONFIG_ADC_TEMP_AVG_SLOPE 0.0043f
/** @brief ADC 上电后稳定等待时间,单位 ms。 */
#define CONFIG_ADC_STABILIZE_DELAY_MS 2U
/** @brief ADC 规则通道采样时间配置。 */
#define CONFIG_ADC_SAMPLE_TIME ADC_SAMPLETIME_239POINT5
/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */
#define CONFIG_ADC_DEFAULT_VDDA 3.3f
/** @brief CAN 外设实例。 */
#define CONFIG_CAN_INSTANCE CAN0
/** @brief CAN 外设时钟使能枚举。 */
#define CONFIG_CAN_RCU RCU_CAN0
/** @brief CAN 状态报文标准帧 ID。 */
#define CONFIG_CAN_STATUS_ID 0x321U
/** @brief CAN 状态载荷中调试标识字符 0。 */
#define CONFIG_CAN_STATUS_TAG0 0x43U
/** @brief CAN 状态载荷中调试标识字符 1。 */
#define CONFIG_CAN_STATUS_TAG1 0x41U
/** @brief CAN 状态载荷中调试标识字符 2。 */
#define CONFIG_CAN_STATUS_TAG2 0x4EU
/** @brief CAN 状态载荷中调试标识字符 3。 */
#define CONFIG_CAN_STATUS_TAG3 0x46U
/** @brief 用户调度定时器实例。 */
#define CONFIG_SCHED_TIMER_INSTANCE TIMER2
/** @brief 用户调度定时器中断号。 */
#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn
/** @brief 用户调度定时器时钟使能宏参数。 */
#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2
/** @brief 用户调度定时器预分频值。 */
#define CONFIG_SCHED_TIMER_PRESCALER 179U
/** @brief 用户调度定时器周期重装值。 */
#define CONFIG_SCHED_TIMER_PERIOD 99U
/** @brief 用户调度定时器 NVIC 抢占优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO 1U
/** @brief 用户调度定时器 NVIC 子优先级。 */
#define CONFIG_SCHED_TIMER_IRQ_SUB_PRIO 0U
/** @brief 调度基准节拍:ADC VREF 采样周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U
/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U
/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U
/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
#endif /* SYS_CONFIG_H */
+7 -1
View File
@@ -13,13 +13,19 @@
#include "sys/systick.h"
#include "sys/task_manager.h"
/**
* @brief 执行应用级外设与调度初始化。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 依次完成 SysTick、USART0、ADC/温度采样、CANFD、任务管理器与调度定时器的启动。
*/
void AppInitializer::init()
{
SysTickTimer::initMs();
Usart0::configurePins();
Usart0::init();
TempSensor::init();
while(!Adc::dmaReady()) { }
CanFd::init();
TaskManager::init();
SchedulerTimer::init();
-28
View File
@@ -1,28 +0,0 @@
/**
* @file initial.cpp
* @brief 应用启动初始化实现。
*/
#include "sys/initial.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "device/usart0.h"
#include "device/Timer.h"
#include "sys/systick.h"
#include "sys/task_manager.h"
/**
* @brief 执行应用启动所需的底层模块初始化。
* @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。
*/
void app_init(void)
{
systick_config_ms();
usart0_gpio_config();
usart0_config();
temp_sensor_init();
canfd_init();
task_manager_init();
user_timer_init();
}
@@ -1,28 +0,0 @@
/**
* @file initial.cpp
* @brief 应用启动初始化实现。
*/
#include "sys/initial.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "device/usart0.h"
#include "device/Timer.h"
#include "sys/systick.h"
#include "sys/task_manager.h"
/**
* @brief 执行应用启动所需的底层模块初始化。
* @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。
*/
void app_init(void)
{
systick_config_ms();
usart0_gpio_config();
usart0_config();
temp_sensor_init();
canfd_init();
task_manager_init();
user_timer_init();
}
-31
View File
@@ -1,31 +0,0 @@
/**
* @file initial.cpp
* @brief 应用启动初始化实现。
*/
#include "sys/initial.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "device/usart0.h"
#include "device/Timer.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/task_manager.h"
/**
* @brief 执行应用启动所需的底层模块初始化。
* @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。
*/
void app_init(void)
{
systick_config_ms();
usart0_gpio_config();
usart0_config();
temp_sensor_init();
while(adc_dma_ready() == 0U) {
}
canfd_init();
task_manager_init();
user_timer_init();
}
@@ -1,28 +0,0 @@
/**
* @file initial.cpp
* @brief 应用启动初始化实现。
*/
#include "initial.h"
#include "../device/canfd.h"
#include "../device/temp_sensor.h"
#include "../device/usart0.h"
#include "../device/Timer.h"
#include "systick.h"
#include "task_manager.h"
/**
* @brief 执行应用启动所需的底层模块初始化。
* @note 初始化顺序:系统时基 → 串口 → 温度采集 → CAN 通信 → 任务调度 → 定时器。
*/
void app_init(void)
{
systick_config_ms();
usart0_gpio_config();
usart0_config();
temp_sensor_init();
canfd_init();
task_manager_init();
user_timer_init();
}
+9 -2
View File
@@ -3,11 +3,18 @@
/**
* @brief 应用初始化器。
* @note 负责组织系统时基、串口、采样、通信、调度器等模块的启动顺序。
* @note 负责按固定顺序组织系统时基、串口、采样、通信、调度器等底层模块启动。
*/
class AppInitializer {
public:
/**
* @brief 按固定顺序完成应用所有底层模块初始化。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 初始化顺序:SysTick → USART0 GPIO/外设 → 温度采样 → 等待 ADC DMA 就绪 → CANFD → 任务调度 → 调度定时器。
*/
static void init();
};
#endif /* SYS_INITIAL_H */
#endif /* SYS_INITIAL_H */
-17
View File
@@ -1,17 +0,0 @@
/**
* @file initial.h
* @brief 应用初始化入口接口定义。
*/
#ifndef SYS_INITIAL_H
#define SYS_INITIAL_H
/**
* @brief 执行应用启动所需的底层模块初始化。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 按既定顺序初始化系统时基、串口、采样、通信与任务调度相关模块。
*/
void app_init(void);
#endif /* SYS_INITIAL_H */
+29 -1
View File
@@ -8,17 +8,38 @@
volatile uint32_t SysTickTimer::s_ms = 0U;
/**
* @brief SysTick 中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 每次 SysTick 中断到来时,将毫秒计数器自增 1。
*/
void SysTickTimer::onInterrupt()
{
s_ms++;
}
/**
* @brief 执行毫秒级阻塞延时。
* @param[in] ms 需要延时的毫秒数。
* @param[out] 无。
* @retval 无返回值。
* @note 基于 SysTick 维护的毫秒计数器忙等,适用于短时初始化等待。
*/
void SysTickTimer::delayMs(uint32_t ms)
{
uint32_t start = s_ms;
while((s_ms - start) < ms) { }
}
/**
* @brief 初始化 1ms SysTick 时基。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 先更新 `SystemCoreClock`,再按 1kHz 频率配置 SysTick。
*/
void SysTickTimer::initMs()
{
SystemCoreClockUpdate();
@@ -26,4 +47,11 @@ void SysTickTimer::initMs()
}
/* ----- 中断入口 C 包装 ---------------------------------------------- */
void SysTick_Handler(void) { SysTickTimer::onInterrupt(); }
/**
* @brief SysTick 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note C 向量包装函数,内部转调 `SysTickTimer::onInterrupt()`。
*/
void SysTick_Handler(void) { SysTickTimer::onInterrupt(); }
@@ -1,38 +0,0 @@
/**
* @file systick.cpp
* @brief SysTick 时基配置、中断处理与毫秒延时实现。
*/
#include "main.h"
#include "sys/systick.h"
/** @brief 自启动后累计的系统节拍毫秒数。 */
static volatile uint32_t systick_ms = 0U;
/**
* @brief SysTick 中断服务例程,每毫秒递增一次节拍计数器。
*/
void SysTick_Handler(void)
{
systick_ms++;
}
/**
* @brief 执行阻塞式毫秒延时。
* @param[in] ms 需要延时的毫秒数。
*/
void delay_ms(uint32_t ms)
{
uint32_t start = systick_ms;
while((systick_ms - start) < ms) {
}
}
/**
* @brief 配置 SysTick 为 1ms 系统节拍。
*/
void systick_config_ms(void)
{
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock / 1000U);
}
-38
View File
@@ -1,38 +0,0 @@
/**
* @file systick.cpp
* @brief SysTick 时基配置、中断处理与毫秒延时实现。
*/
#include "main.h"
#include "sys/systick.h"
/** @brief 自启动后累计的系统节拍毫秒数。 */
static volatile uint32_t systick_ms = 0U;
/**
* @brief SysTick 中断服务例程,每毫秒递增一次节拍计数器。
*/
void SysTick_Handler(void)
{
systick_ms++;
}
/**
* @brief 执行阻塞式毫秒延时。
* @param[in] ms 需要延时的毫秒数。
*/
void delay_ms(uint32_t ms)
{
uint32_t start = systick_ms;
while((systick_ms - start) < ms) {
}
}
/**
* @brief 配置 SysTick 为 1ms 系统节拍。
*/
void systick_config_ms(void)
{
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock / 1000U);
}
@@ -1,38 +0,0 @@
/**
* @file systick.cpp
* @brief SysTick 时基配置、中断处理与毫秒延时实现。
*/
#include "../main.h"
#include "systick.h"
/** @brief 自启动后累计的系统节拍毫秒数。 */
static volatile uint32_t systick_ms = 0U;
/**
* @brief SysTick 中断服务例程,每毫秒递增一次节拍计数器。
*/
void SysTick_Handler(void)
{
systick_ms++;
}
/**
* @brief 执行阻塞式毫秒延时。
* @param[in] ms 需要延时的毫秒数。
*/
void delay_ms(uint32_t ms)
{
uint32_t start = systick_ms;
while((systick_ms - start) < ms) {
}
}
/**
* @brief 配置 SysTick 为 1ms 系统节拍。
*/
void systick_config_ms(void)
{
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock / 1000U);
}
+23 -1
View File
@@ -9,12 +9,34 @@
*/
class SysTickTimer {
public:
/**
* @brief 初始化 SysTick 为 1ms 系统节拍。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
static void initMs();
/**
* @brief 执行阻塞式毫秒延时。
* @param[in] ms 需要延时的毫秒数。
* @param[out] 无。
* @retval 无返回值。
* @note 基于 1ms 中断递增的节拍计数器实现轮询延时。
*/
static void delayMs(uint32_t ms);
/**
* @brief SysTick 中断处理入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 每毫秒递增内部节拍计数器。
*/
static void onInterrupt();
private:
static volatile uint32_t s_ms;
};
#endif /* SYS_SYSTICK_H */
#endif /* SYS_SYSTICK_H */
-26
View File
@@ -1,26 +0,0 @@
/**
* @file systick.h
* @brief SysTick 时基配置与毫秒延时接口定义。
*/
#ifndef SYS_SYSTICK_H
#define SYS_SYSTICK_H
#include <stdint.h>
/**
* @brief 配置 SysTick 为 1ms 系统节拍。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void systick_config_ms(void);
/**
* @brief 执行阻塞式毫秒延时。
* @param[in] ms 需要延时的毫秒数。
* @param[out] 无。
* @retval 无返回值。
*/
void delay_ms(uint32_t ms);
#endif /* SYS_SYSTICK_H */
@@ -1,63 +0,0 @@
#include "main.h"
#include "sys/task_manager.h"
task_scheduler_t g_scheduler = {0};
void task_manager_init(void)
{
g_scheduler.task_flags = 0U;
g_scheduler.tick_100us = 0U;
g_scheduler.tick_1ms = 0U;
g_scheduler.tick_10ms = 0U;
g_scheduler.tick_100ms = 0U;
g_scheduler.tick_1s = 0U;
}
void task_manager_set(uint32_t task_mask)
{
g_scheduler.task_flags |= task_mask;
}
uint32_t task_manager_take_pending(void)
{
uint32_t pending;
__disable_irq();
pending = g_scheduler.task_flags;
g_scheduler.task_flags &= ~pending;
__enable_irq();
return pending;
}
void task_manager_tick_100us(void)
{
g_scheduler.tick_100us++;
if((g_scheduler.tick_100us % 5U) == 0U) {
task_manager_set(1UL << TASK_ADC_VREF_SAMPLE);
}
if((g_scheduler.tick_100us % 10U) == 0U) {
g_scheduler.tick_1ms++;
task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) |
(1UL << TASK_ADC_PROCESS));
}
if((g_scheduler.tick_100us % 100U) == 0U) {
g_scheduler.tick_10ms++;
task_manager_set(1UL << TASK_CAN_POLL);
}
if((g_scheduler.tick_100us % 1000U) == 0U) {
g_scheduler.tick_100ms++;
task_manager_set((1UL << TASK_CAN_REPORT) |
(1UL << TASK_STATUS_LED));
}
if((g_scheduler.tick_100us % 10000U) == 0U) {
g_scheduler.tick_1s++;
task_manager_set((1UL << TASK_HEARTBEAT) |
(1UL << TASK_DIAGNOSTIC_PRINT));
}
}
+51 -6
View File
@@ -9,6 +9,13 @@
task_scheduler_t g_scheduler = {0};
/**
* @brief 初始化任务调度器内部状态。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 清零待执行任务位图以及所有分频节拍计数器。
*/
void TaskManager::init()
{
g_scheduler.task_flags = 0U;
@@ -17,13 +24,32 @@ void TaskManager::init()
g_scheduler.tick_10ms = 0U;
g_scheduler.tick_100ms = 0U;
g_scheduler.tick_1s = 0U;
g_scheduler.div_vref_sample = 0U;
g_scheduler.div_temp_sample = 0U;
g_scheduler.div_can_rx = 0U;
g_scheduler.div_can_tx = 0U;
g_scheduler.div_heartbeat = 0U;
}
/**
* @brief 置位一个或多个待执行任务标志。
* @param[in] taskMask 待置位的任务位图掩码。
* @param[out] 无。
* @retval 无返回值。
* @note 该操作不清除其他已存在的任务位。
*/
void TaskManager::set(uint32_t taskMask)
{
g_scheduler.task_flags |= taskMask;
}
/**
* @brief 原子地取出当前所有待执行任务位图并清零。
* @param[in] 无。
* @param[out] 无。
* @retval uint32_t 返回当前待执行任务位图。
* @note 内部通过关中断实现对 `task_flags` 的原子交换。
*/
uint32_t TaskManager::takePending()
{
uint32_t pending;
@@ -34,35 +60,54 @@ uint32_t TaskManager::takePending()
return pending;
}
/**
* @brief 推进一次 100us 调度节拍。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 按预设周期置位 ADC、CAN 接收、状态灯、心跳与诊断打印等任务。
*/
void TaskManager::tick100us()
{
g_scheduler.tick_100us++;
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS) == 0U) {
if(++g_scheduler.div_vref_sample >= CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS) {
g_scheduler.div_vref_sample = 0U;
set(1UL << TASK_ADC_VREF_SAMPLE);
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS) == 0U) {
if(++g_scheduler.div_temp_sample >= CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS) {
g_scheduler.div_temp_sample = 0U;
g_scheduler.tick_1ms++;
set((1UL << TASK_ADC_TEMP_SAMPLE) | (1UL << TASK_ADC_PROCESS));
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_RX_TICKS) == 0U) {
if(++g_scheduler.div_can_rx >= CONFIG_TASK_PERIOD_CAN_RX_TICKS) {
g_scheduler.div_can_rx = 0U;
g_scheduler.tick_10ms++;
set((1UL << TASK_CAN_RX_POLL) | (1UL << TASK_CAN_RX_PARSE));
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_TX_TICKS) == 0U) {
if(++g_scheduler.div_can_tx >= CONFIG_TASK_PERIOD_CAN_TX_TICKS) {
g_scheduler.div_can_tx = 0U;
g_scheduler.tick_100ms++;
set((1UL << TASK_CAN_TX_PREPARE) | (1UL << TASK_CAN_TX_SEND) | (1UL << TASK_STATUS_LED));
set(1UL << TASK_STATUS_LED);
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) == 0U) {
if(++g_scheduler.div_heartbeat >= CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) {
g_scheduler.div_heartbeat = 0U;
g_scheduler.tick_1s++;
set((1UL << TASK_HEARTBEAT) | (1UL << TASK_DIAGNOSTIC_PRINT));
}
}
/**
* @brief 获取全局调度器状态对象引用。
* @param[in] 无。
* @param[out] 无。
* @retval task_scheduler_t& 返回全局调度器状态对象引用。
* @note 供调试或其他模块观察当前节拍与任务位图。
*/
task_scheduler_t &TaskManager::state()
{
return g_scheduler;
@@ -1,85 +0,0 @@
/**
* @file task_manager.cpp
* @brief 任务标志调度与时基驱动实现。
*/
#include "main.h"
#include "sys/task_manager.h"
task_scheduler_t g_scheduler = {0};
/**
* @brief 初始化任务管理器内部状态。
*/
void task_manager_init(void)
{
g_scheduler.task_flags = 0U;
g_scheduler.tick_100us = 0U;
g_scheduler.tick_1ms = 0U;
g_scheduler.tick_10ms = 0U;
g_scheduler.tick_100ms = 0U;
g_scheduler.tick_1s = 0U;
}
/**
* @brief 设置一个或多个待执行任务标志。
* @param[in] task_mask 待置位的任务位图掩码。
*/
void task_manager_set(uint32_t task_mask)
{
g_scheduler.task_flags |= task_mask;
}
/**
* @brief 原子地取出当前所有待执行任务标志并清零。
* @retval 返回当前待执行任务位图。
*/
uint32_t task_manager_take_pending(void)
{
uint32_t pending;
__disable_irq();
pending = g_scheduler.task_flags;
g_scheduler.task_flags &= ~pending;
__enable_irq();
return pending;
}
/**
* @brief 推进一次 100us 调度节拍并按周期置位待执行任务。
* @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。
*/
void task_manager_tick_100us(void)
{
g_scheduler.tick_100us++;
if((g_scheduler.tick_100us % 5U) == 0U) {
task_manager_set(1UL << TASK_ADC_VREF_SAMPLE);
}
if((g_scheduler.tick_100us % 10U) == 0U) {
g_scheduler.tick_1ms++;
task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) |
(1UL << TASK_ADC_PROCESS));
}
if((g_scheduler.tick_100us % 100U) == 0U) {
g_scheduler.tick_10ms++;
task_manager_set((1UL << TASK_CAN_RX_POLL) |
(1UL << TASK_CAN_RX_PARSE));
}
if((g_scheduler.tick_100us % 1000U) == 0U) {
g_scheduler.tick_100ms++;
task_manager_set((1UL << TASK_CAN_TX_PREPARE) |
(1UL << TASK_CAN_TX_SEND) |
(1UL << TASK_STATUS_LED));
}
if((g_scheduler.tick_100us % 10000U) == 0U) {
g_scheduler.tick_1s++;
task_manager_set((1UL << TASK_HEARTBEAT) |
(1UL << TASK_DIAGNOSTIC_PRINT));
}
}
@@ -1,85 +0,0 @@
/**
* @file task_manager.cpp
* @brief 任务标志调度与时基驱动实现。
*/
#include "main.h"
#include "sys/task_manager.h"
task_scheduler_t g_scheduler = {0};
/**
* @brief 初始化任务管理器内部状态。
*/
void task_manager_init(void)
{
g_scheduler.task_flags = 0U;
g_scheduler.tick_100us = 0U;
g_scheduler.tick_1ms = 0U;
g_scheduler.tick_10ms = 0U;
g_scheduler.tick_100ms = 0U;
g_scheduler.tick_1s = 0U;
}
/**
* @brief 设置一个或多个待执行任务标志。
* @param[in] task_mask 待置位的任务位图掩码。
*/
void task_manager_set(uint32_t task_mask)
{
g_scheduler.task_flags |= task_mask;
}
/**
* @brief 原子地取出当前所有待执行任务标志并清零。
* @retval 返回当前待执行任务位图。
*/
uint32_t task_manager_take_pending(void)
{
uint32_t pending;
__disable_irq();
pending = g_scheduler.task_flags;
g_scheduler.task_flags &= ~pending;
__enable_irq();
return pending;
}
/**
* @brief 推进一次 100us 调度节拍并按周期置位待执行任务。
* @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。
*/
void task_manager_tick_100us(void)
{
g_scheduler.tick_100us++;
if((g_scheduler.tick_100us % 5U) == 0U) {
task_manager_set(1UL << TASK_ADC_VREF_SAMPLE);
}
if((g_scheduler.tick_100us % 10U) == 0U) {
g_scheduler.tick_1ms++;
task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) |
(1UL << TASK_ADC_PROCESS));
}
if((g_scheduler.tick_100us % 100U) == 0U) {
g_scheduler.tick_10ms++;
task_manager_set((1UL << TASK_CAN_RX_POLL) |
(1UL << TASK_CAN_RX_PARSE));
}
if((g_scheduler.tick_100us % 1000U) == 0U) {
g_scheduler.tick_100ms++;
task_manager_set((1UL << TASK_CAN_TX_PREPARE) |
(1UL << TASK_CAN_TX_SEND) |
(1UL << TASK_STATUS_LED));
}
if((g_scheduler.tick_100us % 10000U) == 0U) {
g_scheduler.tick_1s++;
task_manager_set((1UL << TASK_HEARTBEAT) |
(1UL << TASK_DIAGNOSTIC_PRINT));
}
}
@@ -1,86 +0,0 @@
/**
* @file task_manager.cpp
* @brief 任务标志调度与时基驱动实现。
*/
#include "main.h"
#include "sys/task_manager.h"
#include "sys/config.h"
task_scheduler_t g_scheduler = {0};
/**
* @brief 初始化任务管理器内部状态。
*/
void task_manager_init(void)
{
g_scheduler.task_flags = 0U;
g_scheduler.tick_100us = 0U;
g_scheduler.tick_1ms = 0U;
g_scheduler.tick_10ms = 0U;
g_scheduler.tick_100ms = 0U;
g_scheduler.tick_1s = 0U;
}
/**
* @brief 设置一个或多个待执行任务标志。
* @param[in] task_mask 待置位的任务位图掩码。
*/
void task_manager_set(uint32_t task_mask)
{
g_scheduler.task_flags |= task_mask;
}
/**
* @brief 原子地取出当前所有待执行任务标志并清零。
* @retval 返回当前待执行任务位图。
*/
uint32_t task_manager_take_pending(void)
{
uint32_t pending;
__disable_irq();
pending = g_scheduler.task_flags;
g_scheduler.task_flags &= ~pending;
__enable_irq();
return pending;
}
/**
* @brief 推进一次 100us 调度节拍并按周期置位待执行任务。
* @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。
*/
void task_manager_tick_100us(void)
{
g_scheduler.tick_100us++;
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS) == 0U) {
task_manager_set(1UL << TASK_ADC_VREF_SAMPLE);
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS) == 0U) {
g_scheduler.tick_1ms++;
task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) |
(1UL << TASK_ADC_PROCESS));
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_RX_TICKS) == 0U) {
g_scheduler.tick_10ms++;
task_manager_set((1UL << TASK_CAN_RX_POLL) |
(1UL << TASK_CAN_RX_PARSE));
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_CAN_TX_TICKS) == 0U) {
g_scheduler.tick_100ms++;
task_manager_set((1UL << TASK_CAN_TX_PREPARE) |
(1UL << TASK_CAN_TX_SEND) |
(1UL << TASK_STATUS_LED));
}
if((g_scheduler.tick_100us % CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) == 0U) {
g_scheduler.tick_1s++;
task_manager_set((1UL << TASK_HEARTBEAT) |
(1UL << TASK_DIAGNOSTIC_PRINT));
}
}
@@ -1,85 +0,0 @@
/**
* @file task_manager.cpp
* @brief 任务标志调度与时基驱动实现。
*/
#include "../main.h"
#include "task_manager.h"
task_scheduler_t g_scheduler = {0};
/**
* @brief 初始化任务管理器内部状态。
*/
void task_manager_init(void)
{
g_scheduler.task_flags = 0U;
g_scheduler.tick_100us = 0U;
g_scheduler.tick_1ms = 0U;
g_scheduler.tick_10ms = 0U;
g_scheduler.tick_100ms = 0U;
g_scheduler.tick_1s = 0U;
}
/**
* @brief 设置一个或多个待执行任务标志。
* @param[in] task_mask 待置位的任务位图掩码。
*/
void task_manager_set(uint32_t task_mask)
{
g_scheduler.task_flags |= task_mask;
}
/**
* @brief 原子地取出当前所有待执行任务标志并清零。
* @retval 返回当前待执行任务位图。
*/
uint32_t task_manager_take_pending(void)
{
uint32_t pending;
__disable_irq();
pending = g_scheduler.task_flags;
g_scheduler.task_flags &= ~pending;
__enable_irq();
return pending;
}
/**
* @brief 推进一次 100us 调度节拍并按周期置位待执行任务。
* @note 按 500us / 1ms / 10ms / 100ms / 1s 档次分别置位对应任务组。
*/
void task_manager_tick_100us(void)
{
g_scheduler.tick_100us++;
if((g_scheduler.tick_100us % 5U) == 0U) {
task_manager_set(1UL << TASK_ADC_VREF_SAMPLE);
}
if((g_scheduler.tick_100us % 10U) == 0U) {
g_scheduler.tick_1ms++;
task_manager_set((1UL << TASK_ADC_TEMP_SAMPLE) |
(1UL << TASK_ADC_PROCESS));
}
if((g_scheduler.tick_100us % 100U) == 0U) {
g_scheduler.tick_10ms++;
task_manager_set((1UL << TASK_CAN_RX_POLL) |
(1UL << TASK_CAN_RX_PARSE));
}
if((g_scheduler.tick_100us % 1000U) == 0U) {
g_scheduler.tick_100ms++;
task_manager_set((1UL << TASK_CAN_TX_PREPARE) |
(1UL << TASK_CAN_TX_SEND) |
(1UL << TASK_STATUS_LED));
}
if((g_scheduler.tick_100us % 10000U) == 0U) {
g_scheduler.tick_1s++;
task_manager_set((1UL << TASK_HEARTBEAT) |
(1UL << TASK_DIAGNOSTIC_PRINT));
}
}
+40 -2
View File
@@ -6,15 +6,53 @@
/**
* @brief 任务调度管理器。
* @note 负责维护节拍计数器与待执行任务位图。
* @note 负责维护各类节拍计数器与待执行任务位图,由定时器中断与主循环共享访问。
*/
class TaskManager {
public:
/**
* @brief 初始化调度器内部状态。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 将所有节拍计数器和任务标志位清零。
*/
static void init();
/**
* @brief 推进一次 100us 调度节拍。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/LED/心跳/诊断等任务标志。
*/
static void tick100us();
/**
* @brief 原子地取出当前所有待执行任务位图并清零。
* @param[in] 无。
* @param[out] 无。
* @retval uint32_t 返回当前待执行任务位图。
* @note 内部通过关中断保证原子性,供 TaskRunner 消费。
*/
static uint32_t takePending();
/**
* @brief 置位一个或多个待执行任务标志。
* @param[in] taskMask 待置位的任务位图掩码。
* @param[out] 无。
* @retval 无返回值。
*/
static void set(uint32_t taskMask);
/**
* @brief 获取全局调度器状态对象引用。
* @param[in] 无。
* @param[out] 无。
* @retval task_scheduler_t& 全局调度器状态对象的引用。
* @note 提供外部模块对调度状态的可观型访问。
*/
static task_scheduler_t &state();
};
#endif /* SYS_TASK_MANAGER_H */
#endif /* SYS_TASK_MANAGER_H */
@@ -1,11 +0,0 @@
#ifndef SYS_TASK_MANAGER_H
#define SYS_TASK_MANAGER_H
#include <stdint.h>
void task_manager_init(void);
void task_manager_tick_100us(void);
uint32_t task_manager_take_pending(void);
void task_manager_set(uint32_t task_mask);
#endif /* SYS_TASK_MANAGER_H */
@@ -1,42 +0,0 @@
/**
* @file task_manager.h
* @brief 任务标志调度与时基驱动接口定义。
*/
#ifndef SYS_TASK_MANAGER_H
#define SYS_TASK_MANAGER_H
#include <stdint.h>
/**
* @brief 初始化任务管理器内部状态。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void task_manager_init(void);
/**
* @brief 推进一次 100us 调度节拍并按周期置位待执行任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void task_manager_tick_100us(void);
/**
* @brief 原子地取出当前所有待执行任务标志。
* @param[in] 无。
* @param[out] 无。
* @retval uint32_t 返回当前待执行任务位图,并在内部清除对应标志。
*/
uint32_t task_manager_take_pending(void);
/**
* @brief 设置一个或多个待执行任务标志。
* @param[in] task_mask 待置位的任务位图掩码。
* @param[out] 无。
* @retval 无返回值。
*/
void task_manager_set(uint32_t task_mask);
#endif /* SYS_TASK_MANAGER_H */
+123 -31
View File
@@ -7,87 +7,181 @@
#include "device/adc.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "sys/config.h"
#include "sys/task_manager.h"
#include "sys/task_runner.h"
struct TaskRunner::Runtime {
temp_sensor_data_t sample{};
uint16_t last_vin_raw;
uint16_t last_vref_raw;
uint16_t last_temp_raw;
uint32_t last_rx_id;
uint8_t last_rx_len;
uint8_t last_rx_data[64];
uint8_t last_rx_channel = 0U;
uint32_t last_rx_id = 0U;
uint8_t last_rx_len = 0U;
uint8_t last_rx_data[64] = {0};
};
TaskRunner::Runtime TaskRunner::s_runtime;
/**
* @brief 采样 VIN 与 VREF 原始 ADC 数值。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 从 ADC DMA 缓冲中读取外部 VIN_ADC 与内部 VREFINT 通道最新值。
*/
void TaskRunner::taskAdcVrefSample()
{
s_runtime.last_vin_raw = Adc::readVinRaw();
s_runtime.last_vref_raw = Adc::readVrefRaw();
Adc::collectSample(&s_runtime.sample);
}
/**
* @brief 采样内部温度通道原始 ADC 数值。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 读取 ADC DMA 缓冲中的内部温度通道最新值。
*/
void TaskRunner::taskAdcTempSample()
{
s_runtime.last_temp_raw = Adc::readTempRaw();
Adc::collectSample(&s_runtime.sample);
}
/**
* @brief 根据原始采样值计算工程量结果。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 输出 `vin_adc_v`、`vdda` 和 `temperature` 等可直接使用的工程值。
*/
void TaskRunner::taskAdcProcess()
{
auto &s = s_runtime.sample;
s.vin_raw = s_runtime.last_vin_raw;
s.vref_raw = s_runtime.last_vref_raw;
s.temp_raw = s_runtime.last_temp_raw;
s.vdda = Adc::calculateVdda(s.vref_raw);
s.vin_adc_v = Adc::calculateVinAdcVoltage(s.vin_raw, s.vdda);
s.temperature = Adc::calculateTemperature(s.temp_raw, s.vdda);
/* 当前采样任务已直接调用 Adc::collectSample() 填充工程值。
* 该处理任务保留为调度兼容入口,后续若重新拆分采样/换算阶段,可在此恢复。 */
(void)s_runtime.sample;
}
/**
* @brief 执行兼容保留的 CAN 接收轮询任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前三路 CANFD 接收已由 RX0 中断驱动,该任务仅保留接口兼容性。
*/
void TaskRunner::taskCanRxPoll() { CanFd::poll(); }
void TaskRunner::taskCanTxSend() { CanFd::sendPrepared(); }
/**
* @brief 执行预留的 CAN 发送执行任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前阶段不做周期性发送调度;若后续需要显式验证某一路发送能力,可由调用方先 prepare,再直接调用 `CanFd::sendPrepared()`。
*/
void TaskRunner::taskCanTxSend()
{
/* 当前阶段不做周期性发送调度。
* 若后续需要显式验证某一路发送能力,可由调用方先 prepare,再直接调用 CanFd::sendPrepared()。 */
}
/**
* @brief 解析并消费一帧待处理的 CAN 接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前存在待消费报文,则更新最近一次接收的 ID、数据长度、数据内容与来源通道号。
*/
void TaskRunner::taskCanRxParse()
{
if(CanFd::hasRxFrame())
CanFd::consumeRxFrame(&s_runtime.last_rx_id, s_runtime.last_rx_data, &s_runtime.last_rx_len);
CanFd::consumeRxFrame(&s_runtime.last_rx_id, s_runtime.last_rx_data, &s_runtime.last_rx_len, &s_runtime.last_rx_channel);
}
/**
* @brief 执行预留的 CAN 发送准备任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前阶段三路 CANFD 仅要求底层收发能力可用,不做周期性业务状态广播;后续若需要恢复按通道发送,可在此处显式调用 `CanFd::prepareStatusForChannel()` 或 `CanFd::prepareStatus()`。
*/
void TaskRunner::taskCanTxPrepare()
{
CanFd::StatusPayload status;
status.temp_raw = s_runtime.sample.temp_raw;
status.vref_raw = s_runtime.sample.vref_raw;
status.vdda = s_runtime.sample.vdda;
status.temperature = s_runtime.sample.temperature;
CanFd::prepareStatus(&status);
/* 当前阶段三路 CANFD 只要求底层收发能力可用,不做周期性业务状态广播。
* 保留该任务入口,后续若需要恢复按通道发送,可在此处显式调用:
* CanFd::prepareStatusForChannel(channel, &status);
* 或:
* CanFd::prepareStatus(&status);
*/
}
/**
* @brief 执行状态灯维护任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前仅保留任务入口,具体状态灯策略尚未实现。
*/
void TaskRunner::taskStatusLed() { /* 预留 */ }
/**
* @brief 执行系统心跳维护任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前仅保留任务入口,后续可在此加入周期健康检查或轻量保活逻辑。
*/
void TaskRunner::taskHeartbeat() { /* 预留 */ }
/**
* @brief 输出当前温度、电压与 CAN 诊断信息。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 由 1 秒节拍触发,通过 UART 打印当前温度、电压,以及最近一次 CAN 接收与统计信息。
*/
void TaskRunner::taskDiagnosticPrint()
{
const auto *diag = CanFd::diag();
const auto &s = s_runtime.sample;
printf("vin_raw=%u, vin_adc=%.3f V, temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n",
s.vin_raw, s.vin_adc_v, s.temp_raw, s.vref_raw, s.vdda, s.temperature,
#if (CONFIG_UART_DIAG_LEVEL == 0)
printf("temp=%.2f C, vdda=%.3f V, vin_adc=%.3f V\r\n",
s.temperature,
s.vdda,
s.vin_adc_v);
#else
printf("temp=%.2f C, vdda=%.3f V, vin_adc=%.3f V, rx_ch=%u, rx_id=0x%08lX, rx_len=%u, tx_ch=%u, can_tx=%lu, can_rx=%lu, can_drop=%lu, tx_drop=%lu, can_err=%lu\r\n",
s.temperature,
s.vdda,
s.vin_adc_v,
static_cast<unsigned int>(s_runtime.last_rx_channel),
static_cast<unsigned long>(s_runtime.last_rx_id),
static_cast<unsigned int>(s_runtime.last_rx_len),
static_cast<unsigned int>(diag->last_tx_channel),
static_cast<unsigned long>(diag->tx_count),
static_cast<unsigned long>(diag->rx_count),
static_cast<unsigned long>(diag->rx_drop_count),
static_cast<unsigned long>(diag->tx_drop_count),
static_cast<unsigned long>(diag->error_count));
#endif
}
/**
* @brief 初始化任务执行器运行时上下文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 执行一次完整 ADC 采样,并同步填充运行时缓存的原始值起点。
*/
void TaskRunner::init()
{
Adc::collectSample(&s_runtime.sample);
s_runtime.last_vin_raw = s_runtime.sample.vin_raw;
s_runtime.last_vref_raw = s_runtime.sample.vref_raw;
s_runtime.last_temp_raw = s_runtime.sample.temp_raw;
}
/**
* @brief 执行当前所有已置位的待处理任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 先原子取出任务位图;若当前没有待处理任务则直接返回。
*/
void TaskRunner::runPending()
{
const uint32_t pending = TaskManager::takePending();
@@ -98,8 +192,6 @@ void TaskRunner::runPending()
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_PREPARE)) taskCanTxPrepare();
if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend();
if(pending & (1UL << TASK_STATUS_LED)) taskStatusLed();
if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat();
if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint();
@@ -1,179 +0,0 @@
/**
* @file task_runner.cpp
* @brief 任务执行器初始化与待执行任务分发实现。
*/
#include "main.h"
#include "device/adc.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "sys/task_manager.h"
#include "sys/task_runner.h"
namespace {
/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */
struct AppRuntime {
temp_sensor_data_t sample{};
uint16_t last_vref_raw = 0U;
uint16_t last_temp_raw = 0U;
uint32_t last_rx_id = 0U;
uint8_t last_rx_len = 0U;
uint8_t last_rx_data[64] = {0};
};
AppRuntime g_app{};
/**
* @brief 采样内部参考电压原始值。
*/
inline void task_adc_vref_sample()
{
g_app.last_vref_raw = adc_read_vref_raw();
}
/**
* @brief 采样内部温度通道原始值。
*/
inline void task_adc_temp_sample()
{
g_app.last_temp_raw = adc_read_temp_raw();
}
/**
* @brief 根据采样原始值换算 VDDA 与芯片温度。
*/
inline void task_adc_process()
{
g_app.sample.vref_raw = g_app.last_vref_raw;
g_app.sample.temp_raw = g_app.last_temp_raw;
g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw);
g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda);
}
/**
* @brief 轮询 CANFD 接收 FIFO。
*/
inline void task_can_rx_poll()
{
canfd_poll();
}
/**
* @brief 解析并消费已接收的 CANFD 帧。
*/
inline void task_can_rx_parse()
{
if(canfd_has_rx_frame() != 0U) {
canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len);
}
}
/**
* @brief 准备 CANFD 状态发送帧。
*/
inline void task_can_tx_prepare()
{
const canfd_status_payload_t status {
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
};
canfd_prepare_status(&status);
}
/**
* @brief 发送已准备好的 CANFD 帧。
*/
inline void task_can_tx_send()
{
canfd_send_prepared();
}
/**
* @brief 状态灯维护任务(当前为预留占位)。
*/
inline void task_status_led()
{
}
/**
* @brief 系统心跳维护任务(当前为预留占位)。
*/
inline void task_heartbeat()
{
}
/**
* @brief 诊断信息打印任务,输出当前所有运行时数据。
*/
inline void task_diagnostic_print()
{
const canfd_diag_t *diag = canfd_get_diag();
printf("temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n",
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
static_cast<unsigned long>(g_app.last_rx_id),
static_cast<unsigned int>(g_app.last_rx_len),
static_cast<unsigned long>(diag->tx_count),
static_cast<unsigned long>(diag->rx_count),
static_cast<unsigned long>(diag->error_count));
}
} // namespace
/**
* @brief 初始化任务执行器运行时上下文。
* @note 执行一次初始采样,填充运行时数据起点。
*/
void task_runner_init(void)
{
adc_collect_sample(&g_app.sample);
g_app.last_vref_raw = g_app.sample.vref_raw;
g_app.last_temp_raw = g_app.sample.temp_raw;
}
/**
* @brief 执行当前所有已置位的待处理任务。
* @note 若当前没有待执行任务,直接返回。
*/
void task_runner_run_pending(void)
{
const uint32_t pending = task_manager_take_pending();
if(pending == 0U) {
return;
}
if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) {
task_adc_vref_sample();
}
if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) {
task_adc_temp_sample();
}
if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) {
task_adc_process();
}
if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) {
task_can_rx_poll();
}
if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) {
task_can_rx_parse();
}
if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) {
task_can_tx_prepare();
}
if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) {
task_can_tx_send();
}
if((pending & (1UL << TASK_STATUS_LED)) != 0U) {
task_status_led();
}
if((pending & (1UL << TASK_HEARTBEAT)) != 0U) {
task_heartbeat();
}
if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) {
task_diagnostic_print();
}
}
@@ -1,186 +0,0 @@
/**
* @file task_runner.cpp
* @brief 任务执行器初始化与待执行任务分发实现。
*/
#include "main.h"
#include "device/adc.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "sys/task_manager.h"
#include "sys/task_runner.h"
namespace {
/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */
struct AppRuntime {
temp_sensor_data_t sample{};
uint16_t last_vin_raw = 0U;
uint16_t last_vref_raw = 0U;
uint16_t last_temp_raw = 0U;
uint32_t last_rx_id = 0U;
uint8_t last_rx_len = 0U;
uint8_t last_rx_data[64] = {0};
};
AppRuntime g_app{};
/**
* @brief 采样内部参考电压原始值。
*/
inline void task_adc_vref_sample()
{
g_app.last_vin_raw = adc_read_vin_raw();
g_app.last_vref_raw = adc_read_vref_raw();
}
/**
* @brief 采样内部温度通道原始值。
*/
inline void task_adc_temp_sample()
{
g_app.last_temp_raw = adc_read_temp_raw();
}
/**
* @brief 根据采样原始值换算 VDDA 与芯片温度。
*/
inline void task_adc_process()
{
g_app.sample.vin_raw = g_app.last_vin_raw;
g_app.sample.vref_raw = g_app.last_vref_raw;
g_app.sample.temp_raw = g_app.last_temp_raw;
g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw);
g_app.sample.vin_adc_v = adc_calculate_vin_adc_voltage(g_app.sample.vin_raw, g_app.sample.vdda);
g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda);
}
/**
* @brief 轮询 CANFD 接收 FIFO。
*/
inline void task_can_rx_poll()
{
canfd_poll();
}
/**
* @brief 解析并消费已接收的 CANFD 帧。
*/
inline void task_can_rx_parse()
{
if(canfd_has_rx_frame() != 0U) {
canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len);
}
}
/**
* @brief 准备 CANFD 状态发送帧。
*/
inline void task_can_tx_prepare()
{
const canfd_status_payload_t status {
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
};
canfd_prepare_status(&status);
}
/**
* @brief 发送已准备好的 CANFD 帧。
*/
inline void task_can_tx_send()
{
canfd_send_prepared();
}
/**
* @brief 状态灯维护任务(当前为预留占位)。
*/
inline void task_status_led()
{
}
/**
* @brief 系统心跳维护任务(当前为预留占位)。
*/
inline void task_heartbeat()
{
}
/**
* @brief 诊断信息打印任务,输出当前所有运行时数据。
*/
inline void task_diagnostic_print()
{
const canfd_diag_t *diag = canfd_get_diag();
printf("vin_raw=%u, vin_adc=%.3f V, temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n",
g_app.sample.vin_raw,
g_app.sample.vin_adc_v,
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
static_cast<unsigned long>(g_app.last_rx_id),
static_cast<unsigned int>(g_app.last_rx_len),
static_cast<unsigned long>(diag->tx_count),
static_cast<unsigned long>(diag->rx_count),
static_cast<unsigned long>(diag->error_count));
}
} // namespace
/**
* @brief 初始化任务执行器运行时上下文。
* @note 执行一次初始采样,填充运行时数据起点。
*/
void task_runner_init(void)
{
adc_collect_sample(&g_app.sample);
g_app.last_vin_raw = g_app.sample.vin_raw;
g_app.last_vref_raw = g_app.sample.vref_raw;
g_app.last_temp_raw = g_app.sample.temp_raw;
}
/**
* @brief 执行当前所有已置位的待处理任务。
* @note 若当前没有待执行任务,直接返回。
*/
void task_runner_run_pending(void)
{
const uint32_t pending = task_manager_take_pending();
if(pending == 0U) {
return;
}
if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) {
task_adc_vref_sample();
}
if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) {
task_adc_temp_sample();
}
if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) {
task_adc_process();
}
if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) {
task_can_rx_poll();
}
if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) {
task_can_rx_parse();
}
if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) {
task_can_tx_prepare();
}
if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) {
task_can_tx_send();
}
if((pending & (1UL << TASK_STATUS_LED)) != 0U) {
task_status_led();
}
if((pending & (1UL << TASK_HEARTBEAT)) != 0U) {
task_heartbeat();
}
if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) {
task_diagnostic_print();
}
}
@@ -1,179 +0,0 @@
/**
* @file task_runner.cpp
* @brief 任务执行器初始化与待执行任务分发实现。
*/
#include "../main.h"
#include "../device/adc.h"
#include "../device/canfd.h"
#include "../device/temp_sensor.h"
#include "task_manager.h"
#include "task_runner.h"
namespace {
/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */
struct AppRuntime {
temp_sensor_data_t sample{};
uint16_t last_vref_raw = 0U;
uint16_t last_temp_raw = 0U;
uint32_t last_rx_id = 0U;
uint8_t last_rx_len = 0U;
uint8_t last_rx_data[64] = {0};
};
AppRuntime g_app{};
/**
* @brief 采样内部参考电压原始值。
*/
inline void task_adc_vref_sample()
{
g_app.last_vref_raw = adc_read_vref_raw();
}
/**
* @brief 采样内部温度通道原始值。
*/
inline void task_adc_temp_sample()
{
g_app.last_temp_raw = adc_read_temp_raw();
}
/**
* @brief 根据采样原始值换算 VDDA 与芯片温度。
*/
inline void task_adc_process()
{
g_app.sample.vref_raw = g_app.last_vref_raw;
g_app.sample.temp_raw = g_app.last_temp_raw;
g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw);
g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda);
}
/**
* @brief 轮询 CANFD 接收 FIFO。
*/
inline void task_can_rx_poll()
{
canfd_poll();
}
/**
* @brief 解析并消费已接收的 CANFD 帧。
*/
inline void task_can_rx_parse()
{
if(canfd_has_rx_frame() != 0U) {
canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len);
}
}
/**
* @brief 准备 CANFD 状态发送帧。
*/
inline void task_can_tx_prepare()
{
const canfd_status_payload_t status {
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
};
canfd_prepare_status(&status);
}
/**
* @brief 发送已准备好的 CANFD 帧。
*/
inline void task_can_tx_send()
{
canfd_send_prepared();
}
/**
* @brief 状态灯维护任务(当前为预留占位)。
*/
inline void task_status_led()
{
}
/**
* @brief 系统心跳维护任务(当前为预留占位)。
*/
inline void task_heartbeat()
{
}
/**
* @brief 诊断信息打印任务,输出当前所有运行时数据。
*/
inline void task_diagnostic_print()
{
const canfd_diag_t *diag = canfd_get_diag();
printf("temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n",
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
static_cast<unsigned long>(g_app.last_rx_id),
static_cast<unsigned int>(g_app.last_rx_len),
static_cast<unsigned long>(diag->tx_count),
static_cast<unsigned long>(diag->rx_count),
static_cast<unsigned long>(diag->error_count));
}
} // namespace
/**
* @brief 初始化任务执行器运行时上下文。
* @note 执行一次初始采样,填充运行时数据起点。
*/
void task_runner_init(void)
{
adc_collect_sample(&g_app.sample);
g_app.last_vref_raw = g_app.sample.vref_raw;
g_app.last_temp_raw = g_app.sample.temp_raw;
}
/**
* @brief 执行当前所有已置位的待处理任务。
* @note 若当前没有待执行任务,直接返回。
*/
void task_runner_run_pending(void)
{
const uint32_t pending = task_manager_take_pending();
if(pending == 0U) {
return;
}
if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) {
task_adc_vref_sample();
}
if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) {
task_adc_temp_sample();
}
if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) {
task_adc_process();
}
if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) {
task_can_rx_poll();
}
if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) {
task_can_rx_parse();
}
if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) {
task_can_tx_prepare();
}
if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) {
task_can_tx_send();
}
if((pending & (1UL << TASK_STATUS_LED)) != 0U) {
task_status_led();
}
if((pending & (1UL << TASK_HEARTBEAT)) != 0U) {
task_heartbeat();
}
if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) {
task_diagnostic_print();
}
}
@@ -1,179 +0,0 @@
/**
* @file task_runner.cpp
* @brief 任务执行器初始化与待执行任务分发实现。
*/
#include "main.h"
#include "device/adc.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "sys/task_manager.h"
#include "sys/task_runner.h"
namespace {
/** @brief 应用运行时上下文,保存各任务间共享的最新数据。 */
struct AppRuntime {
temp_sensor_data_t sample{};
uint16_t last_vref_raw = 0U;
uint16_t last_temp_raw = 0U;
uint32_t last_rx_id = 0U;
uint8_t last_rx_len = 0U;
uint8_t last_rx_data[64] = {0};
};
AppRuntime g_app{};
/**
* @brief 采样内部参考电压原始值。
*/
inline void task_adc_vref_sample()
{
g_app.last_vref_raw = adc_read_vref_raw();
}
/**
* @brief 采样内部温度通道原始值。
*/
inline void task_adc_temp_sample()
{
g_app.last_temp_raw = adc_read_temp_raw();
}
/**
* @brief 根据采样原始值换算 VDDA 与芯片温度。
*/
inline void task_adc_process()
{
g_app.sample.vref_raw = g_app.last_vref_raw;
g_app.sample.temp_raw = g_app.last_temp_raw;
g_app.sample.vdda = adc_calculate_vdda(g_app.sample.vref_raw);
g_app.sample.temperature = adc_calculate_temperature(g_app.sample.temp_raw, g_app.sample.vdda);
}
/**
* @brief 轮询 CANFD 接收 FIFO。
*/
inline void task_can_rx_poll()
{
canfd_poll();
}
/**
* @brief 解析并消费已接收的 CANFD 帧。
*/
inline void task_can_rx_parse()
{
if(canfd_has_rx_frame() != 0U) {
canfd_consume_rx_frame(&g_app.last_rx_id, g_app.last_rx_data, &g_app.last_rx_len);
}
}
/**
* @brief 准备 CANFD 状态发送帧。
*/
inline void task_can_tx_prepare()
{
const canfd_status_payload_t status {
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
};
canfd_prepare_status(&status);
}
/**
* @brief 发送已准备好的 CANFD 帧。
*/
inline void task_can_tx_send()
{
canfd_send_prepared();
}
/**
* @brief 状态灯维护任务(当前为预留占位)。
*/
inline void task_status_led()
{
}
/**
* @brief 系统心跳维护任务(当前为预留占位)。
*/
inline void task_heartbeat()
{
}
/**
* @brief 诊断信息打印任务,输出当前所有运行时数据。
*/
inline void task_diagnostic_print()
{
const canfd_diag_t *diag = canfd_get_diag();
printf("temp_raw=%u, vref_raw=%u, vdda=%.3f V, temp=%.2f C, rx_id=0x%08lX, rx_len=%u, can_tx=%lu, can_rx=%lu, can_err=%lu\r\n",
g_app.sample.temp_raw,
g_app.sample.vref_raw,
g_app.sample.vdda,
g_app.sample.temperature,
static_cast<unsigned long>(g_app.last_rx_id),
static_cast<unsigned int>(g_app.last_rx_len),
static_cast<unsigned long>(diag->tx_count),
static_cast<unsigned long>(diag->rx_count),
static_cast<unsigned long>(diag->error_count));
}
} // namespace
/**
* @brief 初始化任务执行器运行时上下文。
* @note 执行一次初始采样,填充运行时数据起点。
*/
void task_runner_init(void)
{
adc_collect_sample(&g_app.sample);
g_app.last_vref_raw = g_app.sample.vref_raw;
g_app.last_temp_raw = g_app.sample.temp_raw;
}
/**
* @brief 执行当前所有已置位的待处理任务。
* @note 若当前没有待执行任务,直接返回。
*/
void task_runner_run_pending(void)
{
const uint32_t pending = task_manager_take_pending();
if(pending == 0U) {
return;
}
if((pending & (1UL << TASK_ADC_VREF_SAMPLE)) != 0U) {
task_adc_vref_sample();
}
if((pending & (1UL << TASK_ADC_TEMP_SAMPLE)) != 0U) {
task_adc_temp_sample();
}
if((pending & (1UL << TASK_ADC_PROCESS)) != 0U) {
task_adc_process();
}
if((pending & (1UL << TASK_CAN_RX_POLL)) != 0U) {
task_can_rx_poll();
}
if((pending & (1UL << TASK_CAN_RX_PARSE)) != 0U) {
task_can_rx_parse();
}
if((pending & (1UL << TASK_CAN_TX_PREPARE)) != 0U) {
task_can_tx_prepare();
}
if((pending & (1UL << TASK_CAN_TX_SEND)) != 0U) {
task_can_tx_send();
}
if((pending & (1UL << TASK_STATUS_LED)) != 0U) {
task_status_led();
}
if((pending & (1UL << TASK_HEARTBEAT)) != 0U) {
task_heartbeat();
}
if((pending & (1UL << TASK_DIAGNOSTIC_PRINT)) != 0U) {
task_diagnostic_print();
}
}
+26 -1
View File
@@ -7,23 +7,48 @@
*/
class TaskRunner {
public:
/**
* @brief 初始化任务执行器运行时上下文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 执行一次初始 ADC 采样,填充运行时数据的起点。
*/
static void init();
/**
* @brief 执行当前所有已置位的待处理任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有待执行任务,直接返回。
*/
static void runPending();
private:
struct Runtime;
static Runtime s_runtime;
/** @brief 采样 VIN/VREF 原始值。 */
static void taskAdcVrefSample();
/** @brief 采样内部温度原始值。 */
static void taskAdcTempSample();
/** @brief 根据采样值计算工程量。 */
static void taskAdcProcess();
/** @brief 兼容保留的 CAN 轮询任务。 */
static void taskCanRxPoll();
/** @brief 解析并消费接收帧。 */
static void taskCanRxParse();
/** @brief 预留的 CAN 发送准备任务;当前不主动生成周期业务帧。 */
static void taskCanTxPrepare();
/** @brief 预留的 CAN 发送执行任务;当前仅保留接口,不参与周期调度。 */
static void taskCanTxSend();
/** @brief 状态灯维护任务(当前预留)。 */
static void taskStatusLed();
/** @brief 心跳维护任务(当前预留)。 */
static void taskHeartbeat();
/** @brief 诊断打印任务。 */
static void taskDiagnosticPrint();
};
#endif /* SYS_TASK_RUNNER_H */
#endif /* SYS_TASK_RUNNER_H */
@@ -1,25 +0,0 @@
/**
* @file task_runner.h
* @brief 任务执行器初始化与待执行任务分发接口定义。
*/
#ifndef SYS_TASK_RUNNER_H
#define SYS_TASK_RUNNER_H
/**
* @brief 初始化任务执行器运行时上下文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void task_runner_init(void);
/**
* @brief 执行当前所有已置位的待处理任务。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有待执行任务,函数直接返回。
*/
void task_runner_run_pending(void);
#endif /* SYS_TASK_RUNNER_H */