CONTACTOR Module Sources


contactor.c

/**
 *
 * @copyright © 2010 - 2019, 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    contactor.c
 * @author  foxBMS Team
 * @date    23.09.2015 (date of creation)
 * @ingroup DRIVERS
 * @prefix  CONT
 *
 * @brief   Driver for the contactors.
 *
 */

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

#include "database.h"
#include "diag.h"
#include "FreeRTOS.h"
#include "task.h"

#if BUILD_MODULE_ENABLE_CONTACTOR == 1
/*================== Macros and Definitions ===============================*/

/**
 * used to locally copy the current-sensor value from the global database
 * current table
 */
static DATA_BLOCK_CURRENT_SENSOR_s cont_current_tab = {0};

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

#define CONT_OPENALLCONTACTORS()   CONT_SwitchAllContactorsOff();

#define CONT_OPENMINUS()       CONT_SetContactorState(CONT_MAIN_MINUS, CONT_SWITCH_OFF);
#define CONT_CLOSEMINUS()       CONT_SetContactorState(CONT_MAIN_MINUS, CONT_SWITCH_ON);

#define CONT_OPENPLUS()       CONT_SetContactorState(CONT_MAIN_PLUS, CONT_SWITCH_OFF);
#define CONT_CLOSEPLUS()       CONT_SetContactorState(CONT_MAIN_PLUS, CONT_SWITCH_ON);

#define CONT_OPENPRECHARGE()       CONT_SetContactorState(CONT_PRECHARGE_PLUS, CONT_SWITCH_OFF);
#define CONT_CLOSEPRECHARGE()       CONT_SetContactorState(CONT_PRECHARGE_PLUS, CONT_SWITCH_ON);

#if BS_SEPARATE_POWERLINES == 1
#define CONT_OPENCHARGEMINUS()       CONT_SetContactorState(CONT_CHARGE_MAIN_MINUS, CONT_SWITCH_OFF);
#define CONT_CLOSECHARGEMINUS()       CONT_SetContactorState(CONT_CHARGE_MAIN_MINUS, CONT_SWITCH_ON);

#define CONT_OPENCHARGEPLUS()       CONT_SetContactorState(CONT_CHARGE_MAIN_PLUS, CONT_SWITCH_OFF);
#define CONT_CLOSECHARGEPLUS()       CONT_SetContactorState(CONT_CHARGE_MAIN_PLUS, CONT_SWITCH_ON);

#define CONT_OPENCHARGEPRECHARGE()       CONT_SetContactorState(CONT_CHARGE_PRECHARGE_PLUS, CONT_SWITCH_OFF);
#define CONT_CLOSECHARGEPRECHARGE()       CONT_SetContactorState(CONT_CHARGE_PRECHARGE_PLUS, CONT_SWITCH_ON);
#endif /* BS_SEPARATE_POWERLINES == 1 */

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

/**
 * contains the state of the contactor state machine
 *
 */
static CONT_STATE_s cont_state = {
    .timer                  = 0,
    .statereq               = CONT_STATE_NO_REQUEST,
    .state                  = CONT_STATEMACH_UNINITIALIZED,
    .substate               = CONT_ENTRY,
    .laststate              = CONT_STATEMACH_UNINITIALIZED,
    .lastsubstate           = 0,
    .triggerentry           = 0,
    .ErrRequestCounter      = 0,
    .initFinished           = E_NOT_OK,
    .OscillationCounter     = 0,
    .PrechargeTryCounter    = 0,
    .PrechargeTimeOut       = 0,
    .counter                = 0,
    .activePowerLine        = CONT_POWER_LINE_NONE,
};

static DATA_BLOCK_CONTFEEDBACK_s contfeedback_tab = {
        .contactor_feedback = 0,
        .timestamp = 0,
        .previous_timestamp = 0,
};


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

static CONT_RETURN_TYPE_e CONT_CheckStateRequest(CONT_STATE_REQUEST_e statereq);
static CONT_STATE_REQUEST_e CONT_GetStateRequest(void);
static CONT_STATE_REQUEST_e CONT_TransferStateRequest(void);
static uint8_t CONT_CheckReEntrance(void);
static void CONT_CheckFeedback(void);

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

CONT_ELECTRICAL_STATE_TYPE_s CONT_GetContactorSetValue(CONT_NAMES_e contactor) {
    CONT_ELECTRICAL_STATE_TYPE_s contactorSetInformation = FALSE;
    taskENTER_CRITICAL();
    contactorSetInformation = cont_contactor_states[contactor].set;
    taskEXIT_CRITICAL();
    return contactorSetInformation;
}


CONT_ELECTRICAL_STATE_TYPE_s CONT_GetContactorFeedback(CONT_NAMES_e contactor) {
    CONT_ELECTRICAL_STATE_TYPE_s measuredContactorState = CONT_SWITCH_UNDEF;
    if (CONT_HAS_NO_FEEDBACK == cont_contactors_config[contactor].feedback_pin_type) {
            measuredContactorState = cont_contactor_states[contactor].set;
    } else {
        /* the contactor has a feedback pin, but it has to be differenced if the feedback pin is normally open or normally closed */
        if (CONT_FEEDBACK_NORMALLY_OPEN == cont_contactors_config[contactor].feedback_pin_type) {
            IO_PIN_STATE_e pinstate = IO_PIN_RESET;
            taskENTER_CRITICAL();
            pinstate = IO_ReadPin(cont_contactors_config[contactor].feedback_pin);
            taskEXIT_CRITICAL();
            if (IO_PIN_RESET == pinstate) {
                measuredContactorState = CONT_SWITCH_ON;
            } else if (IO_PIN_SET == pinstate) {
                measuredContactorState = CONT_SWITCH_OFF;
            } else {
                measuredContactorState = CONT_SWITCH_UNDEF;
            }
        }
        if (CONT_FEEDBACK_NORMALLY_CLOSED == cont_contactors_config[contactor].feedback_pin_type) {
            IO_PIN_STATE_e pinstate = IO_PIN_SET;
            taskENTER_CRITICAL();
            pinstate = IO_ReadPin(cont_contactors_config[contactor].feedback_pin);
            taskEXIT_CRITICAL();
            if (IO_PIN_SET == pinstate) {
                measuredContactorState = CONT_SWITCH_ON;
            } else if (IO_PIN_RESET == pinstate) {
                measuredContactorState = CONT_SWITCH_OFF;
            } else {
                measuredContactorState = CONT_SWITCH_UNDEF;
            }
        }
    }
    cont_contactor_states[contactor].feedback = measuredContactorState;
    return measuredContactorState;
}


STD_RETURN_TYPE_e CONT_AcquireContactorFeedbacks(void) {
    STD_RETURN_TYPE_e retVal = E_NOT_OK;
    taskENTER_CRITICAL();
    for (uint8_t i = 0; i < BS_NR_OF_CONTACTORS; i++) {
        cont_contactor_states[i].feedback = CONT_GetContactorFeedback(i);
    }
    retVal = E_OK;
    taskEXIT_CRITICAL();
    return retVal;
}

STD_RETURN_TYPE_e CONT_SetContactorState(CONT_NAMES_e contactor, CONT_ELECTRICAL_STATE_TYPE_s requestedContactorState) {
    STD_RETURN_TYPE_e retVal = E_OK;

    if (requestedContactorState  ==  CONT_SWITCH_ON) {
        cont_contactor_states[contactor].set = CONT_SWITCH_ON;
        IO_WritePin(cont_contactors_config[contactor].control_pin, IO_PIN_SET);
        if (DIAG_HANDLER_RETURN_OK != DIAG_ContHandler(DIAG_EVENT_OK, (uint8_t) contactor, NULL)) {
            /* TODO: explain why empty if */
        }
    } else if (requestedContactorState  ==  CONT_SWITCH_OFF) {
        DB_ReadBlock(&cont_current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
        float currentAtSwitchOff = cont_current_tab.current;
        if (((BAD_SWITCHOFF_CURRENT_POS < currentAtSwitchOff) && (0 < currentAtSwitchOff)) ||
             ((BAD_SWITCHOFF_CURRENT_NEG > currentAtSwitchOff) && (0 > currentAtSwitchOff))) {
            if (DIAG_HANDLER_RETURN_OK != DIAG_ContHandler(DIAG_EVENT_NOK, (uint8_t) contactor, &currentAtSwitchOff)) {
                /* currently no error handling, just logging */
            }
        } else {
            if (DIAG_HANDLER_RETURN_OK != DIAG_ContHandler(DIAG_EVENT_OK, (uint8_t) contactor, NULL)) {
                /* TODO: explain why empty if */
            }
       }
        cont_contactor_states[contactor].set = CONT_SWITCH_OFF;
        IO_WritePin(cont_contactors_config[contactor].control_pin, IO_PIN_RESET);
    } else {
        retVal = E_NOT_OK;
    }

    return retVal;
}


STD_RETURN_TYPE_e CONT_SwitchAllContactorsOff(void) {
    STD_RETURN_TYPE_e retVal = E_NOT_OK;
    uint8_t offCounter = 0;
    STD_RETURN_TYPE_e successfullSet = E_NOT_OK;

    for (uint8_t i = 0; i < BS_NR_OF_CONTACTORS; i++) {
        successfullSet = CONT_SetContactorState(i, CONT_SWITCH_OFF);
        if (E_OK == successfullSet) {
            offCounter = offCounter + 1;
        }
        successfullSet = E_NOT_OK;
    }

    if (BS_NR_OF_CONTACTORS == offCounter) {
        retVal = E_OK;
    } else {
        retVal = E_NOT_OK;
    }
    return retVal;
}



/**
 * @brief   re-entrance check of CONT 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  0 if no further instance of the function is active, 0xff else
 *
 */
static uint8_t CONT_CheckReEntrance(void) {
    uint8_t retval = 0;

    taskENTER_CRITICAL();
    if (!cont_state.triggerentry) {
        cont_state.triggerentry++;
    } else {
        retval = 0xFF;
    }
    taskEXIT_CRITICAL();
    return retval;
}




/**
 * @brief   gets the current state request.
 *
 * @details This function is used in the functioning of the CONT state machine.
 *
 * @return  return the current pending state request
 */
static CONT_STATE_REQUEST_e CONT_GetStateRequest(void) {
    CONT_STATE_REQUEST_e retval = CONT_STATE_NO_REQUEST;

    taskENTER_CRITICAL();
    retval = cont_state.statereq;
    taskEXIT_CRITICAL();

    return retval;
}


CONT_STATEMACH_e CONT_GetState(void) {
    return (cont_state.state);
}

STD_RETURN_TYPE_e CONT_GetInitializationState(void) {
    return (cont_state.initFinished);
}

CONT_POWER_LINE_e CONT_GetActivePowerLine() {
    return (cont_state.activePowerLine);
}


/**
 * @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 the
 *          state machine. It resets the value from cont_state to CONT_STATE_NO_REQUEST
 *
 * @return  current state request, taken from CONT_STATE_REQUEST_e
 *
 */
static CONT_STATE_REQUEST_e CONT_TransferStateRequest(void) {
    CONT_STATE_REQUEST_e retval = CONT_STATE_NO_REQUEST;

    taskENTER_CRITICAL();
    retval    = cont_state.statereq;
    cont_state.statereq = CONT_STATE_NO_REQUEST;
    taskEXIT_CRITICAL();

    return (retval);
}


CONT_RETURN_TYPE_e CONT_SetStateRequest(CONT_STATE_REQUEST_e statereq) {
    CONT_RETURN_TYPE_e retVal = CONT_STATE_NO_REQUEST;

    taskENTER_CRITICAL();
    retVal = CONT_CheckStateRequest(statereq);

    if (retVal == CONT_OK) {
            cont_state.statereq   = statereq;
    }
    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 (type: CONT_RETURN_TYPE_e)
 */
static CONT_RETURN_TYPE_e CONT_CheckStateRequest(CONT_STATE_REQUEST_e statereq) {
    if (statereq == CONT_STATE_ERROR_REQUEST) {
        return CONT_OK;
    }

    if (cont_state.statereq == CONT_STATE_NO_REQUEST) {
        /* init only allowed from the uninitialized state */
        if (statereq == CONT_STATE_INIT_REQUEST) {
            if (cont_state.state == CONT_STATEMACH_UNINITIALIZED) {
                return CONT_OK;
            } else {
                return CONT_ALREADY_INITIALIZED;
            }
        }

        if ((statereq == CONT_STATE_STANDBY_REQUEST) || (statereq == CONT_STATE_NORMAL_REQUEST) || (statereq == CONT_STATE_CHARGE_REQUEST)) {
            return CONT_OK;
        } else if (statereq == CONT_STATE_NORMAL_REQUEST) {
            if (cont_state.state == CONT_STATEMACH_CHARGE_PRECHARGE || cont_state.state == CONT_STATEMACH_CHARGE) {
                return CONT_REQUEST_IMPOSSIBLE;
            } else {
                return CONT_OK;
            }
        } else if (statereq == CONT_STATE_CHARGE_REQUEST) {
            if (cont_state.state == CONT_STATEMACH_PRECHARGE || cont_state.state == CONT_STATEMACH_NORMAL) {
                return CONT_REQUEST_IMPOSSIBLE;
            } else {
                return CONT_OK;
            }
        } else {
            return CONT_ILLEGAL_REQUEST;
        }
    } else {
        return CONT_REQUEST_PENDING;
    }
}

void CONT_Trigger(void) {
    STD_RETURN_TYPE_e retVal = E_OK;
    CONT_STATE_REQUEST_e statereq = CONT_STATE_NO_REQUEST;

    if (CONT_CheckReEntrance()) {
        return;
    }

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

    if (cont_state.state != CONT_STATEMACH_UNINITIALIZED) {
        CONT_CheckFeedback();
    }

    if (cont_state.OscillationCounter > 0) {
        cont_state.OscillationCounter--;
    }

    if (cont_state.PrechargeTimeOut > 0) {
        if (cont_state.PrechargeTimeOut > CONT_TASK_CYCLE_CONTEXT_MS) {
            cont_state.PrechargeTimeOut -= CONT_TASK_CYCLE_CONTEXT_MS;
        } else {
            cont_state.PrechargeTimeOut = 0;
        }
    }

    if (cont_state.timer) {
        if (cont_state.timer > CONT_TASK_CYCLE_CONTEXT_MS) {
            cont_state.timer -= CONT_TASK_CYCLE_CONTEXT_MS;
        } else {
            cont_state.timer = 0;
        }
        if (cont_state.timer) {
            cont_state.triggerentry--;
            return;    /* handle state machine only if timer has elapsed */
        }
    }


    switch (cont_state.state) {
        /****************************UNINITIALIZED***********************************/
        case CONT_STATEMACH_UNINITIALIZED:
            /* waiting for Initialization Request */
            statereq = CONT_TransferStateRequest();
            if (statereq == CONT_STATE_INIT_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_INITIALIZATION;
                cont_state.substate = CONT_ENTRY;
            } else if (statereq == CONT_STATE_NO_REQUEST) {
                /* no actual request pending */
            } else {
                cont_state.ErrRequestCounter++;   /* illegal request pending */
            }
            break;


        /****************************INITIALIZATION**********************************/
        case CONT_STATEMACH_INITIALIZATION:

            CONT_SAVELASTSTATES();
            CONT_OPENALLCONTACTORS();

            cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
            cont_state.state = CONT_STATEMACH_INITIALIZED;
            cont_state.substate = CONT_ENTRY;

            break;

        /****************************INITIALIZED*************************************/
        case CONT_STATEMACH_INITIALIZED:
            CONT_SAVELASTSTATES();
            cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
            cont_state.state = CONT_STATEMACH_IDLE;
            cont_state.substate = CONT_ENTRY;
            break;

        /****************************IDLE*************************************/
        case CONT_STATEMACH_IDLE:
            CONT_SAVELASTSTATES();
            cont_state.initFinished = E_OK;
            cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
            cont_state.state = CONT_STATEMACH_STANDBY;
            cont_state.substate = CONT_ENTRY;
            break;

        /****************************STANDBY*************************************/
        case CONT_STATEMACH_STANDBY:
            CONT_SAVELASTSTATES();

            /* first precharge process */
            if (cont_state.substate == CONT_ENTRY) {
                cont_state.OscillationCounter = CONT_OSCILLATION_LIMIT;
                CONT_OPENPRECHARGE();
                #if BS_SEPARATE_POWERLINES == 1
                    CONT_OPENCHARGEPRECHARGE();
                #endif
                cont_state.activePowerLine = CONT_POWER_LINE_NONE;
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.substate = CONT_OPEN_FIRST_CONTACTOR;
                break;
            } else if (cont_state.substate == CONT_OPEN_FIRST_CONTACTOR) {
                if (BS_CheckCurrent_Direction() == BS_CURRENT_DISCHARGE) {
                    CONT_OPENPLUS();
                    #if BS_SEPARATE_POWERLINES == 1
                        CONT_OPENCHARGEPLUS();
                    #endif
                    cont_state.timer = CONT_DELAY_BETWEEN_OPENING_CONTACTORS_MS;
                    cont_state.substate = CONT_OPEN_SECOND_CONTACTOR_MINUS;
                } else {
                    CONT_OPENMINUS();
                    #if BS_SEPARATE_POWERLINES == 1
                        CONT_OPENCHARGEMINUS();
                    #endif
                    cont_state.timer = CONT_DELAY_BETWEEN_OPENING_CONTACTORS_MS;
                    cont_state.substate = CONT_OPEN_SECOND_CONTACTOR_PLUS;
                }
                break;
            } else if (cont_state.substate == CONT_OPEN_SECOND_CONTACTOR_MINUS) {
                CONT_OPENMINUS();
                #if BS_SEPARATE_POWERLINES == 1
                    CONT_OPENCHARGEMINUS();
                #endif
                cont_state.timer = CONT_DELAY_AFTER_OPENING_SECOND_CONTACTORS_MS;
                cont_state.substate = CONT_STANDBY;
                break;
            } else if (cont_state.substate == CONT_OPEN_SECOND_CONTACTOR_PLUS) {
                CONT_OPENPLUS();
                #if BS_SEPARATE_POWERLINES == 1
                    CONT_OPENCHARGEPLUS();
                #endif
                cont_state.timer = CONT_DELAY_AFTER_OPENING_SECOND_CONTACTORS_MS;
                cont_state.substate = CONT_STANDBY;
                break;
            } else if (cont_state.substate == CONT_STANDBY) {
                /* when process done, look for requests */
                statereq = CONT_TransferStateRequest();
                if (statereq == CONT_STATE_STANDBY_REQUEST) {
                    /* we stay already in requested state, nothing to do */
                } else if (statereq == CONT_STATE_NORMAL_REQUEST) {
                    CONT_SAVELASTSTATES();
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    cont_state.state = CONT_STATEMACH_PRECHARGE;
                    cont_state.substate = CONT_ENTRY;
                }
#if BS_SEPARATE_POWERLINES == 1
                else if (statereq == CONT_STATE_CHARGE_REQUEST) { /* NOLINT(readability/braces) */
                    CONT_SAVELASTSTATES();
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    cont_state.state = CONT_STATEMACH_CHARGE_PRECHARGE;
                    cont_state.substate = CONT_ENTRY;
                }
#endif /* BS_SEPARATE_POWERLINES == 1 */
                else if (statereq == CONT_STATE_ERROR_REQUEST) { /* NOLINT(readability/braces) */
                    CONT_SAVELASTSTATES();
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    cont_state.state = CONT_STATEMACH_ERROR;
                    cont_state.substate = CONT_ENTRY;
                } else if (statereq == CONT_STATE_NO_REQUEST) {
                    /* no actual request pending */
                } else {
                    cont_state.ErrRequestCounter++;  /* illegal request pending */
                }

                /* check fuse state */
                CONT_CheckFuse(CONT_POWERLINE_NORMAL);
                break;
            }
            break;

        /****************************PRECHARGE*************************************/
        case CONT_STATEMACH_PRECHARGE:
            CONT_SAVELASTSTATES();
            /*  check state requests */
            statereq = CONT_TransferStateRequest();
            if (statereq == CONT_STATE_ERROR_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_ERROR;
                cont_state.substate = CONT_ENTRY;
                break;
            }
            if (statereq == CONT_STATE_STANDBY_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_STANDBY;
                cont_state.substate = CONT_ENTRY;
                break;
            }

            /* precharge process, can be interrupted anytime by the requests above */
            if (cont_state.substate == CONT_ENTRY) {
                if (cont_state.OscillationCounter > 0) {
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    break;
                } else {
                    cont_state.PrechargeTryCounter = 0;
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    cont_state.substate = CONT_PRECHARGE_CLOSE_MINUS;
                    break;
                }
            } else if (cont_state.substate == CONT_PRECHARGE_CLOSE_MINUS) {
                cont_state.PrechargeTryCounter++;
                cont_state.PrechargeTimeOut = CONT_PRECHARGE_TIMEOUT_MS;
                CONT_CLOSEMINUS();
                cont_state.timer = CONT_STATEMACH_WAIT_AFTER_CLOSING_MINUS_MS;
                cont_state.substate = CONT_PRECHARGE_CLOSE_PRECHARGE;
                break;
            } else if (cont_state.substate == CONT_PRECHARGE_CLOSE_PRECHARGE) {
                CONT_CLOSEPRECHARGE();
                cont_state.timer = CONT_STATEMACH_WAIT_AFTER_CLOSING_PRECHARGE_MS;
                cont_state.substate = CONT_PRECHARGE_CHECK_VOLTAGES;
                break;
            } else if (cont_state.substate == CONT_PRECHARGE_CHECK_VOLTAGES) {
                retVal = CONT_CheckPrecharge(CONT_POWERLINE_NORMAL);
                if (retVal == E_OK) {
                    CONT_CLOSEPLUS();
                    cont_state.timer = CONT_STATEMACH_WAIT_AFTER_CLOSING_PLUS_MS;
                    cont_state.substate = CONT_PRECHARGE_OPEN_PRECHARGE;
                    break;
                } else if (cont_state.PrechargeTimeOut > 0) {
                    break;
                } else {
                    if (cont_state.PrechargeTryCounter < CONT_PRECHARGE_TRIES) {
                        CONT_OPENALLCONTACTORS();
                        cont_state.timer = CONT_STATEMACH_TIMEAFTERPRECHARGEFAIL_MS;
                        cont_state.substate = CONT_PRECHARGE_CLOSE_MINUS;
                        break;
                    } else {
                        cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                        cont_state.state = CONT_STATEMACH_ERROR;
                        cont_state.substate = CONT_ENTRY;
                        break;
                    }
                }
            } else if (cont_state.substate == CONT_PRECHARGE_OPEN_PRECHARGE) {
                CONT_OPENPRECHARGE();
                cont_state.timer = CONT_STATEMACH_WAIT_AFTER_OPENING_PRECHARGE_MS;
                cont_state.state = CONT_STATEMACH_NORMAL;
                cont_state.substate = CONT_ENTRY;
                cont_state.activePowerLine = CONT_POWER_LINE_0;
                break;
            }

            break;

        /****************************NORMAL*************************************/
        case CONT_STATEMACH_NORMAL:
            CONT_SAVELASTSTATES();
            statereq = CONT_TransferStateRequest();
            if (statereq == CONT_STATE_ERROR_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_ERROR;
                cont_state.substate = CONT_ENTRY;
                break;
            }
            if (statereq == CONT_STATE_STANDBY_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_STANDBY;
                cont_state.substate = CONT_ENTRY;
                break;
            }

            /* check fuse state */
            CONT_CheckFuse(CONT_POWERLINE_NORMAL);
            break;

#if BS_SEPARATE_POWERLINES == 1
        /****************************CHARGE_PRECHARGE*************************************/
        case CONT_STATEMACH_CHARGE_PRECHARGE:
            CONT_SAVELASTSTATES();

            /* check state requests */
            statereq = CONT_TransferStateRequest();
            if (statereq == CONT_STATE_ERROR_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_ERROR;
                cont_state.substate = CONT_ENTRY;
                break;
            }
            if (statereq == CONT_STATE_STANDBY_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_STANDBY;
                cont_state.substate = CONT_ENTRY;
                break;
            }

            /* precharge process, can be interrupted anytime by the requests above */
            if (cont_state.substate == CONT_ENTRY) {
                if (cont_state.OscillationCounter > 0) {
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    break;
                } else {
                    cont_state.PrechargeTryCounter = 0;
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    cont_state.substate = CONT_PRECHARGE_CLOSE_MINUS;
                    break;
                }
            } else if (cont_state.substate == CONT_PRECHARGE_CLOSE_MINUS) {
                cont_state.PrechargeTryCounter++;
                cont_state.PrechargeTimeOut = CONT_CHARGE_PRECHARGE_TIMEOUT_MS;
                CONT_CLOSECHARGEMINUS();
                cont_state.timer = CONT_STATEMACH_CHARGE_WAIT_AFTER_CLOSING_MINUS_MS;
                cont_state.substate = CONT_PRECHARGE_CLOSE_PRECHARGE;
                break;
            } else if (cont_state.substate == CONT_PRECHARGE_CLOSE_PRECHARGE) {
                CONT_CLOSECHARGEPRECHARGE();
                cont_state.timer = CONT_STATEMACH_CHARGE_WAIT_AFTER_CLOSING_PRECHARGE_MS;
                cont_state.substate = CONT_PRECHARGE_CHECK_VOLTAGES;
                break;
            } else if (cont_state.substate == CONT_PRECHARGE_CHECK_VOLTAGES) {
                retVal = CONT_CheckPrecharge(CONT_POWERLINE_CHARGE);
                if (retVal == E_OK) {
                    CONT_CLOSECHARGEPLUS();
                    cont_state.timer = CONT_STATEMACH_CHARGE_WAIT_AFTER_CLOSING_PLUS_MS;
                    cont_state.substate = CONT_PRECHARGE_OPEN_PRECHARGE;
                    break;
                } else if (cont_state.PrechargeTimeOut > 0) {
                    break;
                } else {
                    if (cont_state.PrechargeTryCounter < CONT_PRECHARGE_TRIES) {
                        CONT_OPENALLCONTACTORS();
                        cont_state.timer = CONT_STATEMACH_TIMEAFTERPRECHARGEFAIL_MS;
                        cont_state.substate = CONT_PRECHARGE_CLOSE_MINUS;
                        break;
                    } else {
                        cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                        cont_state.state = CONT_STATEMACH_ERROR;
                        cont_state.substate = CONT_ENTRY;
                        break;
                    }
                }
            } else if (cont_state.substate == CONT_PRECHARGE_OPEN_PRECHARGE) {
                CONT_OPENCHARGEPRECHARGE();
                cont_state.timer = CONT_STATEMACH_WAIT_AFTER_OPENING_PRECHARGE_MS;
                cont_state.state = CONT_STATEMACH_CHARGE;
                cont_state.substate = CONT_ENTRY;
                cont_state.activePowerLine = CONT_POWER_LINE_1;
                break;
            }

            break;

        /****************************CHARGE*************************************/
        case CONT_STATEMACH_CHARGE:
            CONT_SAVELASTSTATES();

            statereq = CONT_TransferStateRequest();
            if (statereq == CONT_STATE_ERROR_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_ERROR;
                cont_state.substate = CONT_ENTRY;
                break;
            }
            if (statereq == CONT_STATE_STANDBY_REQUEST) {
                CONT_SAVELASTSTATES();
                cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                cont_state.state = CONT_STATEMACH_STANDBY;
                cont_state.substate = CONT_ENTRY;
                break;
            }

#endif /* BS_SEPARATE_POWERLINES == 1 */
            break;

        /****************************ERROR*************************************/
        case CONT_STATEMACH_ERROR:
            CONT_SAVELASTSTATES();

            /* first error process */
            if (cont_state.substate == CONT_ENTRY) {
                cont_state.OscillationCounter = CONT_OSCILLATION_LIMIT;
                CONT_OPENPRECHARGE();
                #if BS_SEPARATE_POWERLINES == 1
                    CONT_OPENCHARGEPRECHARGE();
                #endif
                cont_state.timer = CONT_DELAY_BETWEEN_OPENING_CONTACTORS_MS;
                cont_state.substate = CONT_OPEN_FIRST_CONTACTOR;
                break;

            } else if (cont_state.substate == CONT_OPEN_FIRST_CONTACTOR) {
                if (BS_CheckCurrent_Direction() == BS_CURRENT_DISCHARGE) {
                    CONT_OPENPLUS();
                    #if BS_SEPARATE_POWERLINES == 1
                        CONT_OPENCHARGEPLUS();
                    #endif
                    cont_state.timer = CONT_DELAY_BETWEEN_OPENING_CONTACTORS_MS;
                    cont_state.substate = CONT_OPEN_SECOND_CONTACTOR_MINUS;
                } else {
                    CONT_OPENMINUS();
                    #if BS_SEPARATE_POWERLINES == 1
                        CONT_OPENCHARGEMINUS();
                    #endif
                    cont_state.timer = CONT_DELAY_BETWEEN_OPENING_CONTACTORS_MS;
                    cont_state.substate = CONT_OPEN_SECOND_CONTACTOR_PLUS;
                }
                /* mark no powerline as connected */
                cont_state.activePowerLine = CONT_POWER_LINE_NONE;
                break;

            } else if (cont_state.substate == CONT_OPEN_SECOND_CONTACTOR_MINUS) {
                CONT_OPENMINUS();
                #if BS_SEPARATE_POWERLINES == 1
                    CONT_OPENCHARGEMINUS();
                #endif
                cont_state.timer = CONT_DELAY_AFTER_OPENING_SECOND_CONTACTORS_MS;
                cont_state.substate = CONT_ERROR;
                break;

            } else if (cont_state.substate == CONT_OPEN_SECOND_CONTACTOR_PLUS) {
                CONT_OPENPLUS();
                #if BS_SEPARATE_POWERLINES == 1
                    CONT_OPENCHARGEPLUS();
                #endif
                cont_state.timer = CONT_DELAY_AFTER_OPENING_SECOND_CONTACTORS_MS;
                cont_state.substate = CONT_ERROR;
                break;

            } else if (cont_state.substate == CONT_ERROR) {
                /* Check if fuse is tripped */
                CONT_CheckFuse(CONT_POWERLINE_NORMAL);
                /* when process done, look for requests */
                statereq = CONT_TransferStateRequest();
                if (statereq == CONT_STATE_ERROR_REQUEST) {
                    /* we stay already in requested state, nothing to do */
                } else if (statereq == CONT_STATE_STANDBY_REQUEST) {
                    CONT_SAVELASTSTATES();
                    cont_state.timer = CONT_STATEMACH_SHORTTIME_MS;
                    cont_state.state = CONT_STATEMACH_STANDBY;
                    cont_state.substate = CONT_ENTRY;
                } else if (statereq == CONT_STATE_NO_REQUEST) {
                    /* no actual request pending */
                } else {
                    cont_state.ErrRequestCounter++;   /* illegal request pending */
                }
                break;
            }
            break;

        default:
            break;
    }  /* end switch (cont_state.state) */

    cont_state.triggerentry--;
    cont_state.counter++;
}

/**
 * @brief   checks the feedback of the contactors
 *
 * @details makes a DIAG entry for each contactor when the feedback does not match the set value
 */
void CONT_CheckFeedback(void) {
    CONT_ELECTRICAL_STATE_TYPE_s feedback;
    uint16_t contactor_feedback_state = 0;

    for (uint8_t i = 0; i < BS_NR_OF_CONTACTORS; i++) {
        feedback = CONT_GetContactorFeedback(i);

        switch (i) {
            case CONT_MAIN_PLUS:
                contactor_feedback_state |= feedback << CONT_MAIN_PLUS;
                break;
            case CONT_MAIN_MINUS:
                contactor_feedback_state |= feedback << CONT_MAIN_MINUS;
                break;
            case CONT_PRECHARGE_PLUS:
                contactor_feedback_state |= feedback << CONT_PRECHARGE_PLUS;
                break;
#if BS_SEPARATE_POWERLINES == 1
            case CONT_CHARGE_MAIN_PLUS:
                contactor_feedback_state |= feedback << CONT_CHARGE_MAIN_PLUS;
                break;
            case CONT_CHARGE_MAIN_MINUS:
                contactor_feedback_state |= feedback << CONT_CHARGE_MAIN_MINUS;
                break;
            case CONT_CHARGE_PRECHARGE_PLUS:
                contactor_feedback_state |= feedback << CONT_CHARGE_PRECHARGE_PLUS;
                break;
#endif /* BS_SEPARATE_POWERLINES == 1 */
            default:
                break;
        }

        contfeedback_tab.contactor_feedback &= (~0x3F);
        contfeedback_tab.contactor_feedback |= contactor_feedback_state;

        if (feedback != CONT_GetContactorSetValue(i)) {
            switch (i) {
                case CONT_MAIN_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_MAIN_PLUS_FEEDBACK, DIAG_EVENT_NOK, 0);
                    break;
                case CONT_MAIN_MINUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_MAIN_MINUS_FEEDBACK, DIAG_EVENT_NOK, 0);
                    break;
                case CONT_PRECHARGE_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_PRECHARGE_FEEDBACK, DIAG_EVENT_NOK, 0);
                    break;
#if BS_SEPARATE_POWERLINES == 1
                case CONT_CHARGE_MAIN_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_CHARGE_MAIN_PLUS_FEEDBACK, DIAG_EVENT_NOK, 0);
                    break;
                case CONT_CHARGE_MAIN_MINUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_CHARGE_MAIN_MINUS_FEEDBACK, DIAG_EVENT_NOK, 0);
                    break;
                case CONT_CHARGE_PRECHARGE_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_CHARGE_PRECHARGE_FEEDBACK, DIAG_EVENT_NOK, 0);
                    break;
#endif /* BS_SEPARATE_POWERLINES == 1 */
                default:
                    break;
            }
        } else {
            switch (i) {
                case CONT_MAIN_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_MAIN_PLUS_FEEDBACK, DIAG_EVENT_OK, 0);
                    break;
                case CONT_MAIN_MINUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_MAIN_MINUS_FEEDBACK, DIAG_EVENT_OK, 0);
                    break;
                case CONT_PRECHARGE_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_PRECHARGE_FEEDBACK, DIAG_EVENT_OK, 0);
                    break;
#if BS_SEPARATE_POWERLINES == 1
                case CONT_CHARGE_MAIN_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_CHARGE_MAIN_PLUS_FEEDBACK, DIAG_EVENT_OK, 0);
                    break;
                case CONT_CHARGE_MAIN_MINUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_CHARGE_MAIN_MINUS_FEEDBACK, DIAG_EVENT_OK, 0);
                    break;
                case CONT_CHARGE_PRECHARGE_PLUS:
                    DIAG_Handler(DIAG_CH_CONTACTOR_CHARGE_PRECHARGE_FEEDBACK, DIAG_EVENT_OK, 0);
                    break;
#endif /* BS_SEPARATE_POWERLINES == 1 */
                default:
                    break;
            }
        }
    }

    DB_WriteBlock(&contfeedback_tab, DATA_BLOCK_ID_CONTFEEDBACK);
}

#endif /* BUILD_MODULE_ENABLE_CONTACTOR */

contactor.h

/**
 *
 * @copyright &copy; 2010 - 2019, 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    contactor.h
 * @author  foxBMS Team
 * @date    23.09.2015 (date of creation)
 * @ingroup DRIVERS
 * @prefix  CONT
 *
 * @brief   Headers for the driver for the contactors.
 *
 */

#ifndef CONTACTOR_H_
#define CONTACTOR_H_

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

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

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

/*================== Function Prototypes ==================================*/
/**
 * @brief   Checks the configuration of the contactor-module
 *
 * @return  retVal (type: STD_RETURN_TYPE_e)
 */
extern STD_RETURN_TYPE_e CONT_Init(void);

/**
 * @brief   Gets the latest value (TRUE, FALSE) the contactors were set to.
 *
 * @param   contactor (type: CONT_NAMES_e)
 *
 * @return  returns CONT_SWITCH_OFF or CONT_SWITCH_ON
 */
extern CONT_ELECTRICAL_STATE_TYPE_s CONT_GetContactorSetValue(CONT_NAMES_e contactor);


/**
 * @brief   Reads the feedback pin of every contactor and returns its current value
 *          (CONT_SWITCH_OFF/CONT_SWITCH_ON).
 *
 * @details If the contactor has a feedback pin the measured feedback is returned. If the contactor
 *          has no feedback pin, it is assumed that after a certain time the contactor has reached
 *          the requested state.
 *
 * @param   contactor (type: CONT_NAMES_e)
 *
 * @return  measuredContactorState (type: CONT_ELECTRICAL_STATE_TYPE_s)
 */
extern CONT_ELECTRICAL_STATE_TYPE_s CONT_GetContactorFeedback(CONT_NAMES_e contactor);

/**
 * @brief   Reads the feedback pins of all contactors and updates the contactors_cfg[] array with
 *          their current states.
 *
 * @return  Returns E_OK if all feedbacks could be acquired (type: STD_RETURN_TYPE_e)
 */
extern STD_RETURN_TYPE_e CONT_AcquireContactorFeedbacks(void);

/**
 * @brief   Sets the contactor state to its requested state, if the contactor is at that time not
 *          in the requested state.
 *
 * @details If the new state was already requested, but not reached (meaning the measured feedback
 *          does not return the requested state), there are two states: it can be still ok (E_OK),
 *          because the contactor has some time left to get physically in the requested state
 *          (passed time since the request is lower than the limit) or it can be not ok (E_NOT_OK),
 *          because there is timing violation, i.e. the contactor has surpassed the maximum time
 *          for getting in the requested state. It returns E_OK if the requested state was
 *          successfully set or if the contactor was at the requested state before.
 *
 * @param   contactor (type: CONT_NAMES_e)
 * @param   requestedContactorState (type: CONT_ELECTRICAL_STATE_TYPE_s)
 *
 * @return  retVal (type: STD_RETURN_TYPE_e)
 */
extern STD_RETURN_TYPE_e CONT_SetContactorState(CONT_NAMES_e contactor, CONT_ELECTRICAL_STATE_TYPE_s requestedContactorState);


/**
 * @brief   Iterates over the contactor array and switches all contactors off
 *
 * @return  E_OK if all contactors were opened, E_NOT_OK if not all contactors could be opened
 *          (type: STD_RETURN_TYPE_e)
 */
extern STD_RETURN_TYPE_e CONT_SwitchAllContactorsOff(void);


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


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

/**
 * States of the CONT state machine
 */
typedef enum {
    /* Init-Sequence */
    CONT_STATEMACH_UNINITIALIZED             = 0,    /*!<    */
    CONT_STATEMACH_INITIALIZATION            = 1,    /*!<    */
    CONT_STATEMACH_INITIALIZED               = 2,    /*!<    */
    CONT_STATEMACH_IDLE                      = 3,    /*!<    */
    CONT_STATEMACH_STANDBY                   = 4,    /*!<    */
    CONT_STATEMACH_PRECHARGE                 = 5,    /*!<    */
    CONT_STATEMACH_NORMAL                    = 6,    /*!<    */
    CONT_STATEMACH_CHARGE_PRECHARGE          = 7,    /*!<    */
    CONT_STATEMACH_CHARGE                    = 8,    /*!<    */
    CONT_STATEMACH_UNDEFINED                 = 20,   /*!< undefined state                                */
    CONT_STATEMACH_RESERVED1                 = 0x80, /*!< reserved state                                 */
    CONT_STATEMACH_ERROR                     = 0xF0, /*!< Error-State:  */
} CONT_STATEMACH_e;

/**
 * Substates of the CONT state machine
 */
typedef enum {
    CONT_ENTRY                                    = 0,    /*!< Substate entry state       */
    CONT_OPEN_FIRST_CONTACTOR                     = 1,    /*!< Open-sequence: first contactor */
    CONT_OPEN_SECOND_CONTACTOR_MINUS              = 2,    /*!< Open-sequence: second contactor */
    CONT_OPEN_SECOND_CONTACTOR_PLUS               = 3,    /*!< Open-sequence: second contactor */
    CONT_STANDBY                                  = 4,    /*!< Substate stanby */
    CONT_PRECHARGE_CLOSE_MINUS                    = 5,    /*!< Begin of precharge sequence: close main minus */
    CONT_PRECHARGE_CLOSE_PRECHARGE                = 6,    /*!< Next step of precharge sequence: close precharge */
    CONT_PRECHARGE_CLOSE_PLUS                     = 7,    /*!< Next step of precharge sequence: close main plus */
    CONT_PRECHARGE_CHECK_VOLTAGES                 = 8,    /*!< Next step of precharge sequence: check if voltages OK */
    CONT_PRECHARGE_OPEN_PRECHARGE                 = 9,    /*!< Next step of precharge sequence: open precharge */
    CONT_ERROR                                    = 10,   /*!< Error state */
} CONT_STATEMACH_SUB_e;

/**
 * State requests for the CONT statemachine
 */
typedef enum {
    CONT_STATE_INIT_REQUEST                = CONT_STATEMACH_INITIALIZATION,           /*!<    */
    CONT_STATE_STANDBY_REQUEST             = CONT_STATEMACH_STANDBY,                     /*!<    */
    CONT_STATE_NORMAL_REQUEST              = CONT_STATEMACH_NORMAL,                /*!<    */
    CONT_STATE_CHARGE_REQUEST              = CONT_STATEMACH_CHARGE,                /*!<    */
    CONT_STATE_ERROR_REQUEST               = CONT_STATEMACH_ERROR,   /*!<    */
    CONT_STATE_NO_REQUEST                  = CONT_STATEMACH_RESERVED1,                /*!<    */
} CONT_STATE_REQUEST_e;

/**
 * Possible return values when state requests are made to the CONT statemachine
 */
typedef enum {
    CONT_OK                                 = 0,    /*!< CONT --> ok                            */
    CONT_BUSY_OK                            = 1,    /*!< CONT under load --> ok                 */
    CONT_REQUEST_PENDING                    = 2,    /*!< requested to be executed               */
    CONT_REQUEST_IMPOSSIBLE                 = 3,    /*!< requested not possible                 */
    CONT_ILLEGAL_REQUEST                    = 4,    /*!< Request can not be executed            */
    CONT_INIT_ERROR                         = 5,    /*!< Error state: Source: Initialization    */
    CONT_OK_FROM_ERROR                      = 6,    /*!< Return from error --> ok               */
    CONT_ALREADY_INITIALIZED                = 30,   /*!< Initialization of LTC already finished */
    CONT_ILLEGAL_TASK_TYPE                  = 99,   /*!< Illegal                                */
} CONT_RETURN_TYPE_e;

/**
 * @brief Names for connected powerlines.
 */
typedef enum CONT_POWER_LINE_e {
    CONT_POWER_LINE_NONE,    /*!< no power line is connected, contactors are open            */
    CONT_POWER_LINE_0,       /*!< power line 0, e.g. used for the power train                */
#if BS_SEPARATE_POWERLINES == 1
    CONT_POWER_LINE_1,       /*!< power line 1, e.g. used for charging                       */
#endif
} CONT_POWER_LINE_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    */
    CONT_STATE_REQUEST_e statereq;           /*!< current state request made to the state machine                                        */
    CONT_STATEMACH_e state;                  /*!< state of Driver State Machine                                                          */
    CONT_STATEMACH_SUB_e substate;           /*!< current substate of the state machine                                                  */
    CONT_STATEMACH_e laststate;              /*!< previous state of the state machine                                                    */
    CONT_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                            */
    uint16_t OscillationCounter;             /*!< timeout to prevent oscillation of contactors                                           */
    uint8_t PrechargeTryCounter;             /*!< timeout to prevent oscillation of contactors                                           */
    uint16_t PrechargeTimeOut;               /*!< time to wait when precharge has been closed for voltages to settle                     */
    uint8_t triggerentry;                    /*!< counter for re-entrance protection (function running flag)                             */
    uint8_t counter;                         /*!< general purpose counter                                                                */
    CONT_POWER_LINE_e activePowerLine;       /*!< tracks the currently connected power line                                              */
} CONT_STATE_s;


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

/**
 * @brief   Sets the current state request of the state variable cont_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 CONT_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   state request to set
 *
 * @return  #CONT_OK if a state request was made, #CONT_STATE_NO_REQUEST if no state request was made
 */
extern CONT_RETURN_TYPE_e CONT_SetStateRequest(CONT_STATE_REQUEST_e statereq);

/**
 * @brief   Gets the current state.
 *
 * @details This function is used in the functioning of the CONT state machine.
 *
 * @return  current state, taken from #CONT_STATEMACH_e
 */
extern  CONT_STATEMACH_e CONT_GetState(void);

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

/**
 * @brief Returns the active power line.
 *
 * This function returns the value of #cont_state.activePowerLine
 *
 * @return value of #cont_state.activePowerLine
 */
extern CONT_POWER_LINE_e CONT_GetActivePowerLine(void);

/**
 * @brief   Trigger function for the CONT driver state machine.
 *
 * @details This function contains the sequence of events in the CONT state machine. It must be
 *          called time-triggered, every 1ms. It exits without effect, if the function call is
 *          a reentrance.
 */
extern void CONT_Trigger(void);

#endif /* CONTACTOR_H_ */

contactor_cfg.c

/**
 *
 * @copyright &copy; 2010 - 2019, 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    contactor_cfg.c
 * @author  foxBMS Team
 * @date    23.09.2015 (date of creation)
 * @ingroup DRIVERS_CONF
 * @prefix  CONT
 *
 * @brief   Configuration for the driver for the contactors
 *
 */

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

#include "database.h"
#include <float.h>
#include <math.h>

#if BUILD_MODULE_ENABLE_CONTACTOR == 1
/*================== Macros and Definitions ===============================*/

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


const CONT_CONFIG_s cont_contactors_config[BS_NR_OF_CONTACTORS] = {
    {CONT_MAIN_PLUS_CONTROL,        CONT_MAIN_PLUS_FEEDBACK,        CONT_FEEDBACK_NORMALLY_OPEN},
    {CONT_PRECHARGE_PLUS_CONTROL,   CONT_PRECHARGE_PLUS_FEEDBACK,   CONT_FEEDBACK_NORMALLY_OPEN},
    {CONT_MAIN_MINUS_CONTROL,       CONT_MAIN_MINUS_FEEDBACK,       CONT_FEEDBACK_NORMALLY_OPEN},
#if BS_SEPARATE_POWERLINES == 1
    {CONT_CHARGE_MAIN_PLUS_CONTROL,         CONT_CHARGE_MAIN_PLUS_FEEDBACK,         CONT_FEEDBACK_NORMALLY_OPEN},
    {CONT_CHARGE_PRECHARGE_PLUS_CONTROL,    CONT_CHARGE_PRECHARGE_PLUS_FEEDBACK,    CONT_FEEDBACK_NORMALLY_OPEN},
    {CONT_CHARGE_MAIN_MINUS_CONTROL,        CONT_CHARGE_MAIN_MINUS_FEEDBACK,        CONT_FEEDBACK_NORMALLY_OPEN}
#endif /* BS_SEPARATE_POWERLINES == 1 */
};

CONT_ELECTRICAL_STATE_s cont_contactor_states[BS_NR_OF_CONTACTORS] = {
    {0,    CONT_SWITCH_OFF},
    {0,    CONT_SWITCH_OFF},
    {0,    CONT_SWITCH_OFF},
#if BS_SEPARATE_POWERLINES == 1
    {0,    CONT_SWITCH_OFF},
    {0,    CONT_SWITCH_OFF},
    {0,    CONT_SWITCH_OFF},
#endif /* BS_SEPARATE_POWERLINES == 1 */
};

const uint8_t cont_contactors_config_length = sizeof(cont_contactors_config)/sizeof(cont_contactors_config[0]);
const uint8_t cont_contactors_states_length = sizeof(cont_contactor_states)/sizeof(cont_contactor_states[0]);
/*================== Function Prototypes ==================================*/

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

STD_RETURN_TYPE_e CONT_CheckPrecharge(CONT_WHICH_POWERLINE_e caller) {
    DATA_BLOCK_CURRENT_SENSOR_s current_tab = {0};
    STD_RETURN_TYPE_e retVal = E_NOT_OK;

    DB_ReadBlock(&current_tab, DATA_BLOCK_ID_CURRENT_SENSOR);
    float cont_prechargeVoltDiff_mV = 0.0;
    int32_t current_mA = 0;

    /* Only current not current direction is checked */
    if (current_tab.current > 0) {
        current_mA = current_tab.current;
    } else {
        current_mA = -current_tab.current;
    }

    if (caller == CONT_POWERLINE_NORMAL) {
        cont_prechargeVoltDiff_mV = 0.0;
        /* Voltage difference between V2 and V3 of Isabellenhuette current sensor */
        if (current_tab.voltage[1] > current_tab.voltage[2]) {
            cont_prechargeVoltDiff_mV = current_tab.voltage[1] - current_tab.voltage[2];
        } else {
            cont_prechargeVoltDiff_mV = current_tab.voltage[2] - current_tab.voltage[1];
        }

        if ((cont_prechargeVoltDiff_mV < CONT_PRECHARGE_VOLTAGE_THRESHOLD_mV) && (current_mA < CONT_PRECHARGE_CURRENT_THRESHOLD_mA)) {
            retVal = E_OK;
        } else {
            retVal = E_NOT_OK;
        }
    } else if (caller == CONT_POWERLINE_CHARGE) {
        cont_prechargeVoltDiff_mV = 0.0;
        /* Voltage difference between V1 and V3 of Isabellenhuette current sensor */
        if (current_tab.voltage[0] > current_tab.voltage[2]) {
            cont_prechargeVoltDiff_mV = current_tab.voltage[0] - current_tab.voltage[2];
        } else {
            cont_prechargeVoltDiff_mV = current_tab.voltage[2] - current_tab.voltage[0];
        }

        if ((cont_prechargeVoltDiff_mV < CONT_CHARGE_PRECHARGE_VOLTAGE_THRESHOLD_mV) && (current_mA < CONT_CHARGE_PRECHARGE_CURRENT_THRESHOLD_mA)) {
            retVal = E_OK;
        } else {
            retVal = E_NOT_OK;
        }
    }
    return retVal;
}


STD_RETURN_TYPE_e CONT_CheckFuse(CONT_WHICH_POWERLINE_e caller) {
#if (BS_CHECK_FUSE_PLACED_IN_NORMAL_PATH == TRUE) || (BS_CHECK_FUSE_PLACED_IN_CHARGE_PATH == TRUE)
    STD_RETURN_TYPE_e fuseState = E_NOT_OK;
    DATA_BLOCK_CURRENT_SENSOR_s curSensTab;
    DATA_BLOCK_CONTFEEDBACK_s contFeedbackTab;
    uint32_t voltDiff_mV = 0;
    STD_RETURN_TYPE_e checkFuseState = E_NOT_OK;

    DB_ReadBlock(&curSensTab, DATA_BLOCK_ID_CURRENT_SENSOR);
    DB_ReadBlock(&contFeedbackTab, DATA_BLOCK_ID_CONTFEEDBACK);

    if (caller == CONT_POWERLINE_NORMAL) {
        /* Fuse state can only be checked if plus and minus contactors are closed. */
        if ((((contFeedbackTab.contactor_feedback & 0x01) == 0x01) ||
                ((contFeedbackTab.contactor_feedback & 0x02) == 0x02)) &&
                ((contFeedbackTab.contactor_feedback & 0x04) == 0x04)) {
                    /* main plus OR main precharge AND minus are closed */
                checkFuseState = E_OK;
        }  else {
            /* Fuse state can't be checked if no plus contactors are closed */
            checkFuseState = E_NOT_OK;
        }
        /* Check voltage difference between battery voltage and voltage after fuse */
        if (checkFuseState == E_OK) {
            if (curSensTab.voltage[0] > curSensTab.voltage[1]) {
                voltDiff_mV = curSensTab.voltage[0] - curSensTab.voltage[1];
            } else {
                voltDiff_mV = curSensTab.voltage[1] - curSensTab.voltage[0];
            }

            /* If voltage difference is larger than max. allowed voltage drop over fuse*/
            if (voltDiff_mV > BS_MAX_VOLTAGE_DROP_OVER_FUSE_mV) {
                fuseState = E_NOT_OK;
            } else {
                fuseState = E_OK;
            }
        } else {
            /* Can't draw any conclusions about fuse state -> do not return E_NOT_OK */
            fuseState = E_OK;
        }
    } else if (caller == CONT_POWERLINE_CHARGE) {
        /* Fuse state can only be checked if plus and minus contactors are closed. */
        if ((((contFeedbackTab.contactor_feedback & 0x08) == 0x08) ||
                ((contFeedbackTab.contactor_feedback & 0x10) == 0x10)) &&
                ((contFeedbackTab.contactor_feedback & 0x20) == 0x20)) {
            /* charge plus OR charge precharge AND minus are closed */
                checkFuseState = E_OK;
        } else {
            /* Fuse state can't be checked if no plus contactors are closed */
            checkFuseState = E_NOT_OK;
        }
        /* Check voltage difference between battery voltage and voltage after fuse */
        if (checkFuseState == E_OK) {
            if (curSensTab.voltage[0] > curSensTab.voltage[1]) {
                voltDiff_mV = curSensTab.voltage[0] - curSensTab.voltage[2];
            } else {
                voltDiff_mV = curSensTab.voltage[2] - curSensTab.voltage[0];
            }

            /* If voltage difference is larger than max. allowed voltage drop over fuse*/
            if (voltDiff_mV > BS_MAX_VOLTAGE_DROP_OVER_FUSE_mV) {
                fuseState = E_NOT_OK;
            } else {
                fuseState = E_OK;
            }
        } else {
            /* Can't draw any conclusions about fuse state -> do not return E_NOT_OK */
            fuseState = E_OK;
        }
    }
#if BS_CHECK_FUSE_PLACED_IN_NORMAL_PATH == TRUE
    if (fuseState == E_OK) {
        /* Fuse state ok -> check precharging */
        DIAG_Handler(DIAG_CH_FUSE_STATE_NORMAL, DIAG_EVENT_OK, 0);
    } else {
        /* Fuse tripped -> switch to error state */
        DIAG_Handler(DIAG_CH_FUSE_STATE_NORMAL, DIAG_EVENT_NOK, 0);
    }
#endif  /* BS_CHECK_FUSE_PLACED_IN_NORMAL_PATH == TRUE */
#if BS_CHECK_FUSE_PLACED_IN_CHARGE_PATH == TRUE
    if (fuseState == E_OK) {
        /* Fuse state ok -> check precharging */
        DIAG_Handler(DIAG_CH_FUSE_STATE_CHARGE, DIAG_EVENT_OK, 0);
    } else {
        /* Fuse tripped -> switch to error state */
        DIAG_Handler(DIAG_CH_FUSE_STATE_CHARGE, DIAG_EVENT_NOK, 0);
    }
#endif  /* BS_CHECK_FUSE_PLACED_IN_CHARGE_PATH == TRUE */
    return fuseState;
#else /* BS_CHECK_FUSE_PLACED_IN_NORMAL_PATH == FALSE && BS_CHECK_FUSE_PLACED_IN_CHARGE_PATH == FALSE */
    return E_OK;
#endif /* BS_CHECK_FUSE_PLACED_IN_NORMAL_PATH || BS_CHECK_FUSE_PLACED_IN_CHARGE_PATH */
}
#endif /* BUILD_MODULE_ENABLE_CONTACTOR */

contactor_cfg.h

/**
 *
 * @copyright &copy; 2010 - 2019, 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    contactor_cfg.h
 * @author  foxBMS Team
 * @date    23.09.2015 (date of creation)
 * @ingroup DRIVERS_CONF
 * @prefix  CONT
 *
 * @brief   Header for the configuration for the driver for the contactors
 *
 */

#ifndef CONTACTOR_CFG_H_
#define CONTACTOR_CFG_H_

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

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

/**
 * @ingroup CONFIG_CONTACTOR
 * defines the number of bad countings of opening contactors at too
 *  high current
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [9,11]
*/
#define CONT_NUMBER_OF_BAD_COUNTINGS 10

/**
 * @ingroup CONFIG_CONTACTOR
 * This macro describes the limiting current from the the positive to
 * negative side of the contactor at which a damaging of the
 * contactor occurs. If this limit is exceeded the contactor
 * module makes an entry in the diagnosis module indicating a
 * switching off of the contactors under a bad  condition
 *
 * \par Type:
 * float
 * \par Default:
 * 1.0
 * \par Range:
 * [1.0,2.0]
*/
#define BAD_SWITCHOFF_CURRENT_POS 100000.0f

/**
 * @ingroup CONFIG_CONTACTOR
 * This macro describes the limiting current from the the negative to
 * positive side of the contactor at which a damaging of the
 * contactor occurs. If this limit is exceeded the contactor
 * module makes an entry in the diagnosis module indicating a
 * switching off of the contactors under a bad  condition
 *
 * \par Type:
 * float
 * \par Default:
 * -1.0
 * \par Range:
 * [-2.0,-1.0]
*/
#define BAD_SWITCHOFF_CURRENT_NEG -1000000.0f

/*
 * The number of defines per contactor must be the same as the length
 *  of the array cont_contactors_cfg in contactor_cfg.c
 * Every contactor consists of 1 control pin and 1 feedback pin
 * counting together as 1 contactor.
 * E.g. if you have 1 contactor your define has to be:
 *      #define CONT_MAIN_PLUS_CONTROL      IO_PIN_CONTACTOR_0_CONTROL
 *      #define CONT_MAIN_PLUS_FEEDBACK     IO_PIN_CONTACTOR_0_FEEDBACK
 */
#define CONT_MAIN_PLUS_CONTROL                  IO_PIN_CONTACTOR_0_CONTROL
#define CONT_MAIN_PLUS_FEEDBACK                 IO_PIN_CONTACTOR_0_FEEDBACK

#define CONT_PRECHARGE_PLUS_CONTROL             IO_PIN_CONTACTOR_1_CONTROL
#define CONT_PRECHARGE_PLUS_FEEDBACK            IO_PIN_CONTACTOR_1_FEEDBACK

#define CONT_MAIN_MINUS_CONTROL                 IO_PIN_CONTACTOR_2_CONTROL
#define CONT_MAIN_MINUS_FEEDBACK                IO_PIN_CONTACTOR_2_FEEDBACK

#if BS_SEPARATE_POWERLINES == 1
#define CONT_CHARGE_MAIN_PLUS_CONTROL           IO_PIN_CONTACTOR_3_CONTROL
#define CONT_CHARGE_MAIN_PLUS_FEEDBACK          IO_PIN_CONTACTOR_3_FEEDBACK

#define CONT_CHARGE_PRECHARGE_PLUS_CONTROL      IO_PIN_CONTACTOR_4_CONTROL
#define CONT_CHARGE_PRECHARGE_PLUS_FEEDBACK     IO_PIN_CONTACTOR_4_FEEDBACK

#define CONT_CHARGE_MAIN_MINUS_CONTROL          IO_PIN_CONTACTOR_5_CONTROL
#define CONT_CHARGE_MAIN_MINUS_FEEDBACK         IO_PIN_CONTACTOR_5_FEEDBACK
#endif /* BS_SEPARATE_POWERLINES == 1 */
/*
 * additional possible contactors from the io definition
#define CONT_X0_CONTROL                         PIN_CONTACTOR_3_CONTROL
#define CONT_X0_FEEDBACK                        PIN_CONTACTOR_3_FEEDBACK

#define CONT_X1_CONTROL                         PIN_CONTACTOR_4_CONTROL
#define CONT_X1_FEEDBACK                        PIN_CONTACTOR_4_FEEDBACK

#define CONT_X2_CONTROL                         PIN_CONTACTOR_5_CONTROL
#define CONT_X2_FEEDBACK                        PIN_CONTACTOR_5_FEEDBACK
*/


/**
 * This define MUST represent the cycle time of the task in which context the
 * functions run, e.g., if the CONT_Trigger() is running in the 10 ms task
 * then the define must be set to 10.
 *
 * This define also sets the minimum time.
 */

#define CONT_TASK_CYCLE_CONTEXT_MS (10)

/**
 * Counter limit used to prevent contactor oscillation
 */

#define CONT_OSCILLATION_LIMIT 500


/**
 * Number of allowed tries to close contactors
 */
#define CONT_PRECHARGE_TRIES 3

/**
 * Delay between open first and second contactor
 */

#define CONT_DELAY_BETWEEN_OPENING_CONTACTORS_MS        ((50) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after opening second contactor
 */

#define CONT_DELAY_AFTER_OPENING_SECOND_CONTACTORS_MS   ((50) *  (CONT_TASK_CYCLE_CONTEXT_MS))


/**
 * CONT statemachine short time definition in ms
 */

#define CONT_STATEMACH_SHORTTIME_MS                     (CONT_TASK_CYCLE_CONTEXT_MS)


/**
 * CONT statemachine time to wait after contactors opened because precharge failed in ms
 */
#define CONT_STATEMACH_TIMEAFTERPRECHARGEFAIL_MS        ((100) * (CONT_TASK_CYCLE_CONTEXT_MS))


/*================== Main precharge configuration ====================*/

/**
 * Precharge timeout in ms
 */
#define CONT_PRECHARGE_TIMEOUT_MS ((500) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after closing main minus in ms
 */
#define CONT_STATEMACH_WAIT_AFTER_CLOSING_MINUS_MS ((50) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after closing precharge in ms
 */

#define CONT_STATEMACH_WAIT_AFTER_CLOSING_PRECHARGE_MS ((100) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after closing main plus in ms
 */
#define CONT_STATEMACH_WAIT_AFTER_CLOSING_PLUS_MS ((100) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after opening precharge in ms
 */
#define CONT_STATEMACH_WAIT_AFTER_OPENING_PRECHARGE_MS ((50) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * @ingroup CONFIG_CONTACTOR
 * \par Type:
 * int
 * \par Default:
 * 1000
 * \par Range:
 * [1000,3000]
 * \par Unit:
 * V
*/
#define CONT_PRECHARGE_VOLTAGE_THRESHOLD_mV     1000  /* mV */

/**
 * @ingroup CONFIG_CONTACTOR
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [50,500]
 * \par Unit:
 * mA
*/
#define CONT_PRECHARGE_CURRENT_THRESHOLD_mA     50  /* mA */


/*================== Charge precharge configuration ====================*/

/**
 * Charge precharge timeout in ms
 */
#define CONT_CHARGE_PRECHARGE_TIMEOUT_MS ((500) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after closing charge minus in ms
 */
#define CONT_STATEMACH_CHARGE_WAIT_AFTER_CLOSING_MINUS_MS ((50) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after closing charge precharge in ms
 */

#define CONT_STATEMACH_CHARGE_WAIT_AFTER_CLOSING_PRECHARGE_MS ((100) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * Delay after closing charge plus in ms
 */
#define CONT_STATEMACH_CHARGE_WAIT_AFTER_CLOSING_PLUS_MS ((100) * (CONT_TASK_CYCLE_CONTEXT_MS))

/**
 * @ingroup CONFIG_CONTACTOR
 * \par Type:
 * int
 * \par Default:
 * 1000
 * \par Range:
 * [1000,3000]
 * \par Unit:
 * V
*/
#define CONT_CHARGE_PRECHARGE_VOLTAGE_THRESHOLD_mV   1000  /* mV */

/**
 * @ingroup CONFIG_CONTACTOR
 * \par Type:
 * int
 * \par Default:
 * 10
 * \par Range:
 * [50,500]
 * \par Unit:
 * mA
*/
#define CONT_CHARGE_PRECHARGE_CURRENT_THRESHOLD_mA  50  /* mA */


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

/**
 * Symbolic names for contactors' possible states
 */
typedef enum {
    CONT_SWITCH_OFF     = 0,    /*!< Contactor off         --> Contactor is open           */
    CONT_SWITCH_ON      = 1,    /*!< Contactor on          --> Contactor is closed         */
    CONT_SWITCH_UNDEF   = 2,    /*!< Contactor undefined   --> Contactor state not known   */
} CONT_ELECTRICAL_STATE_TYPE_s;

/**
 * Symbolic names for the contactors, which are used in
 * the contactor_config[] array
 */
typedef enum {
    CONT_MAIN_PLUS              = 0,    /*!< Main contactor in the positive path of the powerline      */
    CONT_PRECHARGE_PLUS         = 1,    /*!< Precharge contactor in the positive path of the powerline */
    CONT_MAIN_MINUS             = 2,    /*!< Main contactor in the negative path of the powerline      */
#if BS_SEPARATE_POWERLINES == 1
    CONT_CHARGE_MAIN_PLUS       = 3,    /*!< Main contactor in the positive charge path of the powerline      */
    CONT_CHARGE_PRECHARGE_PLUS  = 4,    /*!< Precharge contactor in the positive charge path of the powerline */
    CONT_CHARGE_MAIN_MINUS      = 5,    /*!< Main contactor in the negative charge path of the powerline      */
#endif /* BS_SEPARATE_POWERLINES == 1 */
} CONT_NAMES_e;

/**
 * Symbolic names defining the electric behavior of the contactor
 */
typedef enum {
    CONT_FEEDBACK_NORMALLY_OPEN     = 0,    /*!< Feedback line of a contactor is normally open      */
    CONT_FEEDBACK_NORMALLY_CLOSED   = 1,    /*!< Feedback line of a contactor is normally closed    */
    CONT_HAS_NO_FEEDBACK    = 0xFF  /*!< Feedback line of the contactor is not used         */
} CONT_FEEDBACK_TYPE_e;

typedef struct {
    CONT_ELECTRICAL_STATE_TYPE_s set;
    CONT_ELECTRICAL_STATE_TYPE_s feedback;
} CONT_ELECTRICAL_STATE_s;

typedef struct {
    IO_PORTS_e control_pin;
    IO_PORTS_e feedback_pin;
    CONT_FEEDBACK_TYPE_e feedback_pin_type;
} CONT_CONFIG_s;


typedef enum {
    CONT_POWERLINE_NORMAL   = 0,
#if BS_SEPARATE_POWERLINES == 1
    CONT_POWERLINE_CHARGE   = 1
#endif /* BS_SEPARATE_POWERLINES == 1 */
} CONT_WHICH_POWERLINE_e;

extern const CONT_CONFIG_s cont_contactors_config[BS_NR_OF_CONTACTORS];
extern CONT_ELECTRICAL_STATE_s cont_contactor_states[BS_NR_OF_CONTACTORS];

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

/**
 * @brief   Checks if the current limitations are violated
 *
 * @return  E_OK if the current limitations are NOT violated, else E_NOT_OK (type: STD_RETURN_TYPE_e)
 */
extern STD_RETURN_TYPE_e CONT_CheckPrecharge(CONT_WHICH_POWERLINE_e caller);


/**
 * Function to check fuse state. Fuse state can only be checked if at least
 * plus contactors are closed. Furthermore fuse needs to be placed in plus
 * path and monitored by Isabellenhuette HV measurement
 *
 * @return  Returns E_OK if fuse is intact, and E_NOT_OK if fuse is tripped.
 *
 */
extern STD_RETURN_TYPE_e CONT_CheckFuse(CONT_WHICH_POWERLINE_e caller);

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


#endif /* CONTACTOR_CFG_H_ */