Files
Motor_v2/Robot_V2/device/Timer.cpp
T
cbb 76f7987238 feat(FOC): 实现电角度零位自动校准并修复ADC采样干扰问题
- 新增FlashEEPROM用于保存零位校准角,掉电不丢失
- 实现安全的零位校准算法:运行中禁止校准、对齐后重新采样、保存到Flash
- 重构ADC架构:分离ADC0(PWM同步电流采样)和ADC1(VBUS/DRV温度轮询)
- 解决ADC采样冲突:移除ADC0的regular连续扫描,避免与注入组抢占资源
- 修复VBUS电压读取错误:从DMA缓冲区改为直接ADC1单次转换
- 修正开环控制Uq幅值限幅逻辑,保留符号避免反转时抖动
- 更新CAN协议:电流数据改为有符号16位打包,支持负电流传输
- 调整版本号至v26.8.16.7
2026-08-16 02:13:37 +08:00

319 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file device/Timer.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "Timer.h"
#include "Can.h"
#include "FOC.h"
#include "Timer.h"
#include "gd32c10x.h"
#include "gd32c10x_gpio.h"
#include "gd32c10x_misc.h"
#include "gd32c10x_rcu.h"
#include "gd32c10x_timer.h"
#include "initial.h"
volatile u32 total_us100_counter = 0; // TIMER2 ISR 内部 100us 调度索引
extern FOC_Controller FOC;
extern TaskScheduler scheduler;
extern CAN_transmiter CAN0_transmiter;
// 添加系统运行时间计数器
extern u32 cnt_test;
// 任务调度查找表 - 每 100us 执行的任务 (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: 空闲
(1u << TASK_CAN_TRANSMIT) // 9: CAN 发送
};
// 任务调度查找表 - 每 1ms 执行的任务 (10ms 周期内)
static const u32 task_schedule_1ms[10] = {
(1u << TASK_THERMAL_CONTROL), // 0: 热量控制
(1u << TASK_MAGNETIC_CHECK), // 1: 磁感应强度检测
0, 0, 0, 0, 0, 0, 0, 0 // 空闲
};
// 任务调度查找表 - 每 10ms 执行的任务 (100ms 周期内)
static const u32 task_schedule_10ms[10] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 当前无任务,预留扩展
};
// 任务调度查找表 - 每 100ms 执行的任务 (1s 周期内)
static const u32 task_schedule_100ms[10] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 当前无任务,预留扩展
};
/**
* @brief 设置引脚中断优先级
* @return void
*
* 设置引脚中断优先级
*/
void nvic_config(void) {
/* 设置 NVIC 优先级分组,1 位抢占优先级,3 位响应优先级 */
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
/* 使能 TIMER2 中断,设置抢占优先级为 1,子优先级为 1 */
nvic_irq_enable(TIMER2_IRQn, 0, 1);
nvic_irq_enable(ADC0_1_IRQn, 0, 0);
nvic_irq_enable(TIMER1_IRQn, 0, 2);
}
/**
* @brief 配置定时器 2-任务管理
* @return void
*
* 配置定时器 2,用于定时执行 FOC 控制、采样、CAN 发送等任务
*/
void TIMER2_config(void) {
/* ----------------------------------------------------------------------------
TIMER2 Configuration:
TIMER2CLK = SystemCoreClock/120 = 1MHz, the period is 100us(100/1000000 =
100us).
----------------------------------------------------------------------------
*/
timer_parameter_struct timer_struct;
/* 使能 TIMER2 时钟 */
rcu_periph_clock_enable(RCU_TIMER2);
/* 复位 TIMER2 外设到默认状态 */
timer_deinit(TIMER2);
/* 初始化 TIMER 初始化参数结构体 */
timer_struct_para_init(&timer_struct);
/* TIMER2 配置参数设置 */
timer_struct.prescaler =
TIMER2_PRESCALER; /* 预分频值:120 分频 (从 0 开始计数) */
timer_struct.alignedmode = TIMER_COUNTER_EDGE; /* 边沿对齐模式 */
timer_struct.counterdirection = TIMER_COUNTER_UP; /* 向上计数模式 */
timer_struct.period =
TIMER2_PERIOD; /* 自动重装载值:100 次计数 (从 0 开始计数) */
timer_struct.clockdivision = TIMER_CKDIV_DIV1; /* 时钟分频值:1 分频 */
timer_init(TIMER2, &timer_struct);
/* 清除更新中断标志位 */
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
/* 使能更新中断 */
timer_interrupt_enable(TIMER2, TIMER_INT_UP);
/* 使能 TIMER2 */
timer_enable(TIMER2);
}
/**
* @brief 配置 TIMER2-任务管理中断
* @return void
*/
void User_timer_init(void) {
/* 配置 TIMER2 */
TIMER2_config();
/* 配置 TIMER2 中断 */
nvic_config();
}
/**
* @brief TIMER2-任务管理中断处理函数 (优化版)
* @return void
* @note 使用查找表替代多层 switch-case,减少代码复杂度和执行时间
* 中断周期:50us
* - 每 50us: 电机控制任务 (角度、速度更新)
* - 每 100us: 传感器采样、CAN 通信
* - 每 1ms: 热量控制、磁检
* - 每 10ms/100ms/1s: 预留扩展任务
*/
void TIMER2_IRQHandler(void) {
// 快速路径:检查并清除中断标志
if (timer_interrupt_flag_get(TIMER2, TIMER_INT_UP) == RESET) {
return;
}
timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
// 固定任务:每 50us 执行电机控制
scheduler.taskFlags |= (1u << TASK_ANGLE_UPDATE) |
(1u << TASK_VELOCITY_UPDATE) |
(1u << TASK_MOTOR_CONTROL);
// 每 100us 执行一次的任务(使用位运算替代布尔变量)
static u32 call_counter = 0;
call_counter++;
if ((call_counter & 1u) == 0u) { // 偶数次调用(每 100us)
// 使用查找表设置 1ms 周期内的任务
scheduler.taskFlags |= task_schedule_100us[total_us100_counter];
total_us100_counter++;
if (total_us100_counter >= 10u) {
total_us100_counter = 0;
// 每 1ms 执行的任务
static u32 ms1_counter = 0;
ms1_counter++;
if (ms1_counter >= 10u) {
ms1_counter = 0;
// 每 10ms 执行的任务
static u32 ms10_counter = 0;
ms10_counter++;
if (ms10_counter >= 10u) {
ms10_counter = 0;
// 每 100ms 执行的任务
static u32 ms100_counter = 0;
ms100_counter++;
if (ms100_counter >= 10u) {
ms100_counter = 0;
// 每 1s 任务预留位置
}
scheduler.taskFlags |= task_schedule_100ms[ms100_counter];
}
scheduler.taskFlags |= task_schedule_10ms[ms10_counter];
}
scheduler.taskFlags |= task_schedule_1ms[ms1_counter];
}
}
}
/**
* @brief TIMER1-PWM GPIO 配置函数
*/
void timer1_gpio_config(void) {
// 时钟使能
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_TIMER1);
// TIMER1 GPIO 初始化
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
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);
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);
/* 配置 TIMER1 完全重映射*/
gpio_pin_remap_config(GPIO_TIMER1_FULL_REMAP, DISABLE);
}
/**
* @brief TIMER1-PWM 配置函数
*/
void timer1_pwm_config(void) {
timer_parameter_struct timer_initpara;
timer_oc_parameter_struct timer_ocinitpara;
// 使能 TIMER1 时钟
rcu_periph_clock_enable(RCU_TIMER1);
// 复位 TIMER1
timer_deinit(TIMER1);
// 配置 TIMER1 基本参数
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = TIMER1_PRESCALER; // 预分频器
timer_initpara.alignedmode = TIMER_COUNTER_CENTER_BOTH; // 中心对齐,给采样留对称平台窗
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = TIMER1_PERIOD; // 计数周期
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER1, &timer_initpara);
// 配置通道输出参数
timer_channel_output_struct_para_init(&timer_ocinitpara);
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
// 配置三个通道为 PWM 模式
timer_channel_output_config(TIMER1, TIMER_CH_0, &timer_ocinitpara);
timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocinitpara);
timer_channel_output_config(TIMER1, TIMER_CH_2, &timer_ocinitpara);
// 配置第四个通道 (CH3) 为固定采样点,只用于导出 ADC 触发
timer_ocinitpara.outputstate =
TIMER_CCX_DISABLE; // 不输出 PWM 波形,仅用于比较事件
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_channel_output_config(TIMER1, TIMER_CH_3, &timer_ocinitpara);
// 设置 PWM 模式和占空比
timer_channel_output_mode_config(TIMER1, TIMER_CH_0, TIMER_OC_MODE_PWM0);
timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM0);
timer_channel_output_mode_config(TIMER1, TIMER_CH_2, TIMER_OC_MODE_PWM0);
timer_channel_output_mode_config(TIMER1, TIMER_CH_3,
TIMER_OC_MODE_PWM0); // CH3 也设为 PWM 模式
/* 将 CH3 预装载比较点作为 TRGO 导出,供 ADC injected 组同步采样 */
timer_master_output_trigger_source_select(TIMER1, TIMER_TRI_OUT_SRC_O3CPRE);
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_0,
TIMER1_DEFAULT_PULSE);
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1,
TIMER1_DEFAULT_PULSE);
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_2,
TIMER1_DEFAULT_PULSE);
// 设置 CH3 为固定 ADC 采样点,避免与三相 PWM duty 耦合
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_3,
TIMER1_CH3_COMPARE_VALUE);
// 使能主输出和自动重载
timer_primary_output_config(TIMER1, ENABLE);
timer_auto_reload_shadow_enable(TIMER1);
// 启动 TIMER1
timer_enable(TIMER1);
}
// CH3 现在仅作为 TRGO 采样点保留;采样完成由 ADC injected 中断接管
/**
* @brief TIMER1 中断处理函数
* @return void
*/
void TIMER1_IRQHandler(void) {
if (timer_interrupt_flag_get(TIMER1, TIMER_INT_FLAG_CH3) != RESET) {
timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_CH3);
}
}
/*!
\brief set PWM
\param[in] channel: TIMER_CH_0/TIMER_CH_1/TIMER_CH_2/TIMER_CH_3 (valid
values: 0, 1, 2, 3)
\param[in] pulse: PWM pulse value
\param[out] none
\retval none
\note The channel parameter must be between 0 and 3 inclusive.
*/
void Set_Pwm(u16 channel, float duty_cycle) {
float pulse = duty_cycle * TIMER1_PERIOD;
if (channel <= 3) {
timer_channel_output_pulse_value_config(TIMER1, channel, pulse);
} else {
} // Ensure channel is within valid range
}
/*!
\brief set PWM
\param[in] channel: TIMER_CH_0/TIMER_CH_1/TIMER_CH_2/TIMER_CH_3 (valid
values: 0, 1, 2, 3)
\param[in] pulse: PWM pulse value
\param[out] none
\retval none
\note The channel parameter must be between 0 and 3 inclusive.
*/
void Set_3Pwm(float duty_cycle0, float duty_cycle1, float duty_cycle2) {
// 直接写寄存器
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);
}