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 */
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 */
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 */
961 {
962  uint8_t cmd[2]= {0x07 , 0x13};
963  cmd_68(cmd);
964 }
965 
966 /* Starts the Mux Decoder diagnostic self test */
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);
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);
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);
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);
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);
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);
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);
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 */
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 */
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 }
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
void LTC681x_adaxd(uint8_t MD, uint8_t CHG)
Start an GPIO Redundancy test.
Definition: LTC681x.cpp:1038
ic_register sctrl
Definition: LTC681x.h:180
void LTC681x_adcvax(uint8_t MD, uint8_t DCP)
Starts cell voltage and GPIO 1&2 conversion.
Definition: LTC681x.cpp:415
uint16_t stat_pec[2]
Status register data PEC error count.
Definition: LTC681x.h:155
General BMS Library.
void spi_write_array(uint8_t len, uint8_t data[])
Definition: LTC68031.cpp:349
uint16_t a_codes[9]
Aux Voltage Codes.
Definition: LTC681x.h:126
ic_register configb
Definition: LTC681x.h:173
void LTC681x_stcomm(uint8_t len)
Issues a stcomm command and clocks data out of the COMM register.
Definition: LTC681x.cpp:1978
void LTC681x_adow(uint8_t MD, uint8_t PUP, uint8_t CH, uint8_t DCP)
Start an open wire Conversion.
Definition: LTC681x.cpp:1292
void LTC681x_set_cfgr_gpio(uint8_t nIC, cell_asic *ic, bool gpio[5])
Definition: LTC681x.cpp:2133
uint8_t spi_read_byte(uint8_t tx_dat)
void spi_write_read(uint8_t tx_Data[], uint8_t tx_len, uint8_t *rx_data, uint8_t rx_len)
Definition: LTC68031.cpp:361
void LTC681x_diagn()
Starts the Mux Decoder diagnostic self test Running this command will start the Mux Decoder Diagnosti...
Definition: LTC681x.cpp:967
uint8_t pec_match[4]
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:127
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
uint8_t tx_data[6]
Stores data to be transmitted.
Definition: LTC681x.h:143
#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
ax aux
Definition: LTC681x.h:175
uint16_t stat_codes[4]
Status codes.
Definition: LTC681x.h:133
void LTC681x_wrpwm(uint8_t total_ic, uint8_t pwmReg, cell_asic ic[])
Definition: LTC681x.cpp:1696
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
uint8_t mux_fail[1]
Mux self test status flag.
Definition: LTC681x.h:135
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
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
uint8_t rx_data[8]
Stores received data.
Definition: LTC681x.h:144
void LTC681x_axow(uint8_t MD, uint8_t PUP)
Start GPIOs open wire ADC conversion.
Definition: LTC681x.cpp:1310
void LTC681x_clrcell()
Clears the LTC681x Cell voltage registers The command clears the cell voltage registers and initializ...
Definition: LTC681x.cpp:937
#define AUX
Definition: LTC6810.h:63
void LTC681x_statst(uint8_t MD, uint8_t ST)
Start a Status Register Self Test Conversion.
Definition: LTC681x.cpp:1006
int8_t LTC681x_rdcfgb(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:307
void LTC681x_adstat(uint8_t MD, uint8_t CHST)
Start a Status ADC Conversion.
Definition: LTC681x.cpp:383
Cell variable structure.
Definition: LTC681x.h:170
void wakeup_sleep(uint8_t total_ic)
Wake the LTC681x from the sleep state.
Definition: LTC681x.cpp:65
#define STAT_CH_ALL
Definition: LTC681x.h:88
#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
#define CELL
Definition: LTC6810.h:62
pec_counter crc_count
Definition: LTC681x.h:184
void LTC681x_clrsctrl()
Clears the LTC681x SCTRL registers The command clears the SCTRL registers and initializes all values ...
Definition: LTC681x.cpp:1900
register_cfg ic_reg
Definition: LTC681x.h:185
static uint8_t channel
LTC2305 Channel selection.
Definition: DC1444A.ino:127
void LTC681x_wrcfg(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:204
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
uint8_t num_cv_reg
Number of Cell voltage register.
Definition: LTC681x.h:164
ic_register com
Definition: LTC681x.h:177
uint8_t num_gpio_reg
Number of Aux register.
Definition: LTC681x.h:165
void LTC681x_wrsctrl(uint8_t total_ic, uint8_t sctrl_reg, cell_asic *ic)
Write the LTC681x Sctrl register.
Definition: LTC681x.cpp:1791
void LTC681x_adax(uint8_t MD, uint8_t CHG)
Start a GPIO and Vref2 Conversion.
Definition: LTC681x.cpp:367
void cs_low(uint8_t pin)
ltc681x hardware library
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
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
#define CFGR
Definition: LTC681x.h:112
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
#define PULL_DOWN_CURRENT
Definition: LTC681x.h:106
void LTC681x_run_gpio_openwire(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1590
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
uint16_t cell_pec[6]
Cell voltage register data PEC error count.
Definition: LTC681x.h:153
void LTC681x_cvst(uint8_t MD, uint8_t ST)
Starts cell voltage self test conversion.
Definition: LTC681x.cpp:974
union LT_union_int32_4bytes data
Definition: DC2094A.ino:138
void LTC681x_adcvsc(uint8_t MD, uint8_t DCP)
Starts cell voltage and SOC conversion.
Definition: LTC681x.cpp:399
void LTC681x_wrcomm(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1907
void LTC681x_stsctrl()
Start Sctrl data communication This command will start the sctrl pulse communication over the spins...
Definition: LTC681x.cpp:1879
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
void LTC681x_reset_crc_count(uint8_t total_ic, cell_asic *ic)
Helper Function that resets the PEC error counters.
Definition: LTC681x.cpp:2059
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
#define CS_PIN
Definition: LTC681x.h:114
#define STAT
Definition: LTC6810.h:64
static const unsigned int crc15Table[256]
Definition: LTC68041.h:90
void delay_u(uint16_t micro)
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
uint16_t pec_count
Overall PEC error count.
Definition: LTC681x.h:151
uint8_t stat_channels
Number of Stat channels.
Definition: LTC681x.h:162
void LTC681x_adstatd(uint8_t MD, uint8_t CHST)
Start a Status register redundancy test Conversion.
Definition: LTC681x.cpp:1054
void LTC681x_clrstat()
Clears the LTC681x Stat registers The command clears the Stat registers and initializes all values to...
Definition: LTC681x.cpp:960
uint32_t LTC681x_pollAdc()
This function will block operation until the ADC has finished it&#39;s conversion.
Definition: LTC681x.cpp:899
ic_register pwm
Definition: LTC681x.h:178
void LTC681x_axst(uint8_t MD, uint8_t ST)
Start an Auxiliary Register Self Test Conversion.
Definition: LTC681x.cpp:990
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
uint8_t LTC681x_pladc()
Sends the poll ADC command.
Definition: LTC681x.cpp:878
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
uint8_t cell_channels
Number of Cell channels.
Definition: LTC681x.h:161
uint8_t num_stat_reg
Number of Status register.
Definition: LTC681x.h:166
#define PULL_UP_CURRENT
Definition: LTC681x.h:105
uint8_t thsd[1]
Thermal shutdown status.
Definition: LTC681x.h:136
static int error
uint8_t pec_match[2]
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:137
uint8_t pec_match[6]
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:120
void LTC681x_set_cfgr_dcto(uint8_t nIC, cell_asic *ic, bool dcto[4])
Definition: LTC681x.cpp:2158
void cmd_68(uint8_t tx_cmd[2])
Sends a command to the BMS IC.
Definition: LTC681x.cpp:77
int8_t LTC681x_rdpwm(uint8_t total_ic, uint8_t pwmReg, cell_asic ic[])
Definition: LTC681x.cpp:1738
uint8_t rx_pec_match
If a PEC error was detected during most recent read cmd.
Definition: LTC681x.h:145
uint16_t cfgr_pec
Configuration register data PEC error count.
Definition: LTC681x.h:152
st stat
Definition: LTC681x.h:176
ic_register config
Definition: LTC681x.h:172
int8_t LTC681x_rdcomm(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1936
#define MD_7KHZ_3KHZ
Definition: LTC681x.h:63
#define NUM_RX_BYT
Definition: LTC681x.h:108
void cs_high(uint8_t pin)
uint16_t c_codes[18]
Cell Voltage Codes.
Definition: LTC681x.h:119
void LTC681x_init_cfg(uint8_t total_ic, cell_asic *ic)
Helper Function to initialize the CFGR data structures.
Definition: LTC681x.cpp:2084
int8_t LTC681x_rdcfg(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:264
#define DCP_DISABLED
Definition: LTC68041.h:183
void LTC681x_clraux()
Clears the LTC681x Auxiliary registers The command clears the Auxiliary registers and initializes all...
Definition: LTC681x.cpp:948
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
#define CFGRB
Definition: LTC681x.h:113
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
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
static int i
Definition: DC2430A.ino:184
void LTC681x_run_openwire_multi(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1416
long system_open_wire
Definition: LTC681x.h:186
cv cells
Definition: LTC681x.h:174
void LTC681x_run_openwire_single(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:1326
#define MD_26HZ_2KHZ
Definition: LTC681x.h:64
void LTC681x_set_cfgr_dis(uint8_t nIC, cell_asic *ic, bool dcc[12])
Definition: LTC681x.cpp:2143
uint8_t aux_channels
Number of Aux channels.
Definition: LTC681x.h:163
void LTC681x_adol(uint8_t MD, uint8_t DCP)
Starts cell voltage overlap conversion.
Definition: LTC681x.cpp:1022
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
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
void wakeup_idle(uint8_t total_ic)
Wake isoSPI up from IDlE state and enters the READY state.
Definition: LTC681x.cpp:54
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
bool isospi_reverse
Definition: LTC681x.h:183
uint16_t LTC681x_st_lookup(uint8_t MD, uint8_t ST, bool adcopt)
Self Test Helper Function.
Definition: LTC681x.cpp:1245
void LTC681x_wrcfgb(uint8_t total_ic, cell_asic ic[])
Definition: LTC681x.cpp:234
uint16_t pec15_calc(uint8_t len, uint8_t *data)
Definition: LTC681x.cpp:183
uint16_t aux_pec[4]
Aux register data PEC error count.
Definition: LTC681x.h:154
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
uint8_t flags[3]
Byte array that contains the uv/ov flag data.
Definition: LTC681x.h:134