Arduino Spectum Analyzer
Arduino Spectum Analyzer
//https://akuzechie.blogspot.com/2020/12/arduino-based-spectrum-analyzer.html
//https://www.youtube.com/watch?v=Ry77glD1Awo
//=========================
#include "fix_fft.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//------------------------------------------
Adafruit_SSD1306 display(128, 64, &Wire, 4);
//------------------------------------------
char im[128], data[128]; char x = 0, ylim = 60; int i = 0, val;
//==============================================================
void setup()
{
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC,0x3C);
display.setTextSize(1);
display.setTextColor(WHITE);
display.clearDisplay();
analogReference(DEFAULT);
}
//==============================================================
void loop()
{
int min=1024, max=0;
for (i=0; i <128; i++) //take 128 samples
{
val = analogRead(A0);
data[i] = val/4 - 128;
im[i] = 0;
if(val>max) max=val; //store max & min sample
if(val<min) min=val;
}
//------------------------------------------
fix_fft(data, im, 7, 0); //FFT on data
//------------------------------------------
display.clearDisplay();
for (i=1; i<64; i++) //bar graph sketch on OLED display
{
int dat = sqrt(data[i] * data[i] + im[i] * im[i]);
display.drawLine(i*2 + x, ylim, i*2 + x, ylim - dat, WHITE);
}
display.setCursor(0,0); display.print(" Spectrum Analyzer");
display.display();
}
//
//https://forum.arduino.cc/t/fast-sampling-adc/64877
//
The fastest you can sample is limited by the ADC clock, which can be as fast as the
main clock frequency divided by 2, or 8 MHz. Now, at that sampling frequency your
results will not be accurate! In addition, the datasheet says the ADC clock should
not exceed 1 MHz (I'm guessing results will just be ridiculously wrong beyond that
point). The "recommended" ADC clock frequency to meet datasheet parameters is 200
kHz or less (many of the ADC parameters are specified at 200 kHz). Another
parameter of note is the "input bandwidth", which has a typical specification of
38.5 kHz so (according to Nyquist's theorem) there's no information beyond 77 kHz
of sampling (though it is beneficial to sample faster than that if your signal
truly does have spectral content up to 38.5 kHz).
OK, so with that out of the way, let's say you settle on 200 kHz, that means you
are sampling data every 5 microseconds. That's way too fast for serial. At the
230.4 kbps baud rate and 8 bits per sample (throwing away the 2 LSB's of the 10-bit
sample) you could transmit 23040 samples per second (10 bits per 8-bit data packet
over serial) if your code was 100% efficient. So there's no need to even think
about the 200 kHz sampling frequency number.
Storing data has a similar tradeoff. Assuming you use an Arduino Mega with 8K of
RAM, set aside 1k for the C stack and other stuff, you could store (7*1024)/200,000
= 35.8 milliseconds worth of data at 200 kHz sampling rate. If you want to store 8
seconds using 7k of RAM you will have to sample at 896 Hz.
--
The Ruggeduino 7: compatible with Arduino UNO, 24V operation, all I/O's fused and
protected
//http://yaab-arduino.blogspot.com/2015/02/fast-sampling-from-analog-input.html?m=1
Arduino provides an convenient way to read analog input this using the analogRead()
function. Without going into much details the analogRead() function takes 100
microseconds leading to a theoretical sampling rate of 9600 Hz. You can read more
about this topic here.
The following piece of code takes 1000 samples using the analogRead() calculates
some statistics.
void setup()
{
Serial.begin(115200);
pinMode(A0, INPUT);
}
void loop()
{
long t0, t;
t0 = micros();
for(int i=0; i<1000; i++) {
analogRead(A0);
}
t = micros()-t0; // calculate elapsed time
This code gives 112us per sample for a 8928 Hz sampling rate.
We now need a little more details. The ADC clock is 16 MHz divided by a 'prescale
factor'. The prescale is set by default to 128 which leads to 16MHz/128 = 125 KHz
ADC clock. Since a conversion takes 13 ADC clocks, the default sample rate is about
9600 Hz (125KHz/13).
Adding few lines of code in the setup() function we can set an ADC prescale to 16
to have a clock of 1 MHz and a sample rate of 76.8KHz.
void setup()
{
sbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
cbi(ADCSRA, ADPS0);
...
The real frequency measured with the test program is 17us per sample for a 58.6 KHz
sampling rate.
The following table shows prescale values with registers values and theoretical
sample rates. Note that prescale values below 16 are not recommended because the
ADC clock is rated.
Prescale ADPS2 ADPS1 ADPS0 Clock freq (MHz) Sampling rate (KHz)
2 0 0 1 8
615
4 0 1 0 4
307
8 0 1 1 2
153
16 1 0 0 1
76.8
32 1 0 1 0.5
38.4
64 1 1 0 0.25 19.2
128 1 1 1 0.125 9.6
Interrupts
A better strategy is to avoid calling the analogRead() function and use the 'ADC
Free Running mode'. This is a mode in which the ADC continuously converts the input
and throws an interrupt at the end of each conversion. This approach has two major
advantages:
Do not waste time waiting for the next sample allowing to execute additional logic
in the loop function.
Improve accuracy of sampling reducing jitter.
In this new test program I set the prescale to 16 as the example above getting a
76.8 KHz sampling rate.
int numSamples=0;
long t, t0;
void setup()
{
Serial.begin(115200);
ISR(ADC_vect)
{
byte x = ADCH; // read 8 bit value from ADC
numSamples++;
}
void loop()
{
if (numSamples>=1000)
{
t = micros()-t0; // calculate elapsed time
// restart
t0 = micros();
numSamples=0;
}
}
If you want to learn more on ADC Free Running mode and tweaking ADC register you
can look at the following pages.