Files
cbb 25ed2a2bde feat(adc): 添加三相电流零点配平并扩展初始化错误上报
- ADC: 新增 zero_calibrate() 函数,在上电后采集 200 帧计算各通道零点偏差
- ADC: 修改 current_adc_to_ma() 支持逐通道扣除配平后的零点偏移
- ADC: 调整电流采样引脚为模拟输入模式并优化采样时间至 13.5 周期
- CAN: STATUS 帧字节 32~35 新增 InitStatus 字段,反馈初始化错误位图
- main: 细化初始化错误检测,分别标记角度/ADC/CAN/Kalman/FOC/门驱动故障
- config.h: 新增 INIT_ERR_* 错误码定义及 CURRENT_ZERO_CAL_LIMIT 阈值
2026-09-07 03:34:47 +08:00

344 lines
12 KiB
C++
Raw Permalink 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 "config.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 周期内)
// 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, 0, 0, 0, 0, 0, // 3~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);
#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);
}
/**
* @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 采样点(低边采样:必须落在三相下管同时导通窗,
// 即中心对齐计数的峰顶附近,见 config.h TIMER1_CH3_COMPARE_VALUE 说明)
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 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 {
} // 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) {
#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
}