refactor(FOC): 重构齿槽转矩校准状态机为独立函数

- 将原本在 calibrate_cogging_tick 中的大函数拆分为多个职责单一的函数
- 新增 start_cogging_calibration、cogging_settling_tick、cogging_sampling_tick、
  cogging_stopping_tick、cogging_done_tick 等状态处理函数
- 添加 process_cogging_run_data、start_next_cogging_run、build_cogging_tables、
  print_cogging_table、print_cogging_c_array 等辅助函数
- 移动全局 FOC 控制器单例注释到更合适位置
- 添加齿槽转矩校准参数常量定义和详细注释
- 添加防御性编程处理默认状态分支,避免状态机卡死
- 提高代码可读性和维护性

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

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

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

- 在 ADC.cpp 中添加关于ADC换算常量的详细注释
- 说明全局采样器单例的初始化和使用方式
This commit is contained in:
cbb
2026-05-24 01:21:41 +08:00
parent 4c4e47e9e3
commit e646544f7f
23 changed files with 1705 additions and 647 deletions
+170 -150
View File
@@ -20,6 +20,9 @@
#include <cstring>
#include <gd32c10x.h>
// 全局 FOC 控制器单例:系统只有一个电机控制对象。
// 初始化顺序由 main::hardware_init() 明确调用 FOC.init() 完成;
// 运行期主要由主循环控制任务访问,CAN 命令只更新目标/参数。
FOC_Controller FOC; // FOC控制初始化
extern sampler sampler;
extern angle_sensor KTH7823;
@@ -62,6 +65,14 @@ inline float FOC_Controller::get_cogging_compensation(float angle_el) {
// 启用方法:config.h 中 FOC_DEBUG 改为 1,通过 CAN 命令触发(RESET_ID bit4)
// 校准完成后把 C Array 硬编码到 init() 中,再改回 FOC_DEBUG 0
#if FOC_DEBUG
// 齿槽转矩校准参数
constexpr u32 COG_CALIB_SETTLE_TICKS = 16000; // 800ms / 50us
constexpr u32 COG_CALIB_STOP_TICKS = 10000; // 500ms / 50us
constexpr u32 COG_CALIB_TARGET_SAMPLES = 3 * COGGING_TABLE_SIZE * 8;
constexpr float COG_CALIB_SLOW_VEL = 1.0f; // 开环速度 rad/s
constexpr u32 COG_CALIB_SAMPLE_DIV = 20; // 1ms / 50us
constexpr u8 COG_CALIB_TOTAL_RUNS = 6; // 3正+3反
// 齿槽转矩校准静态数据
float FOC_Controller::cog_calib_run_data[6][COGGING_TABLE_SIZE] = {};
float FOC_Controller::cog_calib_accum[COGGING_TABLE_SIZE] = {};
@@ -70,172 +81,181 @@ u32 FOC_Controller::cog_calib_count[COGGING_TABLE_SIZE] = {};
/**
* @brief 齿槽转矩校准 - 非阻塞状态机
* 正转3次 + 反转3次,分开存储补偿表。
* 只打印最终平均数据和 C Array。
*/
void FOC_Controller::calibrate_cogging_tick(void) {
/** @brief SETTLE_TICKS 常量定义 */
constexpr u32 SETTLE_TICKS = 16000; // 800ms / 50us
/** @brief STOP_TICKS 常量定义 */
constexpr u32 STOP_TICKS = 10000; // 500ms / 50us
/** @brief TARGET_SAMPLES 常量定义 */
constexpr u32 TARGET_SAMPLES = 3 * COGGING_TABLE_SIZE * 8;
/** @brief SLOW_VEL 常量定义 */
constexpr float SLOW_VEL = 1.0f; // 开环速度 rad/s
/** @brief SAMPLE_DIV 常量定义 */
constexpr u32 SAMPLE_DIV = 20; // 1ms / 50us
/** @brief TOTAL_RUNS 常量定义 */
constexpr u8 TOTAL_RUNS = 6; // 3正+3反
// 入口只做状态派发,避免单函数堆叠所有校准分支。
switch (cog_calib_state) {
case CogCalibState::IDLE:
if (!cogging_calib_request) return;
cogging_calib_request = false;
cog_calib_run = 0;
cog_calib_saved_mode = control_mode;
cog_calib_saved_vel = target_velocity;
cogging_comp_enabled = false;
memset(cog_calib_run_data, 0, sizeof(cog_calib_run_data));
printf("\r\n[COG] === Cogging Calibration: 3 fwd + 3 rev ===\r\n");
control_mode = 6;
target_velocity = SLOW_VEL;
cog_calib_settle_tick = 0;
cog_calib_state = CogCalibState::SETTLING;
start_cogging_calibration();
return;
case CogCalibState::SETTLING:
cog_calib_settle_tick++;
if (cog_calib_settle_tick >= SETTLE_TICKS) {
cog_calib_state = CogCalibState::SAMPLING;
cog_calib_sample_count = 0;
memset(cog_calib_accum, 0, sizeof(cog_calib_accum));
memset(cog_calib_count, 0, sizeof(cog_calib_count));
}
cogging_settling_tick();
return;
case CogCalibState::SAMPLING:
// 采样分频
static u32 sample_div = 0;
sample_div++;
if (sample_div < SAMPLE_DIV) return;
sample_div = 0;
{
float real_angle = KTH7823.getcurrent_sensorangle();
float angle_el = normalizeAngle360(real_angle * PP + KTH7823.calibration_angle);
float iq = rt_current;
if (fabsf(iq) < 5000.0f) {
u16 idx = (u16)(angle_el / 360.0f * (float)COGGING_TABLE_SIZE);
if (idx >= COGGING_TABLE_SIZE) idx = COGGING_TABLE_SIZE - 1;
cog_calib_accum[idx] += iq;
cog_calib_count[idx]++;
}
cog_calib_sample_count++;
}
if (cog_calib_sample_count >= TARGET_SAMPLES) {
target_velocity = 0;
cog_calib_state = CogCalibState::STOPPING;
cog_calib_stop_tick = 0;
}
cogging_sampling_tick();
return;
case CogCalibState::STOPPING:
cog_calib_stop_tick++;
if (cog_calib_stop_tick >= STOP_TICKS) {
// 处理本轮数据:去直流
float dc = 0;
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cog_calib_run_data[cog_calib_run][i] =
(cog_calib_count[i] > 0) ? cog_calib_accum[i] / (float)cog_calib_count[i] : 0.0f;
dc += cog_calib_run_data[cog_calib_run][i];
}
dc /= (float)COGGING_TABLE_SIZE;
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cog_calib_run_data[cog_calib_run][i] -= dc;
}
printf("[COG] Run %d/%d done. DC=%.2f mA\r\n",
cog_calib_run + 1, TOTAL_RUNS, dc);
cog_calib_run++;
if (cog_calib_run < TOTAL_RUNS) {
// run 0~2 正转,run 3~5 反转
control_mode = 6;
target_velocity = (cog_calib_run < 3) ? SLOW_VEL : -SLOW_VEL;
cog_calib_state = CogCalibState::SETTLING;
cog_calib_settle_tick = 0;
} else {
cog_calib_state = CogCalibState::DONE;
}
}
cogging_stopping_tick();
return;
case CogCalibState::DONE:
// 正转平均 (run 0~2)
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cogging_table[i] = (cog_calib_run_data[0][i] +
cog_calib_run_data[1][i] +
cog_calib_run_data[2][i]) / 3.0f;
}
// 反转平均 (run 3~5)
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cogging_table_rev[i] = (cog_calib_run_data[3][i] +
cog_calib_run_data[4][i] +
cog_calib_run_data[5][i]) / 3.0f;
}
cogging_comp_enabled = true;
control_mode = cog_calib_saved_mode;
target_velocity = cog_calib_saved_vel;
// 打印正转平均
printf("\r\n[COG] === FWD Average ===\r\n");
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
float angle_deg = (float)i / (float)COGGING_TABLE_SIZE * 360.0f;
printf("[COG] %.2f, %.2f\r\n", angle_deg, cogging_table[i]);
fwdgt_counter_reload();
}
// 打印反转平均
printf("\r\n[COG] === REV Average ===\r\n");
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
float angle_deg = (float)i / (float)COGGING_TABLE_SIZE * 360.0f;
printf("[COG] %.2f, %.2f\r\n", angle_deg, cogging_table_rev[i]);
fwdgt_counter_reload();
}
// 打印正转 C Array
printf("\r\n[COG] --- FWD C Array ---\r\n");
printf("static const float COGGING_TABLE_FWD[COGGING_TABLE_SIZE] = {\r\n");
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
if (i % 8 == 0) printf(" ");
printf("%.2ff", cogging_table[i]);
if (i < COGGING_TABLE_SIZE - 1) printf(", ");
if (i % 8 == 7 || i == COGGING_TABLE_SIZE - 1) printf("\r\n");
fwdgt_counter_reload();
}
printf("};\r\n");
// 打印反转 C Array
printf("\r\n[COG] --- REV C Array ---\r\n");
printf("static const float COGGING_TABLE_REV[COGGING_TABLE_SIZE] = {\r\n");
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
if (i % 8 == 0) printf(" ");
printf("%.2ff", cogging_table_rev[i]);
if (i < COGGING_TABLE_SIZE - 1) printf(", ");
if (i % 8 == 7 || i == COGGING_TABLE_SIZE - 1) printf("\r\n");
fwdgt_counter_reload();
}
printf("};\r\n");
printf("\r\n[COG] === Done. ===\r\n");
cogging_done_tick();
return;
default:
// 防御性处理:枚举值异常时回到空闲态,避免状态机卡死。
cog_calib_state = CogCalibState::IDLE;
return;
}
}
void FOC_Controller::start_cogging_calibration(void) {
// IDLE 状态下没有请求则保持空闲,不扰动正常控制路径。
if (!cogging_calib_request) return;
cogging_calib_request = false;
cog_calib_run = 0;
// 保存原控制状态,校准完成后恢复现场。
cog_calib_saved_mode = control_mode;
cog_calib_saved_vel = target_velocity;
cogging_comp_enabled = false;
memset(cog_calib_run_data, 0, sizeof(cog_calib_run_data));
printf("\r\n[COG] === Cogging Calibration: 3 fwd + 3 rev ===\r\n");
control_mode = 6;
target_velocity = COG_CALIB_SLOW_VEL;
cog_calib_settle_tick = 0;
cog_calib_state = CogCalibState::SETTLING;
}
void FOC_Controller::cogging_settling_tick(void) {
// 给低速开环运行留出稳定时间,再开始采样。
cog_calib_settle_tick++;
if (cog_calib_settle_tick < COG_CALIB_SETTLE_TICKS) return;
cog_calib_state = CogCalibState::SAMPLING;
cog_calib_sample_count = 0;
memset(cog_calib_accum, 0, sizeof(cog_calib_accum));
memset(cog_calib_count, 0, sizeof(cog_calib_count));
}
void FOC_Controller::cogging_sampling_tick(void) {
static u32 sample_div = 0;
// 每 1ms 采一次,降低校准数据相关处理对实时控制的影响。
sample_div++;
if (sample_div < COG_CALIB_SAMPLE_DIV) return;
sample_div = 0;
float real_angle = KTH7823.getcurrent_sensorangle();
float angle_el = normalizeAngle360(real_angle * PP + KTH7823.calibration_angle);
float iq = rt_current;
if (fabsf(iq) < 5000.0f) {
u16 idx = (u16)(angle_el / 360.0f * (float)COGGING_TABLE_SIZE);
if (idx >= COGGING_TABLE_SIZE) idx = COGGING_TABLE_SIZE - 1;
cog_calib_accum[idx] += iq;
cog_calib_count[idx]++;
}
cog_calib_sample_count++;
if (cog_calib_sample_count >= COG_CALIB_TARGET_SAMPLES) {
target_velocity = 0;
cog_calib_state = CogCalibState::STOPPING;
cog_calib_stop_tick = 0;
}
}
void FOC_Controller::cogging_stopping_tick(void) {
// 每轮采样结束后先停稳,再计算本轮去直流数据。
cog_calib_stop_tick++;
if (cog_calib_stop_tick < COG_CALIB_STOP_TICKS) return;
float dc = process_cogging_run_data();
printf("[COG] Run %d/%d done. DC=%.2f mA\r\n",
cog_calib_run + 1, COG_CALIB_TOTAL_RUNS, dc);
cog_calib_run++;
if (cog_calib_run < COG_CALIB_TOTAL_RUNS) {
start_next_cogging_run();
} else {
cog_calib_state = CogCalibState::DONE;
}
}
float FOC_Controller::process_cogging_run_data(void) {
// 先求每个角度桶的平均电流,再统一去除直流偏置。
float dc = 0;
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cog_calib_run_data[cog_calib_run][i] =
(cog_calib_count[i] > 0) ? cog_calib_accum[i] / (float)cog_calib_count[i] : 0.0f;
dc += cog_calib_run_data[cog_calib_run][i];
}
dc /= (float)COGGING_TABLE_SIZE;
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cog_calib_run_data[cog_calib_run][i] -= dc;
}
return dc;
}
void FOC_Controller::start_next_cogging_run(void) {
control_mode = 6;
target_velocity = (cog_calib_run < 3) ? COG_CALIB_SLOW_VEL : -COG_CALIB_SLOW_VEL;
cog_calib_state = CogCalibState::SETTLING;
cog_calib_settle_tick = 0;
}
void FOC_Controller::cogging_done_tick(void) {
// 生成最终表后恢复用户进入校准前的控制模式和目标速度。
build_cogging_tables();
cogging_comp_enabled = true;
control_mode = cog_calib_saved_mode;
target_velocity = cog_calib_saved_vel;
print_cogging_table("\r\n[COG] === FWD Average ===\r\n", cogging_table);
print_cogging_table("\r\n[COG] === REV Average ===\r\n", cogging_table_rev);
print_cogging_c_array("\r\n[COG] --- FWD C Array ---\r\n",
"COGGING_TABLE_FWD", cogging_table);
print_cogging_c_array("\r\n[COG] --- REV C Array ---\r\n",
"COGGING_TABLE_REV", cogging_table_rev);
printf("\r\n[COG] === Done. ===\r\n");
cog_calib_state = CogCalibState::IDLE;
}
void FOC_Controller::build_cogging_tables(void) {
// 前 3 轮为正转,后 3 轮为反转,分别求平均表。
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
cogging_table[i] = (cog_calib_run_data[0][i] +
cog_calib_run_data[1][i] +
cog_calib_run_data[2][i]) / 3.0f;
cogging_table_rev[i] = (cog_calib_run_data[3][i] +
cog_calib_run_data[4][i] +
cog_calib_run_data[5][i]) / 3.0f;
}
}
void FOC_Controller::print_cogging_table(const char *title, float *table) {
printf("%s", title);
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
float angle_deg = (float)i / (float)COGGING_TABLE_SIZE * 360.0f;
printf("[COG] %.2f, %.2f\r\n", angle_deg, table[i]);
fwdgt_counter_reload();
}
}
void FOC_Controller::print_cogging_c_array(const char *title,
const char *arrayName,
float *table) {
printf("%s", title);
printf("static const float %s[COGGING_TABLE_SIZE] = {\r\n", arrayName);
for (int i = 0; i < COGGING_TABLE_SIZE; i++) {
if (i % 8 == 0) printf(" ");
printf("%.2ff", table[i]);
if (i < COGGING_TABLE_SIZE - 1) printf(", ");
if (i % 8 == 7 || i == COGGING_TABLE_SIZE - 1) printf("\r\n");
fwdgt_counter_reload();
}
printf("};\r\n");
}
#endif // FOC_DEBUG
// 初始变量及函数定义
+57 -1
View File
@@ -167,10 +167,66 @@ class FOC_Controller {
#if FOC_DEBUG
volatile bool cogging_calib_request = false;
/**
* @brief calibrate_cogging_tick 函数
* @brief 齿槽校准状态机入口,每个周期按当前状态派发到对应处理函数
* @return void
*/
void calibrate_cogging_tick(void);
/**
* @brief 处理齿槽校准空闲态,收到请求后保存现场并进入稳定等待态
* @return void
*/
void start_cogging_calibration(void);
/**
* @brief 处理齿槽校准稳定等待态,等待电机进入稳定低速运行
* @return void
*/
void cogging_settling_tick(void);
/**
* @brief 处理齿槽校准采样态,按分频周期累计每个电角度桶的电流
* @return void
*/
void cogging_sampling_tick(void);
/**
* @brief 处理齿槽校准停止态,等待停稳后完成本轮数据归一化
* @return void
*/
void cogging_stopping_tick(void);
/**
* @brief 处理齿槽校准完成态,生成补偿表并打印校准结果
* @return void
*/
void cogging_done_tick(void);
/**
* @brief 计算当前校准轮次的平均电流并去除直流偏置
* @return float 当前轮次直流偏置,单位 mA
*/
float process_cogging_run_data(void);
/**
* @brief 切换到下一轮齿槽校准运行方向并重新进入稳定等待态
* @return void
*/
void start_next_cogging_run(void);
/**
* @brief 根据 3 次正转和 3 次反转采样结果生成最终补偿表
* @return void
*/
void build_cogging_tables(void);
/**
* @brief 打印齿槽补偿表的人类可读角度/电流列表
* @param[in] title 表格标题字符串
* @param[in] table 待打印的补偿表
* @return void
*/
void print_cogging_table(const char *title, float *table);
/**
* @brief 打印可直接复制回源码的齿槽补偿 C 数组
* @param[in] title 输出标题字符串
* @param[in] arrayName C 数组名
* @param[in] table 待打印的补偿表
* @return void
*/
void print_cogging_c_array(const char *title, const char *arrayName,
float *table);
/**
* @brief CogCalibState 枚举定义
*/
+2
View File
@@ -4,6 +4,8 @@ project:
files:
- file: main.h
- file: main.cpp
- file: tasks.h
- file: tasks.cpp
- group: sys
files:
- file: sys/systick.cpp
+5
View File
@@ -16,6 +16,8 @@
#include <cstring>
#include <gd32c10x_dma.h>
// ADC 换算常量均为编译期常量,非可变全局状态。
// 用于 ADC 原始值到电压、温度、电流的固定比例换算。
/** @brief ADC_SCALE 常量定义 */
constexpr float ADC_SCALE = 3.3f / 4096.0f;
/** @brief TEMP_FACTOR 常量定义 */
@@ -27,6 +29,9 @@ constexpr float CURRENT_FACTOR =
1.0f / 50.0f * 100.0f; // 50倍放大,100倍电流采样电阻, 单位mA
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC;
// 全局采样器单例:系统只有一组 ADC 采样缓存,由 hardware_init() 调用 sampler.init() 初始化。
// regular DMA 写 adc_value[];injected ADC 中断写 adc_current_value[] 并置位 adc_current_frame_ready。
sampler sampler; // 采样器初始化
u16 adc_value[3] = {0}; // regular DMA: MCU温度 / DRV温度 / VBUS
static volatile u16 adc_current_value[3] = {0}; // injected 同步采样: U / V / W
+206 -209
View File
@@ -33,6 +33,7 @@
#define CAN_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// CAN0 固定外设号,编译期常量,非可变全局状态。
/** @brief CANX 常量定义 */
constexpr u16 CANX = CAN0;
extern FOC_Controller FOC;
@@ -44,7 +45,9 @@ extern FlashEEPROM flashEEP;
extern VersionManager Version; // 声明外部版本管理器实例
extern u32 motor_runtime_50us; // 电机驱动计算轮次(用于静默模式计时)
// 添加 DMA 相关变量
// CAN RX DMA 与收发队列模块状态:
// - DMA 元数据/busy 标志由 CAN RX 中断与 DMA 完成中断协同维护;
// - rx/tx 队列 head/tail/count 同时被主循环和中断访问,队列操作通过 CAN_EnterCritical()/CAN_ExitCritical() 保护。
dma_parameter_struct dma_rx_parameter;
uint8_t can_rx_buffer[64]; // CAN 接收缓冲区(支持CAN FD最大64字节)
static uint32_t can_rx_dma_words[16]; // CAN FD接收DMA按32位搬运,最多64字节
@@ -81,6 +84,8 @@ static volatile u8 tx_queue_count = 0;
// CAN FD DLC 9~15对应的非线性payload长度表
static constexpr u8 CANFD_DLC_LENGTH_TABLE[7] = {12, 16, 20, 24, 32, 48, 64};
// 全局 CAN 发送器单例:系统只有一个 CAN0 通信对象。
// 初始化顺序由 main::hardware_init() 调用 CAN0_transmiter.init() 保证。
CAN_transmiter CAN0_transmiter; // CAN 初始化
// ============================================================================
@@ -701,8 +706,10 @@ u8 CAN_transmiter::init() {
}
/**
* @brief CAN_EnterCritical 函数
* @return uint32_t 返回值
* @brief 进入 CAN 临界区并保存原 PRIMASK 状态
* @return uint32_t 进入临界区前的 PRIMASK,用于退出时恢复原中断状态
* @note 退出时使用 CAN_ExitCritical() 调用 __set_PRIMASK() 恢复,
* 因此这里不会直接与 __enable_irq() 成对出现。
*/
static uint32_t CAN_EnterCritical(void) {
uint32_t primask = __get_PRIMASK();
@@ -711,9 +718,10 @@ static uint32_t CAN_EnterCritical(void) {
}
/**
* @brief CAN_ExitCritical 函数
* @param[in] primask 参数
* @brief 退出 CAN 临界区并恢复进入前的中断屏蔽状态
* @param[in] primask CAN_EnterCritical() 返回的原 PRIMASK 状态
* @return void
* @note 不直接调用 __enable_irq(),避免误开启外层已经关闭的中断。
*/
static void CAN_ExitCritical(uint32_t primask) { __set_PRIMASK(primask); }
@@ -1074,228 +1082,217 @@ u8 CAN_transmiter::CANFD_all_Send() {
* 1: 有新消息
*/
void CAN_transmiter::communication_check() {
// CAN0 发送数据标志
if (SET == can0_send_flag) {
// 检查是否在静默模式中
if (!is_in_silent_mode()) {
update();
CANFD_all_Send();
} else {
check_silent_mode(); // 检查静默是否结束
}
can0_send_flag = RESET;
}
// 先处理周期发送,再抽空接收队列,保持原来的调度顺序。
handle_periodic_send_tick();
// 处理所有接收到的 CANFD 消息(从底层中断队列中取出)
can_rx_queue_item_t rx_item;
while (CAN_RX_QueuePop(&rx_item)) {
can_receive_message_struct current_message;
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &current_message);
current_message.rx_efid = rx_item.id;
current_message.rx_dlen = rx_item.length;
memcpy(current_message.rx_data, rx_item.data, rx_item.length);
handle_received_queue_item(rx_item.id, rx_item.length, rx_item.data);
}
}
// 提取基础 ID (低 16 位,去除高 16 位节点 ID 部分)
u32 base_id = current_message.rx_efid & 0xFFFF;
void CAN_transmiter::handle_periodic_send_tick() {
// 无发送请求时直接返回,避免每周期进入发送逻辑。
if (SET != can0_send_flag) return;
// 提取发送方的节点 ID
u16 sender_node_id = (u16)((current_message.rx_efid >> 16) & 0xFFFF);
if (!is_in_silent_mode()) {
update();
CANFD_all_Send();
} else {
check_silent_mode();
}
can0_send_flag = RESET;
}
// 特殊处理 base_id=0x0000(BOOT 命令):即使是其他节点的广播消息也要处理
if (base_id == CANFD_BOOT_ID) {
// 判断是广播消息还是其他节点的命令
if (sender_node_id == BROADCAST_NODE_ID || sender_node_id != node_id) {
// 广播消息或其他节点的命令 → 进入静默模式(停发 10 秒)
CAN_DEBUG_PRINT(
"Received base_id=0x0000 from node %d, entering silent mode for 10s\r\n",
sender_node_id);
enter_silent_mode();
} else {
// 自己节点的命令 → 执行 BOOT 功能
CAN_DEBUG_PRINT(
"Received base_id=0x0000 from self, jumping to BOOT\r\n");
motor_sleep();
flashEEP.save_system_config(false);
fmc_unlock();
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_NOT_AVAL); // 设置 APP 为不激活
fmc_lock();
jumpToBOOT();
}
continue; // BOOT 命令处理完跳过后续逻辑
}
void CAN_transmiter::handle_received_queue_item(u32 id, u8 length, const u8 *data) {
// 将轻量队列项还原为原 CAN 消息结构,后续沿用原解析逻辑。
can_receive_message_struct current_message;
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &current_message);
current_message.rx_efid = id;
current_message.rx_dlen = length;
memcpy(current_message.rx_data, data, length);
// 设置节点 ID 命令 (基础 ID: 0x0400)
// 注意:此命令在 is_message_for_me 检查之前处理,所有节点都会响应
// 这样即使节点ID已经改变,仍然可以用旧节点ID或广播ID来修改
else if (base_id == CANFD_SET_NODE_ID) {
// 打印接收到的原始数据
CAN_DEBUG_PRINT(
"SET_NODE_ID: rx_dlen=%d, data[0]=%02X data[1]=%02X\r\n",
current_message.rx_dlen, current_message.rx_data[0],
current_message.rx_data[1]);
// 解析新节点 ID - 支持 1 字节或 2 字节格式
u16 new_node_id;
if (current_message.rx_dlen >= 2) {
new_node_id = unpack_bytes_to_u16(current_message.rx_data, 0);
} else {
new_node_id = current_message.rx_data[0]; // 只发送 1 字节时直接使用
}
u16 old_node_id = node_id; // 保存旧节点 ID 用于构建响应 ID
CAN_DEBUG_PRINT(
"SET_NODE_ID: recv new_id=%d (0x%04X), old_id=%d\r\n",
u32 base_id = current_message.rx_efid & 0xFFFF;
u16 sender_node_id = (u16)((current_message.rx_efid >> 16) & 0xFFFF);
// BOOT 和 SET_NODE_ID 需要在节点过滤前处理。
if (handle_special_message(current_message, base_id, sender_node_id)) return;
if (!is_message_for_me(current_message.rx_efid)) return;
handle_node_message(current_message, base_id);
}
bool CAN_transmiter::handle_special_message(can_receive_message_struct &current_message,
u32 base_id, u16 sender_node_id) {
if (base_id == CANFD_BOOT_ID) {
handle_boot_message(sender_node_id);
return true;
}
if (base_id == CANFD_SET_NODE_ID) {
handle_set_node_id_message(current_message);
return true;
}
return false;
}
void CAN_transmiter::handle_boot_message(u16 sender_node_id) {
// 其他节点或广播 BOOT 命令只让本节点静默,避免抢占总线。
if (sender_node_id == BROADCAST_NODE_ID || sender_node_id != node_id) {
CAN_DEBUG_PRINT(
"Received base_id=0x0000 from node %d, entering silent mode for 10s\r\n",
sender_node_id);
enter_silent_mode();
return;
}
CAN_DEBUG_PRINT("Received base_id=0x0000 from self, jumping to BOOT\r\n");
motor_sleep();
flashEEP.save_system_config(false);
fmc_unlock();
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_NOT_AVAL);
fmc_lock();
jumpToBOOT();
}
void CAN_transmiter::handle_set_node_id_message(can_receive_message_struct &current_message) {
// 支持 1 字节和 2 字节节点 ID 命令,兼容旧上位机。
CAN_DEBUG_PRINT(
"SET_NODE_ID: rx_dlen=%d, data[0]=%02X data[1]=%02X\r\n",
current_message.rx_dlen, current_message.rx_data[0],
current_message.rx_data[1]);
u16 new_node_id = (current_message.rx_dlen >= 2) ?
unpack_bytes_to_u16(current_message.rx_data, 0) : current_message.rx_data[0];
u16 old_node_id = node_id;
CAN_DEBUG_PRINT("SET_NODE_ID: recv new_id=%d (0x%04X), old_id=%d\r\n",
new_node_id, new_node_id, old_node_id);
if (new_node_id <= MAX_NODE_ID && new_node_id != BROADCAST_NODE_ID) {
// 先发送响应(使用旧节点ID构建CAN ID)
// 响应数据格式:[status:1byte][new_node_id:2byte] = 3 字节
transmit_data[0] = 0x01; // 成功
transmit_data[1] = (u8)(new_node_id & 0xFF); // 新 ID 低字节
transmit_data[2] =
(u8)((new_node_id >> 8) & 0xFF); // 新 ID 高字节
transmit_data[1] = (u8)(new_node_id & 0xFF);
transmit_data[2] = (u8)((new_node_id >> 8) & 0xFF);
// 响应帧使用旧节点 ID 构造,确保上位机能收到确认。
node_id = old_node_id;
// 使用旧节点 ID 发送响应
node_id = old_node_id;
u8 send_result = CANFD_AsyncSend(build_can_id(CANFD_SET_NODE_ID), 3,
transmit_data);
if (new_node_id > MAX_NODE_ID || new_node_id == BROADCAST_NODE_ID) {
CAN_DEBUG_PRINT("SET_NODE_ID: invalid new_id=%d (max=%d)\r\n",
new_node_id, MAX_NODE_ID);
transmit_data[0] = 0x00;
CANFD_AsyncSend(build_can_id(CANFD_SET_NODE_ID), 3, transmit_data);
return;
}
// 然后保存新节点 ID 到 Flash
u8 save_status = save_node_id_to_flash(new_node_id);
transmit_data[0] = 0x01;
u8 send_result = CANFD_AsyncSend(build_can_id(CANFD_SET_NODE_ID), 3,
transmit_data);
u8 save_status = save_node_id_to_flash(new_node_id);
CAN_DEBUG_PRINT(
"SET_NODE_ID: save_status=%d, send_result=%d\r\n",
save_status, send_result);
CAN_DEBUG_PRINT("SET_NODE_ID: save_status=%d, send_result=%d\r\n",
save_status, send_result);
if (save_status == FLASH_EEPROM_OK) {
CAN_DEBUG_PRINT(
"SET_NODE_ID: SUCCESS, node_id updated from %d to %d\r\n",
old_node_id, new_node_id);
} else {
CAN_DEBUG_PRINT(
"SET_NODE_ID: Flash write FAILED, status=%d\r\n",
save_status);
// 发送错误响应
transmit_data[0] = 0x00; // 失败
node_id = old_node_id;
CANFD_AsyncSend(build_can_id(CANFD_SET_NODE_ID), 3,
transmit_data);
}
} else {
// 发送错误响应 - 使用旧节点 ID 构建响应 ID
CAN_DEBUG_PRINT("SET_NODE_ID: invalid new_id=%d (max=%d)\r\n",
new_node_id, MAX_NODE_ID);
transmit_data[0] = 0x00; // 失败
transmit_data[1] = (u8)(new_node_id & 0xFF);
transmit_data[2] = (u8)((new_node_id >> 8) & 0xFF);
node_id = old_node_id; // 临时恢复旧 ID 用于构建响应
CANFD_AsyncSend(build_can_id(CANFD_SET_NODE_ID), 3,
transmit_data);
// 保持原 ID 不变
}
continue; // SET_NODE_ID 命令处理完跳过后续逻辑
}
if (save_status == FLASH_EEPROM_OK) {
CAN_DEBUG_PRINT("SET_NODE_ID: SUCCESS, node_id updated from %d to %d\r\n",
old_node_id, new_node_id);
return;
}
// 其他 base_id 的消息,检查是否是发给本节点的
if (!is_message_for_me(current_message.rx_efid)) {
continue; // 不是发给本节点的消息,跳过
}
CAN_DEBUG_PRINT("SET_NODE_ID: Flash write FAILED, status=%d\r\n", save_status);
transmit_data[0] = 0x00;
node_id = old_node_id;
CANFD_AsyncSend(build_can_id(CANFD_SET_NODE_ID), 3, transmit_data);
}
if (base_id == CANFD_CMD_ID) {
// 控制模式
FOC.control_mode = current_message.rx_data[0];
// 目标电压限制
FOC.limit_voltage =
float(unpack_bytes_to_u16(current_message.rx_data, 1)) / 1000;
// 目标电流
target_current_union.f =
unpack_bytes_to_float(current_message.rx_data, 3).f;
FOC.current_loop.set_limit(target_current_union.f);
FOC.vel_inMITloop.set_limit(target_current_union.f);
// 目标速度
target_velocity_union.f =
unpack_bytes_to_float(current_message.rx_data, 7).f;
FOC.ang_inVelAngloop.set_limit(target_velocity_union.f);
FOC.ang_inMITloop.set_limit(target_velocity_union.f);
// 目标角度
target_angle_union.f =
unpack_bytes_to_float(current_message.rx_data, 11).f;
// 赋值
FOC.target_angle = target_angle_union.f;
FOC.target_velocity = target_velocity_union.f;
FOC.target_current = target_current_union.f;
void CAN_transmiter::handle_node_message(can_receive_message_struct &current_message,
u32 base_id) {
// 这里只处理已经确认发给本节点的普通协议消息。
if (base_id == CANFD_CMD_ID) {
handle_control_command(current_message);
} else if (base_id == CANFD_RESET_ID) {
handle_reset_command(current_message);
} else if (base_id == CANFD_VERSION_ID) {
Version.sendVersionInfoViaCan();
} else if (base_id == CANFD_OTA_ACK_ID) {
handle_ota_ack_message();
} else if (base_id == CANFD_GET_NODE_ID) {
pack_u16_to_bytes(transmit_data, 0, get_node_id());
CANFD_AsyncSend(build_can_id(CANFD_GET_NODE_ID), 2, transmit_data);
} else {
CAN_DEBUG_PRINT("Unknown CAN ID: %X (node_id=%d)\n",
current_message.rx_efid, node_id);
}
}
// 解析 PID 参数
float voltage = FOC.limit_voltage;
// PID 参数
u16 P_config = unpack_bytes_to_u16(current_message.rx_data, 15);
FOC.cur_loop_p = DEFAULT_current_loop_p * P_config / voltage;
FOC.cur_MIT_p = DEFAULT_current_MIT_p * P_config / voltage;
void CAN_transmiter::handle_control_command(can_receive_message_struct &current_message) {
// 控制帧布局:模式、电压限制、目标电流/速度/角度、三组 P 参数。
FOC.control_mode = current_message.rx_data[0];
FOC.limit_voltage = float(unpack_bytes_to_u16(current_message.rx_data, 1)) / 1000;
target_current_union.f = unpack_bytes_to_float(current_message.rx_data, 3).f;
FOC.current_loop.set_limit(target_current_union.f);
FOC.vel_inMITloop.set_limit(target_current_union.f);
target_velocity_union.f = unpack_bytes_to_float(current_message.rx_data, 7).f;
FOC.ang_inVelAngloop.set_limit(target_velocity_union.f);
FOC.ang_inMITloop.set_limit(target_velocity_union.f);
target_angle_union.f = unpack_bytes_to_float(current_message.rx_data, 11).f;
FOC.target_angle = target_angle_union.f;
FOC.target_velocity = target_velocity_union.f;
FOC.target_current = target_current_union.f;
P_config = unpack_bytes_to_u16(current_message.rx_data, 17);
FOC.vel_loop_p = DEFAULT_vel_loop_p * P_config / voltage;
FOC.vel_angloop_p = DEFAULT_vel_angloop_p * P_config;
FOC.vel_MIT_p = DEFAULT_vel_MIT_p * P_config;
float voltage = FOC.limit_voltage;
u16 P_config = unpack_bytes_to_u16(current_message.rx_data, 15);
FOC.cur_loop_p = DEFAULT_current_loop_p * P_config / voltage;
FOC.cur_MIT_p = DEFAULT_current_MIT_p * P_config / voltage;
P_config = unpack_bytes_to_u16(current_message.rx_data, 19);
FOC.ang_loop_p = DEFAULT_angle_loop_p * P_config / voltage;
FOC.ang_angloop_p = DEFAULT_angle_angloop_p * P_config / voltage;
FOC.ang_MIT_p = DEFAULT_angle_MIT_p * P_config / voltage;
} else if (base_id == CANFD_RESET_ID) {
u8 first_byte = current_message.rx_data[0];
u8 eigth_byte = current_message.rx_data[7];
// 电机重置
if (first_byte & 0x01) {
motor_reset();
testfloat_union.f = KTH7823.getcurrent_sensorangle();
}
// EEPROM 重置
if (first_byte >> 1 & 0x01) {
// flashEEP.format();
}
// 系统参数保存
if (first_byte >> 2 & 0x01) {
flashEEP.save_system_config(false);
}
// 角度传感器重置
if (first_byte >> 3 & 0x01) {
KTH7823.angle_set(0);
Angle_kalman.init(0); // 角度滤波
Vel_kalman.init(0); // 角度滤波
}
// 齿槽转矩校准 (first_byte bit4) — DEBUG,平时不启用
#if FOC_DEBUG
if (first_byte >> 4 & 0x01) {
FOC.cogging_calib_request = true;
}
#endif
// 休眠
if (eigth_byte & 0x80 >> 7) {
app_prepare_deep_sleep(true);
// CAN_Sleep();
}
} else if (base_id == CANFD_VERSION_ID) {
// 处理版本号请求,发送当前系统版本信息
Version.sendVersionInfoViaCan();
} else if (base_id == CANFD_OTA_ACK_ID) {
volatile u32 *flash_ptr = (volatile u32 *)n_APP_LATEST_EN_ADDR;
u32 app_enable = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_LATEST_CHECK_ADDR;
u32 app_check = *flash_ptr;
transmit_data[0] = 0x0A;
transmit_data[1] = 0xAC;
delay_1ms(1);
if (app_enable == OTA_AVAL && app_check == OTA_AVAL) {
CANFD_AsyncSend(build_can_id(CANFD_OTA_ACK_ID), 2,
transmit_data);
}
}
// 查询节点 ID 命令 (基础 ID: 0x0402)
else if (base_id == CANFD_GET_NODE_ID) {
pack_u16_to_bytes(transmit_data, 0, get_node_id());
CANFD_AsyncSend(build_can_id(CANFD_GET_NODE_ID), 2, transmit_data);
} else {
CAN_DEBUG_PRINT("Unknown CAN ID: %X (node_id=%d)\n",
current_message.rx_efid, node_id);
}
P_config = unpack_bytes_to_u16(current_message.rx_data, 17);
FOC.vel_loop_p = DEFAULT_vel_loop_p * P_config / voltage;
FOC.vel_angloop_p = DEFAULT_vel_angloop_p * P_config;
FOC.vel_MIT_p = DEFAULT_vel_MIT_p * P_config;
P_config = unpack_bytes_to_u16(current_message.rx_data, 19);
FOC.ang_loop_p = DEFAULT_angle_loop_p * P_config / voltage;
FOC.ang_angloop_p = DEFAULT_angle_angloop_p * P_config / voltage;
FOC.ang_MIT_p = DEFAULT_angle_MIT_p * P_config / voltage;
}
void CAN_transmiter::handle_reset_command(can_receive_message_struct &current_message) {
// RESET 帧按 bit 位触发独立动作,保持与原协议兼容。
u8 first_byte = current_message.rx_data[0];
u8 eigth_byte = current_message.rx_data[7];
if (first_byte & 0x01) {
motor_reset();
testfloat_union.f = KTH7823.getcurrent_sensorangle();
}
if (first_byte >> 1 & 0x01) {
// flashEEP.format();
}
if (first_byte >> 2 & 0x01) {
flashEEP.save_system_config(false);
}
if (first_byte >> 3 & 0x01) {
KTH7823.angle_set(0);
Angle_kalman.init(0);
Vel_kalman.init(0);
}
#if FOC_DEBUG
if (first_byte >> 4 & 0x01) {
FOC.cogging_calib_request = true;
}
#endif
if (eigth_byte & 0x80 >> 7) {
app_prepare_deep_sleep(true);
}
}
void CAN_transmiter::handle_ota_ack_message() {
// 只有当前 APP 标记为可用且校验通过时才回 ACK。
volatile u32 *flash_ptr = (volatile u32 *)n_APP_LATEST_EN_ADDR;
u32 app_enable = *flash_ptr;
flash_ptr = (volatile u32 *)n_APP_LATEST_CHECK_ADDR;
u32 app_check = *flash_ptr;
transmit_data[0] = 0x0A;
transmit_data[1] = 0xAC;
delay_1ms(1);
if (app_enable == OTA_AVAL && app_check == OTA_AVAL) {
CANFD_AsyncSend(build_can_id(CANFD_OTA_ACK_ID), 2, transmit_data);
}
}
+59
View File
@@ -235,6 +235,65 @@ class CAN_transmiter {
* @return void
*/
void check_silent_mode(); // 检查静默模式是否结束
/**
* @brief 处理周期性发送请求,静默模式下只检查是否退出静默
* @return void
*/
void handle_periodic_send_tick();
/**
* @brief 将底层接收队列项转换为 CAN 消息并按协议分发
* @param[in] id 接收到的扩展 CAN ID
* @param[in] length 接收数据长度
* @param[in] data 接收数据指针
* @return void
*/
void handle_received_queue_item(u32 id, u8 length, const u8 *data);
/**
* @brief 处理不受本节点过滤限制的特殊消息
* @param[in,out] current_message 当前 CAN 消息
* @param[in] base_id 低 16 位基础 ID
* @param[in] sender_node_id 发送方节点 ID
* @return bool true 表示消息已处理,false 表示继续按普通节点消息处理
*/
bool handle_special_message(can_receive_message_struct &current_message,
u32 base_id, u16 sender_node_id);
/**
* @brief 处理 BOOT 命令或其他节点 BOOT 广播导致的静默逻辑
* @param[in] sender_node_id 发送方节点 ID
* @return void
*/
void handle_boot_message(u16 sender_node_id);
/**
* @brief 处理节点 ID 设置命令,发送应答并保存新 ID 到 Flash
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_set_node_id_message(can_receive_message_struct &current_message);
/**
* @brief 处理已经确认发给本节点的普通协议消息
* @param[in,out] current_message 当前 CAN 消息
* @param[in] base_id 低 16 位基础 ID
* @return void
*/
void handle_node_message(can_receive_message_struct &current_message,
u32 base_id);
/**
* @brief 解析控制命令并更新 FOC 目标值和 PID 参数
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_control_command(can_receive_message_struct &current_message);
/**
* @brief 解析复位命令各 bit 位并执行对应动作
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_reset_command(can_receive_message_struct &current_message);
/**
* @brief 处理 OTA ACK 查询,APP 有效时回发确认帧
* @return void
*/
void handle_ota_ack_message();
public:
// 节点 ID 相关辅助函数 (使用高 16 位存储节点 ID)
+1 -1
View File
@@ -15,7 +15,7 @@
#include "gd32c10x_timer.h"
#include "initial.h"
volatile u32 total_us100_counter = 0; // 总计数器,每 100 微秒增加 1
volatile u32 total_us100_counter = 0; // TIMER2 ISR 内部 100us 调度索引
extern u16 adc_value[3];
extern FOC_Controller FOC;
extern TaskScheduler scheduler;
+3 -1
View File
@@ -34,10 +34,12 @@
/** @brief DRV_SPI_CS_DISABLE 宏定义 */
#define DRV_SPI_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
// DMA 传输完成标志
// SPI DMA 完成标志:由 DMA ISR 置位,spi_rw_half_word_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;
+2
View File
@@ -35,7 +35,9 @@ void motor_init(void) {
*/
void motor_reset(void) {
motor_sleep();
#if FOC_DEBUG
printf("motor reseting...\r\n");
#endif
motor_resume();
}
+6 -109
View File
@@ -5,6 +5,7 @@
*/
#include "main.h"
#include "tasks.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
@@ -30,29 +31,16 @@ 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;
// 函数指针数组,包含各种控制模式对应的函数
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 返回值
@@ -112,94 +100,3 @@ u8 hardware_init(void) {
return init_status;
}
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 参数
* @return void
*/
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)
static u8 vcheck_div = 0;
if (++vcheck_div >= 2u) {
vcheck_div = 0;
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();
}
}
}
// 齿槽转矩校准 tick(DEBUG,平时不启用)
#if FOC_DEBUG
FOC.calibrate_cogging_tick();
#endif
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: 累加忙碌周期
}
-7
View File
@@ -36,13 +36,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define MAIN_H
#include <cstdint>
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 参数
* @return void
*/
void do_task(u32 currentTasks);
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
+1 -164
View File
@@ -12,71 +12,41 @@
// 如果使用 CMSIS DSP 库,需要定义 ARM_MATH_CM4
#define ARM_MATH_CM4
/** @brief EEP_DEBUG 宏定义 */
#define EEP_DEBUG 0
/** @brief OTA_DEBUG 宏定义 */
#define OTA_DEBUG 0
/** @brief SLEEP_DEBUG 宏定义 */
#define SLEEP_DEBUG 0
/** @brief ANG_SENS_DEBUG 宏定义 */
#define ANG_SENS_DEBUG 0
/** @brief CAN_DEBUG 宏定义 */
#define CAN_DEBUG 0
/** @brief MAIN_DEBUG 宏定义 */
#define MAIN_DEBUG 0
/** @brief FOC_DEBUG 宏定义 */
#define FOC_DEBUG 0
// 版本号定义 - 可根据实际项目进行调整
/** @brief SOFTWARE_VERSION_MAJOR 常量定义 */
constexpr u16 SOFTWARE_VERSION_MAJOR = 26;
/** @brief SOFTWARE_VERSION_MINOR 常量定义 */
constexpr u16 SOFTWARE_VERSION_MINOR = 5;
/** @brief SOFTWARE_VERSION_PATCH 常量定义 */
constexpr u16 SOFTWARE_VERSION_PATCH = 19;
/** @brief SOFTWARE_VERSION_BUILD 常量定义 */
constexpr u16 SOFTWARE_VERSION_PATCH = 24;
constexpr u16 SOFTWARE_VERSION_BUILD = 2;
/** @brief HARDWARE_VERSION_MAJOR 常量定义 */
constexpr u16 HARDWARE_VERSION_MAJOR = 25;
/** @brief HARDWARE_VERSION_MINOR 常量定义 */
constexpr u16 HARDWARE_VERSION_MINOR = 12;
/** @brief HARDWARE_VERSION_PATCH 常量定义 */
constexpr u16 HARDWARE_VERSION_PATCH = 01;
/** @brief HARDWARE_VERSION_BUILD 常量定义 */
constexpr u16 HARDWARE_VERSION_BUILD = 0;
/** @brief BOOT_VERSION_MAJOR 常量定义 */
constexpr u16 BOOT_VERSION_MAJOR = 26;
/** @brief BOOT_VERSION_MINOR 常量定义 */
constexpr u16 BOOT_VERSION_MINOR = 1;
/** @brief BOOT_VERSION_PATCH 常量定义 */
constexpr u16 BOOT_VERSION_PATCH = 12;
/** @brief BOOT_VERSION_BUILD 常量定义 */
constexpr u16 BOOT_VERSION_BUILD = 0;
// Flash 分区配置
/** @brief BOOTLOADER_SIZE 常量定义 */
constexpr u32 BOOTLOADER_SIZE = 0x6000; // 24KB
/** @brief BOOTLOADER_START_ADDR 常量定义 */
constexpr u32 BOOTLOADER_START_ADDR = 0x08000000; // BOOT 起始地址
/** @brief APPLATEST_START_ADDR 常量定义 */
constexpr u32 APPLATEST_START_ADDR = 0x08012000; // APP 起始地址
/** @brief APPBACK_START_ADDR 常量定义 */
constexpr u32 APPBACK_START_ADDR = 0x08006000; // APPBACK 起始地址
/** @brief APP_MAX_SIZE 常量定义 */
constexpr u32 APP_MAX_SIZE = 0xC000; // 48KB
/** @brief FMC_PAGE_SIZE 常量定义 */
constexpr u32 FMC_PAGE_SIZE = 0x400; // 1KB
/** @brief SLEEP_CONFIG_START 常量定义 */
constexpr u32 SLEEP_CONFIG_START = 0x0801E400; // 睡眠配置起始地址
/** @brief SLEEP_CONFIG_END 常量定义 */
constexpr u32 SLEEP_CONFIG_END = 0x0801E800; // 睡眠配置结束地址
/** @brief FLASH_EMPTY 常量定义 */
constexpr u32 FLASH_EMPTY = 0xFFFFFFFF; // 空数据
/** @brief SLEEP_ENABLE 常量定义 */
constexpr u32 SLEEP_ENABLE = 0x11111111; // 睡眠使能
/** @brief SLEEP_DISABLE 常量定义 */
constexpr u32 SLEEP_DISABLE = 0x00000000; // 睡眠禁止
// 节点 ID 配置 (用于多电机 CAN 总线隔离)
@@ -88,19 +58,12 @@ constexpr u32 SLEEP_DISABLE = 0x00000000; // 睡眠禁止
// - 采用双重备份机制 (主备份 + 备用备份)
// - 使用魔数 (NODE_ID_MAGIC) 验证数据有效性,防止位翻转导致错读
// - 使用写入计数器区分最新数据
/** @brief NODE_ID_PAGE_ADDR 常量定义 */
constexpr u32 NODE_ID_PAGE_ADDR = 0x0801E800; // 节点 ID 存储页 (1KB,独立区域)
/** @brief NODE_ID_PRIMARY_ADDR 常量定义 */
constexpr u32 NODE_ID_PRIMARY_ADDR = 0x0801E800; // 主存储地址
/** @brief NODE_ID_BACKUP_ADDR 常量定义 */
constexpr u32 NODE_ID_BACKUP_ADDR = 0x0801E840; // 备份存储地址 (偏移64字节)
/** @brief NODE_ID_MAGIC 常量定义 */
constexpr u32 NODE_ID_MAGIC = 0x4E4F4445; // 魔数 'NODE' 用于验证数据有效性
/** @brief DEFAULT_NODE_ID 常量定义 */
constexpr u16 DEFAULT_NODE_ID = 1; // 默认节点 ID
/** @brief BROADCAST_NODE_ID 常量定义 */
constexpr u16 BROADCAST_NODE_ID = 0x1FFF; // 广播 ID (所有节点响应)
/** @brief MAX_NODE_ID 常量定义 */
constexpr u16 MAX_NODE_ID = 0x1FFE; // 最大有效节点 ID (0~0x1FFE)
// 节点 ID 存储结构 (存储在Flash中的格式)
@@ -109,296 +72,174 @@ constexpr u16 MAX_NODE_ID = 0x1FFE; // 最大有效节点 ID (0~0x1FFE)
// 地址偏移 0x06: 保留 (u16,填充为0xFFFF)
// 地址偏移 0x08: 写入计数器 (u32,用于区分最新数据)
// 地址偏移 0x0C: CRC32 校验 (u32)
/** @brief NODE_ID_STRUCT_SIZE 常量定义 */
constexpr u8 NODE_ID_STRUCT_SIZE = 0x10; // 结构体大小 16 字节
/** @brief NODE_ID_MAGIC_OFFSET 常量定义 */
constexpr u8 NODE_ID_MAGIC_OFFSET = 0x00; // 魔数偏移
/** @brief NODE_ID_VALUE_OFFSET 常量定义 */
constexpr u8 NODE_ID_VALUE_OFFSET = 0x04; // 节点ID值偏移
/** @brief NODE_ID_COUNTER_OFFSET 常量定义 */
constexpr u8 NODE_ID_COUNTER_OFFSET = 0x08; // 写入计数器偏移
/** @brief NODE_ID_CRC_OFFSET 常量定义 */
constexpr u8 NODE_ID_CRC_OFFSET = 0x0C; // CRC偏移
// BOOT 相关
/** @brief OTA_CONFIG_NUM 常量定义 */
constexpr u32 OTA_CONFIG_NUM = 6;
/** @brief n_APP_LATEST_AVAL_ADDR 常量定义 */
constexpr u32 n_APP_LATEST_AVAL_ADDR = 0x0801E000; // 最新 APP 可用地址
/** @brief n_APP_BACKUP_AVAL_ADDR 常量定义 */
constexpr u32 n_APP_BACKUP_AVAL_ADDR = 0x0801E004; // 备份 APP 可用地址
/** @brief n_APP_LATEST_EN_ADDR 常量定义 */
constexpr u32 n_APP_LATEST_EN_ADDR = 0x0801E008; // 最新 APP 激活
/** @brief n_APP_BACKUP_EN_ADDR 常量定义 */
constexpr u32 n_APP_BACKUP_EN_ADDR = 0x0801E00C; // 备份 APP 激活
/** @brief n_APP_LATEST_CHECK_ADDR 常量定义 */
constexpr u32 n_APP_LATEST_CHECK_ADDR = 0x0801E010; // 最新 APP 校验
/** @brief n_APP_BACKUP_CHECK_ADDR 常量定义 */
constexpr u32 n_APP_BACKUP_CHECK_ADDR = 0x0801E014; // 备份 APP 校验
/** @brief OTA_NOT_AVAL 常量定义 */
constexpr u32 OTA_NOT_AVAL = 0xFFFFFFFF;
/** @brief OTA_AVAL 常量定义 */
constexpr u32 OTA_AVAL = 0x00000000; // 全零表示可用
// 参数配置
/** @brief PP 常量定义 */
constexpr u8 PP = 11;
/** @brief DIR 常量定义 */
constexpr float DIR = 1.0f; // 默认方向:-1.0f 反转,1.0f 正转
/** @brief REDUCTION_RATIO 常量定义 */
constexpr u16 REDUCTION_RATIO = 25;
/** @brief CURRENT_OFFSET 常量定义 */
constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 当前电流传感器的零点偏移
/** @brief HOLD_PWM_DUTY 常量定义 */
constexpr float HOLD_PWM_DUTY = 0.1f;
/** @brief ANGLESENS_OFFSET 常量定义 */
constexpr u16 ANGLESENS_OFFSET = 0;
/** @brief ANGLESENS_CALIBRATION_ANGLE 常量定义 */
constexpr float ANGLESENS_CALIBRATION_ANGLE = 83.0f;
/** @brief KE_FACTOR 常量定义 */
constexpr float KE_FACTOR = 0.0f;
// 角度/速度 α-β 观测器参数
/** @brief ANGLE_KALMAN_INITIAL_STATE 常量定义 */
constexpr float ANGLE_KALMAN_INITIAL_STATE = 0.0f;
/** @brief ANGLE_KALMAN_INITIAL_COVARIANCE 常量定义 */
constexpr float ANGLE_KALMAN_INITIAL_COVARIANCE = 0.0f;
/** @brief ANGLE_KALMAN_PROCESS_NOISE_Q 常量定义 */
constexpr float ANGLE_KALMAN_PROCESS_NOISE_Q = 0.0f;
/** @brief ANGLE_KALMAN_MEASUREMENT_NOISE_R 常量定义 */
constexpr float ANGLE_KALMAN_MEASUREMENT_NOISE_R = 0.0f;
/** @brief ANGLE_OBSERVER_ALPHA 常量定义 */
constexpr float ANGLE_OBSERVER_ALPHA = 0.75f; // 角度校正增益,越大越跟测量
/** @brief ANGLE_OBSERVER_BETA 常量定义 */
constexpr float ANGLE_OBSERVER_BETA = 0.002f; // 速度校正增益,原0.001微调至0.002
// 旧速度 Kalman 参数保留用于接口兼容;主速度输出现在直接来自 α-β 观测器
/** @brief VELOCITY_KALMAN_INITIAL_STATE 常量定义 */
constexpr float VELOCITY_KALMAN_INITIAL_STATE = 0.0f;
/** @brief VELOCITY_KALMAN_INITIAL_COVARIANCE 常量定义 */
constexpr float VELOCITY_KALMAN_INITIAL_COVARIANCE = 1e-3f;
/** @brief VELOCITY_KALMAN_PROCESS_NOISE_Q 常量定义 */
constexpr float VELOCITY_KALMAN_PROCESS_NOISE_Q =
5e-4f; // 略增大,减少拖尾
/** @brief VELOCITY_KALMAN_MEASUREMENT_NOISE_R 常量定义 */
constexpr float VELOCITY_KALMAN_MEASUREMENT_NOISE_R =
0.05f; // 明显小于旧值,避免压得太死
// 电流卡尔曼滤波器参数
/** @brief CURRENT_KALMAN_INITIAL_STATE 常量定义 */
constexpr float CURRENT_KALMAN_INITIAL_STATE = 0.0f;
/** @brief CURRENT_KALMAN_INITIAL_COVARIANCE 常量定义 */
constexpr float CURRENT_KALMAN_INITIAL_COVARIANCE = 100.0f; // 增大初始协方差
/** @brief CURRENT_KALMAN_PROCESS_NOISE_Q 常量定义 */
constexpr float CURRENT_KALMAN_PROCESS_NOISE_Q = 10.0f; // 降低过程噪声
/** @brief CURRENT_KALMAN_MEASUREMENT_NOISE_R 常量定义 */
constexpr float CURRENT_KALMAN_MEASUREMENT_NOISE_R = 5e-5f; // 增大测量噪声
// PID 配置
/** @brief DEFAULT_current_loop_p 常量定义 */
constexpr float DEFAULT_current_loop_p = 0.1f / 100 * 12; // 电流 P
/** @brief current_loop_Ki 常量定义 */
constexpr float current_loop_Ki = 0.55f; // 电流 P
/** @brief current_loop_Kd 常量定义 */
constexpr float current_loop_Kd = 3e-4f; // 电流 P
/** @brief DEFAULT_vel_loop_p 常量定义 */
constexpr float DEFAULT_vel_loop_p = 0.018f / 100 * 12; // 速度 P
/** @brief vel_loop_Ku 常量定义 */
constexpr float vel_loop_Ku = 3.600000e-03 * 0.7 * 100 / 12;
/** @brief vel_loop_Tu 常量定义 */
constexpr float vel_loop_Tu = 17.0f * 1e-3f;
/** @brief vel_loop_Ki 常量定义 */
constexpr float vel_loop_Ki = 1.2 * vel_loop_Ku / vel_loop_Tu;
/** @brief vel_loop_Kd 常量定义 */
constexpr float vel_loop_Kd = 0.075 * vel_loop_Ku * vel_loop_Tu * 2;
/** @brief DEFAULT_vel_angloop_p 常量定义 */
constexpr float DEFAULT_vel_angloop_p = 0.4f / 100; // 速度 速度 - 角度 P
/** @brief vel_angloop_Ki 常量定义 */
constexpr float vel_angloop_Ki = 5.0f; // 速度 速度 - 角度 I
/** @brief vel_angloop_Kd 常量定义 */
constexpr float vel_angloop_Kd = 5e-05f; // 速度 速度 - 角度 D
/** @brief DEFAULT_angle_angloop_p 常量定义 */
constexpr float DEFAULT_angle_angloop_p =
150.0f / 100 * 12; // 角度 速度 - 角度 P
/** @brief ang_angloop_Ki 常量定义 */
constexpr float ang_angloop_Ki = 0.00f; // 角度 速度 - 角度 I
/** @brief ang_angloop_Kd 常量定义 */
constexpr float ang_angloop_Kd = 8e-04f; // 角度 速度 - 角度 D
/** @brief DEFAULT_angle_loop_p 常量定义 */
constexpr float DEFAULT_angle_loop_p = 60.0f / 100 * 12; // 角度 P
/** @brief DEFAULT_angle_MIT_p 常量定义 */
constexpr float DEFAULT_angle_MIT_p = 100.0f / 100 * 12; // 角度 MIT P
/** @brief angle_MIT_Ki 常量定义 */
constexpr float angle_MIT_Ki = 0.0f;
/** @brief angle_MIT_Kd 常量定义 */
constexpr float angle_MIT_Kd = 8e-04f;
/** @brief DEFAULT_vel_MIT_p 常量定义 */
constexpr float DEFAULT_vel_MIT_p = 5.0f / 100; // 速度 MIT P
/** @brief vel_MIT_Ki 常量定义 */
constexpr float vel_MIT_Ki = 15.0f;
/** @brief vel_MIT_Kd 常量定义 */
constexpr float vel_MIT_Kd = 1e-04f;
/** @brief DEFAULT_current_MIT_p 常量定义 */
constexpr float DEFAULT_current_MIT_p = 0.2f / 100 * 12; // 电流 MIT P
/** @brief current_MIT_Ki 常量定义 */
constexpr float current_MIT_Ki = 0.1f;
/** @brief current_MIT_Kd 常量定义 */
constexpr float current_MIT_Kd = 3e-4f;
// 常量定义
/** @brief SIZE_FLOAT 常量定义 */
constexpr u8 SIZE_FLOAT = sizeof(float);
/** @brief SIZE_U32 常量定义 */
constexpr u8 SIZE_U32 = sizeof(uint32_t);
/** @brief ADC_MAX 常量定义 */
constexpr float ADC_MAX = 65536.0f;
/** @brief PI 常量定义 */
constexpr float PI = 3.14159265358979323846f;
/** @brief INV_PI_3 常量定义 */
constexpr float INV_PI_3 = 3.0f / PI;
/** @brief PI_3 常量定义 */
constexpr float PI_3 = PI / 3.0f;
/** @brief _1_SQRT3 常量定义 */
constexpr float _1_SQRT3 = 0.57735026919f; // 1/sqrt(3)
/** @brief _3PI_2 常量定义 */
constexpr float _3PI_2 = 4.71238898038f;
/** @brief SQRT3 常量定义 */
constexpr float SQRT3 = 1.7320508075688772f;
/** @brief SQRT3DV2 常量定义 */
constexpr float SQRT3DV2 = 0.8660254037844386f; // sqrt(3)/2
/** @brief COS_PI_3 常量定义 */
constexpr float COS_PI_3 = 0.5f;
/** @brief ANGLE_FACTOR 常量定义 */
constexpr float ANGLE_FACTOR = 25.0f / 180.0f * PI;
/** @brief DEG2RAD 常量定义 */
constexpr float DEG2RAD = 1.0f / 180.0f * PI;
/** @brief RAD2DEG 常量定义 */
constexpr float RAD2DEG = 180.0f / PI; // 修正 RAD2DEG 定义,应该是 180/π
/** @brief DEFAULT_VALUE 常量定义 */
constexpr u8 DEFAULT_VALUE = 0;
/** @brief CONFIG_SAVED_NUM 常量定义 */
constexpr u8 CONFIG_SAVED_NUM = 6;
// 三角函数快速计算 LUT
/** @brief LUT_SIZE 常量定义 */
constexpr u16 LUT_SIZE = 1024;
/** @brief LUT_MASK 常量定义 */
constexpr u16 LUT_MASK = (LUT_SIZE - 1);
/** @brief LUT_SCALE 常量定义 */
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
/** @brief DEV_CAN0_ID 常量定义 */
constexpr u16 DEV_CAN0_ID = 0x0000;
/** @brief DEV_CAN0_MASK 常量定义 */
constexpr u16 DEV_CAN0_MASK = 0x0000;
/** @brief DEV_CAN_LOOPBACK_ID 常量定义 */
constexpr u16 DEV_CAN_LOOPBACK_ID = 0x0000;
// 反馈基础 ID
/** @brief CANFD_STATUS_ID 常量定义 */
constexpr u16 CANFD_STATUS_ID = 0x0100; // 状态反馈基础 ID
/** @brief CANFD_CONFIG_ID 常量定义 */
constexpr u16 CANFD_CONFIG_ID = 0x0101; // 配置信息基础 ID
/** @brief CANFD_VERSION_ID 常量定义 */
constexpr u16 CANFD_VERSION_ID = 0x00FF; // 版本信息基础 ID
// 命令基础 ID
/** @brief CANFD_CMD_ID 常量定义 */
constexpr u16 CANFD_CMD_ID = 0x0200; // 控制命令基础 ID
/** @brief CANFD_RESET_ID 常量定义 */
constexpr u16 CANFD_RESET_ID = 0x0301; // 重置命令基础 ID
// 节点 ID 管理基础 ID
/** @brief CANFD_SET_NODE_ID 常量定义 */
constexpr u16 CANFD_SET_NODE_ID = 0x0400; // 设置节点 ID 基础 ID
/** @brief CANFD_GET_NODE_ID 常量定义 */
constexpr u16 CANFD_GET_NODE_ID = 0x0401; // 查询节点 ID 基础 ID
// OTA 相关基础 ID 不受到node ID影响
/** @brief CANFD_BOOT_ID 常量定义 */
constexpr u16 CANFD_BOOT_ID = 0x0000; // BOOT 模式基础 ID (进入 BOOT 使用此 ID)
/** @brief CANFD_OTA_REQ_ID 常量定义 */
constexpr u16 CANFD_OTA_REQ_ID = 0x0100; // OTA 请求基础 ID
/** @brief CANFD_OTA_DATA_ID 常量定义 */
constexpr u16 CANFD_OTA_DATA_ID = 0x0101; // OTA 数据基础 ID
/** @brief CANFD_OTA_ACK_ID 常量定义 */
constexpr u16 CANFD_OTA_ACK_ID = 0x0102; // OTA 确认基础 ID
/** @brief CANFD_OTA_WAIT_ID 常量定义 */
constexpr u16 CANFD_OTA_WAIT_ID = 0x0103; // OTA 等待基础 ID
// ADC
/** @brief ADC_FILTER_SIZE 常量定义 */
constexpr u8 ADC_FILTER_SIZE = 6;
/** @brief BLOCK_SIZE 常量定义 */
constexpr u8 BLOCK_SIZE = ADC_FILTER_SIZE * SIZE_FLOAT;
/** @brief ERROR_ADC_VAL 常量定义 */
constexpr float ERROR_ADC_VAL = 255.0f;
/** @brief MAX_TEMP 常量定义 */
constexpr float MAX_TEMP = 150.0f;
/** @brief MIN_TEMP 常量定义 */
constexpr float MIN_TEMP = -40.0f;
/** @brief MAX_CURRENT 常量定义 */
constexpr float MAX_CURRENT = 40.0f;
/** @brief MIN_CURRENT 常量定义 */
constexpr float MIN_CURRENT = -40.0f;
/** @brief MAX_VCC 常量定义 */
constexpr float MAX_VCC = 65.0f;
/** @brief MIN_VCC 常量定义 */
constexpr float MIN_VCC = -0.1f;
// ang_sensor
/** @brief VEL_FILTER_SIZE 常量定义 */
constexpr u16 VEL_FILTER_SIZE = 4;
/** @brief DEFAULT_COMMAND 常量定义 */
constexpr u16 DEFAULT_COMMAND = 0x0000;
/** @brief FAULT_RECEIVE 常量定义 */
constexpr u16 FAULT_RECEIVE = 0xFFFF;
/** @brief ANGLE_WRAP_THRESHOLD 常量定义 */
constexpr float ANGLE_WRAP_THRESHOLD = 300.0f;
/** @brief ANGLE_WRAP_VALUE 常量定义 */
constexpr float ANGLE_WRAP_VALUE = 360.0f;
/** @brief RAD_FACTOR 常量定义 */
constexpr float RAD_FACTOR = 1.0f / ADC_MAX * 2.0f * PI;
/** @brief ANGLESENS_FACTOR 常量定义 */
constexpr float ANGLESENS_FACTOR = 1.0f / ADC_MAX * 360.0f;
// TIMER2-任务调度
/** @brief TIMER2_PRESCALER 常量定义 */
constexpr int TIMER2_PRESCALER = 119; // 120MHz / 120 = 1MHz
/** @brief TIMER2_PERIOD 常量定义 */
constexpr int TIMER2_PERIOD = 49; // 1 / 1MHz * 50 = 50us
/** @brief MOTOR_PERIOD 常量定义 */
constexpr int MOTOR_PERIOD = 1; // 控制每50us执行一次,无分频
/** @brief TIME_SLICE 常量定义 */
constexpr float TIME_SLICE =
(TIMER2_PERIOD + 1) * (TIMER2_PRESCALER + 1) / 120000000.0f; // 周期
/** @brief TIMER2_FREQUENCY 常量定义 */
constexpr float TIMER2_FREQUENCY = 1 / TIME_SLICE; // 频率
/** @brief MOTOR_FREQUENCY 常量定义 */
constexpr float MOTOR_FREQUENCY = 1 / TIME_SLICE / MOTOR_PERIOD; // 频率
// TIMER1-PWM
/** @brief TIMER1_PRESCALER 常量定义 */
constexpr int TIMER1_PRESCALER = 0; // 不分频,计数器时钟为 120MHz
/** @brief TIMER1_PERIOD 常量定义 */
constexpr int TIMER1_PERIOD = 2999; // 120MHz / (0+1) / (2999+1) = 40kHz计数, 中心对齐实际PWM 20kHz
/** @brief TIMER1_DEFAULT_PULSE 常量定义 */
constexpr int TIMER1_DEFAULT_PULSE = 0;
/** @brief TIMER1_CH3_COMPARE_VALUE 常量定义 */
constexpr int TIMER1_CH3_COMPARE_VALUE =
(TIMER1_PERIOD + 1) * 0.41 - 1; // PWM 周期 41% 位置触发ADC采样
// 齿槽转矩补偿
/** @brief COGGING_TABLE_SIZE 常量定义 */
constexpr u16 COGGING_TABLE_SIZE = 128; // 每电周期采样点数
/** @brief COGGING_COMP_SCALE 常量定义 */
constexpr float COGGING_COMP_SCALE = 0.005f; // 补偿换算系数 V/mA,40mA × 0.005 = 0.2V
// 电压检测参数(基于跌幅检测,不依赖固定额定电压)
@@ -409,13 +250,9 @@ constexpr float COGGING_COMP_SCALE = 0.005f; // 补偿换算系数 V/mA,40mA
// 慢速管理层(power_manage,~10ms周期):
// - 执行Flash保存
// - 状态恢复判断(电压恢复到基准电压的90%以上)
/** @brief VBUS_DROP_THRESHOLD 常量定义 */
constexpr float VBUS_DROP_THRESHOLD = 5.0f; // 电压跌幅阈值 (V)
/** @brief VBUS_ABSOLUTE_MIN 常量定义 */
constexpr float VBUS_ABSOLUTE_MIN = 12.0f; // 绝对最低电压 (V)
/** @brief VBUS_DEBOUNCE_COUNT 常量定义 */
constexpr u32 VBUS_DEBOUNCE_COUNT = 5; // 快速检测:防抖计数 (5次×100us = 500us)
/** @brief VBUS_RECOVERY_RATIO 常量定义 */
constexpr float VBUS_RECOVERY_RATIO = 0.9f; // 恢复比例(基准电压的90%)
+2 -1
View File
@@ -16,6 +16,7 @@ extern sampler sampler;
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC;
extern FlashEEPROM flashEEP;
// 全局热管理控制器单例:由 main 任务周期调用 update(),用于维护过热保护状态。
ThermalController thermalController; // 热量控制器初始化
/** @brief max 宏定义 */
@@ -24,7 +25,7 @@ ThermalController thermalController; // 热量控制器初始化
/** @brief PROTECT_TIME 常量定义 */
constexpr u16 PROTECT_TIME = 6000; // 过热保护时间,单位*10ms
// 电压管理状态机
// 电压管理状态机:仅 power_manage() 慢速任务访问,文件内封装。
static VoltageState voltage_state = VoltageState::NORMAL;
static float last_vbus_slow = 0.0f; // 上次电压值(慢速任务用)
/**
+37 -2
View File
@@ -36,6 +36,7 @@
#endif
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
// 全局版本管理器单例:构造函数只复制 constexpr 版本号常量,不依赖外设初始化。
VersionManager Version;
VersionManager::VersionManager() {
@@ -120,14 +121,48 @@ 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;
}
+7
View File
@@ -50,6 +50,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#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; // 系统运行时间(毫秒)
@@ -253,6 +257,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;
@@ -271,6 +276,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; // 重新开始写入
@@ -289,6 +295,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;
+172
View File
@@ -0,0 +1,172 @@
/**
* @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 "systick.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;
}
/**
* @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();
}
if (FOC.control_mode >= 8) return;
// 模式合法时再派发控制函数,防止数组越界。
reset_control_loops_on_mode_change();
if (!FOC.is_shut_down) {
(FOC.*control_functions[FOC.control_mode])();
} else {
FOC.drv_stop();
}
}
/**
* @brief 执行高频采样和电机控制相关任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
static void run_fast_sample_tasks(u32 currentTasks) {
if (has_task(currentTasks, TASK_CURRENT_UPDATE)) {
sampler.update_current();
}
if (has_task(currentTasks, TASK_ANGLE_UPDATE)) {
KTH7823.update();
FOC.rt_angle = Angle_kalman.update(KTH7823.getAngle());
}
if (has_task(currentTasks, TASK_VELOCITY_UPDATE)) {
FOC.rt_velocity = Angle_kalman.getVelocity();
}
if (has_task(currentTasks, 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)) {
sampler.update_mcu_temp();
}
if (has_task(currentTasks, TASK_DRV_TEMP_UPDATE)) {
sampler.update_drv_temp();
}
if (has_task(currentTasks, TASK_THERMAL_CONTROL)) {
thermalController.update();
}
if (has_task(currentTasks, TASK_MAGNETIC_CHECK)) {
angsens_MG_check();
}
if (has_task(currentTasks, TASK_VBUS_UPDATE)) {
power_manage();
}
}
/**
* @brief 执行 CAN 周期发送任务并喂狗
* @return void
*/
static void run_can_task(void) {
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();
}
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
void do_task(u32 currentTasks) {
busy_task_enter(); // DWT: 记录忙碌起始周期
cnt_test++;
// 高频任务先执行,保持电机控制优先级。
run_fast_sample_tasks(currentTasks);
// 齿槽转矩校准 tick(DEBUG,平时不启用)
#if FOC_DEBUG
FOC.calibrate_cogging_tick();
#endif
run_housekeeping_tasks(currentTasks);
// CAN 发送放在最后,避免通信处理影响前面的实时控制任务。
if (has_task(currentTasks, TASK_CAN_TRANSMIT)) {
run_can_task();
}
busy_task_exit(); // DWT: 累加忙碌周期
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef TASKS_H
#define TASKS_H
#include "initial.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
void do_task(u32 currentTasks);
#ifdef __cplusplus
}
#endif
#endif /* TASKS_H */
+162
View File
@@ -0,0 +1,162 @@
# 静态检查告警审查说明
本文件用于记录人工确认后的静态检查告警处理结论。原则:
- 不为消除低风险 warning 引入会改变实时行为的代码。
- 裸机系统级单例、编译期常量、ISR/主循环共享状态分别分类说明。
- 对涉及实时路径的并发项,优先记录访问边界和偏离理由;只有经过实机验证后再做逻辑修复。
## SUP-GLB-001:文件作用域对象
### 编译期常量
以下 `constexpr` 对象为编译期常量,非可变全局状态,无运行期写入、并发访问或动态初始化顺序风险:
- `device/ADC.cpp`: `ADC_SCALE`, `TEMP_FACTOR`, `VCC_FACTOR`, `CURRENT_FACTOR`
- `device/Can.cpp`: `CANX`
- `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 环境验证。
@@ -0,0 +1,52 @@
# A solution is a collection of related projects that share same base configuration.
solution:
created-for: CMSIS-Toolbox@2.12.0
cdefault: # use CMSIS-Toolbox default Compiler options
# Compilers that can be selected
select-compiler:
- compiler: AC6
- compiler: GCC
- compiler: IAR
- compiler: CLANG
# List of miscellaneous tool-specific controls
misc:
- for-compiler: AC6 # GDB requires DWARF 5, remove when using uVision Debugger
C-CPP:
- -gdwarf-5
ASM:
- -gdwarf-5
# List the packs that define the device and/or board.
packs:
- pack: GigaDevice::GD32C10x_DFP
- pack: ARM::CMSIS
# List different hardware targets that are used to deploy the solution.
target-types:
- type: GD32C103CB
device: GD32C103CB
target-set:
- set:
images:
- project-context: Robot_V2.Release
debugger:
name: ST-Link@pyOCD
clock: 10000000
protocol: swd
# List of different build configurations.
build-types:
- type: Debug
debug: on
optimize: none
- type: Release
debug: off
optimize: balanced
# List related projects.
projects:
- project: Robot_V2/Robot_V2.cproject.yml
compiler: AC6
@@ -0,0 +1,182 @@
/**
* @file sys/initial.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _INITIAL_H
#define _INITIAL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "config.h"
#include "gd32c10x_eval.h"
#include <cstdint>
#include <cstdio>
// 任务标志定义
/**
* @brief 匿名枚举 枚举定义
*/
typedef enum {
TASK_MOTOR_CONTROL = 0,
TASK_CURRENT_UPDATE,
TASK_VELOCITY_UPDATE,
TASK_ANGLE_UPDATE,
TASK_MCU_TEMP_UPDATE,
TASK_DRV_TEMP_UPDATE,
TASK_VBUS_UPDATE,
TASK_THERMAL_CONTROL,
TASK_CAN_TRANSMIT,
TASK_MAGNETIC_CHECK,
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);
/**
* @brief 获取复位原因
* @return u16 返回值
*/
u16 get_reset_cause(void);
/**
* @brief 初始化未使用引脚状态
* @return void
*/
void unused_pin_init();
/**
* @brief 初始化 OTA 与 A/B 分区状态
* @return void
*/
void OTA_initial(void);
// 任务管理结构
typedef struct {
volatile u32 taskFlags; // 任务标志位(每个位代表一个任务)
} TaskScheduler;
// 版本管理类
/**
* @brief VersionManager 类或结构体
*/
class VersionManager {
public:
// 版本信息结构体
typedef struct {
u16 major;
u16 minor;
u16 patch;
u16 build;
} VersionInfo;
// 构造函数
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:
VersionInfo softwareVersion;
VersionInfo hardwareVersion;
VersionInfo bootVersion;
};
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,252 @@
/**
* @file main.cpp
* @brief 应用程序主流程源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "main.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
#include "Timer.h"
#include "adc.h"
#include "ang_sensor.h"
#include "flash.h"
#include "health.h"
#include "initial.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
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
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};
// 电机 50us 控制周期计数:主循环电机任务递增,观测器/计时逻辑读取。
u32 motor_runtime_50us = 0;
// 负载检测/通信心跳计数:主循环递增,CAN 周期发送任务清零。
u32 cnt_test = 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 判断任务标志中是否包含指定任务
* @param[in] currentTasks 当前任务位图快照
* @param[in] task 待检查任务枚举
* @return bool true 表示该任务需要执行
*/
static bool has_task(u32 currentTasks, SystemTask task) {
return (currentTasks & (1u << task)) != 0U;
}
/**
* @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();
}
if (FOC.control_mode >= 8) return;
// 模式合法时再派发控制函数,防止数组越界。
reset_control_loops_on_mode_change();
if (!FOC.is_shut_down) {
(FOC.*control_functions[FOC.control_mode])();
} else {
FOC.drv_stop();
}
}
/**
* @brief 执行高频采样和电机控制相关任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
static void run_fast_sample_tasks(u32 currentTasks) {
if (has_task(currentTasks, TASK_CURRENT_UPDATE)) {
sampler.update_current();
}
if (has_task(currentTasks, TASK_ANGLE_UPDATE)) {
KTH7823.update();
FOC.rt_angle = Angle_kalman.update(KTH7823.getAngle());
}
if (has_task(currentTasks, TASK_VELOCITY_UPDATE)) {
FOC.rt_velocity = Angle_kalman.getVelocity();
}
if (has_task(currentTasks, 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)) {
sampler.update_mcu_temp();
}
if (has_task(currentTasks, TASK_DRV_TEMP_UPDATE)) {
sampler.update_drv_temp();
}
if (has_task(currentTasks, TASK_THERMAL_CONTROL)) {
thermalController.update();
}
if (has_task(currentTasks, TASK_MAGNETIC_CHECK)) {
angsens_MG_check();
}
if (has_task(currentTasks, TASK_VBUS_UPDATE)) {
power_manage();
}
}
/**
* @brief 执行 CAN 周期发送任务并喂狗
* @return void
*/
static void run_can_task(void) {
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();
}
/**
* @brief 应用程序入口函数
* @return int 返回值
*/
int main(void) {
u8 init_status = hardware_init();
while (init_status) { // 初始化检测
MAIN_DEBUG_PRINT("初始化失败\r\n");
}
OTA_initial(); // OTA 初始化,A/B 分区处理
angsens_MG_check(); // 角度传感器磁感应强度测试
watchdog_init(50); // 看门狗初始化
while (1) {
__disable_irq();
u32 currentTasks =
scheduler.taskFlags; // 保存当前任务标志快照以提高效率并确保一致性
scheduler.taskFlags &= ~currentTasks; // 一次性清除所有已处理的任务标志
__enable_irq();
if (currentTasks == 0U) {
idle_task_count();
continue;
}
do_task(currentTasks);
}
return 0;
}
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
*/
u8 hardware_init(void) {
__enable_irq();
systick_config(); // 系统时钟初始化
unused_pin_init(); // 未使用引脚初始化
USART0_init(); // 调试串口初始化
User_timer_init(); // 任务 Timer 初始化
u8 init_status = 0;
init_status += KTH7823.init(); // 角度传感器初始化
init_status += sampler.init(); // 采样器初始化
// EEPROM 初始化 (必须在 CAN 初始化之前,因为 CAN 会从 Flash 读取节点 ID)
flashEEP.init(); // EEPROM 初始化
// CAN 初始化 (会先设置默认节点 ID,然后从 Flash 加载已保存的节点 ID)
init_status += CAN0_transmiter.init(); // CAN 初始化
init_status += Angle_kalman.init(KTH7823.getAngle()); // 角度滤波初始化
init_status += Vel_kalman.init(0.0f); // 速度滤波初始化
init_status += FOC.init(); // FOC 初始化
return init_status;
}
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 参数
* @return void
*/
void do_task(u32 currentTasks) {
busy_task_enter(); // DWT: 记录忙碌起始周期
cnt_test++;
// 高频任务先执行,保持电机控制优先级。
run_fast_sample_tasks(currentTasks);
// 齿槽转矩校准 tick(DEBUG,平时不启用)
#if FOC_DEBUG
FOC.calibrate_cogging_tick();
#endif
run_housekeeping_tasks(currentTasks);
// CAN 发送放在最后,避免通信处理影响前面的实时控制任务。
if (has_task(currentTasks, TASK_CAN_TRANSMIT)) {
run_can_task();
}
busy_task_exit(); // DWT: 累加忙碌周期
}
@@ -0,0 +1,52 @@
/*!
\file main.h
\brief the header file of main
\version 2025-06-20, V1.5.1, firmware for GD32C10x
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MAIN_H
#define MAIN_H
#include <cstdint>
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 参数
* @return void
*/
void do_task(u32 currentTasks);
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
*/
u8 hardware_init(void);
#endif /* MAIN_H */
@@ -2,9 +2,261 @@
"version": 1,
"storageKey": "robot_v2_app_static_report_checklist_v1",
"report": "ROBOT_V2_APP_static_test_automotive_business_20260523",
"updatedAt": "2026-05-23T14:41:51.724Z",
"updatedAt": "2026-05-23T16:25:00.825Z",
"checked": {
"main-3117cb79205a": true,
"main-527272d22f5f": true
"main-527272d22f5f": true,
"main-5ceb0ceaf414": true,
"main-0d853312a4fb": true,
"main-d5c0067cb8c9": true,
"main-b3497022fd57": true,
"main-fb0f6eef33ee": true,
"main-e8d34916ef43": true,
"main-a0e7f44822d5": true,
"main-e8581b775b86": true,
"main-a61db2bcd87e": true,
"main-1bfba6db3552": true,
"main-29a13abcf59d": true,
"main-cc75af885325": true,
"main-2665cbd3f00e": true,
"main-843a028f8696": true,
"main-4117283cf3e7": true,
"main-344a33bfcf2b": true,
"main-efc8f847b950": true,
"main-92473d3ec470": true,
"main-dca22002f30a": true,
"main-668e5afb0750": true,
"main-778a368d79d3": true,
"main-c8ae670ed482": true,
"main-44e5b708ced1": true,
"main-eecf1f323336": true,
"main-cd72d3b37996": true,
"supp-091ea0cb4563": true,
"supp-b251edcd19a4": true,
"supp-72780618de89": true,
"supp-428c4d2d4361": true,
"supp-5bc6c7028f31": true,
"supp-5f30c71b560c": true,
"supp-ec236e967cf5": true,
"supp-6c83765299a8": true,
"supp-a3f3ec52d5c9": true,
"supp-dba1f5ee2bac": true,
"supp-a00f50fd7e91": true,
"supp-cdd0a4a9b8e7": true,
"supp-fc1eb40a78e5": true,
"supp-7b0c20306d7f": true,
"supp-955e70ca702a": true,
"supp-26a75340d328": true,
"supp-b684bf6daa82": true,
"supp-cfb32941b86c": true,
"supp-4db7e8721b38": true,
"supp-18273d7ff405": true,
"supp-a0b262ee0d7f": true,
"supp-c8fcf2049028": true,
"supp-e82e3e95d9c3": true,
"supp-9631705a92be": true,
"supp-0ba6f0a3cbb6": true,
"supp-bfd02b0e6428": true,
"supp-d6a6beb85f8e": true,
"supp-54f7152d525f": true,
"supp-57ed4b5aeb01": true,
"supp-97688039a586": true,
"supp-5f838841063f": true,
"supp-a2c7b377ff4e": true,
"supp-296377880c63": true,
"supp-b038358270db": true,
"supp-6c02c2686174": true,
"supp-038975fb0589": true,
"supp-ddcf3c5981d6": true,
"supp-1f9a0ad0330e": true,
"supp-eb8c172d8fa7": true,
"supp-415c4b510e1b": true,
"supp-5f5afb443061": true,
"supp-52e7dfa0bd8a": true,
"supp-7eca8869fe8d": true,
"supp-cd1b03277370": true,
"supp-9e0e93117b39": true,
"supp-476cb8529804": true,
"supp-9d2d2a633571": true,
"supp-c843a8b60fa0": true,
"supp-a372ad7cc25f": true,
"supp-d47f02ccdc6f": true,
"supp-02752fc39463": true,
"supp-fba82de9f335": true,
"supp-b464d624b89c": true,
"supp-177d79286976": true,
"supp-4b1489b395b5": true,
"supp-fd0c805a01cb": true,
"supp-4f9e1ff5f977": true,
"supp-5bf3d6123aea": true,
"supp-378c2eadddd6": true,
"supp-20a5d232f926": true,
"supp-f3b55e35f9d0": true,
"supp-1acaf41cfdae": true,
"supp-fbc927cfd61d": true,
"supp-b187de9b2c71": true,
"supp-d7851021e615": true,
"supp-7265bf9ffeb6": true,
"supp-262745c8fde4": true,
"supp-fb700e5b9217": true,
"supp-09cf228fa618": true,
"supp-8f1a0be480db": true,
"supp-2ec9227ef7dd": true,
"supp-674252936cac": true,
"supp-7c9b992c78be": true,
"supp-f938632efd00": true,
"supp-ec2f21ab7179": true,
"supp-b59a8a08dd46": true,
"supp-a954c6db3be8": true,
"supp-191496d69aae": true,
"supp-334250f82da0": true,
"supp-cb34e84dcbdf": true,
"supp-624129e18b9e": true,
"supp-58df8dc7015c": true,
"supp-442b031030c0": true,
"supp-4182de41f8da": true,
"supp-4becb5ead2c4": true,
"supp-2eaa8c7f0941": true,
"supp-ff8f09c6bfa5": true,
"supp-63c6dddf8bf5": true,
"supp-9d4b1e19e4f1": true,
"supp-b163701fc702": true,
"supp-6495be6cf728": true,
"supp-007928464d9b": true,
"supp-df5a0f8d5a72": true,
"supp-3dbd2f0de758": true,
"supp-9946aa57c8a9": true,
"supp-07a2866f170a": true,
"supp-2c87813f8aa3": true,
"supp-aa5f59116193": true,
"supp-1fbd51d49dad": true,
"supp-e0380903f6c3": true,
"supp-479efab46368": true,
"supp-09cce3719ad2": true,
"supp-d88f31fa5c0e": true,
"supp-4cf2fa02952e": true,
"supp-a3df150b3937": true,
"supp-fd66a295e97d": true,
"supp-473f9bd57824": true,
"supp-4a07cfb831d2": true,
"supp-85f55cfb9bd2": true,
"supp-816cb5806281": true,
"supp-e6f6ac38039c": true,
"supp-41033b3cde59": true,
"supp-a6c89579f6e4": true,
"supp-9dd3d0dbc734": true,
"supp-1f027dfb746a": true,
"supp-da022ec3d833": true,
"supp-6f6ffee00f58": true,
"supp-89f2f99e8e8d": true,
"supp-b07105c03caa": true,
"supp-a374b2a62cc3": true,
"supp-2687440608b0": true,
"supp-19455bafa3ac": true,
"supp-6e12b0ca84d2": true,
"supp-05a59d0def43": true,
"supp-e7dccd9a4003": true,
"supp-5df96dbf6738": true,
"supp-8148e3027849": true,
"supp-c5e6fb0e92b3": true,
"supp-f21a6ce0ca0e": true,
"supp-d3c74b81ee6f": true,
"supp-206d431ac433": true,
"supp-8f19a6ce3b2b": true,
"supp-dc7c842eaf8a": true,
"supp-b646b277d882": true,
"supp-8035701aea54": true,
"supp-8c8575a287e0": true,
"supp-9345c6e7b49b": true,
"supp-2871d296449d": true,
"supp-d86607ccbc20": true,
"supp-eb4e342e74d7": true,
"supp-23f2e81c8f43": true,
"supp-1a4ed985430d": true,
"supp-cef1f0c10332": true,
"supp-cfa7b96290df": true,
"supp-8066dac3a4e4": true,
"supp-045b8947a919": true,
"supp-39a61666efaf": true,
"supp-da4349769920": true,
"supp-df53690c09aa": true,
"supp-a977c6fa3fc0": true,
"supp-90bbab9c52ba": true,
"supp-7e9875638f06": true,
"supp-2cb25842f03e": true,
"supp-5f1962ce4046": true,
"main-3d52cdf723db": true,
"main-a22197731662": true,
"main-0c3a88910d56": true,
"main-687056efb188": true,
"main-2787e912f9ed": true,
"main-f53db085a93d": true,
"main-552e5d7b3055": true,
"main-4e82e66f670a": true,
"main-ce883b19cd52": true,
"main-5240bf640bb6": true,
"main-61c463500c61": true,
"main-977f3df23a03": true,
"main-a82cf38aaae0": true,
"metric-48d7e9621754": true,
"metric-65df715e5faa": true,
"metric-e7f74eabf84b": true,
"metric-f6c2bfb36b40": true,
"metric-0dfc09c00e94": true,
"metric-98b568f0e6b4": true,
"metric-ac925d0fa661": true,
"supp-be0750f37b75": true,
"supp-babc355f3ba4": true,
"supp-07a2fcbd63fb": true,
"supp-c48ffa36def4": true,
"supp-7464d86d386a": true,
"supp-c2c374b8a410": true,
"supp-f303809c0871": true,
"supp-897f14107d25": true,
"supp-bc539f456a7e": true,
"supp-6466d05c08ad": true,
"supp-e14bc6bcacd6": true,
"supp-73804b17c385": true,
"supp-52ca6be1c9f5": true,
"supp-b0ab1b3cde0e": true,
"supp-e73532e2d89c": true,
"supp-220ae6c1673f": true,
"supp-c0e88a884841": true,
"supp-191ba764ae89": true,
"supp-74fddf697b8f": true,
"supp-c3fac9483ce8": true,
"supp-e92b16152a23": true,
"supp-c08e7fa1d6c4": true,
"supp-5c8269165c25": true,
"supp-212345faf28b": true,
"supp-9838229d4cbb": true,
"supp-3038da272c7c": true,
"supp-0d2a8817da44": true,
"supp-89c98258c38a": true,
"supp-528a6cec1e84": true,
"supp-e242b707d647": true,
"supp-9c6e067632b0": true,
"supp-570cf82a0ea5": true,
"supp-b848b2393298": true,
"supp-2dd905d3d33a": true,
"supp-151515384d9d": true,
"supp-834473d55cbd": true,
"supp-0ed73a1e9822": true,
"supp-1f41a4e9399a": true,
"supp-0d1b7279df03": true,
"supp-3b9068c82c27": true,
"supp-7a89b7e99a15": true,
"supp-0512b0719836": true,
"supp-2bdf5602b64c": true,
"supp-014e603e9a05": true,
"supp-192ee126bacd": true,
"supp-5e8bbfb22dca": true,
"supp-cfd2894ba6c6": true,
"supp-80fbfa1e7f2a": true,
"supp-0a7fb6af2bfa": true,
"supp-79489853ba70": true,
"supp-8f5907226506": true,
"supp-da2839ee2717": true,
"supp-c7cb3cd0801f": true
}
}