Hack einer 16*32 RGB LED Matrix – Teil III Der Sketch

Nachdem im 2. Teil der praktische Aufbau den theoretischen Überlegungen des 1. Teiles folgte, nun die Betrachtung von Softwareseite.

Um den Sketch anfangs möglichst einfach zu gestalten, wird im einfachsten Fall einfach eine statische Adresse an die drei Adresspins A, B und C gelegt. Die beiden RGB Ports werden ebenfalls nur mit statischen Daten über HIGH Pegel bedient. Damit beschränkt sich der Sketch im Wesentlichen auf die for-Schleife zur Taktung des CLK Pins und die Steuerung der Kontrollpins OE und LAT. Da dieser sehr einfache Sketch auf Anhieb seine Funktion erledigte, habe ich diesen sofort erweitert. Auf dem nachfolgenden Bild ist das Ergebnis eines bereits etwas modifizierten Sketches zu sehen, bei dem ein Adresszähler mit implementiert ist. Aber der Reihe nach.

1632_Hack_3cDer Sketch

Da der Sketch am Ende doch etwas länger als erwartet geworden ist, steht dieser auch im Github Repository zum Download zur Verfügung.

Doch fangen wir erst einmal langsam an und bestimmen im Deklarationsteil vorab alle Arduino Pins mit denen das Panel verbunden ist.

Hier die Deklaration der Datenpins RGB Port 0 und RGB Port 1 als Konstanten:

/***  Deklariere Konstanten für Datenpins der RGB Farbenports  ***/
const unsigned int BAUDRATE    =  9600;// Serieller Monitor Baud Rate
const byte DATA_R0             =  2;   // Obere Panelhälfte Daten Rot
const byte DATA_G0             =  3;   // Obere Panelhälfte Daten Grün
const byte DATA_B0             =  4;   // Obere Panelhälfte Daten Blau
const byte DATA_R1             =  5;   // Untere Panelhälfte Daten Rot
const byte DATA_G1             =  6;   // Untere Panel Daten Grün
const byte DATA_B1             =  7;   // Untere Panel Daten Blau

Als nächstes die Steuerpins:

/***  Deklariere Konstanten für die Steuerpins  ***/
const byte CLK                 =  8;   // Clock, pos. Flanke
const byte OE                  =  9;   // Output Enable, neg. Flanke
const byte STB                 =  10;  // LAT Strobe), neg. Flanke

Und zuletzt die Adresspins, über die die jeweilige Zeile 1 bis 8 bzw. je nach Zählweise von 0 bis 7 adressiert werden kann.

/*      lsb
C  B  A  Zeile von oben
0  0  0    0
0  0  1    1
0  1  0    2
0  1  1    3
1  0  0    4
1  0  1    5
1  1  0    6
1  1  1    7  */

/***  Deklariere Konstanten für die Adressspins  ***/
const byte A                   =  A0;  // D14 Adresse A (LSB)
const byte B                   =  A1;  // D15 Adresse B
const byte C                   =  A2;  // D16 Adresse C (MSB)

In der void setup() Routine wird prophylaktisch der serielle Monitor initialisiert, auch wenn dieser in diesem Sketch zumindest bisher nicht benötigt wurde. Diese Zeile kann später jedoch noch zu jeder Zeit auskommentiert oder auch gelöscht werden. Entscheidender sind die folgenden Zeilen zur Initialisierung der RGB Datenpins Port 0 und Port 1. Hier werden die Arduino Digitalpins 2 bis 7 in der 1. Schleife auf OUTPUT gesetzt und in der 2. Schleife alle Ausgänge auf LOW geschaltet. Damit sind die LEDs ausgeschaltet. Würde R0 auf HIGH gesetzt, leuchtet eine rote LED der oberen Panelhälfte. HIGH auf B1 würde eine blaue LED der unteren Hälfte ansteuern.

  /***  Initialisiere 6 Datenpins des RGB Port 0 und RGB Port 1  ***/
  for (int i = 2; i <= 7; i++)         // RGB0=Pin 2-4 und RGB1=5-7
    pinMode(i, OUTPUT);                // Alle Datenpins als Ausgang
  for (int i = 2; i <= 7; i++)         // RGB Port 0 und 1 auf aus
    digitalWrite(i, LOW);              // Datenpins sind Active HIGH

Auf gleiche Weise werden nun die 3 Adresspins initialisiert und auf LOW gesetzt.

  /***  Initialisiere alle 3 Adresspins  ***/
  for (int i = 14; i <= 16; i++)       // Arduino Pin A0 bis A2
    pinMode(i, OUTPUT);
  for (int i = 14; i <= 16; i++)
    digitalWrite(i, LOW);              // Alle LOW=0, 8 / HIGH=7, 15

Zuletzt erfolgt die Initialisierung der 3 Steuerpins. Der CLK Pin wird auf LOW gesetzt, da mit der positiven Flanke getaktet wird. Durch setzen des OE Pin auf HIGH wird das Panel ausgeschaltet. Das Einschalten erfolgt mit der negativen Flanke. Mit einem LOW Signal am STB Pin werden nur die zuvor einmal gespeicherten Daten aus dem Latch ausgelesen.

  /***  Initialisiere alle 3 Steuerpins  ***/
  for (int i = 8; i <= 10; i++)        // Arduino Pin 8, 9, 10
    pinMode(i, OUTPUT);                // Alle Steuerpins Ausgang
  digitalWrite(CLK, LOW);              // Taktleitung zurücksetzen
  digitalWrite(OE, HIGH);              // Panel aus. 
  digitalWrite(STB, LOW);              // Nur Daten aus Latch 

In der folgenden Schleife wird das Taktsignal mit 32 positiven Flanken erzeugt und das weiter oben generierte LOW Signal am RGB0 und RGB1 Port in das Schieberegister getaktet. LOW bedeutet alle 3 LED-Kanäle der RGB LEDs sind ausgeschaltet! Warum das eigentlich? Nun, das ist nicht unbedingt notwendig, verhalf mir aber hier beim Hacken des Displays zu einen definierten Ausgangszustand zu kommen. Später kann man auf diesen Teil durchaus verzichten, sofern der Pixelspeicher sowieso zyklisch mit aktuellen Daten überschrieben wird.

Was aber immer noch fehlt, ist das Speichern dieser 3 * 32 LOW Signale in das Ausgangslatch mittels der fallenden Flanke eines kurzen HIGH Impulses am STB Pin. Mit anstehendem LOW Signal werden jetzt nur noch die gespeicherten Daten aus dem Latch am Ausgang berücksichtigt.

  /***  Schieberegister / Latch mit RGB-Daten mit LOW vorbelegen  ***/
  for (int p = 0; p < 32;  p++)        // 32 Pixel je Zeile
  {
    digitalWrite(CLK, LOW);            // Taktleitung zurücksetzen
    digitalWrite(CLK, HIGH);           // Datenübernahme pos. Flanke
  }
  digitalWrite(CLK, LOW);              // Taktleitung zurücksetzen
  /***  Strobe-Pulse RGB Port0/1 Schieberegister fix speichern  ***/
  digitalWrite(STB, HIGH);             // Datendurchgang HIGH
  digitalWrite(STB, LOW);              // Daten gespeichert mit LOW

Nun muss das Panel nur noch eingeschaltet werden, um die Früchte der Arbeit visuell zu betrachten, zumindest wenn nicht gerade alle RGB LEDs an beiden Ports durch LOW Pegel ausgeschaltet worden wären.

  digitalWrite(OE, LOW);               // Optional Panel einschalten

Wer möchte, endet an dieser Stelle der void setup() Routine erst einmal und belegt einzelne Pins der RGB Ports mit HIGH und beobachtet was passiert. Die void loop() bleibt innerhalb der geschweiften Klammern leer.

Beispiel:

 /***  Initialisiere 6 Datenpins des RGB Port 0 und RGB Port 1  ***/
  for (int i = 2; i <= 7; i++)         // RGB0=Pin 2-4 und RGB1=5-7
    pinMode(i, OUTPUT);                // Alle Datenpins als Ausgang
  for (int i = 2; i <= 7; i++)         // RGB Port 0 und 1 auf aus
    digitalWrite(i, HIGH);             // Datenpins sind Active HIGH

Werden wie oben gezeigt alle Pins auf HIGH gesetzt und der Sketch neu gestartet, sollte das Panel jetzt in voller Stärke in Weiß leuchten.

Der Sketch soll aber doch noch ein wenig komfortabler werden und erhält deshalb eine weitere ähnliche Routine nachdem die oben gerade veränderten RGB Ports wieder alle auf LOW gesetzt worden sind. Nachfolgend können beide RGB Ports 0 und 1 separat gesetzt werden. Durch die unten zusätzlich eingefügte Zeile mit dem delay() läßt sich der Aufbau einer Zeile sehr gut beobachten.

 digitalWrite(OE, LOW);               // Panel ein
  /***  Farbwerte der oberen Hälfte der Matrix  ***/
  digitalWrite(DATA_R0, LOW);
  digitalWrite(DATA_G0, HIGH);
  digitalWrite(DATA_B0, HIGH);
  /***  Farbwerte der unteren Hälfte der Matrix  ***/
  digitalWrite(DATA_R1, HIGH);
  digitalWrite(DATA_G1, HIGH);
  digitalWrite(DATA_B1, LOW);

  for (int p = 0; p < 32;  p++)        // 32 Pixel je Zeile
  {
    digitalWrite(CLK, LOW);            // Taktleitung zurücksetzen
    digitalWrite(CLK, HIGH);           // Datenübernahme 
    /***  Strobe-Pulse RGB Port0/1 Schieberegister fix ins Latch  ***/
    digitalWrite(STB, HIGH);           // Datendurchgang HIGH
    digitalWrite(STB, LOW);            // Daten gespeichert mit LOW
    delay(100);                        // Optional Delay 
  }
  digitalWrite(CLK, LOW);              // Taktleitung zurücksetzen

  digitalWrite(OE, HIGH);              // Panel ausschalten.

Auch die void loop() wird nun aktiviert. Zuvor wird jedoch noch folgende Funktion addressRow() definiert, mit der alle 8 Zeilen einer Pänelhälfte mit den Adressen von 0 bis 7 angesteuert werden können.  Da die obere und die untere Panelhälfte gemeinsam adressiert werden, reicht eine 3-bit Adresse. Zu dem Zweck lassen sich mit der Arduino Funktion bitread() sehr einfach einzelne Bits aus einer gegebenen Zahl extrahieren.  Jedem Adresspin, also A, B oder C ist einfach die entsprechende binäre Wertigkeit zuzuordnen. In diesem Fall dem Pin A also die Wertigkeit 1, B die Wertigkeit 2 und C die 4.

msb   lsb
C  B  A    Zeile von oben
0  0  0      0
0  0  1      1
0  1  0      2
0  1  1      3
1  0  0      4
1  0  1      5
1  1  0      6
1  1  1      7

/***  Funktion Zeilenaufbau. Jede Zeile des Panels ansprechen   ***/
void addressRow(byte ) {
  digitalWrite(A, bitRead(_address, 0));//LSB
  digitalWrite(B, bitRead(_address, 1));
  digitalWrite(C, bitRead(_address, 2));//MSB
}                                      // Ende der Funktion addressRow

Der Vorteil so einer Funktion liegt darin, diese mehrfach in einem Sketch verwenden zu können. Auch ist sie überschaubarer und bedingt portierbar in neue Sketche.

  digitalWrite(OE, LOW);               // Optional Panel einschalten
  /***  Adressiere alle Zeilen mit im Latch gespeicherten Daten  ***/
  for (int i = 0; i <= 7; i++)  {      // Zeile 1 bis 8, 9 bis 16
    addressRow(i);                     // Zeile, Verzögerung
    delay(100);                        // Optional Delay
  }

So bleibt dann auch die obige void loop() völlig überschaubar. In einer Schleife werden die möglichen Adressen von 0 bis 7 hochgezählt und innerhalb dieser Schleife die weiter oben definierte Funktion addressRow() durch Übergabe des Parameters i angewählt. Mit dem delay() in der letzten Zeile innerhalb der void loop() läßt sich der Wechsel der Zeile optisch wieder sehr gut verfolgen.  Werden wir dessen überdrüßig, so können beide delay() Zeilen gelöscht oder auskommentiert werden.

Nachfolgend der Sektch in einem Stück.

/********************************************************************/
/* 1632LEDPanelBasic - Simpler Sketch zum Debuggen der Funktionsweise des 16*32 Adafruit RGB LED Panels auch ohne Library. Die Verkabelung erfolgt allerdings entsprechend der Adafruit Library. Der Dateneingang mit Blick auf die LEDs befindet sich rechts. Der Bildaufbau ist von rechts nach links und von oben nach unten. Panel ist in 2 Sektionen unterteilt mit den Zeilen 0 bis 7 über R0, G0, B0 sowie den Zeilen 8 bis 15 über den Port RGB1 mit R1, G1, B1 erreichbar. 

Getestet mit:         Arduino Uno 
                      Arduino IDE 1.63 / 1.64 

Referenz:             https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/ 

Original Autor:       https://electronicfreakblog.wordpress.com/ 
Autor:                Olaf Meier 

Modifiziert von: 
Autor: 

Hardware Verbindung:  Ardu  -  Gerät 
                      +5V   -  - 
                      GND   -  GND 4 mal 
                      -     -  +5V / 2,5A Stromversorgung
                      -     -  GND        Stromversorgung 

Ergänzungen:          - 
*/
/********************************************************************/
/********************************************************************/
/***   Deklaration globaler Konstanten, Variablen, Bibliotheken   ***/
/********************************************************************/
/***  Software Version und Datum  ***/
const char* sketchname         =  "1632LEDPanelBasic";
const char* revision           =  "R.1.0";
const char* author             =  "Olaf Meier";
const char* date               =  "2015/11/29";

/********************************************************************/
/***  Deklariere Konstanten für Datenpins der RGB Farbenports  ***/
const unsigned int BAUDRATE    =  9600;// Serieller Monitor Baud Rate
const byte DATA_R0             =  2;   // Obere Panelhälfte Daten Rot
const byte DATA_G0             =  3;   // Obere Panelhälfte Daten Grün
const byte DATA_B0             =  4;   // Obere Panelhälfte Daten Blau
const byte DATA_R1             =  5;   // Untere Panelhälfte Daten Rot
const byte DATA_G1             =  6;   // Untere Panel Daten Grün
const byte DATA_B1             =  7;   // Untere Panel Daten Blau
/***  Deklariere Konstanten für die Steuerpins  ***/
const byte CLK                 =  8;   // Clock, pos. Flanke
const byte OE                  =  9;   // Output Enable, neg. Flanke
const byte STB                 =  10;  // LAT Strobe), neg. Flanke
/***  Deklariere Konstanten für die Adressspins  ***/
const byte A                   =  A0;  // D14 Adresse A (LSB)
const byte B                   =  A1;  // D15 Adresse B
const byte C                   =  A2;  // D16 Adresse C (MSB)
/********************************************************************/
/********************************************************************/
void setup() {
  /***  Starte den seriellen Monitor und warte auf Daten  ***/
  Serial.begin(9600);                  // Starte seriellen Monitor
  /*******************************************************************/
  /***  Initialisiere 6 Datenpins des RGB Port 0 und RGB Port 1  ***/
  for (int i = 2; i <= 7; i++)         // RGB0=Pin 2-4 und RGB1=5-7
    pinMode(i, OUTPUT);                // Alle Datenpins als Ausgang
  for (int i = 2; i <= 7; i++)         // RGB Port 0 und 1 auf aus
    digitalWrite(i, LOW);              // Datenpins sind Active HIGH

  /***  Initialisiere alle 3 Adresspins  ***/
  for (int i = 14; i <= 16; i++)       // Arduino Pin A0 bis A2
    pinMode(i, OUTPUT);
  for (int i = 14; i <= 16; i++)
    digitalWrite(i, LOW);              // Alle LOW=0, 8 / HIGH=7, 15

  /***  Initialisiere alle 3 Steuerpins  ***/
  for (int i = 8; i <= 10; i++)        // Arduino Pin 8, 9, 10
    pinMode(i, OUTPUT);                // Alle Steuerpins Ausgang
  digitalWrite(CLK, LOW);              // Taktleitung zurücksetzen
  digitalWrite(OE, HIGH);              // Panel aus. 
  digitalWrite(STB, LOW);              // Nur Daten aus Latch 
  /********************************************************************
 /***  Schieberegister / Latch mit RGB-Daten mit LOW vorbelegen  ***/
  for (int p = 0; p < 32;  p++)        // 32 Pixel je Zeile
  {
    digitalWrite(CLK, LOW);            // Taktleitung zurücksetzen
    digitalWrite(CLK, HIGH);           // Datenübernahme pos. Flanke
  }
  digitalWrite(CLK, LOW);              // Taktleitung zurücksetzen
  /***  Strobe-Pulse RGB Port0/1 Schieberegister fix speichern  ***/
  digitalWrite(STB, HIGH);             // Datendurchgang HIGH
  digitalWrite(STB, LOW);              // Daten gespeichert mit LOW
 /*******************************************************************/
 /***  Pixelaufbau einer Zeile                                    ***/
 /* Ausgabe der Farbwerte der obersten Zeilen 0 und 8 (bzw. 1 und 9)  
 Im oberen Segment wird die Farbe Cyan und im unteren Gelb angezeigt  Das Panel wird an dieser Stelle bereits eingeschaltet, um den   
 Pixelaufbau der Zeile sichtbar zu machen.   
*/
/********************************************************************/
  digitalWrite(OE, LOW);               // Panel ein
  /***  Farbwerte der oberen Hälfte der Matrix  ***/
  digitalWrite(DATA_R0, LOW);
  digitalWrite(DATA_G0, HIGH);
  digitalWrite(DATA_B0, HIGH);
  /***  Farbwerte der unteren Hälfte der Matrix  ***/
  digitalWrite(DATA_R1, HIGH);
  digitalWrite(DATA_G1, HIGH);
  digitalWrite(DATA_B1, LOW);

  for (int p = 0; p < 32;  p++)        // 32 Pixel je Zeile
  {
    digitalWrite(CLK, LOW);            // Taktleitung zurücksetzen
    digitalWrite(CLK, HIGH);           // Datenübernahme 
    /***  Strobe-Pulse RGB Port0/1 Schieberegister fix ins Latch  ***/
    digitalWrite(STB, HIGH);           // Datendurchgang HIGH
    digitalWrite(STB, LOW);            // Daten gespeichert mit LOW
    delay(100);                        // Optional Delay 
  }
  digitalWrite(CLK, LOW);              // Taktleitung zurücksetzen

  digitalWrite(OE, HIGH);              // Panel ausschalten.
}                                      // Ende Setup (Einmalig)
/********************************************************************/
/********************************************************************/
void loop() {
  digitalWrite(OE, LOW);               // Optional Panel einschalten
  /***  Adressiere alle Zeilen mit im Latch gespeicherten Daten  ***/
  for (int i = 0; i <= 7; i++)  {      // Zeile 1 bis 8, 9 bis 16
    addressRow(i);                     // Zeile, Verzögerung
    delay(100);                        // Optional Delay
  }
}                                      // Ende Loop (Endlos)
/********************************************************************/
/********************************************************************/
/***  Funktion Zeilenaufbau. Jede Zeile des Panels ansprechen   ***/
void addressRow(byte _address) {
  digitalWrite(A, bitRead(_address, 0));//LSB
  digitalWrite(B, bitRead(_address, 1));
  digitalWrite(C, bitRead(_address, 2));//MSB
}                                      // Ende der Funktion addressRow
/********************************************************************/
/********************************************************************/

Viel Spaß nun beim weiteren experimentieren. Die Basics zumindest sollten nun vorhanden sein. Zum Schluß nochmals der Link zum Download
Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s