SOX Module Sources¶
sox.c¶
/**
*
* @copyright © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* 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.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file sox.c
* @author foxBMS Team
* @date 13.11.2015 (date of creation)
* @ingroup APPLICATION
* @prefix SOX
*
* @brief SOX module responsible for calculation of current derating and of SOC
*
*/
/*================== Includes =============================================*/
#include "sox.h"
#include "bms.h"
#include "database.h"
#include "batterycell_cfg.h"
#include "batterysystem_cfg.h"
#include "nvramhandler.h"
/*================== Macros and Definitions ===============================*/
/*================== Constant and Variable Definitions ====================*/
static SOX_STATE_s sox_state = {
.sensor_cc_used = 0,
.cc_scaling = 0.0,
.cc_scaling_min = 0.0,
.cc_scaling_max = 0.0,
.counter = 0,
};
static DATA_BLOCK_CURRENT_SENSOR_s sox_current_tab;
static DATA_BLOCK_MINMAX_s cellminmax;
static DATA_BLOCK_SOX_s sox;
static DATA_BLOCK_SOF_s sof;
static DATA_BLOCK_CONTFEEDBACK_s contfeedbacktab;
static uint32_t soc_previous_current_timestamp = 0;
static uint32_t soc_previous_current_timestamp_cc = 0;
/** @{
* module-local static Variables that are calculated at startup and used later to avoid divisions at runtime
*/
static SOF_curve_s sofCurveRecOperatingCurrent;
static SOF_curve_s sofCurve_MOL;
static SOF_curve_s sofCurve_RSL;
static SOF_curve_s sofCurve_MSL;
static SOX_SOF_s sof_recOperatingCurrent;
static SOX_SOF_s sof_mol_Level;
static SOX_SOF_s sof_rsl_Level;
static SOX_SOF_s sof_msl_Level;
/*================== Function Prototypes ==================================*/
static void SOF_CalculateCurves(const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues);
static void SOF_Calculate(int16_t maxtemp, int16_t mintemp, uint16_t maxvolt, uint16_t minvolt, uint16_t maxsoc, uint16_t minsoc);
static void SOF_CalculateVoltageBased(float MinVoltage, float MaxVoltage, SOX_SOF_s *ResultValues, const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues);
static void SOF_CalculateSocBased(float MinSoc, float MaxSoc, SOX_SOF_s *ResultValues, const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues);
static void SOF_CalculateTemperatureBased(float MinTemp, float MaxTemp, SOX_SOF_s *ResultValues, const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues);
static void SOF_MinimumOfThreeSofValues(SOX_SOF_s Ubased, SOX_SOF_s Sbased, SOX_SOF_s Tbased, SOX_SOF_s *resultValues);
static float SOF_MinimumOfThreeValues(float value1, float value2, float value3);
static float SOC_GetFromVoltage(uint16_t voltage_mV);
/*================== Function Implementations =============================*/
void SOC_Init(uint8_t cc_present) {
SOX_SOC_s soc = {50.0, 50.0, 50.0, 0, 0, 0, 0};
DB_ReadBlock(&sox_current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
NVM_getSOC(&soc);
if (cc_present == TRUE) {
soc_previous_current_timestamp_cc = sox_current_tab.timestamp_cc;
sox_state.sensor_cc_used = TRUE;
if (POSITIVE_DISCHARGE_CURRENT == TRUE) {
sox_state.cc_scaling = soc.mean + 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_min = soc.min + 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_max = soc.max + 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
} else {
sox_state.cc_scaling = soc.mean - 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_min = soc.min - 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_max = soc.max - 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
}
sox.soc_mean = soc.mean;
sox.soc_min = soc.min;
sox.soc_max = soc.max;
if (sox.soc_mean > 100.0f) { sox.soc_mean = 100.0; }
if (sox.soc_mean < 0.0f) { sox.soc_mean = 0.0; }
if (sox.soc_min > 100.0f) { sox.soc_min = 100.0; }
if (sox.soc_min < 0.0f) { sox.soc_min = 0.0; }
if (sox.soc_max > 100.0f) { sox.soc_max = 100.0; }
if (sox.soc_max < 0.0f) { sox.soc_max = 0.0; }
/* Alternatively, SOC can be initialized with {V,SOC} lookup table if available */
/* with the function SOC_Init_Lookup_Table() */
sox.state = 0;
sox.timestamp = 0;
sox.previous_timestamp = 0;
} else {
soc_previous_current_timestamp = sox_current_tab.timestamp_cur;
sox_state.sensor_cc_used = FALSE;
}
DB_WriteBlock(&sox, DATA_BLOCK_ID_SOX);
}
void SOC_SetValue(float soc_value_min, float soc_value_max, float soc_value_mean) {
SOX_SOC_s soc = {50.0, 50.0, 50.0, 0, 0, 0, 0};
if (soc_value_min < 0.0f) {
soc_value_min = 0.0;
}
if (soc_value_min > 100.0f) {
soc_value_min = 100.0;
}
if (soc_value_max < 0.0f) {
soc_value_max = 0.0;
}
if (soc_value_max > 100.0f) {
soc_value_max = 100.0;
}
if (soc_value_mean < 0.0f) {
soc_value_mean = 0.0;
}
if (soc_value_mean > 100.0f) {
soc_value_mean = 100.0;
}
if (sox_state.sensor_cc_used == FALSE) {
soc.mean = soc_value_mean;
soc.min = soc_value_min;
soc.max = soc_value_max;
sox.soc_mean = soc.mean;
sox.soc_min = soc.min;
sox.soc_max = soc.max;
} else {
DB_ReadBlock(&sox_current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
soc.mean = soc_value_mean;
soc.min = soc_value_min;
soc.max = soc_value_max;
if (POSITIVE_DISCHARGE_CURRENT == TRUE) {
sox_state.cc_scaling = soc.mean + 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_min = soc.min + 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_max = soc.max + 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
} else {
sox_state.cc_scaling = soc.mean - 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_min = soc.min - 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox_state.cc_scaling_max = soc.max - 100.0f*sox_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
}
sox.soc_mean = soc.mean;
sox.soc_min = soc.min;
sox.soc_max = soc.max;
if (sox.soc_mean > 100.0f) { sox.soc_mean = 100.0; }
if (sox.soc_mean < 0.0f) { sox.soc_mean = 0.0; }
if (sox.soc_min > 100.0f) { sox.soc_min = 100.0; }
if (sox.soc_min < 0.0f) { sox.soc_min = 0.0; }
if (sox.soc_max > 100.0f) { sox.soc_max = 100.0; }
if (sox.soc_max < 0.0f) { sox.soc_max = 0.0; }
}
NVM_setSOC(&soc);
DB_WriteBlock(&sox, DATA_BLOCK_ID_SOX);
}
void SOC_RecalibrateViaLookupTable(void) {
float soc_min = 50.0f;
float soc_max = 50.0f;
float soc_mean = 50.0f;
DB_ReadBlock(&cellminmax, DATA_BLOCK_ID_MINMAX);
soc_mean = SOC_GetFromVoltage((float)(cellminmax.voltage_mean));
soc_min = SOC_GetFromVoltage((float)(cellminmax.voltage_min));
soc_max = SOC_GetFromVoltage((float)(cellminmax.voltage_max));
SOC_SetValue(soc_min, soc_max, soc_mean);
}
void SOC_Calculation(void) {
uint32_t timestamp = 0;
uint32_t previous_timestamp = 0;
uint32_t timestamp_cc = 0;
uint32_t previous_timestamp_cc = 0;
uint32_t timestep = 0;
DATA_BLOCK_CURRENT_SENSOR_s cans_current_tab;
SOX_SOC_s soc = {50.0, 50.0, 50.0, 0, 0, 0, 0};
float deltaSOC = 0.0;
if (BMS_GetBatterySystemState() == BMS_AT_REST) {
/* Recalibrate SOC via LUT */
SOC_RecalibrateViaLookupTable();
} else {
/* Use coulomb/current counting */
if (sox_state.sensor_cc_used == FALSE) {
DB_ReadBlock(&sox_current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
timestamp = sox_current_tab.timestamp_cur;
previous_timestamp = sox_current_tab.previous_timestamp_cur;
if (soc_previous_current_timestamp != timestamp) { /* check if current measurement has been updated */
timestep = timestamp - previous_timestamp;
if (timestep > 0) {
NVM_getSOC(&soc);
/* Current in charge direction negative means SOC increasing --> BAT naming, not ROB */
/* soc_mean = soc_mean - (sox_current_tab.current *mA* /(float)SOX_CELL_CAPACITY (*mAh*)) * (float)(timestep) * (10.0/3600.0); */ /*milliseconds*/
if (POSITIVE_DISCHARGE_CURRENT == TRUE) {
deltaSOC = (((sox_current_tab.current)*(float)(timestep)/10))/(3600.0f * SOX_CELL_CAPACITY); /* ((mA *ms *(1s/1000ms)) / (3600(s/h) *mAh)) *100% */
} else {
deltaSOC = -(((sox_current_tab.current)*(float)(timestep)/10))/(3600.0f * SOX_CELL_CAPACITY); /* ((mA *ms *(1s/1000ms)) / (3600(s/h) *mAh)) *100% */
}
soc.mean = soc.mean - deltaSOC;
soc.min = soc.min - deltaSOC;
soc.max = soc.max - deltaSOC;
if (soc.mean > 100.0f) { soc.mean = 100.0; }
if (soc.mean < 0.0f) { soc.mean = 0.0; }
if (soc.min > 100.0f) { soc.min = 100.0; }
if (soc.min < 0.0f) { soc.min = 0.0; }
if (soc.max > 100.0f) { soc.max = 100.0; }
if (soc.max < 0.0f) { soc.max = 0.0; }
sox.soc_mean = soc.mean;
sox.soc_min = soc.min;
sox.soc_max = soc.max;
NVM_setSOC(&soc);
sox.state++;
DB_WriteBlock(&sox, DATA_BLOCK_ID_SOX);
}
} /* end check if current measurement has been updated */
/* update the variable for the next check */
soc_previous_current_timestamp = sox_current_tab.timestamp;
} else {
DB_ReadBlock(&sox_current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
timestamp_cc = sox_current_tab.timestamp_cc;
previous_timestamp_cc = sox_current_tab.previous_timestamp_cc;
if (previous_timestamp_cc != timestamp_cc) { /* check if cc measurement has been updated */
DB_ReadBlock(&cans_current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
if (POSITIVE_DISCHARGE_CURRENT == TRUE) {
sox.soc_mean = sox_state.cc_scaling - 100.0f*cans_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox.soc_min = sox_state.cc_scaling_min - 100.0f*cans_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox.soc_max = sox_state.cc_scaling_max - 100.0f*cans_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
} else {
sox.soc_mean = sox_state.cc_scaling + 100.0f*cans_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox.soc_min = sox_state.cc_scaling_min + 100.0f*cans_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
sox.soc_max = sox_state.cc_scaling_max + 100.0f*cans_current_tab.current_counter/(3600.0f*(SOX_CELL_CAPACITY/1000.0f));
}
soc.mean = sox.soc_mean;
soc.min = sox.soc_min;
soc.max = sox.soc_max;
if (sox.soc_mean > 100.0f) { sox.soc_mean = 100.0; }
if (sox.soc_mean < 0.0f) { sox.soc_mean = 0.0; }
if (sox.soc_min > 100.0f) { sox.soc_min = 100.0; }
if (sox.soc_min < 0.0f) { sox.soc_min = 0.0; }
if (sox.soc_max > 100.0f) { sox.soc_max = 100.0; }
if (sox.soc_max < 0.0f) { sox.soc_max = 0.0; }
NVM_setSOC(&soc);
sox.state++;
DB_WriteBlock(&sox, DATA_BLOCK_ID_SOX);
}
soc_previous_current_timestamp_cc = sox_current_tab.timestamp_cc;
}
}
}
void SOF_Init(void) {
/* Calculating SOF curve for the recommended operating current */
SOF_CalculateCurves(&sox_sof_config_maxAllowedCurrent, &sofCurveRecOperatingCurrent);
#if BMS_TEST_CELL_SOF_LIMITS == TRUE
/* Calculating SOF curve for maximum operating limit */
SOF_CalculateCurves(&sox_sof_config_MOL, &sofCurve_MOL);
/* Calculating SOF curve for recommended safety limit */
SOF_CalculateCurves(&sox_sof_config_RSL, &sofCurve_RSL);
/* Calculating SOF curve for maximum safety limit */
SOF_CalculateCurves(&sox_sof_config_MSL, &sofCurve_MSL);
#endif
}
static void SOF_CalculateCurves(const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues) {
/* Calculating SOF curve for the maximum allowed current for MOL/RSL/MSL */
calcCurveValues->Slope_TLowDischa = (configLimitValues->I_DischaMax_Cont - configLimitValues->I_Limphome) / (configLimitValues->Cutoff_TLow_Discha - configLimitValues->Limit_TLow_Discha);
calcCurveValues->Offset_TLowDischa = configLimitValues->I_Limphome - (calcCurveValues->Slope_TLowDischa * configLimitValues->Limit_TLow_Discha);
calcCurveValues->Slope_THighDischa = (0 - configLimitValues->I_DischaMax_Cont) / (configLimitValues->Limit_THigh_Discha - configLimitValues->Cutoff_THigh_Discha);
calcCurveValues->Offset_THighDischa = 0 - calcCurveValues->Slope_THighDischa * configLimitValues->Limit_THigh_Discha;
calcCurveValues->Slope_TLowCharge = (configLimitValues->I_ChargeMax_Cont - 0) / (configLimitValues->Cutoff_TLow_Charge - configLimitValues->Limit_TLow_Charge);
calcCurveValues->Offset_TLowCharge = 0 - (calcCurveValues->Slope_TLowCharge * configLimitValues->Limit_TLow_Charge);
calcCurveValues->Slope_THighCharge = (0 - configLimitValues->I_ChargeMax_Cont) / (configLimitValues->Limit_THigh_Charge - configLimitValues->Cutoff_THigh_Charge);
calcCurveValues->Offset_THighCharge = 0 - calcCurveValues->Slope_THighCharge * configLimitValues->Limit_THigh_Charge;
calcCurveValues->Slope_SocDischa = (configLimitValues->I_DischaMax_Cont - configLimitValues->I_Limphome) / (configLimitValues->Cutoff_Soc_Discha - configLimitValues->Limit_Soc_Discha);
calcCurveValues->Offset_SocDischa = configLimitValues->I_Limphome - (calcCurveValues->Slope_SocDischa * configLimitValues->Limit_Soc_Discha);
calcCurveValues->Slope_SocCharge = (configLimitValues->I_ChargeMax_Cont - 0.0f) / (configLimitValues->Cutoff_Soc_Charge - configLimitValues->Limit_Soc_Charge);
calcCurveValues->Offset_SocCharge = 0 - calcCurveValues->Slope_SocCharge * configLimitValues->Limit_Soc_Charge;
calcCurveValues->Slope_VoltageDischa = (configLimitValues->I_DischaMax_Cont - 0) / (configLimitValues->Cutoff_Voltage_Discha - configLimitValues->Limit_Voltage_Discha);
calcCurveValues->Offset_VoltageDischa = 0 - calcCurveValues->Slope_SocDischa * configLimitValues->Limit_Soc_Discha;
calcCurveValues->Slope_VoltageCharge = (configLimitValues->I_ChargeMax_Cont - 0) / (configLimitValues->Cutoff_Voltage_Charge - configLimitValues->Limit_Voltage_Charge);
calcCurveValues->Offset_VoltageCharge = 0 - calcCurveValues->Slope_VoltageCharge * configLimitValues->Limit_Soc_Discha;
}
void SOF_Calculation(void) {
DB_ReadBlock(&cellminmax, DATA_BLOCK_ID_MINMAX);
DB_ReadBlock(&sox, DATA_BLOCK_ID_SOX);
DB_ReadBlock(&sof, DATA_BLOCK_ID_SOF);
DB_ReadBlock(&contfeedbacktab, DATA_BLOCK_ID_CONTFEEDBACK);
/* Calculate SOF limits */
SOF_Calculate(cellminmax.temperature_max, cellminmax.temperature_min, cellminmax.voltage_max, cellminmax.voltage_min, (uint16_t)(100.0f*sox.soc_max), (uint16_t)(100.0f*sox.soc_min));
/* Write MOL level */
sof.continuous_charge_MOL = sof_mol_Level.current_Charge_cont_max;
sof.continuous_discharge_MOL = sof_mol_Level.current_Discha_cont_max;
/* Write RSL level */
sof.continuous_charge_RSL = sof_rsl_Level.current_Charge_cont_max;
sof.continuous_discharge_RSL = sof_rsl_Level.current_Discha_cont_max;
/* Write MSL level */
sof.continuous_charge_MSL = sof_msl_Level.current_Charge_cont_max;
sof.continuous_discharge_MSL = sof_msl_Level.current_Discha_cont_max;
/* if Contactor MainPlus and MainMinus are not closed (when they are closed, state_feedback is 0x0C) */
#if BS_SEPARATE_POWERLINES == 1
if (!((contfeedbacktab.contactor_feedback & 0x3F) == 0x28 || (contfeedbacktab.contactor_feedback & 0x3F) == 0x05)) {
#else
if ((contfeedbacktab.contactor_feedback & 0x3F) != 0x05) {
#endif /* BS_SEPARATE_POWERLINES == 1 */
sof.recommended_continuous_charge = 0.0;
sof.recommended_continuous_discharge = 0.0;
sof.recommended_peak_charge = 0.0;
sof.recommended_peak_discharge = 0.0;
} else {
sof.recommended_continuous_charge = sof_recOperatingCurrent.current_Charge_cont_max;
sof.recommended_continuous_discharge = sof_recOperatingCurrent.current_Discha_cont_max;
sof.recommended_peak_charge = sof_recOperatingCurrent.current_Charge_peak_max;
sof.recommended_peak_discharge = sof_recOperatingCurrent.current_Discha_peak_max;
}
DB_WriteBlock(&sof, DATA_BLOCK_ID_SOF);
}
/**
* @brief look-up table for SOC initialization (mean, min and max).
*
* @param voltage: voltage of battery cell
*
* @return SOC value from 0.00% - 100.0%
*/
float SOC_GetFromVoltage(uint16_t voltage_mV) {
float SOC = 50.0f;
return SOC;
}
/**
* @brief calculates State of function which means how much current can be delivered by battery to stay in safe operating area.
*
* @param maxtemp maximum temperature in system in �C
* @param mintemp minimum temperature in system in �C
* @param maxvolt maximum voltage in system in mV
* @param minvolt minimum voltage in system in mV
* @param maxsoc maximum soc in system with resolution 0.01% (0..10000)
* @param minsoc minimum soc in system with resolution 0.01% (0..10000)
*/
static void SOF_Calculate(int16_t maxtemp, int16_t mintemp, uint16_t maxvolt, uint16_t minvolt, uint16_t maxsoc, uint16_t minsoc) {
SOX_SOF_s UbasedSof = {0.0, 0.0, 0.0, 0.0};
SOX_SOF_s SbasedSof = {0.0, 0.0, 0.0, 0.0};
SOX_SOF_s TbasedSof = {0.0, 0.0, 0.0, 0.0};
/* Calculate maximum allowed current depending on current values */
SOF_CalculateVoltageBased((float)minvolt, (float)maxvolt, &UbasedSof, &sox_sof_config_maxAllowedCurrent, &sofCurveRecOperatingCurrent);
SOF_CalculateSocBased((float)minsoc, (float)maxsoc, &SbasedSof, &sox_sof_config_maxAllowedCurrent, &sofCurveRecOperatingCurrent);
SOF_CalculateTemperatureBased((float)mintemp, (float)maxtemp, &TbasedSof, &sox_sof_config_maxAllowedCurrent, &sofCurveRecOperatingCurrent);
SOF_MinimumOfThreeSofValues(UbasedSof, SbasedSof, TbasedSof, &sof_recOperatingCurrent);
#if BMS_TEST_CELL_SOF_LIMITS == TRUE
/* Calculate maximum allowed current MOL level */
SOF_CalculateVoltageBased((float)minvolt, (float)maxvolt, &UbasedSof, &sox_sof_config_MOL, &sofCurve_MOL);
SOF_CalculateSocBased((float)minsoc, (float)maxsoc, &SbasedSof, &sox_sof_config_MOL, &sofCurve_MOL);
SOF_CalculateTemperatureBased((float)mintemp, (float)maxtemp, &TbasedSof, &sox_sof_config_MOL, &sofCurve_MOL);
SOF_MinimumOfThreeSofValues(UbasedSof, SbasedSof, TbasedSof, &sof_mol_Level);
/* Calculate maximum allowed current RSL level */
SOF_CalculateVoltageBased((float)minvolt, (float)maxvolt, &UbasedSof, &sox_sof_config_RSL, &sofCurve_RSL);
SOF_CalculateSocBased((float)minsoc, (float)maxsoc, &SbasedSof, &sox_sof_config_RSL, &sofCurve_RSL);
SOF_CalculateTemperatureBased((float)mintemp, (float)maxtemp, &TbasedSof, &sox_sof_config_RSL, &sofCurve_RSL);
SOF_MinimumOfThreeSofValues(UbasedSof, SbasedSof, TbasedSof, &sof_rsl_Level);
/* Calculate maximum allowed current MSL level */
SOF_CalculateVoltageBased((float)minvolt, (float)maxvolt, &UbasedSof, &sox_sof_config_MSL, &sofCurve_MSL);
SOF_CalculateSocBased((float)minsoc, (float)maxsoc, &SbasedSof, &sox_sof_config_MSL, &sofCurve_MSL);
SOF_CalculateTemperatureBased((float)mintemp, (float)maxtemp, &TbasedSof, &sox_sof_config_MSL, &sofCurve_MSL);
SOF_MinimumOfThreeSofValues(UbasedSof, SbasedSof, TbasedSof, &sof_msl_Level);
#else
sof_mol_Level.current_Charge_cont_max = BC_CURRENTMAX_CHARGE_MOL;
sof_mol_Level.current_Discha_cont_max = BC_CURRENTMAX_DISCHARGE_MOL;
sof_rsl_Level.current_Charge_cont_max = BC_CURRENTMAX_CHARGE_MOL;
sof_rsl_Level.current_Discha_cont_max = BC_CURRENTMAX_DISCHARGE_MOL;
sof_msl_Level.current_Charge_cont_max = BC_CURRENTMAX_CHARGE_MOL;
sof_msl_Level.current_Discha_cont_max = BC_CURRENTMAX_DISCHARGE_MOL;
#endif
}
/**
* @brief calculates the SoF from voltage data (i.e., minimum and maximum voltage)
*
* @param MinVoltage minimum sell voltage
* @param MaxVoltage maximum cell voltage
* @param ResultValues Voltage-based SOF
*/
static void SOF_CalculateVoltageBased(float MinVoltage, float MaxVoltage, SOX_SOF_s *ResultValues, const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues) {
/* min voltage issues */
if (MinVoltage <= configLimitValues->Limit_Voltage_Discha) {
ResultValues->current_Discha_cont_max = 0;
ResultValues->current_Discha_peak_max = 0;
} else {
if (MinVoltage <= configLimitValues->Cutoff_Voltage_Discha) {
ResultValues->current_Discha_cont_max = (calcCurveValues->Slope_VoltageDischa * (MinVoltage-configLimitValues->Limit_Voltage_Discha));
ResultValues->current_Discha_peak_max = ResultValues->current_Discha_cont_max;
} else {
ResultValues->current_Discha_cont_max = configLimitValues->I_DischaMax_Cont;
ResultValues->current_Discha_peak_max = configLimitValues->I_DischaMax_Cont;
}
}
/* max voltage issues */
if (MaxVoltage >= configLimitValues->Limit_Voltage_Charge) {
ResultValues->current_Charge_cont_max = 0;
ResultValues->current_Charge_peak_max = 0;
} else {
if (MaxVoltage >= configLimitValues->Cutoff_Voltage_Charge) {
ResultValues->current_Charge_cont_max = (calcCurveValues->Slope_VoltageCharge * (MaxVoltage- configLimitValues->Limit_Voltage_Charge));
ResultValues->current_Charge_peak_max = ResultValues->current_Charge_cont_max;
} else {
ResultValues->current_Charge_cont_max = configLimitValues->I_ChargeMax_Cont;
ResultValues->current_Charge_peak_max = configLimitValues->I_ChargeMax_Cont;
}
}
}
/**
* @brief calculates the SoF from SoC data (i.e., minimum and maximum SoC of cells, both values are equal in case of Ah-counter)
*
* @param MinSoc minimum State of Charge
* @param MaxSoc maximum State of Charge
* @param ResultValues pointer where to store the results
*/
static void SOF_CalculateSocBased(float MinSoc, float MaxSoc, SOX_SOF_s *ResultValues, const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues) {
/* min voltage issues */
if (MinSoc <= configLimitValues->Limit_Soc_Discha) {
ResultValues->current_Discha_cont_max = configLimitValues->I_Limphome;
ResultValues->current_Discha_peak_max = configLimitValues->I_Limphome;
} else {
if (MinSoc <= configLimitValues->Cutoff_Soc_Discha) {
ResultValues->current_Discha_cont_max = calcCurveValues->Slope_SocDischa * MinSoc + calcCurveValues->Offset_SocDischa;
ResultValues->current_Discha_peak_max = ResultValues->current_Discha_cont_max;
} else {
ResultValues->current_Discha_cont_max = configLimitValues->I_DischaMax_Cont;
ResultValues->current_Discha_peak_max = configLimitValues->I_DischaMax_Cont;
}
}
/* max voltage issues */
if (MaxSoc >= configLimitValues->Limit_Soc_Charge) {
ResultValues->current_Charge_cont_max = 0;
ResultValues->current_Charge_peak_max = 0;
} else {
if (MaxSoc >= configLimitValues->Cutoff_Soc_Charge) {
ResultValues->current_Charge_cont_max = calcCurveValues->Slope_SocCharge * MaxSoc + calcCurveValues->Offset_SocCharge;
ResultValues->current_Charge_peak_max = ResultValues->current_Charge_cont_max;
} else {
ResultValues->current_Charge_cont_max = configLimitValues->I_ChargeMax_Cont;
ResultValues->current_Charge_peak_max = configLimitValues->I_ChargeMax_Cont;
}
}
}
/**
* @brief calculates the SoF from temperature data (i.e., minimum and maximum temperature of cells)
*
* @param MinTemp minimum temperature of cells
* @param MaxTemp maximum temperature of cells
* @param ResultValues pointer where to store the results
*/
static void SOF_CalculateTemperatureBased(float MinTemp, float MaxTemp, SOX_SOF_s *ResultValues, const SOX_SOF_CONFIG_s *configLimitValues, SOF_curve_s* calcCurveValues) {
SOX_SOF_s dummyResultValues = {0.0, 0.0, 0.0, 0.0};
/* Temperature low Discharge */
if (MinTemp <= configLimitValues->Limit_TLow_Discha) {
ResultValues->current_Discha_cont_max = configLimitValues->I_Limphome;
ResultValues->current_Discha_peak_max = configLimitValues->I_Limphome;
} else {
if (MinTemp <= configLimitValues->Cutoff_TLow_Discha) {
ResultValues->current_Discha_cont_max = calcCurveValues->Slope_TLowDischa * MinTemp + calcCurveValues->Offset_TLowDischa;
ResultValues->current_Discha_peak_max = ResultValues->current_Discha_cont_max;
} else {
ResultValues->current_Discha_cont_max = configLimitValues->I_DischaMax_Cont;
ResultValues->current_Discha_peak_max = configLimitValues->I_DischaMax_Cont;
}
}
/* Temperature low charge */
if (MinTemp <= configLimitValues->Limit_TLow_Charge) {
ResultValues->current_Charge_cont_max = 0;
ResultValues->current_Charge_peak_max = 0;
} else {
if (MinTemp <= configLimitValues->Cutoff_TLow_Charge) {
ResultValues->current_Charge_cont_max = calcCurveValues->Slope_TLowCharge * MinTemp + calcCurveValues->Offset_TLowCharge;
ResultValues->current_Charge_peak_max = ResultValues->current_Charge_cont_max;
} else {
ResultValues->current_Charge_cont_max = configLimitValues->I_ChargeMax_Cont;
ResultValues->current_Charge_peak_max = configLimitValues->I_ChargeMax_Cont;
}
}
/* Temperature high discharge */
if (MaxTemp >= configLimitValues->Limit_THigh_Discha) {
ResultValues->current_Discha_cont_max = 0;
ResultValues->current_Discha_peak_max = 0;
dummyResultValues.current_Discha_cont_max = 0;
dummyResultValues.current_Discha_peak_max = 0;
} else {
if (MaxTemp >= configLimitValues->Cutoff_THigh_Discha) {
dummyResultValues.current_Discha_cont_max = (calcCurveValues->Slope_THighDischa * MaxTemp + calcCurveValues->Offset_THighDischa);
dummyResultValues.current_Discha_peak_max = dummyResultValues.current_Discha_cont_max;
} else {
/* do nothing because this situation is handled with MinTemp */
dummyResultValues.current_Discha_cont_max = configLimitValues->I_DischaMax_Cont;
dummyResultValues.current_Discha_peak_max = configLimitValues->I_DischaMax_Cont;
}
/* take the smaller current as limit */
if (dummyResultValues.current_Discha_cont_max < ResultValues->current_Discha_cont_max)
ResultValues->current_Discha_cont_max = dummyResultValues.current_Discha_cont_max;
if (dummyResultValues.current_Discha_peak_max < ResultValues->current_Discha_peak_max)
ResultValues->current_Discha_peak_max = dummyResultValues.current_Discha_peak_max;
}
/* Temperature high Charge */
if (MaxTemp >= configLimitValues->Limit_THigh_Charge) {
ResultValues->current_Charge_cont_max = 0;
ResultValues->current_Charge_peak_max = 0;
dummyResultValues.current_Charge_cont_max = 0;
dummyResultValues.current_Charge_peak_max = 0;
} else {
if (MaxTemp >= configLimitValues->Cutoff_THigh_Charge) {
dummyResultValues.current_Charge_cont_max = (calcCurveValues->Slope_THighCharge * MaxTemp + calcCurveValues->Offset_THighCharge);
dummyResultValues.current_Charge_peak_max = dummyResultValues.current_Charge_cont_max;
} else {
/* do nothing because this situation is handled with MinTemp */
dummyResultValues.current_Charge_cont_max = configLimitValues->I_ChargeMax_Cont;
dummyResultValues.current_Charge_peak_max = configLimitValues->I_ChargeMax_Cont;
}
/* take the smaller current as limit */
if (dummyResultValues.current_Charge_cont_max < ResultValues->current_Charge_cont_max)
ResultValues->current_Charge_cont_max = dummyResultValues.current_Charge_cont_max;
if (dummyResultValues.current_Charge_peak_max < ResultValues->current_Charge_peak_max)
ResultValues->current_Charge_peak_max = dummyResultValues.current_Charge_peak_max;
}
}
/**
* @brief get the minimum current values of all variants of SoF calculation
*
* @param Ubased voltage constrained current derating values
* @param Sbased SOC constrained current derating values
* @param Tbased temperature constrained current derating values
* @param resultValues minimum SoF current values
*/
static void SOF_MinimumOfThreeSofValues(SOX_SOF_s Ubased, SOX_SOF_s Sbased, SOX_SOF_s Tbased, SOX_SOF_s *resultValues) {
resultValues->current_Charge_cont_max = SOF_MinimumOfThreeValues(Ubased.current_Charge_cont_max, Tbased.current_Charge_cont_max, Sbased.current_Charge_cont_max);
resultValues->current_Charge_peak_max = SOF_MinimumOfThreeValues(Ubased.current_Charge_peak_max, Tbased.current_Charge_peak_max, Sbased.current_Charge_peak_max);
resultValues->current_Discha_cont_max = SOF_MinimumOfThreeValues(Ubased.current_Discha_cont_max, Tbased.current_Discha_cont_max, Sbased.current_Discha_cont_max);
resultValues->current_Discha_peak_max = SOF_MinimumOfThreeValues(Ubased.current_Discha_peak_max, Tbased.current_Discha_peak_max, Sbased.current_Discha_peak_max);
}
/**
* @brief calculates minimum of three values
*
* @param value1
* @param value2
* @param value3
*
* @return minimum of the 3 parameters
*/
static float SOF_MinimumOfThreeValues(float value1, float value2, float value3) {
float result = 0.0;
if (value1 <= value2) {
if (value3 <= value1) {
result = value3;
} else {
result = value1;
}
} else {
if (value3 <= value2) {
result = value3;
} else {
result = value2;
}
}
return result;
}
sox.h¶
/**
*
* @copyright © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* 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.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file sox.h
* @author foxBMS Team
* @date 13.11.2015 (date of creation)
* @ingroup APPLICATION
* @prefix SOX
*
* @brief Header for SOX module, responsible for calculation of current derating and of SOC
*
*/
#ifndef SOX_H_
#define SOX_H_
/*================== Includes =============================================*/
#include "sox_cfg.h"
/*================== Macros and Definitions ===============================*/
/**
* This structure contains all the variables relevant for the SOX.
*
*/
typedef struct {
uint8_t sensor_cc_used; /*!< time in ms before the state machine processes the next state, e.g. in counts of 1ms */
float cc_scaling; /*!< scaling for the C-C value from sensor for average value */
float cc_scaling_min; /*!< scaling for the C-C value from sensor for min value */
float cc_scaling_max; /*!< scaling for the C-C value from sensor for max value */
uint8_t counter; /*!< general purpose counter */
} SOX_STATE_s;
/**
* struct definition for 4 different values: in two current directions (charge, discharge) for two use cases (peak and continuous)
*/
typedef struct {
float current_Charge_cont_max; /*!< maximum current for continues charging */
float current_Charge_peak_max; /*!< maximum current for peak charging */
float current_Discha_cont_max; /*!< maximum current for continues discharging */
float current_Discha_peak_max; /*!< maximum current for peak discharging */
} SOX_SOF_s;
/**
* state of charge (SOC). Since SOC is voltage dependent, three different values are used, min, max and mean
* SOC defined as a float number between 0.0 and 100.0 (0% and 100%)
*/
typedef struct {
float min; /*!< minimum SOC */
float max; /*!< maximum SOC */
float mean; /*!< mean SOC */
float reserved1;/*!< reserved for future use */
float reserved2;/*!< reserved for future use */
float reserved3;/*!< reserved for future use */
float reserved4;/*!< reserved for future use */
} SOX_SOC_s;
/**
* struct definition for calculating the linear SOF curve. The SOF curve is SOC,
* voltage, temperature and charge/discharge dependent.
*/
typedef struct {
float Slope_TLowDischa;
float Offset_TLowDischa;
float Slope_THighDischa;
float Offset_THighDischa;
float Slope_TLowCharge;
float Offset_TLowCharge;
float Slope_THighCharge;
float Offset_THighCharge;
float Slope_SocDischa;
float Offset_SocDischa;
float Slope_SocCharge;
float Offset_SocCharge;
float Slope_VoltageDischa;
float Offset_VoltageDischa;
float Slope_VoltageCharge;
float Offset_VoltageCharge;
}SOF_curve_s;
/*================== Constant and Variable Definitions ====================*/
/*================== Function Prototypes ==================================*/
/**
* @brief initializes startup SOC-related values like lookup from nonvolatile ram at startup
*/
extern void SOC_Init(uint8_t cc_present);
/**
* @brief initializes the area for SOF (where derating starts and is fully active).
*
* Pseudocode for linear function parameter extraction with 2 points
* slope = (y2 - y1) / (x2-x1)
* offset = y1 - (slope) * x1
* function y= slope * x + offset
*/
extern void SOF_Init(void);
/**
* @brief sets SOC value with a parameter between 0.0 and 100.0.
*
* @param soc_value_min SOC min value to set
* @param soc_value_max SOC maxn value to set
* @param soc_value_mean SOC mean value to set
*/
extern void SOC_SetValue(float soc_value_min, float soc_value_max, float soc_value_mean);
/**
* @brief initializes the SOC values with lookup table (mean, min and max).
*/
extern void SOC_RecalibrateViaLookupTable(void);
/**
* @brief integrates current over time to calculate SOC.
*/
extern void SOC_Calculation(void);
/**
* @brief triggers SOF calculation
*
* Calculation made with the function SOF_Calculate().
*/
extern void SOF_Calculation(void);
/*================== Function Implementations =============================*/
#endif /* SOX_H_ */
sox_cfg.c¶
/**
*
* @copyright © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* 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.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file sox_cfg.c
* @author foxBMS Team
* @date 16.11.2015 (date of creation)
* @ingroup APPLICATION_CONF
* @prefix SOX
*
* @brief Configuration for SOX
*
*/
/*================== Includes =============================================*/
#include "sox_cfg.h"
/*================== Macros and Definitions ===============================*/
/*================== Constant and Variable Definitions ====================*/
const SOX_SOF_CONFIG_s sox_sof_config_maxAllowedCurrent = {
.I_ChargeMax_Cont = SOX_CURRENT_MAX_CONTINUOUS_CHARGE,
.I_DischaMax_Cont = SOX_CURRENT_MAX_CONTINUOUS_DISCHARGE,
.I_Limphome = SOX_CURRENT_LIMP_HOME,
.Cutoff_TLow_Discha = SOX_TEMP_LOW_CUTOFF_DISCHARGE,
.Limit_TLow_Discha = SOX_TEMP_LOW_LIMIT_DISCHARGE,
.Cutoff_TLow_Charge = SOX_TEMP_LOW_CUTOFF_CHARGE,
.Limit_TLow_Charge = SOX_TEMP_LOW_LIMIT_CHARGE,
.Cutoff_THigh_Discha = SOX_TEMP_HIGH_CUTOFF_DISCHARGE,
.Limit_THigh_Discha = SOX_TEMP_HIGH_LIMIT_DISCHARGE,
.Cutoff_THigh_Charge = SOX_TEMP_HIGH_CUTOFF_CHARGE,
.Limit_THigh_Charge = SOX_TEMP_HIGH_LIMIT_CHARGE,
.Limit_Soc_Charge = SOX_SOC_LIMIT_CHARGE,
.Cutoff_Soc_Charge = SOX_SOC_CUTOFF_CHARGE,
.Limit_Soc_Discha = SOX_SOC_LIMIT_DISCHARGE,
.Cutoff_Soc_Discha = SOX_SOC_CUTOFF_DISCHARGE,
.Limit_Voltage_Charge = SOX_VOLT_LIMIT_CHARGE,
.Cutoff_Voltage_Charge = SOX_VOLT_CUTOFF_CHARGE,
.Limit_Voltage_Discha = SOX_VOLT_LIMIT_DISCHARGE,
.Cutoff_Voltage_Discha = SOX_VOLT_CUTOFF_DISCHARGE
};
const SOX_SOF_CONFIG_s sox_sof_config_MOL = {
.I_ChargeMax_Cont = SOX_MOL_CURRENT_MAX_CONTINUOUS_CHARGE,
.I_DischaMax_Cont = SOX_MOL_CURRENT_MAX_CONTINUOUS_DISCHARGE,
.I_Limphome = SOX_MOL_CURRENT_LIMP_HOME,
.Cutoff_TLow_Discha = SOX_MOL_TEMP_LOW_CUTOFF_DISCHARGE,
.Limit_TLow_Discha = SOX_MOL_TEMP_LOW_LIMIT_DISCHARGE,
.Cutoff_TLow_Charge = SOX_MOL_TEMP_LOW_CUTOFF_CHARGE,
.Limit_TLow_Charge = SOX_MOL_TEMP_LOW_LIMIT_CHARGE,
.Cutoff_THigh_Discha = SOX_MOL_TEMP_HIGH_CUTOFF_DISCHARGE,
.Limit_THigh_Discha = SOX_MOL_TEMP_HIGH_LIMIT_DISCHARGE,
.Cutoff_THigh_Charge = SOX_MOL_TEMP_HIGH_CUTOFF_CHARGE,
.Limit_THigh_Charge = SOX_MOL_TEMP_HIGH_LIMIT_CHARGE,
.Limit_Soc_Charge = SOX_MOL_SOC_LIMIT_CHARGE,
.Cutoff_Soc_Charge = SOX_MOL_SOC_CUTOFF_CHARGE,
.Limit_Soc_Discha = SOX_MOL_SOC_LIMIT_DISCHARGE,
.Cutoff_Soc_Discha = SOX_MOL_SOC_CUTOFF_DISCHARGE,
.Limit_Voltage_Charge = SOX_MOL_VOLT_LIMIT_CHARGE,
.Cutoff_Voltage_Charge = SOX_MOL_VOLT_CUTOFF_CHARGE,
.Limit_Voltage_Discha = SOX_MOL_VOLT_LIMIT_DISCHARGE,
.Cutoff_Voltage_Discha = SOX_MOL_VOLT_CUTOFF_DISCHARGE
};
const SOX_SOF_CONFIG_s sox_sof_config_RSL = {
.I_ChargeMax_Cont = SOX_RSL_CURRENT_MAX_CONTINUOUS_CHARGE,
.I_DischaMax_Cont = SOX_RSL_CURRENT_MAX_CONTINUOUS_DISCHARGE,
.I_Limphome = SOX_RSL_CURRENT_LIMP_HOME,
.Cutoff_TLow_Discha = SOX_RSL_TEMP_LOW_CUTOFF_DISCHARGE,
.Limit_TLow_Discha = SOX_RSL_TEMP_LOW_LIMIT_DISCHARGE,
.Cutoff_TLow_Charge = SOX_RSL_TEMP_LOW_CUTOFF_CHARGE,
.Limit_TLow_Charge = SOX_RSL_TEMP_LOW_LIMIT_CHARGE,
.Cutoff_THigh_Discha = SOX_RSL_TEMP_HIGH_CUTOFF_DISCHARGE,
.Limit_THigh_Discha = SOX_RSL_TEMP_HIGH_LIMIT_DISCHARGE,
.Cutoff_THigh_Charge = SOX_RSL_TEMP_HIGH_CUTOFF_CHARGE,
.Limit_THigh_Charge = SOX_RSL_TEMP_HIGH_LIMIT_CHARGE,
.Limit_Soc_Charge = SOX_RSL_SOC_LIMIT_CHARGE,
.Cutoff_Soc_Charge = SOX_RSL_SOC_CUTOFF_CHARGE,
.Limit_Soc_Discha = SOX_RSL_SOC_LIMIT_DISCHARGE,
.Cutoff_Soc_Discha = SOX_RSL_SOC_CUTOFF_DISCHARGE,
.Limit_Voltage_Charge = SOX_RSL_VOLT_LIMIT_CHARGE,
.Cutoff_Voltage_Charge = SOX_RSL_VOLT_CUTOFF_CHARGE,
.Limit_Voltage_Discha = SOX_RSL_VOLT_LIMIT_DISCHARGE,
.Cutoff_Voltage_Discha = SOX_RSL_VOLT_CUTOFF_DISCHARGE
};
const SOX_SOF_CONFIG_s sox_sof_config_MSL = {
.I_ChargeMax_Cont = SOX_MSL_CURRENT_MAX_CONTINUOUS_CHARGE,
.I_DischaMax_Cont = SOX_MSL_CURRENT_MAX_CONTINUOUS_DISCHARGE,
.I_Limphome = SOX_MSL_CURRENT_LIMP_HOME,
.Cutoff_TLow_Discha = SOX_MSL_TEMP_LOW_CUTOFF_DISCHARGE,
.Limit_TLow_Discha = SOX_MSL_TEMP_LOW_LIMIT_DISCHARGE,
.Cutoff_TLow_Charge = SOX_MSL_TEMP_LOW_CUTOFF_CHARGE,
.Limit_TLow_Charge = SOX_MSL_TEMP_LOW_LIMIT_CHARGE,
.Cutoff_THigh_Discha = SOX_MSL_TEMP_HIGH_CUTOFF_DISCHARGE,
.Limit_THigh_Discha = SOX_MSL_TEMP_HIGH_LIMIT_DISCHARGE,
.Cutoff_THigh_Charge = SOX_MSL_TEMP_HIGH_CUTOFF_CHARGE,
.Limit_THigh_Charge = SOX_MSL_TEMP_HIGH_LIMIT_CHARGE,
.Limit_Soc_Charge = SOX_MSL_SOC_LIMIT_CHARGE,
.Cutoff_Soc_Charge = SOX_MSL_SOC_CUTOFF_CHARGE,
.Limit_Soc_Discha = SOX_MSL_SOC_LIMIT_DISCHARGE,
.Cutoff_Soc_Discha = SOX_MSL_SOC_CUTOFF_DISCHARGE,
.Limit_Voltage_Charge = SOX_MSL_VOLT_LIMIT_CHARGE,
.Cutoff_Voltage_Charge = SOX_MSL_VOLT_CUTOFF_CHARGE,
.Limit_Voltage_Discha = SOX_MSL_VOLT_LIMIT_DISCHARGE,
.Cutoff_Voltage_Discha = SOX_MSL_VOLT_CUTOFF_DISCHARGE
};
/*================== Function Prototypes ==================================*/
/*================== Function Implementations =============================*/
sox_cfg.h¶
/**
*
* @copyright © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* 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.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file sox_cfg.h
* @author foxBMS Team
* @date 18.08.2015 (date of creation)
* @ingroup APPLICATION_CONF
* @prefix SOX
*
* @brief Configuration header for SOX
*
*/
#ifndef SOX_CFG_H_
#define SOX_CFG_H_
/*================== Includes =============================================*/
#include "general.h"
/*================== Macros and Definitions ===============================*/
/**
* @ingroup CONFIG_SOX
* when initializing SOC from SOC-Voltage lookup table, current must be below
* this value in order to ensure low disturbance and equilibrium in chemical
* reactions (relaxation effects)
* \par Type:
* int
* \par Unit:
* mA
* \par Default:
* 100
*/
#define SOX_SOC_INIT_CURRENT_LIMIT 100
/**
* @ingroup CONFIG_SOX
* when initializing SOC from SOC-Voltage lookup table, the difference between
* two consecutive voltage measurements of the cell with minimum voltage must be below
* this value in order to ensure low disturbance and equilibrium in chemical
* reactions (relaxation effects)
* \par Type:
* int
* \par Unit:
* mV
* \par Default:
* 10
*/
#define SOX_DELTA_MIN_LIMIT 10
/**
* @ingroup CONFIG_SOX
* when initializing SOC from SOC-Voltage lookup table, the difference between
* two consecutive voltage measurements of the cell with maximum voltage must be below
* this value in order to ensure low disturbance and equilibrium in chemical
* reactions (relaxation effects)
* \par Type:
* int
* \par Unit:
* mV
* \par Default:
* 10
*/
#define SOX_DELTA_MAX_LIMIT 10
/**
* @ingroup CONFIG_SOX
* the cell capacity used for SOC calculation, in this case Ah counting
* Specified once according to data sheet of cell usually.
* \par Type:
* float
* \par Unit:
* mAh
* \par Default:
* 20000.0
*/
#define SOX_CELL_CAPACITY 20000.0f
/**
* @ingroup CONFIG_SOX
* the maximum current in charge direction that the battery pack can sustain.
* Normally set once for the specific battery cell from datasheet
* \par Type:
* float
* \par Unit:
* A
* \par Default:
* 120.0
*/
#define SOX_CURRENT_MAX_CONTINUOUS_CHARGE 120.00f
/**
* Different alarm levels for the maximum continuous charge current.
*/
#define SOX_MOL_CURRENT_MAX_CONTINUOUS_CHARGE 100.00f
#define SOX_RSL_CURRENT_MAX_CONTINUOUS_CHARGE 115.00f
#define SOX_MSL_CURRENT_MAX_CONTINUOUS_CHARGE 122.00f
/**
* @ingroup CONFIG_SOX
* the maximum current in discharge direction that the battery pack can deliver.
* Normally set once for the specific battery cell from datasheet
*
* \par Type:
* float
* \par Unit:
* A
* \par Default:
* 120.0
*/
#define SOX_CURRENT_MAX_CONTINUOUS_DISCHARGE 120.00f
/**
* Different alarm levels for the maximum continuous discharge current.
*/
#define SOX_MOL_CURRENT_MAX_CONTINUOUS_DISCHARGE 100.00f
#define SOX_RSL_CURRENT_MAX_CONTINUOUS_DISCHARGE 115.00f
#define SOX_MSL_CURRENT_MAX_CONTINUOUS_DISCHARGE 122.00f
/**
* @ingroup CONFIG_SOX
* the current that the battery pack should be able to discharge when in LIMPHOME mode,
* i.e., something noncritical went wrong but it should be able to drive home.
* The value is system engineer's choice.
*
* \par Type:
* float
* \par Unit:
* A
* \par Range:
* [1,40]
* \par Default:
* 20.0
*/
#define SOX_CURRENT_LIMP_HOME 20.00f
/**
* Different alarm levels for the limp home current
*/
#define SOX_MOL_CURRENT_LIMP_HOME 20.00f
#define SOX_RSL_CURRENT_LIMP_HOME 20.00f
#define SOX_MSL_CURRENT_LIMP_HOME 20.00f
/**
* @ingroup CONFIG_SOX
* the cold temperature where the derating of maximum discharge current starts,
* i.e., below this temperature battery pack should not deliver full discharge current
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* 0.0
*/
#define SOX_TEMP_LOW_CUTOFF_DISCHARGE 0.0f
/**
* Different alarm levels for the cold temperature where the derating of
* maximum discharge current starts, i.e., below this temperature battery pack
* should not deliver full discharge current
*/
#define SOX_MOL_TEMP_LOW_CUTOFF_DISCHARGE 0.0f
#define SOX_RSL_TEMP_LOW_CUTOFF_DISCHARGE 0.0f
#define SOX_MSL_TEMP_LOW_CUTOFF_DISCHARGE 0.0f
/**
* @ingroup CONFIG_SOX
* the cold temperature where the derating of maximum discharge current is fully applied,
* i.e., below this temperature battery pack should not deliver any current in discharge direction
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* -10.0
*/
#define SOX_TEMP_LOW_LIMIT_DISCHARGE -10.0f
/**
* Different alarm levels for the cold temperature where the derating of
* maximum discharge current is fully applied. Below this temperature the
* battery pack should not deliver any current in discharge direction.
*/
#define SOX_MOL_TEMP_LOW_LIMIT_DISCHARGE -10.0
#define SOX_RSL_TEMP_LOW_LIMIT_DISCHARGE -10.0
#define SOX_MSL_TEMP_LOW_LIMIT_DISCHARGE -10.0
/**
* @ingroup CONFIG_SOX
* the cold temperature where the derating of maximum charge current starts,
* i.e., below this temperature battery pack should not deliver full charge current
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* 0.0
*/
#define SOX_TEMP_LOW_CUTOFF_CHARGE 0.0f
/**
* Different alarm levels for the cold temperature where the derating of
* maximum charge current starts, i.e., below this temperature battery pack
* should not deliver full charge current
*/
#define SOX_MOL_TEMP_LOW_CUTOFF_CHARGE 0.0f
#define SOX_RSL_TEMP_LOW_CUTOFF_CHARGE 0.0f
#define SOX_MSL_TEMP_LOW_CUTOFF_CHARGE 0.0f
/**
* @ingroup CONFIG_SOX
* the cold temperature where the derating of maximum charge current is fully applied,
* i.e., below this temperature battery pack should not deliver any current in charge direction
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* -10.0
*/
#define SOX_TEMP_LOW_LIMIT_CHARGE -10.0f
/**
* Different alarm levels for the cold temperature where the derating of
* maximum charge current is fully applied. Below this temperature the
* battery pack should not deliver any current in charge direction.
*/
#define SOX_MOL_TEMP_LOW_LIMIT_CHARGE -10.0f
#define SOX_RSL_TEMP_LOW_LIMIT_CHARGE -10.0f
#define SOX_MSL_TEMP_LOW_LIMIT_CHARGE -10.0f
/**
* @ingroup CONFIG_SOX
* the hot temperature where the derating of maximum discharge current starts,
* i.e., above this temperature battery pack should not deliver full discharge current
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* 45.0
*/
#define SOX_TEMP_HIGH_CUTOFF_DISCHARGE 45.0f
/**
* Different alarm levels for the high temperature where the derating of
* maximum discharge current starts, i.e., above this temperature battery pack
* should not deliver full discharge current
*/
#define SOX_MOL_TEMP_HIGH_CUTOFF_DISCHARGE 45.0f
#define SOX_RSL_TEMP_HIGH_CUTOFF_DISCHARGE 45.0f
#define SOX_MSL_TEMP_HIGH_CUTOFF_DISCHARGE 45.0f
/**
* @ingroup CONFIG_SOX
* the hot temperature where the derating of maximum discharge current is fully applied,
* i.e., above this temperature battery pack should not deliver any current in discharge direction
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* 55.0
*/
#define SOX_TEMP_HIGH_LIMIT_DISCHARGE 55.0f
/**
* Different alarm levels for the high temperature where the derating of
* maximum discharge current is fully applied. Above this temperature the
* battery pack should not deliver any current in discharge direction.
*/
#define SOX_MOL_TEMP_HIGH_LIMIT_DISCHARGE 55.0f
#define SOX_RSL_TEMP_HIGH_LIMIT_DISCHARGE 55.0f
#define SOX_MSL_TEMP_HIGH_LIMIT_DISCHARGE 55.0f
/**
* @ingroup CONFIG_SOX
* the hot temperature where the derating of maximum charge current starts,
* i.e., above this temperature battery pack should not deliver full charge current
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* 45.0
*/
#define SOX_TEMP_HIGH_CUTOFF_CHARGE 45.0f
/**
* Different alarm levels for the high temperature where the derating of
* maximum charge current starts, i.e., above this temperature battery pack
* should not deliver full charge current
*/
#define SOX_MOL_TEMP_HIGH_CUTOFF_CHARGE 45.0f
#define SOX_RSL_TEMP_HIGH_CUTOFF_CHARGE 45.0f
#define SOX_MSL_TEMP_HIGH_CUTOFF_CHARGE 45.0f
/**
* @ingroup CONFIG_SOX
* the hot temperature where the derating of maximum charge current is fully applied,
* i.e., above this temperature battery pack should not deliver any current in charge direction
* \par Type:
* float
* \par Range:
* -40.0<x<80.0
* \par Unit:
* °C
* \par Default:
* 55.0
*/
#define SOX_TEMP_HIGH_LIMIT_CHARGE 55.0f
/**
* Different alarm levels for the high temperature where the derating of
* maximum charge current is fully applied. Above this temperature the
* battery pack should not deliver any current in charge direction.
*/
#define SOX_MOL_TEMP_HIGH_LIMIT_CHARGE 55.0f
#define SOX_RSL_TEMP_HIGH_LIMIT_CHARGE 55.0f
#define SOX_MSL_TEMP_HIGH_LIMIT_CHARGE 55.0f
/**
* @ingroup CONFIG_SOX
* above this SOC value battery pack should not be exposed to full current in charge direction
* \par Type:
* int
* \par Range:
* 0<=x<=10000
* \par Unit:
* 0.01%
* \par Default:
* 8500
*/
#define SOX_SOC_CUTOFF_CHARGE 8500
/**
* Different alarm levels for the upper SOC limit where the derating of
* maximum charge current starts. Above this value the battery pack should
* not deliver full current in charge direction.
*/
#define SOX_MOL_SOC_CUTOFF_CHARGE 8500
#define SOX_RSL_SOC_CUTOFF_CHARGE 8500
#define SOX_MSL_SOC_CUTOFF_CHARGE 8500
/**
* @ingroup CONFIG_SOX
* above this SOC value battery pack should not be exposed to any current in charge direction
* \par Type:
* int
* \par Range:
* 0<=x<=10000
* \par Unit:
* 0.01%
* \par Default:
* 9500
*/
#define SOX_SOC_LIMIT_CHARGE 9500
/**
* Different alarm levels for the upper SOC limit where the derating of
* maximum charge current is fully applied. Above this value the battery pack
* should not deliver full current in charge direction.
*/
#define SOX_MOL_SOC_LIMIT_CHARGE 9500
#define SOX_RSL_SOC_LIMIT_CHARGE 9500
#define SOX_MSL_SOC_LIMIT_CHARGE 9500
/**
* @ingroup CONFIG_SOX
* below this SOC value battery pack should not deliver full current in discharge direction
* \par Type:
* int
* \par Range:
* 0<=x<=10000
* \par Unit:
* 0.01%
* \par Default:
* 1500
*/
#define SOX_SOC_CUTOFF_DISCHARGE 1500
/**
* Different alarm levels for the lower SOC limit where the derating of
* maximum discharge current starts. Below this value the battery pack should
* not deliver full current in discharge direction.
*/
#define SOX_MOL_SOC_CUTOFF_DISCHARGE 1500
#define SOX_RSL_SOC_CUTOFF_DISCHARGE 1500
#define SOX_MSL_SOC_CUTOFF_DISCHARGE 1500
/**
* @ingroup CONFIG_SOX
* below this SOC value battery pack should not deliver any current in discharge direction
* \par Type:
* int
* \par Range:
* 0<=x<=10000
* \par Unit:
* 0.01%
* \par Default:
* 500
*/
#define SOX_SOC_LIMIT_DISCHARGE 500
/**
* Different alarm levels for the lower SOC limit where the derating of
* maximum discharge is fully applied. Below this value the battery pack
* should not deliver any current in discharge direction.
*/
#define SOX_MOL_SOC_LIMIT_DISCHARGE 500
#define SOX_RSL_SOC_LIMIT_DISCHARGE 500
#define SOX_MSL_SOC_LIMIT_DISCHARGE 500
/**
* @ingroup CONFIG_SOX
* above this voltage value battery pack should not be exposed to full current in charge direction
* \par Type:
* int
* \par Unit:
* mV
* \par Range:
* [2000,2500]
* \par Default:
* 2400
*/
#define SOX_VOLT_CUTOFF_CHARGE 2400
/**
* Different alarm levels for the upper voltage limit where the derating of
* maximum charge current starts. Above this value the battery pack
* should not deliver full current in charge direction.
*/
#define SOX_MOL_VOLT_CUTOFF_CHARGE 2400
#define SOX_RSL_VOLT_CUTOFF_CHARGE 2400
#define SOX_MSL_VOLT_CUTOFF_CHARGE 2400
/**
* @ingroup CONFIG_SOX
* above this voltage value battery pack should not be exposed to any current in charge direction
* \par Type:
* int
* \par Unit:
* mV
* \par Range:
* [2500,3000]
* \par Default:
* 2550
*/
#define SOX_VOLT_LIMIT_CHARGE 2550
/**
* Different alarm levels for the upper voltage limit where the derating of
* maximum charge current is fully applied. Above this value the battery pack
* should not deliver any current in charge direction.
*/
#define SOX_MOL_VOLT_LIMIT_CHARGE 2550
#define SOX_RSL_VOLT_LIMIT_CHARGE 2550
#define SOX_MSL_VOLT_LIMIT_CHARGE 2550
/**
* @ingroup CONFIG_SOX
* below this voltage value battery pack should not deliver full current in discharge direction
* \par Type:
* int
* \par Unit:
* mV
* \par Range:
* [1900,2200]
* \par Default:
* 2000
*/
#define SOX_VOLT_CUTOFF_DISCHARGE 2000
/**
* Different alarm levels for the lower voltage limit where the derating of
* maximum discharge current starts. Below this value the battery pack
* should not deliver full current in discharge direction.
*/
#define SOX_MOL_VOLT_CUTOFF_DISCHARGE 2000
#define SOX_RSL_VOLT_CUTOFF_DISCHARGE 2000
#define SOX_MSL_VOLT_CUTOFF_DISCHARGE 2000
/**
* @ingroup CONFIG_SOX
* below this voltage value battery pack should not deliver any current in discharge direction
* \par Type:
* int
* \par Unit:
* mV
* \par Range:
* [1700,1900]
* \par Default:
* 1750
*/
#define SOX_VOLT_LIMIT_DISCHARGE 1750
/**
* Different alarm levels for the lower voltage limit where the derating of
* maximum discharge is fully applied. Below this value the battery pack
* should not deliver any current in discharge direction.
*/
#define SOX_MOL_VOLT_LIMIT_DISCHARGE 1750
#define SOX_RSL_VOLT_LIMIT_DISCHARGE 1750
#define SOX_MSL_VOLT_LIMIT_DISCHARGE 1750
/*================== Constant and Variable Definitions ====================*/
/**
* structure for configuration of SoF Calculation
*/
typedef struct {
float I_DischaMax_Cont;
float I_ChargeMax_Cont;
float I_Limphome;
float Cutoff_TLow_Discha;
float Limit_TLow_Discha;
float Cutoff_TLow_Charge;
float Limit_TLow_Charge;
float Cutoff_THigh_Discha;
float Limit_THigh_Discha;
float Cutoff_THigh_Charge;
float Limit_THigh_Charge;
float Cutoff_Soc_Charge;
float Limit_Soc_Charge;
float Cutoff_Soc_Discha;
float Limit_Soc_Discha;
float Cutoff_Voltage_Charge;
float Limit_Voltage_Charge;
float Cutoff_Voltage_Discha;
float Limit_Voltage_Discha;
} SOX_SOF_CONFIG_s;
extern const SOX_SOF_CONFIG_s sox_sof_config_maxAllowedCurrent;
extern const SOX_SOF_CONFIG_s sox_sof_config_MOL;
extern const SOX_SOF_CONFIG_s sox_sof_config_RSL;
extern const SOX_SOF_CONFIG_s sox_sof_config_MSL;
/*================== Function Prototypes ==================================*/
/*================== Function Implementations =============================*/
#endif /* SOX_CFG_H_ */