Multiplexing 8×8 LED display

Most people multiplex their 8×8 LED displays using 74HC595 shift registers, which are pretty nice. In fact, check out this nice online book for the Arduino:

https://docs.google.com/file/d/0Bw_ruMOtRDDgNXI3OTFGZXhIZ2c/edit provided by http://earthshineelectronics.com/

The design I present uses a 74LS164 shift register and a 74LS138 demultiplexor. Mostly because I had those chips laying around – still, it is worth the analysis to see how these two chips work in tandem to generate a multiplexed display.

Here is a simple demo video.

It is important to note that the current code (see at bottom) is able to display a point at X, Y coordinates, but the code can be adapted to provide a full 8×8 POV-display (i.e. all 64 LEDs appear to be on and individually addressable). That is an exercise left for the future.

The key part is that the shift register can enable any combination of LEDs in a given row and the demux cycles over all the rows, turning on each one at a time (three bit binary input translates into eight possible combinations, or eight outputs in this case). This all happens very quickly, so the display could appear to have all LED turned on when in fact at most eight can be on at a time.

I’m including the schematic, board and code for reference purposes.

Note – the display I happen to be using is twice as large as shown on the board – I made a copy of adafruit’s 8×8 matrix from their Eagle library but didn’t fix the device size. Notice that pin 1 is always lower left when viewing the display from above. Usually the bottom of the display indicates which pin is 1. I’ve had the display for a number of years – there are probably better (brighter) displays out there so I don’t recommend the one I’m using.

Notice also that the board is actually designed so it can be installed as an Arduino shield, which is why the parts are covering the Arduino.

// (C) 2014 zapmaker
// Apache 2.0 license
// Goes with youtube video showing a spiral dot pattern
//
#define srDataPin 2
#define srClockPin 3
#define demuxAPin 4
#define demuxBPin 5
#define demuxCPin 6
#define demuxEnablePin 7

#define START_X 3
#define START_Y 3

byte flipLookup[8];
int lastt = 0;
int x = START_X;
int y = START_Y;
int dx[] = { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 };
int wc = dx[0];
int dpos = 0;
int dmax = sizeof(dx) / sizeof(int);

void setup()
{
  // 74LS164 shift register clock/data
  pinMode(srDataPin, OUTPUT);
  pinMode(srClockPin, OUTPUT);
  // 74LS138 demux data/control
  pinMode(demuxAPin, OUTPUT);
  pinMode(demuxBPin, OUTPUT);
  pinMode(demuxCPin, OUTPUT);
  pinMode(demuxEnablePin, OUTPUT);
  

  digitalWrite(srDataPin, LOW);  
  digitalWrite(srClockPin, LOW);
  digitalWrite(demuxAPin, LOW);
  digitalWrite(demuxBPin, LOW);
  digitalWrite(demuxCPin, LOW);
  digitalWrite(demuxEnablePin, LOW);

  //Serial.begin(115200);
  
  unsigned long sector, fileSize;
  // clear s/r
  for (byte i = 0; i < 8; i++)
  {
    digitalWrite(srClockPin, HIGH);
    digitalWrite(srClockPin, LOW);
  }

  byte initVal = 7;
  for (byte i = 0; i < 8; i++, initVal--)
  {
    flipLookup[i] = initVal;
  }
}

void loop()
{
  int curr = millis();
  if ((curr - lastt) > 10)
  {
    lastt = curr;
          
    switch (dpos & 3)
    {
      case 0:
        x++;
        break;
      case 1:
        y++;
        break;
      case 2:
        x--;
        break;
      case 3:
        y--;
        break;
    }
    
    if (x > 7 || y > 7 || x < 0 || y < 0)
    {
      x = START_X;
      y = START_Y;
      dpos = 0;
      wc = dx[dpos];
    }
    else
    {
      wc--;
      if (wc == 0) 
      {
        dpos++;
        if (dpos >= dmax)
          dpos--;
        wc = dx[dpos];
      }
    }
  }
  
  writeLedMatrix(x, y);
}

void writeLedMatrix(int dc, int sv)
{
  if (dc < 0)
  {
    dc = 0;
  }
  else if (dc > 7)
  {
    dc = 7;
  }
  else if (sv < 0)
  {
    sv = 0;
  }
  else if (sv > 7)
  {
    sv = 7;
  }  

  // this is to save on pins used by the Arduino - let two external chips drive the matrix
  digitalWrite(demuxEnablePin, LOW);
  writeDemux(dc);
  writeShifter(sv);      
  digitalWrite(demuxEnablePin, HIGH);
  delay(10);
}

void writeLedMatrixWithOverflow(int dc, int sv)
{
  word d = 2;
  byte c = 3;
  boolean hit = false;
  if (dc < 0)
  {
    dc = 0;
    hit = true;
    for (byte j = 0; j < c; j++)
      for (byte i = 0; i < 8; i++)
      {
        digitalWrite(demuxEnablePin, LOW);
        writeDemux(dc);
        writeShifter(i);      
        digitalWrite(demuxEnablePin, HIGH);
        delay(d);
      }  
  }
  else if (dc > 7)
  {
    dc = 7;
    hit = true;
    for (byte j = 0; j < c; j++)
      for (byte i = 0; i < 8; i++)
      {
        digitalWrite(demuxEnablePin, LOW);
        writeDemux(dc);
        writeShifter(i);      
        digitalWrite(demuxEnablePin, HIGH);
        delay(d);
      }  
  }
  else if (sv < 0)
  {
    sv = 0;
    hit = true;
    for (byte j = 0; j < c; j++)
      for (byte i = 0; i < 8; i++)
      {
        digitalWrite(demuxEnablePin, LOW);
        writeDemux(i);
        writeShifter(sv);      
        digitalWrite(demuxEnablePin, HIGH);
        delay(d);
      }  
  }
  else if (sv > 7)
  {
    sv = 7;
    hit = true;
    for (byte j = 0; j < c; j++)
      for (byte i = 0; i < 8; i++)
      {
        digitalWrite(demuxEnablePin, LOW);
        writeDemux(i);
        writeShifter(sv);      
        digitalWrite(demuxEnablePin, HIGH);
        delay(d);
      }  
  }  
  else
  {
    digitalWrite(demuxEnablePin, LOW);
    writeDemux(dc);
    writeShifter(sv);      
    digitalWrite(demuxEnablePin, HIGH);
  }
}

void writeDemux(word value)
{
  word newValue = value < 0 ? 0 : value;
  newValue = value > 7 ? 7 : newValue;
  
  newValue = flipLookup[newValue];
  newValue <<= 4;
  
  PORTD = (0x8F & PORTD) | (newValue);
}

void writeShifter(word value)
{
  word newValue = value < 0 ? 0 : value;
  newValue = value > 7 ? 7 : newValue;  
  
  for (byte i = 0; i < 8; i++)
  {
    if (i == newValue)
      digitalWrite(srDataPin, HIGH);
    else
      digitalWrite(srDataPin, LOW);

    digitalWrite(srClockPin, HIGH);
    digitalWrite(srClockPin, LOW);
  }
}