Read an audio signal from the ADC, write it out to a file on the filestytem and perform a 1024 point FFT, writing frequency data to a csv file
main.cpp@0:5b7b619f59cd, 2010-03-21 (annotated)
- Committer:
- jcobb
- Date:
- Sun Mar 21 18:06:46 2010 +0000
- Revision:
- 0:5b7b619f59cd
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
jcobb | 0:5b7b619f59cd | 1 | |
jcobb | 0:5b7b619f59cd | 2 | #define SAMPLE_RATE 48000 |
jcobb | 0:5b7b619f59cd | 3 | |
jcobb | 0:5b7b619f59cd | 4 | #include "mbed.h" |
jcobb | 0:5b7b619f59cd | 5 | #include "adc.h" |
jcobb | 0:5b7b619f59cd | 6 | |
jcobb | 0:5b7b619f59cd | 7 | //Going to use the Mellen FFT rather than STM, as the STM (port by Igor) won't compile |
jcobb | 0:5b7b619f59cd | 8 | //extern "C" void cr4_fft_256_stm32(void *pssOUT, void *pssIN, uint16_t Nbin); |
jcobb | 0:5b7b619f59cd | 9 | extern "C" void fftR4(short *y, short *x, int N); |
jcobb | 0:5b7b619f59cd | 10 | |
jcobb | 0:5b7b619f59cd | 11 | //use the LED as a bargraph |
jcobb | 0:5b7b619f59cd | 12 | DigitalOut l1(LED1); |
jcobb | 0:5b7b619f59cd | 13 | DigitalOut l2(LED2); |
jcobb | 0:5b7b619f59cd | 14 | DigitalOut l3(LED3); |
jcobb | 0:5b7b619f59cd | 15 | DigitalOut l4(LED4); |
jcobb | 0:5b7b619f59cd | 16 | |
jcobb | 0:5b7b619f59cd | 17 | //set up a timer for timing FFT's |
jcobb | 0:5b7b619f59cd | 18 | Timer timer; |
jcobb | 0:5b7b619f59cd | 19 | |
jcobb | 0:5b7b619f59cd | 20 | //Set up filesystem so we can write some useful files |
jcobb | 0:5b7b619f59cd | 21 | |
jcobb | 0:5b7b619f59cd | 22 | LocalFileSystem local("local"); |
jcobb | 0:5b7b619f59cd | 23 | FILE *fp; |
jcobb | 0:5b7b619f59cd | 24 | |
jcobb | 0:5b7b619f59cd | 25 | //Set up a global buffer for audio data so interrupt can access it |
jcobb | 0:5b7b619f59cd | 26 | int Counter = 0; |
jcobb | 0:5b7b619f59cd | 27 | int16_t Buffer[5000]; |
jcobb | 0:5b7b619f59cd | 28 | |
jcobb | 0:5b7b619f59cd | 29 | //Initialise ADC to maximum SAMPLE_RATE and cclk divide set to 1 |
jcobb | 0:5b7b619f59cd | 30 | ADC adc(SAMPLE_RATE, 1); |
jcobb | 0:5b7b619f59cd | 31 | |
jcobb | 0:5b7b619f59cd | 32 | //Functions to write 16 bit audio data and 32 bit headers to files in au format (cf sndRecorder Cookbook) |
jcobb | 0:5b7b619f59cd | 33 | void fwrite16(uint16_t v) |
jcobb | 0:5b7b619f59cd | 34 | { |
jcobb | 0:5b7b619f59cd | 35 | uint8_t *b = (uint8_t *)&v; |
jcobb | 0:5b7b619f59cd | 36 | |
jcobb | 0:5b7b619f59cd | 37 | fprintf(fp,"%c%c", b[1], b[0]); |
jcobb | 0:5b7b619f59cd | 38 | } |
jcobb | 0:5b7b619f59cd | 39 | void fwrite32(uint32_t v) |
jcobb | 0:5b7b619f59cd | 40 | { |
jcobb | 0:5b7b619f59cd | 41 | uint8_t *b = (uint8_t *)&v; |
jcobb | 0:5b7b619f59cd | 42 | |
jcobb | 0:5b7b619f59cd | 43 | fprintf(fp,"%c%c%c%c", b[3], b[2], b[1], b[0]); |
jcobb | 0:5b7b619f59cd | 44 | } |
jcobb | 0:5b7b619f59cd | 45 | |
jcobb | 0:5b7b619f59cd | 46 | //Our interrupt handler for audio sampling |
jcobb | 0:5b7b619f59cd | 47 | void sample_ADC(int chan, uint32_t value) { |
jcobb | 0:5b7b619f59cd | 48 | |
jcobb | 0:5b7b619f59cd | 49 | float s; |
jcobb | 0:5b7b619f59cd | 50 | s = adc.read(p20); |
jcobb | 0:5b7b619f59cd | 51 | int16_t b = (s -2048)*16; |
jcobb | 0:5b7b619f59cd | 52 | Buffer[Counter] = b; |
jcobb | 0:5b7b619f59cd | 53 | Counter += 1; |
jcobb | 0:5b7b619f59cd | 54 | /* bar graph */ |
jcobb | 0:5b7b619f59cd | 55 | int g = abs(s-2048); |
jcobb | 0:5b7b619f59cd | 56 | l1 = g > 0.1f*2048; |
jcobb | 0:5b7b619f59cd | 57 | l2 = g > 0.3f*2048; |
jcobb | 0:5b7b619f59cd | 58 | l3 = g > 0.6f*2048; |
jcobb | 0:5b7b619f59cd | 59 | l4 = g > 0.8f*2048; |
jcobb | 0:5b7b619f59cd | 60 | } |
jcobb | 0:5b7b619f59cd | 61 | |
jcobb | 0:5b7b619f59cd | 62 | int main() { |
jcobb | 0:5b7b619f59cd | 63 | |
jcobb | 0:5b7b619f59cd | 64 | //Prepare for burst mode on all ADC pins and set up interrupt handler (using ADC library from Simon Blandford |
jcobb | 0:5b7b619f59cd | 65 | adc.append(sample_ADC); |
jcobb | 0:5b7b619f59cd | 66 | adc.startmode(0,0); |
jcobb | 0:5b7b619f59cd | 67 | adc.burst(1); |
jcobb | 0:5b7b619f59cd | 68 | adc.setup(p20,1); |
jcobb | 0:5b7b619f59cd | 69 | |
jcobb | 0:5b7b619f59cd | 70 | //introduce a delay as initial waveform has bias whilst decoupling cap charges |
jcobb | 0:5b7b619f59cd | 71 | wait(1); |
jcobb | 0:5b7b619f59cd | 72 | |
jcobb | 0:5b7b619f59cd | 73 | //start the interrupt and wait for about 4096 samples |
jcobb | 0:5b7b619f59cd | 74 | adc.interrupt_state(p20,1); |
jcobb | 0:5b7b619f59cd | 75 | wait(0.1); |
jcobb | 0:5b7b619f59cd | 76 | |
jcobb | 0:5b7b619f59cd | 77 | //Finsh up - Unset pin 20 |
jcobb | 0:5b7b619f59cd | 78 | adc.interrupt_state(p20,0); |
jcobb | 0:5b7b619f59cd | 79 | adc.setup(p20,0); |
jcobb | 0:5b7b619f59cd | 80 | int actual_rate = adc.actual_sample_rate(); |
jcobb | 0:5b7b619f59cd | 81 | |
jcobb | 0:5b7b619f59cd | 82 | //for debugging tell the terminal sample rate and how many samples we took |
jcobb | 0:5b7b619f59cd | 83 | printf("Requested max sample rate is %u, actual max sample rate is %u.\n", |
jcobb | 0:5b7b619f59cd | 84 | SAMPLE_RATE, actual_rate); |
jcobb | 0:5b7b619f59cd | 85 | printf("We did %i samples\n",Counter); |
jcobb | 0:5b7b619f59cd | 86 | |
jcobb | 0:5b7b619f59cd | 87 | //write original audio file to filesytem so we can load on PC and see what's there (cf sndRecorder Cookbook) |
jcobb | 0:5b7b619f59cd | 88 | fp = fopen("/local/out.au", "w"); |
jcobb | 0:5b7b619f59cd | 89 | fprintf(fp,".snd"); |
jcobb | 0:5b7b619f59cd | 90 | fwrite32(24); |
jcobb | 0:5b7b619f59cd | 91 | fwrite32(-1); |
jcobb | 0:5b7b619f59cd | 92 | fwrite32(3); |
jcobb | 0:5b7b619f59cd | 93 | fwrite32(48000); |
jcobb | 0:5b7b619f59cd | 94 | fwrite32(1); |
jcobb | 0:5b7b619f59cd | 95 | int writeCount = 0; |
jcobb | 0:5b7b619f59cd | 96 | while(writeCount <=Counter) { |
jcobb | 0:5b7b619f59cd | 97 | fwrite16(Buffer[writeCount]); |
jcobb | 0:5b7b619f59cd | 98 | writeCount+=1; |
jcobb | 0:5b7b619f59cd | 99 | } |
jcobb | 0:5b7b619f59cd | 100 | |
jcobb | 0:5b7b619f59cd | 101 | //Not using the STM FFT, but leave code here for the moment |
jcobb | 0:5b7b619f59cd | 102 | |
jcobb | 0:5b7b619f59cd | 103 | /* |
jcobb | 0:5b7b619f59cd | 104 | //now do a fft of the initial 1024 samples |
jcobb | 0:5b7b619f59cd | 105 | #define N 256 //Number of points |
jcobb | 0:5b7b619f59cd | 106 | uint32_t x[N], y[N]; // input and output arrays |
jcobb | 0:5b7b619f59cd | 107 | int16_t real[N], imag[N]; // real and imaginary arrays |
jcobb | 0:5b7b619f59cd | 108 | memset(real, 0, sizeof(real)); |
jcobb | 0:5b7b619f59cd | 109 | memset(imag, 0, sizeof(imag)); |
jcobb | 0:5b7b619f59cd | 110 | // real[1]=SHRT_MAX; |
jcobb | 0:5b7b619f59cd | 111 | // Fill the input array |
jcobb | 0:5b7b619f59cd | 112 | for (int i=0; i<N; i++) |
jcobb | 0:5b7b619f59cd | 113 | { |
jcobb | 0:5b7b619f59cd | 114 | x[i] = (((uint16_t)(Buffer[i])) | ((uint32_t)(0<<16))); |
jcobb | 0:5b7b619f59cd | 115 | } |
jcobb | 0:5b7b619f59cd | 116 | timer.reset(); |
jcobb | 0:5b7b619f59cd | 117 | timer.start(); |
jcobb | 0:5b7b619f59cd | 118 | cr4_fft_256_stm32(y, x, N); //computes the FFT of the x[N] samples |
jcobb | 0:5b7b619f59cd | 119 | printf("ST32 fft up took %i\n",timer.read_us()); |
jcobb | 0:5b7b619f59cd | 120 | FILE* log = fopen("/local/stm32.txt","w"); |
jcobb | 0:5b7b619f59cd | 121 | for (int i=0; i<N; i++) |
jcobb | 0:5b7b619f59cd | 122 | { |
jcobb | 0:5b7b619f59cd | 123 | fprintf(log, "%d: %d, %d -> %d, %d\n", i, Buffer[i], 0 , int16_t(y[i] & 0xFFFF), int16_t(y[i] >> 16)); |
jcobb | 0:5b7b619f59cd | 124 | } |
jcobb | 0:5b7b619f59cd | 125 | fclose(log); |
jcobb | 0:5b7b619f59cd | 126 | |
jcobb | 0:5b7b619f59cd | 127 | //compute frequencies and magnitudes of result |
jcobb | 0:5b7b619f59cd | 128 | FILE* spectrum = fopen("/local/stm32Spec.txt","w"); |
jcobb | 0:5b7b619f59cd | 129 | for (int i=0; i<N/2; i++) |
jcobb | 0:5b7b619f59cd | 130 | { |
jcobb | 0:5b7b619f59cd | 131 | float real = int16_t(y[i] & 0xFFFF)* int16_t(y[i] & 0xFFFF); |
jcobb | 0:5b7b619f59cd | 132 | float imag = int16_t(y[i] >> 16)* int16_t(y[i] >> 16); |
jcobb | 0:5b7b619f59cd | 133 | fprintf(spectrum, "%d -> %f\n", int(SAMPLE_RATE/N*i),sqrt(imag+real)); |
jcobb | 0:5b7b619f59cd | 134 | } |
jcobb | 0:5b7b619f59cd | 135 | fclose(spectrum); |
jcobb | 0:5b7b619f59cd | 136 | */ |
jcobb | 0:5b7b619f59cd | 137 | |
jcobb | 0:5b7b619f59cd | 138 | //now lets try mellen fft |
jcobb | 0:5b7b619f59cd | 139 | timer.reset(); |
jcobb | 0:5b7b619f59cd | 140 | timer.start(); |
jcobb | 0:5b7b619f59cd | 141 | #define MN 1024 /*Number of points*/ |
jcobb | 0:5b7b619f59cd | 142 | short mx[MN*2]; // input data 16 bit, 4 byte aligned x0r,x0i,x1r,x1i,.... |
jcobb | 0:5b7b619f59cd | 143 | short my[MN*2]; // output data 16 bit,4 byte aligned y0r,y0i,y1r,y1i,.... |
jcobb | 0:5b7b619f59cd | 144 | for (int i=0;i<MN*2;i++) mx[i]=0; |
jcobb | 0:5b7b619f59cd | 145 | for (int i=0;i<MN;i=i+1) |
jcobb | 0:5b7b619f59cd | 146 | { mx[i*2]=Buffer[i];} |
jcobb | 0:5b7b619f59cd | 147 | printf("Mellen set up took %i\n",timer.read_us()); |
jcobb | 0:5b7b619f59cd | 148 | //call functions |
jcobb | 0:5b7b619f59cd | 149 | timer.reset(); |
jcobb | 0:5b7b619f59cd | 150 | timer.start(); |
jcobb | 0:5b7b619f59cd | 151 | fftR4(my, mx, MN); |
jcobb | 0:5b7b619f59cd | 152 | printf("Mellen fft took %i\n",timer.read_us()); |
jcobb | 0:5b7b619f59cd | 153 | FILE* mlog = fopen("/local/mellen.csv","w"); |
jcobb | 0:5b7b619f59cd | 154 | |
jcobb | 0:5b7b619f59cd | 155 | //now write a CSV file to filesytem of frequency vs amplitude |
jcobb | 0:5b7b619f59cd | 156 | for (int i=0; i<MN; i=i+2) |
jcobb | 0:5b7b619f59cd | 157 | { |
jcobb | 0:5b7b619f59cd | 158 | // fprintf(mlog, "%d: %d -> %d\n", i, mx[i], my[i]); |
jcobb | 0:5b7b619f59cd | 159 | fprintf(mlog, "%d,%f\n", int(actual_rate/MN/2*i),sqrt(float( (my[i]*my[i]) +(my[i+1]*my[i+1]) ) ) ); |
jcobb | 0:5b7b619f59cd | 160 | } |
jcobb | 0:5b7b619f59cd | 161 | fclose(mlog); |
jcobb | 0:5b7b619f59cd | 162 | |
jcobb | 0:5b7b619f59cd | 163 | |
jcobb | 0:5b7b619f59cd | 164 | } |
jcobb | 0:5b7b619f59cd | 165 | |
jcobb | 0:5b7b619f59cd | 166 | |
jcobb | 0:5b7b619f59cd | 167 | |
jcobb | 0:5b7b619f59cd | 168 | |
jcobb | 0:5b7b619f59cd | 169 |