To mój pierwszy poradnik na tejże stronie więc proszę o wyrozumiałość :)
Jest to prosta gra, w której sterujemy potencjometrem kręcąc nim w lewo lub prawo sterując w ten sposób pionkiem. Którego zadaniem jest zmieszczenie się w losowo generowanej dziurze w murach, które z coraz to szybszą prędkością spadają z góry.
Nagrany został przeze mnie prosty film demonstrujący działanie programu dostępny pod adresem:
Do wykonania projektu potrzebne będą:
– Arduino uno ( podróbka za 20zł),
– Potencjometr (2zł),
– Wyświetlacz z noki 5510 (8zł).
[1] Biblioteka –
LCD5110_Graph
http://www.rinkydinkelectronics.com/library.php?id=47
Istnieje pełna dowolność w zastosowaniu potencjometru o dowolnej wartości. Należy jednak dopasować jego wartość do kodu programu. W kodzie, jak i na zamieszczonym filmiku została zaznaczona wartość, którą należy zmienić by program działał tak jak powinien.
Jest to pozycjaR, nie mam dokładnego wzoru na policzenie tej wartości, ponieważ każdy potencjometr ze względu na swoją niedoskonałość nigdy nie ma idealnie tej wartości, którą deklaruje producent i z tego prostego faktu proponuje wybrać ją metodą prób i błędów.
Przed zgraniem programu na nasze arduino i podłączeniu wszystkiego zgodnie ze schematem z rysunku nr 1 nie można zapomnieć o wgraniu odpowiedniej biblioteki do obsługi wyświetlacza [1].
Rys.1 Schemat połączeń arduino z wyświetlaczem i potencjometrem
Po zgraniu programu i podłączeniu wszystkiego zgodnie z schematem gra wygląda następująco:
Rys.2 Działająca gra
Szybkość barier zwiększa się jedynie do uzyskania 50 pkt co wynika z ograniczeń sprzętowych arduino i wariowaniu sprzętu po przekroczeniu granicy prędkości uzyskiwanej po tym czasie. Może uda ci się ją obejść, mi się nie udało :)
Pionek potrafi delikatnie ruszać się lewo prawo bez naszej kontroli co wynika z zjawiska znanego pod nazwą: drganie styków. Można z tym zjawiskiem walczyć na wiele sposobów, lecz w tym przypadku nie jest na tyle uciążliwe by poświęcać mu czas.
Na samej atmedze
Program można również umieści na gołej atmedze, aby tego dokonać musimy wpierw połączyć układ z rysunku numer 3. I z pozycji programu arduino wgrać na naszą atmegę w moim przypadku atmege328p-AN (dostępnej za 12 zł na TME) bootloadera. Nie będę się rozwodził jak to dokładnie zrobić, bo w internecie możemy znaleźć milion poradników. Polecam świetny kanał Elektroprzewodnik.
Rys.3 Schemat to wgrania bootloadera
Po zgraniu bootloadera możemy podmienić amtege z arduino z naszą gołą z już wgranym bootloderem na płyce i wgrać na nią naszą grę. Możemy jeszcze sprawdzić, czy wszystko działa prawidłowo, jeśli tak nic nie stoi na przeszkodzie, by umieścić ją na płytce. Płytce do szybkiego łączenia jak ja lub jeśli chcemy i co ja również planuje na specjalnie zaprojektowanej drukowanej.
Do podłączenia układu bez arduino potrzebne będą:
– Atmega (w moim przypadku atmega328p-AN)
– Rezystor 10k Ohm (zamontowany pomiędzy RESET a 5V, gwarantuje nam że żadne zaindukowane tam napiecie nie zresetuje atmegi),
– Potencjometr,
– Wyświetlacz noki 5510,
– Rezonator kwarcowy 15 MHz,
– Kondensator odprzęgający x2 100pF.
Jeśli mamy już wszystkie elementy możemy połączyć układ zgodnie ze schematem z rysunku nr 4.
Rys.4 Schemat połączeń atmegi
Dzięki pomocy rysunku numer 5 wiemy jakie piny atmegi odpowiadają pinom arduino, wszystko łączymy zgodnie z zamieszonym schematem widocznym na rysunku numer 4. Najważniejsze jest oczywiście zasilanie, które jest po lewej i prawej stronie. Strony odróżnimy charakterystycznym wycięciem widocznym u góry :)
Rys nr.5 Pinout atmegi
Gra bez użycia arudino działa identycznie, wybacznie za nieestetyczne połączenie i ogólny chaos na widocznym rysunku nymer 6, lecz wciąż jestem w fazie projektowania płytki drukowanej.
Rys.6 Moje wykonanie bez arduino
Poniżej zamieszczam kod źródłowy i życzę miłej zabawy w graniu i jego udoskonalaniu :)
Kod:
// Piny wyswietlacza sa polaczone w nastepujacy sposob:
// SCK – Pin 8
// MOSI – Pin 9
// DC – Pin 10
// RST – Pin 11
// CS – Pin 12
#include <LCD5110_Graph.h> // Biblioteka ekranu
LCD5110 myGLCD(8,9,10,11,12); // Deklaracja pinow wyswietlacza
extern unsigned char SmallFont[]; // Zdefiniowanie czcionki
void setup()
{
digitalWrite(7, HIGH); // Ustawienie stanu wysokiego na pinie 7 w celu pomiaru spadku napiecia na potenciometrze
myGLCD.InitLCD(); // Zalaczenie wyswietlacza
myGLCD.setFont(SmallFont); // Deklaracja czcionki
randomSeed(analogRead(0)); // Uruchomienie pseudo generatora liczb losowych
Serial.begin(9600); // Ustawienie szybkosci przesylu bitow na sekunde
}
float pozycjaR; // pozycja potenciometru
float pozycjaP; // pozycja pionka
float randNumber; // wyolosowana wartosc
float przedzial; // przedzial w ktorym porusza sie sciana
float przedzial2; // przedzial ktorym moze dojsc do kolizji
float gameover; // informacja o przegranej
float time; // czas ktory uplynol od rozpoczecia gry
float wynik; // ilosc zdobytych pkt
void loop()
{
if (gameover==1) // polecenie wykonywane gdy przegramy
{
myGLCD.clrScr(); // wyczyszczenie ekranu
myGLCD.print(“gameover”, CENTER, 5); // napis: gameover
myGLCD.print(“Twoj wynik to:”, CENTER, 30); // napis: Twój wynik to
myGLCD.printNumI((wynik),CENTER, 40); // wyswietla ilosc zdobytych pkt
for (int i=84; i>=-(34*6); i–) // petla sluzaca do stworzenia przesuwajacego sie tekstu
{
myGLCD.print(“Wcisnij RESET aby kontynuowac. “, i, 18); // przesuwajacy sie tekst
myGLCD.update(); // odswiezenie ekranu
delay(50); // opoznienie
}
myGLCD.update(); // odswiezenie ekranu
}
if (gameover==0) // jesli nie przegralismy ( jeszcze ) wykonuje sie funkcja gry
{
for (int i=35; i <= 97; i++) // okres w ktorym wykonuje sie gra
{
if(wynik<47) // zabezpieczenie przed zbyt szybka zmiana poziomu trudnosci
{
delay(50-wynik); // zmiana poziomu trudnosci zalezna od ilosci zdobytych pkt
}
else if (wynik>47) // zabezpieczenie przed zbyt szybka zmiana poziomu
{
delay(3); // maksymalny poziom trudnosci
}
time = millis(); // rozpoczecie odliczania czasu
wynik=(time/2000); // przeliczenie uplynietego czasu na wynik
float pozycjaR=analogRead(A0); // okreslenie pozycjiR zaleznie od odczytu z pinu A)
int sensorValue = analogRead(A0); // czyta wartosc z pinu A0
Serial.println(sensorValue); // przekazuje informacje do serial monitor
myGLCD.clrScr(); // wyczyszczenie wyswietlacza
myGLCD.drawRect(0, 0, 83, 47); // narysowanie obramowania
myGLCD.drawLine(24,0 ,24 , 83); // odciecie pola gry od pola z wynikiem
myGLCD.print(“Pkt”, 1, 5); // wyswietlenie napisu: pkt
myGLCD.printNumI(wynik, 1, 15); // wyswietlenie ilosci zdobytych pkt
myGLCD.print(“:)”, (pozycjaP+25),39 ); // wyswietlenie pionka zelenie od wartosci odczytanej na pinie A0, wspolrzedne po przecinku gwarantuja iz pionek nie wyjdzie poza pole gry
pozycjaP=((pozycjaR)/22.5); // Ilosc mozliwych odczytow pinu A0 ( od 0 do 209 ) zostala podzielona tak aby zapewnic stabilne polozenie pionka w granicach pola gry
myGLCD.update(); // odwiezenie ekranu
if ((i > 35)&&(i<37)) // przedzial w ktorym dochodzi do wylosowania liczby odpowadajacej pojawiajacej sie przeszkodzie
{
randNumber = random(4); // losowanie i zakres losowania
}
if ((i > 50)&&(i<97)) // okreslenie przedzialu czasu w ktorym wykonuje sie funkcja gry
{
przedzial=1;
}
else // okreslenie przedzialu czasu w ktorym nie wykonuje sie funkcja gry
{
przedzial=0;
}
if ((i > 89)&&(i<97)) // okreslenie przedzialu czasu w ktorym moze dojsc do kolizji
{
przedzial2=1;
}
else // okreslenie przedzialu czasu w ktorym nie moze dojsc do kolizji
{
przedzial2=0;
}
if((randNumber==0)&&(przedzial==1)) // Wykonanie jednej z 4 mozliwych barier zaleznie od wylosowane wartosci pod warunkiem ze znajdujemy sie w przedziale czasu na wykonanie funkcji gry
{{
myGLCD.drawLine(39,(i-50) ,83 ,(i-50)); // narysowanie lini tworzocej przeszkode
myGLCD.drawLine(39,(i-51) ,83 ,(i-51)); // narysowanie lini tworzocej przeszkode
myGLCD.drawLine(39,(i-52) ,83 ,(i-52)); // narysowanie lini tworzocej przeszkode
myGLCD.drawLine(39,(i-53) ,83 ,(i-53)); // narysowanie lini tworzocej przeszkode
myGLCD.drawLine(39,(i-54) ,83 ,(i-54)); // narysowanie lini tworzocej przeszkode
myGLCD.update(); // odswiezenie ekranu
}
if ((pozycjaP>4)&&(przedzial2==1)) // Sprawdzenie czy doszlo do kolizji uwzgledniajac wysokosc i szerokosc pionkaw pikselach
{
gameover=1; // przekazanie informacji o przegranej
break; // przerwanie petli
}
}
if((randNumber==1)&&(przedzial==1)) // analogicznie jak poprzednio z zmiana wspolrzednych bariery i wspolczynnikow kolizji
{{
myGLCD.drawLine(24,(i-50) ,39 ,(i-50));
myGLCD.drawLine(54,(i-50) ,83 , (i-50));
myGLCD.drawLine(24,(i-51) ,39 ,(i-51));
myGLCD.drawLine(54,(i-51) ,83 , (i-51));
myGLCD.drawLine(24,(i-52) ,39 ,(i-52));
myGLCD.drawLine(54,(i-52) ,83 , (i-52));
myGLCD.drawLine(24,(i-53) ,39 ,(i-53));
myGLCD.drawLine(54,(i-53) ,83 , (i-53));
myGLCD.drawLine(24,(i-54) ,39 ,(i-54));
myGLCD.drawLine(54,(i-54) ,83 , (i-54));
myGLCD.update();
}
if (((pozycjaP < 12 )||(pozycjaP>19))&&(przedzial2==1))
{
gameover=1;
break;
}
}
if((randNumber==2)&&(przedzial==1)) // analogicznie jak poprzednio z zmiana wspolrzednych bariery i wspolczynnikow kolizji
{{
myGLCD.drawLine(24,(i-50) ,54 ,(i-50));
myGLCD.drawLine(68,(i-50) ,83 , (i-50));
myGLCD.drawLine(24,(i-51) ,54 ,(i-51));
myGLCD.drawLine(68,(i-51) ,83 , (i-51));
myGLCD.drawLine(24,(i-52) ,54 ,(i-52));
myGLCD.drawLine(68,(i-52) ,83 , (i-52));
myGLCD.drawLine(24,(i-53) ,54 ,(i-53));
myGLCD.drawLine(68,(i-53) ,83 , (i-53));
myGLCD.drawLine(24,(i-54) ,54 ,(i-54));
myGLCD.drawLine(68,(i-54) ,83 , (i-54));
myGLCD.update();
}
if (((pozycjaP < 27 )||(pozycjaP>33))&&(przedzial2==1))
{
gameover=1;
break;
}
}
if((randNumber==3)&&(przedzial==1)) // analogicznie jak poprzednio z zmiana wspolrzednych bariery i wspolczynnikow kolizji
{{
myGLCD.drawLine(24,(i-50) ,68 ,(i-50));
myGLCD.drawLine(24,(i-51) ,68 ,(i-51));
myGLCD.drawLine(24,(i-52) ,68 ,(i-52));
myGLCD.drawLine(24,(i-53) ,68 ,(i-53));
myGLCD.drawLine(24,(i-54) ,68 ,(i-54));
myGLCD.update();
}
if ((pozycjaP<41)&&(przedzial2==1))
{
gameover=1;
break;
} } } } }