多路复用 LED 矩阵的 Arduino 代码优化

Arduino Code optimization for multiplexing LED Matrix

我构建了一个 10x10RGB(没有 WS2811...,普通的)LED 矩阵,带有 5 个移位寄存器和 1 个 arduino micro。

我的问题是现在我的代码似乎变慢了,或者 16Mhz 的 arduino 无法处理合适的 Hz 速率。目前,当我使用下面的代码时,我得到了一些 flicker/lagging 。我认为大约 60Hz-100Hz 的 Hz 频率会非常好。我已经将 Arduino IDE 编译器设置从 -Os 更改为 -O3 以获得更快的速度(它确实有效)。

该代码具有用于亮度控制的位角调制和多路复用功能。

所以我的问题:是否值得创建一个数组,其中预定义了所有可能的值(10 个值,仅 int < 10),然后在第 312 行中使用它们:

BitMapR1[intLayerSel / 10] = _byte;

我搜索了internt,发现一些文章说arduinos(或微控制器)上的除法非常慢。

setBitMaps() 是位角度调制发生的地方 myloop() 是多路复用发生的地方

代码: http://pastebin.com/tkFZsVxS <-- 最好看这里

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

有很多问题,例如:

leds[0] = CRGB(36, 0, 0);

这意味着:

  • 复制参数入栈
  • 调用CRBG功能
  • 创建本地 LED 对象
  • 通过调用约束
  • 在该本地对象上设置 R
  • setG ...
  • setB ...
  • return 本地对象的副本
  • 在 led[0] 上复制赋值运算符

同样对 12b 颜色使用 8b 宽变量有点多余。 所以一开始我会推荐这样的东西:

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

你可以考虑:

  • 使用硬件SPI
  • 使用/MR输入以一个脉冲清除所有寄存器(比shiftOut快得多)
  • 对行驱动器使用约翰逊计数器 (4017) 并保存一个移位寄存器(缓冲区也适合 uint32_t)。对于以前的版本,您可以使用 Q7S 输出和 MSB 设置为逻辑 1 来更新计数器。