Hallo Forengemeinde,
Mit untenstehenden Code versuche ich mein 2-stelliges Display
anzusteuern. Inhaltlich stimmt alles. Ziffern von 00 - 99 werden richtig
angezeigt. Aber die Ziffer der Einerstelle ist auch leicht (ca. 10%
Helligkeit) auf der 10er Stelle zu sehen, und andersrum.
Es stimmt irgendwie das Timing meiner Routine nicht. Habe versucht,
durch tauschen der Port-Ausgabezeilen erst die andere auszuschalten, und
dann die andere einzuschalten. Aber es ändert sich hierbei nichts.
Die Frequenz ist im Timer1 so eingestellt:
1
//zweite Möglichkeit einen Portpin zu toggeln:
2
if(blinkmerker) blinkmerker=0;
3
else blinkmerker=1;
4
// Die Auswertung von "blinkmerker" wird im Hauptprogramm vorgenommen
5
6
TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen,
7
// da der Timerroutine bei einem Überlauf aufgerufen wird
8
display_ansteuern();
9
10
11
//*********************************
12
void display_ansteuern(void)
13
{
14
// Wenn "blinkmerker" =1 dann wird der PortB1 gesetzt, ansonsten wird er zurückgesetzt.
15
if (blinkmerker)
16
{
17
//PORTB &=~_BV(6);
18
PORTD |= _BV(6);
19
PORTD &=~_BV(7);
20
PORTB = array[(counter % 10)]; // Einerstelle von 'counter' ausgeben
Thomas S. schrieb:> if (counter>0)
Sicher dass das nicht "counter > 9" sein soll?
Dein Programm gibt die Zehnerstelle aktuell auch als Einerstelle aus.
BTW: Man kann auch Dateien als Anhang einstellen. Und dann VOLLSTÄNDIG.
/regards
Andreas H. schrieb:> Sicher dass das nicht "counter > 9" sein soll?
Naja, ich möchte halt die führende Null auch ausgeben. Bei Anzeige Null
war bis hierher dann eben nur eine Null. Aber dann ist dieses Digit dann
heller, weil er das 10er Dgit nicht ansteuern muss. Ich muss glaub ich
auch wenn nix am Display stehen soll das Bitmuster 'leer' einblenden.
Aber das später.
Es ändert hier erstmal nichts um die Doppel-Zahl mit 10% Intensität zu
lösen.
Thomas S. schrieb:> Mit untenstehenden Code versuche ich (...)
Viel ist davon nicht zu sehen, aber oft vergessen Leute das 'volatile'
bei Variablen, wenn diese anschließend in Interruptroutinen und
Hauptprogramm verwendet werden müssen.
Thomas S. schrieb:> Es ändert hier erstmal nichts um die Doppel-Zahl mit 10% Intensität zu> lösen.
Dann bau doch mal einen Delay zwischen der PortD Umschaltung und dem
Ausgeben auf PortB ein. Evtl sind die PortD Pins noch nicht stabil
gesetzt.
/regards
Naja, Du mußt schon die Kausalität beachten.
Erst alle Digits dunkel, dann neues Pattern und dann das Digit an.
Oder Pattern dunkel, Digit wechseln, neues Pattern an. Die 3 Schritte
müssen die richtige Reihenfolge haben.
Du machst auch ständig teure Softwaredivisionen im Interrupt, das kostet
Zeit und solange ist noch das falsche Pattern an.
Man zerlegt den Wert nur einmal, wenn er sich geändert hat und legt die
Pattern im Bild-RAM ab. Der Interrupt muß dann nur das jeweilige Byte
aus dem RAM ausgeben.
Mach aus „blinkermerker“ statt true/false, 0 bis 5, zB.
If blinkermerker (lustiger Variablenname) 1 bis 4, dann mache nix, bis
auf d6 und d7 auf Low zu setzen. (Oder halt auf HIGH; so dass beide
Stellen „aus“ sind.) Man könnte auch bei blinkermerker&1 beide Stellen
inaktiv schalten.
Was der Atmega bei %10/10 zu rechnen hat, ist mehr, als man meint. Würde
ich mir auch noch mal ansehen und ggfls. ändern oder ausm interrupt in
die Main verlagern. Kann mich jetzt aber auch irren, weil der komplette
Code leider nicht vorliegt. Sonst könnte man sich das im Listing mal
ansehen.
Viel Spaß noch.
Ghosting ist das richtige Schlüsselwort. Das hat bei dir 3 Ursachen:
1) Du schaltest auf das andere Segment um, bevor dessen Wert auf Port B
ausgegeben wird. Lösung: Schalte zuerst beide Segmente aus, gebe dann
den Wert auf Port B aus, schalte dann das richtige Segment ein.
2) Der Effekt 1 wird durch die zeitaufwändige Division verstärkt.
Berechne den Wert vor der Ausgabe, dann spielt die benötigte Zeit keine
Rolle.
3) Die beiden Transistoren, mit denen du die gemeinsamen Anoden bzw.
Kathoden der 7-Segment anzeigen ansteuerst, brauchen Zeit zum
Abschalten. Vor allem dann, wenn sie wie üblich (und das ist kein
Fehler) übersteuert werden. Wenn du den Vorschlag zu 1 umsetzt, hast du
wahrscheinlich schon genug tot-zeit zwischen den beiden Segmenten.
Notfalls kannst du ein paar NOP Befehle einfügen.
Monk schrieb:> Die beiden Transistoren, mit> denen du die gemeinsamen Anoden bzw. Kathoden der 7-Segment anzeigen> ansteuerst brauchen Zeit zum abschalten.
Ohne den Schaltplan zu kennen, ist diese Behauptung gewagt. Bei mir
schalten die Transistoren schnell genug, da müssen keine zusätzlichen
Pausen eingefügt werden.
Ghosting liegt oft nur an einem falschen Programmablauf.
Peter D. schrieb:> Ohne den Schaltplan zu kennen, ist diese Behauptung gewagt
War nicht als Behauptung gemeint, sondern als mögliche Fehlerquelle.
Ohne Schaltplan habe ich tatsächlich geraten, wie die Schaltung aussehen
könnte.
Peter D. schrieb:> einfacher Schaltplan für 4 Digits.
Wenn man mit 1mcd mittlerer Helligkeit pro Segment auskommt, denn der
Segmentstrom beträgt auf Grund der 332 Widerstände nur 6mA und teilt
sich auf 4 Stellen, 100 Ohm für 20mA peak wären möglich ohne
Schaltungsänderung für diese low current Spezialdisplays.
Thomas S. schrieb:> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen
Nur mal zum Nachdenken: was ist 65536 in "16 Bit binär"?
- https://www.google.com/search?q=65536+binär> // da der Timerroutine bei einem Überlauf aufgerufen wird
Und zwar bei einem Überlauf nach 0. Also müsste da stehen:
1
TCNT1=0-15;
Oder wie es der Rest der Welt macht:
1
TCNT1=-15;
S. L. schrieb:>> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen ...> Wie hoch ist die Multiplex-Frequenz?
1 MHz, wenn der µC mit den üblichen 16MHz läuft und der Prescaler auf 1
sitzt. Das wäre um den Faktor 10000 zu hoch. Natürlich gibt es da
Ghosting.
Lothar M. schrieb:> S. L. schrieb:>>> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen ...>> Wie hoch ist die Multiplex-Frequenz?> 1 MHz, wenn der µC mit den üblichen 16MHz läuft und der Prescaler auf 1> sitzt.
Auch eine frisch geputze Glaskugel garantiert keine brauchbare
Information ;-)
> 1 MHz, wenn der µC mit den üblichen 16MHz läuft
16 Takte für Multiplexing ist wohl ausgeschlossen, zumal in C. Aber
meine Vermutung geht ebenfalls in Richtung
> ... zu hoch. Natürlich gibt es da Ghosting.
Dass nämlich dieses 'TCNT1 = 65536-15' in der ISR steht, nach der
Rückkehr aus dieser noch zwei oder drei Befehle im Hauptprogramm
ausgeführt werden und dann schon wieder der Interrupt erfolgt.
Lothar M. schrieb:> Oder wie es der Rest der Welt macht: TCNT1 = -15;
Nicht mal das. Der AVR ist kein oller 8051, bei dem das noch nötig war.
Man nutzt den CTC Modus, dann muss man gar nicht nachladen.
Mir ist schon klar, dass hier ein Autoreload-Modus sinnvoll wäre. Aber
darum ging es mir bei den Ausführungen zum Thema "16-Bit-Integer" nicht.
Sondern ums Verständnis, dass bei einem (unsigned) 16-Bit Integer eben
65536 exakt das Selbe ist wie 0. Und dass im Falle des Zählers hier eben
das 17. Bit genau das Overflow-Flag ist.
Hallo Forengemeine, Mitwirkende,
bin erst heim gekommen.
Habe mal den entsprechenden Teil-Plan hier angehängt, und auch den Code,
so er mom ist. Er erhebt noch keinen Anspruch auf fehlerfrei. Alles
noch Test und in der Entstehung.
Das mit erst dunkeltasten, Digit einlesen und wieder darstellen leuchtet
mir ein. Das der Atmega hier aber ein Päuschen braucht, hätte ich nicht
vermutet.
Thomas S. schrieb:> Das der Atmega hier aber ein Päuschen braucht, hätte ich nicht> vermutet.
Der brauch es nicht, aber die Anzeigen. Break before make! Man muss ja
auch erst eine Schallplatte anhalten, bevor man sie wechseln kann!
(Jaja, ein sehr anachronistisches Beispiel).
Habe gerade in den Fuses nachgeschaut. Der läuft auf default mit 1 MHz.
Mit der Einstellung im Timer nehme ich ein leichtes Flackern der Anzeige
war. Dies dürften so ca. <40 Hz sein.
Thomas S. schrieb:
> bin erst heim gekommen.> Habe mal den entsprechenden Teil-Plan hier angehängt, und auch den Code,> so er mom ist. Er erhebt noch keinen Anspruch auf fehlerfrei. Alles> noch Test und in der Entstehung.
Hier mal ein paar Anmerkungen.
Man muss nicht alle globalen Variablen als volatile deklarieren, nur
die, welche sowohl im Hauptprogramm und Interrupt verwendet werden.
Ein Array mit dem Namen array ist doof. Apfelmus ist Mus aus Äpfeln.
Deine Einrückungen der Klammern ist nicht sonderlich sinnvoll.
Nahezu jede Zeile mit einem Kommentar zu versehen ist Unsinn, selbst für
Anfänger und als Lehrbeispiel ist das fragwürdig.
Um Pulse per Software zu erkennen und auszuwerten, muss man MINDESTENS
eine Flankenerkennung einbauen. Wartepausen sind bääääh.
Kurze Funtionen schreibt man besser direkt in die ISR, das spart bei
sehr hohen ISR-Frequenzen wertvolle Takte. Hier ist das egal.
R2 und R4 sind mit 10k ein wenig zu hochohmig, da sollte man eher 1k
benutzen.
Eingänge entprellt man entweder in Software oder Hardware,siehe
Entprellung.
Thomas S. schrieb:> Hallo Forengemeine, Mitwirkende, bin erst heim gekommen.> Habe mal den entsprechenden Teil-Plan hier angehängt, und auch den Code,> so er mom ist. Er erhebt noch keinen Anspruch auf fehlerfrei. Alles> noch Test und in der Entstehung.
Ich habe leider nicht die Zeit, den ganzen Code bis ins Detail zu
analysieren, aber wenn sich am Ende herausstellt, dass es kein
Softwarefehler ist, dann könnte es auch ein Hardwarefehler sein oder
eine Kombination aus beidem. Ich hatte vor vielen Jahren jemandem
geholfen, eine Schaltung zu verbessern, und das sieht bei Dir so ähnlich
aus. Aber warten wir mal ab, wie weit Ihr kommt.
PS: wenn man irgendwann mal anfängt, „#define” im Code für immer
wiederkehrende Ausdrücke – für z.B. Tasterabfragen und Beeinflussung der
Portpins – zu benutzen, wird dieser um Welten leserlicher; für einen
selbst und auch für andere
Gregor J. schrieb:> wenn man irgendwann mal anfängt, „#define” im Code für immer> wiederkehrende Ausdrücke – für z.B. Tasterabfragen und Beeinflussung der> Portpins – zu benutzen, wird dieser um Welten leserlicher; für einen> selbst und auch für andere
Zugleich werden Fehlermeldungen schwerer lesbar. Lieber ganz normale
Funktionen schreiben. Wenn sie klein sind (z.B. Einzeiler) wandelt der
Compiler sie in kompakten inline Code um, verwendet in Fehlermeldungen
aber weiterhin den Namen der Funktion.
Monk schrieb:> Zugleich werden Fehlermeldungen schwerer lesbar.
Dann machst Du vermutlich etwas falsch – kann passieren.
__> Lieber ganz normale Funktionen schreiben.
Funktionen verbrauchen in der Regel wesentlich mehr Ressourcen als
Macros, das kann aber auch mal genau umgekehrt ausfallen – man sollte
Funktionen dort verwenden, wo sie sinnvoll sind, und analog dazu –
Macros dort einsetzen, wo sie Sinn machen. Zu wissen, wo was wann am
besten eingesetzt werden sollte, ist eine Kunst, die nicht jeder
beherrscht. Eine Rossource kann übrigens sowohl Speicher als auch Zeit
sein – je nachdem was gerade relevant ist.
__> Wenn sie klein sind (z.B. Einzeiler) wandelt der Compiler sie in> kompakten inline Code um, verwendet in Fehlermeldungen aber weiterhin den> Namen der Funktion.
Man sollte es nach Möglichkeit nicht dem Compiler überlassen, was als
Inlinecode und was als nicht-Inlinecode kompiliert/übersetzt wird – dazu
gibt es spezielle Methoden und Programmiereingriffe, ansonsten wird das
Produkt zu einem Zufallsprodukt. Dass das nicht jeder beherrscht, kann
ich aber gut nachvollziehen.
Gregor J. schrieb:> Funktionen verbrauchen in der Regel wesentlich mehr Ressourcen als> Macros, das kann aber auch mal genau umgekehrt ausfallen
So sehr das in kritischen Anwendungsfällen zutreffen mag, so vollkommen
irrelevant ist es hier. Es gibt keinen Grund, sich hier durch "premature
optimization" ein Bein zu stellen und das andere am Kühlschrank
festzukleben.
Harald K. schrieb:> Es gibt keinen Grund, sich hier durch "premature> optimization" ein Bein zu stellen und das andere am Kühlschrank> festzukleben.
Man sollte aber schon den Programmablauf durchdenken, ob Funktionen
unnötig oft ausgeführt werden. Das ständig neue Zerlegen einer Variablen
in Ziffern in jedem Interrupt gehört dazu.
In der Praxis wird das Ausgabeinterval oft sogar verlangsamt, so daß max
2..5 Änderungen je Sekunde angezeigt werden. Das ergibt dann eine ruhige
Anzeige und der Benutzer kann jeden Wert auch ablesen.
Harald K. schrieb:> So sehr das in kritischen Anwendungsfällen zutreffen mag, so vollkommen> irrelevant ist es hier. Es gibt keinen Grund, sich hier durch "premature> optimization" ein Bein zu stellen und das andere am Kühlschrank> festzukleben.
Das hat leider nichts mit kritischen Anwendungsfällen oder Optimierung
zu tun und relevant ist es leider immer – je früher man es lernt oder
zumindest damit schon mal in Kontakt kommt, was ich kurz erwähnt habe,
desto besser.
Gregor J. schrieb:> je früher man es lernt oder> zumindest damit schon mal in Kontakt kommt, desto besser.
Sehe ich nicht so. Man muss sich nicht verdrehen, um Code zu optimieren,
nur weil man davon ausgeht, daß der verwendete Compiler das nicht besser
kann, als es der Compiler, den man 1990 schon kennengelernt hat, konnte.
Das ganze erzeugt nur schlecht lesbaren und schlecht wartbaren Code, der
wiederum Anfängern das Erlernen erschwert.
Bevor man etwas optimiert, sollte man das Funktionsprinzip, den
Algorithmus verstanden haben. Dann kann man auch selbst beginnen, zu
verstehen, wann und wo eine Optimierung überhaupt nötig ist.
Dazu kommt, daß auch simple 8-Bit-Architekturen wie die hier verwendeten
AVRs um einiges schneller sind als das, was man in den 80ern mit den
ersten primitiven Compilern traktierte (6502/Z80 etc.), und andererseits
die Aufgaben oft viel simpler -- hier geht es um ein simples
7-Segment-Display, nicht um ein Computerspiel, das möglichst viel aus
der knappen Graphikhardware rausholen soll.
Peters Einwand mit dem Krempel, den man in einer ISR treibt bzw. nicht
treiben sollte, ist eine komplett andere Baustelle (und die würde sich
auch nicht bessern, wenn man den Compiler mit Macros totwerfen würde).
Harald K. schrieb:> um Code zu optimieren (...)
Wie ich bereits erwähnt habe, es geht nicht ums Optimieren etc, sondern
um eine gewisse Struktur des Codes zu erhalten – optimieren kann man
später, nachdem alles funktioniert, immer noch, wenn man will. Das ist
ein ganz anderer Vorgang.
Thomas S. schrieb:> Habe mal den entsprechenden Teil-Plan hier angehängt
R2 und R4 sind viel zu gross, AVCC sollte angeschlossen werden, 100nF
parallel zum NPN können den NPN killen.
Die Schaltung schaltet die Digits mit low durch also:
1
if(blinkmerker)
2
{
3
PORTD|=_BV(7);// 10er aus
4
PORTB=array[counter%10];
5
PORTD&=~_BV(6);// 1er an
6
}
7
else
8
{
9
PORTD|=_BV(6);// 1er aus
10
if(counter>=10)
11
PORTB=array[(counter/10)%10];
12
else
13
PORTB=0;// dunkel
14
PORTD&=~_BV(7);// 10er an
15
}
aber was gibt man sich Mühe dein Programm zu korrigieren, es ist dir
offenbar scheissegal was die Leute hier schreiben, du erwartest wohl
dass jemand kommt und dir eintippt, denn ausprobiert hast du bisher
nichts, postet immer noch denselben code wie zu Beginn.
Michael B. schrieb:> aber was gibt man sich Mühe dein Programm zu korrigieren, es ist dir> offenbar scheissegal was die Leute hier schreiben, du erwartest wohl> dass jemand kommt und dir eintippt, denn ausprobiert hast du bisher> nichts, postet immer noch denselben code wie zu Beginn.
Was meckerst Du rum?
Es geht mir momentan nur um das Display Problem, und wie ich es lösen
kann.
Ich gehe schließlich noch in die Arbeit, umnd komme so wie heute erst
jetzt nach hause.
Ich lese schon mit, kann aber nicht so ganz flüssig am Abend gleich Code
hämmern, da ich kaputt bin vom Tag.
So nun zum Thema.
Das 'array' hab ich mal umbenannt. In 'Digits' wenns passend erscheint.
Den AVCC habe ich in der Schaltung schon auf +Ub, nur im Plan war er
nicht eingezeichnet
Die ISR-Routine, habe ich verstanden, halte ich kurz. Der Code dazu wird
noch umgekrempelt, wenn das Display richtig funktioniert. Außer der
Fehler liegt gleich hierin.
Ich habe mir jetzt die beiden Codes (Original und von Falk) etwas
genauer angeschaut – jeder hat so seine eigene Präferenzen, was das
Programmieren angeht, ich persönlich würde aber diese
Divisionoperationen komplett rausnehmen bzw. umgehen, indem ich sie
durch einfache Addition ersetze. Na wie auch immer – ich bin gespannt
wie die nächste Version zusammengewürfelt und -gedichtet wird. CTC für
den Timer1 mit OCR1A als TOP auf jeden Fall beibehalten, Modus 0 ist
hier eine Sackgasse.
Thomas S. schrieb:> Ich gehe schließlich noch in die Arbeit, umnd komme so wie heute erst> jetzt nach hause.
Du hast Firma: Chipwerkstatt angegeben. Möglicherweise weckt das
profi-mäßige Erwartungen.
Hi
Versuche mal, deine Ausgabe vom Programm zu trennen. Dazu braucht es
nicht viel, gibt dir aber die Möglichkeit, jederzeit eine Änderung ohne
Aufwand einzupflegen. Du schreibst die Werte, die du ausgeben möchtest,
in einen Speicher. (Variablenarray). Bei Änderung oder auch zyklisch
holst du die Werte nacheinander aus dem Variablenarray, addierst den
Wert auf die Anfangsadresse eines anderen Variablenarray, wo du die
Segmente der Anzeige abgelegt hast (Matrixspeicher)und schreibst die so
adressierte Matrix in einen Ausgabespeicher an die entsprechende Stelle.
Ist relativ einfach, Adresszeiger auf Wertearray setzen, Wert Codieren
und in Ausgabepuffer über den Adresszeiger schreiben.
Da ich C nicht beherrsche, kann ich dir keinen Programmcode liefern,
aber das Prinzip sollte problemlos umsetzbar sein.
Bei der Ausgabe wiederum arbeitest du komplett unabhängig vom
Hauptprogramm in einer Interruptroutine, die du jede ms aufrufst. Dabei
brauchst du lediglich einen Zeiger auf die Adresse des Ausgabepuffers,
also einen Zähler für die adressierte Matrix und ein Byte, wo die
Selektierung der Anzeige durchgeschoben wird. Dieses Selektierungsbit
kannst du mit einer Und-Funktion mit einem Null-Byte ausblenden und so
die Anzeige dunkel schalten. Egal, ob du eine gemeinsame Kathode oder
Anode hast, die Anpassung zur Ausgabe geschieht erst bei der Zuweisung
an den Port. Das macht es dir leichter, wenn du z.B. eine gemeinsame
Kathode hast und dann mit der umgekehrten Logik arbeiten musst. Ändert
sich der Anzeigebaustein, brauchst du nur die Anpassung ändern.
Ich hoffe, meine Skizze und die Erklärung waren hilfreich.
Gruß oldmax
Hi
Falk B. schrieb:> Du bist ja ein gaaaaanz Schneller! Das Thema ist erstens schon lange> erledigt und 2. auch schon mit sinnvollem Quelltext versehen.
Na ja, schnell muss ich nicht mehr sein. Das sind andere und das ist
auch ihr gutes Recht. Ob schnell sein immer richtig ist lass ich einfach
mal im Raum stehen. Ich hab halt gelesen, das die Diskussion von einem
Anfänger eröffnet wurde und manchmal ist auch eine Information am Rande
wertvoll. Ein Profi wie du hat mit der Lösung kein Problem und die
Lieferung von sinnvollem Quelltext ist ja auch nicht zu bemängeln. Aber
du triffst ganz speziell diesen Fall. Da kann man direkt warten, wann
die Frage kommt " ich habe eine 4, 6 oder 8 stellige Anzeige...."
Na ja, egal, ich brauch halt immer etwas mehr Zeit. Nun muss ich mich
aber beeilen, es wartet noch Arbeit auf mich......
Gruß oldmax
Martin V. schrieb:> Versuche mal, deine Ausgabe vom Programm zu trennen. Dazu braucht es> nicht viel, gibt dir aber die Möglichkeit, jederzeit eine Änderung ohne> Aufwand einzupflegen.
Hallo Martin,
ich danke Dir hier für die ausführliche Problembehandlung. Habe momentan
alle Hände voll zu tun, und deshalb bin ich manchmal langsam.
Ich habe den Screenshoot schon verstanden, und werde dies mal so
versuchen. Komme frühestens am Wochenende wieder dazu. Meine Kohle per
Monat kommt ja nicht hiervon.
Es war mir nicht klar, warum das eine Digit noch leuchtet, wenn das
andere angesteuert wird. Dieses Ghosting ist hier immer noch mein
Problem. Nun aber anders, und nun wird das linke Digit immer mit einer
leicht leuchtenden '8' hinterlegt. Warum '8'? weiß ich noch nicht.
Die Ausgabe stimmt aber.
Aber nochmal zusammendfassend:
Es werden 2 Stellen benötigt. Zahlenraum von 0-50 Kredite. 99 wird nie
erreicht. Der Einwurf wird (im ee-Prom hinterlegt) bei erreichen des
Wertes gesperrt. Einziges Feature wir im Pausenbetrieb, Zähler auf 0 ist
sein, ein kleines Lauflicht-Spiel mit den Segmenten.
Es gibt einen Ausgang, der High wird, sobald Kredit >0 ist.
Der uC muss nur auf 3 Eingänge reagieren, und entsprechende Kredite, die
im ee-Prom hinterlegt sind auf addieren. Auch auf ein Signal zur
Subtraktion bis hinab auf 0 muss er reagieren.
Eine kleine Service-Routine, für diverse Einstellungen ist dann
vorhanden.
Das wars dann auch schon. Manipulation ist noch ein Thema, habe ich aber
in einem anderen Projekt schon gelöst, abgedeckt.
S. L. schrieb:> Dass nämlich dieses 'TCNT1 = 65536-15' in der ISR steht, nach der> Rückkehr aus dieser noch zwei oder drei Befehle im Hauptprogramm> ausgeführt werden und dann schron wieder der Interrupt erfolgt.
Die Interupt-Routine habe ich mir aus dem 'Tutorial' geholt, und etwas
angepasst.
Aber bitte wenn möglich, erklärt mir, das mit den Werten.
Wenn ich nämlich dieses TCNT1 = 65536-15, was ja den Überlauf erzeugt,
und den Int auslöst, hier in die Initialisierung schiebe, oder nur dort
habe läuft das nicht mehr.
Warum braucht die Routine dies immer wieder?
Am Ende werde ich 2 Interupt-Routinen benötigen.
1. Display
2. Für 'Watch-Dog' Routine, und der Ansteuerung einer LED dafür.
Falk B. schrieb:> Um Pulse per Software zu erkennen und auszuwerten, muss man MINDESTENS> eine Flankenerkennung einbauen. Wartepausen sind bääääh.
Ich habe meiner Meinung dies drin (Wartepause), um erst bei fallender
Flanke den Kredit zu erhöhen. Ist mal was kaputt, erzeugt mir die
Routine dann nicht unendlich Kredite.
Thomas S. schrieb:> Falk B. schrieb:>> Um Pulse per Software zu erkennen und auszuwerten, muss man MINDESTENS>> eine Flankenerkennung einbauen. Wartepausen sind bääääh.>> Ich habe meiner Meinung dies drin (Wartepause), um erst bei fallender> Flanke den Kredit zu erhöhen. Ist mal was kaputt, erzeugt mir die> Routine dann nicht unendlich Kredite.
Mit dem sinnerfassenden Lesen hast du es wohl nicht so? Was hast du an
"Wartepausen sind bääääh."
nicht verstanden?
Und in meinem Beitrag ist ein vollständiger, aufgeräumter Quelltext
vorhanden, der alle Probleme lösten sollte. Hast du den mal compiliert
und auf deinen Controller programmiert?
Falk B. schrieb:> Und in meinem Beitrag ist ein vollständiger, aufgeräumter Quelltext> vorhanden, der alle Probleme lösten sollte.
Oh, .... danke Dir. Hab ich erst grad gesehen. Werde ich gleich mal im
Studio durchlaufen lassen. Aber auf den Atmega kann ich diesen erst
morgen schieben.
Danke Dir, mile Grazie.
Wie gesagt, dass mit den Timer, Interupts, da bin ich erst noch am
verinnerlichen.
Bin eigentlich überrascht, warum das nicht immer anstandslos
funktioniert.
In meinem Timer Projekt machte ich genau das und steuerte im ganzen 56
LEDs im MUX Betrieb an. Allerdings über SPI um Pins zu sparen
(Pro-Mini). Da gab es ein Vier-Digit LED-Display und ein Bargraph und
sechs individuelle LEDs. Siehe Bild:
Beitrag "Re: Zeigt her eure Kunstwerke (2017)"Beitrag "Re: Zeigt her eure Kunstwerke (2017)"
Die Segmente, Bargraph und LEDs werden von einem 74HC164 angesteuert und
die Spalten über einen zweiten 164 und 138 Encoder über
Treibertransistoren.
Eine T2 ISR steuerte die Leds über SPI mit 8MHz Takt an und das
passierte komplett im Hintergrund. Ein volatile globales Array enthielt
die ASCII Daten, die man gemütlich beschreiben konnte. Es gab absolut
keine Probleme damit und es lief beim ersten Mal einschalten. Ghosting
kam nicht vor.
SPI hat den großen Vorteil, die Anzeigeeinheit auch abgesetzt mit nur
wenigen Drahtverbindungen betreiben zu können.
Ich entschloß mich für ASCII aus Bequemlichkeit und kann alle bei
7-Segment möglichen Sonderzeichen wie Buchstaben anzeigen.
Da der Refresh als Timer ISR ausgeführt wird, merkt man im übrigen
Programablauf nichts in der Anzeige, wenn der uC andere Tätigkeiten
ausführen muß. Auch bei Serial Datenübertragungen gibt es keine
Reaktionen.
Peter D. schrieb:> Hier mal ein einfacher Schaltplan für 4 Digits.
Über die NPN-BJTs in der High-Side Steuerung der Digits kann man
unterschiedlicher Meinung sein.
Thomas S. schrieb:> Du hast aber auch ziemlich viel Code in der ISR. Dachte das soll man> vermeiden?
"Viel" ist relativ. Entscheidend ist die Laufzeit der ISR. DIe muss
logischerweise kürzer sein als die Periodendauer. Wenn das ggegben ist,
ist alles OK. Siehe Interrupt. "Kurz" heißt nicht, daß man
krampfhaft nur drei Anweisungen in eine ISR schreiben darf. Ich habe
schon ISRs benutzt, da steht Code mit 1-2 Seiten drin. Trotzdem ist sie
schnell genug in der AUsführung.
Rainer W. schrieb:> Über die NPN-BJTs in der High-Side Steuerung der Digits kann man> unterschiedlicher Meinung sein.
Kann man. Man verliert ca. 400-500mV mehr als bei Nutzung von PNPs in
Emitterschaltung. Das ist meist unkritisch. Es bleibt genug Spannung für
die Vorwiderstände.
Nochmal zur ISR. Die läuft hier mit 200Hz, das sind 5ms Periodendauer.
Die CPU läuft mit 1MHz, macht in der Zeit also 5000 Takte. Die ISR
braucht geschätzt ca. 30-50 Takte, also max. 1% der CPU-Leistung. Das
ist mal sicher kurz genug ;-)
Thomas S. schrieb:> Aber bitte wenn möglich, erklärt mir, das mit den Werten.> Wenn ich nämlich dieses TCNT1 = 65536-15, was ja den Überlauf erzeugt,> und den Int auslöst, hier in die Initialisierung schiebe, oder nur dort> habe läuft das nicht mehr.> Warum braucht die Routine dies immer wieder?
Steht eigentlich alles relativ gut im Datenblatt des Timers Deines
µControllers beschrieben – muss man sich halt etwas Mühe geben, es lesen
und das Verständnis für die permanente Register-Akrobatik des
Timerzählers im Modus 0 ergibt sich dann irgendwann mal, natürlich nicht
unbedingt beim ersten Durchlesen. Diese Akrobatik des Counters ist aber
normalerweise nicht nötig und außerdem etwas riskant, da man sich auch
einen schwerauffindbaren Fehler einhandeln kann – für komplexere
Zählervorgänge gibt es beispielsweise eben den CTC-Modus, wo man nichts
immer wieder von Hand beeinflussen muss, und das sollte man auch nutzen.
Im Anhang die zwei passenden Auszüge aus dem Datenblatt des 328P, beim
ATMEGA8 wird es bestimmt sehr ähnlich aussehen; und auch sehr ähnlich
funktionieren.