| Linduino 1.3.0 Linear Technology Arduino-Compatible Demonstration Board

LTC681x.cpp
Go to the documentation of this file.
1 /*! General BMS Library
2 ***************************************************************************//**
3 * @file LTC681x.cpp
4 * @author BMS (bms.support@analog.com)
5
6 ********************************************************************************
7 * Copyright 2019(c) Analog Devices, Inc.
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * - Neither the name of Analog Devices, Inc. nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 * - The use of this software may or may not infringe the patent rights
23 * of one or more patent holders. This license does not release you
24 * from the requirement that you obtain separate licenses from these
25 * patent holders to use this software.
26 * - Use of the software either in source or binary form, must be run
27 * on or directly connected to an Analog Devices Inc. component.
28 *
29 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
30 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
31 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32 * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
36 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *******************************************************************************/
40
41 /*! @file
42 Library for LTC681x Multi-cell Battery Monitor
43 */
44
45 #include <stdint.h>
46 #include "LTC681x.h"
47 #include "bms_hardware.h"
48
49 #ifdef LINDUINO
50 #include <Arduino.h>
51 #endif
52
53 /* Wake isoSPI up from IDlE state and enters the READY state */
54 void wakeup_idle(uint8_t total_ic) //Number of ICs in the system
55 {
56 for (int i =0; i<total_ic; i++)
57 {
58 cs_low(CS_PIN);
59 spi_read_byte(0xff);//Guarantees the isoSPI will be in ready mode
60 cs_high(CS_PIN);
61 }
62 }
63
64 /* Generic wakeup command to wake the LTC681x from sleep state */
65 void wakeup_sleep(uint8_t total_ic) //Number of ICs in the system
66 {
67 for (int i =0; i<total_ic; i++)
68 {
69 cs_low(CS_PIN);
70 delay_u(300); // Guarantees the LTC681x will be in standby
71 cs_high(CS_PIN);
72 delay_u(10);
73 }
74 }
75
76 /* Generic function to write 68xx commands. Function calculates PEC for tx_cmd data. */
77 void cmd_68(uint8_t tx_cmd[2]) //The command to be transmitted
78 {
79 uint8_t cmd[4];
80 uint16_t cmd_pec;
81 uint8_t md_bits;
82
83 cmd[0] = tx_cmd[0];
84 cmd[1] = tx_cmd[1];
85 cmd_pec = pec15_calc(2, cmd);
86 cmd[2] = (uint8_t)(cmd_pec >> 8);
87 cmd[3] = (uint8_t)(cmd_pec);
88
89 cs_low(CS_PIN);
90 spi_write_array(4,cmd);
91 cs_high(CS_PIN);
92 }
93
94 /*
95 Generic function to write 68xx commands and write payload data.
96 Function calculates PEC for tx_cmd data and the data to be transmitted.
97 */
98 void write_68(uint8_t total_ic, //Number of ICs to be written to
99 uint8_t tx_cmd[2], //The command to be transmitted
100 uint8_t data[] // Payload Data
101 )
102 {
103 const uint8_t BYTES_IN_REG = 6;
104 const uint8_t CMD_LEN = 4+(8*total_ic);
105 uint8_t *cmd;
106 uint16_t data_pec;
107 uint16_t cmd_pec;
108 uint8_t cmd_index;
109
110 cmd = (uint8_t *)malloc(CMD_LEN*sizeof(uint8_t));
111 cmd[0] = tx_cmd[0];
112 cmd[1] = tx_cmd[1];
113 cmd_pec = pec15_calc(2, cmd);
114 cmd[2] = (uint8_t)(cmd_pec >> 8);
115 cmd[3] = (uint8_t)(cmd_pec);
116
117 cmd_index = 4;
118 for (uint8_t current_ic = total_ic; current_ic > 0; current_ic--) // Executes for each LTC681x, this loops starts with the last IC on the stack.
119 { //The first configuration written is received by the last IC in the daisy chain
120 for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++)
121 {
122 cmd[cmd_index] = data[((current_ic-1)*6)+current_byte];
123 cmd_index = cmd_index + 1;
124 }
125
126 data_pec = (uint16_t)pec15_calc(BYTES_IN_REG, &data[(current_ic-1)*6]); // Calculating the PEC for each ICs configuration register data
127 cmd[cmd_index] = (uint8_t)(data_pec >> 8);
128 cmd[cmd_index + 1] = (uint8_t)data_pec;
129 cmd_index = cmd_index + 2;
130 }
131
132 cs_low(CS_PIN);
133 spi_write_array(CMD_LEN, cmd);
134 cs_high(CS_PIN);
135
136 free(cmd);
137 }
138
139 /* Generic function to write 68xx commands and read data. Function calculated PEC for tx_cmd data */
140 int8_t read_68( uint8_t total_ic, // Number of ICs in the system
141 uint8_t tx_cmd[2], // The command to be transmitted
142 uint8_t *rx_data // Data to be read
143 )
144 {
145 const uint8_t BYTES_IN_REG = 8;
146 uint8_t cmd[4];
147 uint8_t data[256];
148 int8_t pec_error = 0;
149 uint16_t cmd_pec;
150 uint16_t data_pec;
151 uint16_t received_pec;
152
153 cmd[0] = tx_cmd[0];
154 cmd[1] = tx_cmd[1];
155 cmd_pec = pec15_calc(2, cmd);
156 cmd[2] = (uint8_t)(cmd_pec >> 8);
157 cmd[3] = (uint8_t)(cmd_pec);
158
159 cs_low(CS_PIN);
160 spi_write_read(cmd, 4, data, (BYTES_IN_REG*total_ic)); //Transmits the command and reads the configuration data of all ICs on the daisy chain into rx_data[] array
161 cs_high(CS_PIN);
162
163 for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++) //Executes for each LTC681x in the daisy chain and packs the data
164 { //into the rx_data array as well as check the received data for any bit errors
165 for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++)
166 {
167 rx_data[(current_ic*8)+current_byte] = data[current_byte + (current_ic*BYTES_IN_REG)];
168 }
169
170 received_pec = (rx_data[(current_ic*8)+6]<<8) + rx_data[(current_ic*8)+7];
171 data_pec = pec15_calc(6, &rx_data[current_ic*8]);
172
173 if (received_pec != data_pec)
174 {
175 pec_error = -1;
176 }
177 }
178
179 return(pec_error);
180 }
181
182 /* Calculates and returns the CRC15 */
183 uint16_t pec15_calc(uint8_t len, //Number of bytes that will be used to calculate a PEC
184 uint8_t *data //Array of data that will be used to calculate a PEC
185 )
186 {
187 uint16_t remainder,addr;
188 remainder = 16;//initialize the PEC
189
190 for (uint8_t i = 0; i<len; i++) // loops for each byte in data array
191 {
192 addr = ((remainder>>7)^data[i])&0xff;//calculate PEC table address
193 #ifdef MBED
194 remainder = (remainder<<8)^crc15Table[addr];
195 #else
196 remainder = (remainder<<8)^pgm_read_word_near(crc15Table+addr);
197 #endif
198 }
199
200 return(remainder*2);//The CRC15 has a 0 in the LSB so the remainder must be multiplied by 2
201 }
202
203 /* Write the LTC681x CFGRA */
204 void LTC681x_wrcfg(uint8_t total_ic, //The number of ICs being written to
205 cell_asic ic[] // A two dimensional array of the configuration data that will be written
206 )
207 {
208 uint8_t cmd[2] = {0x00 , 0x01} ;
209 uint8_t write_buffer[256];
210 uint8_t write_count = 0;
211 uint8_t c_ic = 0;
212
213 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
214 {
215 if (ic->isospi_reverse == false)
216 {
217 c_ic = current_ic;
218 }
219 else
220 {
221 c_ic = total_ic - current_ic - 1;
222 }
223
224 for (uint8_t data = 0; data<6; data++)
225 {
226 write_buffer[write_count] = ic[c_ic].config.tx_data[data];
227 write_count++;
228 }
229 }
230 write_68(total_ic, cmd, write_buffer);
231 }
232
233 /* Write the LTC681x CFGRB */
234 void LTC681x_wrcfgb(uint8_t total_ic, //The number of ICs being written to
235 cell_asic ic[] // A two dimensional array of the configuration data that will be written
236 )
237 {
238 uint8_t cmd[2] = {0x00 , 0x24} ;
239 uint8_t write_buffer[256];
240 uint8_t write_count = 0;
241 uint8_t c_ic = 0;
242
243 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
244 {
245 if (ic->isospi_reverse == false)
246 {
247 c_ic = current_ic;
248 }
249 else
250 {
251 c_ic = total_ic - current_ic - 1;
252 }
253
254 for (uint8_t data = 0; data<6; data++)
255 {
256 write_buffer[write_count] = ic[c_ic].configb.tx_data[data];
257 write_count++;
258 }
259 }
260 write_68(total_ic, cmd, write_buffer);
261 }
262
263 /* Read the LTC681x CFGA */
264 int8_t LTC681x_rdcfg(uint8_t total_ic, //Number of ICs in the system
265 cell_asic ic[] // A two dimensional array that the function stores the read configuration data.
266 )
267 {
268 uint8_t cmd[2]= {0x00 , 0x02};
269 uint8_t read_buffer[256];
270 int8_t pec_error = 0;
271 uint16_t data_pec;
272 uint16_t calc_pec;
273 uint8_t c_ic = 0;
274
275 pec_error = read_68(total_ic, cmd, read_buffer);
276
277 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
278 {
279 if (ic->isospi_reverse == false)
280 {
281 c_ic = current_ic;
282 }
283 else
284 {
285 c_ic = total_ic - current_ic - 1;
286 }
287
288 for (int byte=0; byte<8; byte++)
289 {
290 ic[c_ic].config.rx_data[byte] = read_buffer[byte+(8*current_ic)];
291 }
292
293 calc_pec = pec15_calc(6,&read_buffer[8*current_ic]);
294 data_pec = read_buffer[7+(8*current_ic)] | (read_buffer[6+(8*current_ic)]<<8);
295 if (calc_pec != data_pec )
296 {
297 ic[c_ic].config.rx_pec_match = 1;
298 }
299 else ic[c_ic].config.rx_pec_match = 0;
300 }
301 LTC681x_check_pec(total_ic,CFGR,ic);
302
303 return(pec_error);
304 }
305
306 /* Reads the LTC681x CFGB */
307 int8_t LTC681x_rdcfgb(uint8_t total_ic, //Number of ICs in the system
308 cell_asic ic[] // A two dimensional array that the function stores the read configuration data.
309 )
310 {
311 uint8_t cmd[2]= {0x00 , 0x26};
312 uint8_t read_buffer[256];
313 int8_t pec_error = 0;
314 uint16_t data_pec;
315 uint16_t calc_pec;
316 uint8_t c_ic = 0;
317
318 pec_error = read_68(total_ic, cmd, read_buffer);
319
320 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
321 {
322 if (ic->isospi_reverse == false)
323 {
324 c_ic = current_ic;
325 }
326 else
327 {
328 c_ic = total_ic - current_ic - 1;
329 }
330
331 for (int byte=0; byte<8; byte++)
332 {
333 ic[c_ic].configb.rx_data[byte] = read_buffer[byte+(8*current_ic)];
334 }
335
336 calc_pec = pec15_calc(6,&read_buffer[8*current_ic]);
337 data_pec = read_buffer[7+(8*current_ic)] | (read_buffer[6+(8*current_ic)]<<8);
338 if (calc_pec != data_pec )
339 {
340 ic[c_ic].configb.rx_pec_match = 1;
341 }
342 else ic[c_ic].configb.rx_pec_match = 0;
343 }
344 LTC681x_check_pec(total_ic,CFGRB,ic);
345
346 return(pec_error);
347 }
348
349 /* Starts ADC conversion for cell voltage */
350 void LTC681x_adcv( uint8_t MD, //ADC Mode
351 uint8_t DCP, //Discharge Permit
352 uint8_t CH //Cell Channels to be measured
353 )
354 {
355 uint8_t cmd[2];
356 uint8_t md_bits;
357
358 md_bits = (MD & 0x02) >> 1;
359 cmd[0] = md_bits + 0x02;
360 md_bits = (MD & 0x01) << 7;
361 cmd[1] = md_bits + 0x60 + (DCP<<4) + CH;
362
363 cmd_68(cmd);
364 }
365
366 /* Start ADC Conversion for GPIO and Vref2 */
367 void LTC681x_adax(uint8_t MD, //ADC Mode
368 uint8_t CHG //GPIO Channels to be measured
369 )
370 {
371 uint8_t cmd[4];
372 uint8_t md_bits;
373
374 md_bits = (MD & 0x02) >> 1;
375 cmd[0] = md_bits + 0x04;
376 md_bits = (MD & 0x01) << 7;
377 cmd[1] = md_bits + 0x60 + CHG ;
378
379 cmd_68(cmd);
380 }
381
382 /* Start ADC Conversion for Status */
383 void LTC681x_adstat(uint8_t MD, //ADC Mode
384 uint8_t CHST //Stat Channels to be measured
385 )
386 {
387 uint8_t cmd[4];
388 uint8_t md_bits;
389
390 md_bits = (MD & 0x02) >> 1;
391 cmd[0] = md_bits + 0x04;
392 md_bits = (MD & 0x01) << 7;
393 cmd[1] = md_bits + 0x68 + CHST ;
394
395 cmd_68(cmd);
396 }
397
398 /* Starts cell voltage and SOC conversion */
399 void LTC681x_adcvsc(uint8_t MD, //ADC Mode
400 uint8_t DCP //Discharge Permit
401 )
402 {
403 uint8_t cmd[2];
404 uint8_t md_bits;
405
406 md_bits = (MD & 0x02) >> 1;
407 cmd[0] = md_bits | 0x04;
408 md_bits = (MD & 0x01) << 7;
409 cmd[1] = md_bits | 0x60 | (DCP<<4) | 0x07;
410
411 cmd_68(cmd);
412 }
413
414 /* Starts cell voltage and GPIO 1&2 conversion */
415 void LTC681x_adcvax(uint8_t MD, //ADC Mode
416 uint8_t DCP //Discharge Permit
417 )
418 {
419 uint8_t cmd[2];
420 uint8_t md_bits;
421
422 md_bits = (MD & 0x02) >> 1;
423 cmd[0] = md_bits | 0x04;
424 md_bits = (MD & 0x01) << 7;
425 cmd[1] = md_bits | ((DCP&0x01)<<4) + 0x6F;
426
427 cmd_68(cmd);
428 }
429
430 /*
431 Reads and parses the LTC681x cell voltage registers.
432 The function is used to read the parsed Cell voltages codes of the LTC681x.
433 This function will send the requested read commands parse the data
434 and store the cell voltages in c_codes variable.
435 */
436 uint8_t LTC681x_rdcv(uint8_t reg, // Controls which cell voltage register is read back.
437 uint8_t total_ic, // The number of ICs in the system
438 cell_asic *ic // Array of the parsed cell codes
439 )
440 {
441 int8_t pec_error = 0;
442 uint8_t *cell_data;
443 uint8_t c_ic = 0;
444 cell_data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t));
445
446 if (reg == 0)
447 {
448 for (uint8_t cell_reg = 1; cell_reg<ic[0].ic_reg.num_cv_reg+1; cell_reg++) //Executes once for each of the LTC681x cell voltage registers
449 {
450 LTC681x_rdcv_reg(cell_reg, total_ic,cell_data );
451 for (int current_ic = 0; current_ic<total_ic; current_ic++)
452 {
453 if (ic->isospi_reverse == false)
454 {
455 c_ic = current_ic;
456 }
457 else
458 {
459 c_ic = total_ic - current_ic - 1;
460 }
461 pec_error = pec_error + parse_cells(current_ic,cell_reg, cell_data,
462 &ic[c_ic].cells.c_codes[0],
463 &ic[c_ic].cells.pec_match[0]);
464 }
465 }
466 }
467
468 else
469 {
470 LTC681x_rdcv_reg(reg, total_ic,cell_data);
471
472 for (int current_ic = 0; current_ic<total_ic; current_ic++)
473 {
474 if (ic->isospi_reverse == false)
475 {
476 c_ic = current_ic;
477 }
478 else
479 {
480 c_ic = total_ic - current_ic - 1;
481 }
482 pec_error = pec_error + parse_cells(current_ic,reg, &cell_data[8*c_ic],
483 &ic[c_ic].cells.c_codes[0],
484 &ic[c_ic].cells.pec_match[0]);
485 }
486 }
487 LTC681x_check_pec(total_ic,CELL,ic);
488 free(cell_data);
489
490 return(pec_error);
491 }
492
493 /*
494 The function is used to read the parsed GPIO codes of the LTC681x.
495 This function will send the requested read commands parse the data
496 and store the gpio voltages in a_codes variable.
497 */
498 int8_t LTC681x_rdaux(uint8_t reg, //Determines which GPIO voltage register is read back.
499 uint8_t total_ic,//The number of ICs in the system
500 cell_asic *ic//A two dimensional array of the gpio voltage codes.
501 )
502 {
503 uint8_t *data;
504 int8_t pec_error = 0;
505 uint8_t c_ic =0;
506 data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t));
507
508 if (reg == 0)
509 {
510 for (uint8_t gpio_reg = 1; gpio_reg<ic[0].ic_reg.num_gpio_reg+1; gpio_reg++) //Executes once for each of the LTC681x aux voltage registers
511 {
512 LTC681x_rdaux_reg(gpio_reg, total_ic,data); //Reads the raw auxiliary register data into the data[] array
513 for (int current_ic = 0; current_ic<total_ic; current_ic++)
514 {
515 if (ic->isospi_reverse == false)
516 {
517 c_ic = current_ic;
518 }
519 else
520 {
521 c_ic = total_ic - current_ic - 1;
522 }
523 pec_error = parse_cells(current_ic,gpio_reg, data,
524 &ic[c_ic].aux.a_codes[0],
525 &ic[c_ic].aux.pec_match[0]);
526 }
527 }
528 }
529 else
530 {
531 LTC681x_rdaux_reg(reg, total_ic, data);
532
533 for (int current_ic = 0; current_ic<total_ic; current_ic++)
534 {
535 if (ic->isospi_reverse == false)
536 {
537 c_ic = current_ic;
538 }
539 else
540 {
541 c_ic = total_ic - current_ic - 1;
542 }
543 pec_error = parse_cells(current_ic,reg, data,
544 &ic[c_ic].aux.a_codes[0],
545 &ic[c_ic].aux.pec_match[0]);
546 }
547 }
548 LTC681x_check_pec(total_ic,AUX,ic);
549 free(data);
550
551 return (pec_error);
552 }
553
554 /*
555 Reads and parses the LTC681x stat registers.
556 The function is used to read the parsed Stat codes of the LTC681x.
557 This function will send the requested read commands parse the data
558 and store the gpio voltages in stat_codes variable.
559 */
560 int8_t LTC681x_rdstat(uint8_t reg, //Determines which Stat register is read back.
561 uint8_t total_ic,//The number of ICs in the system
562 cell_asic *ic //A two dimensional array of the stat codes.
563 )
564
565 {
566 const uint8_t BYT_IN_REG = 6;
567 const uint8_t STAT_IN_REG = 3;
568 uint8_t *data;
569 uint8_t data_counter = 0;
570 int8_t pec_error = 0;
571 uint16_t parsed_stat;
572 uint16_t received_pec;
573 uint16_t data_pec;
574 uint8_t c_ic = 0;
575
576 data = (uint8_t *) malloc((12*total_ic)*sizeof(uint8_t));
577
578 if (reg == 0)
579 {
580 for (uint8_t stat_reg = 1; stat_reg< 3; stat_reg++) //Executes once for each of the LTC681x stat voltage registers
581 {
582 data_counter = 0;
583 LTC681x_rdstat_reg(stat_reg, total_ic,data); //Reads the raw status register data into the data[] array
584
585 for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) // Executes for every LTC681x in the daisy chain
586 { // current_ic is used as the IC counter
587 if (ic->isospi_reverse == false)
588 {
589 c_ic = current_ic;
590 }
591 else
592 {
593 c_ic = total_ic - current_ic - 1;
594 }
595
596 if (stat_reg ==1)
597 {
598 for (uint8_t current_stat = 0; current_stat< STAT_IN_REG; current_stat++) // This loop parses the read back data into Status registers,
599 { // it loops once for each of the 3 stat codes in the register
600 parsed_stat = data[data_counter] + (data[data_counter+1]<<8); //Each stat codes is received as two bytes and is combined to create the parsed status code
601 ic[c_ic].stat.stat_codes[current_stat] = parsed_stat;
602 data_counter=data_counter+2; //Because stat codes are two bytes the data counter
603 }
604 }
605 else if (stat_reg == 2)
606 {
607 parsed_stat = data[data_counter] + (data[data_counter+1]<<8); //Each stat is received as two bytes and is combined to create the parsed status code
608 data_counter = data_counter +2;
609 ic[c_ic].stat.stat_codes[3] = parsed_stat;
610 ic[c_ic].stat.flags[0] = data[data_counter++];
611 ic[c_ic].stat.flags[1] = data[data_counter++];
612 ic[c_ic].stat.flags[2] = data[data_counter++];
613 ic[c_ic].stat.mux_fail[0] = (data[data_counter] & 0x02)>>1;
614 ic[c_ic].stat.thsd[0] = data[data_counter++] & 0x01;
615 }
616
617 received_pec = (data[data_counter]<<8)+ data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th
618 //after the 6 status data bytes
619 data_pec = pec15_calc(BYT_IN_REG, &data[current_ic*NUM_RX_BYT]);
620
621 if (received_pec != data_pec)
622 {
623 pec_error = -1; //The pec_error variable is simply set negative if any PEC errors
624 ic[c_ic].stat.pec_match[stat_reg-1]=1; //are detected in the received serial data
625
626 }
627 else
628 {
629 ic[c_ic].stat.pec_match[stat_reg-1]=0;
630 }
631
632 data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter
633 //must be incremented by 2 bytes to point to the next ICs status data
634 }
635 }
636 }
637 else
638 {
639 LTC681x_rdstat_reg(reg, total_ic, data);
640 for (int current_ic = 0 ; current_ic < total_ic; current_ic++) // Executes for every LTC681x in the daisy chain
641 { // current_ic is used as an IC counter
642 if (ic->isospi_reverse == false)
643 {
644 c_ic = current_ic;
645 }
646 else
647 {
648 c_ic = total_ic - current_ic - 1;
649 }
650 if (reg ==1)
651 {
652 for (uint8_t current_stat = 0; current_stat< STAT_IN_REG; current_stat++) // This loop parses the read back data into Status voltages, it
653 { // loops once for each of the 3 stat codes in the register
654
655 parsed_stat = data[data_counter] + (data[data_counter+1]<<8); //Each stat codes is received as two bytes and is combined to
656 // create the parsed stat code
657
658 ic[c_ic].stat.stat_codes[current_stat] = parsed_stat;
659 data_counter=data_counter+2; //Because stat codes are two bytes the data counter
660 //must increment by two for each parsed stat code
661 }
662 }
663 else if (reg == 2)
664 {
665 parsed_stat = data[data_counter++] + (data[data_counter++]<<8); //Each stat codes is received as two bytes and is combined to
666 ic[c_ic].stat.stat_codes[3] = parsed_stat;
667 ic[c_ic].stat.flags[0] = data[data_counter++];
668 ic[c_ic].stat.flags[1] = data[data_counter++];
669 ic[c_ic].stat.flags[2] = data[data_counter++];
670 ic[c_ic].stat.mux_fail[0] = (data[data_counter] & 0x02)>>1;
671 ic[c_ic].stat.thsd[0] = data[data_counter++] & 0x01;
672 }
673
674 received_pec = (data[data_counter]<<8)+ data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th
675 //after the 6 status data bytes
676 data_pec = pec15_calc(BYT_IN_REG, &data[current_ic*NUM_RX_BYT]);
677 if (received_pec != data_pec)
678 {
679 pec_error = -1; //The pec_error variable is simply set negative if any PEC errors
680 ic[c_ic].stat.pec_match[reg-1]=1;
681 }
682
683 data_counter=data_counter+2;
684 }
685 }
686 LTC681x_check_pec(total_ic,STAT,ic);
687
688 free(data);
689
690 return (pec_error);
691 }
692
693 /* Writes the command and reads the raw cell voltage register data */
694 void LTC681x_rdcv_reg(uint8_t reg, //Determines which cell voltage register is read back
695 uint8_t total_ic, //the number of ICs in the
696 uint8_t *data //An array of the unparsed cell codes
697 )
698 {
699 const uint8_t REG_LEN = 8; //Number of bytes in each ICs register + 2 bytes for the PEC
700 uint8_t cmd[4];
701 uint16_t cmd_pec;
702
703 if (reg == 1) //1: RDCVA
704 {
705 cmd[1] = 0x04;
706 cmd[0] = 0x00;
707 }
708 else if (reg == 2) //2: RDCVB
709 {
710 cmd[1] = 0x06;
711 cmd[0] = 0x00;
712 }
713 else if (reg == 3) //3: RDCVC
714 {
715 cmd[1] = 0x08;
716 cmd[0] = 0x00;
717 }
718 else if (reg == 4) //4: RDCVD
719 {
720 cmd[1] = 0x0A;
721 cmd[0] = 0x00;
722 }
723 else if (reg == 5) //4: RDCVE
724 {
725 cmd[1] = 0x09;
726 cmd[0] = 0x00;
727 }
728 else if (reg == 6) //4: RDCVF
729 {
730 cmd[1] = 0x0B;
731 cmd[0] = 0x00;
732 }
733
734 cmd_pec = pec15_calc(2, cmd);
735 cmd[2] = (uint8_t)(cmd_pec >> 8);
736 cmd[3] = (uint8_t)(cmd_pec);
737
738 cs_low(CS_PIN);
739 spi_write_read(cmd,4,data,(REG_LEN*total_ic));
740 cs_high(CS_PIN);
741 }
742
743 /*
744 The function reads a single GPIO voltage register and stores the read data
745 in the *data point as a byte array. This function is rarely used outside of
746 the LTC681x_rdaux() command.
747 */
748 void LTC681x_rdaux_reg(uint8_t reg, //Determines which GPIO voltage register is read back
749 uint8_t total_ic, //The number of ICs in the system
750 uint8_t *data //Array of the unparsed auxiliary codes
751 )
752 {
753 const uint8_t REG_LEN = 8; // Number of bytes in the register + 2 bytes for the PEC
754 uint8_t cmd[4];
755 uint16_t cmd_pec;
756
757 if (reg == 1) //Read back auxiliary group A
758 {
759 cmd[1] = 0x0C;
760 cmd[0] = 0x00;
761 }
762 else if (reg == 2) //Read back auxiliary group B
763 {
764 cmd[1] = 0x0E;
765 cmd[0] = 0x00;
766 }
767 else if (reg == 3) //Read back auxiliary group C
768 {
769 cmd[1] = 0x0D;
770 cmd[0] = 0x00;
771 }
772 else if (reg == 4) //Read back auxiliary group D
773 {
774 cmd[1] = 0x0F;
775 cmd[0] = 0x00;
776 }
777 else //Read back auxiliary group A
778 {
779 cmd[1] = 0x0C;
780 cmd[0] = 0x00;
781 }
782
783 cmd_pec = pec15_calc(2, cmd);
784 cmd[2] = (uint8_t)(cmd_pec >> 8);
785 cmd[3] = (uint8_t)(cmd_pec);
786
787 cs_low(CS_PIN);
788 spi_write_read(cmd,4,data,(REG_LEN*total_ic));
789 cs_high(CS_PIN);
790 }
791
792 /*
793 The function reads a single stat register and stores the read data
794 in the *data point as a byte array. This function is rarely used outside of
795 the LTC681x_rdstat() command.
796 */
797 void LTC681x_rdstat_reg(uint8_t reg, //Determines which stat register is read back
798 uint8_t total_ic, //The number of ICs in the system
799 uint8_t *data //Array of the unparsed stat codes
800 )
801 {
802 const uint8_t REG_LEN = 8; // number of bytes in the register + 2 bytes for the PEC
803 uint8_t cmd[4];
804 uint16_t cmd_pec;
805
806 if (reg == 1) //Read back status group A
807 {
808 cmd[1] = 0x10;
809 cmd[0] = 0x00;
810 }
811 else if (reg == 2) //Read back status group B
812 {
813 cmd[1] = 0x12;
814 cmd[0] = 0x00;
815 }
816
817 else //Read back status group A
818 {
819 cmd[1] = 0x10;
820 cmd[0] = 0x00;
821 }
822
823 cmd_pec = pec15_calc(2, cmd);
824 cmd[2] = (uint8_t)(cmd_pec >> 8);
825 cmd[3] = (uint8_t)(cmd_pec);
826
827 cs_low(CS_PIN);
828 spi_write_read(cmd,4,data,(REG_LEN*total_ic));
829 cs_high(CS_PIN);
830 }
831
832 /* Helper function that parses voltage measurement registers */
833 int8_t parse_cells(uint8_t current_ic, // Current IC
834 uint8_t cell_reg, // Type of register
835 uint8_t cell_data[], // Unparsed data
836 uint16_t *cell_codes, // Parsed data
837 uint8_t *ic_pec // PEC error
838 )
839 {
840 const uint8_t BYT_IN_REG = 6;
841 const uint8_t CELL_IN_REG = 3;
842 int8_t pec_error = 0;
843 uint16_t parsed_cell;
844 uint16_t received_pec;
845 uint16_t data_pec;
846 uint8_t data_counter = current_ic*NUM_RX_BYT; //data counter
847
848
849 for (uint8_t current_cell = 0; current_cell<CELL_IN_REG; current_cell++) // This loop parses the read back data into the register codes, it
850 { // loops once for each of the 3 codes in the register
851
852 parsed_cell = cell_data[data_counter] + (cell_data[data_counter + 1] << 8);//Each code is received as two bytes and is combined to
853 // create the parsed code
854 cell_codes[current_cell + ((cell_reg - 1) * CELL_IN_REG)] = parsed_cell;
855
856 data_counter = data_counter + 2; //Because the codes are two bytes, the data counter
857 //must increment by two for each parsed code
858 }
859 received_pec = (cell_data[data_counter] << 8) | cell_data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th
860 //after the 6 cell voltage data bytes
861 data_pec = pec15_calc(BYT_IN_REG, &cell_data[(current_ic) * NUM_RX_BYT]);
862
863 if (received_pec != data_pec)
864 {
865 pec_error = 1; //The pec_error variable is simply set negative if any PEC errors
866 ic_pec[cell_reg-1]=1;
867 }
868 else
869 {
870 ic_pec[cell_reg-1]=0;
871 }
872 data_counter=data_counter+2;
873
874 return(pec_error);
875 }
876
877 /* Sends the poll ADC command */
878 uint8_t LTC681x_pladc()
879 {
880 uint8_t cmd[4];
881 uint8_t adc_state = 0xFF;
882 uint16_t cmd_pec;
883
884 cmd[0] = 0x07;
885 cmd[1] = 0x14;
886 cmd_pec = pec15_calc(2, cmd);
887 cmd[2] = (uint8_t)(cmd_pec >> 8);
888 cmd[3] = (uint8_t)(cmd_pec);
889
890 cs_low(CS_PIN);
891 spi_write_array(4,cmd);
892 adc_state = spi_read_byte(0xFF);
893 cs_high(CS_PIN);
894
895 return(adc_state);
896 }
897
898 /* This function will block operation until the ADC has finished it's conversion */
899 uint32_t LTC681x_pollAdc()
900 {
901 uint32_t counter = 0;
902 uint8_t finished = 0;
903 uint8_t current_time = 0;
904 uint8_t cmd[4];
905 uint16_t cmd_pec;
906
907 cmd[0] = 0x07;
908 cmd[1] = 0x14;
909 cmd_pec = pec15_calc(2, cmd);
910 cmd[2] = (uint8_t)(cmd_pec >> 8);
911 cmd[3] = (uint8_t)(cmd_pec);
912
913 cs_low(CS_PIN);
914 spi_write_array(4,cmd);
915 while ((counter<200000)&&(finished == 0))
916 {
917 current_time = spi_read_byte(0xff);
918 if (current_time>0)
919 {
920 finished = 1;
921 }
922 else
923 {
924 counter = counter + 10;
925 }
926 }
927 cs_high(CS_PIN);
928
929 return(counter);
930 }
931
932 /*
933 The command clears the cell voltage registers and initializes
934 all values to 1. The register will read back hexadecimal 0xFF
935 after the command is sent.
936 */
937 void LTC681x_clrcell()
938 {
939 uint8_t cmd[2]= {0x07 , 0x11};
940 cmd_68(cmd);
941 }
942
943 /*
944 The command clears the Auxiliary registers and initializes
945 all values to 1. The register will read back hexadecimal 0xFF
946 after the command is sent.
947 */
948 void LTC681x_clraux()
949 {
950 uint8_t cmd[2]= {0x07 , 0x12};
951 cmd_68(cmd);
952 }
953
954 /*
955 The command clears the Stat registers and initializes
956 all values to 1. The register will read back hexadecimal 0xFF
957 after the command is sent.
958
959 */
960 void LTC681x_clrstat()
961 {
962 uint8_t cmd[2]= {0x07 , 0x13};
963 cmd_68(cmd);
964 }
965
966 /* Starts the Mux Decoder diagnostic self test */
967 void LTC681x_diagn()
968 {
969 uint8_t cmd[2] = {0x07 , 0x15};
970 cmd_68(cmd);
971 }
972
973 /* Starts cell voltage self test conversion */
974 void LTC681x_cvst(uint8_t MD, //ADC Mode
975 uint8_t ST //Self Test
976 )
977 {
978 uint8_t cmd[2];
979 uint8_t md_bits;
980
981 md_bits = (MD & 0x02) >> 1;
982 cmd[0] = md_bits + 0x02;
983 md_bits = (MD & 0x01) << 7;
984 cmd[1] = md_bits + ((ST)<<5) +0x07;
985
986 cmd_68(cmd);
987 }
988
989 /* Start an Auxiliary Register Self Test Conversion */
990 void LTC681x_axst(uint8_t MD, //ADC Mode
991 uint8_t ST //Self Test
992 )
993 {
994 uint8_t cmd[2];
995 uint8_t md_bits;
996
997 md_bits = (MD & 0x02) >> 1;
998 cmd[0] = md_bits + 0x04;
999 md_bits = (MD & 0x01) << 7;
1000 cmd[1] = md_bits + ((ST&0x03)<<5) +0x07;
1001
1002 cmd_68(cmd);
1003 }
1004
1005 /* Start a Status Register Self Test Conversion */
1006 void LTC681x_statst(uint8_t MD, //ADC Mode
1007 uint8_t ST //Self Test
1008 )
1009 {
1010 uint8_t cmd[2];
1011 uint8_t md_bits;
1012
1013 md_bits = (MD & 0x02) >> 1;
1014 cmd[0] = md_bits + 0x04;
1015 md_bits = (MD & 0x01) << 7;
1016 cmd[1] = md_bits + ((ST&0x03)<<5) +0x0F;
1017
1018 cmd_68(cmd);
1019 }
1020
1021 /* Starts cell voltage overlap conversion */
1022 void LTC681x_adol(uint8_t MD, //ADC Mode
1023 uint8_t DCP //Discharge Permit
1024 )
1025 {
1026 uint8_t cmd[2];
1027 uint8_t md_bits;
1028
1029 md_bits = (MD & 0x02) >> 1;
1030 cmd[0] = md_bits + 0x02;
1031 md_bits = (MD & 0x01) << 7;
1032 cmd[1] = md_bits + (DCP<<4) +0x01;
1033
1034 cmd_68(cmd);
1035 }
1036
1037 /* Start an GPIO Redundancy test */
1038 void LTC681x_adaxd(uint8_t MD, //ADC Mode
1039 uint8_t CHG //GPIO Channels to be measured
1040 )
1041 {
1042 uint8_t cmd[4];
1043 uint8_t md_bits;
1044
1045 md_bits = (MD & 0x02) >> 1;
1046 cmd[0] = md_bits + 0x04;
1047 md_bits = (MD & 0x01) << 7;
1048 cmd[1] = md_bits + CHG ;
1049
1050 cmd_68(cmd);
1051 }
1052
1053 /* Start a Status register redundancy test Conversion */
1054 void LTC681x_adstatd(uint8_t MD, //ADC Mode
1055 uint8_t CHST //Stat Channels to be measured
1056 )
1057 {
1058 uint8_t cmd[2];
1059 uint8_t md_bits;
1060
1061 md_bits = (MD & 0x02) >> 1;
1062 cmd[0] = md_bits + 0x04;
1063 md_bits = (MD & 0x01) << 7;
1064 cmd[1] = md_bits + 0x08 + CHST ;
1065
1066 cmd_68(cmd);
1067 }
1068
1069 /* Runs the Digital Filter Self Test */
1070 int16_t LTC681x_run_cell_adc_st(uint8_t adc_reg, // Type of register
1071 uint8_t total_ic, // Number of ICs in the daisy chain
1072 cell_asic *ic, // A two dimensional array that will store the data
1073 uint8_t md, // ADC Mode
1074 bool adcopt // ADCOPT bit in the configuration register
1075 )
1076 {
1077 int16_t error = 0;
1078 uint16_t expected_result = 0;
1079
1080 for (int self_test = 1; self_test<3; self_test++)
1081 {
1082 expected_result = LTC681x_st_lookup(md,self_test,adcopt);
1083 wakeup_idle(total_ic);
1084
1085 switch (adc_reg)
1086 {
1087 case CELL:
1088 wakeup_idle(total_ic);
1089 LTC681x_clrcell();
1090 LTC681x_cvst(md,self_test);
1091 LTC681x_pollAdc();
1092
1093 wakeup_idle(total_ic);
1094 error = LTC681x_rdcv(0, total_ic,ic);
1095 for (int cic = 0; cic < total_ic; cic++)
1096 {
1097 for (int channel=0; channel< ic[cic].ic_reg.cell_channels; channel++)
1098 {
1099
1100 if (ic[cic].cells.c_codes[channel] != expected_result)
1101 {
1102 error = error+1;
1103 }
1104 }
1105 }
1106 break;
1107 case AUX:
1108 error = 0;
1109 wakeup_idle(total_ic);
1110 LTC681x_clraux();
1111 LTC681x_axst(md,self_test);
1112 LTC681x_pollAdc();
1113
1114 wakeup_idle(total_ic);
1115 LTC681x_rdaux(0, total_ic,ic);
1116 for (int cic = 0; cic < total_ic; cic++)
1117 {
1118 for (int channel=0; channel< ic[cic].ic_reg.aux_channels; channel++)
1119 {
1120
1121 if (ic[cic].aux.a_codes[channel] != expected_result)
1122 {
1123 error = error+1;
1124 }
1125 }
1126 }
1127 break;
1128 case STAT:
1129 wakeup_idle(total_ic);
1130 LTC681x_clrstat();
1131 LTC681x_statst(md,self_test);
1132 LTC681x_pollAdc();
1133
1134 wakeup_idle(total_ic);
1135 error = LTC681x_rdstat(0,total_ic,ic);
1136 for (int cic = 0; cic < total_ic; cic++)
1137 {
1138 for (int channel=0; channel< ic[cic].ic_reg.stat_channels; channel++)
1139 {
1140 if (ic[cic].stat.stat_codes[channel] != expected_result)
1141 {
1142 error = error+1;
1143 }
1144 }
1145 }
1146 break;
1147
1148 default:
1149 error = -1;
1150 break;
1151 }
1152 }
1153
1154 return(error);
1155 }
1156
1157 /* Runs the ADC overlap test for the IC */
1158 uint16_t LTC681x_run_adc_overlap(uint8_t total_ic, // Number of ICs in the daisy chain
1159 cell_asic *ic // A two dimensional array that will store the data
1160 )
1161 {
1162 uint16_t error = 0;
1163 int32_t measure_delta =0;
1164 int16_t failure_pos_limit = 20;
1165 int16_t failure_neg_limit = -20;
1166 int32_t a, b;
1167
1168 wakeup_idle(total_ic);
1169 LTC681x_adol(MD_7KHZ_3KHZ,DCP_DISABLED);
1170 LTC681x_pollAdc();
1171 wakeup_idle(total_ic);
1172 error = LTC681x_rdcv(0, total_ic,ic);
1173
1174 for (int cic = 0; cic<total_ic; cic++)
1175 {
1176 measure_delta = (int32_t)ic[cic].cells.c_codes[6]-(int32_t)ic[cic].cells.c_codes[7];
1177
1178 if ((measure_delta>failure_pos_limit) || (measure_delta<failure_neg_limit))
1179 {
1180 error = error | (1<<(cic-1));
1181 }
1182 }
1183
1184 return(error);
1185 }
1186
1187 /* Runs the redundancy self test */
1188 int16_t LTC681x_run_adc_redundancy_st(uint8_t adc_mode, // ADC Mode
1189 uint8_t adc_reg, // Type of register
1190 uint8_t total_ic, // Number of ICs in the daisy chain
1191 cell_asic *ic // A two dimensional array that will store the data
1192 )
1193 {
1194 int16_t error = 0;
1195 for (int self_test = 1; self_test<3; self_test++)
1196 {
1197 wakeup_idle(total_ic);
1198 switch (adc_reg)
1199 {
1200 case AUX:
1201 LTC681x_clraux();
1202 LTC681x_adaxd(adc_mode,AUX_CH_ALL);
1203 LTC681x_pollAdc();
1204
1205 wakeup_idle(total_ic);
1206 error = LTC681x_rdaux(0, total_ic,ic);
1207 for (int cic = 0; cic < total_ic; cic++)
1208 {
1209 for (int channel=0; channel< ic[cic].ic_reg.aux_channels; channel++)
1210 {
1211 if (ic[cic].aux.a_codes[channel] >= 65280)
1212 {
1213 error = error+1;
1214 }
1215 }
1216 }
1217 break;
1218 case STAT:
1219 LTC681x_clrstat();
1220 LTC681x_adstatd(adc_mode,STAT_CH_ALL);
1221 LTC681x_pollAdc();
1222 wakeup_idle(total_ic);
1223 error = LTC681x_rdstat(0,total_ic,ic);
1224 for (int cic = 0; cic < total_ic; cic++)
1225 {
1226 for (int channel=0; channel< ic[cic].ic_reg.stat_channels; channel++)
1227 {
1228 if (ic[cic].stat.stat_codes[channel] >= 65280)
1229 {
1230 error = error+1;
1231 }
1232 }
1233 }
1234 break;
1235
1236 default:
1237 error = -1;
1238 break;
1239 }
1240 }
1241 return(error);
1242 }
1243
1244 /* Looks up the result pattern for digital filter self test */
1245 uint16_t LTC681x_st_lookup(uint8_t MD, //ADC Mode
1246 uint8_t ST, //Self Test
1247 bool adcopt // ADCOPT bit in the configuration register
1248 )
1249 {
1250 uint16_t test_pattern = 0;
1251
1252 if (MD == 1)
1253 {
1254 if ( adcopt == false)
1255 {
1256 if (ST == 1)
1257 {
1258 test_pattern = 0x9565;
1259 }
1260 else
1261 {
1262 test_pattern = 0x6A9A;
1263 }
1264 }
1265 else
1266 {
1267 if (ST == 1)
1268 {
1269 test_pattern = 0x9553;
1270 }
1271 else
1272 {
1273 test_pattern = 0x6AAC;
1274 }
1275 }
1276 }
1277 else
1278 {
1279 if (ST == 1)
1280 {
1281 test_pattern = 0x9555;
1282 }
1283 else
1284 {
1285 test_pattern = 0x6AAA;
1286 }
1287 }
1288 return(test_pattern);
1289 }
1290
1291 /* Start an open wire Conversion */
1292 void LTC681x_adow(uint8_t MD, //ADC Mode
1293 uint8_t PUP,//Pull up/Pull down current
1294 uint8_t CH, //Channels
1295 uint8_t DCP//Discharge Permit
1296 )
1297 {
1298 uint8_t cmd[2];
1299 uint8_t md_bits;
1300
1301 md_bits = (MD & 0x02) >> 1;
1302 cmd[0] = md_bits + 0x02;
1303 md_bits = (MD & 0x01) << 7;
1304 cmd[1] = md_bits + 0x28 + (PUP<<6) + CH+(DCP<<4);
1305
1306 cmd_68(cmd);
1307 }
1308
1309 /* Start GPIOs open wire ADC conversion */
1310 void LTC681x_axow(uint8_t MD, //ADC Mode
1311 uint8_t PUP //Pull up/Pull down current
1312 )
1313 {
1314 uint8_t cmd[2];
1315 uint8_t md_bits;
1316
1317 md_bits = (MD & 0x02) >> 1;
1318 cmd[0] = md_bits + 0x04;
1319 md_bits = (MD & 0x01) << 7;
1320 cmd[1] = md_bits + 0x10+ (PUP<<6) ;//+ CH;
1321
1322 cmd_68(cmd);
1323 }
1324
1325 /* Runs the data sheet algorithm for open wire for single cell detection */
1326 void LTC681x_run_openwire_single(uint8_t total_ic, // Number of ICs in the daisy chain
1327 cell_asic ic[] // A two dimensional array that will store the data
1328 )
1329 {
1330 uint16_t OPENWIRE_THRESHOLD = 4000;
1331 const uint8_t N_CHANNELS = ic[0].ic_reg.cell_channels;
1332
1333 uint16_t pullUp[total_ic][N_CHANNELS];
1334 uint16_t pullDwn[total_ic][N_CHANNELS];
1335 int16_t openWire_delta[total_ic][N_CHANNELS];
1336
1337 int8_t error;
1338 int8_t i;
1339 uint32_t conv_time=0;
1340
1341 wakeup_sleep(total_ic);
1342 LTC681x_clrcell();
1343
1344 // Pull Ups
1345 for (i = 0; i < 3; i++)
1346 {
1347 wakeup_idle(total_ic);
1348 LTC681x_adow(MD_26HZ_2KHZ,PULL_UP_CURRENT,CELL_CH_ALL,DCP_DISABLED);
1349 conv_time =LTC681x_pollAdc();
1350 }
1351
1352 wakeup_idle(total_ic);
1353 error=LTC681x_rdcv(0, total_ic,ic);
1354
1355 for (int cic=0; cic<total_ic; cic++)
1356 {
1357 for (int cell=0; cell<N_CHANNELS; cell++)
1358 {
1359 pullUp[cic][cell] = ic[cic].cells.c_codes[cell];
1360 }
1361 }
1362
1363 // Pull Downs
1364 for (i = 0; i < 3; i++)
1365 {
1366 wakeup_idle(total_ic);
1367 LTC681x_adow(MD_26HZ_2KHZ,PULL_DOWN_CURRENT,CELL_CH_ALL,DCP_DISABLED);
1368 conv_time =LTC681x_pollAdc();
1369 }
1370
1371 wakeup_idle(total_ic);
1372 error=LTC681x_rdcv(0, total_ic,ic);
1373
1374 for (int cic=0; cic<total_ic; cic++)
1375 {
1376 for (int cell=0; cell<N_CHANNELS; cell++)
1377 {
1378 pullDwn[cic][cell] = ic[cic].cells.c_codes[cell];
1379 }
1380 }
1381
1382 for (int cic=0; cic<total_ic; cic++)
1383 {
1384 ic[cic].system_open_wire = 0xFFFF;
1385
1386 for (int cell=0; cell<N_CHANNELS; cell++)
1387 {
1388 if (pullDwn[cic][cell] < pullUp[cic][cell])
1389 {
1390 openWire_delta[cic][cell] = (pullUp[cic][cell] - pullDwn[cic][cell]);
1391 }
1392 else
1393 {
1394 openWire_delta[cic][cell] = 0;
1395 }
1396
1397 if (openWire_delta[cic][cell]>OPENWIRE_THRESHOLD)
1398 {
1399 ic[cic].system_open_wire = cell+1;
1400 }
1401 }
1402
1403 if (pullUp[cic][0] == 0)
1404 {
1405 ic[cic].system_open_wire = 0;
1406 }
1407
1408 if (pullUp[cic][(N_CHANNELS-1)] == 0)//checking the Pull up value of the top measured channel
1409 {
1410 ic[cic].system_open_wire = N_CHANNELS;
1411 }
1412 }
1413 }
1414
1415 /* Runs the data sheet algorithm for open wire for multiple cell and two consecutive cells detection */
1416 void LTC681x_run_openwire_multi(uint8_t total_ic, // Number of ICs in the daisy chain
1417 cell_asic ic[] // A two dimensional array that will store the data
1418 )
1419 {
1420 uint16_t OPENWIRE_THRESHOLD = 4000;
1421 const uint8_t N_CHANNELS = ic[0].ic_reg.cell_channels;
1422
1423 uint16_t pullUp[total_ic][N_CHANNELS];
1424 uint16_t pullDwn[total_ic][N_CHANNELS];
1425 uint16_t openWire_delta[total_ic][N_CHANNELS];
1426
1427 int8_t error;
1428 int8_t opencells[N_CHANNELS];
1429 int8_t n=0;
1430 int8_t i,j,k;
1431 uint32_t conv_time=0;
1432
1433 wakeup_sleep(total_ic);
1434 LTC681x_clrcell();
1435
1436 // Pull Ups
1437 for (i = 0; i < 5; i++)
1438 {
1439 wakeup_idle(total_ic);
1440 LTC681x_adow(MD_26HZ_2KHZ,PULL_UP_CURRENT,CELL_CH_ALL,DCP_DISABLED);
1441 conv_time =LTC681x_pollAdc();
1442 }
1443
1444 wakeup_idle(total_ic);
1445 error = LTC681x_rdcv(0, total_ic,ic);
1446
1447 for (int cic=0; cic<total_ic; cic++)
1448 {
1449 for (int cell=0; cell<N_CHANNELS; cell++)
1450 {
1451 pullUp[cic][cell] = ic[cic].cells.c_codes[cell];
1452 }
1453 }
1454
1455 // Pull Downs
1456 for (i = 0; i < 5; i++)
1457 {
1458 wakeup_idle(total_ic);
1459 LTC681x_adow(MD_26HZ_2KHZ,PULL_DOWN_CURRENT,CELL_CH_ALL,DCP_DISABLED);
1460 conv_time = LTC681x_pollAdc();
1461 }
1462
1463 wakeup_idle(total_ic);
1464 error = LTC681x_rdcv(0, total_ic,ic);
1465
1466 for (int cic=0; cic<total_ic; cic++)
1467 {
1468 for (int cell=0; cell<N_CHANNELS; cell++)
1469 {
1470 pullDwn[cic][cell] = ic[cic].cells.c_codes[cell];
1471 }
1472 }
1473
1474 for (int cic=0; cic<total_ic; cic++)
1475 {
1476 for (int cell=0; cell<N_CHANNELS; cell++)
1477 {
1478 if (pullDwn[cic][cell] < pullUp[cic][cell])
1479 {
1480 openWire_delta[cic][cell] = (pullUp[cic][cell] - pullDwn[cic][cell]);
1481 }
1482 else
1483 {
1484 openWire_delta[cic][cell] = 0;
1485 }
1486 }
1487 }
1488
1489 for (int cic=0; cic<total_ic; cic++)
1490 {
1491 n=0;
1492
1493 Serial.print("IC:");
1494 Serial.println(cic+1, DEC);
1495
1496 for (int cell=0; cell<N_CHANNELS; cell++)
1497 {
1498
1499 if (openWire_delta[cic][cell]>OPENWIRE_THRESHOLD)
1500 {
1501 opencells[n] = cell+1;
1502 n++;
1503 for (int j = cell; j < N_CHANNELS-3 ; j++)
1504 {
1505 if (pullUp[cic][j + 2] == 0)
1506 {
1507 opencells[n] = j+2;
1508 n++;
1509 }
1510 }
1511 if((cell==N_CHANNELS-4) && (pullDwn[cic][N_CHANNELS-3] == 0))
1512 {
1513 opencells[n] = N_CHANNELS-2;
1514 n++;
1515 }
1516 }
1517 }
1518 if (pullDwn[cic][0] == 0)
1519 {
1520 opencells[n] = 0;
1521 Serial.println("Cell 0 is Open and multiple open wires maybe possible.");
1522 n++;
1523 }
1524
1525 if (pullDwn[cic][N_CHANNELS-1] == 0)
1526 {
1527 opencells[n] = N_CHANNELS;
1528 n++;
1529 }
1530
1531 if (pullDwn[cic][N_CHANNELS-2] == 0)
1532 {
1533 opencells[n] = N_CHANNELS-1;
1534 n++;
1535 }
1536
1537 //Removing repetitive elements
1538 for(i=0;i<n;i++)
1539 {
1540 for(j=i+1;j<n;)
1541 {
1542 if(opencells[i]==opencells[j])
1543 {
1544 for(k=j;k<n;k++)
1545 opencells[k]=opencells[k+1];
1546
1547 n--;
1548 }
1549 else
1550 j++;
1551 }
1552 }
1553
1554 // Sorting open cell array
1555 for(int i=0; i<n; i++)
1556 {
1557 for(int j=0; j<n-1; j++)
1558 {
1559 if( opencells[j] > opencells[j+1] )
1560 {
1561 k = opencells[j];
1562 opencells[j] = opencells[j+1];
1563 opencells[j+1] = k;
1564 }
1565 }
1566 }
1567
1568 //Checking the value of n
1569 Serial.println("Number of Open wires:");
1570 Serial.println(n);
1571
1572 //Printing open cell array
1573 Serial.println("OPEN CELLS:");
1574 if(n==0)
1575 {
1576 Serial.println("No Open wires");
1577 }
1578 else
1579 {
1580 for(i=0;i<n;i++)
1581 {
1582 Serial.println(opencells[i]);
1583 }
1584 }
1585 }
1586 Serial.println("\n");
1587 }
1588
1589 /* Runs open wire for GPIOs */
1590 void LTC681x_run_gpio_openwire(uint8_t total_ic, // Number of ICs in the daisy chain
1591 cell_asic ic[] // A two dimensional array that will store the data
1592 )
1593 {
1594 uint16_t OPENWIRE_THRESHOLD = 150;
1595 const uint8_t N_CHANNELS = ic[0].ic_reg.aux_channels +1;
1596
1597 uint16_t aux_val[total_ic][N_CHANNELS];
1598 uint16_t pDwn[total_ic][N_CHANNELS];
1599 uint16_t ow_delta[total_ic][N_CHANNELS];
1600
1601 int8_t error;
1602 int8_t i;
1603 uint32_t conv_time=0;
1604
1605 wakeup_sleep(total_ic);
1606 LTC681x_clraux();
1607
1608 for (i = 0; i < 3; i++)
1609 {
1610 wakeup_idle(total_ic);
1611 LTC681x_adax(MD_7KHZ_3KHZ, AUX_CH_ALL);
1612 conv_time= LTC681x_pollAdc();
1613 }
1614
1615 wakeup_idle(total_ic);
1616 error = LTC681x_rdaux(0, total_ic,ic);
1617
1618 for (int cic=0; cic<total_ic; cic++)
1619 {
1620 for (int channel=0; channel<N_CHANNELS; channel++)
1621 {
1622 aux_val[cic][channel]=ic[cic].aux.a_codes[channel];
1623 }
1624 }
1625 LTC681x_clraux();
1626
1627 // pull downs
1628 for (i = 0; i < 3; i++)
1629 {
1630 wakeup_idle(total_ic);
1631 LTC681x_axow(MD_7KHZ_3KHZ,PULL_DOWN_CURRENT);
1632 conv_time =LTC681x_pollAdc();
1633 }
1634
1635 wakeup_idle(total_ic);
1636 error = LTC681x_rdaux(0, total_ic,ic);
1637
1638 for (int cic=0; cic<total_ic; cic++)
1639 {
1640 for (int channel=0; channel<N_CHANNELS; channel++)
1641 {
1642 pDwn[cic][channel]=ic[cic].aux.a_codes[channel] ;
1643 }
1644 }
1645
1646 for (int cic=0; cic<total_ic; cic++)
1647 {
1648 ic[cic].system_open_wire = 0xFFFF;
1649
1650 for (int channel=0; channel<N_CHANNELS; channel++)
1651 {
1652 if (pDwn[cic][channel] > aux_val[cic][channel])
1653 {
1654 ow_delta[cic][channel] = (pDwn[cic][channel] - aux_val[cic][channel]);
1655 }
1656 else
1657 {
1658 ow_delta[cic][channel] = 0;
1659 }
1660
1661 if(channel<5)
1662 {
1663 if (ow_delta[cic][channel] > OPENWIRE_THRESHOLD)
1664 {
1665 ic[cic].system_open_wire= channel+1;
1666
1667 }
1668 }
1669 else if(channel>5)
1670 {
1671 if (ow_delta[cic][channel] > OPENWIRE_THRESHOLD)
1672 {
1673 ic[cic].system_open_wire= channel;
1674
1675 }
1676 }
1677 }
1678 }
1679 }
1680
1681 /* Clears all of the DCC bits in the configuration registers */
1682 void LTC681x_clear_discharge(uint8_t total_ic, // Number of ICs in the daisy chain
1683 cell_asic *ic // A two dimensional array that will store the data
1684 )
1685 {
1686 for (int i=0; i<total_ic; i++)
1687 {
1688 ic[i].config.tx_data[4] = 0;
1689 ic[i].config.tx_data[5] =ic[i].config.tx_data[5]&(0xF0);
1690 ic[i].configb.tx_data[0]=ic[i].configb.tx_data[0]&(0x0F);
1691 ic[i].configb.tx_data[1]=ic[i].configb.tx_data[1]&(0xF0);
1692 }
1693 }
1694
1695 /* Writes the pwm register */
1696 void LTC681x_wrpwm(uint8_t total_ic, // Number of ICs in the daisy chain
1697 uint8_t pwmReg, // The PWM Register to be written A or B
1698 cell_asic ic[] // A two dimensional array that stores the data to be written
1699 )
1700 {
1701 uint8_t cmd[2];
1702 uint8_t write_buffer[256];
1703 uint8_t write_count = 0;
1704 uint8_t c_ic = 0;
1705 if (pwmReg == 0)
1706 {
1707 cmd[0] = 0x00;
1708 cmd[1] = 0x20;
1709 }
1710 else
1711 {
1712 cmd[0] = 0x00;
1713 cmd[1] = 0x1C;
1714 }
1715
1716 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
1717 {
1718 if (ic->isospi_reverse == false)
1719 {
1720 c_ic = current_ic;
1721 }
1722 else
1723 {
1724 c_ic = total_ic - current_ic - 1;
1725 }
1726
1727 for (uint8_t data = 0; data<6; data++)
1728 {
1729 write_buffer[write_count] = ic[c_ic].pwm.tx_data[data];
1730 write_count++;
1731 }
1732 }
1733 write_68(total_ic, cmd, write_buffer);
1734 }
1735
1736
1737 /* Reads pwm registers of a LTC681x daisy chain */
1738 int8_t LTC681x_rdpwm(uint8_t total_ic, //Number of ICs in the system
1739 uint8_t pwmReg, // The PWM Register to be written A or B
1740 cell_asic ic[] // A two dimensional array that will store the data
1741 )
1742 {
1743 const uint8_t BYTES_IN_REG = 8;
1744 uint8_t cmd[4];
1745 uint8_t read_buffer[256];
1746 int8_t pec_error = 0;
1747 uint16_t data_pec;
1748 uint16_t calc_pec;
1749 uint8_t c_ic = 0;
1750
1751 if (pwmReg == 0)
1752 {
1753 cmd[0] = 0x00;
1754 cmd[1] = 0x22;
1755 }
1756 else
1757 {
1758 cmd[0] = 0x00;
1759 cmd[1] = 0x1E;
1760 }
1761
1762 pec_error = read_68(total_ic, cmd, read_buffer);
1763 for (uint8_t current_ic =0; current_ic<total_ic; current_ic++)
1764 {
1765 if (ic->isospi_reverse == false)
1766 {
1767 c_ic = current_ic;
1768 }
1769 else
1770 {
1771 c_ic = total_ic - current_ic - 1;
1772 }
1773
1774 for (int byte=0; byte<8; byte++)
1775 {
1776 ic[c_ic].pwm.rx_data[byte] = read_buffer[byte+(8*current_ic)];
1777 }
1778
1779 calc_pec = pec15_calc(6,&read_buffer[8*current_ic]);
1780 data_pec = read_buffer[7+(8*current_ic)] | (read_buffer[6+(8*current_ic)]<<8);
1781 if (calc_pec != data_pec )
1782 {
1783 ic[c_ic].pwm.rx_pec_match = 1;
1784 }
1785 else ic[c_ic].pwm.rx_pec_match = 0;
1786 }
1787 return(pec_error);
1788 }
1789
1790 /* Write the LTC681x Sctrl register */
1791 void LTC681x_wrsctrl(uint8_t total_ic, // Number of ICs in the daisy chain
1792 uint8_t sctrl_reg, // The Sctrl Register to be written A or B
1793 cell_asic *ic // A two dimensional array that stores the data to be written
1794 )
1795 {
1796 uint8_t cmd[2];
1797 uint8_t write_buffer[256];
1798 uint8_t write_count = 0;
1799 uint8_t c_ic = 0;
1800 if (sctrl_reg == 0)
1801 {
1802 cmd[0] = 0x00;
1803 cmd[1] = 0x14;
1804 }
1805 else
1806 {
1807 cmd[0] = 0x00;
1808 cmd[1] = 0x1C;
1809 }
1810
1811 for(uint8_t current_ic = 0; current_ic<total_ic;current_ic++)
1812 {
1813 if(ic->isospi_reverse == false){c_ic = current_ic;}
1814 else{c_ic = total_ic - current_ic - 1;}
1815
1816
1817 for(uint8_t data = 0; data<6;data++)
1818 {
1819 write_buffer[write_count] = ic[c_ic].sctrl.tx_data[data];
1820 write_count++;
1821 }
1822 }
1823 write_68(total_ic, cmd, write_buffer);
1824 }
1825
1826 /* Reads sctrl registers of a LTC681x daisy chain */
1827 int8_t LTC681x_rdsctrl(uint8_t total_ic, // Number of ICs in the daisy chain
1828 uint8_t sctrl_reg, // The Sctrl Register to be written A or B
1829 cell_asic *ic // A two dimensional array that the function stores the read data
1830 )
1831 {
1832 uint8_t cmd[4];
1833 uint8_t read_buffer[256];
1834 int8_t pec_error = 0;
1835 uint16_t data_pec;
1836 uint16_t calc_pec;
1837 uint8_t c_ic = 0;
1838
1839 if (sctrl_reg == 0)
1840 {
1841 cmd[0] = 0x00;
1842 cmd[1] = 0x16;
1843 }
1844 else
1845 {
1846 cmd[0] = 0x00;
1847 cmd[1] = 0x1E;
1848 }
1849
1850 pec_error = read_68(total_ic, cmd, read_buffer);
1851
1852 for(uint8_t current_ic =0; current_ic<total_ic; current_ic++)
1853 {
1854 if(ic->isospi_reverse == false){c_ic = current_ic;}
1855 else{c_ic = total_ic - current_ic - 1;}
1856
1857
1858 for(int byte=0; byte<8;byte++)
1859 {
1860 ic[c_ic].sctrl.rx_data[byte] = read_buffer[byte+(8*current_ic)];
1861 }
1862
1863 calc_pec = pec15_calc(6,&read_buffer[8*current_ic]);
1864 data_pec = read_buffer[7+(8*current_ic)] | (read_buffer[6+(8*current_ic)]<<8);
1865 if(calc_pec != data_pec )
1866 {
1867 ic[c_ic].sctrl.rx_pec_match = 1;
1868 }
1869 else ic[c_ic].sctrl.rx_pec_match = 0;
1870
1871 }
1872 return(pec_error);
1873 }
1874
1875 /*
1876 Start Sctrl data communication
1877 This command will start the sctrl pulse communication over the spins
1878 */
1879 void LTC681x_stsctrl()
1880 {
1881 uint8_t cmd[4];
1882 uint16_t cmd_pec;
1883
1884 cmd[0] = 0x00;
1885 cmd[1] = 0x19;
1886 cmd_pec = pec15_calc(2, cmd);
1887 cmd[2] = (uint8_t)(cmd_pec >> 8);
1888 cmd[3] = (uint8_t)(cmd_pec);
1889
1890 cs_low(CS_PIN);
1891 spi_write_array(4,cmd);
1892 cs_high(CS_PIN);
1893 }
1894
1895 /*
1896 The command clears the Sctrl registers and initializes
1897 all values to 0. The register will read back hexadecimal 0x00
1898 after the command is sent.
1899 */
1900 void LTC681x_clrsctrl()
1901 {
1902 uint8_t cmd[2]= {0x00 , 0x18};
1903 cmd_68(cmd);
1904 }
1905
1906 /* Writes the comm register */
1907 void LTC681x_wrcomm(uint8_t total_ic, //The number of ICs being written to
1908 cell_asic ic[] // A two dimensional array that stores the data to be written
1909 )
1910 {
1911 uint8_t cmd[2]= {0x07 , 0x21};
1912 uint8_t write_buffer[256];
1913 uint8_t write_count = 0;
1914 uint8_t c_ic = 0;
1915 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
1916 {
1917 if (ic->isospi_reverse == false)
1918 {
1919 c_ic = current_ic;
1920 }
1921 else
1922 {
1923 c_ic = total_ic - current_ic - 1;
1924 }
1925
1926 for (uint8_t data = 0; data<6; data++)
1927 {
1928 write_buffer[write_count] = ic[c_ic].com.tx_data[data];
1929 write_count++;
1930 }
1931 }
1932 write_68(total_ic, cmd, write_buffer);
1933 }
1934
1935 /* Reads COMM registers of a LTC681x daisy chain */
1936 int8_t LTC681x_rdcomm(uint8_t total_ic, //Number of ICs in the system
1937 cell_asic ic[] //A two dimensional array that stores the read data
1938 )
1939 {
1940 uint8_t cmd[2]= {0x07 , 0x22};
1941 uint8_t read_buffer[256];
1942 int8_t pec_error = 0;
1943 uint16_t data_pec;
1944 uint16_t calc_pec;
1945 uint8_t c_ic=0;
1946
1947 pec_error = read_68(total_ic, cmd, read_buffer);
1948
1949 for (uint8_t current_ic = 0; current_ic<total_ic; current_ic++)
1950 {
1951 if (ic->isospi_reverse == false)
1952 {
1953 c_ic = current_ic;
1954 }
1955 else
1956 {
1957 c_ic = total_ic - current_ic - 1;
1958 }
1959
1960 for (int byte=0; byte<8; byte++)
1961 {
1962 ic[c_ic].com.rx_data[byte] = read_buffer[byte+(8*current_ic)];
1963 }
1964
1965 calc_pec = pec15_calc(6,&read_buffer[8*current_ic]);
1966 data_pec = read_buffer[7+(8*current_ic)] | (read_buffer[6+(8*current_ic)]<<8);
1967 if (calc_pec != data_pec )
1968 {
1969 ic[c_ic].com.rx_pec_match = 1;
1970 }
1971 else ic[c_ic].com.rx_pec_match = 0;
1972 }
1973
1974 return(pec_error);
1975 }
1976
1977 /* Shifts data in COMM register out over LTC681x SPI/I2C port */
1978 void LTC681x_stcomm(uint8_t len) //Length of data to be transmitted
1979 {
1980 uint8_t cmd[4];
1981 uint16_t cmd_pec;
1982
1983 cmd[0] = 0x07;
1984 cmd[1] = 0x23;
1985 cmd_pec = pec15_calc(2, cmd);
1986 cmd[2] = (uint8_t)(cmd_pec >> 8);
1987 cmd[3] = (uint8_t)(cmd_pec);
1988
1989 cs_low(CS_PIN);
1990 spi_write_array(4,cmd);
1991 for (int i = 0; i<len*3; i++)
1992 {
1993 spi_read_byte(0xFF);
1994 }
1995 cs_high(CS_PIN);
1996 }
1997
1998 /* Helper function that increments PEC counters */
1999 void LTC681x_check_pec(uint8_t total_ic, //Number of ICs in the system
2000 uint8_t reg, //Type of Register
2001 cell_asic *ic //A two dimensional array that stores the data
2002 )
2003 {
2004 switch (reg)
2005 {
2006 case CFGR:
2007 for (int current_ic = 0 ; current_ic < total_ic; current_ic++)
2008 {
2009 ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].config.rx_pec_match;
2010 ic[current_ic].crc_count.cfgr_pec = ic[current_ic].crc_count.cfgr_pec + ic[current_ic].config.rx_pec_match;
2011 }
2012 break;
2013
2014 case CFGRB:
2015 for (int current_ic = 0 ; current_ic < total_ic; current_ic++)
2016 {
2017 ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].configb.rx_pec_match;
2018 ic[current_ic].crc_count.cfgr_pec = ic[current_ic].crc_count.cfgr_pec + ic[current_ic].configb.rx_pec_match;
2019 }
2020 break;
2021 case CELL:
2022 for (int current_ic = 0 ; current_ic < total_ic; current_ic++)
2023 {
2024 for (int i=0; i<ic[0].ic_reg.num_cv_reg; i++)
2025 {
2026 ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].cells.pec_match[i];
2027 ic[current_ic].crc_count.cell_pec[i] = ic[current_ic].crc_count.cell_pec[i] + ic[current_ic].cells.pec_match[i];
2028 }
2029 }
2030 break;
2031 case AUX:
2032 for (int current_ic = 0 ; current_ic < total_ic; current_ic++)
2033 {
2034 for (int i=0; i<ic[0].ic_reg.num_gpio_reg; i++)
2035 {
2036 ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + (ic[current_ic].aux.pec_match[i]);
2037 ic[current_ic].crc_count.aux_pec[i] = ic[current_ic].crc_count.aux_pec[i] + (ic[current_ic].aux.pec_match[i]);
2038 }
2039 }
2040
2041 break;
2042 case STAT:
2043 for (int current_ic = 0 ; current_ic < total_ic; current_ic++)
2044 {
2045
2046 for (int i=0; i<ic[0].ic_reg.num_stat_reg-1; i++)
2047 {
2048 ic[current_ic].crc_count.pec_count = ic[current_ic].crc_count.pec_count + ic[current_ic].stat.pec_match[i];
2049 ic[current_ic].crc_count.stat_pec[i] = ic[current_ic].crc_count.stat_pec[i] + ic[current_ic].stat.pec_match[i];
2050 }
2051 }
2052 break;
2053 default:
2054 break;
2055 }
2056 }
2057
2058 /* Helper Function to reset PEC counters */
2059 void LTC681x_reset_crc_count(uint8_t total_ic, //Number of ICs in the system
2060 cell_asic *ic //A two dimensional array that stores the data
2061 )
2062 {
2063 for (int current_ic = 0 ; current_ic < total_ic; current_ic++)
2064 {
2065 ic[current_ic].crc_count.pec_count = 0;
2066 ic[current_ic].crc_count.cfgr_pec = 0;
2067 for (int i=0; i<6; i++)
2068 {
2069 ic[current_ic].crc_count.cell_pec[i]=0;
2070
2071 }
2072 for (int i=0; i<4; i++)
2073 {
2074 ic[current_ic].crc_count.aux_pec[i]=0;
2075 }
2076 for (int i=0; i<2; i++)
2077 {
2078 ic[current_ic].crc_count.stat_pec[i]=0;
2079 }
2080 }
2081 }
2082
2083 /* Helper function to initialize CFG variables */
2084 void LTC681x_init_cfg(uint8_t total_ic, //Number of ICs in the system
2085 cell_asic *ic //A two dimensional array that stores the data
2086 )
2087 {
2088 for (uint8_t current_ic = 0; current_ic<total_ic;current_ic++)
2089 {
2090 for (int j =0; j<6; j++)
2091 {
2092 ic[current_ic].config.tx_data[j] = 0;
2093 }
2094 }
2095 }
2096
2097 /* Helper function to set CFGR variable */
2098 void LTC681x_set_cfgr(uint8_t nIC, // Current IC
2099 cell_asic *ic, // A two dimensional array that stores the data
2100 bool refon, // The REFON bit
2101 bool adcopt, // The ADCOPT bit
2102 bool gpio[5], // The GPIO bits
2103 bool dcc[12], // The DCC bits
2104 bool dcto[4], // The Dcto bits
2105 uint16_t uv, // The UV value
2106 uint16_t ov // The OV value
2107 )
2108 {
2109 LTC681x_set_cfgr_refon(nIC,ic,refon);
2110 LTC681x_set_cfgr_adcopt(nIC,ic,adcopt);
2111 LTC681x_set_cfgr_gpio(nIC,ic,gpio);
2112 LTC681x_set_cfgr_dis(nIC,ic,dcc);
2113 LTC681x_set_cfgr_dcto(nIC,ic,dcto);
2114 LTC681x_set_cfgr_uv(nIC, ic, uv);
2115 LTC681x_set_cfgr_ov(nIC, ic, ov);
2116 }
2117
2118 /* Helper function to set the REFON bit */
2119 void LTC681x_set_cfgr_refon(uint8_t nIC, cell_asic *ic, bool refon)
2120 {
2121 if (refon) ic[nIC].config.tx_data[0] = ic[nIC].config.tx_data[0]|0x04;
2122 else ic[nIC].config.tx_data[0] = ic[nIC].config.tx_data[0]&0xFB;
2123 }
2124
2125 /* Helper function to set the ADCOPT bit */
2126 void LTC681x_set_cfgr_adcopt(uint8_t nIC, cell_asic *ic, bool adcopt)
2127 {
2128 if (adcopt) ic[nIC].config.tx_data[0] = ic[nIC].config.tx_data[0]|0x01;
2129 else ic[nIC].config.tx_data[0] = ic[nIC].config.tx_data[0]&0xFE;
2130 }
2131
2132 /* Helper function to set GPIO bits */
2133 void LTC681x_set_cfgr_gpio(uint8_t nIC, cell_asic *ic,bool gpio[5])
2134 {
2135 for (int i =0; i<5; i++)
2136 {
2137 if (gpio[i])ic[nIC].config.tx_data[0] = ic[nIC].config.tx_data[0]|(0x01<<(i+3));
2138 else ic[nIC].config.tx_data[0] = ic[nIC].config.tx_data[0]&(~(0x01<<(i+3)));
2139 }
2140 }
2141
2142 /* Helper function to control discharge */
2143 void LTC681x_set_cfgr_dis(uint8_t nIC, cell_asic *ic,bool dcc[12])
2144 {
2145 for (int i =0; i<8; i++)
2146 {
2147 if (dcc[i])ic[nIC].config.tx_data[4] = ic[nIC].config.tx_data[4]|(0x01<<i);
2148 else ic[nIC].config.tx_data[4] = ic[nIC].config.tx_data[4]& (~(0x01<<i));
2149 }
2150 for (int i =0; i<4; i++)
2151 {
2152 if (dcc[i+8])ic[nIC].config.tx_data[5] = ic[nIC].config.tx_data[5]|(0x01<<i);
2153 else ic[nIC].config.tx_data[5] = ic[nIC].config.tx_data[5]&(~(0x01<<i));
2154 }
2155 }
2156
2157 /* Helper function to control discharge time value */
2158 void LTC681x_set_cfgr_dcto(uint8_t nIC, cell_asic *ic,bool dcto[4])
2159 {
2160 for(int i =0;i<4;i++)
2161 {
2162 if(dcto[i])ic[nIC].config.tx_data[5] = ic[nIC].config.tx_data[5]|(0x01<<(i+4));
2163 else ic[nIC].config.tx_data[5] = ic[nIC].config.tx_data[5]&(~(0x01<<(i+4)));
2164 }
2165 }
2166
2167 /* Helper Function to set UV value in CFG register */
2168 void LTC681x_set_cfgr_uv(uint8_t nIC, cell_asic *ic,uint16_t uv)
2169 {
2170 uint16_t tmp = (uv/16)-1;
2171 ic[nIC].config.tx_data[1] = 0x00FF & tmp;
2172 ic[nIC].config.tx_data[2] = ic[nIC].config.tx_data[2]&0xF0;
2173 ic[nIC].config.tx_data[2] = ic[nIC].config.tx_data[2]|((0x0F00 & tmp)>>8);
2174 }
2175
2176 /* Helper function to set OV value in CFG register */
2177 void LTC681x_set_cfgr_ov(uint8_t nIC, cell_asic *ic,uint16_t ov)
2178 {
2179 uint16_t tmp = (ov/16);
2180 ic[nIC].config.tx_data[3] = 0x00FF & (tmp>>4);
2181 ic[nIC].config.tx_data[2] = ic[nIC].config.tx_data[2]&0x0F;
2182 ic[nIC].config.tx_data[2] = ic[nIC].config.tx_data[2]|((0x000F & tmp)<<4);
2183 }
LTC681x_rdcv_reg
void LTC681x_rdcv_reg(uint8_t reg, uint8_t total_ic, uint8_t *data)
Reads the raw cell voltage register data.
Definition: LTC681x.cpp:694
bms_hardware.h
LTC681x_adaxd
void LTC681x_adaxd(uint8_t MD, uint8_t CHG)
Start an GPIO Redundancy test.
Definition: LTC681x.cpp:1038
cell_asic::sctrl
ic_register sctrl
Definition: LTC681x.h:180
LTC681x_adcvax
void LTC681x_adcvax(uint8_t MD, uint8_t DCP)
Starts cell voltage and GPIO 1&2 conversion.
Definition: LTC681x.cpp:415
pec_counter::stat_pec
uint16_t stat_pec[2]
Status register data PEC error count.
Definition: LTC681x.h:155
LTC681x.h
General BMS Library.
spi_write_array
void spi_write_array(uint8_t len, uint8_t data[])
Definition: LTC68031.cpp:349
ax::a_codes
uint16_t a_codes[9]
Aux Voltage Codes.
Definition: LTC681x.h:126
cell_asic::configb
ic_register configb
Definition: LTC681x.h:173
LTC681x_stcomm
void LTC681x_stcomm(uint8_t len)
Issues a stcomm command and clocks data out of the COMM register.
Definition: LTC681x.cpp:1978
LTC681x_adow
void LTC681x_adow(uint8_t MD, uint8_t PUP, uint8_t CH, uint8_t DCP)
Start an open wire Conversion.
Definition: LTC681x.cpp:1292
LTC681x_set_cfgr_gpio
void LTC681x_set_cfgr_gpio(uint8_t nIC, cell_asic *ic, bool gpio[5])
Definition: LTC681x.cpp:2133
spi_read_byte
uint8_t spi_read_byte(uint8_t tx_dat)
Definition: bms_hardware.cpp:108
spi_write_read
void spi_write_read(uint8_t tx_Data[], uint8_t tx_len, uint8_t *rx_data, uint8_t rx_len)
Definition: LTC68031.cpp:361
LTC681x_diagn
void LTC681x_diagn()
Starts the Mux Decoder diagnostic self test Running this command will start the Mux Decoder Diagnosti...
Definition: LTC681x.cpp:967
ax::pec_match
uint8_t pec_match[4]
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:127
LTC681x_rdstat
int8_t LTC681x_rdstat(uint8_t reg, uint8_t total_ic, cell_asic *ic)
Reads and parses the LTC681x stat registers.
Definition: LTC681x.cpp:560
ic_register::tx_data
uint8_t tx_data[6]
Stores data to be transmitted.
Definition: LTC681x.h:143
CELL_CH_ALL
#define CELL_CH_ALL
CH Dec Channels to convert 0000 All Cells 0011 Cell 1 and Cell 7 0102 Cell 2 and Cell 8 0113 Cell 3 ...
Definition: LTC68041.h:142
cell_asic::aux
ax aux
Definition: LTC681x.h:175
st::stat_codes
uint16_t stat_codes[4]
Status codes.
Definition: LTC681x.h:133
LTC681x_wrpwm
void LTC681x_wrpwm(uint8_t total_ic, uint8_t pwmReg, cell_asic ic[])
Definition: LTC681x.cpp:1696
LTC681x_set_cfgr
void LTC681x_set_cfgr(uint8_t nIC, cell_asic *ic, bool refon, bool adcopt, bool gpio[5], bool dcc[12], bool dcto[4], uint16_t uv, uint16_t ov)
Helper function to set appropriate bits in CFGR register based on bit function.
Definition: LTC681x.cpp:2098
st::mux_fail
uint8_t mux_fail[1]
Mux self test status flag.
Definition: LTC681x.h:135
LTC681x_set_cfgr_uv
void LTC681x_set_cfgr_uv(uint8_t nIC, cell_asic *ic, uint16_t uv)
Helper function to set uv field in CFGRA register.
Definition: LTC681x.cpp:2168
LTC681x_run_adc_overlap
uint16_t LTC681x_run_adc_overlap(uint8_t total_ic, cell_asic *ic)
Helper Function that runs the ADC Overlap test.
Definition: LTC681x.cpp:1158
ic_register::rx_data
uint8_t rx_data[8]
Stores received data.
Definition: LTC681x.h:144
LTC681x_axow
void LTC681x_axow(uint8_t MD, uint8_t PUP)
Start GPIOs open wire ADC conversion.
Definition: LTC681x.cpp:1310
LTC681x_clrcell
void LTC681x_clrcell()
Clears the LTC681x Cell voltage registers The command clears the cell voltage registers and initializ...
Definition: LTC681x.cpp:937
AUX
#define AUX
Definition: LTC6810.h:63
LTC681x_statst
void LTC681x_statst(uint8_t MD, uint8_t ST)
Start a Status Register Self Test Conversion.
Definition: LTC681x.cpp:1006
LTC681x_rdcfgb
int8_t LTC681x_rdcfgb(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:307
LTC681x_adstat
void LTC681x_adstat(uint8_t MD, uint8_t CHST)
Start a Status ADC Conversion.
Definition: LTC681x.cpp:383
cell_asic
Cell variable structure.
Definition: LTC681x.h:170
wakeup_sleep
void wakeup_sleep(uint8_t total_ic)
Wake the LTC681x from the sleep state.
Definition: LTC681x.cpp:65
STAT_CH_ALL
#define STAT_CH_ALL
Definition: LTC681x.h:88
AUX_CH_ALL
#define AUX_CH_ALL
CHG Dec Channels to convert 000 0 All GPIOS and 2nd Ref 001 1 GPIO 1 010 2 GPIO 2 011 3 GPIO 3 100 4...
Definition: LTC68041.h:164
CELL
#define CELL
Definition: LTC6810.h:62
cell_asic::crc_count
pec_counter crc_count
Definition: LTC681x.h:184
LTC681x_clrsctrl
void LTC681x_clrsctrl()
Clears the LTC681x SCTRL registers The command clears the SCTRL registers and initializes all values ...
Definition: LTC681x.cpp:1900
cell_asic::ic_reg
register_cfg ic_reg
Definition: LTC681x.h:185
channel
static uint8_t channel
LTC2305 Channel selection.
Definition: DC1444A.ino:127
LTC681x_wrcfg
void LTC681x_wrcfg(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:204
LTC681x_set_cfgr_ov
void LTC681x_set_cfgr_ov(uint8_t nIC, cell_asic *ic, uint16_t ov)
Helper function to set ov field in CFGRA register.
Definition: LTC681x.cpp:2177
register_cfg::num_cv_reg
uint8_t num_cv_reg
Number of Cell voltage register.
Definition: LTC681x.h:164
cell_asic::com
ic_register com
Definition: LTC681x.h:177
register_cfg::num_gpio_reg
uint8_t num_gpio_reg
Number of Aux register.
Definition: LTC681x.h:165
LTC681x_wrsctrl
void LTC681x_wrsctrl(uint8_t total_ic, uint8_t sctrl_reg, cell_asic *ic)
Write the LTC681x Sctrl register.
Definition: LTC681x.cpp:1791
LTC681x_adax
void LTC681x_adax(uint8_t MD, uint8_t CHG)
Start a GPIO and Vref2 Conversion.
Definition: LTC681x.cpp:367
cs_low
void cs_low(uint8_t pin)
ltc681x hardware library
Definition: bms_hardware.cpp:50
LTC681x_set_cfgr_refon
void LTC681x_set_cfgr_refon(uint8_t nIC, cell_asic *ic, bool refon)
Helper function to turn the REFON bit HIGH or LOW.
Definition: LTC681x.cpp:2119
LTC681x_rdaux_reg
void LTC681x_rdaux_reg(uint8_t reg, uint8_t total_ic, uint8_t *data)
Read the raw data from the LTC681x auxiliary register The function reads a single GPIO voltage regist...
Definition: LTC681x.cpp:748
CFGR
#define CFGR
Definition: LTC681x.h:112
LTC681x_rdcv
uint8_t LTC681x_rdcv(uint8_t reg, uint8_t total_ic, cell_asic *ic)
Reads and parses the LTC681x cell voltage registers.
Definition: LTC681x.cpp:436
PULL_DOWN_CURRENT
#define PULL_DOWN_CURRENT
Definition: LTC681x.h:106
LTC681x_run_gpio_openwire
void LTC681x_run_gpio_openwire(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1590
LTC681x_rdstat_reg
void LTC681x_rdstat_reg(uint8_t reg, uint8_t total_ic, uint8_t *data)
Read the raw data from the LTC681x stat register The function reads a single Status register and stor...
Definition: LTC681x.cpp:797
pec_counter::cell_pec
uint16_t cell_pec[6]
Cell voltage register data PEC error count.
Definition: LTC681x.h:153
LTC681x_cvst
void LTC681x_cvst(uint8_t MD, uint8_t ST)
Starts cell voltage self test conversion.
Definition: LTC681x.cpp:974
data
union LT_union_int32_4bytes data
Definition: DC2094A.ino:138
LTC681x_adcvsc
void LTC681x_adcvsc(uint8_t MD, uint8_t DCP)
Starts cell voltage and SOC conversion.
Definition: LTC681x.cpp:399
LTC681x_wrcomm
void LTC681x_wrcomm(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1907
LTC681x_stsctrl
void LTC681x_stsctrl()
Start Sctrl data communication This command will start the sctrl pulse communication over the spins...
Definition: LTC681x.cpp:1879
LTC681x_rdsctrl
int8_t LTC681x_rdsctrl(uint8_t total_ic, uint8_t sctrl_reg, cell_asic *ic)
Reads sctrl registers of a LTC681x daisy chain.
Definition: LTC681x.cpp:1827
LTC681x_reset_crc_count
void LTC681x_reset_crc_count(uint8_t total_ic, cell_asic *ic)
Helper Function that resets the PEC error counters.
Definition: LTC681x.cpp:2059
LTC681x_clear_discharge
void LTC681x_clear_discharge(uint8_t total_ic, cell_asic *ic)
Helper Function to clear DCC bits in the CFGR Registers.
Definition: LTC681x.cpp:1682
CS_PIN
#define CS_PIN
Definition: LTC681x.h:114
STAT
#define STAT
Definition: LTC6810.h:64
crc15Table
static const unsigned int crc15Table[256]
Definition: LTC68041.h:90
delay_u
void delay_u(uint16_t micro)
Definition: bms_hardware.cpp:60
LTC681x_rdaux
int8_t LTC681x_rdaux(uint8_t reg, uint8_t total_ic, cell_asic *ic)
Reads and parses the LTC681x auxiliary registers.
Definition: LTC681x.cpp:498
pec_counter::pec_count
uint16_t pec_count
Overall PEC error count.
Definition: LTC681x.h:151
register_cfg::stat_channels
uint8_t stat_channels
Number of Stat channels.
Definition: LTC681x.h:162
LTC681x_adstatd
void LTC681x_adstatd(uint8_t MD, uint8_t CHST)
Start a Status register redundancy test Conversion.
Definition: LTC681x.cpp:1054
LTC681x_clrstat
void LTC681x_clrstat()
Clears the LTC681x Stat registers The command clears the Stat registers and initializes all values to...
Definition: LTC681x.cpp:960
LTC681x_pollAdc
uint32_t LTC681x_pollAdc()
This function will block operation until the ADC has finished it&#39;s conversion.
Definition: LTC681x.cpp:899
cell_asic::pwm
ic_register pwm
Definition: LTC681x.h:178
LTC681x_axst
void LTC681x_axst(uint8_t MD, uint8_t ST)
Start an Auxiliary Register Self Test Conversion.
Definition: LTC681x.cpp:990
LTC681x_check_pec
void LTC681x_check_pec(uint8_t total_ic, uint8_t reg, cell_asic *ic)
Helper Function that counts overall PEC errors and register/IC PEC errors.
Definition: LTC681x.cpp:1999
LTC681x_pladc
uint8_t LTC681x_pladc()
Sends the poll ADC command.
Definition: LTC681x.cpp:878
read_68
int8_t read_68(uint8_t total_ic, uint8_t tx_cmd[2], uint8_t *rx_data)
Issues a command onto the daisy chain and reads back 6*total_ic data in the rx_data array...
Definition: LTC681x.cpp:140
register_cfg::cell_channels
uint8_t cell_channels
Number of Cell channels.
Definition: LTC681x.h:161
register_cfg::num_stat_reg
uint8_t num_stat_reg
Number of Status register.
Definition: LTC681x.h:166
PULL_UP_CURRENT
#define PULL_UP_CURRENT
Definition: LTC681x.h:105
st::thsd
uint8_t thsd[1]
Thermal shutdown status.
Definition: LTC681x.h:136
error
static int error
Definition: LT1054_voltage_mode_buck.ino:64
st::pec_match
uint8_t pec_match[2]
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:137
cv::pec_match
uint8_t pec_match[6]
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:120
LTC681x_set_cfgr_dcto
void LTC681x_set_cfgr_dcto(uint8_t nIC, cell_asic *ic, bool dcto[4])
Definition: LTC681x.cpp:2158
cmd_68
void cmd_68(uint8_t tx_cmd[2])
Sends a command to the BMS IC.
Definition: LTC681x.cpp:77
LTC681x_rdpwm
int8_t LTC681x_rdpwm(uint8_t total_ic, uint8_t pwmReg, cell_asic ic[])
Definition: LTC681x.cpp:1738
ic_register::rx_pec_match
uint8_t rx_pec_match
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:145
pec_counter::cfgr_pec
uint16_t cfgr_pec
Configuration register data PEC error count.
Definition: LTC681x.h:152
cell_asic::stat
st stat
Definition: LTC681x.h:176
cell_asic::config
ic_register config
Definition: LTC681x.h:172
LTC681x_rdcomm
int8_t LTC681x_rdcomm(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1936
MD_7KHZ_3KHZ
#define MD_7KHZ_3KHZ
Definition: LTC681x.h:63
NUM_RX_BYT
#define NUM_RX_BYT
Definition: LTC681x.h:108
cs_high
void cs_high(uint8_t pin)
Definition: bms_hardware.cpp:55
cv::c_codes
uint16_t c_codes[18]
Cell Voltage Codes.
Definition: LTC681x.h:119
LTC681x_init_cfg
void LTC681x_init_cfg(uint8_t total_ic, cell_asic *ic)
Helper Function to initialize the CFGR data structures.
Definition: LTC681x.cpp:2084
LTC681x_rdcfg
int8_t LTC681x_rdcfg(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:264
DCP_DISABLED
#define DCP_DISABLED
Definition: LTC68041.h:183
LTC681x_clraux
void LTC681x_clraux()
Clears the LTC681x Auxiliary registers The command clears the Auxiliary registers and initializes all...
Definition: LTC681x.cpp:948
LTC681x_adcv
void LTC681x_adcv(uint8_t MD, uint8_t DCP, uint8_t CH)
Starts cell voltage conversion Starts ADC conversions of the LTC681x Cpin inputs. ...
Definition: LTC681x.cpp:350
CFGRB
#define CFGRB
Definition: LTC681x.h:113
LTC681x_run_adc_redundancy_st
int16_t LTC681x_run_adc_redundancy_st(uint8_t adc_mode, uint8_t adc_reg, uint8_t total_ic, cell_asic *ic)
Helper function that runs the ADC Digital Redundancy commands and checks output for errors...
Definition: LTC681x.cpp:1188
LTC681x_set_cfgr_adcopt
void LTC681x_set_cfgr_adcopt(uint8_t nIC, cell_asic *ic, bool adcopt)
Helper function to turn the ADCOPT bit HIGH or LOW.
Definition: LTC681x.cpp:2126
i
static int i
Definition: DC2430A.ino:184
LTC681x_run_openwire_multi
void LTC681x_run_openwire_multi(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1416
cell_asic::system_open_wire
long system_open_wire
Definition: LTC681x.h:186
cell_asic::cells
cv cells
Definition: LTC681x.h:174
LTC681x_run_openwire_single
void LTC681x_run_openwire_single(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1326
MD_26HZ_2KHZ
#define MD_26HZ_2KHZ
Definition: LTC681x.h:64
LTC681x_set_cfgr_dis
void LTC681x_set_cfgr_dis(uint8_t nIC, cell_asic *ic, bool dcc[12])
Definition: LTC681x.cpp:2143
register_cfg::aux_channels
uint8_t aux_channels
Number of Aux channels.
Definition: LTC681x.h:163
LTC681x_adol
void LTC681x_adol(uint8_t MD, uint8_t DCP)
Starts cell voltage overlap conversion.
Definition: LTC681x.cpp:1022
LTC681x_run_cell_adc_st
int16_t LTC681x_run_cell_adc_st(uint8_t adc_reg, uint8_t total_ic, cell_asic *ic, uint8_t md, bool adcopt)
Helper function that runs the ADC Self Tests.
Definition: LTC681x.cpp:1070
cell_codes
static uint16_t cell_codes[TOTAL_IC][12]
The cell codes will be stored in the cell_codes[][12] array in the following format: ...
Definition: DC1651A.ino:108
wakeup_idle
void wakeup_idle(uint8_t total_ic)
Wake isoSPI up from IDlE state and enters the READY state.
Definition: LTC681x.cpp:54
parse_cells
int8_t parse_cells(uint8_t current_ic, uint8_t cell_reg, uint8_t cell_data[], uint16_t *cell_codes, uint8_t *ic_pec)
Helper function that parses voltage measurement registers.
Definition: LTC681x.cpp:833
cell_asic::isospi_reverse
bool isospi_reverse
Definition: LTC681x.h:183
LTC681x_st_lookup
uint16_t LTC681x_st_lookup(uint8_t MD, uint8_t ST, bool adcopt)
Self Test Helper Function.
Definition: LTC681x.cpp:1245
LTC681x_wrcfgb
void LTC681x_wrcfgb(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:234
pec15_calc
uint16_t pec15_calc(uint8_t len, uint8_t *data)
Definition: LTC681x.cpp:183
pec_counter::aux_pec
uint16_t aux_pec[4]
Aux register data PEC error count.
Definition: LTC681x.h:154
write_68
void write_68(uint8_t total_ic, uint8_t tx_cmd[2], uint8_t data[])
Writes an array of data to the daisy chain.
Definition: LTC681x.cpp:98
st::flags
uint8_t flags[3]
Byte array that contains the uv/ov flag data.
Definition: LTC681x.h:134