/*
* SpaceInvaders.c
*
* Created: 22.05.2014 16:26:08
* Author: Ehlers
*/
#define F_CPU 1000000UL
/* ---------------------------------------------- */
// Includes
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdlib.h>
#include "avrx.h"
#include "avrxfifo.h"
#include "avrx-io.h"
#include "avrx-signal.h"
#include "hardware.h"
#include "lcd.h"
/* ---------------------------------------------- */
// Definitionen
#define NUMBER_ALIEN_SPECIES 4
#define SYMBOL_SPACESHIP 1
#define SYMBOL_SPACESHIP_FIRE 2
#define SYMBOL_EXPLOSION 3
#define SYMBOL_MISS 4
/* ---------------------------------------------- */
// Globale Variablen
// Position des Aliens in der erste Reihe
uint8_t alienPosition;
// Spezies des Aliens
uint8_t alienSpecies = 5;
// Position des Raumschiffes in der zweiten Reihe
uint8_t spaceshipPosition = 0;
// Fehlschuesse
uint8_t spaceshipRemainingEnergie;
// Punkte des Raumschiffes
uint8_t spaceshipPoints = 0;
// Spielzeit
uint8_t playTime = 60;
// Variablen zur Spielsteuerung
uint8_t gameEnd = 1;
uint8_t fire = 0;
uint8_t alienBlocked = 0;
/* ---------------------------------------------- */
// semaphoreData: Mutual exclusion der globalen Variablen
AVRX_MUTEX(semaphoreData);
// semaphoreStart: Start des Spiels
AVRX_MUTEX(semaphoreStart);
/* ---------------------------------------------- */
// Deklaration der Timer (Ein Timer kann nicht in mehreren Prozessen/Tasks genutzt werden)
TimerControlBlock TickTimerClock;
TimerControlBlock TickTimerAlien;
TimerControlBlock TickTimerSpaceship;
TimerControlBlock TickTimerButton;
TimerControlBlock TickTimerLCD;
/* ---------------------------------------------- */
// Deklaration der Nachrichten und der MessageQueue
MessageControlBlock UpdateDisplay;
MessageQueue LcdQueue;
/* ---------------------------------------------- */
// AD-Wandlung
uint16_t adc_read()
{
// AD-Wandlung starten
ADCSRA |= (1<<ADSC);
// auf Abschluss der Konvertierung warten
while (ADCSRA & (1<<ADSC));
// Ergebnis auslesen, umrechnen in Bereich 0-14 und zurückgeben
return ADCW;
}
/* ---------------------------------------------- */
// Initialisierung des AD Wandlers
void adc_init()
{
// PINA0 als Eingang festlegen
DDRA &= ~1;
// Vcc als Referenzspannung
ADMUX = (1<<REFS0);
// Aktivieren und Pre-Scaler auf 128
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);
// Dummy-Wandlung
adc_read(0);
}
/* ---------------------------------------------- */
// Interrupt Handler für die Timer
AVRX_SIGINT(TIMER0_OVF_vect)
{
IntProlog();
TCNT0 = TCNT0_INIT;
AvrXTimerHandler();
Epilog();
}
/* ---------------------------------------------- */
// Initialisierungen
void init_function()
{
MCUCR = _BV(SE);
TCNT0 = TCNT0_INIT;
TCCR0 = TMC8_CK256;
TIMSK = _BV(TOIE0);
DDRA = DDRA & ~(1 << PINA1);
PORTA = PORTA | (1 << PINA1);
}
/* ---------------------------------------------- */
// Zeitmessung
AVRX_GCC_TASKDEF(taskClock, 30, 0)
{
while (1)
{
// warte eine Sekunde
AvrXDelay(&TickTimerClock, 1000);
// warte auf die Semaphore
AvrXWaitSemaphore(&semaphoreData);
// zaehle die Zeit um eine Sekunde runter
playTime--;
// falls die Zeit abgelaufen ist...
if (playTime == 0)
{
// setze das Spiel auf beendet
gameEnd = 1;
}
// das LCD aktualisieren
AvrXSendMessage(&LcdQueue, &UpdateDisplay);
// Semaphore freigeben
AvrXSetSemaphore(&semaphoreData);
}
}
/* ---------------------------------------------- */
// Steuerung des Aliens
AVRX_GCC_TASKDEF(taskAlien, 60, 0)
{
while (1)
{
// warte 1 Sekunde
AvrXDelay(&TickTimerAlien, 1000);
// warte auf die Semaphore
AvrXWaitSemaphore(&semaphoreData);
// die Alienposition zufaellig setzen
alienPosition = rand() % 14;
alienBlocked = 0;
// das LCD aktualisieren
AvrXSendMessage(&LcdQueue, &UpdateDisplay);
// Semaphore freigeben
AvrXSetSemaphore(&semaphoreData);
}
}
/* ---------------------------------------------- */
// Steuerung des Raumschiffes
AVRX_GCC_TASKDEF(taskSpaceship, 60, 0)
{
uint8_t oldPosition;
while (1)
{
// warte 20 Millisekunden
AvrXDelay(&TickTimerSpaceship, 20);
// warte auf die Semaphore
AvrXWaitSemaphore(&semaphoreData);
// die alte Position speichern
oldPosition = spaceshipPosition;
// die neue Position des Raumschiffes aus dem Wert des AD Wandlers errechnen:
// 1023 / 74 als Ganzzahl ist 13
spaceshipPosition = adc_read() / 74;
// das LCD nur aktualisieren, wenn sich die Position geaendert hat
if (oldPosition != spaceshipPosition)
{
// das LCD aktualisieren
AvrXSendMessage(&LcdQueue, &UpdateDisplay);
}
// Semaphore freigeben
AvrXSetSemaphore(&semaphoreData);
}
}
/* ---------------------------------------------- */
// Taster
AVRX_GCC_TASKDEF(taskButton, 60, 0)
{
uint8_t isPressed = 0;
while (1)
{
// warte 10 Millisekunden
AvrXDelay(&TickTimerButton, 10);
// falls der Taster gedrueckt ist...
if (!(PINA & (1 << PINA1)))
{
// ... und vorher noch nicht gedrueckt war
if (!isPressed)
{
// warte auf die Semaphore
AvrXWaitSemaphore(&semaphoreData);
// falls das Spiel beendet ist
if (gameEnd)
{
// starte das Spiel neu
gameEnd = 0;
playTime = 60;
spaceshipPoints = 0;
fire = 0;
spaceshipRemainingEnergie = 10;
// setze die Start-Semaphore
AvrXSetSemaphore(&semaphoreStart);
}
else
{
// wurde das Alien getroffen?
if (alienPosition == spaceshipPosition)
{
// der Schuss war ein Treffer
fire = 1;
// erhoehe die Punktzahl
spaceshipPoints++;
// es gibt 2 Energiepunkte - 1 verbrauchten Energiepunkt
spaceshipRemainingEnergie++;
// setze die neue Alienspezies auf einen zufaelligen Wert
alienSpecies = 5 + rand() % NUMBER_ALIEN_SPECIES;
}
else
{
// der Schuss ging daneben
fire = 2;
// ziehe einen Energiepunkt ab
spaceshipRemainingEnergie--;
// falls keine Energie mehr ueber ist
if (!spaceshipRemainingEnergie)
{
// das Spiel ist vorbei
gameEnd = 2;
}
}
}
// das LCD aktualisieren
AvrXSendMessage(&LcdQueue, &UpdateDisplay);
// Semaphore freigeben
AvrXSetSemaphore(&semaphoreData);
// speichern, dass der Button gedrueckt wird
isPressed = 1;
}
}
else
{
// der Taster ist nicht gedrueckt
isPressed = 0;
}
}
}
/* ---------------------------------------------- */
// LCD Ansteuerung
AVRX_GCC_TASKDEF(taskLcd, 100, 4)
{
MessageControlBlock *p;
while (1)
{
// Wait for messages in the message queue
p = AvrXWaitMessage(&LcdQueue);
// Is the message the one we are waiting for?
if (p == &UpdateDisplay)
{
if (gameEnd)
{
AvrXWaitSemaphore(&semaphoreData);
lcd_clear();
char buf[16];
switch (gameEnd)
{
case 1:
lcd_setcursor(3, 1);
snprintf(buf, 16, "Time Over!");
lcd_string(buf);
break;
case 2:
lcd_setcursor(2, 1);
snprintf(buf, 16, "No Ammo Left!");
lcd_string(buf);
break;
}
lcd_setcursor(3, 2);
snprintf(buf, 16, "Points: %u", spaceshipPoints);
lcd_string(buf);
AvrXDelay(&TickTimerLCD, 2000);
AvrXSetSemaphore(&semaphoreData);
AvrXWaitSemaphore(&semaphoreStart);
}
else
{
AvrXWaitSemaphore(&semaphoreData);
lcd_clear();
lcd_setcursor(14, 1);
char buf[3];
if (playTime < 10)
{
snprintf(buf, 3, "0%u", playTime);
}
else
{
snprintf(buf, 3, "%u", playTime);
}
lcd_string(buf);
lcd_setcursor(14, 2);
if (spaceshipRemainingEnergie < 10)
{
snprintf(buf, 3, "0%u", spaceshipRemainingEnergie);
}
else
{
snprintf(buf, 3, "%u", spaceshipRemainingEnergie);
}
lcd_string(buf);
if (fire)
{
lcd_setcursor(spaceshipPosition, 2);
lcd_data(SYMBOL_SPACESHIP_FIRE);
if (fire == 1)
{
lcd_setcursor(alienPosition, 1);
lcd_data(SYMBOL_EXPLOSION);
alienBlocked = 1;
}
else
{
lcd_setcursor(alienPosition, 1);
lcd_data(alienSpecies);
lcd_setcursor(spaceshipPosition, 1);
lcd_data(SYMBOL_MISS);
}
AvrXDelay(&TickTimerLCD, 250);
fire = 0;
}
else
{
if (!alienBlocked)
{
lcd_setcursor(alienPosition, 1);
lcd_data(alienSpecies);
}
lcd_setcursor(spaceshipPosition, 2);
lcd_data(SYMBOL_SPACESHIP);
}
AvrXSetSemaphore(&semaphoreData);
}
}
}
}
/* ---------------------------------------------- */
int main(void)
{
// Initialisierungen
// Definition des Stack Pointers der main
AvrXSetKernelStack(0);
// Initialisierung von AvrX
init_function();
// Initialisierung der LEDs
lcd_init();
// Initialisierung des ADW
adc_init();
// Definition und Initialisierung des Raumschiffes und der Aliens
uint8_t symbols[] = {0x00, 0x00, 0x00, 0x04, 0x0A, 0x0A, 0x1B, 0x15,
0x04, 0x04, 0x0A, 0x04, 0x0A, 0x0A, 0x1B, 0x15,
0x0A, 0x00, 0x11, 0x0A, 0x0A, 0x11, 0x00, 0x0A,
0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x15, 0x1B, 0x0E, 0x1B,
0x00, 0x11, 0x1F, 0x15, 0x1F, 0x0A, 0x0E, 0x0A,
0x00, 0x00, 0x0E, 0x15, 0x0E, 0x04, 0x0A, 0x11,
// Die folgende Zeile ersetzen, um den eigenen Alien zu erstellen
0x04, 0x0A, 0x0E, 0x11, 0x0A, 0x04, 0x0A, 0x11};
for(int i = 0; i < 8; i++)
{
lcd_generatechar(i+1, symbols+(i*8));
}
// Starten des Tasks
AvrXRunTask(TCB(taskClock));
AvrXRunTask(TCB(taskAlien));
AvrXRunTask(TCB(taskSpaceship));
AvrXRunTask(TCB(taskButton));
AvrXRunTask(TCB(taskLcd));
// Setzen der Semaphore
AvrXSetSemaphore(&semaphoreData);
// Aufrufen des Schedulers
Epilog();
while(1);
}
Comments