Hallo,
ich habe mir die Schaltung von den beiden Screenshots (soll auf 2 kleine
Platinen, deswegen geteilt) auf etwas Loch- und Streifenrasterplatine
aufgebaut.
Angeschlossen ist das an einen Arduino. Zeilentreiber ist ein PCF8574 +
8xBC327 und Spaltentreiber ist ein TLC5940.
Eigentlich sollte ja ein "V" auf der Matrix angezeigt werden, in der
Realität sieht es aber so aus wie auf dem 3. Bild.
An dem TLC5940 sollte es nicht liegen, der ist ja für sowas gemacht.
Bleibt also noch der PCF8574 oder mein Code.
Kann es sein dass der PCF8574 mit den BC327 zu langsam ist?
Oder hab ich einen Denkfehler im Code?
Mein Code:
Hi,
auch wenn ich nur raten kann was Du in #include "Tlc5940.h"
versteckst, geh ich mal davon aus das sich da auch keine, über einen
Timer geregelte Ansteuerung der Matrix verbirgt!? Stichwort Interrupt.
Ohne wirst Du nicht wirklich mit der Anzeige glücklich werden.
Helligkeitsschwankungen...
Aber erstmal sollte es helfen wenn Du die Pause (5ms ?!) nach
Tlc.clear(), vor Tlc.update() machen würdest :)
Ein paar Widerstände von den Basen zu den Emittern der BC327 könnten der
Schaltgeschwindigkeit zuträglich sein, Richtwert 1k...2k. Kleine
Anmerkung: Warum steuert der ATMEGA die Zeilentransistoren nicht direkt?
Und warum nimmst Du kein SPI oder UART im SPI-Mode für den
Spaltentreiber?
Knut Ballhause schrieb:> Ein paar Widerstände von den Basen zu den Emittern der BC327 könnten der> Schaltgeschwindigkeit zuträglich sein, Richtwert 1k...2k.
Der PCF kann in positiver Richtung nur 100uA treiben, da könnte so ein
Pullup schon zum schnelleren Abschalten helfen...
Allerdings sollte während des Acknowledge ein zusätzlicher Transistor
mit 1mA nachhelfen:
1
IOH P port min.30 max.300 µA
2
IOHT P-port transient pullup current High during acknowledge –1 mA
Manfred John schrieb:> Hi,>> auch wenn ich nur raten kann was Du in #include "Tlc5940.h"> versteckst
Ist eine fertige Library gewesen.
> geh ich mal davon aus das sich da auch keine, über einen> Timer geregelte Ansteuerung der Matrix verbirgt!? Stichwort Interrupt.
Zumindest nicht um die Spalten durchzuschalten.
> Aber erstmal sollte es helfen wenn Du die Pause (5ms ?!) nach> Tlc.clear(), vor Tlc.update() machen würdest :)
Schon versucht, brachte leider kein erfolg :/
Ich weiß das 5ms zu viel sind, das war auch nur zum Testen obs überhaupt
läuft.
Werner schrieb:> Einen Abblockkondensatoren über der Versorgung hat keiner deiner> Treiber?
Nein, welche Kapazität nimmt man denn für die ICs? Hätte hier z.B. noch
0,047µ oder 0,1µ sind die Ok?
Knut Ballhause schrieb:> Anmerkung: Warum steuert der ATMEGA die Zeilentransistoren nicht direkt?> Und warum nimmst Du kein SPI oder UART im SPI-Mode für den> Spaltentreiber?
Warum ist die Banane krumm?
Ich bin neu "im Geschäft" und kann leider nicht alles kennen.
Und der ATMega steuert die Transistoren nicht direkt, weil die Matrix
evtl. noch größer werden soll wenn das mal läuft. Dann wirds etwas knapp
mit den Pins. Wenn nicht wirds vielleicht auch ein kleinerer AVR, hab
den 328p nur genommen weil der auch im Arduino steckt.
Das mit den Basis - Emitter Widerständen werd ich evtl. mal versuchen.
Lothar Miller schrieb:> Allerdings sollte während des Acknowledge ein zusätzlicher Transistor> mit 1mA nachhelfen:IOH P port min.30 max.300 µA> IOHT P-port transient pullup current High during acknowledge –1 mA
Ich versteh nur Bahnhof :p
> @ loco> Was passiert, wenn du das mal umdrehst: sendByte(EXPANDER, rows[i]);> Tlc.update();
Ändert leider nichts, war auch mein erster Gedanke.
Aber beim weiteren ausprobieren hat das hier geholfen:
1
Tlc.update();
2
delay(1);
3
sendByte(EXPANDER,rows[i]);
Ich sehe auf der Matrix jetzt ein sauberes "V".
Bin mal gespannt ob das Delay auch im weiteren Verlauf hilft.
Wenn nicht muss ich das mit den Widerständen ausprobieren.
Vielen dank fürs erste, würde mich aber trotzdem noch über
Verbesserungsvorschläge freuen.
Grüße
loco
loco schrieb:> Nein, welche Kapazität nimmt man denn für die ICs? Hätte hier z.B. noch> 0,047µ oder 0,1µ sind die Ok?
Der 0,1µ ist passend. Was für eine Art von Kondensator hast da?
loco schrieb:> Ich bin neu "im Geschäft" und kann leider nicht alles kennen.> Und der ATMega steuert die Transistoren nicht direkt, weil die Matrix> evtl. noch größer werden soll wenn das mal läuft. Dann wirds etwas knapp> mit den Pins. Wenn nicht wirds vielleicht auch ein kleinerer AVR, hab> den 328p nur genommen weil der auch im Arduino steckt.
Kein Problem. i2c ist schon nicht schlecht. Wenn du aber noch erweitern
willst, dann kannst mit deiner 100kHz Begrenzung evtl. Probleme
bekommen. Die meisten benutzen daher Schieberegister zur Ansteuerung.
Hier in der Artikelsammlung gibt es was dazu. Da wird der 74HC595 (hoffe
ich) benutzt. Der ist wesentlich billiger und ebenfalls sehr leicht
anzusteuern.
loco schrieb:> Aber beim weiteren ausprobieren hat das hier geholfen:Tlc.update();> delay(1);> sendByte(EXPANDER, rows[i]);Ich sehe auf der Matrix jetzt ein sauberes "V".> Bin mal gespannt ob das Delay auch im weiteren Verlauf hilft.> Wenn nicht muss ich das mit den Widerständen ausprobieren.
Im Endeffekt musst du mit Timer arbeiten. Selbst wenn du die Widerstände
dazu baust wirst du mit den delays sehr wahrscheinlich nicht glücklich.
mr. mo schrieb:> Der 0,1µ ist passend. Was für eine Art von Kondensator hast da?
Sind Tantalperlen.
> Kein Problem. i2c ist schon nicht schlecht. Wenn du aber noch erweitern> willst, dann kannst mit deiner 100kHz Begrenzung evtl. Probleme> bekommen. Die meisten benutzen daher Schieberegister zur Ansteuerung.> Hier in der Artikelsammlung gibt es was dazu. Da wird der 74HC595 (hoffe> ich) benutzt. Der ist wesentlich billiger und ebenfalls sehr leicht> anzusteuern.
Der 74HC595 ist in der Tat wesentlich billiger. Ist nur schade um die
ganze Arbeit. Aber das lässt sich beim Basteln wohl nur schwer vermeiden
(zumindest wenn man das nur Hobbymäßig macht).
Wie schnell muss sowas denn sein, wenn 100KHz evtl. zu langsam sind?
> Im Endeffekt musst du mit Timer arbeiten. Selbst wenn du die Widerstände> dazu baust wirst du mit den delays sehr wahrscheinlich nicht glücklich.
Ich werde auf jeden Fall Interrupts ausprobieren, muss mir dazu aber
erst mal ein Konzept überlegen und mehr zu dem Thema lesen.
Der BC327 kann nur 0,5A, der TLC aber 120mA * 16 = 1,9A.
Transistoren dimensioniert man nicht unter, sondern reichlich über.
Für die 2A z.B. einen 5A-Typ.
Heutzutage nimmt man aber besser P-FETs.
Ein Treiber über SPI, der andere über I2C ist nicht der Brüller.
Nimm besser den 74HC164 anstelle des PCF.
Aber Dein Hauptproblem wird sein, daß Du keinen Timerinterrupt nimmst.
Peter
loco schrieb:> mr. mo schrieb:>> Der 0,1µ ist passend. Was für eine Art von Kondensator hast da?> Sind Tantalperlen.
Tantal sind glaube ich nicht als Abblockkondensator geeignet. Bin mir
aber nicht sicher.
Ein Keramikkondensator ist auf jeden Fall die bessere Wahl. Aber wenn es
bis dahin schonmal ganz gut funktioniert hat, dann werden die erstmal
nicht Fehlen. Sollten aber auf jeden Fall nachgerüstet werden, damit
deine Schaltung auch zuverlässig ist/bleibt.
> Der 74HC595 ist in der Tat wesentlich billiger. Ist nur schade um die> ganze Arbeit. Aber das lässt sich beim Basteln wohl nur schwer vermeiden> (zumindest wenn man das nur Hobbymäßig macht).
Kenn ich. Lässt sich im Hobby oft schlecht vermeiden. Aber man lernt
etwas dabei :)
> Wie schnell muss sowas denn sein, wenn 100KHz evtl. zu langsam sind?
Kommt auf ein paar Faktoren an. Die Anzahl der Anzeigen. Die
Geschwindigkeit der Ansteuerung, sodass das Auge noch ein Bild sieht.
usw.
Ich will aber nicht sagen, dass die 100kHz langsam sind. Kann nur unter
umständen bisschen eng werden.
Du sprichst deinen Port Expander ja auch ständig neu an. Das kostet auch
immer etwas Zeit bis das ACK kommt und unterbricht dein Programm falls
du da mit einem while() auf das ACK wartest. Dadurch bekommst du als
Nebeneffekt ein delay dazu.
Du könntest den ja auch einmal zum Start des Programms ansprechen und
die "Verbindung" aufrecht erhalten. Dann musst du z.B. nicht immer auf
das ACK warten und die Performance ist schon etwas verbessert.
Peter Dannegger schrieb:> Der BC327 kann nur 0,5A, der TLC aber 120mA * 16 = 1,9A.> Transistoren dimensioniert man nicht unter, sondern reichlich über.
Der TLC ist auf 20mA eingestellt. Wird alles über USB versorgt, deswegen
reichen die BC327 für max. 16*20mA auch aus.
> Nimm besser den 74HC164 anstelle des PCF.>> Aber Dein Hauptproblem wird sein, daß Du keinen Timerinterrupt nimmst.
Siehe mein vorheriger Beitrag.
loco schrieb:> int pattern[8][16] = {> {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},> {0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0},> {0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0},> {0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0},> {0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0},> {0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0},> {0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0},> {0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0}};
Das sind schon 256Byte.
Warum speicherst Du jedes Bit als 16Bit-Wert?
Man muß Speicher nicht sinnlos verschwenden.
Speichere als uint8_t und dann je Byte immer 8Bit zusammen. Dann mußt Du
sie zur Ausgabe auch nicht erst umständlich zusammen fügen.
Peter
@Locus:
rows[] = {B11111110, B11111101, B11111011, B11110111, B11101111,
B11011111, B10111111, B01111111};
Schau dir auch mal den Befehl ">>" und "<<" in c an.
Gruß,
JJ
Jens schrieb:> Schau dir auch mal den Befehl ">>" und "<<" in c an.
1
sendByte(EXPANDER,~(1<<counter));
@Jens: mein Nick ist übrigens loco, nicht Locus :p
Das mit den Widerständen funktioniert nicht so wie ich gehofft habe.
Habe jetzt weiterhin das delay(1) zwischen Spalten und Zeilen update und
kein Basis-Emitter Transistor.
mr. mo schrieb:> Du könntest den ja auch einmal zum Start des Programms ansprechen und> die "Verbindung" aufrecht erhalten. Dann musst du z.B. nicht immer auf> das ACK warten und die Performance ist schon etwas verbessert.
Mein Code um den Expander anzusprechen sieht so aus
1
voidsetup(){
2
Wire.begin()
3
}
4
5
voidsendByte(
6
Wire.beginTransmission();
7
Wire.Write();
8
Wire.endTransmission();
9
}
Ich habe versucht im Setup noch Wire.beginTransmission aufzurufen, den
Expander dann nur noch mit Wire.Write anzusprechen und
Wire.endTransmission weg zu lassen. Das funktioniert leider gar nicht,
der PCF scheint die Ausgänge erst zu schalten wenn Wire.endTransmission
aufgerufen wird.
Hab außerdem noch versucht den Timer1 Interrupt zu benutzen, was leider
nicht funktioniert weil der schon von der TLC Library benutzt wird.
Habs jetzt so gelöst:
1
if((long)(micros()-timerDuration)>=0){
2
timer_Tick();
3
timerDuration+=100;
4
}
Helligkeitsunterschiede gibts nicht mehr, also scheint das ausreichend
zu sein.
Das Geisterbild hatte ich auch schonmal bei einer LED-Matrix.
Bei mir lag es an der falschan Ansteuerung, weil ich die Spalten-LEDs
vor dem Wechsel zur nächsten Zeile nicht ausgeschaltet hatte.
Falsche Programmschleife:
Zeilentreiber weiterschalten
Spalten-LEDs für die Zeile Ausgeben
Warten
Richtig:
Zeilentreiber weiterschalten
Spalten-LEDs für die Zeile Ausgeben
Warten
Spalten-LEDs auschalten
Ich multiplexe die Zeilen, also ist mein Ablauf so:
1. Zeilen ausschalten
2. Muster für Zeile X an Spalten anlegen
3. Zeile X einschalten
4. Warten
Zusätzlich warte ich noch 1ms zwischen 2. und 3.
Die Bilder zeigen den Vergleich mit und ohne dem 1ms Delay.
@loco (Gast)
>Zusätzlich warte ich noch 1ms zwischen 2. und 3.>Die Bilder zeigen den Vergleich mit und ohne dem 1ms Delay.
Dann sind deine Treiber oberfaul. 5µs muss man auf einen schlechten
Treiber warten, 1ms auf einen defekten. Oder du hast einen Bug in deiner
Software.
@ oog (Gast)
>Richtig:> Zeilentreiber weiterschalten> Spalten-LEDs für die Zeile Ausgeben> Warten> Spalten-LEDs auschalten
Du meinst vielleicht das Richtige, aber deine Darstellung ist ungünstig.
Denn praktisch macht man Multiplexen per Timer-Interrupt. Der sieht dann
so aus
Timer-Interrupt_1kHz {
Alle Zeilen ausschalten
Neues Spaltenmuster berechnen
Neues Spaltenmuster ausgeben
Neue Zeile einschalten
}
Da muss man auch gar nicht explizit warten, die Berechungen der neuen
Muster, auch wenn es nur ein einfacher Array-Zugriff ist, reichen aus,
um zwischen zwei Digits mit Lücke umzuschalten, währenddessen alles aus
ist.
MFG
Falk
loco schrieb:> Ich multiplexe die Zeilen, also ist mein Ablauf so:>> 1. Zeilen ausschalten> 2. Muster für Zeile X an Spalten anlegen> 3. Zeile X einschalten> 4. Warten>> Zusätzlich warte ich noch 1ms zwischen 2. und 3.> Die Bilder zeigen den Vergleich mit und ohne dem 1ms Delay.
Bild1
Warum verteilt sich der Strom bei Dir auf alle LEDs der Zeile so
offensichtlich ?
Falk Brunner schrieb:> Dann sind deine Treiber oberfaul. 5µs muss man auf einen schlechten> Treiber warten, 1ms auf einen defekten. Oder du hast einen Bug in deiner> Software.
Ich werd mal nen anderen Treiber einsetzen, hab noch welche vom gleichen
Typ da.
Ich glaube nicht das es am Code liegt, aber wenns dir nichts ausmacht
mal drüber zu schauen: http://pastebin.com/nWP2B1FrDennis Heynlein schrieb:> Warum verteilt sich der Strom bei Dir auf alle LEDs der Zeile so> offensichtlich ?
Sieht nur auf dem Foto so aus.
loco schrieb:> Ich multiplexe die Zeilen, also ist mein Ablauf so:
Was mich an den Bildern arg stutzig macht, ist, dass offenbar bei dem
"verwaschenen" Bild nur die Hälfte des Zeichens draufpasst...
Wird evtl. der Timer viel zu schnell aufgerufen?
timerDuration = micros() + 100;
Schreib da mal 2000 statt 100 hin.
Denn nichts anderes macht dein delay(1)...
loco schrieb:> Ich glaube nicht das es am Code liegt, aber wenns dir nichts ausmacht> mal drüber zu schauen: http://pastebin.com/nWP2B1Fr
Code bitte einfach als Dateianhang mit Endung *.c hier posten.
loco schrieb:> Hab außerdem noch versucht den Timer1 Interrupt zu benutzen, was leider> nicht funktioniert weil der schon von der TLC Library benutzt wird.
Moment.
Wozu braucht die einen Timer und einen Interrupt?
Wenn das so funktioniert, wie ich (anhand deiner Beschreibung) denke
dass es funktioniert, dann hast du demnach keine Garanetie, dass hier
sendByte(EXPANDER, 255);
Tlc.clear();
for(int j = 0; j <= 15; j++)
{
if(pattern[i][j] == 1)
{
Tlc.set(j, 4095);
}
}
Tlc.update(); // <--------------------------- ********
sendByte(EXPANDER, rows[i]);
nach dem Aufruf von Tlc.update() der TLC bereits das neue Muster
ausgibt. Dann darfst du dich allerdings nicht wundern, dass du Ghosting
hast. Wenn du die entsprechende Zeile einschaltest, dann MUSS vom TLC
schon das richtige Muster für diese Zeile kommen. Nicht timergesteuert
irgendwann später, sondern der Vorgang MUSS vor dem sendByte
abgeschlossen sein! Spätestens wenn du die Zeile einschaltest, müssen
die Ausgänge des TLC bereits das Muster für diese Zeile ausgeben.
> Ich glaube nicht das es am Code liegt
Es liegt ziemlich sicher am Code. Es leigt eigentlich meistens am Code.
Die zeitliche Reihenfolge der Aktionen stimmt nicht.
Da du allerdings Fremdlibraries im Einsatz hast, von denen hier keiner
genau weiß, wie sie konkret arbeiten, lassen sich die Nebenbedingungen
nicht einwandfrei einschätzen.
Also: Wie GENAU funktioniert diese TLC Lib?
Lothar Miller schrieb:> Wird evtl. der Timer viel zu schnell aufgerufen?> timerDuration = micros() + 100;> Schreib da mal 2000 statt 100 hin.> Denn nichts anderes macht dein delay(1)...
Wenn ich 2ms bis zum nächsten aufruf warte, fängt die Matrix an zu
flimmern.
das delay(1) ist ja so nicht geplant, aber ohne funktionierts leider
nicht.
Karl Heinz Buchegger schrieb:> Moment.> Wozu braucht die einen Timer und einen Interrupt?
Wenn ich das richtig verstanden habe, benutzt die Library den Timer um
das GSCLK Signal (PWM Steuerung des Treibers) zu erzeugen.
loco schrieb:> Karl Heinz Buchegger schrieb:>> Moment.>> Wozu braucht die einen Timer und einen Interrupt?> Wenn ich das richtig verstanden habe, benutzt die Library den Timer um> das GSCLK Signal (PWM Steuerung des Treibers) zu erzeugen.
War da kein Beispiel dabei?
AUs dem Source
1
/** Shifts in the data from the grayscale data array, #tlc_GSData.
2
If data has already been shifted in this grayscale cycle, another call to
3
update() will immediately return 1 without shifting in the new data. To
4
ensure that a call to update() does shift in new data, use
5
\code while(Tlc.update()); \endcode
6
or
7
\code while(tlc_needXLAT); \endcode
8
\returns 1 if there is data waiting to be latched, 0 if data was
// erst jetzt ist sicher gestellt, dass der TLC tatsächlich
18
// die Ausgangspins umgeschaltet hat
19
20
sendByte(EXPANDER,rows[i]);
(Ich hab immer noch deinen ursprünglichen Code. Musst du eben in deinen
jetzigen Code einbauen. Der springende Punkt: du musst so lange update()
aufrufen, bis da keine 1 mehr zurückkommt. Das ist dein Zeichen, dass
das raustakten beendet ist.
Diese TLC Lib verbraucht 2(!) Timer. Bist du sicher, dass du das
benutzen willst?
1 Timer hast du noch übrig um daran das Multiplexing aufzuhängen. Wobei
das gar nicht so einfach ist, denn diese TLC Lib benötigt Interrupts,
damit update() die Freigabe kriegt um weiterzumachen. Das wird noch -
ähm - interessant.
Hmm.
Alternativ gibt es dann noch die Möglichkeit, sich an den
Funktionspointer tlc_onUpdateFinished zu hängen. Eine entsprechende
Funktion wird dann aufgerufen, wenn der TLC seinen XLAT bekommen hat.
D.h. Das aktivieren der Row kann in eine derartige Funktion verlagert
werden. Dann muss nach dem update() nicht gewartet werden.
/* Tlc.update() sends the data to the TLCs. This is when the LEDs
2
> will
3
> actually change. */
4
>Tlc.update();
Nicht ganz.
UPdate taktet zwar die Daten an den TLC raus. Aber aktiv, in dem Sinne,
dass die LED dann auch den Zustand einnehmen, werden diese Daten erst,
wenn der TLC seinen XLAT Puls bekommen hat. Und den kriegt er
zeitverzögert. D.h. unmittelbar nach dem Aufruf von update() ist NICHT
garantiert, dass die LED-AUsgänge des TLC schon den richtigen Zustand
haben.
Insofern ist dieser Kommentar aus dem Source Code missverständlich!
Normalerweise macht das auch keinen Unterschied. Bei 20 Zustandsleds
spielt es keine Rolle, wenn die erst ein paar µs nach dem update() zu
leuchten beginnen.
Es sei denn natürlich, man will eine Matrix multiplexen und muss laufend
die TLC Ausgänge umstellen. Dann ist alles anders.