Wydział Informatyki Katedra Systemów Czasu Rzeczywistego Laboratorium Architektury Komputerów |
Data: poniedziałek 17:00 27.10.2008 |
Sprawozdanie z projektu nr. 4-5 Skład grupy: Łukasz Jacewicz Michał Jurczuk |
Prowadzący: dr inż. Mirosław Omieljanowicz
|
|
Ocena: |
Cel projektu:
Gra, w której poruszamy się samochodem (x'em) i omijamy przeszkody (zera), jeżeli ominiemy odpowiednią ilość przeszkód wygrywamy. Tempo gry systematycznie wzrasta, zmniejszając czas reakcji gracza w którym może nacisnąć przycisk. Po zakończeniu gry wyświetlana jest liczba zdobytych punktów.
Wiadomości teoretyczne:
Informacje wstępne o wyświetlaczu LCD.
EasyWeb2 nie zawiera wbudowanego sterownika wyświetlacza, dlatego też do wyświetlania wykorzystuje specjalizowany układ wyświetlacza LCD z własnym sterownikiem. Rolę tego sterownika pełni układ firmy Hitach HD44780. Komunikacja z układem polega jedynie na przesyłaniu danych, które mają być wyświetlone, oraz instrukcji określających w jaki sposób te dane mają być wyświetlane.
Wymiana danych pomiędzy układem HD 44780, a mikrokontrolerem może odbywać się za pośrednictwem ośmio lub czterobitowej magistrali danych. Jednak bez względu na wybraną szerokość interfejsu przesłane do modułu wyświetlającego wartości muszą mieć postać 8-bitową. W związku z tym w trybie 4-bitowym transmisja jednej instrukcji lub danej musi dokonać się w dwóch cyklach - w pierwszym przesyłany jest półbajt starszy a w drugim młodszy. W układzie EasyWeb mamy podłączone wyjścia P2.4 do P2.7 do linii danych DB4 do DB7, wobec tego układ należy ustawić w trybie pracy 4 bitowej. Do wyświetlacza podłączone są linie danych DB4-DB7, trzy linie sterujące E, RS, R/W, oraz zasilanie.
Układ posiada 2 8-bitowe rejestry: rejestr instrukcji IR (stan 0 na wejściu RS), służący do przyjmowania kodów instrukcji takich jak ustawienie stanu wyświetlacza czy przesunięcie kursora, oraz rejestr danych DR (stan 1 na wejściu RS), który przyjmuje dane przesyłane na wyświetlacz.
Wyświetlacz posiada 2 linie i na każdej może wyświetlać 16 znaków. W pamięci DD_RAM dla każdej linii może przechowywać 40 znaków, pod adresem 00h-27h górna linia, zaś 40h-67h dolna linia. Przesuwanie danych za pomocą funkcji DATA_ROL_LEFT lub DATA_ROL_RIGHT odbywa się jednocześnie na obu liniach.
Opis linii wyświetlacza.
RS - odpowiada za interpretowanie danych przychodzących do sterownika wyświetlacza
0 - (stan niski) dane interpretowane jako instrukcje
1 - (stan wysoki) dane interpretowane jako adres do tablicy znaków zapisanej w pamięci ROM wyświetlacza
R/W - informuje sterownik wyświetlacza o zamiarze pisania lub czytania do wyświetlacza.
W układzie EasyWeb2 linia ta jest na stałe podłączona do masy, czyli możliwy jest tylko zapis.
E - służy do aktywowania zapisu danych do układu, aktywowana po daniu danych na linie
danych.
DB4 -DB7 - linie do przesyłania danych
DB0-DB4 - linie do przesyłania danych, nie używane podczas 4-bitowych operacji
Komendy wyświetlacza.
#define CLR_DISP 0x01 // czyści wyświetlacz
#define DISP_OFF 0x08 // wyłącza wyświetlacz
#define DISP_ON 0x0c // włącza wyświetlacz
#define DATA_ROL_LEFT 0x18 // przesuwa znaki w lewo
#define DATA_ROL_RIGHT 0x1c // przesuwa znaki w prawo
#define DD_RAM_ADDR 0x80 // ustawia kursor na linii pierwszej
#define DD_RAM_ADDR2 0xc0 // ustawia kursor na linii drugiej
Watchdog timer
WDT jest to układ, który czuwa na prawidłowym działaniem programu. Eliminuje wszelkie zawieszenia programu przez restart całego urządzenia. W programie by odczekać drgania styków używamy długiej pętli, dlatego też jesteśmy zmuszeni do wyłączenia WDT gdyż, w przeciwnym wypadku, każda pętla uznawana byłaby za zawieszenie programu i wtedy automatycznie zostałaby przerwana.
Inicjalizacja wyświetlacza
Ponieważ po włączeniu zasilania występują stany nieustalone i nie wiemy w jakim trybie jest ustawiony interfejs na początku musimy ustawić tryb pracy magistrali, czyli na tryb 4-bitowy. Czyli trzykrotnie wysyłamy do sterownika komendę inicjującą tryb 8-bitowy, a następnie komendę inicjującą tryb 4-bitowy. Po przejściu na tryb 4-bitowy, wysyłamy instrukcję włączenia wyświetlacza, następnie ustawiamy jego parametry i czyścimy wyświetlacz. Po wykonaniu wszystkich czynności możemy już wysyłać dane oraz instrukcje do wyświetlacza.
Kod programu:
#include <msp430x14x.h>
#include "lcd.h"
#include "portyLcd.h"
unsigned char i;
unsigned char znak;
int linia=1; // zmienna określająca linię: 1 -gorna ,2 -dolna
char tablica[2][40]; //tablica znaków z przeszkodami
int pozycja; //aktualna pozycja x'a
int czas; //czas w ciągu którego gracz może zmienić linię
int wynik; //ilość punktów zdobytych przez gracza
int ustawKursorNaOkreslonejPozycjiWLinii(int nr_linii, int nr_pozycji) // funkcja, za pomocą której ustawiamy kursor na określonej pozycji w podanej linii
{
if (nr_pozycji < 0 && nr_pozycji > 40) // w pamięci dla każdej linii przechowywane jest miejsce na 40 pozycji (znaków), ale i tak na wyświetlaczu możemy wyświetlić 16 znaków
return -1; // zatem prawidłowa wartość dla nr_pozycji to liczba z zakresu od 0 do 39.
switch(nr_linii) // nr_linii - to numer linii wyświetlacza
{
case 1:
SEND_CMD(DD_RAM_ADDR | nr_pozycji); // ustawienie kursora na odpowiednia pozycje w pierwszej linii (oczywiście mówimy tu o wyświetlaczu ustawionym na dwie linie)
break;
case 2:
SEND_CMD(DD_RAM_ADDR2 | nr_pozycji); // ustawienie kursora na odpowiednia pozycje w drugiej linii
break;
default:
return -1; // podano nr_linii nie do zrealizowania (inny niż pierwsza czy druga linia)
}
return 0;
}
void ustawZnakNaOkreslonejPozycjiWPodanejLinii(int nr_linii, int nr_pozycji, char znak) // funkcja ustawiająca znak na odpowiedniej pozycji w podanej linii
{
if (-1 == ustawKursorNaOkreslonejPozycjiWLinii(nr_linii, nr_pozycji)) // wykorzystujemy wcześniej zdefiniowaną funkcję do ustawienia kursora na określonej pozycji w linii
return;
SEND_CHAR(znak); // jeśli ustawienie powidło się umieszczenie odpowiedniego znaku na tej pozycji
}
void zamienLiczbe(int liczba) //funkcja zamienia liczbę na ciąg znaków i wypisuje je na ekran (na aktualnej pozycji)
{
if (liczba/10)
zamienLiczbe(liczba/10);
SEND_CHAR(liczba%10+0x30);
}
void init_przeszkody() //funkcja za pomocą której inicjalizujemy tablicę z przeszkodami
{
int i,j;
for(j=0;j<2;j++)
for(i=0;i<40;i++)
tablica[j][i]=' ';
tablica[0][4]='0';
tablica[0][10]='0';
tablica[0][11]='0';
tablica[0][12]='0';
tablica[0][20]='0';
tablica[0][21]='0';
tablica[0][33]='0';
tablica[1][15]='0';
tablica[1][16]='0';
tablica[1][17]='0';
tablica[1][26]='0';
tablica[1][27]='0';
tablica[1][37]='0';
for(j=0;j<2;j++)
for(i=0;i<40;i++)
if(tablica[j][i]=='0') ustawZnakNaOkreslonejPozycjiWPodanejLinii(j+1, i, tablica[j][i]); //wstawiamy znaki do pamięci wyświetlacza
}
int rysujXa(int rol) //w przypadku rol=0 lub rol=-1 funkcja rysuje x'a (pojazd którym steruje gracz)
na odpowiedniej linii w zależności od wciśniętego przycisku, w pozostałych przypadkach następuje przesunięcie ekranu w prawo i odrysowanie x'a (zwraca 0) lub znaku # jeżeli x zderzył się z przeszkodą (zwraca -1)
{
if (0==rol||-1==rol) //gracz wcisnął przycisk
{
int linia_pom;
if(rol==0) linia_pom=1; else linia_pom=2;
if(linia!=linia_pom) { //sprawdza czy wciśnięto przycisk inny niż linia na której znajduje się x
ustawZnakNaOkreslonejPozycjiWPodanejLinii(linia, pozycja , ' ');
linia=linia_pom;
if (tablica[linia-1][pozycja] == '0')
{
ustawZnakNaOkreslonejPozycjiWPodanejLinii(linia, pozycja , '#'); //nastąpiło zderzenie, wstawiamy znak #
return -1;
}
ustawZnakNaOkreslonejPozycjiWPodanejLinii(linia, pozycja , 'x'); //nie nastąpiło zderzenie, odrysowujemy znak x
}
}
else //po określonym czasie następuje przesunięcie w prawo
{
int i;
ustawZnakNaOkreslonejPozycjiWPodanejLinii(linia, pozycja, ' '); //kasujemy x'a, wstawiając w jego miejscie spacje
for (i=0; i<rol; ++i) //przesuwamy ekran w prawo rol ilość razy i odpowiednio zmieniamy pozycję x'a
{
SEND_CMD(DATA_ROL_RIGHT); //przesunięcie w prawo
--pozycja;
if(pozycja==-1) pozycja=39;
}
if (tablica[linia-1][pozycja] == '0')
{
ustawZnakNaOkreslonejPozycjiWPodanejLinii(linia, pozycja , '#'); //nastąpiło zderzenie, rysujemy znak #
Delayx100us(250); //czekamy chwilę czasu by gracz mógł dostrzec, że nastąpiło zderzenie
return -1;
}
ustawZnakNaOkreslonejPozycjiWPodanejLinii(linia, pozycja , 'x');//nie nastąpiło zderzenie, rysujemy x'a
//jeżeli gracz ominą przeszkodę zwiększamy liczbę punktów
int pom=linia-1;
pom=(pom)?0:1;
if (tablica[pom][pozycja] == '0') wynik++;
}
return 0;
}
void main( void )
{
WDTCTL=WDTPW+ WDTHOLD; // zatrzymanie WDT
InitPortsLcd(); // inicjalizacja portów
InitLCD(); // inicjalizacja LCD
clearDisplay(); // czyszczenie wyświetlacza
P4DIR &= ~BIT4; // konfigurowanie linii P4.4 (przycisk B1) jako wejście
P4DIR &= ~BIT5; // konfigurowanie linii P4.4 (przycisk B1) jako wejście\P4DIR &= ~BIT5; // konfigurowanie linii P4.4 (przycisk B1) jako wejście
P4DIR &= ~BIT6; // konfigurowanie linii P4.4 (przycisk B1) jako wejście
// kod odpowiedzialny za umieszczenie napisu 'ABCD' na pierwszej linii wyświetlacza na pozycjach od 1 do 4 (przymuję określenie tutaj pozycji wyświetlacza jaka liczbę od 1 do 16)
while(1) {
//wyświetlenie napisu START
SEND_CMD(CLR_DISP);
ustawKursorNaOkreslonejPozycjiWLinii(1, 4);
SEND_CHAR('S');
Delayx100us(250);
SEND_CHAR('T');
Delayx100us(250);
SEND_CHAR('A');
Delayx100us(250);
SEND_CHAR('R');
Delayx100us(250);
SEND_CHAR('T');
Delayx100us(250);
SEND_CHAR('=');
Delayx100us(70);
SEND_CHAR('=');
Delayx100us(70);
SEND_CHAR('>');
Delayx100us(150);
SEND_CMD(CLR_DISP);
init_przeszkody(); //inicjalizacja przeszkód
int koniec=0,licznik,win=0;
wynik=0;
czas=400; //początkowa ilość czasu, po którym nastąpi przesunięcie w prawo
pozycja=15; //początkowa pozycja pozycja x'a
linia=1;
while(1)
{
licznik=0;
for(;;)
{
if( (P4IN & BIT4) ==0 ) // sprawdzamy stan przycisku
koniec=rysujXa(0);
else if( (P4IN & BIT5) ==0 ) //sprawdzamy stan przycisku 2
koniec=rysujXa(-1);
if(koniec==-1) break; //nastąpiło zderzenie, więc wychodzimy z pętli
//odczekanie niedużego odstępu czasu(w zależności od wartości czas) po których nastąpi sprawdzenie czy 1 z przycisków został wciśnięty
if(czas<20) {
Delayx100us(1);
licznik+=1;
}
else {
Delayx100us(20);
licznik+=20;
}
if(licznik>=czas) break; //jeżeli suma niedużych odstępów czasu wyniesie co najmniej wartość czas, następuje wyjście z pętli a następnie przesunięcie w prawo
}
if(koniec==-1) break; //nastąpiło zderzenie, więc wychodzimy z pętli
koniec=rysujXa(1); //przesunięcie w prawo
if(koniec==-1) break; //nastąpiło zderzenie, więc wychodzimy z pętli
//zmniejszamy czas w którym gracz może nacisnąć przycisk
if(czas>100) czas-=10;
else if(czas<=100&&czas>60) czas-=5;
else if(czas<=60&&czas>1) czas--;
if(wynik>=50) {win=1;break;} //jeżeli ilość ominiętych jest >= od 50 gracz wygrywa, wychodzimy z pętli
}
SEND_CMD(CLR_DISP);
ustawKursorNaOkreslonejPozycjiWLinii(1, 4);
//wypisanie komunikatu o wygranej lub przegranej
if(win||wynik>=50) {
SEND_CHAR('Y');
SEND_CHAR('o');
SEND_CHAR('U');
SEND_CHAR(' ');
SEND_CHAR('W');
SEND_CHAR('i');
SEND_CHAR('N');
}
else {
SEND_CHAR('G');
SEND_CHAR('A');
SEND_CHAR('M');
SEND_CHAR('E');
SEND_CHAR(' ');
SEND_CHAR('O');
SEND_CHAR('V');
SEND_CHAR('E');
SEND_CHAR('R');
}
//wypisanie ilości zdobytych punktów
ustawKursorNaOkreslonejPozycjiWLinii(2,4);
SEND_CHAR('P');
SEND_CHAR('U');
SEND_CHAR('N');
SEND_CHAR('K');
SEND_CHAR('T');
SEND_CHAR('Y');
SEND_CHAR(':');
SEND_CHAR(' ');
zamienLiczbe(wynik); //zamiana i wypis ilość punktów
while(1) if( (P4IN & BIT6) ==0 ) break; //czekamy aż użytkownik naciśnie przycisk 3, po którym gra rozpocznie się od początku
}
}