Compare commits
16
Commits
9955132e0e
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3580f38e4 | ||
|
|
86ca7666fa | ||
|
|
72cc10b40d | ||
|
|
40fea328b0 | ||
|
|
0d3bd9cbc6 | ||
|
|
6bdae905d3 | ||
|
|
42dd1adc06 | ||
|
|
9fbd9c3d30 | ||
|
|
25ed2a2bde | ||
|
|
7c4aaa5ee6 | ||
|
|
76f7987238 | ||
|
|
42c9f2f669 | ||
|
|
b71fba2016 | ||
|
|
e646544f7f | ||
|
|
4c4e47e9e3 | ||
|
|
bec7c11b87 |
+15
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @file Algorithm/thermal.cpp
|
||||
* @brief 算法模块源文件
|
||||
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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
@@ -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
File diff suppressed because it is too large
Load Diff
+310
-6
@@ -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 ¤t_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 ¤t_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 ¤t_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 ¤t_message,
|
||||
u32 base_id);
|
||||
/**
|
||||
* @brief 解析控制命令并更新 FOC 目标值和 PID 参数
|
||||
* @param[in,out] current_message 当前 CAN 消息
|
||||
* @return void
|
||||
*/
|
||||
void handle_control_command(can_receive_message_struct ¤t_message);
|
||||
/**
|
||||
* @brief 解析复位命令各 bit 位并执行对应动作
|
||||
* @param[in,out] current_message 当前 CAN 消息
|
||||
* @return void
|
||||
*/
|
||||
void handle_reset_command(can_receive_message_struct ¤t_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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, ¤t_id, ¤t_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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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--;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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: 累加忙碌周期
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 的问题。
|
||||
Reference in New Issue
Block a user