Space Invaders Design Guide
Space Invaders Design Guide
Design Documentation
4/22/13
Table of Contents
1 Introduction................................................................................................................ 3
2 Scope........................................................................................................................ 3
3 Design Overview....................................................................................................... 3
3.1 Requirements................................................................................................... 3
3.2 Dependencies................................................................................................... 4
3.3 Theory of operation........................................................................................... 4
4 Design Details............................................................................................................ 4
4.1 Hardware Design.............................................................................................. 4
4.2 Software Design............................................................................................... 6
4.2.1 Power or Reset State.................................................................................... 8
4.2.2 Creation State................................................................................................ 11
4.2.3 Update States................................................................................................ 12
4.2.4 Check Collisions State................................................................................... 13
4.2.5 Check for Win State....................................................................................... 14
4.2.6 Player Win / Lose State................................................................................. 14
5 Testing....................................................................................................................... 14
5.1 One Player and Four Alien Missiles at a Time.................................................. 14
5.2 Varying Levels of Difficulty................................................................................ 16
5.3 Sounds.............................................................................................................. 17
5.4 Saving the Highscore........................................................................................ 18
6 Conclusion................................................................................................................. 19
7 Appendix A................................................................................................................. 20
8 Appendix B................................................................................................................. 22
1 Introduction
This document explains the design for Space Invaders. In Space Invaders the objective is to
destroy incoming aliens. The player is a small spaceship located at the bottom of the screen.
The player must fire missiles at the incoming aliens while avoiding the enemy alien missiles that
are fired back. If the player gets hit too many times, the player loses. If the player successfully
destroys all the incoming alien ships, then the player is victorious.
2 Scope
This document describes the design for Space Invaders. It contains the requirements,
dependencies and theory of operation. Schematics, diagrams, and code are used to
demonstrate the architecture described. A list of protocol used to test functionality of the Space
Invaders are explained. The mechanical description of the Space Invaders is not included.
3 Design Overview
3.1 Requirements
The given requirements for the Space Invaders are the following:
1 The game shall be tested on the STM32F103ZE Waveshare board and shall use the
following components: LCD Display, Left/Right/Fire buttons, DAC, DIP switches and an
external speaker.
2 The player shall only be able to have one missile on the screen at a given time.
3 There shall be no more than 4 alien missiles on the screen at a given time.
4 A random alien shall fire the missile.
5 There shall be varying levels of difficulty. This shall be implemented by allowing the
player to determine the number of aliens and the speed at which they advance toward
the player.
6 Sound shall be implemented in the game. The DAC will output to the external speaker
when one of the following events occur:
a The player fires a missile.
b An enemy alien fires a missile.
c The player is hit by an alien missile.
7 The high score shall be saved and displayed when the player wins or loses the game.
3.2 Dependencies
The dependencies of the Space Invaders are:
● 5.0 Volt DC Power Supply (for Microcontroller and external speaker power)
● 3.3 Volt DC Power Supply (can be drawn from Microcontroller or external source)
● 8 MHz Crystal Oscillator
The player’s objective is to kill all the aliens. The player is armed with a cannon, and can fire
one missile at a time. If he is able to destroy all the aliens without taking too much damage (the
player can take five hits) then the player wins. The player must adroitly maneuver his ship using
the move left and move right buttons and shoot missiles with his/her fire button until all the
aliens are destroyed.
4 Design Details
GPIO A 0, 1, 2, 3, 4, 5, 6, 7, 8, 10
GPIO C 0, 1, 2, 3, 6, 7
Table 1: GPIO Pin Selection
Table 2 below describes which pins are being used for which purpose.
This game utilizes the 16-bit timer found at Timer 2’s address. This timer is initialized to count
down from a set value until it reaches zero, then reset the count value and start all over again.
In this state, the timer is initialized and also started. The value initialized with this timer is
0xFFFF. This is important because it is used when choosing the random alien from which the
missile fires. Since 0xFFFF much larger than 50, we use this timer to generate a random Alien
to fire a missile when needed. This count down value is polled in further states in order to
generate a random time value. Every time it expires the Gameplay Loop can repeat. If the
Gameplay Loop executes once before the timer is finished counting down, it must wait until the
timer expires before repeating its execution. This allows the game to never become too fast for
the player to respond.
The code snippet shown below describes the setup and where the hardware is mapped to on
the Waveshare board.
//Setup LCD
LCD_Initialization();
//initialize variables
update = false;
count = 0;
increment = 0;
erasecounter = 0;
numAliens = 0;
m1.enable = 0;
missileEraseCounter = 0;
alienMissileCounter = 0;
for (i = 0; i < 4; i++)
{ missileArray[i].enable = 0;
}
for (i = 0; i < 50; i++)
{ alienArray[i].enable = 0 ;
}
dacIndex = 0;
//Configure timer
RCC->APB1ENR |= 0x20000007; //timer 2, timer 3, timer 4 enable
//Enable GPIO
RCC->APB2ENR |= 0x3D; // enables AFIO and clocks on D
//Setup DAC
//Dac Out 2 is mapped to PA5
//Configure timer
RCC->APB1ENR |= 0x20000007; //timer 2, timer 3, timer 4 enable
TIM4->CR1 = 0x10; // direction down and enable and one-pulse
soundSpeed = 0x1111;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
This code is referenced in more detail in Appendix C and can be used to setup and initialize the
appropriate registers in order for this program to function correctly.
Since this is the return function once the game has finished, there are a few variables that need
to be reset. These deal with creating the new objects, resetting the player’s lives and reading in
the difficulty settings. Once these are reset, the game continues in the Creation State.
The Creation State assigns key characteristics that are parts of each object. The code snippet
below shows the different objects and their composition.
//data structures
struct alien{
unsigned short x, y;
unsigned char enable, direction, type, nextmove;
};
struct player{
unsigned short x, y;
unsigned char lives, enable;
unsigned int score;
};
struct missile{
unsigned short x, y;
unsigned char enable, direction, posture, type;
};
struct barrierpiece{
unsigned short x, y;
unsigned short lives;
unsigned char enable;
};
struct barrier{
unsigned short x, y;
struct barrierpiece pieces[24];
unsigned char totalEnable;
//pieces 0, 5, 19-22 must be black
};
To create the Aliens, a predefined array is used. A for loop iteratively steps through the array
where the direction, enable, type, location (x and y), and animation are all set. Also, a variable
numAliens is incremented every time an Alien is created. By holding the number of Aliens alive
at a given time it is easy to see if the player won the game by checking if numAliens is zero.
To create the Player, he/she is initialized to the bottom center of the screen and given 5 lives
and a current score of 0. The score changes value every time an alien is shot.
To create the Barriers is a less straightforward than the other objects. This is due to the fact
that the Barriers are really a grouping of Barrier Pieces that each have their own properties.
Essentially the location, enabling, and lives of each piece are set within a set of nested for
loops. The outer loop iterates through the Barriers and the inner loop steps through each
Barrier Piece that makes up a barrier.
Each barrier piece is given 3 lives. The color of the barrier piece is indicative of the number of
lives remaining on that piece. Once the barrier piece is red if it is hit again it is destroyed.
Player Update
First the player move is checked. The player move left and move right buttons have interrupts
so that when pressed the player can move immediately. However, once the button is pressed it
is polled until it is released so that the player can hold the button down to continuously move
across the screen.
Whenever the player is either hit or moves the player is redrawn. When hit the player loses 1
life and changes color. When the player moves the color does not change but rather the
location of the player is changed and the player is redrawn at the new location of the player.
Barrier Update
Each Barrier Piece is redrawn according to the number of lives it has remaining. If it has 3 lives,
it is drawn green. If it has 2 lives, it is drawn yellow. If it has 1 life it is drawn red. If it has no
lives remaining it is not drawn.
Alien Update
Afterward the Aliens are updated. The Aliens are updated in rows. This way the Alien rows
move together but independently from other rows. The Aliens move horizontally across the
screen at a rate of 2 pixels per update until they hit the side wall. When this bounds checking
occurs it updates each Alien within the row to move horizontally in the opposite direction after
executing a jump (an advance toward the player set as Jump Speed).
Missile Update
First the Player’s missile is updated. It moves at a rate of 8 pixels upward per update. If the
missile fails to hit an Alien and reaches the top of the screen a check is made to disable and
erase the missile.
Next the Aliens’ missiles are updated. It does this by stepping through a 4 element array of
Alien missiles (missileArray[4] in the code) and moving each one by 8 pixels per update.
If there are less than 4 Alien missiles on the screen then a new missile is created. To find which
alien should fire the missile a value is read in from timer 2. Since this is a value between 0 and
65535 it is changed to a number between 0 and the number of aliens by the modulo operation.
Score Update
The score at the top of the screen is updated with the current player score. Every time an Alien
is killed, the Player’s score is increased depending on which row the Alien resides. In this state,
the score is updated with the current score located in p1’s struct.
The second check is if the X coordinate is larger than 290 pixels. If the missile is in this range, it
has the chance to hit the Player. The Y coordinates of the Player is retrieved and compared to
the missile’s Y coordinates. If it is determined that the missile is hitting the Player, a life is
subtracted from the Player. Once the life is subtracted, a check is run on the player’s life count.
If the count is zero, the Player Lose state is triggered and the game is ended. If the count is
greater than zero, the next missile is then checked to see whether it falls in one of these two
checks.
The final portion of this state is to check if the Player’s missile has hit an alien or one of the
barriers. The X coordinate of the lowest row of Aliens is determined and used as the lower
bound to the check. If the Player’s missile is in the X range necessary for a collision with an
Alien, this state jumps into checking each individual Alien. Every Alien’s X and Y coordinates
are checked to see if the missile is going to hit that Alien. If it is within an Alien’s range and the
Alien is enabled, the Alien is disabled and determined “dead”. The Player’s score is then
updated with the value for that Alien. If the missile is within an Alien’s boundary and the Alien is
disabled then the state moves on.
5 Testing
Design testing was implemented in several forms. Code demonstrations were used in
demonstrating the requirements as outlined in the project proposal.
//missile button
void EXTI3_IRQHandler()
{
//enable missile sound
if (m1.enable == 0)
{ soundSpeed = 0x1111;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
dacIndex = 0;
}
EXTI->PR |= 0x4; // acknowledge interrupt
//enable player (only used after a win or lose)
p1.enable = 1;
//check if already on screen
if (m1.enable)
return;
m1.enable = 1; // turn on missile flag
m1.direction = 1;
m1.posture = 0;
m1.type = 0;
m1.x = p1.x - 8; // sets coordinates of missile
m1.y = p1.y - 6;
}
On line 15 of the above code, a check is made to see if m1 (the Player’s missile) is active. If it
is, then the function returns. If the player’s missile is not active, it plays a sound indicating that a
missile has been fired, activate the missile, and initialize the X and Y coordinates based on
where the Player happens to be at the time of the interrupt.
The Aliens have the ability to fire up to four missiles simultaneously. This is limited in code as
shown below:
void createAlienMissile(void)
{
int k;
for (k = 0; k < 4; k++)
{
if (missileArray[k].enable == 0)
{
randomVal = (TIM2->CNT) % origAliensNum;
soundSpeed = 0x3333;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
dacIndex = 0;
while(alienArray[randomVal].enable == 0)
randomVal = (TIM2->CNT) % origAliensNum;
missileArray[k].x = alienArray[randomVal].x + 25;
missileArray[k].y = alienArray[randomVal].y - 8;
missileArray[k].type = (randomVal % 2) + 1; // creates type
missileArray[k].posture = 0; // animation
missileArray[k].direction =0 ; // down
missileArray[k].enable = 1;// enabled
return;
}
}
}
Every time the running loop runs, it calls the above function. In this function, it loops through the
Alien missile array (missileArray[4]) and determines if any of the missiles have been disabled
during the checkCollisions() function. If a missile has been disabled, the above function
randomly picks one Alien using Timer 2’s count. If that Alien is disabled, it polls the value of
Timer 2 again and picks another Alien. Once an Alien has been selected, the missile’s
coordinates and properties are set and the missile is enabled. Figure 5 shows 4 active Alien
missiles and 1 Player missile.
Figure 5: Missiles
The other way in which the difficulty can be adjusted is through setting the Jump Speed DIP
switch on. This makes the Aliens move at a vertical rate of 4 pixels per jump instead of 2.
Therefore the Aliens arrive at the domain of the player in half the time then if the Jump Speed
were switched off. The code snippets below show where the DIP switches vary the difficulty.
//find difficulty
getval = GPIOA->IDR;
getval = getval & 0x0500;
getval >>= 8;
//gets speed
getval = GPIOC->IDR;
getval = getval & 0x100; //get pin 8
getval >>= 8;
5.3 Sounds
Sound is implemented using the Open103R ADC/DAC development module. As was
mentioned before, the DAC pin are mapped to Port A pin 5’s pin. In addition to this DAC line,
which connects to the AIN line on the module, a 5V, 3.3V, and ground line are necessary to
power the external module. Figure 6 shows the DAC module setup that was implemented using
the STM32F103ZE.
Figure 6: ADC / DAC Module
void TIM4_IRQHandler()
{
TIM4->SR &= 0xFFFE; // acknowledge interrupt
As outlined in the above interrupt handler, Timer 4 was used to control the DAC output. Using
the 40-part sine array pre-defined, Timer 4’s countdown value is used to control the pitch of the
output. This module is setup so that it outputs three tones. There is a tone for an Alien missile,
one for the Player missile, and another for when either an Alien or Player missile contacts a
barrier or their target.
FLASH->AR = 0x0807F800;
FLASH->CR |= 0x40;
vFlashWrite(0x0807F800, score);
i = i;
}
6 Conclusion
The design described in this document provides a functional prototype of the Space Invaders as
setup on the ARM Cortex M3 STM32103ZE microcontroller. A total of six timing and
functionality tests occurred to satisfy all 7 requirements. The tests all passed, showing that this
design of the Space Invaders works as expected on the prototype setup.
This prototype fulfills the basic requirements to implement the Space Invaders game. There are
some potential optimization opportunities that are available to improve the overall gameplay.
There are several interrupts that dictate the precedence of the possible events. In addition,
there are also some potential things that could be simplified in the basic operation of the game.
Regardless of the situation, every state is called during the loop. It isn’t until the state is called
that conditions are checked, such as Player missile activation or having Aliens disabled during
collision detection. It could speed the game up and provide more stability if update functions
are called only under certain conditions, moving everything into the main program loop versus
branched loops.
There are certain aspects of this game that were simplified greatly by developing a spreadsheet
that was used to produce color mapping tables. Rather than produce complicated methods of
writing out the Alien models to the screen, the Aliens were converted to color tables and when
writing out to the screen, the tables are just ran through from start to finish. This avoids the
need to have a complicated pixel-skip algorithm.
One of the biggest difficulties that was faced on this project was understanding how to write out
the high score to a page file in flash memory. This process is outlined in Section 5.4 above.
After understanding how the flash memory worked, it opens up some additional possibilities,
such as storing an array of high scores, player initials, and more. This would make this game
more like the original Arcade game.
7 Appendix A
Shown below is the detailed schematic of the Space Invaders setup on the ARM Cortex M3
STM32103ZE microcontroller.
8 Appendix B
Below is the code for implementing the Space Invaders game using the STM32F103ZE
microcontroller.
//main.c
#include "stm32f10x.h"
#include "LCD.h"
#include "stdbool.h"
#include "pixelmaps.c"
//global variables
volatile int erasecounter, missileEraseCounter, alienMissileCounter, randomVal, soundSpeed;
volatile bool update;
volatile unsigned char count, increment, numAliens, jumpSpeed;
volatile struct alien alienArray[50];
volatile struct missile missileArray[4];
volatile struct barrier barrierArray[4];
volatile struct player p1;
volatile struct missile m1;
volatile unsigned short highScores;
volatile unsigned int scoreLocation __attribute__((at(0x0807F800)));
volatile unsigned int flashACR __attribute__((at(0x40022000)));
volatile unsigned int flashKEYR __attribute__((at(0x40022004)));
volatile unsigned int flashOPTKEYR __attribute__((at(0x40022008)));
volatile unsigned int flashSR __attribute__((at(0x4002200C)));
volatile unsigned int flashCR __attribute__((at(0x40022010)));
volatile unsigned int flashAR __attribute__((at(0x40022014)));
volatile unsigned int flashOBR __attribute__((at(0x4002201C)));
volatile unsigned int flashWRPR __attribute__((at(0x40022020)));
unsigned short color_map[] = {White, Black, Blue, Red, Magenta, Yellow, Green};
volatile int origAliensNum, dacIndex;
//volatile int key1 = 0x45670123;
//volatile int key2 = 0xCDEF89AB;
volatile int rdprt = 0x00A5;
//data structures
struct alien{
unsigned short x, y;
unsigned char enable, direction, type, nextmove;
};
struct player{
unsigned short x, y;
unsigned char lives, enable;
unsigned int score;
};
struct missile{
unsigned short x, y;
unsigned char enable, direction, posture, type;
};
struct barrierpiece{
unsigned short x, y;
unsigned short lives;
unsigned char enable;
};
struct barrier{
unsigned short x, y;
struct barrierpiece pieces[24];
unsigned char totalEnable;
//pieces 0, 5, 19-22 must be black
};
//prototypes
void writeHighScore(short);
int retrieveHighScore(void);
void updateAliens(int);
void drawAlien(struct alien, char);
void drawMissiles(struct missile, char);
void updateMissiles(void);
void checkCollisions(void);
void createAliens(void);
void playerWin(void);
void playerLose(void);
void createPlayer(void);
void drawPlayer(char);
void drawScore(void);
void createBarriers(void);
void updateBarriers(void);
void drawBarriers(struct barrier, char);
void drawBarrierPiece(struct barrierpiece, char);
void createAlienMissile(void);
void updateHighScore(void);
void SystemInit(void)
{
int i;
LCD_Initialization();
//initialize variables
update = false;
count = 0;
increment = 0;
erasecounter = 0;
numAliens = 0;
m1.enable = 0;
missileEraseCounter = 0;
alienMissileCounter = 0;
for (i = 0; i < 4; i++)
{ missileArray[i].enable = 0;
}
for (i = 0; i < 50; i++)
{ alienArray[i].enable = 0 ;
}
dacIndex = 0;
//Configure timer
RCC->APB1ENR |= 0x20000007; //timer 2, timer 3, timer 4 enable
TIM2->ARR = 0xFFFF; // 2ms / (1 / 8Mhz) => .002 / 0.000000125 = 16000 => 0x3E80 - 1 =
3E7F
TIM2->CNT = 0xFFFF;
TIM2->CR1 = 0x10; // direction down and enable and one-pulse
//Enable GPIO
RCC->APB2ENR |= 0x3D; // enables AFIO and clocks on D
//Setup DAC
//Dac Out 2 is mapped to PA5
//Configure timer
RCC->APB1ENR |= 0x20000007; //timer 2, timer 3, timer 4 enable
TIM4->CR1 = 0x10; // direction down and enable and one-pulse
soundSpeed = 0x1111;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
void reset(void)
{
int i;
volatile short getval, difficulty;
//initialize variables
update = false;
count = 0;
increment = 0;
erasecounter = 0;
numAliens = 0;
m1.enable = 0;
missileEraseCounter = 0;
alienMissileCounter = 0;
for (i = 0; i < 4; i++)
{ missileArray[i].enable = 0;
}
for (i = 0; i < 50; i++)
{ alienArray[i].enable = 0 ;
}
difficulty = 0;
//find difficulty
getval = GPIOA->IDR;
getval = getval & 0x0500;
getval >>= 8;
//gets speed
getval = GPIOC->IDR;
getval = getval & 0x100; //get pin 8
getval >>= 8;
createAliens();
createPlayer();
drawPlayer(0);
createBarriers();
//This timer is used so that the screen is only updated when timer 2 expires (setting update flag true)
//This way the game doesn't move impossibly fast when there are only a few objects to draw on the screen.
void TIM2_IRQHandler()
{
TIM2->SR &= 0xFFFE; // acknowledge interrupt
if (increment == 1)
{
update = true; // set update flag
increment = 0;
}
else
increment++;
}
int main()
{
int i;
volatile short getval, difficulty;
difficulty = 0;
//find difficulty
getval = GPIOA->IDR;
getval = getval & 0x0500;
getval >>= 8;
//gets speed
getval = GPIOC->IDR;
getval = getval & 0x100; //get pin 8
getval >>= 8;
createAliens();
createPlayer();
drawPlayer(0);
createBarriers();
while(1)
{
TIM2->CR1 |= 1; //A way to speed it up and slow it down
if (update)
{
}
}
}
FLASH->AR = 0x0807F800;
FLASH->CR |= 0x40;
vFlashWrite(0x0807F800, score);
//FLASH->AR = 0x0807F800;
//FLASH->OBR |= (score<<10);
//scoreLocation = score;
i = i;
}
int retrieveHighScore(void)
{ short val;
val = scoreLocation;
return val;
}
void createAliens(void)
{
int i, j;
j = 2;
// creates the aliens
for(i=0; i<origAliensNum; i++)
{
if (i % 10 == 0)
{ j++;
j = j % 3;
}
alienArray[i].direction = 0;
alienArray[i].enable = 1;
alienArray[i].type = j;
alienArray[i].x = 40+ 20*(i / 10);
alienArray[i].y = 40+(i % 10)*20;
alienArray[i].nextmove = 0;
numAliens += 1;
}
for (z=0; z<21; z++) // this max must be 1 greater than erasecounter
{
if (erasecounter == 20)
{
x = ain.x;
y = ain.y;
type = ain.type;
dir = ain.direction;
k = 0;
//draws aliens
for (i=x; i < x+10; i++)
{
LCD_SetCursor(i,y);
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( 0x0022 );
for(j=y; j < y+16; j++ )
{
if (erase)
{ LCD_WriteData( Black );
}
else
{ LCD_WriteData(color_map[colorMap[k]]);
}
k++;
}
GPIOC->BSRR = CS_low_Pin;
}
}
void createAlienMissile(void)
{
int k;
for (k = 0; k < 4; k++)
{
if (missileArray[k].enable == 0)
{
randomVal = (TIM2->CNT) % origAliensNum;
soundSpeed = 0x3333;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
dacIndex = 0;
while(alienArray[randomVal].enable == 0)
randomVal = (TIM2->CNT) % origAliensNum;
missileArray[k].x = alienArray[randomVal].x + 25;
missileArray[k].y = alienArray[randomVal].y - 8;
missileArray[k].type = (randomVal % 2) + 1; // creates type
missileArray[k].posture = 0; // animation
missileArray[k].direction =0 ; // down
missileArray[k].enable = 1;// enabled
return;
}
}
}
void updateMissiles(void)
{
int j,k;
if (missileEraseCounter == 9)
{
drawMissiles(m1, 1); // erase current missile
m1.x -= 8; // set new
coordinate for missile
missileEraseCounter = 0;
}
else
missileEraseCounter++;
}
}
if (missileArray[k].enable)
drawMissiles(missileArray[k], 0); //draw missile
alienMissileCounter = 0;
}
}
}
else
alienMissileCounter++;
}
updateBarriers();
k = 0;
//draw missile
for (i=x; i < x+8; i++)
{
LCD_SetCursor(i,y);
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( 0x0022 );
for(j=y; j < y+5; j++ )
{
if (erase)
{ LCD_WriteData( Black );
}
else
{ LCD_WriteData(color_map[mColorMap[k]]);
}
k++;
}
GPIOC->BSRR = CS_low_Pin;
}
}
void checkCollisions(void)
{
int i, j, k;
barrierArray[j].pieces[k].lives -= 1;
goto SkipToNext;
}
}
}
}
}
soundSpeed = 0x2222;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
dacIndex = 0;
drawPlayer(0);
if (p1.lives == 0)
playerLose();
else
goto SkipToNext;
}
}
SkipToNext: continue;
}
barrierArray[j].pieces[i-1].lives -= 1;
return;
}
}
}
soundSpeed = 0x2222;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
dacIndex = 0;
return;
}
}
}
void playerWin(void)
{
p1.enable = 0; //disable player
highScores = retrieveHighScore();
if (p1.score > highScores)
{
highScores = p1.score; //set highscores
writeHighScore(p1.score);
}
updateHighScore(); //draw to the screen
while(p1.enable != 1); //wait for button press
//reset
reset();
}
void playerLose(void)
{
p1.enable = 0; //disable player
highScores = retrieveHighScore();
if (p1.score > highScores)
{
highScores = p1.score; //set highscores
writeHighScore(p1.score);
}
updateHighScore(); //draw to screen
while(p1.enable != 1);
//reset
reset();
}
void createPlayer(void)
{
p1.enable = 1; //enable player
p1.x = 300; //set player location
p1.y = 128;
p1.lives = 5; //set amount of lives
p1.score = 0; //reset score
k = 0;
//draw player
for (i=x; i < x+10; i++)
{
LCD_SetCursor(i,y);
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( 0x0022 );
for(j=y; j < y+16; j++ )
{
if (erase || p1.lives == 0)
{ LCD_WriteData( Black );
}
else
{ if(player1[k] == 1)
LCD_WriteData(color_map[player1[k]]);
else
k = 0;
//draw digits
x = 30;
y = 50;
for(k = 0; k < 4; k++)
{
switch (dig[k])
{
case 0: for (m = 0; m < 28; m++) //load map for number 0
{ digit_map[m] = zero[m];
}
break;
case 1: for (m = 0; m < 28; m++) //load map for number 1
{ digit_map[m] = one[m];
}
break;
case 2: for (m = 0; m < 28; m++) //load map for number 2
{ digit_map[m] = two[m];
}
break;
case 3: for (m = 0; m < 28; m++) //load map for number 3
{ digit_map[m] = three[m];
}
break;
case 4: for (m = 0; m < 28; m++) //load map for number 4
{ digit_map[m] = four[m];
}
break;
case 5: for (m = 0; m < 28; m++) //load map for number 5
{ digit_map[m] = five[m];
}
break;
case 6: for (m = 0; m < 28; m++) //load map for number 6
{ digit_map[m] = six[m];
}
break;
case 7: for (m = 0; m < 28; m++) //load map for number 7
{ digit_map[m] = seven[m];
}
break;
case 8: for (m = 0; m < 28; m++) //load map for number 8
{ digit_map[m] = eight[m];
}
break;
case 9: for (m = 0; m < 28; m++) //load map for number 9
{ digit_map[m] = nine[m];
}
break;
}
m = 0;
for (i=x; i < x+7; i++)
{
LCD_SetCursor(i,y +(k*5));
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( 0x0022 );
for(j=y; j < y+4; j++ )
{
LCD_WriteData(color_map[digit_map[m]]);
m++;
}
GPIOC->BSRR = CS_low_Pin;
}
}
}
void createBarriers(void)
{
int i, j, newRow;
newRow = 0;
for (i = 0; i < 4; i++) //each barrier
{ barrierArray[i].x = 270;
barrierArray[i].y = 36 + (i*50);
barrierArray[i].totalEnable = 1;
for (j = 0; j < 24; j++) //each barrier piece
{ newRow = j / 6;
if( (j!=0) && (j!=5) && (j!=19) && (j!=20) && (j!=21) && (j!=22))
{ barrierArray[i].pieces[j].enable = 1;
barrierArray[i].pieces[j].lives = 3;
barrierArray[i].pieces[j].x = barrierArray[i].x + (newRow * 4);
barrierArray[i].pieces[j].y = barrierArray[i].y + ((4*j) % 24);
}
}
}
}
void updateBarriers(void)
{
int i;
for(i = 0; i < 4; i++)
drawBarriers(barrierArray[i], 0);
}
void drawBarriers(struct barrier b, char erase)
{
int i;
for (i = 0; i < 24; i++)
{ if (b.pieces[i].enable)
{ drawBarrierPiece(b.pieces[i], erase);
}
}
}
x = bp.x;
y = bp.y;
}
else if (bp.lives == 3)
{ LCD_WriteData( Green );
}
else if (bp.lives == 2)
{ LCD_WriteData( Yellow );
}
else if (bp.lives == 1)
{ LCD_WriteData( Red );
}
}
GPIOC->BSRR = CS_low_Pin;
}
}
// move left
void EXTI0_IRQHandler()
{
int checkDir;
checkDir = GPIOA->IDR & 0x0002; // find which button is pressed
return;
}
// move right
void EXTI1_IRQHandler()
{
int checkDir;
checkDir = GPIOA->IDR & 0x0001; // find which button is pressed
return;
}
//missile button
void EXTI3_IRQHandler()
{
//enable missile sound
if (m1.enable == 0)
{ soundSpeed = 0x1111;
TIM4->CNT = soundSpeed;
TIM4->ARR = soundSpeed;
dacIndex = 0;
}
void TIM4_IRQHandler()
{
TIM4->SR &= 0xFFFE; // acknowledge interrupt
void updateHighScore(void)
{
unsigned char digit_map[28];
int x, y, i, j, k, m, dig[4];
LCD_Clear( Black );
//draw digits
x = 150;
y = 125;
for(k = 0; k < 4; k++)
{
switch (dig[k])
{
case 0: for (m = 0; m < 28; m++) //load map for number 0
{ digit_map[m] = zero[m];
}
break;
case 1: for (m = 0; m < 28; m++) //load map for number 1
{ digit_map[m] = one[m];
}
break;
case 2: for (m = 0; m < 28; m++) //load map for number 2
{ digit_map[m] = two[m];
}
break;
case 3: for (m = 0; m < 28; m++) //load map for number 3
{ digit_map[m] = three[m];
}
break;
case 4: for (m = 0; m < 28; m++) //load map for number 4
{ digit_map[m] = four[m];
}
break;
case 5: for (m = 0; m < 28; m++) //load map for number 5
{ digit_map[m] = five[m];
}
break;
case 6: for (m = 0; m < 28; m++) //load map for number 6
{ digit_map[m] = six[m];
}
break;
case 7: for (m = 0; m < 28; m++) //load map for number 7
{ digit_map[m] = seven[m];
}
break;
case 8: for (m = 0; m < 28; m++) //load map for number 8
{ digit_map[m] = eight[m];
}
break;
case 9: for (m = 0; m < 28; m++) //load map for number 9
{ digit_map[m] = nine[m];
}
break;
}
m = 0;
for (i=x; i < x+7; i++)
{
LCD_SetCursor(i,y +(k*5));
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( 0x0022 );
for(j=y; j < y+4; j++ )
{
LCD_WriteData(color_map[digit_map[m]]);
m++;
}
GPIOC->BSRR = CS_low_Pin;
}
}
}
//
// lcd.c
//
#include "stm32f10x.h"
#include "lcd.h"
GPIOC->BSRR = CS_low_Pin;
}
void LCD_Initialization(void)
{
unsigned int config_temp;
LCD_Config();
delay_ms(50);
}
LCD_SetCursor(0,0);
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( 0x0022 );
for( i=0; i< MAX_X*MAX_Y; i++ )
LCD_WriteData( Color );
GPIOC->BSRR = CS_low_Pin;
}
// Write a command
void LCD_WriteIndex( unsigned short index )
{ //look at Chapter 13 to figure out how long
//each pin should be high/low... create the ns Delay loop
GPIOC->BRR = DC_Pin; // Set Command
GPIOC->BSRR = RD_low_Pin; // set Rd pin high (why?)
//delay_ms(0);
GPIOB->ODR = index; // write out data
//delay_ms(0);
GPIOC->BRR = WR_low_Pin; // set WR pin low then high
GPIOC->BSRR = WR_low_Pin;
// Write data
void LCD_WriteData( unsigned short data )
{
//
void LCD_WriteReg( unsigned short LCD_Reg, unsigned short LCD_RegValue )
{
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( LCD_Reg );
LCD_WriteData( LCD_RegValue );
GPIOC->BSRR = CS_low_Pin;
}
GPIOC->BSRR = DC_Pin;
GPIOC->BSRR = WR_low_Pin;
GPIOC->BRR = RD_low_Pin;
data = GPIOB->IDR;
//data = GPIOB->IDR; // we added this
GPIOC->BSRR = RD_low_Pin;
return data;
}
GPIOC->BRR = CS_low_Pin;
LCD_WriteIndex( LCD_Reg );
data = LCD_ReadData();
GPIOC->BSRR = CS_low_Pin;
return data;
}
y = (MAX_Y-1) - y;
swap_temp = y;
y = x;
x = swap_temp;
y = (MAX_Y-1) - y;
#endif
LCD_WriteReg( 0x004E, x );
LCD_WriteReg( 0x004F, y );
}