Controlling an 8x8 bicolor LED display

I’m starting a project that involves controlling an 8x8 bicolor LED display http://www.sparkfun.com/products/682. Ultimately, I want to try to program the game “Connect Four” into it, and be able to control where you “drop” your pieces using a few push buttons.

The first step, I would assume, would be to make it display/scroll text, maybe make it play Conways Game of Life, etc. I was originally going off of this tutorial (http://www.instructables.com/id/LED-matrix-using-shift-registers/), but after I bought my parts I realized their code is not for an arduino. So, the parts I currently have are: The matrix, 2 HEF4794 shift registers, 1 MIC2981 driver, and various resistors. My plan was to use one shift register per color and the driver for the rows.

The problem is that there isn’t much information regarding my shift registers. Most guides I see use 74HC595 shift registers, but the tutorial I referenced said that the 4794 is more convenient, as it can handle more current without any additional parts. Right now, I can’t get the 4794 to work, though. I am trying to adapt this site’s guide (http://tronixstuff.wordpress.com/2010/04/30/getting-started-with-arduino-–-chapter-four/) to my shift register, and am using the following code:

/*
Exercise 4.1
Count from 0~255 in binary with the 74HC595 shift register
CC by-sa 3.0
http://tronixstuff.wordpress.com
based on work by Carlyn Maw and Tom Igoe
*/
int latchpin = 8; // connect to pin 12 on the '595
int clockpin = 12; // connect to pin 11 on the '595
int datapin = 11; // connect to pin 14 on the '595
void setup()
{
pinMode(latchpin, OUTPUT);
pinMode(clockpin, OUTPUT);
pinMode(datapin, OUTPUT);
}
void loop()
{
for (int loopy=0; loopy<256; loopy++)
{
digitalWrite(latchpin, LOW);
shiftOut(datapin, clockpin, MSBFIRST, loopy);
digitalWrite(latchpin, HIGH);
delay(200);
}
}

Using the 4794’s data sheet (http://www.datasheetcatalog.org/datasheet/philips/HEF4794BT.pdf) and this code, I connected the 4794’s pin1 (STR/latch) to arduino pin8, 4794 pin2 (data) to arduino pin11, 4794 pin3 (clock) to arduino pin11, 4794 pin8 (Vss) to ground, 4794 pin15 (enable) to +5V, an 4794 pin16 (Vdd) to +5V. This program, as I understand, should output an 8bit number in binary (determined by “loppy”) spread across the 8 4794 outputs. When I hook it up however, I am not getting any voltages out of any of the outputs, so I can’t even hope to hook the outputs up to my LEDs to display this counter.

Anyone think they can help with this? Sorry if this is long winded, just a little frustrated after many hours of it not working :(.

“not getting any voltages out of any of the outputs” so they are just sitting at 0V?

These are Open Drain outputs, meaning they are intended to pull cathodes low to turn on LEDs.
Try putting pullup resistors on the outputs, see if they switch.

CrossRoads: "not getting any voltages out of any of the outputs" so they are just sitting at 0V?

These are Open Drain outputs, meaning they are intended to pull cathodes low to turn on LEDs. Try putting pullup resistors on the outputs, see if they switch.

Yes, they are sitting at 0V. So, I just added a resistor to each output (each going to +5V) and it works (sort of)! My 8 LEDs are counting, but rather than counting by turning LEDs on, it counts by turning them off. Example: when it should turn on the LEDs like 00000001, 00000010, 00000011, 00000100, etc., but instead the LEDs show 11111110, 11111101, 11111100, 11111011, etc.

EDIT: actually, I think I figured it out. The pullup resistors just put all of my output pins HIGH(?), so all of my LEDs will be turned on. If the DATA pin receives 00000001, then current still flows for all of the 0's but not the 1. To fix this, I told my program to count down from 256 instead of counting up to it. I'm still trying to figure out what the pull-up resistor is doing exactly...

Yep, negative logic. Low is on, high is off. You could wire the driver output to the junction of the resistor & anode, with cathode to ground, then a high out will let anode stay high while a low out will pull the anode low and be off.

CrossRoads:
You could wire the driver output to the junction of the resistor & anode, with cathode to ground, then a high out will let anode stay high while a low out will pull the anode low and be off.

You mean keep the shift register as is, then add the MIC2981 to the anodes (which are currently just connected to ground through a resistor)?

No. Your plan was to use MIC2981 to drive the a row (cathode) low, and any column that had a high on its anode woluld then light up, yes? That is the essence of multiplexing. The anodes control what lights up in a column while you scan thru the rows:

Column setup, row1 low, hold a few mS maybe, back high column setup, row 2 low ... : : column 8 setup, row 8 low ...

and repeat.

So to keep a column from lighting up when the row was low, you would also take the anode low.

CrossRoads: You could wire the driver output to the junction of the resistor & anode, with cathode to ground, then a high out will let anode stay high while a low out will pull the anode low and be off.

CrossRoads: Your plan was to use MIC2981 to drive the a row (cathode) low, and any column that had a high on its anode woluld then light up, yes?

I don't think I understand exactly how to wire this up. I understand the code I think, but am having trouble hooking it all up. Right now I have arduino outputs 0-7 hooked into MIC2981 inputs 1-8. Are the MIC2981 outputs suppose to go through a resistor to the rows, or to that junction between the resistor and anode? And just to be clear, the cathodes are the rows and the anodes are the columns going into my shift register?

Other than that, the code, as you say, should just be: give the 8-bit number for the columns that go into row 1 to the shift register, turn row 1 low, wait a few ms, turn it high....give the 8-bit number for the columns that go into row 8 to the shift register, turn row 8 low, wait a few ms, turn it high, etc.

thanks for the help, hopefully I can put this all together soon :)

Let me re-read, forgot where this was going - hang on a few minutes.

Okay, here’s my read of how you’re gonna be stuck using these parts.
The MIC2981 is a current source.
The HEF4794 is a current sink.
These pair of parts I think would be better for a Common Anode array instead of Common Cathode.
Due to the HEF4794 current sink of 40 mA compared to the current source of 350mA for the MIC2981, you will have to set up a row, then turn on no more than 2 columns (like a red/green pair if you want different colors), using PWM on both if you want to mix the color intensity.
Turn on the 8 4791 outputs, pulse the 2 MIC2981 outputs for the on duration you want.

HEF4794-MIC2981_array.jpg

Thank you for such detailed and well thought out answers, I really appreciate it.

So, I am using the MIC2981 to power the columns now while the shift register (4794) powers the rows?

If that is the case, here's what I think I'm understanding: I hook up the 8 2981 inputs to the arduino, and the outputs to red column 1-8. I hook up the 8 rows to the 4794, and attach clock/data/latch appropriately to the arduino as before. Do I need pull-down resistors to ground from the 4794 outputs this time? I am trying to just turn on the top left pixel (column 1, row 1) to see if I can pinpoint pixels this way, but can't really get it to work =\

When scanning, I am also scanning columns now. Put in row data for column 1, scan column 1, put in row data for column 2, scan column 2, etc.

For now I am ignoring the green LEDs and PWM, I am trying to control individual pixels with just red for simplicity.

“So, I am using the MIC2981 to power the columns now while the shift register (4794) powers the rows?” Yes.

“Do I need pull-down resistors to ground from the 4794 outputs this time?” No. You have pin 8 connected to ground that is your low level.

“I am also scanning columns now” I wouldn’t say ‘also’ , I would say ‘instead of’.

“in row data for column 1, scan column 1, put in row data for column 2, scan column 2, etc.”

Let me back up a step - I just noticed the MIC2981 does not have an output enable, so PWM there will be impossible. 1 problem solved.
So do your scanning like this:
Shift a 1 into the MIC2981 using an indivdual digitwrite 1, and clock it 1 pulse.
Shift your row data into the HEF4794.
Turn on the 4794 output enable for however long you want the lights on.
Turn off the output enable.
digital write 0 to the 2981, give it one clock pulse to move the existing 1 over 1 position.
Shift row data into the 4794.
Turn on output enable
Turn off output enable

repeat 6 more times.

CrossRoads: Let me back up a step - I just noticed the MIC2981 does not have an output enable, so PWM there will be impossible. 1 problem solved. So do your scanning like this: Shift a 1 into the MIC2981 using an indivdual digitwrite 1, and clock it 1 pulse. Shift your row data into the HEF4794. Turn on the 4794 output enable for however long you want the lights on. Turn off the output enable. digital write 0 to the 2981, give it one clock pulse to move the existing 1 over 1 position. Shift row data into the 4794. Turn on output enable Turn off output enable

repeat 6 more times.

I understand the entire process except these two steps. The MIC itself doesn't have a clock pin, so I don't know how to clock it/shift data over?

Sorry, I was thinking of it as shift register. What I intended was, walk a 1 across the 8 columns . Could be 8 arduino outputs that you write hi lo, or you may have another shift register like HC595 and you buffer its outputs with the MCI2891 to drive the columns. If a part like HC595, then you could take advantage of PWM later on. Be sure to put pulldown resistors on its outputs so when you PWM the OE line the outputs get pulled low when the 595 goes into tri-state mode for example.

Thanks so much! I am (sort of) getting the LEDs to scroll left to right, row by row using your setup and this code:

int latchpin = 8; // connect to pin 12 on the '595
int clockpin = 12; // connect to pin 11 on the '595
int datapin = 11; // connect to pin 14 on the '595
int oen = 9; // output enable
int column1 = 0; //all columns are red
int column2 = 1;
int column3 = 2;
int column4 = 3;
int column5 = 4;
int column6 = 5;
int column7 = 6;
int column8 = 7;

void setup()
{
pinMode(latchpin, OUTPUT);
pinMode(clockpin, OUTPUT);
pinMode(datapin, OUTPUT);
pinMode(oen, OUTPUT);
pinMode(column1, OUTPUT);
pinMode(column2, OUTPUT);
pinMode(column3, OUTPUT);
pinMode(column4, OUTPUT);
pinMode(column5, OUTPUT);
pinMode(column6, OUTPUT);
pinMode(column7, OUTPUT);
pinMode(column8, OUTPUT);
}

void loop()
{
for (int row=0; row<8; row++)
{
for (int column=0; column<8; column++)
{
digitalWrite(column, HIGH);
digitalWrite(latchpin, LOW);
shiftOut(datapin, clockpin, MSBFIRST, pow(2,row));
digitalWrite(latchpin, HIGH);
digitalWrite(oen, HIGH);
delay(50);
digitalWrite(oen, LOW);
digitalWrite(column, LOW);
}
}
}

The only problem right now is that it scans the rows, one by one, until the third row, at which point it starts doing something funky. However, even knowing I can make the LEDs light up using these chips is a huge improvement :slight_smile: Can’t thank you enough. If I run into further problems down the road I might stop by here again (if I’m not a nuisance :p).

EDIT: my friend suggested replacing pow(2,row) with 1<<row. seems to work. not sure why one worked and the other didn’t

I do not see ‘pow’ defined, so you may be getting unknown data written out for pow(2,row)

column & row are not attached to any pins going out.

I think what you intended was to have an array for the columns?
column[0] thru column[7]

in pre-setu code:
int c=0; // used to scan the array
int r=0; // used to scan the array
char column[8]; // define array of 8 bytes
column[0] = 0; // load it up
column[1] = 1; //etc to define the pins used

in setup:
pinMode (column[0], OUTPUT); // set the pins as outputs
pinMode (coulmt[1], OUTPUT);

read this:
http://arduino.cc/en/Reference/Bitshift

in loop:
for (r = 0; r <8, r<<){ // should result in B00000001, B00000010, B00000100, B00001000, etc
for (c=0; c<8; c++){ // turns on the columns 1 by 1
digitalWrite (column

, HIGH);  // turns 1 column driver
   // row stuff
   what was intended here?  shiftOut(datapin, clockpin, MSBFIRST, pow(2,row));
   maybe just shiftOut(datapin, clockpin, MSBFIRST, r); 
   then will have position (row, column) on:  (0,0), (0,1), (0,2), (0,3), ... (7,5), (7,6), (7,7) 
  // more row stuff, ending with OEN going off
   digitalWrite (column[c], LOW);   // turn off the column drive
   } // next column
} // next row

Next step is defining an 8x8 or 8x16 (2 colors) arrays and sending those out.
Or changing the contents of an array on the fly continuously sending that out

Add comments in your code, will definitely make it more maintainable as you make changes.

couple quick questions on code:

int c=0; // used to scan the array
int r=0; // used to scan the array

why do we define this as 0, but later use c and r as variables we increment with each loop?

char column[8]; // define array of 8 bytes
column[0] = 0; // load it up
column[1] = 1; //etc to define the pins used

not entirely sure what this does here. I looked at the reference and they have a few techniques for creating arrays and they don’t really say much about using “char”. would the syntax be along the lines of
char column[8] = {0, 1, 2, 3, 4, 5, 6, 7};
or am I missing something here

for (r = 0; r <8, r<<){

should r<< read 1<<r?

other than that I think I understand the rest of the code

I am trying to use red columns on arduino pins 1-8, and green columns on pins A0, A1, A2, A3, A4, A5, 8, 9, if that helps

“am trying to use red columns on arduino pins 1-8, and green columns on pins A0, A1, A2, A3, A4, A5, 8, 9, if that helps”

little conflict - you have 8 used twice. Did you mean 0-7? That would agree with the array.
Call the pins 14-19 when you create the array (vs A0-A5, avoid any compiler surprises mixing analon & digital pins in an array).

“int c=0; // used to scan the array
int r=0; // used to scan the array”
This creates the 2 variables, and gives an initial known value. I think if it as good programming practice.
byte c; & byte r; would work as well since they are just used to control 8 bit.

char column[8] = {0, 1, 2, 3, 4, 5, 6, 7};
That probably works fine as well. My code tends to be written out less compactly the first pass thru, gets cleaned up as I get things working. This is a great example of that.
Anyway, the point was just to put that stuff in an array.

The bit shifting for r, I may have that a little off. I think you want to have B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000, so really want to start at r=1 and not 0; if r is an int (16 bit) than want to end at r = 0x0100, and for shifting, I think r<<1 will shift left 1 bit if I’m reading the reference page correctly.

Nice thing about software - easy to tweak if it’s not quite right :slight_smile:

So after tweaking around with it a little, I finally got it to scroll text! I based much of my code off of this guys tutorial: http://www.instructables.com/id/810-LED-Matrix-with-4017/step5/It-is-time-to-program/. Here is the actual code in case someone trying to make something like this in the future stumbles across this topic:

int latchpin = 13; // connect to pin 1 on MIC2981
int clockpin = 12; // connect to pin 3 on MIC2981
int datapin = 11; // connect to pin 2 on MIC2981
int oen = 10; // output enable, connect to pin
int xx; // used to define Letter
int yy; // used define row of Letter
int rRow[8] = {0, 1, 2, 3, 4, 5, 6, 7}; // red columns of 8x8 display, connected to these pins on arduino
int gRow[8] = {14, 15, 16, 17, 18, 19, 8, 9}; // green columns on this 8x8 display, connected to these ins on arduino

// define all Letters here
#define A {B00111100,B01000010,B01000010,B01111110,B01000010,B01000010,B01000010,B01000010}
#define B {B01111100,B01000010,B01000010,B01111100,B01000010,B01000010,B01000010,B01111100}
#define C {B00111110,B01000000,B01000000,B01000000,B01000000,B01000000,B01000000,B00111110}
#define D {B01111100,B01000010,B01000010,B01000010,B01000010,B01000010,B01000010,B01111100}
#define E {B01111110,B01000000,B01000000,B01111100,B01000000,B01000000,B01000000,B01111110}
#define F {B01111110,B01000000,B01000000,B01111100,B01000000,B01000000,B01000000,B01000000}
#define G {B00111100,B01000010,B01000010,B01000000,B01000111,B01000010,B01000010,B00111100}
#define H {B01000010,B01000010,B01000010,B01111110,B01000010,B01000010,B01000010,B01000010}
#define I {B00111000,B00010000,B00010000,B00010000,B00010000,B00010000,B00010000,B00111000}
#define J {B00011100,B00001000,B00001000,B00001000,B00001000,B01001000,B01001000,B00110000}
#define K {B01000100,B01001000,B01010000,B01100000,B01010000,B01001000,B01000100,B01000010}
#define L {B01000000,B01000000,B01000000,B01000000,B01000000,B01000000,B01000000,B01111110}
#define M {B01000100,B10101010,B10010010,B10010010,B10000010,B10000010,B10000010,B10000010}
#define N {B01000010,B01100010,B01010010,B01001010,B01001010,B01001010,B01000110,B01000010}
#define O {B00111100,B01000010,B01000010,B01000010,B01000010,B01000010,B01000010,B00111100}
#define P {B00111100,B01000010,B01000010,B01000010,B01111100,B01000000,B01000000,B01000000}
#define Q {B00111100,B01000010,B01000010,B01000010,B01001010,B01000100,B00111010,B00000000}
#define R {B00111100,B01000010,B01000010,B01000010,B01111100,B01000100,B01000010,B01000010}
#define S {B00111100,B01000010,B01000000,B01000000,B00111100,B00000010,B01000010,B00111100}
#define T {B11111110,B00010000,B00010000,B00010000,B00010000,B00010000,B00010000,B00010000}
#define U {B01000010,B01000010,B01000010,B01000010,B01000010,B01000010,B01000010,B00111100}
#define V {B01000010,B01000010,B01000010,B01000010,B01000010,B01000010,B00100100,B00011000}
#define W {B10000010,B10000010,B10000010,B10000010,B10010010,B10010010,B10101010,B01000100}
#define X {B01000010,B01000010,B00100100,B00011000,B00011000,B00100100,B01000010,B01000010}
#define Y {B10000010,B01000100,B00101000,B00010000,B00010000,B00010000,B00010000,B00010000}
#define Z {B01111110,B00000010,B00000100,B00001000,B00010000,B00100000,B01000000,B01111110}
#define a {B00000000,B00000000,B00000000,B00111000,B01000100,B01000101,B01000101,B00111010}
#define b {B00000000,B00100000,B00100000,B00100000,B00111100,B00100010,B00100010,B00111100}
#define c {B00000000,B00000000,B00000000,B00111100,B01000000,B01000000,B01000000,B00111100}
#define d {B00000000,B00000100,B00000100,B00000100,B00111100,B01000100,B01000100,B00111100}
#define e {B00000000,B00000000,B00111000,B01000100,B01000100,B01111000,B01000000,B00111100}
#define f {B00011000,B00100100,B00100000,B00100000,B01110000,B00100000,B00100000,B00100000}
#define g {B00011100,B00100010,B00100010,B00011110,B00000010,B00000010,B00010010,B00001100}
#define h {B01000000,B01000000,B01000000,B01000000,B01111000,B01000100,B01000100,B01000100}
#define i {B00000000,B00010000,B00000000,B00010000,B00010000,B00010000,B00010000,B00010000}
#define j {B00000000,B00010000,B00000000,B00010000,B00010000,B00010000,B01010000,B00110000}
#define k {B00000000,B00000000,B01001000,B01010000,B01100000,B01100000,B01010000,B01001000}
#define l {B01000000,B01000000,B01000000,B01000000,B01000000,B01000000,B01000000,B01000000}
#define m {B00000000,B00000000,B00110100,B01001010,B01001010,B01001010,B01001010,B01001010}
#define n {B00000000,B00000000,B01111000,B01000100,B01000100,B01000100,B01000100,B01000100}
#define o {B00000000,B00000000,B00000000,B00011100,B00100010,B00100010,B00100010,B00011100}
#define p {B00000000,B00000000,B00011100,B00100010,B00100010,B00111100,B00100000,B00100000}
#define q {B00000000,B00111000,B01000100,B01000100,B00111100,B00000100,B00000100,B00000100}
#define r {B00000000,B00000000,B00111000,B01000000,B01000000,B01000000,B01000000,B01000000}
#define s {B00000000,B00111000,B01000100,B01000000,B00111000,B00000100,B01000100,B00111000}
#define t {B00100000,B00100000,B00100000,B01111000,B00100000,B00100000,B00100010,B00011100}
#define u {B00000000,B00000000,B00000000,B01000100,B01000100,B01000100,B01000100,B00111000}
#define v {B00000000,B00000000,B01000100,B01000100,B01000100,B01000100,B00101000,B00010000}
#define w {B00000000,B00000000,B00000000,B01000100,B01000100,B01010100,B01010100,B00101000}
#define x {B00000000,B00000000,B00000000,B00000000,B00100100,B00011000,B00011000,B00100100}
#define y {B00000000,B01000100,B01000100,B00111100,B00000100,B00000100,B00000100,B00111000}
#define z {B00000000,B00000000,B00000000,B01111100,B00001000,B00010000,B00100000,B01111100}
#define _ {B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000}
#define num_0 {B00111100,B01000110,B01001010,B01001010,B01001010,B01010010,B01100010,B00111100}
#define num_1 {B00001000,B00011000,B00001000,B00001000,B00001000,B00001000,B00001000,B00011100}
#define num_2 {B00111100,B01000010,B00000100,B00001000,B00010000,B00100000,B01000000,B01111110}
#define num_3 {B01111110,B00000010,B00000010,B00011100,B00000010,B00000010,B01000010,B00111100}
#define num_4 {B00000100,B00001100,B00010100,B00100100,B01000100,B01111110,B00000100,B00000100}
#define num_5 {B01111110,B01000000,B01000000,B00111100,B00000010,B00000010,B00000010,B01111100}
#define num_6 {B00111100,B01000000,B01000000,B01111100,B01000010,B01000010,B01000010,B00111100}
#define num_7 {B01111110,B00000010,B00000100,B00001000,B00010000,B00010000,B00010000,B00010000}
#define num_8 {B00111100,B01000010,B01000010,B00111100,B01000010,B01000010,B01000010,B00111100}
#define num_9 {B00111100,B01000010,B01000010,B01000010,B00111110,B00000010,B00000010,B00111100}
#define times {B00000000,B01000010,B00100100,B00011000,B00011000,B00100100,B01000010,B00000000}

const int numLetters = 13; // number of Letters you want to display
byte Letters[numLetters][8]={,H,E,L,L,O,,W,O,R,L,D,_}; // order of Letters (array with numLetters across, 8 down)

void setup()
{
pinMode(latchpin, OUTPUT); // defining outputs
pinMode(clockpin, OUTPUT);
pinMode(datapin, OUTPUT);
pinMode(oen, OUTPUT);
pinMode(rRow[0], OUTPUT);
pinMode(rRow[1], OUTPUT);
pinMode(rRow[2], OUTPUT);
pinMode(rRow[3], OUTPUT);
pinMode(rRow[4], OUTPUT);
pinMode(rRow[5], OUTPUT);
pinMode(rRow[6], OUTPUT);
pinMode(rRow[7], OUTPUT);
pinMode(gRow[0], OUTPUT);
pinMode(gRow[1], OUTPUT);
pinMode(gRow[2], OUTPUT);
pinMode(gRow[3], OUTPUT);
pinMode(gRow[4], OUTPUT);
pinMode(gRow[5], OUTPUT);
pinMode(gRow[6], OUTPUT);
pinMode(gRow[7], OUTPUT);
}

void loop()
{
for (int xx=0; xx<numLetters-1; xx++) // loops over the Letters
{
for (int zz=0; zz<8; zz++) // shifts Letters over one byte per loop
{
for (int tt=0; tt<7; tt++) // determines how fast you want your message to scroll horizontally
{
for (int yy=0; yy<8; yy++) // scans the rows
{
byte currentLetter = Letters[xx][yy]; // these two bytes make later formulas easier to write
byte nextLetter = Letters[xx+1][yy];
digitalWrite(rRow[yy], HIGH); // turn on current row, change colors by using gRow or rRow
digitalWrite(latchpin, LOW);
shiftOut(datapin, clockpin, MSBFIRST, ((currentLetter<<zz)+(nextLetter>>8-zz))); // send row data to HEF4794
digitalWrite(latchpin, HIGH);
digitalWrite(oen, HIGH); // light up LEDs
delay(1);
digitalWrite(oen, LOW); // turn off LEDs
digitalWrite(rRow[yy], LOW); // turn off current row, change colors using gRow or rRow
}
delay(1);
}
}
}
}

Unfortunately, my webcam video is deciding to not work, so I can’t upload a video of it :frowning:

thanks again for all the help