3 Simple Ways to
Share What You Make

With Instructables you can share what you make with the world — and tap into an ever-growing community of creative experts.

PhotosPhotos

Share one or more photos of a project, recipe, or whatever you've made, quickly and easily.

Step by StepStep-By-Step

Share your step-by-step photos with text instructions of what you made so others can do it too!

VideoVideo

Share your how-to video. You'll need your embed code from a video site such as YouTube.


Yet Another Daft Punk Coffee Table (5x5 LED Matrix)

Step 5Code

Code
The Arduino sketch draws the screen scanning row by row from top to bottom, much like a CRT-based TV screen.  It has the added complexity that in order to keep our current within the limits that the Atmega328P can handle, we can only light at most 2 LED's at a time.   Therefore, we also scan from left to right.  Even though only 2 LED's at a time are lit, to our eye, they appear to all be lit at the same time due to a phenomenon called persistence of vision.

The display code is interrupt driven, and uses the Timer1 library .  Timer1 isn't bundled in the Arduino software installation.  Therefore, to install it, you must download TimerOne.zip , and unzip its contents into your sketchbook/libraries/TimerOne.

A Frame is a screenful of data. A very straightforward and common way of storing the data would be to use a byte for each LED.  This would use 25 bytes per frame.   To save memory, we define it as an array of 5 bytes.  Each byte represents an entire row of our display.  Since a byte is composed of 8 bits, and we only have 5 LED's per row, we only use the bottom 5 bits, and the top 3 bits are ignored.  There are 5 bytes because we have 5 rows.  We could save another byte by packing 25 bits into 4 bytes, but that complicates the code unnecessarily, and makes it impossible to graphically view the frame declarations in frames.h.

  #define DIM 5 // x/y dimension - 5x5 matrix
  typedef byte Frame[DIM];


The column LED pins are defined in the cols array.

  int cols[DIM] = {12,11,10,9,8};


The row LED pins are defined in the rows array.

  int rows[DIM] = {7,6,5,4,3};

To turn on a particular LED, we set its cols pin HIGH and its rows pin LOW.  This causes current to flow through the selected LED.  The most straightforward way to do this would be to use the digitalWrite() function on each LED in sequence.  So to turn on the top left LED, we could use

  digitalWrite(cols[0],HIGH);
  digitalWrite(rows[0],LOW);

and do that for each LED.  However, that's not very efficient.   Instead we use direct port manipulation to turn on the columns.  The PORTB register corresponds to digital pins 8 to 13.
Note the ordering which we used in our declaration of the cols array above:  12,11,10,9,8.  This way, bit0 of PORTB corresponds to the far right column, bit1 corresponds to the next column to the left, until bit4, which corresponds to the far left column.  If we set a particular bit to 1, it's the same as calling digitalWrite() with HIGH, and setting a bit to 0 is equivalent to calling digitalWrite() with LOW.   Therefore, we could theoretically replace calling digitalWrite() 5 times to set all 5 columns with one write to PORTB.  But since we are allowed to turn on at most only 2 LED's at a time, we have to loop 3 times for each column, scanning 2 bits at a time.

// Interrupt routine
// each time display() is called, it turns off the previous row
// and turns on the next row
byte bitMask = B00000011;
void display() {
  digitalWrite(rows[row], HIGH); // Turn whole previous row off

  if (bitMask == B00010000) {
    bitMask = B00000011; // light the right 2 columns (pins 9,8)
    // increment row and wrap if necessary
    if (++row == DIM) {
      row = 0;
    }
  }
  else if (bitMask == B00000011) {
    bitMask = B00001100; // light the middle 2 columns (pins 11,10)
  }
  else { // bitMaskIdx == B00001100
    bitMask = B00010000; // light the leftmost column (pin 12)
  }

  // direct port manipulation.
  // PORTB is a pseudo variable for digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable
  // the bottom 5 bits are our columns. We don't want to change the other bits,
  // so we first mask the bits we ignore, and then set the bits we want to light
  PORTB &= B11100000;
  PORTB |= curFrame[row] & bitMask;

  digitalWrite(rows[row], LOW); // turn on the row
}

Note that our comment above says that display() is an interrupt routine.  This is because instead of handling the scanning in our loop() function, we call it in the background via the Timer1 library.

  // interrupt interval in uSec
  // this determines how long to keep each row turned on
  // so if we have 5 rows, we redraw the whole screen
  // once every 5 rows * 3 cycles per row * 1000 usec = .015 sec -> 66.67Hz
  // if you perceive flickering you can decrease to 500 for a 133.33Hz rate
  Timer1.initialize(1000); // fire the interrupt every 1000 microseconds
  Timer1.attachInterrupt(display); // call display() at every interrupt.

The main loop simply steps through all of our animation frames.

  void loop() {
    // increment the frame index and wrap if necessary
    if (++curFrameIdx == FRAMECNT) {
      curFrameIdx = 0;
    }

    // select frame for display
    setFrame(curFrameIdx);

    delay(400); // wait time between frames in ms - reduce this value to speed up the animation, increase it to slow it down
  }

The entire sketch is attached below.
 
« Previous StepDownload PDFView All StepsNext Step »
21 comments
Jun 19, 2011. 8:02 PMfostersfriend says:
(removed by author or community request)
Jun 21, 2011. 12:34 PMfostersfriend says:
Timer3.initialize(1000);

it says this was never declared
Jun 21, 2011. 7:11 PMfostersfriend says:
Dunno i must have played with it and forgot
May 15, 2011. 9:44 AMholmez says:
hello there,

ive added two buttons to the program which are ment to display two different patterns depending on which button is pushed. currently i have it so when a button is pushed it displays the first frame and when it is pushed again it shows next frame, though i want it to display the full sequence. Any help would be greatly appreciated!

Many Thanks,
holmez

p.s. great post!
May 17, 2011. 12:39 PMholmez says:
Thank you brother, worked it out in the end just had to concentrate a bit more. Got that working and a tempo gauge cause couldnt get my hand on a jack plug!

Brilliant project!
May 17, 2011. 2:06 PMholmez says:
No Bother!

thanks again!
Mar 2, 2011. 2:08 PMbesomuk says:
I'm not shure what i'm doing wrong.
When i write

digitalWrite(cols[0],HIGH);
digitalWrite(rows[0],LOW);

entire 0 row lights up, not just (0,0) LED


Mar 2, 2011. 11:09 PMbesomuk says:
Testmode works fine.

But before i proceed...

Does display () function must follow digitalWrite statements? I can fire up only one LED if display() follows them.
Mar 3, 2011. 2:07 PMbesomuk says:
I reviewed entire circuit it was my mistake...I messed with wires :)
Now it's ok.
Thank you very much for your help and great project.
Feb 23, 2011. 3:29 PMMark3rr says:
Oh Yeah, it works..
The TimerOne Library wasn`t in the right place....
I´m working on a mac and there is no Arduino folder except in the dokuments folder..
Thank you for your help...
Feb 24, 2011. 6:20 AMpepehdez says:
You are welcome, it's good to hear it's working now, what did you do with your matrix, table, wall or something else ? Are you creating your own animations ?
Mar 2, 2011. 4:10 PMMark3rr says:
Oh yes, I used the frame creator that you linked. This is really a great instruction and a superb support. THumbs up :)
Mar 3, 2011. 7:29 AMpepehdez says:
All credit goes to lincomatic ( the author ), I'm just paying it forward.
Feb 25, 2011. 6:11 PMakami says:
Hello, can you help me with bitMask for 8x8 matrix?
Feb 23, 2011. 10:17 AMMark3rr says:
Oh I tried... But I only get some errors...
Feb 23, 2011. 10:38 AMpepehdez says:
Maybe you change something in the code by mistake, I recommend downloading all files and then uploading them again to the arduino and start fresh.

Did you unzip the Timer1 library to the libraries folder ?

Feb 23, 2011. 6:38 AMMark3rr says:
Hi,
i wan`t to run some frames from the file frames.h
but my Arduino only runs the testmode.
Could somebody help me?
(I am a newbie)
Feb 23, 2011. 7:25 AMpepehdez says:

Hi, add " // " before " #define TESTMODE " to disable it.

Pro

Get More Out of Instructables

Already have an Account?

close

All Steps Viewing
View all steps of an Instructable on the same page when you're a Pro Member.

Upgrade to Pro today!
32
Followers
2
Author:lincomatic(Lincomatic's Blog)
For more information on my projects as they develop, visit my blog: http://blog.lincomatic.com