Trouble using TI external ADCADC124S021

I have been trying for a couple of days to read more than one channel from an ADC124S021:
http://www.ti.com/product/adc124s021

It’s a 12bit, quad ADC capable of 50-200ksps

I have it connected to my arduino diecimila via a little breakout board.

All the problems seem be with regards to setting the address. I think I might be getting messed up in the order of signal assetion in terms of CS, writing to the DIN, reading the DOUT etc.
I have been over the data sheet many many times but I am struggling.

For one thing it is not clear if setting the address bits affect the current conversion you are about to read or the next conversion?

I can get it to work if I only read one channel. As soon as I want to read two channels in a row everything is messed up and I can’t isolate the problem.

/***************************************************************************
 *
 * Shift in ADC:   shifts a 12 bit ADC value on the given  channel
 *
 ***************************************************************************/
unsigned int shiftInADC(unsigned int channel) { 
  
       xxx00xxx = ch1
       xxx01xxx = ch2
       xxx10xxx = ch3
       xxx11xxx = ch4

   channel = channel << 3;  //take number 1 to 4 and shift into correct format for ADC

  //xchannel = B00001000;
   unsigned char tempMSB = 0;
   unsigned char tempLSB = 0;


  digitalWrite(ADC_CSpin,LOW);   //start serial frame on falling edge

  //shift out the first byte (address) and read in MSB
 
  for (byte i=0; i< 8; i++)
  {
     
   
   digitalWrite(ADC_CLKpin, LOW);

 if (digitalRead(ADC_DOUTpin)) {    //read data after each falling clk pulse
      tempMSB |= 1<<(7-i);
    } 
     
    digitalWrite(ADC_DINpin, bitRead(channel, 7-i));  //Write the address instruction 1 bit at a time
    
    digitalWrite(ADC_CLKpin, HIGH);
    
   
  }
 
  //Now read in LSB
  for (unsigned char i=0; i< 8; i++)
  {
      digitalWrite(ADC_CLKpin, LOW);
     
     if (digitalRead(ADC_OUTpin)) {
      tempLSB |= 1<<(7-i);
    } 
    digitalWrite(ADC_CLKpin, HIGH);
  }
    
  tempMSB &= B00001111;  //(MSB top 4 bits are junk)


 return word(tempMSB,tempLSB);  //convert MSB and LSB bytes to single unsigned word
    
}

Your second loop doesn’t drive the clock high at all! Use a single 16 bit loop to keep things simpler?

The datasheet explicitly says the address sent in the first byte affects the next conversion, not the current one. (It also implies this if you read the detailed description of the timing of conversion where the sample/hold is performed before all the address bits have been sampled)

You can use the SPI hardware/library (with a suitably low clock speed (< 3.2MHz so divide-by-8) and in mode 3, MSB first) to do the interfacing if you want.

I would write a function that writes a given value to a channel, and then call that function to write to your desired channel.

MarkT:
Your second loop doesn’t drive the clock high at all! Use a single 16 bit loop to keep things simpler?

oops! That was actually just a typo. My original code is using direct port manipulation so I rewrote it as digitalWrite to be easier to read. (I have corrected the code above now)

The datasheet explicitly says the address sent in the first byte affects the next conversion, not the current one. (It also implies this if you read the detailed description of the timing of conversion where the sample/hold is performed before all the address bits have been sampled)

ok I think that makes sense.

So let’s say I wanted to read in ch1 and ch3:
Put ADC_CS low
I would need send the address for ch3 (since this will be the second channel I read) while reading the first conversion which by default will always be channel 1.
Then the next word I read will be from ch3 since this is the address I shifted in on the first read?

What happens if I want to just read Ch3? Will I always need to waste one read/write cycle since the first read will always be for ch0?

You can use the SPI hardware/library (with a suitably low clock speed (< 3.2MHz so divide-by-8) and in mode 3, MSB first) to do the interfacing if you want.

I will definitely try this but I want to get the bit bashing version working first so I can make sure I understand what is going on.

thanks!

I tried sending the address for ch2 on the first call for this function which should mean that the first read is Ch1 and the second read is ch2. Instead I get no valid data for either read.

If I call this function with address 0x00 (Ch1) it properly returns the data for channel 1.

It seems like I am not able to send address data correctly and it is screwing things up. (?)

I keep re-reading the data sheet and as far as I can see I am suppose to be clocking in the address data at the same time as I am reading the data in (but obviously ignoring the first 4 bits of input). Is this a misunderstanding? Should I be sending address data first as one byte and then doing a read cycle? It seems pretty clear that one whole read/write cycle is supposed to be only multiples of 16bits.

I wish they had some C code to see as a sample!

I finally got it to work but it is not as expected.

I basically have to use 32 clock cycles for one read:

16 clocks to clock in address (and ignore the reading)
16 cycles to read value for that address previously stored.

I have not been able to do the back to back readings as implied by the Datasheet which I take to mean that I can read all 4 channels in one 64 clock cycle.

I agree, you ought to be able to continuously read all 4 channels in 64 clocks.

The data sheet says that the SCLK duty cycle must be between 30% and 70% regardless of frequency. This is an odd requirement, however your code does not guarantee to meet it. You could insert some delayMicroseconds calls to even up the duty cycle, or use hardware SPI.