Große LED-Matrix direkt Ansteuern

Hallo!

Ich befürchtem ich habe entweder einen großen Denkfehler, oder ich sehe den Wald vor lauter Bäumen nicht mehr.

Ich hab hier vor mir eine Große, selbst gebaute LED-Matrix stehen. 14 x 20 LED's, eine Farbe. Angesteuert wird das ganze über 34 Leitungen. Eine Leitung besteht aus einem Transistors und entweder einer kompletten Reihe oder Spalte) Gesteuert sollte das ganze über einen Arduino Mega werden. Mein Problem ist nun, das ich es nicht schaffe, die Framerate so hoch zu kriegen, das nichts flimmert.
Ich habe ein 2D Array mit den aktuellen Zuständen der einzelnen LED's, welche ich in einer Schleife kontinuierlich mit zwei FOR Schleife durchgehe und dan die jeweilige Spalte und Reihe durchschalte. Aber spätestens ab der hälfte (also 140 LED's) flimmert es ..

Wieviel würde es bringen, wenn ich die Update routine in Assembler programmiere (Ich frage deshalb weil ich mich mit Assembler noch nicht allzuviel beschäftigt habe und es einiges an Arbeit sein wird, mich da reinzuarbeiten . Deshalb vorher fragen :slight_smile: )? Welche anderen Sachen könnte man verbessern und/oder irgendwelche Tricks anwenden ?

Danke schonmal! Ich hoffe ihr versteht was ich meine :slight_smile: Sonst einfach nachfragen!
Mit freundlichen grüßen

Tartaros:
Wieviel würde es bringen, wenn ich die Update routine in Assembler programmiere (Ich frage deshalb weil ich mich mit Assembler noch nicht allzuviel beschäftigt habe und es einiges an Arbeit sein wird, mich da reinzuarbeiten . Deshalb vorher fragen :slight_smile: )? Welche anderen Sachen könnte man verbessern und/oder irgendwelche Tricks anwenden ?

Ohne das Programm zu sehen kann man keine Verbesserung vorschlagen :stuck_out_tongue:
Auch den Schaltplan bitte....

Zur generellen Beschleunigung solltest du auf digitalWrite() verzichten, da das sehr langsam ist.

Das kann man per Hand machen, aber es gibt auf digitalWriteFast():
http://code.google.com/p/digitalwritefast/downloads/list
http://www.billporter.info/2010/08/18/ready-set-oscillate-the-fastest-way-to-change-arduino-pins/

#include <Wire.h>

#define BAUDRATE 19200
const int NUMBER_X = 14;
const int NUMBER_Y = 20;
const int DELAY = 100;

// ARRAYS FOR THE PINS
int vertical[NUMBER_Y];
int horizontaly[NUMBER_X];
int LED_MATRIX[20][14] = {
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

// ==================================
//              SETUP
// ==================================
void setup()
{
  Serial.begin(BAUDRATE);

  // PIN BELEGUNG
  // ==== SENKRECHT 
  int wert = 22;
  vertical[0] = 18;
  for (int i = 1; i < NUMBER_Y; i++){
    vertical[i] = wert;
    wert += 2;
    if (wert == 54){
      wert = 19;
    }
  }
  // ==== WAAGRECHT
  wert = 27;
  for (int i = 0; i < NUMBER_X; i++){
    horizontaly[i] = wert;
    wert += 2;
  }

  // SET DIGITAL PINS TO OUTPUT
  for (int i = 0; i < NUMBER_Y; i++) {
    pinMode(vertical[i], OUTPUT);
  }
  for (int i = 0; i < NUMBER_X; i++){
    pinMode(horizontaly[i], OUTPUT);
  }
}

// ==================================
//              LOOP
// ==================================
void loop() {
    for(int x = 0; x < sizeof(horizontaly); x++) {
        for(int y = sizeof(vertical); y >= 0; y--) {
            if(LED_MATRIX[y][x] == 1) {
                digitalWrite(horizontaly[x], HIGH);
                digitalWrite(vertical[y], HIGH);
                delayMicroseconds(200);
                digitalWrite(horizontaly[x], LOW);
                digitalWrite(vertical[y], LOW);
            }
        }
    }
}

Mit diesem Code habe ich es getestet und man sieht es sehr schön, das es einfach nur flimmert! Dies dürfte aber nicht sein. Man muss ja bedenken, das sogar noch die Logik fehlt.

Die digitalWriteFast Library werde ich mir sofort ansehen! Danke schonmal für diesen Tipp!

Die Pin-Anzahl ist mir eigentlich ziemlich egal. Der Arduino sollte e nur diese Aufgabe erfüllen und mal die Matrix zum leuchten zu bringen. Aber abgesehen davon, verstehe ich noch nicht ganz, was du meinst.

Definiere deine Matrix mal als byte. Int hat zwei Byte. Das ist unnötig :slight_smile:

Stimmt! Ändert aber an der Geschwindigkeit kaum was :slight_smile: oder sehe ich das falsch ?

Auch habe ich Probleme digitalWriteFast zu verwenden. Ich bekomme immer einen Kompilerfehler:
error: invalid type argument of unary ‘*’ (have ‘uint8_t {aka unsigned char}’)

Ich denke es liegt darin, das meine pins auch als integer deklariert sind? Welchen typ brauche ich für meine Pins und wie caste ich einen INT zu diesem Typ?

Nein, schneller wird es nicht. Senkt nur den RAM-Verbrauch um die Hälfte.
Probier mal digitalWriteFast2(). Da gibt es irgendwie zwei Versionen von read, write und pinMode

EDIT: Ich habe mir mal gerade die readme angeschaut und es gibt schlechte Nachrichten:

The extra speed depends on the pin numbers( and HIGH/LOW values ) being known at compile time--it won?t speed things up if its inside a loop or subroutine where the pin number is going to change. If the pin number is not known at compile time it defaults to use the standard (slower) digitalWrite command.

:frowning: Sorry, war mir nicht bewusst

Alles klar.

digitalWriteFast2() funktioniert zwar, bringt aber kaum was. Auch hab ich das sizeOf() aus der FOR rausgenommen, bringt aber auch nicht ersichtlich viel. Habe nun das delayMicroseconds() auf 15 microsekunden verringert. Man erkennt es noch minimalst das es flackert. Aber eben ohne logik ...

Auch ist mir grad aufgefallen, wenn ich in meiner Matrix alles auf 0 setze, und eine 1 reinsetz leuchten 5 LED's.
Entweder ich hab noch nen Logikfehler in meinem Code, oder es stimmt mit der verdrahtung etwas nicht ...

Deine Herangehensweise zur Matrixansteuerung ist nicht optimal. Du bildest jeden Punkt einzelbn ab und machst dann eine Pause von 200µs. Bei einem Durchlauf von 14x20 LEDs sind das schon 56 Millisekunden, also kommst du prinzipbedingt schon nicht über knapp 18 Hz hinaus - und ohne die Zeiten zu berücksichtigen, die für das Pinsetzen verantwortlich sind. Dass das flimmert, sollte klar sein.
Verbesserung sollte zunächst die Verringerung der Pausenzeit bringen. Doch selbst das ist noch nicht das Ende der Fahnenstange, man sollte weiter optimieren. Der Vorteil einer Matrix besteht doch darin, dass man eine ganze Zeile zugleich leuchten lassen kann. Das heißt erst alle digitalWrites für eine Reihe vorgeben und die Kathode mittels Transistor auf GND ziehen usw..

sth77:
Deine Herangehensweise zur Matrixansteuerung ist nicht optimal. ...

@ sth77, Du bist ein großer Schmeichler. ich hätte das einfach als FALSCH bezeichnet.

@ Tartaros:Ich würde die 20 LED auf die Pins von 3 PORTs legen sodaß die 3 Byte in einem 3 einzigen Schreibzugriffen geschrieben werden können.
So hast Du 14 Zeile die nacheinander geschrieben werden müssen.
Welche Vorwiderstände hast Du verwendet?
Grüße Uwe

@uwefed:
Klingt als ob ich einen total falschen Ansatz gewählt habe, und mir darüber zu wenig Gedanken gemacht habe und einfach drauf los gemacht habe :zipper_mouth_face: delayMicroseconds() habe ich, weil damit die LED's stärker leuchten. Normalerweise sollten sie viel heller leuchten ...

Verwendete Transistoren: BC 33 7-45
Verwendete Vorwiederstände: 560Ohm

Könntest du mir bitte deine beschriebene Taktik etwas genauer erklären? Es klingt aber bereits danach, als ob ich eine falsche PIN-Belegung habe. Meine derzeitige:
Senkrechte Ausgaenge von oben
18, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 19, 21, 23
Waagrechte Ausgaenge von außen
27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53

Der Prozessor verwaltet die Pins intern mit Ports. Jeweils 8 Pins sind ein Port. Die werden mit Buchstaben A bis ... bezeichnet. Für jeden gibt es dann ein Eingangsregister, ein Ausgangsregister und ein Richtungsregister.

Das Ausgangsregister heißt PORTx, z.B. PORTA oder PORTB. Man kann dann sowas machen:
PORTA = 11110000b

Siehe hier:

http://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/

Du brauchst dann noch das Mega Pinout um die Ports des Prozessors den Pins des Arduinos zuzuordnen:
http://www.pighixxx.com/pgdev/Temp/ArduinoMega_b.png

Die Port-Bezeichnungen sind da in gelb, z.B. PB5 = PortB, Pin/Bit 5. Du musst dir dann jeweils 8 Pins suchen, die den gleichen Buchstaben haben und diese zusammen ansprechen.

Tja ich denke ich hab mir eben meinen Arduino zerschossen. Ich kann zumindest keinen Code mehr hochladen :fearful:
Programmer is not responding.

Aber ich denke, ich hab das Prinzip verstanden und werd mich mal dransetzen, das so umzusetzen!
Danke für eure Hilfe!

Du könntest die PORTS A, C und K nehmen (pins 22 bis 29, pins 30 bis 37 und A8 bis A15).

@Tartaros
Was hast Du gemacht, um den Arduino kaputtgemacht zu haben.

Grüße Uwe

An was mein Arduino kaputt gegangen ist, würd mich auch interessieren. Aber da war nichts mehr zu machen.
Kein neuinstallieren der Treiber (sowhl Windoof als auch Linux) noch neues flashen von Bootloader oä. Einfach kaputt.
Hab mir heute Vormittag einen neuen geholt .

Eine Frage hätte ich aber noch.
Du meinst ja, ich solle doch die Pin-Belegung ändern -> getan -> nur welche möglichkeiten fallen euch ein, um mein Array (kan es auch anders abspeichern!!) pro Reihe in 2 Binärdatentypen (wie nennt man das ?) zu verwandeln?
Also wenn mein Array für eine Reihe so aussieht :{0,0,1,1,0,0,1,1,0,0,0,0,0,0},, ich das bekomme:
B00110011 und B00000000
?
Kann man den mit einer IF soeinen Typ mit & oder + verknüpfen (unwahrscheinlich?) .
Muss/Kann ich das über ein #define machen oder ein Array, welches den Binärcode beinhaltet und über eine Zahl zur Verfügung steht? Bremst das, das Programm nicht auch sehr aus, wodurch das alles recht Sinnlos sein dürfte?

Danke schonmal für eure Antworten!
Mit freundlichen grüßen und noch einen schönen Abend :slight_smile:

Nimm einfach ein Array aus ints (16 Bit) oder ein 2-dimensionales Arrays aus Bytes.

Hier ist ein Beispiel für eine Matrix:
http://playground.arduino.cc/Code/BitMath#bit_pack

Die steht da aber im Flash. Wenn du sie also verändern willst ist das nichts. Aber man kann den Progmem Teil auch einfach weglassen, wenn du schreibend darauf zugreifen willst.

Aber Achtung:
Bei dem Code ist jeder Pixel einzeln angesteuert! Das ist es ja was du nicht willst. Das lässt sich aber leicht anpassen, wenn man die innere for-Schleife entfernt. Statt über die ausgelese Zeile zu iterieren und einzelne Bits herauszupicken kann man sie dann einfach auf ein Port-Register schreiben.

P.S.:
Ich würde mich nicht darauf verlassen, dass const alleine ein größeres Array wirklich ins Flash schreibt. Mit einzelnen Variablen geht das, aber der Compiler kann const in diesem Sinne auch ignorieren. Dafür gibt es PROGMEM:

Du kannst es aber auch erst mal im RAM lassen. So viel Speicher verbraucht das nämlich bei dir nicht, solange du nicht zig Muster definierst :slight_smile:

Aber Hallo danke für deine Hilfe Serenifly!
Ich hab nun immerhin schon vertikale und senkrechte Streifen mit direkt Port manipulation hinbekommen!

Kann man einen INT in 2 PORTS schreiben?
Also ein Int hat ja normalerweise 16Bits? Also müsste es doch irgendwie möglich sein, auf einfachste Art und Weise den Int in der Mitte zu durchschneiden, und dan jeweils PORTA und PORTB zuweisen ?

Ich seh schon das wird heute eine lange Nacht :smiley:

Dafür gibt es Shift Operatoren. Alternativ wie gesagt ein zwei-dimensionales Array, aber das ist letztendlich komplizierter.

int i = 0xAABB;
byte high = i >> 8;    //schiebt i 8 mal nach rechts
byte low = i;

Danach steht in high 0xAA und in low 0xBB. Der Integer wird bei der Zuweisung auf Byte einfach oben abgeschnitten, daher ist es nicht nötig das High-Byte explizit mit UND auszumaskieren

Halleluja!
Und ich hab das mit 4 IFS gemacht :drooling_face:

Danke Danke Danke!
Ich werd mich jetzt erstmal ausgiebig mit bitschieberei und änderei befassen und mich notfalls nochmal hier melden :slight_smile: !
Danke Danke Danke!

Das Forum hier ist echd der Wahnsinn -

Wenn du einzelne Bits setzen, löschen oder auslesen willst hat die IDE da auch ein paar fertige Funktionen dafür:
http://arduino.cc/en/Reference/BitRead
http://arduino.cc/en/Reference/BitWrite
http://arduino.cc/en/Reference/BitSet
http://arduino.cc/en/Reference/BitClear

Die kapseln aber auch nur eine Kombination aus Bit-Shift und boolscher Logik. Der traditionelle Weg das zu machen ist mit Makros:

#define BITSET(p,n) ((p) |= (1 << (n)))
#define BITCLEAR(p,n) ((p) &= ~((1) << (n)))

Obwohl inline Funktionen dafür schöner sind wenn man wirklich maximale Geschwindigkeit will.