foxBMS - Unit Tests  1.6.0
The foxBMS Unit Tests API Documentation
ltc_6813-1.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2023, 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 2023-10-12 (date of last update)
47  * @version v1.6.0
48  * @ingroup DRIVERS
49  * @prefix LTC
50  *
51  * @brief Driver for the LTC analog front-end.
52  *
53  */
54 
55 /*========== Includes =======================================================*/
56 /* clang-format off */
57 #include "ltc_6813-1_cfg.h"
58 #include "ltc.h"
59 /* clang-format on */
60 
61 #include "HL_spi.h"
62 #include "HL_system.h"
63 
64 #include "afe_plausibility.h"
65 #include "database.h"
66 #include "diag.h"
67 #include "io.h"
68 #include "ltc_pec.h"
69 #include "os.h"
70 #include "pex.h"
71 
72 #include <stdbool.h>
73 #include <stdint.h>
74 
75 /*========== Macros and Definitions =========================================*/
76 
77 /**
78  * TI port expander register addresses
79  * @{
80  */
81 #define LTC_PORT_EXPANDER_TI_INPUT_REG_ADR (0x00u)
82 #define LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR (0x01u)
83 #define LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR (0x03u)
84 /**@}*/
85 
86 /**
87  * LTC COMM definitions
88  * @{
89  */
90 #define LTC_ICOM_START (0x60u)
91 #define LTC_ICOM_STOP (0x10u)
92 #define LTC_ICOM_BLANK (0x00u)
93 #define LTC_ICOM_NO_TRANSMIT (0x70u)
94 #define LTC_FCOM_MASTER_ACK (0x00u)
95 #define LTC_FCOM_MASTER_NACK (0x08u)
96 #define LTC_FCOM_MASTER_NACK_STOP (0x09u)
97 /**@}*/
98 
99 /** maximum number of supported cells */
100 #define LTC_MAX_SUPPORTED_CELLS (12u)
101 
102 /*========== Static Constant and Variable Definitions =======================*/
103 /**
104  * PEC buffer for RX and TX
105  * @{
106  */
107 /* AXIVION Disable Style MisraC2012-1.2: The Pec buffer must be put in the shared RAM section for performance reasons */
108 #pragma SET_DATA_SECTION(".sharedRAM")
111 #pragma SET_DATA_SECTION()
112 /* AXIVION Enable Style MisraC2012-1.2: only Pec buffer needed to be in the shared RAM section */
113 /**@}*/
114 
115 /** index of used cells */
116 static uint16_t ltc_used_cells_index[BS_NR_OF_STRINGS] = {0};
117 /** local copies of database tables */
118 /**@{*/
127 /**@}*/
128 /** stores information on the detected open wires locally */
130 static LTC_ERRORTABLE_s ltc_errorTable = {0}; /*!< init in LTC_ResetErrorTable-function */
131 
132 /** local definition of plausible cell voltage values for the LTC6813 (and similar) */
135  .minimumPlausibleVoltage_mV = 0,
136 };
137 
138 /*========== Extern Constant and Variable Definitions =======================*/
139 
141  .timer = 0,
142  .statereq = {.request = LTC_STATE_NO_REQUEST, .string = 0xFFu},
144  .substate = 0,
145  .laststate = LTC_STATEMACH_UNINITIALIZED,
146  .lastsubstate = 0,
147  .adcModereq = LTC_ADCMODE_FAST_DCP0,
148  .adcMode = LTC_ADCMODE_FAST_DCP0,
149  .adcMeasChreq = LTC_ADCMEAS_UNDEFINED,
150  .adcMeasCh = LTC_ADCMEAS_UNDEFINED,
151  .numberOfMeasuredMux = 32,
152  .triggerentry = 0,
153  .ErrRetryCounter = 0,
154  .ErrRequestCounter = 0,
155  .VoltageSampleTime = 0,
156  .muxSampleTime = 0,
157  .commandDataTransferTime = 3,
158  .commandTransferTime = 3,
159  .gpioClocksTransferTime = 3,
160  .muxmeas_seqptr = {NULL_PTR},
161  .muxmeas_seqendptr = {NULL_PTR},
162  .muxmeas_nr_end = {0},
163  .first_measurement_made = false,
164  .ltc_muxcycle_finished = STD_NOT_OK,
165  .check_spi_flag = STD_NOT_OK,
166  .balance_control_done = STD_NOT_OK,
167  .transmit_ongoing = false,
168  .dummyByte_ongoing = STD_NOT_OK,
169  .spiDiagErrorEntry = DIAG_ID_AFE_SPI,
170  .pecDiagErrorEntry = DIAG_ID_AFE_COM_INTEGRITY,
171  .muxDiagErrorEntry = DIAG_ID_AFE_MUX,
172  .voltMeasDiagErrorEntry = DIAG_ID_AFE_CELL_VOLTAGE_MEAS_ERROR,
173  .tempMeasDiagErrorEntry = DIAG_ID_AFE_CELL_TEMPERATURE_MEAS_ERROR,
174  .ltcData.pSpiInterface = spi_ltcInterface,
175  .ltcData.txBuffer = ltc_TxPecBuffer,
176  .ltcData.rxBuffer = ltc_RxPecBuffer,
177  .ltcData.frameLength = LTC_N_BYTES_FOR_DATA_TRANSMISSION,
178  .ltcData.cellVoltage = &ltc_cellVoltage,
179  .ltcData.cellTemperature = &ltc_celltemperature,
180  .ltcData.balancingFeedback = &ltc_balancing_feedback,
181  .ltcData.balancingControl = &ltc_balancing_control,
182  .ltcData.slaveControl = &ltc_slave_control,
183  .ltcData.openWireDetection = &ltc_openWireDetection,
184  .ltcData.errorTable = &ltc_errorTable,
185  .ltcData.allGpioVoltages = &ltc_allgpiovoltage,
186  .ltcData.openWire = &ltc_openWire,
187  .ltcData.usedCellIndex = ltc_used_cells_index,
188  .currentString = 0u,
189  .requestedString = 0u,
190 };
191 
192 static uint16_t ltc_cmdWRCFG[4] = {0x00, 0x01, 0x3D, 0x6E};
193 static uint16_t ltc_cmdWRCFG2[4] = {0x00, 0x24, 0xB1, 0x9E};
194 static uint16_t ltc_cmdRDCFG[4] = {0x00, 0x02, 0x2B, 0x0A};
195 
196 static uint16_t ltc_cmdRDCVA[4] = {0x00, 0x04, 0x07, 0xC2};
197 static uint16_t ltc_cmdRDCVB[4] = {0x00, 0x06, 0x9A, 0x94};
198 static uint16_t ltc_cmdRDCVC[4] = {0x00, 0x08, 0x5E, 0x52};
199 static uint16_t ltc_cmdRDCVD[4] = {0x00, 0x0A, 0xC3, 0x04};
200 static uint16_t ltc_cmdRDCVE[4] = {0x00, 0x09, 0xD5, 0x60};
201 static uint16_t ltc_cmdRDCVF[4] = {0x00, 0x0B, 0x48, 0x36};
202 static uint16_t ltc_cmdWRCOMM[4] = {0x07, 0x21, 0x24, 0xB2};
203 static uint16_t ltc_cmdSTCOMM[4] = {0x07, 0x23, 0xB9, 0xE4};
204 static uint16_t ltc_cmdRDCOMM[4] = {0x07, 0x22, 0x32, 0xD6};
205 static uint16_t ltc_cmdRDAUXA[4] = {0x00, 0x0C, 0xEF, 0xCC};
206 static uint16_t ltc_cmdRDAUXB[4] = {0x00, 0x0E, 0x72, 0x9A};
207 static uint16_t ltc_cmdRDAUXC[4] = {0x00, 0x0D, 0x64, 0xFE};
208 static uint16_t ltc_cmdRDAUXD[4] = {0x00, 0x0F, 0xF9, 0xA8};
209 
210 /* static uint16_t ltc_cmdMUTE[4] = {0x00, 0x28, 0xE8, 0x0E}; !< MUTE discharging via S pins */
211 /* static uint16_t ltc_cmdUNMUTE[4] = {0x00, 0x29, 0x63, 0x3C}; !< UN-MUTE discharging via S pins */
212 
213 /* LTC I2C commands */
214 /* static uint16_t ltc_I2CcmdDummy[6] = {0x7F, 0xF9, 0x7F, 0xF9, 0x7F, 0xF9}; !< dummy command (no transmit) */
215 
216 static uint16_t ltc_I2CcmdTempSens0[6] = {
217  0x69,
218  0x08,
219  0x00,
220  0x09,
221  0x7F,
222  0xF9}; /*!< sets the internal data pointer of the temperature sensor (address 0x48) to 0x00 */
223 static uint16_t ltc_I2CcmdTempSens1[6] =
224  {0x69, 0x18, 0x0F, 0xF0, 0x0F, 0xF9}; /*!< reads two data bytes from the temperature sensor */
225 
226 static uint16_t ltc_I2CcmdPortExpander1[6] =
227  {0x64, 0x18, 0x0F, 0xF9, 0x7F, 0xF9}; /*!< reads one data byte from the port expander */
228 
229 /* Cells */
230 static uint16_t ltc_cmdADCV_normal_DCP0[4] =
231  {0x03, 0x60, 0xF4, 0x6C}; /*!< All cells, normal mode, discharge not permitted (DCP=0) */
232 static uint16_t ltc_cmdADCV_normal_DCP1[4] =
233  {0x03, 0x70, 0xAF, 0x42}; /*!< All cells, normal mode, discharge permitted (DCP=1) */
234 static uint16_t ltc_cmdADCV_filtered_DCP0[4] =
235  {0x03, 0xE0, 0xB0, 0x4A}; /*!< All cells, filtered mode, discharge not permitted (DCP=0) */
236 static uint16_t ltc_cmdADCV_filtered_DCP1[4] =
237  {0x03, 0xF0, 0xEB, 0x64}; /*!< All cells, filtered mode, discharge permitted (DCP=1) */
238 static uint16_t ltc_cmdADCV_fast_DCP0[4] =
239  {0x02, 0xE0, 0x38, 0x06}; /*!< All cells, fast mode, discharge not permitted (DCP=0) */
240 static uint16_t ltc_cmdADCV_fast_DCP1[4] =
241  {0x02, 0xF0, 0x63, 0x28}; /*!< All cells, fast mode, discharge permitted (DCP=1) */
242 static uint16_t ltc_cmdADCV_fast_DCP0_twocells[4] =
243  {0x02, 0xE1, 0xB3, 0x34}; /*!< Two cells (1 and 7), fast mode, discharge not permitted (DCP=0) */
244 
245 /* GPIOs */
246 static uint16_t ltc_cmdADAX_normal_GPIO1[4] = {0x05, 0x61, 0x58, 0x92}; /*!< Single channel, GPIO 1, normal mode */
247 static uint16_t ltc_cmdADAX_filtered_GPIO1[4] = {0x05, 0xE1, 0x1C, 0xB4}; /*!< Single channel, GPIO 1, filtered mode */
248 static uint16_t ltc_cmdADAX_fast_GPIO1[4] = {0x04, 0xE1, 0x94, 0xF8}; /*!< Single channel, GPIO 1, fast mode */
249 static uint16_t ltc_cmdADAX_normal_GPIO2[4] = {0x05, 0x62, 0x4E, 0xF6}; /*!< Single channel, GPIO 2, normal mode */
250 static uint16_t ltc_cmdADAX_filtered_GPIO2[4] = {0x05, 0xE2, 0x0A, 0xD0}; /*!< Single channel, GPIO 2, filtered mode */
251 static uint16_t ltc_cmdADAX_fast_GPIO2[4] = {0x04, 0xE2, 0x82, 0x9C}; /*!< Single channel, GPIO 2, fast mode */
252 static uint16_t ltc_cmdADAX_normal_GPIO3[4] = {0x05, 0x63, 0xC5, 0xC4}; /*!< Single channel, GPIO 3, normal mode */
253 static uint16_t ltc_cmdADAX_filtered_GPIO3[4] = {0x05, 0xE3, 0x81, 0xE2}; /*!< Single channel, GPIO 3, filtered mode */
254 static uint16_t ltc_cmdADAX_fast_GPIO3[4] = {0x04, 0xE3, 0x09, 0xAE}; /*!< Single channel, GPIO 3, fast mode */
255 /* static uint16_t ltc_cmdADAX_normal_GPIO4[4] = {0x05, 0x64, 0x62, 0x3E}; !< Single channel, GPIO 4, normal mode */
256 /* static uint16_t ltc_cmdADAX_filtered_GPIO4[4] = {0x05, 0xE4, 0x26, 0x18}; !< Single channel, GPIO 4, filtered mode */
257 /* static uint16_t ltc_cmdADAX_fast_GPIO4[4] = {0x04, 0xE4, 0xAE, 0x54}; !< Single channel, GPIO 4, fast mode */
258 /* static uint16_t ltc_cmdADAX_normal_GPIO5[4] = {0x05, 0x65, 0xE9, 0x0C}; !< Single channel, GPIO 5, normal mode */
259 /* static uint16_t ltc_cmdADAX_filtered_GPIO5[4] = {0x05, 0xE5, 0xAD, 0x2A}; !< Single channel, GPIO 5, filtered mode */
260 /* static uint16_t ltc_cmdADAX_fast_GPIO5[4] = {0x04, 0xE5, 0x25, 0x66}; !< Single channel, GPIO 5, fast mode */
261 static uint16_t ltc_cmdADAX_normal_ALLGPIOS[4] = {0x05, 0x60, 0xD3, 0xA0}; /*!< All channels, normal mode */
262 static uint16_t ltc_cmdADAX_filtered_ALLGPIOS[4] =
263  {0x05, 0xE0, 0x97, 0x86}; /*!< All channels, filtered mode */
264 static uint16_t ltc_cmdADAX_fast_ALLGPIOS[4] = {0x04, 0xE0, 0x1F, 0xCA}; /*!< All channels, fast mode */
265 
266 /* Open-wire */
267 static uint16_t ltc_BC_cmdADOW_PUP_normal_DCP0[4] = {
268  0x03,
269  0x68,
270  0x1C,
271  0x62}; /*!< Broadcast, Pull-up current, All cells, normal mode, discharge not permitted (DCP=0) */
272 static uint16_t ltc_BC_cmdADOW_PDOWN_normal_DCP0[4] = {
273  0x03,
274  0x28,
275  0xFB,
276  0xE8}; /*!< Broadcast, Pull-down current, All cells, normal mode, discharge not permitted (DCP=0) */
277 static uint16_t ltc_BC_cmdADOW_PUP_filtered_DCP0[4] = {
278  0x03,
279  0xE8,
280  0x58,
281  0x44}; /*!< Broadcast, Pull-up current, All cells, filtered mode, discharge not permitted (DCP=0) */
283  0x03,
284  0xA8,
285  0xBF,
286  0xCE}; /*!< Broadcast, Pull-down current, All cells, filtered mode, discharge not permitted (DCP=0) */
287 
288 /*========== Static Function Prototypes =====================================*/
289 static void LTC_SetFirstMeasurementCycleFinished(LTC_STATE_s *ltc_state);
290 static void LTC_InitializeDatabase(LTC_STATE_s *ltc_state);
291 static void LTC_SaveBalancingFeedback(LTC_STATE_s *ltc_state, uint16_t *DataBufferSPI_RX, uint8_t stringNumber);
292 static void LTC_GetBalancingControlValues(LTC_STATE_s *ltc_state);
293 static void LTC_SaveLastStates(LTC_STATE_s *ltc_state);
294 static void LTC_StateTransition(LTC_STATE_s *ltc_state, LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms);
295 static void LTC_CondBasedStateTransition(
296  LTC_STATE_s *ltc_state,
297  STD_RETURN_TYPE_e retVal,
298  DIAG_ID_e diagCode,
299  LTC_STATEMACH_e state_ok,
300  uint8_t substate_ok,
301  uint16_t timer_ms_ok,
302  LTC_STATEMACH_e state_nok,
303  uint8_t substate_nok,
304  uint16_t timer_ms_nok);
305 
307  LTC_STATE_s *ltc_state,
308  SPI_INTERFACE_CONFIG_s *pSpiInterface,
309  uint16_t *pTxBuff,
310  uint16_t *pRxBuff,
311  uint32_t frameLength,
312  uint8_t registerSet,
313  uint8_t stringNumber);
314 
315 static void LTC_ResetErrorTable(LTC_STATE_s *ltc_state);
317  SPI_INTERFACE_CONFIG_s *pSpiInterface,
318  uint16_t *pTxBuff,
319  uint16_t *pRxBuff,
320  uint32_t frameLength);
321 
323  SPI_INTERFACE_CONFIG_s *pSpiInterface,
324  LTC_ADCMODE_e adcMode,
325  LTC_ADCMEAS_CHAN_e adcMeasCh);
327  SPI_INTERFACE_CONFIG_s *pSpiInterface,
328  LTC_ADCMODE_e adcMode,
329  LTC_ADCMEAS_CHAN_e adcMeasCh);
331  SPI_INTERFACE_CONFIG_s *pSpiInterface,
332  LTC_ADCMODE_e adcMode,
333  uint8_t PUP);
334 
335 static uint16_t LTC_GetMeasurementTimeCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh);
336 static void LTC_SaveRxToVoltageBuffer(
337  LTC_STATE_s *ltc_state,
338  uint16_t *pRxBuff,
339  uint8_t registerSet,
340  uint8_t stringNumber);
341 static void LTC_SaveRxToGpioBuffer(
342  LTC_STATE_s *ltc_state,
343  uint16_t *pRxBuff,
344  uint8_t registerSet,
345  uint8_t stringNumber);
346 
348  LTC_STATE_s *ltc_state,
349  uint16_t *DataBufferSPI_RX_with_PEC,
350  uint8_t stringNumber);
352  uint16_t *Command,
353  SPI_INTERFACE_CONFIG_s *pSpiInterface,
354  uint16_t *pTxBuff,
355  uint16_t *pRxBuff,
356  uint32_t frameLength);
358  uint16_t *Command,
359  SPI_INTERFACE_CONFIG_s *pSpiInterface,
360  uint16_t *pTxBuff,
361  uint16_t *pRxBuff,
362  uint32_t frameLength);
363 static void LTC_SetMuxChCommand(uint16_t *pTxBuff, uint8_t mux, uint8_t channel);
365  LTC_STATE_s *ltc_state,
366  SPI_INTERFACE_CONFIG_s *pSpiInterface,
367  uint16_t *pTxBuff,
368  uint16_t *pRxBuff,
369  uint32_t frameLength,
370  uint8_t step);
371 static void LTC_SetEepromReadCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step);
372 static void LTC_EepromSaveReadValue(LTC_STATE_s *ltc_state, uint16_t *pRxBuff);
374  LTC_STATE_s *ltc_state,
375  SPI_INTERFACE_CONFIG_s *pSpiInterface,
376  uint16_t *pTxBuff,
377  uint16_t *pRxBuff,
378  uint32_t frameLength,
379  uint8_t step);
380 static void LTC_SetEepromWriteCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step);
382  SPI_INTERFACE_CONFIG_s *pSpiInterface,
383  uint16_t *pTxBuff,
384  uint16_t *pRxBuff,
385  uint32_t frameLength,
386  uint8_t mux,
387  uint8_t channel);
389  LTC_STATE_s *ltc_state,
390  SPI_INTERFACE_CONFIG_s *pSpiInterface,
391  uint16_t *pTxBuff,
392  uint16_t *pRxBuff,
393  uint32_t frameLength);
394 static void LTC_PortExpanderSaveValues(LTC_STATE_s *ltc_state, uint16_t *pRxBuff);
395 static void LTC_TempSensSaveTemp(LTC_STATE_s *ltc_state, uint16_t *pRxBuff);
397  LTC_STATE_s *ltc_state,
398  SPI_INTERFACE_CONFIG_s *pSpiInterface,
399  uint16_t *pTxBuff,
400  uint16_t *pRxBuff,
401  uint32_t frameLength,
404  LTC_STATE_s *ltc_state,
405  SPI_INTERFACE_CONFIG_s *pSpiInterface,
406  uint16_t *pTxBuff,
407  uint16_t *pRxBuff,
408  uint32_t frameLength);
410  LTC_STATE_s *ltc_state,
411  SPI_INTERFACE_CONFIG_s *pSpiInterface,
412  uint16_t *pTxBuff,
413  uint16_t *pRxBuff,
414  uint32_t frameLength,
415  uint8_t step);
416 static void LTC_PortExpanderSaveValuesTi(LTC_STATE_s *ltc_state, uint16_t *pTxBuff);
417 
420  SPI_INTERFACE_CONFIG_s *pSpiInterface,
421  uint16_t *pTxBuff,
422  uint16_t *pRxBuff,
423  uint32_t frameLength,
424  uint16_t *cmd_data);
425 
426 static STD_RETURN_TYPE_e LTC_I2cCheckAck(LTC_STATE_s *ltc_state, uint16_t *pRxBuff, uint8_t mux, uint8_t stringNumber);
427 
428 static void LTC_SaveMuxMeasurement(
429  LTC_STATE_s *ltc_state,
430  uint16_t *pRxBuff,
431  LTC_MUX_CH_CFG_s *muxseqptr,
432  uint8_t stringNumber);
433 
434 static uint32_t LTC_GetSpiClock(SPI_INTERFACE_CONFIG_s *pSpiInterface);
435 static void LTC_SetTransferTimes(LTC_STATE_s *ltc_state);
436 
438 
439 /*========== Static Function Implementations ================================*/
440 /**
441  * @brief in the database, initializes the fields related to the LTC drivers.
442  *
443  * This function loops through all the LTC-related data fields in the database
444  * and sets them to 0. It should be called in the initialization or re-initialization
445  * routine of the LTC driver.
446  *
447  * @param ltc_state: state of the ltc state machine
448  *
449  */
450 static void LTC_InitializeDatabase(LTC_STATE_s *ltc_state) {
451  for (uint8_t s = 0u; s < BS_NR_OF_STRINGS; s++) {
452  ltc_state->ltcData.cellVoltage->state = 0;
453  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
454  for (uint8_t cb = 0u; cb < BS_NR_OF_CELL_BLOCKS_PER_MODULE; cb++) {
455  ltc_state->ltcData.cellVoltage->cellVoltage_mV[s][m][cb] = 0u;
456  }
457  }
458 
459  for (uint16_t i = 0; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
460  ltc_state->ltcData.openWireDetection->openWirePup[s][i] = 0;
461  ltc_state->ltcData.openWireDetection->openWirePdown[s][i] = 0;
462  ltc_state->ltcData.openWireDetection->openWireDelta[s][i] = 0;
463  }
464 
465  ltc_state->ltcData.cellTemperature->state = 0;
466 
467  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
468  for (uint8_t ts = 0; ts < BS_NR_OF_TEMP_SENSORS_PER_STRING; ts++) {
469  ltc_state->ltcData.cellTemperature->cellTemperature_ddegC[s][m][ts] = 0;
470  }
471  }
472 
473  ltc_state->ltcData.balancingFeedback->state = 0;
474  for (uint16_t i = 0; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
475  ltc_state->ltcData.balancingControl->balancingState[s][i] = 0;
476  }
477  ltc_state->ltcData.balancingControl->nrBalancedCells[s] = 0u;
478  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
479  ltc_state->ltcData.balancingFeedback->value[s][i] = 0;
480  }
481 
482  ltc_state->ltcData.slaveControl->state = 0;
483  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
484  ltc_state->ltcData.slaveControl->ioValueIn[i] = 0;
485  ltc_state->ltcData.slaveControl->ioValueOut[i] = 0;
487  ltc_state->ltcData.slaveControl->eepromValueRead[i] = 0;
488  ltc_state->ltcData.slaveControl->eepromValueWrite[i] = 0;
489  }
490  ltc_state->ltcData.slaveControl->eepromReadAddressLastUsed = 0xFFFFFFFF;
491  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
492  ltc_state->ltcData.slaveControl->eepromWriteAddressLastUsed = 0xFFFFFFFF;
493  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
494 
495  ltc_state->ltcData.allGpioVoltages->state = 0;
496  for (uint16_t i = 0; i < (BS_NR_OF_MODULES_PER_STRING * BS_NR_OF_GPIOS_PER_MODULE); i++) {
497  ltc_state->ltcData.allGpioVoltages->gpioVoltages_mV[s][i] = 0;
498  }
499 
500  for (uint16_t i = 0; i < (BS_NR_OF_MODULES_PER_STRING * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1)); i++) {
501  ltc_state->ltcData.openWire->openWire[s][i] = 0;
502  }
503  ltc_state->ltcData.openWire->state = 0;
504  }
505 
507  ltc_state->ltcData.cellVoltage,
508  ltc_state->ltcData.cellTemperature,
509  ltc_state->ltcData.balancingFeedback,
510  ltc_state->ltcData.openWire);
512 }
513 
514 /**
515  * @brief Saves the last state and the last substate
516  *
517  * @param ltc_state: state of the ltc state machine
518  */
519 static void LTC_SaveLastStates(LTC_STATE_s *ltc_state) {
520  ltc_state->laststate = ltc_state->state;
521  ltc_state->lastsubstate = ltc_state->substate;
522 }
523 
524 /**
525  * @brief function for setting LTC_Trigger state transitions
526  *
527  * @param ltc_state: state of the ltc state machine
528  * @param state: state to transition into
529  * @param substate: substate to transition into
530  * @param timer_ms: transition into state, substate after timer elapsed
531  */
532 static void LTC_StateTransition(LTC_STATE_s *ltc_state, LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms) {
533  ltc_state->state = state;
534  ltc_state->substate = substate;
535  ltc_state->timer = timer_ms;
536 }
537 
538 /**
539  * @brief condition-based state transition depending on retVal
540  *
541  * If retVal is #STD_OK, after timer_ms_ok is elapsed the LTC state machine will
542  * transition into state_ok and substate_ok, otherwise after timer_ms_nok the
543  * state machine will transition to state_nok and substate_nok. Depending on
544  * value of retVal the corresponding diagnosis entry will be called.
545  *
546  * @param ltc_state state of the ltc state machine
547  * @param retVal condition to determine if state machine will transition
548  * into ok or nok states
549  * @param diagCode symbolic IDs for diagnosis entry, called with
550  * #DIAG_EVENT_OK if retVal is #STD_OK, #DIAG_EVENT_NOT_OK
551  * otherwise
552  * @param state_ok state to transition into if retVal is #STD_OK
553  * @param substate_ok substate to transition into if retVal is #STD_OK
554  * @param timer_ms_ok transition into state_ok, substate_ok after timer_ms_ok
555  * elapsed
556  * @param state_nok state to transition into if retVal is #STD_NOT_OK
557  * @param substate_nok substate to transition into if retVal is #STD_NOT_OK
558  * @param timer_ms_nok transition into state_nok, substate_nok after
559  * timer_ms_nok elapsed
560  */
562  LTC_STATE_s *ltc_state,
563  STD_RETURN_TYPE_e retVal,
564  DIAG_ID_e diagCode,
565  LTC_STATEMACH_e state_ok,
566  uint8_t substate_ok,
567  uint16_t timer_ms_ok,
568  LTC_STATEMACH_e state_nok,
569  uint8_t substate_nok,
570  uint16_t timer_ms_nok) {
571  if ((retVal != STD_OK)) {
573  LTC_StateTransition(ltc_state, state_nok, substate_nok, timer_ms_nok);
574  } else {
575  DIAG_Handler(diagCode, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
576  LTC_StateTransition(ltc_state, state_ok, substate_ok, timer_ms_ok);
577  }
578 }
579 
580 extern void LTC_SaveVoltages(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
581  /* Pointer validity check */
582  FAS_ASSERT(ltc_state != NULL_PTR);
583  FAS_ASSERT(stringNumber < BS_NR_OF_STRINGS);
584 
585  /* Iterate over all cell to:
586  *
587  * 1. Check open-wires and set respective cell measurements to invalid
588  * 2. Perform minimum/maximum measurement value plausibility check
589  * 3. Calculate string values
590  */
591  STD_RETURN_TYPE_e cellVoltageMeasurementValid = STD_OK;
592  int32_t stringVoltage_mV = 0;
593  uint16_t numberValidMeasurements = 0;
594  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
595  for (uint8_t cb = 0u; cb < BS_NR_OF_CELL_BLOCKS_PER_MODULE; cb++) {
596  /* ------- 1. Check open-wires -----------------
597  * Is cell N input not open wire &&
598  * Is cell N+1 input not open wire &&
599  * Is cell voltage valid because of previous PEC error
600  * If so, everything okay, else set cell voltage measurement to invalid.
601  */
602  if ((ltc_state->ltcData.openWire
603  ->openWire[stringNumber][(m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1u)) + cb] == 0u) &&
604  (ltc_state->ltcData.openWire
605  ->openWire[stringNumber][(m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1u)) + cb + 1u] == 0u) &&
606  ((ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] & (0x01u << cb)) == 0u)) {
607  /* Cell voltage is valid -> perform minimum/maximum plausibility check */
608 
609  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
611  ltc_state->ltcData.cellVoltage->cellVoltage_mV[stringNumber][m][cb],
613  /* Cell voltage is valid -> calculate string voltage */
614  /* -------- 3. Calculate string values ------------- */
615  stringVoltage_mV += ltc_state->ltcData.cellVoltage->cellVoltage_mV[stringNumber][m][cb];
616  numberValidMeasurements++;
617  } else {
618  /* Invalidate cell voltage measurement */
619  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << cb);
620  cellVoltageMeasurementValid = STD_NOT_OK;
621  }
622  } else {
623  /* Set cell voltage measurement value invalid, if not already invalid because of PEC Error */
624  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << cb);
625  cellVoltageMeasurementValid = STD_NOT_OK;
626  }
627  }
628  }
629  DIAG_CheckEvent(cellVoltageMeasurementValid, ltc_state->voltMeasDiagErrorEntry, DIAG_STRING, stringNumber);
630  ltc_state->ltcData.cellVoltage->stringVoltage_mV[stringNumber] = stringVoltage_mV;
631  ltc_state->ltcData.cellVoltage->nrValidCellVoltages[stringNumber] = numberValidMeasurements;
632 
633  /* Increment state variable each time new values are written into database */
634  ltc_state->ltcData.cellVoltage->state++;
635 
636  DATA_WRITE_DATA(ltc_state->ltcData.cellVoltage);
637 }
638 
639 /*========== Extern Function Implementations ================================*/
640 extern void LTC_SaveTemperatures(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
641  FAS_ASSERT(ltc_state != NULL_PTR);
642  STD_RETURN_TYPE_e cellTemperatureMeasurementValid = STD_OK;
643  uint16_t numberValidMeasurements = 0;
644 
645  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
646  for (uint8_t ts = 0u; ts < BS_NR_OF_TEMP_SENSORS_PER_MODULE; ts++) {
647  /* ------- 1. Check valid flag -----------------
648  * Is cell temperature valid because of previous PEC error
649  * If so, everything okay, else set cell temperature measurement to invalid.
650  */
651  if ((ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] & (0x01u << ts)) == 0u) {
652  /* Cell temperature is valid -> perform minimum/maximum plausibility check */
653 
654  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
656  ltc_state->ltcData.cellTemperature->cellTemperature_ddegC[stringNumber][m][ts])) {
657  numberValidMeasurements++;
658  } else {
659  /* Invalidate cell temperature measurement */
660  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] |= (0x01u << ts);
661  cellTemperatureMeasurementValid = STD_NOT_OK;
662  }
663  } else {
664  /* Already invalid because of PEC Error */
665  cellTemperatureMeasurementValid = STD_NOT_OK;
666  }
667  }
668  }
669  DIAG_CheckEvent(cellTemperatureMeasurementValid, ltc_state->tempMeasDiagErrorEntry, DIAG_STRING, stringNumber);
670 
671  ltc_state->ltcData.cellTemperature->nrValidTemperatures[stringNumber] = numberValidMeasurements;
672 
673  ltc_state->ltcData.cellTemperature->state++;
675 }
676 
677 extern void LTC_SaveAllGpioMeasurement(LTC_STATE_s *ltc_state) {
678  FAS_ASSERT(ltc_state != NULL_PTR);
679  ltc_state->ltcData.allGpioVoltages->state++;
681 }
682 
683 /**
684  * @brief stores the measured balancing feedback values in the database.
685  *
686  * This function stores the global balancing feedback value measured on GPIO3 of the LTC into the database
687  *
688  * @param ltc_state state of the ltc state machine
689  * @param DataBufferSPI_RX receive buffer of the SPI interface
690  * @param stringNumber string addressed
691  *
692  */
693 static void LTC_SaveBalancingFeedback(LTC_STATE_s *ltc_state, uint16_t *DataBufferSPI_RX, uint8_t stringNumber) {
694  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
695  const uint16_t val_i = DataBufferSPI_RX[8u + (1u * i * 8u)] |
696  (DataBufferSPI_RX[8u + (1u * i * 8u) + 1u] << 8u); /* raw value, GPIO3 */
697 
698  ltc_state->ltcData.balancingFeedback->value[stringNumber][i] = val_i;
699  }
700 
701  ltc_state->ltcData.balancingFeedback->state++;
703 }
704 
705 /**
706  * @brief gets the balancing orders from the database.
707  *
708  * This function gets the balancing control from the database. Balancing control
709  * is set by the BMS. The LTC driver only executes the balancing orders.
710  *
711  * @param ltc_state: state of the ltc state machine
712  *
713  */
716 }
717 
718 /**
719  * @brief re-entrance check of LTC state machine trigger function
720  *
721  * This function is not re-entrant and should only be called time- or event-triggered.
722  * It increments the triggerentry counter from the state variable ltc_state.
723  * It should never be called by two different processes, so if it is the case, triggerentry
724  * should never be higher than 0 when this function is called.
725  *
726  * @param ltc_state: state of the ltc state machine
727  *
728  * @return retval 0 if no further instance of the function is active, 0xff else
729  *
730  */
731 uint8_t LTC_CheckReEntrance(LTC_STATE_s *ltc_state) {
732  FAS_ASSERT(ltc_state != NULL_PTR);
733  uint8_t retval = 0;
734 
736  if (!ltc_state->triggerentry) {
737  ltc_state->triggerentry++;
738  } else {
739  retval = 0xFF; /* multiple calls of function */
740  }
742 
743  return (retval);
744 }
745 
747  FAS_ASSERT(ltc_state != NULL_PTR);
748  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
749 
751  retval.request = ltc_state->statereq.request;
752  retval.string = ltc_state->statereq.string;
754 
755  return (retval);
756 }
757 
759  FAS_ASSERT(ltc_state != NULL_PTR);
760  return ltc_state->state;
761 }
762 
763 /**
764  * @brief transfers the current state request to the state machine.
765  *
766  * This function takes the current state request from ltc_state and transfers it to the state machine.
767  * It resets the value from ltc_state to LTC_STATE_NO_REQUEST
768  *
769  * @param ltc_state: state of the ltc state machine
770  * @param pBusIDptr bus ID, main or backup (deprecated)
771  * @param pAdcModeptr LTC ADCmeasurement mode (fast, normal or filtered)
772  * @param pAdcMeasChptr number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
773  *
774  * @return retVal current state request, taken from LTC_STATE_REQUEST_e
775  *
776  */
778  LTC_STATE_s *ltc_state,
779  uint8_t *pBusIDptr,
780  LTC_ADCMODE_e *pAdcModeptr,
781  LTC_ADCMEAS_CHAN_e *pAdcMeasChptr) {
782  FAS_ASSERT(ltc_state != NULL_PTR);
783  FAS_ASSERT(pBusIDptr != NULL_PTR);
784  FAS_ASSERT(pAdcModeptr != NULL_PTR);
785  FAS_ASSERT(pAdcMeasChptr != NULL_PTR);
786  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
787 
789  retval.request = ltc_state->statereq.request;
790  retval.string = ltc_state->statereq.string;
791  ltc_state->requestedString = ltc_state->statereq.string;
792  *pAdcModeptr = ltc_state->adcModereq;
793  *pAdcMeasChptr = ltc_state->adcMeasChreq;
795  ltc_state->statereq.string = 0x0u;
797 
798  return (retval);
799 }
800 
802  FAS_ASSERT(ltc_state != NULL_PTR);
803  LTC_RETURN_TYPE_e retVal = LTC_ERROR;
804 
806  retVal = LTC_CheckStateRequest(ltc_state, statereq);
807 
808  if ((retVal == LTC_OK) || (retVal == LTC_BUSY_OK) || (retVal == LTC_OK_FROM_ERROR)) {
809  ltc_state->statereq = statereq;
810  }
812 
813  return (retVal);
814 }
815 
816 void LTC_Trigger(LTC_STATE_s *ltc_state) {
817  FAS_ASSERT(ltc_state != NULL_PTR);
818  STD_RETURN_TYPE_e retVal = STD_OK;
819  LTC_REQUEST_s statereq = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
820  uint8_t tmpbusID = 0;
823  STD_RETURN_TYPE_e continueFunction = STD_OK;
824 
825  FAS_ASSERT(ltc_state != NULL_PTR);
826 
827  /* Check re-entrance of function */
828  if (LTC_CheckReEntrance(ltc_state) > 0u) {
829  continueFunction = STD_NOT_OK;
830  }
831 
832  if (ltc_state->check_spi_flag == STD_NOT_OK) {
833  if (ltc_state->timer > 0u) {
834  if ((--ltc_state->timer) > 0u) {
835  ltc_state->triggerentry--;
836  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
837  }
838  }
839  } else {
840  if (AFE_IsTransmitOngoing(ltc_state) == true) {
841  if (ltc_state->timer > 0u) {
842  if ((--ltc_state->timer) > 0u) {
843  ltc_state->triggerentry--;
844  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
845  }
846  }
847  }
848  }
849 
850  if (continueFunction == STD_OK) {
851  switch (ltc_state->state) {
852  /****************************UNINITIALIZED***********************************/
854  /* waiting for Initialization Request */
855  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
856  if (statereq.request == LTC_STATE_INIT_REQUEST) {
857  LTC_SaveLastStates(ltc_state);
858  LTC_InitializeDatabase(ltc_state);
859  LTC_ResetErrorTable(ltc_state);
862  ltc_state->adcMode = tmpadcMode;
863  ltc_state->adcMeasCh = tmpadcMeasCh;
864  } else if (statereq.request == LTC_STATE_NO_REQUEST) {
865  /* no actual request pending */
866  } else {
867  ltc_state->ErrRequestCounter++; /* illegal request pending */
868  }
869  break;
870 
871  /****************************INITIALIZATION**********************************/
873 
874  LTC_SetTransferTimes(ltc_state);
875 
876  if (ltc_state->substate == LTC_INIT_STRING) {
877  LTC_SaveLastStates(ltc_state);
878  ltc_state->currentString = 0u;
879 
880  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
882  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
885  } else if (ltc_state->substate == LTC_ENTRY_INITIALIZATION) {
886  LTC_SaveLastStates(ltc_state);
887 
888  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
889  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
890  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
891  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
892 
893  retVal =
894  LTC_TRANSMIT_WAKE_UP(ltc_state->spiSeqPtr); /* Send dummy byte to wake up the daisy chain */
896  ltc_state,
897  retVal,
898  ltc_state->spiDiagErrorEntry,
905  } else if (ltc_state->substate == LTC_RE_ENTRY_INITIALIZATION) {
906  LTC_SaveLastStates(ltc_state);
907  retVal = LTC_TRANSMIT_WAKE_UP(
908  ltc_state->spiSeqPtr); /* Send dummy byte again to wake up the daisy chain */
910  ltc_state,
911  retVal,
912  ltc_state->spiDiagErrorEntry,
919  } else if (ltc_state->substate == LTC_START_INIT_INITIALIZATION) {
920  LTC_SaveLastStates(ltc_state);
921  ltc_state->check_spi_flag = STD_OK;
922  AFE_SetTransmitOngoing(ltc_state);
923  retVal = LTC_Init(
924  ltc_state->spiSeqPtr,
925  ltc_state->ltcData.txBuffer,
926  ltc_state->ltcData.rxBuffer,
927  ltc_state->ltcData.frameLength); /* Initialize main LTC loop */
928  ltc_state->lastsubstate = ltc_state->substate;
929  DIAG_CheckEvent(retVal, ltc_state->spiDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
931  ltc_state,
935  } else if (ltc_state->substate == LTC_CHECK_INITIALIZATION) {
936  /* Read values written in config register, currently unused */
937  LTC_SaveLastStates(ltc_state);
938  AFE_SetTransmitOngoing(ltc_state);
939  retVal = LTC_ReadRegister(
940  ltc_cmdRDCFG,
941  ltc_state->spiSeqPtr,
942  ltc_state->ltcData.txBuffer,
943  ltc_state->ltcData.rxBuffer,
944  ltc_state->ltcData.frameLength); /* Read config register */
946  ltc_state,
950  } else if (ltc_state->substate == LTC_EXIT_INITIALIZATION) {
951  LTC_SaveLastStates(ltc_state);
952  ++ltc_state->spiSeqPtr;
953  ++ltc_state->currentString;
954  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
957  } else {
960  }
961  }
962  break;
963 
964  /****************************INITIALIZED*************************************/
966  LTC_SaveLastStates(ltc_state);
968  break;
969 
970  /****************************START MEASUREMENT*******************************/
972 
975 
976  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
978  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
979  ltc_state->currentString = 0u;
980 
981  ltc_state->check_spi_flag = STD_NOT_OK;
982  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
983 
985  ltc_state,
986  retVal,
987  ltc_state->spiDiagErrorEntry,
990  (ltc_state->commandTransferTime +
991  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
995 
996  break;
997 
998  /****************************START MEASUREMENT CONTINUE*******************************/
999  /* Do not reset SPI interface pointer */
1001 
1004 
1005  ltc_state->check_spi_flag = STD_NOT_OK;
1006  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1007 
1009  ltc_state,
1010  retVal,
1011  ltc_state->spiDiagErrorEntry,
1014  (ltc_state->commandTransferTime +
1015  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1019 
1020  break;
1021 
1022  /****************************READ VOLTAGE************************************/
1024 
1026  ltc_state->check_spi_flag = STD_OK;
1027  AFE_SetTransmitOngoing(ltc_state);
1028  retVal = LTC_ReadRegister(
1029  ltc_cmdRDCVA,
1030  ltc_state->spiSeqPtr,
1031  ltc_state->ltcData.txBuffer,
1032  ltc_state->ltcData.rxBuffer,
1033  ltc_state->ltcData.frameLength);
1035  ltc_state,
1036  retVal,
1037  ltc_state->spiDiagErrorEntry,
1044  break;
1045  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE) {
1046  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1047  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1048  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1049 
1050  AFE_SetTransmitOngoing(ltc_state);
1051  retVal = LTC_ReadRegister(
1052  ltc_cmdRDCVB,
1053  ltc_state->spiSeqPtr,
1054  ltc_state->ltcData.txBuffer,
1055  ltc_state->ltcData.rxBuffer,
1056  ltc_state->ltcData.frameLength);
1058  ltc_state,
1059  retVal,
1060  ltc_state->spiDiagErrorEntry,
1067  break;
1068  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE) {
1069  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1070  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1071  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1072 
1073  AFE_SetTransmitOngoing(ltc_state);
1074  retVal = LTC_ReadRegister(
1075  ltc_cmdRDCVC,
1076  ltc_state->spiSeqPtr,
1077  ltc_state->ltcData.txBuffer,
1078  ltc_state->ltcData.rxBuffer,
1079  ltc_state->ltcData.frameLength);
1081  ltc_state,
1082  retVal,
1083  ltc_state->spiDiagErrorEntry,
1090  break;
1091  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE) {
1092  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1093  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1094  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1095 
1096  AFE_SetTransmitOngoing(ltc_state);
1097  retVal = LTC_ReadRegister(
1098  ltc_cmdRDCVD,
1099  ltc_state->spiSeqPtr,
1100  ltc_state->ltcData.txBuffer,
1101  ltc_state->ltcData.rxBuffer,
1102  ltc_state->ltcData.frameLength);
1103  if (LTC_6813_MAX_SUPPORTED_CELLS > 12u) {
1105  ltc_state,
1106  retVal,
1107  ltc_state->spiDiagErrorEntry,
1114  } else {
1116  ltc_state,
1117  retVal,
1118  ltc_state->spiDiagErrorEntry,
1125  }
1126  break;
1127  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE) {
1128  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1129  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1130  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1131 
1132  AFE_SetTransmitOngoing(ltc_state);
1133  retVal = LTC_ReadRegister(
1134  ltc_cmdRDCVE,
1135  ltc_state->spiSeqPtr,
1136  ltc_state->ltcData.txBuffer,
1137  ltc_state->ltcData.rxBuffer,
1138  ltc_state->ltcData.frameLength);
1139  if (LTC_6813_MAX_SUPPORTED_CELLS > 15u) {
1141  ltc_state,
1142  retVal,
1143  ltc_state->spiDiagErrorEntry,
1150  } else {
1152  ltc_state,
1153  retVal,
1154  ltc_state->spiDiagErrorEntry,
1161  }
1162  break;
1163  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE) {
1164  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1165  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1166  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1167 
1168  AFE_SetTransmitOngoing(ltc_state);
1169  retVal = LTC_ReadRegister(
1170  ltc_cmdRDCVF,
1171  ltc_state->spiSeqPtr,
1172  ltc_state->ltcData.txBuffer,
1173  ltc_state->ltcData.rxBuffer,
1174  ltc_state->ltcData.frameLength);
1176  ltc_state,
1177  retVal,
1178  ltc_state->spiDiagErrorEntry,
1185  break;
1186  } else if (ltc_state->substate == LTC_EXIT_READVOLTAGE) {
1187  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1188  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1189  if (LTC_6813_MAX_SUPPORTED_CELLS == 12u) {
1190  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1191  } else if (LTC_6813_MAX_SUPPORTED_CELLS == 15u) {
1192  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1193  } else if (LTC_6813_MAX_SUPPORTED_CELLS == 18u) {
1194  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 5, ltc_state->currentString);
1195  }
1196 
1197  /* Switch to different state if read voltage state is reused
1198  * e.g. open-wire check... */
1199  if (ltc_state->reusageMeasurementMode == LTC_NOT_REUSED) {
1200  LTC_SaveVoltages(ltc_state, ltc_state->currentString);
1202  ltc_state,
1206  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PUP) {
1208  ltc_state,
1212  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PDOWN) {
1214  ltc_state,
1218  }
1219  ltc_state->check_spi_flag = STD_NOT_OK;
1220  }
1221  break;
1222 
1223  /****************************MULTIPLEXED MEASUREMENT CONFIGURATION***********/
1225 
1228 
1229  if (ltc_state->substate == LTC_STATEMACH_MUXCONFIGURATION_INIT) {
1230  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1232 
1233  if (ltc_state->muxmeas_seqptr[ltc_state->currentString] >=
1234  ltc_state->muxmeas_seqendptr[ltc_state->currentString]) {
1235  /* last step of sequence reached (or no sequence configured) */
1236 
1237  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
1238  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
1239  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
1240  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
1241 
1242  LTC_SaveTemperatures(ltc_state, ltc_state->currentString);
1243  }
1244 
1245  ltc_state->check_spi_flag = STD_OK;
1246  AFE_SetTransmitOngoing(ltc_state);
1247  retVal = LTC_SetMuxChannel(
1248  ltc_state->spiSeqPtr,
1249  ltc_state->ltcData.txBuffer,
1250  ltc_state->ltcData.rxBuffer,
1251  ltc_state->ltcData.frameLength,
1252  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID, /* mux */
1253  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh /* channel */);
1254  if (retVal != STD_OK) {
1255  DIAG_Handler(
1257  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1259  ltc_state,
1263  } else {
1264  DIAG_Handler(
1265  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1267  ltc_state,
1271  }
1272  break;
1273  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
1274  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1275  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1276  DIAG_Handler(
1278  } else {
1279  DIAG_Handler(
1280  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1281  }
1282 
1283  AFE_SetTransmitOngoing(ltc_state);
1284  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1285  if (LTC_GOTO_MUX_CHECK == true) {
1287  ltc_state,
1288  retVal,
1289  ltc_state->spiDiagErrorEntry,
1296  ;
1297  } else {
1299  ltc_state,
1300  retVal,
1301  ltc_state->spiDiagErrorEntry,
1308  }
1309  break;
1311  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1312  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1313  DIAG_Handler(
1315  } else {
1316  DIAG_Handler(
1317  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1318  }
1319 
1320  AFE_SetTransmitOngoing(ltc_state);
1321  retVal = LTC_ReadRegister(
1322  ltc_cmdRDCOMM,
1323  ltc_state->spiSeqPtr,
1324  ltc_state->ltcData.txBuffer,
1325  ltc_state->ltcData.rxBuffer,
1326  ltc_state->ltcData.frameLength);
1328  ltc_state,
1329  retVal,
1330  ltc_state->spiDiagErrorEntry,
1337  break;
1339  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1340  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1341  DIAG_Handler(
1343  } else {
1344  DIAG_Handler(
1345  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1346  }
1347 
1348  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1349  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1350 
1351  /* if CRC OK: check multiplexer answer on i2C bus */
1352  retVal = LTC_I2cCheckAck(
1353  ltc_state,
1354  ltc_state->ltcData.rxBuffer,
1355  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID,
1356  ltc_state->currentString);
1357  DIAG_CheckEvent(retVal, ltc_state->muxDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1360  break;
1361  } else if (ltc_state->substate == LTC_STATEMACH_MUXMEASUREMENT) {
1362  if (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh == 0xFF) {
1363  /* actual multiplexer is switched off, so do not make a measurement and follow up with next step (mux configuration) */
1364  ++ltc_state
1365  ->muxmeas_seqptr[ltc_state->currentString]; /* go further with next step of sequence
1366  ltc_state.numberOfMeasuredMux not decremented, this does not count as a measurement */
1368  break;
1369  } else {
1370  if (LTC_GOTO_MUX_CHECK == false) {
1371  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1372  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1373  DIAG_Handler(
1374  ltc_state->spiDiagErrorEntry,
1376  DIAG_STRING,
1377  ltc_state->currentString);
1378  } else {
1379  DIAG_Handler(
1380  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1381  }
1382  }
1383 
1384  ltc_state->check_spi_flag = STD_NOT_OK;
1385  /* user multiplexer type -> connected to GPIO2! */
1386  if ((ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 1) ||
1387  (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 2)) {
1388  retVal = LTC_StartGpioMeasurement(
1389  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2);
1390  } else {
1391  retVal = LTC_StartGpioMeasurement(
1392  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO1);
1393  }
1394  }
1396  ltc_state,
1397  retVal,
1398  ltc_state->spiDiagErrorEntry,
1401  (ltc_state->commandTransferTime +
1403  ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2)), /* wait, ADAX-Command */
1407  break;
1408  } else if (ltc_state->substate == LTC_STATEMACH_READMUXMEASUREMENT) {
1409  ltc_state->check_spi_flag = STD_OK;
1410 
1411  AFE_SetTransmitOngoing(ltc_state);
1412  retVal = LTC_ReadRegister(
1413  ltc_cmdRDAUXA,
1414  ltc_state->spiSeqPtr,
1415  ltc_state->ltcData.txBuffer,
1416  ltc_state->ltcData.rxBuffer,
1417  ltc_state->ltcData.frameLength);
1419  ltc_state,
1420  retVal,
1421  ltc_state->spiDiagErrorEntry,
1428  break;
1429  } else if (ltc_state->substate == LTC_STATEMACH_STOREMUXMEASUREMENT) {
1430  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1431  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1432  DIAG_Handler(
1434  } else {
1435  DIAG_Handler(
1436  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1437  }
1438 
1439  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1440  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1442  ltc_state,
1443  ltc_state->ltcData.rxBuffer,
1444  ltc_state->muxmeas_seqptr[ltc_state->currentString],
1445  ltc_state->currentString);
1446 
1447  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1448 
1451  break;
1452  }
1453 
1454  break;
1455 
1456  /****************************END OF MEASUREMENT CYCLE************************/
1458 
1459  if (ltc_state->balance_control_done == STD_OK) {
1460  if (LTC_IsFirstMeasurementCycleFinished(ltc_state) == false) {
1462  }
1463  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
1464  if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST) {
1466  ltc_state,
1470  ltc_state->balance_control_done = STD_NOT_OK;
1471  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST) {
1473  ltc_state,
1477  ltc_state->balance_control_done = STD_NOT_OK;
1478  } else if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST_TI) {
1480  ltc_state,
1484  ltc_state->balance_control_done = STD_NOT_OK;
1485  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST_TI) {
1487  ltc_state,
1491  ltc_state->balance_control_done = STD_NOT_OK;
1492  } else if (statereq.request == LTC_STATE_EEPROM_READ_REQUEST) {
1495  } else if (statereq.request == LTC_STATE_EEPROM_WRITE_REQUEST) {
1498  ltc_state->balance_control_done = STD_NOT_OK;
1499  } else if (statereq.request == LTC_STATE_TEMP_SENS_READ_REQUEST) {
1502  ltc_state->balance_control_done = STD_NOT_OK;
1503  } else if (statereq.request == LTC_STATEMACH_BALANCEFEEDBACK_REQUEST) {
1506  ltc_state->balance_control_done = STD_NOT_OK;
1507  } else if (statereq.request == LTC_STATE_OPENWIRE_CHECK_REQUEST) {
1509  ltc_state,
1513  /* Send ADOW command with PUP two times */
1515  ltc_state->balance_control_done = STD_NOT_OK;
1516  } else {
1518  ltc_state,
1522  ltc_state->balance_control_done = STD_NOT_OK;
1523  }
1524  } else {
1527  }
1528 
1529  break;
1530 
1531  /****************************BALANCE CONTROL*********************************/
1533 
1534  if (ltc_state->substate == LTC_CONFIG_BALANCECONTROL) {
1535  ltc_state->check_spi_flag = STD_OK;
1536  AFE_SetTransmitOngoing(ltc_state);
1537  retVal = LTC_BalanceControl(
1538  ltc_state,
1539  ltc_state->spiSeqPtr,
1540  ltc_state->ltcData.txBuffer,
1541  ltc_state->ltcData.rxBuffer,
1542  ltc_state->ltcData.frameLength,
1543  0u,
1544  ltc_state->currentString);
1546  ltc_state,
1547  retVal,
1548  ltc_state->spiDiagErrorEntry,
1555  break;
1556  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL) {
1557  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1558  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1559  DIAG_Handler(
1561  } else {
1562  DIAG_Handler(
1563  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1564  }
1565 
1567  AFE_SetTransmitOngoing(ltc_state);
1568  retVal = LTC_BalanceControl(
1569  ltc_state,
1570  ltc_state->spiSeqPtr,
1571  ltc_state->ltcData.txBuffer,
1572  ltc_state->ltcData.rxBuffer,
1573  ltc_state->ltcData.frameLength,
1574  1u,
1575  ltc_state->currentString);
1577  ltc_state,
1578  retVal,
1579  ltc_state->spiDiagErrorEntry,
1586  } else {
1587  /* 12 cells, balancing control finished */
1588  ltc_state->check_spi_flag = STD_NOT_OK;
1589  ++ltc_state->spiSeqPtr;
1590  ++ltc_state->currentString;
1591  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1592  ltc_state->balance_control_done = STD_OK;
1594  } else {
1597  }
1598  }
1599 
1600  break;
1601  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL_END) {
1602  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1603  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1604  DIAG_Handler(
1606  } else {
1607  DIAG_Handler(
1608  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1609  }
1610  /* More than 12 cells, balancing control finished */
1611  ltc_state->check_spi_flag = STD_NOT_OK;
1612  ++ltc_state->spiSeqPtr;
1613  ++ltc_state->currentString;
1614  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1615  ltc_state->balance_control_done = STD_OK;
1617  } else {
1620  }
1621 
1622  break;
1623  }
1624  break;
1625 
1626  /****************************START MEASUREMENT*******************************/
1628 
1629  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1631 
1632  ltc_state->check_spi_flag = STD_NOT_OK;
1633  retVal = LTC_StartGpioMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1635  ltc_state,
1636  retVal,
1637  ltc_state->spiDiagErrorEntry,
1640  (ltc_state->commandTransferTime +
1641  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1643  LTC_ENTRY,
1644  LTC_STATEMACH_SHORTTIME); /* TODO: @koffel here same state is kept if error occurs */
1645  break;
1646 
1647  /****************************READ ALL GPIO VOLTAGE************************************/
1649 
1650  if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_A_RDAUXA) {
1651  ltc_state->check_spi_flag = STD_OK;
1652  AFE_SetTransmitOngoing(ltc_state);
1653  retVal = LTC_ReadRegister(
1654  ltc_cmdRDAUXA,
1655  ltc_state->spiSeqPtr,
1656  ltc_state->ltcData.txBuffer,
1657  ltc_state->ltcData.rxBuffer,
1658  ltc_state->ltcData.frameLength);
1660  ltc_state,
1661  retVal,
1662  ltc_state->spiDiagErrorEntry,
1669  break;
1670  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_B_RDAUXB) {
1671  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1672  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1673  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1674 
1675  AFE_SetTransmitOngoing(ltc_state);
1676  retVal = LTC_ReadRegister(
1677  ltc_cmdRDAUXB,
1678  ltc_state->spiSeqPtr,
1679  ltc_state->ltcData.txBuffer,
1680  ltc_state->ltcData.rxBuffer,
1681  ltc_state->ltcData.frameLength);
1682 
1683  if (LTC_6813_MAX_SUPPORTED_CELLS > 12u) {
1685  ltc_state,
1686  retVal,
1687  ltc_state->spiDiagErrorEntry,
1694  } else {
1696  ltc_state,
1697  retVal,
1698  ltc_state->spiDiagErrorEntry,
1705  }
1706  break;
1707  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_C_RDAUXC) {
1708  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1709  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1710  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1711 
1712  AFE_SetTransmitOngoing(ltc_state);
1713  retVal = LTC_ReadRegister(
1714  ltc_cmdRDAUXC,
1715  ltc_state->spiSeqPtr,
1716  ltc_state->ltcData.txBuffer,
1717  ltc_state->ltcData.rxBuffer,
1718  ltc_state->ltcData.frameLength);
1720  ltc_state,
1721  retVal,
1722  ltc_state->spiDiagErrorEntry,
1729  break;
1730  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_D_RDAUXD) {
1731  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1732  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1733  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1734 
1735  AFE_SetTransmitOngoing(ltc_state);
1736  retVal = LTC_ReadRegister(
1737  ltc_cmdRDAUXD,
1738  ltc_state->spiSeqPtr,
1739  ltc_state->ltcData.txBuffer,
1740  ltc_state->ltcData.rxBuffer,
1741  ltc_state->ltcData.frameLength);
1743  ltc_state,
1744  retVal,
1745  ltc_state->spiDiagErrorEntry,
1752  break;
1753  } else if (ltc_state->substate == LTC_EXIT_READAUXILIARY_ALLGPIOS) {
1754  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1755  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1756 
1757  if (LTC_6813_MAX_SUPPORTED_CELLS == 12u) {
1758  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1759  } else if (LTC_6813_MAX_SUPPORTED_CELLS > 12u) {
1760  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1761  }
1762 
1763  LTC_SaveAllGpioMeasurement(ltc_state);
1764 
1767  }
1768 
1769  break;
1770 
1771  /****************************BALANCE FEEDBACK*********************************/
1773 
1774  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1776 
1777  if (ltc_state->substate == LTC_ENTRY) {
1778  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1779  ltc_state->adcMode = LTC_ADCMODE_NORMAL_DCP0;
1781 
1782  ltc_state->check_spi_flag = STD_NOT_OK;
1783  retVal = LTC_StartGpioMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1785  ltc_state,
1786  retVal,
1787  ltc_state->spiDiagErrorEntry,
1790  (ltc_state->commandDataTransferTime +
1791  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1795  break;
1796  } else if (ltc_state->substate == LTC_READ_FEEDBACK_BALANCECONTROL) {
1797  ltc_state->check_spi_flag = STD_OK;
1798  AFE_SetTransmitOngoing(ltc_state);
1799  retVal = LTC_ReadRegister(
1800  ltc_cmdRDAUXA,
1801  ltc_state->spiSeqPtr,
1802  ltc_state->ltcData.txBuffer,
1803  ltc_state->ltcData.rxBuffer,
1804  ltc_state->ltcData.frameLength); /* read AUXA register */
1806  ltc_state,
1807  retVal,
1808  ltc_state->spiDiagErrorEntry,
1815  } else if (ltc_state->substate == LTC_SAVE_FEEDBACK_BALANCECONTROL) {
1816  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1817  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1818  DIAG_Handler(
1821  break;
1822  } else {
1823  DIAG_Handler(
1824  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1825  }
1826 
1827  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1828  DIAG_Handler(
1830  } else {
1831  DIAG_Handler(
1832  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1833  LTC_SaveBalancingFeedback(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1834  }
1836  break;
1837  }
1838  break;
1839 
1840  /****************************BOARD TEMPERATURE SENSOR*********************************/
1842 
1843  if (ltc_state->substate == LTC_TEMP_SENS_SEND_DATA1) {
1844  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1845  ltc_state->check_spi_flag = STD_OK;
1846  AFE_SetTransmitOngoing(ltc_state);
1847  retVal = LTC_SendI2cCommand(
1848  ltc_state->spiSeqPtr,
1849  ltc_state->ltcData.txBuffer,
1850  ltc_state->ltcData.rxBuffer,
1851  ltc_state->ltcData.frameLength,
1853 
1854  if (retVal != STD_OK) {
1855  DIAG_Handler(
1857  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1859  } else {
1860  DIAG_Handler(
1861  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1863  ltc_state,
1867  }
1868 
1869  break;
1870  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM1) {
1871  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1872  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1873  DIAG_Handler(
1876  break;
1877  } else {
1878  DIAG_Handler(
1879  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1880  }
1881 
1882  AFE_SetTransmitOngoing(ltc_state);
1883  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1885  ltc_state,
1886  retVal,
1887  ltc_state->spiDiagErrorEntry,
1894  break;
1895  } else if (ltc_state->substate == LTC_TEMP_SENS_READ_DATA1) {
1896  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1897  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1898  DIAG_Handler(
1901  break;
1902  } else {
1903  DIAG_Handler(
1904  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1905  }
1906 
1907  AFE_SetTransmitOngoing(ltc_state);
1908  retVal = LTC_SendI2cCommand(
1909  ltc_state->spiSeqPtr,
1910  ltc_state->ltcData.txBuffer,
1911  ltc_state->ltcData.rxBuffer,
1912  ltc_state->ltcData.frameLength,
1914 
1915  if (retVal != STD_OK) {
1916  DIAG_Handler(
1918  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1920  } else {
1921  DIAG_Handler(
1922  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1924  ltc_state,
1928  }
1929 
1930  break;
1931  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM2) {
1932  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1933  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1934  DIAG_Handler(
1937  break;
1938  } else {
1939  DIAG_Handler(
1940  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1941  }
1942 
1943  AFE_SetTransmitOngoing(ltc_state);
1944  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1946  ltc_state,
1947  retVal,
1948  ltc_state->spiDiagErrorEntry,
1955  break;
1957  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1958  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1959  DIAG_Handler(
1962  break;
1963  } else {
1964  DIAG_Handler(
1965  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1966  }
1967 
1968  AFE_SetTransmitOngoing(ltc_state);
1969  retVal = LTC_ReadRegister(
1970  ltc_cmdRDCOMM,
1971  ltc_state->spiSeqPtr,
1972  ltc_state->ltcData.txBuffer,
1973  ltc_state->ltcData.rxBuffer,
1974  ltc_state->ltcData.frameLength);
1976  ltc_state,
1977  retVal,
1978  ltc_state->spiDiagErrorEntry,
1985  break;
1986  } else if (ltc_state->substate == LTC_TEMP_SENS_SAVE_TEMP) {
1987  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1988  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1989  DIAG_Handler(
1992  break;
1993  } else {
1994  DIAG_Handler(
1995  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1996  }
1997 
1998  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1999  DIAG_Handler(
2001  } else {
2002  DIAG_Handler(
2003  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2004  LTC_TempSensSaveTemp(ltc_state, ltc_state->ltcData.rxBuffer);
2005  }
2006 
2008  break;
2009  }
2010  break;
2011 
2012  /****************************WRITE TO PORT EXPANDER IO***********/
2014 
2015  if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER) {
2016  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2017  ltc_state->check_spi_flag = STD_OK;
2018  AFE_SetTransmitOngoing(ltc_state);
2019  retVal = LTC_SetPortExpander(
2020  ltc_state,
2021  ltc_state->spiSeqPtr,
2022  ltc_state->ltcData.txBuffer,
2023  ltc_state->ltcData.rxBuffer,
2024  ltc_state->ltcData.frameLength);
2025 
2026  if (retVal != STD_OK) {
2027  DIAG_Handler(
2029  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2031  } else {
2032  DIAG_Handler(
2033  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2035  ltc_state,
2039  }
2040  break;
2041  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
2042  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2043  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2044  DIAG_Handler(
2047  break;
2048  } else {
2049  DIAG_Handler(
2050  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2051  }
2052 
2053  ltc_state->check_spi_flag = STD_NOT_OK;
2054  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2056  ltc_state,
2057  retVal,
2058  ltc_state->spiDiagErrorEntry,
2060  LTC_ENTRY,
2061  ltc_state->gpioClocksTransferTime,
2063  LTC_ENTRY,
2065  break;
2066  }
2067  break;
2068 
2069  /****************************READ FROM PORT EXPANDER IO***********/
2071 
2072  if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER) {
2073  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2074  ltc_state->check_spi_flag = STD_OK;
2075  AFE_SetTransmitOngoing(ltc_state);
2076  retVal = LTC_SendI2cCommand(
2077  ltc_state->spiSeqPtr,
2078  ltc_state->ltcData.txBuffer,
2079  ltc_state->ltcData.rxBuffer,
2080  ltc_state->ltcData.frameLength,
2082 
2083  if (retVal != STD_OK) {
2084  DIAG_Handler(
2086  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2088  } else {
2089  DIAG_Handler(
2090  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2092  ltc_state,
2096  }
2097 
2098  break;
2099  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM) {
2100  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2101  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2102  DIAG_Handler(
2105  break;
2106  } else {
2107  DIAG_Handler(
2108  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2109  }
2110 
2111  ltc_state->check_spi_flag = STD_NOT_OK;
2112  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2114  ltc_state,
2115  retVal,
2116  ltc_state->spiDiagErrorEntry,
2119  ltc_state->gpioClocksTransferTime,
2123  break;
2124  } else if (ltc_state->substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2125  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2126  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2127  DIAG_Handler(
2130  break;
2131  } else {
2132  DIAG_Handler(
2133  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2134  }
2135 
2136  AFE_SetTransmitOngoing(ltc_state);
2137  retVal = LTC_ReadRegister(
2138  ltc_cmdRDCOMM,
2139  ltc_state->spiSeqPtr,
2140  ltc_state->ltcData.txBuffer,
2141  ltc_state->ltcData.rxBuffer,
2142  ltc_state->ltcData.frameLength);
2144  ltc_state,
2145  retVal,
2146  ltc_state->spiDiagErrorEntry,
2153  break;
2154  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA) {
2155  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2156  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2157  DIAG_Handler(
2160  break;
2161  } else {
2162  DIAG_Handler(
2163  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2164  }
2165 
2166  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2167  DIAG_Handler(
2169  } else {
2170  DIAG_Handler(
2171  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2172  LTC_PortExpanderSaveValues(ltc_state, ltc_state->ltcData.rxBuffer);
2173  }
2174 
2176  break;
2177  }
2178 
2179  break;
2180 
2181  /****************************WRITE TO TI PORT EXPANDER IO***********/
2183 
2184  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2185  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2186  ltc_state->check_spi_flag = STD_OK;
2187  AFE_SetTransmitOngoing(ltc_state);
2189  ltc_state,
2190  ltc_state->spiSeqPtr,
2191  ltc_state->ltcData.txBuffer,
2192  ltc_state->ltcData.rxBuffer,
2193  ltc_state->ltcData.frameLength,
2196  ltc_state,
2197  retVal,
2198  ltc_state->spiDiagErrorEntry,
2203  LTC_ENTRY,
2205  break;
2206  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2207  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2208  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2209  DIAG_Handler(
2212  break;
2213  } else {
2214  DIAG_Handler(
2215  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2216  }
2217 
2218  ltc_state->check_spi_flag = STD_NOT_OK;
2219  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2221  ltc_state,
2222  retVal,
2223  ltc_state->spiDiagErrorEntry,
2226  ltc_state->gpioClocksTransferTime,
2228  LTC_ENTRY,
2230  break;
2231  } else if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER_TI) {
2232  ltc_state->check_spi_flag = STD_OK;
2233  AFE_SetTransmitOngoing(ltc_state);
2234  retVal = LTC_SetPortExpanderOutputTi(
2235  ltc_state,
2236  ltc_state->spiSeqPtr,
2237  ltc_state->ltcData.txBuffer,
2238  ltc_state->ltcData.rxBuffer,
2239  ltc_state->ltcData.frameLength);
2241  ltc_state,
2242  retVal,
2243  ltc_state->spiDiagErrorEntry,
2248  LTC_ENTRY,
2250  break;
2252  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2253  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2254  DIAG_Handler(
2257  break;
2258  } else {
2259  DIAG_Handler(
2260  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2261  }
2262 
2263  ltc_state->check_spi_flag = STD_NOT_OK;
2264  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2266  ltc_state,
2267  retVal,
2268  ltc_state->spiDiagErrorEntry,
2270  LTC_ENTRY,
2271  ltc_state->gpioClocksTransferTime,
2273  LTC_ENTRY,
2275  break;
2276  }
2277  break;
2278 
2279  /****************************READ TI PORT EXPANDER IO***********/
2281 
2282  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2283  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2284  ltc_state->check_spi_flag = STD_OK;
2285  AFE_SetTransmitOngoing(ltc_state);
2287  ltc_state,
2288  ltc_state->spiSeqPtr,
2289  ltc_state->ltcData.txBuffer,
2290  ltc_state->ltcData.rxBuffer,
2291  ltc_state->ltcData.frameLength,
2294  ltc_state,
2295  retVal,
2296  ltc_state->spiDiagErrorEntry,
2301  LTC_ENTRY,
2303  break;
2304  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2305  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2306  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2307  DIAG_Handler(
2310  break;
2311  } else {
2312  DIAG_Handler(
2313  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2314  }
2315 
2316  ltc_state->check_spi_flag = STD_NOT_OK;
2317  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2319  ltc_state,
2320  retVal,
2321  ltc_state->spiDiagErrorEntry,
2324  ltc_state->gpioClocksTransferTime,
2326  LTC_ENTRY,
2328  break;
2329  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST) {
2330  ltc_state->check_spi_flag = STD_OK;
2331  AFE_SetTransmitOngoing(ltc_state);
2332  retVal = LTC_GetPortExpanderInputTi(
2333  ltc_state,
2334  ltc_state->spiSeqPtr,
2335  ltc_state->ltcData.txBuffer,
2336  ltc_state->ltcData.rxBuffer,
2337  ltc_state->ltcData.frameLength,
2338  0);
2340  ltc_state,
2341  retVal,
2342  ltc_state->spiDiagErrorEntry,
2347  LTC_ENTRY,
2349  break;
2351  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2352  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2353  DIAG_Handler(
2356  break;
2357  } else {
2358  DIAG_Handler(
2359  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2360  }
2361 
2362  ltc_state->check_spi_flag = STD_NOT_OK;
2363  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2365  ltc_state,
2366  retVal,
2367  ltc_state->spiDiagErrorEntry,
2370  ltc_state->gpioClocksTransferTime,
2372  LTC_ENTRY,
2374  break;
2375  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND) {
2376  ltc_state->check_spi_flag = STD_OK;
2377  AFE_SetTransmitOngoing(ltc_state);
2378  retVal = LTC_GetPortExpanderInputTi(
2379  ltc_state,
2380  ltc_state->spiSeqPtr,
2381  ltc_state->ltcData.txBuffer,
2382  ltc_state->ltcData.rxBuffer,
2383  ltc_state->ltcData.frameLength,
2384  1);
2386  ltc_state,
2387  retVal,
2388  ltc_state->spiDiagErrorEntry,
2393  LTC_ENTRY,
2395  break;
2397  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2398  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2399  DIAG_Handler(
2402  break;
2403  } else {
2404  DIAG_Handler(
2405  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2406  }
2407 
2408  ltc_state->check_spi_flag = STD_NOT_OK;
2409  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2411  ltc_state,
2412  retVal,
2413  ltc_state->spiDiagErrorEntry,
2416  ltc_state->gpioClocksTransferTime,
2418  LTC_ENTRY,
2420  break;
2422  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2423  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2424  DIAG_Handler(
2427  break;
2428  } else {
2429  DIAG_Handler(
2430  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2431  }
2432 
2433  AFE_SetTransmitOngoing(ltc_state);
2434  retVal = LTC_ReadRegister(
2435  ltc_cmdRDCOMM,
2436  ltc_state->spiSeqPtr,
2437  ltc_state->ltcData.txBuffer,
2438  ltc_state->ltcData.rxBuffer,
2439  ltc_state->ltcData.frameLength);
2441  ltc_state,
2442  retVal,
2443  ltc_state->spiDiagErrorEntry,
2450  break;
2451  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA_TI) {
2452  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2453  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2454  DIAG_Handler(
2457  break;
2458  } else {
2459  DIAG_Handler(
2460  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2461  }
2462 
2463  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2464  DIAG_Handler(
2466  } else {
2467  DIAG_Handler(
2468  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2469  LTC_PortExpanderSaveValuesTi(ltc_state, ltc_state->ltcData.txBuffer);
2470  }
2471 
2473  break;
2474  }
2475 
2476  break;
2477 
2478  /****************************EEPROM READ*********************************/
2480 
2481  if (ltc_state->substate == LTC_EEPROM_READ_DATA1) {
2482  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2483  ltc_state->check_spi_flag = STD_OK;
2484  AFE_SetTransmitOngoing(ltc_state);
2485  retVal = LTC_SendEepromReadCommand(
2486  ltc_state,
2487  ltc_state->spiSeqPtr,
2488  ltc_state->ltcData.txBuffer,
2489  ltc_state->ltcData.rxBuffer,
2490  ltc_state->ltcData.frameLength,
2491  0);
2492 
2493  if (retVal != STD_OK) {
2494  DIAG_Handler(
2496  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2498  } else {
2499  DIAG_Handler(
2500  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2502  ltc_state,
2506  }
2507 
2508  break;
2509  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM1) {
2510  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2511  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2512  DIAG_Handler(
2515  break;
2516  } else {
2517  DIAG_Handler(
2518  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2519  }
2520 
2521  AFE_SetTransmitOngoing(ltc_state);
2522  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2524  ltc_state,
2525  retVal,
2526  ltc_state->spiDiagErrorEntry,
2533  break;
2534  } else if (ltc_state->substate == LTC_EEPROM_READ_DATA2) {
2535  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2536  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2537  DIAG_Handler(
2540  break;
2541  } else {
2542  DIAG_Handler(
2543  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2544  }
2545 
2546  AFE_SetTransmitOngoing(ltc_state);
2547  retVal = LTC_SendEepromReadCommand(
2548  ltc_state,
2549  ltc_state->spiSeqPtr,
2550  ltc_state->ltcData.txBuffer,
2551  ltc_state->ltcData.rxBuffer,
2552  ltc_state->ltcData.frameLength,
2553  1);
2555  ltc_state,
2556  retVal,
2557  ltc_state->spiDiagErrorEntry,
2564  break;
2565  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM2) {
2566  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2567  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2568  DIAG_Handler(
2571  break;
2572  } else {
2573  DIAG_Handler(
2574  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2575  }
2576 
2577  AFE_SetTransmitOngoing(ltc_state);
2578  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2580  ltc_state,
2581  retVal,
2582  ltc_state->spiDiagErrorEntry,
2589  break;
2590  } else if (ltc_state->substate == LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2591  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2592  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2593  DIAG_Handler(
2596  break;
2597  } else {
2598  DIAG_Handler(
2599  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2600  }
2601 
2602  AFE_SetTransmitOngoing(ltc_state);
2603  retVal = LTC_ReadRegister(
2604  ltc_cmdRDCOMM,
2605  ltc_state->spiSeqPtr,
2606  ltc_state->ltcData.txBuffer,
2607  ltc_state->ltcData.rxBuffer,
2608  ltc_state->ltcData.frameLength);
2610  ltc_state,
2611  retVal,
2612  ltc_state->spiDiagErrorEntry,
2619  break;
2620  } else if (ltc_state->substate == LTC_EEPROM_SAVE_READ) {
2621  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2622  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2623  DIAG_Handler(
2626  break;
2627  } else {
2628  DIAG_Handler(
2629  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2630  }
2631 
2632  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2633  DIAG_Handler(
2635  } else {
2636  DIAG_Handler(
2637  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2638  LTC_EepromSaveReadValue(ltc_state, ltc_state->ltcData.rxBuffer);
2639  }
2641  break;
2642  }
2643 
2644  break;
2645 
2646  /****************************EEPROM READ*********************************/
2648 
2649  if (ltc_state->substate == LTC_EEPROM_WRITE_DATA1) {
2650  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2651  ltc_state->check_spi_flag = STD_OK;
2652  AFE_SetTransmitOngoing(ltc_state);
2653  retVal = LTC_SendEepromWriteCommand(
2654  ltc_state,
2655  ltc_state->spiSeqPtr,
2656  ltc_state->ltcData.txBuffer,
2657  ltc_state->ltcData.rxBuffer,
2658  ltc_state->ltcData.frameLength,
2659  0);
2660 
2661  if (retVal != STD_OK) {
2662  DIAG_Handler(
2664  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2666  } else {
2667  DIAG_Handler(
2668  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2670  ltc_state,
2674  }
2675 
2676  break;
2677  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM3) {
2678  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2679  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2680  DIAG_Handler(
2683  break;
2684  } else {
2685  DIAG_Handler(
2686  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2687  }
2688 
2689  AFE_SetTransmitOngoing(ltc_state);
2690  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2692  ltc_state,
2693  retVal,
2694  ltc_state->spiDiagErrorEntry,
2701  break;
2702  } else if (ltc_state->substate == LTC_EEPROM_WRITE_DATA2) {
2703  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2704  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2705  DIAG_Handler(
2708  break;
2709  } else {
2710  DIAG_Handler(
2711  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2712  }
2713 
2714  AFE_SetTransmitOngoing(ltc_state);
2715  retVal = LTC_SendEepromWriteCommand(
2716  ltc_state,
2717  ltc_state->spiSeqPtr,
2718  ltc_state->ltcData.txBuffer,
2719  ltc_state->ltcData.rxBuffer,
2720  ltc_state->ltcData.frameLength,
2721  1);
2723  ltc_state,
2724  retVal,
2725  ltc_state->spiDiagErrorEntry,
2732  break;
2733  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM4) {
2734  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2735  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2736  DIAG_Handler(
2739  break;
2740  } else {
2741  DIAG_Handler(
2742  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2743  }
2744 
2745  AFE_SetTransmitOngoing(ltc_state);
2746  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2748  ltc_state,
2749  retVal,
2750  ltc_state->spiDiagErrorEntry,
2757  break;
2758  } else if (ltc_state->substate == LTC_EEPROM_FINISHED) {
2759  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2760  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2761  DIAG_Handler(
2764  break;
2765  } else {
2766  DIAG_Handler(
2767  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2768  }
2770  break;
2771  }
2772 
2773  break;
2774 
2775  /**************************OPEN-WIRE CHECK*******************************/
2777  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2778  /* This is necessary because the state machine will go through read voltage measurement registers */
2779  ltc_state->currentString = ltc_state->requestedString;
2781  /* Run ADOW command with PUP = 1 */
2782  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2783  ltc_state->check_spi_flag = STD_NOT_OK;
2784 
2785  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 1);
2786  if (retVal == STD_OK) {
2787  DIAG_Handler(
2788  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2790  ltc_state,
2793  (ltc_state->commandDataTransferTime +
2795  ltc_state->resendCommandCounter--;
2796 
2797  /* Check how many retries are left */
2798  if (ltc_state->resendCommandCounter == 0) {
2799  /* Switch to read voltage state to read cell voltages */
2801  ltc_state,
2804  (ltc_state->commandDataTransferTime +
2806  /* Reuse read voltage register */
2808  }
2809  } else {
2810  DIAG_Handler(
2814  }
2815  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK) {
2816  /* Previous state: Read voltage -> information stored in voltage buffer */
2818 
2819  /* Copy data from voltage struct into open-wire struct */
2820  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2821  for (uint16_t cb = 0u; cb < BS_NR_OF_CELL_BLOCKS_PER_MODULE; cb++) {
2822  ltc_state->ltcData.openWireDetection
2823  ->openWirePup[ltc_state->requestedString][(m * BS_NR_OF_CELL_BLOCKS_PER_MODULE) + cb] =
2824  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][m][cb];
2825  }
2826  }
2827 
2828  /* Set number of ADOW retries - send ADOW command with pull-down two times */
2831  ltc_state,
2835  } else if (ltc_state->substate == LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK) {
2836  /* Run ADOW command with PUP = 0 */
2837  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2838  ltc_state->check_spi_flag = STD_NOT_OK;
2839 
2840  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 0);
2841  if (retVal == STD_OK) {
2842  DIAG_Handler(
2843  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2845  ltc_state,
2848  (ltc_state->commandDataTransferTime +
2850  ltc_state->resendCommandCounter--;
2851 
2852  /* Check how many retries are left */
2853  if (ltc_state->resendCommandCounter == 0) {
2854  /* Switch to read voltage state to read cell voltages */
2856  ltc_state,
2859  (ltc_state->commandDataTransferTime +
2861  /* Reuse read voltage register */
2863  }
2864  } else {
2865  DIAG_Handler(
2869  }
2870  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLDOWN_OPENWIRE_CHECK) {
2871  /* Previous state: Read voltage -> information stored in voltage buffer */
2873 
2874  /* Copy data from voltage struct into open-wire struct */
2875  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2876  for (uint8_t cb = 0u; cb < BS_NR_OF_CELL_BLOCKS_PER_MODULE; cb++) {
2877  ltc_state->ltcData.openWireDetection
2878  ->openWirePdown[ltc_state->requestedString]
2879  [(m * BS_NR_OF_CELL_BLOCKS_PER_MODULE) + cb] =
2880  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][m][cb];
2881  }
2882  }
2885  } else if (ltc_state->substate == LTC_PERFORM_OPENWIRE_CHECK) {
2886  /* Perform actual open-wire check */
2887  for (uint8_t m = 0; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2888  /* Open-wire at C0: cell_pup(0) == 0 */
2889  if (ltc_state->ltcData.openWireDetection
2890  ->openWirePup[ltc_state->requestedString][0 + (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] ==
2891  0u) {
2892  ltc_state->ltcData.openWire->openWire[ltc_state->requestedString]
2893  [0 + (m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE))] = 1u;
2894  }
2895  /* Open-wire at Cmax: cell_pdown(BS_NR_OF_CELL_BLOCKS_PER_MODULE-1) == 0 */
2896  if (ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][(
2898  ltc_state->ltcData.openWire
2899  ->openWire[ltc_state->requestedString]
2901  1u;
2902  }
2903  }
2904 
2905  /* Take difference between pull-up and pull-down measurement */
2906  for (uint16_t i = 1u; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
2907  ltc_state->ltcData.openWireDetection->openWireDelta[ltc_state->requestedString][i] =
2908  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] -
2909  ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][i];
2910  }
2911 
2912  /* Open-wire at C(N): delta cell(n+1) < -400mV */
2913  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2914  for (uint8_t c = 1u; c < (BS_NR_OF_CELL_BLOCKS_PER_MODULE - 1); c++) {
2915  if (ltc_state->ltcData.openWireDetection
2916  ->openWireDelta[ltc_state->requestedString]
2918  ltc_state->ltcData.openWire->openWire[ltc_state->requestedString]
2919  [c + (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] = 1;
2920  }
2921  }
2922  }
2923 
2924  /* Write database entry */
2925  DATA_WRITE_DATA(ltc_state->ltcData.openWire);
2926  /* Start new measurement cycle */
2928  }
2929  break;
2930 
2931  /****************************DEFAULT**************************/
2932  default:
2933  /* invalid state */
2935  break;
2936  }
2937 
2938  ltc_state->triggerentry--; /* reentrance counter */
2939  } /* continueFunction */
2940 }
2941 
2942 /**
2943  * @brief saves the multiplexer values read from the LTC daisy-chain.
2944  *
2945  * After a voltage measurement was initiated on GPIO 1 to read the currently
2946  * selected multiplexer voltage, the results is read via SPI from the
2947  * daisy-chain.
2948  * This function is called to store the result from the transmission in a
2949  * buffer.
2950  *
2951  * @param ltc_state state of the ltc state machine
2952  * @param pRxBuff receive buffer
2953  * @param muxseqptr pointer to the multiplexer sequence, which
2954  * configures the currently selected multiplexer ID and
2955  * channel
2956  * @param stringNumber string addressed
2957  */
2959  LTC_STATE_s *ltc_state,
2960  uint16_t *pRxBuff,
2961  LTC_MUX_CH_CFG_s *muxseqptr,
2962  uint8_t stringNumber) {
2963  FAS_ASSERT(ltc_state != NULL_PTR);
2964  FAS_ASSERT(pRxBuff != NULL_PTR);
2965  FAS_ASSERT(muxseqptr != NULL_PTR);
2966  uint16_t val_ui = 0;
2967  int16_t temperature_ddegC = 0;
2968  uint8_t sensor_idx = 0;
2969  uint8_t ch_idx = 0;
2970  uint16_t buffer_LSB = 0;
2971  uint16_t buffer_MSB = 0;
2972 
2973  /* pointer to measurement Sequence of Mux- and Channel-Configurations (1,0xFF)...(3,0xFF),(0,1),...(0,7)) */
2974  /* Channel 0xFF means that the multiplexer is deactivated, therefore no measurement will be made and saved*/
2975  if (muxseqptr->muxCh != 0xFF) {
2976  /* user multiplexer type -> connected to GPIO2! */
2977  if ((muxseqptr->muxID == 1) || (muxseqptr->muxID == 2)) {
2978  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2979  if (muxseqptr->muxID == 1) {
2980  ch_idx = 0 + muxseqptr->muxCh; /* channel index 0..7 */
2981  } else {
2982  ch_idx = 8 + muxseqptr->muxCh; /* channel index 8..15 */
2983  }
2984 
2985  if (ch_idx < (2u * 8u)) {
2986  val_ui = *((uint16_t *)(&pRxBuff[6u + (1u * i * 8u)])); /* raw values, all mux on all LTCs */
2987  /* ltc_user_mux.value[i*8*2+ch_idx] = (uint16_t)(((float_t)(val_ui))*100e-6f*1000.0f); */ /* Unit -> in V -> in mV */
2988  }
2989  }
2990  } else {
2991  /* temperature multiplexer type -> connected to GPIO1! */
2992  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2993  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
2994  buffer_LSB = pRxBuff[4u + (i * 8u)];
2995  val_ui = buffer_LSB | (buffer_MSB << 8);
2996  /* val_ui = *((uint16_t *)(&pRxBuff[4+i*8])); */
2997  /* GPIO voltage in 100uV -> * 0.1 ---- conversion to mV */
2998  temperature_ddegC = LTC_ConvertMuxVoltagesToTemperatures(val_ui / 10u); /* unit: deci &deg;C */
2999  sensor_idx = ltc_muxsensortemperatur_cfg[muxseqptr->muxCh];
3000  /* wrong configuration! */
3001  if (sensor_idx >= BS_NR_OF_TEMP_SENSORS_PER_MODULE) {
3003  }
3004  /* Set bitmask for valid flags */
3005 
3006  /* Check LTC PEC error */
3007  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3008  /* Reset invalid flag */
3009  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] =
3010  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] &
3011  (~(1u << sensor_idx));
3012 
3013  ltc_state->ltcData.cellTemperature->cellTemperature_ddegC[stringNumber][i][sensor_idx] =
3014  temperature_ddegC;
3015  } else {
3016  /* Set invalid flag */
3017  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] |= (1u << sensor_idx);
3018  }
3019  }
3020  }
3021  }
3022 }
3023 
3024 /**
3025  * @brief saves the voltage values read from the LTC daisy-chain.
3026  *
3027  * After a voltage measurement was initiated to measure the voltages of the cells,
3028  * the result is read via SPI from the daisy-chain.
3029  * There are 6 register to read _(A,B,C,D,E,F) to get all cell voltages.
3030  * Only one register can be read at a time.
3031  * This function is called to store the result from the transmission in a buffer.
3032  *
3033  * @param ltc_state state of the ltc state machine
3034  * @param pRxBuff receive buffer
3035  * @param registerSet voltage register that was read (voltage register A,B,C,D,E or F)
3036  * @param stringNumber string addressed
3037  *
3038  */
3040  LTC_STATE_s *ltc_state,
3041  uint16_t *pRxBuff,
3042  uint8_t registerSet,
3043  uint8_t stringNumber) {
3044  FAS_ASSERT(ltc_state != NULL_PTR);
3045  FAS_ASSERT(pRxBuff != NULL_PTR);
3046  uint16_t cellOffset = 0;
3047  uint16_t voltage_index = 0;
3048  uint16_t val_ui = 0;
3049  uint16_t voltage = 0;
3050  uint32_t bitmask = 0;
3051  uint16_t buffer_LSB = 0;
3052  uint16_t buffer_MSB = 0;
3053  bool continueFunction = true;
3054 
3055  if (registerSet == 0u) {
3056  /* RDCVA command -> voltage register group A */
3057  cellOffset = 0;
3058  } else if (registerSet == 1u) {
3059  /* RDCVB command -> voltage register group B */
3060  cellOffset = 3;
3061  } else if (registerSet == 2u) {
3062  /* RDCVC command -> voltage register group C */
3063  cellOffset = 6;
3064  } else if (registerSet == 3u) {
3065  /* RDCVD command -> voltage register group D */
3066  cellOffset = 9;
3067  } else if (registerSet == 4u) {
3068  /* RDCVD command -> voltage register group E (only for 15 and 18 cell version) */
3069  cellOffset = 12;
3070  } else if (registerSet == 5u) {
3071  /* RDCVD command -> voltage register group F (only for 18 cell version) */
3072  cellOffset = 15;
3073  } else {
3074  continueFunction = false;
3075  }
3076 
3077  if (continueFunction == true) {
3078  /* Calculate bitmask for valid flags */
3079  bitmask |= 0x07u << cellOffset; /* 0x07: three voltages in each register */
3080 
3081  /* reinitialize index counter at begin of cycle */
3082  if (cellOffset == 0u) {
3083  (ltc_state->ltcData.usedCellIndex[stringNumber]) = 0;
3084  }
3085 
3086  /* Retrieve data without command and CRC*/
3087  for (uint16_t m = 0u; m < LTC_N_LTC; m++) {
3088  uint16_t incrementations = 0u;
3089 
3090  /* parse all three voltages (3 * 2bytes) contained in one register */
3091  for (uint8_t c = 0u; c < 3u; c++) {
3092  /* index considering maximum number of cells */
3093  voltage_index = c + cellOffset;
3094 
3095  if (ltc_voltage_input_used[voltage_index] == 1u) {
3096  buffer_MSB = pRxBuff[4u + (2u * c) + (m * 8u) + 1u];
3097  buffer_LSB = pRxBuff[4u + (2u * c) + (m * 8u)];
3098  val_ui = buffer_LSB | (buffer_MSB << 8u);
3099  /* val_ui = *((uint16_t *)(&pRxBuff[4+2*j+i*8])); */
3100  voltage = ((val_ui)) * 100e-6f * 1000.0f; /* Unit V -> in mV */
3101 
3102  /* Check PEC for every LTC in the daisy-chain */
3103  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][m] == true) {
3104  ltc_state->ltcData.cellVoltage
3105  ->cellVoltage_mV[stringNumber][m][ltc_state->ltcData.usedCellIndex[stringNumber]] = voltage;
3106  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3107  ltc_state->ltcData.cellVoltage
3108  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] &= bitmask;
3109  } else {
3110  /* PEC_valid == false: Invalidate only flags of this voltage register */
3111  ltc_state->ltcData.cellVoltage
3112  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] |= bitmask;
3113  }
3114 
3115  (ltc_state->ltcData.usedCellIndex[stringNumber])++;
3116  incrementations++;
3117 
3118  if ((ltc_state->ltcData.usedCellIndex[stringNumber]) > BS_NR_OF_CELL_BLOCKS_PER_MODULE) {
3119  break;
3120  }
3121  }
3122  }
3123 
3124  /* Restore start value for next module in the daisy-chain. Only
3125  * decrement used cell index if current module is not the last
3126  * module in the daisy-chain. */
3127  if ((m + 1u) < LTC_N_LTC) {
3128  (ltc_state->ltcData.usedCellIndex[stringNumber]) -= incrementations;
3129  }
3130  }
3131  }
3132 }
3133 
3134 /**
3135  * @brief saves the GPIO voltage values read from the LTC daisy-chain.
3136  *
3137  * After a voltage measurement was initiated to measure the voltages on all GPIOs,
3138  * the result is read via SPI from the daisy-chain. In order to read the result of all GPIO measurements,
3139  * it is necessary to read auxiliary register A and B.
3140  * Only one register can be read at a time.
3141  * This function is called to store the result from the transmission in a buffer.
3142  *
3143  * @param ltc_state state of the ltc state machine
3144  * @param pRxBuff receive buffer
3145  * @param registerSet voltage register that was read (auxiliary register A, B, C or D)
3146  * @param stringNumber string addressed
3147  *
3148  */
3150  LTC_STATE_s *ltc_state,
3151  uint16_t *pRxBuff,
3152  uint8_t registerSet,
3153  uint8_t stringNumber) {
3154  FAS_ASSERT(ltc_state != NULL_PTR);
3155  FAS_ASSERT(pRxBuff != NULL_PTR);
3156  uint8_t i_offset = 0;
3157  uint32_t bitmask = 0;
3158  uint16_t buffer_LSB = 0;
3159  uint16_t buffer_MSB = 0;
3160 
3161  if (registerSet == 0u) {
3162  /* RDAUXA command -> GPIO register group A */
3163  i_offset = 0;
3164  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3165  /* Retrieve data without command and CRC*/
3166  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3167  /* Check if PEC is valid */
3168  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3169  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3170  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3171  /* values received in 100uV -> divide by 10 to convert to mV */
3172  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3173  buffer_LSB = pRxBuff[4u + (i * 8u)];
3174  ltc_state->ltcData.allGpioVoltages
3175  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3176  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3177  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3178  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3179  buffer_LSB = pRxBuff[6u + (i * 8u)];
3180  ltc_state->ltcData.allGpioVoltages
3181  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3182  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3183  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3184  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3185  buffer_LSB = pRxBuff[8u + (i * 8u)];
3186  ltc_state->ltcData.allGpioVoltages
3187  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3188  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3189  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3190  } else {
3191  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3192  }
3193  }
3194  } else if (registerSet == 1u) {
3195  /* RDAUXB command -> GPIO register group B */
3196  i_offset = 3;
3197  bitmask = 0x03u << i_offset; /* 0x03: two temperatures in this register */
3198  /* Retrieve data without command and CRC*/
3199  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3200  /* Check if PEC is valid */
3201  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3202  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3203  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3204  /* values received in 100uV -> divide by 10 to convert to mV */
3205  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3206  buffer_LSB = pRxBuff[4u + (i * 8u)];
3207  ltc_state->ltcData.allGpioVoltages
3208  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3209  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3210  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3211  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3212  buffer_LSB = pRxBuff[6u + (i * 8u)];
3213  ltc_state->ltcData.allGpioVoltages
3214  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3215  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3216  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3217  } else {
3218  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3219  }
3220  }
3221  } else if (registerSet == 2u) {
3222  /* RDAUXC command -> GPIO register group C, for 18 cell version */
3223  i_offset = 5;
3224  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3225  /* Retrieve data without command and CRC*/
3226  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3227  /* Check if PEC is valid */
3228  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3229  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3230  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3231  /* values received in 100uV -> divide by 10 to convert to mV */
3232  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3233  buffer_LSB = pRxBuff[4u + (i * 8u)];
3234  ltc_state->ltcData.allGpioVoltages
3235  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3236  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3237  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3238  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3239  buffer_LSB = pRxBuff[6u + (i * 8u)];
3240  ltc_state->ltcData.allGpioVoltages
3241  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3242  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3243  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3244  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3245  buffer_LSB = pRxBuff[8u + (i * 8u)];
3246  ltc_state->ltcData.allGpioVoltages
3247  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3248  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3249  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3250  } else {
3251  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3252  }
3253  }
3254  } else if (registerSet == 3u) {
3255  /* RDAUXD command -> GPIO register group D, for 18 cell version */
3256  i_offset = 8;
3257  bitmask = 0x01u << i_offset; /* 0x01: one temperature in this register */
3258  /* Retrieve data without command and CRC*/
3259  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3260  /* Check if PEC is valid */
3261  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3262  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3263  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3264  /* values received in 100uV -> divide by 10 to convert to mV */
3265  ltc_state->ltcData.allGpioVoltages
3266  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3267  *((uint16_t *)(&pRxBuff[4u + (i * 8u)])) / 10u;
3268  } else {
3269  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3270  }
3271  }
3272  } else {
3273  ; /* Nothing to do */
3274  }
3275 }
3276 
3277 /**
3278  * @brief checks if the multiplexers acknowledged transmission.
3279  *
3280  * The RDCOMM command can be used to read the answer of the multiplexers to a
3281  * I2C transmission.
3282  * This function determines if the communication with the multiplexers was
3283  * successful or not.
3284  * The array error table is updated to locate the multiplexers that did not
3285  * acknowledge transmission.
3286  *
3287  * @param ltc_state state of the ltc state machine
3288  * @param pRxBuff receive buffer
3289  * @param mux multiplexer to be addressed (multiplexer ID)
3290  * @param stringNumber string addressed
3291  *
3292  * @return STD_OK if there was no error, STD_NOT_OK if there was errors
3293  */
3294 static STD_RETURN_TYPE_e LTC_I2cCheckAck(LTC_STATE_s *ltc_state, uint16_t *pRxBuff, uint8_t mux, uint8_t stringNumber) {
3295  FAS_ASSERT(ltc_state != NULL_PTR);
3296  FAS_ASSERT(pRxBuff != NULL_PTR);
3297  STD_RETURN_TYPE_e muxError = STD_OK;
3298 
3299  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
3300  if ((pRxBuff[4u + 1u + (LTC_NUMBER_OF_LTC_PER_MODULE * i * 8u)] & 0x0Fu) != 0x07u) { /* ACK = 0xX7 */
3301  if (LTC_DISCARD_MUX_CHECK == false) {
3302  if (mux == 0u) {
3303  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 1;
3304  }
3305  if (mux == 1u) {
3306  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 1;
3307  }
3308  if (mux == 2u) {
3309  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 1;
3310  }
3311  if (mux == 3u) {
3312  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 1;
3313  }
3314  }
3315  muxError = STD_NOT_OK;
3316  } else {
3317  if (mux == 0u) {
3318  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 0;
3319  }
3320  if (mux == 1u) {
3321  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 0;
3322  }
3323  if (mux == 2u) {
3324  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 0;
3325  }
3326  if (mux == 3u) {
3327  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 0;
3328  }
3329  }
3330  }
3331 
3332  if (LTC_DISCARD_MUX_CHECK == true) {
3333  muxError = STD_OK;
3334  }
3335  return muxError;
3336 }
3337 
3338 /**
3339  * @brief initialize the daisy-chain.
3340  *
3341  * To initialize the LTC6804 daisy-chain, a dummy byte (0x00) is sent.
3342  *
3343  * @param pSpiInterface pointer to SPI configuration
3344  * @param pTxBuff transmit buffer
3345  * @param pRxBuff receive buffer
3346  * @param frameLength number of words to transmit
3347  *
3348  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3349  *
3350  */
3352  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3353  uint16_t *pTxBuff,
3354  uint16_t *pRxBuff,
3355  uint32_t frameLength) {
3356  FAS_ASSERT(pSpiInterface != NULL_PTR);
3357  FAS_ASSERT(pTxBuff != NULL_PTR);
3358  FAS_ASSERT(pRxBuff != NULL_PTR);
3359  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3360 
3361  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES];
3362  uint16_t PEC_result = 0;
3363 
3364  /* now construct the message to be sent: it contains the wanted data, PLUS the needed PECs */
3365  pTxBuff[0] = ltc_cmdWRCFG[0];
3366  pTxBuff[1] = ltc_cmdWRCFG[1];
3367  pTxBuff[2] = ltc_cmdWRCFG[2];
3368  pTxBuff[3] = ltc_cmdWRCFG[3];
3369 
3370  /* set REFON bit to 1 */
3371  /* data for the configuration */
3372  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3373  /* FC = disable all pull-downs, REFON = 1, DTEN = 0, ADCOPT = 0 */
3374  pTxBuff[4u + (i * 8u)] = 0xFC;
3375  pTxBuff[5u + (i * 8u)] = 0x00;
3376  pTxBuff[6u + (i * 8u)] = 0x00;
3377  pTxBuff[7u + (i * 8u)] = 0x00;
3378  pTxBuff[8u + (i * 8u)] = 0x00;
3379  pTxBuff[9u + (i * 8u)] = 0x00;
3380 
3381  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3382  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3383  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3384  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3385  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3386  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3387 
3388  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3389  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3390  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3391  } /* end for */
3392 
3393  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3394 
3395  return retVal;
3396 }
3397 
3398 /**
3399  * @brief sets the balancing according to the control values read in the database.
3400  *
3401  * To set balancing for the cells, the corresponding bits have to be written in the configuration register.
3402  * The LTC driver only executes the balancing orders written by the BMS in the database.
3403  *
3404  * @param ltc_state state of the ltc state machine
3405  * @param pSpiInterface pointer to SPI configuration
3406  * @param pTxBuff transmit buffer
3407  * @param pRxBuff receive buffer
3408  * @param frameLength number of words to transmit
3409  * @param registerSet register Set, 0: cells 1 to 12 (WRCFG), 1: cells 13 to 15/18 (WRCFG2)
3410  * @param stringNumber string addressed
3411  *
3412  * @return STD_OK if dummy byte was sent correctly by SPI, STD_NOT_OK otherwise
3413  *
3414  */
3416  LTC_STATE_s *ltc_state,
3417  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3418  uint16_t *pTxBuff,
3419  uint16_t *pRxBuff,
3420  uint32_t frameLength,
3421  uint8_t registerSet,
3422  uint8_t stringNumber) {
3423  FAS_ASSERT(ltc_state != NULL_PTR);
3424  FAS_ASSERT(pSpiInterface != NULL_PTR);
3425  FAS_ASSERT(pTxBuff != NULL_PTR);
3426  FAS_ASSERT(pRxBuff != NULL_PTR);
3427  STD_RETURN_TYPE_e retVal = STD_OK;
3428 
3429  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES];
3430  uint16_t PEC_result = 0;
3431 
3432  LTC_GetBalancingControlValues(ltc_state);
3433 
3434  if (registerSet == 0u) { /* cells 1 to 12, WRCFG */
3435  pTxBuff[0] = ltc_cmdWRCFG[0];
3436  pTxBuff[1] = ltc_cmdWRCFG[1];
3437  pTxBuff[2] = ltc_cmdWRCFG[2];
3438  pTxBuff[3] = ltc_cmdWRCFG[3];
3439  for (uint16_t j = 0; j < BS_NR_OF_MODULES_PER_STRING; j++) {
3440  /* The daisy-chain works like a shift register, so the order has to be reversed:
3441  when addressing e.g. the first module in the daisy-chain, the data will be sent last on the SPI bus and
3442  when addressing e.g. the last module in the daisy-chain, the data will be sent first on the SPI bus */
3443  const uint16_t reverseModuleNumber = BS_NR_OF_MODULES_PER_STRING - j - 1u;
3444 
3445  /* FC = disable all pull-downs, REFON = 1 (reference always on), DTEN off, ADCOPT = 0 */
3446  pTxBuff[4u + (reverseModuleNumber * 8u)] = 0xFC;
3447  pTxBuff[5u + (reverseModuleNumber * 8u)] = 0x00;
3448  pTxBuff[6u + (reverseModuleNumber * 8u)] = 0x00;
3449  pTxBuff[7u + (reverseModuleNumber * 8u)] = 0x00;
3450  pTxBuff[8u + (reverseModuleNumber * 8u)] = 0x00;
3451  pTxBuff[9u + (reverseModuleNumber * 8u)] = 0x00;
3452 
3453  if (ltc_state->ltcData.balancingControl
3454  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 0u] == 1u) {
3455  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x01u;
3456  }
3457  if (ltc_state->ltcData.balancingControl
3458  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 1u] == 1u) {
3459  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x02u;
3460  }
3461  if (ltc_state->ltcData.balancingControl
3462  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 2u] == 1u) {
3463  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x04u;
3464  }
3465  if (ltc_state->ltcData.balancingControl
3466  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 3u] == 1u) {
3467  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x08u;
3468  }
3469  if (ltc_state->ltcData.balancingControl
3470  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 4u] == 1u) {
3471  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x10u;
3472  }
3473  if (ltc_state->ltcData.balancingControl
3474  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 5u] == 1u) {
3475  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x20u;
3476  }
3477  if (ltc_state->ltcData.balancingControl
3478  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 6u] == 1u) {
3479  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x40u;
3480  }
3481  if (ltc_state->ltcData.balancingControl
3482  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 7u] == 1u) {
3483  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x80u;
3484  }
3485  if (ltc_state->ltcData.balancingControl
3486  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 8u] == 1u) {
3487  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x01u;
3488  }
3489  if (ltc_state->ltcData.balancingControl
3490  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 9u] == 1u) {
3491  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x02u;
3492  }
3493  if (ltc_state->ltcData.balancingControl
3494  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 10u] == 1u) {
3495  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x04u;
3496  }
3497  if (ltc_state->ltcData.balancingControl
3498  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 11u] == 1u) {
3499  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x08u;
3500  }
3501 
3502  PEC_Check[0] = pTxBuff[4u + (reverseModuleNumber * 8u)];
3503  PEC_Check[1] = pTxBuff[5u + (reverseModuleNumber * 8u)];
3504  PEC_Check[2] = pTxBuff[6u + (reverseModuleNumber * 8u)];
3505  PEC_Check[3] = pTxBuff[7u + (reverseModuleNumber * 8u)];
3506  PEC_Check[4] = pTxBuff[8u + (reverseModuleNumber * 8u)];
3507  PEC_Check[5] = pTxBuff[9u + (reverseModuleNumber * 8u)];
3508 
3509  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3510  pTxBuff[10u + (reverseModuleNumber * 8u)] = (PEC_result >> 8u) & 0xFFu;
3511  pTxBuff[11u + (reverseModuleNumber * 8u)] = PEC_result & 0xFFu;
3512  }
3513  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3514  } else if (registerSet == 1u) { /* cells 13 to 15/18 WRCFG2 */
3515  pTxBuff[0] = ltc_cmdWRCFG2[0];
3516  pTxBuff[1] = ltc_cmdWRCFG2[1];
3517  pTxBuff[2] = ltc_cmdWRCFG2[2];
3518  pTxBuff[3] = ltc_cmdWRCFG2[3];
3519  for (uint16_t j = 0; j < BS_NR_OF_MODULES_PER_STRING; j++) {
3520  /* The daisy-chain works like a shift register, so the order has to be reversed:
3521  when addressing e.g. the first module in the daisy-chain, the data will be sent last on the SPI bus and
3522  when addressing e.g. the last module in the daisy-chain, the data will be sent first on the SPI bus */
3523  const uint16_t reverseModuleNumber = BS_NR_OF_MODULES_PER_STRING - j - 1u;
3524 
3525  /* 0x0F = disable pull-downs on GPIO6-9 */
3526  pTxBuff[4u + (reverseModuleNumber * 8u)] = 0x0F;
3527  pTxBuff[5u + (reverseModuleNumber * 8u)] = 0x00;
3528  pTxBuff[6u + (reverseModuleNumber * 8u)] = 0x00;
3529  pTxBuff[7u + (reverseModuleNumber * 8u)] = 0x00;
3530  pTxBuff[8u + (reverseModuleNumber * 8u)] = 0x00;
3531  pTxBuff[9u + (reverseModuleNumber * 8u)] = 0x00;
3532 
3533  if (ltc_state->ltcData.balancingControl
3534  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 12u] == 1u) {
3535  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x10u;
3536  }
3537  if (ltc_state->ltcData.balancingControl
3538  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 13u] == 1u) {
3539  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x20u;
3540  }
3541  if (ltc_state->ltcData.balancingControl
3542  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 14u] == 1u) {
3543  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x40u;
3544  }
3545  if (BS_NR_OF_CELL_BLOCKS_PER_MODULE > 15u) {
3546  if (ltc_state->ltcData.balancingControl
3547  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 15u] == 1u) {
3548  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x80u;
3549  }
3550  if (ltc_state->ltcData.balancingControl
3551  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 16u] == 1u) {
3552  pTxBuff[5u + (reverseModuleNumber * 8u)] |= 0x01u;
3553  }
3554  if (ltc_state->ltcData.balancingControl
3555  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 17u] == 1u) {
3556  pTxBuff[5u + (reverseModuleNumber * 8u)] |= 0x02u;
3557  }
3558  }
3559 
3560  PEC_Check[0] = pTxBuff[4u + (reverseModuleNumber * 8u)];
3561  PEC_Check[1] = pTxBuff[5u + (reverseModuleNumber * 8u)];
3562  PEC_Check[2] = pTxBuff[6u + (reverseModuleNumber * 8u)];
3563  PEC_Check[3] = pTxBuff[7u + (reverseModuleNumber * 8u)];
3564  PEC_Check[4] = pTxBuff[8u + (reverseModuleNumber * 8u)];
3565  PEC_Check[5] = pTxBuff[9u + (reverseModuleNumber * 8u)];
3566 
3567  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3568  pTxBuff[10u + (reverseModuleNumber * 8u)] = (PEC_result >> 8u) & 0xFFu;
3569  pTxBuff[11u + (reverseModuleNumber * 8u)] = PEC_result & 0xFFu;
3570  }
3571  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3572  } else {
3573  retVal = STD_NOT_OK;
3574  }
3575  return retVal;
3576 }
3577 
3578 /**
3579  * @brief resets the error table.
3580  *
3581  * This function should be called during initialization or before starting a new measurement cycle
3582  *
3583  * @param ltc_state: state of the ltc state machine
3584  *
3585  */
3586 static void LTC_ResetErrorTable(LTC_STATE_s *ltc_state) {
3587  FAS_ASSERT(ltc_state != NULL_PTR);
3588  for (uint8_t s = 0u; s < BS_NR_OF_STRINGS; s++) {
3589  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3590  ltc_state->ltcData.errorTable->PEC_valid[s][i] = false;
3591  ltc_state->ltcData.errorTable->mux0[s][i] = 0;
3592  ltc_state->ltcData.errorTable->mux1[s][i] = 0;
3593  ltc_state->ltcData.errorTable->mux2[s][i] = 0;
3594  ltc_state->ltcData.errorTable->mux3[s][i] = 0;
3595  }
3596  }
3597 }
3598 
3599 /**
3600  * @brief brief missing
3601  * @details Gets the measurement time needed by the LTC analog front-end,
3602  * depending on the measurement mode and the number of channels.
3603  * For all cell voltages or all 5 GPIOS, the measurement time is the
3604  * same.
3605  * For 2 cell voltages or 1 GPIO, the measurement time is the same.
3606  * As a consequence, this function is used for cell voltage and for
3607  * GPIO measurement.
3608  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3609  * @param adcMeasCh number of channels measured for GPIOS (one at a time
3610  * for multiplexers or all five GPIOs) or number of cell
3611  * voltage measured (2 cells or all cells)
3612  * @return measurement time in ms
3613  */
3614 static uint16_t LTC_GetMeasurementTimeCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh) {
3615  uint16_t retVal = LTC_ADCMEAS_UNDEFINED; /* default */
3616 
3617  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_CELLS) {
3618  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3620  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3622  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3624  }
3625  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
3626  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3628  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3630  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3632  }
3633  } else if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_GPIOS) {
3634  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3636  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3638  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3640  }
3641  } else if (
3642  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) ||
3643  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO4) ||
3644  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO5)) {
3645  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3647  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3649  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3651  }
3652  } else {
3653  retVal = LTC_ADCMEAS_UNDEFINED;
3654  }
3655 
3656  return retVal;
3657 }
3658 
3659 /**
3660  * @brief tells the LTC daisy-chain to start measuring the voltage on all cells.
3661  *
3662  * This function sends an instruction to the daisy-chain via SPI, in order to start voltage measurement for all cells.
3663  *
3664  * @param pSpiInterface pointer to SPI configuration
3665  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3666  * @param adcMeasCh number of cell voltage measured (2 cells or all cells)
3667  *
3668  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3669  *
3670  */
3672  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3673  LTC_ADCMODE_e adcMode,
3674  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3675  FAS_ASSERT(pSpiInterface != NULL_PTR);
3676  STD_RETURN_TYPE_e retVal = STD_OK;
3677 
3678  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_CELLS) {
3679  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3680  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_fast_DCP0);
3681  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3682  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_normal_DCP0);
3683  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3684  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_filtered_DCP0);
3685  } else if (adcMode == LTC_ADCMODE_FAST_DCP1) {
3686  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_fast_DCP1);
3687  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP1) {
3688  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_normal_DCP1);
3689  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP1) {
3690  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_filtered_DCP1);
3691  } else {
3692  retVal = STD_NOT_OK;
3693  }
3694  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
3695  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3696  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_fast_DCP0_twocells);
3697  } else {
3698  retVal = STD_NOT_OK;
3699  }
3700  } else {
3701  retVal = STD_NOT_OK;
3702  }
3703  return retVal;
3704 }
3705 
3706 /**
3707  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3708  * @details This function sends an instruction to the daisy-chain via SPI to
3709  * start the measurement.
3710  *
3711  * @param pSpiInterface pointer to SPI configuration
3712  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3713  * @param adcMeasCh number of channels measured for GPIOS (one at a
3714  * time, typically when multiplexers are used, or all
3715  * five GPIOs)
3716  *
3717  * @return #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK
3718  * otherwise
3719  */
3721  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3722  LTC_ADCMODE_e adcMode,
3723  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3724  FAS_ASSERT(pSpiInterface != NULL_PTR);
3725  STD_RETURN_TYPE_e retVal;
3726 
3727  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_GPIOS) {
3728  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3729  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_ALLGPIOS);
3730  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3731  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_ALLGPIOS);
3732  } else {
3733  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3734  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_ALLGPIOS);
3735  }
3736  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) {
3737  /* Single Channel */
3738  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3739  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_GPIO1);
3740  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3741  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_GPIO1);
3742  } else {
3743  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3744 
3745  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_GPIO1);
3746  }
3747  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) {
3748  /* Single Channel */
3749  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3750  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_GPIO2);
3751  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3752  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_GPIO2);
3753  } else {
3754  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3755 
3756  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_GPIO2);
3757  }
3758  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) {
3759  /* Single Channel */
3760  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3761  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_GPIO3);
3762  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3763  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_GPIO3);
3764  } else {
3765  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3766 
3767  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_GPIO3);
3768  }
3769  } else {
3770  retVal = STD_NOT_OK;
3771  }
3772 
3773  return retVal;
3774 }
3775 
3776 /**
3777  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3778  *
3779  * This function sends an instruction to the daisy-chain via SPI to start the measurement.
3780  *
3781  * @param pSpiInterface pointer to SPI configuration
3782  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3783  * @param PUP pull-up bit for pull-up or pull-down current (0: pull-down, 1: pull-up)
3784  *
3785  * @return retVal #STD_OK if command was sent correctly by SPI, #STD_NOT_OK otherwise
3786  *
3787  */
3789  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3790  LTC_ADCMODE_e adcMode,
3791  uint8_t PUP) {
3792  FAS_ASSERT(pSpiInterface != NULL_PTR);
3793  STD_RETURN_TYPE_e retval = STD_NOT_OK;
3794  if (PUP == 0u) {
3795  /* pull-down current */
3796  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3797  retval = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_BC_cmdADOW_PDOWN_normal_DCP0);
3798  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3800  } else {
3801  retval = STD_NOT_OK;
3802  }
3803  } else if (PUP == 1u) {
3804  /* pull-up current */
3805  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3806  retval = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_BC_cmdADOW_PUP_normal_DCP0);
3807  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3808  retval = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_BC_cmdADOW_PUP_filtered_DCP0);
3809  } else {
3810  retval = STD_NOT_OK;
3811  }
3812  }
3813  return retval;
3814 }
3815 
3816 /**
3817  * @brief checks if the data received from the daisy-chain is not corrupt.
3818  *
3819  * This function computes the PEC (CRC) from the data received by the daisy-chain.
3820  * It compares it with the PEC sent by the LTCs.
3821  * If there are errors, the array LTC_ErrorTable is updated to locate the LTCs in daisy-chain
3822  * that transmitted corrupt data.
3823  *
3824  * @param ltc_state state of the ltc state machine
3825  * @param DataBufferSPI_RX_with_PEC data obtained from the SPI transmission
3826  * @param stringNumber string addressed
3827  *
3828  * @return retVal STD_OK if PEC check is OK, STD_NOT_OK otherwise
3829  *
3830  */
3832  LTC_STATE_s *ltc_state,
3833  uint16_t *DataBufferSPI_RX_with_PEC,
3834  uint8_t stringNumber) {
3835  FAS_ASSERT(ltc_state != NULL_PTR);
3836  FAS_ASSERT(DataBufferSPI_RX_with_PEC != NULL_PTR);
3837  STD_RETURN_TYPE_e retVal = STD_OK;
3838  uint8_t PEC_TX[2];
3839  uint16_t PEC_result = 0;
3840  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3841 
3842  /* check all PECs and put data without command and PEC in DataBufferSPI_RX (easier to use) */
3843  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3844  PEC_Check[0] = DataBufferSPI_RX_with_PEC[4u + (i * 8u)];
3845  PEC_Check[1] = DataBufferSPI_RX_with_PEC[5u + (i * 8u)];
3846  PEC_Check[2] = DataBufferSPI_RX_with_PEC[6u + (i * 8u)];
3847  PEC_Check[3] = DataBufferSPI_RX_with_PEC[7u + (i * 8u)];
3848  PEC_Check[4] = DataBufferSPI_RX_with_PEC[8u + (i * 8u)];
3849  PEC_Check[5] = DataBufferSPI_RX_with_PEC[9u + (i * 8u)];
3850 
3851  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3852  PEC_TX[0] = (uint8_t)((PEC_result >> 8u) & 0xFFu);
3853  PEC_TX[1] = (uint8_t)(PEC_result & 0xFFu);
3854 
3855  /* if calculated PEC not equal to received PEC */
3856  if ((PEC_TX[0] != DataBufferSPI_RX_with_PEC[10u + (i * 8u)]) ||
3857  (PEC_TX[1] != DataBufferSPI_RX_with_PEC[11u + (i * 8u)])) {
3858  /* update error table of the corresponding LTC only if PEC check is activated */
3859  if (LTC_DISCARD_PEC == false) {
3860  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = false;
3861  retVal = STD_NOT_OK;
3862  } else {
3863  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3864  }
3865  } else {
3866  /* update error table of the corresponding LTC */
3867  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3868  }
3869  }
3870  return retVal;
3871 }
3872 
3873 /**
3874  * @brief send command to the LTC daisy-chain and receives data from the LTC
3875  * daisy-chain.
3876  * @details This is the core function to receive data from the LTC6813-1
3877  * daisy-chain.
3878  * A 2 byte command is sent with the corresponding PEC.
3879  * *Example*: read configuration register (RDCFG).
3880  * Only command has to be set, the function calculates the PEC
3881  * automatically.
3882  * - The data sent is:
3883  * - 2 bytes (COMMAND) 2 bytes (PEC)
3884  * - The data received is:
3885  * - 6 bytes (LTC1) 2 bytes (PEC) +
3886  * - 6 bytes (LTC2) 2 bytes (PEC) +
3887  * - 6 bytes (LTC3) 2 bytes (PEC) +
3888  * - ... +
3889  * - 6 bytes (LTC{LTC_N_LTC}) 2 bytes (PEC)
3890  *
3891  * The function does not check the PECs. This has to be done
3892  * elsewhere.
3893  *
3894  * @param Command command sent to the daisy-chain
3895  * @param pSpiInterface pointer to SPI configuration
3896  * @param pTxBuff transmit buffer
3897  * @param pRxBuff receive buffer
3898  * @param frameLength number of words to transmit
3899  *
3900  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
3901  */
3903  uint16_t *Command,
3904  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3905  uint16_t *pTxBuff,
3906  uint16_t *pRxBuff,
3907  uint32_t frameLength) {
3908  FAS_ASSERT(Command != NULL_PTR);
3909  FAS_ASSERT(pSpiInterface != NULL_PTR);
3910  FAS_ASSERT(pTxBuff != NULL_PTR);
3911  FAS_ASSERT(pRxBuff != NULL_PTR);
3912  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3913 
3914  /* DataBufferSPI_RX_with_PEC contains the data to receive.
3915  The transmission function checks the PECs.
3916  It constructs DataBufferSPI_RX, which contains the received data without PEC (easier to use). */
3917 
3918  for (uint16_t i = 0; i < LTC_N_BYTES_FOR_DATA_TRANSMISSION; i++) {
3919  pTxBuff[i] = 0x00;
3920  }
3921 
3922  pTxBuff[0] = Command[0];
3923  pTxBuff[1] = Command[1];
3924  pTxBuff[2] = Command[2];
3925  pTxBuff[3] = Command[3];
3926 
3927  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3928 
3929  return retVal;
3930 }
3931 
3932 /**
3933  * @brief sends command and data to the LTC daisy-chain.
3934  * @details This is the core function to transmit data to the LTC6813-1
3935  * daisy-chain.
3936  * The data sent is:
3937  * - COMMAND +
3938  * - 6 bytes (LTC1) +
3939  * - 6 bytes (LTC2) +
3940  * - 6 bytes (LTC3) +
3941  * - ... +
3942  * - 6 bytes (LTC{LTC_N_LTC})
3943  *
3944  * A 2 byte command is sent with the corresponding PEC.
3945  * *Example*: write configuration register (WRCFG).
3946  * The command has to be set and then the function calculates the PEC
3947  * automatically.
3948  * The function calculates the needed PEC to send the data to the
3949  * daisy-chain.
3950  * The sent data has the format:
3951  * - 2 byte-COMMAND (2 bytes PEC) +
3952  * - 6 bytes (LTC1) (2 bytes PEC) +
3953  * - 6 bytes (LTC2) (2 bytes PEC) +
3954  * - 6 bytes (LTC3) (2 bytes PEC) +
3955  * - ... +
3956  * - 6 bytes (LTC{LTC_N_LTC}) (2 bytes PEC)
3957  *
3958  * The function returns 0. The only way to check if the transmission
3959  * was successful is to read the results of the write operation.
3960  * (example: read configuration register after writing to it)
3961  *
3962  * @param Command command sent to the daisy-chain
3963  * @param pSpiInterface pointer to SPI configuration
3964  * @param pTxBuff transmit buffer
3965  * @param pRxBuff receive buffer
3966  * @param frameLength number of words to transmit
3967  *
3968  * @return STD_OK if SPI transmission is OK, STD_NOT_OK otherwise
3969  */
3971  uint16_t *Command,
3972  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3973  uint16_t *pTxBuff,
3974  uint16_t *pRxBuff,
3975  uint32_t frameLength) {
3976  FAS_ASSERT(Command != NULL_PTR);
3977  FAS_ASSERT(pSpiInterface != NULL_PTR);
3978  FAS_ASSERT(pTxBuff != NULL_PTR);
3979  FAS_ASSERT(pRxBuff != NULL_PTR);
3980  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3981 
3982  uint16_t PEC_result = 0;
3983  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3984 
3985  pTxBuff[0] = Command[0];
3986  pTxBuff[1] = Command[1];
3987  pTxBuff[2] = Command[2];
3988  pTxBuff[3] = Command[3];
3989 
3990  /* Calculate PEC of all data (1 PEC value for 6 bytes) */
3991  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3992  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3993  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3994  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3995  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3996  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3997  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3998 
3999  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
4000  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
4001  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
4002  }
4003 
4004  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
4005 
4006  return retVal;
4007 }
4008 
4009 /**
4010  * @brief configures the data that will be sent to the LTC daisy-chain to configure multiplexer channels.
4011  *
4012  * This function does not sent the data to the multiplexer daisy-chain. This is done
4013  * by the function LTC_SetMuxChannel(), which calls LTC_SetMuxChCommand()..
4014  *
4015  * @param pTxBuff transmit buffer
4016  * @param mux multiplexer ID to be configured (0,1,2 or 3)
4017  * @param channel multiplexer channel to be configured (0 to 7)
4018  *
4019  */
4020 static void LTC_SetMuxChCommand(uint16_t *pTxBuff, uint8_t mux, uint8_t channel) {
4021  FAS_ASSERT(pTxBuff != NULL_PTR);
4022  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4023 #if SLAVE_BOARD_VERSION == 2u
4024 
4025  /* using ADG728 */
4026  uint8_t address = 0x98u | ((mux % 4u) << 1u);
4027  uint8_t data = 1u << (channel % 8u);
4028  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
4029  data = 0x00;
4030  }
4031 
4032 #else
4033 
4034  /* using LTC1380 */
4035  uint8_t address = 0x90u | ((mux % 4u) << 1u);
4036  uint8_t data = 0x08u | (channel % 8u);
4037  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
4038  data = 0x00;
4039  }
4040 
4041 #endif
4042 
4043  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | ((address >> 4u) & 0x0Fu); /* 0x6 : LTC6804: ICOM START from Master */
4044  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | ((address << 4u) & 0xF0u);
4045  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | ((data >> 4u) & 0x0Fu);
4046  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | ((data << 4u) & 0xF0u);
4047  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 0x1 : ICOM-STOP */
4048  pTxBuff[9u + (i * 8u)] = 0x00; /* 0x0 : dummy (Dn) */
4049  /* 9: MASTER NACK + STOP (FCOM) */
4050  }
4051 }
4052 
4053 /**
4054  * @brief sends data to the LTC daisy-chain to read EEPROM on slaves.
4055  *
4056  * @param ltc_state state of the ltc state machine
4057  * @param pSpiInterface pointer to SPI configuration
4058  * @param pTxBuff transmit buffer
4059  * @param pRxBuff receive buffer
4060  * @param frameLength number of words to transmit
4061  * @param step first or second stage of read process (0 or 1)
4062  *
4063  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4064  */
4066  LTC_STATE_s *ltc_state,
4067  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4068  uint16_t *pTxBuff,
4069  uint16_t *pRxBuff,
4070  uint32_t frameLength,
4071  uint8_t step) {
4072  FAS_ASSERT(ltc_state != NULL_PTR);
4073  FAS_ASSERT(pSpiInterface != NULL_PTR);
4074  FAS_ASSERT(pTxBuff != NULL_PTR);
4075  FAS_ASSERT(pRxBuff != NULL_PTR);
4076  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4077 
4078  /* send WRCOMM to send I2C message to choose channel */
4079  LTC_SetEepromReadCommand(ltc_state, pTxBuff, step);
4080  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4081 
4082  return statusSPI;
4083 }
4084 
4085 /**
4086  * @brief configures the data that will be sent to the LTC daisy-chain to read EEPROM on slaves.
4087  *
4088  * @param ltc_state state of the ltc state machine
4089  * @param pTxBuff transmit buffer
4090  * @param step first or second stage of read process (0 or 1)
4091  *
4092  */
4093 static void LTC_SetEepromReadCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4094  FAS_ASSERT(ltc_state != NULL_PTR);
4095  FAS_ASSERT(pTxBuff != NULL_PTR);
4096  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4097 
4098  uint32_t address = ltc_state->ltcData.slaveControl->eepromReadAddressToUse;
4099 
4100  address &= 0x3FFFFu;
4101  const uint8_t address0 = address >> 16u;
4102  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4103  const uint8_t address2 = address & 0xFFu;
4104 
4105  if (step == 0u) {
4106  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4107  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4108  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4109  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4110  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4111  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4112  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4113  }
4114  } else { /* step == 1 */
4115  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4116  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4117  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x10u);
4118  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | 0x0Fu;
4119  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0xF0u;
4120  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4121  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4122  }
4123  }
4124 }
4125 
4126 /**
4127  * @brief saves the read values of the external EEPROMs read from the LTC daisy-chain.
4128  *
4129  * @param ltc_state state of the ltc state machine
4130  * @param pRxBuff receive buffer
4131  *
4132  */
4133 static void LTC_EepromSaveReadValue(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4134  FAS_ASSERT(ltc_state != NULL_PTR);
4135  FAS_ASSERT(pRxBuff != NULL_PTR);
4136  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4137 
4138  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4139  ltc_state->ltcData.slaveControl->eepromValueRead[i] = (pRxBuff[6u + (i * 8u)] << 4u) |
4140  ((pRxBuff[7u + (i * 8u)] >> 4u));
4141  }
4142 
4145  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
4146 
4147  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4148 }
4149 
4150 /**
4151  * @brief sends data to the LTC daisy-chain to write EEPROM on slaves.
4152  *
4153  * @param ltc_state state of the ltc state machine
4154  * @param pSpiInterface pointer to SPI configuration
4155  * @param pTxBuff transmit buffer
4156  * @param pRxBuff receive buffer
4157  * @param frameLength number of words to transmit
4158  * @param step first or second stage of read process (0 or 1)
4159  *
4160  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4161  */
4163  LTC_STATE_s *ltc_state,
4164  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4165  uint16_t *pTxBuff,
4166  uint16_t *pRxBuff,
4167  uint32_t frameLength,
4168  uint8_t step) {
4169  FAS_ASSERT(ltc_state != NULL_PTR);
4170  FAS_ASSERT(pSpiInterface != NULL_PTR);
4171  FAS_ASSERT(pTxBuff != NULL_PTR);
4172  FAS_ASSERT(pRxBuff != NULL_PTR);
4173  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4174 
4175  /* send WRCOMM to send I2C message to write EEPROM */
4176  LTC_SetEepromWriteCommand(ltc_state, pTxBuff, step);
4177  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4178 
4179  return statusSPI;
4180 }
4181 
4182 /**
4183  * @brief configures the data that will be sent to the LTC daisy-chain to write EEPROM on slaves.
4184  *
4185  * @param ltc_state state of the ltc state machine
4186  * @param pTxBuff transmit buffer
4187  * @param step first or second stage of read process (0 or 1)
4188  *
4189  */
4190 static void LTC_SetEepromWriteCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4191  FAS_ASSERT(ltc_state != NULL_PTR);
4192  FAS_ASSERT(pTxBuff != NULL_PTR);
4193  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4194 
4195  uint32_t address = ltc_state->ltcData.slaveControl->eepromWriteAddressToUse;
4196 
4197  address &= 0x3FFFFu;
4198  const uint8_t address0 = address >> 16u;
4199  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4200  const uint8_t address2 = address & 0xFFu;
4201 
4202  if (step == 0u) {
4203  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4204  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4205  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4206  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4207  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4208  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4209  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4210  }
4211  } else { /* step == 1 */
4212  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4213  const uint8_t data = ltc_state->ltcData.slaveControl->eepromValueWrite[i];
4214 
4215  pTxBuff[4u + (i * 8u)] = LTC_ICOM_BLANK | (data >> 4u); /* 0x6 : LTC6804: ICOM START from Master */
4216  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | (data << 4u);
4217  pTxBuff[6u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4218  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4219  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4220  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4221  }
4222 
4225  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
4226 
4227  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4228  }
4229 }
4230 
4231 /**
4232  * @brief sends data to the LTC daisy-chain to configure multiplexer channels.
4233  *
4234  * This function calls the function LTC_SetMuxChCommand() to set the data.
4235  *
4236  * @param pSpiInterface pointer to SPI configuration
4237  * @param pTxBuff transmit buffer
4238  * @param pRxBuff receive buffer
4239  * @param frameLength number of words to transmit
4240  * @param mux multiplexer ID to be configured (0,1,2 or 3)
4241  * @param channel multiplexer channel to be configured (0 to 7)
4242  *
4243  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4244  */
4246  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4247  uint16_t *pTxBuff,
4248  uint16_t *pRxBuff,
4249  uint32_t frameLength,
4250  uint8_t mux,
4251  uint8_t channel) {
4252  FAS_ASSERT(pSpiInterface != NULL_PTR);
4253  FAS_ASSERT(pTxBuff != NULL_PTR);
4254  FAS_ASSERT(pRxBuff != NULL_PTR);
4255 
4256  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4257 
4258  /* send WRCOMM to send I2C message to choose channel */
4259  LTC_SetMuxChCommand(pTxBuff, mux, channel);
4260  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4261 
4262  return statusSPI;
4263 }
4264 
4265 /**
4266  * @brief sends data to the LTC daisy-chain to communicate via I2C
4267  *
4268  * This function initiates an I2C signal sent by the LTC6804 on the slave boards
4269  *
4270  * @param pSpiInterface pointer to SPI configuration
4271  * @param pTxBuff transmit buffer
4272  * @param pRxBuff receive buffer
4273  * @param frameLength number of words to transmit
4274  * @param cmd_data command data to be sent
4275  *
4276  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4277  */
4279  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4280  uint16_t *pTxBuff,
4281  uint16_t *pRxBuff,
4282  uint32_t frameLength,
4283  uint16_t *cmd_data) {
4284  FAS_ASSERT(pSpiInterface != NULL_PTR);
4285  FAS_ASSERT(pTxBuff != NULL_PTR);
4286  FAS_ASSERT(pRxBuff != NULL_PTR);
4287  FAS_ASSERT(cmd_data != NULL_PTR);
4288  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4289 
4290  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4291  pTxBuff[4u + (i * 6u)] = cmd_data[0];
4292  pTxBuff[5u + (i * 6u)] = cmd_data[1];
4293 
4294  pTxBuff[6u + (i * 6u)] = cmd_data[2];
4295  pTxBuff[7u + (i * 6u)] = cmd_data[3];
4296 
4297  pTxBuff[8u + (i * 6u)] = cmd_data[4];
4298  pTxBuff[9u + (i * 6u)] = cmd_data[5];
4299  }
4300 
4301  /* send WRCOMM to send I2C message */
4302  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4303 
4304  return statusSPI;
4305 }
4306 
4307 /**
4308  * @brief saves the temperature value of the external temperature sensors read from the LTC daisy-chain.
4309  *
4310  * This function saves the temperature value received from the external temperature sensors
4311  *
4312  * @param ltc_state state of the ltc state machine
4313  * @param pRxBuff receive buffer
4314  *
4315  */
4316 static void LTC_TempSensSaveTemp(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4317  FAS_ASSERT(ltc_state != NULL_PTR);
4318  FAS_ASSERT(pRxBuff != NULL_PTR);
4319  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4320 
4321  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4322  uint8_t temp_tmp[2];
4323  temp_tmp[0] = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4324  temp_tmp[1] = (pRxBuff[8u + (i * 8u)] << 4u) | ((pRxBuff[9u + (i * 8u)] >> 4u));
4325  uint16_t val_i = (temp_tmp[0] << 8u) | (temp_tmp[1]);
4326  val_i = val_i >> 8u;
4327  ltc_state->ltcData.slaveControl->externalTemperatureSensor[i] = val_i;
4328  }
4329 
4330  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4331 }
4332 
4333 /**
4334  * @brief sends data to the LTC daisy-chain to control the user port expander
4335  *
4336  * This function sends a control byte to the register of the user port expander
4337  *
4338  * @param ltc_state state of the ltc state machine
4339  * @param pSpiInterface pointer to SPI configuration
4340  * @param pTxBuff transmit buffer
4341  * @param pRxBuff receive buffer
4342  * @param frameLength number of words to transmit
4343  *
4344  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4345  */
4347  LTC_STATE_s *ltc_state,
4348  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4349  uint16_t *pTxBuff,
4350  uint16_t *pRxBuff,
4351  uint32_t frameLength) {
4352  FAS_ASSERT(ltc_state != NULL_PTR);
4353  FAS_ASSERT(pSpiInterface != NULL_PTR);
4354  FAS_ASSERT(pTxBuff != NULL_PTR);
4355  FAS_ASSERT(pRxBuff != NULL_PTR);
4356 
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_PER_STRING; i++) {
4362  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES_PER_STRING - 1 - i];
4363 
4364  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START |
4365  0x04u; /* 6: ICOM0 start condition, 4: upper nibble of PCA8574 address */
4366  pTxBuff[5u + (i * 8u)] =
4367  0u | LTC_FCOM_MASTER_NACK; /* 0: lower nibble of PCA8574 address + R/W bit, 8: FCOM0 master NACK */
4368 
4369  pTxBuff[6u + (i * 8u)] =
4370  LTC_ICOM_BLANK |
4371  (output_data >> 4u); /* 0: ICOM1 blank, x: upper nibble of PCA8574 data register (0 == pin low) */
4372  pTxBuff[7u + (i * 8u)] =
4373  (uint8_t)(output_data << 4u) |
4374  LTC_FCOM_MASTER_NACK_STOP; /* x: lower nibble of PCA8574 data register, 9: FCOM1 master NACK + STOP */
4375 
4376  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 7: no transmission, F: dummy data */
4377  pTxBuff[9u + (i * 8u)] = 0; /* F: dummy data, 9: FCOM2 master NACK + STOP */
4378  }
4379 
4380  /* send WRCOMM to send I2C message */
4381  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4382 
4383  return statusSPI;
4384 }
4385 
4386 /**
4387  * @brief saves the received values of the external port expander read from the LTC daisy-chain.
4388  *
4389  * This function saves the received data byte from the external port expander
4390  *
4391  * @param ltc_state state of the ltc state machine
4392  * @param pRxBuff receive buffer
4393  *
4394  */
4395 static void LTC_PortExpanderSaveValues(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4396  FAS_ASSERT(ltc_state != NULL_PTR);
4397  FAS_ASSERT(pRxBuff != NULL_PTR);
4398  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4399 
4400  /* extract data */
4401  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4402  const uint8_t val_i = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4403  ltc_state->ltcData.slaveControl->ioValueIn[i] = val_i;
4404  }
4405 
4406  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4407 }
4408 
4409 /**
4410  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4411  *
4412  * This function sends a control byte to the register of the user port expander from TI
4413  *
4414  * @param ltc_state state of the ltc state machine
4415  * @param pSpiInterface pointer to SPI configuration
4416  * @param pTxBuff transmit buffer
4417  * @param pRxBuff receive buffer
4418  * @param frameLength number of words to transmit
4419  * @param direction use port expander pin as input or output
4420  *
4421  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4422  */
4424  LTC_STATE_s *ltc_state,
4425  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4426  uint16_t *pTxBuff,
4427  uint16_t *pRxBuff,
4428  uint32_t frameLength,
4430  FAS_ASSERT(ltc_state != NULL_PTR);
4431  FAS_ASSERT(pSpiInterface != NULL_PTR);
4432  FAS_ASSERT(pTxBuff != NULL_PTR);
4433  FAS_ASSERT(pRxBuff != NULL_PTR);
4434  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4435 
4436  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4437 
4438  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4439  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /*upper nibble of TCA6408A address */
4440  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4441  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4442 
4443  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR >>
4444  4u); /* upper nibble of TCA6408A configuration register address */
4445  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR << 4u) |
4446  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A configuration register address */
4447 
4448  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK |
4449  (direction >> 4u); /* upper nibble of TCA6408A configuration register data */
4450  pTxBuff[9u + (i * 8u)] = (uint8_t)(direction << 4u) |
4451  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A configuration register data */
4452  }
4453 
4454  /* send WRCOMM to send I2C message */
4455  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4456 
4457  return statusSPI;
4458 }
4459 
4460 /**
4461  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4462  *
4463  * This function sends a control byte to the register of the user port expander from TI
4464  *
4465  * @param ltc_state state of the ltc state machine
4466  * @param pSpiInterface pointer to SPI configuration
4467  * @param pTxBuff transmit buffer
4468  * @param pRxBuff receive buffer
4469  * @param frameLength number of words to transmit
4470  *
4471  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4472  */
4474  LTC_STATE_s *ltc_state,
4475  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4476  uint16_t *pTxBuff,
4477  uint16_t *pRxBuff,
4478  uint32_t frameLength) {
4479  FAS_ASSERT(ltc_state != NULL_PTR);
4480  FAS_ASSERT(pSpiInterface != NULL_PTR);
4481  FAS_ASSERT(pTxBuff != NULL_PTR);
4482  FAS_ASSERT(pRxBuff != NULL_PTR);
4483  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4484 
4485  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4486 
4487  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4488  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES_PER_STRING - 1 - i];
4489 
4490  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4491  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4492  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4493 
4494  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR >>
4495  4u); /* upper nibble of TCA6408A output register address */
4496  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR << 4u) |
4497  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A output register address */
4498 
4499  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (output_data >> 4u); /* upper nibble of TCA6408A output register */
4500  pTxBuff[9u + (i * 8u)] = (uint8_t)(output_data << 4u) |
4501  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A output register */
4502  }
4503 
4504  /* send WRCOMM to send I2C message */
4505  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4506 
4507  return statusSPI;
4508 }
4509 
4510 /**
4511  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4512  *
4513  * @details This function sends a control byte to the register of the user port expander from TI
4514  *
4515  * @param ltc_state state of the ltc state machine
4516  * @param pSpiInterface pointer to SPI configuration
4517  * @param pTxBuff transmit buffer
4518  * @param pRxBuff receive buffer
4519  * @param frameLength number of words to transmit
4520  * @param step first or second stage of read process (0 or 1)
4521  *
4522  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4523  */
4525  LTC_STATE_s *ltc_state,
4526  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4527  uint16_t *pTxBuff,
4528  uint16_t *pRxBuff,
4529  uint32_t frameLength,
4530  uint8_t step) {
4531  FAS_ASSERT(ltc_state != NULL_PTR);
4532  FAS_ASSERT(pSpiInterface != NULL_PTR);
4533  FAS_ASSERT(pTxBuff != NULL_PTR);
4534  FAS_ASSERT(pRxBuff != NULL_PTR);
4535  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4536 
4537  if (step == 0u) {
4538  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4539  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */