Reads in audio signal on ADC, performs FFT and displays spectrum on Nokia 5110 LCD display.

Dependencies:   N5110 mbed

Committer:
eencae
Date:
Thu Jul 24 15:25:26 2014 +0000
Revision:
3:0df6485bc3ec
Parent:
2:8da22b498051
Simple code which samples audio on ADC, calculates FFT and displays spectrum on LCD display.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
eencae 0:a497562bb2f9 1 #include "mbed.h"
eencae 1:7c6c1f98b8f5 2 #include "N5110.h"
eencae 0:a497562bb2f9 3
eencae 0:a497562bb2f9 4 extern "C" void fftR4(short *y, short *x, int N);
eencae 0:a497562bb2f9 5
eencae 0:a497562bb2f9 6 float magnitude(short y1, short y2);
eencae 0:a497562bb2f9 7 void updateSamples();
eencae 0:a497562bb2f9 8 void doFFT();
eencae 0:a497562bb2f9 9 void printSpectrum();
eencae 0:a497562bb2f9 10 void printSamples();
eencae 1:7c6c1f98b8f5 11 void ledBarGraph();
eencae 2:8da22b498051 12 void rgbDance();
eencae 1:7c6c1f98b8f5 13 void lcdEqualiser();
eencae 0:a497562bb2f9 14 int calcPeakFrequency();
eencae 0:a497562bb2f9 15
eencae 1:7c6c1f98b8f5 16 AnalogIn audio(p17); // ADC pin must be biased at Vcc/2 using coupling capacitor and potential divider
eencae 0:a497562bb2f9 17 BusOut leds(LED1,LED2,LED3,LED4);
eencae 0:a497562bb2f9 18 LocalFileSystem local("local"); // Create the local filesystem under the name "local"
eencae 0:a497562bb2f9 19 Serial serial(USBTX,USBRX);
eencae 1:7c6c1f98b8f5 20 // VCC,SCE,RST,D/C,MOSI,SCLK,LED
eencae 1:7c6c1f98b8f5 21 N5110 lcd(p7,p8,p9,p10,p11,p13,p21);
eencae 0:a497562bb2f9 22
eencae 2:8da22b498051 23 PwmOut red(p23); // RGB LEDs (same connections as app board)
eencae 2:8da22b498051 24 PwmOut green(p24);
eencae 2:8da22b498051 25 PwmOut blue(p25);
eencae 2:8da22b498051 26
eencae 0:a497562bb2f9 27 #define BUF_LEN 1024
eencae 3:0df6485bc3ec 28 #define SAMP_FREQ 10000
eencae 0:a497562bb2f9 29
eencae 1:7c6c1f98b8f5 30 short samples[BUF_LEN]; // store the values read from ADC
eencae 0:a497562bb2f9 31 short mx[BUF_LEN*2]; // input data 16 bit, 4 byte aligned x0r,x0i,x1r,x1i,....
eencae 0:a497562bb2f9 32 short my[BUF_LEN*2]; // output data 16 bit,4 byte aligned y0r,y0i,y1r,y1i,....
eencae 1:7c6c1f98b8f5 33 float spectrum[BUF_LEN/2]; // frequency spectrum
eencae 1:7c6c1f98b8f5 34
eencae 1:7c6c1f98b8f5 35 char buffer[14]; // screen buffer
eencae 0:a497562bb2f9 36
eencae 3:0df6485bc3ec 37 int tone;
eencae 3:0df6485bc3ec 38
eencae 0:a497562bb2f9 39 int main()
eencae 0:a497562bb2f9 40 {
eencae 1:7c6c1f98b8f5 41 // initialise LCD and display welcomes message
eencae 1:7c6c1f98b8f5 42 lcd.init();
eencae 1:7c6c1f98b8f5 43 lcd.printString("Audio FFT",14,0);
eencae 1:7c6c1f98b8f5 44 lcd.printString("Analyser",20,1);
eencae 1:7c6c1f98b8f5 45 lcd.printString("Craig A. Evans",0,4);
eencae 2:8da22b498051 46
eencae 0:a497562bb2f9 47 serial.baud(115200);
eencae 2:8da22b498051 48
eencae 2:8da22b498051 49 red.period(1e-4); // 10 kHz period for starters
eencae 2:8da22b498051 50 // setting one PWM channel sets them all as they share the same frequency
eencae 2:8da22b498051 51
eencae 0:a497562bb2f9 52 leds = 15;
eencae 1:7c6c1f98b8f5 53 wait(2.0); // short pause to allow coupling capacitor to charge
eencae 0:a497562bb2f9 54 leds = 0;
eencae 1:7c6c1f98b8f5 55 lcd.clear();
eencae 0:a497562bb2f9 56
eencae 0:a497562bb2f9 57 while(1) {
eencae 0:a497562bb2f9 58
eencae 2:8da22b498051 59 //red = 1.0,green=1.0,blue=1.0;
eencae 2:8da22b498051 60
eencae 1:7c6c1f98b8f5 61 updateSamples(); // read in new analog values
eencae 1:7c6c1f98b8f5 62 doFFT(); // calc FFT
eencae 2:8da22b498051 63 ledBarGraph(); // display amplitude bar graph on LEDs from sample values
eencae 1:7c6c1f98b8f5 64 lcdEqualiser(); // plot spectrum on LCD
eencae 2:8da22b498051 65 rgbDance();
eencae 0:a497562bb2f9 66 //printSpectrum();
eencae 0:a497562bb2f9 67 //printSamples();
eencae 3:0df6485bc3ec 68 tone = calcPeakFrequency(); // calculate peak frequcny and send over serial for debug
eencae 2:8da22b498051 69 wait_ms(10); // update display 100 ms
eencae 0:a497562bb2f9 70
eencae 0:a497562bb2f9 71 }
eencae 0:a497562bb2f9 72 }
eencae 0:a497562bb2f9 73
eencae 1:7c6c1f98b8f5 74 void ledBarGraph()
eencae 0:a497562bb2f9 75 {
eencae 1:7c6c1f98b8f5 76 float rms = 0.0; // initialse array
eencae 0:a497562bb2f9 77 for (int i = 0; i < BUF_LEN; i++) {
eencae 0:a497562bb2f9 78 rms+= samples[i]*samples[i];
eencae 0:a497562bb2f9 79 }
eencae 1:7c6c1f98b8f5 80 // calc the sum of the squares
eencae 2:8da22b498051 81
eencae 1:7c6c1f98b8f5 82 rms/=BUF_LEN; // get the mean
eencae 1:7c6c1f98b8f5 83 rms = sqrt(rms); // and root to get the RMS
eencae 0:a497562bb2f9 84 rms/= 16384.0; // scale according to 16-bit signed maximum value
eencae 0:a497562bb2f9 85
eencae 1:7c6c1f98b8f5 86 // check value and update LEDs to show amplitude
eencae 0:a497562bb2f9 87 if (rms > 0.8) {
eencae 0:a497562bb2f9 88 leds = 15;
eencae 0:a497562bb2f9 89 } else if (rms > 0.6) {
eencae 0:a497562bb2f9 90 leds = 7;
eencae 0:a497562bb2f9 91 } else if (rms > 0.4) {
eencae 0:a497562bb2f9 92 leds = 3;
eencae 0:a497562bb2f9 93 } else if (rms > 0.2) {
eencae 0:a497562bb2f9 94 leds = 1;
eencae 0:a497562bb2f9 95 } else {
eencae 0:a497562bb2f9 96 leds = 0;
eencae 0:a497562bb2f9 97 }
eencae 0:a497562bb2f9 98
eencae 1:7c6c1f98b8f5 99 //serial.printf("RMS = %f\n",rms);
eencae 0:a497562bb2f9 100 }
eencae 0:a497562bb2f9 101
eencae 0:a497562bb2f9 102 float magnitude(short y1, short y2)
eencae 0:a497562bb2f9 103 {
eencae 1:7c6c1f98b8f5 104 return sqrt(float(y1*y1+y2*y2)); // pythagoras
eencae 0:a497562bb2f9 105 }
eencae 0:a497562bb2f9 106
eencae 0:a497562bb2f9 107 void updateSamples()
eencae 0:a497562bb2f9 108 {
eencae 0:a497562bb2f9 109 for (int i = 0; i < BUF_LEN; i++) {
eencae 0:a497562bb2f9 110 samples[i] = (short) (audio.read_u16() - 0x8000); // read unsigned 16-bit and convert to signed 16-bit (subtract 32768)
eencae 1:7c6c1f98b8f5 111 wait_us(1e6/SAMP_FREQ); // wait for sampling frequency, should really implement with tickers
eencae 0:a497562bb2f9 112 }
eencae 0:a497562bb2f9 113 }
eencae 0:a497562bb2f9 114
eencae 0:a497562bb2f9 115 void doFFT()
eencae 0:a497562bb2f9 116 {
eencae 1:7c6c1f98b8f5 117 // clear buffers
eencae 0:a497562bb2f9 118 for (int i=0; i<2*BUF_LEN; i++) {
eencae 0:a497562bb2f9 119 my[i] = 0;
eencae 0:a497562bb2f9 120 mx[i] = 0;
eencae 0:a497562bb2f9 121 }
eencae 0:a497562bb2f9 122
eencae 1:7c6c1f98b8f5 123 for (int i=0; i<BUF_LEN; i++) { // load samples in array (skip imaginary input values)
eencae 0:a497562bb2f9 124 mx[i*2]=samples[i];
eencae 0:a497562bb2f9 125 }
eencae 0:a497562bb2f9 126
eencae 0:a497562bb2f9 127 fftR4(my, mx, BUF_LEN); // call FFT routine
eencae 0:a497562bb2f9 128
eencae 0:a497562bb2f9 129 int j = 0;
eencae 0:a497562bb2f9 130 for (int i = 0; i < BUF_LEN; i+=2) {
eencae 1:7c6c1f98b8f5 131 spectrum[j] = magnitude(my[i],my[i+1]); // get magnitude of FFT output to get spectrum data
eencae 0:a497562bb2f9 132 j++;
eencae 0:a497562bb2f9 133 }
eencae 0:a497562bb2f9 134 }
eencae 0:a497562bb2f9 135
eencae 0:a497562bb2f9 136 void printSpectrum()
eencae 0:a497562bb2f9 137 {
eencae 0:a497562bb2f9 138 FILE *fp = fopen("/local/fft.csv","w");
eencae 0:a497562bb2f9 139 //now write a CSV file to filesytem of frequency vs amplitude
eencae 0:a497562bb2f9 140 int j = 0;
eencae 0:a497562bb2f9 141 for (int i = 0; i < BUF_LEN; i+=2) {
eencae 1:7c6c1f98b8f5 142 int frequency = int(SAMP_FREQ/BUF_LEN/2*i); // calculate value of frequency bin
eencae 0:a497562bb2f9 143 fprintf(fp, "%d,%f\n", frequency, spectrum[j]);
eencae 0:a497562bb2f9 144 j++;
eencae 0:a497562bb2f9 145 }
eencae 0:a497562bb2f9 146 fclose(fp);
eencae 0:a497562bb2f9 147 }
eencae 0:a497562bb2f9 148
eencae 0:a497562bb2f9 149 void printSamples()
eencae 0:a497562bb2f9 150 {
eencae 0:a497562bb2f9 151 FILE *fp = fopen("/local/samples.csv","w");
eencae 0:a497562bb2f9 152 //now write a CSV file to filesytem of frequency vs amplitude
eencae 0:a497562bb2f9 153 for (int i = 0; i < BUF_LEN; i++) {
eencae 0:a497562bb2f9 154 fprintf(fp, "%d\n", samples[i]);
eencae 0:a497562bb2f9 155 }
eencae 0:a497562bb2f9 156 fclose(fp);
eencae 0:a497562bb2f9 157 }
eencae 0:a497562bb2f9 158
eencae 0:a497562bb2f9 159 int calcPeakFrequency()
eencae 0:a497562bb2f9 160 {
eencae 0:a497562bb2f9 161 float max = 0.0;
eencae 0:a497562bb2f9 162 int frequency = 0;
eencae 0:a497562bb2f9 163 int j = 0;
eencae 1:7c6c1f98b8f5 164 for (int i=0; i<BUF_LEN; i+=2) { // loop through spectrum and look for maximum value
eencae 0:a497562bb2f9 165 if (spectrum[j] > max) {
eencae 0:a497562bb2f9 166 max = spectrum[j];
eencae 2:8da22b498051 167 frequency = int(SAMP_FREQ/BUF_LEN/2*i);
eencae 0:a497562bb2f9 168 }
eencae 0:a497562bb2f9 169 j++;
eencae 0:a497562bb2f9 170 }
eencae 1:7c6c1f98b8f5 171 //serial.printf("Max = %f\n",max);
eencae 0:a497562bb2f9 172 return frequency;
eencae 1:7c6c1f98b8f5 173 }
eencae 1:7c6c1f98b8f5 174
eencae 1:7c6c1f98b8f5 175 void lcdEqualiser()
eencae 1:7c6c1f98b8f5 176 {
eencae 1:7c6c1f98b8f5 177 // spectrum has BUF_LEN/2 values = 512
eencae 1:7c6c1f98b8f5 178 // screen has 84 pixel, giving 6 spectrum points per pixel
eencae 1:7c6c1f98b8f5 179
eencae 1:7c6c1f98b8f5 180 float max = 0.0; // used for normalisation later
eencae 1:7c6c1f98b8f5 181
eencae 1:7c6c1f98b8f5 182 float pixelValue[84];
eencae 1:7c6c1f98b8f5 183
eencae 1:7c6c1f98b8f5 184 for (int i=0; i<84; i++) { // loop through array
eencae 1:7c6c1f98b8f5 185 pixelValue[i] = 0.0;
eencae 1:7c6c1f98b8f5 186
eencae 1:7c6c1f98b8f5 187 for (int y=0; y<6; y++) {
eencae 1:7c6c1f98b8f5 188 pixelValue[i] += spectrum[i*6+y]; // sum the 6 values in the spectrum
eencae 1:7c6c1f98b8f5 189 }
eencae 1:7c6c1f98b8f5 190 pixelValue[i]/=6; // calc. average for that pixel
eencae 1:7c6c1f98b8f5 191
eencae 1:7c6c1f98b8f5 192 if (pixelValue[i] > max) // check for biggest value
eencae 1:7c6c1f98b8f5 193 max = pixelValue[i];
eencae 1:7c6c1f98b8f5 194 }
eencae 1:7c6c1f98b8f5 195
eencae 1:7c6c1f98b8f5 196 for (int i=0; i<84; i++) { // loop through array
eencae 1:7c6c1f98b8f5 197 pixelValue[i]/=max; // normalise to 1.0
eencae 1:7c6c1f98b8f5 198 }
eencae 1:7c6c1f98b8f5 199
eencae 1:7c6c1f98b8f5 200 lcd.clear();
eencae 1:7c6c1f98b8f5 201 for (int i=0; i<84; i++) { // loop through array
eencae 1:7c6c1f98b8f5 202 for (int j=0; j<= 47 - int(pixelValue[i]*47.0); j++) { // loop through array
eencae 1:7c6c1f98b8f5 203 lcd.setPixel(i,j); // draw bar graphs for spectrum bins
eencae 1:7c6c1f98b8f5 204 }
eencae 1:7c6c1f98b8f5 205 }
eencae 3:0df6485bc3ec 206
eencae 3:0df6485bc3ec 207 sprintf(buffer,"%4u Hz",tone);
eencae 3:0df6485bc3ec 208 lcd.printString(buffer,40,0);
eencae 1:7c6c1f98b8f5 209 lcd.refresh();
eencae 1:7c6c1f98b8f5 210
eencae 2:8da22b498051 211 }
eencae 2:8da22b498051 212
eencae 2:8da22b498051 213 void rgbDance()
eencae 2:8da22b498051 214 {
eencae 2:8da22b498051 215
eencae 2:8da22b498051 216 // spectrum has BUF_LEN/2 values = 512
eencae 2:8da22b498051 217 // split into 3 bins for R, G and B gives 170 bins per colour
eencae 2:8da22b498051 218
eencae 2:8da22b498051 219 float ledColour[3];
eencae 2:8da22b498051 220 float total = 0.0;
eencae 2:8da22b498051 221
eencae 2:8da22b498051 222
eencae 2:8da22b498051 223
eencae 2:8da22b498051 224 for (int i=0; i<60; i++) {
eencae 2:8da22b498051 225 ledColour[0] += spectrum[i]; // sum the 6 values in the spectrum
eencae 2:8da22b498051 226 }
eencae 2:8da22b498051 227 total += ledColour[0];
eencae 2:8da22b498051 228 for (int i=60; i<200; i++) {
eencae 2:8da22b498051 229 ledColour[1] += spectrum[i]; // sum the 6 values in the spectrum
eencae 2:8da22b498051 230 }
eencae 2:8da22b498051 231 total += ledColour[1];
eencae 2:8da22b498051 232 for (int i=200; i<512; i++) {
eencae 2:8da22b498051 233 ledColour[2] += spectrum[i]; // sum the 6 values in the spectrum
eencae 2:8da22b498051 234 }
eencae 2:8da22b498051 235 total += ledColour[2];
eencae 2:8da22b498051 236
eencae 2:8da22b498051 237 float r = ledColour[0]/total;
eencae 2:8da22b498051 238 float g = ledColour[1]/total;
eencae 2:8da22b498051 239 float b = ledColour[2]/total;
eencae 2:8da22b498051 240
eencae 2:8da22b498051 241 serial.printf("RGB = %f , %f , %f (%f)\n",r,g,b,r+g+b);
eencae 2:8da22b498051 242
eencae 2:8da22b498051 243 r = 1.0 - r; // common anode, smaller value is brighter
eencae 2:8da22b498051 244 g = 1.0 - g; // bigger value is duller
eencae 2:8da22b498051 245 b = 1.0 - b;
eencae 2:8da22b498051 246
eencae 2:8da22b498051 247 if (r > 1.0)
eencae 2:8da22b498051 248 r = 1.0;
eencae 2:8da22b498051 249 else if (r < 0.0)
eencae 2:8da22b498051 250 r = 0.0;
eencae 2:8da22b498051 251
eencae 2:8da22b498051 252 if (g > 1.0)
eencae 2:8da22b498051 253 g = 1.0;
eencae 2:8da22b498051 254 else if (g < 0.0)
eencae 2:8da22b498051 255 g = 0.0;
eencae 2:8da22b498051 256
eencae 2:8da22b498051 257 if (b > 1.0)
eencae 2:8da22b498051 258 b = 1.0;
eencae 2:8da22b498051 259 else if (b < 0.0)
eencae 2:8da22b498051 260 b = 0.0;
eencae 2:8da22b498051 261
eencae 2:8da22b498051 262 red.write(r); // set duty cycles
eencae 2:8da22b498051 263 green.write(g);
eencae 2:8da22b498051 264 blue.write(b);
eencae 2:8da22b498051 265
eencae 0:a497562bb2f9 266 }