BMS Module Sources


bms.c (primary)

/**
 *
 * @copyright © 2010 - 2021, 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    bms.c
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE
 * @prefix  BMS
 *
 * @brief   bms driver implementation
 */


/*================== Includes =============================================*/
#include "bms.h"

#include "bal.h"
#include "batterycell_cfg.h"
#include "batterysystem_cfg.h"
#include "database.h"
#include "diag.h"
#include "interlock.h"
#include "ltc_cfg.h"
#include "meas.h"
#include "os.h"
#include "plausibility.h"


/*================== Macros and Definitions ===============================*/

/**
 * Saves the last state and the last substate
 */
#define BMS_SAVELASTSTATES()    bms_state.laststate = bms_state.state; \
                                bms_state.lastsubstate = bms_state.substate;

/*================== Constant and Variable Definitions ====================*/

/**
 * contains the state of the contactor state machine
 */
static BMS_STATE_s bms_state = {
    .timer             = 0,
    .statereq          = BMS_STATE_NO_REQUEST,
    .state             = BMS_STATEMACH_UNINITIALIZED,
    .substate          = BMS_ENTRY,
    .currentFlowState  = BMS_RELAXATION,
    .laststate         = BMS_STATEMACH_UNINITIALIZED,
    .lastsubstate      = 0,
    .triggerentry      = 0,
    .ErrRequestCounter = 0,
    .initFinished      = E_NOT_OK,
    .restTimer_ms      = BS_RELAXATION_PERIOD_MS,
    .counter           = 0,
};

static DATA_BLOCK_CELLVOLTAGE_s bms_tab_cellvolt;
static DATA_BLOCK_CURRENT_SENSOR_s bms_tab_cur_sensor;
static DATA_BLOCK_MINMAX_s bms_tab_minmax;
static DATA_BLOCK_OPENWIRE_s bms_ow_tab;
static DATA_BLOCK_SOF_s bms_tab_sof;


/*================== Function Prototypes ==================================*/

static BMS_RETURN_TYPE_e BMS_CheckStateRequest(BMS_STATE_REQUEST_e statereq);
static BMS_STATE_REQUEST_e BMS_GetStateRequest(void);
static BMS_STATE_REQUEST_e BMS_TransferStateRequest(void);
static uint8_t BMS_CheckReEntrance(void);
static uint8_t BMS_CheckCANRequests(void);
static STD_RETURN_TYPE_e BMS_CheckAnyErrorFlagSet(void);
static void BMS_UpdateBatsysState(DATA_BLOCK_CURRENT_SENSOR_s *curSensor);
static void BMS_GetMeasurementValues(void);
static void BMS_CheckVoltages(void);
static void BMS_CheckTemperatures(void);
static void BMS_CheckCurrent(void);
static void BMS_CheckSlaveTemperatures(void);
static void BMS_CheckOpenSenseWire(void);

/*================== Function Implementations =============================*/

/**
 * @brief   re-entrance check of SYS state machine trigger function
 *
 * @details This function is not re-entrant and should only be called time- or event-triggered. It
 *          increments the triggerentry counter from the state variable ltc_state. It should never
 *          be called by two different processes, so if it is the case, triggerentry should never
 *          be higher than 0 when this function is called.
 *
 * @return  retval  0 if no further instance of the function is active, 0xff else
 */
static uint8_t BMS_CheckReEntrance(void) {
    uint8_t retval = 0;
    OS_TaskEnter_Critical();
    if (!bms_state.triggerentry) {
        bms_state.triggerentry++;
    } else {
        retval = 0xFF;  /* multiple calls of function */
    }
    OS_TaskExit_Critical();
    return (retval);
}

/**
 * @brief   gets the current state request.
 *
 * @details This function is used in the functioning of the SYS state machine.
 *
 * @return  current state request, taken from BMS_STATE_REQUEST_e
 */
static BMS_STATE_REQUEST_e BMS_GetStateRequest(void) {
    BMS_STATE_REQUEST_e retval = BMS_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retval    = bms_state.statereq;
    OS_TaskExit_Critical();

    return (retval);
}


BMS_STATEMACH_e BMS_GetState(void) {
    return (bms_state.state);
}


STD_RETURN_TYPE_e BMS_GetInitializationState(void) {
    return (bms_state.initFinished);
}


/**
 * @brief   transfers the current state request to the state machine.
 *
 * @details This function takes the current state request from cont_state and transfers it to th
 *          state machine. It resets the value from cont_state to BMS_STATE_NO_REQUEST
 *
 * @return  retVal          current state request, taken from BMS_STATE_REQUEST_e
 */
static BMS_STATE_REQUEST_e BMS_TransferStateRequest(void) {
    BMS_STATE_REQUEST_e retval = BMS_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retval    = bms_state.statereq;
    bms_state.statereq = BMS_STATE_NO_REQUEST;
    OS_TaskExit_Critical();
    return (retval);
}



BMS_RETURN_TYPE_e BMS_SetStateRequest(BMS_STATE_REQUEST_e statereq) {
    BMS_RETURN_TYPE_e retVal = BMS_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retVal = BMS_CheckStateRequest(statereq);

    if (retVal == BMS_OK) {
            bms_state.statereq = statereq;
    }
    OS_TaskExit_Critical();

    return (retVal);
}

/**
 * @brief   checks the state requests that are made.
 *
 * @details This function checks the validity of the state requests. The results of the checked is
 *          returned immediately.
 *
 * @param   statereq    state request to be checked
 *
 * @return  result of the state request that was made, taken from BMS_RETURN_TYPE_e
 */
static BMS_RETURN_TYPE_e BMS_CheckStateRequest(BMS_STATE_REQUEST_e statereq) {
    if (statereq == BMS_STATE_ERROR_REQUEST) {
        return BMS_OK;
    }

    if (bms_state.statereq == BMS_STATE_NO_REQUEST) {
        /* init only allowed from the uninitialized state */
        if (statereq == BMS_STATE_INIT_REQUEST) {
            if (bms_state.state == BMS_STATEMACH_UNINITIALIZED) {
                return BMS_OK;
            } else {
                return BMS_ALREADY_INITIALIZED;
            }
        } else {
            return BMS_ILLEGAL_REQUEST;
        }
    } else {
        return BMS_REQUEST_PENDING;
    }
}

void BMS_Trigger(void) {
    BMS_STATE_REQUEST_e statereq = BMS_STATE_NO_REQUEST;
    CONT_STATEMACH_e contstate = CONT_STATEMACH_UNDEFINED;
    DATA_BLOCK_SYSTEMSTATE_s systemstate = {0};
    uint32_t timestamp = OS_getOSSysTick();
    static uint32_t nextOpenWireCheck = 0;

    DIAG_SysMonNotify(DIAG_SYSMON_BMS_ID, 0);  /* task is running, state = ok */

    if (bms_state.state != BMS_STATEMACH_UNINITIALIZED) {
        BMS_GetMeasurementValues();
        BMS_UpdateBatsysState(&bms_tab_cur_sensor);
        BMS_CheckVoltages();
        BMS_CheckTemperatures();
        BMS_CheckCurrent();
        BMS_CheckSlaveTemperatures();
        BMS_CheckOpenSenseWire();

        /* Plausibility check */
        PL_CheckPackvoltage(&bms_tab_cellvolt, &bms_tab_cur_sensor);
    }
    /* Check re-entrance of function */
    if (BMS_CheckReEntrance()) {
        return;
    }

    if (bms_state.timer) {
        if (--bms_state.timer) {
            bms_state.triggerentry--;
            return;    /* handle state machine only if timer has elapsed */
        }
    }

    /****Happens every time the state machine is triggered**************/
    switch (bms_state.state) {
        /****************************UNINITIALIZED***********************************/
        case BMS_STATEMACH_UNINITIALIZED:
            /* waiting for Initialization Request */
            statereq = BMS_TransferStateRequest();
            if (statereq == BMS_STATE_INIT_REQUEST) {
                BMS_SAVELASTSTATES();
                bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                bms_state.state = BMS_STATEMACH_INITIALIZATION;
                bms_state.substate = BMS_ENTRY;
            } else if (statereq == BMS_STATE_NO_REQUEST) {
                /* no actual request pending */
            } else {
                bms_state.ErrRequestCounter++;  /* illegal request pending */
            }
            break;


        /****************************INITIALIZATION**********************************/
        case BMS_STATEMACH_INITIALIZATION:
            BMS_SAVELASTSTATES();

            bms_state.timer = BMS_STATEMACH_LONGTIME_MS;
            bms_state.state = BMS_STATEMACH_INITIALIZED;
            bms_state.substate = BMS_ENTRY;

            break;

        /****************************INITIALIZED*************************************/
        case BMS_STATEMACH_INITIALIZED:
            BMS_SAVELASTSTATES();
            bms_state.initFinished = E_OK;
            bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
            bms_state.state = BMS_STATEMACH_IDLE;
            bms_state.substate = BMS_ENTRY;
            break;

        /****************************IDLE*************************************/
        case BMS_STATEMACH_IDLE:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
                DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                systemstate.bms_state = BMS_STATEMACH_IDLE;
                DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                    break;
                }
            } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                if (BMS_CheckCANRequests() == BMS_REQ_ID_STANDBY) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_STANDBY;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    break;
                }
            }
            break;


        /****************************STANDBY*************************************/
        case BMS_STATEMACH_STANDBY:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
                BAL_SetStateRequest(BAL_STATE_ALLOWBALANCING_REQUEST);
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                CONT_SetStateRequest(CONT_STATE_STANDBY_REQUEST);
#endif /* BUILD_MODULE_ENABLE_CONTACTOR == 1 */
#if BUILD_MODULE_ENABLE_ILCK == 1
                ILCK_SetStateRequest(ILCK_STATE_CLOSE_REQUEST);
#endif /* BUILD_MODULE_ENABLE_ILCK == 1 */
#if LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK == TRUE
                nextOpenWireCheck = timestamp + LTC_STANDBY_OPEN_WIRE_PERIOD_ms;
#endif /* LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK == TRUE */
                bms_state.timer = BMS_STATEMACH_MEDIUMTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS_INTERLOCK;
                DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                systemstate.bms_state = BMS_STATEMACH_STANDBY;
                DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS_INTERLOCK) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_INTERLOCK_CHECKED;
                    break;
                }
            } else if (bms_state.substate == BMS_INTERLOCK_CHECKED) {
                bms_state.timer = BMS_STATEMACH_VERYLONGTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                    break;
                }
            } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                if (BMS_CheckCANRequests() == BMS_REQ_ID_NORMAL) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_PRECHARGE;
                    bms_state.substate = BMS_ENTRY;
                    break;
                }
                if (BMS_CheckCANRequests() == BMS_REQ_ID_CHARGE) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_CHARGE_PRECHARGE;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
#if LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK == TRUE
                    if (nextOpenWireCheck <= timestamp) {
                        MEAS_Request_OpenWireCheck();
                        nextOpenWireCheck = timestamp + LTC_STANDBY_OPEN_WIRE_PERIOD_ms;
                    }
#endif /* LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK == TRUE */
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    break;
                }
            }
            break;

        /****************************PRECHARGE*************************************/
        case BMS_STATEMACH_PRECHARGE:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
                BAL_SetStateRequest(BAL_STATE_NOBALANCING_REQUEST);
                DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                systemstate.bms_state = BMS_STATEMACH_PRECHARGE;
                DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                CONT_SetStateRequest(CONT_STATE_NORMAL_REQUEST);
#endif
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                    break;
                }
            } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                if (BMS_CheckCANRequests() == BMS_REQ_ID_STANDBY) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_STANDBY;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                    bms_state.substate = BMS_CHECK_CONTACTOR_NORMAL_STATE;
#else
                    bms_state.state = BMS_STATEMACH_NORMAL;
                    bms_state.substate = BMS_ENTRY;
#endif
                    break;
                }
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
            } else if (bms_state.substate == BMS_CHECK_CONTACTOR_NORMAL_STATE) {
                contstate = CONT_GetState();
                if (contstate == CONT_STATEMACH_NORMAL) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_NORMAL;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else if (contstate == CONT_STATEMACH_ERROR) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                }
#endif
            }
            break;

        /****************************NORMAL*************************************/
        case BMS_STATEMACH_NORMAL:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
#if LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK == TRUE
                nextOpenWireCheck = timestamp + LTC_NORMAL_OPEN_WIRE_PERIOD_ms;
#endif /* LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK == TRUE */
                DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                systemstate.bms_state = BMS_STATEMACH_NORMAL;
                DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                    break;
                }
            } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                if (BMS_CheckCANRequests() == BMS_REQ_ID_STANDBY) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_STANDBY;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
#if LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK == TRUE
                    if (nextOpenWireCheck <= timestamp) {
                        MEAS_Request_OpenWireCheck();
                        nextOpenWireCheck = timestamp + LTC_NORMAL_OPEN_WIRE_PERIOD_ms;
                    }
#endif /* LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK == TRUE */
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    break;
                }
            }
            break;

            /****************************CHARGE_PRECHARGE*************************************/
            case BMS_STATEMACH_CHARGE_PRECHARGE:
                BMS_SAVELASTSTATES();

                if (bms_state.substate == BMS_ENTRY) {
                    BAL_SetStateRequest(BAL_STATE_NOBALANCING_REQUEST);
                    DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                    systemstate.bms_state = BMS_STATEMACH_CHARGE_PRECHARGE;
                    DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                    CONT_SetStateRequest(CONT_STATE_CHARGE_REQUEST);
#endif
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    break;
                } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                    if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.state = BMS_STATEMACH_ERROR;
                        bms_state.substate = BMS_ENTRY;
                        break;
                    } else {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                        break;
                    }
                } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                    if (BMS_CheckCANRequests() == BMS_REQ_ID_STANDBY) {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.state = BMS_STATEMACH_STANDBY;
                        bms_state.substate = BMS_ENTRY;
                        break;
                    } else {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                        bms_state.substate = BMS_CHECK_CONTACTOR_CHARGE_STATE;
#else
                        bms_state.state = BMS_STATEMACH_CHARGE;
                        bms_state.substate = BMS_ENTRY;
#endif
                        break;
                    }
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                } else if (bms_state.substate == BMS_CHECK_CONTACTOR_CHARGE_STATE) {
                    contstate = CONT_GetState();
                    if (contstate == CONT_STATEMACH_CHARGE) {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.state = BMS_STATEMACH_CHARGE;
                        bms_state.substate = BMS_ENTRY;
                        break;
                    } else if (contstate == CONT_STATEMACH_ERROR) {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.state = BMS_STATEMACH_ERROR;
                        bms_state.substate = BMS_ENTRY;
                        break;
                    } else {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    }
#endif
                }
                break;




            /****************************CHARGE*************************************/
            case BMS_STATEMACH_CHARGE:
                BMS_SAVELASTSTATES();

                if (bms_state.substate == BMS_ENTRY) {
#if LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK == TRUE
                    nextOpenWireCheck = timestamp + LTC_CHARGE_OPEN_WIRE_PERIOD_ms;
#endif /* LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK == TRUE */
                    DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                    systemstate.bms_state = BMS_STATEMACH_CHARGE;
                    DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    break;
                } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                    if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.state = BMS_STATEMACH_ERROR;
                        bms_state.substate = BMS_ENTRY;
                        break;
                    } else {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                        break;
                    }
                } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                    if (BMS_CheckCANRequests() == BMS_REQ_ID_STANDBY) {
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.state = BMS_STATEMACH_STANDBY;
                        bms_state.substate = BMS_ENTRY;
                        break;
                    } else {
#if LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK == TRUE
                    if (nextOpenWireCheck <= timestamp) {
                        MEAS_Request_OpenWireCheck();
                        nextOpenWireCheck = timestamp + LTC_CHARGE_OPEN_WIRE_PERIOD_ms;
                    }
#endif /* LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK == TRUE */
                        bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                        bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                        break;
                    }
                }
                break;

        /****************************ERROR*************************************/
        case BMS_STATEMACH_ERROR:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
                BAL_SetStateRequest(BAL_STATE_NOBALANCING_REQUEST);
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
                CONT_SetStateRequest(CONT_STATE_ERROR_REQUEST);
#endif
                bms_state.timer = BMS_STATEMACH_VERYLONGTIME_MS;
#if BUILD_MODULE_ENABLE_ILCK == 1
                bms_state.substate = BMS_OPEN_INTERLOCK;
#else
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
#endif
                DB_ReadBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                systemstate.bms_state = BMS_STATEMACH_ERROR;
                DB_WriteBlock(&systemstate, DATA_BLOCK_ID_SYSTEMSTATE);
                break;
#if BUILD_MODULE_ENABLE_ILCK == 1
            } else if (bms_state.substate == BMS_OPEN_INTERLOCK) {
                ILCK_SetStateRequest(ILCK_STATE_OPEN_REQUEST);
                nextOpenWireCheck = timestamp + LTC_ERROR_OPEN_WIRE_PERIOD_ms;
                bms_state.timer = BMS_STATEMACH_VERYLONGTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                break;
#endif
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    /* we stay already in requested state */
                    if (nextOpenWireCheck <= timestamp) {
                        /* Perform open-wire check periodically */
                        MEAS_Request_OpenWireCheck();
                        nextOpenWireCheck = timestamp + LTC_ERROR_OPEN_WIRE_PERIOD_ms;
                    }
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_STATE_REQUESTS;
                    break;
                }
            } else if (bms_state.substate == BMS_CHECK_STATE_REQUESTS) {
                if (BMS_CheckCANRequests() == BMS_REQ_ID_STANDBY) {
#if BUILD_MODULE_ENABLE_ILCK == 1
                    ILCK_SetStateRequest(ILCK_STATE_CLOSE_REQUEST);
                    bms_state.substate = BMS_CHECK_INTERLOCK_CLOSE_AFTER_ERROR;
#else
                    bms_state.state = BMS_STATEMACH_STANDBY;
                    bms_state.substate = BMS_ENTRY;
#endif
                    bms_state.timer = BMS_STATEMACH_MEDIUMTIME_MS;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    break;
                }
#if BUILD_MODULE_ENABLE_ILCK == 1
            } else if (bms_state.substate == BMS_CHECK_INTERLOCK_CLOSE_AFTER_ERROR) {
                if (ILCK_GetInterlockFeedback() == ILCK_SWITCH_ON) {
                    /* TODO: check */
                    BAL_SetStateRequest(BAL_STATE_ALLOWBALANCING_REQUEST);
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_STANDBY;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                    break;
                }
#endif
            }
            break;
        default:
            break;
    }  /* end switch (bms_state.state) */

    bms_state.triggerentry--;
    bms_state.counter++;
}

/*================== Static functions =====================================*/
/*
 * @brief   Get latest database entries for static module variables
 */
static void BMS_GetMeasurementValues(void) {
    DB_ReadBlock(&bms_tab_cellvolt, DATA_BLOCK_ID_CELLVOLTAGE);
    DB_ReadBlock(&bms_tab_cur_sensor, DATA_BLOCK_ID_CURRENT_SENSOR);
    DB_ReadBlock(&bms_ow_tab, DATA_BLOCK_ID_OPEN_WIRE);
    DB_ReadBlock(&bms_tab_minmax, DATA_BLOCK_ID_MINMAX);
#if MEAS_TEST_CELL_SOF_LIMITS == TRUE
    /* Database entry only needed if current is checked against SOF values */
    DB_ReadBlock(&bms_tab_sof, DATA_BLOCK_ID_SOF);
#endif /* MEAS_TEST_CELL_SOF_LIMITS == TRUE */
}

/*
 * @brief   Checks the state requests made to the BMS state machine
 *
 * @details Checks of the state request in the database and sets this value as return value
 *
 * @return  requested state
 */
static uint8_t BMS_CheckCANRequests(void) {
    uint8_t retVal = BMS_REQ_ID_NOREQ;
    DATA_BLOCK_STATEREQUEST_s request;

    DB_ReadBlock(&request, DATA_BLOCK_ID_STATEREQUEST);

    if (request.state_request == BMS_REQ_ID_STANDBY) {
        retVal = BMS_REQ_ID_STANDBY;
    } else if (request.state_request == BMS_REQ_ID_NORMAL) {
        retVal = BMS_REQ_ID_NORMAL;
    }

#if BS_SEPARATE_POWERLINES == 1
    else if (request.state_request == BMS_REQ_ID_CHARGE) { /* NOLINT(readability/braces) */
        retVal = BMS_REQ_ID_CHARGE;
    }
#endif /*  BS_SEPARATE_POWERLINES == 1 */

    return retVal;
}

/**
 * @brief   checks the abidance by the safe operating area
 *
 * @details verify for cell voltage measurements (U), if minimum and maximum values are out of range
 */
static void BMS_CheckVoltages(void) {
    uint16_t vol_max = bms_tab_minmax.voltage_max;
    uint16_t vol_min = bms_tab_minmax.voltage_min;
    DIAG_RETURNTYPE_e retvalUndervoltMSL = DIAG_HANDLER_RETURN_ERR_OCCURRED;

    if (vol_max >= BC_VOLTMAX_MOL) {
        /* Over voltage maximum operating limit violated */
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_MOL, DIAG_EVENT_NOK, 0);
        if (vol_max >= BC_VOLTMAX_RSL) {
            /* Over voltage recommended safety limit violated */
            DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_RSL, DIAG_EVENT_NOK, 0);
            if (vol_max >= BC_VOLTMAX_MSL) {
                /* Over voltage maximum safety limit violated */
                DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_MSL, DIAG_EVENT_NOK, 0);
            }
        }
    }
    if (vol_max < BC_VOLTMAX_MSL) {
        /* over voltage maximum safety limit NOT violated */
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_MSL, DIAG_EVENT_OK, 0);
        if (vol_max < BC_VOLTMAX_RSL) {
            /* over voltage recommended safety limit NOT violated */
            DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_RSL, DIAG_EVENT_OK, 0);
            if (vol_max < BC_VOLTMAX_MOL) {
                /* over voltage maximum operating limit NOT violated */
                DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_MOL, DIAG_EVENT_OK, 0);
            }
        }
    }

    if (vol_min <= BC_VOLTMIN_MOL) {
        /* Under voltage maximum operating limit violated */
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_MOL, DIAG_EVENT_NOK, 0);
        if (vol_min <= BC_VOLTMIN_RSL) {
            /* Under voltage recommended safety limit violated */
            DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_RSL, DIAG_EVENT_NOK, 0);
            if (vol_min <= BC_VOLTMIN_MSL) {
                /* Under voltage maximum safety limit violated */
                retvalUndervoltMSL = DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_MSL, DIAG_EVENT_NOK, 0);

                /* If under voltage flag is set and deep-discharge voltage is violated */
                if ((retvalUndervoltMSL == DIAG_HANDLER_RETURN_ERR_OCCURRED) &&
                        (vol_min <= BC_VOLT_DEEP_DISCHARGE)) {
                    DIAG_Handler(DIAG_CH_DEEP_DISCHARGE_DETECTED, DIAG_EVENT_NOK, 0);
                }
            }
        }
    }
    if (vol_min > BC_VOLTMIN_MSL) {
        /* under voltage maximum safety limit NOT violated */
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_MSL, DIAG_EVENT_OK, 0);
        if (vol_min > BC_VOLTMIN_RSL) {
            /* under voltage recommended safety limit NOT violated */
            DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_RSL, DIAG_EVENT_OK, 0);
            if (vol_min > BC_VOLTMIN_MOL) {
                /* under voltage maximum operating limit NOT violated */
                DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_MOL, DIAG_EVENT_OK, 0);
            }
        }
    }
}


/**
 * @brief   checks the abidance by the safe operating area
 *
 * @details verify for cell temperature measurements (T), if minimum and maximum values are out of range
 */
static void BMS_CheckTemperatures(void) {
    int16_t temp_min = bms_tab_minmax.temperature_min;
    int16_t temp_max = bms_tab_minmax.temperature_max;

    /* Over temperature check */
    if (BMS_GetBatterySystemState() == BMS_DISCHARGING) {
        /* Discharge */
        if (temp_max >= BC_TEMPMAX_DISCHARGE_MOL) {
            /* Over temperature maximum operating limit violated*/
            DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_MOL, DIAG_EVENT_NOK, 0);
            if (temp_max >= BC_TEMPMAX_DISCHARGE_RSL) {
                /* Over temperature recommended safety limit violated*/
                DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_RSL, DIAG_EVENT_NOK, 0);
                if (temp_max >= BC_TEMPMAX_DISCHARGE_MSL) {
                    /* Over temperature maximum safety limit violated */
                    DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (temp_max < BC_TEMPMAX_DISCHARGE_MSL) {
            /* over temperature maximum safety limit NOT violated */
            DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_OK, 0);
            if (temp_max < BC_TEMPMAX_DISCHARGE_RSL) {
                /* over temperature recommended safety limit NOT violated */
                DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_RSL, DIAG_EVENT_OK, 0);
                if (temp_max < BC_TEMPMAX_DISCHARGE_MOL) {
                    /* over temperature maximum operating limit NOT violated */
                    DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }

    } else {
        /* Charge/Relaxation/At rest */
        if (temp_max >= BC_TEMPMAX_CHARGE_MOL) {
            /* Over temperature maximum operating limit violated */
            DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_MOL, DIAG_EVENT_NOK, 0);
            if (temp_max >= BC_TEMPMAX_CHARGE_RSL) {
                /* Over temperature recommended safety limit violated */
                DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_RSL, DIAG_EVENT_NOK, 0);
                /* Over temperature maximum safety limit violated */
                if (temp_max >= BC_TEMPMAX_CHARGE_MSL) {
                    DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (temp_max < BC_TEMPMAX_CHARGE_MSL) {
            /* over temperature maximum safety limit NOT violated */
            DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_OK, 0);
            if (temp_max < BC_TEMPMAX_CHARGE_RSL) {
                /* over temperature recommended safety limit NOT violated */
                DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_RSL, DIAG_EVENT_OK, 0);
                if (temp_max < BC_TEMPMAX_CHARGE_MOL) {
                    /* over temperature maximum operating limit NOT violated*/
                    DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    }

    /* Under temperature check */
    if (BMS_GetBatterySystemState() == BMS_DISCHARGING) {
        /* Discharge */
        if (temp_min <= BC_TEMPMIN_DISCHARGE_MOL) {
            /* Under temperature maximum operating limit violated */
            DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_MOL, DIAG_EVENT_NOK, 0);
            if (temp_min <= BC_TEMPMIN_DISCHARGE_RSL) {
                /* Under temperature recommended safety limit violated*/
                DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_RSL, DIAG_EVENT_NOK, 0);
                if (temp_min <= BC_TEMPMIN_DISCHARGE_MSL) {
                    /* Under temperature maximum safety limit violated */
                    DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (temp_min > BC_TEMPMIN_DISCHARGE_MSL) {
            /* under temperature maximum safety limit NOT violated */
            DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_OK, 0);
            if (temp_min > BC_TEMPMIN_DISCHARGE_RSL) {
                /* under temperature recommended safety limit NOT violated */
                DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_RSL, DIAG_EVENT_OK, 0);
                if (temp_min > BC_TEMPMIN_DISCHARGE_MOL) {
                    /* under temperature maximum operating limit NOT violated*/
                    DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else {
        /* Charge/Relaxation/At rest */
        if (temp_min <= BC_TEMPMIN_CHARGE_MOL) {
            /* Under temperature maximum operating limit violated */
            DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_MOL, DIAG_EVENT_NOK, 0);
            if (temp_min <= BC_TEMPMIN_CHARGE_RSL) {
                /* Under temperature recommended safety limit violated */
                DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_RSL, DIAG_EVENT_NOK, 0);
                if (temp_min <= BC_TEMPMIN_CHARGE_MSL) {
                    /* Under temperature maximum safety limit violated */
                    DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (temp_min > BC_TEMPMIN_CHARGE_MSL) {
            /* under temperature maximum safety limit NOT violated */
            DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_OK, 0);
            if (temp_min > BC_TEMPMIN_CHARGE_RSL) {
                /* under temperature recommended safety limit NOT violated */
                DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_RSL, DIAG_EVENT_OK, 0);
                if (temp_min > BC_TEMPMIN_CHARGE_MOL) {
                    /* under temperature maximum operating limit NOT violated*/
                    DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    }
}



/**
 * @brief   checks the abidance by the safe operating area
 *
 * @details verify for cell current measurements (I), if minimum and maximum values are out of range
 */
static void BMS_CheckCurrent(void) {
    int32_t i_current = bms_tab_cur_sensor.current;
    uint32_t i_current_abs = 0;
    BMS_CURRENT_FLOW_STATE_e i_dir = BMS_GetBatterySystemState();
    if (i_current < 0) {
        i_current_abs = - i_current;
    } else {
        i_current_abs = i_current;
    }

    /* initialize variables with default values */
    uint32_t batsys_charge_limit_msl = 0;
    DIAG_CH_ID_e batsys_charge_limit_diag_msl = DIAG_CH_OVERCURRENT_CHARGE_PL0_MSL;
    uint32_t batsys_discharge_limit_msl = 0;
    DIAG_CH_ID_e batsys_discharge_limit_diag_msl = DIAG_CH_OVERCURRENT_DISCHARGE_PL0_MSL;
    uint32_t batsys_charge_limit_rsl = 0;
    DIAG_CH_ID_e batsys_charge_limit_diag_rsl = DIAG_CH_OVERCURRENT_CHARGE_PL0_RSL;
    uint32_t batsys_discharge_limit_rsl = 0;
    DIAG_CH_ID_e batsys_discharge_limit_diag_rsl = DIAG_CH_OVERCURRENT_DISCHARGE_PL0_RSL;
    uint32_t batsys_charge_limit_mol = 0;
    DIAG_CH_ID_e batsys_charge_limit_diag_mol = DIAG_CH_OVERCURRENT_CHARGE_PL0_MOL;
    uint32_t batsys_discharge_limit_mol = 0;
    DIAG_CH_ID_e batsys_discharge_limit_diag_mol = DIAG_CH_OVERCURRENT_DISCHARGE_PL0_MOL;

    /* get active power line */
#if BUILD_MODULE_ENABLE_CONTACTOR == 1
    CONT_POWER_LINE_e powerline = CONT_GetActivePowerLine();
#else
    /* No different power lines exist if project is compiled without contactors
     * -> always check current against current limits of power line 0 */
#warning "Contactors disabled! Current limits always checked against limits of power line 0"
    CONT_POWER_LINE_e powerline = CONT_POWER_LINE_0;
#endif

    /* set limits for batterysystem according to current power line */
    if (powerline == CONT_POWER_LINE_0) {
        batsys_charge_limit_msl = BS_CURRENTMAX_CHARGE_PL0_MSL_mA;
        batsys_charge_limit_diag_msl = DIAG_CH_OVERCURRENT_CHARGE_PL0_MSL;
        batsys_discharge_limit_msl = BS_CURRENTMAX_DISCHARGE_PL0_MSL_mA;
        batsys_discharge_limit_diag_msl = DIAG_CH_OVERCURRENT_DISCHARGE_PL0_MSL;

        batsys_charge_limit_rsl = BS_CURRENTMAX_CHARGE_PL0_RSL_mA;
        batsys_charge_limit_diag_rsl = DIAG_CH_OVERCURRENT_CHARGE_PL0_RSL;
        batsys_discharge_limit_rsl = BS_CURRENTMAX_DISCHARGE_PL0_RSL_mA;
        batsys_discharge_limit_diag_rsl = DIAG_CH_OVERCURRENT_DISCHARGE_PL0_RSL;

        batsys_charge_limit_mol = BS_CURRENTMAX_CHARGE_PL0_MOL_mA;
        batsys_charge_limit_diag_mol = DIAG_CH_OVERCURRENT_CHARGE_PL0_MOL;
        batsys_discharge_limit_mol = BS_CURRENTMAX_DISCHARGE_PL0_MOL_mA;
        batsys_discharge_limit_diag_mol = DIAG_CH_OVERCURRENT_DISCHARGE_PL0_MOL;
#if BS_SEPARATE_POWERLINES == 1
    } else if (powerline == CONT_POWER_LINE_1) {
        batsys_charge_limit_msl = BS_CURRENTMAX_CHARGE_PL1_MSL_mA;
        batsys_charge_limit_diag_msl = DIAG_CH_OVERCURRENT_CHARGE_PL1_MSL;
        batsys_discharge_limit_msl = BS_CURRENTMAX_DISCHARGE_PL1_MSL_mA;
        batsys_discharge_limit_diag_msl = DIAG_CH_OVERCURRENT_DISCHARGE_PL1_MSL;

        batsys_charge_limit_rsl = BS_CURRENTMAX_CHARGE_PL1_RSL_mA;
        batsys_charge_limit_diag_rsl = DIAG_CH_OVERCURRENT_CHARGE_PL1_RSL;
        batsys_discharge_limit_rsl = BS_CURRENTMAX_DISCHARGE_PL1_RSL_mA;
        batsys_discharge_limit_diag_rsl = DIAG_CH_OVERCURRENT_DISCHARGE_PL1_RSL;

        batsys_charge_limit_mol = BS_CURRENTMAX_CHARGE_PL1_MOL_mA;
        batsys_charge_limit_diag_mol = DIAG_CH_OVERCURRENT_CHARGE_PL1_MOL;
        batsys_discharge_limit_mol = BS_CURRENTMAX_DISCHARGE_PL1_MOL_mA;
        batsys_discharge_limit_diag_mol = DIAG_CH_OVERCURRENT_DISCHARGE_PL1_MOL;
#endif
    } else {
        /* this is a configuration error, assume safe default */
        batsys_charge_limit_msl = BS_CS_THRESHOLD_NO_CURRENT_mA;
        batsys_charge_limit_diag_msl = DIAG_CH_OVERCURRENT_PL_NONE;
        batsys_discharge_limit_msl = BS_CS_THRESHOLD_NO_CURRENT_mA;
        batsys_discharge_limit_diag_msl = DIAG_CH_OVERCURRENT_PL_NONE;

        batsys_charge_limit_rsl = BS_CS_THRESHOLD_NO_CURRENT_mA;
        batsys_charge_limit_diag_rsl = DIAG_CH_OVERCURRENT_PL_NONE;
        batsys_discharge_limit_rsl = BS_CS_THRESHOLD_NO_CURRENT_mA;
        batsys_discharge_limit_diag_rsl = DIAG_CH_OVERCURRENT_PL_NONE;

        batsys_charge_limit_mol = BS_CS_THRESHOLD_NO_CURRENT_mA;
        batsys_charge_limit_diag_mol = DIAG_CH_OVERCURRENT_PL_NONE;
        batsys_discharge_limit_mol = BS_CS_THRESHOLD_NO_CURRENT_mA;
        batsys_discharge_limit_diag_mol = DIAG_CH_OVERCURRENT_PL_NONE;
    }

    /* check limits of battery system */
    if (i_dir == BMS_CHARGING) {
        /* Charge */
        if (i_current_abs >= batsys_charge_limit_mol) {
            /* Over current maximum operating limit of batsys violated */
            DIAG_Handler(batsys_charge_limit_diag_mol, DIAG_EVENT_NOK, 0);
            if (i_current_abs >= batsys_charge_limit_rsl) {
                /* Over current recommended safety limit of batsys violated */
                DIAG_Handler(batsys_charge_limit_diag_rsl, DIAG_EVENT_NOK, 0);
                if (i_current_abs >= batsys_charge_limit_msl) {
                    /* Over current maximum safety limit of batsys violated */
                    DIAG_Handler(batsys_charge_limit_diag_msl, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (i_current_abs < batsys_charge_limit_msl) {
            /* Over current maximum safety limit of batsys NOT violated */
            DIAG_Handler(batsys_charge_limit_diag_msl, DIAG_EVENT_OK, 0);
            if (i_current_abs < batsys_charge_limit_rsl) {
                /* Over current recommended safety limit of batsys NOT violated */
                DIAG_Handler(batsys_charge_limit_diag_rsl, DIAG_EVENT_OK, 0);
                if (i_current_abs < batsys_charge_limit_mol) {
                    /* Over current maximum operating limit of batsys NOT violated */
                    DIAG_Handler(batsys_charge_limit_diag_mol, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else if (i_dir == BMS_DISCHARGING) {
        /* Discharge */
        if (i_current_abs >= batsys_discharge_limit_mol) {
            /* Over current maximum operating limit of batsys violated */
            DIAG_Handler(batsys_discharge_limit_diag_mol, DIAG_EVENT_NOK, 0);
            if (i_current_abs >= batsys_discharge_limit_rsl) {
                /* Over current recommended safety limit of batsys violated */
                DIAG_Handler(batsys_discharge_limit_diag_rsl, DIAG_EVENT_NOK, 0);
                if (i_current_abs >= batsys_discharge_limit_msl) {
                    /* Over current maximum safety limit of batsys violated */
                    DIAG_Handler(batsys_discharge_limit_diag_msl, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (i_current_abs < batsys_discharge_limit_msl) {
            /* Over current maximum safety limit of batsys NOT violated */
            DIAG_Handler(batsys_discharge_limit_diag_msl, DIAG_EVENT_OK, 0);
            if (i_current_abs < batsys_discharge_limit_rsl) {
                /* Over current recommended safety limit of batsys NOT violated */
                DIAG_Handler(batsys_discharge_limit_diag_rsl, DIAG_EVENT_OK, 0);
                if (i_current_abs < batsys_discharge_limit_mol) {
                    /* Over current maximum operating limit of batsys NOT violated */
                    DIAG_Handler(batsys_discharge_limit_diag_mol, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else {
        /* BS_CURRENT_NO_CURRENT -> no violations */
        DIAG_Handler(DIAG_CH_OVERCURRENT_PL_NONE, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_PL0_MSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_PL0_RSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_PL0_MOL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_PL1_MSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_PL1_RSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_PL1_MOL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_PL0_MSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_PL0_RSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_PL0_MOL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_PL1_MSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_PL1_RSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_PL1_MOL, DIAG_EVENT_OK, 0);
    }

    /* check limits of cells */
#if MEAS_TEST_CELL_SOF_LIMITS == TRUE
    if (i_dir == BMS_CHARGING) {
        /* Charge */
        if (i_current_abs >= bms_tab_sof.continuous_charge_MOL) {
            /* Over current maximum operating limit violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MOL, DIAG_EVENT_NOK, 0);
            if (i_current_abs >= bms_tab_sof.continuous_charge_RSL) {
                /* Over current recommended safety limit violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_RSL, DIAG_EVENT_NOK, 0);
                if (i_current_abs >= bms_tab_sof.continuous_charge_MSL) {
                    /* Over current maximum safety limit violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (i_current_abs < bms_tab_sof.continuous_charge_MSL) {
            /* over current maximum safety limit NOT violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MSL, DIAG_EVENT_OK, 0);
            if (i_current_abs < bms_tab_sof.continuous_charge_RSL) {
                /* over current recommended safety limit NOT violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_RSL, DIAG_EVENT_OK, 0);
                if (i_current_abs < bms_tab_sof.continuous_charge_MOL) {
                    /* over current maximum operating limit NOT violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else if (i_dir == BMS_DISCHARGING) {
        /* Discharge */
        if (i_current_abs >= bms_tab_sof.continuous_discharge_MOL) {
            /* Over current maximum operating limit violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MOL, DIAG_EVENT_NOK, 0);
            if (i_current_abs >= bms_tab_sof.continuous_discharge_RSL) {
                /* Over current recommended safety limit violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_RSL, DIAG_EVENT_NOK, 0);
                if (i_current_abs >= bms_tab_sof.continuous_discharge_MSL) {
                    /* Over current error */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }

        if (i_current_abs < bms_tab_sof.continuous_discharge_MSL) {
            /* over current maximum safety limit NOT violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MSL, DIAG_EVENT_OK, 0);
            if (i_current_abs < bms_tab_sof.continuous_discharge_RSL) {
                /* over current recommended safety limit NOT violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_RSL, DIAG_EVENT_OK, 0);
                if (i_current_abs < bms_tab_sof.continuous_discharge_MOL) {
                    /* over current maximum operating limit NOT violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else {
        /* BS_CURRENT_NO_CURRENT -> no check needed if no current is floating */
    }
#else /* MEAS_TEST_CELL_SOF_LIMITS == FALSE */
    if (i_dir == BMS_CHARGING) {
        /* Charge */
        if (i_current_abs >= BC_CURRENTMAX_CHARGE_MOL) {
            /* Over current maximum operating limit of cells violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MOL, DIAG_EVENT_NOK, 0);
            if (i_current_abs >= BC_CURRENTMAX_CHARGE_RSL) {
                /* Over current recommended safety limit of cells violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_RSL, DIAG_EVENT_NOK, 0);
                if (i_current_abs >= BC_CURRENTMAX_CHARGE_MSL) {
                    /* Over current maximum safety limit of cells violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (i_current_abs < BC_CURRENTMAX_CHARGE_MSL) {
            /* Over current maximum safety limit of cells NOT violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MSL, DIAG_EVENT_OK, 0);
            if (i_current_abs < BC_CURRENTMAX_CHARGE_RSL) {
                /* Over current recommended safety limit of cells NOT violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_RSL, DIAG_EVENT_OK, 0);
                if (i_current_abs < BC_CURRENTMAX_CHARGE_MOL) {
                    /* Over current maximum operating limit of cells NOT violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else if (i_dir == BMS_DISCHARGING) {
        /* Discharge */
        if (i_current_abs >= BC_CURRENTMAX_DISCHARGE_MOL) {
            /* Over current maximum operating limit of cells violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MOL, DIAG_EVENT_NOK, 0);
            if (i_current_abs >= BC_CURRENTMAX_DISCHARGE_RSL) {
                /* Over current recommended safety limit of cells violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_RSL, DIAG_EVENT_NOK, 0);
                if (i_current_abs >= BC_CURRENTMAX_DISCHARGE_MSL) {
                    /* Over current maximum safety limit of cells violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MSL, DIAG_EVENT_NOK, 0);
                }
            }
        }
        if (i_current_abs < BC_CURRENTMAX_DISCHARGE_MSL) {
            /* Over current maximum safety limit of cells NOT violated */
            DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MSL, DIAG_EVENT_OK, 0);
            if (i_current_abs < BC_CURRENTMAX_DISCHARGE_RSL) {
                /* Over current recommended safety limit of cells NOT violated */
                DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_RSL, DIAG_EVENT_OK, 0);
                if (i_current_abs < BC_CURRENTMAX_DISCHARGE_MOL) {
                    /* Over current maximum operating limit of cells NOT violated */
                    DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MOL, DIAG_EVENT_OK, 0);
                }
            }
        }
    } else {
        /* BS_CURRENT_NO_CURRENT -> no violations */
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_RSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_CHARGE_CELL_MOL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_RSL, DIAG_EVENT_OK, 0);
        DIAG_Handler(DIAG_CH_OVERCURRENT_DISCHARGE_CELL_MOL, DIAG_EVENT_OK, 0);
    }
#endif /* MEAS_TEST_CELL_SOF_LIMITS == TRUE */
}

/**
 * @brief   FOR FUTURE COMPATIBILITY; DUMMY FUNCTION; DO NOT USE
 *
 * @details FOR FUTURE COMPATIBILITY; DUMMY FUNCTION; DO NOT USE
 */
static void BMS_CheckSlaveTemperatures(void) {
    /* TODO: to be implemented */
}


/**
 * @brief   Check for any open voltage sense wire
 */
static void BMS_CheckOpenSenseWire(void) {
    uint8_t openWireDetected = 0;

    /* Iterate over all modules */
    for (uint8_t m = 0; m < BS_NR_OF_MODULES; m++) {
        /* Iterate over all voltage sense wires: cells per module + 1 */
        for (uint8_t wire = 0; wire < (BS_NR_OF_BAT_CELLS_PER_MODULE + 1); wire++) {
            /* open wire detected */
            if (bms_ow_tab.openwire[wire + m*(BS_NR_OF_BAT_CELLS_PER_MODULE + 1) == 1]) {
                openWireDetected++;

                /* Add additional error handling here */
            }
        }
    }
    /* Set error if open wire detected */
    if (openWireDetected == 0u) {
        DIAG_Handler(DIAG_CH_OPEN_WIRE, DIAG_EVENT_OK, 0);
    } else {
        DIAG_Handler(DIAG_CH_OPEN_WIRE, DIAG_EVENT_NOK, 0);
    }
}


/**
 * @brief   Checks the error flags
 *
 * @details Checks all the error flags from the database and returns an error if at least one is set.
 *
 * @return  E_OK if no error flag is set, otherwise E_NOT_OK
 */
static STD_RETURN_TYPE_e BMS_CheckAnyErrorFlagSet(void) {
    STD_RETURN_TYPE_e retVal = E_OK;  /* is set to E_NOT_OK if error detected */
    DATA_BLOCK_ERRORSTATE_s error_flags;
    DATA_BLOCK_MSL_FLAG_s msl_flags;

    DB_ReadBlock(&error_flags, DATA_BLOCK_ID_ERRORSTATE);
    DB_ReadBlock(&msl_flags, DATA_BLOCK_ID_MSL);

    /* Check maximum safety limit flags */
    if (msl_flags.over_current_charge_cell    == 1 ||
        msl_flags.over_current_charge_pl0     == 1 ||
        msl_flags.over_current_charge_pl1     == 1 ||
        msl_flags.over_current_discharge_cell == 1 ||
        msl_flags.over_current_discharge_pl0  == 1 ||
        msl_flags.over_current_discharge_pl1  == 1 ||
        msl_flags.over_voltage                == 1 ||
        msl_flags.under_voltage               == 1 ||
        msl_flags.over_temperature_charge     == 1 ||
        msl_flags.over_temperature_discharge  == 1 ||
        msl_flags.under_temperature_charge    == 1 ||
        msl_flags.under_temperature_discharge == 1) {
        /* error detected */
        retVal = E_NOT_OK;
    }

    /* Check system error flags */
    if (error_flags.currentOnOpenPowerline    == 1 ||
        error_flags.deepDischargeDetected     == 1 ||
        error_flags.main_plus                 == 1 ||
        error_flags.main_minus                == 1 ||
        error_flags.precharge                 == 1 ||
        error_flags.charge_main_plus          == 1 ||
        error_flags.charge_main_minus         == 1 ||
        error_flags.charge_precharge          == 1 ||
        error_flags.fuse_state_normal         == 1 ||
        error_flags.fuse_state_charge         == 1 ||
        error_flags.interlock                 == 1 ||
        error_flags.crc_error                 == 1 ||
        error_flags.mux_error                 == 1 ||
        error_flags.spi_error                 == 1 ||
        error_flags.ltc_config_error          == 1 ||
        error_flags.currentsensorresponding   == 1 ||
        error_flags.open_wire                 == 1 ||
#if BMS_OPEN_CONTACTORS_ON_INSULATION_ERROR == TRUE
        error_flags.insulation_error          == 1 ||
#endif /* BMS_OPEN_CONTACTORS_ON_INSULATION_ERROR */
        error_flags.can_timing_cc             == 1 ||
        error_flags.can_timing                == 1) {
        /* error detected */
        retVal = E_NOT_OK;
    }

    return retVal;
}


/**
 * @brief   Updates battery system state variable depending on measured/recent
 *          current values
 *
 * @param   curSensor   recent measured values from current sensor
 */
static void BMS_UpdateBatsysState(DATA_BLOCK_CURRENT_SENSOR_s *curSensor) {
    if (POSITIVE_DISCHARGE_CURRENT == TRUE) {
        /* Positive current values equal a discharge of the battery system */
        if (curSensor->current >= BS_REST_CURRENT_mA) {
            bms_state.currentFlowState = BMS_DISCHARGING;
            bms_state.restTimer_ms = BS_RELAXATION_PERIOD_MS;
        } else if (curSensor->current <= -BS_REST_CURRENT_mA) {
            bms_state.currentFlowState = BMS_CHARGING;
            bms_state.restTimer_ms = BS_RELAXATION_PERIOD_MS;
        } else {
            /* Current below rest current: either battery system is at rest
             * or the relaxation process is still ongoing */
            if (bms_state.restTimer_ms == 0) {
                /* Rest timer elapsed -> battery system at rest */
                bms_state.currentFlowState = BMS_AT_REST;
            } else {
                bms_state.restTimer_ms--;
                bms_state.currentFlowState = BMS_RELAXATION;
            }
        }
    } else {
        /* Negative current values equal a discharge of the battery system */
        if (curSensor->current <= -BS_REST_CURRENT_mA) {
            bms_state.currentFlowState = BMS_DISCHARGING;
            bms_state.restTimer_ms = BS_RELAXATION_PERIOD_MS;
        } else if (curSensor->current >= BS_REST_CURRENT_mA) {
            bms_state.currentFlowState = BMS_CHARGING;
            bms_state.restTimer_ms = BS_RELAXATION_PERIOD_MS;
        } else {
            /* Current below rest current: either battery system is at rest
             * or the relaxation process is still ongoing */
            if (bms_state.restTimer_ms == 0) {
                /* Rest timer elapsed -> battery system at rest */
                bms_state.currentFlowState = BMS_AT_REST;
            } else {
                bms_state.restTimer_ms--;
                bms_state.currentFlowState = BMS_RELAXATION;
            }
        }
    }
}


BMS_CURRENT_FLOW_STATE_e BMS_GetBatterySystemState(void) {
    return bms_state.currentFlowState;
}

bms.h (primary)

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms.h
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE
 * @prefix  BMS
 *
 * @brief   bms driver header
 *
 *
 */

#ifndef BMS_H_
#define BMS_H_

/*================== Includes =============================================*/
#include "bms_cfg.h"

/*================== Macros and Definitions ===============================*/

/**
 * Symbolic names for battery system state
 */
typedef enum {
    BMS_CHARGING,     /*!< battery is charged */
    BMS_DISCHARGING,  /*!< battery is discharged */
    BMS_RELAXATION,   /*!< battery relaxation ongoing */
    BMS_AT_REST,      /*!< battery is resting */
} BMS_CURRENT_FLOW_STATE_e;


/**
 * Symbolic names for busyness of the syscontrol
 */
typedef enum {
    BMS_CHECK_OK        = 0,    /*!< syscontrol ok      */
    BMS_CHECK_BUSY      = 1,    /*!< syscontrol busy    */
    BMS_CHECK_NOT_OK    = 2,    /*!< syscontrol not ok  */
} BMS_CHECK_e;


typedef enum {
  BMS_MODE_STARTUP_EVENT    = 0,    /*!< syscontrol startup                 */
/*  BMS_MODE_EVENT_INIT      = 1,*/ /*!< todo                               */
  BMS_MODE_CYCLIC_EVENT     = 2,    /*!< for cyclic events                  */
  BMS_MODE_TRIGGERED_EVENT  = 3,    /*!< for triggered events               */
  BMS_MODE_ABNORMAL_EVENT   = 4,    /*!< for abnormal (error etc.) events   */
  BMS_MODE_EVENT_RESERVED   = 0xFF, /*!< do not use                         */
} BMS_TRIG_EVENT_e;


/**
 * States of the SYS state machine
 */
typedef enum {
    /* Init-Sequence */
    BMS_STATEMACH_UNINITIALIZED             = 0,    /*!<    */
    BMS_STATEMACH_INITIALIZATION            = 1,    /*!<    */
    BMS_STATEMACH_INITIALIZED               = 2,    /*!<    */
    BMS_STATEMACH_IDLE                      = 3,    /*!<    */
    BMS_STATEMACH_STANDBY                   = 4,    /*!<    */
    BMS_STATEMACH_PRECHARGE                 = 5,    /*!<    */
    BMS_STATEMACH_NORMAL                    = 6,    /*!<    */
    BMS_STATEMACH_CHARGE_PRECHARGE          = 7,    /*!<    */
    BMS_STATEMACH_CHARGE                    = 8,    /*!<    */
    BMS_STATEMACH_UNDEFINED                 = 20,   /*!< undefined state                                */
    BMS_STATEMACH_RESERVED1                 = 0x80, /*!< reserved state                                 */
    BMS_STATEMACH_ERROR                     = 0xF0, /*!< Error-State:  */
} BMS_STATEMACH_e;


/**
 * Substates of the SYS state machine
 */
typedef enum {
    BMS_ENTRY                                     = 0,    /*!< Substate entry state       */
    BMS_CHECK_ERROR_FLAGS_INTERLOCK               = 1,    /*!< Substate check measurements after interlock closed       */
    BMS_INTERLOCK_CHECKED                         = 2,    /*!< Substate interlocked checked       */
    BMS_CHECK_STATE_REQUESTS                      = 3,    /*!< Substate check if there is a state request   */
    BMS_CHECK_BALANCING_REQUESTS                  = 4,    /*!< Substate check if there is a balancing request   */
    BMS_CHECK_ERROR_FLAGS                         = 5,    /*!< Substate check if any error flag set   */
    BMS_CHECK_CONTACTOR_NORMAL_STATE              = 6,    /*!< Substate in precharge, check if there contactors reached normal   */
    BMS_CHECK_CONTACTOR_CHARGE_STATE              = 7,    /*!< Substate in precharge, check if there contactors reached normal   */
    BMS_OPEN_INTERLOCK                            = 8,    /*!< Substate in error to open interlock after contactors have been opened   */
    BMS_CHECK_INTERLOCK_CLOSE_AFTER_ERROR         = 9,    /*!< Substate in error to close interlock after all error flags were reset   */
} BMS_STATEMACH_SUB_e;


/**
 * State requests for the SYS statemachine
 */
typedef enum {
    BMS_STATE_INIT_REQUEST                = BMS_STATEMACH_INITIALIZATION,           /*!<    */
    BMS_STATE_ERROR_REQUEST               = BMS_STATEMACH_ERROR,   /*!<    */
    BMS_STATE_NO_REQUEST                  = BMS_STATEMACH_RESERVED1,                /*!<    */
} BMS_STATE_REQUEST_e;


/**
 * Possible return values when state requests are made to the SYS statemachine
 */
typedef enum {
    BMS_OK                                 = 0,    /*!< CONT --> ok                             */
    BMS_BUSY_OK                            = 1,    /*!< CONT under load --> ok                  */
    BMS_REQUEST_PENDING                    = 2,    /*!< requested to be executed               */
    BMS_ILLEGAL_REQUEST                    = 3,    /*!< Request can not be executed            */
    BMS_ALREADY_INITIALIZED                = 30,   /*!< Initialization of LTC already finished */
    BMS_ILLEGAL_TASK_TYPE                  = 99,   /*!< Illegal                                */
} BMS_RETURN_TYPE_e;



/**
 * This structure contains all the variables relevant for the CONT state machine.
 * The user can get the current state of the CONT state machine with this variable
 */
typedef struct {
    uint16_t timer;                             /*!< time in ms before the state machine processes the next state, e.g. in counts of 1ms    */
    BMS_STATE_REQUEST_e statereq;               /*!< current state request made to the state machine                                        */
    BMS_STATEMACH_e state;                      /*!< state of Driver State Machine                                                          */
    BMS_STATEMACH_SUB_e substate;               /*!< current substate of the state machine                                                  */
    BMS_CURRENT_FLOW_STATE_e currentFlowState;  /*!< state of battery system                                                                */
    BMS_STATEMACH_e laststate;                  /*!< previous state of the state machine                                                    */
    BMS_STATEMACH_SUB_e lastsubstate;           /*!< previous substate of the state machine                                                 */
    uint32_t ErrRequestCounter;                 /*!< counts the number of illegal requests to the LTC state machine                         */
    STD_RETURN_TYPE_e initFinished;             /*!< #E_OK if the initialization has passed, #E_NOT_OK otherwise                            */
    uint8_t triggerentry;                       /*!< counter for re-entrance protection (function running flag)                             */
    uint32_t restTimer_ms;                     /*!< timer until battery system is at rest                                                  */
    uint8_t counter;                            /*!< general purpose counter                                                                */
} BMS_STATE_s;


/*================== Function Prototypes ==================================*/
/**
 * @brief   sets the current state request of the state variable bms_state.
 *
 * @details This function is used to make a state request to the state machine,e.g, start voltage
 *          measurement, read result of voltage measurement, re-initialization.
 *          It calls BMS_CheckStateRequest() to check if the request is valid. The state request is
 *          rejected if is not valid. The result of the check is returned immediately, so that the
 *          requester can act in case it made a non-valid state request.
 *
 * @param   statereq    state request to set
 *
 * @return  current state request, taken from BMS_STATE_REQUEST_e
 */
extern BMS_RETURN_TYPE_e BMS_SetStateRequest(BMS_STATE_REQUEST_e statereq);

/**
 * @brief   Returns the current state.
 *
 * @details This function is used in the functioning of the SYS state machine.
 *
 * @return  current state, taken from BMS_STATEMACH_e
 */
extern  BMS_STATEMACH_e BMS_GetState(void);

/**
 * @brief   Gets the initialization state.
 *
 * This function is used for getting the BMS initialization state.
 *
 * @return  #E_OK if initialized, otherwise #E_NOT_OK
 */
STD_RETURN_TYPE_e BMS_GetInitializationState(void);

/**
 * @brief   trigger function for the SYS driver state machine.
 *
 * @details This function contains the sequence of events in the SYS state machine. It must be
 *          called time-triggered, every 1ms.
 */
extern void BMS_Trigger(void);


/**
 * @brief   Returns current battery system state (charging/discharging,
 *          resting or in relaxation phase)
 *
 * @return  BS_CHARGING, BS_DISCHARGING, BS_RELAXATION or BS_AT_REST
 */
extern BMS_CURRENT_FLOW_STATE_e BMS_GetBatterySystemState(void);

#endif /* BMS_H_ */

bms.c (secondary)

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms.c
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE
 * @prefix  BMS
 *
 * @brief   bms driver implementation
 */


/*================== Includes =============================================*/
#include "bms.h"

#include "batterycell_cfg.h"
#include "database.h"
#include "diag.h"
#include "interlock.h"
#include "os.h"


/*================== Macros and Definitions ===============================*/

/**
 * Saves the last state and the last substate
 */
#define BMS_SAVELASTSTATES()    bms_state.laststate = bms_state.state; \
                                bms_state.lastsubstate = bms_state.substate;

/*================== Constant and Variable Definitions ====================*/

/**
 * contains the state of the contactor state machine
 */
static BMS_STATE_s bms_state = {
    .timer                  = 0,
    .statereq               = BMS_STATE_NO_REQUEST,
    .state                  = BMS_STATEMACH_UNINITIALIZED,
    .substate               = BMS_ENTRY,
    .laststate              = BMS_STATEMACH_UNINITIALIZED,
    .lastsubstate           = 0,
    .triggerentry           = 0,
    .ErrRequestCounter      = 0,
    .initFinished           = E_NOT_OK,
    .counter                = 0,
};

static DATA_BLOCK_MINMAX_s bms_tab_minmax;

/*================== Function Prototypes ==================================*/

static BMS_RETURN_TYPE_e BMS_CheckStateRequest(BMS_STATE_REQUEST_e statereq);
static BMS_STATE_REQUEST_e BMS_GetStateRequest(void);
static BMS_STATE_REQUEST_e BMS_TransferStateRequest(void);
static uint8_t BMS_CheckReEntrance(void);
static uint8_t BMS_CheckCANRequests(void);
static STD_RETURN_TYPE_e BMS_CheckAnyErrorFlagSet(void);
static void BMS_GetMeasurementValues(void);
static void BMS_CheckVoltages(void);
static void BMS_CheckTemperatures(void);
static void BMS_CheckSlaveTemperatures(void);
/*================== Function Implementations =============================*/

/**
 * @brief   re-entrance check of SYS state machine trigger function
 *
 * @details This function is not re-entrant and should only be called time- or event-triggered. It
 *          increments the triggerentry counter from the state variable ltc_state. It should never
 *          be called by two different processes, so if it is the case, triggerentry should never
 *          be higher than 0 when this function is called.
 *
 * @return  retval  0 if no further instance of the function is active, 0xff else
 */
static uint8_t BMS_CheckReEntrance(void) {
    uint8_t retval = 0;
    OS_TaskEnter_Critical();
    if (!bms_state.triggerentry) {
        bms_state.triggerentry++;
    } else {
        retval = 0xFF;  /* multiple calls of function */
    }
    OS_TaskExit_Critical();
    return (retval);
}

/**
 * @brief   gets the current state request.
 *
 * @details This function is used in the functioning of the SYS state machine.
 *
 * @return  current state request, taken from BMS_STATE_REQUEST_e
 */
static BMS_STATE_REQUEST_e BMS_GetStateRequest(void) {
    BMS_STATE_REQUEST_e retval = BMS_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retval    = bms_state.statereq;
    OS_TaskExit_Critical();

    return (retval);
}


BMS_STATEMACH_e BMS_GetState(void) {
    return (bms_state.state);
}


STD_RETURN_TYPE_e BMS_GetInitializationState(void) {
    return (bms_state.initFinished);
}


/**
 * @brief   transfers the current state request to the state machine.
 *
 * @details This function takes the current state request from cont_state and transfers it to th
 *          state machine. It resets the value from cont_state to BMS_STATE_NO_REQUEST
 *
 * @return  retVal          current state request, taken from BMS_STATE_REQUEST_e
 */
static BMS_STATE_REQUEST_e BMS_TransferStateRequest(void) {
    BMS_STATE_REQUEST_e retval = BMS_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retval    = bms_state.statereq;
    bms_state.statereq = BMS_STATE_NO_REQUEST;
    OS_TaskExit_Critical();
    return (retval);
}



BMS_RETURN_TYPE_e BMS_SetStateRequest(BMS_STATE_REQUEST_e statereq) {
    BMS_RETURN_TYPE_e retVal = BMS_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retVal = BMS_CheckStateRequest(statereq);

    if (retVal == BMS_OK) {
            bms_state.statereq = statereq;
    }
    OS_TaskExit_Critical();

    return (retVal);
}

/**
 * @brief   checks the state requests that are made.
 *
 * @details This function checks the validity of the state requests. The results of the checked is
 *          returned immediately.
 *
 * @param   statereq    state request to be checked
 *
 * @return  result of the state request that was made, taken from BMS_RETURN_TYPE_e
 */
static BMS_RETURN_TYPE_e BMS_CheckStateRequest(BMS_STATE_REQUEST_e statereq) {
    if (statereq == BMS_STATE_ERROR_REQUEST) {
        return BMS_OK;
    }

    if (bms_state.statereq == BMS_STATE_NO_REQUEST) {
        /* init only allowed from the uninitialized state */
        if (statereq == BMS_STATE_INIT_REQUEST) {
            if (bms_state.state == BMS_STATEMACH_UNINITIALIZED) {
                return BMS_OK;
            } else {
                return BMS_ALREADY_INITIALIZED;
            }
        } else {
            return BMS_ILLEGAL_REQUEST;
        }
    } else {
        return BMS_REQUEST_PENDING;
    }
}

void BMS_Trigger(void) {
    BMS_STATE_REQUEST_e statereq = BMS_STATE_NO_REQUEST;

    DIAG_SysMonNotify(DIAG_SYSMON_BMS_ID, 0);  /* task is running, state = ok */

    if (bms_state.state != BMS_STATEMACH_UNINITIALIZED) {
        BMS_GetMeasurementValues();
        BMS_CheckVoltages();
        BMS_CheckTemperatures();
    }
    /* Check re-entrance of function */
    if (BMS_CheckReEntrance()) {
        return;
    }

    if (bms_state.timer) {
        if (--bms_state.timer) {
            bms_state.triggerentry--;
            return;    /* handle state machine only if timer has elapsed */
        }
    }

    /****Happens every time the state machine is triggered**************/
    switch (bms_state.state) {
        /****************************UNINITIALIZED***********************************/
        case BMS_STATEMACH_UNINITIALIZED:
            /* waiting for Initialization Request */
            statereq = BMS_TransferStateRequest();
            if (statereq == BMS_STATE_INIT_REQUEST) {
                BMS_SAVELASTSTATES();
                bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                bms_state.state = BMS_STATEMACH_INITIALIZATION;
                bms_state.substate = BMS_ENTRY;
            } else if (statereq == BMS_STATE_NO_REQUEST) {
                /* no actual request pending */
            } else {
                bms_state.ErrRequestCounter++;  /* illegal request pending */
            }
            break;


        /****************************INITIALIZATION**********************************/
        case BMS_STATEMACH_INITIALIZATION:
            BMS_SAVELASTSTATES();

            bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
            bms_state.state = BMS_STATEMACH_INITIALIZED;
            bms_state.substate = BMS_ENTRY;

            break;

        /****************************INITIALIZED*************************************/
        case BMS_STATEMACH_INITIALIZED:
            BMS_SAVELASTSTATES();
            bms_state.initFinished = E_OK;
            bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
            bms_state.state = BMS_STATEMACH_STANDBY;
            bms_state.substate = BMS_ENTRY;
            break;


        /****************************STANDBY*************************************/
        case BMS_STATEMACH_STANDBY:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
                ILCK_SetStateRequest(ILCK_STATE_CLOSE_REQUEST);
                bms_state.timer = BMS_STATEMACH_MEDIUMTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS_INTERLOCK;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS_INTERLOCK) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                } else {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.substate = BMS_INTERLOCK_CHECKED;
                    break;
                }
            } else if (bms_state.substate == BMS_INTERLOCK_CHECKED) {
                bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    bms_state.timer = BMS_STATEMACH_SHORTTIME_MS;
                    bms_state.state = BMS_STATEMACH_ERROR;
                    bms_state.substate = BMS_ENTRY;
                    break;
                }
            }
            break;

        /****************************ERROR*************************************/
        case BMS_STATEMACH_ERROR:
            BMS_SAVELASTSTATES();

            if (bms_state.substate == BMS_ENTRY) {
                ILCK_SetStateRequest(ILCK_STATE_OPEN_REQUEST);
                bms_state.timer = BMS_STATEMACH_MEDIUMTIME_MS;
                bms_state.substate = BMS_CHECK_ERROR_FLAGS;
                break;
            } else if (bms_state.substate == BMS_CHECK_ERROR_FLAGS) {
                if (BMS_CheckAnyErrorFlagSet() == E_NOT_OK) {
                    /* we stay already in requested state, nothing to do */
                } else {
                    if (SECONDARY_OUT_OF_ERROR_STATE == TRUE) {
                        ILCK_SetStateRequest(ILCK_STATE_CLOSE_REQUEST);
                        bms_state.timer = BMS_STATEMACH_MEDIUMTIME_MS;
                        bms_state.substate = BMS_CHECK_INTERLOCK_CLOSE_AFTER_ERROR;
                        bms_state.state = BMS_STATEMACH_STANDBY;
                        bms_state.substate = BMS_ENTRY;
                    }
                    break;
                }
            }
            break;
        default:
            break;
    }  /* end switch (bms_state.state) */

    bms_state.triggerentry--;
    bms_state.counter++;
}

/*================== Static functions =====================================*/

static void BMS_GetMeasurementValues(void) {
    DB_ReadBlock(&bms_tab_minmax, DATA_BLOCK_ID_MINMAX);
}

static uint8_t BMS_CheckCANRequests(void) {
    uint8_t retVal = BMS_REQ_ID_NOREQ;
    DATA_BLOCK_STATEREQUEST_s request;

    DB_ReadBlock(&request, DATA_BLOCK_ID_STATEREQUEST);

    if (request.state_request == BMS_REQ_ID_STANDBY) {
        retVal = BMS_REQ_ID_STANDBY;
    } else if (request.state_request == BMS_REQ_ID_NORMAL) {
        retVal = BMS_REQ_ID_NORMAL;
    }

    return retVal;
}




/**
 * @brief   checks the abidance by the safe operating area
 *
 * @details verify for cell voltage measurements (U), if minimum and maximum values are out of range
 */
static void BMS_CheckVoltages(void) {
    /* Check over- and undervoltage limits */
    if (bms_tab_minmax.voltage_max > BC_VOLTMAX_MSL) {
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_MSL, DIAG_EVENT_NOK, 0);
    } else {
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_OVERVOLTAGE_MSL, DIAG_EVENT_OK, 0);
    }

    if (bms_tab_minmax.voltage_min < BC_VOLTMIN_MSL) {
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_MSL, DIAG_EVENT_NOK, 0);
    } else {
        DIAG_Handler(DIAG_CH_CELLVOLTAGE_UNDERVOLTAGE_MSL, DIAG_EVENT_OK, 0);
    }
}


/**
 * @brief   checks the abidance by the safe operating area
 *
 * @details verify for cell temperature measurements (T), if minimum and maximum values are out of range
 */
static void BMS_CheckTemperatures(void) {
    /* Check over- and undertemperature. As no information about the current
     * direction is available, only the maximum values of charge/discharge
     * values are checked. This means if different charge and discharge
     * temperature limits are used, only the maximum of the two values is
     * monitored to prevent an unwanted opening of the contactors. */
#if BC_TEMPMAX_DISCHARGE_MSL >= BC_TEMPMAX_CHARGE_MSL
    if (bms_tab_minmax.temperature_max > BC_TEMPMAX_DISCHARGE_MSL) {
        DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_NOK, 0);
    } else {
        DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_OK, 0);
    }
#else /* BC_TEMPMAX_DISCHARGE_MSL < BC_TEMPMAX_CHARGE_MSL */
    if (bms_tab_minmax.temperature_max > BC_TEMPMAX_CHARGE_MSL) {
        DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_NOK, 0);
    } else {
        DIAG_Handler(DIAG_CH_TEMP_OVERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_OK, 0);
    }
#endif /* BC_TEMPMAX_DISCHARGE_MSL >= BC_TEMPMAX_CHARGE_MSL */

#if BC_TEMPMIN_DISCHARGE_MSL <= BC_TEMPMIN_CHARGE_MSL
    if (bms_tab_minmax.temperature_min < BC_TEMPMIN_DISCHARGE_MSL) {
        DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_NOK, 0);
    } else {
        DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_DISCHARGE_MSL, DIAG_EVENT_OK, 0);
    }
#else /* BC_TEMPMIN_DISCHARGE_MSL > BC_TEMPMIN_CHARGE_MSL */
    if (bms_tab_minmax.temperature_min < BC_TEMPMIN_CHARGE_MSL) {
        DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_NOK, 0);
    } else {
        DIAG_Handler(DIAG_CH_TEMP_UNDERTEMPERATURE_CHARGE_MSL, DIAG_EVENT_OK, 0);
    }
#endif /* BC_TEMPMIN_DISCHARGE_MSL <= BC_TEMPMIN_CHARGE_MSL */
}

/**
 * @brief   FOR FUTURE COMPATIBILITY; DUMMY FUNCTION; DO NOT USE
 *
 * @details FOR FUTURE COMPATIBILITY; DUMMY FUNCTION; DO NOT USE
 */
static void BMS_CheckSlaveTemperatures(void) {
    /* TODO: to be implemented */
}

/**
 * @brief   Checks the error flags
 *
 * @details Checks all the error flags from the database and returns an error if at least one is set.
 *
 * @return  E_OK if no error flag is set, otherwise E_NOT_OK
 */
static STD_RETURN_TYPE_e BMS_CheckAnyErrorFlagSet(void) {
    STD_RETURN_TYPE_e retVal = E_OK;  /* is set to E_NOT_OK if error detected */
    DATA_BLOCK_ERRORSTATE_s error_flags;
    DATA_BLOCK_MSL_FLAG_s msl_flags;

    DB_ReadBlock(&error_flags, DATA_BLOCK_ID_ERRORSTATE);
    DB_ReadBlock(&msl_flags, DATA_BLOCK_ID_MSL);

    /* Check maximum safety limit flags */
    if (msl_flags.over_current_charge         == 1 ||
        msl_flags.over_current_discharge      == 1 ||
        msl_flags.over_voltage                == 1 ||
        msl_flags.under_voltage               == 1 ||
        msl_flags.over_temperature_charge     == 1 ||
        msl_flags.over_temperature_discharge  == 1 ||
        msl_flags.under_temperature_charge    == 1 ||
        msl_flags.under_temperature_discharge == 1) {
        retVal = E_NOT_OK;
        msl_flags.general_MSL = 1;
    } else {
        msl_flags.general_MSL = 0;
    }

    /* Check system error flags */
    if (error_flags.main_plus                 == 1 ||
        error_flags.main_minus                == 1 ||
        error_flags.precharge                 == 1 ||
        error_flags.charge_main_plus          == 1 ||
        error_flags.charge_main_minus         == 1 ||
        error_flags.charge_precharge          == 1 ||
        error_flags.interlock                 == 1 ||
        error_flags.crc_error                 == 1 ||
        error_flags.mux_error                 == 1 ||
        error_flags.spi_error                 == 1 ||
        error_flags.currentsensorresponding   == 1 ||
        error_flags.can_timing_cc             == 1 ||
        error_flags.can_timing                == 1) {
        retVal = E_NOT_OK;
        error_flags.general_error = 1;
    } else {
        error_flags.general_error = 0;
    }
    /* Save values back to DB */
    DB_WriteBlock(&error_flags, DATA_BLOCK_ID_ERRORSTATE);
    DB_WriteBlock(&msl_flags, DATA_BLOCK_ID_MSL);

    return retVal;
}

bms.h (secondary)

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms.h
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE
 * @prefix  BMS
 *
 * @brief   bms driver header
 *
 *
 */

#ifndef BMS_H_
#define BMS_H_

/*================== Includes =============================================*/
#include "bms_cfg.h"

/*================== Macros and Definitions ===============================*/

/**
 * Symbolic names for busyness of the syscontrol
 */
typedef enum {
    BMS_CHECK_OK        = 0,    /*!< syscontrol ok      */
    BMS_CHECK_BUSY      = 1,    /*!< syscontrol busy    */
    BMS_CHECK_NOT_OK    = 2,    /*!< syscontrol not ok  */
} BMS_CHECK_e;



typedef enum {
  BMS_MODE_STARTUP_EVENT    = 0,    /*!< syscontrol startup                 */
/* BMS_MODE_EVENT_INIT      = 1,  */  /*!< todo                               */
  BMS_MODE_CYCLIC_EVENT     = 2,    /*!< for cyclic events                  */
  BMS_MODE_TRIGGERED_EVENT  = 3,    /*!< for triggered events               */
  BMS_MODE_ABNORMAL_EVENT   = 4,    /*!< for abnormal (error etc.) events   */
  BMS_MODE_EVENT_RESERVED   = 0xFF, /*!< do not use                         */
} BMS_TRIG_EVENT_e;


/*================== Function Implementations =============================*/

/*================== Includes =============================================*/

/*================== Macros and Definitions ===============================*/

/*================== Constant and Variable Definitions ====================*/

/*================== Function Prototypes ==================================*/

/*================== Function Implementations =============================*/


/*================== Constant and Variable Definitions ====================*/

/**
 * States of the SYS state machine
 */
typedef enum {
    /*  Init-Sequence */
    BMS_STATEMACH_UNINITIALIZED             = 0,    /*!<    */
    BMS_STATEMACH_INITIALIZATION            = 1,    /*!<    */
    BMS_STATEMACH_INITIALIZED               = 2,    /*!<    */
    BMS_STATEMACH_IDLE                      = 3,    /*!<    */
    BMS_STATEMACH_STANDBY                   = 4,    /*!<    */
    BMS_STATEMACH_PRECHARGE                 = 5,    /*!<    */
    BMS_STATEMACH_NORMAL                    = 6,    /*!<    */
    BMS_STATEMACH_CHARGE_PRECHARGE          = 7,    /*!<    */
    BMS_STATEMACH_CHARGE                    = 8,    /*!<    */
    BMS_STATEMACH_UNDEFINED                 = 20,   /*!< undefined state                                */
    BMS_STATEMACH_RESERVED1                 = 0x80, /*!< reserved state                                 */
    BMS_STATEMACH_ERROR                     = 0xF0, /*!< Error-State:  */
} BMS_STATEMACH_e;


/**
 * Substates of the SYS state machine
 */
typedef enum {
    BMS_ENTRY                                     = 0,    /*!< Substate entry state       */
    BMS_CHECK_ERROR_FLAGS_INTERLOCK               = 1,    /*!< Substate check measurements after interlock closed       */
    BMS_INTERLOCK_CHECKED                         = 2,    /*!< Substate interlocked checked       */
    BMS_CHECK_STATE_REQUESTS                      = 3,    /*!< Substate check if there is a state request   */
    BMS_CHECK_ERROR_FLAGS                         = 4,    /*!< Substate check if any error flag set   */
    BMS_CHECK_CONTACTOR_NORMAL_STATE              = 5,    /*!< Substate in precharge, check if there contactors reached normal   */
    BMS_CHECK_CONTACTOR_CHARGE_STATE              = 6,    /*!< Substate in precharge, check if there contactors reached normal   */
    BMS_OPEN_INTERLOCK                            = 7,    /*!< Substate in error to open interlock after contactors have been opened   */
    BMS_CHECK_INTERLOCK_CLOSE_AFTER_ERROR         = 8,    /*!< Substate in error to close interlock after all error flags were reset   */
} BMS_STATEMACH_SUB_e;


/**
 * State requests for the SYS statemachine
 */
typedef enum {
    BMS_STATE_INIT_REQUEST                = BMS_STATEMACH_INITIALIZATION,           /*!<    */
    BMS_STATE_ERROR_REQUEST               = BMS_STATEMACH_ERROR,   /*!<    */
    BMS_STATE_NO_REQUEST                  = BMS_STATEMACH_RESERVED1,                /*!<    */
} BMS_STATE_REQUEST_e;


/**
 * Possible return values when state requests are made to the SYS statemachine
 */
typedef enum {
    BMS_OK                                 = 0,    /*!< CONT --> ok                             */
    BMS_BUSY_OK                            = 1,    /*!< CONT under load --> ok                  */
    BMS_REQUEST_PENDING                    = 2,    /*!< requested to be executed               */
    BMS_ILLEGAL_REQUEST                    = 3,    /*!< Request can not be executed            */
    BMS_ALREADY_INITIALIZED                = 30,   /*!< Initialization of LTC already finished */
    BMS_ILLEGAL_TASK_TYPE                  = 99,   /*!< Illegal                                */
} BMS_RETURN_TYPE_e;



/**
 * This structure contains all the variables relevant for the CONT state machine.
 * The user can get the current state of the CONT state machine with this variable
 */
typedef struct {
    uint16_t timer;                         /*!< time in ms before the state machine processes the next state, e.g. in counts of 1ms    */
    BMS_STATE_REQUEST_e statereq;           /*!< current state request made to the state machine                                        */
    BMS_STATEMACH_e state;                  /*!< state of Driver State Machine                                                          */
    BMS_STATEMACH_SUB_e substate;           /*!< current substate of the state machine                                                  */
    BMS_STATEMACH_e laststate;              /*!< previous state of the state machine                                                    */
    BMS_STATEMACH_SUB_e lastsubstate;       /*!< previous substate of the state machine                                                 */
    uint32_t ErrRequestCounter;             /*!< counts the number of illegal requests to the LTC state machine                         */
    STD_RETURN_TYPE_e initFinished;         /*!< #E_OK if the initialization has passed, #E_NOT_OK otherwise                            */
    uint8_t triggerentry;                   /*!< counter for re-entrance protection (function running flag)                             */
    uint8_t counter;                        /*!< general purpose counter                                                                */
} BMS_STATE_s;


/*================== Function Prototypes ==================================*/
/**
 * @brief   sets the current state request of the state variable bms_state.
 *
 * @details This function is used to make a state request to the state machine,e.g, start voltage
 *          measurement, read result of voltage measurement, re-initialization.
 *          It calls BMS_CheckStateRequest() to check if the request is valid. The state request is
 *          rejected if is not valid. The result of the check is returned immediately, so that the
 *          requester can act in case it made a non-valid state request.
 *
 * @param   statereq    state request to set
 *
 * @return  current state request, taken from BMS_STATE_REQUEST_e
 */
extern BMS_RETURN_TYPE_e BMS_SetStateRequest(BMS_STATE_REQUEST_e statereq);

/**
 * @brief   Returns the current state.
 *
 * @details This function is used in the functioning of the SYS state machine.
 *
 * @return  current state, taken from BMS_STATEMACH_e
 */
extern  BMS_STATEMACH_e BMS_GetState(void);

/**
 * @brief   Gets the initialization state.
 *
 * This function is used for getting the BMS initialization state.
 *
 * @return  #E_OK if initialized, otherwise #E_NOT_OK
 */
STD_RETURN_TYPE_e BMS_GetInitializationState(void);

/**
 * @brief   trigger function for the SYS driver state machine.
 *
 * @details This function contains the sequence of events in the SYS state machine. It must be
 *          called time-triggered, every 1ms.
 */
extern void BMS_Trigger(void);


#endif /* BMS_H_ */

bms_cfg.c

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms_cfg.c
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE_CONF
 * @prefix  BMS
 *
 * @brief   bms driver configuration
 */

/*================== Includes =============================================*/
#include "bms_cfg.h"

/*================== Macros and Definitions ===============================*/

/*================== Function Prototypes ==================================*/

/*================== Function Implementations =============================*/

bms_cfg.h

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms_cfg.h
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE_CONF
 * @prefix  BMS
 *
 * @brief   bms driver configuration header
 */

#ifndef BMS_CFG_H_
#define BMS_CFG_H_

/*================== Includes =============================================*/
#include "general.h"

#include "contactor.h"

/*================== Macros and Definitions ===============================*/

#define BMS_REQ_ID_NOREQ 0

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN signal to go to STANDBY state (ready, but no contactors closed)
 * \par Type:
 * int
 * \par Default:
 * 8
 * \par Range:
 * 0<=x
*/
#define BMS_REQ_ID_STANDBY 8

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN signal to go to NORMAL state (contactors closing procedure)
 * \par Type:
 * int
 * \par Default:
 * 3
 * \par Range:
 * 0<=x
*/
#define BMS_REQ_ID_NORMAL 3

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN signal to go to CHARGE state (contactors closing procedure)
 * \par Type:
 * int
 * \par Default:
 * 4
 * \par Range:
 * 0<=x
*/
#define BMS_REQ_ID_CHARGE 4



#define BMS_BAL_NO_REQUEST 0

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN to deactivate balancing with override
 * \par Type:
 * int
 * \par Default:
 * 8
 * \par Range:
 * 0<=x
*/
#define BMS_BAL_INACTIVE_OVERRIDE 1

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN to go out of override mode
 * \par Type:
 * int
 * \par Default:
 * 8
 * \par Range:
 * 0<=x
*/
#define BMS_BAL_OUT_OF_OVERRIDE 2

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN to activate balancing with override
 * \par Type:
 * int
 * \par Default:
 * 8
 * \par Range:
 * 0<=x
*/
#define BMS_BAL_ACTIVE_OVERRIDE 3


/**
 * BMS statemachine short time definition in ms
 */
#define BMS_STATEMACH_SHORTTIME_MS 1

/**
 * BMS statemachine medium time definition in ms
 */
#define BMS_STATEMACH_MEDIUMTIME_MS 5

/**
 * BMS statemachine long time definition in ms
 */
#define BMS_STATEMACH_LONGTIME_MS 100

/**
 * BMS statemachine very long time definition in ms
 */
#define BMS_STATEMACH_VERYLONGTIME_MS 2000


/**
 * @ingroup CONFIG_BMS
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [5,15]
 * \par Unit:
 * 10*ms
*/
#define BMS_SELFPOWERONCHECK_TIMEOUT    10  /* 100ms */

/**
 * @ingroup CONFIG_BMS
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [5,15]
 * \par Unit:
 * 10*ms
*/
#define BMS_SELFAWAKECHECK_TIMEOUT      10  /* 100ms */


/**
 * @ingroup CONFIG_BMS
 * \par Type:
 * int
 * \par Default:
 * 50
 * \par Range:
 * [40,60]
 * \par Unit:
 * 10*ms
*/
#define BMS_IDLE_TIMEOUT                500  /* 5s timeout to go to sleep or power off in idle state */

#define BMS_GETSELFCHECK_STATE()            BMS_CHECK_OK            /* function could return: BMS_CHECK_NOT_OK or OK BMS_CHECK_BUSY */
#define BMS_GETPOWERONSELFCHECK_STATE()     BMS_CHECK_OK            /* function could return: BMS_CHECK_NOT_OK or OK BMS_CHECK_BUSY */
#define BMS_CHECKPRECHARGE()                BMS_CheckPrecharge()    /* DIAG_CheckPrecharge() */

/*
 * Mapping the marcos from the contactor-module to
 * bms-macros.
 */
#define BMS_ALL_CONTACTORS_OFF()        CONT_SwitchAllContactorsOff()

#define BMS_CONT_MAINMINUS_ON()         CONT_SetContactorState(CONT_MAIN_MINUS, CONT_SWITCH_ON)
#define BMS_CONT_MAINMINUS_OFF()        CONT_SetContactorState(CONT_MAIN_MINUS, CONT_SWITCH_OFF)

#define BMS_CONT_MAINPRECHARGE_ON()     CONT_SetContactorState(CONT_PRECHARGE_PLUS, CONT_SWITCH_ON)
#define BMS_CONT_MAINPRECHARGE_OFF()    CONT_SetContactorState(CONT_PRECHARGE_PLUS, CONT_SWITCH_OFF)

#define BMS_CONT_MAINPLUS_ON()          CONT_SetContactorState(CONT_MAIN_PLUS, CONT_SWITCH_ON)
#define BMS_CONT_MAINPLUS_OFF()         CONT_SetContactorState(CONT_MAIN_PLUS, CONT_SWITCH_OFF)

#if BS_SEPARATE_POWERLINES == 1
#define BMS_CONT_CHARGE_MAINMINUS_ON()         CONT_SetContactorState(CONT_CHARGE_MAIN_MINUS, CONT_SWITCH_ON)
#define BMS_CONT_CHARGE_MAINMINUS_OFF()        CONT_SetContactorState(CONT_CHARGE_MAIN_MINUS, CONT_SWITCH_OFF)

#define BMS_CONT_CHARGE_MAINPRECHARGE_ON()     CONT_SetContactorState(CONT_CHARGE_PRECHARGE_PLUS, CONT_SWITCH_ON)
#define BMS_CONT_CHARGE_MAINPRECHARGE_OFF()    CONT_SetContactorState(CONT_CHARGE_PRECHARGE_PLUS, CONT_SWITCH_OFF)

#define BMS_CONT_CHARGE_MAINPLUS_ON()          CONT_SetContactorState(CONT_CHARGE_MAIN_PLUS, CONT_SWITCH_ON)
#define BMS_CONT_CHARGE_MAINPLUS_OFF()         CONT_SetContactorState(CONT_CHARGE_MAIN_PLUS, CONT_SWITCH_OFF)
#endif  /* BS_SEPARATE_POWERLINES == 1 */

/*================== Function Prototypes ==================================*/

/*================== Function Implementations =============================*/

#endif /* BMS_CFG_H_ */

bms_cfg.c (secondary)

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms_cfg.c
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE_CONF
 * @prefix  BMS
 *
 * @brief   bms driver configuration
 */

/*================== Includes =============================================*/
#include "bms_cfg.h"

/*================== Macros and Definitions ===============================*/

/*================== Function Prototypes ==================================*/

/*================== Function Implementations =============================*/

bms_cfg.h (secondary)

/**
 *
 * @copyright &copy; 2010 - 2021, 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:
 *
 * &Prime;This product uses parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product includes parts of foxBMS&reg;&Prime;
 *
 * &Prime;This product is derived from foxBMS&reg;&Prime;
 *
 */

/**
 * @file    bms_cfg.h
 * @author  foxBMS Team
 * @date    21.09.2015 (date of creation)
 * @ingroup ENGINE_CONF
 * @prefix  BMS
 *
 * @brief   bms driver configuration header
 */

#ifndef BMS_CFG_H_
#define BMS_CFG_H_

/*================== Includes =============================================*/
#include "general.h"

/*================== Macros and Definitions ===============================*/

/**
 * @ingroup CONFIG_BMS
 * maximum current safe operating limit
 * When limit is violated, error state is requested and contactors will open.
 * \par Type:
 * int
 * \par Unit:
 * mA
 * \par Default:
 * 15000
*/
#define BMS_CURRENTMAX 15000

/**
 * @ingroup CONFIG_BMS
 * minimum current safe operating limit
 * When limit is violated, error state is requested and contactors will open.
 * \par Type:
 * int
 * \par Unit:
 * mA
 * \par Default:
 * -15000
*/
#define BMS_CURRENTMIN -15000

/**
 * @ingroup CONFIG_BMS
 * checking if current is in SOF limits of cells. Set FALSE for test without checking against SOF derating.
 * \par Type:
 * select(2)
 * \par Default:
 * 1
*/
/* #define BMS_TEST_CELL_SOF_LIMITS  TRUE */
#define BMS_TEST_CELL_SOF_LIMITS FALSE







#define BMS_REQ_ID_NOREQ 0

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN signal to go to STANDBY state (ready, but no contactors closed)
 * \par Type:
 * int
 * \par Default:
 * 8
 * \par Range:
 * 0<=x
*/
#define BMS_REQ_ID_STANDBY 8

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN signal to go to NORMAL state (contactors closing procedure)
 * \par Type:
 * int
 * \par Default:
 * 3
 * \par Range:
 * 0<=x
*/
#define BMS_REQ_ID_NORMAL 3

/**
 * @ingroup CONFIG_BMS
 * this is the ID that should be requested via CAN signal to go to CHARGE state (contactors closing procedure)
 * \par Type:
 * int
 * \par Default:
 * 4
 * \par Range:
 * 0<=x
*/
#define BMS_REQ_ID_CHARGE 4



/**
 * BMS statemachine short time definition in ms
 */
#define BMS_STATEMACH_SHORTTIME_MS 1

/**
 * BMS statemachine medium time definition in ms
 */
#define BMS_STATEMACH_MEDIUMTIME_MS 5

/**
 * BMS statemachine long time definition in ms
 */
#define BMS_STATEMACH_LONGTIME_MS 100

/**
 * BMS statemachine very long time definition in ms
 */
#define BMS_STATEMACH_VERYLONGTIME_MS 2000


/**
 * @ingroup CONFIG_BMS
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [5,15]
 * \par Unit:
 * 10*ms
*/
#define BMS_SELFPOWERONCHECK_TIMEOUT    10  /* 100ms */

/**
 * @ingroup CONFIG_BMS
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [5,15]
 * \par Unit:
 * 10*ms
*/
#define BMS_SELFAWAKECHECK_TIMEOUT      10  /* 100ms */


/**
 * @ingroup CONFIG_BMS
 * \par Type:
 * int
 * \par Default:
 * 50
 * \par Range:
 * [40,60]
 * \par Unit:
 * 10*ms
*/
#define BMS_IDLE_TIMEOUT                500  /* 5s timeout to go to sleep or power off in idle state */

#define BMS_GETSELFCHECK_STATE()            BMS_CHECK_OK            /* function could return: BMS_CHECK_NOT_OK or OK BMS_CHECK_BUSY */
#define BMS_GETPOWERONSELFCHECK_STATE()     BMS_CHECK_OK            /* function could return: BMS_CHECK_NOT_OK or OK BMS_CHECK_BUSY */
#define BMS_CHECKPRECHARGE()                BMS_CheckPrecharge()    /* DIAG_CheckPrecharge() */


/*================== Function Prototypes ==================================*/

/*================== Function Implementations =============================*/

#endif /* BMS_CFG_H_ */