Projekt UCYF – FILTR CYFROWY
Transkrypt
Projekt UCYF – FILTR CYFROWY
Projekt UCYF – FILTR CYFROWY Prowadzący dr inż. Paweł Tomaszewicz Autorzy Piotr Kowalski - 4T2 Jakub Jachimowicz - 4T3 Data 14 stycznia 2005r. 1. Wprowadzenie i założenia wstępne: Przedmiotem naszych działań było zaprojektowanie w języku AHDL filtru cyfrowego czystego formatu dźwięku (RAW) oraz zademonstrowanie jego działania poprzez uruchomienie gotowego projektu w laboratorium na płytce firmy ALTERA. Wstępnie zdecydowaliśmy się na realizację układu, który będzie tłumił częstotliwości powyżej 2[kHz]. Jest to o tyle istotne, iż wycięcie takiego pasma spowoduje zauważalne zmiany w odsłuchu przefiltrowanego pliku. Jakkolwiek dyskusyjna jest już tutaj kwestia, czy takie pasmo przepustowe ma jakiekolwiek zastosowanie praktyczne. W konfrontacji z pasmem telefonicznym ( 300[Hz] – 3,4[kHz]), możemy liczyć na otrzymanie gorszej jakości dzwięku z uwagi na szerokość pasma. Fakt, iż ucho ludzkie najlepiej reaguje na częstotliwości w otoczeniu 2[kHz], decyduje raczej o tym, że lepszego kompromisu pomiędzy szerokością pasma i zadowalającą jakością dzwięku osiągnąć się nie da. Na rysunku obok pokazano wstępny schemat układu, który zamierzaliśmy zrealizować. Początkowo zajęliśmy się specyfikacją formatu RAW. Otórz jest to, jak było już wspominane, tzw. surowy format zapisu. Po wyedytowaniu przykładowego pliku *.raw, zobaczyliśmy jego strukturę. Są to zwyczajne wartości HEX kolejnych słów (sampli): – dla 16-bit STEREO: [L] [L] [R] [R] – dla 8 bit MONO: [P] [P] [P], gdzie symbole L, R, P stanowią zwykłe liczby szesnastkowe postaci np: FF, 2B, 4C... Specyfikacja format WAV znajduje się na stronie: http://home.elka.pw.edu.pl/~pkowals3/wave.html Zatem ciąg słów szesnastkowych daje nam informację o wysokości napięcia poszczególnych próbek, przy czym warto tu zaznaczyć, iż umownie w plikach komputerowych przyjmuje się wartości dodatnie. Jednak w rzeczywistości odtwarzacze dzwięku interpretują liczby ze znakiem, gdyż w przeciwnym razie przekształcenie napięcia z dodatnich próbek spowodowałoby spalenie głośnika po podaniu ciągu próbek tego samego znaku. Membrana głośnika wychyla się bowiem właśnie odpowiednio proporcjonalnie co do wartości prostego sample'a – tak powstają drgania membrany. Opis ewolucji układu jak i końcowy rezultat naszych zmagań przedstawiamy w dalszej części niniejszej specyfikacji. Dlaczego wybraliśmy właśnie filtr SOI? Początkowe, przeszukiwania Internetu w celu zapoznania się z tematem projektu dawały różne rezultaty. Stopniowo jednak spośród możliwych realizacji filtr FIR (Finite Impulse Response) okazał się być właściwy do naszych celów, choć rozwiązaniem idealnym nie jest. Trudniejszy w implementacji filtr NOI – IIR (Infinitive Impulse Response), jest jednak mniej złożony, gdyż realizacja niższego stopnia daje lepsze rezultaty niż dużo bardziej złożony (wyższego rzędu) filtr FIR. W wyniku zastosowania pętli zwrotnej, filtr ten jakby sam się poprawia w nieskończonej liczbie kroków, zatem nie potrzebuje wielu rzędów, aby uzyskać żądaną transmitancję. Jednym z zamysłów była również implementacja Szybkiej Transfromaty Fouriera (FFT), ale złożoność problemu nas przerosła. Wybraliśmy zatem filtr SOI rzędu szóstego, gdyż uzyskana charakterystyka (patrz dalej) jest zadowalająco dokładna i przy niewielkiej powierzchni układu uda nam się z pewnością uzyskać dobre wyniki. Na wejście filtru podawać będziemy plik MONO 8-bits z pewną częstotliwością próbkowania. Problemy i trudności realizacyjne: Akumulacja: Kwestia zasugerowana przez prowadzącego. Dotyczy przypadku, kiedy po wymnożeniu i zsumowaniu poszczególnych składowych, otrzymujemy wartość próbki o ilczbie bitów większej niż rozmiar wyjściowy. Może to mieć miejsce np. podczas przetwarzania częstotliwości, dla których wartość transmitancji zbliża się do jedynki bądź ją nieznacznie przekracza. W naszym przypadku sytucja taka grozi nam w okolicach zera częstotliwości. Siłą rzeczy nie da się zapisać liczby dziesięcio-bitowej na ośmiu bitach, zatem należało rozwiązać ten problem poprzez stworzenie układu, który nie pozwalał na zwrócenie próbek większych niż ośmiobitowe. Wszystkie liczby większe od 255 były automatycznie sprowadzane do tej wartości – maksymalnej, jaką da się zapisać na 8 bitach. (patrz: plik akumulacja.tdf). Jakkolwiek, sposób wymnażania próbek z uwagi na dokładność (patrz: opis układu) i zaokrąglania przez ALTERĘ wartości próbek, sprzyja przekraczaniu zakresu choć nie spotkaliśmy się z taką sytuacja podczas symulacji. Z pewnością dlatego, że próbki nie były zbyt przesterowane, nie miały wartości bliskich maksymalnej. Znak ujemnych współczynników: W celu uproszczenia układu, współcyznniki filtru dobraliśmy jedynie dodatnie. Metodą 'prób i błędów' symulowaliśmy różne transmitancje tak długo, aż natrafiliśmy na dodatnie mnożniki poszczególnych gałęzi filtru. Takie podejście upraszcza układ, a przede wszystkim bardziej przemawia do wyobraźni. Błędy kompilacji: Najbardziej problematyczny okazał się układ mnożący, z którym mieliśmy podczas kompilacji spore kłopoty. Powodem początkowych niepowodzeń był fakt ignorowania przez kompilator rzekomo nieużywanych wejść układu. W istocie, teoretycznie dane te nie były modyfikowane w ciele modułu, jednak były nam potrzebne ze względu na ośmiobitowe wejście filtru. Zignorowanie tych wejść zaburzyłoby komunikację z portem USB, przez który przychodzą paczki 8-botowych danych. Efektem ubocznym rozwiązania w/w problemu jest dodatkowe wyjście 7-bitowe układu, pod którego kolejne piny warunkowo podpieliśmy bitowe “1”. Nieco sztuczne i niezbyt eleganckie rozwiązanie, ale działa. Być może owe wyjście posłuży w przyszłości do rozbudowy układu. Problem wykorzystania bitów nie wydaje nam się trywialny, gdyż kompilator języka AHDL nie wykonuje danych sekwencyjnie, instrukcja po instrukcji. Na podstawie kodu buduje on mianowicie fizyczny układ opisany właśnie w języku AHDL. Podejście typowo programistyczne w tym wypadku utrudnia wyraźnie uzmysłowienie sobie, że to nie takie proste jak w #C. W języku VHDL byłoby to już rozwiązane inaczej, gdyż tam instrukcje bloku wykonywane są sekwencyjnie – instrukcja po instrukcji. 2. Zaprojektowanie filtru w programie Matlab: Przy pomocy funkcji remez modelujemy filtr szóstego rzędu. Wektory wejściowe funkcji remez to w naszym przypadku: N = 6 % rząd filtru % [F] = [0 0.15 [A] = [0.95 0.9 [B] = remez (N,F,A); 0.4 0.2 0.6 0.15 0.9 0.1 1] 0.05] Wektor [F] opisuje względną częstotliwość, natomiast wektor [A] to ta część amplitudy przenoszona dla danej składowej. Transmitancja filtru o podanych wyżej zależnościach będzie następująca: Transmitancja filtru SOI 6-rzędu Pasmo 3-dB kształtuje się tu zatem na poziomie 1 [kHz]. Odpowiedź filtru na sygnał o składowych 1[kHz] oraz 3[kHz] daje odpowiedź postaci: (poniżej widmo amplitudowe) Zielonym kolorem zaznaczono widmo po przejściu przez filtr. Wyższa składowa wyraźnie wycięta. Podobny efekt będziemy chcieli uzyskać w naszym układzie. Funkcja remez daje nam następujące współczynniki: 0.0232 0.1064 0.2010 0.3475 0.2010 0.1064 0.0232 3. Budowa poszczególnych modułów układu: A) Układ wejściowy: Składa się z siedmiu rejestrów, z których każdy zawiera w sobie po osiem przerzutników. Poszczególne rejestry są połączone ze sobą szeregowo i od każdego odchodzi gałąź do układu mnożącego, skąd wszystkie gałęzie spotkają się w sumatorze, który da końcową próbkę. Schemat układu wejściowego filtru SOI 6-rzędu. Powyżej widzimy łańcuszek rejestrów, które przechowują próbki, jakie przyszły z zewnątrz do układu filtru. Sygnał Enable jest podany do przerzutników jako sygnał zegarowy i wraz z narastającym zboczem następuje wpis do przerzutnika/rejestru. Po następnym takcie następuje już jednoczesne przejście sygnału na wyjście oraz wpis do kolejnego w kolejce rejestru. Sygnał przechodzi więc cyklicznie przez wszystkie rejestry połączone szeregowo, a w tajemnicy opóźnień tkwi serket filtracji. B) Układ mnożący: Jest to chyba najciekawszy fragment układu filtru, który zaprojektowaliśmy. W języku AHDL ciężko jest zrealizować operację mnożenia przez liczbę ułamkową. Nie korzystając z funkcji bibliotecznej posłużyliśmy się operacją przesuwu w prawo uzyskując dzielenie. Przy przesunięciu w prawo o 1 bit liczba dzieli się przez 2, gdy przesuniemy o 2 bity w prawo, to mamy 4 razy mniejszą liczbę itd. Operację takiego dzielenia wykorzystaliśmy do mnożenia przez liczby nie większe niż jeden. Dla liczby 8-bitowej maksymalny przesuw w prawo (lub w lewo) to 7 bitów. Daje nam to liczbę będącą wynikiem mnożenia przez 0.0078125, ponieważ ten ułamek to 1/128 - wynik przesunięcia w prawo o 7 bitów. Poprzez sumowanie takich cząstkowych mnożeń będących efektem elementarnych przesunięć, dostajemy prawie idealnie dopasowane przybliżenie współczynnika filtru. Przykład: Chcąc wymnożyć liczbę 240 przez współczynnik 0.3475 postępujemy tak: – szukamy kombinacji liniowej ułamków ½, ¼,... takiej, aby ich suma była jak najbliższa współczynnikowi oraz liczba ułamków była najmniejsza (chcemy układ minimalny) 0.3475 da sie rozłożyć na sumę: ¼ + 1/16 +1/32 = 11/32 = 0.3437 Liczba 240 binarnie: 11110000, a mnożenie dokładne daje wynik 83.4 AHDL da po sumie trzech przesunięć wynik: 00111100 + 00001111 + 00000111 = 60 + 15 + 7 = 82. Błąd jest zatem jednostkowy! Im więcej składników owej kombinacji liniowej, tym wyniki będą gorsze, ale dla 8 bitów nie będzie już gorzej. Poniżej zamieszczamy fragment kodu: Strzałkami zaznaczono nadmiarowy sygnał wyjścia, który trzyma fikcyjne sygnały tylko po to, by program kompilatora nie ignorował wejść. Układ sumatora oparty jest na sześciu symbolach bibliotecznych add_sub_lpm. Warto jeszcze wspomnieć, że układ mnożący oddaje o 2 bity więcej niż przyjął i podaje je też na sumator. Powód nadmiaru opisany był na początku. Funkcją biblioteczną mógłby być także rejestr, jednak w ramach oszczędności miejsca wygenerowaliśmy go w następujący sposób: Kod układu rejestru (z tablicą ośmiu przerzutników) Na wyjściu sumatora pracuje układ akumulatora, który redukuje 10-bitowe dane do ośmiu bitów. Kod układu do kontroli nasycenia próbek (8-bitowych danych) Układ zajął 182 logiczne komórki układu FLEX 10K i nie wniósł bardzo dużych opóźnień. Na ostatniej stronie układ filtru w całości oraz symulacja filtrowania. Układ filtru SOI 6-rzędu zrealizwanego w języku AHDL. Symulacja układu filtru cyfrowego. 4. Programowanie płytki laboratoryjnej: Po podłączeniu płytki do portu LPT, należy zainstalować ją w systemie jako port gier używając sterowników z katalogu maxplus2/drivers. Po wybraniu powyższej opcji należy wybrać przycisk configure, a następnie ustawić jako aktywny programator ByteBlaster i wybrać port LPT. Często występują problemy z uaktywnieniem portu LPT – jest on niedostępny. Należy wtedy kombinować włączając w różnej kolejności porty układu płytki laboratoryjnej. Po zaprogramowaniu układu (wcześniej oczywiście należy wybrać układ identyczny z układem FLEX na płytce), Assign -> Device wszystkie 3 zielone diody będą się świecić, a na wyświetlaczach siedmiosegmentowych będą zera. Program do wymiany danych z płytką wygląda następująco. Napisaliśmy go w C++ używają środowiska Borland 6 oraz biblioteki FTD2XX.DLL 5. Obsługa portu USB Pomimo, że główną częścią projektu jest filtr cyfrowy, dolnoprzepustowy zaprojektowany w systemie Max Puls+2 amerykańskiej firmy Altera, to jednak zawartość jego (filtru) jest dołączona do pliku uklad.tdf -> patrz załączniki. Dołączony jest także plik wyświetlacza siedmiosegmentowego, który steruje LEDami na płytce. Automat zawarty w/w pliku jest dziewięcio-stanowy – bez niego komunikacja przez port USB byłaby niemożliwa. Najwięcej problemów podczas transmisji sprawił nam właśnie ten układ. Automat wpadał w nieznany nam stan (inny niż zdefiniowane) i zawieszał swoją pracę – od tej pory nie dawał znaku życia. Rozwiązanie przyszło w następującej postaci: 6. Literatura, źródła wiedzy oraz pomocne materiały: 1. Synteza układów cyfrowych, Tadeusz Łuba 2. Wprowadzenie do cyfrowego przetwarzania sygnalów, Richard G. Lyons 3. wwwzpt.tele.pw.edu.pl 4. www.scholar.google.com – polecamy świężą wyszukiwarkę dokumentów naukowych Załączniki do dokumentacji: raw.zip - program w C# i pliki *.RAW altera.zip – pliki projektu soft.zip - program do obslugi wymiany danych przez USB