Project tutorial

# DFT Audio Analyser © GPL3+

Implementing DFT on ATMega328p microcontroller.

• 2,249 views
• 10 respects

## Components and supplies

 Arduino UNO & Genuino UNO ATMega328p MCU is used
×1
 Adafruit Standard LCD - 16x2 White on Blue
×1

## Apps and online services

 Microchip Atmel Studio 7

### The idea

The idea here is to implement DFT on AVR 8 bit mcu platform with simplest algorithm and achieve real time display of Fourier transform of audio signal.

Before we get started with this project here are few prerequisites you need to know:

• DFT - mathematical representation and significance
• AVR micro-controller: fundamentals and C programming
• Audio electronics

Well if you are aware about the above mentioned topics, lets get started.

As we know 'sound' is caused due to physical disturbance in a medium, these disturbances are created using a speaker which converts sinusoidal electrical signal to displacement. Hence all the sound waves are made from sinusoidal signals. Also any wave can be represented as the weighted sum of sinusoids with angular frequency multiples of base frequency, the below represents mathematical form of any waveform.Here the weights are the amplitudes of the corresponding frequency. The DFT algorithm finds these weights for their corresponding frequency when unknown signal is given.

### The DFT algorithm

Now the DFT can be computed by the following equation:

The above equation can be directly written in C, before we get into source code lets begin with circuit schematic:

### Building the circuit

To build the circuit I have used ATmega328P mcu and JHD162A LCD. The LCD is interfaced with PORT-D of mcu and the LCD is configured in 4 bit data bus mode. LM386 is a dedicated audio amplifier, its purpose here is to amplify the audio signal which has max. amplitude of 0.7V (usually). For proper results we need to tune or set the 10K trimmer pot.. Also the arrangement of capacitor needs to be done so that noise produced is minimum.

After uploading the code, on startup the controller sets i/o port, then configures ADC, then loads LCD drivers and then runs a bootup animation...

The DFT code segment starts from 7th line from 'int main()'.

Here I have implemented 32 point DFT so that it can classify the input audio signal to 16 frequencies. For 32 point DFT the mcu needs to compute 512 values of sine and cosine. If the functions of math.h library were used, the computation would require more time so I have made a lookup table in the flash of the mcu because of which the computation has become faster and real time display is achieved.

When the program counter enters the application section (infinite loop) first the input is sampled and is passed over to DFT, the output is displayed in the form of bar-graph (histogram) on LCD these are also called as 'bins'.

Here we are using character LCD, hence we need to define the bars manually by writing the relevant code. This model of LCD allows you to create 8 custom character each of 8 bytes. In the source code I have written a function for creating the custom characters.

### Audio Sampling Section

Now lets get into the audio sampling section, when the mcu starts by default it enters into the mode0 which has the sampling frequency of 17.920Ksps, so we get the range of 560 -  8960 Hz according to Nyquist's criterion. The sampling rate is decided with the prescaler value of ADC which is set in ADCSRA register of mcu. Now the button 'SW' is used to set the range i.e. by default 560 - 8960 Hz (mode0) and when pressed it switches to 280 - 4480 Hz (mode1, sampling rate = 8960 Hz) and when pressed again it rolls back to default state. Here the button is switching the value of ADCSRA register. (for more information refer datasheet of mcu)

The output of DFT is in the range of 0 - 32 (approx.)(adjust the trimmer pot accordingly or add a code segment to divide the output with relevant value so the output is scaled) so I have scaled by dividing it by 2 and displayed the output on LCD (refer code).

Picture of DFT with various inputs and modes:

Now comes the question 'How accurate this device could be?' Well as I have used lookup table approach, to save space in flash I have stored integral values of the sine and cosine by scaling them up by 1000 times and the final result is scaled down by 1000 times so this is not the most accurate device but for accuracy we need to do a proper trade-off between accuracy and speed. As I have opted for real time display I have compromise a bit on accuracy. However this has pretty good accuracy and speed compared to the device which uses functions from math.h library.

### Demo

Here is the video of DFT Audio Analyser demonstrating the song 'flute' by Tony Igy

For more projects on Arduino and AVR visit project website

## Code

##### DFT Audio AnalyzerC/C++
```/*
* DFT_on_AVR.c
*
* Created: 19-08-2016 11:02:32 PM
* Author : Akash Kollipara
*/
#define F_CPU 16000000UL
#define RS 2
#define EN 3

#include <avr/io.h>
#include <util/delay.h>
#include <math.h>
#include <avr/pgmspace.h>

//INITIALZATIONS

#define N 32		//#samples
uint16_t f[N], angle;
float cs, ss;
uint8_t x, k;
char pin;
char arr[8][8]=
{
{0, 0, 0, 0, 0, 0, 0, 31}, 		//L1
{0, 0, 0, 0, 0, 0, 31, 31}, 		//L2
{0, 0, 0, 0, 0, 31, 31, 31}, 		//L3
{0, 0, 0, 0, 31, 31, 31, 31}, 		//L4
{0, 0, 0, 31, 31, 31, 31, 31}, 		//L5
{0, 0, 31, 31, 31, 31, 31, 31}, 	//L6
{0, 31, 31, 31, 31, 31, 31, 31}, 	//L7
{31, 31, 31, 31, 31, 31, 31, 31}	//L8
};

//PRTOTYPING

//LCD FUNCTIONS
void lcd_init();
void cmd(char);
void Data(char);
void lcd_clear();
void lcd_printc(char);
void lcd_prints(char *);
void lcd_cust_char(char, char *);
void lcd_setCursor(char, char);

//BOOTUP
void bootup();

const int16_t cosine_lookup[] PROGMEM =
{
1000, 980, 923, 831, 707, 555, 382, 195, 0, -195, -382, -555, -707, -831, -923, -980, -1000, -980, -923, -831, -707, -555, -382, -195, 0, 195, 382, 555, 707, 831, 923, 980, 1000, 923, 707, 382, 0, -382, -707, -923, -1000, -923, -707, -382, 0, 382, 707, 923, 1000, 923, 707, 382, 0, -382, -707, -923, -1000, -923, -707, -382, 0, 382, 707, 923, 1000, 831, 382, -195, -707, -980, -923, -555, 0, 555, 923, 980, 707, 195, -382, -831, -1000, -831, -382, 195, 707, 980, 923, 555, 0, -555, -923, -980, -707, -195, 382, 831, 1000, 707, 0, -707, -1000, -707, 0, 707, 1000, 707, 0, -707, -1000, -707, 0, 707, 999, 707, 0, -707, -1000, -707, 0, 707, 1000, 707, 0, -707, -1000, -707, 0, 707, 1000, 555, -382, -980, -707, 195, 923, 831, 0, -831, -923, -195, 707, 980, 382, -555, -1000, -555, 382, 980, 707, -195, -923, -831, 0, 831, 923, 195, -707, -980, -382, 555, 1000, 382, -707, -923, 0, 923, 707, -382, -1000, -382, 707, 923, 0, -923, -707, 382, 1000, 382, -707, -923, 0, 923, 707, -382, -1000, -382, 707, 923, 0, -923, -707, 382, 1000, 195, -923, -555, 707, 831, -382, -980, 0, 980, 382, -831, -707, 555, 923, -195, -1000, -195, 923, 555, -707, -831, 382, 980, 0, -980, -382, 831, 707, -555, -923, 195, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 999, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, -195, -923, 555, 707, -831, -382, 980, 0, -980, 382, 831, -707, -555, 923, 195, -1000, 195, 923, -555, -707, 831, 382, -980, 0, 980, -382, -831, 707, 555, -923, -195, 1000, -382, -707, 923, 0, -923, 707, 382, -1000, 382, 707, -923, 0, 923, -707, -382, 1000, -382, -707, 923, 0, -923, 707, 382, -1000, 382, 707, -923, 0, 923, -707, -382, 1000, -555, -382, 980, -707, -195, 923, -831, 0, 831, -923, 195, 707, -980, 382, 555, -1000, 555, 382, -980, 707, 195, -923, 831, 0, -831, 923, -195, -707, 980, -382, -555, 1000, -707, 0, 707, -1000, 707, 0, -707, 1000, -707, 0, 707, -1000, 707, 0, -707, 1000, -707, 0, 707, -1000, 707, 0, -707, 1000, -707, 0, 707, -1000, 707, 0, -707, 1000, -831, 382, 195, -707, 980, -923, 555, 0, -555, 923, -980, 707, -195, -382, 831, -1000, 831, -382, -195, 707, -980, 923, -555, 0, 555, -923, 980, -707, 195, 382, -831, 1000, -923, 707, -382, 0, 382, -707, 923, -1000, 923, -707, 382, 0, -382, 707, -923, 1000, -923, 707, -382, 0, 382, -707, 923, -1000, 923, -707, 382, 0, -382, 707, -923, 1000, -980, 923, -831, 707, -555, 382, -195, 0, 195, -382, 555, -707, 831, -923, 980, -1000, 980, -923, 831, -707, 555, -382, 195, 0, -195, 382, -555, 707, -831, 923, -980, 1000, -1000, 1000, -1000, 999, -1000, 1000, -1000, 1000, -1000, 1000, -1000, 1000, -1000, 1000, -1000, 1000, -1000, 1000, -1000, 1000, -1000, 1000, -999, 1000, -1000, 1000, -1000, 1000, -999, 1000, -1000
};

const int16_t sine_lookup[] PROGMEM =
{
0, 195, 382, 555, 707, 831, 923, 980, 1000, 980, 923, 831, 707, 555, 382, 195, 0, -195, -382, -555, -707, -831, -923, -980, -1000, -980, -923, -831, -707, -555, -382, -195, 0, 382, 707, 923, 1000, 923, 707, 382, 0, -382, -707, -923, -1000, -923, -707, -382, 0, 382, 707, 923, 999, 923, 707, 382, 0, -382, -707, -923, -1000, -923, -707, -382, 0, 555, 923, 980, 707, 195, -382, -831, -1000, -831, -382, 195, 707, 980, 923, 555, 0, -555, -923, -980, -707, -195, 382, 831, 1000, 831, 382, -195, -707, -980, -923, -555, 0, 707, 1000, 707, 0, -707, -1000, -707, 0, 707, 999, 707, 0, -707, -1000, -707, 0, 707, 1000, 707, 0, -707, -1000, -707, 0, 707, 1000, 707, 0, -707, -1000, -707, 0, 831, 923, 195, -707, -980, -382, 555, 999, 555, -382, -980, -707, 195, 923, 831, 0, -831, -923, -195, 707, 980, 382, -555, -1000, -555, 382, 980, 707, -195, -923, -831, 0, 923, 707, -382, -1000, -382, 707, 923, 0, -923, -707, 382, 1000, 382, -707, -923, 0, 923, 707, -382, -1000, -382, 707, 923, 0, -923, -707, 382, 1000, 382, -707, -923, 0, 980, 382, -831, -707, 555, 923, -195, -1000, -195, 923, 555, -707, -831, 382, 980, 0, -980, -382, 831, 707, -555, -923, 195, 1000, 195, -923, -555, 707, 831, -382, -980, 0, 1000, 0, -1000, 0, 999, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 1000, 0, -1000, 0, 980, -382, -831, 707, 555, -923, -195, 1000, -195, -923, 555, 707, -831, -382, 980, 0, -980, 382, 831, -707, -555, 923, 195, -1000, 195, 923, -555, -707, 831, 382, -980, 0, 923, -707, -382, 999, -382, -707, 923, 0, -923, 707, 382, -1000, 382, 707, -923, 0, 923, -707, -382, 1000, -382, -707, 923, 0, -923, 707, 382, -1000, 382, 707, -923, 0, 831, -923, 195, 707, -980, 382, 555, -1000, 555, 382, -980, 707, 195, -923, 831, 0, -831, 923, -195, -707, 980, -382, -555, 1000, -555, -382, 980, -707, -195, 923, -831, 0, 707, -1000, 707, 0, -707, 1000, -707, 0, 707, -1000, 707, 0, -707, 1000, -707, 0, 707, -1000, 707, 0, -707, 1000, -707, 0, 707, -999, 707, 0, -707, 999, -707, 0, 555, -923, 980, -707, 195, 382, -831, 1000, -831, 382, 195, -707, 980, -923, 555, 0, -555, 923, -980, 707, -195, -382, 831, -1000, 831, -382, -195, 707, -980, 923, -555, 0, 382, -707, 923, -1000, 923, -707, 382, 0, -382, 707, -923, 1000, -923, 707, -382, 0, 382, -707, 923, -1000, 923, -707, 382, 0, -382, 707, -923, 999, -923, 707, -382, 0, 195, -382, 555, -707, 831, -923, 980, -1000, 980, -923, 831, -707, 555, -382, 195, 0, -195, 382, -555, 707, -831, 923, -980, 999, -980, 923, -831, 707, -555, 382, -195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

//MAIN
int main(void)
{
bootup();
while(1)
{
for(k = 0; k < N; k++)
for(k = 0; k < N/2; k++)
{
cs = ss = 0;
for(x = 0; x < N; x++)
{
angle = x + (32*k);
cs += (float)f[x]*(int16_t)pgm_read_word(&cosine_lookup[angle]);		//real part of dft
ss += (float)f[x]*(int16_t)pgm_read_word(&sine_lookup[angle]);			//imaginary part of dft
}
cs /= N*1000;
ss /= N*1000;
x = (uint8_t)sqrt((cs*cs)+(ss*ss));		//absolute value of dft
x /= 2;		//scaling
x++;
if(x > 16) x = 16;
if(x < 9)
{
lcd_setCursor(k, 0);
lcd_printc(' ');
lcd_setCursor(k, 1);
lcd_printc(x);

}
else
{
lcd_setCursor(k, 1);
lcd_printc(8);
lcd_setCursor(k, 0);
lcd_printc(x-8);
}
}
if(!(PINB & 0x01) && ((pin & 0x80) == 0x00))
{
pin |= 0x80;
pin++;
if((pin & 0x0f)>1) pin &= 0xf0;
switch((pin & 0x0f))
{
case 0:
{
lcd_clear();
lcd_setCursor(2, 0);
lcd_prints("560 - 8960 Hz");
_delay_ms(1000);
break;
}
case 1:
{
lcd_clear();
lcd_setCursor(2, 0);
lcd_prints("280 - 4480 Hz");
_delay_ms(1000);
break;
}
}
}
else if((pin & 0x80) && ((PINB & 0x01)))
{
pin &= 0x7f;
}
}
return 0;
}

//bootup functions
void bootup()
{
PORTB = 0x01;		//internal pull-up
DDRD = 0xfc;		//set o/p port for LCD
_delay_ms(1);
lcd_init();
for(k = 0; k < 8; k++)
lcd_clear();
}

//SYSTEM FUNCTIONS
{
}

{
}

void lcd_init()
{
cmd(0x28);		//2->4-bit mode, 8->2-line disp mode
_delay_ms(1);
cmd(0x28);		//2->4-bit mode, 8->2-line disp mode
_delay_ms(1);
cmd(0x0c);
_delay_ms(1);
cmd(0x06);
_delay_ms(1);
cmd(0x80);
_delay_ms(1);
lcd_clear();
}

void cmd(char comm)
{
PORTD = (comm & 0xf0) | (1<<EN);
PORTD = (comm & 0xf0) & (~(1<<EN));
PORTD = ((comm << 4) & 0xf0) | (1<<EN);
PORTD = ((comm << 4) & 0xf0) & (~(1<<EN));
_delay_us(1150);
}

void lcd_clear()
{
cmd(0x01);
}

void lcd_printc(char c)
{
Data(c);
}

void lcd_prints(char *arr)
{
for(uint8_t i = 0; arr[i] != '\0'; i++)
Data(arr[i]);
}

void Data(char comm)
{
PORTD = (comm & 0xf0) | (1<<EN) | (1<<RS);
PORTD = ((comm & 0xf0) & (~(1<<EN))) | (1<<RS);
PORTD = ((comm<<4) & 0xf0) | (1<<EN) | (1<<RS);
PORTD = (((comm<<4) & 0xf0) & (~(0<<EN))) | (1<<RS);
_delay_us(100);
}

void lcd_cust_char(char i, char *arr)
{
cmd(0x40 | (8*i));
for(int j = 0; j <8; j++)
Data(arr[j]);
cmd(0x80);
}

void lcd_setCursor(char x, char y)
{
if(y)
cmd(0x80 + 0x40 + x);
else if(!y)
cmd(0x80 + x);
}
```

• 3 projects
• 8 followers

June 22, 2017

#### Members who respect this project

and 5 others

See similar projects
you might like

#### Animate a Billy Bass Mouth With Any Audio Source

Project tutorial by Donald Bell

• 7,526 views
• 31 respects

#### Audio Input

Project showcase by Arduino_Scuola

• 7,395 views
• 6 respects

#### Talkative Automation || Audio from Arduino || HC-05 || Voice

Project tutorial by Vishalsoniindia

• 2,552 views
• 5 respects

#### Internet Radio / DLNA Audio Renderer

Project showcase by Team Luigi Findanno

• 7,895 views
• 15 respects

#### Arduino Audio Reactive Desk Light

Project tutorial by Haziq Azri

• 12,580 views