PROCESORY SYGNAŁOWE - LABORATORIUM
Transkrypt
PROCESORY SYGNAŁOWE - LABORATORIUM
PROCESORY SYGNAŁOWE - LABORATORIUM Ćwiczenie nr 05 Obsługa układu bezpośredniego dostępu do pamięci i realizacja operacji modulacji z wykorzystaniem buforów ping-pong 1. Układ bezpośredniego dostępu do pamięci Układ bezpośredniego dostępu do pamięci (ang. Direct Memory Access – DMA) umożliwia najbardziej efektywną asynchroniczną obsługę urządzeń zewnętrznych. Dzięki jego zastosowaniu można dokonać przesłania danych z jednego obszaru pamięci do drugiego bez użycia procesora. Oznacza to, że nie trzeba używać rozkazów odczytu danych z pamięci i zapisu danych do pamięci. Procesor może realizować w tym samym czasie inne zadania (np. przetwarzać sygnał). Zanim układ DMA będzie zdolny do takiej operacji, należy go odpowiednio skonfigurować. Współczesne układy DMA umożliwiają konfigurację wielu różnych transferów w tym samym czasie. Każdy taki transfer jest realizowany za pośrednictwem tzw. kanału DMA. Każdy kanał DMA można przypisać do odpowiedniego urządzenia, które ma taki transfer zainicjować. Taka inicjacja transferu nazywana jest zdarzeniem (ang. event). Konfiguracja układu EDMA W procesorze TMS320C6713 zdarzenia od poszczególnych urządzeń są przypisywane do poszczególnych kanałów, podobnie jak w układzie kontroli przerwań. Odbywa się to za pomocą rejestrów ESELn (n=0,1,2) układu EDMA (ang. Enhanced DMA) procesora TMS320C6713. Układ EDMA jest w stanie obsługiwać 12 urządzeń w tym samym czasie (12 kanałów). Podobnie jak w układzie kontroli przerwań istnieje możliwość zamaskowania wybranych zdarzeń poprzez wpisanie zer na odpowiednich bitach rejestru EER. Każde niezamaskowane zdarzenie zostanie obsłużone przez układ EDMA. Adres źródłowy i adres docelowy transferu danych konfigurowany jest w układzie EDMA za pomocą specjalnych 2 kB pamięci RAM (ang. Random Access Memory), która jest traktowana jako tablica parametrów transferu (ang. Parameter RAM – PaRAM). Umożliwia ona nie tylko konfigurację parametrów wszystkich kanałów DMA, ale również daje możliwość automatycznego rekonfigurowania transferów (ang. linking) i łączenie w kaskady (ang. chaining). Transfery DMA byłyby bezużyteczne, gdyby nie dało się ich synchronizować z programem sterującym pracą procesora. Ta synchronizacja odbywa się za pomocą przerwania od układu EDMA. W procesorze TMS320C6713 układ EDMA zgłasza tylko jedno przerwanie, które może być obsłużone przez jedną funkcję obsługi. W tablicy parametrów dla każdego transferu konfiguruje się tzw. kod TCC (ang. Transfer Completion Code), który określa numer bitu zapalanego w rejestrze CIPR na zakończenie transferu. Funkcja obsługi przerwania na podstawie stanu bitów rejestru CIPR określa, który transfer ją wywołał. Jest również odpowiedzialna za wyzerowanie bitów w rejestrze CIPR. Automatyczne rekonfigurowanie transferów Automatyczne rekonfigurowanie transferów umożliwia automatyczną modyfikację parametrów transferu (takich jak adres źródłowy, adres docelowy i wszystkich innych parametrów określanych w tablicy parametrów PaRAM. Rekonfiguracja jest możliwa dzięki możliwości kopiowania wszystkich parametrów transferu z innego miejsca w PaRAM. Miejsce to jest definiowane przez specjalne pole adresu (ang. link address) znajdujące się w każdym elemencie tablicy PaRAM. (a) Tablica parametrów na początku transferu Tablica parametrów tuż przed końcem transferu 0x000 0x000 0x180 (b) 0x180 Adres źródłowy Adres źródłowy Adres źródłowy Adres źródłowy Adres docelowy Adres docelowy Adres docelowy Adres docelowy Licznik danych: 128 Licznik danych: 128 Licznik danych: 1 Licznik danych: 128 Link address: 0x180 Link address: 0x180 Link address: 0x180 Link address: 0x180 (c) (d) Tablica parametrów po zakończeniu transferu 0x000 0x180 Rekonfiguracja dla bufora ping-pong 0x000 0x180 0x198 Adres źródłowy Adres źródłowy Adres źródłowy:0x1000 Adres źródłowy:0x1000 Adres źródłowy:0x1000 Adres docelowy Adres docelowy Adres docelowy:0x4000 Adres docelowy:0x3000 Adres docelowy:0x4000 Licznik danych: 128 Licznik danych: 128 Licznik danych: 128 Licznik danych: 128 Licznik danych: 128 Link address: 0x180 Link address: 0x180 Link address: 0x180 Link address: 0x198 Link address: 0x180 Rys. 1. Stany tablicy PaRAM przy automatycznej rekonfiguracji transferów EDMA. Na rys. 1abc zilustrowano różne stany tablicy parametrów podczas automatycznej rekonfiguracji transferu danych do bufora kołowego. Pod adresem 0x000 znajduje się aktualna konfiguracja transferu. Dane będą odczytywane spod adresu źródłowego i zapisywane do adresu docelowego po uwzględnieniu licznika danych i metody modyfikacji adresów. Po wykonaniu transferu elementu licznik danych jest automatycznie zmniejszany przez EDMA. Pod koniec transferu zbliża się do wartości 0 (rys. 1b), a kiedy osiągnie wartość zero, następuje kopiowanie parametrów spod adresu zawartego w polu link address pod adres 0x000. Jeżeli licznik danych osiągnął wartość zero, to znaczy, że wszystkie dane zostały przesłane i transfer się zakończył. Kopiowanie danych spod adresu 0x180 sprawia, że następne zdarzenie EDMA dla tego kanału rozpocznie kolejny transfer od początku bufora (rys. 1c), ponieważ licznik danych z powrotem wynosi 128. Mechanizm automatycznej rekonfiguracji jest bardzo elastyczny. Na rys. 1d pokazano konfigurację, która będzie obsługiwała bufor typu ping-pong. Na zakończenie transferu najpierw zostaną skopiowane parametry spod adresu 0x180, co zmieni nie tylko licznik danych z 0 na 128, ale również adres docelowy z 0x4000 na 0x3000. Zatem kolejny transfer wypełni inną tablicę. Ponieważ pod adresem 0x180 ustawiono link address równy 0x198, to zostanie on również skopiowany pod adres 0x000 i na zakończenie tego transferu EDMA skopiuje parametry spod adresu 0x198. Łatwo zauważyć, że pod adresem 0x198 adres docelowy z powrotem wynosi 0x3000, a link address 0x180. Dzięki temu układ EDMA przez cały czas pracy będzie się przełączał naprzemiennie między adresami 0x3000 i 0x4000, zapisując dane do dwóch różnych buforów. Żeby wykorzystać te dane, program sterujący pracą procesora musi się synchronizować z EDMA za pomocą przerwań, które będą zgłaszane za każdym razem, jak transfer się zakończy. Kompletna dokumentacja do układu EDMA została udostępniona na stronie laboratorium. Na podstawie tej dokumentacji proszę przygotować opis rejestrów i parametrów transferu układu EDMA. Opis należy okazać w formie notatki odręcznej lub wydruku na początku realizacji ćwiczenia. Do konfiguracji układu EDMA zostaną wykorzystane funkcje z biblioteki CSL. Do realizacji ćwiczenia 5 udostępniono projekt CCS o nazwie DDS. W module edma.c tego projektu znajduje się konfiguracja kanału EDMA i obsługi zdarzeń od odbiornika portu szeregowego MCBSP1. Na podstawie udostępnionej na stronie dokumentacji do biblioteki CSL należy sporządzić opis tych funkcji do konfiguracji układu EDMA, które zostały użyte w module edma.c. Opis należy okazać w formie notatki odręcznej lub wydruku na początku realizacji ćwiczenia. 2. Bufor typu ping-pong W ćwiczeniu nr 3 wykorzystywano bufory kołowe, dzięki którym możliwa była synchronizacja przetwarzania sygnałów z funkcjami obsługi przerwań od nadajnika i odbiornika szeregowego portu MCBSP. Cechą charakterystyczną tych buforów było to, że funkcje obsługi przerwania systematycznie wstawiały do nich lub odczytywały z nich dane element po elemencie, wykorzystując rozkazy zapisu do pamięci i odczytu z pamięci. Ponieważ układ DMA sprzętowo kopiuje dane z urządzenia do zaprogramowanego obszaru pamięci i synchronizuje się dopiero po zakończeniu transferu, to struktura bufora kołowego z ćwiczenia 3 przestaje być odpowiednia. Nie ma możliwości, aby uzyskać informację, która część bufora jest aktualnie zapisywana lub odczytywana przez układa DMA. Wiadomo tylko, jak jest skonfigurowany transfer. Do synchronizacji układu DMA z procedurami przetwarzania sygnałów wykorzystuje się tzw. bufor podwójny lub inaczej bufor typu ping-pong. W rozwiązaniu tym bufor stanowią dwa obszary pamięci (dwie tablice lub dwa fragmenty tablicy). W każdej chwili czasu dostęp do jednego z tych obszarów może mieć albo program albo układ DMA. W opisie układu EDMA pokazano, że taka konfiguracja, w której układ EDMA zapisuje dane naprzemiennie do dwóch obszarów pamięci, jest możliwa. Jej elementem jest funkcja obsługi przerwania od układu EDMA, która wywoływana jest na zakończenie każdego transferu i umożliwia synchronizację z programem głównym. Powinna ona sygnalizować, z której części bufora ping-pong może korzystać program sterujący procesorem, a która będzie modyfikowana przez układ EDMA. Musi ona również wykrywać sytuację awaryjną, w której bufor jest pełny (w przypadku bufora wejściowego) lub pusty (w przypadku bufora wyjściowego). Podstawą implementacji bufora ping-pong podobnie jak bufora kołowego będzie typ strukturalny CIRCBUFFER języka C (definicja podana poniżej). typedef struct _CIRCBUFFER { volatile Int16 *buf; volatile int w; volatile int r; volatile int L; }CIRCBUFFER; W przypadku bufora ping-pong typ ten jednak będzie wykorzystywany w inny sposób. W przypadku tego bufora funkcje typu put i get, które zapisują i odczytują dane, nigdy nie będą wykonywane na tym samym buforze (tej samej zmiennej CIRCBUFFER). Ponieważ implementacja bufora ping-pong zakłada, że drugą stroną zawsze jest układ DMA, który nie wykorzystuje procesora. Przykładowo dla bufora wyjściowego dane z bufora odczytuje układ DMA, a zapisuje program za pomocą funkcji typu put. Pamiętamy jednak, że na zakończenie transferu układ DMA zgłosi przerwanie, które trzeba wykorzystać do synchronizacji. Do tego celu będą służyły funkcje typu after_put i after_get. Funkcja typu after_put będzie sprawdzała, czy program odczytał już próbki z poprzedniego transferu. Jeżeli tak, to przełączy ona bufory tak, żeby program zyskał dostęp do próbek z aktualnego transferu. Jeżeli nie, to następuje sygnalizacja błędu, ponieważ układ DMA właśnie zaczął zapisywać dane do bufora aktualnie obsługiwanego przez program, nadpisując przy tym nie odczytane jeszcze próbki. Funkcja typu after_get będzie sprawdzała, czy program zapisał już wszystkie dane do bufora. Jeżeli tak, to przełączy ona bufory tak, aby program mógł zapisywać dane do bufora aktualnie opróżnionego z próbek. Jeżeli nie, to należy zasygnalizować błąd, ponieważ program nie zdążył całkowicie wypełnić próbkami bufora, co spowoduje kopiowanie złych danych (nieaktualnych) przez układ DMA. Bufor ping-pong jest zawsze obsługiwany parą funkcji typu get i after_put (bufor wejściowy) oraz put i after_get (bufor wyjściowy). W zamieszczonym na stronie laboratorium programie DDS w przypadku bufora wejściowego modyfikacja indeksów pozycji w i r struktury CIRCBUFFER odbywa się za pomocą pary funkcji pingpong_get i after_pingpong_put. int pingpong_get(CIRCBUFFER *b, Int16* data) { if(b->w==b->r) return 1; *data=b->buf[b->r]; b->r=(b->r+1)&(b->L-1); return 0; } int after_pingpong_put(CIRCBUFFER* b) { int ret=0; if(b->w!=b->r) ret=1; b->w=( b->w + (b->L>>1) )& ( b->L-1 ); return ret; } Pierwsza z nich umożliwia oczekiwanie w pętli while na dane w buforze ping-pong. To oczekiwanie morze trwać tak długo, jak długo funkcja after_pingpong_put nie zmodyfikuje pozycji zapisu w o rozmiar równy połowie długości bufora L. Jeżeli do tego dojdzie, to funkcja pingpong_get będzie odczytywała dane do czasu, aż odczyta całą zawartość dostępnej aktualnie części bufora. Potem będzie umożliwiała oczekiwanie w pętli while na kolejne przełączenie buforów. Funkcja after_pingpong_put zwróci wartość 1, wtedy gdy pozycja odczytu i zapisu nie jest taka sama. Fakt ten oznacza, że program nie odczytał danych z poprzedniego transferu. W programie DDS ta sytuacja awaryjna sygnalizowana jest diodami LED. Podobnie za pomocą funkcji pingpong_put i after_pingpong_get obsługiwany jest bufor wyjściowy. int after_pingpong_get(CIRCBUFFER* b) { int ret=0; if(b->w!=b->r) ret=1; b->r=(b->r+ (b->L>>1))&(b->L-1); return ret; } int pingpong_put(CIRCBUFFER *b, Int16 data) { if(b->w==b->r) return 1; b->buf[b->w]=data; b->w=(b->w+1)&(b->L-1); return 0; } Jedyna różnica polega na tym, że funkcja after_pingpong_get modyfikuje indeks odczytu r, a funkcja pingpong_put indeks zapisu w. W przypadku bufora ping-pong indeksy w i r inicjalizuje się inaczej, ponieważ od początku program i układ DMA muszą mieć dostęp do innych buforów. void init_pingpong_buffer(CIRCBUFFER *b, Int16 *buf, int N) { b->buf=buf; b->L=N; b->w=(N>>1); b->r=0; } Funkcja init_pingpong_buffer ustawia indeks odczytu r=0, a indeks zapisu w w połowie bufora N/2. Adresy &buf[r] i &buf[w] są pierwszym adresem źródłowym (nadajnik-bufor wyjściowy) lub docelowym (odbiornik-bufor wejściowy) układu DMA. Później układ DMA przełącza się na połowę bufora nadajnika (&buf[w]) lub początek bufora odbiornika (&buf[r]). W ramach przygotowania do ćwiczenie proszę opracować opis (w formie notatki odręcznej lub wydruku) funkcji obsługi przerwania edma_int i obsługi bufora wejściowego input programu DDS udostępnionego na stronie laboratorium. 3. Cyfrowa synteza sygnału sinusoidalnego. Sygnały sinusoidalne są bardzo często wykorzystywane w pomiarach dynamicznych właściwości obiektów i w komunikacji. Z tego powodu niezwykle ważna jest umiejętność generacji sygnałów za pomocą algorytmów cyfrowych. Generatory, które w sposób cyfrowy generują sygnały, nazywane są często generatorami DDS ( ang. Direct Digital Synthesis). Sygnał sinusoidalny można wygenerować na wiele sposobów. W ramach ćwiczenia 5 poznamy dwa z nich. Pierwszy wykorzystuje tablicę próbek generowanego sygnału (ang. Look Up Table - LUT) i może być wykorzystany do generacji dowolnego sygnału okresowego. Natomiast drugi umożliwia generację sygnału sinusoidalnego w oparciu o filtr NOI pracujący na granicy stabilności. Metoda z tablicą LUT W metodzie tej wykorzystywana jest tablica z próbkami pojedynczego okresu generowanego sygnału okresowego. W przypadku sygnału sinusoidalnego będzie to okres funkcji kosinus. Do realizacji tego zadania wykorzystana zostanie struktura bufora kołowego, który zostanie wypełniony próbkami jednego okresu funkcji kosinus zgodnie z wzorem 2n xn cos , n 0 .. N 1 , N (1) w którym N jest rozmiarem bufora kołowego, a więc potęgą liczby 2. Program generujący kolejne próbki kosinusoidy będzie odczytywał tablicę x z krokiem, który będzie zależny od częstotliwości generowanego sygnału. Częstotliwość ta zależy od częstotliwości próbkowania, z jaką taktowany jest przetwornik cyfrowo-analogowy. Im krótszy krok, tym mniejsza częstotliwość generowanego sygnału. Dokładna relacja między częstotliwością generowanego sygnału a krokiem w tablicy i częstotliwością próbkowania podana jest wzorem (2). f kfs N (2) W równaniu tym k oznacza krok w tablicy LUT, fs jest częstotliwością próbkowania, a N długością tablicy. Przykład: Jeżeli częstotliwość próbkowanie fs=48 kHz tablica x zawiera 1024 próbki, generator wysyła co k=4 próbkę, to uzyskujemy sygnał o częstotliwości f=187,5 Hz. Jeżeli k będzie tylko liczbą całkowitą to pozycja p w buforze może być modyfikowana za pomocą operacji p=(p+k)&(N-1). Wartość początkowa tej pozycji definiuje zgodnie z zależnością (3) przesunięcie fazowe generowanego sygnału. 180 p N (3) Taka modyfikacja jednak sprawia, że można wygenerować tylko N/2 dyskretnych częstotliwości od f=46,875 Hz do f=24 kHz z krokiem co 46,875 Hz. Tymczasem krok k może być liczbą rzeczywistą. Wówczas również pozycja w buforze staje się liczbą rzeczywistą, która jest zamieniana na liczbę całkowitą tylko w momencie odczytu wartości sygnału z tablicy. Przy zamianie należy dokonać zaokrąglenia do najbliższej liczby całkowitej. W języku C konwersja z liczby rzeczywistej na przykład typu float do liczby całkowitej na przykład typu int odbywa sie poprzez zwykłe przepisanie wartości: float a=1.6; int b; b=a;// w b będzie wartość 1 Taka konwersja jednak zaokrągla liczbę rzeczywistą poprzez wyzerowanie części ułamkowej (za przecinkiem). Aby uzyskać zaokrąglenie do najbliżej liczby całkowitej trzeba do liczby rzeczywistej dodać wartość 0.5. float a=1.6; int b; b=a+0.5;// w b będzie wartość 2 Żeby uniknąć zapisu poza obszar tablicy nie można użyć operacji iloczynu logicznego, ponieważ zarówno pozycja w buforze p jak i krok k są liczbami rzeczywistymi. Zamiast tego wykorzystuje się zwykłą sumę i instrukcję warunkową w postaci p+=k; if(p >= (N-0.5)) p=p-128.0; Dzięki temu uwzględnia się operację zaokrąglenia przy konwersji pozycji z liczby rzeczywistej na liczbę całkowitą. Na przykład dla N=128 i p=127.6, po wykonaniu powyższych instrukcji p=-0.4. Wówczas indeks w tablicy x po zaokrągleniu w postaci (int)(-0.4+0.5), da nam wartość 0, a więc początek bufora. Jeżeli p=127.4, to po modyfikacji będzie równe dokładnie tyle samo, co po zaokrągleniu (int)(127.4+0.5), da wartość 127, a więc koniec bufora. W programie DDS w pliku nagłówkowym generator.h znajduje się definicja typu strukturalnego LUTGENERATOR. typedef struct _LUTGENERATOR { float x[1024]; float s,p; }LUTGENERATOR; W strukturze tej znajduje się tablica x na próbki generowanego sygnału, pole s z rzeczywistą wartością kroku i pole p z rzeczywistą pozycją w tablicy x. Zmienna tego typu będzie reprezentować generator sygnału o zadanej częstotliwości i przesunięciu fazowym. Metoda rekursywna W tej metodzie generacji sygnału sinusoidalnego wykorzystuje się fakt, że transformata Z sygnału kosinusoidalnego wynosi Z uncos2fn 1 z 1 cos2f 1 2 z 1 cos2f z 2 (4) Z unsin 2fn z 1 sin 2f 1 2 z 1 cos2f z 2 (5) a sinusoidalnego W równaniach tych u[n] oznacza skok jednostkowy, czyli funkcje rozpoczynają się od n=0. Zapisując równania różnicowe filtru o takiej transmitancji otrzymujemy dla transmitancji (4) yn xn cos2f xn 1 2 yn 1cos2f yn 2 (6) a dla transmitancji (5) yn sin2f xn 1 2 yn 1cos2f yn 2 (7) Odpowiedź impulsowa tych filtrów zgodnie z równaniami (5) i (6) będzie odpowiadała funkcjom uncos2fn i unsin 2fn . Zatem jeżeli x[0]=1.0, a pozostałe wartości x[n] będą zero, to obliczane wartości y[n] będą kolejnymi próbkami generowanego sygnału. Częstotliwość sygnału w hercach można obliczyć mnożąc f przez częstotliwość próbkowania fs. Fazę generowanego w ten sposób sygnału można modyfikować wykorzystując zależność (8). cos2fn cos cos2fn sin sin2fn (8) Zatem generując parę sygnałów yc n uncos2fn i ys n unsin2fn możemy uzyskać sygnał o dowolnym przesunięciu fazowym poprzez liniową kombinację sygnałów yn Ayc n Bys n, w której A cos , B sin . (9) W pliku nagłówkowym generator.h w programie DDS zdefiniowana jest struktura NOIGENERATOR, które zawiera wszystkie pola niezbędne do implementacji generatora sygnału sinusoidalnego. W strukturze tej parametry a to współczynniki, przez które mnożymy przeszłe próbki generowanego sygnału. Zgodnie z równaniami różnicowymi (6) i (7) są one takie same dla sygnału sinusoidalnego i kosinusoidalnego. Współczynniki b natomiast muszą być zdefiniowane osobno dla sygnałów ys i yc. Impuls Kroneckera przechowywany jest w tablicy x, a przeszłe próbki sygnałów ys i yc w tablicach o takiej samej nazwie. Pola A i B definiują przesunięcie fazowe i ich wartości są inicjowanie równaniem (9). typedef struct _NOIGENERATOR { float yc[2],ys[2]; float x[2]; float a[2]; float bc[2],bs[2]; float A,B; }NOIGENERATOR; 4. Opis zadań do wykonania Na stronie laboratorium udostępniony jest program startowy o nazwie DDS. W programie tym dane z mikrofonu poprzez port MCBSP1 trafiają do układu mikroprocesorowego. W module edma.c znajduje się funkcja setupEDMA, która konfiguruje kanał EDMA dla odbiornika portu MCBSP1 w taki sposób, że układ EDMA autonomicznie zapisuje dane do bufora input. Układ EDMA rekonfiguruje się automatycznie, przełączając się pomiędzy dwoma obszarami bufora input i synchronizując tę operację z programem procesora za pomocą funkcji after_pingpong_put wywoływanej z wnętrza funkcji obsługi przerwania edma_int. W głównej pętli programu dane odczytywane są za pomocą funkcji pingpong_get. Następnie za pomocą funkcji writeData próbki sygnału są klasycznie zapisywane do nadajnika portu MCBSP1, skąd trafiają do wyjścia słuchawkowego i liniowego układu kodeka audio. Zadanie 1 W zadaniu pierwszym należy tak zmodyfikować funkcję setupEDMA i edma_int, aby zarówno nadajnik jak i odbiornik obsługiwany był przez układ bezpośredniego dostępu do pamięci. W funkcji main należy zamiast funkcji writeData wpisywać próbki do wyjściowego bufora ping-pong. Efekty działania programu powinny być takie same jak dla programu startowego z tą różnicą, że wymiana danych z portem MCBSP1 odbywa się tylko za pomocą układu EDMA. Zadanie 2 W zadaniu drugim należy wypełnić odpowiednim kodem funkcje reset_LUT_generator i LUT_generator, które mają odpowiednio zainicjować zmienną typu LUTGENERATOR i generować kolejne próbki sygnału sinusoidalnego o zadanej częstotliwości i fazie. Do makiety laboratoryjnej należy podłączyć zasilanie symetryczne 18V z zasilacza Hameg HM8143 i na lewym kanale audio generować sygnał o fazie równej zero i częstotliwości podanej przez prowadzącego. Natomiast na prawym kanale ma być generowany sygnał o częstotliwości i fazie podanych przez prowadzącego. Generowane sygnały należy zaprezentować na oscyloskopie Tektronix TDS1001B. Po uzyskaniu zadowalającego wyniku należy użyć tego generatora do modulacji sygnału z mikrofonu z częstotliwością 300 Hz. Operację modulacji wykonuje się poprzez mnożenie sygnału modulowanego przez sygnał modulujący. Zadanie 3 W zadaniu trzecim należy wypełnić odpowiednim kodem funkcje reset_NOI_generator i NOI_generator, które mają odpowiednio zainicjować zmienną typu NOIGENERATOR i generować kolejne próbki sygnału sinusoidalnego o zadanej częstotliwości i fazie. Na lewym kanale ma być generowany sygnał o fazie równej zero i częstotliwości podanej przez prowadzącego, natomiast na prawym kanale sygnał o częstotliwości i fazie podanych przez prowadzącego. Generowane sygnały podobnie jak w zadaniu drugim należy zaprezentować na oscyloskopie. Po uzyskaniu zadowalającego wyniku tak jak w zadaniu drugim należy użyć tego generatora do modulacji sygnału z mikrofonu z częstotliwością 300 Hz.