Your algorithm for correcting the DC Offset is the problem here. You should NOT calculate the sum and correct the offset inside READ_ADC()!so updated with your suggestion and changed little bit program but still no success
Code:#include <SPI.h> #include <arduinoFFT.h> // PIN assignments #define CS_PIN 14 #define DRDY_PIN 15 #define RST_PIN 16 // SPI #define SPISPEED 2500000 #define SAMPLES 1024 #define SAMPLING_FREQUENCY 1000 arduinoFFT FFT = arduinoFFT(); #define BUFFER_SIZE 30000 unsigned int sampling_period_us; unsigned long microseconds = 0; double vReal[SAMPLES]; double vImag[SAMPLES]; float dataWithoutOffset; float dc_offset = 0.0; // 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 DRATE_30K (0xF0) // 30 kSPS #define STATUS_REG_0x03 (0x03) // MSB first, Auto-Cal Dsbl, 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 (DRATE_30K) #define MUX_REG_VALUE (B00001000) // AINCOM volatile int DRDY_state = HIGH; float adc_volt[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(115200); while (!Serial) {} Serial.println("ADS1256 test program"); START_SPI(); attachInterrupt(DRDY_PIN, DRDY_Interrupt, FALLING); } void loop() { int sampleCount = 0; while (true) { uint32_t t0 = micros(); for (int i = 0; i < BUFFER_SIZE; i++) { waitDRDY(); adc_volt[i] = READ_ADC(); delayMicroseconds(10); sampleCount++; if (sampleCount >= BUFFER_SIZE) { sampleCount = 0; break; // Exit the loop after completing the required samples } } fft(adc_volt); // Call fft function with the adc_volt array Serial.println("Cycle completed"); } } void fft(float* samples) { for (int i = 0; i < SAMPLES; i++) { microseconds = micros(); vReal[i] = samples[i]; vImag[i] = 0.0; while (micros() < (microseconds + sampling_period_us)) { } } FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY); Serial.print("Peak frequency is: "); Serial.println(peak); } 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)) { adc_raw |= 0xFF000000; } float sum = 0.0; float v = adc_raw * (float)(VREF / (1 << 2 [/QUOTE] [QUOTE="Lavanya rajan, post: 338851, member: 91221"] sum += v; // Compute mean (DC offset) float mean = sum / BUFFER_SIZE; // Subtract mean from each sample to remove DC offset for (int i = 0; i < BUFFER_SIZE; i++) { v -= mean; return(v ); } } void DRDY_Interrupt() { DRDY_state = LOW; } void waitDRDY() { while (DRDY_state == HIGH) { continue; } DRDY_state = HIGH; } void START_SPI() { /// removed as not necessary for understanding offset correction }
Use READ_ADC() to collect raw data.for (int i = 0; i < SAMPLES; i++)
Sry for the late reply, held up with some work.Lavanya, what do you think is the time in between adc samples??
Try to print/plot the time domain samples before you compute the FFT. Then you will see what’s wrong I think…
#include <SPI.h>
#include <arduinoFFT.h>
// PIN assignements
#define CS_PIN 14
#define DRDY_PIN 15
#define RST_PIN 16
// SPI
#define SPISPEED 2500000
#define SAMPLING_FREQUENCY 1000
#define SAMPLES 1024
arduinoFFT FFT = arduinoFFT();
#define BUFFER_SIZE (30000)
unsigned int sampling_period_us;
double vReal[BUFFER_SIZE];
double vImag[BUFFER_SIZE];
// 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 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 (DRATE_30K)
#define MUX_REG_VALUE (B00001000) // AINCOM
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(115200); // 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()
{
int sampleCount = 0;
while (true) {
uint32_t t0 = micros();
for (int i = 0; i < BUFFER_SIZE; i++) {
waitDRDY();
adc_volt[i] = READ_ADC();
delayMicroseconds(10); // Adjust the delay as needed
sampleCount++;
if (sampleCount >= BUFFER_SIZE) {
sampleCount = 0;
Serial.println("Cycle completed");
}
}
Serial.printf("Read %1d samples in %1lu us\n", BUFFER_SIZE, micros() - t0);
for (int i = 0; i < BUFFER_SIZE; i++) {
Serial.println(adc_volt[i], 4); // Print with four decimal places
}
}
}
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
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY));
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();
}
ADS1256 test program |
Cycle completed |
Read 30000 samples in 999884 us |
actually i just want to collect 30k samples , remove dc offset (if any) and plot fft for the same..I think your original plan was to sample with 1000 Hz, and then do a FFT over 1024 samples, so that you get a frequency spectrum with 512 lines. Those lines correspond to 0 to 500 Hz, in steps of 1000/1024 Hz. The line that you wanted to see was i think ~155 Hz or so.
What you have is something that samples at 33 kHz. If you take 1024 of those samples and then do fft then you will get 512 lines, but now from 0 to 16.5 kHz, in steps of 32 Hz. The frequency that you wanted to see would be line #4 or 5.
This is not the resolution you want is it?
If you want to sample with 1 ms, then sample with 1000 us…
The way you had it was first sample at 33 kHz, and then copy the array of 1024 samples with a carefully managed 1000 us delay in between copying each sample from the original to the new array. That does not change the rate at which the ADC sampled before I’m aftrai
#include <SPI.h>
#include <arduinoFFT.h>
#define CS_PIN 14
#define DRDY_PIN 15
#define RST_PIN 16
#define SPISPEED 2500000
#define SAMPLING_FREQUENCY 4000
#define SAMPLES 4096
arduinoFFT FFT = arduinoFFT();
unsigned long microseconds = 0;
#define BUFFER_SIZE (30000)
unsigned int sampling_period_us;
double vReal[SAMPLES];
double vImag[SAMPLES];
#define STATUS_REG 0x00
#define MUX_REG 0x01
#define ADCON_REG 0x02
#define DRATE_REG 0x03
#define WAKEUP_CMD 0x00
#define RDATA_CMD 0x01
#define RREG_CMD 0x10
#define WREG_CMD 0x50
#define SELFCAL_CMD 0xF0
#define SYNC_CMD 0xFC
#define STANDBY_CMD 0xFD
#define RESET_CMD 0xFE
#define VREF (2.5)
#define DRATE_30K (0xF0)
#define STATUS_REG_0x03 (0x03)
#define ADCON_REG_VALUE (0x21)
#define DRATE_REG_VALUE (DRATE_30K)
#define MUX_REG_VALUE (B00001000)
volatile int DRDY_state = HIGH;
float adc_volt[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(115200);
while (!Serial) {}
Serial.println( "ADS1256 test program" );
START_SPI();
attachInterrupt( DRDY_PIN, DRDY_Interrupt, FALLING );
}
void loop()
{
int sampleCount = 0;
float meanValue = 0.0;
// Collect data and compute mean
while (true) {
uint32_t t0 = micros();
for (int i = 0; i < BUFFER_SIZE; i++) {
waitDRDY();
adc_volt[i] = READ_ADC();
meanValue += adc_volt[i];
delayMicroseconds(10);
sampleCount++;
if (sampleCount >= BUFFER_SIZE) {
meanValue /= BUFFER_SIZE;
sampleCount = 0;
//Serial.println("Cycle completed");
}
}
//Serial.printf("Read %1d samples in %1lu us\n", BUFFER_SIZE, micros() - t0);
// Subtract mean from each data value
for (int i = 0; i < BUFFER_SIZE; i++) {
adc_volt[i] -= meanValue;
fft(adc_volt);
}
// Output adjusted values (optional)
//Serial.println("Adjusted Values:");
for (int i = 0; i < BUFFER_SIZE; i++) {
Serial.println(adc_volt[i], 3);
}
}
}
void fft(float* samples)
{
for (int i = 0; i < SAMPLES; i++)
{
microseconds = micros();
vReal[i] = adc_volt[i] ;
vImag[i] = 0.0;
while (micros() < (microseconds + sampling_period_us)) {
}
}
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
Serial.print("Peak frequency is: ");
Serial.println(peak);
}
float READ_ADC()
{
int32_t adc_raw;
SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));
digitalWriteFast(CS_PIN, LOW);
SPI.transfer( RDATA_CMD );
delayMicroseconds(5);
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)) {
adc_raw |= 0xFF000000;
}
float v = adc_raw * (float)(VREF / (1<<23));
return( v );
}
void DRDY_Interrupt()
{
DRDY_state = LOW;
}
void waitDRDY()
{
while (DRDY_state == HIGH) {
continue;
}
DRDY_state = HIGH;
}
void START_SPI ()
{
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY));
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
pinMode(DRDY_PIN, INPUT);
pinMode(RST_PIN, OUTPUT);
digitalWrite(RST_PIN, LOW);
delay(1);
digitalWrite(RST_PIN, HIGH);
delay(500);
SPI.begin();
delay(500);
while (digitalRead(DRDY_PIN)) {}
SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));
digitalWriteFast(CS_PIN, LOW);
delayMicroseconds(100);
SPI.transfer( RESET_CMD );
delay(5);
writeRegister( STATUS_REG, STATUS_REG_0x03 );
writeRegister( ADCON_REG, ADCON_REG_VALUE );
writeRegister( DRATE_REG, DRATE_REG_VALUE );
writeRegister( MUX_REG, MUX_REG_VALUE );
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();
}
uint32_t start = 0;
for( i = start; i<(BUFFER_SIZE-SAMPLES/2); i+= SAMPLES/2){
// now put data into vReal and vImag
for(j= 0; j<SAMPLES; j++){ // select 4K samples in sliding window
vReal[j+start] = adc_volt[i] ;
vImag[j+start] = 0.0;
//no reason to delay here---the samples are already collected!
} // end of for(j= 0 ... 4K samples ready----each set overlaps previous by 2048 samples
// now you're ready to compute the FFT
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
Serial.print("Peak frequency is: ");
Serial.println(peak);
} // end of for(i= start....
I shouldn't do these things late at night!It looks like you are calling your fft() function 30,000 times, but you only use the values from 0 to SAMPLES each time! You don't seem to ever do the FFT over the rest of the sample.
1. Subtract the mean as you are doing in your loop. DO NOT call your fft() function at that time.
2. In a new loop compute the FFT and advance by SAMPLES/2 each time;
Code:uint32_t start = 0; for( i = start; i<(BUFFER_SIZE-SAMPLES/2); i+= SAMPLES/2){ // now put data into vReal and vImag for(j= 0; j<SAMPLES; j++){ // select 4K samples in sliding window vReal[j+start] = adc_volt[i] ; vImag[j+start] = 0.0; //no reason to delay here---the samples are already collected! } // end of for(j= 0 ... 4K samples ready----each set overlaps previous by 2048 samples // now you're ready to compute the FFT FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY); Serial.print("Peak frequency is: "); Serial.println(peak); } // end of for(i= start....
You should probably increase BUFFER_SIZE to 32768 so that it is a multiple of your sample size.