Multiplexación de matriz LED RGB 10x10 con registros de desplazamiento Modulación de ángulo de bit

He construido una matriz LED RGB de 10x10. Los LED están conectados en filas y columnas. Son 40 pines (30 para RGB, 10 para GND). Me compré cinco circuitos integrados 74CH595 y soldé todo junto. También agregué un BJT 1A al frente de cada fila/columna para proteger los circuitos integrados + Arduino y poder usar una fuente de alimentación externa. Los 3 pines (datos, reloj, pestillo) de los circuitos integrados están conectados a mi micro Arduino.

Todo funciona muy bien (incluso con multiplexación) cuando no uso la modulación de ángulo de bits.

Palabras que probablemente. no sé:

BAM = Modulación de ángulo de bit

En los comentarios "...(Data) an" significa que el pin ...(Data) está activado. "...(Data) aus" significa que el pin de datos está desactivado.

Por qué uso el código de la biblioteca FastLED:

Lo uso porque son un muy buen software de animación para esa Biblioteca/Tipo de LED.

El Código utiliza el 10 % de la memoria del programa y el 21 % de la memoria dinámica.

Aquí está mi código (Arduino IDE 1.6.7, sin 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
  }
}

Entonces, mi primer problema es que la última fila (2 o 10) es más brillante que las otras filas. Creo que este es el momento en que el bucle se reinicia y rellena los Objetos (led en leds) con nuevos Datos.

  1. Creo que mi código es demasiado lento porque todavía parpadeo cuando hago BAM. Si alguien tiene un consejo para mí sobre cómo obtener el código más rápido, sería muy bueno que me lo dijera :)

Si lo necesita, puedo subir un esquema, pero mi Internet es muy lento, así que no lo hago ahora.

Aquí hay una simulación hecha por mí mismo para probar el código. Es solo 1 color y 8x8 pero funciona de la misma manera: https://circuits.io/circuits/2422863-led-matrix-8x8-controler

Por favor cargue el esquema
Acabo de agregar esto a la pregunta prob. también ayuda: [enlace] circuits.io/circuits/2422863-led-matrix-8x8-controler

Respuestas (1)

Creo que sus problemas no son de hardware sino del software que utiliza. La razón por la que obtiene una luz estroboscópica periódica o un brillo inconsistente entre filas mientras multiplexa una matriz de LED es porque el tiempo de encendido para cada fila no es consistente. Aunque su ojo no puede ver la fila encendiéndose y apagándose porque está sucediendo más rápido de lo que permite su persistencia de visión, su ojo puede captar las diferencias en total a tiempo. Este es el principio básico detrás de habilitar la escala de grises basada en PWM en los LED. Para arreglar las cosas, debe asegurarse de que el tiempo de ejecución de cada fila sea consistente.

El problema central es que retrasar 100 microsegundos entre filas no es lo mismo que actualizar cada fila cada 100 microsegundos. La cantidad de tiempo para actualizar una fila con su código puede variar. Por ejemplo, si actualiza la fila 1, luego retrasa 100 microsegundos, luego se necesitan 200 microsegundos para actualizar la fila 2, la fila 1 ha estado activa durante un total de 300 microsegundos. Si se tarda 50 microsegundos en actualizar la fila 3, la fila 2 habría estado activa durante 150 microsegundos. A continuación, puede ver cómo el uso de un retraso entre cada actualización de fila puede generar tiempos de activación incoherentes si los cálculos de cada fila son incoherentes.

En su código, hay varias cosas que conducirían a una puntualidad inconsistente entre las filas. Primero, utiliza un bloque si-entonces de 10 niveles para determinar los bits de control de fila. Tomará más tiempo evaluar eso en la fila 10 que en la fila 1 y probablemente sea por eso que las filas inferiores son más brillantes que las filas superiores. En segundo lugar, se producirá un efecto similar en susetBitMaps()función al evaluar las diferencias de comportamiento entre ciclos. Sospecho que esto amplifica el brillo de la última fila. Finalmente, está creando efectivamente su propio bucle dentro del bucle AVR general. Cuando sale de su propio bucle y luego vuelve a hacer el bucle AVR principal, vuelve a calcular completamente el mapa de bits para sus LED. Sospecho que esto está causando apuñalamientos ocasionales, ya que tomará más tiempo antes de que vuelva a ingresar a sus bucles internos que realizan los escaneos de ciclo, lo que significa que mientras está recalculando el mapa de bits, los LED se dejan encendidos en la última configuración , por lo tanto, un destello momentáneo de brillo (estroboscópico).

Hice una matriz LED de 8x8 de diseño similar y escribí un controlador que está disponible en GitHub aquí. Mi controlador puede crear una imagen estable (sin parpadeo). Si bien mi controlador es un poco más robusto que el suyo, la diferencia clave es que estoy usando una interrupción de temporizador en el microcontrolador AVR. Fuera de la interrupción, hago todos los cálculos necesarios para determinar cuáles deben ser los bits para cada fila, y cuando se dispara la interrupción, todo lo que hago es enviar los bits de las siguientes filas a los registros de desplazamiento. Uso SPI en lugar de bit-banging, pero el concepto general sigue siendo el mismo. Lo que proporciona la interrupción del temporizador es un tiempo de inicio consistente para cada actualización de fila, y solo hacer la transmisión de bits durante la interrupción hace que el trabajo se realice de manera muy consistente, por lo tanto, las actualizaciones de fila (cuando se dispara el pasador de enganche) se cronometran de manera muy consistente, lo que resulta en una imagen suave.

Otro elemento clave de mi diseño es que tengo dos búferes de bits que contienen los bits de fila para un marco de imagen determinado. El volcado de bits fila por fila se produce desde un búfer, mientras que el cálculo para el siguiente cuadro de imagen se realiza en el otro búfer. Cuando el siguiente cuadro de imagen está listo y el escaneo de filas se reinicia, simplemente cambio los búferes. Esto ayuda a enfocar el trabajo necesario durante las interrupciones del temporizador para que solo sea la transmisión de bits de fila a los registros de desplazamiento.

Esto fue muy útil. Agregué la mecánica de interrupción y ahora parece funcionar mucho mejor. Ya arreglé un montón de problemas antes simplemente haciendo que el código fuera mucho más rápido. Eliminé las matrices, aceleré la modulación del ángulo de bits y algunos cambios menores. Además de eso, cambié a un microcontrolador más rápido (Teensy 3.6). Ahora estoy listo para construir una nueva matriz de 16x16, pero esta vez con TLC 5940 (IC de controlador de LED PWM).