Battery Management System (LTC6802 & Arduino)

Standard

For a while now I’ve been looking into battery management systems as part of the Solar Car club at the university. I started designing a BMS a while back but I ran into problems and I paused the project for a while. Now I’m back at it and I finally have it working. The battery management system (BMS) consists of an LTC6802-2 (http://cds.linear.com/docs/en/datasheet/68022fa.pdf) battery stack monitoring chip. This chip can monitor as well as balance a lithium battery pack consisting of no more than 12 cells. However, the chip registers need to be programmed via a microcontroller. The communication is done via SPI. A digital isolator such as the Si8441AB (https://www.silabs.com/Support%20Documents/TechnicalDocs/si844x.pdf) between the monitoring chip and the microcontroller can also be included for extra protection. Once set-up, the LTC6802-2 chip sends the voltage data to the microcontroller via the SPI interface. The diagram below shows the typical setup for a single monitoring chip connected to a microcontroller. In this case we use the Arduino Uno as the microcontroller. *Visit the Arduino website for more on the SPI interface.
ltc6802

The setup looks like this. It will be much nicer if all the components were placed on a PCB but this works for the initial design and testing. bms1 bms2 bms3
(*Note: the LTC 6802-2 works with minimum 4 batteries to achieve complete power ON) Now, the next step is to write the Arduino code. First we need to setup the registers with the values of the Over and Under voltage conditions. *Many other options of configuration are available. Please consult the LTC6802-2 datasheet. We start by giving the address of the LTC6802 chip. Since we’re using only one in our configuration the address is 0 or 0x80, defined by the address pins on the chip (A0,A1,A2,A3). Next we start the WRITE command after which the value of each configuration register is written.

digitalWrite(10, LOW); //CS LOW
SPI.transfer(0x80);
SPI.transfer(0x01);//Write Config
SPI.transfer(0x01);//Reg0
SPI.transfer(0x00);//Reg1
SPI.transfer(0x00);//Reg2
SPI.transfer(0x00);//Reg3
SPI.transfer(0x71);//Reg4 - UV 2.7V
SPI.transfer(0xAB);//Reg5 - OV 4.1V
digitalWrite(10, HIGH); //CS HIGH

We can also read the registers to check if they are properly set.

byte byteTemp;
digitalWrite(10, LOW);
SPI.transfer(0x80);
SPI.transfer(0x02);//Read
for(int i = 0; i < 6; i++)
{
byteTemp = SPI.transfer(0x02);
Serial.println(byteTemp, HEX);
}
digitalWrite(10, HIGH);

The complete code to read the voltage values of 4 cells is:

#include "SPI.h"
// SDO - PIN 12
// SDI - PIN 11
#define WRCFG 0x01 //Write Configuration Registers
#define RDCFG 0x02 // Read config
#define RDCV 0x04 // Read cells
#define STCVAD 0x10 // Start all A/D's - poll status
#define RDFLG 0x06 //Read Flags
#define RDTMP 0x08 //Read Temperatures
#define STCDC 0x60 //A/D converter and poll Status
#define STOWAD 0x20 //Start Test - poll status
#define STTMPAD 0x30// Temperature Reading - ALL
#define address 0x80
//Functions
byte byteTemp;
void setup()
{
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
pinMode(12,INPUT);
pinMode(13,OUTPUT);
digitalWrite(10, HIGH);
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE3);
SPI.setClockDivider(SPI_CLOCK_DIV16);
SPI.begin();
Serial.begin(9600);
writeReg();
}
void loop()
{
readV();
delay(2000);
}
void writeReg()
{
Serial.println("Writing config...");
digitalWrite(10, LOW);
SPI.transfer(address);
SPI.transfer(WRCFG);
SPI.transfer(0x01);//0
SPI.transfer(0x00);//1
SPI.transfer(0x00);//2
SPI.transfer(0x00);//3
SPI.transfer(0x71);//4
SPI.transfer(0xAB);//5
digitalWrite(10, HIGH);
}
void readReg()
{
Serial.println("Reading config...");
digitalWrite(10, LOW);
SPI.transfer(address);
SPI.transfer(RDCFG);
for(int i = 0; i < 6; i++)
{
byteTemp = SPI.transfer(RDCFG);
Serial.println(byteTemp, HEX);
}
digitalWrite(10, HIGH);
}
void readV()
{
digitalWrite(10,LOW);
SPI.transfer(STCVAD);
delay(20); // wait at least 12ms as per data sheet, p.24
digitalWrite(10,HIGH);
byte volt[18];
digitalWrite(10,LOW);
SPI.transfer(0x80);
SPI.transfer(RDCV);
for(int j = 0; j<18;j++)
{
volt[j] = SPI.transfer(RDCV);
}
digitalWrite(10,HIGH);
Serial.println(((volt[0] & 0xFF) | (volt[1] & 0x0F) << 8)*1.5*0.001);
Serial.println(((volt[1] & 0xF0) >> 4 | (volt[2] & 0xFF) << 4)*1.5*0.001);
Serial.println(((volt[3] & 0xFF) | (volt[4] & 0x0F) << 8)*1.5*0.001);
Serial.println(((volt[4] & 0xF0) >> 4 | (volt[5] & 0xFF) << 4)*1.5*0.001);
Serial.println("--------------------");
}

The last part of the code needs some explaining. If we look at page 21 of the datasheet, the voltage value is spread over 12bits but the values of the voltage registers read with the RDCV command are 8 bit. Thus, a single voltage value is stored over two registers. Therefore, the last part of the code takes the 8 bits and adds the other 4 from the next or previous register(see p.21 for details). The 0xFF value does “masking” of the voltage and eliminates all but the last 8 bits of voltage value. Then using the OR (“|”) operator we add the 8 bit value to the 4 bit to give us the completed 12 bit voltage. *Do a quick calculation with random 8 bit registers for more clarification. Another part of the code is the “1.5*0.001” value. This refers to 1.5mV. On p.23 of the datasheet we can see that the voltage value needs to be multiplied by 1.5mV. This has something to do with the ADC conversion algorithm (when the analog voltage is sampled and converted to a digital value). Simple enough, just multiply to get the correct number. Next, we run the code and get:
Test
Great Success! All 4 battery voltages are read correctly with an error of +/-0.05V. It might have something to do with the ADC conversion multiplier but good enough for now. This project is still in the early stages. Next I’ll implement an algorithm that will perform the balancing of battery cells and possibly another function that measures the temperature of the pack (via an external thermistor).

Advertisements

11 thoughts on “Battery Management System (LTC6802 & Arduino)

  1. mahmood

    very nice work, i am doing a project using the older version of that chip (ltc6802-1)

    do you have any idea how should i make anything different to make it work with this code? because it doesn’t work right now

    • From the datasheet I can see that the 6802-1 chip doesn’t have an address or address pins (A0,A1,A2,A3), so first try deleting the line SPI.transfer(address); and SPI.transfer(0x80); (it’s the same) from the code. On page 27 of the datasheet you can see the programming instructions for the chip. It also says that you might need to specify TOP,MIDDLE and BOTTOM devices (it’s something similar to the address bits in the 6802-2 chip) if you have more than 1 chip connected. I’m not sure if that’s the case if you’re using just a single chip.

      • mahmood

        great, thanks.

        but maybe i need some help to go through the code as i am not the expert. the first thing i did was removing all the address parts from the code and tried it but didn’t work. i referred to this bit of the datasheet where it says top middle and bottom devices but i didn’t understand how to implement it in the code. also what is mentioned on page 27 looks – to me at least- like what is implemented here for readV and writeV, or it’s not?

        i would really appreciate ur help as i am doing this for my senior project at the college.

      • One more thing that I noticed in the datasheet is that it says to connect the TOS pin to VREG when your chip is the TOP device. In my wiring diagram I have it connect to ground. TOP device would probably mean the first or main chip and connect TOS to ground for the other devices in the daisy chain. Try this to see if it works. Also, when you call writeReg() you set the under voltage and over voltage limits and other configuration parameters. On page 27 you see that as the first step, the next step is indeed reading the voltages. In my example that is the readV() function. At first try to just write the register commands as in writeReg() and read the registers using readReg(). If this works then you have established communication between the LTC6802 chip and the Arduino. Then the next step would be to test readV(). So first, comment-out readV() in the loop() function, type readReg(); instead. You should see some results printed by the serial interface. See if those match the written registers in writeReg(). Hope that helps.

    • It would depend on the number of cells you have. The datasheet says MAX 60V but that’s if you have 12 cells and that might be extreme. But remember, this chip only balances the voltage of each cell in the pack, it’s not a charger! You would need another circuit to monitor the charging current (you can monitor the voltage using the LTC6802 chip) going into the battery pack. Lithium batteries typically use constant-current, constant-voltage charging scheme. I hope this helps.

  2. Do Cao

    I’ve made a module following your instruction, but something was wrong. When I use 8 batteries (each 1.5V) for my circuit to test, IC LTC6802-2 is graduately warm up. Ive check all the short circuits case, but everything probably fine. It’s normal or not? Please help me. Thank in advanced.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s