Wydział Informatyki
Katedra Systemów Czasu Rzeczywistego
Laboratorium Architektury Komputerów
Data
25.11.2009
Ćwiczenie nr: 4
Temat: Gra
Zespół nr 2 1. Robert Jarocki
Grupa nr 6 2. Jarosław Jastrzębski
Prowadzący
…......................
Ocena
…......................
Gra „Quiz z wiedzy o msp430”
Teoria:
EasyWeb2 pracuje na procesorze z serii ‘1xx’ i nie zawiera wbudowanego sterownika
wyświetlacza. Wykorzystuje on do wyświetlania specjalizowany układ wyświetlacza LCD z
własnym sterownikiem. Rolę sterownika pełni układ firmy Hitach HD 44780. Do sterownika
mikrokontroler wysyła tylko dane (które mają być wyświetlane) i instrukcje (w jaki sposób
mają być wyświetlane).
Wyświetlacz może pracować z ośmio- bądź cztero- bitową magistralą danych.
Zazwyczaj w systemach mikroprocesorowych stosuje się tryb pracy z czterobitową
magistralą danych. Oprócz linii danych DB4-DB7, do wyświetlacza podłączone są trzy linie
sterujące E,RS,R/W oraz zasilanie.
Komunikacja mikrokontrolera z układem HD44780:
Układ ma 2 8-bitowe rejestry rejestr instrukcji IR, wybierany przy stanie 0 na wejściu RS,
oraz rejestr danych DR, wybierany przy stanie 1 na wejściu RS.
Rejestr IR przyjmuje kody instrukcji (np. ustawienie stanu wyświetlacza, czyszczenie
wyświetlacza, przesunięcie kursora). Rejestr DR przyjmuje dane do przesłania na
wyświetlacz.
HD44780U może pracować w jednym z 2 trybów 4 i 8 bitowym. W układzie EasyWeb mamy
podłączone wyjścia P2.4 do P2.7 do linii danych DB0 do DB3 kontrolera wyświetlacza.
Wobec tego układ należy ustawić do pracy w trybie 4 bitowym.
Linia RS odpowiada za interpretowanie danych przychodzących do sterownika
wyświetlacza. Gdy linia RS jest w stanie niskim to dane są interpretowane jako
instrukcje, gdy natomiast jest w stanie wysokim to dane są interpretowane jako adres
znaku z tablicy znaków zapisanej w pamięci ROM wyświetlacza. I na wyświetlaczu
pojawia się znak z tej właśnie tablicy.
Linia R/W informuje sterownik wyświetlacza czy mamy zamiar czytać, czy też pisać
do wyświetlacza. W EasyWeb linia ta jest na stałe podłączona do masy, czyli
możliwy jest tylko zapis. Takie rozwiązanie powoduje niewielkie zmniejszenie
funkcjonalności wyświetlacza, ale sprawia, że odchodzi nam problem sterowania tą
linią.
Linia E służy do aktywowania zapisu danych do układu i powinno być aktywna po podaniu
danych na linie danych.
Wysłanie instrukcji do układu powoduje ustawienie flagi BF, która jest aktywna aż do
przetworzenia bieżącej instrukcji i w tym czasie inne instrukcje nie są akceptowane.
Kiedy RS = 0 i R/S = 1, BF jest podłączona do wyjścia DB7.
Opis gry:
Gra, którą napisaliśmy jest quizem. Użytkownik ma możliwość odpowiedzieć na 18 pytań (jedynie
tyle pytań mogliśmy wgrać do pamięci msp430).
Aby rozpocząć grę wciskamy przycisk żółty (pierwszy).
Możliwe odpowiedzi to:
Tak – przycisk biały (drugi),
Nie – przycisk czerwony (trzeci),
Nie wiem – przycisk zielony (czwarty);
Gracz ma 15 sekund na udzielenie odpowiedzi na każde z pytań. Jeżeli odpowiedź nie zostanie
wybrana to program przechodzi po tym czasie do kolejnego pytania.
Punktacja:
+ 2 pkt. - odpowiedź poprawna,
- 1 pkt. - odpowiedź błędna,
0 pkt. - pominięcie pytania.
Po ukończeniu quizu użytkownik otrzymuje informację o ilości zdobytych punktów a następnie
informację o ilości zdobytych przez poprzedniego gracza.
Budowa gry:
Plik „funkcje.c” zawiera funkcje niezbędne do działania programu.
#include<msp430x14x.h>
#include "lcd.h"
#include "portyLcd.h"
#include "funkcje.h"
void wyswietlInt(int liczba)
// zamiana zmiennej całkowitej na napis na wyświetlaczu
{
if(liczba<0)
// ustawiamy odpowiedni znak liczby i wysyłamy go na ekran
{
liczba=-liczba;
// dalej operujemy na liczbie dodatniej
SEND_CHAR('-');
}
int dzielnik=10, cyfra;
while(dzielnik<=liczba)
// szukamy największej potęgi 10
dzielnik*=10;
dzielnik/=10;
while(dzielnik>0)
{
cyfra=liczba/dzielnik;
liczba=liczba-dzielnik*cyfra;
dzielnik/=10;
SEND_CHAR((char)(0x30+cyfra)); //
wysyłamy uzyskaną cyfrę na wyświetlacz
}
}
int dlugosc_napisu(char * napis)
// funkcja zwraca długość ciągu znaków w tablicy
{
int dlugosc, i=0;
while(napis[i++]) ;
// gdy napotkamy wartość NULL to konczymy liczenie
dlugosc=i-1;
return dlugosc;
}
void wyswietl_napis(char * napis, int dlugosc, int linia)
// wyświetlamy napis od bieżącej pozycji kursora w linii
{
if(linia==1)
SEND_CMD(DD_RAM_ADDR);
else
SEND_CMD(DD_RAM_ADDR2);
for(int i=0; i<dlugosc; ++i)
wyswietl_znak(napis[i]);
}
void wyswietl_przesuwany_napis(char * napis, int linia)
// wyświetlamy dowolnej dlugości napis, ktory jest przesuwany
{
int dlugosc=dlugosc_napisu(napis);
// pobieramy długość napisu
if(linia==1)
// sprawdzamy, w ktorej linii należy wyświetlić
{
clearDisplay();
if(dlugosc<=16)
// jeśli napis zmieści się w jednej linii to go nie przesuwamy
wyswietl_napis(napis, dlugosc, linia);
else
{
wyswietl_napis(napis, 16, linia);
// wyświetlamy to co mieści się w linii
for(int i=0; i<CZAS; ++i)
Delayx100us(1000);
dlugosc-=16;
for(int i=1; dlugosc+16>16; --dlugosc, ++i)
//wyświetlamy resztę napisu okresowo o znak
{
clearDisplay();
wyswietl_napis(&napis[i], 16, linia);
Delayx100us(70);
}
}
}
else
// wyświetlanie napisu przesuwanego w drugiej linii
{
clearDisplay();
if(dlugosc<=16)
wyswietl_napis(napis, dlugosc, linia);
else
{
wyswietl_napis(napis, 16, linia);
for(int i=0; i<CZAS; ++i)
Delayx100us(1000);
dlugosc-=16;
for(int i=1; dlugosc+16>16; --dlugosc, ++i)
{
clearDisplay();
wyswietl_napis(&napis[i], 16, linia);
Delayx100us(70);
}
}
}
}
void wyswietl_znak(char znak)
{
SEND_CHAR(znak);
}
Plik „funkcje.h” zawiera definicje stałych i nagłówki funkcji:
#ifndef FUNKCJE
#define FUNKCJE
#define CZAS 4
#define CZAS_OPOZNIENIA 150000
#define CZAS1 15
void wyswietl_znak(char znak);
void wyswietlInt(int liczba);
void wyswietl_napis(char * napis, int dlugosc, int linia);
void wyswietl_przesuwany_napis(char * napis, int linia);
int dlugosc_napisu(char * napis);
#endif
Plik “pytodp.h” zawiera pytania i odpowiedzi. To tu możemy dodawać i edytować pytania i
odpowiedzi.
#ifndef PYTODP
#define PYTODP
#define PYTANIA 19
char pytania [PYTANIA][100]=
{
{"Czy uklad MSP430 ma 3 porty?"},
{"Czy dioda status jest podlaczona do portu P1.5?"},
{"Czy diody podlaczone do portow P2.1 i P2.2 dzialaja na przekaznikach?"},
{"Czy Watchdog Timer jest odpowiedzialny za sprawdzenie prawidlowego działania programu?"},
{"Czy na linii wyswietlacza LCD na plytce EasyWeb2 miesci sie 20 znakow?"},
{"Czy rozmiar jednego znaku na wyswietlaczu to 40x8?"},
{"Czy WDT ma wbudowany uklad MSP430?"},
{"Czy rejestr PxDIR sluzy do ustawiania flagi BF?"},
{"Czy w celu aktywacji danych dla LCD uzywamy pinu E?"},
{"Czy ustawienie 0 dla WDTHOLD oznacza, ze Watchdog jest wlaczony?"},
{"Czy szerokosc szyny danych LCD w EasyWeb2 to 4 bity?"},
{"Czy rejestr WDTPW ma dlugosc 10 bitow?"},
{"Czy haslo WDT to 05ah?"},
{"Czy domyslnym zegarem procesora jest XT2CLK?"},
{"Czy z WDT zwiazany jest rejestr 0120h?"}
{"Czy na wyświetlaczu możemy wyświetlić 16 znakow?"}
{"Czy wyświetlacz może pracowac z 10-bitowa magistrala danych?"}
{"Czy na wyświetlaczu możemy wyświetlić 3 linie?"}
};
char odpowiedzi[PYTANIA]={0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0};
#endif
Odpowiedź 0 oznacza odpowiedź błędną (Nie), a 1 oznacza odpowiedź poprawną (Tak).
Main:
#include<msp430x14x.h>
#include "lcd.h"
#include "portyLcd.h"
#include "funkcje.h"
#include "pytodp.h"
#define TAK BIT5&P4IN
// przycisk czerwony
#define NIE BIT6&P4IN
// biały
#define NASTEPNE BIT7&P4IN
// zielony
#define RESET BIT4&P4IN
// żółty
void main(void)
{
WDTCTL=WDTPW+ WDTHOLD;
// zatrzymanie WDT
InitPortsLcd();
// inicjalizacja portów
InitLCD();
// inicjalizacja LCD
clearDisplay();
// czyszczenie LCD
int aktualne_punkty, poprzednie_punkty=0,
// definicje zmiennych kontrolnych
odpowiedz, czas=0, czas_caly=0;
wyswietl_napis(" TEST Z MSP430", 15, 1);
// wyświetlenie planszy startowej
wyswietl_napis("START - ZIELONY", 15, 2);
while(1)
// oczekiwanie na wciśnięcie zielonego przycisku
{
if((NASTEPNE)==0)
break;
}
while(1)
// start gry
{
aktualne_punkty=0;
// zerowanie licznika punktów
for(int i=0; i<PYTANIA; ++i)
// ta pętla wyświetla kolejne pytania
{
wyswietl_przesuwany_napis(pytania[i], 1);
// wyświetlamy pytanie
wyswietl_napis(" TAK NIE ?", 15, 2);
// wyświetlamy odpowiedzi
for(long j=0; ; ++j)
// ta pętla ogranicza czas wyświetlania jednego pytania do ok. 15 sekund
{
if(j%1500==0)
// jeśli czas zmienił się o pełną sekundę to wyświetlamy ten
{
SEND_CMD(DD_RAM_ADDR2);
wyswietlInt(j/1500);
++czas;
++czas_caly;
}
if(czas>15)
// czas przekroczył 15 sekund
{
czas=0;
break;
// przechodzimy do kolejnego pytania
}
if((TAK)==0)
// w kolejnych instrukcjach if-else sprawdzamy jaki klawisz został naciśnięty
odpowiedz=1;
else if((NIE)==0)
odpowiedz=0;
else if((NASTEPNE)==0)
odpowiedz=2;
else
continue ;
// jeżeli został naciśnięty inny klawisz lub nie został naciśnięty żaden - sprawdzamy ponownie
if(odpowiedz==1||odpowiedz==0)
// został naciśnięty pasujący klawisz
{
if(odpowiedzi[i]==odpowiedz)
// sprawdzamy czy odpowiedź jest poprawna i aktualizujemy stan
aktualne_punkty+=2;
else
--aktualne_punkty;
break;
}
else
{
if(odpowiedz==2)
// pomijamy pytanie
break;
}
}
}
clearDisplay();
wyswietl_przesuwany_napis("Twoje punkty: ", 1);
// wyświetlamy statystyki
SEND_CMD(DD_RAM_ADDR2);
wyswietlInt(aktualne_punkty);
for(int i=0; i<CZAS1; ++i)
Delayx100us(1000);
wyswietl_przesuwany_napis("Twoj czas:", 1);
SEND_CMD(DD_RAM_ADDR2);
wyswietlInt(czas_caly);
for(int i=0; i<CZAS1; ++i)
Delayx100us(1000);
wyswietl_przesuwany_napis("Poprzedni wynik:", 1);
SEND_CMD(DD_RAM_ADDR2);
wyswietlInt(poprzednie_punkty);
poprzednie_punkty=aktualne_punkty;
// ustawiamy stan punktów poprzedniego gracza na punkty gracza aktualnego
while(1)
// czekamy na naciśnięcie żółtego przycisku
if((RESET)==0)
break;
}
}