Skip to content

Instantly share code, notes, and snippets.

@markfink
Last active July 28, 2017 04:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save markfink/d051d999b8d151d8ad75 to your computer and use it in GitHub Desktop.
Save markfink/d051d999b8d151d8ad75 to your computer and use it in GitHub Desktop.
Arduino Conway's Game of Life on 32 x 62 LED Matrix
// 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