adafruit / Adafruit_ADS1X15

Driver for TI's ADS1015: 12-bit Differential or Single-Ended ADC with PGA and Comparator
Other
298 stars 307 forks source link

Problem with computeVolt and compiler optimisation #93

Open anvgfr opened 3 months ago

anvgfr commented 3 months ago

Hello,

using your library to make data acquisition i noticed that sometimes computeVolt return a bad result, due to some compiler optimisation, like in this output :

Channel 1 and 2 are wrong : rms is rounded as int before calling computeVolt to get Vapp, it should be 0V !

# Channel 0 : rms=0.8519 / min=0 / max=2 /  Vapp = 97897.4375V / min=0.0000V / max=0.0040V /  I=5933178.0000A
# Channel 1 : rms=0.9333 / min=-1 / max=3 /  Vapp = 0.0000V / min=-0.0020V / max=0.0060V /  I=0.0000A
# Channel 2 : rms=0.9070 / min=0 / max=2 /  Vapp = 68943856272903503872.0000V / min=0.0000V / max=0.0040V /  I=4178415527426349072384.0000A
# Channel 3 : rms=0.7184 / min=-1 / max=1 /  Vapp = -0.0000V / min=-0.0020V / max=0.0020V /  I=-0.0000A
# Channel 4 : rms=0.9837 / min=-1 / max=3 /  Vapp = -0.0000V / min=-0.0020V / max=0.0060V /  I=-0.0020A

I am running on ESP32 with Arduino_ESP32 library (Arduino IDE version 2.3.2), AdaFruit latest release (2.5.0). Chips is an ADS1015 (from techniot bought on Amazon)

Original parts of the code producing random wrong value some times:

> > MainPowerChannel.RMSValue = MainPowerChannel.RMSValue / MainSampleRead;
> > MainPowerChannel.RMSValue = sqrt(MainPowerChannel.RMSValue);
> > // Convert to Volt using ADC resolution
> > MainPowerChannel.Vapp = adcComputeVolts(iMainVoltageADC, MainPowerChannel.RMSValue);
> 

(source is used to get the right ADC object to used on a list of 2...)

float adcComputeVolts(int _source, int16_t value) {
  if (_source >= NB_MAX_ADC_CHANNEL) return -1;
  int adsid = _source / NB_CHANNEL_BY_ADC;
  int adspin = _source % NB_CHANNEL_BY_ADC;
  //Serial.printf("ACV? %d=(%d,%d) %d\r\n",_source,adsid,adspin,value);
  //int16_t retvalue=value*1;
  return ads[adsid].computeVolts(value);
}

to solve temporarly this problem i had to use the source value before calling computeVolt in my own code (using a printf or another operation. (removing one or the other comment)

reading the current library code, i found that the current operation give priority on the division of the full range value (float) by the resolution BEFORE applying current source value.

float Adafruit_ADS1015::computeVolts(int16_t counts) 
{
  // see data sheet Table 3
  float fsRange;
  switch (m_gain) {
  case GAIN_TWOTHIRDS:
    fsRange = 6.144f;
    break;
  case GAIN_ONE:
    fsRange = 4.096f;
    break;
  case GAIN_TWO:
    fsRange = 2.048f;
    break;
  case GAIN_FOUR:
    fsRange = 1.024f;
    break;
  case GAIN_EIGHT:
    fsRange = 0.512f;
    break;
  case GAIN_SIXTEEN:
    fsRange = 0.256f;
    break;
  default:
    fsRange = 0.0f;
 }
 return counts * (fsRange / (32768 >> m_bitShift));
}

So i changed the order of the code to do the multiplication first then the division (better for accurancy)

  // Change order of operation to do upscaling before downscaling;
  return (fsRange*counts) / (32768 >> m_bitShift);

And now i did'nt get any problem with the calculation.

anvgfr commented 3 months ago

For other users that will come on this problem :

Searching more for this problem since it still occurs, i fall on a weird bug in espressif SDK with floating point calculation errors due to context switching. It seems to have been corrected in 5.1.0 version and for the ESP32-S3, but since it is not my target, i suspect that the problem is wider and not corrected on the 4.4 branch too.

https://github.com/espressif/esp-idf/issues/11690