LTC Module Sources


ltc.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    ltc.c
 * @author  foxBMS Team
 * @date    01.09.2015 (date of creation)
 * @ingroup DRIVERS
 * @prefix  LTC
 *
 * @brief   Driver for the LTC monitoring chip.
 *
 */

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

#include "database.h"
#include "diag.h"
#include "ltc_pec.h"
#include "os.h"
#include "slaveplausibility.h"

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

/**
 * TI port expander register addresses
 *
 */

#define LTC_PORT_EXPANDER_TI_INPUT_REG_ADR    0x00
#define LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR   0x01
#define LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR   0x03

/**
 * LTC COMM definitions
 */

#define LTC_ICOM_START              0x60
#define LTC_ICOM_STOP               0x10
#define LTC_ICOM_BLANK              0x00
#define LTC_ICOM_NO_TRANSMIT        0x70
#define LTC_FCOM_MASTER_ACK         0x00
#define LTC_FCOM_MASTER_NACK        0x08
#define LTC_FCOM_MASTER_NACK_STOP   0x09

#define LTC_MAX_SUPPORTED_CELLS         12

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

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

static uint8_t ltc_used_cells_index = 0;

static DATA_BLOCK_CELLVOLTAGE_s ltc_cellvoltage;
static DATA_BLOCK_CELLTEMPERATURE_s ltc_celltemperature;
static DATA_BLOCK_MINMAX_s ltc_minmax;
static DATA_BLOCK_BALANCING_FEEDBACK_s ltc_balancing_feedback;
static DATA_BLOCK_USER_MUX_s ltc_user_mux;
static DATA_BLOCK_BALANCING_CONTROL_s ltc_balancing_control;
static DATA_BLOCK_SLAVE_CONTROL_s ltc_slave_control;
static DATA_BLOCK_ALLGPIOVOLTAGE_s ltc_allgpiovoltage;
static DATA_BLOCK_OPENWIRE_s ltc_openwire;
static uint16_t ltc_openwire_pup_buffer[BS_NR_OF_BAT_CELLS];
static uint16_t ltc_openwire_pdown_buffer[BS_NR_OF_BAT_CELLS];
static int32_t ltc_openwire_delta[BS_NR_OF_BAT_CELLS];

static LTC_ERRORTABLE_s LTC_ErrorTable[LTC_N_LTC];  /* init in LTC_ResetErrorTable-function */


static LTC_STATE_s ltc_state = {
    .timer                   = 0,
    .statereq                = LTC_STATE_NO_REQUEST,
    .state                   = LTC_STATEMACH_UNINITIALIZED,
    .substate                = 0,
    .laststate               = LTC_STATEMACH_UNINITIALIZED,
    .lastsubstate            = 0,
    .adcModereq              = LTC_ADCMODE_FAST_DCP0,
    .adcMode                 = LTC_ADCMODE_FAST_DCP0,
    .adcMeasChreq            = LTC_ADCMEAS_UNDEFINED,
    .adcMeasCh               = LTC_ADCMEAS_UNDEFINED,
    .numberOfMeasuredMux     = 32,
    .triggerentry            = 0,
    .ErrRetryCounter         = 0,
    .ErrRequestCounter       = 0,
    .VoltageSampleTime       = 0,
    .muxSampleTime           = 0,
    .commandDataTransferTime = 3,
    .commandTransferTime     = 3,
    .gpioClocksTransferTime  = 3,
    .muxmeas_seqptr          = NULL_PTR,
    .muxmeas_seqendptr       = NULL_PTR,
    .muxmeas_nr_end          = 0,
    .first_measurement_made  = FALSE,
    .ltc_muxcycle_finished   = E_NOT_OK,
    .check_spi_flag          = FALSE,
    .balance_control_done     = FALSE,
};

static const uint8_t ltc_cmdDummy[1]={0x00};
static const uint8_t ltc_cmdWRCFG[4]={0x00, 0x01, 0x3D, 0x6E};
static const uint8_t ltc_cmdWRCFG2[4]={0x00, 0x24, 0xB1, 0x9E};

static const uint8_t ltc_cmdRDCVA[4] = {0x00, 0x04, 0x07, 0xC2};
static const uint8_t ltc_cmdRDCVB[4] = {0x00, 0x06, 0x9A, 0x94};
static const uint8_t ltc_cmdRDCVC[4] = {0x00, 0x08, 0x5E, 0x52};
static const uint8_t ltc_cmdRDCVD[4] = {0x00, 0x0A, 0xC3, 0x04};
static const uint8_t ltc_cmdRDCVE[4] = {0x00, 0x09, 0xD5, 0x60};
static const uint8_t ltc_cmdRDCVF[4] = {0x00, 0x0B, 0x48, 0x36};
static const uint8_t ltc_cmdWRCOMM[4] = {0x07, 0x21, 0x24, 0xB2};
static const uint8_t ltc_cmdSTCOMM[4] = {0x07, 0x23, 0xB9, 0xE4};
static const uint8_t ltc_cmdRDCOMM[4] = {0x07, 0x22, 0x32, 0xD6};
static const uint8_t ltc_cmdRDAUXA[4] = {0x00, 0x0C, 0xEF, 0xCC};
static const uint8_t ltc_cmdRDAUXB[4] = {0x00, 0x0E, 0x72, 0x9A};
static const uint8_t ltc_cmdRDAUXC[4] = {0x00, 0x0D, 0x64, 0xFE};
static const uint8_t ltc_cmdRDAUXD[4] = {0x00, 0x0F, 0xF9, 0xA8};

/* static const uint8_t ltc_cmdMUTE[4] = {0x00, 0x28, 0xE8, 0x0E};                    !< MUTE discharging via S pins */
/* static const uint8_t ltc_cmdUNMUTE[4] = {0x00, 0x29, 0x63, 0x3C};                  !< UN-MUTE discharging via S pins */

/* LTC I2C commands */
/* static const uint8_t ltc_I2CcmdDummy[6] = {0x7F, 0xF9, 0x7F, 0xF9, 0x7F, 0xF9};      !< dummy command (no transmit) */

static const uint8_t ltc_I2CcmdTempSens0[6] = {0x69, 0x08, 0x00, 0x09, 0x7F, 0xF9};  /*!< sets the internal data pointer of the temperature sensor (address 0x48) to 0x00 */
static const uint8_t ltc_I2CcmdTempSens1[6] = {0x69, 0x18, 0x0F, 0xF0, 0x0F, 0xF9};  /*!< reads two data bytes from the temperature sensor */

static const uint8_t ltc_I2CcmdPortExpander1[6] = {0x64, 0x18, 0x0F, 0xF9, 0x7F, 0xF9};  /*!< reads one data byte from the port expander */

/* Cells */
static const uint8_t ltc_cmdADCV_normal_DCP0[4] = {0x03, 0x60, 0xF4, 0x6C};        /*!< All cells, normal mode, discharge not permitted (DCP=0)    */
static const uint8_t ltc_cmdADCV_normal_DCP1[4] = {0x03, 0x70, 0xAF, 0x42};        /*!< All cells, normal mode, discharge permitted (DCP=1)        */
static const uint8_t ltc_cmdADCV_filtered_DCP0[4] = {0x03, 0xE0, 0xB0, 0x4A};      /*!< All cells, filtered mode, discharge not permitted (DCP=0)  */
static const uint8_t ltc_cmdADCV_filtered_DCP1[4] = {0x03, 0xF0, 0xEB, 0x64};      /*!< All cells, filtered mode, discharge permitted (DCP=1)      */
static const uint8_t ltc_cmdADCV_fast_DCP0[4] = {0x02, 0xE0, 0x38, 0x06};          /*!< All cells, fast mode, discharge not permitted (DCP=0)      */
static const uint8_t ltc_cmdADCV_fast_DCP1[4] = {0x02, 0xF0, 0x63, 0x28};          /*!< All cells, fast mode, discharge permitted (DCP=1)          */
static const uint8_t ltc_cmdADCV_fast_DCP0_twocells[4] = {0x02, 0xE1, 0xb3, 0x34}; /*!< Two cells (1 and 7), fast mode, discharge not permitted (DCP=0) */

/* GPIOs  */
static const uint8_t ltc_cmdADAX_normal_GPIO1[4] = {0x05, 0x61, 0x58, 0x92};      /*!< Single channel, GPIO 1, normal mode   */
static const uint8_t ltc_cmdADAX_filtered_GPIO1[4] = {0x05, 0xE1, 0x1C, 0xB4};    /*!< Single channel, GPIO 1, filtered mode */
static const uint8_t ltc_cmdADAX_fast_GPIO1[4] = {0x04, 0xE1, 0x94, 0xF8};        /*!< Single channel, GPIO 1, fast mode     */
static const uint8_t ltc_cmdADAX_normal_GPIO2[4] = {0x05, 0x62, 0x4E, 0xF6};      /*!< Single channel, GPIO 2, normal mode   */
static const uint8_t ltc_cmdADAX_filtered_GPIO2[4] = {0x05, 0xE2, 0x0A, 0xD0};    /*!< Single channel, GPIO 2, filtered mode */
static const uint8_t ltc_cmdADAX_fast_GPIO2[4] = {0x04, 0xE2, 0x82, 0x9C};        /*!< Single channel, GPIO 2, fast mode     */
static const uint8_t ltc_cmdADAX_normal_GPIO3[4] = {0x05, 0x63, 0xC5, 0xC4};      /*!< Single channel, GPIO 3, normal mode   */
static const uint8_t ltc_cmdADAX_filtered_GPIO3[4] = {0x05, 0xE3, 0x81, 0xE2};    /*!< Single channel, GPIO 3, filtered mode */
static const uint8_t ltc_cmdADAX_fast_GPIO3[4] = {0x04, 0xE3, 0x09, 0xAE};        /*!< Single channel, GPIO 3, fast mode     */
/* static const uint8_t ltc_cmdADAX_normal_GPIO4[4] = {0x05, 0x64, 0x62, 0x3E};      !< Single channel, GPIO 4, normal mode   */
/* static const uint8_t ltc_cmdADAX_filtered_GPIO4[4] = {0x05, 0xE4, 0x26, 0x18};    !< Single channel, GPIO 4, filtered mode */
/* static const uint8_t ltc_cmdADAX_fast_GPIO4[4] = {0x04, 0xE4, 0xAE, 0x54};        !< Single channel, GPIO 4, fast mode     */
/* static const uint8_t ltc_cmdADAX_normal_GPIO5[4] = {0x05, 0x65, 0xE9, 0x0C};      !< Single channel, GPIO 5, normal mode   */
/* static const uint8_t ltc_cmdADAX_filtered_GPIO5[4] = {0x05, 0xE5, 0xAD, 0x2A};    !< Single channel, GPIO 5, filtered mode */
/* static const uint8_t ltc_cmdADAX_fast_GPIO5[4] = {0x04, 0xE5, 0x25, 0x66};        !< Single channel, GPIO 5, fast mode     */
static const uint8_t ltc_cmdADAX_normal_ALLGPIOS[4] = {0x05, 0x60, 0xD3, 0xA0};   /*!< All channels, normal mode             */
static const uint8_t ltc_cmdADAX_filtered_ALLGPIOS[4] = {0x05, 0xE0, 0x97, 0x86}; /*!< All channels, filtered mode           */
static const uint8_t ltc_cmdADAX_fast_ALLGPIOS[4] = {0x04, 0xE0, 0x1F, 0xCA};     /*!< All channels, fast mode               */

/* Open-wire */
static const uint8_t ltc2_BC_cmdADOW_PUP_normal_DCP0[4] = {0x03, 0x68, 0x1C, 0x62};    /*!< Broadcast, Pull-up current, All cells, normal mode, discharge not permitted (DCP=0)   */
static const uint8_t ltc2_BC_cmdADOW_PDOWN_normal_DCP0[4] = {0x03, 0x28, 0xFB, 0xE8};  /*!< Broadcast, Pull-down current, All cells, normal mode, discharge not permitted (DCP=0) */
static const uint8_t ltc2_BC_cmdADOW_PUP_filtered_DCP0[4] = {0x03, 0xE8, 0x1C, 0x62};    /*!< Broadcast, Pull-up current, All cells, filtered mode, discharge not permitted (DCP=0)   */
static const uint8_t ltc2_BC_cmdADOW_PDOWN_filtered_DCP0[4] = {0x03, 0xA8, 0xFB, 0xE8};  /*!< Broadcast, Pull-down current, All cells, filtered mode, discharge not permitted (DCP=0) */


static uint8_t ltc_RXPECbuffer[LTC_N_BYTES_FOR_DATA_TRANSMISSION];
static uint8_t ltc_TXPECbuffer[LTC_N_BYTES_FOR_DATA_TRANSMISSION];
static uint8_t ltc_TXBuffer[LTC_N_BYTES_FOR_DATA_TRANSMISSION_DATA_ONLY];

static uint8_t ltc_TXBufferClock[4+9];
static uint8_t ltc_TXPECBufferClock[4+9];


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

static void LTC_Initialize_Database(void);
static void LTC_SaveBalancingFeedback(uint8_t *DataBufferSPI_RX);
static void LTC_Get_BalancingControlValues(void);
static void LTC_StateTransition(LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms);
static void LTC_CondBasedStateTransition(STD_RETURN_TYPE_e retVal, DIAG_CH_ID_e diagCode, uint8_t state_ok, uint8_t substate_ok, uint16_t timer_ms_ok, uint8_t state_nok, uint8_t substate_nok, uint16_t timer_ms_nok);

static STD_RETURN_TYPE_e LTC_BalanceControl(uint8_t registerSet);

static void LTC_ResetErrorTable(void);
static STD_RETURN_TYPE_e LTC_Init(void);

static STD_RETURN_TYPE_e LTC_StartVoltageMeasurement(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e  adcMeasCh);
static STD_RETURN_TYPE_e LTC_StartGPIOMeasurement(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e  adcMeasCh);
static STD_RETURN_TYPE_e LTC_StartOpenWireMeasurement(LTC_ADCMODE_e adcMode, uint8_t PUP);

static uint16_t LTC_Get_MeasurementTCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e  adcMeasCh);
static void LTC_SaveRXtoVoltagebuffer(uint8_t registerSet, uint8_t *rxBuffer);
static void LTC_SaveRXtoGPIOBuffer(uint8_t registerSet, uint8_t *rxBuffer);

static STD_RETURN_TYPE_e LTC_RX_PECCheck(uint8_t *DataBufferSPI_RX_with_PEC);
static STD_RETURN_TYPE_e LTC_RX(uint8_t *Command, uint8_t *DataBufferSPI_RX_with_PEC);
static STD_RETURN_TYPE_e LTC_TX(uint8_t *Command, uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC);
static void LTC_SetMUXChCommand(uint8_t *DataBufferSPI_TX, uint8_t mux, uint8_t channel);
static uint8_t LTC_SendEEPROMReadCommand(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t step);
static void LTC_SetEEPROMReadCommand(uint8_t step, uint8_t *DataBufferSPI_TX);
static void LTC_EEPROMSaveReadValue(uint8_t *rxBuffer);
static uint8_t LTC_SendEEPROMWriteCommand(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t step);
static void LTC_SetEEPROMWriteCommand(uint8_t step, uint8_t *DataBufferSPI_TX);
static uint8_t LTC_SetMuxChannel(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t mux, uint8_t channel);
static uint8_t LTC_SetPortExpander(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC);
static void LTC_PortExpanderSaveValues(uint8_t *rxBuffer);
static void LTC_TempSensSaveTemp(uint8_t *rxBuffer);
static uint8_t LTC_SetPortExpanderDirection_TI(LTC_PORT_EXPANDER_TI_DIRECTION_e direction, uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC);
static uint8_t LTC_SetPortExpander_Output_TI(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC);
static uint8_t LTC_GetPortExpander_Input_TI(uint8_t step, uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC);
static void LTC_PortExpanderSaveValues_TI(uint8_t *rxBuffer);

static STD_RETURN_TYPE_e LTC_I2CClock(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC);
static STD_RETURN_TYPE_e LTC_Send_I2C_Command(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t *cmd_data);

static uint8_t LTC_I2CCheckACK(uint8_t *DataBufferSPI_RX, int mux);

static void LTC_SaveMuxMeasurement(uint8_t *DataBufferSPI_RX, LTC_MUX_CH_CFG_s  *muxseqptr);


static uint32_t LTC_GetSPIClock(void);
static void LTC_SetTransferTimes(void);

static LTC_RETURN_TYPE_e LTC_CheckStateRequest(LTC_STATE_REQUEST_e statereq);

static STD_RETURN_TYPE_e LTC_TimerElapsedAndSPITransmitOngoing(uint16_t timer);

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

/*================== Public functions =====================================*/

/*================== Static functions =====================================*/
/**
 * @brief   in the database, initializes the fields related to the LTC drivers.
 *
 * This function loops through all the LTC-related data fields in the database
 * and sets them to 0. It should be called in the initialization or re-initialization
 * routine of the LTC driver.
 */
static void LTC_Initialize_Database(void) {
    uint16_t i = 0;

    ltc_cellvoltage.state = 0;
    ltc_cellvoltage.timestamp = 0;
    ltc_minmax.voltage_min = 0;
    ltc_minmax.voltage_max = 0;
    ltc_minmax.voltage_module_number_min = 0;
    ltc_minmax.voltage_module_number_max = 0;
    ltc_minmax.voltage_cell_number_min = 0;
    ltc_minmax.voltage_cell_number_max = 0;
    for (i=0; i < BS_NR_OF_BAT_CELLS; i++) {
        ltc_cellvoltage.voltage[i] = 0;
        ltc_openwire_pup_buffer[i] = 0;
        ltc_openwire_pdown_buffer[i] = 0;
        ltc_openwire_delta[i] = 0;
    }

    ltc_celltemperature.state = 0;
    ltc_celltemperature.timestamp = 0;
    ltc_minmax.temperature_min = 0;
    ltc_minmax.temperature_max = 0;
    ltc_minmax.temperature_module_number_min = 0;
    ltc_minmax.temperature_module_number_max = 0;
    ltc_minmax.temperature_sensor_number_min = 0;
    ltc_minmax.temperature_sensor_number_max = 0;
    for (i=0; i < BS_NR_OF_TEMP_SENSORS; i++) {
        ltc_celltemperature.temperature[i] = 0;
    }

    ltc_balancing_feedback.state = 0;
    ltc_balancing_feedback.timestamp = 0;
    ltc_balancing_control.state = 0;
    ltc_balancing_control.timestamp = 0;
    for (i=0; i < BS_NR_OF_BAT_CELLS; i++) {
        ltc_balancing_feedback.value[i] = 0;
        ltc_balancing_control.balancing_state[i] = 0;
    }

    ltc_slave_control.state = 0;
    ltc_slave_control.timestamp = 0;
    ltc_slave_control.previous_timestamp = 0;
    for (i=0; i < BS_NR_OF_MODULES; i++) {
        ltc_slave_control.io_value_in[i] = 0;
        ltc_slave_control.io_value_out[i] = 0;
        ltc_slave_control.external_sensor_temperature[i] = 0;
        ltc_slave_control.eeprom_value_read[i] = 0;
        ltc_slave_control.eeprom_value_write[i] = 0;
    }
    ltc_slave_control.eeprom_read_address_last_used = 0xFFFFFFFF;
    ltc_slave_control.eeprom_read_address_to_use = 0xFFFFFFFF;
    ltc_slave_control.eeprom_write_address_last_used = 0xFFFFFFFF;
    ltc_slave_control.eeprom_write_address_to_use = 0xFFFFFFFF;

    ltc_allgpiovoltage.timestamp = 0;
    ltc_allgpiovoltage.previous_timestamp = 0;
    ltc_allgpiovoltage.state = 0;
    for (i=0; i < BS_NR_OF_MODULES * BS_NR_OF_GPIOS_PER_MODULE; i++) {
        ltc_allgpiovoltage.gpiovoltage[i] = 0;
    }

    for (i = 0; i < BS_NR_OF_MODULES * (BS_NR_OF_BAT_CELLS_PER_MODULE+1); i++) {
        ltc_openwire.openwire[i] = 0;
    }
    ltc_openwire.state = 0;

    DB_WriteBlock(&ltc_cellvoltage, DATA_BLOCK_ID_CELLVOLTAGE);
    DB_WriteBlock(&ltc_celltemperature, DATA_BLOCK_ID_CELLTEMPERATURE);
    DB_WriteBlock(&ltc_minmax, DATA_BLOCK_ID_MINMAX);
    DB_WriteBlock(&ltc_balancing_feedback, DATA_BLOCK_ID_BALANCING_FEEDBACK_VALUES);
    DB_WriteBlock(&ltc_balancing_control, DATA_BLOCK_ID_BALANCING_CONTROL_VALUES);
    DB_WriteBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);
    DB_WriteBlock(&ltc_openwire, DATA_BLOCK_ID_OPEN_WIRE);

    for (i=0; i < (8*2*BS_NR_OF_MODULES); i++) {
        ltc_user_mux.value[i] = 0;
    }
    ltc_user_mux.previous_timestamp = 0;
    ltc_user_mux.timestamp = 0;
    ltc_user_mux.state = 0;
}

/**
 * @brief   function for setting LTC_Trigger state transitions
 *
 * @param  state:    state to transition into
 * @param  substate: substate to transition into
 * @param  timer_ms: transition into state, substate after timer elapsed
 */
static void LTC_StateTransition(LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms) {
        ltc_state.state = state;
        ltc_state.substate = substate;
        ltc_state.timer = timer_ms;
}

/**
 * @brief   condition-based state transition depending on retVal
 *
 * If retVal is E_OK, after timer_ms_ok is elapsed the LTC statemachine will
 * transition into state_ok and substate_ok, otherwise after timer_ms_nok the
 * statemachine will transition to state_nok and substate_nok. Depending on
 * value of retVal the corresponding diagnosis entry will be called.
 *
 * @param  retVal:       condition to determine if statemachine will transition into ok or nok states
 * @param  diagCode:     symbolic IDs for diagnosis entry, called with DIAG_EVENT_OK if retVal is E_OK, DIAG_EVENT_NOK otherwise
 * @param  state_ok      state to transition into if retVal is E_OK
 * @param  substate_ok:  substate to transition into if retVal is E_OK
 * @param  timer_ms_ok:  transition into state_ok, substate_ok after timer_ms_ok elapsed
 * @param  state_nok:    state to transition into if retVal is E_NOT_OK
 * @param  substate_nok: substate to transition into if retVal is E_NOT_OK
 * @param  timer_ms_nok: transition into state_nok, substate_nok after timer_ms_nok elapsed
 */
static void LTC_CondBasedStateTransition(STD_RETURN_TYPE_e retVal, DIAG_CH_ID_e diagCode, uint8_t state_ok, uint8_t substate_ok, uint16_t timer_ms_ok, uint8_t state_nok, uint8_t substate_nok, uint16_t timer_ms_nok) {
    if ((retVal != E_OK)) {
        DIAG_Handler(diagCode, DIAG_EVENT_NOK, 0);
        LTC_StateTransition(state_nok, substate_nok, timer_ms_nok);
    } else {
        DIAG_Handler(diagCode, DIAG_EVENT_OK, 0);
        LTC_StateTransition(state_ok, substate_ok, timer_ms_ok);
    }
}

/**
 * @brief   stores the measured voltages in the database.
 *
 * This function loops through the data of all modules in the LTC daisy-chain that are
 * stored in the LTC_CellVoltages buffer and writes them in the database.
 * At each write iteration, the variable named "state" and related to voltages in the
 * database is incremented.
 *
 */
extern void LTC_SaveVoltages(void) {
    uint16_t i = 0;
    uint16_t j = 0;
    uint16_t min = UINT16_MAX;
    uint16_t max = 0;
    uint32_t mean = 0;
    uint32_t sum = 0;
    uint8_t module_number_min = 0;
    uint8_t module_number_max = 0;
    uint8_t cell_number_min = 0;
    uint8_t cell_number_max = 0;
    uint16_t nrValidCellVoltages = 0;
    STD_RETURN_TYPE_e retval_PLminmax = E_NOT_OK;
    STD_RETURN_TYPE_e retval_PLspread = E_NOT_OK;
    STD_RETURN_TYPE_e result = E_NOT_OK;

    /* Perform min/max voltage plausibility check */
    retval_PLminmax = PL_CheckVoltageMinMax(&ltc_cellvoltage);

    /* Use only valid cell voltages for calculating mean voltage */
    for (i=0; i < BS_NR_OF_MODULES; i++) {
        for (j=0; j < BS_NR_OF_BAT_CELLS_PER_MODULE; j++) {
            if ((ltc_cellvoltage.valid_volt[i] & (0x01 << j)) == 0) {
                /* Cell voltage is valid -> use this voltage for subsequent calculations */
                nrValidCellVoltages++;
                sum += ltc_cellvoltage.voltage[i*(BS_NR_OF_BAT_CELLS_PER_MODULE)+j];
                if (ltc_cellvoltage.voltage[i*(BS_NR_OF_BAT_CELLS_PER_MODULE)+j] < min) {
                    min = ltc_cellvoltage.voltage[i*(BS_NR_OF_BAT_CELLS_PER_MODULE)+j];
                    module_number_min = i;
                    cell_number_min = j;
                }
                if (ltc_cellvoltage.voltage[i*(BS_NR_OF_BAT_CELLS_PER_MODULE)+j] > max) {
                    max = ltc_cellvoltage.voltage[i*(BS_NR_OF_BAT_CELLS_PER_MODULE)+j];
                    module_number_max = i;
                    cell_number_max = j;
                }
            }
        }
    }

    ltc_cellvoltage.packVoltage_mV = sum;

    /* Prevent division by 0, if all cell voltages are invalid */
    if (nrValidCellVoltages > 0) {
        mean = sum/nrValidCellVoltages;
    }

    /* Perform voltage spread plausibility check */
    retval_PLspread = PL_CheckVoltageSpread(&ltc_cellvoltage, mean);

    /* Set flag if plausibility error detected */
    if ((retval_PLminmax == E_OK) && (retval_PLspread == E_OK)) {
        result = E_OK;
    }
    DIAG_checkEvent(result, DIAG_CH_PLAUSIBILITY_CELL_VOLTAGE, 0);

    ltc_cellvoltage.state++;
    ltc_minmax.state++;
    ltc_minmax.voltage_mean = mean;
    ltc_minmax.previous_voltage_min = ltc_minmax.voltage_min;
    ltc_minmax.voltage_min = min;
    ltc_minmax.voltage_module_number_min = module_number_min;
    ltc_minmax.voltage_cell_number_min = cell_number_min;
    ltc_minmax.previous_voltage_max = ltc_minmax.voltage_max;
    ltc_minmax.voltage_max = max;
    ltc_minmax.voltage_module_number_max = module_number_max;
    ltc_minmax.voltage_cell_number_max = cell_number_max;

    DB_WriteBlock(&ltc_cellvoltage, DATA_BLOCK_ID_CELLVOLTAGE);
    DB_WriteBlock(&ltc_minmax, DATA_BLOCK_ID_MINMAX);
}

/**
 * @brief   stores the measured temperatures and the measured multiplexer feedbacks in the database.
 *
 * This function loops through the temperature and multiplexer feedback data of all modules
 * in the LTC daisy-chain that are stored in the LTC_MultiplexerVoltages buffer and writes
 * them in the database.
 * At each write iteration, the variables named "state" and related to temperatures and multiplexer feedbacks
 * in the database are incremented.
 *
 */
extern void LTC_SaveTemperatures(void) {
    uint16_t i = 0;
    uint16_t j = 0;
    int16_t min = INT16_MAX;
    int16_t max = INT16_MIN;
    int32_t sum = 0;
    float mean = 0;
    uint8_t module_number_min = 0;
    uint8_t module_number_max = 0;
    uint8_t sensor_number_min = 0;
    uint8_t sensor_number_max = 0;
    STD_RETURN_TYPE_e retval_PL  = E_NOT_OK;
    uint16_t nrValidTemperatures = 0;

    /* Perform plausibility check */
    retval_PL = PL_CheckTempMinMax(&ltc_celltemperature);
    /* Set flag if plausibility error detected */
    DIAG_checkEvent(retval_PL, DIAG_CH_PLAUSIBILITY_CELL_TEMP, 0);

    for (i=0; i < BS_NR_OF_MODULES; i++) {
        for (j=0; j < BS_NR_OF_TEMP_SENSORS_PER_MODULE; j++) {
            if ((ltc_celltemperature.valid_temperature[i] & (0x01 << j)) == 0) {
                /* Cell voltage is valid -> use this voltage for subsequent calculations */
                nrValidTemperatures++;
                sum += ltc_celltemperature.temperature[i*(BS_NR_OF_TEMP_SENSORS_PER_MODULE)+j];
                if (ltc_celltemperature.temperature[i*(BS_NR_OF_TEMP_SENSORS_PER_MODULE)+j] < min) {
                    min = ltc_celltemperature.temperature[i*(BS_NR_OF_TEMP_SENSORS_PER_MODULE)+j];
                    module_number_min = i;
                    sensor_number_min = j;
                }
                if (ltc_celltemperature.temperature[i*(BS_NR_OF_TEMP_SENSORS_PER_MODULE)+j] > max) {
                    max = ltc_celltemperature.temperature[i*(BS_NR_OF_TEMP_SENSORS_PER_MODULE)+j];
                    module_number_max = i;
                    sensor_number_max = j;
                }
            }
        }
    }

    /* Prevent division by 0, if all temperatues are invalid */
    if (nrValidTemperatures > 0) {
        mean = sum/nrValidTemperatures;
    }

    ltc_celltemperature.state++;
    ltc_minmax.state++;
    ltc_minmax.temperature_mean = mean;
    ltc_minmax.temperature_min = min;
    ltc_minmax.temperature_module_number_min = module_number_min;
    ltc_minmax.temperature_sensor_number_min = sensor_number_min;
    ltc_minmax.temperature_max = max;
    ltc_minmax.temperature_module_number_max = module_number_max;
    ltc_minmax.temperature_sensor_number_max = sensor_number_max;
    DB_WriteBlock(&ltc_celltemperature, DATA_BLOCK_ID_CELLTEMPERATURE);
    DB_WriteBlock(&ltc_minmax, DATA_BLOCK_ID_MINMAX);
}

/**
 * @brief   stores the measured GPIOs in the database.
 *
 * This function loops through the data of all modules in the LTC daisy-chain that are
 * stored in the ltc_allgpiovoltage buffer and writes them in the database.
 * At each write iteration, the variable named "state" and related to voltages in the
 * database is incremented.
 *
 */
extern void LTC_SaveAllGPIOMeasurement(void) {
    ltc_allgpiovoltage.state++;
    DB_WriteBlock(&ltc_allgpiovoltage, DATA_BLOCK_ID_ALLGPIOVOLTAGE);
}

/**
 * @brief   stores the measured balancing feedback values in the database.
 *
 * This function stores the global balancing feedback value measured on GPIO3 of the LTC into the database
 *
 */
static void LTC_SaveBalancingFeedback(uint8_t *DataBufferSPI_RX) {
    uint16_t i = 0;
    uint16_t val_i = 0;

    for (i=0; i < LTC_N_LTC; i++) {
        val_i = DataBufferSPI_RX[8+1*i*8] | (DataBufferSPI_RX[8+1*i*8+1] << 8);    /* raw value, GPIO3 */

            ltc_balancing_feedback.value[i] = val_i;
    }

    ltc_balancing_feedback.state++;
    DB_WriteBlock(&ltc_balancing_feedback, DATA_BLOCK_ID_BALANCING_FEEDBACK_VALUES);
}


/**
 * @brief   gets the balancing orders from the database.
 *
 * This function gets the balancing control from the database. Balancing control
 * is set by the BMS. The LTC driver only executes the balancing orders.
 */
static void LTC_Get_BalancingControlValues(void) {
    DB_ReadBlock(&ltc_balancing_control, DATA_BLOCK_ID_BALANCING_CONTROL_VALUES);
}



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

    OS_TaskEnter_Critical();
    if (!ltc_state.triggerentry) {
        ltc_state.triggerentry++;
    } else {
        retval = 0xFF;    /* multiple calls of function */
    }
    OS_TaskExit_Critical();

    return (retval);
}

/**
 * @brief   gets the current state request.
 *
 * This function is used in the functioning of the LTC state machine.
 *
 * @return  retval  current state request, taken from LTC_STATE_REQUEST_e
 */
extern LTC_STATE_REQUEST_e LTC_GetStateRequest(void) {
    LTC_STATE_REQUEST_e retval = LTC_STATE_NO_REQUEST;

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

    return (retval);
}

/**
 * @brief   gets the current state.
 *
 * This function is used in the functioning of the LTC state machine.
 *
 * @return  current state, taken from LTC_STATEMACH_e
 */
extern LTC_STATEMACH_e LTC_GetState(void) {
    return (ltc_state.state);
}

/**
 * @brief   transfers the current state request to the state machine.
 *
 * This function takes the current state request from ltc_state and transfers it to the state machine.
 * It resets the value from ltc_state to LTC_STATE_NO_REQUEST
 *
 * @param   *busIDptr       bus ID, main or backup (deprecated)
 * @param   *adcModeptr     LTC ADCmeasurement mode (fast, normal or filtered)
 * @param   *adcMeasChptr   number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
 *
 * @return  retVal          current state request, taken from LTC_STATE_REQUEST_e
 *
 */
LTC_STATE_REQUEST_e LTC_TransferStateRequest(uint8_t *busIDptr, LTC_ADCMODE_e *adcModeptr, LTC_ADCMEAS_CHAN_e *adcMeasChptr) {
    LTC_STATE_REQUEST_e retval = LTC_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retval    = ltc_state.statereq;
    *adcModeptr = ltc_state.adcModereq;
    *adcMeasChptr = ltc_state.adcMeasChreq;
    ltc_state.statereq = LTC_STATE_NO_REQUEST;
    OS_TaskExit_Critical();

    return (retval);
}

LTC_RETURN_TYPE_e LTC_SetStateRequest(LTC_STATE_REQUEST_e statereq) {
    LTC_RETURN_TYPE_e retVal = LTC_STATE_NO_REQUEST;

    OS_TaskEnter_Critical();
    retVal = LTC_CheckStateRequest(statereq);

    if (retVal == LTC_OK || retVal == LTC_BUSY_OK || retVal == LTC_OK_FROM_ERROR) {
            ltc_state.statereq   = statereq;
        }
    OS_TaskExit_Critical();

    return (retVal);
}

void LTC_Trigger(void) {
    STD_RETURN_TYPE_e retVal = E_OK;
    LTC_STATE_REQUEST_e statereq = LTC_STATE_NO_REQUEST;
    uint8_t tmpbusID = 0;
    LTC_ADCMODE_e tmpadcMode = LTC_ADCMODE_UNDEFINED;
    LTC_ADCMEAS_CHAN_e tmpadcMeasCh = LTC_ADCMEAS_UNDEFINED;

    /* Check re-entrance of function */
    if (LTC_CheckReEntrance())
        return;

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

    if (ltc_state.check_spi_flag == FALSE) {
        if (ltc_state.timer) {
            if (--ltc_state.timer) {
                ltc_state.triggerentry--;
                return;    /* handle state machine only if timer has elapsed */
            }
        }
    } else {
        if (SPI_IsTransmitOngoing() == TRUE) {
            if (ltc_state.timer) {
                if (--ltc_state.timer) {
                    ltc_state.triggerentry--;
                    return;    /* handle state machine only if timer has elapsed */
                }
            }
        }
    }

    switch (ltc_state.state) {
        /****************************UNINITIALIZED***********************************/
        case LTC_STATEMACH_UNINITIALIZED:
            /* waiting for Initialization Request */
            statereq = LTC_TransferStateRequest(&tmpbusID, &tmpadcMode, &tmpadcMeasCh);
            if (statereq == LTC_STATE_INIT_REQUEST) {
                LTC_SAVELASTSTATES();
                LTC_StateTransition(LTC_STATEMACH_INITIALIZATION, LTC_ENTRY_UNINITIALIZED, LTC_STATEMACH_SHORTTIME);
                ltc_state.adcMode = tmpadcMode;
                ltc_state.adcMeasCh = tmpadcMeasCh;
            } else if (statereq == LTC_STATE_NO_REQUEST) {
                /* no actual request pending */
            } else {
                ltc_state.ErrRequestCounter++;   /* illegal request pending */
            }
            break;

        /****************************INITIALIZATION**********************************/
        case LTC_STATEMACH_INITIALIZATION:

            LTC_SetTransferTimes();
            ltc_state.muxmeas_seqptr = ltc_mux_seq.seqptr;
            ltc_state.muxmeas_nr_end = ltc_mux_seq.nr_of_steps;
            ltc_state.muxmeas_seqendptr = ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr)+ltc_mux_seq.nr_of_steps;  /* last sequence + 1 */

            if (ltc_state.substate == LTC_ENTRY_INITIALIZATION) {
                LTC_SAVELASTSTATES();
                retVal = LTC_SendWakeUp();        /* Send dummy byte to wake up the daisy chain */
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_INITIALIZATION, LTC_RE_ENTRY_INITIALIZATION, LTC_STATEMACH_DAISY_CHAIN_FIRST_INITIALIZATION_TIME,
                                          LTC_STATEMACH_INITIALIZATION, LTC_ENTRY_INITIALIZATION, LTC_STATEMACH_SHORTTIME);

            } else if (ltc_state.substate == LTC_RE_ENTRY_INITIALIZATION) {
                LTC_SAVELASTSTATES();
                retVal = LTC_SendWakeUp();  /* Send dummy byte again to wake up the daisy chain */
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_INITIALIZATION, LTC_START_INIT_INITIALIZATION, LTC_STATEMACH_DAISY_CHAIN_SECOND_INITIALIZATION_TIME,
                                          LTC_STATEMACH_INITIALIZATION, LTC_RE_ENTRY_INITIALIZATION, LTC_STATEMACH_SHORTTIME);

            } else if (ltc_state.substate == LTC_START_INIT_INITIALIZATION) {
                retVal = LTC_Init();  /* Initialize main LTC loop */
                ltc_state.lastsubstate = ltc_state.substate;
                DIAG_checkEvent(retVal, DIAG_CH_LTC_SPI, 0);
                LTC_StateTransition(LTC_STATEMACH_INITIALIZATION, LTC_EXIT_INITIALIZATION, ltc_state.commandDataTransferTime);

            } else if (ltc_state.substate == LTC_EXIT_INITIALIZATION) {
            /* in daisy-chain mode, there is no confirmation of the initialization */
                LTC_SAVELASTSTATES();
                LTC_Initialize_Database();
                LTC_ResetErrorTable();
                LTC_StateTransition(LTC_STATEMACH_INITIALIZED, LTC_ENTRY_INITIALIZATION, LTC_STATEMACH_SHORTTIME);
            }
            break;

        /****************************INITIALIZED*************************************/
        case LTC_STATEMACH_INITIALIZED:
            LTC_SAVELASTSTATES();
            LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
            break;

        /****************************START MEASUREMENT*******************************/
        case LTC_STATEMACH_STARTMEAS:

            ltc_state.adcMode = LTC_VOLTAGE_MEASUREMENT_MODE;
            ltc_state.adcMeasCh = LTC_ADCMEAS_ALLCHANNEL;

            ltc_state.check_spi_flag = FALSE;
            retVal = LTC_StartVoltageMeasurement(ltc_state.adcMode, ltc_state.adcMeasCh);
            LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                      LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_A_RDCVA_READVOLTAGE, (ltc_state.commandTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, ltc_state.adcMeasCh)),
                                      LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_A_RDCVA_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
            break;

        /****************************READ VOLTAGE************************************/
        case LTC_STATEMACH_READVOLTAGE:

            if (ltc_state.substate == LTC_READ_VOLTAGE_REGISTER_A_RDCVA_READVOLTAGE) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDCVA), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                        LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoVoltagebuffer(0, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDCVB), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                        LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoVoltagebuffer(1, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDCVC), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoVoltagebuffer(2, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDCVD), ltc_RXPECbuffer);
                if (BS_MAX_SUPPORTED_CELLS > 12) {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                            LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                            LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                } else {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                            LTC_STATEMACH_READVOLTAGE, LTC_EXIT_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                            LTC_STATEMACH_READVOLTAGE, LTC_EXIT_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                }
                break;

            } else if (ltc_state.substate == LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoVoltagebuffer(3, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDCVE), ltc_RXPECbuffer);
                 if (BS_MAX_SUPPORTED_CELLS > 15) {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                            LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                            LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                } else {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                            LTC_STATEMACH_READVOLTAGE, LTC_EXIT_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                            LTC_STATEMACH_READVOLTAGE, LTC_EXIT_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                }
                break;

            } else if (ltc_state.substate == LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoVoltagebuffer(4, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDCVF), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_READVOLTAGE, LTC_EXIT_READVOLTAGE, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                        LTC_STATEMACH_READVOLTAGE, LTC_EXIT_READVOLTAGE, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EXIT_READVOLTAGE) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                if (BS_MAX_SUPPORTED_CELLS == 12) {
                    LTC_SaveRXtoVoltagebuffer(3, ltc_RXPECbuffer);
                } else if (BS_MAX_SUPPORTED_CELLS == 15) {
                    LTC_SaveRXtoVoltagebuffer(4, ltc_RXPECbuffer);
                } else if (BS_MAX_SUPPORTED_CELLS == 18) {
                    LTC_SaveRXtoVoltagebuffer(5, ltc_RXPECbuffer);
                }

                /* Switch to different state if read voltage state is reused
                 * e.g. open-wire check...                                */
                if (ltc_state.reusageMeasurementMode == LTC_NOT_REUSED) {
                    LTC_SaveVoltages();
                    LTC_StateTransition(LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_MUXCONFIGURATION_INIT, LTC_STATEMACH_SHORTTIME);
                } else if (ltc_state.reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PUP) {
                    LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK, LTC_STATEMACH_SHORTTIME);
                } else if (ltc_state.reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PDOWN) {
                    LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_READ_VOLTAGES_PULLDOWN_OPENWIRE_CHECK, LTC_STATEMACH_SHORTTIME);
                }
                ltc_state.check_spi_flag = FALSE;
            }
            break;


        /****************************MULTIPLEXED MEASUREMENT CONFIGURATION***********/
        case LTC_STATEMACH_MUXMEASUREMENT:

            if (ltc_state.substate == LTC_STATEMACH_MUXCONFIGURATION_INIT) {
                ltc_state.adcMode = LTC_GPIO_MEASUREMENT_MODE;
                ltc_state.adcMeasCh = LTC_ADCMEAS_SINGLECHANNEL_GPIO1;

                if (ltc_state.muxmeas_seqptr >= ltc_state.muxmeas_seqendptr) {
                    /* last step of sequence reached (or no sequence configured) */

                    ltc_state.muxmeas_seqptr = ltc_mux_seq.seqptr;
                    ltc_state.muxmeas_nr_end = ltc_mux_seq.nr_of_steps;
                    ltc_state.muxmeas_seqendptr = ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr)+ltc_mux_seq.nr_of_steps;  /* last sequence + 1 */

                    LTC_SaveTemperatures();

                    if (LTC_IsFirstMeasurementCycleFinished() == FALSE) {
                        LTC_SetFirstMeasurementCycleFinished();
                    }
                }

                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SetMuxChannel(ltc_TXBuffer, ltc_TXPECbuffer,
                                            ltc_state.muxmeas_seqptr->muxID,  /* mux */
                                            ltc_state.muxmeas_seqptr->muxCh  /* channel */);
                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_MUXCONFIGURATION_INIT, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_MUXMEASUREMENT, LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT));
                }
                break;

            } else if (ltc_state.substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                if (LTC_GOTO_MUX_CHECK == TRUE) {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                              LTC_STATEMACH_MUXMEASUREMENT, LTC_READ_I2C_TRANSMISSION_RESULT_RDCOMM_MUXMEASUREMENT_CONFIG, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                              LTC_STATEMACH_MUXMEASUREMENT, LTC_READ_I2C_TRANSMISSION_RESULT_RDCOMM_MUXMEASUREMENT_CONFIG, LTC_STATEMACH_SHORTTIME);;
                } else {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                              LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_MUXMEASUREMENT, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                              LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_SHORTTIME);
                }
                break;

            } else if (ltc_state.substate == LTC_READ_I2C_TRANSMISSION_RESULT_RDCOMM_MUXMEASUREMENT_CONFIG) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)ltc_cmdRDCOMM, ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_MUXMEASUREMENT, LTC_READ_I2C_TRANSMISSION_CHECK_MUXMEASUREMENT_CONFIG, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                                          LTC_STATEMACH_MUXMEASUREMENT, LTC_READ_I2C_TRANSMISSION_CHECK_MUXMEASUREMENT_CONFIG, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_I2C_TRANSMISSION_CHECK_MUXMEASUREMENT_CONFIG) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);

                /* if CRC OK: check multiplexer answer on i2C bus */
                retVal = LTC_I2CCheckACK(ltc_RXPECbuffer, ltc_state.muxmeas_seqptr->muxID);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_MUX, 0);
                LTC_StateTransition(LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_STATEMACH_MUXMEASUREMENT) {
                if (ltc_state.muxmeas_seqptr->muxCh == 0xFF) {
                    /* actual multiplexer is switched off, so do not make a measurement and follow up with next step (mux configuration) */
                    ++ltc_state.muxmeas_seqptr;         /*  go further with next step of sequence
                                                            ltc_state.numberOfMeasuredMux not decremented, this does not count as a measurement */
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    if (LTC_GOTO_MUX_CHECK == FALSE) {
                        if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                            DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                        } else {
                            DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                        }
                    }

                    ltc_state.check_spi_flag = FALSE;
                    /* user multiplexer type -> connected to GPIO2! */
                    if (ltc_state.muxmeas_seqptr->muxID == 1 || ltc_state.muxmeas_seqptr->muxID == 2) {
                        retVal = LTC_StartGPIOMeasurement(ltc_state.adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2);
                    } else {
                        retVal = LTC_StartGPIOMeasurement(ltc_state.adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO1);
                    }
                }
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_READMUXMEASUREMENT, (ltc_state.commandTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2)),  /*  wait, ADAX-Command */
                                          LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_READMUXMEASUREMENT, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_STATEMACH_READMUXMEASUREMENT) {
                ltc_state.check_spi_flag = TRUE;

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDAUXA), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_STOREMUXMEASUREMENT, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                                          LTC_STATEMACH_MUXMEASUREMENT, LTC_STATEMACH_STOREMUXMEASUREMENT, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_STATEMACH_STOREMUXMEASUREMENT) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveMuxMeasurement(ltc_RXPECbuffer, ltc_state.muxmeas_seqptr);

                ++ltc_state.muxmeas_seqptr;

                if (ltc_state.balance_control_done == TRUE) {
                    statereq = LTC_TransferStateRequest(&tmpbusID, &tmpadcMode, &tmpadcMeasCh);
                    if (statereq == LTC_STATE_USER_IO_WRITE_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_CONTROL, LTC_USER_IO_SET_OUTPUT_REGISTER, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_USER_IO_READ_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_READ_INPUT_REGISTER, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_USER_IO_WRITE_REQUEST_TI) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_CONTROL_TI, LTC_USER_IO_SET_DIRECTION_REGISTER_TI, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_USER_IO_READ_REQUEST_TI) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_SET_DIRECTION_REGISTER_TI, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_EEPROM_READ_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_READ_DATA1, LTC_STATEMACH_SHORTTIME);
                    } else if (statereq == LTC_STATE_EEPROM_WRITE_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_WRITE_DATA1, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_TEMP_SENS_READ_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_SEND_DATA1, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATEMACH_BALANCEFEEDBACK_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_BALANCEFEEDBACK, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    }   else if (statereq == LTC_STATE_OPENWIRE_CHECK_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_REQUEST_PULLUP_CURRENT_OPENWIRE_CHECK, LTC_STATEMACH_SHORTTIME);
                        /* Send ADOW command with PUP two times */
                        ltc_state.resendCommandCounter = LTC_NMBR_REQ_ADOW_COMMANDS;
                        ltc_state.balance_control_done = FALSE;
                    } else {
                        LTC_StateTransition(LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = TRUE;
                    }
                } else {
                    LTC_StateTransition(LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);
                    ltc_state.balance_control_done = TRUE;
                }

                break;
            }

            break;

        /****************************BALANCE CONTROL*********************************/
        case LTC_STATEMACH_BALANCECONTROL:

            if (ltc_state.substate == LTC_CONFIG_BALANCECONTROL) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_BalanceControl(0);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG2_BALANCECONTROL, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                        LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG2_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_CONFIG2_BALANCECONTROL) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                if (BS_NR_OF_BAT_CELLS_PER_MODULE > 12) {
                    SPI_SetTransmitOngoing();
                    retVal = LTC_BalanceControl(1);
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                            LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG2_BALANCECONTROL_END, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                            LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG2_BALANCECONTROL_END, LTC_STATEMACH_SHORTTIME);
                } else {
                    /* 12 cells, balancing control finished */
                    ltc_state.check_spi_flag = FALSE;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                }

                break;

            } else if (ltc_state.substate == LTC_CONFIG2_BALANCECONTROL_END) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }
                /* More than 12 cells, balancing control finished */
                ltc_state.check_spi_flag = FALSE;
                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);

                break;
            }
            break;

            /****************************START MEASUREMENT*******************************/
            case LTC_STATEMACH_ALLGPIOMEASUREMENT:

                ltc_state.adcMode = LTC_GPIO_MEASUREMENT_MODE;
                ltc_state.adcMeasCh = LTC_ADCMEAS_ALLCHANNEL;

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_StartGPIOMeasurement(ltc_state.adcMode, ltc_state.adcMeasCh);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_A_RDAUXA, (ltc_state.commandTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, ltc_state.adcMeasCh)),
                                          LTC_STATEMACH_ALLGPIOMEASUREMENT, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);      /* TODO: @koffel here same state is kept if error occurs */
                break;

        /****************************READ ALL GPIO VOLTAGE************************************/
        case LTC_STATEMACH_READALLGPIO:

            if (ltc_state.substate == LTC_READ_AUXILIARY_REGISTER_A_RDAUXA) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDAUXA), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_B_RDAUXB, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_B_RDAUXB, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_AUXILIARY_REGISTER_B_RDAUXB) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoGPIOBuffer(0, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDAUXB), ltc_RXPECbuffer);

                if (BS_MAX_SUPPORTED_CELLS > 12) {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                              LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_C_RDAUXC, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                              LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_C_RDAUXC, LTC_STATEMACH_SHORTTIME);
                } else {
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                              LTC_STATEMACH_READALLGPIO, LTC_EXIT_READAUXILIARY_ALLGPIOS, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                              LTC_STATEMACH_READALLGPIO, LTC_EXIT_READAUXILIARY_ALLGPIOS, LTC_STATEMACH_SHORTTIME);
                }
                break;

            } else if (ltc_state.substate == LTC_READ_AUXILIARY_REGISTER_C_RDAUXC) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoGPIOBuffer(1, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDAUXC), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_D_RDAUXD, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_READALLGPIO, LTC_READ_AUXILIARY_REGISTER_D_RDAUXD, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_AUXILIARY_REGISTER_D_RDAUXD) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);
                LTC_SaveRXtoGPIOBuffer(2, ltc_RXPECbuffer);

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)(ltc_cmdRDAUXD), ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_READALLGPIO, LTC_EXIT_READAUXILIARY_ALLGPIOS, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_READALLGPIO, LTC_EXIT_READAUXILIARY_ALLGPIOS, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EXIT_READAUXILIARY_ALLGPIOS) {
                retVal = LTC_RX_PECCheck(ltc_RXPECbuffer);
                DIAG_checkEvent(retVal, DIAG_CH_LTC_PEC, 0);

                if (BS_MAX_SUPPORTED_CELLS == 12) {
                    LTC_SaveRXtoGPIOBuffer(1, ltc_RXPECbuffer);
                } else if (BS_MAX_SUPPORTED_CELLS > 12) {
                    LTC_SaveRXtoGPIOBuffer(3, ltc_RXPECbuffer);
                }

                LTC_SaveAllGPIOMeasurement();

                if (ltc_state.balance_control_done == TRUE) {
                    statereq = LTC_TransferStateRequest(&tmpbusID, &tmpadcMode, &tmpadcMeasCh);
                    if (statereq == LTC_STATE_USER_IO_WRITE_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_CONTROL, LTC_USER_IO_SET_OUTPUT_REGISTER, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_USER_IO_READ_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_READ_INPUT_REGISTER, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_USER_IO_WRITE_REQUEST_TI) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_CONTROL_TI, LTC_USER_IO_SET_DIRECTION_REGISTER_TI, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_USER_IO_READ_REQUEST_TI) {
                        LTC_StateTransition(LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_SET_DIRECTION_REGISTER_TI, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_EEPROM_READ_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_READ_DATA1, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_EEPROM_WRITE_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_WRITE_DATA1, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATE_TEMP_SENS_READ_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_SEND_DATA1, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    } else if (statereq == LTC_STATEMACH_BALANCEFEEDBACK_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_BALANCEFEEDBACK, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = FALSE;
                    }  else if (statereq == LTC_STATE_OPENWIRE_CHECK_REQUEST) {
                        LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_REQUEST_PULLUP_CURRENT_OPENWIRE_CHECK, LTC_STATEMACH_SHORTTIME);
                        /* Send ADOW command with PUP two times */
                        ltc_state.resendCommandCounter = LTC_NMBR_REQ_ADOW_COMMANDS;
                        ltc_state.balance_control_done = FALSE;
                    } else {
                        LTC_StateTransition(LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);
                        ltc_state.balance_control_done = TRUE;
                    }
                } else {
                    LTC_StateTransition(LTC_STATEMACH_BALANCECONTROL, LTC_CONFIG_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);
                    ltc_state.balance_control_done = TRUE;
                }
            }

            break;


        /****************************BALANCE FEEDBACK*********************************/
        case LTC_STATEMACH_BALANCEFEEDBACK:

             if (ltc_state.substate == LTC_ENTRY) {
                ltc_state.adcMode = LTC_ADCMODE_NORMAL_DCP0;
                ltc_state.adcMeasCh = LTC_ADCMEAS_SINGLECHANNEL_GPIO3;

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_StartGPIOMeasurement(ltc_state.adcMode, ltc_state.adcMeasCh);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_BALANCEFEEDBACK, LTC_READ_FEEDBACK_BALANCECONTROL, (ltc_state.commandDataTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, ltc_state.adcMeasCh)),
                                          LTC_STATEMACH_BALANCEFEEDBACK, LTC_READ_FEEDBACK_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_READ_FEEDBACK_BALANCECONTROL) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)ltc_cmdRDAUXA, ltc_RXPECbuffer);  /* read AUXA register */
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_BALANCEFEEDBACK, LTC_SAVE_FEEDBACK_BALANCECONTROL, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                        LTC_STATEMACH_BALANCEFEEDBACK, LTC_SAVE_FEEDBACK_BALANCECONTROL, LTC_STATEMACH_SHORTTIME);

            } else if (ltc_state.substate == LTC_SAVE_FEEDBACK_BALANCECONTROL) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                if (LTC_RX_PECCheck(ltc_RXPECbuffer) != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_OK, 0);
                    LTC_SaveBalancingFeedback(ltc_RXPECbuffer);
                }
                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }
            break;

        /****************************BOARD TEMPERATURE SENSOR*********************************/
        case LTC_STATEMACH_TEMP_SENS_READ:

            if (ltc_state.substate == LTC_TEMP_SENS_SEND_DATA1) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_Send_I2C_Command(ltc_TXBuffer, ltc_TXPECbuffer, (uint8_t*)ltc_I2CcmdTempSens0);

                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_SEND_CLOCK_STCOMM1, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT);
                }

                break;

            } else if (ltc_state.substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM1) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_READ_DATA1, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_READ_DATA1, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_TEMP_SENS_READ_DATA1) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_Send_I2C_Command(ltc_TXBuffer, ltc_TXPECbuffer, (uint8_t*)ltc_I2CcmdTempSens1);

                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_SEND_CLOCK_STCOMM2, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT));
                }

                break;

            } else if (ltc_state.substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM2) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_READ_I2C_TRANSMISSION_RESULT_RDCOMM, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_READ_I2C_TRANSMISSION_RESULT_RDCOMM, LTC_STATEMACH_SHORTTIME);
                break;
            }  else if (ltc_state.substate == LTC_TEMP_SENS_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)ltc_cmdRDCOMM, ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_SAVE_TEMP, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                                          LTC_STATEMACH_TEMP_SENS_READ, LTC_TEMP_SENS_SAVE_TEMP, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_TEMP_SENS_SAVE_TEMP) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                if (LTC_RX_PECCheck(ltc_RXPECbuffer) != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_OK, 0);
                    LTC_TempSensSaveTemp(ltc_RXPECbuffer);
                }

                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }
            break;

        /****************************WRITE TO PORT EXPANDER IO***********/
        case LTC_STATEMACH_USER_IO_CONTROL:

            if (ltc_state.substate == LTC_USER_IO_SET_OUTPUT_REGISTER) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SetPortExpander(ltc_TXBuffer, ltc_TXPECbuffer);

                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_USER_IO_CONTROL, LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT));
                }
                break;

            } else if (ltc_state.substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_STARTMEAS, LTC_ENTRY, ltc_state.gpioClocksTransferTime,
                                          LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }
            break;

        /****************************READ FROM PORT EXPANDER IO***********/
        case LTC_STATEMACH_USER_IO_FEEDBACK:

            if (ltc_state.substate == LTC_USER_IO_READ_INPUT_REGISTER) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_Send_I2C_Command(ltc_TXBuffer, ltc_TXPECbuffer, (uint8_t*)ltc_I2CcmdPortExpander1);

                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_SEND_CLOCK_STCOMM, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT);
                }

                break;

            } else if (ltc_state.substate == LTC_USER_IO_SEND_CLOCK_STCOMM) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM, ltc_state.gpioClocksTransferTime,
                                          LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM, LTC_STATEMACH_SHORTTIME);
                break;

                } else if (ltc_state.substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
                    if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                        DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                        LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                        break;
                    } else {
                        DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    }

                    SPI_SetTransmitOngoing();
                    retVal = LTC_RX((uint8_t*)ltc_cmdRDCOMM, ltc_RXPECbuffer);
                    LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                              LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_SAVE_DATA, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                                              LTC_STATEMACH_USER_IO_FEEDBACK, LTC_USER_IO_SAVE_DATA, LTC_STATEMACH_SHORTTIME);
                    break;

                } else if (ltc_state.substate == LTC_USER_IO_SAVE_DATA) {
                    if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                        DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                        LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                        break;
                    } else {
                        DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    }

                    if (LTC_RX_PECCheck(ltc_RXPECbuffer) != E_OK) {
                        DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_NOK, 0);
                    } else {
                        DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_OK, 0);
                        LTC_PortExpanderSaveValues(ltc_RXPECbuffer);
                    }

                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                }

            break;

        /****************************WRITE TO TI PORT EXPANDER IO***********/
        case LTC_STATEMACH_USER_IO_CONTROL_TI:

            if (ltc_state.substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SetPortExpanderDirection_TI(LTC_PORT_EXPANDER_TI_OUTPUT, ltc_TXBuffer, ltc_TXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                    LTC_STATEMACH_USER_IO_CONTROL_TI, LTC_USER_IO_SEND_CLOCK_STCOMM_TI, LTC_STATEMACH_SHORTTIME,
                    LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;

                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_USER_IO_CONTROL_TI, LTC_USER_IO_SET_OUTPUT_REGISTER_TI, ltc_state.gpioClocksTransferTime,
                        LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_SET_OUTPUT_REGISTER_TI) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SetPortExpander_Output_TI(ltc_TXBuffer, ltc_TXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_USER_IO_CONTROL_TI, LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_SECOND, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                        LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_SECOND) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                     LTC_STATEMACH_STARTMEAS, LTC_ENTRY, ltc_state.gpioClocksTransferTime,
                     LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }
            break;

        /****************************READ TI PORT EXPANDER IO***********/
        case LTC_STATEMACH_USER_IO_FEEDBACK_TI:

            if (ltc_state.substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SetPortExpanderDirection_TI(LTC_PORT_EXPANDER_TI_INPUT, ltc_TXBuffer, ltc_TXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                    LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_SEND_CLOCK_STCOMM_TI, LTC_STATEMACH_SHORTTIME,
                    LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;

                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST, ltc_state.gpioClocksTransferTime,
                        LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_GetPortExpander_Input_TI(0, ltc_TXBuffer, ltc_TXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                        LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_SECOND, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                        LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_SECOND) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                    LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND, ltc_state.gpioClocksTransferTime,
                    LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            } else if (ltc_state.substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_GetPortExpander_Input_TI(1, ltc_TXBuffer, ltc_TXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                    LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_THIRD, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                    LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_THIRD) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                ltc_state.check_spi_flag = FALSE;
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                    LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_FOURTH, ltc_state.gpioClocksTransferTime,
                    LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            } else if (ltc_state.substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_FOURTH) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)ltc_cmdRDCOMM, ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                    LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_SAVE_DATA_TI, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT,
                    LTC_STATEMACH_USER_IO_FEEDBACK_TI, LTC_USER_IO_SAVE_DATA_TI, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_USER_IO_SAVE_DATA_TI) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                if (LTC_RX_PECCheck(ltc_RXPECbuffer) != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_OK, 0);
                    LTC_PortExpanderSaveValues_TI(ltc_RXPECbuffer);
                }

                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }

            break;

        /****************************EEPROM READ*********************************/
        case LTC_STATEMACH_EEPROM_READ:

            if (ltc_state.substate == LTC_EEPROM_READ_DATA1) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SendEEPROMReadCommand(ltc_TXBuffer, ltc_TXPECbuffer, 0);

                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_SEND_CLOCK_STCOMM1, ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT);
                }

                break;

            } else if (ltc_state.substate == LTC_EEPROM_SEND_CLOCK_STCOMM1) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_READ_DATA2, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_READ_DATA2, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EEPROM_READ_DATA2) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_SendEEPROMReadCommand(ltc_TXBuffer, ltc_TXPECbuffer, 1);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_SEND_CLOCK_STCOMM2, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_SEND_CLOCK_STCOMM2, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EEPROM_SEND_CLOCK_STCOMM2) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM, LTC_STATEMACH_SHORTTIME);
                break;
            }  else if (ltc_state.substate == LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_RX((uint8_t*)ltc_cmdRDCOMM, ltc_RXPECbuffer);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_SAVE_READ, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_READ, LTC_EEPROM_SAVE_READ, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EEPROM_SAVE_READ) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                if (LTC_RX_PECCheck(ltc_RXPECbuffer) != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_NOK, 0);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_PEC, DIAG_EVENT_OK, 0);
                    LTC_EEPROMSaveReadValue(ltc_RXPECbuffer);
                }
                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }

            break;

        /****************************EEPROM READ*********************************/
        case LTC_STATEMACH_EEPROM_WRITE:

            if (ltc_state.substate == LTC_EEPROM_WRITE_DATA1) {
                ltc_state.check_spi_flag = TRUE;
                SPI_SetTransmitOngoing();
                retVal = LTC_SendEEPROMWriteCommand(ltc_TXBuffer, ltc_TXPECbuffer, 0);

                if (retVal != E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    ++ltc_state.muxmeas_seqptr;
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_SEND_CLOCK_STCOMM3, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT));
                }

                break;

            } else if (ltc_state.substate == LTC_EEPROM_SEND_CLOCK_STCOMM3) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_WRITE_DATA2, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_WRITE_DATA2, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EEPROM_WRITE_DATA2) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_SendEEPROMWriteCommand(ltc_TXBuffer, ltc_TXPECbuffer, 1);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_SEND_CLOCK_STCOMM4, (ltc_state.commandDataTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_SEND_CLOCK_STCOMM4, LTC_STATEMACH_SHORTTIME);
                break;

            } else if (ltc_state.substate == LTC_EEPROM_SEND_CLOCK_STCOMM4) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }

                SPI_SetTransmitOngoing();
                retVal = LTC_I2CClock(ltc_TXBufferClock, ltc_TXPECBufferClock);
                LTC_CondBasedStateTransition(retVal, DIAG_CH_LTC_SPI,
                                          LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_FINISHED, (ltc_state.gpioClocksTransferTime+LTC_TRANSMISSION_TIMEOUT),
                                          LTC_STATEMACH_EEPROM_WRITE, LTC_EEPROM_FINISHED, LTC_STATEMACH_SHORTTIME);
                break;
            }  else if (ltc_state.substate == LTC_EEPROM_FINISHED) {
                if (ltc_state.timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                    break;
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                }
                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                break;
            }

            break;

        /**************************OPEN-WIRE CHECK*******************************/
        case LTC_STATEMACH_OPENWIRE_CHECK:
            if (ltc_state.substate == LTC_REQUEST_PULLUP_CURRENT_OPENWIRE_CHECK) {
                /* Run ADOW command with PUP = 1 */
                ltc_state.adcMode = LTC_OW_MEASUREMENT_MODE;
                ltc_state.check_spi_flag = FALSE;

                retVal = LTC_StartOpenWireMeasurement(ltc_state.adcMode, 1);
                if (retVal == E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_REQUEST_PULLUP_CURRENT_OPENWIRE_CHECK, (ltc_state.commandDataTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, LTC_ADCMEAS_ALLCHANNEL)));
                    ltc_state.resendCommandCounter--;

                    /* Check how many retries are left */
                    if (ltc_state.resendCommandCounter == 0) {
                        /* Switch to read voltage state to read cell voltages */
                        LTC_StateTransition(LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_A_RDCVA_READVOLTAGE, (ltc_state.commandDataTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, LTC_ADCMEAS_ALLCHANNEL)));
                        /* Reuse read voltage register */
                        ltc_state.reusageMeasurementMode = LTC_REUSE_READVOLT_FOR_ADOW_PUP;
                    }
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                }
            } else if (ltc_state.substate == LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK) {
                /* Previous state: Read voltage -> information stored in voltage buffer */
                ltc_state.reusageMeasurementMode = LTC_NOT_REUSED;

                /* Copy data from voltage struct into open-wire struct */
                for (uint16_t i = 0; i < BS_NR_OF_BAT_CELLS; i++) {
                    ltc_openwire_pup_buffer[i] = ltc_cellvoltage.voltage[i];
                }

                /* Set number of ADOW retries - send ADOW command with pull-down two times */
                ltc_state.resendCommandCounter = LTC_NMBR_REQ_ADOW_COMMANDS;
                LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK, LTC_STATEMACH_SHORTTIME);

            } else if (ltc_state.substate == LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK) {
                /* Run ADOW command with PUP = 0 */
                ltc_state.adcMode = LTC_OW_MEASUREMENT_MODE;
                ltc_state.check_spi_flag = FALSE;

                retVal = LTC_StartOpenWireMeasurement(ltc_state.adcMode, 0);
                if (retVal == E_OK) {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_OK, 0);
                    LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK, (ltc_state.commandDataTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, LTC_ADCMEAS_ALLCHANNEL)));
                    ltc_state.resendCommandCounter--;

                    /* Check how many retries are left */
                    if (ltc_state.resendCommandCounter == 0) {
                        /* Switch to read voltage state to read cell voltages */
                        LTC_StateTransition(LTC_STATEMACH_READVOLTAGE, LTC_READ_VOLTAGE_REGISTER_A_RDCVA_READVOLTAGE, (ltc_state.commandDataTransferTime + LTC_Get_MeasurementTCycle(ltc_state.adcMode, LTC_ADCMEAS_ALLCHANNEL)));
                        /* Reuse read voltage register */
                        ltc_state.reusageMeasurementMode = LTC_REUSE_READVOLT_FOR_ADOW_PDOWN;
                    }
                } else {
                    DIAG_Handler(DIAG_CH_LTC_SPI, DIAG_EVENT_NOK, 0);
                    LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
                }
            } else if (ltc_state.substate == LTC_READ_VOLTAGES_PULLDOWN_OPENWIRE_CHECK) {
                /* Previous state: Read voltage -> information stored in voltage buffer */
                ltc_state.reusageMeasurementMode = LTC_NOT_REUSED;

                /* Copy data from voltage struct into open-wire struct */
                for (uint16_t i = 0; i < BS_NR_OF_BAT_CELLS; i++) {
                    ltc_openwire_pdown_buffer[i] = ltc_cellvoltage.voltage[i];
                }
                LTC_StateTransition(LTC_STATEMACH_OPENWIRE_CHECK, LTC_PERFORM_OPENWIRE_CHECK, LTC_STATEMACH_SHORTTIME);
            } else if (ltc_state.substate == LTC_PERFORM_OPENWIRE_CHECK) {
                /* Perform actual open-wire check */
                for (uint8_t m = 0; m < BS_NR_OF_MODULES; m++) {
                    /* Open-wire at C0: cell_pup(0) == 0 */
                    if (ltc_openwire_pup_buffer[0 + (m*BS_NR_OF_BAT_CELLS_PER_MODULE)] == 0) {
                        ltc_openwire.openwire[0 + (m*(BS_NR_OF_BAT_CELLS_PER_MODULE))] = 1;
                    }
                    /* Open-wire at Cmax: cell_pdown(BS_NR_OF_BAT_CELLS_PER_MODULE-1) == 0 */
                    if (ltc_openwire_pdown_buffer[((BS_NR_OF_BAT_CELLS_PER_MODULE-1) + (m*BS_NR_OF_BAT_CELLS_PER_MODULE))] == 0) {
                        ltc_openwire.openwire[BS_NR_OF_BAT_CELLS_PER_MODULE + (m*BS_NR_OF_BAT_CELLS_PER_MODULE)] = 1;
                    }
                }

                /* Take difference between pull-up and pull-down measurement */
                for (uint16_t i = 1; i < BS_NR_OF_BAT_CELLS; i++) {
                    ltc_openwire_delta[i] = (int32_t)(ltc_openwire_pup_buffer[i] - ltc_openwire_pdown_buffer[i]);
                }

                /* Open-wire at C(N): delta cell(n+1) < -400mV */
                for (uint8_t m = 0; m < BS_NR_OF_MODULES; m++) {
                    for (uint8_t c = 1; c < BS_NR_OF_BAT_CELLS_PER_MODULE-1; c++) {
                        if (ltc_openwire_delta[c + (m*BS_NR_OF_BAT_CELLS_PER_MODULE)] < -400) {
                            ltc_openwire.openwire[c + (m*BS_NR_OF_BAT_CELLS_PER_MODULE)] = 1;
                        }
                    }
                }

                /* Write database entry */
                DB_WriteBlock(&ltc_openwire, DATA_BLOCK_ID_OPEN_WIRE);
                /* Start new measurement cycle */
                LTC_StateTransition(LTC_STATEMACH_STARTMEAS, LTC_ENTRY, LTC_STATEMACH_SHORTTIME);
            }
            break;

        /****************************DEFAULT**************************/
        default:
            break;
    }

    ltc_state.triggerentry--;        /* reentrance counter */
}



/**
 * @brief   saves the multiplexer values read from the LTC daisy-chain.
 *
 * After a voltage measurement was initiated on GPIO 1 to read the currently selected
 * multiplexer voltage, the results is read via SPI from the daisy-chain.
 * This function is called to store the result from the transmission in a buffer.
 *
 * @param   *DataBufferSPI_RX   buffer containing the data obtained from the SPI transmission
 * @param   muxseqptr           pointer to the multiplexer sequence, which configures the currently selected multiplexer ID and channel
 */
static void LTC_SaveMuxMeasurement(uint8_t *rxBuffer, LTC_MUX_CH_CFG_s  *muxseqptr) {
    uint16_t i = 0;
    uint16_t val_ui = 0;
    int16_t temperature = 0;
    uint8_t sensor_idx = 0;
    uint8_t ch_idx = 0;
    uint32_t bitmask = 0;

    /* pointer to measurement Sequence of Mux- and Channel-Configurations (1,0xFF)...(3,0xFF),(0,1),...(0,7)) */
    if (muxseqptr->muxCh == 0xFF)
        return; /* Channel 0xFF means that the multiplexer is deactivated, therefore no measurement will be made and saved*/

    /* user multiplexer type -> connected to GPIO2! */
    if (muxseqptr->muxID == 1 || muxseqptr->muxID == 2) {
        for (i=0; i < LTC_N_LTC; i++) {
            if (muxseqptr->muxID == 1)
                ch_idx = 0 + muxseqptr->muxCh;    /* channel index 0..7 */
            else
                ch_idx = 8 + muxseqptr->muxCh;    /* channel index 8..15 */

            if (ch_idx < 2*8) {
                val_ui =*((uint16_t *)(&rxBuffer[6+1*i*8]));        /* raw values, all mux on all LTCs */
                ltc_user_mux.value[i*8*2+ch_idx] = (uint16_t)(((float)(val_ui))*100e-6f*1000.0f);  /* Unit -> in V -> in mV */
            }
        }
    } else {
        /* temperature multiplexer type -> connected to GPIO1! */
        for (i=0; i < LTC_N_LTC; i++) {
            val_ui = *((uint16_t *)(&rxBuffer[4+i*8]));
            /* GPIO voltage in 100uV -> * 0.1 ----  conversion to V from mV * 0.001 ----- -> 0.0001 */
            temperature = (int16_t)LTC_Convert_MuxVoltages_to_Temperatures((float)(val_ui)*0.0001f);        /* Unit Celsius */
            sensor_idx = ltc_muxsensortemperatur_cfg[muxseqptr->muxCh];
            /* if wrong configuration: exit and write nothing */
            if (sensor_idx >= BS_NR_OF_TEMP_SENSORS_PER_MODULE)
                return;
            /* Set bitmask for valid flags */
            bitmask |= 1 < sensor_idx;
            /* Check LTC PEC error */
            if (LTC_ErrorTable[i].PEC_valid == TRUE) {
                bitmask = ~bitmask;  /* negate bitmask to only validate flags of this cell voltage */
                ltc_celltemperature.valid_temperature[i] &= bitmask;
                ltc_celltemperature.temperature[i*(BS_NR_OF_TEMP_SENSORS_PER_MODULE)+sensor_idx] = temperature;
            } else {
                ltc_celltemperature.valid_temperature[i] |= bitmask;
            }
        }
    }
}



/**
 * @brief   saves the voltage values read from the LTC daisy-chain.
 *
 * After a voltage measurement was initiated to measure the voltages of the cells,
 * the result is read via SPI from the daisy-chain.
 * There are 6 register to read _(A,B,C,D,E,F) to get all cell voltages.
 * Only one register can be read at a time.
 * This function is called to store the result from the transmission in a buffer.
 *
 * @param   registerSet    voltage register that was read (voltage register A,B,C,D,E or F)
 * @param   *rxBuffer      buffer containing the data obtained from the SPI transmission
 * @param   PEC_valid      tells the functions if the PEC is valid or not, if not, manage indices but do not store
 *
 */
static void LTC_SaveRXtoVoltagebuffer(uint8_t registerSet, uint8_t *rxBuffer) {
    uint16_t i = 0;
    uint16_t j = 0;
    uint16_t i_offset = 0;
    uint16_t voltage_index = 0;
    uint16_t val_ui = 0;
    uint16_t voltage = 0;
    uint8_t incrementations = 0;
    uint32_t bitmask = 0;

    if (registerSet == 0) {
    /* RDCVA command -> voltage register group A */
        i_offset = 0;
    } else if (registerSet == 1) {
    /* RDCVB command -> voltage register group B */
        i_offset = 3;
    } else if (registerSet == 2) {
    /* RDCVC command -> voltage register group C */
        i_offset = 6;
    } else if (registerSet == 3) {
    /* RDCVD command -> voltage register group D */
        i_offset = 9;
    } else if (registerSet == 4) {
    /* RDCVD command -> voltage register group E (only for 15 and 18 cell version) */
        i_offset = 12;
    } else if (registerSet == 5) {
    /* RDCVD command -> voltage register group F (only for 18 cell version) */
        i_offset = 15;
    } else {
        return;
    }

    /* Calculate bitmask for valid flags */
    bitmask |= 0x07 << i_offset;    /* 0x07: three voltages in each register */

    /* reinitialize index counter at begin of cycle */
    if (i_offset == 0) {
        ltc_used_cells_index = 0;
    }

    /* Retrieve data without command and CRC*/
    for (i=0; i < LTC_N_LTC; i++) {
        incrementations = 0;

        /* parse all three voltages (3 * 2bytes) contained in one register */
        for (j=0; j < 3; j++) {
            /* index considering maximum number of cells */
            voltage_index = j+i_offset;

            if (ltc_voltage_input_used[voltage_index] == 1) {
                val_ui = *((uint16_t *)(&rxBuffer[4+2*j+i*8]));
                voltage = (uint16_t)(((float)(val_ui))*100e-6f*1000.0f);        /* Unit V -> in mV */
                /* Check PEC for every LTC in the daisy-chain */
                if (LTC_ErrorTable[i].PEC_valid == TRUE) {
                    ltc_cellvoltage.voltage[ltc_used_cells_index+i*(BS_NR_OF_BAT_CELLS_PER_MODULE)] = voltage;
                    bitmask = ~bitmask;  /* negate bitmask to only validate flags of this voltage register */
                    ltc_cellvoltage.valid_volt[(i/LTC_NUMBER_OF_LTC_PER_MODULE)] &= bitmask;
                } else {
                    /* PEC_valid == FALSE: Invalidate only flags of this voltage register */
                    ltc_cellvoltage.valid_volt[(i/LTC_NUMBER_OF_LTC_PER_MODULE)] |= bitmask;
                }

                ltc_used_cells_index++;
                incrementations++;

                if (ltc_used_cells_index > BS_NR_OF_BAT_CELLS_PER_MODULE) {
                    return;
                }
            }
        }
        /* restore start value for next module */
#pragma GCC diagnostic push
        /* This warning is allowed for the edge case
         * LTC_N_LTC == 1
         */
#pragma GCC diagnostic ignored "-Wtype-limits"
        if (i < LTC_N_LTC-1) {
#pragma GCC diagnostic pop
            ltc_used_cells_index -= incrementations;
        }
    }
}

/**
 * @brief   saves the GPIO voltage values read from the LTC daisy-chain.
 *
 * After a voltage measurement was initiated to measure the voltages on all GPIOs,
 * the result is read via SPI from the daisy-chain. In order to read the result of all GPIO measurements,
 * it is necessary to read auxiliary register A and B.
 * Only one register can be read at a time.
 * This function is called to store the result from the transmission in a buffer.
 *
 * @param   registerSet    voltage register that was read (auxiliary register A, B, C or D)
 * @param   *rxBuffer      buffer containing the data obtained from the SPI transmission
 *
 */
static void LTC_SaveRXtoGPIOBuffer(uint8_t registerSet, uint8_t *rxBuffer) {
    uint16_t i = 0;
    uint8_t i_offset = 0;
    uint32_t bitmask = 0;

    if (registerSet == 0) {
    /* RDAUXA command -> GPIO register group A */
        i_offset = 0;
        bitmask = 0x07 << i_offset;  /* 0x07: three temperatures in this register */
        /* Retrieve data without command and CRC*/
        for (i = 0; i < LTC_N_LTC; i++) {
            /* Check if PEC is valid */
            if (LTC_ErrorTable[i].PEC_valid == TRUE) {
                bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
                ltc_allgpiovoltage.valid_gpiovoltages[i] &= bitmask;
                /* values received in 100uV -> divide by 10 to convert to mV */
                ltc_allgpiovoltage.gpiovoltage[0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[4+i*8]))/10;
                ltc_allgpiovoltage.gpiovoltage[1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[6+i*8]))/10;
                ltc_allgpiovoltage.gpiovoltage[2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[8+i*8]))/10;
            } else {
                ltc_allgpiovoltage.valid_gpiovoltages[i] |= bitmask;
            }
        }
    } else if (registerSet == 1) {
        /* RDAUXB command -> GPIO register group B */
        i_offset = 3;
        bitmask = 0x03 << i_offset;  /* 0x03: two temperatures in this register */
        /* Retrieve data without command and CRC*/
        for (i = 0; i < LTC_N_LTC; i++) {
            /* Check if PEC is valid */
            if (LTC_ErrorTable[i].PEC_valid == TRUE) {
                bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
                ltc_allgpiovoltage.valid_gpiovoltages[i] &= bitmask;
                /* values received in 100uV -> divide by 10 to convert to mV */
                ltc_allgpiovoltage.gpiovoltage[0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[4+i*8]))/10;
                ltc_allgpiovoltage.gpiovoltage[1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[6+i*8]))/10;
            } else {
                ltc_allgpiovoltage.valid_gpiovoltages[i] |= bitmask;
            }
        }
    } else if (registerSet == 2) {
        /* RDAUXC command -> GPIO register group C, for 18 cell version */
        i_offset = 5;
        bitmask = 0x07 << i_offset;  /* 0x07: three temperatures in this register */
        /* Retrieve data without command and CRC*/
        for (i = 0; i < LTC_N_LTC; i++) {
            /* Check if PEC is valid */
            if (LTC_ErrorTable[i].PEC_valid == TRUE) {
                bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
                ltc_allgpiovoltage.valid_gpiovoltages[i] &= bitmask;
                /* values received in 100uV -> divide by 10 to convert to mV */
                ltc_allgpiovoltage.gpiovoltage[0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[4+i*8]))/10;
                ltc_allgpiovoltage.gpiovoltage[1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[6+i*8]))/10;
                ltc_allgpiovoltage.gpiovoltage[2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[8+i*8]))/10;
            } else {
                ltc_allgpiovoltage.valid_gpiovoltages[i] |= bitmask;
            }
        }
    } else if (registerSet == 3) {
        /* RDAUXD command -> GPIO register group D, for 18 cell version */
        i_offset = 8;
        bitmask = 0x01 << i_offset;  /* 0x01: one temperature in this register */
        /* Retrieve data without command and CRC*/
        for (i = 0; i < LTC_N_LTC; i++) {
            /* Check if PEC is valid */
            if (LTC_ErrorTable[i].PEC_valid == TRUE) {
                bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
                ltc_allgpiovoltage.valid_gpiovoltages[i] &= bitmask;
                /* values received in 100uV -> divide by 10 to convert to mV */
                ltc_allgpiovoltage.gpiovoltage[0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&rxBuffer[4+i*8]))/10;
            } else {
                ltc_allgpiovoltage.valid_gpiovoltages[i] |= bitmask;
            }
        }
    } else {
        return;
    }
}


/**
 * @brief   checks if the multiplexers acknowledged transmission.
 *
 * The RDCOMM command can be used to read the answer of the multiplexers to a
 * I2C transmission.
 * This function determines if the communication with the multiplexers was
 * successful or not.
 * The array LTC_ErrorTable is updated to locate the multiplexers that did not
 * acknowledge transmission.
 *
 * @param   *DataBufferSPI_RX    data obtained from the SPI transmission
 * @param   mux                  multiplexer to be addressed (multiplexer ID)
 *
 * @return  mux_error            0 is there was no error, 1 if there was errors
 */
static uint8_t LTC_I2CCheckACK(uint8_t *DataBufferSPI_RX, int mux) {
    uint8_t mux_error = E_OK;
    uint16_t i = 0;

    for (i=0; i < BS_NR_OF_MODULES; i++) {
        if (mux == 0) {
            if ((DataBufferSPI_RX[4+1+LTC_NUMBER_OF_LTC_PER_MODULE*i*8] & 0x0F) != 0x07) {  /* ACK = 0xX7 */
                if (LTC_DISCARD_MUX_CHECK == FALSE) {
                    LTC_ErrorTable[i].mux0 = 1;
                }
                mux_error = E_NOT_OK;
            } else {
                LTC_ErrorTable[i].mux0 = 0;
            }
        }
        if (mux == 1) {
            if ((DataBufferSPI_RX[4+1+LTC_NUMBER_OF_LTC_PER_MODULE*i*8] & 0x0F) != 0x27) {
                if (LTC_DISCARD_MUX_CHECK == FALSE) {
                    LTC_ErrorTable[i].mux1 = 1;
                }
                mux_error = E_NOT_OK;
            } else {
                LTC_ErrorTable[i].mux1 = 0;
            }
        }
        if (mux == 2) {
            if ((DataBufferSPI_RX[4+1+LTC_NUMBER_OF_LTC_PER_MODULE*i*8] & 0x0F) != 0x47) {
                if (LTC_DISCARD_MUX_CHECK == FALSE) {
                    LTC_ErrorTable[i].mux2 = 1;
                }
                mux_error = E_NOT_OK;
            } else {
                LTC_ErrorTable[i].mux2 = 0;
            }
        }
        if (mux == 3) {
            if ((DataBufferSPI_RX[4+1+LTC_NUMBER_OF_LTC_PER_MODULE*i*8] & 0x0F) != 0x67) {
                if (LTC_DISCARD_MUX_CHECK == FALSE) {
                    LTC_ErrorTable[i].mux3 = 1;
                }
                mux_error = E_NOT_OK;
            } else {
                LTC_ErrorTable[i].mux3 = 0;
            }
        }
    }

    if (LTC_DISCARD_MUX_CHECK == TRUE) {
        return 0;
    } else {
        return mux_error;
    }
}



/*
 * @brief   initialize the daisy-chain.
 *
 * To initialize the LTC6804 daisy-chain, a dummy byte (0x00) is sent.
 *
 * @return  retVal  E_OK if dummy byte was sent correctly by SPI, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_Init(void) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;
    STD_RETURN_TYPE_e retVal = E_OK;

    uint8_t PEC_Check[6];
    uint16_t PEC_result = 0;
    uint16_t i = 0;


    /* set REFON bit to 1 */
    /* data for the configuration */
    for (i=0; i < LTC_N_LTC; i++) {
        /* FC = disable all pull-downs, REFON = 1, DTEN = 0, ADCOPT = 0 */
        ltc_TXBuffer[0+(1*i)*6] = 0xFC;
        ltc_TXBuffer[1+(1*i)*6] = 0x00;
        ltc_TXBuffer[2+(1*i)*6] = 0x00;
        ltc_TXBuffer[3+(1*i)*6] = 0x00;
        ltc_TXBuffer[4+(1*i)*6] = 0x00;
        ltc_TXBuffer[5+(1*i)*6] = 0x00;
    }

    /* now construct the message to be sent: it contains the wanted data, PLUS the needed PECs */
    ltc_TXPECbuffer[0] = ltc_cmdWRCFG[0];
    ltc_TXPECbuffer[1] = ltc_cmdWRCFG[1];
    ltc_TXPECbuffer[2] = ltc_cmdWRCFG[2];
    ltc_TXPECbuffer[3] = ltc_cmdWRCFG[3];

    for (i=0; i < LTC_N_LTC; i++) {
        PEC_Check[0] = ltc_TXPECbuffer[4+i*8] = ltc_TXBuffer[0+i*6];
        PEC_Check[1] = ltc_TXPECbuffer[5+i*8] = ltc_TXBuffer[1+i*6];
        PEC_Check[2] = ltc_TXPECbuffer[6+i*8] = ltc_TXBuffer[2+i*6];
        PEC_Check[3] = ltc_TXPECbuffer[7+i*8] = ltc_TXBuffer[3+i*6];
        PEC_Check[4] = ltc_TXPECbuffer[8+i*8] = ltc_TXBuffer[4+i*6];
        PEC_Check[5] = ltc_TXPECbuffer[9+i*8] = ltc_TXBuffer[5+i*6];

        PEC_result = LTC_pec15_calc(6, PEC_Check);
        ltc_TXPECbuffer[10+i*8]=(uint8_t)((PEC_result>>8)&0xff);
        ltc_TXPECbuffer[11+i*8]=(uint8_t)(PEC_result&0xff);
    }  /* end for */

    statusSPI = LTC_SendData(ltc_TXPECbuffer);

    if (statusSPI != E_OK) {
        retVal = E_NOT_OK;
    }

    retVal = statusSPI;

    return retVal;
}




/*
 * @brief   sets the balancing according to the control values read in the database.
 *
 * To set balancing for the cells, the corresponding bits have to be written in the configuration register.
 * The LTC driver only executes the balancing orders written by the BMS in the database.
 *
 * @param registerSet   Register Set, 0: cells 1 to 12 (WRCFG), 1: cells 13 to 15/18 (WRCFG2)
 *
 * @return              E_OK if dummy byte was sent correctly by SPI, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_BalanceControl(uint8_t registerSet) {
    STD_RETURN_TYPE_e retVal = E_OK;

    uint16_t i = 0;
    uint16_t j = 0;

    LTC_Get_BalancingControlValues();

    if (registerSet == 0) {  /* cells 1 to 12, WRCFG */
        for (j=0; j < BS_NR_OF_MODULES; j++) {
            i = BS_NR_OF_MODULES-j-1;

            /* FC = disable all pull-downs, REFON = 1 (reference always on), DTEN off, ADCOPT = 0 */
            ltc_TXBuffer[0+(i)*6] = 0xFC;
            ltc_TXBuffer[1+(i)*6] = 0x00;
            ltc_TXBuffer[2+(i)*6] = 0x00;
            ltc_TXBuffer[3+(i)*6] = 0x00;
            ltc_TXBuffer[4+(i)*6] = 0x00;
            ltc_TXBuffer[5+(i)*6] = 0x00;

            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+0] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x01;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+1] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x02;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+2] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x04;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+3] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x08;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+4] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x10;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+5] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x20;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+6] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x40;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+7] == 1) {
                ltc_TXBuffer[4+(i)*6]|=0x80;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+8] == 1) {
                ltc_TXBuffer[5+(i)*6]|=0x01;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+9] == 1) {
                ltc_TXBuffer[5+(i)*6]|=0x02;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+10] == 1) {
                ltc_TXBuffer[5+(i)*6]|=0x04;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+11] == 1) {
                ltc_TXBuffer[5+(i)*6]|=0x08;
            }
        }
        retVal = LTC_TX((uint8_t*)ltc_cmdWRCFG, ltc_TXBuffer, ltc_TXPECbuffer);
    } else if (registerSet == 1) {  /* cells 13 to 15/18 WRCFG2 */
        for (j=0; j < BS_NR_OF_MODULES; j++) {
            i = BS_NR_OF_MODULES-j-1;

            /* 0x0F = disable pull-downs on GPIO6-9 */
            ltc_TXBuffer[0+(i)*6] = 0x0F;
            ltc_TXBuffer[1+(i)*6] = 0x00;
            ltc_TXBuffer[2+(i)*6] = 0x00;
            ltc_TXBuffer[3+(i)*6] = 0x00;
            ltc_TXBuffer[4+(i)*6] = 0x00;
            ltc_TXBuffer[5+(i)*6] = 0x00;

            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+12] == 1) {
                ltc_TXBuffer[0+(i)*6] |= 0x10;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+13] == 1) {
                ltc_TXBuffer[0+(i)*6] |= 0x20;
            }
            if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+14] == 1) {
                ltc_TXBuffer[0+(i)*6] |= 0x40;
            }
            if (BS_NR_OF_BAT_CELLS_PER_MODULE > 15) {
                if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+15] == 1) {
                    ltc_TXBuffer[0+(i)*6] |= 0x80;
                }
                if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+16] == 1) {
                    ltc_TXBuffer[1+(i)*6] |= 0x01;
                }
                if (ltc_balancing_control.balancing_state[j*(BS_NR_OF_BAT_CELLS_PER_MODULE)+17] == 1) {
                    ltc_TXBuffer[1+(i)*6] |= 0x02;
                }
            }
        }
        retVal = LTC_TX((uint8_t*)ltc_cmdWRCFG2, ltc_TXBuffer, ltc_TXPECbuffer);
    } else {
        return E_NOT_OK;
    }
    return retVal;
}


/*
 * @brief   resets the error table.
 *
 * This function should be called during initialization or before starting a new measurement cycle
 *
 */
static void LTC_ResetErrorTable(void) {
    uint16_t i = 0;

    for (i=0; i < LTC_N_LTC; i++) {
        LTC_ErrorTable[i].PEC_valid = 0;
        LTC_ErrorTable[i].mux0 = 0;
        LTC_ErrorTable[i].mux1 = 0;
        LTC_ErrorTable[i].mux2 = 0;
        LTC_ErrorTable[i].mux3 = 0;
    }
}


/**
 * @brief   brief missing
 *
 * Gets the measurement time needed by the LTC chip, depending on the measurement mode and the number of channels.
 * For all cell voltages or all 5 GPIOS, the measurement time is the same.
 * For 2 cell voltages or 1 GPIO, the measurement time is the same.
 * As a consequence, this function is used for cell voltage and for GPIO measurement.
 *
 * @param   adcMode     LTC ADCmeasurement mode (fast, normal or filtered)
 * @param   adcMeasCh   number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
 *                      or number of cell voltage measured (2 cells or all cells)
 *
 * @return  retVal      measurement time in ms
 */
static uint16_t LTC_Get_MeasurementTCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e  adcMeasCh) {
    uint16_t retVal = LTC_STATEMACH_MEAS_ALL_NORMAL_TCYCLE;  /* default */

    if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL) {
        if (adcMode == LTC_ADCMODE_FAST_DCP0 || adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_STATEMACH_MEAS_ALL_FAST_TCYCLE;
        } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1) {
            retVal = LTC_STATEMACH_MEAS_ALL_NORMAL_TCYCLE;
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0 || adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_STATEMACH_MEAS_ALL_FILTERED_TCYCLE;
        }
    } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1 || adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2
            || adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3 || adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO4
            || adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO5 || adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
        if (adcMode == LTC_ADCMODE_FAST_DCP0 || adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_STATEMACH_MEAS_SINGLE_FAST_TCYCLE;
        } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1) {
            retVal = LTC_STATEMACH_MEAS_SINGLE_NORMAL_TCYCLE;
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0 || adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_STATEMACH_MEAS_SINGLE_FILTERED_TCYCLE;
        }
    } else {
        retVal = LTC_STATEMACH_MEAS_ALL_NORMAL_TCYCLE;
    }

    return retVal;
}


/**
 * @brief   tells the LTC daisy-chain to start measuring the voltage on all cells.
 *
 * This function sends an instruction to the daisy-chain via SPI, in order to start voltage measurement for all cells.
 *
 * @param   adcMode     LTC ADCmeasurement mode (fast, normal or filtered)
 * @param   adcMeasCh   number of cell voltage measured (2 cells or all cells)
 *
 * @return  retVal      E_OK if dummy byte was sent correctly by SPI, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_StartVoltageMeasurement(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh) {
    STD_RETURN_TYPE_e retVal = E_OK;

    if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL) {
        if (adcMode == LTC_ADCMODE_FAST_DCP0) {
            retVal = LTC_SendCmd(ltc_cmdADCV_fast_DCP0);
        } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
            retVal = LTC_SendCmd(ltc_cmdADCV_normal_DCP0);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
            retVal = LTC_SendCmd(ltc_cmdADCV_filtered_DCP0);
        } else if (adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADCV_fast_DCP1);
        } else if (adcMode == LTC_ADCMODE_NORMAL_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADCV_normal_DCP1);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADCV_filtered_DCP1);
        } else {
            retVal = E_NOT_OK;
        }
    } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
        if (adcMode == LTC_ADCMODE_FAST_DCP0) {
            retVal = LTC_SendCmd(ltc_cmdADCV_fast_DCP0_twocells);
        } else {
            retVal = E_NOT_OK;
        }
    } else {
        retVal = E_NOT_OK;
    }
    return retVal;
}


/**
 * @brief   tells LTC daisy-chain to start measuring the voltage on GPIOS.
 *
 * This function sends an instruction to the daisy-chain via SPI to start the measurement.
 *
 * @param   adcMode     LTC ADCmeasurement mode (fast, normal or filtered)
 * @param   adcMeasCh   number of channels measured for GPIOS (one at a time, typically when multiplexers are used, or all five GPIOs)
 *
 * @return  retVal      E_OK if dummy byte was sent correctly by SPI, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_StartGPIOMeasurement(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e  adcMeasCh) {
    STD_RETURN_TYPE_e retVal;

    if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL) {
        if (adcMode == LTC_ADCMODE_FAST_DCP0 || adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_fast_ALLGPIOS);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0 || adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_filtered_ALLGPIOS);
        } else {
            /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
            retVal = LTC_SendCmd(ltc_cmdADAX_normal_ALLGPIOS);
        }
    } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) {
        /* Single Channel */
        if (adcMode == LTC_ADCMODE_FAST_DCP0 || adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_fast_GPIO1);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0 || adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_filtered_GPIO1);
        } else {
            /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/

            retVal = LTC_SendCmd(ltc_cmdADAX_normal_GPIO1);
        }
    } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) {
        /* Single Channel */
        if (adcMode == LTC_ADCMODE_FAST_DCP0 || adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_fast_GPIO2);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0 || adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_filtered_GPIO2);
        } else {
            /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/

            retVal = LTC_SendCmd(ltc_cmdADAX_normal_GPIO2);
        }
    } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) {
        /* Single Channel */
        if (adcMode == LTC_ADCMODE_FAST_DCP0 || adcMode == LTC_ADCMODE_FAST_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_fast_GPIO3);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0 || adcMode == LTC_ADCMODE_FILTERED_DCP1) {
            retVal = LTC_SendCmd(ltc_cmdADAX_filtered_GPIO3);
        } else {
            /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/

            retVal = LTC_SendCmd(ltc_cmdADAX_normal_GPIO3);
        }
    } else {
        retVal = E_NOT_OK;
    }

    return retVal;
}


/**
 * @brief   tells LTC daisy-chain to start measuring the voltage on GPIOS.
 *
 * This function sends an instruction to the daisy-chain via SPI to start the measurement.
 *
 * @param   adcMode     LTC ADCmeasurement mode (fast, normal or filtered)
 * @param   PUP         pull-up bit for pull-up or pull-down current (0: pull-down, 1: pull-up)
 *
 * @return  retVal      E_OK if command was sent correctly by SPI, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_StartOpenWireMeasurement(LTC_ADCMODE_e adcMode, uint8_t PUP) {
    STD_RETURN_TYPE_e retval = E_NOT_OK;
    if (PUP == 0) {
        /* pull-down current */
        if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
            retval = LTC_SendCmd(ltc2_BC_cmdADOW_PDOWN_normal_DCP0);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
            retval = LTC_SendCmd(ltc2_BC_cmdADOW_PDOWN_filtered_DCP0);
        } else {
            retval = E_NOT_OK;
        }
    } else if (PUP == 1) {
        /* pull-up current */
        if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
            retval = LTC_SendCmd(ltc2_BC_cmdADOW_PUP_normal_DCP0);
        } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
            retval = LTC_SendCmd(ltc2_BC_cmdADOW_PUP_filtered_DCP0);
        } else {
            retval = E_NOT_OK;
        }
    }
    return retval;
}



/**
 * @brief   checks if the data received from the daisy-chain is not corrupt.
 *
 * This function computes the PEC (CRC) from the data received by the daisy-chain.
 * It compares it with the PEC sent by the LTCs.
 * If there are errors, the array LTC_ErrorTable is updated to locate the LTCs in daisy-chain
 * that transmitted corrupt data.
 *
 * @param   *DataBufferSPI_RX_with_PEC   data obtained from the SPI transmission
 *
 * @return  retVal                       E_OK if PEC check is OK, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_RX_PECCheck(uint8_t *DataBufferSPI_RX_with_PEC) {
    uint16_t i = 0;
    STD_RETURN_TYPE_e retVal = E_OK;
    uint8_t PEC_TX[2];
    uint16_t PEC_result = 0;
    uint8_t PEC_Check[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    /* check all PECs and put data without command and PEC in DataBufferSPI_RX (easier to use) */
    for (i=0; i < LTC_N_LTC; i++) {
        PEC_Check[0] = DataBufferSPI_RX_with_PEC[4+i*8];
        PEC_Check[1] = DataBufferSPI_RX_with_PEC[5+i*8];
        PEC_Check[2] = DataBufferSPI_RX_with_PEC[6+i*8];
        PEC_Check[3] = DataBufferSPI_RX_with_PEC[7+i*8];
        PEC_Check[4] = DataBufferSPI_RX_with_PEC[8+i*8];
        PEC_Check[5] = DataBufferSPI_RX_with_PEC[9+i*8];

        PEC_result = LTC_pec15_calc(6, PEC_Check);
        PEC_TX[0]=(uint8_t)((PEC_result>>8)&0xff);
        PEC_TX[1]=(uint8_t)(PEC_result&0xff);

        /* if calculated PEC not equal to received PEC */
        if ((PEC_TX[0] != DataBufferSPI_RX_with_PEC[10+i*8]) || (PEC_TX[1] != DataBufferSPI_RX_with_PEC[11+i*8])) {
            /* update error table of the corresponding LTC only if PEC check is activated */
            if (LTC_DISCARD_PEC == FALSE) {
                LTC_ErrorTable[i].PEC_valid = FALSE;
            }
            retVal = E_NOT_OK;

        } else {
            /* update error table of the corresponding LTC */
            LTC_ErrorTable[i].PEC_valid = TRUE;
        }
    }

    if (LTC_DISCARD_PEC == TRUE) {
        return E_OK;
    } else {
        return (retVal);
    }
}


/**
 * @brief   send command to the LTC daisy-chain and receives data from the LTC daisy-chain.
 *
 * This is the core function to receive data from the LTC6804 daisy-chain.
 * A 2 byte command is sent with the corresponding PEC. Example: read configuration register (RDCFG).
 * Only command has to be set, the function calculates the PEC automatically.
 * The data send is:
 * 2 bytes (COMMAND) 2 bytes (PEC)
 * The data received is:
 * 6 bytes (LTC1) 2 bytes (PEC) + 6 bytes (LTC2) 2 bytes (PEC) + 6 bytes (LTC3) 2 bytes (PEC) + ... + 6 bytes (LTC{LTC_N_LTC}) 2 bytes (PEC)
 *
 * The function does not check the PECs. This has to be done elsewhere.
 *
 * @param   *Command                    command sent to the daisy-chain
 * @param   *DataBufferSPI_RX_with_PEC  data to sent to the daisy-chain, i.e. data to be sent + PEC
 *
 * @return  statusSPI                   E_OK if SPI transmission is OK, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_RX(uint8_t *Command, uint8_t *DataBufferSPI_RX_with_PEC) {
    STD_RETURN_TYPE_e statusSPI = E_OK;
    uint16_t i = 0;

    /* DataBufferSPI_RX_with_PEC contains the data to receive.
       The transmission function checks the PECs.
       It constructs DataBufferSPI_RX, which contains the received data without PEC (easier to use). */

    for (i=0; i < LTC_N_BYTES_FOR_DATA_TRANSMISSION; i++) {
        ltc_TXPECbuffer[i] = 0x00;
    }

    ltc_TXPECbuffer[0] = Command[0];
    ltc_TXPECbuffer[1] = Command[1];
    ltc_TXPECbuffer[2] = Command[2];
    ltc_TXPECbuffer[3] = Command[3];

    statusSPI = LTC_ReceiveData(ltc_TXPECbuffer, DataBufferSPI_RX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}



/**
 * @brief   sends command and data to the LTC daisy-chain.
 *
 * This is the core function to transmit data to the LTC6804 daisy-chain.
 * The data sent is:
 * COMMAND + 6 bytes (LTC1) + 6 bytes (LTC2) + 6 bytes (LTC3) + ... + 6 bytes (LTC{LTC_N_LTC})
 * A 2 byte command is sent with the corresponding PEC. Example: write configuration register (WRCFG).
 * THe command has to be set and then the function calculates the PEC automatically.
 * The function calculates the needed PEC to send the data to the daisy-chain. The sent data has the format:
 * 2 byte-COMMAND (2 bytes PEC) + 6 bytes (LTC1) (2 bytes PEC) + 6 bytes (LTC2) (2 bytes PEC) + 6 bytes (LTC3) (2 bytes PEC) + ... + 6 bytes (LTC{LTC_N_LTC}) (2 bytes PEC)
 *
 * The function returns 0. The only way to check if the transmission was successful is to read the results of the write operation.
 * (example: read configuration register after writing to it)
 *
 * @param   *Command                    command sent to the daisy-chain
 * @param   *DataBufferSPI_TX           data to be sent to the daisy-chain
 * @param   *DataBufferSPI_TX_with_PEC  data to sent to the daisy-chain, i.e. data to be sent + PEC (calculated by the function)
 *
 * @return                              E_OK if SPI transmission is OK, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_TX(uint8_t *Command, uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC) {
    uint16_t i = 0;
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;
    uint16_t PEC_result = 0;
    uint8_t PEC_Check[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    /*  DataBufferSPI_TX contains the data to send.
        The transmission function calculates the needed PEC.
        With it constructs DataBufferSPI_TX_with_PEC.
        It corresponds to the data effectively received/sent. */
    for (i=0; i < LTC_N_BYTES_FOR_DATA_TRANSMISSION; i++) {
        DataBufferSPI_TX_with_PEC[i] = 0x00;
    }

    DataBufferSPI_TX_with_PEC[0] = Command[0];
    DataBufferSPI_TX_with_PEC[1] = Command[1];
    DataBufferSPI_TX_with_PEC[2] = Command[2];
    DataBufferSPI_TX_with_PEC[3] = Command[3];

    /* Calculate PEC of all data (1 PEC value for 6 bytes) */
    for (i=0; i < LTC_N_LTC; i++) {
        PEC_Check[0] = DataBufferSPI_TX_with_PEC[4+i*8] = DataBufferSPI_TX[0+i*6];
        PEC_Check[1] = DataBufferSPI_TX_with_PEC[5+i*8] = DataBufferSPI_TX[1+i*6];
        PEC_Check[2] = DataBufferSPI_TX_with_PEC[6+i*8] = DataBufferSPI_TX[2+i*6];
        PEC_Check[3] = DataBufferSPI_TX_with_PEC[7+i*8] = DataBufferSPI_TX[3+i*6];
        PEC_Check[4] = DataBufferSPI_TX_with_PEC[8+i*8] = DataBufferSPI_TX[4+i*6];
        PEC_Check[5] = DataBufferSPI_TX_with_PEC[9+i*8] = DataBufferSPI_TX[5+i*6];

        PEC_result = LTC_pec15_calc(6, PEC_Check);
        DataBufferSPI_TX_with_PEC[10+i*8]=(uint8_t)((PEC_result>>8)&0xff);
        DataBufferSPI_TX_with_PEC[11+i*8]=(uint8_t)(PEC_result&0xff);
    }

    statusSPI = LTC_SendData(DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}

/**
 * @brief   configures the data that will be sent to the LTC daisy-chain to configure multiplexer channels.
 *
 * This function does not sent the data to the multiplexer daisy-chain. This is done
 * by the function LTC_SetMuxChannel(), which calls LTC_SetMUXChCommand()..
 *
 * @param   *DataBufferSPI_TX      data to be sent to the daisy-chain to configure the multiplexer channels
 * @param   mux                    multiplexer ID to be configured (0,1,2 or 3)
 * @param   channel                multiplexer channel to be configured (0 to 7)
 *
 */
static void LTC_SetMUXChCommand(uint8_t *DataBufferSPI_TX, uint8_t mux, uint8_t channel) {
    uint16_t i = 0;

    for (i=0; i < LTC_N_LTC; i++) {
#if SLAVE_BOARD_VERSION == 2

        /* using ADG728 */
        uint8_t address = 0x4C | (mux % 3);
        uint8_t data = 1 << (channel % 8);
        if (channel == 0xFF) {  /* no channel selected, output of multiplexer is high impedance */
            data = 0x00;
        }

#else

        /* using LTC1380 */
        uint8_t address = 0x48 | (mux % 4);
        uint8_t data = 0x08 | (channel % 8);
        if (channel == 0xFF) {  /* no channel selected, output of multiplexer is high impedance */
            data = 0x00;
        }

#endif

        DataBufferSPI_TX[0 + i * 6] = LTC_ICOM_START | (address >> 3);        /* 0x6 : LTC6804: ICOM START from Master */
        DataBufferSPI_TX[1 + i * 6] = LTC_FCOM_MASTER_NACK | (address << 5);
        DataBufferSPI_TX[2 + i * 6] = LTC_ICOM_BLANK | (data >> 4);
        DataBufferSPI_TX[3 + i * 6] = LTC_FCOM_MASTER_NACK_STOP | (data << 4);
        DataBufferSPI_TX[4 + i * 6] = LTC_ICOM_NO_TRANSMIT;        /* 0x1 : ICOM-STOP */
        DataBufferSPI_TX[5 + i * 6] = 0x00;        /* 0x0 : dummy (Dn) */
                                                   /* 9: MASTER NACK + STOP (FCOM) */
    }
}



/**
 * @brief   sends data to the LTC daisy-chain to read EEPROM on slaves.
 *
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 * @param        step                           first or second stage of read process (0 or 1)
 * @param        address                        read address (18 bits)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_SendEEPROMReadCommand(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t step) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    /* send WRCOMM to send I2C message to choose channel */
    LTC_SetEEPROMReadCommand(step, DataBufferSPI_TX);
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}



/**
 * @brief   configures the data that will be sent to the LTC daisy-chain to read EEPROM on slaves.
 *
 * @param   step                   first or second stage of read process (0 or 1)
 * @param   *DataBufferSPI_TX      data to be sent to the daisy-chain
 * @param   address                read address (18 bits)
 *
 */
static void LTC_SetEEPROMReadCommand(uint8_t step, uint8_t *DataBufferSPI_TX) {
    uint16_t i = 0;
    uint32_t address = 0;
    uint8_t address0 = 0;
    uint8_t address1 = 0;
    uint8_t address2 = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    address = ltc_slave_control.eeprom_read_address_to_use;

    address &= 0x3FFFF;
    address0 = address>>16;
    address1 = (address&0xFFFF)>>8;
    address2 = address&0xFF;

    if (step == 0) {
        for (i=0; i < LTC_N_LTC; i++) {
            DataBufferSPI_TX[0 + i * 6] = LTC_ICOM_START | (0x0A);        /* 0x6 : LTC6804: ICOM START from Master */
            DataBufferSPI_TX[1 + i * 6] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03) << 5) | 0x00);
            DataBufferSPI_TX[2 + i * 6] = LTC_ICOM_BLANK | (address1 >> 4);
            DataBufferSPI_TX[3 + i * 6] = LTC_FCOM_MASTER_NACK | (address1 << 4);
            DataBufferSPI_TX[4 + i * 6] = LTC_ICOM_BLANK | (address2 >> 4);
            DataBufferSPI_TX[5 + i * 6] = LTC_FCOM_MASTER_NACK | (address2 << 4);
        }

    } else {  /* step == 1 */
        for (i=0; i < LTC_N_LTC; i++) {
            DataBufferSPI_TX[0 + i * 6] = LTC_ICOM_START | (0x0A);        /* 0x6 : LTC6804: ICOM START from Master */
            DataBufferSPI_TX[1 + i * 6] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03) << 5) | 0x10);
            DataBufferSPI_TX[2 + i * 6] = LTC_ICOM_BLANK | 0x0F;
            DataBufferSPI_TX[3 + i * 6] = LTC_FCOM_MASTER_NACK_STOP | 0xF0;
            DataBufferSPI_TX[4 + i * 6] = LTC_ICOM_NO_TRANSMIT | 0x00;
            DataBufferSPI_TX[5 + i * 6] = LTC_FCOM_MASTER_NACK_STOP | 0x00;
        }
    }
}


/**
 * @brief   saves the read values of the external EEPROMs read from the LTC daisy-chain.
 *
 *
 * @param   *rxBuffer      buffer containing the data obtained from the SPI transmission
 *
 */
static void LTC_EEPROMSaveReadValue(uint8_t *rxBuffer) {
    uint16_t i = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    for (i=0; i < LTC_N_LTC; i++) {
        ltc_slave_control.eeprom_value_read[i] = (rxBuffer[6+i*8] << 4)|((rxBuffer[7+i*8] >> 4));
    }

    ltc_slave_control.eeprom_read_address_last_used = ltc_slave_control.eeprom_read_address_to_use;
    ltc_slave_control.eeprom_read_address_to_use = 0xFFFFFFFF;

    DB_WriteBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);
}



/**
 * @brief   sends data to the LTC daisy-chain to write EEPROM on slaves.
 *
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 * @param        step                           first or second stage of read process (0 or 1)
 * @param        address                        read address (18 bits)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_SendEEPROMWriteCommand(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t step) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    /* send WRCOMM to send I2C message to write EEPROM */
    LTC_SetEEPROMWriteCommand(step, DataBufferSPI_TX);
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}



/**
 * @brief   configures the data that will be sent to the LTC daisy-chain to write EEPROM on slaves.
 *
 * @param   step                   first or second stage of read process (0 or 1)
 * @param   *DataBufferSPI_TX      data to be sent to the daisy-chain
 * @param   address                read address (18 bits)
 *
 */
static void LTC_SetEEPROMWriteCommand(uint8_t step, uint8_t *DataBufferSPI_TX) {
    uint16_t i = 0;
    uint32_t address = 0;
    uint8_t data = 0;
    uint8_t address0 = 0;
    uint8_t address1 = 0;
    uint8_t address2 = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    address = ltc_slave_control.eeprom_write_address_to_use;

    address &= 0x3FFFF;
    address0 = address>>16;
    address1 = (address&0xFFFF)>>8;
    address2 = address&0xFF;

    if (step == 0) {
        for (i=0; i < LTC_N_LTC; i++) {
            DataBufferSPI_TX[0 + i * 6] = LTC_ICOM_START | (0x0A);        /* 0x6 : LTC6804: ICOM START from Master */
            DataBufferSPI_TX[1 + i * 6] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03) << 5) | 0x00);
            DataBufferSPI_TX[2 + i * 6] = LTC_ICOM_BLANK | (address1 >> 4);
            DataBufferSPI_TX[3 + i * 6] = LTC_FCOM_MASTER_NACK | (address1 << 4);
            DataBufferSPI_TX[4 + i * 6] = LTC_ICOM_BLANK | (address2 >> 4);
            DataBufferSPI_TX[5 + i * 6] = LTC_FCOM_MASTER_NACK | (address2 << 4);
        }

    } else {  /* step == 1 */
        for (i=0; i < LTC_N_LTC; i++) {
            data = ltc_slave_control.eeprom_value_write[i];

            DataBufferSPI_TX[0 + i * 6] = LTC_ICOM_BLANK | (data >> 4);        /* 0x6 : LTC6804: ICOM START from Master */
            DataBufferSPI_TX[1 + i * 6] = LTC_FCOM_MASTER_NACK_STOP | (data << 4);
            DataBufferSPI_TX[2 + i * 6] = LTC_ICOM_NO_TRANSMIT | 0x00;
            DataBufferSPI_TX[3 + i * 6] = LTC_FCOM_MASTER_NACK_STOP | 0x00;
            DataBufferSPI_TX[4 + i * 6] = LTC_ICOM_NO_TRANSMIT | 0x00;
            DataBufferSPI_TX[5 + i * 6] = LTC_FCOM_MASTER_NACK_STOP | 0x00;
        }

        ltc_slave_control.eeprom_write_address_last_used = ltc_slave_control.eeprom_write_address_to_use;
        ltc_slave_control.eeprom_write_address_to_use = 0xFFFFFFFF;

        DB_WriteBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);
    }
}


/**
 * @brief   sends data to the LTC daisy-chain to configure multiplexer channels.
 *
 * This function calls the function LTC_SetMUXChCommand() to set the data.
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 * @param        mux                    multiplexer ID to be configured (0,1,2 or 3)
 * @param        channel                multiplexer channel to be configured (0 to 7)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_SetMuxChannel(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t mux, uint8_t channel) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    /* send WRCOMM to send I2C message to choose channel */
    LTC_SetMUXChCommand(DataBufferSPI_TX, mux, channel);
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}



/**
 * @brief   sends data to the LTC daisy-chain to communicate via I2C
 *
 * This function initiates an I2C signal sent by the LTC6804 on the slave boards
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the EEPROM
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the EEPROM, with PEC (calculated by the function)
 * @param        cmd_daa                        command data to be sent
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static STD_RETURN_TYPE_e LTC_Send_I2C_Command(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC, uint8_t *cmd_data) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    uint16_t i = 0;

    for (i=0; i < BS_NR_OF_MODULES; i++) {
        DataBufferSPI_TX[0+i*6] = cmd_data[0];
        DataBufferSPI_TX[1+i*6] = cmd_data[1];

        DataBufferSPI_TX[2+i*6] = cmd_data[2];
        DataBufferSPI_TX[3+i*6] = cmd_data[3];

        DataBufferSPI_TX[4+i*6] = cmd_data[4];
        DataBufferSPI_TX[5+i*6] = cmd_data[5];
    }

    /* send WRCOMM to send I2C message */
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}


/**
 * @brief   saves the temperature value of the external temperature sensors read from the LTC daisy-chain.
 *
 * This function saves the temperature value received from the external temperature sensors
 *
 * @param   *rxBuffer      buffer containing the data obtained from the SPI transmission
 *
 */
static void LTC_TempSensSaveTemp(uint8_t *rxBuffer) {
    uint16_t i = 0;
    uint8_t temp_tmp[2];
    uint16_t val_i = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    for (i=0; i < LTC_N_LTC; i++) {
        temp_tmp[0] = (rxBuffer[6+i*8] << 4) | ((rxBuffer[7+i*8] >> 4));
        temp_tmp[1] = (rxBuffer[8+i*8] << 4) | ((rxBuffer[9+i*8] >> 4));
        val_i = (temp_tmp[0] << 8) | (temp_tmp[1]);
        val_i = val_i>>8;
        ltc_slave_control.external_sensor_temperature[i] = val_i;
    }

    DB_WriteBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);
}

/**
 * @brief   sends data to the LTC daisy-chain to control the user port expander
 *
 * This function sends a control byte to the register of the user port expander
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_SetPortExpander(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    uint16_t i = 0;
    uint8_t output_data = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    for (i=0; i < BS_NR_OF_MODULES; i++) {
        output_data = ltc_slave_control.io_value_out[BS_NR_OF_MODULES-1-i];

        DataBufferSPI_TX[0+i*6] = LTC_ICOM_START | 0x04;     /* 6: ICOM0 start condition, 4: upper nibble of PCA8574 address */
        DataBufferSPI_TX[1+i*6] = 0 | LTC_FCOM_MASTER_NACK;  /* 0: lower nibble of PCA8574 address + R/W bit, 8: FCOM0 master NACK */

        DataBufferSPI_TX[2+i*6] = LTC_ICOM_BLANK | (output_data>>4);  /* 0: ICOM1 blank, x: upper nibble of PCA8574 data register (0 == pin low) */
        DataBufferSPI_TX[3+i*6] = (uint8_t)(output_data << 4) | LTC_FCOM_MASTER_NACK_STOP;  /* x: lower nibble of PCA8574 data register, 9: FCOM1 master NACK + STOP */

        DataBufferSPI_TX[4+i*6] = LTC_ICOM_NO_TRANSMIT;  /* 7: no transmission, F: dummy data */
        DataBufferSPI_TX[5+i*6] = 0;  /* F: dummy data, 9: FCOM2 master NACK + STOP */
    }

    /* send WRCOMM to send I2C message */
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}

/**
 * @brief   saves the received values of the external port expander read from the LTC daisy-chain.
 *
 * This function saves the received data byte from the external port expander
 *
 * @param   *rxBuffer      buffer containing the data obtained from the SPI transmission
 *
 */
static void LTC_PortExpanderSaveValues(uint8_t *rxBuffer) {
    uint16_t i = 0;
    uint8_t val_i;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    /* extract data */
    for (i=0; i < LTC_N_LTC; i++) {
        val_i = (rxBuffer[6+i*8] << 4) | ((rxBuffer[7+i*8] >> 4));
        ltc_slave_control.io_value_in[i] = val_i;
    }

    DB_WriteBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);
}


/**
 * @brief   sends data to the LTC daisy-chain to control the user port expander from TI
 *
 * This function sends a control byte to the register of the user port expander from TI
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_SetPortExpanderDirection_TI(LTC_PORT_EXPANDER_TI_DIRECTION_e direction, uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    uint16_t i = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    for (i=0; i < BS_NR_OF_MODULES; i++) {
        DataBufferSPI_TX[0+i*6] = LTC_ICOM_START | 0x4;     /*upper nibble of TCA6408A address */
        DataBufferSPI_TX[1+i*6] = (uint8_t)( ( LTC_PORTEXPANDER_ADR_TI << 1) << 4) | LTC_FCOM_MASTER_NACK;  /* 0: lower nibble of TCA6408A address + R/W bit */

        DataBufferSPI_TX[2+i*6] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR >> 4);  /* upper nibble of TCA6408A configuration register address */
        DataBufferSPI_TX[3+i*6] = (uint8_t)(LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR << 4) | LTC_FCOM_MASTER_NACK;  /* lower nibble of TCA6408A configuration register address */

        DataBufferSPI_TX[4+i*6] = LTC_ICOM_BLANK | (direction >> 4);  /* upper nibble of TCA6408A configuration register data */
        DataBufferSPI_TX[5+i*6] = (uint8_t)(direction << 4) | LTC_FCOM_MASTER_NACK_STOP;  /* lower nibble of TCA6408A configuration register data */
    }

    /* send WRCOMM to send I2C message */
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}


/**
 * @brief   sends data to the LTC daisy-chain to control the user port expander from TI
 *
 * This function sends a control byte to the register of the user port expander from TI
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_SetPortExpander_Output_TI(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    uint16_t i = 0;
    uint8_t output_data = 0;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    for (i=0; i < BS_NR_OF_MODULES; i++) {
        output_data = ltc_slave_control.io_value_out[BS_NR_OF_MODULES-1-i];

        DataBufferSPI_TX[0+i*6] = LTC_ICOM_START | 0x4;     /* upper nibble of TCA6408A address */
        DataBufferSPI_TX[1+i*6] = (uint8_t)( ( LTC_PORTEXPANDER_ADR_TI << 1) << 4) | LTC_FCOM_MASTER_NACK;  /* 0: lower nibble of TCA6408A address + R/W bit */

        DataBufferSPI_TX[2+i*6] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR >> 4);  /* upper nibble of TCA6408A output register address */
        DataBufferSPI_TX[3+i*6] = (uint8_t)(LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR << 4) | LTC_FCOM_MASTER_NACK;  /* lower nibble of TCA6408A output register address */

        DataBufferSPI_TX[4+i*6] = LTC_ICOM_BLANK | (output_data >> 4);  /* upper nibble of TCA6408A output register */
        DataBufferSPI_TX[5+i*6] = (uint8_t)(output_data << 4) | LTC_FCOM_MASTER_NACK_STOP;  /* lower nibble of TCA6408A output register */
    }

    /* send WRCOMM to send I2C message */
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}

/**
 * @brief   sends data to the LTC daisy-chain to control the user port expander from TI
 *
 * This function sends a control byte to the register of the user port expander from TI
 *
 * @param        *DataBufferSPI_TX              data to be sent to the daisy-chain to configure the multiplexer channels
 * @param        *DataBufferSPI_TX_with_PEC     data to be sent to the daisy-chain to configure the multiplexer channels, with PEC (calculated by the function)
 *
 * @return       E_OK if SPI transmission is OK, E_NOT_OK otherwise
 */
static uint8_t LTC_GetPortExpander_Input_TI(uint8_t step, uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC) {
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    uint16_t i = 0;

    if (step == 0) {
        for (i=0; i < BS_NR_OF_MODULES; i++) {
            DataBufferSPI_TX[0+i*6] = LTC_ICOM_START | 0x4;     /* upper nibble of TCA6408A address */
            DataBufferSPI_TX[1+i*6] = (uint8_t)( ( LTC_PORTEXPANDER_ADR_TI << 1) << 4) | LTC_FCOM_MASTER_NACK;  /* lower nibble of TCA6408A address + R/W bit */

            DataBufferSPI_TX[2+i*6] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_INPUT_REG_ADR >> 4);  /* upper nibble of TCA6408A input register address */
            DataBufferSPI_TX[3+i*6] = (uint8_t)(LTC_PORT_EXPANDER_TI_INPUT_REG_ADR << 4) | LTC_FCOM_MASTER_NACK;  /* x: lower nibble of TCA6408A input register address */

            DataBufferSPI_TX[4+i*6] = LTC_ICOM_NO_TRANSMIT;  /* no transmission */
            DataBufferSPI_TX[5+i*6] = 0;  /* dummy data */
        }

    } else {
        DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

        for (i=0; i < BS_NR_OF_MODULES; i++) {
            DataBufferSPI_TX[0+i*6] = LTC_ICOM_START | 0x4;     /* upper nibble of TCA6408A address */
            DataBufferSPI_TX[1+i*6] = (uint8_t)( ( (LTC_PORTEXPANDER_ADR_TI << 1) | 1) << 4) | LTC_FCOM_MASTER_NACK;  /* lower nibble of TCA6408A address + R/W bit */

            DataBufferSPI_TX[2+i*6] = LTC_ICOM_BLANK | 0x0F;  /* upper nibble slave data, master pulls bus high */
            DataBufferSPI_TX[3+i*6] = LTC_FCOM_MASTER_NACK  | 0xF0;  /* lower nibble slave data, master pulls bus high */

            DataBufferSPI_TX[4+i*6] = LTC_ICOM_NO_TRANSMIT;  /* no transmission */
            DataBufferSPI_TX[5+i*6] = 0;  /* dummy data */
        }
    }

    /* send WRCOMM to send I2C message */
    statusSPI = LTC_TX((uint8_t*)ltc_cmdWRCOMM, DataBufferSPI_TX, DataBufferSPI_TX_with_PEC);

    if (statusSPI != E_OK) {
        return E_NOT_OK;
    } else {
        return E_OK;
    }
}

/**
 * @brief   saves the received values of the external port expander from TI read from the LTC daisy-chain.
 *
 * This function saves the received data byte from the external port expander from TI
 *
 * @param   *rxBuffer      buffer containing the data obtained from the SPI transmission
 *
 */
static void LTC_PortExpanderSaveValues_TI(uint8_t *rxBuffer) {
    uint16_t i = 0;
    uint8_t val_i;

    DB_ReadBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);

    /* extract data */
    for (i=0; i < LTC_N_LTC; i++) {
        val_i = (rxBuffer[6+i*8] << 4) | ((rxBuffer[7+i*8] >> 4));
        ltc_slave_control.io_value_in[i] = val_i;
    }

    DB_WriteBlock(&ltc_slave_control, DATA_BLOCK_ID_SLAVE_CONTROL);
}

/**
 * @brief   sends 72 clock pulses to the LTC daisy-chain.
 *
 * This function is used for the communication with the multiplexers via I2C on the GPIOs.
 * It send the command STCOMM to the LTC daisy-chain.
 *
 * @param   *DataBufferSPI_TX           data to be sent to the daisy-chain, set to 0xFF
 * @param   *DataBufferSPI_TX_with_PEC  data to be sent to the daisy-chain  with PEC (calculated by the function)
 *
 * @return  statusSPI                   E_OK if clock pulses were sent correctly by SPI, E_NOT_OK otherwise
 *
 */
static STD_RETURN_TYPE_e LTC_I2CClock(uint8_t *DataBufferSPI_TX, uint8_t *DataBufferSPI_TX_with_PEC) {
    uint16_t i = 0;
    STD_RETURN_TYPE_e statusSPI = E_NOT_OK;

    for (i=0; i < 4+9; i++) {
        DataBufferSPI_TX_with_PEC[i] = 0xFF;
    }

    DataBufferSPI_TX_with_PEC[0] = ltc_cmdSTCOMM[0];
    DataBufferSPI_TX_with_PEC[1] = ltc_cmdSTCOMM[1];
    DataBufferSPI_TX_with_PEC[2] = ltc_cmdSTCOMM[2];
    DataBufferSPI_TX_with_PEC[3] = ltc_cmdSTCOMM[3];

    statusSPI = LTC_SendI2CCmd(DataBufferSPI_TX_with_PEC);

    return statusSPI;
}



/**
 * @brief   gets the frequency of the SPI clock.
 *
 * This function reads the configuration from the SPI handle directly.
 *
 * @return    frequency of the SPI clock
 */
static uint32_t LTC_GetSPIClock(void) {
    uint32_t SPI_Clock = 0;

    if (LTC_SPI_INSTANCE == SPI2 || LTC_SPI_INSTANCE == SPI3) {
        /* SPI2 and SPI3 are connected to APB1 (PCLK1) */
        /* The prescaler setup bits LTC_SPI_PRESCALER corresponds to the bits 5:3 in the SPI_CR1 register */
        /* Reference manual p.909 */
        /* The shift by 3 puts the bits 5:3 to the first position */
        /* Division are made by powers of 2 which corresponds to shifting to the right */
        /* Then 0 corresponds to divide by 2, 1 corresponds to divide by 4... so 1 has to be added to the value of the configuration bits */

        SPI_Clock = HAL_RCC_GetPCLK1Freq()>>((LTC_SPI_PRESCALER>>3)+1);
    }

    if (LTC_SPI_INSTANCE == SPI1 || LTC_SPI_INSTANCE == SPI4 || LTC_SPI_INSTANCE == SPI5 || LTC_SPI_INSTANCE == SPI6) {
        /* SPI1, SPI4, SPI5 and SPI6 are connected to APB2 (PCLK2) */
        /* The prescaler setup bits LTC_SPI_PRESCALER corresponds to the bits 5:3 in the SPI_CR1 register */
        /* Reference manual p.909 */
        /* The shift by 3 puts the bits 5:3 to the first position */
        /* Division are made by powers of 2 which corresponds to shifting to the right */
        /* Then 0 corresponds to divide by 2, 1 corresponds to divide by 4... so 1 has to be added to the value of the configuration bits */

        SPI_Clock = HAL_RCC_GetPCLK2Freq()>>((LTC_SPI_PRESCALER>>3)+1);
    }

    return SPI_Clock;
}




/**
 * @brief   sets the transfer time needed to receive/send data with the LTC daisy-chain.
 *
 * This function gets the clock frequency and uses the number of LTCs in the daisy-chain.
 *
 */
static void LTC_SetTransferTimes(void) {
    uint32_t transferTime_us = 0;
    uint32_t SPI_Clock = 0;

    SPI_Clock = LTC_GetSPIClock();

    /* Transmission of a command and data */
    /* Multiplication by 1000*1000 to get us */
    transferTime_us = (8*1000*1000)/(SPI_Clock);
    transferTime_us *= LTC_N_BYTES_FOR_DATA_TRANSMISSION;
    transferTime_us = transferTime_us + SPI_WAKEUP_WAIT_TIME;
    ltc_state.commandDataTransferTime = (transferTime_us/1000)+1;

    /* Transmission of a command */
    /* Multiplication by 1000*1000 to get us */
    transferTime_us = ((4)*8*1000*1000)/(SPI_Clock);
    transferTime_us = transferTime_us + SPI_WAKEUP_WAIT_TIME;
    ltc_state.commandTransferTime = (transferTime_us/1000)+1;

    /* Transmission of a command + 9 clocks */
    /* Multiplication by 1000*1000 to get us */
    transferTime_us = ((4+9)*8*1000*1000)/(SPI_Clock);
    transferTime_us = transferTime_us + SPI_WAKEUP_WAIT_TIME;
    ltc_state.gpioClocksTransferTime = (transferTime_us/1000)+1;
}



/**
 * @brief   checks the state requests that are made.
 *
 * This function checks the validity of the state requests.
 * The resuls of the checked is returned immediately.
 *
 * @param   statereq    state request to be checked
 *
 * @return              result of the state request that was made, taken from LTC_RETURN_TYPE_e
 */
static LTC_RETURN_TYPE_e LTC_CheckStateRequest(LTC_STATE_REQUEST_e statereq) {
    if (ltc_state.statereq == LTC_STATE_NO_REQUEST) {
        /* init only allowed from the uninitialized state */
        if (statereq == LTC_STATE_INIT_REQUEST) {
            if (ltc_state.state == LTC_STATEMACH_UNINITIALIZED) {
                return LTC_OK;
            } else {
                return LTC_ALREADY_INITIALIZED;
            }
        }

        return LTC_OK;

    } else {
        return LTC_REQUEST_PENDING;
    }
}

static STD_RETURN_TYPE_e LTC_TimerElapsedAndSPITransmitOngoing(uint16_t timer) {
    STD_RETURN_TYPE_e retVal = E_NOT_OK;
    if (timer == 0 && SPI_IsTransmitOngoing() == TRUE) {
        retVal = E_OK;
    }
    return retVal;
}



/**
 * @brief   gets the measurement initialization status.
 *
 * @return  retval  TRUE if a first measurement cycle was made, FALSE otherwise
 *
 */
extern uint8_t LTC_IsFirstMeasurementCycleFinished(void) {
    uint8_t retval = FALSE;

    OS_TaskEnter_Critical();
    retval    = ltc_state.first_measurement_made;
    OS_TaskExit_Critical();

    return (retval);
}

/**
 * @brief   sets the measurement initialization status.
 *
 * @return  none
 *
 */
extern void LTC_SetFirstMeasurementCycleFinished(void) {
    OS_TaskEnter_Critical();
    ltc_state.first_measurement_made = TRUE;
    OS_TaskExit_Critical();
}



/**
 * @brief   gets the measurement initialization status.
 *
 * @return  retval  TRUE if a first measurement cycle was made, FALSE otherwise
 *
 */
extern STD_RETURN_TYPE_e LTC_GetMuxSequenceState(void) {
    STD_RETURN_TYPE_e retval = FALSE;

    retval    = ltc_state.ltc_muxcycle_finished;

    return (retval);
}

ltc.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    ltc.h
 * @author  foxBMS Team
 * @date    01.09.2015 (date of creation)
 * @ingroup DRIVERS
 * @prefix  LTC
 *
 * @brief   Headers for the driver for the LTC monitoring chip.
 *
 */

#ifndef LTC_H_
#define LTC_H_

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

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

#if SLAVE_BOARD_VERSION == 1
#elif SLAVE_BOARD_VERSION == 2
#else
#error Please select the slave board version you want to use. Configuration file: \src\module\config\ltc_cfg.h
#endif

typedef struct {
    uint8_t PEC_valid;  /*!<    */
    uint8_t mux0;       /*!<    */
    uint8_t mux1;       /*!<    */
    uint8_t mux2;       /*!<    */
    uint8_t mux3;       /*!<    */
} LTC_ERRORTABLE_s;

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

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

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

/**
 * @brief   sets the current state request of the state variable ltc_state.
 *
 * 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 LTC_CheckStateRequest() to check if the request is valid.
 * The state request is rejected if is not valid.
 * The result of the check is returned immediately, so that the requester can act in case
 * it made a non-valid state request.
 *
 * @param   statereq                state request to set
 * @param   adcModereq              LTC ADCmeasurement mode (fast, normal or filtered)
 * @param   adcMeasChreq            number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
 * @param   numberOfMeasuredMux     number of  multiplexer inputs measured between two cell voltage measurements
 *
 * @return  retVal                  current state request, taken from LTC_STATE_REQUEST_e
 */
extern LTC_RETURN_TYPE_e LTC_SetStateRequest(LTC_STATE_REQUEST_e statereq);

extern void LTC_SetFirstMeasurementCycleFinished(void);
extern uint8_t LTC_IsFirstMeasurementCycleFinished(void);
extern STD_RETURN_TYPE_e LTC_GetMuxSequenceState(void);

extern void LTC_SaveAllGPIOs(void);
extern void LTC_SaveVoltages(void);
extern void LTC_SaveTemperatures(void);
extern void LTC_SaveAllGPIOMeasurement(void);
extern LTC_STATE_REQUEST_e LTC_GetStateRequest(void);
extern LTC_STATEMACH_e LTC_GetState(void);

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

#endif /* LTC_H_ */

ltc_defs.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    ltc_defs.h
 * @author  foxBMS Team
 * @date    01.09.2015 (date of creation)
 * @ingroup DRIVERS
 * @prefix  LTC
 *
 * @brief   Headers for the driver for the LTC monitoring chip.
 *
 */

#ifndef LTC_DEFS_H_
#define LTC_DEFS_H_

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

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


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

/**
 * Voltage measurement mode
 * 1: 01 27kHz Mode (Fast) 14kHz Mode
 * 2: 10 7kHz Mode (Normal) 3kHz Mode
 * 3: 11 26Hz Mode (Filtered) 2kHz Mode
 */
typedef enum {
    LTC_ADCMODE_UNDEFINED       = 0,    /*!< ADC measurement mode is not defined        */
    LTC_ADCMODE_FAST_DCP0       = 1,    /*!< ADC measurement mode: Fast with DCP0       */
    LTC_ADCMODE_NORMAL_DCP0     = 2,    /*!< ADC measurement mode: Normal with DCP0     */
    LTC_ADCMODE_FILTERED_DCP0   = 3,    /*!< ADC measurement mode: Filtered with DCP0   */
    LTC_ADCMODE_FAST_DCP1       = 4,    /*!< ADC measurement mode: Fast with DCP1       */
    LTC_ADCMODE_NORMAL_DCP1     = 5,    /*!< ADC measurement mode: Normal with DCP1     */
    LTC_ADCMODE_FILTERED_DCP1   = 6,    /*!< ADC measurement mode: Filtered with DCP1   */
} LTC_ADCMODE_e;

/**
 * Number of measured channels
 */
typedef enum {
    LTC_ADCMEAS_UNDEFINED               = 0,    /*!< not defined                            */
    LTC_ADCMEAS_ALLCHANNEL              = 1,    /*!< all ADC channels are measured          */
    LTC_ADCMEAS_SINGLECHANNEL_GPIO1     = 2,    /*!< only a single ADC channel (GPIO1) is measured  */
    LTC_ADCMEAS_SINGLECHANNEL_GPIO2     = 3,    /*!< only a single ADC channel (GPIO2) is measured  */
    LTC_ADCMEAS_SINGLECHANNEL_GPIO3     = 4,    /*!< only a single ADC channel (GPIO3) is measured  */
    LTC_ADCMEAS_SINGLECHANNEL_GPIO4     = 5,    /*!< only a single ADC channel (GPIO4) is measured  */
    LTC_ADCMEAS_SINGLECHANNEL_GPIO5     = 6,    /*!< only a single ADC channel (GPIO5) is measured  */
    LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS  = 7,    /*!< only two cell voltages are measured */
    LTC_ADCMEAS_ALLCHANNEL_SC           = 8,    /*!< all ADC channels + sum of cells are measured   */
} LTC_ADCMEAS_CHAN_e;

/**
 * States of the LTC state machine
 */
typedef enum {
    /* Init-Sequence */
    LTC_STATEMACH_UNINITIALIZED             = 0,    /*!<    */
    LTC_STATEMACH_INITIALIZATION            = 1,    /*!<    */
    LTC_STATEMACH_REINIT                    = 2,    /*!<    */
    LTC_STATEMACH_INITIALIZED               = 3,    /*!< LTC is initialized                             */
    /* Voltage Measurement Cycle */
    LTC_STATEMACH_IDLE                      = 4,    /*!<    */
    LTC_STATEMACH_STARTMEAS                 = 5,    /*!<    */
    LTC_STATEMACH_READVOLTAGE               = 6,    /*!<    */
    LTC_STATEMACH_MUXMEASUREMENT            = 7,    /*!< Mux (Temperature and Balancing) Measurement    */
    LTC_STATEMACH_MUXMEASUREMENT_FINISHED   = 12,   /*!<    */
    LTC_STATEMACH_BALANCECONTROL            = 14,   /*!<    */
    LTC_STATEMACH_ALLGPIOMEASUREMENT        = 15,   /*!<    */
    LTC_STATEMACH_READALLGPIO               = 16,   /*!<    */
    LTC_STATEMACH_READVOLTAGE_2CELLS        = 17,
    LTC_STATEMACH_STARTMEAS_2CELLS          = 18,
    LTC_STATEMACH_USER_IO_CONTROL           = 19,   /*!< Control of the user port expander              */
    LTC_STATEMACH_USER_IO_FEEDBACK          = 20,   /*!< Control of the user port expander              */
    LTC_STATEMACH_EEPROM_READ               = 21,   /*!< Control of the external EEPROM                 */
    LTC_STATEMACH_EEPROM_WRITE              = 22,   /*!< Control of the external EEPROM                 */
    LTC_STATEMACH_TEMP_SENS_READ            = 23,   /*!< Control of the external temperature sensor     */
    LTC_STATEMACH_BALANCEFEEDBACK           = 24,
    LTC_STATEMACH_OPENWIRE_CHECK            = 25,
    LTC_STATEMACH_DEVICE_PARAMETER          = 26,
    LTC_STATEMACH_ADC_ACCURACY              = 27,
    LTC_STATEMACH_DIGITAL_FILTER            = 28,
    LTC_STATEMACH_VOLTMEAS_SUMOFCELLS       = 29,
    LTC_STATEMACH_EEPROM_READ_UID           = 30,   /*!< Control of the external EEPROM                 */
    LTC_STATEMACH_USER_IO_CONTROL_TI        = 32,   /*!< Control of the user port expander              */
    LTC_STATEMACH_USER_IO_FEEDBACK_TI       = 33,   /*!< Control of the user port expander              */
    LTC_STATEMACH_UNDEFINED                 = 34,   /*!< undefined state                                */
    LTC_STATEMACH_RESERVED1                 = 0x80, /*!< reserved state                                 */
    LTC_STATEMACH_ERROR_SPIFAILED           = 0xF0, /*!< Error-State: SPI error                         */
    LTC_STATEMACH_ERROR_PECFAILED           = 0xF1, /*!< Error-State: PEC error                         */
    LTC_STATEMACH_ERROR_MUXFAILED           = 0xF2, /*!< Error-State: multiplexer error                 */
    LTC_STATEMACH_ERROR_INITIALIZATION      = 0xF3, /*!< Error-State: initialization error              */
} LTC_STATEMACH_e;

/**
 * General substates
 */
typedef enum {
    LTC_ENTRY               = 0,    /*!< Substate entry state       */
    LTC_ERROR_ENTRY         = 1,    /*!< Substate entry error       */
    LTC_ERROR_PROCESSED     = 2,    /*!< Substate error processed   */
} LTC_STATEMACH_SUB_e;

/**
 * Substates for the uninitialized state
 */
typedef enum {
    LTC_ENTRY_UNINITIALIZED     = 0,    /*!< Initialize-sequence */
} LTC_STATEMACH_UNINITIALIZED_SUB_e;

/**
 * Substates for the initialization state
 */
typedef enum {
    /* Init-Sequence */
    LTC_ENTRY_INITIALIZATION            = 0,    /*!<    */
    LTC_START_INIT_INITIALIZATION       = 1,    /*!<    */
    LTC_RE_ENTRY_INITIALIZATION         = 2,    /*!<    */
    LTC_READ_INITIALIZATION_REGISTER    = 3,    /*!<    */
    LTC_EXIT_INITIALIZATION             = 4,    /*!<    */
} LTC_STATEMACH_INITIALIZATION_SUB_e;

/**
 * Substates for the uninitialized state
 */
typedef enum {
    LTC_ENTRY_INITIALIZED   = 0,    /*!<    */
} LTC_STATEMACH_INITIALIZED_SUB_e;

/**
 * Substates for the read voltage state
 */
typedef enum {
    /* Init-Sequence */
    LTC_READ_VOLTAGE_REGISTER_A_RDCVA_READVOLTAGE   = 0,    /*!<    */
    LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE   = 1,    /*!<    */
    LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE   = 2,    /*!<    */
    LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE   = 3,    /*!<    */
    LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE   = 4,    /*!<    */
    LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE   = 5,    /*!<    */
    LTC_EXIT_READVOLTAGE                            = 6,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_A_RDAUXA  = 7,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_B_RDAUXB  = 8,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_C_RDAUXC  = 9,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_D_RDAUXD  = 10,   /*!<    */
    LTC_EXIT_READAUXILIARY_ALLGPIOS                   = 11,   /*!<    */
} LTC_STATEMACH_READVOLTAGE_SUB_e;

/**
 * Substates for the cell voltage + SC measurement state
 */
typedef enum {
    LTC_VOLTMEAS_SC_TRIGGER_CONVERSION  = 0,
    LTC_VOLTMEAS_SC_READ_SC             = 1,
    LTC_EXIT_VOLTMEAS_SC                = 2,
}LTC_STATEMACH_READVOLTAGE_SC_SUB_e;

/**
 * Substates for the balance control state
 */
typedef enum {
    /* Init-Sequence */
    LTC_CONFIG_BALANCECONTROL           = 0,    /*!<    */
    LTC_CONFIG2_BALANCECONTROL          = 1,    /*!<    */
    LTC_CONFIG2_BALANCECONTROL_END      = 2,    /*!<    */
    LTC_REQUEST_FEEDBACK_BALANCECONTROL = 3,    /*!<    */
    LTC_READ_FEEDBACK_BALANCECONTROL    = 4,    /*!<    */
    LTC_SAVE_FEEDBACK_BALANCECONTROL    = 5,    /*!<    */
    LTC_EXIT_BALANCECONTROL             = 6,    /*!<    */
    LTC_STATEMACH_STARTMUXMEASUREMENT   = 7,
    LTC_STATEMACH_MUXCONFIGURATION_INIT = 8,    /*!<    */
    LTC_STATEMACH_MUXMEASUREMENT_CONFIG = 9,    /*!< Configuration of the multiplexers              */
    LTC_STATEMACH_READMUXMEASUREMENT    = 11,   /*!<    */
    LTC_STATEMACH_STOREMUXMEASUREMENT   = 12,   /*!<    */
} LTC_STATEMACH_BALANCECONTROL_SUB;

/**
 * Substates for open-wire check
 */
typedef enum {
    LTC_REQUEST_PULLUP_CURRENT_OPENWIRE_CHECK   = 0,    /*!<    */
    LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK     = 1,    /*!<    */
    LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK = 2,    /*!<    */
    LTC_READ_VOLTAGES_PULLDOWN_OPENWIRE_CHECK   = 3,    /*!<    */
    LTC_PERFORM_OPENWIRE_CHECK                  = 4
}LTC_STATEMACH_OPENWIRECHECK_SUB;

/**
 * Substates for diagnosis state
 */
typedef enum {
    LTC_REQUEST_DEVICE_PARAMETER                        = 0,    /*!<    */
    LTC_DEVICE_PARAMETER_READ_STATUS_REGISTER_A         = 1,
    LTC_DEVICE_PARAMETER_READ_STATUS_REGISTER_B         = 2,
    LTC_REQUEST_REDUNDANCY_CHECK                        = 3,
    LTC_REDUNDANCY_READ_STATUS_REGISTER_A               = 4,
    LTC_REDUNDANCY_READ_STATUS_REGISTER_B               = 5,
    LTC_EXIT_DEVICE_PARAMETER                           = 6,
} LTC_STATEMACH_DIAGNOSIS_SUB_e;


/**
 * Substates for ADC accuracy check
 */
typedef enum {
    LTC_REQUEST_ADC1_VERIFICATION      = 0,
    LTC_READ_ADC1_VERIFICATION_VALUE   = 1,
    LTC_SAVE_ADC1_VERIFICATION_VALUE   = 2,
    LTC_REQUEST_ADC2_VERIFICATION      = 3,
    LTC_READ_ADC2_VERIFICATION_VALUE   = 4,
    LTC_EXIT_ADC_ACCURACY_CHECK        = 5,
} LTC_STATEMACH_ADC_ACCURACY_CHECK_SUB_e;


/**
 * Substates for digital filter check
 */
typedef enum {
    LTC_REQUEST_CLEAR_CELLVOLTAGES                  = 0,
    LTC_REQUEST_CLEAR_AUX_REGISTER                  = 1,
    LTC_REQUEST_CLEAR_STATUS_REGISTER               = 2,
    LTC_REQUEST_DIGITAL_FILTER_CHECK_CELLVOLTAGES   = 3,
    LTC_DIGITAL_FILTER_SELF_TEST_1_CELLVOLTAGES     = 4,
    LTC_DIGITAL_FILTER_SELF_TEST_1_AUX              = 5,
    LTC_DIGITAL_FILTER_READ_STATUS_REGISTER_A       = 6,
    LTC_DIGITAL_FILTER_READ_STATUS_REGISTER_B       = 7,
    LTC_DIGITAL_FILTER_SELF_TEST_1_STATUS           = 8,
    LTC_DIGITAL_FILTER_SELF_TEST_2_CELLVOLTAGES     = 9,
    LTC_DIGITAL_FILTER_SELF_TEST_2_AUX              = 10,
    LTC_DIGITAL_FILTER_READ_STATUS_REGISTER_A_2     = 11,
    LTC_DIGITAL_FILTER_READ_STATUS_REGISTER_B_2     = 12,
    LTC_DIGITAL_FILTER_SELF_TEST_2_STATUS           = 13,
    LTC_EXIT_DIGITAL_FILTER_CHECK                   = 14,
}LTC_STATEMACH_DIGITAL_FILTER_SUB_e;


/**
 * Substates for the user IO control state
 */
typedef enum {
    LTC_USER_IO_SET_OUTPUT_REGISTER                                 = 0,    /*!<    */
    LTC_USER_IO_READ_INPUT_REGISTER                                 = 1,    /*!<    */
    LTC_USER_IO_SEND_CLOCK_STCOMM                                   = 2,    /*!<    */
    LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM                 = 3,    /*!<    */
    LTC_USER_IO_SAVE_DATA                                           = 4,    /*!<    */
    LTC_USER_IO_FINISHED                                            = 5,    /*!<    */
} LTC_STATEMACH_USER_IO_CONTROL_e;


/**
 * Substates for the user IO control state, TI port expander
 */
typedef enum {
    LTC_USER_IO_SET_DIRECTION_REGISTER_TI                              = 0,
    LTC_USER_IO_SET_OUTPUT_REGISTER_TI                                 = 1,    /*!<    */
    LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST                           = 2,    /*!<    */
    LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND                          = 3,    /*!<    */
    LTC_USER_IO_SEND_CLOCK_STCOMM_TI                                   = 4,    /*!<    */
    LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI                 = 5,    /*!<    */
    LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_SECOND          = 6,    /*!<    */
    LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_THIRD           = 7,    /*!<    */
    LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM_TI_FOURTH          = 8,    /*!<    */
    LTC_USER_IO_SAVE_DATA_TI                                           = 9,    /*!<    */
    LTC_USER_IO_FINISHED_TI                                            = 10,    /*!<    */
} LTC_STATEMACH_USER_IO_CONTROL_TI_e;

/**
 * Substates for the EEPROM control state
 */
typedef enum {
    LTC_EEPROM_SET_READ_ADDRESS                                     = 0,    /*!<    */
    LTC_EEPROM_READ_DATA1                                           = 1,    /*!<    */
    LTC_EEPROM_READ_DATA2                                           = 2,    /*!<    */
    LTC_EEPROM_WRITE_DATA1                                          = 3,    /*!<    */
    LTC_EEPROM_WRITE_DATA2                                          = 4,    /*!<    */
    LTC_EEPROM_SEND_CLOCK_STCOMM1                                   = 5,    /*!<    */
    LTC_EEPROM_SEND_CLOCK_STCOMM2                                   = 6,    /*!<    */
    LTC_EEPROM_SEND_CLOCK_STCOMM3                                   = 7,    /*!<    */
    LTC_EEPROM_SEND_CLOCK_STCOMM4                                   = 8,    /*!<    */
    LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM                  = 9,    /*!<    */
    LTC_EEPROM_SAVE_READ                                            = 10,    /*!<    */
    LTC_EEPROM_FINISHED                                             = 11,    /*!<    */
} LTC_STATEMACH_EEPROM_READ_UID_e;

/**
 * Substates for the temperature sensor control state
 */
typedef enum {
    LTC_TEMP_SENS_SEND_DATA1                                        = 0,    /*!<    */
    LTC_TEMP_SENS_READ_DATA1                                        = 1,    /*!<    */
    LTC_TEMP_SENS_READ_DATA3                                        = 2,    /*!<    */
    LTC_TEMP_SENS_SEND_CLOCK_STCOMM1                                = 3,    /*!<    */
    LTC_TEMP_SENS_SEND_CLOCK_STCOMM2                                = 4,    /*!<    */
    LTC_TEMP_SENS_READ_I2C_TRANSMISSION_RESULT_RDCOMM               = 5,    /*!<    */
    LTC_TEMP_SENS_SAVE_TEMP                                         = 6,    /*!<    */
    LTC_TEMP_SENS_FINISHED                                          = 7,    /*!<    */
} LTC_STATEMACH_TEMP_SENS_READ_e;

/**
 * Substates for the multiplexer measurement configuration state
 */
typedef enum {
    /* Init-Sequence */
    LTC_SET_MUX_CHANNEL_WRCOMM_MUXMEASUREMENT_CONFIG                = 0,    /*!<    */
    LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG                     = 1,
    LTC_SEND_CLOCK_STCOMM_DIRECTION_CONFIG                          = 2,    /*!<    */
    LTC_SEND_CLOCK_STCOMM_WRITE_IO                                  = 3,    /*!<    */
    LTC_READ_I2C_TRANSMISSION_RESULT_RDCOMM_MUXMEASUREMENT_CONFIG   = 4,    /*!<    */
    LTC_READ_I2C_TRANSMISSION_CHECK_MUXMEASUREMENT_CONFIG           = 5,    /*!<    */
    LTC_START_GPIO_MEASUREMENT_MUXMEASUREMENT_CONFIG                = 6,    /*!<    */
} LTC_STATEMACH_MUXMEASUREMENT_CONFIG_SUB_e;

/**
 * Substates for the all-GPIO multiplexer measurement state
 */
typedef enum {
    /* Init-Sequence */
    LTC_READ_AUXILIARY_REGISTER_A_RAUXA_READALLGPIO = 0,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_B_RAUXB_READALLGPIO = 1,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_C_RAUXC_READALLGPIO = 2,    /*!<    */
    LTC_READ_AUXILIARY_REGISTER_D_RAUXD_READALLGPIO = 3,    /*!<    */
    LTC_EXIT_READALLGPIO                            = 4,    /*!<    */
} LTC_STATEMACH_READALLGPIO_SUB_e;

/**
 * Substates for the single GPIO multiplexer measurement state
 */
typedef enum {
    /* Init-Sequence */
    LTC_READ_AUXILIARY_REGISTER_A_RAUXA_MUXMEASUREMENT  = 0,    /*!<    */
    LTC_SAVE_MUX_MEASUREMENT_MUXMEASUREMENT             = 1,    /*!<    */
} LTC_STATEMACH_MUXMEASUREMENT_SUB_e;

/**
 * State requests for the LTC statemachine
 */
typedef enum {
    LTC_STATE_INIT_REQUEST                = LTC_STATEMACH_INITIALIZATION,           /*!<    */
    LTC_STATE_USER_IO_WRITE_REQUEST       = LTC_STATEMACH_USER_IO_CONTROL,          /*!<    */
    LTC_STATE_USER_IO_READ_REQUEST        = LTC_STATEMACH_USER_IO_FEEDBACK,          /*!<    */
    LTC_STATE_USER_IO_REQUEST             = LTC_STATEMACH_USER_IO_CONTROL,          /*!<    */
    LTC_STATE_USER_IO_WRITE_REQUEST_TI    = LTC_STATEMACH_USER_IO_CONTROL_TI,          /*!<    */
    LTC_STATE_USER_IO_READ_REQUEST_TI     = LTC_STATEMACH_USER_IO_FEEDBACK_TI,          /*!<    */
    LTC_STATE_EEPROM_READ_REQUEST         = LTC_STATEMACH_EEPROM_READ    ,          /*!<    */
    LTC_STATE_EEPROM_WRITE_REQUEST        = LTC_STATEMACH_EEPROM_WRITE    ,          /*!<    */
    LTC_STATE_EEPROM_READ_UID_REQUEST     = LTC_STATEMACH_EEPROM_READ    ,          /*!<    */
    LTC_STATE_TEMP_SENS_READ_REQUEST      = LTC_STATEMACH_TEMP_SENS_READ,           /*!<    */
    LTC_STATE_BALANCEFEEDBACK_REQUEST     = LTC_STATEMACH_BALANCEFEEDBACK,
    LTC_STATE_REINIT_REQUEST              = LTC_STATEMACH_REINIT,                   /*!<    */
    LTC_STATE_IDLE_REQUEST                = LTC_STATEMACH_IDLE,                     /*!<    */
    LTC_STATE_VOLTAGEMEASUREMENT_REQUEST  = LTC_STATEMACH_STARTMEAS,                /*!<    */
    LTC_STATE_VOLTAGEMEASUREMENT_2CELLS_REQUEST = LTC_STATEMACH_STARTMEAS_2CELLS,   /*!<    */
    LTC_STATE_VOLTAGEMEASUREMENT_SC_REQUEST = LTC_STATEMACH_VOLTMEAS_SUMOFCELLS,    /*!<    */
    LTC_STATE_READVOLTAGE_REQUEST         = LTC_STATEMACH_READVOLTAGE,              /*!<    */
    LTC_STATE_READVOLTAGE_2CELLS_REQUEST  = LTC_STATEMACH_READVOLTAGE_2CELLS,
    LTC_STATE_MUXMEASUREMENT_REQUEST      = LTC_STATEMACH_MUXCONFIGURATION_INIT,      /*!<    */
    LTC_STATE_BALANCECONTROL_REQUEST      = LTC_STATEMACH_BALANCECONTROL,           /*!<    */
    LTC_STATEMACH_BALANCEFEEDBACK_REQUEST = LTC_STATEMACH_BALANCEFEEDBACK,
    LTC_STATE_ALLGPIOMEASUREMENT_REQUEST  = LTC_STATEMACH_ALLGPIOMEASUREMENT,       /*!<    */
    LTC_STATE_OPENWIRE_CHECK_REQUEST      = LTC_STATEMACH_OPENWIRE_CHECK,
    LTC_STATEMACH_DEVICE_PARAMETER_REQUEST = LTC_STATEMACH_DEVICE_PARAMETER,
    LTC_STATEMACH_ADC_ACCURACY_REQUEST    = LTC_STATEMACH_ADC_ACCURACY,
    LTC_STATEMACH_DIGITAL_FILTER_REQUEST = LTC_STATEMACH_DIGITAL_FILTER,
    LTC_STATE_NO_REQUEST                  = LTC_STATEMACH_RESERVED1,                /*!<    */
} LTC_STATE_REQUEST_e;

/**
 * Possible return values when state requests are made to the LTC statemachine
 */
typedef enum {
    LTC_OK                  = 0,    /*!< LTC --> ok                             */
    LTC_BUSY_OK             = 1,    /*!< LTC under load --> ok                  */
    LTC_REQUEST_PENDING     = 2,    /*!< requested to be executed               */
    LTC_ILLEGAL_REQUEST     = 3,    /*!< Request can not be executed            */
    LTC_SPI_ERROR           = 4,    /*!< Error state: Source: SPI               */
    LTC_PEC_ERROR           = 5,    /*!< Error state: Source: PEC               */
    LTC_MUX_ERROR           = 6,    /*!< Error state: Source: MUX               */
    LTC_INIT_ERROR          = 7,    /*!< Error state: Source: Initialization    */
    LTC_OK_FROM_ERROR       = 8,    /*!< Return from error --> ok               */
    LTC_ERROR               = 20,   /*!< General error state                    */
    LTC_ALREADY_INITIALIZED = 30,   /*!< Initialization of LTC already finished */
    LTC_ILLEGAL_TASK_TYPE   = 99,   /*!< Illegal                                */
} LTC_RETURN_TYPE_e;

/**
 * Variable to define if the LTC should measure are restart the initialization sequence
 */
typedef enum {
    LTC_HAS_TO_MEASURE  = 0,    /*!< measurement state of the LTC       */
    LTC_HAS_TO_REINIT   = 1,    /*!< re-initialization state of the LTC */
    LTC_HAS_TO_MEASURE_2CELLS = 2, /*!< measurement state of the LTC    */
} LTC_TASK_TYPE_e;


typedef struct {
    uint8_t muxID;  /*!< multiplexer ID 0 - 3       */
    uint8_t muxCh;  /*!< multiplexer channel 0 - 7   */
} LTC_MUX_CH_CFG_s;

typedef struct {
    uint8_t nr_of_steps;        /*!< number of steps in the multiplexer sequence   */
    LTC_MUX_CH_CFG_s *seqptr;   /*!< pointer to the multiplexer sequence   */
} LTC_MUX_SEQUENZ_s;

/**
 * This struct contains pointer to used data buffers
 */
typedef struct {
    DATA_BLOCK_CELLVOLTAGE_s *cellvoltage;
    DATA_BLOCK_CELLTEMPERATURE_s *celltemperature;
    DATA_BLOCK_MINMAX_s *minmax;
    DATA_BLOCK_BALANCING_FEEDBACK_s *balancing_feedback;
    DATA_BLOCK_USER_MUX_s *user_mux;
    DATA_BLOCK_BALANCING_CONTROL_s *balancing_control;
    DATA_BLOCK_SLAVE_CONTROL_s *user_io_control;
    DATA_BLOCK_OPENWIRE_s *openWire_check;       /* Wie genau open wire check behandeln? Was genau abspeichern? */
    DATA_BLOCK_LTC_DEVICE_PARAMETER_s *deviceParameter;
    DATA_BLOCK_LTC_ADC_ACCURACY_s * adcAccuracy;
    int * openWire_buffer;                   /* BS_NR_OF_BAT_CELLS */
    uint8_t * spi_TX_withPEC;                /* 12 byte */
    uint8_t * spi_RX_withPEC;                /* 12 byte */
    uint16_t * GPIOVoltages;                 /* LTC2_NUMBER_OF_GPIOS * NR_OF_LTCs */
    uint16_t * valid_GPIOPECs;               /* NR_OF_LTCs */
} LTC_DATAPTR_s;

/**
 * This struct contains error counter and pointer to used error buffers
 */
typedef struct {
    uint32_t * errPECCnt;       /* array length: Number of used LTCs */
    uint32_t * errSPICnt;       /* array length: Number of used LTCs */
    uint8_t * ltcStatus;        /* array length: Number of used LTCs */
    uint8_t errPECRetryCnt;
    uint8_t errSPIRetryCnt;
    uint8_t errOccurred;
    uint32_t nrOfConsecutiveErrors;
} LTC_ERROR_s;

/**
 * This struct contains the measurement configuration for the LTC
 * Measurement is deactivated with value = LTC_STATE_NO_REQUEST
 */
typedef struct {
    LTC_STATE_REQUEST_e measVoltage;                /* activated = LTC_STATE_VOLTAGEMEASUREMENT_REQUEST */
    LTC_STATE_REQUEST_e measVoltage2Cells;          /* activated = LTC_STATE_VOLTAGEMEASUREMENT_2CELLS_REQUEST */
    LTC_STATE_REQUEST_e measVoltageSumofCells;      /* activated = LTC_STATE_VOLTAGEMEASUREMENT_SC_REQUEST */
    LTC_STATE_REQUEST_e measMux;                    /* activated = LTC_STATE_MUXMEASUREMENT_REQUEST */
    LTC_STATE_REQUEST_e balancing;                  /* activated = LTC_STATE_BALANCECONTROL_REQUEST */
    LTC_STATE_REQUEST_e balancing_feedback;         /* activated = LTC_STATE_BALANCEFEEDBACK_REQUEST */
    LTC_STATE_REQUEST_e measAllGPIO;                /* activated = LTC_STATE_ALLGPIOMEASUREMENT_REQUEST */
    LTC_STATE_REQUEST_e userIO;                     /* activated = LTC_STATE_USER_IO_REQUEST */
    LTC_STATE_REQUEST_e readEEPROM;                 /* activated = LTC_STATE_EEPROM_READ_UID_REQUEST */
    LTC_STATE_REQUEST_e measTemperature;            /* activated = LTC_STATE_TEMP_SENS_READ_REQUEST */
    LTC_STATE_REQUEST_e openWireCheck;              /* activated = LTC_STATE_OPENWIRE_CHECK_REQUEST */
    LTC_STATE_REQUEST_e deviceParameterCheck;       /* activated = LTC_STATEMACH_DEVICE_PARAMETER_REQUEST */
    LTC_STATE_REQUEST_e accuracyADCverification;    /* activated = LTC_STATEMACH_ADC_ACCURACY_REQUEST */
    LTC_STATE_REQUEST_e digitalFilterCheck;         /* activated = LTC_STATEMACH_DIGITAL_FILTER_REQUEST */
    uint8_t taskCycleCnt;           /* holds the current state machine index */
    uint8_t numberActiveOfStates;   /* number of active states */
    uint8_t activeStates[12];       /* array holds the different substates that are executed one after another */
                                    /* maximum number of states : 12 */
} LTC_CONFIG_s;

/**
 *
 */
typedef enum {
    LTC_NOT_REUSED = 0,
    LTC_REUSE_READVOLT_FOR_ADOW_PUP = 1,
    LTC_REUSE_READVOLT_FOR_ADOW_PDOWN = 2,
} LTC_REUSE_MODE_e;

/**
 *TI port expander IO direction (input or output)
 *
 */
typedef enum {
    LTC_PORT_EXPANDER_TI_OUTPUT   = 0x0,
    LTC_PORT_EXPANDER_TI_INPUT    = 0xFF,
} LTC_PORT_EXPANDER_TI_DIRECTION_e;

/**
 * This structure contains all the variables relevant for the LTC state machine.
 * The user can get the current state of the LTC 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    */
    LTC_TASK_TYPE_e taskMode;                  /*!< current task of the state machine                                                      */
    LTC_STATE_REQUEST_e statereq;             /*!< current state request made to the state machine                                        */
    LTC_STATEMACH_e state;                    /*!< state of Driver State Machine                                                          */
    uint8_t substate;                         /*!< current substate of the state machine                                                  */
    LTC_STATEMACH_e laststate;                /*!< previous state of the state machine                                                    */
    uint8_t lastsubstate;                     /*!< previous substate of the state machine                                                 */
    uint8_t configuration[6];                 /*!< holds the configuration of the ltc (configuration register)                            */
    LTC_ADCMODE_e adcMode;                    /*!< current LTC ADCmeasurement mode (fast, normal or filtered)                                             */
    LTC_ADCMODE_e voltMeasMode;               /*!< current LTC ADCmeasurement mode (fast, normal or filtered)                                             */
    LTC_ADCMODE_e gpioMeasMode;               /*!< current LTC ADCmeasurement mode (fast, normal or filtered)                                             */
    LTC_ADCMODE_e adcModereq;                 /*!< requested LTC ADCmeasurement mode (fast, normal or filtered)                                           */
    LTC_ADCMEAS_CHAN_e adcMeasCh;             /*!< current number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)       */
    LTC_ADCMEAS_CHAN_e adcMeasChreq;          /*!< requested number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)     */
    uint8_t numberOfMeasuredMux;              /*!< number of multiplexer channels measured by the LTC chip before a voltage measurement is made           */
    uint32_t ErrPECCounter;                   /*!< counts the number of times there was A PEC (CRC) error during communication with LTC*/
    uint8_t ErrRetryCounter;                  /*!< counts how many times the drivers retried to communicate with LTC in case of a communication error*/
    uint32_t ErrRequestCounter;               /*!< counts the number of illegal requests to the LTC state machine */
    uint8_t triggerentry;                     /*!< counter for re-entrance protection (function running flag) */
    uint32_t commandDataTransferTime;         /*!< time needed for sending an instruction to the LTC, followed by data transfer from the LTC   */
    uint32_t commandTransferTime;             /*!< time needed for sending an instruction to the LTC                                           */
    uint32_t gpioClocksTransferTime;          /*!< time needed for sending 72 clock signal to the LTC, used for I2C communication              */
    uint32_t VoltageSampleTime;               /*!< time stamp at which the cell voltage were measured                                          */
    uint32_t muxSampleTime;                   /*!< time stamp at which a multiplexer input was measured                                        */
    LTC_MUX_CH_CFG_s *muxmeas_seqptr;         /*!< pointer to the multiplexer sequence to be measured (contains a list of elements [multiplexer id, multiplexer channels]) (1,-1)...(3,-1),(0,1),...(0,7) */
    LTC_MUX_CH_CFG_s *muxmeas_seqendptr;      /*!< point to the end of the multiplexer sequence; pointer to ending point of sequence */
    uint8_t muxmeas_nr_end;                   /*!< number of multiplexer channels that have to be measured; end number of sequence, where measurement is finished*/
    SPI_HandleType_s *spiHandle;              /*!< pointer to SPI Handle the LTC is connected to                                               */
    LTC_DATAPTR_s ltcData;                    /*!< contains pointer to the local data buffer                                                   */
    uint8_t instanceID;                       /*!< number to distinguish between different ltc states, starting with 0,1,2,3....8              */
    uint8_t nrBatcellsPerModule;              /*!< number of cells per module                                                                  */
    uint8_t busSize;                          /*!< number of connected LTCs to parallel bus network                                            */
    LTC_ERROR_s errStatus;                    /*!< contains pointer to local error buffer and error indicators                                 */
    uint8_t * ltcIDs;                         /*!< array with LTC IDs                                                                          */
    uint8_t cntDeviceRD;                      /*!< current Index of array ltcIDs to determine device ID                                        */
    uint32_t ctrlCallCnt;                     /*!< counts the LTC2_CTRL calls                                                                  */
    uint8_t taskCycleCnt;                     /*!< counts the current task cycle                                                               */
    LTC_REUSE_MODE_e reusageMeasurementMode;  /*!< flag that indicates if currently any state is reused i.e. cell voltage measurement          */
    LTC_CONFIG_s ltcConfig;                   /*!< struct that holds the measurement configuration of the ltc network                          */
    uint8_t first_measurement_made;           /*!< flag that indicates if the first measurement cycle was completed                            */
    STD_RETURN_TYPE_e ltc_muxcycle_finished;  /*!< flag that indictes if the measurement sequence of the multiplexers is finished              */
    STD_RETURN_TYPE_e check_spi_flag;         /*!< indicates if interrupt flag or timer must be considered */
    STD_RETURN_TYPE_e balance_control_done;   /*!< indicates if balance control was done */
    uint8_t resendCommandCounter;             /*!< counter if commandy should be send multiple times e.g. ADOW command */
} LTC_STATE_s;

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


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

#endif /* LTC_DEFS_H_ */

ltc_pec.c

/*!
    LTC6804-1 Multicell Battery Monitor
@verbatim
    The LTC6804 is a 3rd generation multicell battery stack
    monitor that measures up to 12 series connected battery
    cells with a total measurement error of less than 1.2mV. The
    cell measurement range of 0V to 5V makes the LTC6804
    suitable for most battery chemistries. All 12 cell voltages
    can be captured in 290uS, and lower data acquisition rates
    can be selected for high noise reduction.

    Using the LTC6804-1, multiple devices are connected in
    a daisy-chain with one host processor connection for all
    devices.
@endverbatim
REVISION HISTORY
$Revision: 1000 $
$Date: 2013-12-13 

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

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.

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 OWNER 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.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.

Copyright 2013 Linear Technology Corp. (LTC)
***********************************************************/

/*!**********************************************************
 \brief calaculates  and returns the CRC15
  
  @param[in] uint8_t len: the length of the data array being passed to the function
               
  @param[in] uint8_t data[] : the array of data that the PEC will be generated from
  

  @returns The calculated pec15 as an unsigned int
***********************************************************/

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

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

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

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

uint16_t LTC_pec15_calc(uint8_t len, uint8_t *data) {
    uint16_t remainder = 0;
    uint16_t addr = 0;

    remainder = 16;  /* initialize the PEC */
    for (uint8_t i = 0; i < len; i++) {  /* loops for each byte in data array */

        addr = ((remainder>>7)^data[i])&0xff;  /* calculate PEC table address */
        remainder = (remainder<<8)^crc15Table[addr];
    }
    return(remainder*2);  /* The CRC15 has a 0 in the LSB so the remainder must be multiplied by 2 */
}

/*================== Public functions =====================================*/

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



ltc_pec.h

/************************************
REVISION HISTORY
$Revision: 1000 $
$Date: 2013-07-15

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

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.

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 OWNER 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.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.

Copyright 2013 Linear Technology Corp. (LTC)
***********************************************************/

/*
    Pre computed crc15 table used for the LTC6804 PEC calculation

    The code used to generate the crc15 table is:
void generate_crc15_table()
{
  int remainder;
    for (int i = 0; i<256;i++)
    {
        remainder =  i<< 7;
        for (int bit = 8; bit > 0; --bit)
          {

                 if ((remainder & 0x4000) > 0)    equivalent to remainder & 2^14 simply check for MSB
                  {
                        remainder = ((remainder << 1)) ;
                        remainder = (remainder ^ 0x4599);
                 }
                 else
                {
                     remainder = ((remainder << 1));
                }
         }

        crc15Table[i] = remainder&0xFFFF;

    }
}
*/

#ifndef LTC_PEC_H_
#define LTC_PEC_H_


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

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

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

static const unsigned int crc15Table[256] = {  /* precomputed CRC15 Table */
    0x0, 0xc599, 0xceab, 0xb32, 0xd8cf, 0x1d56, 0x1664, 0xd3fd, 0xf407, 0x319e, 0x3aac,
    0xff35, 0x2cc8, 0xe951, 0xe263, 0x27fa, 0xad97, 0x680e, 0x633c, 0xa6a5, 0x7558, 0xb0c1,
    0xbbf3, 0x7e6a, 0x5990, 0x9c09, 0x973b, 0x52a2, 0x815f, 0x44c6, 0x4ff4, 0x8a6d, 0x5b2e,
    0x9eb7, 0x9585, 0x501c, 0x83e1, 0x4678, 0x4d4a, 0x88d3, 0xaf29, 0x6ab0, 0x6182, 0xa41b,
    0x77e6, 0xb27f, 0xb94d, 0x7cd4, 0xf6b9, 0x3320, 0x3812, 0xfd8b, 0x2e76, 0xebef, 0xe0dd,
    0x2544, 0x2be, 0xc727, 0xcc15, 0x98c, 0xda71, 0x1fe8, 0x14da, 0xd143, 0xf3c5, 0x365c,
    0x3d6e, 0xf8f7, 0x2b0a, 0xee93, 0xe5a1, 0x2038, 0x7c2, 0xc25b, 0xc969, 0xcf0, 0xdf0d,
    0x1a94, 0x11a6, 0xd43f, 0x5e52, 0x9bcb, 0x90f9, 0x5560, 0x869d, 0x4304, 0x4836, 0x8daf,
    0xaa55, 0x6fcc, 0x64fe, 0xa167, 0x729a, 0xb703, 0xbc31, 0x79a8, 0xa8eb, 0x6d72, 0x6640,
    0xa3d9, 0x7024, 0xb5bd, 0xbe8f, 0x7b16, 0x5cec, 0x9975, 0x9247, 0x57de, 0x8423, 0x41ba,
    0x4a88, 0x8f11, 0x57c, 0xc0e5, 0xcbd7, 0xe4e, 0xddb3, 0x182a, 0x1318, 0xd681, 0xf17b,
    0x34e2, 0x3fd0, 0xfa49, 0x29b4, 0xec2d, 0xe71f, 0x2286, 0xa213, 0x678a, 0x6cb8, 0xa921,
    0x7adc, 0xbf45, 0xb477, 0x71ee, 0x5614, 0x938d, 0x98bf, 0x5d26, 0x8edb, 0x4b42, 0x4070,
    0x85e9, 0xf84, 0xca1d, 0xc12f, 0x4b6, 0xd74b, 0x12d2, 0x19e0, 0xdc79, 0xfb83, 0x3e1a, 0x3528,
    0xf0b1, 0x234c, 0xe6d5, 0xede7, 0x287e, 0xf93d, 0x3ca4, 0x3796, 0xf20f, 0x21f2, 0xe46b, 0xef59,
    0x2ac0, 0xd3a, 0xc8a3, 0xc391, 0x608, 0xd5f5, 0x106c, 0x1b5e, 0xdec7, 0x54aa, 0x9133, 0x9a01,
    0x5f98, 0x8c65, 0x49fc, 0x42ce, 0x8757, 0xa0ad, 0x6534, 0x6e06, 0xab9f, 0x7862, 0xbdfb, 0xb6c9,
    0x7350, 0x51d6, 0x944f, 0x9f7d, 0x5ae4, 0x8919, 0x4c80, 0x47b2, 0x822b, 0xa5d1, 0x6048, 0x6b7a,
    0xaee3, 0x7d1e, 0xb887, 0xb3b5, 0x762c, 0xfc41, 0x39d8, 0x32ea, 0xf773, 0x248e, 0xe117, 0xea25,
    0x2fbc, 0x846, 0xcddf, 0xc6ed, 0x374, 0xd089, 0x1510, 0x1e22, 0xdbbb, 0xaf8, 0xcf61, 0xc453,
    0x1ca, 0xd237, 0x17ae, 0x1c9c, 0xd905, 0xfeff, 0x3b66, 0x3054, 0xf5cd, 0x2630, 0xe3a9, 0xe89b,
    0x2d02, 0xa76f, 0x62f6, 0x69c4, 0xac5d, 0x7fa0, 0xba39, 0xb10b, 0x7492, 0x5368, 0x96f1, 0x9dc3,
    0x585a, 0x8ba7, 0x4e3e, 0x450c, 0x8095
};


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

/**
 * @brief   calculates the PEC
 *
 * @param   len      Number of bytes that will be used to calculate a PEC
 * @param   data     Array of data that will be used to calculate  a PEC
 *
 * @return  retVal   PEC
 */
uint16_t LTC_pec15_calc(uint8_t len, uint8_t *data);

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

#endif /* LTC_PEC_H_ */

ltc_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    ltc_cfg.c
 * @author  foxBMS Team
 * @date    18.02.2015 (date of creation)
 * @ingroup DRIVERS_CONF
 * @prefix  LTC
 *
 * @brief   Configuration for the LTC monitoring chip
 *
 */

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

#include "epcos_b57861s0103f045.h"

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

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

/**
 * Default multiplexer measurement sequence
 * Must be adapted to the application
 */
LTC_MUX_CH_CFG_s ltc_mux_seq_main_ch1[] = {
    /*  multiplexer 0 measurement */
/*         {*/
/*             .muxID    = 0, */
/*             .muxCh    = 0xFF, */
/*         }, */
    {
        .muxID    = 0,
        .muxCh    = 0,
    },
    {
        .muxID    = 0,
        .muxCh    = 1,
    },
    {
        .muxID    = 0,
        .muxCh    = 2,
    },
    {
        .muxID    = 0,
        .muxCh    = 3,
    },
    {
        .muxID    = 0,
        .muxCh    = 4,
    },
    {
        .muxID    = 0,
        .muxCh    = 5,
    },
    {
        .muxID    = 0,
        .muxCh    = 6,
    },
    {
        .muxID    = 0,
        .muxCh    = 7,
    },
/*     ,
      multiplexer 2 and 3 measurement
    {
        .muxID    = 0,
        .muxCh    = 0xFF,    disable enabled mux
    },
    {
        .muxID    = 1,
        .muxCh    = 0,
    },
    {
        .muxID    = 1,
        .muxCh    = 1,
    },
    {
        .muxID    = 1,
        .muxCh    = 2,
    },
    {
        .muxID    = 1,
        .muxCh    = 3,
    },
    {
        .muxID    = 1,
        .muxCh    = 4,
    },
    {
        .muxID    = 1,
        .muxCh    = 5,
    },
    {
        .muxID    = 1,
        .muxCh    = 6,
    },
    {
        .muxID    = 1,
        .muxCh    = 7,
    },
    {
        .muxID    = 1,
        .muxCh    = 0xFF,         disable enabled mux
    },

    {
        .muxID    = 2,
        .muxCh    = 0,
    },
    {
        .muxID    = 2,
        .muxCh    = 1,
    },
    {
        .muxID    = 2,
        .muxCh    = 2,
    },
    {
        .muxID    = 2,
        .muxCh    = 3,
    },
    {
        .muxID    = 2,
        .muxCh    = 4,
    },
    {
        .muxID    = 2,
        .muxCh    = 5,
    },
    {
        .muxID    = 2,
        .muxCh    = 6,
    },
    {
        .muxID    = 2,
        .muxCh    = 7,
    }*/
};


LTC_MUX_SEQUENZ_s ltc_mux_seq = {
    .seqptr         =  &ltc_mux_seq_main_ch1[0],
    .nr_of_steps    =  (sizeof(ltc_mux_seq_main_ch1)/sizeof(LTC_MUX_CH_CFG_s))
};


const uint8_t ltc_muxsensortemperatur_cfg[BS_NR_OF_TEMP_SENSORS_PER_MODULE] = {
    1-1 ,       /*!< index 0 = mux 0, ch 0 */
    2-1 ,       /*!< index 1 = mux 0, ch 1 */
    3-1 ,       /*!< index 2 = mux 0, ch 2 */
    4-1 ,       /*!< index 3 = mux 0, ch 3 */
    5-1 ,       /*!< index 4 = mux 0, ch 4 */
    6-1 ,       /*!< index 5 = mux 0, ch 5 */
    7-1 ,       /*!< index 6 = mux 0, ch 6 */
    8-1 ,       /*!< index 7 = mux 0, ch 7 */
    /* 9-1 ,      !< index 8 = mux 1, ch 0 */
    /* 10-1 ,     !< index 9 = mux 1, ch 1 */
    /* 11-1 ,     !< index 10 = mux 1, ch 2 */
    /* 12-1 ,     !< index 11 = mux 1, ch 3 */
    /* 13-1 ,     !< index 12 = mux 1, ch 4 */
    /* 14-1 ,     !< index 13 = mux 1, ch 5 */
    /* 15-1 ,     !< index 14 = mux 1, ch 6 */
    /* 16-1       !< index 15 = mux 1, ch 7 */
};


const uint8_t ltc_voltage_input_used[BS_MAX_SUPPORTED_CELLS] = {
#if BS_MAX_SUPPORTED_CELLS == 12 || BS_MAX_SUPPORTED_CELLS == 15 || BS_MAX_SUPPORTED_CELLS == 18
    1,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
#endif
#if BS_MAX_SUPPORTED_CELLS == 15 || BS_MAX_SUPPORTED_CELLS == 18
    1 ,
    1 ,
    1 ,
#endif
#if BS_MAX_SUPPORTED_CELLS == 18
    1 ,
    1 ,
    1 ,
#endif
};


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

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

float LTC_Convert_MuxVoltages_to_Temperatures(float v_adc) {
    float temperature = 0.0;

    /* Example: 5th grade polynomial for EPCOS B57861S0103F045 NTC-Thermistor, 10 kOhm, Series B57861S, Vref = 3V, R in series 10k */
    /* temperature = B57861S0103F045_GetTempFromPolynom(v_adc*1000); */

    /* Dummy function, must be adapted to the application */
    temperature = 10 * v_adc;

    return temperature;
}

ltc_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    ltc_cfg.h
 * @author  foxBMS Team
 * @date    18.02.2015 (date of creation)
 * @ingroup DRIVERS_CONF
 * @prefix  LTC
 *
 * @brief    Headers for the configuration for the LTC monitoring chip.
 *
 */

#ifndef LTC_CFG_H_
#define LTC_CFG_H_

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

#include "batterysystem_cfg.h"
#include "ltc_defs.h"

/*================== Macros and Definitions ===============================*/
/**
 * @ingroup CONFIG_LTC
 * If set to 1 LTC driver is configured to use foxBMS slave boards version 1.x
 * If set to 2 LTC driver is configured to use foxBMS slave boards version 2.x
 */
#define SLAVE_BOARD_VERSION 2

/**
 * If set to 0 LTC driver is configured to use PCA8574 port expander
 * If set to 1 LTC driver is configured to use TCA6408A port expander
 */
#define LTC_PORTEXPANDER_VERSION 1

/**
 * Address of TI port expander (0 or 1)
 */
#define LTC_PORTEXPANDER_ADR_TI 0


/* #define LTC_DISCARD_PEC TRUE */
#define LTC_DISCARD_PEC FALSE

#define LTC_GOTO_MUX_CHECK TRUE
/* #define LTC_GOTO_MUX_CHECK FALSE */

/* #define LTC_DISCARD_MUX_CHECK TRUE */
#define LTC_DISCARD_MUX_CHECK FALSE


/**
 * Number of used LTC-ICs
 */

#define LTC_N_LTC                       BS_NR_OF_MODULES
/**
 * Number of multiplexer used per LTC-IC
 */
#define LTC_N_MUX_PER_LTC               3

/**
 * Number of channels per multiplexer
 */
#define LTC_N_MUX_CHANNELS_PER_MUX      8

/**
 * Number of multiplexer measurements per LTC cycle
 */
#define LTC_NUMBER_OF_MUX_MEASUREMENTS_PER_CYCLE 8

/**
 * Number of multiplexed channels per LTC-IC
 */
#define LTC_N_MUX_CHANNELS_PER_LTC      (LTC_N_MUX_PER_LTC*LTC_N_MUX_CHANNELS_PER_MUX)

/**
 * Number of LTC-ICs per battery module
 */
#define LTC_NUMBER_OF_LTC_PER_MODULE    1

/**
 * Measurement modus for voltages
 */
#define LTC_VOLTAGE_MEASUREMENT_MODE    LTC_ADCMODE_NORMAL_DCP0
/* #define LTC_VOLTAGE_MEASUREMENT_MODE LTC_ADCMODE_FILTERED_DCP0 */
/* #define LTC_VOLTAGE_MEASUREMENT_MODE LTC_ADCMODE_FAST_DCP0 */

/**
 *  Measurement modus for GPIOs
 */
#define LTC_GPIO_MEASUREMENT_MODE   LTC_ADCMODE_NORMAL_DCP0
/* #define LTC_GPIO_MEASUREMENT_MODE LTC_ADCMODE_FILTERED_DCP0 */
/* #define LTC_GPIO_MEASUREMENT_MODE LTC_ADCMODE_FAST_DCP0 */

/**
 * Measurement modus for Open-wire check
 */
#define LTC_OW_MEASUREMENT_MODE     LTC_ADCMODE_NORMAL_DCP0
/* #define LTC_OW_MEASUREMENT_MODE     LTC_ADCMODE_FILTERED_DCP0 */


/**
 * Timeout added to the transmission time for interrupt-
 * based SPI trnamission. TIme in ms.
 */
#define LTC_TRANSMISSION_TIMEOUT      10

/**
 * SPI1 is used for communication with LTC
 */
#define LTC_SPI_HANDLE      &spi_devices[0]

#define LTC_SPI_INSTANCE    *LTC_SPI_HANDLE.Instance

#define LTC_SPI_PRESCALER   *LTC_SPI_HANDLE.Init.BaudRatePrescaler

/**
 * start definition of LTC timings
 * Twake (see LTC datasheet)
 */
#define LTC_TWAKE_US    300
/**
 * start definition of LTC timings
 * Tready (see LTC datasheet)
 */
#define LTC_TREADY_US   10
/**
 * start definition of LTC timings
 * Tidle (see LTC datasheet)
 */
#define LTC_TIDLE_US    6700

/**
 * LTC statemachine short time definition in ms
 */
#define LTC_STATEMACH_SHORTTIME     1

/**
 * time for the first initialization of the daisy chain
 * see LTC6804 datasheet page 41
 */
#define LTC_STATEMACH_DAISY_CHAIN_FIRST_INITIALIZATION_TIME     ((LTC_TWAKE_US*LTC_N_LTC)/1000)
/**
 * time for the second initialization of the daisy chain
 * see LTC6804 datasheet page 41
 */
#define LTC_STATEMACH_DAISY_CHAIN_SECOND_INITIALIZATION_TIME    ((LTC_TREADY_US*LTC_N_LTC)/1000)


/*
 * Timings of Voltage Cell and GPIO measurement for all cells or all GPIO
 */

/**
 * ~1.1ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Fast Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_ALL_FAST_TCYCLE          2

/**
 * ~2.3ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Normal Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_ALL_NORMAL_TCYCLE        3

/**
 * ~201ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Filtered Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_ALL_FILTERED_TCYCLE      202

/*
 * Timings of Voltage Cell and GPIO measurement for a pair of cells or a single GPIO
 */

/**
 *  ~0.201ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Fast Mode
 *  unit: ms
 */
#define LTC_STATEMACH_MEAS_SINGLE_FAST_TCYCLE       1

/**
 * ~0.405ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Normal Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_SINGLE_NORMAL_TCYCLE     1

/**
 * ~34 ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Filtered Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_SINGLE_FILTERED_TCYCLE   35

/**
 * LTC statemachine sequence error timing in ms
 */
#define LTC_STATEMACH_SEQERRTTIME   5
/**
 * LTC statemachine CRC-transmission error timing in ms
 */
#define LTC_STATEMACH_PECERRTIME    1

/**
 * Maximum number of re-tries in case of CRC error during the communication with daisy chain
 * before going into error state
 */
#define LTC_TRANSMIT_PECERRLIMIT    10

/**
 * Maximum number of re-tries in case of SPI error during the communication with daisy chain
 * before going into error state
 */
#define LTC_TRANSMIT_SPIERRLIMIT    3

/**
 * If set to 1, check if multiplexers acknowledged transmission
 */
#define LTC_READCOM     0


/**
 * ------------------- OPEN WIRE CHECK ------------------------
 * If open-wire check is performed cell voltages and temperatures are not
 * updated and thus old values can be transmitted on the CAN bus. Check time
 * is dependent on module configuration and external capacitance. Activate
 * open-wire check with care! See table below for various measured open-wire
 * check durations!
 */

/* #define LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK TRUE */
#define LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK FALSE

/**
 * Periodic open-wire check time in STANDBY state in ms
 */
#define LTC_STANDBY_OPEN_WIRE_PERIOD_ms      600000

/* #define LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK TRUE */
#define LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK FALSE

/**
 * Periodic open-wire check time in NORMAL state in ms
 */
#define LTC_NORMAL_OPEN_WIRE_PERIOD_ms      600000

/* #define LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK TRUE */
#define LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK FALSE

/**
 * Periodic open-wire check time in CHARGE state in ms
 */
#define LTC_CHARGE_OPEN_WIRE_PERIOD_ms      600000

/**
 * Periodic open-wire check time in ERROR state in ms
 */
#define LTC_ERROR_OPEN_WIRE_PERIOD_ms      30000

/**
 * Number of required ADOW commands because of external C-Pin capacitance and
 * the respective duration to perform an open wire check for 14 modules with
 * 12 cells each. During this time no cell voltages and temperatures are measured!
 * +----------------+--------------+---------------+----------+----------+
 * | External C pin | Normal  mode | Filtered mode | Duration | Duration |
 * | capacitance    |              |               |  normal  | filtered |
 * | ---------------+--------------+---------------+----------+----------+
 * |   <= 10nF      |      2       |        2      |    32ms  |   828ms  |
 * |     100nF      |     10       |        2      |   112ms  |   828ms  |
 * |       1uF      |    100       |        2      |  1012ms  |   828ms  |
 * |       C        | 1.5+(C/10nF) |        2      |          |          |
 * +----------------+--------------+---------------+----------+----------+
 */
#define LTC_NMBR_REQ_ADOW_COMMANDS      2

/**
 * Number of Bytes to be transmitted in daisy-chain
 * For first 4 Bytes:
 *  - 2 Bytes: command
 *  - 2 Bytes: CRC
 * Following Bytes: Data
 *  - 6 Bytes data per LTC
 *  - 2 Bytes CRC per LTC
 */
#define LTC_N_BYTES_FOR_DATA_TRANSMISSION   (4+(8*LTC_N_LTC))

/**
 * Number of Bytes to be transmitted in daisy-chain
 * Data
 *  - 6 Bytes data per LTC
 */
#define LTC_N_BYTES_FOR_DATA_TRANSMISSION_DATA_ONLY   (0+(6*LTC_N_LTC))


/* Transmit functions */
#define LTC_SendWakeUp()                SPI_Transmit(LTC_SPI_HANDLE, (uint8_t *) ltc_cmdDummy, 1)
#define LTC_SendI2CCmd(txbuf)           SPI_Transmit(LTC_SPI_HANDLE, txbuf, 4+9)
#define LTC_SendData(txbuf)             SPI_Transmit(LTC_SPI_HANDLE, txbuf, LTC_N_BYTES_FOR_DATA_TRANSMISSION)
#define LTC_SendCmd(command)            SPI_Transmit(LTC_SPI_HANDLE, (uint8_t *) command, 4)
#define LTC_ReceiveData(txbuf, rxbuf)    SPI_TransmitReceive(LTC_SPI_HANDLE, txbuf, rxbuf, LTC_N_BYTES_FOR_DATA_TRANSMISSION)


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

/**
 * Definition of the multiplexer measurement sequence
 */
extern LTC_MUX_SEQUENZ_s ltc_mux_seq;

/**
 * On the foxBMS slave board there are 6 multiplexer inputs dedicated to temperature
 * sensors by default.
 * Lookup table between temperature sensors and battery cells
 */
extern const uint8_t ltc_muxsensortemperatur_cfg[BS_NR_OF_TEMP_SENSORS_PER_MODULE];

/**
 * Lookup table to indicate which voltage inpus are used
 */
extern const uint8_t ltc_voltage_input_used[BS_MAX_SUPPORTED_CELLS];

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

/**
 * @brief   converts a raw voltage from multiplexer to a temperature value in Celsius.
 *
 * The temperatures are read from NTC elements via voltage dividers.
 * This function implements the look-up table between voltage and temperature,
 * taking into account the NTC characteristics and the voltage divider.
 *
 * @param   v_adc            voltage read from the multiplexer in V
 *
 * @return  temperature     temperature value in Celsius
 */
extern float LTC_Convert_MuxVoltages_to_Temperatures(float v_adc);

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

#endif /* LTC_CFG_H_ */

ltc_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    ltc_cfg.c
 * @author  foxBMS Team
 * @date    18.02.2015 (date of creation)
 * @ingroup DRIVERS_CONF
 * @prefix  LTC
 *
 * @brief   Configuration for the LTC monitoring chip
 *
 */

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

#include "epcos_b57861s0103f045.h"

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

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

/**
 * Default multiplexer measurement sequence
 * Must be adapted to the application
 */
LTC_MUX_CH_CFG_s ltc_mux_seq_main_ch1[] = {
    /*  multiplexer 0 measurement */
/*         {*/
/*             .muxID    = 0, */
/*             .muxCh    = 0xFF, */
/*         }, */
    {
        .muxID    = 0,
        .muxCh    = 0,
    },
    {
        .muxID    = 0,
        .muxCh    = 1,
    },
    {
        .muxID    = 0,
        .muxCh    = 2,
    },
    {
        .muxID    = 0,
        .muxCh    = 3,
    },
    {
        .muxID    = 0,
        .muxCh    = 4,
    },
    {
        .muxID    = 0,
        .muxCh    = 5,
    },
    {
        .muxID    = 0,
        .muxCh    = 6,
    },
    {
        .muxID    = 0,
        .muxCh    = 7,
    },
/*     ,
      multiplexer 2 and 3 measurement
    {
        .muxID    = 0,
        .muxCh    = 0xFF,    disable enabled mux
    },
    {
        .muxID    = 1,
        .muxCh    = 0,
    },
    {
        .muxID    = 1,
        .muxCh    = 1,
    },
    {
        .muxID    = 1,
        .muxCh    = 2,
    },
    {
        .muxID    = 1,
        .muxCh    = 3,
    },
    {
        .muxID    = 1,
        .muxCh    = 4,
    },
    {
        .muxID    = 1,
        .muxCh    = 5,
    },
    {
        .muxID    = 1,
        .muxCh    = 6,
    },
    {
        .muxID    = 1,
        .muxCh    = 7,
    },
    {
        .muxID    = 1,
        .muxCh    = 0xFF,         disable enabled mux
    },

    {
        .muxID    = 2,
        .muxCh    = 0,
    },
    {
        .muxID    = 2,
        .muxCh    = 1,
    },
    {
        .muxID    = 2,
        .muxCh    = 2,
    },
    {
        .muxID    = 2,
        .muxCh    = 3,
    },
    {
        .muxID    = 2,
        .muxCh    = 4,
    },
    {
        .muxID    = 2,
        .muxCh    = 5,
    },
    {
        .muxID    = 2,
        .muxCh    = 6,
    },
    {
        .muxID    = 2,
        .muxCh    = 7,
    }*/
};


LTC_MUX_SEQUENZ_s ltc_mux_seq = {
    .seqptr         =  &ltc_mux_seq_main_ch1[0],
    .nr_of_steps    =  (sizeof(ltc_mux_seq_main_ch1)/sizeof(LTC_MUX_CH_CFG_s))
};


const uint8_t ltc_muxsensortemperatur_cfg[BS_NR_OF_TEMP_SENSORS_PER_MODULE] = {
    1-1 ,       /*!< index 0 = mux 0, ch 0 */
    2-1 ,       /*!< index 1 = mux 0, ch 1 */
    3-1 ,       /*!< index 2 = mux 0, ch 2 */
    4-1 ,       /*!< index 3 = mux 0, ch 3 */
    5-1 ,       /*!< index 4 = mux 0, ch 4 */
    6-1 ,       /*!< index 5 = mux 0, ch 5 */
    7-1 ,       /*!< index 6 = mux 0, ch 6 */
    8-1 ,       /*!< index 7 = mux 0, ch 7 */
    /* 9-1 ,      !< index 8 = mux 1, ch 0 */
    /* 10-1 ,     !< index 9 = mux 1, ch 1 */
    /* 11-1 ,     !< index 10 = mux 1, ch 2 */
    /* 12-1 ,     !< index 11 = mux 1, ch 3 */
    /* 13-1 ,     !< index 12 = mux 1, ch 4 */
    /* 14-1 ,     !< index 13 = mux 1, ch 5 */
    /* 15-1 ,     !< index 14 = mux 1, ch 6 */
    /* 16-1       !< index 15 = mux 1, ch 7 */
};


const uint8_t ltc_voltage_input_used[BS_MAX_SUPPORTED_CELLS] = {
#if BS_MAX_SUPPORTED_CELLS == 12 || BS_MAX_SUPPORTED_CELLS == 15 || BS_MAX_SUPPORTED_CELLS == 18
    1,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
    1 ,
#endif
#if BS_MAX_SUPPORTED_CELLS == 15 || BS_MAX_SUPPORTED_CELLS == 18
    1 ,
    1 ,
    1 ,
#endif
#if BS_MAX_SUPPORTED_CELLS == 18
    1 ,
    1 ,
    1 ,
#endif
};


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

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

float LTC_Convert_MuxVoltages_to_Temperatures(float v_adc) {
    float temperature = 0.0;

    /* Example: 5th grade polynomial for EPCOS B57861S0103F045 NTC-Thermistor, 10 kOhm, Series B57861S, Vref = 3V, R in series 10k */
    /* temperature = B57861S0103F045_GetTempFromPolynom(v_adc*1000); */

    /* Dummy function, must be adapted to the application */
    temperature = 10 * v_adc;

    return temperature;
}

ltc_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    ltc_cfg.h
 * @author  foxBMS Team
 * @date    18.02.2015 (date of creation)
 * @ingroup DRIVERS_CONF
 * @prefix  LTC
 *
 * @brief    Headers for the configuration for the LTC monitoring chip.
 *
 */

#ifndef LTC_CFG_H_
#define LTC_CFG_H_

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

#include "batterysystem_cfg.h"
#include "ltc_defs.h"

/*================== Macros and Definitions ===============================*/
/**
 * @ingroup CONFIG_LTC
 * If set to 1 LTC driver is configured to use foxBMS slave boards version 1.x
 * If set to 2 LTC driver is configured to use foxBMS slave boards version 2.x
 */
#define SLAVE_BOARD_VERSION 2

/**
 * If set to 0 LTC driver is configured to use PCA8574 port expander
 * If set to 1 LTC driver is configured to use TCA6408A port expander
 */
#define LTC_PORTEXPANDER_VERSION 1

/**
 * Address of TI port expander (0 or 1)
 */
#define LTC_PORTEXPANDER_ADR_TI 0


/* #define LTC_DISCARD_PEC TRUE */
#define LTC_DISCARD_PEC FALSE

#define LTC_GOTO_MUX_CHECK TRUE
/* #define LTC_GOTO_MUX_CHECK FALSE */

/* #define LTC_DISCARD_MUX_CHECK TRUE */
#define LTC_DISCARD_MUX_CHECK FALSE


/**
 * Number of used LTC-ICs
 */

#define LTC_N_LTC                       BS_NR_OF_MODULES
/**
 * Number of multiplexer used per LTC-IC
 */
#define LTC_N_MUX_PER_LTC               3

/**
 * Number of channels per multiplexer
 */
#define LTC_N_MUX_CHANNELS_PER_MUX      8

/**
 * Number of multiplexer measurements per LTC cycle
 */
#define LTC_NUMBER_OF_MUX_MEASUREMENTS_PER_CYCLE 8

/**
 * Number of multiplexed channels per LTC-IC
 */
#define LTC_N_MUX_CHANNELS_PER_LTC      (LTC_N_MUX_PER_LTC*LTC_N_MUX_CHANNELS_PER_MUX)

/**
 * Number of LTC-ICs per battery module
 */
#define LTC_NUMBER_OF_LTC_PER_MODULE    1

/**
 * Measurement modus for voltages
 */
#define LTC_VOLTAGE_MEASUREMENT_MODE    LTC_ADCMODE_NORMAL_DCP0
/* #define LTC_VOLTAGE_MEASUREMENT_MODE LTC_ADCMODE_FILTERED_DCP0 */
/* #define LTC_VOLTAGE_MEASUREMENT_MODE LTC_ADCMODE_FAST_DCP0 */

/**
 *  Measurement modus for GPIOs
 */
#define LTC_GPIO_MEASUREMENT_MODE   LTC_ADCMODE_NORMAL_DCP0
/* #define LTC_GPIO_MEASUREMENT_MODE LTC_ADCMODE_FILTERED_DCP0 */
/* #define LTC_GPIO_MEASUREMENT_MODE LTC_ADCMODE_FAST_DCP0 */

/**
 * Measurement modus for Open-wire check
 */
#define LTC_OW_MEASUREMENT_MODE     LTC_ADCMODE_NORMAL_DCP0
/* #define LTC_OW_MEASUREMENT_MODE     LTC_ADCMODE_FILTERED_DCP0 */


/**
 * Timeout added to the transmission time for interrupt-
 * based SPI trnamission. TIme in ms.
 */
#define LTC_TRANSMISSION_TIMEOUT      10

/**
 * SPI1 is used for communication with LTC
 */
#define LTC_SPI_HANDLE      &spi_devices[0]

#define LTC_SPI_INSTANCE    *LTC_SPI_HANDLE.Instance

#define LTC_SPI_PRESCALER   *LTC_SPI_HANDLE.Init.BaudRatePrescaler

/**
 * start definition of LTC timings
 * Twake (see LTC datasheet)
 */
#define LTC_TWAKE_US    300
/**
 * start definition of LTC timings
 * Tready (see LTC datasheet)
 */
#define LTC_TREADY_US   10
/**
 * start definition of LTC timings
 * Tidle (see LTC datasheet)
 */
#define LTC_TIDLE_US    6700

/**
 * LTC statemachine short time definition in ms
 */
#define LTC_STATEMACH_SHORTTIME     1

/**
 * time for the first initialization of the daisy chain
 * see LTC6804 datasheet page 41
 */
#define LTC_STATEMACH_DAISY_CHAIN_FIRST_INITIALIZATION_TIME     ((LTC_TWAKE_US*LTC_N_LTC)/1000)
/**
 * time for the second initialization of the daisy chain
 * see LTC6804 datasheet page 41
 */
#define LTC_STATEMACH_DAISY_CHAIN_SECOND_INITIALIZATION_TIME    ((LTC_TREADY_US*LTC_N_LTC)/1000)


/*
 * Timings of Voltage Cell and GPIO measurement for all cells or all GPIO
 */

/**
 * ~1.1ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Fast Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_ALL_FAST_TCYCLE          2

/**
 * ~2.3ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Normal Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_ALL_NORMAL_TCYCLE        3

/**
 * ~201ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Filtered Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_ALL_FILTERED_TCYCLE      202

/*
 * Timings of Voltage Cell and GPIO measurement for a pair of cells or a single GPIO
 */

/**
 *  ~0.201ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Fast Mode
 *  unit: ms
 */
#define LTC_STATEMACH_MEAS_SINGLE_FAST_TCYCLE       1

/**
 * ~0.405ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Normal Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_SINGLE_NORMAL_TCYCLE     1

/**
 * ~34 ms Measurement+Calibration Cycle Time When Starting from the REFUP State in Filtered Mode
 * unit: ms
 */
#define LTC_STATEMACH_MEAS_SINGLE_FILTERED_TCYCLE   35

/**
 * LTC statemachine sequence error timing in ms
 */
#define LTC_STATEMACH_SEQERRTTIME   5
/**
 * LTC statemachine CRC-transmission error timing in ms
 */
#define LTC_STATEMACH_PECERRTIME    1

/**
 * Maximum number of re-tries in case of CRC error during the communication with daisy chain
 * before going into error state
 */
#define LTC_TRANSMIT_PECERRLIMIT    10

/**
 * Maximum number of re-tries in case of SPI error during the communication with daisy chain
 * before going into error state
 */
#define LTC_TRANSMIT_SPIERRLIMIT    3

/**
 * If set to 1, check if multiplexers acknowledged transmission
 */
#define LTC_READCOM     0


/**
 * ------------------- OPEN WIRE CHECK ------------------------
 * If open-wire check is performed cell voltages and temperatures are not
 * updated and thus old values can be transmitted on the CAN bus. Check time
 * is dependent on module configuration and external capacitance. Activate
 * open-wire check with care! See table below for various measured open-wire
 * check durations!
 */

/* #define LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK TRUE */
#define LTC_STANDBY_PERIODIC_OPEN_WIRE_CHECK FALSE

/**
 * Periodic open-wire check time in STANDBY state in ms
 */
#define LTC_STANDBY_OPEN_WIRE_PERIOD_ms      600000

/* #define LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK TRUE */
#define LTC_NORMAL_PERIODIC_OPEN_WIRE_CHECK FALSE

/**
 * Periodic open-wire check time in NORMAL state in ms
 */
#define LTC_NORMAL_OPEN_WIRE_PERIOD_ms      600000

/* #define LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK TRUE */
#define LTC_CHARGE_PERIODIC_OPEN_WIRE_CHECK FALSE

/**
 * Periodic open-wire check time in CHARGE state in ms
 */
#define LTC_CHARGE_OPEN_WIRE_PERIOD_ms      600000

/**
 * Periodic open-wire check time in ERROR state in ms
 */
#define LTC_ERROR_OPEN_WIRE_PERIOD_ms      30000

/**
 * Number of required ADOW commands because of external C-Pin capacitance and
 * the respective duration to perform an open wire check for 14 modules with
 * 12 cells each. During this time no cell voltages and temperatures are measured!
 * +----------------+--------------+---------------+----------+----------+
 * | External C pin | Normal  mode | Filtered mode | Duration | Duration |
 * | capacitance    |              |               |  normal  | filtered |
 * | ---------------+--------------+---------------+----------+----------+
 * |   <= 10nF      |      2       |        2      |    32ms  |   828ms  |
 * |     100nF      |     10       |        2      |   112ms  |   828ms  |
 * |       1uF      |    100       |        2      |  1012ms  |   828ms  |
 * |       C        | 1.5+(C/10nF) |        2      |          |          |
 * +----------------+--------------+---------------+----------+----------+
 */
#define LTC_NMBR_REQ_ADOW_COMMANDS      2

/**
 * Number of Bytes to be transmitted in daisy-chain
 * For first 4 Bytes:
 *  - 2 Bytes: command
 *  - 2 Bytes: CRC
 * Following Bytes: Data
 *  - 6 Bytes data per LTC
 *  - 2 Bytes CRC per LTC
 */
#define LTC_N_BYTES_FOR_DATA_TRANSMISSION   (4+(8*LTC_N_LTC))

/**
 * Number of Bytes to be transmitted in daisy-chain
 * Data
 *  - 6 Bytes data per LTC
 */
#define LTC_N_BYTES_FOR_DATA_TRANSMISSION_DATA_ONLY   (0+(6*LTC_N_LTC))


/* Transmit functions */
#define LTC_SendWakeUp()                SPI_Transmit(LTC_SPI_HANDLE, (uint8_t *) ltc_cmdDummy, 1)
#define LTC_SendI2CCmd(txbuf)           SPI_Transmit(LTC_SPI_HANDLE, txbuf, 4+9)
#define LTC_SendData(txbuf)             SPI_Transmit(LTC_SPI_HANDLE, txbuf, LTC_N_BYTES_FOR_DATA_TRANSMISSION)
#define LTC_SendCmd(command)            SPI_Transmit(LTC_SPI_HANDLE, (uint8_t *) command, 4)
#define LTC_ReceiveData(txbuf, rxbuf)    SPI_TransmitReceive(LTC_SPI_HANDLE, txbuf, rxbuf, LTC_N_BYTES_FOR_DATA_TRANSMISSION)


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

/**
 * Definition of the multiplexer measurement sequence
 */
extern LTC_MUX_SEQUENZ_s ltc_mux_seq;

/**
 * On the foxBMS slave board there are 6 multiplexer inputs dedicated to temperature
 * sensors by default.
 * Lookup table between temperature sensors and battery cells
 */
extern const uint8_t ltc_muxsensortemperatur_cfg[BS_NR_OF_TEMP_SENSORS_PER_MODULE];

/**
 * Lookup table to indicate which voltage inpus are used
 */
extern const uint8_t ltc_voltage_input_used[BS_MAX_SUPPORTED_CELLS];

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

/**
 * @brief   converts a raw voltage from multiplexer to a temperature value in Celsius.
 *
 * The temperatures are read from NTC elements via voltage dividers.
 * This function implements the look-up table between voltage and temperature,
 * taking into account the NTC characteristics and the voltage divider.
 *
 * @param   v_adc            voltage read from the multiplexer in V
 *
 * @return  temperature     temperature value in Celsius
 */
extern float LTC_Convert_MuxVoltages_to_Temperatures(float v_adc);

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

#endif /* LTC_CFG_H_ */