CONTACTOR Module Sources¶
contactor.c¶
/**
*
* @copyright © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file 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 "bms.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, ¤tAtSwitchOff)) {
/* 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 (BMS_GetBatterySystemState() == BMS_DISCHARGING) {
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 (BMS_GetBatterySystemState() == BMS_DISCHARGING) {
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 © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file 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 © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file 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(¤t_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 © 2010 - 2020, Fraunhofer-Gesellschaft zur Foerderung der
* angewandten Forschung e.V. All rights reserved.
*
* BSD 3-Clause License
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* We kindly request you to use one or more of the following phrases to refer
* to foxBMS in your hardware, software, documentation or advertising
* materials:
*
* ″This product uses parts of foxBMS®″
*
* ″This product includes parts of foxBMS®″
*
* ″This product is derived from foxBMS®″
*
*/
/**
* @file 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,
CONT_POWERLINE_CHARGE
} 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_ */