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