foxBMS  1.1.1
The foxBMS Battery Management System API Documentation
ltc_6813-1.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2021, Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  * contributors may be used to endorse or promote products derived from
20  * this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * We kindly request you to use one or more of the following phrases to refer to
34  * foxBMS in your hardware, software, documentation or advertising materials:
35  *
36  * - ″This product uses parts of foxBMS®″
37  * - ″This product includes parts of foxBMS®″
38  * - ″This product is derived from foxBMS®″
39  *
40  */
41 
42 /**
43  * @file ltc_6813-1.c
44  * @author foxBMS Team
45  * @date 2019-09-01 (date of creation)
46  * @updated 2021-07-14 (date of last update)
47  * @ingroup DRIVERS
48  * @prefix LTC
49  *
50  * @brief Driver for the LTC monitoring chip.
51  *
52  */
53 
54 /*========== Includes =======================================================*/
55 /* clang-format off */
56 #include "ltc_6813-1_cfg.h"
57 #include "ltc.h"
58 /* clang-format on */
59 
60 #include "HL_spi.h"
61 #include "HL_system.h"
62 
63 #include "database.h"
64 #include "diag.h"
65 #include "io.h"
66 #include "ltc_pec.h"
67 #include "os.h"
68 #include "mic_plausibility.h"
69 
70 /*========== Macros and Definitions =========================================*/
71 
72 /**
73  * LTC 6820 GPIO configuration
74  * @{
75  */
76 #define LTC_LTC6820CONTROL_GIODIR (hetREG1->DIR)
77 #define LTC_LTC6820CONTROL_GIOPORT (hetREG1->DOUT)
78 
79 #define LTC_LTC6820_FORWARD_ENABLE_PIN (22U)
80 #define LTC_LTC6820_FORWARD_SPI1_MASTER_PIN (24U)
81 
82 #define LTC_LTC6820_REVERSE_ENABLE_PIN (23U)
83 #define LTC_LTC6820_REVERSE_MASTER_PIN (25U)
84 /**@}*/
85 
86 /**
87  * TI port expander register addresses
88  * @{
89  */
90 #define LTC_PORT_EXPANDER_TI_INPUT_REG_ADR (0x00u)
91 #define LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR (0x01u)
92 #define LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR (0x03u)
93 /**@}*/
94 
95 /**
96  * LTC COMM definitions
97  * @{
98  */
99 #define LTC_ICOM_START (0x60u)
100 #define LTC_ICOM_STOP (0x10u)
101 #define LTC_ICOM_BLANK (0x00u)
102 #define LTC_ICOM_NO_TRANSMIT (0x70u)
103 #define LTC_FCOM_MASTER_ACK (0x00u)
104 #define LTC_FCOM_MASTER_NACK (0x08u)
105 #define LTC_FCOM_MASTER_NACK_STOP (0x09u)
106 /**@}*/
107 
108 /** maximum number of supported cells */
109 #define LTC_MAX_SUPPORTED_CELLS (12u)
110 
111 /*========== Static Constant and Variable Definitions =======================*/
112 /**
113  * PEC buffer for RX and TX
114  * @{
115  */
116 #pragma SET_DATA_SECTION(".sharedRAM")
119 #pragma SET_DATA_SECTION()
120 /**@}*/
121 
122 /** index of used cells */
123 static uint16_t ltc_used_cells_index[BS_NR_OF_STRINGS] = {0};
124 /** local copies of database tables */
125 /**@{*/
134 /**@}*/
135 /** stores information on the detected open wires locally */
137 static LTC_ERRORTABLE_s ltc_errorTable = {0}; /*!< init in LTC_ResetErrorTable-function */
138 
139 /** local definition of plausible cell voltage values for the LTC6813 (and similar) */
142  .minimumPlausibleVoltage_mV = 0,
143 };
144 
145 /*========== Extern Constant and Variable Definitions =======================*/
146 
148  .timer = 0,
149  .statereq = {.request = LTC_STATE_NO_REQUEST, .string = 0xFFu},
151  .substate = 0,
152  .laststate = LTC_STATEMACH_UNINITIALIZED,
153  .lastsubstate = 0,
154  .adcModereq = LTC_ADCMODE_FAST_DCP0,
155  .adcMode = LTC_ADCMODE_FAST_DCP0,
156  .adcMeasChreq = LTC_ADCMEAS_UNDEFINED,
157  .adcMeasCh = LTC_ADCMEAS_UNDEFINED,
158  .numberOfMeasuredMux = 32,
159  .triggerentry = 0,
160  .ErrRetryCounter = 0,
161  .ErrRequestCounter = 0,
162  .VoltageSampleTime = 0,
163  .muxSampleTime = 0,
164  .commandDataTransferTime = 3,
165  .commandTransferTime = 3,
166  .gpioClocksTransferTime = 3,
167  .muxmeas_seqptr = NULL_PTR,
168  .muxmeas_seqendptr = NULL_PTR,
169  .muxmeas_nr_end = 0,
170  .first_measurement_made = false,
171  .ltc_muxcycle_finished = STD_NOT_OK,
172  .check_spi_flag = STD_NOT_OK,
173  .balance_control_done = STD_NOT_OK,
174  .transmit_ongoing = false,
175  .dummyByte_ongoing = STD_NOT_OK,
176  .spiDiagErrorEntry = DIAG_ID_LTC_SPI,
177  .pecDiagErrorEntry = DIAG_ID_LTC_PEC,
178  .muxDiagErrorEntry = DIAG_ID_LTC_MUX,
179  .voltMeasDiagErrorEntry = DIAG_ID_MEASUREMENT_IC_CELL_VOLTAGE_MEAS_ERROR,
180  .tempMeasDiagErrorEntry = DIAG_ID_MEASUREMENT_IC_CELL_TEMPERATURE_MEAS_ERROR,
181  .ltcData.pSpiInterface = spi_ltcInterface,
182  .ltcData.txBuffer = ltc_TxPecBuffer,
183  .ltcData.rxBuffer = ltc_RxPecBuffer,
184  .ltcData.frameLength = LTC_N_BYTES_FOR_DATA_TRANSMISSION,
185  .ltcData.cellVoltage = &ltc_cellvoltage,
186  .ltcData.cellTemperature = &ltc_celltemperature,
187  .ltcData.balancingFeedback = &ltc_balancing_feedback,
188  .ltcData.balancingControl = &ltc_balancing_control,
189  .ltcData.slaveControl = &ltc_slave_control,
190  .ltcData.openWireDetection = &ltc_openWireDetection,
191  .ltcData.errorTable = &ltc_errorTable,
192  .ltcData.allGpioVoltages = &ltc_allgpiovoltage,
193  .ltcData.openWire = &ltc_openwire,
194  .ltcData.usedCellIndex = ltc_used_cells_index,
195  .currentString = 0u,
196  .requestedString = 0u,
197 };
198 
199 static uint16_t ltc_cmdWRCFG[4] = {0x00, 0x01, 0x3D, 0x6E};
200 static uint16_t ltc_cmdWRCFG2[4] = {0x00, 0x24, 0xB1, 0x9E};
201 static uint16_t ltc_cmdRDCFG[4] = {0x00, 0x02, 0x2B, 0x0A};
202 
203 static uint16_t ltc_cmdRDCVA[4] = {0x00, 0x04, 0x07, 0xC2};
204 static uint16_t ltc_cmdRDCVB[4] = {0x00, 0x06, 0x9A, 0x94};
205 static uint16_t ltc_cmdRDCVC[4] = {0x00, 0x08, 0x5E, 0x52};
206 static uint16_t ltc_cmdRDCVD[4] = {0x00, 0x0A, 0xC3, 0x04};
207 static uint16_t ltc_cmdRDCVE[4] = {0x00, 0x09, 0xD5, 0x60};
208 static uint16_t ltc_cmdRDCVF[4] = {0x00, 0x0B, 0x48, 0x36};
209 static uint16_t ltc_cmdWRCOMM[4] = {0x07, 0x21, 0x24, 0xB2};
210 static uint16_t ltc_cmdSTCOMM[4] = {0x07, 0x23, 0xB9, 0xE4};
211 static uint16_t ltc_cmdRDCOMM[4] = {0x07, 0x22, 0x32, 0xD6};
212 static uint16_t ltc_cmdRDAUXA[4] = {0x00, 0x0C, 0xEF, 0xCC};
213 static uint16_t ltc_cmdRDAUXB[4] = {0x00, 0x0E, 0x72, 0x9A};
214 static uint16_t ltc_cmdRDAUXC[4] = {0x00, 0x0D, 0x64, 0xFE};
215 static uint16_t ltc_cmdRDAUXD[4] = {0x00, 0x0F, 0xF9, 0xA8};
216 
217 /* static uint16_t ltc_cmdMUTE[4] = {0x00, 0x28, 0xE8, 0x0E}; !< MUTE discharging via S pins */
218 /* static uint16_t ltc_cmdUNMUTE[4] = {0x00, 0x29, 0x63, 0x3C}; !< UN-MUTE discharging via S pins */
219 
220 /* LTC I2C commands */
221 /* static uint16_t ltc_I2CcmdDummy[6] = {0x7F, 0xF9, 0x7F, 0xF9, 0x7F, 0xF9}; !< dummy command (no transmit) */
222 
223 static uint16_t ltc_I2CcmdTempSens0[6] = {
224  0x69,
225  0x08,
226  0x00,
227  0x09,
228  0x7F,
229  0xF9}; /*!< sets the internal data pointer of the temperature sensor (address 0x48) to 0x00 */
230 static uint16_t ltc_I2CcmdTempSens1[6] =
231  {0x69, 0x18, 0x0F, 0xF0, 0x0F, 0xF9}; /*!< reads two data bytes from the temperature sensor */
232 
233 static uint16_t ltc_I2CcmdPortExpander1[6] =
234  {0x64, 0x18, 0x0F, 0xF9, 0x7F, 0xF9}; /*!< reads one data byte from the port expander */
235 
236 /* Cells */
237 static uint16_t ltc_cmdADCV_normal_DCP0[4] =
238  {0x03, 0x60, 0xF4, 0x6C}; /*!< All cells, normal mode, discharge not permitted (DCP=0) */
239 static uint16_t ltc_cmdADCV_normal_DCP1[4] =
240  {0x03, 0x70, 0xAF, 0x42}; /*!< All cells, normal mode, discharge permitted (DCP=1) */
241 static uint16_t ltc_cmdADCV_filtered_DCP0[4] =
242  {0x03, 0xE0, 0xB0, 0x4A}; /*!< All cells, filtered mode, discharge not permitted (DCP=0) */
243 static uint16_t ltc_cmdADCV_filtered_DCP1[4] =
244  {0x03, 0xF0, 0xEB, 0x64}; /*!< All cells, filtered mode, discharge permitted (DCP=1) */
245 static uint16_t ltc_cmdADCV_fast_DCP0[4] =
246  {0x02, 0xE0, 0x38, 0x06}; /*!< All cells, fast mode, discharge not permitted (DCP=0) */
247 static uint16_t ltc_cmdADCV_fast_DCP1[4] =
248  {0x02, 0xF0, 0x63, 0x28}; /*!< All cells, fast mode, discharge permitted (DCP=1) */
249 static uint16_t ltc_cmdADCV_fast_DCP0_twocells[4] =
250  {0x02, 0xE1, 0xb3, 0x34}; /*!< Two cells (1 and 7), fast mode, discharge not permitted (DCP=0) */
251 
252 /* GPIOs */
253 static uint16_t ltc_cmdADAX_normal_GPIO1[4] = {0x05, 0x61, 0x58, 0x92}; /*!< Single channel, GPIO 1, normal mode */
254 static uint16_t ltc_cmdADAX_filtered_GPIO1[4] = {0x05, 0xE1, 0x1C, 0xB4}; /*!< Single channel, GPIO 1, filtered mode */
255 static uint16_t ltc_cmdADAX_fast_GPIO1[4] = {0x04, 0xE1, 0x94, 0xF8}; /*!< Single channel, GPIO 1, fast mode */
256 static uint16_t ltc_cmdADAX_normal_GPIO2[4] = {0x05, 0x62, 0x4E, 0xF6}; /*!< Single channel, GPIO 2, normal mode */
257 static uint16_t ltc_cmdADAX_filtered_GPIO2[4] = {0x05, 0xE2, 0x0A, 0xD0}; /*!< Single channel, GPIO 2, filtered mode */
258 static uint16_t ltc_cmdADAX_fast_GPIO2[4] = {0x04, 0xE2, 0x82, 0x9C}; /*!< Single channel, GPIO 2, fast mode */
259 static uint16_t ltc_cmdADAX_normal_GPIO3[4] = {0x05, 0x63, 0xC5, 0xC4}; /*!< Single channel, GPIO 3, normal mode */
260 static uint16_t ltc_cmdADAX_filtered_GPIO3[4] = {0x05, 0xE3, 0x81, 0xE2}; /*!< Single channel, GPIO 3, filtered mode */
261 static uint16_t ltc_cmdADAX_fast_GPIO3[4] = {0x04, 0xE3, 0x09, 0xAE}; /*!< Single channel, GPIO 3, fast mode */
262 /* static uint16_t ltc_cmdADAX_normal_GPIO4[4] = {0x05, 0x64, 0x62, 0x3E}; !< Single channel, GPIO 4, normal mode */
263 /* static uint16_t ltc_cmdADAX_filtered_GPIO4[4] = {0x05, 0xE4, 0x26, 0x18}; !< Single channel, GPIO 4, filtered mode */
264 /* static uint16_t ltc_cmdADAX_fast_GPIO4[4] = {0x04, 0xE4, 0xAE, 0x54}; !< Single channel, GPIO 4, fast mode */
265 /* static uint16_t ltc_cmdADAX_normal_GPIO5[4] = {0x05, 0x65, 0xE9, 0x0C}; !< Single channel, GPIO 5, normal mode */
266 /* static uint16_t ltc_cmdADAX_filtered_GPIO5[4] = {0x05, 0xE5, 0xAD, 0x2A}; !< Single channel, GPIO 5, filtered mode */
267 /* static uint16_t ltc_cmdADAX_fast_GPIO5[4] = {0x04, 0xE5, 0x25, 0x66}; !< Single channel, GPIO 5, fast mode */
268 static uint16_t ltc_cmdADAX_normal_ALLGPIOS[4] = {0x05, 0x60, 0xD3, 0xA0}; /*!< All channels, normal mode */
269 static uint16_t ltc_cmdADAX_filtered_ALLGPIOS[4] =
270  {0x05, 0xE0, 0x97, 0x86}; /*!< All channels, filtered mode */
271 static uint16_t ltc_cmdADAX_fast_ALLGPIOS[4] = {0x04, 0xE0, 0x1F, 0xCA}; /*!< All channels, fast mode */
272 
273 /* Open-wire */
274 static uint16_t ltc_BC_cmdADOW_PUP_normal_DCP0[4] = {
275  0x03,
276  0x68,
277  0x1C,
278  0x62}; /*!< Broadcast, Pull-up current, All cells, normal mode, discharge not permitted (DCP=0) */
279 static uint16_t ltc_BC_cmdADOW_PDOWN_normal_DCP0[4] = {
280  0x03,
281  0x28,
282  0xFB,
283  0xE8}; /*!< Broadcast, Pull-down current, All cells, normal mode, discharge not permitted (DCP=0) */
284 static uint16_t ltc_BC_cmdADOW_PUP_filtered_DCP0[4] = {
285  0x03,
286  0xE8,
287  0x58,
288  0x44}; /*!< Broadcast, Pull-up current, All cells, filtered mode, discharge not permitted (DCP=0) */
290  0x03,
291  0xA8,
292  0xBF,
293  0xCE}; /*!< Broadcast, Pull-down current, All cells, filtered mode, discharge not permitted (DCP=0) */
294 
295 /*========== Static Function Prototypes =====================================*/
296 static void LTC_SetFirstMeasurementCycleFinished(LTC_STATE_s *ltc_state);
297 static void LTC_Initialize_Database(LTC_STATE_s *ltc_state);
298 static void LTC_SaveBalancingFeedback(LTC_STATE_s *ltc_state, uint16_t *DataBufferSPI_RX, uint8_t stringNumber);
299 static void LTC_Get_BalancingControlValues(LTC_STATE_s *ltc_state);
300 static void LTC_SaveLastStates(LTC_STATE_s *ltc_state);
301 static void LTC_StateTransition(LTC_STATE_s *ltc_state, LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms);
302 static void LTC_CondBasedStateTransition(
303  LTC_STATE_s *ltc_state,
304  STD_RETURN_TYPE_e retVal,
305  DIAG_ID_e diagCode,
306  LTC_STATEMACH_e state_ok,
307  uint8_t substate_ok,
308  uint16_t timer_ms_ok,
309  LTC_STATEMACH_e state_nok,
310  uint8_t substate_nok,
311  uint16_t timer_ms_nok);
312 
314  LTC_STATE_s *ltc_state,
315  SPI_INTERFACE_CONFIG_s *pSpiInterface,
316  uint16_t *pTxBuff,
317  uint16_t *pRxBuff,
318  uint32_t frameLength,
319  uint8_t registerSet,
320  uint8_t stringNumber);
321 
322 static void LTC_ResetErrorTable(LTC_STATE_s *ltc_state);
324  SPI_INTERFACE_CONFIG_s *pSpiInterface,
325  uint16_t *pTxBuff,
326  uint16_t *pRxBuff,
327  uint32_t frameLength);
328 
330  SPI_INTERFACE_CONFIG_s *pSpiInterface,
331  LTC_ADCMODE_e adcMode,
332  LTC_ADCMEAS_CHAN_e adcMeasCh);
334  SPI_INTERFACE_CONFIG_s *pSpiInterface,
335  LTC_ADCMODE_e adcMode,
336  LTC_ADCMEAS_CHAN_e adcMeasCh);
338  SPI_INTERFACE_CONFIG_s *pSpiInterface,
339  LTC_ADCMODE_e adcMode,
340  uint8_t PUP);
341 
342 static uint16_t LTC_Get_MeasurementTCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh);
343 static void LTC_SaveRXtoVoltagebuffer(
344  LTC_STATE_s *ltc_state,
345  uint16_t *pRxBuff,
346  uint8_t registerSet,
347  uint8_t stringNumber);
348 static void LTC_SaveRXtoGPIOBuffer(
349  LTC_STATE_s *ltc_state,
350  uint16_t *pRxBuff,
351  uint8_t registerSet,
352  uint8_t stringNumber);
353 
355  LTC_STATE_s *ltc_state,
356  uint16_t *DataBufferSPI_RX_with_PEC,
357  uint8_t stringNumber);
359  uint16_t *Command,
360  SPI_INTERFACE_CONFIG_s *pSpiInterface,
361  uint16_t *pTxBuff,
362  uint16_t *pRxBuff,
363  uint32_t frameLength);
365  uint16_t *Command,
366  SPI_INTERFACE_CONFIG_s *pSpiInterface,
367  uint16_t *pTxBuff,
368  uint16_t *pRxBuff,
369  uint32_t frameLength);
370 static void LTC_SetMUXChCommand(uint16_t *pTxBuff, uint8_t mux, uint8_t channel);
372  LTC_STATE_s *ltc_state,
373  SPI_INTERFACE_CONFIG_s *pSpiInterface,
374  uint16_t *pTxBuff,
375  uint16_t *pRxBuff,
376  uint32_t frameLength,
377  uint8_t step);
378 static void LTC_SetEEPROMReadCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step);
379 static void LTC_EEPROMSaveReadValue(LTC_STATE_s *ltc_state, uint16_t *pRxBuff);
381  LTC_STATE_s *ltc_state,
382  SPI_INTERFACE_CONFIG_s *pSpiInterface,
383  uint16_t *pTxBuff,
384  uint16_t *pRxBuff,
385  uint32_t frameLength,
386  uint8_t step);
387 static void LTC_SetEEPROMWriteCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step);
389  SPI_INTERFACE_CONFIG_s *pSpiInterface,
390  uint16_t *pTxBuff,
391  uint16_t *pRxBuff,
392  uint32_t frameLength,
393  uint8_t mux,
394  uint8_t channel);
396  LTC_STATE_s *ltc_state,
397  SPI_INTERFACE_CONFIG_s *pSpiInterface,
398  uint16_t *pTxBuff,
399  uint16_t *pRxBuff,
400  uint32_t frameLength);
401 static void LTC_PortExpanderSaveValues(LTC_STATE_s *ltc_state, uint16_t *pRxBuff);
402 static void LTC_TempSensSaveTemp(LTC_STATE_s *ltc_state, uint16_t *pRxBuff);
404  LTC_STATE_s *ltc_state,
405  SPI_INTERFACE_CONFIG_s *pSpiInterface,
406  uint16_t *pTxBuff,
407  uint16_t *pRxBuff,
408  uint32_t frameLength,
411  LTC_STATE_s *ltc_state,
412  SPI_INTERFACE_CONFIG_s *pSpiInterface,
413  uint16_t *pTxBuff,
414  uint16_t *pRxBuff,
415  uint32_t frameLength);
417  LTC_STATE_s *ltc_state,
418  SPI_INTERFACE_CONFIG_s *pSpiInterface,
419  uint16_t *pTxBuff,
420  uint16_t *pRxBuff,
421  uint32_t frameLength,
422  uint8_t step);
423 static void LTC_PortExpanderSaveValues_TI(LTC_STATE_s *ltc_state, uint16_t *pTxBuff);
424 
427  SPI_INTERFACE_CONFIG_s *pSpiInterface,
428  uint16_t *pTxBuff,
429  uint16_t *pRxBuff,
430  uint32_t frameLength,
431  uint16_t *cmd_data);
432 
433 static STD_RETURN_TYPE_e LTC_I2CCheckACK(LTC_STATE_s *ltc_state, uint16_t *pRxBuff, uint8_t mux, uint8_t stringNumber);
434 
435 static void LTC_SaveMuxMeasurement(
436  LTC_STATE_s *ltc_state,
437  uint16_t *pRxBuff,
438  LTC_MUX_CH_CFG_s *muxseqptr,
439  uint8_t stringNumber);
440 
441 static uint32_t LTC_GetSPIClock(SPI_INTERFACE_CONFIG_s *pSpiInterface);
442 static void LTC_SetTransferTimes(LTC_STATE_s *ltc_state);
443 
445 
446 /*========== Static Function Implementations ================================*/
447 /**
448  * @brief in the database, initializes the fields related to the LTC drivers.
449  *
450  * This function loops through all the LTC-related data fields in the database
451  * and sets them to 0. It should be called in the initialization or re-initialization
452  * routine of the LTC driver.
453  *
454  * @param ltc_state: state of the ltc state machine
455  *
456  */
457 static void LTC_Initialize_Database(LTC_STATE_s *ltc_state) {
458  for (uint8_t stringNumber = 0u; stringNumber < BS_NR_OF_STRINGS; stringNumber++) {
459  ltc_state->ltcData.cellVoltage->state = 0;
460  for (uint16_t i = 0; i < BS_NR_OF_BAT_CELLS; i++) {
461  ltc_state->ltcData.cellVoltage->cellVoltage_mV[stringNumber][i] = 0;
462  ltc_state->ltcData.openWireDetection->openWirePup[stringNumber][i] = 0;
463  ltc_state->ltcData.openWireDetection->openWirePdown[stringNumber][i] = 0;
464  ltc_state->ltcData.openWireDetection->openWireDelta[stringNumber][i] = 0;
465  }
466 
467  ltc_state->ltcData.cellTemperature->state = 0;
468  for (uint16_t i = 0; i < BS_NR_OF_TEMP_SENSORS_PER_STRING; i++) {
469  ltc_state->ltcData.cellTemperature->cellTemperature_ddegC[stringNumber][i] = 0;
470  }
471 
472  ltc_state->ltcData.balancingFeedback->state = 0;
473  for (uint16_t i = 0; i < BS_NR_OF_BAT_CELLS; i++) {
474  ltc_state->ltcData.balancingControl->balancingState[stringNumber][i] = 0;
475  }
476  ltc_state->ltcData.balancingControl->nrBalancedCells[stringNumber] = 0u;
477  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
478  ltc_state->ltcData.balancingFeedback->value[stringNumber][i] = 0;
479  }
480 
481  ltc_state->ltcData.slaveControl->state = 0;
482  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
483  ltc_state->ltcData.slaveControl->ioValueIn[i] = 0;
484  ltc_state->ltcData.slaveControl->ioValueOut[i] = 0;
486  ltc_state->ltcData.slaveControl->eepromValueRead[i] = 0;
487  ltc_state->ltcData.slaveControl->eepromValueWrite[i] = 0;
488  }
489  ltc_state->ltcData.slaveControl->eepromReadAddressLastUsed = 0xFFFFFFFF;
490  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
491  ltc_state->ltcData.slaveControl->eepromWriteAddressLastUsed = 0xFFFFFFFF;
492  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
493 
494  ltc_state->ltcData.allGpioVoltages->state = 0;
495  for (uint16_t i = 0; i < (BS_NR_OF_MODULES * BS_NR_OF_GPIOS_PER_MODULE); i++) {
496  ltc_state->ltcData.allGpioVoltages->gpioVoltages_mV[stringNumber][i] = 0;
497  }
498 
499  for (uint16_t i = 0; i < (BS_NR_OF_MODULES * (BS_NR_OF_CELLS_PER_MODULE + 1)); i++) {
500  ltc_state->ltcData.openWire->openwire[stringNumber][i] = 0;
501  }
502  ltc_state->ltcData.openWire->state = 0;
503  }
504 
506  ltc_state->ltcData.cellVoltage,
507  ltc_state->ltcData.cellTemperature,
508  ltc_state->ltcData.balancingFeedback,
509  ltc_state->ltcData.openWire);
511 }
512 
513 /**
514  * @brief Saves the last state and the last substate
515  *
516  * @param ltc_state: state of the ltc state machine
517  */
518 static void LTC_SaveLastStates(LTC_STATE_s *ltc_state) {
519  ltc_state->laststate = ltc_state->state;
520  ltc_state->lastsubstate = ltc_state->substate;
521 }
522 
523 /**
524  * @brief function for setting LTC_Trigger state transitions
525  *
526  * @param ltc_state: state of the ltc state machine
527  * @param state: state to transition into
528  * @param substate: substate to transition into
529  * @param timer_ms: transition into state, substate after timer elapsed
530  */
531 static void LTC_StateTransition(LTC_STATE_s *ltc_state, LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms) {
532  ltc_state->state = state;
533  ltc_state->substate = substate;
534  ltc_state->timer = timer_ms;
535 }
536 
537 /**
538  * @brief condition-based state transition depending on retVal
539  *
540  * If retVal is #STD_OK, after timer_ms_ok is elapsed the LTC statemachine will
541  * transition into state_ok and substate_ok, otherwise after timer_ms_nok the
542  * statemachine will transition to state_nok and substate_nok. Depending on
543  * value of retVal the corresponding diagnosis entry will be called.
544  *
545  * @param ltc_state: state of the ltc state machine
546  * @param retVal: condition to determine if statemachine will transition into ok or nok states
547  * @param diagCode: symbolic IDs for diagnosis entry, called with #DIAG_EVENT_OK if retVal is #STD_OK, #DIAG_EVENT_NOT_OK otherwise
548  * @param state_ok state to transition into if retVal is #STD_OK
549  * @param substate_ok: substate to transition into if retVal is #STD_OK
550  * @param timer_ms_ok: transition into state_ok, substate_ok after timer_ms_ok elapsed
551  * @param state_nok: state to transition into if retVal is #STD_NOT_OK
552  * @param substate_nok: substate to transition into if retVal is #STD_NOT_OK
553  * @param timer_ms_nok: transition into state_nok, substate_nok after timer_ms_nok elapsed
554  */
556  LTC_STATE_s *ltc_state,
557  STD_RETURN_TYPE_e retVal,
558  DIAG_ID_e diagCode,
559  LTC_STATEMACH_e state_ok,
560  uint8_t substate_ok,
561  uint16_t timer_ms_ok,
562  LTC_STATEMACH_e state_nok,
563  uint8_t substate_nok,
564  uint16_t timer_ms_nok) {
565  if ((retVal != STD_OK)) {
567  LTC_StateTransition(ltc_state, state_nok, substate_nok, timer_ms_nok);
568  } else {
569  DIAG_Handler(diagCode, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
570  LTC_StateTransition(ltc_state, state_ok, substate_ok, timer_ms_ok);
571  }
572 }
573 
574 extern void LTC_SaveVoltages(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
575  /* Pointer validity check */
576  FAS_ASSERT(ltc_state != NULL_PTR);
577  FAS_ASSERT(stringNumber < BS_NR_OF_STRINGS);
578 
579  /* Iterate over all cell to:
580  *
581  * 1. Check open-wires and set respective cell measurements to invalid
582  * 2. Perform minimum/maximum measurement value plausibility check
583  * 3. Calculate string values
584  */
585  STD_RETURN_TYPE_e cellVoltageMeasurementValid = STD_OK;
586  int32_t stringVoltage_mV = 0;
587  uint16_t numberValidMeasurements = 0;
588  for (uint8_t m = 0u; m < BS_NR_OF_MODULES; m++) {
589  for (uint8_t c = 0u; c < BS_NR_OF_CELLS_PER_MODULE; c++) {
590  /* ------- 1. Check open-wires -----------------
591  * Is cell N input not open wire &&
592  * Is cell N+1 input not open wire &&
593  * Is cell voltage valid because of previous PEC error
594  * If so, everything okay, else set cell voltage measurement to invalid.
595  */
596  if ((ltc_state->ltcData.openWire->openwire[stringNumber][(m * (BS_NR_OF_CELLS_PER_MODULE + 1u)) + c] ==
597  0u) &&
598  (ltc_state->ltcData.openWire->openwire[stringNumber][(m * (BS_NR_OF_CELLS_PER_MODULE + 1u)) + c + 1u] ==
599  0u) &&
600  ((ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] & (0x01u << c)) == 0u)) {
601  /* Cell voltage is valid -> perform minimum/maximum plausibility check */
602 
603  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
605  ltc_state->ltcData.cellVoltage
606  ->cellVoltage_mV[stringNumber][(m * BS_NR_OF_CELLS_PER_MODULE) + c],
608  /* Cell voltage is valid -> calculate string voltage */
609  /* -------- 3. Calculate string values ------------- */
610  stringVoltage_mV += ltc_state->ltcData.cellVoltage
611  ->cellVoltage_mV[stringNumber][(m * BS_NR_OF_CELLS_PER_MODULE) + c];
612  numberValidMeasurements++;
613  } else {
614  /* Invalidate cell voltage measurement */
615  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << c);
616  cellVoltageMeasurementValid = STD_NOT_OK;
617  }
618  } else {
619  /* Set cell voltage measurement value invalid, if not already invalid because of PEC Error */
620  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << c);
621  cellVoltageMeasurementValid = STD_NOT_OK;
622  }
623  }
624  }
625  DIAG_CheckEvent(cellVoltageMeasurementValid, ltc_state->voltMeasDiagErrorEntry, DIAG_STRING, stringNumber);
626  ltc_state->ltcData.cellVoltage->packVoltage_mV[stringNumber] = stringVoltage_mV;
627  ltc_state->ltcData.cellVoltage->nrValidCellVoltages[stringNumber] = numberValidMeasurements;
628 
629  /* Increment state variable each time new values are written into database */
630  ltc_state->ltcData.cellVoltage->state++;
631 
632  DATA_WRITE_DATA(ltc_state->ltcData.cellVoltage);
633 }
634 
635 /*========== Extern Function Implementations ================================*/
636 extern void LTC_SaveTemperatures(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
637  STD_RETURN_TYPE_e cellTemperatureMeasurementValid = STD_OK;
638  uint16_t numberValidMeasurements = 0;
639 
640  for (uint8_t m = 0u; m < BS_NR_OF_MODULES; m++) {
641  for (uint8_t c = 0u; c < BS_NR_OF_TEMP_SENSORS_PER_MODULE; c++) {
642  /* ------- 1. Check valid flag -----------------
643  * Is cell temperature valid because of previous PEC error
644  * If so, everything okay, else set cell temperature measurement to invalid.
645  */
646  if ((ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] & (0x01u << c)) == 0u) {
647  /* Cell temperature is valid -> perform minimum/maximum plausibility check */
648 
649  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
650  if (STD_OK ==
652  ltc_state->ltcData.cellTemperature
653  ->cellTemperature_ddegC[stringNumber][(m * BS_NR_OF_TEMP_SENSORS_PER_MODULE) + c])) {
654  numberValidMeasurements++;
655  } else {
656  /* Invalidate cell temperature measurement */
657  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] |= (0x01u << c);
658  cellTemperatureMeasurementValid = STD_NOT_OK;
659  }
660  } else {
661  /* Already invalid because of PEC Error */
662  cellTemperatureMeasurementValid = STD_NOT_OK;
663  }
664  }
665  }
666  DIAG_CheckEvent(cellTemperatureMeasurementValid, ltc_state->tempMeasDiagErrorEntry, DIAG_STRING, stringNumber);
667 
668  ltc_state->ltcData.cellTemperature->nrValidTemperatures[stringNumber] = numberValidMeasurements;
669 
670  ltc_state->ltcData.cellTemperature->state++;
672 }
673 
674 extern void LTC_SaveAllGPIOMeasurement(LTC_STATE_s *ltc_state) {
675  ltc_state->ltcData.allGpioVoltages->state++;
677 }
678 
679 /**
680  * @brief stores the measured balancing feedback values in the database.
681  *
682  * This function stores the global balancing feedback value measured on GPIO3 of the LTC into the database
683  *
684  * @param ltc_state state of the ltc state machine
685  * @param DataBufferSPI_RX receive buffer of the SPI interface
686  * @param stringNumber string addressed
687  *
688  */
689 static void LTC_SaveBalancingFeedback(LTC_STATE_s *ltc_state, uint16_t *DataBufferSPI_RX, uint8_t stringNumber) {
690  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
691  const uint16_t val_i = DataBufferSPI_RX[8u + (1u * i * 8u)] |
692  (DataBufferSPI_RX[8u + (1u * i * 8u) + 1u] << 8u); /* raw value, GPIO3 */
693 
694  ltc_state->ltcData.balancingFeedback->value[stringNumber][i] = val_i;
695  }
696 
697  ltc_state->ltcData.balancingFeedback->state++;
699 }
700 
701 /**
702  * @brief gets the balancing orders from the database.
703  *
704  * This function gets the balancing control from the database. Balancing control
705  * is set by the BMS. The LTC driver only executes the balancing orders.
706  *
707  * @param ltc_state: state of the ltc state machine
708  *
709  */
712 }
713 
714 /**
715  * @brief re-entrance check of LTC state machine trigger function
716  *
717  * This function is not re-entrant and should only be called time- or event-triggered.
718  * It increments the triggerentry counter from the state variable ltc_state.
719  * It should never be called by two different processes, so if it is the case, triggerentry
720  * should never be higher than 0 when this function is called.
721  *
722  * @param ltc_state: state of the ltc state machine
723  *
724  * @return retval 0 if no further instance of the function is active, 0xff else
725  *
726  */
727 uint8_t LTC_CheckReEntrance(LTC_STATE_s *ltc_state) {
728  uint8_t retval = 0;
729 
731  if (!ltc_state->triggerentry) {
732  ltc_state->triggerentry++;
733  } else {
734  retval = 0xFF; /* multiple calls of function */
735  }
737 
738  return (retval);
739 }
740 
742  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
743 
745  retval.request = ltc_state->statereq.request;
746  retval.string = ltc_state->statereq.string;
748 
749  return (retval);
750 }
751 
753  return ltc_state->state;
754 }
755 
756 /**
757  * @brief transfers the current state request to the state machine.
758  *
759  * This function takes the current state request from ltc_state and transfers it to the state machine.
760  * It resets the value from ltc_state to LTC_STATE_NO_REQUEST
761  *
762  * @param ltc_state: state of the ltc state machine
763  * @param pBusIDptr bus ID, main or backup (deprecated)
764  * @param pAdcModeptr LTC ADCmeasurement mode (fast, normal or filtered)
765  * @param pAdcMeasChptr number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
766  *
767  * @return retVal current state request, taken from LTC_STATE_REQUEST_e
768  *
769  */
771  LTC_STATE_s *ltc_state,
772  uint8_t *pBusIDptr,
773  LTC_ADCMODE_e *pAdcModeptr,
774  LTC_ADCMEAS_CHAN_e *pAdcMeasChptr) {
775  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
776 
778  retval.request = ltc_state->statereq.request;
779  retval.string = ltc_state->statereq.string;
780  ltc_state->requestedString = ltc_state->statereq.string;
781  *pAdcModeptr = ltc_state->adcModereq;
782  *pAdcMeasChptr = ltc_state->adcMeasChreq;
784  ltc_state->statereq.string = 0x0u;
786 
787  return (retval);
788 }
789 
791  LTC_RETURN_TYPE_e retVal = LTC_ERROR;
792 
794  retVal = LTC_CheckStateRequest(ltc_state, statereq);
795 
796  if ((retVal == LTC_OK) || (retVal == LTC_BUSY_OK) || (retVal == LTC_OK_FROM_ERROR)) {
797  ltc_state->statereq = statereq;
798  }
800 
801  return (retVal);
802 }
803 
804 void LTC_Trigger(LTC_STATE_s *ltc_state) {
805  STD_RETURN_TYPE_e retVal = STD_OK;
806  LTC_REQUEST_s statereq = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
807  uint8_t tmpbusID = 0;
810  STD_RETURN_TYPE_e continueFunction = STD_OK;
811 
812  FAS_ASSERT(ltc_state != NULL_PTR);
813 
814  /* Check re-entrance of function */
815  if (LTC_CheckReEntrance(ltc_state) > 0u) {
816  continueFunction = STD_NOT_OK;
817  }
818 
819  if (ltc_state->check_spi_flag == STD_NOT_OK) {
820  if (ltc_state->timer > 0u) {
821  if ((--ltc_state->timer) > 0u) {
822  ltc_state->triggerentry--;
823  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
824  }
825  }
826  } else {
827  if (MIC_IsTransmitOngoing(ltc_state) == true) {
828  if (ltc_state->timer > 0u) {
829  if ((--ltc_state->timer) > 0u) {
830  ltc_state->triggerentry--;
831  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
832  }
833  }
834  }
835  }
836 
837  if (continueFunction == STD_OK) {
838  switch (ltc_state->state) {
839  /****************************UNINITIALIZED***********************************/
841  /* waiting for Initialization Request */
842  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
843  if (statereq.request == LTC_STATE_INIT_REQUEST) {
844  LTC_SaveLastStates(ltc_state);
845  LTC_Initialize_Database(ltc_state);
846  LTC_ResetErrorTable(ltc_state);
849  ltc_state->adcMode = tmpadcMode;
850  ltc_state->adcMeasCh = tmpadcMeasCh;
851  } else if (statereq.request == LTC_STATE_NO_REQUEST) {
852  /* no actual request pending */
853  } else {
854  ltc_state->ErrRequestCounter++; /* illegal request pending */
855  }
856  break;
857 
858  /****************************INITIALIZATION**********************************/
860 
861  LTC_SetTransferTimes(ltc_state);
862 
863  if (ltc_state->substate == LTC_INIT_STRING) {
864  LTC_SaveLastStates(ltc_state);
865  ltc_state->currentString = 0u;
866 
867  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
869  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
872  } else if (ltc_state->substate == LTC_ENTRY_INITIALIZATION) {
873  LTC_SaveLastStates(ltc_state);
874 
875  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
876  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
877  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
878  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
879 
880  retVal = LTC_TransmitWakeUp(ltc_state->spiSeqPtr); /* Send dummy byte to wake up the daisy chain */
882  ltc_state,
883  retVal,
884  ltc_state->spiDiagErrorEntry,
891 
892  } else if (ltc_state->substate == LTC_RE_ENTRY_INITIALIZATION) {
893  LTC_SaveLastStates(ltc_state);
894  retVal =
895  LTC_TransmitWakeUp(ltc_state->spiSeqPtr); /* Send dummy byte again to wake up the daisy chain */
897  ltc_state,
898  retVal,
899  ltc_state->spiDiagErrorEntry,
906 
907  } else if (ltc_state->substate == LTC_START_INIT_INITIALIZATION) {
908  LTC_SaveLastStates(ltc_state);
909  ltc_state->check_spi_flag = STD_OK;
910  MIC_SetTransmitOngoing(ltc_state);
911  retVal = LTC_Init(
912  ltc_state->spiSeqPtr,
913  ltc_state->ltcData.txBuffer,
914  ltc_state->ltcData.rxBuffer,
915  ltc_state->ltcData.frameLength); /* Initialize main LTC loop */
916  ltc_state->lastsubstate = ltc_state->substate;
917  DIAG_CheckEvent(retVal, ltc_state->spiDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
919  ltc_state,
923 
924  } else if (ltc_state->substate == LTC_CHECK_INITIALIZATION) {
925  /* Read values written in config register, currently unused */
926  LTC_SaveLastStates(ltc_state);
927  MIC_SetTransmitOngoing(ltc_state);
928  retVal = LTC_RX(
929  ltc_cmdRDCFG,
930  ltc_state->spiSeqPtr,
931  ltc_state->ltcData.txBuffer,
932  ltc_state->ltcData.rxBuffer,
933  ltc_state->ltcData.frameLength); /* Read config register */
935  ltc_state,
939 
940  } else if (ltc_state->substate == LTC_EXIT_INITIALIZATION) {
941  LTC_SaveLastStates(ltc_state);
942  ++ltc_state->spiSeqPtr;
943  ++ltc_state->currentString;
944  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
947  } else {
950  }
951  }
952  break;
953 
954  /****************************INITIALIZED*************************************/
956  LTC_SaveLastStates(ltc_state);
958  break;
959 
960  /****************************START MEASUREMENT*******************************/
962 
964  ltc_state->adcMeasCh = LTC_ADCMEAS_ALLCHANNEL;
965 
966  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
968  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
969  ltc_state->currentString = 0u;
970 
971  ltc_state->check_spi_flag = STD_NOT_OK;
972  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
973 
975  ltc_state,
976  retVal,
977  ltc_state->spiDiagErrorEntry,
980  (ltc_state->commandTransferTime +
981  LTC_Get_MeasurementTCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
985 
986  break;
987 
988  /****************************START MEASUREMENT CONTINUE*******************************/
989  /* Do not reset SPI interface pointer */
991 
993  ltc_state->adcMeasCh = LTC_ADCMEAS_ALLCHANNEL;
994 
995  ltc_state->check_spi_flag = STD_NOT_OK;
996  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
997 
999  ltc_state,
1000  retVal,
1001  ltc_state->spiDiagErrorEntry,
1004  (ltc_state->commandTransferTime +
1005  LTC_Get_MeasurementTCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1009 
1010  break;
1011 
1012  /****************************READ VOLTAGE************************************/
1014 
1016  ltc_state->check_spi_flag = STD_OK;
1017  MIC_SetTransmitOngoing(ltc_state);
1018  retVal = LTC_RX(
1019  ltc_cmdRDCVA,
1020  ltc_state->spiSeqPtr,
1021  ltc_state->ltcData.txBuffer,
1022  ltc_state->ltcData.rxBuffer,
1023  ltc_state->ltcData.frameLength);
1025  ltc_state,
1026  retVal,
1027  ltc_state->spiDiagErrorEntry,
1034  break;
1035 
1036  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE) {
1037  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1038  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1039  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1040 
1041  MIC_SetTransmitOngoing(ltc_state);
1042  retVal = LTC_RX(
1043  ltc_cmdRDCVB,
1044  ltc_state->spiSeqPtr,
1045  ltc_state->ltcData.txBuffer,
1046  ltc_state->ltcData.rxBuffer,
1047  ltc_state->ltcData.frameLength);
1049  ltc_state,
1050  retVal,
1051  ltc_state->spiDiagErrorEntry,
1058  break;
1059 
1060  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE) {
1061  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1062  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1063  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1064 
1065  MIC_SetTransmitOngoing(ltc_state);
1066  retVal = LTC_RX(
1067  ltc_cmdRDCVC,
1068  ltc_state->spiSeqPtr,
1069  ltc_state->ltcData.txBuffer,
1070  ltc_state->ltcData.rxBuffer,
1071  ltc_state->ltcData.frameLength);
1073  ltc_state,
1074  retVal,
1075  ltc_state->spiDiagErrorEntry,
1082  break;
1083 
1084  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE) {
1085  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1086  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1087  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1088 
1089  MIC_SetTransmitOngoing(ltc_state);
1090  retVal = LTC_RX(
1091  ltc_cmdRDCVD,
1092  ltc_state->spiSeqPtr,
1093  ltc_state->ltcData.txBuffer,
1094  ltc_state->ltcData.rxBuffer,
1095  ltc_state->ltcData.frameLength);
1096  if (BS_MAX_SUPPORTED_CELLS > 12) {
1098  ltc_state,
1099  retVal,
1100  ltc_state->spiDiagErrorEntry,
1107  } else {
1109  ltc_state,
1110  retVal,
1111  ltc_state->spiDiagErrorEntry,
1118  }
1119  break;
1120 
1121  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE) {
1122  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1123  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1124  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1125 
1126  MIC_SetTransmitOngoing(ltc_state);
1127  retVal = LTC_RX(
1128  ltc_cmdRDCVE,
1129  ltc_state->spiSeqPtr,
1130  ltc_state->ltcData.txBuffer,
1131  ltc_state->ltcData.rxBuffer,
1132  ltc_state->ltcData.frameLength);
1133  if (BS_MAX_SUPPORTED_CELLS > 15) {
1135  ltc_state,
1136  retVal,
1137  ltc_state->spiDiagErrorEntry,
1144  } else {
1146  ltc_state,
1147  retVal,
1148  ltc_state->spiDiagErrorEntry,
1155  }
1156  break;
1157 
1158  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE) {
1159  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1160  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1161  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1162 
1163  MIC_SetTransmitOngoing(ltc_state);
1164  retVal = LTC_RX(
1165  ltc_cmdRDCVF,
1166  ltc_state->spiSeqPtr,
1167  ltc_state->ltcData.txBuffer,
1168  ltc_state->ltcData.rxBuffer,
1169  ltc_state->ltcData.frameLength);
1171  ltc_state,
1172  retVal,
1173  ltc_state->spiDiagErrorEntry,
1180  break;
1181 
1182  } else if (ltc_state->substate == LTC_EXIT_READVOLTAGE) {
1183  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1184  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1185  if (BS_MAX_SUPPORTED_CELLS == 12) {
1186  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1187  } else if (BS_MAX_SUPPORTED_CELLS == 15) {
1188  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1189  } else if (BS_MAX_SUPPORTED_CELLS == 18) {
1190  LTC_SaveRXtoVoltagebuffer(ltc_state, ltc_state->ltcData.rxBuffer, 5, ltc_state->currentString);
1191  }
1192 
1193  /* Switch to different state if read voltage state is reused
1194  * e.g. open-wire check... */
1195  if (ltc_state->reusageMeasurementMode == LTC_NOT_REUSED) {
1196  LTC_SaveVoltages(ltc_state, ltc_state->currentString);
1198  ltc_state,
1202  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PUP) {
1204  ltc_state,
1208  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PDOWN) {
1210  ltc_state,
1214  }
1215  ltc_state->check_spi_flag = STD_NOT_OK;
1216  }
1217  break;
1218 
1219  /****************************MULTIPLEXED MEASUREMENT CONFIGURATION***********/
1221 
1222  if (ltc_state->substate == LTC_STATEMACH_MUXCONFIGURATION_INIT) {
1223  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1225 
1226  if (ltc_state->muxmeas_seqptr[ltc_state->currentString] >=
1227  ltc_state->muxmeas_seqendptr[ltc_state->currentString]) {
1228  /* last step of sequence reached (or no sequence configured) */
1229 
1230  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
1231  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
1232  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
1233  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
1234 
1235  LTC_SaveTemperatures(ltc_state, ltc_state->currentString);
1236  }
1237 
1238  ltc_state->check_spi_flag = STD_OK;
1239  MIC_SetTransmitOngoing(ltc_state);
1240  retVal = LTC_SetMuxChannel(
1241  ltc_state->spiSeqPtr,
1242  ltc_state->ltcData.txBuffer,
1243  ltc_state->ltcData.rxBuffer,
1244  ltc_state->ltcData.frameLength,
1245  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID, /* mux */
1246  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh /* channel */);
1247  if (retVal != STD_OK) {
1248  DIAG_Handler(
1250  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1252  ltc_state,
1256  } else {
1257  DIAG_Handler(
1258  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1260  ltc_state,
1264  }
1265  break;
1266 
1267  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
1268  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1269  DIAG_Handler(
1271  } else {
1272  DIAG_Handler(
1273  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1274  }
1275 
1276  MIC_SetTransmitOngoing(ltc_state);
1277  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
1278  if (LTC_GOTO_MUX_CHECK == true) {
1280  ltc_state,
1281  retVal,
1282  ltc_state->spiDiagErrorEntry,
1289  ;
1290  } else {
1292  ltc_state,
1293  retVal,
1294  ltc_state->spiDiagErrorEntry,
1301  }
1302  break;
1303 
1305  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1306  DIAG_Handler(
1308  } else {
1309  DIAG_Handler(
1310  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1311  }
1312 
1313  MIC_SetTransmitOngoing(ltc_state);
1314  retVal = LTC_RX(
1315  ltc_cmdRDCOMM,
1316  ltc_state->spiSeqPtr,
1317  ltc_state->ltcData.txBuffer,
1318  ltc_state->ltcData.rxBuffer,
1319  ltc_state->ltcData.frameLength);
1321  ltc_state,
1322  retVal,
1323  ltc_state->spiDiagErrorEntry,
1330  break;
1331 
1333  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1334  DIAG_Handler(
1336  } else {
1337  DIAG_Handler(
1338  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1339  }
1340 
1341  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1342  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1343 
1344  /* if CRC OK: check multiplexer answer on i2C bus */
1345  retVal = LTC_I2CCheckACK(
1346  ltc_state,
1347  ltc_state->ltcData.rxBuffer,
1348  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID,
1349  ltc_state->currentString);
1350  DIAG_CheckEvent(retVal, ltc_state->muxDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1353  break;
1354 
1355  } else if (ltc_state->substate == LTC_STATEMACH_MUXMEASUREMENT) {
1356  if (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh == 0xFF) {
1357  /* actual multiplexer is switched off, so do not make a measurement and follow up with next step (mux configuration) */
1358  ++ltc_state
1359  ->muxmeas_seqptr[ltc_state->currentString]; /* go further with next step of sequence
1360  ltc_state.numberOfMeasuredMux not decremented, this does not count as a measurement */
1362  break;
1363  } else {
1364  if (LTC_GOTO_MUX_CHECK == false) {
1365  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1366  DIAG_Handler(
1367  ltc_state->spiDiagErrorEntry,
1369  DIAG_STRING,
1370  ltc_state->currentString);
1371  } else {
1372  DIAG_Handler(
1373  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1374  }
1375  }
1376 
1377  ltc_state->check_spi_flag = STD_NOT_OK;
1378  /* user multiplexer type -> connected to GPIO2! */
1379  if ((ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 1) ||
1380  (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 2)) {
1381  retVal = LTC_StartGPIOMeasurement(
1382  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2);
1383  } else {
1384  retVal = LTC_StartGPIOMeasurement(
1385  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO1);
1386  }
1387  }
1389  ltc_state,
1390  retVal,
1391  ltc_state->spiDiagErrorEntry,
1394  (ltc_state->commandTransferTime +
1396  ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2)), /* wait, ADAX-Command */
1400  break;
1401 
1402  } else if (ltc_state->substate == LTC_STATEMACH_READMUXMEASUREMENT) {
1403  ltc_state->check_spi_flag = STD_OK;
1404 
1405  MIC_SetTransmitOngoing(ltc_state);
1406  retVal = LTC_RX(
1407  ltc_cmdRDAUXA,
1408  ltc_state->spiSeqPtr,
1409  ltc_state->ltcData.txBuffer,
1410  ltc_state->ltcData.rxBuffer,
1411  ltc_state->ltcData.frameLength);
1413  ltc_state,
1414  retVal,
1415  ltc_state->spiDiagErrorEntry,
1422  break;
1423 
1424  } else if (ltc_state->substate == LTC_STATEMACH_STOREMUXMEASUREMENT) {
1425  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1426  DIAG_Handler(
1428  } else {
1429  DIAG_Handler(
1430  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1431  }
1432 
1433  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1434  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1436  ltc_state,
1437  ltc_state->ltcData.rxBuffer,
1438  ltc_state->muxmeas_seqptr[ltc_state->currentString],
1439  ltc_state->currentString);
1440 
1441  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1442 
1445  break;
1446  }
1447 
1448  break;
1449 
1450  /****************************END OF MEASUREMENT CYCLE************************/
1452 
1453  if (ltc_state->balance_control_done == STD_OK) {
1454  if (LTC_IsFirstMeasurementCycleFinished(ltc_state) == false) {
1456  }
1457  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
1458  if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST) {
1460  ltc_state,
1464  ltc_state->balance_control_done = STD_NOT_OK;
1465  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST) {
1467  ltc_state,
1471  ltc_state->balance_control_done = STD_NOT_OK;
1472  } else if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST_TI) {
1474  ltc_state,
1478  ltc_state->balance_control_done = STD_NOT_OK;
1479  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST_TI) {
1481  ltc_state,
1485  ltc_state->balance_control_done = STD_NOT_OK;
1486  } else if (statereq.request == LTC_STATE_EEPROM_READ_REQUEST) {
1489  } else if (statereq.request == LTC_STATE_EEPROM_WRITE_REQUEST) {
1492  ltc_state->balance_control_done = STD_NOT_OK;
1493  } else if (statereq.request == LTC_STATE_TEMP_SENS_READ_REQUEST) {
1496  ltc_state->balance_control_done = STD_NOT_OK;
1497  } else if (statereq.request == LTC_STATEMACH_BALANCEFEEDBACK_REQUEST) {
1500  ltc_state->balance_control_done = STD_NOT_OK;
1501  } else if (statereq.request == LTC_STATE_OPENWIRE_CHECK_REQUEST) {
1503  ltc_state,
1507  /* Send ADOW command with PUP two times */
1509  ltc_state->balance_control_done = STD_NOT_OK;
1510  } else {
1512  ltc_state,
1516  ltc_state->balance_control_done = STD_NOT_OK;
1517  }
1518  } else {
1521  }
1522 
1523  break;
1524 
1525  /****************************BALANCE CONTROL*********************************/
1527 
1528  if (ltc_state->substate == LTC_CONFIG_BALANCECONTROL) {
1529  ltc_state->check_spi_flag = STD_OK;
1530  MIC_SetTransmitOngoing(ltc_state);
1531  retVal = LTC_BalanceControl(
1532  ltc_state,
1533  ltc_state->spiSeqPtr,
1534  ltc_state->ltcData.txBuffer,
1535  ltc_state->ltcData.rxBuffer,
1536  ltc_state->ltcData.frameLength,
1537  0u,
1538  ltc_state->currentString);
1540  ltc_state,
1541  retVal,
1542  ltc_state->spiDiagErrorEntry,
1549  break;
1550 
1551  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL) {
1552  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1553  DIAG_Handler(
1555  } else {
1556  DIAG_Handler(
1557  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1558  }
1559 
1560  if (BS_NR_OF_CELLS_PER_MODULE > 12) {
1561  MIC_SetTransmitOngoing(ltc_state);
1562  retVal = LTC_BalanceControl(
1563  ltc_state,
1564  ltc_state->spiSeqPtr,
1565  ltc_state->ltcData.txBuffer,
1566  ltc_state->ltcData.rxBuffer,
1567  ltc_state->ltcData.frameLength,
1568  1u,
1569  ltc_state->currentString);
1571  ltc_state,
1572  retVal,
1573  ltc_state->spiDiagErrorEntry,
1580  } else {
1581  /* 12 cells, balancing control finished */
1582  ltc_state->check_spi_flag = STD_NOT_OK;
1583  ++ltc_state->spiSeqPtr;
1584  ++ltc_state->currentString;
1585  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1586  ltc_state->balance_control_done = STD_OK;
1588  } else {
1591  }
1592  }
1593 
1594  break;
1595 
1596  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL_END) {
1597  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1598  DIAG_Handler(
1600  } else {
1601  DIAG_Handler(
1602  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1603  }
1604  /* More than 12 cells, balancing control finished */
1605  ltc_state->check_spi_flag = STD_NOT_OK;
1606  ++ltc_state->spiSeqPtr;
1607  ++ltc_state->currentString;
1608  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1609  ltc_state->balance_control_done = STD_OK;
1611  } else {
1614  }
1615 
1616  break;
1617  }
1618  break;
1619 
1620  /****************************START MEASUREMENT*******************************/
1622 
1623  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1624  ltc_state->adcMeasCh = LTC_ADCMEAS_ALLCHANNEL;
1625 
1626  ltc_state->check_spi_flag = STD_NOT_OK;
1627  retVal = LTC_StartGPIOMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1629  ltc_state,
1630  retVal,
1631  ltc_state->spiDiagErrorEntry,
1634  (ltc_state->commandTransferTime +
1635  LTC_Get_MeasurementTCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1637  LTC_ENTRY,
1638  LTC_STATEMACH_SHORTTIME); /* TODO: @koffel here same state is kept if error occurs */
1639  break;
1640 
1641  /****************************READ ALL GPIO VOLTAGE************************************/
1643 
1644  if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_A_RDAUXA) {
1645  ltc_state->check_spi_flag = STD_OK;
1646  MIC_SetTransmitOngoing(ltc_state);
1647  retVal = LTC_RX(
1648  ltc_cmdRDAUXA,
1649  ltc_state->spiSeqPtr,
1650  ltc_state->ltcData.txBuffer,
1651  ltc_state->ltcData.rxBuffer,
1652  ltc_state->ltcData.frameLength);
1654  ltc_state,
1655  retVal,
1656  ltc_state->spiDiagErrorEntry,
1663  break;
1664 
1665  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_B_RDAUXB) {
1666  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1667  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1668  LTC_SaveRXtoGPIOBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1669 
1670  MIC_SetTransmitOngoing(ltc_state);
1671  retVal = LTC_RX(
1672  ltc_cmdRDAUXB,
1673  ltc_state->spiSeqPtr,
1674  ltc_state->ltcData.txBuffer,
1675  ltc_state->ltcData.rxBuffer,
1676  ltc_state->ltcData.frameLength);
1677 
1678  if (BS_MAX_SUPPORTED_CELLS > 12) {
1680  ltc_state,
1681  retVal,
1682  ltc_state->spiDiagErrorEntry,
1689  } else {
1691  ltc_state,
1692  retVal,
1693  ltc_state->spiDiagErrorEntry,
1700  }
1701  break;
1702 
1703  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_C_RDAUXC) {
1704  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1705  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1706  LTC_SaveRXtoGPIOBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1707 
1708  MIC_SetTransmitOngoing(ltc_state);
1709  retVal = LTC_RX(
1710  ltc_cmdRDAUXC,
1711  ltc_state->spiSeqPtr,
1712  ltc_state->ltcData.txBuffer,
1713  ltc_state->ltcData.rxBuffer,
1714  ltc_state->ltcData.frameLength);
1716  ltc_state,
1717  retVal,
1718  ltc_state->spiDiagErrorEntry,
1725  break;
1726 
1727  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_D_RDAUXD) {
1728  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1729  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1730  LTC_SaveRXtoGPIOBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1731 
1732  MIC_SetTransmitOngoing(ltc_state);
1733  retVal = LTC_RX(
1734  ltc_cmdRDAUXD,
1735  ltc_state->spiSeqPtr,
1736  ltc_state->ltcData.txBuffer,
1737  ltc_state->ltcData.rxBuffer,
1738  ltc_state->ltcData.frameLength);
1740  ltc_state,
1741  retVal,
1742  ltc_state->spiDiagErrorEntry,
1749  break;
1750 
1751  } else if (ltc_state->substate == LTC_EXIT_READAUXILIARY_ALLGPIOS) {
1752  retVal = LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1753  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1754 
1755  if (BS_MAX_SUPPORTED_CELLS == 12) {
1756  LTC_SaveRXtoGPIOBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1757  } else if (BS_MAX_SUPPORTED_CELLS > 12) {
1758  LTC_SaveRXtoGPIOBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1759  }
1760 
1761  LTC_SaveAllGPIOMeasurement(ltc_state);
1762 
1765  }
1766 
1767  break;
1768 
1769  /****************************BALANCE FEEDBACK*********************************/
1771 
1772  if (ltc_state->substate == LTC_ENTRY) {
1773  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1774  ltc_state->adcMode = LTC_ADCMODE_NORMAL_DCP0;
1776 
1777  ltc_state->check_spi_flag = STD_NOT_OK;
1778  retVal = LTC_StartGPIOMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1780  ltc_state,
1781  retVal,
1782  ltc_state->spiDiagErrorEntry,
1785  (ltc_state->commandDataTransferTime +
1786  LTC_Get_MeasurementTCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1790  break;
1791 
1792  } else if (ltc_state->substate == LTC_READ_FEEDBACK_BALANCECONTROL) {
1793  ltc_state->check_spi_flag = STD_OK;
1794  MIC_SetTransmitOngoing(ltc_state);
1795  retVal = LTC_RX(
1796  ltc_cmdRDAUXA,
1797  ltc_state->spiSeqPtr,
1798  ltc_state->ltcData.txBuffer,
1799  ltc_state->ltcData.rxBuffer,
1800  ltc_state->ltcData.frameLength); /* read AUXA register */
1802  ltc_state,
1803  retVal,
1804  ltc_state->spiDiagErrorEntry,
1811 
1812  } else if (ltc_state->substate == LTC_SAVE_FEEDBACK_BALANCECONTROL) {
1813  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1814  DIAG_Handler(
1817  break;
1818  } else {
1819  DIAG_Handler(
1820  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1821  }
1822 
1823  if (LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1824  DIAG_Handler(
1826  } else {
1827  DIAG_Handler(
1828  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1829  LTC_SaveBalancingFeedback(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1830  }
1832  break;
1833  }
1834  break;
1835 
1836  /****************************BOARD TEMPERATURE SENSOR*********************************/
1838 
1839  if (ltc_state->substate == LTC_TEMP_SENS_SEND_DATA1) {
1840  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1841  ltc_state->check_spi_flag = STD_OK;
1842  MIC_SetTransmitOngoing(ltc_state);
1843  retVal = LTC_Send_I2C_Command(
1844  ltc_state->spiSeqPtr,
1845  ltc_state->ltcData.txBuffer,
1846  ltc_state->ltcData.rxBuffer,
1847  ltc_state->ltcData.frameLength,
1849 
1850  if (retVal != STD_OK) {
1851  DIAG_Handler(
1853  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1855  } else {
1856  DIAG_Handler(
1857  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1859  ltc_state,
1863  }
1864 
1865  break;
1866 
1867  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM1) {
1868  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1869  DIAG_Handler(
1872  break;
1873  } else {
1874  DIAG_Handler(
1875  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1876  }
1877 
1878  MIC_SetTransmitOngoing(ltc_state);
1879  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
1881  ltc_state,
1882  retVal,
1883  ltc_state->spiDiagErrorEntry,
1890  break;
1891 
1892  } else if (ltc_state->substate == LTC_TEMP_SENS_READ_DATA1) {
1893  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1894  DIAG_Handler(
1897  break;
1898  } else {
1899  DIAG_Handler(
1900  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1901  }
1902 
1903  MIC_SetTransmitOngoing(ltc_state);
1904  retVal = LTC_Send_I2C_Command(
1905  ltc_state->spiSeqPtr,
1906  ltc_state->ltcData.txBuffer,
1907  ltc_state->ltcData.rxBuffer,
1908  ltc_state->ltcData.frameLength,
1910 
1911  if (retVal != STD_OK) {
1912  DIAG_Handler(
1914  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1916  } else {
1917  DIAG_Handler(
1918  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1920  ltc_state,
1924  }
1925 
1926  break;
1927 
1928  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM2) {
1929  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1930  DIAG_Handler(
1933  break;
1934  } else {
1935  DIAG_Handler(
1936  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1937  }
1938 
1939  MIC_SetTransmitOngoing(ltc_state);
1940  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
1942  ltc_state,
1943  retVal,
1944  ltc_state->spiDiagErrorEntry,
1951  break;
1953  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1954  DIAG_Handler(
1957  break;
1958  } else {
1959  DIAG_Handler(
1960  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1961  }
1962 
1963  MIC_SetTransmitOngoing(ltc_state);
1964  retVal = LTC_RX(
1965  ltc_cmdRDCOMM,
1966  ltc_state->spiSeqPtr,
1967  ltc_state->ltcData.txBuffer,
1968  ltc_state->ltcData.rxBuffer,
1969  ltc_state->ltcData.frameLength);
1971  ltc_state,
1972  retVal,
1973  ltc_state->spiDiagErrorEntry,
1980  break;
1981 
1982  } else if (ltc_state->substate == LTC_TEMP_SENS_SAVE_TEMP) {
1983  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
1984  DIAG_Handler(
1987  break;
1988  } else {
1989  DIAG_Handler(
1990  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1991  }
1992 
1993  if (LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1994  DIAG_Handler(
1996  } else {
1997  DIAG_Handler(
1998  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1999  LTC_TempSensSaveTemp(ltc_state, ltc_state->ltcData.rxBuffer);
2000  }
2001 
2003  break;
2004  }
2005  break;
2006 
2007  /****************************WRITE TO PORT EXPANDER IO***********/
2009 
2010  if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER) {
2011  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2012  ltc_state->check_spi_flag = STD_OK;
2013  MIC_SetTransmitOngoing(ltc_state);
2014  retVal = LTC_SetPortExpander(
2015  ltc_state,
2016  ltc_state->spiSeqPtr,
2017  ltc_state->ltcData.txBuffer,
2018  ltc_state->ltcData.rxBuffer,
2019  ltc_state->ltcData.frameLength);
2020 
2021  if (retVal != STD_OK) {
2022  DIAG_Handler(
2024  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2026  } else {
2027  DIAG_Handler(
2028  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2030  ltc_state,
2034  }
2035  break;
2036 
2037  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
2038  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2039  DIAG_Handler(
2042  break;
2043  } else {
2044  DIAG_Handler(
2045  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2046  }
2047 
2048  ltc_state->check_spi_flag = STD_NOT_OK;
2049  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2051  ltc_state,
2052  retVal,
2053  ltc_state->spiDiagErrorEntry,
2055  LTC_ENTRY,
2056  ltc_state->gpioClocksTransferTime,
2058  LTC_ENTRY,
2060  break;
2061  }
2062  break;
2063 
2064  /****************************READ FROM PORT EXPANDER IO***********/
2066 
2067  if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER) {
2068  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2069  ltc_state->check_spi_flag = STD_OK;
2070  MIC_SetTransmitOngoing(ltc_state);
2071  retVal = LTC_Send_I2C_Command(
2072  ltc_state->spiSeqPtr,
2073  ltc_state->ltcData.txBuffer,
2074  ltc_state->ltcData.rxBuffer,
2075  ltc_state->ltcData.frameLength,
2077 
2078  if (retVal != STD_OK) {
2079  DIAG_Handler(
2081  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2083  } else {
2084  DIAG_Handler(
2085  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2087  ltc_state,
2091  }
2092 
2093  break;
2094 
2095  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM) {
2096  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2097  DIAG_Handler(
2100  break;
2101  } else {
2102  DIAG_Handler(
2103  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2104  }
2105 
2106  ltc_state->check_spi_flag = STD_NOT_OK;
2107  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2109  ltc_state,
2110  retVal,
2111  ltc_state->spiDiagErrorEntry,
2114  ltc_state->gpioClocksTransferTime,
2118  break;
2119 
2120  } else if (ltc_state->substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2121  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2122  DIAG_Handler(
2125  break;
2126  } else {
2127  DIAG_Handler(
2128  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2129  }
2130 
2131  MIC_SetTransmitOngoing(ltc_state);
2132  retVal = LTC_RX(
2133  ltc_cmdRDCOMM,
2134  ltc_state->spiSeqPtr,
2135  ltc_state->ltcData.txBuffer,
2136  ltc_state->ltcData.rxBuffer,
2137  ltc_state->ltcData.frameLength);
2139  ltc_state,
2140  retVal,
2141  ltc_state->spiDiagErrorEntry,
2148  break;
2149 
2150  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA) {
2151  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2152  DIAG_Handler(
2155  break;
2156  } else {
2157  DIAG_Handler(
2158  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2159  }
2160 
2161  if (LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2162  DIAG_Handler(
2164  } else {
2165  DIAG_Handler(
2166  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2167  LTC_PortExpanderSaveValues(ltc_state, ltc_state->ltcData.rxBuffer);
2168  }
2169 
2171  break;
2172  }
2173 
2174  break;
2175 
2176  /****************************WRITE TO TI PORT EXPANDER IO***********/
2178 
2179  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2180  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2181  ltc_state->check_spi_flag = STD_OK;
2182  MIC_SetTransmitOngoing(ltc_state);
2184  ltc_state,
2185  ltc_state->spiSeqPtr,
2186  ltc_state->ltcData.txBuffer,
2187  ltc_state->ltcData.rxBuffer,
2188  ltc_state->ltcData.frameLength,
2191  ltc_state,
2192  retVal,
2193  ltc_state->spiDiagErrorEntry,
2198  LTC_ENTRY,
2200  break;
2201 
2202  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2203  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2204  DIAG_Handler(
2207  break;
2208 
2209  } else {
2210  DIAG_Handler(
2211  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2212  }
2213 
2214  ltc_state->check_spi_flag = STD_NOT_OK;
2215  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2217  ltc_state,
2218  retVal,
2219  ltc_state->spiDiagErrorEntry,
2222  ltc_state->gpioClocksTransferTime,
2224  LTC_ENTRY,
2226  break;
2227 
2228  } else if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER_TI) {
2229  ltc_state->check_spi_flag = STD_OK;
2230  MIC_SetTransmitOngoing(ltc_state);
2232  ltc_state,
2233  ltc_state->spiSeqPtr,
2234  ltc_state->ltcData.txBuffer,
2235  ltc_state->ltcData.rxBuffer,
2236  ltc_state->ltcData.frameLength);
2238  ltc_state,
2239  retVal,
2240  ltc_state->spiDiagErrorEntry,
2245  LTC_ENTRY,
2247  break;
2248 
2250  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2251  DIAG_Handler(
2254  break;
2255  } else {
2256  DIAG_Handler(
2257  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2258  }
2259 
2260  ltc_state->check_spi_flag = STD_NOT_OK;
2261  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2263  ltc_state,
2264  retVal,
2265  ltc_state->spiDiagErrorEntry,
2267  LTC_ENTRY,
2268  ltc_state->gpioClocksTransferTime,
2270  LTC_ENTRY,
2272  break;
2273  }
2274  break;
2275 
2276  /****************************READ TI PORT EXPANDER IO***********/
2278 
2279  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2280  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2281  ltc_state->check_spi_flag = STD_OK;
2282  MIC_SetTransmitOngoing(ltc_state);
2284  ltc_state,
2285  ltc_state->spiSeqPtr,
2286  ltc_state->ltcData.txBuffer,
2287  ltc_state->ltcData.rxBuffer,
2288  ltc_state->ltcData.frameLength,
2291  ltc_state,
2292  retVal,
2293  ltc_state->spiDiagErrorEntry,
2298  LTC_ENTRY,
2300  break;
2301 
2302  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2303  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2304  DIAG_Handler(
2307  break;
2308 
2309  } else {
2310  DIAG_Handler(
2311  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2312  }
2313 
2314  ltc_state->check_spi_flag = STD_NOT_OK;
2315  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2317  ltc_state,
2318  retVal,
2319  ltc_state->spiDiagErrorEntry,
2322  ltc_state->gpioClocksTransferTime,
2324  LTC_ENTRY,
2326  break;
2327 
2328  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST) {
2329  ltc_state->check_spi_flag = STD_OK;
2330  MIC_SetTransmitOngoing(ltc_state);
2332  ltc_state,
2333  ltc_state->spiSeqPtr,
2334  ltc_state->ltcData.txBuffer,
2335  ltc_state->ltcData.rxBuffer,
2336  ltc_state->ltcData.frameLength,
2337  0);
2339  ltc_state,
2340  retVal,
2341  ltc_state->spiDiagErrorEntry,
2346  LTC_ENTRY,
2348  break;
2349 
2351  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2352  DIAG_Handler(
2355  break;
2356  } else {
2357  DIAG_Handler(
2358  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2359  }
2360 
2361  ltc_state->check_spi_flag = STD_NOT_OK;
2362  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2364  ltc_state,
2365  retVal,
2366  ltc_state->spiDiagErrorEntry,
2369  ltc_state->gpioClocksTransferTime,
2371  LTC_ENTRY,
2373  break;
2374  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND) {
2375  ltc_state->check_spi_flag = STD_OK;
2376  MIC_SetTransmitOngoing(ltc_state);
2378  ltc_state,
2379  ltc_state->spiSeqPtr,
2380  ltc_state->ltcData.txBuffer,
2381  ltc_state->ltcData.rxBuffer,
2382  ltc_state->ltcData.frameLength,
2383  1);
2385  ltc_state,
2386  retVal,
2387  ltc_state->spiDiagErrorEntry,
2392  LTC_ENTRY,
2394  break;
2395 
2397  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2398  DIAG_Handler(
2401  break;
2402  } else {
2403  DIAG_Handler(
2404  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2405  }
2406 
2407  ltc_state->check_spi_flag = STD_NOT_OK;
2408  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2410  ltc_state,
2411  retVal,
2412  ltc_state->spiDiagErrorEntry,
2415  ltc_state->gpioClocksTransferTime,
2417  LTC_ENTRY,
2419  break;
2421  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2422  DIAG_Handler(
2425  break;
2426  } else {
2427  DIAG_Handler(
2428  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2429  }
2430 
2431  MIC_SetTransmitOngoing(ltc_state);
2432  retVal = LTC_RX(
2433  ltc_cmdRDCOMM,
2434  ltc_state->spiSeqPtr,
2435  ltc_state->ltcData.txBuffer,
2436  ltc_state->ltcData.rxBuffer,
2437  ltc_state->ltcData.frameLength);
2439  ltc_state,
2440  retVal,
2441  ltc_state->spiDiagErrorEntry,
2448  break;
2449 
2450  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA_TI) {
2451  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2452  DIAG_Handler(
2455  break;
2456  } else {
2457  DIAG_Handler(
2458  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2459  }
2460 
2461  if (LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2462  DIAG_Handler(
2464  } else {
2465  DIAG_Handler(
2466  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2467  LTC_PortExpanderSaveValues_TI(ltc_state, ltc_state->ltcData.txBuffer);
2468  }
2469 
2471  break;
2472  }
2473 
2474  break;
2475 
2476  /****************************EEPROM READ*********************************/
2478 
2479  if (ltc_state->substate == LTC_EEPROM_READ_DATA1) {
2480  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2481  ltc_state->check_spi_flag = STD_OK;
2482  MIC_SetTransmitOngoing(ltc_state);
2483  retVal = LTC_SendEEPROMReadCommand(
2484  ltc_state,
2485  ltc_state->spiSeqPtr,
2486  ltc_state->ltcData.txBuffer,
2487  ltc_state->ltcData.rxBuffer,
2488  ltc_state->ltcData.frameLength,
2489  0);
2490 
2491  if (retVal != STD_OK) {
2492  DIAG_Handler(
2494  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2496  } else {
2497  DIAG_Handler(
2498  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2500  ltc_state,
2504  }
2505 
2506  break;
2507 
2508  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM1) {
2509  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2510  DIAG_Handler(
2513  break;
2514  } else {
2515  DIAG_Handler(
2516  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2517  }
2518 
2519  MIC_SetTransmitOngoing(ltc_state);
2520  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2522  ltc_state,
2523  retVal,
2524  ltc_state->spiDiagErrorEntry,
2531  break;
2532 
2533  } else if (ltc_state->substate == LTC_EEPROM_READ_DATA2) {
2534  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2535  DIAG_Handler(
2538  break;
2539  } else {
2540  DIAG_Handler(
2541  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2542  }
2543 
2544  MIC_SetTransmitOngoing(ltc_state);
2545  retVal = LTC_SendEEPROMReadCommand(
2546  ltc_state,
2547  ltc_state->spiSeqPtr,
2548  ltc_state->ltcData.txBuffer,
2549  ltc_state->ltcData.rxBuffer,
2550  ltc_state->ltcData.frameLength,
2551  1);
2553  ltc_state,
2554  retVal,
2555  ltc_state->spiDiagErrorEntry,
2562  break;
2563 
2564  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM2) {
2565  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2566  DIAG_Handler(
2569  break;
2570  } else {
2571  DIAG_Handler(
2572  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2573  }
2574 
2575  MIC_SetTransmitOngoing(ltc_state);
2576  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2578  ltc_state,
2579  retVal,
2580  ltc_state->spiDiagErrorEntry,
2587  break;
2588  } else if (ltc_state->substate == LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2589  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2590  DIAG_Handler(
2593  break;
2594  } else {
2595  DIAG_Handler(
2596  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2597  }
2598 
2599  MIC_SetTransmitOngoing(ltc_state);
2600  retVal = LTC_RX(
2601  ltc_cmdRDCOMM,
2602  ltc_state->spiSeqPtr,
2603  ltc_state->ltcData.txBuffer,
2604  ltc_state->ltcData.rxBuffer,
2605  ltc_state->ltcData.frameLength);
2607  ltc_state,
2608  retVal,
2609  ltc_state->spiDiagErrorEntry,
2616  break;
2617 
2618  } else if (ltc_state->substate == LTC_EEPROM_SAVE_READ) {
2619  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2620  DIAG_Handler(
2623  break;
2624  } else {
2625  DIAG_Handler(
2626  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2627  }
2628 
2629  if (LTC_RX_PECCheck(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2630  DIAG_Handler(
2632  } else {
2633  DIAG_Handler(
2634  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2635  LTC_EEPROMSaveReadValue(ltc_state, ltc_state->ltcData.rxBuffer);
2636  }
2638  break;
2639  }
2640 
2641  break;
2642 
2643  /****************************EEPROM READ*********************************/
2645 
2646  if (ltc_state->substate == LTC_EEPROM_WRITE_DATA1) {
2647  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2648  ltc_state->check_spi_flag = STD_OK;
2649  MIC_SetTransmitOngoing(ltc_state);
2650  retVal = LTC_SendEEPROMWriteCommand(
2651  ltc_state,
2652  ltc_state->spiSeqPtr,
2653  ltc_state->ltcData.txBuffer,
2654  ltc_state->ltcData.rxBuffer,
2655  ltc_state->ltcData.frameLength,
2656  0);
2657 
2658  if (retVal != STD_OK) {
2659  DIAG_Handler(
2661  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2663  } else {
2664  DIAG_Handler(
2665  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2667  ltc_state,
2671  }
2672 
2673  break;
2674 
2675  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM3) {
2676  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2677  DIAG_Handler(
2680  break;
2681  } else {
2682  DIAG_Handler(
2683  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2684  }
2685 
2686  MIC_SetTransmitOngoing(ltc_state);
2687  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2689  ltc_state,
2690  retVal,
2691  ltc_state->spiDiagErrorEntry,
2698  break;
2699 
2700  } else if (ltc_state->substate == LTC_EEPROM_WRITE_DATA2) {
2701  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2702  DIAG_Handler(
2705  break;
2706  } else {
2707  DIAG_Handler(
2708  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2709  }
2710 
2711  MIC_SetTransmitOngoing(ltc_state);
2712  retVal = LTC_SendEEPROMWriteCommand(
2713  ltc_state,
2714  ltc_state->spiSeqPtr,
2715  ltc_state->ltcData.txBuffer,
2716  ltc_state->ltcData.rxBuffer,
2717  ltc_state->ltcData.frameLength,
2718  1);
2720  ltc_state,
2721  retVal,
2722  ltc_state->spiDiagErrorEntry,
2729  break;
2730 
2731  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM4) {
2732  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2733  DIAG_Handler(
2736  break;
2737  } else {
2738  DIAG_Handler(
2739  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2740  }
2741 
2742  MIC_SetTransmitOngoing(ltc_state);
2743  retVal = LTC_I2CClock(ltc_state->spiSeqPtr);
2745  ltc_state,
2746  retVal,
2747  ltc_state->spiDiagErrorEntry,
2754  break;
2755  } else if (ltc_state->substate == LTC_EEPROM_FINISHED) {
2756  if ((ltc_state->timer == 0) && (MIC_IsTransmitOngoing(ltc_state) == true)) {
2757  DIAG_Handler(
2760  break;
2761  } else {
2762  DIAG_Handler(
2763  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2764  }
2766  break;
2767  }
2768 
2769  break;
2770 
2771  /**************************OPEN-WIRE CHECK*******************************/
2773  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2774  /* This is necessary because the state machine will go through read voltage measurement registers */
2775  ltc_state->currentString = ltc_state->requestedString;
2777  /* Run ADOW command with PUP = 1 */
2778  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2779  ltc_state->check_spi_flag = STD_NOT_OK;
2780 
2781  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 1);
2782  if (retVal == STD_OK) {
2783  DIAG_Handler(
2784  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2786  ltc_state,
2789  (ltc_state->commandDataTransferTime +
2791  ltc_state->resendCommandCounter--;
2792 
2793  /* Check how many retries are left */
2794  if (ltc_state->resendCommandCounter == 0) {
2795  /* Switch to read voltage state to read cell voltages */
2797  ltc_state,
2800  (ltc_state->commandDataTransferTime +
2802  /* Reuse read voltage register */
2804  }
2805  } else {
2806  DIAG_Handler(
2810  }
2811  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK) {
2812  /* Previous state: Read voltage -> information stored in voltage buffer */
2814 
2815  /* Copy data from voltage struct into open-wire struct */
2816  for (uint16_t i = 0u; i < BS_NR_OF_BAT_CELLS; i++) {
2817  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] =
2818  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][i];
2819  }
2820 
2821  /* Set number of ADOW retries - send ADOW command with pull-down two times */
2824  ltc_state,
2828 
2829  } else if (ltc_state->substate == LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK) {
2830  /* Run ADOW command with PUP = 0 */
2831  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2832  ltc_state->check_spi_flag = STD_NOT_OK;
2833 
2834  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 0);
2835  if (retVal == STD_OK) {
2836  DIAG_Handler(
2837  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2839  ltc_state,
2842  (ltc_state->commandDataTransferTime +
2844  ltc_state->resendCommandCounter--;
2845 
2846  /* Check how many retries are left */
2847  if (ltc_state->resendCommandCounter == 0) {
2848  /* Switch to read voltage state to read cell voltages */
2850  ltc_state,
2853  (ltc_state->commandDataTransferTime +
2855  /* Reuse read voltage register */
2857  }
2858  } else {
2859  DIAG_Handler(
2863  }
2864  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLDOWN_OPENWIRE_CHECK) {
2865  /* Previous state: Read voltage -> information stored in voltage buffer */
2867 
2868  /* Copy data from voltage struct into open-wire struct */
2869  for (uint16_t i = 0u; i < BS_NR_OF_BAT_CELLS; i++) {
2870  ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][i] =
2871  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][i];
2872  }
2875 
2876  } else if (ltc_state->substate == LTC_PERFORM_OPENWIRE_CHECK) {
2877  /* Perform actual open-wire check */
2878  for (uint8_t m = 0; m < BS_NR_OF_MODULES; m++) {
2879  /* Open-wire at C0: cell_pup(0) == 0 */
2880  if (ltc_state->ltcData.openWireDetection
2881  ->openWirePup[ltc_state->requestedString][0 + (m * BS_NR_OF_CELLS_PER_MODULE)] == 0u) {
2882  ltc_state->ltcData.openWire
2883  ->openwire[ltc_state->requestedString][0 + (m * (BS_NR_OF_CELLS_PER_MODULE))] = 1u;
2884  }
2885  /* Open-wire at Cmax: cell_pdown(BS_NR_OF_CELLS_PER_MODULE-1) == 0 */
2886  if (ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][(
2887  (BS_NR_OF_CELLS_PER_MODULE - 1) + (m * BS_NR_OF_CELLS_PER_MODULE))] == 0u) {
2888  ltc_state->ltcData.openWire
2889  ->openwire[ltc_state->requestedString]
2891  }
2892  }
2893 
2894  /* Take difference between pull-up and pull-down measurement */
2895  for (uint16_t i = 1u; i < BS_NR_OF_BAT_CELLS; i++) {
2896  ltc_state->ltcData.openWireDetection->openWireDelta[ltc_state->requestedString][i] =
2897  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] -
2898  ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][i];
2899  }
2900 
2901  /* Open-wire at C(N): delta cell(n+1) < -400mV */
2902  for (uint8_t m = 0u; m < BS_NR_OF_MODULES; m++) {
2903  for (uint8_t c = 1u; c < (BS_NR_OF_CELLS_PER_MODULE - 1); c++) {
2904  if (ltc_state->ltcData.openWireDetection
2905  ->openWireDelta[ltc_state->requestedString][c + (m * BS_NR_OF_CELLS_PER_MODULE)] <
2907  ltc_state->ltcData.openWire
2908  ->openwire[ltc_state->requestedString][c + (m * BS_NR_OF_CELLS_PER_MODULE)] = 1;
2909  }
2910  }
2911  }
2912 
2913  /* Write database entry */
2914  DATA_WRITE_DATA(ltc_state->ltcData.openWire);
2915  /* Start new measurement cycle */
2917  }
2918  break;
2919 
2920  /****************************DEFAULT**************************/
2921  default:
2922  /* invalid state */
2924  break;
2925  }
2926 
2927  ltc_state->triggerentry--; /* reentrance counter */
2928  } /* continueFunction */
2929 }
2930 
2931 /**
2932  * @brief saves the multiplexer values read from the LTC daisy-chain.
2933  *
2934  * After a voltage measurement was initiated on GPIO 1 to read the currently selected
2935  * multiplexer voltage, the results is read via SPI from the daisy-chain.
2936  * This function is called to store the result from the transmission in a buffer.
2937  *
2938  * @param ltc_state state of the ltc state machine
2939  * @param pRxBuff receive buffer
2940  * @param muxseqptr pointer to the multiplexer sequence, which configures the currently selected multiplexer ID and channel
2941  * @param stringNumber string addressed
2942  *
2943  */
2945  LTC_STATE_s *ltc_state,
2946  uint16_t *pRxBuff,
2947  LTC_MUX_CH_CFG_s *muxseqptr,
2948  uint8_t stringNumber) {
2949  uint16_t val_ui = 0;
2950  int16_t temperature_ddegC = 0;
2951  uint8_t sensor_idx = 0;
2952  uint8_t ch_idx = 0;
2953  uint16_t buffer_LSB = 0;
2954  uint16_t buffer_MSB = 0;
2955 
2956  /* pointer to measurement Sequence of Mux- and Channel-Configurations (1,0xFF)...(3,0xFF),(0,1),...(0,7)) */
2957  /* Channel 0xFF means that the multiplexer is deactivated, therefore no measurement will be made and saved*/
2958  if (muxseqptr->muxCh != 0xFF) {
2959  /* user multiplexer type -> connected to GPIO2! */
2960  if ((muxseqptr->muxID == 1) || (muxseqptr->muxID == 2)) {
2961  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2962  if (muxseqptr->muxID == 1) {
2963  ch_idx = 0 + muxseqptr->muxCh; /* channel index 0..7 */
2964  } else {
2965  ch_idx = 8 + muxseqptr->muxCh; /* channel index 8..15 */
2966  }
2967 
2968  if (ch_idx < (2u * 8u)) {
2969  val_ui = *((uint16_t *)(&pRxBuff[6u + (1u * i * 8u)])); /* raw values, all mux on all LTCs */
2970  /* ltc_user_mux.value[i*8*2+ch_idx] = (uint16_t)(((float)(val_ui))*100e-6f*1000.0f); */ /* Unit -> in V -> in mV */
2971  }
2972  }
2973  } else {
2974  /* temperature multiplexer type -> connected to GPIO1! */
2975  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2976  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
2977  buffer_LSB = pRxBuff[4u + (i * 8u)];
2978  val_ui = buffer_LSB | (buffer_MSB << 8);
2979  /* val_ui = *((uint16_t *)(&pRxBuff[4+i*8])); */
2980  /* GPIO voltage in 100uV -> * 0.1 ---- conversion to mV */
2981  temperature_ddegC = LTC_Convert_MuxVoltages_to_Temperatures(val_ui / 10u); /* unit: deci &deg;C */
2982  sensor_idx = ltc_muxsensortemperatur_cfg[muxseqptr->muxCh];
2983  /* wrong configuration! */
2984  if (sensor_idx >= BS_NR_OF_TEMP_SENSORS_PER_MODULE) {
2986  }
2987  /* Set bitmask for valid flags */
2988 
2989  /* Check LTC PEC error */
2990  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
2991  /* Reset invalid flag */
2992  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] =
2993  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] &
2994  (~(1u << sensor_idx));
2995 
2996  ltc_state->ltcData.cellTemperature
2997  ->cellTemperature_ddegC[stringNumber][(i * (BS_NR_OF_TEMP_SENSORS_PER_MODULE)) + sensor_idx] =
2998  temperature_ddegC;
2999  } else {
3000  /* Set invalid flag */
3001  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] |= (1u << sensor_idx);
3002  }
3003  }
3004  }
3005  }
3006 }
3007 
3008 /**
3009  * @brief saves the voltage values read from the LTC daisy-chain.
3010  *
3011  * After a voltage measurement was initiated to measure the voltages of the cells,
3012  * the result is read via SPI from the daisy-chain.
3013  * There are 6 register to read _(A,B,C,D,E,F) to get all cell voltages.
3014  * Only one register can be read at a time.
3015  * This function is called to store the result from the transmission in a buffer.
3016  *
3017  * @param ltc_state state of the ltc state machine
3018  * @param pRxBuff receive buffer
3019  * @param registerSet voltage register that was read (voltage register A,B,C,D,E or F)
3020  * @param stringNumber string addressed
3021  *
3022  */
3024  LTC_STATE_s *ltc_state,
3025  uint16_t *pRxBuff,
3026  uint8_t registerSet,
3027  uint8_t stringNumber) {
3028  uint16_t cellOffset = 0;
3029  uint16_t voltage_index = 0;
3030  uint16_t val_ui = 0;
3031  uint16_t voltage = 0;
3032  uint32_t bitmask = 0;
3033  uint16_t buffer_LSB = 0;
3034  uint16_t buffer_MSB = 0;
3035  bool continueFunction = true;
3036 
3037  if (registerSet == 0u) {
3038  /* RDCVA command -> voltage register group A */
3039  cellOffset = 0;
3040  } else if (registerSet == 1u) {
3041  /* RDCVB command -> voltage register group B */
3042  cellOffset = 3;
3043  } else if (registerSet == 2u) {
3044  /* RDCVC command -> voltage register group C */
3045  cellOffset = 6;
3046  } else if (registerSet == 3u) {
3047  /* RDCVD command -> voltage register group D */
3048  cellOffset = 9;
3049  } else if (registerSet == 4u) {
3050  /* RDCVD command -> voltage register group E (only for 15 and 18 cell version) */
3051  cellOffset = 12;
3052  } else if (registerSet == 5u) {
3053  /* RDCVD command -> voltage register group F (only for 18 cell version) */
3054  cellOffset = 15;
3055  } else {
3056  continueFunction = false;
3057  }
3058 
3059  if (continueFunction == true) {
3060  /* Calculate bitmask for valid flags */
3061  bitmask |= 0x07u << cellOffset; /* 0x07: three voltages in each register */
3062 
3063  /* reinitialize index counter at begin of cycle */
3064  if (cellOffset == 0u) {
3065  (ltc_state->ltcData.usedCellIndex[stringNumber]) = 0;
3066  }
3067 
3068  /* Retrieve data without command and CRC*/
3069  for (uint16_t m = 0u; m < LTC_N_LTC; m++) {
3070  uint16_t incrementations = 0u;
3071 
3072  /* parse all three voltages (3 * 2bytes) contained in one register */
3073  for (uint8_t c = 0u; c < 3u; c++) {
3074  /* index considering maximum number of cells */
3075  voltage_index = c + cellOffset;
3076 
3077  if (ltc_voltage_input_used[voltage_index] == 1u) {
3078  buffer_MSB = pRxBuff[4u + (2u * c) + (m * 8u) + 1u];
3079  buffer_LSB = pRxBuff[4u + (2u * c) + (m * 8u)];
3080  val_ui = buffer_LSB | (buffer_MSB << 8u);
3081  /* val_ui = *((uint16_t *)(&pRxBuff[4+2*j+i*8])); */
3082  voltage = ((val_ui)) * 100e-6f * 1000.0f; /* Unit V -> in mV */
3083 
3084  /* Check PEC for every LTC in the daisy-chain */
3085  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][m] == true) {
3086  ltc_state->ltcData.cellVoltage
3087  ->cellVoltage_mV[stringNumber]
3088  [(ltc_state->ltcData.usedCellIndex[stringNumber]) +
3089  (m * BS_NR_OF_CELLS_PER_MODULE)] = voltage;
3090  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3091  ltc_state->ltcData.cellVoltage
3092  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] &= bitmask;
3093  } else {
3094  /* PEC_valid == false: Invalidate only flags of this voltage register */
3095  ltc_state->ltcData.cellVoltage
3096  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] |= bitmask;
3097  }
3098 
3099  (ltc_state->ltcData.usedCellIndex[stringNumber])++;
3100  incrementations++;
3101 
3102  if ((ltc_state->ltcData.usedCellIndex[stringNumber]) > BS_NR_OF_CELLS_PER_MODULE) {
3103  break;
3104  }
3105  }
3106  }
3107 
3108  /* Restore start value for next module in the daisy-chain. Only
3109  * decrement used cell index if current module is not the last
3110  * module in the daisy-chain. */
3111  if ((m + 1u) < LTC_N_LTC) {
3112  (ltc_state->ltcData.usedCellIndex[stringNumber]) -= incrementations;
3113  }
3114  }
3115  }
3116 }
3117 
3118 /**
3119  * @brief saves the GPIO voltage values read from the LTC daisy-chain.
3120  *
3121  * After a voltage measurement was initiated to measure the voltages on all GPIOs,
3122  * the result is read via SPI from the daisy-chain. In order to read the result of all GPIO measurements,
3123  * it is necessary to read auxiliary register A and B.
3124  * Only one register can be read at a time.
3125  * This function is called to store the result from the transmission in a buffer.
3126  *
3127  * @param ltc_state state of the ltc state machine
3128  * @param pRxBuff receive buffer
3129  * @param registerSet voltage register that was read (auxiliary register A, B, C or D)
3130  * @param stringNumber string addressed
3131  *
3132  */
3134  LTC_STATE_s *ltc_state,
3135  uint16_t *pRxBuff,
3136  uint8_t registerSet,
3137  uint8_t stringNumber) {
3138  uint8_t i_offset = 0;
3139  uint32_t bitmask = 0;
3140  uint16_t buffer_LSB = 0;
3141  uint16_t buffer_MSB = 0;
3142 
3143  if (registerSet == 0u) {
3144  /* RDAUXA command -> GPIO register group A */
3145  i_offset = 0;
3146  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3147  /* Retrieve data without command and CRC*/
3148  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3149  /* Check if PEC is valid */
3150  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3151  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3152  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3153  /* values received in 100uV -> divide by 10 to convert to mV */
3154  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3155  buffer_LSB = pRxBuff[4u + (i * 8u)];
3156  ltc_state->ltcData.allGpioVoltages
3157  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3158  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3159  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3160  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3161  buffer_LSB = pRxBuff[6u + (i * 8u)];
3162  ltc_state->ltcData.allGpioVoltages
3163  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3164  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3165  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3166  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3167  buffer_LSB = pRxBuff[8u + (i * 8u)];
3168  ltc_state->ltcData.allGpioVoltages
3169  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3170  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3171  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3172  } else {
3173  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3174  }
3175  }
3176  } else if (registerSet == 1u) {
3177  /* RDAUXB command -> GPIO register group B */
3178  i_offset = 3;
3179  bitmask = 0x03u << i_offset; /* 0x03: two temperatures in this register */
3180  /* Retrieve data without command and CRC*/
3181  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3182  /* Check if PEC is valid */
3183  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3184  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3185  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3186  /* values received in 100uV -> divide by 10 to convert to mV */
3187  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3188  buffer_LSB = pRxBuff[4u + (i * 8u)];
3189  ltc_state->ltcData.allGpioVoltages
3190  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3191  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3192  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3193  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3194  buffer_LSB = pRxBuff[6u + (i * 8u)];
3195  ltc_state->ltcData.allGpioVoltages
3196  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3197  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3198  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3199  } else {
3200  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3201  }
3202  }
3203  } else if (registerSet == 2u) {
3204  /* RDAUXC command -> GPIO register group C, for 18 cell version */
3205  i_offset = 5;
3206  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3207  /* Retrieve data without command and CRC*/
3208  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3209  /* Check if PEC is valid */
3210  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3211  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3212  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3213  /* values received in 100uV -> divide by 10 to convert to mV */
3214  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3215  buffer_LSB = pRxBuff[4u + (i * 8u)];
3216  ltc_state->ltcData.allGpioVoltages
3217  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3218  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3219  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3220  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3221  buffer_LSB = pRxBuff[6u + (i * 8u)];
3222  ltc_state->ltcData.allGpioVoltages
3223  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3224  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3225  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3226  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3227  buffer_LSB = pRxBuff[8u + (i * 8u)];
3228  ltc_state->ltcData.allGpioVoltages
3229  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3230  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3231  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3232  } else {
3233  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3234  }
3235  }
3236  } else if (registerSet == 3u) {
3237  /* RDAUXD command -> GPIO register group D, for 18 cell version */
3238  i_offset = 8;
3239  bitmask = 0x01u << i_offset; /* 0x01: one temperature in this register */
3240  /* Retrieve data without command and CRC*/
3241  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3242  /* Check if PEC is valid */
3243  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3244  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3245  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3246  /* values received in 100uV -> divide by 10 to convert to mV */
3247  ltc_state->ltcData.allGpioVoltages
3248  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3249  *((uint16_t *)(&pRxBuff[4u + (i * 8u)])) / 10u;
3250  } else {
3251  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3252  }
3253  }
3254  } else {
3255  ; /* Nothing to do */
3256  }
3257 }
3258 
3259 /**
3260  * @brief checks if the multiplexers acknowledged transmission.
3261  *
3262  * The RDCOMM command can be used to read the answer of the multiplexers to a
3263  * I2C transmission.
3264  * This function determines if the communication with the multiplexers was
3265  * successful or not.
3266  * The array error table is updated to locate the multiplexers that did not
3267  * acknowledge transmission.
3268  *
3269  * @param ltc_state state of the ltc state machine
3270  * @param pRxBuff receive buffer
3271  * @param mux multiplexer to be addressed (multiplexer ID)
3272  * @param stringNumber string addressed
3273  *
3274  * @return muxError STD_OK is there was no error, STD_NOT_OK if there was errors
3275  */
3276 static STD_RETURN_TYPE_e LTC_I2CCheckACK(LTC_STATE_s *ltc_state, uint16_t *pRxBuff, uint8_t mux, uint8_t stringNumber) {
3277  STD_RETURN_TYPE_e muxError = STD_OK;
3278 
3279  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
3280  if ((pRxBuff[4u + 1u + (LTC_NUMBER_OF_LTC_PER_MODULE * i * 8u)] & 0x0Fu) != 0x07u) { /* ACK = 0xX7 */
3281  if (LTC_DISCARD_MUX_CHECK == false) {
3282  if (mux == 0u) {
3283  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 1;
3284  }
3285  if (mux == 1u) {
3286  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 1;
3287  }
3288  if (mux == 2u) {
3289  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 1;
3290  }
3291  if (mux == 3u) {
3292  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 1;
3293  }
3294  }
3295  muxError = STD_NOT_OK;
3296  } else {
3297  if (mux == 0u) {
3298  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 0;
3299  }
3300  if (mux == 1u) {
3301  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 0;
3302  }
3303  if (mux == 2u) {
3304  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 0;
3305  }
3306  if (mux == 3u) {
3307  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 0;
3308  }
3309  }
3310  }
3311 
3312  if (LTC_DISCARD_MUX_CHECK == true) {
3313  muxError = STD_OK;
3314  }
3315  return muxError;
3316 }
3317 
3318 /**
3319  * @brief initialize the daisy-chain.
3320  *
3321  * To initialize the LTC6804 daisy-chain, a dummy byte (0x00) is sent.
3322  *
3323  * @param pSpiInterface pointer to SPI configuration
3324  * @param pTxBuff transmit buffer
3325  * @param pRxBuff receive buffer
3326  * @param frameLength number of words to transmit
3327  *
3328  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3329  *
3330  */
3332  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3333  uint16_t *pTxBuff,
3334  uint16_t *pRxBuff,
3335  uint32_t frameLength) {
3336  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3337 
3338  uint8_t PEC_Check[6];
3339  uint16_t PEC_result = 0;
3340 
3341  /* now construct the message to be sent: it contains the wanted data, PLUS the needed PECs */
3342  pTxBuff[0] = ltc_cmdWRCFG[0];
3343  pTxBuff[1] = ltc_cmdWRCFG[1];
3344  pTxBuff[2] = ltc_cmdWRCFG[2];
3345  pTxBuff[3] = ltc_cmdWRCFG[3];
3346 
3347  /* set REFON bit to 1 */
3348  /* data for the configuration */
3349  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3350  /* FC = disable all pull-downs, REFON = 1, DTEN = 0, ADCOPT = 0 */
3351  pTxBuff[4u + (i * 8u)] = 0xFC;
3352  pTxBuff[5u + (i * 8u)] = 0x00;
3353  pTxBuff[6u + (i * 8u)] = 0x00;
3354  pTxBuff[7u + (i * 8u)] = 0x00;
3355  pTxBuff[8u + (i * 8u)] = 0x00;
3356  pTxBuff[9u + (i * 8u)] = 0x00;
3357 
3358  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3359  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3360  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3361  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3362  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3363  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3364 
3365  PEC_result = LTC_pec15_calc(6, PEC_Check);
3366  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3367  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3368  } /* end for */
3369 
3370  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3371 
3372  return retVal;
3373 }
3374 
3375 /**
3376  * @brief sets the balancing according to the control values read in the database.
3377  *
3378  * To set balancing for the cells, the corresponding bits have to be written in the configuration register.
3379  * The LTC driver only executes the balancing orders written by the BMS in the database.
3380  *
3381  * @param ltc_state state of the ltc state machine
3382  * @param pSpiInterface pointer to SPI configuration
3383  * @param pTxBuff transmit buffer
3384  * @param pRxBuff receive buffer
3385  * @param frameLength number of words to transmit
3386  * @param registerSet register Set, 0: cells 1 to 12 (WRCFG), 1: cells 13 to 15/18 (WRCFG2)
3387  * @param stringNumber string addressed
3388  *
3389  * @return STD_OK if dummy byte was sent correctly by SPI, STD_NOT_OK otherwise
3390  *
3391  */
3393  LTC_STATE_s *ltc_state,
3394  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3395  uint16_t *pTxBuff,
3396  uint16_t *pRxBuff,
3397  uint32_t frameLength,
3398  uint8_t registerSet,
3399  uint8_t stringNumber) {
3400  STD_RETURN_TYPE_e retVal = STD_OK;
3401 
3402  uint8_t PEC_Check[6];
3403  uint16_t PEC_result = 0;
3404 
3405  LTC_Get_BalancingControlValues(ltc_state);
3406 
3407  if (registerSet == 0u) { /* cells 1 to 12, WRCFG */
3408  pTxBuff[0] = ltc_cmdWRCFG[0];
3409  pTxBuff[1] = ltc_cmdWRCFG[1];
3410  pTxBuff[2] = ltc_cmdWRCFG[2];
3411  pTxBuff[3] = ltc_cmdWRCFG[3];
3412  for (uint16_t j = 0; j < BS_NR_OF_MODULES; j++) {
3413  const uint16_t i = BS_NR_OF_MODULES - j - 1u;
3414 
3415  /* FC = disable all pull-downs, REFON = 1 (reference always on), DTEN off, ADCOPT = 0 */
3416  pTxBuff[4u + (i * 8u)] = 0xFC;
3417  pTxBuff[5u + (i * 8u)] = 0x00;
3418  pTxBuff[6u + (i * 8u)] = 0x00;
3419  pTxBuff[7u + (i * 8u)] = 0x00;
3420  pTxBuff[8u + (i * 8u)] = 0x00;
3421  pTxBuff[9u + (i * 8u)] = 0x00;
3422 
3423  if (ltc_state->ltcData.balancingControl
3424  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 0u] == 1u) {
3425  pTxBuff[8u + (i * 8u)] |= 0x01u;
3426  }
3427  if (ltc_state->ltcData.balancingControl
3428  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 1u] == 1u) {
3429  pTxBuff[8u + (i * 8u)] |= 0x02u;
3430  }
3431  if (ltc_state->ltcData.balancingControl
3432  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 2u] == 1u) {
3433  pTxBuff[8u + (i * 8u)] |= 0x04u;
3434  }
3435  if (ltc_state->ltcData.balancingControl
3436  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 3u] == 1u) {
3437  pTxBuff[8u + (i * 8u)] |= 0x08u;
3438  }
3439  if (ltc_state->ltcData.balancingControl
3440  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 4u] == 1u) {
3441  pTxBuff[8u + (i * 8u)] |= 0x10u;
3442  }
3443  if (ltc_state->ltcData.balancingControl
3444  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 5u] == 1u) {
3445  pTxBuff[8u + (i * 8u)] |= 0x20u;
3446  }
3447  if (ltc_state->ltcData.balancingControl
3448  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 6u] == 1u) {
3449  pTxBuff[8u + (i * 8u)] |= 0x40u;
3450  }
3451  if (ltc_state->ltcData.balancingControl
3452  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 7u] == 1u) {
3453  pTxBuff[8u + (i * 8u)] |= 0x80u;
3454  }
3455  if (ltc_state->ltcData.balancingControl
3456  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 8u] == 1u) {
3457  pTxBuff[9u + (i * 8u)] |= 0x01u;
3458  }
3459  if (ltc_state->ltcData.balancingControl
3460  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 9u] == 1u) {
3461  pTxBuff[9u + (i * 8u)] |= 0x02u;
3462  }
3463  if (ltc_state->ltcData.balancingControl
3464  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 10u] == 1u) {
3465  pTxBuff[9u + (i * 8u)] |= 0x04u;
3466  }
3467  if (ltc_state->ltcData.balancingControl
3468  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 11u] == 1u) {
3469  pTxBuff[9u + (i * 8u)] |= 0x08u;
3470  }
3471 
3472  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3473  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3474  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3475  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3476  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3477  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3478 
3479  PEC_result = LTC_pec15_calc(6, PEC_Check);
3480  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3481  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3482  }
3483  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3484  } else if (registerSet == 1u) { /* cells 13 to 15/18 WRCFG2 */
3485  pTxBuff[0] = ltc_cmdWRCFG2[0];
3486  pTxBuff[1] = ltc_cmdWRCFG2[1];
3487  pTxBuff[2] = ltc_cmdWRCFG2[2];
3488  pTxBuff[3] = ltc_cmdWRCFG2[3];
3489  for (uint16_t j = 0; j < BS_NR_OF_MODULES; j++) {
3490  const uint16_t i = BS_NR_OF_MODULES - j - 1;
3491 
3492  /* 0x0F = disable pull-downs on GPIO6-9 */
3493  pTxBuff[4u + (i * 8u)] = 0x0F;
3494  pTxBuff[5u + (i * 8u)] = 0x00;
3495  pTxBuff[6u + (i * 8u)] = 0x00;
3496  pTxBuff[7u + (i * 8u)] = 0x00;
3497  pTxBuff[8u + (i * 8u)] = 0x00;
3498  pTxBuff[9u + (i * 8u)] = 0x00;
3499 
3500  if (ltc_state->ltcData.balancingControl
3501  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 12u] == 1u) {
3502  pTxBuff[4u + (i * 8u)] |= 0x10u;
3503  }
3504  if (ltc_state->ltcData.balancingControl
3505  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 13u] == 1u) {
3506  pTxBuff[4u + (i * 8u)] |= 0x20u;
3507  }
3508  if (ltc_state->ltcData.balancingControl
3509  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 14u] == 1u) {
3510  pTxBuff[4u + (i * 8u)] |= 0x40u;
3511  }
3512  if (BS_NR_OF_CELLS_PER_MODULE > 15u) {
3513  if (ltc_state->ltcData.balancingControl
3514  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 15u] == 1u) {
3515  pTxBuff[4u + (i * 8u)] |= 0x80u;
3516  }
3517  if (ltc_state->ltcData.balancingControl
3518  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 16u] == 1u) {
3519  pTxBuff[5u + (i * 8u)] |= 0x01u;
3520  }
3521  if (ltc_state->ltcData.balancingControl
3522  ->balancingState[stringNumber][(j * (BS_NR_OF_CELLS_PER_MODULE)) + 17u] == 1u) {
3523  pTxBuff[5u + (i * 8u)] |= 0x02u;
3524  }
3525  }
3526 
3527  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3528  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3529  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3530  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3531  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3532  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3533 
3534  PEC_result = LTC_pec15_calc(6, PEC_Check);
3535  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3536  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3537  }
3538  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3539  } else {
3540  retVal = STD_NOT_OK;
3541  }
3542  return retVal;
3543 }
3544 
3545 /**
3546  * @brief resets the error table.
3547  *
3548  * This function should be called during initialization or before starting a new measurement cycle
3549  *
3550  * @param ltc_state: state of the ltc state machine
3551  *
3552  */
3553 static void LTC_ResetErrorTable(LTC_STATE_s *ltc_state) {
3554  for (uint8_t stringNumber = 0u; stringNumber < BS_NR_OF_STRINGS; stringNumber++) {
3555  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3556  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = false;
3557  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 0;
3558  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 0;
3559  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 0;
3560  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 0;
3561  }
3562  }
3563 }
3564 
3565 /**
3566  * @brief brief missing
3567  *
3568  * Gets the measurement time needed by the LTC chip, depending on the measurement mode and the number of channels.
3569  * For all cell voltages or all 5 GPIOS, the measurement time is the same.
3570  * For 2 cell voltages or 1 GPIO, the measurement time is the same.
3571  * As a consequence, this function is used for cell voltage and for GPIO measurement.
3572  *
3573  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3574  * @param adcMeasCh number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
3575  * or number of cell voltage measured (2 cells or all cells)
3576  *
3577  * @return retVal measurement time in ms
3578  */
3579 static uint16_t LTC_Get_MeasurementTCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh) {
3580  uint16_t retVal = LTC_STATEMACH_MEAS_ALL_NORMAL_TCYCLE; /* default */
3581 
3582  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL) {
3583  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3585  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3587  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3589  }
3590  } else if (
3591  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) ||
3592  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO4) ||
3593  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO5) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS)) {
3594  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3596  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3598  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3600  }
3601  } else {
3603  }
3604 
3605  return retVal;
3606 }
3607 
3608 /**
3609  * @brief tells the LTC daisy-chain to start measuring the voltage on all cells.
3610  *
3611  * This function sends an instruction to the daisy-chain via SPI, in order to start voltage measurement for all cells.
3612  *
3613  * @param pSpiInterface pointer to SPI configuration
3614  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3615  * @param adcMeasCh number of cell voltage measured (2 cells or all cells)
3616  *
3617  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3618  *
3619  */
3621  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3622  LTC_ADCMODE_e adcMode,
3623  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3624  STD_RETURN_TYPE_e retVal = STD_OK;
3625 
3626  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL) {
3627  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3628  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_fast_DCP0);
3629  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3630  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_normal_DCP0);
3631  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3632  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_filtered_DCP0);
3633  } else if (adcMode == LTC_ADCMODE_FAST_DCP1) {
3634  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_fast_DCP1);
3635  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP1) {
3636  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_normal_DCP1);
3637  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP1) {
3638  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_filtered_DCP1);
3639  } else {
3640  retVal = STD_NOT_OK;
3641  }
3642  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
3643  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3644  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_fast_DCP0_twocells);
3645  } else {
3646  retVal = STD_NOT_OK;
3647  }
3648  } else {
3649  retVal = STD_NOT_OK;
3650  }
3651  return retVal;
3652 }
3653 
3654 /**
3655  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3656  *
3657  * This function sends an instruction to the daisy-chain via SPI to start the measurement.
3658  *
3659  * @param pSpiInterface pointer to SPI configuration
3660  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3661  * @param adcMeasCh number of channels measured for GPIOS (one at a time, typically when multiplexers are used, or all five GPIOs)
3662  *
3663  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3664  *
3665  */
3667  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3668  LTC_ADCMODE_e adcMode,
3669  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3670  STD_RETURN_TYPE_e retVal;
3671 
3672  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL) {
3673  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3674  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_ALLGPIOS);
3675  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3676  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_ALLGPIOS);
3677  } else {
3678  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3679  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_ALLGPIOS);
3680  }
3681  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) {
3682  /* Single Channel */
3683  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3684  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_GPIO1);
3685  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3686  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_GPIO1);
3687  } else {
3688  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3689 
3690  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_GPIO1);
3691  }
3692  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) {
3693  /* Single Channel */
3694  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3695  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_GPIO2);
3696  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3697  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_GPIO2);
3698  } else {
3699  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3700 
3701  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_GPIO2);
3702  }
3703  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) {
3704  /* Single Channel */
3705  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3706  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_GPIO3);
3707  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3708  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_GPIO3);
3709  } else {
3710  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3711 
3712  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_GPIO3);
3713  }
3714  } else {
3715  retVal = STD_NOT_OK;
3716  }
3717 
3718  return retVal;
3719 }
3720 
3721 /**
3722  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3723  *
3724  * This function sends an instruction to the daisy-chain via SPI to start the measurement.
3725  *
3726  * @param pSpiInterface pointer to SPI configuration
3727  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3728  * @param PUP pull-up bit for pull-up or pull-down current (0: pull-down, 1: pull-up)
3729  *
3730  * @return retVal #STD_OK if command was sent correctly by SPI, #STD_NOT_OK otherwise
3731  *
3732  */
3734  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3735  LTC_ADCMODE_e adcMode,
3736  uint8_t PUP) {
3737  STD_RETURN_TYPE_e retval = STD_NOT_OK;
3738  if (PUP == 0u) {
3739  /* pull-down current */
3740  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3741  retval = LTC_TransmitCommand(pSpiInterface, ltc_BC_cmdADOW_PDOWN_normal_DCP0);
3742  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3744  } else {
3745  retval = STD_NOT_OK;
3746  }
3747  } else if (PUP == 1u) {
3748  /* pull-up current */
3749  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3750  retval = LTC_TransmitCommand(pSpiInterface, ltc_BC_cmdADOW_PUP_normal_DCP0);
3751  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3752  retval = LTC_TransmitCommand(pSpiInterface, ltc_BC_cmdADOW_PUP_filtered_DCP0);
3753  } else {
3754  retval = STD_NOT_OK;
3755  }
3756  }
3757  return retval;
3758 }
3759 
3760 /**
3761  * @brief checks if the data received from the daisy-chain is not corrupt.
3762  *
3763  * This function computes the PEC (CRC) from the data received by the daisy-chain.
3764  * It compares it with the PEC sent by the LTCs.
3765  * If there are errors, the array LTC_ErrorTable is updated to locate the LTCs in daisy-chain
3766  * that transmitted corrupt data.
3767  *
3768  * @param ltc_state state of the ltc state machine
3769  * @param DataBufferSPI_RX_with_PEC data obtained from the SPI transmission
3770  * @param stringNumber string addressed
3771  *
3772  * @return retVal STD_OK if PEC check is OK, STD_NOT_OK otherwise
3773  *
3774  */
3776  LTC_STATE_s *ltc_state,
3777  uint16_t *DataBufferSPI_RX_with_PEC,
3778  uint8_t stringNumber) {
3779  STD_RETURN_TYPE_e retVal = STD_OK;
3780  uint8_t PEC_TX[2];
3781  uint16_t PEC_result = 0;
3782  uint8_t PEC_Check[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3783 
3784  /* check all PECs and put data without command and PEC in DataBufferSPI_RX (easier to use) */
3785  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3786  PEC_Check[0] = DataBufferSPI_RX_with_PEC[4u + (i * 8u)];
3787  PEC_Check[1] = DataBufferSPI_RX_with_PEC[5u + (i * 8u)];
3788  PEC_Check[2] = DataBufferSPI_RX_with_PEC[6u + (i * 8u)];
3789  PEC_Check[3] = DataBufferSPI_RX_with_PEC[7u + (i * 8u)];
3790  PEC_Check[4] = DataBufferSPI_RX_with_PEC[8u + (i * 8u)];
3791  PEC_Check[5] = DataBufferSPI_RX_with_PEC[9u + (i * 8u)];
3792 
3793  PEC_result = LTC_pec15_calc(6, PEC_Check);
3794  PEC_TX[0] = (uint8_t)((PEC_result >> 8u) & 0xFFu);
3795  PEC_TX[1] = (uint8_t)(PEC_result & 0xFFu);
3796 
3797  /* if calculated PEC not equal to received PEC */
3798  if ((PEC_TX[0] != DataBufferSPI_RX_with_PEC[10u + (i * 8u)]) ||
3799  (PEC_TX[1] != DataBufferSPI_RX_with_PEC[11u + (i * 8u)])) {
3800  /* update error table of the corresponding LTC only if PEC check is activated */
3801  if (LTC_DISCARD_PEC == false) {
3802  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = false;
3803  retVal = STD_NOT_OK;
3804  } else {
3805  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3806  }
3807  } else {
3808  /* update error table of the corresponding LTC */
3809  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3810  }
3811  }
3812  return retVal;
3813 }
3814 
3815 /**
3816  * @brief send command to the LTC daisy-chain and receives data from the LTC daisy-chain.
3817  *
3818  * This is the core function to receive data from the LTC6804 daisy-chain.
3819  * A 2 byte command is sent with the corresponding PEC. Example: read configuration register (RDCFG).
3820  * Only command has to be set, the function calculates the PEC automatically.
3821  * The data send is:
3822  * 2 bytes (COMMAND) 2 bytes (PEC)
3823  * The data received is:
3824  * 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)
3825  *
3826  * The function does not check the PECs. This has to be done elsewhere.
3827  *
3828  * @param Command command sent to the daisy-chain
3829  * @param pSpiInterface pointer to SPI configuration
3830  * @param pTxBuff transmit buffer
3831  * @param pRxBuff receive buffer
3832  * @param frameLength number of words to transmit
3833  *
3834  * @return statusSPI #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
3835  *
3836  */
3838  uint16_t *Command,
3839  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3840  uint16_t *pTxBuff,
3841  uint16_t *pRxBuff,
3842  uint32_t frameLength) {
3843  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3844 
3845  /* DataBufferSPI_RX_with_PEC contains the data to receive.
3846  The transmission function checks the PECs.
3847  It constructs DataBufferSPI_RX, which contains the received data without PEC (easier to use). */
3848 
3849  for (uint16_t i = 0; i < LTC_N_BYTES_FOR_DATA_TRANSMISSION; i++) {
3850  pTxBuff[i] = 0x00;
3851  }
3852 
3853  pTxBuff[0] = Command[0];
3854  pTxBuff[1] = Command[1];
3855  pTxBuff[2] = Command[2];
3856  pTxBuff[3] = Command[3];
3857 
3858  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3859 
3860  return retVal;
3861 }
3862 
3863 /**
3864  * @brief sends command and data to the LTC daisy-chain.
3865  *
3866  * This is the core function to transmit data to the LTC6804 daisy-chain.
3867  * The data sent is:
3868  * COMMAND + 6 bytes (LTC1) + 6 bytes (LTC2) + 6 bytes (LTC3) + ... + 6 bytes (LTC{LTC_N_LTC})
3869  * A 2 byte command is sent with the corresponding PEC. Example: write configuration register (WRCFG).
3870  * THe command has to be set and then the function calculates the PEC automatically.
3871  * The function calculates the needed PEC to send the data to the daisy-chain. The sent data has the format:
3872  * 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)
3873  *
3874  * The function returns 0. The only way to check if the transmission was successful is to read the results of the write operation.
3875  * (example: read configuration register after writing to it)
3876  *
3877  * @param Command command sent to the daisy-chain
3878  * @param pSpiInterface pointer to SPI configuration
3879  * @param pTxBuff transmit buffer
3880  * @param pRxBuff receive buffer
3881  * @param frameLength number of words to transmit
3882  *
3883  * @return STD_OK if SPI transmission is OK, STD_NOT_OK otherwise
3884  *
3885  */
3887  uint16_t *Command,
3888  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3889  uint16_t *pTxBuff,
3890  uint16_t *pRxBuff,
3891  uint32_t frameLength) {
3892  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3893 
3894  uint16_t PEC_result = 0;
3895  uint8_t PEC_Check[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3896 
3897  pTxBuff[0] = Command[0];
3898  pTxBuff[1] = Command[1];
3899  pTxBuff[2] = Command[2];
3900  pTxBuff[3] = Command[3];
3901 
3902  /* Calculate PEC of all data (1 PEC value for 6 bytes) */
3903  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3904  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3905  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3906  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3907  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3908  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3909  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3910 
3911  PEC_result = LTC_pec15_calc(6, PEC_Check);
3912  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3913  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3914  }
3915 
3916  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3917 
3918  return retVal;
3919 }
3920 
3921 /**
3922  * @brief configures the data that will be sent to the LTC daisy-chain to configure multiplexer channels.
3923  *
3924  * This function does not sent the data to the multiplexer daisy-chain. This is done
3925  * by the function LTC_SetMuxChannel(), which calls LTC_SetMUXChCommand()..
3926  *
3927  * @param pTxBuff transmit buffer
3928  * @param mux multiplexer ID to be configured (0,1,2 or 3)
3929  * @param channel multiplexer channel to be configured (0 to 7)
3930  *
3931  */
3932 static void LTC_SetMUXChCommand(uint16_t *pTxBuff, uint8_t mux, uint8_t channel) {
3933  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3934 #if SLAVE_BOARD_VERSION == 2u
3935 
3936  /* using ADG728 */
3937  uint8_t address = 0x98u | ((mux % 4u) << 1u);
3938  uint8_t data = 1u << (channel % 8u);
3939  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
3940  data = 0x00;
3941  }
3942 
3943 #else
3944 
3945  /* using LTC1380 */
3946  uint8_t address = 0x90u | ((mux % 4u) << 1u);
3947  uint8_t data = 0x08u | (channel % 8u);
3948  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
3949  data = 0x00;
3950  }
3951 
3952 #endif
3953 
3954  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | ((address >> 4u) & 0x0Fu); /* 0x6 : LTC6804: ICOM START from Master */
3955  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | ((address << 4u) & 0xF0u);
3956  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | ((data >> 4u) & 0x0Fu);
3957  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | ((data << 4u) & 0xF0u);
3958  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 0x1 : ICOM-STOP */
3959  pTxBuff[9u + (i * 8u)] = 0x00; /* 0x0 : dummy (Dn) */
3960  /* 9: MASTER NACK + STOP (FCOM) */
3961  }
3962 }
3963 
3964 /**
3965  * @brief sends data to the LTC daisy-chain to read EEPROM on slaves.
3966  *
3967  * @param ltc_state state of the ltc state machine
3968  * @param pSpiInterface pointer to SPI configuration
3969  * @param pTxBuff transmit buffer
3970  * @param pRxBuff receive buffer
3971  * @param frameLength number of words to transmit
3972  * @param step first or second stage of read process (0 or 1)
3973  *
3974  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
3975  */
3977  LTC_STATE_s *ltc_state,
3978  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3979  uint16_t *pTxBuff,
3980  uint16_t *pRxBuff,
3981  uint32_t frameLength,
3982  uint8_t step) {
3983  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
3984 
3985  /* send WRCOMM to send I2C message to choose channel */
3986  LTC_SetEEPROMReadCommand(ltc_state, pTxBuff, step);
3987  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
3988 
3989  return statusSPI;
3990 }
3991 
3992 /**
3993  * @brief configures the data that will be sent to the LTC daisy-chain to read EEPROM on slaves.
3994  *
3995  * @param ltc_state state of the ltc state machine
3996  * @param pTxBuff transmit buffer
3997  * @param step first or second stage of read process (0 or 1)
3998  *
3999  */
4000 static void LTC_SetEEPROMReadCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4001  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4002 
4003  uint32_t address = ltc_state->ltcData.slaveControl->eepromReadAddressToUse;
4004 
4005  address &= 0x3FFFFu;
4006  const uint8_t address0 = address >> 16u;
4007  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4008  const uint8_t address2 = address & 0xFFu;
4009 
4010  if (step == 0u) {
4011  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4012  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4013  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4014  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4015  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4016  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4017  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4018  }
4019 
4020  } else { /* step == 1 */
4021  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4022  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4023  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x10u);
4024  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | 0x0Fu;
4025  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0xF0u;
4026  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4027  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4028  }
4029  }
4030 }
4031 
4032 /**
4033  * @brief saves the read values of the external EEPROMs read from the LTC daisy-chain.
4034  *
4035  * @param ltc_state state of the ltc state machine
4036  * @param pRxBuff receive buffer
4037  *
4038  */
4039 static void LTC_EEPROMSaveReadValue(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4040  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4041 
4042  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4043  ltc_state->ltcData.slaveControl->eepromValueRead[i] = (pRxBuff[6u + (i * 8u)] << 4u) |
4044  ((pRxBuff[7u + (i * 8u)] >> 4u));
4045  }
4046 
4049  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
4050 
4051  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4052 }
4053 
4054 /**
4055  * @brief sends data to the LTC daisy-chain to write EEPROM on slaves.
4056  *
4057  * @param ltc_state state of the ltc state machine
4058  * @param pSpiInterface pointer to SPI configuration
4059  * @param pTxBuff transmit buffer
4060  * @param pRxBuff receive buffer
4061  * @param frameLength number of words to transmit
4062  * @param step first or second stage of read process (0 or 1)
4063  *
4064  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4065  */
4067  LTC_STATE_s *ltc_state,
4068  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4069  uint16_t *pTxBuff,
4070  uint16_t *pRxBuff,
4071  uint32_t frameLength,
4072  uint8_t step) {
4073  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4074 
4075  /* send WRCOMM to send I2C message to write EEPROM */
4076  LTC_SetEEPROMWriteCommand(ltc_state, pTxBuff, step);
4077  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4078 
4079  return statusSPI;
4080 }
4081 
4082 /**
4083  * @brief configures the data that will be sent to the LTC daisy-chain to write EEPROM on slaves.
4084  *
4085  * @param ltc_state state of the ltc state machine
4086  * @param pTxBuff transmit buffer
4087  * @param step first or second stage of read process (0 or 1)
4088  *
4089  */
4090 static void LTC_SetEEPROMWriteCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4091  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4092 
4093  uint32_t address = ltc_state->ltcData.slaveControl->eepromWriteAddressToUse;
4094 
4095  address &= 0x3FFFFu;
4096  const uint8_t address0 = address >> 16u;
4097  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4098  const uint8_t address2 = address & 0xFFu;
4099 
4100  if (step == 0u) {
4101  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4102  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4103  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4104  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4105  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4106  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4107  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4108  }
4109 
4110  } else { /* step == 1 */
4111  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4112  const uint8_t data = ltc_state->ltcData.slaveControl->eepromValueWrite[i];
4113 
4114  pTxBuff[4u + (i * 8u)] = LTC_ICOM_BLANK | (data >> 4u); /* 0x6 : LTC6804: ICOM START from Master */
4115  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | (data << 4u);
4116  pTxBuff[6u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4117  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4118  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4119  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4120  }
4121 
4124  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
4125 
4126  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4127  }
4128 }
4129 
4130 /**
4131  * @brief sends data to the LTC daisy-chain to configure multiplexer channels.
4132  *
4133  * This function calls the function LTC_SetMUXChCommand() to set the data.
4134  *
4135  * @param pSpiInterface pointer to SPI configuration
4136  * @param pTxBuff transmit buffer
4137  * @param pRxBuff receive buffer
4138  * @param frameLength number of words to transmit
4139  * @param mux multiplexer ID to be configured (0,1,2 or 3)
4140  * @param channel multiplexer channel to be configured (0 to 7)
4141  *
4142  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4143  */
4145  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4146  uint16_t *pTxBuff,
4147  uint16_t *pRxBuff,
4148  uint32_t frameLength,
4149  uint8_t mux,
4150  uint8_t channel) {
4151  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4152 
4153  /* send WRCOMM to send I2C message to choose channel */
4154  LTC_SetMUXChCommand(pTxBuff, mux, channel);
4155  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4156 
4157  return statusSPI;
4158 }
4159 
4160 /**
4161  * @brief sends data to the LTC daisy-chain to communicate via I2C
4162  *
4163  * This function initiates an I2C signal sent by the LTC6804 on the slave boards
4164  *
4165  * @param pSpiInterface pointer to SPI configuration
4166  * @param pTxBuff transmit buffer
4167  * @param pRxBuff receive buffer
4168  * @param frameLength number of words to transmit
4169  * @param cmd_data command data to be sent
4170  *
4171  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4172  */
4174  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4175  uint16_t *pTxBuff,
4176  uint16_t *pRxBuff,
4177  uint32_t frameLength,
4178  uint16_t *cmd_data) {
4179  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4180 
4181  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
4182  pTxBuff[4u + (i * 6u)] = cmd_data[0];
4183  pTxBuff[5u + (i * 6u)] = cmd_data[1];
4184 
4185  pTxBuff[6u + (i * 6u)] = cmd_data[2];
4186  pTxBuff[7u + (i * 6u)] = cmd_data[3];
4187 
4188  pTxBuff[8u + (i * 6u)] = cmd_data[4];
4189  pTxBuff[9u + (i * 6u)] = cmd_data[5];
4190  }
4191 
4192  /* send WRCOMM to send I2C message */
4193  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4194 
4195  return statusSPI;
4196 }
4197 
4198 /**
4199  * @brief saves the temperature value of the external temperature sensors read from the LTC daisy-chain.
4200  *
4201  * This function saves the temperature value received from the external temperature sensors
4202  *
4203  * @param ltc_state state of the ltc state machine
4204  * @param pRxBuff receive buffer
4205  *
4206  */
4207 static void LTC_TempSensSaveTemp(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4208  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4209 
4210  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4211  uint8_t temp_tmp[2];
4212  temp_tmp[0] = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4213  temp_tmp[1] = (pRxBuff[8u + (i * 8u)] << 4u) | ((pRxBuff[9u + (i * 8u)] >> 4u));
4214  uint16_t val_i = (temp_tmp[0] << 8u) | (temp_tmp[1]);
4215  val_i = val_i >> 8u;
4216  ltc_state->ltcData.slaveControl->externalTemperatureSensor[i] = val_i;
4217  }
4218 
4219  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4220 }
4221 
4222 /**
4223  * @brief sends data to the LTC daisy-chain to control the user port expander
4224  *
4225  * This function sends a control byte to the register of the user port expander
4226  *
4227  * @param ltc_state state of the ltc state machine
4228  * @param pSpiInterface pointer to SPI configuration
4229  * @param pTxBuff transmit buffer
4230  * @param pRxBuff receive buffer
4231  * @param frameLength number of words to transmit
4232  *
4233  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4234  */
4236  LTC_STATE_s *ltc_state,
4237  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4238  uint16_t *pTxBuff,
4239  uint16_t *pRxBuff,
4240  uint32_t frameLength) {
4241  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4242 
4243  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4244 
4245  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
4246  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES - 1 - i];
4247 
4248  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START |
4249  0x04u; /* 6: ICOM0 start condition, 4: upper nibble of PCA8574 address */
4250  pTxBuff[5u + (i * 8u)] =
4251  0u | LTC_FCOM_MASTER_NACK; /* 0: lower nibble of PCA8574 address + R/W bit, 8: FCOM0 master NACK */
4252 
4253  pTxBuff[6u + (i * 8u)] =
4254  LTC_ICOM_BLANK |
4255  (output_data >> 4u); /* 0: ICOM1 blank, x: upper nibble of PCA8574 data register (0 == pin low) */
4256  pTxBuff[7u + (i * 8u)] =
4257  (uint8_t)(output_data << 4u) |
4258  LTC_FCOM_MASTER_NACK_STOP; /* x: lower nibble of PCA8574 data register, 9: FCOM1 master NACK + STOP */
4259 
4260  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 7: no transmission, F: dummy data */
4261  pTxBuff[9u + (i * 8u)] = 0; /* F: dummy data, 9: FCOM2 master NACK + STOP */
4262  }
4263 
4264  /* send WRCOMM to send I2C message */
4265  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4266 
4267  return statusSPI;
4268 }
4269 
4270 /**
4271  * @brief saves the received values of the external port expander read from the LTC daisy-chain.
4272  *
4273  * This function saves the received data byte from the external port expander
4274  *
4275  * @param ltc_state state of the ltc state machine
4276  * @param pRxBuff receive buffer
4277  *
4278  */
4279 static void LTC_PortExpanderSaveValues(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4280  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4281 
4282  /* extract data */
4283  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4284  const uint8_t val_i = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4285  ltc_state->ltcData.slaveControl->ioValueIn[i] = val_i;
4286  }
4287 
4288  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4289 }
4290 
4291 /**
4292  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4293  *
4294  * This function sends a control byte to the register of the user port expander from TI
4295  *
4296  * @param ltc_state state of the ltc state machine
4297  * @param pSpiInterface pointer to SPI configuration
4298  * @param pTxBuff transmit buffer
4299  * @param pRxBuff receive buffer
4300  * @param frameLength number of words to transmit
4301  * @param direction use port expander pin as input or output
4302  *
4303  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4304  */
4306  LTC_STATE_s *ltc_state,
4307  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4308  uint16_t *pTxBuff,
4309  uint16_t *pRxBuff,
4310  uint32_t frameLength,
4312  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4313 
4314  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4315 
4316  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
4317  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /*upper nibble of TCA6408A address */
4318  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4319  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4320 
4321  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR >>
4322  4u); /* upper nibble of TCA6408A configuration register address */
4323  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR << 4u) |
4324  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A configuration register address */
4325 
4326  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK |
4327  (direction >> 4u); /* upper nibble of TCA6408A configuration register data */
4328  pTxBuff[9u + (i * 8u)] = (uint8_t)(direction << 4u) |
4329  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A configuration register data */
4330  }
4331 
4332  /* send WRCOMM to send I2C message */
4333  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4334 
4335  return statusSPI;
4336 }
4337 
4338 /**
4339  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4340  *
4341  * This function sends a control byte to the register of the user port expander from TI
4342  *
4343  * @param ltc_state state of the ltc state machine
4344  * @param pSpiInterface pointer to SPI configuration
4345  * @param pTxBuff transmit buffer
4346  * @param pRxBuff receive buffer
4347  * @param frameLength number of words to transmit
4348  *
4349  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4350  */
4352  LTC_STATE_s *ltc_state,
4353  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4354  uint16_t *pTxBuff,
4355  uint16_t *pRxBuff,
4356  uint32_t frameLength) {
4357  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4358 
4359  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4360 
4361  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
4362  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES - 1 - i];
4363 
4364  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4365  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4366  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4367 
4368  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR >>
4369  4u); /* upper nibble of TCA6408A output register address */
4370  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR << 4u) |
4371  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A output register address */
4372 
4373  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (output_data >> 4u); /* upper nibble of TCA6408A output register */
4374  pTxBuff[9u + (i * 8u)] = (uint8_t)(output_data << 4u) |
4375  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A output register */
4376  }
4377 
4378  /* send WRCOMM to send I2C message */
4379  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4380 
4381  return statusSPI;
4382 }
4383 
4384 /**
4385  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4386  *
4387  * @details This function sends a control byte to the register of the user port expander from TI
4388  *
4389  * @param ltc_state state of the ltc state machine
4390  * @param pSpiInterface pointer to SPI configuration
4391  * @param pTxBuff transmit buffer
4392  * @param pRxBuff receive buffer
4393  * @param frameLength number of words to transmit
4394  * @param step first or second stage of read process (0 or 1)
4395  *
4396  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4397  */
4399  LTC_STATE_s *ltc_state,
4400  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4401  uint16_t *pTxBuff,
4402  uint16_t *pRxBuff,
4403  uint32_t frameLength,
4404  uint8_t step) {
4405  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4406 
4407  if (step == 0u) {
4408  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
4409  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4410  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4411  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A address + R/W bit */
4412 
4413  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_INPUT_REG_ADR >>
4414  4u); /* upper nibble of TCA6408A input register address */
4415  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_INPUT_REG_ADR << 4u) |
4416  LTC_FCOM_MASTER_NACK; /* x: lower nibble of TCA6408A input register address */
4417 
4418  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* no transmission */
4419  pTxBuff[9u + (i * 8u)] = 0; /* dummy data */
4420  }
4421 
4422  } else {
4423  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4424 
4425  for (uint16_t i = 0; i < BS_NR_OF_MODULES; i++) {
4426  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4427  pTxBuff[5u + (i * 8u)] = (uint8_t)(((LTC_PORTEXPANDER_ADR_TI << 1u) | 1u) << 4u) |
4428  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A address + R/W bit */
4429 
4430  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | 0x0Fu; /* upper nibble slave data, master pulls bus high */
4431  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | 0xF0u; /* lower nibble slave data, master pulls bus high */
4432 
4433  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* no transmission */
4434  pTxBuff[9u + (i * 8u)] = 0; /* dummy data */
4435  }
4436  }
4437 
4438  /* send WRCOMM to send I2C message */
4439  statusSPI = LTC_TX(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4440 
4441  return statusSPI;
4442 }
4443 
4444 /**
4445  * @brief saves the received values of the external port expander from TI read from the LTC daisy-chain.
4446  * @details This function saves the received data byte from the external port expander from TI
4447  * @param ltc_state state of the ltc state machine
4448  * @param pTxBuff transmit buffer
4449  */
4450 static void LTC_PortExpanderSaveValues_TI(LTC_STATE_s *ltc_state, uint16_t *pTxBuff) {
4451  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4452 
4453  /* extract data */
4454  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4455  const uint8_t val_i = (pTxBuff[6u + (i * 8u)] << 4u) | ((pTxBuff[7u + (i * 8u)] >> 4u));
4456  ltc_state->ltcData.slaveControl->ioValueIn[i] = val_i;
4457  }
4458 
4459  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4460 }
4461 
4462 /**
4463  * @brief sends 72 clock pulses to the LTC daisy-chain.
4464  *
4465  * This function is used for the communication with the multiplexers via I2C on the GPIOs.
4466  * It send the command STCOMM to the LTC daisy-chain.
4467  *
4468  * @param pSpiInterface pointer to SPI configuration
4469  *
4470  * @return statusSPI #STD_OK if clock pulses were sent correctly by SPI, #STD_NOT_OK otherwise
4471  *
4472  */
4474  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4475  uint16_t ltc_TXBufferClock[4u + 9u];
4476 
4477  for (uint16_t i = 0; i < (4u + 9u); i++) {
4478  ltc_TXBufferClock[i] = 0xFF;
4479  }
4480 
4481  ltc_TXBufferClock[0] = ltc_cmdSTCOMM[0];
4482  ltc_TXBufferClock[1] = ltc_cmdSTCOMM[1];
4483  ltc_TXBufferClock[2] = ltc_cmdSTCOMM[2];
4484  ltc_TXBufferClock[3] = ltc_cmdSTCOMM[3];
4485 
4486  statusSPI = LTC_TransmitI2cCommand(pSpiInterface, ltc_TXBufferClock);
4487 
4488  return statusSPI;
4489 }
4490 
4491 /**
4492  * @brief gets the frequency of the SPI clock.
4493  *
4494  * This function reads the configuration from the SPI handle directly.
4495  *
4496  * @param pSpiInterface pointer to SPI configuration
4497  *
4498  * @return frequency of the SPI clock
4499  */
4500 static uint32_t LTC_GetSPIClock(SPI_INTERFACE_CONFIG_s *pSpiInterface) {
4501  uint32_t SPI_Clock = 0;
4502  uint32_t prescaler = 0;
4503 
4504  /* if (LTC_SPI_INSTANCE == SPI2 || LTC_SPI_INSTANCE == SPI3) { */
4505  /* SPI2 and SPI3 are connected to APB1 (PCLK1) */
4506  /* The prescaler setup bits LTC_SPI_PRESCALER corresponds to the bits 5:3 in the SPI_CR1 register */
4507  /* Reference manual p.909 */
4508  /* The shift by 3 puts the bits 5:3 to the first position */
4509  /* Division are made by powers of 2 which corresponds to shifting to the right */
4510  /* 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 */
4511 
4512  /* SPI_Clock = HAL_RCC_GetPCLK1Freq()>>((LTC_SPI_PRESCALER>>3)+1);
4513  } */
4514 
4515  /* if (LTC_SPI_INSTANCE == SPI1 || LTC_SPI_INSTANCE == SPI4 || LTC_SPI_INSTANCE == SPI5 || LTC_SPI_INSTANCE == SPI6) { */
4516  /* SPI1, SPI4, SPI5 and SPI6 are connected to APB2 (PCLK2) */
4517  /* The prescaler setup bits LTC_SPI_PRESCALER corresponds to the bits 5:3 in the SPI_CR1 register */
4518  /* Reference manual p.909 */
4519  /* The shift by 3 puts the bits 5:3 to the first position */
4520  /* Division are made by powers of 2 which corresponds to shifting to the right */
4521  /* 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 */
4522 
4523  /* SPI_Clock = HAL_RCC_GetPCLK2Freq()>>((LTC_SPI_PRESCALER>>3)+1);
4524  } */
4525 
4526  /* Get SPI prescaler */
4527  prescaler = ((pSpiInterface->pNode->FMT0) >> 8u) & 0xFFu;
4528  SPI_Clock = (uint32_t)(AVCLK1_FREQ * 1000000u) / (prescaler + 1u);
4529 
4530  return SPI_Clock;
4531 }
4532 
4533 /**
4534  * @brief sets the transfer time needed to receive/send data with the LTC daisy-chain.
4535  *
4536  * This function gets the clock frequency and uses the number of LTCs in the daisy-chain.
4537  *
4538  * @param ltc_state: state of the ltc state machine
4539  *
4540  */
4541 static void LTC_SetTransferTimes(LTC_STATE_s *ltc_state) {
4542  uint32_t transferTime_us = 0;
4543  uint32_t SPI_Clock = 0;
4544 
4545  SPI_Clock = LTC_GetSPIClock(ltc_state->ltcData.pSpiInterface);
4546 
4547  /* Transmission of a command and data */
4548  /* Multiplication by 1000*1000 to get us */
4549  transferTime_us = (8u * 1000u * 1000u) / (SPI_Clock);
4550  transferTime_us *= LTC_N_BYTES_FOR_DATA_TRANSMISSION;
4551  transferTime_us = transferTime_us + LTC_SPI_WAKEUP_WAIT_TIME_US;
4552  ltc_state->commandDataTransferTime = (transferTime_us / 1000u) + 1u;
4553 
4554  /* Transmission of a command */
4555  /* Multiplication by 1000*1000 to get us */
4556  transferTime_us = ((4u) * 8u * 1000u * 1000u) / (SPI_Clock);
4557  transferTime_us = transferTime_us + LTC_SPI_WAKEUP_WAIT_TIME_US;
4558  ltc_state->commandTransferTime = (transferTime_us / 1000u) + 1u;
4559 
4560  /* Transmission of a command + 9 clocks */
4561  /* Multiplication by 1000*1000 to get us */
4562  transferTime_us = ((4u + 9u) * 8u * 1000u * 1000u) / (SPI_Clock);
4563  transferTime_us = transferTime_us + LTC_SPI_WAKEUP_WAIT_TIME_US;
4564  ltc_state->gpioClocksTransferTime = (transferTime_us / 1000u) + 1u;
4565 }
4566 
4567 /**
4568  * @brief checks the state requests that are made.
4569  *
4570  * This function checks the validity of the state requests.
4571  * The results of the checked is returned immediately.
4572  *
4573  * @param ltc_state: state of the ltc state machine
4574  * @param statereq state request to be checked
4575  *
4576  * @return result of the state request that was made, taken from LTC_RETURN_TYPE_e
4577  */
4579  LTC_RETURN_TYPE_e retVal = LTC_OK;
4580  if (statereq.string >= BS_NR_OF_STRINGS) {
4581  retVal = LTC_ILLEGAL_REQUEST;
4582  } else if (ltc_state->statereq.request == LTC_STATE_NO_REQUEST) {
4583  /* init only allowed from the uninitialized state */
4584  if (statereq.request == LTC_STATE_INIT_REQUEST) {
4585  if (ltc_state->state == LTC_STATEMACH_UNINITIALIZED) {
4586  retVal = LTC_OK;
4587  } else {
4588  retVal = LTC_ALREADY_INITIALIZED;
4589  }
4590  } else {
4591  retVal = LTC_OK;
4592  }
4593  } else {
4594  retVal = LTC_REQUEST_PENDING;
4595  }
4596 
4597  return retVal;
4598 }
4599 
4601  bool retval = false;
4602 
4604  retval = ltc_state->first_measurement_made;
4606 
4607  return (retval);
4608 }
4609 
4610 /**
4611  * @brief sets the measurement initialization status.
4612  */
4615  ltc_state->first_measurement_made = true;
4617 }
4618 
4619 extern void LTC_monitoringPinInit(void) {
4620  /* set HET Pins to output */
4625 
4626  /* set LTC6820 forward enable to 1 */
4628  /* set LTC6820 forward master to 1 */