SPI problems with LTC2400 ADC

02 Mar 2011

Hello

I am trying to setup and read 32bit data from an LTC2400 device. The manual for the device can be found here -> http://cds.linear.com/docs/Datasheet/2400fa.pdf It's a 3 wire SPI.

Anyway from the datasheet I gathered the mode was CPOL=0, CPHA=0 and setup the SPI accordingly. I figured since the device has no MISO I could just use the SPI instead of SPIHalfDuplex and not connect the MISO. When CS goes low the device clocks out 32bits of data automatically without sending anything to the device. So I have the following code which doesnt seem to be working. I'm sure I am doing something dumb and one of you nice individuals can help.

Just as a sanity check I used the commented out code to Bit-Bang the SPI port with 32 clock transitions reading the MISO pin of the device. This works as expected and gives proper data bits.

Thanks

#include "mbed.h"

DigitalOut CS(p8);
//DigitalOut clk(p7);
//DigitalIn in(p6);
SPI device(NC, p6, p7); // mosi, miso, sclk


int main() {


    device.format(8,0);
    device.frequency(1000000);
    CS=1;
    wait(.2);



    printf("\n\rHELLO\n\r");

    CS=0;
    wait(.01);
    
    int response = device.write(0xFF);
    
    CS=1;
    printf("result= %u",response);

}
/*  printf("\n\rHELLO\n\r");
  CS=1;
  clk=0;
  CS=0;
  for (int i=0;i<32;i++) {
      wait(.1);
      clk=1;
      wait(.05);
      int B=in;
      printf("%i = %i\n\r",31-i,B);
      clk=0;
  }
  */

02 Mar 2011

You want 32bits? If you put SPI into 8bit mode then you need four fetches:-

int main() {
    uint32_t response;

    device.format(8,0);
    device.frequency(1000000);
    CS=1;
    wait(.2);

    printf("\n\rHELLO\n\r");

    CS=0;
    response = 0;
    response |= device.write(0xFF) << 24;
    response |= device.write(0xFF) << 16;
    response |= device.write(0xFF) << 8;
    response |= device.write(0xFF);
    CS=1;
    printf("result= %lu",response);
}
03 Mar 2011

Ill give that a try,but with my code I should have gotten at least the first byte right? It didn't return anything from the write function....not even the text "result="

03 Mar 2011

printf("result= %u",response);

Actually, you probably did get a response. Just your use of printf() is "faulty".

Try

printf("result= %u\n",response); // ADD \n to FORCE sending it.

or use the specific channel

#include "mbed.h"
Serial pc(USBTX, USBRX);

//... then do 
pc.printf("result= %u",response); // Send directly out tp PC serial port

The problem is "plain old printf()" prints to stdout which is "line buffered". The two solutions (adding \n) forces the stdout to actually come out of the serial port or the second method makes the stream really come out of the serial port as you expect.

The way Mbed does printf() gets a lot of people all wound up in knots without realising it.

03 Mar 2011

Awesome guys. I will test. I usually do usb serial explicitly, except this time....it bit me.

03 Mar 2011

Okay changed the code and getting closer. The data is read as 4 bytes and with the union put into an unsigned int. I then need to manipulate the data to get an int that is constructed from bits 28-4 of those 32 bits. Something in my code doesnt seem to be working because the results vary greatly from one reading to the next.

#include "mbed.h"

DigitalOut CS(p8);
SPI device(NC, p6, p7); // mosi, miso, sclk


union {
    unsigned int i_data;
    char data[3];

} u_data;
unsigned int ADC;

int main() {


    device.format(8,0);
    device.frequency(500000);
//    CS=1;
//    wait(.2);
    printf("\n\rADC SPI Reading\n\r");

    CS=0;
    wait(.2);

    for (int i=0;i<4;i++) {
        u_data.data[i] = device.write(0xFF);
    }

    CS=1;


    ADC = u_data.i_data<<4;
    ADC = ADC>>8;


    printf("result= %u\n\r",ADC);

}

sample output:

ADC SPI Reading result= 16416946

ADC SPI Reading result= 14319794

ADC SPI Reading result= 2785458

ADC SPI Reading result= 11165874

ADC SPI Reading result= 12214450

ADC SPI Reading result= 15356082

ADC SPI Reading result= 1720498

ADC SPI Reading result= 2764978

03 Mar 2011

I think instead of

union {
    unsigned int i_data;
    char data[3];

} u_data;

you meant

union {
    unsigned int i_data;
    char data[4];

} u_data;

Otherwise, when you write the 4th byte in the main loop, bad things will happen.

03 Mar 2011

Oops yes you are correct and in fact I did realize that and change it on my side. Below are two ways to convert the bytes back to a 24bit number. They both seem to work but the first one gives low results every so often while the other never does. Any ideas?

     //     ADC = u_data.i_data<<4;
     //     ADC = ADC>>8;

        ADC =(u_data.data[0]<<20) | (u_data.data[1]<<12) | (u_data.data[2]<<4) | (u_data.data[3]>>4);
        ADC &=0x00FFFFFF;
03 Mar 2011

The union works as follows:

data[0] is the least significant byte of i_data, while data[3] is the most significant byte of i_data.

That is why the results of the first and second versions are different - the second one is scrambling the order of the bytes.

03 Mar 2011

Matt, it looks like you have most of it already. I started this before the last couple of posts. The main points: The data needs to go into the union starting with MSB so byte[3] gets loaded first. The LTC data is in the middle of the 32 bits, looks like you got that already. You should probably look at the sign bit and negate the result if set.

#include "mbed.h"

DigitalOut CS(p8);
SPI device(NC, p6, p7); // mosi, miso, sclk

union LTC2400 {
    unsigned int raw;
    unsigned char byte[4];
    struct {
        unsigned int    
            sub:4,
            reading:24,
            exr:1,
            sgn:1,
            dmy:1,
            eoc:1;
    };
} adc;

int main() {

    CS = 0;
    wait(.2);   // Alternately, monitor mosi and wait for it to go low
    for( int i = 3; i >= 0; i-- ) {
        adc.byte[i] = device.write( 0xFF );
    }
    CS = 1;    

    printf( "raw: 0x%08X  reading: 0x%06X (%d)  sub:%d  exr:%d  sgn:%d  eoc:%d\r\n",
        adc.raw,
        adc.reading,
        adc.reading,
        adc.sub,
        adc.exr,
        adc.sgn,
        adc.eoc );
}