Mehrere Muster speichern und abrufen

  • Hi Leute,


    ich les schon ne ganze Weile bei euch. Ich habe ne LED Matrix (10 * 12) wobei die 12 Spalten über ein Schieberegister angesprochen werden.
    Heißt jetzt im Klartext, ich Multiplexe über die Spalten. Das klappt auch soweit alles. (Atmega8)
    Jetzt meine Frage: Gibt es eine elegantere Möglichkeit Muster zu speichern als von mir jetzt genutzt? (Gibt es sicher, aber WIE?^^)
    Da ich Momentan noch keinen Schalter dran habe schaltet das Muster nach 10 durchläufen selbst um. Ich habe mal die SPalten 3 - 8 weg gelassen. da steht das Selbe wie in den anderen spalten bzw halt die restlichen muster drin.


    Bei mir laufen beide Muster von rechts nachlinks und zurück.


    Also wie kann ich das dauernde IF umgehen und irgendwie die Bilder ablegen und dann passend zur Spalte wieder reinrufen?


    Bin über jeden Ratschlag dankbar :)


    Grüße C-F600


  • Hallo C-F600,


    statt:

    Code
    Zeile 1 = 1
    Zeile 2 = 1
    Zeile 3 = 1
    Zeile 4 = 1
    Zeile 5 = 1
    Zeile 6 = 1
    Zeile 7 = 1
    Zeile 8 = 1


    Was gerade mal ein einziges Muster ausgibt,
    Kannst Du auch sowas machen:



    Hier wird in jeder Codezeile ein 8-Bit Muster der Variable "Portbits" zugewiesen.
    Wie man sieht, handelt es sich in diesem Fall um ein einfaches Lauflicht, wo von rechts nach links, dann von links nachrechts je eine LED an ist.


    Noch in der selben Zeile, rechts vom Doppelpunkt, steht der Aufruf einer Ausgaberoutine (mit dem Namen "Anzeige"), wo im Wesentlichen sowas in der Art 'passiert:


    Code
    Anzeige:
      Portb=Portbits
    Return


    Bei mir passiert in der Prozedur "Anzeige" noch mehr, weswegen der Aufruf einer Ausgaberoutine Sinn macht.



    Der Codeschnipsel gibt also 8 Bit aus, weil die Variable "Portbits" vom Typ Byte ist.
    Das geht natürlich auch in 16 Bit, wenn man Typ Integer nimmt u.s.w.


    Für eine Matrix muss ja auch noch die Spalte geschaltet werden. Das könnte (hier im Beispiel nochmal mit 8-Bit Mustern) so aussehen:



    So kann man frisch und fröhlich eine Matrix editieren und hat alle Bits schön sauber untereinander stehen.


    Natürlich geht das auch gaaaanz anders :)
    Schau Dir doch mal an wie das hier gemacht wird:
    Fehlersuche LED Cube


    Da wird also eine externe Datei "muster.txt" hinzugeladen, in der die Muster definiert sind.
    Vorteil: Man muss nicht mit 'nem Programmiergerät rumfummeln, wenn sich die Muster ändern.
    Die Datei könnte dazu beispielsweise auf einer SD-Karte abgelegt sein, die der AVR ausliest.
    Man füllt also am PC die SD-Card mit der Datei die die Muster enthält und schiebt die am Ende in einen Slot, den der AVR ausliest.


    Gibt noch etliche weitere Möglichkeiten, aber das sind schon sehr praxistaugliche Methoden, mit denen man schnell zum Ziel kommt.
    Kleiner Tipp noch, falls Dir Speicherkarte und externe Datei zu heavy sein sollte: Wenn Du nach diesem Prinzip vorgehst:


    Portbits = &B00000001 : incr Spalte : Gosub Anzeige
    Portbits = &B00000010 : incr Spalte : Gosub Anzeige
    Portbits = &B00000100 : incr Spalte : Gosub Anzeige


    ... dann kopiere am Anfang einfach einen langen Block zusammen, wo alle Bits Null sind.
    Im Editor, per Überschreibmodus (statt Einfügemodus) kann man das dann echt schnell und schmerzlos editieren.

  • Hi, erstmal vielen Dank für deine Antwort.


    Aber entweder ich habe mich oben blöd ausgedrückt oder ich verstehs nicht ganz.


    Das ganze Zeile 1 = 1 pro Spalte sind natürlich alle Zeilen untereinander, die dann für die Spalte 1 ein Muster geben. Danach wird die SPalte gewechselt auf Spalte 2 und dort das Muster ergänzt und so für jede Spalte. Wenn Ich das 12 mal mache habe ich über die ganze Breite mein gewolltes Muster.
    Nun möchte ich nach Knopfdruck oder Zeit (beides bekomm ich hin) Aber ein anderes Muster generieren. Also muss ich alle Zeilen Pro Spalte neu belegen.



    Hier nochmal zur Verdeutlichung. Alles ab THEN bis Else ist das was angehen muss um in dieser Spalte das Muster zu schaffen. Alles ab Else bis End If, ist ein zweites Muster.


    ?^^.


    Als Ergänzung, bei mir liegen leider nicht alle Zeilen auf einem Port (sieht man ja im Config bereich. Sind teilweise von portB0-4 von c 0-2 etc.

  • Noch ein Schnipsel, wo zusammengehörige Muster als "Effekt" in je einer Prozedur gespeichert werden:



    Dieses Beispiel enthält keine Routine für die Spaltensteuerung, es wird also einfach je Zeile ein 8-Bit Muster ausgegeben.
    Aber wie die Spaltensteuerung aussehen kann habe ich ja schon beschrieben, bzw. müsste Dir klar sein.


    Mit der Variable "Zeitquant" kannst Du in der Prozedur "Anzeige" eine Routine realisieren, die das jeweilige Bitmuster entsprechend lange an den Ausgängen verweilen lässt.
    Also ein eleganteres Eqivalent zu "Waitms 60", das mit Timer arbeitet z.B.

  • Und nochmals danke!!


    Die Spaltensteuerung habe ich mitlerweile im Griff (sitze erst seit Donnerstag überhaupt an diesem Thema, darum auch Sorry für die Anfänger Fragen!)


    Diese Zeile:

    Code
    Portbits = &B00001111 : Gosub Anzeige


    Heißt am Port B geht bit 0 1 2 3 an, richtig?
    Kann ich das mit nem anderen Port verknüpfen. Als Beispie:


    Code
    Portbits = &B0000111 ; &C0000111 : Gosub Anzeige?

    (bzw gibts ein Verknüpfungszeichen extra dafür?)
    Bzw wie muss ich dann folgendes:

    Code
    Anzeige:
      Portb=Portbits
    Return


    ändern, dass es mitverschiedenen Ports klappt? Habe für Zeile 1-10 Port B C und D.


    Wird sicher ändern wenn ich meine Matrix fertig habe.

  • Heißt am Port B geht bit 0 1 2 3 an, richtig?

    Richtig.



    Kann ich das mit nem anderen Port verknüpfen. Als Beispie:

    Ja. Das macht dann die Ausgaberoutine "Anzeige".
    Da kannst Du beliebige Posts ansteuern.


    "Portbits" ist bei mir nur eine Variable, die halt so heißt. Die wird mit dem Bitmuster belegt und dann zur Prozedur "Anzeige" gesprungen.
    Du kannst also für "Portbits" auch eine Variable vom Tyt Integer nehmen und dann 16 Bit pro Zeile belegen:


    Portbits = &B0000111100001111


    (Das "&B" steht für "binär", also dafür, dass die Daten als Einsen und Nullen vorliegen. Geht auch u.a. hexadezimal: Portbits = &hFE)

    ändern, dass es mitverschiedenen Ports klappt? Habe für Zeile 1-10 Port B C und D.

    Ha klaro.
    Das erfolgt dann in der Prozedur "Anzeige".


    Code
    Anzeige:
      Portb.1=Portbits.3
      Portb.2=Portbits.7
      Portb.3=Portbits.2
      Portc.1=Portbits.5
      Portc.2=Portbits.1
      Portd.7=Portbits.6
      '... u.s.w.
    Return
  • Ich danke dir und werde es heute abend ausprobieren. Bevor ich jetzt noch weitere Fragen stelle die sich beim Probieren selbst erledigen. Mehr als nicht leuchten bzw falsch leuchten kann ja nicht passieren :) .


    Falls danach noch was unklar sein sollte frage ich einfach nochmal ;)


    EDIT: DING DING DING das Ganze nochmal gelesen und soeben kam der Verstand^^. Cool wenn das jetzt auch noch so klappt bin ich glücklich ;) Gibt nen positiven Karma-Punkt für dich :P

  • Sorry übrigens, dieser F*cking Editor hier ist total buggy und verwurschtelt mir beim Absenden dauernd meine Postings.
    Bin nach jedem Absenden immer gleich noch zwei, drei Mal am Nacharbeiten.
    Lese also gegebenenfalls die vorherigen Postings nochmal.

  • Hi,
    habs jetzt so geändert wie von dir vorgeschlagen. AN sich gehts auch, ABER bei mir leuchten alle LEds genau eine Spalte nach.
    Heißt in Spalte 1 ist die erste LED mit voller Helligkeit da. In der Spalte 2 ist die erste LED leicht gedimmt an. Und so geht das über alle Spalten.
    Warum?




    Ps.: D = 0 ist die Spalte anschalten (nur beim ersten Mal, danach 11 schritte nur ein D=1 nachschieben, wodurch die aktive SPalte eins nach rechts rutscht.

  • Wozu hast Du denn diesen Block drin?

    Raus damit, das incrementieren erfolgt doch schon in den Zeilen davor.


    Weiterhin: Du hast gar keine Verzögerungszeit drin.
    Bau in die Anzeigeroutine noch sowas wie "waitms 40" oder so.

  • Hi,


    hab das oben nur drin, weil ich gerade noc hwas anderes probiert habe.


    Das Nachleuchten habe ich mit waitms an allen Positionen probiert. Konnte es aber leider jetzt nur mit folgendem lösen:


    Habe alles durchprobiert. Keine Chance. Auch oben im Aufruf direkt mit wait etc.

  • Noch was:



    Eleganter:


    Code
    D=1                                    'D ist immer high, außer ...
    if Spalte>Spalte_max then
      Spalte = 0 :  D=0              '... bei Spalte = 0
    End If
    
    
    Clk = 1 : Clk = 0              'Clock pulsen
    Strobe = 1 : Strobe = 0   'Strobe pulsen
  • Habe jetzt meine Ansteuerung im Griff nur noch eine Frage:
    Ich habe im Muster mehr Spalten als in der Matrix vorhanden. Um nun das Muster in Gänze zu zeigen, läuft es nach links. Dafür lasse ich momentan, recht umständlich, nach jedem Durchlauf einen Zähler um eins erhöhen und Zeile nach Zeile aussetzen sozusagen..


    Code
    If Z < 1 Then
    Portbits = &B00000001 : Gosub Anzeige                   	' : Gosub Eins_weiter
    End If
    If Z < 2 Then
    Portbits = &B00000011 : Gosub Anzeige                   	' : Gosub Eins_weiter
    End If
    If Z < 3 Then
    Portbits = &B00000111 : Gosub Anzeige                   	' : Gosub Eins_weiter
    End If


    Jetzt möchte ich das aber nicht für 100 SPalten so machen. Wie gehts denn einfacher? Problem ist bei meiner Ansteuerung, das alle anderen Spalten natürlich weiter mit dem Zeilenmuster gemultiplext werden müssen. darum auch das Z < 1'2'3.


    Gruß

  • Bin gerade in Zeitdruck, kann mich jetzt nicht voll eindenken.
    Aber beschäftige Dich mal mit den Befehlen Select und Case.


    Select Case Z


    Case < 1


    Case < 2


    Case < 3


    Case < 4


    Case 28


    Case > 200


    Case 220 to 230


    Case Else


    End Select



    Kannst Deine Befehle dann zwischen die Zeilen setzen, oder, per Doppelpunkt vom "Case" getrennt, dahinter, was vorteilhafter ist.


    Hier noch ein realer Programmschnipsel von mir:


  • Das macht man normal halt auch ganz anders:


    Du legst Dir nen "Bildschirmspeicher" an, also ein Array, aus dem die Routine, die das Multiplexen macht, die Daten raus liest...


    *diese* gehört dann in nen Timer-Interrupt, läuft also "selbständig im Hintergrund" und zeigt immer das an, was im Bildschirmspeicher drin steht.


    in der Hauptschleife kannst Du dann Deine Muster erzeugen bzw. aus nem "Data"-Bereich lesen, und in den Bildschirmspeicher schreiben, fertig.


    also nix mit if und Case und Portbits = usw. ;)


    siehe als Vorlage mal das hier - bei jedem Timer-Überlauf wird die Routine "Multiplex" aufgerufen, die die Zeilen durchschaltet, und die Werte aus dem Array "Buffer()" ausgibt.


    Bei Dir mit 10x12 und dem SR ist's dann halt ein bisschen anders, zum Multiplexen musst Du ein Bit durch das SR rotieren, und für die Spalten reicht ein Byte ja nicht, also entweder den Buffer dann in 16 Bit und das bei der Ausgabe wieder auseinander dröseln, oder z.B. 2 Arrays, eines für jeden Port...


    in der Hauptschleife musst Du dann nur Bitmuster in dieses Array schreiben, die Multiplex-Routine zeigt die dann an... wo die herkommen ist egal, ob Du die mit nem Algorithmus erzeugst, oder vorgefertigte aus nem Speicherbereich liest...


    mal als Beispiel, weil's so einfacher zu sehen ist, Du hast ne Matrix mit 8x8 - dann würde ein Schriftzug im Flash z.B. so aussehen:



    erkannt..? - Kopf nach rechts drehen, die 1er ergeben das Wort "LED" ;)


    das überträgst Du nun in Deinen Ausgabepuffer:


    Code
    for i = 0 to 7
       Buffer(i) = Lookup (i, Laufschrift)
    next i


    das überträgt jetzt nur die ersten 8 Byte, also einen Ausschnitt aus Deinem Bild - damit das nun durchscrollt, musst Du nur mit einer zweiten Schleife den Ausschnitt verschieben:


    Code
    for pos = 0 to 2
       for i = 0 to 7
          Buffer(i) = Lookup (i+pos, Laufschrift)
       next i
     
      waitms 200
    
    
    next pos


    so wird also alle 200 ms der Ausschnitt eins weiter geschoben - das musst Du natürlich an die tatsächliche Länge des Musters anpassen, diese kannst Du auch in den Data-Zeilen mit speichern und da raus lesen...


    also, das wichtigste ist erst mal, dass Du das trennst, Timer-ISR zeigt einfach das Bild aus dem Speicher an, in der Hauptschleife werden die Bilder "erzeugt" - das so vermengen, dass Du praktisch gleichzeitig Bilder erzuegst und Multiplexing machst, ist Quatsch, total unübersichtlich...


    EDIT: Du kannst jetzt hier bei Dir für den Buffer auch 16 Bit nehmen, damit man in den Data-Zeilen das Muster schön sieht:


    und dann in der Multiplex-Routine das auf Deine Leitungen aufdröseln:


    It's only light - but we like it!


    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

    Einmal editiert, zuletzt von Pesi ()

  • Habe gerade noch was Spannendes herausgefunden:


    Jede Zeile in diesem Stil:

    Code
    Portbits = &B01100000 : Gosub Anzeige


    belegt in der *.bin-Datei genau 10 Bytes!
    Skandal!!! :cursing: ;)


    Dass in solchen Zeilen jedesmal eine Zuweisung von Daten zu einer Variablen erfolgt, gefolgt von einem Prozeduraufruf, kann bei großen Datenmengen offensichtlich nicht der effizienteste Weg sein.
    Aber das es gleich 10 Bytes pro Zeile sind, überrascht mich doch (hey, ich bin halt Bascom-geschädigt; die Assembler-Freaks werden natürlich nur müde grinsen, dass einer nicht weiß, wieviel Datenmüll er generiert).


    Zumal das Auslagern der Ausgabedaten in eine externe Datei ohnehin vorteilhaft ist, wenn man das soweit trennt, dass diese Datei auch von einem externen Medium (CF-Karte) gelesen werden kann. Denn in dem Fall wäre kein erneutes Flashen des Controllers nötig, wenn sich an den Ausgabedaten etwas ändert. Man editiert einfach die Datei auf der CF-Karte.


    Also fast egal wie uneffizient man das Datenformat der externen Datei wählt - 10 Bytes pro Bitmuster lässt sich mühelos unterbieten!
    Doch jetzt wird es interessant - ein Block in diesem Stil:


    Code
    Portbits = &B10000001 : Gosub Anzeige
    Portbits = &B11000011 : Gosub Anzeige
    Portbits = &B11100111 : Gosub Anzeige
    Portbits = &B00111100 : Gosub Anzeige
    Portbits = &B00011000 : Gosub Anzeige
    Portbits = &B00111100 : Gosub Anzeige
    Portbits = &B11100111 : Gosub Anzeige
    Portbits = &B11000011 : Gosub Anzeige
    Portbits = &B10000001 : Gosub Anzeige


    ... lässt sich natürlich problemlos in Data-Zeilen oder sonstwie auslagern.
    Da könnte man zu jedem Bitmuster gleich noch ein zweites Byte mitspeichern, für den Zeitwert, der festlegt, wie lange das jeweilige Bitmuster an den Ausgängen erscheinen soll.
    Dann hätten wir also nur 2 Bytes pro Bitmuster, statt 10.


    Doch bei einem solchen Block:



    ... wird es kniffeliger. Hier ist Redundanz im Spiel.
    Hier wird ein identischer Block von Bitmustern also 20 Mal hintereinander ausgegeben, wobei sich bei jedem Durchlauf die Verweildauer (Variable: "Effektzeitwert") ändert.
    Habe das Beispiel absichtlich etwas simplifiziert.


    Frage ist: Wie lagert man solche Sachen effizient aus, also ohne Redundanz.
    Ich will den Block aus je 9 Bitmustern ja nicht 20 Mal auf der CF-Karte speichern, mit jeweils einem weiteren Byte für die Verweildauer!
    Das wären ja 9 x 20 x 2 = 360 Bytes!
    Oberiger Codeblock belegt im Flash nämlich "nur" 164 Bytes, trotz der verschwenderischen 10 Bytes pro Bitmuster.


    Die große Denksportaufgabe:
    Wie speichert man solche (und noch komplexere) Strukturen effizient auf einem externen Medium, so dass Programmcode und Ausgabedaten vollständig voneinander getrennt sind?
    So dass also kein Neuflashen des Controllers notwendig ist, wenn man an den Ausgabedaten herumeditieren möchte.


    Das "Herumediteiren" sollte tatsächlich in menschenlesbarer Form, also per Editor möglich sein.


    Mir fällt da nur eine Art "Interpreter" ein, der im Flash abgelegt wird und die externe Datei einliest, welche neben den reinen Bitmustern auch noch weitere Anweisungen enthält.
    - Anweisungen, wie oft ein Codeblock wiederholt werden soll.
    - Marker, um Anfang und Ende eines Codeblockes zu markieren und zu benennen.
    - Eine mathematische Funktion, wie sich die Verweildauer der Daten während des Ablaufs eines Codeblockes verändern soll.
    etc.


    Noch gar nicht erwähnt: Die Helligkeit der einzelnen Pixel. Aber klammern wir das erstmal aus.
    Jedenfalls hat man dann am Ende quasi eine eigene Programmierprache erschaffen, die vom Interpreter im Flash "interpretiert" und abgearbeitet wird.
    Das ist ja nun auch ziemlich hardcore.


    Ich kann nicht der erste sein, der vor dieser Problematik steht.
    Nochmal kurz die Anforderung: Vollständige Trennung von Code im Controller und Ausgabedaten (nebst Zeitwerten), so dass man die Ausgabedatei entweder von CF-Card lesen kann (dann spielt Speicher keine Rolle) oder (bei kleineren Projekten) per Include hinzulädt.
    Wie machen die Profis das?


    10 Byte pro Bitmuster gilt es zu unterbieten, aber ohne Aufgabe der Fähigkeit, Codeblöcke in Schleifen zu wiederholen, nebst individueller Modifikation des jeweiligen Zeitwertes.


    Bin gespannt auf Eure klugen Ideen!

  • Ich fall vom Hocker, wollte spaßeshalber mal getestet haben, wieviel Speicher im Flash diese Variante hier belegt:


    Code
    Call Anzeige2(&B00001111)


    Ergebnis: 28 Bytes!!!
    Das ist ja wohl fatal!


    Nun sind Sub und Function mit Parameterübergabe sowieso als uneffizient verschrien, weswegen ich die nie verwende. Aber ich dachte immer dass mit "uneffizient" eher der RAM-Verbrauch gemeint ist. Dass sowas auch Flash verheizt wie Zeitungspapier im Kamin, hätte ich nicht gedacht.
    Das ist ja noch viel dramatischer, als meine bisherige Variante, mit ihren 10 Bytes:


    Code
    Portbits = &B00001111 : Gosub Anzeige



    'Ne Data-Zeile nach diesem Stil hingegen, belegt erwartungsgemäß nur genau ein Byte im Flash:


    Code
    Data &B00001111



    Nun hat man bei LED-Effekten zwar immer etwas Redundanz im Spiel, aber die satten 20 Wiederholungen eines Blocks, wie im Vorposting, sind natürlich ausgesprochen selten.
    Für eine effiziente Speicherung per Data-Zeilen könnte ich mir folgenden Aufbau vorstellen:


    Erstes Byte jeder Zeile ist der Zeitwert, zweites Byte ist das Bitmuster (mehr Bytes gehen natürlich auch, aber immer erst Zeitwert, dann Bitmuster).
    Also so:


    Code
    Data &H20 , &B00000001	'Bitmuster 1
    Data &H20 , &B00000010	'Bitmuster 2
    Data &H20 , &B00000100	'Bitmuster 3
    Data &H20 , &B00001000	'Bitmuster 4
    Data &H20 , &B00010000	'Bitmuster 5
    Data &H20 , &B00100000	'Bitmuster 6
    Data &H20 , &B01000000	'Bitmuster 7
    Data &H20 , &B10000000	'Bitmuster 8
    ' ...


    Sonderfall: Ist das erste Byte &H00, was für einen Zeitwert natürlich unsinnig ist, dann "weiß" der Interpreter, dass das folgende Byte kein Bitmuster repräsentiert, sondern eine Anweisung.
    So kann man auch per Data-Zeilen Repeat-Kommandos realisieren.


    Denkbares Beispiel:



    Damit hätte man eine sehr effiziente Speicherung von Bitmustern mit jeweils individuellem Zeitwert, ohne Redundanz.
    Für solche Schleifen, wo bei jedem Durchlauf die Zeitwerte modifiziert werden, müsste man das Ganze noch etwas ausbauen, aber das hier sei erstmal als laut gedachter Ansatz in den Raum geworfen.

  • Hi ihr,


    vlt stell ich mich mal wieder selten dämlich an, aber ich komme null weiter...


    Wo sind meine Fehler? Ich habe nun gar keine Ausgabe mehr


    Also es ist so gedacht, dass der mit dem ersten D=0 die erste Spalte anschaltet. Was dann mit jedem Gosub Anzeige eins nach rechts wandert.
    Jetzt soll er mit dem Buffer aus der 1. Data Zeile sein Muster nehmen, das dann anzeigen, zweite Data Zeile und so weiter. Die Variante mit dem nach rechts verschieben habe ich erstmal ausgeklammert.
    Nur mein "Display" zeigt gar nichts an :(