Quantcast
Channel: Majsterkowo - zrób to sam!
Viewing all 1332 articles
Browse latest View live

Drabinka rezystorowa – czyli jak podłączyć wiele przycisków do jednego złącza

$
0
0
drabinka rezystorowa
Czy zadaliście sobie kiedyś pytanie: a co gdy będę robił jakiś większy projekt z dużą ilością przycisków i zabraknie mi złącz? Niedawno wkroczyłem w grono szczęśliwych posiadaczy płytki Arduino UNO i też zadałem sobie to pytanie. 13 wejść cyfrowych + 5 analogowych - zbyt dużo do tego nie podłączę, a gdybym chciał np. zrobić jakąś klawiaturkę jako kontroler MIDI powiedzmy na 2 oktawy to mamy już 24 przyciski + dodatkowo kilka funkcyjnych. I tu z pomocą przychodzi mały "patent"... Drabinka rezystorowa to element zbudowany z kilku takich samych rezystorów. Możemy kupić je w gotowych obudowach, bądź zrobić samemu ze zwykłych oporników. Zasada ich działania jest podobna do potencjometru, z tym, że zamiast kręcić pokrętłem, rezystancję zmieniamy podłączając się do kolejnych nóżek. Można by zadać pytanie - "Po co takie coś? Do czego to mi się przyda? Nie lepiej dać potencjometr - w nim mam płynną regulację?"  Otóż podłączając sygnał przez taką drabinkę (sterując przepływem odpowiednimi przyciskami) do wejścia analogowego możemy dzięki zmianom napięcia odczytać który przycisk wcisnęliśmy. W teorii można by podłączyć aż 1022(!) rezystory, jednak w praktyce ciężko byłoby to wykonać ze względu na tolerancję oraz chociażby zależność rezystancji od temperatury. Przykład mój zaprezentuje na prostym układzie składającym się z diody i trzech przycisków. Po naciśnięciu pierwszego dioda zamiga raz, drugiego, dwa, i analogicznie dla trzeciego - trzy. Na początek schemat podłączenia. (Już poprawny) s1,s2,s3 - przyciski; p - potencometr; A0 - analogowe wejście nr 0; DIG4 - wyjście cyfrowe nr4; R - rezystory; LED - dioda led Na początek napiszemy prosty program, dzięki któremu dowiemy się, jakie wartości są odczytywane gdy naciśniemy poszczególne przyciski. kod Ustawiamy potencjometr mniej więcej w połowie. Pozwoli nam to w przyszłości na ewentualną kalibrację. Oczywiście możemy go równie dobrze pominąć. Odpalamy program i wciskamy po kolei przyciski. Dzięki temu po wciśnięciu poszczególnych przycisków w SerialMonitorze uzyskamy coś takiego: Dzięki tym danym możemy zabrać się za pisanie kolejnego kodu, tym razem już wykonującego jakąś czynność - czyli w tym wypadku miganie diodą. Ze względu na to, że wartości zawsze będą miały jakąś małą granicę błędu, w warunkach zaznaczyłem, że wybieramy wartości z pomiędzy. I to by było na tyle, program możemy modyfikować w zależności od naszych potrzeb (zmieniamy kod w warunkach if). Możemy też oczywiście dodać więcej przycisków, dodajemy pary rezystor- przycisk oznaczone czerwonym prostokątem na pierwszym rysunku. Miłej zabawy :) wideo: http://www.youtube.com/watch?v=szDjfWRYkYg PS:Dobra, ze schematem były przeboje, ale dzięki Waszej interwencji wszystko powinno być już OK. Czytaj dalej...

Prosty system powiadamiania o nowych listach znajdujących się w skrzynce pocztowej

$
0
0
system-powiadamiania-o-listach-ikona
Nie wiem jak Wy, ale ja strasznie nie lubię zimą wychodzić z ciepłego domu, żeby zobaczyć, czy w skrzynce na listy nie pojawiło się coś nowego. Jeżeli nie spodziewam się żadnej poczty, to problemu nie ma, bo do skrzynki mogę zajrzeć nawet wieczorem wychodząc z psem na spacer. Gorzej, jeżeli akurat z niecierpliwością na coś czekam - wtedy potrafię co godzinę dreptać do skrzynki ;) Niestety minusowe temperatury bardzo zniechęcają do tych spacerów, dlatego postanowiłem zmontować sobie prosty system, który będzie mnie powiadamiał za pomocą diody LED o nowych listach znajdujących się w skrzynce. Czytaj dalej...

Jak zapisywać w komputerze dane odbierane z czujników podłączonych do Arduino?

$
0
0
coolterm-ikona
Od czasu do czasu różne osoby podpytują mnie na Majsterkowym fanpage, na stronie na Google+ i na naszym czacie, jak najprościej zapisywać dane odbierane z różnych czujników podpiętych do Arduino, żeby móc później zrobić na ich podstawie np. jakiś wykres, lub użyć ich do dalszych obliczeń. Żeby nie musieć powtarzać tego samego każdemu z osobna, postanowiłem przygotować prosty poradnik, w którym pokażę Wam, jak dane z Arduino zapisać bezpośrednio na komputerze i zrobić z nich wykres w Excelu - i to bez żadnych dodatkowych kosztów :) Czytaj dalej...

Arduino steruje segmentowym wyświetlaczem LED

$
0
0
Wyświetlacz LED

Pewnie wielu z Was podłączało kiedyś segmentowy wyświetlacz LED do Arduino i zastanawiało się co będzie jak zabraknie wyjść cyfrowych do ich sterowania. Otóż można wykorzystać interfejs komunikacyjny SPI i rejestry przesuwne. Ale żeby nie namącić Wam w głowach opowiem o wszystkim po kolei.

DSC_0155Po pierwsze układ

Będzie potrzebne oczywiście Arduino. Osobiście polubiłem wersję Nano V3 tak, więc na nim będą zaprezentowane przykłady. Najważniejsze z punktu widzenia tegoż projektu jest znajomość wyprowadzeń w Arduino. Do sterowania wyświetlaczem LED, a właściwie rejestrem przesuwnym, który tym wyświetlaczem steruje potrzebne jest zlokalizowanie wyprowadzeń SPI w Arduino. Poniższe zdjęcie chyba najlepiej to zobrazuje.

Wyprowadzenia SPI

Interfejs SPI jest interfejsem szeregowym, którym przesyłane są po kolei bajt po bajcie (informacje) pomiędzy urządzeniami Master i Slave. W tym przypadku urządzeniem Master będzie Arduino, natomiast Slave – rejestr przesuwny HCF4094. Ale wracając do wyprowadzeń. Najważniejsza jest możliwość przesyłania danych. Dane przesyłać będą wyjściem MOSI czyli Master Out Slave In. Wejściem MISO czyli Master In Slave Out nie będziemy się teraz zajmować bo taki wyświetlacz nie ma dla Arduino żadnych informacji. Wyjście SCLK lub CLK, SCL, różnie ludzie to nazywają, jest to wyjście zegarowe, które pozwala zgrać w czasie pracę urządzeń Master i Slave. Natomiast wyjście CS lub jak niektórzy mówią SS jest niczym innym jak Chip Select lub Slave Select, pozwala na wybranie urządzenia z którym ma zajść komunikacja. Pin CS w Arduino podawany jest jako pin 10, ale nie jest to jakieś ograniczenie. Chip Select’em może być dowolne cyfrowe wyjście. To tyle od strony wyprowadzeń o SPI.

Następnym elementem jest wcześniej wspomniany rejestr przesuwny HCF4094. Akurat wykorzystałem rejestr firmy ST, ale jeśli się nie mylę wszystkie 4094 mają znormalizowane wyprowadzenia, ale najlepiej sprawdzajcie w notach katalogowych. U mnie przedstawiało się to w sposób następujący.

Wyprowadzenia 4094

Ok, ale co z czym się je. Do wejścia STROBE podłączacie CS z SPI. DATA z MOSI i SCLK z CLOCK. Do VDD podłączacie zasilanie np. 5V z Arduino. OUTPUT ENABLE jest swojego rodzaju sygnałem dla rejestru, żeby zasilił wyjścia od Q1 do Q8. A że wyświetlacz ma działać od zaraz i nie chcę zbytnio komplikować prostej sprawy to zwarłem OUTPUT ENABLE z VDD w takim wypadku rejestr ma zezwolenie na pracę bez potrzeby angażowania do tego Arduino. VSS to oczywiście masa (GND). Przyszła kolej na wyjścia Q1Q7. Zakładając, że nasz wyświetlacz ma wspólną katodę to owe wyjścia łączymy z odpowiednimi segmentami wyświetlacza LED. O ile tego co wyżej nie trzeba tłumaczyć to parę słów przydałoby się powiedzieć Q’s i Qs. Każdy rejestr ma na tyle wyprowadzeń że starczy na jeden wyświetlacz LED – pomijam w tym przypadku wyświetlacze powielone, o nich może kiedy indziej. Tak, więc aby połączyć większą ilość rejestrów przesuwnych należy jak wyżej odpowiednio podłączyć wszystkie wyprowadzenia z tą różnicą, że pin DATA kolejnego rejestru podłączamy do wyprowadzenia Qs (pin 9) poprzedniego rejestru. Tym zabiegiem łączymy rejestry szeregowo. Oczywiście wyprowadzenia Q1-Q7 podłączamy odpowiednio do kolejnego wyświetlacza LED. Powinno to wyglądać mniej więcej w ten sposób.

SevenSegment_LED_Display_Counter_aa

Jakby ktoś potrzebował to wewnętrzna struktura takiego pojedynczego wyświetlacza LED przedstawia się następująco.

jp-7_segment_display_labeledCzas na program

Jeśli wszystko mamy już podpięte to nie pozostało nam nic innego jak zaprogramować Arduino odpowiednim programem. Jeśli chodzi o ważniejsze miejsca w kodzie to myślę, że wystarczy odpowiedni komentarz na końcach linii.

[crayon-50f33d12d2edc/]

Jak widać w kodzie zdefiniowałem tablicę znaków (cyfr) w postaci bitowej. „Jedynka” oznacza włączenie odpowiedniego segmentu. Jeśli wszystko zrobiliście dobrze to efekt powinien być taki jak na poniższym filmiku.

http://www.youtube.com/watch?v=EpaCvfdMNGc Z uwagi na to, że SPI jest interfejsem szeregowym to w najprostszej jego postaci bajt, znaki, dane czy jak tam wolicie wysyłane są jeden po drugim, zatem, żeby na wyświetlaczu uzyskać obraz właściwy (nie lustrzany) najpierw wysyłamy bajt najstarszy, a kolejno młodsze. Jak sama nazwa wskazuje rejestry przesuwne przesyłają szeregowo do siebie informacje począwszy od pierwszego na lini (BUS). No ale dobra, żeby nie było zbyt nudno to spróbujcie zrobić coś takiego.

SevenSegment_LED_Display_Counter_bb

A Arduino zaprogramujcie następującym kodem. [crayon-50f33d12d2f55/] Jak powyższy kod działa sprawdźcie sami.

Osobiście podobny wyświetlacz robię dla zaprzyjaźnionej firmy. Mają maszynę gdzie jest zwykły LCD 6x1 i niestety przy pewnych warunkach nasłonecznienia pracownicy muszą wytężać wzrok. A może sami macie jakieś pomysły na zastosowanie takiego wyświetlacza, nie wiem, może wyświetlacz aktualnego biegu w samochodzie ?

Dla tych, którzy by się dziwili stosowania tylu zmiennych globalnych w prostym kodzie powiem tylko, że to są DOBRE PRAKTYKI programowania. Przydadzą Wam się nawyki przy moich bardziej „skomplikowanych” projektach. Czytaj dalej...

Szafka w biurku zamykana RFIDem

$
0
0
2013-01-11-820

W dzisiejszym wpisie chciałbym zaprezentować Wam jak za pomocą Arduino (a później także mikroprocesora) zrobić sobie fajny zamek do szafki w biurku - fajny, albowiem nie na tradycyjny klucz lecz karty zbliżeniowe RFID.

Gadżet sprawi, że do otwarcia szafki potrzebna będzie karta, którą można nosić w portfelu oraz wiedza, gdzie taką kartę przytknąć. Czytaj dalej...

Zdalnie sterowany fotel pneumatyczny

$
0
0
fotel-biurowy-eago-004_323
Jako wielki fan motoryzacji często z zafascynowaniem oglądam filmy ze zlotów samochodowych. Ciekawą modyfikacją  w samochodach jest montaż  AIR RIDE, czyli w skrócie możliwość regulacji wysokości zawieszenia.  Auto takie ma pulpit bądź też pada którym steruje się przód/tył (lub niezależnie każde koło). Pomysł jest fajny i co prawda stosuje się tam poduszki pneumatyczne ale ja postanowiłem przerobić swoje krzesło montując w nim siłownik. Zamieszczam filmik pokazujący wygląd i działanie fotela. http://youtu.be/7S71dFsOFBg Czytaj dalej...

Wieszak na rower

$
0
0
SAMSUNG
Nie wiem jak wy ale ja często mam problem z wydostaniem roweru z piwnicy. Czasem by go wyjąc muszę przestawić 3 lub 4 rowery. Miałem tego serdecznie dość. I w tedy wpadłem na  pomysł by wykonać sobie wieszaki, na których zawisną wszystkie rowery jakie posiadam. Jak postanowiłem tak i zrobiłem, a tu możecie zobaczyć efekt mojej pracy. Do wykonania wieszaka użyłem: Drut o grubości 5 mm wygięty w taki  oto sposób. SAMSUNGBlachę, dociętą na szerokość 5 cm i wygiętą w ten oto sposób. Do wygięcia tej blachy użyłem  wcześniej wspomnianego druta. SAMSUNG Potrzebowałem również wiertło, kołki rozporowe oraz wężyk paliwowy.SAMSUNG Po wywierceniu otworów w ścianie montujemy gotowy wieszak. SAMSUNG A oto efekt końcowy. SAMSUNG Zdaję sobie sprawę, że w sklepie można nabyć gotowe wieszaki ale ich cena jest dosyć spora, rzędy 40 zł za szt. A przy sześciu rowerach robi się spora sumka.  Mam nadzieję że mój pomysł wam się spodobał. Przepraszam was za tak małą ilość zdjęć, ponieważ o stworzeniu takiego postu pomyślałem dopiero po wykonaniu wieszaka. Czytaj dalej...

Zegar ścienny ze starej płyty głównej

$
0
0
zegar-z-plyty-glownej-ikona-1
Moja Kasia już od dłuższego czasu suszyła mi głowę, żebym powiesił w pokoju jakiś zegar. Oczywiście, jak na majsterkowicza przystało, postanowiłem zrobić ten zegar samemu, zamiast wieszać na ścianie jakąś tandetę z marketu ;)

Z czego to zrobić?

Długo się zastanawiałem, z czego zrobić fajną tarczę do zegara. Koncepcji miałem kilka, ale żadna z nich mnie nie satysfakcjonowała. Dopiero w czasie ciągnącej się w nieskończoność drogi powrotnej z warsztatów w Warszawie przypomniałem sobie o spalonej jakiś czas temu płycie głównej, która idealnie powinna się do tego nadać. Płyta głównaCzytaj dalej...

Metronom z dysku twardego

$
0
0
metronom-z-dysku-twardego-ikona-2
Plumkając sobie ostatnio na gitarze doszedłem do wniosku, że przydałby mi się metronom. Mam co prawda metronom w formie programu na PC, jak i aplikacji na Androida, ale zamarzył mi się taki prawdziwy metronom sprzętowy :) I nie mam tutaj na myśli jakiegoś piszczącego pudełeczka, ale coś bardziej mechanicznego... Początkowo myślałem nad wykorzystaniem serwomechanizmu, ale szybko wybiłem sobie ten głupi pomysł z głowy ;) Serwa same w sobie są głośne i tylko by przeszkadzały w czasie gry. Musiałem więc znaleźć coś innego, co pozwoliłoby wystukiwać wyraźny rytm, ale jednocześnie pracować bezszelestnie. I wtem spojrzałem na swoją szlifierkę z dysku twardego i mnie olśniło - w roli metronomu idealnie powinno się sprawdzić ramię głowicy z dysku HDD :D Czytaj dalej...

Wyświetlacz ze starego Siemens’a M55 sterowany przez Arduino + TERMOMETR DS18B20

$
0
0
SONY DSC
Witajcie, dziś przedstawię wam poradnik jak podłączyć wyświetlacz LM15SGFNZ07 który wygrzebiecie z telefonów Siemens M55, S55, A60, C60, MC60. Na aukcjach można pozyskać uszkodzone telefony za ok 10zł. a może nawet macie jakiś gdzieś w czeluściach swoich szuflad :) Jako że ja dostałem od kumpla stary zepsuty Siemens M55 i znalazłem informacje jak wykorzystać z niego LCD to postanowiłem napisać dla was ten poradnik :) 2013-02-14 21.17.39 Poradnik powstał przy zebraniu materiałów z kilku źródeł w których były bardziej szczegółowo opisane zasady działania. Ja postaram się je przedstawić bardziej zrozumiale dla laików bo sam jestem początkujący w świecie Arduino jak i elektroniki. Głównie to moje hobby :) Przejdźmy więc do rzeczy :)

Wyświetlacz:

Wyświetlacz posiada 10 padowe złącze do którego przylutowałem kabelki ale można też wykorzystać taśmę (akurat nie miałem jej pod ręką).

Wymiary wyświetlacza to 101x80px.

SONY DSC

Piny od wyświetlacza:

SONY DSCSONY DSC 
  • Pin 1 - [ Cs ]
  • Pin 2 - [ Reset ]
  • Pin 3 - [ Rs ]
  • Pin 4 - [ Sclk ]
  • Pin 5 - [ Sdata ]
  • Pin 6 - [ +2.9V ]
  • Pin 7 - [ Gnd ]
  • Pin 8 - [ Led1 + ]
  • Pin 9 - [ Led1, Led2 GND ]
  • Pin 10 - [ Led2 + ]

Podłączenie do Arduino:

arduino LCD - Arduino
  • Pin 1 - Pin 6 (Rezystor 10kΩ)
  • Pin 2 - Pin 5 (Rezystor 10kΩ)
  • Pin 3 - Pin 4 (Rezystor 10kΩ)
  • Pin 4 - Pin 3 (Rezystor 10kΩ)
  • Pin 5 - Pin 2 (Rezystor 10kΩ)
  • Pin 6 - 3V3 (Producent zaleca 2.9V ale 3.3V mu nie zaszkodzi)
  • Pin 7 - Gnd
  • Pin 8 - 3V3
  • Pin 9 - Gnd
  • Pin 10 – 3V3
Według tego poradnika należy dać rezystory 4.7kΩ ja dałem 10kΩ (takie posiadałem) czyli większe i działa dobrze. Jak wszystko jest już podłączone to zostało nam pobrać biblioteki i wgrać kod.

Potrzebne biblioteki:

Biblioteki wrzucamy do folderu /libraries/

Kod:

[crayon-511ebcb48e7e6/] 

Opis funkcji

  • send_lcd(unsigned char data) - Funkcja wysyłąjaca bajt do LCD.
  • init_lcd(void) - Funkcja inicjalizująca LCD, należy ja dodać na początku funkcji main()
  • contrast_lcd(unsigned char contrast) - Funkcja ustawiająca kontrast (przyjmuje dane w postaci 12 bitowej np: contrast_lcd (0x11f)
  • window_lcd(unsigned char xstart, unsigned char ystart, unsigned char xend, unsigned char yend) - Funkcja rezerwująca w pamięci dany obszar
  • cursor_lcd(unsigned char row, unsigned char col) - Funkcja ustawiajaca pozycję kursora
  • pixel_lcd(unsigned char x, unsigned char y,unsigned int color) - Funkcja rysująca pixel w danym kolorze; x jest numerem pixela wzdłuż osi poziomej od lewej strony, y wzdłuż osi pionowej od dołu ekranu.
  • clear_lcd(unsigned int col) - Funkcja zapełniajaca cały ekran wybranym kolorem
  • line_lcd(unsigned char x1,unsigned char x2, unsigned char y1, unsigned char y2, unsigned int color) - Funkcja rysująca linię, x adresowane jest od lewej strony, y od dołu
  • area_lcd(unsigned char x,unsigned char y, unsigned char height, unsigned char width, unsigned int color) - Funkcja zapełniająca wybrany obszar kolorem, x adresowane jest od lewej strony, y od dołu, szerokosć w prawa stronę od punktu poczatkowego, wysokosć w górę
  • frame_lcd(unsigned char x,unsigned char y, unsigned char height, unsigned char width, unsigned char size, unsigned int color) - Funkcja rysująca ramkę, wszystko adresowane tak jak dla funkcji area_lcd, przedostatni argument oznacza szerokość linii ramki
  • chr_lcd( char chr, unsigned char x, unsigned char y, unsigned int size, unsigned int color_font, unsigned int color) - Funkcja pisząca znak. Argumenty: x-numer pozycji znaku, y-numer linii znaku, size-rozmiar czcionki (1 lub 2), font_color-kolor czcionki, color-kolor tła czcionki(jesli -1 rysowana jest sama czcionka bez tła)
  • str_lcd(char *str, unsigned char x, unsigned char y, unsigned int size, unsigned int color_font, unsigned int color) - Funkcja pisząca tekst, argumenty jak wyżej,  przykład: str_lcd("TEST LCD",1, 2, 2, RED, WHITE);

Źródło: Obsługa LCD z siemensa M55. Biblioteki z języku C dla AVR. - Drzasiek

Kolory są zdefiniowane w bibliotece (plik LM15SGFNZ07.h)

#define WHITE 0xFFF  // Biały #define BLACK 0x000 //Czarny #define RED 0xF00 // Czerwony #define GREEN 0x0F0 // Zielony #define BLUE 0x00F // Niebieski #define CYAN 0x0FF #define MAGENTA 0xF0F #define YELLOW 0xFF0 //Żółty #define BROWN 0xB22 // Brązowy #define ORANGE 0xFA0 //Pomarańczowy #define PINK 0xF9E // Różowy Przetłumaczyłem dla nieznających angielskiego ale kolory to chyba każdy powinien znać :D Możliwe że można zdefiniować ich więcej ale jeszcze nie próbowałem.

Mamy już działający wyświetlacz a teraz dodajmy do niego termometr :)

Potrzebny nam będzie popularny czujnik temperatury DS18B20 oraz rezystor 4,7kΩ podłączony według schematu. Termometr_DS18B20 

Potrzebne biblioteki:

Biblioteki wrzucamy do folderu /libraries/Kod: [crayon-511ebcb48e87c/] 

Tak to działa:

http://youtu.be/wJlOVzwmSPQ No i to by było na tyle. Mam nadzieję że instrukcja ta będzie dla was pomocna i zrobicie pożytek ze swojego starego telefonu :) Czytaj dalej...

VU Meter – czyli diody świecące w rytm muzyki

$
0
0
vu meter
Dziś pragnę wam przedstawić poradnik na bardzo prosty wskaźnik wysterowania "VU Meter" oparty na Arduino. Czemu akurat Arduino a nie prosty schemat oparty o układy LM3914, LM3915 lub LM3916? Ponieważ dzięki Arduino to od nas zależy jak diody mogą świecić i możemy ten układ udoskonalić o dodatkowe funkcje. Oczywiście w ramach nauki programowania, modyfikując kod :) vu meter

vu meter   wzmacniacz

Co będzie nam potrzebne?

  • Arduino
  • Płytka stykowa
  • 10 szt. diód LED (dyfuzyjnych)
  • 11 szt. rezystorów 220Ω
  • 1 szt. rezystor 2.2KΩ
  • 1 szt. kondensator 1μF
  • 1 szt. kondensator 10μF
  • Układ LM386
  • Mikrofon elektretowy

Schemat połączeń na płytce stykowej

Biegunowość mikrofonu - masa (GND) jest zawsze w wyprowadzeniu które łączy się z obudową.

 

Schemat ideowy:

VU_METER_SCHEMAT Jest to najbardziej uproszczony schemat wzmacniacza jaki znalazłem w sieci który działa całkiem nieźle :) Aby można było ustawić czułość mikrofonu w miejsce rezystora możemy wstawić potencjometr 10KΩ z_potencjometrem 

Zalecam podłączenie Arduino pod zasilacz gdyż przy podłączeniu z USB wzmacniacz nie działa prawidłowo. Zapewne z powodu zbyt słabego napięcia.

  Kod: [crayon-5135067e52657/] Aby zmienić tryb świecenia diód odkometujcie sobie tą część kodu: [crayon-5135067e526bd/]

Polecam zmienić: [crayon-5135067e52717/] na [crayon-5135067e5277c/] aby polepszyć czułość.  

Zobacz jak działa:

http://youtu.be/oP1bUb5K800 Zbudowałem taką oto obudowę do niego i fajnie się prezentuje :) http://youtu.be/lDDc6PsInoM Na filmiku powstaje dziwne złudzenie że są tutaj 2 kanały STEREO ale tak naprawdę jest to jeden kanał MONO z mikrofonu :) W końcowej fazie zmontowałem układ na płytce uniwersalnej i wykorzystałem Atmega8 (koszt 5zł.) Cały układ wyniósł mnie trochę drożej. Coś na oko 20-30zł. Obudowa to już trochę ponad 100zł. bo wykorzystałem sklejkę dociętą w markecie :D Oczywiście półeczki wewnątrz sam musiałem dociąć bo zapłacił bym dużo więcej. Ja na początku korzystałem z modułu płytki mikrofonu z wzmacniaczem z Nettigo http://nettigo.pl/products/92 którą również polecam ;) Czytaj dalej...

Twitterowa drukarka statusów

$
0
0
Twitterowa drukarka statusów
Kilka tygodni temu kupiłem sobie małą drukarkę termiczną, którą dzięki interfejsowi TTL można bardzo prosto podpiąć do Arduino. Drukarkę kupiłem z myślą o zastosowaniu jej w pewnym fajnym (i póki co tajnym;) projekcie, ale najpierw postanowiłem się nią nieco pobawić, więc zrobiłem sobie drukarkę statusów z Twittera :D Jak na tak bezużyteczny gadżet, jakim niewątpliwie jest Twitterowa drukarka, części kosztują dosyć dużo. Pamiętajcie jednak, że sporą część moich projektów trzeba traktować raczej jako wskazanie drogi i przykład zastosowania, a nie jak coś, co obowiązkowo każdy z Was powinien sobie zrobić. Tak jest i tym razem :) Ja do zabawy zrobiłem sobie Twitterową drukarkę, ale Ty możesz sobie zrobić coś dużo bardziej pożytecznego :) Drukarka termiczna TTL

Co nam będzie potrzebne?

Do zrobienia Twitterowej drukarki wykorzystałem:

Jak zrobić Twitterową drukarkę?

Sam układ jest banalnie prosty i jego złożenie ogranicza się w zasadzie do podłączenia tylko kilku przewodów. Na początku wpinamy Ethernet Shield w Arduino, a następnie podłączamy drukarkę w ten sposób: Twitterowa drukarka Całe Arduino możemy oczywiście zasilać z tego samego zasilacza.

Program sterujący drukarką

W Twitterowej drukarce wykorzystałem bibliotekę Thermal Printer, którą po ściągnięciu trzeba wypakować i skopiować do folderu /libraries/ w katalogu z Arduino IDE. Po skopiowaniu biblioteki wystarczy wgrać do Arduino następujący program: [crayon-51390b7f1b098/] Program nie jest mój (znalazłem go w sieci) i prawdę mówiąc pewnej jego części do końca nie rozumiem, więc nawet nie będę próbował go omawiać ;) Program ma pododawane ładne komentarze, więc dociekliwi mogą go sobie przeanalizować :) Co robi powyższy program? Co 25 sekund odpytuje serwery Twittera o statusy, w których występuje słowo "majsterkowo". Jeżeli pojawią się nowe tweety, są od razu drukowane. I to w sumie cała filozofia :) Z rzeczy najważniejszych:
  • w linii 38 możemy sobie ustawić, jak często mają być pobierane nowe Tweety
  • w linii 58 ustawiamy słowo, które ma być wyłapywane w statusach
  • w liniach od 219 do 227 jest to, co leci do drukarki
  • drukować możemy także godzinę wysłania statusu - siedzi ona w zmiennej timeStamp

Drukujemy Tweety!

Po włączeniu zasilania i podłączeniu kabla od internetu wszystko powinno już działać. A działa to mniej więcej tak: http://www.youtube.com/watch?v=CnUi4au_Dno

Trochę o kosztach druku

Kilka osób pytało mnie, jak droga jest eksploatacja tej drukarki. Otóż jest ona bardzo tania w użyciu. Powiedziałbym nawet, że śmiesznie tania ;) Kilka dni temu powiedziałem swoim znajomym o tym, że stojąca u mnie w warsztacie drukarka będzie drukowała ich wiadomości. Ustawiłem im nawet kamerkę i zrobiłem stream, żeby mogli sobie oglądać, jak ich wiadomości wychodzą z drukarki :D Przez kilka godzin znajomych drukarka wydrukowała około 100 statusów zużywając na to ~4 metry papieru. Jedna rolka papieru o szerokości 57mm i długości 20m (takie wchodzą do tej drukarki) kosztuje 1zł, więc wydrukowanie tych 100 tweetów kosztowało jakieś... 20gr :)

Stream na żywo!

Jeżeli chcecie sprawdzić, czy to rzeczywiście działa, to pod tym adresem znajdziecie stream z kamerki umieszczonej przed moją drukarką: https://new.livestream.com/accounts/1974731/events/1915177. Wystarczy, że tweetniecie status zawierający słowo majsterkowo (a także @majsterkowo i #majsterkowo), a po chwili zostanie on u mnie wydrukowany :D W tym miejscu chciałbym też podziękować Tomkowi z zenbox.pl za podesłanie swojej starej kamerki :) Ta moja znaleziona kiedyś w chipsach dawała tak kiepski obraz, że nie dało się odczytać żadnego statusu. Teraz jest już o niebo lepiej, chociaż nie obyło się bez umieszczenia lupy między kamerką a drukarką :D Kamerka z lupą Kiedyś jednak będę musiał zainwestować w jakąś lepszą kamerę, co by nie musieć się tak za każdym razem bawić ;) Stream będzie dostępny przez kilka najbliższych dni. Najprawdopodobniej będę go jedynie wyłączać na noc (drukarka cały czas jest doświetlana lampką, przy której ciężko by się spało;) A tak odbiegając troszeczkę od tematu Twittera - macie jakieś fajne pomysły, do czego jeszcze można by wykorzystać taką drukarkę? Czytaj dalej...

Własny internetowy termometr

$
0
0
Snap_2013.03.09_10h53m20s_001_

W swoim już drugim wpisie na Majsterkowo chciałbym pokazać Wam jak zrobić za pomocą Arduino (oczywiście następnie mikroprocesora) Internetowy termometr. Taki projekt może być przydatny dla Was i Waszych znajomych, w szczególności w miastach które nie mają swoich własnych stacji pogodowych. Termometr wyświetli na żywo aktualny podgląd temperatury oraz pokaże wykres jej zmian.

311317_145481232286174_801433871_n

Nie wszystkie z tych czynności będą realizowane przez układ, większość z nich wykona serwer PHP, gdyż lepiej się sprawdzi przy większych obciążeniach. Przykład działania można zobaczyć u mnie: podgląd temperatury& archiwum zmian temperatur& fanpage.

Potrzebne elementy

  • Płytka stykowa 400
  • LCD Kit 2x16 (możesz pominąć, jeśli nie chcesz widzieć podglądu fizycznie)
  • 2x Czujnik temperatury DS18B20 (działa na złączu OneWire i jego dokładność to 0,5 stopnia, analogowy sensor jest łatwiejszy do zakodowania, jednak o wiele mniej dokładny)
  • ATmega 328P-PU (w mniejszej nie zmieści się biblioteka OneWire wraz z biblioteką modułu Ethernet)
  • Moduł Ethernet ENC28J60 (np. z eBaya)
  • Rezystor 4,7k
  • Kawałek skrętki UTP lub innego kabla, tak aby móc wystawić termometr za okno
  • (opcjonalnie) 3 goldpiny aby łatwiej wtykać kabel w płytkę

Potrzebne biblioteki

Biblioteki należy umieścić w folderze libraries naszego Arduino IDE. Oczywiście umieszczamy je w folderach takich w jakich są, nie luzem. UWAGA! Biblioteka ENC28J60 pokrywa się z funkcjami biblioteki Ethernet Shield, dlatego musimy ją najpierw usunąć z katalogu libraries!

Schemat podłączeń

Posiadamy już wszystkie potrzebne biblioteki oraz części, pora przejść do ich podłączenia. Zastosowałem 2 termometry gdyż docelowo mierzę temperaturę wewnętrzną oraz zewnętrzną. Oczywiście termometr mierzący temperaturę zewnętrzną nie jest wetknięty w płytkę, przylutowałem do niego 3 kabelki ze skrętki UTP i wystawiłem za okno. Po drugiej stronie kabla przylutowałem 3 goldpiny tak aby łatwiej móc podłączyć kabelek do płytki.

temperatury_okno_bb 

Moduł ENCJ28J60 którego użyłem podłączyłem do zasilania 5V. Uczyniłem to dlatego, że model jaki posiadam toleruje takie napięcie i upraszcza to dalszą część czyli przenoszenie kodu na ATmegę, jednak według dokumentacji moduł ten powinno zasilać się napięciem 3,3V!

Kod dla mikroprocesora

[crayon-513bab8a959e3/]

Wyjaśnienia może wymagać tutaj kwestia odliczania między wysłaniem żądania pobrania temperatur a odczytaniem ich wyników. Otóż normalnie program po wysłaniu żądania oczekiwał by na jego zakończenie i dopiero przeszedł dalej. Jest to działanie niepożądane gdyż blokowało by możliwości przyjmowania w tym czasie połączeń internetowych. W związku z tym czujniki zostały ustawione tak, aby temperatura pobierana była w tle. Nie zmienia to jednak faktu, że zanim temperatura będzie do odczytania musi upłynąć pewien okres czasu, jest on ściśle określony w dokumentacji. Wewnątrz pętli sprawdzam, czy czas już upłynął, jeśli tak to odczytuję temperatury i wysyłam nowe żądanie. Dzięki temu moduł ethernet jest w stanie działać cały czas i przyjmować połączenia nawet w trakcie oczekiwania na dane.

Po uruchomieniu programu na wyświetlaczu powinna ukazać się aktualna temperatura wewnętrzna oraz zewnętrzna, możemy teraz przejść dalej.

Kod dla serwera WWW

Z uwagi na to, że taki serwer WWW jaki został stworzony w powyższym kodzie nie był by w stanie realizować bardziej zaawansowanych funkcji dalszą część zrealizowałem na normalnym serwerze WWW z PHP oraz MySQL. Skrypt PHP nawiązuje połączenie z układem i pobiera z niego temperaturę. Zastosowałem taką metodę dlatego, że między serwerem WWW a domem mam stały tunel VPN. W pozostałych przypadkach prawdopodobnie potrzebne okaże się przekierowanie jednego z portów na routerze na IP naszego układu. Oczywiście można było to zbudować w drugą stronę, aby to układ łączył się z serwerem.

Struktura bazy danych

[crayon-513bab8a95a53/]

Pobieranie danych

Jak już wspominałem celem przeniesienia obsługi żądań na normalny serwer WWW było odciążenie układu z serwowania dużej ilości zapytań. W związku z tym nie miało by sensu aby każde zapytanie do strony z temperaturą odpytywało układ. Z tego powodu zdecydowałem się na napisanie pliku pobierającego dane do bazy i umieszczenie go w crontabie wykonywanym co 5 minut:

[crayon-513bab8a95ab4/]

W linii 6 temperatura mnożona wcześniej w układzie jest teraz dzielona tak aby otrzymać normalny wynik. Niestety bez takiego zabiegu nie udało się uzyskać temperatury z miejscami po przecinku. Linia 8 zabezpiecza nas przed pobraniem wartości zaraz po uruchomieniu układu, gdy jeszcze nie ma aktualnych danych o temperaturze.

Wyświetlanie danych

Do wyświetlania danych zastosowałem prosty skrypt PHP który następnie obsadziłem na blogu za pomocą iframki:

[crayon-513bab8a95b11/]

Rysowanie wykresów

To najbardziej złożona część tego projektu. Wykresy są rysowane za pomocą JavaScript przez przeglądarkę po otrzymaniu odpowiednich danych przez PHP. Plik ten docelowo również został wstawiony jako iframka na blogu.

Z uwagi na to, że listing kodu od wykresów jest długi, zamiast wstawiać go tutaj przygotowałem paczkę która zawiera zarówno powyższe kody, jak i kod wykresów oraz niezbędne do jego działania biblioteki JavaScript: kliknij aby pobrać.

Finalizacja projektu

Jak to na koniec realizowania projektu przystało, pora teraz przenieść się z Arduino na czystą ATmegę, tak aby Arduino mieć do kolejnych zabaw. Programujemy mikroprocesor oraz zmieniamy podłączenia kabelków na następujące:

temperatury_okno_atm_bb

Na sam koniec

Internetowy termometr to tylko jedno z przykładowych zastosowań, można go np. przerobić aby był bezprzewodowy, lub też np. zrobić tak aby zapalać sobie światło w pokoju przez Internet (ja tak zrobiłem) - całkiem fajny efekt gdy można zapalić światło bez wstawania od komputera lub przez telefon (jeśli chcecie mogę opisać) oraz wiele wiele innych. Jeżeli macie jakieś fajne pomysły - podzielcie się w komentarzach :)

Czytaj dalej...

Akumulatorowa ładowarka do smartfonów

$
0
0
akumulatorowe-zasilanie-smartfonow-ikona-1
W dzisiejszym poście pokażę Wam, jak ze zwykłych baterii zrobić dodatkowe (zapasowe) zasilanie do smartfonów (i innych urządzeń, które mogą być zasilane z USB). Podobne projekty prezentowałem już kiedyś na swoim starym blogu (wersja pierwsza i druga). Były to jednak konstrukcje kiepskie (delikatnie mówiąc) i strasznie mało wydajne. Dlatego też postanowiłem po latach ponownie podejść do tematu i wreszcie zrobić to tak, jak powinni być zrobione od samego początku.

Co nam będzie potrzebne?

Metalowa puszka, zasobnik na baterie, gniazdo USB, przełącznik, przetwornica

Przetwornica impulsowa Pololu S7V7F5

Swoją poprzednią ładowarkę oparłem o stabilizator 5V. Stabilizatory mają jednak tą wadę, że ich wydajność jest stosunkowo niska, a spora część energii jest oddawana w postaci ciepła. Dodatkowo musimy im zapewnić zasilanie przynajmniej o kilka woltów wyższe, niż napięcie wyjściowe. Chcąc więc mieć na wyjściu 5V, musieliśmy dostarczyć do stabilizatora minimum ~8V. Dużo lepszym, wręcz idealnym wyjściem jest zastosowanie przetwornicy impulsowej DC-DC. Przetwornica taka ma dużo większą sprawność (opisywana w tym poście przetwornica Pololu ma sprawność do 95%) i prawie się nie grzeje. Charakterystyki przetwornicy impulsowej W opisywanej niżej ładowarce zdecydowałem się wykorzystać przetwornicę Pololu S7V7F5: Przetwornica Pololu S7V7F5 Jest to przetwornica STEP-DOWN/STEP-UP, czyli może zarówno obniżać napięcie, jak i je zwiększać. Dzięki temu na wyjściu będziemy mieć stabilne 5V niezależnie od tego, czy włożymy cztery nowiutkie baterie AA dające 6V, czy częściowo rozładowane baterie, które w sumie dadzą 3V. W przypadku tej konkretnej przetwornicy napięcie wejściowe może wynosić od 2,7V do 11,8V.

Robimy akumulatorową ładowarkę USB

Pracę zaczynamy od wycięcia otworów pod gniazdo USB i wyłącznik: Metalowa puszka z wyciętymi otworami Następnie łączymy wszystko wg poniższego schematu i montujemy w obudowie: Schemat połączeniaPuszka z gniazdem USB i wyłącznikiem Koszyczek na baterie przykleiłem bardzo mocną gąbkową taśmą dwustronną, a gniazdo USB przykleiłem klejem epoksydowym (kleiłem dopiero później, więc nie szukajcie tego kleju na zdjęciach;) Puszka z gniazdem USB i wyłącznikiemZawartość akumulatorowej ładowarki USB Żeby uniknąć przypadkowego zwarcia (o które w metalowej puszcze wcale nie jest trudno) wsadziłem całą przetwornicę w koszulkę termokurczliwą: Zawartość akumulatorowej ładowarki USB z zabezpieczoną przetwornicą

Akumulatorowa ładowarka USB

Gotowa ładowarka prezentuje się tak: Akumulatorowa ładowarka USB Myślałem nad pomalowaniem puszki na czarno, ale chyba wtedy całość straciłaby swój urok ;)

Pomiary

Na koniec zrobiłem jeszcze małe pomiary i sprawdziłem jak długo taka ładowarka będzie w stanie ładować smartfona po wsadzeniu czterech zwykłych baterii alkalicznych. Załadowałem więc cztery paluszki z Ikei, podpiąłem całkowicie rozładowanego smartfona i rozpocząłem pomiary. Po zebraniu danych wyszedł mi taki oto wykres: Charakterystyki baterii z Ikei Linia koloru niebieskiego reprezentuje napięcie baterii. Linią czerwoną zostało wyrysowane napięcie na wyjściu ładowarki. Natomiast linia zielona pokazuje prąd ładowania. Jak widać, smartfon grzecznie ładował się przez nieco ponad 2 godziny. Po tym czasie baterie zaczęły się gwałtownie rozładowywać. Gdy ich napięcie spadło poniżej 2,7V przetwornica nie była już w stanie zapewnić na wyjściu ani pełnych 5V, ani tak dużego prądu. Smartfon jednak nie przerwał ładowania i przez kolejną godzinę wysysał z baterii resztę energii, doładowując się jedynie dużo mniejszym prądem. Po trzech godzinach smartfon odciął ładowanie i baterie odetchnęły z ulgą (co ładnie widać na samym końcu wykresu). Na dniach postaram się dorzucić jeszcze wykresy zrobione na podstawie pomiarów z akumulatorków 2700mAh :)

Podsumowując

Taka akumulatorowa ładowarka z pewnością niejednokrotnie uratuje naszą skórę, gdy będąc w terenie niespodziewanie smartfon (czy np. nawigacja) odmówi posłuszeństwa z powodu rozładowanej baterii. Sama ładowarka nie waży prawie nic, a baterie w razie potrzeby można kupić praktycznie wszędzie :) Czytaj dalej...

Zegar cyfrowy LED z bajerami – Część I: matryca LED

$
0
0
matryca-front
Od paru miesięcy śledzę z wypiekami stronę majsterkowo.pl, ale dopiero kilka tygodni temu kupiłem swój pierwszy zestaw startowy Arduino Uno w firmie Nettigo. W kolejnym tygodniu biegałem już niemal codziennie do paczkomatu, odbierać drogocenne, słodko grzechoczące kartoniki, pełne sensorów, potencjometrów, tasiemek, rezystorów, wyświetlaczy i przeróżnego elektronicznego złomu, który teraz zalega każdy zakamarek mojego biurka i niecierpliwie czeka na swoją kolej. Chwilę później okazało się, że moja stara lutownica pistoletowa nadaje się w zasadzie głownie do niczego i nieodzownym było doposażenie warsztatu w kilka innych niespodziewanie niezbędnych narzędzi. A wszystko tylko po to, by po ciężkim dniu pracy pośród gęstych i słodkawych oparów kalafonii oddawać się swej nowej pasji. Ale wystarczy tego barwnego wstępu, reasumując: wydaje mi się, że dojrzałem by podzielić się z Wami swymi nowo nabytymi doświadczeniami. A jak mi wyjdzie, to się jeszcze okaże. No to przejdźmy do rzeczy. Zepsuł mi się zegar. Taki niespecjalnie ładny i niespecjalnie dokładny, więc nie było mi go jakoś szczególnie żal, ale wiadomo: bez zegara - jak bez ręki. Żona wiecznie narzeka, że potyka się o kable z tego mojego nowego hobby i wszędzie po kątach znajduje ukradkiem wypluwane kawałki izolacji, to pomyślałem sobie: hola hola, może w końcu się przyda moja pasja? Wymyśliłem sobie początkowo zegar z dużą matrycą LED, taką przynajmniej 32x16, ale pooglądałem ceny, momentalnie posiwiałem i zdecydowałem się na wersję ULTRA-ECONO. Postanowiłem sklecić matrycę z dwóch kostek TC12-11HWA. Matryca ta posiada 5x7 diód, ale po ustawieniu dwóch matryc poziomo uzyskujemy matryce 14x5. Z szybkich szkiców na kartce w kratkę wynikło, że spokojnie wystarczy do wyświetlenia 4 cyfr w formacie 3x5:

zegar-font

Kiedy przyszły wyczekiwane matryce, spotkała mnie pierwsza niespodzianka. Nigdzie nie znalazłem jak oznaczony jest pin nr 1. Ani w dokumentacji producenta, ani na samej matrycy nie ma żadnego oznaczenia! Oglądałem je chyba z 10 minut z każdej strony. Absolutnie nic. Pomyślałem: ZMIERZĘ! I jak zacząłem mierzyć to okazało się, że wyprowadzenia zrobione są tak sprytnie, że obojętnie jak odwrócimy matrycę w pionie, to 1 zawsze będzie na dole po lewej. Nie wierzycie??? Sprawdźcie sami. Oto moja po partyzancku polutowana matryca:

matryca-front

matryca-rear

Trzyma się to wszystko kupy dzięki sztywnym kablom ze skrętki, które umieściłem poprzecznie (wspólna katoda). Na zdjęciu pewnie niektórzy dociekliwi wypatrzą niechlujnie przyczajoną diodę. Na wyświetlaczu zabrakło miejsca na "mrugający sekundnik", więc postanowiłem dorobić swój :D będzie o nim jeszcze za chwilę. Na razie zapominamy że istnieje. Matryca 14x5 jak łatwo policzyć wymaga 14 linii do sterowania kolumnami (szyna danych), oraz 5 linii do sterowania wierszami (szyna adresowa).

matryca-1

Aż 19 pinów?? Zdawałoby się, że to za dużo jak na nasze małe Arduino. Nic bardziej mylnego! Zwiększymy sobie ilość wyjść używając ośmiobitowych rejestrów przesuwnych 74HC595. W tzw. "internetach" można znaleźć wiele przykładów użycia tego i innych rejestrów wraz z Arduino, więc nie będę się tutaj wielce rozwodził nad zasadą jego działania. W skrócie: działa tak, że podajemy mu bajt (8 bitów) na szynę szeregową (kolejno), a po zamknięciu zatrzasku (latch), układ wystawia nam podany bajt na wyjście równoległe - czyli osiem pinów. Do komunikacji szeregowej z rejestrem potrzebujemy 2 piny + 1 pin(zatrzask), co pozwala nam tylko trzema pinami sterować poziomem 8 wyjść. Oczywiście dzieje się to nieco kosztem szybkości, ale to już osobna historia. Potrzebujemy 19 wyjść, więc użyjemy trzech układów 595. W zestawie startowym z Nettigo, dostałem jeden egzemplarz, który zdążyłem już nieco powyginać, więc musiałem niezwłocznie dokupić. Oczywiście od razu zrobiłem zapas licząc w duchu, że kiedyś poskładam większą matrycę :) Nasze trzy układy 75HC595 łączymy kaskadowo. Co to oznacza? W normalnej sytuacji do sterowania trzema układami 595 potrzebowalibyśmy 3 wyjść szeregowych (6 pinów) i wspólnego zatrzasku. Cóż za niepotrzebne marnotrawstwo pinów! Z pomocą przyjdzie nam tajemnicza nóżka Q7' układu 74HC595. Podłączamy ją do wejścia kolejnego rejestru i otrzymujemy rejestr 16 bitowy sterowany tylko jednym wejściem szeregowym! Jak to działa? Pierwszy bit podany na wejście układu ustawia się na wyjściu o numerze Q0. Po podaniu kolejnego bitu bit z pozycji Q0 przesuwa się na Q1, a nowy wskakuje na Q0. Kolejne bity podawane na wejściu układu wyskakują kolejno na wejście Q0, przesuwając wszystkie na wyższych pozycjach o jeden. Po podaniu 8 bitów mamy zapełnione wszystkie pozycje naszego rejestru, jeżeli jednak nie zamkniemy w tym momencie zatrzasku, tylko podamy kolejne bity to w drobnym uproszczeniu bit z pozycji Q7 "wyskoczy" nam na pozycję Q7'. Stąd możemy go "zabrać" do następnego układu na wyjście Q0. I tam znów będzie kolejno przesuwany, aż do zapełnienia wszystkich rejestrów które połączymy w kaskadę i/lub zamknięcia zatrzasku. Uff... a miałem nie opowiadać jak działa rejestr przesuwny. Wszystkich znudzonych "wyjadaczy" przepraszam. A oto schemat: MATRIX-DRIVER(wykonano w programie FRITZING) Poniżej mój gotowy układ sterownika matrycy:

driver-top

driver-iso

Najmocniej przepraszam za brak sesji z "making of", ale na pomysł wpisu wpadłem już po ukończeniu pierwszej (mam nadzieję) wersji projektu. Znowu wyjadacze moga przeskoczyć do następnego akapitu, ale szybkie wyjaśnienie dla takich nowicjuszy jak ja. Matryca nie świeci cały czas jak monitory LCD. Zapalane są kolejne wiersze lub kolumny matrycy, w bardzo krótkich odstępach czasu. Bezwładność ludzkiego oka powoduje że widzimy stały obraz. Trochę jak w starych kineskopach CRT, ale tylko trochę. I podobnie będzie w naszym wypadku. Tutaj wybrałem zapalanie kolejnych wierszy. I tu kolejna - prócz ceny - zaleta małej matrycy. W każdym cyklu zapalane jest maksymalnie 14 diód. Gdybym miał matrycę o szerokości 32, obawiam się, że prąd który mogę "wyciągnąć" z rejestru, do zasilenia wiersza mógłby nie wystarczyć do zapalenia wszystkich 32 diód. Musiałbym zastosować kluczowane tranzystorem osobne zasilanie poszczególnych wierszy (i ogólnie takie rozwiązanie byłoby zgrabniejsze, wiem). Ale w małej matrycy gdzie mamy 14 diód - nawet przy zasilaniu z Arduino poprzez USB, wyświetlacz jest całkiem czytelny, choć widać różnicę jasności pomiędzy wierszami w których jest zapalonych więcej lub mniej diód. Przy podłączeniu zewnętrznego zasilacza różnice są prawie niezauważalne. Więc nie będziemy się bawić w kluczowanie tranzystorem. Sterowanie matrycą będzie przebiegało w następujący sposób: W każdym cyklu do rejestrów przesyłamy 3 bajty danych, nazwijmy je sobie L P i W z których dwa (L P) ustawiają piny rejestrów podłączonych do kolumn wyświetlaczy, odpowiednio lewego i prawego, a trzeci bajt (W) ustawia wyjścia rejestru w taki sposób, że zwiera do masy (ustawia logiczne zero) odpowiedni pin dla wiersza który właśnie zapalamy (oto i znowu nasza wspólna katoda!). Nasze trzy rejestry ośmiobitowe dają nam 24 wyjścia z 19 potrzebnych. Dla wygody samej konstrukcji płytki wykorzystałem tylko najbardziej znaczące bity rejestru, czyli dla bajtów L i P gdzie potrzebujemy tylko po 7 linii do kolumn odrzuciłem wyjście Q0, a dla rejestru adresowego (5 wierszy) odrzuciłem wyjścia Q0,Q1,Q2. Przykładowy  cykl danych dla napisu "DATA" dla wszystkich pięciu wierszy: [crayon-513fcb7ce8deb/] Widać jak wędruje nam "zero" w rejestrze W :) No to najwyższy czas podłączyć nasze cacko do Arduino. Podłączamy:
  • PIN 2 -> zegar
  • PIN 3 -> szyna danych
  • PIN 4 -> zatrzask
nie zapominając o:
  • +5V -> +5V
  • GND -> GND
i próbujemy załadować poniższy kod: [crayon-513fcb7ce8e62/] TADAM!!!

DATA

 I znowu kilka słów wyjaśnienia: Może nie wszyscy wiedzą, do czego służy polecenie:

shiftOut(pin_danych,pin_zegara,kolejność_bitów,bajt).

Wysyła ono szeregowo bajt, na pinach podanych jako pierwsze dwa argumenty. Czyli dokładnie to czego potrzebujemy aby "naładować" nasze rejestry. Parametr trzeci może mieć dwie wartości: MSBFIRST lub LSBFIRST. Oznaczają one odpowiednio: najbardziej znaczący bit pierwszy lub najmniej znaczący bit pierwszy. Czyli upraszczając: czy podajemy bity kolejno od lewej czy prawej strony. Jak już jesteśmy przy kolejności, to pewnie zauważyliście, że kolejne bajty podaję w odwrotnym szyku niż wcześniej podałem. Szynę danych podłączyłem do rejestru L (lewy wyświetlacz). Potem kaskadowo R i W. Czyli aby po podaniu trzech bajtów w rejestrze W mieć adres wiersza, musimy podać go jako pierwszy, a zawartość lewego wyświetlacza jako ostatni bajt. I tutaj triumfalnie powraca  nasza doklejona dioda! Podłączyłem jej anodę (przez rezystor 220R) do niewykorzystanego wyjścia Q0 rejestru pierwszego (L - lewy wyświetlacz) a katodę do wiersza 4 w rejestrze trzecim (W - wiersz). MATRIX-DRIVER-WITH-LED(wykonano w programie FRITZING) Teraz zamieniając tylko jedną liczbę w naszym kodzie (202 na 203 w ostatnim wysyłanym bajcie) zapalamy sekundnik! Proste i bez wykorzystania dodatkowych pinów z Arduino.

data_on

Oczywiście wyświetlanie i odświeżanie w ten sposób zawartości wyświetlacza byłoby niezwykle uciążliwe, więc zanim zabrałem się za dalsze lutowanie, postanowiłem przygotować dedykowaną bibliotekę, która do minimum uprości sterowanie wyświetlaczem. Wykorzystamy do tego przerwania zegarowe i sprzętowy interfejs SPI Arduino. Ale to ze względu na późną porę, będzie tematem kolejnego wpisu. Oczywiście o ile ten pierwszy spotka się z Waszą aprobatą. Na zachętę wrzucam dwa malutkie zdjęcia gotowego prototypu. ready1ready2 Pozdrawiam i zapraszam do komentowania.    Czytaj dalej...

Zabawka edukacyjna V2

$
0
0
Zabawka_edukacyjna
Witam wszystkich. Zabawę z Arduino jak i ogólnie z elektroniką zacząłem nie tak dawno. Począwszy od startowego projektu BLINK z LED, poprzez mini sterownik sygnalizacji świetlnych (drogowych), zainspirowany projektem Łukasza http://majsterkowo.pl/swiateczna-zabawka-edukacyjna-dla-dzieci/ postanowiłem spróbować własnych sił w rozbudowie tego projektu. http://youtu.be/4ZbRsT8hURM

Miałem problemy z dźwiękiem, dlatego go z filmu wyeliminowałem, ale proszę mi wierzyć - urządzenie piszczy jak powinno :)

Instrukcja obsługi gry: Czas gry jest stały i wynosi 60 sekund. Na starcie gracz otrzymuje 100 punktów. Każdy błąd pozbywa gracza 25 punktów, natomiast każda zaoszczędzona sekunda daje +4 punkty (czyli im szybciej ukończy się grę tym lepiej), więc lepiej jest stracić trochę czasu i przejść grę dokładnie, niż zrobić to szybko i z błędami. Aby rozpocząć należy wcisnąć przycisk START, po którym należy poczekać od 2 do 8 sekund na rozpoczęcia gry. Błędy można "wykasować" wracając na start (czas niestety leci...) :) 60 sekundowa bezczynność gry oznajmia się pikaniem (alarm) Dodatkowo wykorzystałem w ramach nauki EEPROM, który liczy kompletne zagrania (od startu, aż po metę). Tyle z instrukcji obsługi. Strona techniczna:zabawka_fritzing Proszę zwrócić uwagę, że na płytce stykowej użyte przyciski wyłącznie do testowania, które w fazie końcowej zostały zamienione spinaczami, drutem i manipulatorem. Na początku próbowałem podświetlić LCD, prąd mikroprocesora nie dawał oczywiście rady więc umieściłem tranzystor do sterowania prądem wprost z baterii, jednak nawet same baterie na wiele czasu by nie wystarczały i dałem sobie spokój. Wykorzystane części: 1x Atmega8A-Pu 2x 220 Ohm 4x 10K Ohm 2x 47 uF kondensator elektrolityczny 2x 100 nf kondensator ceramiczny 1x LM7805 stabilizator 1x LED 1x Buzzer 1x Wyświetlacz 16x2 1x Przełącznik 1x microswitch Wszystko zasilane 4x LR6 1,5V  DSC_7717 DSC_7712  Kod programu: Program jest może i trochę chaotycznie napisany, pewnie można go w dobry sposób zoptymalizować, ale co najważniejsze, że działa :) [crayon-51423628ca5b6/]   Żona na początku śmiała się dość ironicznie z mojego pomysłu.. teraz walczy wraz ze mną o każdą sekundę ;) Czytaj dalej...

Zegar cyfrowy LED z bajerami – Część II: sterowanie matrycą przez SPI

$
0
0
jupi
Uradowany faktem, że mój wpis wskoczył na główną czuję się zobowiązany zakasać rękawy i zabrać się do pisania biblioteki sterownika. W zasadzie mam ją już gotową i działającą, ale przecież nic nie stoi na przeszkodzie żebym napisał go jeszcze raz od nowa, może poprawimy to i owo? W pierwszej części, sterowaliśmy wyświetlaczem dosyć topornie:
  1. Zwalniamy zatrzask
  2. Wysyłamy na wejście rejestru kolejno 3 bajty (adres wiersza jako pierwszy bajt)
  3. Zamykamy zatrzask
  4. Czekamy milisekundę
  5. Zwalniamy zatrzask
  6. Wysyłamy na wejście rejestru kolejno 3 bajty (adres kolejnego wiersza jako pierwszy bajt)
  7. Zamykamy zatrzask
  8. Czekamy milisekundę
  9. ...
I tak w sumie pięć razy aż zapalimy kolejno wszystkie wiersze, po czym powtarzamy całą procedurę od początku. Na początek pozbędziemy się uciążliwego podawania adresu wiersza. Przypomnę jak wyglądają kolejne adresy wierszy które musimy wysłać do rejestru W: [crayon-51423628bf163/] Jest pewnie kilka,  jak nie kilkanaście sposobów, jak zamienić (zakodować) numer wiersza na wartość którą musimy wpisać do rejestru.  Podam dwa najbardziej dla mnie oczywiste. Pierwszy to tablica wartości: [crayon-51423628bf1e8/] Sposób szybki to i prosty zarazem :) Myślę, że kod nie wymaga żadnych wyjaśnień. Może prócz tego, że wiersze numeruję sobie od 0, a nie od 1. Radzę się do tego przyzwyczaić :) Tak adresowane są tablice w C i wielu innych językach. Pierwszym elementem jest zawsze zero i już. Trzeba przywyknąć - z czasem wchodzi w krew. Dobra, jest super, w kolejnych iteracjach pętli wyciągamy sobie adresy z tablicy. Jest jednak pewne "ale". W przypadku dużych matryc duża tablica przechowująca adresy powoduje nam zajęcie drogocennego - w naszym małym Arduino - RAMu. A mamy go tylko 2KB! Opcją jest przechowywanie tablicy w pamięci programu stosując dyrektywę PROGMEM, ale kod stałby się wtedy nieco mniej czytelny. Do tego też jeszcze dojdziemy jak będziemy robić tablicę czcionek, ale... dobra do celu. Chodzi o to, że możemy sobie sami zgrabnie wyliczyć wartość bajtu, nie korzystając z żadnych tablic. Zróbmy sobie wędrujące zero. Najpierw pokażę jak, a potem wytłumaczę: [crayon-51423628bf24b/] Na początek przyglądnijmy się tylko temu, co mamy w nawiasie po prawej stronie: [crayon-51423628bf2a5/] Symbol << to operator binarny "przesuń w lewo". Analogicznie istnieje też "prawy" operator >>. Powoduje on (jak sama nazwa wskazuje) przesunięcie bajtu (z lewej strony operatora) o zadaną ilość bitów (z prawej strony operatora) w zadanym kierunku. W naszym przypadku w lewo,  czyli: [crayon-51423628bf2fe/] Jak widać na powyższych przykładach, wskakujące z prawej strony brakujące bity uzupełniane są zerami. Dobra, wiemy już jak zrobić wędrującą jedynkę.  To teraz zanegujemy nasz bajt operatorem ~. Tylda to operator binarny NOT. Powoduje zmianę stanu wszystkich bitów na przeciwny: [crayon-51423628bf357/] Mamy wędrujące zero. Już prawie jesteśmy gotowi. Teraz zapakujemy sobie jeszcze kolejne bajty wyświetlacza do tablic i mamy gotowy kod. [crayon-51423628bf3b4/] Prawda, że dużo ładniej??

jupi

Dla swojej, a teraz również Waszej wygody, napisałem na szybko KALKULATOR, do sprytnego wyliczania wartości tablic. Może i ładniej wygląda nasz kod, ale nadal wykonujemy wszystkie operacje odświeżania matrycy w głównej pętli loop(), gdzie docelowo chcielibyśmy wykonywać inne super fajne rzeczy. Przykładowo podmieniać obrazki, albo w ostateczności mierzyć czas. Zatem na dobry początek przeniesiemy sobie całą pętlę odświeżania do zewnętrznej funkcji, oraz zadeklarujemy zmienną, która będzie naszą "pamięcią obrazu". Wszystkie zapisane do tej zmiennej dane, będą automatycznie wyświetlane na matrycy. Trzask prask... [crayon-51423628bf413/] No i zaczyna to powoli wyglądać. Może kilka słów wyjaśnienia do powyższego kodu.  Pierwsza rzecz, to funkcja memcpy. Kopiuje ona zawartość obszaru pamięci.

memcpy(wskaźnik celu, wskaźnik źródła, ilość bajtów do skopiowania);

I tu pojawia się tajemnicze słowo wskaźnik. Coś mi mówi, że gdybym zaczął tłumaczyć czym są wskaźniki i jak można ich sprytnie używać, to skończyłbym pisać ten post w okolicach świąt wielkiej nocy, więc postaram się ograniczyć do niezbędnego minimum. Wskaźnik to pojedyncza liczba, wskazująca położenie zmiennej/funkcji/struktury w fizycznej pamięci naszego Arduino. Pewnie zauważyliście przy pierwszym parametrze funkcji memcpy jakim jest m_Ekran pojawił się znaczek & (ampersand). Wskazuje on kompilatorowi, że pierwszym parametrem nie jest zawartość naszej struktury danych, tylko jej fizyczny adres (wskaźnik). Dlaczego zatem przy drugim parametrze nie wskazujemy kompilatorowi że nie chcemy zawartości tablicy tylko jej adres? No właśnie... dlatego, że zmienna t została przekazana jako parametr funkcji, a że jest tablicą, to została przekazana do funkcji właśnie jako wskaźnik. Więcej mogę wytłumaczyć w komentarzach, albo zapytajcie szwagra. W ostateczności googla. To naprawdę temat rzeka, a jest już dobrze po północy. Dla naszego przykładu wystarczy, że uwierzycie mi na słowo, że tablica przekazana do funkcji jest od razu wskaźnikiem i nie potrzebuje ampersanda. Trzeci parametr już chyba nie wymaga komentarza. Drugą rzeczą która dla początkujących może wydać się niezrozumiała, może być definicja struktury na początku pliku. Skleciłem ją sobie dla wygody. Pozwala mi to odwoływać się do zmiennej m_Ekran.dane[wiersz][bajt], adresując sobie zgrabnie wiersze i bajty. Kolejna rzecz - dlaczego w funkcji rysującej wiersz nie zrobiłem pętli do wyświetlania kolejnych bajtów, tylko podałem je "wprost"? Nie z wrodzonego lenistwa. Funkcja ta ma być wywoływana jak najczęściej, aby uniknąć efektu widocznego migotania matrycy. Prosty eksperyment. Zmieniam wartość opóźnienia cyklu w ostatnim wierszu na 5 milisekund. Niby niewiele. Ale migotanie matrycy już widać i jest nawet całkiem nieznośne. Czyli jeżeli ładowanie rejestrów będzie trwało dłużej niż 5ms, to już lipa. A wyobraźmy sobie matrycę RGB gdzie dla każdego pojedynczego bitu matrycy podajemy 3 bajty danych? Dlatego właśnie nie użyłem pętli. W wypadku podania dwóch bajtów dużo szybciej będzie podać je wprost. Ale co w przypadku gdy nasza matryca byłaby faktycznie większa? Jak jeszcze możemy przyspieszyć nasz kod? W Arduino jak i w wielu innych kontrolerach mamy interfejs SPI. To nic innego jak bardzo szybkie wyjście szeregowe, które może nam zastąpić dosyć powolną funkcję shiftOut(). W Arduino UNO jakie aktualnie katuję, działa ono domyślnie na pinach 10,11,12,13. Znających język Szekspira odsyłam pod adres: http://arduino.cc/en/Reference/SPI My wykorzystamy tylko dwa piny, a to dlatego, że będziemy "gadać" tylko w jedną stronę i tylko z jednym urządzeniem. Przypinamy Arduno do matrycy w następujący sposób: 13 - do wejście zegarowego rejestru 11 - do wejścia danych rejestru Zatrzask śmiało może pozostać na razie na  pinie 4. Do obsługi interfejsu SPI wykorzystamy dołączoną do Arduino IDE bibliotekę SPI.h [crayon-51423628bf474/] Jak widać, kod zmienił się minimalnie, ale wierzcie mi na słowo - różnica ogromna. Można to w prosty sposób sprawdzić, wysyłając w funkcji  m_RysujWiersz kilkanaście pustych bajtów zanim wyślemy nasze trzy istotne. Przy kilkudziesięciu "sztucznych  kolumnach" funkcja shiftOut zaczyna powodować widoczne migotanie matrycy, a SPI radzi sobie absolutnie bez mrugnięcia ;) Na dzisiaj to tyle. Jutro może uda mi się napisać, jak całkowicie przenieść funkcję rysującą wiersz poza pętlę loop, aby wykonywała się samoczynnie w tle w zadanym odstępie czasu. Tyle na dziś i zapraszam do komentowania. Nikt nie ma żadnych pytań? Nie wierzę. Czytaj dalej...

Zegar cyfrowy LED z bajerami – Część III: Przerwa na przerwania zegarowe.

$
0
0
tabela-CS
Oczywiście nie obędzie się bez przydługiego wstępu. Na początek muszę się pożalić, że przez pisanie tego mini-poradnika od czterech dni nie przylutowałem ani jednego kabelka. Mam objawy odstawienne i początki nerwicy. Obiecałem sobie, że w weekend w ramach rekonwalescencji poskładam przetwornik cyfrowo-analogowy i zmuszę Arduino do jodłowania. Dzisiaj będzie niestety dużo gadania, matematyki  i mało kodu. Dla mniej odpornych na teorię nie przewiduję specjalnych atrakcji. Nawet pół zdjęcia. Dobra. Wracamy do naszego zegara. Jak wspomniałem pod koniec części drugiej, przyszedł czas na to, aby zupełnie pozbyć się odświeżania ekranu z główniej funkcji loop(). Między innymi po to, abyśmy swobodnie mogli wykorzystywać funkcję delay. W chwili obecnej, każde wywołanie delay() w pętli głównej, powoduje zatrzymanie wyświetlania na pojedynczej linii. Dlatego, do opóźnień pomiędzy wyświetleniami obrazków w poprzednim przykładzie, musieliśmy używać dodatkowego licznika cykli. Pamiętacie? Zobaczcie jak pięknie i czytelnie mogłaby wtedy wyglądać funkcja loop(): [crayon-5143d4c1e399f/] Coś pięknego! Ale jak wywalić odświeżanie z funkcji loop? Wyjadacze cicho stękną "w przerwaniu". Tak, właśnie w przerwaniu. I tutaj jak zwykle kilka słów wyjaśnienia czym owe przerwania są. Przerwanie to takie ZDARZENIE, które powoduje, że przerywane jest wykonywanie głównego kodu i wykonywany jest kod obsługi przerwania. Coś podobnego jak wywołanie funkcji, z taką różnicą, że nie wywołujemy jej jawnie w kodzie, tylko wywoływana jest automatycznie, zwykle zainicjowana przez jakieś urządzenie zewnętrzne lub sam procesor. Przerwania bardzo często wykorzystywane są do obsługi zdarzeń sprzętowych. Przykładowo:
  • działanie programu jest przerywane, bo jakieś urządzenie zgłasza gotowość do transmisji danych.
  • Procesor "zapamiętuje" sobie co i gdzie właśnie robił,
  • przechodzi do obsługi urządzenia, odbiera/wysyła dane,
  • powraca do wykonywania głównego kodu.
I podobnie będzie w naszym przypadku, z tą różnicą, że przerwanie nie będzie wywoływane sprzętowo przez urządzenie zewnętrzne, a przez wewnętrzny zegar naszego mikroprocesora. Układ Atmega 328P, który jest sercem mojego Arduino Uno posiada trzy timery nazywające się odpowiednio timer0, timer1 i jak nietrudno się domyślić - timer2. Możemy je wykorzystać do generowania cyklicznych przerwań. Ostrzegam wszystkich bojących się wyzwań - będzie trochę czarnej magii. Sam dopiero skubnąłem wiedzy o przerwaniach w Arduino, więc liczę na wyrozumiałość i jeżeli walnę jakąś bzdurę ;) Nieocenionym źródłem wiedzy była dla mnie strona: http://www.instructables.com/id/Arduino-Timer-Interrupts/. Niestety znów po angielsku. Timery działają bardzo podobnie do zmiennej cykl z naszego ostatniego przykładu. Każdy z nich jest automatycznie inkrementowany w  cyklu zegara Arduino(16MHz), czyli 16 milionów razy na sekundę. Sporo, co? Ponadto, każdy z trzech zegarów posiada rejestr "porównawczy". W każdym cyklu po zainkrementowaniu wartości timera, jest ona porównywana z rejestrem i jeżeli osiągnęliśmy oczekiwaną wartość, to licznik timera jest zerowany i wywoływane jest przerwanie zegarowe. Bingo! Czyli naszym zdarzeniem wywołującym przerwanie (rysowanie wiersza matrycy), będzie osiągnięcie przez jeden ze sprzętowych timerów zadanej wartości. Oczywiście jak zwykle, jest drobne "ale". Timer0 i timer2 są ośmiobitowe, więc liczą od 0 do 255 i się "przepełniają" , czyli wskakują na 0 i liczą od nowa. Timer1 ma szesnaście bitów, więc przepełnia się niby dopiero po osiągnięciu wartości 65535. Jeden cykl zegara (przy 16MHz)  trwa około 63ns, co daje nam maksymalny interwał rzędu 16μs dla timerów 0 i 2 oraz tylko około 4ms dla timera 1. Raczej słabo... generalnie do potrzeb matrycy LED, gdzie potrzebujemy właśnie odstępu rzędu milisekundy jest okej, ale gdybyśmy chcieli wywoływać przerwanie raz na sekundę? Timerów nie można w żaden sposób sumować, ani łączyć. Każdy z timerów wyposażony jest także w preskaler (prescaler). Nie wiem jak to zgrabnie nazwać po polsku... podzielnik? Może "wyjadacze" podpowiedzą w komentarzach :) Ów preskaler/podzielnik, pozwala modyfikować nam bazową częstotliwość taktowania zegara procesora dla danego timera według wzoru:

częstotliwość timera = częstotliwość procesora (16MHz) / podzielnik;

 Czyli dla podzielnika 8, nasz timer będzie tykał już tylko 2 miliony razy na sekundę. Coraz lepiej. Tylko jak duży może być nasz podzielnik? Duży. Maksymalnie 1024, jednak jest kolejne "ale". Podzielnik może przyjmować tylko określone wartości, a dokładnie: 1, 8, 64, 256, oraz właśnie 1024. No to liczymy:

16000000 Hz/1024 = 15625Hz

 Tyle razy na sekundę inkrementowany jest timer z podzielnikiem 1024.

1/15625Hz = 0,000064 sekundy = 64 us

Tyle trwa wtedy jeden cykl timera. Zatem dla timera 1:

0,000064 s * 65535 = 4,19424 sekundy

 To maksymalny odstęp z jakim możemy wywoływać przerwania zegarowe. A dokładniej jedno bo 16 bitów ma timer1. Dla timera 0 i 2 maks to:

64ms*255=16ms

To tyle przydługiej teorii, teraz przejdźmy do praktyki. Dane wejściowe jakich potrzebujemy dla zainicjowania naszego przerwania zegarowego to wartość podzielnika i rejestru porównawczego. Do wyliczenia tych wartości musimy wiedzieć, jaką dokładnie częstotliwość wywołania przerwania potrzebujemy dla naszej matrycy. Zakładam, że ma być to wersja Pro De-Luxe, więc nie zadowolimy się marnymi 50Hz , przy których podobno oko ludzkie już nie widzi migotania. Niech będzie szalone 100Hz! Jeżeli chcemy, aby cała matryca odświeżała się 100 razy na sekundę, to pamiętając że jeden cykl "odpala" tylko jeden wiersz, musimy pomnożyć sobie nasze 100Hz * 5. Czyli funkcję rysującą wiersz musimy wywoływać 500 razy na sekundę. Czyli (1/500 = 0.002)  co 2 milisekundy. Skoro maks dla timerów 8 bitowych to 16ms, to dla naszego sterownika spokojnie wystarczy timer 8 bit. Nie potrzebujemy super precyzji - jak będzie 99Hz to oko nam nie pęknie. Okej, znów chwila matematyki: użyjemy zmyślnego wzoru, do wyliczenia wartości rejestru porównawczego:

OCRxA  = ( bazowa częstotliwość procesora / ( dzielnik * oczekiwana częstotliwość) ) - 1

OCRxA to właśnie nazwa rejestru porównawczego, gdzie w miejsce x wstawiamy numer odpowiedniego timera. Chcemy 500Hz , to liczymy:

(16000000/(1*500))-1 = 31999

Na 8 bitów (0-255) to sporo za dużo, więc śmiało podkręcamy dzielnik ;)

 (16000000/(64*500))-1 = 499

O! już blisko, następna dozwolona wartość dzielnika to 256.

(16000000/(256*500))-1 =124

Bingo! Z podzielnikiem 256 mieścimy się w 8 bitach. Wartość podzielnika ustalamy, zapalając odpowiednie bity rejestru kontrolnego B danego timera. Brzmi na skomplikowane, a wcale nie jest. Każdy timer ma dwa rejestry kontrolne (A i B). Nazywają się odpowiednio: TCCRxA i TCCRxB, gdzie w miejsce x wstawiamy numer timera. Rejestry te służące do wyboru trybu pracy, oraz wartości podzielnika. 3 z bitów rejestru B nazywają się CS10, CS11 i CS12 i właśnie one służą do ustawiania wartości podziału, według następującej tabeli: tabela-CS Tylko jak ustawić konkretny bit rejestru? TCCRxB |= (1 << CS12);  //dzielnik 245 Właśnie tak. Jeżeli chcemy ustawić kilka, to możemy to zrobić kolejno, lub jednym poleceniem: TCCRxB |= (1 << CS12) | (1 << CS10);  //dzielnik 1024 dla wszystkich spragnionych głębszej wiedzy na temat rejestrów timerów odsyłam do: https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328 Do użycia timerów nie potrzebujemy żadnych dodatkowych bibliotek. Niestety, dla początkujących kod może wyglądać na nieco zagmatwany. Nie przejmujcie się, ważne żebyście zrozumieli jak ustawiać dzielnik i rejestr porównawczy. Timery ustawiamy (inicjujemy) w funkcji setup(). [crayon-5143d4c1e3a43/] Analogicznie ustawiamy pozostałe timery. Wystarczy zamienić wszystkie dwójki w nazwach rejestrów na odpowiedni numer timera. A! I jeszcze jedno. Tryb obsługi przerwań (CTC) ustawiany jest innymi bitami dla każdego timera. Dla timera 0 linia 16 powinna wyglądać: [crayon-5143d4c1e3ac5/] dla timera 1: [crayon-5143d4c1e3b69/] Brakuje nam jeszcze tylko jednej rzeczy. Musimy wiedzieć jak wskazać timerowi kod, który ma wykonać w momencie wystąpienia przerwania. [crayon-5143d4c1e3c08/] Tyle. Nic prostszego. Analogicznie dla pozostałych timerów. Wystarczy zamienić dwójkę na numer timera. No to mamy wszystko. Kod odświeżający matryce 100 razy na sekundę w przerwaniu zegarowym  wygląda tak: [crayon-5143d4c1e3c83/] Uniezależniliśmy się od wywołań w funkcji loop!!! Jeszcze tylko jedna uwaga na koniec dla tych, którzy chcą używać przerwań zegarowych w swoich projektach. Starajcie się, aby kod obsługi przerwania wykonywał się jak najszybciej. Nie pakujcie tam skomplikowanych obliczeń czy operacji na dużych ilościach danych. Może to powodować znaczne spowolnienie działania głównego programu, w skrajnym przypadku gdy obsługa będzie dłuższa niż interwał między wywołaniami przerwanie zacznie przerywać działanie samego siebie, dojdzie do przepełnienia stosu i program się pięknie wykrzaczy. Ale to już chyba wszystko na dziś. Jeszcze tylko w następnej części poradnika dorobimy wyświetlanie cyferek i zaraz potem będziemy budować moduł zegara czasu rzeczywistego. Tak niewiele nam zostało aby znów zacząć lutować :) Radujmy się! Czytaj dalej...

Zegar cyfrowy LED z bajerami – Część IV: Tablica czcionek i pomiar czasu.

$
0
0
concept
No to zaczynamy kolejny odcinek cyklu “nie dla lalusiów”, gdzie spróbujemy dodać kilka nowych funkcji do naszej biblioteki, i pobawić się już trochę w wyświetlanie i pomiar czasu. Przypominam tym, którzy przez ostatnie dwa odcinki zapomnieli, że końcowym efektem ma być zegar :) Wybaczcie, że tak się szeroko rozpisałem o programowaniu, a mało o majsterkowaniu, ale pomyślałem sobie, że wielu z Was – podobnie jak ja – zagląda tutaj też po to, żeby się czegoś nauczyć, a nie tylko pooglądać zdjęcia fajnych gadżetów. A w programowaniu na razie czuję się mocniej, niż w majsterkowaniu, więc dzielę się tym co potrafię najlepiej. Może komuś przyda się to w jego projekcie i to nie tylko zegara ;) Śmiało, podsumujmy co już potrafi nasza biblioteka:
  • Wyświetla obraz automatycznie z zadanego obszaru pamięci (elegancko w przerwaniu i baaaardzo szybko, bo przez SPI)
  • mamy funkcję która ładuje nam obrazek z tablicy do pamięci obrazu
  • mamy funkcję która robi nam negatyw obrazka
To trochę mało, przecież chcielibyśmy wyświetlać 4 różne cyfry: rysunek matrycy No i oczywiście kropeczka nad matrycą ma mrugać co sekunde ;) Cała reprezentacja zawartości naszej matrycy w pamięci to 5 bajtów na lewy(L) wyświetlacz oraz 5 bajtów na prawy(P), zapisane kolejno:

1L , 1P , 2L ,2P , 3L , 3P , 4L , 4P , 5L ,5P

  I jak być może pamiętacie, w ten właśnie sposób do tej pory zapisywaliśmy wyświetlane obrazki zajmujące całą matrycę: [crayon-51479b3d62444/] Do wyświetlania 4 różnych cyfr raczej słabo się to sprawdzi. Na szybko policzyłem mi, że gdyby zapisać w ten sposób wszystkie możliwe pozycje dla zegara 24 godzinnego mielibyśmy 24*60 = 1440 obrazków do zaprojektowania :)  W dodatku zajęlibyśmy 14KB pamięci. Na taką rozrzutność to sobie nie możemy pozwolić :) Musimy to zrobić inaczej. Zdefiniujemy sobie  każdą cyfrę z osobna i będziemy ją wrzucać do odpowiednich kolumn matrycy, przecież już wiemy jak przesuwać bity w lewo i w prawo. Mam także śliczny odręczny projekt cyferek mojego autorstwa: szkic czcionek No to kodujemy je pojedynczo jako 3 najmłodsze bity i pakujemy w strukturę tablicy: [crayon-51479b3d624a3/] No i mamy tablicę znaków. A dla niezorientowanych w zawiłych meandrach zapisu binarnego, wrzucam obrazek poglądowy z konwersji cyfry 9. Ponoć czasem jeden obraz więcej wart więcej niż tysiąc słów: konwersja dla opornych Przypomniał mi się taki "cyfrowy" suchar z brodą:

Ile jest typów ludzi? 10 - tacy którzy rozumieją zapis binarny i tacy którzy nie.

Jeżeli nie zrozumiałeś żartu, to należysz do tych drugich :D Cyfry w tablicy znaki[] celowo ułożyłem zaczynając od zera, bo przecież wszyscy pamiętamy, że tablice w języku C indeksowane są od zera, prawda? I korzystając właśnie z tej cechy, indeks pozycji naszej tablicy jednocześnie wskazuje nam jaka cyfra jest zawarta w tej pozycji. No i cudownie. Ale zanim przejdziemy dalej, znów pojawi się chwila nudnej teorii, która jednak może się Wam kiedyś przydać. Mnie już dopadł problem o którym za chwilę opowiem, a i to w zasadzie w pierwszym poważnym projekcie na Arduino. A chodzi dokładnie o pamięć RAM, której niestety mamy w procesorze atmega328P tylko 2KB. A w atmega168, na którym właśnie robiłem ów pechowy projekt, mamy jej tylko 1KB. Ale może od początku. Zrobiłem prototyp kontrolera MIDI na Arduno Uno(Atmega328P). Wszystko pięknie działało na płytce stykowej, dumny jak paw postanowiłem z prototypu przejść w fazę upojnego lutowania i przeniesienia oprogramowania właśnie na kość atmega168. Po kompilacji kod wynikowy miał niecałe 10Kb, więc nie spodziewałem się żadnych problemów, w końcu 168 ma jak sama nazwa wskazuje 16KB pamięci programu. A tu lipa panie. Soft kontrolera po przeniesieniu na inny procek działa kilka, czasem kilkanaście sekund i przestaje reagować. Uparcie. I to w przeróżnych odstępach czasu. Bez sensu. Pierwsze podejrzenie zimny lut. Przemierzyłem wszystko wszerz i wzdłuż, wymieniałem kości na inne przy czym, w przypływach rozpaczy coraz częściej czyniłem swój język prawdziwie plugawym, złorzecząc haniebnie na swe próżne starania. Ciągle nic. Ciągle ruletka. Raz działa nawet prawie minute. A potem znów ledwo rusza. W końcu zacząłem wywalać fragmenty kodu i też bez zmian. Wczytałem szkic w wersji z przedwczoraj - HULA! Porównuję co się zmieniło, a tutaj niespodzianka. Praktycznie nic. Zmieniłem tylko w definicjach na początku kodu ilość programowalnych presetów z 2 na 4. Wynikiem tego,  i tak już dosyć spora struktura danych przechowująca ustawienia dla presetów całego urządzenia, puchła na tyle, że gdy tylko procesor próbował zrobić cokolwiek mądrzejszego, to 1KB ramu w Atmega168 przestawało wystarczać. I pac. Totalna zwiecha. Myślałem, że będę musiał zrezygnować z presetów całkiem, ale poszukałem po internetach i okazało się, że jednak jest jeszcze nadzieja. Domyślnie wartości wszystkich zmiennych globalnych, które zadeklarujemy w naszym projekcie, przechowywane są właśnie w pamięci RAM, a ta ze względu na rozmiar kiepsko nadaje się do przechowywania większych struktur. A szczególnie jeżeli nasz procesor ma jeszcze dodatkowo robić cokolwiek innego. Ale mamy jeszcze pamięć flash przeznaczoną na kod programu, której zwykle nie wykorzystujemy do końca. A przecież jest jej kilka a nawet kilkanaście razy więcej niż RAMu! Aby przechowywać zawartość zmiennej w pamięci programu, musimy zadeklarować ją, używając dyrektywy PROGMEM. [crayon-51479b3d62507/] lub alternatywnie: [crayon-51479b3d62562/] Oczywiście jak zwykle, są pewne obostrzenia i limity.
  • w pamięci PROGMEM możemy przechowywać tylko określone typy danych o specjalnych nazwach (o tym za moment)
  • nie możemy w pamięci PROGMEM przechowywać liczb zmiennoprzecinkowych (pośrednio się da, ale to osobna bajka)
  • do dostępu do danych w pamięci PROGMEM musimy używać specjalnych funkcji (mamy je w bibliotece  "avr/pgmspace.h")
Niewielki to koszt, za dodatkowe kilka KB pamięci na nasze drogocenne dane. A te "specjalne" typy danych, to w zasadzie nic innego jak 3 podstawowe typy liczb całkowitych znane nam już z C, tylko trochę inaczej się nazywają: [crayon-51479b3d625c8/] Spokojnie wystarcza. A o tych specjalnych funkcjach dostępu można więcej poczytać na stronie: http://www.arduino.cc/en/Reference/PROGMEM. Nic specjalnie trudnego ;) zaraz zobaczycie na przykładzie. Postaram się tak skomentować kod, aby wszystko było jasne ;). To wrzućmy nasze struktury danych do pamięci programu, dodajmy kilka pożytecznych funkcji, które zbliżą nasz projekt o krok od bycia pełnoprawnym zegarem i zobaczmy co z tego wyjdzie: [crayon-51479b3d62628/] I co nam z tego wyszło? (ktoś narzekał na brak filmów) http://www.youtube.com/watch?v=P_ozDs54-FA Jak widać w kodzie pozwoliłem sobie umieścić czcionki i napis test w pamięci PROGMEM. A sam odczyt niewiele się zmienił. Używamy niemal tej samej funkcji memcpy, tyle, że z przyrostkiem  _P, oznaczającym że odczytujemy dane z pamięci programu. Jedyne co się zmienia, to to, że drugi parametr, czyli źródło musi wskazywać na zmienną w pamięci PROGMEM. Reszta bez zmian. Jeszcze jedna rzecz wymaga chyba wyjaśnienia początkującym: [crayon-51479b3d62689/] taka konstrukcja nazywana jest "operatorem warunkowym":

zmienna = warunek_0 ? wyrażenie_1 : wyrażenie_2;

i działa tak: Jeżeli spełniony jest warunek_0, zmiennej przypisywana jest wartość wyrażenia_1, jeżeli nie jest - wyrażenie 2. Czyli zapis: [crayon-51479b3d626e2/] jest równoważny zapisowi: [crayon-51479b3d6273a/] ale zajmuje dużo mniej miejsca. W naszym przypadku wyrażenie jest spełnione gdy reszta z dzielenia  (% - operator modulo) przez dwa wynosi 1, czyli wartość w nawiasie jest prawdą dla liczb nieparzystych, a fałszem dla parzystych. Trochę to może dziwne na pierwszy rzut oka, ale działa to dlatego, że w języku C wartość zero interpretowana jest jako fałsz, każda inna jako prawda. Czyli zapis (pozycja % 2) jest tożsamy z  ((pozycja % 2)== 0). Jak jeszcze coś jest niejasne - śmiało pytajcie w komentarzach. Teraz jeszcze chwila zabawy z wyświetlaniem cyferek. Podmieniamy funkcję loop() i zrobimy pseudo-stoperek liczący do 100 sekund z dokładnością setnej części sekundy: [crayon-51479b3d62793/] No i efekt: http://www.youtube.com/watch?v=Q1Rs33M3ofw Teoretycznie dwie lewe cyfry wyświetlacza powinny wskazywać ilość upływających sekund. Ale niestety - tylko teoretycznie. Porównując pomiar z "lepszym" stoperem, już w przeciągu dwóch minut nasz stoper spóźnia się około sekundy. Specjalnie nagrałem całe 100 sekund, żeby niedowiarki sami mogli sobie zmierzyć. A dlaczego tak?Pomiędzy wywołaniami funkcji delay(10) wykonujemy także operacje wyświetlania znaków. Dodatkowo każdy delay(10) mający podobno trwać 10 milisekund jest przynajmniej 5 razy przerywany naszym ukrytym przerwaniem zegarowym (przypominam - 500Hz), w którym odświeżana jest zawartość matrycy. Niby przez SPI - więc migiem, ale też nie dzieje się to zupełnie natychmiast.  Zatem - już wiadomo: używając funkcji delay() dokładnego zegara nie zrobimy. Pewnie gdyby zmniejszyć częstotliwość odświeżania i aktualizować cyfry tylko raz na sekundę, precyzja by nieco wzrosła, ale góra do poziomu wystarczającego na zgrubny pomiar w krótkich odstępach czasu. Minutnik do jajek dałoby radę :) Ale przecież nie o to nam chodzi. Inną (kiepską) alternatywą może być użycie funkcji millis(), która zwraca nam (nawet dosyć dokładnie) ilość milisekund która upłynęła od startu naszego programu. Spróbujmy: [crayon-51479b3d627ed/] Nie będę już wrzucał kolejnego, prawie identycznego filmiku, ale jest teraz duuuużo dokładniej. Prawie idealnie. Więc dlaczego napisałem, że jest to kiepska metoda do zrobienia zegara? Po pierwsze dlatego, że wartość zwracana przez funkcję millis() po jakimś czasie (kilku dniach, się nam przepełni) i wyzeruje. Można też próbować nie zliczać milisekund tylko same sekundy i na upartego wydłużylibyśmy ten czas do paru tygodni. Niby tak. Ale co jak odłączymy zasilanie? Albo wciśniemy reset? Za każdym razem musielibyśmy ustawiać na nowo bieżący czas. Kompletnie bez sensu. I tutaj czas na peryferia ;) Podłączymy do Arduino moduł zegara czasu rzeczywistego oparty na układzie DS1307. Sam moduł jest bardzo prosty w konstrukcji, i co ważne - posiada opcje potrzymania zegara baterią. Tutaj poradnik jak poskładać takowy samemu: http://tronixstuff.wordpress.com/2010/05/28/lets-make-an-arduino-real-time-clock-shield/ Pewnie sprawi to niejednemu frajde. Ja jednak - jako urodzony - leń poszedłem na łatwiznę i kupiłem za mniej niż 10 pln - gotowy moduł. W dodatku od razu z baterią. Sam moduł komunikuje się z mikrokontrolerem za pomocą magistrali I2C, która w Arduino Uno znajduje się na pinach ANALOG4 i ANALOG5. Do jego obsługi są też gotowe biblioteki, co czyni jego wykorzystanie banalnie prostym. Podłączymy DS1307, oraz parę innych rzeczy, już w następnym odcinku. Oj, będzie dużo kabelków. Przeniesiemy też projekt na docelową płytkę i będzie też trochę lutowania. A tymczasem kończę część czwartą i życzę udanego weekendu. Czytaj dalej...

Zegar cyfrowy LED z bajerami – Część V: czas na bajery ;)

$
0
0
done
Bardzo tęskniliście? Bo po krótkiej przerwie przyszedł czas, na ostatnią już część nieco przydługiego cyklu o zegarku. Troszkę mnie poniosło i rozpisałem się o nim na 5 artykułów, a chciałbym Wam jeszcze pokazać przynajmniej jeden mój INNY projekt, wiec dziś zepnę się w sobie i postaram jak najmniej przynudzać. Ja sam tytuł wskazuje zegar miał być z bajerami, no to będzie. Na razie mamy samą matrycę, no jak już wspomniałem w poprzednim wpisie za moment podłączymy moduł zegara czasu rzeczywistego. Mimo, że to prosty układzik, zdecydowałem się na gotowy moduł, który wygląda tak:RTC-spodrtc-bateria       Drabinkę goldpinów przylutowałem sam, dlatego tak krzywo :D Używać będziemy tylko 4 z nich:
  • SCL -> Arduino pin ANALOG5
  • SDA -> Arduino pin ANALOG4
  • GND -> Arduino GND
  • VCC -> Arduino 5V
Jak już wspominałem w poprzedniej części układ DS1307 komunikuje się ze światem przy pomocy dwukierunkowej magistrali I2C, która ze względów patentowych nazywa się w Arduino TWI (Two Wire Interface). Mamy gotową bibliotekę do komunikacji poprzez ten interfejs, która nazywa sie Wire.h. A do komunikacji z samym układem zegara wykorzystamy bibliotekę RTClib, pobraną ze strony: https://github.com/adafruit/RTClib. (Bibliotekę trzeba pobrać, i rozpakować do katalogu .../Moje Dokumenty/Arduino/libraries/ i zrestartować Arduino IDE) No to kodujemy, czyli to co tygryski lubią najbardziej ;) [crayon-51506162ece01/] Jak widać obsługa układu zegara czasu rzeczywistego jest banalnie prosta. Myślę, że nie wymaga chyba żadnego dodatkowego omówienia. Może tylko wspomnę, że czas jest na sztywno ustawiany w trakcie kompilacji poleceniem: [crayon-51506162ece70/] Gdyby zegar się rozregulował, albo co bardziej prawdopodobne skończyła się bateryjka i będziemy zmuszeni znów ustawić prawidłowy czas - programujemy układ od nowa. Mało to wygodne - wiem, może w wersji 1.1 pomyślę o jakimś sprytnym ustawianiu czasu bez użycia komputera. No to sprawę czasy mamy niejako z głowy, to teraz pozostał czas na tytułowe BAJERY. Jak pewnie zauważyliście, prócz funkcji wyświetlającej godzinę, mamy także w kodzie funkcję wyświetlania daty, której do tej pory nie wykorzystaliśmy. Początkowo pomyślałem sobie, że będę wyświetlał datę co minutę na jedną sekundę, a to bez sensu. Znając życie zawsze jak chciałbym sprawdzić godzinę to akurat wyświetlałaby się data, i odwrotnie. Więc trzeba zrobić jakiś "kontroler". Jako że zegar ma docelowo stać na dosyć wysokim regale, przycisk też byłby mało wygodny. Sięgnąłem do "szuflad" i znalazłem: srf05 Ultradźwiękowy czujnik odległości. Wykorzystam go sobie jako kontroler :D Wystarczy stanąć przed regałem i "pomachać do zegarka", aby pokazał datę. Jak to działa? Jak prosty sonar. Po podaniu stanu wysokiego (trwającego przynajmniej 10us)  na pin oznaczony Trig  (TRIGGER), układ wystrzeliwuje przed siebie ultradźwiękową wiązkę o częstotliwości 40 kHz, po czym przełącza stan pinu ECHO na stan wysoki. Po "usłyszeniu" przez układ echa odbitego od przeszkody sygnału, stan ten przełączany jest na niski. Zatem czas trwania impulsu na wyjściu ECHO, jest proporcjonalny do odległości od przeszkody. Według danych producenta (http://www.robot-electronics.co.uk/htm/srf05tech.htm) wystarczy długość impulsu podzielić przez 58 aby otrzymać odległość w centymetrach. Cała procedura pomiaru będzie zatem wyglądać mniej więcej tak: [crayon-51506162ecec8/] Pojawiła się nam tutaj nowa funkcja:

unsigned long  pulseIn(numer_pinu, poziom_impulsu, timeout);

Funkcja zwraca w mikrosekundach długość trwania impulsu na pinie podanym jako pierwszy parametr. Drugi parametr może przyjmować wartości HIGH/LOW i mówi funkcji czy mierzymy długość trwania stanu wysokiego/niskiego. Timeout to czas (w mikrosekundach) po jakim funkcja przestaje nasłuchiwać i zwraca zero. Dlaczego w naszym przykładzie ustawiłem timeout na 29ms? Ponieważ czujnik SFR-5 ma sprzętowy timeout po 30ms po którym sam "wygasi" wysoki sygnał na pinie ECHO, więc nie ma sensu nasłuchiwać dłużej. Zanim przejdziemy do podłączania czujnika do zegara, jeszcze jeden "bajer". W przepastnych czeluściach szuflad znalazłem też takie cudo, które oczywiście zaraz podłączymy do zegara:

DHT11

To czujnik temperatury i wilgotności powietrza. Kosztował jakieś 8 peelenów i niestety okazał się być nieco "niestabilny". Czasem wariuje. Jakieś 80% czasu działa, pozostałe 20% czasu pokazuje bzdury. Może trafił mi się jakiś wadliwy. Przy okazji kolejnych zakupów wymienię na mniej chiński :D Do jego obsługi możemy wykorzystać gotową bibliotekę DHT11.h (http://playground.arduino.cc/Main/DHT11Lib), a sama procedura obsługi jest znów niezwykle prosta. Podam najprostszy przykład: [crayon-51506162ecf2d/] Oczywiście, my nie będziemy odczytywać w głównej pętli loop, tylko napiszemy sobie specjalną funkcję. No to chyba najwyższy czas podłączyć bajery do Arduno i rozbudować kod o obsługę czujników: [crayon-51506162ecf88/] No i mamy gotowy zegar! Efekt wygląda tak: http://www.youtube.com/watch?v=MstErHKf1Ho I w zasadzie to już prawie koniec. Tyle, że żeby postawić mój projekt w ostatecznej wersji na półce musiałbym tam wpakować moje jedyne Arduino! Co to to nie. Zatem musiałem polutować sobie osobną płytkę pod atmegę z gotowymi gniazdami na peryferia i modułem zasilania. Efekt poniżej: plyta-glownaplyta-spodplyta-z-czujnikami Jedynym wolnym prockiem jakim dysponowałem była Atmega328PU. Prawie taki sam jak w Arduino Uno. Ale okazało się, że Arduino IDE nie chciało współpracować i już przy próbie wgrania bootloadera powiedziało, że nie zgadza mu sie sygnatura układu. Znów poszukałem po internetach i okazało się, że wystarczy na chwilę podmienić w pliku avrdude.conf wpis: [crayon-51506162ed01c/] na [crayon-51506162ed07a/]  i wszystko elegancko się wgrywa i działa bez zarzutu. Zaprogramowałem układ, powpinałem czujniki, poskładałem całość w prowizoryczną kupkę, podłączyłem do prądu i efekt ostateczny wygląda tak:done GOTOWE. Do zrobienia jeszcze:
  • wymienić czujnik temperatury na lepszy
  • jakaś zgrabniejsza obudowa
  • dodać obsługę  drugiego czujnika temperatury (zewnętrznego za okno)
  • dodać możliwość ustawianiu czasu i daty bez użycia programatora.
Gratuluję wszystkim którzy dzielnie dotrwali do końca tego cyklu. Mam nadzieję, że tych, którzy nie mieli wcześniej do czynienia z podobnymi projektami zainteresowałem i przekonałem, że nie taki diabeł straszny. A tych dla których nie było to nic nowego, zachęciłem do zrobienia ambitniejszych projektów. Dzięki za wszystkie komentarze i konstruktywną krytykę. Do znów :) Czytaj dalej...
Viewing all 1332 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>