Teensy 4.0 + ADS256 - differential reading

frohr

Well-known member
Hi,
I have Teensy 4.0 and ADS1256 module. I read data from ADXL and send via USB to PC. But I have problem with lot of noise from USB. If I use USB isolator, it is OK.
But I am thinking about otehr possibility - differential reading. But I am not sure if it will be useful.

My code is:
Code:
#include <SPI.h>

// PIN assignements
#define CS_PIN    21
#define DRDY_PIN  22 
#define RST_PIN   8 

// SPI
#define SPISPEED 1950000               

// ADS1256 registers
#define STATUS_REG   0x00
#define MUX_REG      0x01
#define ADCON_REG    0x02
#define DRATE_REG    0x03

// ADS1256 commands
#define WAKEUP_CMD   0x00  // completes SYNC and exits standby mode
#define RDATA_CMD    0x01  // read data
#define RREG_CMD     0x10  // read register (register ID in low nibble)
#define WREG_CMD     0x50  // write register (register ID in low nibble)
#define SELFCAL_CMD  0xF0  // offset and gain self-calibration
#define SYNC_CMD     0xFC  // synchronize the A/D conversion
#define STANDBY_CMD  0xFD  // begin standby mode
#define RESET_CMD    0xFE  // reset to power-up values

#define VREF            (2.5)   // for conversion of raw ADC data to Volts
#define BUFFER_SIZE     (30000) // samples

#define DRATE_15K       (0xE0)  // 15 kSPS
#define DRATE_30K       (0xF0)  // 30 kSPS

#define STATUS_REG_0x01 (0x01)  // MSB first, Auto-Cal Dsbl, Input Buffer Dsbl
#define STATUS_REG_0x03 (0x03)  // MSB first, Auto-Cal Dsbl, Input Buffer Enbl
#define STATUS_REG_0x07 (0x07)  // MSB first, Auto-Cal Enbl, Input Buffer Enbl
#define ADCON_REG_VALUE (0x21)  // 0 01 00 001 => Clock Out Freq = fCLKIN, 
                                //             Sensor Detect OFF, gain 2 7.68MHz  
#define DRATE_REG_VALUE (BUFFER_SIZE==15000 ? DRATE_15K : DRATE_30K)  
#define MUX_REG_VALUE   (B00001000) // AINCOM

volatile int DRDY_state = HIGH;

float adc_volt[BUFFER_SIZE];
float adc_g[BUFFER_SIZE];
char inChar;

void writeRegister(uint8_t address, uint8_t value)
{
  SPI.transfer( WREG_CMD | address ); 
  SPI.transfer( 0x00 ); 
  SPI.transfer( value );
  delayMicroseconds( 100 );
}

void setup()
{ 
  Serial.begin( 9600 ); // USB data rate is always max
  while (!Serial) {} // wait for USB serial ready
  //Serial.println( "ADS1256 test program" );
  START_SPI();
  attachInterrupt( DRDY_PIN, DRDY_Interrupt, FALLING );
}

void loop()
{

  //uint32_t t0 = micros();  
  for (int i=0; i < BUFFER_SIZE; i++) {    
    waitDRDY();
    adc_volt[i] = READ_ADC(); 

  }
  //Serial.printf( "Read %1d samples in %1lu us\n", BUFFER_SIZE, micros()-t0 );
  if (Serial.available() > 0) {
    inChar = Serial.read();
    //START_SPI();
      if (inChar == 'r') { 
        for(int i=0; i<BUFFER_SIZE; i++) {
          byte *b = (byte *)&adc_volt[i];
          Serial.write(b[0]);
          Serial.write(b[1]);
          Serial.write(b[2]);
          Serial.write(b[3]);
          }
        }  
      if (inChar == 't') { 
        Serial.println("@");
      }
    }
  
}

float READ_ADC()
{
  int32_t adc_raw;   
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));                             
  digitalWriteFast(CS_PIN, LOW);    
   
  SPI.transfer( RDATA_CMD ); 
  delayMicroseconds(5); // delay MUST be >= 5 us      
  adc_raw = 0;    
  adc_raw |= SPI.transfer(0); 
  adc_raw <<= 8;                 
  adc_raw |= SPI.transfer(0); 
  adc_raw <<= 8;                
  adc_raw |= SPI.transfer(0);    
   
  digitalWriteFast(CS_PIN, HIGH);     
  SPI.endTransaction();  

  if (adc_raw & (1<<23)) {    // if 24-bit MSB == 1
    adc_raw |= 0xFF000000;    //   value is negative, sign-extend to 32 bits
  }
  float v = adc_raw * (float)(VREF / (1<<23));
  return( v );
}  

// DRDY falling-edge interrupt function
void DRDY_Interrupt()
{
  DRDY_state = LOW;
}
 
void waitDRDY() 
{
  // wait for DRDY_state to be LOW
  while (DRDY_state == HIGH) {
    continue;
  }
  // then set it back to HIGH
  DRDY_state = HIGH;
}

void START_SPI ()
{
  // configure I/O pins
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // init CS high (disable)
  pinMode(DRDY_PIN, INPUT);
  pinMode(RST_PIN, OUTPUT);
  
  // hardware reset ADS1256 by toggling pin LOW then HIGH
  digitalWrite(RST_PIN, LOW);
  delay(1); 
  digitalWrite(RST_PIN, HIGH); 
  delay(500); //500
  
  // start the spi-bus
  SPI.begin(); 
  delay(500); //500
  
  // wait for DRDY signal LOW
  while (digitalRead(DRDY_PIN)) {}
   
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); 
  digitalWriteFast(CS_PIN, LOW);
  delayMicroseconds(100);   //100

  // reset to power-up state
  SPI.transfer( RESET_CMD );
  delay(5);

  // configure registers
  writeRegister( STATUS_REG, STATUS_REG_0x03 );
  writeRegister( ADCON_REG, ADCON_REG_VALUE );
  writeRegister( DRATE_REG, DRATE_REG_VALUE );
  writeRegister( MUX_REG, MUX_REG_VALUE ); 

  // auto-calibrate (send command, wait DRDY = LOW, then wait DRDY = HIGH)
  SPI.transfer( SELFCAL_CMD );
  uint32_t t0 = micros();
  while (digitalReadFast(DRDY_PIN)!=LOW && micros()-t0 < 10000) {}
  while (digitalReadFast(DRDY_PIN)!=HIGH && micros()-t0 < 10000) {}
  
  digitalWriteFast(CS_PIN, HIGH);
  SPI.endTransaction(); 
}



if I change to this, will it work? I know, I can try it, but it is not possible now and I will be ready.


Old:
Code:
#define MUX_REG_VALUE   (B00001000) // AINCOM

New:
Code:
#define MUX_REG_VALUE   (B00000001) // AIN0-AIN1

And change float READ_ADC()

Code:
float READ_ADC()
{
  int32_t adc_raw;   
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));                             
  digitalWriteFast(CS_PIN, LOW);    
   
  SPI.transfer( RDATA_CMD ); 
  delayMicroseconds(5); // delay MUST be >= 5 us      
  adc_raw = 0;    
  adc_raw |= SPI.transfer(0); 
  adc_raw <<= 8;                 
  adc_raw |= SPI.transfer(0); 
  adc_raw <<= 8;                
  adc_raw |= SPI.transfer(0);    
   
  digitalWriteFast(CS_PIN, HIGH);     
  SPI.endTransaction();  

  if (adc_raw & (1<<23)) {    // if 24-bit MSB == 1
    adc_raw |= 0xFF000000;    //   value is negative, sign-extend to 32 bits
  }
  float v = adc_raw * (float)(VREF / (1<<23));
  float vd = v - (VREF/2.0);
  return( vd );
}

Will it work if I connect to AIN0 and AIN1 instead AIN0 and AINCOM?

Thanks!
 
if I change to this, will it work? I know, I can try it, but it is not possible now and I will be ready.

old #define MUX_REG_VALUE (B00001000) // AINCOM
new #define MUX_REG_VALUE (B00000001) // AIN0-AIN1

Your change to MUX_REG_VALUE looks correct. Can you explain the change you made to READ_ADC()? I don't understand the added line

Code:
  float vd = v - (VREF/2.0);

Maybe I don't understand what you will connect to pins AIN0 and AIN1.
 
Your change to MUX_REG_VALUE looks correct. Can you explain the change you made to READ_ADC()? I don't understand the added line

Code:
  float vd = v - (VREF/2.0);

Maybe I don't understand what you will connect to pins AIN0 and AIN1.

Now ADXL is connected Vout to AIN0 and gnd to INCOM.
For differential reading I will have connected gnd and Vout to AIN0 a AIN1. Or am I wrong and this line is not necessarry for differential reading?
 
I tried today connect adxl VDD to 3.3V, Vou to AIN0 and gnd to AIN1. I changed code as above but I see just sine wave 50Hz.
I am thinking if is it right wiring.? Is ADXL1005 eval board suitable for differential reading?
 
I tried today connect adxl VDD to 3.3V, Vou to AIN0 and gnd to AIN1. I changed code as above but I see just sine wave 50Hz.
I am thinking if is it right wiring.? Is ADXL1005 eval board suitable for differential reading?

Is the ADXL moving? If not, try rotating it so that it records acceleration of gravity.
 
Yes, I have testing rig for vibrations - adxl is mounted on it. If is connected Vou to AIN0 and gnd to AINCOM, its OK (not differential with noise). But When I connect Vou to AIN0 and gnd to AIN1, I can see just sine wave 50Hz and is not changing. I do not understand why result is the same for #define MUX_REG_VALUE (B00001000) and for #define MUX_REG_VALUE (B00000001) - result is the same for both wiring.
 
Yes, I have testing rig for vibrations - adxl is mounted on it. If is connected Vou to AIN0 and gnd to AINCOM, its OK (not differential with noise). But When I connect Vou to AIN0 and gnd to AIN1, I can see just sine wave 50Hz and is not changing. I do not understand why result is the same for #define MUX_REG_VALUE (B00001000) and for #define MUX_REG_VALUE (B00000001) - result is the same for both wiring.

Well, I still have the ADS1256 from the last time you were asking questions, so when I have a chance, I will try it, but it won't be today. My advice is provide a constant voltage to AIN1 on the ADS for your testing. It will be much easier to see what's going on if you know what voltage to expect. Once you can read a constant voltage between AIN0/AIN1, then use a function generator and measure a sine wave. When you are getting correct results for both of those cases, then you can move on to testing with the ADXL and have some confidence in your results.
 
great, thank you so much for help. The code from last time works very well, only noise make difficulties. Let's see if differential reading will lower the noise.
 
just for information, this is my scheme, wiring Teensy 4.0 and ads1256. Adxl is connected to JP3 and 3V3.

scheme.jpg
 
Starting from the program we did the last time, I changed just the value written to the MUX register from 0x08 (AIN0-AINCOM) to 0x01 (AIN0-AIN1), no other changes, and I get good results. I am using my function generator to produce a 60-Hz sine wave with amplitude 2.5V and offset 1.25V, so 0-2.5Vpp. I connect the signal/ground of the signal generator to AIN0/AIN1 and also jumper AIN1 to AINCOM, and I get a nice 60-Hz sine wave, sampling at 30 kHz. On your board, that would be like connecting your function generator signal/ground to JP3 pins 1/3, but you should also jumper pin 3 to pin 2 (AIN0 COM), or you can just connect AIN0 COM to your GND.

Code:
#include <SPI.h>

// PIN assignments
#define CS_PIN    21
#define DRDY_PIN  22 
#define RST_PIN   8 

// SPI
#define SPISPEED 1950000               

// ADS1256 registers
#define STATUS_REG   0x00
#define MUX_REG      0x01
#define ADCON_REG    0x02
#define DRATE_REG    0x03

// ADS1256 commands
#define WAKEUP_CMD   0x00  // completes SYNC and exits standby mode
#define RDATA_CMD    0x01  // read data
#define RREG_CMD     0x10  // read register (register ID in low nibble)
#define WREG_CMD     0x50  // write register (register ID in low nibble)
#define SELFCAL_CMD  0xF0  // offset and gain self-calibration
#define SYNC_CMD     0xFC  // synchronize the A/D conversion
#define STANDBY_CMD  0xFD  // begin standby mode
#define RESET_CMD    0xFE  // reset to power-up values

#define VREF            (2.5)   // for conversion of raw ADC data to Volts
#define BUFFER_SIZE     (30000) // samples

#define DRATE_15K       (0xE0)  // 15 kSPS
#define DRATE_30K       (0xF0)  // 30 kSPS

#define STATUS_REG_0x01 (0x01)  // MSB first, Auto-Cal Dsbl, Input Buffer Dsbl
#define STATUS_REG_0x03 (0x03)  // MSB first, Auto-Cal Dsbl, Input Buffer Enbl
#define STATUS_REG_0x07 (0x07)  // MSB first, Auto-Cal Enbl, Input Buffer Enbl
#define ADCON_REG_VALUE (0x21)  // 0 01 00 001 => Clock Out Freq = fCLKIN, 
                                //             Sensor Detect OFF, gain 2 7.68MHz  
#define DRATE_REG_VALUE (BUFFER_SIZE==15000 ? DRATE_15K : DRATE_30K)  
//#define MUX_REG_VALUE   (0x08)  // AIN0 - AINCOM
#define MUX_REG_VALUE   (0x01)  // AIN0 - AIN1

volatile int DRDY_state = HIGH;

float adc_volt[BUFFER_SIZE];
float adc_g[BUFFER_SIZE];

void writeRegister(uint8_t address, uint8_t value)
{
  SPI.transfer( WREG_CMD | address ); 
  SPI.transfer( 0x00 ); 
  SPI.transfer( value );
  delayMicroseconds( 100 );
}

void setup()
{ 
  Serial.begin( 9600 ); // USB data rate is always max
  while (!Serial) {} // wait for USB serial ready
  Serial.println( "ADS1256 test program" );
  START_SPI();
  attachInterrupt( DRDY_PIN, DRDY_Interrupt, FALLING );
}

void loop()
{
  uint32_t t0 = micros();  
  for (int i=0; i < BUFFER_SIZE; i++) {    
    waitDRDY();
    adc_volt[i] = READ_ADC(); 
  }
  Serial.printf( "Read %1d samples in %1lu us\n", BUFFER_SIZE, micros()-t0 );
  for (int j=0; j<16; j++)
    Serial.printf( "%5.2f ", adc_volt[(30000/60 +8)/16 * j] );
  Serial.println();

  if (Serial.available() > 0) {
    char inChar = Serial.read();
    for (int i=0; i<BUFFER_SIZE; i++) {
      #if (1)
      Serial.printf( "%5d  %8.3f\n", i, adc_volt[i] );
      #else
      byte *b = (byte *)&adc_volt[i];
      Serial.write(b[0]);
      Serial.write(b[1]);
      Serial.write(b[2]);
      Serial.write(b[3]);
      #endif
    }
  }
}

float READ_ADC()
{
  int32_t adc_raw;   
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));                             
  digitalWriteFast(CS_PIN, LOW);    
   
  SPI.transfer( RDATA_CMD ); 
  delayMicroseconds(5); // delay MUST be >= 5 us      
  adc_raw = 0;    
  adc_raw |= SPI.transfer(0); 
  adc_raw <<= 8;                 
  adc_raw |= SPI.transfer(0); 
  adc_raw <<= 8;                
  adc_raw |= SPI.transfer(0);    
   
  digitalWriteFast(CS_PIN, HIGH);     
  SPI.endTransaction();  

  if (adc_raw & (1<<23)) {    // if 24-bit MSB == 1
    adc_raw |= 0xFF000000;    //   value is negative, sign-extend to 32 bits
  }
  float v = adc_raw * (float)(VREF / (1<<23));
  return( v );
}  

// DRDY falling-edge interrupt function
void DRDY_Interrupt()
{
  DRDY_state = LOW;
}
 
void waitDRDY() 
{
  // wait for DRDY_state to be LOW
  while (DRDY_state == HIGH) {
    continue;
  }
  // then set it back to HIGH
  DRDY_state = HIGH;
}

void START_SPI ()
{
  // configure I/O pins
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // init CS high (disable)
  pinMode(DRDY_PIN, INPUT);
  pinMode(RST_PIN, OUTPUT);
  
  // hardware reset ADS1256 by toggling pin LOW then HIGH
  digitalWrite(RST_PIN, LOW);
  delay(1); 
  digitalWrite(RST_PIN, HIGH); 
  delay(500);
  
  // start the spi-bus
  SPI.begin(); 
  delay(500);
  
  // wait for DRDY signal LOW
  while (digitalRead(DRDY_PIN)) {}
   
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); 
  digitalWriteFast(CS_PIN, LOW);
  delayMicroseconds(100);   

  // reset to power-up state
  SPI.transfer( RESET_CMD );
  delay(5);

  // configure registers
  writeRegister( STATUS_REG, STATUS_REG_0x03 );
  writeRegister( ADCON_REG,  ADCON_REG_VALUE );
  writeRegister( DRATE_REG,  DRATE_REG_VALUE );
  writeRegister( MUX_REG,    MUX_REG_VALUE   ); 

  // auto-calibrate (send command, wait DRDY = LOW, then wait DRDY = HIGH)
  SPI.transfer( SELFCAL_CMD );
  uint32_t t0 = micros();
  while (digitalReadFast(DRDY_PIN)!=LOW && micros()-t0 < 10000) {}
  while (digitalReadFast(DRDY_PIN)!=HIGH && micros()-t0 < 10000) {}
  
  digitalWriteFast(CS_PIN, HIGH);
  SPI.endTransaction(); 
}
 
Thank you Joe. I was thinking that differential reading will help lower noise. But seems it is the same as AIN0 and AINCOM.
There is huge noise from USB to ADXL/ADS1256. I have some usb filter and it helps but it means more devices... But it seems there is no other way.

This is if I connect Teensy directly to laptop USB:
View attachment 30524

And this is with USB filter:
View attachment 30525

You can see, noise without usb filter is 10x higher.

This is connection with USB filter:

View attachment 30526
 
Back
Top