Analizator widma z FFT na STM32 z Cortex-M4
Widmo częstotliwościowe sygnału audio z mikrofonu
Ostatni etap projektu to wyświetlenie widma sygnału jaki otrzymujemy z mikrofonu. Do tego celu przygotowano dwie funkcje DrawSpectrum_Prepare(), która rysuje tylko raz stałe elementy wykresu oraz DrawSpectrum_Update(), która rysuje samo widmo lub go usuwa w zależności od przekazanego jako argument koloru.
void DrawSpectrum_Prepare(uint16_t xpos, uint8_t ypos){ int i; g_SPECTRUM_Xpos = xpos; g_SPECTRUM_Ypos = ypos; // Rysowanie obramowania LCD_SetForegroundColor(White); x_LCD_DrawRectangle(xpos + 10, ypos + 10, SPECTRUM_NR_SAMPLES/2 + 2, SPECTRUM_HEIGHT+2); // Wypełnienie tła x_LCD_DrawFilledRectangle(xpos + 11, ypos + 11, SPECTRUM_NR_SAMPLES/2, SPECTRUM_HEIGHT, SPECTRUM_BG_COLOR); // Rysowanie podziałek osi poziomej LCD_SetFont(&Font8x8); LCD_SetTextColor(Grey); for(i=0; i<=16; ++i){ sprintf(text,"%.2f",i*16*SPECTRUM_FREQ_S_kHz/SPECTRUM_NR_SAMPLES); x_LCD_DrawLine(xpos + 10 + i*16, ypos + 10 + SPECTRUM_HEIGHT+2, 5, Vertical); x_LCD_DrawText(xpos + 10 + i*16 + 4, ypos + 10 + SPECTRUM_HEIGHT+2 + 10, text, LeftJustify, RightOrientation); } x_LCD_DrawText(xpos + 10 + i*16 + 4, ypos + 10 + SPECTRUM_HEIGHT+2 + 10, SPECTRUM_X_LABEL, LeftJustify, RightOrientation); // Rysowanie podziałek osi pionowej for(i=0; i<=6; ++i){ x_LCD_DrawLine(xpos + 10 - 5, ypos + 10 + SPECTRUM_HEIGHT+2 - 1 - i*25, 5, Horizontal); } } void DrawSpectrum_Update(uint16_t color){ int i; // Rysowanie szpilek LCD_SetForegroundColor(color); for(i=0; i<256; ++i){ x_LCD_DrawLine_2(g_SPECTRUM_Xpos + 11 + i, g_SPECTRUM_Ypos + 160, g_SPECTRUM_Xpos + 11 + i, g_SPECTRUM_Ypos + (uint16_t)(160 - buffer_output_mag[i+1])); } }
Poniżej przedstawiono kod który wykorzystuje wszystkie podane dotąd informacje. Komentarza mogą wymagać dwa miejsca. Ponieważ w funkcje obsługi przerwań od odbioru danych od I2S maksymalny rozmiar paczki zapisanej w buforze PCM_Output_Buffer wynosi w danej sytuacji 32 elementy to trzeba zebrać ich 16 aby otrzymać tablicę 512 wartości rzeczywistych przekazywanych do funkcji wyznaczającej transformatę. Po drugie zamiast skalować widmo wykorzystując maksymalną jej wartość jest ono skalowane z wartością stałą wyznaczoną doświadczalnie tak aby wyświetlanie odbywało się poprawnie.
// Rysowanie stałych elementów wykresu; cały obiekt rozpoczyna się w punkcie x=21, y=20 DrawSpectrum_Prepare(21,20); // Włączenie odbioru danych przez układ I2S I2S_Cmd(SPI2, ENABLE); while(1){ // Jeśli wciśnięto przycisk to zakończ wyświetlanie widma if(STM_EVAL_PBGetState(BUTTON_USER) == SET){ while(STM_EVAL_PBGetState(BUTTON_USER) == SET); break; } // Jeśli nowa paczka danych w postaci sygnału PCM jest gotowa if(Data_Status){ // Przekopiuj dane paczki do bufora for(i=0; i<(OUT_FREQ/1000); i++){ buffer_input[i+(OUT_FREQ/1000)*z] = (float32_t) PCM_Output_Buffer[i]; } ++z; if(z > 512/(OUT_FREQ/1000)){ z = 0; // ************************************************************ // Usunięcie poprzedniego widma DrawSpectrum_Update(Black); // ************************************************************ // Wyznaczenie transformaty Fouriera arm_rfft_f32(&S, buffer_input, buffer_output); // Obliczenie modułów arm_cmplx_mag_f32(buffer_output, buffer_output_mag, 512); // Znalezienie składowej harmonicznej sygnału o największej amplitudzie arm_max_f32(&(buffer_output_mag[1]), 512, &maxvalue, &maxvalueindex); // Skalowanie wartości modułów for(i=0; i<512; ++i){ buffer_output_mag[i+1] = 140*buffer_output_mag[i+1]/20000000; } // ************************************************************ // Rysowanie nowego widma DrawSpectrum_Update(Green); } Data_Status = 0; } // Wyświetl częstotliwość składowej harmonicznej o największej amplitudzie sprintf(text,"F=%2.2f[kHz] ",(maxvalueindex+1)*32.0/512); x_LCD_DrawText(100, 15, text, LeftJustify, NormalOrientation); // Czekaj jakiś czas, aby widmo było lepiej widoczne for(i=0; i<0x10000; ++i); } // Wyłącz odbiór danych I2S_Cmd(SPI2, DISABLE);
Rys. 15. Struktura prezentowanego projektu
Ostatecznie struktura projektu wygląda tak jak na rysunku 15, a na rysunkach 16 i 17 przedstawiono konfigurację w zakładce Compile oraz Link środowiska CooCox CoIDE.
Rys. 16. Konfiguracja w zakładce Compile
Rys. 17. Konfiguracja w zakładce Link
W pliku system_stm32f4xx.c można znaleźć wartości użyte do konfiguracji sygnałów taktujących zarówno samego systemu jak również przekazywanego do peryferii I2S. Sprzętowa jednostka FPU nie została włączona gdyż biblioteka PDM nie jest przystosowana do jej obsługi (kompilator wyrzuca błędy).
Literatura:
[1] AN3997 – Audioplayback and recording using the STM32F4DISCOVERY [2] AN3998 – PDM audio software decoding on STM32 microcontrollers [3] MP45DT02 – MEMS audio sensor omnidirectional digital microphone [4] T. Zieliński, Cyfrowe przetwarzanie sygnałów. Od teorii do zastosowań , WKŁ, W-wa 2009