Hallo an alle,
ich bin gerade am einem Projekt dran, in dem eine RGB LED Matrix 8x8
mittels einem AT89C51ED und 4 Stk 74HC595 angesteuert werden soll.
Ich habe aber folgendes Problem:
Es werden nicht immer die richtigen Dots angezeigt... im Bild ist ein
Beispiel zu sehen, es soltle ein U sein, aber die Puntke rechts dürften
nicht da sein...
Der Code ist im Anhang, vielleicht kann sich den jemand angucken?
Aufbau selber:
AT89C51ED2 Testboard von ETT, die Schieberegister sind extern aufgebaut,
20 cm Kabel...
OE und SCLR sind auf 0V, bzw 5V.
Gibt es noch was, auf das man bei so etwas besonders achten muss?
Konstruktive Kritik zum Quellcode nehme ich gerne an ;) das letzte Mal
Programmieren ist schon ne weile her.
Gruß
Arne Pfäffle
Puh,
Das ist der komplexeste, unübersichtlichste Matrix Code, den ich je
gesehen habe.
So macht man das nicht.
Wie macht man das richtig?
Es gibt ein (in deinem Fall drei) globales Array (für jede Farbe eines),
welches als dein Bildspeicher fungiert.
Im Timer-Interrupt wird EINE Zeile dieses Arrays (für jede Farbe)
ausgegeben. Beim nächsten Interrupt kommt dann die nächste Zeile drann,
etc. etc. Immer reihum, bei jedem Interrupt die jeweils nächste Zeile.
Was du dadurch erreicht hast ist, dass du eine Art 'Bildschirmspeicher'
im Programm hast, in dem du Bits setzen und löschen kannst, und zwar so
wie es die Programmerfordernisse vorsehen, ohne dass sich das weitere
Programm darum kümmern muss, wie sich das jetzt in die tatsächlich
Ansteuerung der physikalischen Matrix niederschlägt. Diesen letzten
Punkt erledigt die Timer-Interrupt Funktion ganz von alleine und
eigenständig.
Das ist der grundsätzliche Aufbau, wie man eine gemultiplexte Anzeige
realisiert: Man hat im Programm eine globale Speicherfläche, die für das
restliche Programm 'die Ausgabe' darstellt und eine Interrupt Funktion
kümmert sich darum, dass bei jedem Interrupt die nächste Teilinformation
auf der physikalischen Anzeige realisiert wird. Immer reihum.
Und noch was.
Gewöhn dir Datentypen wie char, int und dergleichen ab.
char: Es gibt 3 Character Datentypen
* signed char (oder auch int8_t)
Wird verwendet, wenn man kleine Zahlen mit Vorzeichen braucht
* unsigned char (oder auch uint8_t)
Wird verwendet, wenn man kleine Zahlen ohne Vorzeichen braucht. Das ist
genau der Datentyp, der einem Byte entspricht
* char
Wird verwendet, wenn man es mit Textverarbeitung zu tun hat
int kannst du zwar verwenden, aber auch hier ist die Verwendung von
int16_t bzw. uint16_t besser, weil im Datentyp schon die Anzahl der Bits
unmissverständlich ausgedrückt wird. Denn: Du willst deinem µC nicht
mehrarbeit für nichts auflasten. Wenn eine Schleife 8 mal durchlaufen
wird, dann wäre es töricht, dafür 16 Bit Arithmetik vorzuschreiben.
Gewöhn dir solche Dinge
for( i = 0; i <= 7; i++ )
gleich wieder ab. Die Schleife lautet
for( i = 0; i < 8; i++ )
Diese Schleife wird 8 mal durchlaufen. Drum steht auch die 8 im Code!
Jede For-Schleife, die nicht dem Muster folgt
for( variable = 0; variable < Anzahl_Wiederholungen; variable++ )
ist erst mal suspekt und muss genauer untersucht werden. Enstpricht eine
for-Schleife aber diesem Muster (variable auf 0 setzen, vergleich
mittels kleiner auf die Anzahl, Erhöhung durch Inkrement), dann ist das
eine normale Zählschleife, wobei die Anzahl der Wiederholungen direkt im
Code steht. 95% aller for-Schleifen sind nach diesem Muster und 99.9%
aller C Programmierer erkennen dieses Konstrukt nachts um halb 3 mit
Vollrausch auch als solches innerhalb einer Zehntel Sekunde. Nur wenn da
was anderes steht, wie zb kleiner/gleich, dann muss man hellwach sein.
mit einem globalen i, geht schon gleich gar nicht. Da muss man fragen:
Bist du des Wahnsinns fette Beute?
Derartige Zählvariablen sind immer lokale Variablen
1
voidAusgabe_14_seg(charzeichen1,charzeichen2)
2
{
3
intgesamt;
4
uint8_ti;// <--- dieses i existiert nur innerhalb dieser Funktion
5
6
gesamt=zeichen1;
7
8
seg2=0;
9
for(i=0;i<=16;i++)
10
{
denn wenn du solche Dinge mit globalen Variablen erledigst, dann läufst
du IMMER Gefahr, dass dir unabsichltlich dieses hier passiert
1
uint8_ti;
2
3
voidfoo()
4
{
5
for(i=0;i<8;i++)
6
bar();
7
}
8
9
voidbar()
10
{
11
for(i=0;i<8;i++)
12
mach_was;
13
}
der Aufruf der Funktion bar() zerstört dir das i in foo. Und das willst
du ganz sicher nicht. Ganz zu schweigen davon, dass du bei allem was
komplizierter als 10 Zeilen Code ist, den Überblick verlieren wirst,
welche Funktion welche globalen Variablen benutzt und verändert.
Variablen sollten grundsätzlich immer so lokal wie möglich sein! Aus
praktischen Gründen muss man auf einem µC manchmal davon abweichen. Aber
sicher nicht bei derartigen Hilfsvariablen, die man zum Beispiel für
Schleifenzähler in einer Funktion braucht.
sind doch die ersten 8 Bits, die reinzuschieben sind, die Zeilennummer
der Zeile, die leuchten soll.
Entweder bin ich fast blind oder ich sehe einfach nicht, wie das
überhaupt funktionieren soll.
Nö, ich bleibe dabei: Du hast dich da in der selbst erzeugten
Komplexität komplett verlaufen. Zurück an den Anfang und diesmal
ordentlich gemacht. Konzentrier dich erst mal nicht darauf, möglichst
viele verschiedene Muster erzeugen zu können, sondern auf einen
ordentlichen und sauber ausgeführten Multiplexer samt Bildspeicher. Dann
sind in weiterer Folge dann auch die diversen Muster nur noch ein
Kinderspiel. So wie das jetzt gemacht ist, ist das alles nicht
tragfähig.
steht, werden dann beim Aufruf von den 16 Bits des Aufrufers erst mal
(wegen char) die oberen 8 Bits gestrippt, nur um dann in der Funktion
die 8 Bit wieder auf 16 aufzublasen.
Schmeiss den Code weg und fang von vorne an. Das taugt alles nichts. Das
ist zu unsystematisch und von Anfang an schon mal grundsätzlich der
komplett falsche Ansatz für eine Matrix. Wenn dein Code an allen Ecken
und Enden mit Zugriffen auf die Schieberegister-Portpins gespickt ist,
dann weißt du, das das alles nichts taugt. Es gibt nur eine einzige
Funktion, die an diesen Pins rumspielen darf (ausser der
Initialisierung) und das ist die Interrupt-Funktion, die reihum Zeile
für Zeile ausgibt.
Pseudocode:
1
// es gibt 8 Zeilen, daher sind edie Arrays jeweils mit 8 dimensioniert
2
uint8_t Zeile_red[8] = 0; // Jedes Bit steht für 1 LED
3
uint8_t Zeile_grn[8] = 0; // Jedes Bit steht für 1 LED
4
uint8_t Zeile_blu[8] = 0; // Jedes Bit steht für 1 LED
... bringe sie jetzt dazu, dass sie ihren Inhalt auch tatsächlich
59
... ausgeben, indem RCK getoggelt wird
60
}
das ist im Prinzip der ganze Multiplex. (ohne die beiden
Hilfsfunktionen). Eine LED einschalten bedeutet dann nichts anderes, als
das man das richtige Bit in den Arrays Zeile_red bzw. Zeile_grn bzw.
Zeile_blu auf 1 bzw auf 0 setzt.
Um also im Hautpprogramm die rote Led in der 2.ten Zeile in der Spalte 5
einzuschalten, muss man in Zeile_red[1] das 5.te Bit auf 1 setzen.
1
Zeile_red[1] |= ( 1 << 4 );
und dann muss auch schon die rote LED an besagter Position aufleuchten.
Der Multiplexcode in der Interrupt Routine sorgt dafür, dass besagte LED
dann auch aufleuchtet, wenn diese Zeile drann ist ausgegeben zu werden.
Das Hauptprogramm muss sich nicht mehr darum kümmern. Das setzt einfach
nur die Bits in den 3 Arrays auf 1 oder 0, je nachdem wie sie stehen
müssen, damit sich das beabsichtigte Bild ergibt. Mit der eigentlichen
Ausgabe hat das Hauptprogramm nichts mehr zu tun.
@ Karl Heinz Buchegger (kbuchegg) (Moderator)
>Das ist der komplexeste, unübersichtlichste Matrix Code, den ich je>gesehen habe.
Das ist nur logisch, denn der Code der "Matrix" ist verschlüsselt ;-)
@Karl:
ok, ich werd mich mal da dran machen...
das mit der 14 seg funktion ist grad mal auf eis gelegt, also kann
ignoriert werden...
gibt es da n kleines beispiel zum denk anstoß?
Plan ist grad in Arbeit...
Karl Heinz Buchegger schrieb:> Arne Pfäffle schrieb:>>> gibt es da n kleines beispiel zum denk anstoß?>> siehe 2 Postings über deinem letzten
grad gesehen, danke ;)
wie gesagt, die 14_segment funktion fliegt aktuell raus...
hatte das nur noch im code drin...
Arne Pfäffle schrieb:> wie gesagt, die 14_segment funktion fliegt aktuell raus...> hatte das nur noch im code drin...
Nicht 'fliegt raus'
Fang bei 0 an!
Dieses "Ich hab da so ähnlichen Code, den nehm ich erst mal als
Grundlage und ändere ihn", ist Quatsch.
Eine Matrix ist was anderes wie eine Reihe von Segment-Anzeigen. Also
ist das auch gerechtfertigt, da einen neuen Code anzufangen. Das man
sich aus anderen Programmen Codeabschnitte rüberkopieren kann, wenn man
sie brauchen kann, steht ausser Frage. Aber grundsätzlich ist das erst
mal ein komplett neues Programm. Vor allen Dingen dann, wenn du andere
um Hilfe bittest. Denn nichts ist unangenehmer, wenn andere dann auch
noch erst mal rausfinden müssen, welcher Code eigentlich alt ist und
eigentlich gar nicht dazugehört.
ok code ist weg...
eine Frage aber noch:
*** WARNING C233 IN LINE 145 OF C:\8051\RGB.c : using absolute registers
together with interrupts can cause register corruptions (mixup of
register banks)
Das kommt beim Timer 0 beim compilieren... woran köntne das liegen?
@ Arne Pfäffle (ob3lix)
>*** WARNING C233 IN LINE 145 OF C:\8051\RGB.c : using absolute registers>together with interrupts can cause register corruptions (mixup of>register banks)
Sieht nach einem Compilerproblem aus. Oder dort ist unsachgemäßer
Inlineassembler drin.