foxBMS  1.0.0
The foxBMS Battery Management System API Documentation
soc_counting.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2021, Fraunhofer-Gesellschaft zur Foerderung der
4  * angewandten Forschung e.V. All rights reserved.
5  *
6  * BSD 3-Clause License
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holder nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * We kindly request you to use one or more of the following phrases to refer
31  * to foxBMS in your hardware, software, documentation or advertising
32  * materials:
33  *
34  * ″This product uses parts of foxBMS®″
35  *
36  * ″This product includes parts of foxBMS®″
37  *
38  * ″This product is derived from foxBMS®″
39  *
40  */
41 
42 /**
43  * @file soc_counting.c
44  * @author foxBMS Team
45  * @date 2020-10-07 (date of creation)
46  * @updated 2020-10-07 (date of last update)
47  * @ingroup APPLICATION
48  * @prefix SOC
49  *
50  * @brief SOC module responsible for calculation of SOC
51  *
52  */
53 
54 /*========== Includes =======================================================*/
55 #include "soc_counting.h"
56 
57 #include "bms.h"
58 #include "database.h"
59 #include "foxmath.h"
60 #include "fram.h"
61 
62 /*========== Macros and Definitions =========================================*/
63 /** This structure contains all the variables relevant for the SOX */
64 typedef struct SOC_STATE {
65  bool socInitialized; /*!< true if the initialization has passed, false otherwise */
66  bool sensorCcUsed[BS_NR_OF_STRINGS]; /*!< bool if coulomb counting functionality from current sensor is used */
67  float ccScalingAverage[BS_NR_OF_STRINGS]; /*!< current sensor offset scaling for average SOC */
68  float ccScalingMinimum[BS_NR_OF_STRINGS]; /*!< current sensor offset scaling value for minimum SOC */
69  float ccScalingMaximum[BS_NR_OF_STRINGS]; /*!< current sensor offset scaling value for maximum SOC */
70  uint32_t previousTimestamp[BS_NR_OF_STRINGS]; /*!< timestamp buffer to check if current/CC data has been updated */
72 
73 /*========== Static Constant and Variable Definitions =======================*/
74 /** state variable for SOC module */
76  .socInitialized = false,
77 };
78 
79 /** local copies of database tables */
80 /**@{*/
83 /**@}*/
84 
85 /*========== Extern Constant and Variable Definitions =======================*/
86 
87 /*========== Static Function Prototypes =====================================*/
88 
89 /**
90  * @brief calculates string SOC in percentage from passed string charge in As
91  * @param[in] charge_As charge in As
92  * @return returns corresponding string SOC in percentage [0.0, 100.0]
93  */
94 static float SOC_GetStringSocPercentageFromCharge(uint32_t charge_As);
95 
96 /**
97  * @brief initializes database and FRAM SOC values via lookup table (average,
98  * minimum and maximum).
99  */
100 static void SOC_RecalibrateViaLookupTable(void);
101 
102 /**
103  * @brief sets SOC value with a parameter between 0.0 and 100.0.
104  * @details limits the SOE value to 0.0 respectively 100.0 if a value outside
105  * of the allowed SOE range is passed. Updates local fram and database
106  * struct but does *NOT* write them
107  * @param[in] socMinimumValue_perc SOC min value to set
108  * @param[in] socMaximumValue_perc SOC max value to set
109  * @param[in] socAverageValue_perc SOC average value to set
110  * @param[in] stringNumber addressed string
111  */
112 static void SOC_SetValue(
113  float socMinimumValue_perc,
114  float socMaximumValue_perc,
115  float socAverageValue_perc,
116  uint8_t stringNumber);
117 
118 /**
119  * @brief Check if all database SOC percentage values are within [0.0, 100.0]
120  * Limits SOC values to limit values if outside of this range.
121  * @param[in,out] pTableSoc pointer to database struct with SOC values
122  * @param[in] stringNumber string that is checked
123  */
124 static void SOC_CheckDatabaseSocPercentageLimits(DATA_BLOCK_SOX_s *pTableSoc, uint8_t stringNumber);
125 
126 /**
127  * @brief Set SOC-related values in non-volatile memory
128  * @param[in] pTableSoc pointer to database struct with SOC values
129  * @param[in] stringNumber addressed string
130  */
131 static void SOC_UpdateNvmValues(DATA_BLOCK_SOX_s *pTableSoc, uint8_t stringNumber);
132 
133 /*========== Static Function Implementations ================================*/
134 static float SOC_GetStringSocPercentageFromCharge(uint32_t charge_As) {
135  float stringSoc_perc = 0.0f;
136  if (charge_As >= SOC_STRING_CAPACITY_As) {
137  stringSoc_perc = 100.0f;
138  } else {
139  stringSoc_perc = 100.0f * ((float)charge_As / (float)SOC_STRING_CAPACITY_As);
140  }
141  return stringSoc_perc;
142 }
143 static void SOC_RecalibrateViaLookupTable(void) {
144  DATA_BLOCK_MIN_MAX_s tableMinMaxCellVoltages = {.header.uniqueId = DATA_BLOCK_ID_MIN_MAX};
145  DATA_READ_DATA(&tableMinMaxCellVoltages);
146 
147  for (uint8_t stringNumber = 0u; stringNumber < BS_NR_OF_STRINGS; stringNumber++) {
148  SOC_SetValue(
149  SOC_GetFromVoltage(tableMinMaxCellVoltages.minimumCellVoltage_mV[stringNumber]),
150  SOC_GetFromVoltage(tableMinMaxCellVoltages.maximumCellVoltage_mV[stringNumber]),
151  SOC_GetFromVoltage(tableMinMaxCellVoltages.averageCellVoltage_mV[stringNumber]),
152  stringNumber);
153  }
156 }
157 
158 static void SOC_SetValue(
159  float socMinimumValue_perc,
160  float socMaximumValue_perc,
161  float socAverageValue_perc,
162  uint8_t stringNumber) {
163  /* Set database values */
164  soc_tableSocValues.averageSoc_perc[stringNumber] = socAverageValue_perc;
165  soc_tableSocValues.minimumSoc_perc[stringNumber] = socMinimumValue_perc;
166  soc_tableSocValues.maximumSoc_perc[stringNumber] = socMaximumValue_perc;
167 
168  if (soc_state.sensorCcUsed[stringNumber] == true) {
169  /* Current sensor database entry is read before the call of SOC_SetValue */
170  float ccOffset_perc =
172 
173 #if POSITIVE_DISCHARGE_CURRENT == false
174  ccOffset_perc *= (-1.0f);
175 #endif /* POSITIVE_DISCHARGE_CURRENT == false */
176 
177  /* Recalibrate scaling values */
178  soc_state.ccScalingAverage[stringNumber] = soc_tableSocValues.averageSoc_perc[stringNumber] + ccOffset_perc;
179  soc_state.ccScalingMinimum[stringNumber] = soc_tableSocValues.minimumSoc_perc[stringNumber] + ccOffset_perc;
180  soc_state.ccScalingMaximum[stringNumber] = soc_tableSocValues.maximumSoc_perc[stringNumber] + ccOffset_perc;
181  }
182 
183  /* Limit SOC values to [0.0, 100.0] */
185 
186  /* Update non-volatile memory values */
187  SOC_UpdateNvmValues(&soc_tableSocValues, stringNumber);
188 
191 }
192 
193 static void SOC_CheckDatabaseSocPercentageLimits(DATA_BLOCK_SOX_s *pTableSoc, uint8_t stringNumber) {
194  FAS_ASSERT(pTableSoc != NULL_PTR);
195  FAS_ASSERT(stringNumber < BS_NR_OF_STRINGS);
196 
197  if (pTableSoc->averageSoc_perc[stringNumber] > 100.0f) {
198  pTableSoc->averageSoc_perc[stringNumber] = 100.0f;
199  }
200  if (pTableSoc->averageSoc_perc[stringNumber] < 0.0f) {
201  pTableSoc->averageSoc_perc[stringNumber] = 0.0f;
202  }
203  if (pTableSoc->minimumSoc_perc[stringNumber] > 100.0f) {
204  pTableSoc->minimumSoc_perc[stringNumber] = 100.0f;
205  }
206  if (pTableSoc->minimumSoc_perc[stringNumber] < 0.0f) {
207  pTableSoc->minimumSoc_perc[stringNumber] = 0.0f;
208  }
209  if (pTableSoc->maximumSoc_perc[stringNumber] > 100.0f) {
210  pTableSoc->maximumSoc_perc[stringNumber] = 100.0f;
211  }
212  if (pTableSoc->maximumSoc_perc[stringNumber] < 0.0f) {
213  pTableSoc->maximumSoc_perc[stringNumber] = 0.0f;
214  }
215  return;
216 }
217 
218 static void SOC_UpdateNvmValues(DATA_BLOCK_SOX_s *pTableSoc, uint8_t stringNumber) {
219  fram_soc.averageSoc_perc[stringNumber] = pTableSoc->averageSoc_perc[stringNumber];
220  fram_soc.minimumSoc_perc[stringNumber] = pTableSoc->minimumSoc_perc[stringNumber];
221  fram_soc.maximumSoc_perc[stringNumber] = pTableSoc->maximumSoc_perc[stringNumber];
222 }
223 
224 /*========== Extern Function Implementations ================================*/
225 
226 void SOC_Init(bool ccPresent, uint8_t stringNumber) {
229 
231 
232  if (ccPresent == true) {
233  soc_state.sensorCcUsed[stringNumber] = true;
234 
235  float scalingOffset_perc =
237 
238 #if POSITIVE_DISCHARGE_CURRENT == false
239  scalingOffset_perc *= (-1.0f);
240 #endif /* POSITIVE_DISCHARGE_CURRENT == false */
241 
242  soc_state.ccScalingAverage[stringNumber] = fram_soc.averageSoc_perc[stringNumber] + scalingOffset_perc;
243  soc_state.ccScalingMinimum[stringNumber] = fram_soc.minimumSoc_perc[stringNumber] + scalingOffset_perc;
244  soc_state.ccScalingMaximum[stringNumber] = fram_soc.maximumSoc_perc[stringNumber] + scalingOffset_perc;
245 
246  soc_tableSocValues.averageSoc_perc[stringNumber] = fram_soc.averageSoc_perc[stringNumber];
247  soc_tableSocValues.minimumSoc_perc[stringNumber] = fram_soc.minimumSoc_perc[stringNumber];
248  soc_tableSocValues.maximumSoc_perc[stringNumber] = fram_soc.maximumSoc_perc[stringNumber];
249 
251 
252  /* Alternatively, SOC can be initialized with {V,SOC} lookup table if available */
253  /* with the function SOC_Init_Lookup_Table() */
254  } else {
256  soc_state.sensorCcUsed[stringNumber] = false;
257  }
258  soc_state.socInitialized = true;
260 }
261 
262 void SOC_Calculation(void) {
263  bool continueFunction = true;
264  if (false == soc_state.socInitialized) {
265  /* Exit if SOC not initialized yet */
266  continueFunction = false;
267  }
268 
269  if (true == continueFunction) {
271  /* Recalibrate SOC via LUT */
273  } else {
274  /* Use coulomb/current counting */
276 
277  for (uint8_t stringNumber = 0u; stringNumber < BS_NR_OF_STRINGS; stringNumber++) {
278  if (soc_state.sensorCcUsed[stringNumber] == false) {
279  /* check if current measurement has been updated */
280  if (soc_state.previousTimestamp[stringNumber] !=
282  float timestep_s =
283  ((float)(soc_tableCurrentSensor.timestampCurrent[stringNumber] - soc_state.previousTimestamp[stringNumber])) /
284  1000.0f;
285 
286  if (timestep_s > 0.0f) {
287  /* Current in charge direction negative means SOC increasing --> BAT naming, not ROB */
288 
289  float deltaSOC_perc =
290  ((((float)soc_tableCurrentSensor.current_mA[stringNumber] / 1000.0f) * timestep_s) /
291  (float)SOC_STRING_CAPACITY_As) *
292  100.0f; /* ((mA / 1000) * 1s) / 1As) * 100% */
293 
294 #if POSITIVE_DISCHARGE_CURRENT == false
295  deltaSOC_perc *= (-1.0f);
296 #endif /* POSITIVE_DISCHARGE_CURRENT == false */
297 
298  soc_tableSocValues.averageSoc_perc[stringNumber] =
299  soc_tableSocValues.averageSoc_perc[stringNumber] - deltaSOC_perc;
300  soc_tableSocValues.minimumSoc_perc[stringNumber] =
301  soc_tableSocValues.minimumSoc_perc[stringNumber] - deltaSOC_perc;
302  soc_tableSocValues.maximumSoc_perc[stringNumber] =
303  soc_tableSocValues.maximumSoc_perc[stringNumber] - deltaSOC_perc;
304 
305  /* Limit SOC calculation to 0% respectively 100% */
307 
308  /* Update values in non-volatile memory */
309  SOC_UpdateNvmValues(&soc_tableSocValues, stringNumber);
310  }
311  soc_state.previousTimestamp[stringNumber] =
313  } /* end check if current measurement has been updated */
314  /* update the variable for the next check */
315  } else {
316  /* check if cc measurement has been updated */
317  if (soc_state.previousTimestamp[stringNumber] !=
319  float deltaSoc_perc = ((float)soc_tableCurrentSensor.currentCounter_As[stringNumber] /
320  (float)SOC_STRING_CAPACITY_As) *
321  100.0f;
322 
323 #if POSITIVE_DISCHARGE_CURRENT == false
324  deltaSoc_perc *= (-1.0f);
325 #endif /* POSITIVE_DISCHARGE_CURRENT == false */
326 
327  soc_tableSocValues.averageSoc_perc[stringNumber] = soc_state.ccScalingAverage[stringNumber] -
328  deltaSoc_perc;
329  soc_tableSocValues.minimumSoc_perc[stringNumber] = soc_state.ccScalingMinimum[stringNumber] -
330  deltaSoc_perc;
331  soc_tableSocValues.maximumSoc_perc[stringNumber] = soc_state.ccScalingMaximum[stringNumber] -
332  deltaSoc_perc;
333 
334  /* Limit SOC values to [0.0, 100.0] */
336 
337  /* Update values in non-volatile memory */
338  SOC_UpdateNvmValues(&soc_tableSocValues, stringNumber);
339 
340  soc_state.previousTimestamp[stringNumber] =
342  } /* end check if cc measurement has been updated */
343  }
344  }
345  /* Update database and FRAM value */
348  }
349  }
350 }
351 
352 float SOC_GetFromVoltage(int16_t voltage_mV) {
353  float SOC = 0.50f;
354 
355  return SOC;
356 }
357 
358 /*========== Externalized Static Function Implementations (Unit Test) =======*/
soc_state
static SOC_STATE_s soc_state
Definition: soc_counting.c:75
SOC_STATE::socInitialized
bool socInitialized
Definition: soc_counting.c:65
SOC_STATE::ccScalingMinimum
float ccScalingMinimum[BS_NR_OF_STRINGS]
Definition: soc_counting.c:68
DATA_BLOCK_CURRENT_SENSOR
Definition: database_cfg.h:209
FRAM_SOC::averageSoc_perc
float averageSoc_perc[BS_NR_OF_STRINGS]
Definition: fram_cfg.h:122
SOC_RecalibrateViaLookupTable
static void SOC_RecalibrateViaLookupTable(void)
initializes database and FRAM SOC values via lookup table (average, minimum and maximum).
Definition: soc_counting.c:143
DATA_BLOCK_MIN_MAX::header
DATA_BLOCK_HEADER_s header
Definition: database_cfg.h:162
fram_soc
FRAM_SOC_s fram_soc
Definition: fram_cfg.c:70
SOC_Calculation
void SOC_Calculation(void)
integrates current over time to calculate SOC.
Definition: soc_counting.c:262
bms.h
bms driver header
SOC_Init
void SOC_Init(bool ccPresent, uint8_t stringNumber)
initializes startup SOC-related values like lookup from nonvolatile ram at startup
Definition: soc_counting.c:226
DATA_WRITE_DATA
#define DATA_WRITE_DATA(...)
Definition: database.h:82
SOC_CheckDatabaseSocPercentageLimits
static void SOC_CheckDatabaseSocPercentageLimits(DATA_BLOCK_SOX_s *pTableSoc, uint8_t stringNumber)
Check if all database SOC percentage values are within [0.0, 100.0] Limits SOC values to limit values...
Definition: soc_counting.c:193
SOC_STATE::sensorCcUsed
bool sensorCcUsed[BS_NR_OF_STRINGS]
Definition: soc_counting.c:66
SOC_GetFromVoltage
float SOC_GetFromVoltage(int16_t voltage_mV)
look-up table for SOC initialization (average, min and max).
Definition: soc_counting.c:352
SOC_STATE::previousTimestamp
uint32_t previousTimestamp[BS_NR_OF_STRINGS]
Definition: soc_counting.c:70
DATA_BLOCK_MIN_MAX::averageCellVoltage_mV
int16_t averageCellVoltage_mV[BS_NR_OF_STRINGS]
Definition: database_cfg.h:164
DATA_BLOCK_SOX::header
DATA_BLOCK_HEADER_s header
Definition: database_cfg.h:489
DATA_BLOCK_CURRENT_SENSOR::current_mA
int32_t current_mA[BS_NR_OF_STRINGS]
Definition: database_cfg.h:214
FAS_ASSERT
#define FAS_ASSERT(x)
Assertion macro that asserts that x is true.
Definition: fassert.h:237
soc_tableSocValues
static DATA_BLOCK_SOX_s soc_tableSocValues
Definition: soc_counting.c:82
SOC_UpdateNvmValues
static void SOC_UpdateNvmValues(DATA_BLOCK_SOX_s *pTableSoc, uint8_t stringNumber)
Set SOC-related values in non-volatile memory.
Definition: soc_counting.c:218
SOC_GetStringSocPercentageFromCharge
static float SOC_GetStringSocPercentageFromCharge(uint32_t charge_As)
calculates string SOC in percentage from passed string charge in As
Definition: soc_counting.c:134
SOC_STRING_CAPACITY_As
#define SOC_STRING_CAPACITY_As
Definition: soc_counting_cfg.h:69
DATA_BLOCK_ID_MIN_MAX
@ DATA_BLOCK_ID_MIN_MAX
Definition: database_cfg.h:75
foxmath.h
math library for often used math functions
FRAM_BLOCK_ID_SOC
@ FRAM_BLOCK_ID_SOC
Definition: fram_cfg.h:89
soc_tableCurrentSensor
static DATA_BLOCK_CURRENT_SENSOR_s soc_tableCurrentSensor
Definition: soc_counting.c:81
FRAM_SOC::maximumSoc_perc
float maximumSoc_perc[BS_NR_OF_STRINGS]
Definition: fram_cfg.h:121
DATA_BLOCK_CURRENT_SENSOR::header
DATA_BLOCK_HEADER_s header
Definition: database_cfg.h:213
SOC_STATE_s
struct SOC_STATE SOC_STATE_s
BMS_AT_REST
@ BMS_AT_REST
Definition: bms.h:69
BMS_GetBatterySystemState
BMS_CURRENT_FLOW_STATE_e BMS_GetBatterySystemState(void)
Returns current battery system state (charging/discharging, resting or in relaxation phase)
Definition: bms.c:1230
SOC_STATE
Definition: soc_counting.c:64
DATA_BLOCK_CURRENT_SENSOR::timestampCurrent
uint32_t timestampCurrent[BS_NR_OF_STRINGS]
Definition: database_cfg.h:218
DATA_BLOCK_MIN_MAX::maximumCellVoltage_mV
int16_t maximumCellVoltage_mV[BS_NR_OF_STRINGS]
Definition: database_cfg.h:167
DATA_BLOCK_SOX::maximumSoc_perc
float maximumSoc_perc[BS_NR_OF_STRINGS]
Definition: database_cfg.h:492
SOC_SetValue
static void SOC_SetValue(float socMinimumValue_perc, float socMaximumValue_perc, float socAverageValue_perc, uint8_t stringNumber)
sets SOC value with a parameter between 0.0 and 100.0.
Definition: soc_counting.c:158
FRAM_Write
STD_RETURN_TYPE_e FRAM_Write(FRAM_BLOCK_ID_e blockId)
Writes a variable to the FRAM.
Definition: fram.c:101
DATA_BLOCK_MIN_MAX
Definition: database_cfg.h:158
DATA_READ_DATA
#define DATA_READ_DATA(...)
Definition: database.h:72
FRAM_SOC::minimumSoc_perc
float minimumSoc_perc[BS_NR_OF_STRINGS]
Definition: fram_cfg.h:120
DATA_BLOCK_CURRENT_SENSOR::currentCounter_As
int32_t currentCounter_As[BS_NR_OF_STRINGS]
Definition: database_cfg.h:226
database.h
Database module header.
NULL_PTR
#define NULL_PTR
Null pointer.
Definition: fstd_types.h:66
FRAM_Read
STD_RETURN_TYPE_e FRAM_Read(FRAM_BLOCK_ID_e blockId)
Reads a variable from the FRAM.
Definition: fram.c:160
DATA_BLOCK_CURRENT_SENSOR::timestampCurrentCounting
uint32_t timestampCurrentCounting[BS_NR_OF_STRINGS]
Definition: database_cfg.h:229
DATA_BLOCK_MIN_MAX::minimumCellVoltage_mV
int16_t minimumCellVoltage_mV[BS_NR_OF_STRINGS]
Definition: database_cfg.h:165
soc_counting.h
Header for SOC module, responsible for calculation of SOC.
DATA_BLOCK_SOX::minimumSoc_perc
float minimumSoc_perc[BS_NR_OF_STRINGS]
Definition: database_cfg.h:491
DATA_BLOCK_ID_CURRENT_SENSOR
@ DATA_BLOCK_ID_CURRENT_SENSOR
Definition: database_cfg.h:76
DATA_BLOCK_SOX::averageSoc_perc
float averageSoc_perc[BS_NR_OF_STRINGS]
Definition: database_cfg.h:490
BS_NR_OF_STRINGS
#define BS_NR_OF_STRINGS
Definition: battery_system_cfg.h:89
SOC_STATE::ccScalingMaximum
float ccScalingMaximum[BS_NR_OF_STRINGS]
Definition: soc_counting.c:69
DATA_BLOCK_SOX
Definition: database_cfg.h:485
DATA_BLOCK_ID_SOX
@ DATA_BLOCK_ID_SOX
Definition: database_cfg.h:91
fram.h
Header for the driver for the FRAM module.
DATA_BLOCKHEADER::uniqueId
DATA_BLOCK_ID_e uniqueId
Definition: database_cfg.h:109
SOC_STATE::ccScalingAverage
float ccScalingAverage[BS_NR_OF_STRINGS]
Definition: soc_counting.c:67