C LED Matrix 8 x 8

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Hallo Freunde, Ich habe mal eine Frage an euch... Bin gerade dabei mir eine LED Matrix zu bauen (Okay... Hardware steht schon)... Nun bin ich dabei die Software zu schreiben... Das Multiplexing funktioniert schon einwandfrei... Mir geht es jetzt darum, wie ich am besten mein "charSet" definiere & darstelle. Es soll in erster linie ein "Würfel" werden. zum einmaligen starten würde ich gerne einen Lauftext anzeigen lassen. Mir fehlen an dieser Stelle die Ideen, wie ich das am besten & intiligentesten umsetzen könnte... Habt ihr Vorschläge für mich?

Hier der aktuelle Quellcode...
___________________________________

Code:
/* Definiert die Frequenz */
    #define F_CPU 8000000

/* Deklariert die Reihen ( LOW AKTIV ) " 1 - 7 " */
    #define ROW_1_ENABLE     PORTA &= ~(1<<PA0)
    #define ROW_2_ENABLE     PORTA &= ~(1<<PA1)
    #define ROW_3_ENABLE     PORTA &= ~(1<<PA2)
    #define ROW_4_ENABLE     PORTA &= ~(1<<PA3)
    #define ROW_5_ENABLE     PORTA &= ~(1<<PA4)
    #define ROW_6_ENABLE     PORTA &= ~(1<<PA6)    
    #define ROW_7_ENABLE     PORTA &= ~(1<<PA5)
    #define ROW_8_ENABLE     PORTA &= ~(1<<PA7)
    
    #define ROW_1_DISABLE     PORTA |= (1<<PA0)
    #define ROW_2_DISABLE     PORTA |= (1<<PA1)
    #define ROW_3_DISABLE     PORTA |= (1<<PA2)
    #define ROW_4_DISABLE     PORTA |= (1<<PA3)
    #define ROW_5_DISABLE     PORTA |= (1<<PA4)
    #define ROW_6_DISABLE     PORTA |= (1<<PA6)
    #define ROW_7_DISABLE     PORTA |= (1<<PA5)
    #define ROW_8_DISABLE     PORTA |= (1<<PA7)

/* Deklariert die Spalten " 1 - 7 " */
    #define COLUMN_1_ENABLE  PORTC |= (1<<PC3)
    #define COLUMN_2_ENABLE  PORTC |= (1<<PC4)
    #define COLUMN_3_ENABLE  PORTC |= (1<<PC5)
    #define COLUMN_4_ENABLE  PORTC |= (1<<PC6)
    #define COLUMN_5_ENABLE  PORTC |= (1<<PC7)
    #define COLUMN_6_ENABLE  PORTB |= (1<<PB2)    
    #define COLUMN_7_ENABLE  PORTB |= (1<<PB1)    
    #define COLUMN_8_ENABLE     PORTB |= (1<<PB0)

    #define COLUMN_1_DISABLE PORTC &= ~(1<<PC3)
    #define COLUMN_2_DISABLE PORTC &= ~(1<<PC4)
    #define COLUMN_3_DISABLE PORTC &= ~(1<<PC5)
    #define COLUMN_4_DISABLE PORTC &= ~(1<<PC6)
    #define COLUMN_5_DISABLE PORTC &= ~(1<<PC7)
    #define COLUMN_6_DISABLE PORTB &= ~(1<<PB2)
    #define COLUMN_7_DISABLE PORTB &= ~(1<<PB1)
    #define COLUMN_8_DISABLE PORTB &= ~(1<<PB0)


/* Umbennung der Eingabe ( Taster ) */
    #define TASTER_1    (!(PIND & (1<<PD5))
    #define TASTER_2    (!(PIND & (1<<PD6))
    #define TASTER_3    (!(PIND & (1<<PD3))
    #define TASTER_4    (!(PIND & (1<<PD4))
    
/* Summer */
    #define SUMMER_AUS    PORTC |=  (1<<PC2)
    #define SUMMER_AN  PORTC &= ~(1<<PC2)
    
/* Multiplex Zeit */
    #define MULTIPLEX 1

                                        
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

/* VRAM */
    const int charSlash [8] =
                            {0B01111111,
                             0B10111111,
                             0B11011111,
                             0B11101111,
                             0B11110111,
                             0B11111011,
                             0B11111101,
                             0B11111110,};

    uint8_t VRAM [8]    =
                            {
        
                            };

uint8_t Test;

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));
    
    sei(); 
   
    TIMSK |= (1<<TOIE1);
    TCCR1B |= (1<<CS11) |  (1<<CS10);     
    TCNT1 = 0xFFFF;         
   

    while(1)
    {    
        
        COLUMN_1_ENABLE;    
        PORTA = VRAM[0];
        _delay_ms(MULTIPLEX);
        COLUMN_1_DISABLE;

        COLUMN_2_ENABLE;
        PORTA = VRAM[1];
        _delay_ms(MULTIPLEX);
        COLUMN_2_DISABLE;

        COLUMN_3_ENABLE;
        PORTA = VRAM[2];
        _delay_ms(MULTIPLEX);
        COLUMN_3_DISABLE;
    
        COLUMN_4_ENABLE;
        PORTA = VRAM[3];
        _delay_ms(MULTIPLEX);
        COLUMN_4_DISABLE;

        COLUMN_5_ENABLE;
        PORTA = VRAM[4];
        _delay_ms(MULTIPLEX);
        COLUMN_5_DISABLE;
    
        COLUMN_6_ENABLE;
        PORTA = VRAM[5];
        _delay_ms(MULTIPLEX);
        COLUMN_6_DISABLE;
    
        COLUMN_7_ENABLE;
        PORTA = VRAM[6];
        _delay_ms(MULTIPLEX);
        COLUMN_7_DISABLE;
    
        COLUMN_8_ENABLE;
        PORTA = VRAM[7];
        _delay_ms(MULTIPLEX);
        COLUMN_8_DISABLE;
    
    }
    
}


ISR(TIMER1_OVF_vect)
{
    if (Test <= 7)
    {    
        Test = Test + 1;
    }
        else
    {
        Test = 0;
    }
    
    switch (Test)
    {
    case 0:
    {
        VRAM[0] = charSlash[0];
        VRAM[1] = charSlash[1];
        VRAM[2] = charSlash[2];
        VRAM[3] = charSlash[3];
        VRAM[4] = charSlash[4];
        VRAM[5] = charSlash[5];
        VRAM[6] = charSlash[6];
        VRAM[7] = charSlash[7];
    }
    
        break;
    }
    
    
}
 
Als erstes legst Du die anderen benötigten Zeichen im Flash/Eeprom ebenso ab. Je nach erforderlicher Vollständigkeit des Zeichensatzes in einer selbst festgelegten Reihenfolge, oder in der des ASCII. letzteres hat den Vorteil, daß Du (insbesondere bei einem Fixed Width Font) einfach die Adresse des jeweils ersten Bytes berechnen lassen kann, aus dem "String-Zeichen" heraus.

Dann sollte die Multiplex-Routine interruptgekoppelt werden, am sinnigsten mit 2 Interrupts eines Timers (im Compare-IRQ werden alle Spalten abgeschaltet und die nächsten Zeilen-Bits auf die Spalten geschaltet, um Überlauf wird die neue Spalte aktiviert, und außerdem eine Zeitbasis generiert.)

Im Hauptprogramm verwaltest Du nun, was konkret dargestellt werden soll. Ob da nun tatsächlich ein Bildspeicher nötig ist, oder die Multiplex-Routine die Adressenin der Zeichentabelle übergeben bekommt, ist Deine Sache...
 
Hallo Lodadac...

Vielen dank für deine Antwort...
& Wie sieht es mit der "Laufschrift" aus ? Wie würde man sowas am schlausten lösen?
 
Nehmen wir mal an, Du hast irgend'n String, den Du durchlaufen lassen willst. Aufgrund der festen Zeichenbreite weißt Du, wie oft der geschoben werden muß, um einmal durchzulaufen (also wie lang der Text in columns ist). Aus der Zeitbasis die beim Multiplexing mitabfällt, kannst Du jetzt 'ne sinnige Zeitbasis für das "schieben" ableiten. Beim "Schieben" wird dann einfach die erste darzustellende Spalte im String hochgezählt.

Also erstmal ist sie null - beim Multiplexing wird also für die Zeilenpixel der ersten column gerechnet: 0/Zeichenbreite (8)=0 Rest 0, also das Zeichen 0 des Stringes. Davon die ASCII-Ziffer bestimmt, und den Rest (0) und die Matrix-column (0) draufaddiert ist die Adresse im Zeichenspeicher, der die darzustellende Pixelinfo enthält. Die nächste Matrix-column ist dann eins weiter.

Wurde bereits 11x geschoben hast Du dann 11/8=1 Rest 3. Also Zeichen 1 des Stringes. Dessen ASCII-Ziffer (dort beginnt das Zeichen im Speicher, aber Du greifst im Multiplexing nicht auf diese Adresse zu , sondern auf die 3 weiter (Rest 3).

Im Prinzip wird hier solange mit Speicheradressen rumjongliert (der String liegt ja auch als Byte-Folge im Speicher, wobei die Bytes bereits der ASCII sind), bis das die gerade darzustellenden Zeilenpixel repräsentierende Byte im Zeichenspeicher indiziert wird. (lufthol)
 
Sorry,

Da steige ich noch nicht ganz durch.

Ich möchte ja quasie eine Routine haben, an der ich meinen "Anzeigetext" übergeben kann. Sprich...

Code:
void Lauftext(uint8 String)
{
  Funktion zum Ausgeben...
}

Das mit dem Multiplexen klappt ja soweit sehr gut. Ein einzelnes Zeichen kann ich schon als "Lauftext" darstellen. Ein String müsste in meinem Fall dann doch ca. so aussehen ?

Code:
    char  String2    [ ]    =     {0xFF,0xFF,0x81,0xF3,0xE7,0xCF,0x81,0xFF,0xFF,0xFF,0x81,0xF3,0xE7,0xCF,0x81,0xFF};

oder bin ich da total auf dem Holzweg?
 
Aber während des Multiplexens macht der Kontroller bei Dir nichts anderes mehr. Du schaltest eine Matrix-column an (mit den entsprechenden row-Pixeln), und WARTEST dann 'ne Weile. Danach die column wieder aus, und mit der nächsten weiter.
Das ganze wird nur unterbrochen (und verzögert), wenn Dein Timer IRQ zuschlägt, welcher bei jedem neunten Aufruf die Zeichen-Bytes in den VRAM kopiert.
OK, wenn Du jetzt andere Zeichen ablegst, kannst Du so die ganzen Zeichen Tauschen lassen - ich dachte, Dein Lauftext soll pixelweise durchlaufen.

Grundlagen: Was ist ein String? Nur eine Folge von Zeichen. Wenn Du in Deiner IDE also die Folge "lustiger String" im Speicher definierst, landet im Kontroller letztendlich die Bytefolge:
0x6c 0x75 0x73 0x74 0x69 0x67 0x65 0x72 0x20 0x53 0x74 0x72 0x69 0x6e 0x67 0x00
Ein Byte für jedes Zeichen, und 'ne NULL, die den String terminiert.
Ebenso kannst Du den String natürlich über die serielle Schnittstelle empfangen, und im Speicher ablegen.
Zur Darstellung des Zeichens auf Deiner Matrix brauchst Du jetzt aber 8 Bytes (mit je 8 Bit) - eben die 64 Pixel. Nun mußt Du aber die Pixelinfos des "g" da oben (0x67) nicht zweimal; Du mußt nur dafür sorgen, daß wenn das Zeichen 0x67 angezeigt werden soll, die Pixelinfos des "g" an das Multiplexing übergeben werden. Und dafür bietet es sich eben an, die Zeichen in der ASCII-Reihenfolge abzulegen.
Nun interessieren Dich auf der einen Seite die Modem-Steuercodes nicht (also die ersten 32), also kannst Du die weglassen. Dann Würde das Leerzeichen (0x32) Dein erstes Zeichen (Zeichen 0) sein. Wenn es also ab Speicheradresse 0x00 beginnt, müßtest Du die weggelassenen 0x32 abziehen (0x32-0x32=0x00), und wegen Deiner 8Bytes pro Zeichen eben mit 8 Multiplizieren (0x00*8 bleibt 0x00). Bei Adresse 0x00 würde jetzt also das erste Byte für die Matrix auszulesen sein, 0x00 wäre die Adresse, die das Multiplexing übergeben bekommt.
Soll "!" ausgegeben werden, also ASCII-0x33 würde die Rechnung ergeben: 0x33-0x32=0x01, 0x01*8=0x08. Also erhält das Multiplexing 0x08 als Adresse.
Zur Erinnerung: die ersten 8 Bytes waren das " " (0x00..0x07), danach käme das Anführungszeichen usw usf...
Beachte: auch wenn Du Dich so auf die 95 druckbaren ASCII beschränkst, benötigst Du 95*8=760 Bytes Zeichenspeicher - kannst dann aber auch fast alles schreiben lassen.
Beginnt Dein Zeichenspeicher nicht an Adresse 0x00 sondern irgendwo anders, ist dieser Offset nach der Multiplikation mit 8 dazuzuaddieren.
Auf Diese Weise kannst Du also auch dem String-Zeichen heraus berechnen, wo das erste Byte des entsprechenden Zeichens in Deinem Zeichenspeicher steht.
Mein Vorschlag des Multplexens wäre wie gesagt, 2IRQs eines Timers zu nutzen. Diese würden je 8mal pro Zeichen zuschlagen, nämlich bei jeder Matrix-column. In der ISR müßtest Du also die Aufrufe mitzählenum zu wissen, welche der 8 columns Du zu aktivieren hast. Diese Zahl wird außerdem jetzt auf die übergebene Start-Adresse des anzuzeigenden Zeichens addiert. Somit hättest Du jetzt die konkrete Zeichenspeicher-Adresse der gerade anzuzeigenden Matrix-Column.

Soll nun der Text pixelweise verschoben werden können, muß die Multiplexing-Routine ggf auf unterschiedliche Zeichen zugreifen können, also wird nicht die Adresse eines Zeichens übergeben, sondern der String (die Adresse wo der im Speicher beginnt), und die Verschiebung in Pixeln. Damit kann dann für die gerade aktive Matrix-Column berechnet werden, welches ASCII dort dargestellt werden soll, welche welche ASCII-Column genau, und wo die dazugehörenden row-Infos im Speicher zu lesen sind.

Im Hauptprogramm wird dann die Verschiebung "durchgezählt", und ggf der Anzuzeigende String aus der seriellen empfangen...

P.S.: Üblicherweise ist die letzte column bei den Zeichen immer aus, um die Buchstaben nicht zusammenzukleben. (in einem Text)
 
Lotadac...

Danke für deine ganze Mühe die du hier aufbringst. Leider verstehe ich zum teil nur Bahnhof... :(
Das mit dem Multiplexen und den beiden IRQ´s verstehe ich gerade noch so.
Für meine Routine zum Ausgeben oder "schieben" habe ich immer noch nicht annähernd eine Idee, wie ich dies umsetzen soll.

Ich weiß das du nicht wirklich C kannst...
Ein kleiner "Code-Schnipsel" oder Pseudocode würde mir da schon extrem weiterhelfen.
 
Mit schieben meinte ich den Text durch die Matrix schieben.
Was meinst Du denn mit Lauftext? Daß ein Zeichen nach dem anderen dargestellt wird, oder daß der Text pixelweise (spaltenweise) durchläuft?

Beim Ausgeben ist die Idee die, daß in einem der Multiplexing-Interrupte aus dem zu zeigendem Zeichen (ASCII-Nummer) und dem Zähler in der Multiplexing-IRQ die Adresse im Zeichenspeicher berechnet wird, die der nächsten Spalte im Multiplexing entspricht.
 
Soweit habe ich das schon verstanden.
Ich komme nur imoment auf keinen Code...
( Ich drücke es gerne so aus : Ich stehe in einem Wald & weiß nicht wie ich nach hause kommen soll )... sprich ich weißt nicht wie ich Anfangen soll & was ich überhaupt machen soll ( das ich den Weg nach Hause finden muss, ist klar :p)

Sowas meinte ich...

https://www.youtube.com/watch?v=rBQ5-smv_Yw
 
Also pixel- bzw spaltenweise.
Ich habe jetzt keine Ahnung, wo C die "const int charslash" konkret ablegt (flash, eeprom, sram), und wo überhaupt 760Bytes verfügbar wären. Aber letztendlich ist "charslash" nur eine Zahl, eine Adresse, ein Zeiger der eben auf die Speicherzelle zeigt, in der das erste Byte (0b01111111) steht. Über "charslash[0]" greifst Du auf diese Zelle zu, über "charslash[3]" auf die vierte (0b11101111).
Und nun stell Dir vor, Du definierst statt dem "charslash[8] alle ASCII-Zeichen in einem Feld "charset[760]". Also wie bei deinem slash je die 8 Bytes. Erst die vom Leerzeichen, dann die vom Ausrufezeichen usw usf.
Wenn Du dann also auf Deinen neuen Slash zugreifen willst, mußt Du halt die ASCII-Nummer des Slash kennen (47dez), davon ziehst Du die Anzahl der ersten weggelassenen ASCII-Zeichen (Steuercodes) ab (32, also 47-32=15). Da jedes Zeichen im Zeichenspeicher 8 Bytes benötigt, multiplizierst Du also nochmal mit 8 (15*8=120). In "charset[120]" sollte sich dann also das erste Byte des Slash befinden (0b01111111).
Und wie kommst Du auf den ASCII des Slash? Nun, C kennt den für Dich - "/" wird ja im Speicher als Byte abgelegt, eben als 47dez, bzw als 0x2F.
Für Deine Hochsprache ist 'ne abgespeicherte Stringvariable eigentlich nichts anderes als die Speicheradresse, an der das erste Zeichen des Strings abgelegt ist. Die einzelnen Zechen stehen dann ab dort nacheinander in Form ihrer ASCII im Speicher.
Ok, Jetzt stell Dir erstmal nur einen ein-Zeichen-String vor, den Du darstellen willst (meinetwegen hast Du definiert
Code:
String lustiger_string="/"
Irgendwo im Speicher wird jetzt also 0x2F 0x00 abgespeichert. Wo? Egal, "lustiger_string" zeigt auf die Speicherzelle, ist die Adresse.

Für das Multiplexing benötigst Du nun noch eine Variable, die die darzustellenden Matrix-Columns durchzählt, diese muß in beiden ISR sichtbar sein. Nennen wir sie muxcnt.
Die Überlauf-ISR schaltet jetzt nur die column[muxcnt] an, und generiert ggf 'ne Zeitbasis.
die Compare-ISR macht etwa folgendes:
Code:
Alle columns ausschalten
Inkrementiere muxcnt (und setze den bei größer 7 zurück auf 0) (muxcnt durchzählen)
lokale bytevariable temp=lustiger_string-32 (weggelessene Steuercodes)
temp=temp*8 (8Bytes pro Zeichen)
temp=temp+muxcnt (entsprechende Column im Zeichen)
Zeilenport=Zeichenspeicher[temp]

In C kannste das sicher auf eine Zeile zusammenquetschen, aber das ist in etwa das, was der Controller dann macht.
Damit sollte sich dann erstmal das (erste) Zeichen von lustiger_string gemultiplext ausgeben lassen. Wenn Du im Hauptprogramm jetzt irgendwann mal'n anderes Zeichen in lustiger_string schreibst (zB mit hilfe der Zeitbasis, oder wenn eins über den UART empfangen wurde), rscheint dieses dann auf der Matrix.
Wenn Du das erstmal hast, kannst Du das so abändern, daß lustiger_string als beliebig langer String verwendet werden kann, die Zeitbasis jetzt einen weiteren Zähler realisiert der festlegt, um wieviele Pixel der Text jetzt verschoben dargestellt werden soll.
Bisher würde auf der ersten Matrix-column die erste Column des ersten Zeichens dargestellt werden, auf der 2ten die zweite des ersten zeichens usw.
Dann wäre zu ergänzen, daß aus dem weiteren Zähler und muxcnt berechnet werden muß, vom wievielten Zeichen im String jetzt welche column zu nehmen ist.
 
Da kann ich schon mehr mit anfangen ;)

Welche IRQ´s würdest du mir ans Herz legen ? Welchen Timer ?
 
Dumme Frage, solange Du nichts zum verwendeten Controller sagst, oder?
Der Timer müßte halt einen Compare-Match-Interrupt und einen Überlauf-Interrupt bieten. Da Du keine extremen Laufzeiten brauchst, und auch keine extreme Auflösung, wird ein 8bit-Timer reichen.
Überlegungen zur Frequenz:
Dein Controller scheint mit 8MHz zu laufen, der Überlauf-IRQ würde also mit 8MHz/256=31,25kHz zuschlagen - Du könntest also 31250 Columns pro Sekunde anzeigen lassen. Deine Matrix besitzt 8 Columns wären also 3906,25Hz Bildwiederholfrequenz. Ich denke, ne Bildwiederholfrequenz von 60-100Hz wäre erstrebenswert also ein Prescaler von ca 65-40. 64 entsprächen dann 61Hz.
 
Stimmt... Sorry :(

Ist ein MEGA32... Habe auch einen ext. Quarz mit 16 Mhz den ich aber aktuell nicht benutze.
 
Dumme Frage, solange Du nichts zum verwendeten Controller sagst, oder?
Der Timer müßte halt einen Compare-Match-Interrupt und einen Überlauf-Interrupt bieten. Da Du keine extremen Laufzeiten brauchst, und auch keine extreme Auflösung, wird ein 8bit-Timer reichen.
Überlegungen zur Frequenz:
Dein Controller scheint mit 8MHz zu laufen, der Überlauf-IRQ würde also mit 8MHz/256=31,25kHz zuschlagen - Du könntest also 31250 Columns pro Sekunde anzeigen lassen. Deine Matrix besitzt 8 Columns wären also 3906,25Hz Bildwiederholfrequenz. Ich denke, ne Bildwiederholfrequenz von 60-100Hz wäre erstrebenswert also ein Prescaler von ca 65-40. 64 entsprächen dann 61Hz.

Also den CompareMatch habe ich jetzt auf ca. 100 Hz kofiguriert. Dumme Frage... Muss der Overflow jetzt auch auf ca. 100 Hz laufen ?
 
Mit dem 16er Quarz hättest Du zwischen den IRQs mehr Takte - allerdings wären der nötige Prescaler dann höher.
Der Mega besitzt 2 8bit-Timer, die sich nicht sehr unterscheiden, beide bieten die beiden benötigten IRQs.
Timer2 bieten mehr Möglichkeiten beim Prescaler und könnte asynchron über einen Uhrenquarz getaktet werden, bzw den Uhrenquarz insbesondere selbst treiben (um einen genauen Sekundentakt zu erhalten zB), Timer0 dagegen kann über einen externen Pin getaktet werden, also quasi 'ne externe clock zählen.
Timer0 würde mit dem 16er Quarz und dem 64er Prescaler dann 'ne Bildwiederholfrequenz von gut 120Hz liefern, den Prescaler für gut 60Hz hat er nicht.
Nein, Du siehst das falsch...
Der Compare hat immer dieselbe Frequenz wie der Überlauf. Du verwendest den normalMode oder eben fastPWM (ohne PWM-Ausgabe - Du machst das ja in Software für die einzelnen Matrix-LEDs).
Bei jedem Durchlauf des Timers fällt ein Compare-IRQ und ein TOV-IRQ - nur eben zu unterschiedlichen Zeiten.
Im TOV schaltest Du die LEDs einer Spalte an, und im (folgenden) Compare (alle) wieder aus - was geschieht also, wenn Du am Comparewert "drehst"?
Ich hatte es in den letzten Beiträgen ja vorgerechnet, Du stellst pro Timerdurchlauf eine Spalte dar, Du hast 8 Spalten, der 8bit-Timer zählt 256 Werte durch, und ist über den Prescaler an den Systemtakt gebunden. Der Rest ist Mathe Klasse 4 :p
 
Ich lege doch meinen "CompareMatch" oder und "Überlauf" Wert fest, dass ist doch richtig oder ?
Da muss ich doch wissen mit welchen Werten ich für den "CompareMatch" arbeiten muss und für den "Überlauf" genau das gleiche...?

z.B
Code:
/* Überlaufwert von Timer 0 (CompareMatch) */
OCR0A = 77;
 
...Überlegungen zur Frequenz:
Dein Controller scheint mit 8MHz zu laufen, der Überlauf-IRQ würde also mit 8MHz/256=31,25kHz zuschlagen...
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?
 
Hallo zusammen,

ich weiß nicht, ob es hier zu einer Lösung gekommen ist, ich habe mir auch ehrlich gesagt nicht alles durchgelesen, bisher ging es anscheinend immer nur um Multiplexen.

Ich hätte einen Vorschlag oder Tipp, der vielleicht weiterhelfen könnte ...

Für ein LCD Grafikdisplay mit 128x64 Pixel Auflösung, hatte ich vor einiger Zeit ein Projekt in C geschrieben. Das Projekt findet ihr in meinem Blog:
Atmel Studio C-Library für Displaymodule XV-GDM128X64 und XV-GDM128X64-RGB


Was hat nun das LCD Grafikdisplay mit einer LED Matrixanzeige zu tun?
Beides sind "Raster-Ausgabegeräte".

Wie könnte das nun weiter helfen?
Der Grafikspeicher befindet sich im SRAM des Mikrocontrollers als Array. Dieser Speicherbereich läßt sich pixelweise adressieren (Locate x, y). Um das für eine LED Matrixanzeige zu verwenden, müsste man "einfach nur" die Ausgaberoutine, welche die Grafikdaten zum LCD Display sendet, entfernen und durch eine Multiplexroutine für die LED Anzeige ersetzen. Diese Routine könnte recht einfach in einem Timerinterrupt zyklisch ablaufen. Wenn der Mikrocontroller das Multiplexen (+Treiben) der Anzeige nicht machen soll, kann man natürlich auch sowas wie den HT16K33 verwenden, dann sendet man die Daten einfach via I2C. Falls man im SRAM Platzprobleme hat, kann man das Array dür den Grafikspeicher natürlich verkleinern.

Ob es sinnvoll ist, die bestehenden Features (siehe Beschreibung in meinem Blog) mit einem kleinen 8x8 LED Array zu verwenden, ist eine andere Sache, aber vielleicht wird die LED-Anzeige ja auch mal größer ;-)

Dirk :ciao:

IMG_0459.jpg
 
Hallo Dirk,

Das hört sich sehr gut an. Wo finde ich denn die "Routine" ?
Ist sie in der Display.h ?

Buchstaben oder Zahlen ausgeben würde mit der Routine auch kein Problem darstellen? Wie würde das funktionieren?
 
Hallo Janiiix,
Wo finde ich denn die "Routine" ?
Ist sie in der Display.h ?

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.

Um das Multiplexen musst du dich selber kümmern. Also einen Timer mit Interrupt nehmen und in der TimerISR die benötigten Daten aus dem Grafikspeicher lesen, Zeilen adressieren, Spalten adressieren, einen Spalten- bzw. Zeilenzähler verwenden. Leider habe ich keine Zeit, um dir eine Lösung für dein Projekt zu schreiben.

Buchstaben oder Zahlen ausgeben würde mit der Routine auch kein Problem darstellen? Wie würde das funktionieren?

Ich habe doch fertige Grafikroutinen, diese "schreiben" in den Grafikspeicher. Du kannst u.a. natürlich Buchstaben und Zahlen ausgeben. Im Blog sind alle Grafikroutinen aufgeführt. Im Projekt werden alle Grafikroutinen angewandt, siehe auch Video. Dein Display hätte eben im Moment nur eine Auflösung von 8x8 Pixel.

Dirk :ciao:
 

Ü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)