Instructables

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

FeaturedContest Winner

Step 5: Code

Picture of 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.
 
 
Remove these adsRemove these ads by Signing Up
jmaxado9 months ago
Hello
I plan to do this project using 3w leds, so I know I need to place some transistores in the circuit. But using transistores for all pins I need to change the code right.
In you case to turn a certain led on you would put one port high and another low, but using transistors I need to put both ports on.
I am thinking correctly ?
If yes where do I change the code in order to be able to use transistors ?
Thanks

José
lincomatic (author)  jmaxado9 months ago
It's been a while since I wrote this code, but I think all you have to do is change the line at the top of display() from
digitalWrite(rows[row], HIGH); // Turn whole previous row off
to
digitalWrite(rows[row], LOW); // Turn whole previous row off

and the bottom line from
digitalWrite(rows[row], LOW); // Turn whole row on at once (for equal lighting times)
to
digitalWrite(rows[row], HIGH); // Turn whole row on at once (for equal lighting times)

Also, you don't need to light only 2 LED's at a time like I did, since you'll be able to handle the current of a whole row at a time. So your display() function should look like this:

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

// increment row and wrap if necessary
if (++row == DIM) {
row = 0;
}

// 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], HIGH); // Turn whole row on at once (for equal lighting times)
}

Thanks for your reply.
regarding the transistor do you have any sugestion ?
Yesterday I got some BUZ12 but I dont think it will do the trick.

José
typomaniac2 years ago
Hey : )

i built your table with some large blue leds but they did not make bright enough light so i went for straw hat leds which seemed to be fine. For testing purposes i put one of them on a cr2032 battery with 3 V and it worked fine. I replaced one of the leds in my arduino driven matrix with one of the new red leds and was disappointed: It seems that it does only glow with much less intensity than it did before on the battery. Did i do something wrong or does the matrix work this way and there is not enough power for the maximum brightness of the leds?

thank you so much,
tm
p.S: I currently use a 91 Ohm resistor which should be even lower than the one it would need for the red ones
lincomatic (author)  typomaniac2 years ago
In order to keep the current within the alllowed levels on the Arduino, the current is limited. If you want to drive your LED's with more current, you will have to either use transistors, or add a driver IC
thank you for your answer! Oh thats bad. I have no idea how to do this. Do you know an instructable or other tutorial for a 5x5 arduino matrix like yours withh drivers or transistors? maybe i can upgrade my matrix..

thank you very much
tm
Did you ever figured out how to do this project with transistors ?
jmaxado9 months ago
Hello
I plan to do this project using 3w leds, so I know I need to place some transistores in the circuit. But using transistores for all pins I need to change the code right.
In you case to turn a certain led on you would put one port high and another low, but using transistors I need to put both ports on.
I am thinking correctly ?
If yes where do I change the code in order to be able to use transistors ?
Thanks

José
leandro_gs1 year ago
I really don't understand what this piece of code does. I'm trying to make a 3x3 matrix but the first column is on all the time.

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)
  }

what do i have to do to fade the leds instead blink them?
lincomatic (author)  leandro_gs1 year ago
the bits that are set to one are the columns that will light. since this is 5x5 the 5th bit is the leftmost column.
If you need to figure out what's going on w/ a column staying on all the time, try TESTMODE... it will light up the LED's in sequence. You might have a wiring error.

You have to use PWM to fade them. I don't have time to write the code for you
(removed by author or community request)
lincomatic (author)  fostersfriend3 years ago
Most likely, you didn't install the TimerOne library correctly. What's the error you're getting?
Timer3.initialize(1000);

it says this was never declared
lincomatic (author)  fostersfriend3 years ago
There's no Timer3 in daftpunk.pde.. what file is causing that error?
Dunno i must have played with it and forgot
holmez3 years ago
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!
lincomatic (author)  holmez3 years ago
the Frames[] variable in Frames.h is what contains the frames. You could make two separate variables, one for each sequence, say Frames1[] and Frames2[] and then change the code to switch between them. depending on the button press
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!
lincomatic (author)  holmez3 years ago
Glad you go it working. Sorry it took me so long to reply.. the message was in my spam folder
No Bother!

thanks again!
besomuk3 years ago
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


lincomatic (author)  besomuk3 years ago
Please uncomment

//#define TESTMODE

by removing the leading // like this:

#define TESTMODE

and run the sketch and tell me what it looks like.
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.
lincomatic (author)  besomuk3 years ago
I'm not sure what you're doing... did you change my code?
display() does all the writing to the ports. the columns are controlled from PORTB and the rows are controlled by digitalWrite().

back to your example yesterday:

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

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

This is because you need to use digitalWrite(cols[i],LOW) to turn off all the other columns where i=1,2,3,4
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.
lincomatic (author)  besomuk3 years ago
You're welcome. I'm glad you got it working.
akami3 years ago
Hello, can you help me with bitMask for 8x8 matrix?
lincomatic (author)  akami3 years ago
For an 8x8 matrix, it's not as straightforward to use direct port manipulation. There is an example using digitalWrite() here: http://www.arduino.cc/playground/Main/DirectDriveLEDMatrix

Make sure to use resistors, though! Also, their code turns on a whole row at a time instead, so unless you modify it to only turn on 2 LED's at a time, you will need to limit the current to 5mA each LED (40mA/8 = 5mA).
Mark3rr3 years ago
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...
Mark3rr3 years ago
Oh I tried... But I only get some errors...
Mark3rr3 years ago
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)