RobTillaart / ADS1X15

Arduino library for ADS1015 = I2C 12 bit ADC and ADS1115 = I2C 16 bit ADC
MIT License
144 stars 28 forks source link

Library compatible for STM32F407 #34

Closed hanmey97 closed 2 years ago

hanmey97 commented 2 years ago

Hey, i'm using your Library with PlatormIO on my Black STM32F407VE. It works good, but the Datarate is a bit too low. I only archive 1375 SPS with the ADS1015.

int x;
int16_t result[600];
long start, end;

void setup()
{
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("ADS1X15_LIB_VERSION: ");
  Serial.println(ADS1X15_LIB_VERSION);

  ADS.begin();
  ADS.setGain(0);  // 6.144 volt
  ADS.setDataRate(7);
  ADS.setWireClock(400000);

}

void loop()
{
    start = micros();
  for (int i = 0; i < 600; i++)
  {

    result[i] = ADS.readADC_Differential_0_1();

  }

  end = micros();

  for (int i = 0; i<600; i++){
    Serial.print("Differential: "); Serial.print(result[i]); Serial.print("("); Serial.print(ADS.toVoltage(result[i])); Serial.println("mV)");
  }
  Serial.print("Dauer pro Messung in Mikrosekunden:\t");
  Serial.println((end - start)/600.);

  Serial.println("\nDone...");
  delay(2000);
}

Do you know, where my Problem is?

Thank you very much!

RobTillaart commented 2 years ago

hi @hanmey97

The max speed is only possible in continuous mode (in my experience) for a single channel. As the differential read need two ADC calls I expect it can only go roughly at half speed. (iirc there is only one ADC internally)

What performance do you get with a single channel read()?

RobTillaart commented 2 years ago

If for instance channel 0 changes much less (e.g. 10x less) than channel 1 you could do the differential yourself in a faster way.

  start = micros();
  for (int i = 0; i < 60; i++)
  {
    uint32_t val0 = ADS.readADC(0);
    for (int j = 0; j < 10; j++)
    {
       result[i] = val0 - ADS.readADC(1);
    }
  }
  end = micros();

If this is usable depends on the signals of both channels.


Another way to get finer granularity of the results is the alternating method. If this is usable also depends on the signals.

  start = micros();
  uint32_t val1 = 0;
  uint32_t val0  = ADS.readADC(0);
  for (int i = 0; i < 600; )
  {
    val1 = ADS.readADC(1);
    result[i++} = val0 - val1;
    val0 = ADS.readADC(0);
    result[i++} = val0 - val1;
  }
  end = micros();
hanmey97 commented 2 years ago

Thank you for your prompt reply!

I tried the Single Channel ans also the alternating Mode. In each Version, it tooks around 720µs for each measurement. I read about the HighSpeed-Mode in the Datasheet, which allows higher I2C Speeds. Up to 3,5MHz. Do you think this could help?

RobTillaart commented 2 years ago

720 us is also ~1400x sec.

The time you measure is (1) initiate the sampling (2) making the sample (3) communicating the value to your processor (4) store it in the result array.

Setting high speed I2C would reduce the communication time.

If you do continuous sampling and use an interrupt flag (1) would go automatically (2) would take same time but when reading (3) the ADS could already make the next sample.

RobTillaart commented 2 years ago

CAn you check the - ADS_async_differential.ino example ? What performance do you get there?

hanmey97 commented 2 years ago

Okay. I tried the continous measurement.

void setup()
{
  Serial.begin(115200);
  //Serial.begin();
  Serial.println(__FILE__);
  Serial.print("ADS1X15_LIB_VERSION: ");
  Serial.println(ADS1X15_LIB_VERSION);

  ADS.begin();
  ADS.setGain(0); // 6.144 volt
  ADS.setDataRate(7);
  ADS.setWireClock(400000);
  ADS.setMode(1);
  ADS.requestADC_Differential_0_1();
}

void loop()
{
  start = micros();
  for (int i = 0; i < (int)samples; i)
  {
    //result[i] = ADS.readADC_Differential_0_1();
    if (ADS.isReady()){
      ADS.requestADC_Differential_0_1();
      result[i] = ADS.getValue();
      i++;
    }

    //result_time[i] = micros();
  }

  end = micros();

with this code i get 574µs per measurement. That would be enough for my purpose.

Still, it's strange that it doesn't even come close to reaching full speed.

RobTillaart commented 2 years ago

@hanmey97 574 us = ~1740 differential readings per second.

Be aware that you need two readings for one differential so the ADC makes ~3480 conversions per second. This is pretty much what is possible.

If you need more samples you need to drive 2 IC's in parallel, each their own ADC and do the diff on the processor. Might be a nice example sketch to write :)

RobTillaart commented 2 years ago

Had a quick "merge of two examples" to get a high speed differential with 2 IC's and taking differential between both channel 0, 1 2 and 3. Will add soon to the example sketches so this is an unverified "sneak preview" ;)

//
//    FILE: ADS_high_speed_differential.ino.ino
//  AUTHOR: Rob.Tillaart
// VERSION: 0.1.0
// PURPOSE: read from 2 IC's for high speed differential
//          interrupt driven to catch all conversions.
//

// test setup (not tested yet)
// - connect 2 ADS1x15 to I2C bus
// - connect potmeters to all channels
// - code reads both at the same frequency
//   and calculates differential per pair.
//   as 2 ADC's go in parallel, two ADS1015 should get
//   3000+ differential samples / second.
//

#include "ADS1X15.h"

// adjust addresses if needed
ADS1115 ADS_1(0x49);
ADS1115 ADS_2(0x48);

volatile bool RDY_1    = false;
volatile bool RDY_2    = false;
uint8_t channel        = 0;
int32_t differential[4] = { 0, 0, 0, 0 };

void setup()
{
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("ADS1X15_LIB_VERSION: ");
  Serial.println(ADS1X15_LIB_VERSION);

  // SETUP FIRST ADS1115
  ADS_1.begin();
  ADS_1.setGain(0);        // 6.144 volt
  ADS_1.setDataRate(7);    // fastest conversion rate.

  // SET ALERT RDY PIN
  ADS_1.setComparatorThresholdHigh(0x8000);
  ADS_1.setComparatorThresholdLow(0x0000);
  ADS_1.setComparatorQueConvert(0);

  // SET INTERRUPT HANDLER TO CATCH CONVERSION READY
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), adsReady_1, RISING);

  ADS_1.setMode(0);          // continuous mode
  ADS_1.readADC(channel);    // trigger first read

  // SETUP SECOND ADS1115
  ADS_2.begin();
  ADS_2.setGain(0);        // 6.144 volt
  ADS_2.setDataRate(7);

  // SET ALERT RDY PIN
  ADS_2.setComparatorThresholdHigh(0x8000);
  ADS_2.setComparatorThresholdLow(0x0000);
  ADS_2.setComparatorQueConvert(0);

  // SET INTERRUPT HANDLER TO CATCH CONVERSION READY
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(3), adsReady_2, RISING);

  ADS_2.setMode(0);          // continuous mode
  ADS_2.readADC(channel);    // trigger first read
}

void loop()
{
  if (handleConversion() == true)
  {
    for (int i = 0; i < 4; i++)
    {
      Serial.print(differential[i]);
      Serial.print("\t");
    }
    Serial.println();
  }

}

// catch interrupt and set flag
void adsReady_1()
{
  RDY_1 = true;
}

void adsReady_2()
{
  RDY_2 = true;
}

// handle conversions if both are ready
bool handleConversion()
{
  if (RDY_1 == false) return false;
  if (RDY_2 == false) return false;

  // read the value of both
  int16_t a = ADS_1.getValue();
  int16_t b = ADS_2.getValue();
  differential[channel] = a - b;
  // request next channel
  channel++;
  if (channel >= 4) channel = 0;
  ADS_1.readADC(channel);
  ADS_2.readADC(channel);
  RDY_1 = false;
  RDY_2 = false;

  return true;
}

// -- END OF FILE --

update: added to master branch, will be part of next release (whenever).

hanmey97 commented 2 years ago

Thats a good version for differential measurement.

I made one for a single measurement. For this example the ADC-Pins are connected in parallel.

I just edited the handleConvrsaion()

int16_t handleConversion()
{
  int16_t analog_val;
  if (first_adc == true && RDY_1 == true){

      analog_val = ADS_1.getValue();
      first_adc = !first_adc;
      RDY_1 = false;
      ADS_1.readADC(channel);
  }

  else if (first_adc == false && RDY_2 == true){

      analog_val = ADS_2.getValue();
      first_adc = !first_adc;
      RDY_2 = false;
      ADS_2.readADC(channel);

  }
  else return false;

  // read the value of both
  channel++;
  if (channel >= 4) channel = 0;

  return analog_val;

}

It's untested too, but my second ADS1015 ist on the way. When it arrives, I'll test it.

RobTillaart commented 2 years ago

Think your function will not return what you expect as the variable analog-val is declared multiple times with different scope.

hanmey97 commented 2 years ago

Oh thats right. It was a copy-paste mistake. On declaration should be enough.

RobTillaart commented 2 years ago

And you should initialize it to some value in case both paths of the if else if fail

RobTillaart commented 2 years ago

Think the issue can be closed?

hanmey97 commented 2 years ago

Yes, thank you very much!