Wizualizacja danych sensorycznych Sprawozdanie z projektu
Transkrypt
Wizualizacja danych sensorycznych Sprawozdanie z projektu
Wizualizacja danych sensorycznych Sprawozdanie z projektu Przestrzenny wyświetlacz smugowy Piotr Jakubik, Piotr Wybieralski. 15 czerwca 2010 1 Spis treści 1 Opis projektu 3 2 Wybór elementów wykorzystanych w projekcie 2.1 Mikroprocesor – ATmega32 16PU . . . . . . . . . . . . 2.2 Zasilanie ramienia – baterie R6 . . . . . . . . . . . . . 2.3 Stabilizator – L4940V5 . . . . . . . . . . . . . . . . . . 2.4 Tranzystory – układy ULN2803 . . . . . . . . . . . . . 2.5 Diody LED . . . . . . . . . . . . . . . . . . . . . . . . 2.6 Encoder – fototranzystor LPT80A oraz dioda IRL80A 2.7 Silnik – KAG M28x20/S . . . . . . . . . . . . . . . . . 2.8 Układ PWM – timer NE555 . . . . . . . . . . . . . . . 2.9 Zasilanie silnika – zasilacz ATX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 3 4 4 4 5 5 5 3 Modyfikacja zasilacza ATX 5 4 Schemat elektryczny części obrotowej 5 5 Schemat montażowy części obrotowej 5 6 Schemat elektryczny zasilania silnika 6 7 Schemat montażowy stojaka z silnikiem 6 8 Elementy konstrukcji wykonane do tej pory 7 9 Charakterystyka mikroprocesora i wykorzystane podzespoły 9.1 Taktowanie mikroprocesora . . . . . . . . . . . . . . . . . . . . . 9.2 Komparator analogowy z histerezą . . . . . . . . . . . . . . . . . 9.3 Timer 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.4 Timer 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 7 9 11 10 Koncepcja działania programu 11 11 Sposób realizacji wyświetlania tekstu 12 12 Oprogramowanie wyświetlające tekst 13 13 Wnioski 18 2 1 Opis projektu W ramach realizacji projektu ze sterowników robotów wykonany został wyświetlacz widmowy. Jest to obracające się urządzenie wyposażone w trzy linijki diodowe (po 8 diód każda). Przy zapewnieniu odpowiednio szybkich obrotów możemy oszukać ludzkie oko i dzięki temu na ścianach walca, po której przemieszczają się linijki diodowe wyświetlać tekst lub obrazy. Każda z linijek jest innego koloru (RGB) oraz znajduje sie odrobinę bliżej osi obrotu. Dzięki temu wyswietlając obrazy będziemy mogli stworzyć wrażenie głebii obrazu. Projekt powstał na użytek własny i jest przez nas całkowicie finansowany. 2 Wybór elementów wykorzystanych w projekcie 2.1 Mikroprocesor – ATmega32 16PU Do zrealizowania projektu zdecydowaliśmy się wykorzystać mikrokontroler firmy Atmel AVR, gdyż jesteśmy w posiadaniu programatora do mikrokontrolerów tej firmy. Przy wyborze konkretnego modelu kierowaliśmy się następującymi parametrami, opisanymi w nocie katalogowej [5]: maksymalna częstotliwość pracy mikroprocesora (musi być odpowiednio wysoka, aby sterować diodami LED z wystarczającą szybkością), ilość pamięci FLASH, w którą wyposażony jest mikroprocesor (w programie będzie dużo tablic z danymi do wyświetlania, które chcielibyśmy zmieścić na wewnętrznej pamięci FLASH, bez konieczności podłączania pamięci zewnętrznych), ilość portów wyjściowych mikroprocesora (sterujemy diodami bezpośrednio z wyjść mikrokontrolera, a mamy 3 linijki diodowe po 8 diód każda, co daje nam 24 wyjścia, do tego należy uwzględnić PINy, do których podłaczać będziemy programator oraz wyprowadzenie emitera fototranzystora do obliczania prędkości obrotowej silnika) Po uwzględnieniu tych warunków zdecydowaliśmy się na wybór mikrokontrolera ATmega 32, która posiada zegar o max. częstotliwości pracy 16MHz, 32Kb pamięci FLASH oraz 4 porty wyjściowo-wejściowe po 8 pinów każdy. Po przejrzeniu zasobów internetu dotyczących realizowanego przez nas projektu znaleźliśmy bardzo podobny projekt, w którym wykorzystana została ATmega 8, która poprawnie realizowała postawione przed nią zadanie. W tym projekcie jednak autor wykorzystał całe 8Kb pamięci FLASH, a sterował tylko 1 linijką diodową, na której wyświetlał tekst. W naszym przypadku, kiedy to sterujemy 3 linijkami diodowymi oraz przewidujemy możliwość wyświetlania prostych obrazów pamięci tej potrzebujemy więcej, stąd nasz wybór 32Kb tejże pamięci. Wybrany przez nas mikrokontroler posiada 4 porty, co daje łącznie 32 PINy sterujące, 24 z nich będziemy wykorzystywać do sterowania diodami, 2 będą wykorzystane przez programator oraz 1 przez fototranzystor. 2.2 Zasilanie ramienia – baterie R6 Cały obracający się układ elektryczny jest zasilany z baterii. Odrzuciliśmy pomysł zasilania go z zasilacza, gdyż w przypadku zasilania obracającego się elementu byłoby to zadanie bardzo trudne do zrealizowania. Układ będzie zasilany 4 bateriami R6 po 1.5V każda, co da nam łącznie 6V. Napięcie będzie dostarczane przez stabilizator o małym spadku napięcia, który będzie stabilizował napięcie na wartości 5V (takie zasilanie wymagane jest przez ATmegę). Wybrany stabilizator ma spadek napięcia 0.5V na 1.5A. Każda z diód będzie pobierać ok. 20mA. Widać więc, iż napięcie na wyjściu stabilizatora nie spadnie poniżej wymaganych 5V, o ile ATmega nie będzie pobierać więcj niż 2,36A, co wydaje się być niemożliwe. 2.3 Stabilizator – L4940V5 Wymaganiami stawianymi przez stabilizator jest mały spadek napięcia na stabilizatorze oraz jego małe wymiary. Na podstawie danych katalogowych [7] wybraliśmy stabilizator L4940V5, w którym spadek na3 pięcia wynosi jedynie 500mV na 1.5A. Stabilizuje on napięcie 5V bez konieczności lutowania dodatkowych rezystorów. Sposób podłączenia kondensatorów filtrujących został również zaczerpnięty z [7]. 2.4 Tranzystory – układy ULN2803 Tranzystory będą wykorzystane jako klucze przełączające zapalające, bądź gaszące diody. Sterowane będą bezpośrednio przez wyjścia ATmegi. Zdecydowaliśmy się wykorzystać do tego celu scalone układy, które będą zawierały w sobie kilka tranzystorów, aby zaoszczędzić miejsca na płytce. Wybraliśmy układ ULN2803, w którego wchodzi 8 tranzystorów NPN w układzie Darlingtona, przystosowane do pracy w układach TTL. Wymagana szybkość przełączania tranzystorów nie wydaje się mieć większego znaczenia, gdyż po uwzględnieniu parametrów fizycznych konstuowanego urządzenia czas wyświetlania pojedynczego piksela wynosi ok. 265,26 µs (jak napisano w nocie [1] w wybranym układzie maksymalne czasy ton oraz tof f wynoszą 1µs). 2.5 Diody LED W przypadku diód LED najważniejszym parametrem jest szybkość zapalania i gaszania. Wymagana szybkość jest identyczna jak w przypadku tranzystorów (co jest spełnione, gdyż diody LED mają częstotliwość graniczną rzędu kilkunastu MHz). Zdecydowaliśmy się na diody o średnicy 5mm, aby otrzymać możliwie duże wyświetlane piksele. Każda linijka diodowa będzie innego koloru, odpowiednio czerwona, zielona oraz niebieska. Pozwoli to nie tylko na oddanie głębii obrazu, ale także zwiększy paletę barw. Wartości rezystorów umieszczonych szeregowo z diodami została obliczona tak, aby na diodach był wymagany spadek napięcia 2.2V oraz płynął przez nie prąd ok. 20mA. Dało to nam w efekcie przy zasilaniu napięciem 5V rezystory o wartości 130Ω. 2.6 Encoder – fototranzystor LPT80A oraz dioda IRL80A Encoder będzie dostarczał mikroprocesorowi informacji o częstotliwości obrotowej urządzenia. Nadajnik (w postaci diody podczerwonej) zostanie przymocowany do nieruchomego stojaka, natomiast odbiornik (w postaci fototranzystora) znajdzie się na obrotowej części, z tej samej strony części obrotowej, co linijki diodowe. W chwili, kiedy nadajnik znajdzie się dokładnie pod odbiornikiem, na wyjściu odbiornika pojawi się stan wysoki, który wywoła sprzętowe przerwanie mikroprocesora. Zakładając częstotliwość obrotów wynoszącą 20Hz otrzymujemy czas przełączania tp = 50ms. Maksymalne czasy przełączania diody IR i fototranzystora muszą być więc mniejsze od tej wartości. Czasy przełączania standardowych fotodiód i fototranzystorów liczone są w µs, tak więc z pewnością będą one wystarczające. Należy mieć na uwadze zjawisko rozpraszania światła emitowanego przez diodę LED oraz fakt, iż fototranzystor mógłby wchodzić w stan przewodzenia, gdy znajdowałby się w bardzo oświetlonym pomieszczeniu lub też został oświetlony latarką. Aby temu zapobiec można zastosować rurkę termokurczliwą o odpowiedniej średnicy. Jeden jej kawałek byłby przymocowany do diody IR, drugi do bazy fototranzystora. W takim wypadku fototranzystor przewodziłby prąd jedynie w momencie, gdy znajdzie się dokładnie nad diodą IR. Jako układ nadajnik-odbiornik wybrany został sparowany zestaw diody IRL80A oraz fototranzystora LPT80A. Ważne było, aby oba elementy miały możliwie jak najbardziej zliżoną do siebie charakterystykę zdolności emisyjnej dla diody oraz zdolności absorbcyjnej dla fototranzystora. Wybrane elementy spełniają to założenie, co jest zilustrowane na odpowiednich wykresach w notach [4] oraz [2]. Maksymalny czas przełączania tranzystora ton oraz tof f wynoszą 10 µs. Baza tranzystora ma grubość ok. 2mm, tak więc przy 20Hz częstotliwości obrotów znajdziej się dokładnie nad diodą IR na ok. 100 µs. Tak więc w tym czasie fototranzystor zdąży przejść w stan przewodzenia. Do emitera tranzystora dołączony będzie szeregowo rezystor. W momencie gdy tranzystor wejdzie w stan przewodzenia na resytorze odłoży się stan wysoki napięcia i zostanie wywołane przerwanie w mikroprocesorze. Prąd ciemny tranzystora ma maksymalną wartość 50nA, a więc aby w tym wypadku na rezystorze napięcie było mniejsze niż 0.4V (stan niski), musi on mieć rezystancję mniejszą niż 8M Ω. Z kolei w stanie przewodzenia prąd wynosi ok. 3.2 mA, a więc rezystor musi mieć rezystancję większą niż 1.5kΩ, aby odłożyło się na nim co najmniej 4.6V. Uśredniając obie wartości krytyczne zdecydowaliśmy się wybrać rezystor 15kΩ. 4 2.7 Silnik – KAG M28x20/S Zdecydowaliśmy się wykorzystać silnik prądu stałego, który będzie sterowany sygnałem PWM przy wykorzystaniu analogowego układu elektronicznego. Wypełnienie sygnału PWM (a więc także średnia wartość napięcia) regulowane będzie potencjometrem, co pozwoli na zmianę prędkości obrotowej silnika. Parametry, które zostały wzięte pod uwagę przy wybieraniu silnika to jego maksymalna częstotliwość obrotów (musi być większa niż 20Hz) oraz napięcie zasilania silnika. Zdecydowaliśmy się na silnik KAG M28x20/S. Zgodnie z dokumentacją [6] silnik ten jest zasilany napięciem równym 12V oraz posiada częstotliwość obrotową przy obciążeniu równą 3150rpm, co daje 52.5 obrotu na minutę, czyli ponad 50Hz. Cenną zaletą są także jego małe wymiary, które pozwolą na łatwe umieszczenie go w wykonanym już stojaku. 2.8 Układ PWM – timer NE555 Postanowiliśmy wykorzystać układ realizujący napięciowe sterowanie PWM. Regulacja szerokości pasma wypełnienia zostanie zrealizowana za pomocą nieskomplikowanego układu elektronicznego. Podstawową jednostką tego układu jest timer NE555, opisany w [3]. Timer ten jest popularnym układem, wykorzystywanym często w amatorskich układach elektronicznych 1 . Projekt został zmodyfikowany poprzez dodanie diody podczerwonej potrzebnej w układzie enkodera. 2.9 Zasilanie silnika – zasilacz ATX Zasilanie silnika zostało zrealizowane przy wykorzystaniu odpowiednio zmodyfikowanego zasilacza komputerowego ATX. Wyprowadzone z niego zasilanie o wartości 12V doprowadzone jest do układu stabilizującego napięcie i zapewniającego regulację prędkości obrotowej przez modulację PWM napięcia wyjściowego zasilającego silnik. 3 Modyfikacja zasilacza ATX Modyfikacja zasilacza ATX polegała na wyprowadzeniu wyjść 3V , ±5V , ±12V oraz masy na zwenątrz obudowy. Ponadto wewnątrz zasilacza zostało umieszczonych 6 rezystorów ceramicznych 10Ω podłączonych do wyjścia 5V jako dodatkowe obciążenie (zasymulowanie rezystancji płyty głównej komputera) w celu ustabilizowania napięcia na wyjściu ±12V . Na obudowie zasilacza zostały umieszczone diody LED sygnalizujące tryb pracy zasilacza: Standby i On oraz dodatkowy przełącznik symulujący uruchomienie komputera. 4 Schemat elektryczny części obrotowej Schemat układu elektrycznego wykonany w programie Ealge oraz płytka drukowana utworzona na jego podstawie przedstawione są odpowiednio na rysunkach 1 oraz 2. 5 Schemat montażowy części obrotowej Podstawą części obrotowej będzie płytka wykonana z pleksy o mymiarach 6cm na 30cm. Będzie ona przymocowana do wału silnika z wykorzystaniem klocka LEGO, który zostanie umieszczony w środku ciężkości całego ramienia. Baterie zasilające (4 baterie R6 umieszczone w specjalnym koszyczku), celem zminimalizowania rozmiarów ramienia oraz przybliżenia środka masy do środka geometrycznego, zostaną umieszczone pod płytką drukowaną, która zostanie podniesiona na filarach odpowiedniej wysokości. Odległość umieszczenia płytki od osi obrotu zostanie tak dobrana, aby obie strony części obrotowej dawały zbliżone momenty wywołane przez siłę ciężkości. Diody LED zostaną umieszczone w ścianie obudowy. Schemat montażowy przedstawiony został na rysunku 3. Na rysunku przedstawione są kolejno: rzut z góry (na urządzenie bez górnej części obudowy), rzut w boku, rzut z przodu od strony diód. Ściany 1 Gotowy układ sterwoania PWM na bazie NE555 opisany jest w [8] 5 Rysunek 1: Schemat układu elektrycznego Rysunek 2: Płytka drukowana zostaną przyklejone do podstawy, natomiast górna część obudowy zostanie do niej przykręcona śrubami (aby umożliwić otwarcie konstrukcji i na przykład wymianę baterii). Wewnątrz obudowy, w oznaczonych na schemacie miejscach będą zamieszczone specjalne belki prostopadłościenne, w które będą przykręcane śruby od obudowy. 6 Schemat elektryczny zasilania silnika Na rysunkach 4 i 5 umieszczone są schemat oraz wzór płytki drukowanej do sterowania pracą silnika. 7 Schemat montażowy stojaka z silnikiem Na rysunkach 6 i 7 umieszczone są schematy konstrukcyjne stojaka. 6 Rysunek 3: Schemat montażowy części obrotowej 8 Elementy konstrukcji wykonane do tej pory W warstwie sprzętowej dotychczas został przerobiony zasilacz komputerowy ATX, z którego wyprowadzono napięcia ±12V ,±5V , 3V oraz GN D. Poprzez dodanie odpowiedniej ilości rezystorów wyjście ±12V zostało ustabilizowane. Wykonany został stojak, w którym umieszczono zakupiony silnik prądu stałego. Zdjęcie wykonanej konstrukcji znajduje się na rysunku nr 8. Silnik ten zasilany jest stabilizowanym napięciem 12V i sterowany jest poprzez układ PWM sterujący średnią wartością napięcia, a dzięki temu możliwe jest sterowanie częstotliwością obrotów silnika. Zlutowany również został układ sterowania diodami umieszczony w części obrotowej, pokazany na rysunku 9. Elementy gotowe i wykonane zostały przedstawione na zdjęciach 10 oraz 11. 9 Charakterystyka mikroprocesora i wykorzystane podzespoły Informacje dotyczące budowy mikroprocesora i umieszczonych w nim podzespołów oraz sposób ich konfiguracji zaczerpnięte zostały z pracy [9]. Praca ta opisuje układ ATmega16, jednakże od zastosowanego przez nas ATmega32 układ ten różni się jedynie ilością pamięci FLASH. Poniżej przedstawione zostały wykorzystane podzespoły mikrokontrolera oraz sposób ich konfiguracji. 9.1 Taktowanie mikroprocesora ATmega32 może być taktowana z maksymalną częstotliwością 16MHz i taka została przez nas wykorzystana, aby zapewnić wystarczającą szybkość pracy urządzenia, a dodatkowo poprawić dokładność wyświetlania pikseli. 9.2 Komparator analogowy z histerezą Mikroprocesor ATmega32 posiada komparator analogowy z histerezą. Został on wykorzystany do obliczania częstotliwości obrotów. Można skonfigurować go w taki sposób, aby napięcie na pinie wejściowym (ujemne wejście komparatora) było porównywane z napięciem referencyjnym równym ok. 1,23V (podawanym na dodatnie wejście komparatora). Komparator został tak ustawiony, aby dawał przerwanie w przypadku wystąpienia zbocza opadającego na swoim wyjściu, a więc sytuacji, gdy fototranzystor pojawił się nad diodą IR i zaczął przewodzić prąd, a na rezystorze podłączonym do emitera odłożyło się napięcie. 7 Rysunek 4: Schemat układu elektrycznego do regulowania pracy silnika Rysunek 5: Płytka drukowana sterowania silnikiem 8 Rysunek 6: Schemat montażowy stojaka, widok z przodu Rysunek 7: Schemat montażowy stojaka, widok z góry W momencie, gdy to napięcie jest większe niż napięcie referencyjne na wejściu dodatnim, wtedy komparator przechodzi ze stanu wysokiego na niski i występuje zbocze opadające. Komparator jednocześnie wywołuje zdarzenie Input Capture w Timerze 1. 9.3 Timer 0 Jest to 8-bitowy licznik, który zlicza impulsy wewnętrznego generatora z odpowiednio ustawionym preskalerem. Preskaler może być ustawiony na wartości: 1, 8, 64, 256 lub 1024. Licznik ten będzie wykorzystywany do odmierzenia czasu wyświetlania pojedynczego piksela, a także czasu potrzebnego, aby linijka diodowa przemieściła się z tyłu wyświetlacza (a więc miejsca, gdzie znajduje się dioda IR) do przodu. Dla częstotliwości obrotów równej 20Hz, którą staramy się osiągnąć, czas potrzebny na prze1 mieszczenie się linijki diodowej z tyłu na przód wynosi t = 2f = 25ms, z kolei czas wyświetlania piksela d o szerokości d = 5mm wynosi f = 2πf r = 284µs. Okres wewnętrznego oscylatora dla częstotliwości taktowania równej 20Hz wynosi 6.25 · 10−8 s. W przypadku mierzenia czasu na przejście od tyłu do przodu, preskaler ustawiony jest na 1024 (największy z możliwych), dzięki czemu licznik może zliczyć czas t = 256 · 1024 · 6.25 · 10−8 = 16ms. Widać więc, iż licznik zliczając czas potrzebny na obrót może się przepełnić. Ta sytuacja została uwzględniona w programie, jeśli zadany czas jest zbyt duży, to przepełnienie się licznika jest brane pod uwagę. Przy tej wartości preskalera najdrobniejszy okres czasu, jaki jest w stanie rozróżnić licznik jest równy 64µs. Nie jest to okres wystarczająco mały, aby zapewnić dokładne wyświetlanie piksela. Aby uzyskać większą dokładność, wartość preskalera przy wyświetlaniu piksela ustawiana jest na 256. Kolejną mniejszą wartością jest 64, jednakże przy szerszym pikselu oraz szybszych obrotach, licznik z takim preskalerem mógłby się przepełnić. 9 Rysunek 8: Zdjęcie stojaka Rysunek 9: Zdjęcie zlutowanej płytki uniwersalnej 10 Rysunek 10: Finalny wygląd ramienia wyświetlacza 9.4 Timer 1 Jest to 16-bitowy licznik, który ma za zadanie zliczać czas, jaki zajął jeden obrót linijki diodowej. Przy częstotliwości obrotów 20Hz czas jednego obrotu wynosi t = 50ms, chcemy jednak zapewnić poprawne działanie od częstotliwości 15Hz, a więc timer musi być w stanie zliczyć czas t = 67ms. Preskaler do tego 64·216 timera został ustawiony na 64, dzięki czemu będzie on w stanie zliczyć t = 16·10 6 s = 262ms. Preskaler mniejszy o jeden stopień wynosi jedynie 8 i jest to za mała wartość, aby licznik nie przepełnił się. 10 Koncepcja działania programu Podczas działania programu wykorzystujemy dwa rodzaje przerwań. Pierwszym jest przerwanie na zbocze opadające komparatora analogowego i odpowiada ono zboczu narastającemu od fototranzystora. Nie zdecydowaliśmy się na wykorzystanie przerwania bezpośrednio od tranzystora, gdyż szybkość narastania zbocza może nie być wystarczająca, aby wygenerować przerwanie. Wraz z wystąpieniem przerwania zostanie wywołane zdarzenie Input Capture Timera 1. W tym przerwaniu zostaje odczytana wartość licznika, dzięki czemu można obliczyć aktualną częstotliwość obrotów. Działanie programu opiera się na średniej z 10 ostatnich częstotliwości obrotów, aby wyeliminować ewentualne zakłócenia. Drugim rodzajem przerwań są przerwania Output Compare od timera 0. Mają one na celu odmierzenie odcinka łuku, który przebywa linijka diodowa. Najpierw linijka diodowa musi obrócić się od tylnej części wyświetlacza (tam, gdzie znajduje się dioda IR) do przodu, gdzie wyświetlany będzie napis bądź obraz. Czas trwania takiego obrotu uzależniony jest od częstotliwości obrotów oraz od szerokości wyświetlanego napisu/obrazu w taki sposób, aby jego środek znajdował się dokładnie po drugiej stronie w stosunku do diody IR. Drugim miejscem, w którym wykorzystane są te przerwania jest wyświetlenie piksela o zadanej szerokości. Tutaj również odmierzany czas obliczany jest na bieżąco na podstawie częstotliwości obrotów oraz zadanej w programie szerokości pojedynczego piksela. 11 Rysunek 11: Finalny wygląd pzestrzennego wyświetlacza smugowego 11 Sposób realizacji wyświetlania tekstu Dane do każdej linijki diodowej wysyłane są bezpośrednio poprzez odpowiedni port. Wystawienie odpowiedniego bajtu danych powoduje zaświecenie się odpowiednich diód. Aby wyświetlić dany znak (na przykład literę) należy wysłać na port odpowiedni ciąg bajtów, jeden po drugim, aby wywołać złudzenie obrazu. Każda litera (a także inne wybrane znaki) zapisane są w globalnie dostepnych tablicach char T[6]. Taka tablica przykładowo dla litery A wygląda następująco: char A[6] = {0x00,0x7C,0x12,0x12,0x7C,0x00};, co jest zobrazowane na rysunku 12. Puste kolumny nr 1 i nr 6 zapewniają odpowiedni odstęp pomiędzy literami, dolny wiersz wykorzystany jest na przykład przy wyświetlaniu przecinka, natomiast górny w przypadku polskich znaków (np. Ń)2 . W przypadku niektórych liter bądź znaków specjalnych tablica zawierająca ten znak może mieć większy rozmiar (przykładowo litera ’w’ składa się z 7 kolumn). Funkcja wyświetlająca cały napis ma prototyp void WyswietlNapis(char * napis,int szerokosc). W funkcji tej, znak po znaku, aż do końca napisu wywoływana jest funkcja void WyswietlLitere(const char * litera,int szerokosc), która zapewnia wyświetlenie odpowiedniej litery o zadanej szerokości piksela. W tej funkcji parametr ’litera’ wskazuje juz na tablice z danymi char[6], w ktorej zapisana jest dana litera. Funkcja wystawia na port wyjściowy odpowiedni bajt danych, zeruje i startuje timer, ustawiając mu preskaler oraz odpowiednią wartość do Output Compare, po czym czeka na zapalenie flagi oznaczającej, iż przebyty został zadany odcinek łuku. Po upływie tego czasu wyświetla kolejny bajt z tablicy danych itd, aż do końca tablicy z literą. Wartość wpisywana do Output Compare wyliczana jest przed wyświetleniem litery na podstawie zadanej szerokości piksela, średniej częstotliwości obrotów oraz promienia, po jakim obraca się linijka diodowa. Przykładowo jeżeli chcemy, aby piksele wyświetlane przez urządzenie były kwadratowe, droga pokonywana przez zapaloną diodę w czasie wyświetlania jednego piksela musi wynosić 5mm, jako że używamy diód o średnicy d = 5mm. Czas, przez jaki musi świecić się 2 Koncepcja zapisu litery w pamięci programu została zaczerpnięta z pracy [10] 12 Rysunek 12: Zapis litery „A”w programie d dioda, aby uzyskać ten efekt obliczany jest ze wzoru t = 2πf r , co po podstawieniu odpowiednich danych d = 5mm,f = 20Hz,r = 140mm daje nam t = 284, 21µs. Piksele na pozostałych linijkach diodowych będą minimalnie inne, gdyż czas wyświetlania będzie uzależniony od środkowej linijki diodowej. Po wyświetleniu całej tablicy zawierającej literę, funkcja kończy się i jest ponownie wywoływana przez void WyswietlNapis(char *,int) z kolejnym znakiem do wyświetlenia, aż do końca napisu. W funkcji main() ustawiane są odpowiednie wartości rejestrów, w celu odpowiedniej konfiguracji mikroprocesora, a następnie w nieskończonej pętli wywoływana jest funkcja WyswietlNapis(’Hello world!,5), co powoduje wyświetlenie napisu podanego w parametrze. Zdecydowaliśmy się zastosować nieskończoną pętlę właśnie w funkcji main(), a nie przykładowo w WyswietlNapis, aby możliwa była zmiana wyświetlanego tekstu po jakimś czasie trwania programu, a także dla wyświetlania obrazów stworzenie prostych animacji. 12 Oprogramowanie wyświetlające tekst Źródło napisanego programu, wraz z komentarzami zostało umieszczone poniżej. W programie zdefiniowano jedynie tablice zawierające litery wymagane do wyświetlenia przykładowego tekstu. Oprogramowanie zostało napisane w AVR Studio, natomiast do przesyłania programu na mikrokontroler wykorzystano WinAVR. 1 3 // zadana s z e r o k o s c p i k s e l a (w m i l i m e t r a c h ) #d e f i n e DLUGOSC 10 // promien okregu , na j a k i m o b r a c a s i e srodkowa l i n i j k a diodowa (w m i l i m e t r a c h ) #d e f i n e PROMIEN 130 5 7 9 #i n c l u d e #i n c l u d e #i n c l u d e #i n c l u d e #i n c l u d e <a v r / i o . h> <a v r / i n t e r r u p t . h> < s t r i n g . h> <math . h> < u t i l / d e l a y . h> 11 13 15 17 19 // a k t u a l n a s r e d n i a c z e s t o t l i w o s c obrotow f l o a t czestotliwosc_srednia ; // t a b l i c a o s t a t n i c h 10 p o p r z e d n i c h w a r t o s c i c z e s t o t l i w o s c i b i e z a c e j // ( n i e u s r e d n i o n e j ) int tablica_czestotliwosci [ DLUGOSC ] ; // i n d e k s t a b l i c y , w k t o r y n a l e z y w p i s a c n a j n o w s z a w a r t o s c c z e s t o t l i w o s c i int indeks ; // suma o s t a t n i c h 10 c z e s t o t l i w o s c i , pamietana aby p r z y s p i e s z y c o b l i c z a n i e nowej // s r e d n i e j c z e s t o t l i w o s c i 13 21 23 25 27 int suma_czestotliwosci ; // s t a n l i c z n i k a w c h w i l i p o p r z e d n i e g o p r z e j s c i a p r z e z n a d a j n i k long poprzedni_czas ; // f l a g a do o c z e k i w a n i a , az u p l y n i e c z a s w y s w i e t l a n i a j e d n e g o p i k s e l a v o l a t i l e int flaga ; // f l a g a do o c z e k i w a n i a , az l i n i j k a o b r o c i s i e z t y l u , g d z i e z n a j d u j e // n a d a j n i k , do przodu , g d z i e n a l e z y w y s w i e t l i c n a p i s v o l a t i l e int obrot_flaga ; 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 // t a b l i c e z l i t e r a m i /* @ @ @ @ @ @ @@@@ @ @ @ @ */ c o n s t c h a r c o n s t H [ 6 ] = {0 x00 , 0 x7E , 0 x04 , 0 x04 , 0 x7E , 0 x 0 0 } ; /* @@ @ @ @@@@ @ @@ */ c o n s t c h a r c o n s t e [ 6 ] = {0 x00 , 0 x1C , 0 x2A , 0 x2A , 0 x18 , 0 x 0 0 } ; /* @ @ @ @ @ @@ */ c o n s t c h a r c o n s t l [ 6 ] = {0 x00 , 0 x00 , 0 x7E , 0 x02 , 0 x00 , 0 x 0 0 } ; /* 63 65 67 69 71 @@ @ @ @ @ @@ */ c o n s t c h a r c o n s t o [ 6 ] = {0 x00 , 0 x0C , 0 x12 , 0 x12 , 0 x0C , 0 x 0 0 } ; /* 73 75 77 79 81 @ @ @ @ @ @ @ @ @ */ c o n s t c h a r c o n s t w [ 7 ] = {0 x00 , 0 x1C , 0 x02 , 0 x04 , 0 x02 , 0 x1C , 0 x 0 0 } ; /* 83 85 87 89 91 93 95 97 99 @@ @ @ @ @ */ c o n s t c h a r c o n s t r [ 6 ] = {0 x00 , 0 x00 , 0 x0E , 0 x10 , 0 x18 , 0 x 0 0 } ; /* @ @ @@@ @ @ @ @ @@@ */ c o n s t c h a r c o n s t d [ 6 ] = {0 x00 , 0 x0C , 0 x12 , 0 x12 , 0 x7E , 0 x 0 0 } ; 14 sie 105 @ @ @ @ @ 107 @ 101 103 109 111 /* */ c o n s t c h a r c o n s t w y k r z y k n i k [ 6 ] = {0 x00 , 0 x00 , 0 x7A , 0 x00 , 0 x00 , 0 x 0 0 } ; /* 113 115 117 119 */ c o n s t c h a r c o n s t s p a c j a [ 6 ] = {0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x00 , 0 x 0 0 } ; 121 123 125 127 /* Obsluga p r z e r w a n i a od komparatora − wykonany z o s t a l p e l n y o b r o t , f o t o t r a n z y s t o r z a c z a l p r z e w o d z i c , na r e z y s t o r z e w y s t a p i l s t a n wysoki , k t o r y podawany j e s t na w e j s c i e o d e j m u j a c e komparatora . Na w e j s c i u ' + ' j e s t podane bandgap − n a p i e c i e r e f e r e n c y j n e ( ok . 1 , 2 3V) . Na w y j s c i u komparatora p o j a w i a s i e w i e c s t a n n i s k i , p r z y z b o c z u opadajacym . Przy t e j k o n f i g u r a c j i programu z b o c z e o p a d a j a c e d a j e p r z e r w a n i e od komparatora ( i j e d n o c z e s n i e uruchamia I n p u t Capture t i m e r a 1 ) . 129 131 133 135 137 139 141 143 145 147 149 151 153 155 157 159 161 163 W f u n k c j i o b s l u g u j a c e j p r z e r w a n i e ma m i e j s c e o b l i c z e n i e nowej c z e s t o t l i w o s c i o b r o t u p r z y w y k o r z y s t a n i u nowego w s k a z a n i e l i c z n i k a , a n a s t e p n i e o b l i c z a n a j e s t w a r t o s c s r e d n i a c z e s t o t l i w o s c i z o s t a t n i c h 10 obrotow */ ISR ( ANA_COMP_vect ) { // o d c z y t a n i e s t a n u l i c z n i k a z I n p u t Capture l o n g n o w y _ c z a s = ( I C R 1 H << 8 ) + I C R 1 L ; // o b l i c z e n i e nowej c z e s t o t l i w o s c i , p r e s k a l e r = 64 i n t nowa_czestotliwosc = F_CPU / ( ( nowy_czas − poprzedni_czas ) * 64) ; // p r z e s u n i e c i e i n d e k s u o j e d e n w prawo , z z a w i n i e c i e m po p r z e k r o c z e n i u z a k r e s u i n d e k s = ( i n d e k s +1)%D L U G O S C ; // o b l i c z e n i e nowej s r e d n i e j c z e s t o t l i w o s c i p o p r z e z o d j e c i e o s t a t n i e j i d o d a n i e // nowej − j e s t t o s z y b s z e n i z o b l i c z a n i e od nowa s r e d n i e j z e w s z y s t k i c h // w a r t o s c i suma_czestotliwosci = suma_czestotliwosci − tablica_czestotliwosci [ indeks ] + nowa_czestotliwosc ; tablica_czestotliwosci [ indeks ] = nowa_czestotliwosc ; czestotliwosc_srednia = suma_czestotliwosci / DLUGOSC ; poprzedni_czas = nowy_czas ; // u s t a w i e n i e f l a g i o b r o t u − program o c z e k u j a c y na z d a r z e n i e moze kontynuowac obrot_flaga = 1; } /* Obsluga p r z e r w a n i a od l i c z n i k a 0 , m i n a l c z a s w y s w i e t l a n i a j e d n e g o p i k s e l a , k t o r y zadawany j e s t na b i e z a c o p r z e z p o l e OCR0 − Output Compare */ ISR ( TIMER0_COMP_vect ) { // u s t a w i e n i e f l a g i , konczymy w y s w i e t l a n i e danego p i k s e l a , p r z e c h o d z i m y do // n a s t e p n e g o flaga = 1; } 165 167 169 /* Funkcja w y s w i e t l a zadana l i t e r e , k t o r a u m i e s z c z o n a j e s t w t a b l i c y c h a r [ 6 ] (w s z c z e g o l n y m przypadku c h a r [ 7 ] d l a s z e r s z y c h l i t e r , np . ' w ' ) . Czas w y s w i e t l a n i a j e d n e g o p i k s e l a u z a l e z n i o n y j e s t od z a d a n e j w p a r a m e t r z e ' s z e r o k o s c ' s z e r o k o s c i p i k s e l a o r a z a k t u a l n e j s r e d n i e j c z e s t o t l i w o s c i obrotow u r z a d z e n i a 171 173 175 177 179 Parametry : l i t e r a − w s k a z n i k na t a b l i c e c h a r [ 6 ] , w k t o r e j z a p i s a n a j e s t l i t e r a s z e r o k o s c − zadana s z e r o k o s c j e d n e g o p i k s e l a w m i l i m e t r a c h */ void WyswietlLitere ( const char * litera , i n t szerokosc ) { int i , t ; // o b l i c z e n i e l i c z b y impulsow , j a k i e musza z o s t a c z l i c z o n e w c e l u o d m i e r z e n i a // c z a s u w y s w i e t l a n i a j e d n e g o p i k s e l a w z a l e z n o s c i od z a d a n e j 15 // s z e r o k o s c i o r a z a k t u a l n e j c z e s t o t l i w o s c i obrotow t = szerokosc * F_CPU / (2 * M_PI * czestotliwosc_srednia * PROMIEN * 256) ; // z a d a n i e w a r t o s c i Output Compare OCR0 = t ; // w y s t a r t o w a n i e l i c z n i k a 0 p o p r z e z u s t a w i e n i a c z e s t o t l i w o s c i t a k t o w a n i a . Czas // w y s w i e t l a n i a j e d n e g o p i k s e l a o s z e r o k o s c i 5mm d l a c z e s t o t l i w o s c i 20Hz w y n o s i // ok . 250 mikrosekund , t a k w i e c musi z o s t a c w l a c z o n y p r e s k a l e r o w a r t o s c i co // n a j m n i e j 32 ( im j e g o w a r t o s c w i e k s z a , tym s z e r s z e p i k s e l e b e d z i e mozna // w y s w i e t l a c , j e d n a k tym m n i e j s z a b e d z i e d o k l a d n o s c s z e r o k o s c i . // Zdecydowalismy s i e uzyc p r e s k a l e r a o w a r t o s c i 256 TCCR0 = 0 b00000100 ; /* 0 0 0 0 0 1 0 0 */ /* \ \ \ c z e s t o t l i w o s c z e g a r a / 256 */ /* \ \ t r y b g e n e r o w a n e g o p r z e b i e g u , normalny */ /* \ brak f u n k c j i */ /* \ wybor z b o c z a d l a I n p u t Capture */ /* niewykorzystywana */ /* \ r e d u k c j a szumow na w e j s c i u I n p u t Capture * / /* niewykorzystywana */ // W y s w i e t l e n i e l i t e r y , d l a ' w ' t a b l i c a ma s z e r o k o s c 7 , d l a i n n y c h l i t e r 6 i f ( l i t e r a == w ) { f o r ( i =0; i<7;++i ) { // z a p a l e n i e l i n i j k i d i o d o w e j P O R T C = * l i t e r a ++; // o c z e k i w a n i e na m i n i e c i e wyznaczonego c z a s u while ( ! flaga ) ; // z e r o w a n i e l i c z n i k a i k o l e j n e o c z e k i w a n i e TCNT0 = 0; flaga = 0; } } else { f o r ( i =0; i<6;++i ) { P O R T C = * l i t e r a ++; while ( ! flaga ) ; TCNT0 = 0; flaga = 0; } } // z a t r z y m a n i e l i c z n i k a TCCR0 = 0; 181 183 185 187 189 191 193 195 197 199 201 203 205 207 209 211 213 215 217 219 221 } 223 225 227 229 /* Funkcja w y s w i e t l a n a p i s podany w a r g u m e n c i e na s r o d k o w e j l i n i j c e d i o d o w e j ( z i e l o n e j ) , kazdy p i k s e l ma s z e r o k o s c podana w p a r a m e t r z e ' s z e r o k o s c ' (w m i l i m e t r a c h ) . Funkcja na p o d s t a w i e b i e z a c e j s r e d n i e j c z e s t o t l i w o s c i obrotow o r a z s z e r o k o s c i n a p i s u wyznacza c z a s , j a k i p o t r z e b n y j e s t , aby p r z e m i e s c i c s i e o zadany o d c i n e k tak , aby s r o d e k n a p i s u z n a l a z l s i e ” z przodu ” w y s w i e t l a c z a ( j a k o t y l uznajemy m i e j s c e , g d z i e z n a j d u j e s i e d i o d a IR ) . 231 233 235 237 239 241 243 245 247 249 251 253 255 257 259 Parametry : − n a p i s − n a p i s w s e n s i e j e z y k a C, w ktorym z n a j d u j e s i e n a p i s do w y s w i e t l e n i a − s z e r o k o s c − zadana s z e r o k o s c p i k s e l i w m i l i m e t r a c h */ void WyswietlNapis ( char * napis , i n t szerokosc ) { int t ; int szerokosc_napisu = strlen ( napis ) * szerokosc ; // o b l i c z e n i e c z a s u , aby n a p i s w y s w i e t l a n y b y l z przodu t = ( M_PI * PROMIEN − szerokosc_napisu / 2) * F_CPU / (2 * M_PI * czestotliwosc_srednia * PROMIEN * 1024) ; // z a d a n i e w a r t o s c i Output Compare , j e s l i t >255 , t o z o s t a n i e z a p i s a n a // j e d y n i e r e s z t a z d z i e l e n i a t /255 OCR0 = t ; // z e z w o l e n i e na p r z e r w a n i e od OC T I M S K |= 0 b 1 0 ; // o c z e k i w a n i e , az d i o d y z n a j d a s i e z t y l u − p r z e r w a n i e od komparatora while ( ! obrot_flaga ) ; obrot_flaga = 0; // w y s t a r t o w a n i e l i c z n i k a 0 − k o n i e c z n e moze byc p r z e j s c i e nawet polowy o k r e g u // o p r o m i e n i u 14cm , na co p o t r z e b a 22ms , w i e c ustawiamy p r e s k a l e r na 1 0 2 4 , // co d a j e maksymalny c z a s 256 * 1024/16000000=16ms , moze s i e w i e c okazac , i z // l i c z n i k p r z e p e l n i s i e TCCR0 = 0 b00000101 ; // o c z e k i w a n i e , az d i o d y z n a j d a s i e z przodu while ( ! flaga ) ; flaga = 0; // j e s l i t r z e b a c z e k a c w i e c e j n i z 16ms , t o czekamy 2 r a z y i f ( t > 255) { 16 261 263 265 267 269 271 273 275 277 279 281 283 285 287 289 291 293 295 297 299 301 303 305 307 309 311 313 315 317 319 321 323 325 327 329 331 333 335 337 339 while ( ! flaga ) ; flaga = 0; } // z a t r z y m a n i e i w y z e r o w a n i e l i c z n i k a TCCR0 = 0; TCNT0 = 0; // w y s w i e t l e n i e n a p i s u while (* napis ) { switch (* napis ) { c a s e 'H ' : WyswietlLitere (H , szerokosc ) ; break ; case ' e ' : WyswietlLitere (e , szerokosc ) ; break ; case ' l ' : WyswietlLitere (l , szerokosc ) ; break ; case 'o ' : WyswietlLitere (o , szerokosc ) ; break ; c a s e 'w ' : WyswietlLitere (w , szerokosc ) ; break ; case ' r ' : WyswietlLitere (r , szerokosc ) ; break ; case 'd ' : WyswietlLitere (d , szerokosc ) ; break ; case ' ! ' : WyswietlLitere ( wykrzyknik , szerokosc ) ; break ; d e f a u l t : break ; } ++n a p i s ; } } i n t main ( void ) { // u s t a w i e n i e k i e r u n k u p r a c y portow A, C,D − w y j s c i o w y DDRA = 0 xff ; DDRC = 0 xff ; DDRD = 0 xff ; // u s t a w i e n i e k i e r u n k u p r a c y p o r t u B − w e j s c i o w y DDRB = 0; // u s t a w i e n i e s t a n u portow − s t a n n i s k i PORTA = 0; PORTC = 0; PORTD = 0; // i n i c j a l i z a c j a c z e s t o t l i w o s c i poczatkowych czestotliwosc_srednia = 0; suma_czestotliwosci = 0; poprzedni_czas = 0; flaga = 0; obrot_flaga = 0; f o r ( i n d e k s =0; i n d e k s <D L U G O S C ;++ i n d e k s ) tablica_czestotliwosci [ indeks ] = 0; indeks = 0; // u s t a w i e n i e d z i a l a n i a komparatora a n a l o g o w e g o ACSR = 0 b01001110 ; /* 0 1 0 0 1 1 1 0 /* \ \ p r z e r w a n i e od z b o c z a o p a d a j a c e g o /* \ u r u c h o m i e n i e I n p u t Capture l i c z n i k a 1 /* \ o d b l o k o w a n i e p r z e r w a n i a komparatora /* \ f l a g a p r z e r w a n i a komparatora /* \ w y j s c i e komparatora /* \ z r o d l o r e f e r e n c y j n e − bandgap /* \ w l a c z e n i e z a s i l a n i a komparatora */ */ */ */ */ */ */ */ /* k o n f i g u r a c j a t i m e r a 1 c z e s t o t l i w o s c z wewnetrznego kwarcu , p r e s k a l e r = 6 4 , uruchamia l i c z n i k , k t o r y z l i c z a do ok . 260ms , co z a p e w n i a brak p r z e p e l n i e n i a nawet d l a 4Hz c z e s t o t l i w o s c i obrotow */ TCCR1B = 0 b00000011 ; /* 0 0 0 0 0 0 1 1 */ /* \ \ \ c z e s t o t l i w o s c z e g a r a / 64 */ /* \ \ t r y b g e n e r o w a n e g o p r z e b i e g u , normalny */ /* \ brak f u n k c j i */ 17 /* /* /* /* 341 343 \ wybor z b o c z a d l a I n p u t Capture 1 niewykorzystywana r e d u k c j a szumow na w e j s c i u I n p u t Capture 1 niewykorzystywana \ */ */ */ */ 345 // g l o b a l n e o d b l o k o w a n i e p r z e r w a n SREG = 0 b10000000 ; // o c z e k i w a n i e , az s i l n i k s i e r o z p e d z i while ( czestotliwosc_srednia < 15) ; // w y s w i e t l e n i e z a d a n e g o n a p i s u w n i e s k o n c z o n e j p e t l i , s z e r o k o s c while (1) { / * Napis w y s w i e t l o n y na w y s w i e t l a c z u b e d z i e m i a l p o s t a c : gdzie : − dioda zgaszona , @ − dioda zapalona . 347 349 351 353 355 p i k s e l a = 5mm 357 @ @ @ @ @ @ @@@@ @ @ @ @ 359 361 363 @@ @ @ @@@@ @ @@ @ @ @ @ @ @@ @ @ @ @ @ @@ @@ @ @ @ @ @@ 365 367 @ @ @ @ @ @ @ @ @ 369 371 @@ @ @ @ @ @@ @@ @ @ @ @ @ @ @ @ @ @@ @ @ @@@ @ @ @ @ @@@ @ @ @ @ @ @ 373 */ W y s w i e t l N a p i s ( ” H e l l o world ! ” , 5 ) ; } return 0; 375 377 } 13 Wnioski W ramach wykonanego projektu zapoznaliśmy się z problematyką projektowania układów elektronicznych w połączeniu z wybranym mikrokontrolerem. Nauczyliśmy się wykonywać te czynności z uprzednim rozważeniem możliwych problemów, a także uwzględnieniem parametrów fizycznych wybieranych przez nas elementów układu w celu poprawnego zrealizowania stawianego przed nimi zadania. Posiedliśmy również umiejętność stworzenia układu elektronicznego w programie Eagle, a także zaprojektowanie płytki drukowanej w tymże programie. Poznaliśmy też możliwości, jakie daje mikroprocesor ATmega32, ze szczególnym naciskiem na umieszczone w nim układy, takie jak komparator analogowy czy liczniki oraz sposób jego konfiguracji. Udało nam się zapoznać ze śrowiskami programistycznymi do programowania mikrokontrolerów AVR (AVRStudio wraz z symulatorem oraz WinAVR) oraz ze sposobem programowania i testowania właściwego, fizycznego układu. Od strony technicznej zagadnienie wyświetlacza widmowego również było niebanalene. Musieliśmy, oprócz dostatecznie szybkiej zmiany stanu diód, dostosować prędkość obrotową ramienia, a co za tym idzie dobrać silnik o pasujących parametrach oraz nauczyć się nim odpowiednio sterować. Dodatkowo koncepcja modułowa całości pozwoliła zapoznać się ze sposobami pomiaru prędkości obrotowej, w tym z wykorzystaniem pary fototranzystor–fotodioda. 18 Literatura [1] ULN2803 ULN2804 Octal High Voltage, High Current Darlington Transistor Arrays, 1996. [2] LPT 80 A NPN-Silizium-Fototransistor Silicon NPN Phototransistor, 1998. [3] NE555 SA555 - SE555 General Purpose Single Bipolar Timers, 1998. [4] IRL 80 A GaAs-Infrarot-Sendediode GaAs Infrared Emitter, 2001. [5] ATmega32 ATmega32L Preliminary, 2003. [6] DC-Motor Type M 28, 2003. [7] L4940 Series Very Low Drop 1.5A Regulators, 2003. [8] K. Górski. Timer ne555 regulator pwm 506-k. projekty/555_pwm_506k.htm. http://www.krzysztofg.elb.vectranet.pl/ [9] K. M. i Systemów Elektronicznych. Dokumentacja mikrokontrolera Atmega16 firmy Atmel. Wydział Elektroniki, Telekomunikacji i Informatyki Politechniki Gdańskiej, 2006. [10] Łukasz Hrapek. Wyświetlacz widmowy. Elektronika dla Wszystkich, 2002. 19