Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

FFT w praktyce z wykorzystaniem ESP32 i Arduino.

TechEkspert 14 Aug 2017 20:32 16980 11
NDN
  • FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    W materiale znajdziecie wprowadzenie do transformacji Fouriera z teorią ograniczoną do minimum. Jest to propozycja zapoznania się z tematem poprzez praktyczne eksperymenty, następnie intuicyjne wyczucie do czego może przydać się FFT. Jeżeli odstrasza Cię skomplikowana matematyka ten materiał może być pomocny. Jeżeli matematyka to twój żywioł, pamiętaj że celowo pominę i uproszę bardzo wiele rzeczy wychodząc z założenia, że od czegoś trzeba zacząć a później we własnym zakresie można szukać dalej. Do eksperymentów wykorzystamy środowisko Arduino, ESP32 oraz monochromatyczny wyświetlacza LCD 128x64px wyposażony w magistralę SPI. Moduł ESP32 dostępny był w gadżetach elektroda.pl, informacje jak skonfigurować środowisko Arduino do pracy z ESP32 znajdziecie tutaj: konfiguracja modułu Wi-Fi i Bluetooth ESP32 w Arduino.
    W materiale znajdziecie zdjęcia i filmy prezentujące uzyskane efekty, dostępny jest też kod wykorzystanego programu co ułatwi własne próby praktyczne.
    Transformacja Fouriera pozwala na przejście z dziedziny czasu (w praktyce np. bufor z próbkami z ADC - przetwornika analogowo-cyfrowego, pobieranymi z określoną częstotliwością) do dziedziny częstotliwości, czyli w praktyce posiadamy informację o amplitudach (lub jak wolisz mocach) na kolejnych pasmach częstotliwości. Dziedzinę czasu znasz dobrze np. z ekranu oscyloskopu, czyli kolejne wartości próbek sygnału w czasie. Reprezentację częstotliwościową sygnału możesz porównać do kręcenia gałką w odbiorniku radiowym i sprawdzaniu poziomu sygnału na poszczególnych częstotliwościach. Możesz spotkać się z implementacją dyskretnej transformacji Fouriera jako DFT (discrete Fourier transform) której implementacja jest dość prosta (pętla for wewnątrz drugiej pętli for operująca na danych wejściowych z bufora próbek) jednak złożoność obliczeniowa DFT to O(N?) czyli wraz ze zwiększającą się ilością danych wejściowych (długością bufora) szybko rośnie ilość potrzebnych operacji (czyli czas wykonania transformacji). FFT (fast Fourier transform) posiada bardziej skomplikowaną implementację ale jej złożoność obliczeniowa jest mniejsza O(N*log2(N)) co pozwala na szybsze wykonanie przekształcenia dzięki mniejszej liczbie potrzebnych do wykonania operacji. Ilość danych wejściowych dla FFT to potęga 2 (np. 8,16,32,64,128 itd.).
    Aby dodać bibliotekę FFT do środowiska Arduino wybieramy:
    Skic->dołącz bibliotekę->zarządzaj bibliotekami
    wyszukujemy "fft" i wybieramy arduinoFFT.
    Sygnałem wejściowym dla transformacji będą próbki z przetwornika ADC 12b w ESP32. Próbki będziemy pobierali z częstotliwością 40kHz i trafią one do bufora o długości 128 próbek. Próbkowanie z częstotliwością 40kHz pozwoli na analizę sygnałów wejściowych o częstotliwości do 20kHz (częstotliwość Nyquista). Czyli górna granica częstotliwości sygnału który będziemy mogli analizować wynosi połowę częstotliwości próbkowania. Napięcie które możemy podać na ADC mieści się w przedziale 0-3.3V, na etapie testów sygnał podamy poprzez rezystor 10kom. Pobieranie kolejnych próbek z ADC będziemy wyzwalali timerem. Gdy uzbieramy 128 próbek uruchomimy FFT i wyświetlimy dane na wyświetlaczu. Dane w buforze próbek przed wykonaniem FFT modyfikowane są przez funkcję okna czasowego (np. okno Hanninga, Hamminga itp.) zmniejsza to przeciek widmowy a w praktyce polepsza to jakość danych jakie uzyskamy. Operację mnożenia sygnału przez okno czasowe można porównać do np. łagodnego "podgłaśniania" amplitudy na początku utworu i "wyciszania" na jego końcu.
    Wynikiem FFT będą dwa bufory danych, w każdym z buforów długość użytecznych dla nas danych będzie dwukrotnie mniejsza niż długość bufora wejściowego z próbkami z ADC. Kolejne pary danych z buforów będą częściami rzeczywistymi i urojonymi liczb zespolonych. Graficznie wynik przekształcenia można przedstawić tak:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    W zerowym elemencie tablic wyjściowych znajdują się informacje o składowej stałej sygnału, w kolejnych elementach tablicy liczby zespolone (szare strzałki pokazują pary Re, Im). Liczby zespolone opisują kolejne pasma częstotliwości badanego sygnału rzeczywistego. Górna częstotliwość badanego sygnału w przykładzie wynosi 20kHz, mamy 64 liczby zespolone, czyli szerokość każdego z pasm wynosi 312.5Hz. Mamy 64 punkty określające sygnał w dziedzinie częstotliwości, pierwszy element odnosi się do częstotliwości 312.5Hz, kolejny 625Hz itd.
    Warto w tym miejscu przypomnieć, że:
    -częstotliwość próbkowania Fs wyznacza maksymalną częstotliwość (Fs/2) jaką możemy analizować w sygnale wejściowym
    -długość bufora N wpływa na ilość punktów FFT (N/2) co określa rozdzielczość z jaką badamy sygnał w dziedzinie częstotliwości
    -długość bufora wpływa na ilość potrzebnych do wykonania operacji, czyli czas wykonania FFT.
    Liczby zespolone przedstawimy graficznie, odkładając składową rzeczywistą na osi X, a składową urojoną na osi Y. Przykładowa liczba zespolona z=3+4i będzie wyglądać tak:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Moduł z liczby zespolonej (długość strzałki) niesie informację o mocy (amplitudzie) na danej częstotliwości analizowanego sygnału. Moduł przykładowej liczby "z" można obliczyć z tw. Pitagorasa (pierwiastek z sumy kwadratów przyprostokątnych) i otrzymamy |z|=5 (długość przeciwprostokątnej). Kąt między "strzałką" a osią X informuje o fazie sygnału i można go obliczyć arctg(Im/Re). W taki sposób przeszliśmy z dziedziny czasu do dziedziny częstotliwości.
    Wyświetlacz LCD obsłużymy poprzez dołączenie do środowiska Arduino biblioteki U8g2:
    Skic->dołącz bibliotekę->zarządzaj bibliotekami
    wyszukujemy ST7565 i wybieramy U8g2.
    Wykorzystany monochromatyczny wyświetlacz graficzny LCD COG 128x64px oparty jest o sterownik ST7565 i komunikuje się z wykorzystaniem magistrali SPI. Niestety zastosowana wersja LCD-AG-C128064CF 128*64 ST7565R wymaga podłączenia zewnętrznych kondensatorów 1uF, ale dostępne są moduły z kondensatorami i wyprowadzeniami SPI na płytce. Z biblioteką U8g2 nie musimy ograniczać się do jednego rodzaju sterownika czy interfejsu, zmieniając ustawiania inicjalizacji wyświetlacza łatwo dostosujemy kod programu do innego typu wyświetlacza https://github.com/olikraus/u8g2/wiki/u8g2setupcpp . Dla posiadanego LCD konfiguracja wygląda tak:
    U8G2_ST7565_NHD_C12864_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 4, /* reset=*/ 16);
    Poniżej schemat wyprowadzeń wyświetlacza i sposób podłączenia kondensatorów:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Podłączenie do ESP32 możemy wykonać na płytce stykowej.
    Badany sygnał wejściowy podajemy na G34 (wejście ADC) pamiętając o dopuszczalnym zakresie napięć 0-3.3V
    Napięcie zasilania dla LCD pobieramy z wyprowadzenia 3.3V podobnie jak masę GND.
    Przycisk boot (G0) będzie przełączał funkcje programu (dziedzina czasu, FFT moce, FFT faza).
    SPI wyprowadzone jest na G18 (CLK), G23 (DATA), G5 (CS).
    Dodatkowo linie sterujące LCD G16 (RST) oraz G4 (D/C).
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Całość po podłączeniu na płytkach stykowych wygląda tak:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Biblioteka nie do końca dobrze współpracuje z wybranym wyświetlaczem i wyświetlany obraz jest przesunięty w prawo (widoczne losowe kropki w pionowym pasku wyświetlacza po lewej). Zaletą wyświetlacza jest jego duży rozmiar co poprawia czytelność. Jeżeli wiesz jak wyeliminować opisany efekt, napisz proszę w tym temacie.
    (Problem przesunięcia rozwiązany dzięki podpowiedzi piotr_go szczegóły poniżej.)
    Dziedzina czasu jest dobrze znana każdemu kto korzystał z oscyloskopu. Na początek sprawdźmy czy pobieranie próbek działa prawidłowo oraz czy uda się wyświetlić zawartość bufora w postaci graficznej na LCD. Tak wygląda efekt działania programu w trybie dziedziny czasu na zdjęciu oraz filmie:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino. FFT w praktyce z wykorzystaniem ESP32 i Arduino.



    Dla prostych przebiegów okresowych można oszacować "na oko" częstotliwość, fazę i amplitudę, dla bardziej złożonych sygnałów byłoby to trudne. Na wejście układu zostały podane sygnały z dwóch generatorów, oba sygnały zostały podane przez osobne rezystory 10kom na wejściu ADC mamy sumę sygnałów z obu generatorów, pofalowana sinusoida do efekt podania dwóch sygnałów sinusoidalnych o różnych częstotliwościach.
    Dziedzina częstotliwości to funkcja wyświetlania gdzie wykorzystamy wyniki działania FFT. Na zdjęciu poniżej widać "prążki" na częstotliwościach odpowiadających częstotliwościom sygnałów podawanych na wejście ADC. Na filmie zobaczycie zmniejszanie częstotliwości sygnału na wyjściu jednego z generatorów, następnie zmniejszanie amplitudy sygnału na wyjściu drugiego generatora. W prawym dolnym rogu widoczny podgląd na ekran oscyloskopu (dziedzina czasu).
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.



    Faza sygnału na określonej częstotliwości jest możliwa do obliczenia podobnie jak amplituda zgodnie z opisem w części o liczbach zespolonych. Znacznie bardziej popularne jest określanie amplitudy (mocy) sygnału na określonej częstotliwości. Spróbujmy narysować wykres fazowy w zależności od częstotliwości. Trzeba pamiętać o tym że sygnał wejściowy nie jest zsynchronizowany z próbkowaniem (płyniecie sinusoidy w trybie prezentacji w dziedzinie czasu). Podobnie na wykresie fazowym wartość fazy będzie się stopniowo zmieniać w miarę jak sinusoida będzie "płynąć" i będzie zmieniać się faza początkowa. Druga sprawa to skończona dokładność reprezentacji liczb w systemie komputerowym oraz fakt, że arctg będzie obliczany ze stosunku dwóch liczb (często prezentujących szum). Liczby zespolone o małej wartości modułu (szum, pomijalne dane) mogą generować informacje o fazie podobne do liczb o dużej wartości modułu (częstotliwości gdzie moce sygnału są większe i dane są istotne). W przeciwieństwie do wartości modułu z liczby zespolonej, przesunięcia fazowe nie pokażą nam gdzie występuje sygnał o większej amplitudzie, dlatego wykres fazowy może przypominać szum. Wprowadźmy obliczanie fazy tylko w punktach gdzie moduł z liczby zespolonej będzie przekraczał określoną wartość progową (np. 1/100 wartości maksymalnej). Poniżej zdjęcie i film prezentujących wykres fazowy wykorzystujący takie "maskowanie" w zależności od wartości modułu liczby zespolonej, na filmie zauważycie ciągłą zmianę wartości fazy, oraz nagranie "płynięcia" sinusa w trybie dziedziny czasu.
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.



    Podłączenie innego wyświetlacza z wykorzystaniem U8g2 ogranicza się do zmiany jednej linijki w programie, oraz podłączeniu wyświetlacza pod odpowiednie wyprowadzenia SPI. Wypróbujmy wyświetlacz OLED ze sterownikiem SSD1306, który został wykorzystany podczas testów cyfrowego czujnika indeksu UV SI1132.
    Zmieniamy linijkę:
    U8G2_ST7565_NHD_C12864_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* dc=*/ 4, /* reset=*/ 16);
    na:
    U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 5, 4, 16);

    Oraz usuwamy linijkę: u8g2.setContrast(255);

    Podłączamy wyświetlacz pod odpowiednie wyprowadzenia modułu ESP32:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Program wyświetla dane na nowym wyświetlaczu OLED:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino. FFT w praktyce z wykorzystaniem ESP32 i Arduino.



    Na wejście układu łatwo możemy podać sygnał z mikrofonu elektrytowego:
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Do FFT istnieje "odwrotny" algorytm IFFT, który pozwala na przejście z dziedziny częstotliwości do dziedziny czasu.
    Poniżej do pobrania kod programu pod Ardino dla ESP32, pamiętajcie o dołączeniu biblioteki arduinoFFT i U8g2 oraz dostosowaniu środowiska Arduino do obsługi ESP32. Kod programu można wykorzystać na płytce innej niż ESP32 po zmianach m.in wyzwalania pobierania próbek oraz konfiguracji ADC.
    Testy przeprowadzone w środowisku Arduino 1.8.3.
    Jaki masz pomysł na wykorzystanie FFT, czy udało się wykonać próby praktyczne, a może masz duże doświadczenie w cyfrowym przetwarzaniu sygnałów i chcesz poruszyć tematy związane z DSP?

    Cool? Ranking DIY
    Do you have a problem with Arduino? Ask question. Visit our forum Arduino.
    About Author
    TechEkspert
    Editor
    Offline 
    W moich materiałach znajdziecie testy i prezentacje sprzętu elektronicznego, modułów, sprzętu pomiarowego, eksperymenty. Interesuje mnie elektronika cyfrowa, cyfrowe przetwarzanie sygnałów, transmisje cyfrowe przewodowe i bezprzewodowe, kryptografia, IT a szczególnie LAN/WAN i systemy przechowywania i przetwarzania danych.
    Has specialization in: mikrokontrolery, rozwiązania it
    TechEkspert wrote 4924 posts with rating 4023, helped 12 times. Been with us since 2014 year.
  • NDN
  • #2
    piotr_go
    DIY electronics designer
    TechEkspert wrote:
    Biblioteka nie do końca dobrze współpracuje z wybranym wyświetlaczem i wyświetlany obraz jest przesunięty w prawo (widoczne losowe kropki w pionowym pasku wyświetlacza po lewej).

    I nie dało się tego ustawić?
    Czy może tak ma być bo to arduino i przestawienie jednego parametru w bibliotece to czarna magia?
  • NDN
  • #3
    TechEkspert
    Editor
    Pewnie wszystko da się ustawić, ale nie wgłębiałem się w ST7565. Wyszedłem z założenia że biblioteka ma zwyczajnie działać tak jak zadziałała z SSD1306. W przypadku ST7565 może zawiodła zawarta w bibliotece procedura inicjalizacji, być może problem był z funkcją przygotowującą bufor lub przesyłającą dane do LCD a może ja coś źle robiłem, być może kiedyś powrócę do kwestii wyświetlacza LCD-AG-C128064CF 128*64 ST7565R i U8g2 a może ktoś podpowie tutaj jak rozwiązać ten problem.
  • #4
    piotr_go
    DIY electronics designer
    Te wyświetlacze tak mają, oledy też. Jeden producent zrobi z adresem startowym X = 0, inny X = 2.
    Pokombinuj z wyborem typu wyświetlacza albo wyedytuj bibliotekę.
  • #5
    TechEkspert
    Editor
    Acha rozumiem czyli różnice są po między wyświetlaczami z tej samej grupy, dzięki za informację.
    Sprawdziłem bibliotekę i poprawiłem to przesunięcie,
    rozwiązanie nie jest "przenośne" ale jeżeli ktoś natrafi na problem z biblioteką Arduino U8g2 i przesuniętym w prawo obrazem na LCD-AG-C128064CF 128*64 ST7565R to należy:
    wejść w folder z bibliotekami Arduino, następnie U8g2->src->clib i otwieramy plik u8x8_d_st7565.c
    Wyszukujemy "NHD-C12864" lub u8x8_st7565_nhd_c12864_display_info i zamieniamy:
    /* default_x_offset = */ 4,
    na
    /* default_x_offset = */ 0

    To właśnie lubię na elektroda.pl materiał nie kończy się po postawieniu kropki w pierwszym poście.

    Poniżej efekty (dodałem kropki w każdym rogu aby sprawdzić czy tym razem nic nie zostało zgubione).

    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
  • #6
    WMKN2205
    Level 9  
    FFT w praktyce z wykorzystaniem ESP32 i Arduino.
    Bardzo fajny opis. Już myślę jak to zaimplementować na FreeRTOS'a
    Niestety wyświetlacz(LCD-AG-C128064CF-DIW W/KK-E6) stawia opór :( widać artefakty.
    Użyłem kondesatory 1uF/16V SMD. czy ktoś może coś podpowiedzieć.
    Rezystor szeregowy 56R z podświetleniem, napięcie zasilania 3.28V
  • #7
    TechEkspert
    Editor
    Mam propozycję abyś zmierzył napięcia na poszczególnych wyprowadzeniach LCD (tam gdzie są podłączone kondensatory) i ew. podejrzał występujące przebiegi oscyloskopem.
    Ja mogę porównać u mnie i może coś uda się ustalić.
    Inny pomysł to programowa próba zmiana kontrastu.

    Chętnie zobaczę realizację na RTOS, szczególnie przy eksperymentach z zewnętrznym ADC i np. w wersji dwukanałowej.

    LCD/TFT są teraz zaskakująco tanie w cenie 25-45zł jest już spory wybór wyświetlaczy,
    wspomniany wyświetlacz kosztował ~20zł wadą są kondensatory zewnętrzne, zaleta to duży rozmiar,
    natomiast OLED to koszt ~35zł.
    Ceny nie są wygórowane a dodanie wyświetlacza daje spore możliwości.
  • #8
    WMKN2205
    Level 9  
    Dzięki za szybką reakcję. Podaję napięcia na kolejnych pinach (oscyloskopu chwilowo nie posiadam - się buduje jak widać ;) ):
    1 -10.0V
    2 -9.02V
    3 - 7.7V
    4 - 2.4V
    5 - 0.0V ?? zaraz sprawdzę może coś się odlutowało?
    6 - 0.0 V
    7 - 1.65V
    8 - 6.77V
    9 - 4.33V
    10 -1.71V
    11 - 9.16V
    12 - 10.01V
    13 - 0.17V ? -powinno być GND
    14,15 -3.29V
    Co do ADC to bardziej myślałem o mikrofonach MEMS na I2S ale nie widziałem sensownych modułów w Polsce.
    Jeśli się okaże że z LCD coś nie tak to mam plan "B" 3,2" na ILI9340.
  • #9
    TechEkspert
    Editor
    Dzisiaj sprawdzę i dam znać.
    Mam pomysł na kontynuowanie tematu DSP, także zapraszam do komentowania i podsuwania pomysłów, być może wspólnie uda się wytworzyć coś ciekawego.

    Co do mikrofonów MEMS I2S są łatwe w wykorzystaniu jednak dość drogie i mają ograniczoną dostępność, warto byłoby rozpracować MEMS PDM są znacznie tańsze i z lepszą dostępnością (nie wiem jak parametry).

    Tak wyglądają pomiary:
    1: 9.45V
    2: 8.40V
    3: 7.34V
    4: 2.12V
    5: 1.07V

    7: 1.60V (1.26khz 50%)
    8: 7.45V (1.26khz 50%)
    9: 4.58V (1.26khz 50%)
    10: 1.60V (1.26khz 50%)
    11: 10.30V (1.26khz 50%)
    12- 11.60V

    14,15: 3.22V
  • #10
    WMKN2205
    Level 9  
    Dzięki wygląda podobnie oprócz "5"
    Myślę że namieszałem trochę w "8x8_d_st7565_nhd_c_init_seq[]" trochę eksperymentowałem:
    static const uint8_t u8x8_d_st7565_nhd_c12864_init_seq[] = {
    
    U8X8_START_TRANSFER(), 	/* enable chip, delay is part of the transfer start */
    
    U8X8_C(0x0e2), 			/* soft reset */
    U8X8_C(0x0ae),		 /* display off */
    U8X8_C(0x040),		 /* set display start line to 0 */
    
    //U8X8_C(0x0a0),		 /* ADC set to reverse */
    //U8X8_C(0x0c8),		 /* common output mode */
    //U8X8_C(0x0a1),		 /* ADC set to reverse */
    //U8X8_C(0x0c0),		 /* common output mode */
    // Flipmode
    U8X8_C(0x0a0),		 /* ADC set to reverse */
    U8X8_C(0x0c0),		 /* common output mode */
    
    U8X8_C(0x0a6),		 /* display normal, bit val 0: LCD pixel off. */
    U8X8_C(0x0a3),		 /* LCD bias 1/9 */
    U8X8_C(0x02f),		 /* all power control circuits on */
    U8X8_CA(0x0f8, 0x000),		/* set booster ratio to 4x */
    U8X8_C(0x023),		 /* set V0 voltage resistor ratio to large*/
    U8X8_CA(0x081, 0x03f),			/* set contrast, contrast value NHD C12864, see issue 186, increased contrast to 180 (issue 219) */
    
    U8X8_C(0x0ae),		 /* display off */
    U8X8_C(0x0a5),		 /* enter powersafe: all pixel on, issue 142 */
    
    U8X8_END_TRANSFER(), 	/* disable chip */
    U8X8_END() 			/* end of sequence */
    };
    


    Dodano po 1 [godziny] 39 [minuty]:

    Chwilo się poddałem i wdrażam plan "B":
    Wyświetlacz dla Raspberry Pi TFT 3.2" 320x240 touch panel (~75pln) + ESP32
    Pierwszy test pozytywny - jak połączę z kodem FFT to wrzucę.
  • #11
    TechEkspert
    Editor
    W nowej wersji biblioteki ten fragment kodu wygląda tak:
    Code: c
    Log in, to see the code


    Wyświetlacz TFT 3.2" na ILI9340 z interfejsem dotykowym to już niestety większy koszt (x2) niż wspomniane monochromatyczne wyświetlacze za 20-30zł, ale możliwości też większe.

    Ten interfejs dotykowy jest rezystancyjny czy pojemnościowy, posiada jakiś kontroler potrafiący przesyłać informacje o zdarzeniach (dotknięcie panelu, gest itp.) ?
  • #12
    WMKN2205
    Level 9  
    Rezystancyjny kontroler XPT2046 jeszcze nie sprawdzałem co potrafi, ma wyjście detekcji dotyku.