RGB 8x8x8 Cube (TLC5940+HC595) - Brauche nen Schubs

Hallo zusammen,

ich lese schon länger hier im Forum, das hier ist aber glaube ich mein erster größerer Post.
Es ist ne Menge Text, bitte lest ihn trotzdem durch damit ihr mein Problem auch versteht... :wink:

Ich habe vor Kurzem einen alten LED-Cube (4x4x4 monochrom) von mir ausgegraben und bissl damit gespielt - das weckte die Lust auf mehr und so habe ich beschlossen direkt "in die Vollen" zu gehen und 8x8x8 in RGB zu bauen.

Mir ist bewusst, dass das ein ambitioniertes Projekt ist und ich bin seit ein paar Tagen am Forschen und Testen.

Die Grundidee ist Folgende:
Ein Dutzend TLC5940 übernehmen die Ansteuerung der 64 RGB LEDs einer Ebene.
Die Umschaltung der Ebenen geht über ein HC595 Schieberegister das wiederum acht HighSide Treiber ansteuert (Logic-Level P-Channel MOSFETs mit PNP/NPN Treiberstufe).

Um das Ganze zu testen habe ich eine 5x8 RGB Matrix fliegend verdrahtet.
Statt der MOSFETs kommen bei diesem Testaufbau noch BC337 als Ebenentreiber (bzw. in diesem Fall Zeilentreiber) in Kollektorschaltung zum Einsatz. Durch die Kollektorschaltung gehen die NPNs nicht in Sättigung und ich brauche mir wenig bis keine Gedanken zum Abschaltverhalten zu machen.

SPI-seitig hängt das HC595 am Arduino, ein einzelner TLC5940 hängt in der Daisy-Chain hinter dem HC595.
Arduino MOSI -> HC595 Sin
HC595 Sout -> TLC5940 Sin
Arduino SCK (13) -> HC595 SRCLK -> TLC5940 SCLK
Arduino 10 (BLANK) -> HC595 /OE -> TLC5940 BLANK
Arduino 9 (Latch) -> HC595 RCLK -> TLC5940 XLAT
Arduino 3 (PWM) -> TLC5940 GSCLK

Die Blank-Leitung hat zusätzlich noch einen PullUp von 10k damit da auch ja nix wackelt.

Das Ganze habe ich fliegend auf einem Steckbrett verdrahtet und verwende zur Ansteuerung die TLC5940 Lib von Sparkfun.
Diese habe ich modifiziert und um rudimentären Support für das Multiplexing erweitert.
Kernpunkt ist, dass man zusätzlich zur Anzahl der TLCs in der Chain nun auch noch die Anzahl der Ebenen konfigurieren kann. Dadurch wird u.U automatisch mehr Speicher für den Puffer allokiert.

Die update() Methode muss nicht mehr manuell aufgerufen werden, die Library refresht nun automatisch Ebene für Ebene. Dafür habe ich die bestehende Overflow-Interrupt Routine aus der Lib etwas modifiziert.

Ergebnis: Die Testmatrix läuft mit stabilen 122Hz Wiederholfrequenz (976Hz Zeilenfrequenz).

Jetzt war ich neugierig und habe einfach mal 8 TLCs konfiguriert (mehr geht mit nem Arduino UNO nicht weil das RAM nicht reicht) um zu schauen wie gut das noch funktioniert.
Durch den Aufbau der SPI-Chain mit dem HC595 als erstes Glied sind die weiteren TLCs quasi einfach "virtuell" vorhanden. Die Daten für die virtuellen Chips werden rausgetaktet, verschwinden aber im Nirvana - braucht ja auch keiner... :wink:

Ergebnis: Die 122Hz werden immer noch erreicht, ABER es bleibt scheinbar nicht mehr viel Zeit außerhalb der Bitshifterei um noch Animationen zu berechnen. Der einfache HSV-Colorfader den ich zum Test laufen lasse wird jedenfalls merklich(!!!) langsamer, d.h. die Farben laufen deutlich langsamer durch wenn ich virtuelle TLCs konfiguriere vs. wenn ich nur den einen, echten TLC konfiguriere.
(Die Farbwerte werden in beiden Fällen nur für den echten TLC berechnet, die Rechenarbeit für die Animation verändert sich also nicht...)

Damit sind wir bei meinem Problem: Für das Projekt scheinen die "normalen" Arduinos nicht mehr auszureichen weil sie beim SPI-Transfer einfach komplett blockiert werden. Weiterhin wären beim späteren Cube ja auch nochmal 50% mehr an Daten zu senden (12 TLCs an Stelle der 8 mit denen ich getestet habe).

Ich könnte die Updates seltener fahren, aber dann sinkt die Wiederholfrequenz direkt auf 60Hz und das nehme ich bereits als leichtes Flimmern wahr. Somit ist das also keine echte Lösung.
Und z.B. 80Hz kann ich nicht anfahren, denn wenn bei "so schnell wie möglich" 120Hz rauskommen kann ich höchstens "jede zweite Periode" nutzen und lande dann direkt bei 60Hz.

Nun die Frage an die Spezialisten unter euch:
Welchen Controller könnte ich nehmen?
Wären die ARMs eine Alternative?

Würde die Nutzung von Interrupt-basiertem SPI was bringen? Pro Byte vergehen 16 Takte, könnte man von denen evtl. noch ein paar für das Hauptprogramm nutzen?

Bin für Ideen und Anregungen dankbar! :slight_smile:

Grüße aus Hessen,

Shuzz

Die Lib von Sparkfun funktioniert nicht.
Die Ebenen müssen kontrolliert dh synchron zum PWM des TLC5940 umgeschaltet werden. Nimm die Lib von GrumpyMike Mini Monome Link im letzten Absatz http://www.thebox.myzen.co.uk/Hardware/Mini_Monome_files/Arduino_Firmware.zip

Außerdem braucht der Bildspeicher schon 8x8x8 Led *3 Farben * 12/8 Bit/Byte =2304 Byte. Ein Arduino Uno ist da zu klein.

Mir wären TLCs zu kompliziert. Warum nimmst Du keine WS2812 LEDs

Auch brauchst Du einen stärkeren Controller (Arduno MEGA oder besser einen teensy >=3.6)

Bei 512 LED ist mit den WS2812 eine theoretische Wiederholrate von 65Hz (bezogen auf jeden einzelne LED) möglich aber nicht realisierbar weil andere Berechnungen zwischen den Updates gemacht werden müssen. 1000000/(512 LED mal 24 Bit *1,25µS)

Grüße Uwe

Hallo,

bei Hardware SPI landet immer ein Byte im Buffer was dann Programm unabhängig rausgeschoben wird. Ob das einen Vorteil bringt kommt darauf wie die Daten entstehen und rausgehen sollen. Berechnet man alles und übergibt es dann der SPI, bringt es keinen Vorteil, weil man dann in der eigenen "Sendeschleife" hängt. Es soll ja alles hintereinander raus. Deswegen macht das eigentlich keinen Unterschied. Man muss nur nicht mehr auf das letzte Byte warten welches raus geht.

Schiebt man nur ein Byte raus und macht bis zum Nächsten etwas anderes, dann bringt es einen Vorteil. Weil man nicht warten muss bis das Byte komplett raus ist.

Das kurz dazu. Zu den Led Effekt Spezialisten gehöre ich nicht.

@uwefed: Die Lib von Sparkfun habe ich selbst umgeschrieben, durch die Verwendung eines HC595 mit den TLCs klappt das mit dem synchronen Umschalten einwandfrei. Meine Testmatrix arbeitet ja stabil.

Die Lib von GrumpyMike war die Vorlage/Inspiration für meinen eigenen "Hack". Hat aber nicht so richtig auf meine Bedürfnisse gepasst, daher hab ich selbst Hand angelegt.

Teensy ist ne Idee, aber auch ziemlich teuer... o0
Kannst Du was zur Programmierung der Timer bei dem Teil sagen?
Funktioniert das ähnlich wie bei den ATMega-basierten Arduinos? Bisher habe ich um die ARMs immer nen Bogen gemacht weil sie von der Hardware her doch deutlich komplexer sind als die ATMegas...

Ansonsten liebäugele ich derzeit mit nem ESP32.
Der scheint ziemlich flexible PWM Möglichkeiten zu besitzen, nur wie genau man das Latch mit dem Blank synchronisieren könnte sehe ich noch nicht...

Edit: Über WS2812 habe ich auch schon nachgedacht, hat für mich aber verschiedene Nachteile:

  1. Das Timing ist ziemlich haarig, ob ich da überhaupt 512 LEDs in Reihe kriege weiß ich nicht.
  2. Die LEDs sind auch bei direkter Bestellung in China relativ teuer.
  3. Ich muss immernoch große Mengen an Daten schaufeln...

@Doc_Arduino: Genau das ist mein Problem bei den ATMegas: Mit der Standard Arduino SPI.send() Methode blockieren sie bis das komplette Byte gesendet ist.
Ich weiß, dass man das SPI-Sende auch in einen Interrupt verlegen kann der beim kompletten versenden eines Bytes aufgerufen wird, ich bin aber nicht sicher ob sich der Aufwand überhaupt lohnt.
Der Aufruf des Interruptvektors braucht ja auch schon einige Takte und ich befürchte, dass man damit unterm Strich langsamer fährt als wenn man auf das Ende des Sendevorgangs wartet, zumindest bei maximaler SPI-Geschwindigkeit.

Edit: Den Code zum raustakten mit dem Code für die Effekte zu verweben würde nicht funktionieren. Das müsste für jeden Effekt separat gemacht werden, dazu kommen noch unterschiedliche Geschwindigkeiten der Effekte usw. usf. - theoretisch vllt. machbar, praktisch aber ein absoluter Albtraum.

ok, Bist also schon weiter als ich gedacht hatte.
Grüße Uwe

Hallo,

die Standardmethode übergibt erst das Byte an das SPDR Register und wartet dann bis es wieder frei.
Man kann das auch umdrehen. Erst prüfen prüfen ob frei ist und dann das neue Byte an SPDR übergeben.

while (!(SPSR & (1 << SPIF)))   // wait for transmission complete
   ;           
SPDR = data;                     // Start transmission

Eines muss man dabei beachten. Bevor das aller erste Byte gesendet werden kann, muss etwas in das SPDR schreiben. Ich schreibe eine 0 rein im setup. Sonst wird das SPIF Flag nicht gelöscht.

Nur wie gesagt, ob das was bringt kommt drauf an. Wenn du alles hintereinander senden möchtest, sparst du nur die Zeit vom letzten Byte ein. Und die Pause zwischen den Bytes ist ein klitzeklein wenig kürzer. Mit 4MHz SPI dauert das senden eines Bytes ziemlich genau 2µs. Fragst du nicht per while sondern mit if ab und kannst damit möglicherweise kurz was anderes machen, wird bestimmt dein senden insgesamt langsamer. Denn 2µs (32 Takte) sind auch schnell verplempert. Das lohnt laut meiner Meinung nach nicht. Der Aufruf und verlassen einer ISR dauert bestimmt länger.

Weil das hier mal Thema war im Forum, vielleicht benötigst einen µC mit DMA Zugriff. Dann macht der das wirklich ohne weiteres zu tun selbstständig. Aber davon habe ich keine Ahnung.

uwefed:
ok, Bist also schon weiter als ich gedacht hatte.

Ja, so den einen oder anderen Gedanken habe ich mir schon gemacht und auch ausprobiert soweit möglich...

Ich hab mir den Teensy nochmal genauer angesehen - da gibt es ja sogar schon ne angepasste TLC library für.
Und so wie die aussieht müsste ich die exakt genauso umschreiben können wie die "originale" auch, d.h. der Umstieg sollte schonmal gar nicht sooo schwer sein.

Leider benutzt die Lib von paulstoffregen noch kein DMA-basiertes SPI, aber ich traue mir zu das bei Bedarf noch einzufügen.

Im ersten Schritt werde ich mir nen Teensy ordern und mal sehen was so geht. Vllt. reicht die Power ja auch ohne DMA bereits aus... :wink:

Bis hierhin auf jeden Fall schonmal Danke! :slight_smile:

So, ich bin nochmal in mich gegangen und habe eine (etwas hackige) Lösung für mein Problem gefunden - ohne ARM... :wink:

Und zwar habe ich mir einen Arduino Mega2560 zugelegt um die drei der vier USARTs für die SPI Transfers zu nutzen. Durch den Send-Buffer ist ein ununterbrochener Datenstrom gewährleistet und wenn man die TLCs nach Farben auftrennt ist auch das raustakten der Daten sehr gut parallelisierbar.

Leider hat das Board aber die XCKn Pins der USARTs nicht auf die Pin-Header rausgeführt...
(Warum eigentlich nicht? Ist doch eh schon eine eigene Pfostenleiste für die USARTs vorhanden, die vier Pins mehr hätten den Kohl nicht fett gemacht... -.-)

Kurz überlegt, Lötkolben rausgeholt und drei Kleckse Lötzinn später hatte ich die XCK1-XCK3 Signale dort wo ich sie brauche: Auf den Pinheadern. Und zwar auf den Pins 6, 38 und 39.

Im Austausch dafür kann ich die OC4A, T0 und ALE Funktionalitäten nicht mehr nutzen, oder zumindest nicht gleichzeitig mit den USARTs. Für dieses Projekt ein annehmbarer Tausch...

Tests mit dem Oszilloskop zeigen, dass es kein Problem ist die drei Datenströme parallel und unterbrechungsfrei zu senden.

Somit brauche ich für einen Ebenenrefresh des Cubes nun rechnerisch nur noch knappe 1600 Takte - das ist denke ich absolut akzeptabel und lässt selbst auf einem ATMega noch mehr als genug Rechenzeit auch für komplexere Animationen.

Im Anhang noch ein Bild von der Operation. Schön isses nicht, aber es funktioniert... :wink:

Grüße,

Shuzz

Hallo,

die Idee finde ich cool. :slight_smile:

Danke!

Ich wollte zuerst Fädeldraht an die Pins löten, aber das hätte nie im Leben gehalten und als ich so drüber nachdachte wie ich dabei Lötbrücken vermeiden könnte kam mir die Idee... :wink:

Shuzz:
Edit: Über WS2812 habe ich auch schon nachgedacht, hat für mich aber verschiedene Nachteile:

  1. Das Timing ist ziemlich haarig, ob ich da überhaupt 512 LEDs in Reihe kriege weiß ich nicht.

Für Dein nächstes Projekt könntest Du APA102 (gibt es auch mit anderen Bezeichnungen und in unterschiedlichen Bauformen) in Erwägung ziehen, die haben Takt und Daten getrennt, wodurch höhere Taktraten zu erreichen sind. Ich hänge die entweder an Hardware-SPI vom Nano (Uno ist meist zu groß) oder an einen Teensy 3.2 (der ist an normalen Pins schnell genug, also Software SPI mit gedrosselter Geschwindigkeit). Derzeit hängen bei mir 150 und 300 Lichtpunkte.

Nur mal so als Anregung.

Gibt es APA102-basierte LEDs auch im Standard 5mm Gehäuse?
Ich finde die nur als SMD-Varianten...

Doof ist halt, dass ich dann Takt UND Din/Dout quer durch den Cube verkabeln müsste.
Dann wird der Cube zu ner prima Sendeantenne für die hochfrequenten SPI-Clocks und Daten.
Radios könnten da schon empfindlich gestört werden, darum lasse ich sowas lieber auf der Platine.

Aber danke für die Anregung! :slight_smile:

Danke, aber APA106 hat ja nun wieder keine Clock-Leitung, daher wieder nur relativ niedrige Datenrate...

Shuzz:
Gibt es APA102-basierte LEDs auch im Standard 5mm Gehäuse?

Leider nur etwas größer, hier ein Beispiel.