/*!
General BMS Library
@verbatim
@endverbatim
REVISION HISTORY
$Revision: 7139 $
$Date: 2017-4
Copyright (c) 2017, 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 2017 Linear Technology Corp. (LTC)
***********************************************************/
#include <stdint.h>
#include "LTC681x.h"
#include "bms_hardware.h"
#ifdef LINDUINO
#include <Arduino.h>
#endif
void wakeup_idle(uint8_t total_ic)
{
for (int i =0; i<total_ic; i++)
{
cs_low(CS_PIN);
//delayMicroseconds(2); //Guarantees the isoSPI will be in ready mode
spi_read_byte(0xff);
cs_high(CS_PIN);
}
}
//Generic wakeup commannd to wake the LTC6813 from sleep
void wakeup_sleep(uint8_t total_ic)
{
for (int i =0; i<total_ic; i++)
{
cs_low(CS_PIN);
delay_u(300); // Guarantees the LTC6813 will be in standby
cs_high(CS_PIN);
delay_u(10);
}
}
//Generic function to write 68xx commands. Function calculated PEC for tx_cmd data
void cmd_68(uint8_t tx_cmd[2])
{
uint8_t cmd[4];
uint16_t cmd_pec;
uint8_t md_bits;
cmd[0] = tx_cmd[0];
cmd[1] = tx_cmd[1];
cmd_pec = pec15_calc(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
cs_low(CS_PIN);
spi_write_array(4,cmd);
cs_high(CS_PIN);
}
//Generic function to write 68xx commands and write payload data. Function calculated PEC for tx_cmd data
void write_68(uint8_t total_ic , uint8_t tx_cmd[2], uint8_t data[])
{
const uint8_t BYTES_IN_REG = 6;
const uint8_t CMD_LEN = 4+(8*total_ic);
uint8_t *cmd;
uint16_t data_pec;
uint16_t cmd_pec;
uint8_t cmd_index;
cmd = (uint8_t *)malloc(CMD_LEN*sizeof(uint8_t));
cmd[0] = tx_cmd[0];
cmd[1] = tx_cmd[1];
cmd_pec = pec15_calc(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
cmd_index = 4;
for (uint8_t current_ic = total_ic; current_ic > 0; current_ic--) // executes for each LTC681x 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++)
{
cmd[cmd_index] = data[((current_ic-1)*6)+current_byte];
cmd_index = cmd_index + 1;
}
data_pec = (uint16_t)pec15_calc(BYTES_IN_REG, &data[(current_ic-1)*6]); // calculating the PEC for each Iss configuration register data
cmd[cmd_index] = (uint8_t)(data_pec >> 8);
cmd[cmd_index + 1] = (uint8_t)data_pec;
cmd_index = cmd_index + 2;
}
cs_low(CS_PIN);
spi_write_array(CMD_LEN, cmd);
cs_high(CS_PIN);
free(cmd);
}
//Generic function to write 68xx commands and read data. Function calculated PEC for tx_cmd data
int8_t read_68( uint8_t total_ic, uint8_t tx_cmd[2], uint8_t *rx_data)
{
const uint8_t BYTES_IN_REG = 8;
uint8_t cmd[4];
uint8_t data[256];
int8_t pec_error = 0;
uint16_t cmd_pec;
uint16_t data_pec;
uint16_t received_pec;
// data = (uint8_t *) malloc((8*total_ic)*sizeof(uint8_t)); // This is a problem because it can fail
cmd[0] = tx_cmd[0];
cmd[1] = tx_cmd[1];
cmd_pec = pec15_calc(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
cs_low(CS_PIN);
spi_write_read(cmd, 4, data, (BYTES_IN_REG*total_ic)); //Read the configuration data of all ICs on the daisy chain into
cs_high(CS_PIN); //rx_data[] array
for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++) //executes for each LTC681x in the daisy chain and packs the data
{
//into the r_comm array as well as check the received Config data
//for any bit errors
for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++)
{
rx_data[(current_ic*8)+current_byte] = data[current_byte + (current_ic*BYTES_IN_REG)];
}
received_pec = (rx_data[(current_ic*8)+6]<<8) + rx_data[(current_ic*8)+7];
data_pec = pec15_calc(6, &rx_data[current_ic*8]);
if (received_pec != data_pec)
{
pec_error = -1;
}
}
return(pec_error);
}
/*
Calculates and returns the CRC15
*/
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
#ifdef MBED
remainder = (remainder<<8)^crc15Table[addr];
#else
remainder = (remainder<<8)^pgm_read_word_near(crc15Table+addr);
#endif
}
return(remainder*2);//The CRC15 has a 0 in the LSB so the remainder must be multiplied by 2
}
//Starts cell voltage conversion
void LTC681x_adcv(
uint8_t MD, //ADC Mode
uint8_t DCP, //Discharge Permit
uint8_t CH //Cell Channels to be measured
)
{
uint8_t cmd[4];
uint8_t md_bits;
md_bits = (MD & 0x02) >> 1;
cmd[0] = md_bits + 0x02;
md_bits = (MD & 0x01) << 7;
cmd[1] = md_bits + 0x60 + (DCP<<4) + CH;
cmd_68(cmd);
}
//Starts cell voltage and SOC conversion
void LTC681x_adcvsc(
uint8_t MD, //ADC Mode
uint8_t DCP //Discharge Permit
)
{
uint8_t cmd[4];
uint8_t md_bits;
md_bits = (MD & 0x02) >> 1;
cmd[0] = md_bits | 0x04;
md_bits = (MD & 0x01) << 7;
cmd[1] = md_bits | 0x60 | (DCP<<4) | 0x07;
cmd_68(cmd);
}
// Starts cell voltage and GPIO 1&2 conversion
void LTC681x_adcvax(
uint8_t MD, //ADC Mode
uint8_t DCP //Discharge Permit
)
{
uint8_t cmd[4];
uint8_t md_bits;
md_bits = (MD & 0x02) >> 1;
cmd[0] = md_bits | 0x04;
md_bits = (MD & 0x01) << 7;
cmd[1] = md_bits | ((DCP&0x01)<<4) + 0x6F;
cmd_68(cmd);
}
//Starts cell voltage overlap conversion
void LTC681x_adol(
uint8_t MD, //ADC Mode
uint8_t DCP //Discharge Permit
)
{
uint8_t cmd[4];
uint8_t md_bits;
md_bits = (MD & 0x02) >> 1;
cmd[0] = md_bits + 0x02;