0
\$\begingroup\$

I've build a 10x10 RGB LED Matrix. The LED's are connectet in rows and colums. Thats 40 pins (30 for RGB, 10 for GND). I got myself 5 74CH595 IC's and solderd everything together. I also added a 1A BJT in front of each row/colum to protect the IC's + Arduino and to be able to use a external power supply. The 3 pins(Data, Clock, latch) from the IC's are connected to my arduino micro.

It's all working very well (even with multiplexing) when I don't use Bit Angle Modulation.

Words you prob. dont know:

BAM = Bit Angle Modulaiton

In the comments "...(Data) an" means the ...(Data) pin is on. "...(Data) aus" means the Data pin is off.

Why I use the FastLED Libary code:

I use it because there're some very good animation software for that Libary/Type of LED.

The Code uses 10% of programmemory and 21% dynamicmemory.

Here is my code (Arduino IDE 1.6.7, no Lib)

class FLED {
private:
bool b;

public:
FLED();
void show();
};

FLED::FLED() : b(false) {

}

void FLED::show() {

}


class LED {
  private:
uint8_t LEDname;
uint8_t R;
uint8_t G;
uint8_t B;

 public:
LED();
uint8_t getR();
uint8_t getG();
uint8_t getB();
void setR(uint8_t _R);
void setG(uint8_t _G);
void setB(uint8_t _B);
};

LED::LED() : R(0), G(0), B(0) {

}

uint8_t LED::getR() {
  return R;
}
uint8_t LED::getG() {
  return G;
}
uint8_t LED::getB() {
  return B;
}

void LED::setR(uint8_t _R) {
  R = _R;
}
void LED::setG(uint8_t _G) {
  G = _G;
}
void LED::setB(uint8_t _B) {
  B = _B;
}

LED leds[100];
FLED FastLED;


void setup() {
  //set pins to output so you can control the shift register
  pinMode(2, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(3, OUTPUT);
  //Serial.begin(1000000);


}

uint8_t BitMapR1[10] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000
};
uint8_t BitMapR2[10] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
 B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000
};
uint8_t BitMapR3[10] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000
};

uint8_t BitMapR4[10] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000
};

LED CRGB(byte _R, byte _G, byte _B) {
  LED _LED = LED();
  _LED.setR(constrain(_R / 16, 0, 15));
  _LED.setG(constrain(_G / 16, 0, 15));
  _LED.setB(constrain(_B / 16, 0, 15));
  return _LED;
}

void loop() {
  //Serial.print(micros()); Serial.println(" Start");

  leds[0] = CRGB(0, 0, 0);
  leds[1] = CRGB(0, 0, 0);
  leds[2] = CRGB(0, 0, 0);
  leds[3] = CRGB(0, 0, 0);
  leds[4] = CRGB(0, 0, 0);
  leds[5] = CRGB(0, 0, 0);
  leds[6] = CRGB(0, 0, 0);
  leds[7] = CRGB(0, 0, 0);
  leds[8] = CRGB(0, 0, 0);
  leds[9] = CRGB(0, 0, 0);
  leds[10] = CRGB(0, 0, 0);
  leds[11] = CRGB(0, 0, 0);
  leds[12] = CRGB(0, 0, 0);
  leds[13] = CRGB(0, 0, 0);
  leds[14] = CRGB(0, 0, 0);
  leds[15] = CRGB(0, 0, 0);
  leds[16] = CRGB(0, 0, 0);
  leds[17] = CRGB(0, 0, 0);
  leds[18] = CRGB(0, 0, 0);
  leds[19] = CRGB(0, 0, 0);
  leds[20] = CRGB(0, 0, 0);
  leds[21] = CRGB(0, 0, 0);
  leds[22] = CRGB(0, 0, 0);
  leds[23] = CRGB(0, 0, 0);
  leds[24] = CRGB(0, 0, 0);
  leds[25] = CRGB(0, 0, 0);
  leds[26] = CRGB(0, 0, 0);
  leds[27] = CRGB(0, 0, 0);
  leds[28] = CRGB(0, 0, 0);
  leds[29] = CRGB(0, 0, 0);
  leds[30] = CRGB(16, 0, 0);
  leds[31] = CRGB(32, 0, 0);
  leds[32] = CRGB(48, 0, 0);
  leds[33] = CRGB(64, 0, 0);
  leds[34] = CRGB(96, 0, 0);
  leds[35] = CRGB(128, 0, 0);
  leds[36] = CRGB(180, 0, 0);
  leds[37] = CRGB(200, 0, 0);
  leds[38] = CRGB(255, 0, 0);
  leds[39] = CRGB(0, 0, 0);
  leds[40] = CRGB(0, 0, 0);
  leds[41] = CRGB(0, 0, 0);
  leds[42] = CRGB(0, 0, 0);
  leds[43] = CRGB(0, 0, 0);
  leds[44] = CRGB(0, 0, 0);
  leds[45] = CRGB(0, 0, 0);
  leds[46] = CRGB(0, 0, 0);
  leds[47] = CRGB(0, 0, 0);
  leds[48] = CRGB(0, 0, 0);
  leds[49] = CRGB(0, 0, 0);
  leds[50] = CRGB(16, 0, 0);
  leds[51] = CRGB(32, 0, 0);
  leds[52] = CRGB(48, 0, 0);
  leds[53] = CRGB(64, 0, 0);
  leds[54] = CRGB(96, 0, 0);
  leds[55] = CRGB(128, 0, 0);
  leds[56] = CRGB(180, 0, 0);
  leds[57] = CRGB(200, 0, 0);
  leds[58] = CRGB(255, 0, 0);
  leds[59] = CRGB(0, 0, 0);
  leds[60] = CRGB(0, 0, 0);
  leds[61] = CRGB(0, 0, 0);
  leds[62] = CRGB(0, 0, 0);
  leds[63] = CRGB(0, 0, 0);
  leds[64] = CRGB(0, 0, 0);
  leds[65] = CRGB(0, 0, 0);
  leds[66] = CRGB(0, 0, 0);
  leds[67] = CRGB(0, 0, 0);
  leds[68] = CRGB(0, 0, 0);
  leds[69] = CRGB(0, 0, 0);
  leds[70] = CRGB(0, 0, 0);
  leds[71] = CRGB(0, 0, 0);
  leds[72] = CRGB(0, 0, 0);
  leds[73] = CRGB(0, 0, 0);
  leds[74] = CRGB(0, 0, 0);
  leds[75] = CRGB(0, 0, 0);
  leds[76] = CRGB(0, 0, 0);
  leds[77] = CRGB(0, 0, 0);
  leds[78] = CRGB(0, 0, 0);
  leds[79] = CRGB(0, 0, 0);
  leds[80] = CRGB(0, 0, 0);
  leds[81] = CRGB(0, 0, 0);
  leds[82] = CRGB(0, 0, 0);
  leds[83] = CRGB(0, 0, 0);
  leds[84] = CRGB(0, 0, 0);
  leds[85] = CRGB(0, 0, 0);
  leds[86] = CRGB(0, 0, 0);
  leds[87] = CRGB(0, 0, 0);
  leds[88] = CRGB(0, 0, 0);
  leds[89] = CRGB(0, 0, 0);
  leds[90] = CRGB(0, 0, 0);
  leds[91] = CRGB(0, 0, 0);
  leds[92] = CRGB(0, 0, 0);
  leds[93] = CRGB(0, 0, 0);
  leds[94] = CRGB(0, 0, 0);
  leds[95] = CRGB(0, 0, 0);
  leds[96] = CRGB(0, 0, 0);
  leds[97] = CRGB(0, 0, 0);
  leds[98] = CRGB(0, 0, 0);
  leds[99] = CRGB(0, 0, 0);
  //Serial.print(micros()); Serial.println(" Objekte");
  FastLED.show();
  //setBitMaps();
  //myloop();
  while(true) {
    BAM();

  }

  //Serial.print(micros()); Serial.println(" BAM");

}

void BAM() {
  for (int cycle = 1; cycle <= 15; cycle++) {
setBitMaps(cycle, 1);
myloop();


  }
}

void setBitMaps(int cycle, int pos) {


  //Register 1
  for (byte intLayerSel = 0; intLayerSel < 100; intLayerSel += 10) { 

byte _byte = 0;
for (byte i = intLayerSel; i < intLayerSel + 8; i++) {
  if (cycle == 1 && (leds[i].getR() & (1 << pos - 1)) != 0) {
    _byte = _byte << 1;
    _byte = _byte + B00000001;
  }
  else if ((cycle == 2 || cycle == 3) && (leds[i].getR() & (1 << pos)) != 0) {
    _byte = _byte << 1;
    _byte = _byte + B00000001;
  }
  else if (cycle >= 4 && cycle <= 7 && (leds[i].getR() & (1 << pos + 1 )) != 0)  {
    _byte = _byte << 1;
    _byte = _byte + B00000001;
  }
  else if (cycle >= 8 && cycle <= 15 && (leds[i].getR() & (1 << pos + 2)) != 0) {
    _byte = _byte << 1;
    _byte = _byte + B00000001;
  }
  else {
    _byte = _byte << 1;
    _byte = _byte + B00000000;
  }
}
BitMapR1[intLayerSel / 10] = _byte;
  }
}



void myloop() {
  byte bLayerA;
  byte bLayerB;

  for (byte bLayerTop = 1; bLayerTop <= 10; bLayerTop++) {
bLayerA = B00000000;
bLayerB = B00000000;
if (bLayerTop == 1) {
  bLayerA = B10000000;
} else if (bLayerTop == 2) {
  bLayerA = B01000000;
} else if (bLayerTop == 3) {
  bLayerA = B00100000;
} else if (bLayerTop == 4) {
  bLayerA = B00010000;
} else if (bLayerTop == 5) {
  bLayerA = B00001000;
} else if (bLayerTop == 6) {
  bLayerA = B00000100;
} else if (bLayerTop == 7) {
  bLayerA = B00000010;
} else if (bLayerTop == 8) {
  bLayerA = B00000001;
} else if (bLayerTop == 9) {
  bLayerB = B00000010;
} else if (bLayerTop == 10) {
  bLayerB = B00000001;
}




// take the latchPin low so
// the LEDs don't change while you're sending in bits:
// shift out the bits:
/*
  shiftOut(dataPin, clockPin, MSBFIRST, bLayerA);
  shiftOut(dataPin, clockPin, MSBFIRST, bLayerB + B00111111);
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);


  PORTD &= ~_BV(PORTD2);
  delayMicroseconds(35);//delay < 35 flackert
  PORTD |= _BV(PORTD2);

*/



PORTD &= ~_BV(PORTD2);
byte bLayer = bLayerTop - 1;
ShiftOut(bLayerA);
ShiftOut(bLayerB + BitMapR4[bLayer]);
ShiftOut(BitMapR3[bLayer]);
ShiftOut(BitMapR2[bLayer]);
//ShiftOut(B11111111);
ShiftOut(BitMapR1[bLayer]);

//take the latch pin high so the LEDs will light up:
PORTD |= _BV(PORTD2);//LatchPin

// pause before next value:

//delay(1);
delayMicroseconds(100);

  }
}

void ShiftOut(byte myDataOut) {
  // This shifts 8 bits out MSB first,
  //on the rising edge of the clock,
  //clock idles low

  //internal function setup
  byte i = 0;

  //clear everything out just in case to
  //prepare shift register for bit shifting
  PORTD &= ~_BV(PORTD3);//Data aus
  PORTD &= ~_BV(PORTD4);//Clock aus

  //for each bit in the byte myDataOutï
  //NOTICE THAT WE ARE COUNTING DOWN in our for loop
  //This means that %00000001 or "1" will go through such
  //that it will be pin Q0 that lights.
  for (i = 0; i <= 7; i++)  {
    PORTD &= ~_BV(PORTD4);//Clock aus

    //if the value passed to myDataOut and a bitmask result
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000
    // and proceeds to set pinState to 1.
    if ( myDataOut & (1 << i) ) {
  PORTD |= _BV(PORTD3);//Data an
} else {
  PORTD &= ~_BV(PORTD3);//Data aus
}
//register shifts bits on upstroke of clock pin
PORTD |= _BV(PORTD4);//Clock an
//zero the data pin after shift to prevent bleed through
PORTD &= ~_BV(PORTD3);//Data aus
  }
}

So my first problem is that the last row (2 or 10) is brighter than the other rows. I think this is the time while the loop is restarting and refilling the Objects (led in leds) with new Data.

  1. I think my code is to slow because I still get some flicker when doing BAM. If anyone has a tip for me how to get the code faster it would be very nice if you tell me :)

If you need I can upload a scematic but my Internet is very slow so I don't do it right now.

Here is a Simulation made myself to test code. It's only 1 color and 8x8 but it works the same way: https://circuits.io/circuits/2422863-led-matrix-8x8-controler

\$\endgroup\$
1
\$\begingroup\$

I believe your issues are not hardware but in the software you use. The reason you get periodic strobing or inconsistent brightness between rows while multiplexing a LED array is because the on time for each row is not consistent. Even though your eye cannot see the row turning on and off because it is happening faster than your persistence of vision allows, your eye can pick up differences in total on time. This is the basic principle behind enabling PWM-based gray scale on LEDs. In order to fix things, you need to ensure that the on-time for each row is consistent.

The core issue is that delaying 100 microseconds between rows is not the same thing as updating each row every 100 microseconds. The amount of time to update a row with your code can vary. For example, if you update row 1, then delay 100 microseconds, then it takes 200 microseconds to update row 2, row 1 has been on for a total of 300 microseconds. If it takes 50 microseconds to update row 3, row 2 would have been on for 150 microseconds. You can then see how using a delay between each row update can lead to inconsistent on-times if each row calculations are themselves inconsistent.

In your code, there are several things that would lead to inconsistent on-time between rows. First, you use a 10-level if-then block to determine the row control bits. That is going to take longer to evaluate on row 10 than on row 1 and is likely why your bottom rows are brighter than your top rows. Second, a similar effect will issue in your setBitMaps() function when evaluating behavior differences between cycles. I suspect this amplifies the last-row brightness. Finally, you are effectively creating your own loop within the overall AVR loop. When you exit your own loop and then do the main AVR loop again, you completely recalculate the bitmap for your LEDs. I suspect this is causing the occasional stabbing as it will take more take more time before you re-enter your internal loops that does the cycle-scans, meaning that while you are recalculating the bit map, the LEDs are left on in the last configuration, hence a momentary flash of brightness (strobe).

I made a 8x8 LED matrix of similar design and wrote a driver which is available on GitHub here. My driver is able to create a stable (non-flashing) image. While my driver is a bit more robust than yours, the key difference is that I am using a timer interrupt on the AVR micro controller. Outside of the interrupt I do all the calculations need to determine what the bits should be for each row, and when the interrupt fires, all I do is blast the next rows bits out to the shift registers. I use SPI rather than bit-banging, but the general concept is still the same. What the timer interrupt provides is a consistent start time for each row update, and only doing the bit transmission during the interrupt makes the work done very consistent, thus the row updates (when the latch pin is fired) are very consistently timed, resulting in a smooth image.

Another key element of my design is that I have two bit buffers that contain the row-bits for a given image frame. The row-by-row bit dumping occurs from one buffer, while the calculation for the next image frame is done on the other buffer. When the next image frame is ready and the row scan is restarting, I simply swap the buffers. This helps focus the work needed during the timer interrupts to only being the row bits transmission to the shift registers.

\$\endgroup\$
  • \$\begingroup\$ This was very helpful. I added the interrupt mechanic and now it seems to work much better. I already fixed a bunch of problems before by just making the code alot faster. I removed the Arrays, speeded up the Bit angle modulation and some minor changes. Beside that I switched over to a faster microcontroller (Teensy 3.6). Now I'm up to build a new 16x16 Matrix but this time with TLC 5940 (PWM LED Driver IC's). \$\endgroup\$ – Tom Stein Jun 21 '17 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.