I stumbled over https://www.hackster.io/dhruvw221/arduino-video-game-with-vga-video-output-789f9f where one used the interrupts of an Arduino Uno to drive a VGA display. The resulting 40x30 pixel display even fits into the small dynamic memory of the processor.
I enhanced the project and added a self playing mode. Now you can watch your Arduino play against itself. I even added some text output. Unfortunately there isn't enough dynamic memory for double buffering.
Enjoy.
One side of a VGA cable was cutted and connected to the 4 pins.
/**
* Pink, Ligh Blue, White (thick core): RGB Pin 2
* Green (thick core): H-Sync Pin 3
* Blue (thin core): V-Sync Pin 10
*
*
*/
#define nop __asm__("nop \n")
#define SCREEN_W 40
#define SCREEN_H 30
char PXL_DATA [SCREEN_H][SCREEN_W];
int lineCounter;
char skipLine;
char PXL_OUT;
char colP1 = (SCREEN_W-3) / 2;
char dirP1 = 1;
char rowP1 = 27;
char colP2 = (SCREEN_W-3) / 2;
char dirP2 = -1;
char rowP2 = 3;
bool bulletP1Fired = 0;
char bulletP1X = 0;
char bulletP1Y = 0;
char dyP1;
bool bulletP2Fired = 0;
char bulletP2X = 0;
char bulletP2Y = 0;
char dyP2;
char scoreP1;
char scoreP2;
ISR(TIMER1_OVF_vect)
{lineCounter = 0;}
ISR(TIMER2_OVF_vect)
{
sei();
__asm__("sleep \n");
}
ISR(TIMER2_COMPB_vect)
{
register char columnCounter;
register char* rowPtr;
switch (PXL_OUT){
case 1:
rowPtr = &(PXL_DATA[(lineCounter - 35)>>4][0]);
columnCounter = SCREEN_W;
while(columnCounter--){
PORTD = (*(rowPtr++)<<PD2);
nop;
}
nop;
nop;
nop;
PORTD = 0;
break;
}
lineCounter++;
skipLine = !skipLine;
if ( skipLine && (lineCounter >= 35) && (lineCounter < 515) ){
PXL_OUT = 1;
}
else{
PXL_OUT = 0;
}
}
void setup() {
cli(); //DISABLE INTERRUPTS
PORTD = 0;
pinMode(10, OUTPUT);
pinMode(3, OUTPUT);
pinMode(2, OUTPUT);
//pinMode(A0,INPUT);
//pinMode(12, INPUT);
TCCR1A = 0b00100011; //PIN D10 OUTPUT, FAST PWM MODE
TCCR1B = 0b00011101; //FAST PWM, PRESCALER OF 1024
TIMSK1 = 0b00000001; //OVERFLOW INTERRUPT ENABLE
TIFR1 = 0b00000001; //CLEAR OVERFLOW FLAG
//TCNT1 = 0; //rst t1
TCCR2A = 0b00100011; //PIN D3 OUTPUT, FAST PWM MODE
TCCR2B = 0b00001010; //FAST PWM, PRESCALER OF 8
TIMSK2 = 0b00000101; //OVERFLOW INTERRUPT ENABLE
TIFR2 = 0b00000101; //CLEAR OVERFLOW FLAG
//TCNT2 = 0; //rst t2
OCR1A = 259; //SET FREQ OF T1
OCR1B = 0; //VSYNC PULSE DURATION
OCR2A = 63; //SET FREQ OF T2
OCR2B = 7; //HSYNC PULSE DURATUIN
SMCR = bit(SE);
memset(PXL_DATA, 0, sizeof(PXL_DATA));
lineCounter = 0;
scoreP1 = 0;
scoreP2 = 0;
sei();
}
void gameCode(){
}
void loop()
{
bool shootP1 = (bulletP1Fired == 0) && (random(0, 100) > 90);
bool shootP2 = (bulletP2Fired == 0) && (random(0, 100) > 90);
colP1 = colP1 + dirP1;
if (colP1 < 0)
{
colP1 = 0;
dirP1 = 1;
}
else if (colP1 > (SCREEN_W-3))
{
colP1 = SCREEN_W-3;
dirP1 = -1;
}
else
{
if (random(0, 10) > 8)
dirP1 = -1 * dirP1;
}
colP2 = colP2 + dirP2;
if (colP2 < 0)
{
colP2 = 0;
dirP2 = 1;
}
else if (colP2 > (SCREEN_W-3))
{
colP2 = SCREEN_W-3;
dirP2 = -1;
}
else
{
if (random(0, 10) > 8)
dirP2 = -1 * dirP2;
}
if (shootP1 == 1)
{
bulletP1Fired = 1;
dyP1 = 0.0;
bulletP1X = colP1;
bulletP1Y = rowP1;
}
if ((bulletP1Fired == 1) && (dyP1 < SCREEN_H))
{
bulletP1Y = rowP1 - dyP1;
dyP1 = dyP1 + 1;
}
if (dyP1 >= SCREEN_H)
{
bulletP1Fired = 0;
bulletP1Y = SCREEN_H;
}
if ((bulletP1Fired == 1) && (bulletP1X >= (colP2-1)) && (bulletP1X <= (colP2+1)) && (bulletP1Y == rowP2))
{
colP2 = random(1, 36);
bulletP1Fired = 0;
dyP1 = SCREEN_H;
scoreP1 = scoreP1 + 4;
if (scoreP1 >= 37)
{
scoreP1 = 0;
}
}
if (shootP2 == 1)
{
bulletP2Fired = 1;
dyP2 = 0.0;
bulletP2X = colP2;
bulletP2Y = rowP2;
}
if ((bulletP2Fired == 1) && (dyP2 < SCREEN_H))
{
bulletP2Y = rowP2 + dyP2;
dyP2 = dyP2 + 1;
}
if (dyP2 >= SCREEN_H)
{
bulletP2Fired = 0;
bulletP2Y = SCREEN_H;
}
if ((bulletP2Fired == 1) && (bulletP2X >= (colP1-1)) && (bulletP2X <= (colP1+1)) && (bulletP2Y == rowP1))
{
colP1 = random(1, 36);
bulletP2Fired = 0;
dyP2 = SCREEN_H;
scoreP2 = scoreP2 + 4;
if (scoreP2 >= 37)
{
scoreP2 = 0;
}
}
screen_refresh();
}
void draw_0(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+0] = 0;
PXL_DATA[y+2][x+2] = 0;
PXL_DATA[y+3][x+0] = 0;
PXL_DATA[y+3][x+2] = 0;
PXL_DATA[y+4][x+0] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_1(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+1] = 0;
PXL_DATA[y+2][x+1] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+4][x+1] = 0;
PXL_DATA[y+5][x+1] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_2(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+2] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+4][x+1] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+6][x+0] = 0;
PXL_DATA[y+6][x+1] = 0;
PXL_DATA[y+6][x+2] = 0;
}
void draw_3(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+2] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_4(char x, char y)
{
PXL_DATA[y+0][x+2] = 0;
PXL_DATA[y+1][x+1] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+0] = 0;
PXL_DATA[y+2][x+2] = 0;
PXL_DATA[y+3][x+0] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+3][x+2] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+2] = 0;
}
void draw_5(char x, char y)
{
PXL_DATA[y+0][x+0] = 0;
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+0][x+2] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+2][x+0] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+3][x+2] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_6(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+0] = 0;
PXL_DATA[y+3][x+0] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+4][x+0] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_7(char x, char y)
{
PXL_DATA[y+0][x+0] = 0;
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+0][x+2] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+1] = 0;
PXL_DATA[y+3][x+0] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+3][x+2] = 0;
PXL_DATA[y+4][x+1] = 0;
PXL_DATA[y+5][x+1] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_8(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+0] = 0;
PXL_DATA[y+2][x+2] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+4][x+0] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_9(char x, char y)
{
PXL_DATA[y+0][x+1] = 0;
PXL_DATA[y+1][x+0] = 0;
PXL_DATA[y+1][x+2] = 0;
PXL_DATA[y+2][x+0] = 0;
PXL_DATA[y+2][x+2] = 0;
PXL_DATA[y+3][x+1] = 0;
PXL_DATA[y+3][x+2] = 0;
PXL_DATA[y+4][x+2] = 0;
PXL_DATA[y+5][x+0] = 0;
PXL_DATA[y+5][x+2] = 0;
PXL_DATA[y+6][x+1] = 0;
}
void draw_sep(char x, char y)
{
PXL_DATA[y+2][x+1] = 0;
PXL_DATA[y+4][x+1] = 0;
}
void screen_refresh()
{
char r, c;
for (r = 0; r < SCREEN_H; r++) {
for (c = 0; c < SCREEN_W; c++) {
if ((r == rowP1) &&
(c >= colP1) &&
(c < colP1 + 3)) {
PXL_DATA[r][c] = 0;
}
else if ((r == rowP1 - 1) &&
(c >= colP1 + 1) &&
(c < colP1 + 2)) {
PXL_DATA[r][c] = 0;
}
else if ((r == rowP2 - 1) &&
(c >= colP2) &&
(c < colP2 + 3)) {
PXL_DATA[r][c] = 0;
}
else if ((r == rowP2) &&
(c >= colP2 + 1) &&
(c < colP2 + 2)) {
PXL_DATA[r][c] = 0;
}
else if ((bulletP1Fired == 1)&&(r == bulletP1Y) &&
(c >= bulletP1X + 1) &&
(c < bulletP1X + 2)) {
PXL_DATA[r][c] = 0;
}
else if ((bulletP2Fired == 1) && (r == bulletP2Y) &&
(c >= bulletP2X + 1) &&
(c < bulletP2X + 2)) {
PXL_DATA[r][c] = 0;
}
/*
else if ((r >= enemyY) &&
(r < enemyY + 1) &&
(c >= enemyX) &&
(c < enemyX + 1)) {
PXL_DATA[r][c] = 0;
}
*/
else if ((r == 0) &&
(c >= 0) &&
(c < scoreP1)) {
PXL_DATA[r][c] = 0;
}
else if ((r == 29) &&
(c >= 0) &&
(c < scoreP2)) {
PXL_DATA[r][c] = 0;
}
else {
PXL_DATA[r][c] = 1;
}
}
}
switch (scoreP1/4)
{
case 0:
draw_0(10, 10);
break;
case 1:
draw_1(10, 10);
break;
case 2:
draw_2(10, 10);
break;
case 3:
draw_3(10, 10);
break;
case 4:
draw_4(10, 10);
break;
case 5:
draw_5(10, 10);
break;
case 6:
draw_6(10, 10);
break;
case 7:
draw_7(10, 10);
break;
case 8:
draw_8(10, 10);
break;
case 9:
draw_9(10, 10);
break;
}
draw_sep(18, 10);
switch (scoreP2/4)
{
case 0:
draw_0(26, 10);
break;
case 1:
draw_1(26, 10);
break;
case 2:
draw_2(26, 10);
break;
case 3:
draw_3(26, 10);
break;
case 4:
draw_4(26, 10);
break;
case 5:
draw_5(26, 10);
break;
case 6:
draw_6(26, 10);
break;
case 7:
draw_7(26, 10);
break;
case 8:
draw_8(26, 10);
break;
case 9:
draw_9(26, 10);
break;
}
}
Thanks to Dhruv Wadhwa.
Comments
Please log in or sign up to comment.