CuriousScientist0 / ADS1256

An Arduino-compatible library for the ADS1256 24-bit analog-to-digital converter.
MIT License
26 stars 9 forks source link

Adaptive PGA Control for Improved Small-Scale Voltage Measurement Precision #5

Closed srisankaran closed 7 months ago

srisankaran commented 7 months ago

It would be cool if there was a function to implement a dynamic function to monitor voltage levels and automatically adjust the Programmable Gain Amplifier (PGA) when readings fall below specified thresholds, such as 2.5V, 1.25V,... This intelligent control mechanism optimizes precision by fine-tuning the PGA settings, ensuring more accurate and reliable small-scale voltage measurements.

CuriousScientist0 commented 7 months ago

Go ahead and implement it. The code is open source.

srisankaran commented 7 months ago

Why isn't it a good idea?

CuriousScientist0 commented 7 months ago

Do it, if you want, I won't do it for you.

srisankaran commented 7 months ago
/*________________________________________________________________________________________________________
ESP32 micro-Voltmeter using ADS1256 
By: Aryn (aka Sri Sankaran)
Last edit: 31/01/2024
________________________________________________________________________________________________________*/

/*INFO

  PIN CONFIG (ESP32)
    SPI *default pins:
    IN    - 23 
    OUT   - 19
    SCK   - 18
    CS    - 5 
    -----------------
    Other pins:
    RST   - 4
    DRDY  - 2 (intr pin)

  CLOCK RATE
    f_CLKIN = 7.68 MHz
    τ_CLKIN = 130.2 ns */

/*______________________________________________________________________________________________________*/

//LIBRARY
  #include <SPI.h> //SPI comunication

//BAUD RATE
  #define bdrt_display 2000000

//ADC PINS
  #define RESET_PIN    4
  #define DRDY_PIN     2 
  #define CS_PIN       5 //!!fixed (do not change!)

/*________________________________________________________________________________________________________
DEFINE REGISTERS 
________________________________________________________________________________________________________*/

  #define STATUS  0x00 
  #define MUX     0x01 
  #define ADCON   0x02 
  #define DRATE   0x03 

  // STATUS 
  // ID   - ID   -  ID     -  ID     -  ORDER  -  ACAL   -  BUFEN  -  DRDY 
  #define STATUS_RST  0x01 

  // MUX 
  // PSEL3 - PSEL2 -  PSEL1  -  PSEL0  -  NSEL3  -  NSEL2   - NSEL1   - NSEL0 
  #define MUX_RST     0x01 
  #define Diff1       P_AIN0+N_AIN1
  #define Diff2       P_AIN2+N_AIN3
  #define Diff3       P_AIN4+N_AIN5
  #define Diff4       P_AIN6+N_AIN7    

  // ADCON
  // 0     - CLK1   -  CLK0   -  SDCS1  -  SDCS0  -  PGA2   -  PGA1   -  PAG0 
  #define ADCON_RST   0x20 
  #define PGA_1       B00100000  //±5V
  #define PGA_2       B00100001  //±2.5V
  #define PGA_4       B00100010  //±1.25V
  #define PGA_8       B00100011  //±0.625V
  #define PGA_16      B00100100  //±0.3125V
  #define PGA_32      B00100101  //±0.15625V    current 0.5µA
  #define PGA_64_2    B00100110  //±0.078125V   current 2µA
  #define PGA_64_10   B00100111  //±0.078125V   current 10µA

  // DRATE
  // DR7  - DR6    -  DR5    -  DR4    -  DR3    -  DR2    -  DR1    -  DR0 
  #define DRATE_RST   0xF0 
  #define DR_100      B10000010 
  #define DR_50       B01100011
  #define DR_10       B00100011

  /*______________________________________________________________________________________________________*/

  // SYSTEM CONTROL 
  #define WAKEUP      0x00  
  #define SYNC        0xFC  
  #define STANDBY     0xFD 
  #define RESET       0xFE 
  #define NOP         0xFF 
  // DATA READ    
  #define RDATA       0x01  
  #define RDATAC      0x03  
  #define SDATAC      0x0F 
  // READ REGISTER    
  #define RREG        0x10 
  #define WREG        0x50  
  // CALIBRATION    
  #define SYSGCAL     0xF4  
  #define SYSOCAL     0xF3  
  #define SELFGCAL    0xF2  
  #define SELFOCAL    0xF1  
  #define SELFCAL     0xF0  

/*________________________________________________________________________________________________________
PROGRAM
________________________________________________________________________________________________________*/

void setup() 
{
  SPI.begin();
  Serial.begin(bdrt_display);
  init_adc();
  ShowRegisterValues();
}

//Variables
int8_t pga = 1;

void loop() 
{ 
  float volt = dynamicVoltRead();
  Serial.print("PGA = ");
  Serial.print(pga);
  Serial.print("        ");
  Serial.println(volt, 6);
}

/*________________________________________________________________________________________________________
ADC FUNCTIONS
________________________________________________________________________________________________________*/

volatile int drdyState = false;  

void init_adc()
{
  pinMode(CS_PIN, OUTPUT);
  pinMode(DRDY_PIN, INPUT);
  pinMode(RESET_PIN, OUTPUT);

  reset_hard();
  delay(50);  
  attachInterrupt(digitalPinToInterrupt(DRDY_PIN), drdy_Interrupt, FALLING);
  delay(50);

  reset_soft();
  delay(100); 
  userConfig();
}

void userConfig() //user's register congiguration
{
  writeReg(MUX, Diff1); 
  writeReg(ADCON, PGA_1); 
  writeReg(DRATE, DR_50);
  delay(200);
  sendCmd(SELFCAL);  
  delay(200);
}

void ShowRegisterValues()
{
  Serial.println();  
  Serial.print("STATUS: ");
  Serial.println(readReg(STATUS));
  Serial.print("MUX:    ");
  Serial.println(readReg(MUX));
  Serial.print("ADCON:  ");
  Serial.println(readReg(ADCON));
  Serial.print("DRATE:  ");
  Serial.println(readReg(DRATE));  
}

long readReg(uint8_t regAdr) 
{
  uint8_t readRegBufr;
  digitalWrite(5, LOW);
  delayMicroseconds(10);
  SPI.transfer(RREG | regAdr); 
  SPI.transfer(0x00);             
  delayMicroseconds(10);
  readRegBufr = SPI.transfer(NOP);     
  delayMicroseconds(10);
  digitalWrite(5, HIGH);
  SPI.endTransaction();
  return readRegBufr;
}

void writeReg(uint8_t regAdr, uint8_t regVal) 
{
  uint8_t regValPre = readReg(regAdr);
  repeat:
  if (regVal != regValPre) 
  {
    drdyWait();
    SPI.beginTransaction(SPISettings(bdrt_display, MSBFIRST, SPI_MODE1)); 
    digitalWrite(5, LOW);
    delayMicroseconds(10);
    SPI.transfer(WREG | regAdr); 
    SPI.transfer(0x00);             
    SPI.transfer(regVal);
    delayMicroseconds(10);
    digitalWrite(5, HIGH);
    SPI.endTransaction();
  } 
  if (regVal != readReg(regAdr)) 
  { 
    goto repeat;
  }
  else 
  {
    // Serial.print("Register Write 0x");  
    // Serial.print(regAdr, HEX);
    // Serial.println(" successful.");
  }
  sendCmd(SELFCAL);
  sendCmd(SELFOCAL);
}

int32_t readAdc() 
{
  int32_t adc_val = 0;
  drdyWait(); 
  SPI.beginTransaction(SPISettings(bdrt_display, MSBFIRST, SPI_MODE1));
  digitalWrite(5, LOW);    
  delayMicroseconds(5);              
  SPI.transfer(RDATA);                 
  delayMicroseconds(10);

  adc_val |= SPI.transfer(NOP);
  delayMicroseconds(10);
  adc_val <<= 8;
  adc_val |= SPI.transfer(NOP);
  delayMicroseconds(10);
  adc_val <<= 8;
  adc_val |= SPI.transfer(NOP);
  delayMicroseconds(5);

  digitalWrite(5, HIGH);
  SPI.endTransaction();

  if (adc_val > 0x7fffff) //if MSB == 1
  { 
    adc_val = adc_val - 16777216; //2's complement
  }
  return adc_val;
}

void drdyWait() 
{
  while (drdyState) 
  {
    continue;
  }
  noInterrupts();
  drdyState = HIGH;
  interrupts();
}

void drdy_Interrupt()  
{
  drdyState = LOW;
}

void reset_soft() 
{
  SPI.beginTransaction(SPISettings(bdrt_display, MSBFIRST, SPI_MODE1)); 
  digitalWrite(5, LOW);
  delayMicroseconds(10);
  SPI.transfer(RESET);    
  delay(2);              
  SPI.transfer(SDATAC);  
  delayMicroseconds(100);
  digitalWrite(5, HIGH);
  SPI.endTransaction();
}

void reset_hard()
{
  digitalWrite(CS_PIN, LOW);
  digitalWrite(RESET_PIN, LOW);
  delay(10); 
  digitalWrite(RESET_PIN, HIGH);
}

void sendCmd(uint8_t directCmd) 
{
  drdyWait();
  SPI.beginTransaction(SPISettings(bdrt_display, MSBFIRST, SPI_MODE1)); 
  digitalWrite(5, LOW);
  delayMicroseconds(10);
  SPI.transfer(directCmd);
  delayMicroseconds(10);
  digitalWrite(5, HIGH);
  SPI.endTransaction();
  delay(50);
}

/*________________________________________________________________________________________________________
USER FUNCTIONS
________________________________________________________________________________________________________*/

float readVolt()
{
  float readVoltBufr = readAdc()*5 / (pow(2,pga-1)* ((1<<23)-1) ); //readAdc*ref / ((2^gain)*resolution)
  return readVoltBufr;
}

float dynamicVoltRead()
{
  writeReg(ADCON, PGA_1); //just for safety pga is set to default
  pga=1;  

  float dynamicVoltBufr = readVolt();

  if (abs(dynamicVoltBufr) < 0.05)
  {
    writeReg(ADCON, PGA_64_2);
    pga=7;
  }
  else if (abs(dynamicVoltBufr) < 0.1)
  {
    writeReg(ADCON, PGA_32);
    pga=6;
  }
  else if (abs(dynamicVoltBufr) < 0.2)
  {
    writeReg(ADCON, PGA_16);
    pga=5;
  }
  else if (abs(dynamicVoltBufr) < 0.5)
  {
    writeReg(ADCON, PGA_8);
    pga=4;
  }
  else if (abs(dynamicVoltBufr) < 1.0)
  {
    writeReg(ADCON, PGA_4);
    pga=3;
  }
  else if (abs(dynamicVoltBufr) < 2.0)
  {
    writeReg(ADCON, PGA_2);
    pga=2;
  }
  else {}

  dynamicVoltBufr = Alot();

  writeReg(ADCON, PGA_1); //set pga back to default
  return dynamicVoltBufr;
}

float Alot()
{
  int32_t timesBfr = 100;
  float SigmaVoltBfr = 0.;
  for(int32_t i=1; i<=timesBfr; i++)
  {
    SigmaVoltBfr=SigmaVoltBfr+readVolt();
  }
  SigmaVoltBfr=SigmaVoltBfr/timesBfr;
  return SigmaVoltBfr;
}

I've successfully implemented and tested my code and was interested in enhancing the ads1256 library by contributing additional features to make it even more robust. I'm a bit puzzled by your response, and I hope my intention is clear. I believe constructive collaboration can lead to the best possible outcome for the library. I hope you don't take offense; I'm just expressing my eagerness to contribute positively to the project.

CuriousScientist0 commented 7 months ago

First of all, the above code has nothing to do with my library, it is a totally different code. The purpose of my library is to implement all the functions of the ADS1256. No less, no more. Therefore, while your idea might be good, it is not necessary to be included in the library. Those who want this function can implement the logic on top of the library, directly in their Arduino code.

srisankaran commented 6 months ago

Do it, if you want, I won't do it for you.

Your comment comes across as unprofessional. I didn't intend to imply that I wanted additional features for my own purposes; rather, I simply believed it would be beneficial if such a feature existed. As you've indicated, the primary objective of the library is to fully implement all functions of the ADS1256 without compromise. I understand and respect that perspective. However, if this is the tone conveyed through this forum, I question its suitability for constructive dialogue.

Additionally, I want to clarify that I have written the above code without utilizing your library, so I am capable of implementing it independently. My intention was to share the idea so that others could also benefit from it.