Arduino Workshop

What choices do you have when trying to make an LED matrix and how do you go about driving them.

 
 

The LED matrix, while not an absolute beginners projects, is one all must come to grips with sooner rather than later. However, it includes some concepts that can take some getting used to. So let’s see what a LED matrix is and how to make one.


A matrix is an arrangement of objects, in this case LEDs, arranged in a grid of rows and columns. This is just the electrical topology, physically it can be arranged in any pattern or grid as you see fit. The point is that with a matrix arrangement we can control a lot of LEDs with just a few output pins. The number of LEDs you can control is the product of the number of columns and rows. That is, suppose you have four columns and six rows you can control 4 X 6 = 24 LEDs. The most usual arrangement is an eight by eight matrix that gives a total of 64 LEDs from 16 output pins. There are ways of reducing this requirement even further as we will see later on.


One of the main points about a matrix is that we can’t control all the LEDs at the same time. You can only turn on the LEDs that lie on one row or columns at any instant. Whether it is a row or column depends on how the matrix is wired up. What ever way round you have it you can only have one set on at a time. However, you can play a trick, by flashing each one in turn fast enough so that they appear to be on all at the same time. This is done by the persistence of vision of the eye, and if it is done fast enough it doesn't appear to be flickering.

 

LED Matrix

The basic idea is that both ends of the LED are under control, it is only when the anode is high and the cathode is low that the LED lights up. Here we see one LED lit (green).

If we have two columns low then two LEDs on the row light up. Note that the high line (row) has to supply (source) the current for two LEDs where as the columns only have to connect to ground (sink) only the current for one LED.

There are two ways of making a matrix, row scanning or column scanning. With each data is supplied to one side of the matrix and a walking bit applied to the adjacent side. A walking bit is simply a sequence where there is one different bit that changes position each step of the sequence. So a walking one is a line of bits that are all zero except one, this position changes at each step like this:-

10000000

01000000

00100000

00010000

00001000

00000100

00000010

00000001

The data is the bit pattern that defines what the pattern the matrix shows. Here is an example of a pattern produced by a row scan. Link for if you can’t see this animation. Link

Notice that the walking bit is a walking one on the rows, at each step the column is fed with data that define the pattern of LEDs that you see. In this example each row driver has to supply the current for up to eight LEDs at any one time where as the columns only have to sink the current for one LED.


In this next example the column scan is used. The walking bit is a walking zero and the data that defines the pattern is applied to the rows. Here the row only sources the current for one LED at any one instant of time where as the column has to sink the current of up to eight LEDs. Link for if you can’t see this animation. Link.

If you want to drive a matrix with an arduino you must use an external driver for the current source or current sink. Despite some tutorials suggesting otherwise, you will over stress the arduino by many times above  the absolute limit if you try. These tutorials (one on the official Arduino site) are very bad and will eventually destroy your Arduino although you might think it works for some time eventually the Arduino will fail. The other point is that current through the LED must be limited some how. The simplest way of doing this is with a series resistor. However, the resistor must be in the line that only takes the  current for one LED. If it were otherwise then the brightness of the LEDs would change depending on how many LEDs are on at any one time. The simplest arrangement with an Arduino is to have a column scan with an external current sink in this form.

The column sink circuit can be the ULN2803 which consists of eight Darlington  drivers conveniently in one package. The collective current that these chips can switch is about 650mA at any one time and so allows a maximum current of  650 / 8 = 80mA per LED. However in practice this is limited by the amount of current the arduino pins on the rows can source. This should be less than 40mA. Where as an LED normally takes a maximum of 20mA of continuos current the fact that any one LED is only on for one eighth of the time means you can put more current in it, this is known as the pulsed current or peak current. It is normal to overdrive the LEDs like this to compensate for the fact that as they are only on for one eighth of the time they do not appear as bright as LEDs that are on for all the time. However there are many other types of current sink circuits like simple transistors or FETs. Note that these driver circuits are inverting, that means you need a logic one to sink the current. Therefore the walking zero pattern has to be a walking one pattern


If you want to go for a Row Scan matrix then you need an external current source driver, using a circuit like this:-

The Row source circuit can use a high sided driver like the ULM2981 or  BCR450 however these are more expensive than current sink circuits. It is easy enough to use a PNP transistor or P-channel FET to supply the drive for the rows. These also invert the signal so the walking one pattern must be a walking zero sent from the Arduino.

If you have higher current requirements with the LEDs you are using you may have to use an external driver for both current source and sink.

In order to cut down on the number of pins you need from the Arduino you can use a demultiplexer like the 74LS42 Decoder or the 74LS145. With these chips you only need three lines to select which one, out of eight lines, goes low. You can also do a similar thing on the current sink circuits,

The 74LS series of chips will happily sink 20mA so they can be connected directly to the columns.


If you do this on both row and column then you can common the enable line on each chip and so reduce the number of lines to control an 8 by 8 matrix down from sixteen to seven.


Instead of using these chips you can also use shift registers to drive the matrix, this ca reduce the pin count even further down to three lines if you cascade two of them. Note this will take longer to refresh and normally a shift register can only source or sink enough current for one LED.

Software to drive a matrix depends on how the matrix is wired up but basically it has to do two things.

  1. 1)Change the LEDs currently on.

  2. 2) Know when to change them.


Changing the LEDs is best done in a function with a descriptive name like refresh(); the data that defines the matrix display should be held in an array so that you can access it sequentially.  This should do the following:-

  1. 1)Remove the walking bit so that all LEDs are off.

  2. 2)Increment a count that tells you what state the walking bit is in, it also tells you what data to use.

  3. 3)Change the data lines to reflect the next group of data.

  4. 4) Set the walking bit so that LEDs are on.

  5. 5)Return from the function.


This example assumes a walking one is driving current sinks on the columns, and the data is presented to the rows.

long int refreshDue;

long int refreshTime = 10; // time in milliseconds between refresh

void loop(){

if(millis() >= refreshDue){

   refreshDue = millis() + refreshTime;

   refresh();

  }

// the rest of the stuff in your program

} // end of the loop

There are two ways of making sure that this function is called regularly, one is to have an interrupt timer call the function and the other is to have it as part of the main loop. Using interrupts has the advantage of not being easy to disrupt with other program functions but it can be a bit tricky and complex to set up the timer. Also spending a long time in an interrupt service routine is generally not a good idea. Putting it in the main loop is easy to do, simply set a long int variable called say refreshDue for the next time you need to refresh the display. Then compare it to the current time given by millis(), after you have called the refresh function then set the time again for the next refresh like this:-

int rowCount = 0;

byte rowPin[ ] = { 2, 3, 4, 5, 6, 7, 8, 9}; // change these to the pins you use

byte colPin[ ] = { 10, 11, 12, 13, 14, 15, 16, 17}; // note pins 14 to 17 are the analogue pins used in a digital mode

byte displayData [ ] = { B11111111, B10000001, B10111101, B10100101, B10100101, B10111101, B10000001, B11111111 }; // display pattern


void setup(){

// set up the pins as outputs

for(int i = 0;  i<8; i++){

   pinMode(rowPin[i], OUTPUT);

   pinMode(colPin[i], OUTPUT);

  }

// the rest of the stuff in your setup

} // end of setup


void refresh(){

byte tempData;

digitalWrite(colPin[rowCount], LOW); // turn off the LEDs

rowCount ++; // increment to next line

if (rowCount == 8) rowCount = 0; // wrap it round so it only counts 0 to 7

// now set the data on the rows

tempData = displayData[rowCount];

for(int i=0 ; i<8; i++){

   if((tempData & 1) == 0) digitalWrite(rowPin[i], LOW); else digitalWrite(rowPin[i], HIGH);

   }

digitalWrite(colPin[rowCount], HIGH); // turn on the LEDs

} // end of refresh

To animate what you are displaying on the matrix all you have to do is to keep changing the data in the the displayData array every eight refreshes or any multiple of eight refreshes. However, the display will flicker or pause if the rest of the stuff in you main loop takes longer that the refreshTime to execute.


There are many variations on this code and it is by no means the best in any way other then, I believe, it’s understandability. Remember what ever you do, it is your project, so understand what you are doing.


Other Notes

Pre Made Matrices

You can buy various sizes of matrix pre built in a single package. If they are square then there is no problem. However, if they are rectangular then you should pay attention to the two types column anode or column cathode as it will define the way you can drive it. More complex are the ones with two coloured LEDs in each position and even an RGB LED in each position.


Hardware support

There are chips like the MAX7219 which will do the refreshing for you in hardware. This saves a lot of CPU time but the chips tend to be expensive. Note that if you use them to drive seven segment displays you have to get the correct type of display, in the case of this chip a common cathode display.


Brightness

Controlling the brightness of individual LEDs in a matrix is just about possible using software but it doesn't leave time to do much else with the processor. Brightness control is best done with hardware support. But that’s another page.


Good luck.