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);
}
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);*/
}