Compare commits

..
16 Commits
Author SHA1 Message Date
cbb f3580f38e4 docs(review): 专家评审 2026-10-19 2026-10-20 21:11:29 +08:00
cbb 86ca7666fa feat(motor): 引入运行禁止机制重构电机控制安全系统
- 添加 run_inhibit.h/cpp 模块作为全局运行闸门,统一管理所有停机/唤醒逻辑
- 移除 FOC 控制器中的 5 个相互引用的开关机函数(shut_down/resume 等)
- 将 nFAULT 硬件故障检测从仅上报改为运行门控制,连续采样故障时申请禁止位
- 改进电压检测逻辑,欠压判定现在通过 run_inhibit 统一执行停机动作
- 修正 PB2(nFAULT) 引脚配置为内部上拉输入,避免浮空误读导致系统锁死
- 在 CAN 状态帧中新增 RunInhibit 位图(字节 36-39),实时反映禁止运行的故障源
- 电流采样失效、过热故障现在通过禁止位管理而非直接操作驱动状态
- 校准函数增加运行许可检查,防止在停机状态下意外唤醒驱动
2026-10-13 00:47:52 +08:00
cbb 72cc10b40d chore(gitignore): 忽略 worktree/评审过程稿与 IDE 缓存
变更点:
- 忽略 .worktrees/、.zcode/、SECURITY_AUDIT_REPORT.*、*.html 过程稿
- 本仓代码无业务变更(最近功能改动已在 40fea32 等提交中)
2026-09-20 22:58:17 +08:00
cbb 40fea328b0 feat(can): 反馈报文刷新率逐帧可配置并持久化
STATUS/CONFIG 反馈帧各自独立按 period_ms(1ms 节拍分频,0=停发)发送,
默认 STATUS 1kHz、CONFIG 250Hz,可运行时修改:

- 0x0400 扩展格式(DLC=4):[选择器 u16][值 u16],选择器 0x0000=节点 ID
  (仅单播),0x0100/0x0101=对应帧刷新周期;回 5B ACK,广播只应用不应答
- 0x0401 扩展格式(DLC=2):查询选择器对应值,回 [sel][val](4B);
  DLC<=1 及 SET DLC 1~2 保持旧格式行为完全不变
- 刷新率经 FlashEEPROM 独立 u16 记录(虚拟地址 0x08/0x09)持久化,
  不并入 6 参数 CRC 组,避免老设备升级后整组参数回退默认值
- save_system_config 追加从 RAM 副本回写两条记录,页满 format()
  重建后配置不丢失;read() 增加保留字节校验防 CRC 头部字误匹配
- CANFD_all_Send 拆分为 send_status_frame/send_config_frame,
  初始化失败上报路径直调 STATUS 帧不受分频影响

GATE_DRIVER_DRV8323 0/1 两种板型均编译通过
2026-09-12 19:00:06 +08:00
cbb 0d3bd9cbc6 chore: 忽略 Qoder IDE 生成的 repowiki 缓存目录 2026-09-09 02:54:39 +08:00
cbb 6bdae905d3 chore(rte): 同步 Debug 上下文 RTE_Components.h 生成结果
由 csolution 重新生成,外设组件版本由陈旧的 1.0.0 更正为 1.3.0,与
Release 上下文及构建实际链接的包版本一致(构建目标名 ..._1_3_0)。

该文件头部标注 "CSOLUTION generated file: DO NOT EDIT",此前手工添加的
Doxygen 注释块在重新生成时被覆盖移除,属预期行为。生成器版本戳为 2.12.0,
低于 Release 的 2.13.0,仅记录最后一次生成所用工具版本,不影响构建。
2026-09-09 02:54:20 +08:00
cbb 42dd1adc06 feat(can): CAN-FD 数据段波特率 2Mbps 提升至 5Mbps
数据段改为 prescaler=2、BS1=4TQ、BS2=1TQ:60MHz / (2 × 6TQ) = 5Mbps,
采样点 (1+4)/6 = 83.3%。仲裁段保持 1Mbps(prescaler=6、7TQ+2TQ)不变。

原注释称"数据段 prescaler 必须与非数据段相同",实际 CAN_DBT 的 DBAUDPSC
独立于仲裁段 BAUDPSC,数据段可自取分频,该约束不成立。

TDC 参数维持 filter=4 / offset=4 不变并补注说明:TDCMOD=CALC_AND_OFFSET
下硬件自动测得回路延时 TDCV,TDCO 仅作微调,经验取值约等于数据段 BS1 的
tq 数,不随波特率等比缩放;放大到 12 会使本节点回采错开约 6 个数据位,
自发帧被判为错误帧。
2026-09-09 02:53:34 +08:00
cbb 9fbd9c3d30 refactor(FOC): 移除齿槽转矩补偿功能
对硬编码的两张 128 点补偿表做谐波分解后确认该补偿无效:
- 能量集中在 k=1(11次/转) 16.1% 与 k=2(22次/转) 82.2%,合计 98.3%;
  而 22 极电机的真实齿槽基频应落在槽极 LCM 处(12槽=132次/转 k=12、
  18槽 k=18、24槽 k=24),实测幅值仅 0.46/0.12/0.08 mA,在噪声地板内。
  即表中属于齿槽的成分不足总能量 0.1%,k=1/k=2 实为电角度零位偏差与
  电流采样通道增益/零点失配的特征。
- 表按电角度查表,而电角度含每台独立的 calibration_angle(Flash 读回)。
  相位偏 φ 时残差比为 2sin(kφ/2):零点差 30° 电角度时 k=2 残差即等于
  原幅值,补偿由减小扰动变为增大扰动。22 极下 2.7° 机械偏差即达此量级,
  硬编码全局共用一张表不成立。
- 表采集于 bec7c11(2026-05),其电流链路为改版前方案,此后陆续修正了
  采样时刻落在上管导通窗、PA3/4/5 未配 AIN、零点配平未应用等问题。
  用当时的链路去测量比其自身误差小三个数量级的量,采到的是链路误差。

删除内容:get_cogging_compensation 查表插值、FOC_DEBUG 校准状态机及
其打印函数与静态缓冲、init() 中两张硬编码表、COGGING_TABLE_SIZE/
COGGING_COMP_SCALE、tasks.cpp 调度 tick、RESET 帧 bit4 触发(该位保留)。

六个控制模式的 Uq 输出去掉补偿项;angle_el_cog 仍供 update_el_sincos
使用,改名回 angle_el。mode3 注释中"反转阻力差异由 REV 齿槽补偿表承担"
的描述一并订正(该依赖本已失效)。
2026-09-09 02:53:11 +08:00
cbb 25ed2a2bde feat(adc): 添加三相电流零点配平并扩展初始化错误上报
- ADC: 新增 zero_calibrate() 函数,在上电后采集 200 帧计算各通道零点偏差
- ADC: 修改 current_adc_to_ma() 支持逐通道扣除配平后的零点偏移
- ADC: 调整电流采样引脚为模拟输入模式并优化采样时间至 13.5 周期
- CAN: STATUS 帧字节 32~35 新增 InitStatus 字段,反馈初始化错误位图
- main: 细化初始化错误检测,分别标记角度/ADC/CAN/Kalman/FOC/门驱动故障
- config.h: 新增 INIT_ERR_* 错误码定义及 CURRENT_ZERO_CAL_LIMIT 阈值
2026-09-07 03:34:47 +08:00
cbb 7c4aaa5ee6 feat(FOC): 优化FOC控制算法并修复多个控制环路问题
- 重构cal_Iq_Id函数,移除电角度参数传递,改用预计算的sin/cos值
  以避免重复三角函数计算,提升性能约15-300周期
- 修复角度同步Uq前馈学习中的运动方向判断逻辑,解决反转时的
  震荡问题和正转正常反转震荡的根本原因
- 实现低速缩放因子函数get_mode3_velocity_scale,改进静摩擦
  hunting抑制效果
- 优化电压检测逻辑,将阻塞式ADC读取改为连续模式零等待,
  提升实时性能
- 修复初始化期间电压分母为零的问题,添加下限保护防止P增益
  无穷大
- 实现PID控制器测量微分功能,消除目标阶跃时的微分冲击
- 改进积分抗饱和策略,添加外部饱和泄放功能,优化到位稳定
  性能

perf(Filter): 优化中值滤波算法性能

- 针对ADC_FILTER_SIZE=6的固定场景实现最优排序网络,
  用12次比较替代通用std::nth_element算法,显著提升性能
- 保留通用回退路径处理其他尺寸情况
2026-09-01 02:46:25 +08:00
cbb 76f7987238 feat(FOC): 实现电角度零位自动校准并修复ADC采样干扰问题
- 新增FlashEEPROM用于保存零位校准角,掉电不丢失
- 实现安全的零位校准算法:运行中禁止校准、对齐后重新采样、保存到Flash
- 重构ADC架构:分离ADC0(PWM同步电流采样)和ADC1(VBUS/DRV温度轮询)
- 解决ADC采样冲突:移除ADC0的regular连续扫描,避免与注入组抢占资源
- 修复VBUS电压读取错误:从DMA缓冲区改为直接ADC1单次转换
- 修正开环控制Uq幅值限幅逻辑,保留符号避免反转时抖动
- 更新CAN协议:电流数据改为有符号16位打包,支持负电流传输
- 调整版本号至v26.8.16.7
2026-08-16 02:13:37 +08:00
cbb 42c9f2f669 feat(can): 添加节点ID设置命令的安全防护和改进闪存写入逻辑
- 添加目标节点验证机制,防止SET_NODE_ID命令被错误处理
- 修改节点ID范围验证逻辑,限制为1到0xFF
- 调整闪存写入顺序,在确认写入成功后再发送响应
- 修复SET_NODE_ID消息处理中的边界条件检查

fix(can): 启用自动总线离线恢复功能

- 将CAN参数auto_bus_off_recovery从DISABLE改为ENABLE
- 提高CAN通信的稳定性和自动恢复能力

chore(config): 更新软件版本号并添加编译时间信息

- 将软件补丁版本从24更新到25
- 添加APP_BUILD_DATE和APP_BUILD_TIME宏定义
- 新增getAppBuildTime函数获取编译时间信息
- 添加Flash页大小相关注释说明

perf(debug): 调整ST-Link调试器时钟频率

- 将调试器时钟从10MHz降低到4MHz
- 提高调试连接的稳定性

refactor(static_test): 移除静态测试文件列表

- 删除static_test_automotive_business_20260523/files.csv文件内容
2026-08-13 01:14:07 +08:00
cbb b71fba2016 feat(FOC): 添加角度同步Uq前馈补偿和速度环优化
- 实现update_angle_sync_uq_feedforward函数,支持mode2和mode3的角度同步  Uq前馈补偿,按物理360°查表学习负载/阻力差异
- 实现get_mode3_velocity_p函数,mode3低速时降低速度环P参数减少抖动
- 在velocity_control和angle_control_with_velocity_limit中集成Uq前馈补偿
- 优化normalizeAngle360函数,大角度时先扣整圈数避免while循环过多
- 调整配置参数,降低DEFAULT_vel_loop_p并添加详细的Uq前馈补偿配置
- 更新电源电压参数使用本周期采样值,避免电压滞后影响
2026-05-24 04:56:33 +08:00
cbb e646544f7f refactor(FOC): 重构齿槽转矩校准状态机为独立函数
- 将原本在 calibrate_cogging_tick 中的大函数拆分为多个职责单一的函数
- 新增 start_cogging_calibration、cogging_settling_tick、cogging_sampling_tick、
  cogging_stopping_tick、cogging_done_tick 等状态处理函数
- 添加 process_cogging_run_data、start_next_cogging_run、build_cogging_tables、
  print_cogging_table、print_cogging_c_array 等辅助函数
- 移动全局 FOC 控制器单例注释到更合适位置
- 添加齿槽转矩校准参数常量定义和详细注释
- 添加防御性编程处理默认状态分支,避免状态机卡死
- 提高代码可读性和维护性

feat(tasks): 添加任务头文件到项目配置

- 在 Robot_V2.cproject.yml 中添加 tasks.h 和 tasks.cpp 文件引用

docs(ADC): 添加ADC转换常量说明注释

- 在 ADC.cpp 中添加关于ADC换算常量的详细注释
- 说明全局采样器单例的初始化和使用方式
2026-05-24 01:21:41 +08:00
cbb 4c4e47e9e3 refactor(FOC): 添加Doxygen文档注释并优化SVPWM算法性能
添加了完整的Doxygen文档注释到FOC算法模块的所有函数、类和变量,提升了代码可读性和维护性。同时优化了SVPWM算法实现,将atan2f替换为符号法扇区判断,显著提升计算效率(约15周期vs atan2f 300周期),并改进了arm_sin_cos_f32替代sqrtf提高性能。

feat(FOC): 统一电气角度计算避免重复运算

重构代码以统一电气角度计算,在各个控制循环中只计算一次电气角度并传递给需要的函数,避免了多次重复的fmodf运算和角度归一化操作,提升了实时控制性能。

refactor(Filter): 改进α-β观测器角度处理精度

修改角度卡尔曼滤波器在归一化角度空间[0,360)内工作,避免大累积角度的浮点精度损失问题,确保静止状态下不会产生虚假速度,同时保持与下游代码的兼容性。

refactor: 为算法模块添加完整文档注释

为所有算法模块(FOC、Filter、pid、thermal)添加了详细的Doxygen文档注释,包括函数说明、参数描述、返回值说明等,便于代码理解和API文档生成。
2026-05-23 22:46:32 +08:00
cbb bec7c11b87 feat(FOC): 添加齿槽转矩补偿功能
- 实现齿槽转矩补偿算法,通过查表+线性插值方式计算补偿电压值
- 添加齿槽转矩校准功能,支持正转3次+反转3次的非阻塞校准状态机
- 在所有控制模式中集成齿槽转矩补偿,包括开环、角度、速度、电流和MIT模式
- 硬编码预校准的正反转补偿表数据
- 通过CAN命令支持运行时触发校准功能
- 添加相关配置参数和调试开关
2026-05-19 01:26:42 +08:00
63 changed files with 5225 additions and 1390 deletions
+15
View File
@@ -80,3 +80,18 @@ uv2csolution.log
# legacy intermediate project file
*.cprj
# Qoder IDE generated repowiki cache
.qoder/
# Compose worktrees
.worktrees/
# Multi-agent review process files
.agents/
ORIGINAL_REQUEST.md
.zcode/
# 安全评审过程稿(不入库)
SECURITY_AUDIT_REPORT.*
*.html
+1 -1
View File
@@ -33,7 +33,7 @@ solution:
- project-context: Robot_V2.Release
debugger:
name: ST-Link@pyOCD
clock: 10000000
clock: 4000000
protocol: swd
# List of different build configurations.
+392 -236
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/FOC.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "FOC.h"
#include "Can.h"
#include "Filter.h"
@@ -5,53 +11,81 @@
#include "adc.h"
#include "ang_sensor.h"
#include "config.h"
#include "flash.h"
#include "motor.h"
#include "pid.h"
#include "run_inhibit.h"
#include "systick.h"
#include <arm_math.h>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <gd32c10x.h>
// 全局 FOC 控制器单例:系统只有一个电机控制对象。
// 初始化顺序由 main::hardware_init() 明确调用 FOC.init() 完成;
// 运行期主要由主循环控制任务访问,CAN 命令只更新目标/参数。
FOC_Controller FOC; // FOC控制初始化
extern sampler sampler;
extern angle_sensor KTH7823;
extern CAN_transmiter CAN0_transmiter;
extern EncoderVelocityKalman Vel_kalman; // 速度滤波
extern CurrentKalman Current_kalman;
extern u16 adc_value[3]; // regular DMA缓冲区:MCU温度 / DRV温度 / VBUS
extern FlashEEPROM flashEEP;
// 初始变量及函数定义
#define _constrain(amt, low, high) \
((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
/** @brief constrain 宏定义 */
#define constrain(value, min, max, target_min, target_max) \
((value - min) / (max - min) * (target_max - target_min))
// 校准步骤(代码模板)
/**
* @brief calibrate_zero_position 函数
* @return void
*/
void calibrate_zero_position(void) {
// Set_3Pwm(.25, 0, 0); // 起劲
// delay_1ms(1000);
// KTH7823.update();
// 运行中禁止校准:要求控制模式为停止(0)且基本静止,
// 否则转子无法对齐到电气 0°,标定结果错误,甚至因电角度突变导致失控。
// 被禁止运行时同样拒绝:本函数会直接 motor_resume() 唤醒驱动,
// 不判闸门就成了停机态下的旁路通道。
if (FOC.control_mode != 0 || !run_inhibit.permitted() ||
fabsf(FOC.rt_velocity) > CALIB_MAX_SPEED) {
return;
}
// // 改进校准算法:获取当前传感器角度并计算补偿值
// float current_sensor_angle = KTH7823.getcurrent_sensorangle();
// float electrical_angle = fmodf(current_sensor_angle * PP, 360.0f); //
// 计算当前电角度
// 使能电机驱动器,准备对齐
motor_resume();
// // 计算需要的补偿值,使得当前位置对应0°电角度
// KTH7823.calibration_angle = -electrical_angle;
// 施加固定电压矢量(A 相高电平),让转子对齐到电气 0° 位置
Set_3Pwm(ALIGN_PWM_DUTY, 0.0f, 0.0f);
delay_1ms(ALIGN_SETTLE_MS);
// // 对补偿角进行归一化处理,确保在[-180, 180]范围内
// KTH7823.calibration_angle = fmodf(KTH7823.calibration_angle + 180.0f,
// 360.0f); if (KTH7823.calibration_angle < 0) { KTH7823.calibration_angle
// += 360.0f;
// }
// KTH7823.calibration_angle -= 180.0f;
// 对齐稳定后重新采样传感器角度,再计算零位补偿角,使当前位置对应电气 0°
KTH7823.update();
float current_sensor_angle = KTH7823.getcurrent_sensorangle();
float electrical_angle = fmodf(current_sensor_angle * PP, 360.0f);
KTH7823.set_calibration(-electrical_angle);
// Set_3Pwm(0, 0, 0); // 停止
// 停止施加电压
Set_3Pwm(0.0f, 0.0f, 0.0f);
// 保存校准角到 Flash,掉电不丢
flashEEP.save_calibration_angle(KTH7823.calibration_angle);
}
// 通过Ia,Ib,Ic计算Iq,Id(目前仅输出Iq)
/**
* @brief cal_Iq_Id 函数
* @param[in] current_a 参数
* @param[in] current_b 参数
* @param[in] current_c 参数
* @param[in] angle_el 参数
* @return float 返回值
*/
inline float FOC_Controller::cal_Iq_Id(float current_a, float current_b,
float current_c) {
float current_c) {
if (current_a == ERROR_ADC_VAL || current_b == ERROR_ADC_VAL ||
current_c == ERROR_ADC_VAL) {
return Current_kalman.getCurrent();
@@ -63,27 +97,149 @@ inline float FOC_Controller::cal_Iq_Id(float current_a, float current_b,
current_b -= current_avg;
current_c -= current_avg;
// 计算电气角度
float angle_el = fmod(shaft_angle * PP + KTH7823.calibration_angle, 360.0f);
// Clarke变换 (使用三相电流)
float I_alpha, I_beta;
I_alpha = (2.0f / 3.0f) * current_a - (1.0f / 3.0f) * current_b -
(1.0f / 3.0f) * current_c;
I_beta = _1_SQRT3 * current_b - _1_SQRT3 * current_c;
// Park变换
float sin_el, cos_el;
arm_sin_cos_f32(angle_el, &sin_el, &cos_el);
// Park变换:sin/cos 由 update_el_sincos() 每周期算一次,
// 与 setSVPWM_PhaseVoltage 的逆 Park 共用,省一次三角函数
float I_q, I_d;
I_q = I_beta * cos_el - I_alpha * sin_el;
I_d = I_beta * sin_el + I_alpha * cos_el;
I_q = I_beta * el_cos - I_alpha * el_sin;
I_d = I_beta * el_sin + I_alpha * el_cos;
// 返回Iq值,乘以1000以转换为mA
return Current_kalman.update(I_q * 1000 - KE_FACTOR * rt_velocity);
// return I_q * 1000 - KE_FACTOR * rt_velocity;
}
/**
* @brief 角度同步 Uq 前馈更新/输出。
* @note 只在稳定同向巡航时输出和学习;停止、低速、反向、明显刹车/超速时立即退出。
*/
float FOC_Controller::update_angle_sync_uq_feedforward(
u8 mode, float table[], u16 table_size, float mech_angle,
float target_vel, float actual_vel, float velocity_error) {
if (table_size == 0U) return 0.0f;
bool enable = false;
bool learn_enable = false;
float min_target = 0.0f;
float stop_speed = 0.0f;
float brake_err = 0.0f;
float learn_err_max = 0.0f;
float learn_alpha = 0.0f;
float gain = 0.0f;
float max_ff = 0.0f;
float dir_sign = 1.0f;
switch (mode) {
case 2:
enable = MODE2_UQ_FF_ENABLE;
learn_enable = MODE2_UQ_FF_LEARN_ENABLE;
min_target = MODE2_UQ_FF_MIN_TARGET;
stop_speed = MODE2_UQ_FF_STOP_SPEED;
brake_err = MODE2_UQ_FF_BRAKE_ERR;
learn_err_max = MODE2_UQ_FF_LEARN_ERR_MAX;
learn_alpha = MODE2_UQ_FF_LEARN_ALPHA;
gain = MODE2_UQ_FF_GAIN;
max_ff = MODE2_UQ_FF_MAX;
dir_sign = 1.0f;
break;
case 3:
enable = MODE3_UQ_FF_ENABLE;
learn_enable = MODE3_UQ_FF_LEARN_ENABLE;
min_target = MODE3_UQ_FF_MIN_TARGET;
stop_speed = MODE3_UQ_FF_STOP_SPEED;
brake_err = MODE3_UQ_FF_BRAKE_ERR;
learn_err_max = MODE3_UQ_FF_LEARN_ERR_MAX;
learn_alpha = MODE3_UQ_FF_LEARN_ALPHA;
gain = MODE3_UQ_FF_GAIN;
max_ff = MODE3_UQ_FF_MAX;
dir_sign = DIR;
break;
default:
return 0.0f;
}
// 超速/制动判定必须沿"运动方向":velocity_error×dir_sign 是 PID 系误差,
// 再乘运动方向符号后,< -brake_err 才表示"沿运动方向明显超速"。
// 原实现只乘 dir_sign:反转(负速度)时判定极性恰好相反——反转超速时
// 前馈继续输出、反转欠速时前馈反而被切断,已学到的电压(可达±MAX)
// 随误差符号通断,形成正转没有、反转独有的 bang-bang 震荡。
const float motion_sign = (target_vel * dir_sign >= 0.0f) ? 1.0f : -1.0f;
const bool active = (fabsf(target_vel) > stop_speed) &&
(fabsf(actual_vel) > stop_speed) &&
((target_vel * actual_vel) > 0.0f) &&
((velocity_error * dir_sign * motion_sign) > -brake_err);
float angle = normalizeAngle360(mech_angle);
float index_f = angle / 360.0f * (float)table_size;
u16 idx0 = (u16)index_f;
if (idx0 >= table_size) idx0 = table_size - 1U;
u16 idx1 = (idx0 + 1U) % table_size;
float frac = index_f - (float)idx0;
float ff = table[idx0] * (1.0f - frac) + table[idx1] * frac;
if (ff > max_ff) ff = max_ff;
else if (ff < -max_ff) ff = -max_ff;
// 学习仅在误差带内进行:启停/换向瞬态误差可达上百 °/s,直接学习会把
// ±MAX 钳位值写进途经的角度桶,下一轮运动经过该角度时突加满幅前馈
// 电压,本身就是一次转矩扰动。
if (learn_enable && active && fabsf(target_vel) > min_target &&
fabsf(velocity_error * dir_sign) < learn_err_max) {
float ff_target = velocity_error * dir_sign * gain;
if (ff_target > max_ff) ff_target = max_ff;
else if (ff_target < -max_ff) ff_target = -max_ff;
table[idx0] += learn_alpha * (ff_target - table[idx0]);
}
return (enable && active && fabsf(target_vel) > min_target) ? ff : 0.0f;
}
/**
* @brief mode3 低速缩放因子:静止附近降到 min_scale,速度升高后线性恢复到 1。
* @note P/I 使用各自的地板(I 地板更低):低速段保持"比例主导",
* 积分充电足够慢,压制静摩擦 hunting(停下瞬间小幅来回抖动)。
* D 不衰减,保持阻尼。
*/
float FOC_Controller::get_mode3_velocity_scale(float target_vel,
float actual_vel,
float min_scale) {
if (!MODE3_LOW_SPEED_P_SCALE_ENABLE) {
return 1.0f;
}
float abs_target = fabsf(target_vel);
float abs_speed = fabsf(actual_vel);
float speed_ref = (abs_target > abs_speed) ? abs_target : abs_speed;
if (speed_ref <= MODE3_LOW_SPEED_P_LOW) {
return min_scale;
}
if (speed_ref < MODE3_LOW_SPEED_P_FULL) {
return min_scale +
(1.0f - min_scale) * (speed_ref - MODE3_LOW_SPEED_P_LOW) /
(MODE3_LOW_SPEED_P_FULL - MODE3_LOW_SPEED_P_LOW);
}
return 1.0f;
}
/**
* @brief mode3 速度内环 P:低速缩放因子(方向无关)。
* @note 历史上曾有"反转 P 提升到 ×2"的增益调度(先是 −120/−30 硬台阶,
* 后改为连续线性过渡):连续化后反转仍剧烈震荡,说明问题不在
* 台阶突跳,而在增益本身——×2 只对反转生效,把反转环路增益整体
* 翻倍而正转不变,正是"正转正常、反转震荡"的直接来源之一。
* 已移除。反转阻力差异由 Uq 前馈学习承担;
* 若实测反转欠速/偏慢,优先增大前馈学习限幅,不要恢复非对称 P。
*/
float FOC_Controller::get_mode3_velocity_p(float target_vel, float actual_vel) {
return vel_angloop_p * get_mode3_velocity_scale(target_vel, actual_vel,
MODE3_LOW_SPEED_P_MIN_SCALE);
}
/**
* @brief 电机控制类初始化函数
*
@@ -92,14 +248,23 @@ inline float FOC_Controller::cal_Iq_Id(float current_a, float current_b,
u8 FOC_Controller::init() {
motor_init(); // 电机驱动初始化
FOC.cur_loop_p = DEFAULT_current_loop_p * 100 / limit_voltage;
FOC.cur_MIT_p = DEFAULT_current_MIT_p * 100 / limit_voltage;
FOC.vel_loop_p = DEFAULT_vel_loop_p * 100 / limit_voltage;
// limit_voltage 从 Flash/CAN 恢复,可能为 0:分母下限保护,防止 P=inf
const float denom = (limit_voltage > MIN_LIMIT_VOLTAGE)
? limit_voltage
: voltage_power_supply;
FOC.cur_loop_p = DEFAULT_current_loop_p * 100 / denom;
FOC.cur_MIT_p = DEFAULT_current_MIT_p * 100 / denom;
FOC.vel_loop_p = DEFAULT_vel_loop_p * 100 / denom;
FOC.vel_angloop_p = DEFAULT_vel_angloop_p * 100;
FOC.vel_MIT_p = DEFAULT_vel_MIT_p * 100;
FOC.ang_loop_p = DEFAULT_angle_loop_p * 100 / limit_voltage;
FOC.ang_angloop_p = DEFAULT_angle_angloop_p * 100 / limit_voltage;
FOC.ang_MIT_p = DEFAULT_angle_MIT_p * 100 / limit_voltage;
FOC.ang_loop_p = DEFAULT_angle_loop_p * 100 / denom;
FOC.ang_angloop_p = DEFAULT_angle_angloop_p * 100 / denom;
FOC.ang_MIT_p = DEFAULT_angle_MIT_p * 100 / denom;
// 运行期不每周期 set_P 的环路(mode1/mode5)在此同步一次有效增益,
// 避免沿用构造期的临时值
angle_loop.set_P(ang_loop_p);
current_loop.set_P(cur_loop_p);
return 0;
}
@@ -109,109 +274,83 @@ u8 FOC_Controller::init() {
*/
FOC_Controller::FOC_Controller() { ; }
// /**
// * @brief 设置电机输出
// *
// * @param Uq: q轴电压
// * @param Ud: d轴电压
// * @param angle_el: 电机轴角度
// */
// inline void FOC_Controller::setPhaseVoltage(float Uq, float Ud) {
// // 计算电气角度
// float angle_el = fmod(shaft_angle * PP, 360.0f);
// // 帕克逆变换 (直接计算,避免函数调用开销)
// float sin_el, cos_el;
// arm_sin_cos_f32(angle_el, &sin_el, &cos_el);
// float Ualpha = Ud * cos_el - Uq * sin_el;
// float Ubeta = Ud * sin_el + Uq * cos_el;
// // 克拉克逆变换 (使用预计算常量优化)
// Ua = Ualpha + v_half;
// Ub = SQRT3DV2 * Ubeta - 0.5f * Ualpha + v_half;
// Uc = -0.5f * Ualpha - SQRT3DV2 * Ubeta + v_half;
// if (Ua > v_safe)
// Ua = v_safe;
// else if (Ua < -v_safe)
// Ua = -v_safe;
// if (Ub > v_safe)
// Ub = v_safe;
// else if (Ub < -v_safe)
// Ub = -v_safe;
// if (Uc > v_safe)
// Uc = v_safe;
// else if (Uc < -v_safe)
// Uc = -v_safe;
// // 设置PWM输出
// float voltage_exchange = limit_voltage / v_safe;
// duty_a = Ua / voltage_power_supply * voltage_exchange;
// duty_b = Ub / voltage_power_supply * voltage_exchange;
// duty_c = Uc / voltage_power_supply * voltage_exchange;
// Set_3Pwm(duty_a, duty_b, duty_c);
// }
/**
* @brief 每控制周期调用一次:计算当前电角度的 sin/cos。
* @note cal_Iq_Id 的 Park 变换与 setSVPWM_PhaseVoltage 的逆 Park 变换
* 使用同一电角度,三角函数每周期只算这一次,两处共用。
*/
inline void FOC_Controller::update_el_sincos(float angle_el) {
arm_sin_cos_f32(angle_el, &el_sin, &el_cos);
}
/**
* @brief 使用SVPWM算法设置电机输出
*
* @param Uq: q轴电压
* @param Ud: d轴电压
*
* 优化:atan2f → X/Y/Z 符号法判扇区(~15周期 vs atan2f ~300周期)
* 边界上扇区编号可能与 atan2f 差1,但 duty 输出完全一致(已数值验证)
* 逆 Park 所需 sin/cos 由 update_el_sincos() 预计算,不再重复计算
*/
inline void FOC_Controller::setSVPWM_PhaseVoltage(float Uq, float Ud) {
// 计算电气角度
float angle_el = shaft_angle * PP + KTH7823.calibration_angle;
// ① 逆Park变换(sin/cos 为本周期预计算值)
float Ualpha = Ud * el_cos - Uq * el_sin;
float Ubeta = Ud * el_sin + Uq * el_cos;
// 对电角度进行归一化,确保在[0, 360)范围内
angle_el = fmodf(angle_el, 360.0f);
// ② 扇区判定(替代 atan2f)
float X = Ubeta;
float Y = SQRT3DV2 * Ualpha - 0.5f * Ubeta;
float Z = -SQRT3DV2 * Ualpha - 0.5f * Ubeta;
// 帕克逆变换
float sin_el, cos_el;
arm_sin_cos_f32(angle_el, &sin_el, &cos_el);
float Ualpha = Ud * cos_el - Uq * sin_el;
float Ubeta = Ud * sin_el + Uq * cos_el;
u8 sector_code = (X > 0.0f ? 1u : 0u)
+ (Y > 0.0f ? 2u : 0u)
+ (Z > 0.0f ? 4u : 0u);
// 计算角度并归一化到 [0, 2π)
float angle = atan2f(Ubeta, Ualpha);
if (angle < 0.0f)
angle += 2.0f * PI;
// 编码 → 扇区 查找表(由 atan2f 扇区定义反推,全角度数值验证通过)
static const u8 sector_lut[8] = {0, 2, 6, 1, 4, 3, 5, 0};
u8 sector = sector_lut[sector_code];
// 扇区与局部角度
int sector = (int)(angle * INV_PI_3) + 1;
if (sector < 1)
sector = 1;
else if (sector > 6)
sector = 6;
float local_angle = angle - (sector - 1) * PI_3;
if (sector == 0u) {
Set_3Pwm(0.5f, 0.5f, 0.5f);
return;
}
// 模长计算
float magnitude = sqrtf(Ualpha * Ualpha + Ubeta * Ubeta);
// 计算电压调制比,同时限制最大输出电压
float modulation_index =
(magnitude / voltage_power_supply); // 90%作为安全裕量
// ③ 模长与调制比(arm_sqrt_f32 替代 sqrtf,~14周期 vs ~100周期)
float magnitude;
arm_sqrt_f32(Ualpha * Ualpha + Ubeta * Ubeta, &magnitude);
float inv_Udc = 1.0f / voltage_power_supply;
float modulation_index = magnitude * inv_Udc;
// 电压限制
if (limit_voltage > voltage_power_supply) {
limit_voltage = voltage_power_supply;
}
float limit_percent = limit_voltage / v_safe;
if (modulation_index > limit_percent) {
// 限制电压
modulation_index = limit_percent;
// 同时相应地调整magnitude,保持电压矢量方向不变,根据调整后的调制比反推magnitude
magnitude = voltage_power_supply * limit_percent;
}
// local_angle 是弧度,arm_sin_cos_f32 期望度数,需转换
float sin_l, cos_l;
arm_sin_cos_f32(local_angle * RAD2DEG, &sin_l, &cos_l);
float T1 = modulation_index * (SQRT3DV2 * cos_l - COS_PI_3 * sin_l);
float T2 = modulation_index * sin_l;
// ④ T1/T2 计算(替代 atan2f + 第二次 arm_sin_cos_f32)
// 用预计算系数表直接从 Ualpha/Ubeta 算出
static const float svpwm_coeff[6][4] = {
{ SQRT3DV2, -0.5f, 0.0f, 1.0f}, // sector 1
{ SQRT3DV2, 0.5f, -SQRT3DV2, 0.5f}, // sector 2
{ 0.0f, 1.0f, -SQRT3DV2, -0.5f}, // sector 3
{ -SQRT3DV2, 0.5f, 0.0f, -1.0f}, // sector 4
{ -SQRT3DV2, -0.5f, SQRT3DV2, -0.5f}, // sector 5
{ 0.0f, -1.0f, SQRT3DV2, 0.5f}, // sector 6
};
float T1 = (svpwm_coeff[sector-1][0] * Ualpha + svpwm_coeff[sector-1][1] * Ubeta) * inv_Udc;
float T2 = (svpwm_coeff[sector-1][2] * Ualpha + svpwm_coeff[sector-1][3] * Ubeta) * inv_Udc;
// 过调制限制:等比缩放 T1/T2
if (modulation_index > limit_percent) {
float scale = limit_percent / modulation_index;
T1 *= scale;
T2 *= scale;
}
float T0 = 1.0f - T1 - T2;
// 计算占空比(按规范化时间映射)
// ⑤ 占空比计算(不变)
switch (sector) {
case 1:
duty_a = 0.5f * T0 + T1 + T2;
@@ -249,47 +388,9 @@ inline void FOC_Controller::setSVPWM_PhaseVoltage(float Uq, float Ud) {
duty_c = 0.5f;
break;
}
// 设置PWM输出
Set_3Pwm(duty_a, duty_b, duty_c);
}
/**
* @brief 电机关机(通用关机,不带原因标记)
*
*/
void FOC_Controller::shut_down() {
drv_stop();
motor_sleep();
is_shut_down = true;
}
/**
* @brief 过热关机
*
*/
void FOC_Controller::shut_down_thermal() {
drv_stop();
motor_sleep();
is_shut_down = true;
thermal_shutdown = true;
}
/**
* @brief 过热恢复
*
*/
void FOC_Controller::resume_from_thermal() {
thermal_shutdown = false;
if (!voltage_error && !over_current) {
is_shut_down = false;
motor_resume();
}
}
/**
* @brief 占空比置0
*
*/
void FOC_Controller::drv_stop() { Set_3Pwm(0.0f, 0.0f, 0.0f); }
/**
* @brief 电机锁止
*/
@@ -315,28 +416,17 @@ float FOC_Controller::PWM_out() {
}
/**
* @brief 电机恢复
*
*/
void FOC_Controller::resume() {
over_current = false;
voltage_error = false;
is_shut_down = false;
motor_resume();
}
/**
* @brief 更新电源电压
*
* @note 读数不在合理区间(含采样链给的 ERROR_ADC_VAL 哨兵值)时保留上次值,
* 不更新。这里不再置"电压故障"标志:欠压判定与停机都在
* fast_voltage_check() → run_inhibit 那条路上做。
*/
void FOC_Controller::update_power(float voltage) {
if (voltage < 36.0f and voltage > 8.0f) {
voltage_error = false;
voltage_power_supply = voltage;
v_half = voltage * 0.5f;
v_safe = voltage * 1.1f;
} else {
voltage_error = true;
}
}
@@ -360,9 +450,16 @@ void FOC_Controller::update_power(float voltage) {
* - VBUS_ABSOLUTE_MIN: 绝对最低电压 (默认8V)
* - VBUS_DEBOUNCE_COUNT: 防抖计数 (默认5次)
*/
/**
* @brief fast_voltage_check 函数
* @return void
*/
void FOC_Controller::fast_voltage_check(void) {
// 直接从DMA缓冲区读取最新ADC值(regular rank2 是 VBUS)
float current_vbus = adc_value[2] * (3.3f / 4096.0f) * 11.0f;
// ADC1 连续模式零等待读最新 VBUS(ch6,11:1 分压)。
// 旧实现 adc1_read_raw 阻塞式"触发+等EOC":10kHz 调用每次 ~1.5μs 死等。
// 通道顺序问题的历史注释已随 ADC1 连续模式改造失效(本函数只读 DR)。
float current_vbus =
adc1_read_vbus_latest() * (3.3f / 4096.0f) * 11.0f;
// 更新基准电压(峰值检测)
// 电压上升时,基准电压跟随;电压下跌时,基准电压保持
@@ -383,15 +480,15 @@ void FOC_Controller::fast_voltage_check(void) {
bool drop_exceeded = (voltage_drop > VBUS_DROP_THRESHOLD);
bool below_absolute = (current_vbus < VBUS_ABSOLUTE_MIN);
if ((drop_exceeded || below_absolute) && (!is_shut_down)) {
// 只看本来源是否已经申请过:不查别的故障源,避免欠压判定被过热/nFAULT 挡住
if ((drop_exceeded || below_absolute) &&
(run_inhibit.sources() & INHIBIT_UNDERVOLTAGE) == 0U) {
// 电压跌落计数(防抖)
vbus_drop_count++;
if (vbus_drop_count >= VBUS_DEBOUNCE_COUNT) {
// 确认需要关机,立即停机
drv_stop();
motor_sleep();
is_shut_down = true;
// 确认需要关机:只申请禁止位,停机动作由 run_inhibit 在控制任务里执行
run_inhibit.request(INHIBIT_UNDERVOLTAGE);
emergency_shutdown = true; // 标记紧急关机,低频任务会执行保存
vbus_drop_count = 0; // 重置计数器
}
@@ -410,14 +507,29 @@ void FOC_Controller::fast_voltage_check(void) {
float FOC_Controller::velocity_open_loop() {
// 通过时间间隔和目标速度计算需要转动的机械角度,存储在 shaft_angle 中
shaft_angle =
fmod(shaft_angle + target_velocity * REDUCTION_RATIO / MOTOR_FREQUENCY,
360.0f);
normalizeAngle360(shaft_angle + target_velocity * REDUCTION_RATIO / MOTOR_FREQUENCY);
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
// 因为在开环控制中,Ud 始终为 0,只控制 Uq
const float Uq = _constrain(target_velocity * REDUCTION_RATIO * 0.005f,
2.0f, voltage_power_supply * 0.5f);
// 幅度限幅必须保留符号:原 _constrain 下限为 +2V,负速度会被钳到 +2V,
// 磁场角度反向而 Uq 仍为正,导致反转时电机原地抖动。
float Uq = target_velocity * REDUCTION_RATIO * 0.005f;
const float UQ_MIN_MAG = 2.0f;
const float UQ_MAX_MAG = voltage_power_supply * 0.5f;
// 零速不输出,避免 2V 最小幅值导致静止时缓慢爬行
if (Uq != 0.0f) {
float Uq_mag = fabsf(Uq);
if (Uq_mag < UQ_MIN_MAG) {
Uq_mag = UQ_MIN_MAG;
} else if (Uq_mag > UQ_MAX_MAG) {
Uq_mag = UQ_MAX_MAG;
}
Uq = (Uq < 0.0f) ? -Uq_mag : Uq_mag;
}
setSVPWM_PhaseVoltage(Uq, 0.0f);
return limit_voltage;
}
@@ -429,11 +541,15 @@ float FOC_Controller::velocity_open_loop() {
* @return float: 当前角度
*/
float FOC_Controller::angle_control() {
// PID 控制计算
float Uq = angle_loop(target_angle - rt_angle);
// PID 控制计算(D 项对测量微分+低通,目标阶跃无微分踢)
angle_loop.set_P(ang_loop_p);
float Uq = angle_loop(target_angle - rt_angle, rt_angle);
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
// 设置相电压
@@ -448,23 +564,50 @@ float FOC_Controller::angle_control() {
* @return float: 当前速度
*/
float FOC_Controller::velocity_control() {
// 速度误差计算
// 获取电机轴角度:mode2 Uq 前馈补偿需要用物理 360° 角度查表。
shaft_angle = KTH7823.getcurrent_sensorangle();
// mode2 速度环:真实速度波动保留在反馈里,PID 继续看真实 rt_velocity。
float velocity_error = (target_velocity - rt_velocity) * DIR * REDUCTION_RATIO;
// 目标速度突变检测:target 大幅变化时清积分
static float last_target_vel = 0.0f;
const float target_delta = target_velocity - last_target_vel;
if (fabsf(target_delta) > 5.0f) {
// mode2 使用独立速度环参数。
vel_loop.set_PID(vel_loop_p, vel_loop_Ki, vel_loop_Kd);
vel_loop.set_limit(limit_voltage);
// 仅"实质性换向"时清积分:目标速度变号且新幅值够大(真实反向指令)才清。
// 最终贴合阶段误差过零引起的微小变号(|target|=150×0.01°≈1.5°/s)不清——
// 否则积分被反复清空,低速段缩小的 P 项(0.12V@0.01°)推不过静摩擦,
// 永远停在差 ~0.01° 的位置
static float last_mode2_target_vel = 0.0f;
if (last_mode2_target_vel * target_velocity < 0.0f &&
fabsf(target_velocity) > 5.0f) {
vel_loop.reset_integral();
}
last_target_vel = target_velocity;
last_mode2_target_vel = target_velocity;
// PID 控制计算
float Uq = vel_loop(velocity_error);
float Uq_pid =
vel_loop(velocity_error, rt_velocity * DIR * REDUCTION_RATIO);
static float uq_ff_table[MODE2_UQ_FF_TABLE_SIZE] = {0.0f};
float Uq_ff = update_angle_sync_uq_feedforward(
2, uq_ff_table, MODE2_UQ_FF_TABLE_SIZE, shaft_angle,
target_velocity, rt_velocity, velocity_error);
// PID 输出已内部钳位;前馈叠加后超限时泄放积分,
// 让 PID 感知真实饱和(否则重载下积分继续累积→释载超调振荡)
float Uq = Uq_pid + Uq_ff;
if (Uq > limit_voltage) {
vel_loop.bleed_integral(Uq - limit_voltage);
Uq = limit_voltage;
} else if (Uq < -limit_voltage) {
vel_loop.bleed_integral(Uq + limit_voltage);
Uq = -limit_voltage;
}
CAN0_transmiter.testfloat_union.f = Uq;
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// 设置相电压
setSVPWM_PhaseVoltage(Uq, 0.0f);
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
@@ -473,20 +616,28 @@ float FOC_Controller::velocity_control() {
return rt_velocity;
}
/**
* @brief current_control 函数
* @return float 返回值
*/
float FOC_Controller::current_control() {
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
CAN0_transmiter.testfloat_union.f = shaft_angle;
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// PID 控制计算
float Iq = rt_current =
cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
float Uq = current_loop(target_current - Iq);
current_loop.set_P(cur_loop_p);
float Uq = current_loop(target_current - Iq, Iq);
if (Uq > limit_voltage) {
Uq = limit_voltage;
} else if (Uq < -limit_voltage) {
Uq = -limit_voltage;
}
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
CAN0_transmiter.testfloat_union.f = shaft_angle;
// 设置相电压
setSVPWM_PhaseVoltage(Uq, 0.0f);
return 1;
@@ -504,45 +655,50 @@ float FOC_Controller::angle_control_with_velocity_limit() {
float angle_error = target_angle - rt_angle;
ang_inVelAngloop.set_P(ang_angloop_p);
// 使用角度PID控制器计算目标速度
float target_velocity = ang_inVelAngloop(angle_error);
// 使用角度PID控制器计算目标速度(D 项对测量微分+低通)
float target_velocity = ang_inVelAngloop(angle_error, rt_angle);
// 使用速度PID控制器计算最终输出电压
// 根据目标速度大小,选择不同的PID参数
if (target_velocity < -120.0f) {
vel_inVelAngloop.set_P(vel_angloop_p * 2.0f);
} else if (target_velocity < -30.0f) {
vel_inVelAngloop.set_P(vel_angloop_p * 1.5f);
} else {
vel_inVelAngloop.set_P(vel_angloop_p);
}
// 低速段 P/I 各自按地板衰减(D 保持满值):I 地板更低(0.15),
// 停止瞬间积分充电速率仅为全速的 15%,进一步压制静摩擦 hunting
float i_scale = get_mode3_velocity_scale(target_velocity, rt_velocity,
MODE3_LOW_SPEED_I_MIN_SCALE);
vel_inVelAngloop.set_PID(get_mode3_velocity_p(target_velocity, rt_velocity),
vel_angloop_Ki * i_scale, vel_angloop_Kd);
float velocity_error = target_velocity - rt_velocity;
// 目标速度突变检测:target 大幅变化时清积分
// 仅"实质性换向"时清积分(变号且新幅值>5°/s)。制动段不清(防欠程);
// 最终贴合阶段误差过零的微小变号也不清(防积分反复被清、停在差0.01°)
static float last_vang_target_vel = 0.0f;
if (fabsf(target_velocity - last_vang_target_vel) > 5.0f) {
if (last_vang_target_vel * target_velocity < 0.0f &&
fabsf(target_velocity) > 5.0f) {
vel_inVelAngloop.reset_integral();
}
last_vang_target_vel = target_velocity;
static float last_velocity_error = 0.0f;
last_velocity_error = velocity_error;
float Uq_pid = vel_inVelAngloop(velocity_error, rt_velocity) * DIR;
float Uq = vel_inVelAngloop(velocity_error)* DIR;
CAN0_transmiter.testfloat_union.f = Uq;
// // 实现力度控制:如果电流超过目标电流,则限制输出
// if (abs(rt_current) > abs(target_current)) {
// // 当实际电流超过目标电流时,减小输出以限制电流
// // 可以采用比例限制或直接限制输出电压的方式
// float current_ratio = abs(target_current) / abs(rt_current);
// if (current_ratio < 1.0f) {
// Uq *= current_ratio; // 按比例缩小输出电压
// }
// }
// 获取电机轴角度
// 获取电机轴角度:mode3 Uq 前馈补偿按物理 360° 查表。
shaft_angle = KTH7823.getcurrent_sensorangle();
static float mode3_uq_ff_table[MODE3_UQ_FF_TABLE_SIZE] = {0.0f};
float Uq_ff = update_angle_sync_uq_feedforward(
3, mode3_uq_ff_table, MODE3_UQ_FF_TABLE_SIZE, shaft_angle,
target_velocity, rt_velocity, velocity_error);
// 前馈叠加后超限时泄放积分(×DIR 补偿:Uq = PID输出×DIR + 前馈)
float Uq = Uq_pid + Uq_ff;
if (Uq > limit_voltage) {
vel_inVelAngloop.bleed_integral(DIR * (Uq - limit_voltage));
Uq = limit_voltage;
} else if (Uq < -limit_voltage) {
vel_inVelAngloop.bleed_integral(DIR * (Uq + limit_voltage));
Uq = -limit_voltage;
}
CAN0_transmiter.testfloat_union.f = shaft_angle;
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// 获取当前实际电流值
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
@@ -561,38 +717,38 @@ float FOC_Controller::angle_control_with_velocity_limit() {
*/
float FOC_Controller::MIT_Mode() {
// 第一层:角度环(外环)
// 根据目标角度和实际角度的差值,通过角度环PID控制器计算目标速度
float angle_error = target_angle - rt_angle;
ang_inMITloop.set_P(ang_MIT_p);
float target_velocity =
ang_inMITloop(angle_error); // 使用angle_loop作为角度到速度的映射
ang_inMITloop(angle_error, rt_angle);
// 第二层:速度环(中环)
// 根据目标速度和实际速度的差值,通过速度环PID控制器计算目标电流
float velocity_error = (target_velocity - rt_velocity)* DIR;
// 目标速度变化检测:target 大幅变化时清积分
// 仅"实质性换向"时清积分(变号且新幅值>5°/s,理由同 mode3)
static float last_mit_target_vel = 0.0f;
if (fabsf(target_velocity - last_mit_target_vel) > 5.0f) {
if (last_mit_target_vel * target_velocity < 0.0f &&
fabsf(target_velocity) > 5.0f) {
vel_inMITloop.reset_integral();
}
last_mit_target_vel = target_velocity;
vel_inMITloop.set_P(vel_MIT_p);
float target_current = vel_inMITloop(velocity_error);
// 第三层:电流环(内环)
// 计算当前实际电流
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
// 通过电流环PID控制器计算输出电压
float current_error = target_current - rt_current;
current_inMITloop.set_P(cur_MIT_p);
float Uq = current_inMITloop(current_error);
float target_current = vel_inMITloop(velocity_error, rt_velocity * DIR);
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// 第三层:电流环(内环)
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
float current_error = target_current - rt_current;
current_inMITloop.set_P(cur_MIT_p);
float Uq = current_inMITloop(current_error, rt_current);
// 设置相电压
setSVPWM_PhaseVoltage(Uq, 0.0f);
+114 -26
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/FOC.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
// 函数声明
#ifndef _FOC_H
#define _FOC_H
@@ -5,12 +11,19 @@
#include "pid.h"
#include <cstdint>
/**
* @brief FOC_Controller 类或结构体
*/
class FOC_Controller {
public:
FOC_Controller();
// 初始化函数
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init();
u8 control_mode = 0;
@@ -26,16 +39,13 @@ class FOC_Controller {
float rt_velocity = 0.0f;
float rt_current = 0.0f;
bool over_current = false;
bool voltage_error = false;
bool is_shut_down = false;
bool thermal_shutdown = false; // 过热关机标志
// 关机事件标记:通知低频任务把当前状态落盘(不是"能不能运行"的判据,
// 运行许可一律看 sys/run_inhibit.h)
bool emergency_shutdown = false; // 紧急电压跌落关机标志
float voltage_power_supply = 24.0f; // 电源电压
float v_safe = voltage_power_supply / 1.05f;
float duty_a = 0, duty_b = 0, duty_c = 0;
float Ua = 0, Ub = 0, Uc = 0;
// 快速电压检测相关成员(基于跌幅检测)
float vbus_reference = 0.0f; // 基准电压(峰值检测,跟踪当前额定电压)
@@ -53,34 +63,79 @@ class FOC_Controller {
float vel_MIT_p = DEFAULT_vel_MIT_p;
float cur_MIT_p = DEFAULT_current_MIT_p;
/**
* @brief velocity_open_loop 函数
* @return float 返回值
*/
float velocity_open_loop(void);
/**
* @brief angle_control 函数
* @return float 返回值
*/
float angle_control(void);
/**
* @brief angle_control_with_velocity_limit 函数
* @return float 返回值
*/
float angle_control_with_velocity_limit(void);
/**
* @brief MIT_Mode 函数
* @return float 返回值
*/
float MIT_Mode(void);
/**
* @brief velocity_control 函数
* @return float 返回值
*/
float velocity_control(void);
/**
* @brief current_control 函数
* @return float 返回值
*/
float current_control(void);
/**
* @brief 更新 power 相关状态
* @param[in] voltage 参数
* @return void
*/
void update_power(float voltage);
/**
* @brief DFOC_alignSensor 函数
* @param[in] _PP 参数
* @return void
*/
void DFOC_alignSensor(int _PP);
void shut_down(void);
void resume(void);
void drv_stop(void);
/**
* @brief drv_hold 函数
* @return float 返回值
*/
float drv_hold(void);
/**
* @brief PWM_out 函数
* @return float 返回值
*/
float PWM_out(void);
void shut_down_thermal(void); // 过热关机
void resume_from_thermal(void); // 过热恢复
/**
* @brief fast_voltage_check 函数
* @return void
*/
void fast_voltage_check(void); // 快速电压检测(100us周期调用)
// 此处默认值不作数
// 注意:构造期 limit_voltage 尚为 0,分母一律用 voltage_power_supply(24V),
// 否则 P 初始化为 +inf;运行期各模式每周期 set_P/set_PID 覆盖。
// 内环 output_ramp 保持原值:实测收紧到 2000 V/s 会在制动段引入相位滞后,
// 导致到位抖动,已回退。
PIDController vel_loop =
PIDController{DEFAULT_vel_loop_p * 100 / limit_voltage, vel_loop_Ki,
vel_loop_Kd, 10000, voltage_power_supply}; // 12V
PIDController{DEFAULT_vel_loop_p * 100 / voltage_power_supply, vel_loop_Ki,
vel_loop_Kd, 10000, voltage_power_supply};
PIDController angle_loop =
PIDController{DEFAULT_angle_loop_p * 100 / limit_voltage, 100.0f, 0.2f,
10000000, voltage_power_supply}; // 12V
PIDController{DEFAULT_angle_loop_p * 100 / voltage_power_supply, 100.0f,
0.02f, 10000000, voltage_power_supply};
PIDController current_loop = PIDController{
DEFAULT_current_loop_p * 100 / limit_voltage, current_loop_Ki,
current_loop_Kd, 1500, voltage_power_supply}; // 12V
DEFAULT_current_loop_p * 100 / voltage_power_supply, current_loop_Ki,
current_loop_Kd, 1500, voltage_power_supply};
PIDController vel_inVelAngloop =
PIDController{DEFAULT_vel_angloop_p * 100, vel_angloop_Ki,
vel_angloop_Kd, 1e6, v_safe};
@@ -89,14 +144,18 @@ class FOC_Controller {
ang_angloop_Ki, ang_angloop_Kd, 1e4, 100};
PIDController ang_inMITloop =
PIDController{DEFAULT_angle_MIT_p * 100 / limit_voltage, angle_MIT_Ki,
PIDController{DEFAULT_angle_MIT_p * 100 / voltage_power_supply, angle_MIT_Ki,
angle_MIT_Kd, 1e5, 100};
PIDController vel_inMITloop = PIDController{
DEFAULT_vel_MIT_p * 100, vel_MIT_Ki, vel_MIT_Kd, 1e9, 1000};
PIDController current_inMITloop = PIDController{
DEFAULT_current_loop_p * 100 / limit_voltage, current_loop_Ki,
current_loop_Kd, 1500, voltage_power_supply}; // 12V
DEFAULT_current_loop_p * 100 / voltage_power_supply, current_loop_Ki,
current_loop_Kd, 1500, voltage_power_supply};
/**
* @brief 更新 pid_limits 相关状态
* @return void
*/
void update_pid_limits() {
vel_loop.limit = voltage_power_supply;
angle_loop.limit = voltage_power_supply;
@@ -107,19 +166,48 @@ class FOC_Controller {
protected:
float shaft_angle = 0;
float Ualpha, Ubeta = 0;
float zero_electric_angle = 0;
float v_half = voltage_power_supply * 0.5f; // 电源电压的一半
float startup_ramp_val = 0.0f; // 软启动系数
float startup_ramp_step = 0.01f; // 启动斜坡步长,可调节以控制启动速度
// 本周期电角度的 sin/cos:update_el_sincos() 每周期算一次,
// Park 变换与逆 Park 变换共用,避免同一角度算两次三角函数
float el_sin = 0.0f;
float el_cos = 0.0f;
/**
* @brief 每周期计算一次电角度 sin/cos 供 Park/逆Park 共用
* @param[in] angle_el 电角度(度)
* @return void
*/
inline void update_el_sincos(float angle_el);
/**
* @brief 设置 setSVPWM_PhaseVoltage 对应的参数或状态
* @param[in] Uq 参数
* @param[in] Ud 参数
* @return void
*/
void setSVPWM_PhaseVoltage(float Uq, float Ud);
/**
* @brief cal_Iq_Id 函数
* @param[in] current_a 参数
* @param[in] current_b 参数
* @param[in] current_c 参数
* @return float 返回值
* @note 电气角度三角函数用 update_el_sincos() 的预计算结果
*/
float cal_Iq_Id(float current_a, float current_b, float current_c);
float update_angle_sync_uq_feedforward(u8 mode, float table[],
u16 table_size, float mech_angle,
float target_vel, float actual_vel,
float velocity_error);
float get_mode3_velocity_p(float target_vel, float actual_vel);
float get_mode3_velocity_scale(float target_vel, float actual_vel,
float min_scale);
};
// 定义函数指针类型
typedef float (FOC_Controller::*FOCFunctionPtr)(void);
/**
* @brief calibrate_zero_position 函数
* @return void
*/
void calibrate_zero_position(void);
inline float _normalizeAngle(float angle);
inline float _electricalAngle();
#endif
+100 -16
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/Filter.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "Filter.h"
#include "Can.h"
#include "ang_sensor.h"
@@ -10,15 +16,39 @@ extern CAN_transmiter CAN0_transmiter;
extern u32 motor_runtime_50us;
// 使用配置文件中的参数
/**
* @brief Angle_kalman 函数
* @param[in] ANGLE_KALMAN_INITIAL_STATE 参数
* @param[in] ANGLE_KALMAN_INITIAL_COVARIANCE 参数
* @param[in] ANGLE_KALMAN_PROCESS_NOISE_Q 参数
* @param[in] ANGLE_KALMAN_MEASUREMENT_NOISE_R 参数
* @return EncoderAngleKalman 返回值
*/
EncoderAngleKalman Angle_kalman(ANGLE_KALMAN_INITIAL_STATE,
ANGLE_KALMAN_INITIAL_COVARIANCE,
ANGLE_KALMAN_PROCESS_NOISE_Q,
ANGLE_KALMAN_MEASUREMENT_NOISE_R);
/**
* @brief Vel_kalman 函数
* @param[in] VELOCITY_KALMAN_INITIAL_STATE 参数
* @param[in] VELOCITY_KALMAN_INITIAL_COVARIANCE 参数
* @param[in] VELOCITY_KALMAN_PROCESS_NOISE_Q 参数
* @param[in] VELOCITY_KALMAN_MEASUREMENT_NOISE_R 参数
* @return EncoderVelocityKalman 返回值
*/
EncoderVelocityKalman Vel_kalman(VELOCITY_KALMAN_INITIAL_STATE,
VELOCITY_KALMAN_INITIAL_COVARIANCE,
VELOCITY_KALMAN_PROCESS_NOISE_Q,
VELOCITY_KALMAN_MEASUREMENT_NOISE_R);
/**
* @brief Current_kalman 函数
* @param[in] CURRENT_KALMAN_INITIAL_STATE 参数
* @param[in] CURRENT_KALMAN_INITIAL_COVARIANCE 参数
* @param[in] CURRENT_KALMAN_PROCESS_NOISE_Q 参数
* @param[in] CURRENT_KALMAN_MEASUREMENT_NOISE_R 参数
* @return CurrentKalman 返回值
*/
CurrentKalman Current_kalman( CURRENT_KALMAN_INITIAL_STATE, CURRENT_KALMAN_INITIAL_COVARIANCE,
CURRENT_KALMAN_PROCESS_NOISE_Q,
CURRENT_KALMAN_MEASUREMENT_NOISE_R);
@@ -28,17 +58,38 @@ CurrentKalman Current_kalman( CURRENT_KALMAN_INITIAL_STATE, CURRENT_KALMAN_INITI
\param[in] values 待滤波的数据数组
\param[in] size 数组大小
\retval 滤波后的中值
\note 使用部分排序优化算法提高效率
\note size==6(ADC_FILTER_SIZE,唯一实际调用)走固定 12 次比较的
排序网络:std::nth_element 是通用算法,对 6 个元素的开销
数倍于固定网络,而本函数在 1kHz 慢速任务里被 3 处调用。
语义与旧版一致:返回上中值(完全排序后的 [size/2])。
*/
static inline void median_cswap(float &a, float &b) {
if (a > b) {
float t = a;
a = b;
b = t;
}
}
float medium_filter(float values[], u8 size) {
// 使用局部数组避免动态内存分配
float sorted[size];
if (size == 6) {
float s0 = values[0], s1 = values[1], s2 = values[2];
float s3 = values[3], s4 = values[4], s5 = values[5];
// n=6 最优排序网络(12 个比较器,Knuth AoCP Vol.3)
median_cswap(s1, s2); median_cswap(s4, s5);
median_cswap(s0, s2); median_cswap(s3, s5);
median_cswap(s0, s1); median_cswap(s3, s4); median_cswap(s2, s5);
median_cswap(s0, s3); median_cswap(s1, s4); median_cswap(s2, s4);
median_cswap(s1, s3); median_cswap(s2, s3);
return s3;
}
// 其他尺寸的通用回退路径(当前无调用者)
float sorted[16];
if (size > 16U) size = 16U;
memcpy(sorted, values, size * SIZE_FLOAT);
// 这比完整排序更高效,时间复杂度从O(n log n)降低到O(n)
std::nth_element(sorted, sorted + size / 2, sorted + size);
return sorted[size / 2]; // 返回中值
return sorted[size / 2];
}
// EncoderVelocityKalman类的实现
@@ -52,6 +103,14 @@ float medium_filter(float values[], u8 size) {
*/
EncoderVelocityKalman::EncoderVelocityKalman(float initial_velocity, float T,
float Q, float R)
/**
* @brief kalman 函数
* @param[in] initial_velocity 参数
* @param[in] T 参数
* @param[in] Q 参数
* @param[in] R 参数
* @return : 返回值
*/
: kalman(initial_velocity, T, Q, R) {}
/*!
@@ -116,17 +175,20 @@ EncoderAngleKalman::EncoderAngleKalman(float initial_angle,
/*!
\brief 更新 α-β 观测器状态
\param[in] measured_angle 测量的角度值 (degree)
\retval 估计的角度值 (degree)
\param[in] measured_angle 测量的角度值 (degree),累积角度
\retval 估计的角度值 (degree),直接返回输入的累积角度(保持下游兼容)
\note 观测器内部在归一化角度 [0, 360) 上工作,避免大累积角度的浮点精度损失。
速度从归一化角度差分推导,静止时不会产生虚假速度。
*/
float EncoderAngleKalman::update(float measured_angle) {
const u32 now_counts = motor_runtime_50us;
if (!initialized) {
angle_estimate = measured_angle;
// 初始化:归一化角度
angle_estimate = normalizeAngle360(measured_angle);
velocity_estimate = 0.0f;
last_update_count = now_counts;
initialized = true;
return angle_estimate;
return measured_angle;
}
u32 dt_counts = now_counts - last_update_count;
@@ -135,13 +197,27 @@ float EncoderAngleKalman::update(float measured_angle) {
dt_counts = 1U;
}
const float dt = TIME_SLICE * static_cast<float>(dt_counts);
const float angle_pred = angle_estimate + velocity_estimate * dt;
const float velocity_pred = velocity_estimate;
const float error = measured_angle - angle_pred;
angle_estimate = angle_pred + alpha_gain * error;
// 将输入的累积角度归一化到 [0, 360)
float measured_norm = normalizeAngle360(measured_angle);
// 预测(在归一化空间)
float angle_pred = normalizeAngle360(angle_estimate + velocity_estimate * dt);
const float velocity_pred = velocity_estimate;
// 计算角度误差,归一化到 [-180, 180) 避免绕回跳变
float error = measured_norm - angle_pred;
if (error > 180.0f) error -= 360.0f;
else if (error < -180.0f) error += 360.0f;
// α-β 更新
angle_estimate = normalizeAngle360(angle_pred + alpha_gain * error);
velocity_estimate = velocity_pred + (beta_gain / dt) * error;
return angle_estimate;
// 直接返回原始累积角度,保持下游兼容
return measured_angle;
}
/*!
@@ -185,6 +261,14 @@ void EncoderAngleKalman::setMeasurementNoise(float r) {
CurrentKalman::CurrentKalman(float initial_current,
float initial_error_covariance,
float process_noise, float measurement_noise)
/**
* @brief kalman 函数
* @param[in] initial_current 参数
* @param[in] initial_error_covariance 参数
* @param[in] process_noise 参数
* @param[in] measurement_noise 参数
* @return : 返回值
*/
: kalman(initial_current, initial_error_covariance, process_noise,
measurement_noise) {}
+81 -2
View File
@@ -1,6 +1,14 @@
/**
* @file Algorithm/Filter.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _FILTER_H
#define _FILTER_H
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -17,6 +25,12 @@ extern "C" {
extern CAN_transmiter CAN0_transmiter;
/**
* @brief medium_filter 函数
* @param[in] values 参数
* @param[in] size 参数
* @return float 返回值
*/
float medium_filter(float values[], u8 size);
/*!
@@ -145,12 +159,31 @@ class EncoderVelocityKalman {
float initial_error_covariance, float process_noise,
float measurement_noise);
/**
* @brief 更新对象内部状态或采样数据
* @param[in] measured_velocity 参数
* @return float 返回值
*/
float update(float measured_velocity);
/**
* @brief 获取 getVelocity 对应的数据或状态
* @return float 返回值
*/
float getVelocity() const;
/**
* @brief 设置 setProcessNoise 对应的参数或状态
* @param[in] q 参数
* @return void
*/
void setProcessNoise(float q);
/**
* @brief 设置 setMeasurementNoise 对应的参数或状态
* @param[in] r 参数
* @return void
*/
void setMeasurementNoise(float r);
u8 init(float init_velocity, float initial_error_covariance = 1.0f) {
@@ -166,8 +199,8 @@ class EncoderVelocityKalman {
*/
class EncoderAngleKalman {
private:
float angle_estimate;
float velocity_estimate;
float angle_estimate; // 归一化角度估计 [0, 360)
float velocity_estimate; // 速度估计 (deg/s)
float alpha_gain;
float beta_gain;
u32 last_update_count;
@@ -177,14 +210,41 @@ class EncoderAngleKalman {
EncoderAngleKalman(float initial_angle, float initial_error_covariance,
float process_noise, float measurement_noise);
/**
* @brief 更新对象内部状态或采样数据
* @param[in] measured_angle 参数
* @return float 返回值
*/
float update(float measured_angle);
/**
* @brief 获取 getAngle 对应的数据或状态
* @return float 返回值
*/
float getAngle() const;
/**
* @brief 获取 getVelocity 对应的数据或状态
* @return float 返回值
*/
float getVelocity() const;
/**
* @brief 设置 setProcessNoise 对应的参数或状态
* @param[in] q 参数
* @return void
*/
void setProcessNoise(float q);
/**
* @brief 获取 getstate 对应的数据或状态
* @return float 返回值
*/
float getstate() const { return angle_estimate; };
/**
* @brief 设置 setMeasurementNoise 对应的参数或状态
* @param[in] r 参数
* @return void
*/
void setMeasurementNoise(float r);
u8 init(float init_angle, float initial_error_covariance = 1.0f) {
(void)initial_error_covariance;
@@ -208,12 +268,31 @@ class CurrentKalman {
CurrentKalman(float initial_current, float initial_error_covariance,
float process_noise, float measurement_noise);
/**
* @brief 更新对象内部状态或采样数据
* @param[in] measured_current 参数
* @return float 返回值
*/
float update(float measured_current);
/**
* @brief 获取 getCurrent 对应的数据或状态
* @return float 返回值
*/
float getCurrent() const;
/**
* @brief 设置 setProcessNoise 对应的参数或状态
* @param[in] q 参数
* @return void
*/
void setProcessNoise(float q);
/**
* @brief 设置 setMeasurementNoise 对应的参数或状态
* @param[in] r 参数
* @return void
*/
void setMeasurementNoise(float r);
u8 init(float init_current, float initial_error_covariance = 1.0f) {
+62 -12
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/pid.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "pid.h"
#define _constrain(amt, low, high) \
((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
@@ -6,33 +12,55 @@
#include <cmath>
extern CAN_transmiter CAN0_transmiter;
// 微分项一阶低通离散系数:α = Ts / (τ + Ts)
constexpr float DERIV_LPF_ALPHA = TIME_SLICE / (DERIV_LPF_TAU + TIME_SLICE);
PIDController::PIDController(float P, float I, float D, float ramp, float limit)
: P(P), I(I), D(D), output_ramp(ramp) // PID控制器加速度限幅
,
limit(limit) // PID控制器输出限幅
,
error_prev(0.0f), output_prev(0.0f), integral_prev(0.0f) {}
error_prev(0.0f), output_prev(0.0f), integral_prev(0.0f),
measurement_prev(0.0f), derivative_filt(0.0f), deriv_init(false) {}
// PID 控制器函数
float PIDController::operator()(float error) {
// 测量微分版本:D 项对测量量而非误差微分,目标阶跃不再产生微分踢
float PIDController::operator()(float error, float measurement) {
// 首次调用只建立测量基线,避免 (meas-0)/Ts 的假微分尖峰
if (!deriv_init) {
measurement_prev = measurement;
deriv_init = true;
}
float d_raw = -(measurement - measurement_prev) / TIME_SLICE;
derivative_filt += DERIV_LPF_ALPHA * (d_raw - derivative_filt);
measurement_prev = measurement;
return step(error, D * derivative_filt);
}
// 公共计算路径:Tustin 积分 + 条件积分抗饱和 + 输出斜坡
float PIDController::step(float error, float derivative) {
// P环
float proportional = P * error;
// Tustin 散点积分(I环)
// 添加积分限幅,防止积分饱和
float integral =
integral_prev + I * TIME_SLICE * 0.5f * (error + error_prev);
// 将P,I,D三环的计算值加起来,与限幅比较得到未饱和/饱和输出
float output_unsat = proportional + integral + derivative;
float output = _constrain(output_unsat, -limit, limit);
// 抗积分饱和(条件积分):输出饱和且误差仍在推高饱和方向时冻结积分。
// 不用大增益反算回退——深饱和时(P 项单独超限幅)反算会把积分压成反号,
// 误差一缩小输出瞬间反向,实测表现为到位疯狂抖动/停不到位。
if (output != output_unsat) {
if ((output_unsat > limit && error > 0.0f) ||
(output_unsat < -limit && error < 0.0f)) {
integral = integral_prev; // 冻结:不再向饱和方向累积
}
}
integral = _constrain(integral, -limit, limit);
// D环(微分环节)
float derivative = D * (error - error_prev) / TIME_SLICE;
// 将P,I,D三环的计算值加起来
float output = proportional + integral + derivative;
output = _constrain(output, -limit, limit);
// 输出斜坡限制
// 简化的斜坡限制(单行逻辑)
if (output_ramp > 0) {
float max_delta = output_ramp * TIME_SLICE;
output = output_prev +
@@ -46,6 +74,25 @@ float PIDController::operator()(float error) {
return output;
}
/**
* @brief 外部饱和泄放积分:前馈叠加后超出限幅时调用
* @param[in] excess 超出限幅的部分(带符号)
* @return void
* @note 仅当积分与超限同号(积分在加剧超限)时泄放,且不泄过零点:
* 防止前馈导致的饱和把积分抽干成反号,到位段失去保持力。
*/
void PIDController::bleed_integral(float excess) {
if (excess > 0.0f && integral_prev > 0.0f) {
float bleed = ANTI_WINDUP_GAIN * excess;
integral_prev = (integral_prev > bleed) ? (integral_prev - bleed)
: 0.0f;
} else if (excess < 0.0f && integral_prev < 0.0f) {
float bleed = ANTI_WINDUP_GAIN * (-excess);
integral_prev = (integral_prev < -bleed) ? (integral_prev + bleed)
: 0.0f;
}
}
/**
* @brief 设置PID参数
*
@@ -87,6 +134,9 @@ void PIDController::reset() {
error_prev = 0.0f;
output_prev = 0.0f;
integral_prev = 0.0f;
measurement_prev = 0.0f;
derivative_filt = 0.0f;
deriv_init = false;
}
/**
+59 -3
View File
@@ -1,17 +1,64 @@
/**
* @file Algorithm/pid.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef PID_H
#define PID_H
/**
* @brief PIDController 类或结构体
*/
class PIDController {
public:
PIDController(float P, float I, float D, float ramp, float limit);
~PIDController() = default;
float operator()(float error);
/**
* @brief PID 计算(唯一入口)
* @param[in] error 控制误差
* @param[in] measurement 反馈测量量(D 项对它微分,目标阶跃不产生微分踢)
* @return float PID 输出
*/
float operator()(float error, float measurement);
/**
* @brief 设置 set_PID 对应的参数
* @param[in] P 参数
* @param[in] I 参数
* @param[in] D 参数
* @return void
*/
void set_PID(float P, float I, float D);
/**
* @brief 设置 set_P 对应的参数
* @param[in] pp 参数
* @return void
*/
void set_P(float pp);
/**
* @brief 设置 set_limit 对应的参数
* @param[in] limit 参数
* @return void
*/
void set_limit(float limit);
/**
* @brief 复位对象内部状态
* @return void
*/
void reset(); // 重置PID控制器状态
/**
* @brief reset_integral 函数
* @return void
*/
void reset_integral(); // 仅清除积分项,保留输出斜坡状态
/**
* @brief 外部饱和泄放积分(反算抗饱和的补充入口)
* @param[in] excess 输出超出限幅的部分(带符号),如前馈叠加后超限
* @return void
* @note 调用方向须与 PID 输出到最终 Uq 的符号一致(有 ×DIR 的要乘上 DIR)
*/
void bleed_integral(float excess);
float P; //!< 比例增益(P环增益)
float I; //!< 积分增益(I环增益)
@@ -20,11 +67,20 @@ class PIDController {
float limit;
protected:
/**
* @brief 公共计算路径:积分、抗饱和、输出斜坡
* @param[in] error 控制误差
* @param[in] derivative 已完成低通滤波的微分项(D 已乘入)
* @return float PID 输出
*/
float step(float error, float derivative);
float error_prev; //!< 最后的跟踪误差值
float output_prev; //!< 最后一个 pid 输出值
float integral_prev; //!< 最后一个积分分量值
unsigned long timestamp_prev; //!< 上次执行时间戳
float integral_separation_threshold; //!< 积分分离阈值,用于抗积分饱和
float measurement_prev; //!< 上次测量值(对测量微分用)
float derivative_filt; //!< 微分项一阶低通滤波后的变化率
bool deriv_init; //!< 测量微分是否已完成首次采样
};
#endif
+5
View File
@@ -0,0 +1,5 @@
/**
* @file Algorithm/thermal.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
+6
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/thermal.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef THERMAL_H
#define THERMAL_H
@@ -35,25 +35,37 @@ OF SUCH DAMAGE.
#include "gd32c10x_adc.h"
/* discontinuous mode macro*/
/** @brief ADC_CHANNEL_LENGTH_SUBTRACT_ONE 宏定义 */
#define ADC_CHANNEL_LENGTH_SUBTRACT_ONE ((uint8_t)1U)
/* ADC regular channel macro */
/** @brief ADC_REGULAR_CHANNEL_RANK_SIX 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_SIX ((uint8_t)6U)
/** @brief ADC_REGULAR_CHANNEL_RANK_TWELVE 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_TWELVE ((uint8_t)12U)
/** @brief ADC_REGULAR_CHANNEL_RANK_SIXTEEN 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_SIXTEEN ((uint8_t)16U)
/** @brief ADC_REGULAR_CHANNEL_RANK_LENGTH 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_LENGTH ((uint8_t)5U)
/* ADC sampling time macro */
/** @brief ADC_CHANNEL_SAMPLE_TEN 宏定义 */
#define ADC_CHANNEL_SAMPLE_TEN ((uint8_t)10U)
/** @brief ADC_CHANNEL_SAMPLE_EIGHTEEN 宏定义 */
#define ADC_CHANNEL_SAMPLE_EIGHTEEN ((uint8_t)18U)
/** @brief ADC_CHANNEL_SAMPLE_LENGTH 宏定义 */
#define ADC_CHANNEL_SAMPLE_LENGTH ((uint8_t)3U)
/* ADC inserted channel macro */
/** @brief ADC_INSERTED_CHANNEL_RANK_LENGTH 宏定义 */
#define ADC_INSERTED_CHANNEL_RANK_LENGTH ((uint8_t)5U)
/** @brief ADC_INSERTED_CHANNEL_SHIFT_LENGTH 宏定义 */
#define ADC_INSERTED_CHANNEL_SHIFT_LENGTH ((uint8_t)15U)
/* ADC inserted channel offset macro */
/** @brief ADC_OFFSET_LENGTH 宏定义 */
#define ADC_OFFSET_LENGTH ((uint8_t)3U)
/** @brief ADC_OFFSET_SHIFT_LENGTH 宏定义 */
#define ADC_OFFSET_SHIFT_LENGTH ((uint8_t)4U)
/*!
@@ -97,6 +109,11 @@ void adc_deinit(uint32_t adc_periph)
\param[out] none
\retval none
*/
/**
* @brief adc_mode_config 函数
* @param[in] mode 参数
* @return void
*/
void adc_mode_config(uint32_t mode)
{
ADC_CTL0(ADC0) &= ~(ADC_CTL0_SYNCM);
@@ -286,6 +303,14 @@ void adc_resolution_config(uint32_t adc_periph , uint32_t resolution)
\param[out] none
\retval none
*/
/**
* @brief adc_oversample_mode_config 函数
* @param[in] adc_periph 参数
* @param[in] mode 参数
* @param[in] shift 参数
* @param[in] ratio 参数
* @return void
*/
void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint8_t ratio)
{
if(ADC_OVERSAMPLING_ONE_CONVERT == mode){
@@ -357,6 +382,13 @@ void adc_dma_mode_disable(uint32_t adc_periph)
\param[out] none
\retval none
*/
/**
* @brief adc_discontinuous_mode_config 函数
* @param[in] adc_periph 参数
* @param[in] adc_channel_group 参数
* @param[in] length 参数
* @return void
*/
void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_group, uint8_t length)
{
/* disable discontinuous mode of regular & inserted channel */
@@ -393,6 +425,13 @@ void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_grou
\param[out] none
\retval none
*/
/**
* @brief adc_channel_length_config 函数
* @param[in] adc_periph 参数
* @param[in] adc_channel_group 参数
* @param[in] length 参数
* @return void
*/
void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t length)
{
switch(adc_channel_group){
@@ -431,6 +470,14 @@ void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, u
\param[out] none
\retval none
*/
/**
* @brief adc_regular_channel_config 函数
* @param[in] adc_periph 参数
* @param[in] rank 参数
* @param[in] adc_channel 参数
* @param[in] sample_time 参数
* @return void
*/
void adc_regular_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc_channel , uint32_t sample_time)
{
uint32_t rsq,sampt;
@@ -499,6 +546,14 @@ void adc_regular_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc
\param[out] none
\retval none
*/
/**
* @brief adc_inserted_channel_config 函数
* @param[in] adc_periph 参数
* @param[in] rank 参数
* @param[in] adc_channel 参数
* @param[in] sample_time 参数
* @return void
*/
void adc_inserted_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc_channel , uint32_t sample_time)
{
uint8_t inserted_length;
@@ -544,6 +599,13 @@ void adc_inserted_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t ad
\param[out] none
\retval none
*/
/**
* @brief adc_inserted_channel_offset_config 函数
* @param[in] adc_periph 参数
* @param[in] inserted_channel 参数
* @param[in] offset 参数
* @return void
*/
void adc_inserted_channel_offset_config(uint32_t adc_periph , uint8_t inserted_channel , uint16_t offset)
{
uint8_t inserted_length;
@@ -592,6 +654,13 @@ void adc_inserted_channel_offset_config(uint32_t adc_periph , uint8_t inserted_c
\param[out] none
\retval none
*/
/**
* @brief adc_external_trigger_source_config 函数
* @param[in] adc_periph 参数
* @param[in] adc_channel_group 参数
* @param[in] external_trigger_source 参数
* @return void
*/
void adc_external_trigger_source_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t external_trigger_source)
{
switch(adc_channel_group){
@@ -817,6 +886,12 @@ void adc_watchdog_threshold_config(uint32_t adc_periph , uint16_t low_threshold
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief adc_flag_get 函数
* @param[in] adc_periph 参数
* @param[in] adc_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus adc_flag_get(uint32_t adc_periph , uint32_t adc_flag)
{
FlagStatus reval = RESET;
@@ -839,6 +914,12 @@ FlagStatus adc_flag_get(uint32_t adc_periph , uint32_t adc_flag)
\param[out] none
\retval none
*/
/**
* @brief adc_flag_clear 函数
* @param[in] adc_periph 参数
* @param[in] adc_flag 参数
* @return void
*/
void adc_flag_clear(uint32_t adc_periph , uint32_t adc_flag)
{
ADC_STAT(adc_periph) &= ~((uint32_t)adc_flag);
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_bkp.h"
/* BKP register bits offset */
/** @brief BKP_TAMPER_BITS_OFFSET 宏定义 */
#define BKP_TAMPER_BITS_OFFSET ((uint32_t)8U)
/*!
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_can.h"
#include <stdlib.h>
/** @brief CAN_ERROR_HANDLE 宏定义 */
#define CAN_ERROR_HANDLE(s) do{}while(1)
/* This table can be used to calculate data length in FD mode */
@@ -71,6 +72,12 @@ void can_deinit(uint32_t can_periph)
\param[out] none
\retval none
*/
/**
* @brief can_struct_para_init 函数
* @param[in] type 参数
* @param[in] p_struct 参数
* @return void
*/
void can_struct_para_init(can_struct_type_enum type, void *p_struct)
{
uint8_t i;
@@ -183,6 +190,12 @@ void can_struct_para_init(can_struct_type_enum type, void *p_struct)
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief can_init 函数
* @param[in] can_periph 参数
* @param[in] can_parameter_init 参数
* @return ErrStatus 返回值
*/
ErrStatus can_init(uint32_t can_periph, can_parameter_struct *can_parameter_init)
{
uint32_t timeout = CAN_TIMEOUT;
@@ -296,6 +309,12 @@ ErrStatus can_init(uint32_t can_periph, can_parameter_struct *can_parameter_init
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief can_fd_init 函数
* @param[in] can_periph 参数
* @param[in] can_fdframe_init 参数
* @return ErrStatus 返回值
*/
ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
{
uint32_t timeout = CAN_TIMEOUT;
@@ -388,6 +407,11 @@ ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
\param[out] none
\retval none
*/
/**
* @brief can_filter_init 函数
* @param[in] can_filter_parameter_init 参数
* @return void
*/
void can_filter_init(can_filter_parameter_struct *can_filter_parameter_init)
{
uint32_t val = 0U;
@@ -470,6 +494,14 @@ void can_filter_init(can_filter_parameter_struct *can_filter_parameter_init)
\param[out] none
\retval none
*/
/**
* @brief can_filter_mask_mode_init 函数
* @param[in] id 参数
* @param[in] mask 参数
* @param[in] format_fifo 参数
* @param[in] filter_number 参数
* @return void
*/
void can_filter_mask_mode_init(uint32_t id, uint32_t mask, can_format_fifo_enum format_fifo, uint16_t filter_number)
{
can_filter_parameter_struct can_filter;
@@ -556,6 +588,12 @@ void can_filter_mask_mode_init(uint32_t id, uint32_t mask, can_format_fifo_enum
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief can_monitor_mode_set 函数
* @param[in] can_periph 参数
* @param[in] mode 参数
* @return ErrStatus 返回值
*/
ErrStatus can_monitor_mode_set(uint32_t can_periph, uint8_t mode)
{
ErrStatus reval = SUCCESS;
@@ -728,6 +766,12 @@ void can_time_trigger_mode_disable(uint32_t can_periph)
\param[out] none
\retval mailbox_number
*/
/**
* @brief can_message_transmit 函数
* @param[in] can_periph 参数
* @param[in] transmit_message 参数
* @return uint8_t 返回值
*/
uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct *transmit_message)
{
uint8_t mailbox_number = CAN_MAILBOX0;
@@ -975,6 +1019,13 @@ ErrStatus can_transmission_stop(uint32_t can_periph, uint8_t mailbox_number)
\arg rx_fi: 0 - 27
\retval none
*/
/**
* @brief can_message_receive 函数
* @param[in] can_periph 参数
* @param[in] fifo_number 参数
* @param[in] receive_message 参数
* @return void
*/
void can_message_receive(uint32_t can_periph, uint8_t fifo_number, can_receive_message_struct *receive_message)
{
uint32_t canfd_en = 0U;
@@ -1214,6 +1265,11 @@ ErrStatus can_wakeup(uint32_t can_periph)
\arg CAN_ERROR_CRC: CRC error
\arg CAN_ERROR_SOFTWARECFG: software configure
*/
/**
* @brief can_error_get 函数
* @param[in] can_periph 参数
* @return can_error_enum 返回值
*/
can_error_enum can_error_get(uint32_t can_periph)
{
can_error_enum error;
@@ -1278,6 +1334,12 @@ uint8_t can_transmit_error_number_get(uint32_t can_periph)
\param[out] none
\retval none
*/
/**
* @brief can_interrupt_enable 函数
* @param[in] can_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void can_interrupt_enable(uint32_t can_periph, uint32_t interrupt)
{
CAN_INTEN(can_periph) |= interrupt;
@@ -1306,6 +1368,12 @@ void can_interrupt_enable(uint32_t can_periph, uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief can_interrupt_disable 函数
* @param[in] can_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void can_interrupt_disable(uint32_t can_periph, uint32_t interrupt)
{
CAN_INTEN(can_periph) &= ~interrupt;
@@ -1354,6 +1422,12 @@ void can_interrupt_disable(uint32_t can_periph, uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief can_flag_get 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus can_flag_get(uint32_t can_periph, can_flag_enum flag)
{
FlagStatus retval;
@@ -1394,6 +1468,12 @@ FlagStatus can_flag_get(uint32_t can_periph, can_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief can_flag_clear 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return void
*/
void can_flag_clear(uint32_t can_periph, can_flag_enum flag)
{
CAN_REG_VAL(can_periph, flag) |= BIT(CAN_BIT_POS(flag));
@@ -1424,6 +1504,12 @@ void can_flag_clear(uint32_t can_periph, can_flag_enum flag)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief can_interrupt_flag_get 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum flag)
{
FlagStatus retval;
@@ -1469,6 +1555,12 @@ FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum f
\param[out] none
\retval none
*/
/**
* @brief can_interrupt_flag_clear 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return void
*/
void can_interrupt_flag_clear(uint32_t can_periph, can_interrupt_flag_enum flag)
{
CAN_REG_VALS(can_periph, flag) |= BIT(CAN_BIT_POS0(flag));
@@ -34,7 +34,9 @@ OF SUCH DAMAGE.
#include "gd32c10x_crc.h"
/** @brief CRC_DATA_RESET_VALUE 宏定义 */
#define CRC_DATA_RESET_VALUE ((uint32_t)0xFFFFFFFFU)
/** @brief CRC_FDATA_RESET_VALUE 宏定义 */
#define CRC_FDATA_RESET_VALUE ((uint32_t)0x00000000U)
/*!
@@ -34,12 +34,17 @@ OF SUCH DAMAGE.
#include "gd32c10x_ctc.h"
/** @brief CTC_FLAG_MASK 宏定义 */
#define CTC_FLAG_MASK ((uint32_t)0x00000700U)
/* CTC register bit offset */
/** @brief CTC_TRIMVALUE_OFFSET 宏定义 */
#define CTC_TRIMVALUE_OFFSET ((uint32_t)8U)
/** @brief CTC_TRIM_VALUE_OFFSET 宏定义 */
#define CTC_TRIM_VALUE_OFFSET ((uint32_t)8U)
/** @brief CTC_REFCAP_OFFSET 宏定义 */
#define CTC_REFCAP_OFFSET ((uint32_t)16U)
/** @brief CTC_LIMIT_VALUE_OFFSET 宏定义 */
#define CTC_LIMIT_VALUE_OFFSET ((uint32_t)16U)
/*!
@@ -163,6 +168,11 @@ void ctc_refsource_signal_select(uint32_t refs)
\param[out] none
\retval none
*/
/**
* @brief ctc_refsource_prescaler_config 函数
* @param[in] prescaler 参数
* @return void
*/
void ctc_refsource_prescaler_config(uint32_t prescaler)
{
CTC_CTL1 &= (uint32_t)(~CTC_CTL1_REFPSC);
@@ -300,6 +310,11 @@ void ctc_interrupt_disable(uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief ctc_interrupt_flag_get 函数
* @param[in] interrupt 参数
* @return FlagStatus 返回值
*/
FlagStatus ctc_interrupt_flag_get(uint32_t interrupt)
{
uint32_t intflag = 0U, intenable = 0U;
@@ -337,6 +352,11 @@ FlagStatus ctc_interrupt_flag_get(uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief ctc_interrupt_flag_clear 函数
* @param[in] interrupt 参数
* @return void
*/
void ctc_interrupt_flag_clear(uint32_t interrupt)
{
if(RESET != (interrupt & CTC_FLAG_MASK)){
@@ -360,6 +380,11 @@ void ctc_interrupt_flag_clear(uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief ctc_flag_get 函数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus ctc_flag_get(uint32_t flag)
{
FlagStatus ret;
@@ -386,6 +411,11 @@ FlagStatus ctc_flag_get(uint32_t flag)
\param[out] none
\retval none
*/
/**
* @brief ctc_flag_clear 函数
* @param[in] flag 参数
* @return void
*/
void ctc_flag_clear(uint32_t flag)
{
if(RESET != (flag & CTC_FLAG_MASK)){
@@ -35,8 +35,11 @@ OF SUCH DAMAGE.
#include "gd32c10x_dac.h"
/* DAC register bit offset */
/** @brief OUT1_REG_OFFSET 宏定义 */
#define OUT1_REG_OFFSET ((uint32_t)0x00000010U)
/** @brief DH_12BIT_OFFSET 宏定义 */
#define DH_12BIT_OFFSET ((uint32_t)0x00000010U)
/** @brief DH_8BIT_OFFSET 宏定义 */
#define DH_8BIT_OFFSET ((uint32_t)0x00000008U)
/*!
@@ -203,6 +206,14 @@ uint16_t dac_output_value_get(uint32_t dac_periph, uint8_t dac_out)
\param[out] none
\retval none
*/
/**
* @brief dac_data_set 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] dac_align 参数
* @param[in] data 参数
* @return void
*/
void dac_data_set(uint32_t dac_periph, uint8_t dac_out, uint32_t dac_align, uint16_t data)
{
/* DAC_OUT0 data alignment */
@@ -299,6 +310,13 @@ void dac_trigger_disable(uint32_t dac_periph, uint8_t dac_out)
\param[out] none
\retval none
*/
/**
* @brief dac_trigger_source_config 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] triggersource 参数
* @return void
*/
void dac_trigger_source_config(uint32_t dac_periph, uint8_t dac_out, uint32_t triggersource)
{
if(DAC_OUT0 == dac_out){
@@ -380,6 +398,13 @@ void dac_wave_mode_config(uint32_t dac_periph, uint8_t dac_out, uint32_t wave_mo
\param[out] none
\retval none
*/
/**
* @brief dac_lfsr_noise_config 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] unmask_bits 参数
* @return void
*/
void dac_lfsr_noise_config(uint32_t dac_periph, uint8_t dac_out, uint32_t unmask_bits)
{
if(DAC_OUT0 == dac_out){
@@ -416,6 +441,13 @@ void dac_lfsr_noise_config(uint32_t dac_periph, uint8_t dac_out, uint32_t unmask
\param[out] none
\retval none
*/
/**
* @brief dac_triangle_noise_config 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] amplitude 参数
* @return void
*/
void dac_triangle_noise_config(uint32_t dac_periph, uint8_t dac_out, uint32_t amplitude)
{
if(DAC_OUT0 == dac_out){
@@ -514,6 +546,14 @@ void dac_concurrent_output_buffer_disable(uint32_t dac_periph)
\param[out] none
\retval none
*/
/**
* @brief dac_concurrent_data_set 函数
* @param[in] dac_periph 参数
* @param[in] dac_align 参数
* @param[in] data0 参数
* @param[in] data1 参数
* @return void
*/
void dac_concurrent_data_set(uint32_t dac_periph, uint32_t dac_align, uint16_t data0, uint16_t data1)
{
uint32_t data = 0U;
@@ -35,9 +35,16 @@ OF SUCH DAMAGE.
#include "gd32c10x_dma.h"
#include <stdio.h>
/** @brief DMA_WRONG_HANDLE 宏定义 */
#define DMA_WRONG_HANDLE while(1){}
/* check whether peripheral matches channels or not */
/**
* @brief dma_periph_and_channel_check 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @return ErrStatus 返回值
*/
static ErrStatus dma_periph_and_channel_check(uint32_t dma_periph, dma_channel_enum channelx);
/*!
@@ -110,6 +117,13 @@ void dma_struct_para_init(dma_parameter_struct* init_struct)
\param[out] none
\retval none
*/
/**
* @brief dma_init 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] init_struct 参数
* @return void
*/
void dma_init(uint32_t dma_periph, dma_channel_enum channelx, dma_parameter_struct* init_struct)
{
uint32_t ctl;
@@ -365,6 +379,13 @@ uint32_t dma_transfer_number_get(uint32_t dma_periph, dma_channel_enum channelx)
\param[out] none
\retval none
*/
/**
* @brief dma_priority_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] priority 参数
* @return void
*/
void dma_priority_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t priority)
{
uint32_t ctl;
@@ -396,6 +417,13 @@ void dma_priority_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_
\param[out] none
\retval none
*/
/**
* @brief dma_memory_width_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] mwidth 参数
* @return void
*/
void dma_memory_width_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t mwidth)
{
uint32_t ctl;
@@ -427,6 +455,13 @@ void dma_memory_width_config(uint32_t dma_periph, dma_channel_enum channelx, uin
\param[out] none
\retval none
*/
/**
* @brief dma_periph_width_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] pwidth 参数
* @return void
*/
void dma_periph_width_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t pwidth)
{
uint32_t ctl;
@@ -533,6 +568,13 @@ void dma_periph_increase_disable(uint32_t dma_periph, dma_channel_enum channelx)
\param[out] none
\retval none
*/
/**
* @brief dma_transfer_direction_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] direction 参数
* @return void
*/
void dma_transfer_direction_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t direction)
{
if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){
@@ -561,6 +603,13 @@ void dma_transfer_direction_config(uint32_t dma_periph, dma_channel_enum channel
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief dma_flag_get 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus dma_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
FlagStatus reval;
@@ -591,6 +640,13 @@ FlagStatus dma_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t
\param[out] none
\retval none
*/
/**
* @brief dma_flag_clear 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return void
*/
void dma_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx);
@@ -611,6 +667,13 @@ void dma_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t fla
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief dma_interrupt_flag_get 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
uint32_t interrupt_enable = 0U, interrupt_flag = 0U;
@@ -661,6 +724,13 @@ FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx
\param[out] none
\retval none
*/
/**
* @brief dma_interrupt_flag_clear 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return void
*/
void dma_interrupt_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx);
@@ -681,6 +751,13 @@ void dma_interrupt_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, ui
\param[out] none
\retval none
*/
/**
* @brief dma_interrupt_enable 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] source 参数
* @return void
*/
void dma_interrupt_enable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source)
{
if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){
@@ -705,6 +782,13 @@ void dma_interrupt_enable(uint32_t dma_periph, dma_channel_enum channelx, uint32
\param[out] none
\retval none
*/
/**
* @brief dma_interrupt_disable 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] source 参数
* @return void
*/
void dma_interrupt_disable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source)
{
if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){
@@ -35,25 +35,41 @@ OF SUCH DAMAGE.
#include "gd32c10x_exmc.h"
/* EXMC bank0 register reset value */
/** @brief BANK0_SNCTL_RESET 宏定义 */
#define BANK0_SNCTL_RESET ((uint32_t)0x000030DBU)
/** @brief BANK0_SNTCFG_RESET 宏定义 */
#define BANK0_SNTCFG_RESET ((uint32_t)0x0FFFFFFFU)
/** @brief BANK0_SNWTCFG_RESET 宏定义 */
#define BANK0_SNWTCFG_RESET ((uint32_t)0x0FFFFFFFU)
/* EXMC register bit offset */
/** @brief SNCTL_NRMUX_OFFSET 宏定义 */
#define SNCTL_NRMUX_OFFSET ((uint32_t)1U)
/** @brief SNCTL_SBRSTEN_OFFSET 宏定义 */
#define SNCTL_SBRSTEN_OFFSET ((uint32_t)8U)
/** @brief SNCTL_WRAPEN_OFFSET 宏定义 */
#define SNCTL_WRAPEN_OFFSET ((uint32_t)10U)
/** @brief SNCTL_WREN_OFFSET 宏定义 */
#define SNCTL_WREN_OFFSET ((uint32_t)12U)
/** @brief SNCTL_NRWTEN_OFFSET 宏定义 */
#define SNCTL_NRWTEN_OFFSET ((uint32_t)13U)
/** @brief SNCTL_EXMODEN_OFFSET 宏定义 */
#define SNCTL_EXMODEN_OFFSET ((uint32_t)14U)
/** @brief SNCTL_ASYNCWAIT_OFFSET 宏定义 */
#define SNCTL_ASYNCWAIT_OFFSET ((uint32_t)15U)
/** @brief SNTCFG_AHLD_OFFSET 宏定义 */
#define SNTCFG_AHLD_OFFSET ((uint32_t)4U)
/** @brief SNTCFG_DSET_OFFSET 宏定义 */
#define SNTCFG_DSET_OFFSET ((uint32_t)8U)
/** @brief SNTCFG_BUSLAT_OFFSET 宏定义 */
#define SNTCFG_BUSLAT_OFFSET ((uint32_t)16U)
/** @brief SNWTCFG_WAHLD_OFFSET 宏定义 */
#define SNWTCFG_WAHLD_OFFSET ((uint32_t)4U)
/** @brief SNWTCFG_WDSET_OFFSET 宏定义 */
#define SNWTCFG_WDSET_OFFSET ((uint32_t)8U)
/** @brief SNWTCFG_WBUSLAT_OFFSET 宏定义 */
#define SNWTCFG_WBUSLAT_OFFSET ((uint32_t)16U)
/*!
@@ -130,6 +146,11 @@ void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_i
\param[out] none
\retval none
*/
/**
* @brief exmc_norsram_init 函数
* @param[in] exmc_norsram_init_struct 参数
* @return void
*/
void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct)
{
uint32_t snctl = 0x00000000U, sntcfg = 0x00000000U, snwtcfg = 0x00000000U;
@@ -34,6 +34,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_exti.h"
/** @brief EXTI_REG_RESET_VALUE 宏定义 */
#define EXTI_REG_RESET_VALUE ((uint32_t)0x00000000U)
/*!
@@ -69,6 +70,13 @@ void exti_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief exti_init 函数
* @param[in] linex 参数
* @param[in] mode 参数
* @param[in] trig_type 参数
* @return void
*/
void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type)
{
/* reset the EXTI line x */
@@ -35,25 +35,45 @@ OF SUCH DAMAGE.
#include "gd32c10x_fmc.h"
/* FMC mask */
/** @brief LOW_8BITS_MASK 宏定义 */
#define LOW_8BITS_MASK ((uint32_t)0x000000FFU)
/** @brief HIGH_8BITS_MASK 宏定义 */
#define HIGH_8BITS_MASK ((uint32_t)0x0000FF00U)
/** @brief LOW_8BITS_MASK1 宏定义 */
#define LOW_8BITS_MASK1 ((uint32_t)0x00FF0000U)
/** @brief HIGH_8BITS_MASK1 宏定义 */
#define HIGH_8BITS_MASK1 ((uint32_t)0xFF000000U)
/** @brief LOW_16BITS_MASK 宏定义 */
#define LOW_16BITS_MASK ((uint32_t)0x0000FFFFU)
/** @brief HIGH_16BITS_MASK 宏定义 */
#define HIGH_16BITS_MASK ((uint32_t)0xFFFF0000U)
/* USER of option bytes mask */
/** @brief OB_USER_MASK 宏定义 */
#define OB_USER_MASK ((uint8_t)0xF8U)
/* OB_WP mask */
/** @brief OB_WP0_MASK 宏定义 */
#define OB_WP0_MASK ((uint32_t)0x000000FFU)
/** @brief OB_WP1_MASK 宏定义 */
#define OB_WP1_MASK ((uint32_t)0x0000FF00U)
/** @brief OB_WP2_MASK 宏定义 */
#define OB_WP2_MASK ((uint32_t)0x00FF0000U)
/** @brief OB_WP3_MASK 宏定义 */
#define OB_WP3_MASK ((uint32_t)0xFF000000U)
/* return the FMC state */
/**
* @brief fmc_state_get 函数
* @return fmc_state_enum 返回值
*/
static fmc_state_enum fmc_state_get(void);
/* check FMC ready or not */
/**
* @brief fmc_ready_wait 函数
* @param[in] timeout 参数
* @return fmc_state_enum 返回值
*/
static fmc_state_enum fmc_ready_wait(uint32_t timeout);
/* FMC main memory programming functions */
@@ -446,6 +466,11 @@ fmc_state_enum ob_erase(void)
\arg FMC_PGAERR: program alignment error
\arg FMC_TOERR: timeout error
*/
/**
* @brief ob_write_protection_enable 函数
* @param[in] ob_wp 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum ob_write_protection_enable(uint32_t ob_wp)
{
uint32_t i;
@@ -505,6 +530,11 @@ fmc_state_enum ob_write_protection_enable(uint32_t ob_wp)
\arg FMC_PGAERR: program alignment error
\arg FMC_TOERR: timeout error
*/
/**
* @brief ob_security_protection_config 函数
* @param[in] ob_spc 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum ob_security_protection_config(uint8_t ob_spc)
{
uint32_t i;
@@ -571,6 +601,13 @@ fmc_state_enum ob_security_protection_config(uint8_t ob_spc)
\arg FMC_PGAERR: program alignment error
\arg FMC_TOERR: timeout error
*/
/**
* @brief ob_user_write 函数
* @param[in] ob_fwdgt 参数
* @param[in] ob_deepsleep 参数
* @param[in] ob_stdby 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum ob_user_write(uint8_t ob_fwdgt, uint8_t ob_deepsleep, uint8_t ob_stdby)
{
uint32_t i;
@@ -700,6 +737,11 @@ uint16_t fmc_ecc_address_get(void)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief fmc_ecc_flag_get 函数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus fmc_ecc_flag_get(uint32_t flag)
{
FlagStatus status = RESET;
@@ -725,6 +767,11 @@ FlagStatus fmc_ecc_flag_get(uint32_t flag)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief fmc_ecc_flag_clear 函数
* @param[in] flag 参数
* @return void
*/
void fmc_ecc_flag_clear(uint32_t flag)
{
FMC_ECCCS |= flag;
@@ -81,6 +81,11 @@ void fwdgt_enable(void)
\param[out] none
\retval ErrStatus: ERROR or SUCCESS
*/
/**
* @brief fwdgt_prescaler_value_config 函数
* @param[in] prescaler_value 参数
* @return ErrStatus 返回值
*/
ErrStatus fwdgt_prescaler_value_config(uint16_t prescaler_value)
{
uint32_t timeout = FWDGT_PSC_TIMEOUT;
@@ -161,6 +166,12 @@ void fwdgt_counter_reload(void)
\param[out] none
\retval ErrStatus: ERROR or SUCCESS
*/
/**
* @brief fwdgt_config 函数
* @param[in] reload_value 参数
* @param[in] prescaler_value 参数
* @return ErrStatus 返回值
*/
ErrStatus fwdgt_config(uint16_t reload_value, uint8_t prescaler_value)
{
uint32_t timeout = FWDGT_PSC_TIMEOUT;
@@ -34,6 +34,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_gpio.h"
/** @brief AFIO_EXTI_SOURCE_MASK 宏定义 */
#define AFIO_EXTI_SOURCE_MASK ((uint8_t)0x03U) /*!< AFIO exti source selection mask*/
#define AFIO_EXTI_SOURCE_FIELDS ((uint8_t)0x04U) /*!< select AFIO exti source registers */
#define LSB_16BIT_MASK ((uint16_t)0xFFFFU) /*!< LSB 16-bit mask */
@@ -120,6 +121,14 @@ void gpio_afio_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief gpio_init 函数
* @param[in] gpio_periph 参数
* @param[in] mode 参数
* @param[in] speed 参数
* @param[in] pin 参数
* @return void
*/
void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)
{
uint16_t i;
@@ -352,6 +361,12 @@ uint16_t gpio_output_port_get(uint32_t gpio_periph)
\param[out] none
\retval none
*/
/**
* @brief gpio_pin_remap_config 函数
* @param[in] remap 参数
* @param[in] newvalue 参数
* @return void
*/
void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue)
{
uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U;
@@ -409,6 +424,12 @@ void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue)
\param[out] none
\retval none
*/
/**
* @brief gpio_exti_source_select 函数
* @param[in] output_port 参数
* @param[in] output_pin 参数
* @return void
*/
void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin)
{
uint32_t source = 0U;
@@ -449,6 +470,12 @@ void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin)
\param[out] none
\retval none
*/
/**
* @brief gpio_event_output_config 函数
* @param[in] output_port 参数
* @param[in] output_pin 参数
* @return void
*/
void gpio_event_output_config(uint8_t output_port, uint8_t output_pin)
{
uint32_t reg = 0U;
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_i2c.h"
/* I2C register bit mask */
/** @brief I2CCLK_MAX 宏定义 */
#define I2CCLK_MAX ((uint32_t)0x0000003FU) /*!< i2cclk maximum value */
#define I2CCLK_MIN ((uint32_t)0x00000002U) /*!< i2cclk minimum value */
#define I2C_FLAG_MASK ((uint32_t)0x0000FFFFU) /*!< i2c flag mask */
@@ -42,6 +43,7 @@ OF SUCH DAMAGE.
#define I2C_ADDRESS2_MASK ((uint32_t)0x000000FEU) /*!< the second i2c address mask */
/* I2C register bit offset */
/** @brief STAT1_PECV_OFFSET 宏定义 */
#define STAT1_PECV_OFFSET ((uint32_t)0x00000008U) /*!< bit offset of PECV in I2C_STAT1 */
/*!
@@ -167,6 +169,14 @@ void i2c_clock_config(uint32_t i2c_periph, uint32_t clkspeed, uint32_t dutycyc)
\param[out] none
\retval none
*/
/**
* @brief i2c_mode_addr_config 函数
* @param[in] i2c_periph 参数
* @param[in] mode 参数
* @param[in] addformat 参数
* @param[in] addr 参数
* @return void
*/
void i2c_mode_addr_config(uint32_t i2c_periph, uint32_t mode, uint32_t addformat, uint32_t addr)
{
/* SMBus/I2C mode selected */
@@ -633,6 +643,12 @@ void i2c_sam_timeout_disable(uint32_t i2c_periph)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief i2c_flag_get 函数
* @param[in] i2c_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus i2c_flag_get(uint32_t i2c_periph, i2c_flag_enum flag)
{
FlagStatus status = RESET;
@@ -664,6 +680,12 @@ FlagStatus i2c_flag_get(uint32_t i2c_periph, i2c_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief i2c_flag_clear 函数
* @param[in] i2c_periph 参数
* @param[in] flag 参数
* @return void
*/
void i2c_flag_clear(uint32_t i2c_periph, i2c_flag_enum flag)
{
if(I2C_FLAG_ADDSEND == flag){
@@ -690,6 +712,12 @@ void i2c_flag_clear(uint32_t i2c_periph, i2c_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief i2c_interrupt_enable 函数
* @param[in] i2c_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void i2c_interrupt_enable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
{
I2C_REG_VAL(i2c_periph, interrupt) |= BIT(I2C_BIT_POS(interrupt));
@@ -710,6 +738,12 @@ void i2c_interrupt_enable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
\param[out] none
\retval none
*/
/**
* @brief i2c_interrupt_disable 函数
* @param[in] i2c_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void i2c_interrupt_disable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
{
I2C_REG_VAL(i2c_periph, interrupt) &= ~BIT(I2C_BIT_POS(interrupt));
@@ -741,6 +775,12 @@ void i2c_interrupt_disable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief i2c_interrupt_flag_get 函数
* @param[in] i2c_periph 参数
* @param[in] int_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus i2c_interrupt_flag_get(uint32_t i2c_periph, i2c_interrupt_flag_enum int_flag)
{
uint32_t intenable = 0U, flagstatus = 0U, bufie;
@@ -789,6 +829,12 @@ FlagStatus i2c_interrupt_flag_get(uint32_t i2c_periph, i2c_interrupt_flag_enum i
\param[out] none
\retval none
*/
/**
* @brief i2c_interrupt_flag_clear 函数
* @param[in] i2c_periph 参数
* @param[in] int_flag 参数
* @return void
*/
void i2c_interrupt_flag_clear(uint32_t i2c_periph, i2c_interrupt_flag_enum int_flag)
{
if(I2C_INT_FLAG_ADDSEND == int_flag){
@@ -62,6 +62,11 @@ void pmu_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief pmu_lvd_select 函数
* @param[in] lvdt_n 参数
* @return void
*/
void pmu_lvd_select(uint32_t lvdt_n)
{
/* disable LVD */
@@ -139,6 +144,12 @@ void pmu_to_sleepmode(uint8_t sleepmodecmd)
\param[out] none
\retval none
*/
/**
* @brief pmu_to_deepsleepmode 函数
* @param[in] ldo 参数
* @param[in] deepsleepmodecmd 参数
* @return void
*/
void pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd)
{
static uint32_t reg_snap[4];
@@ -35,24 +35,34 @@ OF SUCH DAMAGE.
#include "gd32c10x_rcu.h"
/* define clock source */
/** @brief SEL_IRC8M 宏定义 */
#define SEL_IRC8M ((uint16_t)0U) /* IRC8M is selected as CK_SYS */
/** @brief SEL_HXTAL 宏定义 */
#define SEL_HXTAL ((uint16_t)1U) /* HXTAL is selected as CK_SYS */
/** @brief SEL_PLL 宏定义 */
#define SEL_PLL ((uint16_t)2U) /* PLL is selected as CK_SYS */
/* define startup timeout count */
/** @brief OSC_STARTUP_TIMEOUT 宏定义 */
#define OSC_STARTUP_TIMEOUT ((uint32_t)0x000FFFFFU)
/** @brief LXTAL_STARTUP_TIMEOUT 宏定义 */
#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x03FFFFFFU)
/* ADC clock prescaler offset */
/** @brief RCU_ADC_PSC_OFFSET 宏定义 */
#define RCU_ADC_PSC_OFFSET ((uint32_t)14U)
/* RCU IRC8M adjust value mask and offset*/
/** @brief RCU_IRC8M_ADJUST_MASK 宏定义 */
#define RCU_IRC8M_ADJUST_MASK ((uint8_t)0x1FU)
/** @brief RCU_IRC8M_ADJUST_OFFSET 宏定义 */
#define RCU_IRC8M_ADJUST_OFFSET ((uint32_t)3U)
/* RCU PLL1 clock multiplication factor offset */
/** @brief RCU_CFG1_PLL1MF_OFFSET 宏定义 */
#define RCU_CFG1_PLL1MF_OFFSET ((uint32_t)8U)
/* RCU PREDV1 division factor offset*/
/** @brief RCU_CFG1_PREDV1_OFFSET 宏定义 */
#define RCU_CFG1_PREDV1_OFFSET ((uint32_t)4U)
/*!
@@ -115,6 +125,11 @@ void rcu_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_clock_enable 函数
* @param[in] periph 参数
* @return void
*/
void rcu_periph_clock_enable(rcu_periph_enum periph)
{
RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));
@@ -146,6 +161,11 @@ void rcu_periph_clock_enable(rcu_periph_enum periph)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_clock_disable 函数
* @param[in] periph 参数
* @return void
*/
void rcu_periph_clock_disable(rcu_periph_enum periph)
{
RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph));
@@ -201,6 +221,11 @@ void rcu_periph_clock_sleep_disable(rcu_periph_sleep_enum periph)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_reset_enable 函数
* @param[in] periph_reset 参数
* @return void
*/
void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset)
{
RCU_REG_VAL(periph_reset) |= BIT(RCU_BIT_POS(periph_reset));
@@ -228,6 +253,11 @@ void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_reset_disable 函数
* @param[in] periph_reset 参数
* @return void
*/
void rcu_periph_reset_disable(rcu_periph_reset_enum periph_reset)
{
RCU_REG_VAL(periph_reset) &= ~BIT(RCU_BIT_POS(periph_reset));
@@ -371,6 +401,11 @@ void rcu_apb2_clock_config(uint32_t ck_apb2)
\param[out] none
\retval none
*/
/**
* @brief rcu_ckout0_config 函数
* @param[in] ckout0_src 参数
* @return void
*/
void rcu_ckout0_config(uint32_t ckout0_src)
{
uint32_t reg;
@@ -520,6 +555,11 @@ void rcu_pll2_config(uint32_t pll_mul)
\param[out] none
\retval none
*/
/**
* @brief rcu_adc_clock_config 函数
* @param[in] adc_psc 参数
* @return void
*/
void rcu_adc_clock_config(uint32_t adc_psc)
{
uint32_t reg0,reg1;
@@ -577,6 +617,11 @@ void rcu_adc_clock_config(uint32_t adc_psc)
\param[out] none
\retval none
*/
/**
* @brief rcu_usb_clock_config 函数
* @param[in] usb_psc 参数
* @return void
*/
void rcu_usb_clock_config(uint32_t usb_psc)
{
uint32_t reg;
@@ -688,6 +733,11 @@ void rcu_ck48m_clock_config(uint32_t ck48m_clock_source)
\param[out] none
\retval none
*/
/**
* @brief rcu_flag_get 函数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus rcu_flag_get(rcu_flag_enum flag)
{
FlagStatus rcu_flag = RESET;
@@ -729,6 +779,11 @@ void rcu_all_reset_flag_clear(void)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief rcu_interrupt_flag_get 函数
* @param[in] int_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag)
{
FlagStatus rcu_int_flag = RESET;
@@ -759,6 +814,11 @@ FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag)
\param[out] none
\retval none
*/
/**
* @brief rcu_interrupt_flag_clear 函数
* @param[in] int_flag_clear 参数
* @return void
*/
void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag_clear)
{
RCU_REG_VAL(int_flag_clear) |= BIT(RCU_BIT_POS(int_flag_clear));
@@ -779,6 +839,11 @@ void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag_clear)
\param[out] none
\retval none
*/
/**
* @brief rcu_interrupt_enable 函数
* @param[in] stab_int 参数
* @return void
*/
void rcu_interrupt_enable(rcu_int_enum stab_int)
{
RCU_REG_VAL(stab_int) |= BIT(RCU_BIT_POS(stab_int));
@@ -799,6 +864,11 @@ void rcu_interrupt_enable(rcu_int_enum stab_int)
\param[out] none
\retval none
*/
/**
* @brief rcu_interrupt_disable 函数
* @param[in] stab_int 参数
* @return void
*/
void rcu_interrupt_disable(rcu_int_enum stab_int)
{
RCU_REG_VAL(stab_int) &= ~BIT(RCU_BIT_POS(stab_int));
@@ -841,6 +911,11 @@ void rcu_lxtal_drive_capability_config(uint32_t lxtal_dricap)
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief rcu_osci_stab_wait 函数
* @param[in] osci 参数
* @return ErrStatus 返回值
*/
ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci)
{
uint32_t stb_cnt = 0U;
@@ -974,6 +1049,11 @@ ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci)
\param[out] none
\retval none
*/
/**
* @brief rcu_osci_on 函数
* @param[in] osci 参数
* @return void
*/
void rcu_osci_on(rcu_osci_type_enum osci)
{
RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci));
@@ -994,6 +1074,11 @@ void rcu_osci_on(rcu_osci_type_enum osci)
\param[out] none
\retval none
*/
/**
* @brief rcu_osci_off 函数
* @param[in] osci 参数
* @return void
*/
void rcu_osci_off(rcu_osci_type_enum osci)
{
RCU_REG_VAL(osci) &= ~BIT(RCU_BIT_POS(osci));
@@ -35,10 +35,13 @@ OF SUCH DAMAGE.
#include "gd32c10x_rtc.h"
/* RTC register high / low bits mask */
/** @brief RTC_HIGH_BITS_MASK 宏定义 */
#define RTC_HIGH_BITS_MASK ((uint32_t)0x000F0000U) /* RTC high bits mask */
/** @brief RTC_LOW_BITS_MASK 宏定义 */
#define RTC_LOW_BITS_MASK ((uint32_t)0x0000FFFFU) /* RTC low bits mask */
/* RTC register high bits offset */
/** @brief RTC_HIGH_BITS_OFFSET 宏定义 */
#define RTC_HIGH_BITS_OFFSET ((uint32_t)16U)
/*!
@@ -34,21 +34,31 @@ OF SUCH DAMAGE.
#include "gd32c10x_spi.h"
/** @brief SPI_ERROR_HANDLE 宏定义 */
#define SPI_ERROR_HANDLE(s) do{}while(1)
/* SPI/I2S parameter initialization mask */
/** @brief SPI_INIT_MASK 宏定义 */
#define SPI_INIT_MASK ((uint32_t)0x00003040U) /* SPI parameter initialization mask */
/** @brief I2S_INIT_MASK 宏定义 */
#define I2S_INIT_MASK ((uint32_t)0x0000F047U) /* I2S parameter initialization mask */
/* I2S clock source selection, multiplication and division mask */
/** @brief I2S1_CLOCK_SEL 宏定义 */
#define I2S1_CLOCK_SEL ((uint32_t)0x00020000U) /* I2S1 clock source selection */
/** @brief I2S2_CLOCK_SEL 宏定义 */
#define I2S2_CLOCK_SEL ((uint32_t)0x00040000U) /* I2S2 clock source selection */
/** @brief I2S_CLOCK_MUL_MASK 宏定义 */
#define I2S_CLOCK_MUL_MASK ((uint32_t)0x0000F000U) /* I2S clock multiplication mask */
/** @brief I2S_CLOCK_DIV_MASK 宏定义 */
#define I2S_CLOCK_DIV_MASK ((uint32_t)0x000000F0U) /* I2S clock division mask */
/* default value and offset */
/** @brief SPI_I2SPSC_DEFAULT_VALUE 宏定义 */
#define SPI_I2SPSC_DEFAULT_VALUE ((uint32_t)0x00000002U) /* default value of SPI_I2SPSC register */
/** @brief RCU_CFG1_PREDV1_OFFSET 宏定义 */
#define RCU_CFG1_PREDV1_OFFSET 4U /* PREDV1 offset in RCU_CFG1 */
/** @brief RCU_CFG1_PLL2MF_OFFSET 宏定义 */
#define RCU_CFG1_PLL2MF_OFFSET 12U /* PLL2MF offset in RCU_CFG1 */
/*!
@@ -115,6 +125,12 @@ void spi_struct_para_init(spi_parameter_struct* spi_struct)
\param[out] none
\retval none
*/
/**
* @brief spi_init 函数
* @param[in] spi_periph 参数
* @param[in] spi_struct 参数
* @return void
*/
void spi_init(uint32_t spi_periph, spi_parameter_struct* spi_struct)
{
uint32_t reg = 0U;
@@ -187,6 +203,14 @@ void spi_disable(uint32_t spi_periph)
\param[out] none
\retval none
*/
/**
* @brief i2s_init 函数
* @param[in] spi_periph 参数
* @param[in] mode 参数
* @param[in] standard 参数
* @param[in] ckpl 参数
* @return void
*/
void i2s_init(uint32_t spi_periph, uint32_t mode, uint32_t standard, uint32_t ckpl)
{
uint32_t reg = 0U;
@@ -233,6 +257,14 @@ void i2s_init(uint32_t spi_periph, uint32_t mode, uint32_t standard, uint32_t ck
\param[out] none
\retval none
*/
/**
* @brief i2s_psc_config 函数
* @param[in] spi_periph 参数
* @param[in] audiosample 参数
* @param[in] frameformat 参数
* @param[in] mckout 参数
* @return void
*/
void i2s_psc_config(uint32_t spi_periph, uint32_t audiosample, uint32_t frameformat, uint32_t mckout)
{
uint32_t i2sdiv = 2U, i2sof = 0U;
@@ -752,6 +784,12 @@ void spi_i2s_interrupt_disable(uint32_t spi_periph, uint8_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief spi_i2s_interrupt_flag_get 函数
* @param[in] spi_periph 参数
* @param[in] interrupt 参数
* @return FlagStatus 返回值
*/
FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt)
{
FlagStatus status = RESET;
@@ -829,6 +867,12 @@ FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief spi_i2s_flag_get 函数
* @param[in] spi_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus spi_i2s_flag_get(uint32_t spi_periph, uint32_t flag)
{
FlagStatus status = RESET;
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_timer.h"
/* TIMER init parameter mask */
/** @brief ALIGNEDMODE_MASK 宏定义 */
#define ALIGNEDMODE_MASK ((uint32_t)0x00000060U) /*!< TIMER init parameter aligne dmode mask */
#define COUNTERDIRECTION_MASK ((uint32_t)0x00000010U) /*!< TIMER init parameter counter direction mask */
#define CLOCKDIVISION_MASK ((uint32_t)0x00000300U) /*!< TIMER init parameter clock division value mask */
@@ -154,6 +155,12 @@ void timer_struct_para_init(timer_parameter_struct* initpara)
\param[out] none
\retval none
*/
/**
* @brief timer_init 函数
* @param[in] timer_periph 参数
* @param[in] initpara 参数
* @return void
*/
void timer_init(uint32_t timer_periph, timer_parameter_struct* initpara)
{
/* configure the counter prescaler value */
@@ -431,6 +438,12 @@ void timer_update_source_config(uint32_t timer_periph, uint32_t update)
\param[out] none
\retval none
*/
/**
* @brief timer_dma_enable 函数
* @param[in] timer_periph 参数
* @param[in] dma 参数
* @return void
*/
void timer_dma_enable(uint32_t timer_periph, uint16_t dma)
{
TIMER_DMAINTEN(timer_periph) |= (uint32_t) dma;
@@ -451,6 +464,12 @@ void timer_dma_enable(uint32_t timer_periph, uint16_t dma)
\param[out] none
\retval none
*/
/**
* @brief timer_dma_disable 函数
* @param[in] timer_periph 参数
* @param[in] dma 参数
* @return void
*/
void timer_dma_disable(uint32_t timer_periph, uint16_t dma)
{
TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)(dma));
@@ -507,6 +526,13 @@ void timer_channel_dma_request_source_select(uint32_t timer_periph, uint32_t dma
\param[out] none
\retval none
*/
/**
* @brief timer_dma_transfer_config 函数
* @param[in] timer_periph 参数
* @param[in] dma_baseaddr 参数
* @param[in] dma_lenth 参数
* @return void
*/
void timer_dma_transfer_config(uint32_t timer_periph, uint32_t dma_baseaddr, uint32_t dma_lenth)
{
TIMER_DMACFG(timer_periph) &= (~(uint32_t)(TIMER_DMACFG_DMATA | TIMER_DMACFG_DMATC));
@@ -529,6 +555,12 @@ void timer_dma_transfer_config(uint32_t timer_periph, uint32_t dma_baseaddr, uin
\param[out] none
\retval none
*/
/**
* @brief timer_event_software_generate 函数
* @param[in] timer_periph 参数
* @param[in] event 参数
* @return void
*/
void timer_event_software_generate(uint32_t timer_periph, uint16_t event)
{
TIMER_SWEVG(timer_periph) |= (uint32_t)event;
@@ -566,6 +598,12 @@ void timer_break_struct_para_init(timer_break_parameter_struct* breakpara)
\param[out] none
\retval none
*/
/**
* @brief timer_break_config 函数
* @param[in] timer_periph 参数
* @param[in] breakpara 参数
* @return void
*/
void timer_break_config(uint32_t timer_periph, timer_break_parameter_struct* breakpara)
{
TIMER_CCHP(timer_periph) = (uint32_t)(((uint32_t)(breakpara->runoffstate)) |
@@ -710,6 +748,13 @@ void timer_channel_output_struct_para_init(timer_oc_parameter_struct* ocpara)
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocpara 参数
* @return void
*/
void timer_channel_output_config(uint32_t timer_periph, uint16_t channel, timer_oc_parameter_struct* ocpara)
{
switch(channel){
@@ -852,6 +897,13 @@ void timer_channel_output_config(uint32_t timer_periph, uint16_t channel, timer_
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_mode_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocmode 参数
* @return void
*/
void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel, uint16_t ocmode)
{
switch(channel){
@@ -893,6 +945,13 @@ void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel, u
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_pulse_value_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] pulse 参数
* @return void
*/
void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse)
{
switch(channel){
@@ -933,6 +992,13 @@ void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t cha
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_shadow_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocshadow 参数
* @return void
*/
void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel, uint16_t ocshadow)
{
switch(channel){
@@ -977,6 +1043,13 @@ void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel,
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_fast_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocfast 参数
* @return void
*/
void timer_channel_output_fast_config(uint32_t timer_periph, uint16_t channel, uint16_t ocfast)
{
switch(channel){
@@ -1021,6 +1094,13 @@ void timer_channel_output_fast_config(uint32_t timer_periph, uint16_t channel, u
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_clear_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] occlear 参数
* @return void
*/
void timer_channel_output_clear_config(uint32_t timer_periph, uint16_t channel, uint16_t occlear)
{
switch(channel){
@@ -1065,6 +1145,13 @@ void timer_channel_output_clear_config(uint32_t timer_periph, uint16_t channel,
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_polarity_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocpolarity 参数
* @return void
*/
void timer_channel_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocpolarity)
{
switch(channel){
@@ -1108,6 +1195,13 @@ void timer_channel_output_polarity_config(uint32_t timer_periph, uint16_t channe
\param[out] none
\retval none
*/
/**
* @brief timer_channel_complementary_output_polarity_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocnpolarity 参数
* @return void
*/
void timer_channel_complementary_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnpolarity)
{
switch(channel){
@@ -1147,6 +1241,13 @@ void timer_channel_complementary_output_polarity_config(uint32_t timer_periph, u
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_state_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] state 参数
* @return void
*/
void timer_channel_output_state_config(uint32_t timer_periph, uint16_t channel, uint32_t state)
{
switch(channel){
@@ -1190,6 +1291,13 @@ void timer_channel_output_state_config(uint32_t timer_periph, uint16_t channel,
\param[out] none
\retval none
*/
/**
* @brief timer_channel_complementary_output_state_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocnstate 参数
* @return void
*/
void timer_channel_complementary_output_state_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnstate)
{
switch(channel){
@@ -1248,6 +1356,13 @@ void timer_channel_input_struct_para_init(timer_ic_parameter_struct* icpara)
\param[out] none
\retval none
*/
/**
* @brief timer_input_capture_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] icpara 参数
* @return void
*/
void timer_input_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpara)
{
switch(channel){
@@ -1353,6 +1468,13 @@ void timer_input_capture_config(uint32_t timer_periph, uint16_t channel, timer_i
\param[out] none
\retval none
*/
/**
* @brief timer_channel_input_capture_prescaler_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] prescaler 参数
* @return void
*/
void timer_channel_input_capture_prescaler_config(uint32_t timer_periph, uint16_t channel, uint16_t prescaler)
{
switch(channel){
@@ -1436,6 +1558,13 @@ uint32_t timer_channel_capture_value_register_read(uint32_t timer_periph, uint16
\param[out] none
\retval none
*/
/**
* @brief timer_input_pwm_capture_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] icpwm 参数
* @return void
*/
void timer_input_pwm_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpwm)
{
uint16_t icpolarity = 0x0U;
@@ -1570,6 +1699,12 @@ void timer_hall_mode_config(uint32_t timer_periph, uint32_t hallmode)
\param[out] none
\retval none
*/
/**
* @brief timer_input_trigger_source_select 函数
* @param[in] timer_periph 参数
* @param[in] intrigger 参数
* @return void
*/
void timer_input_trigger_source_select(uint32_t timer_periph, uint32_t intrigger)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_TRGS);
@@ -1592,6 +1727,12 @@ void timer_input_trigger_source_select(uint32_t timer_periph, uint32_t intrigger
\param[out] none
\retval none
*/
/**
* @brief timer_master_output_trigger_source_select 函数
* @param[in] timer_periph 参数
* @param[in] outrigger 参数
* @return void
*/
void timer_master_output_trigger_source_select(uint32_t timer_periph, uint32_t outrigger)
{
TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_MMC);
@@ -1615,6 +1756,12 @@ void timer_master_output_trigger_source_select(uint32_t timer_periph, uint32_t o
\retval none
*/
/**
* @brief timer_slave_mode_select 函数
* @param[in] timer_periph 参数
* @param[in] slavemode 参数
* @return void
*/
void timer_slave_mode_select(uint32_t timer_periph, uint32_t slavemode)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC);
@@ -1659,6 +1806,14 @@ void timer_master_slave_mode_config(uint32_t timer_periph, uint32_t masterslave)
\param[out] none
\retval none
*/
/**
* @brief timer_external_trigger_config 函数
* @param[in] timer_periph 参数
* @param[in] extprescaler 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_trigger_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)(TIMER_SMCFG_ETP | TIMER_SMCFG_ETPSC | TIMER_SMCFG_ETFC));
@@ -1685,6 +1840,14 @@ void timer_external_trigger_config(uint32_t timer_periph, uint32_t extprescaler,
\param[out] none
\retval none
*/
/**
* @brief timer_quadrature_decoder_mode_config 函数
* @param[in] timer_periph 参数
* @param[in] decomode 参数
* @param[in] ic0polarity 参数
* @param[in] ic1polarity 参数
* @return void
*/
void timer_quadrature_decoder_mode_config(uint32_t timer_periph, uint32_t decomode, uint16_t ic0polarity, uint16_t ic1polarity)
{
/* configure the quadrature decoder mode */
@@ -1745,6 +1908,14 @@ void timer_internal_trigger_as_external_clock_config(uint32_t timer_periph, uint
\param[out] none
\retval none
*/
/**
* @brief timer_external_trigger_as_external_clock_config 函数
* @param[in] timer_periph 参数
* @param[in] extrigger 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_trigger_as_external_clock_config(uint32_t timer_periph, uint32_t extrigger, uint16_t extpolarity, uint32_t extfilter)
{
if(TIMER_SMCFG_TRGSEL_CI1FE1 == extrigger){
@@ -1807,6 +1978,14 @@ void timer_external_trigger_as_external_clock_config(uint32_t timer_periph, uint
\param[out] none
\retval none
*/
/**
* @brief timer_external_clock_mode0_config 函数
* @param[in] timer_periph 参数
* @param[in] extprescaler 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_clock_mode0_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter)
{
/* configure TIMER external trigger input */
@@ -1834,6 +2013,14 @@ void timer_external_clock_mode0_config(uint32_t timer_periph, uint32_t extpresca
\param[out] none
\retval none
*/
/**
* @brief timer_external_clock_mode1_config 函数
* @param[in] timer_periph 参数
* @param[in] extprescaler 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_clock_mode1_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter)
{
/* configure TIMER external trigger input */
@@ -1910,6 +2097,12 @@ void timer_output_value_selection_config(uint32_t timer_periph, uint16_t outsel)
\param[out] none
\retval none
*/
/**
* @brief timer_interrupt_enable 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt)
{
TIMER_DMAINTEN(timer_periph) |= (uint32_t) interrupt;
@@ -1931,6 +2124,12 @@ void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief timer_interrupt_disable 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void timer_interrupt_disable(uint32_t timer_periph, uint32_t interrupt)
{
TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)interrupt);
@@ -1952,6 +2151,12 @@ void timer_interrupt_disable(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief timer_interrupt_flag_get 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return FlagStatus 返回值
*/
FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t interrupt)
{
uint32_t val;
@@ -1981,6 +2186,12 @@ FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief timer_interrupt_flag_clear 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void timer_interrupt_flag_clear(uint32_t timer_periph, uint32_t interrupt)
{
TIMER_INTF(timer_periph) = (~(uint32_t)interrupt);
@@ -2006,6 +2217,12 @@ void timer_interrupt_flag_clear(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief timer_flag_get 函数
* @param[in] timer_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus timer_flag_get(uint32_t timer_periph, uint32_t flag)
{
FlagStatus ret;
@@ -2037,6 +2254,12 @@ FlagStatus timer_flag_get(uint32_t timer_periph, uint32_t flag)
\param[out] none
\retval none
*/
/**
* @brief timer_flag_clear 函数
* @param[in] timer_periph 参数
* @param[in] flag 参数
* @return void
*/
void timer_flag_clear(uint32_t timer_periph, uint32_t flag)
{
TIMER_INTF(timer_periph) = (~(uint32_t)flag);
@@ -35,8 +35,11 @@ OF SUCH DAMAGE.
#include "gd32c10x_usart.h"
/* USART register bit offset */
/** @brief GP_GUAT_OFFSET 宏定义 */
#define GP_GUAT_OFFSET ((uint32_t)8U) /* bit offset of GUAT in USART_GP */
/** @brief CTL3_SCRTNUM_OFFSET 宏定义 */
#define CTL3_SCRTNUM_OFFSET ((uint32_t)1U) /* bit offset of SCRTNUM in USART_CTL3 */
/** @brief RT_BL_OFFSET 宏定义 */
#define RT_BL_OFFSET ((uint32_t)24U) /* bit offset of BL in USART_RT */
/*!
@@ -261,6 +264,12 @@ void usart_data_first_config(uint32_t usart_periph, uint32_t msbf)
\param[out] none
\retval none
*/
/**
* @brief usart_invert_config 函数
* @param[in] usart_periph 参数
* @param[in] invertpara 参数
* @return void
*/
void usart_invert_config(uint32_t usart_periph, usart_invert_enum invertpara)
{
/* inverted or not the specified signal */
@@ -514,6 +523,14 @@ void usart_synchronous_clock_disable(uint32_t usart_periph)
\param[out] none
\retval none
*/
/**
* @brief usart_synchronous_clock_config 函数
* @param[in] usart_periph 参数
* @param[in] clen 参数
* @param[in] cph 参数
* @param[in] cpl 参数
* @return void
*/
void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl)
{
USART_CTL1(usart_periph) &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL);
@@ -755,6 +772,12 @@ void usart_hardware_flow_coherence_config(uint32_t usart_periph, uint32_t hcm)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief usart_flag_get 函数
* @param[in] usart_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag)
{
FlagStatus ret;
@@ -782,6 +805,12 @@ FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief usart_flag_clear 函数
* @param[in] usart_periph 参数
* @param[in] flag 参数
* @return void
*/
void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag)
{
if(USART_FLAG_EPERR == flag) {
@@ -809,6 +838,12 @@ void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief usart_interrupt_enable 函数
* @param[in] usart_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrupt)
{
USART_REG_VAL(usart_periph, interrupt) |= BIT(USART_BIT_POS(interrupt));
@@ -832,6 +867,12 @@ void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrup
\param[out] none
\retval none
*/
/**
* @brief usart_interrupt_disable 函数
* @param[in] usart_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interrupt)
{
USART_REG_VAL(usart_periph, interrupt) &= ~BIT(USART_BIT_POS(interrupt));
@@ -858,6 +899,12 @@ void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interru
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief usart_interrupt_flag_get 函数
* @param[in] usart_periph 参数
* @param[in] int_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag)
{
uint32_t intenable = 0U, flagstatus = 0U;
@@ -889,6 +936,12 @@ FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_
\param[out] none
\retval none
*/
/**
* @brief usart_interrupt_flag_clear 函数
* @param[in] usart_periph 参数
* @param[in] int_flag 参数
* @return void
*/
void usart_interrupt_flag_clear(uint32_t usart_periph, usart_interrupt_flag_enum int_flag)
{
USART_REG_VAL2(usart_periph, int_flag) = ~BIT(USART_BIT_POS2(int_flag));
@@ -81,6 +81,13 @@ void wwdgt_counter_update(uint16_t counter_value)
\param[out] none
\retval none
*/
/**
* @brief wwdgt_config 函数
* @param[in] counter 参数
* @param[in] window 参数
* @param[in] prescaler 参数
* @return void
*/
void wwdgt_config(uint16_t counter, uint16_t window, uint32_t prescaler)
{
WWDGT_CTL = (uint32_t)(CTL_CNT(counter));
@@ -1,3 +1,9 @@
/**
* @file RTE/Device/GD32C103CB/regions_GD32C103CB.h
* @brief GD32 RTE 外设库或运行时配置源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef REGIONS_GD32C103CB_H
#define REGIONS_GD32C103CB_H
@@ -42,6 +42,7 @@
#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */
/* Vector Table base offset */
/** @brief APP_BASE 宏定义 */
#define APP_BASE ((uint32_t)0x08012000U) /*!< main FLASH base address */
#define VECT_TAB_OFFSET (uint32_t)APP_BASE-FLASH_BASE /* This value must be a multiple of 0x200. */
@@ -62,6 +63,7 @@
/* The following is to prevent Vcore fluctuations caused by frequency switching.
It is strongly recommended to include it to avoid issues caused by self-removal. */
/** @brief RCU_MODIFY_4 宏定义 */
#define RCU_MODIFY_4(__delay) do{ \
volatile uint32_t i, reg; \
if(0 != __delay){ \
@@ -103,49 +105,101 @@
} \
}while(0)
/** @brief SEL_IRC8M 宏定义 */
#define SEL_IRC8M 0x00U
/** @brief SEL_HXTAL 宏定义 */
#define SEL_HXTAL 0x01U
/** @brief SEL_PLL 宏定义 */
#define SEL_PLL 0x02U
/* set the system clock frequency and declare the system clock configuration function */
#ifdef __SYSTEM_CLOCK_IRC8M
uint32_t SystemCoreClock = __SYSTEM_CLOCK_IRC8M;
/**
* @brief system_clock_8m_irc8m 函数
* @return void
*/
static void system_clock_8m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_IRC8M;
/**
* @brief system_clock_48m_irc8m 函数
* @return void
*/
static void system_clock_48m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M;
/**
* @brief system_clock_72m_irc8m 函数
* @return void
*/
static void system_clock_72m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_IRC8M;
/**
* @brief system_clock_108m_irc8m 函数
* @return void
*/
static void system_clock_108m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_IRC8M;
/**
* @brief system_clock_120m_irc8m 函数
* @return void
*/
static void system_clock_120m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL;
/**
* @brief system_clock_hxtal 函数
* @return void
*/
static void system_clock_hxtal(void);
#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_HXTAL;
/**
* @brief system_clock_48m_hxtal 函数
* @return void
*/
static void system_clock_48m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL;
/**
* @brief system_clock_72m_hxtal 函数
* @return void
*/
static void system_clock_72m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_HXTAL;
/**
* @brief system_clock_108m_hxtal 函数
* @return void
*/
static void system_clock_108m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_HXTAL;
/**
* @brief system_clock_120m_hxtal 函数
* @return void
*/
static void system_clock_120m_hxtal(void);
#endif /* __SYSTEM_CLOCK_IRC8M */
/* configure the system clock */
/**
* @brief system_clock_config 函数
* @return void
*/
static void system_clock_config(void);
/* software delay to prevent the impact of Vcore fluctuations.
It is strongly recommended to include it to avoid issues caused by self-removal. */
/**
* @brief _soft_delay_ 函数
* @param[in] time 参数
* @return void
*/
static void _soft_delay_(uint32_t time)
{
__IO uint32_t i;
+19 -19
View File
@@ -1,6 +1,6 @@
/*
* CSOLUTION generated file: DO NOT EDIT!
* Generated by: csolution version 2.13.0+p3-g40b6564e
* Generated by: csolution version 2.12.0
*
* Project: 'Robot_V2.Debug+GD32C103CB'
* Target: 'Debug+GD32C103CB'
@@ -15,41 +15,41 @@
*/
#define CMSIS_device_header "gd32c10x.h"
/* GigaDevice::Device:GD32C10x_StdPeripherals:ADC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:ADC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_ADC
/* GigaDevice::Device:GD32C10x_StdPeripherals:BKP@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:BKP@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_BKP
/* GigaDevice::Device:GD32C10x_StdPeripherals:CAN@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:CAN@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_CAN
/* GigaDevice::Device:GD32C10x_StdPeripherals:CRC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:CRC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_CRC
/* GigaDevice::Device:GD32C10x_StdPeripherals:DBG@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:DBG@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_DBG
/* GigaDevice::Device:GD32C10x_StdPeripherals:DMA@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:DMA@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_DMA
/* GigaDevice::Device:GD32C10x_StdPeripherals:EXTI@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:EXTI@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_EXTI
/* GigaDevice::Device:GD32C10x_StdPeripherals:FMC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:FMC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_FMC
/* GigaDevice::Device:GD32C10x_StdPeripherals:FWDGT@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:FWDGT@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_FWDGT
/* GigaDevice::Device:GD32C10x_StdPeripherals:GPIO@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:GPIO@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_GPIO
/* GigaDevice::Device:GD32C10x_StdPeripherals:MISC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:MISC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_MISC
/* GigaDevice::Device:GD32C10x_StdPeripherals:PMU@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:PMU@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_PMU
/* GigaDevice::Device:GD32C10x_StdPeripherals:RCU@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:RCU@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_RCU
/* GigaDevice::Device:GD32C10x_StdPeripherals:RTC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:RTC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_RTC
/* GigaDevice::Device:GD32C10x_StdPeripherals:SPI_I2S@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:SPI_I2S@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_SPI
/* GigaDevice::Device:GD32C10x_StdPeripherals:TIMER@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:TIMER@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_TIMER
/* GigaDevice::Device:GD32C10x_StdPeripherals:USART@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:USART@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_USART
/* GigaDevice::Device:GD32C10x_StdPeripherals:WWDGT@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:WWDGT@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_WWDGT
+4 -1
View File
@@ -4,6 +4,8 @@ project:
files:
- file: main.h
- file: main.cpp
- file: tasks.h
- file: tasks.cpp
- group: sys
files:
- file: sys/systick.cpp
@@ -12,6 +14,8 @@ project:
- file: sys/initial.h
- file: sys/health.h
- file: sys/health.cpp
- file: sys/run_inhibit.h
- file: sys/run_inhibit.cpp
- file: sys/flash.h
- file: sys/flash.cpp
- file: sys/config.h
@@ -25,7 +29,6 @@ project:
- file: device/adc.h
- file: device/Timer.cpp
- file: device/Timer.h
- file: device/test.cpp
- file: device/motor.cpp
- file: device/motor.h
- group: Algorithm
+323 -99
View File
@@ -1,3 +1,9 @@
/**
* @file device/ADC.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "adc.h"
#include "Can.h"
#include "FOC.h"
@@ -7,103 +13,243 @@
#include "math.h"
#include "systick.h"
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <gd32c10x_dma.h>
// ADC 换算常量均为编译期常量,非可变全局状态。
// 用于 ADC 原始值到电压、温度、电流的固定比例换算。
/** @brief ADC_SCALE 常量定义 */
constexpr float ADC_SCALE = 3.3f / 4096.0f;
/** @brief TEMP_FACTOR 常量定义 */
constexpr float TEMP_FACTOR = 1000.0f / 4.3f;
/** @brief VCC_FACTOR 常量定义 */
constexpr float VCC_FACTOR = 11.0f;
#if GATE_DRIVER_DRV8323
/** @brief CURRENT_FACTOR 常量定义(新板:DRV8323 片上 CSA)
* SOx = VREF/2 ± I×R×G(SPI 已配置 VREF_DIV=1、GAIN 见 config.h)。
* ADC_SCALE 输出为伏特,故系数单位为 mA/V = 1000/(G×R):
* G=10V/V、R=10mΩ → 10000 mA/V,量程约 ±16.5A。
* 修改 config.h 的 DRV8323_CSA_GAIN_VV 后此系数自动跟随。 */
constexpr float CURRENT_FACTOR =
1000.0f / (DRV8323_CSA_GAIN_VV * DRV8323_SHUNT_OHM);
#elif CURRENT_SENSE_INA240
/** @brief CURRENT_FACTOR 常量定义(低边采样板:下桥臂 10mΩ + INA240A2)
* 电流链路单位为安培:get_current() 返回 A,cal_Iq_Id/Can 上报处 ×1000 转 mA。
* OUT = 1.65V ± I×R×G(REF1=AGND、REF2=A3V3,芯片内部分压取中点 1.65V),
* G=50V/V、R=10mΩ → 0.5V/A → 2.0 A/V,ADC 3.3V 满量程对应约 ±3.3A。
* 与老板外置放大链(50倍×10mΩ)标定数值恰好一致;换增益/阻值时此系数
* 随 config.h 的 INA240_CSA_GAIN_VV / INA240_SHUNT_OHM 自动跟随。 */
constexpr float CURRENT_FACTOR =
1.0f / (INA240_CSA_GAIN_VV * INA240_SHUNT_OHM);
#else
/** @brief CURRENT_FACTOR 常量定义(老板:外置放大链,保持原标定) */
constexpr float CURRENT_FACTOR =
1.0f / 50.0f * 100.0f; // 50倍放大,100倍电流采样电阻, 单位mA
#endif
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC;
// 全局采样器单例:系统只有一组 ADC 采样缓存,由 hardware_init() 调用 sampler.init() 初始化。
// ADC0 只跑 injected 组(PWM 同步):中断写 adc_current_value[](U/V/W)与
// adc_mcu_temp_raw(温度,挂注入序列尾部同步转换)并置位 adc_current_frame_ready。
// VBUS / DRV温度 由 ADC1 在慢速任务中软件触发轮询,与电流采样彻底隔离。
// 背景:此前温度/VBUS/DRV 放在 ADC0 regular 组连续扫描,与 injected 抢占同一 ADC。
// 温度通道提到 239.5 周期后,稳态下注入触发几乎必然打断温度采样中段:
// regular 被打断后从 rank0 重启而 DMA 计数不复位 → adc_value[] 数据错位;
// 采样电容长期挂在高内阻温度源电压上,干扰注入组首通道 → 相电流检测故障。
sampler sampler; // 采样器初始化
u16 adc_value[3] = {0}; // regular DMA: MCU温度 / DRV温度 / VBUS
volatile u16 adc_mcu_temp_raw = 0; // ADC0 injected rank3: MCU温度原始值
static volatile u16 adc_current_value[3] = {0}; // injected 同步采样: U / V / W
static volatile u8 adc_current_frame_ready = 0;
static inline float current_adc_to_ma(u16 raw_adc) {
return (raw_adc * ADC_SCALE + CURRENT_OFFSET) * CURRENT_FACTOR;
// ---- 三相电流零点配平结果(上电一次,hardware_init 在 gate_driver_bringup
// 之后调用 zero_calibrate 采集)。含义:各通道实测零点相对 CURRENT_OFFSET
// 基准的偏差(V),采样换算时逐通道扣除,吸收 REF 中点偏差、AGND/PGND 地差
// 与 INA 失调。初始 0 = 未配平(按标称零点换算)。
static float adc_zero_offset_v[3] = {0.0f, 0.0f, 0.0f};
/**
* @brief 将电流 ADC 原始值换算为电流值
* @param[in] raw_adc 注入组转换原始值
* @param[in] ch 通道号 0/1/2 = U/V/W(逐通道扣除上电配平零点偏差)
* @return float 返回值
* @note 函数名沿用历史(_to_ma),实际返回单位是安培:MIN/MAX_CURRENT(±40)
* 为安培阈值,cal_Iq_Id / CAN 上报处再 ×1000 转 mA。
*/
static inline float current_adc_to_ma(u16 raw_adc, u8 ch) {
return (raw_adc * ADC_SCALE + CURRENT_OFFSET - adc_zero_offset_v[ch]) *
CURRENT_FACTOR;
}
// ADC1 连续转换运行标志:adc1_config() 启动后置位,
// adc1_read_raw() 据此判断是否需要暂停/恢复连续模式。
static bool adc1_continuous_active = false;
/*!
\brief configure the DMA peripheral
\brief configure the ADC1 peripheral(VBUS 连续转换 / DRV温度 单次轮询)
\param[in] none
\param[out] none
\retval none
\note ADC1 独占慢速量:ch6(VBUS) 连续转换,读取零等待;ch9(DRV NTC)
由 adc1_read_raw() 按需暂停连续模式单次轮询。无 DMA、无中断,
与 ADC0 的 PWM 同步注入组互不干扰(独立外设)。
原实现每次读取都要"软件触发+死等EOC":fast_voltage_check
10kHz 调用,每次阻塞 ~1.5μs,纯浪费在等待上。
*/
void dma_config(void) {
/* enable DMA0 clock */
rcu_periph_clock_enable(RCU_DMA0);
/* ADC_DMA_channel configuration */
dma_parameter_struct dma_data_parameter;
void adc1_config(void) {
/* enable ADC1 clock(ADC 时钟分频与 ADC0 共用 RCU 配置) */
rcu_periph_clock_enable(RCU_ADC1);
/* ADC DMA_channel configuration */
dma_deinit(DMA0, DMA_CH0);
adc_deinit(ADC1);
adc_mode_config(ADC_MODE_FREE);
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
/* initialize DMA single data mode */
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_data_parameter.memory_addr = (uint32_t)(adc_value);
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_data_parameter.number = 3;
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
/* VBUS 单通道连续转换:软件触发一次后自持续 */
adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 1);
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5);
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL,
ADC0_1_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);
dma_circulation_enable(DMA0, DMA_CH0);
adc_enable(ADC1);
/* ADC校准(校准完成后再进入连续模式,避免校准期间 CONT 位参与) */
adc_calibration_enable(ADC1);
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE);
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);
/* 启动连续转换并等待首个结果,保证 DR 立即有效 */
adc_flag_clear(ADC1, ADC_FLAG_EOC);
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
}
adc_flag_clear(ADC1, ADC_FLAG_EOC);
adc1_continuous_active = true;
}
/*!
\brief configure the ADC peripheral
\brief 零等待读取最新 VBUS 转换结果
\retval u16 最近一次完成转换的原始值
\note 仅在 VBUS 连续模式运行时调用(主循环任务上下文)。
读 DR 硬件清 EOC,下一次完成转换会重新刷新 DR;两次读取之间
完成的转换被静默丢弃(F1 型 ADC 无溢出标志问题),
读到的值陈旧度 ≤1 个转换周期(41 周期 @30MHz ≈ 1.4μs)。
*/
u16 adc1_read_vbus_latest(void) {
return (u16)adc_regular_data_read(ADC1);
}
/*!
\brief ADC1 单次软件触发转换并等待完成
\param[in] channel ADC 通道号(如 ADC_CHANNEL_9)
\retval u16 转换原始值(超时返回上次 RDATA 残留值)
\note 仅在主循环任务上下文调用(所有任务由调度器串行派发,无并发,
与 fast_voltage_check / update_vbus_voltage 不会交错执行)。
单次转换约 41 周期 ≈ 1.4μs @30MHz ADC 时钟。
连续模式运行中调用时:暂停→单次转换→恢复,VBUS 流中断约 3~5μs。
*/
u16 adc1_read_raw(u8 channel) {
const bool was_continuous = adc1_continuous_active;
u16 timeout = 0;
if (was_continuous) {
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, DISABLE);
/* 先清 EOC 再等置位:连续模式下 EOC 常态置位(DR 久未读,
* 后续转换溢出丢失),直接等会观察到陈旧标志而另有转换在飞行中。
* 清除后观察到的置位必然来自"暂停瞬间在飞行中"的最后一次转换
* 完成后才置位——此时 ADC 真正静止,后续配置/触发不会再被
* 飞行中的转换干扰(SWSTART 被忽略、读到上一通道的 DR,
* 曾表现为 DRV 温度偶发出错)。超时兜底覆盖"暂停恰好落在
* 两次转换间隙"的无在飞行转换情形。 */
adc_flag_clear(ADC1, ADC_FLAG_EOC);
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
/* 超时只需覆盖一个转换周期(41 ADC 周期 ≈ 20 次循环),
* 仅"暂停恰好落在转换间隙"时才会走到超时 */
if (++timeout > 100U) break;
}
adc_flag_clear(ADC1, ADC_FLAG_EOC);
}
adc_regular_channel_config(ADC1, 0, channel, ADC_SAMPLETIME_28POINT5);
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
timeout = 0;
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
if (++timeout > 2000U) { // 硬件异常保护,避免死等
break;
}
}
u16 raw = (u16)adc_regular_data_read(ADC1);
adc_flag_clear(ADC1, ADC_FLAG_EOC);
if (was_continuous) {
/* 恢复 VBUS 连续转换 */
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_6,
ADC_SAMPLETIME_28POINT5);
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE);
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
/* 等恢复后的首个 ch6 转换完成并读走(清 EOC),
* 避免 vbus 零等待读取在接下来的 ~1.4μs 窗口内
* 读到 ch9 的残留值 */
timeout = 0;
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
if (++timeout > 2000U) break;
}
(void)adc_regular_data_read(ADC1);
}
return raw;
}
/*!
\brief configure the ADC0 peripheral(PWM 同步电流采样专用)
\param[in] none
\param[out] none
\retval none
\note ADC0 只保留 injected 组,regular 组整体移除:
此前 regular 连续扫描与 injected 触发抢占同一 ADC,注入打断后
regular 从头重启而 DMA 不复位导致数据错位,且温度长采样被打断后
采样电容残余污染注入组首通道。移除 regular 后注入组再无打断源。
*/
void adc_config(void) {
/* 电流采样引脚配模拟输入:U/V/W = PA3/PA4/PA5(ADC0_IN3/4/5)。
* 手册要求 ADC 引脚必须配 AIN——复位默认浮空输入态下数字输入缓冲器
* 挂在引脚上,静态 1.65V 中点正处于线性区,开关噪声环境下会向引脚
* 注入电荷、污染 13.5 周期短采样窗口(v1 外置放大链下凑合未暴露)。
* 顺手关掉该三脚的数字输入缓冲(低功耗&抗扰)。 */
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_2MHZ,
GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
/* enable ADC clock */
rcu_periph_clock_enable(RCU_ADC0);
/* config ADC clock */
/* config ADC clock(ADC0/ADC1 共用分频:120MHz/4 = 30MHz) */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4);
/* 复位ADC */
adc_deinit(ADC0);
/* ADC模式配置 */
adc_mode_config(ADC_MODE_FREE);
/* regular 组保留连续扫描,用于温度/VBUS 后台采样 */
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
/* 扫描模式:注入序列内 4 通道触发后自动连续转换 */
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
/* injected 组用于 PWM 同步电流采样,关闭自动串联,独立由 TIMER1 触发 */
adc_special_function_config(ADC0, ADC_INSERTED_CHANNEL_AUTO, DISABLE);
/* ADC数据右对齐配置 */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
/* 使能ADC温度传感器和内部参考电压 */
/* 使能ADC温度传感器(内部通道仅连接 ADC0 的 ch16/ch17) */
adc_tempsensor_vrefint_enable();
/* 配置ADC通道长度 */
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 3);
/* 配置ADC规则通道 */
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_16,
ADC_SAMPLETIME_41POINT5);
adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_9, ADC_SAMPLETIME_28POINT5);
adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5);
/* regular 组继续软件触发连续转换:本组只服务低速量 */
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL,
ADC0_1_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
/* injected 组改为 TIMER1 CH3 导出的 TRGO 触发,实现三相同一采样时刻锁存 */
/* injected 组:rank0=U, rank1=V, rank2=W, rank3=MCU温度,TIMER1 TRGO 触发。
* 电流通道用最短 13.5 周期采样(INA240 低阻输出足以驱动采样电容):
* 每通道 26 周期,U/V/W 的采样保持窗末端仅滞后触发点 0.45/1.32/2.18µs
* (54/158/262 个计数),三相样本整体压进下管导通窗头部,最大占空比
* ~90% 时仍全部有效;温度通道 ch16 高内阻按手册仍需 239.5 周期,挂尾部。
* 整段 26×3+252 = 330 周期 = 11µs。
* 触发点紧贴计数峰顶(见 config.h):中心对齐下 CH3 每周期匹配两次
* (上升沿 2989 / 下降沿 3011,仅差 22 计数),无论第二次触发是被 ADC
* 忽略(注入忙)还是重启序列,帧的等效采样位置都贴着峰顶——三相下管
* 同时导通窗的正中,帧率 20kHz 与控制环同频。
* 序列空闲期采样电容挂在温度通道(~1.3V),U 相(ch3 运放低阻输出)的
* 13.5 周期保持窗足以完成重建。 */
adc_channel_length_config(ADC0, ADC_INSERTED_CHANNEL, 4);
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_3, ADC_SAMPLETIME_13POINT5);
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_4, ADC_SAMPLETIME_13POINT5);
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_5, ADC_SAMPLETIME_13POINT5);
adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_16,
ADC_SAMPLETIME_239POINT5);
adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL,
ADC0_1_EXTTRIG_INSERTED_T1_TRGO);
adc_external_trigger_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE);
@@ -112,55 +258,107 @@ void adc_config(void) {
adc_enable(ADC0);
/* ADC校准 */
adc_calibration_enable(ADC0);
/* 使能ADC DMA功能 */
adc_dma_mode_enable(ADC0);
/* injected 完成中断负责搬运完整三相样本 */
/* injected 完成中断负责搬运完整三相样本与温度原始值 */
adc_interrupt_enable(ADC0, ADC_INT_EOIC);
/* 先启动 regular 组连续后台采样 */
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
}
/**
* @brief 采样类初始化
*/
u8 sampler::init() {
dma_config(); // DMA采样ADC初始化
adc_config(); // ADC初始化
adc_config(); // ADC0:PWM 同步注入组(U/V/W + MCU温度)
adc1_config(); // ADC1:VBUS / DRV温度 慢速轮询
current_sample_head = 0;
adc_current_frame_ready = 0;
delay_1ms(1);
const float current_init = current_adc_to_ma(
static_cast<u16>((-CURRENT_OFFSET / ADC_SCALE) + 0.5f));
static_cast<u16>((-CURRENT_OFFSET / ADC_SCALE) + 0.5f), 0); // 配平前按标称零点
// 初始采样
for (int i = 0; i < ADC_FILTER_SIZE; i++) {
mcu_temp_samples[i] =
(1.43f - adc_value[0] * ADC_SCALE) * TEMP_FACTOR + 35.0f;
(1.43f - adc_mcu_temp_raw * ADC_SCALE) * TEMP_FACTOR + 35.0f;
currU_samples[i] = current_init;
currV_samples[i] = current_init;
currW_samples[i] = current_init;
vbus_samples[i] = adc_value[2] * ADC_SCALE * VCC_FACTOR;
vbus_samples[i] = adc1_read_vbus_latest() * ADC_SCALE * VCC_FACTOR;
float R_now = (float)adc_value[1] / (4096 - (float)adc_value[1]) * 4700;
const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9);
float R_now = drv_raw / (4096 - drv_raw) * 4700;
drv_temp_samples[i] =
1.0f / (0.00335f + 0.00025f * log(R_now / 10000.0f)) - 273.15f;
delay_1ms(1);
}
return 0;
}
// ---- 三相电流零点配平(上电一次,hardware_init 在 gate_driver_bringup 之后调用) ----
// 必须在门驱动唤醒后进行:DRV8323 板唤醒后 INLx=DRVSLP 常高,0% 占空比下
// 三相下管导通、采样电阻经下管接地,INA240 输入有确定共模(0V),输出即
// 真实零点;若在芯片睡眠 Hi-Z 时采样,下管源极悬空,INA240 IN+ 浮空会把
// 输出拉到轨,配平值无效(老板下管常通,两种时序均有效,但统一放后面)。
// 采集 N 帧均值作为每通道零点偏差(V,相对 CURRENT_OFFSET 基准),折算
// 电流超过 CURRENT_ZERO_CAL_LIMIT 视为电流检测初始化错误。
/**
* @brief 三相电流零点配平并校验
* @return u8 0=成功;1=无采样帧(ADC/触发链路异常);
* 2=配平值超过 CURRENT_ZERO_CAL_LIMIT(电流检测初始化错误)
* @note 阻塞约 15ms(跳过 5ms 上电瞬态 + 采集 200 帧 @20kHz),仅限
* hardware_init 上下文调用(看门狗在其后才初始化,无喂狗需求)。
* main 循环尚未启动,期间 EOIC 中断只刷新 adc_current_value,
* 不会与 update_current 并发写环形缓冲。
*/
u8 sampler::zero_calibrate(void) {
delay_1ms(5); // 跳过上电瞬态(INA 建立、驱动使能稳定),≈100 帧
u32 zero_acc[3] = {0, 0, 0};
const u16 ZERO_CAL_FRAMES = 200;
for (u16 i = 0; i < ZERO_CAL_FRAMES; i++) {
u32 timeout = 0;
while (adc_current_frame_ready == 0U) {
/* 帧周期 50µs,超时覆盖 ~30ms 无帧:ADC/触发链路异常 */
if (++timeout > 600000U) {
printf("[ADC] zero cal fail: no sample frame\r\n");
return 1;
}
}
zero_acc[0] += adc_current_value[0];
zero_acc[1] += adc_current_value[1];
zero_acc[2] += adc_current_value[2];
adc_current_frame_ready = 0U;
}
u8 result = 0;
printf("[ADC] zero cal:");
for (u8 ch = 0; ch < 3U; ch++) {
/* 实测零点偏差(V)= 样本均值电压 + CURRENT_OFFSET 基准 */
adc_zero_offset_v[ch] =
((float)zero_acc[ch] / (float)ZERO_CAL_FRAMES) * ADC_SCALE +
CURRENT_OFFSET;
/* 配平值折算电流(A)超限 → 电流检测初始化错误 */
float offset_a = adc_zero_offset_v[ch] * CURRENT_FACTOR;
printf(" %c=%dmA", "UVW"[ch], (int)(offset_a * 1000.0f));
if (offset_a > CURRENT_ZERO_CAL_LIMIT ||
offset_a < -CURRENT_ZERO_CAL_LIMIT) {
result = 2;
}
}
if (result == 2) {
printf(" -> over limit, current sense init error\r\n");
} else {
printf(" ok\r\n");
}
return result;
}
/**
* @brief 获取MCU温度
* @return float
* 温度值,单位为摄氏度,范围应在-40到145之间,如果读取失败则返回0.0f
*/
float sampler::get_mcu_temp() {
tempora_sample = medium_filter(mcu_temp_samples, ADC_FILTER_SIZE);
// 检查温度读数是否在有效范围内
if (tempora_sample < MIN_TEMP or tempora_sample > MAX_TEMP) {
return ERROR_ADC_VAL;
}
return tempora_sample;
return cached_mcu_temp;
}
/**
@@ -171,12 +369,7 @@ float sampler::get_mcu_temp() {
* 如果计算结果超出有效范围(-40到255摄氏度),则重新采样计算。
*/
float sampler::get_drv_temp() {
tempora_sample = medium_filter(drv_temp_samples, ADC_FILTER_SIZE);
// 检查温度读数是否在有效范围内
if (tempora_sample < MIN_TEMP || tempora_sample > MAX_TEMP) {
return ERROR_ADC_VAL;
}
return tempora_sample;
return cached_drv_temp;
}
/**
@@ -221,13 +414,7 @@ float sampler::get_current(char type) {
* 如果计算结果超出有效范围(-40到255伏特),则重新采样计算。
*/
float sampler::get_vbus_voltage() {
tempora_sample = medium_filter(vbus_samples, ADC_FILTER_SIZE);
// 检查电压读数是否在有效范围内
const u8 mid = ADC_FILTER_SIZE / 2;
if (vbus_samples[mid] > MAX_VCC or vbus_samples[mid] < MIN_VCC) {
return ERROR_ADC_VAL;
}
return tempora_sample;
return cached_vbus;
}
/**
@@ -237,21 +424,41 @@ float sampler::get_vbus_voltage() {
* 如果计算结果超出有效范围(-40到255摄氏度),则重新采样计算。
*/
void sampler::update_mcu_temp() {
memmove(mcu_temp_samples, mcu_temp_samples + 1,
(ADC_FILTER_SIZE - 1) * sizeof(float));
mcu_temp_samples[ADC_FILTER_SIZE - 1] =
(1.43f - adc_value[0] * ADC_SCALE) * TEMP_FACTOR + 35.0f;
// 环形缓冲区写入,替代 memmove
mcu_temp_samples[mcu_temp_head] =
(1.43f - adc_mcu_temp_raw * ADC_SCALE) * TEMP_FACTOR + 35.0f;
mcu_temp_head++;
if (mcu_temp_head >= ADC_FILTER_SIZE) mcu_temp_head = 0;
// 更新缓存:中值滤波
tempora_sample = medium_filter(mcu_temp_samples, ADC_FILTER_SIZE);
if (tempora_sample < MIN_TEMP || tempora_sample > MAX_TEMP) {
cached_mcu_temp = ERROR_ADC_VAL;
} else {
cached_mcu_temp = tempora_sample;
}
}
/**
* @brief 更新MCU温度
*/
void sampler::update_drv_temp() {
memmove(drv_temp_samples, drv_temp_samples + 1,
(ADC_FILTER_SIZE - 1) * sizeof(float));
float R_now = (float)adc_value[1] / (4096 - (float)adc_value[1]) * 4700;
drv_temp_samples[ADC_FILTER_SIZE - 1] =
// ADC1 单次轮询转换 ch9(NTC 分压,低阻源)
const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9);
// 环形缓冲区写入,替代 memmove
float R_now = drv_raw / (4096 - drv_raw) * 4700;
drv_temp_samples[drv_temp_head] =
1.0f / (0.00335f + 0.00025f * log(R_now / 10000.0f)) - 273.15f;
drv_temp_head++;
if (drv_temp_head >= ADC_FILTER_SIZE) drv_temp_head = 0;
// 更新缓存:中值滤波
tempora_sample = medium_filter(drv_temp_samples, ADC_FILTER_SIZE);
if (tempora_sample < MIN_TEMP || tempora_sample > MAX_TEMP) {
cached_drv_temp = ERROR_ADC_VAL;
} else {
cached_drv_temp = tempora_sample;
}
}
/**
@@ -263,9 +470,9 @@ void sampler::update_current() {
}
const u8 idx = current_sample_head;
currU_samples[idx] = current_adc_to_ma(adc_current_value[0]);
currV_samples[idx] = current_adc_to_ma(adc_current_value[1]);
currW_samples[idx] = current_adc_to_ma(adc_current_value[2]);
currU_samples[idx] = current_adc_to_ma(adc_current_value[0], 0);
currV_samples[idx] = current_adc_to_ma(adc_current_value[1], 1);
currW_samples[idx] = current_adc_to_ma(adc_current_value[2], 2);
current_sample_head++;
if (current_sample_head >= ADC_FILTER_SIZE) {
@@ -278,17 +485,34 @@ void sampler::update_current() {
* @brief 更新供电电压
*/
void sampler::update_vbus_voltage() {
memmove(vbus_samples, vbus_samples + 1, (ADC_FILTER_SIZE - 1) * sizeof(float));
vbus_samples[ADC_FILTER_SIZE - 1] = adc_value[2] * ADC_SCALE * VCC_FACTOR;
// ADC1 连续模式零等待读最新 VBUS(ch6,电阻分压,低阻源)
// 环形缓冲区写入,替代 memmove
vbus_samples[temp_vbus_head] =
adc1_read_vbus_latest() * ADC_SCALE * VCC_FACTOR;
temp_vbus_head++;
if (temp_vbus_head >= ADC_FILTER_SIZE) temp_vbus_head = 0;
// 更新缓存:中值滤波 + 范围检查
tempora_sample = medium_filter(vbus_samples, ADC_FILTER_SIZE);
const u8 mid = ADC_FILTER_SIZE / 2;
if (vbus_samples[mid] > MAX_VCC || vbus_samples[mid] < MIN_VCC) {
cached_vbus = ERROR_ADC_VAL;
} else {
cached_vbus = tempora_sample;
}
}
/**
* @brief ADC0_1_IRQHandler 中断处理函数
* @return void
*/
extern "C" void ADC0_1_IRQHandler(void) {
if (adc_interrupt_flag_get(ADC0, ADC_INT_FLAG_EOIC) != RESET) {
/* 丢弃 rank0 dummy,真实样本使用 rank1/2/3,对应 U/V/W 同一 PWM 时刻 */
(void)adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0);
adc_current_value[0] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1);
adc_current_value[1] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_2);
adc_current_value[2] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_3);
/* rank0/1/2 = U/V/W 同一 PWM 时刻样本,rank3 = MCU温度(序列尾部) */
adc_current_value[0] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0);
adc_current_value[1] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1);
adc_current_value[2] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_2);
adc_mcu_temp_raw = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_3);
adc_current_frame_ready = 1U;
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC);
+489 -472
View File
File diff suppressed because it is too large Load Diff
+310 -6
View File
@@ -1,3 +1,9 @@
/**
* @file device/Can.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _CAN_H
#define _CAN_H
@@ -11,18 +17,70 @@ extern "C" {
#include "config.h"
// 定义一个联合体类型用于 float 和 byte 数组之间的转换
/**
* @brief 匿名联合体 联合体定义
*/
typedef union {
float f;
u8 b[4];
} float_union_t;
// 添加辅助函数用于将 u16 类型数据打包到 u8 数组中
/**
* @brief pack_u16_to_bytes 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] value 参数
* @return void
*/
static inline void pack_u16_to_bytes(u8 *buffer, u16 offset, u16 value) {
buffer[offset] = (u8)(value & 0xFF);
buffer[offset + 1] = (u8)((value >> 8) & 0xFF);
}
/**
* @brief pack_u32_to_bytes 函数(小端)
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] value 参数
* @return void
*/
static inline void pack_u32_to_bytes(u8 *buffer, u16 offset, u32 value) {
pack_u16_to_bytes(buffer, offset, (u16)(value & 0xFFFF));
pack_u16_to_bytes(buffer, offset + 2, (u16)((value >> 16) & 0xFFFF));
}
// 将 mA 电流值饱和转换为 int16 后按小端打包。
// DBC 中 U/V/W current 定义为 signed int16([-32768,32767] mA);
// 相电流有正有负,此前用 u16() 打包负值被削成 0,上位机波形下半周丢失。
/**
* @brief pack_current_s16_to_bytes 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] value_mA 参数
* @return void
*/
static inline void pack_current_s16_to_bytes(u8 *buffer, u16 offset,
float value_mA) {
short value;
if (value_mA > 32767.0f) {
value = 32767; // 饱和,避免 float→int 超范围截断环绕
} else if (value_mA < -32768.0f) {
value = -32768;
} else {
value = (short)value_mA;
}
pack_u16_to_bytes(buffer, offset, (u16)value);
}
// 添加辅助函数用于将 float 类型数据打包到 u8 数组中
/**
* @brief pack_float_to_bytes 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] float_union 参数
* @return void
*/
static inline void pack_float_to_bytes(u8 *buffer, u16 offset,
float_union_t &float_union) {
buffer[offset] = float_union.b[0];
@@ -32,18 +90,36 @@ static inline void pack_float_to_bytes(u8 *buffer, u16 offset,
}
// 添加辅助函数用于从 u8 数组中解包 short 类型数据
/**
* @brief unpack_bytes_to_short 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @return short 返回值
*/
static inline short unpack_bytes_to_short(u8 *buffer, u16 offset) {
short value = (short)(buffer[offset] | (buffer[offset + 1] << 8));
return value;
}
// 添加辅助函数用于从 u8 数组中解包 u16 类型数据
/**
* @brief unpack_bytes_to_u16 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @return u16 返回值
*/
static inline u16 unpack_bytes_to_u16(u8 *buffer, u16 offset) {
u16 value = (u16)(buffer[offset] | (buffer[offset + 1] << 8));
return value;
}
// 添加辅助函数用于从 u8 数组中解包 float 类型数据
/**
* @brief unpack_bytes_to_float 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @return float_union_t 返回值
*/
static inline float_union_t unpack_bytes_to_float(u8 *buffer, u16 offset) {
float_union_t float_union;
float_union.b[0] = buffer[offset];
@@ -53,18 +129,47 @@ static inline float_union_t unpack_bytes_to_float(u8 *buffer, u16 offset) {
return float_union;
}
/**
* @brief 初始化 CAN 引脚
* @return u8 返回值
*/
u8 can_gpio_init(void); // CAN 引脚初始化
void can_config(void); // CAN 初始化
/**
* @brief 配置 CAN FD 外设
* @return void
*/
void canfd_config(void); // CANFD 初始化
/**
* @brief 初始化 CAN DMA 通道
* @return void
*/
void can_dma_init(void); // CAN DMA 初始化
/**
* @brief CAN0 发送中断处理函数
* @return void
*/
void CAN0_TX_IRQHandler(void); // CAN 发送中断处理函数
/**
* @brief CAN0 接收中断处理函数
* @return void
*/
void CAN0_RX0_IRQHandler(void); // CAN 接收中断处理函数
/**
* @brief DMA0 通道 4 中断处理函数
* @return void
*/
void DMA0_Channel4_IRQHandler(void); // DMA 通道 4 中断处理函数 (CAN 接收)
/**
* @brief DMA0 通道 5 中断处理函数
* @return void
*/
void DMA0_Channel5_IRQHandler(void); // DMA 通道 5 中断处理函数 (CAN 发送)
/**
* @brief jumpToBOOT 函数
* @return void
*/
void jumpToBOOT();
void can_loopback_init(void);
void can_loopback_test(void);
/*!
\brief CAN 通信类
@@ -102,33 +207,168 @@ class CAN_transmiter {
float_union_t w_current_union = {0}; // W 相电流
float_union_t v_current_union = {0}; // V 相电流
bool MotorStatus = 0; // 电机状态码
u8 EEPROMStatus = 0; // EEPROM 状态码
// MotorStatus 不在此处:判据与上报都收在 health 的 1ms nFAULT 轮询里,
// 声明见 public 段(与 MagStatus/ThermalStatus 同处),避免出现第二个写手。
can_trasnmit_message_struct transmit_message; // 发送消息结构体
u8 transmit_mailbox = CAN_NOMAILBOX; // 初始化为无 mailbox 状态
u8 transmit_data[64]; // CANFD 最大 64 字节
// 周期反馈帧独立刷新率(ms,0=停发)与 1ms 节拍分频计数器。
// init() 从 flashEEP 的 RAM 副本加载,SET 命令(0x0400 扩展格式)运行时修改
u16 status_period_ms = CANFD_TX_PERIOD_STATUS_DEFAULT_MS;
u16 status_period_counter = 0;
u16 config_period_ms = CANFD_TX_PERIOD_CONFIG_DEFAULT_MS;
u16 config_period_counter = 0;
// CAN 接收消息队列
can_receive_message_struct rx_message_buffer[CAN_RX_BUFFER_SIZE];
volatile uint8_t rx_buffer_head = 0;
volatile uint8_t rx_buffer_tail = 0;
/**
* @brief 更新对象内部状态或采样数据
* @return void
*/
void update(); // 更新数据
/**
* @brief 打包并发送 CONFIG 反馈帧(0x0101,控制模式/目标设定回显)
* @return void
*/
void send_config_frame(); // CONFIG 反馈帧发送
/**
* @brief CAN_Receive 函数
* @return u8 返回值
*/
u8 CAN_Receive(); // CAN 接收
/**
* @brief CANFD_Receive 函数
* @return u8 返回值
*/
u8 CANFD_Receive(); // CANFD 接收
/**
* @brief is_rx_buffer_full 函数
* @return bool 返回值
*/
bool is_rx_buffer_full();
/**
* @brief is_rx_buffer_empty 函数
* @return bool 返回值
*/
bool is_rx_buffer_empty();
/**
* @brief dequeue_rx_message 函数
* @param[in] message 参数
* @return bool 返回值
*/
bool dequeue_rx_message(can_receive_message_struct& message);
/**
* @brief check_silent_mode 函数
* @return void
*/
void check_silent_mode(); // 检查静默模式是否结束
/**
* @brief 处理周期性发送请求,静默模式下只检查是否退出静默
* @return void
*/
void handle_periodic_send_tick();
/**
* @brief 将底层接收队列项转换为 CAN 消息并按协议分发
* @param[in] id 接收到的扩展 CAN ID
* @param[in] length 接收数据长度
* @param[in] data 接收数据指针
* @return void
*/
void handle_received_queue_item(u32 id, u8 length, const u8 *data);
/**
* @brief 处理不受本节点过滤限制的特殊消息
* @param[in,out] current_message 当前 CAN 消息
* @param[in] base_id 低 16 位基础 ID
* @param[in] sender_node_id 发送方节点 ID
* @return bool true 表示消息已处理,false 表示继续按普通节点消息处理
*/
bool handle_special_message(can_receive_message_struct &current_message,
u32 base_id, u16 sender_node_id);
/**
* @brief 处理 BOOT 命令或其他节点 BOOT 广播导致的静默逻辑
* @param[in] sender_node_id 发送方节点 ID
* @return void
*/
void handle_boot_message(u16 sender_node_id);
/**
* @brief 处理节点 ID 设置命令,发送应答并保存新 ID 到 Flash
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_set_node_id_message(can_receive_message_struct &current_message);
/**
* @brief 处理 0x0400 扩展格式参数设置(DLC=4:[选择器 u16][值 u16]),
* 选择器 0x0000=节点 ID、反馈帧基础 ID=对应帧刷新周期(ms)
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_set_param_message(can_receive_message_struct &current_message);
/**
* @brief 校验并保存新节点 ID 到 Flash(不修改应答帧)
* @param[in] new_id 新节点 ID
* @return u8 1=成功,0=非法 ID 或 Flash 写入失败
*/
u8 apply_new_node_id(u16 new_id);
/**
* @brief 设置反馈帧刷新周期并持久化到 Flash
* @param[in] base_id 反馈帧基础 ID(CANFD_STATUS_ID/CANFD_CONFIG_ID)
* @param[in] period_ms 刷新周期 ms(0=停发)
* @return u8 1=成功,0=未知帧或 Flash 写入失败
*/
u8 set_tx_period(u16 base_id, u16 period_ms);
/**
* @brief 处理已经确认发给本节点的普通协议消息
* @param[in,out] current_message 当前 CAN 消息
* @param[in] base_id 低 16 位基础 ID
* @return void
*/
void handle_node_message(can_receive_message_struct &current_message,
u32 base_id);
/**
* @brief 解析控制命令并更新 FOC 目标值和 PID 参数
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_control_command(can_receive_message_struct &current_message);
/**
* @brief 解析复位命令各 bit 位并执行对应动作
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_reset_command(can_receive_message_struct &current_message);
/**
* @brief 处理 OTA ACK 查询,APP 有效时回发确认帧
* @return void
*/
void handle_ota_ack_message();
public:
// 节点 ID 相关辅助函数 (使用高 16 位存储节点 ID)
/**
* @brief build_can_id 函数
* @param[in] base_id 参数
* @return u32 返回值
*/
u32 build_can_id(u32 base_id); // 根据节点 ID 构建 CAN ID,格式:[节点 ID(16 位)][基础 ID(16 位)]
/**
* @brief is_message_for_me 函数
* @param[in] received_id 参数
* @return bool 返回值
*/
bool is_message_for_me(u32 received_id); // 判断消息是否发给本节点
public:
// 添加构造函数声明
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init(void);
can_receive_message_struct receive_message; // 接收消息结构体
@@ -138,23 +378,87 @@ class CAN_transmiter {
bool MagStatus = 0; // 磁状态码
bool ThermalStatus = 0; // 热能状态码
// nFAULT 状态位(STATUS 帧 motStatus)。唯一写手 = health.cpp 的 1ms nFAULT
// 轮询,值与"是否允许运行"的判据同源;CAN update() 不再自行读引脚。
bool MotorStatus = 0; // 门驱动故障状态码
/**
* @brief communication_check 函数
* @return void
*/
void communication_check(); // 通信检查
/**
* @brief 打包并发送 STATUS 反馈帧(0x0100,实时状态与错误位图)。
* 不做分频与静默检查,初始化失败上报路径直接调用
* @return u8 1=已提交发送,0=发送失败
*/
u8 send_status_frame(); // STATUS 反馈帧发送
/**
* @brief CAN_Sleep 函数
* @return u8 返回值
*/
u8 CAN_Sleep(); // CAN 进入睡眠
/**
* @brief CAN_Wakeup 函数
* @return u8 返回值
*/
u8 CAN_Wakeup(); // CAN 唤醒
/**
* @brief CAN_AsyncSend 函数
* @param[in] ID 参数
* @param[in] length 参数
* @param[in] data 参数
* @return u8 返回值
*/
u8 CAN_AsyncSend(u32 ID, u8 length, u8 *data); // CAN 异步发送 (使用 DMA)
/**
* @brief CANFD_AsyncSend 函数
* @param[in] ID 参数
* @param[in] length 参数
* @param[in] data 参数
* @return u8 返回值
*/
u8 CANFD_AsyncSend(u32 ID, u8 length, u8 *data); // CANFD 异步发送 (使用 DMA)
u8 CAN_all_Send(); // CAN 发送
u8 CANFD_all_Send(); // CANFD 发送
/**
* @brief enter_silent_mode 函数
* @return void
*/
void enter_silent_mode(); // 进入静默模式(停发 10 秒)
/**
* @brief is_in_silent_mode 函数
* @return bool 返回值
*/
bool is_in_silent_mode(); // 检查是否在静默模式中
// 节点 ID 管理接口
/**
* @brief 获取 get_node_id 对应的数据或状态
* @return u16 返回值
*/
u16 get_node_id(); // 获取当前节点 ID
/**
* @brief 设置 set_node_id 对应的参数或状态
* @param[in] new_id 参数
* @return void
*/
void set_node_id(u16 new_id); // 设置节点 ID
/**
* @brief load_node_id_from_flash 函数
* @return u8 返回值
*/
u8 load_node_id_from_flash(); // 从 Flash 加载节点 ID
/**
* @brief save_node_id_to_flash 函数
* @param[in] new_id 参数
* @return u8 返回值
*/
u8 save_node_id_to_flash(u16 new_id); // 保存节点 ID 到 Flash
// 中断处理函数需要访问的公共接口
/**
* @brief enqueue_rx_message_from_isr 函数
* @param[in] message 参数
* @return void
*/
void enqueue_rx_message_from_isr(const can_receive_message_struct& message) {
if (!is_rx_buffer_full()) {
rx_message_buffer[rx_buffer_head] = message;
+40 -10
View File
@@ -1,7 +1,14 @@
/**
* @file device/Timer.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "Timer.h"
#include "Can.h"
#include "FOC.h"
#include "Timer.h"
#include "config.h"
#include "gd32c10x.h"
#include "gd32c10x_gpio.h"
#include "gd32c10x_misc.h"
@@ -9,8 +16,7 @@
#include "gd32c10x_timer.h"
#include "initial.h"
volatile u32 total_us100_counter = 0; // 总计数器,每 100 微秒增加 1
extern u16 adc_value[3];
volatile u32 total_us100_counter = 0; // TIMER2 ISR 内部 100us 调度索引
extern FOC_Controller FOC;
extern TaskScheduler scheduler;
extern CAN_transmiter CAN0_transmiter;
@@ -18,16 +24,13 @@ extern CAN_transmiter CAN0_transmiter;
extern u32 cnt_test;
// 任务调度查找表 - 每 100us 执行的任务 (1ms 周期内)
// MCU/DRV 温度保持 1kHz:热模型兼作过流保护,温度积累按 1ms 步进,
// 每毫秒都要拿到新温度采样。
static const u32 task_schedule_100us[10] = {
(1u << TASK_MCU_TEMP_UPDATE), // 0: MCU 温度更新
(1u << TASK_DRV_TEMP_UPDATE), // 1: 驱动温度更新
(1u << TASK_VBUS_UPDATE), // 2: 电压更新
0, // 3: 空闲
0, // 4: 空闲
0, // 5: 空闲
0, // 6: 空闲
0, // 7: 空闲
0, // 8: 空闲
0, 0, 0, 0, 0, 0, // 3~8: 空闲
(1u << TASK_CAN_TRANSMIT) // 9: CAN 发送
};
@@ -192,10 +195,18 @@ void timer1_gpio_config(void) {
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
#if GATE_DRIVER_DRV8323
/* 新板:PB12(DRVSLP=ENABLE+INLx) 由 gate_driver 统一管理——
* gate_driver_preinit() 已置低保持芯片睡眠,gate_driver_bringup()
* 在 SPI 配置完成后才拉高。此处严禁提前置高(SPI 未配置前芯片
* 带默认 6x PWM 模式醒来,INLx=1 时上管永远无法导通)。
* PB0 板上接地未用,不配置。 */
#else
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_0 | GPIO_PIN_12);
gpio_bit_set(GPIOB, GPIO_PIN_0);
gpio_bit_set(GPIOB, GPIO_PIN_12);
#endif
/* 配置 TIMER1 完全重映射*/
gpio_pin_remap_config(GPIO_TIMER1_FULL_REMAP, DISABLE);
@@ -255,7 +266,8 @@ void timer1_pwm_config(void) {
TIMER1_DEFAULT_PULSE);
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_2,
TIMER1_DEFAULT_PULSE);
// 设置 CH3 为固定 ADC 采样点,避免与三相 PWM duty 耦合
// 设置 CH3 为固定 ADC 采样点(低边采样:必须落在三相下管同时导通窗,
// 即中心对齐计数的峰顶附近,见 config.h TIMER1_CH3_COMPARE_VALUE 说明)
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_3,
TIMER1_CH3_COMPARE_VALUE);
@@ -290,6 +302,15 @@ void TIMER1_IRQHandler(void) {
void Set_Pwm(u16 channel, float duty_cycle) {
float pulse = duty_cycle * TIMER1_PERIOD;
#if GATE_DRIVER_DRV8323
/* 新板通道重映射:逻辑相 A→CH2、B→CH1、C→CH0(硬件 INHA/B/C
* 分别接 PWM1/2/3 = PA2/PA1/PA0),与 Set_3Pwm 保持一致 */
if (channel == 0) {
channel = 2;
} else if (channel == 2) {
channel = 0;
}
#endif
if (channel <= 3) {
timer_channel_output_pulse_value_config(TIMER1, channel, pulse);
} else {
@@ -306,8 +327,17 @@ void Set_Pwm(u16 channel, float duty_cycle) {
\note The channel parameter must be between 0 and 3 inclusive.
*/
void Set_3Pwm(float duty_cycle0, float duty_cycle1, float duty_cycle2) {
// 直接写寄存器
#if GATE_DRIVER_DRV8323
/* 新板硬件映射:CH0(PA0)→PWM3→INHC,CH1(PA1)→PWM2→INHB,
* CH2(PA2)→PWM1→INHA。入参按 (A,B,C) 相序书写,此处把 A/C 换到
* 物理通道,修正新板与老板相反的 INHx 接线。 */
TIMER_CH0CV(TIMER1) = (uint32_t)(duty_cycle2 * TIMER1_PERIOD);
TIMER_CH1CV(TIMER1) = (uint32_t)(duty_cycle1 * TIMER1_PERIOD);
TIMER_CH2CV(TIMER1) = (uint32_t)(duty_cycle0 * TIMER1_PERIOD);
#else
// 直接写寄存器(老板:CH0/1/2 = IN1/2/3 = A/B/C 相)
TIMER_CH0CV(TIMER1) = (uint32_t)(duty_cycle0 * TIMER1_PERIOD);
TIMER_CH1CV(TIMER1) = (uint32_t)(duty_cycle1 * TIMER1_PERIOD);
TIMER_CH2CV(TIMER1) = (uint32_t)(duty_cycle2 * TIMER1_PERIOD);
#endif
}
+41
View File
@@ -1,19 +1,60 @@
/**
* @file device/Timer.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _TIMER_H
#define _TIMER_H
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <cstdint>
/**
* @brief User_timer_init 函数
* @return void
*/
void User_timer_init(void);
/**
* @brief Set_Pwm 函数
* @param[in] channel 参数
* @param[in] duty_cycle 参数
* @return void
*/
void Set_Pwm(u16 channel, float duty_cycle);
/**
* @brief Set_3Pwm 函数
* @param[in] duty_cycle0 参数
* @param[in] duty_cycle1 参数
* @param[in] duty_cycle2 参数
* @return void
*/
void Set_3Pwm(float duty_cycle0, float duty_cycle1,float duty_cycle2);
/**
* @brief TIMER2 中断处理函数
* @return void
*/
void TIMER2_IRQHandler(void);
/**
* @brief timer1_pwm_config 函数
* @return void
*/
void timer1_pwm_config(void);
/**
* @brief timer1_gpio_config 函数
* @return void
*/
void timer1_gpio_config(void);
/**
* @brief TIMER1 中断处理函数
* @return void
*/
void TIMER1_IRQHandler(void); // 添加TIMER1中断处理函数声明
#ifdef __cplusplus
+88
View File
@@ -1,3 +1,9 @@
/**
* @file device/adc.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _ADC_H
#define _ADC_H
@@ -7,6 +13,9 @@ extern "C" {
#include "config.h"
/**
* @brief sampler 类或结构体
*/
class sampler {
private:
float vbus_samples[ADC_FILTER_SIZE];
@@ -16,24 +25,103 @@ class sampler {
float mcu_temp_samples[ADC_FILTER_SIZE];
float drv_temp_samples[ADC_FILTER_SIZE];
u8 current_sample_head;
u8 temp_vbus_head; // 环形缓冲区 head
u8 mcu_temp_head;
u8 drv_temp_head;
float tempora_sample;
float cached_mcu_temp; // getter 缓存
float cached_drv_temp;
float cached_vbus;
public:
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init(void);
/**
* @brief 三相电流零点配平并校验(hardware_init 在 FOC.init 之后调用)
* @return u8 0=成功;1=无采样帧(ADC/触发链路异常);
* 2=配平值超过 CURRENT_ZERO_CAL_LIMIT(电流检测初始化错误)
*/
u8 zero_calibrate(void);
/**
* @brief 更新 mcu_temp 相关状态
* @return void
*/
void update_mcu_temp(void);
/**
* @brief 更新 drv_temp 相关状态
* @return void
*/
void update_drv_temp(void);
/**
* @brief 更新 vbus_voltage 相关状态
* @return void
*/
void update_vbus_voltage(void);
/**
* @brief 更新 current 相关状态
* @return void
*/
void update_current(void);
/**
* @brief 获取 get_mcu_temp 对应的数据或状态
* @return float 返回值
*/
float get_mcu_temp(void);
/**
* @brief 获取 get_drv_temp 对应的数据或状态
* @return float 返回值
*/
float get_drv_temp(void);
/**
* @brief 获取 get_vbus_voltage 对应的数据或状态
* @return float 返回值
*/
float get_vbus_voltage(void);
/**
* @brief 获取 get_current 对应的数据或状态
* @param[in] char 参数
* @return float 返回值
*/
float get_current(char);
};
/**
* @brief ADC0_1_IRQHandler 中断处理函数
* @return void
*/
void ADC0_1_IRQHandler(void);
/**
* @brief ADC1 初始化:VBUS 连续转换模式(零等待读取)
* @return void
* @note ADC1 上电后即对 ch6(VBUS) 连续转换,软件触发一次后自持续,
* 之后读取 DR 寄存器即可拿到最新完成的转换值(无需阻塞等待)。
*/
void adc1_config(void);
/**
* @brief 零等待读取最新 VBUS 原始值(连续模式下任意时刻可调)
* @return u16 最近一次完成转换的原始值(陈旧度约 1 个转换周期 ≈1.4μs)
* @note 仅主循环任务上下文调用(与其他 ADC1 访问串行,无并发)。
* 原理:读 DR 硬件清 EOC,随后下一次完成转换会刷新 DR,
* 其间完成的转换被丢弃——每次读到的都是刚完成的样本。
*/
u16 adc1_read_vbus_latest(void);
/**
* @brief ADC1 单次软件触发转换并等待完成(仅主循环任务上下文调用)
* @param[in] channel ADC 通道号(如 ADC_CHANNEL_9)
* @return u16 转换原始值
* @note 会临时暂停 VBUS 连续模式、切到目标通道单次转换、再恢复连续,
* 全程约 3~5μs,期间 VBUS 读数最多滞后该时长。drv 温度 1kHz 用。
*/
u16 adc1_read_raw(u8 channel);
#ifdef __cplusplus
}
#endif
+48 -107
View File
@@ -1,3 +1,9 @@
/**
* @file device/ang_sensor.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "ang_sensor.h"
#include "Can.h"
#include "Filter.h"
@@ -12,20 +18,24 @@
// ANG_SENS 调试宏定义
#if ANG_SENS_DEBUG
/** @brief ANG_SENS_DEBUG_PRINT 宏定义 */
#define ANG_SENS_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief ANG_SENS_DEBUG_PRINT 宏定义 */
#define ANG_SENS_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
/** @brief ANG_SPI_CS_ENABLE 宏定义 */
#define ANG_SPI_CS_ENABLE() gpio_bit_reset(GPIOA, GPIO_PIN_15)
/** @brief ANG_SPI_CS_DISABLE 宏定义 */
#define ANG_SPI_CS_DISABLE() gpio_bit_set(GPIOA, GPIO_PIN_15)
/** @brief DRV_SPI_CS_ENABLE 宏定义 */
#define DRV_SPI_CS_ENABLE() gpio_bit_reset(GPIOB, GPIO_PIN_14)
/** @brief DRV_SPI_CS_DISABLE 宏定义 */
#define DRV_SPI_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
// DMA 传输完成标志
volatile bool spi_dma_tx_complete = false;
volatile bool spi_dma_rx_complete = false;
// 全局角度传感器单例:系统只有一个 KTH7823 角度传感器。
// 初始化顺序由 main::hardware_init() 调用 KTH7823.init() 保证。
angle_sensor KTH7823; // 角度传感器初始化
extern FlashEEPROM flashEEP;
extern EncoderAngleKalman Angle_kalman;
@@ -33,120 +43,42 @@ extern EncoderVelocityKalman Vel_kalman;
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
/**
* @brief SPI DMA initialization
*/
void spi_dma_init(void) {
dma_parameter_struct dma_init_struct;
// 使能 DMA 时钟
rcu_periph_clock_enable(RCU_DMA0);
// 初始化发送 DMA 通道
dma_deinit(DMA0, DMA_CH2);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)0; // 实际地址将在传输时设置
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.number = 1;
dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI0));
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH2, &dma_init_struct);
// 初始化接收 DMA 通道
dma_deinit(DMA0, DMA_CH1);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)0; // 实际地址将在传输时设置
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.number = 1;
dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI0));
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH1, &dma_init_struct);
// 使能 SPI 的 DMA 发送和接收功能
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
// 配置 DMA 中断
nvic_irq_enable(DMA0_Channel2_IRQn, 0, 1);
nvic_irq_enable(DMA0_Channel1_IRQn, 0, 1);
// 使能 DMA 传输完成中断
dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF);
dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF);
}
/**
* @brief Send or read half word through SPI using DMA
* @brief Send or read half word through SPI by register polling
*
* @param[in] spi_periph SPI peripheral
* @param[in] tx_data Data to transmit
* @param[out] rx_data Pointer to store received data
* @return 0 if success, -1 if error
* @note 单半字轮询收发。DMA 版本(已移除)每次传输需要:重配 2 个 DMA
* 通道(约 10 次寄存器写) + 2 次 FTF 中断 + 忙等完成标志,而 16bit
* @5MHz 帧本身只有 3.2μs——DMA 的准备与中断开销和传输时间同量级。
* 轮询同样忙等帧时间,但砍掉了全部 DMA 开销与 40k 次/秒的中断。
*/
int spi_rw_half_word_dma(uint32_t spi_periph, uint16_t tx_data,
uint16_t *rx_data) {
// 重置完成标志
spi_dma_tx_complete = false;
spi_dma_rx_complete = false;
int spi_rw_half_word_polling(uint32_t spi_periph, uint16_t tx_data,
uint16_t *rx_data) {
u32 timeout = 1000;
// 配置发送 DMA
dma_channel_disable(DMA0, DMA_CH2);
dma_memory_address_config(DMA0, DMA_CH2, (uint32_t)&tx_data);
dma_transfer_number_config(DMA0, DMA_CH2, 1);
dma_channel_enable(DMA0, DMA_CH2);
// 配置接收 DMA
dma_channel_disable(DMA0, DMA_CH1);
dma_memory_address_config(DMA0, DMA_CH1, (uint32_t)rx_data);
dma_transfer_number_config(DMA0, DMA_CH1, 1);
dma_channel_enable(DMA0, DMA_CH1);
// 等待传输完成,超时时间为 100 个循环
uint32_t timeout = 100;
while ((!spi_dma_tx_complete || !spi_dma_rx_complete) && --timeout) {
// 空循环等待 DMA 传输完成
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE) == RESET) {
if (--timeout == 0U) {
return -1;
}
}
spi_i2s_data_transmit(spi_periph, tx_data);
// 检查是否超时
if (timeout == 0) {
// 禁用 DMA 通道
dma_channel_disable(DMA0, DMA_CH2);
dma_channel_disable(DMA0, DMA_CH1);
ANG_SENS_DEBUG_PRINT("SPI DMA transfer timeout\r\n");
return -1;
timeout = 1000;
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE) == RESET) {
if (--timeout == 0U) {
return -1;
}
}
*rx_data = spi_i2s_data_receive(spi_periph);
// 等待移位完成再返回:调用方返回后立即拉高 CS,
// 若最后一位仍在移位会截断帧
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TRANS) != RESET) {
}
return 0;
}
/**
* @brief DMA0_Channel2 中断处理函数
* @param 无
*/
extern "C" void DMA0_Channel2_IRQHandler(void) {
if (dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INT_FLAG_FTF);
spi_dma_tx_complete = true;
}
}
/**
* @brief DMA0_Channel1 中断处理函数
* @param 无
*/
extern "C" void DMA0_Channel1_IRQHandler(void) {
if (dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF);
spi_dma_rx_complete = true;
}
}
/**
* @brief 角度传感器初始化
* @param 无
@@ -202,7 +134,6 @@ void KTH7823_spi_init(void) {
u8 angle_sensor::init(void) {
KTH7823_gpio_init(); // GPIO 初始化
KTH7823_spi_init(); // SPI 初始化
spi_dma_init(); // DMA 初始化
return 0;
}
@@ -225,7 +156,7 @@ inline float angle_sensor::read_sensorAngle(void) {
uint16_t raw_data;
ANG_SPI_CS_ENABLE();
u8 result = spi_rw_half_word_dma(SPI0, DEFAULT_COMMAND, &raw_data);
u8 result = spi_rw_half_word_polling(SPI0, DEFAULT_COMMAND, &raw_data);
ANG_SPI_CS_DISABLE();
if (result == 0) {
@@ -295,4 +226,14 @@ float angle_sensor::getRawAngle(void) const { return m_cumulativeAngle; }
*/
float angle_sensor::getcurrent_sensorangle(void) const{
return current_angle_deg;
}
/**
* @brief 设置电角度零位校准角
* @param[in] angle 校准角(度)
* @return 0 成功
*/
u8 angle_sensor::set_calibration(float angle) {
calibration_angle = normalizeAngle360(angle);
return 0;
}
+79 -5
View File
@@ -1,3 +1,9 @@
/**
* @file device/ang_sensor.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _angsensor_H
#define _angsensor_H
#ifdef __cplusplus
@@ -7,22 +13,79 @@ extern "C" {
#include <cstring>
#include "config.h"
/**
* @brief spi_rw_half_word 函数
* @param[in] spi_periph 参数
* @param[in] i_HalfWord 参数
* @return u16 返回值
*/
u16 spi_rw_half_word(u32 spi_periph, const u16 i_HalfWord);
int spi_rw_half_word_dma(uint32_t spi_periph, uint16_t tx_data,
uint16_t *rx_data);
/**
* @brief spi_rw_half_word_polling 函数
* @param[in] spi_periph SPI 外设
* @param[in] tx_data 发送的半字
* @param[out] rx_data 接收数据输出指针
* @return int 0 成功,-1 超时
* @note 单个半字收发:直接寄存器轮询。原 DMA 实现每次传输要重配两个
* DMA 通道并触发两次 FTF 中断,之后 CPU 仍忙等完成——对单半字
* 传输 DMA 只有开销没有收益(20kHz 调用下约 40k 次/秒额外中断)。
*/
int spi_rw_half_word_polling(uint32_t spi_periph, uint16_t tx_data,
uint16_t *rx_data);
/**
* @brief angle_sensor 类或结构体
*/
class angle_sensor {
public:
float calibration_angle = ANGLESENS_CALIBRATION_ANGLE;
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init(void);
/**
* @brief 读取 read_sensorAngle 相关数据
* @return float 返回值
*/
float read_sensorAngle(void);
/**
* @brief 更新对象内部状态或采样数据
* @return float 返回值
*/
float update(void);
/**
* @brief 复位对象内部状态
* @return void
*/
void reset(void);
/**
* @brief 获取 getAngle 对应的数据或状态
* @return float 返回值
*/
float getAngle(void) const;
/**
* @brief 获取 getRawAngle 对应的数据或状态
* @return float 返回值
*/
float getRawAngle(void) const;
/**
* @brief 获取 getcurrent_sensorangle 对应的数据或状态
* @return float 返回值
*/
float getcurrent_sensorangle(void) const;
/**
* @brief angle_set 函数
* @param[in] angle 参数
* @return u8 返回值
*/
u8 angle_set(float angle);
/**
* @brief 设置 set_calibration 对应的参数或状态
* @param[in] angle 参数
* @return u8 返回值
*/
u8 set_calibration(float angle);
protected:
@@ -31,10 +94,21 @@ class angle_sensor {
float current_angle_rad;
bool m_firstUpdate;
float m_lastAngle;
float m_lastcumulativeAngle;
float velocity[VEL_FILTER_SIZE] = {0.0f};
};
/**
* @brief 快速角度归一化到 [0, 360)
* 小角度保留 while 快路径;大角度先一次性扣掉整圈数,避免累计角很大时 while 循环过多。
* 不依赖 fmodf,减少嵌入式数学库开销和链接风险。
*/
inline float normalizeAngle360(float angle) {
if (angle >= 720.0f || angle <= -360.0f) {
int turns = (int)(angle / 360.0f);
angle -= (float)turns * 360.0f;
}
while (angle >= 360.0f) angle -= 360.0f;
while (angle < 0.0f) angle += 360.0f;
return angle;
}
#ifdef __cplusplus
}
#endif
+224 -13
View File
@@ -1,9 +1,218 @@
/**
* @file device/motor.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 功率级门驱动芯片抽象层:
* - 老板 DRV8313(集成 FET 三半桥,无 SPI):保持原使能时序
* - 新板 DRV8323RS(外置 MOS):使能前经 SPI 配置 3x PWM 模式、
* CSA 增益、VDS 过流门限、死区与 IDRIVE,并逐条回读校验
*/
#include "motor.h"
#include "Timer.h"
#include "config.h"
#include "ang_sensor.h"
#include "gd32c10x_eval.h"
#include "gd32c10x_gpio.h"
#include "gd32c10x_spi.h"
#include "systick.h"
#include <cstdio>
/* 角度传感器 SPI 初始化(ang_sensor.cpp):DRV 配置完成后恢复其 SPI 模式 */
extern void KTH7823_spi_init(void);
/**
* @brief 门驱动芯片引脚预初始化
* @note 新板:DRVSLP(PB12) 置低保持 DRV8323 睡眠、片选 PB14 释放、
* PB2(nFAULT) 内部上拉输入。必须在任何可能拉高使能脚的代码之前调用;
* 老板:无操作(原时序在 timer1_gpio_config 中保持不变)。
*/
void gate_driver_preinit(void) {
#if GATE_DRIVER_DRV8323
rcu_periph_clock_enable(RCU_GPIOB);
/* 使能脚推挽输出并保持低:芯片睡眠,栅极全 Hi-Z,寄存器默认值不起作用 */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_bit_reset(GPIOB, GPIO_PIN_12);
/* SPI 片选默认释放(SCLK 空闲低时才允许 CS 翻转) */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
gpio_bit_set(GPIOB, GPIO_PIN_14);
/* nFAULT 低有效,开漏输出 → 内部上拉,避免浮空误读成故障 */
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
#endif
}
#if GATE_DRIVER_DRV8323
/* ---------------- DRV8323 SPI ----------------
* 帧格式:[15:11] 寄存器地址 [10] 1读0写 [9:0] 数据。
* 时序:SCLK 空闲低、SDI 下降沿采样(SPI 模式1),与角度传感器
* (模式3)不同:配置前重初始化 SPI0,配置完调 KTH7823_spi_init() 恢复。
* 寄存器/位定义来源:TI SLVSDJ3D(DRV832x datasheet)Register Maps。 */
#define DRV_CS_ENABLE() gpio_bit_reset(GPIOB, GPIO_PIN_14)
#define DRV_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
#define DRV8323_REG_FAULT1 0x00u /* FAULT/VDS_OCP/GDF/UVLO/OTSD/VDS_Hx,VDS_Lx */
#define DRV8323_REG_VGS2 0x01u /* SA/SB/SC_OC、OTW、CPUV、VGS_Hx,VGS_Lx */
#define DRV8323_REG_DRVCTRL 0x02u /* [6:5]PWM_MODE [2]COAST [1]BRAKE [0]CLR_FLT */
#define DRV8323_REG_GDRV_HS 0x03u /* [10]LOCK [9:6]IDRIVEP_HS [5:2]IDRIVEN_HS */
#define DRV8323_REG_GDRV_LS 0x04u /* [10]CBC [9:8]TDRIVE [7:4]IDRIVEP_LS [3:0]IDRIVEN_LS */
#define DRV8323_REG_OCPCTRL 0x05u /* [10]TRETRY [9:8]DEAD_TIME [7:6]OCP_MODE [5:4]OCP_DEG [3:0]VDS_LVL */
#define DRV8323_REG_CSACTRL 0x06u /* [10]CSA_FET [9]VREF_DIV [8]LS_REF [7:6]CSA_GAIN [1:0]SEN_LVL */
/* PWM_MODE=01b:3x PWM。INHx 低→下管开、高→上管开,死区芯片内部插入。
芯片复位默认 00b(6x PWM),6x 下 INLx 语义不同,本板 INLx=DRVSLP 常高,
必须切到 3x,否则上管永远开不了(电流全走体二极管)。 */
#define DRV8323_PWM_MODE_3X (1u << 5)
/* 栅驱动电流:源 170mA/灌 190mA(WSD6035DN33 Qg≈12.6nC,边沿约 75ns,
* 无栅电阻布局下避免过快边沿加重振铃) */
#define DRV8323_IDRIVE_HS ((0x6u << 6) | (0x7u << 2))
#define DRV8323_IDRIVE_HS_MASK ((0xFu << 6) | (0xFu << 2))
#define DRV8323_IDRIVE_LS ((0x6u << 4) | (0x7u << 0))
#define DRV8323_IDRIVE_LS_MASK ((0xFu << 4) | (0xFu << 0))
/* 死区 200ns(20kHz 大 Qg 管的安全值);OCP_MODE=10b 仅上报不动作:
* VDS 门限受 RDSon 温漂影响大(热态 20→30mΩ),先靠软件限流+热模型,
* 避免误跳闸;VDS_LVL=0.75V(默认),故障位可经 drv_fault_status() 读取 */
#define DRV8323_OCP_CFG ((0x2u << 8) | (0x2u << 6) | (0x9u << 0))
#define DRV8323_OCP_CFG_MASK ((0x3u << 8) | (0x3u << 6) | (0xFu << 0))
/** @brief CSA 增益值(V/V) → 0x06[7:6] 编码(5:00b 10:01b 20:10b 40:11b) */
constexpr u16 drv8323_gain_code(float g) {
return (g <= 5.0f) ? 0x0u : (g <= 10.0f) ? 0x1u : (g <= 20.0f) ? 0x2u : 0x3u;
}
#define DRV8323_CSA_GAIN_BITS (drv8323_gain_code(DRV8323_CSA_GAIN_VV) << 6)
/**
* @brief DRV8323 单帧 SPI 收发(片选内 16bit,帧间隔由调用节奏保证)
* @param[in] frame 完整 16bit 帧
* @return u16 从机返回的 10bit 数据
*/
static u16 drv8323_xfer(u16 frame) {
u16 rx = 0;
DRV_CS_ENABLE();
spi_rw_half_word_polling(SPI0, frame, &rx);
DRV_CS_DISABLE();
return rx & 0x03FFu;
}
/**
* @brief 读改写一个寄存器并回读校验
* @param[in] addr 寄存器地址
* @param[in] clrmask 要清零的位
* @param[in] setmask 要置一的位
* @return u8 0=回读一致 1=校验失败
*/
static u8 drv8323_write_verify(u8 addr, u16 clrmask, u16 setmask) {
u16 cur = drv8323_xfer((u16)(((u16)addr << 11) | (1u << 10)));
u16 nv = (u16)((cur & ~clrmask) | setmask);
drv8323_xfer((u16)(((u16)addr << 11) | nv));
u16 rb = drv8323_xfer((u16)(((u16)addr << 11) | (1u << 10)));
return (rb == nv) ? 0u : 1u;
}
#endif /* GATE_DRIVER_DRV8323 */
/**
* @brief 门驱动芯片上电配置
* @return u8 0=成功;非 0 按位指示回读校验失败的寄存器
* (bit0:DRVCTRL bit1:GDRV_HS bit2:GDRV_LS bit3:OCPCTRL bit4:CSACTRL)
* @note 调用时机:TIMER1/PWM 已配置且占空比为 0 之后。拉高 DRVSLP 唤醒
* 芯片的瞬间 INHx=0、INLx=1,默认 6x 模式下为"三相下管导通",
* 静止电机零电流,与 3x 模式 0% 占空比稳态一致,随后写模式无扰动。
*/
u8 gate_driver_bringup(void) {
#if GATE_DRIVER_DRV8323
/* 1. 唤醒芯片:DRVSLP 网络 200nF + 芯片内部复位,留足建立时间 */
drv_enable(1);
delay_1ms(10);
/* 2. SPI0 切到 DRV8323 时序(模式1:CPOL=0/下降沿采样,2MHz) */
spi_parameter_struct spi_init_str;
spi_struct_para_init(&spi_init_str);
spi_init_str.device_mode = SPI_MASTER;
spi_init_str.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_str.frame_size = SPI_FRAMESIZE_16BIT;
spi_init_str.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
spi_init_str.nss = SPI_NSS_SOFT;
spi_init_str.endian = SPI_ENDIAN_MSB;
spi_init_str.prescale = 60; /* 120MHz/60 = 2MHz */
spi_init(SPI0, &spi_init_str);
spi_nss_output_enable(SPI0);
spi_nssp_mode_enable(SPI0);
spi_enable(SPI0);
/* 3. 配置并逐条回读校验(读改写,保留未触碰位的出厂默认:
* VREF_DIV=1→SOx 中点 VREF/2,与 CURRENT_OFFSET 的 1.655V 一致;
* CSA_FET=0→正端 SPx,与 shunt 的 SP/SN 接法一致) */
u8 err = 0;
err |= (u8)(drv8323_write_verify(DRV8323_REG_DRVCTRL, 0x0000u,
DRV8323_PWM_MODE_3X)
<< 0);
err |= (u8)(drv8323_write_verify(DRV8323_REG_GDRV_HS,
DRV8323_IDRIVE_HS_MASK, DRV8323_IDRIVE_HS)
<< 1);
err |= (u8)(drv8323_write_verify(DRV8323_REG_GDRV_LS,
DRV8323_IDRIVE_LS_MASK, DRV8323_IDRIVE_LS)
<< 2);
err |= (u8)(drv8323_write_verify(DRV8323_REG_OCPCTRL, DRV8323_OCP_CFG_MASK,
DRV8323_OCP_CFG)
<< 3);
err |= (u8)(drv8323_write_verify(DRV8323_REG_CSACTRL, 0x3u << 6,
DRV8323_CSA_GAIN_BITS)
<< 4);
/* 4. CLR_FLT 脉冲清一次上电故障锁存,再恢复正常值 */
drv8323_xfer((u16)((DRV8323_REG_DRVCTRL << 11) | DRV8323_PWM_MODE_3X | 0x1u));
drv8323_xfer((u16)((DRV8323_REG_DRVCTRL << 11) | DRV8323_PWM_MODE_3X));
/* 5. 恢复角度传感器的 SPI 模式(模式3) */
KTH7823_spi_init();
return err;
#else
return 0; /* 老板 DRV8313 无 SPI,原使能时序保持不变 */
#endif
}
/**
* @brief 门驱动运行时使能/睡眠
* @note 新板:PB12 同时是 DRV8323 的 ENABLE 与 INLA/B/C 电平——
* 高=正常运转,低=芯片睡眠三相 Hi-Z(惯性滑行,兼作 MCU 复位
* 时的天然 fail-safe);老板:PB12+PC13 双脚,保持原行为。
*/
void drv_enable(u8 en) {
if (en) {
gpio_bit_set(GPIOB, GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
gpio_bit_set(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
#endif
} else {
gpio_bit_reset(GPIOB, GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
gpio_bit_reset(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
#endif
}
}
/**
* @brief 读取 nFAULT 引脚状态(PB2,低有效)
* @return u8 1=故障有效 0=正常
*/
u8 drv_nfault_active(void) {
return (gpio_input_bit_get(GPIOB, GPIO_PIN_2) == RESET) ? 1u : 0u;
}
/**
* @brief 读取门驱动故障状态寄存器
* @return u32 [9:0]=Fault Status1(0x00) [19:10]=VGS Status2(0x01);老板返回 0
*/
u32 drv_fault_status(void) {
#if GATE_DRIVER_DRV8323
u32 f1 = drv8323_xfer((u16)((DRV8323_REG_FAULT1 << 11) | (1u << 10)));
u32 f2 = drv8323_xfer((u16)((DRV8323_REG_VGS2 << 11) | (1u << 10)));
return f1 | (f2 << 10);
#else
return 0;
#endif
}
/**
* @brief driver输出初始化
*/
@@ -12,16 +221,20 @@ void motor_init(void) {
timer1_pwm_config();
// 使能GPIOB时钟
rcu_periph_clock_enable(RCU_GPIOB);
// 配置PB2为输入引脚,用于读取电机状态
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
// 配置PB2为输入引脚,用于读取电机状态(nFAULT)。
// 用内部上拉(GPIO_MODE_IPU)而不是浮空:nFAULT 是开漏输出,无故障时靠
// 上拉保持高。现在 PB2 电平直接决定"允不允许运行",浮空输入在接线未确认
// 或无上拉时会随机读低,把整机锁死;内部上拉(~40kΩ)与外部上拉并联无害。
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
/* 配置GPIOB的PB14为推挽输出 SPICS */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_14 | GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
// 使能GPIOC时钟,用于电机驱动使能引脚
rcu_periph_clock_enable(RCU_MOTOR_EN_GPIO);
// 配置PC13为推挽输出,用于控制电机驱动器使能
// 配置PC13为推挽输出,用于控制电机驱动器使能(老板专用)
gpio_init(MOTOR_EN_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, MOTOR_EN_GPIO_PIN);
#endif
}
/**
@@ -29,24 +242,22 @@ void motor_init(void) {
*/
void motor_reset(void) {
motor_sleep();
#if FOC_DEBUG
printf("motor reseting...\r\n");
#endif
motor_resume();
}
/**
* @brief 电机休眠
*/
void motor_sleep(void) {
gpio_bit_reset(GPIOB, GPIO_PIN_12);
// 同时禁用电机驱动器
gpio_bit_reset(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
void motor_sleep(void) {
drv_enable(0);
}
/**
* @brief 电机启动
*/
void motor_resume(void) {
gpio_bit_set(GPIOB, GPIO_PIN_12);
// 同时启用电机驱动器
gpio_bit_set(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
}
void motor_resume(void) {
drv_enable(1);
}
+62
View File
@@ -1,14 +1,76 @@
/**
* @file device/motor.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _MOTOR_H
#define _MOTOR_H
#include "config.h"
#include "gd32c10x_eval.h"
// 定义电机驱动使能引脚
/** @brief MOTOR_EN_GPIO_PORT 宏定义 */
#define MOTOR_EN_GPIO_PORT GPIOC
/** @brief MOTOR_EN_GPIO_PIN 宏定义 */
#define MOTOR_EN_GPIO_PIN GPIO_PIN_13
/** @brief RCU_MOTOR_EN_GPIO 宏定义 */
#define RCU_MOTOR_EN_GPIO RCU_GPIOC
/**
* @brief 初始化电机驱动输出
* @return void
*/
void motor_init(void);
/**
* @brief 复位电机驱动
* @return void
*/
void motor_reset(void);
/**
* @brief 唤醒电机驱动
* @return void
*/
void motor_resume(void);
/**
* @brief 使电机驱动进入休眠
* @return void
*/
void motor_sleep(void);
/**
* @brief 门驱动芯片引脚预初始化(全部置安全态:使能拉低、CS 释放)
* @note 必须在任何可能拉高使能脚的代码之前调用
* @return void
*/
void gate_driver_preinit(void);
/**
* @brief 门驱动芯片上电配置
* 新板(DRV8323RS):拉高使能 → SPI 写 3x PWM 模式/CSA 增益/VDS
* 保护/死区/IDRIVE 并逐条回读校验,最后恢复角度传感器 SPI 模式;
* 老板(DRV8313):无 SPI,保持原上电序列(无操作)。
* @return u8 0 成功;非 0 为配置/回读校验失败的寄存器位图
* @note 调用时机:TIMER1/PWM 已配置且占空比为 0 之后(此时使能芯片,
* 半桥只会进入"下管常开"的静止安全态)
*/
u8 gate_driver_bringup(void);
/**
* @brief 门驱动运行时使能/睡眠(分板封装 PB12,老板同时控制 PC13)
* @param[in] en 1=使能 0=睡眠
* @return void
*/
void drv_enable(u8 en);
/**
* @brief 读取 nFAULT 引脚状态
* @return u8 1=故障有效(低电平) 0=正常
*/
u8 drv_nfault_active(void);
/**
* @brief 读取门驱动故障状态寄存器(仅 DRV8323 有效)
* @return u16 [9:0]=Fault Status1(0x00) [19:10]=VGS Status2(0x01);老板返回 0
*/
u32 drv_fault_status(void);
#endif
-33
View File
@@ -1,33 +0,0 @@
#include "adc.h"
#include "config.h"
#include <cstdio>
// TEST 调试宏定义
#if TEST_DEBUG
#define TEST_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define TEST_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
constexpr int status_print = 0;
extern sampler sampler;
void test_1111() {
// 初始化 CAN 回环测试
// can_loopback_init();
// can_config();
if (status_print) {
/* value print */
TEST_DEBUG_PRINT(" the temperature data is %2.2f degrees Celsius\r\n",
sampler.get_mcu_temp());
TEST_DEBUG_PRINT(" the drv_temperature data is %2.2f degrees Celsius\r\n",
sampler.get_drv_temp());
TEST_DEBUG_PRINT(" the U current data is %2.2f mA\r\n", sampler.get_current('U'));
TEST_DEBUG_PRINT(" the V current data is %2.2f mA\r\n", sampler.get_current('V'));
TEST_DEBUG_PRINT(" the W current data is %2.2f mA\r\n", sampler.get_current('W'));
TEST_DEBUG_PRINT(" the VBUS is %2.2f V\r\n", sampler.get_vbus_voltage());
// TEST_DEBUG_PRINT(" the angle is %2.2f\r\n", tle5012b_read());
TEST_DEBUG_PRINT("\n----------------------------------------------------\n");
}
// can_loopback_test();
}
+84 -104
View File
@@ -1,4 +1,11 @@
/**
* @file main.cpp
* @brief 应用程序主流程源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "main.h"
#include "tasks.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
@@ -8,12 +15,15 @@
#include "flash.h"
#include "health.h"
#include "initial.h"
#include "motor.h"
#include "systick.h"
// MAIN 调试宏定义
#if MAIN_DEBUG
/** @brief MAIN_DEBUG_PRINT 宏定义 */
#define MAIN_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief MAIN_DEBUG_PRINT 宏定义 */
#define MAIN_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
@@ -22,34 +32,32 @@ extern sampler sampler; // 采样器
extern angle_sensor KTH7823; // 角度传感器
extern EncoderVelocityKalman Vel_kalman; // 速度滤波
extern EncoderAngleKalman Angle_kalman; // 角度滤波
extern ThermalController thermalController; // 热量控制器
extern FOC_Controller FOC; // FOC 控制
extern FlashEEPROM flashEEP; // EEPROM 类
extern VersionManager Version;
// 全局版本管理器实例
// 主任务调度共享位图:TIMER2 ISR 置位任务,main 循环短关中断后取快照并清位。
TaskScheduler scheduler = {0};
u32 motor_runtime_50us = 0; // 电机驱动计算轮次
u32 cnt_test = 0; // 负载检测
// 电机 50us 控制周期计数:主循环电机任务递增,观测器/计时逻辑读取。
u32 motor_runtime_50us = 0;
// 负载检测/通信心跳计数:主循环递增,CAN 周期发送任务清零。
u32 cnt_test = 0;
// 初始化错误位图(INIT_ERR_*,config.h):hardware_init 按模块置位,
// CAN STATUS 帧 InitStatus 反馈给上位机,0 表示全部初始化正常
volatile u32 init_error_flags = 0;
// 函数指针数组,包含各种控制模式对应的函数
static FOCFunctionPtr control_functions[] = {
&FOC_Controller::drv_hold, // 0: 占空比置
&FOC_Controller::angle_control, // 1: 位置闭环控制
&FOC_Controller::velocity_control, // 2: 速度闭环控制
&FOC_Controller::
angle_control_with_velocity_limit, // 3:
// 角度闭环控制,带速度限制
&FOC_Controller::MIT_Mode, // 4: MIT 模式
&FOC_Controller::current_control, // 5: 电流闭环控制
&FOC_Controller::velocity_open_loop, // 6: 速度开环控制
&FOC_Controller::PWM_out, // 0: 占空比置
};
/**
* @brief 应用程序入口函数
* @return int 返回值
*/
int main(void) {
u8 init_status = hardware_init();
while (init_status) { // 初始化检测
MAIN_DEBUG_PRINT("初始化失败\r\n");
// 初始化失败:持续通过 CAN STATUS 帧上报 InitStatus 错误码
// (含电流检测配平超限/无采样帧),上位机无需调试串口即可识别故障模块
printf("初始化失败 InitStatus=0x%08X\r\n", init_error_flags);
CAN0_transmiter.send_status_frame(); // STATUS 帧字节 32~35 携带错误位图
delay_1ms(100);
}
OTA_initial(); // OTA 初始化,A/B 分区处理
@@ -73,101 +81,73 @@ int main(void) {
return 0;
}
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
*/
u8 hardware_init(void) {
__enable_irq();
systick_config(); // 系统时钟初始化
unused_pin_init(); // 未使用引脚初始化
systick_config(); // 系统时钟初始化
unused_pin_init(); // 未使用引脚初始化
gate_driver_preinit(); // 门驱动使能脚预置安全态(新板:DRVSLP 低=芯片睡眠)
USART0_init(); // 调试串口初始化
User_timer_init(); // 任务 Timer 初始化
u8 init_status = 0;
init_status += KTH7823.init(); // 角度传感器初始化
init_status += sampler.init(); // 采样器初始化
// 初始化错误码按模块置位(INIT_ERR_*,位或组合;0=全部正常),
// 经 CAN STATUS 帧 InitStatus 反馈
init_error_flags = 0;
if (KTH7823.init()) {
init_error_flags |= INIT_ERR_ANGLE_SENSOR; // 角度传感器初始化
}
if (sampler.init()) {
init_error_flags |= INIT_ERR_ADC; // 采样器/ADC 初始化
}
// EEPROM 初始化 (必须在 CAN 初始化之前,因为 CAN 会从 Flash 读取节点 ID)
flashEEP.init(); // EEPROM 初始化
// CAN 初始化 (会先设置默认节点 ID,然后从 Flash 加载已保存的节点 ID)
init_status += CAN0_transmiter.init(); // CAN 初始化
if (CAN0_transmiter.init()) {
init_error_flags |= INIT_ERR_CAN; // CAN 初始化
}
init_status += Angle_kalman.init(KTH7823.getAngle()); // 角度滤波初始化
init_status += Vel_kalman.init(0.0f); // 速度滤波初始化
init_status += FOC.init(); // FOC 初始化
return init_status;
if (Angle_kalman.init(KTH7823.getAngle())) {
init_error_flags |= INIT_ERR_ANG_KALMAN; // 角度滤波器初始化
}
if (Vel_kalman.init(0.0f)) {
init_error_flags |= INIT_ERR_VEL_KALMAN; // 速度滤波器初始化
}
if (FOC.init()) {
init_error_flags |= INIT_ERR_FOC; // FOC 初始化
}
// 门驱动上电配置放在 FOC 校验之后:此时 TIMER1/PWM 已启动且占空比为 0,
// 新板(DRV8323)在此拉高 DRVSLP 唤醒芯片并经 SPI 配置 3x PWM/增益/保护,
// 随后即使任务立即输出 0 占空比,桥臂也处于"下管常开"的静止安全态
if (gate_driver_bringup()) {
init_error_flags |= INIT_ERR_GATE_DRIVER; // 门驱动 bringup
}
// 三相电流零点配平:采样前唤醒门驱动,让 0% 占空比下三相下管导通,
// PGNDx 与功率地等电位、共模与运行时一致,配平结果才可用。
// 老板 bringup 是空操作、上电默认睡眠:此处临时唤醒(此时占空比 0=
// 三相下管导通,静止安全态),配平完恢复睡眠,保持"上电未使能"的
// 原始安全态(模式 0 的 drv_hold 恒占空比不会真实输出);新板
// bringup 已唤醒并保持常醒,无需额外操作。配平值超限/无采样帧
// 分别置独立错误位。
#if !GATE_DRIVER_DRV8323
motor_resume();
#endif
u8 current_cal = sampler.zero_calibrate();
#if !GATE_DRIVER_DRV8323
motor_sleep();
#endif
if (current_cal == 1) {
init_error_flags |= INIT_ERR_CURRENT_NOFRAME; // 无采样帧
} else if (current_cal == 2) {
init_error_flags |= INIT_ERR_CURRENT_BAL; // 配平值超限
}
return (init_error_flags != 0U) ? 1U : 0U;
}
void do_task(u32 currentTasks) {
busy_task_enter(); // DWT: 记录忙碌起始周期
cnt_test++;
// 按照优先级和重要性处理任务
if (currentTasks & (1 << TASK_CURRENT_UPDATE)) { // 更新电流采样值
sampler.update_current();
}
if (currentTasks & (1 << TASK_ANGLE_UPDATE)) { // 更新角度传感器值
KTH7823.update();
FOC.rt_angle = Angle_kalman.update(KTH7823.getAngle());
}
if (currentTasks & (1 << TASK_VELOCITY_UPDATE)) { // 更新速度观测值
FOC.rt_velocity = Angle_kalman.getVelocity();
}
if (currentTasks & (1 << TASK_MOTOR_CONTROL)) { // 电机控制任务 (最高优先级)
motor_runtime_50us++;
// 快速电压检测(每100us执行一次,响应时间 < 200us)
FOC.fast_voltage_check();
// 静态变量用于跟踪上一次的控制模式
static int last_control_mode = -1;
// 边界检查,防止数组越界
if (FOC.control_mode >= 0 &&
FOC.control_mode < 8) { // 更新为 8 个控制模式
// 检查控制模式是否发生变化
if (FOC.control_mode != last_control_mode) {
// 重置所有 PID 控制器的状态,避免积分项累积
FOC.vel_loop.reset();
FOC.angle_loop.reset();
FOC.current_loop.reset();
FOC.vel_inVelAngloop.reset();
FOC.ang_inVelAngloop.reset();
FOC.ang_inMITloop.reset();
FOC.vel_inMITloop.reset();
FOC.current_inMITloop.reset();
// 更新上次控制模式
last_control_mode = FOC.control_mode;
}
if (!FOC.is_shut_down) {
// 调用对应的控制函数
(FOC.*control_functions[FOC.control_mode])();
} else {
// 调用对应的控制函数
FOC.drv_stop();
}
}
}
if (currentTasks & (1 << TASK_MCU_TEMP_UPDATE)) { // 更新 mcu 温度传感器值
sampler.update_mcu_temp();
}
if (currentTasks & (1 << TASK_DRV_TEMP_UPDATE)) { // 更新电机温度传感器值
sampler.update_drv_temp();
}
if (currentTasks & (1 << TASK_THERMAL_CONTROL)) { // 热量控制
thermalController.update();
}
if (currentTasks & (1 << TASK_MAGNETIC_CHECK)) { // 磁感应强度检测
angsens_MG_check();
}
if (currentTasks & (1 << TASK_VBUS_UPDATE)) { // 更新 vbus 电压值
power_manage();
}
if (currentTasks & (1 << TASK_CAN_TRANSMIT)) { // CAN 通信
CAN0_transmiter.test_u16 = CAN0_transmiter.get_node_id(); // 负载检测
cnt_test = 0;
CAN0_transmiter.can0_send_flag = SET;
CAN0_transmiter.communication_check();
fwdgt_counter_reload(); // 看门狗复位
}
busy_task_exit(); // DWT: 累加忙碌周期
}
+6 -2
View File
@@ -34,10 +34,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef MAIN_H
#define MAIN_H
#include "config.h"
#include <cstdint>
void do_task(u32 currentTasks);
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
*/
u8 hardware_init(void);
#endif /* MAIN_H */
+233 -54
View File
@@ -1,37 +1,132 @@
/**
* @file sys/config.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#include "gd32c10x_eval.h"
#include <cstdint>
// 项目级定宽类型别名(u8/u16/u32 等):整个工程统一从这里取,
// 重复 typedef 同一类型在 C++ 中合法,包含多次无副作用
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef volatile uint8_t vu8;
typedef volatile uint16_t vu16;
typedef volatile uint32_t vu32;
// 如果使用 CMSIS DSP 库,需要定义 ARM_MATH_CM4
#define ARM_MATH_CM4
#define EEP_DEBUG 1
#define OTA_DEBUG 1
#define SLEEP_DEBUG 1
#define ANG_SENS_DEBUG 1
#define CAN_DEBUG 1
#define MAIN_DEBUG 1
#define TEST_DEBUG 1
#define EEP_DEBUG 0
#define OTA_DEBUG 0
#define SLEEP_DEBUG 0
#define ANG_SENS_DEBUG 0
#define CAN_DEBUG 0
#define MAIN_DEBUG 0
#define FOC_DEBUG 0
// 版本号定义 - 可根据实际项目进行调整
constexpr u16 SOFTWARE_VERSION_MAJOR = 26;
constexpr u16 SOFTWARE_VERSION_MINOR = 5;
constexpr u16 SOFTWARE_VERSION_PATCH = 17;
constexpr u16 SOFTWARE_VERSION_BUILD = 2;
// ===================== 版本号定义 =====================
// 软件版本 = 编译日期自动生成(年.月.日)+ 当天构建小版本号(手动递增)
// __DATE__ 形如 "Mmm dd yyyy"(如 "Aug 29 2026"):2026-08-29 当天第一次
// 构建得到 26.8.29.1;同一天多次出固件时把 SOFTWARE_VERSION_BUILD +1
#define CFG_VER_YEAR \
((__DATE__[7] - '0') * 1000 + (__DATE__[8] - '0') * 100 + \
(__DATE__[9] - '0') * 10 + (__DATE__[10] - '0'))
#define CFG_VER_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define CFG_VER_IS_FEB (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b')
#define CFG_VER_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define CFG_VER_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r')
#define CFG_VER_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define CFG_VER_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define CFG_VER_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define CFG_VER_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g')
#define CFG_VER_IS_SEP (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p')
#define CFG_VER_IS_OCT (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't')
#define CFG_VER_IS_NOV (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v')
#define CFG_VER_IS_DEC (__DATE__[0] == 'D' && __DATE__[1] == 'e' && __DATE__[2] == 'c')
#define CFG_VER_MONTH \
(CFG_VER_IS_JAN ? 1 \
: CFG_VER_IS_FEB ? 2 \
: CFG_VER_IS_MAR ? 3 \
: CFG_VER_IS_APR ? 4 \
: CFG_VER_IS_MAY ? 5 \
: CFG_VER_IS_JUN ? 6 \
: CFG_VER_IS_JUL ? 7 \
: CFG_VER_IS_AUG ? 8 \
: CFG_VER_IS_SEP ? 9 \
: CFG_VER_IS_OCT ? 10 \
: CFG_VER_IS_NOV ? 11 \
: CFG_VER_IS_DEC ? 12 \
: 0)
/* 日字段两位,个位数时首字符为空格(如 "Aug 5 2026") */
#define CFG_VER_DAY \
((__DATE__[4] == ' ') ? (__DATE__[5] - '0') \
: ((__DATE__[4] - '0') * 10 + (__DATE__[5] - '0')))
constexpr u16 SOFTWARE_VERSION_MAJOR = CFG_VER_YEAR % 100; // 年后两位,如 26
constexpr u16 SOFTWARE_VERSION_MINOR = CFG_VER_MONTH; // 月
constexpr u16 SOFTWARE_VERSION_PATCH = CFG_VER_DAY; // 日
// 当天第几次构建(日期由 __DATE__ 自动带出):
// 26.10.10.2 = nFAULT(MotorStatus) 由"仅上报"改为运行门
// 26.10.11.1 = 停机/解除统一收进 RunInhibit 闸门,删掉 FOC 里 5 个互引的开关机函数
constexpr u16 SOFTWARE_VERSION_BUILD = 1;
// 硬件版本:全手写,仅在 PCB 改版时修改(编译 APP 不影响)
constexpr u16 HARDWARE_VERSION_MAJOR = 25;
constexpr u16 HARDWARE_VERSION_MINOR = 12;
constexpr u16 HARDWARE_VERSION_PATCH = 01;
constexpr u16 HARDWARE_VERSION_PATCH = 1;
constexpr u16 HARDWARE_VERSION_BUILD = 0;
// BOOT 版本:全手写,仅在 bootloader 更新时修改(APP 编译不影响)
constexpr u16 BOOT_VERSION_MAJOR = 26;
constexpr u16 BOOT_VERSION_MINOR = 1;
constexpr u16 BOOT_VERSION_PATCH = 12;
constexpr u16 BOOT_VERSION_BUILD = 0;
// ===================== 功率级门驱动芯片选择 =====================
// 1 = 新板 DRV8323RS(外置 WSD6035DN33):使能前经 SPI 配置 3x PWM 模式、
// CSA 增益、VDS 过流门限;3 路 PWM 接 INHA/B/C(CH2/CH1/CH0,
// 固件内已做通道重映射);PB12(DRVSLP) 同时是使能脚与 INLA/B/C 电平
// (高=正常运转,低=三相 Hi-Z 睡眠滑行)。
// 0 = 老板 DRV8313(集成 FET 三半桥):无 SPI,保持原使能时序与相序。
#define GATE_DRIVER_DRV8323 0
#if GATE_DRIVER_DRV8323
// DRV8323 片上 CSA 增益(V/V),经 SPI 写入 0x06[7:6],仅 5/10/20/40 有效。
// 双极性量程 = ±1.655V/(G×Rshunt):G=10、R=10mΩ 时约 ±16.5A。
// 修改后电流换算系数 CURRENT_FACTOR(ADC.cpp)自动跟随,但软件限流标定需复核。
constexpr float DRV8323_CSA_GAIN_VV = 10.0f;
constexpr float DRV8323_SHUNT_OHM = 0.010f;
#endif
// ===================== 相电流采样前端选择(独立于门驱动芯片) =====================
// 1 = 低边采样板(DRV8313 + 下桥臂 10mΩ + INA240A2):采样电阻串在各相下管
// 源极(PGNDx)与功率地之间,INA240A2(50V/V)放大后接 ADC0 注入组。
// OUT 静态中点 = (V_REF1+V_REF2)/2 = 1.65V(REF1 接 AGND、REF2 接 A3V3,
// 由芯片内部分压取中,数据手册支持的接法)。
// 换算 0.5V/A → 2.0 A/V,与老板外置放大链(50倍×10mΩ)数值一致,
// 电流链路单位为安培(get_current 返回 A,上报处 ×1000 转 mA)。
// 注意:下桥臂采样电阻只有对应下管导通(INx=0)时才有相电流流过,
// ADC 采样点必须在三相下管同时导通的窗口内(见 TIMER1_CH3_COMPARE_VALUE)。
// 0 = 老板(DRV8313 + 外置放大链):保持原标定系数与采样点。
#define CURRENT_SENSE_INA240 1
#if CURRENT_SENSE_INA240
constexpr float INA240_CSA_GAIN_VV = 50.0f; // INA240A2 = 50V/V(A1=20/A3=100/A4=200)
constexpr float INA240_SHUNT_OHM = 0.010f; // 低边采样电阻 10mΩ
#endif
// 编译时间(AC6 编译器自动生成)
#define APP_BUILD_DATE __DATE__
#define APP_BUILD_TIME __TIME__
// Flash 分区配置
constexpr u32 BOOTLOADER_SIZE = 0x6000; // 24KB
constexpr u32 BOOTLOADER_START_ADDR = 0x08000000; // BOOT 起始地址
constexpr u32 APPLATEST_START_ADDR = 0x08012000; // APP 起始地址
constexpr u32 APPBACK_START_ADDR = 0x08006000; // APPBACK 起始地址
@@ -41,38 +136,43 @@ constexpr u32 SLEEP_CONFIG_START = 0x0801E400; // 睡眠配置起始地址
constexpr u32 SLEEP_CONFIG_END = 0x0801E800; // 睡眠配置结束地址
constexpr u32 FLASH_EMPTY = 0xFFFFFFFF; // 空数据
constexpr u32 SLEEP_ENABLE = 0x11111111; // 睡眠使能
constexpr u32 SLEEP_DISABLE = 0x00000000; // 睡眠禁止
// 节点 ID 配置 (用于多电机 CAN 总线隔离)
// 使用 CAN ID 高 16 位 (bits 31-16) 存储节点 ID,低 16 位 (bits 15-0) 存储基础 ID
// 格式:[节点 ID(16 位)][基础 ID(16 位)],例如:node_id=1, base_id=0x0200 → 0x00010200
//
// 节点 ID 存储方案:
// - 使用独立 Flash 页 (NODE_ID_PAGE_ADDR) 存储节点 ID,与EEPROM完全隔离
// - 采用双重备份机制 (主备份 + 备用备份)
// - 使用独立 Flash 页存储节点 ID,与EEPROM完全隔离
// - 采用双重备份机制:主备份与备用备份分属两个独立 1KB 页,
// 擦写其一不影响另一份,掉电时至少保留一份有效数据
// - 写入采用乒乓方式:总是写入"非最新"的那一页,另一页全程保有旧值
// - 使用魔数 (NODE_ID_MAGIC) 验证数据有效性,防止位翻转导致错读
// - 使用写入计数器区分最新数据
constexpr u32 NODE_ID_PAGE_ADDR = 0x0801E800; // 节点 ID 存储页 (1KB,独立区域)
constexpr u32 NODE_ID_PRIMARY_ADDR = 0x0801E800; // 主存储地址
constexpr u32 NODE_ID_BACKUP_ADDR = 0x0801E840; // 备份存储地址 (偏移64字节)
// - 使用写入计数器区分最新数据(计数器跳过 0,比较用有符号差值容忍回绕)
constexpr u32 NODE_ID_PAGE_ADDR = 0x0801E800; // 节点 ID 主存储页 (1KB,独立区域)
constexpr u32 NODE_ID_PRIMARY_ADDR = 0x0801E800; // 主备份地址
constexpr u32 NODE_ID_BACKUP_ADDR = 0x0801EC00; // 备用备份地址(独立 1KB 页)
constexpr u32 NODE_ID_MAGIC = 0x4E4F4445; // 魔数 'NODE' 用于验证数据有效性
constexpr u16 DEFAULT_NODE_ID = 1; // 默认节点 ID
constexpr u16 BROADCAST_NODE_ID = 0x1FFF; // 广播 ID (所有节点响应)
constexpr u16 MAX_NODE_ID = 0x1FFE; // 最大有效节点 ID (0~0x1FFE)
constexpr u16 SETTABLE_NODE_ID_MAX = 0x00FF; // SET_NODE_ID 命令允许设置的上限 (1~0xFF)
// 节点 ID 存储结构 (存储在Flash中的格式)
// 节点 ID 存储结构 (主备份/备用备份各一份,每份 16 字节,位于各自页首)
// 地址偏移 0x00: 魔数 (0x4E4F4445 = 'NODE')
// 地址偏移 0x04: 节点 ID (u16)
// 地址偏移 0x06: 保留 (u16,填充为0xFFFF)
// 地址偏移 0x08: 写入计数器 (u32,用于区分最新数据)
// 地址偏移 0x0C: CRC32 校验 (u32)
constexpr u8 NODE_ID_STRUCT_SIZE = 0x10; // 结构体大小 16 字节
constexpr u8 NODE_ID_MAGIC_OFFSET = 0x00; // 魔数偏移
constexpr u8 NODE_ID_VALUE_OFFSET = 0x04; // 节点ID值偏移
constexpr u8 NODE_ID_COUNTER_OFFSET = 0x08; // 写入计数器偏移
constexpr u8 NODE_ID_CRC_OFFSET = 0x0C; // CRC偏移
// BOOT 相关
// 版图/页大小核验:以下 6 个 OTA 标志位共处 0x0801E000 所在的 1KB Flash 页。
// OTA_SET 会整页擦除该页,故必须保证 FMC 页大小为 1KB(0x400),否则会连带擦除
// 相邻的 SLEEP_CONFIG(0x0801E400)/NODE_ID(0x0801E800)。GD32C103CB 实测页大小为
// 1KB(睡眠/节点 ID/EEPROM 分居相邻 1KB 页且互不影响即为佐证),与 FMC_PAGE_SIZE 一致。
constexpr u32 OTA_CONFIG_NUM = 6;
constexpr u32 n_APP_LATEST_AVAL_ADDR = 0x0801E000; // 最新 APP 可用地址
constexpr u32 n_APP_BACKUP_AVAL_ADDR = 0x0801E004; // 备份 APP 可用地址
@@ -87,10 +187,32 @@ constexpr u32 OTA_AVAL = 0x00000000; // 全零表示可用
constexpr u8 PP = 11;
constexpr float DIR = 1.0f; // 默认方向:-1.0f 反转,1.0f 正转
constexpr u16 REDUCTION_RATIO = 25;
constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 当前电流传感器的零点偏移
constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 电流通道零点偏移(V),
// 与 INA240 中点 1.65V / DRV8323 SO 中点 VREF/2 一致
// 三相电流零点配平:配平值(各通道实测零偏折算电流)超过该限值视为
// 电流检测初始化错误,随 init_status 阻止启动
constexpr float CURRENT_ZERO_CAL_LIMIT = 0.1f; // 100mA
// ===================== 初始化错误码(CAN STATUS 帧 InitStatus 反馈) =====================
// hardware_init 按模块置位(可位或组合),InitStatus=0 表示全部初始化正常;
// 信号定义见 DBC v0.74 的 STATUS 帧字节 32~35
constexpr u32 INIT_ERR_ANGLE_SENSOR = 1u << 0; // 角度传感器 KTH7823 初始化错误
constexpr u32 INIT_ERR_ADC = 1u << 1; // 采样器/ADC 初始化错误
constexpr u32 INIT_ERR_CAN = 1u << 2; // CAN 初始化错误
constexpr u32 INIT_ERR_ANG_KALMAN = 1u << 3; // 角度滤波器初始化错误
constexpr u32 INIT_ERR_VEL_KALMAN = 1u << 4; // 速度滤波器初始化错误
constexpr u32 INIT_ERR_FOC = 1u << 5; // FOC 初始化错误
constexpr u32 INIT_ERR_CURRENT_BAL = 1u << 6; // 电流检测:零点配平值超限(>100mA)
constexpr u32 INIT_ERR_CURRENT_NOFRAME = 1u << 7; // 电流检测:无采样帧(触发链路异常)
constexpr u32 INIT_ERR_GATE_DRIVER = 1u << 8; // 门驱动 bringup 错误
extern volatile u32 init_error_flags; // 初始化错误位图,定义在 main.cpp
constexpr float HOLD_PWM_DUTY = 0.1f;
constexpr u16 ANGLESENS_OFFSET = 0;
constexpr float ANGLESENS_CALIBRATION_ANGLE = 83.0f;
// 电角度零位校准:施加的对齐占空比与稳定等待时间
constexpr float ALIGN_PWM_DUTY = 0.25f;
constexpr u32 ALIGN_SETTLE_MS = 1000;
constexpr float CALIB_MAX_SPEED = 5.0f; // 输出级速度阈值(deg/s),超过则拒绝校准
constexpr float KE_FACTOR = 0.0f;
// 角度/速度 α-β 观测器参数
@@ -118,17 +240,62 @@ constexpr float CURRENT_KALMAN_MEASUREMENT_NOISE_R = 5e-5f; // 增大测量噪
// PID 配置
constexpr float DEFAULT_current_loop_p = 0.1f / 100 * 12; // 电流 P
constexpr float current_loop_Ki = 0.55f; // 电流 P
constexpr float current_loop_Kd = 3e-4f; // 电流 P
// 电流反馈噪声大(CurrentKalman 稳态增益≈1,几乎无滤波),D 项只放大噪声,置零
constexpr float current_loop_Kd = 0.0f;
constexpr float DEFAULT_vel_loop_p = 0.018f / 100 * 12; // 速度 P
constexpr float DEFAULT_vel_loop_p = 0.005f / 100 * 12; // 速度 P
constexpr float vel_loop_Ku = 3.600000e-03 * 0.7 * 100 / 12;
constexpr float vel_loop_Tu = 17.0f * 1e-3f;
constexpr float vel_loop_Ki = 1.2 * vel_loop_Ku / vel_loop_Tu;
// 速度 D 恢复(提供加速度阻尼);现走"测量微分+1ms低通"路径,无目标阶跃踢、
// 噪声被低通抑制,比旧的裸误差微分安全
constexpr float vel_loop_Kd = 0.075 * vel_loop_Ku * vel_loop_Tu * 2;
// PID 抗饱和与微分滤波(负载稳定性优化,2026-08)
// 抗饱和主体为条件积分(饱和方向冻结,见 pid.cpp);此增益仅用于
// bleed_integral() 外部饱和泄放(前馈叠加超限时),且带同号保护不过零
constexpr float ANTI_WINDUP_GAIN = 0.3f; // 外部饱和泄放比例
constexpr float DERIV_LPF_TAU = 0.001f; // 微分项一阶低通时间常数 (s)
constexpr float MIN_LIMIT_VOLTAGE = 0.1f; // P 增益换算分母下限,防 limit_voltage=0 除零
// mode2 角度同步 Uq 前馈补偿:固定 Uq 下的真实速度波动不应从反馈中滤掉,
// 而应按物理 360° 提前补偿负载/阻力差异,降低速度 PID 的周期性纠偏量。
constexpr bool MODE2_UQ_FF_ENABLE = true;
constexpr bool MODE2_UQ_FF_LEARN_ENABLE = true;
constexpr u16 MODE2_UQ_FF_TABLE_SIZE = 180;
constexpr float MODE2_UQ_FF_LEARN_ALPHA = 0.01f; // 越小越慢,避免误学瞬态
constexpr float MODE2_UQ_FF_MIN_TARGET = 1.5f; // 目标速度过低时不学习
constexpr float MODE2_UQ_FF_GAIN = 0.003f; // 速度误差(deg/s)到前馈电压(V)的学习比例
constexpr float MODE2_UQ_FF_MAX = 3.5f; // 前馈补偿电压限幅(V)
constexpr float MODE2_UQ_FF_STOP_SPEED = 2.0f; // 目标/实际速度低于该值时不输出前馈
constexpr float MODE2_UQ_FF_BRAKE_ERR = 3.0f; // 明显超速/刹车时不输出前馈,单位同 velocity_error
constexpr float MODE2_UQ_FF_LEARN_ERR_MAX = 375.0f; // 速度误差带内才学习(≈15°/s×25),防启停瞬态污染表
// mode3 角度同步 Uq 前馈补偿:单独建表,避免和 mode2 互相污染。
constexpr bool MODE3_UQ_FF_ENABLE = true;
constexpr bool MODE3_UQ_FF_LEARN_ENABLE = true;
constexpr u16 MODE3_UQ_FF_TABLE_SIZE = 180;
constexpr float MODE3_UQ_FF_LEARN_ALPHA = 0.01f;
constexpr float MODE3_UQ_FF_MIN_TARGET = 2.5f; // mode3 内部目标速度过低时不学习
constexpr float MODE3_UQ_FF_GAIN = 0.3f; // mode3 速度误差未乘减速比,故增益独立于 mode2
constexpr float MODE3_UQ_FF_MAX = 3.5f;
constexpr float MODE3_UQ_FF_STOP_SPEED = 2.0f; // mode3 内部目标/实际速度低于该值时不输出前馈
constexpr float MODE3_UQ_FF_BRAKE_ERR = 3.0f; // 明显超速/刹车时不输出前馈,单位同 velocity_error
constexpr float MODE3_UQ_FF_LEARN_ERR_MAX = 15.0f; // 速度误差带内(°/s)才学习,防启停瞬态污染表
constexpr float DEFAULT_vel_angloop_p = 0.4f / 100; // 速度 速度 - 角度 P
constexpr float vel_angloop_Ki = 5.0f; // 速度 速度 - 角度 I
constexpr float vel_angloop_Kd = 5e-05f; // 速度 速度 - 角度 D
constexpr float vel_angloop_Kd = 5e-05f; // 速度 速度 - 角度 D(恢复,走测量微分+低通)
// mode3 低速/静止附近速度内环增益调度(D 保持满值):
// P/I 各自独立地板——I 地板更低,停止瞬间积分充电速率仅为全速的 15%,
// 与静摩擦相互作用(到位 hunting:停下瞬间小幅来回抖动)的能量被大幅压缩;
// P 地板保持足够刚度,减小突破静摩擦所需的误差。
constexpr bool MODE3_LOW_SPEED_P_SCALE_ENABLE = true;
constexpr float MODE3_LOW_SPEED_P_MIN_SCALE = 0.5f;
constexpr float MODE3_LOW_SPEED_I_MIN_SCALE = 0.15f;
constexpr float MODE3_LOW_SPEED_P_LOW = 1.0f;
constexpr float MODE3_LOW_SPEED_P_FULL = 8.0f;
constexpr float DEFAULT_angle_angloop_p =
150.0f / 100 * 12; // 角度 速度 - 角度 P
@@ -143,45 +310,39 @@ constexpr float angle_MIT_Kd = 8e-04f;
constexpr float DEFAULT_vel_MIT_p = 5.0f / 100; // 速度 MIT P
constexpr float vel_MIT_Ki = 15.0f;
constexpr float vel_MIT_Kd = 1e-04f;
constexpr float vel_MIT_Kd = 1e-04f; // 速度 MIT D(恢复,走测量微分+低通)
constexpr float DEFAULT_current_MIT_p = 0.2f / 100 * 12; // 电流 MIT P
constexpr float current_MIT_Ki = 0.1f;
constexpr float current_MIT_Kd = 3e-4f;
constexpr float current_MIT_Kd = 0.0f; // 电流 MIT D(置零,同 current_loop_Kd)
// 常量定义
constexpr u8 SIZE_FLOAT = sizeof(float);
constexpr u8 SIZE_U32 = sizeof(uint32_t);
constexpr float ADC_MAX = 65536.0f;
constexpr float PI = 3.14159265358979323846f;
constexpr float INV_PI_3 = 3.0f / PI;
constexpr float PI_3 = PI / 3.0f;
constexpr float _1_SQRT3 = 0.57735026919f; // 1/sqrt(3)
constexpr float _3PI_2 = 4.71238898038f;
constexpr float SQRT3 = 1.7320508075688772f;
constexpr float SQRT3DV2 = 0.8660254037844386f; // sqrt(3)/2
constexpr float COS_PI_3 = 0.5f;
constexpr float ANGLE_FACTOR = 25.0f / 180.0f * PI;
constexpr float DEG2RAD = 1.0f / 180.0f * PI;
constexpr float RAD2DEG = 180.0f / PI; // 修正 RAD2DEG 定义,应该是 180/π
constexpr u8 DEFAULT_VALUE = 0;
constexpr u8 CONFIG_SAVED_NUM = 6;
// 三角函数快速计算 LUT
constexpr u16 LUT_SIZE = 1024;
constexpr u16 LUT_MASK = (LUT_SIZE - 1);
constexpr u16 LUT_SCALE = (LUT_SIZE / (2.0f * PI));
// CAN 相关
// 注意:以下为 CAN 基础 ID(只保留低 16 位),实际发送/接收时会自动叠加节点 ID
// 到高 16 位 格式:[节点 ID(16 位)][基础 ID(16 位)] 例如:节点 ID=1,发送命令
// ID = (1 << 16) | CANFD_CMD_ID = 0x00010200
constexpr u16 DEV_CAN0_ID = 0x0000;
constexpr u16 DEV_CAN0_MASK = 0x0000;
constexpr u16 DEV_CAN_LOOPBACK_ID = 0x0000;
// 反馈基础 ID
constexpr u16 CANFD_STATUS_ID = 0x0100; // 状态反馈基础 ID
constexpr u16 CANFD_CONFIG_ID = 0x0101; // 配置信息基础 ID
constexpr u16 CANFD_VERSION_ID = 0x00FF; // 版本信息基础 ID
// 周期反馈帧刷新周期(单位 ms,基于 1ms CAN 任务节拍分频;0=停发),
// 可通过 SET_NODE_ID(0x0400) 扩展格式运行时修改并持久化,
// 未配置时使用以下默认值(CONFIG 帧内容为控制模式/目标设定回显,
// 不实时依赖,默认 250Hz 降低总线占用)
constexpr u16 CANFD_TX_PERIOD_STATUS_DEFAULT_MS = 1; // STATUS 帧默认 1kHz
constexpr u16 CANFD_TX_PERIOD_CONFIG_DEFAULT_MS = 4; // CONFIG 帧默认 250Hz
// 0x0400/0x0401 扩展格式(DLC>=2/4)的参数选择器:0x0000=节点 ID,
// 其余选择器直接使用目标反馈帧的基础 ID(如 0x0100/0x0101)
constexpr u16 CANFD_PARAM_NODE_ID = 0x0000;
// 命令基础 ID
constexpr u16 CANFD_CMD_ID = 0x0200; // 控制命令基础 ID
constexpr u16 CANFD_RESET_ID = 0x0301; // 重置命令基础 ID
@@ -191,14 +352,10 @@ constexpr u16 CANFD_GET_NODE_ID = 0x0401; // 查询节点 ID 基础 ID
// OTA 相关基础 ID 不受到node ID影响
constexpr u16 CANFD_BOOT_ID = 0x0000; // BOOT 模式基础 ID (进入 BOOT 使用此 ID)
constexpr u16 CANFD_OTA_REQ_ID = 0x0100; // OTA 请求基础 ID
constexpr u16 CANFD_OTA_DATA_ID = 0x0101; // OTA 数据基础 ID
constexpr u16 CANFD_OTA_ACK_ID = 0x0102; // OTA 确认基础 ID
constexpr u16 CANFD_OTA_WAIT_ID = 0x0103; // OTA 等待基础 ID
// ADC
constexpr u8 ADC_FILTER_SIZE = 6;
constexpr u8 BLOCK_SIZE = ADC_FILTER_SIZE * SIZE_FLOAT;
constexpr float ERROR_ADC_VAL = 255.0f;
constexpr float MAX_TEMP = 150.0f;
constexpr float MIN_TEMP = -40.0f;
@@ -208,12 +365,10 @@ constexpr float MAX_VCC = 65.0f;
constexpr float MIN_VCC = -0.1f;
// ang_sensor
constexpr u16 VEL_FILTER_SIZE = 4;
constexpr u16 DEFAULT_COMMAND = 0x0000;
constexpr u16 FAULT_RECEIVE = 0xFFFF;
constexpr float ANGLE_WRAP_THRESHOLD = 300.0f;
constexpr float ANGLE_WRAP_VALUE = 360.0f;
constexpr float RAD_FACTOR = 1.0f / ADC_MAX * 2.0f * PI;
constexpr float ANGLESENS_FACTOR = 1.0f / ADC_MAX * 360.0f;
// TIMER2-任务调度
@@ -222,15 +377,24 @@ constexpr int TIMER2_PERIOD = 49; // 1 / 1MHz * 50 = 50us
constexpr int MOTOR_PERIOD = 1; // 控制每50us执行一次,无分频
constexpr float TIME_SLICE =
(TIMER2_PERIOD + 1) * (TIMER2_PRESCALER + 1) / 120000000.0f; // 周期
constexpr float TIMER2_FREQUENCY = 1 / TIME_SLICE; // 频率
constexpr float MOTOR_FREQUENCY = 1 / TIME_SLICE / MOTOR_PERIOD; // 频率
// TIMER1-PWM
constexpr int TIMER1_PRESCALER = 0; // 不分频,计数器时钟为 120MHz
constexpr int TIMER1_PERIOD = 2999; // 120MHz / (0+1) / (2999+1) = 40kHz计数, 中心对齐实际PWM 20kHz
constexpr int TIMER1_DEFAULT_PULSE = 0;
constexpr int TIMER1_CH3_COMPARE_VALUE =
(TIMER1_PERIOD + 1) * 0.41 - 1; // PWM 周期 41% 位置触发ADC采样
// CH3 为固定 ADC 采样触发点(TRGO=O3CPRE)。低边采样要求触发落在"三相下管
// 同时导通"的窗口内:中心对齐 PWM0 下 INx 高(上管导通)区间以计数谷底
// CNT=0 为中心,INx 低(下管导通)区间以计数峰顶 CNT=PERIOD 为中心,故采样
// 点必须贴近峰顶(在线采样时代取 41% 是任意有效点,直接沿用会采到上管
// 导通期、三相电流恒读零)。
// 取 PERIOD-10:紧贴峰顶。中心对齐下每周期匹配两次(上升沿 2989 / 下降沿
// 3011,仅差 22 计数),无论第二次触发被 ADC 忽略(注入转换忙)还是重启
// 序列,帧的等效采样位置都在峰顶附近——行为确定,不依赖芯片的触发丢弃
// 策略;配合 ADC.cpp 电流通道 13.5 周期短采样,最大占空比 ~90% 时三相样本
// 仍全部落在下管导通窗内。占空比更高时序列末位的 W 相先落出窗口(读数
// 趋零、U+V+W≠0),软件侧需对占空比限幅。
constexpr int TIMER1_CH3_COMPARE_VALUE = TIMER1_PERIOD - 10;
// 电压检测参数(基于跌幅检测,不依赖固定额定电压)
// 快速检测层(fast_voltage_check,100us周期):
@@ -245,4 +409,19 @@ constexpr float VBUS_ABSOLUTE_MIN = 12.0f; // 绝对最低电压 (V)
constexpr u32 VBUS_DEBOUNCE_COUNT = 5; // 快速检测:防抖计数 (5次×100us = 500us)
constexpr float VBUS_RECOVERY_RATIO = 0.9f; // 恢复比例(基准电压的90%)
#endif
// ===================== 门驱动 nFAULT(STATUS 帧 motStatus / MotorStatus)=====================
// PB2 读门驱动 nFAULT(开漏低有效,固件已配内部上拉)。DRV8313/DRV8323 的
// UVLO、热关断、过流都会把它拉低,因此它就是"电机侧出错"的硬件凭据。
// 轮询点在 thermalController.update()(TASK_THERMAL_CONTROL,1ms)。
constexpr u8 DRV_FAULT_DEBOUNCE_MS = 3u; // 连续 N 次 1ms 采样为低才判故障
// 解除禁止后驱动需要重新建立:DRV8313 手册 7.4.1 给 ≈1ms,按 50us 控制节拍取 20 拍。
// 建立期内输出保持 0、不派发控制函数(见 sys/run_inhibit.cpp)。
constexpr u8 DRV_WAKE_SETTLE_50US = 20u;
#endif
+229 -71
View File
@@ -1,3 +1,9 @@
/**
* @file sys/flash.cpp
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "flash.h"
#include "FOC.h"
#include "ang_sensor.h"
@@ -12,15 +18,19 @@
// EEP 调试宏定义
#if EEP_DEBUG
/** @brief EEP_DEBUG_PRINT 宏定义 */
#define EEP_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief EEP_DEBUG_PRINT 宏定义 */
#define EEP_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// OTA 调试宏定义
#if OTA_DEBUG
/** @brief OTA_DEBUG_PRINT 宏定义 */
#define OTA_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief OTA_DEBUG_PRINT 宏定义 */
#define OTA_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
@@ -29,6 +39,12 @@ extern angle_sensor KTH7823;
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC; // FOC 控制
/**
* @brief 写入 write_ADDRESS 相关数据
* @param[in] address 参数
* @param[in] data 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum write_ADDRESS(u32 address, u32 data) {
fmc_unlock();
fmc_flag_clear(FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR);
@@ -57,6 +73,28 @@ u8 FlashEEPROM::init() {
// 读取系统配置
read_saved_system_config();
// 加载电角度零位校准角(首次无存储则用默认值)
float calib = ANGLESENS_CALIBRATION_ANGLE;
if (readFloat(ADDR_CALIBRATION_ANGLE, &calib) != FLASH_EEPROM_OK) {
calib = ANGLESENS_CALIBRATION_ANGLE;
} else if (calib == 0.0f) {
// 校准值为 0 视为未校准/异常,回退默认值,避免换向角度错误
calib = ANGLESENS_CALIBRATION_ANGLE;
}
KTH7823.set_calibration(calib);
// 加载周期反馈帧刷新率(独立 u16 记录,无记录/读取失败回退默认值,
// 不影响 EEPROMStatus 的 CRC 参数组判定)
u16 tx_period = 0;
tx_period_status_ms =
(read(ADDR_TX_PERIOD_STATUS, &tx_period) == FLASH_EEPROM_OK)
? tx_period
: CANFD_TX_PERIOD_STATUS_DEFAULT_MS;
tx_period_config_ms =
(read(ADDR_TX_PERIOD_CONFIG, &tx_period) == FLASH_EEPROM_OK)
? tx_period
: CANFD_TX_PERIOD_CONFIG_DEFAULT_MS;
if (EEPROMStatus == FLASH_EEPROM_OK) {
return FLASH_EEPROM_OK;
} else {
@@ -163,8 +201,9 @@ FlashEepromStatus FlashEEPROM::read(u8 virtualAddr, u16 *data) const {
// 检查是否是有效数据(非擦除状态)
if (data_word != 0xFFFFFFFF) {
u8 addr = (u8)(data_word & DATA_ADDR_MASK);
// 找到匹配的虚拟地址
if (addr == virtualAddr) {
// u16 记录保留字节恒为 0(write() 写入格式 [data:16][rsvd:8][addr:8]),
// 借此排除 EEPROM 头部区 CRC 字(低 8 位恰为虚拟地址)的误匹配
if (addr == virtualAddr && (data_word & 0x0000FF00) == 0) {
// 右移 16 位以提取高 16 位的 u16 数据
*data = (u16)((data_word & DATA_U16_MASK) >> 16);
EEP_DEBUG_PRINT("read u16: addr=0x%x, val=%d\r\n", address, *data);
@@ -461,20 +500,53 @@ FlashEepromStatus FlashEEPROM::save_system_config(bool saved) {
CRC_NUM = crc_block_data_calculate(crc_data_words, CONFIG_SAVED_NUM);
writeCRC(CRC_NUM, saved);
// 周期反馈帧刷新率(独立记录,非 CRC 组):从 RAM 副本追加写入。
// 放在 CRC 组保存之后,写入失败不影响参数组状态(下次上电回退默认值);
// 页满 format() 重建时也经此路径从 RAM 副本恢复,刷新率配置不丢失
status = write(ADDR_TX_PERIOD_STATUS, tx_period_status_ms);
if (status != FLASH_EEPROM_OK) {
EEP_DEBUG_PRINT("save tx period STATUS failed (%d)\r\n", status);
}
status = write(ADDR_TX_PERIOD_CONFIG, tx_period_config_ms);
if (status != FLASH_EEPROM_OK) {
EEP_DEBUG_PRINT("save tx period CONFIG failed (%d)\r\n", status);
}
EEPROMStatus = FLASH_EEPROM_OK;
return FLASH_EEPROM_OK;
}
/**
* @brief 读取角度数据从 Flash
* @return float: 角度数据
* @brief 将 RAM 副本中的两帧刷新周期写入 Flash
* @note 独立 u16 记录(非 CRC 参数组),值不变时 write() 自动跳过以减少
* 磨损。运行时 SET 命令修改 RAM 副本后调用本函数持久化;若触发
* 页满 format() 重建,save_system_config() 会从 RAM 副本重写这两
* 条记录,本次修改不会丢失。
* @return FlashEepromStatus 操作状态
*/
float FlashEEPROM::read_angle() {
float angle = DEFAULT_VALUE;
if (readFloat(ADDR_ANGLE, &angle) != FLASH_EEPROM_OK) {
angle = DEFAULT_VALUE;
FlashEepromStatus FlashEEPROM::write_tx_periods() {
FlashEepromStatus status = write(ADDR_TX_PERIOD_STATUS, tx_period_status_ms);
if (status != FLASH_EEPROM_OK) {
EEP_DEBUG_PRINT("write_tx_periods: STATUS period write failed (%d)\r\n",
status);
return status;
}
return angle;
status = write(ADDR_TX_PERIOD_CONFIG, tx_period_config_ms);
if (status != FLASH_EEPROM_OK) {
EEP_DEBUG_PRINT("write_tx_periods: CONFIG period write failed (%d)\r\n",
status);
}
return status;
}
/**
* @brief 保存电角度零位校准角到 Flash
* @param[in] angle 校准角(度)
* @return FlashEepromStatus 操作状态
*/
FlashEepromStatus FlashEEPROM::save_calibration_angle(float angle) {
return writeFloat(ADDR_CALIBRATION_ANGLE, angle);
}
// ============================================================================
@@ -495,6 +567,35 @@ static u32 calculate_node_id_crc(u16 node_id, u32 counter) {
return crc_block_data_calculate(crc_data, 2);
}
/*!
* @brief 比较写入计数器新旧(有符号差值比较,容忍 u32 回绕)
* @param a: 计数器 a
* @param b: 计数器 b
* @retval bool: a 比 b 新则为 true
* @note 计数器写入时跳过 0,只要两次写入间隔不超过 2^31 次即可正确比较
*/
static bool node_id_counter_newer(u32 a, u32 b) {
return (int32_t)(a - b) > 0;
}
/*!
* @brief 擦除单个 1KB Flash 页
* @param page_addr: 页起始地址(1KB 对齐)
* @retval FlashEepromStatus: 操作状态
*/
static FlashEepromStatus erase_flash_page(u32 page_addr) {
fmc_unlock();
fmc_state_enum fmc_status = fmc_page_erase(page_addr);
if (fmc_status != FMC_READY) {
fmc_lock();
EEP_DEBUG_PRINT("Page erase failed at 0x%08X, status=%d\r\n",
page_addr, fmc_status);
return FLASH_EEPROM_ERASE_ERROR;
}
fmc_lock();
return FLASH_EEPROM_OK;
}
/*!
* @brief 从指定地址读取节点 ID 数据结构
* @param addr: Flash 地址
@@ -583,60 +684,87 @@ static FlashEepromStatus write_node_id_struct(u32 addr, u16 node_id, u32 counter
}
/*!
* @brief 擦除节点 ID 存储页
* @brief 擦除节点 ID 存储区(主备份页 + 备用备份页,恢复默认值)
* @retval FlashEepromStatus: 操作状态
* @note 仅用于恢复出厂/调试场景,会同时清除两份备份
*/
FlashEepromStatus FlashEEPROM::eraseNodeId() {
fmc_state_enum fmc_status;
EEP_DEBUG_PRINT("Erasing Node ID page at 0x%08X\r\n", NODE_ID_PAGE_ADDR);
fmc_unlock();
fmc_status = fmc_page_erase(NODE_ID_PAGE_ADDR);
if (fmc_status != FMC_READY) {
fmc_lock();
EEP_DEBUG_PRINT("Node ID page erase failed, status=%d\r\n", fmc_status);
return FLASH_EEPROM_ERASE_ERROR;
EEP_DEBUG_PRINT("Erasing Node ID pages at 0x%08X / 0x%08X\r\n",
NODE_ID_PRIMARY_ADDR, NODE_ID_BACKUP_ADDR);
FlashEepromStatus status = erase_flash_page(NODE_ID_PRIMARY_ADDR);
if (status != FLASH_EEPROM_OK) {
return status;
}
fmc_lock();
return FLASH_EEPROM_OK;
return erase_flash_page(NODE_ID_BACKUP_ADDR);
}
/*!
* @brief 从 Flash 读取节点 ID
* @brief 从 Flash 读取节点 ID(双重备份回退机制)
* @param node_id: 输出节点 ID 值
* @retval FlashEepromStatus: 操作状态
* @note 读取主备份与备用备份两份记录:
* - 两份均有效:取写入计数器更新的一份
* - 仅一份有效:回退使用该份(另一份损坏或未写入)
* - 均无效:返回 DATA_NOT_FOUND,由调用方使用默认值
*/
FlashEepromStatus FlashEEPROM::readNodeId(u16 *node_id) const {
if (node_id == nullptr) {
return FLASH_EEPROM_INVALID_ADDR;
}
u16 id_value = 0;
u32 counter = 0;
u16 id_primary = 0, id_backup = 0;
u32 counter_primary = 0, counter_backup = 0;
// 从主存储区读取
FlashEepromStatus status = read_node_id_struct(NODE_ID_PRIMARY_ADDR, &id_value, &counter);
if (status == FLASH_EEPROM_OK) {
*node_id = id_value;
EEP_DEBUG_PRINT("Node ID read from primary (counter=%u): %d\r\n", counter, *node_id);
return FLASH_EEPROM_OK;
FlashEepromStatus status_primary = read_node_id_struct(
NODE_ID_PRIMARY_ADDR, &id_primary, &counter_primary);
FlashEepromStatus status_backup = read_node_id_struct(
NODE_ID_BACKUP_ADDR, &id_backup, &counter_backup);
if (status_primary == FLASH_EEPROM_OK &&
status_backup == FLASH_EEPROM_OK) {
// 双备份均有效:取计数器更新的一份(计数器相同时优先主备份)
if (node_id_counter_newer(counter_backup, counter_primary)) {
*node_id = id_backup;
EEP_DEBUG_PRINT("Node ID read from backup (counter=%u): %d\r\n",
counter_backup, *node_id);
} else {
*node_id = id_primary;
EEP_DEBUG_PRINT("Node ID read from primary (counter=%u): %d\r\n",
counter_primary, *node_id);
}
} else if (status_primary == FLASH_EEPROM_OK) {
// 主备份有效,备用备份损坏/为空:回退主备份
*node_id = id_primary;
EEP_DEBUG_PRINT(
"Node ID fallback to primary (backup status=%d): %d\r\n",
status_backup, *node_id);
} else if (status_backup == FLASH_EEPROM_OK) {
// 主备份损坏/为空,备用备份有效:回退备用备份
*node_id = id_backup;
EEP_DEBUG_PRINT(
"Node ID fallback to backup (primary status=%d): %d\r\n",
status_primary, *node_id);
} else {
EEP_DEBUG_PRINT("Node ID read failed from primary: status=%d\r\n", status);
EEP_DEBUG_PRINT("Node ID not found in both slots (primary=%d, backup=%d)\r\n",
status_primary, status_backup);
return FLASH_EEPROM_DATA_NOT_FOUND;
}
return FLASH_EEPROM_OK;
}
/*!
* @brief 保存节点 ID 到 Flash(每次写入前先擦除整个页面,写入后校验)
* @brief 保存节点 ID 到 Flash(乒乓式双重备份,写入后校验)
* @param node_id: 要保存的节点 ID 值
* @retval FlashEepromStatus: 操作状态
* @note 由于Flash只能将1写为0,不能将0写为1,因此每次写入新数据前
* 需要先擦除整个页面。简化方案:不再使用备用备份区,
* 每次写入前擦除页面,然后写入主存储区。
* 写入后立即校验数据,如果不对则再次写入,最多3次。
* @note 主备份与备用备份分属两个独立 1KB Flash 页。写入流程:
* 1. 读取两份备份,定位计数器最新的有效记录作为基准
* 2. 选择"非最新"的一页作为写入目标(乒乓交替)
* 3. 只擦除并写入目标页,另一页全程保有旧的有效记录,
* 掉电时至少一份数据存活
* 4. 写入后立即校验,失败则重试,最多3次
* 某一页损坏时无需专门修复:下一次写入目标即切换到该页,
* 擦除+重写天然完成自愈。
*/
FlashEepromStatus FlashEEPROM::writeNodeId(u16 node_id) {
if (node_id > MAX_NODE_ID) {
@@ -644,33 +772,59 @@ FlashEepromStatus FlashEEPROM::writeNodeId(u16 node_id) {
return FLASH_EEPROM_INVALID_ADDR;
}
// 获取当前写入计数器(从主存储区读取)
u16 current_id = 0;
u32 current_counter = 0;
FlashEepromStatus primary_status = read_node_id_struct(NODE_ID_PRIMARY_ADDR, &current_id, &current_counter);
// 计数器 +1
u32 new_counter = current_counter + 1;
// 如果计数器溢出(0xFFFFFFFF + 1 = 0),则使用1
// 读取两份备份,确定当前最新有效记录(计数器基准 + 乒乓目标选择)
u16 id_primary = 0, id_backup = 0;
u32 counter_primary = 0, counter_backup = 0;
FlashEepromStatus status_primary = read_node_id_struct(
NODE_ID_PRIMARY_ADDR, &id_primary, &counter_primary);
FlashEepromStatus status_backup = read_node_id_struct(
NODE_ID_BACKUP_ADDR, &id_backup, &counter_backup);
bool primary_latest = false;
bool backup_latest = false;
if (status_primary == FLASH_EEPROM_OK &&
status_backup == FLASH_EEPROM_OK) {
// 两份均有效:计数器新的为最新(相同时视为主备份最新)
if (node_id_counter_newer(counter_backup, counter_primary)) {
backup_latest = true;
} else {
primary_latest = true;
}
} else if (status_primary == FLASH_EEPROM_OK) {
primary_latest = true;
} else if (status_backup == FLASH_EEPROM_OK) {
backup_latest = true;
}
// 计数器 +1(跳过 0;两份均无效时从 1 开始)
u32 new_counter = 1;
if (primary_latest) {
new_counter = counter_primary + 1;
} else if (backup_latest) {
new_counter = counter_backup + 1;
}
if (new_counter == 0) {
new_counter = 1;
}
// 先擦除整个页面
EEP_DEBUG_PRINT("Erasing Node ID page before write\r\n");
FlashEepromStatus status = eraseNodeId();
if (status != FLASH_EEPROM_OK) {
return status;
}
// 乒乓写入:目标为"非最新有效"的一页,最新有效的一页保持不动
u32 target_addr = primary_latest ? NODE_ID_BACKUP_ADDR : NODE_ID_PRIMARY_ADDR;
// 写入并校验,最多3次
// 写入并校验,最多3次(重试期间另一页的旧记录始终有效)
/** @brief MAX_WRITE_RETRY 宏定义 */
#define MAX_WRITE_RETRY 3
for (u8 retry = 0; retry < MAX_WRITE_RETRY; retry++) {
// 写入主存储区
EEP_DEBUG_PRINT("Writing to primary (counter=%u, retry=%d)\r\n", new_counter, retry);
FlashEepromStatus write_status = write_node_id_struct(NODE_ID_PRIMARY_ADDR, node_id, new_counter);
EEP_DEBUG_PRINT("Writing to 0x%08X (counter=%u, retry=%d)\r\n",
target_addr, new_counter, retry);
FlashEepromStatus status = erase_flash_page(target_addr);
if (status != FLASH_EEPROM_OK) {
continue;
}
FlashEepromStatus write_status =
write_node_id_struct(target_addr, node_id, new_counter);
if (write_status != FLASH_EEPROM_OK) {
EEP_DEBUG_PRINT("Node ID write failed: status=%d\r\n", write_status);
continue;
@@ -679,20 +833,18 @@ FlashEepromStatus FlashEEPROM::writeNodeId(u16 node_id) {
// 立即校验数据
u16 read_id = 0;
u32 read_counter = 0;
FlashEepromStatus read_status = read_node_id_struct(NODE_ID_PRIMARY_ADDR, &read_id, &read_counter);
if (read_status == FLASH_EEPROM_OK && read_id == node_id && read_counter == new_counter) {
EEP_DEBUG_PRINT("Node ID saved and verified successfully: %d (counter=%u)\r\n", node_id, new_counter);
FlashEepromStatus read_status =
read_node_id_struct(target_addr, &read_id, &read_counter);
if (read_status == FLASH_EEPROM_OK && read_id == node_id &&
read_counter == new_counter) {
EEP_DEBUG_PRINT("Node ID saved and verified: %d (counter=%u, slot=0x%08X)\r\n",
node_id, new_counter, target_addr);
return FLASH_EEPROM_OK;
} else {
EEP_DEBUG_PRINT("Node ID verification failed: read_id=%d (expected=%d), read_counter=%u (expected=%u)\r\n",
read_id, node_id, read_counter, new_counter);
// 校验失败,重新擦除并写入
if (retry < MAX_WRITE_RETRY - 1) {
EEP_DEBUG_PRINT("Retrying...\r\n");
eraseNodeId();
}
}
EEP_DEBUG_PRINT("Node ID verify failed: read_id=%d (expected=%d), "
"read_counter=%u (expected=%u)\r\n",
read_id, node_id, read_counter, new_counter);
}
EEP_DEBUG_PRINT("Node ID save failed after %d retries\r\n", MAX_WRITE_RETRY);
@@ -705,7 +857,9 @@ FlashEepromStatus FlashEEPROM::writeNodeId(u16 node_id) {
* @return FlashEepromStatus: 状态
*/
FlashEepromStatus FlashEEPROM::writeCRC(u32 CRC_NUM, bool saved) {
static u32 next_address;
// 每次调用重新扫描可写入位置,不跨调用残留旧地址
// (循环至少执行一次,next_address 必被赋值)
u32 next_address = EEPROM_START_ADDRESS;
// 遍历 Flash 页面寻找下一个可写入 crc 的位置
for (u32 address = EEPROM_DATA_START_ADDRESS - SIZE_U32;
address > EEPROM_START_ADDRESS; address -= SIZE_U32) {
@@ -867,6 +1021,8 @@ u8 OTA_SET(u32 addr, u32 data) {
* @retval 无
*/
void jumpToBOOT() {
// 0. 进 BOOT 前保存系统配置与运行参数(兜底,确保数据落盘)
flashEEP.save_system_config(false);
// 1. 关闭所有中断
__disable_irq();
for (int i = 0; i < 8; i++) {
@@ -895,6 +1051,8 @@ void jumpToBOOT() {
uint32_t reset_handler = *(__IO uint32_t *)(BOOTLOADER_START_ADDR + 4);
typedef void (*AppEntryFunc)(void);
AppEntryFunc app_entry = (AppEntryFunc)reset_handler;
// 跳转前喂狗,给 BOOT 留出一个完整看门狗周期做早期初始化
fwdgt_counter_reload();
__set_MSP(msp_value);
// 7. 跳转
app_entry();
+116 -6
View File
@@ -1,13 +1,25 @@
/**
* @file sys/flash.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef __FLASH_H
#define __FLASH_H
#include "config.h"
#include "gd32c10x.h" // 添加GD32主头文件以确保外设函数声明可用
#include <cstdint>
// 修改后 - 强制4字节对齐
/** @brief ALIGN_4BYTE 宏定义 */
#define ALIGN_4BYTE(addr) ((addr) & 0xFFFFFFFC) // 4字节对齐
// EEPROM操作状态
/**
* @brief 匿名枚举 枚举定义
*/
typedef enum {
FLASH_EEPROM_OK = 0, // 操作成功
FLASH_EEPROM_WRITE_ERROR, // 写入错误
@@ -20,6 +32,9 @@ typedef enum {
} FlashEepromStatus;
// Flash EEPROM类
/**
* @brief FlashEEPROM 类或结构体
*/
class FlashEEPROM {
public:
// EEPROM emulation constants
@@ -41,47 +56,142 @@ class FlashEEPROM {
static constexpr u8 ADDR_VOLTAGE_LIMIT = 0x4; // 电压限制
static constexpr u8 ADDR_VEL_LIMIT = 0x05; // 角度数据存储地址
static constexpr u8 ADDR_NODE_ID = 0x06; // 节点 ID 存储地址
static constexpr u8 ADDR_CALIBRATION_ANGLE = 0x07; // 电角度零位校准角存储地址
static constexpr u8 ADDR_TX_PERIOD_STATUS = 0x08; // STATUS 帧刷新周期(ms)存储地址
static constexpr u8 ADDR_TX_PERIOD_CONFIG = 0x09; // CONFIG 帧刷新周期(ms)存储地址
bool angle_saved = false;
bool system_config_saved = false;
u8 EEPROMStatus;
// 周期反馈帧刷新周期 RAM 副本(ms,0=停发)。init() 从 Flash 加载,
// 无记录时回退 config.h 默认值;CAN_transmiter::init() 读取应用,
// SET 命令修改后经 write_tx_periods() 持久化
u16 tx_period_status_ms = CANFD_TX_PERIOD_STATUS_DEFAULT_MS;
u16 tx_period_config_ms = CANFD_TX_PERIOD_CONFIG_DEFAULT_MS;
// 构造函数
FlashEEPROM();
// 初始化Flash EEPROM模拟
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init();
// 从Flash EEPROM中读取数据
/**
* @brief 读取数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus read(u8 virtualAddr, u16 *data) const;
// 向Flash EEPROM写入数据
/**
* @brief 写入数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus write(u8 virtualAddr, u16 data);
// Float数据读相关函数
/**
* @brief 从 Flash EEPROM 读取浮点数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus readFloat(u8 virtualAddr, float *data) const;
// Float数据存储相关函数
/**
* @brief 向 Flash EEPROM 写入浮点数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus writeFloat(u8 virtualAddr, float data);
// 格式化Flash EEPROM页面
/**
* @brief 格式化存储区域
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus format();
/**
* @brief 保存系统配置参数
* @param[in] saved 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus save_system_config(bool saved);
/**
* @brief 读取已保存的系统配置参数
* @return u8 返回值
*/
u8 read_saved_system_config(void);
float read_angle();
/**
* @brief 保存电角度零位校准角到 Flash
* @param[in] angle 校准角(度)
* @return FlashEepromStatus 操作状态
*/
FlashEepromStatus save_calibration_angle(float angle);
// 节点 ID 独立读写函数(使用魔数+双重备份+计数器机制)
// 从 Flash 读取节点 ID(带魔数验证和双重备份回退)
// 周期反馈帧刷新率持久化(独立 u16 记录,不属于 CRC 参数组,
// 读取失败/无记录由调用方回退默认值,避免老设备升级后 CRC 全组失效)
/**
* @brief 将 RAM 副本中的两帧刷新周期写入 Flash(值不变自动跳过)
* @return FlashEepromStatus 操作状态
*/
FlashEepromStatus write_tx_periods();
// 节点 ID 独立读写函数(主/备双备份分属独立 Flash 页 + 魔数 + 计数器 + CRC)
// 从 Flash 读取节点 ID(双备份均有效取计数器最新,单份有效则回退)
/**
* @brief 读取节点 ID 配置
* @param[in] node_id 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus readNodeId(u16 *node_id) const;
// 保存节点 ID 到 Flash(写入主备份和备用备份)
// 保存节点 ID 到 Flash(乒乓写入非最新页,另一页保有旧值,掉电安全)
/**
* @brief 写入节点 ID 配置
* @param[in] node_id 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus writeNodeId(u16 node_id);
// 擦除节点 ID 存储区(恢复默认值)
// 擦除节点 ID 双备份存储页(恢复默认值)
/**
* @brief 擦除节点 ID 配置
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus eraseNodeId();
private:
/**
* @brief 写入 writeCRC 相关数据
* @param[in] CRC_NUM 参数
* @param[in] saved 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus writeCRC(u32 CRC_NUM, bool saved);
};
/**
* @brief flash_eeprom_example_main 函数
* @return void
*/
void flash_eeprom_example_main(void);
u8 OTA_SET(u32 addr, u32 data);
/**
* @brief move_app_data 函数
* @return fmc_state_enum 返回值
*/
fmc_state_enum move_app_data(void);
/**
* @brief 写入 write_ADDRESS 相关数据
* @param[in] address 参数
* @param[in] data 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum write_ADDRESS(u32 address, u32 data);
#endif /* __FLASH_H */
+111 -31
View File
@@ -1,22 +1,33 @@
/**
* @file sys/health.cpp
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "health.h"
#include "config.h"
#include "Can.h"
#include "FOC.h"
#include "adc.h"
#include "flash.h"
#include "motor.h"
#include "run_inhibit.h"
#include <cmath>
extern sampler sampler;
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC;
extern FlashEEPROM flashEEP;
// 全局热管理控制器单例:由 main 任务周期调用 update(),用于维护过热保护状态。
ThermalController thermalController; // 热量控制器初始化
#define max(a, b, c) \
((a) > (b) ? ((a) > (c) ? (a) : (c)) : ((b) > (c) ? (b) : (c)))
/** @brief PROTECT_TIME 常量定义 */
constexpr u16 PROTECT_TIME = 6000; // 过热保护时间,单位*10ms
// 三相电流连续全部失效帧数(×10ms)阈值,达到后按过热关机处理
constexpr u16 CURRENT_ERROR_SHUTDOWN_COUNT = 100;
// 电压管理状态机
// 电压管理状态机:仅 power_manage() 慢速任务访问,文件内封装。
static VoltageState voltage_state = VoltageState::NORMAL;
static float last_vbus_slow = 0.0f; // 上次电压值(慢速任务用)
/**
@@ -25,6 +36,32 @@ static float last_vbus_slow = 0.0f; // 上次电压值(慢速任务用)
** @retval 热量值
*/
u32 ThermalController::update() {
// nFAULT 检测(STATUS 帧 motStatus / MotorStatus):本函数 1ms 执行一次。
// 旧实现整段被 #if GATE_DRIVER_DRV8323 圈住,而当前固件按 DRV8313 编译
// (config.h 该宏 = 0)→ 驱动报故障(UVLO / 热关断 / OCP 锁存,三者都会
// 拉低 nFAULT)只上报不动作,控制环照常输出。现在两板都判:
// 连续 DRV_FAULT_DEBOUNCE_MS 次采到低电平 → 申请禁止位,
// 停机由 run_inhibit 在 50us 控制任务里执行;解除只认上位机 motReset。
// DRV8323 板的 VDS 过流配置为"仅上报不动作",不会拉低 nFAULT,不在此触发。
static u8 nfault_busy_ms = 0;
const bool nfault = (drv_nfault_active() != 0U);
if (nfault) {
if (++nfault_busy_ms >= DRV_FAULT_DEBOUNCE_MS) {
nfault_busy_ms = 0;
run_inhibit.request(INHIBIT_DRV_NFAULT);
}
} else {
nfault_busy_ms = 0;
}
// MotorStatus 唯一写手:闸门里还有没有 nFAULT 这一位,或此刻引脚确实在低
CAN0_transmiter.MotorStatus =
(run_inhibit.sources() & INHIBIT_DRV_NFAULT) != 0U || nfault;
// 上报位与闸门同源:每拍从禁止位图派生,不要再在各处置 0/1
// (历史上"停机了但状态位说正常"就是多处写造成的)
CAN0_transmiter.ThermalStatus =
(run_inhibit.sources() & INHIBIT_THERMAL) != 0U;
thermal_value = Q16_16_MUL(thermal_value, thermalRate);
// 添加温度积累
int drv_temp = (int)sampler.get_drv_temp();
@@ -35,6 +72,22 @@ u32 ThermalController::update() {
mcu_temp = 0;
thermal_value += drv_temp * drv_temp + mcu_temp * mcu_temp;
// 三相电流采样有效性先算好:热积累与"冷却到点该不该撤销采样失效禁止位"都要用
const float raw_current[3] = {sampler.get_current('U'),
sampler.get_current('V'),
sampler.get_current('W')};
u32 currents[3] = {0, 0, 0};
bool valid[3] = {false, false, false};
for (u8 i = 0; i < 3; i++) {
if (raw_current[i] == ERROR_ADC_VAL) {
continue;
}
// get_current 已保证有效值 |I| ≤ MAX_CURRENT(40A),转 mA ≤ 40000
currents[i] = (u32)(fabsf(raw_current[i]) * 1000.0f);
valid[i] = true;
}
const bool any_valid = valid[0] || valid[1] || valid[2];
if (remainingTime10Ms) {
// 过热停机状态
// 检查是否可以恢复
@@ -43,43 +96,70 @@ u32 ThermalController::update() {
if (thermal_value < recovery_threshold) {
remainingTime10Ms = 1; // 可以恢复工作
}
if (remainingTime10Ms == 1) { // 过热恢复
FOC.resume_from_thermal();
CAN0_transmiter.ThermalStatus = 0;
if (remainingTime10Ms == 1) { // 冷却到点:各自撤销自己申请的禁止位
run_inhibit.release(INHIBIT_THERMAL);
if (any_valid) {
run_inhibit.release(INHIBIT_ISENSE_FAIL);
}
return --remainingTime10Ms;
}
return --remainingTime10Ms;
} else {
// 当前没有过热停机
// 添加电流热积累
u32 currents[3] = {(u32)(sampler.get_current('U') * 1000),
(u32)(sampler.get_current('V') * 1000),
(u32)(sampler.get_current('W') * 1000)};
// 添加电流热积累:取三相有效相的最大绝对值(热量与电流方向无关,
// 负半周同样发热)。采样失效帧(ERROR_ADC_VAL)不参与积累:
// 失效值 255 按 mA 参与平方运算会超出 int/u32 范围,热积累不可控;
// 三相持续全部失效说明采样链异常,按独立原因 fail-safe 停机
// (不再并入"过热"上报,上位机看到的是 INHIBIT_ISENSE_FAIL)。
static u16 current_error_frames = 0;
// 找最大电流(原始ADC值)
current_max = max(currents[0], currents[1], currents[2]);
if (!any_valid) {
if (++current_error_frames >= CURRENT_ERROR_SHUTDOWN_COUNT) {
current_error_frames = 0;
run_inhibit.request(INHIBIT_ISENSE_FAIL);
remainingTime10Ms = PROTECT_TIME;
return thermal_value;
}
return thermal_value;
}
current_error_frames = 0;
// 找最大电流(mA,绝对值);个别相失效时用剩余有效相
u32 max_current = 0;
for (u8 i = 0; i < 3; i++) {
if (valid[i] && currents[i] > max_current) {
max_current = currents[i];
}
}
current_max = (u16)max_current;
// 平方运算先转 float:40A 档 40000²×10 超 int 范围,整数运算会溢出
const float imax = (float)current_max;
// 电流系数按"绝对值全周期"口径标定:取 fabs 后正负半周都积累,
// 交流负载下积累速度约为旧版(仅正半周)的 2 倍,故系数统一取旧值一半,
// 保持原实测触发时机不变;温度积累与恢复阈值不受此补偿影响。
if (current_max < 2000) {
thermal_value += current_max * current_max * gapTimeMs *
0.00007f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.000035f); // 计算当前电流热积累值
} else if (current_max > 3800) {
thermal_value += current_max * current_max * gapTimeMs *
0.01f; // 添加当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.005f); // 添加当前电流热积累值
} else if (current_max > 3400) {
thermal_value += current_max * current_max * gapTimeMs *
0.005f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.0025f); // 计算当前电流热积累值
} else if (current_max > 3000) {
thermal_value += current_max * current_max * gapTimeMs *
0.0002f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.0001f); // 计算当前电流热积累值
} else if (current_max > 2000) {
thermal_value += current_max * current_max * gapTimeMs *
0.0001f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.00005f); // 计算当前电流热积累值
}
}
if (thermal_value > thermal_capacity) {
FOC.shut_down_thermal();
// 过热:只申请本模块的禁止位,停机由 run_inhibit 执行;
// ThermalStatus 由位图派生(见本函数开头),此处不再单独写
run_inhibit.request(INHIBIT_THERMAL);
remainingTime10Ms = PROTECT_TIME; // 过热停机
CAN0_transmiter.ThermalStatus = 1;
return remainingTime10Ms;
}
return thermal_value;
@@ -106,8 +186,8 @@ void power_manage(void) {
sampler.update_vbus_voltage();
float vbus = sampler.get_vbus_voltage();
// 更新FOC的电源电压参数
FOC.update_power(last_vbus_slow);
// 更新FOC的电源电压参数:使用本周期刚采到的 vbus,避免上一拍电压滞后放大速度/电流耦合。
FOC.update_power(vbus);
last_vbus_slow = vbus;
// 如果快速检测已触发紧急关机,执行保存
@@ -126,12 +206,12 @@ void power_manage(void) {
case VoltageState::SHUTDOWN_SAVE:
// 关机保存状态:检查是否可以恢复
// 恢复条件:电压恢复到基准电压的90%以上
// 恢复条件:电压恢复到基准电压的90%以上。
// 只撤销欠压这一位;是否真的能运行由闸门看位图是否清空决定,
// 这里不查其它故障源(过热/nFAULT 各自有各自的解除方)。
if (FOC.vbus_reference > 0 && vbus > FOC.vbus_reference * VBUS_RECOVERY_RATIO) {
if (!FOC.thermal_shutdown) {
FOC.resume();
voltage_state = VoltageState::NORMAL;
}
run_inhibit.release(INHIBIT_UNDERVOLTAGE);
voltage_state = VoltageState::NORMAL;
}
break;
+35 -1
View File
@@ -1,25 +1,43 @@
/**
* @file sys/health.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _HEALTH_H
#define _HEALTH_H
#include "config.h"
#include <cstdint>
typedef int32_t q16_16_t; // Q16.16格式
// 定点数运算宏
/** @brief Q16_16_ONE 宏定义 */
#define Q16_16_ONE 65536 // 1.0 in Q16.16
/** @brief FLOAT_TO_Q16_16 宏定义 */
#define FLOAT_TO_Q16_16(x) ((q16_16_t)((x) * Q16_16_ONE))
/** @brief Q16_16_TO_FLOAT 宏定义 */
#define Q16_16_TO_FLOAT(x) ((float)(x) / Q16_16_ONE)
/** @brief Q16_16_MUL 宏定义 */
#define Q16_16_MUL(a, b) ((q16_16_t)(((int64_t)(a) * (b)) >> 16))
/** @brief Q16_16_SQUARE 宏定义 */
#define Q16_16_SQUARE(x) Q16_16_MUL((x), (x))
// 电压监测状态机状态
/**
* @brief VoltageState 枚举定义
*/
enum class VoltageState : uint8_t {
NORMAL, // 正常运行
VOLTAGE_DROP, // 电压跌落检测中(防抖等待)
SHUTDOWN_SAVE // 已关机,等待保存
};
/**
* @brief ThermalController 类或结构体
*/
class ThermalController {
private:
u8 temperature;
u16 current_max;
q16_16_t thermalRate; // 热耗散系数(每单位时间散热比例)
@@ -34,14 +52,30 @@ class ThermalController {
ThermalController()
: thermalRate(FLOAT_TO_Q16_16(0.995f)),
recoveryRate(FLOAT_TO_Q16_16(0.33f)) {}
/**
* @brief 更新对象内部状态或采样数据
* @return u32 返回值
*/
u32 update();
/**
* @brief 复位对象内部状态
* @return void
*/
void reset() {
thermal_value = 0;
remainingTime10Ms = 0;
}
};
/**
* @brief angsens_MG_check 函数
* @return void
*/
void angsens_MG_check(void);
/**
* @brief power_manage 函数
* @return void
*/
void power_manage(void);
#endif
+105 -44
View File
@@ -1,3 +1,9 @@
/**
* @file sys/initial.cpp
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "initial.h"
#include "Can.h"
#include "config.h"
@@ -13,19 +19,24 @@
// OTA 调试宏定义
#if OTA_DEBUG
/** @brief OTA_DEBUG_PRINT 宏定义 */
#define OTA_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief OTA_DEBUG_PRINT 宏定义 */
#define OTA_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// EEP 调试宏定义
#if EEP_DEBUG
/** @brief EEP_DEBUG_PRINT 宏定义 */
#define EEP_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief EEP_DEBUG_PRINT 宏定义 */
#define EEP_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
// 全局版本管理器单例:构造函数只复制 constexpr 版本号常量,不依赖外设初始化。
VersionManager Version;
VersionManager::VersionManager() {
@@ -38,18 +49,38 @@ VersionManager::VersionManager() {
BOOT_VERSION_BUILD);
}
/**
* @brief 获取 getSoftwareVersion 对应的数据或状态
* @return VersionManager::VersionInfo 返回值
*/
VersionManager::VersionInfo VersionManager::getSoftwareVersion() const {
return softwareVersion;
}
/**
* @brief 获取 getHardwareVersion 对应的数据或状态
* @return VersionManager::VersionInfo 返回值
*/
VersionManager::VersionInfo VersionManager::getHardwareVersion() const {
return hardwareVersion;
}
/**
* @brief 获取 getBootVersion 对应的数据或状态
* @return VersionManager::VersionInfo 返回值
*/
VersionManager::VersionInfo VersionManager::getBootVersion() const {
return bootVersion;
}
/**
* @brief 设置 setSoftwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void VersionManager::setSoftwareVersion(u16 major, u16 minor, u16 patch,
u16 build) {
softwareVersion.major = major;
@@ -58,6 +89,14 @@ void VersionManager::setSoftwareVersion(u16 major, u16 minor, u16 patch,
softwareVersion.build = build;
}
/**
* @brief 设置 setHardwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void VersionManager::setHardwareVersion(u16 major, u16 minor, u16 patch,
u16 build) {
hardwareVersion.major = major;
@@ -66,6 +105,14 @@ void VersionManager::setHardwareVersion(u16 major, u16 minor, u16 patch,
hardwareVersion.build = build;
}
/**
* @brief 设置 setBootVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void VersionManager::setBootVersion(u16 major, u16 minor, u16 patch,
u16 build) {
bootVersion.major = major;
@@ -74,18 +121,56 @@ void VersionManager::setBootVersion(u16 major, u16 minor, u16 patch,
bootVersion.build = build;
}
static void appendVersionChar(char *&out, size_t &remaining, char ch) {
if (remaining > 1U) {
*out++ = ch;
--remaining;
*out = '\0';
}
}
static void appendVersionU16(char *&out, size_t &remaining, u16 value) {
char digits[5]; // u16 最大值 65535,最多 5 位十进制数字
u8 count = 0;
do {
digits[count++] = static_cast<char>('0' + (value % 10U));
value = static_cast<u16>(value / 10U);
} while (value > 0U && count < sizeof(digits));
while (count > 0U) {
appendVersionChar(out, remaining, digits[--count]);
}
}
char *VersionManager::versionToString(const VersionInfo &version, char *buffer,
size_t bufSize) const {
if (buffer == nullptr) {
return nullptr;
}
if (bufSize == 0U) {
return buffer;
}
snprintf(buffer, bufSize, "%d.%d.%d.%d", version.major, version.minor,
version.patch, version.build);
char *out = buffer;
size_t remaining = bufSize;
*out = '\0';
appendVersionU16(out, remaining, version.major);
appendVersionChar(out, remaining, '.');
appendVersionU16(out, remaining, version.minor);
appendVersionChar(out, remaining, '.');
appendVersionU16(out, remaining, version.patch);
appendVersionChar(out, remaining, '.');
appendVersionU16(out, remaining, version.build);
return buffer;
}
/**
* @brief sendVersionInfoViaCan 函数
* @return void
*/
void VersionManager::sendVersionInfoViaCan() const {
// 准备发送版本信息 (ID: CANFD_VERSION_ID)
// 只使用必要的字节数(24 字节)来传输三个版本的信息(每个版本 6 字节)
@@ -128,6 +213,10 @@ void VersionManager::sendVersionInfoViaCan() const {
CAN0_transmiter.build_can_id(CANFD_VERSION_ID), 24, versionData);
}
/**
* @brief printVersions 函数
* @return void
*/
void VersionManager::printVersions() const {
char versionStr[32];
@@ -142,7 +231,12 @@ void VersionManager::printVersions() const {
versionToString(getBootVersion(), versionStr, sizeof(versionStr)));
}
/* retarget the C library printf function to the USART */
/**
* @brief 初始化 USART0 调试串口
* @return u8 返回值
*/
u8 USART0_init() {
gd_eval_com_init(EVAL_COM0);
return 1;
@@ -170,6 +264,10 @@ int fputc(int ch, FILE *f) throw() {
return ch;
}
/**
* @brief 初始化未使用引脚状态
* @return void
*/
void unused_pin_init() {
rcu_periph_clock_enable(RCU_GPIOA); // 使能 GPIOA 时钟
rcu_periph_clock_enable(RCU_GPIOB); // 使能 GPIOB 时钟
@@ -185,48 +283,11 @@ void unused_pin_init() {
gpio_bit_reset(GPIOC, GPIO_PIN_14 | GPIO_PIN_15);
}
/*!
\brief 检测并打印复位源信息
\param[in] none
\param[out] none
\retval none
*/
u16 get_reset_cause(void) {
u16 cause = 0;
if (RESET != rcu_flag_get(RCU_FLAG_FWDGTRST)) {
// OTA_DEBUG_PRINT("Free Watchdog Timer Reset\r\n");
cause = RCU_FLAG_FWDGTRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_EPRST)) {
// OTA_DEBUG_PRINT("External Pin Reset\r\n");
cause = RCU_FLAG_EPRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_PORRST)) {
// OTA_DEBUG_PRINT("Power Reset\r\n");
cause = RCU_FLAG_PORRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_WWDGTRST)) {
// OTA_DEBUG_PRINT("Window Watchdog Timer Reset\r\n");
cause = RCU_FLAG_WWDGTRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_LPRST)) {
// OTA_DEBUG_PRINT("Low Power Reset\r\n");
cause = RCU_FLAG_LPRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_SWRST)) {
// OTA_DEBUG_PRINT("Software Reset\r\n");
cause = RCU_FLAG_SWRST;
}
// 清除所有复位标志
rcu_all_reset_flag_clear();
// OTA_DEBUG_PRINT("cause:%d\r\n", cause);
return cause;
}
/**
* @brief 初始化 OTA 与 A/B 分区状态
* @return void
*/
void OTA_initial(void) {
#if OTA_DEBUG
u32 temp_val[6];
@@ -282,4 +343,4 @@ void OTA_initial(void) {
} else {
EEP_DEBUG_PRINT("check\r\n");
}
}
}
+85 -2
View File
@@ -1,3 +1,9 @@
/**
* @file sys/initial.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _INITIAL_H
#define _INITIAL_H
@@ -11,6 +17,9 @@ extern "C" {
#include <cstdio>
// 任务标志定义
/**
* @brief 匿名枚举 枚举定义
*/
typedef enum {
TASK_MOTOR_CONTROL = 0,
TASK_CURRENT_UPDATE,
@@ -25,14 +34,40 @@ typedef enum {
TASK_COUNT
} SystemTask;
/**
* @brief rcu_config 函数
* @return void
*/
void rcu_config(void);
static volatile uint32_t timer_delay_count = 0; // volatile 很重要!
/**
* @brief fputc 函数
* @param[in] ch 参数
* @param[in] f 参数
* @return int 返回值
*/
int fputc(int ch, FILE *f); // 声明fputc函数以重载标准库实现
/**
* @brief 初始化 USART0 调试串口
* @return u8 返回值
*/
u8 USART0_init(void);
/**
* @brief 初始化独立看门狗
* @param[in] time 参数
* @return u8 返回值
*/
u8 watchdog_init(u32 time);
u16 get_reset_cause(void);
/**
* @brief 初始化未使用引脚状态
* @return void
*/
void unused_pin_init();
/**
* @brief 初始化 OTA 与 A/B 分区状态
* @return void
*/
void OTA_initial(void);
// 任务管理结构
@@ -41,6 +76,9 @@ typedef struct {
} TaskScheduler;
// 版本管理类
/**
* @brief VersionManager 类或结构体
*/
class VersionManager {
public:
// 版本信息结构体
@@ -55,31 +93,76 @@ class VersionManager {
VersionManager();
// 获取软件版本
/**
* @brief 获取 getSoftwareVersion 对应的数据或状态
* @return VersionInfo 返回值
*/
VersionInfo getSoftwareVersion() const;
// 获取硬件版本
/**
* @brief 获取 getHardwareVersion 对应的数据或状态
* @return VersionInfo 返回值
*/
VersionInfo getHardwareVersion() const;
// 获取BOOT版本
/**
* @brief 获取 getBootVersion 对应的数据或状态
* @return VersionInfo 返回值
*/
VersionInfo getBootVersion() const;
// 设置软件版本
/**
* @brief 设置 setSoftwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void setSoftwareVersion(u16 major, u16 minor, u16 patch, u16 build);
// 设置硬件版本
/**
* @brief 设置 setHardwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void setHardwareVersion(u16 major, u16 minor, u16 patch, u16 build);
// 设置BOOT版本
/**
* @brief 设置 setBootVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void setBootVersion(u16 major, u16 minor, u16 patch, u16 build);
// 将版本信息转换为字符串
char *versionToString(const VersionInfo &version, char *buffer,
size_t bufSize) const;
// 打印所有版本信息
/**
* @brief printVersions 函数
* @return void
*/
void printVersions() const;
// 通过CAN发送版本信息
/**
* @brief sendVersionInfoViaCan 函数
* @return void
*/
void sendVersionInfoViaCan() const;
private:
@@ -92,4 +175,4 @@ class VersionManager {
}
#endif
#endif
#endif
+50
View File
@@ -0,0 +1,50 @@
/**
* @file sys/run_inhibit.cpp
* @brief 电机"禁止运行"闸门实现
*/
#include "run_inhibit.h"
#include "Timer.h"
#include "motor.h"
// 全局单例:定义放本文件,声明在 main.cpp 之外由各调用方 extern(见 run_inhibit.h)
RunInhibit run_inhibit;
/**
* @brief 申请禁止
*/
void RunInhibit::request(u32 src) { bits |= src; }
/**
* @brief 撤销本来源的禁止申请
*/
void RunInhibit::release(u32 src) { bits &= ~src; }
/**
* @brief 执行闸门:禁止/唤醒建立期封 PWM,禁止时让驱动睡眠;解除后唤醒并等建立时间
* @note 睡眠(nSLEEP 拉低)本身会复位 DRV8313 内部逻辑含故障锁存,所以
* "芯片侧锁存"由这里顺带清掉;"要不要继续禁止"完全由位图决定。
* 解除后不立即派发控制函数:DRV8313 手册 7.4.1 要求唤醒后约 1ms 才
* 完全工作,这段建立期内输出保持 0。若故障源还在,检测方会在下一轮
* 轮询里重新 request(),本模块不需要知道是谁、为什么。
*/
void RunInhibit::update(void) {
if (bits != 0U) {
if (!stopped) {
motor_sleep();
stopped = true;
}
// 建立窗口每拍重置:解除后要重新等满 DRV_WAKE_SETTLE_50US
wake_ticks = DRV_WAKE_SETTLE_50US;
Set_3Pwm(0.0f, 0.0f, 0.0f);
return;
}
if (stopped) {
motor_resume();
stopped = false;
}
if (wake_ticks != 0U) {
Set_3Pwm(0.0f, 0.0f, 0.0f);
wake_ticks--;
}
}
+77
View File
@@ -0,0 +1,77 @@
/**
* @file sys/run_inhibit.h
* @brief 电机"禁止运行"闸门:唯一有权决定桥臂能不能输出的模块。
* @note 分层约定(新增故障必须照此办,不要在检测处写停机动作):
* - 故障检测方 只调 request(来源位) —— 申请禁止,不碰 PWM/使能脚
* - 故障解除方 只调 release(来源位) —— 撤销自己的申请,不查别人的位
* - 功率级动作 只发生在本模块 update() —— 由 50us 控制任务驱动
* 因此任何一处都不需要知道其它故障源的存在;"能不能运行"= 位图是否为空。
*/
#ifndef _RUN_INHIBIT_H
#define _RUN_INHIBIT_H
#include "config.h"
/**
* @brief 禁止来源位图(每一位一个独立申请方,可并存)
*/
enum InhibitSource : u32 {
INHIBIT_DRV_NFAULT = 1u << 0, // 门驱动 nFAULT(PB2) 有效:UVLO/热关断/OCP 锁存
INHIBIT_UNDERVOLTAGE = 1u << 1, // 母线跌幅超阈值或低于绝对下限
INHIBIT_THERMAL = 1u << 2, // 热模型过热(含温度采集正常时的电流热积累)
INHIBIT_ISENSE_FAIL = 1u << 3, // 三相电流采样持续失效(原先被并入"过热"上报)
// 上位机还没下发过非零模式:保持驱动睡眠。这不是故障,而是"未被使能",
// 放在同一张位图里是为了让功率级只有一个执行者。老板 v2 的 EN 有 10k 下拉、
// DRV8313 上电默认睡眠;若上电即唤醒,模式 0(drv_hold 恒 10% 占空比锁止)
// 会立刻给 A 相通流发热,而"上电先发模式 0 当静止待机"是现有上位机行为。
INHIBIT_NOT_ENABLED = 1u << 7,
};
/**
* @brief RunInhibit 类:禁止运行闸门
*/
class RunInhibit {
// 上电即禁止:等上位机首次下发非零模式解除(见 INHIBIT_NOT_ENABLED)
u32 bits = INHIBIT_NOT_ENABLED;
bool stopped = false; // 上一拍是否已施加停机(避免每 50us 重复翻转使能脚)
u8 wake_ticks = 0; // 唤醒建立倒计时(50us 节拍)
public:
/**
* @brief 申请禁止(幂等;同一位重复申请无副作用)
* @param[in] src InhibitSource 位
* @return void
*/
void request(u32 src);
/**
* @brief 撤销本来源的禁止申请;位图清空后才允许运行
* @param[in] src InhibitSource 位
* @return void
*/
void release(u32 src);
/**
* @brief 是否允许运行(只看位图,不看唤醒状态)
* @return bool true=允许
*/
bool permitted() const { return bits == 0U; }
/**
* @brief 允许运行且驱动唤醒建立已完成,控制函数可派发
* @return bool true=可派发
*/
bool ready() const { return bits == 0U && wake_ticks == 0U; }
/**
* @brief 当前禁止来源位图(上报/诊断用)
* @return u32 位图
*/
u32 sources() const { return bits; }
/**
* @brief 执行闸门:50us 控制任务每拍调用,唯一碰 PWM 与驱动使能的地方
* @return void
*/
void update(void);
};
extern RunInhibit run_inhibit;
#endif
+16 -12
View File
@@ -39,15 +39,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gd32c10x.h"
#include "gd32c10x_fmc.h"
#include "motor.h"
#include "tasks.h"
#include <cstdio>
// SLEEP 调试宏定义
#if SLEEP_DEBUG
/** @brief SLEEP_DEBUG_PRINT 宏定义 */
#define SLEEP_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief SLEEP_DEBUG_PRINT 宏定义 */
#define SLEEP_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// SysTick 模块内部状态,仅在本文件访问:
// - delay / systick_ms 由 SysTick 中断维护,用 volatile 保证主循环可见性;
// - DWT 统计变量用于 CPU 占用率采样;
// - static 限制文件作用域,外部只能通过 get_systick_ms()/get_cpu_usage() 读取。
volatile static uint32_t delay;
volatile static uint32_t systick_ms = 0; // 系统运行时间(毫秒)
@@ -143,18 +150,8 @@ extern "C" void SysTick_Handler(void) {
busy_cycles_accum = 0;
sample_seconds = 0;
}
}
/*!
\brief delay a time in seconds
\param[in] count: count in seconds
\param[out] none
\retval none
*/
void delay_1s(uint32_t count) {
uint32_t i;
for (i = 0; i < count; i++) {
delay_1ms(1000);
// 按任务负载统计与全局口径同步:同一秒窗口
task_busy_cycles_reset();
}
}
@@ -208,6 +205,10 @@ void busy_task_exit(void) {
}
}
/**
* @brief 获取 CPU 使用率
* @return uint8_t 返回值
*/
uint8_t get_cpu_usage(void) { return cpu_usage; }
/*!
@@ -247,6 +248,7 @@ void app_prepare_deep_sleep(bool save_data) {
fwdgt_config(0xFFF, FWDGT_PSC_DIV256); // 设置最大超时时间
fwdgt_write_disable();
uint32_t primask = __get_PRIMASK();
__disable_irq();
// 遍历最后一个数字,后一个地址写 +1
for (u32 address = SLEEP_CONFIG_END - SIZE_U32;
@@ -265,6 +267,7 @@ void app_prepare_deep_sleep(bool save_data) {
if (fmc_status != FMC_READY) { // 擦除失败报错
fmc_lock();
flashEEP.EEPROMStatus = FLASH_EEPROM_ERASE_ERROR;
__set_PRIMASK(primask); // 失败返回前恢复进入前的中断状态
return;
}
address = SLEEP_CONFIG_START - SIZE_U32; // 重新开始写入
@@ -283,6 +286,7 @@ void app_prepare_deep_sleep(bool save_data) {
flashEEP.EEPROMStatus = FLASH_EEPROM_WRITE_ERROR;
SLEEP_DEBUG_PRINT("app wrote %08x at %08x ERROR\r\n", SLEEP_ENABLE,
address);
__set_PRIMASK(primask); // 失败返回前恢复进入前的中断状态
return;
}
break;
+46 -2
View File
@@ -41,28 +41,72 @@ extern "C" {
#include <cstdint>
/* configure systick */
/**
* @brief 配置 SysTick 定时器
* @return void
*/
void systick_config(void);
/* get system tick time in milliseconds */
/**
* @brief 获取系统毫秒计数
* @return uint32_t 返回值
*/
uint32_t get_systick_ms(void);
/* delay a time in milliseconds */
/**
* @brief 毫秒级阻塞延时
* @param[in] count 参数
* @return void
*/
void delay_1ms(uint32_t count);
/* delay a time in seconds */
void delay_1s(uint32_t count);
/* delay decrement */
/**
* @brief 递减延时计数器
* @return void
*/
void delay_decrement(void);
/* 空闲任务计数(兼容接口,DWT 版本无需操作) */
/**
* @brief 记录空闲任务计数
* @return void
*/
void idle_task_count(void);
/* do_task 入口调用,记录忙碌起始周期 */
/**
* @brief 记录忙碌任务开始周期
* @return void
*/
void busy_task_enter(void);
/* do_task 出口调用,累加忙碌周期 */
/**
* @brief 记录忙碌任务结束并累加周期
* @return void
*/
void busy_task_exit(void);
/* 获取CPU使用率 */
/**
* @brief 获取 CPU 使用率
* @return uint8_t 返回值
*/
uint8_t get_cpu_usage(void);
/* 系统软复位 */
/**
* @brief 执行系统软复位
* @return void
*/
void system_soft_reset(void);
/* 准备进入低功耗模式 */
/**
* @brief 准备进入应用低功耗模式
* @param[in] save_data 参数
* @return void
*/
void app_prepare_deep_sleep(bool save_data);
/* 中断唤醒 */
/**
* @brief EXTI5_9 中断处理函数
* @return void
*/
void EXTI5_9_IRQHandler(void);
#ifdef __cplusplus
+199
View File
@@ -0,0 +1,199 @@
/**
* @file tasks.cpp
* @brief 周期任务调度与任务执行实现
*/
#include "tasks.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
#include "adc.h"
#include "ang_sensor.h"
#include "health.h"
#include "run_inhibit.h"
#include "systick.h"
#include "gd32c10x.h"
extern CAN_transmiter CAN0_transmiter;
extern sampler sampler;
extern angle_sensor KTH7823;
extern EncoderAngleKalman Angle_kalman;
extern ThermalController thermalController;
extern FOC_Controller FOC;
extern u32 motor_runtime_50us;
extern u32 cnt_test;
// 函数指针数组,包含各种控制模式对应的函数
static FOCFunctionPtr control_functions[] = {
&FOC_Controller::drv_hold, // 0: 占空比置
&FOC_Controller::angle_control, // 1: 位置闭环控制
&FOC_Controller::velocity_control, // 2: 速度闭环控制
&FOC_Controller::
angle_control_with_velocity_limit, // 3:
// 角度闭环控制,带速度限制
&FOC_Controller::MIT_Mode, // 4: MIT 模式
&FOC_Controller::current_control, // 5: 电流闭环控制
&FOC_Controller::velocity_open_loop, // 6: 速度开环控制
&FOC_Controller::PWM_out, // 7: 占空比输出
};
/**
* @brief 判断任务标志中是否包含指定任务
* @param[in] currentTasks 当前任务位图快照
* @param[in] task 待检查任务枚举
* @return bool true 表示该任务需要执行
*/
static bool has_task(u32 currentTasks, SystemTask task) {
return (currentTasks & (1u << task)) != 0U;
}
// 按任务 DWT 负载计量:定位 CPU 占用分布(与全局 cpu_usage 同 1s 口径,
// 由 SysTick 统计块调用 task_busy_cycles_reset() 同步清零)。
// 120MHz 下 120000 周期 ≈ 1% CPU。仅统计任务本体,不含中断抢占时间。
static volatile u32 task_busy_cycles[TASK_COUNT] = {0};
u32 get_task_busy_cycles(u8 task) {
return (task < TASK_COUNT) ? task_busy_cycles[task] : 0U;
}
void task_busy_cycles_reset(void) {
for (u8 i = 0; i < TASK_COUNT; i++) {
task_busy_cycles[i] = 0;
}
}
// 执行任务并累计其忙碌周期
#define RUN_TIMED(task_enum, call) \
do { \
u32 t0_ = DWT->CYCCNT; \
call; \
task_busy_cycles[task_enum] += DWT->CYCCNT - t0_; \
} while (0)
/**
* @brief 控制模式变化时复位所有控制环,避免积分项跨模式继承
* @return void
*/
static void reset_control_loops_on_mode_change(void) {
static int last_control_mode = -1;
if (FOC.control_mode == last_control_mode) return;
FOC.vel_loop.reset();
FOC.angle_loop.reset();
FOC.current_loop.reset();
FOC.vel_inVelAngloop.reset();
FOC.ang_inVelAngloop.reset();
FOC.ang_inMITloop.reset();
FOC.vel_inMITloop.reset();
FOC.current_inMITloop.reset();
last_control_mode = FOC.control_mode;
}
/**
* @brief 执行 50us 电机控制任务,包含快速电压检测和控制函数派发
* @return void
*/
static void run_motor_control_task(void) {
motor_runtime_50us++;
static u8 vcheck_div = 0;
// 快速电压检测每 100us 执行一次,避免每 50us 都读取处理。
if (++vcheck_div >= 2u) {
vcheck_div = 0;
FOC.fast_voltage_check();
}
// 运行闸门:全工程唯一执行停机/唤醒功率级的地方。放在模式判断之前,
// 这样即使 control_mode 非法(>=8)也照样封住输出。
run_inhibit.update();
if (!run_inhibit.ready()) {
return; // 被禁止运行,或刚解除还在驱动唤醒建立期(输出已由闸门保持 0)
}
if (FOC.control_mode >= 8) return;
// 模式合法时再派发控制函数,防止数组越界。
reset_control_loops_on_mode_change();
(FOC.*control_functions[FOC.control_mode])();
}
/**
* @brief 执行高频采样和电机控制相关任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
static void run_fast_sample_tasks(u32 currentTasks) {
if (has_task(currentTasks, TASK_CURRENT_UPDATE)) {
RUN_TIMED(TASK_CURRENT_UPDATE, sampler.update_current());
}
if (has_task(currentTasks, TASK_ANGLE_UPDATE)) {
RUN_TIMED(TASK_ANGLE_UPDATE, KTH7823.update());
RUN_TIMED(TASK_ANGLE_UPDATE,
FOC.rt_angle = Angle_kalman.update(KTH7823.getAngle()));
}
if (has_task(currentTasks, TASK_VELOCITY_UPDATE)) {
RUN_TIMED(TASK_VELOCITY_UPDATE,
FOC.rt_velocity = Angle_kalman.getVelocity());
}
if (has_task(currentTasks, TASK_MOTOR_CONTROL)) {
RUN_TIMED(TASK_MOTOR_CONTROL, run_motor_control_task());
}
}
/**
* @brief 执行温度、热管理、磁检测和母线电压等低频维护任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
static void run_housekeeping_tasks(u32 currentTasks) {
if (has_task(currentTasks, TASK_MCU_TEMP_UPDATE)) {
RUN_TIMED(TASK_MCU_TEMP_UPDATE, sampler.update_mcu_temp());
}
if (has_task(currentTasks, TASK_DRV_TEMP_UPDATE)) {
RUN_TIMED(TASK_DRV_TEMP_UPDATE, sampler.update_drv_temp());
}
if (has_task(currentTasks, TASK_THERMAL_CONTROL)) {
RUN_TIMED(TASK_THERMAL_CONTROL, thermalController.update());
}
if (has_task(currentTasks, TASK_MAGNETIC_CHECK)) {
RUN_TIMED(TASK_MAGNETIC_CHECK, angsens_MG_check());
}
if (has_task(currentTasks, TASK_VBUS_UPDATE)) {
RUN_TIMED(TASK_VBUS_UPDATE, power_manage());
}
}
/**
* @brief 执行 CAN 周期发送任务并喂狗
* @return void
*/
static void run_can_task(void) {
u32 t0 = DWT->CYCCNT;
CAN0_transmiter.test_u16 = CAN0_transmiter.get_node_id();
cnt_test = 0;
CAN0_transmiter.can0_send_flag = SET;
CAN0_transmiter.communication_check();
fwdgt_counter_reload();
task_busy_cycles[TASK_CAN_TRANSMIT] += DWT->CYCCNT - t0;
}
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
void do_task(u32 currentTasks) {
busy_task_enter(); // DWT: 记录忙碌起始周期
cnt_test++;
// 高频任务先执行,保持电机控制优先级。
run_fast_sample_tasks(currentTasks);
run_housekeeping_tasks(currentTasks);
// CAN 发送放在最后,避免通信处理影响前面的实时控制任务。
if (has_task(currentTasks, TASK_CAN_TRANSMIT)) {
run_can_task();
}
busy_task_exit(); // DWT: 累加忙碌周期
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef TASKS_H
#define TASKS_H
#include "config.h"
#include "initial.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
void do_task(u32 currentTasks);
/**
* @brief 读取上一统计周期(1s)内指定任务累计消耗的 CPU 周期数
* @param[in] task SystemTask 枚举值
* @return u32 周期数(120MHz 下 120000 = 1% CPU)
*/
u32 get_task_busy_cycles(u8 task);
/**
* @brief 清零按任务负载统计(SysTick 每秒统计 cpu_usage 时同步调用)
* @return void
*/
void task_busy_cycles_reset(void);
#ifdef __cplusplus
}
#endif
#endif /* TASKS_H */
+161
View File
@@ -0,0 +1,161 @@
# 静态检查告警审查说明
本文件用于记录人工确认后的静态检查告警处理结论。原则:
- 不为消除低风险 warning 引入会改变实时行为的代码。
- 裸机系统级单例、编译期常量、ISR/主循环共享状态分别分类说明。
- 对涉及实时路径的并发项,优先记录访问边界和偏离理由;只有经过实机验证后再做逻辑修复。
## SUP-GLB-001:文件作用域对象
### 编译期常量
以下 `constexpr` 对象为编译期常量,非可变全局状态,无运行期写入、并发访问或动态初始化顺序风险:
- `device/ADC.cpp`: `ADC_SCALE`, `TEMP_FACTOR`, `VCC_FACTOR`, `CURRENT_FACTOR`
- `sys/health.cpp`: `PROTECT_TIME`
- `sys/config.h`: 版本号、Flash 地址、协议 ID、滤波/PID 参数、数学常量、Timer 参数等 `constexpr` 配置项
处理结论:按静态检查误报/偏离处理。
### 系统级硬件/模块单例
以下对象对应裸机固件中的唯一硬件资源或唯一管理器:
- `Algorithm/FOC.cpp`: `FOC_Controller FOC`
- `device/ADC.cpp`: `sampler sampler`
- `device/Can.cpp`: `CAN_transmiter CAN0_transmiter`
- `device/ang_sensor.cpp`: `angle_sensor KTH7823`
- `sys/health.cpp`: `ThermalController thermalController`
- `sys/initial.cpp`: `VersionManager Version`
访问边界:
- 初始化顺序由 `main::hardware_init()` 显式控制,例如 `KTH7823.init()`、`sampler.init()`、`CAN0_transmiter.init()`、`FOC.init()`。
- `VersionManager Version` 构造仅复制 `constexpr` 版本号常量,不依赖外设初始化。
- 运行期主要由主循环任务访问;CAN 命令路径会更新 FOC 目标值和参数。
处理结论:接受系统级单例设计,保留文件作用域对象,并通过注释说明初始化顺序和访问边界。
### ISR/主循环共享状态
#### `main.cpp`: `TaskScheduler scheduler`
- `TIMER2_IRQHandler()` 置位任务标志。
- main 循环短关中断后读取快照并清除已处理任务位。
处理结论:已有短临界区保护,接受。
#### `device/Can.cpp`: CAN RX/TX 队列索引
涉及:
- `rx_queue_head`, `rx_queue_tail`, `rx_queue_count`
- `tx_queue_head`, `tx_queue_tail`, `tx_queue_count`
访问边界:
- RX/TX 队列被主循环、CAN 中断、DMA 完成中断共同访问。
- 队列 push/pop/peek/disable 通过 `CAN_EnterCritical()` / `CAN_ExitCritical()` 保护。
- `CAN_ExitCritical()` 使用 `__set_PRIMASK(primask)` 恢复进入前中断状态,而不是直接 `__enable_irq()`,避免误打开外层已关闭的中断。
处理结论:已有临界区保护,接受。
#### `device/ADC.cpp`: injected 电流采样缓存
涉及:
- `adc_current_value[3]`
- `adc_current_frame_ready`
访问边界:
- `ADC0_1_IRQHandler()` 写入 U/V/W injected 同步采样值,并置位 `adc_current_frame_ready`。
- `sampler::update_current()` 在主循环任务中看到 ready 后读取三相采样并更新滤波缓存,随后清 ready。
- 该路径属于高频电机采样/控制链路。此前尝试在读取侧加入 PRIMASK 快照后,实机出现上电连续重启;回滚后恢复正常。
处理结论:当前不再为消除 warning 增加临界区逻辑,避免改变高频实时路径时序。保留现有 `volatile` 共享标志设计,作为实机已验证的稳定实现;如后续需要修正,应单独建立实验分支并做示波/日志验证。
#### `sys/systick.cpp`: DWT CPU 占用率统计变量
涉及:
- `task_enter_cyccnt`
- `busy_cycles_accum`
- `sample_seconds`
- `cpu_usage`
- `dwt_initialized`
访问边界:
- `busy_task_enter()` / `busy_task_exit()` 在主循环任务侧记录和累加忙碌周期。
- `SysTick_Handler()` 每 1 秒读取并清零统计值,更新 `cpu_usage`。
- 此路径仅用于诊断 CPU 使用率,不参与控制闭环。
- 此前尝试在 `busy_task_exit()` 加入 PRIMASK 临界区后,与 ADC 临界区改动同轮导致实机上电连续重启;已回滚并恢复正常。
处理结论:当前不再为该诊断统计项增加临界区,避免影响高频任务时序。允许 1 秒窗口内存在极小统计误差,作为诊断值可接受。
#### `device/ang_sensor.cpp`: SPI DMA 完成标志
涉及:
- `spi_dma_tx_complete`
- `spi_dma_rx_complete`
访问边界:
- SPI 传输前清零标志。
- DMA ISR 完成后置位。
- 轮询函数等待标志或超时。
处理结论:典型 ISR/轮询共享 volatile 标志,接受。
#### `device/Timer.cpp`: `total_us100_counter`
访问边界:
- 仅在 `TIMER2_IRQHandler()` 内作为 100us 调度索引写入和使用。
处理结论:虽然为文件作用域变量,但无跨上下文并发写风险,接受。后续可考虑改为 ISR 内 `static`,但当前不为 warning 改运行逻辑。
#### `sys/health.cpp`: 电压管理状态
涉及:
- `voltage_state`
- `last_vbus_slow`
访问边界:
- 仅 `power_manage()` 慢速任务访问。
- 文件内 `static` 封装。
处理结论:模块私有状态机变量,接受。
## SUP-CMP-001:浮点比较
### `normalizeAngle360()` 边界判断
代码形态:
```cpp
while (angle >= 360.0f) angle -= 360.0f;
while (angle < 0.0f) angle += 360.0f;
```
结论:该比较用于角度归一化边界判断,目标区间严格定义为 `[0, 360)`,不是浮点相等性判断,也不是传感器阈值判定。不需要容差。
## printf / snprintf 类检查
- FOC 齿槽校准打印位于 `#if FOC_DEBUG`,正式配置 `FOC_DEBUG=0` 时不参与编译。
- `VersionManager::versionToString()` 已从 `snprintf()` 改为固定格式的轻量手写拼接,避免格式化库引入。
## AUTO-MET-001:圈复杂度
已拆分以下函数,降低单函数圈复杂度:
- `FOC_Controller::calibrate_cogging_tick()`:拆分为状态派发和各状态处理函数。
- `CAN_transmiter::communication_check()`:拆分为周期发送、接收分发、特殊消息、节点消息等处理函数。
- `do_task()`:拆分为高频采样/电机控制、低频维护、CAN 任务。
处理结论:拆分后原目标函数复杂度已显著下降;实编仍需在 Keil/CMSIS/cbuild 环境验证。
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
# STATIC_ANALYSIS_NOTES 复核修订版(2026-10-19)
> 原文件:仓库根目录 `STATIC_ANALYSIS_NOTES.md`(最后修改于 7c4aaa5,2026-09-01),本次保留未改。
> 复核基线:master `86ca766`(2026-10-13)。完整评审见 [Motor_v2_专家评审_2026-10-19.html](Motor_v2_专家评审_2026-10-19.html)。
| 条目 | 原结论 | 当前状态 | 证据 / 说明 |
|---|---|---|---|
| SUP-GLB-001 编译期常量(ADC/health/config constexpr) | 误报/偏离 | **仍成立** | ADC.cpp:22-47;health.cpp:107;config.h 全部为 constexpr |
| 硬件单例 FOC / sampler / CAN0_transmiter / KTH7823 / thermalController / Version | 接受单例 | **仍成立** | FOC.cpp:28;ADC.cpp:60;Can.cpp:86;ang_sensor.cpp:39;health.cpp:104;initial.cpp:218 |
| TaskScheduler scheduler 短临界区 | 已保护,接受 | **仍成立** | main.cpp:68-72 关中断取快照并清位;taskFlags volatile(initial.h:75);两写入方同抢占级 |
| CAN RX/TX 队列临界区与 PRIMASK 恢复 | 已保护,接受 | **基本成立,但有遗漏** | Can.cpp:644-731、480;遗漏:主循环 CANFD_AsyncSend 直接写邮箱与 TX ISR 竞争(Can.cpp:1395/744,见M21) |
| ADC injected 电流缓存不加临界区 | 保留现状 | **风险仍在** | ADC.cpp:62-63、467-481 存在三相撕裂/丢 ready(M33);建议序号双读而非 PRIMASK |
| “加 PRIMASK 后实机连续重启”归因于时序 | 归因时序 | **归因不成立** | 10 周期级临界区不足以致复位;更可能是 update_current 提前返回分支(ADC.cpp:468)未恢复中断 + 与 systick 改动捆绑未隔离 + 50 ms 看门狗(IRC40K)余量小;无复位原因记录无法定位 |
| systick DWT CPU 占用统计 | 允许小误差 | **仍成立** | systick.cpp:75-79、193-206;仅诊断值 |
| ang_sensor SPI DMA 完成标志 | 接受 volatile 标志 | **已过时** | 已改为寄存器轮询 spi_rw_half_word_polling(ang_sensor.cpp:57-82),标志不存在;新问题:TRANS 等待无超时(:78) |
| Timer total_us100_counter | 仅 ISR 使用,接受 | **仍成立** | Timer.cpp:19、149-153;可改为 ISR 内 static |
| health voltage_state / last_vbus_slow | 模块私有,接受 | **仍成立(有残留)** | health.cpp:112-113 文件内 static;last_vbus_slow 只写不读 |
| SUP-CMP-001 normalizeAngle360 浮点比较 | 无需容差 | **比较结论成立,但有新缺陷** | ang_sensor.h:103-111 输入 Inf/NaN 时死循环(M16) |
| printf:FOC 齿槽校准打印位于 FOC_DEBUG | 正式版不编译 | **已过时** | 9fbd9c3 已移除齿槽补偿;FOC_DEBUG 仅剩 motor.cpp:245 一处 |
| versionToString 去 snprintf | 已改 | **仍成立** | initial.cpp:302-346 |
| AUTO-MET-001 calibrate_cogging_tick 拆分 | 已拆分 | **已过时** | 函数随齿槽功能删除;Can.cpp:1186 bit4 改为保留位 |
| AUTO-MET-001 communication_check 拆分 | 已拆分 | **已落实** | Can.cpp:841-1115 |
| AUTO-MET-001 do_task 拆分 | 已拆分 | **已落实** | tasks.cpp:296-399 |
| “实编仍需在 Keil/CMSIS/cbuild 环境验证” | 待验证 | **仍未验证** | 本次评审为静态评审,未实编;cproject -O3 覆盖 build-types(M38)需一并处理 |
## 需要写回原记录的修订要点
1. 删除/标注过时:齿槽校准相关两条(FOC_DEBUG 打印、calibrate_cogging_tick 拆分),9fbd9c3 已移除齿槽补偿。
2. ang_sensor 已改为寄存器轮询,“SPI DMA 完成标志”条目应替换为“轮询超时”条目,并补 TRANS 等待超时。
3. ADC 电流缓存:保留“不加长临界区”的原则,但撕裂风险真实存在,建议改用序号双读;“加 PRIMASK 导致重启”的归因应改为“未定位”,并在启动时记录复位原因后在独立分支复现。
4. CAN 队列:补充“主循环 CANFD_AsyncSend 直接写邮箱与 TX ISR 竞争”的偏离说明或修复。
5. normalizeAngle360:比较结论成立,但需补充非有限输入保护。
6. health:`last_vbus_slow` 只写不读,可删除。
7. 实编验证仍未完成;同时修正 cproject 中强制 -O3 覆盖 build-types 的问题。