foxBMS  1.3.0
The foxBMS Battery Management System 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-05-30 (date of last update)
47  * @version v1.3.0
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 statemachine will transition into ok or nok states
537  * @param diagCode: symbolic IDs for diagnosis entry, called with #DIAG_EVENT_OK if retVal is #STD_OK, #DIAG_EVENT_NOT_OK otherwise
538  * @param state_ok state to transition into if retVal is #STD_OK
539  * @param substate_ok: substate to transition into if retVal is #STD_OK
540  * @param timer_ms_ok: transition into state_ok, substate_ok after timer_ms_ok elapsed
541  * @param state_nok: state to transition into if retVal is #STD_NOT_OK
542  * @param substate_nok: substate to transition into if retVal is #STD_NOT_OK
543  * @param timer_ms_nok: transition into state_nok, substate_nok after timer_ms_nok elapsed
544  */
546  LTC_STATE_s *ltc_state,
547  STD_RETURN_TYPE_e retVal,
548  DIAG_ID_e diagCode,
549  LTC_STATEMACH_e state_ok,
550  uint8_t substate_ok,
551  uint16_t timer_ms_ok,
552  LTC_STATEMACH_e state_nok,
553  uint8_t substate_nok,
554  uint16_t timer_ms_nok) {
555  if ((retVal != STD_OK)) {
557  LTC_StateTransition(ltc_state, state_nok, substate_nok, timer_ms_nok);
558  } else {
559  DIAG_Handler(diagCode, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
560  LTC_StateTransition(ltc_state, state_ok, substate_ok, timer_ms_ok);
561  }
562 }
563 
564 extern void LTC_SaveVoltages(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
565  /* Pointer validity check */
566  FAS_ASSERT(ltc_state != NULL_PTR);
567  FAS_ASSERT(stringNumber < BS_NR_OF_STRINGS);
568 
569  /* Iterate over all cell to:
570  *
571  * 1. Check open-wires and set respective cell measurements to invalid
572  * 2. Perform minimum/maximum measurement value plausibility check
573  * 3. Calculate string values
574  */
575  STD_RETURN_TYPE_e cellVoltageMeasurementValid = STD_OK;
576  int32_t stringVoltage_mV = 0;
577  uint16_t numberValidMeasurements = 0;
578  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
579  for (uint8_t c = 0u; c < BS_NR_OF_CELL_BLOCKS_PER_MODULE; c++) {
580  /* ------- 1. Check open-wires -----------------
581  * Is cell N input not open wire &&
582  * Is cell N+1 input not open wire &&
583  * Is cell voltage valid because of previous PEC error
584  * If so, everything okay, else set cell voltage measurement to invalid.
585  */
586  if ((ltc_state->ltcData.openWire
587  ->openwire[stringNumber][(m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1u)) + c] == 0u) &&
588  (ltc_state->ltcData.openWire
589  ->openwire[stringNumber][(m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE + 1u)) + c + 1u] == 0u) &&
590  ((ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] & (0x01u << c)) == 0u)) {
591  /* Cell voltage is valid -> perform minimum/maximum plausibility check */
592 
593  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
595  ltc_state->ltcData.cellVoltage
596  ->cellVoltage_mV[stringNumber][(m * BS_NR_OF_CELL_BLOCKS_PER_MODULE) + c],
598  /* Cell voltage is valid -> calculate string voltage */
599  /* -------- 3. Calculate string values ------------- */
600  stringVoltage_mV += ltc_state->ltcData.cellVoltage
601  ->cellVoltage_mV[stringNumber][(m * BS_NR_OF_CELL_BLOCKS_PER_MODULE) + c];
602  numberValidMeasurements++;
603  } else {
604  /* Invalidate cell voltage measurement */
605  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << c);
606  cellVoltageMeasurementValid = STD_NOT_OK;
607  }
608  } else {
609  /* Set cell voltage measurement value invalid, if not already invalid because of PEC Error */
610  ltc_state->ltcData.cellVoltage->invalidCellVoltage[stringNumber][m] |= (0x01u << c);
611  cellVoltageMeasurementValid = STD_NOT_OK;
612  }
613  }
614  }
615  DIAG_CheckEvent(cellVoltageMeasurementValid, ltc_state->voltMeasDiagErrorEntry, DIAG_STRING, stringNumber);
616  ltc_state->ltcData.cellVoltage->packVoltage_mV[stringNumber] = stringVoltage_mV;
617  ltc_state->ltcData.cellVoltage->nrValidCellVoltages[stringNumber] = numberValidMeasurements;
618 
619  /* Increment state variable each time new values are written into database */
620  ltc_state->ltcData.cellVoltage->state++;
621 
622  DATA_WRITE_DATA(ltc_state->ltcData.cellVoltage);
623 }
624 
625 /*========== Extern Function Implementations ================================*/
626 extern void LTC_SaveTemperatures(LTC_STATE_s *ltc_state, uint8_t stringNumber) {
627  STD_RETURN_TYPE_e cellTemperatureMeasurementValid = STD_OK;
628  uint16_t numberValidMeasurements = 0;
629 
630  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
631  for (uint8_t c = 0u; c < BS_NR_OF_TEMP_SENSORS_PER_MODULE; c++) {
632  /* ------- 1. Check valid flag -----------------
633  * Is cell temperature valid because of previous PEC error
634  * If so, everything okay, else set cell temperature measurement to invalid.
635  */
636  if ((ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] & (0x01u << c)) == 0u) {
637  /* Cell temperature is valid -> perform minimum/maximum plausibility check */
638 
639  /* ------- 2. Perform minimum/maximum measurement range check ---------- */
640  if (STD_OK ==
642  ltc_state->ltcData.cellTemperature
643  ->cellTemperature_ddegC[stringNumber][(m * BS_NR_OF_TEMP_SENSORS_PER_MODULE) + c])) {
644  numberValidMeasurements++;
645  } else {
646  /* Invalidate cell temperature measurement */
647  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][m] |= (0x01u << c);
648  cellTemperatureMeasurementValid = STD_NOT_OK;
649  }
650  } else {
651  /* Already invalid because of PEC Error */
652  cellTemperatureMeasurementValid = STD_NOT_OK;
653  }
654  }
655  }
656  DIAG_CheckEvent(cellTemperatureMeasurementValid, ltc_state->tempMeasDiagErrorEntry, DIAG_STRING, stringNumber);
657 
658  ltc_state->ltcData.cellTemperature->nrValidTemperatures[stringNumber] = numberValidMeasurements;
659 
660  ltc_state->ltcData.cellTemperature->state++;
662 }
663 
664 extern void LTC_SaveAllGpioMeasurement(LTC_STATE_s *ltc_state) {
665  ltc_state->ltcData.allGpioVoltages->state++;
667 }
668 
669 /**
670  * @brief stores the measured balancing feedback values in the database.
671  *
672  * This function stores the global balancing feedback value measured on GPIO3 of the LTC into the database
673  *
674  * @param ltc_state state of the ltc state machine
675  * @param DataBufferSPI_RX receive buffer of the SPI interface
676  * @param stringNumber string addressed
677  *
678  */
679 static void LTC_SaveBalancingFeedback(LTC_STATE_s *ltc_state, uint16_t *DataBufferSPI_RX, uint8_t stringNumber) {
680  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
681  const uint16_t val_i = DataBufferSPI_RX[8u + (1u * i * 8u)] |
682  (DataBufferSPI_RX[8u + (1u * i * 8u) + 1u] << 8u); /* raw value, GPIO3 */
683 
684  ltc_state->ltcData.balancingFeedback->value[stringNumber][i] = val_i;
685  }
686 
687  ltc_state->ltcData.balancingFeedback->state++;
689 }
690 
691 /**
692  * @brief gets the balancing orders from the database.
693  *
694  * This function gets the balancing control from the database. Balancing control
695  * is set by the BMS. The LTC driver only executes the balancing orders.
696  *
697  * @param ltc_state: state of the ltc state machine
698  *
699  */
702 }
703 
704 /**
705  * @brief re-entrance check of LTC state machine trigger function
706  *
707  * This function is not re-entrant and should only be called time- or event-triggered.
708  * It increments the triggerentry counter from the state variable ltc_state.
709  * It should never be called by two different processes, so if it is the case, triggerentry
710  * should never be higher than 0 when this function is called.
711  *
712  * @param ltc_state: state of the ltc state machine
713  *
714  * @return retval 0 if no further instance of the function is active, 0xff else
715  *
716  */
717 uint8_t LTC_CheckReEntrance(LTC_STATE_s *ltc_state) {
718  uint8_t retval = 0;
719 
721  if (!ltc_state->triggerentry) {
722  ltc_state->triggerentry++;
723  } else {
724  retval = 0xFF; /* multiple calls of function */
725  }
727 
728  return (retval);
729 }
730 
732  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
733 
735  retval.request = ltc_state->statereq.request;
736  retval.string = ltc_state->statereq.string;
738 
739  return (retval);
740 }
741 
743  return ltc_state->state;
744 }
745 
746 /**
747  * @brief transfers the current state request to the state machine.
748  *
749  * This function takes the current state request from ltc_state and transfers it to the state machine.
750  * It resets the value from ltc_state to LTC_STATE_NO_REQUEST
751  *
752  * @param ltc_state: state of the ltc state machine
753  * @param pBusIDptr bus ID, main or backup (deprecated)
754  * @param pAdcModeptr LTC ADCmeasurement mode (fast, normal or filtered)
755  * @param pAdcMeasChptr number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
756  *
757  * @return retVal current state request, taken from LTC_STATE_REQUEST_e
758  *
759  */
761  LTC_STATE_s *ltc_state,
762  uint8_t *pBusIDptr,
763  LTC_ADCMODE_e *pAdcModeptr,
764  LTC_ADCMEAS_CHAN_e *pAdcMeasChptr) {
765  LTC_REQUEST_s retval = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
766 
768  retval.request = ltc_state->statereq.request;
769  retval.string = ltc_state->statereq.string;
770  ltc_state->requestedString = ltc_state->statereq.string;
771  *pAdcModeptr = ltc_state->adcModereq;
772  *pAdcMeasChptr = ltc_state->adcMeasChreq;
774  ltc_state->statereq.string = 0x0u;
776 
777  return (retval);
778 }
779 
781  LTC_RETURN_TYPE_e retVal = LTC_ERROR;
782 
784  retVal = LTC_CheckStateRequest(ltc_state, statereq);
785 
786  if ((retVal == LTC_OK) || (retVal == LTC_BUSY_OK) || (retVal == LTC_OK_FROM_ERROR)) {
787  ltc_state->statereq = statereq;
788  }
790 
791  return (retVal);
792 }
793 
794 void LTC_Trigger(LTC_STATE_s *ltc_state) {
795  STD_RETURN_TYPE_e retVal = STD_OK;
796  LTC_REQUEST_s statereq = {.request = LTC_STATE_NO_REQUEST, .string = 0x0u};
797  uint8_t tmpbusID = 0;
800  STD_RETURN_TYPE_e continueFunction = STD_OK;
801 
802  FAS_ASSERT(ltc_state != NULL_PTR);
803 
804  /* Check re-entrance of function */
805  if (LTC_CheckReEntrance(ltc_state) > 0u) {
806  continueFunction = STD_NOT_OK;
807  }
808 
809  if (ltc_state->check_spi_flag == STD_NOT_OK) {
810  if (ltc_state->timer > 0u) {
811  if ((--ltc_state->timer) > 0u) {
812  ltc_state->triggerentry--;
813  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
814  }
815  }
816  } else {
817  if (AFE_IsTransmitOngoing(ltc_state) == true) {
818  if (ltc_state->timer > 0u) {
819  if ((--ltc_state->timer) > 0u) {
820  ltc_state->triggerentry--;
821  continueFunction = STD_NOT_OK; /* handle state machine only if timer has elapsed */
822  }
823  }
824  }
825  }
826 
827  if (continueFunction == STD_OK) {
828  switch (ltc_state->state) {
829  /****************************UNINITIALIZED***********************************/
831  /* waiting for Initialization Request */
832  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
833  if (statereq.request == LTC_STATE_INIT_REQUEST) {
834  LTC_SaveLastStates(ltc_state);
835  LTC_InitializeDatabase(ltc_state);
836  LTC_ResetErrorTable(ltc_state);
839  ltc_state->adcMode = tmpadcMode;
840  ltc_state->adcMeasCh = tmpadcMeasCh;
841  } else if (statereq.request == LTC_STATE_NO_REQUEST) {
842  /* no actual request pending */
843  } else {
844  ltc_state->ErrRequestCounter++; /* illegal request pending */
845  }
846  break;
847 
848  /****************************INITIALIZATION**********************************/
850 
851  LTC_SetTransferTimes(ltc_state);
852 
853  if (ltc_state->substate == LTC_INIT_STRING) {
854  LTC_SaveLastStates(ltc_state);
855  ltc_state->currentString = 0u;
856 
857  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
859  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
862  } else if (ltc_state->substate == LTC_ENTRY_INITIALIZATION) {
863  LTC_SaveLastStates(ltc_state);
864 
865  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
866  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
867  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
868  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
869 
870  retVal = LTC_TransmitWakeUp(ltc_state->spiSeqPtr); /* Send dummy byte to wake up the daisy chain */
872  ltc_state,
873  retVal,
874  ltc_state->spiDiagErrorEntry,
881 
882  } else if (ltc_state->substate == LTC_RE_ENTRY_INITIALIZATION) {
883  LTC_SaveLastStates(ltc_state);
884  retVal =
885  LTC_TransmitWakeUp(ltc_state->spiSeqPtr); /* Send dummy byte again to wake up the daisy chain */
887  ltc_state,
888  retVal,
889  ltc_state->spiDiagErrorEntry,
896 
897  } else if (ltc_state->substate == LTC_START_INIT_INITIALIZATION) {
898  LTC_SaveLastStates(ltc_state);
899  ltc_state->check_spi_flag = STD_OK;
900  AFE_SetTransmitOngoing(ltc_state);
901  retVal = LTC_Init(
902  ltc_state->spiSeqPtr,
903  ltc_state->ltcData.txBuffer,
904  ltc_state->ltcData.rxBuffer,
905  ltc_state->ltcData.frameLength); /* Initialize main LTC loop */
906  ltc_state->lastsubstate = ltc_state->substate;
907  DIAG_CheckEvent(retVal, ltc_state->spiDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
909  ltc_state,
913 
914  } else if (ltc_state->substate == LTC_CHECK_INITIALIZATION) {
915  /* Read values written in config register, currently unused */
916  LTC_SaveLastStates(ltc_state);
917  AFE_SetTransmitOngoing(ltc_state);
918  retVal = LTC_ReadRegister(
919  ltc_cmdRDCFG,
920  ltc_state->spiSeqPtr,
921  ltc_state->ltcData.txBuffer,
922  ltc_state->ltcData.rxBuffer,
923  ltc_state->ltcData.frameLength); /* Read config register */
925  ltc_state,
929 
930  } else if (ltc_state->substate == LTC_EXIT_INITIALIZATION) {
931  LTC_SaveLastStates(ltc_state);
932  ++ltc_state->spiSeqPtr;
933  ++ltc_state->currentString;
934  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
937  } else {
940  }
941  }
942  break;
943 
944  /****************************INITIALIZED*************************************/
946  LTC_SaveLastStates(ltc_state);
948  break;
949 
950  /****************************START MEASUREMENT*******************************/
952 
955 
956  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface;
958  ltc_state->spiSeqEndPtr = ltc_state->ltcData.pSpiInterface + BS_NR_OF_STRINGS;
959  ltc_state->currentString = 0u;
960 
961  ltc_state->check_spi_flag = STD_NOT_OK;
962  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
963 
965  ltc_state,
966  retVal,
967  ltc_state->spiDiagErrorEntry,
970  (ltc_state->commandTransferTime +
971  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
975 
976  break;
977 
978  /****************************START MEASUREMENT CONTINUE*******************************/
979  /* Do not reset SPI interface pointer */
981 
984 
985  ltc_state->check_spi_flag = STD_NOT_OK;
986  retVal = LTC_StartVoltageMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
987 
989  ltc_state,
990  retVal,
991  ltc_state->spiDiagErrorEntry,
994  (ltc_state->commandTransferTime +
995  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
999 
1000  break;
1001 
1002  /****************************READ VOLTAGE************************************/
1004 
1006  ltc_state->check_spi_flag = STD_OK;
1007  AFE_SetTransmitOngoing(ltc_state);
1008  retVal = LTC_ReadRegister(
1009  ltc_cmdRDCVA,
1010  ltc_state->spiSeqPtr,
1011  ltc_state->ltcData.txBuffer,
1012  ltc_state->ltcData.rxBuffer,
1013  ltc_state->ltcData.frameLength);
1015  ltc_state,
1016  retVal,
1017  ltc_state->spiDiagErrorEntry,
1024  break;
1025 
1026  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_B_RDCVB_READVOLTAGE) {
1027  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1028  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1029  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1030 
1031  AFE_SetTransmitOngoing(ltc_state);
1032  retVal = LTC_ReadRegister(
1033  ltc_cmdRDCVB,
1034  ltc_state->spiSeqPtr,
1035  ltc_state->ltcData.txBuffer,
1036  ltc_state->ltcData.rxBuffer,
1037  ltc_state->ltcData.frameLength);
1039  ltc_state,
1040  retVal,
1041  ltc_state->spiDiagErrorEntry,
1048  break;
1049 
1050  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_C_RDCVC_READVOLTAGE) {
1051  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1052  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1053  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1054 
1055  AFE_SetTransmitOngoing(ltc_state);
1056  retVal = LTC_ReadRegister(
1057  ltc_cmdRDCVC,
1058  ltc_state->spiSeqPtr,
1059  ltc_state->ltcData.txBuffer,
1060  ltc_state->ltcData.rxBuffer,
1061  ltc_state->ltcData.frameLength);
1063  ltc_state,
1064  retVal,
1065  ltc_state->spiDiagErrorEntry,
1072  break;
1073 
1074  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_D_RDCVD_READVOLTAGE) {
1075  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1076  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1077  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1078 
1079  AFE_SetTransmitOngoing(ltc_state);
1080  retVal = LTC_ReadRegister(
1081  ltc_cmdRDCVD,
1082  ltc_state->spiSeqPtr,
1083  ltc_state->ltcData.txBuffer,
1084  ltc_state->ltcData.rxBuffer,
1085  ltc_state->ltcData.frameLength);
1086  if (BS_MAX_SUPPORTED_CELLS > 12) {
1088  ltc_state,
1089  retVal,
1090  ltc_state->spiDiagErrorEntry,
1097  } else {
1099  ltc_state,
1100  retVal,
1101  ltc_state->spiDiagErrorEntry,
1108  }
1109  break;
1110 
1111  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_E_RDCVE_READVOLTAGE) {
1112  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1113  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1114  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1115 
1116  AFE_SetTransmitOngoing(ltc_state);
1117  retVal = LTC_ReadRegister(
1118  ltc_cmdRDCVE,
1119  ltc_state->spiSeqPtr,
1120  ltc_state->ltcData.txBuffer,
1121  ltc_state->ltcData.rxBuffer,
1122  ltc_state->ltcData.frameLength);
1123  if (BS_MAX_SUPPORTED_CELLS > 15) {
1125  ltc_state,
1126  retVal,
1127  ltc_state->spiDiagErrorEntry,
1134  } else {
1136  ltc_state,
1137  retVal,
1138  ltc_state->spiDiagErrorEntry,
1145  }
1146  break;
1147 
1148  } else if (ltc_state->substate == LTC_READ_VOLTAGE_REGISTER_F_RDCVF_READVOLTAGE) {
1149  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1150  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1151  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1152 
1153  AFE_SetTransmitOngoing(ltc_state);
1154  retVal = LTC_ReadRegister(
1155  ltc_cmdRDCVF,
1156  ltc_state->spiSeqPtr,
1157  ltc_state->ltcData.txBuffer,
1158  ltc_state->ltcData.rxBuffer,
1159  ltc_state->ltcData.frameLength);
1161  ltc_state,
1162  retVal,
1163  ltc_state->spiDiagErrorEntry,
1170  break;
1171 
1172  } else if (ltc_state->substate == LTC_EXIT_READVOLTAGE) {
1173  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1174  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1175  if (BS_MAX_SUPPORTED_CELLS == 12) {
1176  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1177  } else if (BS_MAX_SUPPORTED_CELLS == 15) {
1178  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 4, ltc_state->currentString);
1179  } else if (BS_MAX_SUPPORTED_CELLS == 18) {
1180  LTC_SaveRxToVoltageBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 5, ltc_state->currentString);
1181  }
1182 
1183  /* Switch to different state if read voltage state is reused
1184  * e.g. open-wire check... */
1185  if (ltc_state->reusageMeasurementMode == LTC_NOT_REUSED) {
1186  LTC_SaveVoltages(ltc_state, ltc_state->currentString);
1188  ltc_state,
1192  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PUP) {
1194  ltc_state,
1198  } else if (ltc_state->reusageMeasurementMode == LTC_REUSE_READVOLT_FOR_ADOW_PDOWN) {
1200  ltc_state,
1204  }
1205  ltc_state->check_spi_flag = STD_NOT_OK;
1206  }
1207  break;
1208 
1209  /****************************MULTIPLEXED MEASUREMENT CONFIGURATION***********/
1211 
1214 
1215  if (ltc_state->substate == LTC_STATEMACH_MUXCONFIGURATION_INIT) {
1216  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1218 
1219  if (ltc_state->muxmeas_seqptr[ltc_state->currentString] >=
1220  ltc_state->muxmeas_seqendptr[ltc_state->currentString]) {
1221  /* last step of sequence reached (or no sequence configured) */
1222 
1223  ltc_state->muxmeas_seqptr[ltc_state->currentString] = ltc_mux_seq.seqptr;
1224  ltc_state->muxmeas_nr_end[ltc_state->currentString] = ltc_mux_seq.nr_of_steps;
1225  ltc_state->muxmeas_seqendptr[ltc_state->currentString] =
1226  ((LTC_MUX_CH_CFG_s *)ltc_mux_seq.seqptr) + ltc_mux_seq.nr_of_steps; /* last sequence + 1 */
1227 
1228  LTC_SaveTemperatures(ltc_state, ltc_state->currentString);
1229  }
1230 
1231  ltc_state->check_spi_flag = STD_OK;
1232  AFE_SetTransmitOngoing(ltc_state);
1233  retVal = LTC_SetMuxChannel(
1234  ltc_state->spiSeqPtr,
1235  ltc_state->ltcData.txBuffer,
1236  ltc_state->ltcData.rxBuffer,
1237  ltc_state->ltcData.frameLength,
1238  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID, /* mux */
1239  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh /* channel */);
1240  if (retVal != STD_OK) {
1241  DIAG_Handler(
1243  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1245  ltc_state,
1249  } else {
1250  DIAG_Handler(
1251  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1253  ltc_state,
1257  }
1258  break;
1259 
1260  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
1261  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1262  DIAG_Handler(
1264  } else {
1265  DIAG_Handler(
1266  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1267  }
1268 
1269  AFE_SetTransmitOngoing(ltc_state);
1270  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1271  if (LTC_GOTO_MUX_CHECK == true) {
1273  ltc_state,
1274  retVal,
1275  ltc_state->spiDiagErrorEntry,
1282  ;
1283  } else {
1285  ltc_state,
1286  retVal,
1287  ltc_state->spiDiagErrorEntry,
1294  }
1295  break;
1296 
1298  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1299  DIAG_Handler(
1301  } else {
1302  DIAG_Handler(
1303  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1304  }
1305 
1306  AFE_SetTransmitOngoing(ltc_state);
1307  retVal = LTC_ReadRegister(
1308  ltc_cmdRDCOMM,
1309  ltc_state->spiSeqPtr,
1310  ltc_state->ltcData.txBuffer,
1311  ltc_state->ltcData.rxBuffer,
1312  ltc_state->ltcData.frameLength);
1314  ltc_state,
1315  retVal,
1316  ltc_state->spiDiagErrorEntry,
1323  break;
1324 
1326  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1327  DIAG_Handler(
1329  } else {
1330  DIAG_Handler(
1331  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1332  }
1333 
1334  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1335  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1336 
1337  /* if CRC OK: check multiplexer answer on i2C bus */
1338  retVal = LTC_I2cCheckAck(
1339  ltc_state,
1340  ltc_state->ltcData.rxBuffer,
1341  ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID,
1342  ltc_state->currentString);
1343  DIAG_CheckEvent(retVal, ltc_state->muxDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1346  break;
1347 
1348  } else if (ltc_state->substate == LTC_STATEMACH_MUXMEASUREMENT) {
1349  if (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxCh == 0xFF) {
1350  /* actual multiplexer is switched off, so do not make a measurement and follow up with next step (mux configuration) */
1351  ++ltc_state
1352  ->muxmeas_seqptr[ltc_state->currentString]; /* go further with next step of sequence
1353  ltc_state.numberOfMeasuredMux not decremented, this does not count as a measurement */
1355  break;
1356  } else {
1357  if (LTC_GOTO_MUX_CHECK == false) {
1358  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1359  DIAG_Handler(
1360  ltc_state->spiDiagErrorEntry,
1362  DIAG_STRING,
1363  ltc_state->currentString);
1364  } else {
1365  DIAG_Handler(
1366  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1367  }
1368  }
1369 
1370  ltc_state->check_spi_flag = STD_NOT_OK;
1371  /* user multiplexer type -> connected to GPIO2! */
1372  if ((ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 1) ||
1373  (ltc_state->muxmeas_seqptr[ltc_state->currentString]->muxID == 2)) {
1374  retVal = LTC_StartGpioMeasurement(
1375  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2);
1376  } else {
1377  retVal = LTC_StartGpioMeasurement(
1378  ltc_state->spiSeqPtr, ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO1);
1379  }
1380  }
1382  ltc_state,
1383  retVal,
1384  ltc_state->spiDiagErrorEntry,
1387  (ltc_state->commandTransferTime +
1389  ltc_state->adcMode, LTC_ADCMEAS_SINGLECHANNEL_GPIO2)), /* wait, ADAX-Command */
1393  break;
1394 
1395  } else if (ltc_state->substate == LTC_STATEMACH_READMUXMEASUREMENT) {
1396  ltc_state->check_spi_flag = STD_OK;
1397 
1398  AFE_SetTransmitOngoing(ltc_state);
1399  retVal = LTC_ReadRegister(
1400  ltc_cmdRDAUXA,
1401  ltc_state->spiSeqPtr,
1402  ltc_state->ltcData.txBuffer,
1403  ltc_state->ltcData.rxBuffer,
1404  ltc_state->ltcData.frameLength);
1406  ltc_state,
1407  retVal,
1408  ltc_state->spiDiagErrorEntry,
1415  break;
1416 
1417  } else if (ltc_state->substate == LTC_STATEMACH_STOREMUXMEASUREMENT) {
1418  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1419  DIAG_Handler(
1421  } else {
1422  DIAG_Handler(
1423  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1424  }
1425 
1426  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1427  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1429  ltc_state,
1430  ltc_state->ltcData.rxBuffer,
1431  ltc_state->muxmeas_seqptr[ltc_state->currentString],
1432  ltc_state->currentString);
1433 
1434  ++ltc_state->muxmeas_seqptr[ltc_state->currentString];
1435 
1438  break;
1439  }
1440 
1441  break;
1442 
1443  /****************************END OF MEASUREMENT CYCLE************************/
1445 
1446  if (ltc_state->balance_control_done == STD_OK) {
1447  if (LTC_IsFirstMeasurementCycleFinished(ltc_state) == false) {
1449  }
1450  statereq = LTC_TransferStateRequest(ltc_state, &tmpbusID, &tmpadcMode, &tmpadcMeasCh);
1451  if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST) {
1453  ltc_state,
1457  ltc_state->balance_control_done = STD_NOT_OK;
1458  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST) {
1460  ltc_state,
1464  ltc_state->balance_control_done = STD_NOT_OK;
1465  } else if (statereq.request == LTC_STATE_USER_IO_WRITE_REQUEST_TI) {
1467  ltc_state,
1471  ltc_state->balance_control_done = STD_NOT_OK;
1472  } else if (statereq.request == LTC_STATE_USER_IO_READ_REQUEST_TI) {
1474  ltc_state,
1478  ltc_state->balance_control_done = STD_NOT_OK;
1479  } else if (statereq.request == LTC_STATE_EEPROM_READ_REQUEST) {
1482  } else if (statereq.request == LTC_STATE_EEPROM_WRITE_REQUEST) {
1485  ltc_state->balance_control_done = STD_NOT_OK;
1486  } else if (statereq.request == LTC_STATE_TEMP_SENS_READ_REQUEST) {
1489  ltc_state->balance_control_done = STD_NOT_OK;
1490  } else if (statereq.request == LTC_STATEMACH_BALANCEFEEDBACK_REQUEST) {
1493  ltc_state->balance_control_done = STD_NOT_OK;
1494  } else if (statereq.request == LTC_STATE_OPENWIRE_CHECK_REQUEST) {
1496  ltc_state,
1500  /* Send ADOW command with PUP two times */
1502  ltc_state->balance_control_done = STD_NOT_OK;
1503  } else {
1505  ltc_state,
1509  ltc_state->balance_control_done = STD_NOT_OK;
1510  }
1511  } else {
1514  }
1515 
1516  break;
1517 
1518  /****************************BALANCE CONTROL*********************************/
1520 
1521  if (ltc_state->substate == LTC_CONFIG_BALANCECONTROL) {
1522  ltc_state->check_spi_flag = STD_OK;
1523  AFE_SetTransmitOngoing(ltc_state);
1524  retVal = LTC_BalanceControl(
1525  ltc_state,
1526  ltc_state->spiSeqPtr,
1527  ltc_state->ltcData.txBuffer,
1528  ltc_state->ltcData.rxBuffer,
1529  ltc_state->ltcData.frameLength,
1530  0u,
1531  ltc_state->currentString);
1533  ltc_state,
1534  retVal,
1535  ltc_state->spiDiagErrorEntry,
1542  break;
1543 
1544  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL) {
1545  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1546  DIAG_Handler(
1548  } else {
1549  DIAG_Handler(
1550  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1551  }
1552 
1554  AFE_SetTransmitOngoing(ltc_state);
1555  retVal = LTC_BalanceControl(
1556  ltc_state,
1557  ltc_state->spiSeqPtr,
1558  ltc_state->ltcData.txBuffer,
1559  ltc_state->ltcData.rxBuffer,
1560  ltc_state->ltcData.frameLength,
1561  1u,
1562  ltc_state->currentString);
1564  ltc_state,
1565  retVal,
1566  ltc_state->spiDiagErrorEntry,
1573  } else {
1574  /* 12 cells, balancing control finished */
1575  ltc_state->check_spi_flag = STD_NOT_OK;
1576  ++ltc_state->spiSeqPtr;
1577  ++ltc_state->currentString;
1578  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1579  ltc_state->balance_control_done = STD_OK;
1581  } else {
1584  }
1585  }
1586 
1587  break;
1588 
1589  } else if (ltc_state->substate == LTC_CONFIG2_BALANCECONTROL_END) {
1590  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1591  DIAG_Handler(
1593  } else {
1594  DIAG_Handler(
1595  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1596  }
1597  /* More than 12 cells, balancing control finished */
1598  ltc_state->check_spi_flag = STD_NOT_OK;
1599  ++ltc_state->spiSeqPtr;
1600  ++ltc_state->currentString;
1601  if (ltc_state->spiSeqPtr >= ltc_state->spiSeqEndPtr) {
1602  ltc_state->balance_control_done = STD_OK;
1604  } else {
1607  }
1608 
1609  break;
1610  }
1611  break;
1612 
1613  /****************************START MEASUREMENT*******************************/
1615 
1616  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1618 
1619  ltc_state->check_spi_flag = STD_NOT_OK;
1620  retVal = LTC_StartGpioMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1622  ltc_state,
1623  retVal,
1624  ltc_state->spiDiagErrorEntry,
1627  (ltc_state->commandTransferTime +
1628  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1630  LTC_ENTRY,
1631  LTC_STATEMACH_SHORTTIME); /* TODO: @koffel here same state is kept if error occurs */
1632  break;
1633 
1634  /****************************READ ALL GPIO VOLTAGE************************************/
1636 
1637  if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_A_RDAUXA) {
1638  ltc_state->check_spi_flag = STD_OK;
1639  AFE_SetTransmitOngoing(ltc_state);
1640  retVal = LTC_ReadRegister(
1641  ltc_cmdRDAUXA,
1642  ltc_state->spiSeqPtr,
1643  ltc_state->ltcData.txBuffer,
1644  ltc_state->ltcData.rxBuffer,
1645  ltc_state->ltcData.frameLength);
1647  ltc_state,
1648  retVal,
1649  ltc_state->spiDiagErrorEntry,
1656  break;
1657 
1658  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_B_RDAUXB) {
1659  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1660  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1661  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 0, ltc_state->currentString);
1662 
1663  AFE_SetTransmitOngoing(ltc_state);
1664  retVal = LTC_ReadRegister(
1665  ltc_cmdRDAUXB,
1666  ltc_state->spiSeqPtr,
1667  ltc_state->ltcData.txBuffer,
1668  ltc_state->ltcData.rxBuffer,
1669  ltc_state->ltcData.frameLength);
1670 
1671  if (BS_MAX_SUPPORTED_CELLS > 12) {
1673  ltc_state,
1674  retVal,
1675  ltc_state->spiDiagErrorEntry,
1682  } else {
1684  ltc_state,
1685  retVal,
1686  ltc_state->spiDiagErrorEntry,
1693  }
1694  break;
1695 
1696  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_C_RDAUXC) {
1697  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1698  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1699  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1700 
1701  AFE_SetTransmitOngoing(ltc_state);
1702  retVal = LTC_ReadRegister(
1703  ltc_cmdRDAUXC,
1704  ltc_state->spiSeqPtr,
1705  ltc_state->ltcData.txBuffer,
1706  ltc_state->ltcData.rxBuffer,
1707  ltc_state->ltcData.frameLength);
1709  ltc_state,
1710  retVal,
1711  ltc_state->spiDiagErrorEntry,
1718  break;
1719 
1720  } else if (ltc_state->substate == LTC_READ_AUXILIARY_REGISTER_D_RDAUXD) {
1721  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1722  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1723  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 2, ltc_state->currentString);
1724 
1725  AFE_SetTransmitOngoing(ltc_state);
1726  retVal = LTC_ReadRegister(
1727  ltc_cmdRDAUXD,
1728  ltc_state->spiSeqPtr,
1729  ltc_state->ltcData.txBuffer,
1730  ltc_state->ltcData.rxBuffer,
1731  ltc_state->ltcData.frameLength);
1733  ltc_state,
1734  retVal,
1735  ltc_state->spiDiagErrorEntry,
1742  break;
1743 
1744  } else if (ltc_state->substate == LTC_EXIT_READAUXILIARY_ALLGPIOS) {
1745  retVal = LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1746  DIAG_CheckEvent(retVal, ltc_state->pecDiagErrorEntry, DIAG_STRING, ltc_state->currentString);
1747 
1748  if (BS_MAX_SUPPORTED_CELLS == 12) {
1749  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 1, ltc_state->currentString);
1750  } else if (BS_MAX_SUPPORTED_CELLS > 12) {
1751  LTC_SaveRxToGpioBuffer(ltc_state, ltc_state->ltcData.rxBuffer, 3, ltc_state->currentString);
1752  }
1753 
1754  LTC_SaveAllGpioMeasurement(ltc_state);
1755 
1758  }
1759 
1760  break;
1761 
1762  /****************************BALANCE FEEDBACK*********************************/
1764 
1765  ltc_state->adcMode = LTC_GPIO_MEASUREMENT_MODE;
1767 
1768  if (ltc_state->substate == LTC_ENTRY) {
1769  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1770  ltc_state->adcMode = LTC_ADCMODE_NORMAL_DCP0;
1772 
1773  ltc_state->check_spi_flag = STD_NOT_OK;
1774  retVal = LTC_StartGpioMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, ltc_state->adcMeasCh);
1776  ltc_state,
1777  retVal,
1778  ltc_state->spiDiagErrorEntry,
1781  (ltc_state->commandDataTransferTime +
1782  LTC_GetMeasurementTimeCycle(ltc_state->adcMode, ltc_state->adcMeasCh)),
1786  break;
1787 
1788  } else if (ltc_state->substate == LTC_READ_FEEDBACK_BALANCECONTROL) {
1789  ltc_state->check_spi_flag = STD_OK;
1790  AFE_SetTransmitOngoing(ltc_state);
1791  retVal = LTC_ReadRegister(
1792  ltc_cmdRDAUXA,
1793  ltc_state->spiSeqPtr,
1794  ltc_state->ltcData.txBuffer,
1795  ltc_state->ltcData.rxBuffer,
1796  ltc_state->ltcData.frameLength); /* read AUXA register */
1798  ltc_state,
1799  retVal,
1800  ltc_state->spiDiagErrorEntry,
1807 
1808  } else if (ltc_state->substate == LTC_SAVE_FEEDBACK_BALANCECONTROL) {
1809  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1810  DIAG_Handler(
1813  break;
1814  } else {
1815  DIAG_Handler(
1816  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1817  }
1818 
1819  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1820  DIAG_Handler(
1822  } else {
1823  DIAG_Handler(
1824  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1825  LTC_SaveBalancingFeedback(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString);
1826  }
1828  break;
1829  }
1830  break;
1831 
1832  /****************************BOARD TEMPERATURE SENSOR*********************************/
1834 
1835  if (ltc_state->substate == LTC_TEMP_SENS_SEND_DATA1) {
1836  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
1837  ltc_state->check_spi_flag = STD_OK;
1838  AFE_SetTransmitOngoing(ltc_state);
1839  retVal = LTC_SendI2cCommand(
1840  ltc_state->spiSeqPtr,
1841  ltc_state->ltcData.txBuffer,
1842  ltc_state->ltcData.rxBuffer,
1843  ltc_state->ltcData.frameLength,
1845 
1846  if (retVal != STD_OK) {
1847  DIAG_Handler(
1849  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1851  } else {
1852  DIAG_Handler(
1853  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1855  ltc_state,
1859  }
1860 
1861  break;
1862 
1863  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM1) {
1864  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1865  DIAG_Handler(
1868  break;
1869  } else {
1870  DIAG_Handler(
1871  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1872  }
1873 
1874  AFE_SetTransmitOngoing(ltc_state);
1875  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1877  ltc_state,
1878  retVal,
1879  ltc_state->spiDiagErrorEntry,
1886  break;
1887 
1888  } else if (ltc_state->substate == LTC_TEMP_SENS_READ_DATA1) {
1889  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1890  DIAG_Handler(
1893  break;
1894  } else {
1895  DIAG_Handler(
1896  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1897  }
1898 
1899  AFE_SetTransmitOngoing(ltc_state);
1900  retVal = LTC_SendI2cCommand(
1901  ltc_state->spiSeqPtr,
1902  ltc_state->ltcData.txBuffer,
1903  ltc_state->ltcData.rxBuffer,
1904  ltc_state->ltcData.frameLength,
1906 
1907  if (retVal != STD_OK) {
1908  DIAG_Handler(
1910  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
1912  } else {
1913  DIAG_Handler(
1914  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1916  ltc_state,
1920  }
1921 
1922  break;
1923 
1924  } else if (ltc_state->substate == LTC_TEMP_SENS_SEND_CLOCK_STCOMM2) {
1925  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1926  DIAG_Handler(
1929  break;
1930  } else {
1931  DIAG_Handler(
1932  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1933  }
1934 
1935  AFE_SetTransmitOngoing(ltc_state);
1936  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
1938  ltc_state,
1939  retVal,
1940  ltc_state->spiDiagErrorEntry,
1947  break;
1949  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1950  DIAG_Handler(
1953  break;
1954  } else {
1955  DIAG_Handler(
1956  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1957  }
1958 
1959  AFE_SetTransmitOngoing(ltc_state);
1960  retVal = LTC_ReadRegister(
1961  ltc_cmdRDCOMM,
1962  ltc_state->spiSeqPtr,
1963  ltc_state->ltcData.txBuffer,
1964  ltc_state->ltcData.rxBuffer,
1965  ltc_state->ltcData.frameLength);
1967  ltc_state,
1968  retVal,
1969  ltc_state->spiDiagErrorEntry,
1976  break;
1977 
1978  } else if (ltc_state->substate == LTC_TEMP_SENS_SAVE_TEMP) {
1979  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
1980  DIAG_Handler(
1983  break;
1984  } else {
1985  DIAG_Handler(
1986  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1987  }
1988 
1989  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
1990  DIAG_Handler(
1992  } else {
1993  DIAG_Handler(
1994  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
1995  LTC_TempSensSaveTemp(ltc_state, ltc_state->ltcData.rxBuffer);
1996  }
1997 
1999  break;
2000  }
2001  break;
2002 
2003  /****************************WRITE TO PORT EXPANDER IO***********/
2005 
2006  if (ltc_state->substate == LTC_USER_IO_SET_OUTPUT_REGISTER) {
2007  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2008  ltc_state->check_spi_flag = STD_OK;
2009  AFE_SetTransmitOngoing(ltc_state);
2010  retVal = LTC_SetPortExpander(
2011  ltc_state,
2012  ltc_state->spiSeqPtr,
2013  ltc_state->ltcData.txBuffer,
2014  ltc_state->ltcData.rxBuffer,
2015  ltc_state->ltcData.frameLength);
2016 
2017  if (retVal != STD_OK) {
2018  DIAG_Handler(
2020  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2022  } else {
2023  DIAG_Handler(
2024  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2026  ltc_state,
2030  }
2031  break;
2032 
2033  } else if (ltc_state->substate == LTC_SEND_CLOCK_STCOMM_MUXMEASUREMENT_CONFIG) {
2034  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2035  DIAG_Handler(
2038  break;
2039  } else {
2040  DIAG_Handler(
2041  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2042  }
2043 
2044  ltc_state->check_spi_flag = STD_NOT_OK;
2045  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2047  ltc_state,
2048  retVal,
2049  ltc_state->spiDiagErrorEntry,
2051  LTC_ENTRY,
2052  ltc_state->gpioClocksTransferTime,
2054  LTC_ENTRY,
2056  break;
2057  }
2058  break;
2059 
2060  /****************************READ FROM PORT EXPANDER IO***********/
2062 
2063  if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER) {
2064  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2065  ltc_state->check_spi_flag = STD_OK;
2066  AFE_SetTransmitOngoing(ltc_state);
2067  retVal = LTC_SendI2cCommand(
2068  ltc_state->spiSeqPtr,
2069  ltc_state->ltcData.txBuffer,
2070  ltc_state->ltcData.rxBuffer,
2071  ltc_state->ltcData.frameLength,
2073 
2074  if (retVal != STD_OK) {
2075  DIAG_Handler(
2077  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2079  } else {
2080  DIAG_Handler(
2081  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2083  ltc_state,
2087  }
2088 
2089  break;
2090 
2091  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM) {
2092  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2093  DIAG_Handler(
2096  break;
2097  } else {
2098  DIAG_Handler(
2099  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2100  }
2101 
2102  ltc_state->check_spi_flag = STD_NOT_OK;
2103  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2105  ltc_state,
2106  retVal,
2107  ltc_state->spiDiagErrorEntry,
2110  ltc_state->gpioClocksTransferTime,
2114  break;
2115 
2116  } else if (ltc_state->substate == LTC_USER_IO_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2117  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2118  DIAG_Handler(
2121  break;
2122  } else {
2123  DIAG_Handler(
2124  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2125  }
2126 
2127  AFE_SetTransmitOngoing(ltc_state);
2128  retVal = LTC_ReadRegister(
2129  ltc_cmdRDCOMM,
2130  ltc_state->spiSeqPtr,
2131  ltc_state->ltcData.txBuffer,
2132  ltc_state->ltcData.rxBuffer,
2133  ltc_state->ltcData.frameLength);
2135  ltc_state,
2136  retVal,
2137  ltc_state->spiDiagErrorEntry,
2144  break;
2145 
2146  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA) {
2147  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2148  DIAG_Handler(
2151  break;
2152  } else {
2153  DIAG_Handler(
2154  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2155  }
2156 
2157  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2158  DIAG_Handler(
2160  } else {
2161  DIAG_Handler(
2162  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2163  LTC_PortExpanderSaveValues(ltc_state, ltc_state->ltcData.rxBuffer);
2164  }
2165 
2167  break;
2168  }
2169 
2170  break;
2171 
2172  /****************************WRITE TO TI PORT EXPANDER IO***********/
2174 
2175  if (ltc_state->substate == LTC_USER_IO_SET_DIRECTION_REGISTER_TI) {
2176  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2177  ltc_state->check_spi_flag = STD_OK;
2178  AFE_SetTransmitOngoing(ltc_state);
2180  ltc_state,
2181  ltc_state->spiSeqPtr,
2182  ltc_state->ltcData.txBuffer,
2183  ltc_state->ltcData.rxBuffer,
2184  ltc_state->ltcData.frameLength,
2187  ltc_state,
2188  retVal,
2189  ltc_state->spiDiagErrorEntry,
2194  LTC_ENTRY,
2196  break;
2197 
2198  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2199  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2200  DIAG_Handler(
2203  break;
2204 
2205  } else {
2206  DIAG_Handler(
2207  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2208  }
2209 
2210  ltc_state->check_spi_flag = STD_NOT_OK;
2211  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2213  ltc_state,
2214  retVal,
2215  ltc_state->spiDiagErrorEntry,
2218  ltc_state->gpioClocksTransferTime,
2220  LTC_ENTRY,
2222  break;
2223 
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;
2244 
2246  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == 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 
2298  } else if (ltc_state->substate == LTC_USER_IO_SEND_CLOCK_STCOMM_TI) {
2299  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2300  DIAG_Handler(
2303  break;
2304 
2305  } else {
2306  DIAG_Handler(
2307  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2308  }
2309 
2310  ltc_state->check_spi_flag = STD_NOT_OK;
2311  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2313  ltc_state,
2314  retVal,
2315  ltc_state->spiDiagErrorEntry,
2318  ltc_state->gpioClocksTransferTime,
2320  LTC_ENTRY,
2322  break;
2323 
2324  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_FIRST) {
2325  ltc_state->check_spi_flag = STD_OK;
2326  AFE_SetTransmitOngoing(ltc_state);
2327  retVal = LTC_GetPortExpanderInputTi(
2328  ltc_state,
2329  ltc_state->spiSeqPtr,
2330  ltc_state->ltcData.txBuffer,
2331  ltc_state->ltcData.rxBuffer,
2332  ltc_state->ltcData.frameLength,
2333  0);
2335  ltc_state,
2336  retVal,
2337  ltc_state->spiDiagErrorEntry,
2342  LTC_ENTRY,
2344  break;
2345 
2347  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2348  DIAG_Handler(
2351  break;
2352  } else {
2353  DIAG_Handler(
2354  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2355  }
2356 
2357  ltc_state->check_spi_flag = STD_NOT_OK;
2358  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2360  ltc_state,
2361  retVal,
2362  ltc_state->spiDiagErrorEntry,
2365  ltc_state->gpioClocksTransferTime,
2367  LTC_ENTRY,
2369  break;
2370  } else if (ltc_state->substate == LTC_USER_IO_READ_INPUT_REGISTER_TI_SECOND) {
2371  ltc_state->check_spi_flag = STD_OK;
2372  AFE_SetTransmitOngoing(ltc_state);
2373  retVal = LTC_GetPortExpanderInputTi(
2374  ltc_state,
2375  ltc_state->spiSeqPtr,
2376  ltc_state->ltcData.txBuffer,
2377  ltc_state->ltcData.rxBuffer,
2378  ltc_state->ltcData.frameLength,
2379  1);
2381  ltc_state,
2382  retVal,
2383  ltc_state->spiDiagErrorEntry,
2388  LTC_ENTRY,
2390  break;
2391 
2393  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2394  DIAG_Handler(
2397  break;
2398  } else {
2399  DIAG_Handler(
2400  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2401  }
2402 
2403  ltc_state->check_spi_flag = STD_NOT_OK;
2404  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2406  ltc_state,
2407  retVal,
2408  ltc_state->spiDiagErrorEntry,
2411  ltc_state->gpioClocksTransferTime,
2413  LTC_ENTRY,
2415  break;
2417  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2418  DIAG_Handler(
2421  break;
2422  } else {
2423  DIAG_Handler(
2424  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2425  }
2426 
2427  AFE_SetTransmitOngoing(ltc_state);
2428  retVal = LTC_ReadRegister(
2429  ltc_cmdRDCOMM,
2430  ltc_state->spiSeqPtr,
2431  ltc_state->ltcData.txBuffer,
2432  ltc_state->ltcData.rxBuffer,
2433  ltc_state->ltcData.frameLength);
2435  ltc_state,
2436  retVal,
2437  ltc_state->spiDiagErrorEntry,
2444  break;
2445 
2446  } else if (ltc_state->substate == LTC_USER_IO_SAVE_DATA_TI) {
2447  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2448  DIAG_Handler(
2451  break;
2452  } else {
2453  DIAG_Handler(
2454  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2455  }
2456 
2457  if (LTC_CheckPec(ltc_state, ltc_state->ltcData.rxBuffer, ltc_state->currentString) != STD_OK) {
2458  DIAG_Handler(
2460  } else {
2461  DIAG_Handler(
2462  ltc_state->pecDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2463  LTC_PortExpanderSaveValuesTi(ltc_state, ltc_state->ltcData.txBuffer);
2464  }
2465 
2467  break;
2468  }
2469 
2470  break;
2471 
2472  /****************************EEPROM READ*********************************/
2474 
2475  if (ltc_state->substate == LTC_EEPROM_READ_DATA1) {
2476  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2477  ltc_state->check_spi_flag = STD_OK;
2478  AFE_SetTransmitOngoing(ltc_state);
2479  retVal = LTC_SendEepromReadCommand(
2480  ltc_state,
2481  ltc_state->spiSeqPtr,
2482  ltc_state->ltcData.txBuffer,
2483  ltc_state->ltcData.rxBuffer,
2484  ltc_state->ltcData.frameLength,
2485  0);
2486 
2487  if (retVal != STD_OK) {
2488  DIAG_Handler(
2490  ++ltc_state->muxmeas_seqptr[ltc_state->requestedString];
2492  } else {
2493  DIAG_Handler(
2494  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2496  ltc_state,
2500  }
2501 
2502  break;
2503 
2504  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM1) {
2505  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2506  DIAG_Handler(
2509  break;
2510  } else {
2511  DIAG_Handler(
2512  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2513  }
2514 
2515  AFE_SetTransmitOngoing(ltc_state);
2516  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2518  ltc_state,
2519  retVal,
2520  ltc_state->spiDiagErrorEntry,
2527  break;
2528 
2529  } else if (ltc_state->substate == LTC_EEPROM_READ_DATA2) {
2530  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2531  DIAG_Handler(
2534  break;
2535  } else {
2536  DIAG_Handler(
2537  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2538  }
2539 
2540  AFE_SetTransmitOngoing(ltc_state);
2541  retVal = LTC_SendEepromReadCommand(
2542  ltc_state,
2543  ltc_state->spiSeqPtr,
2544  ltc_state->ltcData.txBuffer,
2545  ltc_state->ltcData.rxBuffer,
2546  ltc_state->ltcData.frameLength,
2547  1);
2549  ltc_state,
2550  retVal,
2551  ltc_state->spiDiagErrorEntry,
2558  break;
2559 
2560  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM2) {
2561  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2562  DIAG_Handler(
2565  break;
2566  } else {
2567  DIAG_Handler(
2568  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2569  }
2570 
2571  AFE_SetTransmitOngoing(ltc_state);
2572  retVal = LTC_I2cClock(ltc_state->spiSeqPtr);
2574  ltc_state,
2575  retVal,
2576  ltc_state->spiDiagErrorEntry,
2583  break;
2584  } else if (ltc_state->substate == LTC_EEPROM_READ_I2C_TRANSMISSION_RESULT_RDCOMM) {
2585  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == 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 
2614  } else if (ltc_state->substate == LTC_EEPROM_SAVE_READ) {
2615  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == 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 
2671  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM3) {
2672  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == 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 
2696  } else if (ltc_state->substate == LTC_EEPROM_WRITE_DATA2) {
2697  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == 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 
2727  } else if (ltc_state->substate == LTC_EEPROM_SEND_CLOCK_STCOMM4) {
2728  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == 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  if ((ltc_state->timer == 0) && (AFE_IsTransmitOngoing(ltc_state) == true)) {
2753  DIAG_Handler(
2756  break;
2757  } else {
2758  DIAG_Handler(
2759  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2760  }
2762  break;
2763  }
2764 
2765  break;
2766 
2767  /**************************OPEN-WIRE CHECK*******************************/
2769  ltc_state->spiSeqPtr = ltc_state->ltcData.pSpiInterface + ltc_state->requestedString;
2770  /* This is necessary because the state machine will go through read voltage measurement registers */
2771  ltc_state->currentString = ltc_state->requestedString;
2773  /* Run ADOW command with PUP = 1 */
2774  ltc_state->adcMode = LTC_OW_MEASUREMENT_MODE;
2775  ltc_state->check_spi_flag = STD_NOT_OK;
2776 
2777  retVal = LTC_StartOpenWireMeasurement(ltc_state->spiSeqPtr, ltc_state->adcMode, 1);
2778  if (retVal == STD_OK) {
2779  DIAG_Handler(
2780  ltc_state->spiDiagErrorEntry, DIAG_EVENT_OK, DIAG_STRING, ltc_state->currentString);
2782  ltc_state,
2785  (ltc_state->commandDataTransferTime +
2787  ltc_state->resendCommandCounter--;
2788 
2789  /* Check how many retries are left */
2790  if (ltc_state->resendCommandCounter == 0) {
2791  /* Switch to read voltage state to read cell voltages */
2793  ltc_state,
2796  (ltc_state->commandDataTransferTime +
2798  /* Reuse read voltage register */
2800  }
2801  } else {
2802  DIAG_Handler(
2806  }
2807  } else if (ltc_state->substate == LTC_READ_VOLTAGES_PULLUP_OPENWIRE_CHECK) {
2808  /* Previous state: Read voltage -> information stored in voltage buffer */
2810 
2811  /* Copy data from voltage struct into open-wire struct */
2812  for (uint16_t i = 0u; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
2813  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] =
2814  ltc_state->ltcData.cellVoltage->cellVoltage_mV[ltc_state->requestedString][i];
2815  }
2816 
2817  /* Set number of ADOW retries - send ADOW command with pull-down two times */
2820  ltc_state,
2824 
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 
2872  } else if (ltc_state->substate == LTC_PERFORM_OPENWIRE_CHECK) {
2873  /* Perform actual open-wire check */
2874  for (uint8_t m = 0; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2875  /* Open-wire at C0: cell_pup(0) == 0 */
2876  if (ltc_state->ltcData.openWireDetection
2877  ->openWirePup[ltc_state->requestedString][0 + (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] ==
2878  0u) {
2879  ltc_state->ltcData.openWire->openwire[ltc_state->requestedString]
2880  [0 + (m * (BS_NR_OF_CELL_BLOCKS_PER_MODULE))] = 1u;
2881  }
2882  /* Open-wire at Cmax: cell_pdown(BS_NR_OF_CELL_BLOCKS_PER_MODULE-1) == 0 */
2883  if (ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][(
2885  ltc_state->ltcData.openWire
2886  ->openwire[ltc_state->requestedString]
2888  1u;
2889  }
2890  }
2891 
2892  /* Take difference between pull-up and pull-down measurement */
2893  for (uint16_t i = 1u; i < BS_NR_OF_CELL_BLOCKS_PER_STRING; i++) {
2894  ltc_state->ltcData.openWireDetection->openWireDelta[ltc_state->requestedString][i] =
2895  ltc_state->ltcData.openWireDetection->openWirePup[ltc_state->requestedString][i] -
2896  ltc_state->ltcData.openWireDetection->openWirePdown[ltc_state->requestedString][i];
2897  }
2898 
2899  /* Open-wire at C(N): delta cell(n+1) < -400mV */
2900  for (uint8_t m = 0u; m < BS_NR_OF_MODULES_PER_STRING; m++) {
2901  for (uint8_t c = 1u; c < (BS_NR_OF_CELL_BLOCKS_PER_MODULE - 1); c++) {
2902  if (ltc_state->ltcData.openWireDetection
2903  ->openWireDelta[ltc_state->requestedString]
2905  ltc_state->ltcData.openWire->openwire[ltc_state->requestedString]
2906  [c + (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] = 1;
2907  }
2908  }
2909  }
2910 
2911  /* Write database entry */
2912  DATA_WRITE_DATA(ltc_state->ltcData.openWire);
2913  /* Start new measurement cycle */
2915  }
2916  break;
2917 
2918  /****************************DEFAULT**************************/
2919  default:
2920  /* invalid state */
2922  break;
2923  }
2924 
2925  ltc_state->triggerentry--; /* reentrance counter */
2926  } /* continueFunction */
2927 }
2928 
2929 /**
2930  * @brief saves the multiplexer values read from the LTC daisy-chain.
2931  *
2932  * After a voltage measurement was initiated on GPIO 1 to read the currently selected
2933  * multiplexer voltage, the results is read via SPI from the daisy-chain.
2934  * This function is called to store the result from the transmission in a buffer.
2935  *
2936  * @param ltc_state state of the ltc state machine
2937  * @param pRxBuff receive buffer
2938  * @param muxseqptr pointer to the multiplexer sequence, which configures the currently selected multiplexer ID and channel
2939  * @param stringNumber string addressed
2940  *
2941  */
2943  LTC_STATE_s *ltc_state,
2944  uint16_t *pRxBuff,
2945  LTC_MUX_CH_CFG_s *muxseqptr,
2946  uint8_t stringNumber) {
2947  uint16_t val_ui = 0;
2948  int16_t temperature_ddegC = 0;
2949  uint8_t sensor_idx = 0;
2950  uint8_t ch_idx = 0;
2951  uint16_t buffer_LSB = 0;
2952  uint16_t buffer_MSB = 0;
2953 
2954  /* pointer to measurement Sequence of Mux- and Channel-Configurations (1,0xFF)...(3,0xFF),(0,1),...(0,7)) */
2955  /* Channel 0xFF means that the multiplexer is deactivated, therefore no measurement will be made and saved*/
2956  if (muxseqptr->muxCh != 0xFF) {
2957  /* user multiplexer type -> connected to GPIO2! */
2958  if ((muxseqptr->muxID == 1) || (muxseqptr->muxID == 2)) {
2959  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2960  if (muxseqptr->muxID == 1) {
2961  ch_idx = 0 + muxseqptr->muxCh; /* channel index 0..7 */
2962  } else {
2963  ch_idx = 8 + muxseqptr->muxCh; /* channel index 8..15 */
2964  }
2965 
2966  if (ch_idx < (2u * 8u)) {
2967  val_ui = *((uint16_t *)(&pRxBuff[6u + (1u * i * 8u)])); /* raw values, all mux on all LTCs */
2968  /* ltc_user_mux.value[i*8*2+ch_idx] = (uint16_t)(((float)(val_ui))*100e-6f*1000.0f); */ /* Unit -> in V -> in mV */
2969  }
2970  }
2971  } else {
2972  /* temperature multiplexer type -> connected to GPIO1! */
2973  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
2974  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
2975  buffer_LSB = pRxBuff[4u + (i * 8u)];
2976  val_ui = buffer_LSB | (buffer_MSB << 8);
2977  /* val_ui = *((uint16_t *)(&pRxBuff[4+i*8])); */
2978  /* GPIO voltage in 100uV -> * 0.1 ---- conversion to mV */
2979  temperature_ddegC = LTC_ConvertMuxVoltagesToTemperatures(val_ui / 10u); /* unit: deci &deg;C */
2980  sensor_idx = ltc_muxsensortemperatur_cfg[muxseqptr->muxCh];
2981  /* wrong configuration! */
2982  if (sensor_idx >= BS_NR_OF_TEMP_SENSORS_PER_MODULE) {
2984  }
2985  /* Set bitmask for valid flags */
2986 
2987  /* Check LTC PEC error */
2988  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
2989  /* Reset invalid flag */
2990  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] =
2991  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] &
2992  (~(1u << sensor_idx));
2993 
2994  ltc_state->ltcData.cellTemperature
2995  ->cellTemperature_ddegC[stringNumber][(i * (BS_NR_OF_TEMP_SENSORS_PER_MODULE)) + sensor_idx] =
2996  temperature_ddegC;
2997  } else {
2998  /* Set invalid flag */
2999  ltc_state->ltcData.cellTemperature->invalidCellTemperature[stringNumber][i] |= (1u << sensor_idx);
3000  }
3001  }
3002  }
3003  }
3004 }
3005 
3006 /**
3007  * @brief saves the voltage values read from the LTC daisy-chain.
3008  *
3009  * After a voltage measurement was initiated to measure the voltages of the cells,
3010  * the result is read via SPI from the daisy-chain.
3011  * There are 6 register to read _(A,B,C,D,E,F) to get all cell voltages.
3012  * Only one register can be read at a time.
3013  * This function is called to store the result from the transmission in a buffer.
3014  *
3015  * @param ltc_state state of the ltc state machine
3016  * @param pRxBuff receive buffer
3017  * @param registerSet voltage register that was read (voltage register A,B,C,D,E or F)
3018  * @param stringNumber string addressed
3019  *
3020  */
3022  LTC_STATE_s *ltc_state,
3023  uint16_t *pRxBuff,
3024  uint8_t registerSet,
3025  uint8_t stringNumber) {
3026  uint16_t cellOffset = 0;
3027  uint16_t voltage_index = 0;
3028  uint16_t val_ui = 0;
3029  uint16_t voltage = 0;
3030  uint32_t bitmask = 0;
3031  uint16_t buffer_LSB = 0;
3032  uint16_t buffer_MSB = 0;
3033  bool continueFunction = true;
3034 
3035  if (registerSet == 0u) {
3036  /* RDCVA command -> voltage register group A */
3037  cellOffset = 0;
3038  } else if (registerSet == 1u) {
3039  /* RDCVB command -> voltage register group B */
3040  cellOffset = 3;
3041  } else if (registerSet == 2u) {
3042  /* RDCVC command -> voltage register group C */
3043  cellOffset = 6;
3044  } else if (registerSet == 3u) {
3045  /* RDCVD command -> voltage register group D */
3046  cellOffset = 9;
3047  } else if (registerSet == 4u) {
3048  /* RDCVD command -> voltage register group E (only for 15 and 18 cell version) */
3049  cellOffset = 12;
3050  } else if (registerSet == 5u) {
3051  /* RDCVD command -> voltage register group F (only for 18 cell version) */
3052  cellOffset = 15;
3053  } else {
3054  continueFunction = false;
3055  }
3056 
3057  if (continueFunction == true) {
3058  /* Calculate bitmask for valid flags */
3059  bitmask |= 0x07u << cellOffset; /* 0x07: three voltages in each register */
3060 
3061  /* reinitialize index counter at begin of cycle */
3062  if (cellOffset == 0u) {
3063  (ltc_state->ltcData.usedCellIndex[stringNumber]) = 0;
3064  }
3065 
3066  /* Retrieve data without command and CRC*/
3067  for (uint16_t m = 0u; m < LTC_N_LTC; m++) {
3068  uint16_t incrementations = 0u;
3069 
3070  /* parse all three voltages (3 * 2bytes) contained in one register */
3071  for (uint8_t c = 0u; c < 3u; c++) {
3072  /* index considering maximum number of cells */
3073  voltage_index = c + cellOffset;
3074 
3075  if (ltc_voltage_input_used[voltage_index] == 1u) {
3076  buffer_MSB = pRxBuff[4u + (2u * c) + (m * 8u) + 1u];
3077  buffer_LSB = pRxBuff[4u + (2u * c) + (m * 8u)];
3078  val_ui = buffer_LSB | (buffer_MSB << 8u);
3079  /* val_ui = *((uint16_t *)(&pRxBuff[4+2*j+i*8])); */
3080  voltage = ((val_ui)) * 100e-6f * 1000.0f; /* Unit V -> in mV */
3081 
3082  /* Check PEC for every LTC in the daisy-chain */
3083  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][m] == true) {
3084  ltc_state->ltcData.cellVoltage
3085  ->cellVoltage_mV[stringNumber]
3086  [(ltc_state->ltcData.usedCellIndex[stringNumber]) +
3087  (m * BS_NR_OF_CELL_BLOCKS_PER_MODULE)] = voltage;
3088  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3089  ltc_state->ltcData.cellVoltage
3090  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] &= bitmask;
3091  } else {
3092  /* PEC_valid == false: Invalidate only flags of this voltage register */
3093  ltc_state->ltcData.cellVoltage
3094  ->invalidCellVoltage[stringNumber][(m / LTC_NUMBER_OF_LTC_PER_MODULE)] |= bitmask;
3095  }
3096 
3097  (ltc_state->ltcData.usedCellIndex[stringNumber])++;
3098  incrementations++;
3099 
3100  if ((ltc_state->ltcData.usedCellIndex[stringNumber]) > BS_NR_OF_CELL_BLOCKS_PER_MODULE) {
3101  break;
3102  }
3103  }
3104  }
3105 
3106  /* Restore start value for next module in the daisy-chain. Only
3107  * decrement used cell index if current module is not the last
3108  * module in the daisy-chain. */
3109  if ((m + 1u) < LTC_N_LTC) {
3110  (ltc_state->ltcData.usedCellIndex[stringNumber]) -= incrementations;
3111  }
3112  }
3113  }
3114 }
3115 
3116 /**
3117  * @brief saves the GPIO voltage values read from the LTC daisy-chain.
3118  *
3119  * After a voltage measurement was initiated to measure the voltages on all GPIOs,
3120  * the result is read via SPI from the daisy-chain. In order to read the result of all GPIO measurements,
3121  * it is necessary to read auxiliary register A and B.
3122  * Only one register can be read at a time.
3123  * This function is called to store the result from the transmission in a buffer.
3124  *
3125  * @param ltc_state state of the ltc state machine
3126  * @param pRxBuff receive buffer
3127  * @param registerSet voltage register that was read (auxiliary register A, B, C or D)
3128  * @param stringNumber string addressed
3129  *
3130  */
3132  LTC_STATE_s *ltc_state,
3133  uint16_t *pRxBuff,
3134  uint8_t registerSet,
3135  uint8_t stringNumber) {
3136  uint8_t i_offset = 0;
3137  uint32_t bitmask = 0;
3138  uint16_t buffer_LSB = 0;
3139  uint16_t buffer_MSB = 0;
3140 
3141  if (registerSet == 0u) {
3142  /* RDAUXA command -> GPIO register group A */
3143  i_offset = 0;
3144  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3145  /* Retrieve data without command and CRC*/
3146  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3147  /* Check if PEC is valid */
3148  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3149  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3150  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3151  /* values received in 100uV -> divide by 10 to convert to mV */
3152  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3153  buffer_LSB = pRxBuff[4u + (i * 8u)];
3154  ltc_state->ltcData.allGpioVoltages
3155  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3156  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3157  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3158  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3159  buffer_LSB = pRxBuff[6u + (i * 8u)];
3160  ltc_state->ltcData.allGpioVoltages
3161  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3162  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3163  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3164  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3165  buffer_LSB = pRxBuff[8u + (i * 8u)];
3166  ltc_state->ltcData.allGpioVoltages
3167  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3168  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3169  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3170  } else {
3171  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3172  }
3173  }
3174  } else if (registerSet == 1u) {
3175  /* RDAUXB command -> GPIO register group B */
3176  i_offset = 3;
3177  bitmask = 0x03u << i_offset; /* 0x03: two temperatures in this register */
3178  /* Retrieve data without command and CRC*/
3179  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3180  /* Check if PEC is valid */
3181  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3182  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3183  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3184  /* values received in 100uV -> divide by 10 to convert to mV */
3185  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3186  buffer_LSB = pRxBuff[4u + (i * 8u)];
3187  ltc_state->ltcData.allGpioVoltages
3188  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3189  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3190  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3191  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3192  buffer_LSB = pRxBuff[6u + (i * 8u)];
3193  ltc_state->ltcData.allGpioVoltages
3194  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3195  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3196  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3197  } else {
3198  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3199  }
3200  }
3201  } else if (registerSet == 2u) {
3202  /* RDAUXC command -> GPIO register group C, for 18 cell version */
3203  i_offset = 5;
3204  bitmask = 0x07u << i_offset; /* 0x07: three temperatures in this register */
3205  /* Retrieve data without command and CRC*/
3206  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3207  /* Check if PEC is valid */
3208  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3209  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3210  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3211  /* values received in 100uV -> divide by 10 to convert to mV */
3212  buffer_MSB = pRxBuff[4u + (i * 8u) + 1u];
3213  buffer_LSB = pRxBuff[4u + (i * 8u)];
3214  ltc_state->ltcData.allGpioVoltages
3215  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3216  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3217  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][0 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[4+i*8]))/10; */
3218  buffer_MSB = pRxBuff[6u + (i * 8u) + 1u];
3219  buffer_LSB = pRxBuff[6u + (i * 8u)];
3220  ltc_state->ltcData.allGpioVoltages
3221  ->gpioVoltages_mV[stringNumber][1u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3222  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3223  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][1 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[6+i*8]))/10; */
3224  buffer_MSB = pRxBuff[8u + (i * 8u) + 1u];
3225  buffer_LSB = pRxBuff[8u + (i * 8u)];
3226  ltc_state->ltcData.allGpioVoltages
3227  ->gpioVoltages_mV[stringNumber][2u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3228  ((buffer_LSB | (buffer_MSB << 8u))) / 10u;
3229  /* ltc_state->ltcData.allGpioVoltages->gpiovoltage[stringNumber][2 + i_offset + BS_NR_OF_GPIOS_PER_MODULE*i]= *((uint16_t *)(&pRxBuff[8+i*8]))/10; */
3230  } else {
3231  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3232  }
3233  }
3234  } else if (registerSet == 3u) {
3235  /* RDAUXD command -> GPIO register group D, for 18 cell version */
3236  i_offset = 8;
3237  bitmask = 0x01u << i_offset; /* 0x01: one temperature in this register */
3238  /* Retrieve data without command and CRC*/
3239  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3240  /* Check if PEC is valid */
3241  if (ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] == true) {
3242  bitmask = ~bitmask; /* negate bitmask to only validate flags of this voltage register */
3243  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] &= bitmask;
3244  /* values received in 100uV -> divide by 10 to convert to mV */
3245  ltc_state->ltcData.allGpioVoltages
3246  ->gpioVoltages_mV[stringNumber][0u + i_offset + (BS_NR_OF_GPIOS_PER_MODULE * i)] =
3247  *((uint16_t *)(&pRxBuff[4u + (i * 8u)])) / 10u;
3248  } else {
3249  ltc_state->ltcData.allGpioVoltages->invalidGpioVoltages[stringNumber][i] |= bitmask;
3250  }
3251  }
3252  } else {
3253  ; /* Nothing to do */
3254  }
3255 }
3256 
3257 /**
3258  * @brief checks if the multiplexers acknowledged transmission.
3259  *
3260  * The RDCOMM command can be used to read the answer of the multiplexers to a
3261  * I2C transmission.
3262  * This function determines if the communication with the multiplexers was
3263  * successful or not.
3264  * The array error table is updated to locate the multiplexers that did not
3265  * acknowledge transmission.
3266  *
3267  * @param ltc_state state of the ltc state machine
3268  * @param pRxBuff receive buffer
3269  * @param mux multiplexer to be addressed (multiplexer ID)
3270  * @param stringNumber string addressed
3271  *
3272  * @return muxError STD_OK is there was no error, STD_NOT_OK if there was errors
3273  */
3274 static STD_RETURN_TYPE_e LTC_I2cCheckAck(LTC_STATE_s *ltc_state, uint16_t *pRxBuff, uint8_t mux, uint8_t stringNumber) {
3275  STD_RETURN_TYPE_e muxError = STD_OK;
3276 
3277  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
3278  if ((pRxBuff[4u + 1u + (LTC_NUMBER_OF_LTC_PER_MODULE * i * 8u)] & 0x0Fu) != 0x07u) { /* ACK = 0xX7 */
3279  if (LTC_DISCARD_MUX_CHECK == false) {
3280  if (mux == 0u) {
3281  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 1;
3282  }
3283  if (mux == 1u) {
3284  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 1;
3285  }
3286  if (mux == 2u) {
3287  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 1;
3288  }
3289  if (mux == 3u) {
3290  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 1;
3291  }
3292  }
3293  muxError = STD_NOT_OK;
3294  } else {
3295  if (mux == 0u) {
3296  ltc_state->ltcData.errorTable->mux0[stringNumber][i] = 0;
3297  }
3298  if (mux == 1u) {
3299  ltc_state->ltcData.errorTable->mux1[stringNumber][i] = 0;
3300  }
3301  if (mux == 2u) {
3302  ltc_state->ltcData.errorTable->mux2[stringNumber][i] = 0;
3303  }
3304  if (mux == 3u) {
3305  ltc_state->ltcData.errorTable->mux3[stringNumber][i] = 0;
3306  }
3307  }
3308  }
3309 
3310  if (LTC_DISCARD_MUX_CHECK == true) {
3311  muxError = STD_OK;
3312  }
3313  return muxError;
3314 }
3315 
3316 /**
3317  * @brief initialize the daisy-chain.
3318  *
3319  * To initialize the LTC6804 daisy-chain, a dummy byte (0x00) is sent.
3320  *
3321  * @param pSpiInterface pointer to SPI configuration
3322  * @param pTxBuff transmit buffer
3323  * @param pRxBuff receive buffer
3324  * @param frameLength number of words to transmit
3325  *
3326  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3327  *
3328  */
3330  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3331  uint16_t *pTxBuff,
3332  uint16_t *pRxBuff,
3333  uint32_t frameLength) {
3334  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3335 
3336  uint8_t PEC_Check[6];
3337  uint16_t PEC_result = 0;
3338 
3339  /* now construct the message to be sent: it contains the wanted data, PLUS the needed PECs */
3340  pTxBuff[0] = ltc_cmdWRCFG[0];
3341  pTxBuff[1] = ltc_cmdWRCFG[1];
3342  pTxBuff[2] = ltc_cmdWRCFG[2];
3343  pTxBuff[3] = ltc_cmdWRCFG[3];
3344 
3345  /* set REFON bit to 1 */
3346  /* data for the configuration */
3347  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3348  /* FC = disable all pull-downs, REFON = 1, DTEN = 0, ADCOPT = 0 */
3349  pTxBuff[4u + (i * 8u)] = 0xFC;
3350  pTxBuff[5u + (i * 8u)] = 0x00;
3351  pTxBuff[6u + (i * 8u)] = 0x00;
3352  pTxBuff[7u + (i * 8u)] = 0x00;
3353  pTxBuff[8u + (i * 8u)] = 0x00;
3354  pTxBuff[9u + (i * 8u)] = 0x00;
3355 
3356  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3357  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3358  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3359  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3360  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3361  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3362 
3363  PEC_result = LTC_pec15_calc(6, PEC_Check);
3364  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3365  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3366  } /* end for */
3367 
3368  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3369 
3370  return retVal;
3371 }
3372 
3373 /**
3374  * @brief sets the balancing according to the control values read in the database.
3375  *
3376  * To set balancing for the cells, the corresponding bits have to be written in the configuration register.
3377  * The LTC driver only executes the balancing orders written by the BMS in the database.
3378  *
3379  * @param ltc_state state of the ltc state machine
3380  * @param pSpiInterface pointer to SPI configuration
3381  * @param pTxBuff transmit buffer
3382  * @param pRxBuff receive buffer
3383  * @param frameLength number of words to transmit
3384  * @param registerSet register Set, 0: cells 1 to 12 (WRCFG), 1: cells 13 to 15/18 (WRCFG2)
3385  * @param stringNumber string addressed
3386  *
3387  * @return STD_OK if dummy byte was sent correctly by SPI, STD_NOT_OK otherwise
3388  *
3389  */
3391  LTC_STATE_s *ltc_state,
3392  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3393  uint16_t *pTxBuff,
3394  uint16_t *pRxBuff,
3395  uint32_t frameLength,
3396  uint8_t registerSet,
3397  uint8_t stringNumber) {
3398  STD_RETURN_TYPE_e retVal = STD_OK;
3399 
3400  uint8_t PEC_Check[6];
3401  uint16_t PEC_result = 0;
3402 
3403  LTC_GetBalancingControlValues(ltc_state);
3404 
3405  if (registerSet == 0u) { /* cells 1 to 12, WRCFG */
3406  pTxBuff[0] = ltc_cmdWRCFG[0];
3407  pTxBuff[1] = ltc_cmdWRCFG[1];
3408  pTxBuff[2] = ltc_cmdWRCFG[2];
3409  pTxBuff[3] = ltc_cmdWRCFG[3];
3410  for (uint16_t j = 0; j < BS_NR_OF_MODULES_PER_STRING; j++) {
3411  /* The daisy-chain works like a shift register, so the order has to be reversed:
3412  when addressing e.g. the first module in the daisy-chain, the data will be sent last on the SPI bus and
3413  when addressing e.g. the last module in the daisy-chain, the data will be sent first on the SPI bus */
3414  const uint16_t reverseModuleNumber = BS_NR_OF_MODULES_PER_STRING - j - 1u;
3415 
3416  /* FC = disable all pull-downs, REFON = 1 (reference always on), DTEN off, ADCOPT = 0 */
3417  pTxBuff[4u + (reverseModuleNumber * 8u)] = 0xFC;
3418  pTxBuff[5u + (reverseModuleNumber * 8u)] = 0x00;
3419  pTxBuff[6u + (reverseModuleNumber * 8u)] = 0x00;
3420  pTxBuff[7u + (reverseModuleNumber * 8u)] = 0x00;
3421  pTxBuff[8u + (reverseModuleNumber * 8u)] = 0x00;
3422  pTxBuff[9u + (reverseModuleNumber * 8u)] = 0x00;
3423 
3424  if (ltc_state->ltcData.balancingControl
3425  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 0u] == 1u) {
3426  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x01u;
3427  }
3428  if (ltc_state->ltcData.balancingControl
3429  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 1u] == 1u) {
3430  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x02u;
3431  }
3432  if (ltc_state->ltcData.balancingControl
3433  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 2u] == 1u) {
3434  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x04u;
3435  }
3436  if (ltc_state->ltcData.balancingControl
3437  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 3u] == 1u) {
3438  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x08u;
3439  }
3440  if (ltc_state->ltcData.balancingControl
3441  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 4u] == 1u) {
3442  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x10u;
3443  }
3444  if (ltc_state->ltcData.balancingControl
3445  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 5u] == 1u) {
3446  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x20u;
3447  }
3448  if (ltc_state->ltcData.balancingControl
3449  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 6u] == 1u) {
3450  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x40u;
3451  }
3452  if (ltc_state->ltcData.balancingControl
3453  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 7u] == 1u) {
3454  pTxBuff[8u + (reverseModuleNumber * 8u)] |= 0x80u;
3455  }
3456  if (ltc_state->ltcData.balancingControl
3457  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 8u] == 1u) {
3458  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x01u;
3459  }
3460  if (ltc_state->ltcData.balancingControl
3461  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 9u] == 1u) {
3462  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x02u;
3463  }
3464  if (ltc_state->ltcData.balancingControl
3465  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 10u] == 1u) {
3466  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x04u;
3467  }
3468  if (ltc_state->ltcData.balancingControl
3469  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 11u] == 1u) {
3470  pTxBuff[9u + (reverseModuleNumber * 8u)] |= 0x08u;
3471  }
3472 
3473  PEC_Check[0] = pTxBuff[4u + (reverseModuleNumber * 8u)];
3474  PEC_Check[1] = pTxBuff[5u + (reverseModuleNumber * 8u)];
3475  PEC_Check[2] = pTxBuff[6u + (reverseModuleNumber * 8u)];
3476  PEC_Check[3] = pTxBuff[7u + (reverseModuleNumber * 8u)];
3477  PEC_Check[4] = pTxBuff[8u + (reverseModuleNumber * 8u)];
3478  PEC_Check[5] = pTxBuff[9u + (reverseModuleNumber * 8u)];
3479 
3480  PEC_result = LTC_pec15_calc(6, PEC_Check);
3481  pTxBuff[10u + (reverseModuleNumber * 8u)] = (PEC_result >> 8u) & 0xFFu;
3482  pTxBuff[11u + (reverseModuleNumber * 8u)] = PEC_result & 0xFFu;
3483  }
3484  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3485  } else if (registerSet == 1u) { /* cells 13 to 15/18 WRCFG2 */
3486  pTxBuff[0] = ltc_cmdWRCFG2[0];
3487  pTxBuff[1] = ltc_cmdWRCFG2[1];
3488  pTxBuff[2] = ltc_cmdWRCFG2[2];
3489  pTxBuff[3] = ltc_cmdWRCFG2[3];
3490  for (uint16_t j = 0; j < BS_NR_OF_MODULES_PER_STRING; j++) {
3491  /* The daisy-chain works like a shift register, so the order has to be reversed:
3492  when addressing e.g. the first module in the daisy-chain, the data will be sent last on the SPI bus and
3493  when addressing e.g. the last module in the daisy-chain, the data will be sent first on the SPI bus */
3494  const uint16_t reverseModuleNumber = BS_NR_OF_MODULES_PER_STRING - j - 1u;
3495 
3496  /* 0x0F = disable pull-downs on GPIO6-9 */
3497  pTxBuff[4u + (reverseModuleNumber * 8u)] = 0x0F;
3498  pTxBuff[5u + (reverseModuleNumber * 8u)] = 0x00;
3499  pTxBuff[6u + (reverseModuleNumber * 8u)] = 0x00;
3500  pTxBuff[7u + (reverseModuleNumber * 8u)] = 0x00;
3501  pTxBuff[8u + (reverseModuleNumber * 8u)] = 0x00;
3502  pTxBuff[9u + (reverseModuleNumber * 8u)] = 0x00;
3503 
3504  if (ltc_state->ltcData.balancingControl
3505  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 12u] == 1u) {
3506  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x10u;
3507  }
3508  if (ltc_state->ltcData.balancingControl
3509  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 13u] == 1u) {
3510  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x20u;
3511  }
3512  if (ltc_state->ltcData.balancingControl
3513  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 14u] == 1u) {
3514  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x40u;
3515  }
3516  if (BS_NR_OF_CELL_BLOCKS_PER_MODULE > 15u) {
3517  if (ltc_state->ltcData.balancingControl
3518  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 15u] == 1u) {
3519  pTxBuff[4u + (reverseModuleNumber * 8u)] |= 0x80u;
3520  }
3521  if (ltc_state->ltcData.balancingControl
3522  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 16u] == 1u) {
3523  pTxBuff[5u + (reverseModuleNumber * 8u)] |= 0x01u;
3524  }
3525  if (ltc_state->ltcData.balancingControl
3526  ->balancingState[stringNumber][(j * (BS_NR_OF_CELL_BLOCKS_PER_MODULE)) + 17u] == 1u) {
3527  pTxBuff[5u + (reverseModuleNumber * 8u)] |= 0x02u;
3528  }
3529  }
3530 
3531  PEC_Check[0] = pTxBuff[4u + (reverseModuleNumber * 8u)];
3532  PEC_Check[1] = pTxBuff[5u + (reverseModuleNumber * 8u)];
3533  PEC_Check[2] = pTxBuff[6u + (reverseModuleNumber * 8u)];
3534  PEC_Check[3] = pTxBuff[7u + (reverseModuleNumber * 8u)];
3535  PEC_Check[4] = pTxBuff[8u + (reverseModuleNumber * 8u)];
3536  PEC_Check[5] = pTxBuff[9u + (reverseModuleNumber * 8u)];
3537 
3538  PEC_result = LTC_pec15_calc(6, PEC_Check);
3539  pTxBuff[10u + (reverseModuleNumber * 8u)] = (PEC_result >> 8u) & 0xFFu;
3540  pTxBuff[11u + (reverseModuleNumber * 8u)] = PEC_result & 0xFFu;
3541  }
3542  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3543  } else {
3544  retVal = STD_NOT_OK;
3545  }
3546  return retVal;
3547 }
3548 
3549 /**
3550  * @brief resets the error table.
3551  *
3552  * This function should be called during initialization or before starting a new measurement cycle
3553  *
3554  * @param ltc_state: state of the ltc state machine
3555  *
3556  */
3557 static void LTC_ResetErrorTable(LTC_STATE_s *ltc_state) {
3558  for (uint8_t s = 0u; s < BS_NR_OF_STRINGS; s++) {
3559  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3560  ltc_state->ltcData.errorTable->PEC_valid[s][i] = false;
3561  ltc_state->ltcData.errorTable->mux0[s][i] = 0;
3562  ltc_state->ltcData.errorTable->mux1[s][i] = 0;
3563  ltc_state->ltcData.errorTable->mux2[s][i] = 0;
3564  ltc_state->ltcData.errorTable->mux3[s][i] = 0;
3565  }
3566  }
3567 }
3568 
3569 /**
3570  * @brief brief missing
3571  *
3572  * Gets the measurement time needed by the LTC chip, depending on the measurement mode and the number of channels.
3573  * For all cell voltages or all 5 GPIOS, the measurement time is the same.
3574  * For 2 cell voltages or 1 GPIO, the measurement time is the same.
3575  * As a consequence, this function is used for cell voltage and for GPIO measurement.
3576  *
3577  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3578  * @param adcMeasCh number of channels measured for GPIOS (one at a time for multiplexers or all five GPIOs)
3579  * or number of cell voltage measured (2 cells or all cells)
3580  *
3581  * @return retVal measurement time in ms
3582  */
3583 static uint16_t LTC_GetMeasurementTimeCycle(LTC_ADCMODE_e adcMode, LTC_ADCMEAS_CHAN_e adcMeasCh) {
3584  uint16_t retVal = LTC_ADCMEAS_UNDEFINED; /* default */
3585 
3586  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_CELLS) {
3587  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3589  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3591  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3593  }
3594  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
3595  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3597  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3599  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3601  }
3602  } else if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_GPIOS) {
3603  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3605  } else if ((adcMode == LTC_ADCMODE_NORMAL_DCP0) || (adcMode == LTC_ADCMODE_NORMAL_DCP1)) {
3607  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3609  }
3610  } else if (
3611  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) ||
3612  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) || (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO4) ||
3613  (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO5)) {
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 {
3622  retVal = LTC_ADCMEAS_UNDEFINED;
3623  }
3624 
3625  return retVal;
3626 }
3627 
3628 /**
3629  * @brief tells the LTC daisy-chain to start measuring the voltage on all cells.
3630  *
3631  * This function sends an instruction to the daisy-chain via SPI, in order to start voltage measurement for all cells.
3632  *
3633  * @param pSpiInterface pointer to SPI configuration
3634  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3635  * @param adcMeasCh number of cell voltage measured (2 cells or all cells)
3636  *
3637  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3638  *
3639  */
3641  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3642  LTC_ADCMODE_e adcMode,
3643  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3644  STD_RETURN_TYPE_e retVal = STD_OK;
3645 
3646  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_CELLS) {
3647  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3648  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_fast_DCP0);
3649  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3650  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_normal_DCP0);
3651  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3652  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_filtered_DCP0);
3653  } else if (adcMode == LTC_ADCMODE_FAST_DCP1) {
3654  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_fast_DCP1);
3655  } else if (adcMode == LTC_ADCMODE_NORMAL_DCP1) {
3656  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_normal_DCP1);
3657  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP1) {
3658  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_filtered_DCP1);
3659  } else {
3660  retVal = STD_NOT_OK;
3661  }
3662  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_TWOCELLS) {
3663  if (adcMode == LTC_ADCMODE_FAST_DCP0) {
3664  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADCV_fast_DCP0_twocells);
3665  } else {
3666  retVal = STD_NOT_OK;
3667  }
3668  } else {
3669  retVal = STD_NOT_OK;
3670  }
3671  return retVal;
3672 }
3673 
3674 /**
3675  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3676  *
3677  * This function sends an instruction to the daisy-chain via SPI to start the measurement.
3678  *
3679  * @param pSpiInterface pointer to SPI configuration
3680  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3681  * @param adcMeasCh number of channels measured for GPIOS (one at a time, typically when multiplexers are used, or all five GPIOs)
3682  *
3683  * @return retVal #STD_OK if dummy byte was sent correctly by SPI, #STD_NOT_OK otherwise
3684  *
3685  */
3687  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3688  LTC_ADCMODE_e adcMode,
3689  LTC_ADCMEAS_CHAN_e adcMeasCh) {
3690  STD_RETURN_TYPE_e retVal;
3691 
3692  if (adcMeasCh == LTC_ADCMEAS_ALLCHANNEL_GPIOS) {
3693  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3694  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_ALLGPIOS);
3695  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3696  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_ALLGPIOS);
3697  } else {
3698  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3699  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_ALLGPIOS);
3700  }
3701  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO1) {
3702  /* Single Channel */
3703  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3704  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_GPIO1);
3705  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3706  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_GPIO1);
3707  } else {
3708  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3709 
3710  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_GPIO1);
3711  }
3712  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO2) {
3713  /* Single Channel */
3714  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3715  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_GPIO2);
3716  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3717  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_GPIO2);
3718  } else {
3719  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3720 
3721  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_GPIO2);
3722  }
3723  } else if (adcMeasCh == LTC_ADCMEAS_SINGLECHANNEL_GPIO3) {
3724  /* Single Channel */
3725  if ((adcMode == LTC_ADCMODE_FAST_DCP0) || (adcMode == LTC_ADCMODE_FAST_DCP1)) {
3726  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_fast_GPIO3);
3727  } else if ((adcMode == LTC_ADCMODE_FILTERED_DCP0) || (adcMode == LTC_ADCMODE_FILTERED_DCP1)) {
3728  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_filtered_GPIO3);
3729  } else {
3730  /*if (adcMode == LTC_ADCMODE_NORMAL_DCP0 || adcMode == LTC_ADCMODE_NORMAL_DCP1)*/
3731 
3732  retVal = LTC_TransmitCommand(pSpiInterface, ltc_cmdADAX_normal_GPIO3);
3733  }
3734  } else {
3735  retVal = STD_NOT_OK;
3736  }
3737 
3738  return retVal;
3739 }
3740 
3741 /**
3742  * @brief tells LTC daisy-chain to start measuring the voltage on GPIOS.
3743  *
3744  * This function sends an instruction to the daisy-chain via SPI to start the measurement.
3745  *
3746  * @param pSpiInterface pointer to SPI configuration
3747  * @param adcMode LTC ADCmeasurement mode (fast, normal or filtered)
3748  * @param PUP pull-up bit for pull-up or pull-down current (0: pull-down, 1: pull-up)
3749  *
3750  * @return retVal #STD_OK if command was sent correctly by SPI, #STD_NOT_OK otherwise
3751  *
3752  */
3754  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3755  LTC_ADCMODE_e adcMode,
3756  uint8_t PUP) {
3757  STD_RETURN_TYPE_e retval = STD_NOT_OK;
3758  if (PUP == 0u) {
3759  /* pull-down current */
3760  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3761  retval = LTC_TransmitCommand(pSpiInterface, ltc_BC_cmdADOW_PDOWN_normal_DCP0);
3762  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3764  } else {
3765  retval = STD_NOT_OK;
3766  }
3767  } else if (PUP == 1u) {
3768  /* pull-up current */
3769  if (adcMode == LTC_ADCMODE_NORMAL_DCP0) {
3770  retval = LTC_TransmitCommand(pSpiInterface, ltc_BC_cmdADOW_PUP_normal_DCP0);
3771  } else if (adcMode == LTC_ADCMODE_FILTERED_DCP0) {
3772  retval = LTC_TransmitCommand(pSpiInterface, ltc_BC_cmdADOW_PUP_filtered_DCP0);
3773  } else {
3774  retval = STD_NOT_OK;
3775  }
3776  }
3777  return retval;
3778 }
3779 
3780 /**
3781  * @brief checks if the data received from the daisy-chain is not corrupt.
3782  *
3783  * This function computes the PEC (CRC) from the data received by the daisy-chain.
3784  * It compares it with the PEC sent by the LTCs.
3785  * If there are errors, the array LTC_ErrorTable is updated to locate the LTCs in daisy-chain
3786  * that transmitted corrupt data.
3787  *
3788  * @param ltc_state state of the ltc state machine
3789  * @param DataBufferSPI_RX_with_PEC data obtained from the SPI transmission
3790  * @param stringNumber string addressed
3791  *
3792  * @return retVal STD_OK if PEC check is OK, STD_NOT_OK otherwise
3793  *
3794  */
3796  LTC_STATE_s *ltc_state,
3797  uint16_t *DataBufferSPI_RX_with_PEC,
3798  uint8_t stringNumber) {
3799  STD_RETURN_TYPE_e retVal = STD_OK;
3800  uint8_t PEC_TX[2];
3801  uint16_t PEC_result = 0;
3802  uint8_t PEC_Check[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3803 
3804  /* check all PECs and put data without command and PEC in DataBufferSPI_RX (easier to use) */
3805  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3806  PEC_Check[0] = DataBufferSPI_RX_with_PEC[4u + (i * 8u)];
3807  PEC_Check[1] = DataBufferSPI_RX_with_PEC[5u + (i * 8u)];
3808  PEC_Check[2] = DataBufferSPI_RX_with_PEC[6u + (i * 8u)];
3809  PEC_Check[3] = DataBufferSPI_RX_with_PEC[7u + (i * 8u)];
3810  PEC_Check[4] = DataBufferSPI_RX_with_PEC[8u + (i * 8u)];
3811  PEC_Check[5] = DataBufferSPI_RX_with_PEC[9u + (i * 8u)];
3812 
3813  PEC_result = LTC_pec15_calc(6, PEC_Check);
3814  PEC_TX[0] = (uint8_t)((PEC_result >> 8u) & 0xFFu);
3815  PEC_TX[1] = (uint8_t)(PEC_result & 0xFFu);
3816 
3817  /* if calculated PEC not equal to received PEC */
3818  if ((PEC_TX[0] != DataBufferSPI_RX_with_PEC[10u + (i * 8u)]) ||
3819  (PEC_TX[1] != DataBufferSPI_RX_with_PEC[11u + (i * 8u)])) {
3820  /* update error table of the corresponding LTC only if PEC check is activated */
3821  if (LTC_DISCARD_PEC == false) {
3822  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = false;
3823  retVal = STD_NOT_OK;
3824  } else {
3825  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3826  }
3827  } else {
3828  /* update error table of the corresponding LTC */
3829  ltc_state->ltcData.errorTable->PEC_valid[stringNumber][i] = true;
3830  }
3831  }
3832  return retVal;
3833 }
3834 
3835 /**
3836  * @brief send command to the LTC daisy-chain and receives data from the LTC daisy-chain.
3837  *
3838  * This is the core function to receive data from the LTC6804 daisy-chain.
3839  * A 2 byte command is sent with the corresponding PEC. Example: read configuration register (RDCFG).
3840  * Only command has to be set, the function calculates the PEC automatically.
3841  * The data send is:
3842  * 2 bytes (COMMAND) 2 bytes (PEC)
3843  * The data received is:
3844  * 6 bytes (LTC1) 2 bytes (PEC) + 6 bytes (LTC2) 2 bytes (PEC) + 6 bytes (LTC3) 2 bytes (PEC) + ... + 6 bytes (LTC{LTC_N_LTC}) 2 bytes (PEC)
3845  *
3846  * The function does not check the PECs. This has to be done elsewhere.
3847  *
3848  * @param Command command sent to the daisy-chain
3849  * @param pSpiInterface pointer to SPI configuration
3850  * @param pTxBuff transmit buffer
3851  * @param pRxBuff receive buffer
3852  * @param frameLength number of words to transmit
3853  *
3854  * @return statusSPI #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
3855  *
3856  */
3858  uint16_t *Command,
3859  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3860  uint16_t *pTxBuff,
3861  uint16_t *pRxBuff,
3862  uint32_t frameLength) {
3863  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3864 
3865  /* DataBufferSPI_RX_with_PEC contains the data to receive.
3866  The transmission function checks the PECs.
3867  It constructs DataBufferSPI_RX, which contains the received data without PEC (easier to use). */
3868 
3869  for (uint16_t i = 0; i < LTC_N_BYTES_FOR_DATA_TRANSMISSION; i++) {
3870  pTxBuff[i] = 0x00;
3871  }
3872 
3873  pTxBuff[0] = Command[0];
3874  pTxBuff[1] = Command[1];
3875  pTxBuff[2] = Command[2];
3876  pTxBuff[3] = Command[3];
3877 
3878  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3879 
3880  return retVal;
3881 }
3882 
3883 /**
3884  * @brief sends command and data to the LTC daisy-chain.
3885  *
3886  * This is the core function to transmit data to the LTC6804 daisy-chain.
3887  * The data sent is:
3888  * COMMAND + 6 bytes (LTC1) + 6 bytes (LTC2) + 6 bytes (LTC3) + ... + 6 bytes (LTC{LTC_N_LTC})
3889  * A 2 byte command is sent with the corresponding PEC. Example: write configuration register (WRCFG).
3890  * THe command has to be set and then the function calculates the PEC automatically.
3891  * The function calculates the needed PEC to send the data to the daisy-chain. The sent data has the format:
3892  * 2 byte-COMMAND (2 bytes PEC) + 6 bytes (LTC1) (2 bytes PEC) + 6 bytes (LTC2) (2 bytes PEC) + 6 bytes (LTC3) (2 bytes PEC) + ... + 6 bytes (LTC{LTC_N_LTC}) (2 bytes PEC)
3893  *
3894  * The function returns 0. The only way to check if the transmission was successful is to read the results of the write operation.
3895  * (example: read configuration register after writing to it)
3896  *
3897  * @param Command command sent to the daisy-chain
3898  * @param pSpiInterface pointer to SPI configuration
3899  * @param pTxBuff transmit buffer
3900  * @param pRxBuff receive buffer
3901  * @param frameLength number of words to transmit
3902  *
3903  * @return STD_OK if SPI transmission is OK, STD_NOT_OK otherwise
3904  *
3905  */
3907  uint16_t *Command,
3908  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3909  uint16_t *pTxBuff,
3910  uint16_t *pRxBuff,
3911  uint32_t frameLength) {
3912  STD_RETURN_TYPE_e retVal = STD_NOT_OK;
3913 
3914  uint16_t PEC_result = 0;
3915  uint8_t PEC_Check[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
3916 
3917  pTxBuff[0] = Command[0];
3918  pTxBuff[1] = Command[1];
3919  pTxBuff[2] = Command[2];
3920  pTxBuff[3] = Command[3];
3921 
3922  /* Calculate PEC of all data (1 PEC value for 6 bytes) */
3923  for (uint16_t i = 0u; i < LTC_N_LTC; i++) {
3924  PEC_Check[0] = pTxBuff[4u + (i * 8u)];
3925  PEC_Check[1] = pTxBuff[5u + (i * 8u)];
3926  PEC_Check[2] = pTxBuff[6u + (i * 8u)];
3927  PEC_Check[3] = pTxBuff[7u + (i * 8u)];
3928  PEC_Check[4] = pTxBuff[8u + (i * 8u)];
3929  PEC_Check[5] = pTxBuff[9u + (i * 8u)];
3930 
3931  PEC_result = LTC_pec15_calc(6, PEC_Check);
3932  pTxBuff[10u + (i * 8u)] = (PEC_result >> 8u) & 0xFFu;
3933  pTxBuff[11u + (i * 8u)] = PEC_result & 0xFFu;
3934  }
3935 
3936  retVal = LTC_TransmitReceiveData(pSpiInterface, pTxBuff, pRxBuff, frameLength);
3937 
3938  return retVal;
3939 }
3940 
3941 /**
3942  * @brief configures the data that will be sent to the LTC daisy-chain to configure multiplexer channels.
3943  *
3944  * This function does not sent the data to the multiplexer daisy-chain. This is done
3945  * by the function LTC_SetMuxChannel(), which calls LTC_SetMuxChCommand()..
3946  *
3947  * @param pTxBuff transmit buffer
3948  * @param mux multiplexer ID to be configured (0,1,2 or 3)
3949  * @param channel multiplexer channel to be configured (0 to 7)
3950  *
3951  */
3952 static void LTC_SetMuxChCommand(uint16_t *pTxBuff, uint8_t mux, uint8_t channel) {
3953  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
3954 #if SLAVE_BOARD_VERSION == 2u
3955 
3956  /* using ADG728 */
3957  uint8_t address = 0x98u | ((mux % 4u) << 1u);
3958  uint8_t data = 1u << (channel % 8u);
3959  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
3960  data = 0x00;
3961  }
3962 
3963 #else
3964 
3965  /* using LTC1380 */
3966  uint8_t address = 0x90u | ((mux % 4u) << 1u);
3967  uint8_t data = 0x08u | (channel % 8u);
3968  if (channel == 0xFFu) { /* no channel selected, output of multiplexer is high impedance */
3969  data = 0x00;
3970  }
3971 
3972 #endif
3973 
3974  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | ((address >> 4u) & 0x0Fu); /* 0x6 : LTC6804: ICOM START from Master */
3975  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | ((address << 4u) & 0xF0u);
3976  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | ((data >> 4u) & 0x0Fu);
3977  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | ((data << 4u) & 0xF0u);
3978  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 0x1 : ICOM-STOP */
3979  pTxBuff[9u + (i * 8u)] = 0x00; /* 0x0 : dummy (Dn) */
3980  /* 9: MASTER NACK + STOP (FCOM) */
3981  }
3982 }
3983 
3984 /**
3985  * @brief sends data to the LTC daisy-chain to read EEPROM on slaves.
3986  *
3987  * @param ltc_state state of the ltc state machine
3988  * @param pSpiInterface pointer to SPI configuration
3989  * @param pTxBuff transmit buffer
3990  * @param pRxBuff receive buffer
3991  * @param frameLength number of words to transmit
3992  * @param step first or second stage of read process (0 or 1)
3993  *
3994  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
3995  */
3997  LTC_STATE_s *ltc_state,
3998  SPI_INTERFACE_CONFIG_s *pSpiInterface,
3999  uint16_t *pTxBuff,
4000  uint16_t *pRxBuff,
4001  uint32_t frameLength,
4002  uint8_t step) {
4003  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4004 
4005  /* send WRCOMM to send I2C message to choose channel */
4006  LTC_SetEepromReadCommand(ltc_state, pTxBuff, step);
4007  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4008 
4009  return statusSPI;
4010 }
4011 
4012 /**
4013  * @brief configures the data that will be sent to the LTC daisy-chain to read EEPROM on slaves.
4014  *
4015  * @param ltc_state state of the ltc state machine
4016  * @param pTxBuff transmit buffer
4017  * @param step first or second stage of read process (0 or 1)
4018  *
4019  */
4020 static void LTC_SetEepromReadCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4021  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4022 
4023  uint32_t address = ltc_state->ltcData.slaveControl->eepromReadAddressToUse;
4024 
4025  address &= 0x3FFFFu;
4026  const uint8_t address0 = address >> 16u;
4027  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4028  const uint8_t address2 = address & 0xFFu;
4029 
4030  if (step == 0u) {
4031  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4032  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4033  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4034  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4035  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4036  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4037  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4038  }
4039 
4040  } else { /* step == 1 */
4041  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4042  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4043  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x10u);
4044  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | 0x0Fu;
4045  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0xF0u;
4046  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4047  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4048  }
4049  }
4050 }
4051 
4052 /**
4053  * @brief saves the read values of the external EEPROMs read from the LTC daisy-chain.
4054  *
4055  * @param ltc_state state of the ltc state machine
4056  * @param pRxBuff receive buffer
4057  *
4058  */
4059 static void LTC_EepromSaveReadValue(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4060  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4061 
4062  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4063  ltc_state->ltcData.slaveControl->eepromValueRead[i] = (pRxBuff[6u + (i * 8u)] << 4u) |
4064  ((pRxBuff[7u + (i * 8u)] >> 4u));
4065  }
4066 
4069  ltc_state->ltcData.slaveControl->eepromReadAddressToUse = 0xFFFFFFFF;
4070 
4071  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4072 }
4073 
4074 /**
4075  * @brief sends data to the LTC daisy-chain to write EEPROM on slaves.
4076  *
4077  * @param ltc_state state of the ltc state machine
4078  * @param pSpiInterface pointer to SPI configuration
4079  * @param pTxBuff transmit buffer
4080  * @param pRxBuff receive buffer
4081  * @param frameLength number of words to transmit
4082  * @param step first or second stage of read process (0 or 1)
4083  *
4084  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4085  */
4087  LTC_STATE_s *ltc_state,
4088  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4089  uint16_t *pTxBuff,
4090  uint16_t *pRxBuff,
4091  uint32_t frameLength,
4092  uint8_t step) {
4093  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4094 
4095  /* send WRCOMM to send I2C message to write EEPROM */
4096  LTC_SetEepromWriteCommand(ltc_state, pTxBuff, step);
4097  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4098 
4099  return statusSPI;
4100 }
4101 
4102 /**
4103  * @brief configures the data that will be sent to the LTC daisy-chain to write EEPROM on slaves.
4104  *
4105  * @param ltc_state state of the ltc state machine
4106  * @param pTxBuff transmit buffer
4107  * @param step first or second stage of read process (0 or 1)
4108  *
4109  */
4110 static void LTC_SetEepromWriteCommand(LTC_STATE_s *ltc_state, uint16_t *pTxBuff, uint8_t step) {
4111  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4112 
4113  uint32_t address = ltc_state->ltcData.slaveControl->eepromWriteAddressToUse;
4114 
4115  address &= 0x3FFFFu;
4116  const uint8_t address0 = address >> 16u;
4117  const uint8_t address1 = (address & 0xFFFFu) >> 8u;
4118  const uint8_t address2 = address & 0xFFu;
4119 
4120  if (step == 0u) {
4121  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4122  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | (0x0Au); /* 0x6 : LTC6804: ICOM START from Master */
4123  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (((address0 & 0x03u) << 5u) | 0x00u);
4124  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (address1 >> 4u);
4125  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address1 << 4u);
4126  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (address2 >> 4u);
4127  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK | (address2 << 4u);
4128  }
4129 
4130  } else { /* step == 1 */
4131  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4132  const uint8_t data = ltc_state->ltcData.slaveControl->eepromValueWrite[i];
4133 
4134  pTxBuff[4u + (i * 8u)] = LTC_ICOM_BLANK | (data >> 4u); /* 0x6 : LTC6804: ICOM START from Master */
4135  pTxBuff[5u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | (data << 4u);
4136  pTxBuff[6u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4137  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4138  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT | 0x00u;
4139  pTxBuff[9u + (i * 8u)] = LTC_FCOM_MASTER_NACK_STOP | 0x00u;
4140  }
4141 
4144  ltc_state->ltcData.slaveControl->eepromWriteAddressToUse = 0xFFFFFFFF;
4145 
4146  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4147  }
4148 }
4149 
4150 /**
4151  * @brief sends data to the LTC daisy-chain to configure multiplexer channels.
4152  *
4153  * This function calls the function LTC_SetMuxChCommand() to set the data.
4154  *
4155  * @param pSpiInterface pointer to SPI configuration
4156  * @param pTxBuff transmit buffer
4157  * @param pRxBuff receive buffer
4158  * @param frameLength number of words to transmit
4159  * @param mux multiplexer ID to be configured (0,1,2 or 3)
4160  * @param channel multiplexer channel to be configured (0 to 7)
4161  *
4162  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4163  */
4165  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4166  uint16_t *pTxBuff,
4167  uint16_t *pRxBuff,
4168  uint32_t frameLength,
4169  uint8_t mux,
4170  uint8_t channel) {
4171  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4172 
4173  /* send WRCOMM to send I2C message to choose channel */
4174  LTC_SetMuxChCommand(pTxBuff, mux, channel);
4175  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4176 
4177  return statusSPI;
4178 }
4179 
4180 /**
4181  * @brief sends data to the LTC daisy-chain to communicate via I2C
4182  *
4183  * This function initiates an I2C signal sent by the LTC6804 on the slave boards
4184  *
4185  * @param pSpiInterface pointer to SPI configuration
4186  * @param pTxBuff transmit buffer
4187  * @param pRxBuff receive buffer
4188  * @param frameLength number of words to transmit
4189  * @param cmd_data command data to be sent
4190  *
4191  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4192  */
4194  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4195  uint16_t *pTxBuff,
4196  uint16_t *pRxBuff,
4197  uint32_t frameLength,
4198  uint16_t *cmd_data) {
4199  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4200 
4201  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4202  pTxBuff[4u + (i * 6u)] = cmd_data[0];
4203  pTxBuff[5u + (i * 6u)] = cmd_data[1];
4204 
4205  pTxBuff[6u + (i * 6u)] = cmd_data[2];
4206  pTxBuff[7u + (i * 6u)] = cmd_data[3];
4207 
4208  pTxBuff[8u + (i * 6u)] = cmd_data[4];
4209  pTxBuff[9u + (i * 6u)] = cmd_data[5];
4210  }
4211 
4212  /* send WRCOMM to send I2C message */
4213  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4214 
4215  return statusSPI;
4216 }
4217 
4218 /**
4219  * @brief saves the temperature value of the external temperature sensors read from the LTC daisy-chain.
4220  *
4221  * This function saves the temperature value received from the external temperature sensors
4222  *
4223  * @param ltc_state state of the ltc state machine
4224  * @param pRxBuff receive buffer
4225  *
4226  */
4227 static void LTC_TempSensSaveTemp(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4228  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4229 
4230  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4231  uint8_t temp_tmp[2];
4232  temp_tmp[0] = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4233  temp_tmp[1] = (pRxBuff[8u + (i * 8u)] << 4u) | ((pRxBuff[9u + (i * 8u)] >> 4u));
4234  uint16_t val_i = (temp_tmp[0] << 8u) | (temp_tmp[1]);
4235  val_i = val_i >> 8u;
4236  ltc_state->ltcData.slaveControl->externalTemperatureSensor[i] = val_i;
4237  }
4238 
4239  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4240 }
4241 
4242 /**
4243  * @brief sends data to the LTC daisy-chain to control the user port expander
4244  *
4245  * This function sends a control byte to the register of the user port expander
4246  *
4247  * @param ltc_state state of the ltc state machine
4248  * @param pSpiInterface pointer to SPI configuration
4249  * @param pTxBuff transmit buffer
4250  * @param pRxBuff receive buffer
4251  * @param frameLength number of words to transmit
4252  *
4253  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4254  */
4256  LTC_STATE_s *ltc_state,
4257  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4258  uint16_t *pTxBuff,
4259  uint16_t *pRxBuff,
4260  uint32_t frameLength) {
4261  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4262 
4263  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4264 
4265  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4266  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES_PER_STRING - 1 - i];
4267 
4268  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START |
4269  0x04u; /* 6: ICOM0 start condition, 4: upper nibble of PCA8574 address */
4270  pTxBuff[5u + (i * 8u)] =
4271  0u | LTC_FCOM_MASTER_NACK; /* 0: lower nibble of PCA8574 address + R/W bit, 8: FCOM0 master NACK */
4272 
4273  pTxBuff[6u + (i * 8u)] =
4274  LTC_ICOM_BLANK |
4275  (output_data >> 4u); /* 0: ICOM1 blank, x: upper nibble of PCA8574 data register (0 == pin low) */
4276  pTxBuff[7u + (i * 8u)] =
4277  (uint8_t)(output_data << 4u) |
4278  LTC_FCOM_MASTER_NACK_STOP; /* x: lower nibble of PCA8574 data register, 9: FCOM1 master NACK + STOP */
4279 
4280  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* 7: no transmission, F: dummy data */
4281  pTxBuff[9u + (i * 8u)] = 0; /* F: dummy data, 9: FCOM2 master NACK + STOP */
4282  }
4283 
4284  /* send WRCOMM to send I2C message */
4285  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4286 
4287  return statusSPI;
4288 }
4289 
4290 /**
4291  * @brief saves the received values of the external port expander read from the LTC daisy-chain.
4292  *
4293  * This function saves the received data byte from the external port expander
4294  *
4295  * @param ltc_state state of the ltc state machine
4296  * @param pRxBuff receive buffer
4297  *
4298  */
4299 static void LTC_PortExpanderSaveValues(LTC_STATE_s *ltc_state, uint16_t *pRxBuff) {
4300  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4301 
4302  /* extract data */
4303  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4304  const uint8_t val_i = (pRxBuff[6u + (i * 8u)] << 4u) | ((pRxBuff[7u + (i * 8u)] >> 4u));
4305  ltc_state->ltcData.slaveControl->ioValueIn[i] = val_i;
4306  }
4307 
4308  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4309 }
4310 
4311 /**
4312  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4313  *
4314  * This function sends a control byte to the register of the user port expander from TI
4315  *
4316  * @param ltc_state state of the ltc state machine
4317  * @param pSpiInterface pointer to SPI configuration
4318  * @param pTxBuff transmit buffer
4319  * @param pRxBuff receive buffer
4320  * @param frameLength number of words to transmit
4321  * @param direction use port expander pin as input or output
4322  *
4323  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4324  */
4326  LTC_STATE_s *ltc_state,
4327  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4328  uint16_t *pTxBuff,
4329  uint16_t *pRxBuff,
4330  uint32_t frameLength,
4332  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4333 
4334  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4335 
4336  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4337  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /*upper nibble of TCA6408A address */
4338  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4339  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4340 
4341  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR >>
4342  4u); /* upper nibble of TCA6408A configuration register address */
4343  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_CONFIG_REG_ADR << 4u) |
4344  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A configuration register address */
4345 
4346  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK |
4347  (direction >> 4u); /* upper nibble of TCA6408A configuration register data */
4348  pTxBuff[9u + (i * 8u)] = (uint8_t)(direction << 4u) |
4349  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A configuration register data */
4350  }
4351 
4352  /* send WRCOMM to send I2C message */
4353  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4354 
4355  return statusSPI;
4356 }
4357 
4358 /**
4359  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4360  *
4361  * This function sends a control byte to the register of the user port expander from TI
4362  *
4363  * @param ltc_state state of the ltc state machine
4364  * @param pSpiInterface pointer to SPI configuration
4365  * @param pTxBuff transmit buffer
4366  * @param pRxBuff receive buffer
4367  * @param frameLength number of words to transmit
4368  *
4369  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4370  */
4372  LTC_STATE_s *ltc_state,
4373  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4374  uint16_t *pTxBuff,
4375  uint16_t *pRxBuff,
4376  uint32_t frameLength) {
4377  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4378 
4379  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4380 
4381  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4382  const uint8_t output_data = ltc_state->ltcData.slaveControl->ioValueOut[BS_NR_OF_MODULES_PER_STRING - 1 - i];
4383 
4384  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4385  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4386  LTC_FCOM_MASTER_NACK; /* 0: lower nibble of TCA6408A address + R/W bit */
4387 
4388  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR >>
4389  4u); /* upper nibble of TCA6408A output register address */
4390  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_OUTPUT_REG_ADR << 4u) |
4391  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A output register address */
4392 
4393  pTxBuff[8u + (i * 8u)] = LTC_ICOM_BLANK | (output_data >> 4u); /* upper nibble of TCA6408A output register */
4394  pTxBuff[9u + (i * 8u)] = (uint8_t)(output_data << 4u) |
4395  LTC_FCOM_MASTER_NACK_STOP; /* lower nibble of TCA6408A output register */
4396  }
4397 
4398  /* send WRCOMM to send I2C message */
4399  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4400 
4401  return statusSPI;
4402 }
4403 
4404 /**
4405  * @brief sends data to the LTC daisy-chain to control the user port expander from TI
4406  *
4407  * @details This function sends a control byte to the register of the user port expander from TI
4408  *
4409  * @param ltc_state state of the ltc state machine
4410  * @param pSpiInterface pointer to SPI configuration
4411  * @param pTxBuff transmit buffer
4412  * @param pRxBuff receive buffer
4413  * @param frameLength number of words to transmit
4414  * @param step first or second stage of read process (0 or 1)
4415  *
4416  * @return #STD_OK if SPI transmission is OK, #STD_NOT_OK otherwise
4417  */
4419  LTC_STATE_s *ltc_state,
4420  SPI_INTERFACE_CONFIG_s *pSpiInterface,
4421  uint16_t *pTxBuff,
4422  uint16_t *pRxBuff,
4423  uint32_t frameLength,
4424  uint8_t step) {
4425  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4426 
4427  if (step == 0u) {
4428  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4429  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4430  pTxBuff[5u + (i * 8u)] = (uint8_t)((LTC_PORTEXPANDER_ADR_TI << 1u) << 4u) |
4431  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A address + R/W bit */
4432 
4433  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | (LTC_PORT_EXPANDER_TI_INPUT_REG_ADR >>
4434  4u); /* upper nibble of TCA6408A input register address */
4435  pTxBuff[7u + (i * 8u)] = (uint8_t)(LTC_PORT_EXPANDER_TI_INPUT_REG_ADR << 4u) |
4436  LTC_FCOM_MASTER_NACK; /* x: lower nibble of TCA6408A input register address */
4437 
4438  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* no transmission */
4439  pTxBuff[9u + (i * 8u)] = 0; /* dummy data */
4440  }
4441 
4442  } else {
4443  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4444 
4445  for (uint16_t i = 0; i < BS_NR_OF_MODULES_PER_STRING; i++) {
4446  pTxBuff[4u + (i * 8u)] = LTC_ICOM_START | 0x4u; /* upper nibble of TCA6408A address */
4447  pTxBuff[5u + (i * 8u)] = (uint8_t)(((LTC_PORTEXPANDER_ADR_TI << 1u) | 1u) << 4u) |
4448  LTC_FCOM_MASTER_NACK; /* lower nibble of TCA6408A address + R/W bit */
4449 
4450  pTxBuff[6u + (i * 8u)] = LTC_ICOM_BLANK | 0x0Fu; /* upper nibble slave data, master pulls bus high */
4451  pTxBuff[7u + (i * 8u)] = LTC_FCOM_MASTER_NACK | 0xF0u; /* lower nibble slave data, master pulls bus high */
4452 
4453  pTxBuff[8u + (i * 8u)] = LTC_ICOM_NO_TRANSMIT; /* no transmission */
4454  pTxBuff[9u + (i * 8u)] = 0; /* dummy data */
4455  }
4456  }
4457 
4458  /* send WRCOMM to send I2C message */
4459  statusSPI = LTC_WriteRegister(ltc_cmdWRCOMM, pSpiInterface, pTxBuff, pRxBuff, frameLength);
4460 
4461  return statusSPI;
4462 }
4463 
4464 /**
4465  * @brief saves the received values of the external port expander from TI read from the LTC daisy-chain.
4466  * @details This function saves the received data byte from the external port expander from TI
4467  * @param ltc_state state of the ltc state machine
4468  * @param pTxBuff transmit buffer
4469  */
4470 static void LTC_PortExpanderSaveValuesTi(LTC_STATE_s *ltc_state, uint16_t *pTxBuff) {
4471  DATA_READ_DATA(ltc_state->ltcData.slaveControl);
4472 
4473  /* extract data */
4474  for (uint16_t i = 0; i < LTC_N_LTC; i++) {
4475  const uint8_t val_i = (pTxBuff[6u + (i * 8u)] << 4u) | ((pTxBuff[7u + (i * 8u)] >> 4u));
4476  ltc_state->ltcData.slaveControl->ioValueIn[i] = val_i;
4477  }
4478 
4479  DATA_WRITE_DATA(ltc_state->ltcData.slaveControl);
4480 }
4481 
4482 /**
4483  * @brief sends 72 clock pulses to the LTC daisy-chain.
4484  *
4485  * This function is used for the communication with the multiplexers via I2C on the GPIOs.
4486  * It send the command STCOMM to the LTC daisy-chain.
4487  *
4488  * @param pSpiInterface pointer to SPI configuration
4489  *
4490  * @return statusSPI #STD_OK if clock pulses were sent correctly by SPI, #STD_NOT_OK otherwise
4491  *
4492  */
4494  STD_RETURN_TYPE_e statusSPI = STD_NOT_OK;
4495  uint16_t ltc_TXBufferClock[4u + 9u];
4496 
4497  for (uint16_t i = 0; i < (4u + 9u); i++) {
4498  ltc_TXBufferClock[i] = 0xFF;
4499  }
4500 
4501  ltc_TXBufferClock[0] = ltc_cmdSTCOMM[0];
4502  ltc_TXBufferClock[1] = ltc_cmdSTCOMM[1];
4503  ltc_TXBufferClock[2] = ltc_cmdSTCOMM[2];
4504  ltc_TXBufferClock[3] = ltc_cmdSTCOMM[3];
4505 
4506  statusSPI = LTC_TransmitI2cCommand(pSpiInterface, ltc_TXBufferClock);
4507 
4508  return statusSPI;
4509 }
4510 
4511 /**
4512  * @brief gets the frequency of the SPI clock.
4513  *
4514  * This function reads the configuration from the SPI handle directly.
4515  *
4516  * @param pSpiInterface pointer to SPI configuration
4517  *
4518  * @return frequency of the SPI clock
4519  */
4520 static uint32_t LTC_GetSpiClock(SPI_INTERFACE_CONFIG_s *pSpiInterface) {
4521  uint32_t SPI_Clock = 0;
4522  uint32_t prescaler = 0;
4523 
4524  /* if (LTC_SPI_INSTANCE == SPI2 || LTC_SPI_INSTANCE == SPI3) { */
4525  /* SPI2 and SPI3 are connected to APB1 (PCLK1) */
4526  /* The prescaler setup bits LTC_SPI_PRESCALER corresponds to the bits 5:3 in the SPI_CR1 register */
4527  /* Reference manual p.909 */
4528  /* The shift by 3 puts the bits 5:3 to the first position */
4529  /* Division are made by powers of 2 which corresponds to shifting to the right */
4530  /* Then 0 corresponds to divide by 2, 1 corresponds to divide by 4... so 1 has to be added to the value of the configuration bits */
4531 
4532  /* SPI_Clock = HAL_RCC_GetPCLK1Freq()>>((LTC_SPI_PRESCALER>>3)+1);
4533  } */
4534 
4535  /* if (LTC_SPI_INSTANCE == SPI1 || LTC_SPI_INSTANCE == SPI4 || LTC_SPI_INSTANCE == SPI5 || LTC_SPI_INSTANCE == SPI6) { */
4536  /* SPI1, SPI4, SPI5 and SPI6 are connected to APB2 (PCLK2) */
4537  /* The prescaler setup bits LTC_SPI_PRESCALER corresponds to the bits 5:3 in the SPI_CR1 register */
4538  /* Reference manual p.909 */
4539  /* The shift by 3 puts the bits 5:3 to the first position */
4540  /* Division are made by powers of 2 which corresponds to shifting to the right */
4541  /* Then 0 corresponds to divide by 2, 1 corresponds to divide by 4... so 1 has to be added to the value of the configuration bits */
4542 
4543  /* SPI_Clock = HAL_RCC_GetPCLK2Freq()>>((LTC_SPI_PRESCALER>>3)+1);
4544  } */
4545 
4546  /* Get SPI prescaler */
4547  prescaler = ((pSpiInterface->pNode->FMT0) >> 8u) & 0xFFu;
4548  SPI_Clock = (uint32_t)(AVCLK1_FREQ * 1000000u) / (prescaler + 1u);
4549 
4550  return SPI_Clock;
4551 }
4552 
4553 /**
4554  * @brief sets the transfer time needed to receive/send data with the LTC daisy-chain.
4555  *
4556  * This function gets the clock frequency and uses the number of LTCs in the daisy-chain.
4557  *
4558  * @param ltc_state: state of the ltc state machine
4559  *
4560  */
4561 static void LTC_SetTransferTimes(LTC_STATE_s *ltc_state) {
4562  uint32_t transferTime_us = 0;
4563  uint32_t SPI_Clock = 0;
4564 
4565  SPI_Clock = LTC_GetSpiClock(ltc_state->ltcData.pSpiInterface);
4566 
4567  /* Transmission of a command and data */
4568  /* Multiplication by 1000*1000 to get us */
4569  transferTime_us = (8u * 1000u * 1000u) / (SPI_Clock);
4570  transferTime_us *= LTC_N_BYTES_FOR_DATA_TRANSMISSION;
4571  transferTime_us = transferTime_us + LTC_SPI_WAKEUP_WAIT_TIME_US;
4572  ltc_state->commandDataTransferTime = (transferTime_us / 1000u) + 1u;
4573 
4574  /* Transmission of a command */
4575  /* Multiplication by 1000*1000 to get us */
4576  transferTime_us = ((4u) * 8u * 1000u * 1000u) / (SPI_Clock);
4577  transferTime_us = transferTime_us + LTC_SPI_WAKEUP_WAIT_TIME_US;
4578  ltc_state->commandTransferTime = (transferTime_us / 1000u) + 1u;
4579 
4580  /* Transmission of a command + 9 clocks */
4581  /* Multiplication by 1000*1000 to get us */
4582  transferTime_us = ((4u + 9u) * 8u * 1000u * 1000u) / (SPI_Clock);
4583  transferTime_us = transferTime_us + LTC_SPI_WAKEUP_WAIT_TIME_US;
4584  ltc_state->gpioClocksTransferTime = (transferTime_us / 1000u) + 1u;
4585 }
4586 
4587 /**
4588  * @brief checks the state requests that are made.
4589  *
4590  * This function checks the validity of the state requests.
4591  * The results of the checked is returned immediately.
4592  *
4593  * @param ltc_state: state of the ltc state machine
4594  * @param statereq state request to be checked
4595  *
4596  * @return result of the state request that was made, taken from LTC_RETURN_TYPE_e
4597  */
4599  LTC_RETURN_TYPE_e retVal = LTC_OK;
4600  if (statereq.string >= BS_NR_OF_STRINGS) {
4601  retVal = LTC_ILLEGAL_REQUEST;
4602  } else if (ltc_state->statereq.request == LTC_STATE_NO_REQUEST) {
4603  /* init only allowed from the uninitialized state */
4604  if (statereq.request == LTC_STATE_INIT_REQUEST) {
4605