Last active
July 28, 2017 04:49
-
-
Save markfink/d051d999b8d151d8ad75 to your computer and use it in GitHub Desktop.
Arduino Conway's Game of Life on 32 x 62 LED Matrix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Conway's Game Of Life 32 x 64 | |
// based on https://github.com/markfink/GoL_Javascript | |
#include <avr/pgmspace.h> | |
#include "pins_arduino.h" | |
// Connections to board | |
const byte pinSTB=7; | |
const byte pinClock=12; | |
const byte pinURed=10; | |
const byte pinUGreen=11; | |
const byte pinLRed=8; | |
const byte pinLGreen=9; | |
const byte pinOE=2; | |
const byte pinRowA=3; | |
const byte pinRowB=4; | |
const byte pinRowC=5; | |
const byte pinRowD=6; | |
byte scanRow = 0; | |
unsigned long generation = 0; | |
// --------------------------------------------------------------------------------------------------- | |
//const byte RED = 0b10; | |
const byte GREEN = 0b01; | |
//const byte YELLOW = 0b11; | |
const byte BLACK = 0b00; | |
const uint8_t WIDTH = 64; | |
const uint8_t HEIGHT = 32; | |
byte board[WIDTH/8*HEIGHT] = {0x00}; | |
// --------------------------------------------------------------------------------------------------- | |
// function to colorize a pixel | |
void setPixel(byte buffer[], uint8_t x ,uint8_t y, uint8_t color) | |
{ | |
uint8_t myindex = (y*8)+x/8; // 8 segments per row | |
uint8_t mybitmask = 7 -(x % 8); | |
bitWrite(buffer[myindex], mybitmask, (color & 0b00000001)); // green | |
} | |
bool isAlive(uint8_t x ,uint8_t y) { | |
uint8_t myindex = (y*8)+x/8; // 8 segments per row | |
uint8_t mybitmask = 7 -(x % 8); | |
return bitRead(board[myindex], mybitmask); | |
} | |
// count the active neighbors of a given cell | |
uint8_t countNeighbors(uint8_t x ,uint8_t y) { | |
uint8_t count = 0; | |
for (int i=-1; i<2; i++) { | |
for (int j=-1; j<2; j++) { | |
if (!(i == 0 && j == 0)) { | |
int a = x + i; | |
int b = y + j; | |
if (a == -1) a = WIDTH -1; | |
if (b == -1) b = HEIGHT -1; | |
if (a == WIDTH) a = 0; | |
if (b == HEIGHT) b = 0; | |
if (isAlive(a, b)) count++; | |
} | |
} | |
} | |
return count; | |
} | |
// Use the GoL rules to calculate the next generation | |
void nextGeneration() { | |
byte newboard[WIDTH/8*HEIGHT] = {0x00}; | |
for (uint8_t x = 0; x < WIDTH; x++) { | |
for (uint8_t y = 0; y < HEIGHT; y++) { | |
uint8_t non = countNeighbors(x, y); | |
if (non == 3 || (non == 2 && isAlive(x, y))) setPixel(newboard, x, y, GREEN); | |
} | |
} | |
memcpy(board, newboard, sizeof(board)); | |
} | |
// to draw a creature provide position x,y and enough data! | |
void draw(byte buffer[], uint8_t x, uint8_t y, byte creature[]) { | |
for (uint8_t n=0; n<sizeof(buffer); n++) { | |
for (uint8_t i=0; i<8; i++) { | |
if (creature[n] & (1<<(7-i))) setPixel(buffer, x+i, y+n, GREEN); | |
} | |
} | |
} | |
void drawGlider(byte buffer[], uint8_t x, uint8_t y) { | |
byte creature[3] = { | |
0b0000111, | |
0b0000001, | |
0b0000010 | |
}; | |
draw(buffer, x, y, creature); | |
} | |
void drawRPentomino(byte buffer[], uint8_t x, uint8_t y) { | |
byte creature[3] = { | |
0b0000010, | |
0b0000111, | |
0b0000100 | |
}; | |
draw(buffer, x, y, creature); | |
} | |
void drawGliderGun(byte buffer[], uint8_t x, uint8_t y) { | |
// TODO: is that really a glider gun? | |
int cwidth = 48; | |
long creature[9] = { | |
0b00000000000000000000000000010000000000000000000000, | |
0b00000000000000000000000001010000000000000000000000, | |
0b00000000000000011000000110000000000001100000000000, | |
0b00000000000000100010000110000000000001100000000000, | |
0b00011000000001000001000110000000000000000000000000, | |
0b00011000000001000101100001010000000000000000000000, | |
0b00000000000001000001000000010000000000000000000000, | |
0b00000000000000100010000000000000000000000000000000, | |
0b00000000000000011000000000000000000000000000000000, | |
}; | |
for (uint8_t n=0; n<sizeof(creature); n++) { | |
for (uint8_t i=0; i<cwidth; i++) { | |
if (creature[n] & (1<<(7-i))) setPixel(buffer, x+i, y+n, GREEN); | |
} | |
} | |
} | |
void randomiseMatrix(byte buffer[]) { | |
//Set up initial cells in matrix | |
randomSeed(analogRead(0)); | |
for (int row = 0; row < HEIGHT; row++) { | |
for (int col = 0; col < WIDTH/8; col++) { | |
buffer[row*8 + col] = random(0xff); | |
} | |
} | |
} | |
// helper function to paint one row | |
void shiftOut(uint8_t row) { | |
const uint8_t bitLRed = digitalPinToBitMask(pinLRed); | |
volatile uint8_t *outLRed = portOutputRegister(digitalPinToPort(pinLRed)); | |
const uint8_t bitLGreen = digitalPinToBitMask(pinLGreen); | |
volatile uint8_t *outLGreen = portOutputRegister(digitalPinToPort(pinLGreen)); | |
const uint8_t bitURed = digitalPinToBitMask(pinURed); | |
volatile uint8_t *outURed = portOutputRegister(digitalPinToPort(pinURed)); | |
const uint8_t bitUGreen = digitalPinToBitMask(pinUGreen); | |
volatile uint8_t *outUGreen = portOutputRegister(digitalPinToPort(pinUGreen)); | |
const uint8_t bitClock = digitalPinToBitMask(pinClock); | |
volatile uint8_t *outClock = portOutputRegister(digitalPinToPort(pinClock)); | |
const uint8_t bitRowA = digitalPinToBitMask(pinRowA); | |
volatile uint8_t *outRowA = portOutputRegister(digitalPinToPort(pinRowA)); | |
const uint8_t bitRowB = digitalPinToBitMask(pinRowB); | |
volatile uint8_t *outRowB = portOutputRegister(digitalPinToPort(pinRowB)); | |
const uint8_t bitRowC = digitalPinToBitMask(pinRowC); | |
volatile uint8_t *outRowC = portOutputRegister(digitalPinToPort(pinRowC)); | |
const uint8_t bitRowD = digitalPinToBitMask(pinRowD); | |
volatile uint8_t *outRowD = portOutputRegister(digitalPinToPort(pinRowD)); | |
const uint8_t bitOE = digitalPinToBitMask(pinOE); | |
volatile uint8_t *outOE = portOutputRegister(digitalPinToPort(pinOE)); | |
const uint8_t bitSTB = digitalPinToBitMask(pinSTB); | |
volatile uint8_t *outSTB = portOutputRegister(digitalPinToPort(pinSTB)); | |
*outOE |= bitOE; // Turn off display // digitalWrite(pinOE,HIGH); | |
// select row | |
if (bitRead(row, 0)) *outRowA |= bitRowA; else *outRowA &= ~bitRowA; | |
if (bitRead(row, 1)) *outRowB |= bitRowB; else *outRowB &= ~bitRowB; | |
if (bitRead(row, 2)) *outRowC |= bitRowC; else *outRowC &= ~bitRowC; | |
if (bitRead(row, 3)) *outRowD |= bitRowD; else *outRowD &= ~bitRowD; | |
for(uint8_t column=0; column<8; column++){ // 8 segments | |
uint8_t index = column + (row*8); // 8 segments | |
for(uint8_t i=0; i<8; i++) { | |
*outURed |= bitURed; // pinRed, HIGH | |
if (board[index] & (1<<(7-i))) *outUGreen &= ~bitUGreen; | |
else *outUGreen |= bitUGreen; | |
*outLRed |= bitLRed; // pinRed, HIGH | |
if (board[index+128] & (1<<(7-i))) *outLGreen &= ~bitLGreen; | |
else *outLGreen |= bitLGreen; | |
//Clock Pulse | |
*outClock |= bitClock; //CLK, HIGH | |
*outClock &= ~bitClock; //CLK, LOW | |
} | |
} | |
*outSTB &= ~bitSTB; // digitalWrite(pinSTB,LOW); | |
*outSTB |= bitSTB; // digitalWrite(pinSTB,HIGH); | |
*outOE &= ~bitOE; // Turn on display // digitalWrite(pinOE,LOW); | |
} | |
// interrupt routine is responsible for painting the screen | |
ISR(TIMER2_COMPA_vect){ | |
cli(); | |
shiftOut(scanRow); | |
if (scanRow < 15) scanRow++; else scanRow = 0; | |
sei(); | |
} | |
void setup() { | |
// use timer2 as the scanning interrupt timer | |
cli(); // clear interrupts | |
TCCR2A = 0; TCCR2B = 0; TCNT2 = 0; | |
TCCR2B |= (1 << CS12) | (1 << CS10); // Set 1024 prescaler | |
// 160Hz scan rate = 10 frames/second (16 rows) | |
OCR2A = 97; // 97 = (16,000,000 / (1024*160)) - 1 | |
TCCR2A |= (1 << WGM21); TIMSK2 |= (1 << OCIE2A); | |
pinMode(pinURed, OUTPUT); | |
pinMode(pinUGreen, OUTPUT); | |
pinMode(pinLRed, OUTPUT); | |
pinMode(pinLGreen, OUTPUT); | |
pinMode(pinClock, OUTPUT); | |
pinMode(pinRowA, OUTPUT); | |
pinMode(pinRowB, OUTPUT); | |
pinMode(pinRowC, OUTPUT); | |
pinMode(pinRowD, OUTPUT); | |
pinMode(pinOE, OUTPUT); | |
pinMode(pinSTB, OUTPUT); | |
digitalWrite(pinOE, LOW); | |
digitalWrite(pinURed, HIGH); digitalWrite(pinUGreen, HIGH); | |
digitalWrite(pinLRed, HIGH); digitalWrite(pinLGreen, HIGH); | |
sei(); //allow interrupts | |
setPixel(board, 2,2,GREEN); | |
setPixel(board, 3,2,GREEN); | |
setPixel(board, 4,2,GREEN); | |
//drawGlider(board, 20, 5); | |
//drawGliderGun(board, 20, 5); | |
randomiseMatrix(board); | |
} | |
// main loop is responsible for updating the screen | |
void loop() { | |
generation++; | |
nextGeneration(); | |
// delay(0); // can't go much faster than that | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment