foxBMS - Unit Tests  1.5.1
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-02-23 (date of last update)
47  * @version v1.5.1
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 (uint16_t i = 0; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
454  ltc_state->ltcData.cellVoltage->cellVoltage_mV[s][i] = 0;
455  ltc_state->ltcData.openWireDetection->openWirePup[s][i] = 0;
456  ltc_state->ltcData.openWireDetection->openWirePdown[s][i] = 0;
457  ltc_state->ltcData.openWireDetection->openWireDelta[s][i] = 0;
458  }
459 
460  ltc_state->ltcData.cellTemperature->state = 0;
461  for (uint16_t i = 0; i < BS_NR_OF_TEMP_SENSORS_PER_STRING; i++) {
462  ltc_state->ltcData.cellTemperature->cellTemperature_ddegC[s][i] = 0;
463  }
464 
465  ltc_state->ltcData.balancingFeedback->state = 0;
466  for (uint16_t i = 0; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
467  ltc_state->ltcData.balancingControl->balancingState[s][i] = 0;
468  }
469  ltc_state->ltcData.balancingControl->nrBalancedCells[s] = 0u;
470  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
471  ltc_state->ltcData.balancingFeedback->value[s][i] = 0;
472  }
473 
474  ltc_state->ltcData.slaveControl->state = 0;
475  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
476  ltc_state->ltcData.slaveControl->ioValueIn[i] = 0;
477  ltc_state->ltcData.slaveControl->ioValueOut[i] = 0;
479  ltc_state->ltcData.slaveControl->eepromValueRead[i] = 0;
480  ltc_state->ltcData.slaveControl->eepromValueWrite[i] = 0;
481  }
482  ltc_state->ltcData.slaveControl->eepromReadAddressLastUsed = 0xFFFFFFFF;
483  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
484  ltc_state->ltcData.slaveControl->eepromWriteAddressLastUsed = 0xFFFFFFFF;
485  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
486 
487  ltc_state->ltcData.allGpioVoltages->state = 0;
488  for (uint16_t i = 0; i < (BS_NR_OF_MODULES_PER_STRING * BS_NR_OF_GPIOS_PER_MODULE); i++) {
489  ltc_state->ltcData.allGpioVoltages->gpioVoltages_mV[s][i] = 0;
490  }
491 
492  for (uint16_t i = 0; i < (BS_NR_OF_MODULES_PER_STRING * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1)); i++) {
493  ltc_state->ltcData.openWire->openWire[s][i] = 0;
494  }
495  ltc_state->ltcData.openWire->state = 0;
496  }
497 
499  ltc_state->ltcData.cellVoltage,
500  ltc_state->ltcData.cellTemperature,
501  ltc_state->ltcData.balancingFeedback,
502  ltc_state->ltcData.openWire);
504 }
505 
506 /**
507  * @brief Saves the last state and the last substate
508  *
509  * @param ltc_state: state of the ltc state machine
510  */
511 static void LTC_SaveLastStates(LTC_STATE_s *ltc_state) {
512  ltc_state->laststate = ltc_state->state;
513  ltc_state->lastsubstate = ltc_state->substate;
514 }
515 
516 /**
517  * @brief function for setting LTC_Trigger state transitions
518  *
519  * @param ltc_state: state of the ltc state machine
520  * @param state: state to transition into
521  * @param substate: substate to transition into
522  * @param timer_ms: transition into state, substate after timer elapsed
523  */
524 static void LTC_StateTransition(LTC_STATE_s *ltc_state, LTC_STATEMACH_e state, uint8_t substate, uint16_t timer_ms) {
525  ltc_state->state = state;
526  ltc_state->substate = substate;
527  ltc_state->timer = timer_ms;
528 }
529 
530 /**
531  * @brief condition-based state transition depending on retVal
532  *
533  * If retVal is #STD_OK, after timer_ms_ok is elapsed the LTC statemachine will
534  * transition into state_ok and substate_ok, otherwise after timer_ms_nok the
535  * statemachine will transition to state_nok and substate_nok. Depending on
536  * value of retVal the corresponding diagnosis entry will be called.
537  *
538  * @param ltc_state state of the ltc state machine
539  * @param retVal condition to determine if state machine will transition
540  * into ok or nok states
541  * @param diagCode symbolic IDs for diagnosis entry, called with
542  * #DIAG_EVENT_OK if retVal is #STD_OK, #DIAG_EVENT_NOT_OK
543  * otherwise
544  * @param state_ok state to transition into if retVal is #STD_OK
545  * @param substate_ok substate to transition into if retVal is #STD_OK
546  * @param timer_ms_ok transition into state_ok, substate_ok after timer_ms_ok
547  * elapsed
548  * @param state_nok state to transition into if retVal is #STD_NOT_OK
549  * @param substate_nok substate to transition into if retVal is #STD_NOT_OK
550  * @param timer_ms_nok transition into state_nok, substate_nok after
551  * timer_ms_nok elapsed
552  */
554  LTC_STATE_s *ltc_state,
555  STD_RETURN_TYPE_e retVal,
556  DIAG_ID_e diagCode,
557  LTC_STATEMACH_e state_ok,
558  uint8_t substate_ok,
559  uint16_t timer_ms_ok,
560  LTC_STATEMACH_e state_nok,
561  uint8_t substate_nok,
562  uint16_t timer_ms_nok) {
563  if ((retVal != STD_OK)) {
565  LTC_StateTransition(ltc_state, state_nok, substate_nok, timer_ms_nok);
566  } else {
567  DIAG_Handler(diagCode, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
568  LTC_StateTransition(ltc_state, state_ok, substate_ok, timer_ms_ok);
569  }
570 }
571 
572 extern void LTC_SaveVoltages(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
573  /* Pointer validity check */
574  FAS_ASSERT(ltc_state != NULL_PTR);
575  FAS_ASSERT(stringNumber < BS_NR_OF_STRINGS);
576 
577  /* Iterate over all cell to:
578  *
579  * 1. Check open-wires and set respective cell measurements to invalid
580  * 2. Perform minimum/maximum measurement value plausibility check
581  * 3. Calculate string values
582  */
583  STD_RETURN_TYPE_e cellVoltageMeasurementValid = STD_OK;
584  int32_t stringVoltage_mV = 0;
585  uint16_t numberValidMeasurements = 0;
586  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
587  for (uint8_t c = 0u; c < BS_NR_OF_CELL_BLOCKS_PER_MODULE; c++) {
588  /* ------- 1. Check open-wires -----------------
589  * Is cell N input not open wire &&
590  * Is cell N+1 input not open wire &&
591  * Is cell voltage valid because of previous PEC error
592  * If so, everything okay, else set cell voltage measurement to invalid.
593  */
594  if ((ltc_state->ltcData.openWire
595  ->openWire[stringNumber][(m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1u)) + c] == 0u) &&
596  (ltc_state->ltcData.openWire
597  ->openWire[stringNumber][(m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1u)) + c + 1u] == 0u) &&
598  ((ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] & (0x01u << c)) == 0u)) {
599  /* Cell voltage is valid -> perform minimum/maximum plausibility check */
600 
601  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
603  ltc_state->ltcData.cellVoltage
604  ->cellVoltage_mV[stringNumber][(m * BS_NR_OF_CELL_BLOCKS_PER_MODULE) + c],
606  /* Cell voltage is valid -> calculate string voltage */
607  /* -------- 3. Calculate string values ------------- */
608  stringVoltage_mV += ltc_state->ltcData.cellVoltage
609  ->cellVoltage_mV[stringNumber][(m * BS_NR_OF_CELL_BLOCKS_PER_MODULE) + c];
610  numberValidMeasurements++;
611  } else {
612  /* Invalidate cell voltage measurement */
613  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << c);
614  cellVoltageMeasurementValid = STD_NOT_OK;
615  }
616  } else {
617  /* Set cell voltage measurement value invalid, if not already invalid because of PEC Error */
618  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << c);
619  cellVoltageMeasurementValid = STD_NOT_OK;
620  }
621  }
622  }
623  DIAG_CheckEvent(cellVoltageMeasurementValid, ltc_state->voltMeasDiagErrorEntry, DIAG_STRING, stringNumber);
624  ltc_state->ltcData.cellVoltage->stringVoltage_mV[stringNumber] = stringVoltage_mV;
625  ltc_state->ltcData.cellVoltage->nrValidCellVoltages[stringNumber] = numberValidMeasurements;
626 
627  /* Increment state variable each time new values are written into database */
628  ltc_state->ltcData.cellVoltage->state++;
629 
630  DATA_WRITE_DATA(ltc_state->ltcData.cellVoltage);
631 }
632 
633 /*========== Extern Function Implementations ================================*/
634 extern void LTC_SaveTemperatures(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
635  FAS_ASSERT(ltc_state != NULL_PTR);
636  STD_RETURN_TYPE_e cellTemperatureMeasurementValid = STD_OK;
637  uint16_t numberValidMeasurements = 0;
638 
639  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
640  for (uint8_t c = 0u; c < BS_NR_OF_TEMP_SENSORS_PER_MODULE; c++) {
641  /* ------- 1. Check valid flag -----------------
642  * Is cell temperature valid because of previous PEC error
643  * If so, everything okay, else set cell temperature measurement to invalid.
644  */
645  if ((ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] & (0x01u << c)) == 0u) {
646  /* Cell temperature is valid -> perform minimum/maximum plausibility check */
647 
648  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
649  if (STD_OK ==
651  ltc_state->ltcData.cellTemperature
652  ->cellTemperature_ddegC[stringNumber][(m * BS_NR_OF_TEMP_SENSORS_PER_MODULE) + c])) {
653  numberValidMeasurements++;
654  } else {
655  /* Invalidate cell temperature measurement */
656  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] |= (0x01u << c);
657  cellTemperatureMeasurementValid = STD_NOT_OK;
658  }
659  } else {
660  /* Already invalid because of PEC Error */
661  cellTemperatureMeasurementValid = STD_NOT_OK;
662  }
663  }
664  }
665  DIAG_CheckEvent(cellTemperatureMeasurementValid, ltc_state->tempMeasDiagErrorEntry, DIAG_STRING, stringNumber);
666 
667  ltc_state->ltcData.cellTemperature->nrValidTemperatures[stringNumber] = numberValidMeasurements;
668 
669  ltc_state->ltcData.cellTemperature->state++;
671 }
672 
673 extern void LTC_SaveAllGpioMeasurement(LTC_STATE_s *ltc_state) {
674  FAS_ASSERT(ltc_state != NULL_PTR);
675  ltc_state->ltcData.allGpioVoltages->state++;
677 }
678 
679 /**
680  * @brief stores the measured balancing feedback values in the database.
681  *
682  * This function stores the global balancing feedback value measured on GPIO3 of the LTC into the database
683  *
684  * @param ltc_state state of the ltc state machine
685  * @param DataBufferSPI_RX receive buffer of the SPI interface
686  * @param stringNumber string addressed
687  *
688  */
689 static void LTC_SaveBalancingFeedback(LTC_STATE_s *ltc_state, uint16_t *DataBufferSPI_RX, uint8_t stringNumber) {
690  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
691  const uint16_t val_i = DataBufferSPI_RX[8u + (1u * i * 8u)] |
692  (DataBufferSPI_RX[8u + (1u * i * 8u) + 1u] << 8u); /* raw value, GPIO3 */
693 
694  ltc_state->ltcData.balancingFeedback->value[stringNumber][i] = val_i;
695  }
696 
697  ltc_state->ltcData.balancingFeedback->state++;
699 }
700 
701 /**
702  * @brief gets the balancing orders from the database.
703  *
704  * This function gets the balancing control from the database. Balancing control
705  * is set by the BMS. The LTC driver only executes the balancing orders.
706  *
707  * @param ltc_state: state of the ltc state machine
708  *
709  */
712 }
713 
714 /**
715  * @brief re-entrance check of LTC state machine trigger function
716  *
717  * This function is not re-entrant and should only be called time- or event-triggered.
718  * It increments the triggerentry counter from the state variable ltc_state.
719  * It should never be called by two different processes, so if it is the case, triggerentry
720  * should never be higher than 0 when this function is called.
721  *
722  * @param ltc_state: state of the ltc state machine
723  *
724  * @return retval 0 if no further instance of the function is active, 0xff else
725  *
726  */
727 uint8_t LTC_CheckReEntrance(LTC_STATE_s *ltc_state) {
728  FAS_ASSERT(ltc_state != NULL_PTR);
729  uint8_t retval = 0;
730 
732  if (!ltc_state->triggerentry) {
733  ltc_state->triggerentry++;
734  } else {
735  retval = 0xFF; /* multiple calls of function */
736  }
738 
739  return (retval);
740 }
741 
743  FAS_ASSERT(ltc_state != NULL_PTR);
744  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
745 
747  retval.request = ltc_state->statereq.request;
748  retval.string = ltc_state->statereq.string;
750 
751  return (retval);
752 }
753 
755  FAS_ASSERT(ltc_state != NULL_PTR);
756  return ltc_state->state;
757 }
758 
759 /**
760  * @brief transfers the current state request to the state machine.
761  *
762  * This function takes the current state request from ltc_state and transfers it to the state machine.
763  * It resets the value from ltc_state to LTC_STATE_NO_REQUEST
764  *
765  * @param ltc_state: state of the ltc state machine
766  * @param pBusIDptr bus ID, main or backup (deprecated)
767  * @param pAdcModeptr LTC ADCmeasurement mode (fast, normal or filtered)
768  * @param pAdcMeasChptr number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
769  *
770  * @return retVal current state request, taken from LTC_STATE_REQUEST_e
771  *
772  */
774  LTC_STATE_s *ltc_state,
775  uint8_t *pBusIDptr,
776  LTC_ADCMODE_e *pAdcModeptr,
777  LTC_ADCMEAS_CHAN_e *pAdcMeasChptr) {
778  FAS_ASSERT(ltc_state != NULL_PTR);
779  FAS_ASSERT(pBusIDptr != NULL_PTR);
780  FAS_ASSERT(pAdcModeptr != NULL_PTR);
781  FAS_ASSERT(pAdcMeasChptr != NULL_PTR);
782  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
783 
785  retval.request = ltc_state->statereq.request;
786  retval.string = ltc_state->statereq.string;
787  ltc_state->requestedString = ltc_state->statereq.string;
788  *pAdcModeptr = ltc_state->adcModereq;
789  *pAdcMeasChptr = ltc_state->adcMeasChreq;
791  ltc_state->statereq.string = 0x0u;
793 
794  return (retval);
795 }
796 
798  FAS_ASSERT(ltc_state != NULL_PTR);
799  LTC_RETURN_TYPE_e retVal = LTC_ERROR;
800 
802  retVal = LTC_CheckStateRequest(ltc_state, statereq);
803 
804  if ((retVal == LTC_OK) || (retVal == LTC_BUSY_OK) || (retVal == LTC_OK_FROM_ERROR)) {
805  ltc_state->statereq = statereq;
806  }
808 
809  return (retVal);
810 }
811 
812 void LTC_Trigger(LTC_STATE_s *ltc_state) {
813  FAS_ASSERT(ltc_state != NULL_PTR);
814  STD_RETURN_TYPE_e retVal = STD_OK;
815  LTC_REQUEST_s statereq = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
816  uint8_t tmpbusID = 0;
819  STD_RETURN_TYPE_e continueFunction = STD_OK;
820 
821  FAS_ASSERT(ltc_state != NULL_PTR);
822 
823  /* Check re-entrance of function */
824  if (LTC_CheckReEntrance(ltc_state) > 0u) {
825  continueFunction = STD_NOT_OK;
826  }
827 
828  if (ltc_state->check_spi_flag == STD_NOT_OK) {
829  if (ltc_state->timer > 0u) {
830  if ((--ltc_state->timer) > 0u) {
831  ltc_state->triggerentry--;
832  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
833  }
834  }
835  } else {
836  if (AFE_IsTransmitOngoing(ltc_state) == true) {
837  if (ltc_state->timer > 0u) {
838  if ((--ltc_state->timer) > 0u) {
839  ltc_state->triggerentry--;
840  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
841  }
842  }
843  }
844  }
845 
846  if (continueFunction == STD_OK) {
847  switch (ltc_state->state) {
848  /****************************UNINITIALIZED***********************************/
850  /* waiting for Initialization Request */
851  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
852  if (statereq.request == LTC_STATE_INIT_REQUEST) {
853  LTC_SaveLastStates(ltc_state);
854  LTC_InitializeDatabase(ltc_state);
855  LTC_ResetErrorTable(ltc_state);
858  ltc_state->adcMode = tmpadcMode;
859  ltc_state->adcMeasCh = tmpadcMeasCh;
860  } else if (statereq.request == LTC_STATE_NO_REQUEST) {
861  /* no actual request pending */
862  } else {
863  ltc_state->ErrRequestCounter++; /* illegal request pending */
864  }
865  break;
866 
867  /****************************INITIALIZATION**********************************/
869 
870  LTC_SetTransferTimes(ltc_state);
871 
872  if (ltc_state->substate == LTC_INIT_STRING) {
873  LTC_SaveLastStates(ltc_state);
874  ltc_state->currentString = 0u;
875 
876  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
878  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
881  } else if (ltc_state->substate == LTC_ENTRY_INITIALIZATION) {
882  LTC_SaveLastStates(ltc_state);
883 
884  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
885  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
886  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
887  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
888 
889  retVal =
890  LTC_TRANSMIT_WAKE_UP(ltc_state->spiSeqPtr); /* Send dummy byte to wake up the daisy chain */
892  ltc_state,
893  retVal,
894  ltc_state->spiDiagErrorEntry,
901  } else if (ltc_state->substate == LTC_RE_ENTRY_INITIALIZATION) {
902  LTC_SaveLastStates(ltc_state);
903  retVal = LTC_TRANSMIT_WAKE_UP(
904  ltc_state->spiSeqPtr); /* Send dummy byte again to wake up the daisy chain */
906  ltc_state,
907  retVal,
908  ltc_state->spiDiagErrorEntry,
915  } else if (ltc_state->substate == LTC_START_INIT_INITIALIZATION) {
916  LTC_SaveLastStates(ltc_state);
917  ltc_state->check_spi_flag = STD_OK;
918  AFE_SetTransmitOngoing(ltc_state);
919  retVal = LTC_Init(
920  ltc_state->spiSeqPtr,
921  ltc_state->ltcData.txBuffer,
922  ltc_state->ltcData.rxBuffer,
923  ltc_state->ltcData.frameLength); /* Initialize main LTC loop */
924  ltc_state->lastsubstate = ltc_state->substate;
925  DIAG_CheckEvent(retVal, ltc_state->spiDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
927  ltc_state,
931  } else if (ltc_state->substate == LTC_CHECK_INITIALIZATION) {
932  /* Read values written in config register, currently unused */
933  LTC_SaveLastStates(ltc_state);
934  AFE_SetTransmitOngoing(ltc_state);
935  retVal = LTC_ReadRegister(
936  ltc_cmdRDCFG,
937  ltc_state->spiSeqPtr,
938  ltc_state->ltcData.txBuffer,
939  ltc_state->ltcData.rxBuffer,
940  ltc_state->ltcData.frameLength); /* Read config register */
942  ltc_state,
946  } else if (ltc_state->substate == LTC_EXIT_INITIALIZATION) {
947  LTC_SaveLastStates(ltc_state);
948  ++ltc_state->spiSeqPtr;
949  ++ltc_state->currentString;
950  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
953  } else {
956  }
957  }
958  break;
959 
960  /****************************INITIALIZED*************************************/
962  LTC_SaveLastStates(ltc_state);
964  break;
965 
966  /****************************START MEASUREMENT*******************************/
968 
971 
972  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
974  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
975  ltc_state->currentString = 0u;
976 
977  ltc_state->check_spi_flag = STD_NOT_OK;
978  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
979 
981  ltc_state,
982  retVal,
983  ltc_state->spiDiagErrorEntry,
986  (ltc_state->commandTransferTime +
987  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
991 
992  break;
993 
994  /****************************START MEASUREMENT CONTINUE*******************************/
995  /* Do not reset SPI interface pointer */
997 
1000 
1001  ltc_state->check_spi_flag = STD_NOT_OK;
1002  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1003 
1005  ltc_state,
1006  retVal,
1007  ltc_state->spiDiagErrorEntry,
1010  (ltc_state->commandTransferTime +
1011  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1015 
1016  break;
1017 
1018  /****************************READ VOLTAGE************************************/
1020 
1022  ltc_state->check_spi_flag = STD_OK;
1023  AFE_SetTransmitOngoing(ltc_state);
1024  retVal = LTC_ReadRegister(
1025  ltc_cmdRDCVA,
1026  ltc_state->spiSeqPtr,
1027  ltc_state->ltcData.txBuffer,
1028  ltc_state->ltcData.rxBuffer,
1029  ltc_state->ltcData.frameLength);
1031  ltc_state,
1032  retVal,
1033  ltc_state->spiDiagErrorEntry,
1040  break;
1041  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE) {
1042  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1043  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1044  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1045 
1046  AFE_SetTransmitOngoing(ltc_state);
1047  retVal = LTC_ReadRegister(
1048  ltc_cmdRDCVB,
1049  ltc_state->spiSeqPtr,
1050  ltc_state->ltcData.txBuffer,
1051  ltc_state->ltcData.rxBuffer,
1052  ltc_state->ltcData.frameLength);
1054  ltc_state,
1055  retVal,
1056  ltc_state->spiDiagErrorEntry,
1063  break;
1064  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE) {
1065  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1066  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1067  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1068 
1069  AFE_SetTransmitOngoing(ltc_state);
1070  retVal = LTC_ReadRegister(
1071  ltc_cmdRDCVC,
1072  ltc_state->spiSeqPtr,
1073  ltc_state->ltcData.txBuffer,
1074  ltc_state->ltcData.rxBuffer,
1075  ltc_state->ltcData.frameLength);
1077  ltc_state,
1078  retVal,
1079  ltc_state->spiDiagErrorEntry,
1086  break;
1087  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE) {
1088  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1089  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1090  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1091 
1092  AFE_SetTransmitOngoing(ltc_state);
1093  retVal = LTC_ReadRegister(
1094  ltc_cmdRDCVD,
1095  ltc_state->spiSeqPtr,
1096  ltc_state->ltcData.txBuffer,
1097  ltc_state->ltcData.rxBuffer,
1098  ltc_state->ltcData.frameLength);
1099  if (LTC_6813_MAX_SUPPORTED_CELLS > 12u) {
1101  ltc_state,
1102  retVal,
1103  ltc_state->spiDiagErrorEntry,
1110  } else {
1112  ltc_state,
1113  retVal,
1114  ltc_state->spiDiagErrorEntry,
1121  }
1122  break;
1123  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE) {
1124  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1125  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1126  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1127 
1128  AFE_SetTransmitOngoing(ltc_state);
1129  retVal = LTC_ReadRegister(
1130  ltc_cmdRDCVE,
1131  ltc_state->spiSeqPtr,
1132  ltc_state->ltcData.txBuffer,
1133  ltc_state->ltcData.rxBuffer,
1134  ltc_state->ltcData.frameLength);
1135  if (LTC_6813_MAX_SUPPORTED_CELLS > 15u) {
1137  ltc_state,
1138  retVal,
1139  ltc_state->spiDiagErrorEntry,
1146  } else {
1148  ltc_state,
1149  retVal,
1150  ltc_state->spiDiagErrorEntry,
1157  }
1158  break;
1159  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE) {
1160  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1161  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1162  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1163 
1164  AFE_SetTransmitOngoing(ltc_state);
1165  retVal = LTC_ReadRegister(
1166  ltc_cmdRDCVF,
1167  ltc_state->spiSeqPtr,
1168  ltc_state->ltcData.txBuffer,
1169  ltc_state->ltcData.rxBuffer,
1170  ltc_state->ltcData.frameLength);
1172  ltc_state,
1173  retVal,
1174  ltc_state->spiDiagErrorEntry,
1181  break;
1182  } else if (ltc_state->substate == LTC_EXIT_READVOLTAGE) {
1183  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1184  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1185  if (LTC_6813_MAX_SUPPORTED_CELLS == 12u) {
1186  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1187  } else if (LTC_6813_MAX_SUPPORTED_CELLS == 15u) {
1188  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1189  } else if (LTC_6813_MAX_SUPPORTED_CELLS == 18u) {
1190  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 5, ltc_state->currentString);
1191  }
1192 
1193  /* Switch to different state if read voltage state is reused
1194  * e.g. open-wire check... */
1195  if (ltc_state->reusageMeasurementMode == LTC_NOT_REUSED) {
1196  LTC_SaveVoltages(ltc_state, ltc_state->currentString);
1198  ltc_state,
1202  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PUP) {
1204  ltc_state,
1208  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PDOWN) {
1210  ltc_state,
1214  }
1215  ltc_state->check_spi_flag = STD_NOT_OK;
1216  }
1217  break;
1218 
1219  /****************************MULTIPLEXED MEASUREMENT CONFIGURATION***********/
1221 
1224 
1225  if (ltc_state->substate == LTC_STATEMACH_MUXCONFIGURATION_INIT) {
1226  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1228 
1229  if (ltc_state->muxmeas_seqptr[ltc_state->currentString] >=
1230  ltc_state->muxmeas_seqendptr[ltc_state->currentString]) {
1231  /* last step of sequence reached (or no sequence configured) */
1232 
1233  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
1234  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
1235  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
1236  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
1237 
1238  LTC_SaveTemperatures(ltc_state, ltc_state->currentString);
1239  }
1240 
1241  ltc_state->check_spi_flag = STD_OK;
1242  AFE_SetTransmitOngoing(ltc_state);
1243  retVal = LTC_SetMuxChannel(
1244  ltc_state->spiSeqPtr,
1245  ltc_state->ltcData.txBuffer,
1246  ltc_state->ltcData.rxBuffer,
1247  ltc_state->ltcData.frameLength,
1248  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID, /* mux */
1249  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh /* channel */);
1250  if (retVal != STD_OK) {
1251  DIAG_Handler(
1253  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1255  ltc_state,
1259  } else {
1260  DIAG_Handler(
1261  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1263  ltc_state,
1267  }
1268  break;
1269  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
1270  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1271  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1272  DIAG_Handler(
1274  } else {
1275  DIAG_Handler(
1276  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1277  }
1278 
1279  AFE_SetTransmitOngoing(ltc_state);
1280  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1281  if (LTC_GOTO_MUX_CHECK == true) {
1283  ltc_state,
1284  retVal,
1285  ltc_state->spiDiagErrorEntry,
1292  ;
1293  } else {
1295  ltc_state,
1296  retVal,
1297  ltc_state->spiDiagErrorEntry,
1304  }
1305  break;
1307  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1308  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1309  DIAG_Handler(
1311  } else {
1312  DIAG_Handler(
1313  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1314  }
1315 
1316  AFE_SetTransmitOngoing(ltc_state);
1317  retVal = LTC_ReadRegister(
1318  ltc_cmdRDCOMM,
1319  ltc_state->spiSeqPtr,
1320  ltc_state->ltcData.txBuffer,
1321  ltc_state->ltcData.rxBuffer,
1322  ltc_state->ltcData.frameLength);
1324  ltc_state,
1325  retVal,
1326  ltc_state->spiDiagErrorEntry,
1333  break;
1335  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1336  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1337  DIAG_Handler(
1339  } else {
1340  DIAG_Handler(
1341  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1342  }
1343 
1344  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1345  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1346 
1347  /* if CRC OK: check multiplexer answer on i2C bus */
1348  retVal = LTC_I2cCheckAck(
1349  ltc_state,
1350  ltc_state->ltcData.rxBuffer,
1351  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID,
1352  ltc_state->currentString);
1353  DIAG_CheckEvent(retVal, ltc_state->muxDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1356  break;
1357  } else if (ltc_state->substate == LTC_STATEMACH_MUXMEASUREMENT) {
1358  if (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh == 0xFF) {
1359  /* actual multiplexer is switched off, so do not make a measurement and follow up with next step (mux configuration) */
1360  ++ltc_state
1361  ->muxmeas_seqptr[ltc_state->currentString]; /* go further with next step of sequence
1362  ltc_state.numberOfMeasuredMux not decremented, this does not count as a measurement */
1364  break;
1365  } else {
1366  if (LTC_GOTO_MUX_CHECK == false) {
1367  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1368  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1369  DIAG_Handler(
1370  ltc_state->spiDiagErrorEntry,
1372  DIAG_STRING,
1373  ltc_state->currentString);
1374  } else {
1375  DIAG_Handler(
1376  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1377  }
1378  }
1379 
1380  ltc_state->check_spi_flag = STD_NOT_OK;
1381  /* user multiplexer type -> connected to GPIO2! */
1382  if ((ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 1) ||
1383  (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 2)) {
1384  retVal = LTC_StartGpioMeasurement(
1385  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2);
1386  } else {
1387  retVal = LTC_StartGpioMeasurement(
1388  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO1);
1389  }
1390  }
1392  ltc_state,
1393  retVal,
1394  ltc_state->spiDiagErrorEntry,
1397  (ltc_state->commandTransferTime +
1399  ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2)), /* wait, ADAX-Command */
1403  break;
1404  } else if (ltc_state->substate == LTC_STATEMACH_READMUXMEASUREMENT) {
1405  ltc_state->check_spi_flag = STD_OK;
1406 
1407  AFE_SetTransmitOngoing(ltc_state);
1408  retVal = LTC_ReadRegister(
1409  ltc_cmdRDAUXA,
1410  ltc_state->spiSeqPtr,
1411  ltc_state->ltcData.txBuffer,
1412  ltc_state->ltcData.rxBuffer,
1413  ltc_state->ltcData.frameLength);
1415  ltc_state,
1416  retVal,
1417  ltc_state->spiDiagErrorEntry,
1424  break;
1425  } else if (ltc_state->substate == LTC_STATEMACH_STOREMUXMEASUREMENT) {
1426  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1427  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1428  DIAG_Handler(
1430  } else {
1431  DIAG_Handler(
1432  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1433  }
1434 
1435  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1436  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1438  ltc_state,
1439  ltc_state->ltcData.rxBuffer,
1440  ltc_state->muxmeas_seqptr[ltc_state->currentString],
1441  ltc_state->currentString);
1442 
1443  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1444 
1447  break;
1448  }
1449 
1450  break;
1451 
1452  /****************************END OF MEASUREMENT CYCLE************************/
1454 
1455  if (ltc_state->balance_control_done == STD_OK) {
1456  if (LTC_IsFirstMeasurementCycleFinished(ltc_state) == false) {
1458  }
1459  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
1460  if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST) {
1462  ltc_state,
1466  ltc_state->balance_control_done = STD_NOT_OK;
1467  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST) {
1469  ltc_state,
1473  ltc_state->balance_control_done = STD_NOT_OK;
1474  } else if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST_TI) {
1476  ltc_state,
1480  ltc_state->balance_control_done = STD_NOT_OK;
1481  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST_TI) {
1483  ltc_state,
1487  ltc_state->balance_control_done = STD_NOT_OK;
1488  } else if (statereq.request == LTC_STATE_EEPROM_READ_REQUEST) {
1491  } else if (statereq.request == LTC_STATE_EEPROM_WRITE_REQUEST) {
1494  ltc_state->balance_control_done = STD_NOT_OK;
1495  } else if (statereq.request == LTC_STATE_TEMP_SENS_READ_REQUEST) {
1498  ltc_state->balance_control_done = STD_NOT_OK;
1499  } else if (statereq.request == LTC_STATEMACH_BALANCEFEEDBACK_REQUEST) {
1502  ltc_state->balance_control_done = STD_NOT_OK;
1503  } else if (statereq.request == LTC_STATE_OPENWIRE_CHECK_REQUEST) {
1505  ltc_state,
1509  /* Send ADOW command with PUP two times */
1511  ltc_state->balance_control_done = STD_NOT_OK;
1512  } else {
1514  ltc_state,
1518  ltc_state->balance_control_done = STD_NOT_OK;
1519  }
1520  } else {
1523  }
1524 
1525  break;
1526 
1527  /****************************BALANCE CONTROL*********************************/
1529 
1530  if (ltc_state->substate == LTC_CONFIG_BALANCECONTROL) {
1531  ltc_state->check_spi_flag = STD_OK;
1532  AFE_SetTransmitOngoing(ltc_state);
1533  retVal = LTC_BalanceControl(
1534  ltc_state,
1535  ltc_state->spiSeqPtr,
1536  ltc_state->ltcData.txBuffer,
1537  ltc_state->ltcData.rxBuffer,
1538  ltc_state->ltcData.frameLength,
1539  0u,
1540  ltc_state->currentString);
1542  ltc_state,
1543  retVal,
1544  ltc_state->spiDiagErrorEntry,
1551  break;
1552  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL) {
1553  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1554  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1555  DIAG_Handler(
1557  } else {
1558  DIAG_Handler(
1559  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1560  }
1561 
1563  AFE_SetTransmitOngoing(ltc_state);
1564  retVal = LTC_BalanceControl(
1565  ltc_state,
1566  ltc_state->spiSeqPtr,
1567  ltc_state->ltcData.txBuffer,
1568  ltc_state->ltcData.rxBuffer,
1569  ltc_state->ltcData.frameLength,
1570  1u,
1571  ltc_state->currentString);
1573  ltc_state,
1574  retVal,
1575  ltc_state->spiDiagErrorEntry,
1582  } else {
1583  /* 12 cells, balancing control finished */
1584  ltc_state->check_spi_flag = STD_NOT_OK;
1585  ++ltc_state->spiSeqPtr;
1586  ++ltc_state->currentString;
1587  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1588  ltc_state->balance_control_done = STD_OK;
1590  } else {
1593  }
1594  }
1595 
1596  break;
1597  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL_END) {
1598  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1599  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1600  DIAG_Handler(
1602  } else {
1603  DIAG_Handler(
1604  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1605  }
1606  /* More than 12 cells, balancing control finished */
1607  ltc_state->check_spi_flag = STD_NOT_OK;
1608  ++ltc_state->spiSeqPtr;
1609  ++ltc_state->currentString;
1610  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1611  ltc_state->balance_control_done = STD_OK;
1613  } else {
1616  }
1617 
1618  break;
1619  }
1620  break;
1621 
1622  /****************************START MEASUREMENT*******************************/
1624 
1625  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1627 
1628  ltc_state->check_spi_flag = STD_NOT_OK;
1629  retVal = LTC_StartGpioMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1631  ltc_state,
1632  retVal,
1633  ltc_state->spiDiagErrorEntry,
1636  (ltc_state->commandTransferTime +
1637  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1639  LTC_ENTRY,
1640  LTC_STATEMACH_SHORTTIME); /* TODO: @koffel here same state is kept if error occurs */
1641  break;
1642 
1643  /****************************READ ALL GPIO VOLTAGE************************************/
1645 
1646  if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_A_RDAUXA) {
1647  ltc_state->check_spi_flag = STD_OK;
1648  AFE_SetTransmitOngoing(ltc_state);
1649  retVal = LTC_ReadRegister(
1650  ltc_cmdRDAUXA,
1651  ltc_state->spiSeqPtr,
1652  ltc_state->ltcData.txBuffer,
1653  ltc_state->ltcData.rxBuffer,
1654  ltc_state->ltcData.frameLength);
1656  ltc_state,
1657  retVal,
1658  ltc_state->spiDiagErrorEntry,
1665  break;
1666  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_B_RDAUXB) {
1667  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1668  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1669  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1670 
1671  AFE_SetTransmitOngoing(ltc_state);
1672  retVal = LTC_ReadRegister(
1673  ltc_cmdRDAUXB,
1674  ltc_state->spiSeqPtr,
1675  ltc_state->ltcData.txBuffer,
1676  ltc_state->ltcData.rxBuffer,
1677  ltc_state->ltcData.frameLength);
1678 
1679  if (LTC_6813_MAX_SUPPORTED_CELLS > 12u) {
1681  ltc_state,
1682  retVal,
1683  ltc_state->spiDiagErrorEntry,
1690  } else {
1692  ltc_state,
1693  retVal,
1694  ltc_state->spiDiagErrorEntry,
1701  }
1702  break;
1703  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_C_RDAUXC) {
1704  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1705  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1706  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1707 
1708  AFE_SetTransmitOngoing(ltc_state);
1709  retVal = LTC_ReadRegister(
1710  ltc_cmdRDAUXC,
1711  ltc_state->spiSeqPtr,
1712  ltc_state->ltcData.txBuffer,
1713  ltc_state->ltcData.rxBuffer,
1714  ltc_state->ltcData.frameLength);
1716  ltc_state,
1717  retVal,
1718  ltc_state->spiDiagErrorEntry,
1725  break;
1726  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_D_RDAUXD) {
1727  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1728  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1729  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1730 
1731  AFE_SetTransmitOngoing(ltc_state);
1732  retVal = LTC_ReadRegister(
1733  ltc_cmdRDAUXD,
1734  ltc_state->spiSeqPtr,
1735  ltc_state->ltcData.txBuffer,
1736  ltc_state->ltcData.rxBuffer,
1737  ltc_state->ltcData.frameLength);
1739  ltc_state,
1740  retVal,
1741  ltc_state->spiDiagErrorEntry,
1748  break;
1749  } else if (ltc_state->substate == LTC_EXIT_READAUXILIARY_ALLGPIOS) {
1750  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1751  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1752 
1753  if (LTC_6813_MAX_SUPPORTED_CELLS == 12u) {
1754  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1755  } else if (LTC_6813_MAX_SUPPORTED_CELLS > 12u) {
1756  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1757  }
1758 
1759  LTC_SaveAllGpioMeasurement(ltc_state);
1760 
1763  }
1764 
1765  break;
1766 
1767  /****************************BALANCE FEEDBACK*********************************/
1769 
1770  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1772 
1773  if (ltc_state->substate == LTC_ENTRY) {
1774  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1775  ltc_state->adcMode = LTC_ADCMODE_NORMAL_DCP0;
1777 
1778  ltc_state->check_spi_flag = STD_NOT_OK;
1779  retVal = LTC_StartGpioMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1781  ltc_state,
1782  retVal,
1783  ltc_state->spiDiagErrorEntry,
1786  (ltc_state->commandDataTransferTime +
1787  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1791  break;
1792  } else if (ltc_state->substate == LTC_READ_FEEDBACK_BALANCECONTROL) {
1793  ltc_state->check_spi_flag = STD_OK;
1794  AFE_SetTransmitOngoing(ltc_state);
1795  retVal = LTC_ReadRegister(
1796  ltc_cmdRDAUXA,
1797  ltc_state->spiSeqPtr,
1798  ltc_state->ltcData.txBuffer,
1799  ltc_state->ltcData.rxBuffer,
1800  ltc_state->ltcData.frameLength); /* read AUXA register */
1802  ltc_state,
1803  retVal,
1804  ltc_state->spiDiagErrorEntry,
1811  } else if (ltc_state->substate == LTC_SAVE_FEEDBACK_BALANCECONTROL) {
1812  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1813  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1814  DIAG_Handler(
1817  break;
1818  } else {
1819  DIAG_Handler(
1820  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1821  }
1822 
1823  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1824  DIAG_Handler(
1826  } else {
1827  DIAG_Handler(
1828  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1829  LTC_SaveBalancingFeedback(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1830  }
1832  break;
1833  }
1834  break;
1835 
1836  /****************************BOARD TEMPERATURE SENSOR*********************************/
1838 
1839  if (ltc_state->substate == LTC_TEMP_SENS_SEND_DATA1) {
1840  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1841  ltc_state->check_spi_flag = STD_OK;
1842  AFE_SetTransmitOngoing(ltc_state);
1843  retVal = LTC_SendI2cCommand(
1844  ltc_state->spiSeqPtr,
1845  ltc_state->ltcData.txBuffer,
1846  ltc_state->ltcData.rxBuffer,
1847  ltc_state->ltcData.frameLength,
1849 
1850  if (retVal != STD_OK) {
1851  DIAG_Handler(
1853  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1855  } else {
1856  DIAG_Handler(
1857  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1859  ltc_state,
1863  }
1864 
1865  break;
1866  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM1) {
1867  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1868  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1869  DIAG_Handler(
1872  break;
1873  } else {
1874  DIAG_Handler(
1875  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1876  }
1877 
1878  AFE_SetTransmitOngoing(ltc_state);
1879  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1881  ltc_state,
1882  retVal,
1883  ltc_state->spiDiagErrorEntry,
1890  break;
1891  } else if (ltc_state->substate == LTC_TEMP_SENS_READ_DATA1) {
1892  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1893  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1894  DIAG_Handler(
1897  break;
1898  } else {
1899  DIAG_Handler(
1900  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1901  }
1902 
1903  AFE_SetTransmitOngoing(ltc_state);
1904  retVal = LTC_SendI2cCommand(
1905  ltc_state->spiSeqPtr,
1906  ltc_state->ltcData.txBuffer,
1907  ltc_state->ltcData.rxBuffer,
1908  ltc_state->ltcData.frameLength,
1910 
1911  if (retVal != STD_OK) {
1912  DIAG_Handler(
1914  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1916  } else {
1917  DIAG_Handler(
1918  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1920  ltc_state,
1924  }
1925 
1926  break;
1927  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM2) {
1928  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1929  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1930  DIAG_Handler(
1933  break;
1934  } else {
1935  DIAG_Handler(
1936  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1937  }
1938 
1939  AFE_SetTransmitOngoing(ltc_state);
1940  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1942  ltc_state,
1943  retVal,
1944  ltc_state->spiDiagErrorEntry,
1951  break;
1953  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1954  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1955  DIAG_Handler(
1958  break;
1959  } else {
1960  DIAG_Handler(
1961  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1962  }
1963 
1964  AFE_SetTransmitOngoing(ltc_state);
1965  retVal = LTC_ReadRegister(
1966  ltc_cmdRDCOMM,
1967  ltc_state->spiSeqPtr,
1968  ltc_state->ltcData.txBuffer,
1969  ltc_state->ltcData.rxBuffer,
1970  ltc_state->ltcData.frameLength);
1972  ltc_state,
1973  retVal,
1974  ltc_state->spiDiagErrorEntry,
1981  break;
1982  } else if (ltc_state->substate == LTC_TEMP_SENS_SAVE_TEMP) {
1983  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
1984  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
1985  DIAG_Handler(
1988  break;
1989  } else {
1990  DIAG_Handler(
1991  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1992  }
1993 
1994  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1995  DIAG_Handler(
1997  } else {
1998  DIAG_Handler(
1999  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2000  LTC_TempSensSaveTemp(ltc_state, ltc_state->ltcData.rxBuffer);
2001  }
2002 
2004  break;
2005  }
2006  break;
2007 
2008  /****************************WRITE TO PORT EXPANDER IO***********/
2010 
2011  if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER) {
2012  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2013  ltc_state->check_spi_flag = STD_OK;
2014  AFE_SetTransmitOngoing(ltc_state);
2015  retVal = LTC_SetPortExpander(
2016  ltc_state,
2017  ltc_state->spiSeqPtr,
2018  ltc_state->ltcData.txBuffer,
2019  ltc_state->ltcData.rxBuffer,
2020  ltc_state->ltcData.frameLength);
2021 
2022  if (retVal != STD_OK) {
2023  DIAG_Handler(
2025  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2027  } else {
2028  DIAG_Handler(
2029  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2031  ltc_state,
2035  }
2036  break;
2037  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
2038  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2039  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2040  DIAG_Handler(
2043  break;
2044  } else {
2045  DIAG_Handler(
2046  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2047  }
2048 
2049  ltc_state->check_spi_flag = STD_NOT_OK;
2050  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2052  ltc_state,
2053  retVal,
2054  ltc_state->spiDiagErrorEntry,
2056  LTC_ENTRY,
2057  ltc_state->gpioClocksTransferTime,
2059  LTC_ENTRY,
2061  break;
2062  }
2063  break;
2064 
2065  /****************************READ FROM PORT EXPANDER IO***********/
2067 
2068  if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER) {
2069  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2070  ltc_state->check_spi_flag = STD_OK;
2071  AFE_SetTransmitOngoing(ltc_state);
2072  retVal = LTC_SendI2cCommand(
2073  ltc_state->spiSeqPtr,
2074  ltc_state->ltcData.txBuffer,
2075  ltc_state->ltcData.rxBuffer,
2076  ltc_state->ltcData.frameLength,
2078 
2079  if (retVal != STD_OK) {
2080  DIAG_Handler(
2082  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2084  } else {
2085  DIAG_Handler(
2086  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2088  ltc_state,
2092  }
2093 
2094  break;
2095  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM) {
2096  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2097  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2098  DIAG_Handler(
2101  break;
2102  } else {
2103  DIAG_Handler(
2104  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2105  }
2106 
2107  ltc_state->check_spi_flag = STD_NOT_OK;
2108  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2110  ltc_state,
2111  retVal,
2112  ltc_state->spiDiagErrorEntry,
2115  ltc_state->gpioClocksTransferTime,
2119  break;
2120  } else if (ltc_state->substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2121  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2122  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2123  DIAG_Handler(
2126  break;
2127  } else {
2128  DIAG_Handler(
2129  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2130  }
2131 
2132  AFE_SetTransmitOngoing(ltc_state);
2133  retVal = LTC_ReadRegister(
2134  ltc_cmdRDCOMM,
2135  ltc_state->spiSeqPtr,
2136  ltc_state->ltcData.txBuffer,
2137  ltc_state->ltcData.rxBuffer,
2138  ltc_state->ltcData.frameLength);
2140  ltc_state,
2141  retVal,
2142  ltc_state->spiDiagErrorEntry,
2149  break;
2150  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA) {
2151  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2152  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2153  DIAG_Handler(
2156  break;
2157  } else {
2158  DIAG_Handler(
2159  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2160  }
2161 
2162  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2163  DIAG_Handler(
2165  } else {
2166  DIAG_Handler(
2167  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2168  LTC_PortExpanderSaveValues(ltc_state, ltc_state->ltcData.rxBuffer);
2169  }
2170 
2172  break;
2173  }
2174 
2175  break;
2176 
2177  /****************************WRITE TO TI PORT EXPANDER IO***********/
2179 
2180  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2181  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2182  ltc_state->check_spi_flag = STD_OK;
2183  AFE_SetTransmitOngoing(ltc_state);
2185  ltc_state,
2186  ltc_state->spiSeqPtr,
2187  ltc_state->ltcData.txBuffer,
2188  ltc_state->ltcData.rxBuffer,
2189  ltc_state->ltcData.frameLength,
2192  ltc_state,
2193  retVal,
2194  ltc_state->spiDiagErrorEntry,
2199  LTC_ENTRY,
2201  break;
2202  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2203  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2204  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2205  DIAG_Handler(
2208  break;
2209  } else {
2210  DIAG_Handler(
2211  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2212  }
2213 
2214  ltc_state->check_spi_flag = STD_NOT_OK;
2215  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2217  ltc_state,
2218  retVal,
2219  ltc_state->spiDiagErrorEntry,
2222  ltc_state->gpioClocksTransferTime,
2224  LTC_ENTRY,
2226  break;
2227  } else if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER_TI) {
2228  ltc_state->check_spi_flag = STD_OK;
2229  AFE_SetTransmitOngoing(ltc_state);
2230  retVal = LTC_SetPortExpanderOutputTi(
2231  ltc_state,
2232  ltc_state->spiSeqPtr,
2233  ltc_state->ltcData.txBuffer,
2234  ltc_state->ltcData.rxBuffer,
2235  ltc_state->ltcData.frameLength);
2237  ltc_state,
2238  retVal,
2239  ltc_state->spiDiagErrorEntry,
2244  LTC_ENTRY,
2246  break;
2248  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2249  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2250  DIAG_Handler(
2253  break;
2254  } else {
2255  DIAG_Handler(
2256  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2257  }
2258 
2259  ltc_state->check_spi_flag = STD_NOT_OK;
2260  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2262  ltc_state,
2263  retVal,
2264  ltc_state->spiDiagErrorEntry,
2266  LTC_ENTRY,
2267  ltc_state->gpioClocksTransferTime,
2269  LTC_ENTRY,
2271  break;
2272  }
2273  break;
2274 
2275  /****************************READ TI PORT EXPANDER IO***********/
2277 
2278  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2279  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2280  ltc_state->check_spi_flag = STD_OK;
2281  AFE_SetTransmitOngoing(ltc_state);
2283  ltc_state,
2284  ltc_state->spiSeqPtr,
2285  ltc_state->ltcData.txBuffer,
2286  ltc_state->ltcData.rxBuffer,
2287  ltc_state->ltcData.frameLength,
2290  ltc_state,
2291  retVal,
2292  ltc_state->spiDiagErrorEntry,
2297  LTC_ENTRY,
2299  break;
2300  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2301  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2302  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2303  DIAG_Handler(
2306  break;
2307  } else {
2308  DIAG_Handler(
2309  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2310  }
2311 
2312  ltc_state->check_spi_flag = STD_NOT_OK;
2313  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2315  ltc_state,
2316  retVal,
2317  ltc_state->spiDiagErrorEntry,
2320  ltc_state->gpioClocksTransferTime,
2322  LTC_ENTRY,
2324  break;
2325  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST) {
2326  ltc_state->check_spi_flag = STD_OK;
2327  AFE_SetTransmitOngoing(ltc_state);
2328  retVal = LTC_GetPortExpanderInputTi(
2329  ltc_state,
2330  ltc_state->spiSeqPtr,
2331  ltc_state->ltcData.txBuffer,
2332  ltc_state->ltcData.rxBuffer,
2333  ltc_state->ltcData.frameLength,
2334  0);
2336  ltc_state,
2337  retVal,
2338  ltc_state->spiDiagErrorEntry,
2343  LTC_ENTRY,
2345  break;
2347  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2348  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2349  DIAG_Handler(
2352  break;
2353  } else {
2354  DIAG_Handler(
2355  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2356  }
2357 
2358  ltc_state->check_spi_flag = STD_NOT_OK;
2359  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2361  ltc_state,
2362  retVal,
2363  ltc_state->spiDiagErrorEntry,
2366  ltc_state->gpioClocksTransferTime,
2368  LTC_ENTRY,
2370  break;
2371  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND) {
2372  ltc_state->check_spi_flag = STD_OK;
2373  AFE_SetTransmitOngoing(ltc_state);
2374  retVal = LTC_GetPortExpanderInputTi(
2375  ltc_state,
2376  ltc_state->spiSeqPtr,
2377  ltc_state->ltcData.txBuffer,
2378  ltc_state->ltcData.rxBuffer,
2379  ltc_state->ltcData.frameLength,
2380  1);
2382  ltc_state,
2383  retVal,
2384  ltc_state->spiDiagErrorEntry,
2389  LTC_ENTRY,
2391  break;
2393  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2394  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2395  DIAG_Handler(
2398  break;
2399  } else {
2400  DIAG_Handler(
2401  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2402  }
2403 
2404  ltc_state->check_spi_flag = STD_NOT_OK;
2405  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2407  ltc_state,
2408  retVal,
2409  ltc_state->spiDiagErrorEntry,
2412  ltc_state->gpioClocksTransferTime,
2414  LTC_ENTRY,
2416  break;
2418  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2419  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2420  DIAG_Handler(
2423  break;
2424  } else {
2425  DIAG_Handler(
2426  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2427  }
2428 
2429  AFE_SetTransmitOngoing(ltc_state);
2430  retVal = LTC_ReadRegister(
2431  ltc_cmdRDCOMM,
2432  ltc_state->spiSeqPtr,
2433  ltc_state->ltcData.txBuffer,
2434  ltc_state->ltcData.rxBuffer,
2435  ltc_state->ltcData.frameLength);
2437  ltc_state,
2438  retVal,
2439  ltc_state->spiDiagErrorEntry,
2446  break;
2447  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA_TI) {
2448  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2449  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2450  DIAG_Handler(
2453  break;
2454  } else {
2455  DIAG_Handler(
2456  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2457  }
2458 
2459  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2460  DIAG_Handler(
2462  } else {
2463  DIAG_Handler(
2464  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2465  LTC_PortExpanderSaveValuesTi(ltc_state, ltc_state->ltcData.txBuffer);
2466  }
2467 
2469  break;
2470  }
2471 
2472  break;
2473 
2474  /****************************EEPROM READ*********************************/
2476 
2477  if (ltc_state->substate == LTC_EEPROM_READ_DATA1) {
2478  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2479  ltc_state->check_spi_flag = STD_OK;
2480  AFE_SetTransmitOngoing(ltc_state);
2481  retVal = LTC_SendEepromReadCommand(
2482  ltc_state,
2483  ltc_state->spiSeqPtr,
2484  ltc_state->ltcData.txBuffer,
2485  ltc_state->ltcData.rxBuffer,
2486  ltc_state->ltcData.frameLength,
2487  0);
2488 
2489  if (retVal != STD_OK) {
2490  DIAG_Handler(
2492  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2494  } else {
2495  DIAG_Handler(
2496  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2498  ltc_state,
2502  }
2503 
2504  break;
2505  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM1) {
2506  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2507  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2508  DIAG_Handler(
2511  break;
2512  } else {
2513  DIAG_Handler(
2514  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2515  }
2516 
2517  AFE_SetTransmitOngoing(ltc_state);
2518  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2520  ltc_state,
2521  retVal,
2522  ltc_state->spiDiagErrorEntry,
2529  break;
2530  } else if (ltc_state->substate == LTC_EEPROM_READ_DATA2) {
2531  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2532  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2533  DIAG_Handler(
2536  break;
2537  } else {
2538  DIAG_Handler(
2539  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2540  }
2541 
2542  AFE_SetTransmitOngoing(ltc_state);
2543  retVal = LTC_SendEepromReadCommand(
2544  ltc_state,
2545  ltc_state->spiSeqPtr,
2546  ltc_state->ltcData.txBuffer,
2547  ltc_state->ltcData.rxBuffer,
2548  ltc_state->ltcData.frameLength,
2549  1);
2551  ltc_state,
2552  retVal,
2553  ltc_state->spiDiagErrorEntry,
2560  break;
2561  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM2) {
2562  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2563  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2564  DIAG_Handler(
2567  break;
2568  } else {
2569  DIAG_Handler(
2570  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2571  }
2572 
2573  AFE_SetTransmitOngoing(ltc_state);
2574  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2576  ltc_state,
2577  retVal,
2578  ltc_state->spiDiagErrorEntry,
2585  break;
2586  } else if (ltc_state->substate == LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2587  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2588  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2589  DIAG_Handler(
2592  break;
2593  } else {
2594  DIAG_Handler(
2595  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2596  }
2597 
2598  AFE_SetTransmitOngoing(ltc_state);
2599  retVal = LTC_ReadRegister(
2600  ltc_cmdRDCOMM,
2601  ltc_state->spiSeqPtr,
2602  ltc_state->ltcData.txBuffer,
2603  ltc_state->ltcData.rxBuffer,
2604  ltc_state->ltcData.frameLength);
2606  ltc_state,
2607  retVal,
2608  ltc_state->spiDiagErrorEntry,
2615  break;
2616  } else if (ltc_state->substate == LTC_EEPROM_SAVE_READ) {
2617  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2618  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2619  DIAG_Handler(
2622  break;
2623  } else {
2624  DIAG_Handler(
2625  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2626  }
2627 
2628  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2629  DIAG_Handler(
2631  } else {
2632  DIAG_Handler(
2633  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2634  LTC_EepromSaveReadValue(ltc_state, ltc_state->ltcData.rxBuffer);
2635  }
2637  break;
2638  }
2639 
2640  break;
2641 
2642  /****************************EEPROM READ*********************************/
2644 
2645  if (ltc_state->substate == LTC_EEPROM_WRITE_DATA1) {
2646  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2647  ltc_state->check_spi_flag = STD_OK;
2648  AFE_SetTransmitOngoing(ltc_state);
2649  retVal = LTC_SendEepromWriteCommand(
2650  ltc_state,
2651  ltc_state->spiSeqPtr,
2652  ltc_state->ltcData.txBuffer,
2653  ltc_state->ltcData.rxBuffer,
2654  ltc_state->ltcData.frameLength,
2655  0);
2656 
2657  if (retVal != STD_OK) {
2658  DIAG_Handler(
2660  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2662  } else {
2663  DIAG_Handler(
2664  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2666  ltc_state,
2670  }
2671 
2672  break;
2673  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM3) {
2674  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2675  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2676  DIAG_Handler(
2679  break;
2680  } else {
2681  DIAG_Handler(
2682  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2683  }
2684 
2685  AFE_SetTransmitOngoing(ltc_state);
2686  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2688  ltc_state,
2689  retVal,
2690  ltc_state->spiDiagErrorEntry,
2697  break;
2698  } else if (ltc_state->substate == LTC_EEPROM_WRITE_DATA2) {
2699  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2700  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2701  DIAG_Handler(
2704  break;
2705  } else {
2706  DIAG_Handler(
2707  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2708  }
2709 
2710  AFE_SetTransmitOngoing(ltc_state);
2711  retVal = LTC_SendEepromWriteCommand(
2712  ltc_state,
2713  ltc_state->spiSeqPtr,
2714  ltc_state->ltcData.txBuffer,
2715  ltc_state->ltcData.rxBuffer,
2716  ltc_state->ltcData.frameLength,
2717  1);
2719  ltc_state,
2720  retVal,
2721  ltc_state->spiDiagErrorEntry,
2728  break;
2729  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM4) {
2730  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2731  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2732  DIAG_Handler(
2735  break;
2736  } else {
2737  DIAG_Handler(
2738  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2739  }
2740 
2741  AFE_SetTransmitOngoing(ltc_state);
2742  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2744  ltc_state,
2745  retVal,
2746  ltc_state->spiDiagErrorEntry,
2753  break;
2754  } else if (ltc_state->substate == LTC_EEPROM_FINISHED) {
2755  bool transmitOngoing = AFE_IsTransmitOngoing(ltc_state);
2756  if ((ltc_state->timer == 0) && (transmitOngoing == true)) {
2757  DIAG_Handler(
2760  break;
2761  } else {
2762  DIAG_Handler(
2763  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2764  }
2766  break;
2767  }
2768 
2769  break;
2770 
2771  /**************************OPEN-WIRE CHECK*******************************/
2773  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2774  /* This is necessary because the state machine will go through read voltage measurement registers */
2775  ltc_state->currentString = ltc_state->requestedString;
2777  /* Run ADOW command with PUP = 1 */
2778  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2779  ltc_state->check_spi_flag = STD_NOT_OK;
2780 
2781  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 1);
2782  if (retVal == STD_OK) {
2783  DIAG_Handler(
2784  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2786  ltc_state,
2789  (ltc_state->commandDataTransferTime +
2791  ltc_state->resendCommandCounter--;
2792 
2793  /* Check how many retries are left */
2794  if (ltc_state->resendCommandCounter == 0) {
2795  /* Switch to read voltage state to read cell voltages */
2797  ltc_state,
2800  (ltc_state->commandDataTransferTime +
2802  /* Reuse read voltage register */
2804  }
2805  } else {
2806  DIAG_Handler(
2810  }
2811  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK) {
2812  /* Previous state: Read voltage -> information stored in voltage buffer */
2814 
2815  /* Copy data from voltage struct into open-wire struct */
2816  for (uint16_t i = 0u; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
2817  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] =
2818  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][i];
2819  }
2820 
2821  /* Set number of ADOW retries - send ADOW command with pull-down two times */
2824  ltc_state,
2828  } else if (ltc_state->substate == LTC_REQUEST_PULLDOWN_CURRENT_OPENWIRE_CHECK) {
2829  /* Run ADOW command with PUP = 0 */
2830  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2831  ltc_state->check_spi_flag = STD_NOT_OK;
2832 
2833  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 0);
2834  if (retVal == STD_OK) {
2835  DIAG_Handler(
2836  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2838  ltc_state,
2841  (ltc_state->commandDataTransferTime +
2843  ltc_state->resendCommandCounter--;
2844 
2845  /* Check how many retries are left */
2846  if (ltc_state->resendCommandCounter == 0) {
2847  /* Switch to read voltage state to read cell voltages */
2849  ltc_state,
2852  (ltc_state->commandDataTransferTime +
2854  /* Reuse read voltage register */
2856  }
2857  } else {
2858  DIAG_Handler(
2862  }
2863  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLDOWN_OPENWIRE_CHECK) {
2864  /* Previous state: Read voltage -> information stored in voltage buffer */
2866 
2867  /* Copy data from voltage struct into open-wire struct */
2868  for (uint16_t i = 0u; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
2869  ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][i] =
2870  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][i];
2871  }
2874  } else if (ltc_state->substate == LTC_PERFORM_OPENWIRE_CHECK) {
2875  /* Perform actual open-wire check */
2876  for (uint8_t m = 0; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2877  /* Open-wire at C0: cell_pup(0) == 0 */
2878  if (ltc_state->ltcData.openWireDetection
2879  ->openWirePup[ltc_state->requestedString][0 + (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] ==
2880  0u) {
2881  ltc_state->ltcData.openWire->openWire[ltc_state->requestedString]
2882  [0 + (m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE))] = 1u;
2883  }
2884  /* Open-wire at Cmax: cell_pdown(BS_NR_OF_CELL_BLOCKS_PER_MODULE-1) == 0 */
2885  if (ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][(
2887  ltc_state->ltcData.openWire
2888  ->openWire[ltc_state->requestedString]
2890  1u;
2891  }
2892  }
2893 
2894  /* Take difference between pull-up and pull-down measurement */
2895  for (uint16_t i = 1u; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
2896  ltc_state->ltcData.openWireDetection->openWireDelta[ltc_state->requestedString][i] =
2897  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] -
2898  ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][i];
2899  }
2900 
2901  /* Open-wire at C(N): delta cell(n+1) < -400mV */
2902  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2903  for (uint8_t c = 1u; c < (BS_NR_OF_CELL_BLOCKS_PER_MODULE - 1); c++) {
2904  if (ltc_state->ltcData.openWireDetection
2905  ->openWireDelta[ltc_state->requestedString]
2907  ltc_state->ltcData.openWire->openWire[ltc_state->requestedString]
2908  [c + (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] = 1;
2909  }
2910  }
2911  }
2912 
2913  /* Write database entry */
2914  DATA_WRITE_DATA(ltc_state->ltcData.openWire);
2915  /* Start new measurement cycle */
2917  }
2918  break;
2919 
2920  /****************************DEFAULT**************************/
2921  default:
2922  /* invalid state */
2924  break;
2925  }
2926 
2927  ltc_state->triggerentry--; /* reentrance counter */
2928  } /* continueFunction */
2929 }
2930 
2931 /**
2932  * @brief saves the multiplexer values read from the LTC daisy-chain.
2933  *
2934  * After a voltage measurement was initiated on GPIO 1 to read the currently
2935  * selected multiplexer voltage, the results is read via SPI from the
2936  * daisy-chain.
2937  * This function is called to store the result from the transmission in a
2938  * buffer.
2939  *
2940  * @param ltc_state state of the ltc state machine
2941  * @param pRxBuff receive buffer
2942  * @param muxseqptr pointer to the multiplexer sequence, which
2943  * configures the currently selected multiplexer ID and
2944  * channel
2945  * @param stringNumber string addressed
2946  */
2948  LTC_STATE_s *ltc_state,
2949  uint16_t *pRxBuff,
2950  LTC_MUX_CH_CFG_s *muxseqptr,
2951  uint8_t stringNumber) {
2952  FAS_ASSERT(ltc_state != NULL_PTR);
2953  FAS_ASSERT(pRxBuff != NULL_PTR);
2954  FAS_ASSERT(muxseqptr != NULL_PTR);
2955  uint16_t val_ui = 0;
2956  int16_t temperature_ddegC = 0;
2957  uint8_t sensor_idx = 0;
2958  uint8_t ch_idx = 0;
2959  uint16_t buffer_LSB = 0;
2960  uint16_t buffer_MSB = 0;
2961 
2962  /* pointer to measurement Sequence of Mux- and Channel-Configurations (1,0xFF)...(3,0xFF),(0,1),...(0,7)) */
2963  /* Channel 0xFF means that the multiplexer is deactivated, therefore no measurement will be made and saved*/
2964  if (muxseqptr->muxCh != 0xFF) {
2965  /* user multiplexer type -> connected to GPIO2! */
2966  if ((muxseqptr->muxID == 1) || (muxseqptr->muxID == 2)) {
2967  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2968  if (muxseqptr->muxID == 1) {
2969  ch_idx = 0 + muxseqptr->muxCh; /* channel index 0..7 */
2970  } else {
2971  ch_idx = 8 + muxseqptr->muxCh; /* channel index 8..15 */
2972  }
2973 
2974  if (ch_idx < (2u * 8u)) {
2975  val_ui = *((uint16_t *)(&pRxBuff[6u + (1u * i * 8u)])); /* raw values, all mux on all LTCs */
2976  /* ltc_user_mux.value[i*8*2+ch_idx] = (uint16_t)(((float_t)(val_ui))*100e-6f*1000.0f); */ /* Unit -> in V -> in mV */
2977  }
2978  }
2979  } else {
2980  /* temperature multiplexer type -> connected to GPIO1! */
2981  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2982  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
2983  buffer_LSB = pRxBuff[4u + (i * 8u)];
2984  val_ui = buffer_LSB | (buffer_MSB << 8);
2985  /* val_ui = *((uint16_t *)(&pRxBuff[4+i*8])); */
2986  /* GPIO voltage in 100uV -> * 0.1 ---- conversion to mV */
2987  temperature_ddegC = LTC_ConvertMuxVoltagesToTemperatures(val_ui / 10u); /* unit: deci &deg;C */
2988  sensor_idx = ltc_muxsensortemperatur_cfg[muxseqptr->muxCh];
2989  /* wrong configuration! */
2990  if (sensor_idx >= BS_NR_OF_TEMP_SENSORS_PER_MODULE) {
2992  }
2993  /* Set bitmask for valid flags */
2994 
2995  /* Check LTC PEC error */
2996  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
2997  /* Reset invalid flag */
2998  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] =
2999  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] &
3000  (~(1u << sensor_idx));
3001 
3002  ltc_state->ltcData.cellTemperature
3003  ->cellTemperature_ddegC[stringNumber][(i * (BS_NR_OF_TEMP_SENSORS_PER_MODULE)) + sensor_idx] =
3004  temperature_ddegC;
3005  } else {
3006  /* Set invalid flag */
3007  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] |= (1u << sensor_idx);
3008  }
3009  }
3010  }
3011  }
3012 }
3013 
3014 /**
3015  * @brief saves the voltage values read from the LTC daisy-chain.
3016  *
3017  * After a voltage measurement was initiated to measure the voltages of the cells,
3018  * the result is read via SPI from the daisy-chain.
3019  * There are 6 register to read _(A,B,C,D,E,F) to get all cell voltages.
3020  * Only one register can be read at a time.
3021  * This function is called to store the result from the transmission in a buffer.
3022  *
3023  * @param ltc_state state of the ltc state machine
3024  * @param pRxBuff receive buffer
3025  * @param registerSet voltage register that was read (voltage register A,B,C,D,E or F)
3026  * @param stringNumber string addressed
3027  *
3028  */
3030  LTC_STATE_s *ltc_state,
3031  uint16_t *pRxBuff,
3032  uint8_t registerSet,
3033  uint8_t stringNumber) {
3034  FAS_ASSERT(ltc_state != NULL_PTR);
3035  FAS_ASSERT(pRxBuff != NULL_PTR);
3036  uint16_t cellOffset = 0;
3037  uint16_t voltage_index = 0;
3038  uint16_t val_ui = 0;
3039  uint16_t voltage = 0;
3040  uint32_t bitmask = 0;
3041  uint16_t buffer_LSB = 0;
3042  uint16_t buffer_MSB = 0;
3043  bool continueFunction = true;
3044 
3045  if (registerSet == 0u) {
3046  /* RDCVA command -> voltage register group A */
3047  cellOffset = 0;
3048  } else if (registerSet == 1u) {
3049  /* RDCVB command -> voltage register group B */
3050  cellOffset = 3;
3051  } else if (registerSet == 2u) {
3052  /* RDCVC command -> voltage register group C */
3053  cellOffset = 6;
3054  } else if (registerSet == 3u) {
3055  /* RDCVD command -> voltage register group D */
3056  cellOffset = 9;
3057  } else if (registerSet == 4u) {
3058  /* RDCVD command -> voltage register group E (only for 15 and 18 cell version) */
3059  cellOffset = 12;
3060  } else if (registerSet == 5u) {
3061  /* RDCVD command -> voltage register group F (only for 18 cell version) */
3062  cellOffset = 15;
3063  } else {
3064  continueFunction = false;
3065  }
3066 
3067  if (continueFunction == true) {
3068  /* Calculate bitmask for valid flags */
3069  bitmask |= 0x07u << cellOffset; /* 0x07: three voltages in each register */
3070 
3071  /* reinitialize index counter at begin of cycle */
3072  if (cellOffset == 0u) {
3073  (ltc_state->ltcData.usedCellIndex[stringNumber]) = 0;
3074  }
3075 
3076  /* Retrieve data without command and CRC*/
3077  for (uint16_t m = 0u; m < LTC_N_LTC; m++) {
3078  uint16_t incrementations = 0u;
3079 
3080  /* parse all three voltages (3 * 2bytes) contained in one register */
3081  for (uint8_t c = 0u; c < 3u; c++) {
3082  /* index considering maximum number of cells */
3083  voltage_index = c + cellOffset;
3084 
3085  if (ltc_voltage_input_used[voltage_index] == 1u) {
3086  buffer_MSB = pRxBuff[4u + (2u * c) + (m * 8u) + 1u];
3087  buffer_LSB = pRxBuff[4u + (2u * c) + (m * 8u)];
3088  val_ui = buffer_LSB | (buffer_MSB << 8u);
3089  /* val_ui = *((uint16_t *)(&pRxBuff[4+2*j+i*8])); */
3090  voltage = ((val_ui)) * 100e-6f * 1000.0f; /* Unit V -> in mV */
3091 
3092  /* Check PEC for every LTC in the daisy-chain */
3093  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][m] == true) {
3094  ltc_state->ltcData.cellVoltage
3095  ->cellVoltage_mV[stringNumber]
3096  [(ltc_state->ltcData.usedCellIndex[stringNumber]) +
3097  (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] = voltage;
3098  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3099  ltc_state->ltcData.cellVoltage
3100  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] &= bitmask;
3101  } else {
3102  /* PEC_valid == false: Invalidate only flags of this voltage register */
3103  ltc_state->ltcData.cellVoltage
3104  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] |= bitmask;
3105  }
3106 
3107  (ltc_state->ltcData.usedCellIndex[stringNumber])++;
3108  incrementations++;
3109 
3110  if ((ltc_state->ltcData.usedCellIndex[stringNumber]) > BS_NR_OF_CELL_BLOCKS_PER_MODULE) {
3111  break;
3112  }
3113  }
3114  }
3115 
3116  /* Restore start value for next module in the daisy-chain. Only
3117  * decrement used cell index if current module is not the last
3118  * module in the daisy-chain. */
3119  if ((m + 1u) < LTC_N_LTC) {
3120  (ltc_state->ltcData.usedCellIndex[stringNumber]) -= incrementations;
3121  }
3122  }
3123  }
3124 }
3125 
3126 /**
3127  * @brief saves the GPIO voltage values read from the LTC daisy-chain.
3128  *
3129  * After a voltage measurement was initiated to measure the voltages on all GPIOs,
3130  * the result is read via SPI from the daisy-chain. In order to read the result of all GPIO measurements,
3131  * it is necessary to read auxiliary register A and B.
3132  * Only one register can be read at a time.
3133  * This function is called to store the result from the transmission in a buffer.
3134  *
3135  * @param ltc_state state of the ltc state machine
3136  * @param pRxBuff receive buffer
3137  * @param registerSet voltage register that was read (auxiliary register A, B, C or D)
3138  * @param stringNumber string addressed
3139  *
3140  */
3142  LTC_STATE_s *ltc_state,
3143  uint16_t *pRxBuff,
3144  uint8_t registerSet,
3145  uint8_t stringNumber) {
3146  FAS_ASSERT(ltc_state != NULL_PTR);
3147  FAS_ASSERT(pRxBuff != NULL_PTR);
3148  uint8_t i_offset = 0;
3149  uint32_t bitmask = 0;
3150  uint16_t buffer_LSB = 0;
3151  uint16_t buffer_MSB = 0;
3152 
3153  if (registerSet == 0u) {
3154  /* RDAUXA command -> GPIO register group A */
3155  i_offset = 0;
3156  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3157  /* Retrieve data without command and CRC*/
3158  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3159  /* Check if PEC is valid */
3160  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3161  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3162  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3163  /* values received in 100uV -> divide by 10 to convert to mV */
3164  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3165  buffer_LSB = pRxBuff[4u + (i * 8u)];
3166  ltc_state->ltcData.allGpioVoltages
3167  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3168  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3169  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3170  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3171  buffer_LSB = pRxBuff[6u + (i * 8u)];
3172  ltc_state->ltcData.allGpioVoltages
3173  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3174  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3175  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3176  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3177  buffer_LSB = pRxBuff[8u + (i * 8u)];
3178  ltc_state->ltcData.allGpioVoltages
3179  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3180  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3181  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3182  } else {
3183  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3184  }
3185  }
3186  } else if (registerSet == 1u) {
3187  /* RDAUXB command -> GPIO register group B */
3188  i_offset = 3;
3189  bitmask = 0x03u << i_offset; /* 0x03: two temperatures in this register */
3190  /* Retrieve data without command and CRC*/
3191  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3192  /* Check if PEC is valid */
3193  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3194  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3195  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3196  /* values received in 100uV -> divide by 10 to convert to mV */
3197  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3198  buffer_LSB = pRxBuff[4u + (i * 8u)];
3199  ltc_state->ltcData.allGpioVoltages
3200  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3201  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3202  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3203  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3204  buffer_LSB = pRxBuff[6u + (i * 8u)];
3205  ltc_state->ltcData.allGpioVoltages
3206  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3207  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3208  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3209  } else {
3210  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3211  }
3212  }
3213  } else if (registerSet == 2u) {
3214  /* RDAUXC command -> GPIO register group C, for 18 cell version */
3215  i_offset = 5;
3216  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3217  /* Retrieve data without command and CRC*/
3218  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3219  /* Check if PEC is valid */
3220  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3221  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3222  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3223  /* values received in 100uV -> divide by 10 to convert to mV */
3224  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3225  buffer_LSB = pRxBuff[4u + (i * 8u)];
3226  ltc_state->ltcData.allGpioVoltages
3227  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3228  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3229  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3230  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3231  buffer_LSB = pRxBuff[6u + (i * 8u)];
3232  ltc_state->ltcData.allGpioVoltages
3233  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3234  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3235  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3236  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3237  buffer_LSB = pRxBuff[8u + (i * 8u)];
3238  ltc_state->ltcData.allGpioVoltages
3239  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3240  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3241  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3242  } else {
3243  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3244  }
3245  }
3246  } else if (registerSet == 3u) {
3247  /* RDAUXD command -> GPIO register group D, for 18 cell version */
3248  i_offset = 8;
3249  bitmask = 0x01u << i_offset; /* 0x01: one temperature in this register */
3250  /* Retrieve data without command and CRC*/
3251  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3252  /* Check if PEC is valid */
3253  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3254  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3255  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3256  /* values received in 100uV -> divide by 10 to convert to mV */
3257  ltc_state->ltcData.allGpioVoltages
3258  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3259  *((uint16_t *)(&pRxBuff[4u + (i * 8u)])) / 10u;
3260  } else {
3261  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3262  }
3263  }
3264  } else {
3265  ; /* Nothing to do */
3266  }
3267 }
3268 
3269 /**
3270  * @brief checks if the multiplexers acknowledged transmission.
3271  *
3272  * The RDCOMM command can be used to read the answer of the multiplexers to a
3273  * I2C transmission.
3274  * This function determines if the communication with the multiplexers was
3275  * successful or not.
3276  * The array error table is updated to locate the multiplexers that did not
3277  * acknowledge transmission.
3278  *
3279  * @param ltc_state state of the ltc state machine
3280  * @param pRxBuff receive buffer
3281  * @param mux multiplexer to be addressed (multiplexer ID)
3282  * @param stringNumber string addressed
3283  *
3284  * @return STD_OK if there was no error, STD_NOT_OK if there was errors
3285  */
3286 static STD_RETURN_TYPE_e LTC_I2cCheckAck(LTC_STATE_s *ltc_state, uint16_t *pRxBuff, uint8_t mux, uint8_t stringNumber) {
3287  FAS_ASSERT(ltc_state != NULL_PTR);
3288  FAS_ASSERT(pRxBuff != NULL_PTR);
3289  STD_RETURN_TYPE_e muxError = STD_OK;
3290 
3291  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
3292  if ((pRxBuff[4u + 1u + (LTC_NUMBER_OF_LTC_PER_MODULE * i * 8u)] & 0x0Fu) != 0x07u) { /* ACK = 0xX7 */
3293  if (LTC_DISCARD_MUX_CHECK == false) {
3294  if (mux == 0u) {
3295  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 1;
3296  }
3297  if (mux == 1u) {
3298  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 1;
3299  }
3300  if (mux == 2u) {
3301  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 1;
3302  }
3303  if (mux == 3u) {
3304  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 1;
3305  }
3306  }
3307  muxError = STD_NOT_OK;
3308  } else {
3309  if (mux == 0u) {
3310  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 0;
3311  }
3312  if (mux == 1u) {
3313  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 0;
3314  }
3315  if (mux == 2u) {
3316  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 0;
3317  }
3318  if (mux == 3u) {
3319  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 0;
3320  }
3321  }
3322  }
3323 
3324  if (LTC_DISCARD_MUX_CHECK == true) {
3325  muxError = STD_OK;
3326  }
3327  return muxError;
3328 }
3329 
3330 /**
3331  * @brief initialize the daisy-chain.
3332  *
3333  * To initialize the LTC6804 daisy-chain, a dummy byte (0x00) is sent.
3334  *
3335  * @param pSpiInterface pointer to SPI configuration
3336  * @param pTxBuff transmit buffer
3337  * @param pRxBuff receive buffer
3338  * @param frameLength number of words to transmit
3339  *
3340  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3341  *
3342  */
3344  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3345  uint16_t *pTxBuff,
3346  uint16_t *pRxBuff,
3347  uint32_t frameLength) {
3348  FAS_ASSERT(pSpiInterface != NULL_PTR);
3349  FAS_ASSERT(pTxBuff != NULL_PTR);
3350  FAS_ASSERT(pRxBuff != NULL_PTR);
3351  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3352 
3353  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES];
3354  uint16_t PEC_result = 0;
3355 
3356  /* now construct the message to be sent: it contains the wanted data, PLUS the needed PECs */
3357  pTxBuff[0] = ltc_cmdWRCFG[0];
3358  pTxBuff[1] = ltc_cmdWRCFG[1];
3359  pTxBuff[2] = ltc_cmdWRCFG[2];
3360  pTxBuff[3] = ltc_cmdWRCFG[3];
3361 
3362  /* set REFON bit to 1 */
3363  /* data for the configuration */
3364  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3365  /* FC = disable all pull-downs, REFON = 1, DTEN = 0, ADCOPT = 0 */
3366  pTxBuff[4u + (i * 8u)] = 0xFC;
3367  pTxBuff[5u + (i * 8u)] = 0x00;
3368  pTxBuff[6u + (i * 8u)] = 0x00;
3369  pTxBuff[7u + (i * 8u)] = 0x00;
3370  pTxBuff[8u + (i * 8u)] = 0x00;
3371  pTxBuff[9u + (i * 8u)] = 0x00;
3372 
3373  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3374  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3375  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3376  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3377  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3378  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3379 
3380  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3381  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3382  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3383  } /* end for */
3384 
3385  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3386 
3387  return retVal;
3388 }
3389 
3390 /**
3391  * @brief sets the balancing according to the control values read in the database.
3392  *
3393  * To set balancing for the cells, the corresponding bits have to be written in the configuration register.
3394  * The LTC driver only executes the balancing orders written by the BMS in the database.
3395  *
3396  * @param ltc_state state of the ltc state machine
3397  * @param pSpiInterface pointer to SPI configuration
3398  * @param pTxBuff transmit buffer
3399  * @param pRxBuff receive buffer
3400  * @param frameLength number of words to transmit
3401  * @param registerSet register Set, 0: cells 1 to 12 (WRCFG), 1: cells 13 to 15/18 (WRCFG2)
3402  * @param stringNumber string addressed
3403  *
3404  * @return STD_OK if dummy byte was sent correctly by SPI, STD_NOT_OK otherwise
3405  *
3406  */
3408  LTC_STATE_s *ltc_state,
3409  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3410  uint16_t *pTxBuff,
3411  uint16_t *pRxBuff,
3412  uint32_t frameLength,
3413  uint8_t registerSet,
3414  uint8_t stringNumber) {
3415  FAS_ASSERT(ltc_state != NULL_PTR);
3416  FAS_ASSERT(pSpiInterface != NULL_PTR);
3417  FAS_ASSERT(pTxBuff != NULL_PTR);
3418  FAS_ASSERT(pRxBuff != NULL_PTR);
3419  STD_RETURN_TYPE_e retVal = STD_OK;
3420 
3421  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES];
3422  uint16_t PEC_result = 0;
3423 
3424  LTC_GetBalancingControlValues(ltc_state);
3425 
3426  if (registerSet == 0u) { /* cells 1 to 12, WRCFG */
3427  pTxBuff[0] = ltc_cmdWRCFG[0];
3428  pTxBuff[1] = ltc_cmdWRCFG[1];
3429  pTxBuff[2] = ltc_cmdWRCFG[2];
3430  pTxBuff[3] = ltc_cmdWRCFG[3];
3431  for (uint16_t j = 0; j < BS_NR_OF_MODULES_PER_STRING; j++) {
3432  /* The daisy-chain works like a shift register, so the order has to be reversed:
3433  when addressing e.g. the first module in the daisy-chain, the data will be sent last on the SPI bus and
3434  when addressing e.g. the last module in the daisy-chain, the data will be sent first on the SPI bus */
3435  const uint16_t reverseModuleNumber = BS_NR_OF_MODULES_PER_STRING - j - 1u;
3436 
3437  /* FC = disable all pull-downs, REFON = 1 (reference always on), DTEN off, ADCOPT = 0 */
3438  pTxBuff[4u + (reverseModuleNumber * 8u)] = 0xFC;
3439  pTxBuff[5u + (reverseModuleNumber * 8u)] = 0x00;
3440  pTxBuff[6u + (reverseModuleNumber * 8u)] = 0x00;
3441  pTxBuff[7u + (reverseModuleNumber * 8u)] = 0x00;
3442  pTxBuff[8u + (reverseModuleNumber * 8u)] = 0x00;
3443  pTxBuff[9u + (reverseModuleNumber * 8u)] = 0x00;
3444 
3445  if (ltc_state->ltcData.balancingControl
3446  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 0u] == 1u) {
3447  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x01u;
3448  }
3449  if (ltc_state->ltcData.balancingControl
3450  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 1u] == 1u) {
3451  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x02u;
3452  }
3453  if (ltc_state->ltcData.balancingControl
3454  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 2u] == 1u) {
3455  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x04u;
3456  }
3457  if (ltc_state->ltcData.balancingControl
3458  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 3u] == 1u) {
3459  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x08u;
3460  }
3461  if (ltc_state->ltcData.balancingControl
3462  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 4u] == 1u) {
3463  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x10u;
3464  }
3465  if (ltc_state->ltcData.balancingControl
3466  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 5u] == 1u) {
3467  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x20u;
3468  }
3469  if (ltc_state->ltcData.balancingControl
3470  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 6u] == 1u) {
3471  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x40u;
3472  }
3473  if (ltc_state->ltcData.balancingControl
3474  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 7u] == 1u) {
3475  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x80u;
3476  }
3477  if (ltc_state->ltcData.balancingControl
3478  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 8u] == 1u) {
3479  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x01u;
3480  }
3481  if (ltc_state->ltcData.balancingControl
3482  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 9u] == 1u) {
3483  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x02u;
3484  }
3485  if (ltc_state->ltcData.balancingControl
3486  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 10u] == 1u) {
3487  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x04u;
3488  }
3489  if (ltc_state->ltcData.balancingControl
3490  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 11u] == 1u) {
3491  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x08u;
3492  }
3493 
3494  PEC_Check[0] = pTxBuff[4u + (reverseModuleNumber * 8u)];
3495  PEC_Check[1] = pTxBuff[5u + (reverseModuleNumber * 8u)];
3496  PEC_Check[2] = pTxBuff[6u + (reverseModuleNumber * 8u)];
3497  PEC_Check[3] = pTxBuff[7u + (reverseModuleNumber * 8u)];
3498  PEC_Check[4] = pTxBuff[8u + (reverseModuleNumber * 8u)];
3499  PEC_Check[5] = pTxBuff[9u + (reverseModuleNumber * 8u)];
3500 
3501  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3502  pTxBuff[10u + (reverseModuleNumber * 8u)] = (PEC_result >> 8u) & 0xFFu;
3503  pTxBuff[11u + (reverseModuleNumber * 8u)] = PEC_result & 0xFFu;
3504  }
3505  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3506  } else if (registerSet == 1u) { /* cells 13 to 15/18 WRCFG2 */
3507  pTxBuff[0] = ltc_cmdWRCFG2[0];
3508  pTxBuff[1] = ltc_cmdWRCFG2[1];
3509  pTxBuff[2] = ltc_cmdWRCFG2[2];
3510  pTxBuff[3] = ltc_cmdWRCFG2[3];
3511  for (uint16_t j = 0; j < BS_NR_OF_MODULES_PER_STRING; j++) {
3512  /* The daisy-chain works like a shift register, so the order has to be reversed:
3513  when addressing e.g. the first module in the daisy-chain, the data will be sent last on the SPI bus and
3514  when addressing e.g. the last module in the daisy-chain, the data will be sent first on the SPI bus */
3515  const uint16_t reverseModuleNumber = BS_NR_OF_MODULES_PER_STRING - j - 1u;
3516 
3517  /* 0x0F = disable pull-downs on GPIO6-9 */
3518  pTxBuff[4u + (reverseModuleNumber * 8u)] = 0x0F;
3519  pTxBuff[5u + (reverseModuleNumber * 8u)] = 0x00;
3520  pTxBuff[6u + (reverseModuleNumber * 8u)] = 0x00;
3521  pTxBuff[7u + (reverseModuleNumber * 8u)] = 0x00;
3522  pTxBuff[8u + (reverseModuleNumber * 8u)] = 0x00;
3523  pTxBuff[9u + (reverseModuleNumber * 8u)] = 0x00;
3524 
3525  if (ltc_state->ltcData.balancingControl
3526  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 12u] == 1u) {
3527  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x10u;
3528  }
3529  if (ltc_state->ltcData.balancingControl
3530  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 13u] == 1u) {
3531  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x20u;
3532  }
3533  if (ltc_state->ltcData.balancingControl
3534  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 14u] == 1u) {
3535  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x40u;
3536  }
3537  if (BS_NR_OF_CELL_BLOCKS_PER_MODULE > 15u) {
3538  if (ltc_state->ltcData.balancingControl
3539  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 15u] == 1u) {
3540  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x80u;
3541  }
3542  if (ltc_state->ltcData.balancingControl
3543  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 16u] == 1u) {
3544  pTxBuff[5u + (reverseModuleNumber * 8u)] |= 0x01u;
3545  }
3546  if (ltc_state->ltcData.balancingControl
3547  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 17u] == 1u) {
3548  pTxBuff[5u + (reverseModuleNumber * 8u)] |= 0x02u;
3549  }
3550  }
3551 
3552  PEC_Check[0] = pTxBuff[4u + (reverseModuleNumber * 8u)];
3553  PEC_Check[1] = pTxBuff[5u + (reverseModuleNumber * 8u)];
3554  PEC_Check[2] = pTxBuff[6u + (reverseModuleNumber * 8u)];
3555  PEC_Check[3] = pTxBuff[7u + (reverseModuleNumber * 8u)];
3556  PEC_Check[4] = pTxBuff[8u + (reverseModuleNumber * 8u)];
3557  PEC_Check[5] = pTxBuff[9u + (reverseModuleNumber * 8u)];
3558 
3559  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3560  pTxBuff[10u + (reverseModuleNumber * 8u)] = (PEC_result >> 8u) & 0xFFu;
3561  pTxBuff[11u + (reverseModuleNumber * 8u)] = PEC_result & 0xFFu;
3562  }
3563  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3564  } else {
3565  retVal = STD_NOT_OK;
3566  }
3567  return retVal;
3568 }
3569 
3570 /**
3571  * @brief resets the error table.
3572  *
3573  * This function should be called during initialization or before starting a new measurement cycle
3574  *
3575  * @param ltc_state: state of the ltc state machine
3576  *
3577  */
3578 static void LTC_ResetErrorTable(LTC_STATE_s *ltc_state) {
3579  FAS_ASSERT(ltc_state != NULL_PTR);
3580  for (uint8_t s = 0u; s < BS_NR_OF_STRINGS; s++) {
3581  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3582  ltc_state->ltcData.errorTable->PEC_valid[s][i] = false;
3583  ltc_state->ltcData.errorTable->mux0[s][i] = 0;
3584  ltc_state->ltcData.errorTable->mux1[s][i] = 0;
3585  ltc_state->ltcData.errorTable->mux2[s][i] = 0;
3586  ltc_state->ltcData.errorTable->mux3[s][i] = 0;
3587  }
3588  }
3589 }
3590 
3591 /**
3592  * @brief brief missing
3593  * @details Gets the measurement time needed by the LTC analog front-end,
3594  * depending on the measurement mode and the number of channels.
3595  * For all cell voltages or all 5 GPIOS, the measurement time is the
3596  * same.
3597  * For 2 cell voltages or 1 GPIO, the measurement time is the same.
3598  * As a consequence, this function is used for cell voltage and for
3599  * GPIO measurement.
3600  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3601  * @param adcMeasCh number of channels measured for GPIOS (one at a time
3602  * for multiplexers or all five GPIOs) or number of cell
3603  * voltage measured (2 cells or all cells)
3604  * @return measurement time in ms
3605  */
3606 static uint16_t LTC_GetMeasurementTimeCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh) {
3607  uint16_t retVal = LTC_ADCMEAS_UNDEFINED; /* default */
3608 
3609  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_CELLS) {
3610  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3612  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3614  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3616  }
3617  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
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_ALLCHANNEL_GPIOS) {
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 (
3634  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) ||
3635  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO4) ||
3636  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO5)) {
3637  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3639  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3641  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3643  }
3644  } else {
3645  retVal = LTC_ADCMEAS_UNDEFINED;
3646  }
3647 
3648  return retVal;
3649 }
3650 
3651 /**
3652  * @brief tells the LTC daisy-chain to start measuring the voltage on all cells.
3653  *
3654  * This function sends an instruction to the daisy-chain via SPI, in order to start voltage measurement for all cells.
3655  *
3656  * @param pSpiInterface pointer to SPI configuration
3657  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3658  * @param adcMeasCh number of cell voltage measured (2 cells or all cells)
3659  *
3660  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3661  *
3662  */
3664  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3665  LTC_ADCMODE_e adcMode,
3666  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3667  FAS_ASSERT(pSpiInterface != NULL_PTR);
3668  STD_RETURN_TYPE_e retVal = STD_OK;
3669 
3670  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_CELLS) {
3671  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3672  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_fast_DCP0);
3673  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3674  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_normal_DCP0);
3675  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3676  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_filtered_DCP0);
3677  } else if (adcMode == LTC_ADCMODE_FAST_DCP1) {
3678  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_fast_DCP1);
3679  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP1) {
3680  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_normal_DCP1);
3681  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP1) {
3682  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_filtered_DCP1);
3683  } else {
3684  retVal = STD_NOT_OK;
3685  }
3686  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
3687  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3688  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADCV_fast_DCP0_twocells);
3689  } else {
3690  retVal = STD_NOT_OK;
3691  }
3692  } else {
3693  retVal = STD_NOT_OK;
3694  }
3695  return retVal;
3696 }
3697 
3698 /**
3699  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3700  * @details This function sends an instruction to the daisy-chain via SPI to
3701  * start the measurement.
3702  *
3703  * @param pSpiInterface pointer to SPI configuration
3704  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3705  * @param adcMeasCh number of channels measured for GPIOS (one at a
3706  * time, typically when multiplexers are used, or all
3707  * five GPIOs)
3708  *
3709  * @return #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK
3710  * otherwise
3711  */
3713  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3714  LTC_ADCMODE_e adcMode,
3715  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3716  FAS_ASSERT(pSpiInterface != NULL_PTR);
3717  STD_RETURN_TYPE_e retVal;
3718 
3719  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_GPIOS) {
3720  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3721  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_ALLGPIOS);
3722  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3723  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_ALLGPIOS);
3724  } else {
3725  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3726  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_ALLGPIOS);
3727  }
3728  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) {
3729  /* Single Channel */
3730  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3731  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_GPIO1);
3732  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3733  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_GPIO1);
3734  } else {
3735  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3736 
3737  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_GPIO1);
3738  }
3739  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) {
3740  /* Single Channel */
3741  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3742  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_GPIO2);
3743  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3744  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_GPIO2);
3745  } else {
3746  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3747 
3748  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_GPIO2);
3749  }
3750  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) {
3751  /* Single Channel */
3752  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3753  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_fast_GPIO3);
3754  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3755  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_filtered_GPIO3);
3756  } else {
3757  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3758 
3759  retVal = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_cmdADAX_normal_GPIO3);
3760  }
3761  } else {
3762  retVal = STD_NOT_OK;
3763  }
3764 
3765  return retVal;
3766 }
3767 
3768 /**
3769  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3770  *
3771  * This function sends an instruction to the daisy-chain via SPI to start the measurement.
3772  *
3773  * @param pSpiInterface pointer to SPI configuration
3774  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3775  * @param PUP pull-up bit for pull-up or pull-down current (0: pull-down, 1: pull-up)
3776  *
3777  * @return retVal #STD_OK if command was sent correctly by SPI, #STD_NOT_OK otherwise
3778  *
3779  */
3781  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3782  LTC_ADCMODE_e adcMode,
3783  uint8_t PUP) {
3784  FAS_ASSERT(pSpiInterface != NULL_PTR);
3785  STD_RETURN_TYPE_e retval = STD_NOT_OK;
3786  if (PUP == 0u) {
3787  /* pull-down current */
3788  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3789  retval = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_BC_cmdADOW_PDOWN_normal_DCP0);
3790  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3792  } else {
3793  retval = STD_NOT_OK;
3794  }
3795  } else if (PUP == 1u) {
3796  /* pull-up current */
3797  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3798  retval = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_BC_cmdADOW_PUP_normal_DCP0);
3799  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3800  retval = LTC_TRANSMIT_COMMAND(pSpiInterface, ltc_BC_cmdADOW_PUP_filtered_DCP0);
3801  } else {
3802  retval = STD_NOT_OK;
3803  }
3804  }
3805  return retval;
3806 }
3807 
3808 /**
3809  * @brief checks if the data received from the daisy-chain is not corrupt.
3810  *
3811  * This function computes the PEC (CRC) from the data received by the daisy-chain.
3812  * It compares it with the PEC sent by the LTCs.
3813  * If there are errors, the array LTC_ErrorTable is updated to locate the LTCs in daisy-chain
3814  * that transmitted corrupt data.
3815  *
3816  * @param ltc_state state of the ltc state machine
3817  * @param DataBufferSPI_RX_with_PEC data obtained from the SPI transmission
3818  * @param stringNumber string addressed
3819  *
3820  * @return retVal STD_OK if PEC check is OK, STD_NOT_OK otherwise
3821  *
3822  */
3824  LTC_STATE_s *ltc_state,
3825  uint16_t *DataBufferSPI_RX_with_PEC,
3826  uint8_t stringNumber) {
3827  FAS_ASSERT(ltc_state != NULL_PTR);
3828  FAS_ASSERT(DataBufferSPI_RX_with_PEC != NULL_PTR);
3829  STD_RETURN_TYPE_e retVal = STD_OK;
3830  uint8_t PEC_TX[2];
3831  uint16_t PEC_result = 0;
3832  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3833 
3834  /* check all PECs and put data without command and PEC in DataBufferSPI_RX (easier to use) */
3835  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3836  PEC_Check[0] = DataBufferSPI_RX_with_PEC[4u + (i * 8u)];
3837  PEC_Check[1] = DataBufferSPI_RX_with_PEC[5u + (i * 8u)];
3838  PEC_Check[2] = DataBufferSPI_RX_with_PEC[6u + (i * 8u)];
3839  PEC_Check[3] = DataBufferSPI_RX_with_PEC[7u + (i * 8u)];
3840  PEC_Check[4] = DataBufferSPI_RX_with_PEC[8u + (i * 8u)];
3841  PEC_Check[5] = DataBufferSPI_RX_with_PEC[9u + (i * 8u)];
3842 
3843  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3844  PEC_TX[0] = (uint8_t)((PEC_result >> 8u) & 0xFFu);
3845  PEC_TX[1] = (uint8_t)(PEC_result & 0xFFu);
3846 
3847  /* if calculated PEC not equal to received PEC */
3848  if ((PEC_TX[0] != DataBufferSPI_RX_with_PEC[10u + (i * 8u)]) ||
3849  (PEC_TX[1] != DataBufferSPI_RX_with_PEC[11u + (i * 8u)])) {
3850  /* update error table of the corresponding LTC only if PEC check is activated */
3851  if (LTC_DISCARD_PEC == false) {
3852  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = false;
3853  retVal = STD_NOT_OK;
3854  } else {
3855  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3856  }
3857  } else {
3858  /* update error table of the corresponding LTC */
3859  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3860  }
3861  }
3862  return retVal;
3863 }
3864 
3865 /**
3866  * @brief send command to the LTC daisy-chain and receives data from the LTC
3867  * daisy-chain.
3868  * @details This is the core function to receive data from the LTC6813-1
3869  * daisy-chain.
3870  * A 2 byte command is sent with the corresponding PEC.
3871  * *Example*: read configuration register (RDCFG).
3872  * Only command has to be set, the function calculates the PEC
3873  * automatically.
3874  * - The data sent is:
3875  * - 2 bytes (COMMAND) 2 bytes (PEC)
3876  * - The data received is:
3877  * - 6 bytes (LTC1) 2 bytes (PEC) +
3878  * - 6 bytes (LTC2) 2 bytes (PEC) +
3879  * - 6 bytes (LTC3) 2 bytes (PEC) +
3880  * - ... +
3881  * - 6 bytes (LTC{LTC_N_LTC}) 2 bytes (PEC)
3882  *
3883  * The function does not check the PECs. This has to be done
3884  * elsewhere.
3885  *
3886  * @param Command command sent to the daisy-chain
3887  * @param pSpiInterface pointer to SPI configuration
3888  * @param pTxBuff transmit buffer
3889  * @param pRxBuff receive buffer
3890  * @param frameLength number of words to transmit
3891  *
3892  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
3893  */
3895  uint16_t *Command,
3896  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3897  uint16_t *pTxBuff,
3898  uint16_t *pRxBuff,
3899  uint32_t frameLength) {
3900  FAS_ASSERT(Command != NULL_PTR);
3901  FAS_ASSERT(pSpiInterface != NULL_PTR);
3902  FAS_ASSERT(pTxBuff != NULL_PTR);
3903  FAS_ASSERT(pRxBuff != NULL_PTR);
3904  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3905 
3906  /* DataBufferSPI_RX_with_PEC contains the data to receive.
3907  The transmission function checks the PECs.
3908  It constructs DataBufferSPI_RX, which contains the received data without PEC (easier to use). */
3909 
3910  for (uint16_t i = 0; i < LTC_N_BYTES_FOR_DATA_TRANSMISSION; i++) {
3911  pTxBuff[i] = 0x00;
3912  }
3913 
3914  pTxBuff[0] = Command[0];
3915  pTxBuff[1] = Command[1];
3916  pTxBuff[2] = Command[2];
3917  pTxBuff[3] = Command[3];
3918 
3919  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3920 
3921  return retVal;
3922 }
3923 
3924 /**
3925  * @brief sends command and data to the LTC daisy-chain.
3926  * @details This is the core function to transmit data to the LTC6813-1
3927  * daisy-chain.
3928  * The data sent is:
3929  * - COMMAND +
3930  * - 6 bytes (LTC1) +
3931  * - 6 bytes (LTC2) +
3932  * - 6 bytes (LTC3) +
3933  * - ... +
3934  * - 6 bytes (LTC{LTC_N_LTC})
3935  *
3936  * A 2 byte command is sent with the corresponding PEC.
3937  * *Example*: write configuration register (WRCFG).
3938  * The command has to be set and then the function calculates the PEC
3939  * automatically.
3940  * The function calculates the needed PEC to send the data to the
3941  * daisy-chain.
3942  * The sent data has the format:
3943  * - 2 byte-COMMAND (2 bytes PEC) +
3944  * - 6 bytes (LTC1) (2 bytes PEC) +
3945  * - 6 bytes (LTC2) (2 bytes PEC) +
3946  * - 6 bytes (LTC3) (2 bytes PEC) +
3947  * - ... +
3948  * - 6 bytes (LTC{LTC_N_LTC}) (2 bytes PEC)
3949  *
3950  * The function returns 0. The only way to check if the transmission
3951  * was successful is to read the results of the write operation.
3952  * (example: read configuration register after writing to it)
3953  *
3954  * @param Command command sent to the daisy-chain
3955  * @param pSpiInterface pointer to SPI configuration
3956  * @param pTxBuff transmit buffer
3957  * @param pRxBuff receive buffer
3958  * @param frameLength number of words to transmit
3959  *
3960  * @return STD_OK if SPI transmission is OK, STD_NOT_OK otherwise
3961  */
3963  uint16_t *Command,
3964  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3965  uint16_t *pTxBuff,
3966  uint16_t *pRxBuff,
3967  uint32_t frameLength) {
3968  FAS_ASSERT(Command != NULL_PTR);
3969  FAS_ASSERT(pSpiInterface != NULL_PTR);
3970  FAS_ASSERT(pTxBuff != NULL_PTR);
3971  FAS_ASSERT(pRxBuff != NULL_PTR);
3972  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3973 
3974  uint16_t PEC_result = 0;
3975  uint8_t PEC_Check[LTC_DATA_SIZE_IN_BYTES] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3976 
3977  pTxBuff[0] = Command[0];
3978  pTxBuff[1] = Command[1];
3979  pTxBuff[2] = Command[2];
3980  pTxBuff[3] = Command[3];
3981 
3982  /* Calculate PEC of all data (1 PEC value for 6 bytes) */
3983  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3984  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3985  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3986  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3987  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3988  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3989  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3990 
3991  PEC_result = LTC_CalculatePec15(LTC_DATA_SIZE_IN_BYTES, PEC_Check);
3992  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3993  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3994  }
3995 
3996  retVal = LTC_TRANSMIT_RECEIVE_DATA(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3997 
3998  return retVal;
3999 }
4000 
4001 /**
4002  * @brief configures the data that will be sent to the LTC daisy-chain to configure multiplexer channels.
4003  *
4004  * This function does not sent the data to the multiplexer daisy-chain. This is done
4005  * by the function LTC_SetMuxChannel(), which calls LTC_SetMuxChCommand()..
4006  *
4007  * @param pTxBuff transmit buffer
4008  * @param mux multiplexer ID to be configured (0,1,2 or 3)
4009  * @param channel multiplexer channel to be configured (0 to 7)
4010  *
4011  */
4012 static void LTC_SetMuxChCommand(uint16_t *pTxBuff, uint8_t mux, uint8_t channel) {
4013  FAS_ASSERT(pTxBuff != NULL_PTR);
4014  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4015 #if SLAVE_BOARD_VERSION == 2u
4016 
4017  /* using ADG728 */
4018  uint8_t address = 0x98u | ((mux % 4u) << 1u);
4019  uint8_t data = 1u << (channel % 8u);
4020  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
4021  data = 0x00;
4022  }
4023 
4024 #else
4025 
4026  /* using LTC1380 */
4027  uint8_t address = 0x90u | ((mux % 4u) << 1u);
4028  uint8_t data = 0x08u | (channel % 8u);
4029  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
4030  data = 0x00;
4031  }
4032 
4033 #endif
4034 
4035  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | ((address >> 4u) & 0x0Fu); /* 0x6 : LTC6804: ICOM START from Master */
4036  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | ((address << 4u) & 0xF0u);
4037  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | ((data >> 4u) & 0x0Fu);
4038  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | ((data << 4u) & 0xF0u);
4039  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 0x1 : ICOM-STOP */
4040  pTxBuff[9u + (i * 8u)] = 0x00; /* 0x0 : dummy (Dn) */
4041  /* 9: MASTER NACK + STOP (FCOM) */
4042  }
4043 }
4044 
4045 /**
4046  * @brief sends data to the LTC daisy-chain to read EEPROM on slaves.
4047  *
4048  * @param ltc_state state of the ltc state machine
4049  * @param pSpiInterface pointer to SPI configuration
4050  * @param pTxBuff transmit buffer
4051  * @param pRxBuff receive buffer
4052  * @param frameLength number of words to transmit
4053  * @param step first or second stage of read process (0 or 1)
4054  *
4055  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4056  */
4058  LTC_STATE_s *ltc_state,
4059  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4060  uint16_t *pTxBuff,
4061  uint16_t *pRxBuff,
4062  uint32_t frameLength,
4063  uint8_t step) {
4064  FAS_ASSERT(ltc_state != NULL_PTR);
4065  FAS_ASSERT(pSpiInterface != NULL_PTR);
4066  FAS_ASSERT(pTxBuff != NULL_PTR);
4067  FAS_ASSERT(pRxBuff != NULL_PTR);
4068  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4069 
4070  /* send WRCOMM to send I2C message to choose channel */
4071  LTC_SetEepromReadCommand(ltc_state, pTxBuff, step);
4072  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4073 
4074  return statusSPI;
4075 }
4076 
4077 /**
4078  * @brief configures the data that will be sent to the LTC daisy-chain to read EEPROM on slaves.
4079  *
4080  * @param ltc_state state of the ltc state machine
4081  * @param pTxBuff transmit buffer
4082  * @param step first or second stage of read process (0 or 1)
4083  *
4084  */
4085 static void LTC_SetEepromReadCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4086  FAS_ASSERT(ltc_state != NULL_PTR);
4087  FAS_ASSERT(pTxBuff != NULL_PTR);
4088  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4089 
4090  uint32_t address = ltc_state->ltcData.slaveControl->eepromReadAddressToUse;
4091 
4092  address &= 0x3FFFFu;
4093  const uint8_t address0 = address >> 16u;
4094  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4095  const uint8_t address2 = address & 0xFFu;
4096 
4097  if (step == 0u) {
4098  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4099  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4100  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4101  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4102  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4103  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4104  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4105  }
4106  } else { /* step == 1 */
4107  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4108  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4109  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x10u);
4110  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | 0x0Fu;
4111  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0xF0u;
4112  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4113  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4114  }
4115  }
4116 }
4117 
4118 /**
4119  * @brief saves the read values of the external EEPROMs read from the LTC daisy-chain.
4120  *
4121  * @param ltc_state state of the ltc state machine
4122  * @param pRxBuff receive buffer
4123  *
4124  */
4125 static void LTC_EepromSaveReadValue(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4126  FAS_ASSERT(ltc_state != NULL_PTR);
4127  FAS_ASSERT(pRxBuff != NULL_PTR);
4128  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4129 
4130  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4131  ltc_state->ltcData.slaveControl->eepromValueRead[i] = (pRxBuff[6u + (i * 8u)] << 4u) |
4132  ((pRxBuff[7u + (i * 8u)] >> 4u));
4133  }
4134 
4137  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
4138 
4139  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4140 }
4141 
4142 /**
4143  * @brief sends data to the LTC daisy-chain to write EEPROM on slaves.
4144  *
4145  * @param ltc_state state of the ltc state machine
4146  * @param pSpiInterface pointer to SPI configuration
4147  * @param pTxBuff transmit buffer
4148  * @param pRxBuff receive buffer
4149  * @param frameLength number of words to transmit
4150  * @param step first or second stage of read process (0 or 1)
4151  *
4152  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4153  */
4155  LTC_STATE_s *ltc_state,
4156  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4157  uint16_t *pTxBuff,
4158  uint16_t *pRxBuff,
4159  uint32_t frameLength,
4160  uint8_t step) {
4161  FAS_ASSERT(ltc_state != NULL_PTR);
4162  FAS_ASSERT(pSpiInterface != NULL_PTR);
4163  FAS_ASSERT(pTxBuff != NULL_PTR);
4164  FAS_ASSERT(pRxBuff != NULL_PTR);
4165  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4166 
4167  /* send WRCOMM to send I2C message to write EEPROM */
4168  LTC_SetEepromWriteCommand(ltc_state, pTxBuff, step);
4169  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4170 
4171  return statusSPI;
4172 }
4173 
4174 /**
4175  * @brief configures the data that will be sent to the LTC daisy-chain to write EEPROM on slaves.
4176  *
4177  * @param ltc_state state of the ltc state machine
4178  * @param pTxBuff transmit buffer
4179  * @param step first or second stage of read process (0 or 1)
4180  *
4181  */
4182 static void LTC_SetEepromWriteCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4183  FAS_ASSERT(ltc_state != NULL_PTR);
4184  FAS_ASSERT(pTxBuff != NULL_PTR);
4185  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4186 
4187  uint32_t address = ltc_state->ltcData.slaveControl->eepromWriteAddressToUse;
4188 
4189  address &= 0x3FFFFu;
4190  const uint8_t address0 = address >> 16u;
4191  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4192  const uint8_t address2 = address & 0xFFu;
4193 
4194  if (step == 0u) {
4195  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4196  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4197  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4198  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4199  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4200  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4201  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4202  }
4203  } else { /* step == 1 */
4204  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4205  const uint8_t data = ltc_state->ltcData.slaveControl->eepromValueWrite[i];
4206 
4207  pTxBuff[4u + (i * 8u)] = LTC_ICOM_BLANK | (data >> 4u); /* 0x6 : LTC6804: ICOM START from Master */
4208  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | (data << 4u);
4209  pTxBuff[6u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4210  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4211  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4212  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4213  }
4214 
4217  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
4218 
4219  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4220  }
4221 }
4222 
4223 /**
4224  * @brief sends data to the LTC daisy-chain to configure multiplexer channels.
4225  *
4226  * This function calls the function LTC_SetMuxChCommand() to set the data.
4227  *
4228  * @param pSpiInterface pointer to SPI configuration
4229  * @param pTxBuff transmit buffer
4230  * @param pRxBuff receive buffer
4231  * @param frameLength number of words to transmit
4232  * @param mux multiplexer ID to be configured (0,1,2 or 3)
4233  * @param channel multiplexer channel to be configured (0 to 7)
4234  *
4235  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4236  */
4238  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4239  uint16_t *pTxBuff,
4240  uint16_t *pRxBuff,
4241  uint32_t frameLength,
4242  uint8_t mux,
4243  uint8_t channel) {
4244  FAS_ASSERT(pSpiInterface != NULL_PTR);
4245  FAS_ASSERT(pTxBuff != NULL_PTR);
4246  FAS_ASSERT(pRxBuff != NULL_PTR);
4247 
4248  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4249 
4250  /* send WRCOMM to send I2C message to choose channel */
4251  LTC_SetMuxChCommand(pTxBuff, mux, channel);
4252  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4253 
4254  return statusSPI;
4255 }
4256 
4257 /**
4258  * @brief sends data to the LTC daisy-chain to communicate via I2C
4259  *
4260  * This function initiates an I2C signal sent by the LTC6804 on the slave boards
4261  *
4262  * @param pSpiInterface pointer to SPI configuration
4263  * @param pTxBuff transmit buffer
4264  * @param pRxBuff receive buffer
4265  * @param frameLength number of words to transmit
4266  * @param cmd_data command data to be sent
4267  *
4268  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4269  */
4271  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4272  uint16_t *pTxBuff,
4273  uint16_t *pRxBuff,
4274  uint32_t frameLength,
4275  uint16_t *cmd_data) {
4276  FAS_ASSERT(pSpiInterface != NULL_PTR);
4277  FAS_ASSERT(pTxBuff != NULL_PTR);
4278  FAS_ASSERT(pRxBuff != NULL_PTR);
4279  FAS_ASSERT(cmd_data != NULL_PTR);
4280  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4281 
4282  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4283  pTxBuff[4u + (i * 6u)] = cmd_data[0];
4284  pTxBuff[5u + (i * 6u)] = cmd_data[1];
4285 
4286  pTxBuff[6u + (i * 6u)] = cmd_data[2];
4287  pTxBuff[7u + (i * 6u)] = cmd_data[3];
4288 
4289  pTxBuff[8u + (i * 6u)] = cmd_data[4];
4290  pTxBuff[9u + (i * 6u)] = cmd_data[5];
4291  }
4292 
4293  /* send WRCOMM to send I2C message */
4294  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4295 
4296  return statusSPI;
4297 }
4298 
4299 /**
4300  * @brief saves the temperature value of the external temperature sensors read from the LTC daisy-chain.
4301  *
4302  * This function saves the temperature value received from the external temperature sensors
4303  *
4304  * @param ltc_state state of the ltc state machine
4305  * @param pRxBuff receive buffer
4306  *
4307  */
4308 static void LTC_TempSensSaveTemp(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4309  FAS_ASSERT(ltc_state != NULL_PTR);
4310  FAS_ASSERT(pRxBuff != NULL_PTR);
4311  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4312 
4313  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4314  uint8_t temp_tmp[2];
4315  temp_tmp[0] = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4316  temp_tmp[1] = (pRxBuff[8u + (i * 8u)] << 4u) | ((pRxBuff[9u + (i * 8u)] >> 4u));
4317  uint16_t val_i = (temp_tmp[0] << 8u) | (temp_tmp[1]);
4318  val_i = val_i >> 8u;
4319  ltc_state->ltcData.slaveControl->externalTemperatureSensor[i] = val_i;
4320  }
4321 
4322  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4323 }
4324 
4325 /**
4326  * @brief sends data to the LTC daisy-chain to control the user port expander
4327  *
4328  * This function sends a control byte to the register of the user port expander
4329  *
4330  * @param ltc_state state of the ltc state machine
4331  * @param pSpiInterface pointer to SPI configuration
4332  * @param pTxBuff transmit buffer
4333  * @param pRxBuff receive buffer
4334  * @param frameLength number of words to transmit
4335  *
4336  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4337  */
4339  LTC_STATE_s *ltc_state,
4340  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4341  uint16_t *pTxBuff,
4342  uint16_t *pRxBuff,
4343  uint32_t frameLength) {
4344  FAS_ASSERT(ltc_state != NULL_PTR);
4345  FAS_ASSERT(pSpiInterface != NULL_PTR);
4346  FAS_ASSERT(pTxBuff != NULL_PTR);
4347  FAS_ASSERT(pRxBuff != NULL_PTR);
4348 
4349  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4350 
4351  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4352 
4353  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4354  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES_PER_STRING - 1 - i];
4355 
4356  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START |
4357  0x04u; /* 6: ICOM0 start condition, 4: upper nibble of PCA8574 address */
4358  pTxBuff[5u + (i * 8u)] =
4359  0u | LTC_FCOM_MASTER_NACK; /* 0: lower nibble of PCA8574 address + R/W bit, 8: FCOM0 master NACK */
4360 
4361  pTxBuff[6u + (i * 8u)] =
4362  LTC_ICOM_BLANK |
4363  (output_data >> 4u); /* 0: ICOM1 blank, x: upper nibble of PCA8574 data register (0 == pin low) */
4364  pTxBuff[7u + (i * 8u)] =
4365  (uint8_t)(output_data << 4u) |
4366  LTC_FCOM_MASTER_NACK_STOP; /* x: lower nibble of PCA8574 data register, 9: FCOM1 master NACK + STOP */
4367 
4368  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 7: no transmission, F: dummy data */
4369  pTxBuff[9u + (i * 8u)] = 0; /* F: dummy data, 9: FCOM2 master NACK + STOP */
4370  }
4371 
4372  /* send WRCOMM to send I2C message */
4373  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4374 
4375  return statusSPI;
4376 }
4377 
4378 /**
4379  * @brief saves the received values of the external port expander read from the LTC daisy-chain.
4380  *
4381  * This function saves the received data byte from the external port expander
4382  *
4383  * @param ltc_state state of the ltc state machine
4384  * @param pRxBuff receive buffer
4385  *
4386  */
4387 static void LTC_PortExpanderSaveValues(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4388  FAS_ASSERT(ltc_state != NULL_PTR);
4389  FAS_ASSERT(pRxBuff != NULL_PTR);
4390  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4391 
4392  /* extract data */
4393  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4394  const uint8_t val_i = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4395  ltc_state->ltcData.slaveControl->ioValueIn[i] = val_i;
4396  }
4397 
4398  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4399 }
4400 
4401 /**
4402  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4403  *
4404  * This function sends a control byte to the register of the user port expander from TI
4405  *
4406  * @param ltc_state state of the ltc state machine
4407  * @param pSpiInterface pointer to SPI configuration
4408  * @param pTxBuff transmit buffer
4409  * @param pRxBuff receive buffer
4410  * @param frameLength number of words to transmit
4411  * @param direction use port expander pin as input or output
4412  *
4413  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4414  */
4416  LTC_STATE_s *ltc_state,
4417  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4418  uint16_t *pTxBuff,
4419  uint16_t *pRxBuff,
4420  uint32_t frameLength,
4422  FAS_ASSERT(ltc_state != NULL_PTR);
4423  FAS_ASSERT(pSpiInterface != NULL_PTR);
4424  FAS_ASSERT(pTxBuff != NULL_PTR);
4425  FAS_ASSERT(pRxBuff != NULL_PTR);
4426  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4427 
4428  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4429 
4430  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4431  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /*upper nibble of TCA6408A address */
4432  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4433  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4434 
4435  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR >>
4436  4u); /* upper nibble of TCA6408A configuration register address */
4437  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR << 4u) |
4438  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A configuration register address */
4439 
4440  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK |
4441  (direction >> 4u); /* upper nibble of TCA6408A configuration register data */
4442  pTxBuff[9u + (i * 8u)] = (uint8_t)(direction << 4u) |
4443  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A configuration register data */
4444  }
4445 
4446  /* send WRCOMM to send I2C message */
4447  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4448 
4449  return statusSPI;
4450 }
4451 
4452 /**
4453  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4454  *
4455  * This function sends a control byte to the register of the user port expander from TI
4456  *
4457  * @param ltc_state state of the ltc state machine
4458  * @param pSpiInterface pointer to SPI configuration
4459  * @param pTxBuff transmit buffer
4460  * @param pRxBuff receive buffer
4461  * @param frameLength number of words to transmit
4462  *
4463  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4464  */
4466  LTC_STATE_s *ltc_state,
4467  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4468  uint16_t *pTxBuff,
4469  uint16_t *pRxBuff,
4470  uint32_t frameLength) {
4471  FAS_ASSERT(ltc_state != NULL_PTR);
4472  FAS_ASSERT(pSpiInterface != NULL_PTR);
4473  FAS_ASSERT(pTxBuff != NULL_PTR);
4474  FAS_ASSERT(pRxBuff != NULL_PTR);
4475  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4476 
4477  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4478 
4479  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4480  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES_PER_STRING - 1 - i];
4481 
4482  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4483  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4484  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4485 
4486  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR >>
4487  4u); /* upper nibble of TCA6408A output register address */
4488  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR << 4u) |
4489  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A output register address */
4490 
4491  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (output_data >> 4u); /* upper nibble of TCA6408A output register */
4492  pTxBuff[9u + (i * 8u)] = (uint8_t)(output_data << 4u) |
4493  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A output register */
4494  }
4495 
4496  /* send WRCOMM to send I2C message */
4497  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4498 
4499  return statusSPI;
4500 }
4501 
4502 /**
4503  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4504  *
4505  * @details This function sends a control byte to the register of the user port expander from TI
4506  *
4507  * @param ltc_state state of the ltc state machine
4508  * @param pSpiInterface pointer to SPI configuration
4509  * @param pTxBuff transmit buffer
4510  * @param pRxBuff receive buffer
4511  * @param frameLength number of words to transmit
4512  * @param step first or second stage of read process (0 or 1)
4513  *
4514  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4515  */
4517  LTC_STATE_s *ltc_state,
4518  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4519  uint16_t *pTxBuff,
4520  uint16_t *pRxBuff,
4521  uint32_t frameLength,
4522  uint8_t step) {
4523  FAS_ASSERT(ltc_state != NULL_PTR);
4524  FAS_ASSERT(pSpiInterface != NULL_PTR);
4525  FAS_ASSERT(pTxBuff != NULL_PTR);
4526  FAS_ASSERT(pRxBuff != NULL_PTR);
4527  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4528 
4529  if (step == 0u) {
4530  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4531  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4532  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4533  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A address + R/W bit */
4534 
4535  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_INPUT_REG_ADR >>
4536  4u); /* upper nibble of TCA6408A input register address */
4537  pTxBuff[7u + (i * 8u)] = (uint8_t)(