I've build a 10x10RGB (no WS2811..., the normal ones) LED Matrix with 5 shift registers and 1 arduino micro.
My Problem is now my code seems to be to slow or the arduino with its 16Mhz just cant handle a decent Hz rate. At the moment I get some flicker/lagging when I use the code down below. I think a Hz rate of about 60Hz-100Hz would be very good. I've already changed the Arduino IDE Compiler settings from -Os to -O3 to get better speed (it really worked).
The code has Bit angle modulation for brightness control and a multiplexing feature.
So my Question: Is it worth to create a Array where all the possible values (10 values, only int < 10) are predefined and then use them in line 312:
BitMapR1[intLayerSel / 10] = _byte;
I searched the internt and I found some articles telling that division on arduinos (or microcontrollers) is very slow.
setBitMaps() is where the Bit angle modulation happens myloop() is where the multiplexing happens
Code: http://pastebin.com/tkFZsVxS <-- better look here
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);
pinMode(5, OUTPUT);
//Serial.begin(250000);
//noInterrupts();
}
unsigned long lngLast = 0;
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(36, 0, 0);
leds[1] = CRGB(103, 0, 0);
leds[2] = CRGB(170, 0, 0);
leds[3] = CRGB(255, 0, 0);
leds[4] = CRGB(255, 0, 0);
leds[5] = CRGB(170, 0, 0);
..........
leds[96] = CRGB(103, 0, 0);
leds[97] = CRGB(36, 0, 0);
leds[98] = CRGB(0, 0, 0);
leds[99] = CRGB(0, 0, 0);
//Serial.print(micros()); Serial.println(" Objekte");
BAM();
//Serial.print(micros()); Serial.println(" BAM");
}
void BAM() {
for (byte cycle = 1; cycle <= 15; cycle++) {
//Serial.print(micros()); Serial.println(" bSetBitMaps");
setBitMaps(cycle, 1);
//Serial.print(micros()); Serial.println(" aSetBitMaps");
lngLast = micros();
myloop();
delayMicroseconds(50);
turnoff();
//Serial.print(micros()); Serial.println(" aMyloop");
}
}
void turnoff() {
PORTD &= ~_BV(PORTD2);
ShiftOut(B00000000);
ShiftOut(B00000000);
ShiftOut(B00000000);
ShiftOut(B00000000);
ShiftOut(B00000000);
PORTD |= _BV(PORTD2);//LatchPin
}
void setBitMaps(byte cycle, byte 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;
}
for (byte intLayerSel = 0; intLayerSel < 100; intLayerSel += 10) {
byte _byte = 0;
for (byte i = intLayerSel + 8; i < intLayerSel + 10; 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;
}
}
for (byte i = intLayerSel; i < intLayerSel + 6; i++) {
if (cycle == 1 && (leds[i].getG() & (1 << pos - 1)) != 0) {
_byte = _byte << 1;
_byte = _byte + B00000001;
}
else if ((cycle == 2 || cycle == 3) && (leds[i].getG() & (1 << pos)) != 0) {
_byte = _byte << 1;
_byte = _byte + B00000001;
}
else if (cycle >= 4 && cycle <= 7 && (leds[i].getG() & (1 << pos + 1 )) != 0) {
_byte = _byte << 1;
_byte = _byte + B00000001;
}
else if (cycle >= 8 && cycle <= 15 && (leds[i].getG() & (1 << pos + 2)) != 0) {
_byte = _byte << 1;
_byte = _byte + B00000001;
}
else {
_byte = _byte << 1;
_byte = _byte + B00000000;
}
}
BitMapR2[intLayerSel / 10] = _byte;
}
}
void myloop() {
byte bLayerA;
byte bLayerB;
for (byte bLayerTop = 1; bLayerTop <= 10; bLayerTop++) {
//Serial.print(micros()); Serial.println(" startML");
bLayerA = B00000000;
bLayerB = B00000000;
switch (bLayerTop) {
case 1:
bLayerA = B10000000;
break;
case 2:
bLayerA = B01000000;
break;
case 3:
bLayerA = B00100000;
break;
case 4:
bLayerA = B00010000;
break;
case 5:
bLayerA = B00001000;
break;
case 6:
bLayerA = B00000100;
break;
case 7:
bLayerA = B00000010;
break;
case 8:
bLayerA = B00000001;
break;
case 9:
bLayerB = B00000010;
break;
case 10:
bLayerB = B00000001;
break;
}
/*
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;
}
*/
//Serial.print(micros()); Serial.println(" bWait");
while (micros() - lngLast < 50) {
//Serial.println("call");
}
//Serial.print(micros()); Serial.println(" aWait");
turnoff();
PORTD &= ~_BV(PORTD2); //Latch LOW
//OutPut Enable = False
PORTD |= _BV(PORTD5);
byte bLayer = bLayerTop - 1;
ShiftOut(bLayerA); //Register 5
ShiftOut(bLayerB + BitMapR4[bLayer]); //Register 4
ShiftOut(BitMapR3[bLayer]); //Register 3
ShiftOut(BitMapR2[bLayer]); //Register 2
ShiftOut(BitMapR1[bLayer]); //Register 1
//take the latch pin high so the LEDs will light up:
PORTD |= _BV(PORTD2);//Latch High
//OutPut Enable = True
PORTD &= ~_BV(PORTD5);
// pause before next value:
//delay(1);
//delayMicroseconds(100);
// Serial.print(micros()); Serial.println(" end");
lngLast = micros();
}
}
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 off
PORTD &= ~_BV(PORTD4);//Clock off
//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.
/*
//00001010 - 00000010 = true
switch (myDataOut & (1 << i)) {
case 0:
Serial.println("0");
PORTD &= ~_BV(PORTD3);//Data aus
break;
case 1: //case true
Serial.println("1");
PORTD |= _BV(PORTD3);//Data an
break;
}
*/
/*
digitalWrite(3, myDataOut & (1 << i));
*/
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
}
}
There are many issues, like:
leds[0] = CRGB(36, 0, 0);
This means:
Also using 8b wide variables for 12b colors is little bit redundant. So for the start I would recommend something like that:
class LED {
public:
uint16_t rgb;
LED(uint8_t r=0, uint8_t g=0, uint8_t b=0) {
setRGB(r,g,b);
}
void setRGB(uint8_t r=0, uint8_t g=0, uint8_t b=0) {
r = r >> 4;
g = g&0xF0;
rgb = b&0xF0;
rgb = (rgb<<4) | g | r;
}
bool getBit(uint16_t mask) {
return rgb & mask;
}
};
LED leds[100];
void setup() {
pinMode(2, OUTPUT);
pinMode(4, OUTPUT);
pinMode(3, OUTPUT);
pinMode(5, OUTPUT);
Serial.begin(250000);
}
void loop() {
leds[0].setRGB(36, 0, 0);
leds[1].setRGB(103, 0, 0);
leds[2].setRGB(170, 0, 0);
leds[3].setRGB(255, 0, 0);
leds[4].setRGB(255, 0, 0);
leds[5].setRGB(170, 0, 0);
leds[6].setRGB(103, 0, 0);
leds[7].setRGB(36, 0, 0);
leds[8].setRGB(0, 0, 0);
leds[9].setRGB(0, 0, 0);
leds[10].setRGB(36, 0, 0);
leds[11].setRGB(103, 0, 0);
leds[12].setRGB(170, 0, 0);
leds[13].setRGB(255, 0, 0);
leds[14].setRGB(255, 0, 0);
leds[15].setRGB(170, 0, 0);
leds[16].setRGB(103, 0, 0);
leds[17].setRGB(36, 0, 0);
leds[18].setRGB(0, 0, 0);
leds[19].setRGB(0, 0, 0);
leds[20].setRGB(36, 0, 0);
leds[21].setRGB(103, 0, 0);
leds[22].setRGB(170, 0, 0);
leds[23].setRGB(255, 0, 0);
leds[24].setRGB(255, 0, 0);
leds[25].setRGB(170, 0, 0);
leds[26].setRGB(103, 0, 0);
leds[27].setRGB(36, 0, 0);
leds[28].setRGB(0, 0, 0);
leds[29].setRGB(0, 0, 0);
leds[30].setRGB(36, 0, 0);
leds[31].setRGB(103, 0, 0);
leds[32].setRGB(170, 0, 0);
leds[33].setRGB(255, 0, 0);
leds[34].setRGB(255, 0, 0);
leds[35].setRGB(170, 0, 0);
leds[36].setRGB(103, 0, 0);
leds[37].setRGB(36, 0, 0);
leds[38].setRGB(0, 0, 0);
leds[39].setRGB(0, 0, 0);
leds[40].setRGB(36, 0, 0);
leds[41].setRGB(103, 0, 0);
leds[42].setRGB(170, 0, 0);
leds[43].setRGB(255, 0, 0);
leds[44].setRGB(255, 0, 0);
leds[45].setRGB(170, 0, 0);
leds[46].setRGB(103, 0, 0);
leds[47].setRGB(36, 0, 0);
leds[48].setRGB(0, 0, 0);
leds[49].setRGB(0, 0, 0);
leds[50].setRGB(36, 0, 0);
leds[51].setRGB(103, 0, 0);
leds[52].setRGB(170, 0, 0);
leds[53].setRGB(255, 0, 0);
leds[54].setRGB(255, 0, 0);
leds[55].setRGB(170, 0, 0);
leds[56].setRGB(103, 0, 0);
leds[57].setRGB(36, 0, 0);
leds[58].setRGB(0, 0, 0);
leds[59].setRGB(0, 0, 0);
leds[60].setRGB(36, 0, 0);
leds[61].setRGB(103, 0, 0);
leds[62].setRGB(170, 0, 0);
leds[63].setRGB(255, 0, 0);
leds[64].setRGB(255, 0, 0);
leds[65].setRGB(170, 0, 0);
leds[66].setRGB(103, 0, 0);
leds[67].setRGB(36, 0, 0);
leds[68].setRGB(0, 0, 0);
leds[69].setRGB(0, 0, 0);
leds[70].setRGB(36, 0, 0);
leds[71].setRGB(103, 0, 0);
leds[72].setRGB(170, 0, 0);
leds[73].setRGB(255, 0, 0);
leds[74].setRGB(255, 0, 0);
leds[75].setRGB(170, 0, 0);
leds[76].setRGB(103, 0, 0);
leds[77].setRGB(36, 0, 0);
leds[78].setRGB(0, 0, 0);
leds[79].setRGB(0, 0, 0);
leds[80].setRGB(36, 0, 0);
leds[81].setRGB(103, 0, 0);
leds[82].setRGB(170, 0, 0);
leds[83].setRGB(255, 0, 0);
leds[84].setRGB(255, 0, 0);
leds[85].setRGB(170, 0, 0);
leds[86].setRGB(103, 0, 0);
leds[87].setRGB(36, 0, 0);
leds[88].setRGB(0, 0, 0);
leds[89].setRGB(0, 0, 0);
leds[90].setRGB(36, 0, 0);
leds[91].setRGB(103, 0, 0);
leds[92].setRGB(170, 0, 0);
leds[93].setRGB(255, 0, 0);
leds[94].setRGB(255, 0, 0);
leds[95].setRGB(170, 0, 0);
leds[96].setRGB(103, 0, 0);
leds[97].setRGB(36, 0, 0);
leds[98].setRGB(0, 0, 0);
leds[99].setRGB(255, 255, 255);
// show context
for (uint16_t bitmask = 1U; bitmask < 0x400; bitmask <<= 1) {
for (LED & led : leds) {
Serial.print(led.getBit(bitmask), HEX);
Serial.print(" ");
}
Serial.println();
}
do_cycle();
}
void do_cycle() {
uint16_t bitmask_r = 0;
uint16_t bitmask_g = 0;
uint16_t bitmask_b = 0;
for (byte mag = 1; mag < 16; ++mag) { // magnitude
for (byte row = 0; row < 10; ++row) { // mistake #2
//uint32_t us = micros();
if ((mag & (mag-1)) == 0) { // Is it power of two? Change bitmask
bitmask_r = mag;
bitmask_g = bitmask_r << 4;
bitmask_b = bitmask_g << 4;
}
// shift out init:
PORTD &= ~_BV(PD3); //Data aus
PORTD &= ~_BV(PD4); //Clock aus
for (int8_t cnt = 9; cnt >= 0; --cnt) {
//Serial.print(cnt==row?1:0);
shift1bit(cnt==row); // mistake #1
}
for (int8_t col = 9; col >= 0; --col) {
//Serial.print(leds[row*10+col].getBit(bitmask_b));
shift1bit(leds[row*10+col].getBit(bitmask_b));
}
for (int8_t col = 9; col >= 0; --col) {
//Serial.print(leds[row*10+col].getBit(bitmask_g));
shift1bit(leds[row*10+col].getBit(bitmask_g));
}
for (int8_t col = 9; col >= 0; --col) {
//Serial.print(leds[row*10+col].getBit(bitmask_r));
shift1bit(leds[row*10+col].getBit(bitmask_r));
}
PORTD |= _BV(PD2); // LatchPin
PORTD &= ~_BV(PD2); // disable LatchPin
//Serial.println(micros()-us);
delayMicroseconds(50);
}
}
}
inline void shift1bit (bool b) {
// set data:
if (b) {
PORTD |= _BV(PD3);
} else {
PORTD &= ~_BV(PD3);
}
// clock pulse:
PORTD |= _BV(PD4);
PORTD &= ~_BV(PD4);
}
And you can consider:
SPI
/MR
input to clear all registers by one pulse (much faster than shiftOut)4017
) for row drivers and save one shift register (also buffer fits into the one uint32_t
). With previous you can use Q7S
ouput and MSB set to logic 1 to update counter.