Author Topic: AD7708 ADC Timming Issues Help  (Read 6304 times)

0 Members and 1 Guest are viewing this topic.

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
AD7708 ADC Timming Issues Help
« on: June 09, 2014, 08:21:40 pm »
Hi all

I've been buried with datasheet details with a ADC I'm  sampling the AD7708, so far everything seems to be working from a configuration point of view, however I hit a point where I don’t have a clue how to proceed as the datasheet makes no reference to timing diagrams or application notes.
The problem / confusion is two parts…

How do I reconstruct the 16-bit ADC value from the read-only register inside the ADC?  I tried the following.

1)   I used the SPI data in line to issue ISR when the SPI buffer is full. (SPI enhanced mode) , I get nothing the code locks up when I read the      SPI1BUF register. 

2)   Then I tried a more formal approach and just read the port and built the data as it gets shifted in, however this works so so…
 
A successful ADC conversion is signalled via the READY pin which I can confirm is low (indicating success), but the ADC 16-bit conversion data packet only occurs approximately 350us later (while the READY pin is still low)

How do I go about correctly synchronising the conversion data with the READY PIN at the moment it seems incorrect as the conversion codes are random 65535 (Full scale) with 2.5v applied then it reads 65535 with 0v at the ADC input

Code: [Select]

           adc_spi_start(0);                        // Setup ADC SPI.

        adc_write_byte(ADC_REG_FILTER);   // (ADC Filter Register)
        adc_write_byte(0x45);                    // CHOP=Enabled, fADC=19.79(Hz, tADC=50.34(ms)

        adc_write_byte(ADC_REG_CTRL);    // (ADC Control Register)
        adc_write_byte(0xf);                     // CH-1, enable unipolar coding mode, Input range ± 2.56 V

                                                          // ADC Calibration START.
        adc_write_byte(ADC_REG_MODE);   // (ADC Mode Register)
        adc_write_byte(0x14);                   // Internal zero calibration, 10 pseudo-differential input, reference selected is REFIN1(+) and REFIN1(?) for the active channel.
                                                         // analog negative input (AINCOM) is unbuffered allowing it to be tied to AGND in single-ended input configuration, and chopping is enabled.
        while(ADC_RDY);

        adc_write_byte(ADC_REG_MODE);  // (ADC Mode Register)
        adc_write_byte(0x15);                  // Internal Full-Scale Calibration, 10 pseudo-differential input, reference selected is REFIN1(+) and REFIN1(?) for the active channel.
                                                        // analog negative input (AINCOM) is unbuffered allowing it to be tied to AGND in single-ended input configuration, and chopping is enabled.
        while(ADC_RDY);
                                                        // ADC Calibration END.
 
        adc_write_byte(ADC_REG_MODE); // (ADC Mode Register)
        adc_write_byte(0x03);                 // Continuous conversion mode
        while(ADC_RDY);

        adc_write_byte(ADC_REG_CTRL);  // (ADC Control Register)
        adc_write_byte(0xf);                   // select channel 1 and its operating range.
        while(ADC_RDY);

        close_spi();           // Close Setup ADC SPI.


while(true)
{
        #if TEST_ADC_READ
              adc_spi_start(0);                                // Setup ADC SPI.
              adc_write_byte(ADC_REG_DATA);

              // adc_write_byte(ADC_REG_STATUS);
              // while(ADC_RDY);
                         
              close_spi();           // Close Setup ADC SPI.
       #endif
}


void __attribute__((__interrupt__,__auto_psv__)) _CNInterrupt(void)
{
    /*
        When a CN interrupt occurs, the user should read the PORT register associated with the CN
        pin(s). This will clear the mismatch condition and set up the CN logic to detect the next pin
        change. The current PORT value can be compared to the PORT read value obtained at the last
        CN interrupt to determine the pin that changed.
        The  CN  pins  have  a  minimum  input  pulse-width  specification.  Refer  to  the  ?Electrical
        Characteristics? section of the specific device data sheet for further details.
    */


    if( ADC_RDY == LOW )
    {
        __delay_us(350);
        g_adc_value = adc_read_word();
    }
    process_rotray_encoder();
    IFS1bits.CNIF = 0;    // Clear flag for on change interrupt
}


long adc_read_word()
{
    char i;
    long adc_data = 0;

        for( i = 1; i <= 16; ++i )     // Get the ADC data as a 16-bit value.
        {
            adc_data = adc_data << 1;  // Shift one bit left prepare place for next bit.
            if(ADC_DATA == HIGH)               // ADC bit high ?
            {
                adc_data |= 0x01;      // Append 1-bit at the start of word.
            }
        }
   
    return adc_data;
}



[code]
« Last Edit: June 09, 2014, 08:38:08 pm by diyaudio »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: AD7708 ADC Timming Issues Help
« Reply #1 on: June 10, 2014, 10:52:50 am »
Quote
How do I reconstruct the 16-bit ADC value from the read-only register inside the ADC? 

That's not a question about the timing.

the datasheet suggests a write to the communications register, with A3..0 loaded with the address of the dataregister (0b0100), and the next cycles will read the data register which contains the adc results.

Quote
I tried the following.

No point in trying: just read the datasheet.

Quote
I read the      SPI1BUF register. 

You have to make sure that your spi portion of the code is already working - use a scope on the data lines + click line. Or a logic analyzer.

================================
https://dannyelectronics.wordpress.com/
 

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
Re: AD7708 ADC Timming Issues Help
« Reply #2 on: June 10, 2014, 11:04:12 am »
Quote
How do I reconstruct the 16-bit ADC value from the read-only register inside the ADC? 

That's not a question about the timing.

the datasheet suggests a write to the communications register, with A3..0 loaded with the address of the dataregister (0b0100), and the next cycles will read the data register which contains the adc results.

Quote
I tried the following.

No point in trying: just read the datasheet.

Quote
I read the      SPI1BUF register. 

You have to make sure that your spi portion of the code is already working - use a scope on the data lines + click line. Or a logic analyzer.

Hey dannyf.

I have read the datasheet, I'm getting closer to a solution but I still need help. Here is the part I miss understood.

I thought after querying the data register, the READY pin is low, this was not the case...

Quote from the datasheet
" the RDY bit of the Status Register is monitored to determine when the Data Register is updated."  :-+

There are two ways the ISR or polling method to query data im using the polling method.

Code: [Select]
   while(ADC_RDY == LOW)
                {
                    adc_spi_start(0);                           // Setup ADC SPI.
                    adc_write_byte(ADC_REG_DATA);    // Query the ready-only data register.
                    g_adc_value = adc_read_word();     // Now get the conversion data.
                   // close_spi();                                // Close Setup ADC SPI.
                }

See the timing attachment the problem is clear I think, the adc conversion return data is 16-bit, the SPI queries the registers in 8-bit mode, how do I go about querying the ADC data register (which is in 8-bit mode) then wait for the data which is now in 16-bit mode..

 
see attachment ADC_1-VOLT-SPI_CLK_VS_ADC_DATA

As I slowly change the ten turn pot I can clearly see the data packets change both traces are changing, but why are they so far spaced ?I expected one continuous 16-bit adc conversion data packets, not separate bursts.





« Last Edit: June 10, 2014, 11:13:53 am by diyaudio »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: AD7708 ADC Timming Issues Help
« Reply #3 on: June 10, 2014, 11:10:38 am »
The datasheet talks about the _RDY bit being cleared after a period of time as well.

Quote
the SPI queries the registers in 8-bit mode, how do I go about querying the ADC data register (which is in 8-bit mode) then wait for the data which is now in 16-bit mode..

Two ways:

1) wait for the _RDY pin to go low, which seems to be what you are doing; This is the fastest approach
2) wait for the RDY bit to go high. This approach saves a pin but requires that you read the status register constantly and test the RDY bit.

Approach 1 can be done via polling or interrupt; Approach 2 can be done via polling only.
================================
https://dannyelectronics.wordpress.com/
 

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
Re: AD7708 ADC Timming Issues Help
« Reply #4 on: June 10, 2014, 11:25:06 am »
The datasheet talks about the _RDY bit being cleared after a period of time as well.

Quote
the SPI queries the registers in 8-bit mode, how do I go about querying the ADC data register (which is in 8-bit mode) then wait for the data which is now in 16-bit mode..

Two ways:

1) wait for the _RDY pin to go low, which seems to be what you are doing; This is the fastest approach
2) wait for the RDY bit to go high. This approach saves a pin but requires that you read the status register constantly and test the RDY bit.

Approach 1 can be done via polling or interrupt; Approach 2 can be done via polling only.

Yes, that's what I'm doing.

I also noticed that some of the shy amounts of sample code actually manually clocks the SPI, I assume its probably got to do with this issue.

Code: [Select]

/*unsigned*/ u16 read16(void) 
    {
     /*unsigned char*/ u8 stevec;
     /*unsigned int*/ u16 result=0;

      DIN_AD_1;                 
      chip_select_adc(0); //CS_AD0_1;
      SCL_AD_1;
  SCL_AD_1;
      chip_select_adc(1); //CS_AD0_0;
     stevec = 16;
   do {
       SCL_AD_0;
   SCL_AD_0;
       result = result << 1;
       if(DOUT_AD) result |= 0x01;
       SCL_AD_1;SCL_AD_1;
      } while(--stevec);
       chip_select_adc(0); //CS_AD0_1;
     return result;
    }

 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: AD7708 ADC Timming Issues Help
« Reply #5 on: June 10, 2014, 12:28:46 pm »
Quote
Yes, that's what I'm doing.

There are two options so I am not sure which is "that" you are referring to.

Quote
I also noticed that some of the shy amounts of sample code actually manually clocks the SPI, I assume its probably got to do with this issue.

Not sure what "this issue" is, but there are many reasons to use software spi.

As your modules are built on the spi layer, you have to eliminate any doubt that it is not working before you can go any further.

One possibility, since you didn't show how those write routines work, is your management of the _CS line: a spi routine that manages the CS line on a per byte basis has no use.
================================
https://dannyelectronics.wordpress.com/
 

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
Re: AD7708 ADC Timming Issues Help
« Reply #6 on: June 10, 2014, 12:58:42 pm »
Quote
Yes, that's what I'm doing.

There are two options so I am not sure which is "that" you are referring to.

Quote
I also noticed that some of the shy amounts of sample code actually manually clocks the SPI, I assume its probably got to do with this issue.

Not sure what "this issue" is, but there are many reasons to use software spi.

As your modules are built on the spi layer, you have to eliminate any doubt that it is not working before you can go any further.

One possibility, since you didn't show how those write routines work, is your management of the _CS line: a spi routine that manages the CS line on a per byte basis has no use.

Sorry, for the nebulous response I'm responding and prototyping simultaneously. 

Yes, I meant I'm polling the ready status as you noted.

As for the software SPI clock okay so here is the result, I think I'm almost there. see attachment.

The attachment shows a SPI software clock manually clocked at 16-bits and the response shows clear the result. also
noted the SPI data timing order
1) First the query to the data register  8 bits.
2) Shutdown the hardware SPI and clock the SPI clock using software 16-bits long see the data response.   


Code: [Select]

                // ADC conversion ready ?
                while(ADC_RDY == LOW)
                {

                    adc_spi_start(0);                           // Setup ADC SPI.
                    adc_write_byte(ADC_REG_DATA);    // Query the ready-only data register.
                   close_spi();                                   // Close Setup ADC SPI.
             
                    ADC_CS = LOW;
                    char i = 0;
                      for( i = 1; i <= 16; ++i )     // Get the ADC data as a 16-bit value.
                        {
                            PORTBbits.RB7 = LOW;
                            __delay_us(1);

                            PORTBbits.RB7 = HIGH;
                            __delay_us(1);
                         
                        }
                    ADC_CS = HIGH;
                }

« Last Edit: June 10, 2014, 01:00:27 pm by diyaudio »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: AD7708 ADC Timming Issues Help
« Reply #7 on: June 10, 2014, 01:23:46 pm »
That seems to be very convoluted.

Here is what I would do:

Code: [Select]
#define AD7708_READY()  (ADC_RDY == LOW) //true if adc_ready pin is low

//read ad7708/7718, current channel, right aligned
//READY pin assumed to be low at this point
//CS pin managed inside this routine - may need to change if multiple adc7708 is read
unsigned long adc7708_read(void) {
  unsigned long tmp=0; //hold the intermediarate data
 
  //READY pin is already low
  //put down CS pin - idles high
  SPI_SELECT(ADC7708_CS);
  spi_write_byte(ADC7708_REG_DATA); //set to read the data
  tmp = spi_write_byte(0x00); //read msb
  tmp <<= 8;  tmp |= spi_write_byte(0x00); //read 2nd byte
  //tmp <<= 8;  tmp |= spi_write_byte(0x00); //read 3rd byte for adc7718
  //pull CS pin high
  SPI_DESELECT(ADC7708_CS);
  return tmp;
}

By linking in the hardware or software spi_write_byte(), you would have switched easily between hardware or software spi modules. The high level routines should not care whether the spi transmission is hardware or software.
================================
https://dannyelectronics.wordpress.com/
 

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
Re: AD7708 ADC Timming Issues Help
« Reply #8 on: June 10, 2014, 01:45:16 pm »
 :-+ :-+ Okay the problem has been addressed as I suspected it was a timing issue.

Firstly, when clocking the ADC while writing to the various calibration, channel selection, filter registers works as expected. this is all done in 8-bit mode. 

Secondly, when performing a write and read things are a bit more tricky, all I did was query the ADC data register via the adc config register(in read mode, 8-bit register) using the SPI hardware, then turn off the SPI peripheral, when querying the data register I software clock 16-bits and build the data as it shifts in from the adc data pin. (I find this method very easy to read from a code point of view)


See the image for details.


Unipolar Mode
In unipolar mode of operation the output coding is straight binary. With an analog input voltage of 0 V the output code is 0000Hex
for the AD7708 and 000000Hex for the AD7718. With an analog input voltage of 1.024 V REF /Gain the output code is FFFFHex
for the AD7708 and FFFFFF Hex for the AD7718. The output code for any analog input voltage can be represented as follows:


Code = (AIN  ×  GAIN  ×  2N)/(1.024  ×  V REF )
where
AIN is the analog input voltage and
N = 16 for the AD7708 and N = 24 for the AD7718.

AIN = 2.5V
GAIN = 1
2N = 2 ^ 16
VREF = 2.5

ADC CODE @ full scale = 6400

Note there is some noise as im using a breadboard so noisy codes will be expected, noise immunity was not the goal here.

 





« Last Edit: June 10, 2014, 01:59:06 pm by diyaudio »
 

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
Re: AD7708 ADC Timming Issues Help
« Reply #9 on: June 10, 2014, 02:04:12 pm »
That seems to be very convoluted.

Here is what I would do:

Code: [Select]
#define AD7708_READY()  (ADC_RDY == LOW) //true if adc_ready pin is low

//read ad7708/7718, current channel, right aligned
//READY pin assumed to be low at this point
//CS pin managed inside this routine - may need to change if multiple adc7708 is read
unsigned long adc7708_read(void) {
  unsigned long tmp=0; //hold the intermediarate data
 
  //READY pin is already low
  //put down CS pin - idles high
  SPI_SELECT(ADC7708_CS);
  spi_write_byte(ADC7708_REG_DATA); //set to read the data
  tmp = spi_write_byte(0x00); //read msb
  tmp <<= 8;  tmp |= spi_write_byte(0x00); //read 2nd byte
  //tmp <<= 8;  tmp |= spi_write_byte(0x00); //read 3rd byte for adc7718
  //pull CS pin high
  SPI_DESELECT(ADC7708_CS);
  return tmp;
}

By linking in the hardware or software spi_write_byte(), you would have switched easily between hardware or software spi modules. The high level routines should not care whether the spi transmission is hardware or software.


This is what I did.
Code: [Select]
long adc_read_word()
{
    long adc_data = 0;
    char i = 0;
    ADC_CS = LOW;
    for( i = 1; i <= 16; ++i )     // Get the ADC data as a 16-bit value.
    {
        PORTBbits.RB7 = LOW;
         __delay_us(1);
         adc_data = adc_data << 1;  // Shift one bit left prepare place for next bit.
         if(ADC_DATA == HIGH)        // ADC bit high ?
             adc_data |= 0x01;          // Append 1-bit at the start of word.
         PORTBbits.RB7 = HIGH;
         __delay_us(1);
    }
    ADC_CS = HIGH;
    return adc_data;
}
 




 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: AD7708 ADC Timming Issues Help
« Reply #10 on: June 10, 2014, 02:26:49 pm »
Quote
ADC_CS = LOW;
...
    ADC_CS = HIGH;

That's generally not a good way to manage the code - it makes it impossible to use the code on other chips (7718 for example).
================================
https://dannyelectronics.wordpress.com/
 

Offline diyaudioTopic starter

  • Frequent Contributor
  • **
  • !
  • Posts: 683
  • Country: za
Re: AD7708 ADC Timming Issues Help
« Reply #11 on: June 10, 2014, 02:30:07 pm »
Quote
ADC_CS = LOW;
...
    ADC_CS = HIGH;

That's generally not a good way to manage the code - it makes it impossible to use the code on other chips (7718 for example).

true, I can now after verifying the prototype, start moving things towards portability. 
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: AD7708 ADC Timming Issues Help
« Reply #12 on: June 10, 2014, 05:05:55 pm »
Code: [Select]
     PORTBbits.RB7 = LOW;
         __delay_us(1);
         adc_data = adc_data << 1;  // Shift one bit left prepare place for next bit.
         if(ADC_DATA == HIGH)        // ADC bit high ?
             adc_data |= 0x01;          // Append 1-bit at the start of word.
         PORTBbits.RB7 = HIGH;
         __delay_us(1);

1) The chip can take much faster than clock than those delays would suggest. So I would take them out;
2) You want to produce as close to 50% duty cycle clock pulses as you can. So you should play with the placement of RB7 setting / clearing to achieve that.

I usually do something liks this:

Code: [Select]
  set_clk();
  if (ADC_DATA==HIGH) adc_data |= 0x01;
  else adc_dat|= 0x00;
  clr_clk();
  adc_data = adc_data << 1;

Putting the shift of adc_data after clearing the clock helps produce equal on / off time on the clock.
Having to OR '0' produces equal length execution for the if statement.
================================
https://dannyelectronics.wordpress.com/
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf