/******************************************************************** * 文件名: bsp_charge.c * 描述: 板级支持包 - 充电管理模块 * 包含FSBB(四开关Buck-Boost)变换器的控制实现 ********************************************************************/ #include "gd32f30x.h" #include "systick.h" #include "bsp_charge.h" #include "gd32f30x_rtc.h" extern volatile PowerStatus_t g_powerStatus; /******************************************************************** * 板级定义: BOARD SPMB V2.0 ********************************************************************/ /* ADC采样结果数组,存储7个通道的转换值 */ uint16_t adc_result[7] = {0}; /** @brief 系统节拍计数器(来自RTC) */ volatile uint32_t g_system_tick = 0; /* 软启动变量 - 集中管理所有模式 */ static bool soft_start_active = true; /**< 软启动激活标志 */ static uint32_t soft_start_counter = 0; /**< 软启动计数器 */ static bool prev_battery_connected = false; /**< 上次电池连接状态 */ /** @brief 电流环控制周期 (s), TIMER7 ISR 中 curr 分频 = 400kHz/400 = 1kHz */ #define CONTROL_PERIOD_S 0.001f #define SOFT_START_TIME_MS 5000 /**< 软启动时间 1秒 */ /** * @brief 软启动的总步数(分步数) * @details 计算逻辑:总时间 / 单步时间 * = SOFT_START_TIME_MS (ms) / (CONTROL_PERIOD_S * 1000) (ms) * = 1000 / (0.001 * 1000) * = 1000 / 1 * = 1000 步 * 最终:每步增加 target_iout / SOFT_START_STEPS,1000步后达到target */ #define SOFT_START_STEPS (SOFT_START_TIME_MS / (CONTROL_PERIOD_S * 1000.0f)) /* 系统状态结构体 */ /* 声明时初始化(初始状态) */ FsbbStatus_t fsbb_status = { .convert_mode = IDLE_CONVERT_MODE, /* 初始空闲模式 */ .charge_state = CHARGE_STATE_STANDY, /* 初始待机:等待任务显式设置为START */ .protect_state = PROTECT_NORMAL, /* 正常保护状态 */ .error_code = 0, /* 无错误 */ }; /* =============================== 控制参数 ============================== */ /** * @brief 电流环PI控制器 * @details 用于控制输出电流,输出 energy ∈ [0, out_max] 作为占空比的能量请求系数。 * - energy=1.0 对应前馈平衡点, >1.0 提供额外推力 */ PI_t currentPI = { .kp = 0.015f, .ki = 0.01f, .kd = 0, .integral = 0, .output = 0, .out_min = 0.0f, /* energy 下限 0% */ .out_max = 2.0f, /* energy 上限 200%: 100%前馈 + 100%额外推力 */ .integral_min = 0.0f, .integral_max = 2.0f }; /** * @brief 电压环PI控制器(电压外环) * @details CV模式下根据电池电压误差计算充电电流参考值。 * * 输入: 电压误差 = CHARGE_VOLTAGE_CV - bat (mV级别) * 输出: g_powerStatus.charge_current_ref (mA级别),传给电流内环 * - out_max=CHARGE_CURRENT_CC: CV阶段电流上限 */ PI_t voltagePI = { .kp = 0.5f, /* 比例增益: 500mV误差 → 250mA P输出 */ .ki = 0.002f, /* 积分增益: 500mV@100Hz → 100mA/s,10s到上限 */ .kd = 0, .integral = 0, .output = 0, .out_min = 0, .out_max = CHARGE_CURRENT_CC, /* 上限 = CC电流 (CHARGE_CURRENT_CC) */ .integral_min = 0, .integral_max = CHARGE_CURRENT_CC }; extern void delay_us(unsigned int i); /* 微秒级延时函数,外部定义 */ /*! \brief BspCharge_gpio_config 函数 - 充电相关GPIO配置 \param[in] 无 \param[out] 无 \retval 无 */ static void BspCharge_gpio_config(void) { /* 使能GPIO时钟 */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOC); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_AF); /* 使能复用功能时钟 */ /* 半桥输入高端、低端 - 配置为复用推挽输出,用于PWM驱动 */ gpio_init(GPIOC,GPIO_MODE_AF_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_6); /* PC6: 半桥高端 */ gpio_init(GPIOC,GPIO_MODE_AF_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_7); /* PC7: 半桥低端 */ gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_7); /* PA7: 半桥 */ gpio_init(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_0); /* PB0: 半桥 */ /* 充电控制开关*/ gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE); /* SWD引脚重映射 */ gpio_init(EN_CHARGE_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,EN_CHARGE_PIN); BspCharge_TurnOffMosfet(); /* 关闭电池供电开关 */ gpio_init(EN_CHOOSE_BAT_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,EN_CHOOSE_BAT_PIN); /* ADC输入通道配置 - 配置为模拟输入模式 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_0); /* PA0: ADC通道0 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_1); /* PA1: ADC通道1 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_2); /* PA2: ADC通道2 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_3); /* PA3: ADC通道3 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_4); /* PA4: ADC通道4 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_5); /* PA5: ADC通道5 */ /* NTC热敏电阻温度检测 */ gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_4); /* PC4: NTC输入 */ /* PA6: CS_MOSD, ADC01_IN6 - MOS管电流检测 */ gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_6); /* PA6: CS_MOSD, ADC01_IN6 */ } static void BspCharge_dma_config(void) { /* ADC DMA通道配置结构体 */ dma_parameter_struct dma_data_parameter; /* 使能DMA时钟 */ rcu_periph_clock_enable(RCU_DMA_BOOST); /* ADC DMA通道配置 - 先反初始化 */ dma_deinit(DMA_BOOST, DMA_CH_BOOST); /* 初始化DMA单次数据传输模式 */ dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC_BOOST)); /* 外设地址:ADC数据寄存器 */ dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; /* 外设地址不递增 */ dma_data_parameter.memory_addr = (uint32_t)(&adc_result); /* 内存地址:ADC结果数组 */ dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; /* 内存地址递增 */ dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; /* 外设数据宽度:16位 */ dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT; /* 内存数据宽度:16位 */ dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; /* 传输方向:外设到内存 */ dma_data_parameter.number = 7; /* 传输数据数量:7个ADC通道 */ dma_data_parameter.priority = DMA_PRIORITY_HIGH; /* DMA优先级:高 */ dma_init(DMA_BOOST, DMA_CH_BOOST, &dma_data_parameter); /* 初始化DMA */ /* 使能DMA循环模式,实现连续ADC采样 */ dma_circulation_enable(DMA_BOOST, DMA_CH_BOOST); dma_channel_enable(DMA_BOOST, DMA_CH_BOOST); } /** * @brief FSBB功率路径ADC配置 */ static void BspCharge_adc_config(void) { /* 使能ADC时钟 */ rcu_periph_clock_enable(RCU_ADC_BOOST); /* 配置ADC时钟:APB2时钟的6分频 */ rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6); /* ADC模式配置:独立模式 */ adc_mode_config(ADC_MODE_FREE); /* 禁用ADC连续转换模式(由定时器触发) */ adc_special_function_config(ADC_BOOST, ADC_CONTINUOUS_MODE, DISABLE); /* 使能ADC扫描模式,用于多通道转换 */ adc_special_function_config(ADC_BOOST, ADC_SCAN_MODE, ENABLE); /* ADC数据对齐方式:右对齐 */ adc_data_alignment_config(ADC_BOOST, ADC_DATAALIGN_RIGHT); /* 配置ADC规则通道数量为7 */ adc_channel_length_config(ADC_BOOST, ADC_REGULAR_CHANNEL, 7); /* 配置ADC规则通道 - 7个通道,采样时间28.5个ADC时钟周期 */ adc_regular_channel_config(ADC_BOOST, 0, ADC_CHANNEL_0, ADC_SAMPLETIME_28POINT5); /* PA0: CS_IOUT_CHG - 充电输出电流检测 */ adc_regular_channel_config(ADC_BOOST, 1, ADC_CHANNEL_1, ADC_SAMPLETIME_28POINT5); /* PA1: CS_IOUT_ZERO - 输出电流零点参考 */ adc_regular_channel_config(ADC_BOOST, 2, ADC_CHANNEL_2, ADC_SAMPLETIME_28POINT5); /* PA2: VS_VOUT_CHG - 充电输出电压检测 */ adc_regular_channel_config(ADC_BOOST, 3, ADC_CHANNEL_3, ADC_SAMPLETIME_28POINT5); /* PA3: VS_BAT - 电池电压检测 */ adc_regular_channel_config(ADC_BOOST, 4, ADC_CHANNEL_4, ADC_SAMPLETIME_28POINT5); /* PA4: VS_PV - 光伏输入电压检测 */ adc_regular_channel_config(ADC_BOOST, 5, ADC_CHANNEL_5, ADC_SAMPLETIME_28POINT5); /* PA5: VS_DCPS - DCPS电压检测 */ adc_regular_channel_config(ADC_BOOST, 6, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5); /* PA6: CS_MOSD - MOS管电流检测 */ /* ADC trigger config */ adc_external_trigger_source_config(ADC_BOOST, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(ADC_BOOST, ADC_REGULAR_CHANNEL, ENABLE); /* ADC DMA function enable */ adc_dma_mode_enable(ADC_BOOST); /* enable ADC interface */ adc_enable(ADC_BOOST); delay_us(1000); /* ADC calibration and reset calibration */ adc_calibration_enable(ADC_BOOST); /* ADC software trigger enable */ adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); } /** * @brief 充电定时器配置函数 * @details 配置TIMER7用于FSBB变换器的PWM生成: * - 中心对齐PWM模式 * - 两路互补PWM输出(CH0和CH1) * - 可编程死区时间 * - 更新事件触发ADC采样 * - 使能更新中断用于控制循环 */ static void BspCharge_shrtimer_config(void) { timer_oc_parameter_struct timer_ocintpara; /* 输出比较参数结构体 */ timer_parameter_struct timer_initpara; /* 定时器初始化参数结构体 */ timer_break_parameter_struct timer_breakpara; /* 刹车(断路)参数结构体 */ /* 使能TIMER7时钟 */ rcu_periph_clock_enable(RCU_TIMER7); /* 反初始化定时器 */ timer_deinit(TIMER7); /* TIMER7配置 */ timer_initpara.prescaler = 0; /* 预分频系数:0(不分频) */ timer_initpara.alignedmode = TIMER_COUNTER_CENTER_UP; /* 中心对齐模式(实测为边沿对齐 400kHz) */ timer_initpara.counterdirection = TIMER_COUNTER_UP; /* 计数方向:向上 */ timer_initpara.period = PERIOD_VALUE; /* f_pwm=120M/300=400kHz(实测) */ timer_initpara.clockdivision = TIMER_CKDIV_DIV1; /* 时钟分频:不分频 */ timer_initpara.repetitioncounter = 0; /* 重复计数器:0 */ timer_init(TIMER7,&timer_initpara); /* CH0和CH1配置为PWM模式 */ timer_ocintpara.outputstate = TIMER_CCX_ENABLE; /* 主输出使能 */ timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE; /* 互补输出使能 */ timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; /* 主输出极性:高有效 */ timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; /* 互补输出极性:高有效 */ timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; /* 空闲时主输出:低电平 */ timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; /* 空闲时互补输出:低电平 */ timer_channel_output_config(TIMER7,TIMER_CH_0,&timer_ocintpara); /* 配置CH0 */ timer_channel_output_config(TIMER7,TIMER_CH_1,&timer_ocintpara); /* 配置CH1 */ timer_channel_output_config(TIMER7,TIMER_CH_3,&timer_ocintpara); /* 配置CH0 PWM占空比(初始值:10%) */ timer_channel_output_pulse_value_config(TIMER7,TIMER_CH_0,PERIOD_VALUE/10); timer_channel_output_mode_config(TIMER7,TIMER_CH_0,TIMER_OC_MODE_PWM0); /* PWM模式0 */ timer_channel_output_shadow_config(TIMER7,TIMER_CH_0,TIMER_OC_SHADOW_ENABLE); /* 使能影子寄存器 */ /* 配置CH1 PWM占空比(初始值:10%) */ timer_channel_output_pulse_value_config(TIMER7,TIMER_CH_1,PERIOD_VALUE/10); timer_channel_output_mode_config(TIMER7,TIMER_CH_1,TIMER_OC_MODE_PWM1); /* PWM模式1 */ timer_channel_output_shadow_config(TIMER7,TIMER_CH_1,TIMER_OC_SHADOW_ENABLE); /* 使能影子寄存器 */ timer_channel_output_pulse_value_config(TIMER7,TIMER_CH_3,100); timer_channel_output_mode_config(TIMER7,TIMER_CH_3,TIMER_OC_MODE_PWM1); /* 刹车(断路)功能配置 */ timer_breakpara.runoffstate = TIMER_ROS_STATE_DISABLE; /* 运行模式下断路状态:禁用 */ timer_breakpara.ideloffstate = TIMER_IOS_STATE_DISABLE; /* 空闲模式下断路状态:禁用 */ timer_breakpara.deadtime = DEAD_TIME_VALUE; /* 死区时间设置 */ timer_breakpara.breakpolarity = TIMER_BREAK_POLARITY_LOW; /* 刹车输入极性:低电平 */ timer_breakpara.outputautostate = TIMER_OUTAUTO_DISABLE; /* 自动输出状态:禁用 */ timer_breakpara.protectmode = TIMER_CCHP_PROT_OFF; /* 保护模式:关闭 */ timer_breakpara.breakstate = TIMER_BREAK_DISABLE; /* 刹车功能:禁用 */ timer_break_config(TIMER7,&timer_breakpara); /* 使能定时器主输出(高级定时器必需) */ timer_primary_output_config(TIMER7,ENABLE); /* ADC触发配置 - 使用硬件触发ADC采样 */ timer_master_output_trigger_source_select(TIMER7, TIMER_TRI_OUT_SRC_UPDATE); /* 使能更新中断,用于控制循环 */ // timer_interrupt_enable(TIMER7, TIMER_INT_CH3); timer_interrupt_enable(TIMER7, TIMER_INT_UP); nvic_irq_enable(TIMER7_UP_IRQn, 0, 1); /* 配置中断优先级 */ /* 使能自动重装载影子寄存器 */ timer_auto_reload_shadow_enable(TIMER7); /* 使能PWM输出 */ fsbb_pwm_enable(); /* 启动定时器 */ timer_enable(TIMER7); } /** * @brief 电池ADC配置函数 * @details 配置ADC1用于电池电压独立检测 * 使用单通道、软件触发模式 */ static void Battery_adc_config(void) { /* 使能ADC1时钟 */ rcu_periph_clock_enable(RCU_ADC1); /* 配置ADC时钟:APB2时钟的6分频 */ rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6); /* ADC模式配置:独立模式 */ adc_mode_config(ADC_MODE_FREE); /* ADC数据对齐方式:右对齐 */ adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT); /* 配置ADC规则通道数量为1 */ adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 1U); /* ADC触发配置:软件触发 */ adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE); /* 使能ADC */ adc_enable(ADC1); /* ADC校准 */ adc_calibration_enable(ADC1); } /** * @brief 充电模块初始化 * @details 依次初始化GPIO、定时器、DMA和ADC */ void BspCharge_Init(void) { BspCharge_gpio_config(); /* 初始化GPIO引脚 */ BspCharge_adc_config(); /* 初始化ADC */ BspCharge_dma_config(); /* 初始化ADC DMA */ Battery_adc_config(); /* 初始化电池检测ADC */ BspCharge_shrtimer_config(); /* 初始化PWM定时器 */ } /** * @brief 设置PWM比较值(预留接口,当前未使用) */ /** * @brief 浮点数值限幅辅助函数(内联) * @param v 输入值 * @param min_val 最小值 * @param max_val 最大值 * @return 限幅后的值,保证在 [min_val, max_val] 范围内 * @note 内联实现消除调用开销;调用者应保证 min_val <= max_val */ static __inline float clampf(float v, float min_val, float max_val) { if (v < min_val) return min_val; if (v > max_val) return max_val; return v; } /* FSBB模式滞回状态(文件级静态,允许 idle 时重置) */ static PowerConverterMode_t s_fsbb_last_mode = BUCK_BOOST_CONVERT_MODE; static bool s_mode_just_switched = false; /**< 模式刚切换标志,用于触发 PI 复位和 energy 冻结 */ /****************************************************** * @brief FSBB模式选择函数 * * @details 根据输入电压和电池电压自动选择FSBB工作模式: * - Buck模式:输入电压明显高于电池电压(降压充电) * - Boost模式:输入电压明显低于电池电压(升压充电) * - Buck-Boost模式:输入电压接近电池电压(升降压充电) * * 使用非对称滞回比较器避免模式频繁切换: * - 退出 Buck/Boost: 压差 < EXIT_HYST_MV (3V) 即退出 * - 进入 Buck/Boost: 压差 > ENTER_HYST_MV + EXIT_HYST_MV (5V) 才进入 * * @param vin 输入电压 (mV) * @param vbat 电池电压 (mV) * @author Andy Qu * @date: 2026-01-12 *******************************************************/ void fsbb_select_mode(float vin, float vbat) { PowerConverterMode_t new_mode = s_fsbb_last_mode; /* 保护激活、电池未连接、或非充电状态时不进行模式切换 */ if (fsbb_status.protect_state != PROTECT_NORMAL || !g_powerStatus.battery_connected || fsbb_status.charge_state == CHARGE_STATE_STANDY || fsbb_status.charge_state == CHARGE_STATE_COMPLETE) { return; } /* 根据上一次的模式进行滞回切换 */ switch (s_fsbb_last_mode) { case BUCK_CONVERT_MODE: /* Buck模式:输入电压明显高于电池电压 → 退出到Buck-Boost */ if (vin < vbat + EXIT_HYST_MV) { new_mode = BUCK_BOOST_CONVERT_MODE; } break; case BOOST_CONVERT_MODE: /* Boost模式:输入电压明显低于电池电压 → 退出到Buck-Boost */ if (vin > vbat - EXIT_HYST_MV) { new_mode = BUCK_BOOST_CONVERT_MODE; } break; case BUCK_BOOST_CONVERT_MODE: default: /* Buck-Boost模式:需要较大压差才切换,防止频繁振荡 */ if (vin > vbat + ENTER_THRESHOLD_MV) { new_mode = BUCK_CONVERT_MODE; } else if (vin < vbat - ENTER_THRESHOLD_MV) { new_mode = BOOST_CONVERT_MODE; } break; } /* 检测是否发生实际模式切换 */ if (new_mode != fsbb_status.convert_mode) { s_mode_just_switched = true; /* 通知下游:模式刚切换 */ } /* 更新系统状态 */ fsbb_status.convert_mode = new_mode; s_fsbb_last_mode = new_mode; } /****************************************************** * @brief 设置FSBB Buck模式 * * @details 配置Buck模式下的PWM输出: * - 使能Buck桥臂(左侧半桥PWM调制) * - Boost桥臂右侧高端MOS常开,低端关闭 * - 实现降压充电功能 * * @param buck_duty Buck占空比 (0.0~1.0) ******************************************************/ void set_fsbb_buck_mode(float buck_duty) { /* 非正常保护状态下禁止输出 PWM */ if (fsbb_status.protect_state != PROTECT_NORMAL) { set_fsbb_idle_mode(); return; } /* 将浮点占空比转换为定时器比较值 */ uint16_t ccr_buck = (uint16_t)(buck_duty * PWM_PERIOD_VALUE); /* 设置CH0(Buck桥臂)PWM占空比 */ timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_0, ccr_buck); /* 设置CH1(Boost桥臂)为极小占空比,使右侧高端MOS常开 */ timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_1, 0.05f*PWM_PERIOD_VALUE); /* 使能两路PWM输出 */ timer_channel_output_state_config(TIMER7, TIMER_CH_0, TIMER_CCX_ENABLE); timer_channel_output_state_config(TIMER7, TIMER_CH_1, TIMER_CCX_ENABLE); /* 使能PWM主输出 */ fsbb_pwm_enable(); /* 打开充电MOSFET */ BspCharge_TurnOnMosfet(); } /******************************************************* * @brief 设置FSBB Boost模式 * * @details 配置Boost模式下的PWM输出: * - Buck桥臂左侧高端MOS常开,低端关闭 * - 使能Boost桥臂(右侧半桥PWM调制) * - 实现升压充电功能 * * @param boost_duty Boost占空比 (0.0~0.28) * @note Buck为左侧桥臂常开,Boost为右侧桥臂交替开关 **************************************************/ void set_fsbb_boost_mode(float boost_duty) { /* 非正常保护状态下禁止输出 PWM */ if (fsbb_status.protect_state != PROTECT_NORMAL) { set_fsbb_idle_mode(); return; } const float duty_max = 0.25f; /* Boost占空比上限 */ const float duty_min = 0.01f; /* Boost占空比下限 */ /* 占空比限幅 */ boost_duty = clampf(boost_duty, duty_min, duty_max); /* 将浮点占空比转换为定时器比较值 */ uint16_t boost_ccr_value = (uint16_t)(boost_duty * PWM_PERIOD_VALUE); /* 设置CH0(Buck桥臂)为高占空比,使左侧高端MOS常开 */ timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_0, PWM_PERIOD_VALUE*0.95f); /* 设置CH1(Boost桥臂)PWM占空比 */ timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_1, boost_ccr_value); /* 使能两路PWM输出 */ timer_channel_output_state_config(TIMER7, TIMER_CH_0, TIMER_CCX_ENABLE); timer_channel_output_state_config(TIMER7, TIMER_CH_1, TIMER_CCX_ENABLE); /* 使能PWM主输出 */ fsbb_pwm_enable(); /* 打开充电MOSFET */ BspCharge_TurnOnMosfet(); } /************************************************* * @brief 设置FSBB Buck-Boost模式 * * @details 同时控制Buck桥臂(CH0)和Boost桥臂(CH1): * - CH0: PWM模式0, 高有效 → CCR控制Buck降压占空比 * - CH1: PWM模式1, 高有效 → CCR控制Boost升压占空比 * - CCR=0时CH1N互补输出100% → Boost上管常开(续流) * * @param buck_duty_target Buck占空比 [0.01, BUCK_DUTY_MAX] * @param boost_duty_target Boost占空比 [0, BOOST_DUTY_MAX] *************************************************/ void set_fsbb_buckboost_mode(float buck_duty_target, float boost_duty_target) { /* 非正常保护状态下禁止输出 PWM */ if (fsbb_status.protect_state != PROTECT_NORMAL) { set_fsbb_idle_mode(); return; } /* 硬限幅 */ buck_duty_target = clampf(buck_duty_target, 0.01f, BUCK_DUTY_MAX); /* 0.95 */ boost_duty_target = clampf(boost_duty_target, 0.0f, BOOST_DUTY_MAX); /* */ /* 设置CCR并输出PWM */ timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_0, (uint16_t)(buck_duty_target * PWM_PERIOD_VALUE)); timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_1, (uint16_t)(boost_duty_target * PWM_PERIOD_VALUE)); timer_channel_output_state_config(TIMER7, TIMER_CH_0, TIMER_CCX_ENABLE); timer_channel_output_state_config(TIMER7, TIMER_CH_1, TIMER_CCX_ENABLE); fsbb_pwm_enable(); BspCharge_TurnOnMosfet(); } /** * @brief 设置FSBB空闲模式 * * @details 关闭所有PWM输出和充电MOSFET,进入空闲状态: * - 禁用Buck和Boost PWM通道 * - 关闭充电MOSFET * - 复位PI控制器积分项,防止积分饱和 * - 设置转换模式为空闲 * @author Andy Qu * @date: 2026-01-12 */ void set_fsbb_idle_mode(void) { /* 禁用CH0(Buck桥臂)PWM输出 */ timer_channel_output_state_config(TIMER7, TIMER_CH_0, TIMER_CCX_DISABLE); timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_0, 0); /* 禁用CH1(Boost桥臂)PWM输出 */ timer_channel_output_state_config(TIMER7, TIMER_CH_1, TIMER_CCX_DISABLE); timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_1, 0); /* 关闭充电MOSFET */ BspCharge_TurnOffMosfet(); /* 复位PI控制器积分项,防止下次启动时积分饱和 */ currentPI.integral = 0.0f; /* 设置转换模式为空闲,同时重置滞回状态 */ fsbb_status.convert_mode = IDLE_CONVERT_MODE; s_fsbb_last_mode = BUCK_BOOST_CONVERT_MODE; } /********************************************** * @brief FSBB充电模块初始化 * @details 初始化FSBB充电相关硬件和状态: * - 设置初始PWM占空比为0 * - 确保PWM输出关闭 * @author Andy Qu * @date: 2026-01-12 *****************************************************/ void fsbb_charge_init(void) { /* 设置CH0和CH1初始占空比为0 */ timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_0, 0); timer_channel_output_pulse_value_config(TIMER7, TIMER_CH_1, 0); } /* 使能PWM主输出 */ void fsbb_pwm_enable(void) { timer_primary_output_config(TIMER7, ENABLE); } /* 禁用PWM主输出 */ void fsbb_pwm_disable(void) { timer_primary_output_config(TIMER7, DISABLE); } /** * @brief 使能充电输出 * @details 使能TIMER7的主输出,允许PWM信号输出到MOSFET驱动 */ void BspCharge_EnablePwmOutput(void) { /* 非正常保护状态下禁止使能 PWM 输出 */ if (fsbb_status.protect_state != PROTECT_NORMAL) { return; } timer_primary_output_config(TIMER7,ENABLE); } /** * @brief 禁用充电输出 * @details 禁用TIMER7的主输出,停止PWM信号输出 */ void BspCharge_DisablePwmOutput(void) { timer_primary_output_config(TIMER7,DISABLE); } /** * @brief 获取输出电流(带滤波) * @details 通过ADC通道0和通道1的差值计算输出电流,并应用两级滤波: * 1. 滑动平均滤波(MA_WINDOW=8):抑制高频采样噪声 * 2. 一阶IIR低通滤波(ALPHA_I=0.02):平滑输出,抑制跳变 * * 计算公式: * IOUT_raw = (ADC_CH0 - ADC_CH1) * VREF / 4096 * 4 [mA] * IOUT_ma = 滑动平均(IOUT_raw) * IOUT_iir = IOUT_iir_prev + ALPHA_I * (IOUT_ma - IOUT_iir_prev) * * 滞回特性:IOUT_raw < 50mA 时强制归零,减少噪声引起的虚假读数 * * @return 滤波后的输出电流值(mA) */ int BspCharge_GetIout(void) { /* 输入电压过低时直接返回0,避免无效采样 */ if (g_powerStatus.vin <= VIN_MIN_SAFE_MV) { return 0; } static int16_t ma_buf[MA_WINDOW] = {0}; /* 滑动平均缓冲区 */ static uint8_t ma_idx = 0; /* 滑动平均当前索引 */ static int32_t ma_sum = 0; /* 滑动平均累加和 */ static bool ma_init = false; /* 滑动平均初始化标志 */ static float iir_prev = 0.0f; /* IIR上次输出值 */ int16_t adc_diff; int16_t iout_raw; int16_t iout_ma; float iout_filtered; int result; /* Step 1: 计算原始ADC差值,带滞回归零 */ if (adc_result[0] < adc_result[1]) { adc_diff = 0; } else { adc_diff = (int16_t)(adc_result[0] - adc_result[1]); } /* 计算原始电流值:VREF * diff / 4096 * 4 = VREF * diff / 1024 */ /* 使用 int32 中间变量防止16位溢出 */ iout_raw = (int16_t)(((uint32_t)VREF * (uint32_t)adc_diff) >> 10); /* 滞回归零:原始值小于50mA时强制为0,避免噪声 */ if (iout_raw < 50) { iout_raw = 0; } /* Step 2: 滑动平均滤波 */ if (!ma_init) { /* 首次调用:用当前值填充整个缓冲区 */ for (uint8_t i = 0; i < MA_WINDOW; i++) { ma_buf[i] = iout_raw; } ma_sum = (int32_t)iout_raw * MA_WINDOW; ma_init = true; } else { /* 更新滑动窗口:减去旧值,加上新值 */ ma_sum -= ma_buf[ma_idx]; ma_buf[ma_idx] = iout_raw; ma_sum += ma_buf[ma_idx]; } /* 更新环形缓冲区索引 */ ma_idx++; if (ma_idx >= MA_WINDOW) { ma_idx = 0; } /* 计算滑动平均值 */ iout_ma = (int16_t)(ma_sum / MA_WINDOW); /* Step 3: 一阶IIR低通滤波 */ iout_filtered = iir_prev + ALPHA_I * ((float)iout_ma - iir_prev); iir_prev = iout_filtered; /* 四舍五入转整数 */ result = (int)(iout_filtered + 0.5f); /* 输出限幅:不小于0 */ if (result < 0) { result = 0; } return result; } /** * @brief 获取输出电压 * @details 通过ADC通道2计算输出电压: * VOUT = VREF * ADC_CH2 / 4096 * V_FACTOR * @return 输出电压值(mV) */ int BspCharge_GetVout(void) { return ((uint32_t)(VREF * adc_result[2]) >> 12) * V_FACTOR; } /** * @brief 获取电池电压 * @details 通过ADC通道3计算电池电压: * VBAT = VREF * ADC_CH3 / 4096 * V_FACTOR * @return 电池电压值(mV) */ int BspCharge_GetVbat(void) { return ((uint32_t)(VREF * adc_result[3]) >> 12) * V_FACTOR; } /** * @brief 获取光伏输入电压 * @details 通过ADC通道4计算光伏板电压: * VPV = VREF * ADC_CH4 / 4096 * V_FACTOR * @return 光伏输入电压值(mV) */ int BspCharge_GetVpv(void) { return ((uint32_t)(VREF * adc_result[4]) >> 12) * V_FACTOR; } /** * @brief 获取DCPS电压 * @details 通过ADC通道5计算DCPS电压: * VDC = VREF * ADC_CH5 / 4096 * V_FACTOR * @return DCPS电压值(mV) */ int BspCharge_GetVdc(void) { return ((uint32_t)(VREF * adc_result[5]) >> 12) * V_FACTOR; } /** * @brief 获取MOS管电流检测值(带一阶IIR低通滤波) * @details 通过ADC通道6获取MOS管电流检测的原始ADC值, * 应用一阶IIR低通滤波(ALPHA_I=0.02)平滑输出,抑制噪声跳变。 * 计算公式:output = (1-α)*prev + α*raw (α=ALPHA_I=0.02) * @return 滤波后的MOS管电流ADC原始值 */ int BspCharge_GetImos(void) { /* 输入电压过低时直接返回0,避免无效采样 */ if (g_powerStatus.vin <= VIN_MIN_SAFE_MV) { return 0; } static float imos_iir = 0.0f; /* IIR滤波状态 */ int imos_raw; int result; /* Step 1: 计算原始ADC值 */ imos_raw = (int)(((uint32_t)(VREF * adc_result[6]) >> 12)); /* Step 2: 一阶IIR低通滤波,output = (1-α)*prev + α*raw */ imos_iir = ONE_MINUS_ALPHA_I * imos_iir + ALPHA_I * (float)imos_raw; /* 四舍五入转整数 */ result = (int)(imos_iir + 0.5f); /* 输出限幅:不小于0 */ if (result < 0) { result = 0; } return result; } /** * @brief GPIO引脚电平翻转函数 * @param gpio_periph GPIO外设基地址 * @param pin 要翻转的引脚 */ void gpio_toggle_bits(uint32_t gpio_periph,uint32_t pin) { /* 检查当前引脚是否为高电平 */ if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))) { GPIO_BC(gpio_periph) = (uint32_t)pin; /* 置低 */ } else { GPIO_BOP(gpio_periph) = (uint32_t)pin; /* 置高 */ } } /** * @brief MCU指示灯闪烁 * @details 翻转MCU_LED引脚电平,实现LED闪烁效果 */ void mcu_led_toggle(void) { gpio_toggle_bits(MCU_LED_PORT, MCU_LED_PIN); } /** * @brief 电池指示灯闪烁 * @details 翻转BAT_LED引脚电平,实现电池LED闪烁效果 */ void bat_led_toggle(void) { gpio_toggle_bits(BAT_LED_PORT, BAT_LED_PIN); } /** * @brief IO测试函数 * @param sta 输出状态(SET=高电平,RESET=低电平) * @details 同时设置PV使能、BAT使能、电池加热、传感器加热的输出状态 */ void test_IO(bit_status sta) { gpio_bit_write(EN_PV_PORT,EN_PV_PIN,sta); /* 设置PV输出使能 */ gpio_bit_write(EN_BAT_SYSPOWER_PORT,EN_BAT_SYSPOWER_PIN,sta); /* 设置BAT输出使能 */ gpio_bit_write(BAT_HEAT_PORT,BAT_HEAT_PIN,sta); /* 设置电池加热 */ gpio_bit_write(SEN_HEAT_PORT,SEN_HEAT_PIN,sta); /* 设置传感器加热 */ } /** * @brief 通用GPIO配置函数 * @details 配置系统各功能引脚: * - MCU运行指示灯 * - 电池状态指示灯 * - PV和BAT输出使能 * - 电池加热和传感器加热控制 * - 按键输入 */ void gpio_config(void) { /* 使能GPIO时钟 */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOC); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_GPIOE); /* 配置MCU运行指示灯 - 推挽输出 */ gpio_init(MCU_LED_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,MCU_LED_PIN); /* 配置电池状态指示灯 - 推挽输出 */ gpio_init(BAT_LED_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,BAT_LED_PIN); /* 配置PV输出使能 - 推挽输出 */ gpio_init(EN_PV_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,EN_PV_PIN); /* 配置BAT输出使能 - 推挽输出 */ gpio_init(EN_BAT_SYSPOWER_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,EN_BAT_SYSPOWER_PIN); /* 配置电池加热控制 - 推挽输出 */ gpio_init(BAT_HEAT_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,BAT_HEAT_PIN); /* 配置传感器加热控制 - 推挽输出 */ gpio_init(SEN_HEAT_PORT,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,SEN_HEAT_PIN); /* 配置按键输入 - 上拉输入 */ gpio_init(KEY_PORT,GPIO_MODE_IPU,GPIO_OSPEED_50MHZ,KEY_PIN); /* 初始状态设置 */ MCU_LED_ON; /* 点亮MCU指示灯 */ BspCharge_TurnOffMosfet(); /* 关闭电池输出 */ test_IO(RESET); /* 复位所有测试IO */ SHEAT_ON; /* 打开传感器加热 */ } /** * @brief 关闭PV供电(禁用光伏输入供电) * @details 设置EN_PV引脚为高电平,关闭PV供电回路 */ void BspPv_DisableSupply(void) { gpio_bit_set(EN_PV_PORT, EN_PV_PIN); } /** * @brief 开启PV供电(使能光伏输入供电) * @details 设置EN_PV引脚为低电平,开启PV供电回路 */ void BspPv_EnableSupply(void) { gpio_bit_reset(EN_PV_PORT, EN_PV_PIN); } void BspBat_EnableSupply(void) { gpio_bit_reset(EN_BAT_SYSPOWER_PORT, EN_BAT_SYSPOWER_PIN); /* 低电平使能电池供电 */ } void BspBat_DisableSupply(void) { gpio_bit_set(EN_BAT_SYSPOWER_PORT, EN_BAT_SYSPOWER_PIN); /* 高电平禁用电池供电 */ } /** * @brief 设置 VAUX 总线电源输入源 * @param source 电源源选择 * @note 决定两个 DCDC(3.3V/10V)的供电来源 */ void Vaux_SetSource(VauxSource_t source) { if (source == VAUX_BATTERY_ENABLE) { /* 使能 choose bat=1,电池也输出到 VAUX 总线(两个 DCDC 可由电池供电) */ gpio_bit_set(EN_CHOOSE_BAT_PORT, EN_CHOOSE_BAT_PIN); } else { /* 禁用电池输出,仅由外部适配器为 VAUX 供电 */ gpio_bit_reset(EN_CHOOSE_BAT_PORT, EN_CHOOSE_BAT_PIN); } } /** * @brief 禁能电池充电回路的驱动电源(电池充电供电) * @details 设置EN_CH引脚为低电平,关闭电池充电输出,切断充电回路驱动电源 */ void BspCharge_TurnOffMosfet(void) { gpio_bit_reset(EN_CHARGE_PORT, EN_CHARGE_PIN); } /** * @brief 打开充电MOSFET * @details 通过控制BAT_ON宏来打开电池充电MOSFET开关 */ void BspCharge_TurnOnMosfet(void) { gpio_bit_set(EN_CHARGE_PORT, EN_CHARGE_PIN); } /* 内联控制函数,取代宏,消除分号风险且类型安全 */ /** * @brief 电池LED控制函数(内联) * @param on true=点亮,false=熄灭 */ static inline void bat_led_ctrl(bool on) { LED_Level_t level = on ? LED_LEVEL_ON : LED_LEVEL_OFF; /* 根据开关状态选择电平 */ gpio_bit_write(BAT_LED_PORT, BAT_LED_PIN, (bit_status)level); /* 写入GPIO */ } /* 电池LED状态机 - 私有状态变量 */ LED_State_t s_state = LED_STATE_OFF; /* 当前LED状态:初始灭 */ static uint16_t s_tick_counter = 0; /* 10ms节拍计数器 */ static bool s_led_output = false; /* 当前LED输出状态(true=亮) */ /** * @brief 电池LED初始化 * @details 默认处于空闲状态:常亮 */ void BatLed_Init(void) { bat_led_ctrl(true); /* 点亮LED */ s_state = LED_STATE_IDLE; /* 设置状态为空闲 */ s_tick_counter = 0; /* 复位计数器 */ s_led_output = true; /* 设置输出状态为亮 */ } /** * @brief 电池LED节拍处理函数 * @details 每10ms调用一次,实现充电状态LED闪烁: * - 充电中:每300ms翻转一次LED(闪烁) * - 空闲:常亮 */ void BatLed_UpdateChargeIndicator(void) { /* 输入判断:电池是否连接 */ bool bat_present = (g_powerStatus.bat >= 5000.0f);//5V /* 状态判定:3种LED状态 */ if (!bat_present) { s_state = LED_STATE_OFF; /* 无电池:LED灭 */ } else { s_state = LED_STATE_IDLE; } if ((fsbb_status.charge_state == CHARGE_STATE_PRECHARGE) || (fsbb_status.charge_state == CHARGE_STATE_CC) || (fsbb_status.charge_state == CHARGE_STATE_CV)) { s_state = LED_STATE_CHARGING; /* 充电中:闪烁 */ } /* 状态执行 */ switch (s_state) { case LED_STATE_OFF: bat_led_ctrl(false); s_tick_counter = 0; break; case LED_STATE_IDLE: bat_led_ctrl(true); s_tick_counter = 0; break; case LED_STATE_CHARGING: s_tick_counter++; if (s_tick_counter >= 30) /* 30 × 10ms = 300ms 翻转一次 */ { s_tick_counter = 0; s_led_output = !s_led_output; bat_led_ctrl(s_led_output); } break; } } /************************************* * @brief 停止BUCK-BOOST转换器 * @details 关闭充电MOSFET并禁用PWM输出 * author Andy Qu ******************************************/ void fsbb_shutdown(void) { BspCharge_TurnOffMosfet(); fsbb_pwm_disable(); /* 同时禁用 CH0/CH1 通道输出,防止主输出恢复时意外输出 PWM */ timer_channel_output_state_config(TIMER7, TIMER_CH_0, TIMER_CCX_DISABLE); timer_channel_output_state_config(TIMER7, TIMER_CH_1, TIMER_CCX_DISABLE); g_powerStatus.charge_current_ref = 0; } /* 保护去抖计数器 */ /* 正常状态去抖计数器 */ /** * @brief 检查保护状态 * * @details 检测各种保护条件(过压、过流、欠压、短路等) * 使用去抖机制避免误触发 * * @retval uint8_t 故障标志:0=正常,非0=有故障 */ uint8_t protect_check_outputovervoltage(void) { static uint8_t ovp_debounce = 0; /**< 过压去抖计数器 */ /* 检查当前是否处于故障状态 */ /* 输出过压保护 */ if (g_powerStatus.vout > OVER_OUTPUT_VOLTAGE_THRESHOLD) { if (++ovp_debounce >= PROTECT_DEBOUNCE_COUNT) { fsbb_status.protect_state = PROTECT_OUTPUT_OVERVOLTAGE; return 1; } } else { ovp_debounce = 0; } return 0; } /* 输出过流保护 */ uint8_t protect_check_output_overcurrent(void) { static uint8_t ocp_debounce = 0; /**< 过流去抖计数器 */ if (g_powerStatus.iout > CHARGE_CURRENT_CC + 300.0f) // 允许一定的超调,300mA;否则容易误触发关闭输出 { if (++ocp_debounce >= PROTECT_DEBOUNCE_COUNT) { fsbb_status.protect_state = PROTECT_OUTPUT_OVERCURRENT; return 1; } } else { ocp_debounce = 0; } return 0; } /* 输入欠压保护 */ uint8_t protect_check_input_undervoltage(void) { static uint8_t uvp_debounce = 0; /**< 欠压去抖计数器 */ if (g_powerStatus.vin < VIN_MIN_SAFE_MV) { if (++uvp_debounce >= PROTECT_DEBOUNCE_COUNT) { fsbb_status.protect_state = PROTECT_INPUT_UNDERVOLTAGE; return 1; } } else { uvp_debounce = 0; } return 0; } /* 输入过压保护 */ uint8_t protect_check_input_overvoltage(void) { static uint8_t iovp_debounce = 0; /**< 输入过压去抖计数器 */ if (g_powerStatus.vin > VIN_MAX_SAFE_V) { if (++iovp_debounce >= PROTECT_DEBOUNCE_COUNT) { fsbb_status.protect_state = PROTECT_INPUT_OVERVOLTAGE; return 1; } } else { iovp_debounce = 0; } return 0; } /* 电池欠压保护 */ uint8_t protect_check_battery_undervoltage(void) { static uint8_t buvp_debounce = 0; /**< 电池欠压去抖计数器 */ if (g_powerStatus.bat < BAT_TOTAL_MIN) { if (++buvp_debounce >= PROTECT_DEBOUNCE_COUNT) { fsbb_status.protect_state = PROTECT_BATTERY_UNDERVOLTAGE; return 1; } } else { buvp_debounce = 0; } return 0; } /** * @brief MOS管过流保护检测 * @details 检测MOS管电流ADC原始值是否超过 I_MOS_PROTECT_VALUE 阈值, * 使用去抖机制避免误触发。去抖计数 > 10 时触发保护并关闭输出。 * (原 charge.c TIMER7 ISR 内联逻辑封装) * @retval 0=正常, 非0=检测到MOS过流故障 */ uint8_t protect_check_mos_overcurrent(void) { static uint8_t mosp_debounce = 0; /**< MOS过流去抖计数器 */ if (g_powerStatus.check_imos > I_MOS_PROTECT_VALUE) { if (++mosp_debounce > 10) { fsbb_status.protect_state = PROTECT_MOS_OVERCURRENT; mosp_debounce = 0; return 1; } } else { mosp_debounce = 0; } return 0; } /** * @brief 短路保护检测 * @details 检测输出电流是否超过 OVER_OUT_CURRENT_THRESHOLD 硬件极限, * 使用半去抖次数(更快响应)以保护硬件安全。 * @retval 0=正常, 非0=检测到短路故障 */ uint8_t protect_check_shortcircuit(void) { static uint8_t scp_debounce = 0; /**< 短路去抖计数器 */ if (g_powerStatus.iout > OVER_OUT_CURRENT_THRESHOLD) { if (++scp_debounce >= PROTECT_DEBOUNCE_COUNT / 2) { fsbb_status.protect_state = PROTECT_OUTPUT_SHORT_CIRCUIT; return 1; } } else { scp_debounce = 0; } return 0; } /** * @brief 故障恢复检测(正常状态去抖) * @details 当所有故障条件消除后,经过 PROTECT_DEBOUNCE_COUNT 次确认 * 才将保护状态恢复为 PROTECT_NORMAL,并重置充电状态机。 * 防止故障恢复过程中的抖动导致反复切换。 * @retval 0=仍在故障中, 非0=已恢复正常 */ uint8_t protect_check_fault_recovery(void) { static uint8_t normal_debounce = 0; /* 仅在当前处于故障状态时才进行恢复检测 */ if (fsbb_status.protect_state == PROTECT_NORMAL) { normal_debounce = 0; return 0; } if (++normal_debounce >= PROTECT_DEBOUNCE_COUNT) { fsbb_status.protect_state = PROTECT_NORMAL; fsbb_status.charge_state = CHARGE_STATE_START; normal_debounce = 0; return 1; /* 已恢复 */ } return 0; /* 仍在去抖,保持故障状态 */ } /** * @brief 综合保护状态检查(调度器) * * @details 依次调用各保护检测子功能: * 1. protect_check_outputovervoltage() - 输出过压 * 2. protect_check_output_overcurrent() - 输出过流 * 3. protect_check_input_undervoltage() - 输入欠压 * 4. protect_check_battery_undervoltage() - 电池欠压 * 5. protect_check_shortcircuit() - 短路保护 * * 故障处理:任一保护触发时,关闭充电MOSFET并重置状态机。 * 故障恢复:所有条件清除后由 protect_check_fault_recovery() 处理。 * * @retval uint8_t 0=正常(PROTECT_NORMAL), 非0=有故障 */ uint8_t check_protections(void) { /* ---- 短路保护(半去抖,最快响应,独立处理) ---- */ if (protect_check_shortcircuit() != 0) { fsbb_shutdown(); fsbb_status.charge_state = CHARGE_STATE_STANDY; fsbb_status.convert_mode = IDLE_CONVERT_MODE; return 1; } /* ---- 其余保护依次检测,一旦触发立即处理并返回 ---- */ if (protect_check_outputovervoltage() == 0 && protect_check_output_overcurrent() == 0 && protect_check_input_undervoltage() == 0 && protect_check_input_overvoltage() == 0 && protect_check_battery_undervoltage() == 0 && protect_check_mos_overcurrent() == 0) { /* 全部正常:尝试故障恢复 */ protect_check_fault_recovery(); return 0; } /* 故障处理(过压/过流/输入欠压/电池欠压任一触发走到这里) */ fsbb_shutdown(); fsbb_status.charge_state = CHARGE_STATE_STANDY; fsbb_status.convert_mode = IDLE_CONVERT_MODE; return 1; } void fsbb_charge_protection_check(void) { if ((check_protections() != PROTECT_NORMAL) ) { fsbb_shutdown(); g_powerStatus.charge_current_ref = 0; fsbb_status.convert_mode = IDLE_CONVERT_MODE; fsbb_status.charge_state = CHARGE_STATE_STANDY; } } /***************************************** @brief 检查电池连接状态函数 @details 通过监测电池电压判断电池是否连接 @return bool @note 1. 电池连接条件:电池电压高于最小阈值的一半 2. 如果电池未连接,禁用PWM和充电MOSFET以防止误操作 3. 返回电池连接状态 4. 该函数在主控制循环中定期调用,以确保系统根据电池连接状态正确响应 5. 适用于7s锂电池系统 author: Andy Qu date: 2026-01-12 *************************************************/ bool check_battery_connection(void) { // 检查电池是否连接(电压高于最小阈值) g_powerStatus.battery_connected = (g_powerStatus.bat >= 5.0f);// 电池电压大于5V认为连接 if (!g_powerStatus.battery_connected) { // 未连接电池 - 禁用PWM和充电MOSFET fsbb_shutdown(); fsbb_status.convert_mode = IDLE_CONVERT_MODE; fsbb_status.charge_state = CHARGE_STATE_STANDY; prev_battery_connected = false; } return g_powerStatus.battery_connected; } /* 最终占空比变量 */ float final_duty_buck ,final_duty_boost; /****************************************************** * @brief 软启动触发检测 * * @details 监测电池连接变化和输入电压恢复,触发软启动: * 1. 电池从断开到连接 → 触发软启动 * 2. 输入电压相对基线累积上升超过 VIN_INCREASE_THRESHOLD (5V) → 触发软启动 * 基线跟踪:vin 下降时自动更新基线,上升时累积差值检测。 * 带 3 次连续确认去抖,触发后更新基线防止重复触发。 * * @note 调用频率: 1kHz (TIMER7 ISR curr 分频) * 当 protect_state != PROTECT_NORMAL 时直接返回 * author Andy Qu * date: 2026-07-22 ****************************************************/ void softstart_trigger_check(void) { static uint8_t vin_rise_debounce = 0; /**< 输入电压上升去抖计数器 */ static float vin_baseline = 0.0f; /**< 输入电压基线(用于检测累积上升) */ static bool vin_baseline_valid = false; /**< 基线是否有效 */ /* 保护状态下不触发软启动,重置基线 */ if (fsbb_status.protect_state != PROTECT_NORMAL) { vin_rise_debounce = 0; vin_baseline_valid = false; return; } /* 电池未连接则不触发,重置基线 */ if (!check_battery_connection()) { vin_rise_debounce = 0; vin_baseline_valid = false; return; } /* ---- 软启动触发条件检测 ---- */ /* 条件1: 电池从断开到连接 */ if (!prev_battery_connected && g_powerStatus.battery_connected) { soft_start_active = true; soft_start_counter = 0; } /* 条件2: 输入电压累积上升超过阈值(带去抖) * 基线跟踪:vin 下降时更新基线,vin 上升时累积差值检测 */ if (!vin_baseline_valid || g_powerStatus.vin < vin_baseline) { /* 电压下降或首次:更新基线为当前值 */ vin_baseline = g_powerStatus.vin; vin_baseline_valid = true; vin_rise_debounce = 0; } else if ((g_powerStatus.vin - vin_baseline) > VIN_INCREASE_THRESHOLD) { /* 相对于基线的累积上升超过 5V */ if (++vin_rise_debounce >= 3) { soft_start_active = true; soft_start_counter = 0; vin_rise_debounce = 0; vin_baseline = g_powerStatus.vin; /* 更新基线防止重复触发 */ } } else { vin_rise_debounce = 0; } /* 更新历史状态 */ prev_battery_connected = g_powerStatus.battery_connected; } /** * @brief 7S锂电池充电状态机 (1kHz) * @note 状态转换: * STANDY → START (上电/复位) * START → PRECHARGE (bat < PRECHARGE_VOLTAGE) * START → CC (PRECHARGE_VOLTAGE ≤ bat < CV) * START → CV (bat ≥ CV) * PRECHARGE → CC (bat ≥ PRECHARGE_VOLTAGE) * CC → CV (bat ≥ CV - 500mV) * CV → COMPLETE (iout < CHARGE_STOP_CURRENT 持续) * COMPLETE → START (bat ≤ RESTART_VOLTAGE 或电池断开) * * 超时保护: * PRECHARGE_TIMEOUT / CHARGE_TIMEOUT / MAX_RETRIES */ void charge_step_machine(void) { // 7s锂电池充电状态机 switch (fsbb_status.charge_state) { case CHARGE_STATE_STANDY: break; case CHARGE_STATE_START: // 当电池连接且在电压范围内时过渡到预充电 if (g_powerStatus.battery_connected && g_powerStatus.bat >= BAT_TOTAL_MIN && g_powerStatus.bat < PRECHARGE_VOLTAGE) { fsbb_status.charge_state = CHARGE_STATE_PRECHARGE; } else if (g_powerStatus.battery_connected && g_powerStatus.bat >= PRECHARGE_VOLTAGE && g_powerStatus.bat < CHARGE_VOLTAGE_CV) { fsbb_status.charge_state = CHARGE_STATE_CC; } else if (g_powerStatus.battery_connected && g_powerStatus.bat >= CHARGE_VOLTAGE_CV) { /* 电池电压已达CV目标,直接进入CV模式(或已接近充满) */ fsbb_status.charge_state = CHARGE_STATE_CV; } break; case CHARGE_STATE_PRECHARGE: // 当电池电压达到预充电电压时过渡到CC模式 if (g_powerStatus.bat >= PRECHARGE_VOLTAGE) { fsbb_status.charge_state = CHARGE_STATE_CC; } // 如果电池断开或电压太低则返回START else if (!g_powerStatus.battery_connected || g_powerStatus.bat < BAT_TOTAL_MIN) { fsbb_status.charge_state = CHARGE_STATE_START; } break; case CHARGE_STATE_CC: // 当电池电压达到充电电压时过渡到CV模式 if (g_powerStatus.bat >= CHARGE_VOLTAGE_CV - 500.0f) { fsbb_status.charge_state = CHARGE_STATE_CV; break; } // 如果电池断开或电压太低则返回START else if (!g_powerStatus.battery_connected || g_powerStatus.bat < BAT_TOTAL_MIN) { fsbb_status.charge_state = CHARGE_STATE_START; break; } break; case CHARGE_STATE_CV: // 当充电电流降到终止阈值且电压达到CV目标附近时过渡到完成 // 或电池电压超过CV目标(兜底:防止电流降不下来导致永远无法完成) if ((g_powerStatus.iout <= CHARGE_TERMINATE_CURRENT && g_powerStatus.bat >= CHARGE_VOLTAGE_CV) || g_powerStatus.bat >= CHARGE_VOLTAGE_CV + 500.0f) { fsbb_status.charge_state = CHARGE_STATE_COMPLETE; break; } // 如果电池断开或电压太低则返回START else if (!g_powerStatus.battery_connected || g_powerStatus.bat < BAT_TOTAL_MIN) { fsbb_status.charge_state = CHARGE_STATE_START; } break; case CHARGE_STATE_COMPLETE: /* 每周期强制确保FSBB处于关闭状态,防止任何路径意外重启用PWM */ fsbb_shutdown(); fsbb_status.convert_mode = IDLE_CONVERT_MODE; // 电池电压回落到复充阈值时重新开始充电 if (g_powerStatus.bat <= RECHARGE_VOLTAGE_THRESHOLD) { fsbb_status.charge_state = CHARGE_STATE_START; soft_start_active = true; soft_start_counter = 0; } // 电池断开也回到START else if (!g_powerStatus.battery_connected) { fsbb_status.charge_state = CHARGE_STATE_START; } break; default: break; } } /** * @brief 电压PI控制器运行函数(带抗积分饱和) * @details 使用条件积分(Conditional Integration)防止积分饱和: * - 只有当输出在有效范围内,或误差方向能将输出拉回有效范围时,才累加积分 * - 输出限幅到 out_min ~ out_max * @param pi PI控制器结构体指针 * @param ref_voltage 目标电压 (mV) * @param feedback_voltage 反馈电压 (mV) * @return PI输出值(限幅后) */ int32_t VoltagePI_Run( PI_t *pi, int32_t ref_voltage, int32_t feedback_voltage) { int32_t error; int32_t output; error = ref_voltage - feedback_voltage; /* 比例项 */ output = pi->kp * error; /* 条件积分:只在输出未饱和或误差能将输出拉回范围时才积分 */ /* 避免积分饱和(Anti-Windup) */ if ((pi->output > pi->out_min && pi->output < pi->out_max) || (pi->output <= pi->out_min && error > 0) || (pi->output >= pi->out_max && error < 0)) { pi->integral += pi->ki * error; } /* 积分限幅 */ if(pi->integral > pi->integral_max) { pi->integral = pi->integral_max; } if(pi->integral < pi->integral_min) { pi->integral = pi->integral_min; } /* 比例+积分 */ output += pi->integral; /* 输出限幅 */ if(output > pi->out_max) { output = pi->out_max; } if(output < pi->out_min) { output = pi->out_min; } pi->output = output; return output; } /** * @brief 充电电压/电流参考值控制 (100Hz) * @note 根据充电状态机设定 g_powerStatus.charge_current_ref: * - START/STANDBY: i_ref = 0 * - PRECHARGE: i_ref = PRECHARGE_CURRENT * - CC: i_ref = CHARGE_CURRENT_CC (CHARGE_CURRENT_CC) * - CV: i_ref = voltagePI 输出, 带自适应钳位防止饱和 * - COMPLETE: i_ref = 0 */ void Charge_Voltage_Control(void) { /* 状态切换检测:记录上一次的充电状态 */ static ChargeState_t prev_charge_state = CHARGE_STATE_STANDY; switch(fsbb_status.charge_state) { case CHARGE_STATE_START: /* 启动状态:等待状态机判断电池条件后自动转入CC/CV/PRECHARGE */ g_powerStatus.charge_current_ref = 0; break; case CHARGE_STATE_PRECHARGE: /* 刚进入预充电模式时触发软起动 */ if (prev_charge_state != CHARGE_STATE_PRECHARGE) { soft_start_active = true; soft_start_counter = 0; } g_powerStatus.charge_current_ref = (int32_t)PRECHARGE_CURRENT; /* = PRECHARGE_CURRENT mA */ break; case CHARGE_STATE_CC: /* 刚进入CC模式时触发软起动 */ if (prev_charge_state != CHARGE_STATE_CC) { soft_start_active = true; soft_start_counter = 0; } g_powerStatus.charge_current_ref = CHARGE_CURRENT_CC; break; case CHARGE_STATE_CV: /* * CV阶段控制策略(电压外环 + 电流内环): * * 核心理念: CV阶段电池已接近满电,充电电流应自然下降。 * 电压PI根据电压误差计算"需要的电流",电流PI确保实际电流跟随。 * */ /* 刚进入CV模式时,同时复位电压PI和电流PI */ if (prev_charge_state != CHARGE_STATE_CV) { /* * 电压PI以当前实际电流为起点。 * 进入CV时 bat≈CV目标,iout 就是电池当前需要的电流。 * 后续电压PI只需在这个基础上微调。 */ voltagePI.integral = (int32_t)g_powerStatus.iout; voltagePI.output = (int32_t)g_powerStatus.iout; if (voltagePI.integral < 0) voltagePI.integral = 0; if (voltagePI.integral > CHARGE_CURRENT_CC) voltagePI.integral = CHARGE_CURRENT_CC; /* 电流PI:清零积分 */ currentPI.integral = 0.0f; currentPI.output = 0.0f; } /* 运行电压PI */ g_powerStatus.charge_current_ref = VoltagePI_Run( &voltagePI, CHARGE_VOLTAGE_CV, g_powerStatus.bat); /* * 自适应钳位:防止电压PI积分饱和导致电流参考值虚高 * * 条件: energy饱和(>=0.95) 且 实际电流不到参考值的80% * 结论: 硬件达极限,钳位参考值到"实际电流+10%" */ if (currentPI.output >= 0.95f && g_powerStatus.iout < g_powerStatus.charge_current_ref * 0.8f) { int32_t clamped_ref = (int32_t)(g_powerStatus.iout * 1.1f); if (clamped_ref > g_powerStatus.charge_current_ref) clamped_ref = g_powerStatus.charge_current_ref; /* 只降不升 */ if (clamped_ref < 50) clamped_ref = 50; /* 最小50mA */ g_powerStatus.charge_current_ref = clamped_ref; /* 同步钳位电压PI积分 */ if (voltagePI.integral > clamped_ref) voltagePI.integral = clamped_ref; } /* 硬限幅 */ if (g_powerStatus.charge_current_ref > (int32_t)CHARGE_CURRENT_CC) g_powerStatus.charge_current_ref = (int32_t)CHARGE_CURRENT_CC; else if (g_powerStatus.charge_current_ref < 0) g_powerStatus.charge_current_ref = 0; break; case CHARGE_STATE_COMPLETE: /* 充电完成:清零PI积分、参考电流。shutdown由charge_step_machine首次进入时执行 */ voltagePI.integral = 0; voltagePI.output = 0; currentPI.integral = 0.0f; currentPI.output = 0.0f; g_powerStatus.charge_current_ref = 0; break; default: g_powerStatus.charge_current_ref = 0; break; } /* 记录当前状态,用于下一周期检测状态切换 */ prev_charge_state = fsbb_status.charge_state; } /** * @brief 电流控制更新函数 * @details 将电压环输出的 g_powerStatus.charge_current_ref 设置为电流PI的参考值, * 实现电压外环 + 电流内环的级联控制结构 */ void Current_Control_Update(void) { currentPI.i_ref = g_powerStatus.charge_current_ref; } /** * @brief 电流PI控制器运行函数(归一化误差 + 抗积分饱和) * @details * 1. 误差归一化: error_norm = (i_ref - i_fb) / CURRENT_HARD_LIMIT_MA * 将 mA 级误差映射到 [-1, 1] 无量纲区间 * 2. 条件积分(Anti-Windup): 输出饱和时停止同向积分 * 3. 输出限幅到 [0, out_max],作为 energy(能量/占空比请求系数) * @param pi 电流PI控制器指针 * @param i_ref 电流参考值 (mA),来自电压外环 * @param i_fb 电流反馈值 (mA),来自 ADC 采样 * @return energy ∈ [0, out_max](当前为 [0, 2.0]),传递给 FSBB_Duty_Calculate */ float CurrentPI_Run(PI_t *pi, float i_ref, float i_fb) { float error; /* 原始误差 (mA) */ float error_norm; /* 归一化误差 [-1, 1] */ float out; /* 1. 计算原始误差 */ error = i_ref - i_fb; /* 2. 归一化: 除以电流量程,映射到 [-1, 1] */ error_norm = error / CURRENT_HARD_LIMIT_MA; /* 3. 比例项(基于归一化误差) */ out = pi->kp * error_norm; /* 4. 条件积分(Anti-Windup): * 只在以下情况累加积分: * a) 输出在有效范围内 (未饱和) * b) 输出在下限但误差为正 (需要增大输出) * c) 输出在上限但误差为负 (需要减小输出) */ if ((pi->output > pi->out_min && pi->output < pi->out_max) || (pi->output <= pi->out_min && error_norm > 0) || (pi->output >= pi->out_max && error_norm < 0)) { pi->integral += pi->ki * error_norm; } /* 5. 积分限幅 */ if (pi->integral > pi->integral_max) pi->integral = pi->integral_max; if (pi->integral < pi->integral_min) pi->integral = pi->integral_min; /* 6. 总输出 = 比例 + 积分 */ out += pi->integral; /* 7. 输出限幅到 [out_min, out_max](energy 系数) */ if (out > pi->out_max) out = pi->out_max; if (out < pi->out_min) out = pi->out_min; pi->output = out; return out; } /** * @brief FSBB占空比计算(前馈 + 电流PI energy缩放) * @details * 根据当前工作模式和 vin/bat 电压比计算前馈占空比,再叠加电流PI输出的 energy。 * energy ∈ [0, out_max] 表示需要的功率系数,1.0=前馈平衡, >1.0=额外推力。 * * Buck-Boost 模式的理论占空比: * d1_buck = Vbat / Vin (Buck桥臂,降压比) * d2_boost = 1 - Vin/Vbat (Boost桥臂,升压比) * 当 Vin ≈ Vbat 时,d1≈1, d2≈0,FSBB工作在最优点附近。 * * @param energy 电流PI输出的能量系数 [0, currentPI.out_max] */ void FSBB_Duty_Calculate(float energy) { float d1, d2; /* 非充电状态(STANDBY/START/COMPLETE)或保护状态 → 强制空闲 */ if (fsbb_status.protect_state != PROTECT_NORMAL || fsbb_status.charge_state == CHARGE_STATE_STANDY || fsbb_status.charge_state == CHARGE_STATE_START || fsbb_status.charge_state == CHARGE_STATE_COMPLETE) { set_fsbb_idle_mode(); return; } /* energy 安全限幅(允许 >1.0 用于 Buck-Boost 额外推力) */ if (energy > currentPI.out_max) energy = currentPI.out_max; if (energy < 0.0f) energy = 0.0f; /* 模式刚切换:冻结 energy = 前馈基准 1.0,同时复位电流PI积分防冲击 */ if (s_mode_just_switched) { s_mode_just_switched = false; energy = 1.0f; currentPI.integral = 0.0f; currentPI.output = 1.0f; } switch (fsbb_status.convert_mode) { case BUCK_CONVERT_MODE: /* Buck: d = Vbat/Vin (降压比),乘以energy限制电流 */ d1 = (float)g_powerStatus.bat / g_powerStatus.vin; d1 *= energy; set_fsbb_buck_mode(d1); break; case BOOST_CONVERT_MODE: /* Boost: d = 1 - Vin/Vbat */ d2 = 1.0f - (float)g_powerStatus.vin / g_powerStatus.bat; d2 *= energy; set_fsbb_boost_mode(d2); break; case BUCK_BOOST_CONVERT_MODE: /* * FSBB Buck-Boost 两自由度控制: * Vout = Vin × d1 / (1 - d2) * * 策略: * Step 1: 前馈计算基准占空比 (使 Vout = Vbat) * d1_base = min(Vbat/Vin, BUCK_DUTY_MAX) * d2_base = 1 - Vin×d1_base/Vbat (d1超限时Boost补偿) * * Step 2: energy 调节 (电流PI输出) * energy = 1.0: 前馈基准,Vout ≈ Vbat * energy < 1.0: 等比缩小 d1/d2 * energy > 1.0: 额外增加d2产生推力 → Vout > Vbat → 更大电流 * * Step 3: d2额外推力 = (energy-1.0) * BOOST_DUTY_MAX * 把energy超出1.0的部分映射到额外d2 * energy=1.0: d2=d2_base (刚好平衡) * energy=1.5: d2=d2_base + 0.5×0.30 = d2_base + 0.15 (额外推力) */ /* Step 1: 前馈基准 */ d1 = (float)g_powerStatus.bat / g_powerStatus.vin; d2 = 0.0f; if (d1 > BUCK_DUTY_MAX) { d1 = BUCK_DUTY_MAX; d2 = 1.0f - ((float)g_powerStatus.vin * BUCK_DUTY_MAX) / (float)g_powerStatus.bat; } /* Step 2: energy 已在上方限幅到 [0, out_max],此处直接使用 */ /* Step 3: 基准部分 + 额外推力 */ d1 *= (energy < 1.0f) ? energy : 1.0f; /* d1最大就前馈值,不能再大 */ if (energy > 1.0f) { /* energy超出1.0的部分作为额外d2推力 */ float extra_boost = (energy - 1.0f) * BOOST_DUTY_MAX; d2 += extra_boost; /* d2 = 前馈基准 + 额外推力 */ } else { d2 *= energy; /* energy≤1时等比缩小 */ } /* 同样处理Boost上限:d2到上限就保持,d1也保持(不再反向调小d1) */ if (d2 > BOOST_DUTY_MAX) { d2 = BOOST_DUTY_MAX; /* d2已到极限,d1保持当前值不变,系统已达最大推力 */ } /* 限幅保护 */ if (d1 > BUCK_DUTY_MAX) d1 = BUCK_DUTY_MAX; if (d1 < BUCK_DUTY_MIN) d1 = BUCK_DUTY_MIN; if (d2 > BOOST_DUTY_MAX) d2 = BOOST_DUTY_MAX; if (d2 < 0.0f) d2 = 0.0f; set_fsbb_buckboost_mode(d1, d2); break; case IDLE_CONVERT_MODE: default: set_fsbb_idle_mode(); break; } } float energy_shadow; /** * @brief 电流控制循环 (1kHz) * @details 运行电流PI控制器并输出占空比。 * 当 soft_start_active 为 true 时, * 参考电流从0线性斜坡上升到目标值, * 每步增量 = target / SOFT_START_STEPS */ /* 参考电流突变爬坡保护: * 启动固定步数斜坡从当前值线性过渡到目标值,与软启动逻辑一致 */ #define IREF_SLEW_THRESH_MA 200.0f /**< 突变检测阈值 200mA */ #define IREF_SLEW_STEPS 200 /**< 爬坡总步数 (200步 @1kHz = 200ms) */ void current_control_loop(void) { /* 保护状态非正常或非充电状态时直接返回,不执行电流控制 */ if (fsbb_status.protect_state != PROTECT_NORMAL || fsbb_status.charge_state == CHARGE_STATE_STANDY || fsbb_status.charge_state == CHARGE_STATE_START || fsbb_status.charge_state == CHARGE_STATE_COMPLETE) { return; } volatile float energy; volatile float i_ref = (float)g_powerStatus.charge_current_ref; static bool i_slew_active = false; static uint32_t i_slew_counter = 0; static float i_slew_start = 0.0f; static float i_slew_target = 0.0f; static float i_ref_prev = 0.0f; /**< 上一周期有效参考电流,用于突变检测 */ if (fabsf(i_ref - i_ref_prev) > IREF_SLEW_THRESH_MA) { /* 检测到突变,启动爬坡:记录起点、目标和步数 */ i_slew_active = true; i_slew_counter = 0; i_slew_start = i_ref_prev; i_slew_target = (float)g_powerStatus.charge_current_ref; } if (i_slew_active) { i_slew_counter++; if (i_slew_counter >= IREF_SLEW_STEPS) { /* 爬坡完成,直达目标 */ i_slew_active = false; i_ref = i_slew_target; } else { /* 线性插值:从起点到目标 */ i_ref = i_slew_start + (i_slew_target - i_slew_start) * (float)i_slew_counter / (float)IREF_SLEW_STEPS; } } /* 软启动:参考电流从0线性斜坡到目标 */ if (soft_start_active) { soft_start_counter++; if (soft_start_counter >= SOFT_START_STEPS) { soft_start_active = false; i_ref = (float)g_powerStatus.charge_current_ref; /* 软启动完成,直达目标 */ } else { i_ref = (float)g_powerStatus.charge_current_ref * (float)soft_start_counter / (float)SOFT_START_STEPS; } } i_ref_prev = i_ref; /* 记录本周期有效参考值,供下周期突变检测 */ energy_shadow = energy = CurrentPI_Run(¤tPI, i_ref, g_powerStatus.iout); FSBB_Duty_Calculate(energy); }