TLMoto
Diff: LTC68041.cpp
- Revision:
- 0:f6b9d13870f2
- Child:
- 1:c55c4c93681f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LTC68041.cpp Fri Sep 09 13:46:07 2016 +0000 @@ -0,0 +1,1066 @@ +/*! + LTC6804-1 Multicell Battery Monitor + +@verbatim + The LTC6804 is a 3rd generation multicell battery stack + monitor that measures up to 12 series connected battery + cells with a total measurement error of less than 1.2mV. The + cell measurement range of 0V to 5V makes the LTC6804 + suitable for most battery chemistries. All 12 cell voltages + can be captured in 290uS, and lower data acquisition rates + can be selected for high noise reduction. + + Using the LTC6804-1, multiple devices are connected in + a daisy-chain with one host processor connection for all + devices. +@endverbatim + +http://www.linear.com/product/LTC6804-1 + +http://www.linear.com/product/LTC6804-1#demoboards + +REVISION HISTORY +$Revision: 4437 $ +$Date: 2015-12-01 08:26:42 -0800 (Tue, 01 Dec 2015) $ + +Copyright (c) 2013, Linear Technology Corp.(LTC) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of Linear Technology Corp. + +The Linear Technology Linduino is not affiliated with the official Arduino team. +However, the Linduino is only possible because of the Arduino team's commitment +to the open-source community. Please, visit http://www.arduino.cc and +http://store.arduino.cc , and consider a purchase that will help fund their +ongoing work. + +Copyright 2013 Linear Technology Corp. (LTC) +***********************************************************/ +//! @defgroup LTC68041 LTC6804-1: Multicell Battery Monitor + +/*! @file + @ingroup LTC68041 + Library for LTC6804-1 Multicell Battery Monitor +*/ + +#include <stdint.h> +#include "LTC68041.h" +#include <SPI.h> +#include "mbed.h" + + + +void output_low(uint8_t pin){ + pin = 0; +} + +void output_high(uint8_t pin){ + pin = 1; +} + + +/*! + 6804 conversion command variables. +*/ +uint8_t ADCV[2]; //!< Cell Voltage conversion command. +uint8_t ADAX[2]; //!< GPIO conversion command. + +// SPI 1 + +SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK); +DigitalOut spi_cs(PB_6); +// SPI 2 +//SPI spi(PC_1, PC_2, PB_10); +//DigitalOut spi_cs(PB_12); + + + +/*! + \brief This function will initialize all 6804 variables and the SPI port. + + This function will initialize the Linduino to communicate with the LTC6804 with a 1MHz SPI clock. + The Function also intializes the ADCV and ADAX commands to convert all cell and GPIO voltages in + the Normal ADC mode. +*/ +void LTC6804_initialize() +{ + + spi.frequency(1000000); + spi.format(8,3); //8bits data; mode = CPHA = 1 and CPOL = 1. + + set_adc(MD_NORMAL,DCP_DISABLED,CELL_CH_ALL,AUX_CH_ALL); +} + +/*!******************************************************************************************************************* + \brief Maps global ADC control variables to the appropriate control bytes for each of the different ADC commands + +@param[in] uint8_t MD The adc conversion mode +@param[in] uint8_t DCP Controls if Discharge is permitted during cell conversions +@param[in] uint8_t CH Determines which cells are measured during an ADC conversion command +@param[in] uint8_t CHG Determines which GPIO channels are measured during Auxiliary conversion command + +Command Code: +------------- + +|command | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|-----------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|ADCV: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | MD[1] | MD[2] | 1 | 1 | DCP | 0 | CH[2] | CH[1] | CH[0] | +|ADAX: | 0 | 0 | 0 | 0 | 0 | 1 | 0 | MD[1] | MD[2] | 1 | 1 | DCP | 0 | CHG[2]| CHG[1]| CHG[0]| + ******************************************************************************************************************/ +void set_adc(uint8_t MD, //ADC Mode + uint8_t DCP, //Discharge Permit + uint8_t CH, //Cell Channels to be measured + uint8_t CHG //GPIO Channels to be measured + ) +{ + uint8_t md_bits; + + md_bits = (MD & 0x02) >> 1; + ADCV[0] = md_bits + 0x02; + md_bits = (MD & 0x01) << 7; + ADCV[1] = md_bits + 0x60 + (DCP<<4) + CH; + + md_bits = (MD & 0x02) >> 1; + ADAX[0] = md_bits + 0x04; + md_bits = (MD & 0x01) << 7; + ADAX[1] = md_bits + 0x60 + CHG ; + +} + + +/*!********************************************************************************************* + \brief Starts cell voltage conversion + + Starts ADC conversions of the LTC6804 Cpin inputs. + The type of ADC conversion executed can be changed by setting the associated global variables: + |Variable|Function | + |--------|----------------------------------------------| + | MD | Determines the filter corner of the ADC | + | CH | Determines which cell channels are converted | + | DCP | Determines if Discharge is Permitted | + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|-----------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|ADCV: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | MD[1] | MD[2] | 1 | 1 | DCP | 0 | CH[2] | CH[1] | CH[0] | +***********************************************************************************************/ +void LTC6804_adcv() +{ + + uint8_t cmd[4]; + uint16_t cmd_pec; + + //1 + cmd[0] = ADCV[0]; + cmd[1] = ADCV[1]; + + //2 + cmd_pec = pec15_calc(2, ADCV); + cmd[2] = (uint8_t)(cmd_pec >> 8); + cmd[3] = (uint8_t)(cmd_pec); + + //3 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake. This command can be removed. + + //4 + spi_cs=0; + spi_write_array(4,cmd); +spi_cs=1; + +} +/* + LTC6804_adcv Function sequence: + + 1. Load adcv command into cmd array + 2. Calculate adcv cmd PEC and load pec into cmd array + 3. wakeup isoSPI port, this step can be removed if isoSPI status is previously guaranteed + 4. send broadcast adcv command to LTC6804 daisy chain +*/ + + +/*!****************************************************************************************************** + \brief Start an GPIO Conversion + + Starts an ADC conversions of the LTC6804 GPIO inputs. + The type of ADC conversion executed can be changed by setting the associated global variables: + |Variable|Function | + |--------|----------------------------------------------| + | MD | Determines the filter corner of the ADC | + | CHG | Determines which GPIO channels are converted | + + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|-----------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|ADAX: | 0 | 0 | 0 | 0 | 0 | 1 | 0 | MD[1] | MD[2] | 1 | 1 | DCP | 0 | CHG[2]| CHG[1]| CHG[0]| +*********************************************************************************************************/ +void LTC6804_adax() +{ + uint8_t cmd[4]; + uint16_t cmd_pec; + + cmd[0] = ADAX[0]; + cmd[1] = ADAX[1]; + cmd_pec = pec15_calc(2, ADAX); + cmd[2] = (uint8_t)(cmd_pec >> 8); + cmd[3] = (uint8_t)(cmd_pec); + + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake. This command can be removed. + spi_cs=0; + spi_write_array(4,cmd); +spi_cs=1; + +} +/* + LTC6804_adax Function sequence: + + 1. Load adax command into cmd array + 2. Calculate adax cmd PEC and load pec into cmd array + 3. wakeup isoSPI port, this step can be removed if isoSPI status is previously guaranteed + 4. send broadcast adax command to LTC6804 daisy chain +*/ + + +/***********************************************//** + \brief Reads and parses the LTC6804 cell voltage registers. + + The function is used to read the cell codes of the LTC6804. + This function will send the requested read commands parse the data + and store the cell voltages in cell_codes variable. + + @param[in] uint8_t reg; This controls which cell voltage register is read back. + + 0: Read back all Cell registers + + 1: Read back cell group A + + 2: Read back cell group B + + 3: Read back cell group C + + 4: Read back cell group D + + @param[in] uint8_t total_ic; This is the number of ICs in the daisy chain(-1 only) + + @param[out] uint16_t cell_codes[]; An array of the parsed cell codes from lowest to highest. The cell codes will + be stored in the cell_codes[] array in the following format: + | cell_codes[0][0]| cell_codes[0][1] | cell_codes[0][2]| ..... | cell_codes[0][11]| cell_codes[1][0] | cell_codes[1][1]| ..... | + |------------------|------------------|------------------|--------------|-------------------|-------------------|-----------------|----------| + |IC1 Cell 1 |IC1 Cell 2 |IC1 Cell 3 | ..... | IC1 Cell 12 |IC2 Cell 1 |IC2 Cell 2 | ..... | + + @return int8_t, PEC Status. + + 0: No PEC error detected + + -1: PEC error detected, retry read + + + *************************************************/ +uint8_t LTC6804_rdcv(uint8_t reg, // Controls which cell voltage register is read back. + uint8_t total_ic, // the number of ICs in the system + uint16_t cell_codes[][12] // Array of the parsed cell codes + ) +{ + + const uint8_t NUM_RX_BYT = 8; + const uint8_t BYT_IN_REG = 6; + const uint8_t CELL_IN_REG = 3; + + uint8_t *cell_data; + uint8_t pec_error = 0; + uint16_t parsed_cell; + uint16_t received_pec; + uint16_t data_pec; + uint8_t data_counter=0; //data counter + cell_data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t)); + //1.a + if (reg == 0) + { + //a.i + for (uint8_t cell_reg = 1; cell_reg<5; cell_reg++) //executes once for each of the LTC6804 cell voltage registers + { + data_counter = 0; + LTC6804_rdcv_reg(cell_reg, total_ic,cell_data ); //Reads a single Cell voltage register + + for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) // executes for every LTC6804 in the daisy chain + { + // current_ic is used as the IC counter + + //a.ii + for (uint8_t current_cell = 0; current_cell<CELL_IN_REG; current_cell++) // This loop parses the read back data into cell voltages, it + { + // loops once for each of the 3 cell voltage codes in the register + + parsed_cell = cell_data[data_counter] + (cell_data[data_counter + 1] << 8);//Each cell code is received as two bytes and is combined to + // create the parsed cell voltage code + + cell_codes[current_ic][current_cell + ((cell_reg - 1) * CELL_IN_REG)] = parsed_cell; + data_counter = data_counter + 2; //Because cell voltage codes are two bytes the data counter + //must increment by two for each parsed cell code + } + //a.iii + 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 + //after the 6 cell voltage data bytes + data_pec = pec15_calc(BYT_IN_REG, &cell_data[current_ic * NUM_RX_BYT]); + if (received_pec != data_pec) + { + pec_error = -1; //The pec_error variable is simply set negative if any PEC errors + //are detected in the serial data + } + data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter + //must be incremented by 2 bytes to point to the next ICs cell voltage data + } + } + } +//1.b + else + { + //b.i + LTC6804_rdcv_reg(reg, total_ic,cell_data); + for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) // executes for every LTC6804 in the daisy chain + { + // current_ic is used as the IC counter + //b.ii + for (uint8_t current_cell = 0; current_cell < CELL_IN_REG; current_cell++) // This loop parses the read back data into cell voltages, it + { + // loops once for each of the 3 cell voltage codes in the register + + parsed_cell = cell_data[data_counter] + (cell_data[data_counter+1]<<8); //Each cell code is received as two bytes and is combined to + // create the parsed cell voltage code + + cell_codes[current_ic][current_cell + ((reg - 1) * CELL_IN_REG)] = 0x0000FFFF & parsed_cell; + data_counter= data_counter + 2; //Because cell voltage codes are two bytes the data counter + //must increment by two for each parsed cell code + } + //b.iii + 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 + //after the 6 cell voltage data bytes + data_pec = pec15_calc(BYT_IN_REG, &cell_data[current_ic * NUM_RX_BYT]); + if (received_pec != data_pec) + { + pec_error = -1; //The pec_error variable is simply set negative if any PEC errors + //are detected in the serial data + } + data_counter= data_counter + 2; //Because the transmitted PEC code is 2 bytes long the data_counter + //must be incremented by 2 bytes to point to the next ICs cell voltage data + } + } + +//2 + free(cell_data); + return(pec_error); +} +/* + LTC6804_rdcv Sequence + + 1. Switch Statement: + a. Reg = 0 + i. Read cell voltage registers A-D for every IC in the daisy chain + ii. Parse raw cell voltage data in cell_codes array + iii. Check the PEC of the data read back vs the calculated PEC for each read register command + b. Reg != 0 + i.Read single cell voltage register for all ICs in daisy chain + ii. Parse raw cell voltage data in cell_codes array + iii. Check the PEC of the data read back vs the calculated PEC for each read register command + 2. Return pec_error flag +*/ + + +/***********************************************//** + \brief Read the raw data from the LTC6804 cell voltage register + + The function reads a single cell voltage register and stores the read data + in the *data point as a byte array. This function is rarely used outside of + the LTC6804_rdcv() command. + + @param[in] uint8_t reg; This controls which cell voltage register is read back. + + 1: Read back cell group A + + 2: Read back cell group B + + 3: Read back cell group C + + 4: Read back cell group D + + @param[in] uint8_t total_ic; This is the number of ICs in the daisy chain(-1 only) + + @param[out] uint8_t *data; An array of the unparsed cell codes + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|-----------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|RDCVA: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +|RDCVB: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | +|RDCVC: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | +|RDCVD: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | + + *************************************************/ +void LTC6804_rdcv_reg(uint8_t reg, //Determines which cell voltage register is read back + uint8_t total_ic, //the number of ICs in the + uint8_t *data //An array of the unparsed cell codes + ) +{ + const uint8_t REG_LEN = 8; //number of bytes in each ICs register + 2 bytes for the PEC + uint8_t cmd[4]; + uint16_t cmd_pec; + + //1 + if (reg == 1) //1: RDCVA + { + cmd[1] = 0x04; + cmd[0] = 0x00; + } + else if (reg == 2) //2: RDCVB + { + cmd[1] = 0x06; + cmd[0] = 0x00; + } + else if (reg == 3) //3: RDCVC + { + cmd[1] = 0x08; + cmd[0] = 0x00; + } + else if (reg == 4) //4: RDCVD + { + cmd[1] = 0x0A; + cmd[0] = 0x00; + } + + //2 + cmd_pec = pec15_calc(2, cmd); + cmd[2] = (uint8_t)(cmd_pec >> 8); + cmd[3] = (uint8_t)(cmd_pec); + + //3 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake. This command can be removed. + + //4 + spi_cs=0; + spi_write_read(cmd,4,data,(REG_LEN*total_ic)); +spi_cs=1; + +} +/* + LTC6804_rdcv_reg Function Process: + 1. Determine Command and initialize command array + 2. Calculate Command PEC + 3. Wake up isoSPI, this step is optional + 4. Send Global Command to LTC6804 daisy chain +*/ + + +/***********************************************************************************//** + \brief Reads and parses the LTC6804 auxiliary registers. + + The function is used + to read the parsed GPIO codes of the LTC6804. This function will send the requested + read commands parse the data and store the gpio voltages in aux_codes variable + +@param[in] uint8_t reg; This controls which GPIO voltage register is read back. + + 0: Read back all auxiliary registers + + 1: Read back auxiliary group A + + 2: Read back auxiliary group B + + +@param[in] uint8_t total_ic; This is the number of ICs in the daisy chain(-1 only) + + + @param[out] uint16_t aux_codes[][6]; A two dimensional array of the gpio voltage codes. The GPIO codes will + be stored in the aux_codes[][6] array in the following format: + | aux_codes[0][0]| aux_codes[0][1] | aux_codes[0][2]| aux_codes[0][3]| aux_codes[0][4]| aux_codes[0][5]| aux_codes[1][0] |aux_codes[1][1]| ..... | + |-----------------|-----------------|-----------------|-----------------|-----------------|-----------------|-----------------|---------------|-----------| + |IC1 GPIO1 |IC1 GPIO2 |IC1 GPIO3 |IC1 GPIO4 |IC1 GPIO5 |IC1 Vref2 |IC2 GPIO1 |IC2 GPIO2 | ..... | + +@return int8_t, PEC Status + + 0: No PEC error detected + + -1: PEC error detected, retry read + *************************************************/ +int8_t LTC6804_rdaux(uint8_t reg, //Determines which GPIO voltage register is read back. + uint8_t total_ic,//the number of ICs in the system + uint16_t aux_codes[][6]//A two dimensional array of the gpio voltage codes. + ) +{ + + + const uint8_t NUM_RX_BYT = 8; + const uint8_t BYT_IN_REG = 6; + const uint8_t GPIO_IN_REG = 3; + + uint8_t *data; + uint8_t data_counter = 0; + int8_t pec_error = 0; + uint16_t parsed_aux; + uint16_t received_pec; + uint16_t data_pec; + data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t)); + //1.a + if (reg == 0) + { + //a.i + for (uint8_t gpio_reg = 1; gpio_reg<3; gpio_reg++) //executes once for each of the LTC6804 aux voltage registers + { + data_counter = 0; + LTC6804_rdaux_reg(gpio_reg, total_ic,data); //Reads the raw auxiliary register data into the data[] array + + for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) // executes for every LTC6804 in the daisy chain + { + // current_ic is used as the IC counter + + //a.ii + for (uint8_t current_gpio = 0; current_gpio< GPIO_IN_REG; current_gpio++) // This loop parses the read back data into GPIO voltages, it + { + // loops once for each of the 3 gpio voltage codes in the register + + parsed_aux = data[data_counter] + (data[data_counter+1]<<8); //Each gpio codes is received as two bytes and is combined to + // create the parsed gpio voltage code + + aux_codes[current_ic][current_gpio +((gpio_reg-1)*GPIO_IN_REG)] = parsed_aux; + data_counter=data_counter+2; //Because gpio voltage codes are two bytes the data counter + //must increment by two for each parsed gpio voltage code + + } + //a.iii + received_pec = (data[data_counter]<<8)+ data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th + //after the 6 gpio voltage data bytes + data_pec = pec15_calc(BYT_IN_REG, &data[current_ic*NUM_RX_BYT]); + if (received_pec != data_pec) + { + pec_error = -1; //The pec_error variable is simply set negative if any PEC errors + //are detected in the received serial data + } + + data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter + //must be incremented by 2 bytes to point to the next ICs gpio voltage data + } + + + } + + } + else + { + //b.i + LTC6804_rdaux_reg(reg, total_ic, data); + for (int current_ic = 0 ; current_ic < total_ic; current_ic++) // executes for every LTC6804 in the daisy chain + { + // current_ic is used as an IC counter + + //b.ii + for (int current_gpio = 0; current_gpio<GPIO_IN_REG; current_gpio++) // This loop parses the read back data. Loops + { + // once for each aux voltage in the register + + parsed_aux = (data[data_counter] + (data[data_counter+1]<<8)); //Each gpio codes is received as two bytes and is combined to + // create the parsed gpio voltage code + aux_codes[current_ic][current_gpio +((reg-1)*GPIO_IN_REG)] = parsed_aux; + data_counter=data_counter+2; //Because gpio voltage codes are two bytes the data counter + //must increment by two for each parsed gpio voltage code + } + //b.iii + received_pec = (data[data_counter]<<8) + data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th + //after the 6 gpio voltage data bytes + data_pec = pec15_calc(BYT_IN_REG, &data[current_ic*NUM_RX_BYT]); + if (received_pec != data_pec) + { + pec_error = -1; //The pec_error variable is simply set negative if any PEC errors + //are detected in the received serial data + } + + data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter + //must be incremented by 2 bytes to point to the next ICs gpio voltage data + } + } + free(data); + return (pec_error); +} +/* + LTC6804_rdaux Sequence + + 1. Switch Statement: + a. Reg = 0 + i. Read GPIO voltage registers A-D for every IC in the daisy chain + ii. Parse raw GPIO voltage data in cell_codes array + iii. Check the PEC of the data read back vs the calculated PEC for each read register command + b. Reg != 0 + i.Read single GPIO voltage register for all ICs in daisy chain + ii. Parse raw GPIO voltage data in cell_codes array + iii. Check the PEC of the data read back vs the calculated PEC for each read register command + 2. Return pec_error flag +*/ + + +/***********************************************//** + \brief Read the raw data from the LTC6804 auxiliary register + + The function reads a single GPIO voltage register and stores thre read data + in the *data point as a byte array. This function is rarely used outside of + the LTC6804_rdaux() command. + + @param[in] uint8_t reg; This controls which GPIO voltage register is read back. + + 1: Read back auxiliary group A + + 2: Read back auxiliary group B + + +@param[in] uint8_t total_ic; This is the number of ICs in the daisy chain + +@param[out] uint8_t *data; An array of the unparsed aux codes + + + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|---------------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|RDAUXA: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | +|RDAUXB: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | + + *************************************************/ +void LTC6804_rdaux_reg(uint8_t reg, //Determines which GPIO voltage register is read back + uint8_t total_ic, //The number of ICs in the system + uint8_t *data //Array of the unparsed auxiliary codes + ) +{ + const uint8_t REG_LEN = 8; // number of bytes in the register + 2 bytes for the PEC + uint8_t cmd[4]; + uint16_t cmd_pec; + + //1 + if (reg == 1) //Read back auxiliary group A + { + cmd[1] = 0x0C; + cmd[0] = 0x00; + } + else if (reg == 2) //Read back auxiliary group B + { + cmd[1] = 0x0e; + cmd[0] = 0x00; + } + else //Read back auxiliary group A + { + cmd[1] = 0x0C; + cmd[0] = 0x00; + } + //2 + cmd_pec = pec15_calc(2, cmd); + cmd[2] = (uint8_t)(cmd_pec >> 8); + cmd[3] = (uint8_t)(cmd_pec); + + //3 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake, this command can be removed. + //4 + spi_cs=0; + spi_write_read(cmd,4,data,(REG_LEN*total_ic)); +spi_cs=1; + +} +/* + LTC6804_rdaux_reg Function Process: + 1. Determine Command and initialize command array + 2. Calculate Command PEC + 3. Wake up isoSPI, this step is optional + 4. Send Global Command to LTC6804 daisy chain +*/ + +/********************************************************//** + \brief Clears the LTC6804 cell voltage registers + + The command clears the cell voltage registers and intiallizes + all values to 1. The register will read back hexadecimal 0xFF + after the command is sent. + + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|---------------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|CLRCELL: | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | +************************************************************/ +void LTC6804_clrcell() +{ + uint8_t cmd[4]; + uint16_t cmd_pec; + + //1 + cmd[0] = 0x07; + cmd[1] = 0x11; + + //2 + cmd_pec = pec15_calc(2, cmd); + cmd[2] = (uint8_t)(cmd_pec >> 8); + cmd[3] = (uint8_t)(cmd_pec ); + + //3 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake. This command can be removed. + + //4 + spi_cs=0; + spi_write_read(cmd,4,0,0); +spi_cs=1; +} +/* + LTC6804_clrcell Function sequence: + + 1. Load clrcell command into cmd array + 2. Calculate clrcell cmd PEC and load pec into cmd array + 3. wakeup isoSPI port, this step can be removed if isoSPI status is previously guaranteed + 4. send broadcast clrcell command to LTC6804 daisy chain +*/ + + +/***********************************************************//** + \brief Clears the LTC6804 Auxiliary registers + + The command clears the Auxiliary registers and intiallizes + all values to 1. The register will read back hexadecimal 0xFF + after the command is sent. + + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|---------------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|CLRAUX: | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 2 | 0 | +***************************************************************/ +void LTC6804_clraux() +{ + uint8_t cmd[4]; + uint16_t cmd_pec; + + //1 + cmd[0] = 0x07; + cmd[1] = 0x12; + + //2 + cmd_pec = pec15_calc(2, cmd); + cmd[2] = (uint8_t)(cmd_pec >> 8); + cmd[3] = (uint8_t)(cmd_pec); + + //3 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake.This command can be removed. + //4 + spi_cs=0; + spi_write_read(cmd,4,0,0); +spi_cs=1; +} +/* + LTC6804_clraux Function sequence: + + 1. Load clraux command into cmd array + 2. Calculate clraux cmd PEC and load pec into cmd array + 3. wakeup isoSPI port, this step can be removed if isoSPI status is previously guaranteed + 4. send broadcast clraux command to LTC6804 daisy chain +*/ + + +/*****************************************************//** + \brief Write the LTC6804 configuration register + + This command will write the configuration registers of the LTC6804-1s + connected in a daisy chain stack. The configuration is written in descending + order so the last device's configuration is written first. + + @param[in] uint8_t total_ic; The number of ICs being written to. + + @param[in] uint8_t config[][6] is a two dimensional array of the configuration data that will be written, the array should contain the 6 bytes for each + IC in the daisy chain. The lowest IC in the daisy chain should be the first 6 byte block in the array. The array should + have the following format: + | config[0][0]| config[0][1] | config[0][2]| config[0][3]| config[0][4]| config[0][5]| config[1][0] | config[1][1]| config[1][2]| ..... | + |--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|-----------| + |IC1 CFGR0 |IC1 CFGR1 |IC1 CFGR2 |IC1 CFGR3 |IC1 CFGR4 |IC1 CFGR5 |IC2 CFGR0 |IC2 CFGR1 | IC2 CFGR2 | ..... | + + The function will calculate the needed PEC codes for the write data + and then transmit data to the ICs on a daisy chain. + + +Command Code: +------------- +| | CMD[0] | CMD[1] | +|---------------|---------------------------------------------------------------|---------------------------------------------------------------| +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|---------------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|WRCFG: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | +********************************************************/ +void LTC6804_wrcfg(uint8_t total_ic, //The number of ICs being written to + uint8_t config[][6] //A two dimensional array of the configuration data that will be written + ) +{ + const uint8_t BYTES_IN_REG = 6; + const uint8_t CMD_LEN = 4+(8*total_ic); + uint8_t *cmd; + uint16_t cfg_pec; + uint8_t cmd_index; //command counter + + cmd = (uint8_t *)malloc(CMD_LEN*sizeof(uint8_t)); + + //1 + cmd[0] = 0x00; + cmd[1] = 0x01; + cmd[2] = 0x3d; + cmd[3] = 0x6e; + + //2 + cmd_index = 4; + for (uint8_t current_ic = total_ic; current_ic > 0; current_ic--) // executes for each LTC6804 in daisy chain, this loops starts with + { + // the last IC on the stack. The first configuration written is + // received by the last IC in the daisy chain + + for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++) // executes for each of the 6 bytes in the CFGR register + { + // current_byte is the byte counter + + cmd[cmd_index] = config[current_ic-1][current_byte]; //adding the config data to the array to be sent + cmd_index = cmd_index + 1; + } + //3 + cfg_pec = (uint16_t)pec15_calc(BYTES_IN_REG, &config[current_ic-1][0]); // calculating the PEC for each ICs configuration register data + cmd[cmd_index] = (uint8_t)(cfg_pec >> 8); + cmd[cmd_index + 1] = (uint8_t)cfg_pec; + cmd_index = cmd_index + 2; + } + + //4 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake.This command can be removed. + //5 + spi_cs=0; + spi_write_array(CMD_LEN, cmd); +spi_cs=1; + free(cmd); +} +/* + WRCFG Sequence: + + 1. Load cmd array with the write configuration command and PEC + 2. Load the cmd with LTC6804 configuration data + 3. Calculate the pec for the LTC6804 configuration data being transmitted + 4. wakeup isoSPI port, this step can be removed if isoSPI status is previously guaranteed + 5. Write configuration data to the LTC6804 daisy chain + +*/ + +/*!****************************************************** + \brief Reads configuration registers of a LTC6804 daisy chain + +@param[in] uint8_t total_ic: number of ICs in the daisy chain + +@param[out] uint8_t r_config[][8] is a two dimensional array that the function stores the read configuration data. The configuration data for each IC +is stored in blocks of 8 bytes with the configuration data of the lowest IC on the stack in the first 8 bytes +block of the array, the second IC in the second 8 byte etc. Below is an table illustrating the array organization: + +|r_config[0][0]|r_config[0][1]|r_config[0][2]|r_config[0][3]|r_config[0][4]|r_config[0][5]|r_config[0][6] |r_config[0][7] |r_config[1][0]|r_config[1][1]| ..... | +|--------------|--------------|--------------|--------------|--------------|--------------|----------------|---------------|--------------|--------------|-----------| +|IC1 CFGR0 |IC1 CFGR1 |IC1 CFGR2 |IC1 CFGR3 |IC1 CFGR4 |IC1 CFGR5 |IC1 PEC High |IC1 PEC Low |IC2 CFGR0 |IC2 CFGR1 | ..... | + + +@return int8_t, PEC Status. + + 0: Data read back has matching PEC + + -1: Data read back has incorrect PEC + + +Command Code: +------------- + +|CMD[0:1] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +|---------------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +|RDCFG: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | +********************************************************/ +int8_t LTC6804_rdcfg(uint8_t total_ic, //Number of ICs in the system + uint8_t r_config[][8] //A two dimensional array that the function stores the read configuration data. + ) +{ + const uint8_t BYTES_IN_REG = 8; + + uint8_t cmd[4]; + uint8_t *rx_data; + int8_t pec_error = 0; + uint16_t data_pec; + uint16_t received_pec; + + rx_data = (uint8_t *) malloc((8*total_ic)*sizeof(uint8_t)); + + //1 + cmd[0] = 0x00; + cmd[1] = 0x02; + cmd[2] = 0x2b; + cmd[3] = 0x0A; + + //2 + wakeup_idle (); //This will guarantee that the LTC6804 isoSPI port is awake. This command can be removed. + //3 + spi_cs=0; + spi_write_read(cmd, 4, rx_data, (BYTES_IN_REG*total_ic)); //Read the configuration data of all ICs on the daisy chain into +spi_cs=1; //rx_data[] array + + for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++) //executes for each LTC6804 in the daisy chain and packs the data + { + //into the r_config array as well as check the received Config data + //for any bit errors + //4.a + for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++) + { + r_config[current_ic][current_byte] = rx_data[current_byte + (current_ic*BYTES_IN_REG)]; + } + //4.b + received_pec = (r_config[current_ic][6]<<8) + r_config[current_ic][7]; + data_pec = pec15_calc(6, &r_config[current_ic][0]); + if (received_pec != data_pec) + { + pec_error = -1; + } + } + + free(rx_data); + //5 + return(pec_error); +} +/* + RDCFG Sequence: + + 1. Load cmd array with the write configuration command and PEC + 2. wakeup isoSPI port, this step can be removed if isoSPI status is previously guaranteed + 3. Send command and read back configuration data + 4. For each LTC6804 in the daisy chain + a. load configuration data into r_config array + b. calculate PEC of received data and compare against calculated PEC + 5. Return PEC Error + +*/ + +/*!**************************************************** + \brief Wake isoSPI up from idle state + Generic wakeup commannd to wake isoSPI up out of idle + *****************************************************/ +void wakeup_idle() +{ + spi_cs=0; + wait_us(2); //Guarantees the isoSPI will be in ready mode +spi_cs=1; +} + +/*!**************************************************** + \brief Wake the LTC6804 from the sleep state + + Generic wakeup commannd to wake the LTC6804 from sleep + *****************************************************/ +void wakeup_sleep() +{ + spi_cs=0; + spi_cs = 0; + wait_ms(1); // Guarantees the LTC6804 will be in standby + spi_cs=1; +} +/*!********************************************************** + \brief calaculates and returns the CRC15 + + @param[in] uint8_t len: the length of the data array being passed to the function + + @param[in] uint8_t data[] : the array of data that the PEC will be generated from + + + @returns The calculated pec15 as an unsigned int +***********************************************************/ +uint16_t pec15_calc(uint8_t len, //Number of bytes that will be used to calculate a PEC + uint8_t *data //Array of data that will be used to calculate a PEC + ) +{ + uint16_t remainder,addr; + + remainder = 16;//initialize the PEC + for (uint8_t i = 0; i<len; i++) // loops for each byte in data array + { + addr = ((remainder>>7)^data[i])&0xff;//calculate PEC table address + remainder = (remainder<<8)^crc15Table[addr]; + } + return(remainder*2);//The CRC15 has a 0 in the LSB so the remainder must be multiplied by 2 +} + + +/*! + \brief Writes an array of bytes out of the SPI port + + @param[in] uint8_t len length of the data array being written on the SPI port + @param[in] uint8_t data[] the data array to be written on the SPI port + +*/ +void spi_write_array(uint8_t len, // Option: Number of bytes to be written on the SPI port + uint8_t data[] //Array of bytes to be written on the SPI port + ) +{ + for (uint8_t i = 0; i < len; i++) + { + spi.write((int8_t)data[i]); + } +} + +/*! + \brief Writes and read a set number of bytes using the SPI port. + +@param[in] uint8_t tx_data[] array of data to be written on the SPI port +@param[in] uint8_t tx_len length of the tx_data array +@param[out] uint8_t rx_data array that read data will be written too. +@param[in] uint8_t rx_len number of bytes to be read from the SPI port. + +*/ + +void spi_write_read(uint8_t tx_Data[],//array of data to be written on SPI port + uint8_t tx_len, //length of the tx data arry + uint8_t *rx_data,//Input: array that will store the data read by the SPI port + uint8_t rx_len //Option: number of bytes to be read from the SPI port + ) +{ + for (uint8_t i = 0; i < tx_len; i++) + { + spi.write(tx_Data[i]); + + } + + for (uint8_t i = 0; i < rx_len; i++) + { + rx_data[i] = (uint8_t)spi.write(0xFF); + } + +} + + +