Ledmatrix 32x64 RGB mit 12bit-Farben. Wie machen die das?

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Ledmatrix 32x64 RGB mit 12bit-Farben. Wie machen die das?

      Ich hab' jetzt so ein 32x64 RGB display und kann jetzt selber mit so einem display rumspielen. Es handelt sich dabei um so ein Teil, was auch adafruit im Programm hat. adafruit.com/product/2278
      Ich steuere es derzeit mit einem atmega32 mit 16Mhz an. Das display hat einen 1/16 scan, das heißt, es müssen 16x Daten in das display geladen und angezeigt werden, bis das komplette display 1x den ganzen Inhalt zeigt. Pro scan werden von den 32 Zeilen 2 Zeilen gleichzeitig angezeigt. Es zeigt sich bei meinen Versuchen, dass eine Wiederholrate der isr, die eben 2 Zeilen zur Anzeige bringt, von 1,15ms schon nötig ist, um ein Flackern zu vermeiden. Nach 16x 1,15ms=18,4ms ist ein Vollbild fertig, ergibt eine Bildfrequenz von 54Hz.
      Meine isr braucht dazu 0,25ms, ergibt eine Belastung von 22% (was noch nicht berücksichtig wurde sind die Zeiten für push und pop von den Registern).
      Ich kann jetzt 3bit Farben darstellen, es gibt für jede Farbe einen Bildspeicher, pro Farbe 32x8 bytes=256 bytes, alles zusammen dann 768 bytes.
      Jetzt lese ich im obigen link:
      This matrix has 2048 bright RGB LEDs arranged in a 64x32 grid on the front. On the back there is a PCB with two IDC connectors (one input, one output: in theory you can chain these together) and 12 16-bit latches that allow you to drive the display with a 1:16 scan rate.
      These displays are technically 'chainable' - connect one output to the next input - but our Arduino example code does not support this (yet). It requires a high speed processor and more RAM than the Arduino has!
      This matrix has 2048 bright RGB LEDs arranged in a 64x32 grid on the front. On the back there are two IDC connectors (one input, one output: in theory you can chain these together) and 12 16-bit latches that allow you to drive the display with a 1:16 scan rate.
      These panels require 13 digital pins (6 bit data, 7 bit control) and a good 5V supply, up to 4A per panel. We suggest our 4A regulated 5V adapter and then connecting a 2.1mm jack. Please check out our tutorial for more details!
      Comes with:
      • A single 64x32 RGB panel,
      • An IDC cable
      • A plug in power cable
      • We also include 4 mounting screws and mini-magnets (it appears these are often mounted on a magnetic base).
      Keep in mind that these displays are designed to be driven by FPGAs or other high speed processors: they do not have built in PWM control of any kind. Instead, you're supposed to redraw the screen over and over to 'manually' PWM the whole thing. On a 16 MHz Arduino Mega, we managed to squeeze 12-bit color (4096 colors) with 40% CPU usage but this display would really shine if driven by any FPGA, CPLD, Propeller, XMOS or other high speed multi-core controller. The good news is that the display is pre-white balanced with nice uniformity so if you turn on all the LEDs it's not a particularly tinted white.
      Of course, we wouldn't leave you with a datasheet and a "good luck!" We have a full wiring diagrams and working Arduino library code with examples from drawing pixels, lines, rectangles, circles and text. You'll get your color blasting within the hour! On an Arduino, you'll need 16 digital pins, and about 3200 bytes of RAM to buffer the 12-bit color image.

      Ich interpretiere das so, dass die für jede Farbe 4x den Bildspeicher haben und eben dann mit 4facher Geschwindigkeit die Daten an das display senden, um so eine 4bit PWM hinzukriegen. Jetzt habe ich gedacht, meine isr wäre schnell, die ist aber im Vergleich mit denen ihrer wohl kreuzlahm. Wo kann ich jetzt was verbessern.
      Hier das komplette Programm
      Matrix-RGB32x64-multi-V0.21.bas
      Font_6x8.bas
      Font_8x16.bas
      Font_10x16.bas
      Font_12x16.bas
      und hier die isr

      BASCOM Source Code

      1. 'Interrupt service
      2. Display_refresh: 'show the memory on the display
      3. Load Timer0 , Timer0reload
      4. Incr Page_counter
      5. If Page_counter > 15 Then Page_counter = 0
      6. Index_1 = Page_counter * Bytes_in_row
      7. Index_2 = Index_1 + Channel_offset
      8. For D_2 = 1 To Dy_num_v
      9. For D_1 = 1 To Bytes_in_row
      10. Shift6out Memory_red(index_1) , Memory_green(index_1) , Memory_blue(index_1) , Memory_red(index_2) , Memory_green(index_2) , Memory_blue(index_2)
      11. Incr Index_1 : Incr Index_2
      12. Next D_1
      13. Index_1 = Index_1 + Dy_offset : Index_2 = Index_2 + Dy_offset
      14. Next D_2
      15. Pwm2 = 0
      16. Waitus 20
      17. P_lat = 1 : P_lat = 0
      18. P_a = Page_counter.0
      19. P_b = Page_counter.1
      20. P_c = Page_counter.2
      21. P_d = Page_counter.3
      22. Pwm2 = Brightness 'display on
      23. Trigger = 1
      24. Return
      25. ' R1 G1 B1 R2 G2 B2
      26. Sub Shift6out(byreg R18 As Byte , Byreg R19 As Byte , Byreg R20 As Byte , Byreg R21 As Byte , Byreg R22 As Byte , Byreg R23 As Byte )
      27. 'MSB first
      28. $asm
      29. LDI r17,8
      30. Loop_shift2out:
      31. in R16, dout_port_r1
      32. BST r18,7
      33. BLD r16,Dout_pin_r1
      34. Out Dout_port_r1 , R16
      35. in R16, dout_port_g1
      36. BST r19,7
      37. bld r16,Dout_pin_g1
      38. Out Dout_port_g1 , R16
      39. in R16, dout_port_b1
      40. BST r20,7
      41. bld r16,Dout_pin_b1
      42. Out Dout_port_b1 , R16
      43. in R16, dout_port_r2
      44. BST r21,7
      45. BLD r16,Dout_pin_r2
      46. Out Dout_port_r2 , R16
      47. in R16, dout_port_g2
      48. BST r22,7
      49. bld r16,Dout_pin_g2
      50. Out Dout_port_g2 , R16
      51. in R16, dout_port_b2
      52. BST r23,7
      53. bld r16,Dout_pin_b2
      54. Out Dout_port_b2 , R16
      55. SBI shift_clock_Port, shift_clock_Pin
      56. LSl r18
      57. LSl r19
      58. lsl r20
      59. lsl r21
      60. lsl r22
      61. lsl r23
      62. dec r17
      63. CBI shift_clock_Port, shift_clock_Pin
      64. BRnE loop_shift2out ; if = 0 exit
      65. $end Asm
      66. End Sub
      Display All
      Es ist halt so, dass an 6 Eingängen vom display die bits für die Farben anliegen müssen und mit einem gemeinsamen clock reingeschoben werden. Ein Verketten von den Ausgängen der einen Farbe zu den Eingängen der anderen Farben wäre schon auch möglich, bedeutet aber eine 6x so hohe Tacktrate, bis die bits schließlich an der gewünschten Position angekommen sind.
      (Der Assemblercode ist übrigens von @Galahat für mich entwickelt worden.)
      Die isr ist ja wirklich überschaubar, wo ist jetzt der Trick, dass die Adafruitler so schnell sind?

      Ach ja, es geht jetzt nur um ein angeschlossenes display. Mein Programm kann zwar mit dem mega32 bis zu 2 displays betreiben, aber dann braucht die isr halt auch doppelt so lange.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Meine Überlegungen hauen nicht hin. Die haben für jede Farbe 4 bit, also wären das bei mir 8x den Bildspeicher, den ich für jede Farbe habe. Dann bräuchte ich aber 6144byte. Wenn die nur 3200 byte verbrauchen, dann speichern die für jeden pixel 1,5 bytes ab, dann komme ich auf 3072 bytes.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Also ich denke erstmal das die ISR von BASCOM schonmal zum u.a. sicher/rücksichern der Register deutlich länger braucht als der optimierte C++ Code. Dann werden alle Variablen (so ist mein Wissensstand) immer brav zwischen RAM und Registern hin- und herkopiert - aber vielleicht täusche ich mich auch und es werden doch Variablen in Registern „zwischengelagert“.

      Mit den Farben hatte ich es auch so verstanden das je 4 Bit verwendet werden. Empfohlen werden schnellere Prozessoren mit mehr RAM. Naja, könntest es so wie ich machen und übertakten :evil:
      Aus datenschutzrechtlichen Gründen befindet sich die Kontaktdaten auf der Rückseite dieses Beitrages.
    • Ich würde versuchen, den wiederholten Aufruf der Sub Shift6out wegzulassen.
      Alleine der Aufruf kostet 75 Takte wegen der Array-Elemente, die Sub wird dann in 300 Takten ausgeführt, also 20% Overhead. Und in der inneren For-Schleife werden doch nur die beiden Indizes erhöht. Das kannst du doch auch leicht noch mit in die Sub packen.
    • Wie mache ich das? Mit R18=memory_red(index_1): R19=memory_green(index_1)... die Register füllen und dann den Assemblercode?
      Oder anders?
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • BASCOM Source Code

      1. 'Interrupt service
      2. Display_refresh: 'show the memory on the display
      3. Load Timer0 , Timer0reload
      4. Incr Page_counter
      5. If Page_counter > 15 Then Page_counter = 0
      6. Index_1 = Page_counter * Bytes_in_row
      7. Index_2 = Index_1 + Channel_offset
      8. For D_2 = 1 To Dy_num_v
      9. For D_1 = 1 To Bytes_in_row
      10. R18 = Memory_red(index_1) : R19 = Memory_green(index_1) : R20 = Memory_blue(index_1) : R21 = Memory_red(index_2) : R22 = Memory_green(index_2) : R24 = Memory_blue(index_2)
      11. 'MSB first
      12. $asm
      13. LDI r17,8
      14. Loop_shift2out:
      15. in R16, dout_port_r1
      16. BST r18,7
      17. BLD r16,Dout_pin_r1
      18. Out Dout_port_r1 , R16
      19. in R16, dout_port_g1
      20. BST r19,7
      21. bld r16,Dout_pin_g1
      22. Out Dout_port_g1 , R16
      23. in R16, dout_port_b1
      24. BST r20,7
      25. bld r16,Dout_pin_b1
      26. Out Dout_port_b1 , R16
      27. in R16, dout_port_r2
      28. BST r21,7
      29. BLD r16,Dout_pin_r2
      30. Out Dout_port_r2 , R16
      31. in R16, dout_port_g2
      32. BST r22,7
      33. bld r16,Dout_pin_g2
      34. Out Dout_port_g2 , R16
      35. in R16, dout_port_b2
      36. BST r23,7
      37. bld r16,Dout_pin_b2
      38. Out Dout_port_b2 , R16
      39. SBI shift_clock_Port, shift_clock_Pin
      40. LSl r18
      41. LSl r19
      42. lsl r20
      43. lsl r21
      44. lsl r22
      45. lsl r23
      46. dec r17
      47. CBI shift_clock_Port, shift_clock_Pin
      48. BRnE loop_shift2out ; if = 0 exit
      49. $end Asm
      50. Incr Index_1 : Incr Index_2
      51. Next D_1
      52. Index_1 = Index_1 + Dy_offset : Index_2 = Index_2 + Dy_offset
      53. Next D_2
      54. Pwm2 = 0
      55. Waitus 20
      56. P_lat = 1 : P_lat = 0
      57. P_a = Page_counter.0
      58. P_b = Page_counter.1
      59. P_c = Page_counter.2
      60. P_d = Page_counter.3
      61. Pwm2 = Brightness 'display on
      62. Trigger = 1
      63. Return
      Display All
      wäre jetzt die isr. Die braucht jetzt 0,2466ms, die ursprüngliche mit dem call braucht 0,2501ms.
      Ist jetzt nicht ganz so der brüllende Durchbruch, aber immerhin!
      Die waitus 20 ist das Abwarten der pwm, damit die Leds auch wirklich ausgeschaltet sind, sonst gibt es Geisterpixel, wenn die page umgeschaltet wird. Das kann man sicher noch durch Umschalten des pins von pwm zu normalen IO ersetzen, ich hab' aber nicht das passende Register beim mega32 gefunden ||
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Na ja, du verwendest ja immer noch die Array-Elemente. Ob du die jetzt als Parameter übergibst oder in der Sub zuweist, kann ja nicht viel ändern.
      Die 8 (Bytes_in_row) Array-Elemente liegen ja direkt hintereinander im SRAM. Du holst dir also die Start_Adresse von Memory_red und den Offset auf das erste Element und inkrementierst dann nur noch diese Adresse. Genauso für die anderen Farben.
    • Ich kann jetzt die Register X, Y und Z verwenden, zB
      loadadr Memory_red(index_1) , X ; load address into R26 and R27
      ld R18, X ; load value of location R26/R27 into R18
      Das mache ich für Y und Z entsprechend und wenn die Daten draußen sind nur X, Y und Z per Befehl incrementieren, wieder die R18, R19..laden und raus damit?
      Aber ich habe doch 6 Adressen, jetzt fehlen mir schon die Register a_28_2c02f089
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Ich hab' grad' mal nachgeschaut. Das Laden der Register 18 bis 23 mit den Arraywerten dauert 72 cyclen, wenn das in der Schleife 8x passiert, dauert das dann 0,036ms. Selbst wenn man das drastisch reduziert, bin ich doch dann immernoch lahm, im Vergleich zu denen.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Wenn ich den Bildspeicher so organisiere, dass 6 bit eines bytes 2 pixel wären, dann könnte ich dieses byte einfach zu den 6 Eingängen des displays schicken und ich hätte für 1 pixel in der oberen Hälfte des displays die 3 RGB-bits geladen und gleichzeitig die 3 bits für das korrespondierende pixel in der unteren Hälfte des displays. Dann wäre aber das Beschreiben des Bildspeichers schon kompliziert. a_27_b277ca12
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • In meinen bescheidenen Projekten mache ich das so, dass ich immer die Basisadresse des Arrays in das Registerpaar schreibe und anschließend den Index dazu addiere.


      Source Code

      1. # zu Beginn
      2. CLR R17
      3. LDI XL, Low(Memory_green)
      4. LDI XH, High(Memory_green)
      5. LDS R16, {index_1}
      6. ADD XL, R16
      7. ADC XH, R17
      8. LDS R18, X
      9. ...
      Bei 6x6=36 Takten liege ich damit, das R17 löschen muss man nur 1x. Damit steht der Wert vom „grünen Array“ in R18. Den Rest genauso für die anderen Arrays wiederholen.

      Geht bestimmt noch eleganter...
      Aus datenschutzrechtlichen Gründen befindet sich die Kontaktdaten auf der Rückseite dieses Beitrages.
    • Es geht mir nicht darum, das von adafruit nachahmen zu wollen. Die Ansteuerungen von ähnlichen displays, die ich für einen Bekannten entwickle, verwenden kaskadierte Displays, da kommt es nicht auf die Vielzahl der Farben an. Mich wundert es nur, wie die mit 16Mhz die Daten so schnell raushauen. Eine 4bit pwm pro Farbe gibt doch 16 Abstufungen, dann 1/16 scan ergibt 256 Aufrufe der isr. Wenn man 30Komplettbilder/s haben möchte, dann muss man alle 33ms ein Komplettbild erstellen. Dabei brauchen die 40% der Zeit =14ms. Also pro isr dann 14ms/256=0,055ms. In der Zeit hab' ich grad' paar Register geladen.
      Also, mir bleibt nur das Staunen und minimale Verbesserungen.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • Die Frage ist, ob dort tatsächlich 40% Auslastung erreicht werden. Wenn der Aufruf der ISR mit NOSAVE erfolgt und das PUSH/POP manuell erfolgt: würde HIER diskutiert.
      Also könntest Du nur ein Flag „Display_Out“ = 1 setzen und und in der Main-LOOP mit verringertem Overhead (gespart werden 25x PUSH und 25x POP) die Ausgabe erledigen (eingebettet). Oder zumindest nur die Register sichern/rücksichern die Du veränderst.
      Den überwiegenden Teil der ISR in ASM zu schreiben holt nochmal Zeit raus. Und der Tipp von @Guenther würde auch Zeit sparen.
      Aus datenschutzrechtlichen Gründen befindet sich die Kontaktdaten auf der Rückseite dieses Beitrages.
    • Puh, 'einbinden in die main'! Wo ich da ständig in allen möglichen subs rum springe. Ob ich da eine halbwegs gleichmäßige pixel-Ausgabe hin kriege? Ich glaub' es nicht. In meinem Programm versuche ich ja schon einzelne Manipulationen des Bildspeichers mit der isr zu synchronisieren, um ein Ruckeln (optisch) zu vermeiden, das klappt ja auch nicht überall. Ich versuche mal, die in der isr verwendeten Register zu erkennen und nur die zu sichern. Das kapiere ich noch. Das mit der Verwendung von Adressen, hab' ich noch nicht gerallert, stell ich mal zurück. Dann werde ich mich mehr der main zuwenden, da gibt's noch genug zu tun.
      Danke erstmal an alle, für das Mitdenken hier.
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------
    • ...das ist ein Argument. Dann macht es Sinn die gesamte ISR in ASM zu schreiben. Kann man in den Datenstrukturen noch etwas straffen/optimieren? Die Array-Grenzen könnten auch wie vorgeschlagen in den 256er Schritten angelegt werden.

      Aber mit dem M32 reicht der RAM ja eigentlich nicht für 4K-Farben.
      Aus datenschutzrechtlichen Gründen befindet sich die Kontaktdaten auf der Rückseite dieses Beitrages.
    • tschoeatsch wrote:

      Ich versuche mal, die in der isr verwendeten Register zu erkennen
      Wenn ich im Simulator bin, mir die Register anzeigen lasse, deren Farben zurücksetze und dann nur die isr durchlaufen lasse, sind dann die farbig markierten Register die, die ich sichern muss, weil sie ja offensichtlich verwendet wurden?
      Raum für Notizen

      -----------------------------------------------------------------------------------------------------

      -----------------------------------------------------------------------------------------------------