AD7705/AD7706 Library Revisited

About a year ago, I wrote a simple library for interfacing AD7705/AD7706 with Arduino. The library works, but it requires some decent knowledge of the underlying chip, which had made it somewhat difficult to use. Most issues users reported can be resolved by adjusting the timing in user code, but I admit that it is somewhat difficult for users who are not familiar with the chip. For a library, I should have made it easier to use to begin with. So, I decided to add a few long-awaited features and hopefully these tweaks will make the library easier to use.

One of the changes to the original code is the addition of the dataReady function. This function queries the DRY bit in the communication register and returns true when the data ready bit is cleared.

bool AD770X::dataReady(byte channel) {
    setNextOperation(REG_CMM, channel, 1);

    digitalWrite(pinCS, LOW);
    byte b1 = spiTransfer(0x0);
    digitalWrite(pinCS, HIGH);

    return (b1 & 0x80) == 0x0;
}

Using this function, we can wait till the converted data is ready before reading out the conversion result (rather then using delay statements):

double AD770X::readADResult(byte channel, float refOffset) {
    while (!dataReady(channel)) {
    };
    setNextOperation(REG_DATA, channel, 1);

    return readADResult() * 1.0 / 65536.0 * VRef - refOffset;
}

In readADResult, I added an optional parameter refOffset. If your Vref- is not tied to the ground then you can use this variable to set the offset voltage to be subtracted from the conversion result. The default operating mode is set to be bipolar. For AD7705 and AD7706, the difference between unipolar and bipolar operation is simply how the the input signal is referenced so by setting the input mode to bipolar, you can still measure unipolar voltages. All that is needed is to tie the Vref- to the ground and leave the refOffset with the default value (i.e. 0).

I have also added a reset function. By calling this function first in your setup code, you are guaranteed that the chip is brought to a known state. Some of the difficulties users faced using the original library is that, depending on how the system is powered up, the AD770x may not be in a consistent mode and thus the A/D results seemed to be random. The chip reset can be achieved by either using the RESET pin or code. In my opinion, implementing in code is the desired method unless you need highest performance possible. Another benefit is that this implementation requires one less MCU pin.

Finally, I added a few parameters to the alternative constructor. In case you want to fine-tune your setup (e.g. setup different gain/speed), you can use the alternative constructor instead.

The following example shows how to use this library to read ADC results from multiple channels:

#include <AD770X.h>

AD770X ad7706(2.5);
double v;

void setup()
{
  Serial.begin(9600);

  ad7706.reset();
  ad7706.init(AD770X::CHN_AIN1);  
  ad7706.init(AD770X::CHN_AIN2);
}

void loop()
{
  v = ad7706.readADResult(AD770X::CHN_AIN1);
  Serial.print(v);

  v = ad7706.readADResult(AD770X::CHN_AIN2);
  Serial.print(" : ");
  Serial.println(v);
}

Download: AD770X1.1.tar.gz (compatible with Arduino 1.0 IDE)

Be Sociable, Share!

19 Comments

  1. Vasiliy says:

    Hallo Kerry D.Wong!
    It’s from Russia again :)
    Thank you for the new library! Now it is compiled into the first version of the Arduino! But I have a question, is it possible to increase the polling rate channels? Or you can tell which part of the code of your library, can I change the time delay? So I can experiment with delay …
    Thank you again! With the new library, all channels are working fine!

    • kwong says:

      Yes, there is another constructor that takes the update rate as a parameter:

      init(byte channel, byte clkDivider, byte polarity, byte gain, byte updRate)

      and you can use the constants defined in the header to set your rates.
      static const byte UPDATE_RATE_20 = 0×0; // 20 Hz
      static const byte UPDATE_RATE_25 = 0×1; // 25 Hz
      static const byte UPDATE_RATE_100 = 0×2; // 100 Hz
      static const byte UPDATE_RATE_200 = 0×3; // 200 Hz
      static const byte UPDATE_RATE_50 = 0×4; // 50 Hz
      static const byte UPDATE_RATE_60 = 0×5; // 60 Hz
      static const byte UPDATE_RATE_250 = 0×6; // 250 Hz
      static const byte UPDATE_RATE_500 = 0×7; // 500 Hz

      The clock divider can be set accordingly based on your oscillator frequency.

  2. Alex says:

    Hello Kerry,

    Thanks for the library…very useful. I was wondering if it would be possible to use the library with an AD7715 which is pin compatible with the AD7706 but is only a single channel device. Which parts would need to be changed in order to make it work. I think the byte codes for accessing the different registers are the same??

    Thanks again

    Alex

    • kwong says:

      Thanks Alex. As you observered, AD7705 and AD7706 are almost identical. You can use the exact same code for either AD7705 and AD7706. The only change would be the interpretation of the input pin s used.

      For instance, if the channel setting is CH1=0 and CH0=0, for AD7705 AIN1+ and AIN- are used whereas for AD7706 AIN1 and COMMON are used.

      • Alex says:

        Kerry,

        I was actually looking at using the AD7715 which is not quite the same as an AD7706 – it is very similar though but this device only has one channel not two like the AD7706. There are other differences too though, there is no clock register in the AD7715. How difficult would it be to modify the AD7706 library to work with the AD7715? Please note that my coding skill is almost zero!!

        Cheers

        Alex

  3. Alex says:

    Kerry,

    My sincerest apologies, I have it working and it’s excellent!! I did not actually just try the library! Someday I will have to learn how to code properly and understand how libraries are written! Thank you so much for your library and your response…

    Cheers

    Alex

    • Damien says:

      First thanks for this library kwong!

      Alex, have you realy had success with this library and the AD7715? Because I didn’t…
      I suppose you modify it because of the difference between AD770X and the AD7715 like clock register, and channel…
      Would you offer us your modified code?
      (French man writing…)

      Cheers
      Damien

      • Alex says:

        Damien,

        My code used Kerry’s library without being modified. I’m not a very good programmer so I can’t be certain how it worked with the AD7715. The critical part was adding a 100ms delay to Kerry’s example code. Once I did that the device started to work. I tested it on a breadboard with minimal components as specified in the datasheet with a variable resistor to provide analogue data! It worked first time…best of Luck

        Here is my code:

        /*
        * AD770X Library
        * sample code (AD7706)
        * Kerry D. Wong
        * http://www.kerrywong.com
        * 3/2011
        */

        #include

        AD770X ad7706(2.5f);
        unsigned int v;

        void setup()
        {
        Serial.begin(115200);

        ad7706.reset();
        ad7706.init(AD770X::CHN_AIN1);
        // ad7706.init(AD770X::CHN_AIN2);
        Serial.println(“Here we go…”);
        }

        void loop()
        {
        v = ad7706.readADResult(AD770X::CHN_AIN1);
        Serial.println(v);
        delay(100); //100ms delay for AD7715
        }

        • Damien says:

          Thanks Alex for replying!

          But…
          I can’t believe how it worked for you with the AD7715…
          I’ve tried you sketch, but it didn’t work for me…
          This library doesn’t work for me, but I wrote one for our AD7715.
          I didn’t spend a lot of time on, and even it seem’s to work quite
          well (thanks to kwong) there are a lots of difference between AD770X
          and AD770X…

  4. Bodhi says:

    Hi Kerry,
    i’m using your library for AD7705. I still don’t understand how to get data. To take data from AIN1, this is the code i use:

    #define LOOP_DELAY 120

    setup:
    ad7705.reset();
    ad7705.init(AD770X::CHN_AIN1);
    ad7705.init(AD770X::CHN_AIN3);

    loop:
    delay(LOOP_DELAY);
    temp1 = ad7705.readADResult(AD770X::CHN_AIN1);
    delay(LOOP_DELAY);
    temp2 = ad7705.readADResult(AD770X::CHN_AIN3);
    total[m]=temp2-temp1;

    I’m working with UPDATE_RATE_60. Is there other way to get data?? If i only read AIN3 or AIN1, sometimes it doesn’t get data. Depending on the delay time (loop_delay), accuracy is higher or lower. When i read AIN1, i only have ground and i supposed that AIN1 and AIN3 should be for AIN1+/-. Why do i have to read AIN1 and AIN3 for AIN1+/-??.
    I’m working with a duemilanove ATM328.
    Thanks in advance

    • kwong says:

      Hi Bodhi,

      You can configure the AD7705 in either differential (bipolar) or single-ended (unipolar) operation. The simple constructor you used defaults to unipolar. If you need to take differential measurement, you can use the overloaded constructor: AD770X::init(byte channel, byte clkDivider, byte polarity, byte gain, byte updRate) , you can take a look at the cpp file to see how it can be used.

      I am not sure why you need to include delay in your code though.

      • Bodhi says:

        Hi kwong,
        If i don’t use the delay, sometimes it doesn’t measure anything and accuracy is lower. I have to measure thermocouples and accuracy is very important.
        I tried to use bipolar but values were wrong and the only way i could work was the one i wrote in the past post.
        Thanks for your response.

        • Bodhi says:

          Hi again kwong,
          i tried to test your test program and i also need a delay. if i change the update_rate to other value different to 25 it doesn’t work, only measure 0.0000. With the gain, happens the same, measure something but out of the reality.
          I’m using a crystal of 4.000M, can this be the reason?.
          Thanks in advance

  5. Bodhi says:

    Hi again Kwong,
    how can i poll the dataready? only checking the pin 12 of the ad7705?

  6. Linas says:

    Hello ,

    Can you check you library for issuse :
    ad7706.init(AD770X::CHN_AIN1,AD770X::CLK_DIV_1, AD770X::BIPOLAR, AD770X::GAIN_4, AD770X::UPDATE_RATE_25);

    Init must setup CHN_AIN1 , but it sets CHN_AIN2 … The same with AIN2. This settings must be swaped.
    Regards,
    Linas

  7. Linas says:

    Nice librarry , great job !!

Leave a Reply