Loading

Electrons: My first LED matrix

Time for retro 1 bit graphics with the MAX279

Electrons: My first LED matrix

The next parts kit for me to experiment with is the:

MAX7219 Dot Arduino Matrix Display Module Cascade Control Arduino Module DIY New

This was £1.27 supplied as components and a board.  You can get the board ready built for a few pence more. The board has a LED driver chip, and sockets for an 8x8 LED grid.

Previously I have lit LEDs by connecting each one to its relevant pin on the micro controller.  However the trinket only has 4 output pins - which means I’m limited that way to only 4 LEDs.  The way that you get around this is by using a matrix of LEDs, and then you step through these row by row and column by column and you can do this so quickly that you can’t see the flashing, and it becomes an image.

Of course - that would mean I still needed 16 pins to drive an 8x8 matrix, too many for the trinket.  This is where the MAX279 chip comes in - you communicate with that - using 4 pins, and the chip does all the maths itself and lights up the LEDs at your command.

So if you look at the completed driver board, it has five header pins in, power and ground, then DIN, CS and CLK.  The board also has a similar header on the opposite side - because these chips and boards can be chained together and many different matrices can then still be controlled by the same trinket.  I’ll not cover the daisy chain here.

I can just connect the power and ground connectors to the 3v and ground of the trinket directly, using red and black wires.

Then, I use a white wire from pin 2 of the trinket to go to the DIN (Data in) pin of the matrix board.  Trinket pin 3 (orange) connects to CS (Chip Select I think) and Trinket 4 (orange) to CLK (Clock).

Here is a view of the breadboard wiring with the matrix board removed:

At this point I powered up the board and was rewarded by this sight:

So it seems to work - and the default state of the matrix board appears to be ‘light up everything'

Next I spent several hours looking for some code to use, and then several days trying to get it to work.  There are many examples out there - and finally I used what I will include at the bottom of this post to add several animation frames of a pacman ghost (which I designed)

The program uses an array full of a set of frames made up of 8 binary bytes and a number which is the time to show that on the matrix.

It then goes through each of those frames and sends it out to the first matrix in the chain.

The upshot is that the code I collated produces a series of frames one of which looks like this:

Adafruit does a similar matrix which along with being more expensive works in a different way - I used their idea of building up the frame as a set of binary bytes and including the timing in an array.  However they put all of this data in a separate file, called anim.h  and although I created this header file like they said, I could not get it to compile.  Theirs is a better method because once you have written the code - you can then mess about with the data and animations separately.  However - I couldn’t get that to work.

What I would love is to create a mini ‘Game Frame’ with this but with even more retro graphics.  Then have an SD card with the data on which the Arduino could read - then I could have thousands of frames of animation, and re-program them without having to re-flash the Arduino.  But I’m a long way from that.  I will however build one of these not on a breadboard, as a self contained device.  I’ll write about that in the future when its complete.

As a reward for getting to the end of this post, here’s a time-lapse of the 12 minutes I spent building the board, and a short video of the full animation using the code at the end.

#include 
const byte PROGMEM anim[] = {

    B00111100,    B01111110,    B11111111,  B11011101,   
    B10011001,    B11111111,    B11111111,  B10100101,
    25, 
    B00111100,   B01111110,   B11111111,   B10111011,  
    B10011001,   B11111111,   B11111111,   B10100101,
    25,   
    B00111100,    B01111110,    B11111111,  B11011101,   
    B10011001,    B11111111,    B11111111,  B10100101,
    25, 
    B00111100,   B01111110,   B11111111,   B10111011,  
    B10011001,   B11111111,   B11111111,   B10100101,
    25,
   
    B00111100,   B01000010,   B10000001, B11000101,  
    B11100111,   B10000001,   B11011011, B10100101,
    25,
    B00111100,   B01000010,   B10000001, B10100011,  
    B11100111,   B10000001,   B11011011, B10100101,
    25,
    B00111100,   B01000010,   B10000001, B11000101,  
    B11100111,   B10000001,   B11011011, B10100101,
    25,
    B00111100,   B01000010,   B10000001, B10100011,  
    B11100111,   B10000001,   B11011011, B10100101,
    25,
  
    };
    
    int dataIn = 2;
    int load = 3;
    int clock = 4;
    
    int maxInUse = 1;    //change this variable to set how many MAX7219's you'll use
    
    int e = 0;           // just a varialble
    
                         // define max7219 registers
    byte max7219_reg_noop        = 0x00;
    byte max7219_reg_digit0      = 0x01;
    byte max7219_reg_digit1      = 0x02;
    byte max7219_reg_digit2      = 0x03;
    byte max7219_reg_digit3      = 0x04;
    byte max7219_reg_digit4      = 0x05;
    byte max7219_reg_digit5      = 0x06;
    byte max7219_reg_digit6      = 0x07;
    byte max7219_reg_digit7      = 0x08;
    byte max7219_reg_decodeMode  = 0x09;
    byte max7219_reg_intensity   = 0x0a;
    byte max7219_reg_scanLimit   = 0x0b;
    byte max7219_reg_shutdown    = 0x0c;
    byte max7219_reg_displayTest = 0x0f;
    
    void putByte(byte data) {
      byte i = 8;
      byte mask;
      while(i > 0) {
        mask = 0x01 << (i - 1);      // get bitmask
        digitalWrite( clock, LOW);   // tick
        if (data & mask){            // choose bit
          digitalWrite(dataIn, HIGH);// send 1
        }else{
          digitalWrite(dataIn, LOW); // send 0
        }
        digitalWrite(clock, HIGH);   // tock
        --i;                         // move to lesser bit
      }
    }
    
    void maxSingle( byte reg, byte col) {   
    //maxSingle is the "easy"  function to use for a     //single max7219
    
      digitalWrite(load, LOW);       // begin   
      putByte(reg);                  // specify register
      putByte(col);//((data & 0x01) * 256) + data >> 1); // put data 
      digitalWrite(load, LOW);     
      digitalWrite(load,HIGH);
    }
    
    void maxAll (byte reg, byte col) {    // initialize  all  MAX7219's in the system
      int c = 0;
      digitalWrite(load, LOW);  // begin   
      for ( c =1; c<= maxInUse; c++) {
      putByte(reg);  // specify register
      putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
        }
      digitalWrite(load, LOW);
      digitalWrite(load,HIGH);
    }
    
    void maxOne(byte maxNr, byte reg, byte col) {   
    //maxOne is for adressing different MAX7219's,
    //whilele having a couple of them cascaded
    
      int c = 0;
      digitalWrite(load, LOW);  // begin   
    
      for ( c = maxInUse; c > maxNr; c--) {
        putByte(0);    // means no operation
        putByte(0);    // means no operation
      }
    
      putByte(reg);  // specify register
      putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
    
      for ( c =maxNr-1; c >= 1; c--) {
        putByte(0);   
        putByte(0);  
      }
    
      digitalWrite(load, LOW);
      digitalWrite(load,HIGH);
    }
    
    
    void setup () {
      pinMode(dataIn, OUTPUT);
      pinMode(clock,  OUTPUT);
      pinMode(load,   OUTPUT);
    
      //beginSerial(9600);
      digitalWrite(13, HIGH); 
    
    //initiation of the max 7219
      maxAll(max7219_reg_scanLimit, 0x07);     
      maxAll(max7219_reg_decodeMode, 0x00);  // using an led matrix (not digits)
      maxAll(max7219_reg_shutdown, 0x01);    // not in shutdown mode
      maxAll(max7219_reg_displayTest, 0x00); // no display test
       for (e=1; e<=8; e++) {    // empty registers, turn all LEDs off
        maxAll(e,0);
      }
      maxAll(max7219_reg_intensity, 0x00 & 0x0f);    // the first 0x0f is the value you can set
                                                      // range: 0x00 to 0x0f
    } 
    
    void loop () {
   
    for(int i=0; i

 
        

12-Jan-2015 Add comment

blog comments powered by Disqus Permanent Link

Matthew Norman...

... who lives in the UK, who is not an actor, not a clock manufacturer, did not write a rough guide to cuba and also has never written for the Telegraph. I have written books on coldfusion and web databases.

(c) somewhere in space and time: M.Norman