Using of ADC registers on ATmega4809 (Uno Wi-Fi Rev2 board)

Hi!
I want to create an audio specter analyzer with information output to a serial port (not to LED board) for testing work of an audio device. So I bought MAX9814 audio sensor and connected it to arduino. After I used arduinoFFT.h library and chose third example of library.

#include "arduinoFFT.h"

arduinoFFT FFT = arduinoFFT(); /* Create FFT object */
/*
These values can be changed in order to evaluate the functions
*/
#define CHANNEL A0
const uint16_t samples = 64; //This value MUST ALWAYS be a power of 2
const double samplingFrequency = 100; //Hz, must be less than 10000 due to ADC

unsigned int sampling_period_us;
unsigned long microseconds;

/*
These are the input and output vectors
Input vectors receive computed results from FFT
*/
double vReal[samples];
double vImag[samples];

#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03

void setup()
{
  sampling_period_us = round(1000000*(1.0/samplingFrequency));
  Serial.begin(115200);
  while(!Serial);
  Serial.println("Ready");
}

void loop()
{
  /*SAMPLING*/
  microseconds = micros();
  for(int i=0; i<samples; i++)
  {
      vReal[i] = analogRead(CHANNEL);
      vImag[i] = 0;
      while(micros() - microseconds < sampling_period_us){
        //empty loop
      }
      microseconds += sampling_period_us;
  }
  /* Print the results of the sampling according to time */
  Serial.println("Data:");
  PrintVector(vReal, samples, SCL_TIME);
  FFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);	/* Weigh data */
  Serial.println("Weighed data:");
  PrintVector(vReal, samples, SCL_TIME);
  FFT.Compute(vReal, vImag, samples, FFT_FORWARD); /* Compute FFT */
  Serial.println("Computed Real values:");
  PrintVector(vReal, samples, SCL_INDEX);
  Serial.println("Computed Imaginary values:");
  PrintVector(vImag, samples, SCL_INDEX);
  FFT.ComplexToMagnitude(vReal, vImag, samples); /* Compute magnitudes */
  Serial.println("Computed magnitudes:");
  PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
  double x = FFT.MajorPeak(vReal, samples, samplingFrequency);
  Serial.println(x, 6); //Print out what frequency is the most dominant.
  while(1); /* Run Once */
  // delay(2000); /* Repeat after delay */
}

void PrintVector(double *vData, uint16_t bufferSize, uint8_t scaleType)
{
  for (uint16_t i = 0; i < bufferSize; i++)
  {
    double abscissa;
    /* Print abscissa value */
    switch (scaleType)
    {
      case SCL_INDEX:
        abscissa = (i * 1.0);
	break;
      case SCL_TIME:
        abscissa = ((i * 1.0) / samplingFrequency);
	break;
      case SCL_FREQUENCY:
        abscissa = ((i * 1.0 * samplingFrequency) / samples);
	break;
    }
    Serial.print(abscissa, 6);
    if(scaleType==SCL_FREQUENCY)
      Serial.print("Hz");
    Serial.print(" ");
    Serial.println(vData[i], 4);
  }
  Serial.println();
}

So, I get a frequency response function. The next step to increase sampling rate, because analogRead() function has sampling rate 9,6 KHz, so with using FFT analogRead can analyze frequencies up to 5Khz.
I found this method to increase sampling rate via registers

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void setup()
{
  sbi(ADCSRA, ADPS2);
  cbi(ADCSRA, ADPS1);
  cbi(ADCSRA, ADPS0);

But the code didn’t compile. I check the data sheet, and it turned out that there are no such registers on my board. I tried to use registers in data sheet first, second and I tried to add code with control registers

ADC0.CTRLC |= ADC_PRESC_DIV32_gc;
ADC0.CTRLA |= ADC_ENABLE_bm;

it’s compiled, but converted data still was wrong. I tried to add
ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc;
to address command to pin A0, but pin A0 doesn’t have ADC0 number (page 18 on the first data sheet), so can i increase sample rate for A0 pin on my UNO Wi-Fi? Or I need to buy new arduino (if so, tell me please which one is better to choose)? Or something like that link.
Also, if I can change sample frequency, can I use analogRead after this?
I understand little about the work of registers and have known arduino not so long ago, so describe your answers more detailed. Thanks in advance, I’m glad for any help.

Here’s a little function that should work. The original ADC register setting is restored after each use.

int analogReadFast(int ADCpin) {
  byte ADCregOriginal = ADC0_CTRLC;
  ADC0_CTRLC = 0x54; // reduced cap, Vdd ref, 32 prescaler
  int adc = analogRead(ADCpin);
  ADC0_CTRLC = ADCregOriginal;
  return adc;
}
1 Like

It’s work! Thank you sir :smiley:
One more question : in this parameter ADC0_CTRLC = 0x54;
How can I change prescaler or Vdd for example? 0x54 - what is the value?
Thank you once more!

That’s just the register value I had worked out previously (prescaler of 32 is part of it). See page 408 in the datasheet. This would be 4x faster than the default 128. To use prescaler of 16, you would use 0x53. Note that the readings will become less accurate and stable at higher speeds.

1 Like

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