Acest proiect reprezintă implementarea unui analizator de spectru ce captează sunetul din mediul încojurător. Asupra sample-urilor de sunet captate se realizează o transformare Fourier și se obține spectrul de frecvențe. Datele sunt afișate codificând spectograma obtinută pe un “ecran” de 24×8 LED-uri (alcatuită din 3 matrici LED 8×8) în funcție de frecvență (coloane) și intensitatea sunetului (rânduri). Matricile sunt organizate în așa fel încât frecvențele joase sunt reprezentate cu roșu, cele medii cu verde și cele înalte cu albastru.
Principiul de funcționare al proiectului este următorul:
1. Prin intermediul unui microfon sunt preluate sunetele sub formă de date analog; 2. Asupra acestor date se aplică o transformare Fourier și se obține spectograma; 3. Spectograma este afisată pe ecranul de LED-uri.
Setup
În setup se initializează pinii, se testează că funcționează matricile LED și se initializează un timer. Timer-ul este folosit pentru a crea întreruperi în mod regulat ce va declanșa randarea următoarei coloane de pixeli ai “ecranului”.
void setup() { // Initialize pins pinMode(SER1, OUTPUT); pinMode(RCLK1, OUTPUT); pinMode(SRCLK1, OUTPUT); pinMode(OE, OUTPUT); pinMode(SER2, OUTPUT); pinMode(RCLK2, OUTPUT); pinMode(SRCLK2, OUTPUT); digitalWrite(OE, HIGH); // Code for checking if the LED lights work for(int i = 1; i <= 8; i++) { for(int j = 0; j < 8; j++) { write_mat((1<<i) - 1, (1<<i) - 1, (1<<i) - 1, 1<<j); delay(50); } } write_mat(255, 255, 255, 255); delay(2000); // Initialize Timer for the interrupts Timer1.initialize(1000); //microseconds Timer1.attachInterrupt(draw_column); }
Comunicare cu Shift Register
Conform datasheet-ului, se transmit date seriale ce sunt reținute în shift register. cel mai simplu mod de interacționare este folosirea funcției shiftOut
care transmite un întreg byte de date.
// Send one byte of data to a shift register void write_to_register(byte data, int rclk, int ser, int srclk) { digitalWrite(rclk, LOW); shiftOut(ser, srclk, LSBFIRST, data); digitalWrite(rclk, HIGH); }
Randarea Matricelor LED
Randarea unei matrici se face pe coloane. Refresh rate-ul este de 1000 * 24 µs = 24ms.
// Write to the 24x8 LED matrix using the shift registers void write_mat(byte r, byte g, byte b, byte row) { digitalWrite(OE, HIGH); write_to_register(b, RCLK1, SER1, SRCLK1); write_to_register(g, RCLK1, SER1, SRCLK1); write_to_register(r, RCLK1, SER1, SRCLK1); write_to_register(row ^ 255, RCLK2, SER2, SRCLK2); digitalWrite(OE, LOW); } // Renders the next column of the matrix void draw_column() { if(mat_line < 8) { // Draw a red column write_mat((1 << min(8, histogram[mat_line])) - 1, 0, 0, 1 << mat_line); } else if(mat_line < 16) { // Draw a green column write_mat(0, (1 << min(8, histogram[mat_line])) - 1, 0, 1 << (mat_line & 7)); } else { // Draw a blue column write_mat(0, 0, (1 << min(8, histogram[mat_line])) - 1, 1 << (mat_line & 7)); } mat_line++; if(mat_line == 24) { // Reset the rendering process mat_line = 0; } }
Main Loop
În loop
se citesc datele analog de la microfon și sunt transformate in spectograma utilizând funcția fix_fft
.
void loop() { static int i, j, step; int val; // get audio data for(i = 0; i < SAMPLES; i++) { val = analogRead(AUDIO); // 0-1023 real[i] = (char)(val/4 - 128); // store the result on 8 bits imag[i] = 0; // set all imaginary parts to 0 } // run FFT fix_fft(real, imag, LOG_SAMPLES, 0); histogram[0] = 0; // noise, so we ignore it // extract absolute value of data only, for half the results for(i = 1; i < SAMPLES/2; i++) { histogram[i] = (int)sqrt(real[i] * real[i] + imag[i] * imag[i]); } // compress the histogram to 24 values so it can be printed on the LED matrices for(int i = 0; i < LED_COLS; i++) { // magic to make the spectogram look good and ignore low frequency signals histogram[i] = (histogram[i * 2 + 6] + histogram[i * 2 + 7] + histogram[i * 2 + 8]) * 1.33f; } }
Demo funcționalitate: link
Proiectul a fost finalizat cu succes.
Codul sursa al proiectului poate fi găsit aici: Spectrum Analyser.