C LED Matrix 8 x 8

Hallo Janiiix,


da müsste ich jetzt schauen. In .h definiere ich in der Regel den Prototyp einer Routine oder Funktion.

Die Routine, welche Daten von SRAM zum Display überträgt ist ...
void Display_Update(void);

Diese führst du einfach nicht aus.

Dirk :ciao:

"Diese führst du einfach nicht aus." ? Du meintest bestimmt diese soll ich einfach ausführen ?

Mit dieser Funktion ( Die ich noch ein wenig umschreiben muss, kann ich dann meine Laufschrift dastellen ???

Code:
void Display_Text(int16_t x, int16_t y, char *string)
{
    
    uint8_t width, height;
    
    switch (FontIndex)
    {
        
        case FONT_1:    width = 7;
                        height = 8;
                        break;
        case FONT_2:    width = 10;
                        height = 16;
                        break;
        default:        return;
    }
    
    while (string[0] != 0)
    {
    
        _Display_Char(x, y, string[0], width, height);
        x += width;
        string++;    
        
    }
    
}
 
"Diese führst du einfach nicht aus." ? Du meintest bestimmt diese soll ich einfach ausführen ?

Mit dieser Funktion ( Die ich noch ein wenig umschreiben muss, kann ich dann meine Laufschrift dastellen ???

Code:
void Display_Text(int16_t x, int16_t y, char *string)
...

Nein. Alle Grafikroutinen schreiben ja in den Grafikspeicher, also in das Array im SRAM des Mikrocontrollers.

Diese Routinen und der Grafikspeicher sind erst einmal unabhängig vom "Raster-Anzeigegerät", welches du an den Mikrocontroller anschließt. Das Anzeigegerät kann das Grafik LCD sein, eine LED Matrixanzeige, ein Matrixdrucker oder sonstwas.

Du musst den Inhalt des Grafikspeichers entsprechend zu deinem Anzeigegerät übertragen. In meinem Fall mit dem LCD übertrage ich den Inhalt via SPI. In deinem Fall musst du den Inhalt via multiplexen über PortIOs "übertragen". Bei mir ist für die Übertragung diese "Update" Routine zuständig. Bei dir solltest du das über TimerISR machen.

Dirk :ciao:
 
Womit wir wieder zurück beim Multiplexing wären...
Mal kurz das wesentliche für Dich (Dirk) bisher:
Im Eröffnungspost hatte Janiiix ein Programm präsentiert, bei dem das Multiplexing in der Hauptschleife (endlos) durchlief, also je alle Spalten in einem Schleifendurchlauf und die Verzögerungen mittels _delay_ms. Das anzuzeigende Zeichen (bis dahin nur eins) wurde als const int name [größe], also als Array (vermutlich im Flash??) abgelegt.
(Warum eigentlich int? sind Integer nicht vorzeichenbehaftet? Solange man damit nicht rechnet ist das Wurst, aber sollten es nicht eigentlich Byte sein?)
Sein Hauptprogramm konnte lediglich durch einen Timer-Interrupt unterbrochen werden, in dem regelmäßig des (eine) Zeichen in den VRAM kopiert wurde.

Ich habe dann im wesentlichen versucht, 3 Dinge vorzuschlagen:
1. Das Multiplexing in Interrupts zu verlagern (letzendlich über einen 8bit Timer mit Prescaler 64 als Vorschlag, welcher dann eine Bildwiederholfrequenz von ca 60Hz (bei 8MHz Systemtakt) als Ergebnis hätte, dabei eine Compare-IRQ zum löschen aller Spalten und setzen der nächsten Zeilen, und den Überlauf-IRQ zum setzen der neuen Spalte.
2. Den Zeichenvorrat im Flash(?) in die Reihenfolge der ASCII zu bringen und als ein Array zu betrachten (wozu es in einer Hochsprache lediglich als ein großes Array definiert werden muß, oder?). Dadurch könnte man dann nämlich die anzuzeigenden Texte als string verwalten, was ja letztlich nur ein Array of ASCII-Nummern ist.
3. Sollten ursprünglich ja nur Zeichen bzw Texte, ggf als Lauftext, dargestellt werden. Mein dritter Vorschlag war, den VRAM (als Pixelspeicher) wegzulassen, und das Multiplexing selbst die nötigen Informationen aus dem Zeichenvorrat laden zu lassen. Der anzuzeigende String (array of ASCII) wäre dann sozusagen der VRAM, er und Variable, die die erste aktuell anzuzeigende Spalte des Stringes kennzeichnet (Verschiebung für den Lauftext) müßten in den Interrupts "sichtbar" sein.
Das Multiplexing muß dann nur etwas mit den Adressen (Verschiebung, ASCII-Nummer, aktuelle Spalte der Matrix) Jonglieren, und die sich ergebende Adresse als index des Zeichenvorrates verwenden, und das von dort geladene int (? - byte) auf den PORT (Zeile) legen. Da Janiiix Zeichen 8x8Pixel sind, ist die Rechnerei für den Controller einfach, da lediglich Verchiebungen, Maskierungen und etwas Bitschubserei nötig ist, der String dann quasi die Ladeadressen der Pixelinfos aus dem Zeichenvorrat darstellt.
Außerhalb der beiden ISR könnten dann die Verschiebung und der String manipuliert werden, eben um das laufen des Textes zu erzeugen bzw 'n neuen Text auszugeben.
So,
Du (Dirk) bietest jetzt 'ne mächtige, fertige Bibliothek(?) an, die 2. und teilweise 3. erschlägt. Es wird ein VRAM verwendet, es gibt Routinen die irgendwelche Elemente in dieses Zeichnen (insbesondere auch Texte). Es gibt insbesondere fertige Zeichensätze, sogar mehrere unterschiedlich große. Ich vermute, daß das auch zu Lasten des Speichers geht, aber wenn der da ist...
(Außerdem gibt es die hier nicht benötigte Routine zur seriellen Ausgabe des VRAM zur Darstellung auf einem Display (welches dieses Bild dann permanent selbständig darstellt, permanent bis zur Übertragung des nächsten Bildes).

Aber das wesentliche zu lösende Problem (also für Janiiix) bleibt 1.
 
Hi LotadaC.

Ja, das Janiiix hier mit _delay_ms() in der Hauptschleife arbeitet und dort auch das Multiplexen erledigt ist mir auch aufgefallen. Da bin ich gar nicht weiter drauf eingegangen, da du ja bereits etwas von Timerinterrupt geschrieben hattest. Wie schon geschrieben, alles durchgelesen hatte ich mir nicht.

Auch ist es so, dass im Moment die Grafikdaten im SRAM gehalten werden, da bin ich auch nicht drauf eingegangen ... zum Testen ist es bei wenig Daten erst mal egal, auch ob man hier int, uint8, char verwendet :) Es sind einfach grundsätzlich zu viele "Probleme", die eigentlich erst mal beseitigt werden müssen. Das Multiplexen selber ist das kleinere "Problem", kann man in einer TimerISR - je nachdem wie die Grafikdaten aufgebaut sind - mit ein paar Zeilen Code erledigen.

Schwieriger sind eben die Sachen "wo kommen die Daten her", wie bringe ich die auf einen Grafik-Rasterbereich. Da ist mir dann mein LCD Projekt eingefallen, wenn man dieses ein bisschen anpasst (und zwar nur bei der Ausgabe der Daten, also hier das Multiplexen einbaut), hat man alles fertig. Man kann Strings x-y-locaten, diese können sich im SRAM oder im Flash Memory befinden. Aus dem Flash kommen auch direkt die Fontdaten. Man kann Punkte, Linien, Vierecke und Kreise zeichnen, die Ausgabe kann man verodern, verexoren usw. Das was ich hier für das LCD nicht gemacht habe ist eine Laufschrift, das dürfte aber dann auch nicht mehr so schwer sein, in x Richtung zu locaten und den String entsprechend neu ausgeben. Das ganze Projekt ist halt für eine 8x8 Matrix ein bisschen "überdimensioniert", zumal diese Matrix gerade mal ein Zeichen des kleineren Fonts darstellen kann. :)

Klar bleibt Problem 1 immer noch. Ich denke aber 1 ist leichter zu lösen als alles andere, im Prinzip funktioniert 1 ja anscheinend jetzt schon, wenn auch im Hauptprogramm mit _delay_ms() ;-) aber alles andere noch nicht. Ich denke in dieses Thema müsste man noch ganz schön viel Zeit reinstecken, damit der String als Laufschrift dargestellt werden kann und da ist 1 eine kleinere Aufgabe.

Dirk :ciao:
 
Ich sage ja, Deine Bibliothek löst bereits einen Großteil der restlichen Probleme, übrig bleibe eigentlich nur noch das Multiplexing (da eben Deine Bibliothek ursprünglich für eher statische Anzeigen mit seriellem Transfer gedacht ist/war, sich also nicht mit dem ständigen Anzeigen desselben Bildes beschäftigen mußte), und das Laufen.
Beides wäre für UNS kein Problem, aber ich bin mir nicht sicher, ob Janiiix das Multiplexing mit den Interrupts überhaupt schon (selbst) umsetzen konnte...

Edit: ich meine eigentlich, auch für "meinen" Vorschlag den wesentlichen Rechenweg bereits beschrieben zu haben. Die Verschiebevariable liefert (durch 8 geteilt da Pixelweise) den anzuzeigenden Char im String. Da ein String ein Array of ASCII-Nummern ist, kann man die Verschiebevariable also als Index nutzen, um an den anzuzeigenden Char zu gelangen. Dieser ist jetzt als Byte quasi die Adresse auf den Zeichenvorrat, nur eben mit der Anfangsadresse im Speicher zu korrigieren (und den ersten 32 weggelassenen ASCII und mit 8 multipliziert da 8Bytes pro Char). Die unteren 3 Bits der Verschiebevariable liefern die Spalte des anzuzeigenden Zeichens, werden also auf die Adresse draufaddiert. Von dieser Adresse ist dann das Byte aus dem Zeichenvorrat zu laden.
Ich berechne mir aus der aktuell zu zeichnenden Matrix-Spaltennummer, und der Verschiebevariable die Adresse des anzuzeigenden Zeichens im String, und aus Dessen Inhalt und dem Rest der Verschiebevariable die Adresse des anzuzeigenden Bytes aus dem Zeichenspeicher.
Der wesentliche Aufwand würde im erzeugen/eintippen des Zeichenvorrats (8x8) stecken, und das scheint bei Deinem Weg ja bereits fertig zu sein...
 
Vielen dank an euch beide... Das ihr euch so eine große Mühe gibt.
Soll ich die Multiplexroutine nun lieber in eine ISR machen oder in 2 ?
 
Das kommt darauf an, was Dir lieber IST...

natürlich kannst Du das auch alles in eine ISR (den Timerüberlauf) setzen. Das wären dann die beiden Extremfälle OCR0 sehr dicht an 0 bzw OCR sehr dicht an TOV (je nachdem wierum das in der TOV-ISR steht, wobei erst LEDs anschalten, und direkt danach alles wieder ausschalten natürlich keinen Sinn macht ;))
...Nimm einfach mal an, Du hättest OCR0 auf 127 (also die Hälfte) gesetzt. Meine Frage war: Was geschieht, wenn OCR0 jetzt einen größeren oder einen kleineren Wert erhält?
 
Die Frequenz der Überläufe ändert sich nicht, also auch nicht die Frequenz in der die Spalten gewechselt werden, und damit eben auch nicht die Bildwiederholfrequenz.
Ja, natürlich mußt Du OCR0 einen anderen Wert als 0 geben, sonst liegen die beiden events ja nahezu an derselben Stelle. Nimm einfach mal an, Du hättest OCR0 auf 127 (also die Hälfte) gesetzt. Meine Frage war: Was geschieht, wenn OCR0 jetzt einen größeren oder einen kleineren Wert erhält?

Ja... Dann würde das Multiplexing doch nicht wirklich sauber laufen & wenn sie beide gleich sind, würden sie sich gegenseitig "bekriegen" ?!
 
Bleiben wir mal beim Timer0, im Datenblatt findest Du auf Seite 80, Table 38 'ne Zusammenfassung über die 4 möglichen Timermodi:
Mode0: normal mode - der Timer läuft von 0x00 bis 0xFF, und dann auf 0x00 über (single slope), also 256 Schritte pro Überlauf. Ist TCNT0=OCR0, wird einen Timertakt später der OCR-Interrupt wirksam. Außerdem Wird beim Überlauf der TOV-IRQ wirksam.
Mode1: phasenkorrekter PWM - hier läuft der Timer zwischen 0x00 und 0xFF hin und her (dual slope, rauf und runter), also insgesamt 512 Schritte. Der OCR-IRQ erfolgt dann also zweimal beim "Durchlauf" - einmal beim rauf- und einmal beim runterzählen. Der TOV-IRQ nur, wenn einmal, nämlich wenn TCNT0=0.
Mode2: CTC - der Timer läuft single slope von 0xFF bis OCR0, und danach über (genauer: er läuft nicht über, sondern wird gelöscht. Es erfolgt in dem Moment also kein TOV-IRQ). Also OCR0+1 viele Schritte. Einen Schritt nach dem OCR0-Match, also zusammen mit dem zurücksetzen wird der OCR-IRQ wirksam. Der TOV-IRQ würde weiterhin bei einem echten Überlauf erfolgen. Das passiert, wenn es vorher kein zurücksetzen gab. Wann? Wenn man TCNT0 einen größeren Wert zuweist, als OCR0. Dann läuft der Timer von dort aus weiter rauf, bis er irgendwann (0xFF) überläuft.
Mode3:fastPWM - der Timer läuft von 0x00 bis 0xFF, und dann auf 0x00 über (single slope), also 256 Schritte pro Überlauf. Ist TCNT0=OCR0, wird einen Timertakt später der OCR-Interrupt wirksam. Außerdem Wird beim Überlauf der TOV-IRQ wirksam.
Hmm... sieht aus wie Mode1? genau! Was ist der Unterschied?
Nun, einerseits habe ich die ganze Compare Output Mode Betrachtung weggelassen, wir wollen ja nur die 2 IRQs nutzen, und die Timerhardware keine automatische Waveform am OCPin generieren lassen. Andererseits gibt es doch einen Unterschied:
In PWM Modi ist das OCR0 gepuffert, wird dort also ein Wert hineingeschrieben wird der erst zu einem bestimmten späteren Zeitpunkt wirksam (zusammen mit dem TOV?).
In nonPWM ist es nicht gepuffert, hier wird jede Änderung sofort wirksam.

Ich hatte Dir also Mode0 oder Mode3 empfohlen; single slope von 0x00 bis 0xFF, 256 Schritte pro Überlauf. Mit der oben verlinkten Rechnerei war ich auf den Prescaler 64 gekommen, mit 8Mhz Systemtakt ergäbe das wie gesagt ca 61Hz Wiederholfrequenz auf Deiner Matrix. Mit dem externen Quarz und der damit doppelten Systemfrequenz wären es dann halt ca 122Hz (wenn man den Prescaler nicht auch verdoppelt (was bei Timer0 nicht geht)).
Bei jedem Timerdurchlauf erfolgt also irgendwo ein OCR-Match, und (später) ein TOV-Match (den im CTC erwähnten Fall mal weggelassen -> der tritt nämlich im normal mode ebenso auf da nonPWM -> es ist also besser, fasPWM zu verwenden).

FAngen wir also mal an irgendeinem Punkt kurz vor dem TOV an:
Der Timer inkrementiert ein paar mal, JETZT erfolgt der TOV-IRQ, der Timer läuft über und Deine ISR schaltet die Lichter an.
Der Timer inkrementiert (nach dem Überlauf von 0 aus) weiter, bis er gleich OCR0 ist, JETZT (einen Timertakt später) erfolgt der OCR-IRQ, Deine ISR schaltet die Lichter aus, und berechnet die nächste Spaltenadresse.
Der Timer inkrementiert weiter, irgendwann läuft er im nächsten TOV mit einem IRQ über, und beginnt wieder von 0, Deine ISR schaltet die (neuen) Lichter wieder an.
usw...
Die Frequenz der Überläufe ändert sich nicht, also auch nicht die Frequenz in der die Spalten gewechselt werden, und damit eben auch nicht die Bildwiederholfrequenz.
Ja, natürlich mußt Du OCR0 einen anderen Wert als 0 geben, sonst liegen die beiden events ja nahezu an derselben Stelle. Nimm einfach mal an, Du hättest OCR0 auf 127 (also die Hälfte) gesetzt. Meine Frage war: Was geschieht, wenn OCR0 jetzt einen größeren oder einen kleineren Wert erhält?

Aber du hast Recht... Das mit dem Multiplexen in den ISR ist noch ein bisschen "fremd" für mich...

Könntest du mir einen Pseudocode schreiben ?

TOV:

Tuh dies..
mach das...


COMPA:

Tuh dies...
mach das...


ich kann mir unter deiner Beschreibung nicht wirklich was her leiten...
 
Also mal gaaanz zurück zum Anfang...
Wie war denn das mit dem Multiplexing in Deinem Eröffnungsbeitrag?
Du hattest in der Haupschleife folgendes gemacht:
Code:
erste Spalte an
entsprechende Pixelinfos an (also quasi VRAM[1])
'ne Weile warten
erste Spalte aus
zweite Spalte an
VRAM[2] auf die Zeilen
warten
Spalte 2 aus
Spalte3 an
...
...
Spalte 8 an
VRAM[8] auf die Zeilen
warten Spalte 8 aus
Das ganze wiederholt sich, lediglich unterbrochen durch die Timerinterrupts ununterbrochen.
(Anmerkung nebenbei: es wäre besser, wenn Du die neuen Zeilenwerte aus VRAM zuweist, während die Spalten aus sind. Derzeit würde nach dem aktivieren der jeweils neuen Spalte noch die Zeilenzuweisung der vorherigen Spalte auf der neuen Spalte landen. Zwar nur kurz, aber trotzdem da. Also erst alte Spalte aus, dann neuen Wert aus dem VRAM zuweisen, dann neue Spalte an)
Zurück zum Thema:
Die Laufzeit wird also im wesentlichen durch die 8 identischen Wartezeiten festgelegt. Und damit auch die Bildwiederholfrequenz. Du kannst die Schleife aber auch so aufbauen:
Code:
VRAM[N] auf die Zeilen
Spalte N anschalten
warten
Spalte N ausschalten
N inkrementieren
wenn N größer als 8, N zurück auf 1
(in Assembler würde man das wahrscheinlich eher rückwärts Zählen, da das erreichen von 0 bzw der Unterlauf von 0 auf 255 direkt als Sprungbedingung genutzt werden kann (N zurücksetzen nur dann), aber Dich interessiert das nicht)
Diese Schleife macht eigentlich genau dasselbe wie Deine (bis auf die Anmerkung oben), sie läuft lediglich achtmal so schnell durch, benötigt 8 Durchläufe für die ganze Matrix. Mit N [1..8] geht die Schleife halt die 8 Spalten nacheinander durch.
So, die Schleife läuft ja im wesentlichen ununterbrochen durch (bis auf Interrupte), man könnte also genausogut schreiben:
Code:
 warten
Spalte N ausschalten
N inkrementieren
wenn N größer als 8, N zurück auf 1
VRAM[N] auf die Zeilen
Spalte N anschalten
Statt gezielt die eine Aktive Spalte abzuschalten kannst Du auch alle gleichzeitig abschalten:
Code:
 warten
alle Spalten ausschalten
N inkrementieren
wenn N größer als 8, N zurück auf 1
VRAM[N] auf die Zeilen
Spalte N anschalten
Soweit noch alles klar?
Dein Programm hatte wie gesagt alle 8 Spalten (mit 8 Warteinstruktionen) bei jedem Durchlauf dargestellt.
Nehmen wir mal 'ne Wiederholfrequenz von ca 60Hz an.
Diese Schleife mußte jetzt wie gesagt mit der 8fachen Frequenz überlaufen, da sie ja nacheinander immer nur eine von 8 Spalten zeigt, also ca 480Hz. Die Wartezeit in der Schleife ist hier nur noch ein achtel (1 von 8 waits). Klar?
Und wie ist das jetzt mit dem Interrupt (bzw die Lösung mittels Interrupt)?
Nun, der Interrupt soll das Warten rausnehmen, der Interrupt soll dafür sorgen, daß " die Schleifendurchläufe" (bzw jetzt ISR-Aufrufe) mit derselben Frequenz auftreten. Der Code wandert also aus der Hauptschleife in die TOV-ISR, das warten fliegt raus:
Code:
Spalte N ausschalten
N inkrementieren
wenn N größer als 8, N zurück auf 1
VRAM[N] auf die Zeilen
Spalte N anschalten
Statt zu warten wird jetzt der Code 480mal pro Sekunde ausgeführt (also ca alle 2 ms einmal) - dabei wird jedesmal auf eine neue der 8 Spalten umgeschaltet. In den Zeiten dazwischen rotiert Dein Programm in der Hauptprogrammschleife, wo jetzt die Manipulation des VRAM ausgelöst werden kann (neue Inhalte und/oder Laufen des Textes).
Warum jetzt hier der TOV? Nun, Du könntest stattdessen auch den OCR nehmen. Es geht ja lediglich darum, den jeweiligen Interrupt alle 2ms (480Hz) zu erhalten. Und die Rechnung dazu wäre eben (8MHz Systemtakt)/(64 Timer Prescaler)=125kHz Timertakt. (125kHz)/(256 Timerschritte (8bit))=488Hz Überlauffrequenz des Timers. Der Timer läuft 488mal über, und kann dabei je einen IRQ erzeugen.
Der Timer läuft aber mit einer Konstanten Geschindigkeit, er erreicht also auch den Wert zweiundvierzig 488mal pro Sekunde. Oder 13. Oder 83.
Man könnte also ebenso einen beliebigen Wert in das OCR setzen, und den OCR-IRQ nutzen.

Erst wenn das soweit klar ist, kann man mal meinen Versuch, das auf 2 IRQs zu teilen angehen...
 
Jetzt habe ich es ein bisschen mehr verstanden, was du meintest!
Nur habe ich das Problem... Das ich bei den Reihen oder Spalten (ist ja mehr oder weniger egal) diese nicht alle an einen Port habe.
Meine Reihen sind an einem Port... Meine Spalten jedoch nicht....

Dann kann ich das mit deiner Variante...

Code:
    PORTA &= ~(1<<PA0) << col;
    if (col > 7)
    {
        col = 0;
    };
    PORTA = VRAM[col];
    PORTA |= (1<<PA0) << col;
    col ++;

nicht so ohne weiteres machen oder sehe ich das falsch ?

Die Reihen die an einem Port gehen (ROW 1 = PORTA.0 ...) wollte ich für meine "Bitmasken Ausgabe" benutzen.
 
Hallo Janiiix,
Nur habe ich das Problem... Das ich bei den Reihen oder Spalten (ist ja mehr oder weniger egal) diese nicht alle an einen Port habe.
Meine Reihen sind an einem Port... Meine Spalten jedoch nicht....

hier eine mögliche einfache Lösung:

Code:
ISR(TIMER0_COMP_vect)
{

    /***********************************
    2ms Ereignis
    Framerate 8x2ms = 16ms
    ************************************/

    static volatile uint8_t spalte = 0;

    // alle Spalten abschalten:
    PORTC |= (1<<PC7) | (1<<PC6) | (1<<PC5) | (1<<PC4) | (1<<PC3);
    PORTB |= (1<<PB2) | (1<<PB1) | (1<<PB0);
    
    // neue Zeilendaten ausgeben
    
    PORTA = VRAM[spalte];

    // Spalte aktivieren
    switch (spalte) {
        case 0: PORTC &= ~(1<<PC3);
            break;
        case 1: PORTC &= ~(1<<PC4);
            break;
        case 2: PORTC &= ~(1<<PC5);
            break;
        case 3: PORTC &= ~(1<<PC6);
            break;
        case 4: PORTC &= ~(1<<PC7);
            break;
        case 5: PORTB &= ~(1<<PB2);
            break;
        case 6: PORTB &= ~(1<<PB1);
            break;
        case 7: PORTB &= ~(1<<PB0);
            break;
        default: break;
    }

    spalte++; // nächste Spalte
    if (spalte >= 8) spalte = 0; // Spalten 0..7

}

Da die Spalten auf zwei Ports liegen und dann noch verschoben und "verdreht" sind, habe ich es mal über switch-case gelöst, dann ist das auch verständlicher. Die ISR soll nur andeuten, dass das alles in einer TimerISR ablaufen sollte, nicht im Hauptprogramm. Der Code ist nicht getestet. Man kann auch zwei TimerISR verwenden, um die Helligkeit zu steuern, wie LotadaC erwähnt hat, das kannst du ja später mal machen, wenn die einfache Lösung mit einer TimerISR läuft. (Leider kann ich via PN nicht so viel helfen, also bitte immer das Forum nutzen)

Dirk :ciao:

---
Werde Premiumuser und unterstütze dadurch das Forum :)
 
Das Problem ist daß (1<<X) letztendlich im Controller 'ne Konstante ist, nämlich 2X, bzw 'ne binäre Zahl wo alle Bits 0 sind, nur eben das X-Bit (von hinten) ist 1. Genau das ist ja der Sinn. Um hier bei den PORTs zu bleiben - das PORT-Register eines Ports legt mit seinen 8 Bits die Pegel der 8 Beine fest. dabei fängt man allerdings von rechts an, und mit 0. Das PORTA-Register ist also:
PORTA7|PORTA6|PORTA5|PORTA4|PORTA3|PORTA2|PORTA1|PORTA0
Die IDE kennt die Werte dieser Bits (auch unter den PAn-Namen die Du verwendest). PA7=7, PA6=6,...,PA0=0

Aus (1<<PA2) wird also bei der Compilierung die Konstante 23=8=0b00000100.

'Ne Konstante kann aber zur Laufzeit nicht variabel sein.

Wenn die Spalten an einem PORT liegen, kann man einfach das PORT-Register lesen, den Inhalt eins nach links schieben (verdoppeln), prüfen ob dabei ein Überlauf stattfand (bzw elegant das Carry addieren), und auf den Port zurückschreiben.
Wenn Die Spalten sich zwar auf mehrere PORTs verteilen, aber immerhin noch entsprechend der Zweierpotenzen sortiert sind (also die Bits 0,1,2,3,4,5,6,7) bleiben, nur eben auf unterschiedliche Ports verteilt sind, könnte es so trotzdem noch Sinn machen - stell Dir bei Deinem Code die Zuordnung der Bits im C-PORT mal genau andersrum vor, dann könntest Du das trotzdem erstmal in einem Byte (Variable) so berechnen, und dann den einen Teil (maskiert) dem einen Port zuweisen, und den anderen dem anderen.
Sonst bleibt eigentlich nur noch der Weg, das Fallweise mit case..of, oder switch oder wie das jetzt in C heißt, zu machen (letztendlich vom erzeugten Code her 8 IF..Then..).

Das andere Problem war, von der Nummer des zu setzenden Bits auf den Wert zu kommen - bei PA2 da oben eben von 3 auf 8.
Erstmal ist es sinniger, col von 0 bis 7 laufen zu lassen, das entspricht der Bitnummern. Die Variable, die dem Port (bzw den Ports) zugewiesen wird, muß also aus Col=3 8 erhalten. Wie? Steht eigentlich oben: 23, genauer 2col. Ob C potenzieren kann, weiß ich nicht. Und ob C dann das Potenzieren von 2 effizient umsetzt, weiß ich auch nicht. Ich würde also (um die ISR schnell zu halten) quasi einen 2ten Zähler (Variable) verwenden, der aber nicht inkrementiert, sondern sich verdoppelt. (Wo dann wieder die Frage steht, ob man in C logisch schieben kann (LSL))
Also einen inkrementierenden Zähler für die Adressen im VRAM, und einen potenzierenden (schiebenden) für die Bitnummern im Ausgabeport. Klar kann man auch aus einem den anderen berechnen, aber meiner Meinung nach sind 2 getrennte Zähler schneller als die Rechnung. Der 2te Zähler ist ja dann immer das, was Du in den (ggf kombinierten) Port schreiben mußt. An welchen der beiden Zähler Du jetzt die Kontrolle "achte Spalte fertig?" nagelst, ist egal. Wäre jetzt also folgender Pseudocode in der ISR:
Code:
alle Spalten aus
col inkrementieren
Potenzzähler verdoppeln (in ASM linksschieben, in C ?)
wenn der dabei Überläuft, col und Potenzzähler zurücksetzen
VRAM[col] auf die Zeilen
Potenzzähler auf den Spaltenport
Bei kombinierten Ports halt 2 Zuweisungen mit den je Maskierten Anteilen des Potenzzählers.
Die untersten 3 nach PortB, die obersten 5 nach PortC. In C wären das dann zwei Einzeiler, in etwa so:
PortB wird zugewiesen ((Maske mit B2, B1, B0) bitweises AND (Potenzzähler))
PortC analog...
"wird zugewiesen" ist wegen der restlichen Bits der Ports eine RMW-Zuweisung, aus dem scheinbar kurzen Einzeiler wird also in Wirklichkeit etwas in der Art:
Potenzzähler in ein Rechenregister laden
Port in ein anderes
Im Port die Bits der Maske löschen (ANDI PortRechenregister, ~Maske)
Potenzzähler und Maske verANDen (ANDI Potenzzählerregister, Maske)
Beide verANDen (AND Portrechenregister, Potenzzählerregister)
Ergebnis dem Port zuweisen.
Das siehst Du in C natürlich nicht,in ASM würde man das aber selbst schreiben, und könnte im konkreten Fall hier und da optimieren (konkret weiß ich, daß die Bits im Portregister (Maske) bereits gelöscht sind, das war ja der erste Schritt in der ISR, zum verdoppeln (potenzieren) des Potenzzählers wurde selbiger natürlich auch schon in ein Rechenregister geladen, und hinterher zurückgeschrieben - In ASM könnte ich da das nochmalige laden sparen. )
 
Ich habe jetzt die Methode von Dirk angewandt.
Das mit "switch - case" sollte ja für den Controller kein Problem sein (wegen Geschwindigkeit).


alle LED´s ausschalten & anschalten klappt wunderbar. Nur wenn ich jetzt ein Zeichen darstellen will (was vorher mit meiner Multiplexroutine funktioniert hatte) zeigt er mir auf dem Display irgend nen Mist an... An was kann das liegen ? Ich habe vorher doch nichts anderst gemacht als jetzt (nur das meine Multiplexroutine nicht effizient war)

Code:
    volatile uint8_t VRAM[]= {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};



int main(void)
{
    DDRA |= ((1<<PA0) | (1<<PA1) | (1<<PA2) | (1<<PA3) | (1<<PA4) | (1<<PA5) | (1<<PA6) | (1<<PA7));
    DDRB |= ((1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) | (1<<PB5) | (1<<PB6) | (1<<PB7));
    DDRC |= ((1<<PC3) | (1<<PC4) | (1<<PC5) | (1<<PC6) | (1<<PC7));
    

/* TIMER1 CompareMatch */
    TCCR1B |= (1 << WGM12)|(1 << CS11)|(1 << CS10);
    TCNT1 = 0;
    TIMSK |= (1 << OCIE1A);    
    OCR1A = 3;


    
/* Interrupts global erlauben */
    sei(); 
 
   

    while(1)
    {    
    
    
            
    }
    
}






ISR(TIMER1_COMPA_vect)
{
    
    static volatile uint8_t spalte = 0;
    PORTC |= (1<<PC7) | (1<<PC6) | (1<<PC5) | (1<<PC4) | (1<<PC3);
    PORTB |= (1<<PB2) | (1<<PB1) | (1<<PB0);
    
    PORTA = VRAM[spalte];
    
    switch (spalte) {
        case 0: PORTC &= ~(1<<PC3);
        break;
        case 1: PORTC &= ~(1<<PC4);
        break;
        case 2: PORTC &= ~(1<<PC5);
        break;
        case 3: PORTC &= ~(1<<PC6);
        break;
        case 4: PORTC &= ~(1<<PC7);
        break;
        case 5: PORTB &= ~(1<<PB2);
        break;
        case 6: PORTB &= ~(1<<PB1);
        break;
        case 7: PORTB &= ~(1<<PB0);
        break;
        default: break;
    }

    spalte++; // nächste Spalte
    if (spalte > 7) spalte = 0; // Spalten 0..7


}
 
...Sonst bleibt eigentlich nur noch der Weg, das Fallweise mit case..of, oder switch oder wie das jetzt in C heißt, zu machen (letztendlich vom erzeugten Code her 8 IF..Then..)...
Genau, wie Dirk es inzwischen schon implementiert hat...
Die static volatile uint ist dann sowas wie 'ne globale Variable, die einmal mit 0 initialisiert wurde? Also (hochsprachen-Zugriffsberechtigungen beiseite) es wird irgendwo am Anfang des Programmes ein SRAM-Byte gelöscht (initialisiert), und die Variable zeigt auf diese SRAM-Zelle (Adresse)?

Edit: Warum denn jetzt Timer1? das ist ein 16bit-ter
Und Was hast Du da überhaupt eingestellt?
Prescaler=64
WGM=0b100, also Mode4

Der Timer läuft von 0 bis OCR1A, und da über. dabei fällt der IRQ, also alle 4 Timertakte. (8MHz/64)/4=31,250kHz Spaltenfrequenz (knapp 4kHz Bildwiederholfrequenz), anders gesagt alle 32µs ein IRQ. Ein Systemtakt dauert bei 8 MHz 0,125µs, also fällt der IRQ anders gesagt alle 256 Takte. DAs ist viel(!!) zu schnell, und sicher nicht Deine Absicht...)
 
Genau, wie Dirk es inzwischen schon implementiert hat...
Die static volatile uint ist dann sowas wie 'ne globale Variable, die einmal mit 0 initialisiert wurde? Also (hochsprachen-Zugriffsberechtigungen beiseite) es wird irgendwo am Anfang des Programmes ein SRAM-Byte gelöscht (initialisiert), und die Variable zeigt auf diese SRAM-Zelle (Adresse)?

Genau so wird das gemacht. Nur weiß ich halt nicht warum jetzt meine Zeichen nicht mehr dargestellt werden :(
 
siehe Edit in #35


Ich habe ihn jetzt mit folgendem Setup konfiguriert...

Code:
/* TIMER0 CompareMatch */
    TCCR0 |= (1<<WGM01) | (1<<CS02) | (1<<CS00);
    TIMSK |= (1<<OCIE0);
    OCR0   = 0x4D;


Jetzt habe ich eine Bildwiderholrate von ca. 100 Hz...

Nun flackert es aber extrem auf der Matrix...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)