ADC does not change value on interrupt and gets stuck on while

Hello guys.

I am using adc on free running mode with an interrupt to add the values to a vector, which will then be passed to a Fast Fourier Transform, that can be treated here as an arbitrary function.

When running, however, the code does not reach the Serial.println("Starting FFT"); line, only printing until "Done converting" on the ISR, which means it's stuck on the while(!stop).

If i change that while for a delay(100), things get weird too because all the readings become 1023.0

What is happening?

#include <arduinoFFT.h>

#define micPin A0
#define samples 128

double* vReal;
double* vImag;
volatile uint8_t i;
bool stop;

arduinoFFT FFT;

ISR(ADC_vect){
 vReal[i++] = (double)((ADCL << 8) | ADCH);
 if (i >= samples){
   /* TURN OFF ADC */
   ADCSRA = 0;  
   Serial.println("Done converting");
   stop = true;
 }
}

void setup() {
 vReal = (double*) malloc(samples*sizeof(double));
 vImag = (double*) malloc(samples*sizeof(double));

 ADCSRA = 0; 

 Serial.begin(230400);
 Serial.println("Ready!");
}

void loop() {
 for(uint8_t j = 0; j <= samples; j++)
   vImag[j] = 0.0;

 i = 0;
 stop = false;

 ADMUX = (1 << 6) | ((micPin-14) & 0x07);	
 ADCSRB = 0b00000000;
 /*         RARRRAAA
             C   DDD
             M   TTT
             E   SSS
                 012
 ACME = ?
 ADTS = TRIGGER SOURCE (000 = FREE RUNNING MODE)    */
 Serial.println("Start converting");
 ADCSRA = 0b11101111;
 /*         AAAAAAAA
            DDDDDDDD
            ESAIIPPP
            NCTFESSS
              E  012
 ADEN = ENABLE
 ADSC = SINGLE CONVERSION (MUST BE SET ON FIRST FREE-RUNNING CONVERSION)
 ADATE = AUTO TRIGGER ENABLE (TRIGGER ON SOURCE GIVEN BY ADCSRB'S ADTS BITS)
 ADIF = INTERRUPT FLAG
 ADIE = INTERRUPT ENABLE
 ADPS[2:0] = PRESCALER (111 DIVIDES CLOCK SOURCE BY 128 -> 16M/128 = 125KHZ)  */

 /* WAITS FOR CONVERSIONS TO COMPLETE*/
 while(!stop) {}

 Serial.println("Starting FFT");
 FFT.Compute(vReal, vImag, samples, FFT_FORWARD); 
 FFT.ComplexToMagnitude(vReal, vImag, samples);
 double freq = FFT.MajorPeak(vReal, samples, 8900);
 Serial.print("Signal frequency: ");
 Serial.println(freq);
}
1 Like

I can't see anything inside the while(!stop) loop to change the value of the stop variable? So it'll stay in that loop forever.

stop and vReal should be declared volatile. (see touch1337 comment to see why you need volatile for stop)

Generally not a good idea to use print in an ISR.

Don't do the integer to float conversion in the ISR, use an integer to accumulate the ADC readings, do the heavy math outside the ISR.

some serial prints, added for troubleshooting and removed after the issue has been found could lead to some understandings.

ISR(ADC_vect){
 vReal[i++] = (double)((ADCL << 8) | ADCH);
Serial.print ( "samples= " );
Serial.print( samples );// <<<<<<<<<<<<<<<<<<<<<<<<might reveal why stop does not become true
Serial.print ( " i = " );
Serial.print( i );
Serial.println();
 if (i >= samples){
   /* TURN OFF ADC */
   ADCSRA = 0;  
   Serial.println("Done converting");
   stop = true;
 }
}

Thanks for posting the properly formatted code in code tags.

perhaps adding

ISR(ADC_vect){
 vReal[i++] = (double)((ADCL << 8) | ADCH);
Serial.print ( "samples= " );
Serial.print( samples );// <<<<<<<<<<<<<<<<<<<<<<<<might reveal why stop does not become true
Serial.print ( " i = " );
Serial.print( i );
Serial.println();
i++; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 if (i >= samples){
   /* TURN OFF ADC */
   ADCSRA = 0;  
   Serial.println("Done converting");
   stop = true;
 }
}

might make it work.

The reason for this is:
An ISR should run through as fast as possible

The compiler does not report an error if you do Serial-printing inside the ISR.
But serial printing needs time. It should be done always outside the ISR

If you wish to print only once per change of the value
this can be coded with state-change-detection

Using volatile helped, on the stop, thank you very much!

I took the prints out of the ISR too.

It seems to be working, except that now the readings are stuck on the first reading the ADC took since the system was powered on. (tends to be around 414, but can go to 200ish when I disturb the input).

#include <Arduino.h>
#include <arduinoFFT.h>

#define micPin A0
#define samples 128

volatile double vReal[samples];
volatile double vImag[samples];
volatile uint8_t i;
volatile bool stop;

arduinoFFT FFT;

ISR(ADC_vect){
  vReal[i++] = (double)((ADCH << 8) | ADCL);
  if (i >= samples){
    /* TURN OFF ADC */
    ADCSRA = 0;  
    stop = true;
  }
}

void setup() {

  ADCSRA = 0; 

  Serial.begin(230400);
  Serial.println("Ready!");
}

void loop() {
  for(uint8_t j = 0; j <= samples; j++)
    vImag[j] = 0.0;

  i = 0;
  stop = false;

	ADMUX = (1 << 6) | ((micPin-14) & 0x07);	
  ADCSRB = 0b00000000;
  /*         RARRRAAA
              C   DDD
              M   TTT
              E   SSS
                  012
  ACME = ?
  ADTS = TRIGGER SOURCE (000 = FREE RUNNING MODE)    */
  Serial.println("Start converting");
  ADCSRA = 0b11101111;
  /*         AAAAAAAA
             DDDDDDDD
             ESAIIPPP
             NCTFESSS
               E  012
  ADEN = ENABLE
  ADSC = SINGLE CONVERSION (MUST BE SET ON FIRST FREE-RUNNING CONVERSION)
  ADATE = AUTO TRIGGER ENABLE (TRIGGER ON SOURCE GIVEN BY ADCSRB'S ADTS BITS)
  ADIF = INTERRUPT FLAG
  ADIE = INTERRUPT ENABLE
  ADPS[2:0] = PRESCALER (111 DIVIDES CLOCK SOURCE BY 128 -> 16M/128 = 125KHZ)  */

  /* WAITS FOR CONVERSIONS TO COMPLETE*/
  while (!stop);

  Serial.println("Done converting");  

  for (int j = 0; j < samples; j++){
    Serial.print(j);
    Serial.print(" : ");
    Serial.println(vReal[j]);
  }
  /*
  Serial.println("Starting FFT");
  FFT.Compute(vReal, vImag, samples, FFT_FORWARD); 
  FFT.ComplexToMagnitude(vReal, vImag, samples);
  double freq = FFT.MajorPeak(vReal, samples, 8900);
  Serial.print("Signal frequency: ");
  Serial.println(freq);*/
}

guys i figured out the problem, it's the conversion to double right up there in the ISR. No idea of what causes it tho...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.