controll 12x12 leds via shiftregister and darlington array

Hey there. I am trying to controll a 12 by 12 led matrix that I build by using 2 74HC164 shift registers in a row for the rows and 2 ULN2803 Darlington arrays for the columns.
The idea is to constantly circle through the rows and always light the right columns.

I wrote the following code, but it does a strange thing: it mostly works as intended, but all leds of a column are on but not the one that should be. So it somehow does it the wrong way.

I hope you can help me. Might be a really stupid problem, but I can’t find it.

@the following code: the SimpleTimer class is a simple timer :wink: that i wrote using micros(). I know that shifout() exists, but it would be overkill for this scenario. Don’t wanna set all leds, but simply circle through them.

#include "Arduino.h"
#include <SimpleTimer.h>


  // Pins
  const int dataPin = 11;
  const int clockPin = 13;
  const int button1 = 8;
  const int button2 = 9;  
  const int ledColumns [12] = {5,4,3,2,1,0,A3,A2,A1,A0,7,6};
  const int senderPin = 10;
  const int timePinSCL = A4;  
  const int timePinSDA = A5;
  
  // Program variables
  int bitmap[12]; // 12*16 bit
  int twoBytes;
  int currentRow = 0;
  int button1LastState= 1;  
  int button2LastState = 1;
  int testx = 0;
  int testy = 0;
  
  // Timer
  SimpleTimer timeTimer(200000);
  SimpleTimer pictureTimer(100000);
  SimpleTimer buttonTimer(50000);
  SimpleTimer rowTimer(500);  

void setup() {  
  
  // init Shiftregisters
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  digitalWrite(dataPin, HIGH);
  digitalWrite(clockPin, LOW);
  
  // init led rows
  for (int i = 0; i<12; i++) {
    pinMode(ledColumns[i], OUTPUT);
    digitalWrite(ledColumns[i], LOW);
  }
  
  // init buttons
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  
  // init timers
  timeTimer.start();
  pictureTimer.start();
  buttonTimer.start();
  rowTimer.start(); 
   
  clearBitmap();
  //fillBitmap();
  // set other pins HIGH?
}


void loop() {
  // update time
  if (timeTimer.isReady()) {
    updateTime(); 
  }
  
  // update picture
  if (pictureTimer.isReady()) {
    clearBitmap();
    setABit(testx,testy);
  }
  
  // update buttons
  if (buttonTimer.isReady()) { 
    int button1State = digitalRead(button1);
    int button2State = digitalRead(button2);

    if ((button1State == 0) && (button1LastState == 1)) {
      testx++;
      testx = testx % 12;
    }

    if ((button2State == 0) && (button2LastState == 1)) {
      testy++;
      testy = testy % 12;
    }
    
    button1LastState = button1State;
    button2LastState = button2State;
  }
  
  // update row
  if (rowTimer.isReady()) {
    refresh(); // paint the next row (and change columns accordingly)
  }
}


// ##########################################################
// bitmap and refresh stuff

void refresh() {

  clearColumns();

  if (currentRow == 0) {
    pushOne();
  }
  else {
    pushZero();
  }  

  setColumns();
  
  currentRow++;
  currentRow = currentRow % 12;
}

void pushOne() {
  digitalWrite(dataPin, HIGH);
  digitalWrite(clockPin, LOW);
  digitalWrite(clockPin, HIGH);
}

void pushZero() {
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  digitalWrite(clockPin, HIGH);
}

void clearColumns() {
  for (int i = 0; i < 12; i++) {
    digitalWrite(ledColumns[i], LOW);
  }
}

void setColumns() {
  if (currentRow < 12) {
    for (int i = 0; i < 12; i++) {
      digitalWrite(ledColumns[i], bitRead(bitmap[currentRow], i));
    }
  }
}

void clearBitmap() {
  for (int x = 0; x < 12; x++) {
    for (int y = 0; y < 12; y++) {
      clearABit(x,y);
    }  
  }
}

void fillBitmap() {
  for (int x = 0; x < 12; x++) {
    for (int y = 0; y < 12; y++) {
      setABit(x,y);
    }  
  }
}

void setABit(int x, int y) {
  bitSet(bitmap[y], x);
}

void clearABit(int x, int y) {
  bitClear(bitmap[y], x);
}


// ##########################################################
// time update stuff
void updateTime() {}

Or is this the wrong forum? Should this be in the programmers forum?

I think either forum is good (just don't start doubleposting).

It's hard to say (your code is a hard to follow, also not knowing the code of your timer class). Also you don't really describe the problem you're having that well (it's mostly working, but not the leds you expect are lit? what does this mean? what are you expecting, what are you seeing?)

Can you also explain a bit how your code is supposed to work? how you build the lines & columns? you've split the logic over many little method manipulating global variables. It's quite complicated to follow the logic like that.

Also, it should be possible for you to find out yourself: you've got all this code that does a lot of things. Why not strip it down a bit, remove the button code, remove other fancy things you've got in there, and just first try to show a few test patterns (light all leds, light 1 row, light 1 column, light an individual led). If you can get your code to do all of those things reliably, then it should be easy to make it do whatever you want :). If you can't get that to work, come back here and post your stripped down code that just tries to light a row or column, and it'll be a lot easier for us to help you :). We'll have code that tries to do 1 thing, and that fails with a very clear description of what is expected, and what's happening :).

Hey, thanks for the answer. I’ll try to explain my code. I tried to get a small example to run, but it still does not work as intended. The following example should light a diagonal line of leds. Instead it lights all leds but the diagonal line (so leds get turned off, not on by my commands). I tried to reverse the command (~) but then all leds light up and my commands are completely ignored (or so it seems).

The following code (should) work like this:
The 12x12 matrix of leds is represented by a 12 int array (meaning 12x16 bits). I ignore the 4 bits I dont need, so I have 1212 bits for 1212 leds. A led should light up if the bit in the matrix is 1 and shouldn’t if the bit is 0.
Data and clock pins controll the shift registers, the ledColumn pins the darlington arrays.
The code cycles through the rowTimer event, which always light up one row via the shiftregister and enables the right columns for this row (setColumns()). For this the bitmap array is used.
PushOne() and pushZero() are used to shift the bit in the shiftregister. (I push a 1 every 12 cycles, that then circles through the rows/shiftregisters via pushing zeros.)
All of this should make it possible to only light one row at a time (can’t light them all at the same time) but trick the eye into seeing them all on at the same time (persistance of vision its called afaik).
The setABit() and clearABit() methods only give a simpler acces to bitSet() and bitClear(). They both manipulate the bitmap array which then is used to light the right leds.

simpler Program code

#include "Arduino.h"
#include <SimpleTimer.h>


  // Pins
  const int dataPin = 11;
  const int clockPin = 13;
  const int ledColumns [12] = {5,4,3,2,1,0,A3,A2,A1,A0,7,6};

  
  // Program variables
  int bitmap[12]; // 12*16 bit
  int currentRow = 0;
  
  // Timer
  SimpleTimer rowTimer(500);  

void setup() {  
  
  // init Shiftregisters
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  digitalWrite(dataPin, HIGH);
  digitalWrite(clockPin, LOW);
  
  // init led rows
  for (int i = 0; i<12; i++) {
    pinMode(ledColumns[i], OUTPUT);
    digitalWrite(ledColumns[i], LOW);
  }

  // init timers
  rowTimer.start(); 
   
  clearBitmap();

  setABit(0,0);
  setABit(1,1);
  setABit(2,2);
  setABit(3,3);
  setABit(4,4);
  setABit(5,5);
  setABit(6,6);
  setABit(7,7);
  setABit(8,8);
  setABit(9,9);
  setABit(10,10);
  setABit(11,11);
}


void loop() {

  // update row
  if (rowTimer.isReady()) {
    refresh(); // paint the next row (and change columns accordingly)
  }

}


// ##########################################################
// bitmap and refresh stuff

void refresh() {

  clearColumns();

  // next row via shift register
  if (currentRow == 0) {
    pushOne();
  }
  else {
    pushZero();
  }  

  setColumns();
  
  currentRow++;
  currentRow = currentRow % 12;
}

void pushOne() {
  digitalWrite(dataPin, HIGH);
  digitalWrite(clockPin, LOW);
  digitalWrite(clockPin, HIGH);
}

void pushZero() {
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  digitalWrite(clockPin, HIGH);
}

void clearColumns() {
  for (int i = 0; i < 12; i++) {
    digitalWrite(ledColumns[i], LOW);
  }
}

void setColumns() {
  if (currentRow < 12) {
    for (int i = 0; i < 12; i++) {
      digitalWrite(ledColumns[i], bitRead(bitmap[currentRow], i));
    }
  }
}

void clearBitmap() {
  for (int x = 0; x < 12; x++) {
    for (int y = 0; y < 12; y++) {
      clearABit(x,y);
    }  
  }
}

void fillBitmap() {
  for (int x = 0; x < 12; x++) {
    for (int y = 0; y < 12; y++) {
      setABit(x,y);
    }  
  }
}

void setABit(int x, int y) {
  bitSet(bitmap[y], x);
}

void clearABit(int x, int y) {
  bitClear(bitmap[y], x);
}

SimpleTimer Code

#include "Arduino.h"
#include "SimpleTimer.h"

 SimpleTimer::SimpleTimer(long theTimeTick) {
   timeTick = theTimeTick;
   running = false;
 }
 
 void SimpleTimer::start() {
   running = true;
   lastTick = micros();
 }  

 void SimpleTimer::stop() {
   running = false;
 }  
   
 boolean SimpleTimer::isReady() {
   if (!running)
     return false;
   
   if (micros() - lastTick > timeTick) {
     lastTick = micros();
     return true;
   }
   else
     return false;
 }
[/quote]
and
[quote]#ifndef SimpleTimer_h
#define SimpleTimer_h

#include "Arduino.h"

class SimpleTimer {
 private:
   long lastTick;
   long timeTick;
   boolean running;
 
 public:
   void start();
   void stop();
   boolean isReady();
   SimpleTimer(long theTimeTick);
};
#endif

Ah, can I somehow “hide” all this code behind a button or something? Like this my post gets really big ;(

Yes, you've used the wrong tags - you used "quote" instead of the "code" ([ # ]) icon next to it.

Swap each "quote" for "code".

Thanks, this look way better now ;)

I would still need some help thouhg.

a few questions about how you built it, and about your code. I don't immediatly see the issue yet, but have a few questions:

Can your shift registers handle the current of 12 leds being on at once? the shift register is the one that scans the lines, so it has to be able to light 12 leds at once, but if i check the specs, it can at most handle 50mA...

Why in your code do you call clearColumns all the time? right after that you will set the actual pin values, so it's completely unnecessary...

And as to finding your issue, why don't you slow down your program? have it do 1 line per second, or per 10 seconds. Or investigate individual parts of code. I haven't used the bitmap class yet, nor the way you manipulate its values, don't know how it all responds. But if you calmly analyze the parts of your program, you should quickly find the error. Your logic seems sound, you know what you're trying to do. my suggestions: -slow down the program, see what it does -test individual parts, are writing the row & column pins working as expected? analyze from the ground up everything that your code does, and the error should be clear :).

There are transistors behind the shift registers. You are right, the registers alone can't handle 12 leds. Clearcolumns may indeed be unnecessary. That was just a test. If I dont use that the leds which should be off do light up a little bit (I guess because there is a very small timeframe where the row changes but the columns are still the old ones), so I switched them all off before I changed the row. I will try the code without this later.

I am still debugging and I will do as you suggested: slow it down. The rest of the debugging is not that simple, because I have no Serial connection to the chip.

Btw: The bitmap is no class (at least in my code), but an array of int. I just used the bitSet() and bitClear() methods from the arduino library. As far as I know they should work with every data type... but perhaps someone who knows more that I do can prove me wrong here. Than that would be the problem.

I found the error at last: the shift registers use NAND from both inputs (which I connected both to the data line). So if I want a led to light up I have to use a 0, not a 1 (because of the not in NAND).

So I changed the refresh() method to (switching pushone() and pushZero():

void refresh() {

  clearColumns();

  // next row via shift register
  if (currentRow == 0) {
    pushZero();
  }
  else {
    pushOne();
  }  

  setColumns();
  
  currentRow++;
  currentRow = currentRow % 12;
}

and now everything seems to work as intended. The clearColumns(); seems to be needed, because if I skip that one I get “ghost” images at the neighbor leds.

Well thanks alot guys!

well done :).