My take on the 2 color 8x8 matrix!

I’ve been working on an Electronic ABC cube for my son but had lots of issues with the various matrix control libraries, the sprite library, RAM space, etc.
I managed to come up with this solution, that uses a modified Matrix library to replicate sprite functions without the overhead, EEPROM stored sprites and PROGMEM stored display strings, and 2 color simultaneous animation and text scrolling.
It is able to
a) scroll text in red, green, orange, inverse/negative of those 3, and red on green/green on red
b) do sprite animation flipping in all the above, also can be used as static letters
c) includes a cell animation routine
d) includes Conway’s life routine (still a WIP).
I also have a 50 character sprite set made of a wxPython generated swiss font combined with assorted other animation sprites and symbols…
Now with video!

[edit]BTW: Plenty of bits of this were taken from plenty of different sources, so thanks to everyone who’s code I borrowed or modified or researched[/edit]

Here’s the modified Matrix.h:

*/

#ifndef Matrix_h
#define Matrix_h

#include <inttypes.h>

class Sprite;

class Matrix
{
  private:
    uint8_t _pinData;
    uint8_t _pinClock;
    uint8_t _pinLoad;

    uint8_t* _buffer;
    uint8_t _screens;
    uint8_t _maximumX;

    void putByte(uint8_t);
    
    void syncRow(uint8_t);

    void setScanLimit(uint8_t);

    void buffer(uint8_t, uint8_t, uint8_t);
  public:
    Matrix(uint8_t, uint8_t, uint8_t, uint8_t = 1);
    void setBrightness(uint8_t);
    void write(uint8_t, uint8_t, uint8_t);
    void write(uint8_t, uint8_t, Sprite);
    void write(uint8_t, uint8_t, uint8_t[8][8]);
    void clear(void);
    void setRegister(uint8_t, uint8_t);
};

#endif

and Matrix.cpp:

/*
  Matrix.cpp - Max7219 LED Matrix library for Arduino & Wiring
  Copyright (c) 2006 Nicholas Zambetti.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

// TODO: Support segment displays in api?
// TODO: Support varying vendor layouts?

/******************************************************************************
 * Includes
 ******************************************************************************/

extern "C" {
  // AVR LibC Includes
  #include <inttypes.h>
  #include <stdlib.h>

  // Wiring Core Includes
  #undef abs
  #include "WConstants.h"

  // Wiring Core Prototypes
  //void pinMode(uint8_t, uint8_t);
  //void digitalWrite(int, uint8_t);
}

#include "Sprite.h"
#include "Matrix.h"

/******************************************************************************
 * Definitions
 ******************************************************************************/

// Matrix registers
#define REG_NOOP   0x00
#define REG_DIGIT0 0x01
#define REG_DIGIT1 0x02
#define REG_DIGIT2 0x03
#define REG_DIGIT3 0x04
#define REG_DIGIT4 0x05
#define REG_DIGIT5 0x06
#define REG_DIGIT6 0x07
#define REG_DIGIT7 0x08
#define REG_DECODEMODE  0x09
#define REG_INTENSITY   0x0A
#define REG_SCANLIMIT   0x0B
#define REG_SHUTDOWN    0x0C
#define REG_DISPLAYTEST 0x0F

/******************************************************************************
 * Constructors
 ******************************************************************************/

Matrix::Matrix(uint8_t data, uint8_t clock, uint8_t load, uint8_t screens /* = 1 */)
{
  // record pins for sw spi
  _pinData = data;
  _pinClock = clock;
  _pinLoad = load;

  // set ddr for sw spi pins
  pinMode(_pinClock, OUTPUT);
  pinMode(_pinData, OUTPUT);
  pinMode(_pinLoad, OUTPUT);

  // allocate screenbuffers
  _screens = screens;
  _buffer = (uint8_t*)calloc(1, 64);
  _maximumX = (1* 8);

  // initialize registers
  clear();             // clear display
  setScanLimit(0x07);  // use all rows/digits
  setBrightness(0x0F); // maximum brightness
  setRegister(REG_SHUTDOWN, 0x01);    // normal operation
  setRegister(REG_DECODEMODE, 0x00);  // pixels not integers
  setRegister(REG_DISPLAYTEST, 0x00); // not in test mode
}

/******************************************************************************
 * MAX7219 SPI
 ******************************************************************************/

// sends a single byte by sw spi (no latching)
void Matrix::putByte(uint8_t data)
{
  uint8_t i = 8;
  uint8_t mask;
  while(i > 0) {
    mask = 0x01 << (i - 1);         // get bitmask
    digitalWrite(_pinClock, LOW);   // tick
    if (data & mask){               // choose bit
      digitalWrite(_pinData, HIGH); // set 1
    }else{
      digitalWrite(_pinData, LOW);  // set 0
    }
    digitalWrite(_pinClock, HIGH);  // tock
    --i;                            // move to lesser bit
  }
}

// sets register to a byte value for all screens
void Matrix::setRegister(uint8_t reg, uint8_t data)
{
  digitalWrite(_pinLoad, LOW); // begin
  for(uint8_t i = 0; i < _screens; ++i){
    putByte(reg);  // specify register
    putByte(data); // send data
  }
  digitalWrite(_pinLoad, HIGH);  // latch in data
  digitalWrite(_pinLoad, LOW); // end
}

// syncs row of display with buffer
void Matrix::syncRow(uint8_t row)
{
  if (!_buffer) return;
  
  // uint8_t's can't be negative, so don't test for negative row
  if (row > 8) return;
  digitalWrite(_pinLoad, LOW); // begin
  for(uint8_t i = 0; i < _screens; ++i){
    putByte(8 - row);                // specify register
    putByte(_buffer[row + (8 * i)]); // send data
  }
  digitalWrite(_pinLoad, HIGH);  // latch in data
  digitalWrite(_pinLoad, LOW); // end
}

/******************************************************************************
 * MAX7219 Configuration
 ******************************************************************************/

// sets how many digits are displayed
void Matrix::setScanLimit(uint8_t value)
{
  setRegister(REG_SCANLIMIT, value & 0x07);
}

// sets brightness of the display
void Matrix::setBrightness(uint8_t value)
{
  setRegister(REG_INTENSITY, value & 0x0F);
}

/******************************************************************************
 * Helper Functions
 ******************************************************************************/

void Matrix::buffer(uint8_t x, uint8_t y, uint8_t value)
{
  if (!_buffer) return;
  
  // uint8_t's can't be negative, so don't test for negative x and y.
  if (x >= _maximumX || y >= 8) return;

  uint8_t offset = x; // record x
  x %= 8;             // make x relative to a single matrix
  offset -= x;        // calculate buffer offset*/

  // wrap shift relative x for nexus module layout
  if (x == 0){
    x = 8;
  }
  --x;

  // record value in buffer
  if(value){
    _buffer[y + offset] |= 0x01 << x;
  }else{
    _buffer[y + offset] &= ~(0x01 << x);
  }
}

/******************************************************************************
 * User API
 ******************************************************************************/

// buffers and writes to screen
void Matrix::write(uint8_t x, uint8_t y, uint8_t value)
{
  buffer(x, y, value);
  
  // update affected row
  syncRow(y);
}

void Matrix::write(uint8_t x, uint8_t y, Sprite sprite)
{
  for (uint8_t i = 0; i < sprite.height(); i++){
    for (uint8_t j = 0; j < sprite.width(); j++)
      buffer(x + j, y + i, sprite.read(j, i));
      
    syncRow(y + i);
  }
}
////DRM
void Matrix::write(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
  for (uint8_t i = 0; i < 8; i++){
    for (uint8_t j = 0; j < 8; j++)
      buffer(x + j, y + i, sprarray[j][i]);
      
    syncRow(y + i);
  }
}
////DRM
// clears screens and buffers
void Matrix::clear(void)
{
  if (!_buffer) return;

  // clear buffer
  for(uint8_t i = 0; i < 8; ++i){
    for(uint8_t j = 0; j < _screens; ++j){
      _buffer[i + (8 * j)] = 0x00;
    }
  }

  // clear registers
  for(uint8_t i = 0; i < 8; ++i){
    syncRow(i);
  }
}

And then the letters.h (contains the font):

#define sprite const uint8_t


const uint8_t _A[8]= {
B00111000,
B00111000,
B01101100,
B01101100,
B01101100,
B11111110,
B11000110,
B11000110};

const uint8_t _B[8]= {
B01111100,
B01100110,
B01100110,
B01111100,
B01100110,
B01100110,
B01100110,
B01111100};

const uint8_t _C[8]= {
B00111110,
B01100000,
B01100000,
B01100000,
B01100000,
B01100000,
B01100000,
B00111110};

const uint8_t _D[8]= {
B11111000,
B11001100,
B11000110,
B11000110,
B11000110,
B11000110,
B11001100,
B11111000};

const uint8_t _E[8]= {
B01111100,
B01100000,
B01100000,
B01111100,
B01100000,
B01100000,
B01100000,
B01111100};

const uint8_t _F[8]= {
B01111100,
B01100000,
B01100000,
B01111100,
B01100000,
B01100000,
B01100000,
B01100000};

const uint8_t _G[8]= {
B01111110,
B11000000,
B11000000,
B11000000,
B11001110,
B11000110,
B11000110,
B01111110};

const uint8_t _H[8]= {
B11000110,
B11000110,
B11000110,
B11111110,
B11000110,
B11000110,
B11000110,
B11000110};

const uint8_t _I[8]= {
B00111100,
B00011000,
B00011000,
B00011000,
B00011000,
B00011000,
B00011000,
B00111100};

const uint8_t _J[8]= {
B00111100,
B00001100,
B00001100,
B00001100,
B00001100,
B00001100,
B00001100,
B01111000};

const uint8_t _K[8]= {
B01100110,
B01101100,
B01111000,
B01110000,
B01110000,
B01111000,
B01101100,
B01100110};

const uint8_t _L[8]= {
B01100000,
B01100000,
B01100000,
B01100000,
B01100000,
B01100000,
B01100000,
B01111100};

const uint8_t _M[8]= {
B11000001,
B11100011,
B11110111,
B10111101,
B10011001,
B10000001,
B10000001,
B10000001};

const uint8_t _N[8]= {
B01100010,
B01110010,
B01111010,
B01011110,
B01001110,
B01000110,
B01000010,
B01000010};

const uint8_t _O[8]= {
B01111100,
B11000110,
B11000110,
B11000110,
B11000110,
B11000110,
B11000110,
B01111100};

const uint8_t _P[8]= {
B01111100,
B01100110,
B01100110,
B01100110,
B01111100,
B01100000,
B01100000,
B01100000};

const uint8_t _Q[8]= {
B01111100,
B11000110,
B11000110,
B11000110,
B11000110,
B11010110,
B11001110,
B01111110};

const uint8_t _R[8]= {
B11111000,
B11001100,
B11001100,
B11001100,
B11111000,
B11011000,
B11001100,
B11000110};

const uint8_t _S[8]= {
B00111110,
B01100000,
B01100000,
B01111100,
B00111110,
B00000110,
B00000110,
B01111100};

const uint8_t _T[8]= {
B01111110,
B00011000,
B00011000,
B00011000,
B00011000,
B00011000,
B00011000,
B00011000};

const uint8_t _U[8]= {
B11000110,
B11000110,
B11000110,
B11000110,
B11000110,
B11000110,
B11000110,
B01111100};

const uint8_t _V[8]= {
B01100110,
B01100110,
B01100110,
B00111100,
B00111100,
B00111100,
B00011000,
B00011000};

const uint8_t _w[8]= {
B00000000,
B11011011,
B11011011,
B11011011,
B11111111,
B01100110,
B01100110,
B00000000};

const uint8_t _X[8]= {
B01100110,
B01100110,
B00111100,
B00011000,
B00011000,
B00111100,
B01100110,
B01100110};

const uint8_t _Y[8]= {
B01100110,
B01100110,
B00111100,
B00111100,
B00011000,
B00011000,
B00011000,
B00011000};

const uint8_t _Z[8]= {
B01111110,
B00000110,
B00001110,
B00011100,
B00111000,
B01110000,
B01100000,
B01111110};

const uint8_t __[8]= {
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000};

const uint8_t _Alien1[8]= {
B00011000,
B00111100,
B01111110,
B11011011,
B11111111,
B00100100,
B01011010,
B10100101};

const uint8_t _Alien2[8]= {
B00010000,
B00011000,
B11111111,
B11111111,
B11111111,
B11011011,
B11001011,
B11000011};

const uint8_t _heart[8]= {
B01100110,
B10011001,
B10000001,
B10000001,
B10000001,
B01000010,
B00100100,
B00011000};

const uint8_t _exclaim[8]= {
B00011000,
B00011000,
B00011000,
B00011000,
B00011000,
B00000000,
B00011000,
B00011000};


const uint8_t _Alien1b[8]= {
B00011000,  
B00111100,
B01111110,
B10111101,
B11111111,
B00100100,
B01000010,
B00100100};

const uint8_t _Alien3[8]={
B00100100, 
B01111110,
B11011011,
B11111111,
B10100101,
B10011001,
B10000001,
B11000011};

const uint8_t _Alien3b[8]={
B00100100,
B00011000,
B10111101,
B11011011,
B11111111,
B10111101,
B10011001,
B11000011};

sprite check4a[8] ={
B11001100,
B11001100,
B00110011,
B00110011,
B11001100,
B11001100,
B00110011,
B00110011};

sprite check4b[8] ={
B00110011,
B00110011,
B11001100,
B11001100,
B00110011,
B00110011,
B11001100,
B11001100};

sprite fillbox1[8] ={
B00000000,
B00000000,
B00000000,
B00011000,
B00011000,
B00000000,
B00000000,
B00000000};

sprite fillbox2[8]=  {
B00000000,
B00000000,
B00111100,
B00111100,
B00111100,
B00111100,
B00000000,
B00000000};

sprite fillbox3[8] = {
B00000000,
B01111110,
B01111110,
B01111110,
B01111110,
B01111110,
B01111110,
B00000000};
  
sprite allfull[8] = {
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111};
 
sprite check2a[8] =  {
B00001111,
B00001111,
B00001111,
B00001111,
B11110000,
B11110000,
B11110000,
B11110000};

sprite check2b[8] =  {
B11110000,
B11110000,
B11110000,
B11110000,
B00001111,
B00001111,
B00001111,
B00001111};

sprite horzin1[8] ={
B11111111,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B11111111};
  
sprite horzin2[8] = {
B11111111,
B11111111, 
B00000000,
B00000000,
B00000000,
B00000000,
B11111111, 
B11111111};

sprite horzin3[8] =  {
B11111111,
B11111111, 
B11111111,
B00000000,
B00000000,
B11111111,
B11111111,
B11111111};

sprite fana1[8]=  {
B00010000,
B00010000, 
B00010000,
B00011111,
B11111000,
B00001000, 
B00001000, 
B00001000};
 
sprite fana2[8]=  {
B01110000,
B00110001,
B00010011,
B00011111,
B11111000,
B11001000, 
B10001100, 
B00001110};
 
sprite fana3[8] = {
B11110001,
B01110011, 
B00110111,
B00011111,
B11111000,
B11101100, 
B11001110, 
B10001111};

sprite fanb1[8] = {
B00000100,
B00000000,
B10000000,
B00000000,
B00000000,
B00000001,
B00000000,
B00100000};

sprite fanb2[8] = {
B00001110,
B10000100,
B11000000,
B10000000,
B00000001,
B00000011,
B00100001,
B01110000};

sprite fanb3[8] = {
B10011111,
B11001110,
B11100100,
B11000001,
B10000011,
B00100111,
B01110011,
B11111001};

Then I used this to upload the font to the EEPROM:

#include <EEPROM.h>
#include <Binary.h>
#include "C:\Documents and Settings\D\My Documents\Arduino\scrollkickass\letters.h"

const uint8_t * alphabet[] =  {_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P,_Q,_R,_S,_T,_U,_V,_w,_X,_Y,_Z,__,_exclaim,_heart,_Alien1,_Alien2,_Alien1b,_Alien3,_Alien3b,check4a, check4b, fillbox1,fillbox2,fillbox3,allfull,check2a,check2b,horzin1,horzin2,horzin3,fana1,fana2,fana3,fanb1,fanb2,fanb3};

int alphabetsize = sizeof(alphabet)/sizeof(int);
int promcounter = 0;
void setup()
{
  Serial.begin(9600);
  }

void loop()
{
  for (int i = 0;i < alphabetsize;i++){
    for (uint8_t y = 0;y<8;y++)
    {
      
      Serial.print("Sprite");
      Serial.print("\t");
      Serial.print(i,DEC);
      Serial.print("\t");
      Serial.print("Line \t");
      Serial.println(y,DEC);
      uint8_t lineval = alphabet[i][y];
      Serial.print("value = ");
      Serial.println(lineval,DEC);
      Serial.print("Writing to EEPRom Location: ");
      Serial.println(promcounter,DEC);
      EEPROM.write(promcounter,lineval);
      Serial.print("Written...");
      uint8_t promval = EEPROM.read(promcounter);
      Serial.println(promval,DEC);
      promcounter++;
      
    }
  }
Serial.println("ALL DONE!");
while (1){};


}

Now since I am using 2xMAX7219s to control the display I had to use the ISR and timer code on the playground article. However since that was written for the LedControl module I had to also add the ability to set the Maxims in shutdown into the revised Matrix library. I then created a wrapper include:
matrixwrappers.h

//////////////////
//wrappers
//////////////////////
void startISR(void);
void stopISR(void);
extern Matrix redMatrix;
extern Matrix grMatrix;

typedef struct spriStruct {
  uint8_t row[8];
};

typedef struct bufStruct {
  uint8_t buffer[8][8];
};
/*
void writeGreen(uint8_t x, uint8_t y, Sprite sprite)
{
  stopISR();
  grMatrix.write(x,y,sprite);
  startISR();
}
*/
void writeGreen(uint8_t x, uint8_t y, uint8_t value)
{
  stopISR();
  grMatrix.write(x,y,value);
  startISR();
}
void writeGreen(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
  stopISR();
  grMatrix.write(x,y,sprarray);
  startISR();
}
void writeRed(uint8_t x, uint8_t y, uint8_t value)
{
  stopISR();
  redMatrix.write(x,y,value);
  startISR();
}
/*
void writeRed(uint8_t x, uint8_t y, Sprite sprite)
{
  stopISR();
  redMatrix.write(x,y,sprite);
  startISR();
}
*/
void writeRed(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
  stopISR();
  redMatrix.write(x,y,sprarray);
  startISR();
}
void writeYellow(uint8_t x, uint8_t y, uint8_t value)
{
  stopISR();
  redMatrix.write(x,y,value);
  grMatrix.write(x,y,value);
  startISR();
}
void writeYellow(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
  stopISR();
  redMatrix.write(x,y,sprarray);
  grMatrix.write(x,y,sprarray);
  startISR();
}
/*
void writeYellow(uint8_t x, uint8_t y, Sprite sprite)
{
  stopISR();
  redMatrix.write(x,y,sprite);
  grMatrix.write(x,y,sprite);
  startISR();
}
*/
void writeInvYellow(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
     bufStruct inverseBuffer;
  for (uint8_t x=0;x<8;x++)
  {
      for (uint8_t y=0;y<8;y++)
      {
      inverseBuffer.buffer[x][y]=1-(sprarray[x][y]);
      }
  }  
  stopISR();
  grMatrix.write(x,y,inverseBuffer.buffer);
  redMatrix.write(x,y,inverseBuffer.buffer);
  startISR();
}
void writeInvRed(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
     bufStruct inverseBuffer;
  for (uint8_t x=0;x<8;x++)
  {
      for (uint8_t y=0;y<8;y++)
      {
      inverseBuffer.buffer[x][y]=1-(sprarray[x][y]);
      }
  }  
  stopISR();
  redMatrix.write(x,y,inverseBuffer.buffer);
  startISR();
}
void writeInvGreen(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
     bufStruct inverseBuffer;
  for (uint8_t x=0;x<8;x++)
  {
      for (uint8_t y=0;y<8;y++)
      {
      inverseBuffer.buffer[x][y]=1-(sprarray[x][y]);
      }
  }  
  stopISR();
  grMatrix.write(x,y,inverseBuffer.buffer);
  startISR();
}
void writeGreenonRed(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
 bufStruct inverseBuffer;
  for (uint8_t x=0;x<8;x++)
  {
      for (uint8_t y=0;y<8;y++)
      {
      inverseBuffer.buffer[x][y]=1-(sprarray[x][y]);
      }
  }  
  stopISR();
  grMatrix.write(x,y,sprarray);
  redMatrix.write(x,y,inverseBuffer.buffer);
  startISR();
}
void writeRedonGreen(uint8_t x, uint8_t y, uint8_t sprarray[8][8])
{
 bufStruct inverseBuffer;
  for (uint8_t x=0;x<8;x++)
  {
      for (uint8_t y=0;y<8;y++)
      {
      inverseBuffer.buffer[x][y]=1-(sprarray[x][y]);
      }
  }  
  stopISR();
  grMatrix.write(x,y,inverseBuffer.buffer);
  redMatrix.write(x,y,sprarray);
  startISR();
}
/*
void writeBoth(uint8_t x, uint8_t y, Sprite green, Sprite red)
{
  stopISR();
  redMatrix.write(x,y,red);
  grMatrix.write(x,y,green);
  startISR();
}
*/
void writeBoth(uint8_t x, uint8_t y, uint8_t sprarrayg[8][8], uint8_t sprarrayr[8][8])
{
  stopISR();
  grMatrix.write(x,y,sprarrayg);
  redMatrix.write(x,y,sprarrayr);
  startISR();
}

void clearGreen()
{
  stopISR();
  grMatrix.clear();
  startISR();
}
void clearRed()
{
  stopISR();
  redMatrix.clear();
  startISR();
}
void clearMatrix()
{
  stopISR();
  grMatrix.clear();
  redMatrix.clear();
  startISR();
}

Whew! Then I started to run out of memory with my strings, etc. so I had to use the PROGMEM to store my display strings, and the strings that the animation functions use…
At any rate, still a WIP, but here’s the final program…This is one of my first C/c++ works so I’m pretty happy with all the Cness of it (structs and everything lol!)

#include <Binary.h> 

#include <EEPROM.h>
#include <Matrix.h> 
//#include <Flash.h>
#include <avr/pgmspace.h>
 
#define _BV(bit) (1 << (bit))
//letter sprites and matrix writing functions

#include "C:\Documents and Settings\D\My Documents\Arduino\scrollnosprite\matrixwrappers.h"
//color defines
#define RED 0
#define GREEN 1
#define ORANGE 2
#define GREEN_RED 3
#define RED_GREEN 4
#define INV_RED 5
#define INV_GREEN 6
#define INV_ORANGE 7

#define BRIGHTNESS 10
#define MAX_SPRITES 50
//set up the Matrix objects
Matrix redMatrix = Matrix(8, 6, 7); 
Matrix grMatrix = Matrix(5, 3, 4); 
//all this for the timer
boolean redActive=true;
#define REG_SHUTDOWN    0x0C
#define ISR_FREQ 249
unsigned long ISRTime; 
///variables for the cellular animation
const uint8_t numparticles = 4; 
int velx[numparticles]; 
int vely[numparticles]; 
long posx[numparticles]; 
long posy[numparticles]; 
uint8_t res = 7; 
int maxnum = 8<<res;
uint8_t maxspeed = 30; 
uint8_t row; 
uint8_t col; 
////variables for the life game
//uint8_t world[8][8][2];
uint8_t density = 50; // The density of the population on the the screen
bufStruct life1;
bufStruct life2;
#define XSIZE 8
#define YSIZE 8
//put the strings in progmem
prog_uchar message1[] PROGMEM = " TEXT SCROLLING!! ";
prog_uchar message2[] PROGMEM = " GAME OF LIFE!! ";
prog_uchar message3[] PROGMEM = " CELLULAR ANIMATION!! "; 
prog_uchar message4[] PROGMEM = " SPRITE ANIMATION!! "; 
prog_uchar message5[] PROGMEM = " EEPROM STORED FONT AND SPRITES!! ";
prog_uchar message6[] PROGMEM = " TWO COLOR ANIMATION!! "; 
prog_uchar alienrun[] PROGMEM =" bcbcbcbcbc aaaaa efefefefef ddddd cbcbcbcbcb fefefefe";
prog_uchar moreani[] PROGMEM = " ijkl ijkl ijkl lkji lkji lkji opql opql opql lqpo lqpo lqpo rstl rstl rstl uvwl uvwl uvwl uvwl ";
prog_uchar testanigr[] PROGMEM =" ghghghghghghghghghgh mnmnmnmnmnmn ijkl ijkl ijkl";
prog_uchar testanir[] PROGMEM =" hghghghghghghghghghg nmnmnmnmnmnm  ijk  ijk  ijk";




//timer functions
ISR(TIMER2_COMPA_vect) {  //This ISR toggles shutdown between the 2MAX7221's
  if(redActive==true){
    grMatrix.setRegister(0x0C, 0x00);  // The order here is critical - Shutdown first!
    redMatrix.setRegister(0x0C, 0x01);   // . . . Then restart the other.
    redActive=false;
  }
  else {
    redMatrix.setRegister(0x0C, 0x00);
    grMatrix.setRegister(0x0C, 0x01);
    redActive=true;
  }
}  
void setISRtimer(){  // setup ISR timer controling toggleing
  TCCR2A = 0x02;                        // WGM22=0 + WGM21=1 + WGM20=0 = Mode2 (CTC)
  TCCR2B = 0x05;                // CS22=1 + CS21=0 + CS20=1 = /128 prescaler (125kHz)
  TCNT2 = 0;                            // clear counter
  OCR2A = ISR_FREQ;                     // set TOP (divisor) - see define
}
void startISR(){  // Starts the ISR
  TCNT2 = 0;                            // clear counter (needed here also)
  TIMSK2|=(1<<OCIE2A);                  // set interrupts=enabled (calls ISR(TIMER2_COMPA_vect)
}
void stopISR(){    // Stops the ISR
  TIMSK2&=~(1<<OCIE2A);       // disable interrupts
} 
///////////////////
///more functions for writing to matrix
///////////

//////////////////////

bufStruct loadBuffer(const uint8_t (sprarray)[8]){
  static bufStruct result;
  for (uint8_t x = 0;x<8;x++)
  {
    for (uint8_t y = 0;y<8;y++)
    {
      result.buffer[x][y]=(((sprarray)[y] >>(7-x)) & B00000001);
      //buffer[x][y]=(*alphabet[currentChar])[x]<<8-y) & B00000001;
    }
    
  }
    return result;
}

spriStruct fetchSprite(uint8_t indexno){
  
  static spriStruct test;
  int start = indexno*8;
  for (int y=start, j = start+8 ; y < j  ; y++){
    
    test.row[y%8]=(uint8_t)EEPROM.read(y);
    
  }
 return test;
}
void animateRun(prog_uchar spriteset[],int del, int color){
  int aniLength = strlen_P((prog_char*) spriteset);
  for (int index = 0;index<aniLength;index++)
  {
    int currentChar = getChar(pgm_read_byte_near(spriteset+index));
     spriStruct charSprite = fetchSprite(currentChar);
     bufStruct screen = loadBuffer(charSprite.row);
     runBuffer(screen.buffer, color);
    
      delay(del);
      clearMatrix();
  }
}

void animate2colorRun(prog_uchar spritesetg[],prog_uchar spritesetr[],int del){
  int aniLength = strlen_P((prog_char*)spritesetg);
  for (int index = 0;index<aniLength;index++)
  {
    uint8_t greenChar = getChar(pgm_read_byte_near(spritesetg+index));
    uint8_t redChar = getChar(pgm_read_byte_near(spritesetr+index));
     spriStruct charSpriteg = fetchSprite(greenChar);
     spriStruct charSpriter = fetchSprite(redChar);     
     bufStruct screeng = loadBuffer(charSpriteg.row);
     bufStruct screenr = loadBuffer(charSpriter.row);
     
     //runBuffer(screeng.buffer, GREEN);
     //runBuffer(screenr.buffer, RED);
    writeBoth(0,0,screeng.buffer, screenr.buffer);
      delay(del);
      clearMatrix();
  }
}

void slideText(prog_uchar message[], int del, int color){
    int phraseLength = strlen_P((prog_char*)message);
  //uint8_t buffer[8][8];
  
  
  for (int index = 0;index<phraseLength-1;index++)
  {
 uint8_t currentChar = getChar(pgm_read_byte_near(message+index));
 uint8_t nextChar = getChar(pgm_read_byte_near(message+index+1));
  
  spriStruct charSprite = fetchSprite(currentChar);
  spriStruct nextcharSprite = fetchSprite(nextChar);
  
  bufStruct screen = loadBuffer(charSprite.row);
  
  
 // &buffer = loadBuffer(buffer,(alphabet[currentChar]));
  for (int l = 0;l<9;l++)
  {
    runBuffer(screen.buffer, color);
    int valuer=0;
    int value2r=0;
    delay(del);
    for (int y=0;y<8;y++)
    {
          for (int x=0;x<7;x++)
      {
        screen.buffer[x][y] = screen.buffer[x+1][y];
         }
    }
       for (int y=0;y<8;y++)
    {
      if (l==0){value2r=0;}
      else {value2r = (nextcharSprite.row[y]>>(8-(l-1)) & B00000001) ;}
       screen.buffer[7][y]=value2r;
    }
   }
  clearMatrix();
  }
  //free(screen);
}
/////and the writer for the scroll
void runBuffer(uint8_t buffer[8][8], int color){
   if (color==RED) 
      {
        writeRed(0,0,buffer);
      }
      else if (color == GREEN)
      {
        writeGreen(0,0,buffer);
      }
      else if (color == ORANGE)
      {
        writeYellow(0,0,buffer);
      }
      else if (color == GREEN_RED)
      {
        writeGreenonRed(0,0,buffer);
      }
      else if (color == RED_GREEN)
      {
        writeRedonGreen(0,0,buffer);
      }
      else if (color == INV_RED)
      {
        writeInvRed(0,0,buffer);
      }
      else if (color == INV_GREEN)
      {
        writeInvGreen(0,0,buffer);
      }
      else if (color == INV_ORANGE)
      {
        writeInvYellow(0,0,buffer);
      }
}

void writeValue(uint8_t x, uint8_t y, uint8_t value, int color){
   if (color==RED) 
      {
        writeRed(x,y,value);
      }
      else if (color == GREEN)
      {
        writeGreen(x,y,value);
      }
      else if (color == ORANGE)
      {
        writeYellow(x,y,value);
      }
      /*
      else if (color == GREEN_RED)
      {
        writeGreenonRed(x,y,value);
      }
      else if (color == RED_GREEN)
      {
        writeRedonGreen(x,y,value);
      }
      else if (color == INV_RED)
      {
        writeInvRed(x,y,value);
      }
      else if (color == INV_GREEN)
      {
        writeInvGreen(x,y,value);
      }
      else if (color == INV_ORANGE)
      {
        writeInvYellow(x,y,value);
      }*/
}
void cycleSprites(uint8_t start,uint8_t finish, int del, int color){
  if (finish > MAX_SPRITES) //if pointer size = int size
  {
    finish = MAX_SPRITES;
  }
  for (uint8_t x=start;x<finish+1;x++)
  {
    spriStruct sprite = fetchSprite(x);
    bufStruct screen = loadBuffer(sprite.row);
    runBuffer(screen.buffer, color);
    delay(del);
    clearMatrix();
  }
}

//////////////////////////////////////////////
//some screensaver type stuff
///////////////////////////////////////////////
void cell(int color, int reps)
{
for (int runs=0;runs<reps+1;runs++)
  { 
  clearMatrix(); // clear display 
 
  for( uint8_t i = 0; i<numparticles; i++)
  {
 
    row = posx[i]>>res;
    col = posy[i]>>res;
    writeValue(row, col, HIGH, color); 
 
 
    posx[i]+=velx[i]; 
    posy[i]+=vely[i]; 
 
    if(posx[i]<0) 
    {
      posx[i]=1;
      velx[i]=-velx[i]; 
    }
    else if(posx[i]>=maxnum)
    {
      posx[i] = maxnum+(maxnum-posx[i]); 
      velx[i]= -velx[i];
      vely[i] = vely[i]+random(-1,1); 
 
    if(vely[i]>maxspeed) vely[i] = maxspeed; 
      else if(vely[i]<-maxspeed) vely[i] = -maxspeed; 
 
 
    }
 
    if(posy[i]<0) 
    {
      posy[i]=1;
      vely[i]=-vely[i]; 
    }
    else if(posy[i]>=maxnum)
    {
      posy[i] = maxnum+(maxnum-posy[i]);  
      vely[i]=-vely[i];  
      velx[i] = velx[i]+random(-1,1); 
      if(velx[i]>maxspeed) velx[i] = maxspeed; 
      else if(velx[i]<-maxspeed) velx[i] = -maxspeed; 
 
 
    }
 
  }
  delay(1);
  }
 clearMatrix(); 
}

Part 2:

/////////////////////
//Life
void lifeinit(){
  for (uint8_t i = 0; i < 8; i++) {
    for (uint8_t j = 0; j < 8; j++) {
      if (random(100) < density) {
        
        life1.buffer[i][j] = 1;
        life2.buffer[i][j]= life1.buffer[i][j];
      }
      else {
        life1.buffer[i][j] = 0;
      }
      life2.buffer[i][j] = 0;
    }
  }


}
uint8_t neighbours(uint8_t x, uint8_t y, uint8_t buffer [8][8]) {
 return buffer[(x + 1) % XSIZE][y] +
         buffer[x][(y + 1) % YSIZE] +
         buffer[(x + XSIZE - 1) % XSIZE][y] +
         buffer[x][(y + YSIZE - 1) % YSIZE] +
         buffer[(x + 1) % XSIZE][(y + 1) % YSIZE] +
         buffer[(x + XSIZE - 1) % XSIZE][(y + 1) % YSIZE] +
         buffer[(x + XSIZE - 1) % XSIZE][(y + YSIZE - 1) % YSIZE] +
         buffer[(x + 1) % XSIZE][(y + YSIZE - 1) % YSIZE];
}

void Life(int color,int reps,int del){
  uint8_t repeats = 0;
  bufStruct last;
  for (int goes = 0;goes<reps+1;goes++)
  {
    
    
    
    //bufStruct screen = loadBuffer(life1.row);
    runBuffer(life1.buffer, color);
    delay(del);
    for (int x = 0; x < 8; x++) {
    for (int y = 0; y < 8; y++) {
      // Default is for cell to stay the same
      life2.buffer[x][y] = life1.buffer[x][y];
      int count = neighbours(x, y, life1.buffer);
      if (count == 3 && life1.buffer[x][y] == 0) {
        // A new cell is born
        life2.buffer[x][y] = 1;
        
        
      }
      if ((count < 2 || count > 3) && life1.buffer[x][y] == 1) {
        // Cell dies
        
        life2.buffer[x][y] = 0;
      }       
    }
  }

  // Copy next generation into place
  if (life1.buffer == life2.buffer) {
    repeats++;
  }
  
  for (int x = 0; x < 8; x++) {
    for (int y = 0; y < 8; y++) {
      life1.buffer[x][y] = life2.buffer[x][y];
      
  }
}

    if (repeats>10) { 
      lifeinit(); 
    repeats = 0;
  }
 
  }
}
////////////////////////////////////////////

uint8_t getChar(char character){
 uint8_t returnValue = 0;
  if ((character >64) && (character < 91)) {
    returnValue = character-65;
  }
  else
  {
    switch(character){
    case '!': returnValue = 27;break; 
    case 'a': returnValue = 28; break; //heart
    case 'b': returnValue = 29; break; //alien1
    case 'c': returnValue = 31; break; //alien1b
    case 'd': returnValue = 30; break; //alien2
    case 'e': returnValue = 32; break; //alien3
    case 'f': returnValue = 33; break; //alien3b
    case 'g': returnValue = 34; break; //check4a
    case 'h': returnValue = 35; break; //check4b
    //fillbox1,fillbox2,fillbox3,allfull,check2a,check2b,horzin1,horzin2,horzin3,fana1,fana2,fana3,fanb1,fanb2,fanb3
    case 'i': returnValue = 36; break; //fillbox1
    case 'j': returnValue = 37; break; //fillbox2
    case 'k': returnValue = 38; break; //fillbox3
    case 'l': returnValue = 39; break; //allfull
    case 'm': returnValue = 40; break; //check2a
    case 'n': returnValue = 41; break; //check2b
    case 'o': returnValue = 42; break; //horzin1
    case 'p': returnValue = 43; break; //horzin2
    case 'q': returnValue = 44; break; //horzin2
    case 'r': returnValue = 45; break; //fana1
    case 's': returnValue = 46; break; //fana2
    case 't': returnValue = 47; break; //fana3
    case 'u': returnValue = 48; break; //fanb1
    case 'v': returnValue = 49; break; //fanb2
    case 'w': returnValue = 50; break; //fanb3
    case ' ': returnValue = 26; break; //
   }
    return returnValue;
} 
}
///////////////Setup
void setup() 
{ 
  setISRtimer();
  startISR();
  grMatrix.setBrightness(BRIGHTNESS);
  redMatrix.setBrightness(BRIGHTNESS);
  randomSeed(analogRead(5));// a random seed for the random start pattern

  //setup the cell ani
  for(int i = 0; i<numparticles; i++)
  {
    velx[i] = random(11)+5;
    vely[i] = random(11)+5;
    posx[i] = (i%2)<<res;
    posy[i] = (i/2)<<res;
 
  }
 //setup the life
 lifeinit();

  }
/////////////
void loop()
{
  slideText(message1,75,GREEN);
  delay(1000);
  slideText(message2,75,RED);
  delay(1000);
  Life(GREEN,100,750);
  delay(1000);
  slideText(message3,75,ORANGE);
  delay(1000);
  cell(RED,5000);
  delay(1000);
  /*slideText(message2,50,INV_GREEN);
  delay(1000);
  cell(ORANGE,2000);
  delay(1000);
  */
  slideText(message4,75,ORANGE);
  delay(1000);
  animateRun(alienrun,500,RED);
  delay(1000);
  
  slideText(message5,75,INV_RED);
  delay(1000);
  animateRun(moreani,500,GREEN_RED);
  delay(1000);
  
  animate2colorRun(testanigr,testanir,500);
  delay(1000);
 
  slideText(message6,75,RED_GREEN);
 delay(1000); 
  cycleSprites(0,25,500,ORANGE);
  delay(1000);
  cycleSprites(26,51,500,RED);
  delay(1000);
}

Anyway, needs some cleanup, but hope someone finds it useful!

Hi, Very nice video / demo and set of code. Thanks for sharing it.

I'm also a Dad thinking of making some sort of Arduino powered light / cube / ??? for my son. Was the led matrix a good choice or would you do something different if you were to start over?

One question about the LED matrix - have you tried dimming (via pwm etc) for finer-grain color mixing? Daydreaming about this project it seems like it would be cool to have something like motion blur, or fade-in/out of the characters...

Thanks for any info - still reading up / about to buy an arduino + possibly a couple display driving chips as well. I'm a compsci + ee trained engineer, but it's been a while since I studied ee.

Thanks for the compliments! At any rate I think the LED Matrix was a good choice for my proposed "electronic ABC block" because of the size and brightness of it. For that purpose I would just cycle through the alphabet sprites - all the rest of the code was me just running wild with it. I also think a single color matrix would have been fine for it, but the 2 color sparkfun was what I had handy.

I also purchased a small LCD screen for that purpose thinking I could do the "A" then a picture of an apple, etc. However for the price point/vs screen size the LCD is far more expensive (although much more capable). Also with the pictures you are looking at external storage for the images - SD cards appear to be the most popular choice for this.

The main drawbacks of the approach I have used here are a) Requires 2 external driver chips - I used the max7219. You could direct drive the matrix via the arduino/avr but would require 24 pins (common anode/cathode per row and then each column in red AND green). An avr Mega could do it, but not a standard Arduino-class AVR 168/368. With the 2 driver chips it requires 6 pins, 3 per chip.

b) because of the two chips sharing common cathode wiring you have to do the whole timer based shutdown/switch between the chips as per the playground article. It works fine, but that has implications on what the rest of your code can do (i.e. at a minimum you've lost a timer, and probably an interrupt pin).

c) Excessive wiring for the matrix (again 24 pins) - you can kind of see the sprawl of wire in the video. A good pcb would fix this, and that's next on my list. ;)

d) to tie in with your other question about pwm dimming, etc - as far as I know the MAX7219's etc do not have the ability to do pwm on their outputs, probably due to the multiplexing. You can set an overall brightness, but not per led. This would be possible if you had an AVR with enough pins as in (a).

Anyway good luck with your project! As I bring this farther to completion I'll update. This week I hope to transfer from breadboard to some standard RadioShack protoboard in prep for permanent mounting...

Noah,

I've worked with both the MAX72xx and the '595 shift register for RG matrices, and although I found the "alternating shutdown" method fascinating to develop, in the end, I think I'd suggest using the 595 instead. [u]However, in many ways it's almost a wash for RG.[/u]

Both the MAX72xx and the '595 need some kind of interrupt. For the MAX it's to alternate the shutdown, for the 595 it's to do the refresh.

I really liked using the LedControl lib to drive the MAX, but I found I could make similar functions to drive the 595.

The MAX72xx uses SPI but the LedControl lib does it in software - so both the MAX and the 595 can use any 3 pins and avoid SPI conflicts.

For something non-production, the difference in cost may not be an issue - most get the MAX72xx as free samples.

So what's the advantage? Again, for RG it may not be much, however, the 595 approach is proven to work with RGB, and the MAX72xx seems to get funky around the edges with RGB. So if you want to do RGB in the future, you can build on your 595 code to do it.

You also have direct control over the refresh rate on the 595 which may give you some options. I also found that the 595 seems to be able to run somewhat faster than the MAX. (This surprised me.)

A single color matrix is different. Using the MAX, there is no need for an interrupt like you'd need with a 595.

Having said all this, cthula13, I'm glad to hear that Playground article worked out for you!

RE: PWM - other's will know better, but I think something like the TLC5940 is what you need. However, the big gain in doing PWM is for RGB matrices rather than RG. (IMO)

By "electronic ABC block" do you mean that you are going to use 6 matrices to make all faces of a cube and cram all the electronics and battery in the middle? That's a very interesting project... might be very hard to get it all to fit.

Just to throw out an idea; an accelerometer might be nice too so that it can turn on when it is shaken.

No I was thinking more of just the one matrix as one face of the cube, the rest padded/plushed (sort of chumbylike). Currently I'm thinking of using a tilt sensor I just got as the on/off.