Recently purchased a Maple Mini clone on eBay, for very little money. This is a 32-bit ARM based micro controller running at 72MHz.
Converting the sketch for the Maple was very straightforward. The only changes were those around the Serial connection to the PC (only used for reporting the generations per second achieved) and the SPI connection to the OLED display. I did no performance tuning except to increase the SPI clock speed from 8MHz to 18MHz.
New speed record: 392 generations per second!
The increase in clock speed from 16MHz to 72 MHz (x4.5) should have boosted the generations per second from around 45 to 200. The 32-bit processor seems to be doubling the speed again versus the 8 bit in the Arduino.
// Conway's Game Of Life 128x64
// PaulRB
// Jun 2014
HardwareSPI spi(1);
//Pins controlling SSD1306 Graphic OLED
#define OLED_DC 1
#define OLED_CS 0
#define OLED_RESET 2
union MatrixData {
unsigned long long l;
byte b[8];
};
MatrixData Matrix[129]; // Cell data in ram
void setup() {
pinMode(OLED_DC, OUTPUT);
pinMode(OLED_CS, OUTPUT);
pinMode(OLED_RESET, OUTPUT);
spi.begin(SPI_18MHZ, MSBFIRST, 0);
digitalWrite(OLED_RESET, HIGH);
delay(1);
digitalWrite(OLED_RESET, LOW);
delay(10);
digitalWrite(OLED_RESET, HIGH);
digitalWrite(OLED_DC, LOW);
digitalWrite(OLED_CS, LOW);
spi.write(0xAE); // Display off
spi.write(0xD5); // Set display clock divider
spi.write(0x80);
spi.write(0xA8); // Set multiplex
spi.write(0x3F);
spi.write(0xD3); // Set display offset
spi.write(0x00);
spi.write(0x40); // Set start line to zero
spi.write(0x8D); // Set charge pump
spi.write(0x14);
spi.write(0x20); // Set memory mode
spi.write(0x00);
spi.write(0xA0 | 0x1); // Set segment remapping
spi.write(0xC8); // Set command Scan decode
spi.write(0xDA); // Set Comm pins
spi.write(0x12);
spi.write(0x81); // Set contrast
spi.write(0xCF);
spi.write(0xd9); // Set precharge
spi.write(0xF1);
spi.write(0xDB); // Set Vcom detect
spi.write(0x40);
spi.write(0xA4); // Allow display resume
spi.write(0xA6); // Set normal display
spi.write(0xAF); // Display On
digitalWrite(OLED_CS, HIGH);
//R-pentomino
Matrix[64].l = B0000010; Matrix[64].l = Matrix[64].l << 32;
Matrix[65].l = B0000111; Matrix[65].l = Matrix[65].l << 32;
Matrix[66].l = B0000100; Matrix[66].l = Matrix[66].l << 32;
//randomiseMatrix();
outputMatrix();
}
void loop() {
unsigned long start = millis();
for (int i=0; i<1000; i++) {
generateMatrix();
outputMatrix();
}
SerialUSB.print("Gens/s:"); SerialUSB.println(1000000/(millis() - start));
}
void outputMatrix() {
digitalWrite(OLED_DC, LOW); //Command mode
digitalWrite(OLED_CS, LOW); //Enable display on SPI bus
spi.write(0x21); // Set column address
spi.write(0);
spi.write(127);
spi.write(0x22); // Set page address
spi.write(0);
spi.write(7);
digitalWrite(OLED_CS, HIGH); //Disable display on SPI bus
digitalWrite(OLED_DC, HIGH); // Data mode
digitalWrite(OLED_CS, LOW); //Enable display on SPI bus
//Send matrix data for display on OLED
for (byte col = 0; col < 8; col++) {
for (byte row = 0; row <= 127; row++) {
spi.write(Matrix[row].b[col]);
}
}
digitalWrite(OLED_CS, HIGH);
}
void randomiseMatrix() {
//Set up initial cells in matrix
randomSeed(analogRead(0));
for (byte row = 0; row <= 127; row++) {
for (byte col = 0; col <= 8; col++) {
Matrix[row].b[col] = random(0xff);
}
}
}
void injectGlider() {
byte col = random(127);
byte row = random(63);
Matrix[col++].l |= ((unsigned long long) B0000111) << row;
Matrix[col++].l |= ((unsigned long long) B0000001) << row;
Matrix[col++].l |= ((unsigned long long) B0000010) << row;
}
int generateMatrix() {
//Variables holding data on neighbouring cells
unsigned long long NeighbourN, NeighbourNW, NeighbourNE, CurrCells, NeighbourW, NeighbourE, NeighbourS, NeighbourSW, NeighbourSE;
//Variables used in calculating new cells
unsigned long long tot1, carry, tot2, tot4, NewCells;
int changes = 0; // counts the changes in the matrix
static int prevChanges[4]; // counts the changes in the matrix on prev 4 generations
static int staleCount = 0; // counts the consecutive occurrances of the same number of changes in the matrix
//set up N, NW, NE, W & E neighbour data
NeighbourN = Matrix[127].l;
CurrCells = Matrix[0].l;
Matrix[128].l = CurrCells; // copy row 0 to location after last row to remove need for wrap-around code in the loop
NeighbourNW = NeighbourN >> 1 | NeighbourN << 63;
NeighbourNE = NeighbourN << 1 | NeighbourN >> 63;
NeighbourW = CurrCells >> 1 | CurrCells << 63;
NeighbourE = CurrCells << 1 | CurrCells >> 63;
//Process each row of the matrix
for (byte row = 0; row <= 127; row++) {
//Pick up new S, SW & SE neighbours
NeighbourS = Matrix[row + 1].l;
NeighbourSW = NeighbourS >> 1 | NeighbourS << 63;
NeighbourSE = NeighbourS << 1 | NeighbourS >> 63;
//Count the live neighbours (in parallel) for the current row of cells
//However, if total goes over 3, we don't care (see below), so counting stops at 4
tot1 = NeighbourN;
tot2 = tot1 & NeighbourNW; tot1 = tot1 ^ NeighbourNW;
carry = tot1 & NeighbourNE; tot1 = tot1 ^ NeighbourNE; tot4 = tot2 & carry; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourW; tot1 = tot1 ^ NeighbourW; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourE; tot1 = tot1 ^ NeighbourE; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourS; tot1 = tot1 ^ NeighbourS; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourSW; tot1 = tot1 ^ NeighbourSW; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourSE; tot1 = tot1 ^ NeighbourSE; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
//Calculate the updated cells:
// <2 or >3 neighbours, cell dies
// =2 neighbours, cell continues to live
// =3 neighbours, new cell born
NewCells = (CurrCells | tot1) & tot2 & ~ tot4;
//Have any cells changed?
if (NewCells != CurrCells) {
//Count the change for "stale" test
changes++;
Matrix[row].l = NewCells;
}
//Current cells (before update), E , W, SE, SW and S neighbours become
//new N, NW, NE, E, W neighbours and current cells for next loop
NeighbourN = CurrCells;
NeighbourNW = NeighbourW;
NeighbourNE = NeighbourE;
NeighbourE = NeighbourSE;
NeighbourW = NeighbourSW;
CurrCells = NeighbourS;
}
if (changes != prevChanges[0] && changes != prevChanges[1] && changes != prevChanges[2] && changes != prevChanges[3]) {
staleCount = 0;
}
else {
staleCount++; //Detect "stale" matrix
}
if (staleCount > 64) injectGlider(); //Inject a glider
//SerialUSB.println(changes);
for (int i=3; i>0; i--) {
prevChanges[i] = prevChanges[i-1];
}
prevChanges[0] = changes;
}
Paul