c++optimizationarduinomultiplexingfastled

Arduino Code optimization for multiplexing LED Matrix


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
}
}

Solution

  • 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: