Zostań w domu, zamów taniej!
Nie wychodź z domu i zamów online swoje ulubione pisma 20% taniej. Skorzystaj z kodu rabatowego: czytajwdomu

Eksperymenty z FPGA (10). Podłączamy mikrofon

Eksperymenty z FPGA (10). Podłączamy mikrofon
Pobierz PDF Download icon

W tym odcinku wracamy do przetwornika ADC. Jednak zamiast potencjometru podłączymy do niego mikrofon. Dzięki temu wykonamy eksperymenty z cyfrowego przetwarzania sygnałów. Przed przystąpieniem do pracy jak zwykle przypominam o aktualizacji repozytorium z przykładami (poprzez wywołanie polecenia git pull).

Spis treści

Do eksperymentów zastosujemy popularny mikrofon elektretowy. Niestety zmiany napięcia na jego wyjściu są zbyt małe, aby można je było zmierzyć za pomocą naszego przetwornika ADC. Musimy więc zbudować prosty przedwzmacniacz. Użyjemy do tego celu wzmacniacza operacyjnego LM358 i kilku elementów pasywnych. Schemat układu został pokazany na rysunku 1.

Rysunek 1. Schemat podłączenia mikrofonu

Rezystory R3 i R4 tworzą dzielnik napięcia ustalający „sztuczną masę” dla naszego przedwzmacniacza. Kondensator C1 usuwa składową stałą z wejścia wzmacniacza operacyjnego. Zmieniając wartość R5, możemy regulować wzmocnienie. Na wyjściu znajduje się prosty filtr RC złożony z R6 i C2. Jego częstotliwość graniczna to:

Sygnał podłączamy do analogowego wejścia A0 płytki Rysino. Musimy jeszcze zatroszczyć się o niewykorzystaną połowę LM358. Wyjście drugiego wzmacniacza zostało połączone z jego wejściem odwracającym, natomiast wejście nieodwracające zwarto do masy. Realizacja na płytce stykowej jest pokazana na fotografii 1.

Fotografia 1. Układ eksperymentalny zbudowany na płytce stykowej

Działanie układu najłatwiej sprawdzić za pomocą oscyloskopu. Na rysunku 2 widoczna jest rejestracja mowy. Widać na nim, że sztuczna masa naszego układu odpowiada napięciu około 1,3 V.

Rysunek 2. Napięcia na wejściu ADC przy rejestracji mowy

Natomiast rejestrowane sygnały znajdują się w przedziale napięciowym 1,15 V…1,45 V. Aby sprawdzić, przy jakich wartościach następuje nasycenie, na rysunku 3 pokazano zapis klaśnięcia. Wynika z niego, że roboczy zakres napięciowy zawiera się pomiędzy wartościami około 0,05 V a 2 V.

Rysunek 3. Napięcia na wejściu ADC przy rejestracji klaśnięcia

Rejestracja dźwięku

Kiedy mamy już gotowy układ doświadczalny, możemy zabrać się do przygotowania wsadu dla układu FPGA. Naszym pierwszym celem będzie wysłanie pomiarów z przetwornika ADC przez port szeregowy. Uproszczony schemat tego modułu pokazuje rysunek 4.

Rysunek 4. Schemat rejestratora dźwięku

Aby nie komplikować projektu, będziemy przesyłać jedynie 8 bitów danych. Musimy także zdecydować się na częstotliwość próbkowania. Przyjmuje się, że w mowie występują częstotliwości do około 3 kHz. Oznacza to, że wystarczyłoby próbkować z częstotliwością około 6 kHz. Jednak nie możemy wybrać dowolnej wartości. Ich lista jest dostępna w dokumentacji [1].

Kiedy wybierzemy 50 kHz, możemy łatwo otrzymać próbkowanie z częstotliwością 10 kHz poprzez zwykłe odrzucanie czterech i przepuszczanie co piątej wartości. Uzyskamy to przez wygaszanie sygnału valid. Czynność ta spowoduje, że dane mimo że przejdą przez magistralę, nie zostaną wysłane. Wykorzystamy tu dobrze nam znany licznik modulo. Do przesłania dziesięciu tysięcy 8-bitowych pomiarów, po doliczeniu bitów startu i stopu, uzyskamy minimalną prędkość transmisji równą 100 kbd. Możemy więc skonfigurować UART do standardowej prędkości 115,200 kbd, tak jak w poprzednich eksperymentach, i nadal będziemy mieli pewien zapas.

Rysunek 5. Konfiguracja przetwornika ADC

Budowę rozpoczniemy od wygenerowania bloku IP, obsługującego przetwornik analogowo-cyfrowy. Konfigurację pokazuje rysunek 5. Ustawiamy tam wybraną przez nas częstotliwość próbkowania. Zegar taktujący ADC ustawiamy na 2 MHz (będziemy musieli także odpowiednio skonfigurować pętlę PLL). Tym razem włączamy tylko kanał numer 1.

Listing 1. Kod modułu nagrywającego (10_record/rec.sv)
30 adc adc_inst (
31 .clock_clk(clk),
32 .reset_sink_reset_n(rst),
33 .adc_pll_clock_clk(clk_adc),
34 .adc_pll_locked_export(clk_adc_locked),
35 .command_valid(1’b1),
36 .command_channel(1’b1),
37 .command_startofpacket(1’b0),
38 .command_endofpacket(1’b0),
39 .command_ready(),
40 .response_valid(adc_valid_out),
41 .response_channel(),
42 .response_data(adc_data_out),
43 .response_startofpacket(),
44 .response_endofpacket());

50 assign bus.data = adc_data_out[11-:8];
51
52 counter #(.N(5)) every5 (
53 .clk(clk),
54 .rst(rst),
55 .ce(adc_valid_out),
56 .q(),
57 .ov(ce_5));
58
59 assign bus.valid = ce_5 & adc_valid_out;
60
61 uart_tx #(
62 .F(F),
63 .BAUD(115200)
64 ) uart (
65 .bus(bus),
66 .tx(tx));

Główny moduł naszego projektu pokazuje listing 1. W liniach 30…44 mamy instancję modułu ADC. W tym przypadku konfiguracja magistrali wejściowej jest bardzo prosta, ponieważ chcemy czytać tylko jeden kanał z maksymalną częstotliwością. Wpisujemy numer kanału w sygnale command_channel i ustawiamy command_valid na 1. W linii 50 ustawiamy wysyłanie tylko ośmiu najstarszych bitów. Dalej znajduje się moduł licznika, który zlicza kolejne pomiary z przetwornika, a dla co piątej próbki następuje ustawienie sygnału ce_5. Linia 59 przygotowuje sygnał wyzwalający transmisję. Musimy zastosować bramkę AND, ponieważ sygnał przepełnienia licznika pozostanie aktywny, aż zakończy się następny pomiar. Mogłoby to spowodować kilkukrotne wysłanie pojedynczej próbki. Teoretycznie nie powinno tak się zdarzyć, ponieważ wysłanie jednego bajtu zajmuje więcej czasu niż wynosi odstęp pomiędzy pomiarami przetwornika. Na samym końcu umieszczony został moduł nadajnika portu szeregowego.

Kiedy mamy już gotowy kod, możemy otworzyć w środowisku Quartus projekt 10_record/10_rec.qpf i rozpocząć jego budowę. Po wgraniu bitstreamu do układu FPGA przechodzimy do odczytywania danych. W tym celu można wykorzystać znany nam już program RealTerm. Aby wyświetlane dane były bardziej czytelne, możemy w zakładce Display wybrać opcję uint8 (unsigned int 8 – 8-bitowa liczba całkowita bez znaku), co pokazuje rysunek 6.

Rysunek 6. Wybór sposobu wyświetlania danych

Kiedy powiemy coś do mikrofonu, dane powinny się zmieniać, ale pojawiają się zbyt szybko, by zaobserwować to wyraźnie na ekranie konsoli. Dlatego zapiszemy odbierane informacje w pliku tekstowym do późniejszej analizy. W tym celu otwieramy zakładkę Capture (przechwytywanie), co zaprezentowano na rysunku 7.

Rysunek 7. Przechwytywanie danych w programie RealTerm

Następnie wybieramy plik docelowy i zaznaczamy opcję Capture as Hex (przechwytuj w formacie szesnastkowym). Rozpoczynamy naciśnięciem Start: Overwrite (zacznij: nadpisz), mówimy do mikrofonu i klikamy przycisk Stop Capture (zatrzymaj przechwytywanie).

Wykonane przeze mnie przykładowe nagranie znajduje się w pliku 10_record/parse/voice.txt. Kiedy go otworzymy, zobaczymy jedną, długą linię bez żadnych przerw:

656565656565

Każde dwa kolejne znaki odpowiadają kolejnym odebranym bajtom. Są one zapisane w formacie szesnastkowym. Aby je zdekodować oraz zmienić na format dźwiękowy, przygotujemy krótki skrypt w języku Python. Do jego uruchomienia wykorzystam notatnik Jupyter. Można go pobrać razem z pakietem Anaconda [2]. Domyślnie notatnik jako swój główny katalog uznaje folder użytkownika. Jeżeli przechowujemy pliki projektu w innym miejscu, można uzyskać do nich dostęp poprzez stworzenie dowiązania do wybranego katalogu, na przykład wywołaniem w wierszu poleceń komendy:

mklink /D c c:\

Powyższe działanie spowoduje stworzenie dowiązania c, które wskazuje na dysk C:\. Parametr /D informuje, że mamy do czynienia z katalogiem. Teraz możemy załadować notatnik 10_record/parse/Parse.ipynb. Zawarty w nim kod pokazuje listing 2.

Listing 2. Skrypt w języku Python parsujący odebrane dane (10_record/parse/Parse.ipynb)
01 %matplotlib inline
02
03 import matplotlib
04 import numpy as np.
05 import matplotlib.pyplot as plt
06 from scipy.io.wavfile import write
07
08 def pairwise(iterable):
09 "s -> (s0, s1), (s2, s3), (s4, s5), …"
10 a = iter(iterable)
11 return zip(a, a)
12
13 file="voice.txt"
14 with open(file) as f:
15 data = f.read()
16 x = []
17 for a,b in pairwise(data):
18 s = a + b
19 x.append(int(s, 16))
20 xn = np.uint8(x)
21
22 plt.plot(xn)
23 plt.xlabel("czas, próbki")
24 plt.ylabel("amplituda, j.w.")
25
26 write(‘voice.wav’, 10000, xn)

W pierwszej linii konfigurujemy wyświetlanie wykresów. Następnie ładujemy niezbędne biblioteki. Matplotlib umożliwi nam tworzenie wykresów, a Numpy pozwala na wykonywanie obliczeń numerycznych. Scipy jest rozbudowaną biblioteką ułatwiającą obliczenia naukowe. Wykorzystamy z niej jedynie moduł, pozwalający zapisywać pliki w dźwiękowym formacie wav. W liniach 8…11 znajdziemy funkcję pairwise, która ułatwi nam wczytywanie dwóch kolejnych znaków.

Wiersz 13 zawiera nazwę pliku z zapisanymi danymi. Otwieramy go i wczytujemy zawartość do zmiennej data. Następnie w pętli wczytujemy po dwa znaki i konwertujemy je na liczbę. W linii 20 konwertujemy otrzymaną tablicę na wektor liczb 8-bitowych bez znaku. Na końcu wyrysowujemy wykres z zebranych danych i zapisujemy plik dźwiękowy voice.wav.

Rysunek 8. Wykres wygenerowany z zebranych danych

Uzyskany wykres pokazuje rysunek 8. Sygnał oscyluje wokół liczby 100. Jego wartości mieszczą się w przedziale 80…115. Oznacza to, że większość z dostępnego nam zakresu (0…255) jest niewykorzystana. Używamy jedynie przedziału o szerokości poniżej 35. Odpowiada on nieco ponad pięciu bitom danych, przy czym przesyłamy ich aż osiem. W dalszej części spróbujemy poprawić ten wynik.

Sposób zbierania danych oraz konwersji ich do pliku tekstowego został także przedstawiony na filmie [3].

Artykuł ukazał się w
Elektronika Praktyczna
wrzesień 2020
DO POBRANIA
Pobierz PDF Download icon

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik luty 2021

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio luty 2021

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka Podzespoły Aplikacje styczeń 2021

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna luty 2021

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Praktyczny Kurs Elektroniki 2018

Praktyczny Kurs Elektroniki

24 pasjonujące projekty elektroniczne

Elektronika dla Wszystkich styczeń 2021

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów