Issues with Reading and Writing Configuration Registers for LTC6803-3

Hey guys,

I’m just recently started working with Arduino, am new to programming circuit boards, and am trying to interface an Arduino Uno board with six daisy-chained LTC6803-3 chips which are connected to the Arduino via an SPI Isolator. Right now, I can somewhat communicate with the daisy-chain, but the configuration register values that I’m reading out are not the ones that I’ve written to the board.

My code:

#include <SPI.h>

byte config[6] = {0xE7,0x00,0x00,0x00,0xAB,0x71};

byte WRCFG = 0x01;
byte RDCFG = 0x02;

const int CS0 = 4;
const int CS1 = 5;
const int CS2 = 6;
const int DECODER_EN = 7;

//Function to calc PEC from http://calsol.googlecode.com/svn-history/r238/trunk/BMS/bps.h
byte getPEC(byte * din, int n) {
  byte pec, in0, in1, in2;
  pec = 0x41;
  for(int j=0; j<n; j++) {
    for(int i=0; i<8; i++) {
      in0 = ((din[j] >> (7 - i)) & 0x01) ^ ((pec >> 7) & 0x01);
      in1 = in0 ^ ((pec >> 0) & 0x01);
      in2 = in0 ^ ((pec >> 1) & 0x01);
      pec = in0 | (in1 << 1) | (in2 << 2) | ((pec << 1) & ~0x07);
    }
  }
  return pec;
}

void setup()
{
  pinMode(CS0,OUTPUT);
  pinMode(CS1,OUTPUT);
  pinMode(CS2,OUTPUT);
  pinMode(DECODER_EN,OUTPUT);
  
  Serial.begin(9600);
  SPI.begin();
  //SPI.setClockDivider(SPI_CLOCK_DIV64);
  SPI.setDataMode(SPI_MODE3);
  SPI.setBitOrder(MSBFIRST);
  
  //set up CS
  digitalWrite(DECODER_EN,LOW);
  digitalWrite(CS0,LOW);
  digitalWrite(CS1,HIGH);
  digitalWrite(CS2,LOW);
  
  //send WRCFG
  digitalWrite(DECODER_EN,HIGH);
  
  SPI.transfer(WRCFG);
  SPI.transfer(getPEC(&WRCFG,1));
  
  for(int i=1; i<6; i++){
    SPI.transfer(config[i]);
  }
  SPI.transfer(getPEC(config,6));
  digitalWrite(DECODER_EN,LOW);
  
}

void loop()
{
   //read registers
  digitalWrite(DECODER_EN,HIGH);
  SPI.transfer(RDCFG);
  SPI.transfer(getPEC(&RDCFG,1));

  for(int i=0; i<6; i++){
    config[i] = SPI.transfer(RDCFG);
    Serial.print(config[i],HEX);
    Serial.println();
  }
  byte pec = SPI.transfer(RDCFG);
  Serial.println(pec);
  digitalWrite(DECODER_EN,LOW);
  Serial.println();
  delay(1000);
  
}

The output that I’m currently getting:

E
0
0
0
0
0
0

As I’m a noob when it comes to working with circuit boards, I’m lost as to where to go from here. Any sort of nudge in the right direction is much appreciated. Thanks!

Why do you have the SPI clock divider line commented out? Default speed is 4MHz which is too fast for the chip according to the datasheet.

What kind of SPI isolator (link to the datasheet) are you using? Have you checked that it supports the chosen speed? How have you connected the Chips to the Arduino? What's CS0, CS1, CS2 and DECODER_EN?

I didn’t really understand the clock divider at the time, as this is my first circuit project. I now have the clock divider set to SPI_CLOCK_DIV64, though I’m still getting incorrect reads. Any clock divider settings below this result in a read which consists of all 0’s :confused:

The SPI Isolator I’m using is this one: http://www.analog.com/static/imported-files/data_sheets/ADUM3400_3401_3402.pdf. I assumed that it does indeed support the speed needed for the LTCs, as I did not design the board, and thankfully the two speeds match up.

CS0, CS1, CS2, and DECODER_EN are pins needed to use the chip select decoder on the board, as there are five other ADCs to read on this board. The decoder works fine, as I have tested it individually and have also been able to read from the other ADCs on the board.

Also, I’m not seeing any activity on the MISO channel when I’m expecting to see at least something. I think it has to do with the PEC of the read not matching with the PEC of the write (which they don’t), but I have no idea how to remedy this problem.

My revised code:

#include <SPI.h>

byte config[6] = {0x61,0x00,0x00,0x00,0x00,0x00};

byte WRCFG = 0x01;
byte RDCFG = 0x02;

byte WRPEC = 0xC7;
byte RDPEC = 0xCE;

const int CS0 = 4;
const int CS1 = 5;
const int CS2 = 6;
const int DECODER_EN = 7;
const int SCKPIN = 13;
const int V1_EN = 10;

//Function to calc PEC from http://calsol.googlecode.com/svn-history/r238/trunk/BMS/bps.h
byte getPEC(byte * din, int n) {
  byte pec, in0, in1, in2;
  pec = 0x41;
  for(int j=0; j<n; j++) {
    for(int i=0; i<8; i++) {
      in0 = ((din[j] >> (7 - i)) & 0x01) ^ ((pec >> 7) & 0x01);
      in1 = in0 ^ ((pec >> 0) & 0x01);
      in2 = in0 ^ ((pec >> 1) & 0x01);
      pec = in0 | (in1 << 1) | (in2 << 2) | ((pec << 1) & ~0x07);
    }
  }
  return pec;
}

void setup()
{
  pinMode(CS0,OUTPUT);
  pinMode(CS1,OUTPUT);
  pinMode(CS2,OUTPUT);
  pinMode(DECODER_EN,OUTPUT);
  pinMode(SCKPIN,OUTPUT);
  pinMode(V1_EN,OUTPUT);
  
    //set up CS
  digitalWrite(DECODER_EN,LOW);
  digitalWrite(CS0,HIGH);
  digitalWrite(CS1,HIGH);
  digitalWrite(CS2,HIGH);
  digitalWrite(SCKPIN,HIGH);
  digitalWrite(V1_EN,HIGH);
  
  
  Serial.begin(9600);
  SPI.begin();
  //SPI.setClockDivider(SPI_CLOCK_DIV64);
  SPI.setDataMode(SPI_MODE3);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV64);
  
  digitalWrite(DECODER_EN,HIGH);
  digitalWrite(CS1,LOW);
    //set config
  SPI.transfer(WRCFG);
  //delay(10);
  SPI.transfer(WRPEC);
  
  delay(1000);
  
  for(int i=0; i<6; i++){
    SPI.transfer(config[i]);
    Serial.println(config[i],HEX);
    delay(10);
  }
  SPI.transfer(getPEC(config,6));
  Serial.println(getPEC(config,6),HEX);
  delay(10);
  digitalWrite(CS1,HIGH);
  Serial.println("");
  delay(100);
    
}

void loop()
{
    //read registers
  digitalWrite(CS1,LOW);
  SPI.transfer(RDCFG);
  delay(10);
  SPI.transfer(RDPEC);
  delay(10);
  
  byte rconfig[6]; 
  for(int i=0; i<6; i++){
    rconfig[i] = SPI.transfer(RDCFG);
    delay(10);
    Serial.print(rconfig[i],HEX);
    Serial.println();
  }
  byte pec = SPI.transfer(RDCFG);
  delay(10);
  Serial.println(pec,HEX);
  Serial.println();
  delay(10);
  digitalWrite(CS1,HIGH);
  delay(1000);
}

Also, what I’m reading is all 0’s for the config array and the PEC reads as 10 most of the time, but will sporadically read as 12 as well.

I didn’t really understand the clock divider at the time, as this is my first circuit project. I now have the clock divider set to SPI_CLOCK_DIV64, though I’m still getting incorrect reads. Any clock divider settings below this result in a read which consists of all 0’s :confused:

The clock divider is responsible for the speed of the SPI interface. Default is SPI_CLOCK_DIV4 which means that the 16MHz clock of the Arduino is divided by 4 resulting in a speed of 4Mb/s for the SPI interface.

You chip has a max. data rate of 1Mb/s so I would use the next rate below it, which results in SPI_CLOCK_DIV32.

The SPI Isolator I’m using is this one: http://www.analog.com/static/imported-files/data_sheets/ADUM3400_3401_3402.pdf.

This datasheet is for several type. A ADuM340xARW is not fast enough for your project. Which one do you have exactly?

CS0, CS1, CS2, and DECODER_EN are pins needed to use the chip select decoder on the board, as there are five other ADCs to read on this board. The decoder works fine, as I have tested it individually and have also been able to read from the other ADCs on the board.

Do you have schematics of that board? You are only using CS1 in your sketch so I’m curious what the other may be for.

BTW: the datasheet for the LTC is available here: http://cds.linear.com/docs/en/datasheet/680313fa.pdf

I would interpret figure 7 of the datasheet so that the device is using mode 0 and not 3.

Try this code:

  for(int i=0; i<6; i++){
    rconfig[i] = SPI.transfer(RDCFG);
    Serial.print(rconfig[i],HEX);
    Serial.println();
  }

I removed all irritating delay()s (waiting 10ms, which is an eternity when communicating with such high speeds, is not a good idea) and moved all serial code (which also may disturb the SPI as it’s quite slow but at least done by the hardware just using some interrupt calls) out of the SPI code.

Please post the output of the first 3-4 seconds of this code.

My code output is as follows:

0
0
0
0
0
0
0

0
0
0
0
0
0
0

0
0
0
0
0
0
0

The SPI Isolator I have exactly is the ADUM3401.

I sadly cannot post the schematics for the board.

What I have is a 3 to 8 decoder, because there are multiple other chips I need to talk to, and the CS0, CS1, and CS2 are all used to specify which chip I want to talk to. I’ve tried using DECODER_EN to simply switch CS high and low, but for some reason, disabling the decoder doesn’t toggle chip select. Instead, I hop between the chip I want and a floating pin using CS1, as setting it high switches to the floating chip and setting it low switches to the daisy-chained LTCs. I’ve run multiple tests on the decoder and that’s definitely not the source of the problem.

I finally got a hold of a scope today and found out that the writing section of the code works beautifully, but once it hits the reading section of the code, MISO drops low and remains low, even though I send an SPI.transfer command to get the data.

I’ve had some help here with the code and have an updated version, but I’m still having issues with the reading section (all 0’s).

#include <SPI.h>

//Commands for LTC
byte STCVAD = 0x10;
byte WRCFG = 0x01;
byte RDCFG = 0x02;
byte RDCV = 0x04;

//PEC for command
byte STCVAD_PEC = 0xB0;
byte WRCFG_PEC = 0xC7;
byte RDCFG_PEC = 0xCE;

//config register

//byte config[6] = {0x11,0x00,0xF0,0xFF,0x48,0x72};
byte CFGR0 = 0x11;
byte CFGR1 = 0x00;
byte CFGR2 = 0xF0;
byte CFGR3 = 0xFF;
byte CFGR4 = 0x48;
byte CFGR5 = 0x72;

//Pins
const int CS0 = 4;
const int CS1 = 5;
const int CS2 = 6;
const int DECODER_EN = 7;
const int MOSI_PIN = 11;
const int MISO_PIN = 12;

///////////////////////////////////////////
//////// FUNCTION DEFINITIONS /////////////
///////////////////////////////////////////

byte makePEC(byte dataIn, byte refPEC){
  for(int i = 8; i > 0; i--){
    byte tempPEC = refPEC << 1;
    if(bitRead(refPEC,7) ^ bitRead(dataIn,i-1))
      bitSet(tempPEC,0);
    else
      bitClear(tempPEC,0);
    if(bitRead(refPEC,0) ^ bitRead(tempPEC,0))
      bitSet(tempPEC,1);
    else
      bitClear(tempPEC,0);
    if(bitRead(refPEC,1) ^ bitRead(tempPEC,0))
      bitSet(tempPEC,2);
    else
      bitClear(tempPEC,2);
    refPEC = tempPEC;
  }
  return refPEC;
}

byte multiPEC(byte dataIn0, byte dataIn1, byte dataIn2, byte dataIn3, byte dataIn4, byte dataIn5){
  byte PEC = makePEC(dataIn0, B01000001);
  PEC = makePEC(dataIn1, PEC);
  PEC = makePEC(dataIn2, PEC);
  PEC = makePEC(dataIn3, PEC);
  PEC = makePEC(dataIn4, PEC);
  PEC = makePEC(dataIn5, PEC);
  return PEC;
}

void deselectAll(){
  digitalWrite(CS0,HIGH);
  digitalWrite(CS1,HIGH);
  digitalWrite(CS2,HIGH);
}

void selectLTC(){
  digitalWrite(CS0,HIGH);
  digitalWrite(CS1,LOW);
  digitalWrite(CS2,HIGH);
}

void spiRead(byte command, byte PEC, int numBytes, byte *data){
  Serial.println(command,HEX);
  Serial.println(PEC,HEX);
  SPI.transfer(command);
  SPI.transfer(PEC);
  for(int i = numBytes; i > 0; i--){
    Serial.println(SPI.transfer(0x00));
    Serial.println(data[i-1],HEX);
    delayMicroseconds(5);
  }
  //check for PEC
  Serial.println(SPI.transfer(0x00),HEX);
}

void spiWrite(byte command, byte PEC){
  SPI.transfer(command);
  SPI.transfer(PEC);
}

void spiPollWrite(byte command, byte PEC){
  SPI.transfer(command);
  SPI.transfer(PEC);
  while(!digitalRead(MISO)){
    selectLTC();
  }
}

void writeConfig(){
  byte CFGR0_PEC = (makePEC(CFGR0,B01000001));
  byte CFGR1_PEC = (makePEC(CFGR1,B01000001));
  byte CFGR2_PEC = (makePEC(CFGR2,B01000001));
  byte CFGR3_PEC = (makePEC(CFGR3,B01000001));
  byte CFGR4_PEC = (makePEC(CFGR4,B01000001));
  byte CFGR5_PEC = (makePEC(CFGR5,B01000001));
  byte cfgPEC = (multiPEC(CFGR0_PEC,CFGR1_PEC,CFGR2_PEC,CFGR3_PEC,CFGR4_PEC,CFGR5_PEC));
  
  SPI.transfer(WRCFG);
  SPI.transfer(WRCFG_PEC);
  
  SPI.transfer(CFGR0);
  SPI.transfer(CFGR1);
  SPI.transfer(CFGR2);
  SPI.transfer(CFGR3);
  SPI.transfer(CFGR4);
  SPI.transfer(CFGR5);
  SPI.transfer(cfgPEC);
//  Serial.println(WRCFG,HEX);
//  Serial.println(WRCFG_PEC,HEX);
//  Serial.println(CFGR0,HEX);
//  Serial.println(CFGR1,HEX);
//  Serial.println(CFGR2,HEX);
//  Serial.println(CFGR3,HEX);
//  Serial.println(CFGR4,HEX);
//  Serial.println(CFGR5,HEX);
//  Serial.println(cfgPEC,HEX);
//  delay(3000);
}

void setup()
{
  Serial.begin(9600);
  
  pinMode(CS0,OUTPUT);
  pinMode(CS1,OUTPUT);
  pinMode(CS2,OUTPUT);
  pinMode(DECODER_EN,OUTPUT);
  pinMode(10,OUTPUT);
  
  digitalWrite(10,HIGH);
  
  //set up CS
  digitalWrite(DECODER_EN,HIGH);
  digitalWrite(CS0,HIGH);
  digitalWrite(CS1,HIGH);
  digitalWrite(CS2,HIGH);
  
  //set up SPI
  pinMode(MISO_PIN,INPUT);
  pinMode(MOSI_PIN,OUTPUT);
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE3);
  SPI.setClockDivider(SPI_CLOCK_DIV32);
  SPI.begin();    
}

void loop()
{
  selectLTC();
  for(int i = 0 ; i < 6; i++){
    writeConfig();
    delay(2);
  }
  deselectAll();
  delay(2);
  
  byte config_reg[6][6];
  selectLTC();
  for(int i = 0; i < 6; i++){
    spiRead(RDCFG, makePEC(RDCFG,B01000001),6,config_reg[i]);
    delay(2);
  }
  deselectAll();
}

I have tried SPI_CLOCK_DIV32 through SPI_CLOCK_DIV128 with the same results, which is all 0’s read from the registers, an inactive MISO line during reads, and only one clock pulse with every SPI.transfer(0x00).

You still have delays in your code, remove them, you don't need them.

The SPI Isolator I have exactly is the ADUM3401

The speed depends on the characters after the number. What do you have there?

What I have is a 3 to 8 decoder, because there are multiple other chips I need to talk to, and the CS0, CS1, and CS2 are all used to specify which chip I want to talk to.

What type of decoder do you have? My guess is, you don't use it correctly. Am I right, that CS0 is not directly connected to an LTC6803-3? Did you try your software with a simpler setup before? It's quite unusual to try the software with such a complex setup for the first time because you have many possible factors that may influence the result.

void spiPollWrite(byte command, byte PEC){
  SPI.transfer(command);
  SPI.transfer(PEC);
  while(!digitalRead(MISO)){
    selectLTC();
  }
}

What's the reason for a call to selectLTC() in this routine? If the chip doesn't answer with something other than zero to your command you have an endless loop.

Where do you have the PEC calculation from? Did you implement that yourself?

You haven't changed the SPI mode, did you get to another conclusion about the figure in the datasheet than me?

I took out the delays and still have the same issues.

By looking on the chip, it seems to be that the characters after the numbers are ARWZ. Sorry about the confusion before, the data sheet for this board simply says ADUM3401.

I've checked with the engineer here and we've confirmed that the decoder is being used properly. CS0 is never directly connected to the LTC, but rather is an input to the decoder and the slave select of the LTC is one of the possible outputs of the decoder.

In this case, I have no simpler set up to test any of the hardware on. I'm tasked with programming this board, but I did not design or assemble it.

The PEC calculation came from the person who designed this board. I checked whether its correctly calculating known PEC values which I got from the datasheet, such as the PEC for WRCFG or the example PEC calculation.

The SPI mode should be correct given the datasheet, as it states on page 17 that CPHA = 1 and CPOL = 1, which is SPI_MODE3 according to the arduino SPI reference page.

Also the polling routine was given to me by the designer of the board, though I haven't gotten to progress to that stage as of yet. Its used during the reading voltages stage, which I have yet to implement fully. Most likely that will end up changing, but I'm choosing not to focus on that section of the code until I've resolved the registry issue.

Through more oscillascope observations, I can see that I'm at least sending the correct commands and registry values through the MOSI line connected to the microprocessor. I then tested whether the commands get through the SPI isolator without changing, which they do. Pretty much, all is good in the world of the board until I want to start a read. The read command and PEC are sent through fine, just as the write commands are, but once these read commands finish transferring, MISO drops low until another read command is sent, meant to tell the next chip in the line to send its data.

By looking on the chip, it seems to be that the characters after the numbers are ARWZ

This model works up to max. 1Mbps data frequency. With the SPI frequency divider at 32 you should be on the save side.

I've checked with the engineer here and we've confirmed that the decoder is being used properly. CS0 is never directly connected to the LTC, but rather is an input to the decoder and the slave select of the LTC is one of the possible outputs of the decoder.

Which decoder are you using then? Post a link to the datasheet.

The SPI mode should be correct given the datasheet, as it states on page 17 that CPHA = 1 and CPOL = 1, which is SPI_MODE3 according to the arduino SPI reference page.

You're right with that, I haven't read that paragraph but concentrated on the pictures.